This commit is contained in:
Yangguang Li 2019-06-21 14:41:43 -07:00
Родитель 3fd617fe66
Коммит f1c50d6506
8 изменённых файлов: 0 добавлений и 1162 удалений

Просмотреть файл

@ -1,132 +0,0 @@
import {LitElement, html} from 'https://unpkg.com/@polymer/lit-element@latest/lit-element.js?module';
import '/static/elements/chromedash-color-status.js';
class ChromedashLegend extends LitElement {
static get properties() {
return {
opened: {type: Boolean, reflect: true},
views: {type: Array},
};
}
constructor() {
super();
this.opened = false;
this.views = [];
}
firstUpdated() {
this._dissmissEl = this.$.querySelector('[dialog-dismiss]');
if (this._dissmissEl) {
this.listen(this._dissmissEl, 'click', 'toggle');
}
}
detached() {
if (this.this._dissmissEl) {
this.unlisten(this._dissmissEl, 'click', 'toggle');
}
}
toggle() {
this.opened = !this.opened;
document.body.style.overflow = this.opened ? 'hidden' : '';
}
render() {
return html`
<link rel="stylesheet" href="/static/css/elements/chromedash-legend.css">
<div id="overlay">
<h3>About the data</h3>
<section class="content-wrapper">
<p class="description">What you're looking at is a mostly
comprehensive list of web platform features that have landed in
Chromium, ordered chronologically by the milestone in which they were
added. Features marked "No active development" are being considered or
have yet to be started. Features marked "In development" are currently
being worked on.</p>
<div class="close buttons">
<iron-icon icon="chromestatus:close" dialog-dismiss></iron-icon>
</div>
</section>
<h3>Color legend</h3>
<p>Colors indicate the "interoperability risk" for a given feature. The
risk increases as
<chromedash-color-status .value="1"
.max="${views.vendors.length}"></chromedash-color-status>
<chromedash-color-status .value="${views.vendors.length}"
.max="${views.vendors.length}"></chromedash-color-status>, and the
color meaning differs for browser vendors, web developers, and the
standards process.</p>
<section class="views">
<div>
<label>Browser vendors</label>
<ul>
${this.views.vendors.map((vendor) => html`
<li>
<chromedash-color-status .value="${vendor.key}"
.max="${views.vendors.length}">
</chromedash-color-status>
<span>${vendor.val}</span></li>
`)}
</ul>
</div>
<div>
<label>Web developer</label>
<ul>
${this.views.webdevs.map((webdev) => html`
<li>
<chromedash-color-status .value="${webdev.key}"
.max="${views.webdevs.length}">
</chromedash-color-status>
<span>${webdev.val}</span></li>
`)}
</ul>
</div>
<div>
<label>Standards values</label>
<ul>
${this.views.standards.map((standard) => html`
<li>
<chromedash-color-status .value="${standard.key}"
.max="${views.standards.length}">
</chromedash-color-status>
<span>${standard.val}</span></li>
`)}
</ul>
</div>
</section>
<h3>Search</h3>
<section>
<label>Example search queries</label>
<ul class="queries">
<li>
<span>"browsers.chrome.desktop&lt;30"</span>features that landed before 30
</li>
<li>
<span>"browsers.chrome.desktop&lt;=50"</span>features in desktop chrome 50
</li>
<li>
<span>"browsers.chrome.android&gt;40"</span>features that landed on Chrome Android after 40 (milestone types: browsers.chrome.android, browsers.chrome.ios, browsers.chrome.webview)
</li>
<li>
<span>"browsers.chrome.status.val=4"</span>features behind a flag
</li>
<li>
<span>"category:CSS"</span>features in the CSS category
</li>
<li>
<span>"component:Blink>CSS"</span>features under the Blink component "Blink>CSS"
</li>
<li>
<span>"browsers.chrome.owners:user@example.org"</span>features owned by user@example.org
</li>
</ul>
</section>
</div>
`;
}
}
customElements.define('chromedash-color-status', ChromedashLegend);

Просмотреть файл

