text:
import {
  ControlCenter, VersionedObject,
  areEquals,
  Aspect, Result, traverseAllScope,
} from './core';
import * as DataSourceScope from './datasource.scope';
import { AttributeTypes as V, PathReporter, Reporter } from '@openmicrostep/msbuildsystem.shared';

export namespace DataSourceInternal {
  export import Scope = DataSourceScope.Scope;
  export import ResolvedScope = DataSourceScope.ResolvedScope;
  export import ResolvedSort = DataSourceScope.ResolvedSort;
  export import parseScope = DataSourceScope.parseScope;
  export import parseScopeExtension = DataSourceScope.parseScopeExtension;
  export import traverseScope = DataSourceScope.traverseScope;

  type VarDep = Map<ObjectSet, Set<ObjectSet>>;
  type Solution<T> = {
    partial: Set<T>,
    full: Set<T> | undefined,
  };
  export type Mapper<T> = {
    aspect(object: T): Aspect.Installed;
    has(object: T, attribute: Aspect.InstalledAttribute): boolean;
    get(object: T, attribute: Aspect.InstalledAttribute): any;
    todb(object: T, attribute: Aspect.InstalledAttribute, value): any;
    sort(a, b, attribute: Aspect.InstalledAttribute): number;
  };
  export const versionedObjectMapper: Mapper<VersionedObject> = {
    aspect(vo: VersionedObject) { return vo.manager().aspect(); },
    has(vo: VersionedObject, attribute: Aspect.InstalledAttribute) { return vo.manager().hasAttributeValueFast(attribute); },
    get(vo: VersionedObject, attribute:  Aspect.InstalledAttribute) { return vo.manager().attributeValueFast(attribute); },
    todb(vo: VersionedObject, attribute: Aspect.InstalledAttribute, value) { return value; },
    sort(a, b, attribute: Aspect.InstalledAttribute) {
      if (attribute.isMonoVersionedObjectValue()) {
        a = a.id();
        b = b.id();
      }
      return a === b ? 0 : (a < b ? -1 : +1 );
    },
  };
  export class FilterContext<T> {
    _resolution = new Map<ObjectSet, Solution<T>>();
    constructor(public objects: T[] = [], public mapper: Mapper<T>) {}

    solution(set: ObjectSet) {
      let s = this._resolution.get(set)!;
      if (!s) {
        s = {
          partial: this.solvePartial(set),
          full: undefined,
        };
        this._resolution.set(set, s);
      }
      return s;
    }

    private addDep(deps: VarDep, set: ObjectSet, dep: ObjectSet) {
      let d = deps.get(set);
      if (!d)
        deps.set(set, d = new Set());
      d.add(dep);
    }

    private buildDeps(set: ObjectSet, constraint: Constraint, prefix: string, deps: VarDep) {
      if (constraint instanceof ConstraintTree) {
        constraint.value.forEach(c => this.buildDeps(set, c, prefix + constraint.prefix, deps));
      }
      else if (constraint instanceof ConstraintVariable) {
        let lset = set.variable(prefix + constraint.leftVariable)!;
        let rset = set.variable(prefix + constraint.rightVariable)!;
        this.addDep(deps, lset, rset);
        this.addDep(deps, rset, lset);
      }
    }

    private buildOrder(set: ObjectSet) : ObjectSet[] {
      let deps = new Map<ObjectSet, Set<ObjectSet>>();
      for (let c of set.constraints)
        this.buildDeps(set, c, "", deps);
      let order: ObjectSet[] = [];
      traverse(set);

      function traverse(set: ObjectSet) {
        if (order.indexOf(set) === -1) {
          order.push(set);
          for (let dep of deps.get(set)!)
            traverse(dep);
        }
      }

      return order;
    }

    private pass(set: ObjectSet, constraint: Constraint, prefix: string, object: T) : boolean {
      let ok = true;
      if (constraint instanceof ConstraintTree) {
        switch (constraint.type) {
          case ConstraintType.And: ok = constraint.value.every(c => this.pass(set, c, prefix + constraint.prefix, object)); break;
          case ConstraintType.Or : ok = constraint.value.some (c => this.pass(set, c, prefix + constraint.prefix, object)); break;
        }
      }
      else if (constraint instanceof ConstraintValue) {
        if (set.variable(prefix + constraint.leftVariable) === set) {
          ok = this.mapper.has(object, constraint.leftAttribute) &&
              pass_value(constraint.type, this.mapper.get(object, constraint.leftAttribute), this.mapper.todb(object, constraint.leftAttribute, constraint.value));
        }
      }
      return ok;
    }

    private solvePartial(set: ObjectSet) : Set<T> {
      let ret = new Set<T>();
      for (let object of this.objects) {
        let ok = false;
        for (let c of set.typeConstraints) {
          switch (c.type) {
            case ConstraintType.InstanceOf:
              ok = this.mapper.aspect(object).classname === c.value.classname; // TODO: real instanceof
              break;
            case ConstraintType.Is:
              ok = this.mapper.aspect(object).classname === c.value.classname;
              break;
            case ConstraintType.Union:
              ok = [...c.value].some(s => this.solveFull(s).has(object));
              break;
            case ConstraintType.UnionForAlln: {
              let u_0 = c.value[0];
              let s = this._resolution.get(u_0);
              if (!s) {
                let u_n = c.value[1];
                let u_np1 = c.value[2];
                let sol_u_0 = this.solveFull(u_0);
                let sol_u_n = new Set(sol_u_0);
                let sol_u_np1: Set<T>;
                let size = 0;
                s = { partial: sol_u_n, full: sol_u_n };
                this._resolution.set(u_n, s);
                let resolution = new Map(this._resolution);
                while (size < sol_u_0.size) {
                  // Iterate
                  size = sol_u_0.size;
                  sol_u_np1 = this.solveFull(u_np1);
                  for (let o of sol_u_np1)
                    sol_u_0.add(o);

                  // Prepare the next iteration
                  this._resolution = resolution;
                  s.partial = sol_u_np1;
                  s.full = sol_u_np1;
                }
                this._resolution.delete(u_n);
                // Write the final solution
                s.partial = sol_u_0;
                s.full = sol_u_0;
              }
              ok = s.full!.has(object);
            } break;
            case ConstraintType.Recursion: {
              ok = this.solveFull(c.value).has(object);
            } break;
          }
          if (!ok)
            break;
        }
        ok = ok && set.constraints.every(c => this.pass(set, c, "", object));
        if (ok)
          ret.add(object);
      }

      return ret;
    }

    private passVars(set: ObjectSet, set1: ObjectSet, o1: T, set2: ObjectSet, o2: T, constraint: Constraint, prefix: string) : boolean {
      let ok = true;
      if (constraint instanceof ConstraintTree) {
        switch (constraint.type) {
          case ConstraintType.And: ok = constraint.value.every(c => this.passVars(set, set1, o1, set2, o2, c, prefix + constraint.prefix)); break;
          case ConstraintType.Or : ok = constraint.value.some (c => this.passVars(set, set1, o1, set2, o2, c, prefix + constraint.prefix)); break;
        }
      }
      else if (constraint instanceof ConstraintVariable) {
        let lset = set.variable(prefix + constraint.leftVariable)!;
        let rset = set.variable(prefix + constraint.rightVariable)!;
        if ((lset === set1 || lset === set2) && (rset === set1 || rset === set2)) {
          let lo = (lset === set1 ? o1 : o2);
          let ro = (rset === set1 ? o1 : o2);
          ok = this.mapper.has(lo, constraint.leftAttribute) && this.mapper.has(ro, constraint.rightAttribute)
            && pass_value(constraint.type, this.mapper.get(lo, constraint.leftAttribute), this.mapper.get(ro, constraint.rightAttribute));
        }
      }
      else if (constraint instanceof ConstraintValue) {
        let lset = set.variable(prefix + constraint.leftVariable)!;
        if (lset === set2) {
          ok = this.mapper.has(o2, constraint.leftAttribute) &&
              pass_value(constraint.type, this.mapper.get(o2, constraint.leftAttribute), this.mapper.todb(o2, constraint.leftAttribute, constraint.value));
        }
      }
      return ok;
    }

    solveFull(set: ObjectSet) : Set<T> {
      let solution = this.solution(set);
      if (!solution.full) {
        if (set.variables) {
          let order = this.buildOrder(set);
          for (let i = order.length - 1; i > 0; i--) {
            let set1 = order[i    ];
            let set2 = order[i - 1];
            let objs1 = this.solution(set1).partial;
            let objs2 = this.solution(set2).partial;
            for (let o2 of objs2) {
              let ok2 = false;
              for (let o1 of objs1) {
                let ok1 = set.constraints.every(c => this.passVars(set, set1, o1, set2, o2, c, ""));
                ok2 = ok2 || ok1;
              }
              if (!ok2)
                objs2.delete(o2);
            }
          }
        }
        solution.full = solution.partial;
      }
      return solution.full;
    }

    valueAtPath(o: T, path: Aspect.InstalledAttribute[]) {
      let v = o;
      for (let a of path) {
        v = this.mapper.get(v, a);
      }
      return v;
    }

    solveSorted(set: ObjectSet) : T[] {
      let full = this.solveFull(set);
      if (!set.sort)
        return [...full];

      return [...full].sort((a, b) => {
        let r = 0;
        for (let s of set.sort!) {
          let va = this.valueAtPath(a, s.path);
          let vb = this.valueAtPath(b, s.path);
          r = this.mapper.sort(va, vb, s.path[s.path.length - 1]);
          if (r !== 0)
            return s.asc ? +r : -r;
        }
        return r;
      });
    }

  };

  export enum ConstraintType {
    // a operator b
    BEGIN_a_op_b = 0,
    Equal = 0,
    NotEqual = 1,
    GreaterThan = 2,
    GreaterThanOrEqual = 3,
    LessThan = 4,
    LessThanOrEqual = 5,
    END_a_op_b = 9,

    // A operator b
    BEGIN_A_op_b = 10,
    Contains = 10,
    NotContains = 11,
    END_A_op_b = 19,

    // a operator B
    BEGIN_a_op_B = 20,
    In = 21,
    NotIn = 22,
    END_a_op_B = 29,

    // A operator B
    BEGIN_A_op_B = 30,
    Intersects = 30,
    NotIntersects = 31,
    SubSet = 32,
    NotSubSet = 33,
    SuperSet = 34,
    NotSuperSet = 35,
    SameSet = 36,
    NotSameSet = 37,
    END_A_op_B = 39,

    // a operator value
    Text,
    Exists,

    // set operators
    SubIn,
    SubNotIn,
    InstanceOf,
    Is,
    Union,
    UnionForAlln,
    Recursion,
    Or,
    And,
    CustomStart = 100 // The first 100 ([0-99]) are reserved
  }

