|
|
|
@@ -0,0 +1,90 @@
|
|
|
|
|
// Weather region rendering for live playback.
|
|
|
|
|
|
|
|
|
|
var weatherRegistry = window.pulsePlayerRegionTypes;
|
|
|
|
|
var weatherPlaceholderUtils = window.placeholderUtils || {};
|
|
|
|
|
|
|
|
|
|
function weatherIconForCode(code) {
|
|
|
|
|
var value = Number(code);
|
|
|
|
|
if (value === 0) return 'bi-sun';
|
|
|
|
|
if (value <= 3) return 'bi-cloud-sun';
|
|
|
|
|
if (value <= 48) return 'bi-cloud-fog';
|
|
|
|
|
if (value <= 67 || value > 77 && value <= 82) return 'bi-cloud-rain';
|
|
|
|
|
if (value <= 77) return 'bi-cloud-snow';
|
|
|
|
|
return 'bi-cloud-lightning-rain';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getWeatherLocation(locationId) {
|
|
|
|
|
var locations = Array.isArray(initialData && initialData.weatherLocations) ? initialData.weatherLocations : [];
|
|
|
|
|
return locations.find(function (location) { return Number(location.id) === Number(locationId); }) || null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function normalizeWeatherExpression(expression) {
|
|
|
|
|
var value = String(expression || '').trim();
|
|
|
|
|
var currentAliases = { temp: 'current.temperature_2m', temp_unit: 'current.temperature_unit', feels_like: 'current.apparent_temperature', humidity: 'current.relative_humidity_2m', code: 'current.weather_code', wind: 'current.wind_speed_10m', wind_unit: 'current.wind_speed_unit', precip: 'current.precipitation', precip_unit: 'current.precipitation_unit', uv_index: 'current.uv_index', cloud_cover: 'current.cloud_cover', icon: 'current.weather_code.icon' };
|
|
|
|
|
var globalAliases = { temp_unit: 'temperature_unit', wind_unit: 'wind_speed_unit', precip_unit: 'precipitation_unit' };
|
|
|
|
|
if (globalAliases[value]) return globalAliases[value];
|
|
|
|
|
var currentField = value.replace(/^current\./, '');
|
|
|
|
|
var currentMatch = currentField.match(/^([^.()]+)((?:\(.*\))|(?:\..*))?$/);
|
|
|
|
|
if (currentMatch && currentAliases[currentMatch[1]]) return currentAliases[currentMatch[1]] + (currentMatch[2] || '');
|
|
|
|
|
var indexed = value.match(/^(daily|hourly)\.(\d+)\.(.+)$/);
|
|
|
|
|
if (!indexed) return value;
|
|
|
|
|
var fieldMatch = indexed[3].match(/^([^.()]+)((?:\(.*\))|(?:\..*))?$/);
|
|
|
|
|
var field = fieldMatch ? fieldMatch[1] : indexed[3];
|
|
|
|
|
var aliases = indexed[1] === 'daily' ? { time: 'time', code: 'weather_code', temp_max: 'temperature_2m_max', temp_min: 'temperature_2m_min', precip: 'precipitation_sum', wind: 'wind_speed_10m_max', uv_index: 'uv_index_max', cloud_cover: 'cloud_cover_mean', sunrise: 'sunrise', sunset: 'sunset', icon: 'weather_code' } : { time: 'time', code: 'weather_code', temp: 'temperature_2m', precip: 'precipitation', wind: 'wind_speed_10m', uv_index: 'uv_index', cloud_cover: 'cloud_cover', icon: 'weather_code' };
|
|
|
|
|
if (!Object.prototype.hasOwnProperty.call(aliases, field)) return value;
|
|
|
|
|
return field === 'icon' ? indexed[1] + '.' + aliases[field] + '.' + indexed[2] + '.icon' + (fieldMatch[2] || '') : indexed[1] + '.' + aliases[field] + '.' + indexed[2] + (fieldMatch[2] || '');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function withWeatherUnits(snapshot, location) {
|
|
|
|
|
var data = Object.assign({}, snapshot, { location_label: location.location_label || '', name: location.name || '', timezone: location.timezone || '', temp_unit: location.temperature_unit === 'fahrenheit' ? '°F' : '°C', wind_unit: location.wind_unit === 'mph' ? 'mph' : location.wind_unit === 'ms' ? 'm/s' : 'km/h', precip_unit: location.precipitation_unit === 'inch' ? 'in' : 'mm' });
|
|
|
|
|
var temperatureUnit = location.temperature_unit === 'fahrenheit' ? '°F' : '°C';
|
|
|
|
|
var windUnit = location.wind_unit === 'mph' ? 'mph' : location.wind_unit === 'ms' ? 'm/s' : 'km/h';
|
|
|
|
|
var precipitationUnit = location.precipitation_unit === 'inch' ? 'in' : 'mm';
|
|
|
|
|
data.current = Object.assign({}, snapshot.current || {}, { temperature_unit: temperatureUnit, wind_speed_unit: windUnit, precipitation_unit: precipitationUnit });
|
|
|
|
|
data.daily = Object.assign({}, snapshot.daily || {}, { temperature_unit: temperatureUnit });
|
|
|
|
|
data.hourly = Object.assign({}, snapshot.hourly || {}, { temperature_unit: temperatureUnit });
|
|
|
|
|
return data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function substituteWeatherVariables(html, value) {
|
|
|
|
|
return String(html || '').replace(/\{\{\s*([^{}]+?)\s*\}\}/g, function (_match, expression) {
|
|
|
|
|
if (!value || typeof value !== 'object' || typeof weatherPlaceholderUtils.resolvePlaceholderExpression !== 'function' || typeof weatherPlaceholderUtils.formatPlaceholderValue !== 'function') return '';
|
|
|
|
|
var rawExpression = String(expression).trim();
|
|
|
|
|
var normalizedExpression = normalizeWeatherExpression(rawExpression);
|
|
|
|
|
var iconMatch = normalizedExpression.match(/^(?:current\.weather_code|daily\.weather_code\.\d+|hourly\.weather_code\.\d+)\.icon(?:\((\d+)(?:\s*,\s*(\d+))?\))?$/);
|
|
|
|
|
if (iconMatch) {
|
|
|
|
|
var codeExpression = normalizedExpression.replace(/\.icon(?:\(.*\))?$/, '');
|
|
|
|
|
var width = iconMatch[1] ? Math.max(1, Math.min(1000, Number(iconMatch[1]))) : 0;
|
|
|
|
|
var height = iconMatch[2] ? Math.max(1, Math.min(1000, Number(iconMatch[2]))) : width;
|
|
|
|
|
var style = width ? ' style="display:inline-block;vertical-align:middle;font-size:' + width + 'px;line-height:' + height + 'px;width:' + width + 'px;height:' + height + 'px;"' : '';
|
|
|
|
|
var weatherCode = weatherPlaceholderUtils.resolvePlaceholderExpression(value, codeExpression, { timeZone: value.timezone });
|
|
|
|
|
var icon = '<i class="bi ' + weatherIconForCode(weatherCode) + '"' + style + ' aria-hidden="true"></i>';
|
|
|
|
|
return width ? '<span style="display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;width:' + width + 'px;height:' + height + 'px;">' + icon + '</span>' : icon;
|
|
|
|
|
}
|
|
|
|
|
return escapeHtml(weatherPlaceholderUtils.formatPlaceholderValue(weatherPlaceholderUtils.resolvePlaceholderExpression(value, normalizeWeatherExpression(expression), { timeZone: value.timezone })));
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function renderWeatherRegion(region, regionContent) {
|
|
|
|
|
var location = getWeatherLocation(regionContent && regionContent.weather_location_id);
|
|
|
|
|
var snapshot = location && location.responseJson && typeof location.responseJson === 'object' ? location.responseJson : null;
|
|
|
|
|
var width = Math.max(1, Math.round(Number(region && region.pixelWidth) || 1));
|
|
|
|
|
var height = Math.max(1, Math.round(Number(region && region.pixelHeight) || 1));
|
|
|
|
|
var scale = Number(region && region.canvasScale) || 1;
|
|
|
|
|
var style = 'width:' + width + 'px;height:' + height + 'px;transform:scale(' + scale + ');transform-origin:top left;overflow:hidden;';
|
|
|
|
|
if (!location || !snapshot) return '<div class="template-region weather" style="' + region.baseStyle + '"><div style="' + style + '"></div></div>';
|
|
|
|
|
var current = snapshot.current || {};
|
|
|
|
|
var value = String(regionContent && regionContent.value || '');
|
|
|
|
|
if (value) {
|
|
|
|
|
var weatherData = withWeatherUnits(snapshot, location);
|
|
|
|
|
var fontSize = Math.max(8, Number(regionContent && regionContent.font_size || region && (region.font_size || region.fontSize) || 32) || 32);
|
|
|
|
|
return '<div class="template-region weather" style="' + region.baseStyle + '"><div style="' + style + 'font-family:Arial,sans-serif;font-size:' + fontSize + 'px;line-height:1.5;">' + renderEditorJsContent(substituteWeatherVariables(value, weatherData)) + '</div></div>';
|
|
|
|
|
}
|
|
|
|
|
var daily = snapshot.daily || {};
|
|
|
|
|
var days = (daily.time || []).slice(0, 7).map(function (day, index) {
|
|
|
|
|
return '<div class="weather-region-day"><strong>' + escapeHtml(index === 0 ? 'Today' : String(day).slice(5)) + '</strong><i class="bi ' + weatherIconForCode(daily.weather_code && daily.weather_code[index]) + '"></i><span>' + escapeHtml(daily.temperature_2m_max && daily.temperature_2m_max[index] !== undefined ? daily.temperature_2m_max[index] + '°' : '-') + '</span></div>';
|
|
|
|
|
}).join('');
|
|
|
|
|
return '<div class="template-region weather" style="' + region.baseStyle + '"><div style="' + style + 'padding:3%;font-family:Arial,sans-serif;"><div style="display:flex;align-items:center;justify-content:space-between;"><div><div style="font-size:.8em;opacity:.72;">' + escapeHtml(location.location_label || location.name) + '</div><strong style="font-size:2em;">' + escapeHtml(current.temperature_2m === undefined ? '-' : current.temperature_2m) + '°</strong></div><i class="bi ' + weatherIconForCode(current.weather_code) + '" style="font-size:3em;"></i></div><div style="display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:.35em;margin-top:1em;">' + days + '</div></div></div>';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
weatherRegistry.register('weather', { renderRegion: renderWeatherRegion });
|