Built motion from commit 264c1c7.|0.0.142
[motion.git] / public / bower_components / angular-object-diff / angular-object-diff.js
1 (function () {
2     'use strict';
3
4     angular
5         .module('ds.objectDiff', [])
6         .factory('ObjectDiff', objectDiff)
7         .filter('toJsonView', toJsonViewFilter)
8         .filter('toJsonDiffView', toJsonDiffViewFilter)
9         .filter('objToJsonView', objToJsonViewFilter);
10     
11     objectDiff.$inject = ['$sce'];
12     toJsonViewFilter.$inject = ['ObjectDiff'];
13     toJsonDiffViewFilter.$inject = ['ObjectDiff'];
14     objToJsonViewFilter.$inject = ['ObjectDiff'];
15
16     /* service implementation */
17     function objectDiff($sce) {
18
19         var openChar = '{',
20             closeChar = '}',
21             service = {
22                 setOpenChar: setOpenChar,
23                 setCloseChar: setCloseChar,
24                 diff: diff,
25                 diffOwnProperties: diffOwnProperties,
26                 toJsonView: formatToJsonXMLString,
27                 objToJsonView: formatObjToJsonXMLString,
28                 toJsonDiffView: formatChangesToXMLString
29             };
30
31         return service;
32
33
34         /* service methods */
35
36         /**
37          * @param char
38          */
39         function setOpenChar(char) {
40             openChar = char;
41         }
42
43         /**
44          * @param char
45          */
46         function setCloseChar(char) {
47             closeChar = char;
48         }
49
50         /**
51          * diff between object a and b
52          * @param {Object} a
53          * @param {Object} b
54          * @param shallow
55          * @param isOwn
56          * @return {Object}
57          */
58         function diff(a, b, shallow, isOwn) {
59
60             if (a === b) {
61                 return equalObj(a);
62             }
63
64             var diffValue = {};
65             var equal = true;
66
67             for (var key in a) {
68                 if ((!isOwn && key in b) || (isOwn && b.hasOwnProperty(key))) {
69                     if (a[key] === b[key]) {
70                         diffValue[key] = equalObj(a[key]);
71                     } else {
72                         if (!shallow && isValidAttr(a[key], b[key])) {
73                             var valueDiff = diff(a[key], b[key], isOwn);
74                             if (valueDiff.changed == 'equal') {
75                                 diffValue[key] = equalObj(a[key]);
76                             } else {
77                                 equal = false;
78                                 diffValue[key] = valueDiff;
79                             }
80                         } else {
81                             equal = false;
82                             diffValue[key] = {
83                                 changed: 'primitive change',
84                                 removed: a[key],
85                                 added: b[key]
86                             }
87                         }
88                     }
89                 } else {
90                     equal = false;
91                     diffValue[key] = {
92                         changed: 'removed',
93                         value: a[key]
94                     }
95                 }
96             }
97
98             for (key in b) {
99                 if ((!isOwn && !(key in a)) || (isOwn && !a.hasOwnProperty(key))) {
100                     equal = false;
101                     diffValue[key] = {
102                         changed: 'added',
103                         value: b[key]
104                     }
105                 }
106             }
107
108             if (equal) {
109                 return equalObj(a);
110             } else {
111                 return {
112                     changed: 'object change',
113                     value: diffValue
114                 }
115             }
116         }
117
118
119         /**
120          * diff between object a and b own properties only
121          * @param {Object} a
122          * @param {Object} b
123          * @return {Object}
124          * @param deep
125          */
126         function diffOwnProperties(a, b, deep) {
127             return diff(a, b, deep, true);
128         }
129
130         /**
131          * Convert to a readable xml/html Json structure
132          * @param {Object} changes
133          * @return {string}
134          * @param shallow
135          */
136         function formatToJsonXMLString(changes, shallow) {
137             var properties = [];
138
139             var diff = changes.value;
140             if (changes.changed == 'equal') {
141                 return inspect(diff, shallow);
142             }
143
144             for (var key in diff) {
145                 properties.push(formatChange(key, diff[key], shallow));
146             }
147
148             return $sce.trustAsHtml('<span>' + openChar + '</span>\n<div class="diff-level">' + properties.join('<span>,</span>\n') + '\n</div><span>' + openChar + '</span>');
149
150         }
151
152         /**
153          * Convert to a readable xml/html Json structure
154          * @return {string}
155          * @param obj
156          * @param shallow
157          */
158         function formatObjToJsonXMLString(obj, shallow) {
159             return $sce.trustAsHtml(inspect(obj, shallow));
160         }
161
162         /**
163          * Convert to a readable xml/html Json structure
164          * @param {Object} changes
165          * @return {string}
166          * @param shallow
167          */
168         function formatChangesToXMLString(changes, shallow) {
169             var properties = [];
170
171             if (changes.changed == 'equal') {
172                 return '';
173             }
174
175             var diff = changes.value;
176
177             for (var key in diff) {
178                 var changed = diff[key].changed;
179                 if (changed !== 'equal')
180                     properties.push(formatChange(key, diff[key], shallow, true));
181             }
182
183             return $sce.trustAsHtml('<span>' + openChar + '</span>\n<div class="diff-level">' + properties.join('<span>,</span>\n') + '\n</div><span>' + closeChar + '</span>');
184
185         }
186
187         /**
188          * @param obj
189          * @returns {{changed: string, value: *}}
190          */
191         function equalObj(obj) {
192             return {
193                 changed: 'equal',
194                 value: obj
195             }
196         }
197
198         /**
199          * @param a
200          * @param b
201          * @returns {*|boolean}
202          */
203         function isValidAttr(a, b) {
204             var typeA = typeof a;
205             var typeB = typeof b;
206             return (a && b && (typeA == 'object' || typeA == 'function') && (typeB == 'object' || typeB == 'function'));
207         }
208
209         /**
210          * @param key
211          * @param diffItem
212          * @returns {*}
213          * @param shallow
214          * @param diffOnly
215          */
216         function formatChange(key, diffItem, shallow, diffOnly) {
217             var changed = diffItem.changed;
218             var property;
219             switch (changed) {
220                 case 'equal':
221                     property = (stringifyObjectKey(escapeHTML(key)) + '<span>: </span>' + inspect(diffItem.value));
222                     break;
223
224                 case 'removed':
225                     property = ('<del class="diff">' + stringifyObjectKey(escapeHTML(key)) + '<span>: </span>' + inspect(diffItem.value) + '</del>');
226                     break;
227
228                 case 'added':
229                     property = ('<ins class="diff">' + stringifyObjectKey(escapeHTML(key)) + '<span>: </span>' + inspect(diffItem.value) + '</ins>');
230                     break;
231
232                 case 'primitive change':
233                     var prefix = stringifyObjectKey(escapeHTML(key)) + '<span>: </span>';
234                     property = (
235                     '<del class="diff diff-key">' + prefix + inspect(diffItem.removed) + '</del><span>,</span>\n' +
236                     '<ins class="diff diff-key">' + prefix + inspect(diffItem.added) + '</ins>');
237                     break;
238
239                 case 'object change':
240                     property = shallow ? '' : (stringifyObjectKey(key) + '<span>: </span>' + ( diffOnly ? formatChangesToXMLString(diffItem) : formatToJsonXMLString(diffItem)));
241                     break;
242             }
243
244             return property;
245         }
246
247         /**
248          * @param {string} key
249          * @return {string}
250          */
251         function stringifyObjectKey(key) {
252             return /^[a-z0-9_$]*$/i.test(key) ?
253                 key :
254                 JSON.stringify(key);
255         }
256
257         /**
258          * @param {string} string
259          * @return {string}
260          */
261         function escapeHTML(string) {
262             return string.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
263         }
264
265         /**
266          * @param {Object} obj
267          * @return {string}
268          * @param shallow
269          */
270         function inspect(obj, shallow) {
271
272             return _inspect('', obj, shallow);
273
274             /**
275              * @param {string} accumulator
276              * @param {object} obj
277              * @see http://jsperf.com/continuation-passing-style/3
278              * @return {string}
279              * @param shallow
280              */
281             function _inspect(accumulator, obj, shallow) {
282                 switch (typeof obj) {
283                     case 'object':
284                         if (!obj) {
285                             accumulator += 'null';
286                             break;
287                         }
288                         if (shallow) {
289                             accumulator += '[object]';
290                             break;
291                         }
292                         var keys = Object.keys(obj);
293                         var length = keys.length;
294                         if (length === 0) {
295                             accumulator += '<span>' + openChar + closeChar + '</span>';
296                         } else {
297                             accumulator += '<span>' + openChar + '</span>\n<div class="diff-level">';
298                             for (var i = 0; i < length; i++) {
299                                 var key = keys[i];
300                                 accumulator = _inspect(accumulator + stringifyObjectKey(escapeHTML(key)) + '<span>: </span>', obj[key]);
301                                 if (i < length - 1) {
302                                     accumulator += '<span>,</span>\n';
303                                 }
304                             }
305                             accumulator += '\n</div><span>' + closeChar + '</span>'
306                         }
307                         break;
308
309                     case 'string':
310                         accumulator += JSON.stringify(escapeHTML(obj));
311                         break;
312
313                     case 'undefined':
314                         accumulator += 'undefined';
315                         break;
316
317                     default:
318                         accumulator += escapeHTML(String(obj));
319                         break;
320                 }
321                 return accumulator;
322             }
323         }
324     }
325
326     /* filter implementation */
327     function toJsonViewFilter(ObjectDiff) {
328         return function (value) {
329             return ObjectDiff.toJsonView(value);
330         };
331     }
332
333     function toJsonDiffViewFilter(ObjectDiff) {
334         return function (value) {
335             return ObjectDiff.toJsonDiffView(value);
336         };
337     }
338
339     function objToJsonViewFilter(ObjectDiff) {
340         return function (value) {
341             return ObjectDiff.objToJsonView(value);
342         };
343     }
344 })();