├── .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 | 2 | 3 | 4 |
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 "<$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 += "" } 48 | while (codes.length < depth) { adjust += "<" + key + ">"; codes.push(key) } 49 | if (codes.length && codes[codes.length-1] != key) { 50 | adjust += "<" + 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 | "" + 110 | "" + 111 | "" + 112 | "" + 113 | "" + 114 | "" 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 | --------------------------------------------------------------------------------