/**
 * fputils.js
 *
 * JavaScript library functions.
 *
 * @author Matthew K. Coughlin
 */
(function(){

// Local variables

var window = this;   // Needed for web workers, etc

var appName = navigator.appName.toLowerCase();
var userAgent = navigator.userAgent.toLowerCase();


// Helper functions

function getCssVariations(style) {
    return [style, '-moz-' + style, '-webkit-' + style, '-o-' + style,
            '-ms-' + style, '-khtml-' + style];
}


/**
 * FpUtils constructor
 *
 * Static properties:
 *     imageCache
 *     isHighContrast
 *     browser
 *
 * Static methods:
 *     rand(x)
 *     mod(i, max)
 *     limit(i, max)
 *     decimalPlaces(value, places)
 *     roundAngle(angle)
 *     angleToRadians(angle)
 *     swap(a, b, aScope, bScope)
 *
 *     replace(s, pattern)
 *     sort(x)
 *
 *     getBaseUrl()
 *     getUrlParams(url)
 *
 *     getKey(e)
 *
 *     detectHighContrast()
 *     verifyMouseOutEvent(e, elem)
 *
 *     createElement(data)
 *     cacheImage(url)
 *     displayImage(image, element)
 *
 *     cssToDom(css)
 *     css(element, name, value)
 *     cssIsSupported(css, debug)
 */
FpUtils = window.FpUtils = window.$fp = function() {}


// Static properties

FpUtils.debugInfo = '';

FpUtils.isHighContrast = false;

FpUtils.browser = {
    // Broswer engine
    version:
        (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1],

    ie: ((appName == "microsoft internet explorer") ||
         (userAgent.indexOf("msie") != -1)),

    gecko: ((userAgent.indexOf("gecko") != -1) &&
         (userAgent.indexOf("like gecko") == -1)),

    webkit: (userAgent.indexOf("webkit") != -1),
    presto: (userAgent.indexOf("presto") != -1),

    // Specific broswer
    ie6: (userAgent.indexOf("msie 6") != -1),
    ie7: (userAgent.indexOf("msie 7") != -1),
    ie8: (userAgent.indexOf("msie 8") != -1),
    ie9: (userAgent.indexOf("msie 9") != -1),
    firefox: (userAgent.indexOf("firefox") != -1),

    firefoxmac: ((userAgent.indexOf("firefox") != -1) &&
                 (userAgent.indexOf('mac') != -1)),

    safari: ((userAgent.indexOf("safari") != -1) &&
             (userAgent.indexOf("chrome") == -1)),

    chrome: (userAgent.indexOf("chrome") != -1),
    opera: (userAgent.indexOf('opera') != -1)
}

FpUtils.imageCache = {};

FpUtils.supportedCss = {};


// Static methods

/**
 * Returns a random number or array element.
 *
 * @param x number or indexed array
 * @return  random number in the range [0..x] if x is a number, or
 *          random array element if x is an indexed array.
 */
FpUtils.rand = function(x) {
    if (typeof x === 'number') {
        var now = new Date();
        var seed = (now.getSeconds() * 1000) + now.getMilliseconds();
        return Math.round(Math.random(seed) * x);
    } else if ((typeof x === 'object') && (x.length != undefined)) {
        return x[ FpUtils.rand(x.length - 1) ];
    } else {
        throw Error('Invalid argument in function rand()');
    }
}


/**
 * Modulus function that works for both positive and negative i values.
 *
 * @param i   integer
 * @param max positive integer
 * @return    integer in the range [0..max)
 * @example
 *     mod(-3, 3)   // 0
 *     mod(-2, 3)   // 1
 *     mod(-1, 3)   // 2
 *     mod( 0, 3)   // 0
 *     mod( 1, 3)   // 1
 *     mod( 2, 3)   // 2
 *     mod( 3, 3)   // 0
 */
FpUtils.mod = function(i, max) {
    if ((typeof i !== 'number') || (typeof max !== 'number') || (max === 0)) {
        throw Error('Missing or invalid arguments in function mod()');
    }
    return ((i % max) + max) % max;
}


/**
 * Limits the specified number to a lower and upper value.
 *
 * @param i   integer
 * @param max positive integer
 *
 * @return integer in the range [0..max]
 */
FpUtils.limit = function(i, max) {
    if ((typeof i !== 'number') || (typeof max !== 'number')) {
        throw Error('Missing or invalid arguments in function limit()');
    }
    return Math.min( Math.max(i, 0), max );
}


/**
 * Rounds a floating-point value to the specified number
 * of decimal places.
 *
 * @param value  floating point
 * @param places integer
 * @return       floating point
 */
FpUtils.decimalPlaces = function(value, places) {
    var factor = Math.pow(10, places);
    return Math.round(value * factor) / factor;
}


/**
 * Converts an angle from degrees to radians.
 *
 * @param angle degrees
 * @return      radians
 */
FpUtils.angleToRadians = function(angle) {
    return angle * (Math.PI / 180.0);
}


/**
 * Rounds the specified angle to the range [0.0 .. 360.0).
 *
 * @param angle degree
 * @return      degree in the range [0.0 .. 360.0)
 */
FpUtils.roundAngle = function(angle) {
    while (angle < 0.0)    { angle += 360.0; }
    while (angle >= 360.0) { angle -= 360.0; }
    return angle;
}


/**
 * Indirectly swaps two values by reference.
 *
 * The first two parameters specify the variables or array indexes
 * to be swapped. Additional parameters specify the scope to use.
 * If the last scope is omitted, the same scope will be used for both.
 *
 * Doesn't work for primitives with local scope.
 * 
 * @param a      value
 * @param b      value
 * @param aScope scope of a
 * @param bScope scope of b
 * @return       none
 * @example
 *     swap('x', 'y', a);      // Swap:  a.x    a.y
 *     swap('x', 'y', a.b);    // Swap:  a.b.x  a.b.y
 *     swap('x', 'x', a, b);   // Swap:  a.x    b.x
 *     swap(0, 1, a);          // Swap:  a[0]   a[1]
 */
FpUtils.swap = function(a, b, aScope, bScope) {
    if ( // Missing arguments
         (a == undefined) ||
         (b == undefined) ||
         (aScope == undefined) ||

         // Invalid arguments
         (typeof a !== 'string') ||
         (typeof b !== 'string') ||
         (typeof aScope !== 'object') ||

         // Undefined targets
         ((bScope !== undefined) && (typeof bScope !== 'object')) ||
         ((aScope)[a] == undefined) ||
         ((bScope||aScope)[b] == undefined)
    ){
        throw Error('Missing or invalid arguments in function swap()');
    }
    var tmp = (aScope)[a];
    (aScope)[a] = (bScope||aScope)[b];
    (bScope||aScope)[b] = tmp;
}


/**
 * Applies a set of substitutions to a set of strings.
 *
 * @param s       set of strings to update
 * @param pattern set of substitutions to apply
 * @return        updated set of strings
 * @example
 *     replace('abc', 'a', 'd')                       // 'dbc'
 *     replace('abc', ['a','d'])                      // 'dbc'
 *     replace('abc', [['a','d'], ['c','f']])         // 'dbf'
 *
 *     replace(['abc','app'], 'a', 'd')               // ['dbc','dpp']
 *     replace(['abc','app'], ['a','d'])              // ['dbc','dpp']
 *     replace(['abc','uvw'], [['a','d'], ['u','x']]) // ['dbc','xvw']
 */
FpUtils.replace = function(s, pattern) {

    if (s instanceof Array) {
        // Recursively call the function for each element in s
        for (var i = 0; i < s.length; i++) {
            s[i] = FpUtils.replace(s[i], arguments[1], arguments[2]);
        }
    } else if (pattern[0] instanceof Array) {
        // Recursively call the function for each element in pattern
        for (var i = 0; i < pattern.length; i++) {
            s = FpUtils.replace(s, pattern[i]);
        }
    } else if (arguments[2] != undefined) {
        s = s.replace(arguments[1], arguments[2]);   // replace(s, from, to)
    } else {
        s =  s.replace(pattern[0], pattern[1]);      // replace(s, [from, to])
    }
    return s;
}


/**
 * Sorts the characters in a string.
 *
 * @param x string
 * @return  string of sorted characters
 */
FpUtils.sort = function(x) {
    if (typeof x === 'string') {
        return x.split('').sort().join('');   // Sort the chars in the string
    } else {
        return x;
    }
}


/**
 * Gets the URL of the current page minus URL parameters and named anchor.
 *
 * @return URL
 */
FpUtils.getBaseUrl = function(url) {
    if (!url || (typeof url !== 'string')) url = window.location.href;
    var paramLoc = url.indexOf('?');
    var anchorLoc = url.indexOf('#');
    var baseUrl = url;

    if (paramLoc > -1)
        baseUrl = url.substring(0, paramLoc);
    else if (anchorLoc > -1)
        baseUrl = url.substring(0, anchorLoc);
    return baseUrl;
}


/**
 * Gets the URL parameters from the specified URL (or if not specified
 * from the current page), and stores them in an associative array.
 *
 * @param url URL string (optional)
 * @return    associative array containing the name and value of each
 *            URL parameter
 */
FpUtils.getUrlParams = function(url) {
    if (!url || (typeof url !== 'string')) url = window.location.href;

    // Remove the anchor
    var params = {};
    var i = url.indexOf('#');
    if (i != -1) url = url.substring(0, i);

    // Extract the full URL parameters
    var i = url.indexOf('?');
    if ((i != -1) && (i < (url.length - 1))) {

        // Isolate each individual URL parameter
        var arr = url.substring(i + 1).split('&');
        for (j = 0; j < arr.length; j++) {

            // Separate the parameter into a key-value pair
            if (arr[j].indexOf('=') != -1) {
                var curParam = arr[j].split('=');
                params[curParam[0]] = curParam[1];
            } else {
                params[arr[j]] = '';
            }
        }
    }
    return params;
}


/**
 * Analyzes a keyboard event to determine which key and type of key
 * was used.
 *
 * Avoid using this function in combination with any library event
 * function (jQuery, etc) that sanitizes the event properties.
 * The original unsanitized event properties are needed in some cases
 * for Opera.
 *
 * @param e keyboard event
 * @return
 *     associative array with the following properties:
 *         isPrintable     [true,false]
 *         isAlphanumeric  [true,false]
 *         isLetter        [true,false]
 *         isNumber        [true,false]
 *         ifFunctionKey   [true,false]
 *         repeated        [true,false]
 *         shift           [true,false]
 *
 *         ch              ['a'-'z','A'-'Z','0'-'9','!','@',...,'']
 *         letter          ['a'-'z','']
 *         letterOrd       [0-23,'']
 *         number          [0-9,'']
 *         functionKey     [1-12,'']
 *         type            ['number','letter','blankspace','symbol',
 *                               'fkey','left','right','home','end',...,'']
 *         event           original keyboard event
 */
FpUtils.getKey = function(e) {
    if (!e) e = window.event;
    if (e.altKey || e.ctrlKey) return false;
    if (e.type == 'keyup') return false;

    if ( (!(FpUtils.browser.ie || FpUtils.browser.webkit)) &&
         (e.type == 'keydown') )
        return false;   // FF, Opera

    var printableChars = '                                 !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~';
    //alert( printableChars.charAt(125) );

    var mapKeyCode = {8:'backspace', 9:'tab', 13:'enter', 27:'esc',
                      33:'pageup', 34:'pagedown', 35:'end', 36:'home',
                      37:'left', 38:'up', 39:'right', 40:'down', 45:'insert',
                      46:'delete'};

    var key = {isPrintable:false, isAlphanumeric:false, isLetter:false,
               isNumber:false, isFunctionKey:false, shift:e.shiftKey,
               repeated:false, ch:'', letter:'', letterOrd:'', number:'',
               functionKey:'', type:'', event:e};

    // Sanitize which
    var which = ( (!e.which && ((e.charCode || e.charCode === 0) ? e.charCode : e.keyCode)) ? (e.charCode || e.keyCode) : e.which );

    // {   (special handling for left curly brace)
    if ( // IE, WebKit
         ( (FpUtils.browser.ie || FpUtils.browser.webkit) &&
           (e.type == 'keypress') &&
           (e.keyCode == 123) ) ||
         // Opera
         (FpUtils.browser.opera && (e.which == 123))
    ){
        key.isPrintable = true;
        key.ch = '{';
    }
    // F1..F12
    else if ( (e.keyCode >= 112) &&
              (e.keyCode <= 123) &&
              // IE, WebKit
              ((FpUtils.browser.ie || FpUtils.browser.webkit) ==
               (e.type == 'keydown')) &&  
              // Opera
              (!(FpUtils.browser.opera && e.which))
    ){                                   
        key.isFunctionKey = true;
        key.functionKey = e.keyCode - 111;
        key.type = 'fkey';
    }
    // Additional nonprintable characters
    else if ( mapKeyCode[e.keyCode] &&
              // IE, WebKit
              ((FpUtils.browser.ie || FpUtils.browser.webkit) ==
               (e.type == 'keydown')) &&
              // Opera
              (!(FpUtils.browser.opera && e.which && (e.which > 13)))
    ){
        key.type = mapKeyCode[e.keyCode];
    }
    // Printable characters
    else if (e.type == 'keypress') {

        // 0..9
        if ((which >= 48) && (which <= 57)) {
            key.isPrintable = key.isAlphanumeric = key.isNumber = true;
            key.number = which - 48;
            key.ch = printableChars.charAt(which);
            key.type = 'number';
        }
        // A..Z
        else if ((which >= 65) && (which <= 90)) {
            key.isPrintable = key.isAlphanumeric = key.isLetter = true;
            key.letterOrd = which - 65;
            key.letter = key.ch = printableChars.charAt(which);
            key.type = 'letter';
        }
        // a..z
        else if ((which >= 97) && (which <= 122)) {
            key.isPrintable = key.isAlphanumeric = key.isLetter = true;
            key.letterOrd = which - 97;
            key.letter = key.ch = printableChars.charAt(which);
            key.type = 'letter';
        }
        // Additional printable characters
        else if ( (which >= 32) &&
                  (which <= 126) &&
                  ((printableChars.charAt(which) != ' ') || (which == 32))
        ){
            key.isPrintable = true;
            key.ch = printableChars.charAt(which);
            key.type = ((which == 32) ? 'blankspace' : 'symbol');
        }
    }

    // In IE and WebKit, use keypress for printable characters and keydown
    // for nonprintable characters (all other browsers use keypress).
    //
    // Explanation:
    //   * For all browsers, keypress events must be used for alphabetic
    //     keys to detect if caps lock is on.
    //   * In IE and WebKit, keypress events aren't fired for nonprintable
    //     repeated keys (arrow keys, function keys, ...).
    //   * In Opera on Windows, Firefox on Linux, and Konqueror on Linux,
    //     keydown events aren't fired for repeated keys.

    if (FpUtils.browser.ie || FpUtils.browser.webkit) {
        if ((e.type == 'keypress') != key.isPrintable) return false;
    }
    return key;
}


/**
 * Determines if high-contrast mode is being used in Windows.
 *
 * @return true if high-contrast mode is used, otherwise false
 */
FpUtils.detectHighContrast = function() {
    var obj = document.createElement('div');
    var id = 'testHighContrast';
    obj.id = id;
    obj.style.cssText = 'border:1px solid; border-color:red green; position:absolute; height:5px; top:-999px; background-image:url("images/pixel_invis.gif");';
    document.body.appendChild(obj);

    if (document.defaultView != undefined)
        var style = document.defaultView.getComputedStyle(obj, null);
    else
        var style = document.getElementById(id).currentStyle;

    var bgImg = style.backgroundImage;
    FpUtils.isHighContrast =
        (style.borderTopColor == style.borderRightColor) ||
        (bgImg != null && (bgImg == "none" ||
                           bgImg == "url(invalid-url:)"));

    document.body.removeChild(obj);
    return FpUtils.isHighContrast;
}


/**
 * Verifies that a mouseout event wasn't caused by a child element.
 *
 * @param e    mouse event
 * @param elem DOM element that handled the mouse event
 * @return     true if the event was not caused by a child element,
 *             otherwise false

 */
FpUtils.verifyMouseOutEvent = function(e, elem) {
    e = e || window.event;
    var related = e.relatedTarget || e.toElement;

    while ((related != undefined) &&
           (related != elem) &&
           (related.nodeName != 'BODY'))
    {
        related = related.parentNode;
    }
    return (related != elem);
}


/**
 * Creates an HTML DOM element, with the specified attributes.
 *
 * @param data string containing the element name, or an array specifying
 *             the element name and the set of attributes
 * @return     new HTML DOM element
 * @example createElement('div')
 *          createElement({name:'div'})
 *          createElement({name:'div', attr:{id:'myId', class:'myClass'}})
 */
FpUtils.createElement = function(data) {

    if (typeof data === 'string') {
        var obj = document.createElement(data);
    } else {
        var obj = document.createElement(data.tag);

        // Add attributes
        for (var name in data.attr) {
            var value = data.attr[name];
            if (name == 'class')
                obj.className = value;   // IE6/7
            else
                obj.setAttribute(name, value);
        }
    }
    return obj;
}


/**
 * Preloads an image in the browser cache.
 * 
 * @param url URL string for the image
 * @return    none
 */
FpUtils.cacheImage = function(url) {
    if (url && (typeof url === 'string') && !FpUtils.imageCache[url]) {
        FpUtils.imageCache[url] = new Image();
        FpUtils.imageCache[url].src = url;
    }
    else if (url && (typeof url === 'object')) {
        for (var key in url) {
            FpUtils.cacheImage(url[key]);
        }
    }
}


/**
 * Displays a background image, possibly extracted from a sprite file,
 * possibly scaled.
 *
 * @param image
 *     associative array with the following properties:
 *         url, width, height,
 *         [left, top] | [x, y]
 *         [offsetX] [offsetY]
 *         [fileWidth, fileHeight] | [numSprites]
 *         [sizeFactor[.x,.y]] | [sizeX, sizeY]
 *         [angle]
 *         [tile] [initialize]
 *    usage:
 *      - left, top: Place top-left image corner at this position
 *        within element
 *      - x, y: Center the image around this position within the element
 *      - offsetX, offsetY: Location of the image within the sprite file
 *      - fileWidth, fileHeight: Width and height of the sprite file
 *      - numSprites: Number of equal-sized animation cells (must be 2
 *        or more). Each is followed by an empty cell identical in size
 *        to the animation cells. The file consists of a single row
 *        of cells, alternating between animation cells and empty cells.
 * @param element HTML element (DOM or jQuery object) the image
 *                should be displayed in.
 * @return        none
 */
FpUtils.displayImage = function(image, element) {

    // Default values
    if (image.offsetX === undefined)    image.offsetX = 0;
    if (image.offsetY === undefined)    image.offsetY = 0;
    if (image.tile === undefined)       image.tile = false;
    if (image.initialize === undefined) image.initialize = true;

    var jo = (element instanceof jQuery) ? element : $(element); 

    jo.css({'background-image':'url(' + image.url + ')'})
    if (image.initialize) {
        jo.css({'background-repeat':(image.tile ? 'repeat' : 'no-repeat')});
    }
    var ieMatrix = (FpUtils.browser.ie6 ||
                    FpUtils.browser.ie7 ||
                    FpUtils.browser.ie8);

    var resize = ( ( (image.sizeFactor !== undefined) ||
                     (image.sizeX !== undefined) ||
                     (image.sizeY !== undefined) )
                && (ieMatrix || FpUtils.cssIsSupported('background-size')) );
    if (!resize) {
        // Convert (x, y) to (left, top)
        if ((image.x !== undefined) && (image.y !== undefined)) {
            image.left = Math.round(image.x - (image.width  >> 1));
            image.top  = Math.round(image.y - (image.height >> 1));
        }
        jo.css({'background-position':(image.left - image.offsetX) + 'px ' +
                                      (image.top  - image.offsetY) + 'px'});
        if (!image.tile) {
            jo.css({'clip':'rect(' + image.top + 'px ' +
                                     ((image.left + image.width) - 1) + 'px ' +
                                     ((image.top + image.height) - 1) + 'px ' +
                                     image.left + 'px)'});
        }
    } else {
        // Convert (left, top) to (x, y)
        if ((image.left !== undefined) && (image.top !== undefined)) {
            image.x = Math.round(image.left + (image.width  >> 1));
            image.y = Math.round(image.top + (image.height >> 1));
        }
        if (image.sizeFactor !== undefined) {
            // sizeFactor
            if (typeof image.sizeFactor === 'object') {
                if (image.sizeFactor.x === undefined) image.sizeFactor.x = 1;
                if (image.sizeFactor.y === undefined) image.sizeFactor.y = 1;
            } else {
                image.sizeFactor = {x:image.sizeFactor, y:image.sizeFactor};
            }
            var scaled = {
                halfWidth: Math.round(image.width  * image.sizeFactor.x * 0.5),
                halfHeight:Math.round(image.height * image.sizeFactor.y * 0.5)
            };
        } else {
            // sizeX, sizeY
            if (image.sizeX === undefined) image.sizeX = image.width;
            if (image.sizeY === undefined) image.sizeY = image.height;
            var scaled = {
                halfWidth: Math.round(image.sizeX * 0.5),
                halfHeight:Math.round(image.sizeY * 0.5)
            };
        }
        scaled.left = Math.round(image.x - scaled.halfWidth);
        scaled.top  = Math.round(image.y - scaled.halfHeight);

        scaled.width  = scaled.halfWidth  << 1;
        scaled.height = scaled.halfHeight << 1;
        scaled.xRatio = scaled.width  / image.width;
        scaled.yRatio = scaled.height / image.height;
        scaled.offsetX = Math.round(image.offsetX * scaled.xRatio);
        scaled.offsetY = Math.round(image.offsetY * scaled.yRatio);

        var isSprite = ( (image.numSprites !== undefined) ||
                         (image.fileWidth  !== undefined) ||
                         (image.fileHeight !== undefined) );

        if (image.numSprites !== undefined) {
            // numSprites
            if (image.numSprites > 1) {
                scaled.totalWidth  = scaled.width * image.numSprites * 2;
            } else {
                scaled.totalWidth  = scaled.width;
            }
            scaled.totalHeight = scaled.height;
        } else {
            // fileWidth, fileHeight
            if (image.fileWidth === undefined) image.fileWidth  = image.width;
            if (image.fileHeight=== undefined) image.fileHeight = image.height;
            scaled.totalWidth  = Math.round(image.fileWidth  * scaled.xRatio);
            scaled.totalHeight = Math.round(image.fileHeight * scaled.yRatio);
        }

        if (isSprite && !image.tile) {
            jo.css({clip:'rect(' + scaled.top + 'px '
                                 + ((scaled.left + scaled.width) - 1) + 'px '
                                 + ((scaled.top + scaled.height) - 1) + 'px '
                                 + scaled.left + 'px)'});
        }
        if (ieMatrix) {
            jo.css({'background-position':(-image.offsetX) + 'px '
                                        + (-image.offsetY) + 'px'});
            $fp.css(jo, 'filter', 'progid:DXImageTransform.Microsoft.Matrix(' +
                                  'Dx="'  + scaled.left   + '", ' +
                                  'Dy="'  + scaled.top    + '", ' +
                                  'M11="' + scaled.xRatio + '", ' +
                                  'M22="' + scaled.yRatio + '")');
        } else {
            jo.css({'background-position':
                        (scaled.left - scaled.offsetX) + 'px ' +
                        (scaled.top  - scaled.offsetY) + 'px'});
            $fp.css(jo, 'background-size', scaled.totalWidth  + 'px ' +
                                           scaled.totalHeight + 'px');
        }
    }
}


/**
 * Converts a CSS style name to the corresponding HTML DOM name.
 *
 * @param css string containing the name of a CSS style
 * @return    string containing the name of the corresponding
 *            HTML DOM style
 * @example cssToDom('background-size')   // 'backgroundSize' 
 */
FpUtils.cssToDom = function(css) {
    for (var style = css, i; i = style.indexOf('-'), i != -1;) {
        style = style.substr(0, i) +
                style.charAt(i + 1).toUpperCase() +
                style.substr(i + 2);
    }
    //alert(style);
    return style;
}


/**
 * Sets a CSS style for the specified HTML element.
 *
 * @param element HTML element (DOM or jQuery object)
 * @param name    style name
 * @param value   style value
 * @return        none
 */
FpUtils.css = function(element, name, value) {
    if (element && name) {
        var jo = (element instanceof jQuery) ? element : $(element); 
        if (FpUtils.supportedCss[name] !== undefined)
            name = FpUtils.supportedCss[name];
        if (value !== undefined) jo.css(name, value);
        //return jo.css(name);
    }
}


/**
 * Determines if the specified CSS style is supported by the current
 * web browser.
 *
 * @param css   style name
 * @param debug display debugging info
 * @return      true if the CSS style is supported, otherwise false
 */
FpUtils.cssIsSupported = function(css, debug) {

    // Lazy initialization
    if (FpUtils.supportedCss[css] === undefined) {
        var cssVariations = getCssVariations(css);
        var dom = document.createElement('div');
        if (debug) var str = '';

        for (var i in cssVariations) {
            var domStyle = FpUtils.cssToDom(cssVariations[i]);
            if (dom.style[domStyle] !== undefined) {
                if (!FpUtils.supportedCss[css])
                    FpUtils.supportedCss[css] = cssVariations[i];
                if (debug) str += cssVariations[i] + '\n';
                else break;
            }
        }
        if (debug) alert(!!FpUtils.supportedCss[css] + '\n\n' +
                           FpUtils.supportedCss[css] + '\n\n' + str + '\n');
    }
    return FpUtils.supportedCss[css];
}


})();


