CrazySquirrel/CSTiles

View on GitHub
src/ts/Modules/CSTiles.ts

Summary

Maintainability
F
1 wk
Test Coverage
"use strict";
/**
 * Import interfaces
 */
import IWindow from "../Interfaces/IWindow";

/**
 * Import classes
 */
import CGridParams from "../Classes/CGridParams";
import CTileParams from "../Classes/CTileParams";

declare let window: IWindow;
declare let require: any;

/**
 * CSTiles class
 */
export default class CSTiles {
    /**
     * Convert css object to css
     * @param arrCss
     * @returns {string}
     */
    public static convertCss(arrCss: Object) {
        for (let strMedia in arrCss) {
            if (arrCss.hasOwnProperty(strMedia)) {
                for (let strBlockID in arrCss[strMedia]) {
                    if (arrCss[strMedia].hasOwnProperty(strBlockID)) {
                        for (let strCssProperty in arrCss[strMedia][strBlockID]) {
                            if (arrCss[strMedia][strBlockID].hasOwnProperty(strCssProperty)) {
                                arrCss[strMedia][strBlockID][strCssProperty] = CSTiles.optimizeCssProperty(strCssProperty, arrCss[strMedia][strBlockID][strCssProperty]);
                            }
                        }
                        arrCss[strMedia][strBlockID] = CSTiles.compiledCssProperties(arrCss[strMedia][strBlockID]);
                    }
                }
            }
        }
        let strCss = "";
        for (let strMedia in arrCss) {
            if (arrCss.hasOwnProperty(strMedia)) {
                if (strMedia) {
                    let strMediaRange = strMedia.split("-");
                    strMediaRange[0] = strMediaRange[0] ? `and (min-width: ${strMediaRange[0]}px)` : ``;
                    strMediaRange[1] = strMediaRange[1] ? `and (max-width: ${strMediaRange[1]}px)` : ``;
                    strCss += `@media screen ${strMediaRange[0]} ${strMediaRange[1]} {`;
                }
                if (arrCss[strMedia]) {
                    for (let strBlockID in arrCss[strMedia]) {
                        if (arrCss[strMedia].hasOwnProperty(strBlockID)) {
                            strCss += `#${strBlockID} {`;
                            for (let strCssProperty in arrCss[strMedia][strBlockID]) {
                                if (arrCss[strMedia][strBlockID].hasOwnProperty(strCssProperty)) {
                                    strCss += `${strCssProperty}: ${arrCss[strMedia][strBlockID][strCssProperty]} !important;`;
                                }
                            }
                            strCss += `}`;
                        }
                    }
                }
                if (strMedia) {
                    strCss += `}`;
                }
            }
        }
        return strCss;
    }

    /**
     * Optimize css property
     * @param strCssPropertyName
     * @param strCssPropertyValue
     * @returns {string}
     */
    public static optimizeCssProperty(strCssPropertyName, strCssPropertyValue): string {
        strCssPropertyValue = (" " + strCssPropertyValue + " ")
            .replace(/\s0%/ig, " 0")
            .replace(/[\r\n\t]/ig, " ")
            .replace(/\s+/ig, " ")
            .replace(/^\s/ig, "")
            .replace(/\s$/ig, "");
        if (
            strCssPropertyName === "padding" ||
            strCssPropertyName === "margin") {
            strCssPropertyValue = strCssPropertyValue.split(" ");
            if (strCssPropertyValue.length === 2) {
                if (strCssPropertyValue[0] === strCssPropertyValue[1]) {
                    strCssPropertyValue = [strCssPropertyValue[0]];
                }
            } else if (strCssPropertyValue.length === 3) {
                if (
                    strCssPropertyValue[0] === strCssPropertyValue[2] &&
                    strCssPropertyValue[0] === strCssPropertyValue[2]
                ) {
                    strCssPropertyValue = [strCssPropertyValue[0]];
                } else if (
                    strCssPropertyValue[0] === strCssPropertyValue[2]
                ) {
                    strCssPropertyValue = [strCssPropertyValue[0]];
                }
            } else if (strCssPropertyValue.length === 4) {
                if (
                    strCssPropertyValue[0] === strCssPropertyValue[1] &&
                    strCssPropertyValue[1] === strCssPropertyValue[2] &&
                    strCssPropertyValue[2] === strCssPropertyValue[3]
                ) {
                    strCssPropertyValue = [strCssPropertyValue[0]];
                } else if (
                    strCssPropertyValue[0] === strCssPropertyValue[2] &&
                    strCssPropertyValue[1] === strCssPropertyValue[3]
                ) {
                    strCssPropertyValue = [strCssPropertyValue[0], strCssPropertyValue[1]];
                } else if (
                    strCssPropertyValue[1] === strCssPropertyValue[3]
                ) {
                    strCssPropertyValue = [strCssPropertyValue[0], strCssPropertyValue[1], strCssPropertyValue[2]];
                }
            }
            strCssPropertyValue = strCssPropertyValue.join(" ");
        }
        return strCssPropertyValue;
    }

