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