@ -1,153 +0,0 @@
import {LitElement, html} from 'https://unpkg.com/@polymer/lit-element@latest/lit-element.js?module';
import 'https://unpkg.com/@polymer/iron-icon/iron-icon.js?module';
import '/static/elements/chromedash-x-meter.js';
class ChromedashMetrics extends LitElement {
static get properties() {
return {
type: {type: String}, // From attribute
view: {type: String}, // From attribute
useRemoteData: {type: Boolean}, // From attibute. If true, fetches live data from chromestatus.com instead of localhost.
// Properties used in the template
viewList: {type: Array},
propertyNameSortIcon: {type: String},
percentSortIcon: {type: String},
maxPercentage: {type: Number},
};
}
constructor() {
super();
this.viewList = [];
this.type = '';
this.useRemoteData = false;
this.maxPercentage = 100;
this.sortOrders = {
property_name: {reverse: false, activated: false},
percentage: {reverse: true, activated: true},
};
}
firstUpdated() {
const endpoint = `${this.useRemoteData ? 'https://www.chromestatus.com' : ''}/data/${this.type}${this.view}`;
fetch(endpoint).then((res) => res.json()).then((response) => {
this._updateAfterData(response);
});
}
_updateAfterData(items) {
this.fire('app-ready');
if (!items || !items.length) {
return;
}
for (let i = 0, item; item = items[i]; ++i) {
item.percentage = (item.day_percentage * 100).toFixed(6);
}
this.viewList = items.filter((item) => {
return !['ERROR', 'PageVisits', 'PageDestruction'].includes(item.property_name);
});
this.maxPercentage = this.viewList.reduce((accum, currVal) => {
return Math.max(accum, currVal.percentage);
}, 0);
if (location.hash) {
const hash = decodeURIComponent(location.hash);
if (hash) {
const el = this.$['stack-rank-list'].querySelector(hash);
el.scrollIntoView(true, {behavior: 'smooth'});
}
}
}
sort(e) {
e.preventDefault();
const order = e.target.dataset.order;
sortBy_(order, this.viewList);
switch (order) {
case 'percentage':
this.sortOrders.percentage.activated = true;
this.sortOrders.percentage.reverse = !this.sortOrders.percentage.reverse;
break;
case 'property_name':
this.sortOrders.property_name.activated = true;
this.sortOrders.property_name.reverse = !this.sortOrders.property_name.reverse;
break;
}
this._updateSortIcons();
if (this.sortOrders[order].reverse) {
this.viewList.reverse();
}
// TODO: remove when github.com/Polymer/polymer/issues/2175 is fixed.
this.viewList = this.viewList.slice(0);
}
showTimeline(e) {
window.location.href = `/metrics/${this.type}/timeline/${this.view}/${e.model.prop.bucket_id}`;
}
_updateSortIcons() {
this.propertyNameSortIcon = this._getOrderIcon('property_name');
this.percentSortIcon = this._getOrderIcon('percentage');
}
_getOrderIcon(key) {
if (!this.sortOrders[key].activated) {
return '';
}
return this.sortOrders[key].reverse ?
'chromestatus:arrow-drop-up' : 'chromestatus:arrow-drop-down';
}
render() {
return html`
<link rel="stylesheet" href="/static/css/elements/chromedash-metrics.css">
<b>Showing <span>${this.viewList.length}</span> properties</b>
<ol id="stack-rank-list">
<li class="header">
<label @click="${this.sort}" data-order="property_name">
Property name <iron-icon icon="${this.propertyNameSortIcon}"></iron-icon>
</label>
<label @click="${this.sort}" data-order="percentage" class="percent_label">
Percentage <iron-icon icon="${this.percentSortIcon}"></iron-icon>
</label>
</li>
${this.viewList.map((item) => html`
<li id="${item.property_name}"
title="${item.property_name}. Click to deep link to this property." tabindex="0">
<label>
<a href="#${item.property_name}">${item.property_name}</a>
</label>
<chromedash-x-meter value="${item.percentage}" max="${this.maxPercentage}" @click="${this.showTimeline}"
title="Click to see a timeline view of this property"></chromedash-x-meter>
</li>
`)}
</ol>
`;
}
}
customElements.define('chromedash-metrics', ChromedashMetrics);
const sortBy_ = (propName, arr) => {
const compareAsNumbers = propName === 'percentage' || false;
arr.sort((a, b) => {
const propA = compareAsNumbers ? Number(a[propName]) : a[propName];
const propB = compareAsNumbers ? Number(b[propName]) : b[propName];
if (propA > propB) {
return 1;
}
if (propA < propB) {
return -1;
}
return 0;
});
};

Просмотреть файл

