novo-elements
Version:
Bullhorn's NOVO Element Repository for Angular 2
349 lines (313 loc) • 12.2 kB
text/typescript
export class Helpers {
/**
* Swallows an event to stop further execution
* @param event
*/
static swallowEvent(event) {
if (event) {
event.stopPropagation();
event.preventDefault();
}
}
/**
* Interpolates a string with vars passed to it
* @param {String} str The string to interpolate
* @param {Object} props The params to replace in string.
* @return {String}
*/
static interpolate(str, props) {
return str.replace(/\$([\w\.]+)/g, (original, key) => {
let keys = key.split('.');
let value = props[keys.shift()];
while (keys.length && value !== undefined) {
let k = keys.shift();
value = k ? value[k] : `${value}.`;
}
return value !== undefined ? value : original;
});
}
/**
* Verifies that an object has every property expected by a string to interpolate
* @param {String} str The string to interpolate
* @param {Object} props The params to replace in string.
* @return {Boolean}
*/
static validateInterpolationProps(str, props) {
let keys = str.match(/\$([\w\.]+)/g);
return keys.every(key => {
return props.hasOwnProperty(key.substr(1));
});
}
static isObject(item) {
return (item && typeof item === 'object' && !Array.isArray(item) && item !== null);
}
/**
* Checks to see if the object is a string
*/
static isString(obj: any) {
return typeof obj === 'string';
}
/**
* Checks to see if the object is a undefined or null
*/
static isBlank(obj: any): boolean {
return obj === undefined || obj === null;
}
/**
* Checks to see if the object is a undefined or null
*/
static isEmpty(obj: any): boolean {
return Helpers.isBlank(obj) || obj === '' || (Array.isArray(obj) && obj.length === 0);
}
/**
* Checks to see if the object is a function
*/
static isFunction(obj: any): boolean {
return !!(obj && obj.constructor && obj.call && obj.apply);
}
/**
* Checks to see if the object is a Date
*/
static isDate(obj: any) {
return obj instanceof Date;
}
static sortByField(fields: any, reverse = false) {
return (previous: any, current: any) => {
if (Helpers.isFunction(fields)) {
return fields((reverse) ? 'desc' : 'asc', previous, current);
}
if (!Array.isArray(fields)) {
fields = [fields];
}
for (let i = 0; i < fields.length; i++) {
let field: string = fields[i];
let first = previous[field] || '';
let second = current[field] || '';
if (Helpers.isDate(first) && Helpers.isDate(second)) {
// Dates
first = first.getTime();
second = second.getTime();
} else if (Helpers.isString(first) && Helpers.isString(second)) {
// Basic strings
first = first.toLowerCase();
second = second.toLowerCase();
} else {
// Numbers
first = isNaN(Number(first)) ? first : Number(first);
second = isNaN(Number(second)) ? second : Number(second);
}
if (first > second) {
return (reverse) ? -1 : 1;
} else if (first < second) {
return (reverse) ? 1 : -1;
}
}
return 0;
};
}
static filterByField(key, value) {
return (item) => {
let results = [];
let field = can(item).have(key);
if (value instanceof Function) {
results.push(value(field, item));
} else if (Array.isArray(value)) {
results.push(value.includes(field));
} else if (value instanceof Object) {
if (field instanceof Date) {
field = field.getTime();
}
if (value.min) {
results.push(field > value.min);
}
if (value.max) {
results.push(field < value.max);
}
if (value.any && Array.isArray(value.any)) {
if (Array.isArray(field)) {
results.push(value.any.some(v => field.includes(v)));
} else {
results.push(value.any.includes(field));
}
}
if (value.all && Array.isArray(value.all)) {
results.push(value.all.every(v => field.includes(v)));
}
if (value.not) {
results.push(!Helpers.filterByField(key, value.not)(item));
}
for (let subkey in value) {
if (['min', 'max', 'any', 'all', 'not'].indexOf(subkey) < 0) {
let subvalue = value[subkey];
results.push(Helpers.filterByField(`${key}.${subkey}`, subvalue)(item));
}
}
} else {
results.push(JSON.stringify(field).match(new RegExp(value, 'gi')));
}
return results.every(x => x);
};
}
static calcPositionOffset(position: ClientRect, element: Element, side: string): { top: string, left: string, width: string } {
if (!position) {
return;
}
let supportPageOffset = window.pageXOffset !== undefined;
let isCSS1Compat = ((document.compatMode || '') === 'CSS1Compat');
let x = supportPageOffset ? window.pageXOffset : isCSS1Compat ?
document.documentElement.scrollLeft : document.body.scrollLeft;
let y = supportPageOffset ? window.pageYOffset : isCSS1Compat ?
document.documentElement.scrollTop : document.body.scrollTop;
let sideOffset = 0;
if (side === 'right') {
sideOffset = position.width - element.clientWidth;
}
let top = `${position.top + y + position.height + 10}px`;
let left = `${position.left + x + sideOffset}px`;
let width = `${position.width}px`;
const clientWidth = element.clientWidth,
clientHeight = element.clientHeight,
marginFromBottom = parseInt(top) + clientHeight,
marginFromRight = parseInt(left) + clientWidth,
windowScrollHeight = window.innerHeight + window.scrollY,
windowScrollWidth = window.innerWidth + window.scrollX;
// Force open up
if (marginFromBottom >= windowScrollHeight) {
top = `${parseInt(top.replace('px', '')) - clientHeight - position.height - 20}px`;
}
// Force open right
if (marginFromRight >= windowScrollWidth) {
left = `${parseInt(left.replace('px', '')) - clientWidth + position.width}px`;
}
// Force open middle
if (parseInt(top.replace('px', '')) <= window.scrollY) {
top = `${parseInt(top.replace('px', '')) + (clientHeight / 2) + (position.height / 2) + 10}px`;
}
// Force open left
if (parseInt(left.replace('px', '')) <= window.scrollX) {
left = `${parseInt(left.replace('px', '')) + (clientWidth / 2) + (position.width * 2) - 4}px`;
}
return { top, left, width };
}
static findAncestor(element: Element, selector: string): Element {
while ((element = element.parentElement) && !(element.matches.call(element, selector))); // tslint:disable-line
return element;
}
static deepClone(item: any): any {
if (Array.isArray(item)) {
let newArr = [];
for (let i = item.length; i-- > 0;) { // tslint:disable-line
newArr[i] = Helpers.deepClone(item[i]);
}
return newArr;
}
if (typeof item === 'function' && !(/\(\) \{ \[native/).test(item.toString())) {
let obj;
eval('obj = ' + item.toString()); // tslint:disable-line
for (let k in item) {
if (k in item) {
obj[k] = Helpers.deepClone(item[k]);
}
}
return obj;
}
if (item && typeof item === 'object') {
let obj = {};
for (let k in item) {
if (k in item) {
obj[k] = Helpers.deepClone(item[k]);
}
}
return obj;
}
return item;
}
static deepAssign(...objs) {
if (objs.length < 2) {
throw new Error('Need two or more objects to merge');
}
const target = Object.assign({}, objs[0]);
for (let i = 1; i < objs.length; i++) {
const source = Object.assign({}, objs[i]);
Object.keys(source).forEach(prop => {
const value = source[prop];
if (Helpers.isObject(value)) {
if (target.hasOwnProperty(prop) && Helpers.isObject(target[prop])) {
target[prop] = Helpers.deepAssign(target[prop], value);
} else {
target[prop] = value;
}
} else if (Array.isArray(value)) {
if (target.hasOwnProperty(prop) && Array.isArray(target[prop])) {
const targetArray = target[prop];
value.forEach((sourceItem, itemIndex) => {
if (itemIndex < targetArray.length) {
const targetItem = targetArray[itemIndex];
if (Object.is(targetItem, sourceItem)) {
return;
}
if (Helpers.isObject(targetItem) && Helpers.isObject(sourceItem)) {
targetArray[itemIndex] = Helpers.deepAssign(targetItem, sourceItem);
} else if (Array.isArray(targetItem) && Array.isArray(sourceItem)) {
targetArray[itemIndex] = Helpers.deepAssign(targetItem, sourceItem);
} else {
targetArray[itemIndex] = sourceItem;
}
} else {
targetArray.push(sourceItem);
}
});
} else {
target[prop] = value;
}
} else {
target[prop] = value;
}
});
}
return target;
}
/**
* Workaround for Edge browser since Element:nextElementSibling is undefined inside of template directives
* @param element any document element
* @returns the next sibling node that is of type: Element
*/
static getNextElementSibling(element: Element): Node {
if (element.nextElementSibling) {
return element.nextElementSibling;
} else {
let e = element.nextSibling;
while (e && 1 !== e.nodeType) {
e = e.nextSibling;
}
return e;
}
}
}
export class Can {
obj: Object;
constructor(obj: Object) {
this.obj = obj;
}
have(key: string): any {
let props = key.split('.');
let item: any = this.obj;
for (let i = 0; i < props.length; i++) {
item = item[props[i]];
if (this.check(item) === false) {
return item;
}
}
return item;
}
check(thing: any): boolean {
return thing !== void 0;
}
}
/**
* @param {any} obj
* @returns
*/
export function can(obj) {
return new Can(obj);
}