mirror of
https://github.com/flynx/diff.js.git
synced 2025-10-28 18:40:09 +00:00
started testing...
Signed-off-by: Alex A. Naanou <alex.nanou@gmail.com>
This commit is contained in:
parent
fe8fed83a4
commit
a7573e8f19
301
diff.js
301
diff.js
@ -64,15 +64,15 @@ var MIN_TEXT_LENGTH = 100
|
|||||||
//
|
//
|
||||||
// This is different to Object.keys(..) in that it gets both enumerable
|
// This is different to Object.keys(..) in that it gets both enumerable
|
||||||
// and non-enumerable keys in the whole prototype chain...
|
// and non-enumerable keys in the whole prototype chain...
|
||||||
|
//
|
||||||
|
// XXX should this be in object.js???
|
||||||
var getAllKeys = function(obj){
|
var getAllKeys = function(obj){
|
||||||
var res = new Set()
|
var res = new Set()
|
||||||
while(obj.__proto__ || obj === obj.__proto__){
|
while(obj.__proto__ || obj === obj.__proto__){
|
||||||
Object.getOwnPropertyNames(obj)
|
Object.getOwnPropertyNames(obj)
|
||||||
.forEach(function(n){
|
.forEach(function(n){
|
||||||
res.add(n)
|
res.add(n) })
|
||||||
})
|
obj = obj.__proto__ }
|
||||||
obj = obj.__proto__
|
|
||||||
}
|
|
||||||
return res }
|
return res }
|
||||||
|
|
||||||
|
|
||||||
@ -82,12 +82,12 @@ var getAllKeys = function(obj){
|
|||||||
// zip(func, array, array, ...)
|
// zip(func, array, array, ...)
|
||||||
// -> [func(i, [item, item, ...]), ...]
|
// -> [func(i, [item, item, ...]), ...]
|
||||||
//
|
//
|
||||||
|
// XXX move this to ig-types
|
||||||
var zip = function(func, ...arrays){
|
var zip = function(func, ...arrays){
|
||||||
var i = arrays[0] instanceof Array ? 0 : arrays.shift()
|
var i = arrays[0] instanceof Array ? 0 : arrays.shift()
|
||||||
if(func instanceof Array){
|
if(func instanceof Array){
|
||||||
arrays.splice(0, 0, func)
|
arrays.splice(0, 0, func)
|
||||||
func = null
|
func = null }
|
||||||
}
|
|
||||||
// build the zip item...
|
// build the zip item...
|
||||||
// NOTE: this is done this way to preserve array sparseness...
|
// NOTE: this is done this way to preserve array sparseness...
|
||||||
var s = arrays
|
var s = arrays
|
||||||
@ -149,8 +149,7 @@ var getCommonSections = function(A, B, cmp, min_chunk){
|
|||||||
&& ((a+l in A && b+l in B) ?
|
&& ((a+l in A && b+l in B) ?
|
||||||
cmp(A[a+l], B[b+l])
|
cmp(A[a+l], B[b+l])
|
||||||
: (!(a+l in A) && !(b+l in B)))){
|
: (!(a+l in A) && !(b+l in B)))){
|
||||||
l = chunk.length += 1
|
l = chunk.length += 1 }
|
||||||
}
|
|
||||||
// ignore small chunks...
|
// ignore small chunks...
|
||||||
l = chunk.length >= min_chunk ?
|
l = chunk.length >= min_chunk ?
|
||||||
chunk.length
|
chunk.length
|
||||||
@ -186,11 +185,9 @@ var getCommonSections = function(A, B, cmp, min_chunk){
|
|||||||
cache[a] = cache[a] || []
|
cache[a] = cache[a] || []
|
||||||
cache[a][b] = res
|
cache[a][b] = res
|
||||||
|
|
||||||
return res
|
return res }
|
||||||
}
|
|
||||||
|
|
||||||
return _getCommonSections(0, 0)
|
return _getCommonSections(0, 0) }
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Get diff sections...
|
// Get diff sections...
|
||||||
@ -233,11 +230,9 @@ var getDiffSections = function(A, B, cmp, min_chunk){
|
|||||||
])
|
])
|
||||||
// go to next gap...
|
// go to next gap...
|
||||||
a = e.A + e.length
|
a = e.A + e.length
|
||||||
b = e.B + e.length
|
b = e.B + e.length })
|
||||||
})
|
|
||||||
|
|
||||||
return gaps
|
return gaps }
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Make a proxy method...
|
// Make a proxy method...
|
||||||
@ -362,8 +357,7 @@ var LogicTypePrototype = {
|
|||||||
// cache...
|
// cache...
|
||||||
c.set(obj, !!res)
|
c.set(obj, !!res)
|
||||||
|
|
||||||
return !!res
|
return !!res },
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var Pattern =
|
var Pattern =
|
||||||
@ -381,8 +375,7 @@ var makeCIPattern = function(name, check, init){
|
|||||||
})
|
})
|
||||||
init
|
init
|
||||||
&& (o.__init__ = init)
|
&& (o.__init__ = init)
|
||||||
return object.Constructor(name, o, o)
|
return object.Constructor(name, o, o) }
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Singleton ANY...
|
// Singleton ANY...
|
||||||
@ -470,7 +463,8 @@ module.STRING =
|
|||||||
: this.value != null ?
|
: this.value != null ?
|
||||||
cmp(this.value, obj)
|
cmp(this.value, obj)
|
||||||
: true )) },
|
: true )) },
|
||||||
function(value){ this.value = value })
|
function(value){
|
||||||
|
this.value = value })
|
||||||
|
|
||||||
// shorthand...
|
// shorthand...
|
||||||
var S = module.S = STRING
|
var S = module.S = STRING
|
||||||
@ -511,7 +505,8 @@ module.NUMBER =
|
|||||||
: this.value[0] != null ?
|
: this.value[0] != null ?
|
||||||
cmp(this.value[0], obj)
|
cmp(this.value[0], obj)
|
||||||
: true )) },
|
: true )) },
|
||||||
function(...value){ this.value = value })
|
function(...value){
|
||||||
|
this.value = value })
|
||||||
|
|
||||||
// shorthand...
|
// shorthand...
|
||||||
var N = module.N = NUMBER
|
var N = module.N = NUMBER
|
||||||
@ -556,7 +551,8 @@ module.ARRAY =
|
|||||||
return cmp(value, e)
|
return cmp(value, e)
|
||||||
}).length == obj.length)
|
}).length == obj.length)
|
||||||
}).length == (this.value || []).length) },
|
}).length == (this.value || []).length) },
|
||||||
function(...value){ this.value = value })
|
function(...value){
|
||||||
|
this.value = value })
|
||||||
|
|
||||||
// shorthand...
|
// shorthand...
|
||||||
// NOTE: yes, ARRAY does not even contain the letter "L" but this is
|
// NOTE: yes, ARRAY does not even contain the letter "L" but this is
|
||||||
@ -583,9 +579,7 @@ object.Constructor('OR', Object.assign(Object.create(Pattern.prototype), {
|
|||||||
__cmp__: function(obj, cmp, context){
|
__cmp__: function(obj, cmp, context){
|
||||||
for(var m of this.members){
|
for(var m of this.members){
|
||||||
if(cmp(m, obj, context)){
|
if(cmp(m, obj, context)){
|
||||||
return true
|
return true } }
|
||||||
}
|
|
||||||
}
|
|
||||||
return false },
|
return false },
|
||||||
__init__: function(...members){
|
__init__: function(...members){
|
||||||
this.members = members },
|
this.members = members },
|
||||||
@ -603,10 +597,8 @@ object.Constructor('XOR', Object.assign(Object.create(Pattern.prototype), {
|
|||||||
for(var m of this.members){
|
for(var m of this.members){
|
||||||
cur = cmp(m, obj, context)
|
cur = cmp(m, obj, context)
|
||||||
if(state == cur && state){
|
if(state == cur && state){
|
||||||
return false
|
return false }
|
||||||
}
|
state = cur }
|
||||||
state = cur
|
|
||||||
}
|
|
||||||
return state },
|
return state },
|
||||||
__init__: function(...members){
|
__init__: function(...members){
|
||||||
this.members = members },
|
this.members = members },
|
||||||
@ -620,9 +612,7 @@ object.Constructor('AND', Object.assign(Object.create(Pattern.prototype), {
|
|||||||
__cmp__: function(obj, cmp, context){
|
__cmp__: function(obj, cmp, context){
|
||||||
for(var m of this.members){
|
for(var m of this.members){
|
||||||
if(!cmp(m, obj, context)){
|
if(!cmp(m, obj, context)){
|
||||||
return false
|
return false } }
|
||||||
}
|
|
||||||
}
|
|
||||||
return true },
|
return true },
|
||||||
__init__: function(...members){
|
__init__: function(...members){
|
||||||
this.members = members },
|
this.members = members },
|
||||||
@ -657,8 +647,7 @@ object.Constructor('VAR', Object.assign(Object.create(Pattern.prototype), {
|
|||||||
: this.pattern
|
: this.pattern
|
||||||
if(cmp(pattern, obj)){
|
if(cmp(pattern, obj)){
|
||||||
ns[this.name] = obj
|
ns[this.name] = obj
|
||||||
return true
|
return true }
|
||||||
}
|
|
||||||
return false },
|
return false },
|
||||||
__init__: function(name, pattern){
|
__init__: function(name, pattern){
|
||||||
this.name = name
|
this.name = name
|
||||||
@ -1011,14 +1000,12 @@ module.Types = {
|
|||||||
Object.create(e[1])
|
Object.create(e[1])
|
||||||
: e[1]
|
: e[1]
|
||||||
] }))
|
] }))
|
||||||
return res
|
return res },
|
||||||
},
|
|
||||||
clear: function(){
|
clear: function(){
|
||||||
// XXX should we instead this.handlers.clear() ???
|
// XXX should we instead this.handlers.clear() ???
|
||||||
//this.handlers = new Map()
|
//this.handlers = new Map()
|
||||||
this.handlers.clear()
|
this.handlers.clear()
|
||||||
return this
|
return this },
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
// Placeholder objects...
|
// Placeholder objects...
|
||||||
@ -1060,15 +1047,13 @@ module.Types = {
|
|||||||
o = h.get(o)
|
o = h.get(o)
|
||||||
} while(o != null && h.has(o))
|
} while(o != null && h.has(o))
|
||||||
|
|
||||||
return o
|
return o },
|
||||||
},
|
|
||||||
set: proxy('handlers.set',
|
set: proxy('handlers.set',
|
||||||
function(res, key, handler){
|
function(res, key, handler){
|
||||||
// auto-alias...
|
// auto-alias...
|
||||||
key.name
|
key.name
|
||||||
&& this.set(key.name, key)
|
&& this.set(key.name, key)
|
||||||
return res
|
return res }),
|
||||||
}),
|
|
||||||
delete: proxy('handlers.delete'),
|
delete: proxy('handlers.delete'),
|
||||||
|
|
||||||
// sorted list of types...
|
// sorted list of types...
|
||||||
@ -1083,8 +1068,7 @@ module.Types = {
|
|||||||
k = h.get(k)
|
k = h.get(k)
|
||||||
return k != null
|
return k != null
|
||||||
&& !h.has(k)
|
&& !h.has(k)
|
||||||
&& order.set(k, i++)
|
&& order.set(k, i++) })
|
||||||
})
|
|
||||||
.sort(function(a, b){
|
.sort(function(a, b){
|
||||||
a = h.get(a)
|
a = h.get(a)
|
||||||
b = h.get(b)
|
b = h.get(b)
|
||||||
@ -1096,11 +1080,11 @@ module.Types = {
|
|||||||
a.priority > 0 ? -1 : 1
|
a.priority > 0 ? -1 : 1
|
||||||
: b.priority ?
|
: b.priority ?
|
||||||
b.priority > 0 ? 1 : -1
|
b.priority > 0 ? 1 : -1
|
||||||
: order.get(a) - order.get(b)
|
: order.get(a) - order.get(b) }) },
|
||||||
})
|
|
||||||
},
|
|
||||||
get typeNames(){
|
get typeNames(){
|
||||||
return this.typeKeys.map(function(e){ return e.name || e }) },
|
return this.typeKeys
|
||||||
|
.map(function(e){
|
||||||
|
return e.name || e }) },
|
||||||
get types(){
|
get types(){
|
||||||
var that = this
|
var that = this
|
||||||
return this.typeKeys
|
return this.typeKeys
|
||||||
@ -1149,9 +1133,7 @@ module.Types = {
|
|||||||
if(h.compatible
|
if(h.compatible
|
||||||
&& h.compatible(A, options)){
|
&& h.compatible(A, options)){
|
||||||
type = t
|
type = t
|
||||||
break
|
break } }
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// search instances...
|
// search instances...
|
||||||
if(!type){
|
if(!type){
|
||||||
@ -1162,16 +1144,12 @@ module.Types = {
|
|||||||
if(t === Object
|
if(t === Object
|
||||||
// skip non-conctructor stuff...
|
// skip non-conctructor stuff...
|
||||||
|| !(t instanceof Function)){
|
|| !(t instanceof Function)){
|
||||||
continue
|
continue }
|
||||||
}
|
|
||||||
|
|
||||||
// full hit -- type match...
|
// full hit -- type match...
|
||||||
if(A instanceof t){
|
if(A instanceof t){
|
||||||
type = t
|
type = t
|
||||||
break
|
break } } }
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// combinational types...
|
// combinational types...
|
||||||
if(B !== undefined){
|
if(B !== undefined){
|
||||||
@ -1183,12 +1161,9 @@ module.Types = {
|
|||||||
|
|
||||||
// partial hit -- type mismatch...
|
// partial hit -- type mismatch...
|
||||||
} else {
|
} else {
|
||||||
return 'Basic'
|
return 'Basic' } }
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return type
|
return type },
|
||||||
},
|
|
||||||
|
|
||||||
// Handle the difference between A and B...
|
// Handle the difference between A and B...
|
||||||
//
|
//
|
||||||
@ -1205,24 +1180,21 @@ module.Types = {
|
|||||||
if(handler == null
|
if(handler == null
|
||||||
|| !(handler instanceof Function
|
|| !(handler instanceof Function
|
||||||
|| handler.handle)){
|
|| handler.handle)){
|
||||||
throw new TypeError('Diff: can\'t handle: ' + type)
|
throw new TypeError('Diff: can\'t handle: ' + type) }
|
||||||
}
|
|
||||||
|
|
||||||
// call the handler...
|
// call the handler...
|
||||||
handler.handle ?
|
handler.handle ?
|
||||||
handler.handle.call(this, obj, diff, A, B, options)
|
handler.handle.call(this, obj, diff, A, B, options)
|
||||||
: handler.call(this, obj, diff, A, B, options)
|
: handler.call(this, obj, diff, A, B, options)
|
||||||
|
|
||||||
return obj
|
return obj },
|
||||||
},
|
|
||||||
|
|
||||||
// Diff format walker...
|
// Diff format walker...
|
||||||
//
|
//
|
||||||
walk: function(diff, func, path){
|
walk: function(diff, func, path){
|
||||||
// no changes...
|
// no changes...
|
||||||
if(diff == null){
|
if(diff == null){
|
||||||
return null
|
return null }
|
||||||
}
|
|
||||||
// flat diff...
|
// flat diff...
|
||||||
if(diff instanceof Array){
|
if(diff instanceof Array){
|
||||||
return diff.map(func)
|
return diff.map(func)
|
||||||
@ -1231,8 +1203,7 @@ module.Types = {
|
|||||||
} else {
|
} else {
|
||||||
var handler = this.get(diff.type)
|
var handler = this.get(diff.type)
|
||||||
if(handler == null || !handler.walk){
|
if(handler == null || !handler.walk){
|
||||||
throw new TypeError('Can\'t walk type: '+ diff.type)
|
throw new TypeError('Can\'t walk type: '+ diff.type) }
|
||||||
}
|
|
||||||
return handler.walk.call(this, diff, func, path || []) } },
|
return handler.walk.call(this, diff, func, path || []) } },
|
||||||
|
|
||||||
// Flatten the tree diff format...
|
// Flatten the tree diff format...
|
||||||
@ -1252,8 +1223,7 @@ module.Types = {
|
|||||||
options = options || {}
|
options = options || {}
|
||||||
var res = []
|
var res = []
|
||||||
this.walk(diff, function(change){ res.push(change) })
|
this.walk(diff, function(change){ res.push(change) })
|
||||||
return res
|
return res },
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
// User API...
|
// User API...
|
||||||
@ -1275,9 +1245,7 @@ module.Types = {
|
|||||||
type.reverse
|
type.reverse
|
||||||
&& (c = type.reverse.call(that, c)) })
|
&& (c = type.reverse.call(that, c)) })
|
||||||
|
|
||||||
return c
|
return c }) },
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// Filter diff changes and return a new diff...
|
// Filter diff changes and return a new diff...
|
||||||
//
|
//
|
||||||
@ -1320,8 +1288,9 @@ module.Types = {
|
|||||||
return e == '*' ? ANY
|
return e == '*' ? ANY
|
||||||
: e[0] == '!' ? NOT(e.slice(1))
|
: e[0] == '!' ? NOT(e.slice(1))
|
||||||
: e })
|
: e })
|
||||||
return e.length == 1 ? e[0] : OR(...e)
|
return e.length == 1 ?
|
||||||
})
|
e[0]
|
||||||
|
: OR(...e) })
|
||||||
: filter
|
: filter
|
||||||
|
|
||||||
// path filter (non-function)...
|
// path filter (non-function)...
|
||||||
@ -1345,8 +1314,7 @@ module.Types = {
|
|||||||
: (res.length == 0 || res[n] == '**') ?
|
: (res.length == 0 || res[n] == '**') ?
|
||||||
res.push([e])
|
res.push([e])
|
||||||
: res[n].push(e)
|
: res[n].push(e)
|
||||||
return res
|
return res }, [])
|
||||||
}, [])
|
|
||||||
|
|
||||||
// min length...
|
// min length...
|
||||||
var min = pattern
|
var min = pattern
|
||||||
@ -1381,11 +1349,9 @@ module.Types = {
|
|||||||
|
|
||||||
// XXX Q: should we ignore the last element of the path???
|
// XXX Q: should we ignore the last element of the path???
|
||||||
filter = function(change, i, lst){
|
filter = function(change, i, lst){
|
||||||
return test(change.path, pattern) }
|
return test(change.path, pattern) } }
|
||||||
}
|
|
||||||
|
|
||||||
return diff.filter(filter.bind(this))
|
return diff.filter(filter.bind(this)) },
|
||||||
},
|
|
||||||
|
|
||||||
// XXX there are two approaches to this:
|
// XXX there are two approaches to this:
|
||||||
// 1) naive: simply concatenate all the changes in order...
|
// 1) naive: simply concatenate all the changes in order...
|
||||||
@ -1393,8 +1359,7 @@ module.Types = {
|
|||||||
// XXX do we need a conflict resolution policy???
|
// XXX do we need a conflict resolution policy???
|
||||||
merge: function(diff, other){
|
merge: function(diff, other){
|
||||||
// XXX
|
// XXX
|
||||||
return this.flatten(diff).concat(this.flatten(other))
|
return this.flatten(diff).concat(this.flatten(other)) },
|
||||||
},
|
|
||||||
|
|
||||||
// Build a diff between A and B...
|
// Build a diff between A and B...
|
||||||
//
|
//
|
||||||
@ -1452,12 +1417,12 @@ module.Types = {
|
|||||||
: {})
|
: {})
|
||||||
cache = context.cache = context.cache || new Map()
|
cache = context.cache = context.cache || new Map()
|
||||||
// cached diff...
|
// cached diff...
|
||||||
var diff = cache.diff = cache.diff || function(a, b){
|
var diff = cache.diff = cache.diff
|
||||||
var l2 = cache.get(a) || new Map()
|
|| function(a, b){
|
||||||
var d = l2.get(b) || that.diff(a, b, options, context)
|
var l2 = cache.get(a) || new Map()
|
||||||
cache.set(a, l2.set(b, d))
|
var d = l2.get(b) || that.diff(a, b, options, context)
|
||||||
return d
|
cache.set(a, l2.set(b, d))
|
||||||
}
|
return d }
|
||||||
|
|
||||||
|
|
||||||
// check: if same/matching object...
|
// check: if same/matching object...
|
||||||
@ -1469,13 +1434,11 @@ module.Types = {
|
|||||||
// check with everything but the root cached/diffed...
|
// check with everything but the root cached/diffed...
|
||||||
// XXX not sure if this is efficient...
|
// XXX not sure if this is efficient...
|
||||||
if(bcmp(A, B, cmp)){
|
if(bcmp(A, B, cmp)){
|
||||||
return null
|
return null }
|
||||||
}
|
|
||||||
|
|
||||||
// check: builtin types...
|
// check: builtin types...
|
||||||
if(this.DIFF_TYPES.has(A) || this.DIFF_TYPES.has(B)){
|
if(this.DIFF_TYPES.has(A) || this.DIFF_TYPES.has(B)){
|
||||||
return this.handle('Basic', {}, diff, A, B, options)
|
return this.handle('Basic', {}, diff, A, B, options) }
|
||||||
}
|
|
||||||
|
|
||||||
// find the matching type...
|
// find the matching type...
|
||||||
var type = this.detect(A, B, options)
|
var type = this.detect(A, B, options)
|
||||||
@ -1485,8 +1448,7 @@ module.Types = {
|
|||||||
if(!options.no_attributes
|
if(!options.no_attributes
|
||||||
&& !this.get(type).no_attributes){
|
&& !this.get(type).no_attributes){
|
||||||
// XXX need to strip array items from this...
|
// XXX need to strip array items from this...
|
||||||
this.handle(Object, res, diff, A, B, options)
|
this.handle(Object, res, diff, A, B, options) }
|
||||||
}
|
|
||||||
|
|
||||||
// cleanup -- remove items containing empty arrays...
|
// cleanup -- remove items containing empty arrays...
|
||||||
Object.keys(res)
|
Object.keys(res)
|
||||||
@ -1498,8 +1460,7 @@ module.Types = {
|
|||||||
// return only non-empty diff states...
|
// return only non-empty diff states...
|
||||||
return Object.keys(res).length == 1 ?
|
return Object.keys(res).length == 1 ?
|
||||||
null
|
null
|
||||||
: res
|
: res },
|
||||||
},
|
|
||||||
|
|
||||||
// XXX can we split out the diff walker and simply reuse it for:
|
// XXX can we split out the diff walker and simply reuse it for:
|
||||||
// .diff(..), .cmp(..), ...
|
// .diff(..), .cmp(..), ...
|
||||||
@ -1569,8 +1530,7 @@ module.Types = {
|
|||||||
var l2 = cache.get(a) || new Map()
|
var l2 = cache.get(a) || new Map()
|
||||||
var d = l2.get(b) || that._diff(a, b, options, context)
|
var d = l2.get(b) || that._diff(a, b, options, context)
|
||||||
cache.set(a, l2.set(b, d))
|
cache.set(a, l2.set(b, d))
|
||||||
return d
|
return d }
|
||||||
}
|
|
||||||
|
|
||||||
// XXX ???
|
// XXX ???
|
||||||
options.cmp = cmp
|
options.cmp = cmp
|
||||||
@ -1601,8 +1561,7 @@ module.Types = {
|
|||||||
// we have a match -> no changes, just cache...
|
// we have a match -> no changes, just cache...
|
||||||
if(cmp(A, B)){
|
if(cmp(A, B)){
|
||||||
cache.set(A, cache_l2.set(B, undefined))
|
cache.set(A, cache_l2.set(B, undefined))
|
||||||
return
|
return }
|
||||||
}
|
|
||||||
|
|
||||||
// handler...
|
// handler...
|
||||||
var handler = that.get(
|
var handler = that.get(
|
||||||
@ -1620,8 +1579,7 @@ module.Types = {
|
|||||||
: false
|
: false
|
||||||
// unhandled type...
|
// unhandled type...
|
||||||
if(!handler){
|
if(!handler){
|
||||||
throw new TypeError('Diff: can\'t handle: ' + type)
|
throw new TypeError('Diff: can\'t handle: ' + type) }
|
||||||
}
|
|
||||||
|
|
||||||
// call the handler...
|
// call the handler...
|
||||||
var res = handler.call(that, A, B, next, options)
|
var res = handler.call(that, A, B, next, options)
|
||||||
@ -1637,15 +1595,12 @@ module.Types = {
|
|||||||
return res == null ?
|
return res == null ?
|
||||||
res
|
res
|
||||||
: diff.concat(res
|
: diff.concat(res
|
||||||
.map(updatePath(path)))
|
.map(updatePath(path))) } },
|
||||||
}
|
|
||||||
},
|
|
||||||
// diff...
|
// diff...
|
||||||
[],
|
[],
|
||||||
// node format:
|
// node format:
|
||||||
// [ <path>, <A>, <B> ]
|
// [ <path>, <A>, <B> ]
|
||||||
[[], A, B])
|
[[], A, B]) },
|
||||||
},
|
|
||||||
|
|
||||||
// Deep-compare A and B...
|
// Deep-compare A and B...
|
||||||
//
|
//
|
||||||
@ -1680,8 +1635,7 @@ module.Types = {
|
|||||||
.walk(diff.diff, function(change){
|
.walk(diff.diff, function(change){
|
||||||
// replace the object itself...
|
// replace the object itself...
|
||||||
if(change.path.length == 0){
|
if(change.path.length == 0){
|
||||||
return change.B
|
return change.B }
|
||||||
}
|
|
||||||
|
|
||||||
var parent
|
var parent
|
||||||
var parent_key
|
var parent_key
|
||||||
@ -1704,13 +1658,10 @@ module.Types = {
|
|||||||
parent[parent_key] = res
|
parent[parent_key] = res
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
obj = res
|
obj = res }
|
||||||
}
|
|
||||||
|
|
||||||
return obj
|
return obj })
|
||||||
})
|
.pop()) },
|
||||||
.pop())
|
|
||||||
},
|
|
||||||
// Call the post-patch method of the handlers...
|
// Call the post-patch method of the handlers...
|
||||||
//
|
//
|
||||||
postPatch: function(res){
|
postPatch: function(res){
|
||||||
@ -1755,8 +1706,7 @@ module.Types = {
|
|||||||
|
|
||||||
// check root...
|
// check root...
|
||||||
if(key == null){
|
if(key == null){
|
||||||
return !that.cmp(change.A, target)
|
return !that.cmp(change.A, target) }
|
||||||
}
|
|
||||||
|
|
||||||
// keep only the mismatching changes...
|
// keep only the mismatching changes...
|
||||||
return change.type && that.get(change.type).check ?
|
return change.type && that.get(change.type).check ?
|
||||||
@ -1771,9 +1721,7 @@ module.Types = {
|
|||||||
!that.cmp(change.A,
|
!that.cmp(change.A,
|
||||||
target.slice(key[0][0], key[0][0] + target.length[0]))
|
target.slice(key[0][0], key[0][0] + target.length[0]))
|
||||||
: !that.cmp(change.A, target[key[0]]))
|
: !that.cmp(change.A, target[key[0]]))
|
||||||
: !that.cmp(change.A, target[key])
|
: !that.cmp(change.A, target[key]) }) },
|
||||||
})
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1950,8 +1898,7 @@ Types.set('Basic', {
|
|||||||
;(!options.keep_none && B === NONE)
|
;(!options.keep_none && B === NONE)
|
||||||
|| (obj.B = B)
|
|| (obj.B = B)
|
||||||
|
|
||||||
return [obj]
|
return [obj] },
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
@ -1991,16 +1938,13 @@ Types.set(Object, {
|
|||||||
delete obj[key]
|
delete obj[key]
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
obj[key] = change.B
|
obj[key] = change.B }
|
||||||
}
|
|
||||||
|
|
||||||
// array item...
|
// array item...
|
||||||
// XXX should this make this decision???
|
// XXX should this make this decision???
|
||||||
} else {
|
} else {
|
||||||
this.typeCall(Array, 'patch', obj, key, change, ...rest)
|
this.typeCall(Array, 'patch', obj, key, change, ...rest) }
|
||||||
}
|
return obj },
|
||||||
return obj
|
|
||||||
},
|
|
||||||
|
|
||||||
// XXX EXPERIMENTAL...
|
// XXX EXPERIMENTAL...
|
||||||
get: function(obj, key){
|
get: function(obj, key){
|
||||||
@ -2044,8 +1988,7 @@ Types.set(Object, {
|
|||||||
options)]
|
options)]
|
||||||
// remove seen keys...
|
// remove seen keys...
|
||||||
delete B_index[ka]
|
delete B_index[ka]
|
||||||
return res
|
return res })
|
||||||
})
|
|
||||||
// keys present only in B...
|
// keys present only in B...
|
||||||
.concat(Object.keys(B_index)
|
.concat(Object.keys(B_index)
|
||||||
.map(function(kb){
|
.map(function(kb){
|
||||||
@ -2057,8 +2000,7 @@ Types.set(Object, {
|
|||||||
// cleanup...
|
// cleanup...
|
||||||
.filter(function(e){
|
.filter(function(e){
|
||||||
return e[1] !== null })
|
return e[1] !== null })
|
||||||
return items
|
return items },
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
// XXX EXPERIMENTAL: used by Types._diff(..)
|
// XXX EXPERIMENTAL: used by Types._diff(..)
|
||||||
@ -2074,8 +2016,7 @@ Types.set(Object, {
|
|||||||
return e != null })
|
return e != null })
|
||||||
// XXX add attribute order support...
|
// XXX add attribute order support...
|
||||||
// XXX
|
// XXX
|
||||||
return diff
|
return diff },
|
||||||
},
|
|
||||||
// return aligned attr sets...
|
// return aligned attr sets...
|
||||||
// format:
|
// format:
|
||||||
// [
|
// [
|
||||||
@ -2111,8 +2052,7 @@ Types.set(Object, {
|
|||||||
]
|
]
|
||||||
// remove seen keys...
|
// remove seen keys...
|
||||||
delete B_index[ka]
|
delete B_index[ka]
|
||||||
return res
|
return res })
|
||||||
})
|
|
||||||
// keys present only in B...
|
// keys present only in B...
|
||||||
.concat(Object.keys(B_index)
|
.concat(Object.keys(B_index)
|
||||||
.map(function(kb){
|
.map(function(kb){
|
||||||
@ -2121,8 +2061,7 @@ Types.set(Object, {
|
|||||||
EMPTY,
|
EMPTY,
|
||||||
B[kb],
|
B[kb],
|
||||||
]}))
|
]}))
|
||||||
return items
|
return items },
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
@ -2143,8 +2082,7 @@ Types.set(Array, {
|
|||||||
.filter(function(e){
|
.filter(function(e){
|
||||||
return e.length == 2 ?
|
return e.length == 2 ?
|
||||||
attrs.push(e) && false
|
attrs.push(e) && false
|
||||||
: true
|
: true })
|
||||||
})
|
|
||||||
.map(function(e){
|
.map(function(e){
|
||||||
var v = e[2]
|
var v = e[2]
|
||||||
|
|
||||||
@ -2154,8 +2092,7 @@ Types.set(Array, {
|
|||||||
: [e[0], e[1]]
|
: [e[0], e[1]]
|
||||||
var p = path.concat([i])
|
var p = path.concat([i])
|
||||||
|
|
||||||
return that.walk(v, func, p)
|
return that.walk(v, func, p) })
|
||||||
})
|
|
||||||
// length...
|
// length...
|
||||||
// NOTE: we keep this last as the length should be the last
|
// NOTE: we keep this last as the length should be the last
|
||||||
// thing to get patched...
|
// thing to get patched...
|
||||||
@ -2167,8 +2104,7 @@ Types.set(Array, {
|
|||||||
})
|
})
|
||||||
: [])
|
: [])
|
||||||
// attributes...
|
// attributes...
|
||||||
.concat(this.typeCall(Object, 'walk', {items: attrs}, func, path))
|
.concat(this.typeCall(Object, 'walk', {items: attrs}, func, path)) },
|
||||||
},
|
|
||||||
// XXX add object compatibility checks...
|
// XXX add object compatibility checks...
|
||||||
// XXX revise...
|
// XXX revise...
|
||||||
patch: function(obj, key, change){
|
patch: function(obj, key, change){
|
||||||
@ -2203,9 +2139,7 @@ Types.set(Array, {
|
|||||||
// XXX test...
|
// XXX test...
|
||||||
if(!('B' in change)){
|
if(!('B' in change)){
|
||||||
for(var n=j; n <= lj + j - li; n++){
|
for(var n=j; n <= lj + j - li; n++){
|
||||||
delete obj[n]
|
delete obj[n] } }
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// item manipulation...
|
// item manipulation...
|
||||||
} else {
|
} else {
|
||||||
@ -2242,23 +2176,17 @@ Types.set(Array, {
|
|||||||
delete obj[j]
|
delete obj[j]
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
obj[j] = change.B
|
obj[j] = change.B }
|
||||||
}
|
|
||||||
|
|
||||||
// XXX this is essentially the same as the above case, do we need both??
|
// XXX this is essentially the same as the above case, do we need both??
|
||||||
} else {
|
} else {
|
||||||
obj[j] = change.B
|
obj[j] = change.B } }
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj
|
return obj },
|
||||||
},
|
|
||||||
reverse: function(change){
|
reverse: function(change){
|
||||||
if('length' in change){
|
if('length' in change){
|
||||||
change.length = change.length.slice().reverse()
|
change.length = change.length.slice().reverse() }
|
||||||
}
|
return change },
|
||||||
return change
|
|
||||||
},
|
|
||||||
|
|
||||||
// XXX EXPERIMENTAL...
|
// XXX EXPERIMENTAL...
|
||||||
get: function(obj, key){
|
get: function(obj, key){
|
||||||
@ -2280,10 +2208,8 @@ Types.set(Array, {
|
|||||||
|
|
||||||
// item...
|
// item...
|
||||||
} else {
|
} else {
|
||||||
obj[key] = value
|
obj[key] = value }
|
||||||
}
|
return this },
|
||||||
return this
|
|
||||||
},
|
|
||||||
|
|
||||||
// part handlers...
|
// part handlers...
|
||||||
items: function(diff, A, B, options){
|
items: function(diff, A, B, options){
|
||||||
@ -2374,8 +2300,7 @@ Types.set(Array, {
|
|||||||
: [])
|
: [])
|
||||||
})
|
})
|
||||||
.reduce(function(res, e){
|
.reduce(function(res, e){
|
||||||
return res.concat(e) }, [])
|
return res.concat(e) }, []) },
|
||||||
},
|
|
||||||
// XXX
|
// XXX
|
||||||
order: function(diff, A, B, options){
|
order: function(diff, A, B, options){
|
||||||
// XXX
|
// XXX
|
||||||
@ -2485,11 +2410,9 @@ Types.set(Array, {
|
|||||||
path: ['length'],
|
path: ['length'],
|
||||||
A: A.length,
|
A: A.length,
|
||||||
B: B.length,
|
B: B.length,
|
||||||
})
|
}) }
|
||||||
}
|
|
||||||
|
|
||||||
return diff
|
return diff },
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||||
@ -2580,8 +2503,7 @@ Types.set('Text', {
|
|||||||
|
|
||||||
var res = cache[path] = this.typeCall(Array, 'patch', lines, key, change)
|
var res = cache[path] = this.typeCall(Array, 'patch', lines, key, change)
|
||||||
|
|
||||||
return res
|
return res },
|
||||||
},
|
|
||||||
|
|
||||||
// XXX EXPERIMENTAL...
|
// XXX EXPERIMENTAL...
|
||||||
get: function(obj, key){
|
get: function(obj, key){
|
||||||
@ -2605,12 +2527,9 @@ Types.set('Text', {
|
|||||||
} else {
|
} else {
|
||||||
path.slice(0, -1)
|
path.slice(0, -1)
|
||||||
.reduce(function(res, k){
|
.reduce(function(res, k){
|
||||||
return res[k] }, res)[path.pop()] = text
|
return res[k] }, res)[path.pop()] = text } })
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return res
|
return res },
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@ -2723,16 +2642,14 @@ var DiffClassPrototype = {
|
|||||||
clone: function(name){
|
clone: function(name){
|
||||||
var cls = Object.create(this.__proto__)
|
var cls = Object.create(this.__proto__)
|
||||||
cls.types = this.types.clone()
|
cls.types = this.types.clone()
|
||||||
return object.Constructor(name || 'EDiff', cls, this())
|
return object.Constructor(name || 'EDiff', cls, this()) },
|
||||||
},
|
|
||||||
|
|
||||||
// proxy generic stuff to .types...
|
// proxy generic stuff to .types...
|
||||||
cmp: proxy('types.cmp'),
|
cmp: proxy('types.cmp'),
|
||||||
vars: function(pattern, obj){
|
vars: function(pattern, obj){
|
||||||
var o = {}
|
var o = {}
|
||||||
this.cmp(pattern, obj, null, o)
|
this.cmp(pattern, obj, null, o)
|
||||||
return o.ns || {}
|
return o.ns || {} },
|
||||||
},
|
|
||||||
|
|
||||||
// XXX do format/version conversion...
|
// XXX do format/version conversion...
|
||||||
fromJSON: function(json){
|
fromJSON: function(json){
|
||||||
@ -2809,8 +2726,7 @@ var DiffPrototype = {
|
|||||||
diff.diff(A, B, options)
|
diff.diff(A, B, options)
|
||||||
: diff.flatten(diff.diff(A, B, options), options)
|
: diff.flatten(diff.diff(A, B, options), options)
|
||||||
|
|
||||||
this.timestamp = Date.now()
|
this.timestamp = Date.now() },
|
||||||
},
|
|
||||||
|
|
||||||
// XXX should this be a deep copy???
|
// XXX should this be a deep copy???
|
||||||
clone: function(){
|
clone: function(){
|
||||||
@ -2825,8 +2741,7 @@ var DiffPrototype = {
|
|||||||
: this.diff ?
|
: this.diff ?
|
||||||
Object.assign({}, this.diff)
|
Object.assign({}, this.diff)
|
||||||
: null
|
: null
|
||||||
return res
|
return res },
|
||||||
},
|
|
||||||
|
|
||||||
check: function(obj){
|
check: function(obj){
|
||||||
return Object.assign(
|
return Object.assign(
|
||||||
@ -2848,22 +2763,19 @@ var DiffPrototype = {
|
|||||||
var res = this.clone()
|
var res = this.clone()
|
||||||
res.diff = Object.create(this.constructor.types).reverse(this.diff)
|
res.diff = Object.create(this.constructor.types).reverse(this.diff)
|
||||||
res.parent = this
|
res.parent = this
|
||||||
return res
|
return res },
|
||||||
},
|
|
||||||
filter: function(filter){
|
filter: function(filter){
|
||||||
var res = this.clone()
|
var res = this.clone()
|
||||||
res.diff = this.constructor.types.filter.call(this, this.diff, filter)
|
res.diff = this.constructor.types.filter.call(this, this.diff, filter)
|
||||||
res.parent = this
|
res.parent = this
|
||||||
return res
|
return res },
|
||||||
},
|
|
||||||
|
|
||||||
// XXX should this set .parent ????
|
// XXX should this set .parent ????
|
||||||
merge: function(diff){
|
merge: function(diff){
|
||||||
var res = this.clone()
|
var res = this.clone()
|
||||||
res.diff = this.constructor.types.merge.call(this, this.diff, diff.diff)
|
res.diff = this.constructor.types.merge.call(this, this.diff, diff.diff)
|
||||||
res.parent = this
|
res.parent = this
|
||||||
return res
|
return res },
|
||||||
},
|
|
||||||
|
|
||||||
// XXX EXPERIMENTAL...
|
// XXX EXPERIMENTAL...
|
||||||
end: function(){
|
end: function(){
|
||||||
@ -2880,8 +2792,7 @@ var DiffPrototype = {
|
|||||||
// XXX these need to be prepared for JSON compatibility...
|
// XXX these need to be prepared for JSON compatibility...
|
||||||
options: this.options,
|
options: this.options,
|
||||||
diff: this.diff,
|
diff: this.diff,
|
||||||
}
|
} },
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var Diff =
|
var Diff =
|
||||||
|
|||||||
@ -25,6 +25,9 @@
|
|||||||
"homepage": "https://github.com/flynx/object-diff.js#readme",
|
"homepage": "https://github.com/flynx/object-diff.js#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"generic-walk": "^1.4.0",
|
"generic-walk": "^1.4.0",
|
||||||
"ig-object": "^2.0.0"
|
"ig-object": "^2.2.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"ig-test": "*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
88
test.js
Normal file
88
test.js
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
/**********************************************************************
|
||||||
|
*
|
||||||
|
* test.js
|
||||||
|
*
|
||||||
|
* Repo and docs:
|
||||||
|
* https://github.com/flynx/test.js
|
||||||
|
*
|
||||||
|
***********************************************/ /* c8 ignore next 2 */
|
||||||
|
((typeof define)[0]=='u'?function(f){module.exports=f(require)}:define)
|
||||||
|
(function(require){ var module={} // make module AMD/node compatible...
|
||||||
|
/*********************************************************************/
|
||||||
|
|
||||||
|
var test = require('ig-test')
|
||||||
|
|
||||||
|
var diff = require('./diff')
|
||||||
|
var format = require('./format')
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
|
||||||
|
test.Setups({
|
||||||
|
// XXX make this return a list...
|
||||||
|
basic: function(assert){
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
A: {}, B: {},
|
||||||
|
cmp: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
A: [], B: [],
|
||||||
|
cmp: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
A: 0, B: 0,
|
||||||
|
cmp: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
A: 123, B: 123,
|
||||||
|
cmp: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
A: false, B: false,
|
||||||
|
cmp: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
A: undefined, B: undefined,
|
||||||
|
cmp: true,
|
||||||
|
},
|
||||||
|
// XXX special case -- fails....
|
||||||
|
{
|
||||||
|
A: NaN, B: NaN,
|
||||||
|
cmp: true,
|
||||||
|
},
|
||||||
|
] },
|
||||||
|
})
|
||||||
|
|
||||||
|
test.Tests({
|
||||||
|
cmp: function(assert, setup){
|
||||||
|
setup = setup instanceof Array ? setup : [setup]
|
||||||
|
setup.forEach(function(e){
|
||||||
|
var res
|
||||||
|
'cmp' in e
|
||||||
|
&& assert(
|
||||||
|
(res = diff.cmp(e.A, e.B)) == e.cmp,
|
||||||
|
`cmp(..): cmp(${e.A}, ${e.B}) should be ${e.cmp} got ${res}`) }) },
|
||||||
|
})
|
||||||
|
|
||||||
|
test.Cases({
|
||||||
|
'basics': function(assert){
|
||||||
|
// XXX move reference objects + expected diffs to setups
|
||||||
|
a = {}
|
||||||
|
b = {}
|
||||||
|
|
||||||
|
assert(diff.Diff(a, b), 'Diff(..)')
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
//---------------------------------------------------------------------
|
||||||
|
typeof(__filename) != 'undefined'
|
||||||
|
&& __filename == (require.main || {}).filename
|
||||||
|
&& test.run()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**********************************************************************
|
||||||
|
* vim:set ts=4 sw=4 : */ return module })
|
||||||
Loading…
x
Reference in New Issue
Block a user