Restore Maore Mobile website from Wayback Machine archive
- Add original CSS styling (maore.css) with complete branding and responsive design - Restore all original SVG icons and images from website dump - Replace Google Fonts with locally hosted Varela Round font family - Download clean JavaScript files (jQuery, Bootstrap, maore.js, utils.js) - Implement Hugo multilingual setup with French/English support - Create responsive homepage template matching original design - Add comprehensive translation files for both languages - Configure local asset paths to eliminate external dependencies - Set up proper navigation structure with all original menu items 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
474
static/js/athena.js
Normal file
474
static/js/athena.js
Normal file
@@ -0,0 +1,474 @@
|
||||
// @license magnet:?xt=urn:btih:0b31508aeb0634b347b8270c7bee4d411b5d4109&dn=agpl-3.0.txt AGPL-v3.0
|
||||
/* eslint-disable no-var, semi, prefer-arrow-callback, prefer-template */
|
||||
|
||||
/**
|
||||
* Collection of methods for sending analytics events to Archive.org's analytics server.
|
||||
*
|
||||
* These events are used for internal stats and sent (in anonymized form) to Google Analytics.
|
||||
*
|
||||
* @see analytics.md
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
window.archive_analytics = (function defineArchiveAnalytics() {
|
||||
// keep orignal Date object so as not to be affected by wayback's
|
||||
// hijacking global Date object
|
||||
var Date = window.Date;
|
||||
var ARCHIVE_ANALYTICS_VERSION = 2;
|
||||
var DEFAULT_SERVICE = 'ao_2';
|
||||
var NO_SAMPLING_SERVICE = 'ao_no_sampling'; // sends every event instead of a percentage
|
||||
|
||||
var startTime = new Date();
|
||||
|
||||
/**
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function isPerformanceTimingApiSupported() {
|
||||
return 'performance' in window && 'timing' in window.performance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines how many milliseconds elapsed between the browser starting to parse the DOM and
|
||||
* the current time.
|
||||
*
|
||||
* Uses the Performance API or a fallback value if it's not available.
|
||||
*
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/Performance_API
|
||||
*
|
||||
* @return {Number}
|
||||
*/
|
||||
function getLoadTime() {
|
||||
var start;
|
||||
|
||||
if (isPerformanceTimingApiSupported())
|
||||
start = window.performance.timing.domLoading;
|
||||
else
|
||||
start = startTime.getTime();
|
||||
|
||||
return new Date().getTime() - start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines how many milliseconds elapsed between the user navigating to the page and
|
||||
* the current time.
|
||||
*
|
||||
* @see https://developer.mozilla.org/en-US/docs/Web/API/Performance_API
|
||||
*
|
||||
* @return {Number|null} null if the browser doesn't support the Performance API
|
||||
*/
|
||||
function getNavToDoneTime() {
|
||||
if (!isPerformanceTimingApiSupported())
|
||||
return null;
|
||||
|
||||
return new Date().getTime() - window.performance.timing.navigationStart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs an arithmetic calculation on a string with a number and unit, while maintaining
|
||||
* the unit.
|
||||
*
|
||||
* @param {String} original value to modify, with a unit
|
||||
* @param {Function} doOperation accepts one Number parameter, returns a Number
|
||||
* @returns {String}
|
||||
*/
|
||||
function computeWithUnit(original, doOperation) {
|
||||
var number = parseFloat(original, 10);
|
||||
var unit = original.replace(/(\d*\.\d+)|\d+/, '');
|
||||
|
||||
return doOperation(number) + unit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the default font size of the browser.
|
||||
*
|
||||
* @returns {String|null} computed font-size with units (typically pixels), null if it cannot be computed
|
||||
*/
|
||||
function getDefaultFontSize() {
|
||||
var fontSizeStr;
|
||||
|
||||
if (!('getComputedStyle' in window))
|
||||
return null;
|
||||
|
||||
var style = window.getComputedStyle(document.documentElement);
|
||||
if (!style)
|
||||
return null;
|
||||
|
||||
fontSizeStr = style.fontSize;
|
||||
|
||||
// Don't modify the value if tracking book reader.
|
||||
if (document.querySelector('#BookReader'))
|
||||
return fontSizeStr;
|
||||
|
||||
return computeWithUnit(fontSizeStr, function reverseBootstrapFontSize(number) {
|
||||
// Undo the 62.5% size applied in the Bootstrap CSS.
|
||||
return number * 1.6;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the URL parameters for a given Location
|
||||
* @param {Location}
|
||||
* @return {Object} The URL parameters
|
||||
*/
|
||||
function getParams(location) {
|
||||
if (!location) location = window.location;
|
||||
var vars;
|
||||
var i;
|
||||
var pair;
|
||||
var params = {};
|
||||
var query = location.search;
|
||||
if (!query) return params;
|
||||
vars = query.substring(1).split('&');
|
||||
for (i = 0; i < vars.length; i++) {
|
||||
pair = vars[i].split('=');
|
||||
params[pair[0]] = decodeURIComponent(pair[1]);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
function getMetaProp(name) {
|
||||
var metaTag = document.querySelector('meta[property=' + name + ']');
|
||||
return metaTag ? metaTag.getAttribute('content') || null : null;
|
||||
}
|
||||
|
||||
var ArchiveAnalytics = {
|
||||
/**
|
||||
* @type {String|null}
|
||||
*/
|
||||
service: getMetaProp('service'),
|
||||
mediaType: getMetaProp('mediatype'),
|
||||
primaryCollection: getMetaProp('primary_collection'),
|
||||
|
||||
/**
|
||||
* Key-value pairs to send in pageviews (you can read this after a pageview to see what was
|
||||
* sent).
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
values: {},
|
||||
|
||||
/**
|
||||
* Sends an analytics ping, preferably using navigator.sendBeacon()
|
||||
* @param {Object} values
|
||||
* @param {Function} [onload_callback] (deprecated) callback to invoke once ping to analytics server is done
|
||||
* @param {Boolean} [augment_for_ao_site] (deprecated) if true, add some archive.org site-specific values
|
||||
*/
|
||||
send_ping: function send_ping(values, onload_callback, augment_for_ao_site) {
|
||||
if (typeof window.navigator !== 'undefined' && typeof window.navigator.sendBeacon !== 'undefined')
|
||||
this.send_ping_via_beacon(values);
|
||||
else
|
||||
this.send_ping_via_image(values);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends a ping via Beacon API
|
||||
* NOTE: Assumes window.navigator.sendBeacon exists
|
||||
* @param {Object} values Tracking parameters to pass
|
||||
*/
|
||||
send_ping_via_beacon: function send_ping_via_beacon(values) {
|
||||
var url = this.generate_tracking_url(values || {});
|
||||
window.navigator.sendBeacon(url);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends a ping via Image object
|
||||
* @param {Object} values Tracking parameters to pass
|
||||
*/
|
||||
send_ping_via_image: function send_ping_via_image(values) {
|
||||
var url = this.generate_tracking_url(values || {});
|
||||
var loadtime_img = new Image(1, 1);
|
||||
loadtime_img.src = url;
|
||||
loadtime_img.alt = '';
|
||||
},
|
||||
|
||||
/**
|
||||
* Construct complete tracking URL containing payload
|
||||
* @param {Object} params Tracking parameters to pass
|
||||
* @return {String} URL to use for tracking call
|
||||
*/
|
||||
generate_tracking_url: function generate_tracking_url(params) {
|
||||
var baseUrl = '//athena.archive.org/0.gif';
|
||||
var keys;
|
||||
var outputParams = params;
|
||||
var outputParamsArray = [];
|
||||
|
||||
outputParams.service = outputParams.service || this.service || DEFAULT_SERVICE;
|
||||
|
||||
// Build array of querystring parameters
|
||||
keys = Object.keys(outputParams);
|
||||
keys.forEach(function keyIteration(key) {
|
||||
outputParamsArray.push(encodeURIComponent(key) + '=' + encodeURIComponent(outputParams[key]));
|
||||
});
|
||||
outputParamsArray.push('version=' + ARCHIVE_ANALYTICS_VERSION);
|
||||
outputParamsArray.push('count=' + (keys.length + 2)); // Include `version` and `count` in count
|
||||
|
||||
return baseUrl + '?' + outputParamsArray.join('&');
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {int} page Page number
|
||||
*/
|
||||
send_scroll_fetch_event: function send_scroll_fetch_event(page) {
|
||||
var additionalValues = { ev: page };
|
||||
var loadTime = getLoadTime();
|
||||
var navToDoneTime = getNavToDoneTime();
|
||||
if (loadTime) additionalValues.loadtime = loadTime;
|
||||
if (navToDoneTime) additionalValues.nav_to_done_ms = navToDoneTime;
|
||||
this.send_event('page_action', 'scroll_fetch', location.pathname, additionalValues);
|
||||
},
|
||||
|
||||
send_scroll_fetch_base_event: function send_scroll_fetch_base_event() {
|
||||
var additionalValues = {};
|
||||
var loadTime = getLoadTime();
|
||||
var navToDoneTime = getNavToDoneTime();
|
||||
if (loadTime) additionalValues.loadtime = loadTime;
|
||||
if (navToDoneTime) additionalValues.nav_to_done_ms = navToDoneTime;
|
||||
this.send_event('page_action', 'scroll_fetch_base', location.pathname, additionalValues);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} [options]
|
||||
* @param {String} [options.mediaType]
|
||||
* @param {String} [options.mediaLanguage]
|
||||
* @param {String} [options.page] The path portion of the page URL
|
||||
*/
|
||||
send_pageview: function send_pageview(options) {
|
||||
var settings = options || {};
|
||||
|
||||
var defaultFontSize;
|
||||
var loadTime = getLoadTime();
|
||||
var mediaType = settings.mediaType;
|
||||
var primaryCollection = settings.primaryCollection;
|
||||
var page = settings.page;
|
||||
var navToDoneTime = getNavToDoneTime();
|
||||
|
||||
/**
|
||||
* @return {String}
|
||||
*/
|
||||
function get_locale() {
|
||||
if (navigator) {
|
||||
if (navigator.language)
|
||||
return navigator.language;
|
||||
|
||||
else if (navigator.browserLanguage)
|
||||
return navigator.browserLanguage;
|
||||
|
||||
else if (navigator.systemLanguage)
|
||||
return navigator.systemLanguage;
|
||||
|
||||
else if (navigator.userLanguage)
|
||||
return navigator.userLanguage;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
defaultFontSize = getDefaultFontSize();
|
||||
|
||||
// Set field values
|
||||
this.values.kind = 'pageview';
|
||||
this.values.timediff = (new Date().getTimezoneOffset()/60)*(-1); // *timezone* diff from UTC
|
||||
this.values.locale = get_locale();
|
||||
this.values.referrer = (document.referrer == '' ? '-' : document.referrer);
|
||||
|
||||
if (loadTime)
|
||||
this.values.loadtime = loadTime;
|
||||
|
||||
if (navToDoneTime)
|
||||
this.values.nav_to_done_ms = navToDoneTime;
|
||||
|
||||
if (settings.trackingId) {
|
||||
this.values.ga_tid = settings.trackingId;
|
||||
}
|
||||
|
||||
/* START CUSTOM DIMENSIONS */
|
||||
if (defaultFontSize)
|
||||
this.values.iaprop_fontSize = defaultFontSize;
|
||||
|
||||
if ('devicePixelRatio' in window)
|
||||
this.values.iaprop_devicePixelRatio = window.devicePixelRatio;
|
||||
|
||||
if (mediaType)
|
||||
this.values.iaprop_mediaType = mediaType;
|
||||
|
||||
if (settings.mediaLanguage) {
|
||||
this.values.iaprop_mediaLanguage = settings.mediaLanguage;
|
||||
}
|
||||
|
||||
if (primaryCollection) {
|
||||
this.values.iaprop_primaryCollection = primaryCollection;
|
||||
}
|
||||
/* END CUSTOM DIMENSIONS */
|
||||
|
||||
if (page)
|
||||
this.values.page = page;
|
||||
|
||||
this.send_ping(this.values);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends a tracking "Event".
|
||||
* @param {string} category
|
||||
* @param {string} action
|
||||
* @param {string} label
|
||||
* @param {Object} additionalEventParams
|
||||
*/
|
||||
send_event: function send_event(
|
||||
category,
|
||||
action,
|
||||
label,
|
||||
additionalEventParams
|
||||
) {
|
||||
if (!label) label = window.location.pathname;
|
||||
if (!additionalEventParams) additionalEventParams = {};
|
||||
if (additionalEventParams.mediaLanguage) {
|
||||
additionalEventParams.ga_cd4 = additionalEventParams.mediaLanguage;
|
||||
delete additionalEventParams.mediaLanguage;
|
||||
}
|
||||
var eventParams = Object.assign(
|
||||
{
|
||||
kind: 'event',
|
||||
ec: category,
|
||||
ea: action,
|
||||
el: label,
|
||||
cache_bust: Math.random(),
|
||||
},
|
||||
additionalEventParams
|
||||
);
|
||||
this.send_ping(eventParams);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends every event instead of a small percentage.
|
||||
*
|
||||
* Use this sparingly as it can generate a lot of events.
|
||||
*
|
||||
* @param {string} category
|
||||
* @param {string} action
|
||||
* @param {string} label
|
||||
* @param {Object} additionalEventParams
|
||||
*/
|
||||
send_event_no_sampling: function send_event_no_sampling(
|
||||
category,
|
||||
action,
|
||||
label,
|
||||
additionalEventParams
|
||||
) {
|
||||
var extraParams = additionalEventParams || {};
|
||||
extraParams.service = NO_SAMPLING_SERVICE;
|
||||
this.send_event(category, action, label, extraParams);
|
||||
},
|
||||
|
||||
/**
|
||||
* @param {Object} options see this.send_pageview options
|
||||
*/
|
||||
send_pageview_on_load: function send_pageview_on_load(options) {
|
||||
var self = this;
|
||||
window.addEventListener('load', function send_pageview_with_options() {
|
||||
self.send_pageview(options);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles tracking events passed in URL.
|
||||
* Assumes category and action values are separated by a "|" character.
|
||||
* NOTE: Uses the unsampled analytics property. Watch out for future high click links!
|
||||
* @param {Location}
|
||||
*/
|
||||
process_url_events: function process_url_events(location) {
|
||||
var eventValues;
|
||||
var actionValue;
|
||||
var eventValue = getParams(location).iax;
|
||||
if (!eventValue) return;
|
||||
eventValues = eventValue.split('|');
|
||||
actionValue = eventValues.length >= 1 ? eventValues[1] : '';
|
||||
this.send_event_no_sampling(
|
||||
eventValues[0],
|
||||
actionValue,
|
||||
window.location.pathname
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* Attaches handlers for event tracking.
|
||||
*
|
||||
* To enable click tracking for a link, add a `data-event-click-tracking`
|
||||
* attribute containing the Google Analytics Event Category and Action, separated
|
||||
* by a vertical pipe (|).
|
||||
* e.g. `<a href="foobar" data-event-click-tracking="TopNav|FooBar">`
|
||||
*
|
||||
* To enable form submit tracking, add a `data-event-form-tracking` attribute
|
||||
* to the `form` tag.
|
||||
* e.g. `<form data-event-form-tracking="TopNav|SearchForm" method="GET">`
|
||||
*
|
||||
* Additional tracking options can be added via a `data-event-tracking-options`
|
||||
* parameter. This parameter, if included, should be a JSON string of the parameters.
|
||||
* Valid parameters are:
|
||||
* - service {string}: Corresponds to the Google Analytics property data values flow into
|
||||
*/
|
||||
set_up_event_tracking: function set_up_event_tracking() {
|
||||
var self = this;
|
||||
var clickTrackingAttributeName = 'event-click-tracking';
|
||||
var formTrackingAttributeName = 'event-form-tracking';
|
||||
var trackingOptionsAttributeName = 'event-tracking-options';
|
||||
|
||||
function handleAction(event, attributeName) {
|
||||
var selector = '[data-' + attributeName + ']';
|
||||
var eventTarget = event.target;
|
||||
if (!eventTarget) return;
|
||||
var target = eventTarget.closest(selector);
|
||||
if (!target) return;
|
||||
var categoryAction;
|
||||
var categoryActionParts;
|
||||
var options;
|
||||
categoryAction = target.dataset[toCamelCase(attributeName)];
|
||||
if (!categoryAction) return;
|
||||
categoryActionParts = categoryAction.split('|');
|
||||
options = target.dataset[toCamelCase(trackingOptionsAttributeName)];
|
||||
options = options ? JSON.parse(options) : {};
|
||||
self.send_event(
|
||||
categoryActionParts[0],
|
||||
categoryActionParts[1],
|
||||
categoryActionParts[2] || window.location.pathname,
|
||||
options.service ? { service: options.service } : {}
|
||||
);
|
||||
}
|
||||
|
||||
function toCamelCase(str) {
|
||||
return str.replace(/\W+(.)/g, function (match, chr) {
|
||||
return chr.toUpperCase();
|
||||
});
|
||||
};
|
||||
|
||||
document.addEventListener('click', function(e) {
|
||||
handleAction(e, clickTrackingAttributeName);
|
||||
});
|
||||
|
||||
document.addEventListener('submit', function(e) {
|
||||
handleAction(e, formTrackingAttributeName);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* @returns {Object[]}
|
||||
*/
|
||||
get_data_packets: function get_data_packets() {
|
||||
return [this.values];
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates a tracking image for tracking JS compatibility.
|
||||
*
|
||||
* @param {string} type The type value for track_js_case in query params for 0.gif
|
||||
*/
|
||||
create_tracking_image: function create_tracking_image(type) {
|
||||
this.send_ping_via_image({
|
||||
cache_bust: Math.random(),
|
||||
kind: 'track_js',
|
||||
track_js_case: type,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return ArchiveAnalytics;
|
||||
}());
|
||||
// @license-end
|
||||
Reference in New Issue
Block a user