@amcharts/amcharts4
Version:
amCharts 4
1,118 lines • 33.8 kB
JavaScript
/**
* A collection of universal utility functions.
*/
import { Percent } from "./Percent";
import { isSafari } from "./Browser";
import * as $math from "../utils/Math";
import * as $type from "../utils/Type";
import * as $string from "./String";
import * as $strings from "./Strings";
import * as $object from "./Object";
import * as $array from "./Array";
/**
* ============================================================================
* MISC FUNCTIONS
* ============================================================================
* @hidden
*/
/**
* Marks a value as being used (e.g. because the value has side effects).
*/
export function used(value) { }
/**
* Copies all properties of one object to the other, omitting undefined.
*
* @param fromObject Source object
* @param toObject Target object
* @return Updated target object
* @todo Maybe consolidate with utils.copy?
*/
export function copyProperties(source, target) {
$object.each(source, function (key, value) {
// only if value is set
if ($type.hasValue(value)) {
target[key] = value;
}
});
return target;
}
/**
* Removes target from url
*/
export function stripHash(url) {
return /^[^#]*/.exec(url)[0];
}
export function getBaseURI() {
var url = "#";
// Needed until https://bugs.webkit.org/show_bug.cgi?id=189499 is fixed
if (isSafari()) {
var baseURI = document.baseURI;
if (baseURI) {
baseURI = stripHash(baseURI);
var loc = stripHash(location.href);
if (baseURI !== loc) {
url = loc + url;
}
}
}
return url;
}
/**
* Copies all properties of one object to the other, omitting undefined, but only if property in target object doesn't have a value set.
*
* @param fromObject Source object
* @param toObject Target object
* @return Updated target object
* @todo Maybe consolidate with utils.copy?
*/
export function softCopyProperties(source, target) {
$object.each(source, function (key, value) {
// only if value is set
if ($type.hasValue(value) && !($type.hasValue(target[key]))) {
target[key] = value;
}
});
return target;
}
/**
* Copies all properties of one object to the other.
*
* @param source Source object
* @param recipient Target object
* @return Updated target object
*/
export function copy(source, target) {
$object.each(source, function (key, value) {
target[key] = value;
});
return target;
}
/**
* Checks if value is not empty (undefined or zero-length string).
*
* @param value Value to check
* @return `true` if value is not "empty"
*/
export function isNotEmpty(value) {
return $type.hasValue(value) && (value.toString() !== "");
}
/**
* [relativeToValue description]
*
* @ignore Exclude from docs
* @todo Description
* @param percent [description]
* @param full [description]
* @return [description]
*/
export function relativeToValue(percent, full) {
if ($type.isNumber(percent)) {
return percent;
}
else if (percent != null && $type.isNumber(percent.value) && $type.isNumber(full)) {
return full * percent.value;
}
else {
return 0;
}
}
/**
* [relativeRadiusToValue description]
*
* Differs from relativeToValue so that if a value is negative, it subtracts
* it from full value.
*
* @ignore Exclude from docs
* @todo Description
* @param percent [description]
* @param full [description]
* @param subtractIfNegative [description]
* @return [description]
*/
export function relativeRadiusToValue(percent, full, subtractIfNegative) {
var value;
if ($type.isNumber(percent)) {
value = percent;
if (value < 0) {
if (subtractIfNegative) {
value = full + value;
}
else {
value = full - value;
}
}
}
else if (percent != null && $type.isNumber(percent.value)) {
value = full * percent.value;
}
return value;
}
/**
* [valueToRelative description]
*
* @ignore Exclude from docs
* @todo Description
* @param value [description]
* @param full [description]
* @return [description]
*/
export function valueToRelative(value, full) {
if (value instanceof Percent) {
return value.value;
}
else {
return value / full;
}
}
/**
* Returns pixel ratio of the current screen (used on retina displays).
*
* @return Pixel ratio
*/
export function getPixelRatio() {
var ratio = window.devicePixelRatio || 1;
return ratio;
}
/**
* ============================================================================
* STRING FORMATTING FUNCTIONS
* ============================================================================
* @hidden
*/
/**
* Converts camelCased text to dashed version:
* ("thisIsString" > "this-is-string")
*
* @param str Input
* @return Output
*/
export function camelToDashed(str) {
return str.replace(/\W+/g, '-').replace(/([a-z\d])([A-Z])/g, '$1-$2').toLowerCase();
}
/**
* Converts tring to uppercase.
*
* @param str String to convert
* @return uppercased string
* @todo Maybe make it better
*/
export function capitalize(str) {
var arr = str.split("");
arr[0] = arr[0].toUpperCase();
return arr.join("");
}
/**
* Converts any value into its string representation.
*
* @param value Value
* @return String represantation of the value
*/
export function stringify(value) {
return JSON.stringify(value);
}
/**
* Escapes string so it can safely be used in a Regex.
*
* @param value Unsescaped string
* @return Escaped string
*/
export function escapeForRgex(value) {
return value.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
}
/**
* Splits the text into multiple lines, respecting maximum character count.
* Prioretizes splitting on spaces and punctuation. Falls back on splitting
* mid-word if there's no other option.
*
* @param text Text
* @param maxChars Maximum number of characters per line
* @return An array of split text
*/
export function splitTextByCharCount(text, maxChars, fullWords, rtl, fullWordFallback) {
// Maybe the text fits?
if (text.length <= maxChars) {
return [text];
}
// Init result
var res = [];
// Split by words or by charts
if (fullWords) {
// Split by words first
// Split by spacing
var currentIndex = -1;
//let tmpText = text.replace(/([,;:!?\\\/\.]+[\s]+|[\s])/g, $strings.PLACEHOLDER + "$1" + $strings.PLACEHOLDER);
var tmpText = text.replace(/([,;:!?\\\/]+|[\s])/g, $strings.PLACEHOLDER + "$1" + $strings.PLACEHOLDER);
var words = tmpText.split($strings.PLACEHOLDER);
// Glue end-of-word punctuation to the word itself
for (var i = 1; i < words.length; i++) {
var word = words[i];
if ((word == "." || word == ",") && words[i - 1].match(/[\w]+$/)) {
words[i - 1] += word;
words[i] = "";
}
}
// Process each word
for (var i = 0; i < words.length; i++) {
// Get word and symbol count
var word = words[i];
var wordLength = word.length;
// Ignore empty words
if (wordLength === 0) {
continue;
}
// Check word length
if ((wordLength > maxChars) && (fullWords !== true || fullWordFallback != false)) {
//if (wordLength > maxChars) {
// A single word is longer than allowed symbol count
// Break it up
if (rtl) {
word = reverseString(word);
}
var parts = word.match(new RegExp(".{1," + maxChars + "}", "g"));
// TODO is this correct ?
if (parts) {
if (rtl) {
for (var x = 0; x < parts.length; x++) {
parts[x] = reverseString(parts[x]);
}
//parts.reverse();
}
res = res.concat(parts);
}
}
else {
// Init current line
if (currentIndex === -1) {
res.push("");
currentIndex = 0;
}
// Check if we need to break into another line
if (((res[currentIndex].length + wordLength + 1) > maxChars) && res[currentIndex] !== "") {
res.push("");
currentIndex++;
}
// Add word
res[currentIndex] += word;
}
// Update index
currentIndex = res.length - 1;
}
}
else {
// Splitting by anywhere (living la vida facil)
var parts = text.match(new RegExp(".{1," + maxChars + "}", "g"));
if (parts) {
if (rtl) {
for (var x = 0; x < parts.length; x++) {
if (!rtl) {
parts[x] = reverseString(parts[x]);
}
}
}
res = parts;
}
}
// Do we have only one word that does not fit?
// Since fullWords is set and we can't split the word, we end up with empty
// set.
if (res.length == 1 && fullWords && fullWordFallback && (res[0].length > maxChars)) {
res = [];
}
return res;
}
/**
* Truncates the text to certain character count.
*
* Will add ellipsis if the string is truncated. Optionally, can truncate on
* full words only.
*
* For RTL support, pass in the fifth parameter as `true`.
*
* @param text Input text
* @param maxChars Maximum character count of output
* @param ellipsis Ellipsis string, i.e. "..."
* @param fullWords If `true`, will not break mid-word, unless there's a single word and it does not with into `maxChars`
* @param rtl Is this an RTL text?
* @return Truncated text
*/
export function truncateWithEllipsis(text, maxChars, ellipsis, fullWords, rtl) {
if (text.length <= maxChars) {
return text;
}
// Calc max chars
maxChars -= ellipsis.length;
if (maxChars < 1) {
maxChars = 1;
//ellipsis = "";
}
// Get lines
var lines = splitTextByCharCount(text, maxChars, fullWords, rtl);
// Use first line
return (lines[0] || "") + ellipsis;
}
/**
* Removes whitespace from beginning and end of the string.
*
* @param str Input
* @return Output
*/
export function trim(str) {
return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, "");
}
;
/**
* Removes whitespace from end of the string.
*
* @param str Input
* @return Output
*/
export function rtrim(str) {
return str.replace(/[\s\uFEFF\xA0]+$/g, "");
}
;
/**
* Removes whitespace from beginning of the string.
*
* @param str Input
* @return Output
*/
export function ltrim(str) {
return str.replace(/^[\s\uFEFF\xA0]+/g, "");
}
;
/**
* Reverses string.
*
* @param str Input
* @return Output
*/
export function reverseString(str) {
return str.split("").reverse().join("");
}
/**
* Removes quotes from the string.
*
* @param str Input
* @return Output
*/
export function unquote(str) {
var res = str.trim();
res = str.replace(/^'(.*)'$/, "$1");
if (res == str) {
res = str.replace(/^"(.*)"$/, "$1");
}
return res;
}
/**
* Pads a string with additional characters to certain length.
*
* @param value A numeric value
* @param len Result string length in characters
* @param char A character to use for padding
* @return Padded value as string
*/
export function padString(value, len, char) {
if (len === void 0) { len = 0; }
if (char === void 0) { char = "0"; }
if (typeof value !== "string") {
value = value.toString();
}
return len > value.length ? Array(len - value.length + 1).join(char) + value : value;
}
/**
* Tries to determine format type.
*
* @ignore Exclude from docs
* @param format Format string
* @return Format type ("string" | "number" | "date" | "duration")
*/
export function getFormat(format) {
// Undefined?
if (typeof format === "undefined") {
return $strings.STRING;
}
// Cleanup and lowercase format
format = format.toLowerCase().replace(/^\[[^\]]*\]/, "");
// Remove style tags
format = format.replace(/\[[^\]]+\]/, "");
// Trim
format = format.trim();
// Check for any explicit format hints (i.e. /Date)
var hints = format.match(/\/(date|number|duration)$/);
if (hints) {
return hints[1];
}
// Check for explicit hints
if (format === $strings.NUMBER) {
return $strings.NUMBER;
}
if (format === $strings.DATE) {
return $strings.DATE;
}
if (format === $strings.DURATION) {
return $strings.DURATION;
}
// Detect number formatting symbols
if (format.match(/[#0]/)) {
return $strings.NUMBER;
}
// Detect date formatting symbols
if (format.match(/[ymwdhnsqaxkzgtei]/)) {
return $strings.DATE;
}
// Nothing? Let's display as string
return $strings.STRING;
}
/**
* Cleans up format:
* * Strips out formatter hints
*
* @ignore Exclude from docs
* @param format Format
* @return Cleaned format
*/
export function cleanFormat(format) {
return format.replace(/\/(date|number|duration)$/i, "");
}
/**
* Strips all tags from the string.
*
* @param text Source string
* @return String without tags
*/
export function stripTags(text) {
return text ? text.replace(/<[^>]*>/g, "") : text;
}
/**
* Removes new lines and tags from a string.
*
* @param text String to conver
* @return Converted string
*/
export function plainText(text) {
return text ? stripTags(("" + text).replace(/[\n\r]+/g, ". ")) : text;
}
/**
* ============================================================================
* TYPE CONVERSION FUNCTIONS
* ============================================================================
* @hidden
*/
/**
* Converts numeric value into string. Deals with large or small numbers that
* would otherwise use exponents.
*
* @param value Numeric value
* @return Numeric value as string
*/
export function numberToString(value) {
// TODO handle Infinity and -Infinity
if ($type.isNaN(value)) {
return "NaN";
}
if (value === Infinity) {
return "Infinity";
}
if (value === -Infinity) {
return "-Infinity";
}
// Negative 0
if ((value === 0) && (1 / value === -Infinity)) {
return "-0";
}
// Preserve negative and deal with absoute values
var negative = value < 0;
value = Math.abs(value);
// TODO test this
var parsed = $type.getValue(/^([0-9]+)(?:\.([0-9]+))?(?:e[\+\-]([0-9]+))?$/.exec("" + value));
var digits = parsed[1];
var decimals = parsed[2] || "";
var res;
// Leave the nummber as it is if it does not use exponents
if (parsed[3] == null) {
res = (decimals === "" ? digits : digits + "." + decimals);
}
else {
var exponent = +parsed[3];
// Deal with decimals
if (value < 1) {
var zeros = exponent - 1;
res = "0." + $string.repeat("0", zeros) + digits + decimals;
// Deal with integers
}
else {
var zeros = exponent - decimals.length;
if (zeros === 0) {
res = digits + decimals;
}
else if (zeros < 0) {
res = digits + decimals.slice(0, zeros) + "." + decimals.slice(zeros);
}
else {
res = digits + decimals + $string.repeat("0", zeros);
}
}
}
return negative ? "-" + res : res;
}
/**
* Converts anything to Date object.
*
* @param value A value of any type
* @return Date object representing a value
*/
export function anyToDate(value) {
if ($type.isDate(value)) {
// TODO maybe don't create a new Date ?
var date = new Date(value);
// This is needed because IE does not copy over milliseconds
date.setMilliseconds(value.getMilliseconds());
return date;
}
else if ($type.isNumber(value)) {
return new Date(value);
}
else {
// Try converting to number (assuming timestamp)
var num = Number(value);
if (!$type.isNumber(num)) {
return new Date(value);
}
else {
return new Date(num);
}
}
}
/**
* Tries converting any value to a number.
*
* @param value Source value
* @return Number
*/
export function anyToNumber(value) {
if ($type.isDate(value)) {
return value.getTime();
}
else if ($type.isNumber(value)) {
return value;
}
else if ($type.isString(value)) {
// Try converting to number (assuming timestamp)
var num = Number(value);
if (!$type.isNumber(num)) {
// Failing
return undefined;
}
else {
return num;
}
}
}
/**
* ============================================================================
* DATE-RELATED FUNCTIONS
* ============================================================================
* @hidden
*/
/**
* Returns a year day.
*
* @param date Date
* @param utc Assume UTC dates?
* @return Year day
* @todo Account for UTC
*/
export function getYearDay(date, utc) {
if (utc === void 0) { utc = false; }
var start = new Date(date.getFullYear(), 0, 0);
var diff = (date.getTime() - start.getTime()) + ((start.getTimezoneOffset() - date.getTimezoneOffset()) * 60 * 1000);
var oneDay = 1000 * 60 * 60 * 24;
return Math.floor(diff / oneDay);
}
/**
* Returns week number for a given date.
*
* @param date Date
* @param utc Assume UTC dates?
* @return Week number
* @todo Account for UTC
*/
export function getWeek(date, _utc) {
if (_utc === void 0) { _utc = false; }
var d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
var day = d.getUTCDay() || 7;
d.setUTCDate(d.getUTCDate() + 4 - day);
var firstDay = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
return Math.ceil((((d.getTime() - firstDay.getTime()) / 86400000) + 1) / 7);
}
/**
* Returns a week number in the month.
*
* @param date Source Date
* @param utc Assume UTC dates?
* @return Week number in month
*/
export function getMonthWeek(date, utc) {
if (utc === void 0) { utc = false; }
var firstWeek = getWeek(new Date(date.getFullYear(), date.getMonth(), 1), utc);
var currentWeek = getWeek(date, utc);
if (currentWeek == 1) {
currentWeek = 53;
}
return currentWeek - firstWeek + 1;
}
/**
* Returns a year day out of the given week number.
*
* @param week Week
* @param year Year
* @param weekday Weekday
* @param utc Assume UTC dates
* @return Day in a year
*/
export function getDayFromWeek(week, year, weekday, utc) {
if (weekday === void 0) { weekday = 1; }
if (utc === void 0) { utc = false; }
var date = new Date(year, 0, 4, 0, 0, 0, 0);
if (utc) {
date.setUTCFullYear(year);
}
var day = week * 7 + weekday - ((date.getDay() || 7) + 3);
return day;
}
/**
* Returns 12-hour representation out of the 24-hour hours.
*
* @param hours 24-hour number
* @return 12-hour number
*/
export function get12Hours(hours, base) {
if (hours > 12) {
hours -= 12;
}
else if (hours === 0) {
hours = 12;
}
return $type.hasValue(base) ? hours + (base - 1) : hours;
}
/**
* Returns a string name of the tome zone.
*
* @param date Date object
* @param long Should return long ("Pacific Standard Time") or short abbreviation ("PST")
* @param savings Include information if it's in daylight savings mode
* @param utc Assume UTC dates
* @return Time zone name
*/
export function getTimeZone(date, long, savings, utc) {
if (long === void 0) { long = false; }
if (savings === void 0) { savings = false; }
if (utc === void 0) { utc = false; }
if (utc) {
return long ? "Coordinated Universal Time" : "UTC";
}
var wotz = date.toLocaleString("UTC");
var wtz = date.toLocaleString("UTC", { timeZoneName: long ? "long" : "short" }).substr(wotz.length);
//wtz = wtz.replace(/[+-]+[0-9]+$/, "");
if (savings === false) {
wtz = wtz.replace(/ (standard|daylight|summer|winter) /i, " ");
}
return wtz;
}
/**
* ============================================================================
* NUMBER-RELATED FUNCTIONS
* ============================================================================
* @hidden
*/
/**
* Returns a random number between `from` and `to`.
*
* @param from From number
* @param to To number
* @return Random number
*/
export function random(from, to) {
return Math.floor(Math.random() * (to - from)) + from;
}
/**
* Fits the number into specific `min` and `max` bounds.
*
* @param value Input value
* @param min Minimum value
* @param max Maximum value
* @return Possibly adjusted value
*/
export function fitNumber(value, min, max) {
if (value > max) {
return max;
}
else if (value < min) {
return min;
}
return value;
}
/**
* Fits the number into specific `min` and `max` bounds.
*
* If the value is does not fit withing specified range, it "wraps" around the
* values.
*
* For example, if we have input value 10 with min set at 1 and max set at 8,
* the value will not fit. The remainder that does not fit (2) will be added
* to `min`, resulting in 3.
*
* The output of regular `fitNumber()` would return 8 instead.
*
* @param value Input value
* @param min Minimum value
* @param max Maximum value
* @return Possibly adjusted value
*/
export function fitNumberRelative(value, min, max) {
var gap = max - min;
if (value > max) {
value = min + (value - gap * Math.floor(value / gap));
}
else if (value < min) {
value = min + (value - gap * Math.floor(value / gap));
}
return value;
}
/**
* ============================================================================
* SPRITE-RELATED FUNCTIONS
* ============================================================================
* @hidden
*/
/**
* Converts SVG element coordinates to coordinates within specific [[Sprite]].
*
* @param point SVG coordinates
* @param sprite Sprite
* @return Sprite coordinates
*/
export function svgPointToSprite(point, sprite) {
var x = point.x;
var y = point.y;
var sprites = [];
if (sprite) {
while ($type.hasValue(sprite.parent)) {
sprites.push(sprite);
sprite = sprite.parent;
}
sprites.reverse();
for (var i = 0; i < sprites.length; i++) {
var sprite_1 = sprites[i];
var angle = sprite_1.rotation;
var relativeX = x - sprite_1.pixelX - sprite_1.ex;
var relativeY = y - sprite_1.pixelY - sprite_1.ey;
if (sprite_1.dx) {
x -= sprite_1.dx;
}
if (sprite_1.dy) {
y -= sprite_1.dy;
}
var scale = sprite_1.scale;
// this handles nonscaling
if (sprite_1.group) {
scale = sprite_1.group.scale;
}
x = ($math.cos(-angle) * relativeX - $math.sin(-angle) * relativeY) / scale - sprite_1.pixelPaddingLeft;
y = ($math.cos(-angle) * relativeY + $math.sin(-angle) * relativeX) / scale - sprite_1.pixelPaddingTop;
}
}
return { x: x, y: y };
}
/**
* Converts coordinates within [[Sprite]] to coordinates relative to the whole
* SVG element.
*
* @param point Sprite coordinates
* @param sprite Sprite
* @return SVG coordinates
*/
export function spritePointToSvg(point, sprite) {
var x = point.x;
var y = point.y;
if (sprite) {
while ($type.hasValue(sprite.parent)) {
var angle = sprite.rotation;
x += sprite.pixelPaddingLeft + sprite.ex;
y += sprite.pixelPaddingTop + sprite.ey;
var scale = sprite.scale;
// this handles nonscaling
if (sprite.group) {
scale = sprite.group.scale;
}
var relativeX = sprite.pixelX + ((x * $math.cos(angle) - y * $math.sin(angle))) * scale;
var relativeY = sprite.pixelY + ((x * $math.sin(angle) + y * $math.cos(angle))) * scale;
if (sprite.dx) {
relativeX += sprite.dx;
}
if (sprite.dy) {
relativeY += sprite.dy;
}
x = relativeX;
y = relativeY;
sprite = sprite.parent;
}
}
return { x: x, y: y };
}
/**
* Converts coordinates of one sprite to another.
*
* @param point Sprite coordinates
* @param sprite Sprite
* @param toSprite Sprite
* @return converted coordinates
*/
export function spritePointToSprite(point, sprite, toSprite) {
return svgPointToSprite(spritePointToSvg(point, sprite), toSprite);
}
/**
* Converts a rectangle expressed in SVG element coordinates to coordinates
* within specific [[Sprite]].
*
* @param rect SVG rectangle
* @param sprite Sprite
* @return Sprite rectangle
*/
export function svgRectToSprite(rect, sprite) {
var p1 = svgPointToSprite(rect, sprite);
var p2 = svgPointToSprite({ x: rect.x + rect.width, y: rect.y + rect.height }, sprite);
return { x: p1.x, y: p1.y, width: p2.x - p1.x, height: p2.y - p1.y };
}
/**
* Converts a rectangle expressed in [[Sprite]] coordinates to SVG coordinates.
*
* @param rect Sprite rectangle
* @param sprite Sprite
* @return SVG rectangle
*/
export function spriteRectToSvg(rect, sprite) {
var p1 = spritePointToSvg(rect, sprite);
var p2 = spritePointToSvg({ x: rect.x + rect.width, y: rect.y + rect.height }, sprite);
return { x: p1.x, y: p1.y, width: p2.x - p1.x, height: p2.y - p1.y };
}
/**
* Converts global document-wide coordinates to coordinates within SVG element.
*
* @param point Global coordinates
* @param svgContainer SVG element
* @return SVG coordinates
*/
export function documentPointToSvg(point, svgContainer, cssScale) {
try {
var bbox = svgContainer.getBoundingClientRect();
if (!$type.isNumber(cssScale)) {
cssScale = 1;
}
return {
"x": (point.x - bbox.left) / cssScale,
"y": (point.y - bbox.top) / cssScale
};
}
catch (e) {
return point;
}
}
/**
* Converts SVG coordinates to global document-wide coordinates.
*
* @param point SVG coordinates
* @param svgContainer SVG element
* @return Global coordinates
*/
export function svgPointToDocument(point, svgContainer) {
try {
var bbox = svgContainer.getBoundingClientRect();
return {
"x": point.x + bbox.left,
"y": point.y + bbox.top
};
}
catch (e) {
return point;
}
}
/**
* Converts document-wide global coordinates to coordinates within specific
* [[Sprite]].
*
* @param point Global coordinates
* @param sprite Sprite
* @return Sprite coordinates
*/
export function documentPointToSprite(point, sprite) {
if (sprite && sprite.htmlContainer) {
var svgPoint = documentPointToSvg(point, $type.getValue(sprite.htmlContainer), sprite.svgContainer.cssScale);
return svgPointToSprite(svgPoint, sprite);
}
else {
return point;
}
}
/**
* Converts coordinates within [[Sprite]] to global document coordinates.
*
* @param point Sprite coordinates
* @param sprite Sprite
* @return Global coordinates
*/
export function spritePointToDocument(point, sprite) {
if (sprite && sprite.htmlContainer) {
var svgPoint = spritePointToSvg(point, sprite);
return svgPointToDocument(svgPoint, $type.getValue(sprite.htmlContainer));
}
else {
return point;
}
}
/**
* ============================================================================
* DEPRECATED FUNCTIONS
* @todo Review and remove
* ============================================================================
* @hidden
*/
/**
* Returns element's width.
*
* @ignore Exclude from docs
* @param element Element
* @return Width (px)
* @deprecated Not used anywhere
*/
export function width(element) {
var cs = getComputedStyle(element);
var paddingX = parseFloat(cs.paddingLeft) + parseFloat(cs.paddingRight);
var borderX = parseFloat(cs.borderLeftWidth) + parseFloat(cs.borderRightWidth);
return element.clientWidth - paddingX - borderX;
}
/**
* Returns element's height.
*
* @ignore Exclude from docs
* @param element Element
* @return Height (px)
* @deprecated Not used anywhere
*/
export function height(element) {
var cs = getComputedStyle(element);
var paddingY = parseFloat(cs.paddingTop) + parseFloat(cs.paddingBottom);
var borderY = parseFloat(cs.borderTopWidth) + parseFloat(cs.borderBottomWidth);
return element.clientHeight - paddingY - borderY;
}
/**
* Returns number of decimals
*
* @ignore Exclude from docs
* @param number Input number
* @return Number of decimals
*/
export function decimalPlaces(number) {
var match = ('' + number).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/);
if (!match) {
return 0;
}
return Math.max(0, (match[1] ? match[1].length : 0) - (match[2] ? +match[2] : 0));
}
var urlRegexp = /^([a-zA-Z][a-zA-Z0-9\+\.\-]*:)?(?:(\/\/)([^\@]+\@)?([^\/\?\#\:]*)(\:[0-9]+)?)?([^\?\#]*)(\?[^\#]*)?(\#.*)?$/;
/**
* Parses a URL
*
* @ignore Exclude from docs
*/
// TODO test this
export function parseUrl(url) {
var match = urlRegexp.exec(url);
return {
protocol: (match && match[1]) || "",
separator: (match && match[2]) || "",
authority: (match && match[3]) || "",
domain: (match && match[4]) || "",
port: (match && match[5]) || "",
path: (match && match[6]) || "",
query: (match && match[7]) || "",
hash: (match && match[8]) || ""
};
}
/**
* Serializes a Url into a string
*
* @ignore Exclude from docs
*/
export function serializeUrl(url) {
return url.protocol + url.separator + url.authority + url.domain + url.port + url.path + url.query + url.hash;
}
/**
* Checks whether a Url is relative or not
*
* @ignore Exclude from docs
*/
// TODO is this correct ?
function isRelativeUrl(url) {
return url.protocol === "" &&
url.separator === "" &&
url.authority === "" &&
url.domain === "" &&
url.port === "";
}
/**
* Joins together two URLs, resolving relative URLs correctly
*
* @ignore Exclude from docs
*/
// TODO test this
export function joinUrl(left, right) {
var parsedLeft = parseUrl(left);
var parsedRight = parseUrl(right);
if (isRelativeUrl(parsedLeft)) {
throw new Error("Left URL is not absolute");
}
if (isRelativeUrl(parsedRight)) {
// TODO is this correct ?
if (parsedRight.path !== "") {
if (parsedRight.path[0] === "/") {
parsedLeft.path = parsedRight.path;
// TODO is this correct ?
}
else {
var leftPath = parsedLeft.path.split(/\//);
var rightPath = parsedRight.path.split(/\//);
// TODO is this correct ?
if (leftPath.length === 0) {
if (rightPath.length !== 0) {
leftPath.push("");
}
}
else if (leftPath.length > 1) {
leftPath.pop();
}
$array.pushAll(leftPath, rightPath);
parsedLeft.path = leftPath.join("/");
if (parsedLeft.path !== "" && parsedLeft.path[0] !== "/") {
throw new Error("URL path must start with /");
}
}
}
// TODO is this correct ?
parsedLeft.query = parsedRight.query;
parsedLeft.hash = parsedRight.hash;
return serializeUrl(parsedLeft);
}
else {
return serializeUrl(parsedRight);
}
}
/**
* Detects MSIE.
*
* @return Is IE?
*/
export function isIE() {
return !!window.MSInputMethodContext && !!document.documentMode;
}
//# sourceMappingURL=Utils.js.map