Skip to content

Commit

Permalink
Merge 2a17802 into 818f783
Browse files Browse the repository at this point in the history
  • Loading branch information
zgsrc authored Feb 27, 2018
2 parents 818f783 + 2a17802 commit f63aee9
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 10 deletions.
4 changes: 2 additions & 2 deletions dist/hyperactiv.map.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion react/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

67 changes: 61 additions & 6 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ const observersMap = new WeakMap()

/* Tools */

const isObj = function(o) { return o && typeof o === 'object' }
const isObj = function(o) { return o && typeof o === 'object' && !(o instanceof Date) }
const isArray = Array.isArray
const defineBubblingProperties = function(object, key, parent) {
const defineBubblingProperties = function(object, key, parent, deep) {
Object.defineProperty(object, '__key', { value: key, enumerable: false, configurable: true })
Object.defineProperty(object, '__parent', { value: parent, enumerable: false, configurable: true })
deep && Object.entries(object).forEach(function([key, val]) {
if(isObj(val) && (!val.__key || !val.__parent)) defineBubblingProperties(object[key], key, object)
})
}

const batcher = {
Expand Down Expand Up @@ -109,10 +112,12 @@ const observe = function(obj, options = {}) {

// If the new/old value are equal, return
if((!isArray(obj) || prop !== 'length') && obj[prop] === value) return true
// Remember old value for handler
const oldValue = obj[prop]
// If the deep flag is set we observe the newly set value
obj[prop] = deep && isObj(value) ? observe(value, options) : value
// If we defined a handler, we define the bubbling keys recursively on the new value
handler && deep && isObj(value) && defineBubblingProperties(obj[prop], prop, obj)
handler && deep && isObj(value) && defineBubblingProperties(obj[prop], prop, obj, deep)

if(handler) {
// Retrieve the mutated properties chain & call the handler
Expand All @@ -122,7 +127,7 @@ const observe = function(obj, options = {}) {
ancestry.unshift(parent.__key)
parent = parent.__parent
}
handler(ancestry, value, proxy)
handler(ancestry, value, oldValue, proxy)
}

// If the prop is watched
Expand Down Expand Up @@ -150,16 +155,66 @@ const observe = function(obj, options = {}) {
// Need this for binding es6 classes methods which are stored in the object prototype
const methods = [
...Object.getOwnPropertyNames(obj),
...Object.getOwnPropertyNames(Object.getPrototypeOf(obj))
...Object.getPrototypeOf(obj) && ['String', 'Number', 'Object', 'Array', 'Boolean', 'Date'].indexOf(Object.getPrototypeOf(obj).constructor.name) < 0 ? Object.getOwnPropertyNames(Object.getPrototypeOf(obj)) : []
].filter(prop => prop != 'constructor' && typeof obj[prop] === 'function')
methods.forEach(key => obj[key] = obj[key].bind(proxy))
}

return proxy
}

/* Observable */

const Observable = Base => class extends Base {
constructor(data, options) {
super()
const store = observe(data || { }, options || { deep: true, batch: true })
return new Proxy(this, {
set: (obj, name, value) => {
if(typeof value === 'function') {
this[name] = value
} else {
store[name] = value
if(this[name] === undefined) Object.defineProperty(this, name, { get: () => store[name], enumerable: true, configurable: true })
}
return true
},
deleteProperty: (obj, name) => {
delete store[name]
delete obj[name]
return true
}
})
}
}

/* Computable */

const Computable = Base => class extends Base {
constructor() {
super()
Object.defineProperty(this, '__computed', { value: [ ], enumerable: false })
}
computed(fn) {
this.__computed.push(computed(fn))
}
dispose() {
while(this.__computed.length) dispose(this.__computed.pop())
}
}

/* container */

const container = function(options) {
if(typeof options === 'function') options = { deep: true, batch: true, handler: options }
return observe({ }, options)
}

export default {
observe,
computed,
dispose
dispose,
Observable,
Computable,
container
}

0 comments on commit f63aee9

Please sign in to comment.