);
}
function setButtonState(button, state) {
const hint = button.parentElement && button.parentElement.querySelector(‘[data-author-follow-hint]’);
const followLabel = button.dataset.followLabel || ‘Follow’;
const followingLabel = button.dataset.followingLabel || ‘Following’;
const signinLabel = button.dataset.signinLabel || ‘Sign in to follow’;
const loadingLabel = button.dataset.loadingLabel || ‘Saving…’;
const pendingAuthLabel = button.dataset.pendingAuthLabel || ‘Loading…’;
button.classList.remove(‘is-following’, ‘is-loading’, ‘is-auth-required’);
if (state === ‘loading’) {
button.classList.add(‘is-loading’);
button.disabled = true;
button.textContent = loadingLabel;
if (hint) {
hint.textContent=”Updating your followed authors…”;
}
return;
}
if (state === ‘pending-auth’) {
button.classList.add(‘is-loading’);
button.disabled = true;
button.textContent = pendingAuthLabel;
if (hint) {
hint.textContent=”Checking your sign-in status…”;
}
return;
}
button.disabled = false;
if (state === ‘following’) {
button.classList.add(‘is-following’);
button.textContent = followingLabel;
if (hint) {
hint.textContent = followingLabel === ‘Unfollow’
? ‘Remove this writer from your followed list.’
: ‘Click again to unfollow.’;
}
return;
}
if (state === ‘signin’) {
button.classList.add(‘is-auth-required’);
button.textContent = signinLabel;
if (hint) {
hint.textContent=”Sign in to follow writers and manage your list.”;
}
return;
}
button.textContent = followLabel;
if (hint) {
hint.textContent=”Click to follow.”;
}
}
function renderWidgets(scope) {
const root = scope || document;
root.querySelectorAll(‘[data-author-follow-widget]’).forEach(function (widget) {
const button = widget.querySelector(‘[data-author-follow-button]’);
if (!button) {
return;
}
const staffId = widget.dataset.staffId;
const hasToken = !!getBearerToken();
if (!isPianoIdentityReady() && !hasToken) {
setButtonState(button, ‘pending-auth’);
return;
}
if (!hasToken) {
setButtonState(button, ‘signin’);
return;
}
if (followedAuthorsMap.has(String(staffId))) {
setButtonState(button, ‘following’);
} else {
setButtonState(button, ‘follow’);
}
});
}
function renderManageList(container) {
if (!container) {
return;
}
const hasToken = !!getBearerToken();
if (!isPianoIdentityReady() && !hasToken) {
container.innerHTML = (
‘
‘
);
return;
}
if (!hasToken) {
container.innerHTML = (
‘
‘
);
return;
}
const followedAuthors = Array.from(followedAuthorsMap.values());
if (!followedAuthors.length) {
container.innerHTML = (
‘
‘
);
return;
}
container.innerHTML = followedAuthors
.map(function (author) {
return (
‘
‘
);
})
.join(”);
bindEvents(container);
renderWidgets(container);
}
async function refresh(scope, options) {
const forceFetch = !!(options && options.force);
if (!hasFollowWidgets(scope)) {
return;
}
// Wipe the cached map BEFORE the first render so a user-switch can’t
// momentarily display the previous user’s “Following” state.
discardCacheIfTokenChanged();
renderWidgets(scope);
const manageContainer = document.querySelector(‘[data-author-follow-manage-list]’);
const managePreferencesContainer = document.querySelector(‘[data-author-follow-manage-preferences]’);
const hasToken = !!getBearerToken();
if (!isPianoIdentityReady() && !hasToken) {
renderWidgets(scope);
renderManagePreferences(managePreferencesContainer);
renderManageList(manageContainer);
return;
}
if (!hasToken) {
renderManagePreferences(managePreferencesContainer);
renderManageList(manageContainer);
return;
}
try {
await loadFollowedAuthors(forceFetch);
renderWidgets(scope);
renderManagePreferences(managePreferencesContainer);
renderManageList(manageContainer);
} catch (error) {
console.warn(‘Unable to load followed authors.’, error);
if (error && error.code === ‘AUTH_REQUIRED’) {
renderWidgets(scope);
renderManagePreferences(managePreferencesContainer);
renderManageList(manageContainer);
}
}
}
function startLoginPoll() {
if (loginPoll) {
clearInterval(loginPoll);
}
let attempts = 0;
loginPoll = setInterval(function () {
attempts += 1;
if (getBearerToken() || isLoggedIn() || attempts > 25) {
clearInterval(loginPoll);
loginPoll = null;
refresh(document, { force: true });
}
}, 1000);
}
function stopPianoReadyPoll() {
if (pianoReadyPoll) {
clearInterval(pianoReadyPoll);
pianoReadyPoll = null;
}
}
function startPianoReadyPoll() {
stopPianoReadyPoll();
let attempts = 0;
pianoReadyPoll = setInterval(function () {
attempts += 1;
if (isPianoIdentityReady() || getBearerToken() || attempts > 40) {
stopPianoReadyPoll();
// Non-forced: if bindPianoInitRefresh already fetched, the TTL
// dedupes this call. If it lost the race, this call wins.
refresh(document);
}
}, 500);
}
function bindPianoInitRefresh() {
if (pianoInitBound) {
return;
}
pianoInitBound = whenPianoReady(function () {
// The Piano init callback is the canonical “identity is ready” signal;
// cancel the fallback poll so it can’t issue a redundant second fetch.
stopPianoReadyPoll();
refresh(document, { force: true });
});
}
async function handleWidgetClick(widget, button) {
const staffId = widget.dataset.staffId;
if (!getBearerToken()) {
setButtonState(button, isPianoIdentityReady() ? ‘signin’ : ‘pending-auth’);
if (typeof window.showPianoLogin === ‘function’) {
window.showPianoLogin();
startLoginPoll();
}
return;
}
// If the identity changed since the cache was last filled, drop the stale
// map before the follow/unfollow decision below — otherwise the click
// could issue the wrong action against the previous user’s state.
discardCacheIfTokenChanged();
setButtonState(button, ‘loading’);
try {
if (followedAuthorsMap.has(String(staffId))) {
await apiRequest(apiBaseUrl + “https://www.washingtontimes.com/” + staffId, { method: ‘DELETE’ });
followedAuthorsMap.delete(String(staffId));
removePushlyFollowedAuthorId(staffId);
} else {
const author = await apiRequest(apiBaseUrl + “https://www.washingtontimes.com/” + staffId, {
method: ‘PUT’,
body: JSON.stringify({
push_enabled: followDeliveryPreferences.push_enabled,
email_fallback_enabled: followDeliveryPreferences.email_fallback_enabled,
}),
});
followedAuthorsMap.set(String(staffId), author);
appendPushlyFollowedAuthorId(staffId);
}
saveStoredFollowDeliveryPreferences(
deriveFollowDeliveryPreferences(Array.from(followedAuthorsMap.values())),
);
syncPushlyFollowedAuthorsProfile();
renderManagePreferences(document.querySelector(‘[data-author-follow-manage-preferences]’));
renderWidgets(document);
renderManageList(document.querySelector(‘[data-author-follow-manage-list]’));
} catch (error) {
console.warn(‘Unable to update followed author.’, error);
if (error && error.code === ‘AUTH_REQUIRED’) {
setButtonState(button, ‘signin’);
}
renderWidgets(document);
}
}
function bindEvents(scope) {
const root = scope || document;
root.querySelectorAll(‘[data-author-follow-widget]’).forEach(function (widget) {
if (widget.dataset.followWidgetBound === ‘true’) {
return;
}
widget.dataset.followWidgetBound = ‘true’;
const button = widget.querySelector(‘[data-author-follow-button]’);
if (!button) {
return;
}
button.addEventListener(‘click’, function () {
handleWidgetClick(widget, button);
});
});
root.querySelectorAll(‘[data-author-follow-manage-preferences]’).forEach(function (container) {
if (container.dataset.followGlobalPreferencesBound === ‘true’) {
return;
}
container.dataset.followGlobalPreferencesBound = ‘true’;
async function handlePreferenceChange() {
const pushInput = container.querySelector(‘[data-author-follow-global-pref=”push”]’);
const emailInput = container.querySelector(‘[data-author-follow-global-pref=”email”]’);
if (!pushInput || !emailInput) {
return;
}
const previousPreferences = {
push_enabled: followDeliveryPreferences.push_enabled,
email_fallback_enabled: followDeliveryPreferences.email_fallback_enabled,
};
const payload = {
push_enabled: !!pushInput.checked,
email_fallback_enabled: !!emailInput.checked,
};
// Drop a stale map if the identity changed before iterating — we
// don’t want to issue follow updates against the previous user’s
// authors with the current user’s bearer token.
discardCacheIfTokenChanged();
const followedAuthors = Array.from(followedAuthorsMap.values());
saveStoredFollowDeliveryPreferences(payload);
managePreferencesPending = true;
pushInput.disabled = true;
emailInput.disabled = true;
try {
await Promise.all(
followedAuthors.map(function (author) {
return updateFollowPreferences(author.id, payload);
}),
);
renderManagePreferences(container);
renderWidgets(document);
renderManageList(document.querySelector(‘[data-author-follow-manage-list]’));
} catch (error) {
console.warn(‘Unable to update author follow preferences.’, error);
saveStoredFollowDeliveryPreferences(previousPreferences);
pushInput.checked = previousPreferences.push_enabled;
emailInput.checked = previousPreferences.email_fallback_enabled;
} finally {
managePreferencesPending = false;
pushInput.disabled = false;
emailInput.disabled = false;
}
}
container.addEventListener(‘change’, function (event) {
const target = event.target;
if (!target || !target.matches(‘[data-author-follow-global-pref]’)) {
return;
}
handlePreferenceChange();
});
});
}
function init(scope) {
const root = scope || document;
if (!hasFollowWidgets(root)) {
return;
}
bindEvents(root);
renderWidgets(root);
bindPianoInitRefresh();
startPianoReadyPoll();
refresh(root);
}
window.WTAuthorFollow = {
init: init,
refresh: refresh,
};
document.addEventListener(‘DOMContentLoaded’, function () {
init(document);
});
window.addEventListener(‘focus’, function () {
if (hasFollowWidgets(document)) {
refresh(document);
}
});
document.addEventListener(‘visibilitychange’, function () {
if (!document.hidden && hasFollowWidgets(document)) {
refresh(document);
}
});
} else {
window.WTAuthorFollow.init(document);
}
})();
The United Airlines logo can be …
more >
A United Airlines flight changed directions to Wisconsin’s Dane County Regional Airport after a passenger tried to breach the cockpit.
After the crew reported the passenger disturbance, United Airlines Flight 2005 diverted to the airport 80 miles west of Milwaukee and landed there at around 9:10 p.m. Friday, the Federal Aviation Administration said.
The flight originally took off from Chicago O’Hare International Airport at around 8:30 p.m. for Minneapolis-Saint Paul International Airport, according to FlightAware.
The Dane County Sheriff’s Office told Madison’s WMTV that the passenger trying to breach the cockpit was a 75-year-old man “suffering a mental health crisis.”
The man, unnamed by authorities, was restrained by law enforcement personnel without any of the other 146 passengers or six crew members on board getting hurt, Dane County Regional Airport officials said, according to Milwaukee’s WITI.
“Don’t think they were able to cuff him but were able to get control of him after multiple attempts to try to breach the cockpit,” a crew member explained to air traffic controllers on the ground in audio obtained by WISN, another Milwaukee station.
Though he was arrested once the plane landed, the passenger is not going to face charges for the disturbance, the sheriff’s office told WMTV. His family is traveling to Wisconsin from Minnesota to reunite with him.
Flight 2005 later took off from the Wisconsin airport at 1:37 a.m. Saturday and landed in Minneapolis at 2:19 a.m., according to FlightAware.
Latest Video
