delta-words.js 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. define([
  2. '/bower_components/chainpad/chainpad.dist.js',
  3. ], function (ChainPad) {
  4. var Diff = ChainPad.Diff;
  5. var isSpace = function (S, i) {
  6. return /^\s$/.test(S.charAt(i));
  7. };
  8. var leadingBoundary = function (S, offset) {
  9. if (/\s/.test(S.charAt(offset))) { return offset; }
  10. while (offset > 0) {
  11. offset--;
  12. if (isSpace(S, offset)) { offset++; break; }
  13. }
  14. return offset;
  15. };
  16. var trailingBoundary = function (S, offset) {
  17. if (isSpace(S, offset)) { return offset; }
  18. while (offset < S.length && !/\s/.test(S.charAt(offset))) {
  19. offset++;
  20. }
  21. return offset;
  22. };
  23. var opsToWords = function (previous, current) {
  24. var output = [];
  25. Diff.diff(previous, current).forEach(function (op) {
  26. // ignore deleted sections...
  27. var offset = op.offset;
  28. var toInsert = op.toInsert;
  29. // given an operation, check whether it is a word fragment,
  30. // if it is, expand it to its word boundaries
  31. var first = current.slice(leadingBoundary(current, offset), offset);
  32. var last = current.slice(offset + toInsert.length, trailingBoundary(current, offset + toInsert.length));
  33. var result = first + toInsert + last;
  34. // concat-in-place
  35. Array.prototype.push.apply(output, result.split(/\s+/));
  36. });
  37. return output.filter(Boolean);
  38. };
  39. var runningDiff = function (getter, f, time) {
  40. var last = getter();
  41. // first time through, send all the words :D
  42. f(opsToWords("", last));
  43. return setInterval(function () {
  44. var current = getter();
  45. // find inserted words...
  46. var words = opsToWords(last, current);
  47. last = current;
  48. f(words);
  49. }, time);
  50. };
  51. return runningDiff;
  52. });