You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							314 lines
						
					
					
						
							8.7 KiB
						
					
					
				
			
		
		
	
	
							314 lines
						
					
					
						
							8.7 KiB
						
					
					
				var traverse = module.exports = function (obj) { | 
						|
    return new Traverse(obj); | 
						|
}; | 
						|
 | 
						|
function Traverse (obj) { | 
						|
    this.value = obj; | 
						|
} | 
						|
 | 
						|
Traverse.prototype.get = function (ps) { | 
						|
    var node = this.value; | 
						|
    for (var i = 0; i < ps.length; i ++) { | 
						|
        var key = ps[i]; | 
						|
        if (!node || !hasOwnProperty.call(node, key)) { | 
						|
            node = undefined; | 
						|
            break; | 
						|
        } | 
						|
        node = node[key]; | 
						|
    } | 
						|
    return node; | 
						|
}; | 
						|
 | 
						|
Traverse.prototype.has = function (ps) { | 
						|
    var node = this.value; | 
						|
    for (var i = 0; i < ps.length; i ++) { | 
						|
        var key = ps[i]; | 
						|
        if (!node || !hasOwnProperty.call(node, key)) { | 
						|
            return false; | 
						|
        } | 
						|
        node = node[key]; | 
						|
    } | 
						|
    return true; | 
						|
}; | 
						|
 | 
						|
Traverse.prototype.set = function (ps, value) { | 
						|
    var node = this.value; | 
						|
    for (var i = 0; i < ps.length - 1; i ++) { | 
						|
        var key = ps[i]; | 
						|
        if (!hasOwnProperty.call(node, key)) node[key] = {}; | 
						|
        node = node[key]; | 
						|
    } | 
						|
    node[ps[i]] = value; | 
						|
    return value; | 
						|
}; | 
						|
 | 
						|
Traverse.prototype.map = function (cb) { | 
						|
    return walk(this.value, cb, true); | 
						|
}; | 
						|
 | 
						|
Traverse.prototype.forEach = function (cb) { | 
						|
    this.value = walk(this.value, cb, false); | 
						|
    return this.value; | 
						|
}; | 
						|
 | 
						|
Traverse.prototype.reduce = function (cb, init) { | 
						|
    var skip = arguments.length === 1; | 
						|
    var acc = skip ? this.value : init; | 
						|
    this.forEach(function (x) { | 
						|
        if (!this.isRoot || !skip) { | 
						|
            acc = cb.call(this, acc, x); | 
						|
        } | 
						|
    }); | 
						|
    return acc; | 
						|
}; | 
						|
 | 
						|
Traverse.prototype.paths = function () { | 
						|
    var acc = []; | 
						|
    this.forEach(function (x) { | 
						|
        acc.push(this.path);  | 
						|
    }); | 
						|
    return acc; | 
						|
}; | 
						|
 | 
						|
Traverse.prototype.nodes = function () { | 
						|
    var acc = []; | 
						|
    this.forEach(function (x) { | 
						|
        acc.push(this.node); | 
						|
    }); | 
						|
    return acc; | 
						|
}; | 
						|
 | 
						|
Traverse.prototype.clone = function () { | 
						|
    var parents = [], nodes = []; | 
						|
     | 
						|
    return (function clone (src) { | 
						|
        for (var i = 0; i < parents.length; i++) { | 
						|
            if (parents[i] === src) { | 
						|
                return nodes[i]; | 
						|
            } | 
						|
        } | 
						|
         | 
						|
        if (typeof src === 'object' && src !== null) { | 
						|
            var dst = copy(src); | 
						|
             | 
						|
            parents.push(src); | 
						|
            nodes.push(dst); | 
						|
             | 
						|
            forEach(objectKeys(src), function (key) { | 
						|
                dst[key] = clone(src[key]); | 
						|
            }); | 
						|
             | 
						|
            parents.pop(); | 
						|
            nodes.pop(); | 
						|
            return dst; | 
						|
        } | 
						|
        else { | 
						|
            return src; | 
						|
        } | 
						|
    })(this.value); | 
						|
}; | 
						|
 | 
						|
