// 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;
};