chromium/chrome/test/data/extensions/calculator_app/app/view.js

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

function View(window) {
  this.display = window.document.querySelector('#calculator-display');
  this.buttons = window.document.querySelectorAll('#calculator-buttons button');
  window.addEventListener('keydown', this.handleKey_.bind(this));
  Array.prototype.forEach.call(this.buttons, function(button) {
    button.addEventListener('click', this.handleClick_.bind(this));
    button.addEventListener('mousedown', this.handleMouse_.bind(this));
    button.addEventListener('touchstart', this.handleTouch_.bind(this));
    button.addEventListener('touchmove', this.handleTouch_.bind(this));
    button.addEventListener('touchend', this.handleTouchEnd_.bind(this));
    button.addEventListener('touchcancel', this.handleTouchEnd_.bind(this));
  }, this);
}

View.prototype.clearDisplay = function(values) {
  this.display.innerHTML = '';
  this.addValues(values);
};

View.prototype.addResults = function(values) {
  this.appendChild_(this.display, null, 'div', 'hr');
  this.addValues(values);
};

View.prototype.addValues = function(values) {
  var equation = this.makeElement_('div', 'equation');
  this.appendChild_(equation, null, 'span', 'accumulator', values.accumulator);
  this.appendChild_(equation, null, 'span', 'operation');
  this.appendChild_(equation, '.operation', 'span', 'operator');
  this.appendChild_(equation, '.operation', 'span', 'operand', values.operand);
  this.appendChild_(equation, '.operator', 'div', 'spacer');
  this.appendChild_(equation, '.operator', 'div', 'value', values.operator);
  this.setAttribute_(equation, '.accumulator', 'aria-hidden', 'true');
  this.display.appendChild(equation).scrollIntoView();
};

View.prototype.setValues = function(values) {
  var equation = this.display.lastElementChild;
  this.setContent_(equation, '.accumulator', values.accumulator || '');
  this.setContent_(equation, '.operator .value', values.operator || '');
  this.setContent_(equation, '.operand', values.operand || '');
};

View.prototype.getValues = function() {
  var equation = this.display.lastElementChild;
  return {
    accumulator: this.getContent_(equation, '.accumulator') || null,
    operator: this.getContent_(equation, '.operator .value') || null,
    operand: this.getContent_(equation, '.operand') || null,
  };
};

/** @private */
View.prototype.handleKey_ = function(event) {
  this.onKey.call(this, event.shiftKey ? ('^' + event.which) : event.which);
}

/** @private */
View.prototype.handleClick_ = function(event) {
  this.onButton.call(this, event.target.dataset.button)
}

/** @private */
View.prototype.handleMouse_ = function(event) {
  event.target.setAttribute('data-active', 'mouse');
}

/** @private */
View.prototype.handleTouch_ = function(event) {
  event.preventDefault();
  this.handleTouchChange_(event.touches[0]);
}

/** @private */
View.prototype.handleTouchEnd_ = function(event) {
  this.handleTouchChange_(null);
}

/** @private */
View.prototype.handleTouchChange_ = function(location) {
  var previous = this.touched;
  if (!this.isInButton_(previous, location)) {
    this.touched = this.findButtonContaining_(location);
    if (previous)
      previous.removeAttribute('data-active');
    if (this.touched) {
      this.touched.setAttribute('data-active', 'touch');
      this.onButton.call(this, this.touched.dataset.button);
    }
  }
}

/** @private */
View.prototype.findButtonContaining_ = function(location) {
  var found;
  for (var i = 0; location && i < this.buttons.length && !found; ++i) {
    if (this.isInButton_(this.buttons[i], location))
      found = this.buttons[i];
  }
  return found;
}

/** @private */
View.prototype.isInButton_ = function(button, location) {
  var bounds = location && button && button.getClientRects()[0];
  var x = bounds && location.clientX;
  var y = bounds && location.clientY;
  var x1 = bounds && bounds.left;
  var x2 = bounds && bounds.right;
  var y1 = bounds && bounds.top;
  var y2 = bounds && bounds.bottom;
  return (bounds && x >= x1 && x < x2 && y >= y1 && y < y2);
}

/** @private */
View.prototype.makeElement_ = function(tag, classes, content) {
  var element = this.display.ownerDocument.createElement(tag);
  element.setAttribute('class', classes);
  element.textContent = content || '';
  return element;
};

/** @private */
View.prototype.appendChild_ = function(root, selector, tag, classes, content) {
  var parent = (root && selector) ? root.querySelector(selector) : root;
  parent.appendChild(this.makeElement_(tag, classes, content));
};

/** @private */
View.prototype.setAttribute_ = function(root, selector, name, value) {
  var element = root && root.querySelector(selector);
  if (element)
    element.setAttribute(name, value);
};

/** @private */
View.prototype.setContent_ = function(root, selector, content) {
  var element = root && root.querySelector(selector);
  if (element)
    element.textContent = content || '';
};

/** @private */
View.prototype.getContent_ = function(root, selector) {
  var element = root && root.querySelector(selector);
  return element ? element.textContent : null;
};