JavaScript是一門動態語言,但是面向對象的特征沒有Java那么明顯,導致我們開發起來非常不習慣。拿繼承來說,Java中有專門的extends和implements關鍵字實現,但是在JavaScript卻引入了一個"別扭的"prototype,而且實現方式有很多種,雖然靈活,卻很容易讓初學者一頭霧水不知所措。TWaver HTML5的doc是一個js文件,對編輯器很友好,但是我們看起來卻不是很方便,遠不如JavaDoc來得一目了然,于是我們做了另一個html,算是對原js格式doc的補充。先看一下最后完成的效果:
運行截圖:
這并不是一個靜態頁面,而是用JavaScript動態解析TWaver,所以即使TWaver更新也沒關系,只要在這個html中引入新的twaver.js就可以了。
界面說明:整個頁面的布局借助twaver.controls.SplitPane實現,左側是TWaver的樹組件,中間是List組件,右側是一個pre標簽,借助beautify和prettify展現js代碼。
接下來介紹一下頁面上三個部分的實現方式:
1 //twaver.Util.getAllClassNames()返回TWaver所有的內部類型
2 var allClassNames = twaver.Util.getAllClassNames();
3 //遍歷類名
4 allClassNames.forEach(
function (data) {
5 addClass(data);
6 });
7 var elementBox =
new twaver.ElementBox();
8 var map = {};
9 //初始化Tree box
10 function addClass(name) {
11 var node =
new twaver.Node(name);
12 node.setName(name);
13 //分割字符串,找我們需要的類或對象
14 var clazzArr = name.split(".");
15 var clazz = window;
16 for (
var i = 0; i < clazzArr.length; i++) {
17 clazz = clazz[clazzArr[i]];
18 }
19 //根據不同的類型設置Icon
20 if (
typeof clazz === "object") {
21 node.setIcon("object");
22 }
else if (
typeof clazz === "function") {
23 node.setIcon("class");
24 }
25 //同時將node存入map,方便查詢使用
26 map[name] = node;
27 node.clazz = clazz;
28 elementBox.add(node); }
29 //
30 var tree =
new twaver.controls.Tree(elementBox);
getAllClassNames返回的是類或對象的名字,但是我們要獲得實際的類和對象以便可以解析它們的屬性和方法。思考一下:在瀏覽器環境下window是根對象,所有全局對象都是window對象的屬性而已,TWaver也不例外,所以我們分割字符串,通過window對象一層一層找屬性,最終找到我們要的類或對象。然后我們生成Node,填充box數據容器;同時做了一個< 名字---Node>的映射,后面會用到。 初始化Tree以后設置節點的父子關系:
1 elementBox.toDatas().forEach(function (data) {
2 //TWaver所有內置類型都有superClass屬性,指向實際的父類或父對象,通過它可以得到父類然后從map中取出樹上相應的節點
3 var superClass = data.clazz.superClass;
4 if (superClass && superClass.getClassName) {
5 data.setParent(map[superClass.getClassName()]);
6 }
7 });
大家注意到,Tree的上方還有一個用來過濾數據的文本框,我們看一下它的事件處理:
1 //Tree的過濾文本框
2 var treeFilter = document.createElement("input");
3 treeFilter.type = "text";
4 treeFilter.addEventListener("input",
function () {
5 var value = treeFilter.value.trim().toLowerCase();
6 if (value.length > 0) {
7 tree.setVisibleFunction(
function (data) {
8 if(data.getName().toLowerCase().indexOf(value)>=0){
return true};
9 //如果當前節點的名字與過濾字符串不匹配,就去查詢是否有子節點匹配
10 //如果子節點匹配,父節點同樣可見
11 return isChildVisible(data, value);
12 });
13 }
else {
14 tree.setVisibleFunction(
null);
15 }
16 });
17 //
18 //遞歸是否有子節點可見
19 function isChildVisible(parent, value) {
20 var children = parent.getChildren();
21 for (
var i = 0; i < children.size(); i++) {
22 var child = children.get(i);
23 if (child.getName().toLowerCase().indexOf(value) >= 0) {
24 return true;
25 }
else if (isChildVisible(child, value)) {
26 return true;
27 }
28 }
29 return false;
30 }
31 對于Tree過濾器,不能簡單的判斷當前節點,因為可能當前節點的名字不符合過濾字符串,但是子節點符合,這種時候父節點也要顯示,所以需要做遞歸處理:如果當前節點的子節點符合過濾字符串,當前節點同樣可見。
List比Tree要簡單的多,監聽Tree的選中改變事件,得到選中的Node然后解析屬性和方法,填充進List的數據容器
1 tree.getSelectionModel().addSelectionChangeListener(function (e) {
2 var selectedData = tree.getSelectionModel().getLastData();
3 if (selectedData) {
4 var html = '';
5 list.getDataBox().clear();
6 //TWaver內置的屬性和方法都在prototype上,所以如果data的類型是function,我們就遍歷它的prototype
7 var obj = typeof selectedData.clazz === "function" ? selectedData.clazz.prototype : selectedData.clazz;
8 for (var name in obj) {
9 if (obj.hasOwnProperty(name)) {
10 var listNode = new twaver.Node();
11 if (typeof obj[name] === "function") {
12 listNode.setIcon("method");
13 } else {
14 listNode.setIcon("property");
15 }
16 listNode.setName(name + " - " + typeof obj[name]);
17 //注意getContent方法,對于function直接返回,對于Object再進行一次遍歷
18 listNode.content = "" + getContent(obj[name]);
19 html += name + ":" + listNode.content;
20 list.getDataBox().add(listNode);
21 }
22 }
23 pre.innerHTML = '';
24 html = js_beautify(html);
25 pre.appendChild(document.createTextNode(html));
26 prettyPrint();
27 }
28 });
除了填充list,我們還直接把對象屬性的js代碼組合起來放到右側pre中,為了規范js代碼,我們用到了js_beautify和prettify兩個類庫,感興趣的同學可以谷歌一下。
在List上點擊某個屬性或方法的時候,更新pre的內容為屬性值或方法代碼
1 //list選中的節點發生變化時更新pre
2 list.getSelectionModel().addSelectionChangeListener(function (e) {
3 var data = list.getSelectionModel().getLastData();
4 if (data) {
5 pre.innerHTML = '';
6 var html = js_beautify(data.content + "");
7 pre.appendChild(document.createTextNode(html));
8 prettyPrint();
9 }
10 });
這三部分介紹完,實際上這個頁面也就寫完了,通過這些代碼,大家應該可以感受到JavaScript的靈活之處,頁面布局部分的代碼就不介紹了,大家可從下載附件自行研究,最后附上附件
見原文最下方