aboutsummaryrefslogtreecommitdiff
path: root/guidelines/ASM/asm-8.5.js
blob: fa3eaf96b767ed7b65294a529bfbc5861e79c1fe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/*
 * Abnormal Situation Management - Guidelines
 *
 * 8.5 - p2: Use consistent numeric formats to enable quick reading.
 * Notes: Don't use leading 0's for integer values, *do* provide leading 0 for
 * fractional values < 1, justify columns on decimal point, include units.
 * Some easy, some hard. (how do we map units to numbers? can we get the screen
 * coordinates for every '.' in the dom?)
 *
 */
exports.name='Inconsistent numerical formatting';
exports.description='Use consistent numeric formats to enable quick reading.';

exports.rule = function (report) {

  // Predicate used on DOM elements below.
  var hasText = function(i) { return ($5(this).text().trim().length); };

  // Rule specifications:
  // (we assume re's below run on strings which already match the general
  // number format given in getNums.)
  var bads = [ { re:  '[+-]?0\\d'   // e.g. 032
               , msg: 'numbers larger than 1 should not lead with a zero' }

               , { re:  '^[+-]?\\.\\d'  // e.g. -.145
                 , msg: 'fractional numbers less than 1 should have a leading zero' }

               , { re:  '\\.$'  // e.g. 32.
                 , msg: 'numbers should not end with a decimal' } ];

  /* Returns a function from a rule specification. */
  var checkBad = function(b) {
    var res = {};
    var f = function (n) {
      var r = new RegExp(b.re);
      if (r.exec(n)) { return true; }
      else { return false; }
    };
    res.fcn = f;
    res.rule = b;  // save the rule
    return res;
  };
  var fcns = _.map(bads, checkBad);

  // DOM element -> it's full text content including children separeted by
  // spaces. We do this instead of just calling text() or .textContent because
  // we want text of children separeted from each other with whitespace.
  var betterText= function (e) {
    if (!$5(e).children()) {
      return $5(e).text();
    }
    else {
      // extract text from the children recursively
      var ctxts = _.map($5(e).children(), betterText);
      // remove the children and extract text
      ctxts.push($(e).clone().children().remove().end().text());
      return ctxts.join(' ');
    }
  };

  /* Returns an array of the numbers found in the given DOM node's text. */
  var getNums = function (e) {
    //var txt = $5(e).text().trim();
    var txt = betterText(e);
    var re = new RegExp('(^|\\b)[+-]?(\\d+(\\.)?\\d*|\\.\\d+)(\\b|$)', 'g');
    var matches = [], found;
    while (found = re.exec(txt)) {
      matches.push(found[0]);
    }
    return matches;
  };

  // Main rule query
  $5("*").filter(hasText).each(function(i, elt) {
    var nums = getNums(elt);
    _.each(fcns, function (rul, ii, l) {
      var m = _.map(nums, rul.fcn);
      if (_.some(m)) { // some number in nums is bad for this rule
        // check if a child node has the same problem
        var childNums = _.flatten(_.map($5(elt).children(), getNums));
        if (_.some(_.map(childNums, rul.fcn))) {
          return;  // don't report it, break out of _.each iteration
        }
        else {  // no child has the same problem
          report.error(rul.msg, elt);
        }
      }
    });
  });

}