    /**
     * Compiled complex css properties
     * @param objCssProperties
     * @returns {Object}
     */
    public static compiledCssProperties(objCssProperties: Object): Object {
        let objCompiledRules = {
            background: [
                "background-image",
                "background-position/background-size",
                "background-repeat",
                "background-attachment",
                "background-origin",
                "background-clip",
                "background-color",
            ],
            "border-top": [
                "border-top-width",
                "border-top-style",
                "border-top-color",
            ],
            "border-right": [
                "border-right-width",
                "border-right-style",
                "border-right-color",
            ],
            "border-bottom": [
                "border-bottom-width",
                "border-bottom-style",
                "border-bottom-color",
            ],
            "border-left": [
                "border-right-width",
                "border-right-style",
                "border-right-color",
            ],
            border: [
                "border-width",
                "border-style",
                "border-color",
            ],
            font: [
                "font-style",
                "font-variant",
                "font-weight",
                "font-size",
                "line-height",
                "font-family",
            ],
            "list-style": [
                "list-style-type",
                "list-style-position",
                "list-style-image",
            ],
            margin: [
                "margin-top",
                "margin-right",
                "margin-bottom",
                "margin-left",
            ],
            padding: [
                "padding-top",
                "padding-right",
                "padding-bottom",
                "padding-left",
            ],
            outline: [
                "outline-color",
                "outline-style",
                "outline-width",
            ],
        };
        for (let strCompiledRule in objCompiledRules) {
            if (
                objCssProperties.hasOwnProperty(strCompiledRule) && !objCssProperties[strCompiledRule]
            ) {
                objCssProperties[strCompiledRule] = [];
                for (let strRule in objCompiledRules[strCompiledRule]) {
                    if (objCompiledRules[strCompiledRule].hasOwnProperty(strRule)) {
                        let arrRules = objCompiledRules[strCompiledRule][strRule].split("/");
                        let arrCompiledRules = [];
                        for (let _strRule of arrRules) {
                            if (objCssProperties[_strRule]) {
                                arrCompiledRules.push(objCssProperties[_strRule]);
                                delete objCssProperties[_strRule];
                            }
                        }
                        let strCompiledRules = arrCompiledRules.join("/");
                        if (strCompiledRules) {
                            objCssProperties[strCompiledRule].push(strCompiledRules);
                        }
                    }
                }
                if (objCssProperties[strCompiledRule].length > 0) {
                    objCssProperties[strCompiledRule] = objCssProperties[strCompiledRule].join(" ");
                } else {
                    delete objCssProperties[strCompiledRule];
                }
            }
        }
        return objCssProperties;
    }

    /**
     * Insert tile content
     * @param domParentNode
     * @param objContent
     * @param arrCss
     */
    public static insertContent(domParentNode: HTMLElement,
                                objContent: any,
                                arrCss: Object) {
        switch (objContent.type) {
            case "none":
                break;
            case "image":
                CSTiles.insertContentImage(domParentNode, objContent, arrCss);
                break;
            case "video":
                CSTiles.insertContentVideo(domParentNode, objContent, arrCss);
                break;
            case "audio":
                CSTiles.insertContentAudio(domParentNode, objContent, arrCss);
                break;
            case "iframe":
                CSTiles.insertContentiFrame(domParentNode, objContent);
                break;
            case "tiles":
                CSTiles.insertContentTiles(domParentNode, objContent);
                break;
            case "html":
                CSTiles.insertContentHtml(domParentNode, objContent, arrCss);
                break;
            case "dom":
                CSTiles.insertContentDom(domParentNode, objContent, arrCss);
                break;
            default:
                console.log(objContent.type);
        }
    }

    /**
     * Insert tile dom content
     * @param domParentNode
     * @param objContent
     * @param arrCss
     */
    public static insertContentDom(domParentNode: HTMLElement,
                                   objContent: {
                                       query: string,
                                       poster: string,
                                   },
                                   arrCss: Object) {
        if (objContent.query) {
            let dom = document.querySelector(objContent.query);
            if (dom) {
                domParentNode.appendChild(dom);
            }
        }
        if (objContent.poster) {
            arrCss[""][domParentNode.id]["background-image"] = `url("${objContent.poster}")`;
            arrCss[""][domParentNode.id]["background-position"] = `center center`;
            arrCss[""][domParentNode.id]["background-repeat"] = `no-repeat`;
            arrCss[""][domParentNode.id]["background-size"] = `cover`;
        }
    }

    /**
     * Insert tile html content
     * @param domParentNode
     * @param objContent
     * @param arrCss
     */
    public static insertContentHtml(domParentNode: HTMLElement,
                                    objContent: {
                                        html: string,
                                        poster: string,
                                    },
                                    arrCss: Object) {
        if (objContent.html) {
            domParentNode.innerHTML = objContent.html;
        }
        if (objContent.poster) {
            arrCss[""][domParentNode.id]["background-image"] = `url("${objContent.poster}")`;
            arrCss[""][domParentNode.id]["background-position"] = `center center`;
            arrCss[""][domParentNode.id]["background-repeat"] = `no-repeat`;
            arrCss[""][domParentNode.id]["background-size"] = `cover`;
        }
    }

    /**
     * Insert tile tiles content
     * @param domParentNode
     * @param objContent
     */
    public static insertContentTiles(domParentNode: HTMLElement,
                                     objContent: {
                                         params: {
                                             grid: CGridParams,
                                             tiles: Array<CTileParams>,
                                         }
                                     }) {
        let tile = null;
        if (
            objContent.params &&
            objContent.params.grid &&
            objContent.params.tiles
        ) {
            tile = new CSTiles(domParentNode, objContent.params.grid, objContent.params.tiles);
        }
        return !!tile;
    }

    /**
     * Insert tile iframe content
     * @param domParentNode
     * @param objContent
     */
    public static insertContentiFrame(domParentNode: HTMLElement,
                                      objContent: any) {
        if (objContent.src) {
            let domTileiFrame = document.createElement("iframe");
            domTileiFrame.src = objContent.src;
            domTileiFrame.width = "100%";
            domTileiFrame.height = "100%";
            domTileiFrame.frameBorder = "no";
            domParentNode.appendChild(domTileiFrame);
        }
    }

    /**
     * Insert tile audio content
     * @param domParentNode
     * @param objContent
     * @param arrCss
     */
    public static insertContentAudio(domParentNode: HTMLElement,
                                     objContent: any,
                                     arrCss: Object) {
        let domTileVideo = document.createElement("audio");
        domTileVideo.id = CSTiles.getUUID();
        domTileVideo.controls = true;
        domTileVideo.preload = "auto";
        if (objContent.src) {
            for (let strVideo of objContent.src) {
                let domTileSource = document.createElement("source");
                domTileSource.src = strVideo;
                if (strVideo.indexOf(".ogv") !== -1) {
                    domTileSource.type = "audio/ogg; codecs=vorbis";
                } else if (strVideo.indexOf(".mp3") !== -1) {
                    domTileSource.type = "audio/mpeg";
                }
                domTileVideo.appendChild(domTileSource);
            }
            for (let strVideo of objContent.src) {
                let domTileA = document.createElement("a");
                domTileA.href = strVideo;
                strVideo = strVideo.split(".");
                domTileA.text = "Скачайте аудио ." + (strVideo[strVideo.length - 1]);
                domTileVideo.appendChild(domTileA);
            }
        }
        domParentNode.appendChild(domTileVideo);
        arrCss[""] = arrCss[""] || {};
        arrCss[""][domTileVideo.id] = arrCss[""][domTileVideo.id] || {};
        arrCss[""][domTileVideo.id].width = "100%";
        arrCss[""][domTileVideo.id].height = "100%";
        if (objContent.poster) {
            arrCss[""][domParentNode.id]["background-image"] = `url("${objContent.poster}")`;
            arrCss[""][domParentNode.id]["background-position"] = `center center`;
            arrCss[""][domParentNode.id]["background-repeat"] = `no-repeat`;
            arrCss[""][domParentNode.id]["background-size"] = `cover`;
        }
    }