@ -1,170 +0,0 @@
import {LitElement, html} from 'https://unpkg.com/@polymer/lit-element@latest/lit-element.js?module';
import 'https://unpkg.com/@polymer/iron-icon/iron-icon.js?module';
class ChromedashSamplePanel extends LitElement {
static get properties() {
return {
categories: {type: Object}, // Edited in static/js/samples.js
features: {type: Array}, // Edited in static/js/samples.js
filtered: {type: Array}, // Edited in static/js/samples.js
prevSelectedCategory: {type: Object},
};
}
_fireEvent(eventName, detail) {
let event = new CustomEvent(eventName, {detail});
this.dispatchEvent(event);
}
_computeIcon(el) {
return 'chromestatus:' + (el ? el.dataset.category : '');
}
_computeIconId(categoryStr) {
return 'chromestatus:' + this.categories[categoryStr];
}
_computeFeatureLink(id) {
return '/features/' + id;
}
_onSelectCategory() {
// Enable toggling selected item until
// https://github.com/PolymerElements/paper-menu/issues/50 is fixed.
if (this.selectedCategory === this.prevSelectedCategory) {
categoryMenuEl.selected = null;
this.selectedCategory = null;
this.prevSelectedCategory = null;
} else {
this.prevSelectedCategory = this.selectedCategory;
}
this.filter(searchEl.value);
}
_filterOnOperation(features, operator, version) {
return features.filter((feature) => {
const platformMilestones = [
parseInt(feature.shipped_milestone),
parseInt(feature.shipped_android_milestone),
parseInt(feature.shipped_ios_milestone),
parseInt(feature.shipped_webview_milestone),
];
for (let i = 0, milestone; milestone = platformMilestones[i]; ++i) {
if (matchesMilestone_(milestone, operator, version)) {
return true; // Only one of the platforms needs to match.
}
}
});
}
// TODO: move filtering into a behavior. Mostly duped from chromedash-featurelist.html.
filter(val) {
let features = this.features;
if (!features) {
return;
}
if (!val && !this.selectedCategory) {
if (history && history.replaceState) {
history.replaceState('', document.title, location.pathname + location.search);
} else {
location.hash = '';
}
this.filtered = features;
this._fireEvent('update-length', {lenght: this.filtered.length});
return;
}
if (val) {
val = val.trim();
if (history && history.replaceState) {
// TODO debounce this 500ms
history.replaceState({}, document.title, '/samples#' + val);
}
// Returns operator and version query e.g. ["<=25", "<=", "25"].
const operatorMatch = /^([<>=]=?)\s*([0-9]+)/.exec(val);
if (operatorMatch) {
features = this._filterOnOperation(features, operatorMatch[1], operatorMatch[2]);
} else {
const regex = new RegExp(val, 'i');
features = features.filter((feature) => {
return regex.test(feature.name) || regex.test(feature.summary);
});
}
}
// Further refine list based on selected category in menu.
if (this.selectedCategory) {
const category = this.selectedCategory.textContent.trim();
const regex = new RegExp(category, 'i');
features = features.filter((feature) => {
return regex.test(feature.category);
});
}
this.filtered = features;
this._fireEvent('update-length', {lenght: this.filtered.length});
}
render() {
return html`
<div id="sample-panel">
<ul id="samplelist" class="card-content">
${this.filtered.map((feature) => html`
<li>
<div class="card">
<h3 class="feature-name">
<a href="${this._computeFeatureLink(feature.id)}">${feature.name}</a>
<span class="milestone" ?hidden="${!feature.shipped_milestone}">Chrome <span>${feature.shipped_milestone}</span></span>
</h3>
<div class="summary">${feature.summary}</div>
<div class="sample_links">
<div class="demo-links layout horizontal center">
${this.feature.sample_links.map((link) => html`
<a href="${link}" target="_blank" class="demo-link">
<iron-icon icon="chromestatus:open-in-browser"></iron-icon> View Demo
</a>
`)}
</div>
<iron-icon icon="${this._computeIconId(feature.category)}"
title="${feature.category}"></iron-icon>
</div>
</div>
</li>
`)}
</ul>
</div>
`;
}
}
customElements.define('chromedash-sample-panel', ChromedashSamplePanel);
// TODO: move this into a behavior. It's duplicated from chromedash-featurelist.html.
function matchesMilestone_(milestone, operator, version) {
switch (operator) {
case '<':
return milestone < version;
break;
case '<=':
return milestone <= version;
break;
case '>':
return milestone > version;
break;
case '>=':
return milestone >= version;
break;
case '=': // Support both '=' and '=='.
case '==':
return milestone == version;
break;
default:
return false;
}
}

Просмотреть файл

