├── .gitignore
├── README.md
├── c2-fed-index.js
├── c2-fed-roster.js
├── c2-fed-update.sh
├── docs
├── .htaccess
├── CNAME
├── c2-fed-index.json
├── c2-fed-roster.json
├── compare.html
├── favicon.gif
├── favicon.png
├── index.html
├── inlinks.json
├── markup.js
├── md5.js
├── names.txt
├── spin.gif
├── style.css
└── wiki.gif
├── encode-links.js
├── json.c
├── json.rb
├── links.txt
├── sync-gh-pages.sh
├── sync.sh
└── words.txt
/.gitignore:
--------------------------------------------------------------------------------
1 | wiki.wdb
2 | new
3 | static/pages
4 | a.out
5 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Remodeling
2 | We're rewriting the original wiki as a single-page application.
3 |
4 | Write privileges were withdrawn in 2015 after a disgruntled visitor threatened automated destruction.
5 | We described the situation then in one of our usual system notices.
6 |
7 | https://wiki.c2.com/?WikiWikiSystemNotice
8 |
9 | We were closed completely for one week this year with the following explanation. We're open now and continuing to improve.
10 |
11 | https://c2.com/wiki/closed.html
12 |
13 | # Process
14 | We will focus on restoring some service as soon as possible. Then advance a broader agenda in several more stages.
15 | Throughout our remodel we will focus on simple implementation with minimum dependencies
16 | requiring the least attention in years ahead.
17 |
18 | - [return wiki to 95% service](https://github.com/WardCunningham/remodeling/issues/1) ✔︎
19 | - [full service static site](https://github.com/WardCunningham/remodeling/issues/2)
20 | - [integrate with read-write federated wiki pods](https://github.com/WardCunningham/remodeling/issues/3) ✔︎
21 |
22 | # Website
23 | We have a new site running and available for browsing.
24 | Review our checklists above to align your expectations.
25 | We're aware of missing data and all formatting is provisional.
26 |
27 | https://wiki.c2.com/?WelcomeVisitors
28 |
29 | # Integration
30 | We allow viewing more than one page at a time.
31 | We offer a button to export the visible pages in federated wiki format.
32 | Any author can import these translated page and drag content to supplement their own work.
33 |
34 | The federation scraper recognizes pages originating in the c2 wiki.
35 | These locations are fed back into the remodeled wiki where they appear as notices on excerpted pages.
36 |
--------------------------------------------------------------------------------
/c2-fed-index.js:
--------------------------------------------------------------------------------
1 | // Retrieve all changes for last week from scrape logs, index c2 wiki links
2 | // usage: deno run --allow-net c2-fed-index.js > docs/c2-fed-index.json
3 |
4 | const sleep = msec => new Promise(res => setTimeout(res,msec))
5 | const asSlug = (title) => title.replace(/\s/g, '-').replace(/[^A-Za-z0-9-]/g, '').toLowerCase()
6 |
7 | // let index = {
8 | // last: 'Sat-1400',
9 | // sites: [],
10 | // slugs: [],
11 | // titles: [],
12 | // md5ws: [],
13 | // pages: [],
14 | // rows:[]
15 | // }
16 |
17 | let index = await fetch(`https://wiki.c2.com/c2-fed-index.json`).then(res => res.json())
18 | let site = `http://search.fed.wiki.org:3030`
19 | let list = await fetch(`${site}/logs`).then(res => res.text())
20 | let logs = list.trim().split(/\n/).map(line => line.split('"')[1])
21 | let seen = new Set()
22 |
23 | let had = index.rows.length
24 | for (let log of logs) {
25 | if(log.includes(index.last)) break
26 | console.error(new Date())
27 | let text = await fetch(`${site}${log}`).then(res => res.text())
28 | let who, what, match
29 | for (let line of text.split(/\n/)) {
30 | match = line.match(/^([\w.-]+), (\d+) pages$/)
31 | if (match) {who = match[1]}
32 | match = line.match(/^\t(.+?), (\d+) days ago$/)
33 | if (match) {
34 | what = match[1]
35 | if(!seen.has(who+what))
36 | // console.error(log,who,asSlug(what))
37 | await scan(log,who,what)
38 | seen.add(who+what)
39 | }
40 | }
41 | await sleep(500)
42 | }
43 | if (index.rows.length == had) {
44 | console.error(`No new joins`)
45 | Deno.exit(1)
46 | } else {
47 | console.error(`${index.rows.length - had} new joins`)
48 | }
49 |
50 | function num (column, value) {
51 | let col = index[column]
52 | let n = col.findIndex(val => val == value)
53 | if (n >= 0) return n
54 | col.push(value)
55 | return col.length-1
56 | }
57 |
58 | async function scan(log,who,what) {
59 | const includes = row => index.rows.find(has =>
60 | has[0]==row[0] && has[1]==row[1] && has[2]==row[2] && has[3]==row[3] && has[4]==row[4])
61 | let items = await fetch(`${site}/sites/${who}/pages/${asSlug(what)}/items.txt`).then(res => res.text())
62 | let md5ws = items.split(/\n/).filter(item => /^[0-9a-f]{16}w$/.test(item))
63 | if (!md5ws.length) return
64 | console.error(log,who,what,md5ws)
65 | let siten = num('sites',who)
66 | let slugn = num('slugs',asSlug(what))
67 | let titlen = num('titles',what)
68 | let page = await fetch(`http://${who}/${asSlug(what)}.json`).then(res => res.json())
69 | for (let md5w of md5ws) {
70 | let item = page.story.find(item => item.id == md5w)
71 | let md5wn = num('md5ws',md5w)
72 | let pagen = num('pages',item.wiki)
73 | let row = [siten,slugn,titlen,md5wn,pagen]
74 | if(!includes(row)) index.rows.push(row)
75 | }
76 | await sleep(500)
77 | }
78 |
79 | index.last = logs[0].split(/[\/\.]/)[2]
80 | console.log(JSON.stringify(index,null,2))
--------------------------------------------------------------------------------
/c2-fed-roster.js:
--------------------------------------------------------------------------------
1 | // Construct a roster listing indexed sites
2 | // usage: deno run --allow-read=docs c2-fed-roster.js > docs/c2-fed-roster.json
3 |
4 | const sleep = msec => new Promise(res => setTimeout(res,msec))
5 | const asSlug = (title) => title.replace(/\s/g, '-').replace(/[^A-Za-z0-9-]/g, '').toLowerCase()
6 |
7 | // let index = {
8 | // last: 'Sat-1400',
9 | // sites: [],
10 | // slugs: [],
11 | // titles: [],
12 | // md5ws: [],
13 | // pages: [],
14 | // rows:[]
15 | // }
16 |
17 | let index = JSON.parse(Deno.readTextFileSync(`docs/c2-fed-index.json`))
18 | console.error(`${index.sites.length} sites as of ${index.last} `)
19 | let title = `C2 Fed Roster`
20 | let story = [
21 | {type:'paragraph',text:`Sites with wiki.c2.com excerpts found ${index.last}.`,id:'84823980980282'},
22 | {type:'roster',text:index.sites.join("\n"),id:'23984712394812'}
23 | ]
24 | let journal = [
25 | {type:'create',date:Date.now(),item:JSON.parse(JSON.stringify({title,story}))}
26 | ]
27 | console.log(JSON.stringify({title,story,journal},null,2))
--------------------------------------------------------------------------------
/c2-fed-update.sh:
--------------------------------------------------------------------------------
1 | # Update index and roster
2 | # usage: git pull; sh c2-fed-update.sh
3 |
4 | set -e
5 | git pull
6 | deno run --allow-net c2-fed-index.js > /tmp/c2-fed-index.json
7 | mv /tmp/c2-fed-index.json docs
8 | deno run --allow-read=docs c2-fed-roster.js > docs/c2-fed-roster.json
9 | git add docs
10 | git commit -m 'c2-fed index update'
11 | git push
--------------------------------------------------------------------------------
/docs/.htaccess:
--------------------------------------------------------------------------------
1 | Header set Access-Control-Allow-Origin "*"
2 |
--------------------------------------------------------------------------------
/docs/CNAME:
--------------------------------------------------------------------------------
1 | wiki.c2.com
--------------------------------------------------------------------------------
/docs/c2-fed-index.json:
--------------------------------------------------------------------------------
1 | {
2 | "last": "Sun-1400",
3 | "sites": [
4 | "found.ward.bay.wiki.org",
5 | "code.fed.wiki.org",
6 | "marc.tries.fed.wiki",
7 | "ward.eu.wiki.org",
8 | "forage.rodwell.me",
9 | "thompson.fed.wiki",
10 | "wellspring.fed.wiki",
11 | "ward.dojo.fed.wiki",
12 | "ward.mud.asia.wiki.org"
13 | ],
14 | "slugs": [
15 | "feral-scooters-of-the-future",
16 | "wiki-prayer",
17 | "biota",
18 | "bowling-score-spiked",
19 | "test-point",
20 | "foreign-currency",
21 | "extreme-programming",
22 | "c2-for-me",
23 | "original-wiki-quotes",
24 | "extreme-humility",
25 | "test-page",
26 | "noosphere",
27 | "unbounded-consciousness",
28 | "sudoku-helper",
29 | "grin"
30 | ],
31 | "titles": [
32 | "Feral Scooters of the Future",
33 | "Wiki Prayer",
34 | "Biota",
35 | "Bowling Score Spiked",
36 | "Test Point",
37 | "Foreign Currency",
38 | "Extreme Programming",
39 | "C2 for Me",
40 | "Original Wiki Quotes",
41 | "Extreme Humility",
42 | "Test Page",
43 | "Noosphere",
44 | "Unbounded Consciousness",
45 | "Sudoku Helper",
46 | "grin"
47 | ],
48 | "md5ws": [
49 | "7fd4e713e6400497w",
50 | "579090949e30f10ew",
51 | "f438e1c4056766dcw",
52 | "16d1089a1e5c4d1dw",
53 | "9ef6a558a74e7105w",
54 | "74152aae66076004w",
55 | "2f1cb7ad6d06c735w",
56 | "630cab398719da03w",
57 | "77019e7ec8617643w",
58 | "c6b36d9904f2b624w",
59 | "f31c7d9022a32078w",
60 | "faf579f581acfe8bw",
61 | "0c490445809a506ew",
62 | "699f6519d1948834w",
63 | "9fef03f81900724ew",
64 | "10822d0b9a93a3f8w",
65 | "5f69df4c8757e27ew",
66 | "3cbb265e4dc56d76w",
67 | "ace3f3959e44fcdcw",
68 | "2a057c092336d12fw",
69 | "82d47990f5a4fa77w",
70 | "8a7c581abbad5d8dw",
71 | "7a44158c5b847f1ew",
72 | "9675e824fba64025w",
73 | "b2c2b4e1c4a12aeew"
74 | ],
75 | "pages": [
76 | "BeepSpeek",
77 | "WikiPrayer",
78 | "BiotaLanguage",
79 | "BowlingGameSpikes",
80 | "TestPoint",
81 | "MoneyObject",
82 | "ExtremeProgramming",
83 | "PatternsAsMemes",
84 | "DramaticIdentity",
85 | "WelcomeVisitors",
86 | "MetaMagicalThemas",
87 | "EgolessProgramming",
88 | "SystemsAsLivingThings",
89 | "ExtremeHumility",
90 | "CoCreateInstance",
91 | "NooSphere",
92 | "UnboundedConsciousness",
93 | "SuDoku",
94 | "PeterGervai"
95 | ],
96 | "rows": [
97 | [
98 | 0,
99 | 0,
100 | 0,
101 | 0,
102 | 0
103 | ],
104 | [
105 | 0,
106 | 1,
107 | 1,
108 | 1,
109 | 1
110 | ],
111 | [
112 | 1,
113 | 2,
114 | 2,
115 | 2,
116 | 2
117 | ],
118 | [
119 | 1,
120 | 3,
121 | 3,
122 | 3,
123 | 3
124 | ],
125 | [
126 | 1,
127 | 4,
128 | 4,
129 | 4,
130 | 4
131 | ],
132 | [
133 | 1,
134 | 5,
135 | 5,
136 | 5,
137 | 5
138 | ],
139 | [
140 | 0,
141 | 6,
142 | 6,
143 | 6,
144 | 6
145 | ],
146 | [
147 | 0,
148 | 6,
149 | 6,
150 | 7,
151 | 6
152 | ],
153 | [
154 | 2,
155 | 7,
156 | 7,
157 | 8,
158 | 7
159 | ],
160 | [
161 | 2,
162 | 7,
163 | 7,
164 | 9,
165 | 8
166 | ],
167 | [
168 | 2,
169 | 7,
170 | 7,
171 | 10,
172 | 9
173 | ],
174 | [
175 | 2,
176 | 7,
177 | 7,
178 | 11,
179 | 10
180 | ],
181 | [
182 | 3,
183 | 7,
184 | 7,
185 | 10,
186 | 9
187 | ],
188 | [
189 | 4,
190 | 8,
191 | 8,
192 | 12,
193 | 11
194 | ],
195 | [
196 | 4,
197 | 8,
198 | 8,
199 | 13,
200 | 12
201 | ],
202 | [
203 | 4,
204 | 8,
205 | 8,
206 | 14,
207 | 12
208 | ],
209 | [
210 | 4,
211 | 9,
212 | 9,
213 | 15,
214 | 13
215 | ],
216 | [
217 | 4,
218 | 9,
219 | 9,
220 | 16,
221 | 13
222 | ],
223 | [
224 | 5,
225 | 10,
226 | 10,
227 | 17,
228 | 14
229 | ],
230 | [
231 | 6,
232 | 11,
233 | 11,
234 | 18,
235 | 15
236 | ],
237 | [
238 | 6,
239 | 12,
240 | 12,
241 | 19,
242 | 16
243 | ],
244 | [
245 | 7,
246 | 13,
247 | 13,
248 | 20,
249 | 17
250 | ],
251 | [
252 | 7,
253 | 13,
254 | 13,
255 | 21,
256 | 17
257 | ],
258 | [
259 | 8,
260 | 14,
261 | 14,
262 | 22,
263 | 18
264 | ],
265 | [
266 | 8,
267 | 14,
268 | 14,
269 | 23,
270 | 18
271 | ],
272 | [
273 | 8,
274 | 14,
275 | 14,
276 | 24,
277 | 18
278 | ]
279 | ]
280 | }
281 |
--------------------------------------------------------------------------------
/docs/c2-fed-roster.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "C2 Fed Roster",
3 | "story": [
4 | {
5 | "type": "paragraph",
6 | "text": "Sites with wiki.c2.com excerpts found Sun-1400.",
7 | "id": "84823980980282"
8 | },
9 | {
10 | "type": "roster",
11 | "text": "found.ward.bay.wiki.org\ncode.fed.wiki.org\nmarc.tries.fed.wiki\nward.eu.wiki.org\nforage.rodwell.me\nthompson.fed.wiki\nwellspring.fed.wiki\nward.dojo.fed.wiki\nward.mud.asia.wiki.org",
12 | "id": "23984712394812"
13 | }
14 | ],
15 | "journal": [
16 | {
17 | "type": "create",
18 | "date": 1644162867991,
19 | "item": {
20 | "title": "C2 Fed Roster",
21 | "story": [
22 | {
23 | "type": "paragraph",
24 | "text": "Sites with wiki.c2.com excerpts found Sun-1400.",
25 | "id": "84823980980282"
26 | },
27 | {
28 | "type": "roster",
29 | "text": "found.ward.bay.wiki.org\ncode.fed.wiki.org\nmarc.tries.fed.wiki\nward.eu.wiki.org\nforage.rodwell.me\nthompson.fed.wiki\nwellspring.fed.wiki\nward.dojo.fed.wiki\nward.mud.asia.wiki.org",
30 | "id": "23984712394812"
31 | }
32 | ]
33 | }
34 | }
35 | ]
36 | }
37 |
--------------------------------------------------------------------------------
/docs/compare.html:
--------------------------------------------------------------------------------
1 |
5 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/docs/favicon.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WardCunningham/remodeling/ba7509b028e8e2bd734e92a910e95b5f9b37e6e8/docs/favicon.gif
--------------------------------------------------------------------------------
/docs/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WardCunningham/remodeling/ba7509b028e8e2bd734e92a910e95b5f9b37e6e8/docs/favicon.png
--------------------------------------------------------------------------------
/docs/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
19 |
20 |
21 |

22 |
This site uses features not available in older browsers.
23 |
24 |
25 |
275 |
276 |
277 |
--------------------------------------------------------------------------------
/docs/markup.js:
--------------------------------------------------------------------------------
1 | export {markup}
2 |
3 | var names
4 | var options
5 |
6 | function markup (text,opt) {
7 | options = opt
8 | names = options.names
9 | var lines = text.replace(/\\\n/,' ').split(/\n/)
10 | var expand = lines.map(render).join("\n")
11 | return expand + complete('')
12 | }
13 |
14 | function render (text) {
15 | return links(text, inner)
16 | }
17 |
18 | function escape (text) {
19 | return text
20 | .replace(/&/g, '&')
21 | .replace(//g, '>')
23 | .replace(/"/g, '"')
24 | }
25 | function emphasis (text) {
26 | return text
27 | .replace(/'''(.*?)'''/g, '$1<\/strong>')
28 | .replace(/''(.*?)''/g, '$1<\/em>')
29 | .replace(/^-----*/, '
')
30 | }
31 |
32 | var code = ''
33 | var codes = []
34 | function enter(key, replacement) {
35 | // $code = $key;
36 | // while (@code > $depth) {local($_) = pop @code; print "$_>\n"}
37 | // while (@code < $depth) {push (@code, $key); print "<$key>\n"}
38 | // if ($code[$#code] ne $key) {
39 | // print "$code[$#code]><$key>\n";
40 | // $code[$#code] = $key;
41 | // }
42 | return function (match, p1, p2) {
43 | var depth = p1.length || 0
44 | var adjust = ''
45 | code = key
46 | if (code != '...') {
47 | while (codes.length > depth) { adjust += "" + codes.pop() + ">" }
48 | while (codes.length < depth) { adjust += "<" + key + ">"; codes.push(key) }
49 | if (codes.length && codes[codes.length-1] != key) {
50 | adjust += "" + codes[codes.length-1] + "><" + key + ">"
51 | codes[codes.length-1] = key
52 | }
53 | }
54 | return adjust + replacement.replace(/{p1}/, p1).replace(/{p2}/, p2)
55 | }
56 | }
57 | function complete (text) {
58 | return enter("", "")("","","") + text
59 | }
60 | function bullets (text) {
61 | // $code = "";
62 | // s/^\s*$/<\/p>/ && ($code = '...');
63 | // s/^(\t+)(.+):\t/
$2/ && &enter('DL', length $1);
64 | // s/^(\t+)\*// && &enter('UL', length $1);
65 | // s/^(\*+)// && &enter('UL', length $1);
66 | // s/^(\t+)\d+\.?// && &enter('OL', length $1);
67 | // /^\s/ && &enter('PRE', 1);
68 | code = ''
69 | let result = text
70 | .replace(/^\s*$/, enter('...', '' ) )
71 | .replace(/^(\t+)(.+):\t/, enter('DL', '{p2}') )
72 | .replace(/^(\t+)\*/, enter('UL', '') )
73 | .replace(/^(\*+)/, enter('UL', '') )
74 | .replace(/^(\t+)\d+\.?/, enter('OL', '') )
75 | .replace(/^(\s)/, enter('PRE', '{p1}') )
76 | return result
77 | }
78 | function links (text, sanitize) {
79 | // link conversion happens in four phases:
80 | // unexpected markers are adulterated
81 | // links are found, converted, and stashed away properly escaped
82 | // remaining text is processed and escaped
83 | // unique markers are replaced with unstashed links
84 | var stashed = []
85 | function stash (text) {
86 | var here = stashed.length
87 | stashed.push(text)
88 | return "〖" + here + "〗"
89 | }
90 | function unstash (match, digits) {
91 | return stashed[+digits]
92 | }
93 | function internal (title) {
94 | if (names && names.indexOf(title)!=-1) {
95 | return stash(`${title}`)
96 | } else {
97 | return title
98 | }
99 | }
100 | function external (url) {
101 | if (url.match(/\.(gif|jpg|jpeg|png)$/)) {
102 | return stash("
")
103 | } else {
104 | return stash("" + url + "")
105 | }
106 | }
107 | function youtube (match, p1, p2) {
108 | var embed =
109 | ""
115 | return stash(embed)
116 | }
117 | function isbn (match, isbn) {
118 | var code = isbn.replace(/[- ]/g, "")
119 | if (code.match(/^\d{9}.$/)) {
120 | return "ISBN " + isbn
121 | } else {
122 | return "ISBN " + isbn
123 | }
124 | }
125 | function titlesearch () {
126 | return stash(options.titlesearch())
127 | }
128 | function fullsearch () {
129 | return stash(options.fullsearch())
130 | }
131 | var prepass = text
132 | .replace(/〖(\d+)〗/g, '〖 $1 〗')
133 | .replace(/^https:\/\/(www.)?youtube.com\/watch\?v=([-\w]+)/, youtube)
134 | .replace(/\[Search\]/, titlesearch)
135 | .replace(/\[Fullsearch\]/, fullsearch)
136 | .replace(/\[?ISBN:? *([0-9- xX]{10,})\]?/i, isbn)
137 | .replace(/\b(https?|ftp|mailto|file|telnet|news):[^\s<>\[\]"'\(\)]*[^\s<>\[\]"'\(\)\,\.\?]/g,external)
138 | .replace(/\b[A-Z][a-z]+([A-Z][a-z]+)+\b/g, internal)
139 | var postpass = sanitize(prepass)
140 | .replace(/〖(\d+)〗/g, unstash)
141 | if (code == '') {
142 | postpass = complete(postpass)
143 | }
144 | return postpass
145 | }
146 | function inner (text) {
147 | text = escape(text)
148 | text = bullets(text)
149 | text = emphasis(text)
150 | return text
151 | }
152 |
--------------------------------------------------------------------------------
/docs/md5.js:
--------------------------------------------------------------------------------
1 | /*
2 | * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
3 | * Digest Algorithm, as defined in RFC 1321.
4 | * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
5 | * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
6 | * Distributed under the BSD License
7 | * See http://pajhome.org.uk/crypt/md5 for more info.
8 | */
9 |
10 | /*
11 | * Configurable variables. You may need to tweak these to be compatible with
12 | * the server-side, but the defaults work in most cases.
13 | */
14 | var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
15 | var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
16 |
17 | /*
18 | * These are the functions you'll usually want to call
19 | * They take string arguments and return either hex or base-64 encoded strings
20 | */
21 |
22 | export function hex_md5(s) { return rstr2hex(rstr_md5(str2rstr_utf8(s))); }
23 | export function b64_md5(s) { return rstr2b64(rstr_md5(str2rstr_utf8(s))); }
24 | export function any_md5(s, e) { return rstr2any(rstr_md5(str2rstr_utf8(s)), e); }
25 | export function hex_hmac_md5(k, d)
26 | { return rstr2hex(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); }
27 | export function b64_hmac_md5(k, d)
28 | { return rstr2b64(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); }
29 | export function any_hmac_md5(k, d, e)
30 | { return rstr2any(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d)), e); }
31 |
32 | /*
33 | * Perform a simple self-test to see if the VM is working
34 | */
35 | export function md5_vm_test()
36 | {
37 | return hex_md5("abc").toLowerCase() == "900150983cd24fb0d6963f7d28e17f72";
38 | }
39 |
40 | /*
41 | * Calculate the MD5 of a raw string
42 | */
43 | function rstr_md5(s)
44 | {
45 | return binl2rstr(binl_md5(rstr2binl(s), s.length * 8));
46 | }
47 |
48 | /*
49 | * Calculate the HMAC-MD5, of a key and some data (raw strings)
50 | */
51 | function rstr_hmac_md5(key, data)
52 | {
53 | var bkey = rstr2binl(key);
54 | if(bkey.length > 16) bkey = binl_md5(bkey, key.length * 8);
55 |
56 | var ipad = Array(16), opad = Array(16);
57 | for(var i = 0; i < 16; i++)
58 | {
59 | ipad[i] = bkey[i] ^ 0x36363636;
60 | opad[i] = bkey[i] ^ 0x5C5C5C5C;
61 | }
62 |
63 | var hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
64 | return binl2rstr(binl_md5(opad.concat(hash), 512 + 128));
65 | }
66 |
67 | /*
68 | * Convert a raw string to a hex string
69 | */
70 | function rstr2hex(input)
71 | {
72 | try { hexcase } catch(e) { hexcase=0; }
73 | var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
74 | var output = "";
75 | var x;
76 | for(var i = 0; i < input.length; i++)
77 | {
78 | x = input.charCodeAt(i);
79 | output += hex_tab.charAt((x >>> 4) & 0x0F)
80 | + hex_tab.charAt( x & 0x0F);
81 | }
82 | return output;
83 | }
84 |
85 | /*
86 | * Convert a raw string to a base-64 string
87 | */
88 | function rstr2b64(input)
89 | {
90 | try { b64pad } catch(e) { b64pad=''; }
91 | var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
92 | var output = "";
93 | var len = input.length;
94 | for(var i = 0; i < len; i += 3)
95 | {
96 | var triplet = (input.charCodeAt(i) << 16)
97 | | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
98 | | (i + 2 < len ? input.charCodeAt(i+2) : 0);
99 | for(var j = 0; j < 4; j++)
100 | {
101 | if(i * 8 + j * 6 > input.length * 8) output += b64pad;
102 | else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
103 | }
104 | }
105 | return output;
106 | }
107 |
108 | /*
109 | * Convert a raw string to an arbitrary string encoding
110 | */
111 | function rstr2any(input, encoding)
112 | {
113 | var divisor = encoding.length;
114 | var i, j, q, x, quotient;
115 |
116 | /* Convert to an array of 16-bit big-endian values, forming the dividend */
117 | var dividend = Array(Math.ceil(input.length / 2));
118 | for(i = 0; i < dividend.length; i++)
119 | {
120 | dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
121 | }
122 |
123 | /*
124 | * Repeatedly perform a long division. The binary array forms the dividend,
125 | * the length of the encoding is the divisor. Once computed, the quotient
126 | * forms the dividend for the next step. All remainders are stored for later
127 | * use.
128 | */
129 | var full_length = Math.ceil(input.length * 8 /
130 | (Math.log(encoding.length) / Math.log(2)));
131 | var remainders = Array(full_length);
132 | for(j = 0; j < full_length; j++)
133 | {
134 | quotient = Array();
135 | x = 0;
136 | for(i = 0; i < dividend.length; i++)
137 | {
138 | x = (x << 16) + dividend[i];
139 | q = Math.floor(x / divisor);
140 | x -= q * divisor;
141 | if(quotient.length > 0 || q > 0)
142 | quotient[quotient.length] = q;
143 | }
144 | remainders[j] = x;
145 | dividend = quotient;
146 | }
147 |
148 | /* Convert the remainders to the output string */
149 | var output = "";
150 | for(i = remainders.length - 1; i >= 0; i--)
151 | output += encoding.charAt(remainders[i]);
152 |
153 | return output;
154 | }
155 |
156 | /*
157 | * Encode a string as utf-8.
158 | * For efficiency, this assumes the input is valid utf-16.
159 | */
160 | function str2rstr_utf8(input)
161 | {
162 | var output = "";
163 | var i = -1;
164 | var x, y;
165 |
166 | while(++i < input.length)
167 | {
168 | /* Decode utf-16 surrogate pairs */
169 | x = input.charCodeAt(i);
170 | y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
171 | if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF)
172 | {
173 | x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
174 | i++;
175 | }
176 |
177 | /* Encode output as utf-8 */
178 | if(x <= 0x7F)
179 | output += String.fromCharCode(x);
180 | else if(x <= 0x7FF)
181 | output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
182 | 0x80 | ( x & 0x3F));
183 | else if(x <= 0xFFFF)
184 | output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
185 | 0x80 | ((x >>> 6 ) & 0x3F),
186 | 0x80 | ( x & 0x3F));
187 | else if(x <= 0x1FFFFF)
188 | output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
189 | 0x80 | ((x >>> 12) & 0x3F),
190 | 0x80 | ((x >>> 6 ) & 0x3F),
191 | 0x80 | ( x & 0x3F));
192 | }
193 | return output;
194 | }
195 |
196 | /*
197 | * Encode a string as utf-16
198 | */
199 | function str2rstr_utf16le(input)
200 | {
201 | var output = "";
202 | for(var i = 0; i < input.length; i++)
203 | output += String.fromCharCode( input.charCodeAt(i) & 0xFF,
204 | (input.charCodeAt(i) >>> 8) & 0xFF);
205 | return output;
206 | }
207 |
208 | function str2rstr_utf16be(input)
209 | {
210 | var output = "";
211 | for(var i = 0; i < input.length; i++)
212 | output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
213 | input.charCodeAt(i) & 0xFF);
214 | return output;
215 | }
216 |
217 | /*
218 | * Convert a raw string to an array of little-endian words
219 | * Characters >255 have their high-byte silently ignored.
220 | */
221 | function rstr2binl(input)
222 | {
223 | var output = Array(input.length >> 2);
224 | for(var i = 0; i < output.length; i++)
225 | output[i] = 0;
226 | for(var i = 0; i < input.length * 8; i += 8)
227 | output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (i%32);
228 | return output;
229 | }
230 |
231 | /*
232 | * Convert an array of little-endian words to a string
233 | */
234 | function binl2rstr(input)
235 | {
236 | var output = "";
237 | for(var i = 0; i < input.length * 32; i += 8)
238 | output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
239 | return output;
240 | }
241 |
242 | /*
243 | * Calculate the MD5 of an array of little-endian words, and a bit length.
244 | */
245 | function binl_md5(x, len)
246 | {
247 | /* append padding */
248 | x[len >> 5] |= 0x80 << ((len) % 32);
249 | x[(((len + 64) >>> 9) << 4) + 14] = len;
250 |
251 | var a = 1732584193;
252 | var b = -271733879;
253 | var c = -1732584194;
254 | var d = 271733878;
255 |
256 | for(var i = 0; i < x.length; i += 16)
257 | {
258 | var olda = a;
259 | var oldb = b;
260 | var oldc = c;
261 | var oldd = d;
262 |
263 | a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
264 | d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
265 | c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);
266 | b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
267 | a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
268 | d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);
269 | c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
270 | b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
271 | a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
272 | d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
273 | c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
274 | b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
275 | a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);
276 | d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
277 | c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
278 | b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);
279 |
280 | a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
281 | d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
282 | c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);
283 | b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
284 | a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
285 | d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);
286 | c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
287 | b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
288 | a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);
289 | d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
290 | c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
291 | b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);
292 | a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
293 | d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
294 | c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);
295 | b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
296 |
297 | a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
298 | d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
299 | c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);
300 | b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
301 | a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
302 | d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);
303 | c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
304 | b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
305 | a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);
306 | d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
307 | c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
308 | b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);
309 | a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
310 | d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
311 | c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);
312 | b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
313 |
314 | a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
315 | d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);
316 | c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
317 | b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
318 | a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);
319 | d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
320 | c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
321 | b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
322 | a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
323 | d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
324 | c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
325 | b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);
326 | a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
327 | d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
328 | c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);
329 | b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
330 |
331 | a = safe_add(a, olda);
332 | b = safe_add(b, oldb);
333 | c = safe_add(c, oldc);
334 | d = safe_add(d, oldd);
335 | }
336 | return Array(a, b, c, d);
337 | }
338 |
339 | /*
340 | * These functions implement the four basic operations the algorithm uses.
341 | */
342 | function md5_cmn(q, a, b, x, s, t)
343 | {
344 | return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
345 | }
346 | function md5_ff(a, b, c, d, x, s, t)
347 | {
348 | return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
349 | }
350 | function md5_gg(a, b, c, d, x, s, t)
351 | {
352 | return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
353 | }
354 | function md5_hh(a, b, c, d, x, s, t)
355 | {
356 | return md5_cmn(b ^ c ^ d, a, b, x, s, t);
357 | }
358 | function md5_ii(a, b, c, d, x, s, t)
359 | {
360 | return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
361 | }
362 |
363 | /*
364 | * Add integers, wrapping at 2^32. This uses 16-bit operations internally
365 | * to work around bugs in some JS interpreters.
366 | */
367 | function safe_add(x, y)
368 | {
369 | var lsw = (x & 0xFFFF) + (y & 0xFFFF);
370 | var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
371 | return (msw << 16) | (lsw & 0xFFFF);
372 | }
373 |
374 | /*
375 | * Bitwise rotate a 32-bit number to the left.
376 | */
377 | function bit_rol(num, cnt)
378 | {
379 | return (num << cnt) | (num >>> (32 - cnt));
380 | }
--------------------------------------------------------------------------------
/docs/spin.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WardCunningham/remodeling/ba7509b028e8e2bd734e92a910e95b5f9b37e6e8/docs/spin.gif
--------------------------------------------------------------------------------
/docs/style.css:
--------------------------------------------------------------------------------
1 | .page {
2 | width: 500px;
3 | margin: 15px;
4 | padding: 15px;
5 | word-wrap: break-word;
6 | box-shadow: 10px 10px 30px 10px rgba(0,0,0,.1);
7 | background-color: white;
8 | }
9 | h1 {
10 | display: flex;
11 | align-items: baseline;
12 | }
13 | h1 span {
14 | display: inline-block;
15 | margin: 0 0 0 10px;
16 | }
17 | img {
18 | max-width: 500px;
19 | }
20 | pre {
21 | word-wrap: break-word;
22 | white-space: pre-wrap;
23 | }
24 | a {
25 | text-decoration: none;
26 | }
27 | input {
28 | width: 60%;
29 | }
30 | ul, ol {
31 | padding-left: 1em;
32 | }
33 | dd {
34 | margin: 1em;
35 | }
36 |
--------------------------------------------------------------------------------
/docs/wiki.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/WardCunningham/remodeling/ba7509b028e8e2bd734e92a910e95b5f9b37e6e8/docs/wiki.gif
--------------------------------------------------------------------------------
/encode-links.js:
--------------------------------------------------------------------------------
1 | // Encode links.txt into links.json compainion to docs/names.txt
2 | // Usage: deno run --allow-read encode-links.js >docs/inlinks.json
3 |
4 | let names = Deno.readTextFileSync('docs/names.txt').split(/\r?\n/)
5 | let outbounds = Deno.readTextFileSync('links.txt').split(/\r?\n/)
6 |
7 | let inbounds = {}
8 | for (let outbound of outbounds) {
9 | let [key, ...links] = outbound.split(' ')
10 | let index = names.indexOf(key)
11 | if (index < 0) {console.log('missing',key); continue}
12 | for (let link of links) {
13 | let inbound = inbounds[link] ||= []
14 | inbound.push(index)
15 | }
16 | }
17 | console.log(JSON.stringify(names.map(name => inbounds[name]||[])))
--------------------------------------------------------------------------------
/json.c:
--------------------------------------------------------------------------------
1 | int main()
2 | {
3 | int ch;
4 | int getchar();
5 | int putchar();
6 | while ((ch=getchar())>0) {
7 | if (ch==0263) {
8 | putchar('<');
9 | putchar('<');
10 | putchar('<');
11 | putchar('<');
12 | putchar('g');
13 | putchar('s');
14 | putchar('>');
15 | putchar('>');
16 | putchar('>');
17 | putchar('>');
18 | } else {
19 | putchar(ch);
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/json.rb:
--------------------------------------------------------------------------------
1 | # convert traditional format to json objects
2 | # usage: ruby json.rb
3 |
4 | require 'json'
5 |
6 | Encoding.default_external = Encoding::UTF_8
7 |
8 | def get file
9 | raw = `cat #{file} | ./a.out`
10 | it = Hash[raw.split(/<<<>>>/).each_slice(2).to_a]
11 | {date:it['date'], text:it['text']}
12 | rescue Exception => e
13 | {}
14 | end
15 |
16 | Dir.glob('wiki.wdb/*') do |file|
17 | File.open(file.gsub(/wiki.wdb/, 'static/pages'),'w') do |output|
18 | output.puts JSON.pretty_generate(get(file))
19 | end
20 | print '.'
21 | end
--------------------------------------------------------------------------------
/sync-gh-pages.sh:
--------------------------------------------------------------------------------
1 | # update gh-pages from static subtree
2 | # https://gist.github.com/cobyism/4730490
3 | # usage: git push; sh sync-gh-pages.sh
4 |
5 | git subtree push --prefix static origin gh-pages
6 |
--------------------------------------------------------------------------------
/sync.sh:
--------------------------------------------------------------------------------
1 | # publish workspace changes to public remodel testbed
2 | # usage: sh sync.sh
3 |
4 | rsync -az static/ c2.com:web/wiki/remodel/
5 |
--------------------------------------------------------------------------------