<commit_before>// Copyright (c) 2015, the uix project authors. Please see the AUTHORS file for
// details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

library uix.src.component;

import 'dart:async';
import 'dart:html' as html;
import 'data/node.dart';
import 'vcontext.dart';
import 'vnode.dart';
import 'env.dart';

abstract class Component<P> extends RevisionedNode with StreamListenerNode implements VContext {
  static const dirtyFlag = 1;
  static const attachedFlag = 1 << 1;
  static const shouldUpdateViewFlags = dirtyFlag | attachedFlag;

  String get tag => 'div';

  int flags = dirtyFlag;
  int depth = 0;
  Component _parent;
  P data;
  html.Element element;
  VNode _root;

  Component get parent => _parent;
  set parent(Component c) {
    _parent = c;
    depth = c.depth + 1;
  }

  VNode get root => _root;
  set root(VNode n) {
    if (n != null) {
      if (_root == null) {
        n.ref = element;
        n.cref = this;
        n.render(this);
      } else {
        _root.update(n, this);
      }
      _root = n;
    }
  }

  List<VNode> get children => null;
  set children(List<VNode> c) {}

  bool get isDirty => (flags & dirtyFlag) != 0;
  bool get isAttached => (flags & attachedFlag) != 0;

  Component() {
    element = html.document.createElement(tag);
    init();
  }

  void init() {}

  void update([_]) {
    if ((flags & shouldUpdateViewFlags) == shouldUpdateViewFlags) {
      if (updateState()) {
        updateView();
      }
      updateRev();
      flags &= ~dirtyFlag;
    }
  }

  bool updateState() => true;
  void updateView() { root = build(); }

  VNode build() => null;

  void invalidate([_]) {
    if ((flags & dirtyFlag) == 0) {
      flags |= dirtyFlag;

      if (!scheduler.isRunning) {
        if (identical(Zone.current, scheduler.zone)) {
          scheduler.nextFrame.write(depth).then(update);
        } else {
          scheduler.zone.run(() {
            scheduler.nextFrame.write(depth).then(update);
          });
        }
      }

      resetTransientSubscriptions();
      invalidated();
    }
  }

  void invalidated() {}

  void attached() {}
  void detached() {}
  void disposed() {}

  void dispose() {
    if (_root != null) {
      _root.dispose();
    }
    flags &= ~attachedFlag;
    detached();
    resetTransientSubscriptions();
    resetSubscriptions();
    disposed();
  }

  void attach() {
    assert(!isAttached);
    flags |= attachedFlag;
    attached();
    if (_root != null) {
      _root.attach();
    }
  }

  void detach() {
    assert(isAttached);
    if (_root != null) {
      _root.detached();<commit_msg>Don't call `detached()` when Component is disposed and isn't attached<commit_after>// Copyright (c) 2015, the uix project authors. Please see the AUTHORS file for
// details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

library uix.src.component;

import 'dart:async';
import 'dart:html' as html;
import 'data/node.dart';
import 'vcontext.dart';
import 'vnode.dart';
import 'env.dart';

abstract class Component<P> extends RevisionedNode with StreamListenerNode implements VContext {
  static const dirtyFlag = 1;
  static const attachedFlag = 1 << 1;
  static const shouldUpdateViewFlags = dirtyFlag | attachedFlag;

  String get tag => 'div';

  int flags = dirtyFlag;
  int depth = 0;
  Component _parent;
  P data;
  html.Element element;
  VNode _root;

  Component get parent => _parent;
  set parent(Component c) {
    _parent = c;
    depth = c.depth + 1;
  }

  VNode get root => _root;
  set root(VNode n) {
    if (n != null) {
      if (_root == null) {
        n.ref = element;
        n.cref = this;
        n.render(this);
      } else {
        _root.update(n, this);
      }
      _root = n;
    }
  }

  List<VNode> get children => null;
  set children(List<VNode> c) {}

  bool get isDirty => (flags & dirtyFlag) != 0;
  bool get isAttached => (flags & attachedFlag) != 0;

  Component() {
    element = html.document.createElement(tag);
    init();
  }

  void init() {}

  void update([_]) {
    if ((flags & shouldUpdateViewFlags) == shouldUpdateViewFlags) {
      if (updateState()) {
        updateView();
      }
      updateRev();
      flags &= ~dirtyFlag;
    }
  }

  bool updateState() => true;
  void updateView() { root = build(); }

  VNode build() => null;

  void invalidate([_]) {
    if ((flags & dirtyFlag) == 0) {
      flags |= dirtyFlag;

      if (!scheduler.isRunning) {
        if (identical(Zone.current, scheduler.zone)) {
          scheduler.nextFrame.write(depth).then(update);
        } else {
          scheduler.zone.run(() {
            scheduler.nextFrame.write(depth).then(update);
          });
        }
      }

      resetTransientSubscriptions();
      invalidated();
    }
  }

  void invalidated() {}

  void attached() {}
  void detached() {}
  void disposed() {}

  void dispose() {
    if (_root != null) {
      _root.dispose();
    }
    if (isAttached) {
      flags &= ~attachedFlag;
      detached();
    }
    resetTransientSubscriptions();
    resetSubscriptions();
    disposed();
  }

  void attach() {
    assert(!isAttached);
    flags |= attachedFlag;
    attached();
    if (_root != null) {
      _root.attach();
    }
  }

  void detach() {
    assert(isAttached);
    if (_root != null) {
      _root.detached();
{'fraction_non_alphanumeric': 0.13609684519442405, 'fraction_numerical': 0.004402054292002935, 'mean_word_length': 2.5271668822768434, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4474755', 'n_tokens_mistral': 1781, 'n_tokens_neox': 1710, 'n_words': 474}