<!DOCTYPE html>
<html>

  <head>
    <link data-require="bootstrap-css@2.3.2" data-semver="2.3.2" rel="stylesheet" href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.2/css/bootstrap-combined.min.css" />
    <script data-require="angular.js@1.0.7" data-semver="1.0.7" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js"></script>
    <script data-require="ui-bootstrap@0.4.0" data-semver="0.4.0" src="http://angular-ui.github.io/bootstrap/ui-bootstrap-tpls-0.4.0.min.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
  </head>

  <body>
  <p>
    Calls are logged to the console.  By opening the console you can
    see when the zScoreValue() function is being called.  That function is 
    referenced in the popover attribute.
  </p>
<div class="row-fluid" data-ng-app="my-app" data-ng-controller="my-controller" data-ng-init="initTrends()">
    <div class="span12">
        <table class="table table-bordered table-hover">
            <tr><th>Row Name</th><th colspan="6">Values</th></tr>
            <tr data-ng-repeat="row in rowNames">
                <td><strong>{{ row }}</strong></td>
                <td data-ng-repeat="v in getRowData(row)" class="zscore" popover="{{zscores[row][$index]}}" popover-trigger="mouseenter" popover-append-to-body="true" popover-title="zScore">
                    <!-- TODO : FIXME : the popover causes ALL cells to be recomputed when any cell is entered ! -->
                    {{ v | number:1 }}
                </td>
            </tr>
        </table>
    </div>
</div>
  </body>

</html>
        // Return an object with { stdDev:sigma, mean:average } since we compute both at the same time.
        function stdDevAndMean(data) {
            var ss=0;     // sum of squares
            var m=0;      // arithmetic mean
            var numer;    // current (numeric) value being processed from the column data
            var len = data.length;
            for (var j = 0; j < len; j++) {
                numer = data[j];
                m += numer;
                ss += Math.pow(numer,2);
            }
            m /= len;
            ss = Math.sqrt((ss/len) - Math.pow(m,2));
            return {stdDev:ss, mean:m};
        }
  
        function zScore(v, sdm) {
            console.log("Computing zScore for " + v);
            return (sdm.stdDev == 0) ? 0 : (v - sdm.mean) / sdm.stdDev;
        }

        var myApp = angular.module('my-app', ['ui.bootstrap']);

        myApp.controller('my-controller', function ($scope) {

            $scope.rowNames = ['linear','quadratic'];
            $scope.rowData = {
                'linear': [1, 2, 3, 4, 5, 6],
                'quadratic': [1, 4, 9, 16, 25, 36]
            };

            $scope.trends = {};
            $scope.zscores = {};
            
            $scope.initTrends = function() {
                var cTrends = {};
                var sdm;
                for (var row in $scope.rowData) {
                    var data = $scope.rowData[row];
                    sdm = stdDevAndMean(data);;
                    cTrends[row] = sdm;
                    var zs = [];
                    for (var i=0, len=data.length; i<len; ++i) {
                        zs.push(Math.round(zScore(data[i], sdm)*100) / 100);
                    }
                    $scope.zscores[row] = zs;
                }
                $scope.trends.c = cTrends;
            }
            
            $scope.getRowData = function(row) {
              console.log("getRowData: " + row);
              return $scope.rowData[row];
            }

            $scope.zScoreValue = function(rowName, dataV) {
                if ($scope.trends.c != null) {
                    console.log("Computing zScore for " + rowName + ", from " + dataV);
                    return zScore(dataV, $scope.trends.c[rowName]);
                } else {
                    return null;
                }
            }
        });
/* Styles go here */