Files
aladin-lite/src/js/Utils.js
2019-12-05 09:51:58 +01:00

314 lines
9.5 KiB
JavaScript

// Copyright 2013 - UDS/CNRS
// The Aladin Lite program is distributed under the terms
// of the GNU General Public License version 3.
//
// This file is part of Aladin Lite.
//
// Aladin Lite is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 3 of the License.
//
// Aladin Lite is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// The GNU General Public License is available in COPYING file
// along with Aladin Lite.
//
/******************************************************************************
* Aladin Lite project
*
* File Utils
*
* Author: Thomas Boch[CDS]
*
*****************************************************************************/
Utils = Utils || {};
Utils.cssScale = undefined;
// adding relMouseCoords to HTMLCanvasElement prototype (see http://stackoverflow.com/questions/55677/how-do-i-get-the-coordinates-of-a-mouse-click-on-a-canvas-element )
function relMouseCoords(event) {
var totalOffsetX = 0;
var totalOffsetY = 0;
var canvasX = 0;
var canvasY = 0;
var currentElement = this;
if (event.offsetX) {
return {x: event.offsetX, y:event.offsetY};
}
else {
if (!Utils.cssScale) {
var st = window.getComputedStyle(document.body, null);
var tr = st.getPropertyValue("-webkit-transform") ||
st.getPropertyValue("-moz-transform") ||
st.getPropertyValue("-ms-transform") ||
st.getPropertyValue("-o-transform") ||
st.getPropertyValue("transform");
var matrixRegex = /matrix\((-?\d*\.?\d+),\s*0,\s*0,\s*(-?\d*\.?\d+),\s*0,\s*0\)/;
var matches = tr.match(matrixRegex);
if (matches) {
Utils.cssScale = parseFloat(matches[1]);
}
else {
Utils.cssScale = 1;
}
}
var e = event;
var canvas = e.target;
// http://www.jacklmoore.com/notes/mouse-position/
var target = e.target || e.srcElement;
var style = target.currentStyle || window.getComputedStyle(target, null);
var borderLeftWidth = parseInt(style['borderLeftWidth'], 10);
var borderTopWidth = parseInt(style['borderTopWidth'], 10);
var rect = target.getBoundingClientRect();
var clientX = e.clientX;
var clientY = e.clientY;
if (e.clientX == undefined) {
clientX = e.originalEvent.changedTouches[0].clientX;
clientY = e.originalEvent.changedTouches[0].clientY;
}
var offsetX = clientX - borderLeftWidth - rect.left;
var offsetY = clientY - borderTopWidth - rect.top
return {x: parseInt(offsetX/Utils.cssScale), y: parseInt(offsetY/Utils.cssScale)};
}
}
HTMLCanvasElement.prototype.relMouseCoords = relMouseCoords;
//Function.prototype.bind polyfill from
//https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
if (!Function.prototype.bind) {
Function.prototype.bind = function (obj) {
// closest thing possible to the ECMAScript 5 internal IsCallable function
if (typeof this !== 'function') {
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
var slice = [].slice,
args = slice.call(arguments, 1),
self = this,
nop = function () { },
bound = function () {
return self.apply(this instanceof nop ? this : (obj || {}),
args.concat(slice.call(arguments)));
};
bound.prototype = this.prototype;
return bound;
};
}
$ = $ || jQuery;
/* source : http://stackoverflow.com/a/8764051 */
$.urlParam = function(name, queryString){
if (queryString===undefined) {
queryString = location.search;
}
return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(queryString)||[,""])[1].replace(/\+/g, '%20'))||null;
};
/* source: http://stackoverflow.com/a/1830844 */
Utils.isNumber = function(n) {
return !isNaN(parseFloat(n)) && isFinite(n);
};
Utils.isInt = function(n) {
return Utils.isNumber(n) && Math.floor(n)==n;
};
/* a debounce function, used to prevent multiple calls to the same function if less than delay milliseconds have passed */
Utils.debounce = function(fn, delay) {
var timer = null;
return function () {
var context = this, args = arguments;
clearTimeout(timer);
timer = setTimeout(function () {
fn.apply(context, args);
}, delay);
};
};
/* return a throttled function, to rate limit the number of calls (by default, one call every 250 milliseconds) */
Utils.throttle = function(fn, threshhold, scope) {
threshhold || (threshhold = 250);
var last,
deferTimer;
return function () {
var context = scope || this;
var now = +new Date,
args = arguments;
if (last && now < last + threshhold) {
// hold on to it
clearTimeout(deferTimer);
deferTimer = setTimeout(function () {
last = now;
fn.apply(context, args);
}, threshhold);
} else {
last = now;
fn.apply(context, args);
}
};
}
/* A LRU cache, inspired by https://gist.github.com/devinus/409353#file-gistfile1-js */
// TODO : utiliser le LRU cache pour les tuiles images
Utils.LRUCache = function (maxsize) {
this._keys = [];
this._items = {};
this._expires = {};
this._size = 0;
this._maxsize = maxsize || 1024;
};
Utils.LRUCache.prototype = {
set: function (key, value) {
var keys = this._keys,
items = this._items,
expires = this._expires,
size = this._size,
maxsize = this._maxsize;
if (size >= maxsize) { // remove oldest element when no more room
keys.sort(function (a, b) {
if (expires[a] > expires[b]) return -1;
if (expires[a] < expires[b]) return 1;
return 0;
});
size--;
delete expires[keys[size]];
delete items[keys[size]];
}
keys[size] = key;
items[key] = value;
expires[key] = Date.now();
size++;
this._keys = keys;
this._items = items;
this._expires = expires;
this._size = size;
},
get: function (key) {
var item = this._items[key];
if (item) this._expires[key] = Date.now();
return item;
},
keys: function() {
return this._keys;
}
};
////////////////////////////////////////////////////////////////////////////:
/**
Make an AJAX call, given a list of potential mirrors
First successful call will result in options.onSuccess being called back
If all calls fail, onFailure is called back at the end
This method assumes the URL are CORS-compatible, no proxy will be used
*/
Utils.loadFromMirrors = function(urls, options) {
var data = options && options.data || null;
var method = options && options.method || 'GET';
var dataType = options && options.dataType || null;
var timeout = options && options.timeout || 20;
var onSuccess = options && options.onSuccess || null;
var onFailure = options && options.onFailure || null;
if (urls.length === 0) {
(typeof onFailure === 'function') && onFailure();
}
else {
var ajaxOptions = {
url: urls[0],
data: data
}
if (dataType) {
ajaxOptions.dataType = dataType;
}
$.ajax(ajaxOptions)
.done(function(data) {
(typeof onSuccess === 'function') && onSuccess(data);
})
.fail(function() {
Utils.loadFromMirrors(urls.slice(1), options);
});
}
}
// return the jquery ajax object configured with the requested parameters
// by default, we use the proxy (safer, as we don't know if the remote server supports CORS)
Utils.getAjaxObject = function(url, method, dataType, useProxy) {
if (useProxy!==false) {
useProxy = true;
}
if (useProxy===true) {
var urlToRequest = Aladin.JSONP_PROXY + '?url=' + encodeURIComponent(url);
}
else {
urlToRequest = url;
}
method = method || 'GET';
dataType = dataType || null;
return $.ajax({
url: urlToRequest,
method: method,
dataType: dataType
});
};
// return true if script is executed in a HTTPS context
// return false otherwise
Utils.isHttpsContext = function() {
return ( window.location.protocol === 'https:' );
};
// generate an absolute URL from a relative URL
// example: getAbsoluteURL('foo/bar/toto') return http://cds.unistra.fr/AL/foo/bar/toto if executed from page http://cds.unistra.fr/AL/
Utils.getAbsoluteURL = function(url) {
var a = document.createElement('a');
a.href = url;
return a.href;
};
// generate a valid v4 UUID
Utils.uuidv4 = function() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}