First separation commit.
Added LICENSE, README.md, CONTRIBUTORS.md
This commit is contained in:
+139
@@ -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>
|
||||
+393
@@ -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
@@ -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 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 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">☰</span>Select text freely</li>
|
||||
<li><span class="drag-handle">☰</span>Drag my handle</li>
|
||||
<li><span class="drag-handle">☰</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…</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>
|
||||
+17
@@ -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
|
||||
}
|
||||
});
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
});
|
||||
+87
@@ -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))
|
||||
}))
|
||||
})
|
||||
}
|
||||
}
|
||||
}])
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
});
|
||||
+582
@@ -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)
|
||||
}
|
||||
}
|
||||
+1606
File diff suppressed because it is too large
Load Diff
+9
@@ -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
@@ -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);
|
||||
@@ -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>
|
||||
+246
@@ -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");
|
||||
+1605
File diff suppressed because it is too large
Load Diff
+231
@@ -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
|
||||
});
|
||||
@@ -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
|
||||
});
|
||||
@@ -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
|
||||
});
|
||||
@@ -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
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
define([], function() {
|
||||
"use strict";
|
||||
|
||||
function CameraRoll() {}
|
||||
return CameraRoll.prototype.getFiles = function() {
|
||||
return Promise.resolve([])
|
||||
}, new CameraRoll
|
||||
});
|
||||
@@ -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
|
||||
});
|
||||
@@ -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
|
||||
});
|
||||
+30
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
define([], function() {
|
||||
"use strict";
|
||||
|
||||
function FileUpload() {}
|
||||
return FileUpload.prototype.upload = function(file, url) {
|
||||
return Promise.reject()
|
||||
}, FileUpload
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,8 @@
|
||||
define([], function() {
|
||||
"use strict";
|
||||
return {
|
||||
findServers: function(timeoutMs) {
|
||||
return Promise.resolve([])
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -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
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
});
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
}))
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -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())
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -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
|
||||
});
|
||||
@@ -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
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
define([], function() {
|
||||
"use strict";
|
||||
|
||||
function send(info) {
|
||||
return Promise.reject()
|
||||
}
|
||||
|
||||
function isSupported() {
|
||||
return !1
|
||||
}
|
||||
return {
|
||||
send: send,
|
||||
isSupported: isSupported
|
||||
}
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"></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
|
||||
}
|
||||
});
|
||||
@@ -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()
|
||||
})
|
||||
}
|
||||
});
|
||||
@@ -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()
|
||||
}
|
||||
});
|
||||
+59
@@ -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
|
||||
});
|
||||
@@ -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"></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"></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
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
});
|
||||
@@ -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
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"> </div>' : '<div class="cardText cardTextCentered cardText-secondary"> </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 + "'> </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"></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 || " ")), 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"></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"></i></button>'), options.overlayMoreButton && (overlayButtons += '<button is="paper-icon-button-light" class="' + btnCssClass + '" data-action="menu"><i class="md-icon cardOverlayButtonIcon"></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"></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"></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"></i></button>'
|
||||
}
|
||||
return html += '<button is="paper-icon-button-light" class="' + btnCssClass + '" data-action="menu"><i class="md-icon cardOverlayButtonIcon cardOverlayButtonIcon-hover"></i></button>', html += "</div>", html += "</div>"
|
||||
}
|
||||
|
||||
function getCardDefaultText(item, options) {
|
||||
var collectionType = item.CollectionType;
|
||||
return "livetv" === collectionType ? '<i class="cardImageIcon md-icon"></i>' : "homevideos" === collectionType || "photos" === collectionType ? '<i class="cardImageIcon md-icon"></i>' : "music" === collectionType ? '<i class="cardImageIcon md-icon"></i>' : "MusicAlbum" === item.Type ? '<i class="cardImageIcon md-icon"></i>' : "MusicArtist" === item.Type || "Person" === item.Type ? '<i class="cardImageIcon md-icon"></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"></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"></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
|
||||
}
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
});
|
||||
@@ -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: ""
|
||||
}), cardBuilder.buildCards(items, options)
|
||||
}
|
||||
return {
|
||||
buildPeopleCards: buildPeopleCards
|
||||
}
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
});
|
||||
@@ -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
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
+119
@@ -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"></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"></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
|
||||
});
|
||||
@@ -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()
|
||||
})
|
||||
}
|
||||
});
|
||||
@@ -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()
|
||||
}
|
||||
});
|
||||
+123
@@ -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
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
});
|
||||
@@ -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)
|
||||
})
|
||||
})
|
||||
}
|
||||
});
|
||||
@@ -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>
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -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
|
||||
});
|
||||
+191
@@ -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>
|
||||
+105
@@ -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
|
||||
}
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
});
|
||||
+10
@@ -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"
|
||||
})
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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") || "",
|
||||
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"
|
||||
})
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
})
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
})
|
||||
});
|
||||
Vendored
+32
@@ -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"
|
||||
})
|
||||
});
|
||||
+220
@@ -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"
|
||||
})
|
||||
});
|
||||
+157
@@ -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
|
||||
}
|
||||
+21
@@ -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
|
||||
});
|
||||
+23
@@ -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>
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
})
|
||||
});
|
||||
+67
@@ -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
|
||||
}
|
||||
+75
@@ -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 ? "" : "";
|
||||
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"
|
||||
})
|
||||
});
|
||||
@@ -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"
|
||||
})
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"></i></div>')
|
||||
}
|
||||
}, EmbySelectPrototype.setLabel = function(text) {
|
||||
this.parentNode.querySelector("label").innerHTML = text
|
||||
}, document.registerElement("emby-select", {
|
||||
prototype: EmbySelectPrototype,
|
||||
extends: "select"
|
||||
})
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
})
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
})
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
@@ -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"
|
||||
})
|
||||
});
|
||||
@@ -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
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
define(["css!./emby-toggle", "registerElement"], function() {
|
||||
"use strict";
|
||||
|
||||
function onKeyDown(e) {
|
||||
if (13 === e.keyCode) return e.preventDefault(), this.checked = !this.checked, this.dispatchEvent(new CustomEvent("change", {
|
||||
bubbles: !0
|
||||
})), !1
|
||||
}
|
||||
var EmbyTogglePrototype = Object.create(HTMLInputElement.prototype);
|
||||
EmbyTogglePrototype.attachedCallback = function() {
|
||||
if ("true" !== this.getAttribute("data-embytoggle")) {
|
||||
this.setAttribute("data-embytoggle", "true"), this.classList.add("mdl-switch__input");
|
||||
var labelElement = this.parentNode;
|
||||
labelElement.classList.add("mdl-switch"), labelElement.classList.add("mdl-js-switch");
|
||||
var labelTextElement = labelElement.querySelector("span");
|
||||
labelElement.insertAdjacentHTML("beforeend", '<div class="mdl-switch__trackContainer"><div class="mdl-switch__track"></div><div class="mdl-switch__thumb"><span class="mdl-switch__focus-helper"></span></div></div>'), labelTextElement.classList.add("toggleButtonLabel"), labelTextElement.classList.add("mdl-switch__label"), this.addEventListener("keydown", onKeyDown)
|
||||
}
|
||||
}, document.registerElement("emby-toggle", {
|
||||
prototype: EmbyTogglePrototype,
|
||||
extends: "input"
|
||||
})
|
||||
});
|
||||
@@ -0,0 +1,54 @@
|
||||
define([], function() {
|
||||
"use strict";
|
||||
|
||||
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;
|
||||
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);
|
||||
var url = request.url;
|
||||
if (request.query) {
|
||||
var paramString = paramsToString(request.query);
|
||||
paramString && (url += "?" + paramString)
|
||||
}
|
||||
return request.timeout ? fetchWithTimeout(url, fetchRequest, request.timeout) : fetch(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 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 ajax(request) {
|
||||
if (!request) throw new Error("Request cannot be null");
|
||||
return request.headers = request.headers || {}, console.log("requesting url: " + request.url), getFetchPromise(request).then(function(response) {
|
||||
return console.log("response status: " + response.status + ", url: " + request.url), response.status < 400 ? "json" === request.dataType || "application/json" === request.headers.accept ? response.json() : "text" === request.dataType || 0 === (response.headers.get("Content-Type") || "").toLowerCase().indexOf("text/") ? response.text() : response : Promise.reject(response)
|
||||
}, function(err) {
|
||||
throw console.log("request failed to url: " + request.url), err
|
||||
})
|
||||
}
|
||||
return {
|
||||
getFetchPromise: getFetchPromise,
|
||||
ajax: ajax
|
||||
}
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user