mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
chore(build): rename ionic directory to src and update all references in the build process.
This commit is contained in:
54
src/util/click-block.ts
Normal file
54
src/util/click-block.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import {nativeTimeout} from './dom';
|
||||
|
||||
|
||||
const CSS_CLICK_BLOCK = 'click-block-active';
|
||||
const DEFAULT_EXPIRE = 330;
|
||||
let cbEle, fallbackTimerId;
|
||||
let isShowing = false;
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
export class ClickBlock {
|
||||
private _enabled: boolean = false;
|
||||
|
||||
enable() {
|
||||
cbEle = document.createElement('click-block');
|
||||
document.body.appendChild(cbEle);
|
||||
cbEle.addEventListener('touchmove', function(ev) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
});
|
||||
this._enabled = true;
|
||||
}
|
||||
|
||||
show(shouldShow, expire) {
|
||||
if (this._enabled) {
|
||||
if (shouldShow) {
|
||||
show(expire);
|
||||
|
||||
} else {
|
||||
hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function show(expire) {
|
||||
clearTimeout(fallbackTimerId);
|
||||
fallbackTimerId = nativeTimeout(hide, expire || DEFAULT_EXPIRE);
|
||||
|
||||
if (!isShowing) {
|
||||
cbEle.classList.add(CSS_CLICK_BLOCK);
|
||||
isShowing = true;
|
||||
}
|
||||
}
|
||||
|
||||
function hide() {
|
||||
clearTimeout(fallbackTimerId);
|
||||
if (isShowing) {
|
||||
cbEle.classList.remove(CSS_CLICK_BLOCK);
|
||||
isShowing = false;
|
||||
}
|
||||
}
|
||||
503
src/util/datetime-util.ts
Normal file
503
src/util/datetime-util.ts
Normal file
@@ -0,0 +1,503 @@
|
||||
import {isBlank, isPresent, isString, isObject, assign} from './util';
|
||||
|
||||
|
||||
export function renderDateTime(template: string, value: DateTimeData, locale: LocaleData) {
|
||||
if (isBlank(value)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
let tokens = [];
|
||||
let hasText = false;
|
||||
FORMAT_KEYS.forEach((format, index) => {
|
||||
if (template.indexOf(format.f) > -1) {
|
||||
var token = '{' + index + '}';
|
||||
var text = renderTextFormat(format.f, value[format.k], value, locale);
|
||||
|
||||
if (!hasText && text && isPresent(value[format.k])) {
|
||||
hasText = true;
|
||||
}
|
||||
|
||||
tokens.push(token, text);
|
||||
|
||||
template = template.replace(format.f, token);
|
||||
}
|
||||
});
|
||||
|
||||
if (!hasText) {
|
||||
return '';
|
||||
}
|
||||
|
||||
for (var i = 0; i < tokens.length; i += 2) {
|
||||
template = template.replace(tokens[i], tokens[i + 1]);
|
||||
}
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
|
||||
export function renderTextFormat(format: string, value: any, date: DateTimeData, locale: LocaleData): string {
|
||||
|
||||
if (format === FORMAT_DDDD || format === FORMAT_DDD) {
|
||||
try {
|
||||
value = (new Date(date.year, date.month - 1, date.day)).getDay();
|
||||
|
||||
if (format === FORMAT_DDDD) {
|
||||
return (isPresent(locale.dayNames) ? locale.dayNames : DAY_NAMES)[value];
|
||||
}
|
||||
|
||||
return (isPresent(locale.dayShortNames) ? locale.dayShortNames : DAY_SHORT_NAMES)[value];
|
||||
|
||||
} catch (e) {}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
if (format === FORMAT_A) {
|
||||
return date ? date.hour < 12 ? 'AM' : 'PM' : isPresent(value) ? value.toUpperCase() : '';
|
||||
}
|
||||
|
||||
if (format === FORMAT_a) {
|
||||
return date ? date.hour < 12 ? 'am' : 'pm' : isPresent(value) ? value : '';
|
||||
}
|
||||
|
||||
if (isBlank(value)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (format === FORMAT_YY || format === FORMAT_MM ||
|
||||
format === FORMAT_DD || format === FORMAT_HH ||
|
||||
format === FORMAT_mm) {
|
||||
return twoDigit(value);
|
||||
}
|
||||
|
||||
if (format === FORMAT_YYYY) {
|
||||
return fourDigit(value);
|
||||
}
|
||||
|
||||
if (format === FORMAT_MMMM) {
|
||||
return (isPresent(locale.monthNames) ? locale.monthNames : MONTH_NAMES)[value - 1];
|
||||
}
|
||||
|
||||
if (format === FORMAT_MMM) {
|
||||
return (isPresent(locale.monthShortNames) ? locale.monthShortNames : MONTH_SHORT_NAMES)[value - 1];
|
||||
}
|
||||
|
||||
if (format === FORMAT_hh || format === FORMAT_h) {
|
||||
if (value === 0) {
|
||||
return '12';
|
||||
}
|
||||
if (value > 12) {
|
||||
value -= 12;
|
||||
}
|
||||
if (format === FORMAT_hh && value < 10) {
|
||||
return ('0' + value);
|
||||
}
|
||||
}
|
||||
|
||||
return value.toString();
|
||||
}
|
||||
|
||||
|
||||
export function dateValueRange(format: string, min: DateTimeData, max: DateTimeData): any[] {
|
||||
let opts: any[] = [];
|
||||
let i: number;
|
||||
|
||||
if (format === FORMAT_YYYY || format === FORMAT_YY) {
|
||||
// year
|
||||
i = max.year;
|
||||
while (i >= min.year) {
|
||||
opts.push(i--);
|
||||
}
|
||||
|
||||
} else if (format === FORMAT_MMMM || format === FORMAT_MMM ||
|
||||
format === FORMAT_MM || format === FORMAT_M ||
|
||||
format === FORMAT_hh || format === FORMAT_h) {
|
||||
// month or 12-hour
|
||||
for (i = 1; i < 13; i++) {
|
||||
opts.push(i);
|
||||
}
|
||||
|
||||
} else if (format === FORMAT_DDDD || format === FORMAT_DDD ||
|
||||
format === FORMAT_DD || format === FORMAT_D) {
|
||||
// day
|
||||
for (i = 1; i < 32; i++) {
|
||||
opts.push(i);
|
||||
}
|
||||
|
||||
} else if (format === FORMAT_HH || format === FORMAT_H) {
|
||||
// 24-hour
|
||||
for (i = 0; i < 24; i++) {
|
||||
opts.push(i);
|
||||
}
|
||||
|
||||
} else if (format === FORMAT_mm || format === FORMAT_m) {
|
||||
// minutes
|
||||
for (i = 0; i < 60; i++) {
|
||||
opts.push(i);
|
||||
}
|
||||
|
||||
} else if (format === FORMAT_A || format === FORMAT_a) {
|
||||
// AM/PM
|
||||
opts.push('am', 'pm');
|
||||
}
|
||||
|
||||
return opts;
|
||||
}
|
||||
|
||||
export function dateSortValue(year: number, month: number, day: number): number {
|
||||
return parseInt(`1${fourDigit(year)}${twoDigit(month)}${twoDigit(day)}`, 10);
|
||||
}
|
||||
|
||||
export function dateDataSortValue(data: DateTimeData): number {
|
||||
if (data) {
|
||||
return dateSortValue(data.year, data.month, data.day);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
export function daysInMonth(month: number, year: number): number {
|
||||
return (month === 4 || month === 6 || month === 9 || month === 11) ? 30 : (month === 2) ? isLeapYear(year) ? 29 : 28 : 31;
|
||||
}
|
||||
|
||||
export function isLeapYear(year: number): boolean {
|
||||
return (year % 4 === 0 && year % 100 !== 0) || (year % 400 === 0);
|
||||
}
|
||||
|
||||
|
||||
const ISO_8601_REGEXP = /^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/;
|
||||
const TIME_REGEXP = /^((\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/;
|
||||
|
||||
export function parseDate(val: any): DateTimeData {
|
||||
// manually parse IS0 cuz Date.parse cannot be trusted
|
||||
// ISO 8601 format: 1994-12-15T13:47:20Z
|
||||
let parse: any[];
|
||||
|
||||
if (isPresent(val) && val !== '') {
|
||||
// try parsing for just time first, HH:MM
|
||||
parse = TIME_REGEXP.exec(val);
|
||||
if (isPresent(parse)) {
|
||||
// adjust the array so it fits nicely with the datetime parse
|
||||
parse.unshift(undefined, undefined);
|
||||
parse[2] = parse[3] = undefined;
|
||||
|
||||
} else {
|
||||
// try parsing for full ISO datetime
|
||||
parse = ISO_8601_REGEXP.exec(val);
|
||||
}
|
||||
}
|
||||
|
||||
if (isBlank(parse)) {
|
||||
// wasn't able to parse the ISO datetime
|
||||
return null;
|
||||
}
|
||||
|
||||
// ensure all the parse values exist with at least 0
|
||||
for (var i = 1; i < 8; i++) {
|
||||
parse[i] = (parse[i] !== undefined ? parseInt(parse[i], 10) : null);
|
||||
}
|
||||
|
||||
var tzOffset = 0;
|
||||
if (isPresent(parse[9]) && isPresent(parse[10])) {
|
||||
// hours
|
||||
tzOffset = parseInt(parse[10], 10) * 60;
|
||||
if (isPresent(parse[11])) {
|
||||
// minutes
|
||||
tzOffset += parseInt(parse[11], 10);
|
||||
}
|
||||
if (parse[9] === '-') {
|
||||
// + or -
|
||||
tzOffset *= -1;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
year: parse[1],
|
||||
month: parse[2],
|
||||
day: parse[3],
|
||||
hour: parse[4],
|
||||
minute: parse[5],
|
||||
second: parse[6],
|
||||
millisecond: parse[7],
|
||||
tzOffset: tzOffset,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
export function updateDate(existingData: DateTimeData, newData: any) {
|
||||
if (isPresent(newData) && newData !== '') {
|
||||
|
||||
if (isString(newData)) {
|
||||
// new date is a string, and hopefully in the ISO format
|
||||
// convert it to our DateTimeData if a valid ISO
|
||||
newData = parseDate(newData);
|
||||
if (newData) {
|
||||
// successfully parsed the ISO string to our DateTimeData
|
||||
assign(existingData, newData);
|
||||
return;
|
||||
}
|
||||
|
||||
} else if ((isPresent(newData.year) || isPresent(newData.hour))) {
|
||||
// newData is from of a datetime picker's selected values
|
||||
// update the existing DateTimeData data with the new values
|
||||
|
||||
// do some magic for 12-hour values
|
||||
if (isPresent(newData.ampm) && isPresent(newData.hour)) {
|
||||
if (newData.ampm.value === 'pm') {
|
||||
newData.hour.value = (newData.hour.value === 12 ? 12 : newData.hour.value + 12);
|
||||
|
||||
} else {
|
||||
newData.hour.value = (newData.hour.value === 12 ? 0 : newData.hour.value);
|
||||
}
|
||||
}
|
||||
|
||||
// merge new values from the picker's selection
|
||||
// to the existing DateTimeData values
|
||||
for (var k in newData) {
|
||||
existingData[k] = newData[k].value;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// eww, invalid data
|
||||
console.warn(`Error parsing date: "${newData}". Please provide a valid ISO 8601 datetime format: https://www.w3.org/TR/NOTE-datetime`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function parseTemplate(template: string): string[] {
|
||||
let formats: string[] = [];
|
||||
|
||||
template = template.replace(/[^\w\s]/gi, ' ');
|
||||
|
||||
FORMAT_KEYS.forEach(format => {
|
||||
if (format.f.length > 1 && template.indexOf(format.f) > -1 && template.indexOf(format.f + format.f.charAt(0)) < 0) {
|
||||
template = template.replace(format.f, ' ' + format.f + ' ');
|
||||
}
|
||||
});
|
||||
|
||||
let words = template.split(' ').filter(w => w.length > 0);
|
||||
words.forEach((word, i) => {
|
||||
FORMAT_KEYS.forEach(format => {
|
||||
if (word === format.f) {
|
||||
if (word === FORMAT_A || word === FORMAT_a) {
|
||||
// this format is an am/pm format, so it's an "a" or "A"
|
||||
if ((formats.indexOf(FORMAT_h) < 0 && formats.indexOf(FORMAT_hh) < 0) ||
|
||||
(words[i - 1] !== FORMAT_m && words[i - 1] !== FORMAT_mm)) {
|
||||
// template does not already have a 12-hour format
|
||||
// or this am/pm format doesn't have a minute format immediately before it
|
||||
// so do not treat this word "a" or "A" as an am/pm format
|
||||
return;
|
||||
}
|
||||
}
|
||||
formats.push(word);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return formats;
|
||||
}
|
||||
|
||||
|
||||
export function getValueFromFormat(date: DateTimeData, format: string) {
|
||||
if (format === FORMAT_A || format === FORMAT_a) {
|
||||
return (date.hour < 12 ? 'am' : 'pm');
|
||||
}
|
||||
if (format === FORMAT_hh || format === FORMAT_h) {
|
||||
return (date.hour > 12 ? date.hour - 12 : date.hour);
|
||||
}
|
||||
return date[convertFormatToKey(format)];
|
||||
}
|
||||
|
||||
|
||||
export function convertFormatToKey(format: string): string {
|
||||
for (var k in FORMAT_KEYS) {
|
||||
if (FORMAT_KEYS[k].f === format) {
|
||||
return FORMAT_KEYS[k].k;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
export function convertDataToISO(data: DateTimeData): string {
|
||||
// https://www.w3.org/TR/NOTE-datetime
|
||||
let rtn = '';
|
||||
|
||||
if (isPresent(data)) {
|
||||
if (isPresent(data.year)) {
|
||||
// YYYY
|
||||
rtn = fourDigit(data.year);
|
||||
|
||||
if (isPresent(data.month)) {
|
||||
// YYYY-MM
|
||||
rtn += '-' + twoDigit(data.month);
|
||||
|
||||
if (isPresent(data.day)) {
|
||||
// YYYY-MM-DD
|
||||
rtn += '-' + twoDigit(data.day);
|
||||
|
||||
if (isPresent(data.hour)) {
|
||||
// YYYY-MM-DDTHH:mm:SS
|
||||
rtn += `T${twoDigit(data.hour)}:${twoDigit(data.minute)}:${twoDigit(data.second)}`;
|
||||
|
||||
if (data.millisecond > 0) {
|
||||
// YYYY-MM-DDTHH:mm:SS.SSS
|
||||
rtn += '.' + threeDigit(data.millisecond);
|
||||
}
|
||||
|
||||
if (data.tzOffset === 0) {
|
||||
// YYYY-MM-DDTHH:mm:SSZ
|
||||
rtn += 'Z';
|
||||
|
||||
} else {
|
||||
// YYYY-MM-DDTHH:mm:SS+/-HH:mm
|
||||
rtn += (data.tzOffset > 0 ? '+' : '-') + twoDigit(Math.floor(data.tzOffset / 60)) + ':' + twoDigit(data.tzOffset % 60);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if (isPresent(data.hour)) {
|
||||
// HH:mm
|
||||
rtn = twoDigit(data.hour) + ':' + twoDigit(data.minute);
|
||||
|
||||
if (isPresent(data.second)) {
|
||||
// HH:mm:SS
|
||||
rtn += ':' + twoDigit(data.second);
|
||||
|
||||
if (isPresent(data.millisecond)) {
|
||||
// HH:mm:SS.SSS
|
||||
rtn += '.' + threeDigit(data.millisecond);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rtn;
|
||||
}
|
||||
|
||||
function twoDigit(val: number): string {
|
||||
return ('0' + (isPresent(val) ? val : '0')).slice(-2);
|
||||
}
|
||||
|
||||
function threeDigit(val: number): string {
|
||||
return ('00' + (isPresent(val) ? val : '0')).slice(-3);
|
||||
}
|
||||
|
||||
function fourDigit(val: number): string {
|
||||
return ('000' + (isPresent(val) ? val : '0')).slice(-4);
|
||||
}
|
||||
|
||||
|
||||
export interface DateTimeData {
|
||||
year?: number;
|
||||
month?: number;
|
||||
day?: number;
|
||||
hour?: number;
|
||||
minute?: number;
|
||||
second?: number;
|
||||
millisecond?: number;
|
||||
tzOffset?: number;
|
||||
}
|
||||
|
||||
|
||||
export interface LocaleData {
|
||||
monthNames?: string[];
|
||||
monthShortNames?: string[];
|
||||
dayNames?: string[];
|
||||
dayShortNames?: string[];
|
||||
}
|
||||
|
||||
|
||||
const FORMAT_YYYY = 'YYYY';
|
||||
const FORMAT_YY = 'YY';
|
||||
const FORMAT_MMMM = 'MMMM';
|
||||
const FORMAT_MMM = 'MMM';
|
||||
const FORMAT_MM = 'MM';
|
||||
const FORMAT_M = 'M';
|
||||
const FORMAT_DDDD = 'DDDD';
|
||||
const FORMAT_DDD = 'DDD';
|
||||
const FORMAT_DD = 'DD';
|
||||
const FORMAT_D = 'D';
|
||||
const FORMAT_HH = 'HH';
|
||||
const FORMAT_H = 'H';
|
||||
const FORMAT_hh = 'hh';
|
||||
const FORMAT_h = 'h';
|
||||
const FORMAT_mm = 'mm';
|
||||
const FORMAT_m = 'm';
|
||||
const FORMAT_A = 'A';
|
||||
const FORMAT_a = 'a';
|
||||
|
||||
const FORMAT_KEYS = [
|
||||
{ f: FORMAT_YYYY, k: 'year' },
|
||||
{ f: FORMAT_MMMM, k: 'month' },
|
||||
{ f: FORMAT_DDDD, k: 'day' },
|
||||
{ f: FORMAT_MMM, k: 'month' },
|
||||
{ f: FORMAT_DDD, k: 'day' },
|
||||
{ f: FORMAT_YY, k: 'year' },
|
||||
{ f: FORMAT_MM, k: 'month' },
|
||||
{ f: FORMAT_DD, k: 'day' },
|
||||
{ f: FORMAT_HH, k: 'hour' },
|
||||
{ f: FORMAT_hh, k: 'hour' },
|
||||
{ f: FORMAT_mm, k: 'minute' },
|
||||
{ f: FORMAT_M, k: 'month' },
|
||||
{ f: FORMAT_D, k: 'day' },
|
||||
{ f: FORMAT_H, k: 'hour' },
|
||||
{ f: FORMAT_h, k: 'hour' },
|
||||
{ f: FORMAT_m, k: 'minute' },
|
||||
{ f: FORMAT_A, k: 'ampm' },
|
||||
{ f: FORMAT_a, k: 'ampm' },
|
||||
];
|
||||
|
||||
const FORMAT_REGEX = /(\[[^\[]*\])|(\\)?([Hh]mm(ss)?|Mo|MM?M?M?|DD?D?D?|ddd?d?|YYYY|YY|a|A|hh?|HH?|mm?|ss?|.)/g;
|
||||
|
||||
const DAY_NAMES = [
|
||||
'Sunday',
|
||||
'Monday',
|
||||
'Tuesday',
|
||||
'Wednesday',
|
||||
'Thursday',
|
||||
'Friday',
|
||||
'Saturday',
|
||||
];
|
||||
|
||||
const DAY_SHORT_NAMES = [
|
||||
'Sun',
|
||||
'Mon',
|
||||
'Tue',
|
||||
'Wed',
|
||||
'Thu',
|
||||
'Fri',
|
||||
'Sat',
|
||||
];
|
||||
|
||||
const MONTH_NAMES = [
|
||||
'January',
|
||||
'February',
|
||||
'March',
|
||||
'April',
|
||||
'May',
|
||||
'June',
|
||||
'July',
|
||||
'August',
|
||||
'September',
|
||||
'October',
|
||||
'November',
|
||||
'December',
|
||||
];
|
||||
|
||||
const MONTH_SHORT_NAMES = [
|
||||
'Jan',
|
||||
'Feb',
|
||||
'Mar',
|
||||
'Apr',
|
||||
'May',
|
||||
'Jun',
|
||||
'Jul',
|
||||
'Aug',
|
||||
'Sep',
|
||||
'Oct',
|
||||
'Nov',
|
||||
'Dec',
|
||||
];
|
||||
307
src/util/dom.ts
Normal file
307
src/util/dom.ts
Normal file
@@ -0,0 +1,307 @@
|
||||
|
||||
// RequestAnimationFrame Polyfill (Android 4.3 and below)
|
||||
/*! @author Paul Irish */
|
||||
/*! @source https://gist.github.com/paulirish/1579671 */
|
||||
(function() {
|
||||
var rafLastTime = 0;
|
||||
const win: any = window;
|
||||
if (!win.requestAnimationFrame) {
|
||||
win.requestAnimationFrame = function(callback, element) {
|
||||
var currTime = Date.now();
|
||||
var timeToCall = Math.max(0, 16 - (currTime - rafLastTime));
|
||||
|
||||
var id = window.setTimeout(function() {
|
||||
callback(currTime + timeToCall);
|
||||
}, timeToCall);
|
||||
|
||||
rafLastTime = currTime + timeToCall;
|
||||
return id;
|
||||
};
|
||||
}
|
||||
|
||||
if (!win.cancelAnimationFrame) {
|
||||
win.cancelAnimationFrame = function(id) { clearTimeout(id); };
|
||||
}
|
||||
})();
|
||||
|
||||
// use native raf rather than the zone wrapped one
|
||||
let originalRaf = (window[window['Zone']['__symbol__']('requestAnimationFrame')] || window[window['Zone']['__symbol__']('webkitRequestAnimationFrame')]);
|
||||
// if the originalRaf from the Zone symbol is not available, we need to provide the polyfilled version
|
||||
export const nativeRaf = originalRaf !== undefined ? originalRaf['bind'](window) : window.requestAnimationFrame.bind(window);
|
||||
|
||||
// zone wrapped raf
|
||||
export const raf = window.requestAnimationFrame.bind(window);
|
||||
export const cancelRaf = window.cancelAnimationFrame.bind(window);
|
||||
|
||||
export const nativeTimeout = window[window['Zone']['__symbol__']('setTimeout')]['bind'](window);
|
||||
export const clearNativeTimeout = window[window['Zone']['__symbol__']('clearTimeout')]['bind'](window);
|
||||
|
||||
export function rafFrames(framesToWait, callback) {
|
||||
framesToWait = Math.ceil(framesToWait);
|
||||
|
||||
if (framesToWait < 2) {
|
||||
nativeRaf(callback);
|
||||
|
||||
} else {
|
||||
nativeTimeout(() => {
|
||||
nativeRaf(callback);
|
||||
}, (framesToWait - 1) * 16.6667);
|
||||
}
|
||||
}
|
||||
|
||||
export let CSS: {
|
||||
transform?: string,
|
||||
transition?: string,
|
||||
transitionDuration?: string,
|
||||
transitionDelay?: string,
|
||||
transitionTimingFn?: string,
|
||||
transitionStart?: string,
|
||||
transitionEnd?: string,
|
||||
} = {};
|
||||
|
||||
(function() {
|
||||
// transform
|
||||
var i, keys = ['webkitTransform', 'transform', '-webkit-transform', 'webkit-transform',
|
||||
'-moz-transform', 'moz-transform', 'MozTransform', 'mozTransform', 'msTransform'];
|
||||
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
if (document.documentElement.style[keys[i]] !== undefined) {
|
||||
CSS.transform = keys[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// transition
|
||||
keys = ['webkitTransition', 'mozTransition', 'msTransition', 'transition'];
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
if (document.documentElement.style[keys[i]] !== undefined) {
|
||||
CSS.transition = keys[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// The only prefix we care about is webkit for transitions.
|
||||
var isWebkit = CSS.transition.indexOf('webkit') > -1;
|
||||
|
||||
// transition duration
|
||||
CSS.transitionDuration = (isWebkit ? '-webkit-' : '') + 'transition-duration';
|
||||
|
||||
// transition timing function
|
||||
CSS.transitionTimingFn = (isWebkit ? '-webkit-' : '') + 'transition-timing-function';
|
||||
|
||||
// transition delay
|
||||
CSS.transitionDelay = (isWebkit ? '-webkit-' : '') + 'transition-delay';
|
||||
|
||||
// To be sure transitionend works everywhere, include *both* the webkit and non-webkit events
|
||||
CSS.transitionEnd = (isWebkit ? 'webkitTransitionEnd ' : '') + 'transitionend';
|
||||
})();
|
||||
|
||||
|
||||
export function transitionEnd(el: HTMLElement, callback: Function) {
|
||||
if (el) {
|
||||
CSS.transitionEnd.split(' ').forEach(eventName => {
|
||||
el.addEventListener(eventName, onEvent);
|
||||
});
|
||||
|
||||
return unregister;
|
||||
}
|
||||
|
||||
function unregister() {
|
||||
CSS.transitionEnd.split(' ').forEach(eventName => {
|
||||
el.removeEventListener(eventName, onEvent);
|
||||
});
|
||||
}
|
||||
|
||||
function onEvent(ev) {
|
||||
if (el === ev.target) {
|
||||
unregister();
|
||||
callback(ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function ready(callback?: Function) {
|
||||
let promise = null;
|
||||
|
||||
if (!callback) {
|
||||
// a callback wasn't provided, so let's return a promise instead
|
||||
promise = new Promise(resolve => { callback = resolve; });
|
||||
}
|
||||
|
||||
if (document.readyState === 'complete' || document.readyState === 'interactive') {
|
||||
callback();
|
||||
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', completed, false);
|
||||
window.addEventListener('load', completed, false);
|
||||
}
|
||||
|
||||
return promise;
|
||||
|
||||
function completed() {
|
||||
document.removeEventListener('DOMContentLoaded', completed, false);
|
||||
window.removeEventListener('load', completed, false);
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
export function windowLoad(callback?: Function) {
|
||||
let promise = null;
|
||||
|
||||
if (!callback) {
|
||||
// a callback wasn't provided, so let's return a promise instead
|
||||
promise = new Promise(resolve => { callback = resolve; });
|
||||
}
|
||||
|
||||
if (document.readyState === 'complete') {
|
||||
callback();
|
||||
|
||||
} else {
|
||||
|
||||
window.addEventListener('load', completed, false);
|
||||
}
|
||||
|
||||
return promise;
|
||||
|
||||
function completed() {
|
||||
window.removeEventListener('load', completed, false);
|
||||
callback();
|
||||
}
|
||||
}
|
||||
|
||||
export function pointerCoord(ev: any): {x: number, y: number} {
|
||||
// get coordinates for either a mouse click
|
||||
// or a touch depending on the given event
|
||||
let c = { x: 0, y: 0 };
|
||||
if (ev) {
|
||||
const touches = ev.touches && ev.touches.length ? ev.touches : [ev];
|
||||
const e = (ev.changedTouches && ev.changedTouches[0]) || touches[0];
|
||||
if (e) {
|
||||
c.x = e.clientX || e.pageX || 0;
|
||||
c.y = e.clientY || e.pageY || 0;
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
export function hasPointerMoved(threshold, startCoord, endCoord) {
|
||||
return startCoord && endCoord &&
|
||||
(Math.abs(startCoord.x - endCoord.x) > threshold || Math.abs(startCoord.y - endCoord.y) > threshold);
|
||||
}
|
||||
|
||||
export function isActive(ele) {
|
||||
return !!(ele && (document.activeElement === ele));
|
||||
}
|
||||
|
||||
export function hasFocus(ele) {
|
||||
return isActive(ele) && (ele.parentElement.querySelector(':focus') === ele);
|
||||
}
|
||||
|
||||
export function isTextInput(ele) {
|
||||
return !!ele &&
|
||||
(ele.tagName === 'TEXTAREA' ||
|
||||
ele.contentEditable === 'true' ||
|
||||
(ele.tagName === 'INPUT' && !(/^(radio|checkbox|range|file|submit|reset|color|image|button)$/i).test(ele.type)));
|
||||
}
|
||||
|
||||
export function hasFocusedTextInput() {
|
||||
let ele = document.activeElement;
|
||||
if (isTextInput(ele)) {
|
||||
return (ele.parentElement.querySelector(':focus') === ele);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const skipInputAttrsReg = /^(value|checked|disabled|type|class|style|id|autofocus|autocomplete|autocorrect)$/i;
|
||||
export function copyInputAttributes(srcElement, destElement) {
|
||||
// copy attributes from one element to another
|
||||
// however, skip over a few of them as they're already
|
||||
// handled in the angular world
|
||||
var attrs = srcElement.attributes;
|
||||
for (var i = 0; i < attrs.length; i++) {
|
||||
var attr = attrs[i];
|
||||
if (!skipInputAttrsReg.test(attr.name)) {
|
||||
destElement.setAttribute(attr.name, attr.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let matchesFn: string;
|
||||
let matchesMethods: Array<string> = ['matches', 'webkitMatchesSelector', 'mozMatchesSelector', 'msMatchesSelector'];
|
||||
matchesMethods.some((fn: string) => {
|
||||
if (typeof document.documentElement[fn] === 'function') {
|
||||
matchesFn = fn;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
export function closest(ele: HTMLElement, selector: string, checkSelf?: boolean) {
|
||||
if (ele && matchesFn) {
|
||||
|
||||
// traverse parents
|
||||
ele = (checkSelf ? ele : ele.parentElement);
|
||||
|
||||
while (ele !== null) {
|
||||
if (ele[matchesFn](selector)) {
|
||||
return ele;
|
||||
}
|
||||
ele = ele.parentElement;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the element offsetWidth and offsetHeight. Values are cached
|
||||
* to reduce DOM reads. Cache is cleared on a window resize.
|
||||
*/
|
||||
export function getDimensions(ele: HTMLElement, id: string): {
|
||||
width: number, height: number, left: number, top: number
|
||||
} {
|
||||
let dimensions = dimensionCache[id];
|
||||
if (!dimensions) {
|
||||
// make sure we got good values before caching
|
||||
if (ele.offsetWidth && ele.offsetHeight) {
|
||||
dimensions = dimensionCache[id] = {
|
||||
width: ele.offsetWidth,
|
||||
height: ele.offsetHeight,
|
||||
left: ele.offsetLeft,
|
||||
top: ele.offsetTop
|
||||
};
|
||||
|
||||
} else {
|
||||
// do not cache bad values
|
||||
return { width: 0, height: 0, left: 0, top: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
return dimensions;
|
||||
}
|
||||
|
||||
export function clearDimensions(id: string) {
|
||||
delete dimensionCache[id];
|
||||
}
|
||||
|
||||
export function windowDimensions(): {width: number, height: number} {
|
||||
if (!dimensionCache.win) {
|
||||
// make sure we got good values before caching
|
||||
if (window.innerWidth && window.innerHeight) {
|
||||
dimensionCache.win = {
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight
|
||||
};
|
||||
} else {
|
||||
// do not cache bad values
|
||||
return { width: 0, height: 0 };
|
||||
}
|
||||
}
|
||||
return dimensionCache.win;
|
||||
}
|
||||
|
||||
export function flushDimensionCache() {
|
||||
dimensionCache = {};
|
||||
}
|
||||
|
||||
let dimensionCache: any = {};
|
||||
102
src/util/events.ts
Normal file
102
src/util/events.ts
Normal file
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* @name Events
|
||||
* @description
|
||||
* Events is a publish-subscribe style event system for sending and responding to application-level
|
||||
* events across your app.
|
||||
*
|
||||
* @usage
|
||||
* ```ts
|
||||
* import {Events} from 'ionic-angular';
|
||||
*
|
||||
* constructor(public events: Events) {}
|
||||
*
|
||||
* // first page (publish an event when a user is created)
|
||||
* function createUser(user) {
|
||||
* console.log('User created!')
|
||||
* events.publish('user:created', user);
|
||||
* }
|
||||
*
|
||||
* // second page (listen for the user created event)
|
||||
* events.subscribe('user:created', (user) => {
|
||||
* console.log('Welcome', user);
|
||||
* });
|
||||
*
|
||||
* ```
|
||||
* @demo /docs/v2/demos/events/
|
||||
*/
|
||||
export class Events {
|
||||
private _channels: Array<any> = [];
|
||||
|
||||
/**
|
||||
* Subscribe to an event topic. Events that get posted to that topic will trigger the provided handler.
|
||||
*
|
||||
* @param {string} topic the topic to subscribe to
|
||||
* @param {function} handler the event handler
|
||||
*/
|
||||
subscribe(topic, ...handlers) {
|
||||
if (!this._channels[topic]) {
|
||||
this._channels[topic] = [];
|
||||
}
|
||||
handlers.forEach((handler) => {
|
||||
this._channels[topic].push(handler);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from the given topic. Your handler will no longer receive events published to this topic.
|
||||
*
|
||||
* @param {string} topic the topic to unsubscribe from
|
||||
* @param {function} handler the event handler
|
||||
*
|
||||
* @return true if a handler was removed
|
||||
*/
|
||||
unsubscribe(topic, handler) {
|
||||
let t = this._channels[topic];
|
||||
if (!t) {
|
||||
// Wasn't found, wasn't removed
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!handler) {
|
||||
// Remove all handlers for this topic
|
||||
delete this._channels[topic];
|
||||
return true;
|
||||
}
|
||||
|
||||
// We need to find and remove a specific handler
|
||||
let i = t.indexOf(handler);
|
||||
|
||||
if (i < 0) {
|
||||
// Wasn't found, wasn't removed
|
||||
return false;
|
||||
}
|
||||
|
||||
t.splice(i, 1);
|
||||
|
||||
// If the channel is empty now, remove it from the channel map
|
||||
if (!t.length) {
|
||||
delete this._channels[topic];
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Publish an event to the given topic.
|
||||
*
|
||||
* @param {string} topic the topic to publish to
|
||||
* @param {any} eventData the data to send as the event
|
||||
*/
|
||||
publish(topic, ...args) {
|
||||
var t = this._channels[topic];
|
||||
if (!t) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let responses = [];
|
||||
t.forEach((handler) => {
|
||||
responses.push(handler(args));
|
||||
});
|
||||
return responses;
|
||||
}
|
||||
}
|
||||
51
src/util/feature-detect.ts
Normal file
51
src/util/feature-detect.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
|
||||
export class FeatureDetect {
|
||||
private _results: any = {};
|
||||
|
||||
run(window, document) {
|
||||
for (let name in featureDetects) {
|
||||
this._results[name] = featureDetects[name](window, document, document.body);
|
||||
}
|
||||
}
|
||||
|
||||
has(featureName) {
|
||||
return !!this._results[featureName];
|
||||
}
|
||||
|
||||
static add(name, fn) {
|
||||
featureDetects[name] = fn;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
let featureDetects = {};
|
||||
|
||||
|
||||
// FeatureDetect.add('sticky', function(window, document) {
|
||||
// // css position sticky
|
||||
// let ele = document.createElement('div');
|
||||
// ele.style.cssText = 'position:-webkit-sticky;position:sticky';
|
||||
// return ele.style.position.indexOf('sticky') > -1;
|
||||
// });
|
||||
|
||||
|
||||
FeatureDetect.add('hairlines', function(window, document, body) {
|
||||
/**
|
||||
* Hairline Shim
|
||||
* Add the "hairline" CSS class name to the body tag
|
||||
* if the browser supports subpixels.
|
||||
*/
|
||||
let canDo = false;
|
||||
if (window.devicePixelRatio >= 2) {
|
||||
var hairlineEle = document.createElement('div');
|
||||
hairlineEle.style.border = '.5px solid transparent';
|
||||
body.appendChild(hairlineEle);
|
||||
|
||||
if (hairlineEle.offsetHeight === 1) {
|
||||
body.classList.add('hairlines');
|
||||
canDo = true;
|
||||
}
|
||||
body.removeChild(hairlineEle);
|
||||
}
|
||||
return canDo;
|
||||
});
|
||||
84
src/util/form.ts
Normal file
84
src/util/form.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
@Injectable()
|
||||
export class Form {
|
||||
private _blur: HTMLElement;
|
||||
private _focused = null;
|
||||
private _ids: number = -1;
|
||||
private _inputs: Array<any> = [];
|
||||
|
||||
constructor() {
|
||||
this.focusCtrl(document);
|
||||
}
|
||||
|
||||
register(input) {
|
||||
this._inputs.push(input);
|
||||
}
|
||||
|
||||
deregister(input) {
|
||||
let index = this._inputs.indexOf(input);
|
||||
if (index > -1) {
|
||||
this._inputs.splice(index, 1);
|
||||
}
|
||||
if (input === this._focused) {
|
||||
this._focused = null;
|
||||
}
|
||||
}
|
||||
|
||||
focusCtrl(document) {
|
||||
// raw DOM fun
|
||||
let focusCtrl = document.createElement('focus-ctrl');
|
||||
focusCtrl.setAttribute('aria-hidden', true);
|
||||
|
||||
this._blur = document.createElement('button');
|
||||
this._blur.tabIndex = -1;
|
||||
focusCtrl.appendChild(this._blur);
|
||||
|
||||
document.body.appendChild(focusCtrl);
|
||||
}
|
||||
|
||||
focusOut() {
|
||||
console.debug('focusOut');
|
||||
let activeElement: any = document.activeElement;
|
||||
if (activeElement) {
|
||||
activeElement.blur();
|
||||
}
|
||||
this._blur.focus();
|
||||
}
|
||||
|
||||
setAsFocused(input) {
|
||||
this._focused = input;
|
||||
}
|
||||
|
||||
/**
|
||||
* Focuses the next input element, if it exists.
|
||||
*/
|
||||
tabFocus(currentInput) {
|
||||
let index = this._inputs.indexOf(currentInput);
|
||||
if (index > -1 && (index + 1) < this._inputs.length) {
|
||||
let nextInput = this._inputs[index + 1];
|
||||
if (nextInput !== this._focused) {
|
||||
console.debug('tabFocus, next');
|
||||
return nextInput.initFocus();
|
||||
}
|
||||
}
|
||||
|
||||
index = this._inputs.indexOf(this._focused);
|
||||
if (index > 0) {
|
||||
let previousInput = this._inputs[index - 1];
|
||||
if (previousInput) {
|
||||
console.debug('tabFocus, previous');
|
||||
previousInput.initFocus();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nextId() {
|
||||
return ++this._ids;
|
||||
}
|
||||
|
||||
}
|
||||
269
src/util/functions.scss
Normal file
269
src/util/functions.scss
Normal file
@@ -0,0 +1,269 @@
|
||||
|
||||
// Color Functions
|
||||
// --------------------------------------------------
|
||||
|
||||
@function color-brightness($color-value) {
|
||||
@if (type-of($color-value) != color) {
|
||||
@return color-error($color-value);
|
||||
}
|
||||
|
||||
@return (red($color-value) * .299 + green($color-value) * .587 + blue($color-value) * .114) / 255 * 100%;
|
||||
}
|
||||
|
||||
|
||||
@function color-inverse($color-value, $dark: #000, $light: #fff) {
|
||||
@if (type-of($color-value) != color) {
|
||||
@return color-error($color-value);
|
||||
}
|
||||
|
||||
$brightness: color-brightness($color-value);
|
||||
$red: red($color-value);
|
||||
$green: green($color-value);
|
||||
|
||||
@if ($brightness > 79) {
|
||||
@return $dark;
|
||||
}
|
||||
|
||||
@if ($green > 240) {
|
||||
@return $dark;
|
||||
}
|
||||
|
||||
@if ($red > 220 and $green > 180) {
|
||||
@return $dark;
|
||||
}
|
||||
|
||||
@return $light;
|
||||
}
|
||||
|
||||
// Pass dark and light colors based on the mode
|
||||
// this is mostly used for toolbar buttons/titles
|
||||
//
|
||||
// @param {String} $color-value - color to get the inverse of
|
||||
// @param {Boolean} $custom-contrast-mode - the mode to use
|
||||
// in order to pass the custom colors
|
||||
//
|
||||
// @return {Color}
|
||||
// --------------------------------------------------
|
||||
@function mode-inverse($color-value, $custom-contrast-mode) {
|
||||
$dark: #000;
|
||||
$light: #fff;
|
||||
|
||||
@if ($custom-contrast-mode == md) {
|
||||
$dark: #424242;
|
||||
$light: #fff;
|
||||
} @else if ($custom-contrast-mode == ios) {
|
||||
$dark: color($colors-ios, primary);
|
||||
$light: #fff;
|
||||
}
|
||||
|
||||
@return color-inverse($color-value, $dark, $light);
|
||||
}
|
||||
|
||||
|
||||
@function color-shade($color-value, $amount:8%) {
|
||||
@if (type-of($color-value) != color) {
|
||||
@return color-error($color-value);
|
||||
}
|
||||
|
||||
$lightness: lightness($color-value);
|
||||
|
||||
$shade: #fff;
|
||||
|
||||
@if ($lightness > 50) {
|
||||
$shade: #000;
|
||||
}
|
||||
|
||||
@return mix($shade, $color-value, $amount);
|
||||
}
|
||||
|
||||
@function color-error($color-value) {
|
||||
@error "
|
||||
|
||||
The value `#{$color-value}` must be a color.
|
||||
If you are setting the value as a map make sure
|
||||
both the base and contrast are defined as colors.
|
||||
|
||||
For example:
|
||||
|
||||
$colors: (
|
||||
primary: #327eff,
|
||||
secondary: (base: #32db64, contrast: #000),
|
||||
);";
|
||||
|
||||
@return false;
|
||||
}
|
||||
|
||||
// Copy Colors Map
|
||||
// --------------------------------------------------
|
||||
|
||||
@function copy-colors($colors-map) {
|
||||
@return map-merge($colors-map, ());
|
||||
}
|
||||
|
||||
|
||||
// String Replace Function
|
||||
// --------------------------------------------------
|
||||
|
||||
@function str-replace($string, $search, $replace: "") {
|
||||
$index: str-index($string, $search);
|
||||
|
||||
@if $index {
|
||||
@return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
|
||||
}
|
||||
|
||||
@return $string;
|
||||
}
|
||||
|
||||
|
||||
// URL Encode Function
|
||||
// --------------------------------------------------
|
||||
|
||||
@function url-encode($val) {
|
||||
@return str-replace($val, " ", "%20");
|
||||
}
|
||||
|
||||
|
||||
// Fetch nested keys
|
||||
// @param {Map} $map - Map
|
||||
// @param {Arglist} $keys - Keys to fetch
|
||||
// @return {*}
|
||||
// --------------------------------------------------
|
||||
|
||||
@function map-fetch($map, $keys...) {
|
||||
@each $key in $keys {
|
||||
$map: map-get($map, $key);
|
||||
}
|
||||
|
||||
@return $map;
|
||||
}
|
||||
|
||||
|
||||
// Fetch map color value
|
||||
// @param {Map} $map - Map
|
||||
// @param {String} $color-name - Color name to get
|
||||
// @param {String} $color-key - Color key (optional), default base
|
||||
// @return {Color}
|
||||
// --------------------------------------------------
|
||||
|
||||
@function color($map, $color-name, $color-key:null) {
|
||||
// Get the value from $color-name in the map
|
||||
// this can be of type color or a map
|
||||
$color-value: map-get($map, $color-name);
|
||||
|
||||
// If we were given a map we need to grab the value
|
||||
// of the key that is passed or the base key
|
||||
@if(type-of($color-value) == map) {
|
||||
@if($color-key) {
|
||||
$color-value: map-fetch($map, $color-name, $color-key);
|
||||
} @else {
|
||||
$color-value: map-fetch($map, $color-name, base);
|
||||
}
|
||||
}
|
||||
|
||||
// If it isn't a map then return the value
|
||||
@return $color-value;
|
||||
}
|
||||
|
||||
// Get the color map key based on the value
|
||||
// if it doesn't exist then return the value
|
||||
// --------------------------------------------------
|
||||
@function color-key($colors, $value) {
|
||||
@each $color-name, $color-value in $colors {
|
||||
$base-value: color($colors, $color-name);
|
||||
@if ($base-value == $value) {
|
||||
@return map-get($colors, $color-name);
|
||||
}
|
||||
}
|
||||
|
||||
@return $value;
|
||||
}
|
||||
|
||||
// Fetch map color contrast
|
||||
// @param {Map} $colors - colors map
|
||||
// @param {String} $value - color value or color name
|
||||
//
|
||||
// Example values
|
||||
// --------------------------------------------------
|
||||
// primary | #327eff | #444
|
||||
// map key | map value | hex color not in map
|
||||
// --------------------------------------------------
|
||||
//
|
||||
// @param {Boolean} $custom-contrast-mode - use custom
|
||||
// contrast function
|
||||
// @return {Color}
|
||||
// --------------------------------------------------
|
||||
@function color-contrast($colors, $value, $custom-contrast-mode: null) {
|
||||
$color-value: null;
|
||||
|
||||
// If the value is a color (e.g. #fff)
|
||||
// we need to call color-key to see if
|
||||
// it exists in the color map or not
|
||||
@if (type-of($value) == color) {
|
||||
$color-value: color-key($colors, $value);
|
||||
|
||||
// If the value is a string (e.g. primary)
|
||||
// we want to get the value from the map
|
||||
// where it is the key
|
||||
} @else {
|
||||
$color-value: map-get($colors, $value);
|
||||
}
|
||||
|
||||
// If the value is a map then get the contrast
|
||||
// from the map (e.g. (base: #327eff, contrast: blue))
|
||||
@if (type-of($color-value) == map) {
|
||||
// If the map has the contrast key then use that
|
||||
// otherwise it is a map with just a base so get
|
||||
// the inverse of that base color
|
||||
@if map-has-key($color-value, contrast) {
|
||||
$color-value: map-get($color-value, contrast);
|
||||
} @else {
|
||||
$color-value: color-inverse(map-get($color-value, base));
|
||||
}
|
||||
|
||||
// If a mode was passed we need to call
|
||||
// the custom inverse function to get the inverse
|
||||
// color based on the mode
|
||||
} @elseif ($custom-contrast-mode) {
|
||||
$color-value: mode-inverse($color-value, $custom-contrast-mode);
|
||||
|
||||
// Otherwise we were passed a color and can use the
|
||||
// function to get the inverse of that color
|
||||
// (e.g. #f4f4f4)
|
||||
} @else {
|
||||
$color-value: color-inverse($color-value);
|
||||
}
|
||||
|
||||
// If the final value being returned is not a color
|
||||
// we should error
|
||||
@if (type-of($color-value) != color) {
|
||||
@return color-error($color-value);
|
||||
}
|
||||
|
||||
@return $color-value;
|
||||
}
|
||||
|
||||
|
||||
// Create a list using the colors map
|
||||
// @param {Map} $colors - colors map
|
||||
// @return {List} $color-name, $color-base, $color-contrast
|
||||
// ----------------------------------------------------------
|
||||
@function get-colors($colors) {
|
||||
$colors-list: ();
|
||||
|
||||
@each $color-name, $color-value in $colors {
|
||||
$color-base: null;
|
||||
$color-contrast: null;
|
||||
|
||||
@if(type-of($color-value) == map) {
|
||||
$color-base: map-get($color-value, base);
|
||||
$color-contrast: map-get($color-value, contrast);
|
||||
} @else {
|
||||
$color-base: $color-value;
|
||||
$color-contrast: color-inverse($color-value);
|
||||
}
|
||||
|
||||
$colors-list: append($colors-list, ($color-name, $color-base, $color-contrast), comma);
|
||||
}
|
||||
|
||||
@return $colors-list;
|
||||
}
|
||||
186
src/util/keyboard.ts
Normal file
186
src/util/keyboard.ts
Normal file
@@ -0,0 +1,186 @@
|
||||
import {Injectable, NgZone} from '@angular/core';
|
||||
|
||||
import {Config} from '../config/config';
|
||||
import {Form} from './form';
|
||||
import {hasFocusedTextInput, nativeRaf, rafFrames, nativeTimeout} from './dom';
|
||||
|
||||
/**
|
||||
* @name Keyboard
|
||||
* @description
|
||||
* The `Keyboard` class allows you to work with the keyboard events provided by the Ionic keyboard plugin.
|
||||
*
|
||||
* @usage
|
||||
* ```ts
|
||||
* export class MyClass{
|
||||
* constructor(keyboard: Keyboard){
|
||||
* this.keyboard = keyboard;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* ```
|
||||
*/
|
||||
|
||||
@Injectable()
|
||||
export class Keyboard {
|
||||
|
||||
constructor(config: Config, private _form: Form, private _zone: NgZone) {
|
||||
_zone.runOutsideAngular(() => {
|
||||
this.focusOutline(config.get('focusOutline'), document);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check to see if the keyboard is open or not.
|
||||
*
|
||||
* ```ts
|
||||
* export class MyClass{
|
||||
* constructor(keyboard: Keyboard){
|
||||
* this.keyboard = keyboard;
|
||||
* }
|
||||
* keyboardCheck(){
|
||||
* setTimeout(() => console.log('is the keyboard open ', this.keyboard.isOpen()));
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* ```
|
||||
*
|
||||
* @return {boolean} returns a true or flase value if the keyboard is open or not
|
||||
*/
|
||||
isOpen() {
|
||||
return hasFocusedTextInput();
|
||||
}
|
||||
|
||||
/**
|
||||
* When the keyboard is closed, call any methods you want
|
||||
*
|
||||
* ```ts
|
||||
* export class MyClass{
|
||||
* constructor(keyboard: Keyboard){
|
||||
* this.keyboard = keyboard;
|
||||
* this.keyboard.onClose(this.closeCallback);
|
||||
* }
|
||||
* closeCallback(){
|
||||
* // call what ever functionality you want on keyboard close
|
||||
* console.log('Closing time');
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* ```
|
||||
* @param {function} callback method you want to call when the keyboard has been closed
|
||||
* @return {function} returns a callback that gets fired when the keyboard is closed
|
||||
*/
|
||||
onClose(callback, pollingInternval = KEYBOARD_CLOSE_POLLING, pollingChecksMax = KEYBOARD_POLLING_CHECKS_MAX) {
|
||||
console.debug('keyboard onClose');
|
||||
const self = this;
|
||||
let checks = 0;
|
||||
|
||||
let promise = null;
|
||||
|
||||
if (!callback) {
|
||||
// a callback wasn't provided, so let's return a promise instead
|
||||
promise = new Promise(resolve => { callback = resolve; });
|
||||
}
|
||||
|
||||
function checkKeyboard() {
|
||||
console.debug('keyboard isOpen', self.isOpen());
|
||||
if (!self.isOpen() || checks > pollingChecksMax) {
|
||||
rafFrames(30, () => {
|
||||
self._zone.run(() => {
|
||||
console.debug('keyboard closed');
|
||||
callback();
|
||||
});
|
||||
});
|
||||
|
||||
} else {
|
||||
nativeTimeout(checkKeyboard, pollingInternval);
|
||||
}
|
||||
checks++;
|
||||
}
|
||||
|
||||
nativeTimeout(checkKeyboard, pollingInternval);
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Programmatically close the keyboard
|
||||
*
|
||||
*/
|
||||
close() {
|
||||
console.debug('keyboard close()');
|
||||
nativeRaf(() => {
|
||||
if (hasFocusedTextInput()) {
|
||||
// only focus out when a text input has focus
|
||||
this._form.focusOut();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
focusOutline(setting, document) {
|
||||
/* Focus Outline
|
||||
* --------------------------------------------------
|
||||
* By default, when a keydown event happens from a tab key, then
|
||||
* the 'focus-outline' css class is added to the body element
|
||||
* so focusable elements have an outline. On a mousedown or
|
||||
* touchstart event, then the 'focus-outline' css class is removed.
|
||||
*
|
||||
* Config default overrides:
|
||||
* focusOutline: true - Always add the focus-outline
|
||||
* focusOutline: false - Do not add the focus-outline
|
||||
*/
|
||||
|
||||
let self = this;
|
||||
let isKeyInputEnabled = false;
|
||||
|
||||
function cssClass() {
|
||||
nativeRaf(() => {
|
||||
document.body.classList[isKeyInputEnabled ? 'add' : 'remove']('focus-outline');
|
||||
});
|
||||
}
|
||||
|
||||
if (setting === true) {
|
||||
isKeyInputEnabled = true;
|
||||
return cssClass();
|
||||
|
||||
} else if (setting === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// default is to add the focus-outline when the tab key is used
|
||||
function keyDown(ev) {
|
||||
if (!isKeyInputEnabled && ev.keyCode === 9) {
|
||||
isKeyInputEnabled = true;
|
||||
enableKeyInput();
|
||||
}
|
||||
}
|
||||
|
||||
function pointerDown() {
|
||||
isKeyInputEnabled = false;
|
||||
enableKeyInput();
|
||||
}
|
||||
|
||||
function enableKeyInput() {
|
||||
cssClass();
|
||||
|
||||
self._zone.runOutsideAngular(() => {
|
||||
document.removeEventListener('mousedown', pointerDown);
|
||||
document.removeEventListener('touchstart', pointerDown);
|
||||
|
||||
if (isKeyInputEnabled) {
|
||||
document.addEventListener('mousedown', pointerDown);
|
||||
document.addEventListener('touchstart', pointerDown);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', keyDown);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const KEYBOARD_CLOSE_POLLING = 150;
|
||||
const KEYBOARD_POLLING_CHECKS_MAX = 100;
|
||||
54
src/util/mixins.scss
Normal file
54
src/util/mixins.scss
Normal file
@@ -0,0 +1,54 @@
|
||||
|
||||
// Appearance
|
||||
// --------------------------------------------------
|
||||
|
||||
@mixin appearance($val) {
|
||||
-webkit-appearance: $val;
|
||||
-moz-appearance: $val;
|
||||
}
|
||||
|
||||
|
||||
// Calc
|
||||
// --------------------------------------------------
|
||||
|
||||
@mixin calc($property, $expression) {
|
||||
#{$property}: -webkit-calc(#{$expression});
|
||||
#{$property}: calc(#{$expression});
|
||||
}
|
||||
|
||||
|
||||
// Input Placeholder
|
||||
// --------------------------------------------------
|
||||
|
||||
@mixin placeholder($color: #999, $text-indent: 0) {
|
||||
&::-moz-placeholder { // Firefox 19+
|
||||
color: $color;
|
||||
}
|
||||
|
||||
&:-ms-input-placeholder {
|
||||
color: $color;
|
||||
}
|
||||
|
||||
&::-webkit-input-placeholder {
|
||||
// Safari placeholder margin issue
|
||||
text-indent: $text-indent;
|
||||
color: $color;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// SVG Background Image Mixin
|
||||
// --------------------------------------------------
|
||||
|
||||
@mixin svg-background-image($svg) {
|
||||
$url: url-encode($svg);
|
||||
background-image: url("data:image/svg+xml;charset=utf-8,#{$url}");
|
||||
}
|
||||
|
||||
|
||||
// User Select None
|
||||
// --------------------------------------------------
|
||||
|
||||
@mixin user-select-none() {
|
||||
user-select: none;
|
||||
}
|
||||
283
src/util/scroll-view.ts
Normal file
283
src/util/scroll-view.ts
Normal file
@@ -0,0 +1,283 @@
|
||||
import {CSS, pointerCoord, nativeRaf, cancelRaf} from '../util/dom';
|
||||
|
||||
|
||||
export class ScrollView {
|
||||
private _el: HTMLElement;
|
||||
private _js: boolean = false;
|
||||
private _top: number = 0;
|
||||
private _pos: Array<number>;
|
||||
private _velocity: number;
|
||||
private _max: number;
|
||||
private _rafId: number;
|
||||
private _cb: Function;
|
||||
isPlaying: boolean;
|
||||
|
||||
constructor(ele: HTMLElement) {
|
||||
this._el = ele;
|
||||
}
|
||||
|
||||
getTop(): number {
|
||||
if (this._js) {
|
||||
return this._top;
|
||||
}
|
||||
|
||||
return this._top = this._el.scrollTop;
|
||||
}
|
||||
|
||||
setTop(top: number) {
|
||||
this._top = top;
|
||||
|
||||
if (this._js) {
|
||||
this._el.style[CSS.transform] = `translate3d(0px,${top * -1}px,0px)`;
|
||||
|
||||
} else {
|
||||
this._el.scrollTop = top;
|
||||
}
|
||||
}
|
||||
|
||||
scrollTo(x: number, y: number, duration: number): Promise<any> {
|
||||
// scroll animation loop w/ easing
|
||||
// credit https://gist.github.com/dezinezync/5487119
|
||||
let self = this;
|
||||
|
||||
if (!self._el) {
|
||||
// invalid element
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
x = x || 0;
|
||||
y = y || 0;
|
||||
|
||||
let fromY = self._el.scrollTop;
|
||||
let fromX = self._el.scrollLeft;
|
||||
|
||||
let xDistance = Math.abs(x - fromX);
|
||||
let yDistance = Math.abs(y - fromY);
|
||||
let maxAttempts = (duration / 16) + 100;
|
||||
|
||||
return new Promise(resolve => {
|
||||
let startTime: number;
|
||||
let attempts = 0;
|
||||
|
||||
// scroll loop
|
||||
function step() {
|
||||
attempts++;
|
||||
|
||||
if (!self._el || !self.isPlaying || attempts > maxAttempts) {
|
||||
self.isPlaying = false;
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
let time = Math.min(1, ((Date.now() - startTime) / duration));
|
||||
|
||||
// where .5 would be 50% of time on a linear scale easedT gives a
|
||||
// fraction based on the easing method
|
||||
let easedT = (--time) * time * time + 1;
|
||||
|
||||
if (fromY !== y) {
|
||||
self.setTop((easedT * (y - fromY)) + fromY);
|
||||
}
|
||||
|
||||
if (fromX !== x) {
|
||||
self._el.scrollLeft = Math.floor((easedT * (x - fromX)) + fromX);
|
||||
}
|
||||
|
||||
if (easedT < 1) {
|
||||
nativeRaf(step);
|
||||
|
||||
} else {
|
||||
// done
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
|
||||
// start scroll loop
|
||||
self.isPlaying = true;
|
||||
|
||||
// chill out for a frame first
|
||||
nativeRaf(() => {
|
||||
startTime = Date.now();
|
||||
nativeRaf(step);
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
scrollToTop(duration: number): Promise<any> {
|
||||
return this.scrollTo(0, 0, duration);
|
||||
}
|
||||
|
||||
scrollToBottom(duration: number): Promise<any> {
|
||||
let y = 0;
|
||||
if (this._el) {
|
||||
y = this._el.scrollHeight - this._el.clientHeight;
|
||||
}
|
||||
return this.scrollTo(0, y, duration);
|
||||
}
|
||||
|
||||
stop() {
|
||||
this.isPlaying = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* JS Scrolling has been provided only as a temporary solution
|
||||
* until iOS apps can take advantage of scroll events at all times.
|
||||
* The goal is to eventually remove JS scrolling entirely. This
|
||||
* method may be removed in the future.
|
||||
*/
|
||||
jsScroll(onScrollCallback: Function): Function {
|
||||
this._js = true;
|
||||
this._cb = onScrollCallback;
|
||||
this._pos = [];
|
||||
|
||||
this._el.addEventListener('touchstart', this._start.bind(this));
|
||||
this._el.addEventListener('touchmove', this._move.bind(this));
|
||||
this._el.addEventListener('touchend', this._end.bind(this));
|
||||
|
||||
this._el.parentElement.classList.add('js-scroll');
|
||||
|
||||
return () => {
|
||||
this._el.removeEventListener('touchstart', this._start.bind(this));
|
||||
this._el.removeEventListener('touchmove', this._move.bind(this));
|
||||
this._el.removeEventListener('touchend', this._end.bind(this));
|
||||
|
||||
this._el.parentElement.classList.remove('js-scroll');
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Used for JS scrolling. May be removed in the future.
|
||||
*/
|
||||
private _start(ev) {
|
||||
this._velocity = 0;
|
||||
this._pos.length = 0;
|
||||
this._max = null;
|
||||
this._pos.push(pointerCoord(ev).y, Date.now());
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Used for JS scrolling. May be removed in the future.
|
||||
*/
|
||||
private _move(ev) {
|
||||
if (this._pos.length) {
|
||||
let y = pointerCoord(ev).y;
|
||||
|
||||
// ******** DOM READ ****************
|
||||
this._setMax();
|
||||
|
||||
this._top -= (y - this._pos[this._pos.length - 2]);
|
||||
|
||||
this._top = Math.min(Math.max(this._top, 0), this._max);
|
||||
|
||||
this._pos.push(y, Date.now());
|
||||
|
||||
// ******** DOM READ THEN DOM WRITE ****************
|
||||
this._cb(this._top);
|
||||
|
||||
// ******** DOM WRITE ****************
|
||||
this.setTop(this._top);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Used for JS scrolling. May be removed in the future.
|
||||
*/
|
||||
private _setMax() {
|
||||
if (!this._max) {
|
||||
// ******** DOM READ ****************
|
||||
this._max = (this._el.offsetHeight - this._el.parentElement.offsetHeight + this._el.parentElement.offsetTop);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Used for JS scrolling. May be removed in the future.
|
||||
*/
|
||||
private _end(ev) {
|
||||
// figure out what the scroll position was about 100ms ago
|
||||
let positions = this._pos;
|
||||
this._velocity = 0;
|
||||
cancelRaf(this._rafId);
|
||||
|
||||
if (!positions.length) return;
|
||||
|
||||
let y = pointerCoord(ev).y;
|
||||
|
||||
positions.push(y, Date.now());
|
||||
|
||||
let endPos = (positions.length - 1);
|
||||
let startPos = endPos;
|
||||
let timeRange = (Date.now() - 100);
|
||||
|
||||
// move pointer to position measured 100ms ago
|
||||
for (var i = endPos; i > 0 && positions[i] > timeRange; i -= 2) {
|
||||
startPos = i;
|
||||
}
|
||||
|
||||
if (startPos !== endPos) {
|
||||
// compute relative movement between these two points
|
||||
let timeOffset = (positions[endPos] - positions[startPos]);
|
||||
let movedTop = (positions[startPos - 1] - positions[endPos - 1]);
|
||||
|
||||
// based on XXms compute the movement to apply for each render step
|
||||
this._velocity = ((movedTop / timeOffset) * FRAME_MS);
|
||||
|
||||
// verify that we have enough velocity to start deceleration
|
||||
if (Math.abs(this._velocity) > MIN_VELOCITY_START_DECELERATION) {
|
||||
// ******** DOM READ ****************
|
||||
this._setMax();
|
||||
|
||||
this._rafId = nativeRaf(this._decelerate.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
positions.length = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Used for JS scrolling. May be removed in the future.
|
||||
*/
|
||||
private _decelerate() {
|
||||
var self = this;
|
||||
|
||||
if (self._velocity) {
|
||||
self._velocity *= DECELERATION_FRICTION;
|
||||
|
||||
// update top with updated velocity
|
||||
// clamp top within scroll limits
|
||||
self._top = Math.min(Math.max(self._top + self._velocity, 0), self._max);
|
||||
|
||||
// ******** DOM READ THEN DOM WRITE ****************
|
||||
self._cb(self._top);
|
||||
|
||||
// ******** DOM WRITE ****************
|
||||
self.setTop(self._top);
|
||||
|
||||
if (self._top > 0 && self._top < self._max && Math.abs(self._velocity) > MIN_VELOCITY_CONTINUE_DECELERATION) {
|
||||
self._rafId = nativeRaf(self._decelerate.bind(self));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
destroy() {
|
||||
this._velocity = 0;
|
||||
this.stop();
|
||||
this._el = null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const MAX_VELOCITY = 150;
|
||||
const MIN_VELOCITY_START_DECELERATION = 4;
|
||||
const MIN_VELOCITY_CONTINUE_DECELERATION = 0.12;
|
||||
const DECELERATION_FRICTION = 0.97;
|
||||
const FRAME_MS = (1000 / 60);
|
||||
843
src/util/test/datetime-util.spec.ts
Normal file
843
src/util/test/datetime-util.spec.ts
Normal file
@@ -0,0 +1,843 @@
|
||||
import * as datetime from '../../../src/util/datetime-util';
|
||||
|
||||
export function run() {
|
||||
|
||||
describe('convertDataToISO', () => {
|
||||
|
||||
it('should convert DateTimeData to datetime string, +330 tz offset', () => {
|
||||
var data: datetime.DateTimeData = {
|
||||
year: 1994,
|
||||
month: 12,
|
||||
day: 15,
|
||||
hour: 13,
|
||||
minute: 47,
|
||||
second: 20,
|
||||
millisecond: 789,
|
||||
tzOffset: 330,
|
||||
};
|
||||
|
||||
var str = datetime.convertDataToISO(data);
|
||||
expect(str).toEqual('1994-12-15T13:47:20.789+05:30');
|
||||
});
|
||||
|
||||
it('should convert DateTimeData to datetime string, Z timezone', () => {
|
||||
var data: datetime.DateTimeData = {
|
||||
year: 1994,
|
||||
month: 12,
|
||||
day: 15,
|
||||
hour: 13,
|
||||
minute: null,
|
||||
second: null,
|
||||
millisecond: null,
|
||||
tzOffset: 0,
|
||||
};
|
||||
|
||||
var str = datetime.convertDataToISO(data);
|
||||
expect(str).toEqual('1994-12-15T13:00:00Z');
|
||||
});
|
||||
|
||||
it('should convert DateTimeData to YYYY-MM-DD', () => {
|
||||
var data: datetime.DateTimeData = {
|
||||
year: 1994,
|
||||
month: 1,
|
||||
day: 1,
|
||||
hour: null,
|
||||
minute: null,
|
||||
second: null,
|
||||
millisecond: null,
|
||||
tzOffset: 0,
|
||||
};
|
||||
|
||||
var str = datetime.convertDataToISO(data);
|
||||
expect(str).toEqual('1994-01-01');
|
||||
});
|
||||
|
||||
it('should convert DateTimeData to YYYY-MM', () => {
|
||||
var data: datetime.DateTimeData = {
|
||||
year: 1994,
|
||||
month: 1,
|
||||
day: null,
|
||||
hour: null,
|
||||
minute: null,
|
||||
second: null,
|
||||
millisecond: null,
|
||||
tzOffset: 0,
|
||||
};
|
||||
|
||||
var str = datetime.convertDataToISO(data);
|
||||
expect(str).toEqual('1994-01');
|
||||
});
|
||||
|
||||
it('should convert DateTimeData to YYYY', () => {
|
||||
var data: datetime.DateTimeData = {
|
||||
year: 1994,
|
||||
month: null,
|
||||
day: null,
|
||||
hour: null,
|
||||
minute: null,
|
||||
second: null,
|
||||
millisecond: null,
|
||||
tzOffset: 0,
|
||||
};
|
||||
|
||||
var str = datetime.convertDataToISO(data);
|
||||
expect(str).toEqual('1994');
|
||||
});
|
||||
|
||||
it('should convert DateTimeData to HH:mm:SS.SSS', () => {
|
||||
var data: datetime.DateTimeData = {
|
||||
year: null,
|
||||
month: null,
|
||||
day: null,
|
||||
hour: 13,
|
||||
minute: 47,
|
||||
second: 20,
|
||||
millisecond: 789,
|
||||
tzOffset: 0,
|
||||
};
|
||||
|
||||
var str = datetime.convertDataToISO(data);
|
||||
expect(str).toEqual('13:47:20.789');
|
||||
});
|
||||
|
||||
it('should convert DateTimeData to HH:mm:SS string', () => {
|
||||
var data: datetime.DateTimeData = {
|
||||
year: null,
|
||||
month: null,
|
||||
day: null,
|
||||
hour: 13,
|
||||
minute: 47,
|
||||
second: 20,
|
||||
millisecond: null,
|
||||
tzOffset: 0,
|
||||
};
|
||||
|
||||
var str = datetime.convertDataToISO(data);
|
||||
expect(str).toEqual('13:47:20');
|
||||
});
|
||||
|
||||
it('should convert DateTimeData to HH:mm string', () => {
|
||||
var data: datetime.DateTimeData = {
|
||||
year: null,
|
||||
month: null,
|
||||
day: null,
|
||||
hour: 13,
|
||||
minute: 47,
|
||||
second: null,
|
||||
millisecond: null,
|
||||
tzOffset: 0,
|
||||
};
|
||||
|
||||
var str = datetime.convertDataToISO(data);
|
||||
expect(str).toEqual('13:47');
|
||||
});
|
||||
|
||||
it('should not convert DateTimeData with null data', () => {
|
||||
var data: datetime.DateTimeData = {
|
||||
year: null,
|
||||
month: null,
|
||||
day: null,
|
||||
hour: null,
|
||||
minute: null,
|
||||
second: null,
|
||||
millisecond: null,
|
||||
tzOffset: 0,
|
||||
};
|
||||
|
||||
var str = datetime.convertDataToISO(data);
|
||||
expect(str).toEqual('');
|
||||
|
||||
var str = datetime.convertDataToISO({});
|
||||
expect(str).toEqual('');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('convertFormatToKey', () => {
|
||||
|
||||
it('should convert year formats to their DateParse key', () => {
|
||||
expect(datetime.convertFormatToKey('YYYY')).toEqual('year');
|
||||
expect(datetime.convertFormatToKey('YY')).toEqual('year');
|
||||
});
|
||||
|
||||
it('should convert month formats to their DateParse key', () => {
|
||||
expect(datetime.convertFormatToKey('MMMM')).toEqual('month');
|
||||
expect(datetime.convertFormatToKey('MMM')).toEqual('month');
|
||||
expect(datetime.convertFormatToKey('MM')).toEqual('month');
|
||||
expect(datetime.convertFormatToKey('M')).toEqual('month');
|
||||
});
|
||||
|
||||
it('should convert day formats to their DateParse key', () => {
|
||||
expect(datetime.convertFormatToKey('DDDD')).toEqual('day');
|
||||
expect(datetime.convertFormatToKey('DDD')).toEqual('day');
|
||||
expect(datetime.convertFormatToKey('DD')).toEqual('day');
|
||||
expect(datetime.convertFormatToKey('D')).toEqual('day');
|
||||
});
|
||||
|
||||
it('should convert hour formats to their DateParse key', () => {
|
||||
expect(datetime.convertFormatToKey('HH')).toEqual('hour');
|
||||
expect(datetime.convertFormatToKey('H')).toEqual('hour');
|
||||
expect(datetime.convertFormatToKey('hh')).toEqual('hour');
|
||||
expect(datetime.convertFormatToKey('h')).toEqual('hour');
|
||||
});
|
||||
|
||||
it('should convert minute formats to their DateParse key', () => {
|
||||
expect(datetime.convertFormatToKey('mm')).toEqual('minute');
|
||||
expect(datetime.convertFormatToKey('m')).toEqual('minute');
|
||||
});
|
||||
|
||||
it('should convert am/pm formats to their DateParse key', () => {
|
||||
expect(datetime.convertFormatToKey('A')).toEqual('ampm');
|
||||
expect(datetime.convertFormatToKey('a')).toEqual('ampm');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('getValueFromFormat', () => {
|
||||
|
||||
it('should convert 24 hour to am value', () => {
|
||||
var d = datetime.parseDate('00:47');
|
||||
expect(datetime.getValueFromFormat(d, 'hh')).toEqual(0);
|
||||
expect(datetime.getValueFromFormat(d, 'h')).toEqual(0);
|
||||
|
||||
var d = datetime.parseDate('11:47');
|
||||
expect(datetime.getValueFromFormat(d, 'hh')).toEqual(11);
|
||||
expect(datetime.getValueFromFormat(d, 'h')).toEqual(11);
|
||||
});
|
||||
|
||||
it('should convert 24 hour to pm value', () => {
|
||||
var d = datetime.parseDate('12:47');
|
||||
expect(datetime.getValueFromFormat(d, 'hh')).toEqual(12);
|
||||
expect(datetime.getValueFromFormat(d, 'h')).toEqual(12);
|
||||
|
||||
var d = datetime.parseDate('13:47');
|
||||
expect(datetime.getValueFromFormat(d, 'hh')).toEqual(1);
|
||||
expect(datetime.getValueFromFormat(d, 'h')).toEqual(1);
|
||||
});
|
||||
|
||||
it('should convert am hours to am value', () => {
|
||||
var d = datetime.parseDate('00:47');
|
||||
expect(datetime.getValueFromFormat(d, 'A')).toEqual('am');
|
||||
expect(datetime.getValueFromFormat(d, 'a')).toEqual('am');
|
||||
|
||||
var d = datetime.parseDate('11:47');
|
||||
expect(datetime.getValueFromFormat(d, 'A')).toEqual('am');
|
||||
expect(datetime.getValueFromFormat(d, 'a')).toEqual('am');
|
||||
});
|
||||
|
||||
it('should convert pm hours to pm value', () => {
|
||||
var d = datetime.parseDate('12:47');
|
||||
expect(datetime.getValueFromFormat(d, 'A')).toEqual('pm');
|
||||
expect(datetime.getValueFromFormat(d, 'a')).toEqual('pm');
|
||||
|
||||
var d = datetime.parseDate('23:47');
|
||||
expect(datetime.getValueFromFormat(d, 'A')).toEqual('pm');
|
||||
expect(datetime.getValueFromFormat(d, 'a')).toEqual('pm');
|
||||
});
|
||||
|
||||
it('should convert date formats to values', () => {
|
||||
var d = datetime.parseDate('1994-12-15T13:47:20.789Z');
|
||||
expect(datetime.getValueFromFormat(d, 'YYYY')).toEqual(1994);
|
||||
expect(datetime.getValueFromFormat(d, 'M')).toEqual(12);
|
||||
expect(datetime.getValueFromFormat(d, 'DDDD')).toEqual(15);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('parseTemplate', () => {
|
||||
|
||||
it('should not parse D thats apart of a word', () => {
|
||||
var formats = datetime.parseTemplate('D Days');
|
||||
expect(formats.length).toEqual(1);
|
||||
expect(formats[0]).toEqual('D');
|
||||
});
|
||||
|
||||
it('should not parse D thats apart of a word', () => {
|
||||
var formats = datetime.parseTemplate('DD Days');
|
||||
expect(formats.length).toEqual(1);
|
||||
expect(formats[0]).toEqual('DD');
|
||||
});
|
||||
|
||||
it('should not parse m thats apart of a word', () => {
|
||||
var formats = datetime.parseTemplate('m mins');
|
||||
expect(formats.length).toEqual(1);
|
||||
expect(formats[0]).toEqual('m');
|
||||
});
|
||||
|
||||
it('should not parse M thats apart of a word', () => {
|
||||
var formats = datetime.parseTemplate('mm Minutes');
|
||||
expect(formats.length).toEqual(1);
|
||||
expect(formats[0]).toEqual('mm');
|
||||
});
|
||||
|
||||
it('should not pickup "a" within 12-hour, but its not the am/pm', () => {
|
||||
var formats = datetime.parseTemplate('hh:mm is a time');
|
||||
expect(formats.length).toEqual(2);
|
||||
expect(formats[0]).toEqual('hh');
|
||||
expect(formats[1]).toEqual('mm');
|
||||
});
|
||||
|
||||
it('should allow am/pm when using 12-hour and no spaces', () => {
|
||||
var formats = datetime.parseTemplate('hh:mma');
|
||||
expect(formats.length).toEqual(3);
|
||||
expect(formats[0]).toEqual('hh');
|
||||
expect(formats[1]).toEqual('mm');
|
||||
expect(formats[2]).toEqual('a');
|
||||
});
|
||||
|
||||
it('should allow am/pm when using 12-hour', () => {
|
||||
var formats = datetime.parseTemplate('hh:mm a');
|
||||
expect(formats.length).toEqual(3);
|
||||
expect(formats[0]).toEqual('hh');
|
||||
expect(formats[1]).toEqual('mm');
|
||||
expect(formats[2]).toEqual('a');
|
||||
});
|
||||
|
||||
it('should not add am/pm when not using 24-hour', () => {
|
||||
var formats = datetime.parseTemplate('HH:mm a');
|
||||
expect(formats.length).toEqual(2);
|
||||
expect(formats[0]).toEqual('HH');
|
||||
expect(formats[1]).toEqual('mm');
|
||||
});
|
||||
|
||||
it('should get formats from template "m mm h hh H HH D DD DDD DDDD M MM MMM MMMM YY YYYY"', () => {
|
||||
var formats = datetime.parseTemplate('m mm h hh H HH D DD DDD DDDD M MM MMM MMMM YY YYYY');
|
||||
expect(formats[0]).toEqual('m');
|
||||
expect(formats[1]).toEqual('mm');
|
||||
expect(formats[2]).toEqual('h');
|
||||
expect(formats[3]).toEqual('hh');
|
||||
expect(formats[4]).toEqual('H');
|
||||
expect(formats[5]).toEqual('HH');
|
||||
expect(formats[6]).toEqual('D');
|
||||
expect(formats[7]).toEqual('DD');
|
||||
expect(formats[8]).toEqual('DDD');
|
||||
expect(formats[9]).toEqual('DDDD');
|
||||
expect(formats[10]).toEqual('M');
|
||||
expect(formats[11]).toEqual('MM');
|
||||
expect(formats[12]).toEqual('MMM');
|
||||
expect(formats[13]).toEqual('MMMM');
|
||||
expect(formats[14]).toEqual('YY');
|
||||
expect(formats[15]).toEqual('YYYY');
|
||||
});
|
||||
|
||||
it('should get formats from template YYMMMMDDHHmm', () => {
|
||||
var formats = datetime.parseTemplate('YYMMMMDDHHmm');
|
||||
expect(formats[0]).toEqual('YY');
|
||||
expect(formats[1]).toEqual('MMMM');
|
||||
expect(formats[2]).toEqual('DD');
|
||||
expect(formats[3]).toEqual('HH');
|
||||
expect(formats[4]).toEqual('mm');
|
||||
});
|
||||
|
||||
it('should get formats from template MM/DD/YYYY', () => {
|
||||
var formats = datetime.parseTemplate('MM/DD/YYYY');
|
||||
expect(formats[0]).toEqual('MM');
|
||||
expect(formats[1]).toEqual('DD');
|
||||
expect(formats[2]).toEqual('YYYY');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('renderDateTime', () => {
|
||||
|
||||
it('should show correct month and day name defaults', () => {
|
||||
var d = datetime.parseDate('2016-05-12');
|
||||
var r = datetime.renderDateTime('DDDD MMM D YYYY', d, {});
|
||||
expect(r).toEqual('Thursday May 12 2016');
|
||||
});
|
||||
|
||||
it('should format h:mm a, PM', () => {
|
||||
var d = datetime.parseDate('1994-12-15T13:47:20.789Z');
|
||||
expect(datetime.renderDateTime('h:mm a', d, {})).toEqual('1:47 pm');
|
||||
});
|
||||
|
||||
it('should get empty text for format without data', () => {
|
||||
var emptyObj = {};
|
||||
expect(datetime.renderDateTime('MMMM D, YYYY h:mm a', emptyObj, {})).toEqual('');
|
||||
|
||||
var dataWithNulls: datetime.DateTimeData = {
|
||||
year: null,
|
||||
month: null,
|
||||
day: null,
|
||||
hour: null,
|
||||
minute: null,
|
||||
second: null,
|
||||
millisecond: null,
|
||||
tzOffset: 0,
|
||||
};
|
||||
expect(datetime.renderDateTime('MMMM D, YYYY h:mm a', dataWithNulls, {})).toEqual('');
|
||||
});
|
||||
|
||||
it('should format h:mm a, AM', () => {
|
||||
var d = datetime.parseDate('1994-12-15T00:47:20.789Z');
|
||||
expect(datetime.renderDateTime('h:mm a', d, {})).toEqual('12:47 am');
|
||||
});
|
||||
|
||||
it('should format HH:mm', () => {
|
||||
var d = datetime.parseDate('1994-12-15T13:47:20.789Z');
|
||||
expect(datetime.renderDateTime('HH:mm', d, {})).toEqual('13:47');
|
||||
});
|
||||
|
||||
it('should format MMMM D, YYYY', () => {
|
||||
var d = datetime.parseDate('1994-12-15T13:47:20.789Z');
|
||||
expect(datetime.renderDateTime('MMMM D, YYYY', d, {})).toEqual('December 15, 1994');
|
||||
});
|
||||
|
||||
it('should format MM/DD/YYYY', () => {
|
||||
var d = datetime.parseDate('1994-12-15T13:47:20.789Z');
|
||||
expect(datetime.renderDateTime('MM/DD/YYYY', d, {})).toEqual('12/15/1994');
|
||||
});
|
||||
|
||||
it('should format DD-MM-YY', () => {
|
||||
var d = datetime.parseDate('1994-12-15T13:47:20.789Z');
|
||||
expect(datetime.renderDateTime('DD-MM-YY', d, {})).toEqual('15-12-94');
|
||||
});
|
||||
|
||||
it('should format YYYY', () => {
|
||||
var d = datetime.parseDate('1994-12-15T13:47:20.789Z');
|
||||
expect(datetime.renderDateTime('DD-MM-YY', d, {})).toEqual('15-12-94');
|
||||
});
|
||||
|
||||
it('should format YYYY$MM.DD*HH?mm', () => {
|
||||
var d = datetime.parseDate('1994-12-15T13:47:20.789Z');
|
||||
expect(datetime.renderDateTime('YYYY$MM.DD*HH?mm', d, {})).toEqual('1994$12.15*13?47');
|
||||
});
|
||||
|
||||
it('should return empty when template invalid', () => {
|
||||
var d = datetime.parseDate('1994-12-15T13:47:20.789Z');
|
||||
expect(datetime.renderDateTime('', d, {})).toEqual('');
|
||||
});
|
||||
|
||||
it('should return empty when date invalid', () => {
|
||||
var d = datetime.parseDate(null);
|
||||
expect(datetime.renderDateTime('YYYY', d, {})).toEqual('');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('renderTextFormat', () => {
|
||||
|
||||
it('should return a', () => {
|
||||
var d = datetime.parseDate('00:47');
|
||||
expect(datetime.renderTextFormat('a', 'am', d, {})).toEqual('am');
|
||||
expect(datetime.renderTextFormat('a', 'am', null, {})).toEqual('am');
|
||||
|
||||
var d = datetime.parseDate('11:47');
|
||||
expect(datetime.renderTextFormat('a', 'am', d, {})).toEqual('am');
|
||||
expect(datetime.renderTextFormat('a', 'am', null, {})).toEqual('am');
|
||||
|
||||
var d = datetime.parseDate('12:47');
|
||||
expect(datetime.renderTextFormat('a', 'pm', d, {})).toEqual('pm');
|
||||
expect(datetime.renderTextFormat('a', 'pm', null, {})).toEqual('pm');
|
||||
});
|
||||
|
||||
it('should return A', () => {
|
||||
var d = datetime.parseDate('00:47');
|
||||
expect(datetime.renderTextFormat('A', 'am', d, {})).toEqual('AM');
|
||||
expect(datetime.renderTextFormat('A', 'am', null, {})).toEqual('AM');
|
||||
|
||||
var d = datetime.parseDate('11:47');
|
||||
expect(datetime.renderTextFormat('A', 'am', d, {})).toEqual('AM');
|
||||
expect(datetime.renderTextFormat('A', 'am', null, {})).toEqual('AM');
|
||||
|
||||
var d = datetime.parseDate('12:47');
|
||||
expect(datetime.renderTextFormat('A', 'pm', d, {})).toEqual('PM');
|
||||
expect(datetime.renderTextFormat('A', 'pm', null, {})).toEqual('PM');
|
||||
});
|
||||
|
||||
it('should return m', () => {
|
||||
expect(datetime.renderTextFormat('m', 1, null, {})).toEqual('1');
|
||||
expect(datetime.renderTextFormat('m', 12, null, {})).toEqual('12');
|
||||
});
|
||||
|
||||
it('should return mm', () => {
|
||||
expect(datetime.renderTextFormat('mm', 1, null, {})).toEqual('01');
|
||||
expect(datetime.renderTextFormat('mm', 12, null, {})).toEqual('12');
|
||||
});
|
||||
|
||||
it('should return hh', () => {
|
||||
expect(datetime.renderTextFormat('hh', 0, null, {})).toEqual('12');
|
||||
expect(datetime.renderTextFormat('hh', 1, null, {})).toEqual('01');
|
||||
expect(datetime.renderTextFormat('hh', 11, null, {})).toEqual('11');
|
||||
expect(datetime.renderTextFormat('hh', 12, null, {})).toEqual('12');
|
||||
expect(datetime.renderTextFormat('hh', 13, null, {})).toEqual('01');
|
||||
expect(datetime.renderTextFormat('hh', 21, null, {})).toEqual('09');
|
||||
expect(datetime.renderTextFormat('hh', 23, null, {})).toEqual('11');
|
||||
});
|
||||
|
||||
it('should return h', () => {
|
||||
expect(datetime.renderTextFormat('h', 0, null, {})).toEqual('12');
|
||||
expect(datetime.renderTextFormat('h', 1, null, {})).toEqual('1');
|
||||
expect(datetime.renderTextFormat('h', 11, null, {})).toEqual('11');
|
||||
expect(datetime.renderTextFormat('h', 12, null, {})).toEqual('12');
|
||||
expect(datetime.renderTextFormat('h', 13, null, {})).toEqual('1');
|
||||
expect(datetime.renderTextFormat('h', 21, null, {})).toEqual('9');
|
||||
expect(datetime.renderTextFormat('h', 23, null, {})).toEqual('11');
|
||||
});
|
||||
|
||||
it('should return hh', () => {
|
||||
expect(datetime.renderTextFormat('hh', 1, null, {})).toEqual('01');
|
||||
expect(datetime.renderTextFormat('hh', 12, null, {})).toEqual('12');
|
||||
});
|
||||
|
||||
it('should return H', () => {
|
||||
expect(datetime.renderTextFormat('H', 1, null, {})).toEqual('1');
|
||||
expect(datetime.renderTextFormat('H', 12, null, {})).toEqual('12');
|
||||
});
|
||||
|
||||
it('should return HH', () => {
|
||||
expect(datetime.renderTextFormat('HH', 1, null, {})).toEqual('01');
|
||||
expect(datetime.renderTextFormat('HH', 12, null, {})).toEqual('12');
|
||||
});
|
||||
|
||||
it('should return D', () => {
|
||||
expect(datetime.renderTextFormat('D', 1, null, {})).toEqual('1');
|
||||
expect(datetime.renderTextFormat('D', 12, null, {})).toEqual('12');
|
||||
});
|
||||
|
||||
it('should return DD', () => {
|
||||
expect(datetime.renderTextFormat('DD', 1, null, {})).toEqual('01');
|
||||
expect(datetime.renderTextFormat('DD', 12, null, {})).toEqual('12');
|
||||
});
|
||||
|
||||
it('should return DDD', () => {
|
||||
var d: datetime.DateTimeData = {
|
||||
year: 2016,
|
||||
month: 5,
|
||||
day: 12,
|
||||
};
|
||||
|
||||
expect(datetime.renderTextFormat('DDD', null, d, {})).toEqual('Thu');
|
||||
});
|
||||
|
||||
it('should return DDD with custom locale', () => {
|
||||
var d: datetime.DateTimeData = {
|
||||
year: 2016,
|
||||
month: 5,
|
||||
day: 12,
|
||||
};
|
||||
|
||||
expect(datetime.renderTextFormat('DDD', null, d, customLocale)).toEqual('qui');
|
||||
});
|
||||
|
||||
it('should return DDDD', () => {
|
||||
var d: datetime.DateTimeData = {
|
||||
year: 2016,
|
||||
month: 5,
|
||||
day: 12,
|
||||
};
|
||||
|
||||
expect(datetime.renderTextFormat('DDDD', null, d, {})).toEqual('Thursday');
|
||||
});
|
||||
|
||||
it('should return DDDD with custom locale', () => {
|
||||
var d: datetime.DateTimeData = {
|
||||
year: 2016,
|
||||
month: 5,
|
||||
day: 12,
|
||||
};
|
||||
|
||||
expect(datetime.renderTextFormat('DDDD', null, d, customLocale)).toEqual('quinta-feira');
|
||||
});
|
||||
|
||||
it('should return M', () => {
|
||||
expect(datetime.renderTextFormat('M', 1, null, {})).toEqual('1');
|
||||
expect(datetime.renderTextFormat('M', 12, null, {})).toEqual('12');
|
||||
});
|
||||
|
||||
it('should return MM', () => {
|
||||
expect(datetime.renderTextFormat('MM', 1, null, {})).toEqual('01');
|
||||
expect(datetime.renderTextFormat('MM', 12, null, {})).toEqual('12');
|
||||
});
|
||||
|
||||
it('should return MMM', () => {
|
||||
expect(datetime.renderTextFormat('MMM', 1, null, {})).toEqual('Jan');
|
||||
expect(datetime.renderTextFormat('MMM', 12, null, {})).toEqual('Dec');
|
||||
});
|
||||
|
||||
it('should return MMM with custom locale', () => {
|
||||
expect(datetime.renderTextFormat('MMM', 1, null, customLocale)).toEqual('jan');
|
||||
});
|
||||
|
||||
it('should return MMMM', () => {
|
||||
expect(datetime.renderTextFormat('MMMM', 1, null, {})).toEqual('January');
|
||||
expect(datetime.renderTextFormat('MMMM', 12, null, {})).toEqual('December');
|
||||
});
|
||||
|
||||
it('should return MMMM with custom locale', () => {
|
||||
expect(datetime.renderTextFormat('MMMM', 1, null, customLocale)).toEqual('janeiro');
|
||||
});
|
||||
|
||||
it('should return YY', () => {
|
||||
expect(datetime.renderTextFormat('YY', 1994, null, {})).toEqual('94');
|
||||
expect(datetime.renderTextFormat('YY', 94, null, {})).toEqual('94');
|
||||
});
|
||||
|
||||
it('should return YYYY', () => {
|
||||
expect(datetime.renderTextFormat('YYYY', 1994, null, {})).toEqual('1994');
|
||||
expect(datetime.renderTextFormat('YYYY', 0, null, {})).toEqual('0000');
|
||||
});
|
||||
|
||||
it('should return empty when blank', () => {
|
||||
expect(datetime.renderTextFormat(null, null, null, {})).toEqual('');
|
||||
expect(datetime.renderTextFormat(null, 1994, null, {})).toEqual('1994');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('parseISODate', () => {
|
||||
|
||||
it('should get HH:MM:SS.SSS+HH:MM', () => {
|
||||
var parsed = datetime.parseDate('13:47:20.789+05:30');
|
||||
expect(parsed.year).toEqual(null);
|
||||
expect(parsed.month).toEqual(null);
|
||||
expect(parsed.day).toEqual(null);
|
||||
expect(parsed.hour).toEqual(13);
|
||||
expect(parsed.minute).toEqual(47);
|
||||
expect(parsed.second).toEqual(20);
|
||||
expect(parsed.millisecond).toEqual(789);
|
||||
expect(parsed.tzOffset).toEqual(330);
|
||||
});
|
||||
|
||||
it('should get HH:MM:SS.SSS', () => {
|
||||
var parsed = datetime.parseDate('13:47:20.789');
|
||||
expect(parsed.year).toEqual(null);
|
||||
expect(parsed.month).toEqual(null);
|
||||
expect(parsed.day).toEqual(null);
|
||||
expect(parsed.hour).toEqual(13);
|
||||
expect(parsed.minute).toEqual(47);
|
||||
expect(parsed.second).toEqual(20);
|
||||
expect(parsed.millisecond).toEqual(789);
|
||||
expect(parsed.tzOffset).toEqual(0);
|
||||
});
|
||||
|
||||
it('should get HH:MM:SS', () => {
|
||||
var parsed = datetime.parseDate('13:47:20');
|
||||
expect(parsed.year).toEqual(null);
|
||||
expect(parsed.month).toEqual(null);
|
||||
expect(parsed.day).toEqual(null);
|
||||
expect(parsed.hour).toEqual(13);
|
||||
expect(parsed.minute).toEqual(47);
|
||||
expect(parsed.second).toEqual(20);
|
||||
expect(parsed.millisecond).toEqual(null);
|
||||
expect(parsed.tzOffset).toEqual(0);
|
||||
});
|
||||
|
||||
it('should get HH:MM', () => {
|
||||
var parsed = datetime.parseDate('13:47');
|
||||
expect(parsed.year).toEqual(null);
|
||||
expect(parsed.month).toEqual(null);
|
||||
expect(parsed.day).toEqual(null);
|
||||
expect(parsed.hour).toEqual(13);
|
||||
expect(parsed.minute).toEqual(47);
|
||||
expect(parsed.second).toEqual(null);
|
||||
expect(parsed.millisecond).toEqual(null);
|
||||
expect(parsed.tzOffset).toEqual(0);
|
||||
});
|
||||
|
||||
it('should get YYYY-MM-DDTHH:MM:SS.SSS+HH:MM', () => {
|
||||
var parsed = datetime.parseDate('1994-12-15T13:47:20.789+05:30');
|
||||
expect(parsed.year).toEqual(1994);
|
||||
expect(parsed.month).toEqual(12);
|
||||
expect(parsed.day).toEqual(15);
|
||||
expect(parsed.hour).toEqual(13);
|
||||
expect(parsed.minute).toEqual(47);
|
||||
expect(parsed.second).toEqual(20);
|
||||
expect(parsed.millisecond).toEqual(789);
|
||||
expect(parsed.tzOffset).toEqual(330);
|
||||
});
|
||||
|
||||
it('should get YYYY-MM-DDTHH:MM:SS.SSS-HH:MM', () => {
|
||||
var parsed = datetime.parseDate('1994-12-15T13:47:20.789-11:45');
|
||||
expect(parsed.year).toEqual(1994);
|
||||
expect(parsed.month).toEqual(12);
|
||||
expect(parsed.day).toEqual(15);
|
||||
expect(parsed.hour).toEqual(13);
|
||||
expect(parsed.minute).toEqual(47);
|
||||
expect(parsed.second).toEqual(20);
|
||||
expect(parsed.millisecond).toEqual(789);
|
||||
expect(parsed.tzOffset).toEqual(-705);
|
||||
});
|
||||
|
||||
it('should get YYYY-MM-DDTHH:MM:SS.SSS-HH', () => {
|
||||
var parsed = datetime.parseDate('1994-12-15T13:47:20.789-02');
|
||||
expect(parsed.year).toEqual(1994);
|
||||
expect(parsed.month).toEqual(12);
|
||||
expect(parsed.day).toEqual(15);
|
||||
expect(parsed.hour).toEqual(13);
|
||||
expect(parsed.minute).toEqual(47);
|
||||
expect(parsed.second).toEqual(20);
|
||||
expect(parsed.millisecond).toEqual(789);
|
||||
expect(parsed.tzOffset).toEqual(-120);
|
||||
});
|
||||
|
||||
it('should get YYYY-MM-DDTHH:MM:SS.SSSZ and set UTC offset', () => {
|
||||
var parsed = datetime.parseDate('1994-12-15T13:47:20.789Z');
|
||||
expect(parsed.year).toEqual(1994);
|
||||
expect(parsed.month).toEqual(12);
|
||||
expect(parsed.day).toEqual(15);
|
||||
expect(parsed.hour).toEqual(13);
|
||||
expect(parsed.minute).toEqual(47);
|
||||
expect(parsed.second).toEqual(20);
|
||||
expect(parsed.millisecond).toEqual(789);
|
||||
expect(parsed.tzOffset).toEqual(0);
|
||||
});
|
||||
|
||||
it('should get YYYY-MM-DDTHH:MM:SS', () => {
|
||||
var parsed = datetime.parseDate('1994-12-15T13:47:20');
|
||||
expect(parsed.year).toEqual(1994);
|
||||
expect(parsed.month).toEqual(12);
|
||||
expect(parsed.day).toEqual(15);
|
||||
expect(parsed.hour).toEqual(13);
|
||||
expect(parsed.minute).toEqual(47);
|
||||
expect(parsed.second).toEqual(20);
|
||||
expect(parsed.millisecond).toEqual(null);
|
||||
expect(parsed.tzOffset).toEqual(0);
|
||||
});
|
||||
|
||||
it('should get YYYY-MM-DDTHH:MM', () => {
|
||||
var parsed = datetime.parseDate('1994-12-15T13:47');
|
||||
expect(parsed.year).toEqual(1994);
|
||||
expect(parsed.month).toEqual(12);
|
||||
expect(parsed.day).toEqual(15);
|
||||
expect(parsed.hour).toEqual(13);
|
||||
expect(parsed.minute).toEqual(47);
|
||||
expect(parsed.second).toEqual(null);
|
||||
expect(parsed.millisecond).toEqual(null);
|
||||
expect(parsed.tzOffset).toEqual(0);
|
||||
});
|
||||
|
||||
it('should NOT work with YYYY-MM-DDTHH', () => {
|
||||
var parsed = datetime.parseDate('1994-12-15T13');
|
||||
expect(parsed).toEqual(null);
|
||||
});
|
||||
|
||||
it('should get YYYY-MM-DD', () => {
|
||||
var parsed = datetime.parseDate('1994-12-15');
|
||||
expect(parsed.year).toEqual(1994);
|
||||
expect(parsed.month).toEqual(12);
|
||||
expect(parsed.day).toEqual(15);
|
||||
expect(parsed.hour).toEqual(null);
|
||||
expect(parsed.minute).toEqual(null);
|
||||
expect(parsed.second).toEqual(null);
|
||||
expect(parsed.millisecond).toEqual(null);
|
||||
expect(parsed.tzOffset).toEqual(0);
|
||||
});
|
||||
|
||||
it('should get YYYY-MM', () => {
|
||||
var parsed = datetime.parseDate('1994-12');
|
||||
expect(parsed.year).toEqual(1994);
|
||||
expect(parsed.month).toEqual(12);
|
||||
expect(parsed.day).toEqual(null);
|
||||
expect(parsed.hour).toEqual(null);
|
||||
expect(parsed.minute).toEqual(null);
|
||||
expect(parsed.second).toEqual(null);
|
||||
expect(parsed.millisecond).toEqual(null);
|
||||
expect(parsed.tzOffset).toEqual(0);
|
||||
});
|
||||
|
||||
it('should get YYYY', () => {
|
||||
var parsed = datetime.parseDate('1994');
|
||||
expect(parsed.year).toEqual(1994);
|
||||
expect(parsed.month).toEqual(null);
|
||||
expect(parsed.day).toEqual(null);
|
||||
expect(parsed.hour).toEqual(null);
|
||||
expect(parsed.minute).toEqual(null);
|
||||
expect(parsed.second).toEqual(null);
|
||||
expect(parsed.millisecond).toEqual(null);
|
||||
expect(parsed.tzOffset).toEqual(0);
|
||||
});
|
||||
|
||||
it('should handle bad date formats', () => {
|
||||
var parsed = datetime.parseDate('12/15/1994');
|
||||
expect(parsed).toEqual(null);
|
||||
|
||||
var parsed = datetime.parseDate('12-15-1994');
|
||||
expect(parsed).toEqual(null);
|
||||
|
||||
var parsed = datetime.parseDate('1994-1994');
|
||||
expect(parsed).toEqual(null);
|
||||
|
||||
var parsed = datetime.parseDate('1994 12 15');
|
||||
expect(parsed).toEqual(null);
|
||||
|
||||
var parsed = datetime.parseDate('12.15.1994');
|
||||
expect(parsed).toEqual(null);
|
||||
|
||||
var parsed = datetime.parseDate('12\\15\\1994');
|
||||
expect(parsed).toEqual(null);
|
||||
|
||||
var parsed = datetime.parseDate('200');
|
||||
expect(parsed).toEqual(null);
|
||||
|
||||
var parsed = datetime.parseDate('holla');
|
||||
expect(parsed).toEqual(null);
|
||||
});
|
||||
|
||||
it('should get nothing with null date', () => {
|
||||
var parsed = datetime.parseDate(null);
|
||||
expect(parsed).toEqual(null);
|
||||
|
||||
var parsed = datetime.parseDate(undefined);
|
||||
expect(parsed).toEqual(null);
|
||||
|
||||
var parsed = datetime.parseDate('');
|
||||
expect(parsed).toEqual(null);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// pt-br
|
||||
var customLocale: datetime.LocaleData = {
|
||||
dayNames: [
|
||||
'domingo',
|
||||
'segunda-feira',
|
||||
'ter\u00e7a-feira',
|
||||
'quarta-feira',
|
||||
'quinta-feira',
|
||||
'sexta-feira',
|
||||
's\u00e1bado'
|
||||
],
|
||||
dayShortNames: [
|
||||
'dom',
|
||||
'seg',
|
||||
'ter',
|
||||
'qua',
|
||||
'qui',
|
||||
'sex',
|
||||
's\u00e1b'
|
||||
],
|
||||
monthNames: [
|
||||
'janeiro',
|
||||
'fevereiro',
|
||||
'mar\u00e7o',
|
||||
'abril',
|
||||
'maio',
|
||||
'junho',
|
||||
'julho',
|
||||
'agosto',
|
||||
'setembro',
|
||||
'outubro',
|
||||
'novembro',
|
||||
'dezembro'
|
||||
],
|
||||
monthShortNames: [
|
||||
'jan',
|
||||
'fev',
|
||||
'mar',
|
||||
'abr',
|
||||
'mai',
|
||||
'jun',
|
||||
'jul',
|
||||
'ago',
|
||||
'set',
|
||||
'out',
|
||||
'nov',
|
||||
'dez'
|
||||
],
|
||||
};
|
||||
|
||||
}
|
||||
400
src/util/test/util.spec.ts
Normal file
400
src/util/test/util.spec.ts
Normal file
@@ -0,0 +1,400 @@
|
||||
import * as util from '../../../src/util';
|
||||
|
||||
export function run() {
|
||||
|
||||
describe('isCheckedProperty', () => {
|
||||
|
||||
it('should test a=undefined', () => {
|
||||
expect(util.isCheckedProperty(undefined, undefined)).toBe(true);
|
||||
expect(util.isCheckedProperty(undefined, null)).toBe(true);
|
||||
expect(util.isCheckedProperty(undefined, '')).toBe(true);
|
||||
expect(util.isCheckedProperty(undefined, 'string')).toBe(false);
|
||||
expect(util.isCheckedProperty(undefined, 0)).toBe(false);
|
||||
expect(util.isCheckedProperty(undefined, '0')).toBe(false);
|
||||
expect(util.isCheckedProperty(undefined, 1000)).toBe(false);
|
||||
expect(util.isCheckedProperty(undefined, '1000')).toBe(false);
|
||||
expect(util.isCheckedProperty(undefined, -1)).toBe(false);
|
||||
expect(util.isCheckedProperty(undefined, true)).toBe(false);
|
||||
expect(util.isCheckedProperty(undefined, false)).toBe(false);
|
||||
expect(util.isCheckedProperty(undefined, 'true')).toBe(false);
|
||||
expect(util.isCheckedProperty(undefined, 'false')).toBe(false);
|
||||
});
|
||||
|
||||
it('should test a=null', () => {
|
||||
expect(util.isCheckedProperty(null, undefined)).toBe(true);
|
||||
expect(util.isCheckedProperty(null, null)).toBe(true);
|
||||
expect(util.isCheckedProperty(null, '')).toBe(true);
|
||||
expect(util.isCheckedProperty(null, 'string')).toBe(false);
|
||||
expect(util.isCheckedProperty(null, 0)).toBe(false);
|
||||
expect(util.isCheckedProperty(null, '0')).toBe(false);
|
||||
expect(util.isCheckedProperty(null, 1000)).toBe(false);
|
||||
expect(util.isCheckedProperty(null, '1000')).toBe(false);
|
||||
expect(util.isCheckedProperty(null, -1)).toBe(false);
|
||||
expect(util.isCheckedProperty(null, true)).toBe(false);
|
||||
expect(util.isCheckedProperty(null, false)).toBe(false);
|
||||
expect(util.isCheckedProperty(null, 'true')).toBe(false);
|
||||
expect(util.isCheckedProperty(null, 'false')).toBe(false);
|
||||
});
|
||||
|
||||
it('should test a=""', () => {
|
||||
expect(util.isCheckedProperty('', undefined)).toBe(true);
|
||||
expect(util.isCheckedProperty('', null)).toBe(true);
|
||||
expect(util.isCheckedProperty('', '')).toBe(true);
|
||||
expect(util.isCheckedProperty('', 'string')).toBe(false);
|
||||
expect(util.isCheckedProperty('', 0)).toBe(false);
|
||||
expect(util.isCheckedProperty('', '0')).toBe(false);
|
||||
expect(util.isCheckedProperty('', 1000)).toBe(false);
|
||||
expect(util.isCheckedProperty('', '1000')).toBe(false);
|
||||
expect(util.isCheckedProperty('', -1)).toBe(false);
|
||||
expect(util.isCheckedProperty('', true)).toBe(false);
|
||||
expect(util.isCheckedProperty('', false)).toBe(false);
|
||||
expect(util.isCheckedProperty('', 'true')).toBe(false);
|
||||
expect(util.isCheckedProperty('', 'false')).toBe(false);
|
||||
});
|
||||
|
||||
it('should test a=true', () => {
|
||||
expect(util.isCheckedProperty(true, undefined)).toBe(false);
|
||||
expect(util.isCheckedProperty(true, null)).toBe(false);
|
||||
expect(util.isCheckedProperty(true, '')).toBe(false);
|
||||
expect(util.isCheckedProperty(true, 'string')).toBe(false);
|
||||
expect(util.isCheckedProperty(true, 0)).toBe(false);
|
||||
expect(util.isCheckedProperty(true, '0')).toBe(false);
|
||||
expect(util.isCheckedProperty(true, 1000)).toBe(false);
|
||||
expect(util.isCheckedProperty(true, '1000')).toBe(false);
|
||||
expect(util.isCheckedProperty(true, -1)).toBe(false);
|
||||
expect(util.isCheckedProperty(true, true)).toBe(true);
|
||||
expect(util.isCheckedProperty(true, false)).toBe(false);
|
||||
expect(util.isCheckedProperty(true, 'true')).toBe(true);
|
||||
expect(util.isCheckedProperty(true, 'false')).toBe(false);
|
||||
});
|
||||
|
||||
it('should test a="true"', () => {
|
||||
expect(util.isCheckedProperty('true', undefined)).toBe(false);
|
||||
expect(util.isCheckedProperty('true', null)).toBe(false);
|
||||
expect(util.isCheckedProperty('true', '')).toBe(false);
|
||||
expect(util.isCheckedProperty('true', 'string')).toBe(false);
|
||||
expect(util.isCheckedProperty('true', 0)).toBe(false);
|
||||
expect(util.isCheckedProperty('true', '0')).toBe(false);
|
||||
expect(util.isCheckedProperty('true', 1000)).toBe(false);
|
||||
expect(util.isCheckedProperty('true', '1000')).toBe(false);
|
||||
expect(util.isCheckedProperty('true', -1)).toBe(false);
|
||||
expect(util.isCheckedProperty('true', true)).toBe(true);
|
||||
expect(util.isCheckedProperty('true', false)).toBe(false);
|
||||
expect(util.isCheckedProperty('true', 'true')).toBe(true);
|
||||
expect(util.isCheckedProperty('true', 'false')).toBe(false);
|
||||
});
|
||||
|
||||
it('should test a=false', () => {
|
||||
expect(util.isCheckedProperty(false, undefined)).toBe(false);
|
||||
expect(util.isCheckedProperty(false, null)).toBe(false);
|
||||
expect(util.isCheckedProperty(false, '')).toBe(false);
|
||||
expect(util.isCheckedProperty(false, 'string')).toBe(false);
|
||||
expect(util.isCheckedProperty(false, 0)).toBe(false);
|
||||
expect(util.isCheckedProperty(false, '0')).toBe(false);
|
||||
expect(util.isCheckedProperty(false, 1000)).toBe(false);
|
||||
expect(util.isCheckedProperty(false, '1000')).toBe(false);
|
||||
expect(util.isCheckedProperty(false, -1)).toBe(false);
|
||||
expect(util.isCheckedProperty(false, true)).toBe(false);
|
||||
expect(util.isCheckedProperty(false, false)).toBe(true);
|
||||
expect(util.isCheckedProperty(false, 'true')).toBe(false);
|
||||
expect(util.isCheckedProperty(false, 'false')).toBe(true);
|
||||
});
|
||||
|
||||
it('should test a="false"', () => {
|
||||
expect(util.isCheckedProperty('false', undefined)).toBe(false);
|
||||
expect(util.isCheckedProperty('false', null)).toBe(false);
|
||||
expect(util.isCheckedProperty('false', '')).toBe(false);
|
||||
expect(util.isCheckedProperty('false', 'string')).toBe(false);
|
||||
expect(util.isCheckedProperty('false', 0)).toBe(false);
|
||||
expect(util.isCheckedProperty('false', '0')).toBe(false);
|
||||
expect(util.isCheckedProperty('false', 1000)).toBe(false);
|
||||
expect(util.isCheckedProperty('false', '1000')).toBe(false);
|
||||
expect(util.isCheckedProperty('false', -1)).toBe(false);
|
||||
expect(util.isCheckedProperty('false', true)).toBe(false);
|
||||
expect(util.isCheckedProperty('false', false)).toBe(true);
|
||||
expect(util.isCheckedProperty('false', 'true')).toBe(false);
|
||||
expect(util.isCheckedProperty('false', 'false')).toBe(true);
|
||||
});
|
||||
|
||||
it('should test a=0', () => {
|
||||
expect(util.isCheckedProperty(0, undefined)).toBe(false);
|
||||
expect(util.isCheckedProperty(0, null)).toBe(false);
|
||||
expect(util.isCheckedProperty(0, '')).toBe(false);
|
||||
expect(util.isCheckedProperty(0, 'string')).toBe(false);
|
||||
expect(util.isCheckedProperty(0, 0)).toBe(true);
|
||||
expect(util.isCheckedProperty(0, '0')).toBe(true);
|
||||
expect(util.isCheckedProperty(0, 1000)).toBe(false);
|
||||
expect(util.isCheckedProperty(0, '1000')).toBe(false);
|
||||
expect(util.isCheckedProperty(0, -1)).toBe(false);
|
||||
expect(util.isCheckedProperty(0, true)).toBe(false);
|
||||
expect(util.isCheckedProperty(0, false)).toBe(false);
|
||||
expect(util.isCheckedProperty(0, 'true')).toBe(false);
|
||||
expect(util.isCheckedProperty(0, 'false')).toBe(false);
|
||||
});
|
||||
|
||||
it('should test a="0"', () => {
|
||||
expect(util.isCheckedProperty('0', undefined)).toBe(false);
|
||||
expect(util.isCheckedProperty('0', null)).toBe(false);
|
||||
expect(util.isCheckedProperty('0', '')).toBe(false);
|
||||
expect(util.isCheckedProperty('0', 'string')).toBe(false);
|
||||
expect(util.isCheckedProperty('0', 0)).toBe(true);
|
||||
expect(util.isCheckedProperty('0', '0')).toBe(true);
|
||||
expect(util.isCheckedProperty('0', 1000)).toBe(false);
|
||||
expect(util.isCheckedProperty('0', '1000')).toBe(false);
|
||||
expect(util.isCheckedProperty('0', -1)).toBe(false);
|
||||
expect(util.isCheckedProperty('0', true)).toBe(false);
|
||||
expect(util.isCheckedProperty('0', false)).toBe(false);
|
||||
expect(util.isCheckedProperty('0', 'true')).toBe(false);
|
||||
expect(util.isCheckedProperty('0', 'false')).toBe(false);
|
||||
});
|
||||
|
||||
it('should test a=1000', () => {
|
||||
expect(util.isCheckedProperty(1000, undefined)).toBe(false);
|
||||
expect(util.isCheckedProperty(1000, null)).toBe(false);
|
||||
expect(util.isCheckedProperty(1000, '')).toBe(false);
|
||||
expect(util.isCheckedProperty(1000, 'string')).toBe(false);
|
||||
expect(util.isCheckedProperty(1000, 0)).toBe(false);
|
||||
expect(util.isCheckedProperty(1000, '0')).toBe(false);
|
||||
expect(util.isCheckedProperty(1000, 1000)).toBe(true);
|
||||
expect(util.isCheckedProperty(1000, '1000')).toBe(true);
|
||||
expect(util.isCheckedProperty(1000, -1)).toBe(false);
|
||||
expect(util.isCheckedProperty(1000, true)).toBe(false);
|
||||
expect(util.isCheckedProperty(1000, false)).toBe(false);
|
||||
expect(util.isCheckedProperty(1000, 'true')).toBe(false);
|
||||
expect(util.isCheckedProperty(1000, 'false')).toBe(false);
|
||||
});
|
||||
|
||||
it('should test a="1000"', () => {
|
||||
expect(util.isCheckedProperty('1000', undefined)).toBe(false);
|
||||
expect(util.isCheckedProperty('1000', null)).toBe(false);
|
||||
expect(util.isCheckedProperty('1000', '')).toBe(false);
|
||||
expect(util.isCheckedProperty('1000', 'string')).toBe(false);
|
||||
expect(util.isCheckedProperty('1000', 0)).toBe(false);
|
||||
expect(util.isCheckedProperty('1000', '0')).toBe(false);
|
||||
expect(util.isCheckedProperty('1000', 1000)).toBe(true);
|
||||
expect(util.isCheckedProperty('1000', '1000')).toBe(true);
|
||||
expect(util.isCheckedProperty('1000', -1)).toBe(false);
|
||||
expect(util.isCheckedProperty('1000', true)).toBe(false);
|
||||
expect(util.isCheckedProperty('1000', false)).toBe(false);
|
||||
expect(util.isCheckedProperty('1000', 'true')).toBe(false);
|
||||
expect(util.isCheckedProperty('1000', 'false')).toBe(false);
|
||||
});
|
||||
|
||||
it('should test a=-1', () => {
|
||||
expect(util.isCheckedProperty(-1, undefined)).toBe(false);
|
||||
expect(util.isCheckedProperty(-1, null)).toBe(false);
|
||||
expect(util.isCheckedProperty(-1, '')).toBe(false);
|
||||
expect(util.isCheckedProperty(-1, 'string')).toBe(false);
|
||||
expect(util.isCheckedProperty(-1, 0)).toBe(false);
|
||||
expect(util.isCheckedProperty(-1, '0')).toBe(false);
|
||||
expect(util.isCheckedProperty(-1, 1000)).toBe(false);
|
||||
expect(util.isCheckedProperty(-1, '1000')).toBe(false);
|
||||
expect(util.isCheckedProperty(-1, -1)).toBe(true);
|
||||
expect(util.isCheckedProperty(-1, true)).toBe(false);
|
||||
expect(util.isCheckedProperty(-1, false)).toBe(false);
|
||||
expect(util.isCheckedProperty(-1, 'true')).toBe(false);
|
||||
expect(util.isCheckedProperty(-1, 'false')).toBe(false);
|
||||
});
|
||||
|
||||
it('should test a="-1"', () => {
|
||||
expect(util.isCheckedProperty('-1', undefined)).toBe(false);
|
||||
expect(util.isCheckedProperty('-1', null)).toBe(false);
|
||||
expect(util.isCheckedProperty('-1', '')).toBe(false);
|
||||
expect(util.isCheckedProperty('-1', 'string')).toBe(false);
|
||||
expect(util.isCheckedProperty('-1', 0)).toBe(false);
|
||||
expect(util.isCheckedProperty('-1', '0')).toBe(false);
|
||||
expect(util.isCheckedProperty('-1', 1000)).toBe(false);
|
||||
expect(util.isCheckedProperty('-1', '1000')).toBe(false);
|
||||
expect(util.isCheckedProperty('-1', -1)).toBe(true);
|
||||
expect(util.isCheckedProperty('-1', true)).toBe(false);
|
||||
expect(util.isCheckedProperty('-1', false)).toBe(false);
|
||||
expect(util.isCheckedProperty('-1', 'true')).toBe(false);
|
||||
expect(util.isCheckedProperty('-1', 'false')).toBe(false);
|
||||
});
|
||||
|
||||
it('should test a="string"', () => {
|
||||
expect(util.isCheckedProperty('string', undefined)).toBe(false);
|
||||
expect(util.isCheckedProperty('string', null)).toBe(false);
|
||||
expect(util.isCheckedProperty('string', '')).toBe(false);
|
||||
expect(util.isCheckedProperty('string', 'string')).toBe(true);
|
||||
expect(util.isCheckedProperty('string', 'otherstring')).toBe(false);
|
||||
expect(util.isCheckedProperty('string', 0)).toBe(false);
|
||||
expect(util.isCheckedProperty('string', '0')).toBe(false);
|
||||
expect(util.isCheckedProperty('string', 1000)).toBe(false);
|
||||
expect(util.isCheckedProperty('string', '1000')).toBe(false);
|
||||
expect(util.isCheckedProperty('string', -1)).toBe(false);
|
||||
expect(util.isCheckedProperty('string', true)).toBe(false);
|
||||
expect(util.isCheckedProperty('string', false)).toBe(false);
|
||||
expect(util.isCheckedProperty('string', 'true')).toBe(false);
|
||||
expect(util.isCheckedProperty('string', 'false')).toBe(false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('getQuerystring', () => {
|
||||
it('should have no entries for empty url', () => {
|
||||
expect(util.getQuerystring('')).toEqual({});
|
||||
expect(util.getQuerystring(null)).toEqual({});
|
||||
expect(util.getQuerystring(undefined)).toEqual({});
|
||||
});
|
||||
|
||||
it('should have no entries when without ?', () => {
|
||||
expect(util.getQuerystring('http://localhost:1234/')).toEqual({});
|
||||
});
|
||||
|
||||
it('should have no entries with only ?', () => {
|
||||
expect(util.getQuerystring('http://localhost:1234/?')).toEqual({});
|
||||
});
|
||||
|
||||
it('should have no entries for key with no =', () => {
|
||||
expect(util.getQuerystring('http://localhost:1234/?key')).toEqual({});
|
||||
});
|
||||
|
||||
it('should have no entries with only #?', () => {
|
||||
expect(util.getQuerystring('http://localhost:1234/#?')).toEqual({});
|
||||
});
|
||||
|
||||
it('should have no entries with only #?=', () => {
|
||||
expect(util.getQuerystring('http://localhost:1234/#?=')).toEqual({});
|
||||
});
|
||||
|
||||
it('should have no entries for url with no "?" character', () => {
|
||||
expect(util.getQuerystring('http://localhost:1234/#key1=1&key2=2')).toEqual({});
|
||||
});
|
||||
|
||||
it('should contain key/value entries for all the parameters after "?" character', () => {
|
||||
expect(util.getQuerystring('http://localhost:1234/#key1=1&key2x=2x?key3=3&key4=4')).toEqual({
|
||||
key3: '3',
|
||||
key4: '4'
|
||||
});
|
||||
});
|
||||
|
||||
it('should lowercase param keys', () => {
|
||||
expect(util.getQuerystring('http://localhost:1234/#?KEY1=1&kEy2=2')).toEqual({
|
||||
key1: '1',
|
||||
key2: '2'
|
||||
});
|
||||
});
|
||||
|
||||
it('should not include any values when # comes after ?', () => {
|
||||
expect(util.getQuerystring('http://localhost:1234/?key1=1#key2=2')).toEqual({
|
||||
key1: '1'
|
||||
});
|
||||
});
|
||||
|
||||
it('should ignore empty ?& and &&', () => {
|
||||
expect(util.getQuerystring('http://localhost:1234/#?&&')).toEqual({});
|
||||
|
||||
expect(util.getQuerystring('http://localhost:1234/#?&&key1=1&key2=2&&')).toEqual({
|
||||
key1: '1',
|
||||
key2: '2'
|
||||
});
|
||||
});
|
||||
|
||||
it('should get "" when key has no value', () => {
|
||||
expect(util.getQuerystring('http://localhost:1234/#?key=')).toEqual({
|
||||
key: ''
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('isTrueProperty', () => {
|
||||
|
||||
it('should be true from boolean true', () => {
|
||||
expect(util.isTrueProperty(true)).toBe(true);
|
||||
});
|
||||
|
||||
it('should be true from string "true"', () => {
|
||||
expect(util.isTrueProperty('true')).toBe(true);
|
||||
expect(util.isTrueProperty('TRUE')).toBe(true);
|
||||
expect(util.isTrueProperty(' true ')).toBe(true);
|
||||
});
|
||||
|
||||
it('should be true from string "on"', () => {
|
||||
expect(util.isTrueProperty(true)).toBe(true);
|
||||
});
|
||||
|
||||
it('should be true from empty string ""', () => {
|
||||
expect(util.isTrueProperty('')).toBe(true);
|
||||
expect(util.isTrueProperty(' ')).toBe(true);
|
||||
});
|
||||
|
||||
it('should be true from number greater than zero', () => {
|
||||
expect(util.isTrueProperty(1)).toBe(true);
|
||||
expect(util.isTrueProperty(999)).toBe(true);
|
||||
});
|
||||
|
||||
it('should be false from boolean false', () => {
|
||||
expect(util.isTrueProperty(false)).toBe(false);
|
||||
});
|
||||
|
||||
it('should be false from string "off"', () => {
|
||||
expect(util.isTrueProperty(true)).toBe(true);
|
||||
});
|
||||
|
||||
it('should be false from null', () => {
|
||||
expect(util.isTrueProperty(null)).toBe(false);
|
||||
});
|
||||
|
||||
it('should be false from undefined', () => {
|
||||
expect(util.isTrueProperty(undefined)).toBe(false);
|
||||
});
|
||||
|
||||
it('should be false from string "false"', () => {
|
||||
expect(util.isTrueProperty('false')).toBe(false);
|
||||
expect(util.isTrueProperty(' FALSE ')).toBe(false);
|
||||
expect(util.isTrueProperty('doesnt actually matter')).toBe(false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('extend', () => {
|
||||
|
||||
it('should extend simple', () => {
|
||||
var obj = { a: '0', c: '0' };
|
||||
expect( util.assign(obj, { a: '1', b: '2' }) ).toBe(obj);
|
||||
expect(obj).toEqual({ a: '1', b: '2', c: '0' });
|
||||
});
|
||||
|
||||
it('should extend complex', () => {
|
||||
expect(util.assign(
|
||||
{ a: '0', b: '0' },
|
||||
{ b: '1', c: '1' },
|
||||
{ c: '2', d: '2' }
|
||||
)).toEqual({
|
||||
a: '0',
|
||||
b: '1',
|
||||
c: '2',
|
||||
d: '2'
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('defaults', () => {
|
||||
|
||||
it('should simple defaults', () => {
|
||||
var obj = { a: '1' };
|
||||
expect(util.defaults(obj, { a: '2', b: '2' })).toBe(obj);
|
||||
expect(obj).toEqual({
|
||||
a: '1', b: '2'
|
||||
});
|
||||
});
|
||||
|
||||
it('should complex defaults', () => {
|
||||
expect(util.defaults(
|
||||
{ a: '0', b: '0' },
|
||||
{ b: '1', c: '1', e: '1' },
|
||||
{ c: '2', d: '2' }
|
||||
)).toEqual({
|
||||
a: '0',
|
||||
b: '0',
|
||||
c: '2',
|
||||
d: '2',
|
||||
e: '1'
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
140
src/util/util.scss
Executable file
140
src/util/util.scss
Executable file
@@ -0,0 +1,140 @@
|
||||
@import "../globals.core";
|
||||
|
||||
.hide,
|
||||
[hidden],
|
||||
template {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
// Focus Outline
|
||||
// --------------------------------------------------
|
||||
|
||||
$focus-outline-border-color: #51a7e8 !default;
|
||||
$focus-outline-box-shadow: 0 0 8px 0 $focus-outline-border-color !default;
|
||||
|
||||
|
||||
:focus,
|
||||
:active {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.focus-outline {
|
||||
|
||||
:focus {
|
||||
outline: thin dotted;
|
||||
outline-offset: -1px;
|
||||
}
|
||||
|
||||
button:focus,
|
||||
[button]:focus {
|
||||
border-color: $focus-outline-border-color;
|
||||
outline: thin solid $focus-outline-border-color;
|
||||
box-shadow: $focus-outline-box-shadow;
|
||||
}
|
||||
|
||||
ion-input :focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Focus Controls
|
||||
// -------------------------------
|
||||
|
||||
focus-ctrl {
|
||||
position: fixed;
|
||||
|
||||
input,
|
||||
button {
|
||||
position: fixed;
|
||||
top: 1px;
|
||||
left: -9999px;
|
||||
z-index: 9999;
|
||||
|
||||
width: 9px;
|
||||
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Backdrop
|
||||
// --------------------------------------------------
|
||||
|
||||
$backdrop-color: #000 !default;
|
||||
|
||||
.backdrop {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: $z-index-backdrop;
|
||||
display: block;
|
||||
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
background-color: $backdrop-color;
|
||||
opacity: .01;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
|
||||
|
||||
// Click Block
|
||||
// --------------------------------------------------
|
||||
// Fill the screen to block clicks (a better pointer-events: none)
|
||||
// to avoid full-page reflows and paints which can cause flickers
|
||||
|
||||
click-block {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: $z-index-click-block;
|
||||
display: block;
|
||||
|
||||
opacity: 0;
|
||||
transform: translate3d(0, -100%, 0);
|
||||
transform: translate3d(0, calc(-100% + 1px), 0);
|
||||
|
||||
// background: red;
|
||||
// opacity: .3;
|
||||
}
|
||||
|
||||
.click-block-active {
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
// Loading Icon
|
||||
// --------------------------------------------------
|
||||
|
||||
@keyframes rotation {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
|
||||
ion-loading-icon {
|
||||
display: flex;
|
||||
|
||||
align-self: center;
|
||||
|
||||
margin: auto;
|
||||
|
||||
font-family: "Ionicons";
|
||||
|
||||
font-size: 128px;
|
||||
color: #666;
|
||||
|
||||
animation: rotation 45s infinite linear;
|
||||
|
||||
&::before {
|
||||
content: "\f44e";
|
||||
}
|
||||
}
|
||||
230
src/util/util.ts
Normal file
230
src/util/util.ts
Normal file
@@ -0,0 +1,230 @@
|
||||
|
||||
/**
|
||||
* Given a min and max, restrict the given number
|
||||
* to the range.
|
||||
* @param min the minimum
|
||||
* @param n the value
|
||||
* @param max the maximum
|
||||
*/
|
||||
export function clamp(min, n, max) {
|
||||
return Math.max(min, Math.min(n, max));
|
||||
}
|
||||
|
||||
/**
|
||||
* The assign() method is used to copy the values of all enumerable own
|
||||
* properties from one or more source objects to a target object. It will
|
||||
* return the target object. When available, this method will use
|
||||
* `Object.assign()` under-the-hood.
|
||||
* @param target The target object
|
||||
* @param source(s) The source object
|
||||
*/
|
||||
export function assign(...args: any[]): any {
|
||||
if (typeof Object.assign !== 'function') {
|
||||
// use the old-school shallow extend method
|
||||
return _baseExtend(args[0], [].slice.call(args, 1), false);
|
||||
}
|
||||
|
||||
// use the built in ES6 Object.assign method
|
||||
return Object.assign.apply(null, args);
|
||||
}
|
||||
|
||||
/**
|
||||
* Do a deep extend (merge).
|
||||
* @param dst the destination
|
||||
* @param ... the param objects
|
||||
*/
|
||||
export function merge(dst: any, ...args: any[]) {
|
||||
return _baseExtend(dst, [].slice.call(arguments, 1), true);
|
||||
}
|
||||
|
||||
function _baseExtend(dst, objs, deep) {
|
||||
for (var i = 0, ii = objs.length; i < ii; ++i) {
|
||||
var obj = objs[i];
|
||||
if (!obj || !isObject(obj) && !isFunction(obj)) continue;
|
||||
var keys = Object.keys(obj);
|
||||
for (var j = 0, jj = keys.length; j < jj; j++) {
|
||||
var key = keys[j];
|
||||
var src = obj[key];
|
||||
|
||||
if (deep && isObject(src)) {
|
||||
if (!isObject(dst[key])) dst[key] = isArray(src) ? [] : {};
|
||||
_baseExtend(dst[key], [src], true);
|
||||
} else {
|
||||
dst[key] = src;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
export function debounce(fn: Function, wait: number, immediate: boolean = false): any {
|
||||
var timeout, args, context, timestamp: number, result;
|
||||
return function() {
|
||||
context = this;
|
||||
args = arguments;
|
||||
timestamp = Date.now();
|
||||
var later: any = function() {
|
||||
var last: any = Date.now() - timestamp;
|
||||
if (last < wait) {
|
||||
timeout = setTimeout(later, wait - last);
|
||||
} else {
|
||||
timeout = null;
|
||||
if (!immediate) result = fn.apply(context, args);
|
||||
}
|
||||
};
|
||||
var callNow = immediate && !timeout;
|
||||
if (!timeout) {
|
||||
timeout = setTimeout(later, wait);
|
||||
}
|
||||
if (callNow) result = fn.apply(context, args);
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Apply default arguments if they don't exist in
|
||||
* the first object.
|
||||
* @param the destination to apply defaults to.
|
||||
*/
|
||||
export function defaults(dest, ...args: any[]) {
|
||||
for (let i = arguments.length - 1; i >= 1; i--) {
|
||||
let source = arguments[i] || {};
|
||||
for (let key in source) {
|
||||
if (source.hasOwnProperty(key) && !dest.hasOwnProperty(key)) {
|
||||
dest[key] = source[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
export const isBoolean = val => typeof val === 'boolean';
|
||||
export const isString = val => typeof val === 'string';
|
||||
export const isNumber = val => typeof val === 'number';
|
||||
export const isFunction = val => typeof val === 'function';
|
||||
export const isDefined = val => typeof val !== 'undefined';
|
||||
export const isUndefined = val => typeof val === 'undefined';
|
||||
export const isPresent = val => val !== undefined && val !== null;
|
||||
export const isBlank = val => val === undefined || val === null;
|
||||
export const isObject = val => typeof val === 'object';
|
||||
export const isArray = Array.isArray;
|
||||
|
||||
export const isTrueProperty = function(val: any): boolean {
|
||||
if (typeof val === 'string') {
|
||||
val = val.toLowerCase().trim();
|
||||
return (val === 'true' || val === 'on' || val === '');
|
||||
}
|
||||
return !!val;
|
||||
};
|
||||
|
||||
export const isCheckedProperty = function(a: any, b: any): boolean {
|
||||
if (a === undefined || a === null || a === '') {
|
||||
return (b === undefined || b === null || b === '');
|
||||
|
||||
} else if (a === true || a === 'true') {
|
||||
return (b === true || b === 'true');
|
||||
|
||||
} else if (a === false || a === 'false') {
|
||||
return (b === false || b === 'false');
|
||||
|
||||
} else if (a === 0 || a === '0') {
|
||||
return (b === 0 || b === '0');
|
||||
}
|
||||
|
||||
// not using strict comparison on purpose
|
||||
/* tslint:disable */
|
||||
return (a == b);
|
||||
/* tslint:enable */
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a string in the format thisIsAString to a slug format this-is-a-string
|
||||
*/
|
||||
export function pascalCaseToDashCase(val: string = ''): string {
|
||||
return val.charAt(0).toLowerCase() + val.substring(1).replace(/[A-Z]/g, match => {
|
||||
return '-' + match.toLowerCase();
|
||||
});
|
||||
}
|
||||
|
||||
let uid = 0;
|
||||
export function nextUid(): number {
|
||||
return ++uid;
|
||||
}
|
||||
|
||||
export const array = {
|
||||
find(arr, cb) {
|
||||
for (let i = 0, ii = arr.length; i < ii; i++) {
|
||||
if (cb(arr[i], i)) return arr[i];
|
||||
}
|
||||
},
|
||||
remove(arr, itemOrIndex) {
|
||||
let index = -1;
|
||||
if (isNumber(itemOrIndex)) {
|
||||
index = itemOrIndex;
|
||||
} else {
|
||||
index = arr.indexOf(itemOrIndex);
|
||||
}
|
||||
if (index < 0) {
|
||||
return false;
|
||||
}
|
||||
arr.splice(index, 1);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Grab all query strings keys and values.
|
||||
* @param url
|
||||
*/
|
||||
export function getQuerystring(url: string): any {
|
||||
var queryParams = {};
|
||||
if (url) {
|
||||
const startIndex = url.indexOf('?');
|
||||
if (startIndex !== -1) {
|
||||
const queries = url.slice(startIndex + 1).split('&');
|
||||
for (var i = 0; i < queries.length; i++) {
|
||||
if (queries[i].indexOf('=') > 0) {
|
||||
var split = queries[i].split('=');
|
||||
if (split.length > 1) {
|
||||
queryParams[split[0].toLowerCase()] = split[1].split('#')[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return queryParams;
|
||||
}
|
||||
|
||||
/**
|
||||
* Throttle the given fun, only allowing it to be
|
||||
* called at most every `wait` ms.
|
||||
*/
|
||||
export function throttle(fn: Function, wait: number, options: any): any {
|
||||
var context, args, result;
|
||||
var timeout = null;
|
||||
var previous = 0;
|
||||
options || (options = {});
|
||||
var later = function() {
|
||||
previous = options.leading === false ? 0 : Date.now();
|
||||
timeout = null;
|
||||
result = fn.apply(context, args);
|
||||
};
|
||||
return function() {
|
||||
var now = Date.now();
|
||||
if (!previous && options.leading === false) previous = now;
|
||||
var remaining = wait - (now - previous);
|
||||
context = this;
|
||||
args = arguments;
|
||||
if (remaining <= 0) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
previous = now;
|
||||
result = fn.apply(context, args);
|
||||
} else if (!timeout && options.trailing !== false) {
|
||||
timeout = setTimeout(later, remaining);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user