view.js 58 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275
  1. window.Tracker = window.Tracker || {};
  2. Tracker.View = (function( window ){
  3. var global, host, location, push, join, version;
  4. version = "1.8.9";
  5. global = window;
  6. host = global.document;
  7. location = global.location;
  8. var View = function(){
  9. return {
  10. templates: {
  11. url: Tracker.Util.tmpl( "<a href='<%= url %>' target='_blank'><%= url %></a>" ),
  12. frameset: Tracker.Util.tmpl( [
  13. "<!DOCTYPE html>",
  14. "<html>",
  15. "<head>",
  16. "<meta charset='<%= charset %>'>",
  17. "<meta name='description' content='fehelper-tracker-frame'>",
  18. "<title><%= title %></title>",
  19. "<style type='text/css'>",
  20. "html, body{ margin: 0; padding: 0; overflow: hidden; width: 100%; height: 100%; position: relative; }",
  21. ".fullness{ position: absolute; left: 0; right: 0; top: 0; bottom: 0; }",
  22. "#tracker_proxy {display:none;}",
  23. "#wrapper{}",
  24. "#tracker_controller_ct{ z-index: 10; }",
  25. "#tracker_page_ct{ top: 43px; z-index: 20; background-color: #fff; }",
  26. "body.control-power-mode #tracker_page_ct{ z-index: 0; }",
  27. "body.hidden-page-mode #tracker_page_ct{ display: none; }",
  28. "iframe{ border: 0; width: 100%; height: 100%; }",
  29. "</style>",
  30. "</head>",
  31. "<body>",
  32. "<div id='wrapper' class='fullness'>",
  33. "<div id='tracker_proxy'><input type=\"button\" id=\"btnTrackerProxy\"></div>",
  34. "<div id='tracker_controller_ct' class='fullness'><iframe src='about:blank' id='tracker_controller' name='tracker_controller' frameborder='no'></iframe></div>",
  35. "<div id='tracker_page_ct' class='fullness'><iframe src='<%= url %>' id='tracker_page' name='tracker_page' frameborder='no'></iframe></div>",
  36. "</div>",
  37. "</body>",
  38. "</html>"
  39. ].join( "" ) ),
  40. controllerPage: Tracker.Util.tmpl( [
  41. "<!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Frameset//EN' 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd'>",
  42. "<html>",
  43. "<head>",
  44. "<meta charset='<%= charset %>'>",
  45. "<meta name='author' content='dron'>",
  46. "<title>Tracker!</title>",
  47. "<link href='<%= trackerCss %>' type='text/css' rel='stylesheet' />",
  48. "</head>",
  49. "<body>",
  50. "<%= header %>",
  51. "<div class='main' id='main'>",
  52. "<ul id='pages' class='unstyled tab-content'>",
  53. "<% if( mode == 'embed' ){ %>",
  54. "<li class='tab-content-active target-web-page'></li>",
  55. "<% } %>",
  56. "<li class='<%= mode == 'embed' ? '' : 'tab-content-active' %>'>",
  57. "<%= codeList %>",
  58. "<%= codeDetail %>",
  59. "</li>",
  60. "</ul>",
  61. "</div>",
  62. "<script> window.readyState = 'done'; </script>",
  63. "</body>",
  64. "</html>"
  65. ].join( "" ) ),
  66. controllerTopbar: Tracker.Util.tmpl( [
  67. "<div id='loading' class='navbar'>",
  68. "<div class='navbar-inner'>",
  69. "<span>&#35831;&#31245;&#31561;&#65292;&#25910;&#38598;&#20013;...</span>",
  70. "<span id='waitTime'></span>",
  71. "</div>",
  72. "</div>",
  73. "<div id='top-navbar' class='navbar'>",
  74. "<div class='navbar-inner'>",
  75. '<a href="http://www.baidufe.com/feheler" target="_blank" class="fe-icon pull-left" action="frame#close">',
  76. '<img src="' + chrome.runtime.getURL('/static/img/fe-16.png') + '" alt="FeHelper">',
  77. "FeHelper:",
  78. '</a>',
  79. "<span class='fe-title'>Js覆盖面检测</span>",
  80. "<ul id='top-nav' class='nav pull-left' data-target='pages'>",
  81. "<% if( mode == 'embed' ){ %>",
  82. "<li><a href='' onclick='return false'>当前网页</a></li>",
  83. "<li data-name='code-list' class='active'><a href='' onclick='return false'>代码列表</a></li>",
  84. "<% }else{ %>",
  85. "<li class='active' data-name='code-list'><a href='' onclick='return false'>代码列表</a></li>",
  86. "<% } %>",
  87. "</ul>",
  88. "<ul class='nav pull-right'>",
  89. "<li class='dropdown'>",
  90. "<a href='' onclick='return false;' class='dropdown-toggle' data-toggle='dropdown'>",
  91. "视图切换",
  92. "<b class='caret'></b>",
  93. "</a>",
  94. "<ul class='dropdown-menu'>",
  95. "<li><a id='window-mode-trigger' action='frame#toggle' href='#' onclick='return false;'>单窗口模式</a></li>",
  96. "<li><a action='frame#close' href='#' onclick='return false;'>关闭控制台</a></li>",
  97. "</ul>",
  98. "</li>",
  99. "<li><a href='http://www.baidufe.com/fehelper/feedback.html' target='_blank'>意见反馈</a></li>",
  100. "</ul>",
  101. "</div>",
  102. "</div>",
  103. ].join( "" ) ),
  104. controllerCodeList: Tracker.Util.tmpl( [
  105. "<table class='table compact-width'>",
  106. "<thead>",
  107. "<tr>",
  108. "<th width='<%= widthIndex %>'>#</th>",
  109. "<th width='<%= widthName %>'>&#21517;&#31216;</th>",
  110. "<th width='<%= widthType %>'>&#31867;&#22411;</th>",
  111. "<th width='<%= widthCover %>'>&#25191;&#34892;&#35206;&#30422;</th>",
  112. "<th width='<%= widthCoverLine %>'>&#25191;&#34892;&#34892;&#25968;</th>",
  113. "<th width='<%= widthLines %>'>&#24635;&#34892;&#25968;</th>",
  114. "<th width='<%= widthSize %>'>&#21407;&#22987;&#22823;&#23567;</th>",
  115. "<th width='<%= widthBSize %>'>&#35299;&#21387;&#22823;&#23567;</th>",
  116. "<th width='<%= widthLoadConsum %>'>&#21152;&#36733;&#32791;&#26102;</th>",
  117. "<th width='<%= widthRunConsum %>'>&#36816;&#34892;&#32791;&#26102;</th>",
  118. "<th width='<%= widthRError %>'>&#25191;&#34892;&#25253;&#38169;</th>",
  119. "<th width='<%= widthSError %>'>&#35821;&#27861;&#38169;&#35823;</th>",
  120. "<th width='<%= widthState %>'>&#29366;&#24577;</th>",
  121. "<th width='*'>&nbsp;</th>",
  122. "</tr>",
  123. "</thead>",
  124. "</table>",
  125. "<div id='list-codes' class='scrollable'>",
  126. "<table class='table table-striped table-hover table-condensed'>",
  127. "<colgroup>",
  128. "<col width='<%= widthIndex %>'>",
  129. "<col width='<%= widthName %>'>",
  130. "<col width='<%= widthType %>'>",
  131. "<col width='<%= widthCover %>'>",
  132. "<col width='<%= widthCoverLine %>'>",
  133. "<col width='<%= widthLines %>'>",
  134. "<col width='<%= widthSize %>'>",
  135. "<col width='<%= widthBSize %>'>",
  136. "<col width='<%= widthLoadConsum %>'>",
  137. "<col width='<%= widthRunConsum %>'>",
  138. "<col width='<%= widthRError %>'>",
  139. "<col width='<%= widthSError %>'>",
  140. "<col width='<%= widthState %>'>",
  141. "</colgroup>",
  142. "<tbody id='list-codes-tbody'>",
  143. "</tbody>",
  144. "</table>",
  145. "</div>"
  146. ].join( "" ) ),
  147. controllerCodeDetail: Tracker.Util.tmpl( [
  148. "<div id='code-detail' class='absolute'>",
  149. "<div class='code-toolbar clearfix'>",
  150. "<ul class='code-toolbar-inner'>",
  151. "<li class='close-button-like'><button class='close' action='code#close'>&times;</button></li>",
  152. "<li class='tab-like'>",
  153. "<ul id='code-detail-head' class='nav nav-tabs' data-target='code-detail-body'>",
  154. "<li class='active'><a href='' onclick='return false;'>&#20195;&#30721;</a></li>",
  155. "<li><a href='' onclick='return false;'>&#20449;&#24687;</a></li>",
  156. "</ul>",
  157. "</li>",
  158. "<li class='label-like right tab-desc tab-desc-0'>&#26032;&#27963;&#21160;</li>",
  159. "<li class='image-like right tab-desc tab-desc-0'><div class='hooking image'></div></li>",
  160. "<li class='label-like right tab-desc tab-desc-0'>&#26410;&#25191;&#34892;</li>",
  161. "<li class='image-like right tab-desc tab-desc-0'><div class='unarrive image'></div></li>",
  162. "<li class='label-like right tab-desc tab-desc-0'>&#24050;&#25191;&#34892;</li>",
  163. "<li class='image-like right tab-desc tab-desc-0'><div class='arrive image'></div></li>",
  164. "<li class='label-like right tab-desc tab-desc-0'>&#22270;&#20363;&#65306;</li>",
  165. "</ul>",
  166. "</div>",
  167. "<ul class='unstyled tab-content' id='code-detail-body'>",
  168. "<li class='tab-content-active'>",
  169. "<div id='code-content' class='relative scrollable'></div>",
  170. "</li>",
  171. "<li class='scrollable'>",
  172. "<div id='code-info'></div>",
  173. "</li>",
  174. "</ul>",
  175. "</div>"
  176. ].join( "" ) ),
  177. controllerCodeInfo: Tracker.Util.tmpl( [
  178. "<dl class='group'>",
  179. "<dt>&#26469;&#28304;</dt>",
  180. "<dd><%= fileName %></dd>",
  181. "</dl>",
  182. "<dl class='group'>",
  183. "<dt>&#31867;&#22411;</dt>",
  184. "<dd><%= type %></dd>",
  185. "</dl>",
  186. "<dl class='group'>",
  187. "<dt>&#25191;&#34892;&#35206;&#30422;&#29575;</dt>",
  188. "<dd><%= rate %></dd>",
  189. "</dl>",
  190. "<dl class='group'>",
  191. "<dt>&#25191;&#34892;&#34892;&#25968;</dt>",
  192. "<dd><%= arriveRowsCount %></dd>",
  193. "</dl>",
  194. "<dl class='group'>",
  195. "<dt>&#24635;&#34892;&#25968;</dt>",
  196. "<dd><%= rowsCount %></dd>",
  197. "</dl>",
  198. "<dl class='group'>",
  199. "<dt>&#21407;&#22987;&#22823;&#23567;</dt>",
  200. "<dd><%= size %></dd>",
  201. "</dl>",
  202. "<dl class='group'>",
  203. "<dt>&#35299;&#21387;&#22823;&#23567;</dt>",
  204. "<dd><%= bsize %></dd>",
  205. "</dl>",
  206. "<dl class='group'>",
  207. "<dt>&#21152;&#36733;&#32791;&#26102;</dt>",
  208. "<dd><%= loadConsum %></dd>",
  209. "</dl>",
  210. "<dl class='group'>",
  211. "<dt>&#36816;&#34892;&#32791;&#26102;</dt>",
  212. "<dd><%= runConsum %></dd>",
  213. "</dl>",
  214. "<dl class='group'>",
  215. "<dt>&#25191;&#34892;&#25253;&#38169;</dt>",
  216. "<dd><%= rerror %></dd>",
  217. "</dl>",
  218. "<dl class='group'>",
  219. "<dt>&#35821;&#27861;&#38169;&#35823;</dt>",
  220. "<dd><%= serror %></dd>",
  221. "</dl>",
  222. "<dl class='group'>",
  223. "<dt>&#29366;&#24577;</dt>",
  224. "<dd><%= state %></dd>",
  225. "</dl>",
  226. ].join( "" ) ),
  227. codeListLine: Tracker.Util.tmpl( [
  228. "<tr data-code-id='<%= id %>'>",
  229. "<td><div class='ellipsisable' style='width: <%= widthIndex %>px;'><%= index %></div></td>",
  230. "<td><div class='ellipsisable' style='width: <%= widthName %>px;'><%= fileName %></div></td>",
  231. "<td><div class='ellipsisable' style='width: <%= widthType %>px;'><%= type %></div></td>",
  232. "<td><div id='code-<%= id %>-rate' class='ellipsisable' style='width: <%= widthCover %>px;'><%= rate %></div></td>",
  233. "<td><div id='code-<%= id %>-arriveRowsCount' class='ellipsisable' style='width: <%= widthCoverLine %>px;'><%= arriveRowsCount %></div></td>",
  234. "<td><div class='ellipsisable' style='width: <%= widthLines %>px;'><%= rowsCount %></div></td>",
  235. "<td><div class='ellipsisable' style='width: <%= widthSize %>px;'><%= size %></div></td>",
  236. "<td><div class='ellipsisable' style='width: <%= widthBSize %>px;'><%= bsize %></div></td>",
  237. "<td><div id='code-<%= id %>-loadConsum' class='ellipsisable' style='width: <%= widthLoadConsum %>px;'><%= loadConsum %></div></td>",
  238. "<td><div id='code-<%= id %>-runConsum' class='ellipsisable' style='width: <%= widthRunConsum %>px;'><%= runConsum %></div></td>",
  239. "<td><div id='code-<%= id %>-runErrors' class='ellipsisable' style='width: <%= widthRError %>px;'><%= rerror %></div></td>",
  240. "<td><div class='ellipsisable' style='width: <%= widthSError %>px;'><%= serror %></div></td>",
  241. "<td><div class='ellipsisable' style='width: <%= widthState %>px;'><%= state %></div></td>",
  242. "<td></td>",
  243. "</tr>"
  244. ].join( "" ) )
  245. },
  246. Loading: function(){
  247. var layer, span1, span2, animateTimer, count, progress, body;
  248. count = progress = 0;
  249. body = host.body;
  250. var create = function(){
  251. var span;
  252. layer = Tracker.Util.makeElement( "div", "position: fixed; padding: 30px; border: 1px solid rgba(255, 255, 255, .2); border-radius: 10px; background: #000; font-size: 20px; line-height: 20px; text-align: center; color: #fff; top: 50px; left: 50px; box-shadow: 0 0 5px #fff; z-index: 65535; font-family: 'Courier New', 'Heiti SC', 'Microsoft Yahei';" );
  253. layer.innerHTML = "&#27491;&#22312;&#20998;&#26512;&#32593;&#39029; <span>...</span> <span>(0/0)</span>";
  254. body.appendChild( layer );
  255. host.documentElement.scrollTop = body.scrollTop = 0;
  256. span = layer.getElementsByTagName( "span" );
  257. span1 = span[0];
  258. span2 = span[1];
  259. };
  260. var animate = function(){
  261. var count, word, n, s, e;
  262. count = 0;
  263. word = "......";
  264. clearInterval( animateTimer );
  265. animateTimer = setInterval( function(){
  266. n = count % 7;
  267. s = word.substr( 0, n );
  268. e = word.substr( 0, 6 - n );
  269. span1.innerHTML = s + "<span style='color: #000;'>" +
  270. e + "</span>";
  271. count += 1;
  272. }, 100 );
  273. };
  274. return {
  275. show: function(){
  276. if( !layer )
  277. create();
  278. else
  279. layer.style.display = "block";
  280. animate();
  281. },
  282. hide: function(){
  283. if( layer )
  284. layer.style.display = "none";
  285. clearInterval( animateTimer );
  286. },
  287. text: function( text ){
  288. var me, pm;
  289. if( layer )
  290. layer.innerHTML = text;
  291. me = this;
  292. pm = new Tracker.Promise();
  293. clearInterval( animateTimer );
  294. setTimeout( function(){
  295. me.hide();
  296. pm.resolve();
  297. }, 2e3 );
  298. return pm;
  299. },
  300. addCount: function(){
  301. count ++;
  302. span2.innerHTML = "(" + ( progress / count * 100 ).toFixed( 2 ) + "%)";
  303. },
  304. addProgress: function(){
  305. progress ++;
  306. span2.innerHTML = "(" + ( progress / count * 100 ).toFixed( 2 ) + "%)";
  307. }
  308. }
  309. }(),
  310. ControlFrame: function(){
  311. var document = window.document, controlWindow, hasCreateEmbeded = false,
  312. currentMode = "embed", pageBuilder, controllerBuilder;
  313. var config = {
  314. windowWidth: 800,
  315. windowHeight: 600
  316. };
  317. var lookupForWindowReady = function( target ){
  318. var pm, timer, timer2, now, timeout, clear;
  319. pm = new Tracker.Promise();
  320. timeout = 5000;
  321. now = Tracker.Util.time();
  322. clear = function(){
  323. clearInterval( timer );
  324. clearTimeout( timer2 );
  325. };
  326. timer = setInterval( function(){
  327. if( target.readyState == "complete" ){
  328. clear();
  329. pm.resolve();
  330. }
  331. }, 10 );
  332. timer2 = setTimeout( function(){
  333. pm.reject();
  334. }, timeout );
  335. return pm;
  336. };
  337. return Tracker.Event.bind( {
  338. state: "preshow",
  339. pageBuilder: function( fn ){
  340. pageBuilder = fn;
  341. },
  342. controllerBuilder: function( fn ){
  343. controllerBuilder = fn;
  344. },
  345. show: function(){
  346. var controller, page, window;
  347. if( currentMode === "embed" ){
  348. controller = document.getElementById( "tracker_controller_ct" ),
  349. page = document.getElementById( "tracker_page_ct" ),
  350. controller.style.display = "block",
  351. page.style.top = "";
  352. }else if( currentMode === "window" ){
  353. window = this.getWindow( "tracker_controller" );
  354. if( window && !window.closed )
  355. window.focus();
  356. else
  357. this.createWindow();
  358. }
  359. this.state = "show";
  360. this.fire( "show" );
  361. },
  362. hide: function(){
  363. var controller, page;
  364. if( currentMode === "embed" )
  365. controller = document.getElementById( "tracker_controller_ct" ),
  366. page = document.getElementById( "tracker_page_ct" ),
  367. controller.style.display = "none",
  368. page.style.top = "0";
  369. else if( currentMode === "window" )
  370. controlWindow.close();
  371. this.state = "hide";
  372. this.fire( "hide" );
  373. },
  374. toggleMode: function(){
  375. this.removeControllerFrame();
  376. if( currentMode === "embed" )
  377. currentMode = "window",
  378. this.createWindow();
  379. else if( currentMode === "window" )
  380. currentMode = "embed",
  381. this.createEmbed(),
  382. this.show();
  383. },
  384. getMode: function(){
  385. return currentMode;
  386. },
  387. getWindow: function( name ){
  388. // name: tracker_main | tracker_page | tracker_controller
  389. var w;
  390. if( !arguments.length || name === "tracker_main" )
  391. return window;
  392. if( currentMode === "window" && name === "tracker_controller" )
  393. return controlWindow;
  394. else if( w = window.frames[ name ] )
  395. return window.document.getElementById( name ).contentWindow;
  396. },
  397. // privates
  398. createEmbed: function(){
  399. var page, controller;
  400. Tracker.Promise.when(
  401. hasCreateEmbeded ? [ controllerBuilder( "embed" ) ] :
  402. [ pageBuilder(), controllerBuilder( "embed" ) ]
  403. ).then( Tracker.Util.bind( function( pageHtml, controllerHtml ){
  404. if( !controllerHtml )
  405. controllerHtml = pageHtml,
  406. pageHtml = null;
  407. if( pageHtml ){
  408. window.name = "tracker_main";
  409. this.write( "tracker_main", View.templates.frameset( {
  410. url: location.href,
  411. title: document.title,
  412. charset: document.characterSet || "utf-8"
  413. } ) );
  414. this.write( "tracker_page", pageHtml );
  415. var pageWin = this.getWindow( "tracker_page" );
  416. // 整个页面都加载完成了,可以开始script inject了
  417. this.fire('pageLoad',window,pageWin);
  418. }
  419. this.write( "tracker_controller", controllerHtml );
  420. controller = this.getWindow( "tracker_controller" );
  421. lookupForWindowReady( controller.document ).then( Tracker.Util.bind( function(){
  422. this.fire( "controllerLoad", controller, controller.document );
  423. }, this ) );
  424. }, this ) );
  425. hasCreateEmbeded = true;
  426. },
  427. createWindow: function( conf ){
  428. var width = screen.width - 200, height = screen.height - 200,
  429. left = 100, top = 100, controller;
  430. controlWindow = window.open( "about:blank", "", "width=" + width +
  431. ", height=" + height + ", left=" + left + ", top=" + top +
  432. ", toolbar=no, menubar=no, resizable=yes, status=no, " +
  433. "location=no, scrollbars=yes" );
  434. controllerBuilder( "window" ).then( Tracker.Util.bind( function( html ){
  435. this.write( "tracker_controller", html );
  436. controller = this.getWindow( "tracker_controller" );
  437. lookupForWindowReady( controller.document ).then( Tracker.Util.bind( function(){
  438. this.fire( "controllerLoad", controller, controller.document );
  439. }, this ) );
  440. }, this ) );
  441. },
  442. removeControllerFrame: function(){
  443. this.hide();
  444. if( currentMode === "embed" )
  445. this.write( "tracker_controller", "about:blank" );
  446. else if( currentMode === "window" )
  447. controlWindow = null;
  448. },
  449. write: function( name, content ){
  450. var document, i, l, t, timer, write;
  451. document = this.getWindow( name ).document;
  452. document.open( "text/html", "replace" );
  453. if( name == "tracker_page" ){
  454. i = 0;
  455. t = 10240; // 10k/ms
  456. l = content.length;
  457. write = function(){
  458. c = content.substr( i, t );
  459. document.write( c );
  460. i += t;
  461. if( i > l )
  462. document.close(),
  463. clearInterval( timer );
  464. };
  465. timer = setInterval( write, 1 );
  466. }else{
  467. document.write( content );
  468. document.close();
  469. }
  470. }
  471. } );
  472. }(),
  473. ControlPanel: function(){
  474. var actions, window, document, currentSelectedCode, updateInterval, codeEl,
  475. codeIndex;
  476. actions = {};
  477. codeIndex = 0;
  478. var rate = function( code ){
  479. var r, c;
  480. r = code.arriveRowsCount / code.rowsCount * 100 || 0;
  481. c = r == 0 ? "stress" : "";
  482. return "<span class='" + c + "'>" + r.toFixed( 1 ) + "%</span>";
  483. };
  484. var size = function( number ){
  485. return ( number / 1024 ).toFixed( 1 ) + "k";
  486. };
  487. var yesno = function( bool ){
  488. return ( bool && bool.length ) ?
  489. "<span class='stress'>&#26159;<span>" : "&#21542;";
  490. };
  491. var state = function( state ){
  492. switch( state ){
  493. case "normal":
  494. return "&#27491;&#24120;";
  495. case "timeout":
  496. return "<span class='stress'>&#36229;&#26102;</span>";
  497. case "empty":
  498. return "<span class='stress'>&#26080;&#20869;&#23481;</span>";
  499. }
  500. };
  501. var type = function( code ){
  502. switch( code.type ){
  503. case "embed":
  504. return "内嵌";
  505. case "link":
  506. return "文件链接";
  507. case "append":
  508. return "动态插入";
  509. };
  510. };
  511. var time = function( time, s ){
  512. if( time == -1 )
  513. return "-1";
  514. if( !s )
  515. return time + "ms";
  516. else
  517. return ( time / 1000 ).toFixed( 2 ) + "s";
  518. };
  519. var width = function(){
  520. var mapping, offsets;
  521. mapping = {
  522. index: 30, name: 220, type: 90, cover: 60, "cover-line": 60, lines: 60,
  523. size: 60, bsize: 60, rerror: 60, serror: 60, state: 50, loadConsum: 60,
  524. runConsum: 60
  525. };
  526. offsets = {
  527. };
  528. return function( name, type ){
  529. return mapping[ name ] + ( offsets[ type ] || 0 );
  530. };
  531. }();
  532. var withWidths = function( data ){
  533. var widths = {
  534. widthIndex: width( "index" ),
  535. widthName: width( "name" ),
  536. widthType: width( "type" ),
  537. widthCover: width( "cover" ),
  538. widthCoverLine: width( "cover-line" ),
  539. widthLines: width( "lines" ),
  540. widthSize: width( "size" ),
  541. widthBSize: width( "bsize" ),
  542. widthRError: width( "rerror" ),
  543. widthSError: width( "serror" ),
  544. widthState: width( "state" ),
  545. widthLoadConsum: width( "loadConsum" ),
  546. widthRunConsum: width( "runConsum" )
  547. };
  548. if( !data )
  549. return widths;
  550. for( var i in widths )
  551. data[ i ] = widths[ i ];
  552. return data;
  553. };
  554. var codeTemplate = function( code ){
  555. return View.templates.codeListLine( withWidths( {
  556. id: code.id,
  557. index: ++ codeIndex,
  558. fileName: code.fileName,
  559. type: type( code ),
  560. rate: rate( code ),
  561. arriveRowsCount: code.arriveRowsCount,
  562. rowsCount: code.rowsCount,
  563. size: size( code.size ),
  564. bsize: size( code.beautifySize ),
  565. rerror: yesno( code.runErrors ),
  566. serror: yesno( code.syntaxErrors ),
  567. state: state( code.state ),
  568. runConsum: time( code.runConsum ),
  569. loadConsum: time( code.loadConsum )
  570. } ) );
  571. };
  572. var codeListTemplate = function( codeList ){
  573. var htmls;
  574. htmls = [];
  575. if( codeList.length ){
  576. Tracker.Util.forEach( codeList, function( code, index ){
  577. htmls[ index ] = codeTemplate( code );
  578. } );
  579. return htmls.join( "" );
  580. }else{
  581. return "<tr><td colspan='20'><div class='none'>该网页没有任何JS代码</div></td></tr>";
  582. }
  583. };
  584. var makeCodeTr = function( code ){
  585. var layer, html;
  586. layer = document.createElement( "tbody" );
  587. html = codeTemplate( code );
  588. layer.innerHTML = html;
  589. return layer.firstChild;
  590. };
  591. var asnyShowCode = function(){
  592. var timer, timeout, interval, prepare, partCount, nowIndex, init,
  593. currentDisposeLines, gutterEl, linesEl, regx1, regx2, ckeyIdRegx, result,
  594. linesCount, h1, h2, focusOnFlag, focusOnFlagTarget;
  595. timeout = 1;
  596. partCount = 100;
  597. regx1 = /\x00/g;
  598. regx2 = /\x01/g;
  599. ckeyIdRegx = /id=ckey-(\d+)/g;
  600. h1 = [];
  601. h2 = [];
  602. init = function(){
  603. nowIndex = 0;
  604. linesCount = 0;
  605. window.clearInterval( timer );
  606. };
  607. prepare = function(){
  608. var innerElId = Tracker.Util.id();
  609. var gutterId = Tracker.Util.id();
  610. var linesId = Tracker.Util.id();
  611. codeEl.innerHTML = "<div id='" + innerElId + "' class='block clearfix' " +
  612. "style='height: " + ( linesCount * 20 + 10 ) + "px;'>" +
  613. "<div id='" + gutterId + "' class='gutter'></div>" +
  614. "<div id='" + linesId + "' class='lines'></div></div>";
  615. codeEl.scrollTop = 0;
  616. gutterEl = document.getElementById( gutterId );
  617. linesEl = document.getElementById( linesId );
  618. };
  619. interval = function(){
  620. var t, p1, p2, a;
  621. h1.length = h2.length = 0;
  622. for( var i = 0; i < partCount; i ++ ){
  623. if( nowIndex >= linesCount ){
  624. init();
  625. break;
  626. }
  627. t = Tracker.Util.html( currentDisposeLines[ nowIndex ] ).replace( regx1, "<" )
  628. .replace( regx2, ">" );
  629. t = t.replace( ckeyIdRegx, function( all, id ){
  630. a = Tracker.StatusPool.arrivedSnippetGet( id );
  631. if( a & 2 )
  632. a = 2;
  633. else if( a & 1 )
  634. a = 1;
  635. if( focusOnFlag && !focusOnFlagTarget && focusOnFlag == a ){
  636. focusOnFlagTarget = id;
  637. focusOnFlag = 0;
  638. }
  639. return a ? all + " class='arrive arrive-" + a + "'" : all;
  640. } );
  641. h1.push( Tracker.Util.tag( nowIndex + 1, "pre" ) );
  642. h2.push( Tracker.Util.tag( t || " ", "pre" ) );
  643. nowIndex ++;
  644. }
  645. p1 = document.createElement( "div" );
  646. p2 = document.createElement( "div" );
  647. p1.innerHTML = h1.join( "" );
  648. p2.innerHTML = h2.join( "" );
  649. gutterEl.appendChild( p1 );
  650. linesEl.appendChild( p2 );
  651. if( focusOnFlagTarget ){
  652. document.getElementById( "ckey-" + focusOnFlagTarget ).scrollIntoView();
  653. focusOnFlagTarget = undefined;
  654. }
  655. };
  656. result = function( code, _focusOnFlag ){
  657. init();
  658. focusOnFlag = _focusOnFlag;
  659. if( code.state == "empty" ){
  660. codeEl.innerHTML = "<div class='empty-code'>" +
  661. "&#20869;&#23481;&#20026;&#31354;</div>"; // 内容为空
  662. }else if( code.state == "timeout" ){
  663. codeEl.innerHTML = "<div class='timeout-code'>" +
  664. "&#35299;&#26512;&#36229;&#26102;</div>"; // 解析超时
  665. }else{
  666. currentDisposeLines = code.linesViewHtml;
  667. linesCount = currentDisposeLines.length;
  668. prepare();
  669. timer = window.setInterval( interval, timeout );
  670. }
  671. };
  672. result.clear = init;
  673. return result;
  674. }();
  675. var setupBootstrapPatch = function(){
  676. var setupDropdownMenu = function(){
  677. var lastOpen;
  678. var setup = function( el ){
  679. var dropdownMenu = el.querySelector( ".dropdown-menu" );
  680. Tracker.Event.add( dropdownMenu, "click", function( e ){
  681. Tracker.Util.removeClass( el, "open" );
  682. lastOpen = null;
  683. e.stopPropagation();
  684. Tracker.TrackerGlobalEvent.fire( "bootstrap: dropdown.close" );
  685. } );
  686. Tracker.Event.add( el, "click", function( e ){
  687. Tracker.Util.addClass( el, "open" );
  688. if( lastOpen && lastOpen != el )
  689. Tracker.Util.removeClass( lastOpen, "open" );
  690. lastOpen = el;
  691. Tracker.TrackerGlobalEvent.fire( "bootstrap: dropdown.open" );
  692. e.stopPropagation();
  693. } );
  694. };
  695. return function(){
  696. var dropdowns = document.querySelectorAll( ".dropdown" );
  697. for( var i = 0, l = dropdowns.length; i < l; i ++ )
  698. setup( dropdowns[ i ] );
  699. Tracker.Event.add( document, "click", function(){
  700. var found;
  701. for( var i = 0, l = dropdowns.length; i < l; i ++ )
  702. if( Tracker.Util.hasClass( dropdowns[ i ], "open" ) )
  703. found = true,
  704. Tracker.Util.removeClass( dropdowns[ i ], "open" );
  705. if( found )
  706. Tracker.TrackerGlobalEvent.fire( "bootstrap: dropdown.close" );
  707. } );
  708. }
  709. }();
  710. var setupModalDialog = function(){
  711. var open = function( modal ){
  712. return function(){
  713. modal.style.display = "block";
  714. Tracker.TrackerGlobalEvent.fire( "bootstrap: dialog.open" );
  715. }
  716. };
  717. var close = function( modal ){
  718. return function(){
  719. modal.style.display = "none";
  720. Tracker.TrackerGlobalEvent.fire( "bootstrap: dialog.close" );
  721. }
  722. };
  723. var setup = function( modal ){
  724. var closeBtns = modal.querySelectorAll( ".modal-header .close, .modal-footer .btn" );
  725. var fclose = close( modal );
  726. var fopen = open( modal );
  727. for( var i = 0, l = closeBtns.length; i < l; i ++ )
  728. Tracker.Event.add( closeBtns[ i ], "click", fclose );
  729. modal.open = fopen;
  730. };
  731. return function(){
  732. var modals = document.querySelectorAll( ".modal" );
  733. for( var i = 0, l = modals.length; i < l; i ++ )
  734. setup( modals[ i ] );
  735. }
  736. }();
  737. var setupTab = function(){
  738. var setup = function( tab ){
  739. var target = tab.getAttribute( "data-target" );
  740. if( !target )
  741. return ;
  742. target = document.getElementById( target );
  743. var heads = tab.childNodes;
  744. var bodys = target.childNodes;
  745. tab.active = function( index ){
  746. Tracker.Util.removeClass( heads[ tab.actived ], "active" );
  747. Tracker.Util.removeClass( bodys[ tab.actived ], "tab-content-active" );
  748. Tracker.Util.addClass( heads[ index ], "active" );
  749. Tracker.Util.addClass( bodys[ index ], "tab-content-active" );
  750. tab.actived = index;
  751. tab.tabEvent.fire( "active",
  752. index, heads[ index ].getAttribute( "data-name" ) );
  753. };
  754. Tracker.Event.add( tab, "click", function( e ){
  755. var li;
  756. li = Tracker.Util.findParent( e.target, "li", this );
  757. Tracker.Util.forEach( this.childNodes, function( l, index ){
  758. if( li === l ){
  759. if( tab.actived == index )
  760. return ;
  761. tab.active( index );
  762. }
  763. } );
  764. } );
  765. tab.tabEvent = Tracker.Event.bind();
  766. tab.actived = 0;
  767. };
  768. return function(){
  769. var tabs = document.querySelectorAll( ".nav" );
  770. for( var i = tabs.length - 1; i >= 0; i -- )
  771. setup( tabs[ i ] );
  772. };
  773. }();
  774. return function(){
  775. setupDropdownMenu();
  776. setupModalDialog();
  777. setupTab();
  778. };
  779. }();
  780. var autoUpdateCodeFn = function(){
  781. Tracker.CodeList.each( function( code ){
  782. if( !code.lastUpdate || code.lastUpdate < code.lastModified )
  783. View.ControlPanel.updateCode( code );
  784. } );
  785. };
  786. return Tracker.Event.bind( {
  787. bindWindow: function( win ){
  788. window = win;
  789. document = window.document;
  790. codeEl = document.getElementById( "code-content" );
  791. },
  792. addCode: function( code ){
  793. var tbody, tr, index;
  794. if( !document )
  795. return ;
  796. tbody = document.getElementById( "list-codes-tbody" );
  797. index = Tracker.CodeList.count() - 1;
  798. if( code instanceof Array ){
  799. tbody.innerHTML = codeListTemplate( code );
  800. }else if( code instanceof Tracker.Code ){
  801. code.onReady( function(){
  802. // code.index = index;
  803. tr = makeCodeTr( code );
  804. tbody.appendChild( tr );
  805. } );
  806. }
  807. },
  808. showCodeDetail: function( id, focusOnFlag ){
  809. var codeDetailHeadTab, actived;
  810. codeDetailHeadTab = document.getElementById( "code-detail-head" );
  811. actived = codeDetailHeadTab.actived;
  812. currentSelectedCode = id || null;
  813. document.getElementById( "code-detail" ).style.display =
  814. id ? "block" : "none";
  815. if( id ){
  816. if( actived === 0 )
  817. this.showCode( id, focusOnFlag );
  818. else if( actived == 1 )
  819. this.showCodeInfo( id, focusOnFlag );
  820. var elementListCodes = document.getElementById( "list-codes" );
  821. var trs = elementListCodes.querySelectorAll( "tr" );
  822. Tracker.Util.forEach( trs, function( tr ){
  823. if( tr.getAttribute( "data-code-id" ) == id )
  824. Tracker.Util.addClass( tr, "info" );
  825. else
  826. Tracker.Util.removeClass( tr, "info" );
  827. } );
  828. }
  829. },
  830. showCode: function( id, focusOnFlag ){
  831. if( id ){
  832. code = Tracker.CodeList.get( id );
  833. asnyShowCode( code, focusOnFlag );
  834. }
  835. },
  836. showCodeInfo: function( id, focusOnFlag ){
  837. var elementCodeInfo, code;
  838. elementCodeInfo = document.getElementById( "code-info" );
  839. code = Tracker.CodeList.get( id );
  840. elementCodeInfo.innerHTML = View.templates.controllerCodeInfo( {
  841. // id: code.id,
  842. // index: ++ codeIndex,
  843. fileName: code.fullUrl ?
  844. View.templates.url( { url: code.fullUrl } ) :
  845. "&#26469;&#33258;&#39029;&#38754;",
  846. type: type( code ),
  847. rate: rate( code ),
  848. arriveRowsCount: code.arriveRowsCount,
  849. rowsCount: code.rowsCount,
  850. size: size( code.size ),
  851. bsize: size( code.beautifySize ),
  852. rerror: yesno( code.runErrors ),
  853. serror: yesno( code.syntaxErrors ),
  854. state: state( code.state ),
  855. loadConsum: time( code.loadConsum ),
  856. runConsum: time( code.runConsum )
  857. } );
  858. },
  859. updateCode: function( code ){
  860. if( currentSelectedCode == code.id )
  861. this.showCodeDetail( code.id );
  862. var rateEl, arriveRowsCountEl, runErrorsEl, runConsumEl, loadConsumEl;
  863. rateEl = document.getElementById( "code-" + code.id + "-rate" );
  864. arriveRowsCountEl = document.getElementById( "code-" + code.id +
  865. "-arriveRowsCount" );
  866. runErrorsEl = document.getElementById( "code-" + code.id + "-runErrors" );
  867. runConsumEl = document.getElementById( "code-" + code.id + "-runConsum" );
  868. loadConsumEl = document.getElementById( "code-" + code.id + "-loadConsum" );
  869. if( !rateEl )
  870. return;
  871. rateEl.innerHTML = rate( code );
  872. arriveRowsCountEl.innerHTML = code.arriveRowsCount;
  873. runErrorsEl.innerHTML = yesno( code.runErrors );
  874. runConsumEl.innerHTML = time( code.runConsum );
  875. loadConsumEl.innerHTML = time( code.loadConsum );
  876. code.lastUpdate = Tracker.Util.time();
  877. },
  878. autoUpdateCodeStart: function(){
  879. updateInterval = setInterval( autoUpdateCodeFn, 5e2 );
  880. },
  881. autoUpdateCodeStop: function(){
  882. clearInterval( updateInterval );
  883. },
  884. activeTab: function( name ){
  885. var topNav, baseIndex;
  886. topNav = document.getElementById( "top-nav" );
  887. baseIndex = 0;
  888. if( View.ControlFrame.getMode() == "embed" )
  889. baseIndex = 1;
  890. if( typeof name != "undefined" ){
  891. if( !topNav.active )
  892. return ;
  893. if( name == "code-list" )
  894. name = baseIndex + 0;
  895. topNav.active( name );
  896. }else{
  897. return topNav.actived;
  898. }
  899. },
  900. setControlPower: function( bool, hiddenPage ){
  901. var parent, window, b, c1, c2;
  902. if( View.ControlFrame.getMode() == "embed" ){
  903. parent = View.ControlFrame.getWindow( "tracker_main" );
  904. window = View.ControlFrame.getWindow( "tracker_controller" );
  905. c1 = bool ? Tracker.Util.addClass : Tracker.Util.removeClass;
  906. c2 = hiddenPage ? Tracker.Util.addClass : Tracker.Util.removeClass;
  907. b = parent.document.body;
  908. c1( b, "control-power-mode" );
  909. c2( b, "hidden-page-mode" );
  910. }
  911. },
  912. actions: function( acts ){
  913. for( var name in acts )
  914. actions[ name ] = acts[ name ];
  915. },
  916. htmlBuilder: function(){
  917. var pm = new Tracker.Promise(), mode;
  918. codeIndex = 0;
  919. mode = View.ControlFrame.getMode();
  920. Tracker.Util.delay( function(){
  921. pm.resolve( View.templates.controllerPage( withWidths( {
  922. charset: global.document.characterSet || "utf-8",
  923. trackerCss: chrome.runtime.getURL('/static/css/js-tracker.css'),
  924. header: View.templates.controllerTopbar( {
  925. mode: mode
  926. } ),
  927. codeList: View.templates.controllerCodeList( withWidths() ),
  928. codeDetail: View.templates.controllerCodeDetail(),
  929. mode: mode,
  930. version: version,
  931. uid: host.tracker_uid
  932. } ) ) );
  933. } );
  934. return pm;
  935. },
  936. eventBuilder: function(){
  937. var me = this;
  938. var elementListCodes = document.getElementById( "list-codes" );
  939. var elementCodeDetail = document.getElementById( "code-detail" );
  940. var elementCodeToolbarInner = document.querySelector( ".code-toolbar-inner" );
  941. var elementCodeDetailHead = document.getElementById( "code-detail-head" );
  942. var elementCodeContent = document.getElementById( "code-content" );
  943. var tr, focusInList;
  944. var tabDescRegx = /tab-desc-(\d+)/;
  945. Tracker.Event.add( elementListCodes, {
  946. click: function( e ){
  947. var codeId;
  948. if( tr = Tracker.Util.findParent( e.target, "tr", elementListCodes ) )
  949. if( codeId = tr.getAttribute( "data-code-id" ) )
  950. focusInList = true,
  951. codeId == currentSelectedCode ||
  952. View.ControlPanel.showCodeDetail( codeId );
  953. }
  954. } );
  955. Tracker.Event.add( elementCodeDetail, {
  956. click: function(){
  957. focusInList = false;
  958. }
  959. } );
  960. Tracker.Event.add( document, {
  961. mouseup: function( e ){
  962. var action;
  963. if( ( action = e.target.getAttribute( "action" ) ) &&
  964. actions[ action ] )
  965. actions[ action ].call( me, e.target );
  966. },
  967. keydown: function( e ){
  968. var selectId;
  969. // command + R, F5
  970. if( ( e.metaKey && e.keyCode == 82 ) || e.keyCode == 116 ){
  971. if( View.ControlFrame.getMode() == "window" ){
  972. e.preventDefault && e.preventDefault();
  973. }
  974. }
  975. if( focusInList && currentSelectedCode ){
  976. var offset = 0;
  977. if( e.keyCode == 38 ){ // up
  978. offset = -1;
  979. }else if( e.keyCode == 40 ){ // down
  980. offset = 1;
  981. }
  982. if( offset ){
  983. var trs = elementListCodes.querySelectorAll( "tr" ),
  984. nowIndex = -1, tr;
  985. for(var i = 0, l = trs.length; i < l; i ++){
  986. if( trs[i].getAttribute( "data-code-id" ) ==
  987. currentSelectedCode ){
  988. nowIndex = i;
  989. break;
  990. }
  991. }
  992. if( nowIndex > -1 ){
  993. nowIndex = ( nowIndex += offset ) < 0 ?
  994. 0 : nowIndex == trs.length ?
  995. nowIndex - 1 : nowIndex;
  996. tr = trs[ nowIndex ];
  997. selectId = tr.getAttribute( "data-code-id" );
  998. if( currentSelectedCode != selectId )
  999. View.ControlPanel.showCodeDetail( selectId );
  1000. e.preventDefault && e.preventDefault();
  1001. }
  1002. }
  1003. }
  1004. }
  1005. // mousewheel: function( e ){
  1006. // e.preventDefault();
  1007. // }
  1008. } );
  1009. if( !actions[ "code#close" ] ){
  1010. actions[ "code#close" ] = function( e ){
  1011. focusInList = true;
  1012. View.ControlPanel.showCodeDetail( false );
  1013. };
  1014. }
  1015. if( View.ControlFrame.getMode() == "window" )
  1016. document.getElementById( "window-mode-trigger" ).innerHTML = "内嵌模式";
  1017. var lastScrollLeft = 0;
  1018. Tracker.Event.add( elementCodeContent, {
  1019. scroll: function( e ){
  1020. if( this.scrollLeft == 0 )
  1021. this.scrollLeft = 1;
  1022. if( this.scrollLeft == this.scrollWidth - this.clientWidth )
  1023. this.scrollLeft -= 1;
  1024. if( lastScrollLeft == this.scrollLeft )
  1025. return ;
  1026. var gutter = this.querySelector( ".gutter" );
  1027. gutter.style.left = this.scrollLeft + "px";
  1028. lastScrollLeft = this.scrollLeft;
  1029. }
  1030. } );
  1031. setupBootstrapPatch();
  1032. elementCodeDetailHead.tabEvent.on( "active", function( index ){
  1033. if( this.currentShown != currentSelectedCode ){
  1034. this.currentShown = currentSelectedCode;
  1035. if( index == 0 )
  1036. View.ControlPanel.showCode( currentSelectedCode );
  1037. else if( index == 1 )
  1038. View.ControlPanel.showCodeInfo( currentSelectedCode );
  1039. }
  1040. var tabDescs = elementCodeToolbarInner.querySelectorAll( ".tab-desc" );
  1041. Tracker.Util.forEach( tabDescs, function( tabDesc ){
  1042. tabDescRegx.test( tabDesc.className );
  1043. tabDesc.style.display = RegExp.$1 == index ? "" : "none";
  1044. } );
  1045. } );
  1046. if( currentSelectedCode )
  1047. this.showCodeDetail( currentSelectedCode );
  1048. }
  1049. } );
  1050. }()
  1051. }
  1052. }();
  1053. return View;
  1054. })(this);