@ -1,257 +0,0 @@
import {LitElement, html} from 'https://unpkg.com/@polymer/lit-element@latest/lit-element.js?module';
import 'https://unpkg.com/@polymer/iron-icon/iron-icon.js?module';
class ChromedashSchedule extends LitElement {
static get properties() {
return {
};
}
constructor() {
super();
}
_objKeys(obj) {
if (!obj) {
return [];
}
return Object.keys(obj).sort();
}
_featuresFor(features, componentName) {
return features[componentName];
}
isRemoved(implStatusChrome) {
return implStatusChrome === 'Removed';
}
isDeprecated(implStatusChrome) {
return implStatusChrome === 'Deprecated' || implStatusChrome === 'No longer pursuing';
}
_computeDaysUntil(dateStr) {
const today = new Date();
const diff = dateDiffInDays(new Date(dateStr), today);
const prefix = diff.future ? 'in' : '';
const days = diff.days > 1 ? 's' : '';
const ago = !diff.future ? 'ago' : '';
return `${prefix} ${diff.days} day${days} ${ago}`;
}
_computeDate(dateStr) {
const opts = {/* weekday: 'short', */month: 'short', day: 'numeric'};
const date = new Date(dateStr);
return new Intl.DateTimeFormat('en-US', opts).format(date);
}
pushDisabled() {
return PushNotifier.GRANTED_ACCESS ? '' : 'disabled';
}
_computePushSubscribed(subscribed) {
return subscribed ? 'chromestatus:notifications' : 'chromestatus:notifications-off';
}
subscribeToFeature(e) {
e.preventDefault();
e.stopPropagation();
const featureId = Polymer.dom(e).event.model.f.id;
const icon = Polymer.dom(e).localTarget;
const receivePush = icon.icon !== 'chromestatus:notifications';
icon.icon = this._computePushSubscribed(receivePush);
if (receivePush) {
PushNotifications.subscribeToFeature(featureId);
} else {
PushNotifications.unsubscribeFromFeature(featureId);
}
}
render() {
return html`
<div>
<div class="releases layout horizontal wrap">
<section class="release">
<div class="layout vertical center">
<h1 class="channel_label">stable</h1>
<h1 class="chrome_version layout horizontal center">
<span class="chrome-logo"></span>
<a href="https://www.google.com/chrome/browser/desktop/index.html" target="_blank" title="Download Chrome stable">Chrome [[channels.stable.version]]</a>
</h1>
</div>
<div class="milestone_info layout horizontal center-center">
<h3><span class="channel_label">Beta</span> was <span class="milestone_info-beta">[[_computeDate(channels.stable.earliest_beta)]] - [[_computeDate(channels.stable.latest_beta)]]</span></h3>
</div>
<div class="milestone_info layout horizontal center-center">
<h3><span class="channel_label">Stable</span> [[_computeDaysUntil(channels.stable.stable_date)]]
<span class="release-stable">( [[_computeDate(channels.stable.stable_date)]] )</span>
</h3>
</div>
<div class="features_list">
<div class="features_header">Features in this release:</div>
<template is="dom-repeat" items="[[_objKeys(channels.stable.components)]]" as="componentName">
<h3 class="feature_components">{{componentName}}</h3>
<ul>
<template is="dom-repeat" items="[[_featuresFor(channels.stable.components, componentName)]]" as="f">
<li data-feature-id$="[[f.id]]">
<a href$="/feature/[[f.id]]">[[f.name]]</a>
<span class="icon_row">
<template is="dom-if" if="[[f.browsers.chrome.origintrial]]">
<span class="tooltip" title="Origin Trial">
<iron-icon icon="chromestatus:extension" class="experimental" data-tooltip></iron-icon>
</span>
</template>
<template is="dom-if" if="[[f.browsers.chrome.intervention]]">
<span class="tooltip" title="Browser intervention">
<iron-icon icon="chromestatus:pan-tool" class="intervention" data-tooltip></iron-icon>
</span>
</template>
<template is="dom-if" if="[[isRemoved(f.browsers.chrome.status.text)]]">
<span class="tooltip" title="Removed">
<iron-icon icon="chromestatus:cancel" class="remove" data-tooltip></iron-icon>
</span>
</template>
<template is="dom-if" if="[[isDeprecated(f.browsers.chrome.status.text)]]">
<span class="tooltip" title="Deprecated">
<iron-icon icon="chromestatus:warning" class="deprecated" data-tooltip></iron-icon>
</span>
</template>
<span class="tooltip no-push-notifications" title="Subscribe to notification updates">
<iron-icon icon="chromestatus:notifications-off"
on-click="subscribeToFeature" class$="pushicon [[pushDisabled()]]"></iron-icon>
</span>
</span>
</li>
</template>
</ul>
</template>
</div>
</section>
<section class="release release-beta">
<div class="layout vertical center">
<h1 class="channel_label">Next up</h1>
<h1 class="chrome_version chrome_version--beta layout horizontal center">
<span class="chrome-logo"></span>
<a href="https://www.google.com/chrome/browser/beta.html" target="_blank" title="Download Chrome Beta">Chrome [[channels.beta.version]]</a>
</h1>
</div>
<div class="milestone_info layout horizontal center-center">
<h3><span class="channel_label">Beta</span> between <span class="milestone_info-beta">[[_computeDate(channels.beta.earliest_beta)]] - [[_computeDate(channels.beta.latest_beta)]]</span></h3>
</div>
<div class="milestone_info layout horizontal center-center">
<h3><span class="channel_label">Stable</span> [[_computeDaysUntil(channels.beta.stable_date)]]
<span class="release-stable">( [[_computeDate(channels.beta.stable_date)]] )</span>
</h3>
</div>
<div class="features_list">
<div class="features_header">Features planned in this release:</div>
<template is="dom-repeat" items="[[_objKeys(channels.beta.components)]]" as="componentName">
<h3 class="feature_components">{{componentName}}</h3>
<ul>
<template is="dom-repeat" items="[[_featuresFor(channels.beta.components, componentName)]]" as="f">
<li data-feature-id$="[[f.id]]">
<a href$="/feature/[[f.id]]">[[f.name]]</a>
<span class="icon_row">
<template is="dom-if" if="[[f.browsers.chrome.origintrial]]">
<span class="tooltip" title="Origin Trial">
<iron-icon icon="chromestatus:extension" class="experimental" data-tooltip></iron-icon>
</span>
</template>
<template is="dom-if" if="[[f.browsers.chrome.intervention]]">
<span class="tooltip" title="Browser intervention">
<iron-icon icon="chromestatus:pan-tool" class="intervention" data-tooltip></iron-icon>
</span>
</template>
<template is="dom-if" if="[[isRemoved(f.browsers.chrome.status.text)]]">
<span class="tooltip" title="Removed">
<iron-icon icon="chromestatus:cancel" class="remove" data-tooltip></iron-icon>
</span>
</template>
<template is="dom-if" if="[[isDeprecated(f.browsers.chrome.status.text)]]">
<span class="tooltip" title="Deprecated">
<iron-icon icon="chromestatus:warning" class="deprecated" data-tooltip></iron-icon>
</span>
</template>
<span class="tooltip no-push-notifications" title="Subscribe to notification updates">
<iron-icon icon="chromestatus:notifications-off"
on-click="subscribeToFeature" class$="pushicon [[pushDisabled()]]"></iron-icon>
</span>
</span>
</li>
</template>
</ul>
</template>
</div>
</section>
<section class="release">
<div class="layout vertical center">
<h1 class="channel_label">&nbsp;</h1>
<h1 class="chrome_version chrome_version--dev layout horizontal center">
<span class="chrome-logo"></span>
<a href="https://www.google.com/chrome/browser/canary.html" target="_blank" title="Download Chrome Canary">Chrome [[channels.dev.version]]</a>
</h1>
</div>
<div class="milestone_info layout horizontal center-center">
<h3><span class="channel_label">Beta </span> coming <span class="milestone_info-beta">[[_computeDate(channels.dev.earliest_beta)]] - [[_computeDate(channels.dev.latest_beta)]]</span></h3>
</div>
<div class="milestone_info layout horizontal center-center">
<h3><span class="channel_label">Stable</span> [[_computeDaysUntil(channels.dev.stable_date)]]
<span class="release-stable">( [[_computeDate(channels.dev.stable_date)]] )</span>
</h3>
</div>
<div class="features_list">
<div class="features_header">Features planned in this release:</div>
<template is="dom-repeat" items="[[_objKeys(channels.dev.components)]]" as="componentName">
<h3 class="feature_components">{{componentName}}</h3>
<ul>
<template is="dom-repeat" items="[[_featuresFor(channels.dev.components, componentName)]]" as="f">
<li data-feature-id$="[[f.id]]">
<a href$="/feature/[[f.id]]">[[f.name]]</a>
<span class="icon_row">
<template is="dom-if" if="[[f.browsers.chrome.origintrial]]">
<span class="tooltip" title="Origin Trial">
<iron-icon icon="chromestatus:extension" class="experimental" data-tooltip></iron-icon>
</span>
</template>
<template is="dom-if" if="[[f.browsers.chrome.intervention]]">
<span class="tooltip" title="Browser intervention">
<iron-icon icon="chromestatus:pan-tool" class="intervention" data-tooltip></iron-icon>
</span>
</template>
<template is="dom-if" if="[[isRemoved(f.browsers.chrome.status.text)]]">
<span class="tooltip" title="Removed">
<iron-icon icon="chromestatus:cancel" class="remove" data-tooltip></iron-icon>
</span>
</template>
<template is="dom-if" if="[[isDeprecated(f.browsers.chrome.status.text)]]">
<span class="tooltip" title="Deprecated">
<iron-icon icon="chromestatus:warning" class="deprecated" data-tooltip></iron-icon>
</span>
</template>
<span class="tooltip no-push-notifications" title="Subscribe to notification updates">
<iron-icon icon="chromestatus:notifications-off"
on-click="subscribeToFeature" class$="pushicon [[pushDisabled()]]"></iron-icon>
</span>
</span>
</li>
</template>
</ul>
</template>
</div>
</section>
</div>
</div>
`;
}
}
customElements.define('chromedash-schedule', ChromedashSchedule);

