<commit_before>describe("Jax.Geometry.Triangle", function() {
  describe("intersections", function() {
    describe("triangle", function() {
      var tri;

      beforeEach(function() {
        tri = new Jax.Geometry.Triangle([0,0,0], [-1,0,0], [0,1,0]);
      });

      it("should be CCW", function() {
        expect(tri).toBeCounterClockwise();
      });

      it("should not be CW", function() {
        expect(tri).not.toBeClockwise();
      });

      describe("with a matrix representing a look backward", function() {
        var mat;
        beforeEach(function() {
          mat = new Jax.Camera({direction: [0, 0, 1]}).getTransformationMatrix();
        });

        it("should not be CCW", function() {
          expect(tri).not.toBeCounterClockwise(mat);
        });

        it("should be CW", function() {
          expect(tri).toBeClockwise(mat);
        });
      });

      describe("with triangle", function() {
        // more complete tritri tests found in tritri_spec.js
        it("no intersect", function() {
          var tri2 = new Jax.Geometry.Triangle([0,0.5,2],[-1,0.5,2],[0,1.5,2]);
          expect(tri.intersectTriangle(tri2)).toBeFalsy();
        });

        it("intersect", function() {
          var tri2 = new Jax.Geometry.Triangle([0,0.95,-1],[-1,0.95,2],[1,0.95,2]);
          expect(tri.intersectTriangle(tri2)).toBeTruthy();
        });

        it("identical, positioned above", function() {
          tri = new Jax.Geometry.Triangle([0,0.95,-1],[-1,0.95,2],[1,0.95,2]);
          var tri2 = new Jax.Geometry.Triangle([0,3.95,-1],[-1,3.95,2],[1,3.95,2]);

          expect(tri.intersectTriangle(tri2)).toBeFalsy();
        });

        it("should not raise errors", function() {
          // TypeError: Object [Line a:[Float32Array: -0.44343942403793335,0.4999999701976776,-0.4434394836425781], b:[Float32Array: 0.5037173628807068,-0.5,0.28691262006759644]] has no method 'contains' with
          var dist = vec3.create();
          var tri1 = new Jax.Geometry.Triangle([0.5,0.5,0.5000000596046448], [-0.5,0.4999999701976776,-0.5000000596046448], [0.5,0.4999999701976776,-0.5000000596046448]);
          var tri2 = new Jax.Geometry.Triangle([0.5037173628807068,0.5,0.28691262006759644], [0.5037173628807068,-0.5,0.28691262006759644], [0.8630872964859009,0.5,-0.6462825536727905]);

          expect(function() { tri1.intersectTriangle(tri2, dist); }).not.toThrow();
        });
      });

      describe("with triangle and capture line", function() {
        var line;
        beforeEach(function() { line = new Jax.Geometry.Line(); });

        // more complete tritri tests found in tritri_spec.js
        it("no intersect", function() {
          var tri2 = new Jax.Geometry.Triangle([0,0.5,2],[-1,0.5,2],[0,1.5,2]);
          expect(tri.intersectTriangle(tri2, line)).toBeFalsy();
        });

        it("intersect", function() {
          var tri2 = new Jax.Geometry.Triangle([0,0.95,-1],[-1,0.95,2],[1,0.95,2]);
          expect(tri.intersectTriangle(tri2, line)).toBeTruthy();
          expect(Math.abs(line.a[0]) + Math.abs(line.a[1]) + Math.abs(line.a[2])).toBeGreaterThan(Math.EPSILON);
        });

        it("identical, positioned above", function() {
          tri = new Jax.Geometry.Triangle([0,0.95,-1],[-1,0.95,2],[1,0.95,2]);
          var tri2 = new Jax.Geometry.Triangle([0,3.95,-1],[-1,3.95,2],[1,3.95,2]);

          expect(tri.intersectTriangle(tri2, line)).toBeFalsy();
        });
      });

      describe("with triangle and capture vec3", function() {
        var point;
        beforeEach(function() { point = vec3.create(); });

        // more complete tritri tests found in tritri_spec.js
        it("no intersect", function() {
          var tri2 = new Jax.Geometry.Triangle([0,0.5,2],[-1,0.5,2],[0,1.5,2]);
          expect(tri.intersectTriangle(tri2, point)).toBeFalsy();
        });

        it("intersect", function() {
          var tri2 = new Jax.Geometry.Triangle([0,0.95,-1],[-1,0.95,2],[1,0.95,2]);
          expect(tri.intersectTriangle(tri2, point)).toBeTruthy();
          expect(Math.abs(point[0]) + Math.abs(point[1]) + Math.abs(point[2])).toBeGreaterThan(Math.EPSILON);
        });

        it("identical, positioned above", function() {
          tri = new Jax.Geometry.Triangle([0,0.95,-1],[-1,0.95,2],[1,0.95,2]);
          var tri2 = new Jax.Geometry.Triangle([0,3.95,-1],[-1,3.95,2],[1,3.95,2]);

          expect(tri.intersectTriangle(tri2, point)).toBeFalsy();
        });

        it("identical, same position", function() {
          tri  = new Jax.Geometry.Triangle([-0.7006292939186096,0.5,0.5090369582176208], [-0.7911535501480103,0.5,0.3522442579269409], [-0.7390738129615784,0.5877852439880371,0.32905685901641846]);
          tri2 = new Jax.Geometry.Triangle([-0.7006292939186096,0.5,0.5090369582176208], [-0.7911535501480103,0.5,0.3522442579269409], [-0.7390738129615784,0.5877852439880371,0.32905685901641846]);

          expect(tri.intersectTriangle(tri2, point)).toBeTruthy();
          expect(Math.abs(point[0])+Math.abs(point[1])+Math.abs(point[2])).toBeGreaterThan(Math.EPSILON);
        });
      });

      describe("with ray", function() {
        it("intersect", function() {
          var O = [0,0,1];
          var D = [0,0,-1];
          var cp = [];
          var segmax = 2;

          expect(tri.intersectRay(O, D, cp, segmax)).toBeTruthy();
          // it intersects at [0,0,0], where distance from [0,0,-1] is 1
          expect(cp).toEqualVector([0,0,0,1]);
        });

        it("no intersect", function() {
          var O = [1,0,0];
          var D = [0,0,-1];
          var cp = [];
          var segmax = 2;

          expect(tri.intersectRay(O, D, cp, segmax)).toBeFalsy();
        });
      });

      describe("with sphere", function() {
        it("interior intersect", function() {
          var O = [-2,0,0];
          var radius = 1;
          var cp = [];

          expect(tri.intersectSphere(O, radius, cp)).toBeTruthy();
          expect(cp).toEqualVector([-1,0,0]);
        });

        it("edge intersect", function() {
          var O = [1,0.5,0];
          var radius = 1;
          var cp = [];

          expect(tri.intersectSphere(O, radius, cp)).toBeTruthy();
          expect(cp).toEqualVector([0,0.5,0]);
        });

        it("no intersect", function() {
          var O = [2, 0.5, 0];
          var radius = 1;
          var cp = [];

          expect(tri.intersectSphere(O, radius, cp)).toBeFalsy();
        });
      });
    });
  });
});
<commit_msg>Exposing intersectRay "bug" when passing vec4 as cp, sometimes (it does not bug with all triangles, just some of them)<commit_after>describe("Jax.Geometry.Triangle", function() {
  describe("intersections", function() {
    describe("triangle", function() {
      var tri;

      beforeEach(function() {
        tri = new Jax.Geometry.Triangle([0,0,0], [-1,0,0], [0,1,0]);
      });

      it("should be CCW", function() {
        expect(tri).toBeCounterClockwise();
      });

      it("should not be CW", function() {
        expect(tri).not.toBeClockwise();
      });

      describe("with a matrix representing a look backward", function() {
        var mat;
        beforeEach(function() {
          mat = new Jax.Camera({direction: [0, 0, 1]}).getTransformationMatrix();
        });

        it("should not be CCW", function() {
          expect(tri).not.toBeCounterClockwise(mat);
        });

        it("should be CW", function() {
          expect(tri).toBeClockwise(mat);
        });
      });

      describe("with triangle", function() {
        // more complete tritri tests found in tritri_spec.js
        it("no intersect", function() {
          var tri2 = new Jax.Geometry.Triangle([0,0.5,2],[-1,0.5,2],[0,1.5,2]);
          expect(tri.intersectTriangle(tri2)).toBeFalsy();
        });

        it("intersect", function() {
          var tri2 = new Jax.Geometry.Triangle([0,0.95,-1],[-1,0.95,2],[1,0.95,2]);
          expect(tri.intersectTriangle(tri2)).toBeTruthy();
        });

        it("identical, positioned above", function() {
          tri = new Jax.Geometry.Triangle([0,0.95,-1],[-1,0.95,2],[1,0.95,2]);
          var tri2 = new Jax.Geometry.Triangle([0,3.95,-1],[-1,3.95,2],[1,3.95,2]);

          expect(tri.intersectTriangle(tri2)).toBeFalsy();
        });

        it("should not raise errors", function() {
          // TypeError: Object [Line a:[Float32Array: -0.44343942403793335,0.4999999701976776,-0.4434394836425781], b:[Float32Array: 0.5037173628807068,-0.5,0.28691262006759644]] has no method 'contains' with
          var dist = vec3.create();
          var tri1 = new Jax.Geometry.Triangle([0.5,0.5,0.5000000596046448], [-0.5,0.4999999701976776,-0.5000000596046448], [0.5,0.4999999701976776,-0.5000000596046448]);
          var tri2 = new Jax.Geometry.Triangle([0.5037173628807068,0.5,0.28691262006759644], [0.5037173628807068,-0.5,0.28691262006759644], [0.8630872964859009,0.5,-0.6462825536727905]);

          expect(function() { tri1.intersectTriangle(tri2, dist); }).not.toThrow();
        });
      });

      describe("with triangle and capture line", function() {
        var line;
        beforeEach(function() { line = new Jax.Geometry.Line(); });

        // more complete tritri tests found in tritri_spec.js
        it("no intersect", function() {
          var tri2 = new Jax.Geometry.Triangle([0,0.5,2],[-1,0.5,2],[0,1.5,2]);
          expect(tri.intersectTriangle(tri2, line)).toBeFalsy();
        });

        it("intersect", function() {
          var tri2 = new Jax.Geometry.Triangle([0,0.95,-1],[-1,0.95,2],[1,0.95,2]);
          expect(tri.intersectTriangle(tri2, line)).toBeTruthy();
          expect(Math.abs(line.a[0]) + Math.abs(line.a[1]) + Math.abs(line.a[2])).toBeGreaterThan(Math.EPSILON);
        });

        it("identical, positioned above", function() {
          tri = new Jax.Geometry.Triangle([0,0.95,-1],[-1,0.95,2],[1,0.95,2]);
          var tri2 = new Jax.Geometry.Triangle([0,3.95,-1],[-1,3.95,2],[1,3.95,2]);

          expect(tri.intersectTriangle(tri2, line)).toBeFalsy();
        });
      });

      describe("with triangle and capture vec3", function() {
        var point;
        beforeEach(function() { point = vec3.create(); });

        // more complete tritri tests found in tritri_spec.js
        it("no intersect", function() {
          var tri2 = new Jax.Geometry.Triangle([0,0.5,2],[-1,0.5,2],[0,1.5,2]);
          expect(tri.intersectTriangle(tri2, point)).toBeFalsy();
        });

        it("intersect", function() {
          var tri2 = new Jax.Geometry.Triangle([0,0.95,-1],[-1,0.95,2],[1,0.95,2]);
          expect(tri.intersectTriangle(tri2, point)).toBeTruthy();
          expect(Math.abs(point[0]) + Math.abs(point[1]) + Math.abs(point[2])).toBeGreaterThan(Math.EPSILON);
        });

        it("identical, positioned above", function() {
          tri = new Jax.Geometry.Triangle([0,0.95,-1],[-1,0.95,2],[1,0.95,2]);
          var tri2 = new Jax.Geometry.Triangle([0,3.95,-1],[-1,3.95,2],[1,3.95,2]);

          expect(tri.intersectTriangle(tri2, point)).toBeFalsy();
        });

        it("identical, same position", function() {
          tri  = new Jax.Geometry.Triangle([-0.7006292939186096,0.5,0.5090369582176208], [-0.7911535501480103,0.5,0.3522442579269409], [-0.7390738129615784,0.5877852439880371,0.32905685901641846]);
          tri2 = new Jax.Geometry.Triangle([-0.7006292939186096,0.5,0.5090369582176208], [-0.7911535501480103,0.5,0.3522442579269409], [-0.7390738129615784,0.5877852439880371,0.32905685901641846]);

          expect(tri.intersectTriangle(tri2, point)).toBeTruthy();
          expect(Math.abs(point[0])+Math.abs(point[1])+Math.abs(point[2])).toBeGreaterThan(Math.EPSILON);
        });
      });

      describe("with ray", function() {
        it("intersect", function() {
          var O = [0,0,1];
          var D = [0,0,-1];
          var cp = [];
          var segmax = 2;

          expect(tri.intersectRay(O, D, cp, segmax)).toBeTruthy();
          // it intersects at [0,0,0], where distance from [0,0,-1] is 1
          expect(cp).toEqualVector([0,0,0,1]);
        });

        it("should accept a vec4 as cp parameter", function() {

          var O = [-1.5328891277313232, 1.9750583171844482, 0.0059257312677800655];
          var D = [0.7422370314598083, -0.6457902193069458, -0.17899484932422638];
          var cp = [];

          var triangle = new Jax.Geometry.Triangle(
            [-0.891882598400116,1.443096399307251,0],
            [-0.985137939453125,1.3854613304138184,0],
            [-0.9207000732421875,1.4252861738204956,0.10426265746355057]
          );

          expect(triangle.intersectRay(O, D, cp)).toBeFalsy(); // OK, because it does not intersect

          cp = vec4.create();

          expect(triangle.intersectRay(O, D, cp)).toBeFalsy(); // WAT
        });

        it("no intersect", function() {
          var O = [1,0,0];
          var D = [0,0,-1];
          var cp = [];
          var segmax = 2;

          expect(tri.intersectRay(O, D, cp, segmax)).toBeFalsy();
        });
      });

      describe("with sphere", function() {
        it("interior intersect", function() {
          var O = [-2,0,0];
          var radius = 1;
          var cp = [];

          expect(tri.intersectSphere(O, radius, cp)).toBeTruthy();
          expect(cp).toEqualVector([-1,0,0]);
        });

        it("edge intersect", function() {
          var O = [1,0.5,0];
          var radius = 1;
          var cp = [];

          expect(tri.intersectSphere(O, radius, cp)).toBeTruthy();
          expect(cp).toEqualVector([0,0.5,0]);
        });

        it("no intersect", function() {
          var O = [2, 0.5, 0];
          var radius = 1;
          var cp = [];

          expect(tri.intersectSphere(O, radius, cp)).toBeFalsy();
        });
      });
    });
  });
});
<|endoftext|>


