Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Custom Elements v1 API #67

Merged
merged 12 commits into from
Jan 3, 2018
2 changes: 1 addition & 1 deletion examples/polymer.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/webcomponentsjs/0.7.23/webcomponents.min.js"></script>
<script src="../dist/time-elements-legacy.js"></script>
</head>
<body>
<h2>Past Date</h2>
Expand Down Expand Up @@ -91,5 +90,6 @@ <h2>Future Date</h2>
Oops! This browser doesn't support Web Components.
</time>
</p>
<script src="../dist/time-elements-legacy.js"></script>
</body>
</html>
98 changes: 48 additions & 50 deletions src/extended-time-element.js
Original file line number Diff line number Diff line change
@@ -1,63 +1,61 @@
import {makeFormatter} from './utils'

const ExtendedTimePrototype = Object.create(HTMLElement.prototype)

// Internal: Refresh the time element's formatted date when an attribute changes.
//
// Returns nothing.
ExtendedTimePrototype.attributeChangedCallback = function(attrName, oldValue, newValue) {
if (attrName === 'datetime') {
const millis = Date.parse(newValue)
this._date = isNaN(millis) ? null : new Date(millis)
}

const title = this.getFormattedTitle()
if (title) {
this.setAttribute('title', title)
export default class ExtendedTimeElement extends HTMLElement {
static get observedAttributes() {
return ['datetime', 'day', 'format', 'hour', 'minute', 'month', 'second', 'title', 'weekday', 'year']
}

const text = this.getFormattedDate()
if (text) {
this.textContent = text
}
}
// Internal: Refresh the time element's formatted date when an attribute changes.
//
// Returns nothing.
attributeChangedCallback(attrName, oldValue, newValue) {
if (attrName === 'datetime') {
const millis = Date.parse(newValue)
this._date = isNaN(millis) ? null : new Date(millis)
}

// Internal: Format the ISO 8601 timestamp according to the user agent's
// locale-aware formatting rules. The element's existing `title` attribute
// value takes precedence over this custom format.
//
// Returns a formatted time String.
ExtendedTimePrototype.getFormattedTitle = function() {
if (!this._date) {
return
}
const title = this.getFormattedTitle()
if (title && !this.hasAttribute('title')) {
this.setAttribute('title', title)
}

if (this.hasAttribute('title')) {
return this.getAttribute('title')
const text = this.getFormattedDate()
if (text) {
this.textContent = text
}
}

const formatter = makeFormatter({
day: 'numeric',
month: 'short',
year: 'numeric',
hour: 'numeric',
minute: '2-digit',
timeZoneName: 'short'
})
// Internal: Format the ISO 8601 timestamp according to the user agent's
// locale-aware formatting rules. The element's existing `title` attribute
// value takes precedence over this custom format.
//
// Returns a formatted time String.
getFormattedTitle() {
if (!this._date) {
return
}

if (formatter) {
return formatter.format(this._date)
} else {
try {
return this._date.toLocaleString()
} catch (e) {
if (e instanceof RangeError) {
return this._date.toString()
} else {
throw e
const formatter = makeFormatter({
day: 'numeric',
month: 'short',
year: 'numeric',
hour: 'numeric',
minute: '2-digit',
timeZoneName: 'short'
})

if (formatter) {
return formatter.format(this._date)
} else {
try {
return this._date.toLocaleString()
} catch (e) {
if (e instanceof RangeError) {
return this._date.toString()
} else {
throw e
}
}
}
}
}

export default ExtendedTimePrototype
69 changes: 27 additions & 42 deletions src/local-time-element.js
Original file line number Diff line number Diff line change
@@ -1,46 +1,32 @@
import {strftime, makeFormatter, isDayFirst} from './utils'
import ExtendedTimePrototype from './extended-time-element'
import ExtendedTimeElement from './extended-time-element'

const LocalTimePrototype = Object.create(ExtendedTimePrototype)

LocalTimePrototype.createdCallback = function() {
let value

value = this.getAttribute('datetime')
if (value) {
this.attributeChangedCallback('datetime', null, value)
}

value = this.getAttribute('format')
if (value) {
this.attributeChangedCallback('format', null, value)
}
}
export default class LocalTimeElement extends ExtendedTimeElement {
// Formats the element's date, in the user's current locale, according to
// the formatting attribute values. Values are not passed straight through to
// an Intl.DateTimeFormat instance so that weekday and month names are always
// displayed in English, for now.
//
// Supported attributes are:
//
// weekday - "short", "long"
// year - "numeric", "2-digit"
// month - "short", "long"
// day - "numeric", "2-digit"
// hour - "numeric", "2-digit"
// minute - "numeric", "2-digit"
// second - "numeric", "2-digit"
//
// Returns a formatted time String.
getFormattedDate() {
if (!this._date) {
return
}

// Formats the element's date, in the user's current locale, according to
// the formatting attribute values. Values are not passed straight through to
// an Intl.DateTimeFormat instance so that weekday and month names are always
// displayed in English, for now.
//
// Supported attributes are:
//
// weekday - "short", "long"
// year - "numeric", "2-digit"
// month - "short", "long"
// day - "numeric", "2-digit"
// hour - "numeric", "2-digit"
// minute - "numeric", "2-digit"
// second - "numeric", "2-digit"
//
// Returns a formatted time String.
LocalTimePrototype.getFormattedDate = function() {
if (!this._date) {
return
const date = formatDate(this) || ''
const time = formatTime(this) || ''
return `${date} ${time}`.trim()
}

const date = formatDate(this) || ''
const time = formatTime(this) || ''
return `${date} ${time}`.trim()
}

// Private: Format a date according to the `weekday`, `day`, `month`,
Expand Down Expand Up @@ -132,6 +118,5 @@ function formatTime(el) {
// var time = new LocalTimeElement()
// # => <local-time></local-time>
//
window.LocalTimeElement = document.registerElement('local-time', {
prototype: LocalTimePrototype
})
window.LocalTimeElement = LocalTimeElement
window.customElements.define('local-time', LocalTimeElement)
58 changes: 24 additions & 34 deletions src/relative-time-element.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,33 @@
import RelativeTime from './relative-time'
import ExtendedTimePrototype from './extended-time-element'
import ExtendedTimeElement from './extended-time-element'

const RelativeTimePrototype = Object.create(ExtendedTimePrototype)

RelativeTimePrototype.createdCallback = function() {
const value = this.getAttribute('datetime')
if (value) {
this.attributeChangedCallback('datetime', null, value)
}
}

RelativeTimePrototype.getFormattedDate = function() {
if (this._date) {
return new RelativeTime(this._date).toString()
export default class RelativeTimeElement extends ExtendedTimeElement {
getFormattedDate() {
if (this._date) {
return new RelativeTime(this._date).toString()
}
}
}

RelativeTimePrototype.attachedCallback = function() {
nowElements.push(this)
connectedCallback() {
nowElements.push(this)

if (!updateNowElementsId) {
updateNowElements()
updateNowElementsId = setInterval(updateNowElements, 60 * 1000)
if (!updateNowElementsId) {
updateNowElements()
updateNowElementsId = setInterval(updateNowElements, 60 * 1000)
}
}
}

RelativeTimePrototype.detachedCallback = function() {
const ix = nowElements.indexOf(this)
if (ix !== -1) {
nowElements.splice(ix, 1)
}
disconnectedCallback() {
const ix = nowElements.indexOf(this)
if (ix !== -1) {
nowElements.splice(ix, 1)
}

if (!nowElements.length) {
if (updateNowElementsId) {
clearInterval(updateNowElementsId)
updateNowElementsId = null
if (!nowElements.length) {
if (updateNowElementsId) {
clearInterval(updateNowElementsId)
updateNowElementsId = null
}
}
}
}
Expand All @@ -61,8 +54,5 @@ function updateNowElements() {
// var time = new RelativeTimeElement()
// # => <relative-time></relative-time>
//
window.RelativeTimeElement = document.registerElement('relative-time', {
prototype: RelativeTimePrototype
})

export default RelativeTimePrototype
window.RelativeTimeElement = RelativeTimeElement
window.customElements.define('relative-time', RelativeTimeElement)
25 changes: 12 additions & 13 deletions src/time-ago-element.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import RelativeTime from './relative-time'
import RelativeTimePrototype from './relative-time-element'
import RelativeTimeElement from './relative-time-element'

const TimeAgoPrototype = Object.create(RelativeTimePrototype)

TimeAgoPrototype.getFormattedDate = function() {
if (this._date) {
const format = this.getAttribute('format')
if (format === 'micro') {
return new RelativeTime(this._date).microTimeAgo()
} else {
return new RelativeTime(this._date).timeAgo()
export default class TimeAgoElement extends RelativeTimeElement {
getFormattedDate() {
if (this._date) {
const format = this.getAttribute('format')
if (format === 'micro') {
return new RelativeTime(this._date).microTimeAgo()
} else {
return new RelativeTime(this._date).timeAgo()
}
}
}
}

window.TimeAgoElement = document.registerElement('time-ago', {
prototype: TimeAgoPrototype
})
window.TimeAgoElement = TimeAgoElement
window.customElements.define('time-ago', TimeAgoElement)
25 changes: 12 additions & 13 deletions src/time-until-element.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import RelativeTime from './relative-time'
import RelativeTimePrototype from './relative-time-element'
import RelativeTimeElement from './relative-time-element'

const TimeUntilPrototype = Object.create(RelativeTimePrototype)

TimeUntilPrototype.getFormattedDate = function() {
if (this._date) {
const format = this.getAttribute('format')
if (format === 'micro') {
return new RelativeTime(this._date).microTimeUntil()
} else {
return new RelativeTime(this._date).timeUntil()
export default class TimeUntilElement extends RelativeTimeElement {
getFormattedDate() {
if (this._date) {
const format = this.getAttribute('format')
if (format === 'micro') {
return new RelativeTime(this._date).microTimeUntil()
} else {
return new RelativeTime(this._date).timeUntil()
}
}
}
}

window.TimeUntilElement = document.registerElement('time-until', {
prototype: TimeUntilPrototype
})
window.TimeUntilElement = TimeUntilElement
window.customElements.define('time-until', TimeUntilElement)