update collapsible

This commit is contained in:
Luke Pulverenti 2016-07-02 14:05:40 -04:00
parent f351643d29
commit 891a865464
42 changed files with 1185 additions and 536 deletions

View File

@ -15,12 +15,12 @@
},
"devDependencies": {},
"ignore": [],
"version": "1.4.60",
"_release": "1.4.60",
"version": "1.4.62",
"_release": "1.4.62",
"_resolution": {
"type": "version",
"tag": "1.4.60",
"commit": "28747871beb1d800a18c86c5e30255ae6015489d"
"tag": "1.4.62",
"commit": "e8f6a2939a975c0ec2bb31b99e3c4e66b34db229"
},
"_source": "https://github.com/MediaBrowser/emby-webcomponents.git",
"_target": "^1.2.0",

View File

@ -448,7 +448,14 @@
dlg.classList.add('dialog');
if (options.scrollY !== false) {
if (options.scrollX) {
dlg.classList.add('smoothScrollX');
if (layoutManager.tv) {
scrollHelper.centerFocus.on(dlg, true);
}
}
else if (options.scrollY !== false) {
dlg.classList.add('smoothScrollY');
if (layoutManager.tv) {

View File

@ -0,0 +1,24 @@
.collapseContent {
border-width: 0 1px 1px 1px;
padding: 1em 1.25em;
height: 0;
transition-property: height;
transition-duration: 300ms;
}
.emby-collapsible-button {
margin: 0;
display: flex;
align-items: center;
text-transform: none;
width: 100%;
}
.emby-collapse-expandIcon {
transform-origin: 50% 50%;
transition: transform 350ms ease-out;
}
.emby-collapse-expandIcon.expanded {
transform: rotate(180deg);
}

View File

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

View File

@ -1,4 +1,4 @@
define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'connectionManager', 'require', 'loading', 'scrollHelper', 'emby-checkbox', 'emby-button', 'emby-collapsible', 'emby-input', 'paper-icon-button-light', 'css!./../formdialog', 'css!./recordingcreator', 'material-icons'], function (dialogHelper, globalize, layoutManager, mediaInfo, appHost, connectionManager, require, loading, scrollHelper) {
define(['dialogHelper', 'globalize', 'layoutManager', 'mediaInfo', 'apphost', 'connectionManager', 'require', 'loading', 'scrollHelper', 'emby-checkbox', 'emby-button', 'emby-collapse', 'emby-input', 'paper-icon-button-light', 'css!./../formdialog', 'css!./recordingcreator', 'material-icons'], function (dialogHelper, globalize, layoutManager, mediaInfo, appHost, connectionManager, require, loading, scrollHelper) {
var currentProgramId;
var currentServerId;

View File

@ -48,51 +48,53 @@
</div>
</div>
<div class="advanced hide">
<emby-collapsible title="${Advanced}">
<div class="seriesDays hide">
<div>
<h1>${Days}</h1>
</div>
<div class="checkboxList">
<label>
<input type="checkbox" is="emby-checkbox" id="chkSunday" />
<span>${Sunday}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" id="chkMonday" />
<span>${Monday}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" id="chkTuesday" />
<span>${Tuesday}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" id="chkWednesday" />
<span>${Wednesday}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" id="chkThursday" />
<span>${Thursday}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" id="chkFriday" />
<span>${Friday}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" id="chkSaturday" />
<span>${Saturday}</span>
</label>
<div is="emby-collapse" title="${Advanced}">
<div class="collapseContent">
<div class="seriesDays hide">
<div>
<h1>${Days}</h1>
</div>
<div class="checkboxList">
<label>
<input type="checkbox" is="emby-checkbox" id="chkSunday" />
<span>${Sunday}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" id="chkMonday" />
<span>${Monday}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" id="chkTuesday" />
<span>${Tuesday}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" id="chkWednesday" />
<span>${Wednesday}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" id="chkThursday" />
<span>${Thursday}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" id="chkFriday" />
<span>${Friday}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" id="chkSaturday" />
<span>${Saturday}</span>
</label>
</div>
<br />
</div>
<br />
<div class="inputContainer">
<input is="emby-input" type="number" id="txtPrePaddingMinutes" pattern="[0-9]*" required="required" min="0" step="1" label="${LabelPrePaddingMinutes}" />
</div>
<div class="inputContainer">
<input is="emby-input" type="number" id="txtPostPaddingMinutes" pattern="[0-9]*" required="required" min="0" step="1" label="${LabelPostPaddingMinutes}" />
</div>
</div>
<br />
<div class="inputContainer">
<input is="emby-input" type="number" id="txtPrePaddingMinutes" pattern="[0-9]*" required="required" min="0" step="1" label="${LabelPrePaddingMinutes}" />
</div>
<div class="inputContainer">
<input is="emby-input" type="number" id="txtPostPaddingMinutes" pattern="[0-9]*" required="required" min="0" step="1" label="${LabelPostPaddingMinutes}" />
</div>
</emby-collapsible>
</div>
<br />
</div>
<br />

View File

@ -1,6 +1,6 @@
{
"name": "hls.js",
"version": "0.5.40",
"version": "0.5.41",
"license": "Apache-2.0",
"description": "Media Source Extension - HLS library, by/for Dailymotion",
"homepage": "https://github.com/dailymotion/hls.js",
@ -16,11 +16,11 @@
"test",
"tests"
],
"_release": "0.5.40",
"_release": "0.5.41",
"_resolution": {
"type": "version",
"tag": "v0.5.40",
"commit": "878b91d51073a6f4d7b6528c204208c3e2a4cf48"
"tag": "v0.5.41",
"commit": "e8d85916067a06ab823d66bf291ace771b886478"
},
"_source": "git://github.com/dailymotion/hls.js.git",
"_target": "~0.5.7",

View File

@ -170,6 +170,7 @@ configuration parameters could be provided to hls.js upon instantiation of Hls O
var config = {
autoStartLoad : true,
startPosition : -1,
capLevelToPlayerSize: false,
debug : false,
defaultAudioCodec : undefined,
@ -228,7 +229,14 @@ a logger object could also be provided for custom logging : ```config.debug=cust
(default true)
- if set to true, start level playlist and first fragments will be loaded automatically, after triggering of ```Hls.Events.MANIFEST_PARSED``` event
- if set to false, an explicit API call (```hls.startLoad()```) will be needed to start quality level/fragment loading.
- if set to false, an explicit API call (```hls.startLoad(startPosition=-1)```) will be needed to start quality level/fragment loading.
#### ```startPosition```
(default -1)
- if set to -1, playback will start from initialTime=0 for VoD and according to ```liveSyncDuration/liveSyncDurationCount``` config params for Live
- Otherwise, playback will start from predefined value. (unless stated otherwise in ```autoStartLoad=false``` mode : in that case startPosition can be overrided using ```hls.startLoad(startPosition)```).
#### ```defaultAudioCodec```
(default undefined)
@ -477,36 +485,46 @@ parameter should be a boolean
#### ```abrEwmaFastLive```
(default : 5.0)
Fast bitrate Exponential moving average half-life , used to compute average bitrate for Live streams
Fast bitrate Exponential moving average half-life, used to compute average bitrate for Live streams
Half of the estimate is based on the last abrEwmaFastLive seconds of sample history.
Each of the sample is weighted by the fragment loading duration.
parameter should be a float greater than 0
#### ```abrEwmaSlowLive```
(default : 9.0)
Slow bitrate Exponential moving average half-life , used to compute average bitrate for Live streams
Slow bitrate Exponential moving average half-life, used to compute average bitrate for Live streams
Half of the estimate is based on the last abrEwmaSlowLive seconds of sample history.
Each of the sample is weighted by the fragment loading duration.
parameter should be a float greater than abrEwmaFastLive
parameter should be a float greater than abrEwmaFastLive
#### ```abrEwmaFastVoD```
(default : 4.0)
Fast bitrate Exponential moving average half-life , used to compute average bitrate for VoD streams
Fast bitrate Exponential moving average half-life, used to compute average bitrate for VoD streams
Half of the estimate is based on the last abrEwmaFastVoD seconds of sample history.
Each of the sample is weighted by the fragment loading duration.
parameter should be a float greater than 0
#### ```abrEwmaSlowVoD```
(default : 15.0)
Slow bitrate Exponential moving average half-life , used to compute average bitrate for VoD streams
Slow bitrate Exponential moving average half-life, used to compute average bitrate for VoD streams
Half of the estimate is based on the last abrEwmaSlowVoD seconds of sample history.
Each of the sample is weighted by the fragment loading duration.
parameter should be a float greater than abrEwmaFastVoD
#### ```abrEwmaDefaultEstimate```
(default : 500000)
Default bandwidth estimate in bits/second prior to collecting fragment bandwidth samples.
parameter should be a float
#### ```abrBandWidthFactor```
(default : 0.8)
@ -599,9 +617,12 @@ by default, hls.js will automatically start loading quality level playlists, and
however if ```config.autoStartLoad``` is set to ```false```, the following method needs to be called to manually start playlist and fragments loading:
#### ```hls.startLoad()```
#### ```hls.startLoad(startPosition=-1)```
start/restart playlist/fragment loading. this is only effective if MANIFEST_PARSED event has been triggered and video element has been attached to hls object.
startPosition is the initial position in the playlist.
if startPosition is not set to -1, it allows to override default startPosition to the one you want (it will bypass hls.config.liveSync* config params for Live for example, so that user can start playback from whatever position)
#### ```hls.stopLoad()```
stop playlist/fragment loading. could be resumed later on by calling ```hls.startLoad()```

View File

@ -1,6 +1,6 @@
{
"name": "hls.js",
"version": "0.5.40",
"version": "0.5.41",
"license": "Apache-2.0",
"description": "Media Source Extension - HLS library, by/for Dailymotion",
"homepage": "https://github.com/dailymotion/hls.js",

View File

@ -48,7 +48,7 @@ design idea is pretty simple :
- [src/controller/abr-controller.js][]
- in charge of determining auto quality level.
- auto quality switch algorithm is bitrate based : fragment loading bitrate is monitored and smoothed using 2 exponential weighted moving average (a fast one, to adapt quickly on bandwidth drop and a slow one, to avoid ramping up to quickly on bandwidth increase)
- auto quality switch algorithm is bitrate based : fragment loading bitrate is monitored and smoothed using 2 exponential weighted moving average (a fast one, to adapt quickly on bandwidth drop and a slow one, to avoid ramping up too quickly on bandwidth increase)
- in charge of **monitoring fragment loading speed** (by monitoring data received from FRAG_LOAD_PROGRESS event)
- "expected time of fragment load completion" is computed using "fragment loading instant bandwidth".
- this time is compared to the "expected time of buffer starvation".

View File

@ -452,7 +452,7 @@ var AbrController = function (_EventHandler) {
ewmaFast = config.abrEwmaFastVoD;
ewmaSlow = config.abrEwmaSlowVoD;
}
this.bwEstimator = new _ewmaBandwidthEstimator2.default(hls, ewmaSlow, ewmaFast);
this.bwEstimator = new _ewmaBandwidthEstimator2.default(hls, ewmaSlow, ewmaFast, config.abrEwmaDefaultEstimate);
}
var frag = data.frag;
@ -603,7 +603,7 @@ var AbrController = function (_EventHandler) {
return Math.min(this._nextAutoLevel, maxAutoLevel);
}
var avgbw = this.bwEstimator.getEstimate(),
var avgbw = this.bwEstimator ? this.bwEstimator.getEstimate() : config.abrEwmaDefaultEstimate,
adjustedbw = void 0;
// follow algorithm captured from stagefright :
// https://android.googlesource.com/platform/frameworks/av/+/master/media/libstagefright/httplive/LiveSession.cpp
@ -1251,11 +1251,11 @@ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { de
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var EwmaBandWidthEstimator = function () {
function EwmaBandWidthEstimator(hls, slow, fast) {
function EwmaBandWidthEstimator(hls, slow, fast, defaultEstimate) {
_classCallCheck(this, EwmaBandWidthEstimator);
this.hls = hls;
this.defaultEstimate_ = 5e5; // 500kbps
this.defaultEstimate_ = defaultEstimate;
this.minWeight_ = 0.001;
this.minDelayMs_ = 50;
this.slow_ = new _ewma2.default(slow);
@ -1277,7 +1277,7 @@ var EwmaBandWidthEstimator = function () {
}, {
key: 'getEstimate',
value: function getEstimate() {
if (!this.fast_ || this.fast_.getTotalWeight() < this.minWeight_) {
if (!this.fast_ || !this.slow_ || this.fast_.getTotalWeight() < this.minWeight_) {
return this.defaultEstimate_;
}
//console.log('slow estimate:'+ Math.round(this.slow_.getEstimate()));
@ -1739,9 +1739,7 @@ var StreamController = function (_EventHandler) {
}
}, {
key: 'startLoad',
value: function startLoad() {
var startPosition = arguments.length <= 0 || arguments[0] === undefined ? 0 : arguments[0];
value: function startLoad(startPosition) {
if (this.levels) {
var media = this.media,
lastCurrentTime = this.lastCurrentTime;
@ -1752,7 +1750,7 @@ var StreamController = function (_EventHandler) {
}
this.level = -1;
this.fragLoadError = 0;
if (media && lastCurrentTime) {
if (media && lastCurrentTime > 0) {
_logger.logger.log('configure startPosition @' + lastCurrentTime);
if (!this.lastPaused) {
_logger.logger.log('resuming video');
@ -1978,10 +1976,10 @@ var StreamController = function (_EventHandler) {
var deltaPTS = fragPrevious.deltaPTS,
curSNIdx = frag.sn - levelDetails.startSN;
// if there is a significant delta between audio and video, larger than max allowed hole,
// it might be because video fragment does not start with a keyframe.
// and if previous remuxed fragment did not start with a keyframe. (fragPrevious.dropped)
// let's try to load previous fragment again to get last keyframe
// then we will reload again current fragment (that way we should be able to fill the buffer hole ...)
if (deltaPTS && deltaPTS > config.maxBufferHole) {
if (deltaPTS && deltaPTS > config.maxBufferHole && fragPrevious.dropped) {
frag = fragments[curSNIdx - 1];
_logger.logger.warn('SN just loaded, with large PTS gap between audio and video, maybe frag is not starting with a keyframe ? load previous one to try to overcome this');
// decrement previous frag load counter to avoid frag loop loading error when next fragment will get reloaded
@ -1990,6 +1988,10 @@ var StreamController = function (_EventHandler) {
frag = fragments[curSNIdx + 1];
_logger.logger.log('SN just loaded, load next one: ' + frag.sn);
}
// ensure frag is not undefined
if (!frag) {
break;
}
} else {
// have we reached end of VOD playlist ?
if (!levelDetails.live) {
@ -2158,8 +2160,16 @@ var StreamController = function (_EventHandler) {
_logger.logger.log('immediateLevelSwitch');
if (!this.immediateSwitch) {
this.immediateSwitch = true;
this.previouslyPaused = this.media.paused;
this.media.pause();
var media = this.media,
previouslyPaused = void 0;
if (media) {
previouslyPaused = media.paused;
media.pause();
} else {
// don't restart playback after instant level switch in case media not attached
previouslyPaused = true;
}
this.previouslyPaused = previouslyPaused;
}
var fragCurrent = this.fragCurrent;
if (fragCurrent && fragCurrent.loader) {
@ -2257,8 +2267,9 @@ var StreamController = function (_EventHandler) {
media.addEventListener('seeking', this.onvseeking);
media.addEventListener('seeked', this.onvseeked);
media.addEventListener('ended', this.onvended);
if (this.levels && this.config.autoStartLoad) {
this.hls.startLoad();
var config = this.config;
if (this.levels && config.autoStartLoad) {
this.hls.startLoad(config.startPosition);
}
}
}, {
@ -2373,8 +2384,9 @@ var StreamController = function (_EventHandler) {
this.levels = data.levels;
this.startLevelLoaded = false;
this.startFragRequested = false;
if (this.config.autoStartLoad) {
this.hls.startLoad();
var config = this.config;
if (config.autoStartLoad) {
this.hls.startLoad(config.startPosition);
}
}
}, {
@ -2411,12 +2423,23 @@ var StreamController = function (_EventHandler) {
curLevel.details = newDetails;
this.hls.trigger(_events2.default.LEVEL_UPDATED, { details: newDetails, level: newLevelId });
// compute start position
if (this.startFragRequested === false) {
// if live playlist, set start position to be fragment N-this.config.liveSyncDurationCount (usually 3)
if (newDetails.live) {
var targetLatency = this.config.liveSyncDuration !== undefined ? this.config.liveSyncDuration : this.config.liveSyncDurationCount * newDetails.targetduration;
this.startPosition = Math.max(0, sliding + duration - targetLatency);
// compute start position if set to -1. use it straight away if value is defined
if (this.startPosition === -1) {
// first, check if start time offset has been set in playlist, if yes, use this value
var startTimeOffset = newDetails.startTimeOffset;
if (!isNaN(startTimeOffset)) {
_logger.logger.log('start time offset found in playlist, adjust startPosition to ' + startTimeOffset);
this.startPosition = startTimeOffset;
} else {
// if live playlist, set start position to be fragment N-this.config.liveSyncDurationCount (usually 3)
if (newDetails.live) {
var targetLatency = this.config.liveSyncDuration !== undefined ? this.config.liveSyncDuration : this.config.liveSyncDurationCount * newDetails.targetduration;
this.startPosition = Math.max(0, sliding + duration - targetLatency);
} else {
this.startPosition = 0;
}
}
}
this.nextLoadPosition = this.startPosition;
}
@ -2474,7 +2497,7 @@ var StreamController = function (_EventHandler) {
}
}
this.pendingAppending = 0;
_logger.logger.log('Demuxing ' + sn + ' of [' + details.startSN + ' ,' + details.endSN + '],level ' + level);
_logger.logger.log('Demuxing ' + sn + ' of [' + details.startSN + ' ,' + details.endSN + '],level ' + level + ', cc ' + fragCurrent.cc);
var demuxer = this.demuxer;
if (demuxer) {
demuxer.push(data.payload, audioCodec, currentLevel.videoCodec, start, fragCurrent.cc, level, sn, duration, fragCurrent.decryptdata);
@ -2576,12 +2599,17 @@ var StreamController = function (_EventHandler) {
var level = this.levels[this.level],
frag = this.fragCurrent;
_logger.logger.log('parsed ' + data.type + ',PTS:[' + data.startPTS.toFixed(3) + ',' + data.endPTS.toFixed(3) + '],DTS:[' + data.startDTS.toFixed(3) + '/' + data.endDTS.toFixed(3) + '],nb:' + data.nb);
_logger.logger.log('parsed ' + data.type + ',PTS:[' + data.startPTS.toFixed(3) + ',' + data.endPTS.toFixed(3) + '],DTS:[' + data.startDTS.toFixed(3) + '/' + data.endDTS.toFixed(3) + '],nb:' + data.nb + ',dropped:' + (data.dropped || 0));
var drift = _levelHelper2.default.updateFragPTSDTS(level.details, frag.sn, data.startPTS, data.endPTS, data.startDTS, data.endDTS),
hls = this.hls;
hls.trigger(_events2.default.LEVEL_PTS_UPDATED, { details: level.details, level: this.level, drift: drift });
// has remuxer dropped video frames located before first keyframe ?
if (data.type === 'video') {
frag.dropped = data.dropped;
}
[data.data1, data.data2].forEach(function (buffer) {
if (buffer) {
_this2.pendingAppending++;
@ -3905,7 +3933,7 @@ var DemuxerWorker = function DemuxerWorker(self) {
});
observer.on(_events2.default.FRAG_PARSING_DATA, function (ev, data) {
var objData = { event: ev, type: data.type, startPTS: data.startPTS, endPTS: data.endPTS, startDTS: data.startDTS, endDTS: data.endDTS, data1: data.data1.buffer, data2: data.data2.buffer, nb: data.nb };
var objData = { event: ev, type: data.type, startPTS: data.startPTS, endPTS: data.endPTS, startDTS: data.startDTS, endDTS: data.endDTS, data1: data.data1.buffer, data2: data.data2.buffer, nb: data.nb, dropped: data.dropped };
// pass data1/data2 as transferable object (no copy)
self.postMessage(objData, [objData.data1, objData.data2]);
});
@ -4055,7 +4083,8 @@ var Demuxer = function () {
startDTS: data.startDTS,
endDTS: data.endDTS,
type: data.type,
nb: data.nb
nb: data.nb,
dropped: data.dropped
});
break;
case _events2.default.FRAG_PARSING_METADATA:
@ -4653,7 +4682,7 @@ var TSDemuxer = function () {
value: function switchLevel() {
this.pmtParsed = false;
this._pmtId = -1;
this._avcTrack = { container: 'video/mp2t', type: 'video', id: -1, sequenceNumber: 0, samples: [], len: 0, nbNalu: 0 };
this._avcTrack = { container: 'video/mp2t', type: 'video', id: -1, sequenceNumber: 0, samples: [], len: 0, nbNalu: 0, dropped: 0 };
this._aacTrack = { container: 'video/mp2t', type: 'audio', id: -1, sequenceNumber: 0, samples: [], len: 0 };
this._id3Track = { type: 'id3', id: -1, sequenceNumber: 0, samples: [], len: 0 };
this._txtTrack = { type: 'text', id: -1, sequenceNumber: 0, samples: [], len: 0 };
@ -4999,6 +5028,9 @@ var TSDemuxer = function () {
samples.push(avcSample);
track.len += length;
track.nbNalu += units2.length;
} else {
// dropped samples, track it
track.dropped++;
}
units2 = [];
length = 0;
@ -5966,6 +5998,7 @@ var Hls = function () {
if (!Hls.defaultConfig) {
Hls.defaultConfig = {
autoStartLoad: true,
startPosition: -1,
debug: false,
capLevelToPlayerSize: false,
maxBufferLength: 30,
@ -6010,6 +6043,7 @@ var Hls = function () {
abrEwmaSlowLive: 9,
abrEwmaFastVoD: 4,
abrEwmaSlowVoD: 15,
abrEwmaDefaultEstimate: 5e5, // 500 kbps
abrBandWidthFactor: 0.8,
abrBandWidthUpFactor: 0.7
};
@ -6125,7 +6159,7 @@ var Hls = function () {
}, {
key: 'startLoad',
value: function startLoad() {
var startPosition = arguments.length <= 0 || arguments[0] === undefined ? 0 : arguments[0];
var startPosition = arguments.length <= 0 || arguments[0] === undefined ? -1 : arguments[0];
_logger.logger.log('startLoad');
this.levelController.startLoad();
@ -6685,7 +6719,7 @@ var PlaylistLoader = function (_EventHandler) {
byteRangeEndOffset,
byteRangeStartOffset;
regexp = /(?:#EXT-X-(MEDIA-SEQUENCE):(\d+))|(?:#EXT-X-(TARGETDURATION):(\d+))|(?:#EXT-X-(KEY):(.*))|(?:#EXT(INF):([\d\.]+)[^\r\n]*([\r\n]+[^#|\r\n]+)?)|(?:#EXT-X-(BYTERANGE):([\d]+[@[\d]*)]*[\r\n]+([^#|\r\n]+)?|(?:#EXT-X-(ENDLIST))|(?:#EXT-X-(DIS)CONTINUITY))|(?:#EXT-X-(PROGRAM-DATE-TIME):(.*)[\r\n]+([^#|\r\n]+)?)/g;
regexp = /(?:#EXT-X-(MEDIA-SEQUENCE):(\d+))|(?:#EXT-X-(TARGETDURATION):(\d+))|(?:#EXT-X-(KEY):(.*))|(?:#EXT-X-(START):(.*))|(?:#EXT(INF):([\d\.]+)[^\r\n]*([\r\n]+[^#|\r\n]+)?)|(?:#EXT-X-(BYTERANGE):([\d]+[@[\d]*)]*[\r\n]+([^#|\r\n]+)?|(?:#EXT-X-(ENDLIST))|(?:#EXT-X-(DIS)CONTINUITY))|(?:#EXT-X-(PROGRAM-DATE-TIME):(.*)[\r\n]+([^#|\r\n]+)?)/g;
while ((result = regexp.exec(string)) !== null) {
result.shift();
result = result.filter(function (n) {
@ -6760,6 +6794,14 @@ var PlaylistLoader = function (_EventHandler) {
}
}
break;
case 'START':
var startParams = result[1];
var startAttrs = new _attrList2.default(startParams);
var startTimeOffset = startAttrs.decimalFloatingPoint('TIME-OFFSET');
if (startTimeOffset) {
level.startTimeOffset = startTimeOffset;
}
break;
case 'PROGRAM-DATE-TIME':
programDateTime = new Date(Date.parse(result[1]));
if (frag && !frag.url && result.length >= 3) {
@ -7656,8 +7698,10 @@ var MP4Remuxer = function () {
}
// next AVC sample DTS should be equal to last sample DTS + last sample duration
this.nextAvcDts = dtsnorm + lastSampleDuration * pes2mp4ScaleFactor;
var dropped = track.dropped;
track.len = 0;
track.nbNalu = 0;
track.dropped = 0;
if (samples.length && navigator.userAgent.toLowerCase().indexOf('chrome') > -1) {
flags = samples[0].flags;
// chrome workaround, mark first sample as being a Random Access Point to avoid sourcebuffer append issue
@ -7676,7 +7720,8 @@ var MP4Remuxer = function () {
startDTS: firstDTS / pesTimeScale,
endDTS: this.nextAvcDts / pesTimeScale,
type: 'video',
nb: samples.length
nb: samples.length,
dropped: dropped
});
}
}, {
@ -7977,7 +8022,8 @@ var PassThroughRemuxer = function () {
startPTS: timeOffset,
startDTS: timeOffset,
type: 'audiovideo',
nb: 1
nb: 1,
dropped: 0
});
}
}, {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,6 +1,6 @@
{
"name": "hls.js",
"version": "0.5.40",
"version": "0.5.41",
"license": "Apache-2.0",
"description": "Media Source Extension - HLS library, by/for Dailymotion",
"homepage": "https://github.com/dailymotion/hls.js",

View File

@ -50,7 +50,7 @@ class AbrController extends EventHandler {
ewmaFast = config.abrEwmaFastVoD;
ewmaSlow = config.abrEwmaSlowVoD;
}
this.bwEstimator = new EwmaBandWidthEstimator(hls,ewmaSlow,ewmaFast);
this.bwEstimator = new EwmaBandWidthEstimator(hls,ewmaSlow,ewmaFast,config.abrEwmaDefaultEstimate);
}
let frag = data.frag;
@ -184,7 +184,8 @@ class AbrController extends EventHandler {
return Math.min(this._nextAutoLevel,maxAutoLevel);
}
let avgbw = this.bwEstimator.getEstimate(),adjustedbw;
let avgbw = this.bwEstimator ? this.bwEstimator.getEstimate() : config.abrEwmaDefaultEstimate,
adjustedbw;
// follow algorithm captured from stagefright :
// https://android.googlesource.com/platform/frameworks/av/+/master/media/libstagefright/httplive/LiveSession.cpp
// Pick the highest bandwidth stream below or equal to estimated bandwidth.

View File

@ -11,9 +11,9 @@ import EWMA from '../utils/ewma';
class EwmaBandWidthEstimator {
constructor(hls,slow,fast) {
constructor(hls,slow,fast,defaultEstimate) {
this.hls = hls;
this.defaultEstimate_ = 5e5; // 500kbps
this.defaultEstimate_ = defaultEstimate;
this.minWeight_ = 0.001;
this.minDelayMs_ = 50;
this.slow_ = new EWMA(slow);
@ -32,7 +32,7 @@ class EwmaBandWidthEstimator {
getEstimate() {
if (!this.fast_ || this.fast_.getTotalWeight() < this.minWeight_) {
if (!this.fast_ || !this.slow_ || this.fast_.getTotalWeight() < this.minWeight_) {
return this.defaultEstimate_;
}
//console.log('slow estimate:'+ Math.round(this.slow_.getEstimate()));

View File

@ -61,7 +61,7 @@ class StreamController extends EventHandler {
this.state = State.STOPPED;
}
startLoad(startPosition=0) {
startLoad(startPosition) {
if (this.levels) {
var media = this.media, lastCurrentTime = this.lastCurrentTime;
this.stopLoad();
@ -71,7 +71,7 @@ class StreamController extends EventHandler {
}
this.level = -1;
this.fragLoadError = 0;
if (media && lastCurrentTime) {
if (media && lastCurrentTime > 0) {
logger.log(`configure startPosition @${lastCurrentTime}`);
if (!this.lastPaused) {
logger.log('resuming video');
@ -288,10 +288,10 @@ class StreamController extends EventHandler {
let deltaPTS = fragPrevious.deltaPTS,
curSNIdx = frag.sn - levelDetails.startSN;
// if there is a significant delta between audio and video, larger than max allowed hole,
// it might be because video fragment does not start with a keyframe.
// and if previous remuxed fragment did not start with a keyframe. (fragPrevious.dropped)
// let's try to load previous fragment again to get last keyframe
// then we will reload again current fragment (that way we should be able to fill the buffer hole ...)
if (deltaPTS && deltaPTS > config.maxBufferHole) {
if (deltaPTS && deltaPTS > config.maxBufferHole && fragPrevious.dropped) {
frag = fragments[curSNIdx-1];
logger.warn(`SN just loaded, with large PTS gap between audio and video, maybe frag is not starting with a keyframe ? load previous one to try to overcome this`);
// decrement previous frag load counter to avoid frag loop loading error when next fragment will get reloaded
@ -300,6 +300,10 @@ class StreamController extends EventHandler {
frag = fragments[curSNIdx+1];
logger.log(`SN just loaded, load next one: ${frag.sn}`);
}
// ensure frag is not undefined
if(!frag) {
break;
}
} else {
// have we reached end of VOD playlist ?
if (!levelDetails.live) {
@ -489,8 +493,15 @@ class StreamController extends EventHandler {
logger.log('immediateLevelSwitch');
if (!this.immediateSwitch) {
this.immediateSwitch = true;
this.previouslyPaused = this.media.paused;
this.media.pause();
let media = this.media, previouslyPaused;
if (media) {
previouslyPaused = media.paused;
media.pause();
} else {
// don't restart playback after instant level switch in case media not attached
previouslyPaused = true;
}
this.previouslyPaused = previouslyPaused;
}
var fragCurrent = this.fragCurrent;
if (fragCurrent && fragCurrent.loader) {
@ -579,8 +590,9 @@ class StreamController extends EventHandler {
media.addEventListener('seeking', this.onvseeking);
media.addEventListener('seeked', this.onvseeked);
media.addEventListener('ended', this.onvended);
if(this.levels && this.config.autoStartLoad) {
this.hls.startLoad();
let config = this.config;
if(this.levels && config.autoStartLoad) {
this.hls.startLoad(config.startPosition);
}
}
@ -688,8 +700,9 @@ class StreamController extends EventHandler {
this.levels = data.levels;
this.startLevelLoaded = false;
this.startFragRequested = false;
if (this.config.autoStartLoad) {
this.hls.startLoad();
let config = this.config;
if (config.autoStartLoad) {
this.hls.startLoad(config.startPosition);
}
}
@ -725,12 +738,23 @@ class StreamController extends EventHandler {
curLevel.details = newDetails;
this.hls.trigger(Event.LEVEL_UPDATED, { details: newDetails, level: newLevelId });
// compute start position
if (this.startFragRequested === false) {
// if live playlist, set start position to be fragment N-this.config.liveSyncDurationCount (usually 3)
if (newDetails.live) {
let targetLatency = this.config.liveSyncDuration !== undefined ? this.config.liveSyncDuration : this.config.liveSyncDurationCount * newDetails.targetduration;
this.startPosition = Math.max(0, sliding + duration - targetLatency);
// compute start position if set to -1. use it straight away if value is defined
if (this.startPosition === -1) {
// first, check if start time offset has been set in playlist, if yes, use this value
let startTimeOffset = newDetails.startTimeOffset;
if(!isNaN(startTimeOffset)) {
logger.log(`start time offset found in playlist, adjust startPosition to ${startTimeOffset}`);
this.startPosition = startTimeOffset;
} else {
// if live playlist, set start position to be fragment N-this.config.liveSyncDurationCount (usually 3)
if (newDetails.live) {
let targetLatency = this.config.liveSyncDuration !== undefined ? this.config.liveSyncDuration : this.config.liveSyncDurationCount * newDetails.targetduration;
this.startPosition = Math.max(0, sliding + duration - targetLatency);
} else {
this.startPosition = 0;
}
}
}
this.nextLoadPosition = this.startPosition;
}
@ -789,7 +813,7 @@ class StreamController extends EventHandler {
}
}
this.pendingAppending = 0;
logger.log(`Demuxing ${sn} of [${details.startSN} ,${details.endSN}],level ${level}`);
logger.log(`Demuxing ${sn} of [${details.startSN} ,${details.endSN}],level ${level}, cc ${fragCurrent.cc}`);
let demuxer = this.demuxer;
if (demuxer) {
demuxer.push(data.payload, audioCodec, currentLevel.videoCodec, start, fragCurrent.cc, level, sn, duration, fragCurrent.decryptdata);
@ -885,12 +909,17 @@ class StreamController extends EventHandler {
var level = this.levels[this.level],
frag = this.fragCurrent;
logger.log(`parsed ${data.type},PTS:[${data.startPTS.toFixed(3)},${data.endPTS.toFixed(3)}],DTS:[${data.startDTS.toFixed(3)}/${data.endDTS.toFixed(3)}],nb:${data.nb}`);
logger.log(`parsed ${data.type},PTS:[${data.startPTS.toFixed(3)},${data.endPTS.toFixed(3)}],DTS:[${data.startDTS.toFixed(3)}/${data.endDTS.toFixed(3)}],nb:${data.nb},dropped:${data.dropped || 0}`);
var drift = LevelHelper.updateFragPTSDTS(level.details,frag.sn,data.startPTS,data.endPTS,data.startDTS,data.endDTS),
hls = this.hls;
hls.trigger(Event.LEVEL_PTS_UPDATED, {details: level.details, level: this.level, drift: drift});
// has remuxer dropped video frames located before first keyframe ?
if(data.type === 'video') {
frag.dropped = data.dropped;
}
[data.data1, data.data2].forEach(buffer => {
if (buffer) {
this.pendingAppending++;

View File

@ -38,7 +38,7 @@ var DemuxerWorker = function (self) {
});
observer.on(Event.FRAG_PARSING_DATA, function(ev, data) {
var objData = {event: ev, type: data.type, startPTS: data.startPTS, endPTS: data.endPTS, startDTS: data.startDTS, endDTS: data.endDTS, data1: data.data1.buffer, data2: data.data2.buffer, nb: data.nb};
var objData = {event: ev, type: data.type, startPTS: data.startPTS, endPTS: data.endPTS, startDTS: data.startDTS, endDTS: data.endDTS, data1: data.data1.buffer, data2: data.data2.buffer, nb: data.nb, dropped : data.dropped};
// pass data1/data2 as transferable object (no copy)
self.postMessage(objData, [objData.data1, objData.data2]);
});

View File

@ -88,7 +88,8 @@ class Demuxer {
startDTS: data.startDTS,
endDTS: data.endDTS,
type: data.type,
nb: data.nb
nb: data.nb,
dropped: data.dropped,
});
break;
case Event.FRAG_PARSING_METADATA:

View File

@ -37,7 +37,7 @@
switchLevel() {
this.pmtParsed = false;
this._pmtId = -1;
this._avcTrack = {container : 'video/mp2t', type: 'video', id :-1, sequenceNumber: 0, samples : [], len : 0, nbNalu : 0};
this._avcTrack = {container : 'video/mp2t', type: 'video', id :-1, sequenceNumber: 0, samples : [], len : 0, nbNalu : 0, dropped : 0};
this._aacTrack = {container : 'video/mp2t', type: 'audio', id :-1, sequenceNumber: 0, samples : [], len : 0};
this._id3Track = {type: 'id3', id :-1, sequenceNumber: 0, samples : [], len : 0};
this._txtTrack = {type: 'text', id: -1, sequenceNumber: 0, samples: [], len: 0};
@ -355,6 +355,9 @@
samples.push(avcSample);
track.len += length;
track.nbNalu += units2.length;
} else {
// dropped samples, track it
track.dropped++;
}
units2 = [];
length = 0;

View File

@ -43,6 +43,7 @@ class Hls {
if(!Hls.defaultConfig) {
Hls.defaultConfig = {
autoStartLoad: true,
startPosition: -1,
debug: false,
capLevelToPlayerSize: false,
maxBufferLength: 30,
@ -87,6 +88,7 @@ class Hls {
abrEwmaSlowLive: 9,
abrEwmaFastVoD: 4,
abrEwmaSlowVoD: 15,
abrEwmaDefaultEstimate: 5e5, // 500 kbps
abrBandWidthFactor : 0.8,
abrBandWidthUpFactor : 0.7
};
@ -181,7 +183,7 @@ class Hls {
this.trigger(Event.MANIFEST_LOADING, {url: url});
}
startLoad(startPosition=0) {
startLoad(startPosition=-1) {
logger.log('startLoad');
this.levelController.startLoad();
this.streamController.startLoad(startPosition);

View File

@ -137,7 +137,7 @@ class PlaylistLoader extends EventHandler {
byteRangeEndOffset,
byteRangeStartOffset;
regexp = /(?:#EXT-X-(MEDIA-SEQUENCE):(\d+))|(?:#EXT-X-(TARGETDURATION):(\d+))|(?:#EXT-X-(KEY):(.*))|(?:#EXT(INF):([\d\.]+)[^\r\n]*([\r\n]+[^#|\r\n]+)?)|(?:#EXT-X-(BYTERANGE):([\d]+[@[\d]*)]*[\r\n]+([^#|\r\n]+)?|(?:#EXT-X-(ENDLIST))|(?:#EXT-X-(DIS)CONTINUITY))|(?:#EXT-X-(PROGRAM-DATE-TIME):(.*)[\r\n]+([^#|\r\n]+)?)/g;
regexp = /(?:#EXT-X-(MEDIA-SEQUENCE):(\d+))|(?:#EXT-X-(TARGETDURATION):(\d+))|(?:#EXT-X-(KEY):(.*))|(?:#EXT-X-(START):(.*))|(?:#EXT(INF):([\d\.]+)[^\r\n]*([\r\n]+[^#|\r\n]+)?)|(?:#EXT-X-(BYTERANGE):([\d]+[@[\d]*)]*[\r\n]+([^#|\r\n]+)?|(?:#EXT-X-(ENDLIST))|(?:#EXT-X-(DIS)CONTINUITY))|(?:#EXT-X-(PROGRAM-DATE-TIME):(.*)[\r\n]+([^#|\r\n]+)?)/g;
while ((result = regexp.exec(string)) !== null) {
result.shift();
result = result.filter(function(n) { return (n !== undefined); });
@ -210,6 +210,14 @@ class PlaylistLoader extends EventHandler {
}
}
break;
case 'START':
let startParams = result[1];
let startAttrs = new AttrList(startParams);
let startTimeOffset = startAttrs.decimalFloatingPoint('TIME-OFFSET');
if (startTimeOffset) {
level.startTimeOffset = startTimeOffset;
}
break;
case 'PROGRAM-DATE-TIME':
programDateTime = new Date(Date.parse(result[1]));
if (frag && !frag.url && result.length >= 3) {

View File

@ -247,8 +247,10 @@ class MP4Remuxer {
}
// next AVC sample DTS should be equal to last sample DTS + last sample duration
this.nextAvcDts = dtsnorm + lastSampleDuration * pes2mp4ScaleFactor;
let dropped = track.dropped;
track.len = 0;
track.nbNalu = 0;
track.dropped = 0;
if(samples.length && navigator.userAgent.toLowerCase().indexOf('chrome') > -1) {
flags = samples[0].flags;
// chrome workaround, mark first sample as being a Random Access Point to avoid sourcebuffer append issue
@ -267,7 +269,8 @@ class MP4Remuxer {
startDTS: firstDTS / pesTimeScale,
endDTS: this.nextAvcDts / pesTimeScale,
type: 'video',
nb: samples.length
nb: samples.length,
dropped : dropped
});
}

View File

@ -62,7 +62,8 @@ class PassThroughRemuxer {
startPTS: timeOffset,
startDTS: timeOffset,
type: 'audiovideo',
nb: 1
nb: 1,
dropped : 0
});
}
}

View File

@ -1,6 +1,6 @@
{
"name": "iron-a11y-keys-behavior",
"version": "1.1.5",
"version": "1.1.6",
"description": "A behavior that enables keybindings for greater a11y.",
"keywords": [
"web-components",
@ -30,14 +30,14 @@
"webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0"
},
"ignore": [],
"homepage": "https://github.com/PolymerElements/iron-a11y-keys-behavior",
"_release": "1.1.5",
"homepage": "https://github.com/polymerelements/iron-a11y-keys-behavior",
"_release": "1.1.6",
"_resolution": {
"type": "version",
"tag": "v1.1.5",
"commit": "b6f935cf203b328651f96ac3d21979cac7c9c241"
"tag": "v1.1.6",
"commit": "28435b2e02d0e5e5268e984d925b03643cea5e34"
},
"_source": "git://github.com/PolymerElements/iron-a11y-keys-behavior.git",
"_source": "git://github.com/polymerelements/iron-a11y-keys-behavior.git",
"_target": "^1.0.0",
"_originalSource": "PolymerElements/iron-a11y-keys-behavior"
"_originalSource": "polymerelements/iron-a11y-keys-behavior"
}

View File

@ -1,6 +1,6 @@
{
"name": "iron-a11y-keys-behavior",
"version": "1.1.5",
"version": "1.1.6",
"description": "A behavior that enables keybindings for greater a11y.",
"keywords": [
"web-components",

View File

@ -236,12 +236,31 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
* and uses an expressive syntax to filter key presses.
*
* Use the `keyBindings` prototype property to express what combination of keys
* will trigger the event to fire.
* will trigger the callback. A key binding has the format
* `"KEY+MODIFIER:EVENT": "callback"` (`"KEY": "callback"` or
* `"KEY:EVENT": "callback"` are valid as well). Some examples:
*
* Use the `key-event-target` attribute to set up event handlers on a specific
* keyBindings: {
* 'space': '_onKeydown', // same as 'space:keydown'
* 'shift+tab': '_onKeydown',
* 'enter:keypress': '_onKeypress',
* 'esc:keyup': '_onKeyup'
* }
*
* The callback will receive with an event containing the following information in `event.detail`:
*
* _onKeydown: function(event) {
* console.log(event.detail.combo); // KEY+MODIFIER, e.g. "shift+tab"
* console.log(event.detail.key); // KEY only, e.g. "tab"
* console.log(event.detail.event); // EVENT, e.g. "keydown"
* console.log(event.detail.keyboardEvent); // the original KeyboardEvent
* }
*
* Use the `keyEventTarget` attribute to set up event handlers on a specific
* node.
* The `keys-pressed` event will fire when one of the key combinations set with the
* `keys` property is pressed.
*
* See the [demo source code](https://github.com/PolymerElements/iron-a11y-keys-behavior/blob/master/demo/x-key-aware.html)
* for an example.
*
* @demo demo/index.html
* @polymerBehavior
@ -290,6 +309,12 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
'_resetKeyEventListeners(keyEventTarget, _boundKeyHandlers)'
],
/**
* To be used to express what combination of keys will trigger the relative
* callback. e.g. `keyBindings: { 'esc': '_onEscPressed'}`
* @type {Object}
*/
keyBindings: {},
registered: function() {

View File

@ -1,6 +1,6 @@
{
"name": "iron-location",
"version": "0.8.4",
"version": "0.8.5",
"description": "Bidirectional data binding into the page's URL.",
"private": true,
"authors": [
@ -37,11 +37,11 @@
"webcomponentsjs": "webcomponents/webcomponentsjs#^0.7.0",
"iron-demo-helpers": "PolymerElements/iron-demo-helpers#^1.2.3"
},
"_release": "0.8.4",
"_release": "0.8.5",
"_resolution": {
"type": "version",
"tag": "v0.8.4",
"commit": "dcb05b63c0f367fde7ebfd3bebefee606f69dd68"
"tag": "v0.8.5",
"commit": "d7c5a9c991bf5e94094c16e94eb34e2eb30db4b0"
},
"_source": "git://github.com/PolymerElements/iron-location.git",
"_target": "^0.8.0",

View File

@ -1,6 +1,6 @@
{
"name": "iron-location",
"version": "0.8.4",
"version": "0.8.5",
"description": "Bidirectional data binding into the page's URL.",
"private": true,
"authors": [

View File

@ -231,9 +231,14 @@ milliseconds.
if (!href) {
return;
}
event.preventDefault();
// If the navigation is to the current page we shouldn't add a history
// entry or fire a change event.
if (href === window.location.href) {
return;
}
window.history.pushState({}, '', href);
this.fire('location-changed', {}, {node: window});
event.preventDefault();
},
/**
* Returns the absolute URL of the link (if any) that this click event
@ -316,11 +321,6 @@ milliseconds.
// Need to use a full URL in case the containing page has a base URI.
var fullNormalizedHref = new URL(
normalizedHref, window.location.href).href;
// If the navigation is to the current page we shouldn't add a history
// entry.
if (fullNormalizedHref === window.location.href) {
return null;
}
return fullNormalizedHref;
},
_makeRegExp: function(urlSpaceRegex) {

View File

@ -168,6 +168,113 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
expect(replaceStateCalls).to.be.equal(1);
expect(pushStateCalls).to.be.equal(0);
});
suite('when intercepting links', function() {
/**
* Clicks the given link while an iron-location element with the given
* urlSpaceRegex is in the same document. Returns whether the
* iron-location prevented the default behavior of the click.
*
* No matter what, it prevents the default behavior of the click itself
* to ensure that no navigation occurs (as that would interrupt
* running and reporting these tests!)
*/
function isClickCaptured(anchor, urlSpaceRegex) {
var defaultWasPrevented;
function handler(event) {
expect(event.target).to.be.eq(anchor);
defaultWasPrevented = event.defaultPrevented;
event.preventDefault();
expect(event.defaultPrevented).to.be.eq(true);
}
window.addEventListener('click', handler);
var ironLocation = fixture('Solo');
if (urlSpaceRegex != null) {
ironLocation.urlSpaceRegex = urlSpaceRegex;
}
document.body.appendChild(anchor);
anchor.click();
document.body.removeChild(anchor);
window.removeEventListener('click', handler);
return defaultWasPrevented;
}
test('simple link to / is intercepted', function() {
var anchor = document.createElement('a');
if (document.baseURI !== window.location.href) {
anchor.href = makeAbsoluteUrl('/');
} else {
anchor.href = '/';
}
expect(isClickCaptured(anchor)).to.be.eq(true);
expect(pushStateCalls).to.be.equal(1);
});
test('link that matches url space is intercepted', function() {
var anchor = document.createElement('a');
anchor.href = makeAbsoluteUrl('/foo');
expect(isClickCaptured(anchor, '/fo+')).to.be.eq(true);
expect(pushStateCalls).to.be.equal(1);
});
test('link that doesn\'t match url space isn\'t intercepted', function() {
var anchor = document.createElement('a');
anchor.href = makeAbsoluteUrl('/bar');
expect(isClickCaptured(anchor, '/fo+')).to.be.eq(false);
expect(pushStateCalls).to.be.equal(0);
});
test('link to another domain isn\'t intercepted', function() {
var anchor = document.createElement('a');
anchor.href = 'http://example.com/';
expect(isClickCaptured(anchor)).to.be.eq(false);
expect(pushStateCalls).to.be.equal(0);
});
test('a link with target=_blank isn\'t intercepted', function() {
var anchor = document.createElement('a');
anchor.href = makeAbsoluteUrl('/');
anchor.target = '_blank';
expect(isClickCaptured(anchor)).to.be.eq(false);
expect(pushStateCalls).to.be.equal(0);
});
test('a link with an href to the ' +
'current page shouldn\'t add to history.', function() {
var anchor = document.createElement('a');
anchor.href = window.location.href;
// The click is captured, but it doesn't add to history.
expect(isClickCaptured(anchor)).to.be.equal(true);
expect(pushStateCalls).to.be.equal(0);
});
test('a click that has already been defaultPrevented ' +
'shouldn\'t result in a navigation', function() {
fixture('Solo');
var anchor = document.createElement('a');
anchor.href = makeAbsoluteUrl('/');
anchor.addEventListener('click', function(event) {
event.preventDefault();
});
document.body.appendChild(anchor);
var originalPushState = window.history.pushState;
var count = 0;
window.history.pushState = function() {
count++;
}
anchor.click();
window.history.pushState = originalPushState;
expect(count).to.be.equal(0);
})
});
});
suite('when used with other iron-location elements', function() {
var otherUrlElem;
@ -195,106 +302,6 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
assertHaveSameUrls(urlElem, otherUrlElem);
});
});
suite('when intercepting links', function() {
/**
* Clicks the given link while an iron-location element with the given
* urlSpaceRegex is in the same document. Returns whether the
* iron-location prevented the default behavior of the click.
*
* No matter what, it prevents the default behavior of the click itself
* to ensure that no navigation occurs (as that would interrupt
* running and reporting these tests!)
*/
function isClickCaptured(anchor, urlSpaceRegex) {
var defaultWasPrevented;
function handler(event) {
expect(event.target).to.be.eq(anchor);
defaultWasPrevented = event.defaultPrevented;
event.preventDefault();
expect(event.defaultPrevented).to.be.eq(true);
}
window.addEventListener('click', handler);
var ironLocation = fixture('Solo');
if (urlSpaceRegex != null) {
ironLocation.urlSpaceRegex = urlSpaceRegex;
}
document.body.appendChild(anchor);
anchor.click();
document.body.removeChild(anchor);
window.removeEventListener('click', handler);
return defaultWasPrevented;
}
test('simple link to / is intercepted', function() {
var anchor = document.createElement('a');
if (document.baseURI !== window.location.href) {
anchor.href = makeAbsoluteUrl('/');
} else {
anchor.href = '/';
}
expect(isClickCaptured(anchor)).to.be.eq(true);
});
test('link that matches url space is intercepted', function() {
var anchor = document.createElement('a');
anchor.href = makeAbsoluteUrl('/foo');
expect(isClickCaptured(anchor, '/fo+')).to.be.eq(true);
});
test('link that doesn\'t match url space isn\'t intercepted', function() {
var anchor = document.createElement('a');
anchor.href = makeAbsoluteUrl('/bar');
expect(isClickCaptured(anchor, '/fo+')).to.be.eq(false);
});
test('link to another domain isn\'t intercepted', function() {
var anchor = document.createElement('a');
anchor.href = 'http://example.com/';
expect(isClickCaptured(anchor)).to.be.eq(false);
});
test('a link with target=_blank isn\'t intercepted', function() {
var anchor = document.createElement('a');
anchor.href = makeAbsoluteUrl('/');
anchor.target = '_blank';
expect(isClickCaptured(anchor)).to.be.eq(false);
});
test('a link with an href to the ' +
'current page shouldn\'t add to history.', function() {
var anchor = document.createElement('a');
anchor.href = window.location.href;
expect(isClickCaptured(anchor)).to.be.equal(false);
});
test('a click that has already been defaultPrevented ' +
'shouldn\'t result in a navigation', function() {
fixture('Solo');
var anchor = document.createElement('a');
anchor.href = makeAbsoluteUrl('/');
anchor.addEventListener('click', function(event) {
event.preventDefault();
});
document.body.appendChild(anchor);
var originalPushState = window.history.pushState;
var count = 0;
window.history.pushState = function() {
count++;
}
anchor.click();
window.history.pushState = originalPushState;
expect(count).to.be.equal(0);
})
});
suite('supports doing synchronous redirection', function() {
test('of the hash portion of the URL', function() {

View File

@ -1,6 +1,6 @@
{
"name": "polymer",
"version": "1.5.0",
"version": "1.6.0",
"main": [
"polymer.html",
"polymer-mini.html",
@ -32,13 +32,13 @@
},
"private": true,
"homepage": "https://github.com/Polymer/polymer",
"_release": "1.5.0",
"_release": "1.6.0",
"_resolution": {
"type": "version",
"tag": "v1.5.0",
"commit": "ce5b9fb2d8aa03c698410e2e55cffcfa0b788a3a"
"tag": "v1.6.0",
"commit": "8715c83bf04a228de00ec662ed43eb6141e61b91"
},
"_source": "git://github.com/Polymer/polymer.git",
"_target": "^1.0.0",
"_target": "^1.1.0",
"_originalSource": "Polymer/polymer"
}

View File

@ -1,6 +1,6 @@
{
"name": "polymer",
"version": "1.5.0",
"version": "1.6.0",
"main": [
"polymer.html",
"polymer-mini.html",

View File

@ -6,11 +6,7 @@ The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<script>(function () {
--><script>(function () {
function resolve() {
document.body.removeAttribute('unresolved');
}
@ -40,6 +36,8 @@ settings.useNativeImports = settings.hasNativeImports;
settings.useNativeCustomElements = !window.CustomElements || window.CustomElements.useNative;
settings.useNativeShadow = settings.useShadow && settings.nativeShadow;
settings.usePolyfillProto = !settings.useNativeCustomElements && !Object.__proto__;
settings.hasNativeCSSProperties = !navigator.userAgent.match('AppleWebKit/601') && window.CSS && CSS.supports && CSS.supports('box-shadow', '0 0 0 var(--foo)');
settings.useNativeCSSProperties = settings.hasNativeCSSProperties && settings.lazyRegister && settings.useNativeCSSProperties;
return settings;
}()
};(function () {
@ -127,6 +125,9 @@ fn,
args
]);
},
hasRendered: function () {
return this._ready;
},
_watchNextRender: function () {
if (!this._waitingNextRender) {
this._waitingNextRender = true;
@ -678,7 +679,7 @@ default:
return value != null ? value : undefined;
}
}
});Polymer.version = "1.5.0";Polymer.Base._addFeature({
});Polymer.version = '1.6.0';Polymer.Base._addFeature({
_registerFeatures: function () {
this._prepIs();
this._prepBehaviors();
@ -707,3 +708,8 @@ this._marshalBehaviors();

View File

@ -8,8 +8,6 @@ Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
--><link rel="import" href="polymer-micro.html">
<script>Polymer.Base._addFeature({
_prepTemplate: function () {
if (this._template === undefined) {
@ -510,7 +508,7 @@ node.__dom.previousSibling = ref_node ? ref_node.__dom.previousSibling : contain
if (node.__dom.previousSibling) {
node.__dom.previousSibling.__dom.nextSibling = node;
}
node.__dom.nextSibling = ref_node;
node.__dom.nextSibling = ref_node || null;
if (node.__dom.nextSibling) {
node.__dom.nextSibling.__dom.previousSibling = node;
}
@ -2162,3 +2160,5 @@ _marshalBehavior: function (b) {

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
define(['dialogHelper', 'events', 'browser', 'emby-checkbox', 'emby-collapsible', 'css!components/filterdialog/style', 'paper-radio-button', 'paper-radio-group'], function (dialogHelper, events, browser) {
define(['dialogHelper', 'events', 'browser', 'emby-checkbox', 'emby-collapse', 'css!components/filterdialog/style', 'paper-radio-button', 'paper-radio-group'], function (dialogHelper, events, browser) {
function renderOptions(context, selector, cssClass, items, isCheckedFn) {

View File

@ -47,7 +47,7 @@
</h2>
<div class="checkboxList">
<label>
<input type="checkbox" is="emby-checkbox" class="chkStatus" data-filter="Continuing"/>
<input type="checkbox" is="emby-checkbox" class="chkStatus" data-filter="Continuing" />
<span>${OptionContinuing}</span>
</label>
<label>
@ -57,62 +57,68 @@
</div>
</div>
<br />
<emby-collapsible title="${HeaderAirDays}" class="airdays hide">
<div class="checkboxList">
<label>
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkSunday" data-filter="Sunday" />
<span>${OptionSunday}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkMonday" data-filter="Monday" />
<span>${OptionMonday}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkTuesday" data-filter="Tuesday" />
<span>${OptionTuesday}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkWednesday" data-filter="Wednesday" />
<span>${OptionWednesday}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkThursday" data-filter="Thursday" />
<span>${OptionThursday}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkFriday" data-filter="Friday" />
<span>${OptionFriday}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkSaturday" data-filter="Saturday" />
<span>${OptionSaturday}</span>
</label>
<div is="emby-collapse" title="${HeaderAirDays}" class="airdays hide">
<div class="collapseContent">
<div class="checkboxList">
<label>
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkSunday" data-filter="Sunday" />
<span>${OptionSunday}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkMonday" data-filter="Monday" />
<span>${OptionMonday}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkTuesday" data-filter="Tuesday" />
<span>${OptionTuesday}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkWednesday" data-filter="Wednesday" />
<span>${OptionWednesday}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkThursday" data-filter="Thursday" />
<span>${OptionThursday}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkFriday" data-filter="Friday" />
<span>${OptionFriday}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkAirDays" id="chkSaturday" data-filter="Saturday" />
<span>${OptionSaturday}</span>
</label>
</div>
</div>
</emby-collapsible>
<emby-collapsible title="${HeaderFeatures}" class="features hide">
<div class="checkboxList">
<label>
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter" id="chkSubtitle" />
<span>${OptionHasSubtitles}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter" id="chkTrailer" />
<span>${OptionHasTrailer}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter" id="chkSpecialFeature" />
<span>${OptionHasSpecialFeatures}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter" id="chkThemeSong" />
<span>${OptionHasThemeSong}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter" id="chkThemeVideo" />
<span>${OptionHasThemeVideo}</span>
</label>
</div>
<div is="emby-collapse" title="${HeaderFeatures}" class="features hide">
<div class="collapseContent">
<div class="checkboxList">
<label>
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter" id="chkSubtitle" />
<span>${OptionHasSubtitles}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter" id="chkTrailer" />
<span>${OptionHasTrailer}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter" id="chkSpecialFeature" />
<span>${OptionHasSpecialFeatures}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter" id="chkThemeSong" />
<span>${OptionHasThemeSong}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkFeatureFilter" id="chkThemeVideo" />
<span>${OptionHasThemeVideo}</span>
</label>
</div>
</div>
</emby-collapsible>
</div>
<div class="players hide">
<h2 style="margin-bottom: .25em;">
@ -125,55 +131,58 @@
<paper-radio-button class="radioPlayers" name="4">${Option4Player}</paper-radio-button>
</paper-radio-group>
</div>
<emby-collapsible title="${HeaderGenres}" class="genreFilters hide">
<div class="filterOptions">
<div is="emby-collapse" title="${HeaderGenres}" class="genreFilters hide">
<div class="collapseContent filterOptions">
</div>
</emby-collapsible>
</div>
<emby-collapsible title="${HeaderParentalRatings}" class="officialRatingFilters hide">
<div class="filterOptions">
<div is="emby-collapse" title="${HeaderParentalRatings}" class="officialRatingFilters hide">
<div class="collapseContent filterOptions">
</div>
</emby-collapsible>
</div>
<emby-collapsible title="${HeaderTags}" class="tagFilters hide">
<div class="filterOptions">
<div is="emby-collapse" title="${HeaderTags}" class="tagFilters hide">
<div class="collapseContent filterOptions">
</div>
</emby-collapsible>
</div>
<emby-collapsible title="${HeaderVideoTypes}" class="videoTypeFilters hide">
<div class="checkboxList">
<label>
<input type="checkbox" is="emby-checkbox" class="chkVideoTypeFilter chkBluray" data-filter="Bluray" />
<span>${OptionBluray}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkVideoTypeFilter chkDvd" data-filter="Dvd" />
<span>${OptionDvd}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkVideoTypeFilter chkIso" data-filter="Iso" />
<span>${OptionIso}</span>
</label>
<div is="emby-collapse" title="${HeaderVideoTypes}" class="videoTypeFilters hide">
<div class="collapseContent">
<div class="checkboxList">
<label>
<input type="checkbox" is="emby-checkbox" class="chkVideoTypeFilter chkBluray" data-filter="Bluray" />
<span>${OptionBluray}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkVideoTypeFilter chkDvd" data-filter="Dvd" />
<span>${OptionDvd}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkVideoTypeFilter chkIso" data-filter="Iso" />
<span>${OptionIso}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkHDFilter IsHD" />
<span>${OptionIsHD}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkHDFilter IsHD" />
<span>${OptionIsHD}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkSDFilter IsHD" />
<span>${OptionIsSD}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chkSDFilter IsHD" />
<span>${OptionIsSD}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chk3DFilter chk3D" />
<span>${Option3D}</span>
</label>
<label>
<input type="checkbox" is="emby-checkbox" class="chk3DFilter chk3D" />
<span>${Option3D}</span>
</label>
</div>
</div>
</emby-collapsible>
</div>
<emby-collapsible title="${HeaderYears}" class="yearFilters hide">
<div class="filterOptions">
<div is="emby-collapse" title="${HeaderYears}" class="yearFilters hide">
<div class="collapseContent filterOptions">
</div>
</emby-collapsible>
</div>
</div>

View File

@ -420,9 +420,6 @@
var deps = [];
deps.push('paper-icon-item');
deps.push('paper-item-body');
require(deps, function () {
var itemsContainer = context.querySelector('.playlist');

View File

@ -19,21 +19,23 @@
<form class="newImageForm userProfileSettingsForm hide" style="margin: 1em auto 0;">
<emby-collapsible title="${HeaderUploadNewImage}">
<div id="fldNewImage">
<p>${ImageUploadAspectRatioHelp}</p>
<input type="file" accept="image/*" id="uploadUserImage" name="uploadUserImage" />
<div is="emby-collapse" title="${HeaderUploadNewImage}">
<div class="collapseContent">
<div id="fldNewImage">
<p>${ImageUploadAspectRatioHelp}</p>
<input type="file" accept="image/*" id="uploadUserImage" name="uploadUserImage" />
<div id="userImageDropZone" class="imageDropZone">
<h3>${LabelDropImageHere}</h3>
<output id="userImageOutput"></output>
<div id="userImageDropZone" class="imageDropZone">
<h3>${LabelDropImageHere}</h3>
<output id="userImageOutput"></output>
</div>
</div>
<div id="fldNewImagePreview"></div>
<div id="fldUpload" class="hide">
<button type="submit" data-icon="check" data-theme="a">${ButtonUpload}</button>
</div>
</div>
<div id="fldNewImagePreview"></div>
<div id="fldUpload" class="hide">
<button type="submit" data-icon="check" data-theme="a">${ButtonUpload}</button>
</div>
</emby-collapsible>
</div>
</form>

View File

@ -666,19 +666,7 @@ var Dashboard = {
menuHtml += "<div class='sidebarDivider'></div>";
}
if (item.items) {
var style = item.color ? ' iconstyle="color:' + item.color + '"' : '';
var expanded = item.expanded ? (' expanded') : '';
if (item.icon) {
menuHtml += '<emby-collapsible icon="' + item.icon + '" title="' + item.name + '"' + style + expanded + '>';
} else {
menuHtml += '<emby-collapsible title="' + item.name + '"' + style + expanded + '>';
}
menuHtml += item.items.map(Dashboard.getToolsLinkHtml).join('');
menuHtml += '</emby-collapsible>';
}
else if (item.href) {
if (item.href) {
menuHtml += Dashboard.getToolsLinkHtml(item);
} else {
@ -1805,6 +1793,7 @@ var AppInfo = {};
define("libjass", [bowerPath + "/libjass/libjass", "css!" + bowerPath + "/libjass/libjass"], returnFirstDependency);
define("emby-collapse", [embyWebComponentsBowerPath + "/emby-collapse/emby-collapse"], returnFirstDependency);
define("emby-button", [embyWebComponentsBowerPath + "/emby-button/emby-button"], returnFirstDependency);
define("alphaPicker", [embyWebComponentsBowerPath + "/alphapicker/alphapicker"], returnFirstDependency);
define("paper-icon-button-light", [embyWebComponentsBowerPath + "/emby-button/paper-icon-button-light"]);
@ -2779,7 +2768,7 @@ var AppInfo = {};
defineRoute({
path: '/myprofile.html',
dependencies: ['emby-button', 'emby-collapsible', 'emby-checkbox', 'emby-input'],
dependencies: ['emby-button', 'emby-collapse', 'emby-checkbox', 'emby-input'],
autoFocus: false,
transition: 'fade',
controller: 'scripts/myprofile'

View File

@ -81,7 +81,7 @@
return new Promise(function (resolve, reject) {
require(['emby-checkbox', 'emby-input', 'emby-collapsible'], function () {
require(['emby-checkbox', 'emby-input', 'emby-collapse'], function () {
appHost.appInfo().then(function (appInfo) {
renderFormInternal(options, appInfo, resolve);
@ -168,8 +168,8 @@
if (dialogOptions.Options.indexOf('SyncNewContent') != -1 ||
dialogOptions.Options.indexOf('ItemLimit') != -1) {
html += '<emby-collapsible title="' + Globalize.translate('HeaderAdvanced') + '">';
html += '<div style="padding:0 0 1em;">';
html += '<div is="emby-collapse" title="' + Globalize.translate('HeaderAdvanced') + '">';
html += '<div class="collapseContent">';
if (dialogOptions.Options.indexOf('SyncNewContent') != -1) {
html += '<br/>';
html += '<div class="checkboxContainer">';
@ -188,7 +188,7 @@
html += '</div>';
}
html += '</div>';
html += '</emby-collapsible>';
html += '</div>';
html += '<br/>';
}