| 'use strict'; | 
|   | 
| var utils = require('./utils'); | 
|   | 
| module.exports = function(braces, options) { | 
|   braces.compiler | 
|   | 
|     /** | 
|      * bos | 
|      */ | 
|   | 
|     .set('bos', function() { | 
|       if (this.output) return; | 
|       this.ast.queue = isEscaped(this.ast) ? [this.ast.val] : []; | 
|       this.ast.count = 1; | 
|     }) | 
|   | 
|     /** | 
|      * Square brackets | 
|      */ | 
|   | 
|     .set('bracket', function(node) { | 
|       var close = node.close; | 
|       var open = !node.escaped ? '[' : '\\['; | 
|       var negated = node.negated; | 
|       var inner = node.inner; | 
|   | 
|       inner = inner.replace(/\\(?=[\\\w]|$)/g, '\\\\'); | 
|       if (inner === ']-') { | 
|         inner = '\\]\\-'; | 
|       } | 
|   | 
|       if (negated && inner.indexOf('.') === -1) { | 
|         inner += '.'; | 
|       } | 
|       if (negated && inner.indexOf('/') === -1) { | 
|         inner += '/'; | 
|       } | 
|   | 
|       var val = open + negated + inner + close; | 
|       var queue = node.parent.queue; | 
|       var last = utils.arrayify(queue.pop()); | 
|   | 
|       queue.push(utils.join(last, val)); | 
|       queue.push.apply(queue, []); | 
|     }) | 
|   | 
|     /** | 
|      * Brace | 
|      */ | 
|   | 
|     .set('brace', function(node) { | 
|       node.queue = isEscaped(node) ? [node.val] : []; | 
|       node.count = 1; | 
|       return this.mapVisit(node.nodes); | 
|     }) | 
|   | 
|     /** | 
|      * Open | 
|      */ | 
|   | 
|     .set('brace.open', function(node) { | 
|       node.parent.open = node.val; | 
|     }) | 
|   | 
|     /** | 
|      * Inner | 
|      */ | 
|   | 
|     .set('text', function(node) { | 
|       var queue = node.parent.queue; | 
|       var escaped = node.escaped; | 
|       var segs = [node.val]; | 
|   | 
|       if (node.optimize === false) { | 
|         options = utils.extend({}, options, {optimize: false}); | 
|       } | 
|   | 
|       if (node.multiplier > 1) { | 
|         node.parent.count *= node.multiplier; | 
|       } | 
|   | 
|       if (options.quantifiers === true && utils.isQuantifier(node.val)) { | 
|         escaped = true; | 
|   | 
|       } else if (node.val.length > 1) { | 
|         if (isType(node.parent, 'brace') && !isEscaped(node)) { | 
|           var expanded = utils.expand(node.val, options); | 
|           segs = expanded.segs; | 
|   | 
|           if (expanded.isOptimized) { | 
|             node.parent.isOptimized = true; | 
|           } | 
|   | 
|           // if nothing was expanded, we probably have a literal brace | 
|           if (!segs.length) { | 
|             var val = (expanded.val || node.val); | 
|             if (options.unescape !== false) { | 
|               // unescape unexpanded brace sequence/set separators | 
|               val = val.replace(/\\([,.])/g, '$1'); | 
|               // strip quotes | 
|               val = val.replace(/["'`]/g, ''); | 
|             } | 
|   | 
|             segs = [val]; | 
|             escaped = true; | 
|           } | 
|         } | 
|   | 
|       } else if (node.val === ',') { | 
|         if (options.expand) { | 
|           node.parent.queue.push(['']); | 
|           segs = ['']; | 
|         } else { | 
|           segs = ['|']; | 
|         } | 
|       } else { | 
|         escaped = true; | 
|       } | 
|   | 
|       if (escaped && isType(node.parent, 'brace')) { | 
|         if (node.parent.nodes.length <= 4 && node.parent.count === 1) { | 
|           node.parent.escaped = true; | 
|         } else if (node.parent.length <= 3) { | 
|           node.parent.escaped = true; | 
|         } | 
|       } | 
|   | 
|       if (!hasQueue(node.parent)) { | 
|         node.parent.queue = segs; | 
|         return; | 
|       } | 
|   | 
|       var last = utils.arrayify(queue.pop()); | 
|       if (node.parent.count > 1 && options.expand) { | 
|         last = multiply(last, node.parent.count); | 
|         node.parent.count = 1; | 
|       } | 
|   | 
|       queue.push(utils.join(utils.flatten(last), segs.shift())); | 
|       queue.push.apply(queue, segs); | 
|     }) | 
|   | 
|     /** | 
|      * Close | 
|      */ | 
|   | 
|     .set('brace.close', function(node) { | 
|       var queue = node.parent.queue; | 
|       var prev = node.parent.parent; | 
|       var last = prev.queue.pop(); | 
|       var open = node.parent.open; | 
|       var close = node.val; | 
|   | 
|       if (open && close && isOptimized(node, options)) { | 
|         open = '('; | 
|         close = ')'; | 
|       } | 
|   | 
|       // if a close brace exists, and the previous segment is one character | 
|       // don't wrap the result in braces or parens | 
|       var ele = utils.last(queue); | 
|       if (node.parent.count > 1 && options.expand) { | 
|         ele = multiply(queue.pop(), node.parent.count); | 
|         node.parent.count = 1; | 
|         queue.push(ele); | 
|       } | 
|   | 
|       if (close && typeof ele === 'string' && ele.length === 1) { | 
|         open = ''; | 
|         close = ''; | 
|       } | 
|   | 
|       if ((isLiteralBrace(node, options) || noInner(node)) && !node.parent.hasEmpty) { | 
|         queue.push(utils.join(open, queue.pop() || '')); | 
|         queue = utils.flatten(utils.join(queue, close)); | 
|       } | 
|   | 
|       if (typeof last === 'undefined') { | 
|         prev.queue = [queue]; | 
|       } else { | 
|         prev.queue.push(utils.flatten(utils.join(last, queue))); | 
|       } | 
|     }) | 
|   | 
|     /** | 
|      * eos | 
|      */ | 
|   | 
|     .set('eos', function(node) { | 
|       if (this.input) return; | 
|   | 
|       if (options.optimize !== false) { | 
|         this.output = utils.last(utils.flatten(this.ast.queue)); | 
|       } else if (Array.isArray(utils.last(this.ast.queue))) { | 
|         this.output = utils.flatten(this.ast.queue.pop()); | 
|       } else { | 
|         this.output = utils.flatten(this.ast.queue); | 
|       } | 
|   | 
|       if (node.parent.count > 1 && options.expand) { | 
|         this.output = multiply(this.output, node.parent.count); | 
|       } | 
|   | 
|       this.output = utils.arrayify(this.output); | 
|       this.ast.queue = []; | 
|     }); | 
|   | 
| }; | 
|   | 
| /** | 
|  * Multiply the segments in the current brace level | 
|  */ | 
|   | 
| function multiply(queue, n, options) { | 
|   return utils.flatten(utils.repeat(utils.arrayify(queue), n)); | 
| } | 
|   | 
| /** | 
|  * Return true if `node` is escaped | 
|  */ | 
|   | 
| function isEscaped(node) { | 
|   return node.escaped === true; | 
| } | 
|   | 
| /** | 
|  * Returns true if regex parens should be used for sets. If the parent `type` | 
|  * is not `brace`, then we're on a root node, which means we should never | 
|  * expand segments and open/close braces should be `{}` (since this indicates | 
|  * a brace is missing from the set) | 
|  */ | 
|   | 
| function isOptimized(node, options) { | 
|   if (node.parent.isOptimized) return true; | 
|   return isType(node.parent, 'brace') | 
|     && !isEscaped(node.parent) | 
|     && options.expand !== true; | 
| } | 
|   | 
| /** | 
|  * Returns true if the value in `node` should be wrapped in a literal brace. | 
|  * @return {Boolean} | 
|  */ | 
|   | 
| function isLiteralBrace(node, options) { | 
|   return isEscaped(node.parent) || options.optimize !== false; | 
| } | 
|   | 
| /** | 
|  * Returns true if the given `node` does not have an inner value. | 
|  * @return {Boolean} | 
|  */ | 
|   | 
| function noInner(node, type) { | 
|   if (node.parent.queue.length === 1) { | 
|     return true; | 
|   } | 
|   var nodes = node.parent.nodes; | 
|   return nodes.length === 3 | 
|     && isType(nodes[0], 'brace.open') | 
|     && !isType(nodes[1], 'text') | 
|     && isType(nodes[2], 'brace.close'); | 
| } | 
|   | 
| /** | 
|  * Returns true if the given `node` is the given `type` | 
|  * @return {Boolean} | 
|  */ | 
|   | 
| function isType(node, type) { | 
|   return typeof node !== 'undefined' && node.type === type; | 
| } | 
|   | 
| /** | 
|  * Returns true if the given `node` has a non-empty queue. | 
|  * @return {Boolean} | 
|  */ | 
|   | 
| function hasQueue(node) { | 
|   return Array.isArray(node.queue) && node.queue.length; | 
| } |