support sub dirs
[mdref/mdref] / index.js
1 function log() {
2 // console.log.apply(console, arguments);
3 }
4
5 function is_constant(s) {
6 s = s.replace(/v\d+(_\d+)?$/, "");
7 if (s.length < 2) {
8 return false;
9 }
10 return s.toUpperCase(s) === s;
11 }
12
13 function is_variable(s) {
14 return s.substring(0,1) === "$";
15 }
16
17 var is_in_string = false;
18
19 function type(s, nn) {
20 var i, j, t;
21 //log("type", s);
22 // nothing
23 if (!s.match(/[a-zA-Z]/)) {
24 return;
25 }
26
27 switch (s) {
28 // types
29 case "void":
30 case "bool":
31 case "int":
32 case "float":
33 case "string":
34 case "array":
35 case "object":
36 case "callable":
37 case "mixed":
38 // Zend/SPL
39 case "stdClass":
40 case "Exception":
41 case "ErrorException":
42 case "Closure":
43 case "Generator":
44 case "Countable":
45 case "Serializable":
46 case "Traversable":
47 case "Iterator":
48 case "IteratorAggregate":
49 case "ArrayAccess":
50 case "ArrayObject":
51 case "ArrayIterator":
52 case "RecursiveArrayIterator":
53 case "SplObserver":
54 case "SplSubject":
55 case "SplObjectStorage":
56 return "<code>";
57
58 // keywords
59 case "extends":
60 case "implements":
61 if (nn === "H1") {
62 return "<br>&nbsp;<em>";
63 }
64 case "class":
65 case "interface":
66 case "namespace":
67 case "public":
68 case "protected":
69 case "private":
70 case "static":
71 case "final":
72 case "abstract":
73 case "self":
74 case "parent":
75 // phrases
76 case "Optional":
77 case "optional":
78 return "<em>";
79 }
80
81 // class members
82 if (-1 !== (i = s.indexOf("::"))) {
83 t = s.substring(i+2);
84 if (!is_constant(t) && !is_variable(t)) {
85 // methods
86 return "<a href=\"" + s.replace(/::|\\/g, "/") + "\">";
87 }
88 }
89 if (-1 !== (j = s.indexOf("\\")) && s.substr(j+1,1) !== "n") {
90 return "<a href=\"" + s.replace(/\\/g, "/").replace(/::|$/, "#") + "\">";
91 }
92
93 switch (s.toLowerCase()) {
94 // variables
95 default:
96 if (!is_variable(s)) {
97 break;
98 }
99 // special constants
100 case "null":
101 case "true":
102 case "false":
103 return "<span class=\"var\">";
104 }
105
106 // constants
107 if (is_constant(s)) {
108 return "<span class=\"constant\">";
109 }
110 }
111
112 function node(s, nn) {
113 //log("node", s);
114
115 var t;
116
117 if ((t = type(s, nn))) {
118 return $(t).text(s);
119 }
120 return document.createTextNode(s);
121 }
122 function wrap(n, nn) {
123 var $n = $(n)
124 var a = [];
125
126 $n.text().split(/([^a-zA-Z0-9_\\\$:]+)/).forEach(function(v) {
127 a.push(node(v, nn));
128 });
129 $n.replaceWith(a);
130 }
131 function walk(i, e) {
132 log("walk", i, e);
133
134 $.each($.makeArray(e.childNodes), function(i, n) {
135 switch (n.nodeName) {
136 case "A":
137 case "BR":
138 case "HR":
139 break;
140 case "#text":
141 wrap(n, e.nodeName);
142 break;
143 default:
144 walk(-1, n);
145 break;
146 }
147 });
148 }
149
150 function blink(c) {
151 var $c = $(c);
152
153 $c.fadeOut("fast").queue(function(next) {
154 this.style.color = "red";
155 next();
156 }).fadeIn("fast").fadeOut("slow").queue(function(next) {
157 this.style.color = "";
158 next();
159 }).fadeIn("slow");
160 }
161
162 function hashchange() {
163 if (location.hash.length > 1) {
164 var hash = location.hash.substring(1);
165 var name = is_variable(hash) ? ".var" : ".constant";
166 var scrolled = false;
167
168 $(name).each(hash.substring(hash.length-1) === "_" ? function(i, c) {
169 if (c.textContent.substring(0, hash.length) === hash) {
170 if (!scrolled) {
171 $(window).scrollTop($(c).offset().top - 100);
172 scrolled = true;
173 }
174 blink(c);
175 }
176 } : function(i, c) {
177 if (c.textContent === hash) {
178 $(window).scrollTop($(c).offset().top - 100);
179 blink(c);
180 return false;
181 }
182 });
183 }
184 }
185
186 $(function() {
187 $("h1,h2,h3,h4,h5,h6,p,li,code").each(walk);
188 $(window).on("hashchange", hashchange);
189 hashchange();
190 });