<commit_before>'use strict';

module.exports =

/*@ngInject*/
function ProofreadingPlayCtrl(
  $scope, $state, ProofreadingService, RuleService, _
) {
  $scope.id = $state.params.id;

  function error(e) {
    $state.go('index');
  }

  /* Returns null or an array of matches */
  //TODO Only looking for line break tags right now
  function htmlMatches(text) {
    if (!text) {
      return null;
    }
    return text.match(/<\s*br\s*?\/>/g);
  }

  function prepareProofreading(pf) {
    $scope.passageQuestions = {};
    pf.replace(/{\+([^-]+)-([^|]+)\|([^}]+)}/g, function(key, plus, minus, ruleNumber) {
      $scope.passageQuestions[key] = {
        plus: plus,
        minus: minus,
        ruleNumber: ruleNumber
      };
      pf = pf.replace(key, minus.split(/\s/).join('|'));
    });
    var prepared = _.chain(pf.split(/\s/))
      .filter(function removeNullWords(n) {
        return n !== '';
      })
      .map(function parseHtmlTokens(w) {
        var matches = htmlMatches(w);
        if (matches) {
          _.each(matches, function(match) {
            if (w !== match) {
              w = w.replace(new RegExp(match, 'g'), ' ' + match + ' ');
            }
          });
          w = w.trim();
          return w.split(/\s/);
        }
        return w;
      })
      .flatten()
      .map(function parseHangingPfQuestionsWithNoSpace(w) {
        _.each($scope.passageQuestions, function(v, key) {
          if (w !== key && w.indexOf(key) !== -1) {
            w = w.split(key).join(' ' + key).split(/\s/);
          }
        });
        return w;
      })
      .flatten()
      .map(function(w) {
        w = w.replace(/\|/g, ' ');
        var passageQuestion = _.findWhere($scope.passageQuestions, {minus: w});
        if (passageQuestion) {
          var c = _.clone(passageQuestion);
          c.text = c.minus;
          c.responseText = c.text;
          return c;
        } else {
          return {
            text: w,
            responseText: w
          };
        }
      })
      .map(function trim(w) {
        w.text = w.text.trim();
        w.responseText = w.responseText.trim();
        return w;
      })
      .filter(function removeSpaces(w) {
        return w.text !== '';
      })
      .value();

    return prepared;

  }

  $scope.obscure = function(key) {
    return btoa(key);
  };

  $scope.ubObscure = function(o) {
    return atob(o);
  };

  ProofreadingService.getProofreading($scope.id).then(function(pf) {
    pf.passage = prepareProofreading(pf.passage);
    $scope.pf = pf;
  }, error);

  $scope.submitPassage = function(passage) {
    function isValid(passageEntry) {
      if (_.has(passageEntry, 'minus')) {
        //A grammar entry
        return passageEntry.responseText === passageEntry.plus;
      } else {
        //A regular word
        return passageEntry.text === passageEntry.responseText;
      }
    }
    var errors = [];
    _.each(passage, function(p) {
      if (!isValid(p)) {
        errors.push(p);
      }
    });
    if (errors.length > 1) {
      showErrors(errors);
    } else {
      showNext();
    }
  };

  $scope.isBr = function(text) {
    return htmlMatches(text) !== null;
  };

  function showErrors(errors) {
    $scope.errors = errors;
  }

  function showNext() {

  }
};
<commit_msg>index the errors for quick lookup<commit_after>'use strict';

module.exports =