function walk (root, cb, immutable) { | 
						|
    var path = []; | 
						|
    var parents = []; | 
						|
    var alive = true; | 
						|
     | 
						|
    return (function walker (node_) { | 
						|
        var node = immutable ? copy(node_) : node_; | 
						|
        var modifiers = {}; | 
						|
         | 
						|
        var keepGoing = true; | 
						|
         | 
						|
        var state = { | 
						|
            node : node, | 
						|
            node_ : node_, | 
						|
            path : [].concat(path), | 
						|
            parent : parents[parents.length - 1], | 
						|
            parents : parents, | 
						|
            key : path.slice(-1)[0], | 
						|
            isRoot : path.length === 0, | 
						|
            level : path.length, | 
						|
            circular : null, | 
						|
            update : function (x, stopHere) { | 
						|
                if (!state.isRoot) { | 
						|
                    state.parent.node[state.key] = x; | 
						|
                } | 
						|
                state.node = x; | 
						|
                if (stopHere) keepGoing = false; | 
						|
            }, | 
						|
            'delete' : function (stopHere) { | 
						|
                delete state.parent.node[state.key]; | 
						|
                if (stopHere) keepGoing = false; | 
						|
            }, | 
						|
            remove : function (stopHere) { | 
						|
                if (isArray(state.parent.node)) { | 
						|
                    state.parent.node.splice(state.key, 1); | 
						|
                } | 
						|
                else { | 
						|
                    delete state.parent.node[state.key]; | 
						|
                } | 
						|
                if (stopHere) keepGoing = false; | 
						|
            }, | 
						|
            keys : null, | 
						|
            before : function (f) { modifiers.before = f }, | 
						|
            after : function (f) { modifiers.after = f }, | 
						|
            pre : function (f) { modifiers.pre = f }, | 
						|
            post : function (f) { modifiers.post = f }, | 
						|
            stop : function () { alive = false }, | 
						|
            block : function () { keepGoing = false } | 
						|
        }; | 
						|
         | 
						|
        if (!alive) return state; | 
						|
         | 
						|
        function updateState() { | 
						|
            if (typeof state.node === 'object' && state.node !== null) { | 
						|
                if (!state.keys || state.node_ !== state.node) { | 
						|
                    state.keys = objectKeys(state.node) | 
						|
                } | 
						|
                 | 
						|
                state.isLeaf = state.keys.length == 0; | 
						|
                 | 
						|
                for (var i = 0; i < parents.length; i++) { | 
						|
                    if (parents[i].node_ === node_) { | 
						|
                        state.circular = parents[i]; | 
						|
                        break; | 
						|
                    } | 
						|
                } | 
						|
            } | 
						|
            else { | 
						|
                state.isLeaf = true; | 
						|
                state.keys = null; | 
						|
            } | 
						|
             | 
						|
            state.notLeaf = !state.isLeaf; | 
						|
            state.notRoot = !state.isRoot; | 
						|
        } | 
						|
         | 
						|
        updateState(); | 
						|
         | 
						|
        // use return values to update if defined | 
						|
        var ret = cb.call(state, state.node); | 
						|
        if (ret !== undefined && state.update) state.update(ret); | 
						|
         | 
						|
        if (modifiers.before) modifiers.before.call(state, state.node); | 
						|
         | 
						|
        if (!keepGoing) return state; | 
						|
         | 
						|
        if (typeof state.node == 'object' | 
						|
        && state.node !== null && !state.circular) { | 
						|
            parents.push(state); | 
						|
             | 
						|
            updateState(); | 
						|
             | 
						|
            forEach(state.keys, function (key, i) { | 
						|
                path.push(key); | 
						|
                 | 
						|
                if (modifiers.pre) modifiers.pre.call(state, state.node[key], key); | 
						|
                 | 
						|
                var child = walker(state.node[key]); | 
						|
                if (immutable && hasOwnProperty.call(state.node, key)) { | 
						|
                    state.node[key] = child.node; | 
						|
                } | 
						|
                 | 
						|
                child.isLast = i == state.keys.length - 1; | 
						|
                child.isFirst = i == 0; | 
						|
                 | 
						|
                if (modifiers.post) modifiers.post.call(state, child); | 
						|
                 | 
						|
                path.pop(); | 
						|
            }); | 
						|
            parents.pop(); | 
						|
        } | 
						|
         | 
						|
        if (modifiers.after) modifiers.after.call(state, state.node); | 
						|
         | 
						|
        return state; | 
						|
    })(root).node; | 
						|
} | 
						|
 | 
						|
