Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
528 changes: 120 additions & 408 deletions src/js/animation/Animate.js

Large diffs are not rendered by default.

72 changes: 24 additions & 48 deletions src/js/core/Browser.js
Original file line number Diff line number Diff line change
@@ -1,68 +1,44 @@
/*
Based on Leaflet Browser
Browser and device detection utilities
Modernized to remove IE-specific detection
*/

export const ua = navigator ? navigator.userAgent.toLowerCase() : 'no-user-agent-specified';

const doc = document ? document.documentElement : null;
const phantomjs = ua ? ua.indexOf("phantom") !== -1 : false;


export const ie = window && 'ActiveXObject' in window

export const ie9 = Boolean(ie && ua.match(/MSIE 9/i))
export const ielt9 = ie && document && !document.addEventListener

export const webkit = ua.indexOf('webkit') !== -1
export const android = ua.indexOf('android') !== -1
export const mobile = window ? /mobile|tablet|ip(ad|hone|od)|android/i.test(ua) || 'ontouchstart' in window : false

export const android23 = ua.search('android [23]') !== -1
export const mobile = (window) ? typeof window.orientation !== 'undefined' : false
export const msPointer = (navigator && window) ? navigator.msPointerEnabled && navigator.msMaxTouchPoints && !window.PointerEvent : false
export const pointer = (navigator && window) ? (window.PointerEvent && navigator.pointerEnabled && navigator.maxTouchPoints) : msPointer

export const opera = window ? window.opera : false;
export const gecko = ua.indexOf("gecko") !== -1 && !webkit
export const firefox = gecko && ua.indexOf("firefox") !== -1
export const chrome = ua.indexOf("chrome") !== -1
export const edge = ua.indexOf("edge/") !== -1 || ua.indexOf("edg/") !== -1
export const safari = webkit && ua.indexOf("safari") !== -1 && !chrome && !edge

export const gecko = ua.indexOf("gecko") !== -1 && !webkit && !opera && !ie;
export const firefox = ua.indexOf("gecko") !== -1 && !webkit && !opera && !ie;
export const chrome = ua.indexOf("chrome") !== -1;
export const edge = ua.indexOf("edge/") !== -1;

export const ie3d = (doc) ? ie && 'transition' in doc.style : false
export const webkit3d = (window) ? ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23 : false
export const gecko3d = (doc) ? 'MozPerspective' in doc.style : false
export const opera3d = (doc) ? 'OTransition' in doc.style : false

export const any3d = window && !window.L_DISABLE_3D &&
(ie3d || webkit3d || gecko3d || opera3d) && !phantomjs
// Modern browsers all support 3D transforms
export const webkit3d = window ? ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) : false
export const any3d = window && !window.L_DISABLE_3D

export const mobileWebkit = mobile && webkit
export const mobileWebkit3d = mobile && webkit3d
export const mobileOpera = mobile && window.opera

export let retina = (window) ? 'devicePixelRatio' in window && window.devicePixelRatio > 1 : false
// Retina display detection
export let retina = window ? 'devicePixelRatio' in window && window.devicePixelRatio > 1 : false

if (!retina && window && 'matchMedia' in window) {
let media_matches = window.matchMedia('(min-resolution:144dpi)');
const media_matches = window.matchMedia('(min-resolution:144dpi), (-webkit-min-device-pixel-ratio: 1.5)');
retina = media_matches && media_matches.matches;
}

export const touch = window &&
!window.L_NO_TOUCH &&
!phantomjs &&
(pointer || 'ontouchstart' in window || (window.DocumentTouch && document instanceof window.DocumentTouch))

// Touch support - using modern pointer events when available
export const pointer = window && window.PointerEvent && navigator.maxTouchPoints > 0
export const touch = window && (pointer || 'ontouchstart' in window || (window.DocumentTouch && document instanceof window.DocumentTouch))

/**
* Determine device orientation based on viewport dimensions
* @returns {string} "portrait" or "landscape"
*/
export function orientation() {
var w = window.innerWidth,
h = window.innerHeight,
_orientation = "portrait";

if (w > h) {
_orientation = "landscape";
}
if (Math.abs(window.orientation) == 90) {
//_orientation = "landscape";
}
return _orientation;
const w = window.innerWidth;
const h = window.innerHeight;
return w > h ? "landscape" : "portrait";
}
133 changes: 59 additions & 74 deletions src/js/core/ConfigFactory.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,90 +260,75 @@ export async function jsonFromGoogleURL(google_url, options) {
}

