ngrok.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. var ngrok = angular.module("ngrok", []);
  2. var hexRepr = function(bytes) {
  3. var buf = [];
  4. var ascii = [];
  5. for (var i=0; i<bytes.length; ++i) {
  6. var b = bytes[i];
  7. if (!(i%8) && i!=0) {
  8. buf.push("\t");
  9. buf.push.apply(buf, ascii)
  10. buf.push('\n');
  11. ascii = [];
  12. }
  13. if (b < 16) {
  14. buf.push("0");
  15. }
  16. if (b < 0x20 || b > 0x7e) {
  17. ascii.push('.');
  18. } else {
  19. ascii.push(String.fromCharCode(b));
  20. }
  21. buf.push(b.toString(16));
  22. buf.push(" ");
  23. ascii.push(" ");
  24. }
  25. if (ascii.length > 0) {
  26. var charsLeft = 8 - (ascii.length / 2);
  27. for (i=0; i<charsLeft; ++i) {
  28. buf.push(" ");
  29. }
  30. buf.push("\t");
  31. buf.push.apply(buf, ascii);
  32. }
  33. return buf.join("");
  34. }
  35. ngrok.directive({
  36. "keyval": function() {
  37. return {
  38. scope: {
  39. title: "@",
  40. tuples: "=",
  41. },
  42. replace: true,
  43. restrict: "E",
  44. template: "" +
  45. '<div ng-show="hasKeys">' +
  46. '<h6>{{title}}</h6>' +
  47. '<table class="table params">' +
  48. '<tr ng-repeat="(key, value) in tuples">' +
  49. '<th>{{ key }}</th>' +
  50. '<td>{{ value }}</td>' +
  51. '</tr>' +
  52. '</table>' +
  53. '</div>',
  54. link: function($scope) {
  55. $scope.hasKeys = false;
  56. for (key in $scope.tuples) { $scope.hasKeys = true; break; }
  57. }
  58. };
  59. },
  60. "tabs": function() {
  61. return {
  62. scope: {
  63. "tabs": "@",
  64. "btn": "@",
  65. "onbtnclick": "&"
  66. },
  67. replace: true,
  68. template: '' +
  69. '<ul class="nav nav-pills">' +
  70. '<li ng-repeat="tab in tabNames" ng-class="{\'active\': isTab(tab)}">' +
  71. '<a href="" ng-click="setTab(tab)">{{tab}}</a>' +
  72. '</li>' +
  73. '<li ng-show="!!btn" class="pull-right"> <button class="btn btn-primary" ng-click="onbtnclick()">{{btn}}</button></li>' +
  74. '</ul>',
  75. link: function postLink(scope, element, attrs) {
  76. scope.tabNames = attrs.tabs.split(",");
  77. scope.activeTab = scope.tabNames[0];
  78. scope.setTab = function(t) {
  79. scope.activeTab = t;
  80. };
  81. scope.$parent.isTab = scope.isTab = function(t) {
  82. return t == scope.activeTab;
  83. };
  84. },
  85. };
  86. },
  87. "body": function($timeout) {
  88. return {
  89. scope: {
  90. "body": "=",
  91. "binary": "="
  92. },
  93. template: '' +
  94. '<h6 ng-show="hasBody">' +
  95. '{{ Body.Length }} bytes ' +
  96. '{{ Body.RawContentType }}' +
  97. '</h6>' +
  98. '' +
  99. '<div ng-show="!isForm && !binary">' +
  100. '<pre ng-show="hasBody"><code ng-class="syntaxClass">{{ Body.Text }}</code></pre>' +
  101. '</div>' +
  102. '' +
  103. '<div ng-show="isForm">' +
  104. '<keyval title="Form Params" tuples="Body.Form">' +
  105. '</div>' +
  106. '<div ng-show="hasError" class="alert">' +
  107. '{{ Body.Error }}' +
  108. '</div>',
  109. controller: function($scope) {
  110. var body = $scope.body;
  111. if ($scope.binary) {
  112. body.Text = "";
  113. } else {
  114. body.Text = Base64.decode(body.Text).text;
  115. }
  116. $scope.isForm = (body.ContentType == "application/x-www-form-urlencoded");
  117. $scope.hasBody = (body.Length > 0);
  118. $scope.hasError = !!body.Error;
  119. $scope.syntaxClass = {
  120. "text/xml": "xml",
  121. "application/xml": "xml",
  122. "text/html": "xml",
  123. "text/css": "css",
  124. "application/json": "json",
  125. "text/javascript": "javascript",
  126. "application/javascript": "javascript",
  127. }[body.ContentType];
  128. var transform = {
  129. "xml": "xml",
  130. "json": "json"
  131. }[$scope.syntaxClass];
  132. if (!$scope.hasError && !!transform) {
  133. try {
  134. // vkbeautify does poorly at formatting html
  135. if (body.ContentType != "text/html") {
  136. body.Text = vkbeautify[transform](body.Text);
  137. }
  138. } catch (e) {
  139. }
  140. }
  141. $scope.Body = body;
  142. },
  143. link: function($scope, $elem) {
  144. $timeout(function() {
  145. $code = $elem.find("code").get(0);
  146. hljs.highlightBlock($code);
  147. if ($scope.Body.ErrorOffset > -1) {
  148. var offset = $scope.Body.ErrorOffset;
  149. function textNodes(node) {
  150. var textNodes = [];
  151. function getTextNodes(node) {
  152. if (node.nodeType == 3) {
  153. textNodes.push(node);
  154. } else {
  155. for (var i = 0, len = node.childNodes.length; i < len; ++i) {
  156. getTextNodes(node.childNodes[i]);
  157. }
  158. }
  159. }
  160. getTextNodes(node);
  161. return textNodes;
  162. }
  163. var tNodes = textNodes($elem.find("code").get(0));
  164. for (var i=0; i<tNodes.length; i++) {
  165. offset -= tNodes[i].nodeValue.length;
  166. if (offset < 0) {
  167. $(tNodes[i]).parent().css("background-color", "orange");
  168. break;
  169. }
  170. }
  171. }
  172. });
  173. }
  174. };
  175. }
  176. });
  177. ngrok.controller({
  178. "HttpTxns": function($scope) {
  179. $scope.publicUrl = window.data.UiState.Url;
  180. $scope.txns = window.data.Txns;
  181. if (!!window.WebSocket) {
  182. var ws = new WebSocket("ws://localhost:4040/_ws");
  183. ws.onopen = function() {
  184. console.log("connected websocket for real-time updates");
  185. };
  186. ws.onmessage = function(message) {
  187. $scope.$apply(function() {
  188. $scope.txns.unshift(JSON.parse(message.data));
  189. });
  190. };
  191. ws.onerror = function(err) {
  192. console.log("Web socket error:" + err);
  193. };
  194. ws.onclose = function(cls) {
  195. console.log("Web socket closed:" + cls);
  196. };
  197. }
  198. },
  199. "HttpRequest": function($scope) {
  200. $scope.Req = $scope.txn.Req;
  201. $scope.replay = function() {
  202. $.ajax({
  203. type: "POST",
  204. url: "/http/in/replay",
  205. data: { txnid: $scope.txn.Id }
  206. });
  207. }
  208. var decoded = Base64.decode($scope.Req.Raw);
  209. $scope.Req.RawBytes = hexRepr(decoded.bytes);
  210. if (!$scope.Req.Binary) {
  211. $scope.Req.RawText = decoded.text;
  212. }
  213. },
  214. "HttpResponse": function($scope) {
  215. $scope.Resp = $scope.txn.Resp;
  216. $scope.statusClass = {
  217. '2': "text-info",
  218. '3': "muted",
  219. '4': "text-warning",
  220. '5': "text-error"
  221. }[$scope.Resp.Status[0]];
  222. var decoded = Base64.decode($scope.Resp.Raw);
  223. $scope.Resp.RawBytes = hexRepr(decoded.bytes);
  224. if (!$scope.Resp.Binary) {
  225. $scope.Resp.RawText = decoded.text;
  226. }
  227. }
  228. });