Просмотреть файл

@ -1,218 +0,0 @@
import {LitElement, html} from 'https://unpkg.com/@polymer/lit-element@latest/lit-element.js?module';
import 'https://www.gstatic.com/charts/loader.js'; // Global `google.charts.load`
class ChromedashTimeline extends LitElement {
static get properties() {
return {
selectedBucketId: {type: Number},
showAllHistoricalData: {type: Boolean},
timeline: {type: Object}, // Not used?
title: {type: String},
type: {type: String},
view: {type: String},
props: {type: Array}, // Directly edited from metrics/css/timeline/popularity and metrics/feature/timeline/popularity
useRemoteData: {type: Boolean}, // If true, fetches live data from chromestatus.com instead of localhost.
};
}
constructor() {
super();
this.selectedBucketId = 1;
this.showAllHistoricalData = false;
this.title = '';
this.type = '';
this.view = '';
this.props = [];
this.useRemoteData = false;
}
updated(changedProperties) {
const TRACKING_PROPERTIES = [
'selectedBucketId',
'type',
'view',
'useRemoteData',
'showAllHistoricalData',
];
if (TRACKING_PROPERTIES.some((property) => changedProperties.get(property))) {
this._updateTimeline();
}
}
updateSelectedBucketId(e) {
this.selectedBucketId = e.currentTarget.value;
}
toggleShowAllHistoricalData() {
this.showAllHistoricalData = !this.showAllHistoricalData;
}
ready() {
google.charts.load('current', {'packages': ['corechart']});
google.charts.setOnLoadCallback(() => {
// If there's an id in the URL, load the property it.
const lastSlash = location.pathname.lastIndexOf('/');
if (lastSlash > 0) {
const id = parseInt(location.pathname.substring(lastSlash + 1));
if (String(id) != 'NaN') {
this.selectedBucketId = id;
}
}
});
}
drawVisualization(data, bucketId, showAllHistoricalData) {
const table = new google.visualization.DataTable();
table.addColumn('date', 'Date');
table.addColumn('number', 'Percentage');
table.addColumn({type: 'string', role: 'annotation'});
table.addColumn({type: 'string', role: 'annotationText'});
const rowArray = [];
for (let i = 0, item; item = data[i]; ++i) {
const dateStr = item.date.split('-');
const date = new Date();
date.setFullYear(parseInt(dateStr[0]), parseInt(dateStr[1]) - 1, parseInt(dateStr[2]));
const row = [date, parseFloat((item.day_percentage * 100).toFixed(6))];
// Add annotation where UMA data switched to new stuff.
if (item.date === '2017-10-27') {
row.push('A', 'Modernized metrics');
} else {
row.push(null, null);
}
rowArray.push(row);
}
table.addRows(rowArray);
table = google.visualization.data.group(table,
[{column: 0, modifier: new Date(date.getFullYear(), date.getMonth()), type: 'date'}],
[{
column: 1,
aggregation: google.visualization.data.avg,
type: 'number',
label: 'Monthly Average',
}],
[{column: 2, type: 'string'}]
);
const formatter = new google.visualization.NumberFormat({fractionDigits: 6});
formatter.format(table, 1); // Apply formatter to percentage column.
let view = table;
if (!showAllHistoricalData) {
const startYear = (new Date()).getFullYear() - 2; // Show only 2 years of data by default.
view = new google.visualization.DataView(table);
view.setRows(view.getFilteredRows([{column: 0, minValue: new Date(startYear, 0, 1)}]));
}
const chart = new google.visualization.LineChart(this.$.chart);
chart.draw(view, {
// title: this.title,
// subtitle: 'all channels and platforms',
backgroundColor: 'white',
legend: {position: 'none'},
curveType: 'function',
vAxis: {
title: '% page loads',
// maxValue: 100,
minValue: 0,
},
hAxis: {
title: 'Date',
format: 'M/yy',
},
width: '100%',
height: '100%',
pointSize: 4,
series: {0: {color: '#4580c0'}},
trendlines: {
0: {
type: 'linear',
opacity: 0.5,
color: '#bdd6ed',
pointsVisible: false,
},
},
});
}
_updateTimeline() {
if (this.selectedBucketId === 1) {
return;
}
const prefix = this.useRemoteData ? 'https://www.chromestatus.com' : '';
const url = prefix + '/data/timeline/' + this.type + this.view +
'?bucket_id=' + this.selectedBucketId;
this._renderHTTPArchiveData();
fetch(url).then((res) => res.json()).then((response) => {
this.drawVisualization(response, this.selectedBucketId, this.showAllHistoricalData);
});
if (history.pushState) {
const url = '/metrics/' + this.type + '/timeline/' + this.view + '/' +
this.selectedBucketId;
history.pushState({id: this.selectedBucketId}, '', url);
}
}
_renderHTTPArchiveData() {
if (!this.props.length) {
return;
}
const feature = this.props.find((el) => el[0] === parseInt(this.selectedBucketId));
if (feature) {
const featureName = feature[1];
const REPORT_ID = '1M8kXOqPkwYNKjJhtag_nvDNJCpvmw_ri';
const dsEmbedUrl = `https://datastudio.google.com/embed/reporting/${REPORT_ID}/page/tc5b?config=%7B"df3":"include%25EE%2580%25800%25EE%2580%2580IN%25EE%2580%2580${featureName}"%7D`;
this.$.httparchivedata.src = dsEmbedUrl;
this.$.bigquery.textContent = `#standardSQL
SELECT yyyymmdd, client, pct_urls, sample_urls
FROM \`httparchive.blink_features.usage\`
WHERE feature = '${featureName}'
ORDER BY yyyymmdd DESC, client`;
}
}
render() {
return html`
<link rel="stylesheet" href="/static/css/elements/chromedash-timeline.css">
<select .value="${this.selectedBucketId}" @change="${this.updateSelectedBucketId}">
<option disabled value="1">Select a property</option>
${this.props.map((prop) => html`
<option value="${prop[0]}">${prop[1]}</option>
`)}
</select>
<label>Show all historical data: <input type="checkbox" ?checked="${this.showAllHistoricalData}" @change="${this.toggleShowAllHistoricalData}"></label>
<h3 id="usage" class="header_title">Percentage of page loads that use this feature</h3>
<p class="description">The chart below shows the percentage of page loads (in Chrome) that use
this feature at least once. Data is across all channels and platforms.
</p>
<div id="chart"></div>
<p class="callout">
<b>Note</b>: on 2017-10-26 the underlying metrics were switched over to a newer collection system
which is <a href="https://groups.google.com/a/chromium.org/forum/#!msg/blink-api-owners-discuss/IpIkbz0qtrM/HUCfSMv2AQAJ" target="_blank">more accurate</a>.
This is also the reason for the abrupt spike around 2017-10-26.
</p>
<h3 id="httparchive" class="header_title">Adoption of the feature on top sites</h3>
<p class="description">The chart below shows the adoption of the feature by the top URLs on the internet. Data from <a href="https://httparchive.org/" target="blank">HTTP Archive</a>.</p>
<iframe id="httparchivedata"></iframe>
<p class="callout">
<b>Note</b>: The jump around July and December 2018 are because the corpus of URLs crawled by HTTP Archive increased. These jumps have no correlation with the jump in the top graph.
See the <a href="https://discuss.httparchive.org/t/changes-to-the-http-archive-corpus/1539" target="_blank">announcement</a> for more details.
</p>
<p class="description">Copy and run this command in <a href="https://bigquery.cloud.google.com" target="_blank">BigQuery</a> to produce similar results:</p>
<pre id="bigquery"></pre>
`;
}
}
customElements.define('chromedash-timeline', ChromedashTimeline);

