Merge pull request #1925 from MediaBrowser/dev

Dev
This commit is contained in:
Luke 2016-07-07 12:01:35 -04:00 committed by GitHub
commit 9fa90ba08f
25 changed files with 1344 additions and 266 deletions

View File

@ -15,12 +15,12 @@
}, },
"devDependencies": {}, "devDependencies": {},
"ignore": [], "ignore": [],
"version": "1.4.69", "version": "1.4.71",
"_release": "1.4.69", "_release": "1.4.71",
"_resolution": { "_resolution": {
"type": "version", "type": "version",
"tag": "1.4.69", "tag": "1.4.71",
"commit": "24a5ea44282dc2e0a537c6245aedd88877ebb9ad" "commit": "0613abc5976a0da9db22854809449e09517ae2b6"
}, },
"_source": "https://github.com/MediaBrowser/emby-webcomponents.git", "_source": "https://github.com/MediaBrowser/emby-webcomponents.git",
"_target": "^1.2.0", "_target": "^1.2.0",

View File

@ -20,14 +20,29 @@
cursor: pointer; cursor: pointer;
outline: none !important; outline: none !important;
width: 100%; width: 100%;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
} }
[is="emby-select"] option { [is="emby-select"] option {
color: initial; color: initial;
} }
.selectContainer { .selectContainer {
margin-bottom: 2em; margin-bottom: 2em;
position: relative;
}
.selectArrowContainer {
position: absolute;
right: 0;
top: .25em;
color: inherit;
}
.selectArrow {
margin-top: .35em;
} }
.selectLabel { .selectLabel {

View File

@ -153,6 +153,17 @@
var div = document.createElement('div'); var div = document.createElement('div');
div.classList.add('emby-select-selectionbar'); div.classList.add('emby-select-selectionbar');
this.parentNode.insertBefore(div, this.nextSibling); this.parentNode.insertBefore(div, this.nextSibling);
var arrowContainer = document.createElement('div');
arrowContainer.classList.add('selectArrowContainer');
arrowContainer.innerHTML = '<div style="visibility:hidden;">0</div>';
this.parentNode.appendChild(arrowContainer);
var arrow = document.createElement('i');
arrow.classList.add('md-icon');
arrow.classList.add('selectArrow');
arrow.innerHTML = '&#xE313;';
arrowContainer.appendChild(arrow);
} }
}; };

View File

@ -125,10 +125,6 @@ define(['visibleinviewport', 'imageFetcher', 'layoutManager', 'events', 'browser
fillImage(target); fillImage(target);
filledCount++; filledCount++;
} }
if (filledCount >= images.length) {
observer.disconnect();
}
}, },
options options
); );

View File

