StructureJS

0.15.2

A class based utility library for building modular and scalable web platform applications. Features opt-in classes and utilities which provide a solid foundation and toolset to build your next project.

File: ts/util/BrowserUtil.ts

import EventDispatcher from '../event/EventDispatcher';
import BaseEvent from '../event/BaseEvent';
import Util from '../util/Util';

/**
 * A helper class to detect OS and browsers.
 *
 * @class BrowserUtil
 * @module StructureJS
 * @submodule util
 * @author Robert S. (www.codeBelt.com)
 * @static
 */
class BrowserUtil
{
    /**
     * A reference to the EventDispatcher object.
     *
     * @property _eventDispatcher
     * @type {EventDispatcher}
     * @private
     * @static
     */
    private static _eventDispatcher:EventDispatcher = new EventDispatcher();

    /**
     * A reference to the browser window object.
     *
     * @property _window
     * @type {Window}
     * @private
     * @static
     */
    private static _window:Window = window;

    /**
     * A reference to the getComputedStyle method.
     *
     * @property _styleDeclaration
     * @type {any}
     * @private
     * @static
     */
    private static _styleDeclaration:any = window.getComputedStyle(document.querySelector('body'), ':after');

    /**
     * TODO: YUIDoc_comment
     *
     * @property _onBreakpointChangeHandler
     * @type {any}
     * @private
     * @static
     */
    private static _onBreakpointChangeHandler:any = null;

    /**
     * The delay in milliseconds for the Util.{{#crossLink "Util/debounce:method"}}{{/crossLink}} method that dispatches
     * the BaseEvent.RESIZE event to get the your browser breakpoints. See {{#crossLink "BrowserUtil/getBreakpoint:method"}}{{/crossLink}}.
     *
     * @property debounceDelay
     * @type {number}
     * @default 250
     * @public
     * @static
     */
    public static debounceDelay:number = 250;

    /**
     * The isEnabled property is used to keep track of the enabled state of listening for Breakpoint changes.
     *
     * @property isEnabled
     * @type {boolean}
     * @public
     * @static
     */
    public static isEnabled:boolean = false;

    constructor()
    {
        throw new Error('[BrowserUtil] Do not instantiate the BrowserUtil class because it is a static class.');
    }

    /**
     * Dispatches a breakpoint event.
     *
     * @method enable
     * @public
     * @static
     */
    public static enable():void
    {
        if (BrowserUtil.isEnabled === true)
        {
            return;
        }

        BrowserUtil._onBreakpointChangeHandler = Util.debounce(BrowserUtil._onBreakpointChange, BrowserUtil.debounceDelay, false, BrowserUtil);
        BrowserUtil._window.addEventListener('resize', BrowserUtil._onBreakpointChangeHandler);

        BrowserUtil.isEnabled = true;
    }

    /**
     * @overridden EventDispatcher.disable
     */
    public static disable():void
    {
        if (BrowserUtil.isEnabled === false)
        {
            return;
        }

        BrowserUtil._window.removeEventListener('resize', BrowserUtil._onBreakpointChangeHandler);

        BrowserUtil.isEnabled = false;
    }

    /**
     * Returns the name of the browser.
     *
     * @method browserName
     * @return {string}
     * @public
     * @static
     * @example
     *      BrowserUtil.browserName();
     *      // 'Chrome'
     */
    public static browserName():string
    {
        return BrowserUtil.getBrowser()[0];
    }

    /**
     * Returns the version of the browser.
     *
     * @method browserVersion
     * @param [majorVersion=true] {boolean}
     * @return {number|string}
     * @public
     * @static
     * @example
     *      BrowserUtil.browserVersion();
     *      // 39
     *
     *      BrowserUtil.browserVersion(false);
     *      // '39.0.2171.99'
     */
    public static browserVersion(majorVersion:boolean = true):any
    {
        const version:string = BrowserUtil.getBrowser()[1];

        if (majorVersion === true)
        {
            return parseInt(version, 10);
        }
        else
        {
            return version;
        }
    }

    /**
     * Gets the browser name a user agent.
     *
     * @method getBrowser
     * @public
     * @return {Array.<string>}
     * @static
     * @example
     *      BrowserUtil.getBrowser();
     *      // ["Chrome", "39.0.2171.99"]
     */
    public static getBrowser():Array<string>
    {
        const N = navigator.appName;
        const ua = navigator.userAgent;
        const tem = ua.match(/version\/([\.\d]+)/i);
        let M = ua.match(/(opera|chrome|safari|firefox|msie)\/?\s*(\.?\d+(\.\d+)*)/i);

        if (M && tem != null)
        {
            M[2] = tem[1];
        }

        M = M ? [M[1], M[2]] : [N, navigator.appVersion, '-?'];

        return M;
    }

    /**
     * Determines if the device OS is Android.
     *
     * @method isAndroid
     * @returns {boolean}
     * @public
     * @static
     * @example
     *      BrowserUtil.isAndroid();
     *      // false
     */
    public static isAndroid():boolean
    {
        return !!navigator.userAgent.match(/Android/i);
    }

    /**
     * Determines if the device OS is Android.
     *
     * @method isBlackBerry
     * @returns {boolean}
     * @public
     * @static
     * @example
     *      BrowserUtil.isBlackBerry();
     *      // false
     */
    public static isBlackBerry():boolean
    {
        return Boolean(!!navigator.userAgent.match(/BlackBerry/i) || navigator.userAgent.match(/BB10; Touch/));
    }