Просмотреть файл

@ -1,88 +0,0 @@
import {LitElement, html} from 'https://unpkg.com/@polymer/lit-element@latest/lit-element.js?module';
// Keeps track of the toast currently opened.
let _currentToast = null;
class ChromedashToast extends LitElement {
static get properties() {
return {
msg: {type: String}, // The toast's message.
actionLabel: {type: String}, // A label for the call to action of the toast.
duration: {type: Number}, // The duration in milliseconds to show the toast. -1 or `Infinity`, to disable the toast auto-closing.
open: {type: Boolean, reflect: true},
};
}
constructor() {
super();
this.msg = '';
this.actionLabel = '';
this.duration = 7000;
this.open = false;
}
/**
* Shows a message in the toast.
* @method showMessage
* @param {string} msg Message to show.Notification (event) type.
* @param {string} optAction Optional action link.
* @param {function} optTapHandler Optional handler to execute when the
* toast action is tapped.
* @param {Number} optDuration Optional duration to show the toast for.
*/
showMessage(msg, optAction, optTapHandler, optDuration) {
this.msg = msg;
this.actionLabel = optAction;
this.shadowRoot.querySelector('#action').addEventListener('click', () => {
e.preventDefault();
if (optTapHandler) {
optTapHandler();
}
}, {once: true});
// Override duration just for this toast.
const originalDuration = this.duration;
if (typeof optDuration !== 'undefined') {
this.duration = optDuration;
}
this.open = true;
this.duration = originalDuration; // reset site-wide duration.
}
close() {
this.open = false;
}
_openChanged() {
clearTimeout(this._timerId);
if (this.open) {
// Close existing toast if one is showing.
if (_currentToast && _currentToast !== this) {
_currentToast.close();
}
_currentToast = this;
if (this.duration >= 0) {
this._timerId = setTimeout(() => this.close(), this.duration);
}
} else if (_currentToast === this) {
_currentToast = null;
}
}
render() {
return html`
<link rel="stylesheet" href="/static/css/elements/chromedash-toast.css">
<div id="message_container">
<span id="msg">${this.msg}</span>
<a href="#" id="action">${this.actionLabel}</a>
</div>
`;
}
}
customElements.define('chromedash-toast', ChromedashToast);