    /**
     * Insert tile video content
     * @param domParentNode
     * @param objContent
     * @param arrCss
     */
    public static insertContentVideo(domParentNode: HTMLElement,
                                     objContent: any,
                                     arrCss: Object) {
        let domTileVideo = document.createElement("video");
        domTileVideo.id = CSTiles.getUUID();
        domTileVideo.controls = true;
        domTileVideo.preload = "auto";
        domTileVideo.poster = require("../../images/empty.png");
        if (objContent.src) {
            for (let strVideo of objContent.src) {
                let domTileSource = document.createElement("source");
                domTileSource.src = strVideo;
                if (strVideo.indexOf(".ogv") !== -1) {
                    domTileSource.type = "video/ogg; codecs='theora, vorbis'";
                } else if (strVideo.indexOf(".mp4") !== -1) {
                    domTileSource.type = "video/mp4; codecs='avc1.42E01E, mp4a.40.2'";
                } else if (strVideo.indexOf(".webm") !== -1) {
                    domTileSource.type = "video/webm; codecs='vp8, vorbis'";
                }
                domTileVideo.appendChild(domTileSource);
            }
            for (let strVideo of objContent.src) {
                let domTileA = document.createElement("a");
                domTileA.href = strVideo;
                strVideo = strVideo.split(".");
                domTileA.text = "Скачайте видео ." + (strVideo[strVideo.length - 1]);
                domTileVideo.appendChild(domTileA);
            }
        }
        domParentNode.appendChild(domTileVideo);
        arrCss[""] = arrCss[""] || {};
        arrCss[""][domTileVideo.id] = arrCss[""][domTileVideo.id] || {};
        arrCss[""][domTileVideo.id].width = "100%";
        arrCss[""][domTileVideo.id].height = "100%";
        if (objContent.poster) {
            arrCss[""][domParentNode.id]["background-image"] = `url("${objContent.poster}")`;
            arrCss[""][domParentNode.id]["background-position"] = `center center`;
            arrCss[""][domParentNode.id]["background-repeat"] = `no-repeat`;
            arrCss[""][domParentNode.id]["background-size"] = `cover`;
        }
    }

    /**
     * Insert tile image content
     * @param domParentNode
     * @param objContent
     * @param arrCss
     */
    public static insertContentImage(domParentNode: HTMLElement,
                                     objContent: any,
                                     arrCss: Object) {
        if (objContent.src) {
            arrCss[""] = arrCss[""] || {};
            arrCss[""][domParentNode.id] = arrCss[""][domParentNode.id] || {};
            arrCss[""][domParentNode.id]["background-image"] = `url("${objContent.src}")`;
            arrCss[""][domParentNode.id]["background-position"] = `center center`;
            arrCss[""][domParentNode.id]["background-repeat"] = `no-repeat`;
            arrCss[""][domParentNode.id]["background-size"] = `cover`;
        }
    }

    /**
     * Get unique ID
     * @returns {string}
     */
    public static getUUID() {
        let s = "";
        do {
            s += Math.round(Math.random() * 1e6).toString(36);
        } while (s.length < 32);
        return `x${s.substr(1, 7)}-${s.substr(8, 4)}-${s.substr(12, 4)}-${s.substr(16, 4)}-${s.substr(20, 12)}`;
    }

    /**
     * Assign default grid params
     * @param objGridParams
     * @returns {Object}
     */
    public static assignDefaultGridParams(objGridParams: CGridParams): CGridParams {
        /**
         * Field empty grid parameters from default settings
         */
        objGridParams = CSTiles.assignEmpty(objGridParams, new CGridParams(), CGridParams);
        objGridParams.gridID = CSTiles.getUUID();
        /**
         * Field empty grid adaptive settings from base settings
         */
        objGridParams = CSTiles.assignAdaptives(
            objGridParams,
            objGridParams.adaptiveMedia,
            ["gridSize", "tileMargin", "tilePadding"],
            ["gridAdaptiveSize", "tileAdaptiveMargin", "tileAdaptivePadding"]
        );
        return objGridParams;
    }

