{"size": 7679, "ext": "js", "max_stars_repo_path": "modules/angular2/src/core/compiler/pipeline/compile_element.js", "max_stars_repo_name": "hrjason/changular", "max_stars_repo_stars_event_min_datetime": "2016-11-04T00:44:57.000Z", "max_stars_repo_stars_event_max_datetime": "2016-11-04T21:25:51.000Z", "max_issues_repo_path": "modules/angular2/src/core/compiler/pipeline/compile_element.js", "max_issues_repo_name": "hrjason/changular", "max_issues_repo_issues_event_min_datetime": null, "max_issues_repo_issues_event_max_datetime": null, "max_forks_repo_path": "modules/angular2/src/core/compiler/pipeline/compile_element.js", "max_forks_repo_name": "hrjason/changular", "max_forks_repo_forks_event_min_datetime": null, "max_forks_repo_forks_event_max_datetime": null}
{"max_stars_count": 2, "max_issues_count": null, "max_forks_count": null, "avg_line_length": 34.9045454545, "max_line_length": 106, "alphanum_fraction": 0.7032165647}
import {List, Map, ListWrapper, MapWrapper} from 'changular2/src/facade/collection';
import {DOM} from 'changular2/src/dom/dom_adapter';
import {int, isBlank, isPresent, Type, StringJoiner, assertionsEnabled} from 'changular2/src/facade/lang';
import {DirectiveMetadata} from '../directive_metadata';
import {Decorator, Component, Viewport, DynamicComponent} from '../../annotations/annotations';
import {ElementBinder} from '../element_binder';
import {ProtoElementInjector} from '../element_injector';
import * as viewModule from '../view';
import {dashCaseToCamelCase} from './util';

import {AST} from 'changular2/change_detection';

/**
 * Collects all data that is needed to process an element
 * in the compile process. Fields are filled
 * by the CompileSteps starting out with the pure HTMLElement.
 */
export class CompileElement {
  element;
  _attrs:Map;
  _classList:List;
  textNodeBindings:Map;
  propertyBindings:Map;
  eventBindings:Map;

  /// Store directive name to template name mapping.
  /// Directive name is what the directive exports the variable as
  /// Template name is how it is reffered to it in template
  variableBindings:Map;
  decoratorDirectives:List<DirectiveMetadata>;
  viewportDirective:DirectiveMetadata;
  componentDirective:DirectiveMetadata;
  hasNestedView:boolean;
  _allDirectives:List<DirectiveMetadata>;
  isViewRoot:boolean;
  hasBindings:boolean;
  inheritedProtoView:viewModule.ProtoView;
  inheritedProtoElementInjector:ProtoElementInjector;
  inheritedElementBinder:ElementBinder;
  distanceToParentInjector:int;
  distanceToParentBinder:int;
  compileChildren: boolean;
  ignoreBindings: boolean;
  elementDescription: string; // e.g. '<div [class]="foo">' : used to provide context in case of error
  contentTagSelector: string;

  constructor(element, compilationUnit = '') {
    this.element = element;
    this._attrs = null;
    this._classList = null;
    this.textNodeBindings = null;
    this.propertyBindings = null;
    this.eventBindings = null;
    this.variableBindings = null;
    this.decoratorDirectives = null;
    this.viewportDirective = null;
    this.componentDirective = null;
    this.hasNestedView = false;
    this._allDirectives = null;
    this.isViewRoot = false;
    this.hasBindings = false;
    // inherited down to children if they don't have
    // an own protoView
    this.inheritedProtoView = null;
    // inherited down to children if they don't have
    // an own protoElementInjector
    this.inheritedProtoElementInjector = null;
    // inherited down to children if they don't have
    // an own elementBinder
    this.inheritedElementBinder = null;
    this.distanceToParentInjector = 0;
    this.distanceToParentBinder = 0;
    this.compileChildren = true;
    // set to true to ignore all the bindings on the element
    this.ignoreBindings = false;
    this.contentTagSelector = null;
    // description is calculated here as compilation steps may change the element
    var tplDesc = assertionsEnabled()? getElementDescription(element) : null;
    if (compilationUnit !== '') {
      this.elementDescription = compilationUnit;
      if (isPresent(tplDesc)) this.elementDescription += ": " + tplDesc;
    } else {
      this.elementDescription = tplDesc;
    }
  }

  refreshAttrs() {
    this._attrs = null;
  }

  attrs():Map<string,string> {
    if (isBlank(this._attrs)) {
      this._attrs = DOM.attributeMap(this.element);
    }
    return this._attrs;
  }

  refreshClassList() {
    this._classList = null;
  }

  classList():List<string> {
    if (isBlank(this._classList)) {
      this._classList = ListWrapper.create();
      var elClassList = DOM.classList(this.element);
      for (var i = 0; i < elClassList.length; i++) {
        ListWrapper.push(this._classList, elClassList[i]);
      }
    }
    return this._classList;
  }

  addTextNodeBinding(indexInParent:int, expression:AST) {
    if (isBlank(this.textNodeBindings)) {
      this.textNodeBindings = MapWrapper.create();
    }
    MapWrapper.set(this.textNodeBindings, indexInParent, expression);
  }

  addPropertyBinding(property:string, expression:AST) {
    if (isBlank(this.propertyBindings)) {
      this.propertyBindings = MapWrapper.create();
    }
    MapWrapper.set(this.propertyBindings, dashCaseToCamelCase(property), expression);
  }

  addVariableBinding(variableName:string, variableValue:string) {
    if (isBlank(this.variableBindings)) {
      this.variableBindings = MapWrapper.create();
    }

    // Store the variable map from value to variable, reflecting how it will be used later by
    // View. When a local is set to the view, a lookup for the variable name will take place keyed
    // by the "value", or exported identifier. For example, chang-repeat sets a view local of "index".
    // When this occurs, a lookup keyed by "index" must occur to find if there is a var referencing
    // it.
    MapWrapper.set(this.variableBindings, variableValue, dashCaseToCamelCase(variableName));
  }

  addEventBinding(eventName:string, expression:AST) {
    if (isBlank(this.eventBindings)) {
      this.eventBindings = MapWrapper.create();
    }
    MapWrapper.set(this.eventBindings, eventName, expression);
  }

  addDirective(directive:DirectiveMetadata) {
    var annotation = directive.annotation;
    this._allDirectives = null;
    if (annotation instanceof Decorator) {
      if (isBlank(this.decoratorDirectives)) {
        this.decoratorDirectives = ListWrapper.create();
      }
      ListWrapper.push(this.decoratorDirectives, directive);
      if (!annotation.compileChildren) {
        this.compileChildren = false;
      }
    } else if (annotation instanceof Viewport) {
      this.viewportDirective = directive;
    } else if (annotation instanceof Component) {
      this.componentDirective = directive;
      this.hasNestedView = true;
    } else if (annotation instanceof DynamicComponent) {
      this.componentDirective = directive;
    }
  }

  getAllDirectives(): List<DirectiveMetadata> {
    if (this._allDirectives === null) {
      // Collect all the directives
      // When present the component directive must be first
      var directives = ListWrapper.create();
      if (isPresent(this.componentDirective)) {
        ListWrapper.push(directives, this.componentDirective);
      }
      if (isPresent(this.viewportDirective)) {
        ListWrapper.push(directives, this.viewportDirective);
      }
      if (isPresent(this.decoratorDirectives)) {
        directives = ListWrapper.concat(directives, this.decoratorDirectives);
      }
      this._allDirectives = directives;
    }
    return this._allDirectives;
  }
}

// return an HTML representation of an element start tag - without its content
// this is used to give contextual information in case of errors
function getElementDescription(domElement):string {
  var buf = new StringJoiner();
  var atts = DOM.attributeMap(domElement);

  buf.add("<");
  buf.add(DOM.tagName(domElement).toLowerCase());

  // show id and class first to ease element identification
  addDescriptionAttribute(buf, "id", MapWrapper.get(atts, "id"));
  addDescriptionAttribute(buf, "class", MapWrapper.get(atts, "class"));
  MapWrapper.forEach(atts, (attValue, attName) => {
      if (attName !== "id" && attName !== "class") {
          addDescriptionAttribute(buf, attName, attValue);
      }
  });

  buf.add(">");
  return buf.toString();
}


function addDescriptionAttribute(buffer:StringJoiner, attName:string, attValue) {
  if (isPresent(attValue)) {
      if (attValue.length === 0) {
          buffer.add(' ' + attName);
      } else {
          buffer.add(' ' + attName + '="' + attValue + '"');
      }
  }
}