/*@ngInject*/
function ProofreadingPlayCtrl(
  $scope, $state, ProofreadingService, RuleService, _
) {
  $scope.id = $state.params.id;

  function error(e) {
    $state.go('index');
  }

  /* Returns null or an array of matches */
  //TODO Only looking for line break tags right now
  function htmlMatches(text) {
    if (!text) {
      return null;
    }
    return text.match(/<\s*br\s*?\/>/g);
  }

  function prepareProofreading(pf) {
    $scope.passageQuestions = {};
    pf.replace(/{\+([^-]+)-([^|]+)\|([^}]+)}/g, function(key, plus, minus, ruleNumber) {
      $scope.passageQuestions[key] = {
        plus: plus,
        minus: minus,
        ruleNumber: ruleNumber
      };
      pf = pf.replace(key, minus.split(/\s/).join('|'));
    });
    var prepared = _.chain(pf.split(/\s/))
      .filter(function removeNullWords(n) {
        return n !== '';
      })
      .map(function parseHtmlTokens(w) {
        var matches = htmlMatches(w);
        if (matches) {
          _.each(matches, function(match) {
            if (w !== match) {
              w = w.replace(new RegExp(match, 'g'), ' ' + match + ' ');
            }
          });
          w = w.trim();
          return w.split(/\s/);
        }
        return w;
      })
      .flatten()
      .map(function parseHangingPfQuestionsWithNoSpace(w) {
        _.each($scope.passageQuestions, function(v, key) {
          if (w !== key && w.indexOf(key) !== -1) {
            w = w.split(key).join(' ' + key).split(/\s/);
          }
        });
        return w;
      })
      .flatten()
      .map(function(w) {
        w = w.replace(/\|/g, ' ');
        var passageQuestion = _.findWhere($scope.passageQuestions, {minus: w});
        if (passageQuestion) {
          var c = _.clone(passageQuestion);
          c.text = c.minus;
          c.responseText = c.text;
          return c;
        } else {
          return {
            text: w,
            responseText: w
          };
        }
      })
      .map(function trim(w) {
        w.text = w.text.trim();
        w.responseText = w.responseText.trim();
        return w;
      })
      .filter(function removeSpaces(w) {
        return w.text !== '';
      })
      .value();

    return prepared;

  }

  $scope.obscure = function(key) {
    return btoa(key);
  };

  $scope.ubObscure = function(o) {
    return atob(o);
  };

  ProofreadingService.getProofreading($scope.id).then(function(pf) {
    pf.passage = prepareProofreading(pf.passage);
    $scope.pf = pf;
  }, error);

  $scope.submitPassage = function(passage) {
    function isValid(passageEntry) {
      if (_.has(passageEntry, 'minus')) {
        //A grammar entry
        return passageEntry.responseText === passageEntry.plus;
      } else {
        //A regular word
        return passageEntry.text === passageEntry.responseText;
      }
    }
    var errors = [];
    _.each(passage, function(p, i) {
      if (!isValid(p)) {
        errors.push({index: i, passageEntry: p});
      }
    });
    if (errors.length > 1) {
      showErrors(errors);
    } else {
      showNext();
    }
  };

  $scope.isBr = function(text) {
    return htmlMatches(text) !== null;
  };

  function showErrors(passageErrors) {
    _.each(passageErrors, function(pe) {
      $scope.pf.passage[pe.index].hasError = true;
    });
  }

  function showNext() {

  }
};
<|endoftext|>


<commit_before>'use strict';

module.exports =

/*@ngInject*/
function ProofreadingPlayCtrl (
  $scope, $state, ProofreadingService, RuleService, _,
  $location, localStorageService, $document, $timeout,
  $analytics
) {
  $scope.id = $state.params.uid;

  function error() {
    console.error(arguments[0].stack);
    $state.go('index');
  }

  $scope.obscure = function (key) {
    return btoa(key);
  };

  $scope.ubObscure = function (o) {
    return atob(o);
  };

  ProofreadingService.getProofreading($scope.id).then(function (pf) {
    pf.passage = ProofreadingService.prepareProofreading(pf.passage, $scope);
    $scope.pf = pf;
    fetchListedRules();
  }, error);

  /*
   * set number of changes to passage. Functions for incrementing and decrementing
   * this number.
   */
  $scope.numChanges = 0;
  $scope.madeChange = false;

  $scope.onInputChange = function (word) {
    var nc = $scope.numChanges;
    if (word.responseText === '') {
      if (!word.countedChange) {
        nc++;
        word.countedChange = true;
      }
    } else if (word.responseText !== word.text) {
      if (!word.countedChange) {
        nc++;
      }
      word.countedChange = true;
    } else if (word.responseText === word.text && word.countedChange) {
      nc = Math.max(nc - 1, 0);
      word.countedChange = false;
    }
    $scope.numChanges = nc;
    if (!$scope.madeChange && nc > 0) {
      $scope.madeChange = true;
      $analytics.eventTrack('Edit One Passage Word', word);
    }
  };

  /*
   * Functions for interacting with the referenced rules in the
   * passage questions.
   */

  function fetchListedRules() {
    var ruleIds = _.pluck($scope.passageQuestions, 'ruleNumber');
    RuleService.getRules(ruleIds).then(function (rules) {
      $scope.referencedRules = rules;
    });
  }

  $scope.getRuleInfoBy = function (ruleNumber) {
    return _.findWhere($scope.referencedRules, {ruleNumber: Number(ruleNumber)}).title;
  };

  /*
   * These functions below handle submission errors
   * and state/$scope updates after the student has submitted
   * their passage.
   */

  $scope.INCORRECT_ERROR = 'Incorrect';
  $scope.NOT_NECESSARY_ERROR = 'Not Necessary';
  $scope.CORRECT = 'Correct';

  $scope.hasNotNecessaryError = function (word) {
    return word.type === $scope.NOT_NECESSARY_ERROR;
  };

  $scope.hasIncorrectError = function (word) {
    return word.type === $scope.INCORRECT_ERROR;
  };

  $scope.hasCorrect = function (word) {
    return word.type === $scope.CORRECT;
  };

  $scope.submitPassage = function () {
    var passage = $scope.pf.passage;
    function isValid(passageEntry) {
      if (_.has(passageEntry, 'minus')) {
        //A grammar entry
        return passageEntry.responseText === passageEntry.plus;
      } else {
        //A regular word
        return passageEntry.text === passageEntry.responseText;
      }
    }
    function getErrorType(passageEntry) {
      return _.has(passageEntry, 'minus') ? $scope.INCORRECT_ERROR : $scope.NOT_NECESSARY_ERROR;
    }
    $scope.results = [];
    _.each(passage, function (p, i) {
      if (!isValid(p)) {
        $scope.results.push({index: i, passageEntry: p, type: getErrorType(p)});
      }
      if (isValid(p) && _.has(p, 'minus')) {
        $scope.results.push({index: i, passageEntry: p, type: $scope.CORRECT});
      }
    });
    var numErrors = _.keys($scope.passageQuestions).length;
    var numErrorsToSolve = Math.floor(numErrors / 2);
    var numErrorsFound = getNumCorrect($scope.results);
    var numEdits = $scope.numChanges;
    if (numErrorsFound >= numErrorsToSolve ||  numEdits >= numErrorsToSolve) {
      showResultsModal($scope.results, numErrorsFound, numErrors);
    } else {
      showModalNotEnoughFound(numErrorsToSolve);
    }
  };

  function getNumCorrect(results) {
    return _.where(results, {type: $scope.CORRECT}).length;
  }

  $scope.getNumErrors = function () {
    return _.keys($scope.passageQuestions).length;
  };

  function getNumResults() {
    return _.keys($scope.results).length;
  }
  /*
   * Modal settings
   */
  function showModalNotEnoughFound(needed) {
    $scope.pf.modal = {
      title: 'Keep Trying!',
      message: 'You must make at least ' + needed + ' edits.',
      buttonMessage: 'Find Edits',
      buttonClick: function () {
        $scope.pf.modal.show = false;
      },
      show: true
    };
  }

  function showResultsModal(results, numErrorsFound, numErrorsToSolve) {
    function createTitle() {
      if (numErrorsFound === numErrorsToSolve) {
        return 'Congratulations!';
      } else if (numErrorsFound >= numErrorsToSolve / 2) {
        return 'Good Work!';
      }
      return '';
    }
    var nf = numErrorsFound === numErrorsToSolve ? 'all ' + String(numErrorsFound) : String(numErrorsFound) + ' of ' + String(numErrorsToSolve);
    var title = createTitle();

    $scope.pf.modal = {
      title: title,
      message: 'You found ' + nf + ' errors.',
      buttonMessage: 'Review Your Work',
      buttonClick: function () {
        $scope.pf.modal.show = false;
        showResults(results);
      },
      show: true
    };
  }

  /*
   * Convenience html methods
   */

  $scope.nextAction = function (word, index) {
    if (!$scope.results) {
      return {};
    }
    var allCorrect = getNumCorrect($scope.results) === $scope.getNumErrors();
    var na = {
      fn: null,
      title: ''
    };
    if (word.resultIndex + 1 >= getNumResults()) {
      if (allCorrect) {
        na.fn = function () {
          $state.go('play-sw-gen', {
            ids: [],
            passageId: $scope.id,
            pfAllCorrect: true
          });
        };
        na.title = 'View Results';
      } else {
        na.fn = function () {
          $scope.goToLesson();
        };
        na.title = 'Start My Activity';
      }
    } else {
      na.fn = function () {
        $scope.focusResult(word.resultIndex + 1, index);
      };
      na.title = 'Next Edit';
    }

    return na;
  };
  var getAbsPosition = function (el) {
    var el2 = el;
    var curtop = 0;
    var curleft = 0;
    if (document.getElementById || document.all) {
      do {
        curleft += el.offsetLeft - el.scrollLeft;
        curtop += el.offsetTop - el.scrollTop;
        el = el.offsetParent;
        el2 = el2.parentNode;
        while (el2 !== el) {
          curleft -= el2.scrollLeft;
          curtop -= el2.scrollTop;
          el2 = el2.parentNode;
        }
      } while (el.offsetParent);
    } else if (document.layers) {
      curtop += el.y;
      curleft += el.x;
    }
    return [curtop, curleft];
  };

  function calculateTop(sIndex) {
    var breakIndexes = _.chain($scope.pf.passage)
      .map(function (word, index) {
        return [index, $scope.isBr(word.responseText)];
      })
      .filter(function (v) {
        return v[1];
      })
      .map(function (v) {
        return v[0];
      })
      .sortBy(function (num) {
        return num;
      })
      .value();
    var indexWithOutGoingOver = _.find(breakIndexes, function (i) {
      return i >= sIndex;
    });
    var scrollId = 'last-chance-tooltip-breakpoint-at-panel';
    if (indexWithOutGoingOver) {
      scrollId = 'break-binding-point-' + String(indexWithOutGoingOver);
    }
    var elem = document.getElementById(scrollId);
    var top = getAbsPosition(elem)[0];
    return String(top) + 'px';
  }

  $scope.focusResult = function (resultIndex, scrollIndex) {
    var p = $scope.results[resultIndex - 1];
    var r = $scope.results[resultIndex];
    if (p) {
      $scope.pf.passage[p.index].tooltip = {};
    }

    if (r) {
      $scope.pf.passage[r.index].tooltip = {
        style: {
          visibility: 'visible',
          opacity: 1,
          top: calculateTop(r.index)
        }
      };
    }
    var scrollTo = r.index;
    if (!scrollTo) {
      scrollTo = scrollIndex;
    }
    if (String(scrollTo)) {
      var scrollId = 'error-tooltip-scroll-' + String(scrollTo);
      var elem = angular.element(document.getElementById(scrollId));
      $document.scrollToElement(elem);
    }
  };

  $scope.getErrorTooltipClass = function (index) {
    var results = $scope.$parent.results;
    var d = {'error-tooltip-reverse': true};
    if (false) {
      var ri = _.indexOf(_.pluck(results, 'index'), index);
      var sp = 3;
      d = {
        'error-tooltip': ri > sp,
        'error-tooltip-reverse': ri <= sp
      };
    }
    return d;
  };

  $scope.getErrorTooltipTopClass = function (type) {
    var obj = {'top-panel': true};
    obj[type] = true;
    return obj;
  };

  $scope.errorCounter = function (word) {
    return 'Edit ' + String(word.resultIndex + 1) + ' of ' + word.totalResults;
  };

  $scope.answerImageName = function (t) {
    if (!t) {
      return;
    }
    return _.map(t.split(' '), function (s) {
      return s.toLowerCase();
    }).join('_');
  };

  $scope.needsUnderlining = function (p) {
    if ($scope.pf && $scope.pf.underlineErrorsInProofreader && _.has(p, 'minus')) {
      return true;
    }
  };

  $scope.isBr = function (text) {
    return ProofreadingService.htmlMatches(text) !== null;
  };

  $scope.hasErrorToShow = function (word) {
    return _.any([$scope.hasNotNecessaryError, $scope.hasCorrect, $scope.hasIncorrectError], function (fn) {
      return fn(word);
    });
  };

  /*
   * Function to return the grammatical concept for a word
   * With v1, we are just using the rule title. In the future
   * we will make a data model change.
   */
  $scope.getGrammaticalConceptForWord = function (word) {
    if (word.ruleNumber) {
      var rule = _.findWhere($scope.referencedRules, {ruleNumber: Number(word.ruleNumber)});
      if (rule && rule.title) {
        return rule.title;
      }
    }
  };

  function showResults(passageResults) {
    _.each(passageResults, function (pr, i) {
      $scope.pf.passage[pr.index].type = pr.type;
      $scope.pf.passage[pr.index].resultIndex = i;
      $scope.pf.passage[pr.index].ruleNumber = pr.passageEntry.ruleNumber;
      $scope.pf.passage[pr.index].totalResults = passageResults.length;
      $scope.pf.passage[pr.index].nextAction = $scope.nextAction($scope.pf.passage[pr.index], pr.index);
    });
    var ruleNumbers = _.chain(passageResults)
      .pluck('passageEntry')
      .reject(function (r) {
        return r.type !== $scope.INCORRECT_ERROR;
      })
      .pluck('ruleNumber')
      .reject(_.isUndefined)
      .uniq()
      .value();
    generateLesson(ruleNumbers);
    $scope.focusResult(0, passageResults[0].index);
    sendResultsAnalytics(passageResults);
    saveResults(getLocalResults(passageResults));
    $scope.pf.passage.submitted = true;
  }

  function saveResults(r) {
    localStorageService.set('pf-' + $scope.id, r);
    localStorageService.remove('sw-' + $scope.id);
    localStorageService.remove('sw-temp-' + $scope.id);
  }

  /*
   * Mapping results for analytics
   */
  function sendResultsAnalytics(results) {
    var event = 'Press Check Answer';
    var passageResults = _.chain(results)
      .pluck('passageEntry')
      .map(function pick (p) {
        return _.pick(p, ['minus', 'ruleNumber', 'responseText', 'plus', 'type']);
      })
      .value();
    var correct = _.filter(passageResults, function (pr) {
      return pr.type === $scope.CORRECT;
    });
    var incorrect = _.filter(passageResults, function (pr) {
      return pr.type !== $scope.CORRECT;
    });

    var correctWords = _.map(correct, function (c) {
      return c.responseText;
    });

    var incorrectWords = _.map(incorrect, function (i) {
      return i.responseText;
    });

    var correctRuleNumbers = _.map(correct, function (c) {
      return c.ruleNumber;
    });

    var incorrectRuleNumbers = _.map(incorrect, function (i) {
      return i.ruleNumber;
    });

    var attrs = {
      correct: correct,
      incorrect: incorrect,
      correctWords: correctWords,
      correctRuleNumbers: correctRuleNumbers,
      incorrectRuleNumbers: incorrectRuleNumbers,
      incorrectWords: incorrectWords,
      score: Number(correct.length / results.length) * 100
    };
    $analytics.eventTrack(event, attrs);
  }

  /*
   * generate passage results for local results
   */

  function getLocalResults(passageResults) {
    var rules = $scope.referencedRules;
    return _.chain(passageResults)
      .pluck('passageEntry')
      .reject(function (pe) {
        return _.isUndefined(pe.ruleNumber);
      })
      .map(function (pe) {
        var rule = _.findWhere(rules, {ruleNumber: Number(pe.ruleNumber)});
        return {
          title: rule.title,
          correct: pe.type === $scope.CORRECT
        };
      })
      .groupBy('title')
      .map(function (entries, title) {
        return {
          conceptClass: title,
          total: entries.length,
          correct: _.filter(entries, function (v) { return v.correct; }).length
        };
      })
      .value();
  }

  /*
   * Below when handle building the lesson and showing
   * the appropriate ui.
   */

  function generateLesson(ruleNumbers) {
    $scope.goToLesson = function () {
      $state.go('play-sw-gen', {
        ids: ruleNumbers,
        passageId: $scope.id,
      });
    };
    $scope.hasLesson = true;
  }
};
<commit_msg>Add student param to state change<commit_after>'use strict';

