Jump To …

calc.coffee

AngularJS Calculator Demo

copyright 2013 - Thom Porter (www.thomporter.com)

Register Application Module

Calc = angular.module('Calc', [])

Register Directive to detect global key press

Calc.directive("bnDocumentKeypress", ($document, $parse) ->
    linkFunction = ($scope, $element, $attributes) ->
      scopeExpression = $attributes.bnDocumentKeypress
      invoker = $parse( scopeExpression )
      $document.on(
        "keyup"
        (event) ->
          $scope.$apply(() ->
            invoker($scope, {$event:event})
          )
      )
      return linkFunction;
)

Register Application Controller

Calc.controller("CalcController", ($scope) ->
  

this is our screen's view:
<div ng-bind-html-unsafe="calcInput" class="screen" />
(I'm not fond of this "unsafe" binding, but it was the only way I could get it to work.)

  $scope.calcInput = '0'
  

Calculator memory storage

  $scope.memory = 0
  

history of input

  $scope.stuff = [0]
  

switch indicating that we just evaluated the input, and are displaying the result.

  $scope.justGotMath = false

method to Accept Input

  $scope.acceptInput = (n) ->
    
    if $scope.justGotMath
        $scope.justGotMath = false
        $scope.stuff = [] if !$scope.isOperator(n)

    n = n.toString()
    if n == '.'
      last = $scope.stuff.pop()
      if $scope.isOperator(last) || Math.ceil(last) != parseInt(last)
        $scope.stuff.push(last)
      else
        last += '.'
        $scope.stuff.push(last)
        $scope.writeScreen()
      return


    if ($scope.isOperator(n))
      return if !$scope.stuff.length
      last = $scope.stuff.pop()
      $scope.stuff.push(last) if !$scope.isOperator(last)
      $scope.stuff.push(n)
    else if $scope.stuff.length
      last = $scope.stuff.pop()
      if $scope.isOperator(last)
        $scope.stuff.push(last)
        $scope.stuff.push(n)
      else if last.toString() == '0'
        $scope.stuff.push(n)
      else
        last += n.toString()
        $scope.stuff.push(last)
    else
      $scope.stuff.push(n)
    $scope.writeScreen()
    console.log $scope.stuff
    @

test for telling if a character is an operator or not

  $scope.isOperator = (n) ->
    if isNaN(n)
      return true if n != '.'
    return false

method to change the sign of the current input

  $scope.changeSign = () ->
    last = $scope.stuff.pop()
    if $scope.isOperator(last)
      second_last = $scope.stuff.pop()
      if second_last.substr(0,1) == '-'
        second_last = substr(1)
      else
        second_last = '-' + second_last
      $scope.stuff.push(second_last)
    else
      if last.substr(0,1) == '-'
        last = substr(1)
      else
        last = '-' + last
    $scope.stuff.push(last)
    $scope.writeScreen()
    

writes output onto the screen.

  $scope.writeScreen = () ->
    write = '';
    $.each($scope.stuff, (i,k)->
      if write == ''
        write = k
      else
        write += k.toString()
    )
    write = '0' if (write == '')
    $scope.calcInput = write.toString();
    return
    
    if $scope.isOperator(n)
      write = if n == '/'
        '&divide;'
      else if n == '*'
        '&times;'
      else if n == '+'
        '&plus;'
      else if n == '-'
        '&minus;'
    if (!$scope.stuff.length)
      $scope.calcInput = write
    else
      $scope.calcInput += write.toString()
    @
    

method to clear the calculator

  $scope.clearCalc = () ->
    $scope.stuff = []
    $scope.writeScreen()

shortcut method to evaluate current input and write to screen.

  $scope.doMath = () ->
    $scope.justGotMath = true
    $scope.stuff = [$scope.getMath()]
    $scope.writeScreen()

evaluate current input and return it.

  $scope.getMath = ->

if the last item in the list is an operator, leave it off, expressions can not end in operators

    last = $scope.stuff.pop()
    if (!$scope.isOperator(last))
      $scope.stuff.push(last)

put the stuff array together and eval for the math

    job = $scope.stuff.join('');
    x = 0
    eval('x = ' + job + ';');
    x
  

event handler to deal with global keypresses

  $scope.handleKeypress = (event) ->
    code = event.which
    console.log code
    if code == 81 # Q = MC
      $scope.memoryClear()
    else if code == 87 # W = M+
      $scope.memoryAdd()
    else if code == 69 # E = M-
      $scope.memorySub()
    else if code == 82 # R = MR
      $scope.memoryShow()
    else if code == 192 # ESC = clear
      $scope.clearCalc()
    else if code == 190 # .
      $scope.acceptInput('.')
    else if (code == 13) # =
      $scope.doMath()
    else if (47 < code < 58)# 0-9 top row
      $scope.acceptInput(String.fromCharCode(code))
    else if (95 < code < 106)
      code -= 48
      $scope.acceptInput(String.fromCharCode(code))
    else if (code == 187 || code == 107) # +
      $scope.acceptInput('+')
    else if (code == 189 || code == 109) # -
      $scope.acceptInput('-')
    else if (code == 106) # *
      $scope.acceptInput('*')
    else if (code == 191 || code == 111) # /
      $scope.acceptInput('/')

method to clear the memory

  $scope.memoryClear = ->
    $scope.memory = 0
    $scope.memoryIndicator(0)

method to show the memory on screen

  $scope.memoryShow = ->
    $scope.stuff = [$scope.memory.toString()]
    $scope.writeScreen()

method to add to memory

  $scope.memoryAdd = () ->
    $scope.memory += $scope.getMath()
    $scope.memoryIndicator(1)

method to subtract from memory

  $scope.memorySub = () ->
    $scope.memory -= $scope.getMath()
    if ($scope.memory != 0)
      $scope.memoryIndicator(1)
    else
      $scope.memoryIndicator(0)

method to to set memory indicator (for demo the memory button is changed to a green background.)

  $scope.memoryIndicator = (show) ->
    if !show
      $('#memoryBtn').removeClass('memoryOn')
    else if (!$('#memoryBtn').hasClass('memoryOn'))
      $('#memoryBtn').addClass('memoryOn')
)