mirror of https://github.com/vuejs/vue.git
				
				
				
			further simplify observe
This commit is contained in:
		
							parent
							
								
									1dfb10e491
								
							
						
					
					
						commit
						36599833de
					
				| 
						 | 
					@ -46,6 +46,7 @@ exports.$add = function (key, val) {
 | 
				
			||||||
  if (!_.isReserved(key)) {
 | 
					  if (!_.isReserved(key)) {
 | 
				
			||||||
    this._data.$add(key, val)
 | 
					    this._data.$add(key, val)
 | 
				
			||||||
    this._proxy(key)
 | 
					    this._proxy(key)
 | 
				
			||||||
 | 
					    this._digest()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -59,6 +60,7 @@ exports.$delete = function (key) {
 | 
				
			||||||
  if (!_.isReserved(key)) {
 | 
					  if (!_.isReserved(key)) {
 | 
				
			||||||
    this._data.$delete(key)
 | 
					    this._data.$delete(key)
 | 
				
			||||||
    this._unproxy(key)
 | 
					    this._unproxy(key)
 | 
				
			||||||
 | 
					    this._digest()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -115,8 +115,6 @@ exports.$destroy = function (remove) {
 | 
				
			||||||
  for (i in this._userWatchers) {
 | 
					  for (i in this._userWatchers) {
 | 
				
			||||||
    this._userWatchers[i].teardown()
 | 
					    this._userWatchers[i].teardown()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  // teardown data/scope
 | 
					 | 
				
			||||||
  this._teardownScope()
 | 
					 | 
				
			||||||
  // clean up
 | 
					  // clean up
 | 
				
			||||||
  this._data =
 | 
					  this._data =
 | 
				
			||||||
  this._watchers =
 | 
					  this._watchers =
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,6 +18,7 @@ exports._init = function (options) {
 | 
				
			||||||
  this.$el           = null
 | 
					  this.$el           = null
 | 
				
			||||||
  this.$root         = this.$root || this
 | 
					  this.$root         = this.$root || this
 | 
				
			||||||
  this.$             = {}
 | 
					  this.$             = {}
 | 
				
			||||||
 | 
					  this._watcherList  = []
 | 
				
			||||||
  this._watchers     = {}
 | 
					  this._watchers     = {}
 | 
				
			||||||
  this._userWatchers = {}
 | 
					  this._userWatchers = {}
 | 
				
			||||||
  this._directives   = []
 | 
					  this._directives   = []
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,18 +23,6 @@ exports._initScope = function () {
 | 
				
			||||||
  this._initMeta()
 | 
					  this._initMeta()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Teardown the scope.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
exports._teardownScope = function () {
 | 
					 | 
				
			||||||
  var dataOb = this._data.__ob__
 | 
					 | 
				
			||||||
  dataOb.vmCount--
 | 
					 | 
				
			||||||
  dataOb.tryRelease()
 | 
					 | 
				
			||||||
  // unset data reference
 | 
					 | 
				
			||||||
  this._data = null
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Initialize the data. 
 | 
					 * Initialize the data. 
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
| 
						 | 
					@ -48,8 +36,7 @@ exports._initData = function () {
 | 
				
			||||||
    this._proxy(keys[i])
 | 
					    this._proxy(keys[i])
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  // observe data
 | 
					  // observe data
 | 
				
			||||||
  var ob = Observer.create(data)
 | 
					  Observer.create(data)
 | 
				
			||||||
  ob.vmCount++
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					@ -82,20 +69,8 @@ exports._setData = function (newData) {
 | 
				
			||||||
      this._proxy(key)
 | 
					      this._proxy(key)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  // observe new / teardown old
 | 
					  Observer.create(newData)
 | 
				
			||||||
  var newOb = Observer.create(newData)
 | 
					  this._digest()
 | 
				
			||||||
  var oldOb = oldData.__ob__
 | 
					 | 
				
			||||||
  newOb.vmCount++
 | 
					 | 
				
			||||||
  oldOb.vmCount--
 | 
					 | 
				
			||||||
  // memory managment, important!
 | 
					 | 
				
			||||||
  oldOb.tryRelease()
 | 
					 | 
				
			||||||
  // update ALL watchers
 | 
					 | 
				
			||||||
  for (key in this._watchers) {
 | 
					 | 
				
			||||||
    this._watchers[key].update()
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  for (key in this._userWatchers) {
 | 
					 | 
				
			||||||
    this._userWatchers[key].update()
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					@ -134,6 +109,23 @@ exports._unproxy = function (key) {
 | 
				
			||||||
  delete this[key]
 | 
					  delete this[key]
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Force update on every watcher in scope.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					exports._digest = function () {
 | 
				
			||||||
 | 
					  var i = this._watcherList.length
 | 
				
			||||||
 | 
					  while (i--) {
 | 
				
			||||||
 | 
					    this._watcherList[i].update()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (this._children) {
 | 
				
			||||||
 | 
					    i = this._children.length
 | 
				
			||||||
 | 
					    while (i--) {
 | 
				
			||||||
 | 
					      this._children[i]._digest()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Setup computed properties. They are essentially
 | 
					 * Setup computed properties. They are essentially
 | 
				
			||||||
 * special getter/setters
 | 
					 * special getter/setters
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,8 +28,7 @@ var arrayAugmentations = Object.create(Array.prototype)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    var result = original.apply(this, args)
 | 
					    var result = original.apply(this, args)
 | 
				
			||||||
    var ob = this.__ob__
 | 
					    var ob = this.__ob__
 | 
				
			||||||
    var inserted, removed
 | 
					    var inserted
 | 
				
			||||||
 | 
					 | 
				
			||||||
    switch (method) {
 | 
					    switch (method) {
 | 
				
			||||||
      case 'push':
 | 
					      case 'push':
 | 
				
			||||||
        inserted = args
 | 
					        inserted = args
 | 
				
			||||||
| 
						 | 
					@ -37,28 +36,13 @@ var arrayAugmentations = Object.create(Array.prototype)
 | 
				
			||||||
      case 'unshift':
 | 
					      case 'unshift':
 | 
				
			||||||
        inserted = args
 | 
					        inserted = args
 | 
				
			||||||
        break
 | 
					        break
 | 
				
			||||||
      case 'pop':
 | 
					 | 
				
			||||||
        removed = [result]
 | 
					 | 
				
			||||||
        break
 | 
					 | 
				
			||||||
      case 'shift':
 | 
					 | 
				
			||||||
        removed = [result]
 | 
					 | 
				
			||||||
        break
 | 
					 | 
				
			||||||
      case 'splice':
 | 
					      case 'splice':
 | 
				
			||||||
        inserted = args.slice(2)
 | 
					        inserted = args.slice(2)
 | 
				
			||||||
        removed = result
 | 
					 | 
				
			||||||
        break
 | 
					        break
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // link/unlink added/removed elements
 | 
					 | 
				
			||||||
    if (inserted) ob.observeArray(inserted)
 | 
					    if (inserted) ob.observeArray(inserted)
 | 
				
			||||||
    if (removed) ob.unobserveArray(removed)
 | 
					    // notify change
 | 
				
			||||||
 | 
					    ob.binding.notify()
 | 
				
			||||||
    // notify bindings
 | 
					 | 
				
			||||||
    i = ob.bindings.length
 | 
					 | 
				
			||||||
    while (i--) {
 | 
					 | 
				
			||||||
      ob.bindings[i].notify()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    return result
 | 
					    return result
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  // define wrapped method
 | 
					  // define wrapped method
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,13 +27,12 @@ function Observer (value, type) {
 | 
				
			||||||
  this.id = ++uid
 | 
					  this.id = ++uid
 | 
				
			||||||
  this.value = value
 | 
					  this.value = value
 | 
				
			||||||
  this.type = type
 | 
					  this.type = type
 | 
				
			||||||
  this.parentCount = 0
 | 
					  this.active = true
 | 
				
			||||||
  this.vmCount = 0
 | 
					  this.binding = new Binding()
 | 
				
			||||||
  if (value) {
 | 
					  if (value) {
 | 
				
			||||||
    _.define(value, '__ob__', this)
 | 
					    _.define(value, '__ob__', this)
 | 
				
			||||||
    if (type === ARRAY) {
 | 
					    if (type === ARRAY) {
 | 
				
			||||||
      _.augment(value, arrayAugmentations)
 | 
					      _.augment(value, arrayAugmentations)
 | 
				
			||||||
      this.bindings = []
 | 
					 | 
				
			||||||
      this.observeArray(value)
 | 
					      this.observeArray(value)
 | 
				
			||||||
    } else if (type === OBJECT) {
 | 
					    } else if (type === OBJECT) {
 | 
				
			||||||
      _.augment(value, objectAugmentations)
 | 
					      _.augment(value, objectAugmentations)
 | 
				
			||||||
| 
						 | 
					@ -100,35 +99,13 @@ p.walk = function (obj) {
 | 
				
			||||||
 * and if value is array, link binding to the array.
 | 
					 * and if value is array, link binding to the array.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * @param {*} val
 | 
					 * @param {*} val
 | 
				
			||||||
 * @param {Binding} [binding]
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
p.observe = function (val, binding) {
 | 
					p.observe = function (val) {
 | 
				
			||||||
  var ob = Observer.create(val)
 | 
					  var ob = Observer.create(val)
 | 
				
			||||||
  if (ob) {
 | 
					  if (ob) {
 | 
				
			||||||
    ob.parentCount++
 | 
					    // ob.parentCount++
 | 
				
			||||||
    if (binding && ob.type === ARRAY) {
 | 
					    return ob.binding
 | 
				
			||||||
      ob.bindings.push(binding)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Unobserve a value.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @param {*} val
 | 
					 | 
				
			||||||
 * @param {Binding} [binding]
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
p.unobserve = function (val, binding) {
 | 
					 | 
				
			||||||
  var ob = val && val.__ob__
 | 
					 | 
				
			||||||
  if (ob) {
 | 
					 | 
				
			||||||
    ob.parentCount--
 | 
					 | 
				
			||||||
    if (binding && ob.type === ARRAY) {
 | 
					 | 
				
			||||||
      var i = ob.bindings.indexOf(binding)
 | 
					 | 
				
			||||||
      if (i > -1) ob.bindings.splice()
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    ob.tryRelease()
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -145,19 +122,6 @@ p.observeArray = function (items) {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Unobserve a list of Array items.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * @param {Array} items
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
p.unobserveArray = function (items) {
 | 
					 | 
				
			||||||
  var i = items.length
 | 
					 | 
				
			||||||
  while (i--) {
 | 
					 | 
				
			||||||
    this.unobserve(items[i])
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * Convert a property into getter/setter so we can emit
 | 
					 * Convert a property into getter/setter so we can emit
 | 
				
			||||||
 * the events when the property is accessed/changed.
 | 
					 * the events when the property is accessed/changed.
 | 
				
			||||||
| 
						 | 
					@ -168,58 +132,31 @@ p.unobserveArray = function (items) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
p.convert = function (key, val) {
 | 
					p.convert = function (key, val) {
 | 
				
			||||||
  var ob = this
 | 
					  var ob = this
 | 
				
			||||||
  var binding = new Binding()
 | 
					  var binding = ob.observe(val) || new Binding()
 | 
				
			||||||
  ob.observe(val, binding)
 | 
					 | 
				
			||||||
  Object.defineProperty(ob.value, key, {
 | 
					  Object.defineProperty(ob.value, key, {
 | 
				
			||||||
    enumerable: true,
 | 
					    enumerable: true,
 | 
				
			||||||
    configurable: true,
 | 
					    configurable: true,
 | 
				
			||||||
    get: function () {
 | 
					    get: function () {
 | 
				
			||||||
      // Observer.target is a watcher whose getter is
 | 
					      // Observer.target is a watcher whose getter is
 | 
				
			||||||
      // currently being evaluated.
 | 
					      // currently being evaluated.
 | 
				
			||||||
      if (Observer.target) {
 | 
					      if (ob.active && Observer.target) {
 | 
				
			||||||
        Observer.target.addDep(binding)
 | 
					        Observer.target.addDep(binding)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return val
 | 
					      return val
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    set: function (newVal) {
 | 
					    set: function (newVal) {
 | 
				
			||||||
      if (newVal === val) return
 | 
					      if (newVal === val) return
 | 
				
			||||||
      ob.unobserve(val, binding)
 | 
					 | 
				
			||||||
      ob.observe(newVal, binding)
 | 
					 | 
				
			||||||
      val = newVal
 | 
					      val = newVal
 | 
				
			||||||
 | 
					      var newBinding = ob.observe(newVal)
 | 
				
			||||||
 | 
					      if (newBinding) {
 | 
				
			||||||
 | 
					        // handle over binding
 | 
				
			||||||
 | 
					        newBinding.subs = binding.subs
 | 
				
			||||||
 | 
					        binding.subs = []
 | 
				
			||||||
 | 
					        binding = newBinding
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
      binding.notify()
 | 
					      binding.notify()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					 | 
				
			||||||
 * Attempt to teardown the observer if the value is no
 | 
					 | 
				
			||||||
 * longer needed. Two requirements have to be met:
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * 1. The observer has no parent obervers depending on it.
 | 
					 | 
				
			||||||
 * 2. The observer is not being used as the root $data by
 | 
					 | 
				
			||||||
 *    by a vm instance.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This is important because each observer holds strong
 | 
					 | 
				
			||||||
 * reference to all its parents and if we don't do this
 | 
					 | 
				
			||||||
 * those parents can be leaked when a vm is destroyed.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
p.tryRelease = function () {
 | 
					 | 
				
			||||||
  if (!this.parentCount && !this.vmCount) {
 | 
					 | 
				
			||||||
    var value = this.value
 | 
					 | 
				
			||||||
    if (_.isArray(value)) {
 | 
					 | 
				
			||||||
      value.__ob__.bindings = null
 | 
					 | 
				
			||||||
      this.unobserveArray(value)
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      for (var key in value) {
 | 
					 | 
				
			||||||
        var val = value[key]
 | 
					 | 
				
			||||||
        this.unobserve(val)
 | 
					 | 
				
			||||||
        // release closure
 | 
					 | 
				
			||||||
        _.define(value, key, val, true)
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    value.__ob__ = null
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
module.exports = Observer
 | 
					module.exports = Observer
 | 
				
			||||||
| 
						 | 
					@ -13,6 +13,7 @@ var objectAgumentations = Object.create(Object.prototype)
 | 
				
			||||||
function $add (key, val) {
 | 
					function $add (key, val) {
 | 
				
			||||||
  if (this.hasOwnProperty(key)) return
 | 
					  if (this.hasOwnProperty(key)) return
 | 
				
			||||||
  this.__ob__.convert(key, val)
 | 
					  this.__ob__.convert(key, val)
 | 
				
			||||||
 | 
					  this.__ob__.binding.notify()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
| 
						 | 
					@ -25,8 +26,8 @@ function $add (key, val) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function $delete (key) {
 | 
					function $delete (key) {
 | 
				
			||||||
  if (!this.hasOwnProperty(key)) return
 | 
					  if (!this.hasOwnProperty(key)) return
 | 
				
			||||||
  this.__ob__.unobserve(this[key])
 | 
					 | 
				
			||||||
  delete this[key]
 | 
					  delete this[key]
 | 
				
			||||||
 | 
					  this.__ob__.binding.notify()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if (_.hasProto) {
 | 
					if (_.hasProto) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,7 @@ var uid = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function Watcher (vm, expression, cb, filters, needSet) {
 | 
					function Watcher (vm, expression, cb, filters, needSet) {
 | 
				
			||||||
  this.vm = vm
 | 
					  this.vm = vm
 | 
				
			||||||
 | 
					  vm._watcherList.push(this)
 | 
				
			||||||
  this.expression = expression
 | 
					  this.expression = expression
 | 
				
			||||||
  this.cbs = [cb]
 | 
					  this.cbs = [cb]
 | 
				
			||||||
  this.id = ++uid // uid for batching
 | 
					  this.id = ++uid // uid for batching
 | 
				
			||||||
| 
						 | 
					@ -173,6 +174,8 @@ p.removeCb = function (cb) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
p.teardown = function () {
 | 
					p.teardown = function () {
 | 
				
			||||||
  if (this.active) {
 | 
					  if (this.active) {
 | 
				
			||||||
 | 
					    var list = this.vm._watcherList
 | 
				
			||||||
 | 
					    list.splice(list.indexOf(this))
 | 
				
			||||||
    for (var id in this.deps) {
 | 
					    for (var id in this.deps) {
 | 
				
			||||||
      this.deps[id].removeSub(this)
 | 
					      this.deps[id].removeSub(this)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -167,16 +167,12 @@ describe('Watcher', function () {
 | 
				
			||||||
    var oldData = vm.$data
 | 
					    var oldData = vm.$data
 | 
				
			||||||
    var watcher = new Watcher(vm, '$data', spy)
 | 
					    var watcher = new Watcher(vm, '$data', spy)
 | 
				
			||||||
    expect(watcher.value).toBe(oldData)
 | 
					    expect(watcher.value).toBe(oldData)
 | 
				
			||||||
    vm.a = 2
 | 
					    var newData = {}
 | 
				
			||||||
    nextTick(function () {
 | 
					    vm.$data = newData
 | 
				
			||||||
      expect(spy).toHaveBeenCalledWith(oldData, oldData)
 | 
					    nextTick(function() {
 | 
				
			||||||
      var newData = {}
 | 
					      expect(spy).toHaveBeenCalledWith(newData, oldData)
 | 
				
			||||||
      vm.$data = newData
 | 
					      expect(watcher.value).toBe(newData)
 | 
				
			||||||
      nextTick(function() {
 | 
					      done()
 | 
				
			||||||
        expect(spy).toHaveBeenCalledWith(newData, oldData)
 | 
					 | 
				
			||||||
        expect(watcher.value).toBe(newData)
 | 
					 | 
				
			||||||
        done()
 | 
					 | 
				
			||||||
      })
 | 
					 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  })
 | 
					  })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue