20 |
欢迎使用 AppHost Remote Debugger
21 |
** 调试之前,首先打开一个 h5 页面 **
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
99 |
100 |
108 |
109 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
--------------------------------------------------------------------------------
/AppHostExample/AppHost.framework/server.js:
--------------------------------------------------------------------------------
1 | window.appHost = {
2 | invoke: function (action, param) {
3 | var xhr = new XMLHttpRequest();
4 | xhr.open("POST", "/command.do", true);
5 | xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
6 | xhr.onload = function () {
7 | console.log(xhr.responseURL); // http://example.com/test
8 | };
9 |
10 | xhr.send(
11 | "action=" + action + "¶m=" + encodeURIComponent(window.JSON.stringify(param))
12 | );
13 | }
14 | };
15 |
16 | function _renderLogs(logs) {
17 | if (!logs) return;
18 |
19 | for (var i = 0; i < logs.length; i++) {
20 | var log = window.JSON.parse(logs[i]);
21 | var logType = log.type;
22 | var logVal = log.value;
23 |
24 | // 查询所有的接口,显示需要特殊处理下
25 | if (logVal.action === "requestToTiming_on_mac"){
26 | ah_timing(logVal.param);
27 | } else if (logVal.action === "list") {
28 | var apis = [];
29 | for (var key in logVal.param) {
30 | if (logVal.param.hasOwnProperty(key)) {
31 | var response = logVal.param[key];
32 | if (response.length > 0) {
33 | apis.push({
34 | type: "group",
35 | value: key + " 的方法包括;"
36 | });
37 | for (var k = 0; k < response.length; k++) {
38 | apis.push({
39 | type: "api",
40 | value: " - " + response[k]
41 | });
42 | }
43 | }
44 | }
45 | }
46 | addStore({
47 | type: "list",
48 | apis: apis
49 | });
50 | } else if (logVal.action.indexOf("apropos.") >= 0) {
51 | // 特殊处理 API 接口的显示
52 | var doc = logVal.param;
53 | addStore({
54 | type: "apropos_item",
55 | doc: doc
56 | });
57 | } else if (logVal.action == 'eval') {
58 | var r = '';
59 | if (logVal.param){
60 | r = logVal.param.result || logVal.param.err;
61 | }
62 | addStore({
63 | type: "evalResult",
64 | message: r?r:'(空)'
65 | });
66 | } else if (logVal.action == 'console.log') {
67 | addStore({
68 | type: "console.log",
69 | message: logVal.param.text
70 | });
71 | } else {
72 | // 先显示日志类型,
73 | var eleId = "eid" + window.kLogIndex++;
74 | /**
75 | * 先初始化一个带 id 的 div,然后在确认渲染成功后,使用 dom 原生的方法,把 renderjson 对象加上去.
76 | * 注意 renderjson 对象是带事件的,如果直接渲染为 HTML 会出现丢失事件的情况
77 | *
78 | * */
79 |
80 | var preEle = renderjson.set_icons("+", "-").set_show_to_level(2)(logVal);
81 |
82 | var metaFunc = function (_id, e, d) {
83 | return function () {
84 | var ele = d.getElementById(_id);
85 | ele.appendChild(e);
86 | };
87 | };
88 | addStore(
89 | {
90 | type: "log",
91 | message: logType,
92 | eid: eleId
93 | },
94 | metaFunc(eleId, preEle, document)
95 | );
96 | }
97 | }
98 | }
99 |
100 | window.kLogIndex = 1;
101 | function loop() {
102 | var xhr = new XMLHttpRequest();
103 | xhr.open("POST", "/react_log.do", true);
104 | xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
105 | xhr.responseType = "json";
106 | xhr.onload = function () {
107 | // console.log(xhr.response);
108 | var json = xhr.response;
109 | if (json.code == "OK") {
110 | var data = json.data;
111 | var logs = data ? data.logs : [];
112 | _renderLogs(logs);
113 | }
114 | };
115 | xhr.send("");
116 | }
117 |
118 | function scrollToBottom() {
119 | // scroll to bottom
120 | var output = document.getElementById("app");
121 | output.scrollTop = output.scrollHeight;
122 | }
123 |
124 | // 如果可以处理,且已经处理完毕,则返回 null
125 | // 无法处理的则抛到外部
126 | function _parseCommand(com) {
127 | if (com.indexOf(":") == 0) {
128 | var args;
129 | if (com == ":testcase") {
130 | com = "window.appHost.invoke('testcase', {})";
131 | } else if (com.indexOf(":list") >= 0) {
132 | com = "window.appHost.invoke('list', {})";
133 | } else if (com.indexOf(":apropos") >= 0) {
134 | args = com.split(" ");
135 | if (args.length == 2) {
136 | com = "window.appHost.invoke('apropos', {name:'" + args[1] + "'})";
137 | } else {
138 | console.log("参数出错 " + com);
139 | com = null;
140 | }
141 | } else if (com.indexOf(":weinre") >= 0) {
142 | var url = com.replace(':weinre','').trim();
143 | if (url.length > 0) {
144 | if (url === "disable") {
145 | com = "window.appHost.invoke('weinre', {disabled:true})";
146 | } else {
147 | com = "window.appHost.invoke('weinre', {url:'" + url + "'})";
148 | }
149 | } else {
150 | console.log("参数出错 " + com);
151 | com = null;
152 | }
153 | } else if (com.indexOf(":timing") >= 0) {
154 | var mobile = com.replace(":timing", "").trim();
155 | if (mobile.length > 0){
156 | com = "window.appHost.invoke('timing', {mobile:true})";
157 | } else {
158 | com = "window.appHost.invoke('timing', {})";
159 | }
160 | } else if (com.indexOf(":eval") >= 0) {
161 | var code = com.replace(":eval", "").trim();
162 | if (code.length > 0) {
163 | var p = window.JSON.stringify({ code: code.trim() });
164 | com = "window.appHost.invoke('eval', " + p + ")";
165 | } else {
166 | console.log("参数出错 " + com);
167 | com = null;
168 | }
169 | } else {
170 | window.alert("不支持的命令 " + com);
171 | com = null;
172 | }
173 | }
174 |
175 | return com;
176 | }
177 | // vue
178 | var store = {
179 | debug: true,
180 | state: {
181 | dataSource: []
182 | },
183 | setMessageAction: function (newValue) {
184 | if (this.debug) console.log("setMessageAction triggered with", newValue);
185 | this.state.message = newValue;
186 | },
187 | clearMessageAction: function () {
188 | if (this.debug) console.log("clearMessageAction triggered");
189 | this.state.message = "";
190 | }
191 | };
192 |
193 | function addStore(_obj, _domreadyblock) {
194 | store.state.dataSource.push(_obj);
195 | Vue.nextTick(function () {
196 | if (_domreadyblock && typeof _domreadyblock === "function") {
197 | _domreadyblock();
198 | }
199 | scrollToBottom();
200 | });
201 | }
202 | // 输入命令和点击按钮区域
203 | var clientStorage = window.localStorage;
204 | var COMMOND_HISTORY = 'command_history';
205 | var history_header_cursor = clientStorage.length;
206 | var history_search_cursor = history_header_cursor;
207 | var MAX_HISTORY = 100;
208 |
209 | function _run_command(com) {
210 | if (com.length === 0) {
211 | alert("请输入命令");
212 | return;
213 | }
214 | // 先处理对控制台的控制的命令,然后处理需要获取业务数据的命令
215 | if (com == ":clear") {
216 | store.state.dataSource.splice(0);
217 | com = null;
218 | } else if (com == ":history") {
219 | var cm = [];
220 | var len = clientStorage.length;
221 | for (var i = len - 1; i >= 0; i--){
222 | cm.push(clientStorage.getItem(COMMOND_HISTORY + i));
223 | }
224 | addStore({
225 | type: "history",
226 | data: cm
227 | });
228 | } else if (com == ":help") {
229 | addStore({
230 | type: "help",
231 | message: ""
232 | });
233 | } else {
234 | addStore({
235 | type: "command",
236 | message: com
237 | });
238 | try {
239 | var newCom = _parseCommand(com);
240 | if (newCom && newCom.length > 0) {
241 | var r = window.eval(newCom);
242 | if (r) {
243 | addStore({
244 | type: "evalResult",
245 | message: r.toString()
246 | });
247 | }
248 | }
249 | } catch (error) {
250 | if (error) {
251 | addStore({
252 | type: "error",
253 | message: error.message
254 | });
255 | }
256 | }
257 | }
258 | }
259 |
260 | Vue.component("command-value", {
261 | data: function () {
262 | return {
263 | command: ":help"
264 | };
265 | },
266 | template: "#command-value-template",
267 | methods: {
268 | submit: function () {
269 | this.$refs.run.click();
270 | },
271 | history: function(up){
272 | if (up){
273 | history_search_cursor--;
274 | } else {
275 | history_search_cursor++;
276 | }
277 | history_search_cursor = history_search_cursor % MAX_HISTORY;
278 | history_search_cursor = Math.max(0, history_search_cursor);
279 | history_search_cursor = Math.min(history_header_cursor, history_search_cursor);
280 | var n = clientStorage.getItem(COMMOND_HISTORY + history_search_cursor);
281 | if (n){
282 | document.getElementById('command').value = n;
283 | this.command = n;
284 | }
285 | },
286 | run: function () {
287 | var com = this.command;
288 | var oldCom = com;
289 |
290 | if(window.ah_env.isMobile){
291 | com = ':eval ' + com;
292 | }
293 | _run_command(com);
294 | command.value = '';
295 | this.command = '';
296 | //
297 | clientStorage.setItem(COMMOND_HISTORY + (history_header_cursor++), oldCom);
298 | history_search_cursor = history_header_cursor;
299 | }
300 | }
301 | });
302 |
303 | // 执行结果或者服务器推送的结果区域
304 | Vue.component("command-output", {
305 | data: function () {
306 | return {
307 | dataSource: store.state.dataSource
308 | };
309 | },
310 | methods: {
311 | useHistoryCommand: function(e){
312 | var ele = e.target;
313 | var com = ele.dataset.command;
314 | _run_command(com);
315 | }
316 | },
317 | template: "#command-output-template"
318 | });
319 |
320 | document.addEventListener(
321 | "DOMContentLoaded",
322 | function (event) {
323 | console.log("DOM ready!");
324 | var app = new Vue({
325 | el: "#app",
326 | created: function () {
327 | console.log("App goes");
328 | },
329 | mounted: function () {
330 | window.setInterval(loop, 2000);
331 | }
332 | });
333 | },
334 | false
335 | );
336 |
337 | function jdb(line) {
338 | // do a thing, possibly async, then…
339 | if (window.__bri == line) {
340 | window.alert("Stop at Debugger;");
341 | } else {
342 | console.log("Skip at line " + line);
343 | }
344 | }
345 | document.addEventListener("readystatechange", function (event) {
346 | console.log("readystatechange!" + document.readyState);
347 | if (document.readyState == "complete") {
348 | // jsdebugger
349 | window.__bri = -1;
350 |
351 | var command = document.getElementById("command");
352 | // jdb(0);
353 | // var run = document.getElementById('run');
354 | // jdb(1);
355 | // var a = 10;
356 | // jdb(2)
357 | // var c = 9;
358 | // jdb(3)
359 | // a = a + ~c + 1;
360 | // jdb(4)
361 | // console.log(a);
362 | // jdb(5)
363 |
364 | // run.onclick = function (e) {
365 | // var com = command.value; jdb(6);
366 | // if (com.length > 0) {
367 | // eval(com); jdb(7);
368 | // } else {
369 | // alert('请输入命令'); jdb(8);
370 | // }
371 | // }
372 | }
373 | });
374 |
--------------------------------------------------------------------------------
/AppHostExample/AppHost.framework/testcase.tmpl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |