1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

First separation commit.

Added LICENSE, README.md, CONTRIBUTORS.md
This commit is contained in:
Erwin de Haan 2019-01-09 12:36:54 +01:00
parent 09513af31b
commit 4678528d00
657 changed files with 422 additions and 0 deletions

97
src/addplugin.html Normal file
View file

@ -0,0 +1,97 @@
<div id="addPluginPage" data-role="page" class="page type-interior pluginConfigurationPage" data-backbutton="true" data-require="emby-select,emby-collapse,emby-linkbutton">
<div>
<div class="content-primary">
<div class="readOnlyContent">
<div class="verticalSection">
<div class="sectionTitleContainer flex align-items-center">
<h1 class="sectionTitle pluginName"></h1>
<a is="emby-linkbutton" class="raised button-alt headerHelpButton" target="_blank" href="https://web.archive.org/web/20181216120305/https://github.com/MediaBrowser/Wiki/wiki/Plugins">${Help}</a>
</div>
<p id="tagline" style="font-style: italic;"></p>
<p id="pPreviewImage"></p>
<p id="overview"></p>
</div>
<div class="verticalSection">
<h2 class="sectionTitle">${HeaderInstall}</h2>
<form class="addPluginForm">
<p id="pCurrentVersion"></p>
<div id="pSelectVersion" class="hide selectContainer">
<select id="selectVersion" name="selectVersion" is="emby-select" label="${LabelSelectVersionToInstall}"></select>
</div>
<p id="btnInstallDiv" class="hide">
<button is="emby-button" type="submit" id="btnInstall" class="raised button-submit block">
<span>${Install}</span>
</button>
<div class="fieldDescription">${ServerRestartNeededAfterPluginInstall}</div>
</p>
<p id="nonServerMsg"></p>
</form>
<div class="premiumPackage" style="display: none; margin-top: 1.5em;">
<div>
<form name="_xclick" action="https://www.paypal.com/cgi-bin/webscr" method="post">
<p id="regStatus">
</p>
<div class="supporterDescription">
<p>${MessagePluginRequiresSubscription}</p>
<p><a is="emby-linkbutton" class="button-link" href="https://github.com/jellyfin/jellyfin" target="_blank">${LinkLearnMoreAboutSubscription}</a></p>
</div>
<div class="premiumDescription">
<p>${MessagePremiumPluginRequiresMembership}</p>
<p><a is="emby-linkbutton" class="button-link" href="https://github.com/jellyfin/jellyfin" target="_blank">${LinkLearnMoreAboutSubscription}</a></p>
</div>
<div class="premiumHasPrice" style="display: none">
<p id="regPrice">
</p>
<input type="hidden" name="cmd" value="_xclick">
<input type="hidden" id="payPalEmail" name="business" value="mb_1358534950_biz@reedsplace.com">
<input type="hidden" name="currency_code" value="USD">
<input type="hidden" id="featureName" name="item_name" value="">
<input type="hidden" id="amount" name="amount" value="10">
<input type="hidden" id="featureId" name="item_number" value="">
<input type="hidden" name="notify_url" value="https://mb3admin.local/admin/service/services/ppipn.php">
<input type="hidden" name="return" id="paypalReturnUrl" value="#">
<button is="emby-button" type="submit" id="ppButton" class="raised block button-submit" style="background-color: #179BD7;color:#fff;">
<span>${RegisterWithPayPal}</span>
</button>
</div>
</form>
</div>
</div>
</div>
</div>
<br />
<div class="readOnlyContent">
<div is="emby-collapse" title="${HeaderDeveloperInfo}">
<div class="collapseContent">
<p id="developer"></p>
<p id="pViewWebsite" style="display: none;">
<a is="emby-linkbutton" class="button-link" href="#" target="_blank">${ButtonViewWebsite}</a>
</p>
</div>
</div>
<div is="emby-collapse" title="${HeaderRevisionHistory}">
<div class="collapseContent">
<div id="revisionHistory"></div>
</div>
</div>
</div>
</div>
</div>
</div>

21
src/appservices.html Normal file
View file

@ -0,0 +1,21 @@
<div id="appServicesPage" data-role="page" class="page type-interior appServicesPage withTabs fullWidthContent" data-require="scripts/appservices">
<div>
<div class="content-primary">
<div class="verticalSection">
<div class="sectionTitleContainer sectionTitleContainer-cards flex align-items-center">
<h2 class="sectionTitle sectionTitle-cards">${HeaderInstalledServices}</h2>
<a is="emby-linkbutton" class="raised button-alt headerHelpButton" target="_blank" href="https://web.archive.org/web/20181216120305/https://github.com/MediaBrowser/Wiki/wiki/Plugins">${Help}</a>
</div>
<div class="installedPlugins"></div>
</div>
<div class="verticalSection">
<h2 class="sectionTitle sectionTitle-cards">${HeaderAvailableServices}</h2>
<div class="catalog"></div>
</div>
</div>
</div>
</div>

View file

@ -0,0 +1,139 @@
<link rel="import" href="../polymer/polymer.html">
<script src="./Sortable.js"></script>
<dom-module id="sortable-js">
<template>
<content></content>
</template>
</dom-module>
<script>
Polymer({
is: "sortable-js",
properties: {
group : { type: String, value: () => Math.random(), observer: "groupChanged" },
sort : { type: Boolean, value: true, observer: "sortChanged" },
disabled : { type: Boolean, value: false, observer: "disabledChanged" },
store : { type: Object, value: null, observer: "storeChanged" },
handle : { type: String, value: null, observer: "handleChanged" },
scrollSensitivity : { type: Number, value: 30, observer: "scrollSensitivityChanged" },
scrollSpeed : { type: Number, value: 10, observer: "scrollSpeedChanged" },
ghostClass : { type: String, value: "sortable-ghost", observer: "ghostClassChanged" },
chosenClass : { type: String, value: "sortable-chosen", observer: "chosenClassChanged" },
ignore : { type: String, value: "a, img", observer: "ignoreChanged" },
filter : { type: Object, value: null, observer: "filterChanged" },
animation : { type: Number, value: 0, observer: "animationChanged" },
dropBubble : { type: Boolean, value: false, observer: "dropBubbleChanged" },
dragoverBubble : { type: Boolean, value: false, observer: "dragoverBubbleChanged" },
dataIdAttr : { type: String, value: "data-id", observer: "dataIdAttrChanged" },
delay : { type: Number, value: 0, observer: "delayChanged" },
forceFallback : { type: Boolean, value: false, observer: "forceFallbackChanged" },
fallbackClass : { type: String, value: "sortable-fallback", observer: "fallbackClassChanged" },
fallbackOnBody : { type: Boolean, value: false, observer: "fallbackOnBodyChanged" },
draggable : {},
scroll : {}
},
created() {
// override default DOM property behavior
Object.defineProperties(this, {
draggable: { get() { return this._draggable || this.getAttribute("draggable") || ">*"}, set(value) { this._draggable = value; this.draggableChanged(value)} },
scroll: { get() { return this._scroll || JSON.parse(this.getAttribute("scroll") || "true") }, set(value) { this._scroll = value; this.scrollChanged(value)} }
})
},
attached: function() {
// Given
// <sortable-js>
// <template is="dom-repeat" items={{data}}>
// <div>
// <template is="dom-if" if="true">
// <span>hello</span></template></div>
// After render, it becomes
// <sortable-js>
// <div>
// <span>hello</span>
// <template is="dom-if">
// <tempalte is="dom-repeat">
var templates = this.querySelectorAll("template[is='dom-repeat']")
var template = templates[templates.length-1]
var options = {}
Object.keys(this.properties).forEach(key => {
options[key] = this[key]
})
this.sortable = Sortable.create(this, Object.assign(options, {
onUpdate: e => {
if (template) {
template.splice("items", e.newIndex, 0, template.splice("items", e.oldIndex, 1)[0])
}
this.fire("update", e)
},
onAdd: e => {
if (template) {
var froms = e.from.querySelectorAll("template[is='dom-repeat']")
var from = froms[froms.length-1]
var item = from.items[e.oldIndex]
template.splice("items", e.newIndex, 0, item)
}
this.fire("add", e)
},
onRemove: e => {
if (template) {
template.splice("items", e.oldIndex, 1)[0]
}
this.fire("remove", e)
},
onStart: e => {
this.fire("start", e)
},
onEnd: e => {
this.fire("end", e)
},
onSort: e => {
this.fire("sort", e)
},
onFilter: e => {
this.fire("filter", e)
},
onMove: e => {
this.fire("move", e)
}
}))
},
detached: function() {
this.sortable.destroy()
},
groupChanged : function(value) { this.sortable && this.sortable.option("group", value) },
sortChanged : function(value) { this.sortable && this.sortable.option("sort", value) },
disabledChanged : function(value) { this.sortable && this.sortable.option("disabled", value) },
storeChanged : function(value) { this.sortable && this.sortable.option("store", value) },
handleChanged : function(value) { this.sortable && this.sortable.option("handle", value) },
scrollChanged : function(value) { this.sortable && this.sortable.option("scroll", value) },
scrollSensitivityChanged : function(value) { this.sortable && this.sortable.option("scrollSensitivity", value) },
scrollSpeedChanged : function(value) { this.sortable && this.sortable.option("scrollSpeed", value) },
draggableChanged : function(value) { this.sortable && this.sortable.option("draggable", value) },
ghostClassChanged : function(value) { this.sortable && this.sortable.option("ghostClass", value) },
chosenClassChanged : function(value) { this.sortable && this.sortable.option("chosenClass", value) },
ignoreChanged : function(value) { this.sortable && this.sortable.option("ignore", value) },
filterChanged : function(value) { this.sortable && this.sortable.option("filter", value) },
animationChanged : function(value) { this.sortable && this.sortable.option("animation", value) },
dropBubbleChanged : function(value) { this.sortable && this.sortable.option("dropBubble", value) },
dragoverBubbleChanged : function(value) { this.sortable && this.sortable.option("dragoverBubble", value) },
dataIdAttrChanged : function(value) { this.sortable && this.sortable.option("dataIdAttr", value) },
delayChanged : function(value) { this.sortable && this.sortable.option("delay", value) },
forceFallbackChanged : function(value) { this.sortable && this.sortable.option("forceFallback", value) },
fallbackClassChanged : function(value) { this.sortable && this.sortable.option("fallbackClass", value) },
fallbackOnBodyChanged : function(value) { this.sortable && this.sortable.option("fallbackOnBody", value) }
})
</script>

View file

@ -0,0 +1,393 @@
/*! Sortable 1.4.2 - MIT | git://github.com/rubaxa/Sortable.git */ ! function(a) {
"use strict";
"function" == typeof define && define.amd ? define(a) : "undefined" != typeof module && "undefined" != typeof module.exports ? module.exports = a() : "undefined" != typeof Package ? Sortable = a() : window.Sortable = a()
}(function() {
"use strict";
function a(a, b) {
if (!a || !a.nodeType || 1 !== a.nodeType) throw "Sortable: `el` must be HTMLElement, and not " + {}.toString.call(a);
this.el = a, this.options = b = r({}, b), a[L] = this;
var c = {
group: Math.random(),
sort: !0,
disabled: !1,
store: null,
handle: null,
scroll: !0,
scrollSensitivity: 30,
scrollSpeed: 10,
draggable: /[uo]l/i.test(a.nodeName) ? "li" : ">*",
ghostClass: "sortable-ghost",
chosenClass: "sortable-chosen",
ignore: "a, img",
filter: null,
animation: 0,
setData: function(a, b) {
a.setData("Text", b.textContent)
},
dropBubble: !1,
dragoverBubble: !1,
dataIdAttr: "data-id",
delay: 0,
forceFallback: !1,
fallbackClass: "sortable-fallback",
fallbackOnBody: !1
};
for (var d in c) !(d in b) && (b[d] = c[d]);
V(b);
for (var f in this) "_" === f.charAt(0) && (this[f] = this[f].bind(this));
this.nativeDraggable = b.forceFallback ? !1 : P, e(a, "mousedown", this._onTapStart), e(a, "touchstart", this._onTapStart), this.nativeDraggable && (e(a, "dragover", this), e(a, "dragenter", this)), T.push(this._onDragOver), b.store && this.sort(b.store.get(this))
}
function b(a) {
v && v.state !== a && (h(v, "display", a ? "none" : ""), !a && v.state && w.insertBefore(v, s), v.state = a)
}
function c(a, b, c) {
if (a) {
c = c || N, b = b.split(".");
var d = b.shift().toUpperCase(),
e = new RegExp("\\s(" + b.join("|") + ")(?=\\s)", "g");
do
if (">*" === d && a.parentNode === c || ("" === d || a.nodeName.toUpperCase() == d) && (!b.length || ((" " + a.className + " ").match(e) || []).length == b.length)) return a; while (a !== c && (a = a.parentNode))
}
return null
}
function d(a) {
a.dataTransfer && (a.dataTransfer.dropEffect = "move"), a.preventDefault()
}
function e(a, b, c) {
a.addEventListener(b, c, !1)
}
function f(a, b, c) {
a.removeEventListener(b, c, !1)
}
function g(a, b, c) {
if (a)
if (a.classList) a.classList[c ? "add" : "remove"](b);
else {
var d = (" " + a.className + " ").replace(K, " ").replace(" " + b + " ", " ");
a.className = (d + (c ? " " + b : "")).replace(K, " ")
}
}
function h(a, b, c) {
var d = a && a.style;
if (d) {
if (void 0 === c) return N.defaultView && N.defaultView.getComputedStyle ? c = N.defaultView.getComputedStyle(a, "") : a.currentStyle && (c = a.currentStyle), void 0 === b ? c : c[b];
b in d || (b = "-webkit-" + b), d[b] = c + ("string" == typeof c ? "" : "px")
}
}
function i(a, b, c) {
if (a) {
var d = a.getElementsByTagName(b),
e = 0,
f = d.length;
if (c)
for (; f > e; e++) c(d[e], e);
return d
}
return []
}
function j(a, b, c, d, e, f, g) {
var h = N.createEvent("Event"),
i = (a || b[L]).options,
j = "on" + c.charAt(0).toUpperCase() + c.substr(1);
h.initEvent(c, !0, !0), h.to = b, h.from = e || b, h.item = d || b, h.clone = v, h.oldIndex = f, h.newIndex = g, b.dispatchEvent(h), i[j] && i[j].call(a, h)
}
function k(a, b, c, d, e, f) {
var g, h, i = a[L],
j = i.options.onMove;
return g = N.createEvent("Event"), g.initEvent("move", !0, !0), g.to = b, g.from = a, g.dragged = c, g.draggedRect = d, g.related = e || b, g.relatedRect = f || b.getBoundingClientRect(), a.dispatchEvent(g), j && (h = j.call(i, g)), h
}
function l(a) {
a.draggable = !1
}
function m() {
R = !1
}
function n(a, b) {
var c = a.lastElementChild,
d = c.getBoundingClientRect();
return (b.clientY - (d.top + d.height) > 5 || b.clientX - (d.right + d.width) > 5) && c
}
function o(a) {
for (var b = a.tagName + a.className + a.src + a.href + a.textContent, c = b.length, d = 0; c--;) d += b.charCodeAt(c);
return d.toString(36)
}
function p(a) {
var b = 0;
if (!a || !a.parentNode) return -1;
for (; a && (a = a.previousElementSibling);) "TEMPLATE" !== a.nodeName.toUpperCase() && b++;
return b
}
function q(a, b) {
var c, d;
return function() {
void 0 === c && (c = arguments, d = this, setTimeout(function() {
1 === c.length ? a.call(d, c[0]) : a.apply(d, c), c = void 0
}, b))
}
}
function r(a, b) {
if (a && b)
for (var c in b) b.hasOwnProperty(c) && (a[c] = b[c]);
return a
}
var s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J = {},
K = /\s+/g,
L = "Sortable" + (new Date).getTime(),
M = window,
N = M.document,
O = M.parseInt,
P = !!("draggable" in N.createElement("div")),
Q = function(a) {
return a = N.createElement("x"), a.style.cssText = "pointer-events:auto", "auto" === a.style.pointerEvents
}(),
R = !1,
S = Math.abs,
T = ([].slice, []),
U = q(function(a, b, c) {
if (c && b.scroll) {
var d, e, f, g, h = b.scrollSensitivity,
i = b.scrollSpeed,
j = a.clientX,
k = a.clientY,
l = window.innerWidth,
m = window.innerHeight;
if (z !== c && (y = b.scroll, z = c, y === !0)) {
y = c;
do
if (y.offsetWidth < y.scrollWidth || y.offsetHeight < y.scrollHeight) break; while (y = y.parentNode)
}
y && (d = y, e = y.getBoundingClientRect(), f = (S(e.right - j) <= h) - (S(e.left - j) <= h), g = (S(e.bottom - k) <= h) - (S(e.top - k) <= h)), f || g || (f = (h >= l - j) - (h >= j), g = (h >= m - k) - (h >= k), (f || g) && (d = M)), (J.vx !== f || J.vy !== g || J.el !== d) && (J.el = d, J.vx = f, J.vy = g, clearInterval(J.pid), d && (J.pid = setInterval(function() {
d === M ? M.scrollTo(M.pageXOffset + f * i, M.pageYOffset + g * i) : (g && (d.scrollTop += g * i), f && (d.scrollLeft += f * i))
}, 24)))
}
}, 30),
V = function(a) {
var b = a.group;
b && "object" == typeof b || (b = a.group = {
name: b
}), ["pull", "put"].forEach(function(a) {
a in b || (b[a] = !0)
}), a.groups = " " + b.name + (b.put.join ? " " + b.put.join(" ") : "") + " "
};
return a.prototype = {
constructor: a,
_onTapStart: function(a) {
var b = this,
d = this.el,
e = this.options,
f = a.type,
g = a.touches && a.touches[0],
h = (g || a).target,
i = h,
k = e.filter;
if (!("mousedown" === f && 0 !== a.button || e.disabled) && (h = c(h, e.draggable, d))) {
if (D = p(h), "function" == typeof k) {
if (k.call(this, a, h, this)) return j(b, i, "filter", h, d, D), void a.preventDefault()
} else if (k && (k = k.split(",").some(function(a) {
return a = c(i, a.trim(), d), a ? (j(b, a, "filter", h, d, D), !0) : void 0
}))) return void a.preventDefault();
(!e.handle || c(i, e.handle, d)) && this._prepareDragStart(a, g, h)
}
},
_prepareDragStart: function(a, b, c) {
var d, f = this,
h = f.el,
j = f.options,
k = h.ownerDocument;
c && !s && c.parentNode === h && (G = a, w = h, s = c, t = s.parentNode, x = s.nextSibling, F = j.group, d = function() {
f._disableDelayedDrag(), s.draggable = !0, g(s, f.options.chosenClass, !0), f._triggerDragStart(b)
}, j.ignore.split(",").forEach(function(a) {
i(s, a.trim(), l)
}), e(k, "mouseup", f._onDrop), e(k, "touchend", f._onDrop), e(k, "touchcancel", f._onDrop), j.delay ? (e(k, "mouseup", f._disableDelayedDrag), e(k, "touchend", f._disableDelayedDrag), e(k, "touchcancel", f._disableDelayedDrag), e(k, "mousemove", f._disableDelayedDrag), e(k, "touchmove", f._disableDelayedDrag), f._dragStartTimer = setTimeout(d, j.delay)) : d())
},
_disableDelayedDrag: function() {
var a = this.el.ownerDocument;
clearTimeout(this._dragStartTimer), f(a, "mouseup", this._disableDelayedDrag), f(a, "touchend", this._disableDelayedDrag), f(a, "touchcancel", this._disableDelayedDrag), f(a, "mousemove", this._disableDelayedDrag), f(a, "touchmove", this._disableDelayedDrag)
},
_triggerDragStart: function(a) {
a ? (G = {
target: s,
clientX: a.clientX,
clientY: a.clientY
}, this._onDragStart(G, "touch")) : this.nativeDraggable ? (e(s, "dragend", this), e(w, "dragstart", this._onDragStart)) : this._onDragStart(G, !0);
try {
N.selection ? N.selection.empty() : window.getSelection().removeAllRanges()
} catch (b) {}
},
_dragStarted: function() {
w && s && (g(s, this.options.ghostClass, !0), a.active = this, j(this, w, "start", s, w, D))
},
_emulateDragOver: function() {
if (H) {
if (this._lastX === H.clientX && this._lastY === H.clientY) return;
this._lastX = H.clientX, this._lastY = H.clientY, Q || h(u, "display", "none");
var a = N.elementFromPoint(H.clientX, H.clientY),
b = a,
c = " " + this.options.group.name,
d = T.length;
if (b)
do {
if (b[L] && b[L].options.groups.indexOf(c) > -1) {
for (; d--;) T[d]({
clientX: H.clientX,
clientY: H.clientY,
target: a,
rootEl: b
});
break
}
a = b
} while (b = b.parentNode);
Q || h(u, "display", "")
}
},
_onTouchMove: function(b) {
if (G) {
a.active || this._dragStarted(), this._appendGhost();
var c = b.touches ? b.touches[0] : b,
d = c.clientX - G.clientX,
e = c.clientY - G.clientY,
f = b.touches ? "translate3d(" + d + "px," + e + "px,0)" : "translate(" + d + "px," + e + "px)";
I = !0, H = c, h(u, "webkitTransform", f), h(u, "mozTransform", f), h(u, "msTransform", f), h(u, "transform", f), b.preventDefault()
}
},
_appendGhost: function() {
if (!u) {
var a, b = s.getBoundingClientRect(),
c = h(s),
d = this.options;
u = s.cloneNode(!0), g(u, d.ghostClass, !1), g(u, d.fallbackClass, !0), h(u, "top", b.top - O(c.marginTop, 10)), h(u, "left", b.left - O(c.marginLeft, 10)), h(u, "width", b.width), h(u, "height", b.height), h(u, "opacity", "0.8"), h(u, "position", "fixed"), h(u, "zIndex", "100000"), h(u, "pointerEvents", "none"), d.fallbackOnBody && N.body.appendChild(u) || w.appendChild(u), a = u.getBoundingClientRect(), h(u, "width", 2 * b.width - a.width), h(u, "height", 2 * b.height - a.height)
}
},
_onDragStart: function(a, b) {
var c = a.dataTransfer,
d = this.options;
this._offUpEvents(), "clone" == F.pull && (v = s.cloneNode(!0), h(v, "display", "none"), w.insertBefore(v, s)), b ? ("touch" === b ? (e(N, "touchmove", this._onTouchMove), e(N, "touchend", this._onDrop), e(N, "touchcancel", this._onDrop)) : (e(N, "mousemove", this._onTouchMove), e(N, "mouseup", this._onDrop)), this._loopId = setInterval(this._emulateDragOver, 50)) : (c && (c.effectAllowed = "move", d.setData && d.setData.call(this, c, s)), e(N, "drop", this), setTimeout(this._dragStarted, 0))
},
_onDragOver: function(a) {
var d, e, f, g = this.el,
i = this.options,
j = i.group,
l = j.put,
o = F === j,
p = i.sort;
if (void 0 !== a.preventDefault && (a.preventDefault(), !i.dragoverBubble && a.stopPropagation()), I = !0, F && !i.disabled && (o ? p || (f = !w.contains(s)) : F.pull && l && (F.name === j.name || l.indexOf && ~l.indexOf(F.name))) && (void 0 === a.rootEl || a.rootEl === this.el)) {
if (U(a, i, this.el), R) return;
if (d = c(a.target, i.draggable, g), e = s.getBoundingClientRect(), f) return b(!0), void(v || x ? w.insertBefore(s, v || x) : p || w.appendChild(s));
if (0 === g.children.length || g.children[0] === u || g === a.target && (d = n(g, a))) {
if (d) {
if (d.animated) return;
r = d.getBoundingClientRect()
}
b(o), k(w, g, s, e, d, r) !== !1 && (s.contains(g) || (g.appendChild(s), t = g), this._animate(e, s), d && this._animate(r, d))
} else if (d && !d.animated && d !== s && void 0 !== d.parentNode[L]) {
A !== d && (A = d, B = h(d), C = h(d.parentNode));
var q, r = d.getBoundingClientRect(),
y = r.right - r.left,
z = r.bottom - r.top,
D = /left|right|inline/.test(B.cssFloat + B.display) || "flex" == C.display && 0 === C["flex-direction"].indexOf("row"),
E = d.offsetWidth > s.offsetWidth,
G = d.offsetHeight > s.offsetHeight,
H = (D ? (a.clientX - r.left) / y : (a.clientY - r.top) / z) > .5,
J = d.nextElementSibling,
K = k(w, g, s, e, d, r);
if (K !== !1) {
if (R = !0, setTimeout(m, 30), b(o), 1 === K || -1 === K) q = 1 === K;
else if (D) {
var M = s.offsetTop,
N = d.offsetTop;
q = M === N ? d.previousElementSibling === s && !E || H && E : N > M
} else q = J !== s && !G || H && G;
s.contains(g) || (q && !J ? g.appendChild(s) : d.parentNode.insertBefore(s, q ? J : d)), t = s.parentNode, this._animate(e, s), this._animate(r, d)
}
}
}
},
_animate: function(a, b) {
var c = this.options.animation;
if (c) {
var d = b.getBoundingClientRect();
h(b, "transition", "none"), h(b, "transform", "translate3d(" + (a.left - d.left) + "px," + (a.top - d.top) + "px,0)"), b.offsetWidth, h(b, "transition", "all " + c + "ms"), h(b, "transform", "translate3d(0,0,0)"), clearTimeout(b.animated), b.animated = setTimeout(function() {
h(b, "transition", ""), h(b, "transform", ""), b.animated = !1
}, c)
}
},
_offUpEvents: function() {
var a = this.el.ownerDocument;
f(N, "touchmove", this._onTouchMove), f(a, "mouseup", this._onDrop), f(a, "touchend", this._onDrop), f(a, "touchcancel", this._onDrop)
},
_onDrop: function(b) {
var c = this.el,
d = this.options;
clearInterval(this._loopId), clearInterval(J.pid), clearTimeout(this._dragStartTimer), f(N, "mousemove", this._onTouchMove), this.nativeDraggable && (f(N, "drop", this), f(c, "dragstart", this._onDragStart)), this._offUpEvents(), b && (I && (b.preventDefault(), !d.dropBubble && b.stopPropagation()), u && u.parentNode.removeChild(u), s && (this.nativeDraggable && f(s, "dragend", this), l(s), g(s, this.options.ghostClass, !1), g(s, this.options.chosenClass, !1), w !== t ? (E = p(s), E >= 0 && (j(null, t, "sort", s, w, D, E), j(this, w, "sort", s, w, D, E), j(null, t, "add", s, w, D, E), j(this, w, "remove", s, w, D, E))) : (v && v.parentNode.removeChild(v), s.nextSibling !== x && (E = p(s), E >= 0 && (j(this, w, "update", s, w, D, E), j(this, w, "sort", s, w, D, E)))), a.active && ((null === E || -1 === E) && (E = D), j(this, w, "end", s, w, D, E), this.save())), w = s = t = u = x = v = y = z = G = H = I = E = A = B = F = a.active = null)
},
handleEvent: function(a) {
var b = a.type;
"dragover" === b || "dragenter" === b ? s && (this._onDragOver(a), d(a)) : ("drop" === b || "dragend" === b) && this._onDrop(a)
},
toArray: function() {
for (var a, b = [], d = this.el.children, e = 0, f = d.length, g = this.options; f > e; e++) a = d[e], c(a, g.draggable, this.el) && b.push(a.getAttribute(g.dataIdAttr) || o(a));
return b
},
sort: function(a) {
var b = {},
d = this.el;
this.toArray().forEach(function(a, e) {
var f = d.children[e];
c(f, this.options.draggable, d) && (b[a] = f)
}, this), a.forEach(function(a) {
b[a] && (d.removeChild(b[a]), d.appendChild(b[a]))
})
},
save: function() {
var a = this.options.store;
a && a.set(this)
},
closest: function(a, b) {
return c(a, b || this.options.draggable, this.el)
},
option: function(a, b) {
var c = this.options;
return void 0 === b ? c[a] : (c[a] = b, void("group" === a && V(c)))
},
destroy: function() {
var a = this.el;
a[L] = null, f(a, "mousedown", this._onTapStart), f(a, "touchstart", this._onTapStart), this.nativeDraggable && (f(a, "dragover", this), f(a, "dragenter", this)), Array.prototype.forEach.call(a.querySelectorAll("[draggable]"), function(a) {
a.removeAttribute("draggable")
}), T.splice(T.indexOf(this._onDragOver), 1), this._onDrop(), this.el = a = null
}
}, a.utils = {
on: e,
off: f,
css: h,
find: i,
is: function(a, b) {
return !!c(a, b, a)
},
extend: r,
throttle: q,
closest: c,
toggleClass: g,
index: p
}, a.create = function(b, c) {
return new a(b, c)
}, a.version = "1.4.2", a
});

344
src/bower_components/Sortable/index.html vendored Normal file
View file

@ -0,0 +1,344 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta property="og:image" content="/st/og-image.png"/>
<title>Sortable. No jQuery.</title>
<meta name="keywords" content="sortable, reorder, list, javascript, html5, drag and drop, dnd, animation, groups, angular, ng-sortable, react, mixin, effects, rubaxa"/>
<meta name="description" content="Sortable - is a minimalist JavaScript library for reorderable drag-and-drop lists on modern browsers and touch devices. No jQuery. Supports Meteor, AngularJS, React and any CSS library, e.g. Bootstrap."/>
<meta name="viewport" content="width=device-width, initial-scale=0.5"/>
<link href="//rubaxa.github.io/Ply/ply.css" rel="stylesheet" type="text/css"/>
<link href="//fonts.googleapis.com/css?family=Roboto:300" rel="stylesheet" type="text/css"/>
<link href="st/app.css" rel="stylesheet" type="text/css"/>
</head>
<body>
<a href="https://github.com/RubaXa/Sortable"><img style="position: fixed; top: 0; right: 0; border: 0; z-index: 10000;" src="//s3.amazonaws.com/github/ribbons/forkme_right_orange_ff7600.png" alt="Fork me on GitHub"></a>
<div class="container">
<div style="padding: 80px 150px 0; height: 160px;">
<a class="logo" href="https://github.com/RubaXa/Sortable"><img src="st/logo.png"/></a>
<h1 data-force="40" data-force-y="2.5">The JavaScript library for modern browsers and touch devices. No&nbsp;jQuery.</h1>
</div>
</div>
<!-- Connected lists -->
<div class="container" style="height: 520px">
<div data-force="30" class="layer block" style="left: 14.5%; top: 0; width: 37%">
<div class="layer title">List A</div>
<ul id="foo" class="block__list block__list_words">
<li>бегемот</li>
<li>корм</li>
<li>антон</li>
<li>сало</li>
<li>железосталь</li>
<li>валик</li>
<li>кровать</li>
<li>краб</li>
</ul>
</div>
<div data-force="18" class="layer block" style="left: 58%; top: 143px; width: 40%;">
<div class="layer title">List B</div>
<ul id="bar" class="block__list block__list_tags">
<li>казнить</li>
<li>,</li>
<li>нельзя</li>
<li>помиловать</li>
</ul>
</div>
</div>
<!-- Multi connected lists -->
<a name="m"></a>
<div class="container">
<div id="multi" style="margin-left: 30px">
<div><div data-force="5" class="layer title title_xl">Multi</div></div>
<div class="layer tile" data-force="30">
<div class="tile__name">Group A</div>
<div class="tile__list">
<img src="st/face-01.jpg"/><!--
--><img src="st/face-02.jpg"/><!--
--><img src="st/face-03.jpg"/><!--
--><img src="st/face-04.jpg"/>
</div>
</div>
<div class="layer tile" data-force="25">
<div class="tile__name">Group B</div>
<div class="tile__list">
<img src="st/face-05.jpg"/><!--
--><img src="st/face-06.jpg"/><!--
--><img src="st/face-07.jpg"/>
</div>
</div>
<div class="layer tile" data-force="20">
<div class="tile__name">Group C</div>
<div class="tile__list">
<img src="st/face-08.jpg"/><!--
--><img src="st/face-09.jpg"/>
</div>
</div>
</div>
</div>
<!-- Editable list -->
<a name="e"></a>
<div class="container" style="margin-top: 100px">
<div id="filter" style="margin-left: 30px">
<div><div data-force="5" class="layer title title_xl">Editable list</div></div>
<div style="margin-top: -8px; margin-left: 10px" class="block__list block__list_words">
<ul id="editable">
<li>Оля<i class="js-remove"></i></li>
<li>Владимир<i class="js-remove"></i></li>
<li>Алина<i class="js-remove"></i></li>
</ul>
<button id="addUser">Add</button>
</div>
</div>
</div>
<!-- Advanced connected lists -->
<a name="ag"></a>
<div class="container" style="margin-top: 100px;">
<div id="advanced" style="margin-left: 30px;">
<div><div data-force="5" class="layer title title_xl">Advanced groups</div></div>
<div style="width: 25%; float: left; margin-top: 15px; margin-left: 10px" class="block__list block__list_words">
<div class="block__list-title">pull & put</div>
<ul id="advanced-1">
<li>Meat</li>
<li>Potato</li>
<li>Tea</li>
</ul>
</div>
<div style="width: 25%; float: left; margin-top: 15px; margin-left: 10px" class="block__list block__list_words">
<div class="block__list-title">only pull (clone) no&nbsp;reordering</div>
<ul id="advanced-2">
<li>Sex</li>
<li>Drugs</li>
<li>Rock'n'roll</li>
</ul>
</div>
<div style="width: 25%; float: left; margin-top: 15px; margin-left: 10px" class="block__list block__list_words">
<div class="block__list-title">only put</div>
<ul id="advanced-3">
<li>Money</li>
<li>Force</li>
<li>Agility</li>
</ul>
</div>
<div style="clear: both"></div>
</div>
</div>
<!-- 'handle' option -->
<a name="h"></a>
<div class="container" style="margin-top: 100px;">
<div id="handle" style="margin-left: 30px;">
<div><div data-force="5" class="layer title title_xl">Drag handle and selectable text</div></div>
<div style="width: 30%; margin-left: 10px" class="block__list_words">
<ul id="handle-1">
<li><span class="drag-handle">&#9776;</span>Select text freely</li>
<li><span class="drag-handle">&#9776;</span>Drag my handle</li>
<li><span class="drag-handle">&#9776;</span>Best of both worlds</li>
</ul>
</div>
<div style="clear: both"></div>
</div>
</div>
<!-- Angular -->
<a name="ng"></a>
<div id="todos" ng-app="todoApp" class="container" style="margin-top: 100px">
<div style="margin-left: 30px">
<div><div data-force="5" class="layer title title_xl">AngularJS / ng-sortable</div></div>
<div style="width: 30%; margin-top: -8px; margin-left: 10px; float: left;" class="block__list block__list_words">
<div ng-controller="TodoController">
<span style="padding-left: 20px">{{remaining()}} of {{todos.length}} remaining</span>
[ <a href="" ng-click="archive()">archive</a> ]
<ul ng-sortable="{ group: 'todo', animation: 150 }" class="unstyled">
<li ng-repeat="todo in todos">
<input type="checkbox" ng-model="todo.done">
<span class="done-{{todo.done}}">{{todo.text}}</span>
</li>
</ul>
<form ng-submit="addTodo()" style="padding-left: 20px">
<input type="text" ng-model="todoText" size="30"
placeholder="add new todo here">
</form>
</div>
</div>
<div style="width: 30%; margin-top: -8px; margin-left: 10px; float: left;" class="block__list block__list_words">
<div ng-controller="TodoControllerNext">
<span style="padding-left: 20px">{{remaining()}} of {{todos.length}} remaining</span>
<ul ng-sortable="sortableConfig" class="unstyled">
<li ng-repeat="todo in todos">
<input type="checkbox" ng-model="todo.done">
<span class="done-{{todo.done}}">{{todo.text}}</span>
</li>
</ul>
</div>
</div>
<div style="clear: both"></div>
</div>
</div>
<!-- Code example -->
<a name="c"></a>
<div class="container" style="margin-top: 100px">
<div style="margin-left: 30px">
<div><div class="layer title title_xl">Code example</div></div>
<pre data-force="100" class="layer javascript" style="margin-top: -8px; margin-left: 10px; width: 90%"><code>// Simple list
var list = document.getElementById("my-ui-list");
Sortable.create(list); // That's all.
// Grouping
var foo = document.getElementById("foo");
Sortable.create(foo, { group: "omega" });
var bar = document.getElementById("bar");
Sortable.create(bar, { group: "omega" });
// Or
var container = document.getElementById("multi");
var sort = Sortable.create(container, {
animation: 150, // ms, animation speed moving items when sorting, `0` — without animation
handle: ".tile__title", // Restricts sort start click/touch to the specified element
draggable: ".tile", // Specifies which items inside the element should be sortable
onUpdate: function (evt/**Event*/){
var item = evt.item; // the current dragged HTMLElement
}
});
// ..
sort.destroy();
// Editable list
var editableList = Sortable.create(editable, {
filter: '.js-remove',
onFilter: function (evt) {
var el = editableList.closest(evt.item); // get dragged item
el && el.parentNode.removeChild(el);
}
});
</code></pre>
</div>
<div class="container" style="margin: 100px 0;">
<div style="margin-left: 30px">
<div><div class="layer title title_xl">See also</div></div>
<div id="rubaxa-repos" data-force="100" class="layer" style="margin-top: -8px; margin-left: 10px; width: 90%; background-color: #fff;">Loading&hellip;</div>
<script src="//rubaxa.github.io/repos.js"></script>
</div>
</div>
</div>
<script src="Sortable.js"></script>
<script src="//rubaxa.github.io/Ply/Ply.min.js"></script>
<script src="//ajax.googleapis.com/ajax/libs/angularjs/1.2.26/angular.min.js"></script>
<script src="ng-sortable.js"></script>
<script src="st/app.js"></script>
<!-- highlight.js -->
<style>
/* Tomorrow Theme */
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
/* Original theme - https://github.com/chriskempson/tomorrow-theme */
/* http://jmblog.github.com/color-themes-for-google-code-highlightjs */
.tomorrow-comment, pre .comment, pre .title {
color: #8e908c;
}
.tomorrow-red, pre .variable, pre .attribute, pre .tag, pre .regexp, pre .ruby .constant, pre .xml .tag .title, pre .xml .pi, pre .xml .doctype, pre .html .doctype, pre .css .id, pre .css .class, pre .css .pseudo {
color: #c82829;
}
.tomorrow-orange, pre .number, pre .preprocessor, pre .built_in, pre .literal, pre .params, pre .constant {
color: #f5871f;
}
.tomorrow-yellow, pre .class, pre .ruby .class .title, pre .css .rules .attribute {
color: #eab700;
}
.tomorrow-green, pre .string, pre .value, pre .inheritance, pre .header, pre .ruby .symbol, pre .xml .cdata {
color: #718c00;
}
.tomorrow-aqua, pre .css .hexcolor {
color: #3e999f;
}
.tomorrow-blue, pre .function, pre .python .decorator, pre .python .title, pre .ruby .function .title, pre .ruby .title .keyword, pre .perl .sub, pre .javascript .title, pre .coffeescript .title {
color: #4271ae;
}
.tomorrow-purple, pre .keyword, pre .javascript .function {
color: #8959a8;
}
pre {
border: 0;
background-color: #fff;
}
pre code {
display: block;
color: #4d4d4c;
font-size: 15px;
font-family: Menlo, Monaco, Consolas, monospace;
line-height: 1.5;
padding: 30px;
}
</style>
<script src="//yandex.st/highlightjs/7.5/highlight.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-16483888-3', 'rubaxa.github.io');
ga('send', 'pageview');
</script>
</body>
</html>

View file

@ -0,0 +1,17 @@
! function(factory) {
"use strict";
"function" == typeof define && define.amd ? define(["jquery"], factory) : factory(jQuery)
}(function($) {
"use strict";
$.fn.sortable = function(options) {
var retVal, args = arguments;
return this.each(function() {
var $el = $(this),
sortable = $el.data("sortable");
if (sortable || !(options instanceof Object) && options || (sortable = new Sortable(this, options), $el.data("sortable", sortable)), sortable) {
if ("widget" === options) return sortable;
"destroy" === options ? (sortable.destroy(), $el.removeData("sortable")) : "function" == typeof sortable[options] ? retVal = sortable[options].apply(sortable, [].slice.call(args, 1)) : options in sortable.options && (retVal = sortable.option.apply(sortable, args))
}
}), void 0 === retVal ? this : retVal
}
});

View file

@ -0,0 +1,97 @@
! function(factory) {
"use strict";
if ("function" == typeof define && define.amd) define(["knockout"], factory);
else if ("function" == typeof require && "object" == typeof exports && "object" == typeof module) {
var ko = require("knockout");
factory(ko)
} else factory(window.ko)
}(function(ko) {
"use strict";
var init = function(element, valueAccessor, allBindings, viewModel, bindingContext, sortableOptions) {
var options = buildOptions(valueAccessor, sortableOptions);
["onStart", "onEnd", "onRemove", "onAdd", "onUpdate", "onSort", "onFilter"].forEach(function(e) {
(options[e] || eventHandlers[e]) && (options[e] = function(eventType, parentVM, parentBindings, handler, e) {
var itemVM = ko.dataFor(e.item),
bindings = ko.utils.peekObservable(parentBindings()),
bindingHandlerBinding = bindings.sortable || bindings.draggable,
collection = bindingHandlerBinding.collection || bindingHandlerBinding.foreach;
handler && handler(e, itemVM, parentVM, collection, bindings), eventHandlers[eventType] && eventHandlers[eventType](e, itemVM, parentVM, collection, bindings)
}.bind(void 0, e, viewModel, allBindings, options[e]))
});
var sortableElement = Sortable.create(element, options);
return ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
sortableElement.destroy()
}), ko.bindingHandlers.template.init(element, valueAccessor)
},
update = function(element, valueAccessor, allBindings, viewModel, bindingContext, sortableOptions) {
return ko.bindingHandlers.template.update(element, valueAccessor, allBindings, viewModel, bindingContext)
},
eventHandlers = function(handlers) {
var moveOperations = [],
tryMoveOperation = function(e, itemVM, parentVM, collection, parentBindings) {
var currentOperation = {
event: e,
itemVM: itemVM,
parentVM: parentVM,
collection: collection,
parentBindings: parentBindings
},
existingOperation = moveOperations.filter(function(op) {
return op.itemVM === currentOperation.itemVM
})[0];
if (existingOperation) {
moveOperations.splice(moveOperations.indexOf(existingOperation), 1);
var removeOperation = "remove" === currentOperation.event.type ? currentOperation : existingOperation,
addOperation = "add" === currentOperation.event.type ? currentOperation : existingOperation;
moveItem(itemVM, removeOperation.collection, addOperation.collection, addOperation.event.clone, addOperation.event)
} else moveOperations.push(currentOperation)
},
moveItem = function(itemVM, from, to, clone, e) {
var fromArray = from(),
originalIndex = fromArray.indexOf(itemVM),
newIndex = e.newIndex;
e.item.previousElementSibling && (newIndex = fromArray.indexOf(ko.dataFor(e.item.previousElementSibling)), originalIndex > newIndex && (newIndex += 1)), e.item.parentNode.removeChild(e.item), fromArray.splice(originalIndex, 1), from.valueHasMutated(), clone && from !== to && (fromArray.splice(originalIndex, 0, itemVM), from.valueHasMutated()), to().splice(newIndex, 0, itemVM), to.valueHasMutated()
};
return handlers.onRemove = tryMoveOperation, handlers.onAdd = tryMoveOperation, handlers.onUpdate = function(e, itemVM, parentVM, collection, parentBindings) {
moveItem(itemVM, collection, collection, !1, e)
}, handlers
}({}),
buildOptions = function(bindingOptions, options) {
var merge = function(into, from) {
for (var prop in from) "[object Object]" === Object.prototype.toString.call(from[prop]) ? ("[object Object]" !== Object.prototype.toString.call(into[prop]) && (into[prop] = {}), into[prop] = merge(into[prop], from[prop])) : into[prop] = from[prop];
return into
},
unwrappedOptions = ko.utils.peekObservable(bindingOptions()).options || {};
return options = merge({}, options), unwrappedOptions.group && "[object Object]" !== Object.prototype.toString.call(unwrappedOptions.group) && (unwrappedOptions.group = {
name: unwrappedOptions.group
}), merge(options, unwrappedOptions)
};
ko.bindingHandlers.draggable = {
sortableOptions: {
group: {
pull: "clone",
put: !1
},
sort: !1
},
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
return init(element, valueAccessor, allBindings, viewModel, 0, ko.bindingHandlers.draggable.sortableOptions)
},
update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
return update(element, valueAccessor, allBindings, viewModel, bindingContext, ko.bindingHandlers.draggable.sortableOptions)
}
}, ko.bindingHandlers.sortable = {
sortableOptions: {
group: {
pull: !0,
put: !0
}
},
init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
return init(element, valueAccessor, allBindings, viewModel, 0, ko.bindingHandlers.sortable.sortableOptions)
},
update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
return update(element, valueAccessor, allBindings, viewModel, bindingContext, ko.bindingHandlers.sortable.sortableOptions)
}
}
});

View file

@ -0,0 +1,87 @@
! function(factory) {
"use strict";
"function" == typeof define && define.amd ? define(["angular", "./Sortable"], factory) : "function" == typeof require && "object" == typeof exports && "object" == typeof module ? (require("angular"), factory(angular, require("./Sortable")), module.exports = "ng-sortable") : window.angular && window.Sortable && factory(angular, Sortable)
}(function(angular, Sortable) {
"use strict";
var expando = "Sortable:ng-sortable";
angular.module("ng-sortable", []).constant("ngSortableVersion", "0.4.0").constant("ngSortableConfig", {}).directive("ngSortable", ["$parse", "ngSortableConfig", function($parse, ngSortableConfig) {
var removed, nextSibling, getSourceFactory = function(el, scope) {
var ngRepeat = [].filter.call(el.childNodes, function(node) {
return 8 === node.nodeType && -1 !== node.nodeValue.indexOf("ngRepeat:")
})[0];
if (!ngRepeat) return function() {
return null
};
ngRepeat = ngRepeat.nodeValue.match(/ngRepeat:\s*(?:\(.*?,\s*)?([^\s)]+)[\s)]+in\s+([^\s|]+)/);
var itemsExpr = $parse(ngRepeat[2]);
return function() {
return itemsExpr(scope.$parent) || []
}
};
return {
restrict: "AC",
scope: {
ngSortable: "=?"
},
link: function(scope, $el) {
function _emitEvent(evt, item) {
var name = "on" + evt.type.charAt(0).toUpperCase() + evt.type.substr(1),
source = getSource();
options[name] && options[name]({
model: item || source[evt.newIndex],
models: source,
oldIndex: evt.oldIndex,
newIndex: evt.newIndex
})
}
function _sync(evt) {
var items = getSource();
if (items) {
var oldIndex = evt.oldIndex,
newIndex = evt.newIndex;
if (el !== evt.from) {
var prevItems = evt.from[expando]();
removed = prevItems[oldIndex], evt.clone ? (removed = angular.copy(removed), prevItems.splice(Sortable.utils.index(evt.clone), 0, prevItems.splice(oldIndex, 1)[0]), evt.from.removeChild(evt.clone)) : prevItems.splice(oldIndex, 1), items.splice(newIndex, 0, removed), evt.from.insertBefore(evt.item, nextSibling)
} else items.splice(newIndex, 0, items.splice(oldIndex, 1)[0]);
scope.$apply()
}
}
var sortable, el = $el[0],
options = angular.extend(scope.ngSortable || {}, ngSortableConfig),
watchers = [],
getSource = getSourceFactory(el, scope);
el[expando] = getSource, sortable = Sortable.create(el, Object.keys(options).reduce(function(opts, name) {
return opts[name] = opts[name] || options[name], opts
}, {
onStart: function(evt) {
nextSibling = evt.item.nextSibling, _emitEvent(evt), scope.$apply()
},
onEnd: function(evt) {
_emitEvent(evt, removed), scope.$apply()
},
onAdd: function(evt) {
_sync(evt), _emitEvent(evt, removed), scope.$apply()
},
onUpdate: function(evt) {
_sync(evt), _emitEvent(evt)
},
onRemove: function(evt) {
_emitEvent(evt, removed)
},
onSort: function(evt) {
_emitEvent(evt)
}
})), $el.on("$destroy", function() {
angular.forEach(watchers, function(unwatch) {
unwatch()
}), sortable.destroy(), el[expando] = null, el = null, watchers = null, sortable = null, nextSibling = null
}), angular.forEach(["sort", "disabled", "draggable", "handle", "animation", "group", "ghostClass", "filter", "onStart", "onEnd", "onAdd", "onUpdate", "onRemove", "onSort"], function(name) {
watchers.push(scope.$watch("ngSortable." + name, function(value) {
void 0 !== value && (options[name] = value, /^on[A-Z]/.test(name) || sortable.option(name, value))
}))
})
}
}
}])
});

View file

@ -0,0 +1,71 @@
! function(factory) {
"use strict";
"undefined" != typeof module && void 0 !== module.exports ? module.exports = factory(require("./Sortable")) : "function" == typeof define && define.amd ? define(["./Sortable"], factory) : window.SortableMixin = factory(Sortable)
}(function(Sortable) {
"use strict";
function _getModelName(component) {
return component.sortableOptions && component.sortableOptions.model || _defaultOptions.model
}
function _getModelItems(component) {
var name = _getModelName(component);
return (component.state && component.state[name] || component.props[name]).slice()
}
function _extend(dst, src) {
for (var key in src) src.hasOwnProperty(key) && (dst[key] = src[key]);
return dst
}
var _nextSibling, _activeComponent, _defaultOptions = {
ref: "list",
model: "items",
animation: 100,
onStart: "handleStart",
onEnd: "handleEnd",
onAdd: "handleAdd",
onUpdate: "handleUpdate",
onRemove: "handleRemove",
onSort: "handleSort",
onFilter: "handleFilter",
onMove: "handleMove"
};
return {
sortableMixinVersion: "0.1.1",
_sortableInstance: null,
componentDidMount: function() {
var DOMNode, options = _extend(_extend({}, _defaultOptions), this.sortableOptions || {}),
copyOptions = _extend({}, options),
emitEvent = function(type, evt) {
var method = this[options[type]];
method && method.call(this, evt, this._sortableInstance)
}.bind(this);
"onStart onEnd onAdd onSort onUpdate onRemove onFilter onMove".split(" ").forEach(function(name) {
copyOptions[name] = function(evt) {
if ("onStart" === name) _nextSibling = evt.item.nextElementSibling, _activeComponent = this;
else if ("onAdd" === name || "onUpdate" === name) {
evt.from.insertBefore(evt.item, _nextSibling);
var remoteItems, item, newState = {},
remoteState = {},
oldIndex = evt.oldIndex,
newIndex = evt.newIndex,
items = _getModelItems(this);
"onAdd" === name ? (remoteItems = _getModelItems(_activeComponent), item = remoteItems.splice(oldIndex, 1)[0], items.splice(newIndex, 0, item), remoteState[_getModelName(_activeComponent)] = remoteItems) : items.splice(newIndex, 0, items.splice(oldIndex, 1)[0]), newState[_getModelName(this)] = items, copyOptions.stateHandler ? this[copyOptions.stateHandler](newState) : this.setState(newState), this !== _activeComponent && _activeComponent.setState(remoteState)
}
setTimeout(function() {
emitEvent(name, evt)
}, 0)
}.bind(this)
}, this), DOMNode = this.getDOMNode() ? (this.refs[options.ref] || this).getDOMNode() : this.refs[options.ref] || this, this._sortableInstance = Sortable.create(DOMNode, copyOptions)
},
componentWillReceiveProps: function(nextProps) {
var newState = {},
modelName = _getModelName(this),
items = nextProps[modelName];
items && (newState[modelName] = items, this.setState(newState))
},
componentWillUnmount: function() {
this._sortableInstance.destroy(), this._sortableInstance = null
}
}
});

View file

@ -0,0 +1,582 @@
/**
* Swiper 3.3.1
* Most modern mobile touch slider and framework with hardware accelerated transitions
*
* http://www.idangero.us/swiper/
*
* Copyright 2016, Vladimir Kharlampidi
* The iDangero.us
* http://www.idangero.us/
*
* Licensed under MIT
*
* Released on: February 7, 2016
*/
.swiper-container {
margin: 0 auto;
position: relative;
overflow: hidden;
z-index: 1
}
.swiper-container-no-flexbox .swiper-slide {
float: left
}
.swiper-container-vertical>.swiper-wrapper {
-webkit-box-orient: vertical;
-moz-box-orient: vertical;
-ms-flex-direction: column;
-webkit-flex-direction: column;
flex-direction: column
}
.swiper-wrapper {
position: relative;
width: 100%;
height: 100%;
z-index: 1;
display: -webkit-box;
display: -moz-box;
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
-webkit-transition-property: -webkit-transform;
-moz-transition-property: -moz-transform;
-o-transition-property: -o-transform;
-ms-transition-property: -ms-transform;
transition-property: transform;
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
box-sizing: content-box
}
.swiper-container-android .swiper-slide,
.swiper-wrapper {
-webkit-transform: translate3d(0, 0, 0);
-moz-transform: translate3d(0, 0, 0);
-o-transform: translate(0, 0);
-ms-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0)
}
.swiper-container-multirow>.swiper-wrapper {
-webkit-box-lines: multiple;
-moz-box-lines: multiple;
-ms-flex-wrap: wrap;
-webkit-flex-wrap: wrap;
flex-wrap: wrap
}
.swiper-container-free-mode>.swiper-wrapper {
-webkit-transition-timing-function: ease-out;
-moz-transition-timing-function: ease-out;
-ms-transition-timing-function: ease-out;
-o-transition-timing-function: ease-out;
transition-timing-function: ease-out;
margin: 0 auto
}
.swiper-slide {
-webkit-flex-shrink: 0;
-ms-flex: 0 0 auto;
flex-shrink: 0;
width: 100%;
height: 100%;
position: relative
}
.swiper-container-autoheight,
.swiper-container-autoheight .swiper-slide {
height: auto
}
.swiper-container-autoheight .swiper-wrapper {
-webkit-box-align: start;
-ms-flex-align: start;
-webkit-align-items: flex-start;
align-items: flex-start;
-webkit-transition-property: -webkit-transform, height;
-moz-transition-property: -moz-transform;
-o-transition-property: -o-transform;
-ms-transition-property: -ms-transform;
transition-property: transform, height
}
.swiper-container .swiper-notification {
position: absolute;
left: 0;
top: 0;
pointer-events: none;
opacity: 0;
z-index: -1000
}
.swiper-wp8-horizontal {
-ms-touch-action: pan-y;
touch-action: pan-y
}
.swiper-wp8-vertical {
-ms-touch-action: pan-x;
touch-action: pan-x
}
.swiper-button-next,
.swiper-button-prev {
position: absolute;
top: 50%;
width: 27px;
height: 44px;
margin-top: -22px;
z-index: 10;
cursor: pointer;
-moz-background-size: 27px 44px;
-webkit-background-size: 27px 44px;
background-size: 27px 44px;
background-position: center;
background-repeat: no-repeat
}
.swiper-button-next.swiper-button-disabled,
.swiper-button-prev.swiper-button-disabled {
opacity: .35;
cursor: auto;
pointer-events: none
}
.swiper-button-prev,
.swiper-container-rtl .swiper-button-next {
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M0%2C22L22%2C0l2.1%2C2.1L4.2%2C22l19.9%2C19.9L22%2C44L0%2C22L0%2C22L0%2C22z'%20fill%3D'%23007aff'%2F%3E%3C%2Fsvg%3E");
left: 10px;
right: auto
}
.swiper-button-prev.swiper-button-black,
.swiper-container-rtl .swiper-button-next.swiper-button-black {
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M0%2C22L22%2C0l2.1%2C2.1L4.2%2C22l19.9%2C19.9L22%2C44L0%2C22L0%2C22L0%2C22z'%20fill%3D'%23000000'%2F%3E%3C%2Fsvg%3E")
}
.swiper-button-prev.swiper-button-white,
.swiper-container-rtl .swiper-button-next.swiper-button-white {
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M0%2C22L22%2C0l2.1%2C2.1L4.2%2C22l19.9%2C19.9L22%2C44L0%2C22L0%2C22L0%2C22z'%20fill%3D'%23ffffff'%2F%3E%3C%2Fsvg%3E")
}
.swiper-button-next,
.swiper-container-rtl .swiper-button-prev {
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M27%2C22L27%2C22L5%2C44l-2.1-2.1L22.8%2C22L2.9%2C2.1L5%2C0L27%2C22L27%2C22z'%20fill%3D'%23007aff'%2F%3E%3C%2Fsvg%3E");
right: 10px;
left: auto
}
.swiper-button-next.swiper-button-black,
.swiper-container-rtl .swiper-button-prev.swiper-button-black {
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M27%2C22L27%2C22L5%2C44l-2.1-2.1L22.8%2C22L2.9%2C2.1L5%2C0L27%2C22L27%2C22z'%20fill%3D'%23000000'%2F%3E%3C%2Fsvg%3E")
}
.swiper-button-next.swiper-button-white,
.swiper-container-rtl .swiper-button-prev.swiper-button-white {
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20viewBox%3D'0%200%2027%2044'%3E%3Cpath%20d%3D'M27%2C22L27%2C22L5%2C44l-2.1-2.1L22.8%2C22L2.9%2C2.1L5%2C0L27%2C22L27%2C22z'%20fill%3D'%23ffffff'%2F%3E%3C%2Fsvg%3E")
}
.swiper-pagination {
position: absolute;
text-align: center;
-webkit-transition: .3s;
-moz-transition: .3s;
-o-transition: .3s;
transition: .3s;
-webkit-transform: translate3d(0, 0, 0);
-ms-transform: translate3d(0, 0, 0);
-o-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
z-index: 10
}
.swiper-pagination.swiper-pagination-hidden {
opacity: 0
}
.swiper-container-horizontal>.swiper-pagination-bullets,
.swiper-pagination-custom,
.swiper-pagination-fraction {
bottom: 10px;
left: 0;
width: 100%
}
.swiper-pagination-bullet {
width: 8px;
height: 8px;
display: inline-block;
border-radius: 100%;
background: #000;
opacity: .2
}
button.swiper-pagination-bullet {
border: none;
margin: 0;
padding: 0;
box-shadow: none;
-moz-appearance: none;
-ms-appearance: none;
-webkit-appearance: none;
appearance: none
}
.swiper-pagination-clickable .swiper-pagination-bullet {
cursor: pointer
}
.swiper-pagination-white .swiper-pagination-bullet {
background: #fff
}
.swiper-pagination-bullet-active {
opacity: 1;
background: #007aff
}
.swiper-pagination-white .swiper-pagination-bullet-active {
background: #fff
}
.swiper-pagination-black .swiper-pagination-bullet-active {
background: #000
}
.swiper-container-vertical>.swiper-pagination-bullets {
right: 10px;
top: 50%;
-webkit-transform: translate3d(0, -50%, 0);
-moz-transform: translate3d(0, -50%, 0);
-o-transform: translate(0, -50%);
-ms-transform: translate3d(0, -50%, 0);
transform: translate3d(0, -50%, 0)
}
.swiper-container-vertical>.swiper-pagination-bullets .swiper-pagination-bullet {
margin: 5px 0;
display: block
}
.swiper-container-horizontal>.swiper-pagination-bullets .swiper-pagination-bullet {
margin: 0 5px
}
.swiper-pagination-progress {
background: rgba(0, 0, 0, .25);
position: absolute
}
.swiper-pagination-progress .swiper-pagination-progressbar {
background: #007aff;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
-webkit-transform: scale(0);
-ms-transform: scale(0);
-o-transform: scale(0);
transform: scale(0);
-webkit-transform-origin: left top;
-moz-transform-origin: left top;
-ms-transform-origin: left top;
-o-transform-origin: left top;
transform-origin: left top
}
.swiper-container-rtl .swiper-pagination-progress .swiper-pagination-progressbar {
-webkit-transform-origin: right top;
-moz-transform-origin: right top;
-ms-transform-origin: right top;
-o-transform-origin: right top;
transform-origin: right top
}
.swiper-container-horizontal>.swiper-pagination-progress {
width: 100%;
height: 4px;
left: 0;
top: 0
}
.swiper-container-vertical>.swiper-pagination-progress {
width: 4px;
height: 100%;
left: 0;
top: 0
}
.swiper-pagination-progress.swiper-pagination-white {
background: rgba(255, 255, 255, .5)
}
.swiper-pagination-progress.swiper-pagination-white .swiper-pagination-progressbar {
background: #fff
}
.swiper-pagination-progress.swiper-pagination-black .swiper-pagination-progressbar {
background: #000
}
.swiper-container-3d {
-webkit-perspective: 1200px;
-moz-perspective: 1200px;
-o-perspective: 1200px;
perspective: 1200px
}
.swiper-container-3d .swiper-cube-shadow,
.swiper-container-3d .swiper-slide,
.swiper-container-3d .swiper-slide-shadow-bottom,
.swiper-container-3d .swiper-slide-shadow-left,
.swiper-container-3d .swiper-slide-shadow-right,
.swiper-container-3d .swiper-slide-shadow-top,
.swiper-container-3d .swiper-wrapper {
-webkit-transform-style: preserve-3d;
-moz-transform-style: preserve-3d;
-ms-transform-style: preserve-3d;
transform-style: preserve-3d
}
.swiper-container-3d .swiper-slide-shadow-bottom,
.swiper-container-3d .swiper-slide-shadow-left,
.swiper-container-3d .swiper-slide-shadow-right,
.swiper-container-3d .swiper-slide-shadow-top {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
pointer-events: none;
z-index: 10
}
.swiper-container-3d .swiper-slide-shadow-left {
background-image: -webkit-gradient(linear, left top, right top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, 0)));
background-image: -webkit-linear-gradient(right, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0));
background-image: -moz-linear-gradient(right, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0));
background-image: -o-linear-gradient(right, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0));
background-image: linear-gradient(to left, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0))
}
.swiper-container-3d .swiper-slide-shadow-right {
background-image: -webkit-gradient(linear, right top, left top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, 0)));
background-image: -webkit-linear-gradient(left, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0));
background-image: -moz-linear-gradient(left, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0));
background-image: -o-linear-gradient(left, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0));
background-image: linear-gradient(to right, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0))
}
.swiper-container-3d .swiper-slide-shadow-top {
background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, 0)));
background-image: -webkit-linear-gradient(bottom, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0));
background-image: -moz-linear-gradient(bottom, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0));
background-image: -o-linear-gradient(bottom, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0));
background-image: linear-gradient(to top, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0))
}
.swiper-container-3d .swiper-slide-shadow-bottom {
background-image: -webkit-gradient(linear, left bottom, left top, from(rgba(0, 0, 0, .5)), to(rgba(0, 0, 0, 0)));
background-image: -webkit-linear-gradient(top, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0));
background-image: -moz-linear-gradient(top, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0));
background-image: -o-linear-gradient(top, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0));
background-image: linear-gradient(to bottom, rgba(0, 0, 0, .5), rgba(0, 0, 0, 0))
}
.swiper-container-coverflow .swiper-wrapper,
.swiper-container-flip .swiper-wrapper {
-ms-perspective: 1200px
}
.swiper-container-cube,
.swiper-container-flip {
overflow: visible
}
.swiper-container-cube .swiper-slide,
.swiper-container-flip .swiper-slide {
pointer-events: none;
-webkit-backface-visibility: hidden;
-moz-backface-visibility: hidden;
-ms-backface-visibility: hidden;
backface-visibility: hidden;
z-index: 1
}
.swiper-container-cube .swiper-slide .swiper-slide,
.swiper-container-flip .swiper-slide .swiper-slide {
pointer-events: none
}
.swiper-container-cube .swiper-slide-active,
.swiper-container-cube .swiper-slide-active .swiper-slide-active,
.swiper-container-flip .swiper-slide-active,
.swiper-container-flip .swiper-slide-active .swiper-slide-active {
pointer-events: auto
}
.swiper-container-cube .swiper-slide-shadow-bottom,
.swiper-container-cube .swiper-slide-shadow-left,
.swiper-container-cube .swiper-slide-shadow-right,
.swiper-container-cube .swiper-slide-shadow-top,
.swiper-container-flip .swiper-slide-shadow-bottom,
.swiper-container-flip .swiper-slide-shadow-left,
.swiper-container-flip .swiper-slide-shadow-right,
.swiper-container-flip .swiper-slide-shadow-top {
z-index: 0;
-webkit-backface-visibility: hidden;
-moz-backface-visibility: hidden;
-ms-backface-visibility: hidden;
backface-visibility: hidden
}
.swiper-container-cube .swiper-slide {
visibility: hidden;
-webkit-transform-origin: 0 0;
-moz-transform-origin: 0 0;
-ms-transform-origin: 0 0;
transform-origin: 0 0;
width: 100%;
height: 100%
}
.swiper-container-cube.swiper-container-rtl .swiper-slide {
-webkit-transform-origin: 100% 0;
-moz-transform-origin: 100% 0;
-ms-transform-origin: 100% 0;
transform-origin: 100% 0
}
.swiper-container-cube .swiper-slide-active,
.swiper-container-cube .swiper-slide-next,
.swiper-container-cube .swiper-slide-next+.swiper-slide,
.swiper-container-cube .swiper-slide-prev {
pointer-events: auto;
visibility: visible
}
.swiper-container-cube .swiper-cube-shadow {
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 100%;
background: #000;
opacity: .6;
-webkit-filter: blur(50px);
filter: blur(50px);
z-index: 0
}
.swiper-container-fade.swiper-container-free-mode .swiper-slide {
-webkit-transition-timing-function: ease-out;
-moz-transition-timing-function: ease-out;
-ms-transition-timing-function: ease-out;
-o-transition-timing-function: ease-out;
transition-timing-function: ease-out
}
.swiper-container-fade .swiper-slide {
pointer-events: none;
-webkit-transition-property: opacity;
-moz-transition-property: opacity;
-o-transition-property: opacity;
transition-property: opacity
}
.swiper-container-fade .swiper-slide .swiper-slide {
pointer-events: none
}
.swiper-container-fade .swiper-slide-active,
.swiper-container-fade .swiper-slide-active .swiper-slide-active {
pointer-events: auto
}
.swiper-scrollbar {
border-radius: 10px;
position: relative;
-ms-touch-action: none;
background: rgba(0, 0, 0, .1)
}
.swiper-container-horizontal>.swiper-scrollbar {
position: absolute;
left: 1%;
bottom: 3px;
z-index: 50;
height: 5px;
width: 98%
}
.swiper-container-vertical>.swiper-scrollbar {
position: absolute;
right: 3px;
top: 1%;
z-index: 50;
width: 5px;
height: 98%
}
.swiper-scrollbar-drag {
height: 100%;
width: 100%;
position: relative;
background: rgba(0, 0, 0, .5);
border-radius: 10px;
left: 0;
top: 0
}
.swiper-scrollbar-cursor-drag {
cursor: move
}
.swiper-lazy-preloader {
width: 42px;
height: 42px;
position: absolute;
left: 50%;
top: 50%;
margin-left: -21px;
margin-top: -21px;
z-index: 10;
-webkit-transform-origin: 50%;
-moz-transform-origin: 50%;
transform-origin: 50%;
-webkit-animation: swiper-preloader-spin 1s steps(12, end) infinite;
-moz-animation: swiper-preloader-spin 1s steps(12, end) infinite;
animation: swiper-preloader-spin 1s steps(12, end) infinite
}
.swiper-lazy-preloader:after {
display: block;
content: "";
width: 100%;
height: 100%;
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20viewBox%3D'0%200%20120%20120'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20xmlns%3Axlink%3D'http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink'%3E%3Cdefs%3E%3Cline%20id%3D'l'%20x1%3D'60'%20x2%3D'60'%20y1%3D'7'%20y2%3D'27'%20stroke%3D'%236c6c6c'%20stroke-width%3D'11'%20stroke-linecap%3D'round'%2F%3E%3C%2Fdefs%3E%3Cg%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(30%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(60%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(90%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(120%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(150%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.37'%20transform%3D'rotate(180%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.46'%20transform%3D'rotate(210%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.56'%20transform%3D'rotate(240%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.66'%20transform%3D'rotate(270%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.75'%20transform%3D'rotate(300%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.85'%20transform%3D'rotate(330%2060%2C60)'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E");
background-position: 50%;
-webkit-background-size: 100%;
background-size: 100%;
background-repeat: no-repeat
}
.swiper-lazy-preloader-white:after {
background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg%20viewBox%3D'0%200%20120%20120'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%20xmlns%3Axlink%3D'http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink'%3E%3Cdefs%3E%3Cline%20id%3D'l'%20x1%3D'60'%20x2%3D'60'%20y1%3D'7'%20y2%3D'27'%20stroke%3D'%23fff'%20stroke-width%3D'11'%20stroke-linecap%3D'round'%2F%3E%3C%2Fdefs%3E%3Cg%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(30%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(60%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(90%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(120%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.27'%20transform%3D'rotate(150%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.37'%20transform%3D'rotate(180%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.46'%20transform%3D'rotate(210%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.56'%20transform%3D'rotate(240%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.66'%20transform%3D'rotate(270%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.75'%20transform%3D'rotate(300%2060%2C60)'%2F%3E%3Cuse%20xlink%3Ahref%3D'%23l'%20opacity%3D'.85'%20transform%3D'rotate(330%2060%2C60)'%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E")
}
@-webkit-keyframes swiper-preloader-spin {
100% {
-webkit-transform: rotate(360deg)
}
}
@keyframes swiper-preloader-spin {
100% {
transform: rotate(360deg)
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,9 @@
var version = "3.3.1";
Package.describe({
name: "nolimits4web:swiper",
summary: "iDangero.us Swiper - mobile touch slider with hardware accelerated transitions and native behavior",
version: version,
git: "https://github.com/nolimits4web/Swiper"
}), Package.onUse(function(api) {
api.versionsFrom("1.1.0.2"), api.addFiles(["dist/css/swiper.min.css", "dist/js/swiper.js"], ["client"])
}), Package.onTest(function(api) {});

418
src/bower_components/alameda/alameda.js vendored Normal file
View file

@ -0,0 +1,418 @@
var requirejs, require, define;
! function(global, Promise, undef) {
function commentReplace(match, singlePrefix) {
return singlePrefix || ""
}
function hasProp(obj, prop) {
return hasOwn.call(obj, prop)
}
function getOwn(obj, prop) {
return obj && hasProp(obj, prop) && obj[prop]
}
function obj() {
return Object.create(null)
}
function eachProp(obj, func) {
var prop;
for (prop in obj)
if (hasProp(obj, prop) && func(obj[prop], prop)) break
}
function mixin(target, source, force, deepStringMixin) {
return source && eachProp(source, function(value, prop) {
!force && hasProp(target, prop) || (!deepStringMixin || "object" != typeof value || !value || Array.isArray(value) || "function" == typeof value || value instanceof RegExp ? target[prop] = value : (target[prop] || (target[prop] = {}), mixin(target[prop], value, force, deepStringMixin)))
}), target
}
function getGlobal(value) {
if (!value) return value;
var g = global;
return value.split(".").forEach(function(part) {
g = g[part]
}), g
}
function newContext(contextName) {
function trimDots(ary) {
var i, part, length = ary.length;
for (i = 0; i < length; i++)
if ("." === (part = ary[i])) ary.splice(i, 1), i -= 1;
else if (".." === part) {
if (0 === i || 1 === i && ".." === ary[2] || ".." === ary[i - 1]) continue;
i > 0 && (ary.splice(i - 1, 2), i -= 2)
}
}
function normalize(name, baseName, applyMap) {
var mapValue, nameParts, i, j, nameSegment, lastIndex, foundMap, foundI, foundStarMap, starI, baseParts = baseName && baseName.split("/"),
normalizedBaseParts = baseParts,
map = config.map,
starMap = map && map["*"];
if (name && (name = name.split("/"), lastIndex = name.length - 1, config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex]) && (name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, "")), "." === name[0].charAt(0) && baseParts && (normalizedBaseParts = baseParts.slice(0, baseParts.length - 1), name = normalizedBaseParts.concat(name)), trimDots(name), name = name.join("/")), applyMap && map && (baseParts || starMap)) {
nameParts = name.split("/");
outerLoop: for (i = nameParts.length; i > 0; i -= 1) {
if (nameSegment = nameParts.slice(0, i).join("/"), baseParts)
for (j = baseParts.length; j > 0; j -= 1)
if ((mapValue = getOwn(map, baseParts.slice(0, j).join("/"))) && (mapValue = getOwn(mapValue, nameSegment))) {
foundMap = mapValue, foundI = i;
break outerLoop
}! foundStarMap && starMap && getOwn(starMap, nameSegment) && (foundStarMap = getOwn(starMap, nameSegment), starI = i)
}!foundMap && foundStarMap && (foundMap = foundStarMap, foundI = starI), foundMap && (nameParts.splice(0, foundI, foundMap), name = nameParts.join("/"))
}
return getOwn(config.pkgs, name) || name
}
function makeShimExports(value) {
function fn() {
var ret;
return value.init && (ret = value.init.apply(global, arguments)), ret || value.exports && getGlobal(value.exports)
}
return fn
}
function takeQueue(anonId) {
var i, id, args, shim;
for (i = 0; i < queue.length; i += 1) {
if ("string" != typeof queue[i][0]) {
if (!anonId) break;
queue[i].unshift(anonId), anonId = undef
}
args = queue.shift(), id = args[0], i -= 1, id in defined || id in waiting || (id in deferreds ? main.apply(undef, args) : waiting[id] = args)
}
anonId && (shim = getOwn(config.shim, anonId) || {}, main(anonId, shim.deps || [], shim.exportsFn))
}
function makeRequire(relName, topLevel) {
var req = function(deps, callback, errback, alt) {
var name, cfg;
if (topLevel && takeQueue(), "string" == typeof deps) {
if (handlers[deps]) return handlers[deps](relName);
if (!((name = makeMap(deps, relName, !0).id) in defined)) throw new Error("Not loaded: " + name);
return defined[name]
}
return deps && !Array.isArray(deps) && (cfg = deps, deps = undef, Array.isArray(callback) && (deps = callback, callback = errback, errback = alt), topLevel) ? req.config(cfg)(deps, callback, errback) : (callback = callback || function() {
return slice.call(arguments, 0)
}, asyncResolve.then(function() {
return takeQueue(), main(undef, deps || [], callback, errback, relName)
}))
};
return req.isBrowser = "undefined" != typeof document && "undefined" != typeof navigator, req.nameToUrl = function(moduleName, ext, skipExt) {
var paths, syms, i, parentModule, url, parentPath, bundleId, pkgMain = getOwn(config.pkgs, moduleName);
if (pkgMain && (moduleName = pkgMain), bundleId = getOwn(bundlesMap, moduleName)) return req.nameToUrl(bundleId, ext, skipExt);
if (urlRegExp.test(moduleName)) url = moduleName + (ext || "");
else {
for (paths = config.paths, syms = moduleName.split("/"), i = syms.length; i > 0; i -= 1)
if (parentModule = syms.slice(0, i).join("/"), parentPath = getOwn(paths, parentModule)) {
Array.isArray(parentPath) && (parentPath = parentPath[0]), syms.splice(0, i, parentPath);
break
} url = syms.join("/"), url += ext || (/^data\:|^blob\:|\?/.test(url) || skipExt ? "" : ".js"), url = ("/" === url.charAt(0) || url.match(/^[\w\+\.\-]+:/) ? "" : config.baseUrl) + url
}
return config.urlArgs && !/^blob\:/.test(url) ? url + config.urlArgs(moduleName, url) : url
}, req.toUrl = function(moduleNamePlusExt) {
var ext, index = moduleNamePlusExt.lastIndexOf("."),
segment = moduleNamePlusExt.split("/")[0],
isRelative = "." === segment || ".." === segment;
return -1 !== index && (!isRelative || index > 1) && (ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length), moduleNamePlusExt = moduleNamePlusExt.substring(0, index)), req.nameToUrl(normalize(moduleNamePlusExt, relName), ext, !0)
}, req.defined = function(id) {
return makeMap(id, relName, !0).id in defined
}, req.specified = function(id) {
return (id = makeMap(id, relName, !0).id) in defined || id in deferreds
}, req
}
function resolve(name, d, value) {
name && (defined[name] = value, requirejs.onResourceLoad && requirejs.onResourceLoad(context, d.map, d.deps)), d.finished = !0, d.resolve(value)
}
function reject(d, err) {
d.finished = !0, d.rejected = !0, d.reject(err)
}
function makeNormalize(relName) {
return function(name) {
return normalize(name, relName, !0)
}
}
function defineModule(d) {
d.factoryCalled = !0;
var ret, name = d.map.id;
try {
ret = context.execCb(name, d.factory, d.values, defined[name])
} catch (err) {
return reject(d, err)
}
name ? ret === undef && (d.cjsModule ? ret = d.cjsModule.exports : d.usingExports && (ret = defined[name])) : requireDeferreds.splice(requireDeferreds.indexOf(d), 1), resolve(name, d, ret)
}
function depFinished(val, i) {
this.rejected || this.depDefined[i] || (this.depDefined[i] = !0, this.depCount += 1, this.values[i] = val, this.depending || this.depCount !== this.depMax || defineModule(this))
}
function makeDefer(name, calculatedMap) {
var d = {};
return d.promise = new Promise(function(resolve, reject) {
d.resolve = resolve, d.reject = function(err) {
name || requireDeferreds.splice(requireDeferreds.indexOf(d), 1), reject(err)
}
}), d.map = name ? calculatedMap || makeMap(name) : {}, d.depCount = 0, d.depMax = 0, d.values = [], d.depDefined = [], d.depFinished = depFinished, d.map.pr && (d.deps = [makeMap(d.map.pr)]), d
}
function getDefer(name, calculatedMap) {
var d;
return name ? (d = name in deferreds && deferreds[name]) || (d = deferreds[name] = makeDefer(name, calculatedMap)) : (d = makeDefer(), requireDeferreds.push(d)), d
}
function makeErrback(d, name) {
return function(err) {
d.rejected || (err.dynaId || (err.dynaId = "id" + (errCount += 1), err.requireModules = [name]), reject(d, err))
}
}
function waitForDep(depMap, relName, d, i) {
d.depMax += 1, callDep(depMap, relName).then(function(val) {
d.depFinished(val, i)
}, makeErrback(d, depMap.id)).catch(makeErrback(d, d.map.id))
}
function makeLoad(id) {
function load(value) {
fromTextCalled || resolve(id, getDefer(id), value)
}
var fromTextCalled;
return load.error = function(err) {
reject(getDefer(id), err)
}, load.fromText = function(text, textAlt) {
var execError, d = getDefer(id),
map = makeMap(makeMap(id).n),
plainId = map.id;
fromTextCalled = !0, d.factory = function(p, val) {
return val
}, textAlt && (text = textAlt), hasProp(config.config, id) && (config.config[plainId] = config.config[id]);
try {
req.exec(text)
} catch (e) {
execError = new Error("fromText eval for " + plainId + " failed: " + e), execError.requireType = "fromtexteval", reject(d, execError)
}
takeQueue(plainId), d.deps = [map], waitForDep(map, null, d, d.deps.length)
}, load
}
function callPlugin(plugin, map, relName) {
plugin.load(map.n, makeRequire(relName), makeLoad(map.id), config)
}
function splitPrefix(name) {
var prefix, index = name ? name.indexOf("!") : -1;
return index > -1 && (prefix = name.substring(0, index), name = name.substring(index + 1, name.length)), [prefix, name]
}
function breakCycle(d, traced, processed) {
var id = d.map.id;
traced[id] = !0, !d.finished && d.deps && d.deps.forEach(function(depMap) {
var depId = depMap.id,
dep = !hasProp(handlers, depId) && getDefer(depId, depMap);
!dep || dep.finished || processed[depId] || (hasProp(traced, depId) ? d.deps.forEach(function(depMap, i) {
depMap.id === depId && d.depFinished(defined[depId], i)
}) : breakCycle(dep, traced, processed))
}), processed[id] = !0
}
function check(d) {
var err, mid, dfd, notFinished = [],
waitInterval = 1e3 * config.waitSeconds,
expired = waitInterval && startTime + waitInterval < (new Date).getTime();
if (0 === loadCount && (d ? d.finished || breakCycle(d, {}, {}) : requireDeferreds.length && requireDeferreds.forEach(function(d) {
breakCycle(d, {}, {})
})), expired) {
for (mid in deferreds) dfd = deferreds[mid], dfd.finished || notFinished.push(dfd.map.id);
err = new Error("Timeout for modules: " + notFinished), err.requireModules = notFinished, err.requireType = "timeout", notFinished.forEach(function(id) {
reject(getDefer(id), err)
})
} else(loadCount || requireDeferreds.length) && (checkingLater || (checkingLater = !0, setTimeout(function() {
checkingLater = !1, check()
}, 70)))
}
function delayedError(e) {
return setTimeout(function() {
e.dynaId && trackedErrors[e.dynaId] || (trackedErrors[e.dynaId] = !0, req.onError(e))
}), e
}
var req, main, makeMap, callDep, handlers, checkingLater, load, context, defined = obj(),
waiting = obj(),
config = {
waitSeconds: 7,
baseUrl: "./",
paths: {},
bundles: {},
pkgs: {},
shim: {},
config: {}
},
mapCache = obj(),
requireDeferreds = [],
deferreds = obj(),
calledDefine = obj(),
calledPlugin = obj(),
loadCount = 0,
startTime = (new Date).getTime(),
errCount = 0,
trackedErrors = obj(),
urlFetched = obj(),
bundlesMap = obj(),
asyncResolve = Promise.resolve();
return load = "function" == typeof importScripts ? function(map) {
var url = map.url;
urlFetched[url] || (urlFetched[url] = !0, getDefer(map.id), importScripts(url), takeQueue(map.id))
} : function(map) {
var script, id = map.id,
url = map.url;
urlFetched[url] || (urlFetched[url] = !0, script = document.createElement("script"), script.setAttribute("data-requiremodule", id), script.type = config.scriptType || "text/javascript", script.charset = "utf-8", script.async = !0, loadCount += 1, script.addEventListener("load", function() {
loadCount -= 1, takeQueue(id)
}, !1), script.addEventListener("error", function() {
loadCount -= 1;
var err, pathConfig = getOwn(config.paths, id);
if (pathConfig && Array.isArray(pathConfig) && pathConfig.length > 1) {
script.parentNode.removeChild(script), pathConfig.shift();
var d = getDefer(id);
d.map = makeMap(id), d.map.url = req.nameToUrl(id), load(d.map)
} else err = new Error("Load failed: " + id + ": " + script.src), err.requireModules = [id], err.requireType = "scripterror", reject(getDefer(id), err)
}, !1), script.src = url, 10 === document.documentMode ? asap.then(function() {
document.head.appendChild(script)
}) : document.head.appendChild(script))
}, callDep = function(map, relName) {
var args, bundleId, name = map.id,
shim = config.shim[name];
if (name in waiting) args = waiting[name], delete waiting[name], main.apply(undef, args);
else if (!(name in deferreds))
if (map.pr) {
if (!(bundleId = getOwn(bundlesMap, name))) return callDep(makeMap(map.pr)).then(function(plugin) {
var newMap = map.prn ? map : makeMap(name, relName, !0),
newId = newMap.id,
shim = getOwn(config.shim, newId);
return newId in calledPlugin || (calledPlugin[newId] = !0, shim && shim.deps ? req(shim.deps, function() {
callPlugin(plugin, newMap, relName)
}) : callPlugin(plugin, newMap, relName)), getDefer(newId).promise
});
map.url = req.nameToUrl(bundleId), load(map)
} else shim && shim.deps ? req(shim.deps, function() {
load(map)
}) : load(map);
return getDefer(name).promise
}, makeMap = function(name, relName, applyMap) {
if ("string" != typeof name) return name;
var plugin, url, parts, prefix, result, prefixNormalized, cacheKey = name + " & " + (relName || "") + " & " + !!applyMap;
return parts = splitPrefix(name), prefix = parts[0], name = parts[1], !prefix && cacheKey in mapCache ? mapCache[cacheKey] : (prefix && (prefix = normalize(prefix, relName, applyMap), plugin = prefix in defined && defined[prefix]), prefix ? plugin && plugin.normalize ? (name = plugin.normalize(name, makeNormalize(relName)), prefixNormalized = !0) : name = -1 === name.indexOf("!") ? normalize(name, relName, applyMap) : name : (name = normalize(name, relName, applyMap), parts = splitPrefix(name), prefix = parts[0], name = parts[1], url = req.nameToUrl(name)), result = {
id: prefix ? prefix + "!" + name : name,
n: name,
pr: prefix,
url: url,
prn: prefix && prefixNormalized
}, prefix || (mapCache[cacheKey] = result), result)
}, handlers = {
require: function(name) {
return makeRequire(name)
},
exports: function(name) {
var e = defined[name];
return void 0 !== e ? e : defined[name] = {}
},
module: function(name) {
return {
id: name,
uri: "",
exports: handlers.exports(name),
config: function() {
return getOwn(config.config, name) || {}
}
}
}
}, main = function(name, deps, factory, errback, relName) {
if (name) {
if (name in calledDefine) return;
calledDefine[name] = !0
}
var d = getDefer(name);
return deps && !Array.isArray(deps) && (factory = deps, deps = []), deps = deps ? slice.call(deps, 0) : null, errback || (hasProp(config, "defaultErrback") ? config.defaultErrback && (errback = config.defaultErrback) : errback = delayedError), errback && d.promise.catch(errback), relName = relName || name, "function" == typeof factory ? (!deps.length && factory.length && (factory.toString().replace(commentRegExp, commentReplace).replace(cjsRequireRegExp, function(match, dep) {
deps.push(dep)
}), deps = (1 === factory.length ? ["require"] : ["require", "exports", "module"]).concat(deps)), d.factory = factory, d.deps = deps, d.depending = !0, deps.forEach(function(depName, i) {
var depMap;
deps[i] = depMap = makeMap(depName, relName, !0), depName = depMap.id, "require" === depName ? d.values[i] = handlers.require(name) : "exports" === depName ? (d.values[i] = handlers.exports(name), d.usingExports = !0) : "module" === depName ? d.values[i] = d.cjsModule = handlers.module(name) : void 0 === depName ? d.values[i] = void 0 : waitForDep(depMap, relName, d, i)
}), d.depending = !1, d.depCount === d.depMax && defineModule(d)) : name && resolve(name, d, factory), startTime = (new Date).getTime(), name || check(d), d.promise
}, req = makeRequire(null, !0), req.config = function(cfg) {
if (cfg.context && cfg.context !== contextName) {
var existingContext = getOwn(contexts, cfg.context);
return existingContext ? existingContext.req.config(cfg) : newContext(cfg.context).config(cfg)
}
if (mapCache = obj(), cfg.baseUrl && "/" !== cfg.baseUrl.charAt(cfg.baseUrl.length - 1) && (cfg.baseUrl += "/"), "string" == typeof cfg.urlArgs) {
var urlArgs = cfg.urlArgs;
cfg.urlArgs = function(id, url) {
return (-1 === url.indexOf("?") ? "?" : "&") + urlArgs
}
}
var shim = config.shim,
objs = {
paths: !0,
bundles: !0,
config: !0,
map: !0
};
return eachProp(cfg, function(value, prop) {
objs[prop] ? (config[prop] || (config[prop] = {}), mixin(config[prop], value, !0, !0)) : config[prop] = value
}), cfg.bundles && eachProp(cfg.bundles, function(value, prop) {
value.forEach(function(v) {
v !== prop && (bundlesMap[v] = prop)
})
}), cfg.shim && (eachProp(cfg.shim, function(value, id) {
Array.isArray(value) && (value = {
deps: value
}), !value.exports && !value.init || value.exportsFn || (value.exportsFn = makeShimExports(value)), shim[id] = value
}), config.shim = shim), cfg.packages && cfg.packages.forEach(function(pkgObj) {
var location, name;
pkgObj = "string" == typeof pkgObj ? {
name: pkgObj
} : pkgObj, name = pkgObj.name, location = pkgObj.location, location && (config.paths[name] = pkgObj.location), config.pkgs[name] = pkgObj.name + "/" + (pkgObj.main || "main").replace(currDirRegExp, "").replace(jsSuffixRegExp, "")
}), (cfg.deps || cfg.callback) && req(cfg.deps, cfg.callback), req
}, req.onError = function(err) {
throw err
}, context = {
id: contextName,
defined: defined,
waiting: waiting,
config: config,
deferreds: deferreds,
req: req,
execCb: function(name, callback, args, exports) {
return callback.apply(exports, args)
}
}, contexts[contextName] = context, req
}
if (!Promise) throw new Error("No Promise implementation available");
var topReq, dataMain, src, subPath, bootstrapConfig = requirejs || require,
hasOwn = Object.prototype.hasOwnProperty,
contexts = {},
queue = [],
currDirRegExp = /^\.\//,
urlRegExp = /^\/|\:|\?|\.js$/,
commentRegExp = /\/\*[\s\S]*?\*\/|([^:"'=]|^)\/\/.*$/gm,
cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,
jsSuffixRegExp = /\.js$/,
slice = Array.prototype.slice;
if ("function" != typeof requirejs) {
var asap = Promise.resolve(void 0);
requirejs = topReq = newContext("_"), "function" != typeof require && (require = topReq), topReq.exec = function(text) {
return eval(text)
}, topReq.contexts = contexts, define = function() {
queue.push(slice.call(arguments, 0))
}, define.amd = {
jQuery: !0
}, bootstrapConfig && topReq.config(bootstrapConfig), topReq.isBrowser && !contexts._.config.skipDataMain && (dataMain = document.querySelectorAll("script[data-main]")[0], (dataMain = dataMain && dataMain.getAttribute("data-main")) && (dataMain = dataMain.replace(jsSuffixRegExp, ""), bootstrapConfig && bootstrapConfig.baseUrl || -1 !== dataMain.indexOf("!") || (src = dataMain.split("/"), dataMain = src.pop(), subPath = src.length ? src.join("/") + "/" : "./", topReq.config({
baseUrl: subPath
})), topReq([dataMain])))
}
}(this, "undefined" != typeof Promise ? Promise : void 0);

View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<title>testing my-element</title>
<script src="build/document-register-element.js"></script>
<script src="test/my-element.js"></script>
</head>
<body>
<my-element>
some content
</my-element>
</body>

View file

@ -0,0 +1,246 @@
/*! (C) WebReflection Mit Style License */
(function(e, t, n, r) {
"use strict";
function rt(e, t) {
for (var n = 0, r = e.length; n < r; n++) vt(e[n], t)
}
function it(e) {
for (var t = 0, n = e.length, r; t < n; t++) r = e[t], nt(r, b[ot(r)])
}
function st(e) {
return function(t) {
j(t) && (vt(t, e), rt(t.querySelectorAll(w), e))
}
}
function ot(e) {
var t = e.getAttribute("is"),
n = e.nodeName.toUpperCase(),
r = S.call(y, t ? v + t.toUpperCase() : d + n);
return t && -1 < r && !ut(n, t) ? -1 : r
}
function ut(e, t) {
return -1 < w.indexOf(e + '[is="' + t + '"]')
}
function at(e) {
var t = e.currentTarget,
n = e.attrChange,
r = e.attrName,
i = e.target;
Q && (!i || i === t) && t.attributeChangedCallback && r !== "style" && e.prevValue !== e.newValue && t.attributeChangedCallback(r, n === e[a] ? null : e.prevValue, n === e[l] ? null : e.newValue)
}
function ft(e) {
var t = st(e);
return function(e) {
X.push(t, e.target)
}
}
function lt(e) {
K && (K = !1, e.currentTarget.removeEventListener(h, lt)), rt((e.target || t).querySelectorAll(w), e.detail === o ? o : s), B && pt()
}
function ct(e, t) {
var n = this;
q.call(n, e, t), G.call(n, {
target: n
})
}
function ht(e, t) {
D(e, t), et ? et.observe(e, z) : (J && (e.setAttribute = ct, e[i] = Z(e), e.addEventListener(p, G)), e.addEventListener(c, at)), e.createdCallback && Q && (e.created = !0, e.createdCallback(), e.created = !1)
}
function pt() {
for (var e, t = 0, n = F.length; t < n; t++) e = F[t], E.contains(e) || (n--, F.splice(t--, 1), vt(e, o))
}
function dt(e) {
throw new Error("A " + e + " type is already registered")
}
function vt(e, t) {
var n, r = ot(e); - 1 < r && (tt(e, b[r]), r = 0, t === s && !e[s] ? (e[o] = !1, e[s] = !0, r = 1, B && S.call(F, e) < 0 && F.push(e)) : t === o && !e[o] && (e[s] = !1, e[o] = !0, r = 1), r && (n = e[t + "Callback"]) && n.call(e))
}
if (r in t) return;
var i = "__" + r + (Math.random() * 1e5 >> 0),
s = "attached",
o = "detached",
u = "extends",
a = "ADDITION",
f = "MODIFICATION",
l = "REMOVAL",
c = "DOMAttrModified",
h = "DOMContentLoaded",
p = "DOMSubtreeModified",
d = "<",
v = "=",
m = /^[A-Z][A-Z0-9]*(?:-[A-Z0-9]+)+$/,
g = ["ANNOTATION-XML", "COLOR-PROFILE", "FONT-FACE", "FONT-FACE-SRC", "FONT-FACE-URI", "FONT-FACE-FORMAT", "FONT-FACE-NAME", "MISSING-GLYPH"],
y = [],
b = [],
w = "",
E = t.documentElement,
S = y.indexOf || function(e) {
for (var t = this.length; t-- && this[t] !== e;);
return t
},
x = n.prototype,
T = x.hasOwnProperty,
N = x.isPrototypeOf,
C = n.defineProperty,
k = n.getOwnPropertyDescriptor,
L = n.getOwnPropertyNames,
A = n.getPrototypeOf,
O = n.setPrototypeOf,
M = !!n.__proto__,
_ = n.create || function mt(e) {
return e ? (mt.prototype = e, new mt) : this
},
D = O || (M ? function(e, t) {
return e.__proto__ = t, e
} : L && k ? function() {
function e(e, t) {
for (var n, r = L(t), i = 0, s = r.length; i < s; i++) n = r[i], T.call(e, n) || C(e, n, k(t, n))
}
return function(t, n) {
do e(t, n); while ((n = A(n)) && !N.call(n, t));
return t
}
}() : function(e, t) {
for (var n in t) e[n] = t[n];
return e
}),
P = e.MutationObserver || e.WebKitMutationObserver,
H = (e.HTMLElement || e.Element || e.Node).prototype,
B = !N.call(H, E),
j = B ? function(e) {
return e.nodeType === 1
} : function(e) {
return N.call(H, e)
},
F = B && [],
I = H.cloneNode,
q = H.setAttribute,
R = H.removeAttribute,
U = t.createElement,
z = P && {
attributes: !0,
characterData: !0,
attributeOldValue: !0
},
W = P || function(e) {
J = !1, E.removeEventListener(c, W)
},
X, V = e.requestAnimationFrame || e.webkitRequestAnimationFrame || e.mozRequestAnimationFrame || e.msRequestAnimationFrame || function(e) {
setTimeout(e, 10)
},
$ = !1,
J = !0,
K = !0,
Q = !0,
G, Y, Z, et, tt, nt;
O || M ? (tt = function(e, t) {
N.call(t, e) || ht(e, t)
}, nt = ht) : (tt = function(e, t) {
e[i] || (e[i] = n(!0), ht(e, t))
}, nt = tt), B ? (J = !1, function() {
var e = k(H, "addEventListener"),
t = e.value,
n = function(e) {
var t = new CustomEvent(c, {
bubbles: !0
});
t.attrName = e, t.prevValue = this.getAttribute(e), t.newValue = null, t[l] = t.attrChange = 2, R.call(this, e), this.dispatchEvent(t)
},
r = function(e, t) {
var n = this.hasAttribute(e),
r = n && this.getAttribute(e),
i = new CustomEvent(c, {
bubbles: !0
});
q.call(this, e, t), i.attrName = e, i.prevValue = n ? r : null, i.newValue = t, n ? i[f] = i.attrChange = 1 : i[a] = i.attrChange = 0, this.dispatchEvent(i)
},
s = function(e) {
var t = e.currentTarget,
n = t[i],
r = e.propertyName,
s;
n.hasOwnProperty(r) && (n = n[r], s = new CustomEvent(c, {
bubbles: !0
}), s.attrName = n.name, s.prevValue = n.value || null, s.newValue = n.value = t[r] || null, s.prevValue == null ? s[a] = s.attrChange = 0 : s[f] = s.attrChange = 1, t.dispatchEvent(s))
};
e.value = function(e, o, u) {
e === c && this.attributeChangedCallback && this.setAttribute !== r && (this[i] = {
className: {
name: "class",
value: this.className
}
}, this.setAttribute = r, this.removeAttribute = n, t.call(this, "propertychange", s)), t.call(this, e, o, u)
}, C(H, "addEventListener", e)
}()) : P || (E.addEventListener(c, W), E.setAttribute(i, 1), E.removeAttribute(i), J && (G = function(e) {
var t = this,
n, r, s;
if (t === e.target) {
n = t[i], t[i] = r = Z(t);
for (s in r) {
if (!(s in n)) return Y(0, t, s, n[s], r[s], a);
if (r[s] !== n[s]) return Y(1, t, s, n[s], r[s], f)
}
for (s in n)
if (!(s in r)) return Y(2, t, s, n[s], r[s], l)
}
}, Y = function(e, t, n, r, i, s) {
var o = {
attrChange: e,
currentTarget: t,
attrName: n,
prevValue: r,
newValue: i
};
o[s] = e, at(o)
}, Z = function(e) {
for (var t, n, r = {}, i = e.attributes, s = 0, o = i.length; s < o; s++) t = i[s], n = t.name, n !== "setAttribute" && (r[n] = t.value);
return r
})), t[r] = function(n, r) {
c = n.toUpperCase(), $ || ($ = !0, P ? (et = function(e, t) {
function n(e, t) {
for (var n = 0, r = e.length; n < r; t(e[n++]));
}
return new P(function(r) {
for (var i, s, o, u = 0, a = r.length; u < a; u++) i = r[u], i.type === "childList" ? (n(i.addedNodes, e), n(i.removedNodes, t)) : (s = i.target, Q && s.attributeChangedCallback && i.attributeName !== "style" && (o = s.getAttribute(i.attributeName), o !== i.oldValue && s.attributeChangedCallback(i.attributeName, i.oldValue, o)))
})
}(st(s), st(o)), et.observe(t, {
childList: !0,
subtree: !0
})) : (X = [], V(function E() {
while (X.length) X.shift().call(null, X.shift());
V(E)
}), t.addEventListener("DOMNodeInserted", ft(s)), t.addEventListener("DOMNodeRemoved", ft(o))), t.addEventListener(h, lt), t.addEventListener("readystatechange", lt), t.createElement = function(e, n) {
var r = U.apply(t, arguments),
i = "" + e,
s = S.call(y, (n ? v : d) + (n || i).toUpperCase()),
o = -1 < s;
return n && (r.setAttribute("is", n = n.toLowerCase()), o && (o = ut(i.toUpperCase(), n))), Q = !t.createElement.innerHTMLHelper, o && nt(r, b[s]), r
}, H.cloneNode = function(e) {
var t = I.call(this, !!e),
n = ot(t);
return -1 < n && nt(t, b[n]), e && it(t.querySelectorAll(w)), t
}), -2 < S.call(y, v + c) + S.call(y, d + c) && dt(n);
if (!m.test(c) || -1 < S.call(g, c)) throw new Error("The type " + n + " is invalid");
var i = function() {
return f ? t.createElement(l, c) : t.createElement(l)
},
a = r || x,
f = T.call(a, u),
l = f ? r[u].toUpperCase() : c,
c, p;
return f && -1 < S.call(y, d + l) && dt(l), p = y.push((f ? v : d) + c) - 1, w = w.concat(w.length ? "," : "", f ? l + '[is="' + n.toLowerCase() + '"]' : l), i.prototype = b[p] = T.call(a, "prototype") ? a.prototype : _(H), rt(t.querySelectorAll(w), s), i
}
})(window, document, Object, "registerElement");

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,231 @@
define(["apiclientcore", "localassetmanager"], function(ApiClient, localassetmanager) {
"use strict";
function isLocalId(str) {
return startsWith(str, localPrefix)
}
function isLocalViewId(str) {
return startsWith(str, localViewPrefix)
}
function isTopLevelLocalViewId(str) {
return "localview" === str
}
function stripLocalPrefix(str) {
var res = stripStart(str, localPrefix);
return res = stripStart(res, localViewPrefix)
}
function startsWith(str, find) {
return !!(str && find && str.length > find.length && 0 === str.indexOf(find))
}
function stripStart(str, find) {
return startsWith(str, find) ? str.substr(find.length) : str
}
function createEmptyList() {
return {
Items: [],
TotalRecordCount: 0
}
}
function convertGuidToLocal(guid) {
return guid ? isLocalId(guid) ? guid : "local:" + guid : null
}
function adjustGuidProperties(downloadedItem) {
downloadedItem.Id = convertGuidToLocal(downloadedItem.Id), downloadedItem.SeriesId = convertGuidToLocal(downloadedItem.SeriesId), downloadedItem.SeasonId = convertGuidToLocal(downloadedItem.SeasonId), downloadedItem.AlbumId = convertGuidToLocal(downloadedItem.AlbumId), downloadedItem.ParentId = convertGuidToLocal(downloadedItem.ParentId), downloadedItem.ParentThumbItemId = convertGuidToLocal(downloadedItem.ParentThumbItemId), downloadedItem.ParentPrimaryImageItemId = convertGuidToLocal(downloadedItem.ParentPrimaryImageItemId), downloadedItem.PrimaryImageItemId = convertGuidToLocal(downloadedItem.PrimaryImageItemId), downloadedItem.ParentLogoItemId = convertGuidToLocal(downloadedItem.ParentLogoItemId), downloadedItem.ParentBackdropItemId = convertGuidToLocal(downloadedItem.ParentBackdropItemId), downloadedItem.ParentBackdropImageTags = null
}
function getLocalView(instance, serverId, userId) {
return instance.getLocalFolders(serverId, userId).then(function(views) {
var localView = null;
return views.length > 0 && (localView = {
Name: instance.downloadsTitleText || "Downloads",
ServerId: serverId,
Id: "localview",
Type: "localview",
IsFolder: !0
}), Promise.resolve(localView)
})
}
function ApiClientEx(serverAddress, clientName, applicationVersion, deviceName, deviceId, devicePixelRatio) {
ApiClient.call(this, serverAddress, clientName, applicationVersion, deviceName, deviceId, devicePixelRatio)
}
var localPrefix = "local:",
localViewPrefix = "localview:";
return Object.assign(ApiClientEx.prototype, ApiClient.prototype), ApiClientEx.prototype.getPlaybackInfo = function(itemId, options, deviceProfile) {
var onFailure = function() {
return ApiClient.prototype.getPlaybackInfo.call(instance, itemId, options, deviceProfile)
};
if (isLocalId(itemId)) return localassetmanager.getLocalItem(this.serverId(), stripLocalPrefix(itemId)).then(function(item) {
return {
MediaSources: item.Item.MediaSources.map(function(m) {
return m.SupportsDirectPlay = !0, m.SupportsDirectStream = !1, m.SupportsTranscoding = !1, m.IsLocal = !0, m
})
}
}, onFailure);
var instance = this;
return localassetmanager.getLocalItem(this.serverId(), itemId).then(function(item) {
if (item) {
var mediaSources = item.Item.MediaSources.map(function(m) {
return m.SupportsDirectPlay = !0, m.SupportsDirectStream = !1, m.SupportsTranscoding = !1, m.IsLocal = !0, m
});
return localassetmanager.fileExists(item.LocalPath).then(function(exists) {
if (exists) {
var res = {
MediaSources: mediaSources
};
return Promise.resolve(res)
}
return ApiClient.prototype.getPlaybackInfo.call(instance, itemId, options, deviceProfile)
}, onFailure)
}
return ApiClient.prototype.getPlaybackInfo.call(instance, itemId, options, deviceProfile)
}, onFailure)
}, ApiClientEx.prototype.getItems = function(userId, options) {
var i, serverInfo = this.serverInfo();
if (serverInfo && "localview" === options.ParentId) return this.getLocalFolders(serverInfo.Id, userId).then(function(items) {
var result = {
Items: items,
TotalRecordCount: items.length
};
return Promise.resolve(result)
});
if (serverInfo && options && (isLocalId(options.ParentId) || isLocalId(options.SeriesId) || isLocalId(options.SeasonId) || isLocalViewId(options.ParentId) || isLocalId(options.AlbumIds))) return localassetmanager.getViewItems(serverInfo.Id, userId, options).then(function(items) {
items.forEach(function(item) {
adjustGuidProperties(item)
});
var result = {
Items: items,
TotalRecordCount: items.length
};
return Promise.resolve(result)
});
if (options && options.ExcludeItemIds && options.ExcludeItemIds.length) {
var exItems = options.ExcludeItemIds.split(",");
for (i = 0; i < exItems.length; i++)
if (isLocalId(exItems[i])) return Promise.resolve(createEmptyList())
} else if (options && options.Ids && options.Ids.length) {
var ids = options.Ids.split(","),
hasLocal = !1;
for (i = 0; i < ids.length; i++) isLocalId(ids[i]) && (hasLocal = !0);
if (hasLocal) return localassetmanager.getItemsFromIds(serverInfo.Id, ids).then(function(items) {
items.forEach(function(item) {
adjustGuidProperties(item)
});
var result = {
Items: items,
TotalRecordCount: items.length
};
return Promise.resolve(result)
})
}
return ApiClient.prototype.getItems.call(this, userId, options)
}, ApiClientEx.prototype.getUserViews = function(options, userId) {
var instance = this;
options = options || {};
var basePromise = ApiClient.prototype.getUserViews.call(instance, options, userId);
return options.enableLocalView ? basePromise.then(function(result) {
var serverInfo = instance.serverInfo();
return serverInfo ? getLocalView(instance, serverInfo.Id, userId).then(function(localView) {
return localView && (result.Items.push(localView), result.TotalRecordCount++), Promise.resolve(result)
}) : Promise.resolve(result)
}) : basePromise
}, ApiClientEx.prototype.getItem = function(userId, itemId) {
if (!itemId) throw new Error("null itemId");
itemId && (itemId = itemId.toString());
var serverInfo;
return isTopLevelLocalViewId(itemId) && (serverInfo = this.serverInfo()) ? getLocalView(this, serverInfo.Id, userId) : isLocalViewId(itemId) && (serverInfo = this.serverInfo()) ? this.getLocalFolders(serverInfo.Id, userId).then(function(items) {
var views = items.filter(function(item) {
return item.Id === itemId
});
return views.length > 0 ? Promise.resolve(views[0]) : Promise.reject()
}) : isLocalId(itemId) && (serverInfo = this.serverInfo()) ? localassetmanager.getLocalItem(serverInfo.Id, stripLocalPrefix(itemId)).then(function(item) {
return adjustGuidProperties(item.Item), Promise.resolve(item.Item)
}) : ApiClient.prototype.getItem.call(this, userId, itemId)
}, ApiClientEx.prototype.getLocalFolders = function(userId) {
var serverInfo = this.serverInfo();
return userId = userId || serverInfo.UserId, localassetmanager.getViews(serverInfo.Id, userId)
}, ApiClientEx.prototype.getNextUpEpisodes = function(options) {
return options.SeriesId && isLocalId(options.SeriesId) ? Promise.resolve(createEmptyList()) : ApiClient.prototype.getNextUpEpisodes.call(this, options)
}, ApiClientEx.prototype.getSeasons = function(itemId, options) {
return isLocalId(itemId) ? (options.SeriesId = itemId, options.IncludeItemTypes = "Season", options.SortBy = "SortName", this.getItems(this.getCurrentUserId(), options)) : ApiClient.prototype.getSeasons.call(this, itemId, options)
}, ApiClientEx.prototype.getEpisodes = function(itemId, options) {
return isLocalId(options.SeasonId) || isLocalId(options.seasonId) ? (options.SeriesId = itemId, options.IncludeItemTypes = "Episode", options.SortBy = "SortName", this.getItems(this.getCurrentUserId(), options)) : isLocalId(itemId) ? (options.SeriesId = itemId, options.IncludeItemTypes = "Episode", options.SortBy = "SortName", this.getItems(this.getCurrentUserId(), options)) : ApiClient.prototype.getEpisodes.call(this, itemId, options)
}, ApiClientEx.prototype.getLatestOfflineItems = function(options) {
options.SortBy = "DateCreated", options.SortOrder = "Descending";
var serverInfo = this.serverInfo();
return serverInfo ? localassetmanager.getViewItems(serverInfo.Id, null, options).then(function(items) {
return items.forEach(function(item) {
adjustGuidProperties(item)
}), Promise.resolve(items)
}) : Promise.resolve([])
}, ApiClientEx.prototype.getThemeMedia = function(userId, itemId, inherit) {
return isLocalViewId(itemId) || isLocalId(itemId) || isTopLevelLocalViewId(itemId) ? Promise.reject() : ApiClient.prototype.getThemeMedia.call(this, userId, itemId, inherit)
}, ApiClientEx.prototype.getSpecialFeatures = function(userId, itemId) {
return isLocalId(itemId) ? Promise.resolve([]) : ApiClient.prototype.getSpecialFeatures.call(this, userId, itemId)
}, ApiClientEx.prototype.getSimilarItems = function(itemId, options) {
return isLocalId(itemId) ? Promise.resolve(createEmptyList()) : ApiClient.prototype.getSimilarItems.call(this, itemId, options)
}, ApiClientEx.prototype.updateFavoriteStatus = function(userId, itemId, isFavorite) {
return isLocalId(itemId) ? Promise.resolve() : ApiClient.prototype.updateFavoriteStatus.call(this, userId, itemId, isFavorite)
}, ApiClientEx.prototype.getScaledImageUrl = function(itemId, options) {
if (isLocalId(itemId) || options && options.itemid && isLocalId(options.itemid)) {
var serverInfo = this.serverInfo(),
id = stripLocalPrefix(itemId);
return localassetmanager.getImageUrl(serverInfo.Id, id, options)
}
return ApiClient.prototype.getScaledImageUrl.call(this, itemId, options)
}, ApiClientEx.prototype.reportPlaybackStart = function(options) {
if (!options) throw new Error("null options");
return isLocalId(options.ItemId) ? Promise.resolve() : ApiClient.prototype.reportPlaybackStart.call(this, options)
}, ApiClientEx.prototype.reportPlaybackProgress = function(options) {
if (!options) throw new Error("null options");
if (isLocalId(options.ItemId)) {
var serverInfo = this.serverInfo();
return serverInfo ? localassetmanager.getLocalItem(serverInfo.Id, stripLocalPrefix(options.ItemId)).then(function(item) {
var libraryItem = item.Item;
return "Video" === libraryItem.MediaType || "AudioBook" === libraryItem.Type ? (libraryItem.UserData = libraryItem.UserData || {}, libraryItem.UserData.PlaybackPositionTicks = options.PositionTicks, libraryItem.UserData.PlayedPercentage = Math.min(libraryItem.RunTimeTicks ? (options.PositionTicks || 0) / libraryItem.RunTimeTicks * 100 : 0, 100), localassetmanager.addOrUpdateLocalItem(item)) : Promise.resolve()
}) : Promise.resolve()
}
return ApiClient.prototype.reportPlaybackProgress.call(this, options)
}, ApiClientEx.prototype.reportPlaybackStopped = function(options) {
if (!options) throw new Error("null options");
if (isLocalId(options.ItemId)) {
var serverInfo = this.serverInfo(),
action = {
Date: (new Date).getTime(),
ItemId: stripLocalPrefix(options.ItemId),
PositionTicks: options.PositionTicks,
ServerId: serverInfo.Id,
Type: 0,
UserId: this.getCurrentUserId()
};
return localassetmanager.recordUserAction(action)
}
return ApiClient.prototype.reportPlaybackStopped.call(this, options)
}, ApiClientEx.prototype.getIntros = function(itemId) {
return isLocalId(itemId) ? Promise.resolve({
Items: [],
TotalRecordCount: 0
}) : ApiClient.prototype.getIntros.call(this, itemId)
}, ApiClientEx.prototype.getInstantMixFromItem = function(itemId, options) {
return isLocalId(itemId) ? Promise.resolve({
Items: [],
TotalRecordCount: 0
}) : ApiClient.prototype.getInstantMixFromItem.call(this, itemId, options)
}, ApiClientEx.prototype.getItemDownloadUrl = function(itemId) {
if (isLocalId(itemId)) {
var serverInfo = this.serverInfo();
if (serverInfo) return localassetmanager.getLocalItem(serverInfo.Id, stripLocalPrefix(itemId)).then(function(item) {
return Promise.resolve(item.LocalPath)
})
}
return ApiClient.prototype.getItemDownloadUrl.call(this, itemId)
}, ApiClientEx
});

View file

@ -0,0 +1,23 @@
define([], function() {
"use strict";
function MyStore() {}
function updateCache(instance) {
instance.cache.put("data", new Response(JSON.stringify(instance.localData)))
}
return MyStore.prototype.init = function() {
var instance = this;
return caches.open("embydata").then(function(result) {
instance.cache = result, instance.localData = {}
})
}, MyStore.prototype.setItem = function(name, value) {
if (this.localData) {
this.localData[name] !== value && (this.localData[name] = value, updateCache(this))
}
}, MyStore.prototype.getItem = function(name) {
if (this.localData) return this.localData[name]
}, MyStore.prototype.removeItem = function(name) {
this.localData && (this.localData[name] = null, delete this.localData[name], updateCache(this))
}, new MyStore
});

View file

@ -0,0 +1,37 @@
define([], function() {
"use strict";
function onCachePutFail(e) {
console.log(e)
}
function updateCache(instance) {
var cache = instance.cache;
cache && cache.put("data", new Response(JSON.stringify(instance.localData))).catch(onCachePutFail)
}
function onCacheOpened(result) {
this.cache = result, this.localData = {}
}
function MyStore() {
try {
self.caches && caches.open("embydata").then(onCacheOpened.bind(this))
} catch (err) {
console.log("Error opening cache: " + err)
}
}
return MyStore.prototype.setItem = function(name, value) {
localStorage.setItem(name, value);
var localData = this.localData;
if (localData) {
localData[name] !== value && (localData[name] = value, updateCache(this))
}
}, MyStore.prototype.getItem = function(name) {
return localStorage.getItem(name)
}, MyStore.prototype.removeItem = function(name) {
localStorage.removeItem(name);
var localData = this.localData;
localData && (localData[name] = null, delete localData[name], updateCache(this))
}, new MyStore
});

View file

@ -0,0 +1,14 @@
define([], function() {
"use strict";
function MyStore() {
this.localData = {}
}
return MyStore.prototype.setItem = function(name, value) {
this.localData[name] = value
}, MyStore.prototype.getItem = function(name) {
return this.localData[name]
}, MyStore.prototype.removeItem = function(name) {
this.localData[name] = null
}, new MyStore
});

View file

@ -0,0 +1,8 @@
define([], function() {
"use strict";
function CameraRoll() {}
return CameraRoll.prototype.getFiles = function() {
return Promise.resolve([])
}, new CameraRoll
});

View file

@ -0,0 +1,744 @@
define(["events", "apiclient", "appStorage"], function(events, apiClientFactory, appStorage) {
"use strict";
function getServerAddress(server, mode) {
switch (mode) {
case ConnectionMode.Local:
return server.LocalAddress;
case ConnectionMode.Manual:
return server.ManualAddress;
case ConnectionMode.Remote:
return server.RemoteAddress;
default:
return server.ManualAddress || server.LocalAddress || server.RemoteAddress
}
}
function paramsToString(params) {
var values = [];
for (var key in params) {
var value = params[key];
null !== value && void 0 !== value && "" !== value && values.push(encodeURIComponent(key) + "=" + encodeURIComponent(value))
}
return values.join("&")
}
function resolveFailure(instance, resolve) {
resolve({
State: "Unavailable",
ConnectUser: instance.connectUser()
})
}
function mergeServers(credentialProvider, list1, list2) {
for (var i = 0, length = list2.length; i < length; i++) credentialProvider.addOrUpdateServer(list1, list2[i]);
return list1
}
function updateServerInfo(server, systemInfo) {
server.Name = systemInfo.ServerName, systemInfo.Id && (server.Id = systemInfo.Id), systemInfo.LocalAddress && (server.LocalAddress = systemInfo.LocalAddress), systemInfo.WanAddress && (server.RemoteAddress = systemInfo.WanAddress)
}
function getEmbyServerUrl(baseUrl, handler) {
return baseUrl + "/emby/" + handler
}
function getFetchPromise(request) {
var headers = request.headers || {};
"json" === request.dataType && (headers.accept = "application/json");
var fetchRequest = {
headers: headers,
method: request.type,
credentials: "same-origin"
},
contentType = request.contentType;
return request.data && ("string" == typeof request.data ? fetchRequest.body = request.data : (fetchRequest.body = paramsToString(request.data), contentType = contentType || "application/x-www-form-urlencoded; charset=UTF-8")), contentType && (headers["Content-Type"] = contentType), request.timeout ? fetchWithTimeout(request.url, fetchRequest, request.timeout) : fetch(request.url, fetchRequest)
}
function fetchWithTimeout(url, options, timeoutMs) {
return console.log("fetchWithTimeout: timeoutMs: " + timeoutMs + ", url: " + url), new Promise(function(resolve, reject) {
var timeout = setTimeout(reject, timeoutMs);
options = options || {}, options.credentials = "same-origin", fetch(url, options).then(function(response) {
clearTimeout(timeout), console.log("fetchWithTimeout: succeeded connecting to url: " + url), resolve(response)
}, function(error) {
clearTimeout(timeout), console.log("fetchWithTimeout: timed out connecting to url: " + url), reject()
})
})
}
function ajax(request) {
if (!request) throw new Error("Request cannot be null");
return request.headers = request.headers || {}, console.log("ConnectionManager requesting url: " + request.url), getFetchPromise(request).then(function(response) {
return console.log("ConnectionManager response status: " + response.status + ", url: " + request.url), response.status < 400 ? "json" === request.dataType || "application/json" === request.headers.accept ? response.json() : response : Promise.reject(response)
}, function(err) {
throw console.log("ConnectionManager request failed to url: " + request.url), err
})
}
function getConnectUrl(handler) {
return "https://connect.emby.media/service/" + handler
}
function replaceAll(originalString, strReplace, strWith) {
var reg = new RegExp(strReplace, "ig");
return originalString.replace(reg, strWith)
}
function normalizeAddress(address) {
return address = address.trim(), 0 !== address.toLowerCase().indexOf("http") && (address = "http://" + address), address = replaceAll(address, "Http:", "http:"), address = replaceAll(address, "Https:", "https:")
}
function stringEqualsIgnoreCase(str1, str2) {
return (str1 || "").toLowerCase() === (str2 || "").toLowerCase()
}
function compareVersions(a, b) {
a = a.split("."), b = b.split(".");
for (var i = 0, length = Math.max(a.length, b.length); i < length; i++) {
var aVal = parseInt(a[i] || "0"),
bVal = parseInt(b[i] || "0");
if (aVal < bVal) return -1;
if (aVal > bVal) return 1
}
return 0
}
var defaultTimeout = 2e4,
ConnectionMode = {
Local: 0,
Remote: 1,
Manual: 2
},
ConnectionManager = function(credentialProvider, appName, appVersion, deviceName, deviceId, capabilities, devicePixelRatio) {
function onConnectUserSignIn(user) {
connectUser = user, events.trigger(self, "connectusersignedin", [user])
}
function onAuthenticated(apiClient, result, options, saveCredentials) {
var credentials = credentialProvider.credentials(),
servers = credentials.Servers.filter(function(s) {
return s.Id === result.ServerId
}),
server = servers.length ? servers[0] : apiClient.serverInfo();
return !1 !== options.updateDateLastAccessed && (server.DateLastAccessed = (new Date).getTime()), server.Id = result.ServerId, saveCredentials ? (server.UserId = result.User.Id, server.AccessToken = result.AccessToken) : (server.UserId = null, server.AccessToken = null), credentialProvider.addOrUpdateServer(credentials.Servers, server), credentialProvider.credentials(credentials), apiClient.enableAutomaticBitrateDetection = options.enableAutomaticBitrateDetection, apiClient.serverInfo(server), afterConnected(apiClient, options), onLocalUserSignIn(server, apiClient.serverAddress(), result.User)
}
function afterConnected(apiClient, options) {
options = options || {}, !1 !== options.reportCapabilities && apiClient.reportCapabilities(capabilities), apiClient.enableAutomaticBitrateDetection = options.enableAutomaticBitrateDetection, !1 !== options.enableWebSocket && (console.log("calling apiClient.ensureWebSocket"), apiClient.ensureWebSocket())
}
function onLocalUserSignIn(server, serverUrl, user) {
return self._getOrAddApiClient(server, serverUrl), (self.onLocalUserSignedIn ? self.onLocalUserSignedIn.call(self, user) : Promise.resolve()).then(function() {
events.trigger(self, "localusersignedin", [user])
})
}
function ensureConnectUser(credentials) {
return connectUser && connectUser.Id === credentials.ConnectUserId ? Promise.resolve() : credentials.ConnectUserId && credentials.ConnectAccessToken ? (connectUser = null, getConnectUser(credentials.ConnectUserId, credentials.ConnectAccessToken).then(function(user) {
return onConnectUserSignIn(user), Promise.resolve()
}, function() {
return Promise.resolve()
})) : Promise.resolve()
}
function getConnectUser(userId, accessToken) {
if (!userId) throw new Error("null userId");
if (!accessToken) throw new Error("null accessToken");
return ajax({
type: "GET",
url: "https://connect.emby.media/service/user?id=" + userId,
dataType: "json",
headers: {
"X-Application": appName + "/" + appVersion,
"X-Connect-UserToken": accessToken
}
})
}
function addAuthenticationInfoFromConnect(server, serverUrl, credentials) {
if (!server.ExchangeToken) throw new Error("server.ExchangeToken cannot be null");
if (!credentials.ConnectUserId) throw new Error("credentials.ConnectUserId cannot be null");
var url = getEmbyServerUrl(serverUrl, "Connect/Exchange?format=json&ConnectUserId=" + credentials.ConnectUserId),
auth = 'MediaBrowser Client="' + appName + '", Device="' + deviceName + '", DeviceId="' + deviceId + '", Version="' + appVersion + '"';
return ajax({
type: "GET",
url: url,
dataType: "json",
headers: {
"X-MediaBrowser-Token": server.ExchangeToken,
"X-Emby-Authorization": auth
}
}).then(function(auth) {
return server.UserId = auth.LocalUserId, server.AccessToken = auth.AccessToken, auth
}, function() {
return server.UserId = null, server.AccessToken = null, Promise.reject()
})
}
function validateAuthentication(server, serverUrl) {
return ajax({
type: "GET",
url: getEmbyServerUrl(serverUrl, "System/Info"),
dataType: "json",
headers: {
"X-MediaBrowser-Token": server.AccessToken
}
}).then(function(systemInfo) {
return updateServerInfo(server, systemInfo), Promise.resolve()
}, function() {
return server.UserId = null, server.AccessToken = null, Promise.resolve()
})
}
function getImageUrl(localUser) {
if (connectUser && connectUser.ImageUrl) return {
url: connectUser.ImageUrl
};
if (localUser && localUser.PrimaryImageTag) {
return {
url: self.getApiClient(localUser).getUserImageUrl(localUser.Id, {
tag: localUser.PrimaryImageTag,
type: "Primary"
}),
supportsParams: !0
}
}
return {
url: null,
supportsParams: !1
}
}
function logoutOfServer(apiClient) {
var serverInfo = apiClient.serverInfo() || {},
logoutInfo = {
serverId: serverInfo.Id
};
return apiClient.logout().then(function() {
events.trigger(self, "localusersignedout", [logoutInfo])
}, function() {
events.trigger(self, "localusersignedout", [logoutInfo])
})
}
function getConnectServers(credentials) {
return console.log("Begin getConnectServers"), credentials.ConnectAccessToken && credentials.ConnectUserId ? ajax({
type: "GET",
url: "https://connect.emby.media/service/servers?userId=" + credentials.ConnectUserId,
dataType: "json",
headers: {
"X-Application": appName + "/" + appVersion,
"X-Connect-UserToken": credentials.ConnectAccessToken
}
}).then(function(servers) {
return servers.map(function(i) {
return {
ExchangeToken: i.AccessKey,
ConnectServerId: i.Id,
Id: i.SystemId,
Name: i.Name,
RemoteAddress: i.Url,
LocalAddress: i.LocalAddress,
UserLinkType: "guest" === (i.UserType || "").toLowerCase() ? "Guest" : "LinkedUser"
}
})
}, function() {
return credentials.Servers.slice(0).filter(function(s) {
return s.ExchangeToken
})
}) : Promise.resolve([])
}
function filterServers(servers, connectServers) {
return servers.filter(function(server) {
return !server.ExchangeToken || connectServers.filter(function(connectServer) {
return server.Id === connectServer.Id
}).length > 0
})
}
function findServers() {
return new Promise(function(resolve, reject) {
var onFinish = function(foundServers) {
var servers = foundServers.map(function(foundServer) {
var info = {
Id: foundServer.Id,
LocalAddress: convertEndpointAddressToManualAddress(foundServer) || foundServer.Address,
Name: foundServer.Name
};
return info.LastConnectionMode = info.ManualAddress ? ConnectionMode.Manual : ConnectionMode.Local, info
});
resolve(servers)
};
require(["serverdiscovery"], function(serverDiscovery) {
serverDiscovery.findServers(1e3).then(onFinish, function() {
onFinish([])
})
})
})
}
function convertEndpointAddressToManualAddress(info) {
if (info.Address && info.EndpointAddress) {
var address = info.EndpointAddress.split(":")[0],
parts = info.Address.split(":");
if (parts.length > 1) {
var portString = parts[parts.length - 1];
isNaN(parseInt(portString)) || (address += ":" + portString)
}
return normalizeAddress(address)
}
return null
}
function getTryConnectPromise(url, connectionMode, state, resolve, reject) {
console.log("getTryConnectPromise " + url), ajax({
url: getEmbyServerUrl(url, "system/info/public"),
timeout: defaultTimeout,
type: "GET",
dataType: "json"
}).then(function(result) {
state.resolved || (state.resolved = !0, console.log("Reconnect succeeded to " + url), resolve({
url: url,
connectionMode: connectionMode,
data: result
}))
}, function() {
state.resolved || (console.log("Reconnect failed to " + url), ++state.rejects >= state.numAddresses && reject())
})
}
function tryReconnect(serverInfo) {
var addresses = [],
addressesStrings = [];
return !serverInfo.manualAddressOnly && serverInfo.LocalAddress && -1 === addressesStrings.indexOf(serverInfo.LocalAddress) && (addresses.push({
url: serverInfo.LocalAddress,
mode: ConnectionMode.Local,
timeout: 0
}), addressesStrings.push(addresses[addresses.length - 1].url)), serverInfo.ManualAddress && -1 === addressesStrings.indexOf(serverInfo.ManualAddress) && (addresses.push({
url: serverInfo.ManualAddress,
mode: ConnectionMode.Manual,
timeout: 100
}), addressesStrings.push(addresses[addresses.length - 1].url)), !serverInfo.manualAddressOnly && serverInfo.RemoteAddress && -1 === addressesStrings.indexOf(serverInfo.RemoteAddress) && (addresses.push({
url: serverInfo.RemoteAddress,
mode: ConnectionMode.Remote,
timeout: 200
}), addressesStrings.push(addresses[addresses.length - 1].url)), console.log("tryReconnect: " + addressesStrings.join("|")), new Promise(function(resolve, reject) {
var state = {};
state.numAddresses = addresses.length, state.rejects = 0, addresses.map(function(url) {
setTimeout(function() {
state.resolved || getTryConnectPromise(url.url, url.mode, state, resolve, reject)
}, url.timeout)
})
})
}
function onSuccessfulConnection(server, systemInfo, connectionMode, serverUrl, options, resolve) {
var credentials = credentialProvider.credentials();
options = options || {}, credentials.ConnectAccessToken && !1 !== options.enableAutoLogin ? ensureConnectUser(credentials).then(function() {
server.ExchangeToken ? addAuthenticationInfoFromConnect(server, serverUrl, credentials).then(function() {
afterConnectValidated(server, credentials, systemInfo, connectionMode, serverUrl, !0, options, resolve)
}, function() {
afterConnectValidated(server, credentials, systemInfo, connectionMode, serverUrl, !0, options, resolve)
}) : afterConnectValidated(server, credentials, systemInfo, connectionMode, serverUrl, !0, options, resolve)
}) : afterConnectValidated(server, credentials, systemInfo, connectionMode, serverUrl, !0, options, resolve)
}
function afterConnectValidated(server, credentials, systemInfo, connectionMode, serverUrl, verifyLocalAuthentication, options, resolve) {
if (options = options || {}, !1 === options.enableAutoLogin) server.UserId = null, server.AccessToken = null;
else if (verifyLocalAuthentication && server.AccessToken && !1 !== options.enableAutoLogin) return void validateAuthentication(server, serverUrl).then(function() {
afterConnectValidated(server, credentials, systemInfo, connectionMode, serverUrl, !1, options, resolve)
});
updateServerInfo(server, systemInfo), server.LastConnectionMode = connectionMode, !1 !== options.updateDateLastAccessed && (server.DateLastAccessed = (new Date).getTime()), credentialProvider.addOrUpdateServer(credentials.Servers, server), credentialProvider.credentials(credentials);
var result = {
Servers: []
};
result.ApiClient = self._getOrAddApiClient(server, serverUrl), result.ApiClient.setSystemInfo(systemInfo), result.State = server.AccessToken && !1 !== options.enableAutoLogin ? "SignedIn" : "ServerSignIn", result.Servers.push(server), result.ApiClient.enableAutomaticBitrateDetection = options.enableAutomaticBitrateDetection, result.ApiClient.updateServerInfo(server, serverUrl);
var resolveActions = function() {
resolve(result), events.trigger(self, "connected", [result])
};
"SignedIn" === result.State ? (afterConnected(result.ApiClient, options), result.ApiClient.getCurrentUser().then(function(user) {
onLocalUserSignIn(server, serverUrl, user).then(resolveActions, resolveActions)
}, resolveActions)) : resolveActions()
}
function getCacheKey(feature, apiClient, options) {
options = options || {};
var viewOnly = options.viewOnly,
cacheKey = "regInfo-" + apiClient.serverId();
return viewOnly && (cacheKey += "-viewonly"), cacheKey
}
function addAppInfoToConnectRequest(request) {
request.headers = request.headers || {}, request.headers["X-Application"] = appName + "/" + appVersion
}
function exchangePin(pinInfo) {
if (!pinInfo) throw new Error("pinInfo cannot be null");
var request = {
type: "POST",
url: getConnectUrl("pin/authenticate"),
data: {
deviceId: pinInfo.DeviceId,
pin: pinInfo.Pin
},
dataType: "json"
};
return addAppInfoToConnectRequest(request), ajax(request)
}
console.log("Begin ConnectionManager constructor");
var self = this;
this._apiClients = [];
var connectUser;
self.connectUser = function() {
return connectUser
}, self._minServerVersion = "3.2.33", self.appVersion = function() {
return appVersion
}, self.appName = function() {
return appName
}, self.capabilities = function() {
return capabilities
}, self.deviceId = function() {
return deviceId
}, self.credentialProvider = function() {
return credentialProvider
}, self.connectUserId = function() {
return credentialProvider.credentials().ConnectUserId
}, self.connectToken = function() {
return credentialProvider.credentials().ConnectAccessToken
}, self.getServerInfo = function(id) {
return credentialProvider.credentials().Servers.filter(function(s) {
return s.Id === id
})[0]
}, self.getLastUsedServer = function() {
var servers = credentialProvider.credentials().Servers;
return servers.sort(function(a, b) {
return (b.DateLastAccessed || 0) - (a.DateLastAccessed || 0)
}), servers.length ? servers[0] : null
}, self.addApiClient = function(apiClient) {
self._apiClients.push(apiClient);
var existingServers = credentialProvider.credentials().Servers.filter(function(s) {
return stringEqualsIgnoreCase(s.ManualAddress, apiClient.serverAddress()) || stringEqualsIgnoreCase(s.LocalAddress, apiClient.serverAddress()) || stringEqualsIgnoreCase(s.RemoteAddress, apiClient.serverAddress())
}),
existingServer = existingServers.length ? existingServers[0] : apiClient.serverInfo();
if (existingServer.DateLastAccessed = (new Date).getTime(), existingServer.LastConnectionMode = ConnectionMode.Manual, existingServer.ManualAddress = apiClient.serverAddress(), apiClient.manualAddressOnly && (existingServer.manualAddressOnly = !0), apiClient.serverInfo(existingServer), apiClient.onAuthenticated = function(instance, result) {
return onAuthenticated(instance, result, {}, !0)
}, !existingServers.length) {
var credentials = credentialProvider.credentials();
credentials.Servers = [existingServer], credentialProvider.credentials(credentials)
}
events.trigger(self, "apiclientcreated", [apiClient])
}, self.clearData = function() {
console.log("connection manager clearing data"), connectUser = null;
var credentials = credentialProvider.credentials();
credentials.ConnectAccessToken = null, credentials.ConnectUserId = null, credentials.Servers = [], credentialProvider.credentials(credentials)
}, self._getOrAddApiClient = function(server, serverUrl) {
var apiClient = self.getApiClient(server.Id);
return apiClient || (apiClient = new apiClientFactory(serverUrl, appName, appVersion, deviceName, deviceId, devicePixelRatio), self._apiClients.push(apiClient), apiClient.serverInfo(server), apiClient.onAuthenticated = function(instance, result) {
return onAuthenticated(instance, result, {}, !0)
}, events.trigger(self, "apiclientcreated", [apiClient])), console.log("returning instance from getOrAddApiClient"), apiClient
}, self.getOrCreateApiClient = function(serverId) {
var credentials = credentialProvider.credentials(),
servers = credentials.Servers.filter(function(s) {
return stringEqualsIgnoreCase(s.Id, serverId)
});
if (!servers.length) throw new Error("Server not found: " + serverId);
var server = servers[0];
return self._getOrAddApiClient(server, getServerAddress(server, server.LastConnectionMode))
}, self.user = function(apiClient) {
return new Promise(function(resolve, reject) {
function onLocalUserDone(e) {
var image = getImageUrl(localUser);
resolve({
localUser: localUser,
name: connectUser ? connectUser.Name : localUser ? localUser.Name : null,
imageUrl: image.url,
supportsImageParams: image.supportsParams,
connectUser: connectUser
})
}
function onEnsureConnectUserDone() {
apiClient && apiClient.getCurrentUserId() ? apiClient.getCurrentUser().then(function(u) {
localUser = u, onLocalUserDone()
}, onLocalUserDone) : onLocalUserDone()
}
var localUser, credentials = credentialProvider.credentials();
!credentials.ConnectUserId || !credentials.ConnectAccessToken || apiClient && apiClient.getCurrentUserId() ? onEnsureConnectUserDone() : ensureConnectUser(credentials).then(onEnsureConnectUserDone, onEnsureConnectUserDone)
})
}, self.logout = function() {
console.log("begin connectionManager loguot");
for (var promises = [], i = 0, length = self._apiClients.length; i < length; i++) {
var apiClient = self._apiClients[i];
apiClient.accessToken() && promises.push(logoutOfServer(apiClient))
}
return Promise.all(promises).then(function() {
for (var credentials = credentialProvider.credentials(), servers = credentials.Servers.filter(function(u) {
return "Guest" !== u.UserLinkType
}), j = 0, numServers = servers.length; j < numServers; j++) {
var server = servers[j];
server.UserId = null, server.AccessToken = null, server.ExchangeToken = null
}
credentials.Servers = servers, credentials.ConnectAccessToken = null, credentials.ConnectUserId = null, credentialProvider.credentials(credentials), connectUser && (connectUser = null, events.trigger(self, "connectusersignedout"))
})
}, self.getSavedServers = function() {
var credentials = credentialProvider.credentials(),
servers = credentials.Servers.slice(0);
return servers.sort(function(a, b) {
return (b.DateLastAccessed || 0) - (a.DateLastAccessed || 0)
}), servers
}, self.getAvailableServers = function() {
console.log("Begin getAvailableServers");
var credentials = credentialProvider.credentials();
return Promise.all([getConnectServers(credentials), findServers()]).then(function(responses) {
var connectServers = responses[0],
foundServers = responses[1],
servers = credentials.Servers.slice(0);
return mergeServers(credentialProvider, servers, foundServers), mergeServers(credentialProvider, servers, connectServers), servers = filterServers(servers, connectServers), servers.sort(function(a, b) {
return (b.DateLastAccessed || 0) - (a.DateLastAccessed || 0)
}), credentials.Servers = servers, credentialProvider.credentials(credentials), servers
})
}, self.connectToServers = function(servers, options) {
console.log("Begin connectToServers, with " + servers.length + " servers");
var firstServer = servers.length ? servers[0] : null;
return firstServer ? self.connectToServer(firstServer, options).then(function(result) {
return "Unavailable" === result.State && (result.State = "ServerSelection"), console.log("resolving connectToServers with result.State: " + result.State), result
}) : Promise.resolve({
Servers: servers,
State: servers.length || self.connectUser() ? "ServerSelection" : "ConnectSignIn",
ConnectUser: self.connectUser()
})
}, self.connectToServer = function(server, options) {
return console.log("begin connectToServer"), new Promise(function(resolve, reject) {
options = options || {}, tryReconnect(server).then(function(result) {
var serverUrl = result.url,
connectionMode = result.connectionMode;
result = result.data, 1 === compareVersions(self.minServerVersion(), result.Version) ? (console.log("minServerVersion requirement not met. Server version: " + result.Version), resolve({
State: "ServerUpdateNeeded",
Servers: [server]
})) : server.Id && result.Id !== server.Id ? (console.log("http request succeeded, but found a different server Id than what was expected"), resolveFailure(self, resolve)) : onSuccessfulConnection(server, result, connectionMode, serverUrl, options, resolve)
}, function() {
resolveFailure(self, resolve)
})
})
}, self.connectToAddress = function(address, options) {
function onFail() {
return console.log("connectToAddress " + address + " failed"), Promise.resolve({
State: "Unavailable",
ConnectUser: instance.connectUser()
})
}
if (!address) return Promise.reject();
address = normalizeAddress(address);
var instance = this,
server = {
ManualAddress: address,
LastConnectionMode: ConnectionMode.Manual
};
return self.connectToServer(server, options).catch(onFail)
}, self.loginToConnect = function(username, password) {
return username && password ? ajax({
type: "POST",
url: "https://connect.emby.media/service/user/authenticate",
data: {
nameOrEmail: username,
rawpw: password
},
dataType: "json",
contentType: "application/x-www-form-urlencoded; charset=UTF-8",
headers: {
"X-Application": appName + "/" + appVersion
}
}).then(function(result) {
var credentials = credentialProvider.credentials();
return credentials.ConnectAccessToken = result.AccessToken, credentials.ConnectUserId = result.User.Id, credentialProvider.credentials(credentials), onConnectUserSignIn(result.User), result
}) : Promise.reject()
}, self.signupForConnect = function(options) {
var email = options.email,
username = options.username,
password = options.password,
passwordConfirm = options.passwordConfirm;
if (!email) return Promise.reject({
errorCode: "invalidinput"
});
if (!username) return Promise.reject({
errorCode: "invalidinput"
});
if (!password) return Promise.reject({
errorCode: "invalidinput"
});
if (!passwordConfirm) return Promise.reject({
errorCode: "passwordmatch"
});
if (password !== passwordConfirm) return Promise.reject({
errorCode: "passwordmatch"
});
var data = {
email: email,
userName: username,
rawpw: password
};
return options.grecaptcha && (data.grecaptcha = options.grecaptcha), ajax({
type: "POST",
url: "https://connect.emby.media/service/register",
data: data,
dataType: "json",
contentType: "application/x-www-form-urlencoded; charset=UTF-8",
headers: {
"X-Application": appName + "/" + appVersion,
"X-CONNECT-TOKEN": "CONNECT-REGISTER"
}
}).catch(function(response) {
try {
return response.json()
} catch (err) {
throw err
}
}).then(function(result) {
if (result && result.Status) return "SUCCESS" === result.Status ? Promise.resolve(result) : Promise.reject({
errorCode: result.Status
});
Promise.reject()
})
}, self.getUserInvitations = function() {
var connectToken = self.connectToken();
if (!connectToken) throw new Error("null connectToken");
if (!self.connectUserId()) throw new Error("null connectUserId");
return ajax({
type: "GET",
url: "https://connect.emby.media/service/servers?userId=" + self.connectUserId() + "&status=Waiting",
dataType: "json",
headers: {
"X-Connect-UserToken": connectToken,
"X-Application": appName + "/" + appVersion
}
})
}, self.deleteServer = function(serverId) {
if (!serverId) throw new Error("null serverId");
var server = credentialProvider.credentials().Servers.filter(function(s) {
return s.Id === serverId
});
return server = server.length ? server[0] : null, new Promise(function(resolve, reject) {
function onDone() {
var credentials = credentialProvider.credentials();
credentials.Servers = credentials.Servers.filter(function(s) {
return s.Id !== serverId
}), credentialProvider.credentials(credentials), resolve()
}
if (!server.ConnectServerId) return void onDone();
var connectToken = self.connectToken(),
connectUserId = self.connectUserId();
if (!connectToken || !connectUserId) return void onDone();
ajax({
type: "DELETE",
url: "https://connect.emby.media/service/serverAuthorizations?serverId=" + server.ConnectServerId + "&userId=" + connectUserId,
headers: {
"X-Connect-UserToken": connectToken,
"X-Application": appName + "/" + appVersion
}
}).then(onDone, onDone)
})
}, self.rejectServer = function(serverId) {
var connectToken = self.connectToken();
if (!serverId) throw new Error("null serverId");
if (!connectToken) throw new Error("null connectToken");
if (!self.connectUserId()) throw new Error("null connectUserId");
var url = "https://connect.emby.media/service/serverAuthorizations?serverId=" + serverId + "&userId=" + self.connectUserId();
return fetch(url, {
method: "DELETE",
headers: {
"X-Connect-UserToken": connectToken,
"X-Application": appName + "/" + appVersion
}
})
}, self.acceptServer = function(serverId) {
var connectToken = self.connectToken();
if (!serverId) throw new Error("null serverId");
if (!connectToken) throw new Error("null connectToken");
if (!self.connectUserId()) throw new Error("null connectUserId");
return ajax({
type: "GET",
url: "https://connect.emby.media/service/ServerAuthorizations/accept?serverId=" + serverId + "&userId=" + self.connectUserId(),
headers: {
"X-Connect-UserToken": connectToken,
"X-Application": appName + "/" + appVersion
}
})
}, self.resetRegistrationInfo = function(apiClient) {
var cacheKey = getCacheKey("themes", apiClient, {
viewOnly: !0
});
appStorage.removeItem(cacheKey), cacheKey = getCacheKey("themes", apiClient, {
viewOnly: !1
}), appStorage.removeItem(cacheKey)
}, self.getRegistrationInfo = function(feature, apiClient, options) {
var cacheKey = getCacheKey(feature, apiClient, options);
appStorage.setItem(cacheKey, JSON.stringify({
lastValidDate: new Date().getTime(),
deviceId: self.deviceId()
}));
return Promise.resolve();
}, self.createPin = function() {
var request = {
type: "POST",
url: getConnectUrl("pin"),
data: {
deviceId: deviceId
},
dataType: "json"
};
return addAppInfoToConnectRequest(request), ajax(request)
}, self.getPinStatus = function(pinInfo) {
if (!pinInfo) throw new Error("pinInfo cannot be null");
var queryString = {
deviceId: pinInfo.DeviceId,
pin: pinInfo.Pin
},
request = {
type: "GET",
url: getConnectUrl("pin") + "?" + paramsToString(queryString),
dataType: "json"
};
return addAppInfoToConnectRequest(request), ajax(request)
}, self.exchangePin = function(pinInfo) {
if (!pinInfo) throw new Error("pinInfo cannot be null");
return exchangePin(pinInfo).then(function(result) {
var credentials = credentialProvider.credentials();
return credentials.ConnectAccessToken = result.AccessToken, credentials.ConnectUserId = result.UserId, credentialProvider.credentials(credentials), ensureConnectUser(credentials)
})
}
};
return ConnectionManager.prototype.connect = function(options) {
console.log("Begin connect");
var instance = this;
return instance.getAvailableServers().then(function(servers) {
return instance.connectToServers(servers, options)
})
}, ConnectionManager.prototype.isLoggedIntoConnect = function() {
return !(!this.connectToken() || !this.connectUserId())
}, ConnectionManager.prototype.getApiClients = function() {
for (var servers = this.getSavedServers(), i = 0, length = servers.length; i < length; i++) {
var server = servers[i];
server.Id && this._getOrAddApiClient(server, getServerAddress(server, server.LastConnectionMode))
}
return this._apiClients
}, ConnectionManager.prototype.getApiClient = function(item) {
if (!item) throw new Error("item or serverId cannot be null");
return item.ServerId && (item = item.ServerId), this._apiClients.filter(function(a) {
var serverInfo = a.serverInfo();
return !serverInfo || serverInfo.Id === item
})[0]
}, ConnectionManager.prototype.minServerVersion = function(val) {
return val && (this._minServerVersion = val), this._minServerVersion
}, ConnectionManager.prototype.handleMessageReceived = function(msg) {
var serverId = msg.ServerId;
if (serverId) {
var apiClient = this.getApiClient(serverId);
if (apiClient) {
if ("string" == typeof msg.Data) try {
msg.Data = JSON.parse(msg.Data)
} catch (err) {}
apiClient.handleMessageReceived(msg)
}
}
}, ConnectionManager
});

View file

@ -0,0 +1,29 @@
define(["events", "appStorage"], function(events, appStorage) {
"use strict";
function ensure(instance, data) {
if (!instance._credentials) {
var json = appStorage.getItem(instance.key) || "{}";
console.log("credentials initialized with: " + json), instance._credentials = JSON.parse(json), instance._credentials.Servers = instance._credentials.Servers || []
}
}
function set(instance, data) {
data ? (instance._credentials = data, appStorage.setItem(instance.key, JSON.stringify(data))) : instance.clear(), events.trigger(instance, "credentialsupdated")
}
function Credentials(key) {
this.key = key || "servercredentials3"
}
return Credentials.prototype.clear = function() {
this._credentials = null, appStorage.removeItem(this.key)
}, Credentials.prototype.credentials = function(data) {
return data && set(this, data), ensure(this), this._credentials
}, Credentials.prototype.addOrUpdateServer = function(list, server) {
if (!server.Id) throw new Error("Server.Id cannot be null or empty");
var existing = list.filter(function(s) {
return s.Id === server.Id
})[0];
return existing ? (existing.DateLastAccessed = Math.max(existing.DateLastAccessed || 0, server.DateLastAccessed || 0), existing.UserLinkType = server.UserLinkType, server.AccessToken && (existing.AccessToken = server.AccessToken, existing.UserId = server.UserId), server.ExchangeToken && (existing.ExchangeToken = server.ExchangeToken), server.RemoteAddress && (existing.RemoteAddress = server.RemoteAddress), server.ManualAddress && (existing.ManualAddress = server.ManualAddress), server.LocalAddress && (existing.LocalAddress = server.LocalAddress), server.Name && (existing.Name = server.Name), server.WakeOnLanInfos && server.WakeOnLanInfos.length && (existing.WakeOnLanInfos = server.WakeOnLanInfos), null != server.LastConnectionMode && (existing.LastConnectionMode = server.LastConnectionMode), server.ConnectServerId && (existing.ConnectServerId = server.ConnectServerId), existing) : (list.push(server), server)
}, Credentials
});

View file

@ -0,0 +1,30 @@
define([], function() {
"use strict";
function getCallbacks(obj, name) {
if (!obj) throw new Error("obj cannot be null!");
obj._callbacks = obj._callbacks || {};
var list = obj._callbacks[name];
return list || (obj._callbacks[name] = [], list = obj._callbacks[name]), list
}
return {
on: function(obj, eventName, fn) {
getCallbacks(obj, eventName).push(fn)
},
off: function(obj, eventName, fn) {
var list = getCallbacks(obj, eventName),
i = list.indexOf(fn); - 1 !== i && list.splice(i, 1)
},
trigger: function(obj, eventName) {
var eventObject = {
type: eventName
},
eventArgs = [];
eventArgs.push(eventObject);
for (var additionalArgs = arguments[2] || [], i = 0, length = additionalArgs.length; i < length; i++) eventArgs.push(additionalArgs[i]);
getCallbacks(obj, eventName).slice(0).forEach(function(c) {
c.apply(obj, eventArgs)
})
}
}
});

View file

@ -0,0 +1,8 @@
define([], function() {
"use strict";
function FileUpload() {}
return FileUpload.prototype.upload = function(file, url) {
return Promise.reject()
}, FileUpload
});

View file

@ -0,0 +1,406 @@
define(["filerepository", "itemrepository", "useractionrepository", "transfermanager"], function(filerepository, itemrepository, useractionrepository, transfermanager) {
"use strict";
function getLocalItem(serverId, itemId) {
return console.log("[lcoalassetmanager] Begin getLocalItem"), itemrepository.get(serverId, itemId)
}
function recordUserAction(action) {
return action.Id = createGuid(), useractionrepository.set(action.Id, action)
}
function getUserActions(serverId) {
return useractionrepository.getByServerId(serverId)
}
function deleteUserAction(action) {
return useractionrepository.remove(action.Id)
}
function deleteUserActions(actions) {
var results = [];
return actions.forEach(function(action) {
results.push(deleteUserAction(action))
}), Promise.all(results)
}
function getServerItems(serverId) {
return console.log("[localassetmanager] Begin getServerItems"), itemrepository.getAll(serverId)
}
function getItemsFromIds(serverId, ids) {
var actions = ids.map(function(id) {
var strippedId = stripStart(id, "local:");
return getLocalItem(serverId, strippedId)
});
return Promise.all(actions).then(function(items) {
var libItems = items.map(function(locItem) {
return locItem.Item
});
return Promise.resolve(libItems)
})
}
function getViews(serverId, userId) {
return itemrepository.getServerItemTypes(serverId, userId).then(function(types) {
var item, list = [];
return types.indexOf("Audio") > -1 && (item = {
Name: "Music",
ServerId: serverId,
Id: "localview:MusicView",
Type: "MusicView",
CollectionType: "music",
IsFolder: !0
}, list.push(item)), types.indexOf("Photo") > -1 && (item = {
Name: "Photos",
ServerId: serverId,
Id: "localview:PhotosView",
Type: "PhotosView",
CollectionType: "photos",
IsFolder: !0
}, list.push(item)), types.indexOf("Episode") > -1 && (item = {
Name: "TV",
ServerId: serverId,
Id: "localview:TVView",
Type: "TVView",
CollectionType: "tvshows",
IsFolder: !0
}, list.push(item)), types.indexOf("Movie") > -1 && (item = {
Name: "Movies",
ServerId: serverId,
Id: "localview:MoviesView",
Type: "MoviesView",
CollectionType: "movies",
IsFolder: !0
}, list.push(item)), types.indexOf("Video") > -1 && (item = {
Name: "Videos",
ServerId: serverId,
Id: "localview:VideosView",
Type: "VideosView",
CollectionType: "videos",
IsFolder: !0
}, list.push(item)), types.indexOf("MusicVideo") > -1 && (item = {
Name: "Music Videos",
ServerId: serverId,
Id: "localview:MusicVideosView",
Type: "MusicVideosView",
CollectionType: "videos",
IsFolder: !0
}, list.push(item)), Promise.resolve(list)
})
}
function updateFiltersForTopLevelView(parentId, mediaTypes, includeItemTypes, query) {
switch (parentId) {
case "MusicView":
return query.Recursive ? includeItemTypes.push("Audio") : includeItemTypes.push("MusicAlbum"), !0;
case "PhotosView":
return query.Recursive ? includeItemTypes.push("Photo") : includeItemTypes.push("PhotoAlbum"), !0;
case "TVView":
return query.Recursive ? includeItemTypes.push("Episode") : includeItemTypes.push("Series"), !0;
case "VideosView":
return query.Recursive, includeItemTypes.push("Video"), !0;
case "MoviesView":
return query.Recursive, includeItemTypes.push("Movie"), !0;
case "MusicVideosView":
return query.Recursive, includeItemTypes.push("MusicVideo"), !0
}
return !1
}
function normalizeId(id) {
return id ? (id = stripStart(id, "localview:"), id = stripStart(id, "local:")) : null
}
function normalizeIdList(val) {
return val ? val.split(",").map(normalizeId) : []
}
function shuffle(array) {
for (var temporaryValue, randomIndex, currentIndex = array.length; 0 !== currentIndex;) randomIndex = Math.floor(Math.random() * currentIndex), currentIndex -= 1, temporaryValue = array[currentIndex], array[currentIndex] = array[randomIndex], array[randomIndex] = temporaryValue;
return array
}
function sortItems(items, query) {
if (!query.SortBy || 0 === query.SortBy.length) return items;
if ("Random" === query.SortBy) return shuffle(items);
var sortSpec = getSortSpec(query);
return items.sort(function(a, b) {
for (var i = 0; i < sortSpec.length; i++) {
var result = compareValues(a, b, sortSpec[i].Field, sortSpec[i].OrderDescending);
if (0 !== result) return result
}
return 0
}), items
}
function compareValues(a, b, field, orderDesc) {
if (!a.hasOwnProperty(field) || !b.hasOwnProperty(field)) return 0;
var valA = a[field],
valB = b[field],
result = 0;
return "string" == typeof valA || "string" == typeof valB ? (valA = valA || "", valB = valB || "", result = valA.toLowerCase().localeCompare(valB.toLowerCase())) : valA > valB ? result = 1 : valA < valB && (result = -1), orderDesc && (result *= -1), result
}
function getSortSpec(query) {
for (var sortFields = (query.SortBy || "").split(","), sortOrders = (query.SortOrder || "").split(","), sortSpec = [], i = 0; i < sortFields.length; i++) {
var orderDesc = !1;
i < sortOrders.length && -1 !== sortOrders[i].toLowerCase().indexOf("desc") && (orderDesc = !0), sortSpec.push({
Field: sortFields[i],
OrderDescending: orderDesc
})
}
return sortSpec
}
function getViewItems(serverId, userId, options) {
var searchParentId = options.ParentId;
searchParentId = normalizeId(searchParentId);
var seasonId = normalizeId(options.SeasonId || options.seasonId),
seriesId = normalizeId(options.SeriesId || options.seriesId),
albumIds = normalizeIdList(options.AlbumIds || options.albumIds),
includeItemTypes = options.IncludeItemTypes ? options.IncludeItemTypes.split(",") : [],
filters = options.Filters ? options.Filters.split(",") : [],
mediaTypes = options.MediaTypes ? options.MediaTypes.split(",") : [];
return updateFiltersForTopLevelView(searchParentId, mediaTypes, includeItemTypes, options) && (searchParentId = null), getServerItems(serverId).then(function(items) {
var itemsMap = new Map,
subtreeIdSet = new Set;
if (items.forEach(function(item) {
item.Item.LocalChildren = [], itemsMap.set(item.Item.Id, item.Item)
}), itemsMap.forEach(function(item, ignored, ignored2) {
if (item.ParentId && itemsMap.has(item.ParentId)) {
itemsMap.get(item.ParentId).LocalChildren.push(item)
}
}), options.Recursive && searchParentId && itemsMap.has(searchParentId)) {
var addSubtreeIds = function(recurseItem) {
subtreeIdSet.has(recurseItem.Id) || subtreeIdSet.add(recurseItem.Id), recurseItem.LocalChildren.forEach(function(childItem) {
addSubtreeIds(childItem)
})
},
searchParentItem = itemsMap.get(searchParentId);
addSubtreeIds(searchParentItem)
}
var resultItems = items.filter(function(item) {
return (!item.SyncStatus || "synced" === item.SyncStatus) && ((!mediaTypes.length || -1 !== mediaTypes.indexOf(item.Item.MediaType || "")) && ((!seriesId || item.Item.SeriesId === seriesId) && ((!seasonId || item.Item.SeasonId === seasonId) && ((!albumIds.length || -1 !== albumIds.indexOf(item.Item.AlbumId || "")) && ((!item.Item.IsFolder || -1 === filters.indexOf("IsNotFolder")) && (!(!item.Item.IsFolder && -1 !== filters.indexOf("IsFolder")) && ((!includeItemTypes.length || -1 !== includeItemTypes.indexOf(item.Item.Type || "")) && (!searchParentId || (options.Recursive ? subtreeIdSet.has(item.Item.Id) : item.Item.ParentId === searchParentId)))))))))
}).map(function(item2) {
return item2.Item
});
return resultItems = sortItems(resultItems, options), options.Limit && (resultItems = resultItems.slice(0, options.Limit)), Promise.resolve(resultItems)
})
}
function removeObsoleteContainerItems(serverId) {
return getServerItems(serverId).then(function(items) {
var seriesItems = items.filter(function(item) {
return "series" === (item.Item.Type || "").toLowerCase()
}),
seasonItems = items.filter(function(item) {
return "season" === (item.Item.Type || "").toLowerCase()
}),
albumItems = items.filter(function(item) {
var type = (item.Item.Type || "").toLowerCase();
return "musicalbum" === type || "photoalbum" === type
}),
requiredSeriesIds = items.filter(function(item) {
return "episode" === (item.Item.Type || "").toLowerCase()
}).map(function(item2) {
return item2.Item.SeriesId
}).filter(filterDistinct),
requiredSeasonIds = items.filter(function(item) {
return "episode" === (item.Item.Type || "").toLowerCase()
}).map(function(item2) {
return item2.Item.SeasonId
}).filter(filterDistinct),
requiredAlbumIds = items.filter(function(item) {
var type = (item.Item.Type || "").toLowerCase();
return "audio" === type || "photo" === type
}).map(function(item2) {
return item2.Item.AlbumId
}).filter(filterDistinct),
obsoleteItems = [];
seriesItems.forEach(function(item) {
requiredSeriesIds.indexOf(item.Item.Id) < 0 && obsoleteItems.push(item)
}), seasonItems.forEach(function(item) {
requiredSeasonIds.indexOf(item.Item.Id) < 0 && obsoleteItems.push(item)
}), albumItems.forEach(function(item) {
requiredAlbumIds.indexOf(item.Item.Id) < 0 && obsoleteItems.push(item)
});
var p = Promise.resolve();
return obsoleteItems.forEach(function(item) {
p = p.then(function() {
return itemrepository.remove(item.ServerId, item.Id)
})
}), p
})
}
function removeLocalItem(localItem) {
return itemrepository.get(localItem.ServerId, localItem.Id).then(function(item) {
var onFileDeletedSuccessOrFail = function() {
return itemrepository.remove(localItem.ServerId, localItem.Id)
},
p = Promise.resolve();
return item.LocalPath && (p = p.then(function() {
return filerepository.deleteFile(item.LocalPath)
})), item && item.Item && item.Item.MediaSources && item.Item.MediaSources.forEach(function(mediaSource) {
mediaSource.MediaStreams && mediaSource.MediaStreams.length > 0 && mediaSource.MediaStreams.forEach(function(mediaStream) {
mediaStream.Path && (p = p.then(function() {
return filerepository.deleteFile(mediaStream.Path)
}))
})
}), p.then(onFileDeletedSuccessOrFail, onFileDeletedSuccessOrFail)
}, function(item) {
return Promise.resolve()
})
}
function addOrUpdateLocalItem(localItem) {
return itemrepository.set(localItem.ServerId, localItem.Id, localItem)
}
function getSubtitleSaveFileName(localItem, mediaPath, language, isForced, format) {
var name = getNameWithoutExtension(mediaPath);
name = filerepository.getValidFileName(name), language && (name += "." + language.toLowerCase()), isForced && (name += ".foreign"), name = name + "." + format.toLowerCase();
var mediaFolder = filerepository.getParentPath(localItem.LocalPath);
return filerepository.combinePath(mediaFolder, name)
}
function getItemFileSize(path) {
return filerepository.getItemFileSize(path)
}
function getNameWithoutExtension(path) {
var fileName = path,
pos = fileName.lastIndexOf(".");
return pos > 0 && (fileName = fileName.substring(0, pos)), fileName
}
function downloadFile(url, localItem) {
var imageUrl = getImageUrl(localItem.Item.ServerId, localItem.Item.Id, {
type: "Primary",
index: 0
});
return transfermanager.downloadFile(url, localItem, imageUrl)
}
function downloadSubtitles(url, fileName) {
return transfermanager.downloadSubtitles(url, fileName)
}
function getImageUrl(serverId, itemId, imageOptions) {
var imageType = imageOptions.type,
index = imageOptions.index,
pathArray = getImagePath(serverId, itemId, imageType, index);
return filerepository.getImageUrl(pathArray)
}
function hasImage(serverId, itemId, imageType, index) {
var pathArray = getImagePath(serverId, itemId, imageType, index),
localFilePath = filerepository.getFullMetadataPath(pathArray);
return filerepository.fileExists(localFilePath).then(function(exists) {
return Promise.resolve(exists)
}, function(err) {
return Promise.resolve(!1)
})
}
function fileExists(localFilePath) {
return filerepository.fileExists(localFilePath)
}
function downloadImage(localItem, url, serverId, itemId, imageType, index) {
var localPathParts = getImagePath(serverId, itemId, imageType, index);
return transfermanager.downloadImage(url, localPathParts)
}
function isDownloadFileInQueue(path) {
return transfermanager.isDownloadFileInQueue(path)
}
function getDownloadItemCount() {
return transfermanager.getDownloadItemCount()
}
function getDirectoryPath(item) {
var parts = [],
itemtype = item.Type.toLowerCase(),
mediaType = (item.MediaType || "").toLowerCase();
"episode" === itemtype || "series" === itemtype || "season" === itemtype ? parts.push("TV") : "video" === mediaType ? parts.push("Videos") : "audio" === itemtype || "musicalbum" === itemtype || "musicartist" === itemtype ? parts.push("Music") : "photo" === itemtype || "photoalbum" === itemtype ? parts.push("Photos") : "game" !== itemtype && "gamesystem" !== itemtype || parts.push("Games");
var albumArtist = item.AlbumArtist;
albumArtist && parts.push(albumArtist);
var seriesName = item.SeriesName;
seriesName && parts.push(seriesName);
var seasonName = item.SeasonName;
seasonName && parts.push(seasonName), item.Album && parts.push(item.Album), ("video" === mediaType && "episode" !== itemtype || "game" === itemtype || item.IsFolder) && parts.push(item.Name);
for (var finalParts = [], i = 0; i < parts.length; i++) finalParts.push(filerepository.getValidFileName(parts[i]));
return finalParts
}
function getImagePath(serverId, itemId, imageType, index) {
var parts = [];
parts.push("images"), index = index || 0, parts.push(itemId + "_" + imageType + "_" + index.toString());
for (var finalParts = [], i = 0; i < parts.length; i++) finalParts.push(parts[i]);
return finalParts
}
function getLocalFileName(item, originalFileName) {
var filename = originalFileName || item.Name;
return filerepository.getValidFileName(filename)
}
function resyncTransfers() {
return transfermanager.resyncTransfers()
}
function createGuid() {
var d = (new Date).getTime();
return window.performance && "function" == typeof window.performance.now && (d += performance.now()), "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
var r = (d + 16 * Math.random()) % 16 | 0;
return d = Math.floor(d / 16), ("x" === c ? r : 3 & r | 8).toString(16)
})
}
function startsWith(str, find) {
return !!(str && find && str.length > find.length && 0 === str.indexOf(find))
}
function stripStart(str, find) {
return startsWith(str, find) ? str.substr(find.length) : str
}
function filterDistinct(value, index, self) {
return self.indexOf(value) === index
}
function enableBackgroundCompletion() {
return transfermanager.enableBackgroundCompletion
}
return {
getLocalItem: getLocalItem,
getDirectoryPath: getDirectoryPath,
getLocalFileName: getLocalFileName,
recordUserAction: recordUserAction,
getUserActions: getUserActions,
deleteUserAction: deleteUserAction,
deleteUserActions: deleteUserActions,
removeLocalItem: removeLocalItem,
addOrUpdateLocalItem: addOrUpdateLocalItem,
downloadFile: downloadFile,
downloadSubtitles: downloadSubtitles,
hasImage: hasImage,
downloadImage: downloadImage,
getImageUrl: getImageUrl,
getSubtitleSaveFileName: getSubtitleSaveFileName,
getServerItems: getServerItems,
getItemFileSize: getItemFileSize,
isDownloadFileInQueue: isDownloadFileInQueue,
getDownloadItemCount: getDownloadItemCount,
getViews: getViews,
getViewItems: getViewItems,
resyncTransfers: resyncTransfers,
getItemsFromIds: getItemsFromIds,
removeObsoleteContainerItems: removeObsoleteContainerItems,
fileExists: fileExists,
enableBackgroundCompletion: enableBackgroundCompletion
}
});

View file

@ -0,0 +1,8 @@
define([], function() {
"use strict";
return {
findServers: function(timeoutMs) {
return Promise.resolve([])
}
}
});

View file

@ -0,0 +1,57 @@
define(["localassetmanager", "cameraRoll"], function(localAssetManager, cameraRoll) {
"use strict";
function getFilesToUpload(files, uploadHistory) {
return files.filter(function(file) {
if (!file) return !1;
var uploadId = getUploadId(file);
return 0 === uploadHistory.FilesUploaded.filter(function(u) {
return uploadId === u.Id
}).length
})
}
function getUploadId(file) {
return btoa(file.Id + "1")
}
function uploadNext(files, index, server, apiClient, resolve, reject) {
var length = files.length;
if (index >= length) return void resolve();
uploadFile(files[index], apiClient).then(function() {
uploadNext(files, index + 1, server, apiClient, resolve, reject)
}, function() {
uploadNext(files, index + 1, server, apiClient, resolve, reject)
})
}
function uploadFile(file, apiClient) {
return new Promise(function(resolve, reject) {
require(["fileupload"], function(FileUpload) {
var url = apiClient.getUrl("Devices/CameraUploads", {
DeviceId: apiClient.deviceId(),
Name: file.Name,
Album: "Camera Roll",
Id: getUploadId(file),
api_key: apiClient.accessToken()
});
console.log("Uploading file to " + url), (new FileUpload).upload(file, url).then(resolve, reject)
})
})
}
function ContentUploader() {}
return ContentUploader.prototype.uploadImages = function(connectionManager, server) {
return cameraRoll.getFiles().then(function(photos) {
if (!photos.length) return Promise.resolve();
var apiClient = connectionManager.getApiClient(server.Id);
return apiClient.getContentUploadHistory().then(function(uploadHistory) {
return photos = getFilesToUpload(photos, uploadHistory), console.log("Found " + photos.length + " files to upload"), new Promise(function(resolve, reject) {
uploadNext(photos, 0, server, apiClient, resolve, reject)
})
}, function() {
return Promise.resolve()
})
})
}, ContentUploader
});

View file

@ -0,0 +1,45 @@
define([], function() {
"use strict";
function getValidFileName(path) {
return path
}
function getFullLocalPath(pathArray) {
return pathArray.join("/")
}
function getPathFromArray(pathArray) {
return pathArray.join("/")
}
function deleteFile(path) {
return Promise.resolve()
}
function deleteDirectory(path) {
return Promise.resolve()
}
function fileExists(path) {
return Promise.resolve()
}
function getItemFileSize(path) {
return Promise.resolve(0)
}
function getImageUrl(pathParts) {
return pathParts.join("/")
}
return {
getValidFileName: getValidFileName,
getFullLocalPath: getFullLocalPath,
getPathFromArray: getPathFromArray,
deleteFile: deleteFile,
deleteDirectory: deleteDirectory,
fileExists: fileExists,
getItemFileSize: getItemFileSize,
getImageUrl: getImageUrl
}
});

View file

@ -0,0 +1,123 @@
define([], function() {
"use strict";
function ServerDatabase(dbName, readyCallback) {
var request = indexedDB.open(dbName, dbVersion);
request.onerror = function(event) {}, request.onupgradeneeded = function(event) {
var db = event.target.result;
db.createObjectStore(dbName).transaction.oncomplete = function(event) {
readyCallback(db)
}
}, request.onsuccess = function(event) {
var db = event.target.result;
readyCallback(db)
}
}
function getDbName(serverId) {
return "items_" + serverId
}
function getDb(serverId, callback) {
var dbName = getDbName(serverId),
db = databases[dbName];
if (db) return void callback(db);
new ServerDatabase(dbName, function(db) {
databases[dbName] = db, callback(db)
})
}
function getServerItemTypes(serverId, userId) {
return getAll(serverId, userId).then(function(all) {
return all.map(function(item2) {
return item2.Item.Type || ""
}).filter(filterDistinct)
})
}
function getAll(serverId, userId) {
return new Promise(function(resolve, reject) {
getDb(serverId, function(db) {
var request, storeName = getDbName(serverId),
transaction = db.transaction([storeName], "readonly"),
objectStore = transaction.objectStore(storeName);
if ("getAll" in objectStore) request = objectStore.getAll(null, 1e4), request.onsuccess = function(event) {
resolve(event.target.result)
};
else {
var results = [];
request = objectStore.openCursor(), request.onsuccess = function(event) {
var cursor = event.target.result;
cursor ? (results.push(cursor.value), cursor.continue()) : resolve(results)
}
}
request.onerror = reject
})
})
}
function get(serverId, key) {
return new Promise(function(resolve, reject) {
getDb(serverId, function(db) {
var storeName = getDbName(serverId),
transaction = db.transaction([storeName], "readonly"),
objectStore = transaction.objectStore(storeName),
request = objectStore.get(key);
request.onerror = reject, request.onsuccess = function(event) {
resolve(request.result)
}
})
})
}
function set(serverId, key, val) {
return new Promise(function(resolve, reject) {
getDb(serverId, function(db) {
var storeName = getDbName(serverId),
transaction = db.transaction([storeName], "readwrite"),
objectStore = transaction.objectStore(storeName),
request = objectStore.put(val, key);
request.onerror = reject, request.onsuccess = resolve
})
})
}
function remove(serverId, key) {
return new Promise(function(resolve, reject) {
getDb(serverId, function(db) {
var storeName = getDbName(serverId),
transaction = db.transaction([storeName], "readwrite"),
objectStore = transaction.objectStore(storeName),
request = objectStore.delete(key);
request.onerror = reject, request.onsuccess = resolve
})
})
}
function clear(serverId) {
return new Promise(function(resolve, reject) {
getDb(serverId, function(db) {
var storeName = getDbName(serverId),
transaction = db.transaction([storeName], "readwrite"),
objectStore = transaction.objectStore(storeName),
request = objectStore.clear();
request.onerror = reject, request.onsuccess = resolve
})
})
}
function filterDistinct(value, index, self) {
return self.indexOf(value) === index
}
var indexedDB = self.indexedDB || self.mozIndexedDB || self.webkitIndexedDB || self.msIndexedDB,
dbVersion = (self.IDBTransaction || self.webkitIDBTransaction || self.msIDBTransaction, self.IDBKeyRange || self.webkitIDBKeyRange || self.msIDBKeyRange, 1),
databases = {};
return {
get: get,
set: set,
remove: remove,
clear: clear,
getAll: getAll,
getServerItemTypes: getServerItemTypes
}
});

View file

@ -0,0 +1,17 @@
define(["connectionManager"], function(connectionManager) {
"use strict";
var isSyncing;
return {
sync: function(options) {
return console.log("localSync.sync starting..."), isSyncing ? Promise.resolve() : (isSyncing = !0, new Promise(function(resolve, reject) {
require(["multiserversync", "appSettings"], function(MultiServerSync, appSettings) {
options = options || {}, options.cameraUploadServers = appSettings.cameraUploadServers(), (new MultiServerSync).sync(connectionManager, options).then(function() {
isSyncing = null, resolve()
}, function(err) {
isSyncing = null, reject(err)
})
})
}))
}
}
});

View file

@ -0,0 +1,368 @@
define(["localassetmanager"], function(localassetmanager) {
"use strict";
function processDownloadStatus(apiClient, serverInfo, options) {
return console.log("[mediasync] Begin processDownloadStatus"), localassetmanager.resyncTransfers().then(function() {
return localassetmanager.getServerItems(serverInfo.Id).then(function(items) {
console.log("[mediasync] Begin processDownloadStatus getServerItems completed");
var p = Promise.resolve(),
cnt = 0;
return items.filter(function(item) {
return "transferring" === item.SyncStatus || "queued" === item.SyncStatus
}).forEach(function(item) {
p = p.then(function() {
return reportTransfer(apiClient, item)
}), cnt++
}), p.then(function() {
return console.log("[mediasync] Exit processDownloadStatus. Items reported: " + cnt.toString()), Promise.resolve()
})
})
})
}
function reportTransfer(apiClient, item) {
return localassetmanager.getItemFileSize(item.LocalPath).then(function(size) {
return size > 0 ? apiClient.reportSyncJobItemTransferred(item.SyncJobItemId).then(function() {
return item.SyncStatus = "synced", console.log("[mediasync] reportSyncJobItemTransferred called for " + item.LocalPath), localassetmanager.addOrUpdateLocalItem(item)
}, function(error) {
return console.error("[mediasync] Mediasync error on reportSyncJobItemTransferred", error), item.SyncStatus = "error", localassetmanager.addOrUpdateLocalItem(item)
}) : localassetmanager.isDownloadFileInQueue(item.LocalPath).then(function(result) {
return result ? Promise.resolve() : (console.log("[mediasync] reportTransfer: Size is 0 and download no longer in queue. Deleting item."), localassetmanager.removeLocalItem(item).then(function() {
return console.log("[mediasync] reportTransfer: Item deleted."), Promise.resolve()
}, function(err2) {
return console.log("[mediasync] reportTransfer: Failed to delete item.", err2), Promise.resolve()
}))
})
}, function(error) {
return console.error("[mediasync] reportTransfer: error on getItemFileSize. Deleting item.", error), localassetmanager.removeLocalItem(item).then(function() {
return console.log("[mediasync] reportTransfer: Item deleted."), Promise.resolve()
}, function(err2) {
return console.log("[mediasync] reportTransfer: Failed to delete item.", err2), Promise.resolve()
})
})
}
function reportOfflineActions(apiClient, serverInfo) {
return console.log("[mediasync] Begin reportOfflineActions"), localassetmanager.getUserActions(serverInfo.Id).then(function(actions) {
return actions.length ? apiClient.reportOfflineActions(actions).then(function() {
return localassetmanager.deleteUserActions(actions).then(function() {
return console.log("[mediasync] Exit reportOfflineActions (actions reported and deleted.)"), Promise.resolve()
})
}, function(err) {
return console.error("[mediasync] error on apiClient.reportOfflineActions: " + err.toString()), localassetmanager.deleteUserActions(actions)
}) : (console.log("[mediasync] Exit reportOfflineActions (no actions)"), Promise.resolve())
})
}
function syncData(apiClient, serverInfo) {
return console.log("[mediasync] Begin syncData"), localassetmanager.getServerItems(serverInfo.Id).then(function(items) {
var completedItems = items.filter(function(item) {
return item && ("synced" === item.SyncStatus || "error" === item.SyncStatus)
}),
request = {
TargetId: apiClient.deviceId(),
LocalItemIds: completedItems.map(function(xitem) {
return xitem.ItemId
})
};
return apiClient.syncData(request).then(function(result) {
return afterSyncData(apiClient, serverInfo, result).then(function() {
return console.log("[mediasync] Exit syncData"), Promise.resolve()
}, function(err) {
return console.error("[mediasync] Error in syncData: " + err.toString()), Promise.resolve()
})
})
})
}
function afterSyncData(apiClient, serverInfo, syncDataResult) {
console.log("[mediasync] Begin afterSyncData");
var p = Promise.resolve();
return syncDataResult.ItemIdsToRemove && syncDataResult.ItemIdsToRemove.length > 0 && syncDataResult.ItemIdsToRemove.forEach(function(itemId) {
p = p.then(function() {
return removeLocalItem(itemId, serverInfo.Id)
})
}), p = p.then(function() {
return removeObsoleteContainerItems(serverInfo.Id)
}), p.then(function() {
return console.log("[mediasync] Exit afterSyncData"), Promise.resolve()
})
}
function removeObsoleteContainerItems(serverId) {
return console.log("[mediasync] Begin removeObsoleteContainerItems"), localassetmanager.removeObsoleteContainerItems(serverId)
}
function removeLocalItem(itemId, serverId) {
return console.log("[mediasync] Begin removeLocalItem"), localassetmanager.getLocalItem(serverId, itemId).then(function(item) {
return item ? localassetmanager.removeLocalItem(item) : Promise.resolve()
}, function(err2) {
return console.error("[mediasync] removeLocalItem: Failed: ", err2), Promise.resolve()
})
}
function getNewMedia(apiClient, downloadCount) {
return console.log("[mediasync] Begin getNewMedia"), apiClient.getReadySyncItems(apiClient.deviceId()).then(function(jobItems) {
console.log("[mediasync] getReadySyncItems returned " + jobItems.length + " items");
var p = Promise.resolve(),
currentCount = downloadCount;
return jobItems.forEach(function(jobItem) {
currentCount++ <= 10 && (p = p.then(function() {
return getNewItem(jobItem, apiClient)
}))
}), p.then(function() {
return console.log("[mediasync] Exit getNewMedia"), Promise.resolve()
})
}, function(err) {
return console.error("[mediasync] getReadySyncItems: Failed: ", err), Promise.resolve()
})
}
function afterMediaDownloaded(apiClient, jobItem, localItem) {
return console.log("[mediasync] Begin afterMediaDownloaded"), getImages(apiClient, jobItem, localItem).then(function() {
var libraryItem = jobItem.Item;
return downloadParentItems(apiClient, jobItem, libraryItem).then(function() {
return getSubtitles(apiClient, jobItem, localItem)
})
})
}
function createLocalItem(libraryItem, jobItem) {
console.log("[localassetmanager] Begin createLocalItem");
var item = {
Item: libraryItem,
ItemId: libraryItem.Id,
ServerId: libraryItem.ServerId,
Id: libraryItem.Id
};
return jobItem && (item.SyncJobItemId = jobItem.SyncJobItemId), console.log("[localassetmanager] End createLocalItem"), item
}
function getNewItem(jobItem, apiClient) {
console.log("[mediasync] Begin getNewItem");
var libraryItem = jobItem.Item;
return localassetmanager.getLocalItem(libraryItem.ServerId, libraryItem.Id).then(function(existingItem) {
if (existingItem && ("queued" === existingItem.SyncStatus || "transferring" === existingItem.SyncStatus || "synced" === existingItem.SyncStatus) && (console.log("[mediasync] getNewItem: getLocalItem found existing item"), localassetmanager.enableBackgroundCompletion())) return Promise.resolve();
libraryItem.CanDelete = !1, libraryItem.CanDownload = !1, libraryItem.SupportsSync = !1, libraryItem.People = [], libraryItem.Chapters = [], libraryItem.Studios = [], libraryItem.SpecialFeatureCount = null, libraryItem.LocalTrailerCount = null, libraryItem.RemoteTrailers = [];
var localItem = createLocalItem(libraryItem, jobItem);
return localItem.SyncStatus = "queued", downloadMedia(apiClient, jobItem, localItem)
})
}
function downloadParentItems(apiClient, jobItem, libraryItem) {
var p = Promise.resolve();
return libraryItem.SeriesId && (p = p.then(function() {
return downloadItem(apiClient, libraryItem.SeriesId)
})), libraryItem.SeasonId && (p = p.then(function() {
return downloadItem(apiClient, libraryItem.SeasonId).then(function(seasonItem) {
return libraryItem.SeasonPrimaryImageTag = (seasonItem.Item.ImageTags || {}).Primary, Promise.resolve()
})
})), libraryItem.AlbumId && (p = p.then(function() {
return downloadItem(apiClient, libraryItem.AlbumId)
})), p
}
function downloadItem(apiClient, itemId) {
return apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function(downloadedItem) {
downloadedItem.CanDelete = !1, downloadedItem.CanDownload = !1, downloadedItem.SupportsSync = !1, downloadedItem.People = [], downloadedItem.SpecialFeatureCount = null, downloadedItem.BackdropImageTags = null, downloadedItem.ParentBackdropImageTags = null, downloadedItem.ParentArtImageTag = null, downloadedItem.ParentLogoImageTag = null;
var localItem = createLocalItem(downloadedItem, null);
return localassetmanager.addOrUpdateLocalItem(localItem).then(function() {
return Promise.resolve(localItem)
}, function(err) {
return console.error("[mediasync] downloadItem failed: " + err.toString()), Promise.resolve(null)
})
})
}
function ensureLocalPathParts(localItem, jobItem) {
if (!localItem.LocalPathParts) {
var libraryItem = localItem.Item,
parts = localassetmanager.getDirectoryPath(libraryItem);
parts.push(localassetmanager.getLocalFileName(libraryItem, jobItem.OriginalFileName)), localItem.LocalPathParts = parts
}
}
function downloadMedia(apiClient, jobItem, localItem) {
console.log("[mediasync] downloadMedia: start.");
var url = apiClient.getUrl("Sync/JobItems/" + jobItem.SyncJobItemId + "/File", {
api_key: apiClient.accessToken()
});
return ensureLocalPathParts(localItem, jobItem), localassetmanager.downloadFile(url, localItem).then(function(result) {
console.log("[mediasync] downloadMedia-downloadFile returned path: " + result.path);
var localPath = result.path,
libraryItem = localItem.Item;
if (localPath && libraryItem.MediaSources)
for (var i = 0; i < libraryItem.MediaSources.length; i++) {
var mediaSource = libraryItem.MediaSources[i];
mediaSource.Path = localPath, mediaSource.Protocol = "File"
}
return localItem.LocalPath = localPath, localItem.SyncStatus = "transferring", localassetmanager.addOrUpdateLocalItem(localItem).then(function() {
return afterMediaDownloaded(apiClient, jobItem, localItem).then(function() {
return result.isComplete ? (localItem.SyncStatus = "synced", reportTransfer(apiClient, localItem)) : Promise.resolve()
}, function(err) {
return console.log("[mediasync] downloadMedia: afterMediaDownloaded failed: " + err), Promise.reject(err)
})
}, function(err) {
return console.log("[mediasync] downloadMedia: addOrUpdateLocalItem failed: " + err), Promise.reject(err)
})
}, function(err) {
return console.log("[mediasync] downloadMedia: localassetmanager.downloadFile failed: " + err), Promise.reject(err)
})
}
function getImages(apiClient, jobItem, localItem) {
console.log("[mediasync] Begin getImages");
var p = Promise.resolve(),
libraryItem = localItem.Item,
serverId = libraryItem.ServerId,
mainImageTag = (libraryItem.ImageTags || {}).Primary;
libraryItem.Id && mainImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.Id, mainImageTag, "Primary")
}));
var logoImageTag = (libraryItem.ImageTags || {}).Logo;
libraryItem.Id && logoImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.Id, logoImageTag, "Logo")
}));
var artImageTag = (libraryItem.ImageTags || {}).Art;
libraryItem.Id && artImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.Id, artImageTag, "Art")
}));
var bannerImageTag = (libraryItem.ImageTags || {}).Banner;
libraryItem.Id && bannerImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.Id, bannerImageTag, "Banner")
}));
var thumbImageTag = (libraryItem.ImageTags || {}).Thumb;
if (libraryItem.Id && thumbImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.Id, thumbImageTag, "Thumb")
})), libraryItem.Id && libraryItem.BackdropImageTags)
for (var i = 0; i < libraryItem.BackdropImageTags.length; i++);
return libraryItem.SeriesId && libraryItem.SeriesPrimaryImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.SeriesId, libraryItem.SeriesPrimaryImageTag, "Primary")
})), libraryItem.SeriesId && libraryItem.SeriesThumbImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.SeriesId, libraryItem.SeriesThumbImageTag, "Thumb")
})), libraryItem.SeasonId && libraryItem.SeasonPrimaryImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.SeasonId, libraryItem.SeasonPrimaryImageTag, "Primary")
})), libraryItem.AlbumId && libraryItem.AlbumPrimaryImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.AlbumId, libraryItem.AlbumPrimaryImageTag, "Primary")
})), libraryItem.ParentThumbItemId && libraryItem.ParentThumbImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.ParentThumbItemId, libraryItem.ParentThumbImageTag, "Thumb")
})), libraryItem.ParentPrimaryImageItemId && libraryItem.ParentPrimaryImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.ParentPrimaryImageItemId, libraryItem.ParentPrimaryImageTag, "Primary")
})), p.then(function() {
return console.log("[mediasync] Finished getImages"), localassetmanager.addOrUpdateLocalItem(localItem)
}, function(err) {
return console.log("[mediasync] Error getImages: " + err.toString()), Promise.resolve()
})
}
function downloadImage(localItem, apiClient, serverId, itemId, imageTag, imageType, index) {
return index = index || 0, localassetmanager.hasImage(serverId, itemId, imageType, index).then(function(hasImage) {
if (hasImage) return console.log("[mediasync] downloadImage - skip existing: " + itemId + " " + imageType + "_" + index.toString()), Promise.resolve();
var maxWidth = 400;
"backdrop" === imageType && (maxWidth = null);
var imageUrl = apiClient.getScaledImageUrl(itemId, {
tag: imageTag,
type: imageType,
maxWidth: maxWidth,
api_key: apiClient.accessToken()
});
return console.log("[mediasync] downloadImage " + itemId + " " + imageType + "_" + index.toString()), localassetmanager.downloadImage(localItem, imageUrl, serverId, itemId, imageType, index).then(function(result) {
return Promise.resolve(result)
}, function(err) {
return console.log("[mediasync] Error downloadImage: " + err.toString()), Promise.resolve()
})
}, function(err) {
return console.log("[mediasync] Error downloadImage: " + err.toString()), Promise.resolve()
})
}
function getSubtitles(apiClient, jobItem, localItem) {
if (console.log("[mediasync] Begin getSubtitles"), !jobItem.Item.MediaSources.length) return console.log("[mediasync] Cannot download subtitles because video has no media source info."), Promise.resolve();
var files = jobItem.AdditionalFiles.filter(function(f) {
return "Subtitles" === f.Type
}),
mediaSource = jobItem.Item.MediaSources[0],
p = Promise.resolve();
return files.forEach(function(file) {
p = p.then(function() {
return getItemSubtitle(file, apiClient, jobItem, localItem, mediaSource)
})
}), p.then(function() {
return console.log("[mediasync] Exit getSubtitles"), Promise.resolve()
})
}
function getItemSubtitle(file, apiClient, jobItem, localItem, mediaSource) {
console.log("[mediasync] Begin getItemSubtitle");
var subtitleStream = mediaSource.MediaStreams.filter(function(m) {
return "Subtitle" === m.Type && m.Index === file.Index
})[0];
if (!subtitleStream) return console.log("[mediasync] Cannot download subtitles because matching stream info was not found."), Promise.resolve();
var url = apiClient.getUrl("Sync/JobItems/" + jobItem.SyncJobItemId + "/AdditionalFiles", {
Name: file.Name,
api_key: apiClient.accessToken()
}),
fileName = localassetmanager.getSubtitleSaveFileName(localItem, jobItem.OriginalFileName, subtitleStream.Language, subtitleStream.IsForced, subtitleStream.Codec);
return localassetmanager.downloadSubtitles(url, fileName).then(function(subtitleResult) {
return localItem.AdditionalFiles && localItem.AdditionalFiles.forEach(function(item) {
item.Name === file.Name && (item.Path = subtitleResult.path)
}), subtitleStream.Path = subtitleResult.path, subtitleStream.DeliveryMethod = "External", localassetmanager.addOrUpdateLocalItem(localItem)
})
}
function checkLocalFileExistence(apiClient, serverInfo, options) {
return options.checkFileExistence ? (console.log("[mediasync] Begin checkLocalFileExistence"), localassetmanager.getServerItems(serverInfo.Id).then(function(items) {
var completedItems = items.filter(function(item) {
return item && ("synced" === item.SyncStatus || "error" === item.SyncStatus)
}),
p = Promise.resolve();
return completedItems.forEach(function(completedItem) {
p = p.then(function() {
return localassetmanager.fileExists(completedItem.LocalPath).then(function(exists) {
return exists ? Promise.resolve() : localassetmanager.removeLocalItem(completedItem).then(function() {
return Promise.resolve()
}, function() {
return Promise.resolve()
})
})
})
}), p
})) : Promise.resolve()
}
return function() {
var self = this;
"string" == typeof webWorkerBaseUrl && -1 !== webWorkerBaseUrl.indexOf("ms-appx://") ? self.sync = function(apiClient, serverInfo, options) {
return console.log("[mediasync]************************************* Start sync"), checkLocalFileExistence(apiClient, serverInfo, options).then(function() {
return processDownloadStatus(apiClient, serverInfo, options).then(function() {
return localassetmanager.getDownloadItemCount().then(function(downloadCount) {
return !0 === options.syncCheckProgressOnly && downloadCount > 2 ? Promise.resolve() : reportOfflineActions(apiClient, serverInfo).then(function() {
return getNewMedia(apiClient, downloadCount).then(function() {
return syncData(apiClient, serverInfo).then(function() {
return console.log("[mediasync]************************************* Exit sync"), Promise.resolve()
})
})
})
})
})
}, function(err) {
console.error(err.toString())
})
} : self.sync = function(apiClient, serverInfo, options) {
return console.log("[mediasync]************************************* Start sync"), checkLocalFileExistence(apiClient, serverInfo, options).then(function() {
return syncData(apiClient, serverInfo).then(function() {
return processDownloadStatus(apiClient, serverInfo, options).then(function() {
return localassetmanager.getDownloadItemCount().then(function(downloadCount) {
return !0 === options.syncCheckProgressOnly && downloadCount > 2 ? Promise.resolve() : reportOfflineActions(apiClient, serverInfo).then(function() {
return getNewMedia(apiClient, downloadCount).then(function() {
return syncData(apiClient, serverInfo)
})
})
})
})
})
}, function(err) {
console.error(err.toString())
})
}
}
});

View file

@ -0,0 +1,22 @@
define(["serversync"], function(ServerSync) {
"use strict";
function syncNext(connectionManager, servers, index, options, resolve, reject) {
var length = servers.length;
if (index >= length) return console.log("MultiServerSync.sync complete"), void resolve();
var server = servers[index];
console.log("Creating ServerSync to server: " + server.Id), (new ServerSync).sync(connectionManager, server, options).then(function() {
console.log("ServerSync succeeded to server: " + server.Id), syncNext(connectionManager, servers, index + 1, options, resolve, reject)
}, function(err) {
console.log("ServerSync failed to server: " + server.Id + ". " + err), syncNext(connectionManager, servers, index + 1, options, resolve, reject)
})
}
function MultiServerSync() {}
return MultiServerSync.prototype.sync = function(connectionManager, options) {
return console.log("MultiServerSync.sync starting..."), new Promise(function(resolve, reject) {
var servers = connectionManager.getSavedServers();
syncNext(connectionManager, servers, 0, options, resolve, reject)
})
}, MultiServerSync
});

View file

@ -0,0 +1,46 @@
define([], function() {
"use strict";
function performSync(connectionManager, server, options) {
console.log("ServerSync.performSync to server: " + server.Id), options = options || {};
var cameraUploadServers = options.cameraUploadServers || [];
console.log("ServerSync cameraUploadServers: " + JSON.stringify(cameraUploadServers));
var uploadPhotos = -1 !== cameraUploadServers.indexOf(server.Id);
return console.log("ServerSync uploadPhotos: " + uploadPhotos), (uploadPhotos ? uploadContent(connectionManager, server, options) : Promise.resolve()).then(function() {
return syncMedia(connectionManager, server, options)
})
}
function uploadContent(connectionManager, server, options) {
return new Promise(function(resolve, reject) {
require(["contentuploader"], function(ContentUploader) {
(new ContentUploader).uploadImages(connectionManager, server).then(resolve, reject)
})
})
}
function syncMedia(connectionManager, server, options) {
return new Promise(function(resolve, reject) {
require(["mediasync"], function(MediaSync) {
var apiClient = connectionManager.getApiClient(server.Id);
(new MediaSync).sync(apiClient, server, options).then(resolve, reject)
})
})
}
function ServerSync() {}
return ServerSync.prototype.sync = function(connectionManager, server, options) {
if (!server.AccessToken && !server.ExchangeToken) return console.log("Skipping sync to server " + server.Id + " because there is no saved authentication information."), Promise.resolve();
var connectionOptions = {
updateDateLastAccessed: !1,
enableWebSocket: !1,
reportCapabilities: !1,
enableAutomaticBitrateDetection: !1
};
return connectionManager.connectToServer(server, connectionOptions).then(function(result) {
return "SignedIn" === result.State ? performSync(connectionManager, server, options) : (console.log("Unable to connect to server id: " + server.Id), Promise.reject())
}, function(err) {
throw console.log("Unable to connect to server id: " + server.Id), err
})
}, ServerSync
});

View file

@ -0,0 +1,30 @@
define([], function() {
"use strict";
function downloadFile(url, folder, localItem, imageUrl) {
return Promise.reject()
}
function downloadSubtitles(url, folder, fileName) {
return Promise.reject()
}
function downloadImage(url, folder, fileName) {
return Promise.reject()
}
function resyncTransfers() {
return Promise.resolve()
}
function getDownloadItemCount() {
return Promise.resolve(0)
}
return {
downloadFile: downloadFile,
downloadSubtitles: downloadSubtitles,
downloadImage: downloadImage,
resyncTransfers: resyncTransfers,
getDownloadItemCount: getDownloadItemCount
}
});

View file

@ -0,0 +1,108 @@
define([], function() {
"use strict";
function getDb(callback) {
var db = databaseInstance;
if (db) return void callback(db);
var request = indexedDB.open(dbName, dbVersion);
request.onerror = function(event) {}, request.onupgradeneeded = function(event) {
var db = event.target.result;
db.createObjectStore(dbName).transaction.oncomplete = function(event) {
callback(db)
}
}, request.onsuccess = function(event) {
var db = event.target.result;
callback(db)
}
}
function getByServerId(serverId) {
return getAll().then(function(items) {
return items.filter(function(item) {
return item.ServerId === serverId
})
})
}
function getAll() {
return new Promise(function(resolve, reject) {
getDb(function(db) {
var request, storeName = dbName,
transaction = db.transaction([storeName], "readonly"),
objectStore = transaction.objectStore(storeName);
if ("getAll" in objectStore) request = objectStore.getAll(null, 1e4), request.onsuccess = function(event) {
resolve(event.target.result)
};
else {
var results = [];
request = objectStore.openCursor(), request.onsuccess = function(event) {
var cursor = event.target.result;
cursor ? (results.push(cursor.value), cursor.continue()) : resolve(results)
}
}
request.onerror = reject
})
})
}
function get(key) {
return new Promise(function(resolve, reject) {
getDb(function(db) {
var storeName = dbName,
transaction = db.transaction([storeName], "readonly"),
objectStore = transaction.objectStore(storeName),
request = objectStore.get(key);
request.onerror = reject, request.onsuccess = function(event) {
resolve(request.result)
}
})
})
}
function set(key, val) {
return new Promise(function(resolve, reject) {
getDb(function(db) {
var storeName = dbName,
transaction = db.transaction([storeName], "readwrite"),
objectStore = transaction.objectStore(storeName),
request = objectStore.put(val, key);
request.onerror = reject, request.onsuccess = resolve
})
})
}
function remove(key) {
return new Promise(function(resolve, reject) {
getDb(function(db) {
var storeName = dbName,
transaction = db.transaction([storeName], "readwrite"),
objectStore = transaction.objectStore(storeName),
request = objectStore.delete(key);
request.onerror = reject, request.onsuccess = resolve
})
})
}
function clear() {
return new Promise(function(resolve, reject) {
getDb(function(db) {
var storeName = dbName,
transaction = db.transaction([storeName], "readwrite"),
objectStore = transaction.objectStore(storeName),
request = objectStore.clear();
request.onerror = reject, request.onsuccess = resolve
})
})
}
var databaseInstance, indexedDB = self.indexedDB || self.mozIndexedDB || self.webkitIndexedDB || self.msIndexedDB,
dbName = (self.IDBTransaction || self.webkitIDBTransaction || self.msIDBTransaction, self.IDBKeyRange || self.webkitIDBKeyRange || self.msIDBKeyRange, "useractions"),
dbVersion = 1;
return {
get: get,
set: set,
remove: remove,
clear: clear,
getAll: getAll,
getByServerId: getByServerId
}
});

View file

@ -0,0 +1,15 @@
define([], function() {
"use strict";
function send(info) {
return Promise.reject()
}
function isSupported() {
return !1
}
return {
send: send,
isSupported: isSupported
}
});

View file

@ -0,0 +1,158 @@
.actionSheet,
.actionSheetContent {
display: -webkit-box;
display: -webkit-flex
}
.actionSheetContent,
.actionSheetScroller {
-webkit-box-orient: vertical;
-webkit-box-direction: normal
}
.actionSheet {
display: flex;
-webkit-box-pack: center;
-webkit-justify-content: center;
justify-content: center;
padding: 0;
border: none;
max-height: 84%;
-webkit-border-radius: .1em !important;
border-radius: .1em !important
}
.actionsheet-not-fullscreen {
max-width: 90%;
max-height: 90%
}
.actionsheet-fullscreen {
max-height: none;
-webkit-border-radius: 0 !important;
border-radius: 0 !important
}
.actionSheetContent-centered {
text-align: center;
-webkit-box-align: center;
-webkit-align-items: center;
align-items: center
}
.actionSheetContent {
margin: 0 !important;
padding: .4em 0 !important;
-webkit-flex-direction: column;
flex-direction: column;
display: flex;
-webkit-box-pack: center;
-webkit-justify-content: center;
justify-content: center;
-webkit-box-flex: 1;
-webkit-flex-grow: 1;
flex-grow: 1;
overflow: hidden
}
.actionSheetMenuItem {
font-weight: inherit;
-webkit-box-shadow: none;
box-shadow: none;
-webkit-flex-shrink: 0;
flex-shrink: 0
}
.actionSheetMenuItem:focus {
-webkit-transform: none !important;
transform: none !important
}
.actionsheetListItemBody {
padding: .4em 1em .4em .6em !important
}
.actionSheetItemText {
white-space: nowrap;
overflow: hidden;
-o-text-overflow: ellipsis;
text-overflow: ellipsis;
vertical-align: middle;
-webkit-box-flex: 1;
-webkit-flex-grow: 1;
flex-grow: 1;
display: -webkit-box;
display: -webkit-flex;
display: flex;
-webkit-box-pack: start;
-webkit-justify-content: flex-start;
justify-content: flex-start
}
.actionSheetItemAsideText {
opacity: .7;
font-size: 90%;
display: -webkit-box;
display: -webkit-flex;
display: flex;
-webkit-box-pack: end;
-webkit-justify-content: flex-end;
justify-content: flex-end;
-webkit-flex-shrink: 0;
flex-shrink: 0;
margin-left: 5em;
margin-right: .5em
}
.actionSheetScroller {
margin-bottom: 0 !important;
display: -webkit-box;
display: -webkit-flex;
display: flex;
-webkit-flex-direction: column;
flex-direction: column;
width: 100%
}
.actionSheetScroller-tv {
max-height: 64%;
max-width: 60%;
width: auto
}
.actionsheetDivider {
height: .07em;
margin: .25em 0;
-webkit-flex-shrink: 0;
flex-shrink: 0
}
.actionSheetTitle {
margin: .6em 0 .7em !important;
padding: 0 .9em;
-webkit-box-flex: 0;
-webkit-flex-grow: 0;
flex-grow: 0
}
.actionSheetText {
padding: 0 1em;
-webkit-box-flex: 0;
-webkit-flex-grow: 0;
flex-grow: 0
}
.actionsheetMenuItemIcon {
margin: 0 .85em 0 .45em !important;
padding: 0 !important
}
.actionsheet-xlargeFont {
font-size: 112% !important
}
.btnCloseActionSheet {
position: fixed;
top: .75em;
left: .5em
}

View file

@ -0,0 +1,93 @@
define(["dialogHelper", "layoutManager", "globalize", "browser", "dom", "emby-button", "css!./actionsheet", "material-icons", "scrollStyles", "listViewStyle"], function(dialogHelper, layoutManager, globalize, browser, dom) {
"use strict";
function getOffsets(elems) {
var doc = document,
results = [];
if (!doc) return results;
for (var box, elem, i = 0, length = elems.length; i < length; i++) elem = elems[i], box = elem.getBoundingClientRect ? elem.getBoundingClientRect() : {
top: 0,
left: 0
}, results[i] = {
top: box.top,
left: box.left,
width: box.width,
height: box.height
};
return results
}
function getPosition(options, dlg) {
var windowSize = dom.getWindowSize(),
windowHeight = windowSize.innerHeight,
windowWidth = windowSize.innerWidth;
if (windowWidth < 600 || windowHeight < 600) return null;
var pos = getOffsets([options.positionTo])[0];
"top" !== options.positionY && (pos.top += (pos.height || 0) / 2), pos.left += (pos.width || 0) / 2;
var height = dlg.offsetHeight || 300,
width = dlg.offsetWidth || 160;
pos.top -= height / 2, pos.left -= width / 2;
var overflowX = pos.left + width - windowWidth,
overflowY = pos.top + height - windowHeight;
return overflowX > 0 && (pos.left -= overflowX + 20), overflowY > 0 && (pos.top -= overflowY + 20), pos.top += options.offsetTop || 0, pos.left += options.offsetLeft || 0, pos.top = Math.max(pos.top, 10), pos.left = Math.max(pos.left, 10), pos
}
function centerFocus(elem, horiz, on) {
require(["scrollHelper"], function(scrollHelper) {
var fn = on ? "on" : "off";
scrollHelper.centerFocus[fn](elem, horiz)
})
}
function show(options) {
var isFullscreen, dialogOptions = {
removeOnClose: !0,
enableHistory: options.enableHistory,
scrollY: !1
};
layoutManager.tv ? (dialogOptions.size = "fullscreen", isFullscreen = !0, !0, dialogOptions.autoFocus = !0) : (dialogOptions.modal = !1, dialogOptions.entryAnimation = options.entryAnimation, dialogOptions.exitAnimation = options.exitAnimation, dialogOptions.entryAnimationDuration = options.entryAnimationDuration || 140, dialogOptions.exitAnimationDuration = options.exitAnimationDuration || 100, dialogOptions.autoFocus = !1);
var dlg = dialogHelper.createDialog(dialogOptions);
isFullscreen ? dlg.classList.add("actionsheet-fullscreen") : dlg.classList.add("actionsheet-not-fullscreen"), dlg.classList.add("actionSheet"), options.dialogClass && dlg.classList.add(options.dialogClass);
var html = "",
scrollClassName = layoutManager.tv ? "scrollY smoothScrollY hiddenScrollY" : "scrollY",
style = "";
if (options.items.length > 20) {
style += "min-width:" + (dom.getWindowSize().innerWidth >= 300 ? 240 : 200) + "px;"
}
var i, length, option, itemIcon, renderIcon = !1,
icons = [];
for (i = 0, length = options.items.length; i < length; i++) option = options.items[i], itemIcon = option.icon || (option.selected ? "check" : null), itemIcon && (renderIcon = !0), icons.push(itemIcon || "");
layoutManager.tv && (html += '<button is="paper-icon-button-light" class="btnCloseActionSheet hide-mouse-idle-tv" tabindex="-1"><i class="md-icon">&#xE5C4;</i></button>');
var center = options.title && !renderIcon;
center || layoutManager.tv ? html += '<div class="actionSheetContent actionSheetContent-centered">' : html += '<div class="actionSheetContent">', options.title && (html += '<h1 class="actionSheetTitle">', html += options.title, html += "</h1>"), options.text && (html += '<p class="actionSheetText">', html += options.text, html += "</p>");
var scrollerClassName = "actionSheetScroller";
layoutManager.tv && (scrollerClassName += " actionSheetScroller-tv focuscontainer-x focuscontainer-y"), html += '<div class="' + scrollerClassName + " " + scrollClassName + '" style="' + style + '">';
var menuItemClass = "listItem listItem-button actionSheetMenuItem";
for ((options.border || options.shaded) && (menuItemClass += " listItem-border"), options.menuItemClass && (menuItemClass += " " + options.menuItemClass), layoutManager.tv && (menuItemClass += " listItem-focusscale"), layoutManager.mobile && (menuItemClass += " actionsheet-xlargeFont"), i = 0, length = options.items.length; i < length; i++)
if (option = options.items[i], option.divider) html += '<div class="actionsheetDivider"></div>';
else {
var autoFocus = option.selected && layoutManager.tv ? " autoFocus" : "",
optionId = null == option.id || "" === option.id ? option.value : option.id;
html += "<button" + autoFocus + ' is="emby-button" type="button" class="' + menuItemClass + '" data-id="' + optionId + '">', itemIcon = icons[i], itemIcon ? html += '<i class="actionsheetMenuItemIcon listItemIcon listItemIcon-transparent md-icon">' + itemIcon + "</i>" : renderIcon && !center && (html += '<i class="actionsheetMenuItemIcon listItemIcon listItemIcon-transparent md-icon" style="visibility:hidden;">check</i>'), html += '<div class="listItemBody actionsheetListItemBody">', html += '<div class="listItemBodyText actionSheetItemText">', html += option.name || option.textContent || option.innerText, html += "</div>", option.secondaryText && (html += '<div class="listItemBodyText secondary">', html += option.secondaryText, html += "</div>"), html += "</div>", option.asideText && (html += '<div class="listItemAside actionSheetItemAsideText">', html += option.asideText, html += "</div>"), html += "</button>"
} options.showCancel && (html += '<div class="buttons">', html += '<button is="emby-button" type="button" class="btnCloseActionSheet">' + globalize.translate("sharedcomponents#ButtonCancel") + "</button>", html += "</div>"), html += "</div>", dlg.innerHTML = html, layoutManager.tv && centerFocus(dlg.querySelector(".actionSheetScroller"), !1, !0), dlg.querySelector(".btnCloseActionSheet") && dlg.querySelector(".btnCloseActionSheet").addEventListener("click", function() {
dialogHelper.close(dlg)
});
var selectedId, timeout;
return options.timeout && (timeout = setTimeout(function() {
dialogHelper.close(dlg)
}, options.timeout)), new Promise(function(resolve, reject) {
var isResolved;
dlg.addEventListener("click", function(e) {
var actionSheetMenuItem = dom.parentWithClass(e.target, "actionSheetMenuItem");
actionSheetMenuItem && (selectedId = actionSheetMenuItem.getAttribute("data-id"), options.resolveOnClick && (options.resolveOnClick.indexOf ? -1 !== options.resolveOnClick.indexOf(selectedId) && (resolve(selectedId), isResolved = !0) : (resolve(selectedId), isResolved = !0)), dialogHelper.close(dlg))
}), dlg.addEventListener("close", function() {
layoutManager.tv && centerFocus(dlg.querySelector(".actionSheetScroller"), !1, !1), timeout && (clearTimeout(timeout), timeout = null), isResolved || (null != selectedId ? (options.callback && options.callback(selectedId), resolve(selectedId)) : reject())
}), dialogHelper.open(dlg);
var pos = options.positionTo && "fullscreen" !== dialogOptions.size ? getPosition(options, dlg) : null;
pos && (dlg.style.position = "fixed", dlg.style.margin = 0, dlg.style.left = pos.left + "px", dlg.style.top = pos.top + "px")
})
}
return {
show: show
}
});

View file

@ -0,0 +1,18 @@
define(["dialog", "globalize"], function(dialog, globalize) {
"use strict";
return function(text, title) {
var options;
options = "string" == typeof text ? {
title: title,
text: text
} : text;
var items = [];
return items.push({
name: globalize.translate("sharedcomponents#ButtonGotIt"),
id: "ok",
type: "submit"
}), options.buttons = items, dialog(options).then(function(result) {
return "ok" === result ? Promise.resolve() : Promise.reject()
})
}
});

View file

@ -0,0 +1,14 @@
define([], function() {
"use strict";
function replaceAll(str, find, replace) {
return str.split(find).join(replace)
}
return function(options) {
"string" == typeof options && (options = {
text: options
});
var text = replaceAll(options.text || "", "<br/>", "\n");
return alert(text), Promise.resolve()
}
});

View file

@ -0,0 +1,59 @@
define(["dom", "focusManager"], function(dom, focusManager) {
"use strict";
function onKeyDown(e) {
if (!e.ctrlKey && !e.shiftKey && !e.altKey) {
var key = e.key,
chr = key ? alphanumeric(key) : null;
chr && (chr = chr.toString().toUpperCase(), 1 === chr.length && (currentDisplayTextContainer = this.options.itemsContainer, onAlphanumericKeyPress(e, chr)))
}
}
function alphanumeric(value) {
var letterNumber = /^[0-9a-zA-Z]+$/;
return value.match(letterNumber)
}
function ensureInputDisplayElement() {
inputDisplayElement || (inputDisplayElement = document.createElement("div"), inputDisplayElement.classList.add("alphanumeric-shortcut"), inputDisplayElement.classList.add("hide"), document.body.appendChild(inputDisplayElement))
}
function clearAlphaNumericShortcutTimeout() {
alpanumericShortcutTimeout && (clearTimeout(alpanumericShortcutTimeout), alpanumericShortcutTimeout = null)
}
function resetAlphaNumericShortcutTimeout() {
clearAlphaNumericShortcutTimeout(), alpanumericShortcutTimeout = setTimeout(onAlphanumericShortcutTimeout, 2e3)
}
function onAlphanumericKeyPress(e, chr) {
currentDisplayText.length >= 3 || (ensureInputDisplayElement(), currentDisplayText += chr, inputDisplayElement.innerHTML = currentDisplayText, inputDisplayElement.classList.remove("hide"), resetAlphaNumericShortcutTimeout())
}
function onAlphanumericShortcutTimeout() {
var value = currentDisplayText,
container = currentDisplayTextContainer;
currentDisplayText = "", currentDisplayTextContainer = null, inputDisplayElement.innerHTML = "", inputDisplayElement.classList.add("hide"), clearAlphaNumericShortcutTimeout(), selectByShortcutValue(container, value)
}
function selectByShortcutValue(container, value) {
value = value.toUpperCase();
var focusElem;
"#" === value && (focusElem = container.querySelector("*[data-prefix]")), focusElem || (focusElem = container.querySelector("*[data-prefix^='" + value + "']")), focusElem && focusManager.focus(focusElem)
}
function AlphaNumericShortcuts(options) {
this.options = options;
var keyDownHandler = onKeyDown.bind(this);
dom.addEventListener(window, "keydown", keyDownHandler, {
passive: !0
}), this.keyDownHandler = keyDownHandler
}
var inputDisplayElement, currentDisplayTextContainer, alpanumericShortcutTimeout, currentDisplayText = "";
return AlphaNumericShortcuts.prototype.destroy = function() {
var keyDownHandler = this.keyDownHandler;
keyDownHandler && (dom.removeEventListener(window, "keydown", keyDownHandler, {
passive: !0
}), this.keyDownHandler = null), this.options = null
}, AlphaNumericShortcuts
});

View file

@ -0,0 +1,127 @@
define(["focusManager", "layoutManager", "dom", "css!./style.css", "paper-icon-button-light", "material-icons"], function(focusManager, layoutManager, dom) {
"use strict";
function focus() {
var scope = this,
selected = scope.querySelector("." + selectedButtonClass);
selected ? focusManager.focus(selected) : focusManager.autoFocus(scope, !0)
}
function getAlphaPickerButtonClassName(vertical) {
var alphaPickerButtonClassName = "alphaPickerButton";
return layoutManager.tv && (alphaPickerButtonClassName += " alphaPickerButton-tv"), vertical && (alphaPickerButtonClassName += " alphaPickerButton-vertical"), alphaPickerButtonClassName
}
function getLetterButton(l, vertical) {
return '<button data-value="' + l + '" class="' + getAlphaPickerButtonClassName(vertical) + '">' + l + "</button>"
}
function mapLetters(letters, vertical) {
return letters.map(function(l) {
return getLetterButton(l, vertical)
})
}
function render(element, options) {
element.classList.add("alphaPicker"), layoutManager.tv && element.classList.add("alphaPicker-tv");
var vertical = element.classList.contains("alphaPicker-vertical");
vertical || element.classList.add("focuscontainer-x");
var letters, html = "",
alphaPickerButtonClassName = getAlphaPickerButtonClassName(vertical),
rowClassName = "alphaPickerRow";
vertical && (rowClassName += " alphaPickerRow-vertical"), html += '<div class="' + rowClassName + '">', "keyboard" === options.mode ? html += '<button data-value=" " is="paper-icon-button-light" class="' + alphaPickerButtonClassName + '"><i class="md-icon alphaPickerButtonIcon">&#xE256;</i></button>' : (letters = ["#"], html += mapLetters(letters, vertical).join("")), letters = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"], html += mapLetters(letters, vertical).join(""), "keyboard" === options.mode ? (html += '<button data-value="backspace" is="paper-icon-button-light" class="' + alphaPickerButtonClassName + '"><i class="md-icon alphaPickerButtonIcon">&#xE14A;</i></button>', html += "</div>", letters = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"], html += '<div class="' + rowClassName + '">', html += "<br/>", html += mapLetters(letters, vertical).join(""), html += "</div>") : html += "</div>", element.innerHTML = html, element.classList.add("focusable"), element.focus = focus
}
function AlphaPicker(options) {
function onItemFocusTimeout() {
itemFocusTimeout = null, self.value(itemFocusValue)
}
function onAlphaFocusTimeout() {
if (alphaFocusTimeout = null, document.activeElement === alphaFocusedElement) {
var value = alphaFocusedElement.getAttribute("data-value");
self.value(value, !0)
}
}
function onAlphaPickerInKeyboardModeClick(e) {
var alphaPickerButton = dom.parentWithClass(e.target, "alphaPickerButton");
if (alphaPickerButton) {
var value = alphaPickerButton.getAttribute("data-value");
element.dispatchEvent(new CustomEvent("alphavalueclicked", {
cancelable: !1,
detail: {
value: value
}
}))
}
}
function onAlphaPickerClick(e) {
var alphaPickerButton = dom.parentWithClass(e.target, "alphaPickerButton");
if (alphaPickerButton) {
var value = alphaPickerButton.getAttribute("data-value");
(this._currentValue || "").toUpperCase() === value.toUpperCase() ? self.value(null, !0) : self.value(value, !0)
}
}
function onAlphaPickerFocusIn(e) {
alphaFocusTimeout && (clearTimeout(alphaFocusTimeout), alphaFocusTimeout = null);
var alphaPickerButton = dom.parentWithClass(e.target, "alphaPickerButton");
alphaPickerButton && (alphaFocusedElement = alphaPickerButton, alphaFocusTimeout = setTimeout(onAlphaFocusTimeout, 600))
}
function onItemsFocusIn(e) {
var item = dom.parentWithClass(e.target, itemClass);
if (item) {
var prefix = item.getAttribute("data-prefix");
prefix && prefix.length && (itemFocusValue = prefix[0], itemFocusTimeout && clearTimeout(itemFocusTimeout), itemFocusTimeout = setTimeout(onItemFocusTimeout, 100))
}
}
var self = this;
this.options = options;
var itemFocusValue, itemFocusTimeout, alphaFocusedElement, alphaFocusTimeout, element = options.element,
itemsContainer = options.itemsContainer,
itemClass = options.itemClass;
self.enabled = function(enabled) {
enabled ? (itemsContainer && itemsContainer.addEventListener("focus", onItemsFocusIn, !0), "keyboard" === options.mode && element.addEventListener("click", onAlphaPickerInKeyboardModeClick), "click" !== options.valueChangeEvent ? element.addEventListener("focus", onAlphaPickerFocusIn, !0) : element.addEventListener("click", onAlphaPickerClick.bind(this))) : (itemsContainer && itemsContainer.removeEventListener("focus", onItemsFocusIn, !0), element.removeEventListener("click", onAlphaPickerInKeyboardModeClick), element.removeEventListener("focus", onAlphaPickerFocusIn, !0), element.removeEventListener("click", onAlphaPickerClick.bind(this)))
}, render(element, options), this.enabled(!0), this.visible(!0)
}
var selectedButtonClass = "alphaPickerButton-selected";
return AlphaPicker.prototype.value = function(value, applyValue) {
var btn, selected, element = this.options.element;
if (void 0 !== value)
if (null != value) {
if (value = value.toUpperCase(), this._currentValue = value, "keyboard" !== this.options.mode) {
selected = element.querySelector("." + selectedButtonClass);
try {
btn = element.querySelector(".alphaPickerButton[data-value='" + value + "']")
} catch (err) {
console.log("Error in querySelector: " + err)
}
btn && btn !== selected && btn.classList.add(selectedButtonClass), selected && selected !== btn && selected.classList.remove(selectedButtonClass)
}
} else this._currentValue = value, (selected = element.querySelector("." + selectedButtonClass)) && selected.classList.remove(selectedButtonClass);
return applyValue && element.dispatchEvent(new CustomEvent("alphavaluechanged", {
cancelable: !1,
detail: {
value: value
}
})), this._currentValue
}, AlphaPicker.prototype.on = function(name, fn) {
this.options.element.addEventListener(name, fn)
}, AlphaPicker.prototype.off = function(name, fn) {
this.options.element.removeEventListener(name, fn)
}, AlphaPicker.prototype.visible = function(visible) {
this.options.element.style.visibility = visible ? "visible" : "hidden"
}, AlphaPicker.prototype.values = function() {
for (var element = this.options.element, elems = element.querySelectorAll(".alphaPickerButton"), values = [], i = 0, length = elems.length; i < length; i++) values.push(elems[i].getAttribute("data-value"));
return values
}, AlphaPicker.prototype.focus = function() {
var element = this.options.element;
focusManager.autoFocus(element, !0)
}, AlphaPicker.prototype.destroy = function() {
var element = this.options.element;
this.enabled(!1), element.classList.remove("focuscontainer-x"), this.options = null
}, AlphaPicker
});

View file

@ -0,0 +1,163 @@
.alphaPicker,
.alphaPickerRow {
display: -webkit-box;
display: -webkit-flex;
-webkit-box-direction: normal
}
.alphaPicker,
.alphaPickerRow,
.alphaPickerRow-vertical {
-webkit-box-direction: normal
}
.alphaPicker {
text-align: center;
display: flex;
-webkit-box-orient: vertical;
-webkit-flex-direction: column;
flex-direction: column;
-webkit-align-self: center;
align-self: center
}
.alphaPicker-vertical {
line-height: 1
}
.alphaPicker-fixed {
position: fixed;
bottom: 5.5em;
z-index: 999999
}
.alphaPickerRow {
display: flex;
-webkit-box-align: center;
-webkit-align-items: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
justify-content: center;
-webkit-box-orient: horizontal;
-webkit-flex-direction: row;
flex-direction: row
}
.alphaPickerRow-vertical {
-webkit-box-orient: vertical;
-webkit-flex-direction: column;
flex-direction: column
}
.alphaPickerButton {
border: 0 !important;
cursor: pointer;
outline: 0 !important;
vertical-align: middle;
font-family: inherit;
font-size: inherit;
min-width: initial;
margin: 0;
padding: .1em .4em;
width: auto;
-webkit-border-radius: .1em;
border-radius: .1em;
font-weight: 400;
-webkit-flex-shrink: 0;
flex-shrink: 0;
-webkit-box-flex: 1;
-webkit-flex-grow: 1;
flex-grow: 1
}
@media all and (max-height:50em) {
.alphaPicker-fixed {
bottom: 5em
}
.alphaPickerButton-vertical {
padding-top: 1px !important;
padding-bottom: 1px !important
}
}
@media all and (max-height:49em) {
.alphaPicker-vertical {
font-size: 94%
}
}
@media all and (max-height:44em) {
.alphaPicker-vertical {
font-size: 90%
}
.alphaPickerButton-vertical {
padding-top: 0 !important;
padding-bottom: 0 !important
}
}
@media all and (max-height:37em) {
.alphaPicker-vertical {
font-size: 82%
}
}
@media all and (max-height:32em) {
.alphaPicker-vertical {
font-size: 74%
}
}
.alphaPicker-vertical.alphaPicker-tv {
font-size: 86%
}
.alphaPickerButton-tv.alphaPickerButton-vertical {
padding: 0
}
.alphaPickerButton-vertical {
width: 1.5em;
display: -webkit-box;
display: -webkit-flex;
display: flex;
-webkit-box-pack: center;
-webkit-justify-content: center;
justify-content: center;
text-align: center
}
.alphaPickerButtonIcon {
font-size: 100% !important
}
.alphaPicker-fixed.alphaPicker-tv {
bottom: 1%
}
.alphaPicker-fixed-left {
left: .4em
}
.alphaPicker-fixed-right {
right: .4em
}
@media all and (min-width:62.5em) {
.alphaPicker-fixed-left {
left: 1em
}
.alphaPicker-fixed-right {
right: 1em
}
}
@media all and (max-height:31.25em) {
.alphaPicker-fixed {
display: none !important
}
}

View file

@ -0,0 +1,16 @@
.appfooter {
position: fixed;
left: 0;
right: 0;
z-index: 1;
bottom: 0;
-webkit-transition: -webkit-transform 180ms linear;
-o-transition: transform 180ms linear;
transition: transform 180ms linear;
contain: layout style
}
.appfooter.headroom--unpinned {
-webkit-transform: translateY(100%) !important;
transform: translateY(100%) !important
}

View file

@ -0,0 +1,20 @@
define(["browser", "css!./appfooter"], function(browser) {
"use strict";
function render(options) {
var elem = document.createElement("div");
return elem.classList.add("appfooter"), browser.chrome || elem.classList.add("appfooter-blurred"), document.body.appendChild(elem), elem
}
function appFooter(options) {
var self = this;
self.element = render(options), self.add = function(elem) {
self.element.appendChild(elem)
}, self.insert = function(elem) {
"string" == typeof elem ? self.element.insertAdjacentHTML("afterbegin", elem) : self.element.insertBefore(elem, self.element.firstChild)
}
}
return appFooter.prototype.destroy = function() {
this.element = null
}, appFooter
});

View file

@ -0,0 +1,39 @@
define(["appStorage", "events"], function(appStorage, events) {
"use strict";
function getKey(name, userId) {
return userId && (name = userId + "-" + name), name
}
function AppSettings() {}
return AppSettings.prototype.enableAutoLogin = function(val) {
return null != val && this.set("enableAutoLogin", val.toString()), "false" !== this.get("enableAutoLogin")
}, AppSettings.prototype.enableAutomaticBitrateDetection = function(isInNetwork, mediaType, val) {
var key = "enableautobitratebitrate-" + mediaType + "-" + isInNetwork;
return null != val && (isInNetwork && "Audio" === mediaType && (val = !0), this.set(key, val.toString())), !(!isInNetwork || "Audio" !== mediaType) || "false" !== this.get(key)
}, AppSettings.prototype.maxStreamingBitrate = function(isInNetwork, mediaType, val) {
var key = "maxbitrate-" + mediaType + "-" + isInNetwork;
return null != val && (isInNetwork && "Audio" === mediaType || this.set(key, val)), isInNetwork && "Audio" === mediaType ? 15e7 : parseInt(this.get(key) || "0") || 15e5
}, AppSettings.prototype.maxStaticMusicBitrate = function(val) {
void 0 !== val && this.set("maxStaticMusicBitrate", val);
var defaultValue = 32e4;
return parseInt(this.get("maxStaticMusicBitrate") || defaultValue.toString()) || defaultValue
}, AppSettings.prototype.maxChromecastBitrate = function(val) {
return null != val && this.set("chromecastBitrate1", val), val = this.get("chromecastBitrate1"), val ? parseInt(val) : null
}, AppSettings.prototype.syncOnlyOnWifi = function(val) {
return null != val && this.set("syncOnlyOnWifi", val.toString()), "false" !== this.get("syncOnlyOnWifi")
}, AppSettings.prototype.syncPath = function(val) {
return null != val && this.set("syncPath", val), this.get("syncPath")
}, AppSettings.prototype.cameraUploadServers = function(val) {
return null != val && this.set("cameraUploadServers", val.join(",")), val = this.get("cameraUploadServers"), val ? val.split(",") : []
}, AppSettings.prototype.runAtStartup = function(val) {
return null != val && this.set("runatstartup", val.toString()), "true" === this.get("runatstartup")
}, AppSettings.prototype.set = function(name, value, userId) {
var currentValue = this.get(name, userId);
appStorage.setItem(getKey(name, userId), value), currentValue !== value && events.trigger(this, "change", [name])
}, AppSettings.prototype.get = function(name, userId) {
return appStorage.getItem(getKey(name, userId))
}, AppSettings.prototype.enableSystemExternalPlayers = function(val) {
return null != val && this.set("enableSystemExternalPlayers", val.toString()), "true" === this.get("enableSystemExternalPlayers")
}, new AppSettings
});

View file

@ -0,0 +1,150 @@
define(["browser", "connectionManager", "playbackManager", "dom", "css!./style"], function(browser, connectionManager, playbackManager, dom) {
"use strict";
function enableAnimation(elem) {
return !browser.slow
}
function enableRotation() {
return !browser.tv && !browser.firefox
}
function Backdrop() {}
function getBackdropContainer() {
return backdropContainer || (backdropContainer = document.querySelector(".backdropContainer")), backdropContainer || (backdropContainer = document.createElement("div"), backdropContainer.classList.add("backdropContainer"), document.body.insertBefore(backdropContainer, document.body.firstChild)), backdropContainer
}
function clearBackdrop(clearAll) {
clearRotation(), currentLoadingBackdrop && (currentLoadingBackdrop.destroy(), currentLoadingBackdrop = null), getBackdropContainer().innerHTML = "", clearAll && (hasExternalBackdrop = !1), internalBackdrop(!1)
}
function getBackgroundContainer() {
return backgroundContainer || (backgroundContainer = document.querySelector(".backgroundContainer")), backgroundContainer
}
function setBackgroundContainerBackgroundEnabled() {
hasInternalBackdrop || hasExternalBackdrop ? getBackgroundContainer().classList.add("withBackdrop") : getBackgroundContainer().classList.remove("withBackdrop")
}
function internalBackdrop(enabled) {
hasInternalBackdrop = enabled, setBackgroundContainerBackgroundEnabled()
}
function externalBackdrop(enabled) {
hasExternalBackdrop = enabled, setBackgroundContainerBackgroundEnabled()
}
function setBackdropImage(url) {
currentLoadingBackdrop && (currentLoadingBackdrop.destroy(), currentLoadingBackdrop = null);
var elem = getBackdropContainer(),
existingBackdropImage = elem.querySelector(".displayingBackdropImage");
if (existingBackdropImage && existingBackdropImage.getAttribute("data-url") === url) {
if (existingBackdropImage.getAttribute("data-url") === url) return;
existingBackdropImage.classList.remove("displayingBackdropImage")
}
var instance = new Backdrop;
instance.load(url, elem, existingBackdropImage), currentLoadingBackdrop = instance
}
function getBackdropMaxWidth() {
var width = dom.getWindowSize().innerWidth;
if (-1 !== standardWidths.indexOf(width)) return width;
return width = 100 * Math.floor(width / 100), Math.min(width, 1920)
}
function getItemImageUrls(item, imageOptions) {
imageOptions = imageOptions || {};
var apiClient = connectionManager.getApiClient(item.ServerId);
return item.BackdropImageTags && item.BackdropImageTags.length > 0 ? item.BackdropImageTags.map(function(imgTag, index) {
return apiClient.getScaledImageUrl(item.BackdropItemId || item.Id, Object.assign(imageOptions, {
type: "Backdrop",
tag: imgTag,
maxWidth: getBackdropMaxWidth(),
index: index
}))
}) : item.ParentBackdropItemId && item.ParentBackdropImageTags && item.ParentBackdropImageTags.length ? item.ParentBackdropImageTags.map(function(imgTag, index) {
return apiClient.getScaledImageUrl(item.ParentBackdropItemId, Object.assign(imageOptions, {
type: "Backdrop",
tag: imgTag,
maxWidth: getBackdropMaxWidth(),
index: index
}))
}) : []
}
function getImageUrls(items, imageOptions) {
for (var list = [], onImg = function(img) {
list.push(img)
}, i = 0, length = items.length; i < length; i++) {
getItemImageUrls(items[i], imageOptions).forEach(onImg)
}
return list
}
function arraysEqual(a, b) {
if (a === b) return !0;
if (null == a || null == b) return !1;
if (a.length !== b.length) return !1;
for (var i = 0; i < a.length; ++i)
if (a[i] !== b[i]) return !1;
return !0
}
function setBackdrops(items, imageOptions, enableImageRotation) {
var images = getImageUrls(items, imageOptions);
images.length ? startRotation(images, enableImageRotation) : clearBackdrop()
}
function startRotation(images, enableImageRotation) {
arraysEqual(images, currentRotatingImages) || (clearRotation(), currentRotatingImages = images, currentRotationIndex = -1, images.length > 1 && !1 !== enableImageRotation && enableRotation() && (rotationInterval = setInterval(onRotationInterval, 24e3)), onRotationInterval())
}
function onRotationInterval() {
if (!playbackManager.isPlayingLocally(["Video"])) {
var newIndex = currentRotationIndex + 1;
newIndex >= currentRotatingImages.length && (newIndex = 0), currentRotationIndex = newIndex, setBackdropImage(currentRotatingImages[newIndex])
}
}
function clearRotation() {
var interval = rotationInterval;
interval && clearInterval(interval), rotationInterval = null, currentRotatingImages = [], currentRotationIndex = -1
}
function setBackdrop(url, imageOptions) {
url && "string" != typeof url && (url = getImageUrls([url], imageOptions)[0]), url ? (clearRotation(), setBackdropImage(url)) : clearBackdrop()
}
Backdrop.prototype.load = function(url, parent, existingBackdropImage) {
var img = new Image,
self = this;
img.onload = function() {
if (!self.isDestroyed) {
var backdropImage = document.createElement("div");
if (backdropImage.classList.add("backdropImage"), backdropImage.classList.add("displayingBackdropImage"), backdropImage.style.backgroundImage = "url('" + url + "')", backdropImage.setAttribute("data-url", url), backdropImage.classList.add("backdropImageFadeIn"), parent.appendChild(backdropImage), !enableAnimation(backdropImage)) return existingBackdropImage && existingBackdropImage.parentNode && existingBackdropImage.parentNode.removeChild(existingBackdropImage), void internalBackdrop(!0);
var onAnimationComplete = function() {
dom.removeEventListener(backdropImage, dom.whichAnimationEvent(), onAnimationComplete, {
once: !0
}), backdropImage === self.currentAnimatingElement && (self.currentAnimatingElement = null), existingBackdropImage && existingBackdropImage.parentNode && existingBackdropImage.parentNode.removeChild(existingBackdropImage)
};
dom.addEventListener(backdropImage, dom.whichAnimationEvent(), onAnimationComplete, {
once: !0
}), internalBackdrop(!0)
}
}, img.src = url
}, Backdrop.prototype.cancelAnimation = function() {
var elem = this.currentAnimatingElement;
elem && (elem.classList.remove("backdropImageFadeIn"), this.currentAnimatingElement = null)
}, Backdrop.prototype.destroy = function() {
this.isDestroyed = !0, this.cancelAnimation()
};
var backdropContainer, backgroundContainer, hasInternalBackdrop, hasExternalBackdrop, currentLoadingBackdrop, rotationInterval, standardWidths = [480, 720, 1280, 1440, 1920],
currentRotatingImages = [],
currentRotationIndex = -1;
return {
setBackdrops: setBackdrops,
setBackdrop: setBackdrop,
clear: clearBackdrop,
externalBackdrop: externalBackdrop
}
});

View file

@ -0,0 +1,41 @@
.backdropContainer {
contain: layout style size
}
.backdropImage {
background-repeat: no-repeat;
background-position: center center;
-webkit-background-size: cover;
background-size: cover;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
contain: layout style
}
.backdropImageFadeIn {
-webkit-animation: backdrop-fadein .8s ease-in normal both;
animation: backdrop-fadein .8s ease-in normal both
}
@-webkit-keyframes backdrop-fadein {
from {
opacity: 0
}
to {
opacity: 1
}
}
@keyframes backdrop-fadein {
from {
opacity: 0
}
to {
opacity: 1
}
}

View file

@ -0,0 +1,69 @@
define([], function() {
"use strict";
function supportsCssAnimation(allowPrefix) {
if (allowPrefix) {
if (!0 === _supportsCssAnimationWithPrefix || !1 === _supportsCssAnimationWithPrefix) return _supportsCssAnimationWithPrefix
} else if (!0 === _supportsCssAnimation || !1 === _supportsCssAnimation) return _supportsCssAnimation;
var animation = !1,
domPrefixes = ["Webkit", "O", "Moz"],
pfx = "",
elm = document.createElement("div");
if (void 0 !== elm.style.animationName && (animation = !0), !1 === animation && allowPrefix)
for (var i = 0; i < domPrefixes.length; i++)
if (void 0 !== elm.style[domPrefixes[i] + "AnimationName"]) {
pfx = domPrefixes[i], pfx + "Animation", "-" + pfx.toLowerCase() + "-", animation = !0;
break
} return allowPrefix ? _supportsCssAnimationWithPrefix = animation : _supportsCssAnimation = animation
}
var _supportsCssAnimation, _supportsCssAnimationWithPrefix, userAgent = navigator.userAgent,
matched = function(ua) {
ua = ua.toLowerCase();
var match = /(edge)[ \/]([\w.]+)/.exec(ua) || /(opera)[ \/]([\w.]+)/.exec(ua) || /(opr)[ \/]([\w.]+)/.exec(ua) || /(chrome)[ \/]([\w.]+)/.exec(ua) || /(safari)[ \/]([\w.]+)/.exec(ua) || /(firefox)[ \/]([\w.]+)/.exec(ua) || /(msie) ([\w.]+)/.exec(ua) || ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || [],
versionMatch = /(version)[ \/]([\w.]+)/.exec(ua),
platform_match = /(ipad)/.exec(ua) || /(iphone)/.exec(ua) || /(windows)/.exec(ua) || /(android)/.exec(ua) || [],
browser = match[1] || "";
"edge" === browser ? platform_match = [""] : -1 !== ua.indexOf("windows phone") || -1 !== ua.indexOf("iemobile") ? browser = "msie" : -1 !== ua.indexOf("like gecko") && -1 === ua.indexOf("webkit") && -1 === ua.indexOf("opera") && -1 === ua.indexOf("chrome") && -1 === ua.indexOf("safari") && (browser = "msie"), "opr" === browser && (browser = "opera");
var version;
versionMatch && versionMatch.length > 2 && (version = versionMatch[2]), version = version || match[2] || "0";
var versionMajor = parseInt(version.split(".")[0]);
return isNaN(versionMajor) && (versionMajor = 0), {
browser: browser,
version: version,
platform: platform_match[0] || "",
versionMajor: versionMajor
}
}(userAgent),
browser = {};
return matched.browser && (browser[matched.browser] = !0, browser.version = matched.version, browser.versionMajor = matched.versionMajor), matched.platform && (browser[matched.platform] = !0), browser.chrome || browser.msie || browser.edge || browser.opera || -1 === userAgent.toLowerCase().indexOf("webkit") || (browser.safari = !0), -1 !== userAgent.toLowerCase().indexOf("playstation 4") && (browser.ps4 = !0, browser.tv = !0),
function(userAgent) {
for (var terms = ["mobi", "ipad", "iphone", "ipod", "silk", "gt-p1000", "nexus 7", "kindle fire", "opera mini"], lower = userAgent.toLowerCase(), i = 0, length = terms.length; i < length; i++)
if (-1 !== lower.indexOf(terms[i])) return !0;
return !1
}(userAgent) && (browser.mobile = !0), browser.xboxOne = -1 !== userAgent.toLowerCase().indexOf("xbox"), browser.animate = "undefined" != typeof document && null != document.documentElement.animate, browser.tizen = -1 !== userAgent.toLowerCase().indexOf("tizen") || null != self.tizen, browser.web0s = -1 !== userAgent.toLowerCase().indexOf("Web0S".toLowerCase()), browser.edgeUwp = browser.edge && (-1 !== userAgent.toLowerCase().indexOf("msapphost") || -1 !== userAgent.toLowerCase().indexOf("webview")), browser.tizen || (browser.orsay = -1 !== userAgent.toLowerCase().indexOf("smarthub")), browser.edgeUwp && (browser.edge = !0), browser.tv = function() {
var userAgent = navigator.userAgent.toLowerCase();
return -1 !== userAgent.indexOf("tv") || (-1 !== userAgent.indexOf("samsungbrowser") || (-1 !== userAgent.indexOf("nintendo") || (-1 !== userAgent.indexOf("viera") || -1 !== userAgent.indexOf("webos"))))
}(), browser.operaTv = browser.tv && -1 !== userAgent.toLowerCase().indexOf("opr/"),
function(prop, value) {
if ("undefined" == typeof window) return !1;
if (value = 2 === arguments.length ? value : "inherit", "CSS" in window && "supports" in window.CSS) return window.CSS.supports(prop, value);
if ("supportsCSS" in window) return window.supportsCSS(prop, value);
try {
var camel = prop.replace(/-([a-z]|[0-9])/gi, function(all, letter) {
return (letter + "").toUpperCase()
}),
support = camel in el.style,
el = document.createElement("div");
return el.style.cssText = prop + ":" + value, support && "" !== el.style[camel]
} catch (err) {
return !1
}
}("display", "flex") || (browser.noFlex = !0), (browser.mobile || browser.tv) && (browser.slow = !0), "undefined" != typeof document && ("ontouchstart" in window || window.DocumentTouch && document instanceof DocumentTouch) && (browser.touch = !0), browser.keyboard = function(browser) {
return !!browser.touch || (!!browser.xboxOne || (!!browser.ps4 || (!!browser.edgeUwp || !!browser.tv)))
}(browser), browser.supportsCssAnimation = supportsCssAnimation, browser.osx = -1 !== userAgent.toLowerCase().indexOf("os x"), browser.iOS = browser.ipad || browser.iphone || browser.ipod, browser.iOS && (browser.iOSVersion = function() {
if (/iP(hone|od|ad)/.test(navigator.platform)) {
var v = navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/);
return [parseInt(v[1], 10), parseInt(v[2], 10), parseInt(v[3] || 0, 10)]
}
}(), browser.iOSVersion = browser.iOSVersion[0] + browser.iOSVersion[1] / 10), browser.chromecast = browser.chrome && -1 !== userAgent.toLowerCase().indexOf("crkey"), browser
});

View file

@ -0,0 +1,384 @@
define(["browser"], function(browser) {
"use strict";
function canPlayH264(videoTestElement) {
return !(!videoTestElement.canPlayType || !videoTestElement.canPlayType('video/mp4; codecs="avc1.42E01E, mp4a.40.2"').replace(/no/, ""))
}
function canPlayH265(videoTestElement, options) {
if (browser.tizen || browser.orsay || browser.xboxOne || browser.web0s || options.supportsHevc) return !0;
var userAgent = navigator.userAgent.toLowerCase();
if (browser.chromecast) {
if (-1 !== userAgent.indexOf("aarch64")) return !0
}
return !!(browser.iOS && (browser.iOSVersion || 0) >= 11) || !(!videoTestElement.canPlayType || !videoTestElement.canPlayType('video/hevc; codecs="hevc, aac"').replace(/no/, ""))
}
function supportsTextTracks() {
return !(!browser.tizen && !browser.orsay) || (null == _supportsTextTracks && (_supportsTextTracks = null != document.createElement("video").textTracks), _supportsTextTracks)
}
function canPlayHls(src) {
return null == _canPlayHls && (_canPlayHls = canPlayNativeHls() || canPlayHlsWithMSE()), _canPlayHls
}
function canPlayNativeHls() {
if (browser.tizen || browser.orsay) return !0;
var media = document.createElement("video");
return !(!media.canPlayType("application/x-mpegURL").replace(/no/, "") && !media.canPlayType("application/vnd.apple.mpegURL").replace(/no/, ""))
}
function canPlayHlsWithMSE() {
return null != window.MediaSource
}
function canPlayAudioFormat(format) {
var typeString;
if ("flac" === format) {
if (browser.tizen || browser.orsay || browser.web0s) return !0;
if (browser.edgeUwp) return !0
} else if ("wma" === format) {
if (browser.tizen || browser.orsay) return !0;
if (browser.edgeUwp) return !0
} else {
if ("opus" === format) return typeString = 'audio/ogg; codecs="opus"', !!document.createElement("audio").canPlayType(typeString).replace(/no/, "");
if ("mp2" === format) return !1
}
if ("webma" === format) typeString = "audio/webm";
else if ("mp2" === format) typeString = "audio/mpeg";
else if ("ogg" === format || "oga" === format) {
if (browser.chrome) return !1;
typeString = "audio/" + format
} else typeString = "audio/" + format;
return !!document.createElement("audio").canPlayType(typeString).replace(/no/, "")
}
function testCanPlayMkv(videoTestElement) {
if (browser.tizen || browser.orsay || browser.web0s) return !0;
if (videoTestElement.canPlayType("video/x-matroska").replace(/no/, "") || videoTestElement.canPlayType("video/mkv").replace(/no/, "")) return !0;
var userAgent = navigator.userAgent.toLowerCase();
return browser.chrome ? !browser.operaTv && (-1 === userAgent.indexOf("vivaldi") && -1 === userAgent.indexOf("opera")) : !!browser.edgeUwp
}
function testCanPlayTs() {
return browser.tizen || browser.orsay || browser.web0s || browser.edgeUwp
}
function supportsMpeg2Video() {
return browser.orsay || browser.tizen || browser.edgeUwp || browser.web0s
}
function supportsVc1() {
return browser.orsay || browser.tizen || browser.edgeUwp || browser.web0s
}
function getDirectPlayProfileForVideoContainer(container, videoAudioCodecs, videoTestElement, options) {
var supported = !1,
profileContainer = container,
videoCodecs = [];
switch (container) {
case "asf":
supported = browser.tizen || browser.orsay || browser.edgeUwp, videoAudioCodecs = [];
break;
case "avi":
supported = browser.tizen || browser.orsay || browser.edgeUwp;
break;
case "mpg":
case "mpeg":
supported = browser.edgeUwp || browser.tizen || browser.orsay;
break;
case "flv":
supported = browser.tizen || browser.orsay;
break;
case "3gp":
case "mts":
case "trp":
case "vob":
case "vro":
supported = browser.tizen || browser.orsay;
break;
case "mov":
supported = browser.tizen || browser.orsay || browser.chrome || browser.edgeUwp, videoCodecs.push("h264");
break;
case "m2ts":
supported = browser.tizen || browser.orsay || browser.web0s || browser.edgeUwp, videoCodecs.push("h264"), supportsVc1() && videoCodecs.push("vc1"), supportsMpeg2Video() && videoCodecs.push("mpeg2video");
break;
case "wmv":
supported = browser.tizen || browser.orsay || browser.web0s || browser.edgeUwp, videoAudioCodecs = [];
break;
case "ts":
supported = testCanPlayTs(), videoCodecs.push("h264"), canPlayH265(videoTestElement, options) && (videoCodecs.push("h265"), videoCodecs.push("hevc")), supportsVc1() && videoCodecs.push("vc1"), supportsMpeg2Video() && videoCodecs.push("mpeg2video"), profileContainer = "ts,mpegts"
}
return supported ? {
Container: profileContainer,
Type: "Video",
VideoCodec: videoCodecs.join(","),
AudioCodec: videoAudioCodecs.join(",")
} : null
}
function getGlobalMaxVideoBitrate() {
var userAgent = navigator.userAgent.toLowerCase();
if (browser.chromecast) {
return -1 !== userAgent.indexOf("aarch64") ? null : self.screen && self.screen.width >= 3800 ? null : 3e7
}
var isTizenFhd = !1;
if (browser.tizen) try {
isTizenFhd = !webapis.productinfo.isUdPanelSupported(), console.log("isTizenFhd = " + isTizenFhd)
} catch (error) {
console.log("isUdPanelSupported() error code = " + error.code)
}
return browser.ps4 ? 8e6 : browser.xboxOne ? 12e6 : browser.edgeUwp ? null : browser.tizen && isTizenFhd ? 2e7 : null
}
function supportsAc3(videoTestElement) {
return !!(browser.edgeUwp || browser.tizen || browser.orsay || browser.web0s) || videoTestElement.canPlayType('audio/mp4; codecs="ac-3"').replace(/no/, "") && !browser.osx && !browser.iOS
}
function supportsEac3(videoTestElement) {
return !!(browser.tizen || browser.orsay || browser.web0s) || videoTestElement.canPlayType('audio/mp4; codecs="ec-3"').replace(/no/, "")
}
var _supportsTextTracks, _canPlayHls;
return function(options) {
options = options || {};
var physicalAudioChannels = options.audioChannels || (browser.tv || browser.ps4 || browser.xboxOne ? 6 : 2),
videoTestElement = document.createElement("video"),
canPlayVp8 = videoTestElement.canPlayType('video/webm; codecs="vp8"').replace(/no/, ""),
canPlayVp9 = videoTestElement.canPlayType('video/webm; codecs="vp9"').replace(/no/, ""),
webmAudioCodecs = ["vorbis"],
canPlayMkv = testCanPlayMkv(videoTestElement),
profile = {};
profile.MaxStreamingBitrate = 12e7, profile.MaxStaticBitrate = 1e8, profile.MusicStreamingTranscodingBitrate = Math.min(12e7, 192e3), profile.DirectPlayProfiles = [];
var videoAudioCodecs = [],
hlsVideoAudioCodecs = [],
supportsMp3VideoAudio = videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.69"').replace(/no/, "") || videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.6B"').replace(/no/, ""),
supportsMp2VideoAudio = browser.edgeUwp || browser.tizen || browser.orsay || browser.web0s,
maxVideoWidth = browser.xboxOne && self.screen ? self.screen.width : null;
options.maxVideoWidth && (maxVideoWidth = options.maxVideoWidth);
var canPlayAacVideoAudio = videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.40.2"').replace(/no/, "");
if (canPlayAacVideoAudio && browser.chromecast && physicalAudioChannels <= 2 && videoAudioCodecs.push("aac"), supportsAc3(videoTestElement)) {
videoAudioCodecs.push("ac3");
var eAc3 = supportsEac3(videoTestElement);
eAc3 && videoAudioCodecs.push("eac3");
(!browser.edge || !browser.touch || browser.edgeUwp) && (hlsVideoAudioCodecs.push("ac3"), eAc3 && hlsVideoAudioCodecs.push("eac3"))
}
canPlayAacVideoAudio && browser.chromecast && -1 === videoAudioCodecs.indexOf("aac") && videoAudioCodecs.push("aac"), supportsMp3VideoAudio && (videoAudioCodecs.push("mp3"), browser.ps4 || physicalAudioChannels <= 2 && hlsVideoAudioCodecs.push("mp3")), canPlayAacVideoAudio && (-1 === videoAudioCodecs.indexOf("aac") && videoAudioCodecs.push("aac"), hlsVideoAudioCodecs.push("aac")), supportsMp3VideoAudio && (browser.ps4 || -1 === hlsVideoAudioCodecs.indexOf("mp3") && hlsVideoAudioCodecs.push("mp3")), supportsMp2VideoAudio && videoAudioCodecs.push("mp2");
var supportsDts = browser.tizen || browser.orsay || browser.web0s || options.supportsDts;
if (self.tizen && self.tizen.systeminfo) {
var v = tizen.systeminfo.getCapability("http://tizen.org/feature/platform.version");
v && parseFloat(v) >= parseFloat("4.0") && (supportsDts = !1)
}
supportsDts && (videoAudioCodecs.push("dca"), videoAudioCodecs.push("dts")), (browser.tizen || browser.orsay || browser.web0s) && (videoAudioCodecs.push("pcm_s16le"), videoAudioCodecs.push("pcm_s24le")), options.supportsTrueHd && videoAudioCodecs.push("truehd"), (browser.tizen || browser.orsay) && videoAudioCodecs.push("aac_latm"), canPlayAudioFormat("opus") && (videoAudioCodecs.push("opus"), hlsVideoAudioCodecs.push("opus"), webmAudioCodecs.push("opus")), canPlayAudioFormat("flac") && videoAudioCodecs.push("flac"), videoAudioCodecs = videoAudioCodecs.filter(function(c) {
return -1 === (options.disableVideoAudioCodecs || []).indexOf(c)
}), hlsVideoAudioCodecs = hlsVideoAudioCodecs.filter(function(c) {
return -1 === (options.disableHlsVideoAudioCodecs || []).indexOf(c)
});
var mp4VideoCodecs = [],
hlsVideoCodecs = [];
canPlayH264(videoTestElement) && (mp4VideoCodecs.push("h264"), hlsVideoCodecs.push("h264")), canPlayH265(videoTestElement, options) && (mp4VideoCodecs.push("h265"), mp4VideoCodecs.push("hevc"), (browser.tizen || browser.web0s) && (hlsVideoCodecs.push("h265"), hlsVideoCodecs.push("hevc"))), supportsMpeg2Video() && mp4VideoCodecs.push("mpeg2video"), supportsVc1() && mp4VideoCodecs.push("vc1"), (browser.tizen || browser.orsay) && mp4VideoCodecs.push("msmpeg4v2"), canPlayVp8 && mp4VideoCodecs.push("vp8"), canPlayVp9 && mp4VideoCodecs.push("vp9"), (canPlayVp8 || browser.tizen || browser.orsay) && videoAudioCodecs.push("vorbis"), mp4VideoCodecs.length && profile.DirectPlayProfiles.push({
Container: "mp4,m4v",
Type: "Video",
VideoCodec: mp4VideoCodecs.join(","),
AudioCodec: videoAudioCodecs.join(",")
}), canPlayMkv && mp4VideoCodecs.length && profile.DirectPlayProfiles.push({
Container: "mkv",
Type: "Video",
VideoCodec: mp4VideoCodecs.join(","),
AudioCodec: videoAudioCodecs.join(",")
}), ["m2ts", "wmv", "ts", "asf", "avi", "mpg", "mpeg", "flv", "3gp", "mts", "trp", "vob", "vro", "mov"].map(function(container) {
return getDirectPlayProfileForVideoContainer(container, videoAudioCodecs, videoTestElement, options)
}).filter(function(i) {
return null != i
}).forEach(function(i) {
profile.DirectPlayProfiles.push(i)
}), ["opus", "mp3", "mp2", "aac", "flac", "alac", "webma", "wma", "wav", "ogg", "oga"].filter(canPlayAudioFormat).forEach(function(audioFormat) {
"mp2" === audioFormat ? profile.DirectPlayProfiles.push({
Container: "mp2,mp3",
Type: "Audio",
AudioCodec: audioFormat
}) : "mp3" === audioFormat ? profile.DirectPlayProfiles.push({
Container: audioFormat,
Type: "Audio",
AudioCodec: audioFormat
}) : profile.DirectPlayProfiles.push({
Container: "webma" === audioFormat ? "webma,webm" : audioFormat,
Type: "Audio"
}), "aac" !== audioFormat && "alac" !== audioFormat || profile.DirectPlayProfiles.push({
Container: "m4a",
AudioCodec: audioFormat,
Type: "Audio"
})
}), canPlayVp8 && profile.DirectPlayProfiles.push({
Container: "webm",
Type: "Video",
AudioCodec: webmAudioCodecs.join(","),
VideoCodec: "VP8"
}), canPlayVp9 && profile.DirectPlayProfiles.push({
Container: "webm",
Type: "Video",
AudioCodec: webmAudioCodecs.join(","),
VideoCodec: "VP9"
}), profile.TranscodingProfiles = [];
var hlsBreakOnNonKeyFrames = !(!(browser.iOS || browser.osx || browser.edge) && canPlayNativeHls());
canPlayHls() && !1 !== browser.enableHlsAudio && profile.TranscodingProfiles.push({
Container: !canPlayNativeHls() || browser.edge || browser.android ? "ts" : "aac",
Type: "Audio",
AudioCodec: "aac",
Context: "Streaming",
Protocol: "hls",
MaxAudioChannels: physicalAudioChannels.toString(),
MinSegments: browser.iOS || browser.osx ? "2" : "1",
BreakOnNonKeyFrames: hlsBreakOnNonKeyFrames
}), ["aac", "mp3", "opus", "wav"].filter(canPlayAudioFormat).forEach(function(audioFormat) {
profile.TranscodingProfiles.push({
Container: audioFormat,
Type: "Audio",
AudioCodec: audioFormat,
Context: "Streaming",
Protocol: "http",
MaxAudioChannels: physicalAudioChannels.toString()
})
}), ["opus", "mp3", "aac", "wav"].filter(canPlayAudioFormat).forEach(function(audioFormat) {
profile.TranscodingProfiles.push({
Container: audioFormat,
Type: "Audio",
AudioCodec: audioFormat,
Context: "Static",
Protocol: "http",
MaxAudioChannels: physicalAudioChannels.toString()
})
}), !canPlayMkv || browser.tizen || browser.orsay || !1 === options.enableMkvProgressive || profile.TranscodingProfiles.push({
Container: "mkv",
Type: "Video",
AudioCodec: videoAudioCodecs.join(","),
VideoCodec: mp4VideoCodecs.join(","),
Context: "Streaming",
MaxAudioChannels: physicalAudioChannels.toString(),
CopyTimestamps: !0
}), canPlayMkv && profile.TranscodingProfiles.push({
Container: "mkv",
Type: "Video",
AudioCodec: videoAudioCodecs.join(","),
VideoCodec: mp4VideoCodecs.join(","),
Context: "Static",
MaxAudioChannels: physicalAudioChannels.toString(),
CopyTimestamps: !0
}), canPlayHls() && !1 !== options.enableHls && profile.TranscodingProfiles.push({
Container: "ts",
Type: "Video",
AudioCodec: hlsVideoAudioCodecs.join(","),
VideoCodec: hlsVideoCodecs.join(","),
Context: "Streaming",
Protocol: "hls",
MaxAudioChannels: physicalAudioChannels.toString(),
MinSegments: browser.iOS || browser.osx ? "2" : "1",
BreakOnNonKeyFrames: hlsBreakOnNonKeyFrames
}), canPlayVp8 && profile.TranscodingProfiles.push({
Container: "webm",
Type: "Video",
AudioCodec: "vorbis",
VideoCodec: "vpx",
Context: "Streaming",
Protocol: "http",
MaxAudioChannels: physicalAudioChannels.toString()
}), profile.TranscodingProfiles.push({
Container: "mp4",
Type: "Video",
AudioCodec: videoAudioCodecs.join(","),
VideoCodec: "h264",
Context: "Static",
Protocol: "http"
}), profile.ContainerProfiles = [], profile.CodecProfiles = [];
var supportsSecondaryAudio = browser.tizen || browser.orsay || videoTestElement.audioTracks,
aacCodecProfileConditions = [];
videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.40.5"').replace(/no/, "") || aacCodecProfileConditions.push({
Condition: "NotEquals",
Property: "AudioProfile",
Value: "HE-AAC"
}), supportsSecondaryAudio || aacCodecProfileConditions.push({
Condition: "Equals",
Property: "IsSecondaryAudio",
Value: "false",
IsRequired: "false"
}), browser.chromecast && aacCodecProfileConditions.push({
Condition: "LessThanEqual",
Property: "AudioChannels",
Value: "2",
IsRequired: !0
}), aacCodecProfileConditions.length && profile.CodecProfiles.push({
Type: "VideoAudio",
Codec: "aac",
Conditions: aacCodecProfileConditions
}), supportsSecondaryAudio || profile.CodecProfiles.push({
Type: "VideoAudio",
Conditions: [{
Condition: "Equals",
Property: "IsSecondaryAudio",
Value: "false",
IsRequired: "false"
}]
});
var maxH264Level = browser.chromecast ? 42 : 51,
h264Profiles = "high|main|baseline|constrained baseline";
maxH264Level >= 51 && browser.chrome && !browser.osx && (h264Profiles += "|high 10"), profile.CodecProfiles.push({
Type: "Video",
Codec: "h264",
Conditions: [{
Condition: "NotEquals",
Property: "IsAnamorphic",
Value: "true",
IsRequired: !1
}, {
Condition: "EqualsAny",
Property: "VideoProfile",
Value: h264Profiles
}, {
Condition: "LessThanEqual",
Property: "VideoLevel",
Value: maxH264Level.toString()
}]
}), browser.edgeUwp || browser.tizen || browser.orsay || browser.web0s, maxVideoWidth && profile.CodecProfiles[profile.CodecProfiles.length - 1].Conditions.push({
Condition: "LessThanEqual",
Property: "Width",
Value: maxVideoWidth.toString(),
IsRequired: !1
});
var globalMaxVideoBitrate = (getGlobalMaxVideoBitrate() || "").toString(),
h264MaxVideoBitrate = globalMaxVideoBitrate;
h264MaxVideoBitrate && profile.CodecProfiles[profile.CodecProfiles.length - 1].Conditions.push({
Condition: "LessThanEqual",
Property: "VideoBitrate",
Value: h264MaxVideoBitrate,
IsRequired: !0
});
var globalVideoConditions = [];
return globalMaxVideoBitrate && globalVideoConditions.push({
Condition: "LessThanEqual",
Property: "VideoBitrate",
Value: globalMaxVideoBitrate
}), maxVideoWidth && globalVideoConditions.push({
Condition: "LessThanEqual",
Property: "Width",
Value: maxVideoWidth.toString(),
IsRequired: !1
}), globalVideoConditions.length && profile.CodecProfiles.push({
Type: "Video",
Conditions: globalVideoConditions
}), browser.chromecast && profile.CodecProfiles.push({
Type: "Audio",
Codec: "flac",
Conditions: [{
Condition: "LessThanEqual",
Property: "AudioSampleRate",
Value: "96000"
}]
}), profile.SubtitleProfiles = [], supportsTextTracks() && profile.SubtitleProfiles.push({
Format: "vtt",
Method: "External"
}), profile.ResponseProfiles = [], profile.ResponseProfiles.push({
Type: "Video",
Container: "m4v",
MimeType: "video/mp4"
}), profile
}
});

View file

@ -0,0 +1,885 @@
.card,
.card:focus {
font-weight: inherit !important
}
.card,
.cardBox,
.cardContent,
.textActionButton {
-webkit-tap-highlight-color: transparent;
outline: 0 !important
}
button::-moz-focus-inner {
padding: 0;
border: 0
}
button {
-webkit-border-fit: border !important
}
.card {
border: 0;
font-size: inherit !important;
font-family: inherit !important;
text-transform: none;
background: 0 0 !important;
margin: 0;
padding: 0;
display: block;
color: inherit !important;
cursor: pointer;
contain: layout style;
-webkit-flex-shrink: 0;
flex-shrink: 0
}
.cardContent-button,
.textActionButton {
cursor: pointer;
vertical-align: middle;
font-family: inherit
}
.card-nofocustransform {
contain: layout style paint
}
.itemsContainer {
display: -webkit-box;
display: -webkit-flex;
display: flex
}
.vertical-list,
.vertical-wrap {
display: -webkit-box;
display: -webkit-flex;
-webkit-box-direction: normal
}
.vertical-list {
display: flex;
-webkit-box-orient: vertical;
-webkit-flex-direction: column;
flex-direction: column;
-webkit-flex-wrap: nowrap;
flex-wrap: nowrap
}
.vertical-wrap {
display: flex;
-webkit-box-orient: horizontal;
-webkit-flex-direction: row;
flex-direction: row;
-webkit-flex-wrap: wrap;
flex-wrap: wrap
}
.cardImageContainer,
.mediaSourceIndicator {
display: -webkit-box;
-webkit-box-align: center
}
.vertical-wrap.centered {
-webkit-box-pack: center;
-webkit-justify-content: center;
justify-content: center
}
.cardScalable {
position: relative;
contain: layout style
}
.cardPadder-backdrop,
.cardPadder-mixedBackdrop,
.cardPadder-overflowBackdrop,
.cardPadder-overflowSmallBackdrop,
.cardPadder-smallBackdrop {
padding-bottom: 56.25%;
contain: strict
}
.cardPadder-mixedSquare,
.cardPadder-overflowSquare,
.cardPadder-square,
.overflowSquareCard-textCardPadder {
padding-bottom: 100%;
contain: strict
}
.cardPadder-mixedPortrait,
.cardPadder-overflowPortrait,
.cardPadder-portrait,
.overflowPortraitCard-textCardPadder {
padding-bottom: 150%;
contain: strict
}
.cardPadder-banner {
padding-bottom: 18.5%;
contain: strict
}
.cardBox {
padding: 0 !important;
margin: .42em;
-webkit-transition: none;
-o-transition: none;
transition: none;
border: 0 solid transparent;
contain: layout style
}
@media (min-width:50em) {
.cardBox {
margin: .9em
}
}
.cardBox-withfocuscontent-large {
margin: .4em
}
.card-focuscontent-large {
border: .5em solid transparent
}
.cardBox-focustransform {
will-change: transform;
-webkit-transition: -webkit-transform .2s ease-out;
-o-transition: transform .2s ease-out;
transition: transform .2s ease-out
}
.card:focus>.cardBox-focustransform {
-webkit-transform: scale(1.18, 1.18);
transform: scale(1.18, 1.18)
}
.cardBox-bottompadded {
margin-bottom: 1.8em !important
}
@media (max-width:50em) {
.cardBox-bottompadded {
margin-bottom: 1.2em !important
}
}
.card:focus {
position: relative !important;
z-index: 10 !important
}
.btnCardOptions {
position: absolute;
bottom: .25em;
right: 0;
margin: 0 !important;
z-index: 1
}
.mediaSourceIndicator {
display: -webkit-flex;
display: flex;
position: absolute;
-webkit-align-items: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
justify-content: center;
top: .3em;
left: .3em;
text-align: center;
vertical-align: middle;
width: 1.6em;
height: 1.6em;
-webkit-border-radius: 50%;
border-radius: 50%;
color: #fff;
background: #38c
}
.cardText,
.innerCardFooter {
overflow: hidden;
text-align: left
}
.cardImageContainer {
-webkit-background-size: contain;
background-size: contain;
background-repeat: no-repeat;
background-position: center center;
-webkit-align-items: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
justify-content: center;
position: relative;
-webkit-background-clip: content-box !important;
background-clip: content-box !important;
color: inherit;
height: 100%;
contain: strict
}
.cardContent,
.cardImage {
position: absolute;
right: 0;
top: 0;
left: 0;
bottom: 0
}
.chapterCardImageContainer {
background-color: #000;
-webkit-border-radius: 0;
border-radius: 0
}
.textCardImageContainer {
background-color: #333
}
.cardContent {
overflow: hidden;
display: block;
margin: 0 !important;
height: 100%;
contain: strict
}
.cardContent-button {
border: 0 !important;
padding: 0 !important;
color: inherit;
width: 100%;
font-size: inherit
}
.cardContent-button:not(.defaultCardBackground) {
background-color: transparent
}
.visualCardBox .cardContent {
-webkit-border-bottom-left-radius: 0;
border-bottom-left-radius: 0;
-webkit-border-bottom-right-radius: 0;
border-bottom-right-radius: 0
}
.cardContent-shadow {
-webkit-box-shadow: 0 .0725em .29em 0 rgba(0, 0, 0, .37);
box-shadow: 0 .0725em .29em 0 rgba(0, 0, 0, .37)
}
.cardImageContainer {
display: -webkit-box;
display: -webkit-flex;
display: flex
}
.cardImage {
-webkit-background-size: contain;
background-size: contain;
background-repeat: no-repeat;
background-position: center bottom
}
.cardImage-img {
max-height: 100%;
max-width: 100%;
min-height: 70%;
min-width: 70%;
margin: auto
}
.coveredImage-img {
width: 100%;
height: 100%
}
.coveredImage-noscale-img {
max-height: none;
max-width: none
}
.coveredImage {
-webkit-background-size: 100% 100%;
background-size: 100% 100%;
background-position: center center
}
.coveredImage-noScale {
-webkit-background-size: cover;
background-size: cover
}
.cardFooter {
padding: .3em .3em .5em;
position: relative
}
.visualCardBox {
-webkit-box-shadow: 0 .0725em .29em 0 rgba(0, 0, 0, .37);
box-shadow: 0 .0725em .29em 0 rgba(0, 0, 0, .37);
-webkit-border-radius: .145em;
border-radius: .145em
}
.innerCardFooter {
background: rgba(0, 0, 0, .7);
position: absolute;
bottom: 0;
left: 0;
z-index: 1;
max-width: 100%;
color: #fff
}
.innerCardFooterClear {
background-color: transparent
}
.fullInnerCardFooter {
right: 0
}
.cardText {
padding: .06em .5em;
white-space: nowrap;
-o-text-overflow: ellipsis;
text-overflow: ellipsis
}
.cardDefaultText,
.cardTextCentered {
text-align: center
}
.cardText-secondary {
font-size: 86%
}
.cardText-first {
padding-top: .24em
}
.innerCardFooter>.cardText {
padding: .3em .5em
}
.cardFooter-withlogo {
padding-left: 4em;
position: relative
}
.cardFooterLogo {
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 4.5em;
-webkit-background-size: 70% auto;
background-size: 70% auto;
background-repeat: no-repeat;
background-position: center center
}
.cardText-rightmargin {
margin-right: 2em
}
.cardDefaultText {
white-space: normal
}
.textActionButton {
background: 0 0;
border: 0 !important;
padding: 0 !important;
color: inherit;
font-size: inherit
}
.textActionButton:hover {
text-decoration: underline
}
.cardImageIcon {
font-size: 5em;
color: inherit
}
.cardImageIcon-small {
font-size: 3em;
margin-bottom: .1em
}
.cardIndicators {
right: .225em;
top: .225em;
position: absolute;
display: -webkit-box;
display: -webkit-flex;
display: flex;
-webkit-box-align: center;
-webkit-align-items: center;
align-items: center;
contain: layout style
}
.cardProgramAttributeIndicators {
top: 0;
left: 0;
position: absolute;
display: -webkit-box;
display: -webkit-flex;
display: flex;
text-transform: uppercase;
font-size: 92%
}
.programAttributeIndicator {
padding: .18em .5em;
color: #fff;
font-weight: 500
}
.cardOverlayButton {
color: rgba(255, 255, 255, .76) !important;
margin: 0;
z-index: 1;
padding: .75em;
font-size: 88%
}
.cardOverlayButton-br {
position: absolute;
bottom: 0;
right: 0
}
.cardOverlayButtonIcon {
background-color: rgba(0, 0, 0, .7) !important;
-webkit-border-radius: 100em;
border-radius: 100em;
width: 1.5em !important;
height: 1.5em !important;
-webkit-box-pack: center;
-webkit-justify-content: center;
justify-content: center;
-webkit-box-align: center;
-webkit-align-items: center;
align-items: center;
display: -webkit-box;
display: -webkit-flex;
display: flex;
font-size: 1.66956521739130434em !important
}
.cardOverlayButton-centered {
bottom: initial;
right: initial;
position: static;
position: absolute;
display: -webkit-box;
display: -webkit-flex;
display: flex;
font-size: 112%;
margin: -1.3em 0 0 -1.3em;
width: 2.6em;
height: 2.6em;
top: 50%;
left: 50%;
background-color: rgba(0, 0, 0, .5) !important;
border: .06em solid rgba(255, 255, 255, .6);
padding: .38em !important;
color: rgba(255, 255, 255, .76);
-webkit-transition: -webkit-transform .2s ease-out;
-o-transition: transform .2s ease-out;
transition: transform .2s ease-out
}
.cardOverlayButton-centered:hover {
-webkit-transform: scale(1.2, 1.2);
transform: scale(1.2, 1.2)
}
.backdropCard,
.bannerCard {
width: 100%
}
.smallBackdropCard,
.squareCard {
width: 50%
}
.portraitCard {
width: 33.333333333333333333333333333333%
}
.mixedPortraitCard {
width: 12em
}
.mixedSquareCard {
width: 18em
}
.mixedBackdropCard {
width: 32em
}
@media (min-width:25em) {
.backdropCard {
width: 50%
}
}
@media (min-width:31.25em) {
.portraitCard,
.smallBackdropCard,
.squareCard {
width: 33.333333333333333333333333333333%
}
}
@media (min-width:43.75em) {
.portraitCard,
.squareCard {
width: 25%
}
}
@media (min-width:48.125em) {
.backdropCard {
width: 33.333333333333333333333333333333%
}
}
@media (min-width:50em) {
.bannerCard {
width: 50%
}
.portraitCard,
.squareCard {
width: 20%
}
.smallBackdropCard {
width: 25%
}
}
@media (min-width:62.5em) {
.smallBackdropCard {
width: 20%
}
}
@media (min-width:75em) {
.backdropCard {
width: 25%
}
.portraitCard,
.squareCard {
width: 16.666666666666666666666666666667%
}
.bannerCard {
width: 33.333333333333333333333333333333%
}
.smallBackdropCard {
width: 16.666666666666666666666666666667%
}
}
@media (min-width:87.5em) {
.portraitCard,
.smallBackdropCard,
.squareCard {
width: 14.285714285714285714285714285714%
}
}
@media (min-width:100em) {
.smallBackdropCard {
width: 12.5%
}
.backdropCard {
width: 20%
}
.portraitCard,
.squareCard {
width: 12.5%
}
}
@media (min-width:120em) {
.portraitCard,
.squareCard {
width: 11.111111111111111111111111111111%
}
}
@media (min-width:131.25em) {
.bannerCard {
width: 25%
}
.portraitCard,
.squareCard {
width: 10%
}
}
@media (min-width:156.25em) {
.backdropCard {
width: 16.666666666666666666666666666667%
}
}
.itemsContainer-tv>.backdropCard {
width: 25%
}
.itemsContainer-tv>.portraitCard,
.itemsContainer-tv>.squareCard {
width: 16.666666666666666666666666666667%
}
.overflowBackdropCard,
.overflowSmallBackdropCard {
width: 72vw
}
.overflowPortraitCard,
.overflowSquareCard {
width: 40vw
}
@media (min-width:25em) {
.overflowPortraitCard {
width: 31.2vw
}
}
@media (min-width:35em) {
.overflowSquareCard {
width: 31.2vw
}
.overflowBackdropCard {
width: 45.5vw
}
.overflowSmallBackdropCard {
width: 30vw
}
}
@media (min-width:43.75em) {
.overflowPortraitCard,
.overflowSquareCard {
width: 23.3vw
}
}
@media (min-width:48.125em) {
.overflowBackdropCard,
.overflowSmallBackdropCard {
width: 30vw
}
}
@media (orientation:landscape) {
.overflowBackdropCard,
.overflowSmallBackdropCard {
width: 30vw
}
.overflowPortraitCard,
.overflowSquareCard {
width: 23.3vw
}
}
@media (orientation:landscape) and (min-width:48.125em) {
.overflowBackdropCard,
.overflowSmallBackdropCard {
width: 23.3vw
}
}
@media (orientation:landscape) and (min-width:50em) {
.overflowSmallBackdropCard {
width: 15.5vw
}
}
@media (min-width:50em) {
.overflowPortraitCard,
.overflowSquareCard {
width: 18.4vw
}
}
@media (min-width:75em) {
.overflowBackdropCard,
.overflowSmallBackdropCard {
width: 23.3vw
}
.overflowPortraitCard,
.overflowSquareCard {
width: 15.5vw
}
}
@media (min-width:87.5em) {
.overflowPortraitCard,
.overflowSquareCard {
width: 13.3vw
}
}
@media (min-width:100em) {
.overflowBackdropCard,
.overflowSmallBackdropCard {
width: 18.7vw
}
.overflowPortraitCard,
.overflowSquareCard {
width: 11.6vw
}
}
@media (min-width:120em) {
.overflowPortraitCard,
.overflowSquareCard {
width: 10.3vw
}
}
@media (min-width:131.25em) {
.overflowPortraitCard,
.overflowSquareCard {
width: 9.3vw
}
}
@media (min-width:156.25em) {
.overflowBackdropCard,
.overflowSmallBackdropCard {
width: 15.6vw
}
}
.itemsContainer-tv>.overflowBackdropCard {
width: 23.5vw
}
.overflowBackdropCard-textCard {
width: 15.5vw !important
}
.overflowBackdropCard-textCardPadder {
padding-bottom: 87.75%
}
.itemsContainer-tv>.overflowPortraitCard,
.itemsContainer-tv>.overflowSquareCard {
width: 15.6vw
}
.itemsContainer-tv>.overflowSmallBackdropCard {
width: 18.8vw
}
.cardOverlayContainer {
background: -webkit-radial-gradient(50% 50%, farthest-corner, rgba(30, 30, 30, .5) 50%, #2c2c2c 100%);
background: -o-radial-gradient(50% 50%, farthest-corner, rgba(30, 30, 30, .5) 50%, #2c2c2c 100%);
background: radial-gradient(farthest-corner at 50% 50%, rgba(30, 30, 30, .5) 50%, #2c2c2c 100%);
opacity: 0;
-webkit-transition: opacity .2s;
-o-transition: opacity .2s;
transition: opacity .2s;
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none
}
.card-hoverable :hover .cardOverlayContainer {
opacity: 1
}
.cardOverlayButton-hover {
opacity: 0;
-webkit-transition: opacity .2s;
-o-transition: opacity .2s;
transition: opacity .2s;
background: 0 0;
color: #fff !important;
padding: .5em
}
.cardOverlayButtonIcon-hover {
background: 0 0 !important
}
.card-hoverable:hover .cardOverlayButton-hover {
opacity: 1
}
.cardOverlayFab-primary {
font-size: 130%;
padding: 0;
width: 3em;
height: 3em;
margin-top: -1.5em;
margin-left: -1.5em;
position: absolute;
top: 50%;
left: 50%
}
.cardOverlayFab-primary i {
border: .07em solid rgba(255, 255, 255, .9);
color: #fff
}

View file

@ -0,0 +1,515 @@
define(["datetime", "imageLoader", "connectionManager", "itemHelper", "focusManager", "indicators", "globalize", "layoutManager", "apphost", "dom", "browser", "playbackManager", "itemShortcuts", "css!./card", "paper-icon-button-light", "programStyles"], function(datetime, imageLoader, connectionManager, itemHelper, focusManager, indicators, globalize, layoutManager, appHost, dom, browser, playbackManager, itemShortcuts) {
"use strict";
function getCardsHtml(items, options) {
return 1 === arguments.length && (options = arguments[0], items = options.items), buildCardsHtmlInternal(items, options)
}
function getPostersPerRow(shape, screenWidth, isOrientationLandscape) {
switch (shape) {
case "portrait":
return layoutManager.tv ? 5.9999999988 : screenWidth >= 2200 ? 10 : screenWidth >= 1920 ? 9.000000000009 : screenWidth >= 1600 ? 8 : screenWidth >= 1400 ? 7.0000000000021 : screenWidth >= 1200 ? 5.9999999988 : screenWidth >= 800 ? 5 : screenWidth >= 700 ? 4 : 3.0000000003;
case "square":
return layoutManager.tv ? 5.9999999988 : screenWidth >= 2200 ? 10 : screenWidth >= 1920 ? 9.000000000009 : screenWidth >= 1600 ? 8 : screenWidth >= 1400 ? 7.0000000000021 : screenWidth >= 1200 ? 5.9999999988 : screenWidth >= 800 ? 5 : screenWidth >= 700 ? 4 : screenWidth >= 500 ? 3.0000000003 : 2;
case "banner":
return screenWidth >= 2200 ? 4 : screenWidth >= 1200 ? 3.0000000003 : screenWidth >= 800 ? 2 : 1;
case "backdrop":
return layoutManager.tv ? 4 : screenWidth >= 2500 ? 6 : screenWidth >= 1600 ? 5 : screenWidth >= 1200 ? 4 : screenWidth >= 770 ? 3 : screenWidth >= 420 ? 2 : 1;
case "smallBackdrop":
return screenWidth >= 1600 ? 8 : screenWidth >= 1400 ? 7.000000000007001 : screenWidth >= 1200 ? 6 : screenWidth >= 1e3 ? 5 : screenWidth >= 800 ? 4 : screenWidth >= 500 ? 3.0000000003 : 2;
case "overflowSmallBackdrop":
return layoutManager.tv ? 100 / 18.9 : isOrientationLandscape ? screenWidth >= 800 ? 100 / 15.5 : 100 / 23.3 : screenWidth >= 540 ? 100 / 30 : 100 / 72;
case "overflowPortrait":
return layoutManager.tv ? 100 / 15.5 : isOrientationLandscape ? screenWidth >= 1700 ? 100 / 11.6 : 100 / 15.5 : screenWidth >= 1400 ? 100 / 15 : screenWidth >= 1200 ? 100 / 18 : screenWidth >= 760 ? 100 / 23 : screenWidth >= 400 ? 100 / 31.5 : 100 / 42;
case "overflowSquare":
return layoutManager.tv ? 100 / 15.5 : isOrientationLandscape ? screenWidth >= 1700 ? 100 / 11.6 : 100 / 15.5 : screenWidth >= 1400 ? 100 / 15 : screenWidth >= 1200 ? 100 / 18 : screenWidth >= 760 ? 100 / 23 : screenWidth >= 540 ? 100 / 31.5 : 100 / 42;
case "overflowBackdrop":
return layoutManager.tv ? 100 / 23.3 : isOrientationLandscape ? screenWidth >= 1700 ? 100 / 18.5 : 100 / 23.3 : screenWidth >= 1800 ? 100 / 23.5 : screenWidth >= 1400 ? 100 / 30 : screenWidth >= 760 ? 2.5 : screenWidth >= 640 ? 100 / 56 : 100 / 72;
default:
return 4
}
}
function isResizable(windowWidth) {
var screen = window.screen;
if (screen) {
if (screen.availWidth - windowWidth > 20) return !0
}
return !1
}
function getImageWidth(shape, screenWidth, isOrientationLandscape) {
var imagesPerRow = getPostersPerRow(shape, screenWidth, isOrientationLandscape),
shapeWidth = screenWidth / imagesPerRow;
return Math.round(shapeWidth)
}
function setCardData(items, options) {
options.shape = options.shape || "auto";
var primaryImageAspectRatio = imageLoader.getPrimaryImageAspectRatio(items);
if ("auto" === options.shape || "autohome" === options.shape || "autooverflow" === options.shape || "autoVertical" === options.shape) {
var requestedShape = options.shape;
options.shape = null, primaryImageAspectRatio && (primaryImageAspectRatio >= 3 ? (options.shape = "banner", options.coverImage = !0) : options.shape = primaryImageAspectRatio >= 1.33 ? "autooverflow" === requestedShape ? "overflowBackdrop" : "backdrop" : primaryImageAspectRatio > .71 ? "autooverflow" === requestedShape ? "overflowSquare" : "square" : "autooverflow" === requestedShape ? "overflowPortrait" : "portrait"), options.shape || (options.shape = options.defaultShape || ("autooverflow" === requestedShape ? "overflowSquare" : "square"))
}
if ("auto" === options.preferThumb && (options.preferThumb = "backdrop" === options.shape || "overflowBackdrop" === options.shape), options.uiAspect = getDesiredAspect(options.shape), options.primaryImageAspectRatio = primaryImageAspectRatio, !options.width && options.widths && (options.width = options.widths[options.shape]), options.rows && "number" != typeof options.rows && (options.rows = options.rows[options.shape]), !options.width) {
var screenWidth = dom.getWindowSize().innerWidth,
screenHeight = dom.getWindowSize().innerHeight;
if (isResizable(screenWidth)) {
screenWidth = 100 * Math.floor(screenWidth / 100)
}
options.width = getImageWidth(options.shape, screenWidth, screenWidth > 1.3 * screenHeight)
}
}
function buildCardsHtmlInternal(items, options) {
var isVertical;
"autoVertical" === options.shape && (isVertical = !0), setCardData(items, options);
var currentIndexValue, hasOpenRow, hasOpenSection, apiClient, lastServerId, i, length, html = "",
itemsInRow = 0,
sectionTitleTagName = options.sectionTitleTagName || "div";
for (i = 0, length = items.length; i < length; i++) {
var item = items[i],
serverId = item.ServerId || options.serverId;
if (serverId !== lastServerId && (lastServerId = serverId, apiClient = connectionManager.getApiClient(lastServerId)), options.indexBy) {
var newIndexValue = "";
if ("PremiereDate" === options.indexBy) {
if (item.PremiereDate) try {
newIndexValue = datetime.toLocaleDateString(datetime.parseISO8601Date(item.PremiereDate), {
weekday: "long",
month: "long",
day: "numeric"
})
} catch (err) {}
} else "ProductionYear" === options.indexBy ? newIndexValue = item.ProductionYear : "CommunityRating" === options.indexBy && (newIndexValue = item.CommunityRating ? Math.floor(item.CommunityRating) + (item.CommunityRating % 1 >= .5 ? .5 : 0) + "+" : null);
newIndexValue !== currentIndexValue && (hasOpenRow && (html += "</div>", hasOpenRow = !1, itemsInRow = 0), hasOpenSection && (html += "</div>", isVertical && (html += "</div>"), hasOpenSection = !1), html += isVertical ? '<div class="verticalSection">' : '<div class="horizontalSection">', html += "<" + sectionTitleTagName + ' class="sectionTitle">' + newIndexValue + "</" + sectionTitleTagName + ">", isVertical && (html += '<div class="itemsContainer vertical-wrap">'), currentIndexValue = newIndexValue, hasOpenSection = !0)
}
options.rows && 0 === itemsInRow && (hasOpenRow && (html += "</div>", hasOpenRow = !1), html += '<div class="cardColumn">', hasOpenRow = !0), html += buildCard(i, item, apiClient, options), itemsInRow++, options.rows && itemsInRow >= options.rows && (html += "</div>", hasOpenRow = !1, itemsInRow = 0)
}
hasOpenRow && (html += "</div>"), hasOpenSection && (html += "</div>", isVertical && (html += "</div>"));
var cardFooterHtml = "";
for (i = 0, length = options.lines || 0; i < length; i++) cardFooterHtml += 0 === i ? '<div class="cardText cardTextCentered cardText-first">&nbsp;</div>' : '<div class="cardText cardTextCentered cardText-secondary">&nbsp;</div>';
return html
}
function getDesiredAspect(shape) {
if (shape) {
if (shape = shape.toLowerCase(), -1 !== shape.indexOf("portrait")) return 2 / 3;
if (-1 !== shape.indexOf("backdrop")) return 16 / 9;
if (-1 !== shape.indexOf("square")) return 1;
if (-1 !== shape.indexOf("banner")) return 1e3 / 185
}
return null
}
function getCardImageUrl(item, apiClient, options, shape) {
item = item.ProgramInfo || item;
var width = options.width,
height = null,
primaryImageAspectRatio = item.PrimaryImageAspectRatio,
forceName = !1,
imgUrl = null,
coverImage = !1,
uiAspect = null;
return options.preferThumb && item.ImageTags && item.ImageTags.Thumb ? imgUrl = apiClient.getScaledImageUrl(item.Id, {
type: "Thumb",
maxWidth: width,
tag: item.ImageTags.Thumb
}) : (options.preferBanner || "banner" === shape) && item.ImageTags && item.ImageTags.Banner ? imgUrl = apiClient.getScaledImageUrl(item.Id, {
type: "Banner",
maxWidth: width,
tag: item.ImageTags.Banner
}) : options.preferDisc && item.ImageTags && item.ImageTags.Disc ? imgUrl = apiClient.getScaledImageUrl(item.Id, {
type: "Disc",
maxWidth: width,
tag: item.ImageTags.Disc
}) : options.preferLogo && item.ImageTags && item.ImageTags.Logo ? imgUrl = apiClient.getScaledImageUrl(item.Id, {
type: "Logo",
maxWidth: width,
tag: item.ImageTags.Logo
}) : options.preferLogo && item.ParentLogoImageTag && item.ParentLogoItemId ? imgUrl = apiClient.getScaledImageUrl(item.ParentLogoItemId, {
type: "Logo",
maxWidth: width,
tag: item.ParentLogoImageTag
}) : options.preferThumb && item.SeriesThumbImageTag && !1 !== options.inheritThumb ? imgUrl = apiClient.getScaledImageUrl(item.SeriesId, {
type: "Thumb",
maxWidth: width,
tag: item.SeriesThumbImageTag
}) : options.preferThumb && item.ParentThumbItemId && !1 !== options.inheritThumb && "Photo" !== item.MediaType ? imgUrl = apiClient.getScaledImageUrl(item.ParentThumbItemId, {
type: "Thumb",
maxWidth: width,
tag: item.ParentThumbImageTag
}) : options.preferThumb && item.BackdropImageTags && item.BackdropImageTags.length ? (imgUrl = apiClient.getScaledImageUrl(item.Id, {
type: "Backdrop",
maxWidth: width,
tag: item.BackdropImageTags[0]
}), forceName = !0) : options.preferThumb && item.ParentBackdropImageTags && item.ParentBackdropImageTags.length && !1 !== options.inheritThumb && "Episode" === item.Type ? imgUrl = apiClient.getScaledImageUrl(item.ParentBackdropItemId, {
type: "Backdrop",
maxWidth: width,
tag: item.ParentBackdropImageTags[0]
}) : item.ImageTags && item.ImageTags.Primary ? (height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null, imgUrl = apiClient.getScaledImageUrl(item.Id, {
type: "Primary",
maxHeight: height,
maxWidth: width,
tag: item.ImageTags.Primary
}), options.preferThumb && !1 !== options.showTitle && (forceName = !0), primaryImageAspectRatio && (uiAspect = getDesiredAspect(shape)) && (coverImage = Math.abs(primaryImageAspectRatio - uiAspect) / uiAspect <= .2)) : item.PrimaryImageTag ? (height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null, imgUrl = apiClient.getScaledImageUrl(item.PrimaryImageItemId || item.Id || item.ItemId, {
type: "Primary",
maxHeight: height,
maxWidth: width,
tag: item.PrimaryImageTag
}), options.preferThumb && !1 !== options.showTitle && (forceName = !0), primaryImageAspectRatio && (uiAspect = getDesiredAspect(shape)) && (coverImage = Math.abs(primaryImageAspectRatio - uiAspect) / uiAspect <= .2)) : item.ParentPrimaryImageTag ? imgUrl = apiClient.getScaledImageUrl(item.ParentPrimaryImageItemId, {
type: "Primary",
maxWidth: width,
tag: item.ParentPrimaryImageTag
}) : item.SeriesPrimaryImageTag ? imgUrl = apiClient.getScaledImageUrl(item.SeriesId, {
type: "Primary",
maxWidth: width,
tag: item.SeriesPrimaryImageTag
}) : item.AlbumId && item.AlbumPrimaryImageTag ? (width = primaryImageAspectRatio ? Math.round(height * primaryImageAspectRatio) : null, imgUrl = apiClient.getScaledImageUrl(item.AlbumId, {
type: "Primary",
maxHeight: height,
maxWidth: width,
tag: item.AlbumPrimaryImageTag
}), primaryImageAspectRatio && (uiAspect = getDesiredAspect(shape)) && (coverImage = Math.abs(primaryImageAspectRatio - uiAspect) / uiAspect <= .2)) : "Season" === item.Type && item.ImageTags && item.ImageTags.Thumb ? imgUrl = apiClient.getScaledImageUrl(item.Id, {
type: "Thumb",
maxWidth: width,
tag: item.ImageTags.Thumb
}) : item.BackdropImageTags && item.BackdropImageTags.length ? imgUrl = apiClient.getScaledImageUrl(item.Id, {
type: "Backdrop",
maxWidth: width,
tag: item.BackdropImageTags[0]
}) : item.ImageTags && item.ImageTags.Thumb ? imgUrl = apiClient.getScaledImageUrl(item.Id, {
type: "Thumb",
maxWidth: width,
tag: item.ImageTags.Thumb
}) : item.SeriesThumbImageTag && !1 !== options.inheritThumb ? imgUrl = apiClient.getScaledImageUrl(item.SeriesId, {
type: "Thumb",
maxWidth: width,
tag: item.SeriesThumbImageTag
}) : item.ParentThumbItemId && !1 !== options.inheritThumb ? imgUrl = apiClient.getScaledImageUrl(item.ParentThumbItemId, {
type: "Thumb",
maxWidth: width,
tag: item.ParentThumbImageTag
}) : item.ParentBackdropImageTags && item.ParentBackdropImageTags.length && !1 !== options.inheritThumb && (imgUrl = apiClient.getScaledImageUrl(item.ParentBackdropItemId, {
type: "Backdrop",
maxWidth: width,
tag: item.ParentBackdropImageTags[0]
})), {
imgUrl: imgUrl,
forceName: forceName,
coverImage: coverImage
}
}
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min
}
function getDefaultColorIndex(str) {
if (str) {
for (var charIndex = Math.floor(str.length / 2), character = String(str.substr(charIndex, 1).charCodeAt()), sum = 0, i = 0; i < character.length; i++) sum += parseInt(character.charAt(i));
return String(sum).substr(-1) % numRandomColors + 1
}
return getRandomInt(1, numRandomColors)
}
function getCardTextLines(lines, cssClass, forceLines, isOuterFooter, cardLayout, addRightMargin, maxLines) {
var i, length, html = "",
valid = 0;
for (i = 0, length = lines.length; i < length; i++) {
var currentCssClass = cssClass,
text = lines[i];
if (valid > 0 && isOuterFooter ? currentCssClass += " cardText-secondary" : 0 === valid && isOuterFooter && (currentCssClass += " cardText-first"), addRightMargin && (currentCssClass += " cardText-rightmargin"), text && (html += "<div class='" + currentCssClass + "'>", html += text, html += "</div>", valid++, maxLines && valid >= maxLines)) break
}
if (forceLines)
for (length = maxLines || Math.min(lines.length, maxLines || lines.length); valid < length;) html += "<div class='" + cssClass + "'>&nbsp;</div>", valid++;
return html
}
function isUsingLiveTvNaming(item) {
return "Program" === item.Type || "Timer" === item.Type || "Recording" === item.Type
}
function getAirTimeText(item, showAirDateTime, showAirEndTime) {
var airTimeText = "";
if (item.StartDate) try {
var date = datetime.parseISO8601Date(item.StartDate);
showAirDateTime && (airTimeText += datetime.toLocaleDateString(date, {
weekday: "short",
month: "short",
day: "numeric"
}) + " "), airTimeText += datetime.getDisplayTime(date), item.EndDate && showAirEndTime && (date = datetime.parseISO8601Date(item.EndDate), airTimeText += " - " + datetime.getDisplayTime(date))
} catch (e) {
console.log("Error parsing date: " + item.StartDate)
}
return airTimeText
}
function getCardFooterText(item, apiClient, options, showTitle, forceName, overlayText, imgUrl, footerClass, progressHtml, logoUrl, isOuterFooter) {
var html = "";
logoUrl && (html += '<div class="lazy cardFooterLogo" data-src="' + logoUrl + '"></div>');
var showOtherText = isOuterFooter ? !overlayText : overlayText;
isOuterFooter && options.cardLayout && layoutManager.mobile && "none" !== options.cardFooterAside && (html += '<button is="paper-icon-button-light" class="itemAction btnCardOptions cardText-secondary" data-action="menu"><i class="md-icon">&#xE5D3;</i></button>');
var titleAdded, cssClass = options.centerText ? "cardText cardTextCentered" : "cardText",
lines = [],
parentTitleUnderneath = "MusicAlbum" === item.Type || "Audio" === item.Type || "MusicVideo" === item.Type;
if (showOtherText && (options.showParentTitle || options.showParentTitleOrTitle) && !parentTitleUnderneath)
if (isOuterFooter && "Episode" === item.Type && item.SeriesName) item.SeriesId ? lines.push(getTextActionButton({
Id: item.SeriesId,
ServerId: item.ServerId,
Name: item.SeriesName,
Type: "Series",
IsFolder: !0
})) : lines.push(item.SeriesName);
else if (isUsingLiveTvNaming(item)) lines.push(item.Name), item.EpisodeTitle || (titleAdded = !0);
else {
var parentTitle = item.SeriesName || item.Series || item.Album || item.AlbumArtist || item.GameSystem || "";
(parentTitle || showTitle) && lines.push(parentTitle)
}
var showMediaTitle = showTitle && !titleAdded || options.showParentTitleOrTitle && !lines.length;
if (showMediaTitle || titleAdded || !showTitle && !forceName || (showMediaTitle = !0), showMediaTitle) {
var name = "auto" !== options.showTitle || item.IsFolder || "Photo" !== item.MediaType ? itemHelper.getDisplayName(item, {
includeParentInfo: options.includeParentInfoInTitle
}) : "";
lines.push(name)
}
if (showOtherText) {
if (options.showParentTitle && parentTitleUnderneath && (isOuterFooter && item.AlbumArtists && item.AlbumArtists.length ? (item.AlbumArtists[0].Type = "MusicArtist", item.AlbumArtists[0].IsFolder = !0, lines.push(getTextActionButton(item.AlbumArtists[0], null, item.ServerId))) : lines.push(isUsingLiveTvNaming(item) ? item.Name : item.SeriesName || item.Series || item.Album || item.AlbumArtist || item.GameSystem || "")), options.showItemCounts) {
var itemCountHtml = getItemCountsHtml(options, item);
lines.push(itemCountHtml)
}
if (options.textLines)
for (var additionalLines = options.textLines(item), i = 0, length = additionalLines.length; i < length; i++) lines.push(additionalLines[i]);
if (options.showSongCount) {
var songLine = "";
item.SongCount && (songLine = 1 === item.SongCount ? globalize.translate("sharedcomponents#ValueOneSong") : globalize.translate("sharedcomponents#ValueSongCount", item.SongCount)), lines.push(songLine)
}
if (options.showPremiereDate)
if (item.PremiereDate) try {
lines.push(getPremiereDateText(item))
} catch (err) {
lines.push("")
} else lines.push("");
(options.showYear || options.showSeriesYear) && ("Series" === item.Type ? "Continuing" === item.Status ? lines.push(globalize.translate("sharedcomponents#SeriesYearToPresent", item.ProductionYear || "")) : item.EndDate && item.ProductionYear ? lines.push(item.ProductionYear + " - " + datetime.parseISO8601Date(item.EndDate).getFullYear()) : lines.push(item.ProductionYear || "") : lines.push(item.ProductionYear || "")), options.showRuntime && (item.RunTimeTicks ? lines.push(datetime.getDisplayRunningTime(item.RunTimeTicks)) : lines.push("")), options.showAirTime && lines.push(getAirTimeText(item, options.showAirDateTime, options.showAirEndTime) || ""), options.showChannelName && (item.ChannelId ? lines.push(getTextActionButton({
Id: item.ChannelId,
ServerId: item.ServerId,
Name: item.ChannelName,
Type: "TvChannel",
MediaType: item.MediaType,
IsFolder: !1
}, item.ChannelName)) : lines.push(item.ChannelName || "&nbsp;")), options.showCurrentProgram && "TvChannel" === item.Type && (item.CurrentProgram ? lines.push(item.CurrentProgram.Name) : lines.push("")), options.showCurrentProgramTime && "TvChannel" === item.Type && (item.CurrentProgram ? lines.push(getAirTimeText(item.CurrentProgram, !1, !0) || "") : lines.push("")), options.showSeriesTimerTime && (item.RecordAnyTime ? lines.push(globalize.translate("sharedcomponents#Anytime")) : lines.push(datetime.getDisplayTime(item.StartDate))), options.showSeriesTimerChannel && (item.RecordAnyChannel ? lines.push(globalize.translate("sharedcomponents#AllChannels")) : lines.push(item.ChannelName || globalize.translate("sharedcomponents#OneChannel"))), options.showPersonRoleOrType && (item.Role ? lines.push("as " + item.Role) : item.Type ? lines.push(globalize.translate("sharedcomponents#" + item.Type)) : lines.push(""))
}(showTitle || !imgUrl) && forceName && overlayText && 1 === lines.length && (lines = []);
var addRightTextMargin = isOuterFooter && options.cardLayout && !options.centerText && "none" !== options.cardFooterAside && layoutManager.mobile;
return html += getCardTextLines(lines, cssClass, !options.overlayText, isOuterFooter, options.cardLayout, addRightTextMargin, options.lines), progressHtml && (html += progressHtml), html && (!isOuterFooter || logoUrl || options.cardLayout) && (html = '<div class="' + footerClass + '">' + html, html += "</div>"), html
}
function getTextActionButton(item, text, serverId) {
if (text || (text = itemHelper.getDisplayName(item)), layoutManager.tv) return text;
var html = "<button " + itemShortcuts.getShortcutAttributesHtml(item, serverId) + ' type="button" class="itemAction textActionButton" data-action="link">';
return html += text, html += "</button>"
}
function getItemCountsHtml(options, item) {
var childText, counts = [];
if ("Playlist" === item.Type) {
if (childText = "", item.RunTimeTicks) {
var minutes = item.RunTimeTicks / 6e8;
minutes = minutes || 1, childText += globalize.translate("sharedcomponents#ValueMinutes", Math.round(minutes))
} else childText += globalize.translate("sharedcomponents#ValueMinutes", 0);
counts.push(childText)
} else "Genre" === item.Type || "Studio" === item.Type ? (item.MovieCount && (childText = 1 === item.MovieCount ? globalize.translate("sharedcomponents#ValueOneMovie") : globalize.translate("sharedcomponents#ValueMovieCount", item.MovieCount), counts.push(childText)), item.SeriesCount && (childText = 1 === item.SeriesCount ? globalize.translate("sharedcomponents#ValueOneSeries") : globalize.translate("sharedcomponents#ValueSeriesCount", item.SeriesCount), counts.push(childText)), item.EpisodeCount && (childText = 1 === item.EpisodeCount ? globalize.translate("sharedcomponents#ValueOneEpisode") : globalize.translate("sharedcomponents#ValueEpisodeCount", item.EpisodeCount), counts.push(childText)), item.GameCount && (childText = 1 === item.GameCount ? globalize.translate("sharedcomponents#ValueOneGame") : globalize.translate("sharedcomponents#ValueGameCount", item.GameCount), counts.push(childText))) : "GameGenre" === item.Type ? item.GameCount && (childText = 1 === item.GameCount ? globalize.translate("sharedcomponents#ValueOneGame") : globalize.translate("sharedcomponents#ValueGameCount", item.GameCount), counts.push(childText)) : "MusicGenre" === item.Type || "MusicArtist" === options.context ? (item.AlbumCount && (childText = 1 === item.AlbumCount ? globalize.translate("sharedcomponents#ValueOneAlbum") : globalize.translate("sharedcomponents#ValueAlbumCount", item.AlbumCount), counts.push(childText)), item.SongCount && (childText = 1 === item.SongCount ? globalize.translate("sharedcomponents#ValueOneSong") : globalize.translate("sharedcomponents#ValueSongCount", item.SongCount), counts.push(childText)), item.MusicVideoCount && (childText = 1 === item.MusicVideoCount ? globalize.translate("sharedcomponents#ValueOneMusicVideo") : globalize.translate("sharedcomponents#ValueMusicVideoCount", item.MusicVideoCount), counts.push(childText))) : "Series" === item.Type && (childText = 1 === item.RecursiveItemCount ? globalize.translate("sharedcomponents#ValueOneEpisode") : globalize.translate("sharedcomponents#ValueEpisodeCount", item.RecursiveItemCount), counts.push(childText));
return counts.join(", ")
}
function requireRefreshIndicator() {
refreshIndicatorLoaded || (refreshIndicatorLoaded = !0, require(["emby-itemrefreshindicator"]))
}
function getDefaultBackgroundClass(str) {
return "defaultCardBackground defaultCardBackground" + getDefaultColorIndex(str)
}
function buildCard(index, item, apiClient, options) {
var action = options.action || "link";
"play" === action && item.IsFolder ? action = "link" : "Photo" === item.MediaType && (action = "play");
var shape = options.shape;
if ("mixed" === shape) {
shape = null;
var primaryImageAspectRatio = item.PrimaryImageAspectRatio;
primaryImageAspectRatio && (shape = primaryImageAspectRatio >= 1.33 ? "mixedBackdrop" : primaryImageAspectRatio > .71 ? "mixedSquare" : "mixedPortrait"), shape = shape || "mixedSquare"
}
var className = "card";
shape && (className += " " + shape + "Card"), options.cardCssClass && (className += " " + options.cardCssClass), options.cardClass && (className += " " + options.cardClass), layoutManager.desktop && (className += " card-hoverable"), enableFocusTransfrom && layoutManager.tv || (className += " card-nofocustransform");
var imgInfo = getCardImageUrl(item, apiClient, options, shape),
imgUrl = imgInfo.imgUrl,
forceName = imgInfo.forceName,
showTitle = "auto" === options.showTitle || (options.showTitle || "PhotoAlbum" === item.Type || "Folder" === item.Type),
overlayText = options.overlayText;
forceName && !options.cardLayout && null == overlayText && (overlayText = !0);
var cardImageContainerClass = "cardImageContainer";
(options.coverImage || imgInfo.coverImage) && (cardImageContainerClass += " coveredImage", ("Photo" === item.MediaType || "PhotoAlbum" === item.Type || "Folder" === item.Type || item.ProgramInfo || "Program" === item.Type || "Recording" === item.Type) && (cardImageContainerClass += " coveredImage-noScale")), imgUrl || (cardImageContainerClass += " " + getDefaultBackgroundClass(item.Name));
var cardBoxClass = options.cardLayout ? "cardBox visualCardBox" : "cardBox";
layoutManager.tv && (cardBoxClass += enableFocusTransfrom ? " cardBox-focustransform cardBox-withfocuscontent" : " cardBox-withfocuscontent-large", options.cardLayout && (cardBoxClass += " card-focuscontent", enableFocusTransfrom || (cardBoxClass += " card-focuscontent-large")));
var footerCssClass, logoUrl, progressHtml = indicators.getProgressBarHtml(item),
innerCardFooter = "",
footerOverlayed = !1;
options.showChannelLogo && item.ChannelPrimaryImageTag ? logoUrl = apiClient.getScaledImageUrl(item.ChannelId, {
type: "Primary",
height: 40,
tag: item.ChannelPrimaryImageTag
}) : options.showLogo && item.ParentLogoImageTag && (logoUrl = apiClient.getScaledImageUrl(item.ParentLogoItemId, {
type: "Logo",
height: 40,
tag: item.ParentLogoImageTag
})), overlayText ? (logoUrl = null, footerCssClass = progressHtml ? "innerCardFooter fullInnerCardFooter" : "innerCardFooter", innerCardFooter += getCardFooterText(item, apiClient, options, showTitle, forceName, overlayText, imgUrl, footerCssClass, progressHtml, logoUrl, !1), footerOverlayed = !0) : progressHtml && (innerCardFooter += '<div class="innerCardFooter fullInnerCardFooter innerCardFooterClear">', innerCardFooter += progressHtml, innerCardFooter += "</div>", progressHtml = "");
var mediaSourceCount = item.MediaSourceCount || 1;
mediaSourceCount > 1 && (innerCardFooter += '<div class="mediaSourceIndicator">' + mediaSourceCount + "</div>");
var outerCardFooter = "";
overlayText || footerOverlayed || (footerCssClass = options.cardLayout ? "cardFooter" : "cardFooter cardFooter-transparent", logoUrl && (footerCssClass += " cardFooter-withlogo"), options.cardLayout || (logoUrl = null), outerCardFooter = getCardFooterText(item, apiClient, options, showTitle, forceName, overlayText, imgUrl, footerCssClass, progressHtml, logoUrl, !0)), outerCardFooter && !options.cardLayout && (cardBoxClass += " cardBox-bottompadded");
var overlayButtons = "";
if (layoutManager.mobile) {
var overlayPlayButton = options.overlayPlayButton;
null != overlayPlayButton || options.overlayMoreButton || options.overlayInfoButton || options.cardLayout || (overlayPlayButton = "Video" === item.MediaType);
var btnCssClass = "cardOverlayButton cardOverlayButton-br itemAction";
options.centerPlayButton && (overlayButtons += '<button is="paper-icon-button-light" class="' + btnCssClass + ' cardOverlayButton-centered" data-action="play"><i class="md-icon cardOverlayButtonIcon">&#xE037;</i></button>'), !overlayPlayButton || item.IsPlaceHolder || "Virtual" === item.LocationType && item.MediaType && "Program" !== item.Type || "Person" === item.Type || (overlayButtons += '<button is="paper-icon-button-light" class="' + btnCssClass + '" data-action="play"><i class="md-icon cardOverlayButtonIcon">&#xE037;</i></button>'), options.overlayMoreButton && (overlayButtons += '<button is="paper-icon-button-light" class="' + btnCssClass + '" data-action="menu"><i class="md-icon cardOverlayButtonIcon">&#xE5D3;</i></button>')
}
options.showChildCountIndicator && item.ChildCount && (className += " groupedCard");
var cardImageContainerOpen, cardImageContainerClose = "",
cardBoxClose = "",
cardScalableClose = "",
cardContentClass = "cardContent";
options.cardLayout || (cardContentClass += " cardContent-shadow"), layoutManager.tv ? (cardImageContainerOpen = imgUrl ? '<div class="' + cardImageContainerClass + " " + cardContentClass + ' lazy" data-src="' + imgUrl + '">' : '<div class="' + cardImageContainerClass + " " + cardContentClass + '">', cardImageContainerClose = "</div>") : (cardImageContainerOpen = imgUrl ? '<button data-action="' + action + '" class="cardContent-button ' + cardImageContainerClass + " " + cardContentClass + ' itemAction lazy" data-src="' + imgUrl + '">' : '<button data-action="' + action + '" class="cardContent-button ' + cardImageContainerClass + " " + cardContentClass + ' itemAction">', cardImageContainerClose = "</button>");
var cardScalableClass = "cardScalable";
layoutManager.tv && !options.cardLayout && (cardScalableClass += " card-focuscontent", enableFocusTransfrom || (cardScalableClass += " card-focuscontent-large")), cardImageContainerOpen = '<div class="' + cardBoxClass + '"><div class="' + cardScalableClass + '"><div class="cardPadder-' + shape + '"></div>' + cardImageContainerOpen, cardBoxClose = "</div>", cardScalableClose = "</div>";
var indicatorsHtml = "";
if (!1 !== options.missingIndicator && (indicatorsHtml += indicators.getMissingIndicator(item)), indicatorsHtml += indicators.getSyncIndicator(item), indicatorsHtml += indicators.getTimerIndicator(item), indicatorsHtml += indicators.getTypeIndicator(item), options.showGroupCount ? indicatorsHtml += indicators.getChildCountIndicatorHtml(item, {
minCount: 1
}) : indicatorsHtml += indicators.getPlayedIndicatorHtml(item), "CollectionFolder" === item.Type || item.CollectionType) {
indicatorsHtml += '<div is="emby-itemrefreshindicator"' + (item.RefreshProgress || item.RefreshStatus && "Idle" !== virtualFolder.item ? "" : ' class="hide"') + ' data-progress="' + (item.RefreshProgress || 0) + '" data-status="' + item.RefreshStatus + '"></div>', requireRefreshIndicator()
}
indicatorsHtml && (cardImageContainerOpen += '<div class="cardIndicators">' + indicatorsHtml + "</div>"), imgUrl || (cardImageContainerOpen += getCardDefaultText(item, options));
var tagName = layoutManager.tv && !overlayButtons ? "button" : "div",
nameWithPrefix = item.SortName || item.Name || "",
prefix = nameWithPrefix.substring(0, Math.min(3, nameWithPrefix.length));
prefix && (prefix = prefix.toUpperCase());
var timerAttributes = "";
item.TimerId && (timerAttributes += ' data-timerid="' + item.TimerId + '"'), item.SeriesTimerId && (timerAttributes += ' data-seriestimerid="' + item.SeriesTimerId + '"');
var actionAttribute;
"button" === tagName ? (className += " itemAction", actionAttribute = ' data-action="' + action + '"') : actionAttribute = "", "MusicAlbum" !== item.Type && "MusicArtist" !== item.Type && "Audio" !== item.Type && (className += " card-withuserdata");
var positionTicksData = item.UserData && item.UserData.PlaybackPositionTicks ? ' data-positionticks="' + item.UserData.PlaybackPositionTicks + '"' : "",
collectionIdData = options.collectionId ? ' data-collectionid="' + options.collectionId + '"' : "",
playlistIdData = options.playlistId ? ' data-playlistid="' + options.playlistId + '"' : "",
mediaTypeData = item.MediaType ? ' data-mediatype="' + item.MediaType + '"' : "",
collectionTypeData = item.CollectionType ? ' data-collectiontype="' + item.CollectionType + '"' : "",
channelIdData = item.ChannelId ? ' data-channelid="' + item.ChannelId + '"' : "",
contextData = options.context ? ' data-context="' + options.context + '"' : "",
parentIdData = options.parentId ? ' data-parentid="' + options.parentId + '"' : "",
additionalCardContent = "";
return layoutManager.desktop && (additionalCardContent += getHoverMenuHtml(item, action)), "<" + tagName + ' data-index="' + index + '"' + timerAttributes + actionAttribute + ' data-isfolder="' + (item.IsFolder || !1) + '" data-serverid="' + (item.ServerId || options.serverId) + '" data-id="' + (item.Id || item.ItemId) + '" data-type="' + item.Type + '"' + mediaTypeData + collectionTypeData + channelIdData + positionTicksData + collectionIdData + playlistIdData + contextData + parentIdData + ' data-prefix="' + prefix + '" class="' + className + '">' + cardImageContainerOpen + innerCardFooter + cardImageContainerClose + overlayButtons + additionalCardContent + cardScalableClose + outerCardFooter + cardBoxClose + "</" + tagName + ">"
}
function getHoverMenuHtml(item, action) {
var html = "";
html += '<div class="cardOverlayContainer itemAction" data-action="' + action + '">';
var btnCssClass = "cardOverlayButton cardOverlayButton-hover itemAction";
playbackManager.canPlay(item) && (html += '<button is="paper-icon-button-light" class="' + btnCssClass + ' cardOverlayFab-primary" data-action="resume"><i class="md-icon cardOverlayButtonIcon">&#xE037;</i></button>'), html += '<div class="cardOverlayButton-br">';
var userData = item.UserData || {};
if (itemHelper.canMarkPlayed(item) && (require(["emby-playstatebutton"]), html += '<button is="emby-playstatebutton" type="button" data-action="none" class="' + btnCssClass + '" data-id="' + item.Id + '" data-serverid="' + item.ServerId + '" data-itemtype="' + item.Type + '" data-played="' + userData.Played + '"><i class="md-icon cardOverlayButtonIcon cardOverlayButtonIcon-hover">&#xE5CA;</i></button>'), itemHelper.canRate(item)) {
var likes = null == userData.Likes ? "" : userData.Likes;
require(["emby-ratingbutton"]), html += '<button is="emby-ratingbutton" type="button" data-action="none" class="' + btnCssClass + '" data-id="' + item.Id + '" data-serverid="' + item.ServerId + '" data-itemtype="' + item.Type + '" data-likes="' + likes + '" data-isfavorite="' + userData.IsFavorite + '"><i class="md-icon cardOverlayButtonIcon cardOverlayButtonIcon-hover">&#xE87D;</i></button>'
}
return html += '<button is="paper-icon-button-light" class="' + btnCssClass + '" data-action="menu"><i class="md-icon cardOverlayButtonIcon cardOverlayButtonIcon-hover">&#xE5D3;</i></button>', html += "</div>", html += "</div>"
}
function getCardDefaultText(item, options) {
var collectionType = item.CollectionType;
return "livetv" === collectionType ? '<i class="cardImageIcon md-icon">&#xE1B2;</i>' : "homevideos" === collectionType || "photos" === collectionType ? '<i class="cardImageIcon md-icon">&#xE412;</i>' : "music" === collectionType ? '<i class="cardImageIcon md-icon">&#xE310;</i>' : "MusicAlbum" === item.Type ? '<i class="cardImageIcon md-icon">&#xE019;</i>' : "MusicArtist" === item.Type || "Person" === item.Type ? '<i class="cardImageIcon md-icon">&#xE7FD;</i>' : options.defaultCardImageIcon ? '<i class="cardImageIcon md-icon">' + options.defaultCardImageIcon + "</i>" : '<div class="cardText cardDefaultText">' + (isUsingLiveTvNaming(item) ? item.Name : itemHelper.getDisplayName(item)) + "</div>"
}
function buildCards(items, options) {
if (document.body.contains(options.itemsContainer)) {
if (options.parentContainer) {
if (!items.length) return void options.parentContainer.classList.add("hide");
options.parentContainer.classList.remove("hide")
}
var html = buildCardsHtmlInternal(items, options);
html ? (options.itemsContainer.cardBuilderHtml !== html && (options.itemsContainer.innerHTML = html, items.length < 50 ? options.itemsContainer.cardBuilderHtml = html : options.itemsContainer.cardBuilderHtml = null), imageLoader.lazyChildren(options.itemsContainer)) : (options.itemsContainer.innerHTML = html, options.itemsContainer.cardBuilderHtml = null), options.autoFocus && focusManager.autoFocus(options.itemsContainer, !0)
}
}
function ensureIndicators(card, indicatorsElem) {
if (indicatorsElem) return indicatorsElem;
if (!(indicatorsElem = card.querySelector(".cardIndicators"))) {
var cardImageContainer = card.querySelector(".cardImageContainer");
indicatorsElem = document.createElement("div"),
indicatorsElem.classList.add("cardIndicators"), cardImageContainer.appendChild(indicatorsElem)
}
return indicatorsElem
}
function updateUserData(card, userData) {
var type = card.getAttribute("data-type"),
enableCountIndicator = "Series" === type || "BoxSet" === type || "Season" === type,
indicatorsElem = null,
playedIndicator = null,
countIndicator = null,
itemProgressBar = null;
userData.Played ? (playedIndicator = card.querySelector(".playedIndicator"), playedIndicator || (playedIndicator = document.createElement("div"), playedIndicator.classList.add("playedIndicator"), playedIndicator.classList.add("indicator"), indicatorsElem = ensureIndicators(card, indicatorsElem), indicatorsElem.appendChild(playedIndicator)), playedIndicator.innerHTML = '<i class="md-icon indicatorIcon">&#xE5CA;</i>') : (playedIndicator = card.querySelector(".playedIndicator")) && playedIndicator.parentNode.removeChild(playedIndicator), userData.UnplayedItemCount ? (countIndicator = card.querySelector(".countIndicator"), countIndicator || (countIndicator = document.createElement("div"), countIndicator.classList.add("countIndicator"), indicatorsElem = ensureIndicators(card, indicatorsElem), indicatorsElem.appendChild(countIndicator)), countIndicator.innerHTML = userData.UnplayedItemCount) : enableCountIndicator && (countIndicator = card.querySelector(".countIndicator")) && countIndicator.parentNode.removeChild(countIndicator);
var progressHtml = indicators.getProgressBarHtml({
Type: type,
UserData: userData,
MediaType: "Video"
});
if (progressHtml) {
if (!(itemProgressBar = card.querySelector(".itemProgressBar"))) {
itemProgressBar = document.createElement("div"), itemProgressBar.classList.add("itemProgressBar");
var innerCardFooter = card.querySelector(".innerCardFooter");
if (!innerCardFooter) {
innerCardFooter = document.createElement("div"), innerCardFooter.classList.add("innerCardFooter");
card.querySelector(".cardImageContainer").appendChild(innerCardFooter)
}
innerCardFooter.appendChild(itemProgressBar)
}
itemProgressBar.innerHTML = progressHtml
} else(itemProgressBar = card.querySelector(".itemProgressBar")) && itemProgressBar.parentNode.removeChild(itemProgressBar)
}
function onUserDataChanged(userData, scope) {
for (var cards = (scope || document.body).querySelectorAll('.card-withuserdata[data-id="' + userData.ItemId + '"]'), i = 0, length = cards.length; i < length; i++) updateUserData(cards[i], userData)
}
function onTimerCreated(programId, newTimerId, itemsContainer) {
for (var cells = itemsContainer.querySelectorAll('.card[data-id="' + programId + '"]'), i = 0, length = cells.length; i < length; i++) {
var cell = cells[i];
if (!cell.querySelector(".timerIndicator")) {
ensureIndicators(cell).insertAdjacentHTML("beforeend", '<i class="md-icon timerIndicator indicatorIcon">&#xE061;</i>')
}
cell.setAttribute("data-timerid", newTimerId)
}
}
function onTimerCancelled(id, itemsContainer) {
for (var cells = itemsContainer.querySelectorAll('.card[data-timerid="' + id + '"]'), i = 0, length = cells.length; i < length; i++) {
var cell = cells[i],
icon = cell.querySelector(".timerIndicator");
icon && icon.parentNode.removeChild(icon), cell.removeAttribute("data-timerid")
}
}
function onSeriesTimerCancelled(id, itemsContainer) {
for (var cells = itemsContainer.querySelectorAll('.card[data-seriestimerid="' + id + '"]'), i = 0, length = cells.length; i < length; i++) {
var cell = cells[i],
icon = cell.querySelector(".timerIndicator");
icon && icon.parentNode.removeChild(icon), cell.removeAttribute("data-seriestimerid")
}
}
var refreshIndicatorLoaded, enableFocusTransfrom = (window.devicePixelRatio, !browser.slow && !browser.edge),
numRandomColors = 5;
return {
getCardsHtml: getCardsHtml,
buildCards: buildCards,
onUserDataChanged: onUserDataChanged,
onTimerCreated: onTimerCreated,
onTimerCancelled: onTimerCancelled,
onSeriesTimerCancelled: onSeriesTimerCancelled
}
});

View file

@ -0,0 +1,59 @@
define(["datetime", "imageLoader", "connectionManager", "layoutManager", "browser"], function(datetime, imageLoader, connectionManager, layoutManager, browser) {
"use strict";
function buildChapterCardsHtml(item, chapters, options) {
var className = "card itemAction chapterCard";
layoutManager.tv && (browser.animate || browser.edge) && (className += " card-focusscale");
var mediaStreams = ((item.MediaSources || [])[0] || {}).MediaStreams || [],
videoStream = mediaStreams.filter(function(i) {
return "Video" === i.Type
})[0] || {},
shape = options.backdropShape || "backdrop";
videoStream.Width && videoStream.Height && videoStream.Width / videoStream.Height <= 1.2 && (shape = options.squareShape || "square"), className += " " + shape + "Card", (options.block || options.rows) && (className += " block");
for (var html = "", itemsInRow = 0, apiClient = connectionManager.getApiClient(item.ServerId), i = 0, length = chapters.length; i < length; i++) {
options.rows && 0 === itemsInRow && (html += '<div class="cardColumn">');
html += buildChapterCard(item, apiClient, chapters[i], i, options, className, shape), itemsInRow++, options.rows && itemsInRow >= options.rows && (itemsInRow = 0, html += "</div>")
}
return html
}
function getImgUrl(item, chapter, index, maxWidth, apiClient) {
return chapter.ImageTag ? apiClient.getScaledImageUrl(item.Id, {
maxWidth: maxWidth,
tag: chapter.ImageTag,
type: "Chapter",
index: index
}) : null
}
function buildChapterCard(item, apiClient, chapter, index, options, className, shape) {
var imgUrl = getImgUrl(item, chapter, index, options.width || 400, apiClient),
cardImageContainerClass = "cardContent cardContent-shadow cardImageContainer chapterCardImageContainer";
options.coverImage && (cardImageContainerClass += " coveredImage");
var dataAttributes = ' data-action="play" data-isfolder="' + item.IsFolder + '" data-id="' + item.Id + '" data-serverid="' + item.ServerId + '" data-type="' + item.Type + '" data-mediatype="' + item.MediaType + '" data-positionticks="' + chapter.StartPositionTicks + '"',
cardImageContainer = imgUrl ? '<div class="' + cardImageContainerClass + ' lazy" data-src="' + imgUrl + '">' : '<div class="' + cardImageContainerClass + '">';
imgUrl || (cardImageContainer += '<i class="md-icon cardImageIcon">local_movies</i>');
var nameHtml = "";
nameHtml += '<div class="cardText">' + chapter.Name + "</div>", nameHtml += '<div class="cardText">' + datetime.getDisplayRunningTime(chapter.StartPositionTicks) + "</div>";
var cardBoxCssClass = "cardBox",
cardScalableClass = "cardScalable";
if (layoutManager.tv) {
var enableFocusTransfrom = !browser.slow && !browser.edge;
cardScalableClass += " card-focuscontent", enableFocusTransfrom ? cardBoxCssClass += " cardBox-focustransform cardBox-withfocuscontent" : (cardBoxCssClass += " cardBox-withfocuscontent-large", cardScalableClass += " card-focuscontent-large")
}
return '<button type="button" class="' + className + '"' + dataAttributes + '><div class="' + cardBoxCssClass + '"><div class="' + cardScalableClass + '"><div class="cardPadder-' + shape + '"></div>' + cardImageContainer + '</div><div class="innerCardFooter">' + nameHtml + "</div></div></div></button>"
}
function buildChapterCards(item, chapters, options) {
if (options.parentContainer) {
if (!document.body.contains(options.parentContainer)) return;
if (!chapters.length) return void options.parentContainer.classList.add("hide");
options.parentContainer.classList.remove("hide")
}
var html = buildChapterCardsHtml(item, chapters, options);
options.itemsContainer.innerHTML = html, imageLoader.lazyChildren(options.itemsContainer)
}
return {
buildChapterCards: buildChapterCards
}
});

View file

@ -0,0 +1,18 @@
define(["cardBuilder"], function(cardBuilder) {
"use strict";
function buildPeopleCards(items, options) {
options = Object.assign(options || {}, {
cardLayout: !1,
centerText: !0,
showTitle: !0,
cardFooterAside: "none",
showPersonRoleOrType: !0,
cardCssClass: "personCard",
defaultCardImageIcon: "&#xE7FD;"
}), cardBuilder.buildCards(items, options)
}
return {
buildPeopleCards: buildPeopleCards
}
});

View file

@ -0,0 +1,10 @@
.card-round:focus>.cardBox-focustransform {
-webkit-transform: scale(1.26, 1.26);
transform: scale(1.26, 1.26)
}
.cardImage-round,
.cardImageContainer-round {
-webkit-border-radius: 1000px;
border-radius: 1000px
}

View file

@ -0,0 +1,67 @@
define(["events"], function(events) {
"use strict";
function isValidIpAddress(address) {
return 1 == LinkParser.parse(address).length
}
function isLocalIpAddress(address) {
return address = address.toLowerCase(), -1 !== address.indexOf("127.0.0.1") || -1 !== address.indexOf("localhost")
}
function getServerAddress(apiClient) {
var serverAddress = apiClient.serverAddress();
if (isValidIpAddress(serverAddress) && !isLocalIpAddress(serverAddress)) return Promise.resolve(serverAddress);
var cachedValue = getCachedValue(serverAddress);
return cachedValue ? Promise.resolve(cachedValue) : apiClient.getEndpointInfo().then(function(endpoint) {
return endpoint.IsInNetwork ? apiClient.getPublicSystemInfo().then(function(info) {
return addToCache(serverAddress, info.LocalAddress), info.LocalAddress
}) : (addToCache(serverAddress, serverAddress), serverAddress)
})
}
function clearCache() {
cache = {}
}
function addToCache(key, value) {
cache[key] = {
value: value,
time: (new Date).getTime()
}
}
function getCachedValue(key) {
var obj = cache[key];
return obj && (new Date).getTime() - obj.time < 18e4 ? obj.value : null
}! function() {
function ensureProtocol(url) {
return url.match(protocolRegExp) || (url = "http://" + url), url
}
var protocols = "(?:(?:http|https|rtsp|ftp):\\/\\/)",
linkRegExp = RegExp("(?:(?:(?:http|https|rtsp|ftp):\\/\\/)?(?:(?:[a-z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-f0-9]{2})){1,64}(?:\\:(?:[a-z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-f0-9]{2})){1,25})?\\@)?(?:((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]d|1dd|[1-9]?d)(.(25[0-5]|2[0-4]d|1dd|[1-9]?d)){3}))|:)))(%.+)?|(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[0-9])))(?:\\:\\d{1,5})?(?:\\/(?:(?:[a-z0-9\\/\\@\\&\\#\\~\\*\\_\\-\\+])|(?:\\%[a-f0-9]{2})|(?:[\\;\\?\\:\\.\\!\\'\\(\\)\\,\\=]+(?=(?:[a-z0-9\\/\\@\\&\\#\\~\\*\\_\\-\\+])|(?:\\%[a-f0-9]{2}))))*|\\b|$)", "gi"),
protocolRegExp = RegExp("^" + protocols, "i"),
LinkParser = {
parse: function(text) {
for (var match, links = []; match = linkRegExp.exec(text);) {
var txt = match[0],
pos = match.index,
len = txt.length,
url = ensureProtocol(text);
links.push({
pos: pos,
text: txt,
len: len,
url: url
})
}
return links
}
};
window.LinkParser = LinkParser
}();
var cache = {};
return events.on(ConnectionManager, "localusersignedin", clearCache), events.on(ConnectionManager, "localusersignedout", clearCache), {
getServerAddress: getServerAddress
}
});

View file

@ -0,0 +1,425 @@
define(["appSettings", "userSettings", "playbackManager", "connectionManager", "globalize", "events", "require", "castSenderApiLoader"], function(appSettings, userSettings, playbackManager, connectionManager, globalize, events, require, castSenderApiLoader) {
"use strict";
function sendConnectionResult(isOk) {
var resolve = currentResolve,
reject = currentReject;
currentResolve = null, currentReject = null, isOk ? resolve && resolve() : reject ? reject() : playbackManager.removeActivePlayer(PlayerName)
}
function alertText(text, title) {
require(["alert"], function(alert) {
alert({
text: text,
title: title
})
})
}
function normalizeImages(state) {
if (state && state.NowPlayingItem) {
var item = state.NowPlayingItem;
item.ImageTags && item.ImageTags.Primary || item.PrimaryImageTag && (item.ImageTags = item.ImageTags || {}, item.ImageTags.Primary = item.PrimaryImageTag), item.BackdropImageTag && item.BackdropItemId === item.Id && (item.BackdropImageTags = [item.BackdropImageTag]), item.BackdropImageTag && item.BackdropItemId !== item.Id && (item.ParentBackdropImageTags = [item.BackdropImageTag], item.ParentBackdropItemId = item.BackdropItemId)
}
}
function getItemsForPlayback(apiClient, query) {
var userId = apiClient.getCurrentUserId();
return query.Ids && 1 === query.Ids.split(",").length ? apiClient.getItem(userId, query.Ids.split(",")).then(function(item) {
return {
Items: [item],
TotalRecordCount: 1
}
}) : (query.Limit = query.Limit || 100, query.ExcludeLocationTypes = "Virtual", query.EnableTotalRecordCount = !1, apiClient.getItems(userId, query))
}
function bindEventForRelay(instance, eventName) {
events.on(instance._castPlayer, eventName, function(e, data) {
var state = instance.getPlayerStateInternal(data);
events.trigger(instance, eventName, [state])
})
}
function initializeChromecast() {
var instance = this;
instance._castPlayer = new CastPlayer, document.dispatchEvent(new CustomEvent("chromecastloaded", {
detail: {
player: instance
}
})), events.on(instance._castPlayer, "connect", function(e) {
currentResolve ? sendConnectionResult(!0) : playbackManager.setActivePlayer(PlayerName, instance.getCurrentTargetInfo()), console.log("cc: connect"), instance.lastPlayerData = null
}), events.on(instance._castPlayer, "playbackstart", function(e, data) {
console.log("cc: playbackstart"), instance._castPlayer.initializeCastPlayer();
var state = instance.getPlayerStateInternal(data);
events.trigger(instance, "playbackstart", [state])
}), events.on(instance._castPlayer, "playbackstop", function(e, data) {
console.log("cc: playbackstop");
var state = instance.getPlayerStateInternal(data);
events.trigger(instance, "playbackstop", [state]), instance.lastPlayerData = {}
}), events.on(instance._castPlayer, "playbackprogress", function(e, data) {
var state = instance.getPlayerStateInternal(data);
events.trigger(instance, "timeupdate", [state])
}), bindEventForRelay(instance, "timeupdate"), bindEventForRelay(instance, "pause"), bindEventForRelay(instance, "unpause"), bindEventForRelay(instance, "volumechange"), bindEventForRelay(instance, "repeatmodechange"), events.on(instance._castPlayer, "playstatechange", function(e, data) {
var state = instance.getPlayerStateInternal(data);
events.trigger(instance, "pause", [state])
})
}
function ChromecastPlayer() {
this.name = PlayerName, this.type = "mediaplayer", this.id = "chromecast", this.isLocalPlayer = !1, this.lastPlayerData = {}, castSenderApiLoader.load().then(initializeChromecast.bind(this))
}
var currentResolve, currentReject, PlayerName = "Chromecast",
DEVICE_STATE = {
IDLE: 0,
ACTIVE: 1,
WARNING: 2,
ERROR: 3
},
PLAYER_STATE = {
IDLE: "IDLE",
LOADING: "LOADING",
LOADED: "LOADED",
PLAYING: "PLAYING",
PAUSED: "PAUSED",
STOPPED: "STOPPED",
SEEKING: "SEEKING",
ERROR: "ERROR"
},
CastPlayer = function() {
this.deviceState = DEVICE_STATE.IDLE, this.currentMediaSession = null, this.session = null, this.castPlayerState = PLAYER_STATE.IDLE, this.hasReceivers = !1, this.errorHandler = this.onError.bind(this), this.mediaStatusUpdateHandler = this.onMediaStatusUpdate.bind(this), this.initializeCastPlayer()
};
return CastPlayer.prototype.initializeCastPlayer = function() {
var chrome = window.chrome;
if (chrome) {
if (!chrome.cast || !chrome.cast.isAvailable) return void setTimeout(this.initializeCastPlayer.bind(this), 1e3);
var sessionRequest = new chrome.cast.SessionRequest("2D4B1DA3"),
apiConfig = new chrome.cast.ApiConfig(sessionRequest, this.sessionListener.bind(this), this.receiverListener.bind(this), "origin_scoped");
console.log("chromecast.initialize"), chrome.cast.initialize(apiConfig, this.onInitSuccess.bind(this), this.errorHandler)
}
}, CastPlayer.prototype.onInitSuccess = function() {
this.isInitialized = !0, console.log("chromecast init success")
}, CastPlayer.prototype.onError = function() {
console.log("chromecast error")
}, CastPlayer.prototype.sessionListener = function(e) {
this.session = e, this.session && (this.session.media[0] && this.onMediaDiscovered("activeSession", this.session.media[0]), this.onSessionConnected(e))
}, CastPlayer.prototype.messageListener = function(namespace, message) {
if ("string" == typeof message && (message = JSON.parse(message)), "playbackerror" === message.type) {
var errorCode = message.data;
setTimeout(function() {
alertText(globalize.translate("MessagePlaybackError" + errorCode), globalize.translate("HeaderPlaybackError"))
}, 300)
} else "connectionerror" === message.type ? setTimeout(function() {
alertText(globalize.translate("MessageChromecastConnectionError"), globalize.translate("HeaderError"))
}, 300) : message.type && events.trigger(this, message.type, [message.data])
}, CastPlayer.prototype.receiverListener = function(e) {
this.hasReceivers = "available" === e
}, CastPlayer.prototype.sessionUpdateListener = function(isAlive) {
isAlive || (this.session = null, this.deviceState = DEVICE_STATE.IDLE, this.castPlayerState = PLAYER_STATE.IDLE, this.currentMediaSession = null, sendConnectionResult(!1))
}, CastPlayer.prototype.launchApp = function() {
chrome.cast.requestSession(this.onRequestSessionSuccess.bind(this), this.onLaunchError.bind(this))
}, CastPlayer.prototype.onRequestSessionSuccess = function(e) {
this.onSessionConnected(e)
}, CastPlayer.prototype.onSessionConnected = function(session) {
this.session = session, this.deviceState = DEVICE_STATE.ACTIVE, this.session.addMessageListener("urn:x-cast:com.connectsdk", this.messageListener.bind(this)), this.session.addMediaListener(this.sessionMediaListener.bind(this)), this.session.addUpdateListener(this.sessionUpdateListener.bind(this)), events.trigger(this, "connect"), this.sendMessage({
options: {},
command: "Identify"
})
}, CastPlayer.prototype.sessionMediaListener = function(e) {
this.currentMediaSession = e, this.currentMediaSession.addUpdateListener(this.mediaStatusUpdateHandler)
}, CastPlayer.prototype.onLaunchError = function() {
this.deviceState = DEVICE_STATE.ERROR, sendConnectionResult(!1)
}, CastPlayer.prototype.stopApp = function() {
this.session && this.session.stop(this.onStopAppSuccess.bind(this, "Session stopped"), this.errorHandler)
}, CastPlayer.prototype.onStopAppSuccess = function(message) {
this.deviceState = DEVICE_STATE.IDLE, this.castPlayerState = PLAYER_STATE.IDLE, this.currentMediaSession = null
}, CastPlayer.prototype.loadMedia = function(options, command) {
return this.session ? (options.items = options.items.map(function(i) {
return {
Id: i.Id,
ServerId: i.ServerId,
Name: i.Name,
Type: i.Type,
MediaType: i.MediaType,
IsFolder: i.IsFolder
}
}), this.sendMessage({
options: options,
command: command
})) : Promise.reject()
}, CastPlayer.prototype.sendMessage = function(message) {
var player = this,
receiverName = null,
session = player.session;
session && session.receiver && session.receiver.friendlyName && (receiverName = session.receiver.friendlyName);
var apiClient;
apiClient = message.options && message.options.ServerId ? connectionManager.getApiClient(message.options.ServerId) : message.options && message.options.items && message.options.items.length ? connectionManager.getApiClient(message.options.items[0].ServerId) : connectionManager.currentApiClient(), message = Object.assign(message, {
userId: apiClient.getCurrentUserId(),
deviceId: apiClient.deviceId(),
accessToken: apiClient.accessToken(),
serverAddress: apiClient.serverAddress(),
serverId: apiClient.serverId(),
serverVersion: apiClient.serverVersion(),
receiverName: receiverName
});
var bitrateSetting = appSettings.maxChromecastBitrate();
return bitrateSetting && (message.maxBitrate = bitrateSetting), message.options && message.options.items && (message.subtitleAppearance = userSettings.getSubtitleAppearanceSettings(), message.subtitleBurnIn = appSettings.get("subtitleburnin") || ""), new Promise(function(resolve, reject) {
require(["chromecastHelper"], function(chromecastHelper) {
chromecastHelper.getServerAddress(apiClient).then(function(serverAddress) {
message.serverAddress = serverAddress, player.sendMessageInternal(message).then(resolve, reject)
}, reject)
})
})
}, CastPlayer.prototype.sendMessageInternal = function(message) {
return message = JSON.stringify(message), this.session.sendMessage("urn:x-cast:com.connectsdk", message, this.onPlayCommandSuccess.bind(this), this.errorHandler), Promise.resolve()
}, CastPlayer.prototype.onPlayCommandSuccess = function() {}, CastPlayer.prototype.onMediaDiscovered = function(how, mediaSession) {
this.currentMediaSession = mediaSession, "loadMedia" === how && (this.castPlayerState = PLAYER_STATE.PLAYING), "activeSession" === how && (this.castPlayerState = mediaSession.playerState), this.currentMediaSession.addUpdateListener(this.mediaStatusUpdateHandler)
}, CastPlayer.prototype.onMediaStatusUpdate = function(e) {
!1 === e && (this.castPlayerState = PLAYER_STATE.IDLE)
}, CastPlayer.prototype.setReceiverVolume = function(mute, vol) {
this.currentMediaSession && (mute ? this.session.setReceiverMuted(!0, this.mediaCommandSuccessCallback.bind(this), this.errorHandler) : this.session.setReceiverVolumeLevel(vol || 1, this.mediaCommandSuccessCallback.bind(this), this.errorHandler))
}, CastPlayer.prototype.mute = function() {
this.setReceiverVolume(!0)
}, CastPlayer.prototype.mediaCommandSuccessCallback = function(info, e) {}, ChromecastPlayer.prototype.tryPair = function(target) {
var castPlayer = this._castPlayer;
return castPlayer.deviceState !== DEVICE_STATE.ACTIVE && castPlayer.isInitialized ? new Promise(function(resolve, reject) {
currentResolve = resolve, currentReject = reject, castPlayer.launchApp()
}) : (currentResolve = null, currentReject = null, Promise.reject())
}, ChromecastPlayer.prototype.getTargets = function() {
var targets = [];
return this._castPlayer && this._castPlayer.hasReceivers && targets.push(this.getCurrentTargetInfo()), Promise.resolve(targets)
}, ChromecastPlayer.prototype.getCurrentTargetInfo = function() {
var appName = null,
castPlayer = this._castPlayer;
return castPlayer.session && castPlayer.session.receiver && castPlayer.session.receiver.friendlyName && (appName = castPlayer.session.receiver.friendlyName), {
name: PlayerName,
id: PlayerName,
playerName: PlayerName,
playableMediaTypes: ["Audio", "Video"],
isLocalPlayer: !1,
appName: PlayerName,
deviceName: appName,
supportedCommands: ["VolumeUp", "VolumeDown", "Mute", "Unmute", "ToggleMute", "SetVolume", "SetAudioStreamIndex", "SetSubtitleStreamIndex", "DisplayContent", "SetRepeatMode", "EndSession", "PlayMediaSource", "PlayTrailers"]
}
}, ChromecastPlayer.prototype.getPlayerStateInternal = function(data) {
var triggerStateChange = !1;
return data && !this.lastPlayerData && (triggerStateChange = !0), data = data || this.lastPlayerData, this.lastPlayerData = data, normalizeImages(data), triggerStateChange && events.trigger(this, "statechange", [data]), data
}, ChromecastPlayer.prototype.playWithCommand = function(options, command) {
if (!options.items) {
var apiClient = connectionManager.getApiClient(options.serverId),
instance = this;
return apiClient.getItem(apiClient.getCurrentUserId(), options.ids[0]).then(function(item) {
return options.items = [item], instance.playWithCommand(options, command)
})
}
return this._castPlayer.loadMedia(options, command)
}, ChromecastPlayer.prototype.seek = function(position) {
position = parseInt(position), position /= 1e7, this._castPlayer.sendMessage({
options: {
position: position
},
command: "Seek"
})
}, ChromecastPlayer.prototype.setAudioStreamIndex = function(index) {
this._castPlayer.sendMessage({
options: {
index: index
},
command: "SetAudioStreamIndex"
})
}, ChromecastPlayer.prototype.setSubtitleStreamIndex = function(index) {
this._castPlayer.sendMessage({
options: {
index: index
},
command: "SetSubtitleStreamIndex"
})
}, ChromecastPlayer.prototype.setMaxStreamingBitrate = function(options) {
this._castPlayer.sendMessage({
options: options,
command: "SetMaxStreamingBitrate"
})
}, ChromecastPlayer.prototype.isFullscreen = function() {
var state = this.lastPlayerData || {};
return state = state.PlayState || {}, state.IsFullscreen
}, ChromecastPlayer.prototype.nextTrack = function() {
this._castPlayer.sendMessage({
options: {},
command: "NextTrack"
})
}, ChromecastPlayer.prototype.previousTrack = function() {
this._castPlayer.sendMessage({
options: {},
command: "PreviousTrack"
})
}, ChromecastPlayer.prototype.volumeDown = function() {
this._castPlayer.sendMessage({
options: {},
command: "VolumeDown"
})
}, ChromecastPlayer.prototype.endSession = function() {
var instance = this;
this.stop().then(function() {
setTimeout(function() {
instance._castPlayer.stopApp()
}, 1e3)
})
}, ChromecastPlayer.prototype.volumeUp = function() {
this._castPlayer.sendMessage({
options: {},
command: "VolumeUp"
})
}, ChromecastPlayer.prototype.setVolume = function(vol) {
vol = Math.min(vol, 100), vol = Math.max(vol, 0), this._castPlayer.sendMessage({
options: {
volume: vol
},
command: "SetVolume"
})
}, ChromecastPlayer.prototype.unpause = function() {
this._castPlayer.sendMessage({
options: {},
command: "Unpause"
})
}, ChromecastPlayer.prototype.playPause = function() {
this._castPlayer.sendMessage({
options: {},
command: "PlayPause"
})
}, ChromecastPlayer.prototype.pause = function() {
this._castPlayer.sendMessage({
options: {},
command: "Pause"
})
}, ChromecastPlayer.prototype.stop = function() {
return this._castPlayer.sendMessage({
options: {},
command: "Stop"
})
}, ChromecastPlayer.prototype.displayContent = function(options) {
this._castPlayer.sendMessage({
options: options,
command: "DisplayContent"
})
}, ChromecastPlayer.prototype.setMute = function(isMuted) {
var castPlayer = this._castPlayer;
isMuted ? castPlayer.sendMessage({
options: {},
command: "Mute"
}) : castPlayer.sendMessage({
options: {},
command: "Unmute"
})
}, ChromecastPlayer.prototype.getRepeatMode = function() {
var state = this.lastPlayerData || {};
return state = state.PlayState || {}, state.RepeatMode
}, ChromecastPlayer.prototype.playTrailers = function(item) {
this._castPlayer.sendMessage({
options: {
ItemId: item.Id,
ServerId: item.ServerId
},
command: "PlayTrailers"
})
}, ChromecastPlayer.prototype.setRepeatMode = function(mode) {
this._castPlayer.sendMessage({
options: {
RepeatMode: mode
},
command: "SetRepeatMode"
})
}, ChromecastPlayer.prototype.toggleMute = function() {
this._castPlayer.sendMessage({
options: {},
command: "ToggleMute"
})
}, ChromecastPlayer.prototype.audioTracks = function() {
var state = this.lastPlayerData || {};
return state = state.NowPlayingItem || {}, (state.MediaStreams || []).filter(function(s) {
return "Audio" === s.Type
})
}, ChromecastPlayer.prototype.getAudioStreamIndex = function() {
var state = this.lastPlayerData || {};
return state = state.PlayState || {}, state.AudioStreamIndex
}, ChromecastPlayer.prototype.subtitleTracks = function() {
var state = this.lastPlayerData || {};
return state = state.NowPlayingItem || {}, (state.MediaStreams || []).filter(function(s) {
return "Subtitle" === s.Type
})
}, ChromecastPlayer.prototype.getSubtitleStreamIndex = function() {
var state = this.lastPlayerData || {};
return state = state.PlayState || {}, state.SubtitleStreamIndex
}, ChromecastPlayer.prototype.getMaxStreamingBitrate = function() {
var state = this.lastPlayerData || {};
return state = state.PlayState || {}, state.MaxStreamingBitrate
}, ChromecastPlayer.prototype.getVolume = function() {
var state = this.lastPlayerData || {};
return state = state.PlayState || {}, null == state.VolumeLevel ? 100 : state.VolumeLevel
}, ChromecastPlayer.prototype.isPlaying = function() {
return null != (this.lastPlayerData || {}).NowPlayingItem
}, ChromecastPlayer.prototype.isPlayingVideo = function() {
var state = this.lastPlayerData || {};
return state = state.NowPlayingItem || {}, "Video" === state.MediaType
}, ChromecastPlayer.prototype.isPlayingAudio = function() {
var state = this.lastPlayerData || {};
return state = state.NowPlayingItem || {}, "Audio" === state.MediaType
}, ChromecastPlayer.prototype.currentTime = function(val) {
if (null != val) return this.seek(val);
var state = this.lastPlayerData || {};
return state = state.PlayState || {}, state.PositionTicks
}, ChromecastPlayer.prototype.duration = function() {
var state = this.lastPlayerData || {};
return state = state.NowPlayingItem || {}, state.RunTimeTicks
}, ChromecastPlayer.prototype.getBufferedRanges = function() {
var state = this.lastPlayerData || {};
return state = state.PlayState || {}, state.BufferedRanges || []
}, ChromecastPlayer.prototype.paused = function() {
var state = this.lastPlayerData || {};
return state = state.PlayState || {}, state.IsPaused
}, ChromecastPlayer.prototype.isMuted = function() {
var state = this.lastPlayerData || {};
return state = state.PlayState || {}, state.IsMuted
}, ChromecastPlayer.prototype.shuffle = function(item) {
var apiClient = connectionManager.getApiClient(item.ServerId),
userId = apiClient.getCurrentUserId(),
instance = this;
apiClient.getItem(userId, item.Id).then(function(item) {
instance.playWithCommand({
items: [item]
}, "Shuffle")
})
}, ChromecastPlayer.prototype.instantMix = function(item) {
var apiClient = connectionManager.getApiClient(item.ServerId),
userId = apiClient.getCurrentUserId(),
instance = this;
apiClient.getItem(userId, item.Id).then(function(item) {
instance.playWithCommand({
items: [item]
}, "InstantMix")
})
}, ChromecastPlayer.prototype.canPlayMediaType = function(mediaType) {
return "audio" === (mediaType = (mediaType || "").toLowerCase()) || "video" === mediaType
}, ChromecastPlayer.prototype.canQueueMediaType = function(mediaType) {
return this.canPlayMediaType(mediaType)
}, ChromecastPlayer.prototype.queue = function(options) {
this.playWithCommand(options, "PlayLast")
}, ChromecastPlayer.prototype.queueNext = function(options) {
this.playWithCommand(options, "PlayNext")
}, ChromecastPlayer.prototype.play = function(options) {
if (options.items) return this.playWithCommand(options, "PlayNow");
if (!options.serverId) throw new Error("serverId required!");
var instance = this;
return getItemsForPlayback(connectionManager.getApiClient(options.serverId), {
Ids: options.ids.join(",")
}).then(function(result) {
return options.items = result.Items, instance.playWithCommand(options, "PlayNow")
})
}, ChromecastPlayer.prototype.toggleFullscreen = function() {}, ChromecastPlayer.prototype.beginPlayerUpdates = function() {}, ChromecastPlayer.prototype.endPlayerUpdates = function() {}, ChromecastPlayer.prototype.getPlaylist = function() {
return Promise.resolve([])
}, ChromecastPlayer.prototype.getCurrentPlaylistItemId = function() {}, ChromecastPlayer.prototype.setCurrentPlaylistItem = function(playlistItemId) {
return Promise.resolve()
}, ChromecastPlayer.prototype.removeFromPlaylist = function(playlistItemIds) {
return Promise.resolve()
}, ChromecastPlayer.prototype.getPlayerState = function() {
return this.getPlayerStateInternal() || {}
}, ChromecastPlayer
});

View file

@ -0,0 +1,12 @@
.clearButton {
background: 0 0;
border: 0 !important;
padding: 0 !important;
cursor: pointer;
outline: 0 !important;
color: inherit;
width: 100%;
vertical-align: middle;
font-family: inherit;
font-size: inherit
}

View file

@ -0,0 +1,119 @@
define(["dialogHelper", "loading", "apphost", "layoutManager", "connectionManager", "appRouter", "globalize", "emby-checkbox", "emby-input", "paper-icon-button-light", "emby-select", "material-icons", "css!./../formdialog", "emby-button", "emby-linkbutton", "flexStyles"], function(dialogHelper, loading, appHost, layoutManager, connectionManager, appRouter, globalize) {
"use strict";
function parentWithClass(elem, className) {
for (; !elem.classList || !elem.classList.contains(className);)
if (!(elem = elem.parentNode)) return null;
return elem
}
function onSubmit(e) {
loading.show();
var panel = parentWithClass(this, "dialog"),
collectionId = panel.querySelector("#selectCollectionToAddTo").value,
apiClient = connectionManager.getApiClient(currentServerId);
return collectionId ? addToCollection(apiClient, panel, collectionId) : createCollection(apiClient, panel), e.preventDefault(), !1
}
function createCollection(apiClient, dlg) {
var url = apiClient.getUrl("Collections", {
Name: dlg.querySelector("#txtNewCollectionName").value,
IsLocked: !dlg.querySelector("#chkEnableInternetMetadata").checked,
Ids: dlg.querySelector(".fldSelectedItemIds").value || ""
});
apiClient.ajax({
type: "POST",
url: url,
dataType: "json"
}).then(function(result) {
loading.hide();
var id = result.Id;
dlg.submitted = !0, dialogHelper.close(dlg), redirectToCollection(apiClient, id)
})
}
function redirectToCollection(apiClient, id) {
appRouter.showItem(id, apiClient.serverId())
}
function addToCollection(apiClient, dlg, id) {
var url = apiClient.getUrl("Collections/" + id + "/Items", {
Ids: dlg.querySelector(".fldSelectedItemIds").value || ""
});
apiClient.ajax({
type: "POST",
url: url
}).then(function() {
loading.hide(), dlg.submitted = !0, dialogHelper.close(dlg), require(["toast"], function(toast) {
toast(globalize.translate("sharedcomponents#MessageItemsAdded"))
})
})
}
function triggerChange(select) {
select.dispatchEvent(new CustomEvent("change", {}))
}
function populateCollections(panel) {
loading.show();
var select = panel.querySelector("#selectCollectionToAddTo");
panel.querySelector(".newCollectionInfo").classList.add("hide");
var options = {
Recursive: !0,
IncludeItemTypes: "BoxSet",
SortBy: "SortName",
EnableTotalRecordCount: !1
},
apiClient = connectionManager.getApiClient(currentServerId);
apiClient.getItems(apiClient.getCurrentUserId(), options).then(function(result) {
var html = "";
html += '<option value="">' + globalize.translate("sharedcomponents#OptionNew") + "</option>", html += result.Items.map(function(i) {
return '<option value="' + i.Id + '">' + i.Name + "</option>"
}), select.innerHTML = html, select.value = "", triggerChange(select), loading.hide()
})
}
function getEditorHtml() {
var html = "";
return html += '<div class="formDialogContent smoothScrollY" style="padding-top:2em;">', html += '<div class="dialogContentInner dialog-content-centered">', html += '<form class="newCollectionForm" style="margin:auto;">', html += "<div>", html += globalize.translate("sharedcomponents#NewCollectionHelp"), html += "</div>", html += '<div class="fldSelectCollection">', html += "<br/>", html += "<br/>", html += '<div class="selectContainer">', html += '<select is="emby-select" label="' + globalize.translate("sharedcomponents#LabelCollection") + '" id="selectCollectionToAddTo" autofocus></select>', html += "</div>", html += "</div>", html += '<div class="newCollectionInfo">', html += '<div class="inputContainer">', html += '<input is="emby-input" type="text" id="txtNewCollectionName" required="required" label="' + globalize.translate("sharedcomponents#LabelName") + '" />', html += '<div class="fieldDescription">' + globalize.translate("sharedcomponents#NewCollectionNameExample") + "</div>", html += "</div>", html += '<label class="checkboxContainer">', html += '<input is="emby-checkbox" type="checkbox" id="chkEnableInternetMetadata" />', html += "<span>" + globalize.translate("sharedcomponents#SearchForCollectionInternetMetadata") + "</span>", html += "</label>", html += "</div>", html += '<div class="formDialogFooter">', html += '<button is="emby-button" type="submit" class="raised btnSubmit block formDialogFooterItem button-submit">' + globalize.translate("sharedcomponents#ButtonOk") + "</button>", html += "</div>", html += '<input type="hidden" class="fldSelectedItemIds" />', html += "</form>", html += "</div>", html += "</div>"
}
function initEditor(content, items) {
if (content.querySelector("#selectCollectionToAddTo").addEventListener("change", function() {
this.value ? (content.querySelector(".newCollectionInfo").classList.add("hide"), content.querySelector("#txtNewCollectionName").removeAttribute("required")) : (content.querySelector(".newCollectionInfo").classList.remove("hide"), content.querySelector("#txtNewCollectionName").setAttribute("required", "required"))
}), content.querySelector("form").addEventListener("submit", onSubmit), content.querySelector(".fldSelectedItemIds", content).value = items.join(","), items.length) content.querySelector(".fldSelectCollection").classList.remove("hide"), populateCollections(content);
else {
content.querySelector(".fldSelectCollection").classList.add("hide");
var selectCollectionToAddTo = content.querySelector("#selectCollectionToAddTo");
selectCollectionToAddTo.innerHTML = "", selectCollectionToAddTo.value = "", triggerChange(selectCollectionToAddTo)
}
}
function centerFocus(elem, horiz, on) {
require(["scrollHelper"], function(scrollHelper) {
var fn = on ? "on" : "off";
scrollHelper.centerFocus[fn](elem, horiz)
})
}
function CollectionEditor() {}
var currentServerId;
return CollectionEditor.prototype.show = function(options) {
var items = options.items || {};
currentServerId = options.serverId;
var dialogOptions = {
removeOnClose: !0,
scrollY: !1
};
layoutManager.tv ? dialogOptions.size = "fullscreen" : dialogOptions.size = "small";
var dlg = dialogHelper.createDialog(dialogOptions);
dlg.classList.add("formDialog");
var html = "",
title = items.length ? globalize.translate("sharedcomponents#HeaderAddToCollection") : globalize.translate("sharedcomponents#NewCollection");
return html += '<div class="formDialogHeader">', html += '<button is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1"><i class="md-icon">&#xE5C4;</i></button>', html += '<h3 class="formDialogHeaderTitle">', html += title, html += "</h3>", appHost.supports("externallinks") && (html += '<a is="emby-linkbutton" class="button-link btnHelp flex align-items-center" href="https://github.com/MediaBrowser/Wiki/wiki/Collections" target="_blank" style="margin-left:auto;margin-right:.5em;padding:.25em;" title="' + globalize.translate("sharedcomponents#Help") + '"><i class="md-icon">&#xE88E;</i><span style="margin-left:.25em;">' + globalize.translate("sharedcomponents#Help") + "</span></a>"), html += "</div>", html += getEditorHtml(), dlg.innerHTML = html, initEditor(dlg, items), dlg.querySelector(".btnCancel").addEventListener("click", function() {
dialogHelper.close(dlg)
}), layoutManager.tv && centerFocus(dlg.querySelector(".formDialogContent"), !1, !0), dialogHelper.open(dlg).then(function() {
return layoutManager.tv && centerFocus(dlg.querySelector(".formDialogContent"), !1, !1), dlg.submitted ? Promise.resolve() : Promise.reject()
})
}, CollectionEditor
});

View file

@ -0,0 +1,22 @@
define(["dialog", "globalize"], function(dialog, globalize) {
"use strict";
return function(text, title) {
var options;
options = "string" == typeof text ? {
title: title,
text: text
} : text;
var items = [];
return items.push({
name: options.cancelText || globalize.translate("sharedcomponents#ButtonCancel"),
id: "cancel",
type: "cancel" === options.primary ? "submit" : "cancel"
}), items.push({
name: options.confirmText || globalize.translate("sharedcomponents#ButtonOk"),
id: "ok",
type: "cancel" === options.primary ? "cancel" : "submit"
}), options.buttons = items, dialog(options).then(function(result) {
return "ok" === result ? Promise.resolve() : Promise.reject()
})
}
});

View file

@ -0,0 +1,15 @@
define([], function() {
"use strict";
function replaceAll(str, find, replace) {
return str.split(find).join(replace)
}
return function(options) {
"string" == typeof options && (options = {
title: "",
text: options
});
var text = replaceAll(options.text || "", "<br/>", "\n");
return confirm(text) ? Promise.resolve() : Promise.reject()
}
});

View file

@ -0,0 +1,123 @@
define(["globalize"], function(globalize) {
"use strict";
function parseISO8601Date(s, toLocal) {
var re = /(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(\.\d+)?(Z|([+-])(\d{2}):(\d{2}))?/,
d = s.match(re);
if (!d) throw "Couldn't parse ISO 8601 date string '" + s + "'";
var a = [1, 2, 3, 4, 5, 6, 10, 11];
for (var i in a) d[a[i]] = parseInt(d[a[i]], 10);
d[7] = parseFloat(d[7]);
var ms = Date.UTC(d[1], d[2] - 1, d[3], d[4], d[5], d[6]);
if (d[7] > 0 && (ms += Math.round(1e3 * d[7])), "Z" !== d[8] && d[10]) {
var offset = 60 * d[10] * 60 * 1e3;
d[11] && (offset += 60 * d[11] * 1e3), "-" === d[9] ? ms -= offset : ms += offset
} else !1 === toLocal && (ms += 6e4 * (new Date).getTimezoneOffset());
return new Date(ms)
}
function getDisplayRunningTime(ticks) {
var parts = [],
hours = ticks / 36e9;
hours = Math.floor(hours), hours && parts.push(hours), ticks -= 36e9 * hours;
var minutes = ticks / 6e8;
minutes = Math.floor(minutes), ticks -= 6e8 * minutes, minutes < 10 && hours && (minutes = "0" + minutes), parts.push(minutes);
var seconds = ticks / 1e7;
return seconds = Math.floor(seconds), seconds < 10 && (seconds = "0" + seconds), parts.push(seconds), parts.join(":")
}
function getOptionList(options) {
var list = [];
for (var i in options) list.push({
name: i,
value: options[i]
});
return list
}
function toLocaleString(date, options) {
if (!date) throw new Error("date cannot be null");
if (options = options || {}, toLocaleTimeStringSupportsLocales) {
var currentLocale = globalize.getCurrentDateTimeLocale();
if (currentLocale) return date.toLocaleString(currentLocale, options)
}
return date.toLocaleString()
}
function toLocaleDateString(date, options) {
if (!date) throw new Error("date cannot be null");
if (options = options || {}, toLocaleTimeStringSupportsLocales) {
var currentLocale = globalize.getCurrentDateTimeLocale();
if (currentLocale) return date.toLocaleDateString(currentLocale, options)
}
var optionList = getOptionList(options);
if (1 === optionList.length && "weekday" === optionList[0].name) {
var weekday = [];
return weekday[0] = "Sun", weekday[1] = "Mon", weekday[2] = "Tue", weekday[3] = "Wed", weekday[4] = "Thu", weekday[5] = "Fri", weekday[6] = "Sat", weekday[date.getDay()]
}
return date.toLocaleDateString()
}
function toLocaleTimeString(date, options) {
if (!date) throw new Error("date cannot be null");
if (options = options || {}, toLocaleTimeStringSupportsLocales) {
var currentLocale = globalize.getCurrentDateTimeLocale();
if (currentLocale) return date.toLocaleTimeString(currentLocale, options)
}
return date.toLocaleTimeString()
}
function getDisplayTime(date) {
if (!date) throw new Error("date cannot be null");
if ("string" === (typeof date).toString().toLowerCase()) try {
date = parseISO8601Date(date, !0)
} catch (err) {
return date
}
if (toLocaleTimeStringSupportsLocales) return toLocaleTimeString(date, {
hour: "numeric",
minute: "2-digit"
});
var time = toLocaleTimeString(date),
timeLower = time.toLowerCase();
if (-1 !== timeLower.indexOf("am") || -1 !== timeLower.indexOf("pm")) {
time = timeLower;
var hour = date.getHours() % 12,
suffix = date.getHours() > 11 ? "pm" : "am";
hour || (hour = 12);
var minutes = date.getMinutes();
minutes < 10 && (minutes = "0" + minutes), minutes = ":" + minutes, time = hour + minutes + suffix
} else {
var timeParts = time.split(":");
timeParts.length > 2 && (timeParts.length = 2, time = timeParts.join(":"))
}
return time
}
function isRelativeDay(date, offsetInDays) {
if (!date) throw new Error("date cannot be null");
var yesterday = new Date,
day = yesterday.getDate() + offsetInDays;
return yesterday.setDate(day), date.getFullYear() === yesterday.getFullYear() && date.getMonth() === yesterday.getMonth() && date.getDate() === day
}
var toLocaleTimeStringSupportsLocales = function() {
try {
(new Date).toLocaleTimeString("i")
} catch (e) {
return "RangeError" === e.name
}
return !1
}();
return {
parseISO8601Date: parseISO8601Date,
getDisplayRunningTime: getDisplayRunningTime,
toLocaleDateString: toLocaleDateString,
toLocaleString: toLocaleString,
getDisplayTime: getDisplayTime,
isRelativeDay: isRelativeDay,
toLocaleTimeString: toLocaleTimeString,
supportsLocalization: function() {
return toLocaleTimeStringSupportsLocales
}
}
});

View file

@ -0,0 +1,39 @@
define(["connectionManager", "confirm", "appRouter", "globalize"], function(connectionManager, confirm, appRouter, globalize) {
"use strict";
function alertText(options) {
return new Promise(function(resolve, reject) {
require(["alert"], function(alert) {
alert(options).then(resolve, resolve)
})
})
}
function deleteItem(options) {
var item = options.item,
itemId = item.Id,
parentId = item.SeasonId || item.SeriesId || item.ParentId,
serverId = item.ServerId,
msg = globalize.translate("sharedcomponents#ConfirmDeleteItem"),
title = globalize.translate("sharedcomponents#HeaderDeleteItem"),
apiClient = connectionManager.getApiClient(item.ServerId);
return confirm({
title: title,
text: msg,
confirmText: globalize.translate("sharedcomponents#Delete"),
primary: "cancel"
}).then(function() {
return apiClient.deleteItem(itemId).then(function() {
options.navigate && (parentId ? appRouter.showItem(parentId, serverId) : appRouter.goHome())
}, function(err) {
var result = function() {
return Promise.reject(err)
};
return alertText(globalize.translate("sharedcomponents#ErrorDeletingItem")).then(result, result)
})
})
}
return {
deleteItem: deleteItem
}
});

View file

@ -0,0 +1,46 @@
define(["dialogHelper", "dom", "layoutManager", "scrollHelper", "globalize", "require", "material-icons", "emby-button", "paper-icon-button-light", "emby-input", "formDialogStyle", "flexStyles"], function(dialogHelper, dom, layoutManager, scrollHelper, globalize, require) {
"use strict";
function showDialog(options, template) {
function onButtonClick() {
dialogResult = this.getAttribute("data-id"), dialogHelper.close(dlg)
}
var dialogOptions = {
removeOnClose: !0,
scrollY: !1
},
enableTvLayout = layoutManager.tv;
enableTvLayout && (dialogOptions.size = "fullscreen");
var dlg = dialogHelper.createDialog(dialogOptions);
dlg.classList.add("formDialog"), dlg.innerHTML = globalize.translateHtml(template, "sharedcomponents"), dlg.classList.add("align-items-center"), dlg.classList.add("justify-content-center");
var formDialogContent = dlg.querySelector(".formDialogContent");
formDialogContent.classList.add("no-grow"), enableTvLayout ? (formDialogContent.style["max-width"] = "50%", formDialogContent.style["max-height"] = "60%", scrollHelper.centerFocus.on(formDialogContent, !1)) : (formDialogContent.style.maxWidth = Math.min(150 * options.buttons.length + 200, dom.getWindowSize().innerWidth - 50) + "px", dlg.classList.add("dialog-fullscreen-lowres")), options.title ? dlg.querySelector(".formDialogHeaderTitle").innerHTML = options.title || "" : dlg.querySelector(".formDialogHeaderTitle").classList.add("hide");
var displayText = options.html || options.text || "";
dlg.querySelector(".text").innerHTML = displayText, displayText || dlg.querySelector(".dialogContentInner").classList.add("hide");
var i, length, html = "",
hasDescriptions = !1;
for (i = 0, length = options.buttons.length; i < length; i++) {
var item = options.buttons[i],
autoFocus = 0 === i ? " autofocus" : "",
buttonClass = "btnOption raised formDialogFooterItem formDialogFooterItem-autosize";
item.type && (buttonClass += " button-" + item.type), item.description && (hasDescriptions = !0), hasDescriptions && (buttonClass += " formDialogFooterItem-vertical formDialogFooterItem-nomarginbottom"), html += '<button is="emby-button" type="button" class="' + buttonClass + '" data-id="' + item.id + '"' + autoFocus + ">" + item.name + "</button>", item.description && (html += '<div class="formDialogFooterItem formDialogFooterItem-autosize fieldDescription" style="margin-top:.25em!important;margin-bottom:1.25em!important;">' + item.description + "</div>")
}
dlg.querySelector(".formDialogFooter").innerHTML = html, hasDescriptions && dlg.querySelector(".formDialogFooter").classList.add("formDialogFooter-vertical");
var dialogResult, buttons = dlg.querySelectorAll(".btnOption");
for (i = 0, length = buttons.length; i < length; i++) buttons[i].addEventListener("click", onButtonClick);
return dialogHelper.open(dlg).then(function() {
return enableTvLayout && scrollHelper.centerFocus.off(dlg.querySelector(".formDialogContent"), !1), dialogResult || Promise.reject()
})
}
return function(text, title) {
var options;
return options = "string" == typeof text ? {
title: title,
text: text
} : text, new Promise(function(resolve, reject) {
require(["text!./dialog.template.html"], function(template) {
showDialog(options, template).then(resolve, reject)
})
})
}
});

View file

@ -0,0 +1,15 @@
<div class="formDialogHeader formDialogHeader-clear justify-content-center">
<h1 class="formDialogHeaderTitle" style="margin-left:0;margin-top: .5em;padding: 0 1em;"></h1>
</div>
<div class="formDialogContent smoothScrollY">
<div class="dialogContentInner dialog-content-centered" style="padding-top:1em;padding-bottom: 1em; text-align: center;">
<div class="text">
</div>
</div>
</div>
<div class="formDialogFooter formDialogFooter-clear formDialogFooter-flex" style="padding-bottom: 1.5em;">
</div>

View file

@ -0,0 +1,267 @@
.dialogContainer {
display: -webkit-box;
display: -webkit-flex;
display: flex;
-webkit-box-align: center;
-webkit-align-items: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
justify-content: center;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 999999 !important;
contain: strict;
overflow: hidden;
overscroll-behavior: contain
}
.dialog {
margin: 0;
-webkit-border-radius: .2em;
border-radius: .2em;
-webkit-font-smoothing: antialiased;
border: 0;
padding: 0;
will-change: transform, opacity;
contain: style paint;
-webkit-box-shadow: 0 16px 24px 2px rgba(0, 0, 0, .14), 0 6px 30px 5px rgba(0, 0, 0, .12), 0 8px 10px -5px rgba(0, 0, 0, .4);
box-shadow: 0 16px 24px 2px rgba(0, 0, 0, .14), 0 6px 30px 5px rgba(0, 0, 0, .12), 0 8px 10px -5px rgba(0, 0, 0, .4)
}
.dialog-fixedSize {
-webkit-border-radius: 0;
border-radius: 0;
max-height: none;
max-width: none;
contain: layout style paint
}
.dialog-fullscreen {
position: fixed !important;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: 0;
-webkit-box-shadow: none;
box-shadow: none
}
@-webkit-keyframes scaledown {
from {
opacity: 1;
-webkit-transform: none;
transform: none
}
to {
opacity: 0;
-webkit-transform: scale(.5);
transform: scale(.5)
}
}
@keyframes scaledown {
from {
opacity: 1;
-webkit-transform: none;
transform: none
}
to {
opacity: 0;
-webkit-transform: scale(.5);
transform: scale(.5)
}
}
@-webkit-keyframes scaleup {
from {
-webkit-transform: scale(.5);
transform: scale(.5);
opacity: 0
}
to {
-webkit-transform: none;
transform: none;
opacity: 1
}
}
@keyframes scaleup {
from {
-webkit-transform: scale(.5);
transform: scale(.5);
opacity: 0
}
to {
-webkit-transform: none;
transform: none;
opacity: 1
}
}
@-webkit-keyframes fadein {
from {
opacity: 0
}
to {
opacity: 1
}
}
@keyframes fadein {
from {
opacity: 0
}
to {
opacity: 1
}
}
@-webkit-keyframes fadeout {
from {
opacity: 1
}
to {
opacity: 0
}
}
@keyframes fadeout {
from {
opacity: 1
}
to {
opacity: 0
}
}
@-webkit-keyframes slideup {
from {
opacity: 0;
-webkit-transform: translate3d(0, 30%, 0);
transform: translate3d(0, 30%, 0)
}
to {
opacity: 1;
-webkit-transform: none;
transform: none
}
}
@keyframes slideup {
from {
opacity: 0;
-webkit-transform: translate3d(0, 30%, 0);
transform: translate3d(0, 30%, 0)
}
to {
opacity: 1;
-webkit-transform: none;
transform: none
}
}
@-webkit-keyframes slidedown {
from {
opacity: 1;
-webkit-transform: none;
transform: none
}
to {
opacity: 0;
-webkit-transform: translate3d(0, 20%, 0);
transform: translate3d(0, 20%, 0)
}
}
@keyframes slidedown {
from {
opacity: 1;
-webkit-transform: none;
transform: none
}
to {
opacity: 0;
-webkit-transform: translate3d(0, 20%, 0);
transform: translate3d(0, 20%, 0)
}
}
@media all and (max-width:80em),
all and (max-height:45em) {
.dialog-fixedSize,
.dialog-fullscreen-lowres {
position: fixed !important;
top: 0 !important;
bottom: 0 !important;
left: 0 !important;
right: 0 !important;
margin: 0 !important;
-webkit-box-shadow: none;
box-shadow: none
}
}
@media all and (min-width:80em) and (min-height:45em) {
.dialog-medium {
width: 80%;
height: 80%
}
.dialog-medium-tall {
width: 80%;
height: 90%
}
.dialog-small {
width: 60%;
height: 80%
}
.dialog-fullscreen-border {
width: 90%;
height: 90%
}
}
.noScroll {
overflow-x: hidden !important;
overflow-y: hidden !important
}
.dialogBackdrop {
background-color: #000;
opacity: 0;
position: fixed !important;
top: 0 !important;
bottom: 0 !important;
left: 0 !important;
right: 0 !important;
margin: 0 !important;
z-index: 999999 !important;
-webkit-transition: opacity ease-out .2s;
-o-transition: opacity ease-out .2s;
transition: opacity ease-out .2s;
will-change: opacity
}
.dialogBackdropOpened {
opacity: .5
}

View file

@ -0,0 +1,225 @@
define(["appRouter", "focusManager", "browser", "layoutManager", "inputManager", "dom", "css!./dialoghelper.css", "scrollStyles"], function(appRouter, focusManager, browser, layoutManager, inputManager, dom) {
"use strict";
function enableAnimation() {
return !browser.tv && browser.supportsCssAnimation()
}
function removeCenterFocus(dlg) {
layoutManager.tv && (dlg.classList.contains("scrollX") ? centerFocus(dlg, !0, !1) : dlg.classList.contains("smoothScrollY") && centerFocus(dlg, !1, !1))
}
function tryRemoveElement(elem) {
var parentNode = elem.parentNode;
if (parentNode) try {
parentNode.removeChild(elem)
} catch (err) {
console.log("Error removing dialog element: " + err)
}
}
function DialogHashHandler(dlg, hash, resolve) {
function onHashChange(e) {
var isBack = self.originalUrl === window.location.href;
!isBack && isOpened(dlg) || window.removeEventListener("popstate", onHashChange), isBack && (self.closedByBack = !0, closeDialog(dlg))
}
function onBackCommand(e) {
"back" === e.detail.command && (self.closedByBack = !0, e.preventDefault(), e.stopPropagation(), closeDialog(dlg))
}
function onDialogClosed() {
if (isHistoryEnabled(dlg) || inputManager.off(dlg, onBackCommand), window.removeEventListener("popstate", onHashChange), removeBackdrop(dlg), dlg.classList.remove("opened"), removeScrollLockOnClose && document.body.classList.remove("noScroll"), !self.closedByBack && isHistoryEnabled(dlg)) {
(history.state || {}).dialogId === hash && history.back()
}
if (layoutManager.tv && focusManager.focus(activeElement), "false" !== dlg.getAttribute("data-removeonclose")) {
removeCenterFocus(dlg);
var dialogContainer = dlg.dialogContainer;
dialogContainer ? (tryRemoveElement(dialogContainer), dlg.dialogContainer = null) : tryRemoveElement(dlg)
}
setTimeout(function() {
resolve({
element: dlg,
closedByBack: self.closedByBack
})
}, 1)
}
var self = this;
self.originalUrl = window.location.href;
var activeElement = document.activeElement,
removeScrollLockOnClose = !1;
dlg.addEventListener("close", onDialogClosed), !dlg.classList.contains("dialog-fixedSize") && dlg.classList.add("centeredDialog"), dlg.classList.remove("hide"), addBackdropOverlay(dlg), dlg.classList.add("opened"), dlg.dispatchEvent(new CustomEvent("open", {
bubbles: !1,
cancelable: !1
})), "true" !== dlg.getAttribute("data-lockscroll") || document.body.classList.contains("noScroll") || (document.body.classList.add("noScroll"), removeScrollLockOnClose = !0), animateDialogOpen(dlg), isHistoryEnabled(dlg) ? (appRouter.pushState({
dialogId: hash
}, "Dialog", "#" + hash), window.addEventListener("popstate", onHashChange)) : inputManager.on(dlg, onBackCommand)
}
function addBackdropOverlay(dlg) {
var backdrop = document.createElement("div");
backdrop.classList.add("dialogBackdrop");
var backdropParent = dlg.dialogContainer || dlg;
backdropParent.parentNode.insertBefore(backdrop, backdropParent), dlg.backdrop = backdrop, backdrop.offsetWidth, backdrop.classList.add("dialogBackdropOpened"), dom.addEventListener(dlg.dialogContainer || backdrop, "click", function(e) {
e.target === dlg.dialogContainer && close(dlg)
}, {
passive: !0
})
}
function isHistoryEnabled(dlg) {
return "true" === dlg.getAttribute("data-history")
}
function open(dlg) {
globalOnOpenCallback && globalOnOpenCallback(dlg);
var parent = dlg.parentNode;
parent && parent.removeChild(dlg);
var dialogContainer = document.createElement("div");
return dialogContainer.classList.add("dialogContainer"), dialogContainer.appendChild(dlg), dlg.dialogContainer = dialogContainer, document.body.appendChild(dialogContainer), new Promise(function(resolve, reject) {
new DialogHashHandler(dlg, "dlg" + (new Date).getTime(), resolve)
})
}
function isOpened(dlg) {
return !dlg.classList.contains("hide")
}
function close(dlg) {
isOpened(dlg) && (isHistoryEnabled(dlg) ? history.back() : closeDialog(dlg))
}
function closeDialog(dlg) {
if (!dlg.classList.contains("hide")) {
dlg.dispatchEvent(new CustomEvent("closing", {
bubbles: !1,
cancelable: !1
}));
animateDialogClose(dlg, function() {
focusManager.popScope(dlg), dlg.classList.add("hide"), dlg.dispatchEvent(new CustomEvent("close", {
bubbles: !1,
cancelable: !1
}))
})
}
}
function animateDialogOpen(dlg) {
var onAnimationFinish = function() {
focusManager.pushScope(dlg), "true" === dlg.getAttribute("data-autofocus") && focusManager.autoFocus(dlg)
};
if (enableAnimation()) {
var onFinish = function() {
dom.removeEventListener(dlg, dom.whichAnimationEvent(), onFinish, {
once: !0
}), onAnimationFinish()
};
return void dom.addEventListener(dlg, dom.whichAnimationEvent(), onFinish, {
once: !0
})
}
onAnimationFinish()
}
function animateDialogClose(dlg, onAnimationFinish) {
if (enableAnimation()) {
var animated = !0;
switch (dlg.animationConfig.exit.name) {
case "fadeout":
dlg.style.animation = "fadeout " + dlg.animationConfig.exit.timing.duration + "ms ease-out normal both";
break;
case "scaledown":
dlg.style.animation = "scaledown " + dlg.animationConfig.exit.timing.duration + "ms ease-out normal both";
break;
case "slidedown":
dlg.style.animation = "slidedown " + dlg.animationConfig.exit.timing.duration + "ms ease-out normal both";
break;
default:
animated = !1
}
var onFinish = function() {
dom.removeEventListener(dlg, dom.whichAnimationEvent(), onFinish, {
once: !0
}), onAnimationFinish()
};
if (dom.addEventListener(dlg, dom.whichAnimationEvent(), onFinish, {
once: !0
}), animated) return
}
onAnimationFinish()
}
function shouldLockDocumentScroll(options) {
return !(supportsOverscrollBehavior && (options.size || !browser.touch)) && (null != options.lockScroll ? options.lockScroll : "fullscreen" === options.size || (!!options.size || browser.touch))
}
function removeBackdrop(dlg) {
var backdrop = dlg.backdrop;
if (backdrop) {
dlg.backdrop = null;
var onAnimationFinish = function() {
tryRemoveElement(backdrop)
};
if (enableAnimation()) return backdrop.classList.remove("dialogBackdropOpened"), void setTimeout(onAnimationFinish, 300);
onAnimationFinish()
}
}
function centerFocus(elem, horiz, on) {
require(["scrollHelper"], function(scrollHelper) {
var fn = on ? "on" : "off";
scrollHelper.centerFocus[fn](elem, horiz)
})
}
function createDialog(options) {
options = options || {};
var dlg = document.createElement("div");
dlg.classList.add("focuscontainer"), dlg.classList.add("hide"), shouldLockDocumentScroll(options) && dlg.setAttribute("data-lockscroll", "true"), !1 !== options.enableHistory && appRouter.enableNativeHistory() && dlg.setAttribute("data-history", "true"), !1 !== options.modal && dlg.setAttribute("modal", "modal"), !1 !== options.autoFocus && dlg.setAttribute("data-autofocus", "true");
var defaultEntryAnimation, defaultExitAnimation;
defaultEntryAnimation = "scaleup", defaultExitAnimation = "scaledown";
var entryAnimation = options.entryAnimation || defaultEntryAnimation,
exitAnimation = options.exitAnimation || defaultExitAnimation,
entryAnimationDuration = options.entryAnimationDuration || ("fullscreen" !== options.size ? 180 : 280),
exitAnimationDuration = options.exitAnimationDuration || ("fullscreen" !== options.size ? 120 : 220);
if (dlg.animationConfig = {
entry: {
name: entryAnimation,
timing: {
duration: entryAnimationDuration,
easing: "ease-out"
}
},
exit: {
name: exitAnimation,
timing: {
duration: exitAnimationDuration,
easing: "ease-out",
fill: "both"
}
}
}, dlg.classList.add("dialog"), options.scrollX ? (dlg.classList.add("scrollX"), dlg.classList.add("smoothScrollX"), layoutManager.tv && centerFocus(dlg, !0, !0)) : !1 !== options.scrollY && (dlg.classList.add("smoothScrollY"), layoutManager.tv && centerFocus(dlg, !1, !0)), options.removeOnClose && dlg.setAttribute("data-removeonclose", "true"), options.size && (dlg.classList.add("dialog-fixedSize"), dlg.classList.add("dialog-" + options.size)), enableAnimation()) switch (dlg.animationConfig.entry.name) {
case "fadein":
dlg.style.animation = "fadein " + entryAnimationDuration + "ms ease-out normal";
break;
case "scaleup":
dlg.style.animation = "scaleup " + entryAnimationDuration + "ms ease-out normal both";
break;
case "slideup":
dlg.style.animation = "slideup " + entryAnimationDuration + "ms ease-out normal";
break;
case "slidedown":
dlg.style.animation = "slidedown " + entryAnimationDuration + "ms ease-out normal"
}
return dlg
}
var globalOnOpenCallback, supportsOverscrollBehavior = "overscroll-behavior-y" in document.body.style;
return {
open: open,
close: close,
createDialog: createDialog,
setOnOpen: function(val) {
globalOnOpenCallback = val
}
}
});

View file

@ -0,0 +1,122 @@
define(["require", "browser", "layoutManager", "appSettings", "pluginManager", "apphost", "focusManager", "datetime", "globalize", "loading", "connectionManager", "skinManager", "dom", "events", "emby-select", "emby-checkbox", "emby-linkbutton"], function(require, browser, layoutManager, appSettings, pluginManager, appHost, focusManager, datetime, globalize, loading, connectionManager, skinManager, dom, events) {
"use strict";
function fillThemes(select, isDashboard) {
select.innerHTML = skinManager.getThemes().map(function(t) {
var value = t.id;
return t.isDefault && !isDashboard ? value = "" : t.isDefaultServerDashboard && isDashboard && (value = ""), '<option value="' + value + '">' + t.name + "</option>"
}).join("")
}
function loadScreensavers(context, userSettings) {
var selectScreensaver = context.querySelector(".selectScreensaver"),
options = pluginManager.ofType("screensaver").map(function(plugin) {
return {
name: plugin.name,
value: plugin.id
}
});
options.unshift({
name: globalize.translate("sharedcomponents#None"),
value: "none"
}), selectScreensaver.innerHTML = options.map(function(o) {
return '<option value="' + o.value + '">' + o.name + "</option>"
}).join(""), selectScreensaver.value = userSettings.screensaver(), selectScreensaver.value || (selectScreensaver.value = "none")
}
function loadSoundEffects(context, userSettings) {
var selectSoundEffects = context.querySelector(".selectSoundEffects"),
options = pluginManager.ofType("soundeffects").map(function(plugin) {
return {
name: plugin.name,
value: plugin.id
}
});
options.unshift({
name: globalize.translate("sharedcomponents#None"),
value: "none"
}), selectSoundEffects.innerHTML = options.map(function(o) {
return '<option value="' + o.value + '">' + o.name + "</option>"
}).join(""), selectSoundEffects.value = userSettings.soundEffects(), selectSoundEffects.value || (selectSoundEffects.value = "none")
}
function loadSkins(context, userSettings) {
var selectSkin = context.querySelector(".selectSkin"),
options = pluginManager.ofType("skin").map(function(plugin) {
return {
name: plugin.name,
value: plugin.id
}
});
selectSkin.innerHTML = options.map(function(o) {
return '<option value="' + o.value + '">' + o.name + "</option>"
}).join(""), selectSkin.value = userSettings.skin(), !selectSkin.value && options.length && (selectSkin.value = options[0].value), options.length > 1 && appHost.supports("skins") ? context.querySelector(".selectSkinContainer").classList.remove("hide") : context.querySelector(".selectSkinContainer").classList.add("hide")
}
function showOrHideMissingEpisodesField(context, user, apiClient) {
if (browser.tizen || browser.web0s) return void context.querySelector(".fldDisplayMissingEpisodes").classList.add("hide");
context.querySelector(".fldDisplayMissingEpisodes").classList.remove("hide")
}
function loadForm(context, user, userSettings, apiClient) {
apiClient.getCurrentUserId(), user.Id;
user.Policy.IsAdministrator ? context.querySelector(".selectDashboardThemeContainer").classList.remove("hide") : context.querySelector(".selectDashboardThemeContainer").classList.add("hide"), appHost.supports("displaylanguage") ? context.querySelector(".languageSection").classList.remove("hide") : context.querySelector(".languageSection").classList.add("hide"), appHost.supports("displaymode") ? context.querySelector(".fldDisplayMode").classList.remove("hide") : context.querySelector(".fldDisplayMode").classList.add("hide"), appHost.supports("externallinks") ? context.querySelector(".learnHowToContributeContainer").classList.remove("hide") : context.querySelector(".learnHowToContributeContainer").classList.add("hide"), appHost.supports("runatstartup") ? context.querySelector(".fldAutorun").classList.remove("hide") : context.querySelector(".fldAutorun").classList.add("hide"), appHost.supports("soundeffects") ? context.querySelector(".fldSoundEffects").classList.remove("hide") : context.querySelector(".fldSoundEffects").classList.add("hide"), appHost.supports("screensaver") ? context.querySelector(".selectScreensaverContainer").classList.remove("hide") : context.querySelector(".selectScreensaverContainer").classList.add("hide"), datetime.supportsLocalization() ? context.querySelector(".fldDateTimeLocale").classList.remove("hide") : context.querySelector(".fldDateTimeLocale").classList.add("hide"), browser.tizen || browser.web0s ? (context.querySelector(".fldSeasonalThemes").classList.add("hide"), context.querySelector(".fldBackdrops").classList.add("hide"), context.querySelector(".fldThemeSong").classList.add("hide"), context.querySelector(".fldThemeVideo").classList.add("hide")) : (context.querySelector(".fldSeasonalThemes").classList.remove("hide"), context.querySelector(".fldBackdrops").classList.remove("hide"), context.querySelector(".fldThemeSong").classList.remove("hide"), context.querySelector(".fldThemeVideo").classList.remove("hide")), context.querySelector(".chkRunAtStartup").checked = appSettings.runAtStartup();
var selectTheme = context.querySelector("#selectTheme"),
selectDashboardTheme = context.querySelector("#selectDashboardTheme");
fillThemes(selectTheme), fillThemes(selectDashboardTheme, !0), loadScreensavers(context, userSettings), loadSoundEffects(context, userSettings), loadSkins(context, userSettings), context.querySelector(".chkDisplayMissingEpisodes").checked = user.Configuration.DisplayMissingEpisodes || !1, context.querySelector("#chkThemeSong").checked = userSettings.enableThemeSongs(), context.querySelector("#chkThemeVideo").checked = userSettings.enableThemeVideos(), context.querySelector("#chkBackdrops").checked = userSettings.enableBackdrops(), context.querySelector("#chkSeasonalThemes").checked = userSettings.enableSeasonalThemes(), context.querySelector("#selectLanguage").value = userSettings.language() || "", context.querySelector(".selectDateTimeLocale").value = userSettings.dateTimeLocale() || "", selectDashboardTheme.value = userSettings.dashboardTheme() || "", selectTheme.value = userSettings.theme() || "", context.querySelector(".selectLayout").value = layoutManager.getSavedLayout() || "", showOrHideMissingEpisodesField(context, user, apiClient), loading.hide()
}
function saveUser(context, user, userSettingsInstance, apiClient) {
return appSettings.runAtStartup(context.querySelector(".chkRunAtStartup").checked), user.Configuration.DisplayMissingEpisodes = context.querySelector(".chkDisplayMissingEpisodes").checked, appHost.supports("displaylanguage") && userSettingsInstance.language(context.querySelector("#selectLanguage").value), userSettingsInstance.dateTimeLocale(context.querySelector(".selectDateTimeLocale").value), userSettingsInstance.enableThemeSongs(context.querySelector("#chkThemeSong").checked), userSettingsInstance.enableThemeVideos(context.querySelector("#chkThemeVideo").checked), userSettingsInstance.dashboardTheme(context.querySelector("#selectDashboardTheme").value), userSettingsInstance.theme(context.querySelector("#selectTheme").value), userSettingsInstance.soundEffects(context.querySelector(".selectSoundEffects").value), userSettingsInstance.screensaver(context.querySelector(".selectScreensaver").value), userSettingsInstance.skin(context.querySelector(".selectSkin").value), userSettingsInstance.enableBackdrops(context.querySelector("#chkBackdrops").checked), userSettingsInstance.enableSeasonalThemes(context.querySelector("#chkSeasonalThemes").checked), user.Id === apiClient.getCurrentUserId() && skinManager.setTheme(userSettingsInstance.theme()), layoutManager.setLayout(context.querySelector(".selectLayout").value), apiClient.updateUserConfiguration(user.Id, user.Configuration)
}
function save(instance, context, userId, userSettings, apiClient, enableSaveConfirmation) {
loading.show(), apiClient.getUser(userId).then(function(user) {
saveUser(context, user, userSettings, apiClient).then(function() {
loading.hide(), enableSaveConfirmation && require(["toast"], function(toast) {
toast(globalize.translate("sharedcomponents#SettingsSaved"))
}), events.trigger(instance, "saved")
}, function() {
loading.hide()
})
})
}
function onSubmit(e) {
var self = this,
apiClient = connectionManager.getApiClient(self.options.serverId),
userId = self.options.userId,
userSettings = self.options.userSettings;
return userSettings.setUserInfo(userId, apiClient).then(function() {
var enableSaveConfirmation = self.options.enableSaveConfirmation;
save(self, self.options.element, userId, userSettings, apiClient, enableSaveConfirmation)
}), e && e.preventDefault(), !1
}
function embed(options, self) {
require(["text!./displaysettings.template.html"], function(template) {
options.element.innerHTML = globalize.translateDocument(template, "sharedcomponents"), options.element.querySelector("form").addEventListener("submit", onSubmit.bind(self)), options.enableSaveButton && options.element.querySelector(".btnSave").classList.remove("hide"), self.loadData(options.autoFocus)
})
}
function DisplaySettings(options) {
this.options = options, embed(options, this)
}
return DisplaySettings.prototype.loadData = function(autoFocus) {
var self = this,
context = self.options.element;
loading.show();
var userId = self.options.userId,
apiClient = connectionManager.getApiClient(self.options.serverId),
userSettings = self.options.userSettings;
return apiClient.getUser(userId).then(function(user) {
return userSettings.setUserInfo(userId, apiClient).then(function() {
self.dataLoaded = !0, loadForm(context, user, userSettings, apiClient), autoFocus && focusManager.autoFocus(context)
})
})
}, DisplaySettings.prototype.submit = function() {
onSubmit.call(this)
}, DisplaySettings.prototype.destroy = function() {
this.options = null
}, DisplaySettings
});

View file

@ -0,0 +1,191 @@
<form style="margin: 0 auto;">
<h2 class="sectionTitle">
${Display}
</h2>
<div class="selectContainer languageSection hide">
<select id="selectLanguage" is="emby-select" label="${LabelDisplayLanguage}">
<option value="">${Auto}</option>
<option value="ar">Arabic</option>
<option value="be-BY">Belarusian (Belarus)</option>
<option value="bg-BG">Bulgarian (Bulgaria)</option>
<option value="ca">Catalan</option>
<option value="zh-CN">Chinese Simplified</option>
<option value="zh-TW">Chinese Traditional</option>
<option value="zh-HK">Chinese Traditional (Hong Kong)</option>
<option value="hr">Croatian</option>
<option value="cs">Czech</option>
<option value="da">Danish</option>
<option value="nl">Dutch</option>
<option value="en-GB">English (United Kingdom)</option>
<option value="en-US">English (United States)</option>
<option value="fi">Finnish</option>
<option value="fr">French</option>
<option value="fr-CA">French (Canada)</option>
<option value="de">German</option>
<option value="el">Greek</option>
<option value="he">Hebrew</option>
<option value="hi-IN">Hindi (India)</option>
<option value="hu">Hungarian</option>
<option value="id">Indonesian</option>
<option value="it">Italian</option>
<option value="ja">Japanese</option>
<option value="kk">Kazakh</option>
<option value="ko">Korean</option>
<option value="lt-LT">Lithuanian</option>
<option value="ms">Malay</option>
<option value="nb">Norwegian Bokmål</option>
<option value="fa">Persian</option>
<option value="pl">Polish</option>
<option value="pt-BR">Portuguese (Brazil)</option>
<option value="pt-PT">Portuguese (Portugal)</option>
<option value="ro">Romanian</option>
<option value="ru">Russian</option>
<option value="sk">Slovak</option>
<option value="sl-SI">Slovenian (Slovenia)</option>
<option value="es">Spanish</option>
<option value="es-419">Spanish (Latin America)</option>
<option value="es-MX">Spanish (Mexico)</option>
<option value="sv">Swedish</option>
<option value="gsw">Swiss German</option>
<option value="tr">Turkish</option>
<option value="uk">Ukrainian</option>
<option value="vi">Vietnamese</option>
</select>
<div class="fieldDescription">
<div>${LabelDisplayLanguageHelp}</div>
<div class="learnHowToContributeContainer hide" style="margin-top: .25em;">
<a is="emby-linkbutton" class="button-link" href="https://github.com/jellyfin/jellyfin" target="_blank">${LearnHowYouCanContribute}</a>
</div>
</div>
</div>
<div class="selectContainer fldDateTimeLocale hide">
<select is="emby-select" class="selectDateTimeLocale" label="${LabelDateTimeLocale}">
<option value="">${AutoBasedOnLanguageSetting}</option>
<option value="ar">Arabic</option>
<option value="be-BY">Belarusian (Belarus)</option>
<option value="bg-BG">Bulgarian (Bulgaria)</option>
<option value="ca">Catalan</option>
<option value="zh-CN">Chinese Simplified</option>
<option value="zh-TW">Chinese Traditional</option>
<option value="zh-HK">Chinese Traditional (Hong Kong)</option>
<option value="hr">Croatian</option>
<option value="cs">Czech</option>
<option value="da">Danish</option>
<option value="nl">Dutch</option>
<option value="en-GB">English (United Kingdom)</option>
<option value="en-US">English (United States)</option>
<option value="fi">Finnish</option>
<option value="fr">French</option>
<option value="fr-CA">French (Canada)</option>
<option value="de">German</option>
<option value="el">Greek</option>
<option value="he">Hebrew</option>
<option value="hi-IN">Hindi (India)</option>
<option value="hu">Hungarian</option>
<option value="id">Indonesian</option>
<option value="it">Italian</option>
<option value="ja">Japanese</option>
<option value="kk">Kazakh</option>
<option value="ko">Korean</option>
<option value="lt-LT">Lithuanian</option>
<option value="ms">Malay</option>
<option value="nb">Norwegian Bokmål</option>
<option value="fa">Persian</option>
<option value="pl">Polish</option>
<option value="pt-BR">Portuguese (Brazil)</option>
<option value="pt-PT">Portuguese (Portugal)</option>
<option value="ro">Romanian</option>
<option value="ru">Russian</option>
<option value="sk">Slovak</option>
<option value="sl-SI">Slovenian (Slovenia)</option>
<option value="es">Spanish</option>
<option value="es-419">Spanish (Latin America)</option>
<option value="es-MX">Spanish (Mexico)</option>
<option value="sv">Swedish</option>
<option value="gsw">Swiss German</option>
<option value="tr">Turkish</option>
<option value="uk">Ukrainian</option>
<option value="vi">Vietnamese</option>
</select>
</div>
<div class="selectContainer fldDisplayMode hide">
<select is="emby-select" class="selectLayout" label="${LabelDisplayMode}">
<option value="">${Auto}</option>
<option value="desktop">${Desktop}</option>
<option value="mobile">${Mobile}</option>
<option value="tv">${TV}</option>
</select>
<div class="fieldDescription">${DisplayModeHelp}</div>
</div>
<div class="selectContainer hide selectSkinContainer">
<select is="emby-select" class="selectSkin" label="${LabelSkin}"></select>
</div>
<div class="selectContainer">
<select id="selectTheme" is="emby-select" label="${LabelTheme}"></select>
</div>
<div class="checkboxContainer checkboxContainer-withDescription fldSeasonalThemes hide">
<label>
<input type="checkbox" is="emby-checkbox" id="chkSeasonalThemes" />
<span>${AllowSeasonalThemes}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${AllowSeasonalThemesHelp}</div>
</div>
<div class="selectContainer selectDashboardThemeContainer hide">
<select id="selectDashboardTheme" is="emby-select" label="${LabelDashboardTheme}"></select>
</div>
<div class="selectContainer hide selectScreensaverContainer">
<select is="emby-select" class="selectScreensaver" label="${LabelScreensaver}"></select>
</div>
<div class="selectContainer fldSoundEffects hide">
<select is="emby-select" class="selectSoundEffects" label="${LabelSoundEffects}"></select>
</div>
<div class="checkboxContainer checkboxContainer-withDescription fldBackdrops hide">
<label>
<input type="checkbox" is="emby-checkbox" id="chkBackdrops" />
<span>${EnableBackdrops}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${EnableBackdropsHelp}</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription fldThemeSong hide">
<label>
<input type="checkbox" is="emby-checkbox" id="chkThemeSong" />
<span>${EnableThemeSongs}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${EnableThemeSongsHelp}</div>
</div>
<div class="checkboxContainer checkboxContainer-withDescription fldThemeVideo hide">
<label>
<input type="checkbox" is="emby-checkbox" id="chkThemeVideo" />
<span>${EnableThemeVideos}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${EnableThemeVideosHelp}</div>
</div>
<div class="checkboxContainer hide fldAutorun">
<label>
<input type="checkbox" is="emby-checkbox" class="chkRunAtStartup" />
<span>${RunAtStartup}</span>
</label>
</div>
<div class="checkboxContainer checkboxContainer-withDescription fldDisplayMissingEpisodes hide">
<label>
<input type="checkbox" is="emby-checkbox" class="chkDisplayMissingEpisodes" />
<span>${DisplayMissingEpisodesWithinSeasons}</span>
</label>
<div class="fieldDescription checkboxFieldDescription">${DisplayMissingEpisodesWithinSeasonsHelp}</div>
</div>
<button is="emby-button" type="submit" class="raised button-submit block btnSave hide">
<span>${Save}</span>
</button>
</form>

View file

@ -0,0 +1,105 @@
define([], function() {
"use strict";
function parentWithAttribute(elem, name, value) {
for (; value ? elem.getAttribute(name) !== value : !elem.getAttribute(name);)
if (!(elem = elem.parentNode) || !elem.getAttribute) return null;
return elem
}
function parentWithTag(elem, tagNames) {
for (Array.isArray(tagNames) || (tagNames = [tagNames]); - 1 === tagNames.indexOf(elem.tagName || "");)
if (!(elem = elem.parentNode)) return null;
return elem
}
function containsAnyClass(classList, classNames) {
for (var i = 0, length = classNames.length; i < length; i++)
if (classList.contains(classNames[i])) return !0;
return !1
}
function parentWithClass(elem, classNames) {
for (Array.isArray(classNames) || (classNames = [classNames]); !elem.classList || !containsAnyClass(elem.classList, classNames);)
if (!(elem = elem.parentNode)) return null;
return elem
}
function addEventListenerWithOptions(target, type, handler, options) {
var optionsOrCapture = options;
supportsCaptureOption || (optionsOrCapture = options.capture), target.addEventListener(type, handler, optionsOrCapture)
}
function removeEventListenerWithOptions(target, type, handler, options) {
var optionsOrCapture = options;
supportsCaptureOption || (optionsOrCapture = options.capture), target.removeEventListener(type, handler, optionsOrCapture)
}
function clearWindowSize() {
windowSize = null
}
function getWindowSize() {
return windowSize || (windowSize = {
innerHeight: window.innerHeight,
innerWidth: window.innerWidth
}, windowSizeEventsBound || (windowSizeEventsBound = !0, addEventListenerWithOptions(window, "orientationchange", clearWindowSize, {
passive: !0
}), addEventListenerWithOptions(window, "resize", clearWindowSize, {
passive: !0
}))), windowSize
}
function whichAnimationEvent() {
if (_animationEvent) return _animationEvent;
var t, el = document.createElement("div"),
animations = {
animation: "animationend",
OAnimation: "oAnimationEnd",
MozAnimation: "animationend",
WebkitAnimation: "webkitAnimationEnd"
};
for (t in animations)
if (void 0 !== el.style[t]) return _animationEvent = animations[t], animations[t];
return _animationEvent = "animationend"
}
function whichAnimationCancelEvent() {
return whichAnimationEvent().replace("animationend", "animationcancel").replace("AnimationEnd", "AnimationCancel")
}
function whichTransitionEvent() {
if (_transitionEvent) return _transitionEvent;
var t, el = document.createElement("div"),
transitions = {
transition: "transitionend",
OTransition: "oTransitionEnd",
MozTransition: "transitionend",
WebkitTransition: "webkitTransitionEnd"
};
for (t in transitions)
if (void 0 !== el.style[t]) return _transitionEvent = transitions[t], transitions[t];
return _transitionEvent = "transitionend"
}
var supportsCaptureOption = !1;
try {
var opts = Object.defineProperty({}, "capture", {
get: function() {
supportsCaptureOption = !0
}
});
window.addEventListener("test", null, opts)
} catch (e) {}
var windowSize, windowSizeEventsBound, _animationEvent, _transitionEvent;
return {
parentWithAttribute: parentWithAttribute,
parentWithClass: parentWithClass,
parentWithTag: parentWithTag,
addEventListener: addEventListenerWithOptions,
removeEventListener: removeEventListenerWithOptions,
getWindowSize: getWindowSize,
whichTransitionEvent: whichTransitionEvent,
whichAnimationEvent: whichAnimationEvent,
whichAnimationCancelEvent: whichAnimationCancelEvent
}
});

View file

@ -0,0 +1,222 @@
.emby-button,
.fab {
-webkit-box-sizing: border-box;
-webkit-box-align: center
}
.button-flat,
.button-link {
background: 0 0
}
.emby-button,
.paper-icon-button-light {
text-align: center;
font-family: inherit;
color: inherit;
outline: 0 !important;
-webkit-tap-highlight-color: transparent;
position: relative
}
.emby-button {
display: -webkit-inline-box;
display: -webkit-inline-flex;
display: inline-flex;
-webkit-align-items: center;
align-items: center;
box-sizing: border-box;
margin: 0 .29em;
font-size: inherit;
-moz-user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
user-select: none;
cursor: pointer;
z-index: 0;
padding: .86em 1em;
border: 0;
vertical-align: middle;
-webkit-border-radius: .2em;
border-radius: .2em;
font-weight: 600;
text-decoration: none;
line-height: 1.35
}
.emby-button::-moz-focus-inner {
border: 0
}
.button-flat:hover {
opacity: .5
}
.button-link {
margin: 0;
padding: 0;
vertical-align: initial
}
.button-link-inline {
display: inline
}
.button-link:hover {
text-decoration: underline
}
.emby-button-focusscale {
-webkit-transition: -webkit-transform 180ms ease-out !important;
-o-transition: transform 180ms ease-out !important;
transition: transform 180ms ease-out !important;
-webkit-transform-origin: center center;
transform-origin: center center
}
.emby-button-focusscale:focus {
-webkit-transform: scale(1.16);
transform: scale(1.16);
z-index: 1
}
.emby-button>i {
font-size: 1.36em
}
.button-link>i {
font-size: 1em
}
.fab {
display: -webkit-inline-box;
display: -webkit-inline-flex;
display: inline-flex;
-webkit-border-radius: 50%;
border-radius: 50%;
padding: .6em;
box-sizing: border-box;
-webkit-align-items: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
justify-content: center;
text-align: center
}
.emby-button.block {
display: block;
-webkit-box-align: center;
-webkit-align-items: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
justify-content: center;
margin: .25em 0;
width: 100%
}
.paper-icon-button-light {
display: -webkit-inline-box;
display: -webkit-inline-flex;
display: inline-flex;
-webkit-box-align: center;
-webkit-align-items: center;
align-items: center;
-webkit-box-sizing: border-box;
box-sizing: border-box;
margin: 0 .29em;
background: 0 0;
font-size: inherit;
-moz-user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
user-select: none;
cursor: pointer;
z-index: 0;
min-width: initial;
min-height: initial;
width: auto;
height: auto;
padding: .556em;
border: 0;
vertical-align: middle;
overflow: hidden;
-webkit-border-radius: 50%;
border-radius: 50%;
-webkit-box-pack: center;
-webkit-justify-content: center;
justify-content: center
}
.paper-icon-button-light::-moz-focus-inner {
border: 0
}
.paper-icon-button-light[disabled] {
opacity: .3
}
.paper-icon-button-light>i {
font-size: 1.66956521739130434em;
position: relative;
z-index: 1;
vertical-align: middle
}
.paper-icon-button-light>img {
width: 1.72em;
max-height: 100%;
position: relative;
z-index: 1;
vertical-align: middle
}
.emby-button-foreground {
position: relative;
z-index: 1
}
.icon-button-focusscale {
-webkit-transition: -webkit-transform 180ms ease-out !important;
-o-transition: transform 180ms ease-out !important;
transition: transform 180ms ease-out !important;
-webkit-transform-origin: center center;
transform-origin: center center
}
.icon-button-focusscale:focus {
-webkit-transform: scale(1.3);
transform: scale(1.3);
z-index: 1
}
.btnFilterWithBubble {
position: relative
}
.filterButtonBubble {
color: #fff;
position: absolute;
top: 0;
right: 0;
width: 1.6em;
height: 1.6em;
z-index: 100000000;
display: -webkit-box;
display: -webkit-flex;
display: flex;
-webkit-box-align: center;
-webkit-align-items: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
justify-content: center;
font-size: 82%;
-webkit-border-radius: 100em;
border-radius: 100em;
-webkit-box-shadow: 0 4px 5px 0 rgba(0, 0, 0, .14), 0 1px 10px 0 rgba(0, 0, 0, .12), 0 2px 4px -1px rgba(0, 0, 0, .2);
box-shadow: 0 4px 5px 0 rgba(0, 0, 0, .14), 0 1px 10px 0 rgba(0, 0, 0, .12), 0 2px 4px -1px rgba(0, 0, 0, .2);
background: #03A9F4;
font-weight: 700
}

View file

@ -0,0 +1,29 @@
define(["browser", "dom", "layoutManager", "shell", "appRouter", "apphost", "css!./emby-button", "registerElement"], function(browser, dom, layoutManager, shell, appRouter, appHost) {
"use strict";
function openPremiumInfo() {
require(["registrationServices"], function(registrationServices) {
registrationServices.showPremiereInfo()
})
}
function onAnchorClick(e) {
var href = this.getAttribute("href") || "";
"#" !== href ? this.getAttribute("target") ? -1 === href.indexOf("emby.media/premiere") || appHost.supports("externalpremium") ? appHost.supports("targetblank") || (e.preventDefault(), shell.openUrl(href)) : (e.preventDefault(), openPremiumInfo()) : appRouter.handleAnchorClick(e) : e.preventDefault()
}
var EmbyButtonPrototype = Object.create(HTMLButtonElement.prototype),
EmbyLinkButtonPrototype = Object.create(HTMLAnchorElement.prototype);
return EmbyButtonPrototype.createdCallback = function() {
this.classList.contains("emby-button") || (this.classList.add("emby-button"), browser.firefox && this.classList.add("button-link-inline"), layoutManager.tv && ("false" !== this.getAttribute("data-focusscale") && this.classList.add("emby-button-focusscale"), this.classList.add("emby-button-tv")))
}, EmbyButtonPrototype.attachedCallback = function() {
"A" === this.tagName && (dom.removeEventListener(this, "click", onAnchorClick, {}), dom.addEventListener(this, "click", onAnchorClick, {}), "true" === this.getAttribute("data-autohide") && (appHost.supports("externallinks") ? this.classList.remove("hide") : this.classList.add("hide")))
}, EmbyButtonPrototype.detachedCallback = function() {
dom.removeEventListener(this, "click", onAnchorClick, {})
}, EmbyLinkButtonPrototype.createdCallback = EmbyButtonPrototype.createdCallback, EmbyLinkButtonPrototype.attachedCallback = EmbyButtonPrototype.attachedCallback, document.registerElement("emby-button", {
prototype: EmbyButtonPrototype,
extends: "button"
}), document.registerElement("emby-linkbutton", {
prototype: EmbyLinkButtonPrototype,
extends: "a"
}), EmbyButtonPrototype
});

View file

@ -0,0 +1,10 @@
define(["layoutManager", "css!./emby-button", "registerElement"], function(layoutManager) {
"use strict";
var EmbyButtonPrototype = Object.create(HTMLButtonElement.prototype);
EmbyButtonPrototype.createdCallback = function() {
this.classList.add("paper-icon-button-light"), layoutManager.tv && this.classList.add("icon-button-focusscale")
}, document.registerElement("paper-icon-button-light", {
prototype: EmbyButtonPrototype,
extends: "button"
})
});

View file

@ -0,0 +1,161 @@
.emby-checkbox-label {
position: relative;
z-index: 1;
vertical-align: middle;
display: -webkit-inline-box;
display: -webkit-inline-flex;
display: inline-flex;
-webkit-box-sizing: border-box;
box-sizing: border-box;
width: 100%;
margin: 0;
padding: 0 0 0 2.4em;
-webkit-box-align: center;
-webkit-align-items: center;
align-items: center;
height: 2.35em;
cursor: pointer
}
.checkboxContainer,
.checkboxListContainer {
margin-bottom: 1.8em
}
.checkboxFieldDescription {
padding-left: 2.4em
}
.checkboxContainer {
display: -webkit-box;
display: -webkit-flex;
display: flex
}
.checkboxContainer-withDescription {
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-webkit-flex-direction: column;
flex-direction: column
}
.emby-checkbox {
position: absolute;
width: 1px;
height: 1px;
margin: 0;
padding: 0;
opacity: 0;
-ms-appearance: none;
-moz-appearance: none;
-webkit-appearance: none;
appearance: none;
border: none
}
.checkboxOutline {
position: absolute;
top: 3px;
left: 0;
-webkit-box-sizing: border-box;
box-sizing: border-box;
width: 1.83em;
height: 1.83em;
margin: 0;
overflow: hidden;
border: 2px solid currentcolor;
-webkit-border-radius: .14em;
border-radius: .14em;
z-index: 2;
display: -webkit-box;
display: -webkit-flex;
display: flex;
-webkit-box-align: center;
-webkit-align-items: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
justify-content: center
}
.emby-checkbox-focushelper {
position: absolute;
top: -.915em;
left: -.915em;
width: 3.66em;
height: 3.66em;
display: inline-block;
-webkit-box-sizing: border-box;
box-sizing: border-box;
margin: 3px 0 0;
-webkit-border-radius: 50%;
border-radius: 50%;
background-color: transparent
}
.checkboxIcon {
font-size: 1.6em;
color: #fff
}
.checkboxIcon-checked {
display: none
}
.emby-checkbox:checked+span+span+.checkboxOutline>.checkboxIcon-checked {
display: -webkit-box !important;
display: -webkit-flex !important;
display: flex !important
}
.emby-checkbox:checked+span+span+.checkboxOutline>.checkboxIcon-unchecked {
display: none !important
}
.emby-checkbox:checked[disabled]+span+span+.checkboxOutline>.checkboxIcon {
background-color: rgba(0, 0, 0, .26)
}
.checkboxLabel {
position: relative;
margin: 0
}
.checkboxList>.emby-checkbox-label {
display: -webkit-box;
display: -webkit-flex;
display: flex;
margin: .5em 0
}
.checkboxList-verticalwrap {
display: -webkit-box;
display: -webkit-flex;
display: flex;
-webkit-flex-wrap: wrap;
flex-wrap: wrap
}
.checkboxList-verticalwrap>.emby-checkbox-label {
display: -webkit-inline-box;
display: -webkit-inline-flex;
display: inline-flex;
margin: .3em 0;
width: 12em
}
.checkboxList-paperList {
padding: 1em !important
}
.checkboxListLabel {
margin-bottom: .25em
}
@-webkit-keyframes repaintChrome {
from,
to {
padding: 0
}
}

View file

@ -0,0 +1,55 @@
define(["browser", "dom", "css!./emby-checkbox", "registerElement"], function(browser, dom) {
"use strict";
function onKeyDown(e) {
if (13 === e.keyCode) return e.preventDefault(), this.checked = !this.checked, this.dispatchEvent(new CustomEvent("change", {
bubbles: !0
})), !1
}
function forceRefresh(loading) {
var elem = this.parentNode;
elem.style.webkitAnimationName = "repaintChrome", elem.style.webkitAnimationDelay = !0 === loading ? "500ms" : "", elem.style.webkitAnimationDuration = "10ms", elem.style.webkitAnimationIterationCount = "1", setTimeout(function() {
elem.style.webkitAnimationName = ""
}, !0 === loading ? 520 : 20)
}
var EmbyCheckboxPrototype = Object.create(HTMLInputElement.prototype),
enableRefreshHack = !!(browser.tizen || browser.orsay || browser.operaTv || browser.web0s);
EmbyCheckboxPrototype.attachedCallback = function() {
if ("true" !== this.getAttribute("data-embycheckbox")) {
this.setAttribute("data-embycheckbox", "true"), this.classList.add("emby-checkbox");
var labelElement = this.parentNode;
labelElement.classList.add("emby-checkbox-label");
var labelTextElement = labelElement.querySelector("span"),
outlineClass = "checkboxOutline",
customClass = this.getAttribute("data-outlineclass");
customClass && (outlineClass += " " + customClass);
var checkedIcon = this.getAttribute("data-checkedicon") || "&#xE5CA;",
uncheckedIcon = this.getAttribute("data-uncheckedicon") || "",
checkHtml = '<i class="md-icon checkboxIcon checkboxIcon-checked">' + checkedIcon + "</i>",
uncheckedHtml = '<i class="md-icon checkboxIcon checkboxIcon-unchecked">' + uncheckedIcon + "</i>";
labelElement.insertAdjacentHTML("beforeend", '<span class="emby-checkbox-focushelper"></span><span class="' + outlineClass + '">' + checkHtml + uncheckedHtml + "</span>"), labelTextElement.classList.add("checkboxLabel"), this.addEventListener("keydown", onKeyDown), enableRefreshHack && (forceRefresh.call(this, !0), dom.addEventListener(this, "click", forceRefresh, {
passive: !0
}), dom.addEventListener(this, "blur", forceRefresh, {
passive: !0
}), dom.addEventListener(this, "focus", forceRefresh, {
passive: !0
}), dom.addEventListener(this, "change", forceRefresh, {
passive: !0
}))
}
}, EmbyCheckboxPrototype.detachedCallback = function() {
this.removeEventListener("keydown", onKeyDown), dom.removeEventListener(this, "click", forceRefresh, {
passive: !0
}), dom.removeEventListener(this, "blur", forceRefresh, {
passive: !0
}), dom.removeEventListener(this, "focus", forceRefresh, {
passive: !0
}), dom.removeEventListener(this, "change", forceRefresh, {
passive: !0
})
}, document.registerElement("emby-checkbox", {
prototype: EmbyCheckboxPrototype,
extends: "input"
})
});

View file

@ -0,0 +1,56 @@
.emby-collapse {
margin: .5em 0
}
.collapseContent {
border-width: 0;
padding: 1.25em;
height: 0;
-webkit-transition-property: height;
-o-transition-property: height;
transition-property: height;
-webkit-transition-duration: .3s;
-o-transition-duration: .3s;
transition-duration: .3s;
overflow: hidden
}
.emby-collapsible-button {
margin: 0;
display: -webkit-box;
display: -webkit-flex;
display: flex;
-webkit-box-align: center;
-webkit-align-items: center;
align-items: center;
width: 100%;
text-align: left;
text-transform: none;
border-width: 0 0 .1em;
border-style: solid;
padding-left: .1em;
background: 0 0;
-webkit-box-shadow: none;
box-shadow: none
}
.emby-collapse-expandIcon {
-webkit-transform-origin: 50% 50%;
transform-origin: 50% 50%;
-webkit-transition: -webkit-transform 180ms ease-out;
-o-transition: transform 180ms ease-out;
transition: transform 180ms ease-out;
position: absolute;
right: .5em;
font-size: 1.5em
}
.emby-collapse-expandIconExpanded {
-webkit-transform: rotate(180deg);
transform: rotate(180deg)
}
.emby-collapsible-title {
margin: 0;
padding: 0
}

View file

@ -0,0 +1,43 @@
define(["browser", "css!./emby-collapse", "registerElement", "emby-button"], function(browser) {
"use strict";
function slideDownToShow(button, elem) {
elem.classList.remove("hide"), elem.classList.add("expanded"), elem.style.height = "auto";
var height = elem.offsetHeight + "px";
elem.style.height = "0";
elem.offsetHeight;
elem.style.height = height, setTimeout(function() {
elem.classList.contains("expanded") ? elem.classList.remove("hide") : elem.classList.add("hide"), elem.style.height = "auto"
}, 300), button.querySelector("i").classList.add("emby-collapse-expandIconExpanded")
}
function slideUpToHide(button, elem) {
elem.style.height = elem.offsetHeight + "px";
elem.offsetHeight;
elem.classList.remove("expanded"), elem.style.height = "0", setTimeout(function() {
elem.classList.contains("expanded") ? elem.classList.remove("hide") : elem.classList.add("hide")
}, 300), button.querySelector("i").classList.remove("emby-collapse-expandIconExpanded")
}
function onButtonClick(e) {
var button = this,
collapseContent = button.parentNode.querySelector(".collapseContent");
collapseContent.expanded ? (collapseContent.expanded = !1, slideUpToHide(button, collapseContent)) : (collapseContent.expanded = !0, slideDownToShow(button, collapseContent))
}
var EmbyButtonPrototype = Object.create(HTMLDivElement.prototype);
EmbyButtonPrototype.attachedCallback = function() {
if (!this.classList.contains("emby-collapse")) {
this.classList.add("emby-collapse");
var collapseContent = this.querySelector(".collapseContent");
collapseContent && collapseContent.classList.add("hide");
var title = this.getAttribute("title"),
html = '<button is="emby-button" type="button" on-click="toggleExpand" id="expandButton" class="emby-collapsible-button iconRight"><h3 class="emby-collapsible-title" title="' + title + '">' + title + '</h3><i class="md-icon emby-collapse-expandIcon">expand_more</i></button>';
this.insertAdjacentHTML("afterbegin", html);
var button = this.querySelector(".emby-collapsible-button");
button.addEventListener("click", onButtonClick), "true" === this.getAttribute("data-expanded") && onButtonClick.call(button)
}
}, document.registerElement("emby-collapse", {
prototype: EmbyButtonPrototype,
extends: "div"
})
});

View file

@ -0,0 +1,106 @@
define(["globalize", "apphost", "loading", "alert", "emby-linkbutton"], function(globalize, appHost, loading, alert) {
"use strict";
function resolvePromise() {
return Promise.resolve()
}
function rejectPromise() {
return Promise.reject()
}
function showNewUserInviteMessage(result) {
if (!result.IsNewUserInvitation && !result.IsPending) return Promise.resolve();
var message = result.IsNewUserInvitation ? globalize.translate("sharedcomponents#MessageInvitationSentToNewUser", result.GuestDisplayName) : globalize.translate("sharedcomponents#MessageInvitationSentToUser", result.GuestDisplayName);
return alert({
text: message,
title: globalize.translate("sharedcomponents#HeaderInvitationSent")
}).then(resolvePromise, resolvePromise)
}
function inviteGuest(options) {
var apiClient = options.apiClient;
return loading.show(), apiClient.ajax({
type: "POST",
url: apiClient.getUrl("Connect/Invite"),
dataType: "json",
data: options.guestOptions || {}
}).then(function(result) {
return loading.hide(), showNewUserInviteMessage(result)
}, function(response) {
loading.hide();
var statusCode = response ? response.status : 0;
return 502 === statusCode ? showConnectServerUnreachableErrorMessage().then(rejectPromise, rejectPromise) : 404 === statusCode ? alert({
text: globalize.translate("sharedcomponents#GuestUserNotFound")
}).then(rejectPromise, rejectPromise) : (statusCode || 0) >= 500 ? alert({
text: globalize.translate("sharedcomponents#ErrorReachingEmbyConnect")
}).then(rejectPromise, rejectPromise) : showGuestGeneralErrorMessage().then(rejectPromise, rejectPromise)
})
}
function showGuestGeneralErrorMessage() {
var html;
appHost.supports("externallinks") && (html = globalize.translate("sharedcomponents#ErrorAddingGuestAccount1", '<a is="emby-linkbutton" class="button-link" href="https://github.com/jellyfin/jellyfin" target="_blank">https://github.com/jellyfin/jellyfin</a>'), html += "<br/><br/>" + globalize.translate("sharedcomponents#ErrorAddingGuestAccount2", "apps@emby.media"));
var text = globalize.translate("sharedcomponents#ErrorAddingGuestAccount1", "https://github.com/jellyfin/jellyfin");
return text += "\n\n" + globalize.translate("sharedcomponents#ErrorAddingGuestAccount2", "apps@emby.media"), alert({
text: text,
html: html
})
}
function showConnectServerUnreachableErrorMessage() {
var text = globalize.translate("sharedcomponents#ErrorConnectServerUnreachable", "https://connect.emby.media");
return alert({
text: text
})
}
function showLinkUserErrorMessage(username, statusCode) {
var html, text;
return 502 === statusCode ? showConnectServerUnreachableErrorMessage() : (username ? (appHost.supports("externallinks") && (html = globalize.translate("sharedcomponents#ErrorAddingEmbyConnectAccount1", '<a is="emby-linkbutton" class="button-link" href="https://github.com/jellyfin/jellyfin" target="_blank">https://github.com/jellyfin/jellyfin</a>'), html += "<br/><br/>" + globalize.translate("sharedcomponents#ErrorAddingEmbyConnectAccount2", "apps@emby.media")), text = globalize.translate("sharedcomponents#ErrorAddingEmbyConnectAccount1", "https://github.com/jellyfin/jellyfin"), text += "\n\n" + globalize.translate("sharedcomponents#ErrorAddingEmbyConnectAccount2", "apps@emby.media")) : html = text = globalize.translate("sharedcomponents#DefaultErrorMessage"), alert({
text: text,
html: html
}))
}
function updateUserLink(apiClient, user, newConnectUsername) {
var currentConnectUsername = user.ConnectUserName || "",
enteredConnectUsername = newConnectUsername,
linkUrl = apiClient.getUrl("Users/" + user.Id + "/Connect/Link");
return currentConnectUsername && !enteredConnectUsername ? apiClient.ajax({
type: "DELETE",
url: linkUrl
}).then(function() {
return alert({
text: globalize.translate("sharedcomponents#MessageEmbyAccontRemoved"),
title: globalize.translate("sharedcomponents#HeaderEmbyAccountRemoved")
}).catch(resolvePromise)
}, function(response) {
return 502 === (response ? response.status : 0) ? showConnectServerUnreachableErrorMessage().then(rejectPromise) : alert({
text: globalize.translate("sharedcomponents#ErrorRemovingEmbyConnectAccount")
}).then(rejectPromise)
}) : currentConnectUsername !== enteredConnectUsername ? apiClient.ajax({
type: "POST",
url: linkUrl,
data: {
ConnectUsername: enteredConnectUsername
},
dataType: "json"
}).then(function(result) {
var msgKey = result.IsPending ? "sharedcomponents#MessagePendingEmbyAccountAdded" : "sharedcomponents#MessageEmbyAccountAdded";
return alert({
text: globalize.translate(msgKey),
title: globalize.translate("sharedcomponents#HeaderEmbyAccountAdded")
}).catch(resolvePromise)
}, function(response) {
var statusCode = response ? response.status : 0;
return 502 === statusCode ? showConnectServerUnreachableErrorMessage().then(rejectPromise) : showLinkUserErrorMessage(".", statusCode).then(rejectPromise)
}) : Promise.reject()
}
return {
inviteGuest: inviteGuest,
updateUserLink: updateUserLink,
showLinkUserErrorMessage: showLinkUserErrorMessage,
showConnectServerUnreachableErrorMessage: showConnectServerUnreachableErrorMessage
}
});

View file

@ -0,0 +1,36 @@
.emby-input {
display: block;
margin: 0;
margin-bottom: 0 !important;
font-size: 110%;
font-family: inherit;
font-weight: inherit;
padding: .4em .25em;
-webkit-box-sizing: border-box;
box-sizing: border-box;
outline: 0 !important;
-webkit-tap-highlight-color: transparent;
width: 100%
}
.emby-input::-moz-focus-inner {
border: 0
}
.inputContainer {
margin-bottom: 1.8em
}
.inputLabel {
display: inline-block;
margin-bottom: .25em
}
.emby-input+.fieldDescription {
margin-top: .25em
}
.emby-input-iconbutton {
-webkit-align-self: flex-end;
align-self: flex-end
}

View file

@ -0,0 +1,56 @@
define(["layoutManager", "browser", "dom", "css!./emby-input", "registerElement"], function(layoutManager, browser, dom) {
"use strict";
function onChange() {
var label = this.labelElement;
if (this.value) label.classList.remove("inputLabel-float");
else {
supportsFloatingLabel && "date" !== this.type && "time" !== this.type && label.classList.add("inputLabel-float")
}
}
var EmbyInputPrototype = Object.create(HTMLInputElement.prototype),
inputId = 0,
supportsFloatingLabel = !1;
if (Object.getOwnPropertyDescriptor && Object.defineProperty) {
var descriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value");
if (descriptor && descriptor.configurable) {
var baseSetMethod = descriptor.set;
descriptor.set = function(value) {
baseSetMethod.call(this, value), this.dispatchEvent(new CustomEvent("valueset", {
bubbles: !1,
cancelable: !1
}))
}, Object.defineProperty(HTMLInputElement.prototype, "value", descriptor), supportsFloatingLabel = !0
}
}
EmbyInputPrototype.createdCallback = function() {
if (this.id || (this.id = "embyinput" + inputId, inputId++), !this.classList.contains("emby-input")) {
this.classList.add("emby-input");
var parentNode = this.parentNode,
document = this.ownerDocument,
label = document.createElement("label");
label.innerHTML = this.getAttribute("label") || "", label.classList.add("inputLabel"), label.classList.add("inputLabelUnfocused"), label.htmlFor = this.id, parentNode.insertBefore(label, this), this.labelElement = label, dom.addEventListener(this, "focus", function() {
onChange.call(this), document.attachIME && document.attachIME(this), label.classList.add("inputLabelFocused"), label.classList.remove("inputLabelUnfocused")
}, {
passive: !0
}), dom.addEventListener(this, "blur", function() {
onChange.call(this), label.classList.remove("inputLabelFocused"), label.classList.add("inputLabelUnfocused")
}, {
passive: !0
}), dom.addEventListener(this, "change", onChange, {
passive: !0
}), dom.addEventListener(this, "input", onChange, {
passive: !0
}), dom.addEventListener(this, "valueset", onChange, {
passive: !0
}), browser.orsay && this === document.activeElement && document.attachIME && document.attachIME(this)
}
}, EmbyInputPrototype.attachedCallback = function() {
this.labelElement.htmlFor = this.id, onChange.call(this)
}, EmbyInputPrototype.label = function(text) {
this.labelElement.innerHTML = text
}, document.registerElement("emby-input", {
prototype: EmbyInputPrototype,
extends: "input"
})
});

View file

@ -0,0 +1,32 @@
define(["emby-progressring", "dom", "serverNotifications", "events", "registerElement"], function(EmbyProgressRing, dom, serverNotifications, events) {
"use strict";
function addNotificationEvent(instance, name, handler) {
var localHandler = handler.bind(instance);
events.on(serverNotifications, name, localHandler), instance[name] = localHandler
}
function removeNotificationEvent(instance, name) {
var handler = instance[name];
handler && (events.off(serverNotifications, name, handler), instance[name] = null)
}
function onRefreshProgress(e, apiClient, info) {
var indicator = this;
if (indicator.itemId || (indicator.itemId = dom.parentWithAttribute(indicator, "data-id").getAttribute("data-id")), info.ItemId === indicator.itemId) {
var progress = parseFloat(info.Progress);
progress && progress < 100 ? this.classList.remove("hide") : this.classList.add("hide"), this.setProgress(progress)
}
}
var EmbyItemRefreshIndicatorPrototype = Object.create(EmbyProgressRing);
EmbyItemRefreshIndicatorPrototype.createdCallback = function() {
EmbyProgressRing.createdCallback && EmbyProgressRing.createdCallback.call(this), addNotificationEvent(this, "RefreshProgress", onRefreshProgress)
}, EmbyItemRefreshIndicatorPrototype.attachedCallback = function() {
EmbyProgressRing.attachedCallback && EmbyProgressRing.attachedCallback.call(this)
}, EmbyItemRefreshIndicatorPrototype.detachedCallback = function() {
EmbyProgressRing.detachedCallback && EmbyProgressRing.detachedCallback.call(this), removeNotificationEvent(this, "RefreshProgress"), this.itemId = null
}, document.registerElement("emby-itemrefreshindicator", {
prototype: EmbyItemRefreshIndicatorPrototype,
extends: "div"
})
});

View file

@ -0,0 +1,220 @@
define(["itemShortcuts", "inputManager", "connectionManager", "playbackManager", "imageLoader", "layoutManager", "browser", "dom", "loading", "focusManager", "serverNotifications", "events", "registerElement"], function(itemShortcuts, inputManager, connectionManager, playbackManager, imageLoader, layoutManager, browser, dom, loading, focusManager, serverNotifications, events) {
"use strict";
function onClick(e) {
var itemsContainer = this,
multiSelect = (e.target, itemsContainer.multiSelect);
multiSelect && !1 === multiSelect.onContainerClick.call(itemsContainer, e) || itemShortcuts.onClick.call(itemsContainer, e)
}
function disableEvent(e) {
return e.preventDefault(), e.stopPropagation(), !1
}
function onContextMenu(e) {
var target = e.target,
card = dom.parentWithAttribute(target, "data-id");
if (card && card.getAttribute("data-serverid")) return inputManager.trigger("menu", {
sourceElement: card
}), e.preventDefault(), e.stopPropagation(), !1
}
function getShortcutOptions() {
return {
click: !1
}
}
function onDrop(evt, itemsContainer) {
var el = evt.item,
newIndex = evt.newIndex,
itemId = el.getAttribute("data-playlistitemid"),
playlistId = el.getAttribute("data-playlistid");
if (!playlistId) {
var oldIndex = evt.oldIndex;
return void el.dispatchEvent(new CustomEvent("itemdrop", {
detail: {
oldIndex: oldIndex,
newIndex: newIndex,
playlistItemId: itemId
},
bubbles: !0,
cancelable: !1
}))
}
var serverId = el.getAttribute("data-serverid"),
apiClient = connectionManager.getApiClient(serverId);
loading.show(), apiClient.ajax({
url: apiClient.getUrl("Playlists/" + playlistId + "/Items/" + itemId + "/Move/" + newIndex),
type: "POST"
}).then(function() {
loading.hide()
}, function() {
loading.hide(), itemsContainer.refreshItems()
})
}
function onUserDataChanged(e, apiClient, userData) {
var itemsContainer = this;
require(["cardBuilder"], function(cardBuilder) {
cardBuilder.onUserDataChanged(userData, itemsContainer)
});
var eventsToMonitor = getEventsToMonitor(itemsContainer); - 1 !== eventsToMonitor.indexOf("markfavorite") ? itemsContainer.notifyRefreshNeeded() : -1 !== eventsToMonitor.indexOf("markplayed") && itemsContainer.notifyRefreshNeeded()
}
function getEventsToMonitor(itemsContainer) {
var monitor = itemsContainer.getAttribute("data-monitor");
return monitor ? monitor.split(",") : []
}
function onTimerCreated(e, apiClient, data) {
var itemsContainer = this;
if (-1 !== getEventsToMonitor(itemsContainer).indexOf("timers")) return void itemsContainer.notifyRefreshNeeded();
var programId = data.ProgramId,
newTimerId = data.Id;
require(["cardBuilder"], function(cardBuilder) {
cardBuilder.onTimerCreated(programId, newTimerId, itemsContainer)
})
}
function onSeriesTimerCreated(e, apiClient, data) {
var itemsContainer = this;
if (-1 !== getEventsToMonitor(itemsContainer).indexOf("seriestimers")) return void itemsContainer.notifyRefreshNeeded()
}
function onTimerCancelled(e, apiClient, data) {
var itemsContainer = this;
if (-1 !== getEventsToMonitor(itemsContainer).indexOf("timers")) return void itemsContainer.notifyRefreshNeeded();
var id = data.Id;
require(["cardBuilder"], function(cardBuilder) {
cardBuilder.onTimerCancelled(id, itemsContainer)
})
}
function onSeriesTimerCancelled(e, apiClient, data) {
var itemsContainer = this;
if (-1 !== getEventsToMonitor(itemsContainer).indexOf("seriestimers")) return void itemsContainer.notifyRefreshNeeded();
var id = data.Id;
require(["cardBuilder"], function(cardBuilder) {
cardBuilder.onSeriesTimerCancelled(id, itemsContainer)
})
}
function onLibraryChanged(e, apiClient, data) {
var itemsContainer = this,
eventsToMonitor = getEventsToMonitor(itemsContainer);
if (-1 === eventsToMonitor.indexOf("seriestimers") && -1 === eventsToMonitor.indexOf("timers")) {
var itemsAdded = data.ItemsAdded || [],
itemsRemoved = data.ItemsRemoved || [];
if (itemsAdded.length || itemsRemoved.length) {
var parentId = itemsContainer.getAttribute("data-parentid");
if (parentId) {
var foldersAddedTo = data.FoldersAddedTo || [],
foldersRemovedFrom = data.FoldersRemovedFrom || [],
collectionFolders = data.CollectionFolders || [];
if (-1 === foldersAddedTo.indexOf(parentId) && -1 === foldersRemovedFrom.indexOf(parentId) && -1 === collectionFolders.indexOf(parentId)) return
}
itemsContainer.notifyRefreshNeeded()
}
}
}
function onPlaybackStopped(e, stopInfo) {
var itemsContainer = this,
state = stopInfo.state,
eventsToMonitor = getEventsToMonitor(itemsContainer);
if (state.NowPlayingItem && "Video" === state.NowPlayingItem.MediaType) {
if (-1 !== eventsToMonitor.indexOf("videoplayback")) return void itemsContainer.notifyRefreshNeeded(!0)
} else if (state.NowPlayingItem && "Audio" === state.NowPlayingItem.MediaType && -1 !== eventsToMonitor.indexOf("audioplayback")) return void itemsContainer.notifyRefreshNeeded(!0)
}
function addNotificationEvent(instance, name, handler, owner) {
var localHandler = handler.bind(instance);
owner = owner || serverNotifications, events.on(owner, name, localHandler), instance["event_" + name] = localHandler
}
function removeNotificationEvent(instance, name, owner) {
var handler = instance["event_" + name];
handler && (owner = owner || serverNotifications, events.off(owner, name, handler), instance["event_" + name] = null)
}
function clearRefreshInterval(itemsContainer, isPausing) {
itemsContainer.refreshInterval && (clearInterval(itemsContainer.refreshInterval), itemsContainer.refreshInterval = null, isPausing || (itemsContainer.refreshIntervalEndTime = null))
}
function resetRefreshInterval(itemsContainer, intervalMs) {
clearRefreshInterval(itemsContainer), intervalMs || (intervalMs = parseInt(itemsContainer.getAttribute("data-refreshinterval") || "0")), intervalMs && (itemsContainer.refreshInterval = setInterval(itemsContainer.notifyRefreshNeeded.bind(itemsContainer), intervalMs), itemsContainer.refreshIntervalEndTime = (new Date).getTime() + intervalMs)
}
function onDataFetched(result) {
var items = result.Items || result,
parentContainer = this.parentContainer;
parentContainer && (items.length ? parentContainer.classList.remove("hide") : parentContainer.classList.add("hide"));
var focusId, hasActiveElement, activeElement = document.activeElement;
this.contains(activeElement) && (hasActiveElement = !0, focusId = activeElement.getAttribute("data-id")), this.innerHTML = this.getItemsHtml(items), imageLoader.lazyChildren(this), hasActiveElement && setFocus(this, focusId), resetRefreshInterval(this), this.afterRefresh && this.afterRefresh(result)
}
function setFocus(itemsContainer, focusId) {
if (focusId) {
var newElement = itemsContainer.querySelector('[data-id="' + focusId + '"]');
if (newElement) try {
return void focusManager.focus(newElement)
} catch (err) {}
}
focusManager.autoFocus(itemsContainer)
}
var ItemsContainerProtoType = Object.create(HTMLDivElement.prototype);
ItemsContainerProtoType.enableMultiSelect = function(enabled) {
var current = this.multiSelect;
if (!enabled) return void(current && (current.destroy(), this.multiSelect = null));
if (!current) {
var self = this;
require(["multiSelect"], function(MultiSelect) {
self.multiSelect = new MultiSelect({
container: self,
bindOnClick: !1
})
})
}
}, ItemsContainerProtoType.enableDragReordering = function(enabled) {
var current = this.sortable;
if (!enabled) return void(current && (current.destroy(), this.sortable = null));
if (!current) {
var self = this;
require(["sortable"], function(Sortable) {
self.sortable = new Sortable(self, {
draggable: ".listItem",
handle: ".listViewDragHandle",
onEnd: function(evt) {
return onDrop(evt, self)
}
})
})
}
}, ItemsContainerProtoType.createdCallback = function() {
this.classList.add("itemsContainer")
}, ItemsContainerProtoType.attachedCallback = function() {
this.addEventListener("click", onClick), browser.touch ? this.addEventListener("contextmenu", disableEvent) : "false" !== this.getAttribute("data-contextmenu") && this.addEventListener("contextmenu", onContextMenu), (layoutManager.desktop || layoutManager.mobile) && "false" !== this.getAttribute("data-multiselect") && this.enableMultiSelect(!0), layoutManager.tv && this.classList.add("itemsContainer-tv"), itemShortcuts.on(this, getShortcutOptions()), addNotificationEvent(this, "UserDataChanged", onUserDataChanged), addNotificationEvent(this, "TimerCreated", onTimerCreated), addNotificationEvent(this, "SeriesTimerCreated", onSeriesTimerCreated), addNotificationEvent(this, "TimerCancelled", onTimerCancelled), addNotificationEvent(this, "SeriesTimerCancelled", onSeriesTimerCancelled), addNotificationEvent(this, "LibraryChanged", onLibraryChanged), addNotificationEvent(this, "playbackstop", onPlaybackStopped, playbackManager), "true" === this.getAttribute("data-dragreorder") && this.enableDragReordering(!0)
}, ItemsContainerProtoType.detachedCallback = function() {
clearRefreshInterval(this), this.enableMultiSelect(!1), this.enableDragReordering(!1), this.removeEventListener("click", onClick), this.removeEventListener("contextmenu", onContextMenu), this.removeEventListener("contextmenu", disableEvent), itemShortcuts.off(this, getShortcutOptions()), removeNotificationEvent(this, "UserDataChanged"), removeNotificationEvent(this, "TimerCreated"), removeNotificationEvent(this, "SeriesTimerCreated"), removeNotificationEvent(this, "TimerCancelled"), removeNotificationEvent(this, "SeriesTimerCancelled"), removeNotificationEvent(this, "LibraryChanged"), removeNotificationEvent(this, "playbackstop", playbackManager), this.fetchData = null, this.getItemsHtml = null, this.parentContainer = null
}, ItemsContainerProtoType.pause = function() {
clearRefreshInterval(this, !0), this.paused = !0
}, ItemsContainerProtoType.resume = function(options) {
this.paused = !1;
var refreshIntervalEndTime = this.refreshIntervalEndTime;
if (refreshIntervalEndTime) {
var remainingMs = refreshIntervalEndTime - (new Date).getTime();
remainingMs > 0 && !this.needsRefresh ? resetRefreshInterval(this, remainingMs) : (this.needsRefresh = !0, this.refreshIntervalEndTime = null)
}
return this.needsRefresh || options && options.refresh ? this.refreshItems() : Promise.resolve()
}, ItemsContainerProtoType.refreshItems = function() {
return this.fetchData ? this.paused ? (this.needsRefresh = !0, Promise.resolve()) : (this.needsRefresh = !1, this.fetchData().then(onDataFetched.bind(this))) : Promise.resolve()
}, ItemsContainerProtoType.notifyRefreshNeeded = function(isInForeground) {
if (this.paused) return void(this.needsRefresh = !0);
var timeout = this.refreshTimeout;
timeout && clearTimeout(timeout), !0 === isInForeground ? this.refreshItems() : this.refreshTimeout = setTimeout(this.refreshItems.bind(this), 1e4)
}, document.registerElement("emby-itemscontainer", {
prototype: ItemsContainerProtoType,
extends: "div"
})
});

View file

@ -0,0 +1,157 @@
.progressring {
position: relative;
width: 2.6em;
height: 2.6em;
float: left;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-box-sizing: border-box;
box-sizing: border-box
}
.progressring-bg {
width: 100%;
height: 100%;
-webkit-border-radius: 50%;
border-radius: 50%;
border: .25em solid rgba(0, 0, 0, 1);
-webkit-box-sizing: border-box;
box-sizing: border-box;
background: rgba(0, 0, 0, .9);
display: -webkit-box;
display: -webkit-flex;
display: flex;
-webkit-box-align: center;
-webkit-align-items: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
justify-content: center
}
.spiner-holder-one,
.spiner-holder-two {
position: absolute;
top: 0;
left: 0;
overflow: hidden;
background: 0 0;
-webkit-box-sizing: border-box
}
.progressring-text {
text-align: center;
color: #ddd;
font-size: 90%
}
.spiner-holder-one {
width: 51%;
height: 51%;
box-sizing: border-box
}
.spiner-holder-two {
width: 100%;
height: 100%;
box-sizing: border-box
}
.progressring-spiner {
width: 200%;
height: 200%;
-webkit-border-radius: 50%;
border-radius: 50%;
border-width: .25em;
border-style: solid;
-webkit-box-sizing: border-box;
box-sizing: border-box
}
.animate-0-25-a {
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
-webkit-transform-origin: 100% 100%;
transform-origin: 100% 100%;
-webkit-transition: -webkit-transform 180ms ease-out;
-o-transition: transform 180ms ease-out;
transition: transform 180ms ease-out
}
.animate-0-25-b,
.animate-25-50-a {
-webkit-transition: -webkit-transform 180ms ease-out;
-o-transition: transform 180ms ease-out
}
.animate-0-25-b {
-webkit-transform: rotate(-90deg);
transform: rotate(-90deg);
-webkit-transform-origin: 100% 100%;
transform-origin: 100% 100%;
transition: transform 180ms ease-out
}
.animate-25-50-a {
-webkit-transform: rotate(180deg);
transform: rotate(180deg);
-webkit-transform-origin: 100% 100%;
transform-origin: 100% 100%;
transition: transform 180ms ease-out
}
.animate-25-50-b,
.animate-50-75-a {
-webkit-transition: -webkit-transform 180ms ease-out;
-o-transition: transform 180ms ease-out
}
.animate-25-50-b {
-webkit-transform: rotate(-90deg);
transform: rotate(-90deg);
-webkit-transform-origin: 100% 100%;
transform-origin: 100% 100%;
transition: transform 180ms ease-out
}
.animate-50-75-a {
-webkit-transform: rotate(270deg);
transform: rotate(270deg);
-webkit-transform-origin: 100% 100%;
transform-origin: 100% 100%;
transition: transform 180ms ease-out
}
.animate-50-75-b,
.animate-75-100-a {
-webkit-transition: -webkit-transform 180ms ease-out;
-o-transition: transform 180ms ease-out
}
.animate-50-75-b {
-webkit-transform: rotate(-90deg);
transform: rotate(-90deg);
-webkit-transform-origin: 100% 100%;
transform-origin: 100% 100%;
transition: transform 180ms ease-out
}
.animate-75-100-a {
-webkit-transform: rotate(0);
transform: rotate(0);
-webkit-transform-origin: 100% 100%;
transform-origin: 100% 100%;
transition: transform 180ms ease-out
}
.animate-75-100-b {
-webkit-transform: rotate(-90deg);
transform: rotate(-90deg);
-webkit-transform-origin: 100% 100%;
transform-origin: 100% 100%;
-webkit-transition: -webkit-transform 180ms ease-out;
-o-transition: transform 180ms ease-out;
transition: transform 180ms ease-out
}

View file

@ -0,0 +1,21 @@
define(["require", "css!./emby-progressring", "registerElement"], function(require) {
"use strict";
var EmbyProgressRing = Object.create(HTMLDivElement.prototype);
return EmbyProgressRing.createdCallback = function() {
this.classList.add("progressring");
var instance = this;
require(["text!./emby-progressring.template.html"], function(template) {
instance.innerHTML = template, instance.setProgress(parseFloat(instance.getAttribute("data-progress") || "0"))
})
}, EmbyProgressRing.setProgress = function(progress) {
progress = Math.floor(progress);
var angle;
progress < 25 ? (angle = progress / 100 * 360 - 90, this.querySelector(".animate-0-25-b").style.transform = "rotate(" + angle + "deg)", this.querySelector(".animate-25-50-b").style.transform = "rotate(-90deg)", this.querySelector(".animate-50-75-b").style.transform = "rotate(-90deg)", this.querySelector(".animate-75-100-b").style.transform = "rotate(-90deg)") : progress >= 25 && progress < 50 ? (angle = (progress - 25) / 100 * 360 - 90, this.querySelector(".animate-0-25-b").style.transform = "none", this.querySelector(".animate-25-50-b").style.transform = "rotate(" + angle + "deg)", this.querySelector(".animate-50-75-b").style.transform = "rotate(-90deg)", this.querySelector(".animate-75-100-b").style.transform = "rotate(-90deg)") : progress >= 50 && progress < 75 ? (angle = (progress - 50) / 100 * 360 - 90, this.querySelector(".animate-0-25-b").style.transform = "none", this.querySelector(".animate-25-50-b").style.transform = "none", this.querySelector(".animate-50-75-b").style.transform = "rotate(" + angle + "deg)", this.querySelector(".animate-75-100-b").style.transform = "rotate(-90deg)") : progress >= 75 && progress <= 100 && (angle = (progress - 75) / 100 * 360 - 90, this.querySelector(".animate-0-25-b").style.transform = "none", this.querySelector(".animate-25-50-b").style.transform = "none", this.querySelector(".animate-50-75-b").style.transform = "none", this.querySelector(".animate-75-100-b").style.transform = "rotate(" + angle + "deg)"), this.querySelector(".progressring-text").innerHTML = progress + "%"
}, EmbyProgressRing.attachedCallback = function() {}, EmbyProgressRing.detachedCallback = function() {
var observer = this.observer;
observer && (observer.disconnect(), this.observer = null)
}, document.registerElement("emby-progressring", {
prototype: EmbyProgressRing,
extends: "div"
}), EmbyProgressRing
});

View file

@ -0,0 +1,23 @@
<div class="progressring-bg">
<div class="progressring-text"></div>
</div>
<div class="spiner-holder-one animate-0-25-a">
<div class="spiner-holder-two animate-0-25-b">
<div class="progressring-spiner"></div>
</div>
</div>
<div class="spiner-holder-one animate-25-50-a">
<div class="spiner-holder-two animate-25-50-b">
<div class="progressring-spiner"></div>
</div>
</div>
<div class="spiner-holder-one animate-50-75-a">
<div class="spiner-holder-two animate-50-75-b">
<div class="progressring-spiner"></div>
</div>
</div>
<div class="spiner-holder-one animate-75-100-a">
<div class="spiner-holder-two animate-75-100-b">
<div class="progressring-spiner"></div>
</div>
</div>

View file

@ -0,0 +1,117 @@
.mdl-radio {
position: relative;
line-height: 24px;
display: inline-block;
-webkit-box-sizing: border-box;
box-sizing: border-box;
margin: 0;
padding-left: 24px
}
.radio-label-block {
display: -webkit-box;
display: -webkit-flex;
display: flex;
-webkit-box-align: center;
-webkit-align-items: center;
align-items: center;
margin-top: .5em;
margin-bottom: .5em
}
.mdl-radio__button {
line-height: 24px;
position: absolute;
width: 1px;
height: 1px;
margin: 0;
padding: 0;
opacity: 0;
-ms-appearance: none;
-moz-appearance: none;
-webkit-appearance: none;
appearance: none;
border: none
}
.mdl-radio__outer-circle {
position: absolute;
top: 4px;
left: 0;
display: inline-block;
-webkit-box-sizing: border-box;
box-sizing: border-box;
width: 16px;
height: 16px;
margin: 0;
cursor: pointer;
border: 2px solid currentcolor;
-webkit-border-radius: 50%;
border-radius: 50%;
z-index: 2
}
.mdl-radio__button:checked+.mdl-radio__label+.mdl-radio__outer-circle {
border: 2px solid #00a4dc
}
.mdl-radio__button:disabled+.mdl-radio__label+.mdl-radio__outer-circle {
border: 2px solid rgba(0, 0, 0, .26);
cursor: auto
}
.mdl-radio__inner-circle {
position: absolute;
z-index: 1;
margin: 0;
top: 8px;
left: 4px;
-webkit-box-sizing: border-box;
box-sizing: border-box;
width: 8px;
height: 8px;
cursor: pointer;
-webkit-transition-duration: .28s;
-o-transition-duration: .28s;
transition-duration: .28s;
-webkit-transition-timing-function: cubic-bezier(.4, 0, .2, 1);
-o-transition-timing-function: cubic-bezier(.4, 0, .2, 1);
transition-timing-function: cubic-bezier(.4, 0, .2, 1);
-o-transition-property: transform;
-webkit-transition-property: -webkit-transform, -webkit-transform;
transition-property: transform, -webkit-transform;
-webkit-transform: scale3d(0, 0, 0);
transform: scale3d(0, 0, 0);
-webkit-border-radius: 50%;
border-radius: 50%;
background: #00a4dc
}
.mdl-radio__button:checked+.mdl-radio__label+.mdl-radio__outer-circle+.mdl-radio__inner-circle {
-webkit-transform: scale3d(1, 1, 1);
transform: scale3d(1, 1, 1)
}
.mdl-radio__button:disabled+.mdl-radio__label+.mdl-radio__outer-circle+.mdl-radio__inner-circle {
background: rgba(0, 0, 0, .26);
cursor: auto
}
.mdl-radio__button:focus+.mdl-radio__label+.mdl-radio__outer-circle+.mdl-radio__inner-circle {
-webkit-box-shadow: 0 0 0 10px rgba(255, 255, 255, .76);
box-shadow: 0 0 0 10px rgba(255, 255, 255, .76)
}
.mdl-radio__button:checked:focus+.mdl-radio__label+.mdl-radio__outer-circle+.mdl-radio__inner-circle {
-webkit-box-shadow: 0 0 0 10px rgba(0,164,220, .26);
box-shadow: 0 0 0 10px rgba(0,164,220, .26)
}
.mdl-radio__label {
cursor: pointer
}
.mdl-radio__button:disabled+.mdl-radio__label {
color: rgba(0, 0, 0, .26);
cursor: auto
}

View file

@ -0,0 +1,20 @@
define(["css!./emby-radio", "registerElement"], function() {
"use strict";
function onKeyDown(e) {
if (13 === e.keyCode) return e.preventDefault(), this.checked = !0, !1
}
var EmbyRadioPrototype = Object.create(HTMLInputElement.prototype);
EmbyRadioPrototype.attachedCallback = function() {
if ("true" !== this.getAttribute("data-radio")) {
this.setAttribute("data-radio", "true"), this.classList.add("mdl-radio__button");
var labelElement = this.parentNode;
labelElement.classList.add("mdl-radio"), labelElement.classList.add("mdl-js-radio"), labelElement.classList.add("mdl-js-ripple-effect");
var labelTextElement = labelElement.querySelector("span");
labelTextElement.classList.add("radioButtonLabel"), labelTextElement.classList.add("mdl-radio__label"), labelElement.insertAdjacentHTML("beforeend", '<span class="mdl-radio__outer-circle"></span><span class="mdl-radio__inner-circle"></span>'), this.addEventListener("keydown", onKeyDown)
}
}, document.registerElement("emby-radio", {
prototype: EmbyRadioPrototype,
extends: "input"
})
});

View file

@ -0,0 +1,67 @@
.emby-scrollbuttons-scroller {
position: relative
}
.scrollbuttoncontainer {
position: absolute;
top: 0;
bottom: 0;
-webkit-box-align: center;
-webkit-align-items: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
justify-content: center;
z-index: 1;
font-size: 3em;
color: #fff;
display: none;
overflow: hidden
}
.scrollbuttoncontainer-left {
background: rgba(20, 20, 20, .5);
background: -webkit-linear-gradient(left, #000 0, rgba(0, 0, 0, 0) 100%);
background: -webkit-gradient(linear, left top, right top, from(#000), to(rgba(0, 0, 0, 0)));
background: -webkit-linear-gradient(left, #000, rgba(0, 0, 0, 0));
background: -o-linear-gradient(left, #000, rgba(0, 0, 0, 0));
background: linear-gradient(to right, #000, rgba(0, 0, 0, 0));
left: 0
}
.scrollbuttoncontainer-right {
background: rgba(20, 20, 20, .5);
background: -webkit-linear-gradient(right, #000 0, rgba(0, 0, 0, 0) 100%);
background: -webkit-gradient(linear, right top, left top, from(#000), to(rgba(0, 0, 0, 0)));
background: -webkit-linear-gradient(right, #000, rgba(0, 0, 0, 0));
background: -o-linear-gradient(right, #000, rgba(0, 0, 0, 0));
background: linear-gradient(to left, #000, rgba(0, 0, 0, 0));
right: 0
}
.emby-scrollbuttons-scroller:hover .scrollbuttoncontainer {
display: -webkit-box;
display: -webkit-flex;
display: flex
}
.emby-scrollbuttons-scrollbutton {
margin: 0 -.2em;
-webkit-transition: -webkit-transform 160ms ease-out;
-o-transition: transform 160ms ease-out;
transition: transform 160ms ease-out
}
.scrollbuttoncontainer:hover>.emby-scrollbuttons-scrollbutton {
-webkit-transform: scale(1.3, 1.3);
transform: scale(1.3, 1.3)
}
.emby-scrollbuttons-scrollbutton:after {
content: '';
display: none !important
}
.emby-scrollbuttons-scrollbutton:focus {
color: inherit !important
}

View file

@ -0,0 +1,75 @@
define(["layoutManager", "dom", "css!./emby-scrollbuttons", "registerElement", "paper-icon-button-light"], function(layoutManager, dom) {
"use strict";
function getScrollButtonContainerHtml(direction) {
var html = "";
html += '<div class="scrollbuttoncontainer scrollbuttoncontainer-' + direction + ("left" === direction ? " hide" : "") + '">';
var icon = "left" === direction ? "&#xE5CB;" : "&#xE5CC;";
return html += '<button type="button" is="paper-icon-button-light" data-ripple="false" data-direction="' + direction + '" class="emby-scrollbuttons-scrollbutton">', html += '<i class="md-icon">' + icon + "</i>", html += "</button>", html += "</div>"
}
function getScrollPosition(parent) {
return parent.getScrollPosition ? parent.getScrollPosition() : 0
}
function getScrollWidth(parent) {
return parent.getScrollSize ? parent.getScrollSize() : 0
}
function onScrolledToPosition(scrollButtons, pos, scrollWidth) {
pos > 0 ? scrollButtons.scrollButtonsLeft.classList.remove("hide") : scrollButtons.scrollButtonsLeft.classList.add("hide"), scrollWidth > 0 && (pos += scrollButtons.offsetWidth, pos >= scrollWidth ? scrollButtons.scrollButtonsRight.classList.add("hide") : scrollButtons.scrollButtonsRight.classList.remove("hide"))
}
function onScroll(e) {
var scrollButtons = this,
scroller = this.scroller;
onScrolledToPosition(scrollButtons, getScrollPosition(scroller), getScrollWidth(scroller))
}
function getStyleValue(style, name) {
var value = style.getPropertyValue(name);
return value && (value = value.replace("px", "")) ? (value = parseInt(value), isNaN(value) ? 0 : value) : 0
}
function getScrollSize(elem) {
var scrollSize = elem.offsetWidth,
style = window.getComputedStyle(elem, null),
paddingLeft = getStyleValue(style, "padding-left");
paddingLeft && (scrollSize -= paddingLeft);
var paddingRight = getStyleValue(style, "padding-right");
paddingRight && (scrollSize -= paddingRight);
var slider = elem.getScrollSlider();
return style = window.getComputedStyle(slider, null), paddingLeft = getStyleValue(style, "padding-left"), paddingLeft && (scrollSize -= paddingLeft), paddingRight = getStyleValue(style, "padding-right"), paddingRight && (scrollSize -= paddingRight), scrollSize
}
function onScrollButtonClick(e) {
var newPos, parent = dom.parentWithAttribute(this, "is", "emby-scroller"),
direction = this.getAttribute("data-direction"),
scrollSize = getScrollSize(parent),
pos = getScrollPosition(parent);
newPos = "left" === direction ? Math.max(0, pos - scrollSize) : pos + scrollSize, parent.scrollToPosition(newPos, !1)
}
var EmbyScrollButtonsPrototype = Object.create(HTMLDivElement.prototype);
EmbyScrollButtonsPrototype.createdCallback = function() {}, EmbyScrollButtonsPrototype.attachedCallback = function() {
var parent = dom.parentWithAttribute(this, "is", "emby-scroller");
this.scroller = parent, parent.classList.add("emby-scrollbuttons-scroller"), this.innerHTML = getScrollButtonContainerHtml("left") + getScrollButtonContainerHtml("right");
var scrollHandler = onScroll.bind(this);
this.scrollHandler = scrollHandler;
var buttons = this.querySelectorAll(".emby-scrollbuttons-scrollbutton");
buttons[0].addEventListener("click", onScrollButtonClick), buttons[1].addEventListener("click", onScrollButtonClick), buttons = this.querySelectorAll(".scrollbuttoncontainer"), this.scrollButtonsLeft = buttons[0], this.scrollButtonsRight = buttons[1], parent.addScrollEventListener(scrollHandler, {
capture: !1,
passive: !0
})
}, EmbyScrollButtonsPrototype.detachedCallback = function() {
var parent = this.scroller;
this.scroller = null;
var scrollHandler = this.scrollHandler;
parent && scrollHandler && parent.removeScrollEventListener(scrollHandler, {
capture: !1,
passive: !0
}), this.scrollHandler = null, this.scrollButtonsLeft = null, this.scrollButtonsRight = null
}, document.registerElement("emby-scrollbuttons", {
prototype: EmbyScrollButtonsPrototype,
extends: "div"
})
});

View file

@ -0,0 +1,101 @@
define(["scroller", "dom", "layoutManager", "inputManager", "focusManager", "browser", "registerElement"], function(scroller, dom, layoutManager, inputManager, focusManager, browser) {
"use strict";
function initCenterFocus(elem, scrollerInstance) {
dom.addEventListener(elem, "focus", function(e) {
var focused = focusManager.focusableParent(e.target);
focused && scrollerInstance.toCenter(focused)
}, {
capture: !0,
passive: !0
})
}
function onInputCommand(e) {
var cmd = e.detail.command;
"end" === cmd ? (focusManager.focusLast(this, "." + this.getAttribute("data-navcommands")), e.preventDefault(), e.stopPropagation()) : "pageup" === cmd ? (focusManager.moveFocus(e.target, this, "." + this.getAttribute("data-navcommands"), -12), e.preventDefault(), e.stopPropagation()) : "pagedown" === cmd && (focusManager.moveFocus(e.target, this, "." + this.getAttribute("data-navcommands"), 12), e.preventDefault(), e.stopPropagation())
}
function initHeadroom(elem) {
require(["headroom"], function(Headroom) {
var headroom = new Headroom([], {
scroller: elem
});
headroom.init(), headroom.add(document.querySelector(".skinHeader")), elem.headroom = headroom
})
}
function loadScrollButtons(scroller) {
require(["emby-scrollbuttons"], function() {
scroller.insertAdjacentHTML("beforeend", '<div is="emby-scrollbuttons"></div>')
})
}
var ScrollerProtoType = Object.create(HTMLDivElement.prototype);
ScrollerProtoType.createdCallback = function() {
this.classList.add("emby-scroller")
}, ScrollerProtoType.scrollToBeginning = function() {
this.scroller && this.scroller.slideTo(0, !0)
}, ScrollerProtoType.toStart = function(elem, immediate) {
this.scroller && this.scroller.toStart(elem, immediate)
}, ScrollerProtoType.toCenter = function(elem, immediate) {
this.scroller && this.scroller.toCenter(elem, immediate)
}, ScrollerProtoType.scrollToPosition = function(pos, immediate) {
this.scroller && this.scroller.slideTo(pos, immediate)
}, ScrollerProtoType.getScrollPosition = function() {
if (this.scroller) return this.scroller.getScrollPosition()
}, ScrollerProtoType.getScrollSize = function() {
if (this.scroller) return this.scroller.getScrollSize()
}, ScrollerProtoType.getScrollEventName = function() {
if (this.scroller) return this.scroller.getScrollEventName()
}, ScrollerProtoType.getScrollSlider = function() {
if (this.scroller) return this.scroller.getScrollSlider()
}, ScrollerProtoType.addScrollEventListener = function(fn, options) {
this.scroller && dom.addEventListener(this.scroller.getScrollFrame(), this.scroller.getScrollEventName(), fn, options)
}, ScrollerProtoType.removeScrollEventListener = function(fn, options) {
this.scroller && dom.removeEventListener(this.scroller.getScrollFrame(), this.scroller.getScrollEventName(), fn, options)
}, ScrollerProtoType.attachedCallback = function() {
this.getAttribute("data-navcommands") && inputManager.on(this, onInputCommand);
var horizontal = "false" !== this.getAttribute("data-horizontal"),
slider = this.querySelector(".scrollSlider");
horizontal && (slider.style["white-space"] = "nowrap");
var bindHeader = "true" === this.getAttribute("data-bindheader"),
scrollFrame = this,
enableScrollButtons = layoutManager.desktop && horizontal && "false" !== this.getAttribute("data-scrollbuttons"),
options = {
horizontal: horizontal,
mouseDragging: 1,
mouseWheel: "false" !== this.getAttribute("data-mousewheel"),
touchDragging: 1,
slidee: slider,
scrollBy: 200,
speed: horizontal ? 270 : 240,
elasticBounds: 1,
dragHandle: 1,
scrollWidth: "auto" === this.getAttribute("data-scrollsize") ? null : 5e6,
autoImmediate: !0,
skipSlideToWhenVisible: "true" === this.getAttribute("data-skipfocuswhenvisible"),
dispatchScrollEvent: enableScrollButtons || bindHeader || "true" === this.getAttribute("data-scrollevent"),
hideScrollbar: enableScrollButtons || "true" === this.getAttribute("data-hidescrollbar"),
allowNativeSmoothScroll: "true" === this.getAttribute("data-allownativesmoothscroll") && !enableScrollButtons,
allowNativeScroll: !enableScrollButtons,
forceHideScrollbars: enableScrollButtons,
requireAnimation: enableScrollButtons && browser.edge
};
this.scroller = new scroller(scrollFrame, options), this.scroller.init(), layoutManager.tv && this.getAttribute("data-centerfocus") && initCenterFocus(this, this.scroller), bindHeader && initHeadroom(this), enableScrollButtons && loadScrollButtons(this)
}, ScrollerProtoType.pause = function() {
var headroom = this.headroom;
headroom && headroom.pause()
}, ScrollerProtoType.resume = function() {
var headroom = this.headroom;
headroom && headroom.resume()
}, ScrollerProtoType.detachedCallback = function() {
this.getAttribute("data-navcommands") && inputManager.off(this, onInputCommand);
var headroom = this.headroom;
headroom && (headroom.destroy(), this.headroom = null);
var scrollerInstance = this.scroller;
scrollerInstance && (scrollerInstance.destroy(), this.scroller = null)
}, document.registerElement("emby-scroller", {
prototype: ScrollerProtoType,
extends: "div"
})
});

View file

@ -0,0 +1,118 @@
.emby-select {
display: block;
margin: 0;
margin-bottom: 0 !important;
font-size: 110%;
font-family: inherit;
font-weight: inherit;
padding: .5em 1.9em .5em .5em;
-webkit-box-sizing: border-box;
box-sizing: border-box;
outline: 0 !important;
-webkit-tap-highlight-color: transparent;
width: 100%
}
.emby-select[disabled] {
background: 0 0 !important;
border-color: transparent !important;
color: inherit !important;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none
}
.selectContainer-inline>.emby-select {
padding: .3em 1.9em .3em .5em;
font-size: inherit
}
.selectContainer-inline>.emby-select[disabled] {
padding-left: 0;
padding-right: 0
}
.emby-select::-moz-focus-inner {
border: 0
}
.emby-select-focusscale {
-webkit-transition: -webkit-transform 180ms ease-out !important;
-o-transition: transform 180ms ease-out !important;
transition: transform 180ms ease-out !important;
-webkit-transform-origin: center center;
transform-origin: center center
}
.emby-select-focusscale:focus {
-webkit-transform: scale(1.04);
transform: scale(1.04);
z-index: 1
}
.emby-select+.fieldDescription {
margin-top: .25em
}
.selectContainer {
margin-bottom: 1.8em;
position: relative
}
.selectContainer-inline {
display: -webkit-inline-box;
display: -webkit-inline-flex;
display: inline-flex;
margin-bottom: 0;
-webkit-box-align: center;
-webkit-align-items: center;
align-items: center
}
.selectLabel {
display: block;
margin-bottom: .25em
}
.selectContainer-inline>.selectLabel {
margin-bottom: 0;
margin-right: .5em;
-webkit-flex-shrink: 0;
flex-shrink: 0
}
.emby-select-withcolor {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
-webkit-border-radius: .2em;
border-radius: .2em
}
.selectArrowContainer {
position: absolute;
right: .3em;
top: .2em;
color: inherit;
pointer-events: none
}
.selectContainer-inline>.selectArrowContainer {
top: initial;
bottom: .24em;
font-size: 90%
}
.emby-select[disabled]+.selectArrowContainer {
display: none
}
.selectArrow {
margin-top: .35em;
font-size: 1.7em
}
.emby-select-iconbutton {
-webkit-align-self: flex-end;
align-self: flex-end
}

View file

@ -0,0 +1,75 @@
define(["layoutManager", "browser", "actionsheet", "css!./emby-select", "registerElement"], function(layoutManager, browser, actionsheet) {
"use strict";
function enableNativeMenu() {
return !(!browser.edgeUwp && !browser.xboxOne) || !(browser.tizen || browser.orsay || browser.web0s) && (!!browser.tv || !layoutManager.tv)
}
function triggerChange(select) {
var evt = document.createEvent("HTMLEvents");
evt.initEvent("change", !1, !0), select.dispatchEvent(evt)
}
function setValue(select, value) {
select.value = value
}
function showActionSheet(select) {
var labelElem = getLabel(select),
title = labelElem ? labelElem.textContent || labelElem.innerText : null;
actionsheet.show({
items: select.options,
positionTo: select,
title: title
}).then(function(value) {
setValue(select, value), triggerChange(select)
})
}
function getLabel(select) {
for (var elem = select.previousSibling; elem && "LABEL" !== elem.tagName;) elem = elem.previousSibling;
return elem
}
function onFocus(e) {
var label = getLabel(this);
label && label.classList.add("selectLabelFocused")
}
function onBlur(e) {
var label = getLabel(this);
label && label.classList.remove("selectLabelFocused")
}
function onMouseDown(e) {
e.button || enableNativeMenu() || (e.preventDefault(), showActionSheet(this))
}
function onKeyDown(e) {
switch (e.keyCode) {
case 13:
return void(enableNativeMenu() || (e.preventDefault(), showActionSheet(this)));
case 37:
case 38:
case 39:
case 40:
return void(layoutManager.tv && e.preventDefault())
}
}
var EmbySelectPrototype = Object.create(HTMLSelectElement.prototype),
inputId = 0;
EmbySelectPrototype.createdCallback = function() {
this.id || (this.id = "embyselect" + inputId, inputId++), browser.firefox || (this.classList.add("emby-select-withcolor"), layoutManager.tv && this.classList.add("emby-select-tv-withcolor")), layoutManager.tv && this.classList.add("emby-select-focusscale"), this.addEventListener("mousedown", onMouseDown), this.addEventListener("keydown", onKeyDown), this.addEventListener("focus", onFocus), this.addEventListener("blur", onBlur)
}, EmbySelectPrototype.attachedCallback = function() {
if (!this.classList.contains("emby-select")) {
this.classList.add("emby-select");
var label = this.ownerDocument.createElement("label");
label.innerHTML = this.getAttribute("label") || "", label.classList.add("selectLabel"), label.htmlFor = this.id, this.parentNode.insertBefore(label, this), this.classList.contains("emby-select-withcolor") && this.parentNode.insertAdjacentHTML("beforeend", '<div class="selectArrowContainer"><div style="visibility:hidden;">0</div><i class="selectArrow md-icon">&#xE313;</i></div>')
}
}, EmbySelectPrototype.setLabel = function(text) {
this.parentNode.querySelector("label").innerHTML = text
}, document.registerElement("emby-select", {
prototype: EmbySelectPrototype,
extends: "select"
})
});

View file

@ -0,0 +1,234 @@
_:-ms-input-placeholder {
-ms-appearance: none;
height: 2.223em;
margin: 0
}
.mdl-slider {
width: 100%;
-webkit-appearance: none;
-moz-appearance: none;
-ms-appearance: none;
appearance: none;
height: .2em;
background: 0 0;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
outline: 0;
padding: 1em 0;
color: #00a4dc;
-webkit-align-self: center;
align-self: center;
z-index: 1;
cursor: pointer;
margin: 0;
-webkit-tap-highlight-color: transparent;
display: block
}
.mdl-slider::-moz-focus-outer {
border: 0
}
.mdl-slider::-ms-tooltip {
display: none
}
.mdl-slider::-webkit-slider-runnable-track {
background: 0 0
}
.mdl-slider::-moz-range-track {
background: #444;
border: none
}
.mdl-slider::-moz-range-progress {
background: #00a4dc
}
.mdl-slider::-ms-track {
background: 0 0;
color: transparent;
height: .2em;
width: 100%;
border: none
}
.mdl-slider::-ms-fill-lower {
display: none
}
.mdl-slider::-ms-fill-upper {
display: none
}
.mdl-slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 1.8em;
height: 1.8em;
-webkit-box-sizing: border-box;
box-sizing: border-box;
-webkit-border-radius: 50%;
border-radius: 50%;
background: #00a4dc;
border: none;
-webkit-transition: -webkit-transform .3s cubic-bezier(.4, 0, .2, 1), border .18s cubic-bezier(.4, 0, .2, 1), -webkit-box-shadow .18s cubic-bezier(.4, 0, .2, 1), background .28s cubic-bezier(.4, 0, .2, 1);
transition: transform .3s cubic-bezier(.4, 0, .2, 1), border .18s cubic-bezier(.4, 0, .2, 1), box-shadow .18s cubic-bezier(.4, 0, .2, 1), background .28s cubic-bezier(.4, 0, .2, 1)
}
.mdl-slider-hoverthumb::-webkit-slider-thumb {
margin-left: -.12em;
-webkit-transform: scale(.7, .7);
transform: scale(.7, .7)
}
.mdl-slider:hover::-webkit-slider-thumb {
-webkit-transform: none;
transform: none
}
.slider-no-webkit-thumb::-webkit-slider-thumb {
opacity: 0 !important
}
.mdl-slider::-moz-range-thumb {
-moz-appearance: none;
width: 1.8em;
height: 1.8em;
box-sizing: border-box;
border-radius: 50%;
background: #00a4dc;
border: none
}
.mdl-slider::-ms-thumb {
-webkit-appearance: none;
width: 1.8em;
height: 1.8em;
box-sizing: border-box;
border-radius: 50%;
background: #00a4dc;
border: none;
transition: transform .3s cubic-bezier(.4, 0, .2, 1), border .18s cubic-bezier(.4, 0, .2, 1), box-shadow .18s cubic-bezier(.4, 0, .2, 1), background .28s cubic-bezier(.4, 0, .2, 1)
}
.mdl-slider-hoverthumb::-ms-thumb {
margin-left: -.4em;
transform: scale(.5, .5)
}
.mdl-slider:hover::-ms-thumb {
transform: none
}
.mdl-slider[disabled]::-webkit-slider-thumb {
display: none
}
.mdl-slider[disabled]::-moz-range-thumb {
display: none
}
.mdl-slider[disabled]::-ms-thumb {
display: none
}
.mdl-slider-ie-container {
height: 1.25em;
overflow: visible;
border: none;
margin: 0;
padding: 0
}
.mdl-slider-container {
height: 1.25em;
position: relative;
background: 0 0;
display: -webkit-box;
display: -webkit-flex;
display: flex;
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
-webkit-flex-direction: row;
flex-direction: row
}
.mdl-slider-background-flex {
background: #333;
position: absolute;
height: .2em;
margin-top: -.1em;
width: 100%;
top: 50%;
left: 0;
display: -webkit-box;
display: -webkit-flex;
display: flex;
overflow: hidden;
border: 0;
padding: 0
}
.mdl-slider-background-flex-inner {
position: relative;
width: 100%
}
.mdl-slider-background-lower {
position: absolute;
left: 0;
width: 0;
top: 0;
bottom: 0;
background-color: #00a4dc
}
.mdl-slider-background-lower-clear {
background-color: transparent
}
.mdl-slider-background-lower-withtransform {
width: 100%;
-webkit-transform-origin: left center;
transform-origin: left center;
-webkit-transform: scaleX(0);
transform: scaleX(0)
}
.mdl-slider-background-upper {
background: #666;
background: rgba(255, 255, 255, .4);
position: absolute;
left: 0;
width: 0;
top: 0;
bottom: 0
}
.sliderBubble {
position: absolute;
top: 0;
left: 0;
-webkit-transform: translate3d(-48%, -120%, 0);
transform: translate3d(-48%, -120%, 0);
background: #282828;
color: #fff;
display: -webkit-box;
display: -webkit-flex;
display: flex;
-webkit-box-align: center;
-webkit-align-items: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
justify-content: center
}
.sliderBubbleText {
margin: 0;
padding: .5em .75em
}

View file

@ -0,0 +1,101 @@
define(["browser", "dom", "layoutManager", "css!./emby-slider", "registerElement", "emby-input"], function(browser, dom, layoutManager) {
"use strict";
function updateValues() {
var range = this,
value = range.value;
requestAnimationFrame(function() {
var backgroundLower = range.backgroundLower;
if (backgroundLower) {
var fraction = (value - range.min) / (range.max - range.min);
enableWidthWithTransform ? backgroundLower.style.transform = "scaleX(" + fraction + ")" : (fraction *= 100, backgroundLower.style.width = fraction + "%")
}
})
}
function updateBubble(range, value, bubble, bubbleText) {
requestAnimationFrame(function() {
bubble.style.left = value + "%", range.getBubbleHtml ? value = range.getBubbleHtml(value) : (value = range.getBubbleText ? range.getBubbleText(value) : Math.round(value), value = '<h1 class="sliderBubbleText">' + value + "</h1>"), bubble.innerHTML = value
})
}
function setRange(elem, startPercent, endPercent) {
var style = elem.style;
style.left = Math.max(startPercent, 0) + "%";
var widthPercent = endPercent - startPercent;
style.width = Math.max(Math.min(widthPercent, 100), 0) + "%"
}
function mapRangesFromRuntimeToPercent(ranges, runtime) {
return runtime ? ranges.map(function(r) {
return {
start: r.start / runtime * 100,
end: r.end / runtime * 100
}
}) : []
}
function startInterval(range) {
var interval = range.interval;
interval && clearInterval(interval), range.interval = setInterval(updateValues.bind(range), 100)
}
var enableWidthWithTransform, EmbySliderPrototype = Object.create(HTMLInputElement.prototype),
supportsNativeProgressStyle = browser.firefox,
supportsValueSetOverride = !1;
if (Object.getOwnPropertyDescriptor && Object.defineProperty) {
var descriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value");
descriptor && descriptor.configurable && (supportsValueSetOverride = !0)
}
EmbySliderPrototype.attachedCallback = function() {
if ("true" !== this.getAttribute("data-embyslider")) {
this.setAttribute("data-embyslider", "true"), this.classList.add("mdl-slider"), this.classList.add("mdl-js-slider"), browser.noFlex && this.classList.add("slider-no-webkit-thumb"), layoutManager.mobile || this.classList.add("mdl-slider-hoverthumb");
var containerElement = this.parentNode;
containerElement.classList.add("mdl-slider-container");
var htmlToInsert = "";
supportsNativeProgressStyle || (htmlToInsert += '<div class="mdl-slider-background-flex">', htmlToInsert += '<div class="mdl-slider-background-flex-inner">', htmlToInsert += '<div class="mdl-slider-background-upper"></div>', htmlToInsert += enableWidthWithTransform ? '<div class="mdl-slider-background-lower mdl-slider-background-lower-withtransform"></div>' : '<div class="mdl-slider-background-lower"></div>', htmlToInsert += "</div>", htmlToInsert += "</div>"), htmlToInsert += '<div class="sliderBubble hide"></div>', containerElement.insertAdjacentHTML("beforeend", htmlToInsert), this.backgroundLower = containerElement.querySelector(".mdl-slider-background-lower"), this.backgroundUpper = containerElement.querySelector(".mdl-slider-background-upper");
var sliderBubble = containerElement.querySelector(".sliderBubble"),
hasHideClass = sliderBubble.classList.contains("hide");
dom.addEventListener(this, "input", function(e) {
this.dragging = !0, updateBubble(this, this.value, sliderBubble), hasHideClass && (sliderBubble.classList.remove("hide"), hasHideClass = !1)
}, {
passive: !0
}), dom.addEventListener(this, "change", function() {
this.dragging = !1, updateValues.call(this), sliderBubble.classList.add("hide"), hasHideClass = !0
}, {
passive: !0
}), browser.firefox || (dom.addEventListener(this, window.PointerEvent ? "pointermove" : "mousemove", function(e) {
if (!this.dragging) {
var rect = this.getBoundingClientRect(),
clientX = e.clientX,
bubbleValue = (clientX - rect.left) / rect.width;
bubbleValue *= 100, updateBubble(this, bubbleValue, sliderBubble), hasHideClass && (sliderBubble.classList.remove("hide"), hasHideClass = !1)
}
}, {
passive: !0
}), dom.addEventListener(this, window.PointerEvent ? "pointerleave" : "mouseleave", function() {
sliderBubble.classList.add("hide"), hasHideClass = !0
}, {
passive: !0
})), supportsNativeProgressStyle || (supportsValueSetOverride ? this.addEventListener("valueset", updateValues) : startInterval(this))
}
}, EmbySliderPrototype.setBufferedRanges = function(ranges, runtime, position) {
var elem = this.backgroundUpper;
if (elem) {
null != runtime && (ranges = mapRangesFromRuntimeToPercent(ranges, runtime), position = position / runtime * 100);
for (var i = 0, length = ranges.length; i < length; i++) {
var range = ranges[i];
if (!(null != position && position >= range.end)) return void setRange(elem, range.start, range.end)
}
setRange(elem, 0, 0)
}
}, EmbySliderPrototype.setIsClear = function(isClear) {
var backgroundLower = this.backgroundLower;
backgroundLower && (isClear ? backgroundLower.classList.add("mdl-slider-background-lower-clear") : backgroundLower.classList.remove("mdl-slider-background-lower-clear"))
}, EmbySliderPrototype.detachedCallback = function() {
var interval = this.interval;
interval && clearInterval(interval), this.interval = null, this.backgroundUpper = null, this.backgroundLower = null
}, document.registerElement("emby-slider", {
prototype: EmbySliderPrototype,
extends: "input"
})
});

View file

@ -0,0 +1,43 @@
.emby-tab-button,
.emby-tabs-slider {
position: relative
}
.emby-tab-button {
background: 0 0;
-webkit-box-shadow: none;
box-shadow: none;
cursor: pointer;
outline: 0 !important;
width: auto;
font-family: inherit;
font-size: inherit;
display: inline-block;
vertical-align: middle;
-webkit-flex-shrink: 0;
flex-shrink: 0;
margin: 0;
padding: 1em .9em;
height: auto;
min-width: initial;
line-height: initial;
-webkit-border-radius: 0 !important;
border-radius: 0 !important;
overflow: hidden;
font-weight: 600
}
.emby-tab-button.emby-button-tv:focus {
-webkit-transform: scale(1.32);
transform: scale(1.32);
-webkit-transform-origin: center center;
transform-origin: center center
}
.emby-tab-button-ripple-effect {
background: rgba(0, 0, 0, .7) !important
}
.tabContent:not(.is-active) {
display: none
}

View file

@ -0,0 +1,191 @@
define(["dom", "scroller", "browser", "layoutManager", "focusManager", "registerElement", "css!./emby-tabs", "scrollStyles"], function(dom, scroller, browser, layoutManager, focusManager) {
"use strict";
function setActiveTabButton(tabs, newButton, oldButton, animate) {
newButton.classList.add(activeButtonClass)
}
function getFocusCallback(tabs, e) {
return function() {
onClick.call(tabs, e)
}
}
function onFocus(e) {
layoutManager.tv && (this.focusTimeout && clearTimeout(this.focusTimeout), this.focusTimeout = setTimeout(getFocusCallback(this, e), 700))
}
function getTabPanel(tabs, index) {
return null
}
function removeActivePanelClass(tabs, index) {
var tabPanel = getTabPanel(tabs, index);
tabPanel && tabPanel.classList.remove("is-active")
}
function fadeInRight(elem) {
var pct = browser.mobile ? "4%" : "0.5%",
keyframes = [{
opacity: "0",
transform: "translate3d(" + pct + ", 0, 0)",
offset: 0
}, {
opacity: "1",
transform: "none",
offset: 1
}];
elem.animate(keyframes, {
duration: 160,
iterations: 1,
easing: "ease-out"
})
}
function triggerBeforeTabChange(tabs, index, previousIndex) {
tabs.dispatchEvent(new CustomEvent("beforetabchange", {
detail: {
selectedTabIndex: index,
previousIndex: previousIndex
}
})), null != previousIndex && previousIndex !== index && removeActivePanelClass(tabs, previousIndex);
var newPanel = getTabPanel(tabs, index);
newPanel && (newPanel.animate && fadeInRight(newPanel), newPanel.classList.add("is-active"))
}
function onClick(e) {
this.focusTimeout && clearTimeout(this.focusTimeout);
var tabs = this,
current = tabs.querySelector("." + activeButtonClass),
tabButton = dom.parentWithClass(e.target, buttonClass);
if (tabButton && tabButton !== current) {
current && current.classList.remove(activeButtonClass);
var previousIndex = current ? parseInt(current.getAttribute("data-index")) : null;
setActiveTabButton(tabs, tabButton, current, !0);
var index = parseInt(tabButton.getAttribute("data-index"));
triggerBeforeTabChange(tabs, index, previousIndex), setTimeout(function() {
tabs.selectedTabIndex = index, tabs.dispatchEvent(new CustomEvent("tabchange", {
detail: {
selectedTabIndex: index,
previousIndex: previousIndex
}
}))
}, 120), tabs.scroller && tabs.scroller.toCenter(tabButton, !1)
}
}
function initScroller(tabs) {
if (!tabs.scroller) {
var contentScrollSlider = tabs.querySelector(".emby-tabs-slider");
contentScrollSlider ? (tabs.scroller = new scroller(tabs, {
horizontal: 1,
itemNav: 0,
mouseDragging: 1,
touchDragging: 1,
slidee: contentScrollSlider,
smart: !0,
releaseSwing: !0,
scrollBy: 200,
speed: 120,
elasticBounds: 1,
dragHandle: 1,
dynamicHandle: 1,
clickBar: 1,
hiddenScroll: !0,
requireAnimation: !browser.safari,
allowNativeSmoothScroll: !0
}), tabs.scroller.init()) : (tabs.classList.add("scrollX"), tabs.classList.add("hiddenScrollX"), tabs.classList.add("smoothScrollX"))
}
}
function getSelectedTabButton(elem) {
return elem.querySelector("." + activeButtonClass)
}
function getSibling(elem, method) {
for (var sibling = elem[method]; sibling;) {
if (sibling.classList.contains(buttonClass) && !sibling.classList.contains("hide")) return sibling;
sibling = sibling[method]
}
return null
}
var EmbyTabs = Object.create(HTMLDivElement.prototype),
buttonClass = "emby-tab-button",
activeButtonClass = buttonClass + "-active";
EmbyTabs.createdCallback = function() {
this.classList.contains("emby-tabs") || (this.classList.add("emby-tabs"), this.classList.add("focusable"), dom.addEventListener(this, "click", onClick, {
passive: !0
}), dom.addEventListener(this, "focus", onFocus, {
passive: !0,
capture: !0
}))
}, EmbyTabs.focus = function() {
var selected = this.querySelector("." + activeButtonClass);
selected ? focusManager.focus(selected) : focusManager.autoFocus(this)
}, EmbyTabs.refresh = function() {
this.scroller && this.scroller.reload()
}, EmbyTabs.attachedCallback = function() {
initScroller(this);
var current = this.querySelector("." + activeButtonClass),
currentIndex = current ? parseInt(current.getAttribute("data-index")) : parseInt(this.getAttribute("data-index") || "0");
if (-1 !== currentIndex) {
this.selectedTabIndex = currentIndex;
var tabButtons = this.querySelectorAll("." + buttonClass),
newTabButton = tabButtons[currentIndex];
newTabButton && setActiveTabButton(this, newTabButton, current, !1)
}
this.readyFired || (this.readyFired = !0, this.dispatchEvent(new CustomEvent("ready", {})))
}, EmbyTabs.detachedCallback = function() {
this.scroller && (this.scroller.destroy(), this.scroller = null), dom.removeEventListener(this, "click", onClick, {
passive: !0
}), dom.removeEventListener(this, "focus", onFocus, {
passive: !0,
capture: !0
})
}, EmbyTabs.selectedIndex = function(selected, triggerEvent) {
var tabs = this;
if (null == selected) return tabs.selectedTabIndex || 0;
var current = tabs.selectedIndex();
tabs.selectedTabIndex = selected;
var tabButtons = tabs.querySelectorAll("." + buttonClass);
if (current === selected || !1 === triggerEvent) {
triggerBeforeTabChange(tabs, selected, current), tabs.dispatchEvent(new CustomEvent("tabchange", {
detail: {
selectedTabIndex: selected
}
}));
var currentTabButton = tabButtons[current];
setActiveTabButton(tabs, tabButtons[selected], currentTabButton, !1), current !== selected && currentTabButton && currentTabButton.classList.remove(activeButtonClass)
} else onClick.call(tabs, {
target: tabButtons[selected]
})
}, EmbyTabs.selectNext = function() {
var current = getSelectedTabButton(this),
sibling = getSibling(current, "nextSibling");
sibling && onClick.call(this, {
target: sibling
})
}, EmbyTabs.selectPrevious = function() {
var current = getSelectedTabButton(this),
sibling = getSibling(current, "previousSibling");
sibling && onClick.call(this, {
target: sibling
})
}, EmbyTabs.triggerBeforeTabChange = function(selected) {
var tabs = this;
triggerBeforeTabChange(tabs, tabs.selectedIndex())
}, EmbyTabs.triggerTabChange = function(selected) {
var tabs = this;
tabs.dispatchEvent(new CustomEvent("tabchange", {
detail: {
selectedTabIndex: tabs.selectedIndex()
}
}))
}, EmbyTabs.setTabEnabled = function(index, enabled) {
var btn = this.querySelector('.emby-tab-button[data-index="' + index + '"]');
enabled ? btn.classList.remove("hide") : btn.classList.remove("add")
}, document.registerElement("emby-tabs", {
prototype: EmbyTabs,
extends: "div"
})
});

View file

@ -0,0 +1,31 @@
.emby-textarea {
display: block;
margin: 0;
margin-bottom: 0 !important;
font-size: inherit;
font-family: inherit;
font-weight: inherit;
color: inherit;
padding: .35em .25em;
-webkit-box-sizing: border-box;
box-sizing: border-box;
outline: 0 !important;
-webkit-tap-highlight-color: transparent;
width: 100%
}
.emby-textarea::-moz-focus-inner {
border: 0
}
.textareaLabel {
display: inline-block;
-webkit-transition: all .2s ease-out;
-o-transition: all .2s ease-out;
transition: all .2s ease-out;
margin-bottom: .25em
}
.emby-textarea+.fieldDescription {
margin-top: .25em
}

View file

@ -0,0 +1,55 @@
define(["layoutManager", "browser", "css!./emby-textarea", "registerElement", "emby-input"], function(layoutManager, browser) {
"use strict";
function autoGrow(textarea, maxLines) {
function reset() {
textarea.rows = 1, offset = self.getOffset(textarea), self.rows = textarea.rows || 1, self.lineHeight = textarea.scrollHeight / self.rows - offset / self.rows, self.maxAllowedHeight = self.lineHeight * maxLines - offset
}
function autogrowFn() {
if ((!self.lineHeight || self.lineHeight <= 0) && reset(), self.lineHeight <= 0) return textarea.style.overflowY = "scroll", textarea.style.height = "auto", void(textarea.rows = 3);
var newHeight = 0;
textarea.scrollHeight - offset > self.maxAllowedHeight ? (textarea.style.overflowY = "scroll", newHeight = self.maxAllowedHeight) : (textarea.style.overflowY = "hidden", textarea.style.height = "auto", newHeight = textarea.scrollHeight), textarea.style.height = newHeight + "px"
}
var self = this;
void 0 === maxLines && (maxLines = 999), self.getOffset = function(textarea) {
for (var style = window.getComputedStyle(textarea, null), props = ["paddingTop", "paddingBottom"], offset = 0, i = 0; i < props.length; i++) offset += parseInt(style[props[i]]);
return offset
};
var offset;
textarea.addEventListener("input", autogrowFn), textarea.addEventListener("focus", autogrowFn), textarea.addEventListener("valueset", autogrowFn), autogrowFn()
}
var EmbyTextAreaPrototype = Object.create(HTMLTextAreaElement.prototype),
elementId = 0;
if (Object.getOwnPropertyDescriptor && Object.defineProperty) {
var descriptor = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, "value");
if (descriptor && descriptor.configurable) {
var baseSetMethod = descriptor.set;
descriptor.set = function(value) {
baseSetMethod.call(this, value), this.dispatchEvent(new CustomEvent("valueset", {
bubbles: !1,
cancelable: !1
}))
}, Object.defineProperty(HTMLTextAreaElement.prototype, "value", descriptor)
}
}
EmbyTextAreaPrototype.createdCallback = function() {
this.id || (this.id = "embytextarea" + elementId, elementId++)
}, EmbyTextAreaPrototype.attachedCallback = function() {
if (!this.classList.contains("emby-textarea")) {
this.rows = 1, this.classList.add("emby-textarea");
var parentNode = this.parentNode,
label = this.ownerDocument.createElement("label");
label.innerHTML = this.getAttribute("label") || "", label.classList.add("textareaLabel"), label.htmlFor = this.id, parentNode.insertBefore(label, this), this.addEventListener("focus", function() {
label.classList.add("textareaLabelFocused"), label.classList.remove("textareaLabelUnfocused")
}), this.addEventListener("blur", function() {
label.classList.remove("textareaLabelFocused"), label.classList.add("textareaLabelUnfocused")
}), this.label = function(text) {
label.innerHTML = text
}, new autoGrow(this)
}
}, document.registerElement("emby-textarea", {
prototype: EmbyTextAreaPrototype,
extends: "textarea"
})
});

View file

@ -0,0 +1,155 @@
.mdl-switch {
position: relative;
z-index: 1;
vertical-align: middle;
display: -webkit-inline-box;
display: -webkit-inline-flex;
display: inline-flex;
-webkit-box-align: center;
-webkit-align-items: center;
align-items: center;
-webkit-box-sizing: border-box;
box-sizing: border-box;
width: 100%;
margin: 0;
padding: 0;
overflow: visible;
-webkit-touch-callout: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
-webkit-box-orient: horizontal;
-webkit-box-direction: reverse;
-webkit-flex-direction: row-reverse;
flex-direction: row-reverse;
-webkit-box-pack: end;
-webkit-justify-content: flex-end;
justify-content: flex-end
}
.toggleContainer {
margin-bottom: 1.8em
}
.mdl-switch__input {
width: 0;
height: 0;
margin: 0;
padding: 0;
opacity: 0;
-ms-appearance: none;
-moz-appearance: none;
-webkit-appearance: none;
appearance: none;
border: none
}
.mdl-switch__trackContainer {
position: relative;
width: 2.9em
}
.mdl-switch__track {
background: rgba(0, 0, 0, .2);
height: 1em;
-webkit-border-radius: 1em;
border-radius: 1em;
cursor: pointer
}
.mdl-switch__input:checked+.mdl-switch__label+.mdl-switch__trackContainer>.mdl-switch__track {
background: rgba(0,164,220, .5)
}
.mdl-switch__input[disabled]+.mdl-switch__label+.mdl-switch__trackContainer>.mdl-switch__track {
background: rgba(0, 0, 0, .12);
cursor: auto
}
.mdl-switch__thumb {
background: #999;
position: absolute;
left: 0;
top: -.25em;
height: 1.44em;
width: 1.44em;
-webkit-border-radius: 50%;
border-radius: 50%;
cursor: pointer;
-webkit-box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12);
box-shadow: 0 2px 2px 0 rgba(0, 0, 0, .14), 0 3px 1px -2px rgba(0, 0, 0, .2), 0 1px 5px 0 rgba(0, 0, 0, .12);
-webkit-transition-duration: .28s;
-o-transition-duration: .28s;
transition-duration: .28s;
-webkit-transition-timing-function: cubic-bezier(.4, 0, .2, 1);
-o-transition-timing-function: cubic-bezier(.4, 0, .2, 1);
transition-timing-function: cubic-bezier(.4, 0, .2, 1);
-webkit-transition-property: left;
-o-transition-property: left;
transition-property: left;
display: -webkit-box;
display: -webkit-flex;
display: flex;
-webkit-box-align: center;
-webkit-align-items: center;
align-items: center;
-webkit-box-pack: center;
-webkit-justify-content: center;
justify-content: center
}
.mdl-switch__input:checked+.mdl-switch__label+.mdl-switch__trackContainer>.mdl-switch__thumb {
background: #00a4dc;
left: 1.466em;
-webkit-box-shadow: 0 3px .28em 0 rgba(0, 0, 0, .14), 0 3px 3px -2px rgba(0, 0, 0, .2), 0 1px .56em 0 rgba(0, 0, 0, .12);
box-shadow: 0 3px .28em 0 rgba(0, 0, 0, .14), 0 3px 3px -2px rgba(0, 0, 0, .2), 0 1px .56em 0 rgba(0, 0, 0, .12)
}
.mdl-switch__input[disabled]+.mdl-switch__label+.mdl-switch__trackContainer>.mdl-switch__thumb {
background: #bdbdbd;
cursor: auto
}
.mdl-switch__focus-helper {
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
display: inline-block;
-webkit-box-sizing: border-box;
box-sizing: border-box;
width: .6em;
height: .6em;
-webkit-border-radius: 50%;
border-radius: 50%;
background-color: transparent
}
.mdl-switch__input:focus+.mdl-switch__label+.mdl-switch__trackContainer .mdl-switch__focus-helper {
-webkit-box-shadow: 0 0 0 1.39em rgba(0, 0, 0, .05);
box-shadow: 0 0 0 1.39em rgba(0, 0, 0, .05)
}
.mdl-switch__input:checked:focus+.mdl-switch__label+.mdl-switch__trackContainer .mdl-switch__focus-helper {
-webkit-box-shadow: 0 0 0 1.39em rgba(0,164,220, .26);
box-shadow: 0 0 0 1.39em rgba(0,164,220, .26);
background-color: rgba(0,164,220, .26)
}
.mdl-switch__label {
cursor: pointer;
margin: 0 0 0 .7em;
display: -webkit-inline-box;
display: -webkit-inline-flex;
display: inline-flex;
-webkit-box-align: center;
-webkit-align-items: center;
align-items: center
}
.mdl-switch__input[disabled] .mdl-switch__label {
color: #bdbdbd;
cursor: auto
}

Some files were not shown because too many files have changed in this diff Show more