  export type Value = string | number | boolean | Date | undefined;
  export type ValueSet = Value[] | Set<Value>;
  export type AnyValue = Value | ValueSet;
  export type ConstraintBetweenValueAndValue =
    ConstraintType.Equal |
    ConstraintType.NotEqual |
    ConstraintType.GreaterThan |
    ConstraintType.GreaterThanOrEqual |
    ConstraintType.LessThan |
    ConstraintType.LessThanOrEqual;
  export type ConstraintBetweenValueSetAndValue =
    ConstraintType.In |
    ConstraintType.NotIn;
  export type ConstraintBetweenValueAndValueSet =
    ConstraintType.Contains |
    ConstraintType.NotContains;
  export type ConstraintBetweenValueSetAndValueSet =
    ConstraintType.Intersects |
    ConstraintType.NotIntersects |
    ConstraintType.SubSet |
    ConstraintType.NotSubSet |
    ConstraintType.SuperSet |
    ConstraintType.NotSuperSet |
    ConstraintType.SameSet |
    ConstraintType.NotSameSet;
  export type ConstraintBetweenAnyValueAndAnyValue =
    ConstraintBetweenValueAndValue |
    ConstraintBetweenValueSetAndValue |
    ConstraintBetweenValueAndValueSet |
    ConstraintBetweenValueSetAndValueSet;
  export type ConstraintBetweenAnyValueAndFixedValue =
    ConstraintBetweenAnyValueAndAnyValue |
    ConstraintType.Text |
    ConstraintType.Exists;

  export type ConstraintOnType =
     { type: ConstraintType.InstanceOf | ConstraintType.Is, value: Aspect.Installed } |
     { type: ConstraintType.Union, value: Set<ObjectSet> } |
     { type: ConstraintType.UnionForAlln, value: [ObjectSet, ObjectSet, ObjectSet] } |
     { type: ConstraintType.Recursion, value: ObjectSet };

  export class ConstraintTree {
    constructor(
      public type: ConstraintType.Or | ConstraintType.And,
      public prefix: string, // for fast variable prefixing
      public value: Constraint[]) {}
  }

  export class ConstraintValue {
    constructor(
      public type: ConstraintBetweenAnyValueAndFixedValue,
      public leftVariable: string,
      public leftAttribute: Aspect.InstalledAttribute,
      public value: any) {}
  }

  export class ConstraintVariable {
    constructor(
      public type: ConstraintBetweenAnyValueAndAnyValue,
      public leftVariable: string,
      public leftAttribute: Aspect.InstalledAttribute,
      public rightVariable: string,
      public rightAttribute: Aspect.InstalledAttribute) {}
  }

  export class ConstraintSub {
    constructor(
      public type: ConstraintType.SubIn | ConstraintType.SubNotIn,
      public attribute: Aspect.InstalledAttribute,
      public sub: string) {}
  }

  export type Constraint = ConstraintTree | ConstraintValue | ConstraintVariable | ConstraintSub;

  function hasRSet(var_set: ObjectSet, r_set: ObjectSet) {
    let has = false;
    for (let c of var_set.typeConstraints) {
      if (c.type === ConstraintType.Union) {
        for (let s of c.value) {
          has = hasRSet(s, r_set);
          if (has) break;
        }
      }
      else if (c.type === ConstraintType.UnionForAlln) {
        has = hasRSet(c.value[0], r_set) || hasRSet(c.value[2], r_set);
      }
      else if (c.type === ConstraintType.Recursion) {
        has = c.value === r_set;
      }
      if (has) break;
    }
    return has;
  }

  function fillVariableAttribute(set: ObjectSet, r_set: ObjectSet | undefined, attributes: Map<string, Aspect.InstalledAttribute>, variable: string, attribute: Aspect.InstalledAttribute) {
    let var_set = set.variable(variable);
    if (var_set === set || (var_set && r_set && hasRSet(var_set, r_set))) {
      attributes.set(attribute.name, attribute);
    }
    return false;
  }
  function fill_attributes(set: ObjectSet, r_set: ObjectSet | undefined, attributes: Map<string, Aspect.InstalledAttribute>, constraint: Constraint, prefix: string): boolean {
    if (constraint instanceof ConstraintTree) {
      for (let c of constraint.value)
        fill_attributes(set, r_set, attributes, c, prefix + constraint.prefix);
    }
    else if (constraint instanceof ConstraintValue) {
      fillVariableAttribute(set, r_set, attributes, prefix + constraint.leftVariable, constraint.leftAttribute);
    }
    else if (constraint instanceof ConstraintVariable) {
      fillVariableAttribute(set, r_set, attributes, prefix + constraint.leftVariable , constraint.leftAttribute );
      fillVariableAttribute(set, r_set, attributes, prefix + constraint.rightVariable, constraint.rightAttribute);
    }
    else if (constraint instanceof ConstraintSub) {
      attributes.set(constraint.attribute.name, constraint.attribute);
    }
    return false;
  }
  function hasAllAttributes(aspect: Aspect.Installed, attributes: Map<string, Aspect.InstalledAttribute>) {
    for (let [name, attr] of attributes) {
      let f_attr = aspect.attributes.get(name);
      if (
        (!attr.isVirtualValue()) &&
        (!f_attr || !Aspect.Type.areComparable(f_attr.type, attr.type)) &&
        (name !== "_id" || !Aspect.Type.areComparable(aspect.attribute_ref.type, attr.type))
      )
       return false;
    }
    return true;
  }
  function _possibleAspectsAndRequiredAttributes(cc: ControlCenter, set: ObjectSet, r_set: ObjectSet | undefined,
    aspects = new Set<Aspect.Installed>(),
    attributes: Map<string, Aspect.InstalledAttribute> | undefined
  ) {
    for (let c of set.typeConstraints) {
      if (c.type === ConstraintType.InstanceOf || c.type === ConstraintType.Is) {
        aspects.add(c.value);
      }
      else if (c.type === ConstraintType.Union) {
        for (let s of c.value) {
          _possibleAspectsAndRequiredAttributes(cc, s, undefined, aspects, undefined);
        }
      }
      else if (c.type === ConstraintType.UnionForAlln) {
        _possibleAspectsAndRequiredAttributes(cc, c.value[0], undefined, aspects, attributes);
        _possibleAspectsAndRequiredAttributes(cc, c.value[2], c.value[1], aspects, attributes);
      }
    }
    if (attributes) for (let c of set.constraints)
      fill_attributes(set, r_set, attributes, c, '');
  }

  export class ObjectSet {
    _name: string;
    typeConstraints: ConstraintOnType[] = [];
    name?: string = undefined;
    scope: ResolvedScope = ResolvedScope.EMPTY;
    sort?: ResolvedSort = undefined;
    constraints: Constraint[] = [];
    variables?: Map<string, ObjectSet> = undefined;
    subs?: Map<string, ObjectSet> = undefined;

    clone(name: string) {
      let ret = new ObjectSet(name);
      ret.typeConstraints = this.typeConstraints.slice(0);
      ret.constraints = this.constraints.slice(0);
      if (this.variables) {
        for (let [name, variable] of this.variables)
          ret.setVariable(name, variable === this ? ret : variable);
      }
      if (ret.constraints.length)
        ret.setVariable(this._name, ret);
      if (this.subs)
        ret.subs = new Map(this.subs);
      return ret;
    }

    hasVariable(name: string) : boolean {
      return !!(name === this._name || (this.variables && this.variables.has(name)) || (this.subs && this.subs.has(name)));
    }
    setVariable(name: string, set: ObjectSet) {
      if (name === this._name) {
        if (set !== this)
          throw new Error(`variable ${name} is already used`);
        return;
      }

      if (!this.variables)
        this.variables = new Map();
      else if (this.variables.get(name) === set)
        return;
      if (this.hasVariable(name))
        throw new Error(`variable ${name} is already used`);
      this.variables.set(name, set);
    }

    variable(name: string): ObjectSet | undefined {
      if (name === this._name)
        return this;
      return this.variables && this.variables.get(name);
    }
    sub(sub: ObjectSet) : string {
      if (!this.subs)
        this.subs = new Map();
      let name = sub._name;
      if (this.hasVariable(name))
        throw new Error(`variable ${name} is already used`);
      this.subs.set(name, sub);
      return name;
    }

    protected canAddType(c_with: ConstraintOnType) : boolean | undefined {
      for (let c_self of this.typeConstraints) {
        if (c_self.type === c_with.type) {
          if (areEquals(c_self.value, c_with.value))
            return undefined;
          return false;
        }
      }
      return true;
    }

    addType(c_with: ConstraintOnType) {
      if (this.canAddType(c_with) === true)
        this.typeConstraints.push(c_with);
    }

    tryToMerge(other: ObjectSet) : boolean {
      let r: ConstraintOnType[] = [];
      let can: boolean | undefined = true;
      for (let c_with of other.typeConstraints) {
        can = this.canAddType(c_with);
        if (can === false)
          break;
        if (can === true)
          r.push(c_with);
      }
      if (can !== false) {
        this.typeConstraints.push(...r);
        let prefix = "";
        if (other.variables) {
          prefix = `${other._name}.`;
          for (let [k, s] of other.variables.entries())
            this.setVariable(prefix + k, s === other ? this : s);
        }
        if (other.constraints.length) {
          this.setVariable(prefix + other._name, this);
          this.and(c_and(other.constraints, prefix));
        }
        return true;
      }
      return false;
    }

    and(constraint?: Constraint) : void {
      if (constraint)
        this.constraints.push(constraint);
    }

    constraint() {
      return c_and(this.constraints);
    }

    attributesAndCompatibleAspects(cc: ControlCenter) {
      let aspects = new Set<Aspect.Installed>();
      let attributes = new Map<string, Aspect.InstalledAttribute>();
      _possibleAspectsAndRequiredAttributes(cc, this, undefined, aspects, attributes);
      if (aspects.size === 0) {
        for (let aspect of cc.installedAspects()) {
          if (hasAllAttributes(aspect, attributes))
            aspects.add(aspect);
        }
      }
      else {
        for (let aspect of aspects) {
          if (!hasAllAttributes(aspect, attributes))
            aspects.delete(aspect);
        }
      }
      return {
        compatibleAspects: aspects,
        attributes: attributes,
      };
    }

    constructor(name: string) {
      this._name = name;
    }
  }

