index.js 71 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668
  1. var MagicEditor = {
  2. init : function(){
  3. this.config = {};
  4. var skin = this.getValue('skin');
  5. if(skin){
  6. $('body').addClass('skin-' + skin);
  7. }
  8. this.addedGroups = {};
  9. this.apiId = null;
  10. this.apiList = [];
  11. this.debugSessionId = null;
  12. this.defaultRequestValue = '{\r\n\t"request" : {\r\n\t\t"message" : "Hello MagicAPI!"\r\n\t},\r\n\t"path" : {\r\n\t\t"id" : "123456"\r\n\t},\r\n\t"header" : {\r\n\t\t"token" : "tokenValue"\r\n\t}\r\n}';
  13. this.initMTA();
  14. this.initShortKey();
  15. this.initSkin();
  16. this.initLeftToobarContainer();
  17. this.initBottomContainer();
  18. this.initSelect();
  19. this.initContextMenu();
  20. this.initScriptEditor();
  21. this.resetEditor();
  22. this.checkUpdate();
  23. this.backupInterval();
  24. this.login();
  25. var _this = this;
  26. $.getJSON('config.json',function(data){
  27. _this.config = data;
  28. Parser.importPackages = ['java.util.','java.lang.'].concat((_this.config.autoImportPackage||'').replace(/\\s/g,'').replace(/\*/g,'').split(','));
  29. })
  30. },
  31. initSkin : function(){
  32. var skinSelector = $('.skin-selector');
  33. $('.button-skin').on('click',function(){
  34. skinSelector.toggle();
  35. return false;
  36. });
  37. var $body = $('body');
  38. $body.on('click',function(){
  39. skinSelector.hide();
  40. })
  41. var _this = this;
  42. skinSelector.on('click','li',function(){
  43. skinSelector.hide();
  44. $(this).siblings().each(function(){
  45. $body.removeClass('skin-' + $(this).text())
  46. })
  47. _this.setSkin($(this).text());
  48. })
  49. },
  50. login : function(){
  51. $('.loading-wrapper').remove();
  52. var _this = this;
  53. this.ajax({
  54. url : 'login',
  55. async : false,
  56. success : function(successed){
  57. if(!successed){
  58. MagicEditor.createDialog({
  59. title : '登录',
  60. shade : true,
  61. content : '<label style="width:80px;text-align: right;display: inline-block">用户名:</label><input type="text" name="username" autocomplete="off"/><div style="height:2px;"></div><label style="width:80px;text-align: right;display: inline-block">密码:</label><input type="password" name="password" autocomplete="off"/>',
  62. replace : false,
  63. allowClose : false,
  64. autoClose : false,
  65. buttons : [{
  66. name : '登录',
  67. click : function($dom){
  68. var username = $dom.find('input[name=username]').val();
  69. var password = $dom.find('input[name=password]').val();
  70. var successed = false;
  71. _this.ajax({
  72. url : 'login',
  73. data : {
  74. username : username,
  75. password : password
  76. },
  77. async : false,
  78. success : function(succ){
  79. successed = succ;
  80. }
  81. })
  82. if(!successed){
  83. _this.alert('登录','登录失败,用户名或密码不正确');
  84. return false;
  85. }
  86. _this.loadAPI();
  87. }
  88. }]
  89. })
  90. }else{
  91. _this.loadAPI();
  92. }
  93. }
  94. })
  95. },
  96. resetEditor : function(){
  97. $('input[name=group]').val('未分组');
  98. $('input[name=method]').val('GET');
  99. $('input[name=name]').val('');
  100. $('input[name=path]').val('');
  101. $('input[name=prefix]').val('');
  102. this.outputJson = null;
  103. this.apiId = null;
  104. this.scriptEditor&&this.scriptEditor.setValue('return message;');
  105. this.requestEditor && this.requestEditor.setValue(this.defaultRequestValue);
  106. this.resultEditor&&this.resultEditor.setValue('');
  107. this.optionsEditor && this.optionsEditor.setValue('{\r\n}');
  108. },
  109. addBreakPoint : function(line){
  110. var model = this.scriptEditor.getModel();
  111. model.deltaDecorations([],[{
  112. range : new monaco.Range(line, 1, line, 1),
  113. options: {
  114. isWholeLine: true,
  115. linesDecorationsClassName: 'breakpoints',
  116. className : 'breakpoint-line',
  117. }
  118. }])
  119. },
  120. removeBreakPoint : function(line){
  121. var model = this.scriptEditor.getModel();
  122. var decorations = [];
  123. if (line !== undefined) {
  124. decorations = model.getLineDecorations(line);
  125. } else {
  126. decorations = model.getAllDecorations();
  127. }
  128. var ids = [];
  129. for (var i=0,len =decorations.length;i<len;i++) {
  130. if (decorations[i].options.linesDecorationsClassName === 'breakpoints') {
  131. ids.push(decorations[i].id)
  132. }
  133. }
  134. model.deltaDecorations(ids, [])
  135. },
  136. hasBreakPoint : function(line){
  137. var decorations = this.scriptEditor.getLineDecorations(line);
  138. for (var i=0,len =decorations.length;i<len;i++) {
  139. if (decorations[i].options.linesDecorationsClassName === 'breakpoints') {
  140. return true;
  141. }
  142. }
  143. },
  144. renderApiList : function(){
  145. var empty = true;
  146. var root = [];
  147. var groups = {};
  148. var apiList = this.apiList;
  149. if(apiList&&apiList.length > 0){
  150. var $groupUL = $('input[name=group]').next();
  151. for(var i=0,len = apiList.length;i<len;i++){
  152. var info = apiList[i];
  153. info.groupName = info.groupName || '未分组';
  154. if(!groups[info.groupName]){
  155. groups[info.groupName] = {
  156. id : info.groupName,
  157. children : [],
  158. spread : true,
  159. groupPrefix : info.groupPrefix,
  160. title : info.groupName
  161. }
  162. if($groupUL.find('[data-name='+$.escapeSelector(info.groupName)+']').length == 0){
  163. $groupUL.append($('<li data-name="'+info.groupName+'" data-prefix="'+(info.groupPrefix || '')+'"/>').append(info.groupName))
  164. }
  165. }
  166. if(info.show!==false){
  167. groups[info.groupName].children.push({
  168. id : info.id,
  169. groupName : info.groupName,
  170. groupPrefix : info.groupPrefix,
  171. name : info.name,
  172. title : '<label style="padding-right: 4px;color:#000">' + info.name + "</label>" + info.path,
  173. path : info.path
  174. });
  175. }
  176. }
  177. }
  178. for(var key in this.addedGroups){
  179. if(!groups[key]){
  180. groups[key] = this.addedGroups[key];
  181. }
  182. }
  183. var $dom = $('.api-list-container').html('');
  184. for(var key in groups){
  185. var group = groups[key];
  186. var $item = $('<div/>').addClass('group-item')
  187. .addClass('opened')
  188. .append($('<div/>').addClass('group-header')
  189. .append('<i class="iconfont icon-arrow-bottom"></i><i class="iconfont icon-list"></i>')
  190. .append($('<label/>').append(key))
  191. .append(group.groupPrefix ? '<span>('+group.groupPrefix+')</span>': '')
  192. );
  193. if(group.children){
  194. var $ul = $('<ul/>').addClass('group-list');
  195. for(var i =0,len = group.children.length;i<len;i++){
  196. var info = group.children[i];
  197. $ul.append($('<li/>').attr('data-id',info.id).append('<i class="iconfont icon-script"></i>')
  198. .append('<label>'+info.name+'</label>')
  199. .append('<span>('+info.path+')</span>'));
  200. }
  201. $item.append($ul);
  202. }
  203. $dom.append($item);
  204. }
  205. },
  206. loadAPI : function(id,isCopy){
  207. var _this = this;
  208. if(id){
  209. this.ajax({
  210. url : 'get',
  211. data : {
  212. id : id
  213. },
  214. success : function(info){
  215. _this.resetEditor();
  216. $('.button-delete').removeClass('disabled');
  217. if(isCopy === true){
  218. $('input[name=name]').val();
  219. $('input[name=path]').val();
  220. MagicEditor.setStatusBar('复制接口:' + info.name + '(' + info.path + ')')
  221. }else{
  222. _this.apiId = id;
  223. $('input[name=name]').val(info.name);
  224. $('input[name=path]').val(info.path);
  225. MagicEditor.setStatusBar('编辑接口:' + info.name + '(' + info.path + ')')
  226. }
  227. $('input[name=method]').val(info.method);
  228. $('input[name=group]').val(info.groupName || '未分组');
  229. $('input[name=prefix]').val(info.groupPrefix || '');
  230. $('.button-run,.button-delete').removeClass('disabled');
  231. _this.scriptEditor && _this.scriptEditor.setValue(info.script);
  232. _this.requestEditor && _this.requestEditor.setValue(info.parameter || _this.defaultRequestValue);
  233. _this.optionsEditor && _this.optionsEditor.setValue(info.option || '{\r\n}');
  234. }
  235. })
  236. }else{
  237. this.ajax({
  238. url : 'list',
  239. success : function(list){
  240. _this.apiList = list;
  241. _this.renderApiList();
  242. }
  243. })
  244. }
  245. },
  246. createNew : function($header){
  247. MagicEditor.createDialog({
  248. title : '新建接口',
  249. content : '新建接口会清空当前编辑器,是否继续?',
  250. buttons : [{
  251. name : '继续',
  252. click : function(){
  253. $('.group-item .group-list li.selected').removeClass('selected');
  254. MagicEditor.resetEditor();
  255. $('.button-delete').addClass('disabled');
  256. if($header){
  257. $('input[name=group]').val($header.find('label').text());
  258. var prefix = $header.find('span').text();
  259. if(prefix){
  260. $('input[name=prefix]').val(prefix.substring(1,prefix.length - 1));
  261. }
  262. }
  263. MagicEditor.report('create_api');
  264. MagicEditor.setStatusBar('创建接口');
  265. }
  266. },{
  267. name : '取消'
  268. }]
  269. })
  270. },
  271. setStatusBar : function(value){
  272. $('.footer-container').html(value);
  273. },
  274. initMTA : function(){
  275. window._mtac = {};
  276. var element = document.createElement("script");
  277. element.src = "//pingjs.qq.com/h5/stats.js?v2.0.4";
  278. element.setAttribute("name", "MTAH5");
  279. element.setAttribute("sid", "500724136");
  280. element.setAttribute("cid", "500724141");
  281. var s = document.getElementsByTagName("script")[0];
  282. s.parentNode.insertBefore(element, s);
  283. var _this = this;
  284. element.onload = element.onreadystatechange = function(){
  285. if(!this.readyState||this.readyState=='loaded'||this.readyState=='complete') {
  286. _this.report('v0_4_7');
  287. }
  288. }
  289. },
  290. // 修改分组
  291. updateGroup : function($header){
  292. var _this = MagicEditor;
  293. var oldGroupName = $header.find('label').text();
  294. var oldPrefix = $header.find('span').text();
  295. oldPrefix = oldPrefix ? oldPrefix.substring(1,oldPrefix.length - 1) : '';
  296. _this.createDialog({
  297. title : '修改分组:' + oldGroupName,
  298. content : '<label>分组名称:</label><input type="text" name="name" value="'+oldGroupName+'" autocomplete="off"/><div style="height:2px;"></div><label>分组前缀:</label><input type="text" value="'+oldPrefix+'" name="prefix" autocomplete="off"/>',
  299. replace : false,
  300. buttons : [{
  301. name : '修改',
  302. click : function($dom){
  303. var groupName = $dom.find('input[name=name]').val();
  304. var groupPrefix = $dom.find('input[name=prefix]').val();
  305. if(!groupName){
  306. $dom.find('input[name=path]').focus();
  307. return false;
  308. }
  309. var exists = false;
  310. $('.group-header').each(function(){
  311. if(this !== $header[0]){
  312. var name = $(this).find('label').text();
  313. if(name == groupName){
  314. exists = true;
  315. return false;
  316. }
  317. }
  318. });
  319. if(groupName.indexOf("'")!= -1 || groupName.indexOf('"') != -1){
  320. _this.alert('创建分组','分组名不能包含特殊字符 \' "');
  321. return false;
  322. }
  323. if(groupPrefix.indexOf("'")!= -1 || groupPrefix.indexOf('"') != -1){
  324. _this.alert('创建分组','分组前缀不能包含特殊字符 \' "');
  325. return false;
  326. }
  327. if(exists){
  328. _this.alert('创建分组','分组已存在!');
  329. return false;
  330. }
  331. _this.report('group_update');
  332. _this.ajax({
  333. url : 'group/update',
  334. data : {
  335. oldGroupName : oldGroupName,
  336. groupName : groupName,
  337. prefix : groupPrefix
  338. },
  339. success : function(){
  340. if(_this.addedGroups[oldGroupName]){
  341. delete _this.addedGroups[oldGroupName]
  342. }
  343. _this.addedGroups[groupName] = {
  344. groupName : groupName,
  345. groupPrefix : groupPrefix
  346. }
  347. var apiList = _this.apiList;
  348. if(apiList&&apiList.length > 0){
  349. for(var i=0,len = apiList.length;i<len;i++){
  350. if(apiList[i].groupName == oldGroupName){
  351. apiList[i].groupName = groupName;
  352. apiList[i].groupPrefix = groupPrefix || '';
  353. }
  354. }
  355. }
  356. var $group = $('input[name=group]');
  357. $group.next().find('li[data-name='+$.escapeSelector(oldGroupName)+']').attr('data-prefix',(groupPrefix || '')).attr('data-name',groupName).html(groupName);
  358. if($group.val() == oldGroupName){
  359. $group.val(groupName);
  360. $('input[name=prefix]').val(groupPrefix);
  361. }
  362. $header.find('label').html(groupName).next().html(groupPrefix ? '('+groupPrefix+')' : '');
  363. _this.renderApiList();
  364. }
  365. })
  366. }
  367. },{
  368. name : '取消'
  369. }]
  370. })
  371. },
  372. // 创建分组
  373. createGroup : function(){
  374. var _this = MagicEditor;
  375. _this.setStatusBar('创建分组..');
  376. MagicEditor.createDialog({
  377. title : '创建分组',
  378. content : '<label>分组名称:</label><input type="text" name="name" autocomplete="off"/><div style="height:2px;"></div><label>分组前缀:</label><input type="text" name="prefix" autocomplete="off"/>',
  379. replace : false,
  380. buttons : [{
  381. name : '创建',
  382. click : function($dom){
  383. var groupName = $dom.find('input[name=name]').val();
  384. var groupPrefix = $dom.find('input[name=prefix]').val();
  385. if(!groupName){
  386. $dom.find('input[name=path]').focus();
  387. return false;
  388. }
  389. if(groupName.indexOf("'")!= -1 || groupName.indexOf('"') != -1){
  390. _this.alert('创建分组','分组名不能包含特殊字符 \' "');
  391. return false;
  392. }
  393. if(groupPrefix.indexOf("'")!= -1 || groupPrefix.indexOf('"') != -1){
  394. _this.alert('创建分组','分组前缀不能包含特殊字符 \' "');
  395. return false;
  396. }
  397. var exists = false;
  398. $('.group-header').each(function(){
  399. var name = $(this).find('label').text();
  400. if(name == groupName){
  401. exists = true;
  402. return false;
  403. }
  404. });
  405. if(exists){
  406. _this.setStatusBar('分组「'+groupName + '」');
  407. _this.alert('创建分组','分组已存在!');
  408. return false;
  409. }
  410. _this.addedGroups[groupName] = {
  411. groupName : groupName,
  412. groupPrefix : groupPrefix
  413. }
  414. _this.report('group_create');
  415. _this.setStatusBar('分组「'+groupName + '」创建成功');
  416. $('input[name=group]').next().append($('<li data-name="'+groupName+'" data-prefix="'+(groupPrefix || '')+'"/>').append(groupName));
  417. _this.renderApiList();
  418. }
  419. },{
  420. name : '取消'
  421. }]
  422. })
  423. },
  424. // 删除分组
  425. deleteGroup : function($header){
  426. var _this = MagicEditor;
  427. var groupName = $header.find('label').text();
  428. _this.setStatusBar('准备删除分组「'+groupName + '」');
  429. _this.createDialog({
  430. title : '删除接口分组',
  431. content : '是否要删除接口分组「'+groupName + '」',
  432. buttons : [{
  433. name : '删除',
  434. click : function(){
  435. _this.report('group_delete');
  436. var ids = [];
  437. $header.next().find('li').each(function(){
  438. ids.push($(this).data('id'));
  439. });
  440. _this.setStatusBar('准备删除接口分组「'+groupName + '」');
  441. delete _this.addedGroups[groupName];
  442. _this.ajax({
  443. url : 'group/delete',
  444. data : {
  445. apiIds : ids.join(','),
  446. groupName : groupName
  447. },
  448. success : function(){
  449. _this.setStatusBar('接口分组「'+groupName + '」已删除');
  450. _this.loadAPI(); //重新加载
  451. }
  452. })
  453. }
  454. },{
  455. name : '取消'
  456. }]
  457. })
  458. },
  459. report : function(eventId){
  460. try{
  461. MtaH5.clickStat(eventId);
  462. }catch(ignored){}
  463. },
  464. deleteApi : function($li){
  465. var text = $li.text();
  466. MagicEditor.createDialog({
  467. title : '删除接口',
  468. content : '是否要删除接口「'+text + '」',
  469. buttons : [{
  470. name : '删除',
  471. click : function(){
  472. MagicEditor.setStatusBar('准备删除接口');
  473. MagicEditor.report('script_delete')
  474. var apiId = $li.data('id');
  475. MagicEditor.ajax({
  476. url : 'delete',
  477. data : {
  478. id : apiId
  479. },
  480. success : function(){
  481. if(MagicEditor.apiId == apiId){
  482. MagicEditor.apiId = null;
  483. }
  484. MagicEditor.setStatusBar('接口「'+text + '」已删除');
  485. MagicEditor.loadAPI(); //重新加载
  486. }
  487. })
  488. }
  489. },{
  490. name : '取消'
  491. }]
  492. })
  493. },
  494. ajax : function(options){
  495. $.ajax({
  496. url : options.url,
  497. headers : options.headers,
  498. async : options.async,
  499. type : options.type || 'post',
  500. dataType : 'json',
  501. contentType : options.contentType,
  502. data : options.data,
  503. success : options.successd || function(json,data,xhr){
  504. if(json.code == 1){
  505. options&&options.success(json.data,json,xhr);
  506. }else{
  507. var val = options.exception&&options.exception(json.code,json.message,json);
  508. if(val !== false){
  509. MagicEditor.alert('Error',json.message);
  510. }
  511. }
  512. },
  513. error : function(){
  514. MagicEditor.setStatusBar('ajax请求失败');
  515. MagicEditor.alert('网络错误','ajax请求失败');
  516. options.error&&options.error();
  517. }
  518. })
  519. },
  520. copyApi : function($li){
  521. var id = $li&&$li.data('id');
  522. id&&MagicEditor.confirm('复制接口','复制接口会清空当前编辑器,是否继续?',function(){
  523. MagicEditor.loadAPI(id,true);
  524. })
  525. },
  526. copyApiPath : function($li){
  527. var _this = MagicEditor;
  528. var path = $li&&$li.find('span').text();
  529. if(_this.config.web&&path){
  530. path = path.substring(1,path.length - 1);
  531. var prefix = $li.parent().prev().find('span').text() || '';
  532. if(prefix){
  533. prefix = prefix.substring(1,prefix.length - 1).replace(/(^\/+)|(\/+$)/g,'');
  534. }
  535. path = prefix + '/' + path.replace(/(^\/+)/g,'');
  536. if(_this.config&&_this.config.prefix){
  537. path = _this.config.prefix.replace(/(^\/+)|(\/+$)/g,'') + '/'+ path;
  538. }
  539. var host = location.href.substring(0,location.href.indexOf(_this.config.web)).replace(/(\/+$)/g,'');
  540. if(_this.config.prefix){
  541. host = host + '/' + _this.config.prefix.replace(/(^\/+)|(\/+$)/g,'');
  542. }
  543. path = host + '/' + path;
  544. try {
  545. var copyText = document.createElement('textarea');
  546. copyText.style = 'position:absolute;left:-99999999px';
  547. document.body.appendChild(copyText);
  548. copyText.innerHTML = path;
  549. copyText.readOnly = false;
  550. copyText.select();
  551. copyText.setSelectionRange(0, copyText.value.length);
  552. document.execCommand("copy");
  553. _this.alert('复制接口路径','复制成功');
  554. } catch (e) {
  555. _this.alert('复制接口路径失败,请手动赋值',path);
  556. }
  557. }
  558. },
  559. resetDebugContent : function(){
  560. $('.bottom-item-body table tbody').html('<tr><td colspan="3" align="center">no message.</td></tr>');
  561. },
  562. doContinue : function(step){
  563. if($('.button-continue').hasClass('disabled')){
  564. return;
  565. }
  566. if(this.debugSessionId){
  567. MagicEditor.resetDebugContent();
  568. $('.button-continue,.button-step-over').addClass('disabled');
  569. var _this = this;
  570. var headers = _this.requestHeaders || {};
  571. headers['Magic-Request-Session'] = this.debugSessionId;
  572. headers['Magic-Request-Continue'] = true;
  573. headers['Magic-Request-Breakpoints'] = this.getBreakPoints().join(',');
  574. headers['Magic-Request-Step-Into'] = step ? '1' : '0';
  575. this.ajax({
  576. url : _this.requestURL,
  577. type : _this.requestMethod,
  578. headers : headers,
  579. successd : function(json,status,xhr){
  580. _this.convertResult(json,xhr);
  581. },
  582. error : function(){
  583. $('.button-run').removeClass('disabled');
  584. }
  585. })
  586. }
  587. },
  588. paddingZero : function(val){
  589. if(val < 10){
  590. return '0' + val;
  591. }
  592. return val.toString();
  593. },
  594. getTimeStr : function(date){
  595. var month = date.getMonth() + 1;
  596. var day = date.getDate();
  597. var hour = date.getHours();
  598. var minute = date.getMinutes();
  599. var seconds = date.getSeconds();
  600. return date.getFullYear() + '-' + this.paddingZero(month) + '-' + this.paddingZero(day) + ' ' + this.paddingZero(hour) + ':' + this.paddingZero(minute) + ':'+this.paddingZero(seconds);
  601. },
  602. appendLog : function(level,message,throwable){
  603. var $div = $('<div class="output-log-line level-'+level+'"/>')
  604. $div.append($('<div class="timestamp"/>').append(this.getTimeStr(new Date())));
  605. $div.append($('<div class="level"/>').append(level.toUpperCase()));
  606. var messages = message.replace(/ /g,'&nbsp;').replace(/\t/g,'&nbsp;&nbsp;&nbsp;&nbsp;').split('\n');
  607. $div.append($('<div class="message"/>').append(messages[0]));
  608. if(messages.length > 1){
  609. for(var i=1;i<messages.length;i++){
  610. $div.append($('<div class="message-line level-'+level+'" />').append(messages[i]));
  611. }
  612. }
  613. if(throwable){
  614. messages = throwable.replace(/ /g,'&nbsp;').replace(/\t/g,'&nbsp;&nbsp;&nbsp;&nbsp;').split('\n');
  615. for(var i=0;i<messages.length;i++){
  616. $div.append($('<div class="message-line level-'+level+'" />').append(messages[i]));
  617. }
  618. }
  619. if(!this.$output){
  620. this.$output = $('.bottom-container .bottom-item-body.output');
  621. }
  622. this.$output.append($div);
  623. this.$output.scrollTop(this.$output[0].scrollHeight);
  624. },
  625. createConsole : function(callback){
  626. var _this = this;
  627. var source = new EventSourcePolyfill('console',{
  628. headers : _this.requestHeaders
  629. });
  630. source.onerror = function(){
  631. source.close();
  632. }
  633. source.addEventListener('create',function(e){
  634. _this.navigateTo(4);
  635. callback&&callback(e.data);
  636. })
  637. source.addEventListener('close',function(e){
  638. source.close();
  639. })
  640. source.addEventListener('log',function(e){
  641. var data = JSON.parse(e.data);
  642. _this.appendLog(data.level,data.message,data.throwable);
  643. })
  644. },
  645. getBreakPoints : function(){
  646. var decorations = MagicEditor.scriptEditor.getModel().getAllDecorations();
  647. var breakpoints = [];
  648. for (var i=0,len =decorations.length;i<len;i++) {
  649. if (decorations[i].options.linesDecorationsClassName === 'breakpoints') {
  650. breakpoints.push(decorations[i].range.startLineNumber);
  651. }
  652. }
  653. return breakpoints;
  654. },
  655. doTest : function(){
  656. var _this = this;
  657. if($('.button-run').hasClass('disabled')){
  658. return;
  659. }
  660. var prefix = $("input[name=prefix]").val();
  661. var path = $("input[name=path]").val();
  662. var host = location.href.substring(0,location.href.indexOf(_this.config.web)).replace(/(\/+$)/g,'');
  663. if(_this.config.prefix){
  664. host = host + '/' + _this.config.prefix.replace(/(^\/+)|(\/+$)/g,'');
  665. }
  666. var url = host + ('/' +prefix + '/' + path).replace(/\/+/g,'/');
  667. var request = _this.requestEditor.getValue();
  668. try{
  669. request = JSON.parse(request);
  670. _this.requestHeaders = request.header || {};
  671. delete request.header;
  672. if(typeof request != 'object'){
  673. _this.setStatusBar('请求参数有误!');
  674. _this.alert('运行测试','请求参数有误!');
  675. return;
  676. }
  677. }catch(e){
  678. _this.setStatusBar('请求参数有误!');
  679. _this.alert('运行测试','请求参数有误!');
  680. return;
  681. }
  682. if(request.path){
  683. for(var key in request.path){
  684. url = url.replace('{' + key + '}',request.path[key]);
  685. }
  686. }
  687. _this.setStatusBar('开始测试...');
  688. _this.createConsole(function(sessionId){
  689. _this.report('run');
  690. request.script = _this.scriptEditor.getValue();
  691. var breakpoints = _this.getBreakPoints();
  692. _this.requestHeaders['Magic-Request-Session'] = sessionId;
  693. _this.requestHeaders['Magic-Request-Breakpoints'] = breakpoints.join(',');
  694. _this.resetDebugContent();
  695. $('.button-run').addClass('disabled');
  696. $('.button-continue,.button-step-over').addClass('disabled');
  697. _this.requestMethod = $('input[name=method]').val();
  698. var isRequestBody = _this.requestMethod != 'GET' && request&&request.body&&(Array.isArray(request.body) || Object.getOwnPropertyNames(request.body).length >0);
  699. var requestData;
  700. if(isRequestBody){
  701. if(request.request){
  702. var params = [];
  703. for(var key in request.request){
  704. params.push(key + '=' + request.request[key]);
  705. }
  706. if(params.length > 0){
  707. url = url + "?" + params.join("&");
  708. }
  709. }
  710. }else{
  711. requestData = request.request;
  712. }
  713. _this.requestURL = url;
  714. var contentType = isRequestBody ? 'application/json;charset=utf-8' : undefined;
  715. _this.ajax({
  716. url : _this.requestURL,
  717. type : _this.requestMethod,
  718. headers : _this.requestHeaders,
  719. data : isRequestBody ? JSON.stringify(request.body) : requestData,
  720. contentType : contentType,
  721. successd : function(json,status,xhr){
  722. _this.convertResult(json,xhr);
  723. },
  724. error : function(){
  725. $('.button-run').removeClass('disabled');
  726. }
  727. })
  728. });
  729. },
  730. doSave : function(){
  731. if($('.button-save').hasClass('disabled')){
  732. return;
  733. }
  734. $('.button-save').addClass('disabled');
  735. var name = $('input[name=name]').val();
  736. var path = $('input[name=path]').val();
  737. var method = $('input[name=method]').val();
  738. var groupName = $('input[name=group]').val();
  739. var groupPrefix = $('input[name=prefix]').val();
  740. this.setStatusBar('准备保存接口:' + name + "(" + path + ")");
  741. var _this = this;
  742. this.ajax({
  743. url : 'save',
  744. data : {
  745. script : this.scriptEditor.getValue(),
  746. path : path,
  747. method : method,
  748. id : this.apiId,
  749. groupName : groupName,
  750. groupPrefix : groupPrefix,
  751. parameter: this.requestEditor.getValue(),
  752. option: this.optionsEditor.getValue(),
  753. name : name,
  754. output : this.outputJson
  755. },
  756. async : false,
  757. exception : function(){
  758. $('.button-save').removeClass('disabled');
  759. },
  760. error : function(){
  761. $('.button-save').removeClass('disabled');
  762. },
  763. success : function(id){
  764. $('.button-save,.button-delete').removeClass('disabled');
  765. if(_this.apiId){
  766. _this.report('script_save');
  767. for(var i=0,len = _this.apiList.length;i<len;i++){
  768. if(_this.apiList[i].id == _this.apiId){
  769. _this.apiList[i].name = name;
  770. _this.apiList[i].path = path;
  771. _this.apiList[i].method = method;
  772. _this.apiList[i].groupName = groupName;
  773. break;
  774. }
  775. }
  776. }else{
  777. _this.report('script_add');
  778. _this.apiId = id;
  779. _this.apiList.unshift({
  780. id : id,
  781. name : name,
  782. path : path,
  783. method : method,
  784. groupName : groupName || '未分组',
  785. })
  786. }
  787. _this.setStatusBar('保存成功!');
  788. _this.loadAPI();
  789. }
  790. })
  791. },
  792. convertResult : function(json,xhr){
  793. var outputJson;
  794. if(xhr && 'true' == xhr.getResponseHeader('Response-With-Magic-API')){
  795. var code = json.code;
  796. var message = json.message;
  797. this.debugSessionId = null;
  798. this.resetDebugContent();
  799. this.debugDecorations&&this.scriptEditor&&this.scriptEditor.deltaDecorations(this.debugDecorations,[]);
  800. this.debugDecorations = null;
  801. var _this = this;
  802. var ret = undefined;
  803. if(code === -1000){
  804. MagicEditor.setStatusBar('脚本执行出错..');
  805. MagicEditor.report('script_error');
  806. $(".button-run").removeClass('disabled');
  807. $('.button-continue,.button-step-over').addClass('disabled');
  808. this.navigateTo(2);
  809. if (json.body) {
  810. var line = json.body;
  811. var range = new monaco.Range(line[0], line[2], line[1], line[3] + 1);
  812. var decorations = this.scriptEditor&&this.scriptEditor.deltaDecorations([],[{
  813. range: range,
  814. options : {
  815. hoverMessage : {
  816. value : message
  817. },
  818. inlineClassName : 'squiggly-error',
  819. }
  820. }])
  821. this.scriptEditor.revealRangeInCenter(range);
  822. this.scriptEditor.focus();
  823. setTimeout(function(){
  824. _this.scriptEditor&&_this.scriptEditor.deltaDecorations(decorations,[])
  825. },10000)
  826. }
  827. ret = false;
  828. }else if(code === 1000){ // debug断点
  829. $(".button-run").addClass('disabled');
  830. $('.button-continue,.button-step-over').removeClass('disabled');
  831. this.navigateTo(3);
  832. this.debugIn(message, json.body);
  833. return false;
  834. }
  835. MagicEditor.setStatusBar('脚本执行完毕');
  836. $(".button-run").removeClass('disabled');
  837. $('.button-continue,.button-step-over').addClass('disabled');
  838. this.navigateTo(2)
  839. var contentType = xhr&&xhr.getResponseHeader('ma-content-type');
  840. if(contentType == 'application/octet-stream'){ //文件下载
  841. var disposition = xhr.getResponseHeader('ma-content-disposition');
  842. var filename = 'output';
  843. if(disposition){
  844. filename = decodeURIComponent(disposition.substring(disposition.indexOf('filename=') + 9));
  845. }
  846. outputJson = this.formatJson({
  847. filename : filename
  848. });
  849. var a = document.createElement("a");
  850. a.download = filename;
  851. var bstr = atob(json.data);
  852. var n = bstr.length;
  853. var u8arr = new Uint8Array(n);
  854. while (n--) {
  855. u8arr[n] = bstr.charCodeAt(n);
  856. }
  857. a.href = window.URL.createObjectURL(new Blob([u8arr]));
  858. a.click();
  859. MagicEditor.report('output_blob');
  860. }else if(contentType && contentType.indexOf('image') == 0){ //image开头
  861. outputJson = this.formatJson(json.data);
  862. this.createDialog({
  863. title : '图片结果',
  864. content : '<p align="center"><img src="data:'+contentType+';base64,'+json.data+'"></p>',
  865. replace : false,
  866. buttons : [{name : 'OK'}]
  867. })
  868. MagicEditor.report('output_image');
  869. }else{
  870. outputJson = this.formatJson(json.data);
  871. }
  872. }else{
  873. this.navigateTo(2);
  874. outputJson = this.formatJson(json);
  875. }
  876. this.outputJson = outputJson;
  877. this.resultEditor.setValue(outputJson);
  878. return ret;
  879. },
  880. debugIn : function(id,data){
  881. MagicEditor.setStatusBar('进入断点...');
  882. MagicEditor.report('debug_in');
  883. this.debugSessionId = id;
  884. if(data.variables.length > 0){
  885. var $tbody = $('.bottom-item-body table tbody').html('');
  886. for(var i =0,len = data.variables.length;i<len;i++){
  887. var item = data.variables[i];
  888. var $tr = $('<tr/>');
  889. $tr.append($('<td/>').html(item.name))
  890. $tr.append($('<td/>').html(item.value))
  891. $tr.append($('<td/>').html(item.type))
  892. $tbody.append($tr);
  893. }
  894. }else{
  895. this.resetDebugContent();
  896. }
  897. this.debugDecorations = [this.scriptEditor&&this.scriptEditor.deltaDecorations([],[{
  898. range : new monaco.Range(data.range[0],1,data.range[0],1),
  899. options: {
  900. isWholeLine: true,
  901. inlineClassName : 'debug-line',
  902. className : 'debug-line',
  903. }
  904. }])];
  905. },
  906. backupInterval : function(){
  907. var _this = this;
  908. var info = _this.getValue('api_info');
  909. if(info){
  910. info = JSON.parse(info);
  911. _this.apiId = info.id;
  912. $('input[name=name]').val(info.name || '');
  913. $('input[name=path]').val(info.path || '');
  914. $('input[name=method]').val(info.method || '');
  915. $('input[name=group]').val(info.groupName || '');
  916. $('input[name=prefix]').val(info.groupPrefix || '');
  917. _this.scriptEditor&&_this.scriptEditor.setValue(info.script || 'return message;');
  918. _this.requestEditor&&_this.requestEditor.setValue(info.parameters || _this.defaultRequestValue);
  919. _this.options&&_this.options.setValue(info.options || '{\r\n}');
  920. _this.output&&_this.output.setValue(info.options || '');
  921. }
  922. setInterval(function(){
  923. _this.setValue('api_info',{
  924. id : _this.apiId,
  925. name : $('input[name=name]').val(),
  926. path : $('input[name=path]').val(),
  927. method : $('input[name=method]').val(),
  928. groupName : $('input[name=group]').val(),
  929. groupPrefix : $('input[name=prefix]').val(),
  930. script : _this.scriptEditor&&_this.scriptEditor.getValue(),
  931. parameters : _this.requestEditor&&_this.requestEditor.getValue(),
  932. options : _this.optionsEditor&&_this.optionsEditor.getValue(),
  933. output: _this.outputEditor&&_this.outputEditor.getValue()
  934. })
  935. },5000)
  936. },
  937. // 初始化快捷键
  938. initShortKey : function(){
  939. var _this = this;
  940. $('body').on('keydown',function(e){
  941. if(e.keyCode == 119){ //F8
  942. _this.doContinue();
  943. e.preventDefault();
  944. }else if(e.keyCode == 117){ //F6
  945. _this.doContinue(true);
  946. e.preventDefault();
  947. }else if(e.keyCode == 81 && (e.metaKey || e.ctrlKey)){ //Ctrl + Q
  948. _this.doTest();
  949. e.preventDefault();
  950. }else if(e.keyCode == 83 && (e.metaKey || e.ctrlKey)){ //Ctrl + S
  951. _this.doSave();
  952. e.preventDefault();
  953. }else if(e.keyCode == 78 && e.altKey){ //Alt + N
  954. _this.createNew();
  955. e.preventDefault();
  956. }else if(e.keyCode == 71 && e.altKey){ //Alt + G
  957. _this.createGroup();
  958. e.preventDefault();
  959. }else if(e.keyCode == 27 || e.keyCode == 13){ //Enter or Esc
  960. $('.dialog-wrapper:not(.disabled-auto-close)').remove();
  961. }
  962. })
  963. },
  964. //检测更新
  965. checkUpdate : function(){
  966. var _this = this;
  967. var ignoreVersion = this.getValue('ignore-version');
  968. $.ajax({
  969. url : 'https://img.shields.io/maven-central/v/org.ssssssss/magic-api.json',
  970. dataType : 'json',
  971. success : function(data){
  972. if(data.value != 'v0.4.7'){
  973. if(ignoreVersion != data.value){
  974. _this.createDialog({
  975. title : '更新提示',
  976. content : '检测到已有新版本'+data.value+',是否更新?',
  977. buttons : [{
  978. name : '更新日志',
  979. click : function(){
  980. _this.setValue('ignore-version',data.value)
  981. window.open('http://www.ssssssss.org/changelog.html')
  982. }
  983. },{
  984. name : '残忍拒绝',
  985. click : function(){
  986. _this.setValue('ignore-version',data.value)
  987. }
  988. }]
  989. })
  990. }
  991. MagicEditor.setStatusBar('版本检测完毕,最新版本为:' + data.value+',建议更新!!');
  992. }else{
  993. MagicEditor.setStatusBar('版本检测完毕,当前已是最新版');
  994. }
  995. },
  996. error : function(){
  997. MagicEditor.setStatusBar('版本检测失败');
  998. }
  999. })
  1000. },
  1001. alert : function(title,content){
  1002. this.createDialog({
  1003. title : title,
  1004. content : content,
  1005. buttons : [{name : 'OK'}]
  1006. });
  1007. },
  1008. confirm : function(title,content,callback){
  1009. this.createDialog({
  1010. title : title,
  1011. content : content,
  1012. buttons : [{name : '确定',click : function(){callback&&callback();}},{name : '取消'}]
  1013. });
  1014. },
  1015. doShowHistory : function(){
  1016. if(!this.apiId){
  1017. this.alert('历史记录','请选择接口后在查看历史记录');
  1018. return;
  1019. }
  1020. var _this = this;
  1021. var apiId = this.apiId;
  1022. var name = $('input[name=name]').val();
  1023. var scriptModel = monaco.editor.createModel(this.scriptEditor.getValue(),'magicscript');
  1024. _this.report('history_view');
  1025. this.ajax({
  1026. url : 'backups',
  1027. data : {
  1028. id : apiId
  1029. },
  1030. success : function(timestamps){
  1031. if(timestamps.length == 0){
  1032. _this.alert('历史记录','暂无历史记录信息');
  1033. return;
  1034. }
  1035. var $ul = $('<ul class="not-select"/>')
  1036. for(var i=0,len = timestamps.length;i<len;i++){
  1037. var timestamp = timestamps[i];
  1038. var timeStr = _this.getTimeStr(new Date(timestamp));
  1039. $ul.append($('<li/>').attr('data-timestamp',timestamp).attr('data-id',apiId).append(timeStr))
  1040. }
  1041. var html = $ul[0].outerHTML;
  1042. html+= '<div class="version"><span class="version-time"></span><span class="current">当前版本</span></div>'
  1043. html += '<div class="diff-editor"></div>';
  1044. _this.setStatusBar('查看历史记录:' + (name || ''));
  1045. _this.createDialog({
  1046. title : '历史记录:' + (name || ''),
  1047. content : html,
  1048. replace : false,
  1049. className : 'history-list',
  1050. buttons : [{
  1051. name : '恢复',
  1052. click : function(){
  1053. _this.scriptEditor.setValue(scriptModel.getValue());
  1054. _this.report('history_revert');
  1055. _this.setStatusBar('恢复历史记录:' + (name || ''));
  1056. }
  1057. },{
  1058. name : '取消'
  1059. }],
  1060. close : function(){
  1061. _this.diffEditor = null;
  1062. },
  1063. onCreate : function($dom){
  1064. _this.diffEditor = monaco.editor.createDiffEditor($dom.find('.diff-editor')[0], {
  1065. enableSplitViewResizing: false,
  1066. minimap : {
  1067. enabled : false
  1068. },
  1069. folding : false,
  1070. lineDecorationsWidth : 20,
  1071. fixedOverflowWidgets :false
  1072. });
  1073. _this.diffEditor.setModel({
  1074. original : scriptModel,
  1075. modified : scriptModel
  1076. });
  1077. var $version = $dom.find('.version-time');
  1078. $dom.on('click','ul li[data-timestamp]',function(){
  1079. $(this).addClass('selected').siblings().removeClass('selected');
  1080. var timestamp = $(this).data('timestamp');
  1081. $version.html($(this).text());
  1082. _this.ajax({
  1083. url : 'backup/get',
  1084. data : {
  1085. id : apiId,
  1086. timestamp : timestamp
  1087. },
  1088. success : function(info){
  1089. _this.diffEditor.setModel({
  1090. original : monaco.editor.createModel(info.script,'magicscript'),
  1091. modified : scriptModel
  1092. });
  1093. }
  1094. })
  1095. })
  1096. }
  1097. })
  1098. }
  1099. })
  1100. },
  1101. //初始化右键菜单
  1102. initContextMenu : function(){
  1103. var _this = this;
  1104. $('.api-list-container').on('contextmenu','.group-header',function(e){
  1105. _this.createContextMenu([{
  1106. name : '新建接口',
  1107. shortKey : 'Alt+N',
  1108. click : _this.createNew
  1109. },{
  1110. name : '刷新接口',
  1111. shortKey : '',
  1112. click : function (){_this.loadAPI()}
  1113. },{
  1114. name : '删除组',
  1115. shortKey : '',
  1116. click : _this.deleteGroup
  1117. },{
  1118. name : '新建分组',
  1119. shortKey : 'Alt+G',
  1120. click : _this.createGroup
  1121. },{
  1122. name : '修改分组',
  1123. shortKey : '',
  1124. click : _this.updateGroup
  1125. }],e.pageX,e.pageY,$(this));
  1126. return false;
  1127. }).on('contextmenu','.group-list li',function(e){
  1128. var $li = $(this);
  1129. _this.createContextMenu([{
  1130. name : '复制接口',
  1131. shortKey : '',
  1132. click : _this.copyApi,
  1133. },{
  1134. name : '复制路径',
  1135. shortKey : '',
  1136. click : _this.copyApiPath
  1137. },{
  1138. name : '刷新接口',
  1139. shortKey : '',
  1140. click : function (){_this.loadAPI()}
  1141. },{
  1142. name : '移动',
  1143. shortKey : 'Ctrl+M',
  1144. click : function(){
  1145. _this.alert('移动接口','功能暂未实现!');
  1146. }
  1147. },{
  1148. name : '删除接口',
  1149. shortKey : '',
  1150. click : _this.deleteApi
  1151. },{
  1152. name : '新建分组',
  1153. shortKey : 'Alt+G',
  1154. click : _this.createGroup
  1155. }],e.pageX,e.pageY,$li)
  1156. return false;
  1157. }).on('contextmenu',function(e){
  1158. _this.createContextMenu([{
  1159. name : '新建分组',
  1160. shortKey : 'Alt+G',
  1161. click : _this.createGroup
  1162. }],e.pageX,e.pageY,$(this));
  1163. return false;
  1164. })
  1165. },
  1166. initSelect : function(){
  1167. var _this = this;
  1168. $('body').on('click','.select',function(){
  1169. $('.select ul').hide();
  1170. $(this).find('ul').show();
  1171. return false;
  1172. }).on('click','.select ul li',function(){
  1173. var $this = $(this);
  1174. var prefix = $this.data('prefix');
  1175. if(prefix !== undefined){
  1176. $('input[name=prefix]').val(prefix || '');
  1177. }
  1178. $this.parent().hide().parent().find('input').val($this.text());
  1179. $this.addClass('selected').siblings().removeClass('selected');
  1180. return false;
  1181. }).on('click',function(){
  1182. $('.select ul').hide();
  1183. }).on('click','.api-list-container ul li',function(){
  1184. $('.api-list-container ul li.selected').removeClass('selected');
  1185. _this.loadAPI($(this).addClass('selected').data('id'))
  1186. }).on('click','.button-run',function(){
  1187. _this.doTest();
  1188. }).on('click','.button-history',function(){
  1189. _this.doShowHistory();
  1190. }).on('click','.button-delete',function(){
  1191. if($(this).hasClass('disabled')){
  1192. return;
  1193. }
  1194. if(_this.apiId){
  1195. var $li = $('.group-list li[data-id='+_this.apiId+']');
  1196. if($li.length > 0){
  1197. _this.deleteApi($li);
  1198. }
  1199. }
  1200. }).on('click','.button-save',function(){
  1201. _this.doSave();
  1202. }).on('click','.button-continue',function(){
  1203. _this.doContinue();
  1204. }).on('click','.button-step-over',function(){
  1205. _this.doContinue(true);
  1206. }).on('click','.button-gitee',function(){
  1207. MagicEditor.report('button-gitee');
  1208. window.open('https://gitee.com/ssssssss-team/magic-api');
  1209. }).on('click','.button-github',function(){
  1210. MagicEditor.report('button-github');
  1211. window.open('https://github.com/ssssssss-team/magic-api')
  1212. }).on('click','.button-qq',function(){
  1213. window.open('https://shang.qq.com/wpa/qunwpa?idkey=10faa4cf9743e0aa379a72f2ad12a9e576c81462742143c8f3391b52e8c3ed8d')
  1214. }).on('click','.button-help',function(){
  1215. MagicEditor.report('button-help');
  1216. window.open('https://ssssssss.org')
  1217. });
  1218. },
  1219. getValue : function(key){
  1220. return localStorage&&localStorage.getItem(key) || '';
  1221. },
  1222. setValue : function(key,value){
  1223. if(Array.isArray(value) || typeof value == 'object'){
  1224. value = JSON.stringify(value);
  1225. }
  1226. localStorage&&localStorage.setItem(key,value) || '';
  1227. },
  1228. removeValue : function(key){
  1229. localStorage&&localStorage.removeItem(key);
  1230. },
  1231. bindEditorShortKey : function(editor){
  1232. editor.addAction({
  1233. id: "editor.action.triggerSuggest.extension",
  1234. label: "触发代码提示",
  1235. precondition: "!suggestWidgetVisible && !markersNavigationVisible && !parameterHintsVisible && !findWidgetVisible",
  1236. run : function(){
  1237. editor.trigger(null, 'editor.action.triggerSuggest', {})
  1238. }
  1239. })
  1240. // Alt + / 代码提示
  1241. editor.addCommand(monaco.KeyMod.Alt | monaco.KeyCode.US_SLASH,function(){
  1242. var triggerParameterHints = editor.getAction('editor.action.triggerParameterHints');
  1243. var triggerSuggest = editor.getAction('editor.action.triggerSuggest.extension');
  1244. triggerParameterHints.run().then(function(){
  1245. setTimeout(function(){
  1246. if(triggerSuggest.isSupported()){
  1247. triggerSuggest.run();
  1248. }
  1249. },0)
  1250. });
  1251. },'!findWidgetVisible && !inreferenceSearchEditor && !editorHasSelection');
  1252. // Ctrl + Shift + U 转大写
  1253. editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KEY_U,function(){
  1254. editor.trigger(null, 'editor.action.transformToUppercase', {})
  1255. });
  1256. // Ctrl + Shift + X 转小写
  1257. editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyMod.Shift | monaco.KeyCode.KEY_X,function(){
  1258. editor.trigger(null, 'editor.action.transformToLowercase', {})
  1259. });
  1260. // Ctrl + Alt + L 代码格式化
  1261. editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyMod.Alt | monaco.KeyCode.KEY_L,function(){
  1262. editor.trigger(null, 'editor.action.formatDocument', {})
  1263. },'editorHasDocumentFormattingProvider && editorTextFocus && !editorReadonly');
  1264. // Ctrl + Alt + L 选中代码格式化
  1265. editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyMod.Alt | monaco.KeyCode.KEY_L,function(){
  1266. editor.trigger(null, 'editor.action.formatSelection', {})
  1267. },'editorHasDocumentFormattingProvider && editorHasSelection && editorTextFocus && !editorReadonly');
  1268. },
  1269. // 初始化脚本编辑器
  1270. initScriptEditor : function(){
  1271. this.ajax({
  1272. url: 'classes',
  1273. async : false,
  1274. success: function (data) {
  1275. data = data || {};
  1276. Parser.scriptClass = data.classes || {};
  1277. Parser.extensions = data.extensions || {};
  1278. }
  1279. })
  1280. $.get('classes.txt',function(txt){
  1281. Parser.importClass = txt.split('\r\n');
  1282. })
  1283. monaco.editor.defineTheme('default', {
  1284. base: 'vs',
  1285. inherit: true,
  1286. rules: [
  1287. { background: '#ffffff' },
  1288. { token: 'keywords', foreground: '000080',fontStyle : 'bold'},
  1289. { token: 'number', foreground: '0000FF' },
  1290. { token: 'keyword', foreground: '000080',fontStyle : 'bold'},
  1291. { token: 'string.sql', foreground: '008000'},
  1292. { token: 'predefined.sql', foreground: '000000'},
  1293. { token: 'operator.sql', foreground: '000080',fontStyle : 'bold'},
  1294. { token: 'key', foreground: '660E7A' },
  1295. { token: 'string.key.json', foreground: '660E7A' },
  1296. { token: 'string.value.json', foreground: '008000' },
  1297. { token: 'keyword.json', foreground: '0000FF' },
  1298. { token: 'string', foreground: '008000',fontStyle : 'bold' },
  1299. { token: 'string.invalid', foreground: '008000' ,background : 'FFCCCC'},
  1300. { token: 'string.escape.invalid', foreground: '008000' ,background : 'FFCCCC'},
  1301. { token: 'string.escape', foreground: '000080',fontStyle : 'bold'},
  1302. { token: 'comment', foreground: '808080'},
  1303. { token: 'comment.doc', foreground: '808080'},
  1304. { token: 'string.escape', foreground: '000080'}
  1305. ],
  1306. colors: {
  1307. 'editor.foreground': '#000000',
  1308. 'editor.background': '#ffffff',
  1309. // 'editor.lineHighlightBorder' : '#00000000',
  1310. 'editorLineNumber.foreground': '#999999', //行号的颜色
  1311. 'editorGutter.background' : '#f0f0f0', //行号背景色
  1312. 'editor.lineHighlightBackground' : '#FFFAE3', //光标所在行的颜色
  1313. 'dropdown.background' : '#F2F2F2', //右键菜单
  1314. 'dropdown.foreground' : '#000000', //右键菜单文字颜色
  1315. 'list.activeSelectionBackground': '#1A7DC4', //右键菜单悬浮背景色
  1316. 'list.activeSelectionForeground' : '#ffffff', //右键菜单悬浮文字颜色
  1317. }
  1318. });
  1319. monaco.editor.defineTheme('dark', {
  1320. base: 'vs-dark',
  1321. inherit: true,
  1322. rules: [
  1323. { foreground: 'A9B7C6' },
  1324. { token: 'keywords', foreground: 'CC7832',fontStyle : 'bold'},
  1325. { token: 'keyword', foreground: 'CC7832',fontStyle : 'bold'},
  1326. { token: 'number', foreground: '6897BB' },
  1327. { token: 'string', foreground: '6A8759',fontStyle : 'bold' },
  1328. { token: 'string.sql', foreground: '6A8759'},
  1329. // { token: 'predefined.sql', foreground: 'A9B7C6'},
  1330. { token: 'key', foreground: '9876AA' },
  1331. { token: 'string.key.json', foreground: '9876AA' },
  1332. { token: 'string.value.json', foreground: '6A8759' },
  1333. { token: 'keyword.json', foreground: '6897BB' },
  1334. { token: 'operator.sql', foreground: 'CC7832',fontStyle : 'bold'},
  1335. { token: 'string.invalid', foreground: '008000' ,background : 'FFCCCC'},
  1336. { token: 'string.escape.invalid', foreground: '008000' ,background : 'FFCCCC'},
  1337. { token: 'string.escape', foreground: '000080',fontStyle : 'bold'},
  1338. { token: 'comment', foreground: '808080'},
  1339. { token: 'comment.doc', foreground: '629755'},
  1340. { token: 'string.escape', foreground: 'CC7832'}
  1341. ],
  1342. colors: {
  1343. 'editor.background': '#2B2B2B',
  1344. // 'editor.lineHighlightBorder' : '#00000000',
  1345. 'editorLineNumber.foreground': '#999999', //行号的颜色
  1346. 'editorGutter.background' : '#313335', //行号背景色
  1347. 'editor.lineHighlightBackground' : '#323232', //光标所在行的颜色
  1348. 'dropdown.background' : '#3C3F41', //右键菜单
  1349. 'dropdown.foreground' : '#BBBBBB', //右键菜单文字颜色
  1350. 'list.activeSelectionBackground': '#4B6EAF', //右键菜单悬浮背景色
  1351. 'list.activeSelectionForeground' : '#FFFFFF', //右键菜单悬浮文字颜色
  1352. }
  1353. });
  1354. var theme = this.getValue('skin') || 'default';
  1355. this.report('theme_' + theme);
  1356. this.scriptEditor = monaco.editor.create($('.editor-container')[0], {
  1357. minimap : {
  1358. enabled : false
  1359. },
  1360. language: 'magicscript',
  1361. folding : true,
  1362. lineDecorationsWidth : 35,
  1363. wordWrap : 'on',
  1364. theme : theme,
  1365. })
  1366. this.requestEditor = monaco.editor.create($('.request-editor')[0], {
  1367. value: "{}",
  1368. minimap : {
  1369. enabled : false
  1370. },
  1371. language: 'json',
  1372. folding : true,
  1373. fixedOverflowWidgets :true,
  1374. theme : theme
  1375. })
  1376. this.optionsEditor = monaco.editor.create($('.options-editor')[0], {
  1377. value: "{}",
  1378. minimap : {
  1379. enabled : false
  1380. },
  1381. language: 'json',
  1382. folding : true,
  1383. fixedOverflowWidgets :true,
  1384. theme : theme
  1385. })
  1386. this.resultEditor = monaco.editor.create($('.result-editor')[0], {
  1387. value: "{}",
  1388. minimap : {
  1389. enabled : false
  1390. },
  1391. language: 'json',
  1392. folding : true,
  1393. readOnly : true,
  1394. fixedOverflowWidgets : true,
  1395. theme : theme,
  1396. wordWrap : 'on'
  1397. })
  1398. var _this = this;
  1399. this.scriptEditor.onMouseDown(function(e){
  1400. if($(e.target.element).hasClass("codicon")){
  1401. return;
  1402. }
  1403. if (e.target.detail && e.target.detail.offsetX && e.target.detail.offsetX >= 0 && e.target.detail.offsetX <= 90) {
  1404. var line = e.target.position.lineNumber;
  1405. if (_this.scriptEditor.getModel().getLineContent(line).trim() === '') {
  1406. return
  1407. }
  1408. if(_this.hasBreakPoint(line)){
  1409. _this.removeBreakPoint(line);
  1410. }else{
  1411. _this.addBreakPoint(line);
  1412. }
  1413. }
  1414. });
  1415. this.bindEditorShortKey(this.scriptEditor);
  1416. this.bindEditorShortKey(this.requestEditor);
  1417. this.bindEditorShortKey(this.optionsEditor);
  1418. },
  1419. navigateTo : function(index){
  1420. var $parent = $('.bottom-container');
  1421. var $dom = $parent.find('.bottom-content-container').show();
  1422. $parent.find('.bottom-tab li').eq(index).addClass('selected').siblings().removeClass('selected');
  1423. $dom.find('.bottom-content-item').eq(index).show().siblings('.bottom-content-item').hide();
  1424. this.layout();
  1425. },
  1426. createDialog : function(options){
  1427. options = options || {};
  1428. var $dialog = $('<div/>').addClass('dialog');
  1429. if(options.className){
  1430. $dialog.addClass(options.className);
  1431. }
  1432. var $header = $('<div/>').addClass('dialog-header').addClass('not-select').append(options.title || '');
  1433. if(options.allowClose !== false){
  1434. var $close = $('<span/>').append('<i class="iconfont icon-close"></i>');
  1435. $header.append($close);
  1436. $close.on('click',function(){
  1437. if(options.close&&options.close()){
  1438. return;
  1439. }
  1440. $wrapper.remove();
  1441. })
  1442. }
  1443. $dialog.append($header);
  1444. var content = options.content || '';
  1445. if(options.replace !== false){
  1446. content = content.replace(/\n/g,'<br>').replace(/ /g,'&nbsp;').replace(/\t/g,'&nbsp;&nbsp;&nbsp;&nbsp;');
  1447. }
  1448. $dialog.append('<div class="dialog-content">' + content + '</div>');
  1449. var buttons = options.buttons || [];
  1450. var $buttons = $('<div/>').addClass('dialog-buttons').addClass('not-select');
  1451. if(buttons.length > 1){
  1452. $buttons.addClass('button-align-right');
  1453. }
  1454. for(var i=0,len = buttons.length;i<len;i++){
  1455. var button = buttons[i];
  1456. $buttons.append($('<button/>').html(button.name || '').addClass(button.className || '').addClass(i == 0 ? 'active' : ''));
  1457. }
  1458. $dialog.append($buttons);
  1459. var $wrapper = $('<div/>').addClass('dialog-wrapper').append($dialog);
  1460. if(!options.autoClose){
  1461. $wrapper.addClass("disabled-auto-close")
  1462. }
  1463. if(options.shade){
  1464. $wrapper.addClass("shade")
  1465. }
  1466. $buttons.on('click','button',function(){
  1467. var index = $(this).index();
  1468. if(buttons[index].click&&buttons[index].click($dialog) === false){
  1469. return;
  1470. }
  1471. options.close&&options.close();
  1472. $wrapper.remove();
  1473. })
  1474. $('body').append($wrapper);
  1475. options.onCreate&&options.onCreate($wrapper);
  1476. },
  1477. createContextMenu : function(menus,left,top,$dom){
  1478. $('.context-menu').remove();
  1479. var $ul = $('<ul/>').addClass('context-menu').addClass('not-select');
  1480. for(var i=0,len = menus.length;i<len;i++){
  1481. var menu = menus[i];
  1482. $ul.append($('<li/>').append('<label>'+menu.name+'</label>').append('<span>'+(menu.shortKey || '')+'<span>'));
  1483. }
  1484. $ul.on('click','li',function(){
  1485. var menu = menus[$(this).index()]
  1486. menu&&menu.click&&menu.click($dom);
  1487. });
  1488. $ul.css({
  1489. left : left + 'px',
  1490. top : top + 'px'
  1491. })
  1492. $('body').append($ul).on('click',function(){
  1493. $ul.remove();
  1494. });
  1495. },
  1496. // 初始化左侧工具条
  1497. initLeftToobarContainer : function(){
  1498. var $apiContainr = $('.api-list-container');
  1499. var value = this.getValue('left-toolbar-width');
  1500. if(value && !isNaN(Number(value))){
  1501. $apiContainr.width(value);
  1502. }
  1503. if('false' == this.getValue('left-toolbar-show')){
  1504. $('.left-toolbar-container li').removeClass('selected');
  1505. $apiContainr.hide();
  1506. }
  1507. var _this = this;
  1508. $('.left-toolbar-container').on('click','li',function(){
  1509. var $this = $(this);
  1510. if($this.hasClass('selected')){ //当前是选中状态
  1511. $this.removeClass('selected');
  1512. _this.setValue('left-toolbar-show',false);
  1513. $apiContainr.hide();
  1514. }else{
  1515. $this.addClass('selected');
  1516. _this.setValue('left-toolbar-show',true);
  1517. $apiContainr.show();
  1518. }
  1519. _this.layout();
  1520. })
  1521. var $middleContainer = $('.middle-container');
  1522. // 调整宽度
  1523. var resizer = $middleContainer.find('.resizer-x')[0];
  1524. resizer.onmousedown = function(){
  1525. var box = $apiContainr[0].getClientRects()[0];
  1526. document.onmousemove = function(e){
  1527. var move = e.clientX - 22;
  1528. if(move > 150 && move < 700){
  1529. _this.layout();
  1530. _this.setValue('left-toolbar-width',move);
  1531. $apiContainr.width(move);
  1532. }
  1533. }
  1534. document.onmouseup = function(evt){
  1535. document.onmousemove = null;
  1536. document.onmouseup = null;
  1537. resizer.releaseCapture && resizer.releaseCapture();
  1538. }
  1539. resizer.setCapture && resizer.setCapture();
  1540. }
  1541. $('body').on('click','.group-header',function(){
  1542. var $parent = $(this).parent();
  1543. if($parent.hasClass('opened')){
  1544. $parent.removeClass('opened');
  1545. $(this).find('.icon-arrow-bottom').removeClass('icon-arrow-bottom').addClass('icon-arrow-right');
  1546. }else{
  1547. $parent.addClass('opened');
  1548. $(this).find('.icon-arrow-right').removeClass('icon-arrow-right').addClass('icon-arrow-bottom');
  1549. }
  1550. })
  1551. },
  1552. formatJson : function (val, defaultVal) {
  1553. return (val ? JSON.stringify(val, null, 4) : defaultVal) || '';
  1554. },
  1555. // 初始化底部
  1556. initBottomContainer : function(){
  1557. var $contentContainer = $('.bottom-container .bottom-content-container');
  1558. var value = this.getValue('bottom-container-height');
  1559. if(value && !isNaN(Number(value))){
  1560. $contentContainer.height(value);
  1561. }
  1562. if('false' == this.getValue('bottom-tab-show')){
  1563. $contentContainer.hide(); //隐藏全部
  1564. $('.bottom-container .bottom-tab li').removeClass('selected');
  1565. }else{
  1566. var index = Number(this.getValue('bottom-tab-index'));
  1567. if(!isNaN(index)){
  1568. this.navigateTo(index);
  1569. }
  1570. }
  1571. var _this = this;
  1572. $('.bottom-container').on('click','.bottom-tab li',function(){
  1573. var $this = $(this);
  1574. if($this.hasClass('selected')){ //当前是选中状态
  1575. $contentContainer.hide(); //隐藏全部
  1576. $this.removeClass('selected')
  1577. _this.setValue('bottom-tab-show',true);
  1578. }else{
  1579. $this.addClass('selected').siblings().removeClass('selected'); //选中选择项,取消其他选择项
  1580. var index = $(this).index();
  1581. _this.setValue('bottom-tab-index',index);
  1582. _this.setValue('bottom-tab-show',true);
  1583. $contentContainer.show().find('.bottom-content-item').hide().eq(index).show();
  1584. }
  1585. _this.layout();
  1586. }).on('click','.button-minimize',function(){
  1587. _this.setValue('bottom-tab-show',false);
  1588. $contentContainer.hide(); //隐藏全部
  1589. $('.bottom-tab li').removeClass('selected');
  1590. _this.layout();
  1591. });
  1592. // 调整底部高度
  1593. var resizer = $contentContainer.find('.resizer-y')[0];
  1594. resizer.onmousedown = function(){
  1595. var box = $contentContainer[0].getClientRects()[0];
  1596. document.onmousemove = function(e){
  1597. if(e.clientY > 150){
  1598. var move = box.height - (e.clientY - box.y);
  1599. if(move > 30){
  1600. _this.setValue('bottom-container-height',move);
  1601. _this.layout();
  1602. $contentContainer.height(move);
  1603. }
  1604. }
  1605. }
  1606. document.onmouseup = function(evt){
  1607. document.onmousemove = null;
  1608. document.onmouseup = null;
  1609. resizer.releaseCapture && resizer.releaseCapture();
  1610. }
  1611. resizer.setCapture && resizer.setCapture();
  1612. }
  1613. $('.bottom-container').on('click','.bottom-content-item:eq(0) .button-clear',function(){
  1614. _this.requestEditor&&_this.requestEditor.setValue('{}');
  1615. }).on('click','.bottom-content-item:eq(0) .button-format',function(){
  1616. try{
  1617. _this.requestEditor.setValue(_this.formatJson(JSON.parse(_this.requestEditor.getValue()),'{\r\n}'));
  1618. }catch(e){}
  1619. }).on('click','.bottom-content-item:eq(1) .button-clear',function(){
  1620. _this.optionsEditor&&_this.optionsEditor.setValue('{}');
  1621. }).on('click','.bottom-content-item:eq(1) .button-format',function(){
  1622. try{
  1623. _this.optionsEditor.setValue(_this.formatJson(JSON.parse(_this.optionsEditor.getValue()),'{\r\n}'));
  1624. }catch(e){}
  1625. }).on('click','.bottom-content-item:eq(2) .button-clear',function(){
  1626. _this.resultEditor&&_this.resultEditor.setValue('{}');
  1627. }).on('click','.bottom-content-item:eq(2) .button-format',function(){
  1628. try{
  1629. _this.resultEditor.setValue(_this.formatJson(JSON.parse(_this.resultEditor.getValue()),'{\r\n}'));
  1630. }catch(e){}
  1631. }).on('click','.bottom-content-item:eq(4) .button-clear',function(){
  1632. $('.bottom-container .bottom-item-body.output').html('')
  1633. })
  1634. },
  1635. setSkin : function(skin){
  1636. $('body').addClass('skin-' + skin);
  1637. this.setValue('skin',skin);
  1638. monaco.editor.setTheme(skin);
  1639. MagicEditor.report('theme_' + skin);
  1640. MagicEditor.setStatusBar('切换皮肤至:' + skin);
  1641. //this.scriptEditor&&this.scriptEditor.setTheme(skin);
  1642. },
  1643. layout : function(){
  1644. this.scriptEditor&&this.scriptEditor.layout();
  1645. this.optionsEditor&&this.optionsEditor.layout();
  1646. this.requestEditor&&this.requestEditor.layout();
  1647. this.resultEditor&&this.resultEditor.layout();
  1648. this.diffEditor&&this.diffEditor.layout();
  1649. }
  1650. }
  1651. $(function(){
  1652. require(['vs/editor/editor.main'],function(){
  1653. MagicEditor.init();
  1654. })
  1655. $(window).resize(function(){
  1656. MagicEditor.layout();
  1657. });
  1658. });