browser/devtools/shared/observable-object.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* this source code form is subject to the terms of the mozilla public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 /**
michael@0 6 * ObservableObject
michael@0 7 *
michael@0 8 * An observable object is a JSON-like object that throws
michael@0 9 * events when its direct properties or properties of any
michael@0 10 * contained objects, are getting accessed or set.
michael@0 11 *
michael@0 12 * Inherits from EventEmitter.
michael@0 13 *
michael@0 14 * Properties:
michael@0 15 * ⬩ object: JSON-like object
michael@0 16 *
michael@0 17 * Events:
michael@0 18 * ⬩ "get" / path (array of property names)
michael@0 19 * ⬩ "set" / path / new value
michael@0 20 *
michael@0 21 * Example:
michael@0 22 *
michael@0 23 * let emitter = new ObservableObject({ x: { y: [10] } });
michael@0 24 * emitter.on("set", console.log);
michael@0 25 * emitter.on("get", console.log);
michael@0 26 * let obj = emitter.object;
michael@0 27 * obj.x.y[0] = 50;
michael@0 28 *
michael@0 29 */
michael@0 30
michael@0 31 "use strict";
michael@0 32
michael@0 33 const EventEmitter = require("devtools/toolkit/event-emitter");
michael@0 34
michael@0 35 function ObservableObject(object = {}) {
michael@0 36 EventEmitter.decorate(this);
michael@0 37 let handler = new Handler(this);
michael@0 38 this.object = new Proxy(object, handler);
michael@0 39 handler._wrappers.set(this.object, object);
michael@0 40 handler._paths.set(object, []);
michael@0 41 }
michael@0 42
michael@0 43 module.exports = ObservableObject;
michael@0 44
michael@0 45 function isObject(x) {
michael@0 46 if (typeof x === "object")
michael@0 47 return x !== null;
michael@0 48 return typeof x === "function";
michael@0 49 }
michael@0 50
michael@0 51 function Handler(emitter) {
michael@0 52 this._emitter = emitter;
michael@0 53 this._wrappers = new WeakMap();
michael@0 54 this._values = new WeakMap();
michael@0 55 this._paths = new WeakMap();
michael@0 56 }
michael@0 57
michael@0 58 Handler.prototype = {
michael@0 59 wrap: function(target, key, value) {
michael@0 60 let path;
michael@0 61 if (!isObject(value)) {
michael@0 62 path = this._paths.get(target).concat(key);
michael@0 63 } else if (this._wrappers.has(value)) {
michael@0 64 path = this._paths.get(value);
michael@0 65 } else if (this._paths.has(value)) {
michael@0 66 path = this._paths.get(value);
michael@0 67 value = this._values.get(value);
michael@0 68 } else {
michael@0 69 path = this._paths.get(target).concat(key);
michael@0 70 this._paths.set(value, path);
michael@0 71 let wrapper = new Proxy(value, this);
michael@0 72 this._wrappers.set(wrapper, value);
michael@0 73 this._values.set(value, wrapper);
michael@0 74 value = wrapper;
michael@0 75 }
michael@0 76 return [value, path];
michael@0 77 },
michael@0 78 unwrap: function(target, key, value) {
michael@0 79 if (!isObject(value) || !this._wrappers.has(value)) {
michael@0 80 return [value, this._paths.get(target).concat(key)];
michael@0 81 }
michael@0 82 return [this._wrappers.get(value), this._paths.get(target).concat(key)];
michael@0 83 },
michael@0 84 get: function(target, key) {
michael@0 85 let value = target[key];
michael@0 86 let [wrapped, path] = this.wrap(target, key, value);
michael@0 87 this._emitter.emit("get", path, value);
michael@0 88 return wrapped;
michael@0 89 },
michael@0 90 set: function(target, key, value) {
michael@0 91 let [wrapped, path] = this.unwrap(target, key, value);
michael@0 92 target[key] = value;
michael@0 93 this._emitter.emit("set", path, value);
michael@0 94 },
michael@0 95 getOwnPropertyDescriptor: function(target, key) {
michael@0 96 let desc = Object.getOwnPropertyDescriptor(target, key);
michael@0 97 if (desc) {
michael@0 98 if ("value" in desc) {
michael@0 99 let [wrapped, path] = this.wrap(target, key, desc.value);
michael@0 100 desc.value = wrapped;
michael@0 101 this._emitter.emit("get", path, desc.value);
michael@0 102 } else {
michael@0 103 if ("get" in desc) {
michael@0 104 [desc.get] = this.wrap(target, "get "+key, desc.get);
michael@0 105 }
michael@0 106 if ("set" in desc) {
michael@0 107 [desc.set] = this.wrap(target, "set "+key, desc.set);
michael@0 108 }
michael@0 109 }
michael@0 110 }
michael@0 111 return desc;
michael@0 112 },
michael@0 113 defineProperty: function(target, key, desc) {
michael@0 114 if ("value" in desc) {
michael@0 115 let [unwrapped, path] = this.unwrap(target, key, desc.value);
michael@0 116 desc.value = unwrapped;
michael@0 117 Object.defineProperty(target, key, desc);
michael@0 118 this._emitter.emit("set", path, desc.value);
michael@0 119 } else {
michael@0 120 if ("get" in desc) {
michael@0 121 [desc.get] = this.unwrap(target, "get "+key, desc.get);
michael@0 122 }
michael@0 123 if ("set" in desc) {
michael@0 124 [desc.set] = this.unwrap(target, "set "+key, desc.set);
michael@0 125 }
michael@0 126 Object.defineProperty(target, key, desc);
michael@0 127 }
michael@0 128 }
michael@0 129 };

mercurial