module.exports =

/*@ngInject*/
function ProofreadingPlayCtrl (
  $scope, $state, ProofreadingService, RuleService, _,
  $location, localStorageService, $document, $timeout,
  $analytics
) {
  $scope.id = $state.params.uid;

  function error() {
    console.error(arguments[0].stack);
    $state.go('index');
  }

  $scope.obscure = function (key) {
    return btoa(key);
  };

  $scope.ubObscure = function (o) {
    return atob(o);
  };

  ProofreadingService.getProofreading($scope.id).then(function (pf) {
    pf.passage = ProofreadingService.prepareProofreading(pf.passage, $scope);
    $scope.pf = pf;
    fetchListedRules();
  }, error);

  /*
   * set number of changes to passage. Functions for incrementing and decrementing
   * this number.
   */
  $scope.numChanges = 0;
  $scope.madeChange = false;

  $scope.onInputChange = function (word) {
    var nc = $scope.numChanges;
    if (word.responseText === '') {
      if (!word.countedChange) {
        nc++;
        word.countedChange = true;
      }
    } else if (word.responseText !== word.text) {
      if (!word.countedChange) {
        nc++;
      }
      word.countedChange = true;
    } else if (word.responseText === word.text && word.countedChange) {
      nc = Math.max(nc - 1, 0);
      word.countedChange = false;
    }
    $scope.numChanges = nc;
    if (!$scope.madeChange && nc > 0) {
      $scope.madeChange = true;
      $analytics.eventTrack('Edit One Passage Word', word);
    }
  };

  /*
   * Functions for interacting with the referenced rules in the
   * passage questions.
   */

  function fetchListedRules() {
    var ruleIds = _.pluck($scope.passageQuestions, 'ruleNumber');
    RuleService.getRules(ruleIds).then(function (rules) {
      $scope.referencedRules = rules;
    });
  }

  $scope.getRuleInfoBy = function (ruleNumber) {
    return _.findWhere($scope.referencedRules, {ruleNumber: Number(ruleNumber)}).title;
  };

  /*
   * These functions below handle submission errors
   * and state/$scope updates after the student has submitted
   * their passage.
   */

  $scope.INCORRECT_ERROR = 'Incorrect';
  $scope.NOT_NECESSARY_ERROR = 'Not Necessary';
  $scope.CORRECT = 'Correct';

  $scope.hasNotNecessaryError = function (word) {
    return word.type === $scope.NOT_NECESSARY_ERROR;
  };

  $scope.hasIncorrectError = function (word) {
    return word.type === $scope.INCORRECT_ERROR;
  };

  $scope.hasCorrect = function (word) {
    return word.type === $scope.CORRECT;
  };

  $scope.submitPassage = function () {
    var passage = $scope.pf.passage;
    function isValid(passageEntry) {
      if (_.has(passageEntry, 'minus')) {
        //A grammar entry
        return passageEntry.responseText === passageEntry.plus;
      } else {
        //A regular word
        return passageEntry.text === passageEntry.responseText;
      }
    }
    function getErrorType(passageEntry) {
      return _.has(passageEntry, 'minus') ? $scope.INCORRECT_ERROR : $scope.NOT_NECESSARY_ERROR;
    }
    $scope.results = [];
    _.each(passage, function (p, i) {
      if (!isValid(p)) {
        $scope.results.push({index: i, passageEntry: p, type: getErrorType(p)});
      }
      if (isValid(p) && _.has(p, 'minus')) {
        $scope.results.push({index: i, passageEntry: p, type: $scope.CORRECT});
      }
    });
    var numErrors = _.keys($scope.passageQuestions).length;
    var numErrorsToSolve = Math.floor(numErrors / 2);
    var numErrorsFound = getNumCorrect($scope.results);
    var numEdits = $scope.numChanges;
    if (numErrorsFound >= numErrorsToSolve ||  numEdits >= numErrorsToSolve) {
      showResultsModal($scope.results, numErrorsFound, numErrors);
    } else {
      showModalNotEnoughFound(numErrorsToSolve);
    }
  };

  function getNumCorrect(results) {
    return _.where(results, {type: $scope.CORRECT}).length;
  }

  $scope.getNumErrors = function () {
    return _.keys($scope.passageQuestions).length;
  };

  function getNumResults() {
    return _.keys($scope.results).length;
  }
  /*
   * Modal settings
   */
  function showModalNotEnoughFound(needed) {
    $scope.pf.modal = {
      title: 'Keep Trying!',
      message: 'You must make at least ' + needed + ' edits.',
      buttonMessage: 'Find Edits',
      buttonClick: function () {
        $scope.pf.modal.show = false;
      },
      show: true
    };
  }

  function showResultsModal(results, numErrorsFound, numErrorsToSolve) {
    function createTitle() {
      if (numErrorsFound === numErrorsToSolve) {
        return 'Congratulations!';
      } else if (numErrorsFound >= numErrorsToSolve / 2) {
        return 'Good Work!';
      }
      return '';
    }
    var nf = numErrorsFound === numErrorsToSolve ? 'all ' + String(numErrorsFound) : String(numErrorsFound) + ' of ' + String(numErrorsToSolve);
    var title = createTitle();

    $scope.pf.modal = {
      title: title,
      message: 'You found ' + nf + ' errors.',
      buttonMessage: 'Review Your Work',
      buttonClick: function () {
        $scope.pf.modal.show = false;
        showResults(results);
      },
      show: true
    };
  }

  /*
   * Convenience html methods
   */

  $scope.nextAction = function (word, index) {
    if (!$scope.results) {
      return {};
    }
    var allCorrect = getNumCorrect($scope.results) === $scope.getNumErrors();
    var na = {
      fn: null,
      title: ''
    };
    if (word.resultIndex + 1 >= getNumResults()) {
      if (allCorrect) {
        na.fn = function () {
          $state.go('play-sw-gen', {
            ids: [],
            passageId: $scope.id,
            pfAllCorrect: true,
            student: $state.params.student
          });
        };
        na.title = 'View Results';
      } else {
        na.fn = function () {
          $scope.goToLesson();
        };
        na.title = 'Start My Activity';
      }
    } else {
      na.fn = function () {
        $scope.focusResult(word.resultIndex + 1, index);
      };
      na.title = 'Next Edit';
    }

    return na;
  };
  var getAbsPosition = function (el) {
    var el2 = el;
    var curtop = 0;
    var curleft = 0;
    if (document.getElementById || document.all) {
      do {
        curleft += el.offsetLeft - el.scrollLeft;
        curtop += el.offsetTop - el.scrollTop;
        el = el.offsetParent;
        el2 = el2.parentNode;
        while (el2 !== el) {
          curleft -= el2.scrollLeft;
          curtop -= el2.scrollTop;
          el2 = el2.parentNode;
        }
      } while (el.offsetParent);
    } else if (document.layers) {
      curtop += el.y;
      curleft += el.x;
    }
    return [curtop, curleft];
  };

  function calculateTop(sIndex) {
    var breakIndexes = _.chain($scope.pf.passage)
      .map(function (word, index) {
        return [index, $scope.isBr(word.responseText)];
      })
      .filter(function (v) {
        return v[1];
      })
      .map(function (v) {
        return v[0];
      })
      .sortBy(function (num) {
        return num;
      })
      .value();
    var indexWithOutGoingOver = _.find(breakIndexes, function (i) {
      return i >= sIndex;
    });
    var scrollId = 'last-chance-tooltip-breakpoint-at-panel';
    if (indexWithOutGoingOver) {
      scrollId = 'break-binding-point-' + String(indexWithOutGoingOver);
    }
    var elem = document.getElementById(scrollId);
    var top = getAbsPosition(elem)[0];
    return String(top) + 'px';
  }

  $scope.focusResult = function (resultIndex, scrollIndex) {
    var p = $scope.results[resultIndex - 1];
    var r = $scope.results[resultIndex];
    if (p) {
      $scope.pf.passage[p.index].tooltip = {};
    }

    if (r) {
      $scope.pf.passage[r.index].tooltip = {
        style: {
          visibility: 'visible',
          opacity: 1,
          top: calculateTop(r.index)
        }
      };
    }
    var scrollTo = r.index;
    if (!scrollTo) {
      scrollTo = scrollIndex;
    }
    if (String(scrollTo)) {
      var scrollId = 'error-tooltip-scroll-' + String(scrollTo);
      var elem = angular.element(document.getElementById(scrollId));
      $document.scrollToElement(elem);
    }
  };

  $scope.getErrorTooltipClass = function (index) {
    var results = $scope.$parent.results;
    var d = {'error-tooltip-reverse': true};
    if (false) {
      var ri = _.indexOf(_.pluck(results, 'index'), index);
      var sp = 3;
      d = {
        'error-tooltip': ri > sp,
        'error-tooltip-reverse': ri <= sp
      };
    }
    return d;
  };

  $scope.getErrorTooltipTopClass = function (type) {
    var obj = {'top-panel': true};
    obj[type] = true;
    return obj;
  };

  $scope.errorCounter = function (word) {
    return 'Edit ' + String(word.resultIndex + 1) + ' of ' + word.totalResults;
  };

  $scope.answerImageName = function (t) {
    if (!t) {
      return;
    }
    return _.map(t.split(' '), function (s) {
      return s.toLowerCase();
    }).join('_');
  };

  $scope.needsUnderlining = function (p) {
    if ($scope.pf && $scope.pf.underlineErrorsInProofreader && _.has(p, 'minus')) {
      return true;
    }
  };

  $scope.isBr = function (text) {
    return ProofreadingService.htmlMatches(text) !== null;
  };

  $scope.hasErrorToShow = function (word) {
    return _.any([$scope.hasNotNecessaryError, $scope.hasCorrect, $scope.hasIncorrectError], function (fn) {
      return fn(word);
    });
  };

  /*
   * Function to return the grammatical concept for a word
   * With v1, we are just using the rule title. In the future
   * we will make a data model change.
   */
  $scope.getGrammaticalConceptForWord = function (word) {
    if (word.ruleNumber) {
      var rule = _.findWhere($scope.referencedRules, {ruleNumber: Number(word.ruleNumber)});
      if (rule && rule.title) {
        return rule.title;
      }
    }
  };

  function showResults(passageResults) {
    _.each(passageResults, function (pr, i) {
      $scope.pf.passage[pr.index].type = pr.type;
      $scope.pf.passage[pr.index].resultIndex = i;
      $scope.pf.passage[pr.index].ruleNumber = pr.passageEntry.ruleNumber;
      $scope.pf.passage[pr.index].totalResults = passageResults.length;
      $scope.pf.passage[pr.index].nextAction = $scope.nextAction($scope.pf.passage[pr.index], pr.index);
    });
    var ruleNumbers = _.chain(passageResults)
      .pluck('passageEntry')
      .reject(function (r) {
        return r.type !== $scope.INCORRECT_ERROR;
      })
      .pluck('ruleNumber')
      .reject(_.isUndefined)
      .uniq()
      .value();
    generateLesson(ruleNumbers);
    $scope.focusResult(0, passageResults[0].index);
    sendResultsAnalytics(passageResults);
    saveResults(getLocalResults(passageResults));
    $scope.pf.passage.submitted = true;
  }

  function saveResults(r) {
    localStorageService.set('pf-' + $scope.id, r);
    localStorageService.remove('sw-' + $scope.id);
    localStorageService.remove('sw-temp-' + $scope.id);
  }

  /*
   * Mapping results for analytics
   */
  function sendResultsAnalytics(results) {
    var event = 'Press Check Answer';
    var passageResults = _.chain(results)
      .pluck('passageEntry')
      .map(function pick (p) {
        return _.pick(p, ['minus', 'ruleNumber', 'responseText', 'plus', 'type']);
      })
      .value();
    var correct = _.filter(passageResults, function (pr) {
      return pr.type === $scope.CORRECT;
    });
    var incorrect = _.filter(passageResults, function (pr) {
      return pr.type !== $scope.CORRECT;
    });

    var correctWords = _.map(correct, function (c) {
      return c.responseText;
    });

    var incorrectWords = _.map(incorrect, function (i) {
      return i.responseText;
    });

    var correctRuleNumbers = _.map(correct, function (c) {
      return c.ruleNumber;
    });

    var incorrectRuleNumbers = _.map(incorrect, function (i) {
      return i.ruleNumber;
    });

    var attrs = {
      correct: correct,
      incorrect: incorrect,
      correctWords: correctWords,
      correctRuleNumbers: correctRuleNumbers,
      incorrectRuleNumbers: incorrectRuleNumbers,
      incorrectWords: incorrectWords,
      score: Number(correct.length / results.length) * 100
    };
    $analytics.eventTrack(event, attrs);
  }

  /*
   * generate passage results for local results
   */

  function getLocalResults(passageResults) {
    var rules = $scope.referencedRules;
    return _.chain(passageResults)
      .pluck('passageEntry')
      .reject(function (pe) {
        return _.isUndefined(pe.ruleNumber);
      })
      .map(function (pe) {
        var rule = _.findWhere(rules, {ruleNumber: Number(pe.ruleNumber)});
        return {
          title: rule.title,
          correct: pe.type === $scope.CORRECT
        };
      })
      .groupBy('title')
      .map(function (entries, title) {
        return {
          conceptClass: title,
          total: entries.length,
          correct: _.filter(entries, function (v) { return v.correct; }).length
        };
      })
      .value();
  }

  /*
   * Below when handle building the lesson and showing
   * the appropriate ui.
   */

  function generateLesson(ruleNumbers) {
    $scope.goToLesson = function () {
      $state.go('play-sw-gen', {
        ids: ruleNumbers,
        passageId: $scope.id,
        student: $state.params.student
      });
    };
    $scope.hasLesson = true;
  }
};
<|endoftext|>


