~holger-seelig/cobweb.js/trunk

« back to all changes in this revision

Viewing changes to cobweb.js/lib/sprintf.js/src/sprintf.js

  • Committer: Holger Seelig
  • Date: 2017-08-22 04:53:24 UTC
  • Revision ID: holger.seelig@yahoo.de-20170822045324-4of4xxgt79669gbt
Switched to npm.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
(function(window) {
2
 
    var re = {
3
 
        not_string: /[^s]/,
4
 
        number: /[diefg]/,
5
 
        json: /[j]/,
6
 
        not_json: /[^j]/,
7
 
        text: /^[^\x25]+/,
8
 
        modulo: /^\x25{2}/,
9
 
        placeholder: /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-gijosuxX])/,
10
 
        key: /^([a-z_][a-z_\d]*)/i,
11
 
        key_access: /^\.([a-z_][a-z_\d]*)/i,
12
 
        index_access: /^\[(\d+)\]/,
13
 
        sign: /^[\+\-]/
14
 
    }
15
 
 
16
 
    function sprintf() {
17
 
        var key = arguments[0], cache = sprintf.cache
18
 
        if (!(cache[key] && cache.hasOwnProperty(key))) {
19
 
            cache[key] = sprintf.parse(key)
20
 
        }
21
 
        return sprintf.format.call(null, cache[key], arguments)
22
 
    }
23
 
 
24
 
    sprintf.format = function(parse_tree, argv) {
25
 
        var cursor = 1, tree_length = parse_tree.length, node_type = "", arg, output = [], i, k, match, pad, pad_character, pad_length, is_positive = true, sign = ""
26
 
        for (i = 0; i < tree_length; i++) {
27
 
            node_type = get_type(parse_tree[i])
28
 
            if (node_type === "string") {
29
 
                output[output.length] = parse_tree[i]
30
 
            }
31
 
            else if (node_type === "array") {
32
 
                match = parse_tree[i] // convenience purposes only
33
 
                if (match[2]) { // keyword argument
34
 
                    arg = argv[cursor]
35
 
                    for (k = 0; k < match[2].length; k++) {
36
 
                        if (!arg.hasOwnProperty(match[2][k])) {
37
 
                            throw new Error(sprintf("[sprintf] property '%s' does not exist", match[2][k]))
38
 
                        }
39
 
                        arg = arg[match[2][k]]
40
 
                    }
41
 
                }
42
 
                else if (match[1]) { // positional argument (explicit)
43
 
                    arg = argv[match[1]]
44
 
                }
45
 
                else { // positional argument (implicit)
46
 
                    arg = argv[cursor++]
47
 
                }
48
 
 
49
 
                if (get_type(arg) == "function") {
50
 
                    arg = arg()
51
 
                }
52
 
 
53
 
                if (re.not_string.test(match[8]) && re.not_json.test(match[8]) && (get_type(arg) != "number" && isNaN(arg))) {
54
 
                    throw new TypeError(sprintf("[sprintf] expecting number but found %s", get_type(arg)))
55
 
                }
56
 
 
57
 
                if (re.number.test(match[8])) {
58
 
                    is_positive = arg >= 0
59
 
                }
60
 
 
61
 
                switch (match[8]) {
62
 
                    case "b":
63
 
                        arg = arg.toString(2)
64
 
                    break
65
 
                    case "c":
66
 
                        arg = String.fromCharCode(arg)
67
 
                    break
68
 
                    case "d":
69
 
                    case "i":
70
 
                        arg = parseInt(arg, 10)
71
 
                    break
72
 
                    case "j":
73
 
                        arg = JSON.stringify(arg, null, match[6] ? parseInt(match[6]) : 0)
74
 
                    break
75
 
                    case "e":
76
 
                        arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential()
77
 
                    break
78
 
                    case "f":
79
 
                        arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg)
80
 
                    break
81
 
                    case "g":
82
 
                        arg = match[7] ? parseFloat(arg).toPrecision(match[7]) : parseFloat(arg)
83
 
                    break
84
 
                    case "o":
85
 
                        arg = arg.toString(8)
86
 
                    break
87
 
                    case "s":
88
 
                        arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg)
89
 
                    break
90
 
                    case "u":
91
 
                        arg = arg >>> 0
92
 
                    break
93
 
                    case "x":
94
 
                        arg = arg.toString(16)
95
 
                    break
96
 
                    case "X":
97
 
                        arg = arg.toString(16).toUpperCase()
98
 
                    break
99
 
                }
100
 
                if (re.json.test(match[8])) {
101
 
                    output[output.length] = arg
102
 
                }
103
 
                else {
104
 
                    if (re.number.test(match[8]) && (!is_positive || match[3])) {
105
 
                        sign = is_positive ? "+" : "-"
106
 
                        arg = arg.toString().replace(re.sign, "")
107
 
                    }
108
 
                    else {
109
 
                        sign = ""
110
 
                    }
111
 
                    pad_character = match[4] ? match[4] === "0" ? "0" : match[4].charAt(1) : " "
112
 
                    pad_length = match[6] - (sign + arg).length
113
 
                    pad = match[6] ? (pad_length > 0 ? str_repeat(pad_character, pad_length) : "") : ""
114
 
                    output[output.length] = match[5] ? sign + arg + pad : (pad_character === "0" ? sign + pad + arg : pad + sign + arg)
115
 
                }
116
 
            }