    /**
     * Assign default tile params
     * @param objGridParams
     * @param arrTiles
     * @returns any
     */
    public static assignDefaultTileParams(objGridParams: CGridParams, arrTiles: Array<CTileParams>): any {
        /**
         * The enumeration of all tiles
         */
        let grid = [];
        for (let objTileID in arrTiles) {
            if (arrTiles.hasOwnProperty(objTileID)) {
                /**
                 * Get params from default grid
                 */
                arrTiles[objTileID].tileMargin = arrTiles[objTileID].tileMargin || objGridParams.tileMargin;
                arrTiles[objTileID].tilePadding = arrTiles[objTileID].tilePadding || objGridParams.tilePadding;
                arrTiles[objTileID].tileContent = arrTiles[objTileID].tileContent || objGridParams.tileContent;
                /**
                 * Field empty tile parameters from default settings
                 */
                arrTiles[objTileID] = CSTiles.assignEmpty(arrTiles[objTileID], new CTileParams(), CTileParams);
                arrTiles[objTileID].tileID = CSTiles.getUUID();
                /**
                 * Get tile parameters from grid? if it is empty
                 */
                arrTiles[objTileID] = CSTiles.assignEmpty(arrTiles[objTileID], objGridParams, CTileParams);
                /**
                 * Field empty tile adaptive settings from base settings
                 */
                arrTiles[objTileID] = CSTiles.assignAdaptives(
                    arrTiles[objTileID],
                    objGridParams.adaptiveMedia,
                    ["tileSize", "tilePosition", "tileMargin", "tilePadding"],
                    ["tileAdaptiveSize", "tileAdaptivePosition", "tileAdaptiveMargin", "tileAdaptivePadding"]
                );
                /**
                 * Set grid position
                 */
                let r = CSTiles.findPosition(
                    grid,
                    objGridParams.gridSize,
                    arrTiles[objTileID].tileSize,
                    arrTiles[objTileID].tilePosition
                );
                grid = r.grid;
                arrTiles[objTileID].tilePosition = [r.x, r.y];
            }
        }
        /**
         * Set adaptive position
         */
        let adaptiveGrid = {};
        for (let strMediaName in objGridParams.adaptiveMedia) {
            if (objGridParams.adaptiveMedia.hasOwnProperty(strMediaName)) {
                adaptiveGrid[strMediaName] = [];
                for (let objTileID in arrTiles) {
                    if (arrTiles.hasOwnProperty(objTileID)) {
                        let r = CSTiles.findPosition(adaptiveGrid[strMediaName], objGridParams.gridAdaptiveSize[strMediaName], arrTiles[objTileID].tileAdaptiveSize[strMediaName], arrTiles[objTileID].tileAdaptivePosition[strMediaName]);
                        if (r) {
                            adaptiveGrid[strMediaName] = r.grid;
                            arrTiles[objTileID].tileAdaptivePosition[strMediaName] = [r.x, r.y];
                        }
                    }
                }
            }
        }
        return {
            _arrTiles: arrTiles,
            grid,
            adaptiveGrid,
        };
    }

    /**
     * Find free position for tile
     * @param grid
     * @param gridSize
     * @param tileSize
     * @param tilePosition
     * @returns {{x: number, y: number, grid: Array<Array<boolean>>}}
     */
    public static findPosition(grid: Array<Array<boolean>>,
                               gridSize: number,
                               tileSize: Array<number>,
                               tilePosition: Array<number>): {x: number, y: number, grid: Array<Array<boolean>>} {
        let x;
        let y;
        let i;
        let j;
        let z;
        if (
            tilePosition &&
            tilePosition.length === 2
        ) {
            x = tilePosition[0];
            y = tilePosition[1];
            do {
                grid = CSTiles.addLineToGrid(grid, gridSize);
            } while (grid.length < y + tileSize[1]);
            for (i = y; i < y + tileSize[1]; i++) {
                for (j = x; j < x + tileSize[0]; j++) {
                    grid[i][j] = true;
                }
            }
            return {
                x,
                y,
                grid,
            };
        } else {
            let isFunded = false;
            z = 0;
            do {
                isFunded = false;
                if ((grid.length - tileSize[1]) > 0) {
                    for (y = 0; y <= grid.length - tileSize[1]; y++) {
                        for (x = 0; x <= grid[y].length - tileSize[0]; x++) {
                            let ifGap = true;
                            for (i = y; i < y + tileSize[1]; i++) {
                                for (j = x; j < x + tileSize[0]; j++) {
                                    if (grid[i][j]) {
                                        ifGap = false;
                                        break;
                                    }
                                }
                                if (!ifGap) {
                                    break;
                                }
                            }
                            if (ifGap) {
                                isFunded = true;
                                break;
                            }
                        }
                        if (isFunded) {
                            break;
                        }
                    }
                }
                if (!isFunded) {
                    grid = CSTiles.addLineToGrid(grid, gridSize);
                }
                z++;
            } while (!isFunded && z < 10);
            if (isFunded) {
                for (i = y; i < y + tileSize[1]; i++) {
                    for (j = x; j < x + tileSize[0]; j++) {
                        grid[i][j] = true;
                    }
                }
                return {
                    x,
                    y,
                    grid,
                };
            } else {
                return null;
            }
        }
    }

