4242var REMOVE = 'remove' ;
4343var REPLACE = 'replace' ;
4444var ADD = 'add' ;
45+ var MOVE = 'move' ;
4546
4647function diffApply ( obj , diff , pathConverter ) {
4748 if ( ! obj || typeof obj != 'object' ) {
@@ -57,23 +58,40 @@ function diffApply(obj, diff, pathConverter) {
5758 var thisDiff = diff [ i ] ;
5859 var subObject = obj ;
5960 var thisOp = thisDiff . op ;
60- var thisPath = thisDiff . path ;
61- if ( pathConverter ) {
62- thisPath = pathConverter ( thisPath ) ;
63- if ( ! Array . isArray ( thisPath ) ) {
64- throw new Error ( 'pathConverter must return an array' ) ;
61+
62+ var thisPath = transformPath ( pathConverter , thisDiff . path ) ;
63+ var thisFromPath = thisDiff . from && transformPath ( pathConverter , thisDiff . from ) ;
64+ var toPath , toPathCopy , lastToProp , subToObject , valueToMove ;
65+
66+ if ( thisFromPath ) {
67+ // MOVE only, "fromPath" is effectively path and "path" is toPath
68+ toPath = thisPath ;
69+ thisPath = thisFromPath ;
70+
71+ toPathCopy = toPath . slice ( ) ;
72+ lastToProp = toPathCopy . pop ( ) ;
73+ prototypeCheck ( lastToProp ) ;
74+ if ( lastToProp == null ) {
75+ return false ;
6576 }
66- } else {
67- if ( ! Array . isArray ( thisPath ) ) {
68- throw new Error ( 'diff path must be an array, consider supplying a path converter' ) ;
77+
78+ var thisToProp ;
79+ while ( ( ( thisToProp = toPathCopy . shift ( ) ) ) != null ) {
80+ prototypeCheck ( thisToProp ) ;
81+ if ( ! ( thisToProp in subToObject ) ) {
82+ subToObject [ thisToProp ] = { } ;
83+ }
84+ subToObject = subToObject [ thisToProp ] ;
6985 }
7086 }
87+
7188 var pathCopy = thisPath . slice ( ) ;
7289 var lastProp = pathCopy . pop ( ) ;
7390 prototypeCheck ( lastProp ) ;
7491 if ( lastProp == null ) {
7592 return false ;
7693 }
94+
7795 var thisProp ;
7896 while ( ( ( thisProp = pathCopy . shift ( ) ) ) != null ) {
7997 prototypeCheck ( thisProp ) ;
@@ -82,21 +100,50 @@ function diffApply(obj, diff, pathConverter) {
82100 }
83101 subObject = subObject [ thisProp ] ;
84102 }
85- if ( thisOp === REMOVE || thisOp === REPLACE ) {
103+ if ( thisOp === REMOVE || thisOp === REPLACE || thisOp === MOVE ) {
104+ var path = thisOp === MOVE ? thisDiff . from : thisDiff . path ;
86105 if ( ! subObject . hasOwnProperty ( lastProp ) ) {
87- throw new Error ( [ 'expected to find property' , thisDiff . path , 'in object' , obj ] . join ( ' ' ) ) ;
106+ throw new Error ( [ 'expected to find property' , path , 'in object' , obj ] . join ( ' ' ) ) ;
88107 }
89108 }
90- if ( thisOp === REMOVE ) {
109+ if ( thisOp === REMOVE || thisOp === MOVE ) {
110+ if ( thisOp === MOVE ) {
111+ valueToMove = subObject [ lastProp ] ;
112+ }
91113 Array . isArray ( subObject ) ? subObject . splice ( lastProp , 1 ) : delete subObject [ lastProp ] ;
92114 }
93115 if ( thisOp === REPLACE || thisOp === ADD ) {
94116 subObject [ lastProp ] = thisDiff . value ;
95117 }
118+
119+ if ( thisOp === MOVE ) {
120+ subObject [ lastToProp ] = valueToMove ;
121+ }
96122 }
97123 return subObject ;
98124}
99125
126+ function transformPath ( pathConverter , thisPath ) {
127+ if ( pathConverter ) {
128+ thisPath = pathConverter ( thisPath ) ;
129+ if ( ! Array . isArray ( thisPath ) ) {
130+ throw new Error ( [
131+ 'pathConverter must return an array, returned:' ,
132+ thisPath ,
133+ ] . join ( ' ' ) ) ;
134+ }
135+ } else {
136+ if ( ! Array . isArray ( thisPath ) ) {
137+ throw new Error ( [
138+ 'diff path' ,
139+ thisPath ,
140+ 'must be an array, consider supplying a path converter' ]
141+ . join ( ' ' ) ) ;
142+ }
143+ }
144+ return thisPath ;
145+ }
146+
100147function jsonPatchPathConverter ( stringPath ) {
101148 return stringPath . split ( '/' ) . slice ( 1 ) ;
102149}
0 commit comments