Name: js-handler/node_modules/nodeunit/node_modules/tap/node_modules/difflet/index.js 
1:
var traverse = require('traverse');
2:
var Stream = require('stream').Stream;
3:
var charm = require('charm');
4:
var deepEqual = require('deep-is');
5:
 
6:
var exports = module.exports = function (opts_) {
7:
    var fn = difflet.bind(null, opts_);
8:
    fn.compare = function (prev, next) {
9:
        var opts = Object.keys(opts_ || {}).reduce(function (acc, key) {
10:
            acc[key] = opts_[key];
11:
            return acc;
12:
        }, {});
13:
        var s = opts.stream = new Stream;
14:
        var data = '';
15:
        s.write = function (buf) { data += buf };
16:
        s.end = function () {};
17:
        s.readable = true;
18:
        s.writable = true;
19:
        
20:
        difflet(opts, prev, next);
21:
        return data;
22:
    };
23:
    return fn;
24:
};
25:
 
26:
exports.compare = function (prev, next) {
27:
    return exports({}).compare(prev, next);
28:
};
29:
 
30:
function difflet (opts, prev, next) {
31:
    var stream = opts.stream || new Stream;
32:
    if (!opts.stream) {
33:
        stream.readable = true;
34:
        stream.writable = true;
35:
        stream.write = function (buf) { this.emit('data', buf) };
36:
        stream.end = function () { this.emit('end') };
37:
    }
38:
    
39:
    if (!opts) opts = {};
40:
    if (opts.start === undefined && opts.stop === undefined) {
41:
        var c = charm(stream);
42:
        opts.start = function (type) {
43:
            c.foreground({
44:
                inserted : 'green',
45:
                updated : 'blue',
46:
                deleted : 'red',
47:
                comment : 'cyan',
48:
            }[type]);
49:
            c.display('bright');
50:
        };
51:
        opts.stop = function (type) {
52:
            c.display('reset');
53:
        };
54:
    }
55:
    var write = function (buf) {
56:
        if (opts.write) opts.write(buf, stream)
57:
        else stream.write(buf)
58:
    };
59:
    
60:
    var commaFirst = opts.comma === 'first';
61:
    
62:
    var stringify = function (node, params) {
63:
        return stringifier.call(this, true, node, params || opts);
64:
    };
65:
    var plainStringify = function (node, params) {
66:
        return stringifier.call(this, false, node, params || opts);
67:
    };
68:
    
69:
    var levels = 0;
70:
    function set (type) {
71:
        if (levels === 0) opts.start(type, stream);
72:
        levels ++;
73:
    }
74:
    
75:
    function unset (type) {
76:
        if (--levels === 0) opts.stop(type, stream);
77:
    }
78:
    
79:
    function stringifier (insertable, node, opts) {
80:
        var indent = opts.indent;
81:
        
82:
        if (insertable) {
83:
            var prevNode = traverse.get(prev, this.path || []);
84:
        }
85:
        var inserted = insertable && prevNode === undefined;
86:
        
87:
        var indentx;
88:
        try {
89:
            indentx = indent ? Array(
90:
                ((this.path || []).length + 1) * indent + 1
91:
            ).join(' ') : '';
92:
        } catch (e) {
93:
            // at times we get an invalid Array size here and need to prevent crashing
94:
            indentx = '';
95:
        }
96:
        if (commaFirst) indentx = indentx.slice(indent);
97:
        
98:
        if (Array.isArray(node)) {
99:
            var updated = (prevNode || traverse.has(prev, this.path))
100:
                && !Array.isArray(prevNode);
101:
            if (updated) {
102:
                set('updated');
103:
            }
104:
            
105:
            if (opts.comment && !Array.isArray(prevNode)) {
106:
                indent = 0;
107:
            }
108:
            
109:
            this.before(function () {
110:
                if (inserted) set('inserted');
111:
                if (indent && commaFirst) {
112:
                    if ((this.path || []).length === 0
113:
                    || Array.isArray(this.parent.node)) {
114:
                        write('[ ');
115:
                    }
116:
                    else write('\n' + indentx + '[ ');
117:
                }
118:
                else if (indent) {
119:
                    write('[\n' + indentx);
120:
                }
121:
                else {
122:
                    write('[');
123:
                }
124:
            });
125:
            
126:
            this.post(function (child) {
127:
                if (!child.isLast && !(indent && commaFirst)) {
128:
                    write(',');
129:
                }
130:
                
131:
                var prev = prevNode && prevNode[child.key];
132:
                if (indent && opts.comment && child.node !== prev
133:
                && (typeof child.node !== 'object' || typeof prev !== 'object')
134:
                ) {
135:
                    set('comment');
136:
                    write(' // != ');
137:
                    traverse(prev).forEach(function (x) {
138:
                        plainStringify.call(this, x, { indent : 0 });
139:
                    });
140:
                    unset('comment');
141:
                }
142:
                
143:
                if (!child.isLast) {
144:
                    if (indent && commaFirst) {
145:
                        write('\n' + indentx + ', ');
146:
                    }
147:
                    else if (indent) {
148:
                        write('\n' + indentx);
149:
                    }
150:
                }
151:
            });
152:
            
153:
            this.after(function () {
154:
                if (indent && commaFirst) write('\n' + indentx);
155:
                else if (indent) write('\n' + indentx.slice(indent));
156:
                
157:
                write(']');
158:
                if (updated) unset('updated');
159:
                if (inserted) unset('inserted');
160:
            });
161:
        }
162:
        else if (isRegExp(node)) {
163:
            this.block();
164:
            
165:
            if (inserted) {
166:
                set('inserted');
167:
                write(node.toString());
168:
                unset('inserted');
169:
            }
170:
            else if (insertable && prevNode !== node) {
171:
                set('updated');
172:
                write(node.toString());
173:
                unset('updated');
174:
            }
175:
            else write(node.toString());
176:
        }
177:
        else if (typeof node === 'object'
178:
        && node && typeof node.inspect === 'function') {
179:
            this.block();
180:
            if (inserted) {
181:
                set('inserted');
182:
                write(node.inspect());
183:
                unset('inserted');
184:
            }
185:
            else if (!(prevNode && typeof prevNode.inspect === 'function'
186:
            && prevNode.inspect() === node.inspect())) {
187:
                set('updated');
188:
                write(node.inspect());
189:
                unset('updated');
190:
            }
191:
            else write(node.inspect());
192:
        }
193:
        else if (typeof node == 'object' && node !== null) {
194:
            var insertedKey = false;
195:
            var deleted = insertable && typeof prevNode === 'object' && prevNode
196:
                ? Object.keys(prevNode).filter(function (key) {
197:
                    return !Object.hasOwnProperty.call(node, key);
198:
                })
199:
                : []
200:
            ;
201:
            
202:
            this.before(function () {
203:
                if (inserted) set('inserted');
204:
                write(indent && commaFirst && !this.isRoot
205:
                    ? '\n' + indentx + '{ '
206:
                    : '{'
207:
                );
208:
            });
209:
            
210:
            this.pre(function (x, key) {
211:
                if (insertable) {
212:
                    var obj = traverse.get(prev, this.path.concat(key));
213:
                    if (obj === undefined) {
214:
                        insertedKey = true;
215:
                        set('inserted');
216:
                    }
217:
                }
218:
                
219:
                if (indent && !commaFirst) write('\n' + indentx);
220:
                
221:
                plainStringify(key);
222:
                write(indent ? ' : ' : ':');
223:
            });
224:
            
225:
            this.post(function (child) {
226:
                if (!child.isLast && !(indent && commaFirst)) {
227:
                    write(',');
228:
                }
229:
                
230:
                if (child.isLast && deleted.length) {
231:
                    if (insertedKey) unset('inserted');
232:
                    insertedKey = false;
233:
                }
234:
                else if (insertedKey) {
235:
                    unset('inserted');
236:
                    insertedKey = false;
237:
                }
238:
                
239:
                var prev = prevNode && prevNode[child.key];
240:
                if (indent && opts.comment && child.node !== prev
241:
                && (typeof child.node !== 'object' || typeof prev !== 'object')
242:
                ) {
243:
                    set('comment');
244:
                    write(' // != ');
245:
                    traverse(prev).forEach(function (x) {
246:
                        plainStringify.call(this, x, { indent : 0 });
247:
                    });
248:
                    unset('comment');
249:
                }
250:
                
251:
                if (child.isLast && deleted.length) {
252:
                    if (insertedKey) unset('inserted');
253:
                    insertedKey = false;
254:
                    
255:
                    if (indent && commaFirst) {
256:
                        write('\n' + indentx + ', ')
257:
                    }
258:
                    else if (opts.comment && indent) {
259:
                        write('\n' + indentx);
260:
                    }
261:
                    else if (indent) {
262:
                        write(',\n' + indentx);
263:
                    }
264:
                    else write(',');
265:
                }
266:
                else {
267:
                    if (!child.isLast) {
268:
                        if (indent && commaFirst) {
269:
                            write('\n' + indentx + ', ');
270:
                        }
271:
                    }
272:
                }
273:
            });
274:
            
275:
            this.after(function () {
276:
                if (inserted) unset('inserted');
277:
                
278:
                if (deleted.length) {
279:
                    if (indent && !commaFirst
280:
                    && Object.keys(node).length === 0) {
281:
                        write('\n' + indentx);
282:
                    }
283:
                    
284:
                    set('deleted');
285:
                    deleted.forEach(function (key, ix) {
286:
                        if (indent && opts.comment) {
287:
                            unset('deleted');
288:
                            set('comment');
289:
                            write('// ');
290:
                            unset('comment');
291:
                            set('deleted');
292:
                        }
293:
                        
294:
                        plainStringify(key);
295:
                        write(indent ? ' : ' : ':');
296:
                        traverse(prevNode[key]).forEach(function (x) {
297:
                            plainStringify.call(this, x, { indent : 0 });
298:
                        });
299:
                        
300:
                        var last = ix === deleted.length - 1;
301:
                        if (insertable && !last) {
302:
                            if (indent && commaFirst) {
303:
                                write('\n' + indentx + ', ');
304:
                            }
305:
                            else if (indent) {
306:
                                write(',\n' + indentx);
307:
                            }
308:
                            else write(',');
309:
                        }
310:
                    });
311:
                    unset('deleted');
312:
                }
313:
                
314:
                if (commaFirst && indent) {
315:
                    write(indentx.slice(indent) + ' }');
316:
                }
317:
                else if (indent) {
318:
                    write('\n' + indentx.slice(indent) + '}');
319:
                }
320:
                else write('}');
321:
            });
322:
        }
323:
        else {
324:
            var changed = false;
325:
            
326:
            if (inserted) set('inserted');
327:
            else if (insertable && !deepEqual(prevNode, node)) {
328:
                changed = true;
329:
                set('updated');
330:
            }
331:
            
332:
            if (typeof node === 'string') {
333:
                write('"' + node.toString().replace(/"/g, '\\"') + '"');
334:
            }
335:
            else if (isRegExp(node)) {
336:
                write(node.toString());
337:
            }
338:
            else if (typeof node === 'function') {
339:
                write(node.name
340:
                    ? '[Function: ' + node.name + ']'
341:
                    : '[Function]'
342:
                );
343:
            }
344:
            else if (node === undefined) {
345:
                write('undefined');
346:
            }
347:
            else if (node === null) {
348:
                write('null');
349:
            }
350:
            else {
351:
                write(node.toString());
352:
            }
353:
            
354:
            if (inserted) unset('inserted');
355:
            else if (changed) unset('updated');
356:
        }
357:
    }
358:
    
359:
    if (opts.stream) {
360:
        traverse(next).forEach(stringify);
361:
    }
362:
    else process.nextTick(function () {
363:
        traverse(next).forEach(stringify);
364:
        stream.emit('end');
365:
    });
366:
    
367:
    return stream;
368:
}
369:
 
370:
function isRegExp (node) {
371:
    return node instanceof RegExp || (node
372:
        && typeof node.test === 'function'
373:
        && typeof node.exec === 'function'
374:
        && typeof node.compile === 'function'
375:
        && node.constructor && node.constructor.name === 'RegExp'
376:
    );
377:
}