    /**
     * Add another line to grid
     * @param grid
     * @param gridSize
     * @returns {Array<Array<boolean>>}
     */
    public static addLineToGrid(grid: Array<Array<boolean>>, gridSize: number): Array<Array<boolean>> {
        grid.push([]);
        for (let i = 0; i < gridSize; i++) {
            grid[grid.length - 1][i] = false;
        }
        return grid;
    }

    /**
     * Assign adaptive settings
     * @param objObject
     * @param arrAdaptiveMedia
     * @param strPropertyFrom
     * @param strPropertyTo
     * @returns {Object}
     */
    public static assignAdaptives(objObject: Object,
                                  arrAdaptiveMedia: Object,
                                  strPropertyFrom: Array<string>,
                                  strPropertyTo: Array<string>): any {
        for (let i in strPropertyFrom) {
            if (strPropertyFrom.hasOwnProperty(i)) {
                objObject = CSTiles.assignAdaptive(objObject, arrAdaptiveMedia, strPropertyFrom[i], strPropertyTo[i]);
            }
        }
        return objObject;
    }

    /**
     * Assign adaptive settings
     * @param objObject
     * @param arrAdaptiveMedia
     * @param strPropertyFrom
     * @param strPropertyTo
     * @returns {Object}
     */
    public static assignAdaptive(objObject: Object,
                                 arrAdaptiveMedia: Object,
                                 strPropertyFrom: string,
                                 strPropertyTo: string): any {
        objObject = objObject || {};
        for (let i in arrAdaptiveMedia) {
            if (arrAdaptiveMedia.hasOwnProperty(i)) {
                objObject[strPropertyTo] = objObject[strPropertyTo] || {};
                if (
                    CSTiles.isEmpty(objObject[strPropertyTo][i]) && !CSTiles.isEmpty(objObject[strPropertyFrom])
                ) {
                    objObject[strPropertyTo][i] = objObject[strPropertyFrom];
                }
            }
        }
        return objObject;
    }

    /**
     * Assign empty parameters with default
     * @param objFirstObject
     * @param objSecondObject
     * @param claObjectClass
     * @returns {Object}
     */
    public static assignEmpty(objFirstObject: Object,
                              objSecondObject: Object,
                              claObjectClass: any): any {
        objFirstObject = objFirstObject || {};

        let objInterface = CSTiles.getInterface(new claObjectClass());
        for (let key in objInterface) {
            if (
                CSTiles.isEmpty(objFirstObject[key]) && !CSTiles.isEmpty(objSecondObject[key]) &&
                typeof objSecondObject[key] === objInterface[key]
            ) {
                objFirstObject[key] = objSecondObject[key];
            }
        }
        return objFirstObject;
    }

    /**
     * Get class interface
     * @param objObject
     * @returns {{}}
     */
    public static getInterface(objObject: Object) {
        let objInterface = {};
        for (let i in objObject) {
            if (objObject.hasOwnProperty(i)) {
                objInterface[i] = typeof(objObject[i]);
            }
        }
        return objInterface;
    }

    /**
     * Check if variable is empty
     * @param variable
     * @returns {boolean}
     */
    public static isEmpty(variable: any) {
        if (
            Array.isArray(variable)
        ) {
            return variable.length === 0;
        } else if (
            typeof variable === "object"
        ) {
            return Object.keys(variable).length === 0;
        } else if (
            typeof variable === "string"
        ) {
            return false;
        } else {
            return typeof variable === "undefined";
        }
    }

    /**
     * Get object dump
     * @param objObject
     * @returns {string}
     */
    public static getObjectToDump(objObject: Object): string {
        let strDump = "";
        for (let i in objObject) {
            if (objObject.hasOwnProperty(i)) {
                strDump += "\t" + i + ":" + objObject[i] + "\r\n";
            }
        }
        return strDump;
    }

    /**
     * Check is instance of
     * @param objFirstObject
     * @param objSecondObject
     * @returns {boolean}
     */
    public static isInstanceOf(objFirstObject: Object,
                               objSecondObject: Object): boolean {
        if (
            !(
                typeof objFirstObject === "object" &&
                objFirstObject &&
                typeof objSecondObject === "object" &&
                objSecondObject
            )
        ) {
            return false;
        }
        for (let i in objFirstObject) {
            if (!objSecondObject.hasOwnProperty(i)) {
                return false;
            }
        }
        for (let i in objSecondObject) {
            if (!objFirstObject.hasOwnProperty(i)) {
                return false;
            }
        }
        return true;
    }

