|  | @@ -22,6 +22,7 @@ namespace display {
 | 
	
		
			
				|  |  |      element: HTMLElement;
 | 
	
		
			
				|  |  |      state: state.StateMachine;
 | 
	
		
			
				|  |  |      observer: state.Observer;
 | 
	
		
			
				|  |  | +    remove: () => void;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      constructor(kana: string, state: state.StateMachine) {
 | 
	
		
			
				|  |  |        this.state = state;
 | 
	
	
		
			
				|  | @@ -81,67 +82,53 @@ namespace display {
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -  class RomajiDisplayComponent implements Component {
 | 
	
		
			
				|  |  | -    element: HTMLElement;
 | 
	
		
			
				|  |  | -    state: state.StateMachine;
 | 
	
		
			
				|  |  | +  export class RomajiDisplayController {
 | 
	
		
			
				|  |  |      observer: state.Observer;
 | 
	
		
			
				|  |  | +    inputState: InputState | null;
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    constructor(state: state.StateMachine) {
 | 
	
		
			
				|  |  | -      this.state = state;
 | 
	
		
			
				|  |  | -      this.observer = result => this.rerender(result);
 | 
	
		
			
				|  |  | -      this.state.addObserver(this.observer);
 | 
	
		
			
				|  |  | -      this.element = document.createElement('span');
 | 
	
		
			
				|  |  | -      this.element.classList.add('romaji');
 | 
	
		
			
				|  |  | -      this.element.textContent = this.state.getDisplay();
 | 
	
		
			
				|  |  | +    constructor(
 | 
	
		
			
				|  |  | +      readonly firstElement: HTMLElement,
 | 
	
		
			
				|  |  | +      readonly restElement: HTMLElement
 | 
	
		
			
				|  |  | +    ) {
 | 
	
		
			
				|  |  | +      this.observer = (result) => this.rerender(result);
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    rerender(result: TransitionResult): void {
 | 
	
		
			
				|  |  | -      switch (result) {
 | 
	
		
			
				|  |  | -        case TransitionResult.FAILED:
 | 
	
		
			
				|  |  | -          this.element.classList.remove('error');
 | 
	
		
			
				|  |  | -          this.element.offsetHeight; // trigger reflow
 | 
	
		
			
				|  |  | -          this.element.classList.add('error');
 | 
	
		
			
				|  |  | -          break;
 | 
	
		
			
				|  |  | -        case TransitionResult.SUCCESS:
 | 
	
		
			
				|  |  | -        case TransitionResult.FINISHED:
 | 
	
		
			
				|  |  | -          this.element.textContent = this.state.getDisplay();
 | 
	
		
			
				|  |  | -          break;
 | 
	
		
			
				|  |  | +    setInputState(inputState: InputState) {
 | 
	
		
			
				|  |  | +      this.clearObservers();
 | 
	
		
			
				|  |  | +      this.inputState = inputState;
 | 
	
		
			
				|  |  | +      if (this.inputState != null) {
 | 
	
		
			
				|  |  | +        this.inputState.map((_, machine) => {
 | 
	
		
			
				|  |  | +          machine.addObserver(this.observer);
 | 
	
		
			
				|  |  | +        });
 | 
	
		
			
				|  |  | +        this.rerender(TransitionResult.SUCCESS);
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        this.firstElement.textContent = '';
 | 
	
		
			
				|  |  | +        this.restElement.textContent = '';
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    destroy(): void {
 | 
	
		
			
				|  |  | -      this.state.removeObserver(this.observer);
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -  }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -  export class RomajiDisplayController implements Component {
 | 
	
		
			
				|  |  | -    children: KanaDisplayComponent[];
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    constructor(readonly element: HTMLElement) {
 | 
	
		
			
				|  |  | -      this.children = [];
 | 
	
		
			
				|  |  | -    }
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    setInputState(inputState: InputState) {
 | 
	
		
			
				|  |  | -      this.clearChildren();
 | 
	
		
			
				|  |  | -      if (inputState == null) {
 | 
	
		
			
				|  |  | -        this.children = [];
 | 
	
		
			
				|  |  | -      } else {
 | 
	
		
			
				|  |  | -        this.children = inputState.map((_, machine) => {
 | 
	
		
			
				|  |  | -          return new RomajiDisplayComponent(machine);
 | 
	
		
			
				|  |  | +    private clearObservers(): void {
 | 
	
		
			
				|  |  | +      if (this.inputState != null) {
 | 
	
		
			
				|  |  | +        this.inputState.map((_, machine) => {
 | 
	
		
			
				|  |  | +          machine.removeObserver(this.observer);
 | 
	
		
			
				|  |  |          });
 | 
	
		
			
				|  |  | -        this.children.forEach(child => this.element.appendChild(child.element));
 | 
	
		
			
				|  |  |        }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -    private clearChildren(): void {
 | 
	
		
			
				|  |  | -      this.children.forEach(child => {
 | 
	
		
			
				|  |  | -        child.destroy();
 | 
	
		
			
				|  |  | -        this.element.removeChild(child.element);
 | 
	
		
			
				|  |  | -      });
 | 
	
		
			
				|  |  | +    rerender(result: TransitionResult): void {
 | 
	
		
			
				|  |  | +      if (result === TransitionResult.FAILED) {
 | 
	
		
			
				|  |  | +        this.firstElement.classList.remove('error');
 | 
	
		
			
				|  |  | +        this.firstElement.offsetHeight; // trigger reflow
 | 
	
		
			
				|  |  | +        this.firstElement.classList.add('error');
 | 
	
		
			
				|  |  | +      } else {
 | 
	
		
			
				|  |  | +        let remaining = this.inputState.getRemainingInput();
 | 
	
		
			
				|  |  | +        this.firstElement.textContent = remaining.charAt(0);
 | 
	
		
			
				|  |  | +        this.restElement.textContent = remaining.substring(1);
 | 
	
		
			
				|  |  | +      }
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      destroy(): void {
 | 
	
		
			
				|  |  | -      this.clearChildren();
 | 
	
		
			
				|  |  | +      this.clearObservers();
 | 
	
		
			
				|  |  |      }
 | 
	
		
			
				|  |  |    }
 | 
	
		
			
				|  |  |  
 |