15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/**
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Copyright (c) 2012 The Chromium Authors. All rights reserved.
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * Use of this source code is governed by a BSD-style license that can be
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) * found in the LICENSE file.
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) **/
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Checking for "chrome.app.runtime" availability allows this Chrome app code to
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// be tested in a regular web page (like tests/manual.html). Checking for
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// "chrome" and "chrome.app" availability further allows this code to be tested
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// in non-Chrome browsers, which is useful for example to test touch support
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// with a non-Chrome touch device.
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)if (typeof chrome !== 'undefined' && chrome.app && chrome.app.runtime) {
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  var showCalculatorWindow = function () {
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    chrome.app.window.create('calculator.html', {
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      defaultWidth: 243, minWidth: 243, maxWidth: 243,
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      defaultHeight: 380, minHeight: 380, maxHeight: 380,
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      id: 'calculator'
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }, function(appWindow) {
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      appWindow.contentWindow.onload = function() {
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        new Controller(new Model(9), new View(appWindow.contentWindow));
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      };
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      chrome.storage.local.set({windowVisible: true});
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      appWindow.onClosed.addListener(function() {
252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        chrome.storage.local.set({windowVisible: false});
262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      });
272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    });
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  chrome.app.runtime.onLaunched.addListener(showCalculatorWindow);
312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  chrome.app.runtime.onRestarted.addListener(function() {
322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    chrome.storage.local.get('windowVisible', function(data) {
332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (data.windowVisible)
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        showCalculatorWindow();
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    });
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  });
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)function Controller(model, view) {
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.inputs = this.defineInputs_();
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.model = model;
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.view = view;
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.view.onButton = function(button) {
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.handleInput_(this.inputs.byButton[button]);
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }.bind(this);
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.view.onKey = function(key) {
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    this.handleInput_(this.inputs.byKey[key]);
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }.bind(this);
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** @private */
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Controller.prototype.defineInputs_ = function() {
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var inputs = {byButton: {}, byKey: {}};
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  inputs.byButton['zero'] = inputs.byKey['48'] = '0';
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  inputs.byButton['one'] = inputs.byKey['49'] = '1';
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  inputs.byButton['two'] = inputs.byKey['50'] = '2';
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  inputs.byButton['three'] = inputs.byKey['51'] = '3';
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  inputs.byButton['four'] = inputs.byKey['52'] = '4';
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  inputs.byButton['five'] = inputs.byKey['53'] = '5';
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  inputs.byButton['six'] = inputs.byKey['54'] = '6';
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  inputs.byButton['seven'] = inputs.byKey['55'] = '7';
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  inputs.byButton['eight'] = inputs.byKey['56'] = '8';
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  inputs.byButton['nine'] = inputs.byKey['57'] = '9';
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  inputs.byButton['point'] = inputs.byKey['190'] = '.';
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  inputs.byButton['add'] = inputs.byKey['^187'] = '+';
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  inputs.byButton['subtract'] = inputs.byKey['189'] = '-';
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  inputs.byButton['multiply'] = inputs.byKey['^56'] = '*';
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  inputs.byButton['divide'] = inputs.byKey['191'] = '/';
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  inputs.byButton['equals'] = inputs.byKey['187'] = inputs.byKey['13'] = '=';
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  inputs.byButton['negate'] = inputs.byKey['32'] = '+ / -';
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  inputs.byButton['clear'] = inputs.byKey['67'] = 'AC';
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  inputs.byButton['back'] = inputs.byKey['8'] = 'back';
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return inputs;
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** @private */
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Controller.prototype.handleInput_ = function(input) {
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var values, accumulator, operator, operand;
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (input) {
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    values = this.model.handle(input);
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    accumulator = values.accumulator;
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    operator = values.operator;
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    operand = values.operand;
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (input === 'AC') {
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.view.clearDisplay({operand: '0'});
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (input === '=') {
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.view.addResults({accumulator: accumulator, operand: accumulator});
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (input.match(/^[+*/-]$/)) {
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.updateValues_({accumulator: accumulator});
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.view.addValues({operator: values.operator});
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else if (!this.updateValues_({operator: operator, operand: operand})) {
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      this.view.addValues({operator: operator, operand: operand});
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** @private */
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Controller.prototype.updateValues_ = function(values) {
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Values which are "finalized" (which have an accumulator value) shouldn't
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // and won't be updated, and this method will return false for them.
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var before = this.view.getValues();
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var after = !before.accumulator ? values : {};
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  this.view.setValues({
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    accumulator: this.getUpdatedValue_(before, after, 'accumulator'),
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    operator: this.getUpdatedValue_(before, after, 'operator'),
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    operand: this.getUpdatedValue_(before, after, 'operand', !before.operator)
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  });
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return !before.accumulator;
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)/** @private */
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)Controller.prototype.getUpdatedValue_ = function(before, after, key, zero) {
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  var value = (typeof after[key] !== 'undefined') ? after[key] : before[key];
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return zero ? (value || '0') : value;
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
116