    /**
     * Determines if the device OS is IOS.
     *
     * @method isIOS
     * @returns {boolean}
     * @public
     * @static
     * @example
     *      BrowserUtil.isIOS();
     *      // false
     */
    public static isIOS():boolean
    {
        return !!navigator.userAgent.match(/iPhone|iPad|iPod/i);
    }

    /**
     * Determines if the device OS is Opera Mini.
     *
     * @method isOperaMini
     * @returns {boolean}
     * @public
     * @static
     * @example
     *      BrowserUtil.isOperaMini();
     *      // false
     */
    public static isOperaMini():boolean
    {
        return !!navigator.userAgent.match(/Opera Mini/i);
    }

    /**
     * Determines if the device OS is IE Mobile.
     *
     * @method isIEMobile
     * @returns {boolean}
     * @public
     * @static
     * @example
     *      BrowserUtil.isIEMobile();
     *      // false
     */
    public static isIEMobile():boolean
    {
        return !!navigator.userAgent.match(/IEMobile/i);
    }

    /**
     * Determines if the it is run on a mobile or desktop device.
     *
     * @method isMobile
     * @returns {boolean}
     * @public
     * @static
     * @example
     *      BrowserUtil.isMobile();
     *      // false
     */
    public static isMobile():boolean
    {
        return (BrowserUtil.isAndroid() || BrowserUtil.isBlackBerry() || BrowserUtil.isIOS() || BrowserUtil.isOperaMini() || BrowserUtil.isIEMobile());
    }

    /**
     * Determines if the browser can you Browser History push states.
     *
     * @method hasBrowserHistory
     * @returns {boolean}
     * @public
     * @static
     * @example
     *      BrowserUtil.hasBrowserHistory();
     *      // true
     */
    public static hasBrowserHistory():boolean
    {
        return !!(window.history && history.pushState);
    }

    /**
     * Determines if the browser can you Local Storage.
     *
     * @method hasLocalStorage
     * @returns {boolean}
     * @public
     * @static
     * @example
     *      BrowserUtil.hasLocalStorage();
     *      // true
     */
    public static hasLocalStorage():boolean
    {
        try
        {
            return ('localStorage' in window) && window.localStorage !== null;
        }
        catch (error)
        {
            return false;
        }
    }

    /**
     * Determines if the browser can you Session Storage.
     *
     * @method hasSessionStorage
     * @returns {boolean}
     * @public
     * @static
     * @example
     *      BrowserUtil.hasSessionStorage();
     *      // true
     */
    public static hasSessionStorage():boolean
    {
        try
        {
            return ('sessionStorage' in window) && window.sessionStorage !== null;
        }
        catch (error)
        {
            return false;
        }
    }

    /**
     * Get the current breakpoint from the style sheets. You must add a body:after property with a specific content
     * in each of your style sheets for this functionality to work.
     *
     * @method getBreakpoint
     * @returns {string} The string value of the current style sheet.
     * @public
     * @static
     * @example
     *      // For each of your different style sheets add something like below:
     *      // screen_lg.css
     *      body:after {
     *          content: 'screen_lg';  width: 1px; height: 1px; padding: 0; margin: -1px; border: 0; position: absolute; clip: rect(0 0 0 0); overflow: hidden;
     *      }
     *      // screen_sm.css
     *      body:after {
     *          content: 'screen_sm';  width: 1px; height: 1px; padding: 0; margin: -1px; border: 0; position: absolute; clip: rect(0 0 0 0); overflow: hidden;
     *      }
     *
     *      BrowserUtil.getBreakpoint();
     *      // 'screen_lg'
     *
     *      // Add a listener to get notified when the browser is resized:
     *      BrowserUtil.addEventListener(BaseEvent.RESIZE, this._onBreakpointChange, this);
     *      ...
     *      _onBreakpointChange(baseEvent) {
     *          console.log(baseEvent.data);
     *          // 'screen_sm'
     *      }
     */
    public static getBreakpoint()
    {
        return BrowserUtil._styleDeclaration.getPropertyValue('content').replace(/["']/g, '');
    }

    /**
     * TODO: YUIDoc_comment
     *
     * @method addEventListener
     * @public
     * @static
     */
    public static addEventListener(type:string, callback:Function, scope:any, priority:number = 0):void
    {
        BrowserUtil._eventDispatcher.addEventListener(type, callback, scope, priority);
        BrowserUtil.enable();
    }

    /**
     * TODO: YUIDoc_comment
     *
     * @method removeEventListener
     * @public
     * @static
     */
    public static removeEventListener(type:string, callback:Function, scope:any):void
    {
        BrowserUtil._eventDispatcher.removeEventListener(type, callback, scope);
    }

    /**
     * TODO: YUIDoc_comment
     *
     * @method dispatchEvent
     * @public
     * @static
     */
    public static dispatchEvent(type:any, data:any = null):void
    {
        let event:any = type;

        if (typeof event === 'string')
        {
            event = new BaseEvent(type, false, false, data);
        }

        event.target = BrowserUtil;
        event.currentTarget = BrowserUtil;

        BrowserUtil._eventDispatcher.dispatchEvent(event);
    }

    /**
     * Dispatches a breakpoint event.
     *
     * @method _onBreakpointChange
     * @private
     * @static
     */
    private static _onBreakpointChange(event)
    {
        BrowserUtil.dispatchEvent(new BaseEvent(BaseEvent.RESIZE, true, false, BrowserUtil.getBreakpoint()));
    }

}

export default BrowserUtil;