1/*
2 * Copyright (c) 2020 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15import { ObserverStack, canObserve } from './utils';
16
17/**
18 * Observer constructor
19 * @param {ViewModel} context execute context of callback
20 * @param {Function} getter getter function
21 * @param {Function} callback callback function
22 * @param {Object} meta meta data that Observer object don't care about
23 */
24export function Observer(context, getter, callback, meta) {
25  this._ctx = context;
26  this._getter = getter;
27  this._fn = callback;
28  this._meta = meta;
29  this._lastValue = this._get();
30}
31
32Observer.prototype._get = function() {
33  try {
34    ObserverStack.push(this);
35    return this._getter.call(this._ctx);
36  } finally {
37    ObserverStack.pop();
38  }
39};
40
41Observer.prototype.update = function() {
42  const lastValue = this._lastValue;
43  const nextValue = this._get();
44  const context = this._ctx;
45  const meta = this._meta;
46
47  if (nextValue !== lastValue || canObserve(nextValue)) {
48    this._fn.call(context, nextValue, lastValue, meta);
49    this._lastValue = nextValue;
50  }
51};
52
53Observer.prototype.subscribe = function(subject, key) {
54  const detach = subject.attach(key, this);
55  if (typeof detach !== 'function') {
56    return void 0;
57  }
58  if (!this._detaches) {
59    this._detaches = new Set();
60  }
61  this._detaches.add(detach);
62  return void 1;
63};
64
65Observer.prototype.unsubscribe = function() {
66  const detaches = this._detaches;
67  if (!detaches) {
68    return void 0;
69  }
70  detaches.forEach( detach => {
71    detach();
72  });
73  this._detaches.clear();
74  return void 1;
75};
76