  function c_or (constraints: Constraint[] = [], prefix = "") { return constraints.length === 1 && !prefix ? constraints[0] : new ConstraintTree(ConstraintType.Or , prefix, constraints); }
  function c_and(constraints: Constraint[] = [], prefix = "") { return constraints.length === 1 && !prefix ? constraints[0] : new ConstraintTree(ConstraintType.And, prefix, constraints); }
  function c_value(type: ConstraintBetweenAnyValueAndFixedValue, leftVariable: string, leftAttribute: Aspect.InstalledAttribute, value: any): ConstraintValue {
    return new ConstraintValue(type, leftVariable, leftAttribute, value); }
  function c_var(type: ConstraintBetweenAnyValueAndAnyValue, leftVariable: string, leftAttribute: Aspect.InstalledAttribute, rightVariable: string, rightAttribute: Aspect.InstalledAttribute): ConstraintVariable {
    return new ConstraintVariable(type, leftVariable, leftAttribute, rightVariable, rightAttribute); }
  function c_subin (sub: string, attribute: Aspect.InstalledAttribute): ConstraintSub { return new ConstraintSub(ConstraintType.SubIn   , attribute, sub); }
  function c_subnin(sub: string, attribute: Aspect.InstalledAttribute): ConstraintSub { return new ConstraintSub(ConstraintType.SubNotIn, attribute, sub); }

  function find(set: ValueSet, value: Value) { // O(set.size)
    for (let v of set) {
      if (v === value)
        return true;
    }
    return false;
  }
  function a_intersects_b(a: ValueSet, b: ValueSet) { // TODO: fix O(a.size * b.size) -> O(a.size * log(b.size))
    for (let va of a) {
      if (find(b, va))
        return true;
    }
    return false;
  }
  function a_subsetof_b(a: ValueSet, b: ValueSet) { // TODO: fix O(a.size * b.size) -> O(a.size * log(b.size))
    for (let va of a) {
      if (!find(b, va))
        return false;
    }
    return true;
  }
  function a_samesetas_b(a: ValueSet, b: ValueSet) { // TODO: fix O(a.size * b.size * 2) -> O(a.size * log(b.size))
    return a_subsetof_b(a, b) && a_subsetof_b(b, a);
  }

  function pass_value(op: ConstraintBetweenAnyValueAndFixedValue, left: AnyValue, right: AnyValue) {
    switch (op) {
      // a operator b
      case ConstraintType.Equal: return left === right;
      case ConstraintType.NotEqual: return left !== right;
      case ConstraintType.GreaterThan: return left! > right!;
      case ConstraintType.GreaterThanOrEqual: return left! >= right!;
      case ConstraintType.LessThan: return left! < right!;
      case ConstraintType.LessThanOrEqual: return left! <= right!;

      // A operator b
      case ConstraintType.Contains: return find(left as ValueSet, right as Value);
      case ConstraintType.NotContains: return !find(left as ValueSet, right as Value);

      // a operator B
      case ConstraintType.In: return find(right as ValueSet, left as Value);
      case ConstraintType.NotIn: return !find(right as ValueSet, left as Value);

      // A operator B
      case ConstraintType.Intersects: return a_intersects_b(left as ValueSet, right as ValueSet);
      case ConstraintType.NotIntersects: return !a_intersects_b(left as ValueSet, right as ValueSet);
      case ConstraintType.SubSet: return a_subsetof_b(left as ValueSet, right as ValueSet);
      case ConstraintType.NotSubSet: return !a_subsetof_b(left as ValueSet, right as ValueSet);
      case ConstraintType.SuperSet: return a_subsetof_b(right as ValueSet, left as ValueSet);
      case ConstraintType.NotSuperSet: return !a_subsetof_b(right as ValueSet, left as ValueSet);
      case ConstraintType.SameSet: return a_samesetas_b(left as ValueSet, right as ValueSet);
      case ConstraintType.NotSameSet: return !a_samesetas_b(left as ValueSet, right as ValueSet);

      // a operator value
      case ConstraintType.Text: return (left as string).indexOf(right as string) !== -1;
      case ConstraintType.Exists: return right !== undefined && (!Array.isArray(right) || right.length > 0);
    }
    throw new Error(`Unsupported on value constraint ${ConstraintType[op as any]}`);
  }

  export type Instance<R> = string | R;
  export interface ConstraintDefinition {
    $eq?: Value;
    $neq?: Value;
    $gt?: string | Date | number;
    $gte?: string | Date | number;
    $lt?: string | Date | number;
    $lte?: string | Date | number;
    $exists?: boolean;
    $in?: Instance<ObjectSetDefinition> | (Value[]);
    $nin?: Instance<ObjectSetDefinition> | (Value[]);
    $contains?: Instance<ObjectSetDefinition> | Value;
    $ncontains?: Instance<ObjectSetDefinition> | Value;
    [s: string]: AnyValue | ConstraintDefinition | Function | undefined;
  }
  export interface ObjectSetDefinitionR {
    $in: Instance<ObjectSetDefinition>;
    $nin: Instance<ObjectSetDefinition>;
    $union: Instance<ObjectSetDefinition>[];
    $unionForAlln: string;
    $intersection: Instance<ObjectSetDefinition>[];
    $diff: [Instance<ObjectSetDefinition>, Instance<ObjectSetDefinition>];
    $instanceOf: string | Function;
    $is: string | Function;
    $text: string;
    $out: string;
    $or: ConstraintDefinition[];
    $and: ConstraintDefinition[];
  }
  export type ObjectSetDefinition = Partial<ObjectSetDefinitionR> & {
    [s: string]: Value | ConstraintDefinition | Function
  }
  export interface Element extends ObjectSetDefinitionR {
    $elementOf: Instance<ObjectSetDefinition>;
  }
  export type ResultDefinition = {
    name: string;
    where: Instance<ObjectSetDefinition>;
    scope?: Scope;
    [s: string]: any;
  }
  export type RequestDefinition = ResultDefinition | { results:ResultDefinition[], [s: string]: any; };

  type OperatorOnSet<T> = (context: ParseContext, p: PathReporter, set: ObjectSet, value: T) => void;
  const operatorsOnSet: { [op: string]: OperatorOnSet<any>; } = {
    $or: (context, p, set, value) => {
      let constraints: Constraint[] = [];
      context.parseConditionsArray(p, set, constraints, context.head, value);
      if (constraints.length)
        set.and(c_or(constraints));
    },
    $and: (context, p, set, value) => {
      let constraints: Constraint[] = [];
      context.parseConditionsArray(p, set, constraints, context.head, value);
      if (constraints.length)
        set.and(c_and(constraints));
    },
    $elementOf: (context, p, set, value) => {
      let sub = context.parseSet(p, value, `${set._name}.$elementOf`);
      if (!set.tryToMerge(sub)) // compatible
        throw new Error(`cannot elementOf between incompatible sets`);
    },
    $instanceOf: (context, p, set, value) => {
      set.addType({ type: ConstraintType.InstanceOf, value: context.aspect(value) });
    },
    $is: (context, p, set, value) => {
      set.addType({ type: ConstraintType.Is, value: context.aspect(value) });
    },
    $union: (context, p, set, value) => {
      p.pushArray();
      let subs = value.map((v, i) => context.parseSet(p.setArrayKey(i), v, `${set._name}.$union[${i}]`));
      p.popArray();
      set.addType({ type: ConstraintType.Union, value: new Set(subs) });
    },
    $unionForAlln: (context, p, set, value) => {
      let m = typeof value === "string" ? value.match(/^=(\w+)\(\s*n\s*\)$/) : null;
      if (!m)
        throw new Error(`$unionForAlln must be a reference to a recursive set definition`);
      let letter = m[1];
      let u_n_name = `${letter}(n)=`;
      let s = new ParseStack(p, { ...context.head.original, [u_n_name]: {} }, context.head.parent);
      let c = context.derive(s);
      let u_n = c.createSet(`${letter}(n)`);
      s.resolved.set(u_n_name, u_n);
      let u_0 = c.resolve(p, `${letter}(0)`, c.head);
      u_n.typeConstraints.push({ type: ConstraintType.Recursion, value: u_n });
      u_n.typeConstraints.push(...u_0.typeConstraints);
      let u_np1 = c.resolve(p, `${letter}(n + 1)`, c.head);
      set.addType({ type: ConstraintType.UnionForAlln, value: [u_0, u_n, u_np1] });
    },
    $intersection: (context, p, set, value) => {
      p.pushArray();
      value.forEach((v, i) => {
        let sub = context.parseSet(p.setArrayKey(i), v, `${set._name}.$intersection[${i}]`);
        if (!set.tryToMerge(sub)) // must be compatible
          throw new Error(`cannot intersect between incompatible sets`);
      });
      p.popArray()
    },
    $diff: (context, p, set, value) => {
      if (!Array.isArray(value) || value.length !== 2)
        return p.diagnostic({ is: "error", msg: `diff value must be an array of 2 object set` });
      p.pushArray();
      let add = context.parseSet(p.setArrayKey(0), value[0], `${set._name}.$diff+`);
      let del = context.parseSet(p.setArrayKey(1), value[1], `${set._name}.$diff-`);
      p.popArray()
      set.and(c_subin(set.sub(add), Aspect.attribute_id));
      set.and(c_subnin(set.sub(del), Aspect.attribute_id));
    },
    $in: (context, p, set, value) => {
      if (Array.isArray(value))
        in_value_on_set(context, p, set, value, ConstraintType.In);
      else {
        let sub = context.parseSet(p, value, `${set._name}.$in`);
        if (!set.tryToMerge(sub)) // must be compatible
          return p.diagnostic({ is: "error", msg: `cannot intersect between incompatible sets` });
      }
    },
    $nin: (context, p, set, value) => {
      if (Array.isArray(value))
        in_value_on_set(context, p, set, value, ConstraintType.NotIn);
      else {
        let sub = context.parseSet(p, value, `${set._name}.$nin`);
        set.and(c_subnin(set.sub(sub), Aspect.attribute_id));
      }
    },
    $out: (context, p, set, value) => {
      throw new Error(`$out is managed in ParseContext`);
    },
    $text: (context, p, set, value) => {
      if (typeof value !== "string")
        throw new Error(`$text value must be a string`);
      if (value) // No constraint on empty string
        set.and(c_value(ConstraintType.Text, set._name, Aspect.attribute_id, value));
    },
  } as { [K in keyof Element]: OperatorOnSet<Element[K]>; };

  enum OperatorKind {
    a_op_b,
    A_op_b,
    a_op_B,
    A_op_B,
    a_op_v,
    set_op,
  }
  type OperatorValidation = (at: PathReporter, left_var: VarPath, right_var: VarPath | undefined, right_fixed: any) => boolean;

  function in_value_on_set(context: ParseContext, at: PathReporter, set: DataSourceInternal.ObjectSet, value: any[], type: ConstraintBetweenAnyValueAndFixedValue) {
    let ok = true;
    at.pushArray();
    for (let [i, v] of value.entries()) {
      if (!(v instanceof VersionedObject)) {
        at.setArrayKey(i).diagnostic({ is: "error", msg: `only versioned object are allowed here` });
        ok = false;
      }
    }
    at.popArray();
    let attr = ok && context.aspectAttribute(at, set, undefined);
    if (attr)
      set.and(c_value(ConstraintType.In, set._name, attr, value));
  }

  function validate_var_is_undefined(at: PathReporter, v: VarPath, side: string): boolean {
    at.diagnostic({ is: "error", msg: `${side} operand must be a fixed value` });
    return false;
  }
  function validate_var_is_value(at: PathReporter, v: VarPath, side: string): boolean {
    let ret = v.attribute.isMonoValue();
    if (!ret)
      at.diagnostic({ is: "error", msg: `${side} operand must be a single value, ${JSON.stringify(v.attribute.type)} was found` });
    return ret;
  }
  function validate_var_is_set(at: PathReporter, v: VarPath, side: string): boolean {
    let ret = v.attribute.isMultValue();
    if (!ret)
      at.diagnostic({ is: "error", msg: `${side} operand must be a set of values, ${JSON.stringify(v.attribute.type)} was found` });
    return ret;
  }
  function validate_fixed_is_value(at: PathReporter, fixed: any): boolean {
    let ret = fixed === undefined || !(fixed instanceof Array || fixed instanceof Set);
    if (!ret)
      at.diagnostic({ is: "error", msg: `right operand must be a single value,  ${Object.prototype.toString.call(fixed)} was found` });
    return ret;
  }
  function validate_fixed_is_set(at: PathReporter, fixed: any): boolean {
    let ret = fixed === undefined || fixed instanceof Array || fixed instanceof Set;
    if (!ret)
      at.diagnostic({ is: "error", msg: `right operand must be a set of values, ${Object.prototype.toString.call(fixed)} was found` });
    return ret;
  }
  function validate_are_comparable(at: PathReporter, a: Aspect.Type, b: Aspect.Type) {
    let ret = Aspect.Type.areComparable(a, b);
    if (!ret) {
      at.diagnostic({ is: "error",
        msg: `operands are incompatible, ${a} !== ${b}`
      });
    }
    return ret;
  }
  function subtype(a: Aspect.Type) : Aspect.Type {
    return (a as any).type; // TODO: better typings
  }
  const a_op_b: OperatorValidation = function a_op_b(at, left_var, right_var, right_fixed) {
    if (left_var.attribute === Aspect.attribute_id && !right_fixed)
      at.diagnostic({ is: "error", msg: `right operand must be defined for _id` });
    return validate_var_is_value(at, left_var, 'left')
      && right_var
      ? validate_var_is_value(at, right_var, 'right') && validate_are_comparable(at, left_var.attribute.type, right_var.attribute.type)
      : validate_fixed_is_value(at, right_fixed);
  };
  const A_op_b: OperatorValidation = function a_op_b(at, left_var, right_var, right_fixed) {
    return validate_var_is_set(at, left_var, 'left')
      && right_var
      ? validate_var_is_value(at, right_var, 'right') && validate_are_comparable(at, subtype(left_var.attribute.type), right_var.attribute.type)
      : validate_fixed_is_value(at, right_fixed);
  };
  const a_op_B: OperatorValidation = function a_op_b(at, left_var, right_var, right_fixed) {
    return validate_var_is_value(at, left_var, 'left')
      && right_var
      ? validate_var_is_set(at, right_var, 'right') && validate_are_comparable(at, left_var.attribute.type, subtype(right_var.attribute.type))
      : validate_fixed_is_set(at, right_fixed);
  };
  const A_op_B: OperatorValidation = function a_op_b(at, left_var, right_var, right_fixed) {
    return validate_var_is_set(at, left_var, 'left')
      && right_var
      ? validate_var_is_set(at, right_var, 'right') && validate_are_comparable(at, left_var.attribute.type, right_var.attribute.type)
      : validate_fixed_is_set(at, right_fixed);
  };
  const a_op_v: OperatorValidation = function a_op_b(at, left_var, right_var, right_fixed) {
    let ret = validate_var_is_value(at, left_var, 'left')
      && right_var
      ? validate_var_is_undefined(at, right_var, 'right')
      : validate_fixed_is_value(at, right_fixed);
    return ret;
  };
  const set_op: OperatorValidation = function a_op_b(at, left_var, right_var, right_fixed) {
    at.diagnostic({ is: "error", msg: `only operators on attributes are allowed here` });
    return false;
  };
  type OperatorDesc = { is: OperatorValidation, type: ConstraintType };
  const operators: { [op: string]: OperatorDesc } = {
    // a operator b
    $eq:  { is: a_op_b, type: ConstraintType.Equal              },
    $neq: { is: a_op_b, type: ConstraintType.NotEqual           },
    $gt:  { is: a_op_b, type: ConstraintType.GreaterThan        },
    $gte: { is: a_op_b, type: ConstraintType.GreaterThanOrEqual },
    $lt:  { is: a_op_b, type: ConstraintType.LessThan           },
    $lte: { is: a_op_b, type: ConstraintType.LessThanOrEqual    },

    // A operator b
    $contains:    { is: A_op_b, type: ConstraintType.Contains      },
    $ncontains:   { is: A_op_b, type: ConstraintType.NotContains   },

    // a operator B
    $in:          { is: a_op_B, type: ConstraintType.In            },
    $nin:         { is: a_op_B, type: ConstraintType.NotIn         },

    // A operator B
    $intersects:  { is: A_op_B, type: ConstraintType.Intersects    },
    $nintersects: { is: A_op_B, type: ConstraintType.NotIntersects },
    $subset:      { is: A_op_B, type: ConstraintType.SubSet        },
    $nsubset:     { is: A_op_B, type: ConstraintType.NotSubSet     },
    $superset:    { is: A_op_B, type: ConstraintType.SuperSet      },
    $nsuperset:   { is: A_op_B, type: ConstraintType.NotSuperSet   },
    $sameset:     { is: A_op_B, type: ConstraintType.SameSet       },
    $nsameset:    { is: A_op_B, type: ConstraintType.NotSameSet    },

    // a operator value
    $text:   { is: a_op_v, type: ConstraintType.Text   },
    $exists: { is: a_op_v, type: ConstraintType.Exists },

    // set operators
    $instanceOf:   { is: set_op, type: ConstraintType.InstanceOf   },
    $is:           { is: set_op, type: ConstraintType.Is           },
    $union:        { is: set_op, type: ConstraintType.Union        },
    $unionForAlln: { is: set_op, type: ConstraintType.UnionForAlln },
    $or:           { is: set_op, type: ConstraintType.Or           },
    $and:          { is: set_op, type: ConstraintType.And          },
  };

  function isResult(result: RequestDefinition): result is ResultDefinition {
    return (result as ResultDefinition).name !== undefined;
  }

  class ParseStack {
    parent: ParseStack | undefined;
    at: PathReporter;
    resolved: Map<string, ObjectSet>; // Pre-resolved sets
    original: any; // Original object
    constructor(at: PathReporter, original, parent: ParseStack | undefined) {
      this.parent = parent;
      this.resolved = new Map();
      this.original = original;
      this.at = at;
    }
  }
  type VarPath = { set: ObjectSet, variable: string, attribute: Aspect.InstalledAttribute };
  class ParseContext {
    constructor(
      public head: ParseStack,
      public cc: ControlCenter,
    ) {}

    derive(head: ParseStack) {
      return new ParseContext(head, this.cc);
    }
    aspect(name: string | Function) : Aspect.Installed {
      let n: string = typeof name === "string" ? name : (name as any).aspect ? (name as any).aspect.name : (name as any).definition.name;
      return this.cc.aspectChecked(n);
    }
    aspects() {
      return this.cc.installedAspects();
    }
    push(at: PathReporter, original: any) {
      this.head = new ParseStack(at.copy(), original, this.head);
    }
    pop() {
      if (!this.head.parent)
        throw new Error(`cannot pop stack`);
      this.head = this.head.parent;
    }

    resolve(at: PathReporter, reference: string, end: ParseStack | undefined = undefined) : ObjectSet {
      let key = `${reference}=`;
      let v: ObjectSet | undefined;
      let s: ParseStack | undefined = this.head;
      for (; !v && s; s = s.parent) {
        let o = s.original[key];
        if (o) {
          v = s.resolved.get(key);
          if (!v) {
            let c = this.derive(s);
            v = c.parseSet(s.at, o, reference);
            s.resolved.set(key, v);
          }
        }
        if (s === end)
          break;
      }
      if (!v) {
        at.diagnostic({ is: "error", msg: `no object set with the name ${key} found` });
        v = this.createSet(key);
      }
      return v;
    }

    createSet(name: string): ObjectSet {
      let set = new ObjectSet(name);
      return set;
    }

    resolveSet(at: PathReporter, reference: string) : ObjectSet {
      let parts = reference.split(':');
      let set = this.resolve(at, parts[0]);
      if (parts.length > 1) {
        let k = set._name;
        let vars: [string, ObjectSet, Constraint | undefined][] = [];
        let fattr = this.resolveAttribute(at, set, parts, k, 1, parts.length, (k, s, c) => {
          vars.push([k, s, c]);
        });
        if (!fattr)
          return this.createSet(reference);
        let fset = fattr.set;
        fset.setVariable(k, set);
        for (let [k, s, c] of vars) {
          fset.setVariable(k, s);
          fset.and(c);
        }
        set = fset;
      }
      return set;
    }

    resolveElement(at: PathReporter, reference: string, set: ObjectSet, end: ParseStack | undefined) : VarPath | undefined {
      let parts = reference.split('.');
      let k = parts[0];
      let variable = set.variable(k);
      if (!variable) {
        let sub = this.resolve(at, k, end);
        variable = sub.clone(k);
        set.setVariable(k, variable);
      }
      return this.resolveAttribute(at, variable, parts, k, 1);
    }

    aspectAttribute(at: PathReporter, set: ObjectSet, name: string | undefined): Aspect.InstalledAttribute| undefined {
      if (name === "_id")
        return Aspect.attribute_id;

      let attr: Aspect.InstalledAttribute | undefined = undefined;
      let n = 0;
      let mult = false;
      const setAttr = (aspect: Aspect.Installed, name: string | undefined) => {
        let a = name ? (aspect.attributes.get(name) || DataSourceScope.parse_virtual_attribute(at, aspect, name)) : aspect.attribute_ref;
        if (a) {
          if (attr && !DataSourceScope.attribute_name_type_are_equals(a, attr)) {
            if (!mult)
              at.diagnostic({ is: "error", msg: `attribute ${name} refer to multiple aspect attributes` });
            mult = true;
          }
          attr = a;
        }
      };

      const recurse = (set: ObjectSet) => {
        for (let c_self of set.typeConstraints) {
          if (c_self.type === ConstraintType.InstanceOf || c_self.type === ConstraintType.Is) {
            n++;
            setAttr(c_self.value, name);
          }
          else if (c_self.type === ConstraintType.Union) {
            for (let u of c_self.value)
              recurse(u);
          }
          else if (c_self.type === ConstraintType.UnionForAlln) {
            recurse(c_self.value[0]);
          }
        }
      }

      recurse(set);
      if (n === 0) {
        for (let aspect of this.aspects())
          setAttr(aspect, name);
      }
      if (!attr)
        at.diagnostic({ is: "error", msg: `attribute ${name} not found` });
      return attr;
    }

    resolveAttribute(at: PathReporter, set: ObjectSet, parts: string[], k: string = set._name, start: number = 0, last = parts.length - 1, decl?: (k: string, s: ObjectSet, c?: Constraint) => void) : VarPath | undefined {
      if (!decl) {
        decl = (k, s, c) => {
          set.setVariable(k, s);
          set.and(c);
        };
      }
      let i = start;
      for (; i < last; i++) { // >.attr1<.attr2
        k += `.${parts[i]}`;
        let s = set.variable(k);
        if (!s) {
          s = this.createSet(k);
          let attr = this.aspectAttribute(at, set, parts[i]);
          if (attr) {
            let aspect = attr.containedVersionedObjectIfAlone();
            if (aspect) {
              s.addType({ type: ConstraintType.InstanceOf, value: aspect });
              decl(k, s, c_var(ConstraintType.Equal, set._name, attr, k, Aspect.attribute_id));
            }
            else {
              at.diagnostic({ is: "error", msg: `invalid constraint attribute type ${attr.type} on ${parts[i]}` });
            }
          }
        }
        set = s;
      }
      let attr = this.aspectAttribute(at, set, i < parts.length ? parts[i] : undefined);
      return attr ? { set: set, variable: k, attribute: attr  } : undefined;
    }

    parseSet(at: PathReporter, v: Instance<ObjectSetDefinition>, name: string, set?: ObjectSet) : ObjectSet {
      if (typeof v === "string") {
        if (!v.startsWith("=")) {
          at.diagnostic({ is: "error", msg: `an object set definition or reference was expected` });
          return this.createSet("bad reference");
        }
        return this.resolveSet(at, v.substring(1));
      }
      this.push(at, v);

      let nout = v["$out"];
      if (nout) {
        at.push('.$out');
        if (!nout.startsWith("=") && nout.indexOf(".") !== -1) {
          at.diagnostic({ is: "error", msg: `an object set definition or reference was expected` });
          return this.createSet("bad $out");
        }
        nout = nout.substring(1);
        set = this.resolve(at, nout);
        at.pop();
      }
      else {
        set = set || this.createSet(name);
      }

      at.push('.', '');
      for (let key in v) {
        if (key.startsWith('$') && key !== "$out") {
          // key is an operator
          at.set(key);
          let op_on_set = operatorsOnSet[key];
          if (!op_on_set) {
            let msg = operators[key] ? `only operators on set are allowed here` : `unknown operator`;
            at.diagnostic({ is: "error", msg: msg });
          }
          else
            op_on_set(this, at, set, v[key]);
        }
      }
      at.pop(2);

      this.parseConditions(at, set, set.constraints, this.head, v);

      this.pop();
      return set;
    }

    parseConditionsArray(at: PathReporter, set: ObjectSet, constraints: Constraint[], end: ParseStack | undefined, c: ConstraintDefinition[]) {
      at.pushArray();
      for (let [i, conditions] of c.entries())
        this.parseConditions(at.setArrayKey(i), set, constraints, this.head, conditions);
      at.popArray();
    }

    parseConditions(at: PathReporter, set: ObjectSet, constraints: Constraint[], end: ParseStack | undefined, conditions: ConstraintDefinition) {
      at.push('.', '');
      for (let key in conditions) {
        if (!key.startsWith('$')) {
          at.set(key);
          if (key.startsWith('=$')) {
            //virtual attributes
            let a = this.resolveAttribute(at, set, [key.substring(1)]);
            if (a)
              this.parseRightConditions(at, set, constraints, end, a, conditions[key]);
          }
          else if (key.startsWith('=')) {
            // only elements are allowed here ( ie. =element_name(.attribute)* )
            let a = this.resolveElement(at, key.substring(1), set, end);
            if (a)
              this.parseRightConditions(at, set, constraints, end, a, conditions[key]);
          }
          else if (!key.endsWith('=')) {
            // key is an attribute path
            let a = this.resolveAttribute(at, set, key.split('.'));
            if (a)
              this.parseRightConditions(at, set, constraints, end, a, conditions[key]);
          }
        }
      }
      at.pop(2);
    }

    parseRightConditions(at: PathReporter, set: ObjectSet, constraints: Constraint[], end: ParseStack | undefined, left: VarPath, conditions: any) {
      if (conditions && typeof conditions === "object") {
        if (conditions instanceof VersionedObject) {
          constraints.push(c_value(ConstraintType.Equal, left.variable, left.attribute, conditions));
        }
        else {
          this.push(at, conditions);
          at.push('.', '');
          for (var key in conditions) {
            at.set(key);
            if (!key.startsWith(`$`))
              at.diagnostic({ is: "error", msg: `an operator was expected` });
            else {
              let v = conditions[key];
              let op = operators[key];
              if (!op)
                at.diagnostic({ is: "error", msg: `unknown operator` });
              else if (typeof v === "string" && v.startsWith('=')) {
                let right = this.resolveElement(at, v.substring(1), set, end);
                if (right && op.is(at, left, right, undefined))
                  constraints.push(c_var(op.type as any, left.variable, left.attribute, right.variable, right.attribute));
              }
              else {
                if (op.is(at, left, undefined, v))
                  constraints.push(c_value(op.type as any, left.variable, left.attribute, v));
              }
            }
          }
          at.pop(2);
          this.pop();
        }
      }
      else if (typeof conditions === "string" && conditions.startsWith('=')) {
        let right = this.resolveElement(at, conditions.substring(1), set, end);
        if (right)
          constraints.push(c_var(ConstraintType.Equal, left.variable, left.attribute, right.variable, right.attribute));
      }
      else {
        constraints.push(c_value(ConstraintType.Equal, left.variable, left.attribute, conditions));
      }
    }
  }

  function parseResult(context: ParseContext, at: PathReporter, result: ResultDefinition) {
    context.push(at, result);
    at.push('.where');
    let set = context.parseSet(at, result.where, result.name);
    at.pop();
    set = set.clone(set._name);
    set.name = result.name;
    if (result.scope) {
      let r = parseScope(result.scope, function *(type) {
        if (type !== '_')
          yield context.aspect(type);
        else
          yield* set.attributesAndCompatibleAspects(context.cc).compatibleAspects;
      });
      set.scope = r.scope;
      set.sort = r.sort;
    }
    context.pop();
    return set;
  }

  export function resolveScopeForObjects(unsafe_scope: Scope, cc: ControlCenter, objects: Iterable<VersionedObject>) : ResolvedScope {
    return parseScope(unsafe_scope, function *(type) {
      if (type !== '_') {
        yield cc.aspectChecked(type);
      }
      else {
        let aspects = new Set<Aspect.Installed>();
        for (let o of objects)
          aspects.add(o.manager().aspect());
        yield* aspects;
      }
    }).scope;
  }

  export function parseRequest(request: RequestDefinition, cc: ControlCenter) : Result<ObjectSet[]> {
    let at = new PathReporter(new Reporter());
    let context = new ParseContext(new ParseStack(at, request, undefined), cc);
    let sets: ObjectSet[] = [];
    if (isResult(request))
      sets.push(parseResult(context, at, request));
    else {
      at.push('results').pushArray();
      request.results.forEach((result, i) => {
        sets.push(parseResult(context, at.setArrayKey(i), result));
      });
    }
    return Result.fromReporterAndValue(at.reporter, sets);
  }

  export function applyWhere(where: ObjectSetDefinition, objects: VersionedObject[], cc: ControlCenter) : Result<VersionedObject[]> {
    let at = new PathReporter(new Reporter());
    let context = new ParseContext(new ParseStack(at, where, undefined), cc);
    let set = context.parseSet(at, where, "where");
    let sctx = new FilterContext(objects, versionedObjectMapper);
    return Result.fromReporterAndValue(at.reporter, sctx.solveSorted(set));
  }

  export function applyRequest(request: RequestDefinition, objects: VersionedObject[], cc: ControlCenter) : Result<{ [s: string]: VersionedObject[] }> {
    let req = parseRequest(request, cc);
    return Result.fromResultWithMappedValue(req, (sets) => {
      let map = applySets(req.value(), objects, true);
      let ret = {};
      map.forEach((objs, set) => {
        ret[set.name] = objs;
      });
      return ret;
    });
  }

  export function applySets(sets: ObjectSet[], objects: VersionedObject[], namedOnly: true): Map<ObjectSet & { name: string }, VersionedObject[]>
  export function applySets(sets: ObjectSet[], objects: VersionedObject[], namedOnly?: boolean): Map<ObjectSet, VersionedObject[]>
  export function applySets<T>(sets: ObjectSet[], objects: T[], namedOnly: true, mapper: Mapper<T>): Map<ObjectSet & { name: string }, T[]>
  export function applySets<T>(sets: ObjectSet[], objects: T[], namedOnly: boolean, mapper: Mapper<T>): Map<ObjectSet, T[]>
  export function applySets(sets: ObjectSet[], objects: any[], namedOnly = true, mapper: Mapper<any> = versionedObjectMapper): Map<ObjectSet, VersionedObject[]> {
    let ret = new Map<ObjectSet, VersionedObject[]>();
    let sctx = new FilterContext(objects, mapper);
    for (let set of sets) {
      if (set.name || !namedOnly) {
        ret.set(set, sctx.solveSorted(set));
      }
    }
    return ret;
  }
}

redpajama_set_name: RedPajamaGithub


text:
Amy Viteri, Investigative Reporter
Published: February 20, 2019, 11:29 PM
Tags: News, Health, Local, Miami, Local 10 Investigates, Miami-Dade
Lawmaker wants to hold disreputable plastic surgery clinics to account
Several people have died during unsafe procedures in South Florida
MIAMI – A Florida lawmaker is proposing a bill that would crack down on surgery clinics accused of putting profits ahead of patient safety after a string of patients have died at South Florida businesses.
The proposed legislation comes too late for Tarrance London and his family. His mother Kizzy London died in December of 2017 after traveling to Miami from Baton Rouge, Louisiana to get a Brazilian butt lift procedure at Jolie Plastic Surgery. London remembers the last conversation he had with his mother.
"Next time you talk to me, I'm going to be on the beach drinking mimosas with my new body," he recalled her saying.
The 40-year-old mother of two underwent the procedure in which fat is removed from unwanted areas with liposuction and then injected into the backside. She didn't survive. According to Florida's Department of Health she died from an embolism when fat was injected into her vein. She isn't the only woman who has lost her life at high-volume surgery clinics in South Florida in recent years.
"Who actually cares about the women?" Tarrance London asked. "About the women and not the money? Who in Florida actually cares about these women that are losing their lives and the families of these women?"
State Sen. Anitere Flores told Local 10's Amy Viteri she gets at least two to three unsolicited calls a day from someone who has been hurt or lost a loved one at a South Florida surgery clinic.
Kizzy London died in December 2017 after a butt lift procedure at Jolie Plastic Surgery.
"You have people from across the country saying, 'Well, I never want to go to Florida because that's where people go to die from cosmetic surgery,'" Flores said.
Several of the clinics involved are in her district, leading Flores to sponsor a bill that would give the state more power to push back.
"One of the common threads is these are being run as businesses by nonmedical professionals," Flores explained. "This is not I would say a loophole. This has been a gaping hole in the state's regulatory scheme."
The bill would require clinics to be owned and operated by medical doctors. Currently, anyone can own a surgery center in Florida. The state would also be able to revoke a clinic's registration following a death or serious injury. A doctor who loses their registration could be barred from operating in any facility for a five-year period. If passed, the bill would also require an anesthesiologist to be present for more complicated procedures.
"They are putting these people under and they have no expertise when it comes to what to do when something goes wrong," Flores said. "We want to stop the practice that some of these clinics do where some of these doctors, these bad actors, go from place to place to place."
The doctor who performed Kizzy London's surgery, Arnaldo Valls, has no board certification or known training in plastic surgery. Jolie's website has described Valls as one of their "most experienced plastic surgeons." When asked by Viteri about his experience, Valls claimed he was trained in cosmetic surgery, a designation not recognized by the American Board of Medical Specialties.
Following Kizzy London's death, Florida's Department of Health placed an emergency restriction on Valls' medical license, finding he fell below the minimum standard of care by injecting fat too deep into a vein, sending fat to the lungs, which caused "immediate cardiac arrest."
Despite performing cosmetic procedures, Arnaldo Valls has no board certification or known training in plastic surgery.
The report said that Valls knew he was not competent to perform the procedure "due to lack of training and experience."
In a separate complaint from 2016, officials wrote Valls perforated a woman's small bowel during another Brazilian butt lift procedure.
In a settlement agreement waiting on approval from the Board of Medicine, the Health Department has recommended Valls be restricted from doing the procedures until further evaluation and pay a fine of $15,000.
"These are clinics that are being run by individuals who have no care for patients," Flores said, "They feel like they have no obligation for the safety of patients."
Kizzy London left behind two sons. Her youngest is now just 2 years old. Tarrance London said he has experienced deep depression over his mother's death and wants everyone to know how much was taken from his family.
"They took the glue away from it really," he said. "You take the nails out of the house it's going to fall."
He also had a message to lawmakers considering the bill and the regulations it would create: "Imagine if it was your family."
After Kizzy London's death, Jolie issued a statement sending condolences, saying in part, "Cosmetic surgeries, like all surgeries, carry some degree of risk."
Flores' proposed legislation, Senate Bill 732, is scheduled to be heard in the upcoming legislative session in March.
Amy Viteri
Amy Viteri is an Emmy Award-winning journalist who joined Local 10 News in September 2015. She's currently an investigative reporter and enjoys uncovering issues facing South Florida communities. A native of the Washington, D.C., area, she's happy to be back in South Florida, where she earned a masters degree at the University of Miami.

redpajama_set_name: RedPajamaCommonCrawl


text:
Children of Bodom byla finská metalová kapela z města Espoo.

Kapela byla založena v roce 1993 pod jménem IneartheD, ale později si na návrh nahrávací společnosti změnila jméno na Children of Bodom, odvozené z proslulých "bodomských vražd", které se udály u jezera Bodom v Espoo v roce 1960.

Tématem skladeb je většinou právě jezero Bodom, dále smrt a problémy v osobních vztazích. Zpěvák Alexi Laiho užíval chraplavě křičícího vokálu (screaming) s často k nepoznání zkomolenými slovy, proto byl často mylně považován za zpěváka s velmi špatnou anglickou výslovností, ačkoli ve skutečnosti mluvil anglicky plynule.

Sestava

Finální sestava
 Alexi Laiho – vokály, kytara
 Daniel Freyberg - rytmická kytara
 Janne Wirman – klávesy
 Henkka T. Blacksmith – baskytara
 Jaska W. Raatikainen – bicí

Bývalí členové
 Alexander Kuoppala – kytara, 1993-2003
 Erna Siikavirta – klávesy (na turné)
 Kimberly Goss – klávesy (na turné)
 Roope Latvala – rytmická kytara
 Antti Wirman – rytmická kytara (na turné)

Časová osa

Diskografie
 Something Wild (1997)
 Hatebreeder (1999)
 Follow the Reaper (2000)
 Hate Crew Deathroll (2003)
 Are You Dead Yet? (2005)
 Blooddrunk (2008)
 Relentless Reckless Forever (2011)
 Halo of Blood (2013)
 I Worship Chaos (2015)
 Hexed (2019)

Koncertní alba
 Tokyo Warhearts (1999)
 Chaos Ridden Years – Stockholm Knockout Live (2006)

Ostatní
 Children of Bodom - split singl (1997)
 Bestbreeder from 1997 to 2000 – best of/kompilace (2003)
 Thrashed, Lost & Strungout – EP (2004)
 Skeletons in the Closet – kompilace (2009)

Externí odkazy

 Oficiální stránky
 Children of Bodom na MySpace
 Oficiální fanouškovské fórum

Finské deathmetalové hudební skupiny
Melodic deathmetalové hudební skupiny
Powermetalové hudební skupiny
Hudební skupiny 1990–1999
Hudební skupiny 2000–2009
Hudební skupiny 2010–2019
Hudební skupiny založené v roce 1993

redpajama_set_name: RedPajamaWikipedia


text:
Bataan's 2nd congressional district is one of the three congressional districts of the Philippines in the province of Bataan. It has been represented in the House of Representatives since 1987. The district consists of the provincial capital Balanga and adjacent southern Bataan municipalities of Limay, Orion and Pilar. It is currently represented in the 19th Congress by Albert Garcia of the National Unity Party (NUP).

Representation history

Election results

2022

2019

2016

2013

2010

See also
Legislative districts of Bataan

References

Congressional districts of the Philippines
Politics of Bataan
1987 establishments in the Philippines
Congressional districts of Central Luzon
Constituencies established in 1987


text:
HHS Likely To Extend Emergency Declaration After Provider Pressure
By Chelsea Cirruzzo, Dorothy Mills-Gregg / June 29, 2020 at 6:09 PM
The hospice lobby told Inside Health Policy on Monday (June 29) that HHS signaled to the group an extension of the public health emergency declaration is likely as a growing number of provider groups join hospitals' calls to HHS to extend the declaration before its expiration on July 25. HHS Assistant Secretary for Public Affairs Michael Caputo confirmed on Twitter late Monday that HHS would be extending the emergency declaration before it expires. The National Hospice and Palliative Care...


text:
Maburaho
Maburaho Season 1 Episode 24
Kazuki's body was restored with Yuna and Chihaya's ashes. But now, instead of one Kazuki, there are now ten! In the end, Kuriko suggested that she would take around six Kazukis all for herself and the remaining four will be given to Rin, Yuna, and Chihaya. Yuna disapproved as she is (or so she believes) Kazuki's wife and she would consider it unfaithful should any of the ten Kazukis date another girl.
Serie: Maburaho
Episode Title: It Ended...
When the Golden Race invited other races to join them in the stars, three sentient races answered their call. The Goldens called them the Bronze, Silver and Heroic Tribes. Just…
Edward and Alphonse Elric are two brothers gifted with the ability of alchemy, the science of taking one thing and changing it into another. However, alchemy works on the theory…
Hikaru Shindo is just a normal 12 year old boy, but one day he's rummaging through his Grandfather's things to see if he can find something to sell and pulls…
The Tree In Sunlight
A story set in the mid-1800's about a young doctor who has been trained in Western-style medicine and a young samurai who is trying to live up to the old…
Genre: Animation, Drama, War & Politics
Ronja the Robber's Daughter
Ronja is the only daughter of Mattis, a bandit leader who lives in a castle in the middle of a large forest. When Ronja grows old enough, she ventures into…
Genre: Action & Adventure, Animation, Comedy, Family, Fantasy
The world's most beloved animated characters as precocious preschoolers, discovering the world one baby step at a time. KissAnime Review: Really fantastic TV series, my favorite in animation category. Baby…
Argevollen
The story takes place in a world where two countries, Arandas and Ingelmia, have been warring against each other for a very long time. Tokimune, a young man belonging to…
Status: In Production
Danny Phantom is an American animated television series created by Butch Hartman for Nickelodeon, produced by Billionfold Studios. It was distributed by Nelvana, a Canadian animation company, as was The…
Sket Dance is a manga series written and illustrated by Kenta Shinohara and serialized, beginning in July 2007, in Shueisha's manga magazine Weekly Shōnen Jump. Sket Dance won the 55th…
Set in a ruined medieval city called Dreamland, Disenchantment follows the grubby adventures of a hard-drinking princess, her feisty elf companion and her personal demon. KissAnime Review: My favorite TV…
Dragon Ball Z Kai
Rejoin Goku and his friends in a series of cosmic battles! Toei has redubbed, recut, and cleaned up the animation of the original 1989 animated series. The show's story arc…
Yukihira Souma's dream is to become a full-time chef in his father's restaurant and surpass his father's culinary skill. But just as Yukihira graduates from middle schools his father, Yukihira…


text:
Home News Airport commissioners approve new gas station plans
Airport commissioners approve new gas station plans
Rich Saltzberg
The vacant lot in the Airport Business Park where a gas station, convenience store and car wash are proposed. - Rich Saltzberg
On the recommendation of their Land Use Subcommittee, the Martha's Vineyard Airport Commission voted 3-1 in favor of approving construction plans for North Line Shell, a gas station-convenience store-car wash plaza at Airport Business Park Lot 33, site of the recently razed Airport Mobil.
The Thursday-afternoon decision cued up Depot Corner Inc., the lessee of the lot, for further review by the Martha's Vineyard Commission. The proposed Shell station will include a 2,925-square-foot convenience store, a 792-square-foot car wash, and three subterranean fuel storage tanks with an aggregate capacity of 37,000 gallons.
Clarence "Trip" Barnes was the lone dissenter. Mr. Barnes argued vehemently for the inclusion of oil-change facilities at the station.
"I can't vote for this unless it's in there," Mr. Barnes said. "That's what was there before, and that's what should be on there now."
Lou Paciello, Depot Corner president, said there is insufficient volume. "In my business plan, it does not make money," he said.
Tetra Tech engineer Ron Myrick later summarized a pre-decommissioning inspection report of the former Airport Mobil that had been asked for by airport manager Ann Richart.
"No evidence of groundwater impacts associated with the operations," Mr. Myrick said. "There were some low-level impacts around the old tanks — not problematic levels, some low-level impacts on the surface in the area of the old diesel dispenser, and that's it."
Chuck September 15, 2017 at 5:04 am
This was so predictable …and Trip Barnes is so correct. The previous lease holder provided a portfolio of services,needed by the community, which produced a sustaining total operating profit. To cherry pick the services offered to maximize that profit should should raise the ire of all the Commission members. This whole process was just above the skill level of the Commission.
WashAbhored September 15, 2017 at 12:34 pm
There are a bunch of places to get oil changes on the Vineyard including many shops that don't benefit from the huge profits the gas places make. If their bid didn't include oil changes and it was accepted then they have no obligation to provide them.
Mo Fuzz September 16, 2017 at 7:23 am
Are there any places where you can get an oil change while you wait? All the places I know of, you have to drop the car off in the morning and come back at the end of the day
WashAbhored September 16, 2017 at 9:36 am
Yes, several will allow you to wait and there's even a service that will come to your home/work and do the oil change right there for you.
Onecheapbasterd September 16, 2017 at 9:11 am
Since when is any business obligated to build what a commissioner wants? Is this not the same person that did the video presentation showing how the roundabout would not work for trucks?
Previous articleBridgette with the henna
Next articleAquinnah employee escorted out for working OT


text:
Henoch ist im Alten Testament der Sohn Kains und Enkel Adams.

Dieser Henoch ist nicht zu verwechseln mit Henoch, dem Nachkommen Sets .

Etymologie
Die Herkunft des Personennamens  (in  חֲנֹךְ ḥǎnokh) ist unsicher. Hans Rechenmacher leitet den Namen von kanaanäisch ḫanāku "Gefolgsmann" ab und nimmt an, dass es sich um die Kurzform einer Genitivverbindung bestehend aus Grundwort und Bestimmungswort handelt. Das Bestimmungswort und zugleich theophore Element sei ausgefallen, übrig sei noch das Grundwort "Gefolgsmann". Der Name bedeute daher "Gefolgsmann (Gottes)". Inhaltlich lassen sich dazu die mit  gebildeten Namen vergleichen (z. B. ). Martin Noth hingegen geht von der Wurzel ḤNK aus, vergleicht arabisch ḥunukun und übersetzt den Namen als "klug, gelehrt". Schließlich kommt eine Ableitung des Namens von der Wurzel חנך ḥnk "einweihen" in Betracht, sodass der Name als "Einweiher / Gründer" übersetzt werden könnte.

Die Septuaginta gibt den Namen als Ενωχ Enōch wieder, die Vulgata als Enoch und der Samaritanische Pentateuch als 'Īnok.

Biblischer Bericht
Nach  ist Henoch der erstgeborene Sohn Kains. Seine Mutter ist eine Frau, die Kain im Land Nod geheiratet hatte. Nach Henoch wird eine Stadt benannt, die sein Vater Kain gründete. Nach  ist Henoch der Vater Irads.

Traditionelle Auslegung
Wenn Adam und Eva die ersten Menschen sind, stellt sich die Frage, wen Kain dann zur Frau genommen habe, und wer die Mutter Henochs sei. Diese Frage wird dahingehend beantwortet, dass Adam und Eva nach Kain, Abel und Set auch noch Töchter bekommen hätten, Kain also seine eigene Schwester geheiratet hätte. So wird es auch im Buch der Jubiläen 4,1.9 dargestellt, wo die Mutter Henochs Awan genannt wird.

Historisch-kritische Auslegung
Die Person des Henoch wird in nichtpriesterschriftlicher Überlieferung als Sohn des Kain bezeichnet, während sie innerhalb der Priesterschrift an siebter Stelle in der Genealogie von Seth bis Noach erscheint. Dabei wird davon ausgegangen, dass die eine Person des Henoch jeweils an verschiedenen Stellen in die Genealogie eingeordnet wurde.

Die Henochüberlieferung ist ursprünglich unabhängig von der Erzählung von Adam und Eva. Folglich setzt sie nicht eine sehr geringe Anzahl von Menschen auf der Erde voraus, sondern eine Anzahl von Völkern. So kann Kain eine Frau aus einem anderen Volk heiraten. Auch, dass Kain in  in das Land Nod weggeht, setzt bereits voraus, dass dort ein Volk lebt, bei dem er sich ansiedeln kann.

Literatur

Art. חֲנוֹךְ, In: Gesenius, 18. Aufl. 2013, S. 370.
Martin Noth: Die israelitischen Personennamen im Rahmen der gemeinsemitischen Namengebung, Stuttgart: Kohlhammer, 1928, S. 228.243.
Hans Rechenmacher: Althebräische Personennamen. Münster 2012, S. 163.204.

Person im Buch Genesis


text:
MAFormViewController
==================

##Note - This project is no longer being actively supported. Thanks everyone for their contributions!

MAFormViewController is designed to be used in tandem with MATextFieldCells for extremely quick and easy UITableView-based form creation that automatically handles form configuration, formatting, navigation, validation, and submission.

Usage
=====

Drop in the MAFormViewController.h/m, MAFormField.h/m, and MATextFieldCell.h/m into your project, and import MAFormViewController.h and MAFormField.h into whatever view controller you want to display the form from.

MAFormViewControllers are made up of arrays of MAFormField objects - which in a single line of code encapsulates lots of annoying code and logic that you won't need to write over and over again. The key is used later to identify the information the user entered for each field, the type (see below for more details) defines the type of form field that will be created, which includes keyboard types, data domain restrictions, an initial value in case you want to pre-propulate fields in the form with existing data, a placeholder for each field, a BOOL to tell the form whether or not you want to have the placeholders "animate" above the textfield so the users can see what they're editing after there's text present, and a BOOL used when we validate the form to ensure (or not) that field has an entry present.

Later you can group these MAFormField objects into arrays, representing a section of a form. A form can consist of any number of sections and fields. All you need to do is create all the fields, group them how you'd prefer, and pass them to the MAFormViewController's custom init method, which accepts the fields you created, a title for the button used to submit/send/save the form information, and a block that will pass back a dictionary representation of the form using the keys you provided when creating the fields as the keys in the dictionary, and the values the user entered into the fields as the values associated with those keys.

```js
MAFormField *nameField = [MAFormField fieldWithKey:@"name" type:MATextFieldTypeName initialValue:nil placeholder:@"Name" required:YES];
MAFormField *usernameField = [MAFormField fieldWithKey:@"username" type:MATextFieldTypeName initialValue:nil placeholder:@"Username" required:YES];
MAFormField *passwordField = [MAFormField fieldWithKey:@"password" type:MATextFieldTypePassword initialValue:nil placeholder:@"Password" required:YES];

// separate the cells into sections
NSArray *firstSection = @[nameField];
NSArray *secondSection = @[usernameField, passwordField];
NSArray *cellConfig = @[firstSection, secondSection];

// create the form and present it modally with its own navigation controller
MAFormViewController *formVC = [[MAFormViewController alloc] initWithCellConfigurations:cellConfig actionText:@"Save" animatePlaceholders:YES handler:^(NSDictionary *resultDictionary) {
    // now that we're done, dismiss the form
    [self dismissViewControllerAnimated:YES completion:nil];

    // if we don't have a result dictionary, the user cancelled, rather than submitted the form
    if (!resultDictionary) {
        return;
    }

    // do whatever you want with the results - you can access specific values from the dictionary using
    // the key you provided when you created the form
    [[[UIAlertView alloc] initWithTitle:nil message:[NSString stringWithFormat:@"Thanks for registering %@!", resultDictionary[@"name"]] delegate:nil cancelButtonTitle:@"Yay!" otherButtonTitles:nil] show];
}];

    // optional - disable the unsaved changes warning
    // formVC.warnForUnsavedChanges = NO;

    // optional - override the default unsaved changes message
    // formVC.unsavedChangesMessage = @"WAIT!!! You have unsaved changes!!";

UINavigationController *formNC = [[UINavigationController alloc] initWithRootViewController:formVC];
[self presentViewController:formNC animated:YES completion:nil];
```

Coupling the convenience and ease of MAFormViewControllers and MATextFieldCells, takes away the need for you to handle the most annoying and repetitive tasks associated with creating forms. All of the following is done automatically for you:

- Creating and configuring all of the textfields and table views
- Automatically bringing up the keyboard and setting focus on the first cell for quick editing.
- Setting the correct keyboard type for the data domain of the field.
- Setting the correct return key type (Next or Done) depending on if the field is the last in the form or not.
- Navigation from cell to cell using the keyboard so users don't need to tap on the next cell to continue filling out the form.
- Dismissing the keyboard when the last field is done being edited.
- Validation of required fields - ensuring they aren't left blank, telling users which fields are missing and setting the focus on the cell with the empty value.
- Warning users about unsaved changes before dismissing the form when they hit cancel.
- Packaging up all of the form data into a dictionary for ease-of-use.

With Animated Placeholders:
=====

![demo](Screenshots/form_demo_animated_placeholders.gif)

Without Animated Placeholders:
=====

![demo](Screenshots/form_demo.gif)

MATextFieldTypes and Standard Settings
=====

**Default**
- auto-capitalize new sentences
- auto-correction on
- default keyboard

**Name**
- auto-capitalize all words
- auto-correction off
- default keyboard

**Phone**
- number pad keyboard
- auto-formats text following format: (xxx) xxx-xxxx
- adds toolbar to keyboard for action button

**Email**
- auto-capitalize off
- auto-correction off
- email keyboard

**Address**
- auto-capitalize all words
- auto-correction on
- default keyboard

**StateAbbr**
- auto-capitalize all characters
- auto-correction off
- default keyboard
- 2 character limit

**ZIP**
- number pad keyboard
- 5 digit limit

**Number**
- number pad keyboard

**Decimal**
- decimal pad keyboard

**Date**
- number pad keyboard
- automatically formats entry to "MM/DD/YYYY" format

**Password**
- auto-capitalize off
- auto-correction off
- default keyboard
- secure text entry (characters are obfuscated)

**URL**
- auto-capitalize off
- auto-correction off
- URL keyboard

**NonEditable**
- disable the textfield - for display/informational purposes

MATextFieldActionTypes
=====

**None**
- does not create action bar/button, and uses default return key type for keyboard. Hitting 'return' on the keyboard does nothing.

**Next**
- if the field type requires a numeric keyboard and therefore has no 'return key', a toolbar is created and added as an input accessory view to the keyboard with "Next" as the title. You'll want to be sure you also implement the expected action (switch which textfield is the firstResponder) inside the actionBlock property as shown above.
- if the field type has a keyboard that includes a 'return key', the return key type is set to "Next". You'll want to be sure you also implement the expected action (switch which textfield is the firstResponder) inside the actionBlock property as shown above.

**Done**
- if the field type requires a numeric keyboard and therefore has no 'return key', a toolbar is created and added as an input accessory view to the keyboard with "Done" as the title. You'll want to be sure you also implement the expected action (resign the appropriate first responder) inside the actionBlock property as shown above.
- if the field type has a keyboard that includes a 'return key', the return key type is set to "Done". You'll want to be sure you also implement the expected action (resign the appropriate first responder) inside the actionBlock property as shown above.

Testing
=====

Open the project in Xcode, select the simulator and hit command-U.

Community
=====

Questions, comments, issues, and pull requests welcomed!! Thanks to Whelton for the CocoaPods integration, and thanks to jverdi's awesome project (https://github.com/jverdi/JVFloatLabeledTextField) for inspiration on the animated placeholders.

License
=====

This project is made available under the MIT license. See LICENSE.txt for details.

[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/mamaral/maformviewcontroller/trend.png)](https://bitdeli.com/free "Bitdeli Badge")


text:
TECH360.TV
Tech Fights Covid
#techmewithyou
Tech360.tv Official
Home Events CES 2021 HARMAN ExPLORE 2021: JBL Celebrates 75 Years With Huge New Product Lineup
HARMAN ExPLORE 2021: JBL Celebrates 75 Years With Huge New Product Lineup
With JBL's 75th anniversary this year, HARMAN ExPLORE 2021 focused mostly on the additions to JBL's personal audio lineup. Here are the cool new products you can expect from JBL in the upcoming months.
1. JBL Tour Series
Credit: JBL
With the over-ear JBL Tour ONE headphones and in-ear JBL Tour Pro+ true wireless earbuds in this lineup, there's something for everyone, and both products are even CES 2021 Innovation Awards "Best of Innovation" honourees.
Both products offer Active Noise Cancellation (ANC), and the JBL Tour ONE headphones boasts 50 hours of total playback time, supports high-res audio playback and has a new Smart Audio mode that allows users to optimise the Bluetooth connection for "normal listening", "music mode" and "video mode".
The JBL Tour Pro+ on the other hand, has over 30 hours of battery life, Qi wireless charging and more. The JBL Tour Series will be available at major retailers and on JBL.com on 30 May 2021, although it remains to be seen when it will come to Singapore. The JBL Tour ONE will cost US$299.95 while the JBL Tour Pro+ will cost US$199.95.
2. JBL LIVE Series
The LIVE Series is getting four new additions, with the JBL LIVE PRO+, JBL LIVE FREE NC+, JBL LIVE 660NC and JBL LIVE 460NC all set to be available in America from 14 March 2021 onwards.
The JBL LIVE PRO+ are in-ear true wireless earbuds that feature a stem design, and comes with Adaptive noise cancelling, Qi wireless charging, IPX4 water resistance and up to seven hours of battery life in the earbuds and a total of 28 hours with the charging case. The earbuds will retail at US$179.95.
The JBL LIVE FREE NC+ are also true wireless earbuds, but with the bud design instead. These have slightly less overall battery life at a total of 21 hours, but the water resistance rating is bumped up to IPX7. The earbuds will cost US$149.95.
If you prefer over-ear headphones though, the JBL LIVE 660NC might be for you. With up to 40 hours of battery life with noise cancelling and 50 hours without, these headphones will definitely last. There's also multipoint connectivity, which is great for working across two devices. The headphones will be priced at US$199.95.
Last but not least, the JBL LIVE 460NC are the most economical offering in the updated LIVE Series. At US$129.95, you get Adaptive noise cancelling, multipoint connectivity, up to 50 hours of playback time and more.
3. JBL Reflect Mini NC TWS
This new addition to the Reflect Series is definitely suitable for those who lead a more active lifestyle. The Reflect Mini NC TWS are the first earbuds in the Reflect Series to come equipped with Active Noise Cancelling, so that's a major plus for fitness enthusiasts who want to block out the world as they hit up the gym.
With up to 21 hours of playback time, the earbuds also come with rubber fins which ensure they sit securely in the ear no matter how vigorous the exercise session. The earbuds will be available Spring 2021 at US$149.95.
4. JBL Bar 5.0 MultiBeam
The JBL Bar 5.0 MultiBeam is the newest soundbar from JBL, offering 3D surround sound by combining Virtual Dolby Atmos and JBL's proprietary beamforming technology, MultiBeam. The all-in-one soundbar works with iOS and Android devices, and is equipped with four passive radiators, which JBL claims eliminates the need for an additional subwoofer.
The soundbar will retail for US$399.95 in Spring 2021.
5. JBL Charge 5 Bluetooth Speaker
If you're looking for a smaller, more portable speaker though, look no further than the Charge 5. The successor to the popular Charge 4, the Charge 5 has improved IP67 water and dust resistance rating, but still offers up to 20 hours of playtime and there's still the built-in 7,500mAh powerbank that allows the speaker to charge external devices like handphones.
The JBL Charge 5 will be available on JBL's website and select retailers starting in April 2021 for US$179.95.
6. JBL TUNE Series
If you thought "that's a lot of products", well, JBL has even more products coming to the TUNE Series, with two in-ear earphones and two on-ear headphones. The new products in the TUNE Series will be available from 14 March 2021 onwards.
The JBL TUNE 660NC has up to 50 hours of battery life, Active Noise Cancelling, multipoint connectivity and runs on Bluetooth 5.0, all for the extremely affordable price of just US$99.95.
Coming in at an even more unbelievable price, the JBL TUNE 510BT is priced at just US$49.95, but offers up to 40 hours of battery life, multipoint connectivity, Bluetooth 5.0 and fast charging, with a five-minute charge providing two hours of playback time.
If you prefer earbuds though, the JBL TUNE 215BT comes in a neckband design that ensures you won't lose the earbuds. There's a 16-hour battery life here, with multipoint connectivity and Bluetooth 5.0 for just US$39.95.
And finally, the JBL TUNE 125BT is pretty similar to the 215BT, but comes with magnetic earbuds for easy cable management and has silicone ear tips instead of the open-fit design that the 215BT has. The 125BT is also priced at US$39.95.
JBL has definitely come a long way, and it's exciting to see how much they're offering in their products at a relatively affordable price. Here's to hoping these come to Singapore soon!
Previous articleThis Is How WhatsApp's New TOS Will Affect You
Next articleTrump Banned On Social Media Platforms After Capitol Siege
Cheryl Tan
© 2018 tech360.tv. All rights reserved.


A really romantic pre-wedding photo shoot under the bright sky of Santorini.
We had the chance to photograph the engagement of this lovely couple one year before their destination wedding.
They got carried away by each other and we as photographers couldn't ask for more!
We took them around with the best background, like the blue domes and the blue sea.
They're beautiful and young and we love them already!


text:
The second big screen outing for the Aardman favourite will delight kids and adults alike
The loveable fluffball is baa-ck on the big screen in a pristinely shambolic stop-motion romp from Aardman Animations that offers an idiosyncratic skew on extra-terrestrial encounters. Introduced in 1995's Wallace and Gromit short A Close Shave, before being granted his own 2007 spin-off series, the anarchy of Shaun and co was elevated to an artform in 2015's Shaun the Sheep Movie. Farmageddon follows up fittingly, blending the familiar with the ambitious as it looks to the stars and finds blue rabbits.
Directed by Will Becher and Richard Phelan, making their feature debut, it's in safe hands: Becher was a regular on the series. Down at the farm on the outskirts of Mossingham, the gang of dimwitted sheep, presided over by smarter-than-average Shaun, are getting stuck into some seriously dangerous mischief that puts them at odds with sensible sheepdog Bitzer. Their death-defying antics are interrupted when an alien craft brings a curious visitor to up the ante, with the sinister bods from the Ministry for Alien Detection close behind.
As ever, save the odd mumble, it's a dialogue-free affair. Unafraid of the C-word, cuteness abounds, lovingly moulded in the studio's inimitable claymation style, in service of a rattling good yarn. And, plump of cheek, wide of eye and pink of ear, the otherworldly Lu-Lu is a gorgeously marketable creation who kids will adore.
Featuring lost chips, crap fairground attractions and aghast snails, what's so wonderful about Farmageddon – which is absolutely typical of an Aardman animation – is the deft way it crowd-pleases across the ages, stretching from tiny tots to those who'll remember 1977's Close Encounters in the cinema. With adult sci-fi references aplenty accompanying the splendid craftsmanship, eyes seem firmly on Oscar's Best Animation prize. It might be too bafflingly British for that (though the studio have bagged it before and its predecessor was nominated), but it's easily got the (lamb) chops to be a contender. Not bad for a little lad from Mossy Bottom.
General release from Fri 18 Oct.
Directed by: Richard Starzak
Cast: Justin Fletcher, John Sparkes
UK release: 18 October 2019
Shaun and his gang of sheep are getting up to mischief that puts them at odds with sensible sheepdog Bitzer; then an alien craft arrives. This dialogue-free pristinely shambolic stop-motion romp is, in its deft way, a crowd-pleaser for everyone, with enough Close Encounters references to keep parents interested.
Joe Sugg cast in Shaun the Sheep sequel(4 Jul 2019)
Film gossip
Asterix: The Secret of the Magic Potion (26 Aug 2019)
The beloved comic book characters return for another computer-animated adventure
The Good Dinosaur (24 Nov 2015)
Pixar's latest follows a young dinosaur on an epic journey of friendship and discovery
The Man from U.N.C.L.E (11 Aug 2015)
Stylish but poorly scripted spy caper starring Henry Cavill and Armie Hammer
Inside Out (20 May 2015)
Cannes 2015: Truthful and delightful Pixar animation, with Amy Poehler
Shaun the Sheep Movie (2 Feb 2015)
This big screen adaptation of the stop-motion kids' TV show boasts bags of charm
Night at the Museum: Secret of the Tomb (15 Dec 2014)
Ben Stiller and co return for the third film in Shawn Levy's family friendly franchise
Penguins of Madagascar (1 Dec 2014)
Knockabout spin-off featuring the voices of John Malkovich and Benedict Cumberbatch
Joe Sugg
John Sparkes
Justin Fletcher
Richard Phelan
Will Becher
Kids newsletter
Something for the Kids – packed full of great family-friendly places to go and things to do


Cool Retina Wallpapers (iOS) - Funky-cool HD wallpaper by the million ~ Android Forever!
Don't worry iOS-lovers, you won't be left out on Barry's Best! Since I'm bi-phonal I will always remember you! Here's a gargantuan collection of hi-resolution wallpapers for your sweet iOS device's screen. These are truly cool, just as the app's name implies!
Cool Retina Wallpapers is FREE in the App Store!