<commit_before>/**
 * React Native Playground
 * https://github.com/jsierles/rnplay
 */

'use strict';

var React = require('react-native');
var Explore = require('./Explore');
var MyAppsContainer = require('./MyAppsContainer');
var Settings = require('./Settings');
var About = require('./About');
var QRCodeReader = require('./QRCodeReader');

var SMXTabBarIOS = require('SMXTabBarIOS');
var SMXTabBarItemIOS = SMXTabBarIOS.Item;

var {
  AppRegistry,
  StyleSheet,
  Text,
  TouchableOpacity
} = React;

var Home = React.createClass({
  getInitialState() {
    return {
      selectedTab: 'about',
    };
  },

  render() {
    return (
      <SMXTabBarIOS
        selectedTab={this.state.selectedTab}
        tintColor={'#712FA9'}
        style={styles.tabBar}
        barTintColor={'white'}>
        <SMXTabBarItemIOS
          name="explore"
          iconName={'ion|ios-search-strong'}
          title={'Explore'}
          iconSize={32}
          accessibilityLabel="Explore Tab"
          selected={this.state.selectedTab === 'explore'}
          onPress={() => { this.setState({ selectedTab: 'explore', }); }}>
          <Explore />
        </SMXTabBarItemIOS>
        <SMXTabBarItemIOS
            name="my-apps"
            iconName={'ion|ios-briefcase-outline'}
            title={'My Apps'}
            iconSize={32}
            accessibilityLabel="My Apps Tab"
            selected={this.state.selectedTab === 'my-apps'}
            onPress={() => { this.setState({selectedTab: 'my-apps',}); }}>
          <MyAppsContainer />
        </SMXTabBarItemIOS>
        <SMXTabBarItemIOS
            name="qr_code_reader"
            iconName={'ion|camera'}
            title={'Scan Code'}
            iconSize={32}
            accessibilityLabel="QR Code Reader"
            selected={this.state.selectedTab === 'qr_code_reader'}
            onPress={() => { this.setState({ selectedTab: 'qr_code_reader', }); }}>
          <QRCodeReader />
        </SMXTabBarItemIOS>
        <SMXTabBarItemIOS
          name="about"
          iconName={'ion|ios-help-outline'}
          title={'About'}
          iconSize={32}
          accessibilityLabel="About Tab"
          selected={this.state.selectedTab === 'about'}
          onPress={() => { this.setState({ selectedTab: 'about', }); }}>
          <About />
        </SMXTabBarItemIOS>
        <SMXTabBarItemIOS
            name="settings"
            iconName={'ion|gear-a'}
            title={'Settings'}
            iconSize={32}
            accessibilityLabel="Settings Tab"
            selected={this.state.selectedTab === 'settings'}
            onPress={() => { this.setState({ selectedTab: 'settings', }); }}>
          <Settings />
        </SMXTabBarItemIOS>
      </SMXTabBarIOS>
    )
  }
});

