mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
sort through the define statements
This commit is contained in:
parent
02a23ffaee
commit
a260e33182
8 changed files with 54 additions and 39 deletions
310
src/scripts/browser.js
Normal file
310
src/scripts/browser.js
Normal file
|
@ -0,0 +1,310 @@
|
|||
define([], function () {
|
||||
'use strict';
|
||||
|
||||
function isTv() {
|
||||
|
||||
// This is going to be really difficult to get right
|
||||
var userAgent = navigator.userAgent.toLowerCase();
|
||||
|
||||
if (userAgent.indexOf('tv') !== -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (userAgent.indexOf('samsungbrowser') !== -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (userAgent.indexOf('nintendo') !== -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (userAgent.indexOf('viera') !== -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (userAgent.indexOf('webos') !== -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function isMobile(userAgent) {
|
||||
|
||||
var terms = [
|
||||
'mobi',
|
||||
'ipad',
|
||||
'iphone',
|
||||
'ipod',
|
||||
'silk',
|
||||
'gt-p1000',
|
||||
'nexus 7',
|
||||
'kindle fire',
|
||||
'opera mini'
|
||||
];
|
||||
|
||||
var lower = userAgent.toLowerCase();
|
||||
|
||||
for (var i = 0, length = terms.length; i < length; i++) {
|
||||
if (lower.indexOf(terms[i]) !== -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function isStyleSupported(prop, value) {
|
||||
|
||||
if (typeof window === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If no value is supplied, use "inherit"
|
||||
value = arguments.length === 2 ? value : 'inherit';
|
||||
// Try the native standard method first
|
||||
if ('CSS' in window && 'supports' in window.CSS) {
|
||||
return window.CSS.supports(prop, value);
|
||||
}
|
||||
// Check Opera's native method
|
||||
if ('supportsCSS' in window) {
|
||||
return window.supportsCSS(prop, value);
|
||||
}
|
||||
|
||||
// need try/catch because it's failing on tizen
|
||||
|
||||
try {
|
||||
// Convert to camel-case for DOM interactions
|
||||
var camel = prop.replace(/-([a-z]|[0-9])/ig, function (all, letter) {
|
||||
return (letter + '').toUpperCase();
|
||||
});
|
||||
// Check if the property is supported
|
||||
var support = (camel in el.style);
|
||||
// Create test element
|
||||
var el = document.createElement('div');
|
||||
// Assign the property and value to invoke
|
||||
// the CSS interpreter
|
||||
el.style.cssText = prop + ':' + value;
|
||||
// Ensure both the property and value are
|
||||
// supported and return
|
||||
return support && (el.style[camel] !== '');
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function hasKeyboard(browser) {
|
||||
|
||||
if (browser.touch) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (browser.xboxOne) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (browser.ps4) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (browser.edgeUwp) {
|
||||
// This is OK for now, but this won't always be true
|
||||
// Should we use this?
|
||||
// https://gist.github.com/wagonli/40d8a31bd0d6f0dd7a5d
|
||||
return true;
|
||||
}
|
||||
|
||||
if (browser.tv) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function iOSversion() {
|
||||
if (/iP(hone|od|ad)/.test(navigator.platform)) {
|
||||
// supports iOS 2.0 and later: <http://bit.ly/TJjs1V>
|
||||
var v = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/);
|
||||
return [parseInt(v[1], 10), parseInt(v[2], 10), parseInt(v[3] || 0, 10)];
|
||||
}
|
||||
}
|
||||
|
||||
var _supportsCssAnimation;
|
||||
var _supportsCssAnimationWithPrefix;
|
||||
function supportsCssAnimation(allowPrefix) {
|
||||
|
||||
if (allowPrefix) {
|
||||
if (_supportsCssAnimationWithPrefix === true || _supportsCssAnimationWithPrefix === false) {
|
||||
return _supportsCssAnimationWithPrefix;
|
||||
}
|
||||
} else {
|
||||
if (_supportsCssAnimation === true || _supportsCssAnimation === false) {
|
||||
return _supportsCssAnimation;
|
||||
}
|
||||
}
|
||||
|
||||
var animation = false,
|
||||
animationstring = 'animation',
|
||||
keyframeprefix = '',
|
||||
domPrefixes = ['Webkit', 'O', 'Moz'],
|
||||
pfx = '',
|
||||
elm = document.createElement('div');
|
||||
|
||||
if (elm.style.animationName !== undefined) { animation = true; }
|
||||
|
||||
if (animation === false && allowPrefix) {
|
||||
for (var i = 0; i < domPrefixes.length; i++) {
|
||||
if (elm.style[domPrefixes[i] + 'AnimationName'] !== undefined) {
|
||||
pfx = domPrefixes[i];
|
||||
animationstring = pfx + 'Animation';
|
||||
keyframeprefix = '-' + pfx.toLowerCase() + '-';
|
||||
animation = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allowPrefix) {
|
||||
_supportsCssAnimationWithPrefix = animation;
|
||||
return _supportsCssAnimationWithPrefix;
|
||||
} else {
|
||||
_supportsCssAnimation = animation;
|
||||
return _supportsCssAnimation;
|
||||
}
|
||||
}
|
||||
|
||||
var uaMatch = 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) ||
|
||||
[];
|
||||
|
||||
var versionMatch = /(version)[ \/]([\w.]+)/.exec(ua);
|
||||
|
||||
var platform_match = /(ipad)/.exec(ua) ||
|
||||
/(iphone)/.exec(ua) ||
|
||||
/(windows)/.exec(ua) ||
|
||||
/(android)/.exec(ua) ||
|
||||
[];
|
||||
|
||||
var browser = match[1] || "";
|
||||
|
||||
if (browser === "edge") {
|
||||
platform_match = [""];
|
||||
} else {
|
||||
if (ua.indexOf("windows phone") !== -1 || ua.indexOf("iemobile") !== -1) {
|
||||
|
||||
// http://www.neowin.net/news/ie11-fakes-user-agent-to-fool-gmail-in-windows-phone-81-gdr1-update
|
||||
browser = "msie";
|
||||
}
|
||||
else if (ua.indexOf("like gecko") !== -1 && ua.indexOf('webkit') === -1 && ua.indexOf('opera') === -1 && ua.indexOf('chrome') === -1 && ua.indexOf('safari') === -1) {
|
||||
browser = "msie";
|
||||
}
|
||||
}
|
||||
|
||||
if (browser === 'opr') {
|
||||
browser = 'opera';
|
||||
}
|
||||
|
||||
var version;
|
||||
if (versionMatch && versionMatch.length > 2) {
|
||||
version = versionMatch[2];
|
||||
}
|
||||
|
||||
version = version || match[2] || "0";
|
||||
|
||||
var versionMajor = parseInt(version.split('.')[0]);
|
||||
|
||||
if (isNaN(versionMajor)) {
|
||||
versionMajor = 0;
|
||||
}
|
||||
|
||||
return {
|
||||
browser: browser,
|
||||
version: version,
|
||||
platform: platform_match[0] || "",
|
||||
versionMajor: versionMajor
|
||||
};
|
||||
};
|
||||
|
||||
var userAgent = navigator.userAgent;
|
||||
|
||||
var matched = uaMatch(userAgent);
|
||||
var browser = {};
|
||||
|
||||
if (matched.browser) {
|
||||
browser[matched.browser] = true;
|
||||
browser.version = matched.version;
|
||||
browser.versionMajor = matched.versionMajor;
|
||||
}
|
||||
|
||||
if (matched.platform) {
|
||||
browser[matched.platform] = true;
|
||||
}
|
||||
|
||||
if (!browser.chrome && !browser.msie && !browser.edge && !browser.opera && userAgent.toLowerCase().indexOf("webkit") !== -1) {
|
||||
browser.safari = true;
|
||||
}
|
||||
|
||||
if (userAgent.toLowerCase().indexOf("playstation 4") !== -1) {
|
||||
browser.ps4 = true;
|
||||
browser.tv = true;
|
||||
}
|
||||
|
||||
if (isMobile(userAgent)) {
|
||||
browser.mobile = true;
|
||||
}
|
||||
|
||||
browser.xboxOne = userAgent.toLowerCase().indexOf('xbox') !== -1;
|
||||
browser.animate = typeof document !== 'undefined' && document.documentElement.animate != null;
|
||||
browser.tizen = userAgent.toLowerCase().indexOf('tizen') !== -1 || self.tizen != null;
|
||||
browser.web0s = userAgent.toLowerCase().indexOf('Web0S'.toLowerCase()) !== -1;
|
||||
browser.edgeUwp = browser.edge && (userAgent.toLowerCase().indexOf('msapphost') !== -1 || userAgent.toLowerCase().indexOf('webview') !== -1);
|
||||
|
||||
if (!browser.tizen) {
|
||||
browser.orsay = userAgent.toLowerCase().indexOf('smarthub') !== -1;
|
||||
}
|
||||
|
||||
if (browser.edgeUwp) {
|
||||
browser.edge = true;
|
||||
}
|
||||
|
||||
browser.tv = isTv();
|
||||
browser.operaTv = browser.tv && userAgent.toLowerCase().indexOf('opr/') !== -1;
|
||||
|
||||
if (!isStyleSupported('display', 'flex')) {
|
||||
browser.noFlex = true;
|
||||
}
|
||||
|
||||
if (browser.mobile || browser.tv) {
|
||||
browser.slow = true;
|
||||
}
|
||||
|
||||
if (typeof document !== 'undefined') {
|
||||
if (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
|
||||
browser.touch = true;
|
||||
}
|
||||
}
|
||||
|
||||
browser.keyboard = hasKeyboard(browser);
|
||||
browser.supportsCssAnimation = supportsCssAnimation;
|
||||
|
||||
browser.osx = userAgent.toLowerCase().indexOf('os x') !== -1;
|
||||
browser.iOS = browser.ipad || browser.iphone || browser.ipod;
|
||||
|
||||
if (browser.iOS) {
|
||||
browser.iOSVersion = iOSversion();
|
||||
browser.iOSVersion = browser.iOSVersion[0] + (browser.iOSVersion[1] / 10);
|
||||
}
|
||||
|
||||
browser.chromecast = browser.chrome && userAgent.toLowerCase().indexOf('crkey') !== -1;
|
||||
|
||||
return browser;
|
||||
});
|
916
src/scripts/browserdeviceprofile.js
Normal file
916
src/scripts/browserdeviceprofile.js
Normal file
|
@ -0,0 +1,916 @@
|
|||
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 true;
|
||||
}
|
||||
|
||||
var userAgent = navigator.userAgent.toLowerCase();
|
||||
|
||||
if (browser.chromecast) {
|
||||
|
||||
var isChromecastUltra = userAgent.indexOf('aarch64') !== -1;
|
||||
if (isChromecastUltra) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Unfortunately haven't yet found a canPlayType for proper detection
|
||||
if (browser.iOS && (browser.iOSVersion || 0) >= 11) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !!(videoTestElement.canPlayType && videoTestElement.canPlayType('video/hevc; codecs="hevc, aac"').replace(/no/, ''));
|
||||
}
|
||||
|
||||
var _supportsTextTracks;
|
||||
function supportsTextTracks() {
|
||||
|
||||
if (browser.tizen || browser.orsay) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_supportsTextTracks == null) {
|
||||
_supportsTextTracks = document.createElement('video').textTracks != null;
|
||||
}
|
||||
|
||||
// For now, until ready
|
||||
return _supportsTextTracks;
|
||||
}
|
||||
|
||||
var _canPlayHls;
|
||||
function canPlayHls(src) {
|
||||
|
||||
if (_canPlayHls == null) {
|
||||
_canPlayHls = canPlayNativeHls() || canPlayHlsWithMSE();
|
||||
}
|
||||
return _canPlayHls;
|
||||
}
|
||||
|
||||
function canPlayNativeHls() {
|
||||
|
||||
if (browser.tizen || browser.orsay) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var media = document.createElement('video');
|
||||
if (media.canPlayType('application/x-mpegURL').replace(/no/, '') ||
|
||||
media.canPlayType('application/vnd.apple.mpegURL').replace(/no/, '')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function canPlayHlsWithMSE() {
|
||||
if (window.MediaSource != null) {
|
||||
// text tracks don’t work with this in firefox
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function canPlayAudioFormat(format) {
|
||||
|
||||
var typeString;
|
||||
|
||||
if (format === 'flac') {
|
||||
if (browser.tizen || browser.orsay || browser.web0s) {
|
||||
return true;
|
||||
}
|
||||
if (browser.edgeUwp) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
else if (format === 'wma') {
|
||||
if (browser.tizen || browser.orsay) {
|
||||
return true;
|
||||
}
|
||||
if (browser.edgeUwp) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
else if (format === 'opus') {
|
||||
typeString = 'audio/ogg; codecs="opus"';
|
||||
|
||||
if (document.createElement('audio').canPlayType(typeString).replace(/no/, '')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
else if (format === 'mp2') {
|
||||
|
||||
// For now
|
||||
return false;
|
||||
}
|
||||
|
||||
if (format === 'webma') {
|
||||
typeString = 'audio/webm';
|
||||
} else if (format === 'mp2') {
|
||||
typeString = 'audio/mpeg';
|
||||
} else if (format === 'ogg' || format === 'oga') {
|
||||
|
||||
// chrome says probably, but seeing failures
|
||||
if (browser.chrome) {
|
||||
return false;
|
||||
}
|
||||
typeString = 'audio/' + format;
|
||||
|
||||
} else {
|
||||
typeString = 'audio/' + format;
|
||||
}
|
||||
|
||||
if (document.createElement('audio').canPlayType(typeString).replace(/no/, '')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function testCanPlayMkv(videoTestElement) {
|
||||
|
||||
if (browser.tizen || browser.orsay || browser.web0s) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (videoTestElement.canPlayType('video/x-matroska').replace(/no/, '') ||
|
||||
videoTestElement.canPlayType('video/mkv').replace(/no/, '')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var userAgent = navigator.userAgent.toLowerCase();
|
||||
|
||||
// Unfortunately there's no real way to detect mkv support
|
||||
if (browser.chrome) {
|
||||
|
||||
// Not supported on opera tv
|
||||
if (browser.operaTv) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Filter out browsers based on chromium that don't support mkv
|
||||
if (userAgent.indexOf('vivaldi') !== -1 || userAgent.indexOf('opera') !== -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (browser.edgeUwp) {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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 getFlvMseDirectPlayProfile() {
|
||||
|
||||
var videoAudioCodecs = ['aac'];
|
||||
|
||||
if (!browser.edge && !browser.msie) {
|
||||
videoAudioCodecs.push('mp3');
|
||||
}
|
||||
|
||||
return {
|
||||
Container: 'flv',
|
||||
Type: 'Video',
|
||||
VideoCodec: 'h264',
|
||||
AudioCodec: videoAudioCodecs.join(',')
|
||||
};
|
||||
}
|
||||
|
||||
function getDirectPlayProfileForVideoContainer(container, videoAudioCodecs, videoTestElement, options) {
|
||||
|
||||
var supported = false;
|
||||
var profileContainer = container;
|
||||
var 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;
|
||||
//if (!supported && window.MediaSource != null && window.MediaSource.isTypeSupported('video/mp4; codecs="avc1.42E01E,mp4a.40.2"')) {
|
||||
// return getFlvMseDirectPlayProfile();
|
||||
//}
|
||||
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');
|
||||
if (supportsVc1()) {
|
||||
videoCodecs.push('vc1');
|
||||
}
|
||||
if (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');
|
||||
if (canPlayH265(videoTestElement, options)) {
|
||||
videoCodecs.push('h265');
|
||||
videoCodecs.push('hevc');
|
||||
}
|
||||
if (supportsVc1()) {
|
||||
videoCodecs.push('vc1');
|
||||
}
|
||||
if (supportsMpeg2Video()) {
|
||||
videoCodecs.push('mpeg2video');
|
||||
}
|
||||
profileContainer = 'ts,mpegts';
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!supported) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
Container: profileContainer,
|
||||
Type: 'Video',
|
||||
VideoCodec: videoCodecs.join(','),
|
||||
AudioCodec: videoAudioCodecs.join(',')
|
||||
};
|
||||
}
|
||||
|
||||
function getMaxBitrate() {
|
||||
|
||||
return 120000000;
|
||||
}
|
||||
|
||||
function getGlobalMaxVideoBitrate() {
|
||||
|
||||
var userAgent = navigator.userAgent.toLowerCase();
|
||||
|
||||
if (browser.chromecast) {
|
||||
|
||||
var isChromecastUltra = userAgent.indexOf('aarch64') !== -1;
|
||||
if (isChromecastUltra) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// This is a hack to try and detect chromecast on vizio
|
||||
if (self.screen && self.screen.width >= 3800) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return 30000000;
|
||||
}
|
||||
|
||||
var isTizenFhd = false;
|
||||
if (browser.tizen) {
|
||||
try {
|
||||
var isTizenUhd = webapis.productinfo.isUdPanelSupported();
|
||||
isTizenFhd = !isTizenUhd;
|
||||
console.log("isTizenFhd = " + isTizenFhd);
|
||||
} catch (error) {
|
||||
console.log("isUdPanelSupported() error code = " + error.code);
|
||||
}
|
||||
}
|
||||
|
||||
return browser.ps4 ? 8000000 :
|
||||
(browser.xboxOne ? 12000000 :
|
||||
(browser.edgeUwp ? null :
|
||||
(browser.tizen && isTizenFhd ? 20000000 : null)));
|
||||
}
|
||||
|
||||
function supportsAc3(videoTestElement) {
|
||||
|
||||
if (browser.edgeUwp || browser.tizen || browser.orsay || browser.web0s) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (videoTestElement.canPlayType('audio/mp4; codecs="ac-3"').replace(/no/, '') && !browser.osx && !browser.iOS);
|
||||
}
|
||||
|
||||
function supportsEac3(videoTestElement) {
|
||||
|
||||
if (browser.tizen || browser.orsay || browser.web0s) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return videoTestElement.canPlayType('audio/mp4; codecs="ec-3"').replace(/no/, '');
|
||||
}
|
||||
|
||||
return function (options) {
|
||||
|
||||
options = options || {};
|
||||
var physicalAudioChannels = options.audioChannels || (browser.tv || browser.ps4 || browser.xboxOne ? 6 : 2);
|
||||
|
||||
var bitrateSetting = getMaxBitrate();
|
||||
|
||||
var videoTestElement = document.createElement('video');
|
||||
|
||||
var canPlayVp8 = videoTestElement.canPlayType('video/webm; codecs="vp8"').replace(/no/, '');
|
||||
var canPlayVp9 = videoTestElement.canPlayType('video/webm; codecs="vp9"').replace(/no/, '');
|
||||
var webmAudioCodecs = ['vorbis'];
|
||||
|
||||
var canPlayMkv = testCanPlayMkv(videoTestElement);
|
||||
|
||||
var profile = {};
|
||||
|
||||
profile.MaxStreamingBitrate = bitrateSetting;
|
||||
profile.MaxStaticBitrate = 100000000;
|
||||
profile.MusicStreamingTranscodingBitrate = Math.min(bitrateSetting, 192000);
|
||||
|
||||
profile.DirectPlayProfiles = [];
|
||||
|
||||
var videoAudioCodecs = [];
|
||||
var hlsVideoAudioCodecs = [];
|
||||
|
||||
var supportsMp3VideoAudio = videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.69"').replace(/no/, '') ||
|
||||
videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.6B"').replace(/no/, '');
|
||||
|
||||
// Not sure how to test for this
|
||||
var supportsMp2VideoAudio = browser.edgeUwp || browser.tizen || browser.orsay || browser.web0s;
|
||||
|
||||
var maxVideoWidth = browser.xboxOne ?
|
||||
(self.screen ? self.screen.width : null) :
|
||||
null;
|
||||
|
||||
if (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) {
|
||||
// prioritize this first
|
||||
videoAudioCodecs.push('aac');
|
||||
}
|
||||
|
||||
// Only put mp3 first if mkv support is there
|
||||
// Otherwise with HLS and mp3 audio we're seeing some browsers
|
||||
// safari is lying
|
||||
if (supportsAc3(videoTestElement)) {
|
||||
|
||||
videoAudioCodecs.push('ac3');
|
||||
|
||||
var eAc3 = supportsEac3(videoTestElement);
|
||||
if (eAc3) {
|
||||
videoAudioCodecs.push('eac3');
|
||||
}
|
||||
|
||||
// This works in edge desktop, but not mobile
|
||||
// TODO: Retest this on mobile
|
||||
var supportsAc3InHls = (!browser.edge || !browser.touch || browser.edgeUwp);
|
||||
if (supportsAc3InHls) {
|
||||
hlsVideoAudioCodecs.push('ac3');
|
||||
if (eAc3) {
|
||||
hlsVideoAudioCodecs.push('eac3');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (canPlayAacVideoAudio && browser.chromecast && videoAudioCodecs.indexOf('aac') === -1) {
|
||||
// prioritize this first
|
||||
videoAudioCodecs.push('aac');
|
||||
}
|
||||
|
||||
if (supportsMp3VideoAudio) {
|
||||
videoAudioCodecs.push('mp3');
|
||||
|
||||
// PS4 fails to load HLS with mp3 audio
|
||||
if (!browser.ps4) {
|
||||
|
||||
// mp3 encoder only supports 2 channels, so only make that preferred if we're only requesting 2 channels
|
||||
// Also apply it for chromecast because it no longer supports AAC 5.1
|
||||
if (physicalAudioChannels <= 2) {
|
||||
hlsVideoAudioCodecs.push('mp3');
|
||||
}
|
||||
}
|
||||
}
|
||||
if (canPlayAacVideoAudio) {
|
||||
|
||||
if (videoAudioCodecs.indexOf('aac') === -1) {
|
||||
videoAudioCodecs.push('aac');
|
||||
}
|
||||
|
||||
hlsVideoAudioCodecs.push('aac');
|
||||
}
|
||||
if (supportsMp3VideoAudio) {
|
||||
// PS4 fails to load HLS with mp3 audio
|
||||
if (!browser.ps4) {
|
||||
if (hlsVideoAudioCodecs.indexOf('mp3') === -1) {
|
||||
hlsVideoAudioCodecs.push('mp3');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (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');
|
||||
|
||||
// DTS audio not supported in 2018 models (Tizen 4.0)
|
||||
if (v && parseFloat(v) >= parseFloat('4.0')) {
|
||||
supportsDts = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (supportsDts) {
|
||||
videoAudioCodecs.push('dca');
|
||||
videoAudioCodecs.push('dts');
|
||||
}
|
||||
|
||||
if (browser.tizen || browser.orsay || browser.web0s) {
|
||||
videoAudioCodecs.push('pcm_s16le');
|
||||
videoAudioCodecs.push('pcm_s24le');
|
||||
}
|
||||
|
||||
if (options.supportsTrueHd) {
|
||||
videoAudioCodecs.push('truehd');
|
||||
}
|
||||
|
||||
if (browser.tizen || browser.orsay) {
|
||||
videoAudioCodecs.push('aac_latm');
|
||||
}
|
||||
|
||||
if (canPlayAudioFormat('opus')) {
|
||||
videoAudioCodecs.push('opus');
|
||||
hlsVideoAudioCodecs.push('opus');
|
||||
webmAudioCodecs.push('opus');
|
||||
}
|
||||
|
||||
if (canPlayAudioFormat('flac')) {
|
||||
videoAudioCodecs.push('flac');
|
||||
}
|
||||
|
||||
videoAudioCodecs = videoAudioCodecs.filter(function (c) {
|
||||
return (options.disableVideoAudioCodecs || []).indexOf(c) === -1;
|
||||
});
|
||||
|
||||
hlsVideoAudioCodecs = hlsVideoAudioCodecs.filter(function (c) {
|
||||
return (options.disableHlsVideoAudioCodecs || []).indexOf(c) === -1;
|
||||
});
|
||||
|
||||
var mp4VideoCodecs = [];
|
||||
var hlsVideoCodecs = [];
|
||||
|
||||
if (canPlayH264(videoTestElement)) {
|
||||
mp4VideoCodecs.push('h264');
|
||||
hlsVideoCodecs.push('h264');
|
||||
}
|
||||
if (canPlayH265(videoTestElement, options)) {
|
||||
mp4VideoCodecs.push('h265');
|
||||
mp4VideoCodecs.push('hevc');
|
||||
|
||||
if (browser.tizen || browser.web0s) {
|
||||
hlsVideoCodecs.push('h265');
|
||||
hlsVideoCodecs.push('hevc');
|
||||
}
|
||||
}
|
||||
|
||||
if (supportsMpeg2Video()) {
|
||||
mp4VideoCodecs.push('mpeg2video');
|
||||
}
|
||||
|
||||
if (supportsVc1()) {
|
||||
mp4VideoCodecs.push('vc1');
|
||||
}
|
||||
|
||||
if (browser.tizen || browser.orsay) {
|
||||
mp4VideoCodecs.push('msmpeg4v2');
|
||||
}
|
||||
|
||||
if (canPlayVp8) {
|
||||
mp4VideoCodecs.push('vp8');
|
||||
}
|
||||
if (canPlayVp9) {
|
||||
mp4VideoCodecs.push('vp9');
|
||||
}
|
||||
|
||||
if (canPlayVp8 || browser.tizen || browser.orsay) {
|
||||
videoAudioCodecs.push('vorbis');
|
||||
}
|
||||
|
||||
if (mp4VideoCodecs.length) {
|
||||
profile.DirectPlayProfiles.push({
|
||||
Container: 'mp4,m4v',
|
||||
Type: 'Video',
|
||||
VideoCodec: mp4VideoCodecs.join(','),
|
||||
AudioCodec: videoAudioCodecs.join(',')
|
||||
});
|
||||
}
|
||||
|
||||
if (canPlayMkv && mp4VideoCodecs.length) {
|
||||
profile.DirectPlayProfiles.push({
|
||||
Container: 'mkv',
|
||||
Type: 'Video',
|
||||
VideoCodec: mp4VideoCodecs.join(','),
|
||||
AudioCodec: videoAudioCodecs.join(',')
|
||||
});
|
||||
}
|
||||
|
||||
// These are formats we can't test for but some devices will support
|
||||
['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 i != null;
|
||||
}).forEach(function (i) {
|
||||
profile.DirectPlayProfiles.push(i);
|
||||
});
|
||||
|
||||
['opus', 'mp3', 'mp2', 'aac', 'flac', 'alac', 'webma', 'wma', 'wav', 'ogg', 'oga'].filter(canPlayAudioFormat).forEach(function (audioFormat) {
|
||||
|
||||
if (audioFormat === 'mp2') {
|
||||
|
||||
profile.DirectPlayProfiles.push({
|
||||
Container: 'mp2,mp3',
|
||||
Type: 'Audio',
|
||||
AudioCodec: audioFormat
|
||||
});
|
||||
}
|
||||
|
||||
else if (audioFormat === 'mp3') {
|
||||
|
||||
profile.DirectPlayProfiles.push({
|
||||
Container: audioFormat,
|
||||
Type: 'Audio',
|
||||
AudioCodec: audioFormat
|
||||
});
|
||||
|
||||
} else {
|
||||
profile.DirectPlayProfiles.push({
|
||||
Container: audioFormat === 'webma' ? 'webma,webm' : audioFormat,
|
||||
Type: 'Audio'
|
||||
});
|
||||
}
|
||||
|
||||
// aac also appears in the m4a container
|
||||
if (audioFormat === 'aac' || audioFormat === 'alac') {
|
||||
|
||||
profile.DirectPlayProfiles.push({
|
||||
Container: 'm4a',
|
||||
AudioCodec: audioFormat,
|
||||
Type: 'Audio'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (canPlayVp8) {
|
||||
profile.DirectPlayProfiles.push({
|
||||
Container: 'webm',
|
||||
Type: 'Video',
|
||||
AudioCodec: webmAudioCodecs.join(','),
|
||||
VideoCodec: 'VP8'
|
||||
});
|
||||
}
|
||||
|
||||
if (canPlayVp9) {
|
||||
profile.DirectPlayProfiles.push({
|
||||
Container: 'webm',
|
||||
Type: 'Video',
|
||||
AudioCodec: webmAudioCodecs.join(','),
|
||||
VideoCodec: 'VP9'
|
||||
});
|
||||
}
|
||||
|
||||
profile.TranscodingProfiles = [];
|
||||
|
||||
var hlsBreakOnNonKeyFrames = browser.iOS || browser.osx || browser.edge || !canPlayNativeHls() ? true : false;
|
||||
|
||||
if (canPlayHls() && browser.enableHlsAudio !== false) {
|
||||
profile.TranscodingProfiles.push({
|
||||
|
||||
// hlsjs, edge, and android all seem to require ts container
|
||||
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
|
||||
});
|
||||
}
|
||||
|
||||
// For streaming, prioritize opus transcoding after mp3/aac. It is too problematic with random failures
|
||||
// But for static (offline sync), it will be just fine.
|
||||
// Prioritize aac higher because the encoder can accept more channels than mp3
|
||||
['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()
|
||||
});
|
||||
});
|
||||
|
||||
if (canPlayMkv && !browser.tizen && !browser.orsay && options.enableMkvProgressive !== false) {
|
||||
profile.TranscodingProfiles.push({
|
||||
Container: 'mkv',
|
||||
Type: 'Video',
|
||||
AudioCodec: videoAudioCodecs.join(','),
|
||||
VideoCodec: mp4VideoCodecs.join(','),
|
||||
Context: 'Streaming',
|
||||
MaxAudioChannels: physicalAudioChannels.toString(),
|
||||
CopyTimestamps: true
|
||||
});
|
||||
}
|
||||
|
||||
if (canPlayMkv) {
|
||||
profile.TranscodingProfiles.push({
|
||||
Container: 'mkv',
|
||||
Type: 'Video',
|
||||
AudioCodec: videoAudioCodecs.join(','),
|
||||
VideoCodec: mp4VideoCodecs.join(','),
|
||||
Context: 'Static',
|
||||
MaxAudioChannels: physicalAudioChannels.toString(),
|
||||
CopyTimestamps: true
|
||||
});
|
||||
}
|
||||
|
||||
if (canPlayHls() && options.enableHls !== false) {
|
||||
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
|
||||
});
|
||||
}
|
||||
|
||||
if (canPlayVp8) {
|
||||
profile.TranscodingProfiles.push({
|
||||
Container: 'webm',
|
||||
Type: 'Video',
|
||||
AudioCodec: 'vorbis',
|
||||
VideoCodec: 'vpx',
|
||||
Context: 'Streaming',
|
||||
Protocol: 'http',
|
||||
// If audio transcoding is needed, limit channels to number of physical audio channels
|
||||
// Trying to transcode to 5 channels when there are only 2 speakers generally does not sound good
|
||||
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;
|
||||
|
||||
var aacCodecProfileConditions = [];
|
||||
|
||||
// Handle he-aac not supported
|
||||
if (!videoTestElement.canPlayType('video/mp4; codecs="avc1.640029, mp4a.40.5"').replace(/no/, '')) {
|
||||
// TODO: This needs to become part of the stream url in order to prevent stream copy
|
||||
aacCodecProfileConditions.push({
|
||||
Condition: 'NotEquals',
|
||||
Property: 'AudioProfile',
|
||||
Value: 'HE-AAC'
|
||||
});
|
||||
}
|
||||
|
||||
if (!supportsSecondaryAudio) {
|
||||
aacCodecProfileConditions.push({
|
||||
Condition: 'Equals',
|
||||
Property: 'IsSecondaryAudio',
|
||||
Value: 'false',
|
||||
IsRequired: false
|
||||
});
|
||||
}
|
||||
|
||||
if (browser.chromecast) {
|
||||
aacCodecProfileConditions.push({
|
||||
Condition: 'LessThanEqual',
|
||||
Property: 'AudioChannels',
|
||||
Value: '2',
|
||||
IsRequired: true
|
||||
});
|
||||
}
|
||||
|
||||
if (aacCodecProfileConditions.length) {
|
||||
profile.CodecProfiles.push({
|
||||
Type: 'VideoAudio',
|
||||
Codec: 'aac',
|
||||
Conditions: aacCodecProfileConditions
|
||||
});
|
||||
}
|
||||
|
||||
if (!supportsSecondaryAudio) {
|
||||
profile.CodecProfiles.push({
|
||||
Type: 'VideoAudio',
|
||||
Conditions: [
|
||||
{
|
||||
Condition: 'Equals',
|
||||
Property: 'IsSecondaryAudio',
|
||||
Value: 'false',
|
||||
IsRequired: false
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
var maxH264Level = browser.chromecast ? 42 : 51;
|
||||
var h264Profiles = 'high|main|baseline|constrained baseline';
|
||||
|
||||
if (maxH264Level >= 51 && browser.chrome && !browser.osx) {
|
||||
h264Profiles += '|high 10';
|
||||
}
|
||||
|
||||
profile.CodecProfiles.push({
|
||||
Type: 'Video',
|
||||
Codec: 'h264',
|
||||
Conditions: [
|
||||
{
|
||||
Condition: 'NotEquals',
|
||||
Property: 'IsAnamorphic',
|
||||
Value: 'true',
|
||||
IsRequired: false
|
||||
},
|
||||
{
|
||||
Condition: 'EqualsAny',
|
||||
Property: 'VideoProfile',
|
||||
Value: h264Profiles
|
||||
},
|
||||
{
|
||||
Condition: 'LessThanEqual',
|
||||
Property: 'VideoLevel',
|
||||
Value: maxH264Level.toString()
|
||||
}]
|
||||
});
|
||||
|
||||
if (!browser.edgeUwp && !browser.tizen && !browser.orsay && !browser.web0s) {
|
||||
//profile.CodecProfiles[profile.CodecProfiles.length - 1].Conditions.push({
|
||||
// Condition: 'NotEquals',
|
||||
// Property: 'IsAVC',
|
||||
// Value: 'false',
|
||||
// IsRequired: false
|
||||
//});
|
||||
|
||||
//profile.CodecProfiles[profile.CodecProfiles.length - 1].Conditions.push({
|
||||
// Condition: 'NotEquals',
|
||||
// Property: 'IsInterlaced',
|
||||
// Value: 'true',
|
||||
// IsRequired: false
|
||||
//});
|
||||
}
|
||||
|
||||
if (maxVideoWidth) {
|
||||
profile.CodecProfiles[profile.CodecProfiles.length - 1].Conditions.push({
|
||||
Condition: 'LessThanEqual',
|
||||
Property: 'Width',
|
||||
Value: maxVideoWidth.toString(),
|
||||
IsRequired: false
|
||||
});
|
||||
}
|
||||
|
||||
var globalMaxVideoBitrate = (getGlobalMaxVideoBitrate() || '').toString();
|
||||
|
||||
var h264MaxVideoBitrate = globalMaxVideoBitrate;
|
||||
|
||||
if (h264MaxVideoBitrate) {
|
||||
profile.CodecProfiles[profile.CodecProfiles.length - 1].Conditions.push({
|
||||
Condition: 'LessThanEqual',
|
||||
Property: 'VideoBitrate',
|
||||
Value: h264MaxVideoBitrate,
|
||||
IsRequired: true
|
||||
});
|
||||
}
|
||||
|
||||
var globalVideoConditions = [];
|
||||
|
||||
if (globalMaxVideoBitrate) {
|
||||
globalVideoConditions.push({
|
||||
Condition: 'LessThanEqual',
|
||||
Property: 'VideoBitrate',
|
||||
Value: globalMaxVideoBitrate
|
||||
});
|
||||
}
|
||||
|
||||
if (maxVideoWidth) {
|
||||
globalVideoConditions.push({
|
||||
Condition: 'LessThanEqual',
|
||||
Property: 'Width',
|
||||
Value: maxVideoWidth.toString(),
|
||||
IsRequired: false
|
||||
});
|
||||
}
|
||||
|
||||
if (globalVideoConditions.length) {
|
||||
profile.CodecProfiles.push({
|
||||
Type: 'Video',
|
||||
Conditions: globalVideoConditions
|
||||
});
|
||||
}
|
||||
|
||||
if (browser.chromecast) {
|
||||
profile.CodecProfiles.push({
|
||||
Type: 'Audio',
|
||||
Codec: 'flac',
|
||||
Conditions: [
|
||||
{
|
||||
Condition: 'LessThanEqual',
|
||||
Property: 'AudioSampleRate',
|
||||
Value: '96000'
|
||||
}]
|
||||
});
|
||||
}
|
||||
|
||||
// Subtitle profiles
|
||||
// External vtt or burn in
|
||||
profile.SubtitleProfiles = [];
|
||||
if (supportsTextTracks()) {
|
||||
|
||||
profile.SubtitleProfiles.push({
|
||||
Format: 'vtt',
|
||||
Method: 'External'
|
||||
});
|
||||
}
|
||||
|
||||
profile.ResponseProfiles = [];
|
||||
|
||||
profile.ResponseProfiles.push({
|
||||
Type: 'Video',
|
||||
Container: 'm4v',
|
||||
MimeType: 'video/mp4'
|
||||
});
|
||||
|
||||
return profile;
|
||||
};
|
||||
});
|
282
src/scripts/datetime.js
Normal file
282
src/scripts/datetime.js
Normal file
|
@ -0,0 +1,282 @@
|
|||
define(['globalize'], function (globalize) {
|
||||
'use strict';
|
||||
|
||||
function parseISO8601Date(s, toLocal) {
|
||||
|
||||
// parenthese matches:
|
||||
// year month day hours minutes seconds
|
||||
// dotmilliseconds
|
||||
// tzstring plusminus hours minutes
|
||||
var re = /(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(\.\d+)?(Z|([+-])(\d{2}):(\d{2}))?/;
|
||||
|
||||
var d = s.match(re);
|
||||
|
||||
// "2010-12-07T11:00:00.000-09:00" parses to:
|
||||
// ["2010-12-07T11:00:00.000-09:00", "2010", "12", "07", "11",
|
||||
// "00", "00", ".000", "-09:00", "-", "09", "00"]
|
||||
// "2010-12-07T11:00:00.000Z" parses to:
|
||||
// ["2010-12-07T11:00:00.000Z", "2010", "12", "07", "11",
|
||||
// "00", "00", ".000", "Z", undefined, undefined, undefined]
|
||||
|
||||
if (!d) {
|
||||
|
||||
throw "Couldn't parse ISO 8601 date string '" + s + "'";
|
||||
}
|
||||
|
||||
// parse strings, leading zeros into proper ints
|
||||
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]);
|
||||
|
||||
// Date.UTC(year, month[, date[, hrs[, min[, sec[, ms]]]]])
|
||||
// note that month is 0-11, not 1-12
|
||||
// see https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Date/UTC
|
||||
var ms = Date.UTC(d[1], d[2] - 1, d[3], d[4], d[5], d[6]);
|
||||
|
||||
// if there are milliseconds, add them
|
||||
if (d[7] > 0) {
|
||||
ms += Math.round(d[7] * 1000);
|
||||
}
|
||||
|
||||
// if there's a timezone, calculate it
|
||||
if (d[8] !== "Z" && d[10]) {
|
||||
var offset = d[10] * 60 * 60 * 1000;
|
||||
if (d[11]) {
|
||||
offset += d[11] * 60 * 1000;
|
||||
}
|
||||
if (d[9] === "-") {
|
||||
ms -= offset;
|
||||
} else {
|
||||
ms += offset;
|
||||
}
|
||||
} else if (toLocal === false) {
|
||||
ms += new Date().getTimezoneOffset() * 60000;
|
||||
}
|
||||
|
||||
return new Date(ms);
|
||||
}
|
||||
|
||||
function getDisplayRunningTime(ticks) {
|
||||
var ticksPerHour = 36000000000;
|
||||
var ticksPerMinute = 600000000;
|
||||
var ticksPerSecond = 10000000;
|
||||
|
||||
var parts = [];
|
||||
|
||||
var hours = ticks / ticksPerHour;
|
||||
hours = Math.floor(hours);
|
||||
|
||||
if (hours) {
|
||||
parts.push(hours);
|
||||
}
|
||||
|
||||
ticks -= (hours * ticksPerHour);
|
||||
|
||||
var minutes = ticks / ticksPerMinute;
|
||||
minutes = Math.floor(minutes);
|
||||
|
||||
ticks -= (minutes * ticksPerMinute);
|
||||
|
||||
if (minutes < 10 && hours) {
|
||||
minutes = '0' + minutes;
|
||||
}
|
||||
parts.push(minutes);
|
||||
|
||||
var seconds = ticks / ticksPerSecond;
|
||||
seconds = Math.floor(seconds);
|
||||
|
||||
if (seconds < 10) {
|
||||
seconds = '0' + seconds;
|
||||
}
|
||||
parts.push(seconds);
|
||||
|
||||
return parts.join(':');
|
||||
}
|
||||
|
||||
var toLocaleTimeStringSupportsLocales = function () {
|
||||
try {
|
||||
new Date().toLocaleTimeString('i');
|
||||
} catch (e) {
|
||||
return e.name === 'RangeError';
|
||||
}
|
||||
return false;
|
||||
}();
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
options = options || {};
|
||||
|
||||
if (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');
|
||||
}
|
||||
|
||||
options = options || {};
|
||||
|
||||
if (toLocaleTimeStringSupportsLocales) {
|
||||
|
||||
var currentLocale = globalize.getCurrentDateTimeLocale();
|
||||
|
||||
if (currentLocale) {
|
||||
return date.toLocaleDateString(currentLocale, options);
|
||||
}
|
||||
}
|
||||
|
||||
// This is essentially a hard-coded polyfill
|
||||
var optionList = getOptionList(options);
|
||||
if (optionList.length === 1 && optionList[0].name === 'weekday') {
|
||||
var weekday = [];
|
||||
weekday[0] = "Sun";
|
||||
weekday[1] = "Mon";
|
||||
weekday[2] = "Tue";
|
||||
weekday[3] = "Wed";
|
||||
weekday[4] = "Thu";
|
||||
weekday[5] = "Fri";
|
||||
weekday[6] = "Sat";
|
||||
return weekday[date.getDay()];
|
||||
}
|
||||
|
||||
return date.toLocaleDateString();
|
||||
}
|
||||
|
||||
function toLocaleTimeString(date, options) {
|
||||
|
||||
if (!date) {
|
||||
throw new Error('date cannot be null');
|
||||
}
|
||||
|
||||
options = options || {};
|
||||
|
||||
if (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 ((typeof date).toString().toLowerCase() === 'string') {
|
||||
try {
|
||||
|
||||
date = parseISO8601Date(date, true);
|
||||
|
||||
} catch (err) {
|
||||
return date;
|
||||
}
|
||||
}
|
||||
|
||||
if (toLocaleTimeStringSupportsLocales) {
|
||||
return toLocaleTimeString(date, {
|
||||
|
||||
hour: 'numeric',
|
||||
minute: '2-digit'
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
var time = toLocaleTimeString(date);
|
||||
|
||||
var timeLower = time.toLowerCase();
|
||||
|
||||
if (timeLower.indexOf('am') !== -1 || timeLower.indexOf('pm') !== -1) {
|
||||
|
||||
time = timeLower;
|
||||
var hour = date.getHours() % 12;
|
||||
var suffix = date.getHours() > 11 ? 'pm' : 'am';
|
||||
if (!hour) {
|
||||
hour = 12;
|
||||
}
|
||||
var minutes = date.getMinutes();
|
||||
|
||||
if (minutes < 10) {
|
||||
minutes = '0' + minutes;
|
||||
}
|
||||
|
||||
minutes = ':' + minutes;
|
||||
time = hour + minutes + suffix;
|
||||
} else {
|
||||
|
||||
var timeParts = time.split(':');
|
||||
|
||||
// Trim off seconds
|
||||
if (timeParts.length > 2) {
|
||||
|
||||
// setting to 2 also handles '21:00:28 GMT+9:30'
|
||||
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();
|
||||
var day = yesterday.getDate() + offsetInDays;
|
||||
|
||||
yesterday.setDate(day); // automatically adjusts month/year appropriately
|
||||
|
||||
return date.getFullYear() === yesterday.getFullYear() && date.getMonth() === yesterday.getMonth() && date.getDate() === day;
|
||||
}
|
||||
|
||||
return {
|
||||
parseISO8601Date: parseISO8601Date,
|
||||
getDisplayRunningTime: getDisplayRunningTime,
|
||||
toLocaleDateString: toLocaleDateString,
|
||||
toLocaleString: toLocaleString,
|
||||
getDisplayTime: getDisplayTime,
|
||||
isRelativeDay: isRelativeDay,
|
||||
toLocaleTimeString: toLocaleTimeString,
|
||||
supportsLocalization: function () {
|
||||
return toLocaleTimeStringSupportsLocales;
|
||||
}
|
||||
};
|
||||
});
|
274
src/scripts/globalize.js
Normal file
274
src/scripts/globalize.js
Normal file
|
@ -0,0 +1,274 @@
|
|||
define(['connectionManager', 'userSettings', 'events'], function (connectionManager, userSettings, events) {
|
||||
'use strict';
|
||||
var fallbackCulture = 'en-us';
|
||||
|
||||
var allTranslations = {};
|
||||
var currentCulture;
|
||||
var currentDateTimeCulture;
|
||||
|
||||
function getCurrentLocale() {
|
||||
return currentCulture;
|
||||
}
|
||||
|
||||
function getCurrentDateTimeLocale() {
|
||||
return currentDateTimeCulture;
|
||||
}
|
||||
|
||||
function getDefaultLanguage() {
|
||||
var culture = document.documentElement.getAttribute('data-culture');
|
||||
if (culture) {
|
||||
return culture;
|
||||
}
|
||||
|
||||
if (navigator.language) {
|
||||
return navigator.language;
|
||||
}
|
||||
if (navigator.userLanguage) {
|
||||
return navigator.userLanguage;
|
||||
}
|
||||
if (navigator.languages && navigator.languages.length) {
|
||||
return navigator.languages[0];
|
||||
}
|
||||
|
||||
return fallbackCulture;
|
||||
}
|
||||
|
||||
function updateCurrentCulture() {
|
||||
var culture;
|
||||
try {
|
||||
culture = userSettings.language();
|
||||
} catch (err) {
|
||||
console.log('no language set in user settings');
|
||||
}
|
||||
culture = culture || getDefaultLanguage();
|
||||
|
||||
currentCulture = normalizeLocaleName(culture);
|
||||
|
||||
var dateTimeCulture;
|
||||
try {
|
||||
dateTimeCulture = userSettings.dateTimeLocale();
|
||||
} catch (err) {
|
||||
console.log('no date format set in user settings');
|
||||
}
|
||||
|
||||
if (dateTimeCulture) {
|
||||
currentDateTimeCulture = normalizeLocaleName(dateTimeCulture);
|
||||
} else {
|
||||
currentDateTimeCulture = currentCulture;
|
||||
}
|
||||
ensureTranslations(currentCulture);
|
||||
}
|
||||
|
||||
function ensureTranslations(culture) {
|
||||
for (var i in allTranslations) {
|
||||
ensureTranslation(allTranslations[i], culture);
|
||||
}
|
||||
if (culture !== fallbackCulture) {
|
||||
for (var i in allTranslations) {
|
||||
ensureTranslation(allTranslations[i], fallbackCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function ensureTranslation(translationInfo, culture) {
|
||||
if (translationInfo.dictionaries[culture]) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return loadTranslation(translationInfo.translations, culture).then(function (dictionary) {
|
||||
translationInfo.dictionaries[culture] = dictionary;
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeLocaleName(culture) {
|
||||
// TODO remove normalizations
|
||||
culture = culture.replace('_', '-');
|
||||
|
||||
// convert de-DE to de
|
||||
var parts = culture.split('-');
|
||||
if (parts.length === 2) {
|
||||
if (parts[0].toLowerCase() === parts[1].toLowerCase()) {
|
||||
culture = parts[0].toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
var lower = culture.toLowerCase();
|
||||
if (lower === 'ca-es') {
|
||||
return 'ca';
|
||||
}
|
||||
|
||||
// normalize Swedish
|
||||
if (lower === 'sv-se') {
|
||||
return 'sv';
|
||||
}
|
||||
|
||||
return lower;
|
||||
}
|
||||
|
||||
function getDictionary(module, locale) {
|
||||
if (!module) {
|
||||
module = defaultModule();
|
||||
}
|
||||
|
||||
var translations = allTranslations[module];
|
||||
if (!translations) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return translations.dictionaries[locale];
|
||||
}
|
||||
|
||||
function register(options) {
|
||||
allTranslations[options.name] = {
|
||||
translations: options.strings || options.translations,
|
||||
dictionaries: {}
|
||||
};
|
||||
}
|
||||
|
||||
function loadStrings(options) {
|
||||
var locale = getCurrentLocale();
|
||||
var promises = [];
|
||||
var optionsName;
|
||||
if (typeof options === 'string') {
|
||||
optionsName = options;
|
||||
} else {
|
||||
optionsName = options.name;
|
||||
register(options);
|
||||
}
|
||||
promises.push(ensureTranslation(allTranslations[optionsName], locale));
|
||||
promises.push(ensureTranslation(allTranslations[optionsName], fallbackCulture));
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
var cacheParam = new Date().getTime();
|
||||
function loadTranslation(translations, lang) {
|
||||
lang = normalizeLocaleName(lang);
|
||||
var filtered = translations.filter(function (t) {
|
||||
return normalizeLocaleName(t.lang) === lang;
|
||||
});
|
||||
|
||||
if (!filtered.length) {
|
||||
filtered = translations.filter(function (t) {
|
||||
return normalizeLocaleName(t.lang) === fallbackCulture;
|
||||
});
|
||||
}
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
if (!filtered.length) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
var url = filtered[0].path;
|
||||
|
||||
url += url.indexOf('?') === -1 ? '?' : '&';
|
||||
url += 'v=' + cacheParam;
|
||||
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', url, true);
|
||||
|
||||
xhr.onload = function (e) {
|
||||
if (this.status < 400) {
|
||||
resolve(JSON.parse(this.response));
|
||||
} else {
|
||||
resolve({});
|
||||
}
|
||||
};
|
||||
|
||||
xhr.onerror = function () {
|
||||
resolve({});
|
||||
};
|
||||
xhr.send();
|
||||
});
|
||||
}
|
||||
|
||||
function translateKey(key) {
|
||||
var parts = key.split('#');
|
||||
var module;
|
||||
|
||||
if (parts.length > 1) {
|
||||
module = parts[0];
|
||||
key = parts[1];
|
||||
}
|
||||
|
||||
return translateKeyFromModule(key, module);
|
||||
}
|
||||
|
||||
function translateKeyFromModule(key, module) {
|
||||
var dictionary = getDictionary(module, getCurrentLocale());
|
||||
if (!dictionary || !dictionary[key]) {
|
||||
dictionary = getDictionary(module, fallbackCulture);
|
||||
}
|
||||
if (!dictionary) {
|
||||
return key;
|
||||
}
|
||||
return dictionary[key] || key;
|
||||
}
|
||||
|
||||
function replaceAll(str, find, replace) {
|
||||
return str.split(find).join(replace);
|
||||
}
|
||||
|
||||
function translate(key) {
|
||||
var val = translateKey(key);
|
||||
for (var i = 1; i < arguments.length; i++) {
|
||||
val = replaceAll(val, '{' + (i - 1) + '}', arguments[i]);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
function translateHtml(html, module) {
|
||||
if (!module) {
|
||||
module = defaultModule();
|
||||
}
|
||||
if (!module) {
|
||||
throw new Error('module cannot be null or empty');
|
||||
}
|
||||
|
||||
var startIndex = html.indexOf('${');
|
||||
if (startIndex === -1) {
|
||||
return html;
|
||||
}
|
||||
|
||||
startIndex += 2;
|
||||
var endIndex = html.indexOf('}', startIndex);
|
||||
if (endIndex === -1) {
|
||||
return html;
|
||||
}
|
||||
|
||||
var key = html.substring(startIndex, endIndex);
|
||||
var val = translateKeyFromModule(key, module);
|
||||
|
||||
html = html.replace('${' + key + '}', val);
|
||||
return translateHtml(html, module);
|
||||
}
|
||||
|
||||
var _defaultModule;
|
||||
function defaultModule(val) {
|
||||
if (val) {
|
||||
_defaultModule = val;
|
||||
}
|
||||
return _defaultModule;
|
||||
}
|
||||
|
||||
updateCurrentCulture();
|
||||
|
||||
events.on(connectionManager, 'localusersignedin', updateCurrentCulture);
|
||||
events.on(userSettings, 'change', function (e, name) {
|
||||
if (name === 'language' || name === 'datetimelocale') {
|
||||
updateCurrentCulture();
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
getString: translate,
|
||||
translate: translate,
|
||||
translateDocument: translateHtml,
|
||||
translateHtml: translateHtml,
|
||||
loadStrings: loadStrings,
|
||||
defaultModule: defaultModule,
|
||||
getCurrentLocale: getCurrentLocale,
|
||||
getCurrentDateTimeLocale: getCurrentDateTimeLocale,
|
||||
register: register
|
||||
};
|
||||
});
|
241
src/scripts/inputManager.js
Normal file
241
src/scripts/inputManager.js
Normal file
|
@ -0,0 +1,241 @@
|
|||
define(['playbackManager', 'focusManager', 'appRouter', 'dom'], function (playbackManager, focusManager, appRouter, dom) {
|
||||
'use strict';
|
||||
|
||||
var lastInputTime = new Date().getTime();
|
||||
|
||||
function notify() {
|
||||
lastInputTime = new Date().getTime();
|
||||
handleCommand('unknown');
|
||||
}
|
||||
|
||||
function notifyMouseMove() {
|
||||
lastInputTime = new Date().getTime();
|
||||
}
|
||||
|
||||
function idleTime() {
|
||||
return new Date().getTime() - lastInputTime;
|
||||
}
|
||||
|
||||
function select(sourceElement) {
|
||||
sourceElement.click();
|
||||
}
|
||||
|
||||
var eventListenerCount = 0;
|
||||
function on(scope, fn) {
|
||||
if (eventListenerCount) {
|
||||
eventListenerCount++;
|
||||
}
|
||||
dom.addEventListener(scope, 'command', fn, {});
|
||||
}
|
||||
|
||||
function off(scope, fn) {
|
||||
if (eventListenerCount) {
|
||||
eventListenerCount--;
|
||||
}
|
||||
dom.removeEventListener(scope, 'command', fn, {});
|
||||
}
|
||||
|
||||
var commandTimes = {};
|
||||
|
||||
function checkCommandTime(command) {
|
||||
|
||||
var last = commandTimes[command] || 0;
|
||||
var now = new Date().getTime();
|
||||
|
||||
if ((now - last) < 1000) {
|
||||
return false;
|
||||
}
|
||||
|
||||
commandTimes[command] = now;
|
||||
return true;
|
||||
}
|
||||
|
||||
function handleCommand(name, options) {
|
||||
|
||||
lastInputTime = new Date().getTime();
|
||||
|
||||
var sourceElement = (options ? options.sourceElement : null);
|
||||
|
||||
if (sourceElement) {
|
||||
sourceElement = focusManager.focusableParent(sourceElement);
|
||||
}
|
||||
|
||||
sourceElement = sourceElement || document.activeElement || window;
|
||||
|
||||
if (eventListenerCount) {
|
||||
var customEvent = new CustomEvent("command", {
|
||||
detail: {
|
||||
command: name
|
||||
},
|
||||
bubbles: true,
|
||||
cancelable: true
|
||||
});
|
||||
|
||||
var eventResult = sourceElement.dispatchEvent(customEvent);
|
||||
if (!eventResult) {
|
||||
// event cancelled
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (name) {
|
||||
case 'up':
|
||||
focusManager.moveUp(sourceElement);
|
||||
break;
|
||||
case 'down':
|
||||
focusManager.moveDown(sourceElement);
|
||||
break;
|
||||
case 'left':
|
||||
focusManager.moveLeft(sourceElement);
|
||||
break;
|
||||
case 'right':
|
||||
focusManager.moveRight(sourceElement);
|
||||
break;
|
||||
case 'home':
|
||||
appRouter.goHome();
|
||||
break;
|
||||
case 'settings':
|
||||
appRouter.showSettings();
|
||||
break;
|
||||
case 'back':
|
||||
appRouter.back();
|
||||
break;
|
||||
case 'forward':
|
||||
break;
|
||||
case 'select':
|
||||
select(sourceElement);
|
||||
break;
|
||||
case 'pageup':
|
||||
break;
|
||||
case 'pagedown':
|
||||
break;
|
||||
case 'end':
|
||||
break;
|
||||
case 'menu':
|
||||
break;
|
||||
case 'info':
|
||||
break;
|
||||
case 'nextchapter':
|
||||
playbackManager.nextChapter();
|
||||
break;
|
||||
case 'next':
|
||||
case 'nexttrack':
|
||||
playbackManager.nextTrack();
|
||||
break;
|
||||
case 'previous':
|
||||
case 'previoustrack':
|
||||
playbackManager.previousTrack();
|
||||
break;
|
||||
case 'previouschapter':
|
||||
playbackManager.previousChapter();
|
||||
break;
|
||||
case 'guide':
|
||||
appRouter.showGuide();
|
||||
break;
|
||||
case 'recordedtv':
|
||||
appRouter.showRecordedTV();
|
||||
break;
|
||||
case 'record':
|
||||
break;
|
||||
case 'livetv':
|
||||
appRouter.showLiveTV();
|
||||
break;
|
||||
case 'mute':
|
||||
playbackManager.setMute(true);
|
||||
break;
|
||||
case 'unmute':
|
||||
playbackManager.setMute(false);
|
||||
break;
|
||||
case 'togglemute':
|
||||
playbackManager.toggleMute();
|
||||
break;
|
||||
case 'channelup':
|
||||
playbackManager.channelUp();
|
||||
break;
|
||||
case 'channeldown':
|
||||
playbackManager.channelDown();
|
||||
break;
|
||||
case 'volumedown':
|
||||
playbackManager.volumeDown();
|
||||
break;
|
||||
case 'volumeup':
|
||||
playbackManager.volumeUp();
|
||||
break;
|
||||
case 'play':
|
||||
playbackManager.unpause();
|
||||
break;
|
||||
case 'pause':
|
||||
playbackManager.pause();
|
||||
break;
|
||||
case 'playpause':
|
||||
playbackManager.playPause();
|
||||
break;
|
||||
case 'stop':
|
||||
if (checkCommandTime('stop')) {
|
||||
playbackManager.stop();
|
||||
}
|
||||
break;
|
||||
case 'changezoom':
|
||||
playbackManager.toggleAspectRatio();
|
||||
break;
|
||||
case 'changeaudiotrack':
|
||||
playbackManager.changeAudioStream();
|
||||
break;
|
||||
case 'changesubtitletrack':
|
||||
playbackManager.changeSubtitleStream();
|
||||
break;
|
||||
case 'search':
|
||||
appRouter.showSearch();
|
||||
break;
|
||||
case 'favorites':
|
||||
appRouter.showFavorites();
|
||||
break;
|
||||
case 'fastforward':
|
||||
playbackManager.fastForward();
|
||||
break;
|
||||
case 'rewind':
|
||||
playbackManager.rewind();
|
||||
break;
|
||||
case 'togglefullscreen':
|
||||
playbackManager.toggleFullscreen();
|
||||
break;
|
||||
case 'disabledisplaymirror':
|
||||
playbackManager.enableDisplayMirroring(false);
|
||||
break;
|
||||
case 'enabledisplaymirror':
|
||||
playbackManager.enableDisplayMirroring(true);
|
||||
break;
|
||||
case 'toggledisplaymirror':
|
||||
playbackManager.toggleDisplayMirroring();
|
||||
break;
|
||||
case 'nowplaying':
|
||||
appRouter.showNowPlaying();
|
||||
break;
|
||||
case 'repeatnone':
|
||||
playbackManager.setRepeatMode('RepeatNone');
|
||||
break;
|
||||
case 'repeatall':
|
||||
playbackManager.setRepeatMode('RepeatAll');
|
||||
break;
|
||||
case 'repeatone':
|
||||
playbackManager.setRepeatMode('RepeatOne');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dom.addEventListener(document, 'click', notify, {
|
||||
passive: true
|
||||
});
|
||||
|
||||
return {
|
||||
trigger: handleCommand,
|
||||
handle: handleCommand,
|
||||
notify: notify,
|
||||
notifyMouseMove: notifyMouseMove,
|
||||
idleTime: idleTime,
|
||||
on: on,
|
||||
off: off
|
||||
};
|
||||
});
|
|
@ -645,25 +645,22 @@ var AppInfo = {};
|
|||
var bowerPath = getBowerPath();
|
||||
var componentsPath = getComponentsPath();
|
||||
var paths = {
|
||||
browserdeviceprofile: "scripts/browserdeviceprofile",
|
||||
browser: "scripts/browser",
|
||||
libraryBrowser: "scripts/librarybrowser",
|
||||
inputManager: "scripts/inputManager",
|
||||
datetime: "scripts/datetime",
|
||||
globalize: "scripts/globalize",
|
||||
libraryMenu: "scripts/librarymenu",
|
||||
playlisteditor: componentsPath + "/playlisteditor/playlisteditor",
|
||||
medialibrarycreator: componentsPath + "/medialibrarycreator/medialibrarycreator",
|
||||
medialibraryeditor: componentsPath + "/medialibraryeditor/medialibraryeditor",
|
||||
imageoptionseditor: componentsPath + "/imageoptionseditor/imageoptionseditor",
|
||||
humanedate: componentsPath + "/humanedate",
|
||||
apphost: componentsPath + "/apphost",
|
||||
libraryBrowser: "scripts/librarybrowser",
|
||||
events: bowerPath + "/apiclient/events",
|
||||
credentialprovider: bowerPath + "/apiclient/credentialprovider",
|
||||
connectionManagerFactory: bowerPath + "/apiclient/connectionmanager",
|
||||
visibleinviewport: componentsPath + "/visibleinviewport",
|
||||
browserdeviceprofile: componentsPath + "/browserdeviceprofile",
|
||||
browser: componentsPath + "/browser",
|
||||
inputManager: componentsPath + "/inputManager",
|
||||
qualityoptions: componentsPath + "/qualityoptions",
|
||||
page: bowerPath + "/page",
|
||||
focusManager: componentsPath + "/focusManager",
|
||||
datetime: componentsPath + "/datetime",
|
||||
globalize: componentsPath + "/globalize",
|
||||
itemHelper: componentsPath + "/itemhelper",
|
||||
itemShortcuts: componentsPath + "/shortcuts",
|
||||
playQueueManager: componentsPath + "/playback/playqueuemanager",
|
||||
|
@ -706,13 +703,57 @@ var AppInfo = {};
|
|||
window.jQuery = jQuery;
|
||||
});
|
||||
|
||||
require(["css!css/site"]);
|
||||
|
||||
// define styles
|
||||
// TODO determine which of these files can be moved to the components themselves
|
||||
define("material-icons", ["css!css/material-icons/style"], returnFirstDependency);
|
||||
define("systemFontsCss", ["css!css/fonts"], returnFirstDependency);
|
||||
define("systemFontsSizedCss", ["css!css/fonts.sized"], returnFirstDependency);
|
||||
define("scrollStyles", ["css!css/scrollstyles"], returnFirstDependency);
|
||||
define("dashboardcss", ["css!css/dashboard"], returnFirstDependency);
|
||||
define("programStyles", ["css!" + componentsPath + "/guide/programs"], returnFirstDependency);
|
||||
define("listViewStyle", ["css!" + componentsPath + "/listview/listview"], returnFirstDependency);
|
||||
define("formDialogStyle", ["css!" + componentsPath + "/formdialog"], returnFirstDependency);
|
||||
define("clearButtonStyle", ["css!" + componentsPath + "/clearbutton"], returnFirstDependency);
|
||||
define("cardStyle", ["css!" + componentsPath + "/cardbuilder/card"], returnFirstDependency);
|
||||
define("flexStyles", ["css!" + componentsPath + "/flexstyles"], returnFirstDependency);
|
||||
|
||||
// define legacy features
|
||||
// TODO delete the rest of these
|
||||
define("fnchecked", ["legacy/fnchecked"], returnFirstDependency);
|
||||
define("legacyDashboard", ["legacy/dashboard"], returnFirstDependency);
|
||||
define("legacySelectMenu", ["legacy/selectmenu"], returnFirstDependency);
|
||||
|
||||
// there are several objects that need to be instantiated
|
||||
// TODO find a better way to do this
|
||||
define("appFooter", [componentsPath + "/appfooter/appfooter"], returnFirstDependency);
|
||||
define("appFooter-shared", ["appFooter"], createSharedAppFooter);
|
||||
|
||||
// TODO pull apiclient out of this repository
|
||||
define('events', [bowerPath + "/apiclient/events"], returnFirstDependency);
|
||||
define('credentialprovider', [bowerPath + "/apiclient/credentialprovider"], returnFirstDependency);
|
||||
define('connectionManagerFactory', [bowerPath + "/apiclient/connectionmanager"], returnFirstDependency);
|
||||
define('appStorage', [bowerPath + "/apiclient/appStorage"], returnFirstDependency);
|
||||
define("serversync", [bowerPath + "/apiclient/sync/serversync"], returnFirstDependency);
|
||||
define("multiserversync", [bowerPath + "/apiclient/sync/multiserversync"], returnFirstDependency);
|
||||
define("mediasync", [bowerPath + "/apiclient/sync/mediasync"], returnFirstDependency);
|
||||
define("itemrepository", [bowerPath + "/apiclient/sync/itemrepository"], returnFirstDependency);
|
||||
define("useractionrepository", [bowerPath + "/apiclient/sync/useractionrepository"], returnFirstDependency);
|
||||
|
||||
// also pull out these libs
|
||||
define("page", [bowerPath + "/page"], returnFirstDependency);
|
||||
define("fetch", [bowerPath + "/fetch/fetch"], returnFirstDependency);
|
||||
define("queryString", [bowerPath + "/query-string/index"], function () {
|
||||
return queryString;
|
||||
});
|
||||
|
||||
define("chromecastHelper", [componentsPath + "/chromecast/chromecasthelpers"], returnFirstDependency);
|
||||
define("mediaSession", [componentsPath + "/playback/mediasession"], returnFirstDependency);
|
||||
define("actionsheet", [componentsPath + "/actionsheet/actionsheet"], returnFirstDependency);
|
||||
define("tunerPicker", [componentsPath + "/tunerpicker"], returnFirstDependency);
|
||||
define("mainTabsManager", [componentsPath + "/maintabsmanager"], returnFirstDependency);
|
||||
define("imageLoader", [componentsPath + "/images/imageLoader"], returnFirstDependency);
|
||||
define("appFooter", [componentsPath + "/appfooter/appfooter"], returnFirstDependency);
|
||||
define("directorybrowser", [componentsPath + "/directorybrowser/directorybrowser"], returnFirstDependency);
|
||||
define("metadataEditor", [componentsPath + "/metadataeditor/metadataeditor"], returnFirstDependency);
|
||||
define("personEditor", [componentsPath + "/metadataeditor/personeditor"], returnFirstDependency);
|
||||
|
@ -720,7 +761,6 @@ var AppInfo = {};
|
|||
define("playerSettingsMenu", [componentsPath + "/playback/playersettingsmenu"], returnFirstDependency);
|
||||
define("playMethodHelper", [componentsPath + "/playback/playmethodhelper"], returnFirstDependency);
|
||||
define("brightnessOsd", [componentsPath + "/playback/brightnessosd"], returnFirstDependency);
|
||||
define("libraryMenu", ["scripts/librarymenu"], returnFirstDependency);
|
||||
define("emby-collapse", [componentsPath + "/emby-collapse/emby-collapse"], returnFirstDependency);
|
||||
define("emby-button", [componentsPath + "/emby-button/emby-button"], returnFirstDependency);
|
||||
define("emby-itemscontainer", [componentsPath + "/emby-itemscontainer/emby-itemscontainer"], returnFirstDependency);
|
||||
|
@ -779,14 +819,11 @@ var AppInfo = {};
|
|||
define("refreshDialog", [componentsPath + "/refreshdialog/refreshdialog"], returnFirstDependency);
|
||||
define("backdrop", [componentsPath + "/backdrop/backdrop"], returnFirstDependency);
|
||||
define("fetchHelper", [componentsPath + "/fetchhelper"], returnFirstDependency);
|
||||
define("cardStyle", ["css!" + componentsPath + "/cardbuilder/card"], returnFirstDependency);
|
||||
define("cardBuilder", [componentsPath + "/cardbuilder/cardBuilder"], returnFirstDependency);
|
||||
define("peoplecardbuilder", [componentsPath + "/cardbuilder/peoplecardbuilder"], returnFirstDependency);
|
||||
define("chaptercardbuilder", [componentsPath + "/cardbuilder/chaptercardbuilder"], returnFirstDependency);
|
||||
define("flexStyles", ["css!" + componentsPath + "/flexstyles"], returnFirstDependency);
|
||||
define("deleteHelper", [componentsPath + "/deletehelper"], returnFirstDependency);
|
||||
define("tvguide", [componentsPath + "/guide/guide"], returnFirstDependency);
|
||||
define("programStyles", ["css!" + componentsPath + "/guide/programs"], returnFirstDependency);
|
||||
define("guide-settings-dialog", [componentsPath + "/guide/guide-settings"], returnFirstDependency);
|
||||
define("loadingDialog", [componentsPath + "/loadingdialog/loadingdialog"], returnFirstDependency);
|
||||
define("viewManager", [componentsPath + "/viewManager/viewManager"], function (viewManager) {
|
||||
|
@ -794,29 +831,18 @@ var AppInfo = {};
|
|||
viewManager.dispatchPageEvents(true);
|
||||
return viewManager;
|
||||
});
|
||||
define('appStorage', [bowerPath + "/apiclient/appStorage"], returnFirstDependency);
|
||||
define("dashboardcss", ["css!css/dashboard"], returnFirstDependency);
|
||||
define("slideshow", [componentsPath + "/slideshow/slideshow"], returnFirstDependency);
|
||||
define("fetch", [bowerPath + "/fetch/fetch"], returnFirstDependency);
|
||||
define("objectassign", [componentsPath + "/polyfills/objectassign"], returnFirstDependency);
|
||||
define("clearButtonStyle", ["css!" + componentsPath + "/clearbutton"], returnFirstDependency);
|
||||
define("userdataButtons", [componentsPath + "/userdatabuttons/userdatabuttons"], returnFirstDependency);
|
||||
define("emby-playstatebutton", [componentsPath + "/userdatabuttons/emby-playstatebutton"], returnFirstDependency);
|
||||
define("emby-ratingbutton", [componentsPath + "/userdatabuttons/emby-ratingbutton"], returnFirstDependency);
|
||||
define("listView", [componentsPath + "/listview/listview"], returnFirstDependency);
|
||||
define("listViewStyle", ["css!" + componentsPath + "/listview/listview"], returnFirstDependency);
|
||||
define("formDialogStyle", ["css!" + componentsPath + "/formdialog"], returnFirstDependency);
|
||||
define("indicators", [componentsPath + "/indicators/indicators"], returnFirstDependency);
|
||||
define("viewSettings", [componentsPath + "/viewsettings/viewsettings"], returnFirstDependency);
|
||||
define("filterMenu", [componentsPath + "/filtermenu/filtermenu"], returnFirstDependency);
|
||||
define("sortMenu", [componentsPath + "/sortmenu/sortmenu"], returnFirstDependency);
|
||||
define("serversync", [bowerPath + "/apiclient/sync/serversync"], returnFirstDependency);
|
||||
define("multiserversync", [bowerPath + "/apiclient/sync/multiserversync"], returnFirstDependency);
|
||||
define("mediasync", [bowerPath + "/apiclient/sync/mediasync"], returnFirstDependency);
|
||||
define("idb", [componentsPath + "/idb"], returnFirstDependency);
|
||||
define("sanitizefilename", [componentsPath + "/sanitizefilename"], returnFirstDependency);
|
||||
define("itemrepository", [bowerPath + "/apiclient/sync/itemrepository"], returnFirstDependency);
|
||||
define("useractionrepository", [bowerPath + "/apiclient/sync/useractionrepository"], returnFirstDependency);
|
||||
define("scroller", [componentsPath + "/scroller"], returnFirstDependency);
|
||||
define("toast", [componentsPath + "/toast/toast"], returnFirstDependency);
|
||||
define("scrollHelper", [componentsPath + "/scrollhelper"], returnFirstDependency);
|
||||
|
@ -824,22 +850,13 @@ var AppInfo = {};
|
|||
define("appSettings", [componentsPath + "/appSettings"], returnFirstDependency);
|
||||
define("userSettings", [componentsPath + "/usersettings/usersettings"], returnFirstDependency);
|
||||
define("userSettingsBuilder", [componentsPath + "/usersettings/usersettingsbuilder", "layoutManager", "browser"], returnFirstDependency);
|
||||
define("material-icons", ["css!css/material-icons/style"], returnFirstDependency);
|
||||
define("systemFontsCss", ["css!css/fonts"], returnFirstDependency);
|
||||
define("systemFontsSizedCss", ["css!css/fonts.sized"], returnFirstDependency);
|
||||
define("scrollStyles", ["css!" + componentsPath + "/scrollstyles"], returnFirstDependency);
|
||||
define("imageUploader", [componentsPath + "/imageuploader/imageuploader"], returnFirstDependency);
|
||||
define("navdrawer", [componentsPath + "/navdrawer/navdrawer"], returnFirstDependency);
|
||||
define("htmlMediaHelper", [componentsPath + "/htmlMediaHelper"], returnFirstDependency);
|
||||
define("viewContainer", [componentsPath + "/viewContainer"], returnFirstDependency);
|
||||
define("queryString", [bowerPath + "/query-string/index"], function () {
|
||||
return queryString;
|
||||
});
|
||||
define("fnchecked", ["legacy/fnchecked"], returnFirstDependency);
|
||||
define("dialogHelper", [componentsPath + "/dialogHelper/dialogHelper"], returnFirstDependency);
|
||||
define("inputmanager", ["inputManager"], returnFirstDependency);
|
||||
define("serverNotifications", [componentsPath + "/serverNotifications/serverNotifications"], returnFirstDependency);
|
||||
define("appFooter-shared", ["appFooter"], createSharedAppFooter);
|
||||
define("skinManager", [componentsPath + "/skinManager"], returnFirstDependency);
|
||||
define("keyboardnavigation", [componentsPath + "/keyboardnavigation"], returnFirstDependency);
|
||||
define("connectionManager", [], function () {
|
||||
|
@ -1115,8 +1132,6 @@ var AppInfo = {};
|
|||
});
|
||||
})();
|
||||
|
||||
require(["css!css/site"]);
|
||||
|
||||
return require(["browser"], onWebComponentsReady);
|
||||
}();
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue