mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
update notifications
This commit is contained in:
parent
d10ba94be9
commit
e9151896b2
27 changed files with 710 additions and 667 deletions
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "iron-location",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"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.5",
|
||||
"_release": "0.8.6",
|
||||
"_resolution": {
|
||||
"type": "version",
|
||||
"tag": "v0.8.5",
|
||||
"commit": "d7c5a9c991bf5e94094c16e94eb34e2eb30db4b0"
|
||||
"tag": "0.8.6",
|
||||
"commit": "8f628b3477fcba70da47d75e521a8afe8f12e4f7"
|
||||
},
|
||||
"_source": "git://github.com/PolymerElements/iron-location.git",
|
||||
"_target": "^0.8.0",
|
||||
|
|
|
@ -5,19 +5,19 @@ addons:
|
|||
firefox: '46.0'
|
||||
apt:
|
||||
sources:
|
||||
- google-chrome
|
||||
- google-chrome
|
||||
packages:
|
||||
- google-chrome-stable
|
||||
- google-chrome-stable
|
||||
sauce_connect: true
|
||||
before_script:
|
||||
- npm install -g bower polylint web-component-tester
|
||||
- bower install
|
||||
- polylint
|
||||
- npm install -g bower polylint web-component-tester
|
||||
- bower install
|
||||
- polylint
|
||||
script:
|
||||
- xvfb-run wct
|
||||
- "if [ \"${TRAVIS_PULL_REQUEST}\" = \"false\" ]; then wct -s 'default'; fi"
|
||||
- xvfb-run wct
|
||||
- if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then wct -s 'default'; fi
|
||||
env:
|
||||
global:
|
||||
- secure: GQ+cUlta7BWa8Gq4YXrBStPzwRpHT2QG79T4pbDTz2Zs1RvT0GYQaEUksPQnsNCwnTF8ondXhUfMxHcC/r8p7YTCt2hSJSsKkx0lMertsjbKW38ZG28liaAN8msYGb9hnTs4qxhpVEX1pZtOI13RKBU85dw+jKbtxKDX/jVrMn42XCMhEmTeLxM4z8dW5nBu6LW6F3nwABQIUfdc/3OeIYG+2n+84zkXIsX1BFeejq28E6fYJAoMJgqfugLPgPu4IEVCWZJwYl2SgdXwxAcJ2auPph5GJ3DLd0fRRD1TZ94/u0A+eJcQ0OPiPu8hLpQNXOkCgAnO5jkpTCDERNQnB4nY8VfZeZdf1RLAB4VmxzOAbJwJcnqGrh89H6RhKKXyhcuFCgFACYfkzncBCs+co5KwVcwLq88cDOUbnmo3DlwQWpkfusvKy/ZrMrFrX1zycJWN4u8rDsIH4ELasSQh/J3OIa9l2mKfL/OEvqCmpv/ZLGlOVSvNLpr4U7vTVdZBP6C9rtwVXX7VzrClttiidHfoxztAMrNh2GBMjNH9n3FuWMoA/OSoxQGc7RreTsuzdniw3iJYUHIeG08R9bqRtSVA71AlQrbqUaHR+WM7rf7GUx6xG0uDop5vH0RDkE0Nld1W8XuVhHQUg3y3fd4AHJAQVmM7Zsfa3AY1gSr3hV0=
|
||||
- secure: He0JAbtg9jFzuEBRHQdFWHJ33uueY/9Hxq4NB5PCAI1Z9ebIiTs73ofdNy6e+ftBqlQnBuhoKLfIpuD8Qj2kSdLHQvg1s6ojvNDmAvau1ZINCJRgOSKbGC0TvCVx9rT9Kqc83eqKvKDzr/rcpaIArgMYJzBrSG0D2Kn4luUQnWkKfo1knn17ytJFCvzqQvPWZTIZ6beJ7MRKXRU093a4wYMsKIxQHH65T4Ypj+RBsgv6Xnidjb8qZbNsEwaeOwExfsh30WUo/hSygRi2CP3KSRSc/vsMgSrGpFghZpnhjeDJAGTiDzCTxpJkAkHXereJT4agsWErcgSrRTaxi5G6f18o56pRS+I61BC5DuGGwSL7hOHWSC8pGzkwVFyz31MB2ll0HO3iQHMmcSjY37+G7toEP/vJ/UHm6SZoQq36HGJea7Ycv/2mk4HAHcVEDxhYG42bXXflxevFeqAkVUI7SxSaQpQuZF76/4th4uKFmAHPvRVj5yx8OWil9Lt6cG8DIEZaxXdJVueGgiODmmul7lAd5osO/1UCg4CTy1OnmuSJj7ax9LBa6YY2+3uvnBfE7fNUVKmVmVhlLsF0QAdj0LaFoSU0eQFWdReYqBxEvc4gOT3AtEpaAvfZnL11Q6wVyI7kCHhTHrltA4WENPOSA2u7W//WsQfHX3gRdpIVIVI=
|
||||
- secure: LSHof0ip5tJswHk4wAIXXsf8Z0WL44Y1pB1XmqFtIsjQLl+2K5bdhRq6Hd6RUZYcpTL29cMStCLQJEG1hxnMks37omHuVFWRtk0NaYlGnJY4i/m/ukUBmM7Yr7LROWRnBgU75PVUvc4YMbCjvlQ9KKjdgBbQBFH2KjhKr9GUN+ihB1yaKRMADvFtC6JWisG4RPv1pRxF6UgvGkOsaCdVb9H9AbKA0psAI1cpg8IMGgUBslbwhYw+viMEQdZ5lW++T3Tb9cwzCVaTqBgvkQCXW4I6T4En9yvUWbV0Ry8uChchvs6BJfwpgN/nksYnLCbCLmN+Rw5eU0+XyWZDLiki/HpsNQblqVEW7vHo1hmtpK24GUv2g8kuE1OjjW7K/l+Q20D8FJFIcPdFjmZ/XgLWxdtXbgNC8n4Z1XBEqKuV1BJ29ojvZk5a5/gFBf9PWdMNTVwSepk7+LoEVQW17vzSHT/HDF7WYEs14uZ0x9I0AV2+339RG7iCcVFMZKYgIif0DmZVysnm0Z9j+wRRk227JpiF9MHeVkFUaXl3+3XIQMz04t+H1wOXiDRlJLJG9IUrzFs+KYuCA+OxNc0XrxjA6Zkl75AQAjAY5vMEWYD+N2OGjkfwD7dB4JtltmZ4llMhEZUt/RPLeZJvL4Sus8hfA6SNG9/tdVgef0mQ91acxZg=
|
||||
- secure: h0j4eT4lJqbDkEVz4OjkPuAHxxT0Cy7bWbT71oDllhDAvl9zXxIn5BHSnCaUfO0xUhXVpwNuVmDzOE6yQETJ3MamoqL2Hrbg0VCr4hux9/2ipa4tlCTjQ2Y/YaIoID7CpCchdoOQAjrxeg0ZhpMnwxV18NHTqlxNrsdoSDDSmCdffWQwgjVI07O4h1ISIWx6oY0i1Z+9nJI001iZIw5vGopK8zACa5hfSu9my8Nog5gYOBOEoYmT8lNBaKkNuZacGAkHmXPmeeWiflM49sRO5MfyCgVvNtn6yohXs5D1QD2KUZs4mTzVBebT3vPuV7I9NFd+ZY9GT9YeH9BLuxKpYhGlnjL2HhGAvGjIcKX8315EOCpVvgwX3VE1f9PJe3K9q6LP/sFUqf8cUrbj9FhiAVp79IPJkvLUc+1hUG3iQ8X83mjQxcrCt6I50BINOLgjsfzCmnvv12g3FuRry2EBRxEultvPLRCNc2bJCPqol6kiWzs8ySOPXCYOcXqW5/6Z4DYLMTYevapkJZhErcTBuIH7rXEX4tGsdBjtGjTepcW7k2dsCElrul79czZA3AwdLEK/fuygxXR8eMCeS8Tdh4+Ba4M9TfXtAjjnR30NayTOCwyWW9IvTJeWeXKd6SilJcGVO7r546ayGWJ2G+Fltyi7RGixMplRMm8y6YfeM9k=
|
||||
dist: trusty
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "iron-location",
|
||||
"version": "0.8.5",
|
||||
"version": "0.8.6",
|
||||
"description": "Bidirectional data binding into the page's URL.",
|
||||
"private": true,
|
||||
"authors": [
|
||||
|
|
|
@ -50,281 +50,287 @@ milliseconds.
|
|||
|
||||
-->
|
||||
<script>
|
||||
'use strict';
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
Polymer({
|
||||
is: 'iron-location',
|
||||
properties: {
|
||||
/**
|
||||
* The pathname component of the URL.
|
||||
*/
|
||||
path: {
|
||||
type: String,
|
||||
notify: true,
|
||||
value: function() {
|
||||
return window.location.pathname;
|
||||
Polymer({
|
||||
is: 'iron-location',
|
||||
properties: {
|
||||
/**
|
||||
* The pathname component of the URL.
|
||||
*/
|
||||
path: {
|
||||
type: String,
|
||||
notify: true,
|
||||
value: function() {
|
||||
return window.decodeURIComponent(window.location.pathname);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The query string portion of the URL.
|
||||
*/
|
||||
query: {
|
||||
type: String,
|
||||
notify: true,
|
||||
value: function() {
|
||||
return window.decodeURIComponent(window.location.search.slice(1));
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The hash component of the URL.
|
||||
*/
|
||||
hash: {
|
||||
type: String,
|
||||
notify: true,
|
||||
value: function() {
|
||||
return window.decodeURIComponent(window.location.hash.slice(1));
|
||||
}
|
||||
},
|
||||
/**
|
||||
* If the user was on a URL for less than `dwellTime` milliseconds, it
|
||||
* won't be added to the browser's history, but instead will be replaced
|
||||
* by the next entry.
|
||||
*
|
||||
* This is to prevent large numbers of entries from clogging up the user's
|
||||
* browser history. Disable by setting to a negative number.
|
||||
*/
|
||||
dwellTime: {
|
||||
type: Number,
|
||||
value: 2000
|
||||
},
|
||||
|
||||
/**
|
||||
* A regexp that defines the set of URLs that should be considered part
|
||||
* of this web app.
|
||||
*
|
||||
* Clicking on a link that matches this regex won't result in a full page
|
||||
* navigation, but will instead just update the URL state in place.
|
||||
*
|
||||
* This regexp is given everything after the origin in an absolute
|
||||
* URL. So to match just URLs that start with /search/ do:
|
||||
* url-space-regex="^/search/"
|
||||
*
|
||||
* @type {string|RegExp}
|
||||
*/
|
||||
urlSpaceRegex: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
|
||||
/**
|
||||
* urlSpaceRegex, but coerced into a regexp.
|
||||
*
|
||||
* @type {RegExp}
|
||||
*/
|
||||
_urlSpaceRegExp: {
|
||||
computed: '_makeRegExp(urlSpaceRegex)'
|
||||
},
|
||||
|
||||
_lastChangedAt: {
|
||||
type: Number
|
||||
},
|
||||
|
||||
_initialized: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The query string portion of the URL.
|
||||
*/
|
||||
query: {
|
||||
type: String,
|
||||
notify: true,
|
||||
value: function() {
|
||||
return window.location.search.slice(1);
|
||||
hostAttributes: {
|
||||
hidden: true
|
||||
},
|
||||
observers: [
|
||||
'_updateUrl(path, query, hash)'
|
||||
],
|
||||
attached: function() {
|
||||
this.listen(window, 'hashchange', '_hashChanged');
|
||||
this.listen(window, 'location-changed', '_urlChanged');
|
||||
this.listen(window, 'popstate', '_urlChanged');
|
||||
this.listen(/** @type {!HTMLBodyElement} */(document.body), 'click', '_globalOnClick');
|
||||
// Give a 200ms grace period to make initial redirects without any
|
||||
// additions to the user's history.
|
||||
this._lastChangedAt = window.performance.now() - (this.dwellTime - 200);
|
||||
|
||||
this._initialized = true;
|
||||
this._urlChanged();
|
||||
},
|
||||
detached: function() {
|
||||
this.unlisten(window, 'hashchange', '_hashChanged');
|
||||
this.unlisten(window, 'location-changed', '_urlChanged');
|
||||
this.unlisten(window, 'popstate', '_urlChanged');
|
||||
this.unlisten(/** @type {!HTMLBodyElement} */(document.body), 'click', '_globalOnClick');
|
||||
this._initialized = false;
|
||||
},
|
||||
_hashChanged: function() {
|
||||
this.hash = window.decodeURIComponent(window.location.hash.substring(1));
|
||||
},
|
||||
_urlChanged: function() {
|
||||
// We want to extract all info out of the updated URL before we
|
||||
// try to write anything back into it.
|
||||
//
|
||||
// i.e. without _dontUpdateUrl we'd overwrite the new path with the old
|
||||
// one when we set this.hash. Likewise for query.
|
||||
this._dontUpdateUrl = true;
|
||||
this._hashChanged();
|
||||
this.path = window.decodeURIComponent(window.location.pathname);
|
||||
this.query = window.decodeURIComponent(
|
||||
window.location.search.substring(1));
|
||||
this._dontUpdateUrl = false;
|
||||
this._updateUrl();
|
||||
},
|
||||
_getUrl: function() {
|
||||
var partiallyEncodedPath = window.encodeURI(
|
||||
this.path).replace(/\#/g, '%23').replace(/\?/g, '%3F');
|
||||
var partiallyEncodedQuery = '';
|
||||
if (this.query) {
|
||||
partiallyEncodedQuery = '?' + window.encodeURI(
|
||||
this.query).replace(/\#/g, '%23');
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The hash component of the URL.
|
||||
*/
|
||||
hash: {
|
||||
type: String,
|
||||
notify: true,
|
||||
value: function() {
|
||||
return window.location.hash.slice(1);
|
||||
var partiallyEncodedHash = '';
|
||||
if (this.hash) {
|
||||
partiallyEncodedHash = '#' + window.encodeURI(this.hash);
|
||||
}
|
||||
return (
|
||||
partiallyEncodedPath + partiallyEncodedQuery + partiallyEncodedHash);
|
||||
},
|
||||
/**
|
||||
* If the user was on a URL for less than `dwellTime` milliseconds, it
|
||||
* won't be added to the browser's history, but instead will be replaced
|
||||
* by the next entry.
|
||||
*
|
||||
* This is to prevent large numbers of entries from clogging up the user's
|
||||
* browser history. Disable by setting to a negative number.
|
||||
*/
|
||||
dwellTime: {
|
||||
type: Number,
|
||||
value: 2000
|
||||
},
|
||||
|
||||
/**
|
||||
* A regexp that defines the set of URLs that should be considered part
|
||||
* of this web app.
|
||||
*
|
||||
* Clicking on a link that matches this regex won't result in a full page
|
||||
* navigation, but will instead just update the URL state in place.
|
||||
*
|
||||
* This regexp is given everything after the origin in an absolute
|
||||
* URL. So to match just URLs that start with /search/ do:
|
||||
* url-space-regex="^/search/"
|
||||
*
|
||||
* @type {string|RegExp}
|
||||
*/
|
||||
urlSpaceRegex: {
|
||||
type: String,
|
||||
value: ''
|
||||
},
|
||||
|
||||
/**
|
||||
* urlSpaceRegex, but coerced into a regexp.
|
||||
*
|
||||
* @type {RegExp}
|
||||
*/
|
||||
_urlSpaceRegExp: {
|
||||
computed: '_makeRegExp(urlSpaceRegex)'
|
||||
},
|
||||
|
||||
_lastChangedAt: {
|
||||
type: Number
|
||||
},
|
||||
|
||||
_initialized: {
|
||||
type: Boolean,
|
||||
value: false
|
||||
}
|
||||
},
|
||||
hostAttributes: {
|
||||
hidden: true
|
||||
},
|
||||
observers: [
|
||||
'_updateUrl(path, query, hash)'
|
||||
],
|
||||
attached: function() {
|
||||
this.listen(window, 'hashchange', '_hashChanged');
|
||||
this.listen(window, 'location-changed', '_urlChanged');
|
||||
this.listen(window, 'popstate', '_urlChanged');
|
||||
this.listen(/** @type {!HTMLBodyElement} */(document.body), 'click', '_globalOnClick');
|
||||
// Give a 200ms grace period to make initial redirects without any
|
||||
// additions to the user's history.
|
||||
this._lastChangedAt = window.performance.now() - (this.dwellTime - 200);
|
||||
|
||||
this._initialized = true;
|
||||
this._urlChanged();
|
||||
},
|
||||
detached: function() {
|
||||
this.unlisten(window, 'hashchange', '_hashChanged');
|
||||
this.unlisten(window, 'location-changed', '_urlChanged');
|
||||
this.unlisten(window, 'popstate', '_urlChanged');
|
||||
this.unlisten(/** @type {!HTMLBodyElement} */(document.body), 'click', '_globalOnClick');
|
||||
this._initialized = false;
|
||||
},
|
||||
_hashChanged: function() {
|
||||
this.hash = window.location.hash.substring(1);
|
||||
},
|
||||
_urlChanged: function() {
|
||||
// We want to extract all info out of the updated URL before we
|
||||
// try to write anything back into it.
|
||||
//
|
||||
// i.e. without _dontUpdateUrl we'd overwrite the new path with the old
|
||||
// one when we set this.hash. Likewise for query.
|
||||
this._dontUpdateUrl = true;
|
||||
this._hashChanged();
|
||||
this.path = window.location.pathname;
|
||||
this.query = window.location.search.substring(1);
|
||||
this._dontUpdateUrl = false;
|
||||
this._updateUrl();
|
||||
},
|
||||
_getUrl: function() {
|
||||
var url = this.path;
|
||||
var query = this.query;
|
||||
if (query) {
|
||||
url += '?' + query;
|
||||
}
|
||||
if (this.hash) {
|
||||
url += '#' + this.hash;
|
||||
}
|
||||
return url;
|
||||
},
|
||||
_updateUrl: function() {
|
||||
if (this._dontUpdateUrl || !this._initialized) {
|
||||
return;
|
||||
}
|
||||
var newUrl = this._getUrl();
|
||||
var currentUrl = (
|
||||
window.location.pathname +
|
||||
window.location.search +
|
||||
window.location.hash
|
||||
);
|
||||
if (newUrl == currentUrl) {
|
||||
// nothing to do, the URL didn't change
|
||||
return;
|
||||
}
|
||||
// Need to use a full URL in case the containing page has a base URI.
|
||||
var fullNewUrl = new URL(
|
||||
newUrl, window.location.protocol + '//' + window.location.host).href;
|
||||
var now = window.performance.now();
|
||||
var shouldReplace =
|
||||
this._lastChangedAt + this.dwellTime > now;
|
||||
this._lastChangedAt = now;
|
||||
if (shouldReplace) {
|
||||
window.history.replaceState({}, '', fullNewUrl);
|
||||
} else {
|
||||
window.history.pushState({}, '', fullNewUrl);
|
||||
}
|
||||
this.fire('location-changed', {}, {node: window});
|
||||
},
|
||||
/**
|
||||
* A necessary evil so that links work as expected. Does its best to
|
||||
* bail out early if possible.
|
||||
*
|
||||
* @param {MouseEvent} event .
|
||||
*/
|
||||
_globalOnClick: function(event) {
|
||||
// If another event handler has stopped this event then there's nothing
|
||||
// for us to do. This can happen e.g. when there are multiple
|
||||
// iron-location elements in a page.
|
||||
if (event.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
var href = this._getSameOriginLinkHref(event);
|
||||
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});
|
||||
},
|
||||
/**
|
||||
* Returns the absolute URL of the link (if any) that this click event
|
||||
* is clicking on, if we can and should override the resulting full
|
||||
* page navigation. Returns null otherwise.
|
||||
*
|
||||
* @param {MouseEvent} event .
|
||||
* @return {string?} .
|
||||
*/
|
||||
_getSameOriginLinkHref: function(event) {
|
||||
// We only care about left-clicks.
|
||||
if (event.button !== 0) {
|
||||
return null;
|
||||
}
|
||||
// We don't want modified clicks, where the intent is to open the page
|
||||
// in a new tab.
|
||||
if (event.metaKey || event.ctrlKey) {
|
||||
return null;
|
||||
}
|
||||
var eventPath = Polymer.dom(event).path;
|
||||
var anchor = null;
|
||||
for (var i = 0; i < eventPath.length; i++) {
|
||||
var element = eventPath[i];
|
||||
if (element.tagName === 'A' && element.href) {
|
||||
anchor = element;
|
||||
break;
|
||||
_updateUrl: function() {
|
||||
if (this._dontUpdateUrl || !this._initialized) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If there's no link there's nothing to do.
|
||||
if (!anchor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Target blank is a new tab, don't intercept.
|
||||
if (anchor.target === '_blank') {
|
||||
return null;
|
||||
}
|
||||
// If the link is for an existing parent frame, don't intercept.
|
||||
if ((anchor.target === '_top' ||
|
||||
anchor.target === '_parent') &&
|
||||
window.top !== window) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var href = anchor.href;
|
||||
|
||||
// It only makes sense for us to intercept same-origin navigations.
|
||||
// pushState/replaceState don't work with cross-origin links.
|
||||
var url;
|
||||
if (document.baseURI != null) {
|
||||
url = new URL(href, /** @type {string} */(document.baseURI));
|
||||
} else {
|
||||
url = new URL(href);
|
||||
}
|
||||
|
||||
var origin;
|
||||
|
||||
// IE Polyfill
|
||||
if (window.location.origin) {
|
||||
origin = window.location.origin;
|
||||
} else {
|
||||
origin = window.location.protocol + '//' + window.location.hostname;
|
||||
|
||||
if (window.location.port) {
|
||||
origin += ':' + window.location.port;
|
||||
if (this.path === window.decodeURIComponent(window.location.pathname) &&
|
||||
this.query === window.decodeURIComponent(
|
||||
window.location.search.substring(1)) &&
|
||||
this.hash === window.decodeURIComponent(
|
||||
window.location.hash.substring(1))) {
|
||||
// Nothing to do, the current URL is a representation of our properties.
|
||||
return;
|
||||
}
|
||||
var newUrl = this._getUrl();
|
||||
// Need to use a full URL in case the containing page has a base URI.
|
||||
var fullNewUrl = new URL(
|
||||
newUrl, window.location.protocol + '//' + window.location.host).href;
|
||||
var now = window.performance.now();
|
||||
var shouldReplace =
|
||||
this._lastChangedAt + this.dwellTime > now;
|
||||
this._lastChangedAt = now;
|
||||
if (shouldReplace) {
|
||||
window.history.replaceState({}, '', fullNewUrl);
|
||||
} else {
|
||||
window.history.pushState({}, '', fullNewUrl);
|
||||
}
|
||||
this.fire('location-changed', {}, {node: window});
|
||||
},
|
||||
/**
|
||||
* A necessary evil so that links work as expected. Does its best to
|
||||
* bail out early if possible.
|
||||
*
|
||||
* @param {MouseEvent} event .
|
||||
*/
|
||||
_globalOnClick: function(event) {
|
||||
// If another event handler has stopped this event then there's nothing
|
||||
// for us to do. This can happen e.g. when there are multiple
|
||||
// iron-location elements in a page.
|
||||
if (event.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
var href = this._getSameOriginLinkHref(event);
|
||||
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});
|
||||
},
|
||||
/**
|
||||
* Returns the absolute URL of the link (if any) that this click event
|
||||
* is clicking on, if we can and should override the resulting full
|
||||
* page navigation. Returns null otherwise.
|
||||
*
|
||||
* @param {MouseEvent} event .
|
||||
* @return {string?} .
|
||||
*/
|
||||
_getSameOriginLinkHref: function(event) {
|
||||
// We only care about left-clicks.
|
||||
if (event.button !== 0) {
|
||||
return null;
|
||||
}
|
||||
// We don't want modified clicks, where the intent is to open the page
|
||||
// in a new tab.
|
||||
if (event.metaKey || event.ctrlKey) {
|
||||
return null;
|
||||
}
|
||||
var eventPath = Polymer.dom(event).path;
|
||||
var anchor = null;
|
||||
for (var i = 0; i < eventPath.length; i++) {
|
||||
var element = eventPath[i];
|
||||
if (element.tagName === 'A' && element.href) {
|
||||
anchor = element;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (url.origin !== origin) {
|
||||
return null;
|
||||
}
|
||||
var normalizedHref = url.pathname + url.search + url.hash;
|
||||
// If there's no link there's nothing to do.
|
||||
if (!anchor) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If we've been configured not to handle this url... don't handle it!
|
||||
if (this._urlSpaceRegExp &&
|
||||
!this._urlSpaceRegExp.test(normalizedHref)) {
|
||||
return null;
|
||||
// Target blank is a new tab, don't intercept.
|
||||
if (anchor.target === '_blank') {
|
||||
return null;
|
||||
}
|
||||
// If the link is for an existing parent frame, don't intercept.
|
||||
if ((anchor.target === '_top' ||
|
||||
anchor.target === '_parent') &&
|
||||
window.top !== window) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var href = anchor.href;
|
||||
|
||||
// It only makes sense for us to intercept same-origin navigations.
|
||||
// pushState/replaceState don't work with cross-origin links.
|
||||
var url;
|
||||
if (document.baseURI != null) {
|
||||
url = new URL(href, /** @type {string} */(document.baseURI));
|
||||
} else {
|
||||
url = new URL(href);
|
||||
}
|
||||
|
||||
var origin;
|
||||
|
||||
// IE Polyfill
|
||||
if (window.location.origin) {
|
||||
origin = window.location.origin;
|
||||
} else {
|
||||
origin = window.location.protocol + '//' + window.location.hostname;
|
||||
|
||||
if (window.location.port) {
|
||||
origin += ':' + window.location.port;
|
||||
}
|
||||
}
|
||||
|
||||
if (url.origin !== origin) {
|
||||
return null;
|
||||
}
|
||||
var normalizedHref = url.pathname + url.search + url.hash;
|
||||
|
||||
// If we've been configured not to handle this url... don't handle it!
|
||||
if (this._urlSpaceRegExp &&
|
||||
!this._urlSpaceRegExp.test(normalizedHref)) {
|
||||
return null;
|
||||
}
|
||||
// Need to use a full URL in case the containing page has a base URI.
|
||||
var fullNormalizedHref = new URL(
|
||||
normalizedHref, window.location.href).href;
|
||||
return fullNormalizedHref;
|
||||
},
|
||||
_makeRegExp: function(urlSpaceRegex) {
|
||||
return RegExp(urlSpaceRegex);
|
||||
}
|
||||
// Need to use a full URL in case the containing page has a base URI.
|
||||
var fullNormalizedHref = new URL(
|
||||
normalizedHref, window.location.href).href;
|
||||
return fullNormalizedHref;
|
||||
},
|
||||
_makeRegExp: function(urlSpaceRegex) {
|
||||
return RegExp(urlSpaceRegex);
|
||||
}
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
|
|
@ -19,8 +19,30 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
|||
<link rel="import" href="../../promise-polyfill/promise-polyfill.html">
|
||||
<link rel="import" href="../iron-location.html">
|
||||
<link rel="import" href="./redirection.html">
|
||||
<style>
|
||||
#safari-cooldown {
|
||||
font-size: 18px;
|
||||
max-width: 300px;
|
||||
}
|
||||
#safari-cooldown div {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div id='safari-cooldown' hidden>
|
||||
<div>Danger: URL overheating.</div>
|
||||
<div>
|
||||
Safari requires a cooldown period before we call
|
||||
pushState/replaceState any more.
|
||||
</div>
|
||||
<div>Testing will resume after 30 seconds.</div>
|
||||
<div>
|
||||
<a href="https://forums.developer.apple.com/thread/36650">More info</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<test-fixture id='Solo'>
|
||||
<template>
|
||||
<iron-location></iron-location>
|
||||
|
@ -78,8 +100,16 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
|||
function ironLocationTests() {
|
||||
suite('when used solo', function() {
|
||||
var urlElem;
|
||||
var toRemove;
|
||||
setup(function() {
|
||||
replaceState('/');
|
||||
urlElem = fixture('Solo');
|
||||
toRemove = [];
|
||||
});
|
||||
teardown(function() {
|
||||
for (var i = 0; i < toRemove.length; i++) {
|
||||
document.body.removeChild(toRemove[i]);
|
||||
}
|
||||
});
|
||||
test('basic functionality with #hash urls', function() {
|
||||
// Initialized to the window location's hash.
|
||||
|
@ -116,6 +146,75 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
|||
window.dispatchEvent(new CustomEvent('location-changed'));
|
||||
expect(urlElem.path).to.be.equal('/baz');
|
||||
});
|
||||
function makeTemporaryIronLocation() {
|
||||
var ironLocation = document.createElement('iron-location');
|
||||
document.body.appendChild(ironLocation);
|
||||
toRemove.push(ironLocation);
|
||||
return ironLocation
|
||||
}
|
||||
test('dealing with paths with unusual characters', function() {
|
||||
var pathEncodingExamples = {
|
||||
'/foo': '/foo',
|
||||
'/': '/',
|
||||
'/foo bar': '/foo%20bar',
|
||||
'/foo#bar': '/foo%23bar',
|
||||
'/foo?xyz': '/foo%3Fxyz',
|
||||
'/foo\'bar\'baz': '/foo\'bar\'baz',
|
||||
};
|
||||
|
||||
for (var plainTextPath in pathEncodingExamples) {
|
||||
var encodedPath = pathEncodingExamples[plainTextPath];
|
||||
|
||||
urlElem.path = plainTextPath;
|
||||
expect(window.location.pathname).to.be.equal(encodedPath);
|
||||
expect(urlElem.path).to.be.equal(plainTextPath);
|
||||
var temporaryIronLocation = makeTemporaryIronLocation();
|
||||
expect(temporaryIronLocation.path).to.be.equal(plainTextPath);
|
||||
}
|
||||
});
|
||||
test('dealing with hashes with unusual characters', function() {
|
||||
var hashEncodingExamples = {
|
||||
'foo': '#foo',
|
||||
'': '',
|
||||
'foo bar': ['#foo%20bar', '#foo bar'],
|
||||
'foo#bar': '#foo#bar',
|
||||
'foo?bar': '#foo?bar',
|
||||
'/foo\'bar\'baz': ['#/foo%27bar%27baz', '#/foo\'bar\'baz'],
|
||||
};
|
||||
for (var plainTextHash in hashEncodingExamples) {
|
||||
var encodedHashes = hashEncodingExamples[plainTextHash];
|
||||
if (typeof encodedHashes === 'string') {
|
||||
encodedHashes = [encodedHashes];
|
||||
}
|
||||
|
||||
urlElem.hash = plainTextHash;
|
||||
expect(encodedHashes).to.contain(window.location.hash);
|
||||
expect(urlElem.hash).to.be.equal(plainTextHash);
|
||||
expect(makeTemporaryIronLocation().hash).to.be.equal(plainTextHash);
|
||||
}
|
||||
});
|
||||
test('dealing with queries with unusual characters', function() {
|
||||
var queryEncodingExamples = {
|
||||
'foo': '?foo',
|
||||
'': '',
|
||||
'foo bar': '?foo%20bar',
|
||||
'foo#bar': '?foo%23bar',
|
||||
'foo?bar': '?foo?bar',
|
||||
'/foo\'bar\'baz': ['?/foo%27bar%27baz', '?/foo\'bar\'baz'],
|
||||
};
|
||||
for (var plainTextQuery in queryEncodingExamples) {
|
||||
var encodedQueries = queryEncodingExamples[plainTextQuery];
|
||||
if (typeof encodedQueries === 'string') {
|
||||
encodedQueries = [encodedQueries];
|
||||
}
|
||||
|
||||
expect(urlElem._initialized).to.be.eq(true);
|
||||
urlElem.query = plainTextQuery;
|
||||
expect(encodedQueries).to.contain(window.location.search);
|
||||
expect(urlElem.query).to.be.equal(plainTextQuery);
|
||||
expect(makeTemporaryIronLocation().query).to.be.equal(plainTextQuery);
|
||||
}
|
||||
});
|
||||
test('assigning to a relative path URL', function() {
|
||||
urlElem.path = '/foo/bar';
|
||||
expect(window.location.pathname).to.be.equal('/foo/bar');
|
||||
|
@ -167,80 +266,80 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
|||
ironLocation.path = '/foo';
|
||||
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;
|
||||
/**
|
||||
* 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 = '/';
|
||||
}
|
||||
|
||||
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(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');
|
||||
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(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');
|
||||
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(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/';
|
||||
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(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';
|
||||
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(isClickCaptured(anchor)).to.be.eq(false);
|
||||
expect(pushStateCalls).to.be.equal(0);
|
||||
});
|
||||
|
||||
|
@ -273,8 +372,8 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
|||
window.history.pushState = originalPushState;
|
||||
|
||||
expect(count).to.be.equal(0);
|
||||
})
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
||||
suite('when used with other iron-location elements', function() {
|
||||
var otherUrlElem;
|
||||
|
@ -345,7 +444,26 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
|||
window.history.replaceState({}, '', initialUrl);
|
||||
});
|
||||
|
||||
ironLocationTests();
|
||||
// This is as dumb as it looks. See #safari-cooldown in the dom above.
|
||||
var cooldownFunction = function() {};
|
||||
if (/^Apple/.test(navigator.vendor)) {
|
||||
cooldownFunction = function(done) {
|
||||
var cooldownPeriod = 30 * 1000;
|
||||
this.timeout(cooldownPeriod + 5000);
|
||||
var cooldownMessage = document.querySelector('#safari-cooldown');
|
||||
cooldownMessage.removeAttribute('hidden');
|
||||
setTimeout(function() {
|
||||
done();
|
||||
cooldownMessage.setAttribute('hidden', 'hidden');
|
||||
}, cooldownPeriod);
|
||||
};
|
||||
}
|
||||
|
||||
suite('without a base URI', function() {
|
||||
ironLocationTests();
|
||||
|
||||
suiteTeardown(cooldownFunction);
|
||||
});
|
||||
|
||||
suite('with a base URI', function() {
|
||||
var baseElem;
|
||||
|
@ -360,7 +478,7 @@ subject to an additional IP rights grant found at http://polymer.github.io/PATEN
|
|||
teardown(function() {
|
||||
document.head.removeChild(baseElem);
|
||||
});
|
||||
|
||||
suiteTeardown(cooldownFunction);
|
||||
ironLocationTests();
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue