1/** 2 * Copyright (c) 2012 The Chromium Authors. All rights reserved. 3 * Use of this source code is governed by a BSD-style license that can be 4 * found in the LICENSE file. 5 **/ 6 7function View(window) { 8 this.display = window.document.querySelector('#calculator-display'); 9 this.buttons = window.document.querySelectorAll('#calculator-buttons button'); 10 window.addEventListener('keydown', this.handleKey_.bind(this)); 11 Array.prototype.forEach.call(this.buttons, function(button) { 12 button.addEventListener('click', this.handleClick_.bind(this)); 13 button.addEventListener('mousedown', this.handleMouse_.bind(this)); 14 button.addEventListener('touchstart', this.handleTouch_.bind(this)); 15 button.addEventListener('touchmove', this.handleTouch_.bind(this)); 16 button.addEventListener('touchend', this.handleTouchEnd_.bind(this)); 17 button.addEventListener('touchcancel', this.handleTouchEnd_.bind(this)); 18 }, this); 19} 20 21View.prototype.clearDisplay = function(values) { 22 this.display.innerHTML = ''; 23 this.addValues(values); 24}; 25 26View.prototype.addResults = function(values) { 27 this.appendChild_(this.display, null, 'div', 'hr'); 28 this.addValues(values); 29}; 30 31View.prototype.addValues = function(values) { 32 var equation = this.makeElement_('div', 'equation'); 33 this.appendChild_(equation, null, 'span', 'accumulator', values.accumulator); 34 this.appendChild_(equation, null, 'span', 'operation'); 35 this.appendChild_(equation, '.operation', 'span', 'operator'); 36 this.appendChild_(equation, '.operation', 'span', 'operand', values.operand); 37 this.appendChild_(equation, '.operator', 'div', 'spacer'); 38 this.appendChild_(equation, '.operator', 'div', 'value', values.operator); 39 this.setAttribute_(equation, '.accumulator', 'aria-hidden', 'true'); 40 this.display.appendChild(equation).scrollIntoView(); 41}; 42 43View.prototype.setValues = function(values) { 44 var equation = this.display.lastElementChild; 45 this.setContent_(equation, '.accumulator', values.accumulator || ''); 46 this.setContent_(equation, '.operator .value', values.operator || ''); 47 this.setContent_(equation, '.operand', values.operand || ''); 48}; 49 50View.prototype.getValues = function() { 51 var equation = this.display.lastElementChild; 52 return { 53 accumulator: this.getContent_(equation, '.accumulator') || null, 54 operator: this.getContent_(equation, '.operator .value') || null, 55 operand: this.getContent_(equation, '.operand') || null, 56 }; 57}; 58 59/** @private */ 60View.prototype.handleKey_ = function(event) { 61 this.onKey.call(this, event.shiftKey ? ('^' + event.which) : event.which); 62} 63 64/** @private */ 65View.prototype.handleClick_ = function(event) { 66 this.onButton.call(this, event.target.dataset.button) 67} 68 69/** @private */ 70View.prototype.handleMouse_ = function(event) { 71 event.target.setAttribute('data-active', 'mouse'); 72} 73 74/** @private */ 75View.prototype.handleTouch_ = function(event) { 76 event.preventDefault(); 77 this.handleTouchChange_(event.touches[0]); 78} 79 80/** @private */ 81View.prototype.handleTouchEnd_ = function(event) { 82 this.handleTouchChange_(null); 83} 84 85/** @private */ 86View.prototype.handleTouchChange_ = function(location) { 87 var previous = this.touched; 88 if (!this.isInButton_(previous, location)) { 89 this.touched = this.findButtonContaining_(location); 90 if (previous) 91 previous.removeAttribute('data-active'); 92 if (this.touched) { 93 this.touched.setAttribute('data-active', 'touch'); 94 this.onButton.call(this, this.touched.dataset.button); 95 } 96 } 97} 98 99/** @private */ 100View.prototype.findButtonContaining_ = function(location) { 101 var found; 102 for (var i = 0; location && i < this.buttons.length && !found; ++i) { 103 if (this.isInButton_(this.buttons[i], location)) 104 found = this.buttons[i]; 105 } 106 return found; 107} 108 109/** @private */ 110View.prototype.isInButton_ = function(button, location) { 111 var bounds = location && button && button.getClientRects()[0]; 112 var x = bounds && location.clientX; 113 var y = bounds && location.clientY; 114 var x1 = bounds && bounds.left; 115 var x2 = bounds && bounds.right; 116 var y1 = bounds && bounds.top; 117 var y2 = bounds && bounds.bottom; 118 return (bounds && x >= x1 && x < x2 && y >= y1 && y < y2); 119} 120 121/** @private */ 122View.prototype.makeElement_ = function(tag, classes, content) { 123 var element = this.display.ownerDocument.createElement(tag); 124 element.setAttribute('class', classes); 125 element.textContent = content || ''; 126 return element; 127}; 128 129/** @private */ 130View.prototype.appendChild_ = function(root, selector, tag, classes, content) { 131 var parent = (root && selector) ? root.querySelector(selector) : root; 132 parent.appendChild(this.makeElement_(tag, classes, content)); 133}; 134 135/** @private */ 136View.prototype.setAttribute_ = function(root, selector, name, value) { 137 var element = root && root.querySelector(selector); 138 if (element) 139 element.setAttribute(name, value); 140}; 141 142/** @private */ 143View.prototype.setContent_ = function(root, selector, content) { 144 var element = root && root.querySelector(selector); 145 if (element) 146 element.textContent = content || ''; 147}; 148 149/** @private */ 150View.prototype.getContent_ = function(root, selector) { 151 var element = root && root.querySelector(selector); 152 return element ? element.textContent : null; 153}; 154