117
 
        }
118
 
        return output.join("")
119
 
    }
120
 
 
121
 
    sprintf.cache = {}
122
 
 
123
 
    sprintf.parse = function(fmt) {
124
 
        var _fmt = fmt, match = [], parse_tree = [], arg_names = 0
125
 
        while (_fmt) {
126
 
            if ((match = re.text.exec(_fmt)) !== null) {
127
 
                parse_tree[parse_tree.length] = match[0]
128
 
            }
129
 
            else if ((match = re.modulo.exec(_fmt)) !== null) {
130
 
                parse_tree[parse_tree.length] = "%"
131
 
            }
132
 
            else if ((match = re.placeholder.exec(_fmt)) !== null) {
133
 
                if (match[2]) {
134
 
                    arg_names |= 1
135
 
                    var field_list = [], replacement_field = match[2], field_match = []
136
 
                    if ((field_match = re.key.exec(replacement_field)) !== null) {
137
 
                        field_list[field_list.length] = field_match[1]
138
 
                        while ((replacement_field = replacement_field.substring(field_match[0].length)) !== "") {
139
 
                            if ((field_match = re.key_access.exec(replacement_field)) !== null) {
140
 
                                field_list[field_list.length] = field_match[1]
141
 
                            }
142
 
                            else if ((field_match = re.index_access.exec(replacement_field)) !== null) {
143
 
                                field_list[field_list.length] = field_match[1]
144
 
                            }
145
 
                            else {
146
 
                                throw new SyntaxError("[sprintf] failed to parse named argument key")
147
 
                            }
148
 
                        }
149
 
                    }
150
 
                    else {
151
 
                        throw new SyntaxError("[sprintf] failed to parse named argument key")
152
 
                    }
153
 
                    match[2] = field_list
154
 
                }
155
 
                else {
156
 
                    arg_names |= 2
157
 
                }
158
 
                if (arg_names === 3) {
159
 
                    throw new Error("[sprintf] mixing positional and named placeholders is not (yet) supported")
160
 
                }
161
 
                parse_tree[parse_tree.length] = match
162
 
            }
163
 
            else {
164
 
                throw new SyntaxError("[sprintf] unexpected placeholder")
165
 
            }
166
 
            _fmt = _fmt.substring(match[0].length)
167
 
        }
168
 
        return parse_tree
169
 
    }
170
 
 
171
 
    var vsprintf = function(fmt, argv, _argv) {
172
 
        _argv = (argv || []).slice(0)
173
 
        _argv.splice(0, 0, fmt)
174
 
        return sprintf.apply(null, _argv)
175
 
    }
176
 
 
177
 
    /**
178
 
     * helpers
179
 
     */
180
 
    function get_type(variable) {
181
 
        return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase()
182
 
    }
183
 
 
184
 
    function str_repeat(input, multiplier) {
185
 
        return Array(multiplier + 1).join(input)
186
 
    }
187
 
 
188
 
    /**
189
 
     * export to either browser or node.js
190
 
     */
191
 
    if (typeof exports !== "undefined") {
192
 
        exports.sprintf = sprintf
193
 
        exports.vsprintf = vsprintf
194
 
    }
195
 
    else {
196
 
        window.sprintf = sprintf
197
 
        window.vsprintf = vsprintf
198
 
 
199
 
        if (typeof define === "function" && define.amd) {
200
 
            define(function() {
201
 
                return {
202
 
                    sprintf: sprintf,
203
 
                    vsprintf: vsprintf
204
 
                }
205
 
            })
206
 
        }
207
 
    }
208
 
})(typeof window === "undefined" ? this : window);