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