From b8ff8a90466cf4d5fb9f63b6fe61373262259a8f Mon Sep 17 00:00:00 2001 From: Chris Johnson Date: Wed, 5 Aug 2020 08:47:36 +0100 Subject: [PATCH] First version --- github-game-of-life.js | 121 +++++++++++++++++++++++++++++++++++++++++ icon128.png | Bin 0 -> 1326 bytes icon16.png | Bin 0 -> 316 bytes icon48.png | Bin 0 -> 545 bytes manifest.json | 21 +++++++ 5 files changed, 142 insertions(+) create mode 100644 github-game-of-life.js create mode 100644 icon128.png create mode 100644 icon16.png create mode 100644 icon48.png create mode 100644 manifest.json diff --git a/github-game-of-life.js b/github-game-of-life.js new file mode 100644 index 0000000..e787b00 --- /dev/null +++ b/github-game-of-life.js @@ -0,0 +1,121 @@ +// GitHub Game of Life - 1.0.1 +// Chris Johnson +// @defaced / defaced.dev / github.com/workeffortwaste +(function () { + const startGgol = () => { + // Settings + const cols = 53 + const rows = 7 + + // Pull down the contributions from the graph. + const contributions = document.getElementsByClassName('js-calendar-graph-svg')[0].children[0].getElementsByTagName('g') + + // Read contributions as columns from the contribution frame. + const readContributions = (contributions) => { + // Init output Arr. + const output = [] + + for (const contribution of contributions) { + const days = contribution.getElementsByTagName('rect') + const lifeRow = [] + for (const day of days) { + // Convert to live or dead cells. + lifeRow.push(day.getAttribute('data-count') > 0 ? 1 : 0) + } + // Add column to output. + output.push(lifeRow) + } + + // Fill in any blanks in the first and last rows. + for (let i = 0; i < 7; i++) { + if (!output[0][i]) { + output[0][i] = 0 + } + if (!output[output.length - 1][i]) { + output[output.length - 1][i] = 0 + } + } + + return output + } + + // Our initial game of life state. + let lifeArray = readContributions(contributions) + + // Write to the contributions graphic. + const writeContributions = (contributions, nextGenerationArray) => { + for (let i = 0; i < contributions.length; i++) { + const days = contributions[i].getElementsByTagName('rect') + for (let d = 0; d < 7; d++) { // Magic number - 7 days of the week. + try { days[d].setAttribute('fill', nextGenerationArray[i][d] ? '#40c463' : '#ebedf0') } catch {} + } + } + } + + const countNeighbors = (grid, x, y) => { + let sum = 0 + + for (let i = -1; i < 2; i++) { + for (let j = -1; j < 2; j++) { + const col = (x + i + cols) % cols + const row = (y + j + rows) % rows + sum += grid[col][row] + } + } + + sum -= grid[x][y] + return sum + } + + const generateNextIteration = (grid) => { + // Make empty array + const next = [...Array(53)].map(e => Array(7)) + + for (let i = 0; i < cols; i++) { + for (let j = 0; j < rows; j++) { + const state = grid[i][j] + const neighbors = countNeighbors(grid, i, j) + + if (state === 0 && neighbors === 3) { + next[i][j] = 1 + } else if (state === 1 && (neighbors < 2 || neighbors > 3)) { + next[i][j] = 0 + } else { + next[i][j] = state + } + } + } + + return next + } + + const update = () => { + // Generate the next iteration. + const next = generateNextIteration(lifeArray) + + // If the output is the same as the input we've reached a stable state so kill the interval. + if (JSON.stringify(next) === JSON.stringify(lifeArray)) { + clearInterval(timer) + } + + // Update the lifeArray with the next generation. + lifeArray = next + writeContributions(contributions, lifeArray) + } + + // Update every 500 ms. + const timer = setInterval(function () { update() }, 500) + } + + const observerGgol = (entry) => { + if (entry[0].intersectionRatio > 0) { + // Kill the observer. + observer.disconnect() + // Start the game of life. + startGgol() + } + } + + const observer = new IntersectionObserver(observerGgol) + try { observer.observe(document.getElementsByClassName('js-calendar-graph-svg')[0]) } catch {} +})() diff --git a/icon128.png b/icon128.png new file mode 100644 index 0000000000000000000000000000000000000000..3b73333765bde467c3e6dd7799f90116cd07a394 GIT binary patch literal 1326 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSoCO|{#S9Eg=Ruh9(xNTQ3=Av} zJY5_^D(1YM>zH@TL8A44*j;JGZwt*6=DR*%D_~YijqhXn?eUcDDO;oKw>t|G^A(@5 ztkn6dcw?TVu;$l4=c?~-+Ir>s%=|Z!-gPzAwY8#6r_0aTnu##Ta50?gzfo^*ziVNK zkL!}_pA7bPoJhNH-p^ND?My^Ubo|VX%O%_2x>y%qDy-p*6}ZFl%zeAa#Q6W#i7e_$ zpB?RQx9FYh&(l+KJ*Sg+?C65|Yn{cPxQF}vu}VDFbalae-J1G`q6ZEDC2_WC#dd2vkX(-ed2s&SlIOfPi5<~KDc@-^3m?J9kx`&IShs$ENV2=lD& zZ~pV5tH=E0<-MOb|FZay%d}j#J?oL0{jR{LvnDP)DSkHS)QXw9zWaVWyQc1Nd-lrg z6=_n^XClsCX)SzWC?Lzlz{kqa&eY({cwiyJg8+sC4F(Iar%4m`3un#vqh`-2W^DRM zSRq1f!i214x1K*@TaotakVO^ijTg_)h203X&y@SJx%T7D9SeKd&z86uzrS=$>tRWr z_Z5~4`FEb5Oqy0WQ72e=(XJEfUyGf)GvBS={P7**mT9SzDyE9AP8WH#{i3M3-=0%l z_0=)4fAcfG1wQIw&yD;T_SQtqHS40|Yt_JwNsF~^WnC&`3>G(<=Tx-s;Ie>%A9A2b z^a~D5@i#ZjE&C*5eKq&;=GT)>em-pBn1A8h%WofFeHYv{U26ND`C6aeCZ|r@xahT}yf^f%S76FlRxj}`rQ3Bci+OdrR8q-{46IQ%pFvDoKT_nUWlX4W z`aX@I*_J+{u3@*Gdc<7KzB6ZhaLwNMXorWGw&YU9#Z!-N`QZ_ml6b22(TDa3&c$X$ zXN2x^@+~YA(3~E*?92{>xWCdWFnUfK?WP^^?vFm!>=lb*3 zJU_vXwst`lwVOD}dwnW!VJ%sl&NU6*U5^sAVd`k9M7V*f7dQZl_Px!85$ z*7@5$awcqiFu`@=d}rO@@cu@z+djH!SuO7=OUuZ?SF%)N)2x-;t`lP=jwb{zQRSPL z|Ni^2Q#y0gtQODIof_Gdp9f3_tFI}U8h=l*iPY}m3E6mclX}Wi?OPn8u6jHk54K!A zRK43gC{0o~@yZ)x=JM53&t>*5TXbUKG_EaiLZNroC9$^WXS_aY<9JNz_Ah41<(giL qN@SXCy>+@++K)Rx@}s2SPj=3pP_Mx1tHoUQ&+lQuoeEPs=6vaX zo!Qb-vE6Rx>+g3jXP>+8s@A|L*U6y6viJ9R{n4|?BVh~NzeX=M|wU9+`n1f;_A+o6B}>xx^R2(s_{Pqg$jeG LtDnm{r-UW|q8Wt# literal 0 HcmV?d00001 diff --git a/icon48.png b/icon48.png new file mode 100644 index 0000000000000000000000000000000000000000..6acf9379097037104806fdfc4ee628e7981acc1a GIT binary patch literal 545 zcmV++0^a?JP)Y?Y`q>vJ^^+ z+R~#onW$})6uG6H13)%WyQi@m{4iVrGLL2iBLK2gQdDiH3jkO#wWX3GYdf6+z?P}i z05Hc8Gx{QPFps3P<(juO>BxSk5aw<%0UjIdwUVMBEmn~=)4)bau_rxtu{lFqVTlAgK&aUB0d6J6 zTOh-;xcq#@X1HNATCBtC?bBOMR}aTtFvcFLIGEo6O-8ttcwyZ(9P>c?q6X*~9P`ME zJRg37X_By&Bu`1p3tSdP4nT>Qj4P jFMA;Qua`Z71pvMQTdJxjwh5Xk00000NkvXXu0mjfev#=e literal 0 HcmV?d00001 diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..c4f9b08 --- /dev/null +++ b/manifest.json @@ -0,0 +1,21 @@ +{ + "name": "GitHub Game of Life", + "description": "Replace the GitHub contributions graph with Conway's Game of Life.", + "version": "1.0.1", + "icons": { + "16": "icon16.png", + "48": "icon48.png", + "128": "icon128.png" + }, + "content_scripts": [ + { + "matches": [ + "https://github.com/*" + ], + "exclude_globs": ["https://github.com/*/*"], + "js": ["github-game-of-life.js"], + "run_at": "document_end" + } + ], + "manifest_version": 2 +}