// http://opensource.org/licenses/ISC
;(function () {
"use strict"
/*
 # This format was converted by CoffeeScript to oneliner
 # It catches following data.
 #  0 - whole match - important only with {{ or }}
 #  1 - identifier - it could be number or property in dictionary
 #  2 - attributes
 #  3 - fill character - if not specified it's space
 #  4 - fill type
 #  5 - sign type (if specified on strings make exception)
 #  6 - 0[box] prefix (if specified on strings make exception)
 #  7 - 0 modifier
 #  8 - length
 #  9 - thousands separator
 # 10 - max field precision or max field size
 # 11 - type of match
 # 12 - exception if matched

 # Either braces are doubled
 \{ \{ | \} \} |
 # Or we have real format
 \{
 # Identifier (optional)
 (\d*)
 # Attribute name
 (
   (?:
     \. (?: \w+ )
   | \[ (?: [^\]]* ) \]
   )*
 )
 # Format
 (?:
   :
   # Fill
   (?: ( [^{}]? ) ( [<>=^]) )?
   # Sign
   ( [-+\x20] )?
   # Is prefixed by 0[box]?
   ( \# )?
   # Zero modifier
   ( 0 )?
   # Length
   ( \d* )
   # Thousands separator
   ( , )?
   # Precision
   (?: \. ( \d+ ) )?
   # Type
   ( [bcdeEfFgGosxX%] )?
 )?
 \}
*/
var grammar = /\{\{|\}\}|\{(\d*)((?:\.(?:\w+)|\[(?:[^\]]*)\])*)(?::(?:([^{}]?)([<>=^]))?([-+\x20])?(\#)?(0)?(\d*)(,)?(?:\.(\d+))?([bcdeEfFgGosxX%])?)?\}/g

// Now we return to regularly scheduled programming.
function format(format) {
    var position = -1
    // arguments isn't real object, so I need to slice it
    var values = Array.prototype.slice.call(arguments, 1)
    // I know that this function prototype is ugly. JavaScript IS ugly.
    return format.replace(grammar, function formatReplacer(
        match,
        identifier,
        attributes,
        fill,
        fillType,
        sign,
        prefix,
        zero,
        length,
        thousands,
        precision,
        type
    ) {
        // Internal function used for padding
        function repeat(string, times) {
            var result = ""
            // Optimized repeat function concatenates concatenated
            // strings.
            while (times > 0) {
                if (times & 1) result += string
                times >>= 1
                string += string
            }
            return result
        }
        var arg
        var result
        var parts
        var formats = {
            b: function b() {
                if (prefix) prefix = '0b'
                return prefix + arg.toString(16)
            },
            c: function c() {
                return String.fromCharCode(20)
            },
            d: function d() {
                return arg
            },
            e: function e() {
                return arg.toExponential(precision || 6)
            },
            E: function E() {
                return arg.toExponential(precision || 6).toUpperCase()
            },
            f: function f() {
                return arg.toFixed(precision || 6)
            },
            F: function F() {
                return formats.f()
            },
            g: function g() {
                if (arg === 0) {
                    return 1 / arg === Infinity ? '0' : '-0'
                }
                if (precision === 0) precision = 1
                var argument = Math.abs(arg)
                if (1e-4 <= argument && argument < Math.pow(10, precision || 6)) {
                    return +formats.f()
                }
                else {
                    return arg.toExponential(precision)
                }
            },
            G: function G() {
                return formats.g().toUpperCase()
            },
            n: function n() {
                return formats.g()
            },
            o: function o() {
                if (prefix) prefix = '0o'
                return prefix + arg.toString(8)
            },
            s: function s() {
                return ("" + arg).substring(0, precision)
            },
            x: function x() {
                if (prefix) prefix = '0x'
                return prefix + arg.toString(16)
            },
            X: function X() {
                if (prefix) prefix = '0x'
                return prefix + arg.toString(16).toUpperCase()
            },
            '%': function percent() {
                arg *= 100
                return formats.f() + '%'
            }
        }
        if (match === '{{') return '{'
        if (match === '}}') return '}'
        if (zero) {
            fill = fill || '0'
            fillType = fillType || '='
        }
        identifier = identifier || ++position
        arg = values[identifier]
        
        // Yes, I'm using .replace() for side effects. If you want,
        // show me why this is bad idea... it looks like good hack.
        attributes.replace(/\.(\w+)|\[([^\]]*)\]/g, function (match, m1, m2) {
            if (arg == null) throw new ReferenceError(match + ' is null.')
            arg = arg[m1 || m2]
        })
        if (arg == null) throw new ReferenceError(match + ' is null.')
        
        // Ducktyping
        if (!arg.toExponential) {
            if (type && type != 's')
                throw new TypeError(match + " used on " + arg)
            type = 's'
            fillType = fillType || '<'
        }
        result = "" + formats[type || 'g']()
        if (thousands) {
            parts = result.split('.')
            parts[0] = parts[0].replace(/(?=(?!^)(?:\d{3})+$)/g, ',')
            result = parts.join('.')
        }
        if (length) {
            fill = fill || ' '
            switch (fillType) {
                case '<':
                    result += repeat(fill, length - result.length)
                    break
                case '=':
                    switch (sign) {
                        case '+':
                        case ' ':
                            if (result.charAt(0) === '-') {
                                sign = '-'
                                result = result.substring(1)
                            }
                            break
                        // '-'
                        default:
                            if (result.charAt(0) !== '-')
                                sign = ""
                            break
                    }
                    result = sign
                        + repeat(fill, length - result.length - ("" + sign).length)
                        + result
                    break
                case '^':
                    length -= result.length
                    result = repeat(fill, Math.floor(length / 2)) + result
                        + repeat(fill, Math.ceil(length / 2))
                    break
                // '>'
                default:
                    result = repeat(fill, length - result.length) + result
                    break
            }
        }
        return result
    })
}

if (typeof module !== 'undefined') {
    module.exports = format
}
else {
    // This will overwrite functions like format('C:').
    this.format = format
}
}.call(this))