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的灵活之处,页面布局部分的代码就不介绍了,大家可从下载附件自行研究,最后附上附件
见原文最下方