a23104e955017355df53d7f3cfdff521ce3cf938
[platform/framework/web/crosswalk-tizen.git] /
1 var concatMap = require('concat-map');
2 var balanced = require('balanced-match');
3
4 module.exports = expandTop;
5
6 var escSlash = '\0SLASH'+Math.random()+'\0';
7 var escOpen = '\0OPEN'+Math.random()+'\0';
8 var escClose = '\0CLOSE'+Math.random()+'\0';
9 var escComma = '\0COMMA'+Math.random()+'\0';
10 var escPeriod = '\0PERIOD'+Math.random()+'\0';
11
12 function numeric(str) {
13   return parseInt(str, 10) == str
14     ? parseInt(str, 10)
15     : str.charCodeAt(0);
16 }
17
18 function escapeBraces(str) {
19   return str.split('\\\\').join(escSlash)
20             .split('\\{').join(escOpen)
21             .split('\\}').join(escClose)
22             .split('\\,').join(escComma)
23             .split('\\.').join(escPeriod);
24 }
25
26 function unescapeBraces(str) {
27   return str.split(escSlash).join('\\')
28             .split(escOpen).join('{')
29             .split(escClose).join('}')
30             .split(escComma).join(',')
31             .split(escPeriod).join('.');
32 }
33
34
35 // Basically just str.split(","), but handling cases
36 // where we have nested braced sections, which should be
37 // treated as individual members, like {a,{b,c},d}
38 function parseCommaParts(str) {
39   if (!str)
40     return [''];
41
42   var parts = [];
43   var m = balanced('{', '}', str);
44
45   if (!m)
46     return str.split(',');
47
48   var pre = m.pre;
49   var body = m.body;
50   var post = m.post;
51   var p = pre.split(',');
52
53   p[p.length-1] += '{' + body + '}';
54   var postParts = parseCommaParts(post);
55   if (post.length) {
56     p[p.length-1] += postParts.shift();
57     p.push.apply(p, postParts);
58   }
59
60   parts.push.apply(parts, p);
61
62   return parts;
63 }
64
65 function expandTop(str) {
66   if (!str)
67     return [];
68
69   return expand(escapeBraces(str), true).map(unescapeBraces);
70 }
71
72 function identity(e) {
73   return e;
74 }
75
76 function embrace(str) {
77   return '{' + str + '}';
78 }
79 function isPadded(el) {
80   return /^-?0\d/.test(el);
81 }
82
83 function lte(i, y) {
84   return i <= y;
85 }
86 function gte(i, y) {
87   return i >= y;
88 }
89
90 function expand(str, isTop) {
91   var expansions = [];
92
93   var m = balanced('{', '}', str);
94   if (!m || /\$$/.test(m.pre)) return [str];
95
96   var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
97   var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
98   var isSequence = isNumericSequence || isAlphaSequence;
99   var isOptions = /^(.*,)+(.+)?$/.test(m.body);
100   if (!isSequence && !isOptions) {
101     // {a},b}
102     if (m.post.match(/,.*}/)) {
103       str = m.pre + '{' + m.body + escClose + m.post;
104       return expand(str);
105     }
106     return [str];
107   }
108
109   var n;
110   if (isSequence) {
111     n = m.body.split(/\.\./);
112   } else {
113     n = parseCommaParts(m.body);
114     if (n.length === 1) {
115       // x{{a,b}}y ==> x{a}y x{b}y
116       n = expand(n[0], false).map(embrace);
117       if (n.length === 1) {
118         var post = m.post.length
119           ? expand(m.post, false)
120           : [''];
121         return post.map(function(p) {
122           return m.pre + n[0] + p;
123         });
124       }
125     }
126   }
127
128   // at this point, n is the parts, and we know it's not a comma set
129   // with a single entry.
130
131   // no need to expand pre, since it is guaranteed to be free of brace-sets
132   var pre = m.pre;
133   var post = m.post.length
134     ? expand(m.post, false)
135     : [''];
136
137   var N;
138
139   if (isSequence) {
140     var x = numeric(n[0]);
141     var y = numeric(n[1]);
142     var width = Math.max(n[0].length, n[1].length)
143     var incr = n.length == 3
144       ? Math.abs(numeric(n[2]))
145       : 1;
146     var test = lte;
147     var reverse = y < x;
148     if (reverse) {
149       incr *= -1;
150       test = gte;
151     }
152     var pad = n.some(isPadded);
153
154     N = [];
155
156     for (var i = x; test(i, y); i += incr) {
157       var c;
158       if (isAlphaSequence) {
159         c = String.fromCharCode(i);
160         if (c === '\\')
161           c = '';
162       } else {
163         c = String(i);
164         if (pad) {
165           var need = width - c.length;
166           if (need > 0) {
167             var z = new Array(need + 1).join('0');
168             if (i < 0)
169               c = '-' + z + c.slice(1);
170             else
171               c = z + c;
172           }
173         }
174       }
175       N.push(c);
176     }
177   } else {
178     N = concatMap(n, function(el) { return expand(el, false) });
179   }
180
181   for (var j = 0; j < N.length; j++) {
182     for (var k = 0; k < post.length; k++) {
183       var expansion = pre + N[j] + post[k];
184       if (!isTop || isSequence || expansion)
185         expansions.push(expansion);
186     }
187   }
188
189   return expansions;
190 }
191