Просмотреть файл

@ -1,105 +0,0 @@
import {LitElement, html} from 'https://unpkg.com/@polymer/lit-element@latest/lit-element.js?module';
class ChromedashUserlist extends LitElement {
static get properties() {
return {
action: {type: String},
users: {type: Array},
};
}
constructor() {
super();
this.users = [];
}
addUser(user) {
this.splice('users', 0, 0, user);
}
removeUser(idx) {
return this.splice('users', idx, 1);
}
ajaxSubmit(e) {
e.preventDefault();
if (this.$.form.checkValidity()) {
// TODO(ericbidelman): move back to this.$.form.email.value when SD
// polyfill merges the commit that wraps element.form.
const email = this.$.form.querySelector('input[name="email"]').value;
const formData = new FormData();
formData.append('email', email);
fetch(this.action, {
method: 'POST',
body: formData,
credentials: 'same-origin', // Needed for admin permissions to be sent.
}).then((resp) => {
if (resp.status === 200) {
alert('Thanks. But that user already exists');
throw new Error('User already added');
} else if (resp.status !== 201) {
throw new Error('Sever error adding new user');
}
return resp.json();
}).then((json) => {
this.addUser(json);
this.$.form.reset();
});
}
}
ajaxDelete(e) {
e.preventDefault();
if (!confirm('Remove user?')) {
return;
}
// Get index of user model instance that was clicked from template.
const user = e.model.user;
const idx = this.users.indexOf(user);
fetch(e.currentTarget.href, {
method: 'POST',
credentials: 'same-origin',
}).then(() => {
const liEl = e.currentTarget.parentElement;
liEl.classList.add('faded');
if ('ontransitionend' in window) {
liEl.addEventListener('transitionend', () => {
this.removeUser(idx);
});
liEl.classList.add('faded');
} else {
this.removeUser(idx);
}
});
}
render() {
return html`
<link rel="stylesheet" href="/static/css/elements/chromedash-userlist.css">
<form id="form" name="user_form" method="post" action="${this.action}" onsubmit="return false;">
<table>
<tr>
<td><input type="email" placeholder="Email address" name="email" id="id_email" required></td>
<td><input type="submit" @click="${this.ajaxSubmit}"></td>
</tr>
</table>
</form>
<ul id="user-list">
${this.users.map(user => html`
<li>
<a href="${this.action}/${user.id}" @click="${this.ajaxDelete}">delete</a>
<span>${user.email}</span>
</li>
`)}
</ul>
`;
}
}
customElements.define('chromedash-userlist', ChromedashUserlist);

Просмотреть файл

@ -1,39 +0,0 @@
import {LitElement, html} from 'https://unpkg.com/@polymer/lit-element@latest/lit-element.js?module';
class ChromedashXMeter extends LitElement {
static get properties() {
return {
value: {type: Number},
valueFormatted: {type: Number},
max: {type: Number}, // Normalized, maximum width for the bar
};
}
get valueFormatted() {
return this.valueFormatted_;
}
set valueFormatted(value) {
this.valueFormatted_ = value <= 0.000001 ? '<=0.000001%' : value + '%';
}
constructor() {
super();
this.value = 0;
this.max = 100;
}
render() {
return html`
<link rel="stylesheet" href="/static/css/chromedash-x-meter.css">
<div class="meter" id="meter">
<div style="width: ${(this.value / this.max * 100)}%">
<span>${this.valueFormatted}</span>
</div>
</div>
`;
}
}
customElements.define('chromedash-x-meter', ChromedashXMeter);