@ -0,0 +1,981 @@
define(['browser', 'layoutManager', 'scrollStyles'], function (browser, layoutManager) {
/**
* Return type of the value.
*
* @param {Mixed} value
*
* @return {String}
*/
function type(value) {
if (value == null) {
return String(value);
}
if (typeof value === 'object' || typeof value === 'function') {
return Object.prototype.toString.call(value).match(/\s([a-z]+)/i)[1].toLowerCase() || 'object';
}
return typeof value;
}
/**
* Event preventDefault & stopPropagation helper.
*
* @param {Event} event Event object.
* @param {Bool} noBubbles Cancel event bubbling.
*
* @return {Void}
*/
function stopDefault(event, noBubbles) {
event.preventDefault();
if (noBubbles) {
event.stopPropagation();
}
}
/**
* Disables an event it was triggered on and unbinds itself.
*
* @param {Event} event
*
* @return {Void}
*/
function disableOneEvent(event) {
/*jshint validthis:true */
stopDefault(event, 1);
this.removeEventListener(event.type, disableOneEvent);
}
/**
* Check if variable is a number.
*
* @param {Mixed} value
*s
* @return {Boolean}
*/
function isNumber(value) {
return !isNaN(parseFloat(value)) && isFinite(value);
}
/**
* Make sure that number is within the limits.
*
* @param {Number} number
* @param {Number} min
* @param {Number} max
*
* @return {Number}
*/
function within(number, min, max) {
return number < min ? min : number > max ? max : number;
}
var pluginName = 'sly';
var className = 'Sly';
var namespace = pluginName;
// Other global values
var dragInitEventNames = ['touchstart', 'mousedown'];
var dragInitEvents = 'touchstart.' + namespace + ' mousedown.' + namespace;
var dragMouseEvents = ['mousemove', 'mouseup'];
var dragTouchEvents = ['touchmove', 'touchend'];
var wheelEvent = (document.implementation.hasFeature('Event.wheel', '3.0') ? 'wheel' : 'mousewheel');
var clickEvent = 'click.' + namespace;
var mouseDownEvent = 'mousedown.' + namespace;
var interactiveElements = ['INPUT', 'SELECT', 'TEXTAREA'];
var tmpArray = [];
var time;
// Math shorthands
var abs = Math.abs;
var sqrt = Math.sqrt;
var pow = Math.pow;
var round = Math.round;
var max = Math.max;
var min = Math.min;
var scrollerFactory = function (frame, options) {
// Extend options
var o = extend({}, {
slidee: null, // Selector, DOM element, or jQuery object with DOM element representing SLIDEE.
horizontal: false, // Switch to horizontal mode.
// Scrolling
scrollSource: null, // Element for catching the mouse wheel scrolling. Default is FRAME.
scrollBy: 0, // Pixels or items to move per one mouse scroll. 0 to disable scrolling.
scrollHijack: 300, // Milliseconds since last wheel event after which it is acceptable to hijack global scroll.
scrollTrap: false, // Don't bubble scrolling when hitting scrolling limits.
// Dragging
dragSource: null, // Selector or DOM element for catching dragging events. Default is FRAME.
mouseDragging: 1, // Enable navigation by dragging the SLIDEE with mouse cursor.
touchDragging: 1, // Enable navigation by dragging the SLIDEE with touch events.
releaseSwing: false, // Ease out on dragging swing release.
swingSpeed: 0.2, // Swing synchronization speed, where: 1 = instant, 0 = infinite.
elasticBounds: false, // Stretch SLIDEE position limits when dragging past FRAME boundaries.
dragThreshold: 3, // Distance in pixels before Sly recognizes dragging.
intervactive: null, // Selector for special interactive elements.
// Mixed options
speed: 0, // Animations speed in milliseconds. 0 to disable animations.
// Classes
draggedClass: 'dragged', // Class for dragged elements (like SLIDEE or scrollbar handle).
activeClass: 'active', // Class for active items and pages.
disabledClass: 'disabled' // Class for disabled navigation elements.
}, options);
var isSmoothScrollSupported = 'scrollBehavior' in document.documentElement.style;
// native scroll is a must with touch input
// also use native scroll when scrolling vertically in desktop mode - excluding horizontal because the mouse wheel support is choppy at the moment
// in cases with firefox, if the smooth scroll api is supported then use that because their implementation is very good
if (browser.operaTv) {
// no scrolling supported
options.enableNativeScroll = false;
}
else if (browser.edge && !browser.xboxOne) {
// no scrolling supported
options.enableNativeScroll = false;
}
else if (isSmoothScrollSupported && browser.firefox) {
// native smooth scroll
options.enableNativeScroll = true;
}
else if (options.requireAnimation) {
// transform is the only way to guarantee animation
options.enableNativeScroll = false;
}
else if (layoutManager.mobile ||
layoutManager.desktop ||
!browser.animate) {
options.enableNativeScroll = true;
}
// Private variables
var self = this;
self.options = o;
// Frame
var frameElement = frame;
var slideeElement = o.slidee ? o.slidee : sibling(frameElement.firstChild)[0];
var frameSize = 0;
var pos = {
start: 0,
center: 0,
end: 0,
cur: 0,
dest: 0
};
var transform = !options.enableNativeScroll;
var hPos = {
start: 0,
end: 0,
cur: 0
};
// Items
var rel = {
activeItem: null
};
// Miscellaneous
var scrollSource = o.scrollSource ? o.scrollSource : frameElement;
var dragSourceElement = o.dragSource ? o.dragSource : frameElement;
var animation = {};
var dragging = {
released: 1
};
var scrolling = {
last: 0,
delta: 0,
resetTime: 200
};
var historyID = 0;
var i, l;
// Normalizing frame
frame = frameElement;
// Expose properties
self.initialized = 0;
self.frame = frame;
self.slidee = slideeElement;
self.options = o;
self.dragging = dragging;
function sibling(n, elem) {
var matched = [];
for (; n; n = n.nextSibling) {
if (n.nodeType === 1 && n !== elem) {
matched.push(n);
}
}
return matched;
}
/**
* Loading function.
*
* Populate arrays, set sizes, bind events, ...
*
* @param {Boolean} [isInit] Whether load is called from within self.init().
* @return {Void}
*/
function load(isInit) {
// Reset global variables
frameSize = getWidthOrHeight(frameElement, o.horizontal ? 'width' : 'height');
var slideeSize = o.scrollWidth || Math.max(slideeElement[o.horizontal ? 'offsetWidth' : 'offsetHeight'], slideeElement[o.horizontal ? 'scrollWidth' : 'scrollHeight']);
// Set position limits & relativess
pos.start = 0;
pos.end = max(slideeSize - frameSize, 0);
if (!isInit) {
// Fix possible overflowing
slideTo(within(pos.dest, pos.start, pos.end));
}
}
var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source;
var rnumnonpx = new RegExp("^(" + pnum + ")(?!px)[a-z%]+$", "i");
function getWidthOrHeight(elem, name, extra) {
// Start with offset property, which is equivalent to the border-box value
var valueIsBorderBox = true,
val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
styles = getComputedStyle(elem, null),
isBorderBox = styles.getPropertyValue("box-sizing") === "border-box";
// Some non-html elements return undefined for offsetWidth, so check for null/undefined
// svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
// MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
if (val <= 0 || val == null) {
// Fall back to computed then uncomputed css if necessary
//val = curCSS(elem, name, styles);
if (val < 0 || val == null) {
val = elem.style[name];
}
// Computed unit is not pixels. Stop here and return.
if (rnumnonpx.test(val)) {
return val;
}
// Check for style in case a browser which returns unreliable values
// for getComputedStyle silently falls back to the reliable elem.style
valueIsBorderBox = isBorderBox &&
(support.boxSizingReliable() || val === elem.style[name]);
// Normalize "", auto, and prepare for extra
val = parseFloat(val) || 0;
}
// Use the active box-sizing model to add/subtract irrelevant styles
return (val +
augmentWidthOrHeight(
elem,
name,
extra || (isBorderBox ? "border" : "content"),
valueIsBorderBox,
styles
)
);
}
var cssExpand = ["Top", "Right", "Bottom", "Left"];
function augmentWidthOrHeight(elem, name, extra, isBorderBox, styles) {
var i = extra === (isBorderBox ? "border" : "content") ?
// If we already have the right measurement, avoid augmentation
4 :
// Otherwise initialize for horizontal or vertical properties
name === "width" ? 1 : 0,
val = 0;
for (; i < 4; i += 2) {
// Both box models exclude margin, so add it if we want it
if (extra === "margin") {
//val += jQuery.css(elem, extra + cssExpand[i], true, styles);
}
if (isBorderBox) {
// border-box includes padding, so remove it if we want content
if (extra === "content") {
//val -= jQuery.css(elem, "padding" + cssExpand[i], true, styles);
}
// At this point, extra isn't border nor margin, so remove border
if (extra !== "margin") {
//val -= jQuery.css(elem, "border" + cssExpand[i] + "Width", true, styles);
}
} else {
// At this point, extra isn't content, so add padding
//val += jQuery.css(elem, "padding" + cssExpand[i], true, styles);
// At this point, extra isn't content nor padding, so add border
if (extra !== "padding") {
//val += jQuery.css(elem, "border" + cssExpand[i] + "Width", true, styles);
}
}
}
return val;
}
self.reload = function () { load(); };
function nativeScrollTo(container, pos, immediate) {
if (!immediate && container.scrollTo) {
if (o.horizontal) {
container.scrollTo(pos, 0);
} else {
container.scrollTo(0, pos);
}
} else {
if (o.horizontal) {
container.scrollLeft = Math.round(pos);
} else {
container.scrollTop = Math.round(pos);
}
}
}
/**
* Animate to a position.
*
* @param {Int} newPos New position.
* @param {Bool} immediate Reposition immediately without an animation.
*
* @return {Void}
*/
function slideTo(newPos, immediate) {
// Handle overflowing position limits
if (dragging.init && dragging.slidee && o.elasticBounds) {
if (newPos > pos.end) {
newPos = pos.end + (newPos - pos.end) / 6;
} else if (newPos < pos.start) {
newPos = pos.start + (newPos - pos.start) / 6;
}
} else {
newPos = within(newPos, pos.start, pos.end);
}
if (!transform) {
nativeScrollTo(slideeElement, newPos, immediate);
return;
}
// Update the animation object
animation.from = pos.cur;
animation.to = newPos;
animation.tweesing = dragging.tweese || dragging.init && !dragging.slidee;
animation.immediate = !animation.tweesing && (immediate || dragging.init && dragging.slidee || !o.speed);
// Reset dragging tweesing request
dragging.tweese = 0;
// Start animation rendering
if (newPos !== pos.dest) {
pos.dest = newPos;
renderAnimate(animation);
}
}
var scrollEvent = new CustomEvent("scroll");
function renderAnimate() {
if (!transform) {
return;
}
var obj = getComputedStyle(slideeElement, null).getPropertyValue('transform').match(/([-+]?(?:\d*\.)?\d+)\D*, ([-+]?(?:\d*\.)?\d+)\D*\)/);
if (obj) {
// [1] = x, [2] = y
pos.cur = parseInt(o.horizontal ? obj[1] : obj[2]) * -1;
}
var keyframes;
animation.to = round(animation.to);
if (o.horizontal) {
keyframes = [
{ transform: 'translate3d(' + (-round(pos.cur || animation.from)) + 'px, 0, 0)', offset: 0 },
{ transform: 'translate3d(' + (-round(animation.to)) + 'px, 0, 0)', offset: 1 }
];
} else {
keyframes = [
{ transform: 'translate3d(0, ' + (-round(pos.cur || animation.from)) + 'px, 0)', offset: 0 },
{ transform: 'translate3d(0, ' + (-round(animation.to)) + 'px, 0)', offset: 1 }
];
}
var speed = o.speed;
if (animation.immediate) {
speed = o.immediateSpeed || 50;
if (!browser.animate) {
o.immediateSpeed = 0;
}
}
var animationConfig = {
duration: speed,
iterations: 1,
fill: 'both'
};
if (!animation.immediate || browser.animate) {
animationConfig.easing = 'ease-out';
}
var animationInstance = slideeElement.animate(keyframes, animationConfig);
animationInstance.onfinish = function () {
pos.cur = animation.to;
document.dispatchEvent(scrollEvent);
};
}
function getOffsets(elems) {
var doc = document;
var results = [];
if (!doc) {
return results;
}
var docElem = doc.documentElement;
var docElemValues = {
clientTop: docElem.clientTop,
clientLeft: docElem.clientLeft
};
var win = doc.defaultView;
var winValues = {
pageXOffset: win.pageXOffset,
pageYOffset: win.pageYOffset
};
var box;
var elem;
for (var i = 0, length = elems.length; i < length; i++) {
elem = elems[i];
// Support: BlackBerry 5, iOS 3 (original iPhone)
// If we don't have gBCR, just use 0,0 rather than error
if (elem.getBoundingClientRect) {
box = elem.getBoundingClientRect();
} else {
box = { top: 0, left: 0 };
}
results[i] = {
top: box.top + winValues.pageYOffset - docElemValues.clientTop,
left: box.left + winValues.pageXOffset - docElemValues.clientLeft
};
}
return results;
}
/**
* Returns the position object.
*
* @param {Mixed} item
*
* @return {Object}
*/
self.getPos = function (item) {
var offsets = getOffsets([slideeElement, item]);
var slideeOffset = offsets[0];
var itemOffset = offsets[1];
var offset = o.horizontal ? itemOffset.left - slideeOffset.left : itemOffset.top - slideeOffset.top;
var size = item[o.horizontal ? 'offsetWidth' : 'offsetHeight'];
var centerOffset = o.centerOffset || 0;
if (!transform) {
centerOffset = 0;
if (o.horizontal) {
offset += slideeElement.scrollLeft;
} else {
offset += slideeElement.scrollTop;
}
}
return {
start: offset,
center: offset + centerOffset - (frameSize / 2) + (size / 2),
end: offset - frameSize + size,
size: size
};
};
/**
* Slide SLIDEE by amount of pixels.
*
* @param {Int} delta Pixels/Items. Positive means forward, negative means backward.
* @param {Bool} immediate Reposition immediately without an animation.
*
* @return {Void}
*/
self.slideBy = function (delta, immediate) {
if (!delta) {
return;
}
slideTo(pos.dest + delta, immediate);
};
/**
* Animate SLIDEE to a specific position.
*
* @param {Int} pos New position.
* @param {Bool} immediate Reposition immediately without an animation.
*
* @return {Void}
*/
self.slideTo = function (pos, immediate) {
slideTo(pos, immediate);
};
/**
* Core method for handling `toLocation` methods.
*
* @param {String} location
* @param {Mixed} item
* @param {Bool} immediate
*
* @return {Void}
*/
function to(location, item, immediate) {
// Optional arguments logic
if (type(item) === 'boolean') {
immediate = item;
item = undefined;
v
}
if (item === undefined) {
slideTo(pos[location], immediate);
} else {
//if (!transform) {
// item.scrollIntoView();
// return;
//}
var itemPos = self.getPos(item);
if (itemPos) {
slideTo(itemPos[location], immediate, true);
}
}
}
/**
* Animate element or the whole SLIDEE to the start of the frame.
*
* @param {Mixed} item Item DOM element, or index starting at 0. Omitting will animate SLIDEE.
* @param {Bool} immediate Reposition immediately without an animation.
*
* @return {Void}
*/
self.toStart = function (item, immediate) {
to('start', item, immediate);
};
/**
* Animate element or the whole SLIDEE to the end of the frame.
*
* @param {Mixed} item Item DOM element, or index starting at 0. Omitting will animate SLIDEE.
* @param {Bool} immediate Reposition immediately without an animation.
*
* @return {Void}
*/
self.toEnd = function (item, immediate) {
to('end', item, immediate);
};
/**
* Animate element or the whole SLIDEE to the center of the frame.
*
* @param {Mixed} item Item DOM element, or index starting at 0. Omitting will animate SLIDEE.
* @param {Bool} immediate Reposition immediately without an animation.
*
* @return {Void}
*/
self.toCenter = function (item, immediate) {
to('center', item, immediate);
};
function extend() {
for (var i = 1; i < arguments.length; i++)
for (var key in arguments[i])
if (arguments[i].hasOwnProperty(key))
arguments[0][key] = arguments[i][key];
return arguments[0];
}
/**
* Keeps track of a dragging delta history.
*
* @return {Void}
*/
function draggingHistoryTick() {
// Looking at this, I know what you're thinking :) But as we need only 4 history states, doing it this way
// as opposed to a proper loop is ~25 bytes smaller (when minified with GCC), a lot faster, and doesn't
// generate garbage. The loop version would create 2 new variables on every tick. Unexaptable!
dragging.history[0] = dragging.history[1];
dragging.history[1] = dragging.history[2];
dragging.history[2] = dragging.history[3];
dragging.history[3] = dragging.delta;
}
/**
* Initialize continuous movement.
*
* @return {Void}
*/
function continuousInit(source) {
dragging.released = 0;
dragging.source = source;
dragging.slidee = source === 'slidee';
}
function dragInitSlidee(event) {
dragInit(event, 'slidee');
}
/**
* Dragging initiator.
*a
* @param {Event} event
*
* @return {Void}
*/
function dragInit(event, source) {
var isTouch = event.type === 'touchstart';
var isSlidee = source === 'slidee';
// Ignore when already in progress, or interactive element in non-touch navivagion
if (dragging.init || !isTouch && isInteractive(event.target)) {
return;
}
// SLIDEE dragging conditions
if (isSlidee && !(isTouch ? o.touchDragging : o.mouseDragging && event.which < 2)) {
return;
}
if (!isTouch) {
// prevents native image dragging in Firefox
stopDefault(event);
}
// Reset dragging object
continuousInit(source);
// Properties used in dragHandler
dragging.init = 0;
dragging.source = event.target;
dragging.touch = isTouch;
dragging.pointer = isTouch ? event.touches[0] : event;
dragging.initX = dragging.pointer.pageX;
dragging.initY = dragging.pointer.pageY;
dragging.initPos = isSlidee ? pos.cur : hPos.cur;
dragging.start = +new Date();
dragging.time = 0;
dragging.path = 0;
dragging.delta = 0;
dragging.locked = 0;
dragging.history = [0, 0, 0, 0];
dragging.pathToLock = isSlidee ? isTouch ? 30 : 10 : 0;
// Bind dragging events
if (isTouch) {
dragTouchEvents.forEach(function (eventName) {
document.addEventListener(eventName, dragHandler);
});
} else {
dragMouseEvents.forEach(function (eventName) {
document.addEventListener(eventName, dragHandler);
});
}
// Add dragging class
if (isSlidee) {
slideeElement.classList.add(o.draggedClass);
}
// Keep track of a dragging path history. This is later used in the
// dragging release swing calculation when dragging SLIDEE.
if (isSlidee) {
historyID = setInterval(draggingHistoryTick, 10);
}
}
/**
* Handler for dragging scrollbar handle or SLIDEE.
*
* @param {Event} event
*
* @return {Void}
*/
function dragHandler(event) {
dragging.released = event.type === 'mouseup' || event.type === 'touchend';
dragging.pointer = dragging.touch ? event[dragging.released ? 'changedTouches' : 'touches'][0] : event;
dragging.pathX = dragging.pointer.pageX - dragging.initX;
dragging.pathY = dragging.pointer.pageY - dragging.initY;
dragging.path = sqrt(pow(dragging.pathX, 2) + pow(dragging.pathY, 2));
dragging.delta = o.horizontal ? dragging.pathX : dragging.pathY;
if (!dragging.released && dragging.path < 1) return;
// We haven't decided whether this is a drag or not...
if (!dragging.init) {
// If the drag path was very short, maybe it's not a drag?
if (dragging.path < o.dragThreshold) {
// If the pointer was released, the path will not become longer and it's
// definitely not a drag. If not released yet, decide on next iteration
return dragging.released ? dragEnd() : undefined;
} else {
// If dragging path is sufficiently long we can confidently start a drag
// if drag is in different direction than scroll, ignore it
if (o.horizontal ? abs(dragging.pathX) > abs(dragging.pathY) : abs(dragging.pathX) < abs(dragging.pathY)) {
dragging.init = 1;
} else {
return dragEnd();
}
}
}
stopDefault(event);
// Disable click on a source element, as it is unwelcome when dragging
if (!dragging.locked && dragging.path > dragging.pathToLock && dragging.slidee) {
dragging.locked = 1;
dragging.source.addEventListener('click', disableOneEvent);
}
// Cancel dragging on release
if (dragging.released) {
dragEnd();
// Adjust path with a swing on mouse release
if (o.releaseSwing && dragging.slidee) {
dragging.swing = (dragging.delta - dragging.history[0]) / 40 * 300;
dragging.delta += dragging.swing;
dragging.tweese = abs(dragging.swing) > 10;
}
}
slideTo(dragging.slidee ? round(dragging.initPos - dragging.delta) : handleToSlidee(dragging.initPos + dragging.delta));
}
/**
* Stops dragging and cleans up after it.
*
* @return {Void}
*/
function dragEnd() {
clearInterval(historyID);
dragging.released = true;
if (dragging.touch) {
dragTouchEvents.forEach(function (eventName) {
document.removeEventListener(eventName, dragHandler);
});
} else {
dragMouseEvents.forEach(function (eventName) {
document.removeEventListener(eventName, dragHandler);
});
}
if (dragging.slidee) {
slideeElement.classList.remove(o.draggedClass);
}
// Make sure that disableOneEvent is not active in next tick.
setTimeout(function () {
dragging.source.removeEventListener('click', disableOneEvent);
});
dragging.init = 0;
}
/**
* Check whether element is interactive.
*
* @return {Boolean}
*/
function isInteractive(element) {
while (element) {
if (interactiveElements.indexOf(element.tagName) != -1) {
return true;
}
element = element.parentNode;
}
return false;
}
/**
* Mouse wheel delta normalization.
*
* @param {Event} event
*
* @return {Int}
*/
function normalizeWheelDelta(event) {
// wheelDelta needed only for IE8-
scrolling.curDelta = ((o.horizontal ? event.deltaY || event.deltaX : event.deltaY) || -event.wheelDelta);
if (transform) {
scrolling.curDelta /= event.deltaMode === 1 ? 3 : 100;
}
return scrolling.curDelta;
}
/**
* Mouse scrolling handler.
*
* @param {Event} event
*
* @return {Void}
*/
function scrollHandler(event) {
event[namespace] = self;
// Ignore if there is no scrolling to be done
if (!o.scrollBy || pos.start === pos.end) {
return;
}
var delta = normalizeWheelDelta(event);
// Trap scrolling only when necessary and/or requested
if (o.scrollTrap || delta > 0 && pos.dest < pos.end || delta < 0 && pos.dest > pos.start) {
stopDefault(event, 1);
}
if (transform) {
self.slideBy(o.scrollBy * delta);
} else {
if (o.horizontal) {
slideeElement.scrollLeft += o.scrollBy * delta;
} else {
slideeElement.scrollTop += o.scrollBy * delta;
}
}
}
/**
* Destroys instance and everything it created.
*
* @return {Void}
*/
self.destroy = function () {
window.removeEventListener('resize', onResize, true);
scrollSource.removeEventListener(wheelEvent, scrollHandler);
// Reset initialized status and return the instance
self.initialized = 0;
return self;
};
function onResize() {
load(false);
}
/**
* Initialize.
*
* @return {Object}
*/
self.init = function () {
if (self.initialized) {
return;
}
// Disallow multiple instances on the same element
if (frame.sly) throw new Error('There is already a Sly instance on this element');
frame.sly = true;
// Set required styles
var movables = [];
if (slideeElement) {
movables.push(slideeElement);
}
if (!transform) {
if (o.horizontal) {
if (layoutManager.desktop && o.hiddenScroll === false) {
slideeElement.classList.add('smoothScrollX');
} else {
slideeElement.classList.add('hiddenScrollX');
}
} else {
if (layoutManager.desktop && o.hiddenScroll === false) {
slideeElement.classList.add('smoothScrollY');
} else {
slideeElement.classList.add('hiddenScrollY');
}
}
} else {
slideeElement.style['will-change'] = 'transform';
}
// Scrolling navigation
scrollSource.addEventListener(wheelEvent, scrollHandler);
if (transform) {
dragInitEventNames.forEach(function(eventName) {
dragSourceElement.addEventListener(eventName, dragInitSlidee);
});
window.addEventListener('resize', onResize, true);
}
// Mark instance as initialized
self.initialized = 1;
// Load
load(true);
// Return instance
return self;
};
};
scrollerFactory.create = function (frame, options) {
var instance = new scrollerFactory(frame, options);
return Promise.resolve(instance);
};
return scrollerFactory;
});

