// Device.js
// (c) 2014 Matthew Hudson
// Device.js is freely distributable under the MIT license.
// For all details and documentation:
// http://matthewhudson.me/projects/device.js/

(function() {

    var device,
        previousDevice,
        addClass,
        documentElement,
        find,
        handleOrientation,
        hasClass,
        orientationEvent,
        removeClass,
        userAgent;

    // Save the previous value of the device variable.
    previousDevice = window.device;

    device = {};

    // Add device as a global object.
    window.device = device;

    // The <html> element.
    documentElement = window.document.documentElement;

    // The client user agent string.
    // Lowercase, so we can use the more efficient indexOf(), instead of Regex
    userAgent = window.navigator.userAgent.toLowerCase();

    // Main functions
    // --------------
    device.type = false;

    device.ios = function() {
        return device.iphone() || device.ipod() || device.ipad();
    };

    device.iphone = function() {
        return find('iphone');
    };

    device.ipod = function() {
        return find('ipod');
    };

    device.ipad = function() {
        return find('ipad');
    };

    device.android = function() {
        return find('android');
    };

    device.androidPhone = function() {
        return device.android() && find('mobile');
    };

    device.androidTablet = function() {
        return device.android() && !find('mobile');
    };

    device.blackberry = function() {
        return find('blackberry') || find('bb10') || find('rim');
    };

    device.blackberryPhone = function() {
        return device.blackberry() && !find('tablet');
    };

    device.blackberryTablet = function() {
        return device.blackberry() && find('tablet');
    };

    device.windows = function() {
        return find('windows');
    };

    device.windowsPhone = function() {
        return device.windows() && find('phone');
    };

    device.windowsTablet = function() {
        return device.windows() && (find('touch') && !device.windowsPhone());
    };

    device.fxos = function() {
        return (find('(mobile;') || find('(tablet;')) && find('; rv:');
    };

    device.fxosPhone = function() {
        return device.fxos() && find('mobile');
    };

    device.fxosTablet = function() {
        return device.fxos() && find('tablet');
    };

    device.meego = function() {
        return find('meego');
    };

    device.cordova = function() {
        return window.cordova && location.protocol === 'file:';
    };

    device.nodeWebkit = function() {
        return typeof window.process === 'object';
    };

    device.mobile = function() {
        return device.androidPhone() || device.iphone() || device.ipod() || device.windowsPhone() || device.blackberryPhone() || device.fxosPhone() || device.meego();
    };

    device.tablet = function() {
        return device.ipad() || device.androidTablet() || device.blackberryTablet() || device.windowsTablet() || device.fxosTablet();
    };

    device.desktop = function() {
        return !device.tablet() && !device.mobile();
    };

    device.television = function() {
        var i, tvString;

        television = [
            "googletv",
            "viera",
            "smarttv",
            "internet.tv",
            "netcast",
            "nettv",
            "appletv",
            "boxee",
            "kylo",
            "roku",
            "dlnadoc",
            "roku",
            "pov_tv",
            "hbbtv",
            "ce-html"
        ];

        i = 0;
        while(i < television.length) {
            if(find(television[i])) {
                return true;
            }
            i++;
        }
        return false;
    };

    device.portrait = function() {
        return (window.innerHeight / window.innerWidth) > 1;
    };

    device.landscape = function() {
        return (window.innerHeight / window.innerWidth) < 1;
    };

    // Public Utility Functions
    // ------------------------

    // Run device.js in noConflict mode,
    // returning the device variable to its previous owner.
    device.noConflict = function() {
        window.device = previousDevice;
        return this;
    };

    // Private Utility Functions
    // -------------------------

    // Simple UA string search
    find = function(needle) {
        return userAgent.indexOf(needle) !== -1;
    };

    // Check if documentElement already has a given class.
    hasClass = function(className) {
        var regex;
        regex = new RegExp(className, 'i');
        return documentElement.className.match(regex);
    };

    // Add one or more CSS classes to the <html> element.
    addClass = function(className) {
        if(!hasClass(className)) {
            documentElement.className = documentElement.className.trim() + " " + className;
        }
    };

    // Remove single CSS class from the <html> element.
    removeClass = function(className) {
        if(hasClass(className)) {
            documentElement.className = documentElement.className.replace(" " + className, "");
        }
    };

    if(device.ipad() || device.iphone() || device.ipod() ||
        device.android() || device.blackberry() || device.windowsTablet() ||
        device.windowsPhone() || device.fxos() || device.meego() ||
        (/tablet|mobile/i.test(document.body.className.substr(0)))) {
        device.type = true;
    }

    // HTML Element Handling
    // ---------------------

    // Insert the appropriate CSS class based on the _user_agent.

    if(device.ios()) {
        if(device.ipad()) {
            addClass("ios ipad tablet");
        } else if(device.iphone()) {
            addClass("ios iphone mobile");
        } else if(device.ipod()) {
            addClass("ios ipod mobile");
        }
    } else if(device.android()) {
        if(device.androidTablet()) {
            addClass("android tablet");
        } else {
            addClass("android mobile");
        }
    } else if(device.blackberry()) {
        if(device.blackberryTablet()) {
            addClass("blackberry tablet");
        } else {
            addClass("blackberry mobile");
        }
    } else if(device.windows()) {
        if(device.windowsTablet()) {
            addClass("windows tablet");
        } else if(device.windowsPhone()) {
            addClass("windows mobile");
        } else {
            addClass("desktop");
        }
    } else if(device.fxos()) {
        if(device.fxosTablet()) {
            addClass("fxos tablet");
        } else {
            addClass("fxos mobile");
        }
    } else if(device.meego()) {
        addClass("meego mobile");
    } else if(device.nodeWebkit()) {
        addClass("node-webkit");
    } else if(device.television()) {
        addClass("television");
    } else if(device.desktop()) {
        addClass("desktop");
    }

    if(device.cordova()) {
        addClass("cordova");
    }

    // Orientation Handling
    // --------------------

    // Handle device orientation changes.
    handleOrientation = function() {
        if(device.landscape()) {
            removeClass("portrait");
            addClass("landscape");
        } else {
            removeClass("landscape");
            addClass("portrait");
        }
        return;
    };

    // Detect whether device supports orientationchange event,
    // otherwise fall back to the resize event.
    if(window.hasOwnProperty("onorientationchange")) {
        orientationEvent = "orientationchange";
    } else {
        orientationEvent = "resize";
    }

    // Listen for changes in orientation.
    if(window.addEventListener) {
        window.addEventListener(orientationEvent, handleOrientation, false);
    } else if(window.attachEvent) {
        window.attachEvent(orientationEvent, handleOrientation);
    } else {
        window[orientationEvent] = handleOrientation;
    }

    handleOrientation();

    if(typeof define === 'function' && typeof define.amd === 'object' && define.amd) {
        define(function() {
            return device;
        });
    } else if(typeof module !== 'undefined' && module.exports) {
        module.exports = device;
    } else {
        window.device = device;
    }

}).call(this);