    /**
     * Check input parameters for correctness
     * @param domParentNode
     * @param objGridParams
     * @param arrTiles
     * @returns {boolean}
     */
    public static checkInnerParamsForErrors(domParentNode?: HTMLElement,
                                            objGridParams?: CGridParams,
                                            arrTiles?: Array<CTileParams>) {
        let isError = false;
        if (
            !isError && !(
                typeof domParentNode === "object" &&
                domParentNode instanceof HTMLElement &&
                typeof domParentNode.parentNode === "object" &&
                domParentNode.parentNode instanceof HTMLElement
            )
        ) {
            console.warn("The first parameter should be a DOM element.");
            isError = true;
        }
        if (
            !isError && !(
                typeof domParentNode.parentNode === "object" &&
                domParentNode.parentNode instanceof HTMLElement
            )
        ) {
            console.warn("The first parameter must be an existing DOM element.");
            isError = true;
        }
        if (
            !isError && !(
                typeof objGridParams === "object"
            )
        ) {
            console.warn("The second parameter must be an object.");
            isError = true;
        }
        if (
            !isError && !(
                CSTiles.isInstanceOf(objGridParams, new CGridParams())
            )
        ) {
            console.warn("The object with the parameters of the tile needs to implement the interface:" + "\r\n" + CSTiles.getObjectToDump(CSTiles.getInterface(new CGridParams())));
            isError = true;
        }
        if (
            !isError && !(
                Array.isArray(arrTiles)
            )
        ) {
            console.warn("The third parameter must be an array with the tiles.");
            isError = true;
        }
        if (
            !isError && !(
                arrTiles.length > 0
            )
        ) {
            console.warn("An array of tiles must contain at least one element.");
            isError = true;
        }
        for (let objTileID in arrTiles) {
            if (
                !isError && !(
                    CSTiles.isInstanceOf(arrTiles[objTileID], new CTileParams())
                )
            ) {
                console.warn("Tile number " + objTileID + " must implement the interface:" + "\r\n" + CSTiles.getObjectToDump(CSTiles.getInterface(new CTileParams())));
                isError = true;
            }
        }
        return isError;
    }