View File

@ -1,9 +1,15 @@
define(['playbackManager'], function (playbackManager) { define(['playbackManager'], function (playbackManager) {
function setActiveDevice(name) {
return function () {
playbackManager.trySetActiveDeviceName(name);
};
}
return function (result) { return function (result) {
result.success = true;
if (result.properties.devicename) { if (result.properties.devicename) {
playbackManager.trySetActiveDeviceName(result.properties.devicename); return setActiveDevice(result.properties.devicename);
} }
return; return;
} }

View File

@ -1,13 +1,18 @@
define(['inputManager'], function (inputManager) { define(['inputManager'], function (inputManager) {
function disableDisplayMirror() {
return function () {
inputManager.trigger('disabledisplaymirror');
};
}
return function (result) { return function (result) {
result.success = true;
switch (result.item.deviceid) { switch (result.item.deviceid) {
case 'displaymirroring': case 'displaymirroring':
inputManager.trigger('disabledisplaymirror'); return disableDisplayMirror();
break; break;
default: default:
result.success = false;
return; return;
} }
} }

View File

@ -1,13 +1,18 @@
define(['inputManager'], function (inputManager) { define(['inputManager'], function (inputManager) {
function enableDisplayMirror() {
return function () {
inputManager.trigger('enabledisplaymirror');
};
}
return function (result) { return function (result) {
result.success = true;
switch (result.item.deviceid) { switch (result.item.deviceid) {
case 'displaymirroring': case 'displaymirroring':
inputManager.trigger('enabledisplaymirror'); return enableDisplayMirror();
break; break;
default: default:
result.success = false;
return; return;
} }
} }

View File

@ -49,58 +49,52 @@
} }
return function (result) { return function (result) {
result.success = false;
var query = { return function () {
var query = {
Limit: result.item.limit || 100, Limit: result.item.limit || 100,
UserId: result.userId, UserId: result.userId,
ExcludeLocationTypes: "Virtual" ExcludeLocationTypes: "Virtual"
}; };
if (result.item.itemType) { if (result.item.itemType) {
query.IncludeItemTypes = result.item.itemType; query.IncludeItemTypes = result.item.itemType;
} }
var apiClient = connectionManager.currentApiClient(); var apiClient = connectionManager.currentApiClient();
if (result.item.sourceid === 'nextup') { if (result.item.sourceid === 'nextup') {
apiClient.getNextUpEpisodes(query).then(function (queryResult) { apiClient.getNextUpEpisodes(query).then(function (queryResult) {
playItems(queryResult.Items, result.item.shuffle);
});
}
if (result.item.shuffle) {
result.item.sortBy = result.sortBy ? 'Random,' + result.item.sortBy : 'Random';
}
query.SortBy = result.item.sortBy;
query.SortOrder = result.item.sortOrder;
query.Recursive = true;
if (result.item.filters.indexOf('unplayed') !== -1) {
query.IsPlayed = false;
}
if (result.item.filters.indexOf('played') !== -1) {
query.IsPlayed = true;
}
if (result.item.filters.indexOf('favorite') !== -1) {
query.Filters = 'IsFavorite';
}
apiClient.getItems(apiClient.getCurrentUserId(), query).then(function (queryResult) {
playItems(queryResult.Items, result.item.shuffle); playItems(queryResult.Items, result.item.shuffle);
}); });
result.success = true; };
return;
}
if (result.item.shuffle) {
result.item.sortBy = result.sortBy ? 'Random,' + result.item.sortBy : 'Random';
}
query.SortBy = result.item.sortBy;
query.SortOrder = result.item.sortOrder;
query.Recursive = true;
if (result.item.filters.indexOf('unplayed') !== -1) {
query.IsPlayed = false;
}
if (result.item.filters.indexOf('played') !== -1) {
query.IsPlayed = true;
}
if (result.item.filters.indexOf('favorite') !== -1) {
query.Filters = 'IsFavorite';
}
apiClient.getItems(apiClient.getCurrentUserId(), query).then(function (queryResult) {
playItems(queryResult.Items, result.item.shuffle);
});
result.success = true;
return;
} }
}); });

View File

@ -3,7 +3,6 @@
return function (result) { return function (result) {
switch (result.item.deviceid) { switch (result.item.deviceid) {
default: default:
result.success = false;
return; return;
} }
} }

View File

@ -1,96 +1,131 @@
define(['inputManager', 'connectionManager', 'embyRouter'], function (inputManager, connectionManager, embyRouter) { define(['inputManager', 'connectionManager', 'embyRouter'], function (inputManager, connectionManager, embyRouter) {
return function (result) { function getMusicCommand(result) {
result.success = true; return function () {
switch (result.item.sourceid) { inputManager.trigger('music');
case 'music': };
inputManager.trigger('music'); }
break;
case 'movies':
if (result.properties.movieName) {
//TODO: Find a way to display movie function getMoviesCommand(result) {
var query = { return function () {
Limit: 1, if (result.properties.movieName) {
UserId: result.userId,
ExcludeLocationTypes: "Virtual",
NameStartsWith: result.item.itemType
};
if (result.item.itemType) { //TODO: Find a way to display movie
query.IncludeItemTypes = result.item.itemType; var query = {
} Limit: 1,
UserId: result.userId,
ExcludeLocationTypes: "Virtual",
NameStartsWith: result.item.itemType
};
var apiClient = connectionManager.currentApiClient(); if (result.item.itemType) {
apiClient.getItems(apiClient.getCurrentUserId(), query).then(function (queryResult) { query.IncludeItemTypes = result.item.itemType;
if (queryResult.Items.length) {
embyRouter.showItem(queryResult.Items[0]);
}
});
} else {
inputManager.trigger('movies');
} }
break; var apiClient = connectionManager.currentApiClient();
case 'tvseries': apiClient.getItems(apiClient.getCurrentUserId(), query).then(function (queryResult) {
inputManager.trigger('tv');
break; if (queryResult.Items.length) {
case 'livetv': embyRouter.showItem(queryResult.Items[0]);
var act = result.item.menuid;
if (act) {
if (act.indexOf('livetv') != -1) {
inputManager.trigger('livetv');
} else if (act.indexOf('guide') != -1) {
inputManager.trigger('guide');
} else if (act.indexOf('channels') != -1) {
inputManager.trigger('livetv');
} else if (act.indexOf('recordings') != -1) {
inputManager.trigger('recordedtv');
} else if (act.indexOf('scheduled') != -1) {
inputManager.trigger('recordedtv');
} else if (act.indexOf('series') != -1) {
inputManager.trigger('recordedtv');
} else {
inputManager.trigger('livetv');
} }
});
} else {
inputManager.trigger('movies');
}
};
}
function getTVCommand(result) {
return function () {
inputManager.trigger('tv');
};
}
function getLiveTVCommand(result) {
return function () {
var act = result.item.menuid;
if (act) {
if (act.indexOf('livetv') != -1) {
inputManager.trigger('livetv');
} else if (act.indexOf('guide') != -1) {
inputManager.trigger('guide');
} else if (act.indexOf('channels') != -1) {
inputManager.trigger('livetv');
} else if (act.indexOf('recordings') != -1) {
inputManager.trigger('recordedtv');
} else if (act.indexOf('scheduled') != -1) {
inputManager.trigger('recordedtv');
} else if (act.indexOf('series') != -1) {
inputManager.trigger('recordedtv');
} else { } else {
inputManager.trigger('livetv'); inputManager.trigger('livetv');
} }
break; } else {
case 'recordings': inputManager.trigger('livetv');
inputManager.trigger('recordedtv'); }
break; };
case 'latestepisodes': }
inputManager.trigger('latestepisodes');
case 'home': function getRecordingsCommand(result) {
var act = result.item.menuid; return function () {
if (act) { inputManager.trigger('recordedtv');
if (act.indexOf('home') != -1) { };
inputManager.trigger('home'); }
}
else if (act.indexOf('nextup') != -1) { function getLatestEpisodesCommand(result) {
inputManager.trigger('nextup'); return function () {
} inputManager.trigger('latestepisodes');
else if (act.indexOf('favorites') != -1) { };
inputManager.trigger('favorites'); }
} else if (act.indexOf('upcoming') != -1) {
inputManager.trigger('upcomingtv'); function getHomeCommand(result) {
} return function () {
else if (act.indexOf('nowplaying') != -1) { var act = result.item.menuid;
inputManager.trigger('nowplaying'); if (act) {
} if (act.indexOf('home') != -1) {
else {
inputManager.trigger('home');
}
} else {
inputManager.trigger('home'); inputManager.trigger('home');
} }
else if (act.indexOf('nextup') != -1) {
inputManager.trigger('nextup');
}
else if (act.indexOf('favorites') != -1) {
inputManager.trigger('favorites');
} else if (act.indexOf('upcoming') != -1) {
inputManager.trigger('upcomingtv');
}
else if (act.indexOf('nowplaying') != -1) {
inputManager.trigger('nowplaying');
}
else {
inputManager.trigger('home');
}
} else {
inputManager.trigger('home');
}
};
}
return function (result) {
switch (result.item.sourceid) {
case 'music':
return getMusicCommand(result);
case 'movies':
return getMoviesCommand(result);
case 'tvseries':
return getTVCommand(result);
case 'livetv':
return getLiveTVCommand(result);
case 'recordings':
return getRecordingsCommand(result);
case 'latestepisodes':
return getLatestEpisodesCommand(result);
case 'home':
return getHomeCommand(result);
case 'group': case 'group':
break; return;
default: default:
result.success = false;
return; return;
} }

View File

@ -1,14 +1,20 @@
define(['inputManager'], function (inputManager) { define(['inputManager'], function (inputManager) {
function toggleDisplayMirror() {
return function () {
inputManager.trigger('toggledisplaymirror');
};
}
return function (result) { return function (result) {
result.success = true;
switch (result.item.deviceid) { switch (result.item.deviceid) {
case 'displaymirroring': case 'displaymirroring':
inputManager.trigger('toggledisplaymirror'); return toggleDisplayMirror();
break; break;
default: default:
result.success = false;
return; return;
} }
} }
}); });

View File

@ -11,11 +11,15 @@ define(['require'], function (require) {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
require([commandPath], function (command) { require([commandPath], function (command) {
command(result);
if (result.success) { var fn = command(result);
if (fn) {
result.fn = fn;
resolve(result); resolve(result);
} else {
reject();
} }
reject();
}); });
}); });
@ -23,38 +27,34 @@ define(['require'], function (require) {
return function (result) { return function (result) {
return new Promise(function (resolve, reject) { switch (result.item.actionid) {
switch (result.item.actionid) { case 'show':
return processCommand('./commands/showcommands.js', result);
case 'show': break;
processCommand('./commands/showcommands.js', result).then(function (result) { resolve(result); }); case 'play':
break; return processCommand('./commands/playcommands.js', result);
case 'play': break;
processCommand('./commands/playcommands.js', result).then(function (result) { resolve(result); }); case 'shuffle':
break; return processCommand('./commands/playcommands.js', result);
case 'shuffle': break;
processCommand('./commands/playcommands.js', result).then(function (result) { resolve(result); }); case 'search':
break; return processCommand('./commands/searchcommands.js', result);
case 'search': break;
processCommand('./commands/searchcommands.js', result).then(function (result) { resolve(result); }); case 'control':
break; return processCommand('./commands/controlcommands.js', result);
case 'control': break;
processCommand('./commands/controlcommands.js', result).then(function (result) { resolve(result); }); case 'enable':
break; return processCommand('./commands/enablecommands.js', result);
case 'enable': break;
processCommand('./commands/enablecommands.js', result).then(function (result) { resolve(result); }); case 'disable':
break; return processCommand('./commands/disablecommands.js', result);
case 'disable': break;
processCommand('./commands/disablecommands.js', result).then(function (result) { resolve(result); }); case 'toggle':
break; return processCommand('./commands/togglecommands.js', result);
case 'toggle': break;
processCommand('./commands/togglecommands.js', result).then(function (result) { resolve(result); }); default:
break; return Promise.reject();
default: }
reject();
return;
}
});
} }
}); });

View File

@ -247,10 +247,15 @@ define(['dialogHelper', './voicereceiver', './voiceprocessor', 'globalize', 'emb
listen(); listen();
} }
function listen() { function listen() {
voicereceiver.listenForCommand(lang || "en-US").then(processInput).then(function (data) { voicereceiver.listenForCommand(lang || "en-US").then(processInput).then(function (result) {
closeDialog(); closeDialog();
// Put a delay here in case navigation/popstate is involved. Allow that to flush out
setTimeout(function () {
result.fn();
}, 1);
}, function (result) { }, function (result) {
if (result.error == 'group') { if (result.error == 'group') {
showVoiceHelp(result.item.groupid, result.groupName); showVoiceHelp(result.item.groupid, result.groupName);

View File

@ -28,11 +28,12 @@
console.log("Command from Grammar Processor", processor); console.log("Command from Grammar Processor", processor);
return voicecommands(processor) return voicecommands(processor)
.then(function (result) { .then(function (result) {
console.log("Result of executed command", result); console.log("Result of executed command", result);
if (result.item.actionid === 'show' && result.item.sourceid === 'group') { if (result.item.actionid === 'show' && result.item.sourceid === 'group') {
return Promise.resolve({ error: "group", item: result.item, groupName: result.name }); return Promise.resolve({ error: "group", item: result.item, groupName: result.name, fn: result.fn });
} else { } else {
return Promise.resolve({ item: result.item }); return Promise.resolve({ item: result.item, fn: result.fn });
} }
}, function () { }, function () {
return Promise.reject({ error: "unrecognized-command", text: text }); return Promise.reject({ error: "unrecognized-command", text: text });

View File

@ -36,7 +36,7 @@
"tag": "v1.5.2", "tag": "v1.5.2",
"commit": "18e8e12dcd9a4560de480562f65935feed334b86" "commit": "18e8e12dcd9a4560de480562f65935feed334b86"
}, },
"_source": "git://github.com/PolymerElements/iron-selector.git", "_source": "git://github.com/polymerelements/iron-selector.git",
"_target": "^1.0.0", "_target": "^1.0.0",
"_originalSource": "PolymerElements/iron-selector" "_originalSource": "polymerelements/iron-selector"
} }

View File

@ -39,6 +39,6 @@
"commit": "8715c83bf04a228de00ec662ed43eb6141e61b91" "commit": "8715c83bf04a228de00ec662ed43eb6141e61b91"
}, },
"_source": "git://github.com/Polymer/polymer.git", "_source": "git://github.com/Polymer/polymer.git",
"_target": "^1.1.0", "_target": "^1.2.0",
"_originalSource": "Polymer/polymer" "_originalSource": "Polymer/polymer"
} }

View File

@ -17,7 +17,6 @@
.touch-menu-la.transition { .touch-menu-la.transition {
/*transition: transform 0.3s ease-out;*/ /*transition: transform 0.3s ease-out;*/
transition: -webkit-transform ease-out 260ms, left ease-out 260ms;
transition: transform ease-out 260ms, left ease-out 260ms; transition: transform ease-out 260ms, left ease-out 260ms;
/*transition: -webkit-transform ease-in-out 0.3s, width ease-in-out 0.3s, visibility 0.3s; /*transition: -webkit-transform ease-in-out 0.3s, width ease-in-out 0.3s, visibility 0.3s;
transition: transform ease-in-out 0.3s, width ease-in-out 0.3s, visibility 0.3s;*/ transition: transform ease-in-out 0.3s, width ease-in-out 0.3s, visibility 0.3s;*/

View File

@ -1,11 +1,13 @@
<div id="liveTvSuggestedPage" data-dom-cache="true" data-role="page" class="page libraryPage liveTvPage pageWithAbsoluteTabs" data-contextname="${HeaderLiveTv}" data-backdroptype="series,movie"> <div id="liveTvSuggestedPage" data-dom-cache="true" data-role="page" class="page libraryPage liveTvPage pageWithAbsoluteTabs" data-contextname="${HeaderLiveTv}" data-backdroptype="series,movie">
<div class="libraryViewNav"> <div class="libraryViewNav">
<button class="pageTabButton is-active" data-index="0">${TabSuggestions}</button> <div class="contentScrollSlider">
<button class="pageTabButton" data-index="1">${TabGuide}</button> <button class="pageTabButton is-active" data-index="0">${TabSuggestions}</button>
<button class="pageTabButton" data-index="2">${TabChannels}</button> <button class="pageTabButton" data-index="1">${TabGuide}</button>
<button class="pageTabButton" data-index="3">${TabRecordings}</button> <button class="pageTabButton" data-index="2">${TabChannels}</button>
<button class="pageTabButton" data-index="4">${TabSeries}</button> <button class="pageTabButton" data-index="3">${TabRecordings}</button>
<button class="pageTabButton" data-index="4">${TabSeries}</button>
</div>
</div> </div>
<div class="is-active pageTabContent ehsContent" id="suggestionsTab" data-index="0"> <div class="is-active pageTabContent ehsContent" id="suggestionsTab" data-index="0">
<div id="activePrograms" class="homePageSection"> <div id="activePrograms" class="homePageSection">

View File

@ -1,12 +1,14 @@
<div id="moviesPage" data-role="page" data-dom-cache="true" class="page libraryPage backdropPage collectionEditorPage pageWithAbsoluteTabs" data-backdroptype="movie"> <div id="moviesPage" data-role="page" data-dom-cache="true" class="page libraryPage backdropPage collectionEditorPage pageWithAbsoluteTabs" data-backdroptype="movie">
<div class="libraryViewNav"> <div class="libraryViewNav">
<button class="pageTabButton is-active" data-index="0">${TabSuggestions}</button> <div class="contentScrollSlider">
<button class="pageTabButton" data-index="1">${TabMovies}</button> <button class="pageTabButton is-active" data-index="0">${TabSuggestions}</button>
<button class="pageTabButton movieTrailersTab" data-index="2">${TabTrailers}</button> <button class="pageTabButton" data-index="1">${TabMovies}</button>
<button class="pageTabButton" data-index="3">${TabCollections}</button> <button class="pageTabButton movieTrailersTab" data-index="2">${TabTrailers}</button>
<button class="pageTabButton" data-index="4">${TabGenres}</button> <button class="pageTabButton" data-index="3">${TabCollections}</button>
<button class="pageTabButton movieStudiosTab" data-index="5">${TabStudios}</button> <button class="pageTabButton" data-index="4">${TabGenres}</button>
<button class="pageTabButton movieStudiosTab" data-index="5">${TabStudios}</button>
</div>
</div> </div>
<div class="is-active pageTabContent ehsContent" id="suggestionsTab" data-index="0"> <div class="is-active pageTabContent ehsContent" id="suggestionsTab" data-index="0">
<div id="resumableSection" class="homePageSection hide"> <div id="resumableSection" class="homePageSection hide">

View File

@ -1,4 +1,4 @@
define(['scrollHelper', 'viewManager', 'appSettings', 'appStorage', 'apphost', 'datetime', 'itemHelper', 'mediaInfo', 'scrollStyles'], function (scrollHelper, viewManager, appSettings, appStorage, appHost, datetime, itemHelper, mediaInfo) { define(['scrollHelper', 'viewManager', 'appSettings', 'appStorage', 'apphost', 'datetime', 'itemHelper', 'mediaInfo', 'scroller', 'scrollStyles'], function (scrollHelper, viewManager, appSettings, appStorage, appHost, datetime, itemHelper, mediaInfo, scroller) {
function parentWithClass(elem, className) { function parentWithClass(elem, className) {
@ -101,17 +101,6 @@
var libraryBrowser = { var libraryBrowser = {
getDefaultPageSize: function (key, defaultValue) { getDefaultPageSize: function (key, defaultValue) {
return 100;
var saved = appStorage.getItem(key || pageSizeKey);
if (saved) {
return parseInt(saved);
}
if (defaultValue) {
return defaultValue;
}
return 100; return 100;
}, },
@ -275,8 +264,6 @@
} }
} }
tabs.classList.add('hiddenScrollX');
tabs.addEventListener('click', function (e) { tabs.addEventListener('click', function (e) {
var current = tabs.querySelector('.is-active'); var current = tabs.querySelector('.is-active');
@ -307,6 +294,8 @@
fadeInRight(newPanel); fadeInRight(newPanel);
} }
tabs.selectedTabIndex = index;
tabs.dispatchEvent(new CustomEvent("tabchange", { tabs.dispatchEvent(new CustomEvent("tabchange", {
detail: { detail: {
selectedTabIndex: index selectedTabIndex: index
@ -314,13 +303,40 @@
})); }));
newPanel.classList.add('is-active'); newPanel.classList.add('is-active');
//scrollHelper.toCenter(tabs, link, true);
}, 120); }, 120);
if (tabs.scroller) {
tabs.scroller.toCenter(link, false);
}
} }
}); });
ownerpage.addEventListener('viewbeforeshow', LibraryBrowser.onTabbedpagebeforeshow); ownerpage.addEventListener('viewbeforeshow', LibraryBrowser.onTabbedpagebeforeshow);
var contentScrollSlider = tabs.querySelector('.contentScrollSlider');
if (contentScrollSlider) {
tabs.scroller = new scroller(tabs, {
horizontal: 1,
itemNav: 0,
mouseDragging: 1,
touchDragging: 1,
slidee: tabs.querySelector('.contentScrollSlider'),
smart: true,
releaseSwing: true,
scrollBy: 200,
speed: 120,
elasticBounds: 1,
dragHandle: 1,
dynamicHandle: 1,
clickBar: 1,
//centerOffset: window.innerWidth * .05,
hiddenScroll: true,
requireAnimation: true
});
tabs.scroller.init();
} else {
tabs.classList.add('hiddenScrollX');
}
}, },
onTabbedpagebeforeshow: function (e) { onTabbedpagebeforeshow: function (e) {
@ -1644,7 +1660,6 @@
getPostersPerRow: function (screenWidth) { getPostersPerRow: function (screenWidth) {
var cache = true;
function getValue(shape) { function getValue(shape) {
switch (shape) { switch (shape) {
@ -1679,24 +1694,37 @@
if (screenWidth >= 770) return 3; if (screenWidth >= 770) return 3;
if (screenWidth >= 420) return 2; if (screenWidth >= 420) return 2;
return 1; return 1;
case 'smallBackdrop':
if (screenWidth >= 1440) return 6;
if (screenWidth >= 1100) return 6;
if (screenWidth >= 800) return 5;
if (screenWidth >= 600) return 4;
if (screenWidth >= 540) return 3;
if (screenWidth >= 420) return 2;
return 1;
case 'homePageSmallBackdrop':
if (screenWidth >= 1440) return 6;
if (screenWidth >= 1100) return 6;
if (screenWidth >= 800) return 5;
if (screenWidth >= 600) return 4;
if (screenWidth >= 540) return 3;
if (screenWidth >= 420) return 2;
return 1;
case 'overflowPortrait':
if (screenWidth >= 1000) return 100 / 23;
if (screenWidth >= 640) return 100 / 36;
return 2.5;
case 'overflowSquare':
if (screenWidth >= 1000) return 100 / 22;
if (screenWidth >= 640) return 100 / 30;
return 100 / 42;
case 'overflowBackdrop':
if (screenWidth >= 1000) return 100 / 40;
if (screenWidth >= 640) return 100 / 60;
return 100 / 84;
default: default:
break; return 4;
} }
var div = document.createElement('div');
div.classList.add('card');
div.classList.add(shape + 'Card');
div.innerHTML = '<div class="cardBox"><div class="cardImage"></div></div>';
document.body.appendChild(div);
var innerWidth = div.querySelector('.cardImage').clientWidth;
if (!innerWidth || isNaN(innerWidth)) {
cache = false;
innerWidth = Math.min(400, screenWidth / 2);
}
var width = screenWidth / innerWidth;
div.parentNode.removeChild(div);
return Math.floor(width);
} }
var info = {}; var info = {};
@ -1705,7 +1733,6 @@
var currentShape = LibraryBrowser.shapes[i]; var currentShape = LibraryBrowser.shapes[i];
info[currentShape] = getValue(currentShape); info[currentShape] = getValue(currentShape);
} }
info.cache = cache;
return info; return info;
}, },
@ -1727,9 +1754,7 @@
var result = LibraryBrowser.getPosterViewInfoInternal(screenWidth); var result = LibraryBrowser.getPosterViewInfoInternal(screenWidth);
result.screenWidth = screenWidth; result.screenWidth = screenWidth;
if (result.cache) { cachedResults.push(result);
cachedResults.push(result);
}
return result; return result;
}, },
@ -1741,31 +1766,14 @@
var result = {}; var result = {};
result.screenWidth = screenWidth; result.screenWidth = screenWidth;
if (AppInfo.hasLowImageBandwidth) {
if (!AppInfo.isNativeApp) {
screenWidth *= .75;
}
} else {
screenWidth *= 1.2;
}
var roundTo = 100;
for (var i = 0, length = LibraryBrowser.shapes.length; i < length; i++) { for (var i = 0, length = LibraryBrowser.shapes.length; i < length; i++) {
var currentShape = LibraryBrowser.shapes[i]; var currentShape = LibraryBrowser.shapes[i];
var shapeWidth = screenWidth / imagesPerRow[currentShape]; var shapeWidth = screenWidth / imagesPerRow[currentShape];
if (!browserInfo.mobile) {
shapeWidth = Math.round(shapeWidth / roundTo) * roundTo;
}
result[currentShape + 'Width'] = Math.round(shapeWidth); result[currentShape + 'Width'] = Math.round(shapeWidth);
} }
result.cache = imagesPerRow.cache;
return result; return result;
}, },
@ -1941,7 +1949,7 @@
width = posterWidth; width = posterWidth;
height = primaryImageAspectRatio ? Math.round(posterWidth / primaryImageAspectRatio) : null; height = primaryImageAspectRatio ? Math.round(posterWidth / primaryImageAspectRatio) : null;
imgUrl = ApiClient.getImageUrl(imageItem.Id, { imgUrl = ApiClient.getScaledImageUrl(imageItem.Id, {
type: "Primary", type: "Primary",
maxHeight: height, maxHeight: height,
maxWidth: width, maxWidth: width,
@ -2026,7 +2034,7 @@
width = posterWidth; width = posterWidth;
height = primaryImageAspectRatio ? Math.round(posterWidth / primaryImageAspectRatio) : null; height = primaryImageAspectRatio ? Math.round(posterWidth / primaryImageAspectRatio) : null;
imgUrl = ApiClient.getImageUrl(imageItem.Id, { imgUrl = ApiClient.getScaledImageUrl(imageItem.Id, {
type: "Primary", type: "Primary",
maxHeight: height, maxHeight: height,
maxWidth: width, maxWidth: width,
@ -2044,7 +2052,7 @@
} }
else if (imageItem.ParentPrimaryImageTag) { else if (imageItem.ParentPrimaryImageTag) {
imgUrl = ApiClient.getImageUrl(imageItem.ParentPrimaryImageItemId, { imgUrl = ApiClient.getScaledImageUrl(imageItem.ParentPrimaryImageItemId, {
type: "Primary", type: "Primary",
maxWidth: posterWidth, maxWidth: posterWidth,
tag: item.ParentPrimaryImageTag, tag: item.ParentPrimaryImageTag,

View File

@ -53,7 +53,7 @@
page.querySelector('#selectMaxBitrate').value = appSettings.maxStreamingBitrate(); page.querySelector('#selectMaxBitrate').value = appSettings.maxStreamingBitrate();
} }
page.querySelector('#selectMaxChromecastBitrate').value = appSettings.maxChromecastBitrate(); page.querySelector('#selectMaxChromecastBitrate').value = appSettings.maxChromecastBitrate() || '';
Dashboard.hideLoadingMsg(); Dashboard.hideLoadingMsg();
}); });

View File

@ -1179,6 +1179,10 @@ var Dashboard = {
quality -= 10; quality -= 10;
} }
if (browserInfo.mobile) {
quality -= 10;
}
if (AppInfo.hasLowImageBandwidth) { if (AppInfo.hasLowImageBandwidth) {
// The native app can handle a little bit more than safari // The native app can handle a little bit more than safari
@ -1963,6 +1967,7 @@ var AppInfo = {};
define("swiper", [bowerPath + "/Swiper/dist/js/swiper.min", "css!" + bowerPath + "/Swiper/dist/css/swiper.min"], returnFirstDependency); define("swiper", [bowerPath + "/Swiper/dist/js/swiper.min", "css!" + bowerPath + "/Swiper/dist/css/swiper.min"], returnFirstDependency);
define("scroller", [embyWebComponentsBowerPath + "/scroller/smoothscroller"], returnFirstDependency);
define("toast", [embyWebComponentsBowerPath + "/toast/toast"], returnFirstDependency); define("toast", [embyWebComponentsBowerPath + "/toast/toast"], returnFirstDependency);
define("scrollHelper", [embyWebComponentsBowerPath + "/scrollhelper"], returnFirstDependency); define("scrollHelper", [embyWebComponentsBowerPath + "/scrollhelper"], returnFirstDependency);

View File

@ -18,7 +18,7 @@
view.querySelector('.fldSelectEncoderPathType').classList.remove('hide'); view.querySelector('.fldSelectEncoderPathType').classList.remove('hide');
} }
/*if (systemInfo.OperatingSystem == 'Windows' && systemInfo.SystemArchitecture != 'Arm') { if (systemInfo.OperatingSystem == 'Windows' && systemInfo.SystemArchitecture != 'Arm') {
view.querySelector('.suggestedLocation').innerHTML = Globalize.translate('FFmpegSuggestedDownload', '<a target="_blank" href="https://ffmpeg.zeranoe.com/builds">https://ffmpeg.zeranoe.com</a>'); view.querySelector('.suggestedLocation').innerHTML = Globalize.translate('FFmpegSuggestedDownload', '<a target="_blank" href="https://ffmpeg.zeranoe.com/builds">https://ffmpeg.zeranoe.com</a>');
@ -29,7 +29,7 @@
instructions = 'Download FFmpeg 64-Bit Static'; instructions = 'Download FFmpeg 64-Bit Static';
} }
} else*/ if (systemInfo.OperatingSystem == 'Linux' && systemInfo.SystemArchitecture != 'Arm') { } else if (systemInfo.OperatingSystem == 'Linux' && systemInfo.SystemArchitecture != 'Arm') {
view.querySelector('.suggestedLocation').innerHTML = Globalize.translate('FFmpegSuggestedDownload', '<a target="_blank" href="http://johnvansickle.com/ffmpeg">http://johnvansickle.com/ffmpeg</a>'); view.querySelector('.suggestedLocation').innerHTML = Globalize.translate('FFmpegSuggestedDownload', '<a target="_blank" href="http://johnvansickle.com/ffmpeg">http://johnvansickle.com/ffmpeg</a>');

View File

@ -1,14 +1,17 @@
<div id="tvRecommendedPage" data-dom-cache="true" data-role="page" class="page libraryPage backdropPage pageWithAbsoluteTabs" data-backdroptype="series"> <div id="tvRecommendedPage" data-dom-cache="true" data-role="page" class="page libraryPage backdropPage pageWithAbsoluteTabs" data-backdroptype="series">
<div class="libraryViewNav"> <div class="libraryViewNav">
<button class="pageTabButton is-active" data-index="0">${TabSuggestions}</button> <div class="contentScrollSlider">
<button class="pageTabButton" data-index="1">${TabLatest}</button> <button class="pageTabButton is-active" data-index="0">${TabSuggestions}</button>
<button class="pageTabButton" data-index="2">${TabUpcoming}</button> <button class="pageTabButton" data-index="1">${TabLatest}</button>
<button class="pageTabButton" data-index="3">${TabShows}</button> <button class="pageTabButton" data-index="2">${TabUpcoming}</button>
<button class="pageTabButton tvEpisodesTab" data-index="4">${TabEpisodes}</button> <button class="pageTabButton" data-index="3">${TabShows}</button>
<button class="pageTabButton" data-index="5">${TabGenres}</button> <button class="pageTabButton tvEpisodesTab" data-index="4">${TabEpisodes}</button>
<button class="pageTabButton" data-index="6">${TabNetworks}</button> <button class="pageTabButton" data-index="5">${TabGenres}</button>
<button class="pageTabButton" data-index="6">${TabNetworks}</button>
</div>
</div> </div>
<div class="is-active pageTabContent ehsContent" id="suggestionsTab" data-index="0"> <div class="is-active pageTabContent ehsContent" id="suggestionsTab" data-index="0">
<div id="resumableSection" class="homePageSection"> <div id="resumableSection" class="homePageSection">
<div> <div>