var styles = StyleSheet.create({
  tabBar: {
  }
});

module.exports = Home;
<commit_msg>remove settings tab for now<commit_after>/**
 * React Native Playground
 * https://github.com/jsierles/rnplay
 */

'use strict';

var React = require('react-native');
var Explore = require('./Explore');
var MyAppsContainer = require('./MyAppsContainer');
var Settings = require('./Settings');
var About = require('./About');
var QRCodeReader = require('./QRCodeReader');

var SMXTabBarIOS = require('SMXTabBarIOS');
var SMXTabBarItemIOS = SMXTabBarIOS.Item;

var {
  AppRegistry,
  StyleSheet,
  Text,
  TouchableOpacity
} = React;

var Home = React.createClass({
  getInitialState() {
    return {
      selectedTab: 'about',
    };
  },

  render() {
    return (
      <SMXTabBarIOS
        selectedTab={this.state.selectedTab}
        tintColor={'#712FA9'}
        style={styles.tabBar}
        barTintColor={'white'}>
        <SMXTabBarItemIOS
          name="explore"
          iconName={'ion|ios-search-strong'}
          title={'Explore'}
          iconSize={32}
          accessibilityLabel="Explore Tab"
          selected={this.state.selectedTab === 'explore'}
          onPress={() => { this.setState({ selectedTab: 'explore', }); }}>
          <Explore />
        </SMXTabBarItemIOS>
        <SMXTabBarItemIOS
            name="my-apps"
            iconName={'ion|ios-briefcase-outline'}
            title={'My Apps'}
            iconSize={32}
            accessibilityLabel="My Apps Tab"
            selected={this.state.selectedTab === 'my-apps'}
            onPress={() => { this.setState({selectedTab: 'my-apps',}); }}>
          <MyAppsContainer />
        </SMXTabBarItemIOS>
        <SMXTabBarItemIOS
            name="qr_code_reader"
            iconName={'ion|camera'}
            title={'Scan Code'}
            iconSize={32}
            accessibilityLabel="QR Code Reader"
            selected={this.state.selectedTab === 'qr_code_reader'}
            onPress={() => { this.setState({ selectedTab: 'qr_code_reader', }); }}>
          <QRCodeReader />
        </SMXTabBarItemIOS>
        <SMXTabBarItemIOS
          name="about"
          iconName={'ion|ios-help-outline'}
          title={'About'}
          iconSize={32}
          accessibilityLabel="About Tab"
          selected={this.state.selectedTab === 'about'}
          onPress={() => { this.setState({ selectedTab: 'about', }); }}>
          <About />
        </SMXTabBarItemIOS>
      </SMXTabBarIOS>
    )
  }
});

var styles = StyleSheet.create({
  tabBar: {
  }
});

module.exports = Home;
<|endoftext|>


<commit_before>(function() {
  'use strict';
  angular.module('civic.events.genes')
    .directive('geneTalkRevisionSummary', geneTalkRevisionSummary)
    .controller('GeneTalkRevisionSummaryController', GeneTalkRevisionSummaryController);

  // @ngInject
  function geneTalkRevisionSummary() {
    var directive = {
      restrict: 'E',
      replace: true,
      require: ['^^entityTalkView', '^^entityView'],
      templateUrl: 'app/views/events/genes/talk/geneTalkRevisionSummary.tpl.html',
      link: geneTalkRevisionsSummaryLink,
      controller: 'GeneTalkRevisionSummaryController'
    };
    return directive;
  }

  // @ngInject
  function geneTalkRevisionsSummaryLink(scope, element, attributes, controllers) {
    var ctrl,
      geneTalkModel,
      geneModel,
      changeId;

    ctrl = scope.ctrl = {};
    geneTalkModel = scope.geneTalkModel= controllers[0].entityTalkModel;
    geneModel = scope.geneModel = controllers[1].entityModel;
    changeId = scope.changeId;

    geneTalkModel.actions.getChange(changeId);
    geneTalkModel.actions.getChangeComments(changeId);

    scope.geneName = controllers[1].entityModel.data.entity.entrez_name;
    scope.geneDescription = controllers[1].entityModel.data.entity.description;
  }

  // @ngInject
  function GeneTalkRevisionSummaryController($scope, $stateParams) {
    // wait until models linked

    $scope.changeId = $stateParams.changeId;

    $scope.acceptChange = function() {
      $scope.geneTalkModel.actions.acceptChange($stateParams.changeId)
        .then(function() {
          $scope.geneTalkModel.actions.getChanges($stateParams.geneId); // will eventually refresh revisions data grid
          $scope.geneModel.actions.refresh(); // will eventually refresh parent gene
        });
    };

    $scope.rejectChange = function() {
      $scope.geneTalkModel.actions.rejectChange($stateParams.changeId)
        .then(function() {
          $scope.geneTalkModel.actions.getChanges($stateParams.geneId);
        });
    };
  }
})();
<commit_msg>starting on integrated updated civic-server gene routes for entrez_id -> id transition<commit_after>(function() {
  'use strict';
  angular.module('civic.events.genes')
    .directive('geneTalkRevisionSummary', geneTalkRevisionSummary)
    .controller('GeneTalkRevisionSummaryController', GeneTalkRevisionSummaryController);

  // @ngInject
  function geneTalkRevisionSummary() {
    var directive = {
      restrict: 'E',
      replace: true,
      require: ['^^entityTalkView', '^^entityView'],
      templateUrl: 'app/views/events/genes/talk/geneTalkRevisionSummary.tpl.html',
      link: geneTalkRevisionsSummaryLink,
      controller: 'GeneTalkRevisionSummaryController'
    };
    return directive;
  }

  // @ngInject
  function geneTalkRevisionsSummaryLink(scope, element, attributes, controllers) {
    var ctrl,
      geneTalkModel,
      geneModel,
      changeId;

    ctrl = scope.ctrl = {};
    geneTalkModel = scope.geneTalkModel= controllers[0].entityTalkModel;
    geneModel = scope.geneModel = controllers[1].entityModel;
    changeId = scope.changeId;

    geneTalkModel.actions.getChange(changeId);
    geneTalkModel.actions.getChangeComments(changeId);

  }

  // @ngInject
  function GeneTalkRevisionSummaryController($scope, $stateParams) {
    // wait until models linked

    $scope.changeId = $stateParams.changeId;

    $scope.acceptChange = function() {
      $scope.geneTalkModel.actions.acceptChange($stateParams.changeId)
        .then(function() {
          $scope.geneTalkModel.actions.getChanges($stateParams.geneId); // will eventually refresh revisions data grid
          $scope.geneModel.actions.refresh(); // will eventually refresh parent gene
        });
    };

    $scope.rejectChange = function() {
      $scope.geneTalkModel.actions.rejectChange($stateParams.changeId)
        .then(function() {
          $scope.geneTalkModel.actions.getChanges($stateParams.geneId);
        });
    };
  }
})();
<|endoftext|>