    /**
     * CSTiles tile constructor
     * @param domParentNode
     * @param objGridParams
     * @param arrTiles
     */
    constructor(domParentNode?: HTMLElement,
                objGridParams?: CGridParams,
                arrTiles?: Array<CTileParams>) {
        /**
         * Assign default params
         */
        objGridParams = CSTiles.assignDefaultGridParams(objGridParams);
        let {_arrTiles, grid, adaptiveGrid} = CSTiles.assignDefaultTileParams(objGridParams, arrTiles);
        arrTiles = _arrTiles;
        /**
         * Parameter validation errors
         */
        if (!CSTiles.checkInnerParamsForErrors(domParentNode, objGridParams, arrTiles)) {
            let arrCss = {};
            arrCss[""] = {};
            /**
             * Create grid DOM
             */
            let domGrid = document.createElement("div");
            domGrid.id = objGridParams.gridID;
            /**
             * Write grid styles
             */
            arrCss[""][domGrid.id] = {};
            arrCss[""][domGrid.id].display = "block";
            arrCss[""][domGrid.id].position = "relative";
            arrCss[""][domGrid.id].width = "100%";
            arrCss[""][domGrid.id].height = "auto";
            /**
             * Loop tiles
             */
            for (let objTile of arrTiles) {
                /**
                 * Create tile DOM
                 */
                let domTile = document.createElement("div");
                domTile.id = objTile.tileID;
                domGrid.appendChild(domTile);
                /**
                 * Write tile styles
                 */
                arrCss[""][objTile.tileID] = {};
                arrCss[""][objTile.tileID].width = `0`;
                arrCss[""][objTile.tileID].height = `0`;
                arrCss[""][objTile.tileID].display = `block`;
                arrCss[""][objTile.tileID].position = `absolute`;
                arrCss[""][objTile.tileID].padding = `${objTile.tileSize[0] / objGridParams.gridSize * 50}% ${objTile.tileSize[1] / objGridParams.gridSize * 50}%`;
                arrCss[""][objTile.tileID].margin = `${objTile.tilePosition[1] / objGridParams.gridSize * 100}% 0 0 ${objTile.tilePosition[0] / objGridParams.gridSize * 100}%`;
                /**
                 * Create tile wrap DOM
                 */
                let domTileWrap = document.createElement("div");
                domTileWrap.id = CSTiles.getUUID();
                objTile.tileWrapID = domTileWrap.id;
                domTile.appendChild(domTileWrap);
                /**
                 * Write tile wrap style
                 */
                arrCss[""][domTileWrap.id] = {};
                arrCss[""][domTileWrap.id].padding = `${objTile.tileMargin * 0.5}px`;
                arrCss[""][domTileWrap.id].left = `0`;
                arrCss[""][domTileWrap.id].top = `0`;
                arrCss[""][domTileWrap.id].width = `100%`;
                arrCss[""][domTileWrap.id].height = `100%`;
                arrCss[""][domTileWrap.id]["box-sizing"] = `border-box`;
                arrCss[""][domTileWrap.id].display = `block`;
                arrCss[""][domTileWrap.id].position = `absolute`;
                /**
                 * Create tile content DOM
                 */
                let domTileContent = document.createElement("div");
                domTileContent.id = CSTiles.getUUID();
                objTile.tileContentID = domTileWrap.id;
                domTileWrap.appendChild(domTileContent);
                /**
                 * Write tile content style
                 */
                arrCss[""][domTileContent.id] = {};
                arrCss[""][domTileContent.id]["z-index"] = `1`;
                arrCss[""][domTileContent.id].width = `100%`;
                arrCss[""][domTileContent.id].height = `100%`;
                arrCss[""][domTileContent.id]["box-sizing"] = `border-box`;
                arrCss[""][domTileContent.id].overflow = `hidden`;
                /**
                 * Add tile content
                 */
                CSTiles.insertContent(domTileContent, objTile.tileContent, arrCss);
            }
            /**
             * Create fix clean DOM
             */
            let domFix = document.createElement("div");
            domFix.id = CSTiles.getUUID();
            domGrid.appendChild(domFix);
            /**
             * Write fix style
             */
            arrCss[""][domFix.id] = {};
            arrCss[""][domFix.id].position = `relative`;
            arrCss[""][domFix.id].width = `0`;
            arrCss[""][domFix.id].height = `0`;
            arrCss[""][domFix.id].padding = `${(grid.length - 1) / objGridParams.gridSize[0] * 100}% 0 0 0;`;
            arrCss[""][domFix.id]["z-index"] = `0`;
            /**
             * Write adaptive styles
             */
            for (let strMediaName in objGridParams.adaptiveMedia) {
                if (objGridParams.adaptiveMedia.hasOwnProperty(strMediaName)) {
                    let strMediaRange = objGridParams.adaptiveMedia[strMediaName];
                    arrCss[strMediaRange] = {};
                    for (let objTile of arrTiles) {
                        arrCss[strMediaRange][objTile.tileID] = {};
                        arrCss[strMediaRange][objTile.tileID].padding = `${objTile.tileAdaptiveSize[strMediaName][0] / objGridParams.gridAdaptiveSize[strMediaName] * 50}% ${objTile.tileAdaptiveSize[strMediaName][1] / objGridParams.gridAdaptiveSize[strMediaName] * 50}%`;
                        arrCss[strMediaRange][objTile.tileID].margin = `${objTile.tileAdaptivePosition[strMediaName][1] / objGridParams.gridAdaptiveSize[strMediaName] * 100}% 0 0 ${objTile.tileAdaptivePosition[strMediaName][0] / objGridParams.gridAdaptiveSize[strMediaName] * 100}%`;
                        arrCss[strMediaRange][objTile.tileWrapID] = {};
                        arrCss[strMediaRange][objTile.tileWrapID].padding = `${objTile.tileAdaptiveMargin[strMediaName] * 0.5}px`;
                        arrCss[strMediaRange][objTile.tileContentID] = {};
                        arrCss[strMediaRange][objTile.tileContentID].padding = `${objTile.tileAdaptivePadding[strMediaName]}px`;
                    }
                    arrCss[strMediaRange][domFix.id] = {};
                    arrCss[strMediaRange][domFix.id].position = `relative`;
                    arrCss[strMediaRange][domFix.id].width = `0`;
                    arrCss[strMediaRange][domFix.id].height = `0`;
                    arrCss[strMediaRange][domFix.id].padding = `${(adaptiveGrid[strMediaName].length - 1) / objGridParams.gridAdaptiveSize[strMediaName] * 100}% 0 0 0`;
                    arrCss[strMediaRange][domFix.id]["z-index"] = `0`;
                }
            }

            /**
             * Create style DOM
             * @type {HTMLStyleElement|HTMLElement}
             */
            let domStyle = document.createElement("style");
            domStyle.innerHTML = CSTiles.convertCss(arrCss);
            domGrid.appendChild(domStyle);
            /**
             * Add grid in DOM
             */
            domParentNode.appendChild(domGrid);
        }
    }
}
/**
 * Export CSTiles to global
 */
window.CSTiles = CSTiles;