This commit is contained in:
dntzhang 2018-12-06 11:33:46 +08:00
parent cbdd851695
commit af66475726
12 changed files with 457 additions and 452 deletions

View File

@ -1,6 +1,4 @@
//app.js
App({
onLaunch: function () {
}
})
onLaunch: function() {}
})

View File

@ -1,17 +1,15 @@
import vm from '../../view-model/todo'
Component({
properties: {
items:{
items: {
type: Array,
value:[]
value: []
}
},
ready: function () {
},
ready: function() {},
methods: {
checkboxChange: function(e) {
vm.toogleComplete(e.currentTarget.dataset.id)

View File

@ -56,7 +56,7 @@ export default class Todo {
toogleComplete(id) {
this.items.every(item => {
if (id === item.id) {
item.completed = !item.completed
item.completed = !item.completed
return false
}
return true

View File

@ -1,19 +1,20 @@
import vm from '../../view-model/todo'
import create from '../../utils/create'
create.Page(vm, {
data:{
value:''
data: {
text: ''
},
onLoad: function () {
onLoad: function() {
vm.getAll()
},
tapHandler:function(){
vm.add('abc')
inputHandler: function(e) {
this.text = e.detail.value
},
tapHandler: function() {
vm.add(this.text)
this.setData({
value:''
text: ''
})
}
})

View File

@ -4,7 +4,7 @@
<view>
<todo-list items="{{items}}" />
<view class="form">
<input class="input" type="text" placeholder="Input your task" on value="{{value}}" />
<input class="input" bindinput="inputHandler" type="text" placeholder="Input your task" value="{{text}}" />
<button class="button" bindtap="tapHandler">Add{{items.length}}</button>
</view>
</view>

42
packages/mp-mvvm/utils/create.js Executable file → Normal file
View File

@ -1,33 +1,29 @@
import JSONProxy from './proxy'
const create = {}
create.Page = function (vm, options) {
create.Page = function(vm, options) {
options.data = vm.data
options.data = vm.data
const onLoad = options.onLoad
const onLoad = options.onLoad
options.onLoad = function (e) {
vm.data = new JSONProxy(vm.data).observe(false, info => {
this.setData(vm.data)
})
onLoad && onLoad.call(this, e)
}
Page(options)
options.onLoad = function(e) {
vm.data = new JSONProxy(vm.data).observe(false, info => {
this.setData(vm.data)
})
onLoad && onLoad.call(this, e)
}
Page(options)
}
create.Component = function () {
create.Component = function() {}
}
if (typeof exports == "object") {
module.exports = create
} else if (typeof define == "function" && define.amd) {
define([], function () { return create })
if (typeof exports == 'object') {
module.exports = create
} else if (typeof define == 'function' && define.amd) {
define([], function() {
return create
})
} else {
window.create = create
}
window.create = create
}

View File

@ -13,168 +13,170 @@ const fnMapping = {}
const fnPreResult = {}
const fnCurrentResult = {}
const handler = function (patch) {
clearTimeout(timeout)
if (patch.op === 'remove') {//fix arr splice
const kv = getArrayPatch(patch.path)
patchs[kv.k] = kv.v
timeout = setTimeout(function () {
_update(patchs)
patchs = {}
})
} else {
const key = fixPath(patch.path)
patchs[key] = patch.value
timeout = setTimeout(function () {
_update(patchs)
patchs = {}
})
}
const handler = function(patch) {
clearTimeout(timeout)
if (patch.op === 'remove') {
//fix arr splice
const kv = getArrayPatch(patch.path)
patchs[kv.k] = kv.v
timeout = setTimeout(function() {
_update(patchs)
patchs = {}
})
} else {
const key = fixPath(patch.path)
patchs[key] = patch.value
timeout = setTimeout(function() {
_update(patchs)
patchs = {}
})
}
}
export default function create(store, option) {
if (arguments.length === 2) {
if (option.data && Object.keys(option.data).length > 0) {
Object.assign(store.data, option.data)
}
if (!store.instances) {
store.instances = {}
store.update = update
}
getApp().globalData && (getApp().globalData.store = store)
globalStore = store
option.data = store.data
currentData = store.data
const jp = new JSONProxy(store.data, handler)
const onLoad = option.onLoad
setFnMapping(globalStore.data)
option.onLoad = function (e) {
this.update = update
this.store = store
this.store.data = jp.observe(true, handler)
store.instances[this.route] = []
store.instances[this.route].push(this)
onLoad && onLoad.call(this, e)
}
Page(option)
} else {
const ready = store.ready
store.ready = function () {
this.update = update
this.page = getCurrentPages()[getCurrentPages().length - 1]
this.store = this.page.store
Object.assign(this.store.data, store.data)
setFnMapping(store.data)
this.setData.call(this, this.store.data)
this.store.instances[this.page.route].push(this)
ready && ready.call(this)
}
Component(store)
if (arguments.length === 2) {
if (option.data && Object.keys(option.data).length > 0) {
Object.assign(store.data, option.data)
}
if (!store.instances) {
store.instances = {}
store.update = update
}
getApp().globalData && (getApp().globalData.store = store)
globalStore = store
option.data = store.data
currentData = store.data
const jp = new JSONProxy(store.data, handler)
const onLoad = option.onLoad
setFnMapping(globalStore.data)
option.onLoad = function(e) {
this.update = update
this.store = store
this.store.data = jp.observe(true, handler)
store.instances[this.route] = []
store.instances[this.route].push(this)
onLoad && onLoad.call(this, e)
}
Page(option)
} else {
const ready = store.ready
store.ready = function() {
this.update = update
this.page = getCurrentPages()[getCurrentPages().length - 1]
this.store = this.page.store
Object.assign(this.store.data, store.data)
setFnMapping(store.data)
this.setData.call(this, this.store.data)
this.store.instances[this.page.route].push(this)
ready && ready.call(this)
}
Component(store)
}
}
function _update(kv) {
defineFnProp()
Object.keys(fnCurrentResult).forEach(key => {
const v = fnCurrentResult[key]
if(v !== fnPreResult[key]){
kv[key] = v
fnPreResult[key] = v
}
})
setFnMapping(globalStore.data)
for (let key in globalStore.instances) {
globalStore.instances[key].forEach(ins => {
ins.setData.call(ins, kv)
})
defineFnProp()
Object.keys(fnCurrentResult).forEach(key => {
const v = fnCurrentResult[key]
if (v !== fnPreResult[key]) {
kv[key] = v
fnPreResult[key] = v
}
globalStore.onChange && globalStore.onChange(kv)
})
setFnMapping(globalStore.data)
for (let key in globalStore.instances) {
globalStore.instances[key].forEach(ins => {
ins.setData.call(ins, kv)
})
}
globalStore.onChange && globalStore.onChange(kv)
}
function update(patch) {
if (patch) {
for (let key in patch) {
updateByPath(globalStore.data, key, patch[key])
}
if (patch) {
for (let key in patch) {
updateByPath(globalStore.data, key, patch[key])
}
}
}
function setFnMapping(data) {
Object.keys(data).forEach(key => {
const fn = data[key]
if (typeof fn == 'function') {
fnMapping[key] = () => {
return fn.call(globalStore.data)
}
}
})
Object.keys(data).forEach(key => {
const fn = data[key]
if (typeof fn == 'function') {
fnMapping[key] = () => {
return fn.call(globalStore.data)
}
}
})
}
function defineFnProp(){
Object.keys(fnMapping).forEach(key => {
fnCurrentResult[key] = fnMapping[key]()
})
function defineFnProp() {
Object.keys(fnMapping).forEach(key => {
fnCurrentResult[key] = fnMapping[key]()
})
}
function getArrayPatch(path) {
const arr = path.replace('/', '').split('/')
let current = currentData[arr[0]]
for (let i = 1, len = arr.length; i < len - 1; i++) {
current = current[arr[i]]
}
return { k: fixArrPath(path), v: current }
const arr = path.replace('/', '').split('/')
let current = currentData[arr[0]]
for (let i = 1, len = arr.length; i < len - 1; i++) {
current = current[arr[i]]
}
return { k: fixArrPath(path), v: current }
}
function fixArrPath(path) {
let mpPath = ''
const arr = path.replace('/', '').split('/')
const len = arr.length
arr.forEach((item, index) => {
if (index < len - 1) {
if (index) {
if (isNaN(parseInt(item))) {
mpPath += '.' + item
} else {
mpPath += '[' + item + ']'
}
} else {
mpPath += item
}
let mpPath = ''
const arr = path.replace('/', '').split('/')
const len = arr.length
arr.forEach((item, index) => {
if (index < len - 1) {
if (index) {
if (isNaN(parseInt(item))) {
mpPath += '.' + item
} else {
mpPath += '[' + item + ']'
}
})
return mpPath
} else {
mpPath += item
}
}
})
return mpPath
}
function fixPath(path) {
let mpPath = ''
const arr = path.replace('/', '').split('/')
arr.forEach((item, index) => {
if (index) {
if (isNaN(parseInt(item))) {
mpPath += '.' + item
} else {
mpPath += '[' + item + ']'
}
} else {
mpPath += item
}
})
return mpPath
let mpPath = ''
const arr = path.replace('/', '').split('/')
arr.forEach((item, index) => {
if (index) {
if (isNaN(parseInt(item))) {
mpPath += '.' + item
} else {
mpPath += '[' + item + ']'
}
} else {
mpPath += item
}
})
return mpPath
}
function updateByPath(origin, path, value) {
const arr = path.replace(/]/g,'').replace(/\[/g, '.').split('.')
let current = origin
for (let i = 0, len = arr.length; i < len; i++) {
if (i === len - 1) {
current[arr[i]] = value
} else {
current = current[arr[i]]
}
const arr = path
.replace(/]/g, '')
.replace(/\[/g, '.')
.split('.')
let current = origin
for (let i = 0, len = arr.length; i < len; i++) {
if (i === len - 1) {
current[arr[i]] = value
} else {
current = current[arr[i]]
}
}
}
}

View File

@ -9,19 +9,19 @@
var ARRAYTYPE = '[object Array]'
var OBJECTTYPE = '[object Object]'
var mapping = function (options) {
var mapping = function(options) {
var from = options.from
var to = options.to
var rules = options.rule
var res = to || {}
Object.keys(from).forEach(function (key) {
Object.keys(from).forEach(function(key) {
res[key] = from[key]
})
rules &&
Object.keys(rules).forEach(function (key) {
Object.keys(rules).forEach(function(key) {
var rule = rules[key]
var isPath = key.match(/\.|\[/)
if (typeof rule === 'function') {
@ -69,12 +69,12 @@ function setPathValue(obj, path, value) {
}
}
mapping.auto = function (from, to) {
mapping.auto = function(from, to) {
return objMapping(from, to)
}
function arrayMapping(from, to) {
from.forEach(function (item, index) {
from.forEach(function(item, index) {
if (isArray(item)) {
to[index] = to[index] || []
arrayMapping(item, to[index])
@ -111,10 +111,12 @@ function isObject(obj) {
return Object.prototype.toString.call(obj) === OBJECTTYPE
}
if (typeof exports == "object") {
if (typeof exports == 'object') {
module.exports = mapping
} else if (typeof define == "function" && define.amd) {
define([], function () { return mapping })
} else if (typeof define == 'function' && define.amd) {
define([], function() {
return mapping
})
} else {
window.mapping = mapping
}

View File

@ -13,168 +13,170 @@ const fnMapping = {}
const fnPreResult = {}
const fnCurrentResult = {}
const handler = function (patch) {
clearTimeout(timeout)
if (patch.op === 'remove') {//fix arr splice
const kv = getArrayPatch(patch.path)
patchs[kv.k] = kv.v
timeout = setTimeout(function () {
_update(patchs)
patchs = {}
})
} else {
const key = fixPath(patch.path)
patchs[key] = patch.value
timeout = setTimeout(function () {
_update(patchs)
patchs = {}
})
}
const handler = function(patch) {
clearTimeout(timeout)
if (patch.op === 'remove') {
//fix arr splice
const kv = getArrayPatch(patch.path)
patchs[kv.k] = kv.v
timeout = setTimeout(function() {
_update(patchs)
patchs = {}
})
} else {
const key = fixPath(patch.path)
patchs[key] = patch.value
timeout = setTimeout(function() {
_update(patchs)
patchs = {}
})
}
}
export default function create(store, option) {
if (arguments.length === 2) {
if (option.data && Object.keys(option.data).length > 0) {
Object.assign(store.data, option.data)
}
if (!store.instances) {
store.instances = {}
store.update = update
}
getApp().globalData && (getApp().globalData.store = store)
globalStore = store
option.data = store.data
currentData = store.data
const jp = new JSONProxy(store.data, handler)
const onLoad = option.onLoad
setFnMapping(globalStore.data)
option.onLoad = function (e) {
this.update = update
this.store = store
this.store.data = jp.observe(true, handler)
store.instances[this.route] = []
store.instances[this.route].push(this)
onLoad && onLoad.call(this, e)
}
Page(option)
} else {
const ready = store.ready
store.ready = function () {
this.update = update
this.page = getCurrentPages()[getCurrentPages().length - 1]
this.store = this.page.store
Object.assign(this.store.data, store.data)
setFnMapping(store.data)
this.setData.call(this, this.store.data)
this.store.instances[this.page.route].push(this)
ready && ready.call(this)
}
Component(store)
if (arguments.length === 2) {
if (option.data && Object.keys(option.data).length > 0) {
Object.assign(store.data, option.data)
}
if (!store.instances) {
store.instances = {}
store.update = update
}
getApp().globalData && (getApp().globalData.store = store)
globalStore = store
option.data = store.data
currentData = store.data
const jp = new JSONProxy(store.data, handler)
const onLoad = option.onLoad
setFnMapping(globalStore.data)
option.onLoad = function(e) {
this.update = update
this.store = store
this.store.data = jp.observe(true, handler)
store.instances[this.route] = []
store.instances[this.route].push(this)
onLoad && onLoad.call(this, e)
}
Page(option)
} else {
const ready = store.ready
store.ready = function() {
this.update = update
this.page = getCurrentPages()[getCurrentPages().length - 1]
this.store = this.page.store
Object.assign(this.store.data, store.data)
setFnMapping(store.data)
this.setData.call(this, this.store.data)
this.store.instances[this.page.route].push(this)
ready && ready.call(this)
}
Component(store)
}
}
function _update(kv) {
defineFnProp()
Object.keys(fnCurrentResult).forEach(key => {
const v = fnCurrentResult[key]
if(v !== fnPreResult[key]){
kv[key] = v
fnPreResult[key] = v
}
})
setFnMapping(globalStore.data)
for (let key in globalStore.instances) {
globalStore.instances[key].forEach(ins => {
ins.setData.call(ins, kv)
})
defineFnProp()
Object.keys(fnCurrentResult).forEach(key => {
const v = fnCurrentResult[key]
if (v !== fnPreResult[key]) {
kv[key] = v
fnPreResult[key] = v
}
globalStore.onChange && globalStore.onChange(kv)
})
setFnMapping(globalStore.data)
for (let key in globalStore.instances) {
globalStore.instances[key].forEach(ins => {
ins.setData.call(ins, kv)
})
}
globalStore.onChange && globalStore.onChange(kv)
}
function update(patch) {
if (patch) {
for (let key in patch) {
updateByPath(globalStore.data, key, patch[key])
}
if (patch) {
for (let key in patch) {
updateByPath(globalStore.data, key, patch[key])
}
}
}
function setFnMapping(data) {
Object.keys(data).forEach(key => {
const fn = data[key]
if (typeof fn == 'function') {
fnMapping[key] = () => {
return fn.call(globalStore.data)
}
}
})
Object.keys(data).forEach(key => {
const fn = data[key]
if (typeof fn == 'function') {
fnMapping[key] = () => {
return fn.call(globalStore.data)
}
}
})
}
function defineFnProp(){
Object.keys(fnMapping).forEach(key => {
fnCurrentResult[key] = fnMapping[key]()
})
function defineFnProp() {
Object.keys(fnMapping).forEach(key => {
fnCurrentResult[key] = fnMapping[key]()
})
}
function getArrayPatch(path) {
const arr = path.replace('/', '').split('/')
let current = currentData[arr[0]]
for (let i = 1, len = arr.length; i < len - 1; i++) {
current = current[arr[i]]
}
return { k: fixArrPath(path), v: current }
const arr = path.replace('/', '').split('/')
let current = currentData[arr[0]]
for (let i = 1, len = arr.length; i < len - 1; i++) {
current = current[arr[i]]
}
return { k: fixArrPath(path), v: current }
}
function fixArrPath(path) {
let mpPath = ''
const arr = path.replace('/', '').split('/')
const len = arr.length
arr.forEach((item, index) => {
if (index < len - 1) {
if (index) {
if (isNaN(parseInt(item))) {
mpPath += '.' + item
} else {
mpPath += '[' + item + ']'
}
} else {
mpPath += item
}
let mpPath = ''
const arr = path.replace('/', '').split('/')
const len = arr.length
arr.forEach((item, index) => {
if (index < len - 1) {
if (index) {
if (isNaN(parseInt(item))) {
mpPath += '.' + item
} else {
mpPath += '[' + item + ']'
}
})
return mpPath
} else {
mpPath += item
}
}
})
return mpPath
}
function fixPath(path) {
let mpPath = ''
const arr = path.replace('/', '').split('/')
arr.forEach((item, index) => {
if (index) {
if (isNaN(parseInt(item))) {
mpPath += '.' + item
} else {
mpPath += '[' + item + ']'
}
} else {
mpPath += item
}
})
return mpPath
let mpPath = ''
const arr = path.replace('/', '').split('/')
arr.forEach((item, index) => {
if (index) {
if (isNaN(parseInt(item))) {
mpPath += '.' + item
} else {
mpPath += '[' + item + ']'
}
} else {
mpPath += item
}
})
return mpPath
}
function updateByPath(origin, path, value) {
const arr = path.replace(/]/g,'').replace(/\[/g, '.').split('.')
let current = origin
for (let i = 0, len = arr.length; i < len; i++) {
if (i === len - 1) {
current[arr[i]] = value
} else {
current = current[arr[i]]
}
const arr = path
.replace(/]/g, '')
.replace(/\[/g, '.')
.split('.')
let current = origin
for (let i = 0, len = arr.length; i < len; i++) {
if (i === len - 1) {
current[arr[i]] = value
} else {
current = current[arr[i]]
}
}
}
}

View File

@ -1,52 +1,52 @@
'use strict';
'use strict'
/*!
* https://github.com/Palindrom/JSONPatcherProxy
* (c) 2017 Starcounter
* (c) 2017 Starcounter
* MIT license
*/
/** Class representing a JS Object observer */
const JSONPatcherProxy = (function() {
/**
* Deep clones your object and returns a new object.
*/
* Deep clones your object and returns a new object.
*/
function deepClone(obj) {
switch (typeof obj) {
case 'object':
return JSON.parse(JSON.stringify(obj)); //Faster than ES5 clone - http://jsperf.com/deep-cloning-of-objects/5
case 'undefined':
return null; //this is how JSON.stringify behaves for array items
default:
return obj; //no need to clone primitives
return JSON.parse(JSON.stringify(obj)) //Faster than ES5 clone - http://jsperf.com/deep-cloning-of-objects/5
case 'undefined':
return null //this is how JSON.stringify behaves for array items
default:
return obj //no need to clone primitives
}
}
JSONPatcherProxy.deepClone = deepClone;
JSONPatcherProxy.deepClone = deepClone
function escapePathComponent(str) {
if (str.indexOf('/') == -1 && str.indexOf('~') == -1) return str;
return str.replace(/~/g, '~0').replace(/\//g, '~1');
if (str.indexOf('/') == -1 && str.indexOf('~') == -1) return str
return str.replace(/~/g, '~0').replace(/\//g, '~1')
}
JSONPatcherProxy.escapePathComponent = escapePathComponent;
JSONPatcherProxy.escapePathComponent = escapePathComponent
/**
* Walk up the parenthood tree to get the path
* @param {JSONPatcherProxy} instance
* @param {JSONPatcherProxy} instance
* @param {Object} obj the object you need to find its path
*/
function findObjectPath(instance, obj) {
const pathComponents = [];
let parentAndPath = instance.parenthoodMap.get(obj);
const pathComponents = []
let parentAndPath = instance.parenthoodMap.get(obj)
while (parentAndPath && parentAndPath.path) {
// because we're walking up-tree, we need to use the array as a stack
pathComponents.unshift(parentAndPath.path);
parentAndPath = instance.parenthoodMap.get(parentAndPath.parent);
pathComponents.unshift(parentAndPath.path)
parentAndPath = instance.parenthoodMap.get(parentAndPath.parent)
}
if (pathComponents.length) {
const path = pathComponents.join('/');
return '/' + path;
const path = pathComponents.join('/')
return '/' + path
}
return '';
return ''
}
/**
* A callback to be used as th proxy set trap callback.
@ -57,17 +57,17 @@ const JSONPatcherProxy = (function() {
* @param {Any} newValue the value being set
*/
function setTrap(instance, target, key, newValue) {
const parentPath = findObjectPath(instance, target);
const parentPath = findObjectPath(instance, target)
const destinationPropKey = parentPath + '/' + escapePathComponent(key);
const destinationPropKey = parentPath + '/' + escapePathComponent(key)
if (instance.proxifiedObjectsMap.has(newValue)) {
const newValueOriginalObject = instance.proxifiedObjectsMap.get(newValue);
const newValueOriginalObject = instance.proxifiedObjectsMap.get(newValue)
instance.parenthoodMap.set(newValueOriginalObject.originalObject, {
parent: target,
path: key
});
})
}
/*
mark already proxified values as inherited.
@ -79,7 +79,7 @@ const JSONPatcherProxy = (function() {
by default, the second operation would revoke the proxy, and this renders arr revoked.
That's why we need to remember the proxies that are inherited.
*/
const revokableInstance = instance.proxifiedObjectsMap.get(newValue);
const revokableInstance = instance.proxifiedObjectsMap.get(newValue)
/*
Why do we need to check instance.isProxifyingTreeNow?
@ -92,7 +92,7 @@ const JSONPatcherProxy = (function() {
but in fact is is a proxified object moved around the tree
*/
if (revokableInstance && !instance.isProxifyingTreeNow) {
revokableInstance.inherited = true;
revokableInstance.inherited = true
}
// if the new value is an object, make sure to watch it
@ -104,52 +104,54 @@ const JSONPatcherProxy = (function() {
instance.parenthoodMap.set(newValue, {
parent: target,
path: key
});
newValue = instance._proxifyObjectTreeRecursively(target, newValue, key);
})
newValue = instance._proxifyObjectTreeRecursively(target, newValue, key)
}
// let's start with this operation, and may or may not update it later
const operation = {
op: 'remove',
path: destinationPropKey
};
}
if (typeof newValue == 'undefined') {
// applying De Morgan's laws would be a tad faster, but less readable
if (!Array.isArray(target) && !target.hasOwnProperty(key)) {
// `undefined` is being set to an already undefined value, keep silent
return Reflect.set(target, key, newValue);
return Reflect.set(target, key, newValue)
} else {
// when array element is set to `undefined`, should generate replace to `null`
if (Array.isArray(target)) {
// undefined array elements are JSON.stringified to `null`
(operation.op = 'replace'), (operation.value = null);
;(operation.op = 'replace'), (operation.value = null)
}
const oldValue = instance.proxifiedObjectsMap.get(target[key]);
const oldValue = instance.proxifiedObjectsMap.get(target[key])
// was the deleted a proxified object?
if (oldValue) {
instance.parenthoodMap.delete(target[key]);
instance.disableTrapsForProxy(oldValue);
instance.proxifiedObjectsMap.delete(oldValue);
instance.parenthoodMap.delete(target[key])
instance.disableTrapsForProxy(oldValue)
instance.proxifiedObjectsMap.delete(oldValue)
}
}
} else {
if (Array.isArray(target) && !Number.isInteger(+key.toString())) {
/* array props (as opposed to indices) don't emit any patches, to avoid needless `length` patches */
if(key != 'length') {
console.warn('JSONPatcherProxy noticed a non-integer prop was set for an array. This will not emit a patch');
if (key != 'length') {
console.warn(
'JSONPatcherProxy noticed a non-integer prop was set for an array. This will not emit a patch'
)
}
return Reflect.set(target, key, newValue);
return Reflect.set(target, key, newValue)
}
operation.op = 'add';
operation.op = 'add'
if (target.hasOwnProperty(key)) {
if (typeof target[key] !== 'undefined' || Array.isArray(target)) {
operation.op = 'replace'; // setting `undefined` array elements is a `replace` op
operation.op = 'replace' // setting `undefined` array elements is a `replace` op
}
}
operation.value = newValue;
operation.value = newValue
}
const reflectionResult = Reflect.set(target, key, newValue);
instance.defaultCallback(operation);
return reflectionResult;
const reflectionResult = Reflect.set(target, key, newValue)
instance.defaultCallback(operation)
return reflectionResult
}
/**
* A callback to be used as th proxy delete trap callback.
@ -160,12 +162,12 @@ const JSONPatcherProxy = (function() {
*/
function deleteTrap(instance, target, key) {
if (typeof target[key] !== 'undefined') {
const parentPath = findObjectPath(instance, target);
const destinationPropKey = parentPath + '/' + escapePathComponent(key);
const parentPath = findObjectPath(instance, target)
const destinationPropKey = parentPath + '/' + escapePathComponent(key)
const revokableProxyInstance = instance.proxifiedObjectsMap.get(
target[key]
);
)
if (revokableProxyInstance) {
if (revokableProxyInstance.inherited) {
@ -177,91 +179,91 @@ const JSONPatcherProxy = (function() {
it is a good idea to remove this flag if we come across it here, in deleteProperty trap.
We DO want to revoke the proxy if it was removed again.
*/
revokableProxyInstance.inherited = false;
revokableProxyInstance.inherited = false
} else {
instance.parenthoodMap.delete(revokableProxyInstance.originalObject);
instance.disableTrapsForProxy(revokableProxyInstance);
instance.proxifiedObjectsMap.delete(target[key]);
instance.parenthoodMap.delete(revokableProxyInstance.originalObject)
instance.disableTrapsForProxy(revokableProxyInstance)
instance.proxifiedObjectsMap.delete(target[key])
}
}
const reflectionResult = Reflect.deleteProperty(target, key);
const reflectionResult = Reflect.deleteProperty(target, key)
instance.defaultCallback({
op: 'remove',
path: destinationPropKey
});
})
return reflectionResult;
return reflectionResult
}
}
/* pre-define resume and pause functions to enhance constructors performance */
function resume() {
this.defaultCallback = operation => {
this.isRecording && this.patches.push(operation);
this.userCallback && this.userCallback(operation);
};
this.isObserving = true;
this.isRecording && this.patches.push(operation)
this.userCallback && this.userCallback(operation)
}
this.isObserving = true
}
function pause() {
this.defaultCallback = () => {};
this.isObserving = false;
this.defaultCallback = () => {}
this.isObserving = false
}
/**
* Creates an instance of JSONPatcherProxy around your object of interest `root`.
* @param {Object|Array} root - the object you want to wrap
* @param {Boolean} [showDetachedWarning = true] - whether to log a warning when a detached sub-object is modified @see {@link https://github.com/Palindrom/JSONPatcherProxy#detached-objects}
* @returns {JSONPatcherProxy}
* @constructor
*/
* Creates an instance of JSONPatcherProxy around your object of interest `root`.
* @param {Object|Array} root - the object you want to wrap
* @param {Boolean} [showDetachedWarning = true] - whether to log a warning when a detached sub-object is modified @see {@link https://github.com/Palindrom/JSONPatcherProxy#detached-objects}
* @returns {JSONPatcherProxy}
* @constructor
*/
function JSONPatcherProxy(root, showDetachedWarning) {
this.isProxifyingTreeNow = false;
this.isObserving = false;
this.proxifiedObjectsMap = new Map();
this.parenthoodMap = new Map();
this.isProxifyingTreeNow = false
this.isObserving = false
this.proxifiedObjectsMap = new Map()
this.parenthoodMap = new Map()
// default to true
if (typeof showDetachedWarning !== 'boolean') {
showDetachedWarning = true;
showDetachedWarning = true
}
this.showDetachedWarning = showDetachedWarning;
this.originalObject = root;
this.cachedProxy = null;
this.isRecording = false;
this.userCallback;
this.showDetachedWarning = showDetachedWarning
this.originalObject = root
this.cachedProxy = null
this.isRecording = false
this.userCallback
/**
* @memberof JSONPatcherProxy
* Restores callback back to the original one provided to `observe`.
*/
this.resume = resume.bind(this);
this.resume = resume.bind(this)
/**
* @memberof JSONPatcherProxy
* Replaces your callback with a noop function.
*/
this.pause = pause.bind(this);
this.pause = pause.bind(this)
}
JSONPatcherProxy.prototype.generateProxyAtPath = function(parent, obj, path) {
if (!obj) {
return obj;
return obj
}
const traps = {
set: (target, key, value, receiver) =>
setTrap(this, target, key, value, receiver),
deleteProperty: (target, key) => deleteTrap(this, target, key)
};
const revocableInstance = Proxy.revocable(obj, traps);
}
const revocableInstance = Proxy.revocable(obj, traps)
// cache traps object to disable them later.
revocableInstance.trapsInstance = traps;
revocableInstance.originalObject = obj;
revocableInstance.trapsInstance = traps
revocableInstance.originalObject = obj
/* keeping track of object's parent and path */
this.parenthoodMap.set(obj, { parent, path });
this.parenthoodMap.set(obj, { parent, path })
/* keeping track of all the proxies to be able to revoke them later */
this.proxifiedObjectsMap.set(revocableInstance.proxy, revocableInstance);
return revocableInstance.proxy;
};
this.proxifiedObjectsMap.set(revocableInstance.proxy, revocableInstance)
return revocableInstance.proxy
}
// grab tree's leaves one by one, encapsulate them into a proxy and return
JSONPatcherProxy.prototype._proxifyObjectTreeRecursively = function(
parent,
@ -275,12 +277,12 @@ const JSONPatcherProxy = (function() {
root,
root[key],
escapePathComponent(key)
);
)
}
}
}
return this.generateProxyAtPath(parent, root, path);
};
return this.generateProxyAtPath(parent, root, path)
}
// this function is for aesthetic purposes
JSONPatcherProxy.prototype.proxifyObjectTree = function(root) {
/*
@ -290,18 +292,18 @@ const JSONPatcherProxy = (function() {
that's why we disable recording through this
initial process;
*/
this.pause();
this.isProxifyingTreeNow = true;
this.pause()
this.isProxifyingTreeNow = true
const proxifiedObject = this._proxifyObjectTreeRecursively(
undefined,
root,
''
);
)
/* OK you can record now */
this.isProxifyingTreeNow = false;
this.resume();
return proxifiedObject;
};
this.isProxifyingTreeNow = false
this.resume()
return proxifiedObject
}
/**
* Turns a proxified object into a forward-proxy object; doesn't emit any patches anymore, like a normal object
* @param {Proxy} proxy - The target proxy object
@ -311,36 +313,36 @@ const JSONPatcherProxy = (function() {
) {
if (this.showDetachedWarning) {
const message =
"You're accessing an object that is detached from the observedObject tree, see https://github.com/Palindrom/JSONPatcherProxy#detached-objects";
"You're accessing an object that is detached from the observedObject tree, see https://github.com/Palindrom/JSONPatcherProxy#detached-objects"
revokableProxyInstance.trapsInstance.set = (
targetObject,
propKey,
newValue
) => {
console.warn(message);
return Reflect.set(targetObject, propKey, newValue);
};
console.warn(message)
return Reflect.set(targetObject, propKey, newValue)
}
revokableProxyInstance.trapsInstance.set = (
targetObject,
propKey,
newValue
) => {
console.warn(message);
return Reflect.set(targetObject, propKey, newValue);
};
console.warn(message)
return Reflect.set(targetObject, propKey, newValue)
}
revokableProxyInstance.trapsInstance.deleteProperty = (
targetObject,
propKey
) => {
return Reflect.deleteProperty(targetObject, propKey);
};
return Reflect.deleteProperty(targetObject, propKey)
}
} else {
delete revokableProxyInstance.trapsInstance.set;
delete revokableProxyInstance.trapsInstance.get;
delete revokableProxyInstance.trapsInstance.deleteProperty;
delete revokableProxyInstance.trapsInstance.set
delete revokableProxyInstance.trapsInstance.get
delete revokableProxyInstance.trapsInstance.deleteProperty
}
};
}
/**
* Proxifies the object that was passed in the constructor and returns a proxified mirror of it. Even though both parameters are options. You need to pass at least one of them.
* @param {Boolean} [record] - whether to record object changes to a later-retrievable patches array.
@ -348,47 +350,47 @@ const JSONPatcherProxy = (function() {
*/
JSONPatcherProxy.prototype.observe = function(record, callback) {
if (!record && !callback) {
throw new Error('You need to either record changes or pass a callback');
throw new Error('You need to either record changes or pass a callback')
}
this.isRecording = record;
this.userCallback = callback;
this.isRecording = record
this.userCallback = callback
/*
I moved it here to remove it from `unobserve`,
this will also make the constructor faster, why initiate
the array before they decide to actually observe with recording?
They might need to use only a callback.
*/
if (record) this.patches = [];
this.cachedProxy = this.proxifyObjectTree(this.originalObject);
return this.cachedProxy;
};
if (record) this.patches = []
this.cachedProxy = this.proxifyObjectTree(this.originalObject)
return this.cachedProxy
}
/**
* If the observed is set to record, it will synchronously return all the patches and empties patches array.
*/
JSONPatcherProxy.prototype.generate = function() {
if (!this.isRecording) {
throw new Error('You should set record to true to get patches later');
throw new Error('You should set record to true to get patches later')
}
return this.patches.splice(0, this.patches.length);
};
return this.patches.splice(0, this.patches.length)
}
/**
* Revokes all proxies rendering the observed object useless and good for garbage collection @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/revocable}
*/
JSONPatcherProxy.prototype.revoke = function() {
this.proxifiedObjectsMap.forEach(el => {
el.revoke();
});
};
el.revoke()
})
}
/**
* Disables all proxies' traps, turning the observed object into a forward-proxy object, like a normal object that you can modify silently.
*/
JSONPatcherProxy.prototype.disableTraps = function() {
this.proxifiedObjectsMap.forEach(this.disableTrapsForProxy, this);
};
return JSONPatcherProxy;
})();
this.proxifiedObjectsMap.forEach(this.disableTrapsForProxy, this)
}
return JSONPatcherProxy
})()
if (typeof module !== 'undefined') {
module.exports = JSONPatcherProxy;
module.exports.default = JSONPatcherProxy;
}
module.exports = JSONPatcherProxy
module.exports.default = JSONPatcherProxy
}

View File

@ -6,7 +6,11 @@ const formatTime = date => {
const minute = date.getMinutes()
const second = date.getSeconds()
return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')
return (
[year, month, day].map(formatNumber).join('/') +
' ' +
[hour, minute, second].map(formatNumber).join(':')
)
}
const formatNumber = n => {

View File

@ -23,7 +23,7 @@ class TodoViewModel {
this.update()
}
toogleComplete(id){
toogleComplete(id) {
todo.toogleComplete(id)
this.update()
}