Home | History | Annotate | Download | only in app
      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 
      7 function 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 
     21 View.prototype.clearDisplay = function(values) {
     22   this.display.innerHTML = '';
     23   this.addValues(values);
     24 };
     25 
     26 View.prototype.addResults = function(values) {
     27   this.appendChild_(this.display, null, 'div', 'hr');
     28   this.addValues(values);
     29 };
     30 
     31 View.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 
     43 View.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 
     50 View.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 */
     60 View.prototype.handleKey_ = function(event) {
     61   this.onKey.call(this, event.shiftKey ? ('^' + event.which) : event.which);
     62 }
     63 
     64 /** @private */
     65 View.prototype.handleClick_ = function(event) {
     66   this.onButton.call(this, event.target.dataset.button)
     67 }
     68 
     69 /** @private */
     70 View.prototype.handleMouse_ = function(event) {
     71   event.target.setAttribute('data-active', 'mouse');
     72 }
     73 
     74 /** @private */
     75 View.prototype.handleTouch_ = function(event) {
     76   event.preventDefault();
     77   this.handleTouchChange_(event.touches[0]);
     78 }
     79 
     80 /** @private */
     81 View.prototype.handleTouchEnd_ = function(event) {
     82   this.handleTouchChange_(null);
     83 }
     84 
     85 /** @private */
     86 View.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 */
    100 View.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 */
    110 View.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 */
    122 View.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 */
    130 View.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 */
    136 View.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 */
    143 View.prototype.setContent_ = function(root, selector, content) {
    144   var element = root && root.querySelector(selector);
    145   if (element)
    146     element.textContent = content || '';
    147 };
    148 
    149 /** @private */
    150 View.prototype.getContent_ = function(root, selector) {
    151   var element = root && root.querySelector(selector);
    152   return element ? element.textContent : null;
    153 };
    154