<commit_before>(function () {
    var Ext = window.Ext4 || window.Ext;

    Ext.define('Rally.apps.roadmapplanningboard.TimeframeDatesPopoverView', {
        extend: 'Rally.ui.popover.Popover',
        alias: 'widget.capacitypopover',
        requires: [
            'Rally.apps.roadmapplanningboard.util.TimelineViewModel',
            'Rally.ui.picker.DatePicker',
            'Rally.ui.DateField'
        ],
        placement: 'bottom',
        shouldHidePopoverOnBodyClick: false,
        shouldHidePopoverOnIframeClick: false,
        saveOnClose: true,
        closable: false,
        waitTimeForDateFieldValidation: 100,
        cls: 'roadmap-planning-popover',
        chevronPrefixCls: 'roadmap-planning-popover-chevron',
        config: {
            timelineViewModel: null
        },

        initComponent: function () {
            this.items = this._getItems();
            this.callParent(arguments);

            this.addEvents(
                'save'
            );

            this.startDate = this.down('#startDate');
            this.endDate = this.down('#endDate');
        },

        _getItems: function () {
            return [
                {
                    itemId: 'datesPopoverLayout',
                    layout: {
                        type: 'table',
                        align: 'center',
                        columns: 3
                    },
                    items: [
                        {
                            xtype: 'component',
                            html: 'Date Range',
                            cls: 'popoverDateRangeText'
                        },
                        {
                            xtype: 'container',
                            colspan: 2,
                            cellCls: 'date-buttons',
                            items: [
                                {
                                    xtype: 'rallybutton',
                                    itemId: 'datesCancel',
                                    text: 'Cancel',
                                    cls: 'secondary dark button small right',
                                    listeners: {
                                        click: this._onCancel,
                                        scope: this
                                    }
                                },
                                {
                                    xtype: 'rallybutton',
                                    itemId: 'datesDone',
                                    text: 'Done',
                                    cls: 'primary button small right',
                                    listeners: {
                                        click: this._onDone,
                                        scope: this
                                    }
                                }
                            ]
                        },
                        this._getDateFieldConfig('start', this.timelineViewModel.currentTimeframe.start),
                        {
                            xtype: 'component',
                            html: 'to',
                            cls: 'popoverToText'
                        },
                        this._getDateFieldConfig('end', this.timelineViewModel.currentTimeframe.end),
                        {
                            xtype: 'component',
                            itemId: 'dateType',
                            tpl: "
{dateType}
",
                            colspan: 3
                        }
                    ]
                },
                {
                    xtype: 'component',
                    autoEl: 'div',
                    id: 'startdate-validation-error',
                    cls: ['form-error-msg-field']
                },
                {
                    xtype: 'component',
                    autoEl: 'div',
                    id: 'enddate-validation-error',
                    cls: ['form-error-msg-field']
                }
            ];
        },

        _getDateFieldConfig: function (fieldPrefix, value) {
            var _this = this;
            var displayText = fieldPrefix + ' date';

            return {
                xtype: 'rallydatefield',
                cls: 'dateField',
                itemId: fieldPrefix + 'Date',
                msgTarget: fieldPrefix + 'date-validation-error',
                checkChangeBuffer: this.waitTimeForDateFieldValidation,
                value: value,
                validateOnBlur: false,
                validator: function () {
                    return _this._validateDateRanges(this);
                },
                onTriggerClick: function () {
                    return _this._createPicker(this, displayText);
                },
                listeners: {
                    validitychange: function (dateField, isValid) {
                        var doneButton = this.down('#datesDone');

                        if(isValid) {
                            doneButton.enable();
                        } else {
                            doneButton.disable();
                        }
                    },
                    focus: function (dateField) {
                        if (this.picker && this.picker.dateFieldId !== dateField.itemId) {
                            this._createPicker(dateField, displayText);
                        } else {
                            this._resetAndAddClsToDateField(dateField);
                        }
                    },
                    aftervalidate: function (dateField, isValid) {
                        if (this.picker && isValid) {
                            this.picker.value = dateField.getValue();
                            this._updatePicker(dateField);
                        }
                    },
                    scope: this
                }
            };
        },

        _validateDateRanges: function (dateField) {
            var start = dateField.itemId === this.startDate.itemId ? this.startDate.getValue() : this.timelineViewModel.currentTimeframe.start;
            var end = dateField.itemId === this.endDate.itemId ? this.endDate.getValue() : this.timelineViewModel.currentTimeframe.end;

            try {
                this.timelineViewModel.setCurrentTimeframe({
                    start: start,
                    end: end
                });
                return true;
            } catch(error) {
                return error;
            }
        },

        _onCancel: function () {
            this.saveOnClose = false;
            this.destroy();
        },

        _onDone: function () {
            this.saveOnClose = true;
            this.destroy();
        },

        destroy: function () {
            if(this.saveOnClose && this.startDate.isValid() && this.endDate.isValid()) {
                this._save();
            }

            this.callParent(arguments);
        },

        _save: function () {
            this.fireEvent('save', {
                startDate: this.timelineViewModel.currentTimeframe.start,
                endDate: this.timelineViewModel.currentTimeframe.end
            });
        },

        _createPicker: function (dateField, displayText) {
            var _this = this;

            if (this.picker) {
                this.picker.destroy();
            }

            var pickerOpts = {
                xtype: 'rallydatepicker',
                itemId: 'datePicker',
                floating: false,
                hidden: false,
                focusOnShow: false,
                focusOnToFront: false,
                enableMonthPicker: false,
                colspan: 3,
                dateFieldId: dateField.itemId,
                handler: function (picker, date) {
                    dateField.setValue(date);

                    _this._updatePicker(dateField);
                    _this._addDateSelectedTransitions(dateField);
                },
                minDate: this._getPickerMinDate(dateField),
                maxDate: this._getPickerMaxDate(dateField),
                rangeStart: this.timelineViewModel.currentTimeframe.start,
                rangeEnd: this.timelineViewModel.currentTimeframe.end
            };

            if (dateField.itemId === this.startDate.itemId) {
                pickerOpts.value = this.timelineViewModel.currentTimeframe.start;
                pickerOpts.minText = 'This date overlaps an earlier timeframe';
                pickerOpts.maxText = 'This date is after the end date';
            } else {
                pickerOpts.value = this.timelineViewModel.currentTimeframe.end;
                pickerOpts.minText = 'This date is before the start date';
                pickerOpts.maxText = 'This date overlaps a later timeframe';
            }

            this.picker = this.down('#datesPopoverLayout').add(pickerOpts);

            this._addPickerClasses();
            this._resetAndAddClsToDateField(dateField);
            this.down('#dateType').update({
                dateType: displayText
            });
        },

        _updatePicker: function (dateField) {
            this.picker.rangeStart = this.timelineViewModel.currentTimeframe.start;
            this.picker.rangeEnd = this.timelineViewModel.currentTimeframe.end;
            this.picker.update(dateField.getValue(), true);
        },

        _getPickerMinDate: function (dateField) {
            var prevTimeframe = this.timelineViewModel.getPreviousTimeframe();

            if (dateField.itemId === this.endDate.itemId) {
                return this.timelineViewModel.currentTimeframe.start;
            }

            if (prevTimeframe) {
                return Ext.Date.add(prevTimeframe.end, Ext.Date.DAY, 1);
            }
        },

        _getPickerMaxDate: function (dateField) {
            var nextTimeframe = this.timelineViewModel.getNextTimeframe();

            if (dateField.itemId === this.startDate.itemId) {
                return this.timelineViewModel.currentTimeframe.end;
            }

            if (nextTimeframe) {
                return Ext.Date.add(nextTimeframe.start, Ext.Date.DAY, -1);
            }
        },

        _addDateSelectedTransitions: function (dateField) {
            dateField.addCls('transition-bg-color');
            dateField.addCls('dateSelected');
            return setTimeout(function () {
                return dateField.removeCls('dateSelected');
            }, 1);
        },

        _resetAndAddClsToDateField: function (dateField) {
            _.each([this.startDate, this.endDate], function (comp) {
                comp.removeCls('triggerSelected');
            });

            dateField.addCls('triggerSelected');
        },

        _addPickerClasses: function () {
            var datePickerPrev = this.picker.getEl().down( '.' + Ext.baseCSSPrefix + 'datepicker-prev a');
            datePickerPrev.addCls('icon-chevron-left');

            var datePickerNext = this.picker.getEl().down('.' + Ext.baseCSSPrefix + 'datepicker-next a');
            datePickerNext.addCls('icon-chevron-right');

            var todayButton = this.picker.getEl().down('.' + Ext.baseCSSPrefix + 'datepicker-footer .' + Ext.baseCSSPrefix + 'btn');
            todayButton.addCls('secondary');
        }
    });

}).call(this);
<commit_msg>DE19509 - fix some merge issues<commit_after>(function () {
    var Ext = window.Ext4 || window.Ext;

    Ext.define('Rally.apps.roadmapplanningboard.TimeframeDatesPopoverView', {
        extend: 'Rally.ui.popover.Popover',
        alias: 'widget.capacitypopover',
        requires: [
            'Rally.apps.roadmapplanningboard.util.TimelineViewModel',
            'Rally.ui.picker.DatePicker',
            'Rally.ui.DateField'
        ],
        placement: 'bottom',
        shouldHidePopoverOnBodyClick: false,
        shouldHidePopoverOnIframeClick: false,
        saveOnClose: true,
        closable: false,
        waitTimeForDateFieldValidation: 100,
        cls: 'roadmap-planning-popover',
        chevronPrefixCls: 'roadmap-planning-popover-chevron',
        config: {
            timelineViewModel: null
        },

        initComponent: function () {
            this.items = this._getItems();
            this.callParent(arguments);

            this.addEvents(
                'save'
            );

            this.startDate = this.down('#startDate');
            this.endDate = this.down('#endDate');
        },

        _getItems: function () {
            return [
                {
                    itemId: 'datesPopoverLayout',
                    layout: {
                        type: 'table',
                        align: 'center',
                        columns: 3
                    },
                    items: [
                        {
                            xtype: 'component',
                            html: 'Date Range',
                            cls: 'popoverDateRangeText'
                        },
                        {
                            xtype: 'container',
                            colspan: 2,
                            cellCls: 'date-buttons',
                            items: [
                                {
                                    xtype: 'rallybutton',
                                    itemId: 'datesCancel',
                                    text: 'Cancel',
                                    cls: 'secondary dark button small right',
                                    listeners: {
                                        click: this._onCancel,
                                        scope: this
                                    }
                                },
                                {
                                    xtype: 'rallybutton',
                                    itemId: 'datesDone',
                                    text: 'Done',
                                    cls: 'primary button small right',
                                    listeners: {
                                        click: this._onDone,
                                        scope: this
                                    }
                                }
                            ]
                        },
                        this._getDateFieldConfig('start', this.timelineViewModel.currentTimeframe.start),
                        {
                            xtype: 'component',
                            html: 'to',
                            cls: 'popoverToText'
                        },
                        this._getDateFieldConfig('end', this.timelineViewModel.currentTimeframe.end),
                        {
                            xtype: 'component',
                            itemId: 'dateType',
                            tpl: "
{dateType}
",
                            colspan: 3
                        }
                    ]
                },
                {
                    xtype: 'component',
                    autoEl: 'div',
                    id: 'startdate-validation-error',
                    cls: ['form-error-msg-field']
                },
                {
                    xtype: 'component',
                    autoEl: 'div',
                    id: 'enddate-validation-error',
                    cls: ['form-error-msg-field']
                }
            ];
        },

        _getDateFieldConfig: function (fieldPrefix, value) {
            var _this = this;
            var displayText = fieldPrefix + ' date';

            return {
                xtype: 'rallydatefield',
                cls: 'dateField',
                itemId: fieldPrefix + 'Date',
                msgTarget: fieldPrefix + 'date-validation-error',
                checkChangeBuffer: this.waitTimeForDateFieldValidation,
                value: value,
                validateOnBlur: false,
                validator: function () {
                    return _this._validateDateRanges(this);
                },
                onTriggerClick: function () {
                    return _this._createPicker(this, displayText);
                },
                listeners: {
                    validitychange: function (dateField, isValid) {
                        var doneButton = this.down('#datesDone');

                        if(isValid) {
                            doneButton.enable();
                        } else {
                            doneButton.disable();
                        }
                    },
                    focus: function (dateField) {
                        if (this.picker && this.picker.dateFieldId !== dateField.itemId) {
                            this._createPicker(dateField, displayText);
                        } else {
                            this._resetAndAddClsToDateField(dateField);
                        }
                    },
                    aftervalidate: function (dateField, isValid) {
                        if (this.picker && isValid) {
                            this.picker.value = dateField.getValue();
                            this._updatePicker(dateField);
                        }
                    },
                    scope: this
                }
            };
        },

        _validateDateRanges: function (dateField) {
            var start = dateField.itemId === this.startDate.itemId ? this.startDate.getValue() : this.timelineViewModel.currentTimeframe.start;
            var end = dateField.itemId === this.endDate.itemId ? this.endDate.getValue() : this.timelineViewModel.currentTimeframe.end;

            try {
                this.timelineViewModel.setCurrentTimeframe({
                    start: start,
                    end: end
                });
                return true;
            } catch(error) {
                return error;
            }
        },

        _onCancel: function () {
            this.saveOnClose = false;
            this.destroy();
        },

        _onDone: function () {
            this.saveOnClose = true;
            this.destroy();
        },

        destroy: function () {
            if(this.saveOnClose && this.startDate.isValid() && this.endDate.isValid()) {
                this._save();
            }

            this.callParent(arguments);
        },

        _save: function () {
            this.fireEvent('save', {
                startDate: this.timelineViewModel.currentTimeframe.start,
                endDate: this.timelineViewModel.currentTimeframe.end
            });
        },

        _createPicker: function (dateField, displayText) {
            var _this = this;

            if (this.picker) {
                this.picker.destroy();
            }

            var pickerOpts = {
                xtype: 'rallydatepicker',
                itemId: 'datePicker',
                floating: false,
                hidden: false,
                focusOnShow: false,
                focusOnToFront: false,
                enableMonthPicker: false,
                colspan: 3,
                dateFieldId: dateField.itemId,
                handler: function (picker, date) {
                    dateField.setValue(date);

                    _this._updatePicker(dateField);
                    _this._addDateSelectedTransitions(dateField);
                },
                minDate: this._getPickerMinDate(dateField),
                maxDate: this._getPickerMaxDate(dateField),
                rangeStart: this.timelineViewModel.currentTimeframe.start,
                rangeEnd: this.timelineViewModel.currentTimeframe.end
            };

            if (dateField.itemId === this.startDate.itemId) {
                pickerOpts.value = this.timelineViewModel.currentTimeframe.start;
                pickerOpts.minText = 'This date overlaps an earlier timeframe';
                pickerOpts.maxText = 'This date is after the end date';
            } else {
                pickerOpts.value = this.timelineViewModel.currentTimeframe.end;
                pickerOpts.minText = 'This date is before the start date';
                pickerOpts.maxText = 'This date overlaps a later timeframe';
            }

            this.picker = this.down('#datesPopoverLayout').add(pickerOpts);

            this._addPickerClasses();
            this._resetAndAddClsToDateField(dateField);
            this.down('#dateType').update({
                dateType: displayText
            });
        },

        _updatePicker: function (dateField) {
            this.picker.rangeStart = this.timelineViewModel.currentTimeframe.start;
            this.picker.rangeEnd = this.timelineViewModel.currentTimeframe.end;
            this.picker.update(dateField.getValue(), true);
        },

        _getPickerMinDate: function (dateField) {
            var prevTimeframe = this.timelineViewModel.getPreviousTimeframe();

            if (dateField.itemId === this.endDate.itemId) {
                return this.timelineViewModel.currentTimeframe.start;
            }

            if (prevTimeframe) {
                return Ext.Date.add(prevTimeframe.end, Ext.Date.DAY, 1);
            }
        },

        _getPickerMaxDate: function (dateField) {
            var nextTimeframe = this.timelineViewModel.getNextTimeframe();

            if (dateField.itemId === this.startDate.itemId) {
                return this.timelineViewModel.currentTimeframe.end;
            }

            if (nextTimeframe) {
                return Ext.Date.add(nextTimeframe.start, Ext.Date.DAY, -1);
            }
        },

        _addDateSelectedTransitions: function (dateField) {
            dateField.addCls('transition-bg-color');
            dateField.addCls('dateSelected');
            return setTimeout(function () {
                return dateField.removeCls('dateSelected');
            }, 1);
        },

        _resetAndAddClsToDateField: function (dateField) {
            _.each([this.startDate, this.endDate], function (comp) {
                comp.removeCls('triggerSelected');
            });

            dateField.addCls('triggerSelected');
        },

        _addPickerClasses: function () {
            var datePickerPrev = this.picker.getEl().down( '.' + Ext.baseCSSPrefix + 'datepicker-prev');
            datePickerPrev.addCls('icon-chevron-left');

            var datePickerNext = this.picker.getEl().down('.' + Ext.baseCSSPrefix + 'datepicker-next');
            datePickerNext.addCls('icon-chevron-right');

            var todayButton = this.picker.getEl().down('.' + Ext.baseCSSPrefix + 'datepicker-footer .' + Ext.baseCSSPrefix + 'btn');
            todayButton.addCls('secondary');
        }
    });

}).call(this);
<|endoftext|>