function copy (src) { | 
						|
    if (typeof src === 'object' && src !== null) { | 
						|
        var dst; | 
						|
         | 
						|
        if (isArray(src)) { | 
						|
            dst = []; | 
						|
        } | 
						|
        else if (isDate(src)) { | 
						|
            dst = new Date(src.getTime ? src.getTime() : src); | 
						|
        } | 
						|
        else if (isRegExp(src)) { | 
						|
            dst = new RegExp(src); | 
						|
        } | 
						|
        else if (isError(src)) { | 
						|
            dst = { message: src.message }; | 
						|
        } | 
						|
        else if (isBoolean(src)) { | 
						|
            dst = new Boolean(src); | 
						|
        } | 
						|
        else if (isNumber(src)) { | 
						|
            dst = new Number(src); | 
						|
        } | 
						|
        else if (isString(src)) { | 
						|
            dst = new String(src); | 
						|
        } | 
						|
        else if (Object.create && Object.getPrototypeOf) { | 
						|
            dst = Object.create(Object.getPrototypeOf(src)); | 
						|
        } | 
						|
        else if (src.constructor === Object) { | 
						|
            dst = {}; | 
						|
        } | 
						|
        else { | 
						|
            var proto = | 
						|
                (src.constructor && src.constructor.prototype) | 
						|
                || src.__proto__ | 
						|
                || {} | 
						|
            ; | 
						|
            var T = function () {}; | 
						|
            T.prototype = proto; | 
						|
            dst = new T; | 
						|
        } | 
						|
         | 
						|
        forEach(objectKeys(src), function (key) { | 
						|
            dst[key] = src[key]; | 
						|
        }); | 
						|
        return dst; | 
						|
    } | 
						|
    else return src; | 
						|
} | 
						|
 | 
						|
var objectKeys = Object.keys || function keys (obj) { | 
						|
    var res = []; | 
						|
    for (var key in obj) res.push(key) | 
						|
    return res; | 
						|
}; | 
						|
 | 
						|
function toS (obj) { return Object.prototype.toString.call(obj) } | 
						|
function isDate (obj) { return toS(obj) === '[object Date]' } | 
						|
function isRegExp (obj) { return toS(obj) === '[object RegExp]' } | 
						|
function isError (obj) { return toS(obj) === '[object Error]' } | 
						|
function isBoolean (obj) { return toS(obj) === '[object Boolean]' } | 
						|
function isNumber (obj) { return toS(obj) === '[object Number]' } | 
						|
function isString (obj) { return toS(obj) === '[object String]' } | 
						|
 | 
						|
var isArray = Array.isArray || function isArray (xs) { | 
						|
    return Object.prototype.toString.call(xs) === '[object Array]'; | 
						|
}; | 
						|
 | 
						|
var forEach = function (xs, fn) { | 
						|
    if (xs.forEach) return xs.forEach(fn) | 
						|
    else for (var i = 0; i < xs.length; i++) { | 
						|
        fn(xs[i], i, xs); | 
						|
    } | 
						|
}; | 
						|
 | 
						|
forEach(objectKeys(Traverse.prototype), function (key) { | 
						|
    traverse[key] = function (obj) { | 
						|
        var args = [].slice.call(arguments, 1); | 
						|
        var t = new Traverse(obj); | 
						|
        return t[key].apply(t, args); | 
						|
    }; | 
						|
}); | 
						|
 | 
						|
var hasOwnProperty = Object.hasOwnProperty || function (obj, key) { | 
						|
    return key in obj; | 
						|
};
 | 
						|
 |