/**
* Using the given URL, fetch or create a JS Object suitable for configuring a timeline. Use
* that to create a TimelineConfig, and invoke the callback with that object as its argument.
* If the second argument is an object instead of a callback function, it must have a
* 'callback' property which will be invoked with the config.
* Even in error cases, a minimal TimelineConfig object will be created and passed to the callback
* so that error messages can be displayed in the host page.
*
* @param {String} url the URL or Google Spreadsheet key which can be used to get configuration information
* @param {function|object} callback_or_options either a callback function or an object with a 'callback' property and other configuration properties
* Using the given URL, fetch or create a JS Object suitable for configuring a timeline.
* Returns a Promise that resolves to a TimelineConfig.
* Even in error cases, a minimal TimelineConfig object will be created with logged errors.
*
* @param {String} url - The URL or Google Spreadsheet key to fetch configuration from
* @param {Object|function} [optionsOrCallback] - Options object or legacy callback function
* @param {string} [optionsOrCallback.sheets_proxy] - Proxy URL for Google Sheets
* @param {function} [optionsOrCallback.callback] - Legacy callback (deprecated, use Promise instead)
* @returns {Promise<TimelineConfig>} Promise that resolves to a TimelineConfig
*/
export async function makeConfig(url, callback_or_options) {

let callback = null,
options = {};
if (typeof(callback_or_options) == 'function') {
callback = callback_or_options
} else if (typeof(callback_or_options) == 'object') {
options = callback_or_options
callback = callback_or_options['callback']
if (typeof(options['callback']) == 'function') callback = options['callback']
}

if (!callback) {
throw new TLError("Second argument to makeConfig must be either a function or an object which includes a 'callback' property with a 'function' type value")
export async function makeConfig(url, optionsOrCallback) {
// Support legacy callback pattern for backward compatibility
let callback = null;
let options = {};

if (typeof optionsOrCallback === 'function') {
callback = optionsOrCallback;
console.warn('makeConfig: callback parameter is deprecated, use the returned Promise instead');
} else if (typeof optionsOrCallback === 'object') {
options = optionsOrCallback;
if (typeof options.callback === 'function') {
callback = options.callback;
console.warn('makeConfig: callback parameter is deprecated, use the returned Promise instead');
}
}

var tc,
json,
key = parseGoogleSpreadsheetURL(url);
let tc;
const key = parseGoogleSpreadsheetURL(url);

if (key) {
try {
try {
if (key) {
// Handle Google Sheets URL
console.log(`reading url ${url}`);
json = await jsonFromGoogleURL(url, options);
} catch (e) {
// even with an error, we make
// a TimelineConfig because it's
// the most straightforward way to display messages
// in the DOM
tc = new TimelineConfig();
if (e.name == 'NetworkError') {
tc.logError(new TLError("network_err"));
} else if (e.name == 'TLError') {
tc.logError(e);
} else {
tc.logError(new TLError("unknown_read_err", e.name));
const json = await jsonFromGoogleURL(url, options);
tc = new TimelineConfig(json);
if (json.errors) {
for (let i = 0; i < json.errors.length; i++) {
tc.logError(json.errors[i]);
}
}
} else {
// Handle regular JSON URL using fetch
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
callback(tc);
return; // don't process further if there were errors
const data = await response.json();
tc = new TimelineConfig(data);
}

tc = new TimelineConfig(json);
if (json.errors) {
for (var i = 0; i < json.errors.length; i++) {
tc.logError(json.errors[i]);
};
} catch (e) {
// Even with an error, create a TimelineConfig to display messages in DOM
tc = new TimelineConfig();
if (e.name === 'NetworkError' || e.message.includes('HTTP error')) {
tc.logError(new TLError("network_err"));
} else if (e.name === 'TLError') {
tc.logError(e);
} else if (e.name === 'SyntaxError') {
tc.logError(new TLError("invalid_url_err"));
} else {
tc.logError(new TLError("unknown_read_err", e.message || e.name));
}
callback(tc);
} else {
ajax({
url: url,
dataType: 'json',
success: function(data) {
try {
tc = new TimelineConfig(data);
} catch (e) {
tc = new TimelineConfig();
tc.logError(e);
}
callback(tc);
},
error: function(xhr, errorType, error) {
tc = new TimelineConfig();
if (errorType == 'parsererror') {
var error = new TLError("invalid_url_err");
} else {
var error = new TLError("unknown_read_err", errorType)
}
tc.logError(error);
callback(tc);
}
});
}

// Call legacy callback if provided
if (callback) {
callback(tc);
}

return tc;
}

function handleRow(event, timeline_config) {
Expand Down
Loading