playback.js 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  3. * You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. define(["jquery", "util", "session", "storage", "require"], function ($, util, session, storage, require) {
  5. var playback = util.Module("playback");
  6. var assert = util.assert;
  7. var ALWAYS_REPLAY = {
  8. "cursor-update": true,
  9. "scroll-update": true
  10. };
  11. playback.getLogs = function (url) {
  12. if (url.search(/^local:/) === 0) {
  13. return $.Deferred(function (def) {
  14. storage.get("recording." + url.substr("local:".length)).then(function (logs) {
  15. if (! logs) {
  16. def.resolve(null);
  17. return;
  18. }
  19. logs = parseLogs(logs);
  20. def.resolve(logs);
  21. }, function (error) {
  22. def.reject(error);
  23. });
  24. });
  25. }
  26. return $.Deferred(function (def) {
  27. $.ajax({
  28. url: url,
  29. dataType: "text"
  30. }).then(
  31. function (logs) {
  32. logs = parseLogs(logs);
  33. def.resolve(logs);
  34. },
  35. function (error) {
  36. def.reject(error);
  37. });
  38. });
  39. };
  40. function parseLogs(logs) {
  41. logs = logs.replace(/\r\n/g, '\n');
  42. logs = logs.split(/\n/g);
  43. var result = [];
  44. for (var i=0; i<logs.length; i++) {
  45. var line = logs[i];
  46. line = line.replace(/^\s+/, "").replace(/\s+$/, "");
  47. if (line.search(/\/\*/) === 0) {
  48. var last = line.search(/\*\//);
  49. if (last == -1) {
  50. console.warn("bad line:", line);
  51. continue;
  52. }
  53. line = line.substr(last+2);
  54. }
  55. line = line.replace(/^\s+/, "");
  56. if (! line) {
  57. continue;
  58. }
  59. line = JSON.parse(line);
  60. result.push(line);
  61. }
  62. return Logs(result);
  63. }
  64. var Logs = util.Class({
  65. constructor: function (logs, fromStorage) {
  66. this.logs = logs;
  67. this.fromStorage = fromStorage;
  68. this.pos = 0;
  69. },
  70. play: function () {
  71. this.start = Date.now();
  72. if (this.pos >= this.logs.length) {
  73. this.unload();
  74. return;
  75. }
  76. if (this.pos !== 0) {
  77. // First we need to play the hello
  78. var toReplay = [];
  79. var foundHello = false;
  80. for (var i=this.pos-1; i>=0; i--) {
  81. var item = this.logs[i];
  82. if (ALWAYS_REPLAY[item.type]) {
  83. toReplay.push(item);
  84. }
  85. if (item.type == "hello" || item.type == "hello-back") {
  86. this.playItem(item);
  87. foundHello = true;
  88. break;
  89. }
  90. }
  91. if (! foundHello) {
  92. console.warn("No hello message found before position", this.pos);
  93. }
  94. toReplay.reverse();
  95. for (i=0; i<toReplay.length; i++) {
  96. this.playItem(toReplay[i]);
  97. }
  98. }
  99. this.playOne();
  100. },
  101. cancel: function () {
  102. if (this.playTimer) {
  103. clearTimeout(this.playTimer);
  104. this.playTimer = null;
  105. }
  106. this.start = null;
  107. this.pos = 0;
  108. this.unload();
  109. },
  110. pause: function () {
  111. if (this.playTimer) {
  112. clearTimeout(this.playTimer);
  113. this.playTimer = null;
  114. }
  115. },
  116. playOne: function () {
  117. this.playTimer = null;
  118. if (this.pos >= this.logs.length) {
  119. this.unload();
  120. return;
  121. }
  122. var item = this.logs[this.pos];
  123. this.playItem(item);
  124. this.pos++;
  125. if (this.pos >= this.logs.length) {
  126. this.unload();
  127. return;
  128. }
  129. var next = this.logs[this.pos];
  130. var pause = next.date - item.date;
  131. this.playTimer = setTimeout(this.playOne.bind(this), pause);
  132. if (this.fromStorage) {
  133. this.savePos();
  134. }
  135. },
  136. playItem: function (item) {
  137. if (item.type == "hello") {
  138. // We may need to pause here
  139. if (item.url != (location.href+"").replace(/\#.*/, "")) {
  140. this.pause();
  141. }
  142. }
  143. try {
  144. session._getChannel().onmessage(item);
  145. } catch (e) {
  146. console.warn("Could not play back message:", item, "error:", e);
  147. }
  148. },
  149. save: function () {
  150. this.fromStorage = true;
  151. storage.set("playback.logs", this.logs);
  152. this.savePos();
  153. },
  154. savePos: function () {
  155. storage.set("playback.pos", this.pos);
  156. },
  157. unload: function () {
  158. if (this.fromStorage) {
  159. storage.set("playback.logs", undefined);
  160. storage.set("playback.pos", undefined);
  161. }
  162. // FIXME: should do a bye message here
  163. }
  164. });
  165. playback.getRunningLogs = function () {
  166. return storage.get("playback.logs").then(function (value) {
  167. if (! value) {
  168. return null;
  169. }
  170. var logs = Logs(value, true);
  171. return storage.get("playback.pos").then(function (pos) {
  172. pos = pos || 0;
  173. logs.pos = pos;
  174. return logs;
  175. });
  176. });
  177. };
  178. return playback;
  179. });