Export 0.2.1
[platform/framework/web/web-ui-fw.git] / libs / js / jquery-mobile-1.2.0 / node_modules / grunt / 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-equal');
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 = indent ? Array(
88             ((this.path || []).length + 1) * indent + 1
89         ).join(' ') : '';
90         if (commaFirst) indentx = indentx.slice(indent);
91         
92         if (Array.isArray(node)) {
93             var updated = (prevNode || traverse.has(prev, this.path))
94                 && !Array.isArray(prevNode);
95             if (updated) {
96                 set('updated');
97             }
98             
99             if (opts.comment && !Array.isArray(prevNode)) {
100                 indent = 0;
101             }
102             
103             this.before(function () {
104                 if (inserted) set('inserted');
105                 if (indent && commaFirst) {
106                     if ((this.path || []).length === 0
107                     || Array.isArray(this.parent.node)) {
108                         write('[ ');
109                     }
110                     else write('\n' + indentx + '[ ');
111                 }
112                 else if (indent) {
113                     write('[\n' + indentx);
114                 }
115                 else {
116                     write('[');
117                 }
118             });
119             
120             this.post(function (child) {
121                 if (!child.isLast && !(indent && commaFirst)) {
122                     write(',');
123                 }
124                 
125                 var prev = prevNode && prevNode[child.key];
126                 if (indent && opts.comment && child.node !== prev
127                 && (typeof child.node !== 'object' || typeof prev !== 'object')
128                 ) {
129                     set('comment');
130                     write(' // != ');
131                     traverse(prev).forEach(function (x) {
132                         plainStringify.call(this, x, { indent : 0 });
133                     });
134                     unset('comment');
135                 }
136                 
137                 if (!child.isLast) {
138                     if (indent && commaFirst) {
139                         write('\n' + indentx + ', ');
140                     }
141                     else if (indent) {
142                         write('\n' + indentx);
143                     }
144                 }
145             });
146             
147             this.after(function () {
148                 if (indent && commaFirst) write('\n' + indentx);
149                 else if (indent) write('\n' + indentx.slice(indent));
150                 
151                 write(']');
152                 if (updated) unset('updated');
153                 if (inserted) unset('inserted');
154             });
155         }
156         else if (isRegExp(node)) {
157             this.block();
158             
159             if (inserted) {
160                 set('inserted');
161                 write(node.toString());
162                 unset('inserted');
163             }
164             else if (insertable && prevNode !== node) {
165                 set('updated');
166                 write(node.toString());
167                 unset('updated');
168             }
169             else write(node.toString());
170         }
171         else if (typeof node === 'object'
172         && node && typeof node.inspect === 'function') {
173             this.block();
174             if (inserted) {
175                 set('inserted');
176                 write(node.inspect());
177                 unset('inserted');
178             }
179             else if (!(prevNode && typeof prevNode.inspect === 'function'
180             && prevNode.inspect() === node.inspect())) {
181                 set('updated');
182                 write(node.inspect());
183                 unset('updated');
184             }
185             else write(node.inspect());
186         }
187         else if (typeof node == 'object' && node !== null) {
188             var insertedKey = false;
189             var deleted = insertable && typeof prevNode === 'object' && prevNode
190                 ? Object.keys(prevNode).filter(function (key) {
191                     return !Object.hasOwnProperty.call(node, key);
192                 })
193                 : []
194             ;
195             
196             this.before(function () {
197                 if (inserted) set('inserted');
198                 write(indent && commaFirst && !this.isRoot
199                     ? '\n' + indentx + '{ '
200                     : '{'
201                 );
202             });
203             
204             this.pre(function (x, key) {
205                 if (insertable) {
206                     var obj = traverse.get(prev, this.path.concat(key));
207                     if (obj === undefined) {
208                         insertedKey = true;
209                         set('inserted');
210                     }
211                 }
212                 
213                 if (indent && !commaFirst) write('\n' + indentx);
214                 
215                 plainStringify(key);
216                 write(indent ? ' : ' : ':');
217             });
218             
219             this.post(function (child) {
220                 if (!child.isLast && !(indent && commaFirst)) {
221                     write(',');
222                 }
223                 
224                 if (child.isLast && deleted.length) {
225                     if (insertedKey) unset('inserted');
226                     insertedKey = false;
227                 }
228                 else if (insertedKey) {
229                     unset('inserted');
230                     insertedKey = false;
231                 }
232                 
233                 var prev = prevNode && prevNode[child.key];
234                 if (indent && opts.comment && child.node !== prev
235                 && (typeof child.node !== 'object' || typeof prev !== 'object')
236                 ) {
237                     set('comment');
238                     write(' // != ');
239                     traverse(prev).forEach(function (x) {
240                         plainStringify.call(this, x, { indent : 0 });
241                     });
242                     unset('comment');
243                 }
244                 
245                 if (child.isLast && deleted.length) {
246                     if (insertedKey) unset('inserted');
247                     insertedKey = false;
248                     
249                     if (indent && commaFirst) {
250                         write('\n' + indentx + ', ')
251                     }
252                     else if (opts.comment && indent) {
253                         write('\n' + indentx);
254                     }
255                     else if (indent) {
256                         write(',\n' + indentx);
257                     }
258                     else write(',');
259                 }
260                 else {
261                     if (!child.isLast) {
262                         if (indent && commaFirst) {
263                             write('\n' + indentx + ', ');
264                         }
265                     }
266                 }
267             });
268             
269             this.after(function () {
270                 if (inserted) unset('inserted');
271                 
272                 if (deleted.length) {
273                     if (indent && !commaFirst
274                     && Object.keys(node).length === 0) {
275                         write('\n' + indentx);
276                     }
277                     
278                     set('deleted');
279                     deleted.forEach(function (key, ix) {
280                         if (indent && opts.comment) {
281                             unset('deleted');
282                             set('comment');
283                             write('// ');
284                             unset('comment');
285                             set('deleted');
286                         }
287                         
288                         plainStringify(key);
289                         write(indent ? ' : ' : ':');
290                         traverse(prevNode[key]).forEach(function (x) {
291                             plainStringify.call(this, x, { indent : 0 });
292                         });
293                         
294                         var last = ix === deleted.length - 1;
295                         if (insertable && !last) {
296                             if (indent && commaFirst) {
297                                 write('\n' + indentx + ', ');
298                             }
299                             else if (indent) {
300                                 write(',\n' + indentx);
301                             }
302                             else write(',');
303                         }
304                     });
305                     unset('deleted');
306                 }
307                 
308                 if (commaFirst && indent) {
309                     write(indentx.slice(indent) + ' }');
310                 }
311                 else if (indent) {
312                     write('\n' + indentx.slice(indent) + '}');
313                 }
314                 else write('}');
315             });
316         }
317         else {
318             var changed = false;
319             
320             if (inserted) set('inserted');
321             else if (insertable && !deepEqual(prevNode, node)) {
322                 changed = true;
323                 set('updated');
324             }
325             
326             if (typeof node === 'string') {
327                 write('"' + node.toString().replace(/"/g, '\\"') + '"');
328             }
329             else if (isRegExp(node)) {
330                 write(node.toString());
331             }
332             else if (typeof node === 'function') {
333                 write(node.name
334                     ? '[Function: ' + node.name + ']'
335                     : '[Function]'
336                 );
337             }
338             else if (node === undefined) {
339                 write('undefined');
340             }
341             else if (node === null) {
342                 write('null');
343             }
344             else {
345                 write(node.toString());
346             }
347             
348             if (inserted) unset('inserted');
349             else if (changed) unset('updated');
350         }
351     }
352     
353     if (opts.stream) {
354         traverse(next).forEach(stringify);
355     }
356     else process.nextTick(function () {
357         traverse(next).forEach(stringify);
358         stream.emit('end');
359     });
360     
361     return stream;
362 }
363
364 function isRegExp (node) {
365     return node instanceof RegExp || (node
366         && typeof node.test === 'function' 
367         && typeof node.exec === 'function'
368         && typeof node.compile === 'function'
369         && node.constructor && node.constructor.name === 'RegExp'
370     );
371 }