Compare commits
No commits in common. 'docs' and 'heroku-pages' have entirely different histories.
docs
...
heroku-pag
@ -1,8 +1 @@
@@ -1,8 +1 @@
|
||||
# ignore mkdocs output/gh-pages branch |
||||
site/ |
||||
|
||||
# ignore secret branch |
||||
secret/ |
||||
|
||||
# ignore any makefiles |
||||
Makefile |
||||
vp/ |
||||
|
@ -1,3 +0,0 @@
@@ -1,3 +0,0 @@
|
||||
[submodule "mkdocs-material"] |
||||
path = mkdocs-material |
||||
url = https://git.charlesreid1.com/charlesreid1/mkdocs-material.git |
@ -1,13 +0,0 @@
@@ -1,13 +0,0 @@
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE |
||||
Version 458.835.16.92, May 2018 |
||||
|
||||
Copyright (C) 2018 Charles Reid <charles@charlesreid1.com> |
||||
|
||||
Everyone is permitted to copy and distribute verbatim or modified |
||||
copies of this license document, and changing it is allowed as long |
||||
as the name is changed. |
||||
|
||||
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE |
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION |
||||
|
||||
0. You just DO WHAT THE FUCK YOU WANT TO. |
@ -1,58 +0,0 @@
@@ -1,58 +0,0 @@
|
||||
# github-heroku-attack-rabbits |
||||
|
||||
## What's this business all about, then? |
||||
|
||||
This repository helps you protect your secret pages by (deep breath): |
||||
|
||||
hosting your secret page of static and/or dynamic content using a free Heroku app |
||||
running a Python Flask server that uses Flask-Dance to authenticate visitors |
||||
with Github which allows you fine-grained access control over your pages based on |
||||
user attributes like organization or team membership or even things like how many |
||||
repositories a user has or how many vowels are in their username. |
||||
|
||||
Also, did I mention the attack rabbits? |
||||
|
||||
![warning: attack rabbits ahead](docs/img/warning.png) |
||||
|
||||
|
||||
## Where is everything? |
||||
|
||||
Final pages: |
||||
|
||||
* The finished product (pages on Heroku protected by attack rabbits) |
||||
is at [github-heroku-attack-rabbits.herokuapp.com](https://github-heroku-attack-rabbits.herokuapp.com) |
||||
|
||||
* The documentation is at [pages.charlesreid1.com/github-heroku-attack-rabbits](https://pages.charlesreid1.com/github-heroku-attack-rabbits) |
||||
|
||||
Two branches in this repo compose the github-heroku-attack-rabbits documentation: |
||||
|
||||
* (**YOU ARE HERE**) The [docs](https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits/src/branch/docs) branch |
||||
contains the files needed to generate the |
||||
[github-heroku-attack-rabbits documentation site](https://pages.charlesreid1.com/github-heroku-attack-rabbits). |
||||
|
||||
* The [gh-pages](https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits/src/branch/gh-pages) branch |
||||
contains the static files generated from the documentation. |
||||
The contents of this branch compose the |
||||
[github-heroku-attack-rabbits documentation site](https://pages.charlesreid1.com/github-heroku-attack-rabbits). |
||||
|
||||
Two branches illustrate github-heroku-attack-rabbits in practice: |
||||
|
||||
* The [secret](https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits/src/branch/secret) branch contains the files needed to create the secret page. |
||||
This repository is public, so obviously these aren't *actually* secret, |
||||
but in practice this would be in a protected repository. |
||||
|
||||
* The [heroku-pages](https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits/src/branch/heroku-pages) branch |
||||
contains the content that is actually pushed to Heroku - that is, |
||||
the final Flask app. |
||||
|
||||
|
||||
## Where do I start? |
||||
|
||||
See the [documentation](https://pages.charlesreid1.com/github-heroku-attack-rabbits) |
||||
or [docs/index.md](docs/index.md). |
||||
|
||||
|
||||
## License |
||||
|
||||
This is released under the [WTFPL](LICENSE). |
||||
|
@ -0,0 +1,13 @@
@@ -0,0 +1,13 @@
|
||||
# github-heroku-attack-rabbits: heroku-pages branch |
||||
|
||||
This branch contains the files needed |
||||
for heroku to run a flask oauth server |
||||
to authenticate Github users. |
||||
|
||||
Files: |
||||
|
||||
* `Procfile` - tells heroku what application to run |
||||
* `app.json` - info about the application for heroku |
||||
* `github.py` - github authentication application |
||||
* `requirements.txt` - how to install what with python |
||||
* `runtime.txt` - version of python to use |
@ -0,0 +1,23 @@
@@ -0,0 +1,23 @@
|
||||
{ |
||||
"name": "Github Heroku Attack Rabbits", |
||||
"description": "Protect private pages hosted on Heroku by authenticating with Github using Flask-Dance. Also, attack rabbits.", |
||||
"keywords": [ |
||||
"oauth" |
||||
], |
||||
"website": "https://pages.charlesreid1.com/github-heroku-attack-rabbits", |
||||
"repository": "https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits", |
||||
"env": { |
||||
"FLASK_SECRET_KEY": { |
||||
"description": "A secret key for verifying the integrity of signed cookies.", |
||||
"generator": "secret" |
||||
}, |
||||
"GITHUB_OAUTH_CLIENT_ID": { |
||||
"description": "The OAuth client ID for your application, assigned by GitHub", |
||||
"value": "client-id-goes-here" |
||||
}, |
||||
"GITHUB_OAUTH_CLIENT_SECRET": { |
||||
"description": "The OAuth client secret for your application, assigned by GitHub", |
||||
"value": "client-secret-goes-here" |
||||
} |
||||
} |
||||
} |
After Width: | Height: | Size: 521 B |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.6 KiB |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r,i,n;e.da=function(){this.pipeline.reset(),this.pipeline.add(e.da.trimmer,e.da.stopWordFilter,e.da.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.da.stemmer))},e.da.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.da.trimmer=e.trimmerSupport.generateTrimmer(e.da.wordCharacters),e.Pipeline.registerFunction(e.da.trimmer,"trimmer-da"),e.da.stemmer=(r=e.stemmerSupport.Among,i=e.stemmerSupport.SnowballProgram,n=new function(){var e,n,t,s=[new r("hed",-1,1),new r("ethed",0,1),new r("ered",-1,1),new r("e",-1,1),new r("erede",3,1),new r("ende",3,1),new r("erende",5,1),new r("ene",3,1),new r("erne",3,1),new r("ere",3,1),new r("en",-1,1),new r("heden",10,1),new r("eren",10,1),new r("er",-1,1),new r("heder",13,1),new r("erer",13,1),new r("s",-1,2),new r("heds",16,1),new r("es",16,1),new r("endes",18,1),new r("erendes",19,1),new r("enes",18,1),new r("ernes",18,1),new r("eres",18,1),new r("ens",16,1),new r("hedens",24,1),new r("erens",24,1),new r("ers",16,1),new r("ets",16,1),new r("erets",28,1),new r("et",-1,1),new r("eret",30,1)],o=[new r("gd",-1,-1),new r("dt",-1,-1),new r("gt",-1,-1),new r("kt",-1,-1)],a=[new r("ig",-1,1),new r("lig",0,1),new r("elig",1,1),new r("els",-1,1),new r("løst",-1,2)],d=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,48,0,128],u=[239,254,42,3,0,0,0,0,0,0,0,0,0,0,0,0,16],c=new i;function l(){var e,r=c.limit-c.cursor;c.cursor>=n&&(e=c.limit_backward,c.limit_backward=n,c.ket=c.cursor,c.find_among_b(o,4)?(c.bra=c.cursor,c.limit_backward=e,c.cursor=c.limit-r,c.cursor>c.limit_backward&&(c.cursor--,c.bra=c.cursor,c.slice_del())):c.limit_backward=e)}this.setCurrent=function(e){c.setCurrent(e)},this.getCurrent=function(){return c.getCurrent()},this.stem=function(){var r,i=c.cursor;return function(){var r,i=c.cursor+3;if(n=c.limit,0<=i&&i<=c.limit){for(e=i;;){if(r=c.cursor,c.in_grouping(d,97,248)){c.cursor=r;break}if(c.cursor=r,r>=c.limit)return;c.cursor++}for(;!c.out_grouping(d,97,248);){if(c.cursor>=c.limit)return;c.cursor++}(n=c.cursor)<e&&(n=e)}}(),c.limit_backward=i,c.cursor=c.limit,function(){var e,r;if(c.cursor>=n&&(r=c.limit_backward,c.limit_backward=n,c.ket=c.cursor,e=c.find_among_b(s,32),c.limit_backward=r,e))switch(c.bra=c.cursor,e){case 1:c.slice_del();break;case 2:c.in_grouping_b(u,97,229)&&c.slice_del()}}(),c.cursor=c.limit,l(),c.cursor=c.limit,function(){var e,r,i,t=c.limit-c.cursor;if(c.ket=c.cursor,c.eq_s_b(2,"st")&&(c.bra=c.cursor,c.eq_s_b(2,"ig")&&c.slice_del()),c.cursor=c.limit-t,c.cursor>=n&&(r=c.limit_backward,c.limit_backward=n,c.ket=c.cursor,e=c.find_among_b(a,5),c.limit_backward=r,e))switch(c.bra=c.cursor,e){case 1:c.slice_del(),i=c.limit-c.cursor,l(),c.cursor=c.limit-i;break;case 2:c.slice_from("løs")}}(),c.cursor=c.limit,c.cursor>=n&&(r=c.limit_backward,c.limit_backward=n,c.ket=c.cursor,c.out_grouping_b(d,97,248)?(c.bra=c.cursor,t=c.slice_to(t),c.limit_backward=r,c.eq_v_b(t)&&c.slice_del()):c.limit_backward=r),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}),e.Pipeline.registerFunction(e.da.stemmer,"stemmer-da"),e.da.stopWordFilter=e.generateStopWordFilter("ad af alle alt anden at blev blive bliver da de dem den denne der deres det dette dig din disse dog du efter eller en end er et for fra ham han hans har havde have hende hendes her hos hun hvad hvis hvor i ikke ind jeg jer jo kunne man mange med meget men mig min mine mit mod ned noget nogle nu når og også om op os over på selv sig sin sine sit skal skulle som sådan thi til ud under var vi vil ville vor være været".split(" ")),e.Pipeline.registerFunction(e.da.stopWordFilter,"stopWordFilter-da")}}); |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r="2"==e.version[0];e.jp=function(){this.pipeline.reset(),this.pipeline.add(e.jp.stopWordFilter,e.jp.stemmer),r?this.tokenizer=e.jp.tokenizer:(e.tokenizer&&(e.tokenizer=e.jp.tokenizer),this.tokenizerFn&&(this.tokenizerFn=e.jp.tokenizer))};var t=new e.TinySegmenter;e.jp.tokenizer=function(n){if(!arguments.length||null==n||null==n)return[];if(Array.isArray(n))return n.map(function(t){return r?new e.Token(t.toLowerCase()):t.toLowerCase()});for(var i=n.toString().toLowerCase().replace(/^\s+/,""),o=i.length-1;o>=0;o--)if(/\S/.test(i.charAt(o))){i=i.substring(0,o+1);break}return t.segment(i).filter(function(e){return!!e}).map(function(t){return r?new e.Token(t):t})},e.jp.stemmer=function(e){return e},e.Pipeline.registerFunction(e.jp.stemmer,"stemmer-jp"),e.jp.wordCharacters="一二三四五六七八九十百千万億兆一-龠々〆ヵヶぁ-んァ-ヴーア-ン゙a-zA-Za-zA-Z0-90-9",e.jp.stopWordFilter=function(t){if(-1===e.jp.stopWordFilter.stopWords.indexOf(r?t.toString():t))return t},e.jp.stopWordFilter=e.generateStopWordFilter("これ それ あれ この その あの ここ そこ あそこ こちら どこ だれ なに なん 何 私 貴方 貴方方 我々 私達 あの人 あのかた 彼女 彼 です あります おります います は が の に を で え から まで より も どの と し それで しかし".split(" ")),e.Pipeline.registerFunction(e.jp.stopWordFilter,"stopWordFilter-jp")}}); |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
!function(e,i){"function"==typeof define&&define.amd?define(i):"object"==typeof exports?module.exports=i():i()(e.lunr)}(this,function(){return function(e){e.multiLanguage=function(){for(var i=Array.prototype.slice.call(arguments),t=i.join("-"),r="",n=[],s=[],p=0;p<i.length;++p)"en"==i[p]?(r+="\\w",n.unshift(e.stopWordFilter),n.push(e.stemmer),s.push(e.stemmer)):(r+=e[i[p]].wordCharacters,n.unshift(e[i[p]].stopWordFilter),n.push(e[i[p]].stemmer),s.push(e[i[p]].stemmer));var o=e.trimmerSupport.generateTrimmer(r);return e.Pipeline.registerFunction(o,"lunr-multi-trimmer-"+t),n.unshift(o),function(){this.pipeline.reset(),this.pipeline.add.apply(this.pipeline,n),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add.apply(this.searchPipeline,s))}}}}); |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r,n,i;e.no=function(){this.pipeline.reset(),this.pipeline.add(e.no.trimmer,e.no.stopWordFilter,e.no.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.no.stemmer))},e.no.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.no.trimmer=e.trimmerSupport.generateTrimmer(e.no.wordCharacters),e.Pipeline.registerFunction(e.no.trimmer,"trimmer-no"),e.no.stemmer=(r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,i=new function(){var e,i,t=[new r("a",-1,1),new r("e",-1,1),new r("ede",1,1),new r("ande",1,1),new r("ende",1,1),new r("ane",1,1),new r("ene",1,1),new r("hetene",6,1),new r("erte",1,3),new r("en",-1,1),new r("heten",9,1),new r("ar",-1,1),new r("er",-1,1),new r("heter",12,1),new r("s",-1,2),new r("as",14,1),new r("es",14,1),new r("edes",16,1),new r("endes",16,1),new r("enes",16,1),new r("hetenes",19,1),new r("ens",14,1),new r("hetens",21,1),new r("ers",14,1),new r("ets",14,1),new r("et",-1,1),new r("het",25,1),new r("ert",-1,3),new r("ast",-1,1)],o=[new r("dt",-1,-1),new r("vt",-1,-1)],s=[new r("leg",-1,1),new r("eleg",0,1),new r("ig",-1,1),new r("eig",2,1),new r("lig",2,1),new r("elig",4,1),new r("els",-1,1),new r("lov",-1,1),new r("elov",7,1),new r("slov",7,1),new r("hetslov",9,1)],a=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,48,0,128],m=[119,125,149,1],l=new n;this.setCurrent=function(e){l.setCurrent(e)},this.getCurrent=function(){return l.getCurrent()},this.stem=function(){var r,n,u,d,c=l.cursor;return function(){var r,n=l.cursor+3;if(i=l.limit,0<=n||n<=l.limit){for(e=n;;){if(r=l.cursor,l.in_grouping(a,97,248)){l.cursor=r;break}if(r>=l.limit)return;l.cursor=r+1}for(;!l.out_grouping(a,97,248);){if(l.cursor>=l.limit)return;l.cursor++}(i=l.cursor)<e&&(i=e)}}(),l.limit_backward=c,l.cursor=l.limit,function(){var e,r,n;if(l.cursor>=i&&(r=l.limit_backward,l.limit_backward=i,l.ket=l.cursor,e=l.find_among_b(t,29),l.limit_backward=r,e))switch(l.bra=l.cursor,e){case 1:l.slice_del();break;case 2:n=l.limit-l.cursor,l.in_grouping_b(m,98,122)?l.slice_del():(l.cursor=l.limit-n,l.eq_s_b(1,"k")&&l.out_grouping_b(a,97,248)&&l.slice_del());break;case 3:l.slice_from("er")}}(),l.cursor=l.limit,n=l.limit-l.cursor,l.cursor>=i&&(r=l.limit_backward,l.limit_backward=i,l.ket=l.cursor,l.find_among_b(o,2)?(l.bra=l.cursor,l.limit_backward=r,l.cursor=l.limit-n,l.cursor>l.limit_backward&&(l.cursor--,l.bra=l.cursor,l.slice_del())):l.limit_backward=r),l.cursor=l.limit,l.cursor>=i&&(d=l.limit_backward,l.limit_backward=i,l.ket=l.cursor,(u=l.find_among_b(s,11))?(l.bra=l.cursor,l.limit_backward=d,1==u&&l.slice_del()):l.limit_backward=d),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}),e.Pipeline.registerFunction(e.no.stemmer,"stemmer-no"),e.no.stopWordFilter=e.generateStopWordFilter("alle at av bare begge ble blei bli blir blitt både båe da de deg dei deim deira deires dem den denne der dere deres det dette di din disse ditt du dykk dykkar då eg ein eit eitt eller elles en enn er et ett etter for fordi fra før ha hadde han hans har hennar henne hennes her hjå ho hoe honom hoss hossen hun hva hvem hver hvilke hvilken hvis hvor hvordan hvorfor i ikke ikkje ikkje ingen ingi inkje inn inni ja jeg kan kom korleis korso kun kunne kva kvar kvarhelst kven kvi kvifor man mange me med medan meg meget mellom men mi min mine mitt mot mykje ned no noe noen noka noko nokon nokor nokre nå når og også om opp oss over på samme seg selv si si sia sidan siden sin sine sitt sjøl skal skulle slik so som som somme somt så sånn til um upp ut uten var vart varte ved vere verte vi vil ville vore vors vort vår være være vært å".split(" ")),e.Pipeline.registerFunction(e.no.stopWordFilter,"stopWordFilter-no")}}); |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
!function(r,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():t()(r.lunr)}(this,function(){return function(r){r.stemmerSupport={Among:function(r,t,i,s){if(this.toCharArray=function(r){for(var t=r.length,i=new Array(t),s=0;s<t;s++)i[s]=r.charCodeAt(s);return i},!r&&""!=r||!t&&0!=t||!i)throw"Bad Among initialisation: s:"+r+", substring_i: "+t+", result: "+i;this.s_size=r.length,this.s=this.toCharArray(r),this.substring_i=t,this.result=i,this.method=s},SnowballProgram:function(){var r;return{bra:0,ket:0,limit:0,cursor:0,limit_backward:0,setCurrent:function(t){r=t,this.cursor=0,this.limit=t.length,this.limit_backward=0,this.bra=this.cursor,this.ket=this.limit},getCurrent:function(){var t=r;return r=null,t},in_grouping:function(t,i,s){if(this.cursor<this.limit){var e=r.charCodeAt(this.cursor);if(e<=s&&e>=i&&t[(e-=i)>>3]&1<<(7&e))return this.cursor++,!0}return!1},in_grouping_b:function(t,i,s){if(this.cursor>this.limit_backward){var e=r.charCodeAt(this.cursor-1);if(e<=s&&e>=i&&t[(e-=i)>>3]&1<<(7&e))return this.cursor--,!0}return!1},out_grouping:function(t,i,s){if(this.cursor<this.limit){var e=r.charCodeAt(this.cursor);if(e>s||e<i)return this.cursor++,!0;if(!(t[(e-=i)>>3]&1<<(7&e)))return this.cursor++,!0}return!1},out_grouping_b:function(t,i,s){if(this.cursor>this.limit_backward){var e=r.charCodeAt(this.cursor-1);if(e>s||e<i)return this.cursor--,!0;if(!(t[(e-=i)>>3]&1<<(7&e)))return this.cursor--,!0}return!1},eq_s:function(t,i){if(this.limit-this.cursor<t)return!1;for(var s=0;s<t;s++)if(r.charCodeAt(this.cursor+s)!=i.charCodeAt(s))return!1;return this.cursor+=t,!0},eq_s_b:function(t,i){if(this.cursor-this.limit_backward<t)return!1;for(var s=0;s<t;s++)if(r.charCodeAt(this.cursor-t+s)!=i.charCodeAt(s))return!1;return this.cursor-=t,!0},find_among:function(t,i){for(var s=0,e=i,n=this.cursor,u=this.limit,o=0,h=0,c=!1;;){for(var a=s+(e-s>>1),f=0,l=o<h?o:h,_=t[a],m=l;m<_.s_size;m++){if(n+l==u){f=-1;break}if(f=r.charCodeAt(n+l)-_.s[m])break;l++}if(f<0?(e=a,h=l):(s=a,o=l),e-s<=1){if(s>0||e==s||c)break;c=!0}}for(;;){if(o>=(_=t[s]).s_size){if(this.cursor=n+_.s_size,!_.method)return _.result;var b=_.method();if(this.cursor=n+_.s_size,b)return _.result}if((s=_.substring_i)<0)return 0}},find_among_b:function(t,i){for(var s=0,e=i,n=this.cursor,u=this.limit_backward,o=0,h=0,c=!1;;){for(var a=s+(e-s>>1),f=0,l=o<h?o:h,_=(m=t[a]).s_size-1-l;_>=0;_--){if(n-l==u){f=-1;break}if(f=r.charCodeAt(n-1-l)-m.s[_])break;l++}if(f<0?(e=a,h=l):(s=a,o=l),e-s<=1){if(s>0||e==s||c)break;c=!0}}for(;;){var m;if(o>=(m=t[s]).s_size){if(this.cursor=n-m.s_size,!m.method)return m.result;var b=m.method();if(this.cursor=n-m.s_size,b)return m.result}if((s=m.substring_i)<0)return 0}},replace_s:function(t,i,s){var e=s.length-(i-t),n=r.substring(0,t),u=r.substring(i);return r=n+s+u,this.limit+=e,this.cursor>=i?this.cursor+=e:this.cursor>t&&(this.cursor=t),e},slice_check:function(){if(this.bra<0||this.bra>this.ket||this.ket>this.limit||this.limit>r.length)throw"faulty slice operation"},slice_from:function(r){this.slice_check(),this.replace_s(this.bra,this.ket,r)},slice_del:function(){this.slice_from("")},insert:function(r,t,i){var s=this.replace_s(r,t,i);r<=this.bra&&(this.bra+=s),r<=this.ket&&(this.ket+=s)},slice_to:function(){return this.slice_check(),r.substring(this.bra,this.ket)},eq_v_b:function(r){return this.eq_s_b(r.length,r)}}}},r.trimmerSupport={generateTrimmer:function(r){var t=new RegExp("^[^"+r+"]+"),i=new RegExp("[^"+r+"]+$");return function(r){return"function"==typeof r.update?r.update(function(r){return r.replace(t,"").replace(i,"")}):r.replace(t,"").replace(i,"")}}}}}); |
@ -0,0 +1 @@
@@ -0,0 +1 @@
|
||||
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r,n,t;e.sv=function(){this.pipeline.reset(),this.pipeline.add(e.sv.trimmer,e.sv.stopWordFilter,e.sv.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.sv.stemmer))},e.sv.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.sv.trimmer=e.trimmerSupport.generateTrimmer(e.sv.wordCharacters),e.Pipeline.registerFunction(e.sv.trimmer,"trimmer-sv"),e.sv.stemmer=(r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,t=new function(){var e,t,i=[new r("a",-1,1),new r("arna",0,1),new r("erna",0,1),new r("heterna",2,1),new r("orna",0,1),new r("ad",-1,1),new r("e",-1,1),new r("ade",6,1),new r("ande",6,1),new r("arne",6,1),new r("are",6,1),new r("aste",6,1),new r("en",-1,1),new r("anden",12,1),new r("aren",12,1),new r("heten",12,1),new r("ern",-1,1),new r("ar",-1,1),new r("er",-1,1),new r("heter",18,1),new r("or",-1,1),new r("s",-1,2),new r("as",21,1),new r("arnas",22,1),new r("ernas",22,1),new r("ornas",22,1),new r("es",21,1),new r("ades",26,1),new r("andes",26,1),new r("ens",21,1),new r("arens",29,1),new r("hetens",29,1),new r("erns",21,1),new r("at",-1,1),new r("andet",-1,1),new r("het",-1,1),new r("ast",-1,1)],s=[new r("dd",-1,-1),new r("gd",-1,-1),new r("nn",-1,-1),new r("dt",-1,-1),new r("gt",-1,-1),new r("kt",-1,-1),new r("tt",-1,-1)],a=[new r("ig",-1,1),new r("lig",0,1),new r("els",-1,1),new r("fullt",-1,3),new r("löst",-1,2)],o=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,24,0,32],u=[119,127,149],m=new n;this.setCurrent=function(e){m.setCurrent(e)},this.getCurrent=function(){return m.getCurrent()},this.stem=function(){var r,n=m.cursor;return function(){var r,n=m.cursor+3;if(t=m.limit,0<=n||n<=m.limit){for(e=n;;){if(r=m.cursor,m.in_grouping(o,97,246)){m.cursor=r;break}if(m.cursor=r,m.cursor>=m.limit)return;m.cursor++}for(;!m.out_grouping(o,97,246);){if(m.cursor>=m.limit)return;m.cursor++}(t=m.cursor)<e&&(t=e)}}(),m.limit_backward=n,m.cursor=m.limit,function(){var e,r=m.limit_backward;if(m.cursor>=t&&(m.limit_backward=t,m.cursor=m.limit,m.ket=m.cursor,e=m.find_among_b(i,37),m.limit_backward=r,e))switch(m.bra=m.cursor,e){case 1:m.slice_del();break;case 2:m.in_grouping_b(u,98,121)&&m.slice_del()}}(),m.cursor=m.limit,r=m.limit_backward,m.cursor>=t&&(m.limit_backward=t,m.cursor=m.limit,m.find_among_b(s,7)&&(m.cursor=m.limit,m.ket=m.cursor,m.cursor>m.limit_backward&&(m.bra=--m.cursor,m.slice_del())),m.limit_backward=r),m.cursor=m.limit,function(){var e,r;if(m.cursor>=t){if(r=m.limit_backward,m.limit_backward=t,m.cursor=m.limit,m.ket=m.cursor,e=m.find_among_b(a,5))switch(m.bra=m.cursor,e){case 1:m.slice_del();break;case 2:m.slice_from("lös");break;case 3:m.slice_from("full")}m.limit_backward=r}}(),!0}},function(e){return"function"==typeof e.update?e.update(function(e){return t.setCurrent(e),t.stem(),t.getCurrent()}):(t.setCurrent(e),t.stem(),t.getCurrent())}),e.Pipeline.registerFunction(e.sv.stemmer,"stemmer-sv"),e.sv.stopWordFilter=e.generateStopWordFilter("alla allt att av blev bli blir blivit de dem den denna deras dess dessa det detta dig din dina ditt du där då efter ej eller en er era ert ett från för ha hade han hans har henne hennes hon honom hur här i icke ingen inom inte jag ju kan kunde man med mellan men mig min mina mitt mot mycket ni nu när någon något några och om oss på samma sedan sig sin sina sitta själv skulle som så sådan sådana sådant till under upp ut utan vad var vara varför varit varje vars vart vem vi vid vilka vilkas vilken vilket vår våra vårt än är åt över".split(" ")),e.Pipeline.registerFunction(e.sv.stopWordFilter,"stopWordFilter-sv")}}); |
@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
|
||||
.md-typeset h1 { font-weight: 600; } |
||||
.md-typeset h2 { font-weight: 600; } |
||||
.md-typeset h3 { font-weight: 600; } |
||||
.md-typeset h4 { font-weight: 600; } |
||||
|
||||
body { |
||||
background-color: #f2f9f7; |
||||
} |
||||
div.body { |
||||
background-color: #f2f9f7; |
||||
} |
@ -0,0 +1,401 @@
@@ -0,0 +1,401 @@
|
||||
|
||||
|
||||
|
||||
|
||||
<!DOCTYPE html> |
||||
<html lang="en" class="no-js"> |
||||
<head> |
||||
|
||||
<meta charset="utf-8"> |
||||
<meta name="viewport" content="width=device-width,initial-scale=1"> |
||||
<meta http-equiv="x-ua-compatible" content="ie=edge"> |
||||
|
||||
|
||||
<link rel="canonical" href="https://github-heroku-attack-rabbits.herokuapp.com/fishslap/"> |
||||
|
||||
|
||||
|
||||
<meta name="lang:clipboard.copy" content="Copy to clipboard"> |
||||
|
||||
<meta name="lang:clipboard.copied" content="Copied to clipboard"> |
||||
|
||||
<meta name="lang:search.language" content="en"> |
||||
|
||||
<meta name="lang:search.pipeline.stopwords" content="True"> |
||||
|
||||
<meta name="lang:search.pipeline.trimmer" content="True"> |
||||
|
||||
<meta name="lang:search.result.none" content="No matching documents"> |
||||
|
||||
<meta name="lang:search.result.one" content="1 matching document"> |
||||
|
||||
<meta name="lang:search.result.other" content="# matching documents"> |
||||
|
||||
<meta name="lang:search.tokenizer" content="[\s\-]+"> |
||||
|
||||
<link rel="shortcut icon" href="../"> |
||||
<meta name="generator" content="mkdocs-0.17.3, mkdocs-material-2.7.2"> |
||||
|
||||
|
||||
|
||||
<title>Fish Slapping - This Is The Secret Site</title> |
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../assets/stylesheets/application.8d40d89b.css"> |
||||
|
||||
<link rel="stylesheet" href="../assets/stylesheets/application-palette.6079476c.css"> |
||||
|
||||
|
||||
|
||||
<script src="../assets/javascripts/modernizr.1aa3b519.js"></script> |
||||
|
||||
|
||||
<link href="https://fonts.gstatic.com" rel="preconnect" crossorigin> |
||||
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Questrial:300,400,400i,700|Roboto+Mono"> |
||||
<style>body,input{font-family:"Questrial","Helvetica Neue",Helvetica,Arial,sans-serif}code,kbd,pre{font-family:"Roboto Mono","Courier New",Courier,monospace}</style> |
||||
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons"> |
||||
|
||||
|
||||
<link rel="stylesheet" href="../css/custom.css"> |
||||
|
||||
|
||||
</head> |
||||
|
||||
|
||||
|
||||
<body dir="ltr" data-md-color-primary="teal" data-md-color-accent="teal"> |
||||
|
||||
<svg class="md-svg"> |
||||
<defs> |
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="416" height="448" |
||||
viewBox="0 0 416 448" id="github"> |
||||
<path fill="currentColor" d="M160 304q0 10-3.125 20.5t-10.75 19-18.125 |
||||
8.5-18.125-8.5-10.75-19-3.125-20.5 3.125-20.5 10.75-19 18.125-8.5 |
||||
18.125 8.5 10.75 19 3.125 20.5zM320 304q0 10-3.125 20.5t-10.75 |
||||
19-18.125 8.5-18.125-8.5-10.75-19-3.125-20.5 3.125-20.5 10.75-19 |
||||
18.125-8.5 18.125 8.5 10.75 19 3.125 20.5zM360 |
||||
304q0-30-17.25-51t-46.75-21q-10.25 0-48.75 5.25-17.75 2.75-39.25 |
||||
2.75t-39.25-2.75q-38-5.25-48.75-5.25-29.5 0-46.75 21t-17.25 51q0 22 8 |
||||
38.375t20.25 25.75 30.5 15 35 7.375 37.25 1.75h42q20.5 0 |
||||
37.25-1.75t35-7.375 30.5-15 20.25-25.75 8-38.375zM416 260q0 51.75-15.25 |
||||
82.75-9.5 19.25-26.375 33.25t-35.25 21.5-42.5 11.875-42.875 5.5-41.75 |
||||
1.125q-19.5 0-35.5-0.75t-36.875-3.125-38.125-7.5-34.25-12.875-30.25-20.25-21.5-28.75q-15.5-30.75-15.5-82.75 |
||||
0-59.25 34-99-6.75-20.5-6.75-42.5 0-29 12.75-54.5 27 0 47.5 9.875t47.25 |
||||
30.875q36.75-8.75 77.25-8.75 37 0 70 8 26.25-20.5 |
||||
46.75-30.25t47.25-9.75q12.75 25.5 12.75 54.5 0 21.75-6.75 42 34 40 34 |
||||
99.5z" /> |
||||
</svg> |
||||
|
||||
</defs> |
||||
</svg> |
||||
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="drawer" autocomplete="off"> |
||||
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="search" autocomplete="off"> |
||||
<label class="md-overlay" data-md-component="overlay" for="drawer"></label> |
||||
|
||||
<a href="#the-secret-society-of-the-fish-slapping-dance" tabindex="1" class="md-skip"> |
||||
Skip to content |
||||
</a> |
||||
|
||||
|
||||
<header class="md-header" data-md-component="header"> |
||||
<nav class="md-header-nav md-grid"> |
||||
<div class="md-flex"> |
||||
<div class="md-flex__cell md-flex__cell--shrink"> |
||||
<a href="https://github-heroku-attack-rabbits.herokuapp.com" title="This Is The Secret Site" class="md-header-nav__button md-logo"> |
||||
|
||||
<img src="../img/bunny.png" width="24" height="24"> |
||||
|
||||
</a> |
||||
</div> |
||||
<div class="md-flex__cell md-flex__cell--shrink"> |
||||
<label class="md-icon md-icon--menu md-header-nav__button" for="drawer"></label> |
||||
</div> |
||||
<div class="md-flex__cell md-flex__cell--stretch"> |
||||
<div class="md-flex__ellipsis md-header-nav__title" data-md-component="title"> |
||||
|
||||
|
||||
<span class="md-header-nav__topic"> |
||||
This Is The Secret Site |
||||
</span> |
||||
<span class="md-header-nav__topic"> |
||||
Fish Slapping |
||||
</span> |
||||
|
||||
|
||||
</div> |
||||
</div> |
||||
<div class="md-flex__cell md-flex__cell--shrink"> |
||||
|
||||
|
||||
<label class="md-icon md-icon--search md-header-nav__button" for="search"></label> |
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog"> |
||||
<label class="md-search__overlay" for="search"></label> |
||||
<div class="md-search__inner" role="search"> |
||||
<form class="md-search__form" name="search"> |
||||
<input type="text" class="md-search__input" name="query" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="query" data-md-state="active"> |
||||
<label class="md-icon md-search__icon" for="search"></label> |
||||
<button type="reset" class="md-icon md-search__icon" data-md-component="reset" tabindex="-1"> |
||||
 |
||||
</button> |
||||
</form> |
||||
<div class="md-search__output"> |
||||
<div class="md-search__scrollwrap" data-md-scrollfix> |
||||
<div class="md-search-result" data-md-component="result"> |
||||
<div class="md-search-result__meta"> |
||||
Type to start searching |
||||
</div> |
||||
<ol class="md-search-result__list"></ol> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
|
||||
</div> |
||||
|
||||
<div class="md-flex__cell md-flex__cell--shrink"> |
||||
<div class="md-header-nav__source"> |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits" title="Go to repository" class="md-source" data-md-source="github"> |
||||
|
||||
<div class="md-source__icon"> |
||||
<svg viewBox="0 0 24 24" width="24" height="24"> |
||||
<use xlink:href="#github" width="24" height="24"></use> |
||||
</svg> |
||||
</div> |
||||
|
||||
<div class="md-source__repository"> |
||||
charlesreid1/github-heroku-attack-rabbits |
||||
</div> |
||||
</a> |
||||
|
||||
</div> |
||||
</div> |
||||
|
||||
</div> |
||||
</nav> |
||||
</header> |
||||
|
||||
<div class="md-container"> |
||||
|
||||
|
||||
|
||||
<main class="md-main"> |
||||
<div class="md-main__inner md-grid" data-md-component="container"> |
||||
|
||||
|
||||
<div class="md-sidebar md-sidebar--primary" data-md-component="navigation"> |
||||
<div class="md-sidebar__scrollwrap"> |
||||
<div class="md-sidebar__inner"> |
||||
<nav class="md-nav md-nav--primary" data-md-level="0"> |
||||
<label class="md-nav__title md-nav__title--site" for="drawer"> |
||||
<span class="md-nav__button md-logo"> |
||||
|
||||
<img src="../img/bunny.png" width="48" height="48"> |
||||
|
||||
</span> |
||||
This Is The Secret Site |
||||
</label> |
||||
|
||||
<div class="md-nav__source"> |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits" title="Go to repository" class="md-source" data-md-source="github"> |
||||
|
||||
<div class="md-source__icon"> |
||||
<svg viewBox="0 0 24 24" width="24" height="24"> |
||||
<use xlink:href="#github" width="24" height="24"></use> |
||||
</svg> |
||||
</div> |
||||
|
||||
<div class="md-source__repository"> |
||||
charlesreid1/github-heroku-attack-rabbits |
||||
</div> |
||||
</a> |
||||
|
||||
</div> |
||||
|
||||
<ul class="md-nav__list" data-md-scrollfix> |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item"> |
||||
<a href=".." title="Index" class="md-nav__link"> |
||||
Index |
||||
</a> |
||||
</li> |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item md-nav__item--active"> |
||||
|
||||
<input class="md-toggle md-nav__toggle" data-md-toggle="toc" type="checkbox" id="toc"> |
||||
|
||||
|
||||
|
||||
|
||||
<a href="./" title="Fish Slapping" class="md-nav__link md-nav__link--active"> |
||||
Fish Slapping |
||||
</a> |
||||
|
||||
</li> |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item"> |
||||
<a href="../sillywalk/" title="Silly Walks" class="md-nav__link"> |
||||
Silly Walks |
||||
</a> |
||||
</li> |
||||
|
||||
|
||||
</ul> |
||||
</nav> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
|
||||
<div class="md-sidebar md-sidebar--secondary" data-md-component="toc"> |
||||
<div class="md-sidebar__scrollwrap"> |
||||
<div class="md-sidebar__inner"> |
||||
|
||||
<nav class="md-nav md-nav--secondary"> |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</nav> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
|
||||
<div class="md-content"> |
||||
<article class="md-content__inner md-typeset"> |
||||
|
||||
|
||||
|
||||
<h1 id="the-secret-society-of-the-fish-slapping-dance">The Secret Society of the Fish Slapping Dance</h1> |
||||
<p>Welcome, and congratulations on having an even number of vowels in your Github handle. </p> |
||||
<p>And now for something completely expected:</p> |
||||
<p><a href="https://www.youtube.com/watch?v=T8XeDvKqI4E">Fish-Slapping Dance</a></p> |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</article> |
||||
</div> |
||||
</div> |
||||
</main> |
||||
|
||||
|
||||
<footer class="md-footer"> |
||||
|
||||
<div class="md-footer-nav"> |
||||
<nav class="md-footer-nav__inner md-grid"> |
||||
|
||||
<a href=".." title="Index" class="md-flex md-footer-nav__link md-footer-nav__link--prev" rel="prev"> |
||||
<div class="md-flex__cell md-flex__cell--shrink"> |
||||
<i class="md-icon md-icon--arrow-back md-footer-nav__button"></i> |
||||
</div> |
||||
<div class="md-flex__cell md-flex__cell--stretch md-footer-nav__title"> |
||||
<span class="md-flex__ellipsis"> |
||||
<span class="md-footer-nav__direction"> |
||||
Previous |
||||
</span> |
||||
Index |
||||
</span> |
||||
</div> |
||||
</a> |
||||
|
||||
|
||||
<a href="../sillywalk/" title="Silly Walks" class="md-flex md-footer-nav__link md-footer-nav__link--next" rel="next"> |
||||
<div class="md-flex__cell md-flex__cell--stretch md-footer-nav__title"> |
||||
<span class="md-flex__ellipsis"> |
||||
<span class="md-footer-nav__direction"> |
||||
Next |
||||
</span> |
||||
Silly Walks |
||||
</span> |
||||
</div> |
||||
<div class="md-flex__cell md-flex__cell--shrink"> |
||||
<i class="md-icon md-icon--arrow-forward md-footer-nav__button"></i> |
||||
</div> |
||||
</a> |
||||
|
||||
</nav> |
||||
</div> |
||||
|
||||
<div class="md-footer-meta md-typeset"> |
||||
<div class="md-footer-meta__inner md-grid"> |
||||
<div class="md-footer-copyright"> |
||||
|
||||
<div class="md-footer-copyright__highlight"> |
||||
Copyright © 2018 Charles Reid, released under the <a href="https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits/src/branch/docs/LICENSE">WTFPL</a>.<br /><br />Many Bothans died to bring us this documentation.<br /><br /> |
||||
</div> |
||||
|
||||
powered by |
||||
<a href="http://www.mkdocs.org">MkDocs</a> |
||||
and |
||||
<a href="https://squidfunk.github.io/mkdocs-material/"> |
||||
Material for MkDocs</a> |
||||
</div> |
||||
|
||||
|
||||
|
||||
</div> |
||||
</div> |
||||
</footer> |
||||
|
||||
</div> |
||||
|
||||
<script src="../assets/javascripts/application.0cf9b500.js"></script> |
||||
|
||||
<script>app.initialize({version:"0.17.3",url:{base:".."}})</script> |
||||
|
||||
<script src="../search/require.js"></script> |
||||
|
||||
<script src="../search/search.js"></script> |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</body> |
||||
</html> |
After Width: | Height: | Size: 598 KiB |
After Width: | Height: | Size: 621 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
After Width: | Height: | Size: 34 KiB |
@ -0,0 +1,394 @@
@@ -0,0 +1,394 @@
|
||||
|
||||
|
||||
|
||||
|
||||
<!DOCTYPE html> |
||||
<html lang="en" class="no-js"> |
||||
<head> |
||||
|
||||
<meta charset="utf-8"> |
||||
<meta name="viewport" content="width=device-width,initial-scale=1"> |
||||
<meta http-equiv="x-ua-compatible" content="ie=edge"> |
||||
|
||||
|
||||
<link rel="canonical" href="https://github-heroku-attack-rabbits.herokuapp.com/"> |
||||
|
||||
|
||||
|
||||
<meta name="lang:clipboard.copy" content="Copy to clipboard"> |
||||
|
||||
<meta name="lang:clipboard.copied" content="Copied to clipboard"> |
||||
|
||||
<meta name="lang:search.language" content="en"> |
||||
|
||||
<meta name="lang:search.pipeline.stopwords" content="True"> |
||||
|
||||
<meta name="lang:search.pipeline.trimmer" content="True"> |
||||
|
||||
<meta name="lang:search.result.none" content="No matching documents"> |
||||
|
||||
<meta name="lang:search.result.one" content="1 matching document"> |
||||
|
||||
<meta name="lang:search.result.other" content="# matching documents"> |
||||
|
||||
<meta name="lang:search.tokenizer" content="[\s\-]+"> |
||||
|
||||
<link rel="shortcut icon" href="./"> |
||||
<meta name="generator" content="mkdocs-0.17.3, mkdocs-material-2.7.2"> |
||||
|
||||
|
||||
|
||||
<title>This Is The Secret Site</title> |
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="./assets/stylesheets/application.8d40d89b.css"> |
||||
|
||||
<link rel="stylesheet" href="./assets/stylesheets/application-palette.6079476c.css"> |
||||
|
||||
|
||||
|
||||
<script src="./assets/javascripts/modernizr.1aa3b519.js"></script> |
||||
|
||||
|
||||
<link href="https://fonts.gstatic.com" rel="preconnect" crossorigin> |
||||
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Questrial:300,400,400i,700|Roboto+Mono"> |
||||
<style>body,input{font-family:"Questrial","Helvetica Neue",Helvetica,Arial,sans-serif}code,kbd,pre{font-family:"Roboto Mono","Courier New",Courier,monospace}</style> |
||||
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons"> |
||||
|
||||
|
||||
<link rel="stylesheet" href="./css/custom.css"> |
||||
|
||||
|
||||
</head> |
||||
|
||||
|
||||
|
||||
<body dir="ltr" data-md-color-primary="teal" data-md-color-accent="teal"> |
||||
|
||||
<svg class="md-svg"> |
||||
<defs> |
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="416" height="448" |
||||
viewBox="0 0 416 448" id="github"> |
||||
<path fill="currentColor" d="M160 304q0 10-3.125 20.5t-10.75 19-18.125 |
||||
8.5-18.125-8.5-10.75-19-3.125-20.5 3.125-20.5 10.75-19 18.125-8.5 |
||||
18.125 8.5 10.75 19 3.125 20.5zM320 304q0 10-3.125 20.5t-10.75 |
||||
19-18.125 8.5-18.125-8.5-10.75-19-3.125-20.5 3.125-20.5 10.75-19 |
||||
18.125-8.5 18.125 8.5 10.75 19 3.125 20.5zM360 |
||||
304q0-30-17.25-51t-46.75-21q-10.25 0-48.75 5.25-17.75 2.75-39.25 |
||||
2.75t-39.25-2.75q-38-5.25-48.75-5.25-29.5 0-46.75 21t-17.25 51q0 22 8 |
||||
38.375t20.25 25.75 30.5 15 35 7.375 37.25 1.75h42q20.5 0 |
||||
37.25-1.75t35-7.375 30.5-15 20.25-25.75 8-38.375zM416 260q0 51.75-15.25 |
||||
82.75-9.5 19.25-26.375 33.25t-35.25 21.5-42.5 11.875-42.875 5.5-41.75 |
||||
1.125q-19.5 0-35.5-0.75t-36.875-3.125-38.125-7.5-34.25-12.875-30.25-20.25-21.5-28.75q-15.5-30.75-15.5-82.75 |
||||
0-59.25 34-99-6.75-20.5-6.75-42.5 0-29 12.75-54.5 27 0 47.5 9.875t47.25 |
||||
30.875q36.75-8.75 77.25-8.75 37 0 70 8 26.25-20.5 |
||||
46.75-30.25t47.25-9.75q12.75 25.5 12.75 54.5 0 21.75-6.75 42 34 40 34 |
||||
99.5z" /> |
||||
</svg> |
||||
|
||||
</defs> |
||||
</svg> |
||||
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="drawer" autocomplete="off"> |
||||
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="search" autocomplete="off"> |
||||
<label class="md-overlay" data-md-component="overlay" for="drawer"></label> |
||||
|
||||
<a href="#secret-index" tabindex="1" class="md-skip"> |
||||
Skip to content |
||||
</a> |
||||
|
||||
|
||||
<header class="md-header" data-md-component="header"> |
||||
<nav class="md-header-nav md-grid"> |
||||
<div class="md-flex"> |
||||
<div class="md-flex__cell md-flex__cell--shrink"> |
||||
<a href="https://github-heroku-attack-rabbits.herokuapp.com" title="This Is The Secret Site" class="md-header-nav__button md-logo"> |
||||
|
||||
<img src="./img/bunny.png" width="24" height="24"> |
||||
|
||||
</a> |
||||
</div> |
||||
<div class="md-flex__cell md-flex__cell--shrink"> |
||||
<label class="md-icon md-icon--menu md-header-nav__button" for="drawer"></label> |
||||
</div> |
||||
<div class="md-flex__cell md-flex__cell--stretch"> |
||||
<div class="md-flex__ellipsis md-header-nav__title" data-md-component="title"> |
||||
|
||||
|
||||
<span class="md-header-nav__topic"> |
||||
This Is The Secret Site |
||||
</span> |
||||
<span class="md-header-nav__topic"> |
||||
Index |
||||
</span> |
||||
|
||||
|
||||
</div> |
||||
</div> |
||||
<div class="md-flex__cell md-flex__cell--shrink"> |
||||
|
||||
|
||||
<label class="md-icon md-icon--search md-header-nav__button" for="search"></label> |
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog"> |
||||
<label class="md-search__overlay" for="search"></label> |
||||
<div class="md-search__inner" role="search"> |
||||
<form class="md-search__form" name="search"> |
||||
<input type="text" class="md-search__input" name="query" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="query" data-md-state="active"> |
||||
<label class="md-icon md-search__icon" for="search"></label> |
||||
<button type="reset" class="md-icon md-search__icon" data-md-component="reset" tabindex="-1"> |
||||
 |
||||
</button> |
||||
</form> |
||||
<div class="md-search__output"> |
||||
<div class="md-search__scrollwrap" data-md-scrollfix> |
||||
<div class="md-search-result" data-md-component="result"> |
||||
<div class="md-search-result__meta"> |
||||
Type to start searching |
||||
</div> |
||||
<ol class="md-search-result__list"></ol> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
|
||||
</div> |
||||
|
||||
<div class="md-flex__cell md-flex__cell--shrink"> |
||||
<div class="md-header-nav__source"> |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits" title="Go to repository" class="md-source" data-md-source="github"> |
||||
|
||||
<div class="md-source__icon"> |
||||
<svg viewBox="0 0 24 24" width="24" height="24"> |
||||
<use xlink:href="#github" width="24" height="24"></use> |
||||
</svg> |
||||
</div> |
||||
|
||||
<div class="md-source__repository"> |
||||
charlesreid1/github-heroku-attack-rabbits |
||||
</div> |
||||
</a> |
||||
|
||||
</div> |
||||
</div> |
||||
|
||||
</div> |
||||
</nav> |
||||
</header> |
||||
|
||||
<div class="md-container"> |
||||
|
||||
|
||||
|
||||
<main class="md-main"> |
||||
<div class="md-main__inner md-grid" data-md-component="container"> |
||||
|
||||
|
||||
<div class="md-sidebar md-sidebar--primary" data-md-component="navigation"> |
||||
<div class="md-sidebar__scrollwrap"> |
||||
<div class="md-sidebar__inner"> |
||||
<nav class="md-nav md-nav--primary" data-md-level="0"> |
||||
<label class="md-nav__title md-nav__title--site" for="drawer"> |
||||
<span class="md-nav__button md-logo"> |
||||
|
||||
<img src="./img/bunny.png" width="48" height="48"> |
||||
|
||||
</span> |
||||
This Is The Secret Site |
||||
</label> |
||||
|
||||
<div class="md-nav__source"> |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits" title="Go to repository" class="md-source" data-md-source="github"> |
||||
|
||||
<div class="md-source__icon"> |
||||
<svg viewBox="0 0 24 24" width="24" height="24"> |
||||
<use xlink:href="#github" width="24" height="24"></use> |
||||
</svg> |
||||
</div> |
||||
|
||||
<div class="md-source__repository"> |
||||
charlesreid1/github-heroku-attack-rabbits |
||||
</div> |
||||
</a> |
||||
|
||||
</div> |
||||
|
||||
<ul class="md-nav__list" data-md-scrollfix> |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item md-nav__item--active"> |
||||
|
||||
<input class="md-toggle md-nav__toggle" data-md-toggle="toc" type="checkbox" id="toc"> |
||||
|
||||
|
||||
|
||||
|
||||
<a href="." title="Index" class="md-nav__link md-nav__link--active"> |
||||
Index |
||||
</a> |
||||
|
||||
</li> |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item"> |
||||
<a href="fishslap/" title="Fish Slapping" class="md-nav__link"> |
||||
Fish Slapping |
||||
</a> |
||||
</li> |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item"> |
||||
<a href="sillywalk/" title="Silly Walks" class="md-nav__link"> |
||||
Silly Walks |
||||
</a> |
||||
</li> |
||||
|
||||
|
||||
</ul> |
||||
</nav> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
|
||||
<div class="md-sidebar md-sidebar--secondary" data-md-component="toc"> |
||||
<div class="md-sidebar__scrollwrap"> |
||||
<div class="md-sidebar__inner"> |
||||
|
||||
<nav class="md-nav md-nav--secondary"> |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</nav> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
|
||||
<div class="md-content"> |
||||
<article class="md-content__inner md-typeset"> |
||||
|
||||
|
||||
|
||||
<h1 id="secret-index">Secret Index</h1> |
||||
<p>This is an index of secret pages. Note that this page is not protected.</p> |
||||
<p>You will be asked to authenticate when you click the links below.</p> |
||||
<p>If your username has an <strong>even number of vowels</strong>, you are a member |
||||
of the <a href="fishslap/">Secret Society of the Fish Slappers</a>. |
||||
Click the link to log in and proceed, or else be attacked by |
||||
the Github-Heroku attack rabbits.</p> |
||||
<p>If your username has an <strong>odd number of vowels</strong>, you are a member |
||||
of the <a href="sillywalk/">Secret Ministerial Department for Theoretical Silly Walk Studies</a>. |
||||
Click the link to log in and proceed, or else be attacked by |
||||
the Github-Heroku attack rabbits.</p> |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</article> |
||||
</div> |
||||
</div> |
||||
</main> |
||||
|
||||
|
||||
<footer class="md-footer"> |
||||
|
||||
<div class="md-footer-nav"> |
||||
<nav class="md-footer-nav__inner md-grid"> |
||||
|
||||
|
||||
<a href="fishslap/" title="Fish Slapping" class="md-flex md-footer-nav__link md-footer-nav__link--next" rel="next"> |
||||
<div class="md-flex__cell md-flex__cell--stretch md-footer-nav__title"> |
||||
<span class="md-flex__ellipsis"> |
||||
<span class="md-footer-nav__direction"> |
||||
Next |
||||
</span> |
||||
Fish Slapping |
||||
</span> |
||||
</div> |
||||
<div class="md-flex__cell md-flex__cell--shrink"> |
||||
<i class="md-icon md-icon--arrow-forward md-footer-nav__button"></i> |
||||
</div> |
||||
</a> |
||||
|
||||
</nav> |
||||
</div> |
||||
|
||||
<div class="md-footer-meta md-typeset"> |
||||
<div class="md-footer-meta__inner md-grid"> |
||||
<div class="md-footer-copyright"> |
||||
|
||||
<div class="md-footer-copyright__highlight"> |
||||
Copyright © 2018 Charles Reid, released under the <a href="https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits/src/branch/docs/LICENSE">WTFPL</a>.<br /><br />Many Bothans died to bring us this documentation.<br /><br /> |
||||
</div> |
||||
|
||||
powered by |
||||
<a href="http://www.mkdocs.org">MkDocs</a> |
||||
and |
||||
<a href="https://squidfunk.github.io/mkdocs-material/"> |
||||
Material for MkDocs</a> |
||||
</div> |
||||
|
||||
|
||||
|
||||
</div> |
||||
</div> |
||||
</footer> |
||||
|
||||
</div> |
||||
|
||||
<script src="./assets/javascripts/application.0cf9b500.js"></script> |
||||
|
||||
<script>app.initialize({version:"0.17.3",url:{base:"."}})</script> |
||||
|
||||
<script src="./search/require.js"></script> |
||||
|
||||
<script src="./search/search.js"></script> |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</body> |
||||
</html> |
@ -0,0 +1,36 @@
@@ -0,0 +1,36 @@
|
||||
/* |
||||
RequireJS 2.1.16 Copyright (c) 2010-2015, The Dojo Foundation All Rights Reserved. |
||||
Available via the MIT or new BSD license. |
||||
see: http://github.com/jrburke/requirejs for details
|
||||
*/ |
||||
var requirejs,require,define; |
||||
(function(ba){function G(b){return"[object Function]"===K.call(b)}function H(b){return"[object Array]"===K.call(b)}function v(b,c){if(b){var d;for(d=0;d<b.length&&(!b[d]||!c(b[d],d,b));d+=1);}}function T(b,c){if(b){var d;for(d=b.length-1;-1<d&&(!b[d]||!c(b[d],d,b));d-=1);}}function t(b,c){return fa.call(b,c)}function m(b,c){return t(b,c)&&b[c]}function B(b,c){for(var d in b)if(t(b,d)&&c(b[d],d))break}function U(b,c,d,e){c&&B(c,function(c,g){if(d||!t(b,g))e&&"object"===typeof c&&c&&!H(c)&&!G(c)&&!(c instanceof |
||||
RegExp)?(b[g]||(b[g]={}),U(b[g],c,d,e)):b[g]=c});return b}function u(b,c){return function(){return c.apply(b,arguments)}}function ca(b){throw b;}function da(b){if(!b)return b;var c=ba;v(b.split("."),function(b){c=c[b]});return c}function C(b,c,d,e){c=Error(c+"\nhttp://requirejs.org/docs/errors.html#"+b);c.requireType=b;c.requireModules=e;d&&(c.originalError=d);return c}function ga(b){function c(a,k,b){var f,l,c,d,e,g,i,p,k=k&&k.split("/"),h=j.map,n=h&&h["*"];if(a){a=a.split("/");l=a.length-1;j.nodeIdCompat&& |
||||
Q.test(a[l])&&(a[l]=a[l].replace(Q,""));"."===a[0].charAt(0)&&k&&(l=k.slice(0,k.length-1),a=l.concat(a));l=a;for(c=0;c<l.length;c++)if(d=l[c],"."===d)l.splice(c,1),c-=1;else if(".."===d&&!(0===c||1==c&&".."===l[2]||".."===l[c-1])&&0<c)l.splice(c-1,2),c-=2;a=a.join("/")}if(b&&h&&(k||n)){l=a.split("/");c=l.length;a:for(;0<c;c-=1){e=l.slice(0,c).join("/");if(k)for(d=k.length;0<d;d-=1)if(b=m(h,k.slice(0,d).join("/")))if(b=m(b,e)){f=b;g=c;break a}!i&&(n&&m(n,e))&&(i=m(n,e),p=c)}!f&&i&&(f=i,g=p);f&&(l.splice(0, |
||||
g,f),a=l.join("/"))}return(f=m(j.pkgs,a))?f:a}function d(a){z&&v(document.getElementsByTagName("script"),function(k){if(k.getAttribute("data-requiremodule")===a&&k.getAttribute("data-requirecontext")===i.contextName)return k.parentNode.removeChild(k),!0})}function e(a){var k=m(j.paths,a);if(k&&H(k)&&1<k.length)return k.shift(),i.require.undef(a),i.makeRequire(null,{skipMap:!0})([a]),!0}function n(a){var k,c=a?a.indexOf("!"):-1;-1<c&&(k=a.substring(0,c),a=a.substring(c+1,a.length));return[k,a]}function p(a, |
||||
k,b,f){var l,d,e=null,g=k?k.name:null,j=a,p=!0,h="";a||(p=!1,a="_@r"+(K+=1));a=n(a);e=a[0];a=a[1];e&&(e=c(e,g,f),d=m(r,e));a&&(e?h=d&&d.normalize?d.normalize(a,function(a){return c(a,g,f)}):-1===a.indexOf("!")?c(a,g,f):a:(h=c(a,g,f),a=n(h),e=a[0],h=a[1],b=!0,l=i.nameToUrl(h)));b=e&&!d&&!b?"_unnormalized"+(O+=1):"";return{prefix:e,name:h,parentMap:k,unnormalized:!!b,url:l,originalName:j,isDefine:p,id:(e?e+"!"+h:h)+b}}function s(a){var k=a.id,b=m(h,k);b||(b=h[k]=new i.Module(a));return b}function q(a, |
||||
k,b){var f=a.id,c=m(h,f);if(t(r,f)&&(!c||c.defineEmitComplete))"defined"===k&&b(r[f]);else if(c=s(a),c.error&&"error"===k)b(c.error);else c.on(k,b)}function w(a,b){var c=a.requireModules,f=!1;if(b)b(a);else if(v(c,function(b){if(b=m(h,b))b.error=a,b.events.error&&(f=!0,b.emit("error",a))}),!f)g.onError(a)}function x(){R.length&&(ha.apply(A,[A.length,0].concat(R)),R=[])}function y(a){delete h[a];delete V[a]}function F(a,b,c){var f=a.map.id;a.error?a.emit("error",a.error):(b[f]=!0,v(a.depMaps,function(f, |
||||
d){var e=f.id,g=m(h,e);g&&(!a.depMatched[d]&&!c[e])&&(m(b,e)?(a.defineDep(d,r[e]),a.check()):F(g,b,c))}),c[f]=!0)}function D(){var a,b,c=(a=1E3*j.waitSeconds)&&i.startTime+a<(new Date).getTime(),f=[],l=[],g=!1,h=!0;if(!W){W=!0;B(V,function(a){var i=a.map,j=i.id;if(a.enabled&&(i.isDefine||l.push(a),!a.error))if(!a.inited&&c)e(j)?g=b=!0:(f.push(j),d(j));else if(!a.inited&&(a.fetched&&i.isDefine)&&(g=!0,!i.prefix))return h=!1});if(c&&f.length)return a=C("timeout","Load timeout for modules: "+f,null, |
||||
f),a.contextName=i.contextName,w(a);h&&v(l,function(a){F(a,{},{})});if((!c||b)&&g)if((z||ea)&&!X)X=setTimeout(function(){X=0;D()},50);W=!1}}function E(a){t(r,a[0])||s(p(a[0],null,!0)).init(a[1],a[2])}function I(a){var a=a.currentTarget||a.srcElement,b=i.onScriptLoad;a.detachEvent&&!Y?a.detachEvent("onreadystatechange",b):a.removeEventListener("load",b,!1);b=i.onScriptError;(!a.detachEvent||Y)&&a.removeEventListener("error",b,!1);return{node:a,id:a&&a.getAttribute("data-requiremodule")}}function J(){var a; |
||||
for(x();A.length;){a=A.shift();if(null===a[0])return w(C("mismatch","Mismatched anonymous define() module: "+a[a.length-1]));E(a)}}var W,Z,i,L,X,j={waitSeconds:7,baseUrl:"./",paths:{},bundles:{},pkgs:{},shim:{},config:{}},h={},V={},$={},A=[],r={},S={},aa={},K=1,O=1;L={require:function(a){return a.require?a.require:a.require=i.makeRequire(a.map)},exports:function(a){a.usingExports=!0;if(a.map.isDefine)return a.exports?r[a.map.id]=a.exports:a.exports=r[a.map.id]={}},module:function(a){return a.module? |
||||
a.module:a.module={id:a.map.id,uri:a.map.url,config:function(){return m(j.config,a.map.id)||{}},exports:a.exports||(a.exports={})}}};Z=function(a){this.events=m($,a.id)||{};this.map=a;this.shim=m(j.shim,a.id);this.depExports=[];this.depMaps=[];this.depMatched=[];this.pluginMaps={};this.depCount=0};Z.prototype={init:function(a,b,c,f){f=f||{};if(!this.inited){this.factory=b;if(c)this.on("error",c);else this.events.error&&(c=u(this,function(a){this.emit("error",a)}));this.depMaps=a&&a.slice(0);this.errback= |
||||
c;this.inited=!0;this.ignore=f.ignore;f.enabled||this.enabled?this.enable():this.check()}},defineDep:function(a,b){this.depMatched[a]||(this.depMatched[a]=!0,this.depCount-=1,this.depExports[a]=b)},fetch:function(){if(!this.fetched){this.fetched=!0;i.startTime=(new Date).getTime();var a=this.map;if(this.shim)i.makeRequire(this.map,{enableBuildCallback:!0})(this.shim.deps||[],u(this,function(){return a.prefix?this.callPlugin():this.load()}));else return a.prefix?this.callPlugin():this.load()}},load:function(){var a= |
||||
this.map.url;S[a]||(S[a]=!0,i.load(this.map.id,a))},check:function(){if(this.enabled&&!this.enabling){var a,b,c=this.map.id;b=this.depExports;var f=this.exports,l=this.factory;if(this.inited)if(this.error)this.emit("error",this.error);else{if(!this.defining){this.defining=!0;if(1>this.depCount&&!this.defined){if(G(l)){if(this.events.error&&this.map.isDefine||g.onError!==ca)try{f=i.execCb(c,l,b,f)}catch(d){a=d}else f=i.execCb(c,l,b,f);this.map.isDefine&&void 0===f&&((b=this.module)?f=b.exports:this.usingExports&& |
||||
(f=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",w(this.error=a)}else f=l;this.exports=f;if(this.map.isDefine&&!this.ignore&&(r[c]=f,g.onResourceLoad))g.onResourceLoad(i,this.map,this.depMaps);y(c);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else this.fetch()}},callPlugin:function(){var a= |
||||
this.map,b=a.id,d=p(a.prefix);this.depMaps.push(d);q(d,"defined",u(this,function(f){var l,d;d=m(aa,this.map.id);var e=this.map.name,P=this.map.parentMap?this.map.parentMap.name:null,n=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(f.normalize&&(e=f.normalize(e,function(a){return c(a,P,!0)})||""),f=p(a.prefix+"!"+e,this.map.parentMap),q(f,"defined",u(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),d=m(h,f.id)){this.depMaps.push(f); |
||||
if(this.events.error)d.on("error",u(this,function(a){this.emit("error",a)}));d.enable()}}else d?(this.map.url=i.nameToUrl(d),this.load()):(l=u(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),l.error=u(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];B(h,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&y(a.map.id)});w(a)}),l.fromText=u(this,function(f,c){var d=a.name,e=p(d),P=M;c&&(f=c);P&&(M=!1);s(e);t(j.config,b)&&(j.config[d]=j.config[b]);try{g.exec(f)}catch(h){return w(C("fromtexteval", |
||||
"fromText eval for "+b+" failed: "+h,h,[b]))}P&&(M=!0);this.depMaps.push(e);i.completeLoad(d);n([d],l)}),f.load(a.name,n,l,j))}));i.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){V[this.map.id]=this;this.enabling=this.enabled=!0;v(this.depMaps,u(this,function(a,b){var c,f;if("string"===typeof a){a=p(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=m(L,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;q(a,"defined",u(this,function(a){this.defineDep(b, |
||||
a);this.check()}));this.errback?q(a,"error",u(this,this.errback)):this.events.error&&q(a,"error",u(this,function(a){this.emit("error",a)}))}c=a.id;f=h[c];!t(L,c)&&(f&&!f.enabled)&&i.enable(a,this)}));B(this.pluginMaps,u(this,function(a){var b=m(h,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){v(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};i={config:j,contextName:b, |
||||
registry:h,defined:r,urlFetched:S,defQueue:A,Module:Z,makeModuleMap:p,nextTick:g.nextTick,onError:w,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=j.shim,c={paths:!0,bundles:!0,config:!0,map:!0};B(a,function(a,b){c[b]?(j[b]||(j[b]={}),U(j[b],a,!0,!0)):j[b]=a});a.bundles&&B(a.bundles,function(a,b){v(a,function(a){a!==b&&(aa[a]=b)})});a.shim&&(B(a.shim,function(a,c){H(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a); |
||||
b[c]=a}),j.shim=b);a.packages&&v(a.packages,function(a){var b,a="string"===typeof a?{name:a}:a;b=a.name;a.location&&(j.paths[b]=a.location);j.pkgs[b]=a.name+"/"+(a.main||"main").replace(ia,"").replace(Q,"")});B(h,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=p(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ba,arguments));return b||a.exports&&da(a.exports)}},makeRequire:function(a,e){function j(c,d,m){var n, |
||||
q;e.enableBuildCallback&&(d&&G(d))&&(d.__requireJsBuild=!0);if("string"===typeof c){if(G(d))return w(C("requireargs","Invalid require call"),m);if(a&&t(L,c))return L[c](h[a.id]);if(g.get)return g.get(i,c,a,j);n=p(c,a,!1,!0);n=n.id;return!t(r,n)?w(C("notloaded",'Module name "'+n+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):r[n]}J();i.nextTick(function(){J();q=s(p(null,a));q.skipMap=e.skipMap;q.init(c,d,m,{enabled:!0});D()});return j}e=e||{};U(j,{isBrowser:z,toUrl:function(b){var d, |
||||
e=b.lastIndexOf("."),k=b.split("/")[0];if(-1!==e&&(!("."===k||".."===k)||1<e))d=b.substring(e,b.length),b=b.substring(0,e);return i.nameToUrl(c(b,a&&a.id,!0),d,!0)},defined:function(b){return t(r,p(b,a,!1,!0).id)},specified:function(b){b=p(b,a,!1,!0).id;return t(r,b)||t(h,b)}});a||(j.undef=function(b){x();var c=p(b,a,!0),e=m(h,b);d(b);delete r[b];delete S[c.url];delete $[b];T(A,function(a,c){a[0]===b&&A.splice(c,1)});e&&(e.events.defined&&($[b]=e.events),y(b))});return j},enable:function(a){m(h,a.id)&& |
||||
s(a).enable()},completeLoad:function(a){var b,c,d=m(j.shim,a)||{},g=d.exports;for(x();A.length;){c=A.shift();if(null===c[0]){c[0]=a;if(b)break;b=!0}else c[0]===a&&(b=!0);E(c)}c=m(h,a);if(!b&&!t(r,a)&&c&&!c.inited){if(j.enforceDefine&&(!g||!da(g)))return e(a)?void 0:w(C("nodefine","No define call for "+a,null,[a]));E([a,d.deps||[],d.exportsFn])}D()},nameToUrl:function(a,b,c){var d,e,h;(d=m(j.pkgs,a))&&(a=d);if(d=m(aa,a))return i.nameToUrl(d,b,c);if(g.jsExtRegExp.test(a))d=a+(b||"");else{d=j.paths; |
||||
a=a.split("/");for(e=a.length;0<e;e-=1)if(h=a.slice(0,e).join("/"),h=m(d,h)){H(h)&&(h=h[0]);a.splice(0,e,h);break}d=a.join("/");d+=b||(/^data\:|\?/.test(d)||c?"":".js");d=("/"===d.charAt(0)||d.match(/^[\w\+\.\-]+:/)?"":j.baseUrl)+d}return j.urlArgs?d+((-1===d.indexOf("?")?"?":"&")+j.urlArgs):d},load:function(a,b){g.load(i,a,b)},execCb:function(a,b,c,d){return b.apply(d,c)},onScriptLoad:function(a){if("load"===a.type||ja.test((a.currentTarget||a.srcElement).readyState))N=null,a=I(a),i.completeLoad(a.id)}, |
||||
onScriptError:function(a){var b=I(a);if(!e(b.id))return w(C("scripterror","Script error for: "+b.id,a,[b.id]))}};i.require=i.makeRequire();return i}var g,x,y,D,I,E,N,J,s,O,ka=/(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,la=/[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,Q=/\.js$/,ia=/^\.\//;x=Object.prototype;var K=x.toString,fa=x.hasOwnProperty,ha=Array.prototype.splice,z=!!("undefined"!==typeof window&&"undefined"!==typeof navigator&&window.document),ea=!z&&"undefined"!==typeof importScripts,ja= |
||||
z&&"PLAYSTATION 3"===navigator.platform?/^complete$/:/^(complete|loaded)$/,Y="undefined"!==typeof opera&&"[object Opera]"===opera.toString(),F={},q={},R=[],M=!1;if("undefined"===typeof define){if("undefined"!==typeof requirejs){if(G(requirejs))return;q=requirejs;requirejs=void 0}"undefined"!==typeof require&&!G(require)&&(q=require,require=void 0);g=requirejs=function(b,c,d,e){var n,p="_";!H(b)&&"string"!==typeof b&&(n=b,H(c)?(b=c,c=d,d=e):b=[]);n&&n.context&&(p=n.context);(e=m(F,p))||(e=F[p]=g.s.newContext(p)); |
||||
n&&e.configure(n);return e.require(b,c,d)};g.config=function(b){return g(b)};g.nextTick="undefined"!==typeof setTimeout?function(b){setTimeout(b,4)}:function(b){b()};require||(require=g);g.version="2.1.16";g.jsExtRegExp=/^\/|:|\?|\.js$/;g.isBrowser=z;x=g.s={contexts:F,newContext:ga};g({});v(["toUrl","undef","defined","specified"],function(b){g[b]=function(){var c=F._;return c.require[b].apply(c,arguments)}});if(z&&(y=x.head=document.getElementsByTagName("head")[0],D=document.getElementsByTagName("base")[0]))y= |
||||
x.head=D.parentNode;g.onError=ca;g.createNode=function(b){var c=b.xhtml?document.createElementNS("http://www.w3.org/1999/xhtml","html:script"):document.createElement("script");c.type=b.scriptType||"text/javascript";c.charset="utf-8";c.async=!0;return c};g.load=function(b,c,d){var e=b&&b.config||{};if(z)return e=g.createNode(e,c,d),e.setAttribute("data-requirecontext",b.contextName),e.setAttribute("data-requiremodule",c),e.attachEvent&&!(e.attachEvent.toString&&0>e.attachEvent.toString().indexOf("[native code"))&& |
||||
!Y?(M=!0,e.attachEvent("onreadystatechange",b.onScriptLoad)):(e.addEventListener("load",b.onScriptLoad,!1),e.addEventListener("error",b.onScriptError,!1)),e.src=d,J=e,D?y.insertBefore(e,D):y.appendChild(e),J=null,e;if(ea)try{importScripts(d),b.completeLoad(c)}catch(m){b.onError(C("importscripts","importScripts failed for "+c+" at "+d,m,[c]))}};z&&!q.skipDataMain&&T(document.getElementsByTagName("script"),function(b){y||(y=b.parentNode);if(I=b.getAttribute("data-main"))return s=I,q.baseUrl||(E=s.split("/"), |
||||
s=E.pop(),O=E.length?E.join("/")+"/":"./",q.baseUrl=O),s=s.replace(Q,""),g.jsExtRegExp.test(s)&&(s=I),q.deps=q.deps?q.deps.concat(s):[s],!0});define=function(b,c,d){var e,g;"string"!==typeof b&&(d=c,c=b,b=null);H(c)||(d=c,c=null);!c&&G(d)&&(c=[],d.length&&(d.toString().replace(ka,"").replace(la,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));if(M){if(!(e=J))N&&"interactive"===N.readyState||T(document.getElementsByTagName("script"),function(b){if("interactive"=== |
||||
b.readyState)return N=b}),e=N;e&&(b||(b=e.getAttribute("data-requiremodule")),g=F[e.getAttribute("data-requirecontext")])}(g?g.defQueue:R).push([b,c,d])};define.amd={jQuery:!0};g.exec=function(b){return eval(b)};g(q)}})(this); |
@ -0,0 +1,4 @@
@@ -0,0 +1,4 @@
|
||||
<article> |
||||
<h3><a href="{{location}}">{{title}}</a></h3> |
||||
<p>{{summary}}</p> |
||||
</article> |
@ -0,0 +1,92 @@
@@ -0,0 +1,92 @@
|
||||
require.config({ |
||||
baseUrl: base_url + "/search/" |
||||
}); |
||||
|
||||
require([ |
||||
'mustache.min', |
||||
'lunr.min', |
||||
'text!search-results-template.mustache', |
||||
'text!search_index.json', |
||||
], function (Mustache, lunr, results_template, data) { |
||||
"use strict"; |
||||
|
||||
function getSearchTerm() |
||||
{ |
||||
var sPageURL = window.location.search.substring(1); |
||||
var sURLVariables = sPageURL.split('&'); |
||||
for (var i = 0; i < sURLVariables.length; i++) |
||||
{ |
||||
var sParameterName = sURLVariables[i].split('='); |
||||
if (sParameterName[0] == 'q') |
||||
{ |
||||
return decodeURIComponent(sParameterName[1].replace(/\+/g, '%20')); |
||||
} |
||||
} |
||||
} |
||||
|
||||
var index = lunr(function () { |
||||
this.field('title', {boost: 10}); |
||||
this.field('text'); |
||||
this.ref('location'); |
||||
}); |
||||
|
||||
data = JSON.parse(data); |
||||
var documents = {}; |
||||
|
||||
for (var i=0; i < data.docs.length; i++){ |
||||
var doc = data.docs[i]; |
||||
doc.location = base_url + doc.location; |
||||
index.add(doc); |
||||
documents[doc.location] = doc; |
||||
} |
||||
|
||||
var search = function(){ |
||||
|
||||
var query = document.getElementById('mkdocs-search-query').value; |
||||
var search_results = document.getElementById("mkdocs-search-results"); |
||||
while (search_results.firstChild) { |
||||
search_results.removeChild(search_results.firstChild); |
||||
} |
||||
|
||||
if(query === ''){ |
||||
return; |
||||
} |
||||
|
||||
var results = index.search(query); |
||||
|
||||
if (results.length > 0){ |
||||
for (var i=0; i < results.length; i++){ |
||||
var result = results[i]; |
||||
doc = documents[result.ref]; |
||||
doc.base_url = base_url; |
||||
doc.summary = doc.text.substring(0, 200); |
||||
var html = Mustache.to_html(results_template, doc); |
||||
search_results.insertAdjacentHTML('beforeend', html); |
||||
} |
||||
} else { |
||||
search_results.insertAdjacentHTML('beforeend', "<p>No results found</p>"); |
||||
} |
||||
|
||||
if(jQuery){ |
||||
/* |
||||
* We currently only automatically hide bootstrap models. This |
||||
* requires jQuery to work. |
||||
*/ |
||||
jQuery('#mkdocs_search_modal a').click(function(){ |
||||
jQuery('#mkdocs_search_modal').modal('hide'); |
||||
}); |
||||
} |
||||
|
||||
}; |
||||
|
||||
var search_input = document.getElementById('mkdocs-search-query'); |
||||
|
||||
var term = getSearchTerm(); |
||||
if (term){ |
||||
search_input.value = term; |
||||
search(); |
||||
} |
||||
|
||||
if (search_input){search_input.addEventListener("keyup", search);} |
||||
|
||||
}); |
@ -0,0 +1,34 @@
@@ -0,0 +1,34 @@
|
||||
{ |
||||
"docs": [ |
||||
{ |
||||
"location": "/", |
||||
"text": "Secret Index\n\n\nThis is an index of secret pages. Note that this page is not protected.\n\n\nYou will be asked to authenticate when you click the links below.\n\n\nIf your username has an \neven number of vowels\n, you are a member \nof the \nSecret Society of the Fish Slappers\n.\nClick the link to log in and proceed, or else be attacked by \nthe Github-Heroku attack rabbits.\n\n\nIf your username has an \nodd number of vowels\n, you are a member \nof the \nSecret Ministerial Department for Theoretical Silly Walk Studies\n.\nClick the link to log in and proceed, or else be attacked by \nthe Github-Heroku attack rabbits.", |
||||
"title": "Index" |
||||
}, |
||||
{ |
||||
"location": "/#secret-index", |
||||
"text": "This is an index of secret pages. Note that this page is not protected. You will be asked to authenticate when you click the links below. If your username has an even number of vowels , you are a member \nof the Secret Society of the Fish Slappers .\nClick the link to log in and proceed, or else be attacked by \nthe Github-Heroku attack rabbits. If your username has an odd number of vowels , you are a member \nof the Secret Ministerial Department for Theoretical Silly Walk Studies .\nClick the link to log in and proceed, or else be attacked by \nthe Github-Heroku attack rabbits.", |
||||
"title": "Secret Index" |
||||
}, |
||||
{ |
||||
"location": "/fishslap/", |
||||
"text": "The Secret Society of the Fish Slapping Dance\n\n\nWelcome, and congratulations on having an even number of vowels in your Github handle. \n\n\nAnd now for something completely expected:\n\n\nFish-Slapping Dance", |
||||
"title": "Fish Slapping" |
||||
}, |
||||
{ |
||||
"location": "/fishslap/#the-secret-society-of-the-fish-slapping-dance", |
||||
"text": "Welcome, and congratulations on having an even number of vowels in your Github handle. And now for something completely expected: Fish-Slapping Dance", |
||||
"title": "The Secret Society of the Fish Slapping Dance" |
||||
}, |
||||
{ |
||||
"location": "/sillywalk/", |
||||
"text": "The Secret Ministry of Silly Walks\n\n\nWelcome, and congratulations on having an odd number of vowels in your Github handle. \n\n\nAnd now for something completely expected:\n\n\nMinistry of Sily Walks", |
||||
"title": "Silly Walks" |
||||
}, |
||||
{ |
||||
"location": "/sillywalk/#the-secret-ministry-of-silly-walks", |
||||
"text": "Welcome, and congratulations on having an odd number of vowels in your Github handle. And now for something completely expected: Ministry of Sily Walks", |
||||
"title": "The Secret Ministry of Silly Walks" |
||||
} |
||||
] |
||||
} |
@ -0,0 +1,390 @@
@@ -0,0 +1,390 @@
|
||||
/** |
||||
* @license RequireJS text 2.0.12 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. |
||||
* Available via the MIT or new BSD license. |
||||
* see: http://github.com/requirejs/text for details
|
||||
*/ |
||||
/*jslint regexp: true */ |
||||
/*global require, XMLHttpRequest, ActiveXObject, |
||||
define, window, process, Packages, |
||||
java, location, Components, FileUtils */ |
||||
|
||||
define(['module'], function (module) { |
||||
'use strict'; |
||||
|
||||
var text, fs, Cc, Ci, xpcIsWindows, |
||||
progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'], |
||||
xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, |
||||
bodyRegExp = /<body[^>]*>\s*([\s\S]+)\s*<\/body>/im, |
||||
hasLocation = typeof location !== 'undefined' && location.href, |
||||
defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''), |
||||
defaultHostName = hasLocation && location.hostname, |
||||
defaultPort = hasLocation && (location.port || undefined), |
||||
buildMap = {}, |
||||
masterConfig = (module.config && module.config()) || {}; |
||||
|
||||
text = { |
||||
version: '2.0.12', |
||||
|
||||
strip: function (content) { |
||||
//Strips <?xml ...?> declarations so that external SVG and XML
|
||||
//documents can be added to a document without worry. Also, if the string
|
||||
//is an HTML document, only the part inside the body tag is returned.
|
||||
if (content) { |
||||
content = content.replace(xmlRegExp, ""); |
||||
var matches = content.match(bodyRegExp); |
||||
if (matches) { |
||||
content = matches[1]; |
||||
} |
||||
} else { |
||||
content = ""; |
||||
} |
||||
return content; |
||||
}, |
||||
|
||||
jsEscape: function (content) { |
||||
return content.replace(/(['\\])/g, '\\$1') |
||||
.replace(/[\f]/g, "\\f") |
||||
.replace(/[\b]/g, "\\b") |
||||
.replace(/[\n]/g, "\\n") |
||||
.replace(/[\t]/g, "\\t") |
||||
.replace(/[\r]/g, "\\r") |
||||
.replace(/[\u2028]/g, "\\u2028") |
||||
.replace(/[\u2029]/g, "\\u2029"); |
||||
}, |
||||
|
||||
createXhr: masterConfig.createXhr || function () { |
||||
//Would love to dump the ActiveX crap in here. Need IE 6 to die first.
|
||||
var xhr, i, progId; |
||||
if (typeof XMLHttpRequest !== "undefined") { |
||||
return new XMLHttpRequest(); |
||||
} else if (typeof ActiveXObject !== "undefined") { |
||||
for (i = 0; i < 3; i += 1) { |
||||
progId = progIds[i]; |
||||
try { |
||||
xhr = new ActiveXObject(progId); |
||||
} catch (e) {} |
||||
|
||||
if (xhr) { |
||||
progIds = [progId]; // so faster next time
|
||||
break; |
||||
} |
||||
} |
||||
} |
||||
|
||||
return xhr; |
||||
}, |
||||
|
||||
/** |
||||
* Parses a resource name into its component parts. Resource names |
||||
* look like: module/name.ext!strip, where the !strip part is |
||||
* optional. |
||||
* @param {String} name the resource name |
||||
* @returns {Object} with properties "moduleName", "ext" and "strip" |
||||
* where strip is a boolean. |
||||
*/ |
||||
parseName: function (name) { |
||||
var modName, ext, temp, |
||||
strip = false, |
||||
index = name.indexOf("."), |
||||
isRelative = name.indexOf('./') === 0 || |
||||
name.indexOf('../') === 0; |
||||
|
||||
if (index !== -1 && (!isRelative || index > 1)) { |
||||
modName = name.substring(0, index); |
||||
ext = name.substring(index + 1, name.length); |
||||
} else { |
||||
modName = name; |
||||
} |
||||
|
||||
temp = ext || modName; |
||||
index = temp.indexOf("!"); |
||||
if (index !== -1) { |
||||
//Pull off the strip arg.
|
||||
strip = temp.substring(index + 1) === "strip"; |
||||
temp = temp.substring(0, index); |
||||
if (ext) { |
||||
ext = temp; |
||||
} else { |
||||
modName = temp; |
||||
} |
||||
} |
||||
|
||||
return { |
||||
moduleName: modName, |
||||
ext: ext, |
||||
strip: strip |
||||
}; |
||||
}, |
||||
|
||||
xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/, |
||||
|
||||
/** |
||||
* Is an URL on another domain. Only works for browser use, returns |
||||
* false in non-browser environments. Only used to know if an |
||||
* optimized .js version of a text resource should be loaded |
||||
* instead. |
||||
* @param {String} url |
||||
* @returns Boolean |
||||
*/ |
||||
useXhr: function (url, protocol, hostname, port) { |
||||
var uProtocol, uHostName, uPort, |
||||
match = text.xdRegExp.exec(url); |
||||
if (!match) { |
||||
return true; |
||||
} |
||||
uProtocol = match[2]; |
||||
uHostName = match[3]; |
||||
|
||||
uHostName = uHostName.split(':'); |
||||
uPort = uHostName[1]; |
||||
uHostName = uHostName[0]; |
||||
|
||||
return (!uProtocol || uProtocol === protocol) && |
||||
(!uHostName || uHostName.toLowerCase() === hostname.toLowerCase()) && |
||||
((!uPort && !uHostName) || uPort === port); |
||||
}, |
||||
|
||||
finishLoad: function (name, strip, content, onLoad) { |
||||
content = strip ? text.strip(content) : content; |
||||
if (masterConfig.isBuild) { |
||||
buildMap[name] = content; |
||||
} |
||||
onLoad(content); |
||||
}, |
||||
|
||||
load: function (name, req, onLoad, config) { |
||||
//Name has format: some.module.filext!strip
|
||||
//The strip part is optional.
|
||||
//if strip is present, then that means only get the string contents
|
||||
//inside a body tag in an HTML string. For XML/SVG content it means
|
||||
//removing the <?xml ...?> declarations so the content can be inserted
|
||||
//into the current doc without problems.
|
||||
|
||||
// Do not bother with the work if a build and text will
|
||||
// not be inlined.
|
||||
if (config && config.isBuild && !config.inlineText) { |
||||
onLoad(); |
||||
return; |
||||
} |
||||
|
||||
masterConfig.isBuild = config && config.isBuild; |
||||
|
||||
var parsed = text.parseName(name), |
||||
nonStripName = parsed.moduleName + |
||||
(parsed.ext ? '.' + parsed.ext : ''), |
||||
url = req.toUrl(nonStripName), |
||||
useXhr = (masterConfig.useXhr) || |
||||
text.useXhr; |
||||
|
||||
// Do not load if it is an empty: url
|
||||
if (url.indexOf('empty:') === 0) { |
||||
onLoad(); |
||||
return; |
||||
} |
||||
|
||||
//Load the text. Use XHR if possible and in a browser.
|
||||
if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) { |
||||
text.get(url, function (content) { |
||||
text.finishLoad(name, parsed.strip, content, onLoad); |
||||
}, function (err) { |
||||
if (onLoad.error) { |
||||
onLoad.error(err); |
||||
} |
||||
}); |
||||
} else { |
||||
//Need to fetch the resource across domains. Assume
|
||||
//the resource has been optimized into a JS module. Fetch
|
||||
//by the module name + extension, but do not include the
|
||||
//!strip part to avoid file system issues.
|
||||
req([nonStripName], function (content) { |
||||
text.finishLoad(parsed.moduleName + '.' + parsed.ext, |
||||
parsed.strip, content, onLoad); |
||||
}); |
||||
} |
||||
}, |
||||
|
||||
write: function (pluginName, moduleName, write, config) { |
||||
if (buildMap.hasOwnProperty(moduleName)) { |
||||
var content = text.jsEscape(buildMap[moduleName]); |
||||
write.asModule(pluginName + "!" + moduleName, |
||||
"define(function () { return '" + |
||||
content + |
||||
"';});\n"); |
||||
} |
||||
}, |
||||
|
||||
writeFile: function (pluginName, moduleName, req, write, config) { |
||||
var parsed = text.parseName(moduleName), |
||||
extPart = parsed.ext ? '.' + parsed.ext : '', |
||||
nonStripName = parsed.moduleName + extPart, |
||||
//Use a '.js' file name so that it indicates it is a
|
||||
//script that can be loaded across domains.
|
||||
fileName = req.toUrl(parsed.moduleName + extPart) + '.js'; |
||||
|
||||
//Leverage own load() method to load plugin value, but only
|
||||
//write out values that do not have the strip argument,
|
||||
//to avoid any potential issues with ! in file names.
|
||||
text.load(nonStripName, req, function (value) { |
||||
//Use own write() method to construct full module value.
|
||||
//But need to create shell that translates writeFile's
|
||||
//write() to the right interface.
|
||||
var textWrite = function (contents) { |
||||
return write(fileName, contents); |
||||
}; |
||||
textWrite.asModule = function (moduleName, contents) { |
||||
return write.asModule(moduleName, fileName, contents); |
||||
}; |
||||
|
||||
text.write(pluginName, nonStripName, textWrite, config); |
||||
}, config); |
||||
} |
||||
}; |
||||
|
||||
if (masterConfig.env === 'node' || (!masterConfig.env && |
||||
typeof process !== "undefined" && |
||||
process.versions && |
||||
!!process.versions.node && |
||||
!process.versions['node-webkit'])) { |
||||
//Using special require.nodeRequire, something added by r.js.
|
||||
fs = require.nodeRequire('fs'); |
||||
|
||||
text.get = function (url, callback, errback) { |
||||
try { |
||||
var file = fs.readFileSync(url, 'utf8'); |
||||
//Remove BOM (Byte Mark Order) from utf8 files if it is there.
|
||||
if (file.indexOf('\uFEFF') === 0) { |
||||
file = file.substring(1); |
||||
} |
||||
callback(file); |
||||
} catch (e) { |
||||
if (errback) { |
||||
errback(e); |
||||
} |
||||
} |
||||
}; |
||||
} else if (masterConfig.env === 'xhr' || (!masterConfig.env && |
||||
text.createXhr())) { |
||||
text.get = function (url, callback, errback, headers) { |
||||
var xhr = text.createXhr(), header; |
||||
xhr.open('GET', url, true); |
||||
|
||||
//Allow plugins direct access to xhr headers
|
||||
if (headers) { |
||||
for (header in headers) { |
||||
if (headers.hasOwnProperty(header)) { |
||||
xhr.setRequestHeader(header.toLowerCase(), headers[header]); |
||||
} |
||||
} |
||||
} |
||||
|
||||
//Allow overrides specified in config
|
||||
if (masterConfig.onXhr) { |
||||
masterConfig.onXhr(xhr, url); |
||||
} |
||||
|
||||
xhr.onreadystatechange = function (evt) { |
||||
var status, err; |
||||
//Do not explicitly handle errors, those should be
|
||||
//visible via console output in the browser.
|
||||
if (xhr.readyState === 4) { |
||||
status = xhr.status || 0; |
||||
if (status > 399 && status < 600) { |
||||
//An http 4xx or 5xx error. Signal an error.
|
||||
err = new Error(url + ' HTTP status: ' + status); |
||||
err.xhr = xhr; |
||||
if (errback) { |
||||
errback(err); |
||||
} |
||||
} else { |
||||
callback(xhr.responseText); |
||||
} |
||||
|
||||
if (masterConfig.onXhrComplete) { |
||||
masterConfig.onXhrComplete(xhr, url); |
||||
} |
||||
} |
||||
}; |
||||
xhr.send(null); |
||||
}; |
||||
} else if (masterConfig.env === 'rhino' || (!masterConfig.env && |
||||
typeof Packages !== 'undefined' && typeof java !== 'undefined')) { |
||||
//Why Java, why is this so awkward?
|
||||
text.get = function (url, callback) { |
||||
var stringBuffer, line, |
||||
encoding = "utf-8", |
||||
file = new java.io.File(url), |
||||
lineSeparator = java.lang.System.getProperty("line.separator"), |
||||
input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)), |
||||
content = ''; |
||||
try { |
||||
stringBuffer = new java.lang.StringBuffer(); |
||||
line = input.readLine(); |
||||
|
||||
// Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324
|
||||
// http://www.unicode.org/faq/utf_bom.html
|
||||
|
||||
// Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK:
|
||||
// http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058
|
||||
if (line && line.length() && line.charAt(0) === 0xfeff) { |
||||
// Eat the BOM, since we've already found the encoding on this file,
|
||||
// and we plan to concatenating this buffer with others; the BOM should
|
||||
// only appear at the top of a file.
|
||||
line = line.substring(1); |
||||
} |
||||
|
||||
if (line !== null) { |
||||
stringBuffer.append(line); |
||||
} |
||||
|
||||
while ((line = input.readLine()) !== null) { |
||||
stringBuffer.append(lineSeparator); |
||||
stringBuffer.append(line); |
||||
} |
||||
//Make sure we return a JavaScript string and not a Java string.
|
||||
content = String(stringBuffer.toString()); //String
|
||||
} finally { |
||||
input.close(); |
||||
} |
||||
callback(content); |
||||
}; |
||||
} else if (masterConfig.env === 'xpconnect' || (!masterConfig.env && |
||||
typeof Components !== 'undefined' && Components.classes && |
||||
Components.interfaces)) { |
||||
//Avert your gaze!
|
||||
Cc = Components.classes; |
||||
Ci = Components.interfaces; |
||||
Components.utils['import']('resource://gre/modules/FileUtils.jsm'); |
||||
xpcIsWindows = ('@mozilla.org/windows-registry-key;1' in Cc); |
||||
|
||||
text.get = function (url, callback) { |
||||
var inStream, convertStream, fileObj, |
||||
readData = {}; |
||||
|
||||
if (xpcIsWindows) { |
||||
url = url.replace(/\//g, '\\'); |
||||
} |
||||
|
||||
fileObj = new FileUtils.File(url); |
||||
|
||||
//XPCOM, you so crazy
|
||||
try { |
||||
inStream = Cc['@mozilla.org/network/file-input-stream;1'] |
||||
.createInstance(Ci.nsIFileInputStream); |
||||
inStream.init(fileObj, 1, 0, false); |
||||
|
||||
convertStream = Cc['@mozilla.org/intl/converter-input-stream;1'] |
||||
.createInstance(Ci.nsIConverterInputStream); |
||||
convertStream.init(inStream, "utf-8", inStream.available(), |
||||
Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); |
||||
|
||||
convertStream.readString(inStream.available(), readData); |
||||
convertStream.close(); |
||||
inStream.close(); |
||||
callback(readData.value); |
||||
} catch (e) { |
||||
throw new Error((fileObj && fileObj.path || '') + ': ' + e); |
||||
} |
||||
}; |
||||
} |
||||
return text; |
||||
}); |
@ -0,0 +1,387 @@
@@ -0,0 +1,387 @@
|
||||
|
||||
|
||||
|
||||
|
||||
<!DOCTYPE html> |
||||
<html lang="en" class="no-js"> |
||||
<head> |
||||
|
||||
<meta charset="utf-8"> |
||||
<meta name="viewport" content="width=device-width,initial-scale=1"> |
||||
<meta http-equiv="x-ua-compatible" content="ie=edge"> |
||||
|
||||
|
||||
<link rel="canonical" href="https://github-heroku-attack-rabbits.herokuapp.com/sillywalk/"> |
||||
|
||||
|
||||
|
||||
<meta name="lang:clipboard.copy" content="Copy to clipboard"> |
||||
|
||||
<meta name="lang:clipboard.copied" content="Copied to clipboard"> |
||||
|
||||
<meta name="lang:search.language" content="en"> |
||||
|
||||
<meta name="lang:search.pipeline.stopwords" content="True"> |
||||
|
||||
<meta name="lang:search.pipeline.trimmer" content="True"> |
||||
|
||||
<meta name="lang:search.result.none" content="No matching documents"> |
||||
|
||||
<meta name="lang:search.result.one" content="1 matching document"> |
||||
|
||||
<meta name="lang:search.result.other" content="# matching documents"> |
||||
|
||||
<meta name="lang:search.tokenizer" content="[\s\-]+"> |
||||
|
||||
<link rel="shortcut icon" href="../"> |
||||
<meta name="generator" content="mkdocs-0.17.3, mkdocs-material-2.7.2"> |
||||
|
||||
|
||||
|
||||
<title>Silly Walks - This Is The Secret Site</title> |
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="../assets/stylesheets/application.8d40d89b.css"> |
||||
|
||||
<link rel="stylesheet" href="../assets/stylesheets/application-palette.6079476c.css"> |
||||
|
||||
|
||||
|
||||
<script src="../assets/javascripts/modernizr.1aa3b519.js"></script> |
||||
|
||||
|
||||
<link href="https://fonts.gstatic.com" rel="preconnect" crossorigin> |
||||
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Questrial:300,400,400i,700|Roboto+Mono"> |
||||
<style>body,input{font-family:"Questrial","Helvetica Neue",Helvetica,Arial,sans-serif}code,kbd,pre{font-family:"Roboto Mono","Courier New",Courier,monospace}</style> |
||||
|
||||
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons"> |
||||
|
||||
|
||||
<link rel="stylesheet" href="../css/custom.css"> |
||||
|
||||
|
||||
</head> |
||||
|
||||
|
||||
|
||||
<body dir="ltr" data-md-color-primary="teal" data-md-color-accent="teal"> |
||||
|
||||
<svg class="md-svg"> |
||||
<defs> |
||||
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="416" height="448" |
||||
viewBox="0 0 416 448" id="github"> |
||||
<path fill="currentColor" d="M160 304q0 10-3.125 20.5t-10.75 19-18.125 |
||||
8.5-18.125-8.5-10.75-19-3.125-20.5 3.125-20.5 10.75-19 18.125-8.5 |
||||
18.125 8.5 10.75 19 3.125 20.5zM320 304q0 10-3.125 20.5t-10.75 |
||||
19-18.125 8.5-18.125-8.5-10.75-19-3.125-20.5 3.125-20.5 10.75-19 |
||||
18.125-8.5 18.125 8.5 10.75 19 3.125 20.5zM360 |
||||
304q0-30-17.25-51t-46.75-21q-10.25 0-48.75 5.25-17.75 2.75-39.25 |
||||
2.75t-39.25-2.75q-38-5.25-48.75-5.25-29.5 0-46.75 21t-17.25 51q0 22 8 |
||||
38.375t20.25 25.75 30.5 15 35 7.375 37.25 1.75h42q20.5 0 |
||||
37.25-1.75t35-7.375 30.5-15 20.25-25.75 8-38.375zM416 260q0 51.75-15.25 |
||||
82.75-9.5 19.25-26.375 33.25t-35.25 21.5-42.5 11.875-42.875 5.5-41.75 |
||||
1.125q-19.5 0-35.5-0.75t-36.875-3.125-38.125-7.5-34.25-12.875-30.25-20.25-21.5-28.75q-15.5-30.75-15.5-82.75 |
||||
0-59.25 34-99-6.75-20.5-6.75-42.5 0-29 12.75-54.5 27 0 47.5 9.875t47.25 |
||||
30.875q36.75-8.75 77.25-8.75 37 0 70 8 26.25-20.5 |
||||
46.75-30.25t47.25-9.75q12.75 25.5 12.75 54.5 0 21.75-6.75 42 34 40 34 |
||||
99.5z" /> |
||||
</svg> |
||||
|
||||
</defs> |
||||
</svg> |
||||
<input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="drawer" autocomplete="off"> |
||||
<input class="md-toggle" data-md-toggle="search" type="checkbox" id="search" autocomplete="off"> |
||||
<label class="md-overlay" data-md-component="overlay" for="drawer"></label> |
||||
|
||||
<a href="#the-secret-ministry-of-silly-walks" tabindex="1" class="md-skip"> |
||||
Skip to content |
||||
</a> |
||||
|
||||
|
||||
<header class="md-header" data-md-component="header"> |
||||
<nav class="md-header-nav md-grid"> |
||||
<div class="md-flex"> |
||||
<div class="md-flex__cell md-flex__cell--shrink"> |
||||
<a href="https://github-heroku-attack-rabbits.herokuapp.com" title="This Is The Secret Site" class="md-header-nav__button md-logo"> |
||||
|
||||
<img src="../img/bunny.png" width="24" height="24"> |
||||
|
||||
</a> |
||||
</div> |
||||
<div class="md-flex__cell md-flex__cell--shrink"> |
||||
<label class="md-icon md-icon--menu md-header-nav__button" for="drawer"></label> |
||||
</div> |
||||
<div class="md-flex__cell md-flex__cell--stretch"> |
||||
<div class="md-flex__ellipsis md-header-nav__title" data-md-component="title"> |
||||
|
||||
|
||||
<span class="md-header-nav__topic"> |
||||
This Is The Secret Site |
||||
</span> |
||||
<span class="md-header-nav__topic"> |
||||
Silly Walks |
||||
</span> |
||||
|
||||
|
||||
</div> |
||||
</div> |
||||
<div class="md-flex__cell md-flex__cell--shrink"> |
||||
|
||||
|
||||
<label class="md-icon md-icon--search md-header-nav__button" for="search"></label> |
||||
|
||||
<div class="md-search" data-md-component="search" role="dialog"> |
||||
<label class="md-search__overlay" for="search"></label> |
||||
<div class="md-search__inner" role="search"> |
||||
<form class="md-search__form" name="search"> |
||||
<input type="text" class="md-search__input" name="query" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="query" data-md-state="active"> |
||||
<label class="md-icon md-search__icon" for="search"></label> |
||||
<button type="reset" class="md-icon md-search__icon" data-md-component="reset" tabindex="-1"> |
||||
 |
||||
</button> |
||||
</form> |
||||
<div class="md-search__output"> |
||||
<div class="md-search__scrollwrap" data-md-scrollfix> |
||||
<div class="md-search-result" data-md-component="result"> |
||||
<div class="md-search-result__meta"> |
||||
Type to start searching |
||||
</div> |
||||
<ol class="md-search-result__list"></ol> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
|
||||
</div> |
||||
|
||||
<div class="md-flex__cell md-flex__cell--shrink"> |
||||
<div class="md-header-nav__source"> |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits" title="Go to repository" class="md-source" data-md-source="github"> |
||||
|
||||
<div class="md-source__icon"> |
||||
<svg viewBox="0 0 24 24" width="24" height="24"> |
||||
<use xlink:href="#github" width="24" height="24"></use> |
||||
</svg> |
||||
</div> |
||||
|
||||
<div class="md-source__repository"> |
||||
charlesreid1/github-heroku-attack-rabbits |
||||
</div> |
||||
</a> |
||||
|
||||
</div> |
||||
</div> |
||||
|
||||
</div> |
||||
</nav> |
||||
</header> |
||||
|
||||
<div class="md-container"> |
||||
|
||||
|
||||
|
||||
<main class="md-main"> |
||||
<div class="md-main__inner md-grid" data-md-component="container"> |
||||
|
||||
|
||||
<div class="md-sidebar md-sidebar--primary" data-md-component="navigation"> |
||||
<div class="md-sidebar__scrollwrap"> |
||||
<div class="md-sidebar__inner"> |
||||
<nav class="md-nav md-nav--primary" data-md-level="0"> |
||||
<label class="md-nav__title md-nav__title--site" for="drawer"> |
||||
<span class="md-nav__button md-logo"> |
||||
|
||||
<img src="../img/bunny.png" width="48" height="48"> |
||||
|
||||
</span> |
||||
This Is The Secret Site |
||||
</label> |
||||
|
||||
<div class="md-nav__source"> |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<a href="https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits" title="Go to repository" class="md-source" data-md-source="github"> |
||||
|
||||
<div class="md-source__icon"> |
||||
<svg viewBox="0 0 24 24" width="24" height="24"> |
||||
<use xlink:href="#github" width="24" height="24"></use> |
||||
</svg> |
||||
</div> |
||||
|
||||
<div class="md-source__repository"> |
||||
charlesreid1/github-heroku-attack-rabbits |
||||
</div> |
||||
</a> |
||||
|
||||
</div> |
||||
|
||||
<ul class="md-nav__list" data-md-scrollfix> |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item"> |
||||
<a href=".." title="Index" class="md-nav__link"> |
||||
Index |
||||
</a> |
||||
</li> |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item"> |
||||
<a href="../fishslap/" title="Fish Slapping" class="md-nav__link"> |
||||
Fish Slapping |
||||
</a> |
||||
</li> |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<li class="md-nav__item md-nav__item--active"> |
||||
|
||||
<input class="md-toggle md-nav__toggle" data-md-toggle="toc" type="checkbox" id="toc"> |
||||
|
||||
|
||||
|
||||
|
||||
<a href="./" title="Silly Walks" class="md-nav__link md-nav__link--active"> |
||||
Silly Walks |
||||
</a> |
||||
|
||||
</li> |
||||
|
||||
|
||||
</ul> |
||||
</nav> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
|
||||
<div class="md-sidebar md-sidebar--secondary" data-md-component="toc"> |
||||
<div class="md-sidebar__scrollwrap"> |
||||
<div class="md-sidebar__inner"> |
||||
|
||||
<nav class="md-nav md-nav--secondary"> |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</nav> |
||||
</div> |
||||
</div> |
||||
</div> |
||||
|
||||
|
||||
<div class="md-content"> |
||||
<article class="md-content__inner md-typeset"> |
||||
|
||||
|
||||
|
||||
<h1 id="the-secret-ministry-of-silly-walks">The Secret Ministry of Silly Walks</h1> |
||||
<p>Welcome, and congratulations on having an odd number of vowels in your Github handle. </p> |
||||
<p>And now for something completely expected:</p> |
||||
<p><a href="https://www.youtube.com/watch?v=IqhlQfXUk7w">Ministry of Sily Walks</a></p> |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</article> |
||||
</div> |
||||
</div> |
||||
</main> |
||||
|
||||
|
||||
<footer class="md-footer"> |
||||
|
||||
<div class="md-footer-nav"> |
||||
<nav class="md-footer-nav__inner md-grid"> |
||||
|
||||
<a href="../fishslap/" title="Fish Slapping" class="md-flex md-footer-nav__link md-footer-nav__link--prev" rel="prev"> |
||||
<div class="md-flex__cell md-flex__cell--shrink"> |
||||
<i class="md-icon md-icon--arrow-back md-footer-nav__button"></i> |
||||
</div> |
||||
<div class="md-flex__cell md-flex__cell--stretch md-footer-nav__title"> |
||||
<span class="md-flex__ellipsis"> |
||||
<span class="md-footer-nav__direction"> |
||||
Previous |
||||
</span> |
||||
Fish Slapping |
||||
</span> |
||||
</div> |
||||
</a> |
||||
|
||||
|
||||
</nav> |
||||
</div> |
||||
|
||||
<div class="md-footer-meta md-typeset"> |
||||
<div class="md-footer-meta__inner md-grid"> |
||||
<div class="md-footer-copyright"> |
||||
|
||||
<div class="md-footer-copyright__highlight"> |
||||
Copyright © 2018 Charles Reid, released under the <a href="https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits/src/branch/docs/LICENSE">WTFPL</a>.<br /><br />Many Bothans died to bring us this documentation.<br /><br /> |
||||
</div> |
||||
|
||||
powered by |
||||
<a href="http://www.mkdocs.org">MkDocs</a> |
||||
and |
||||
<a href="https://squidfunk.github.io/mkdocs-material/"> |
||||
Material for MkDocs</a> |
||||
</div> |
||||
|
||||
|
||||
|
||||
</div> |
||||
</div> |
||||
</footer> |
||||
|
||||
</div> |
||||
|
||||
<script src="../assets/javascripts/application.0cf9b500.js"></script> |
||||
|
||||
<script>app.initialize({version:"0.17.3",url:{base:".."}})</script> |
||||
|
||||
<script src="../search/require.js"></script> |
||||
|
||||
<script src="../search/search.js"></script> |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</body> |
||||
</html> |
@ -0,0 +1,28 @@
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> |
||||
|
||||
|
||||
<url> |
||||
<loc>https://github-heroku-attack-rabbits.herokuapp.com/</loc> |
||||
<lastmod>2018-05-25</lastmod> |
||||
<changefreq>daily</changefreq> |
||||
</url> |
||||
|
||||
|
||||
|
||||
<url> |
||||
<loc>https://github-heroku-attack-rabbits.herokuapp.com/fishslap/</loc> |
||||
<lastmod>2018-05-25</lastmod> |
||||
<changefreq>daily</changefreq> |
||||
</url> |
||||
|
||||
|
||||
|
||||
<url> |
||||
<loc>https://github-heroku-attack-rabbits.herokuapp.com/sillywalk/</loc> |
||||
<lastmod>2018-05-25</lastmod> |
||||
<changefreq>daily</changefreq> |
||||
</url> |
||||
|
||||
|
||||
</urlset> |
@ -1,10 +0,0 @@
@@ -1,10 +0,0 @@
|
||||
body { |
||||
background-color: #efefef; |
||||
} |
||||
div.body { |
||||
background-color: #efefef; |
||||
} |
||||
.md-typeset pre { |
||||
background-color: #ccc; |
||||
} |
||||
|
@ -1,33 +0,0 @@
@@ -1,33 +0,0 @@
|
||||
# Custom Domains |
||||
|
||||
If you want to use a custom domain: |
||||
|
||||
* Set up custom domain using Heroku command line interface |
||||
* This will set up a DNS subdomain specifically for your app |
||||
* Point your DNS records to the Heroku DNS subdomain |
||||
|
||||
This also introduces complications with HTTP vs HTTPS: |
||||
OAuth must happen over HTTPS (required by protocol). |
||||
But you the user can control your domain and create |
||||
SSL certificates for it, but Heroku is hosting the app. |
||||
|
||||
You have two options: the free option, and the pay option. |
||||
|
||||
**The pay option:** For $7/mo you can upgrade to hobby nodes, |
||||
which allows you to give Heroku permission to create an SSL |
||||
certificate for your domain. This is the easiest solution |
||||
and requires zero setup, zero certificate management. |
||||
|
||||
**The free option:** You can have your domain (HTTP only) |
||||
forward to Heroku (HTTPS can't be forwarded - that's ***key***). |
||||
When you hit the Heroku domain, it will log the user in to Github. |
||||
When the user logs in successfully, Github will redirect them to |
||||
the callback URL. This callback URL ***MUST*** be HTTPS, so it cannot |
||||
redirect back to your (HTTP-only) custom domain. |
||||
|
||||
That means the userr will, after authenticating with Github, |
||||
always be redirected to `https://my-cool-app.herokuapp.com` |
||||
and never `http://my-cool-custom-domain-that-cannot-be-used-as-a-callback-because-it-is-https-only.com`. |
||||
|
||||
The paid option is much, much simpler in the end |
||||
and will save you $7/mo in setup time alone. |
@ -1,174 +0,0 @@
@@ -1,174 +0,0 @@
|
||||
# Create a Flask App using Flask-Dance |
||||
|
||||
This is the heart of the method. |
||||
|
||||
The best thing to do here is just to walk you through the script. |
||||
|
||||
Import statements: |
||||
|
||||
``` |
||||
import os, json |
||||
from os.path import join, isfile, isdir |
||||
from werkzeug.contrib.fixers import ProxyFix |
||||
from flask import Flask, redirect, url_for, send_from_directory |
||||
from flask_dance.contrib.github import make_github_blueprint, github |
||||
``` |
||||
|
||||
Note that flask-dance adds an OAuth login/callback route |
||||
to your Flask app by creating a `/login` blueprint, |
||||
meaning all the OAuth stuff is just magically available |
||||
via `/login`. |
||||
|
||||
Set paths for static content: |
||||
|
||||
``` |
||||
PROJECT_ROOT = os.path.dirname(os.path.realpath(__file__)) |
||||
STATIC_PATH = 'content' |
||||
``` |
||||
|
||||
Create and configure app. This requires confidential information |
||||
for the Github application you created, specifically the client |
||||
ID and client secret. These are at the very top of the page when |
||||
you visit your app's settings page. |
||||
|
||||
To find this, after you log in, click your profile photo in the |
||||
upper right > Settings > Developer Settings > OAuth Apps > click the |
||||
name for your OAuth app. |
||||
|
||||
``` |
||||
app = Flask(__name__) |
||||
|
||||
# this worked locally, but not on heroku |
||||
app.wsgi_app = ProxyFix(app.wsgi_app) |
||||
|
||||
app.secret_key = os.environ.get("FLASK_SECRET_KEY", "9502861d41e8729c5cae3225920b1b46") |
||||
|
||||
app.config["RESULT_STATIC_PATH"] = STATIC_PATH #os.path.join(PROJECT_ROOT,STATIC_PATH) |
||||
app.config["GITHUB_OAUTH_CLIENT_ID"] = os.environ.get("GITHUB_OAUTH_CLIENT_ID") |
||||
app.config["GITHUB_OAUTH_CLIENT_SECRET"] = os.environ.get("GITHUB_OAUTH_CLIENT_SECRET") |
||||
``` |
||||
|
||||
Now the magic happens: we use flask-dance to create a blueprint |
||||
that has methods and settings all ready to go for us to do the |
||||
OAuth dance. |
||||
|
||||
`make_github_blueprint()` is part of the contrib module of flask-dance. |
||||
There are several similar methods to generate blueprints for |
||||
authenticating with other APIs. |
||||
|
||||
``` |
||||
github_bp = make_github_blueprint( |
||||
client_id = os.environ.get('GITHUB_OAUTH_CLIENT_ID'), |
||||
client_secret = os.environ.get('GITHUB_OAUTH_CLIENT_SECRET'), |
||||
scope='read:org') |
||||
|
||||
app.register_blueprint(github_bp, url_prefix="/login") |
||||
|
||||
contents404 = "<html><body><h1>Status: Error 404 Page Not Found</h1></body></html>" |
||||
contents403 = "<html><body><h1>Status: Error 403 Access Denied</h1></body></html>" |
||||
contents200 = "<html><body><h1>Status: OK 200</h1></body></html>" |
||||
``` |
||||
|
||||
Deal with the `/` route first: |
||||
|
||||
* Check if authorized, if not, redirect them to the login URL |
||||
(magical URLs taken care of magically by our `make_github_blueprint` |
||||
function above) |
||||
* If user is authorized (i.e., if they have gone through the OAuth |
||||
process and given their passsword to Github and been redirected |
||||
to your app with an OAuth token), then the next step is to |
||||
find out some information about them. |
||||
* Use the `github` object to call the Github API directly. |
||||
* Decide what to do from there. |
||||
|
||||
``` |
||||
@app.route('/') |
||||
def index(): |
||||
if not github.authorized: |
||||
return redirect(url_for("github.login")) |
||||
|
||||
resp = github.get("/user/orgs") |
||||
if resp.ok: |
||||
|
||||
all_orgs = resp.json() |
||||
for org in all_orgs: |
||||
if org['login']=='rainbow-mind-machine': |
||||
``` |
||||
|
||||
The next line is important to how the server works: |
||||
if all of the criteria above have been met, we return |
||||
a static file: |
||||
|
||||
``` |
||||
return send_from_directory(STATIC_PATH, 'index.html') |
||||
``` |
||||
|
||||
This is normally "bad practice," and numerous type A people |
||||
on the internet will tell you Flask should not be used for |
||||
serving static files, and that you should use nginx etc., |
||||
but these fail to igonore the following: |
||||
|
||||
* Heroku does not let you run or configure nginx |
||||
* For crying out loud this example is about attack rabbits |
||||
stop taking everything so seriously |
||||
|
||||
Now that we've got that out of the way... |
||||
|
||||
Here's how we serve up static files. This is a total hack, |
||||
but it works. It takes any arbitrary path supplied by the |
||||
user, and attempts to find a corresponding file to serve up |
||||
on disk. |
||||
|
||||
If the user passes a file, then that file is served up. |
||||
|
||||
If the user passes a directory, then `index.html` is served up. |
||||
|
||||
If the user asks for a non-existent file, a 404 error is shown. |
||||
|
||||
If the user is not allowed to view the content, they will face |
||||
the bowel-emptying terrors of the 403 error. |
||||
|
||||
``` |
||||
@app.route('/<path:path>') |
||||
def catch_all(path): |
||||
|
||||
if not github.authorized: |
||||
return redirect(url_for("github.login")) |
||||
|
||||
username = github.get("/user").json()['login'] |
||||
|
||||
rsp = app.config["RESULT_STATIC_PATH"] |
||||
resp = github.get("/user/orgs") |
||||
if resp.ok: |
||||
|
||||
all_orgs = resp.json() |
||||
for org in all_orgs: |
||||
if org['login']=='dcppc': |
||||
|
||||
if(path==''): |
||||
return send_from_directory(rsp, 'index.html') |
||||
|
||||
elif(isdir(join(rsp,path))): |
||||
return send_from_directory(join(rsp,path),'index.html') |
||||
|
||||
elif(isfile(join(rsp,path))): |
||||
return send_from_directory(rsp, path) |
||||
|
||||
else: |
||||
return contents404 |
||||
|
||||
return contents403 |
||||
``` |
||||
|
||||
Last, set a default 404 handler, and run the app: |
||||
|
||||
``` |
||||
@app.errorhandler(404) |
||||
def oops(e): |
||||
return contents404 |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
app.run() |
||||
``` |
||||
|
@ -1,25 +0,0 @@
@@ -1,25 +0,0 @@
|
||||
# Authenticate Users on Github Membership |
||||
|
||||
For the sake of simplicity, will just demonstrate how this |
||||
works for a single route, but you can combine different |
||||
rules into multiple routes to provide access to different |
||||
people on different parts of a site. |
||||
|
||||
Here is the relevant method that serves up `index.html` |
||||
if the user is authenticated: |
||||
|
||||
``` |
||||
@app.route('/') |
||||
def index(): |
||||
if not github.authorized: |
||||
return redirect(url_for("github.login")) |
||||
|
||||
resp = github.get("/user") |
||||
|
||||
if resp.ok: |
||||
return send_from_directory(STATIC_PATH, 'index.html') |
||||
|
||||
return contents403 |
||||
``` |
||||
|
||||
|
@ -1,22 +0,0 @@
@@ -1,22 +0,0 @@
|
||||
# Authenticate Users on Organization or Team Membership |
||||
|
||||
Here is how we can make access to a given page or route |
||||
conditional on membership in an organization (in this |
||||
example, membership in the `rainbow-mind-machine` organization |
||||
is required for access): |
||||
|
||||
``` |
||||
@app.route('/') |
||||
def index(): |
||||
if not github.authorized: |
||||
return redirect(url_for("github.login")) |
||||
|
||||
resp = github.get("/user/orgs") |
||||
if resp.ok: |
||||
|
||||
all_orgs = resp.json() |
||||
for org in all_orgs: |
||||
if org['login']=='rainbow-mind-machine': |
||||
return send_from_directory(STATIC_PATH, 'index.html') |
||||
``` |
||||
|
@ -1,27 +0,0 @@
@@ -1,27 +0,0 @@
|
||||
# Authenticate Users on Other Criteria |
||||
|
||||
Suppose we wanted to do something silly like restrict |
||||
access to a page to users with Github handles that were |
||||
between 5 and 7 letters. |
||||
|
||||
Here is the relevant method that serves up `index.html` |
||||
if the user's Github handle is 5-7 letters long: |
||||
|
||||
``` |
||||
@app.route('/') |
||||
def index(): |
||||
if not github.authorized: |
||||
return redirect(url_for("github.login")) |
||||
|
||||
resp = github.get("/user") |
||||
|
||||
if resp.ok: |
||||
username = resp.json()['login'] |
||||
if len(username)>=5 and len(username)<=7: |
||||
return send_from_directory(STATIC_PATH, 'index.html') |
||||
|
||||
return contents403 |
||||
``` |
||||
|
||||
|
||||
|
@ -1,64 +0,0 @@
@@ -1,64 +0,0 @@
|
||||
# Authenticate Different Users on Different Portions of Site |
||||
|
||||
**NOTE: if you are authenticating using membership on a team, you will need the |
||||
team id of the team you are interested in authenticating against.** |
||||
|
||||
For this example, let's expand the routes we're looking at |
||||
a bit more. |
||||
|
||||
Suppose we have two folders, `team_only` and `org_only`. |
||||
|
||||
The `team_only` folder should only be accessible to your team, `Team Gold`. |
||||
|
||||
The `org_only` folder should only be accessible to your organization, `Colorful Colors`. |
||||
|
||||
The main site (i.e., all other files) should be publicly accessible. |
||||
|
||||
``` |
||||
@app.route('/') |
||||
def index(): |
||||
return send_from_directory(STATIC_PATH, 'index.html') |
||||
|
||||
|
||||
@app.route('/team_only/<path:path>') |
||||
def team_gold(path): |
||||
|
||||
if not github.authorized: |
||||
return redirect(url_for("github.login")) |
||||
|
||||
rsp = app.config["RESULT_STATIC_PATH"] |
||||
|
||||
resp = github.get("/user") |
||||
if resp.ok: |
||||
username = resp.json()['login'] |
||||
|
||||
team_id = 'XXXXX' |
||||
|
||||
resp = github.get("/teams/%s/members/%s"%( team_id, username )) |
||||
if resp.code==204: |
||||
team_gold_dir = os.path.join(STATIC_PATH, 'team_only') |
||||
return send_from_directory(team_gold_dir, 'index.html') |
||||
return contents403 |
||||
|
||||
|
||||
@app.route('/org_only/<path:path>') |
||||
def team_gold(path): |
||||
|
||||
if not github.authorized: |
||||
return redirect(url_for("github.login")) |
||||
|
||||
rsp = app.config["RESULT_STATIC_PATH"] |
||||
|
||||
resp = github.get("/user") |
||||
if resp.ok: |
||||
|
||||
my_org = 'XXXXXXXX' |
||||
|
||||
all_orgs = resp.json() |
||||
for org in all_orgs: |
||||
if org['login']==my_org: |
||||
color_org_dir = os.path.join(STATIC_PATH, 'org_only') |
||||
return send_from_directory(color_org_dir, 'index.html') |
||||
return contents403 |
||||
``` |
||||
|
@ -1,98 +0,0 @@
@@ -1,98 +0,0 @@
|
||||
# Deploying to Heroku |
||||
|
||||
Once we have debugged the Flask app and we are happy with it, |
||||
we are ready to deploy it to Heroku. |
||||
|
||||
|
||||
## Repository Setup |
||||
|
||||
To do this, you should set up your repo as follows: |
||||
|
||||
Clone the repo: |
||||
|
||||
``` |
||||
$ git clone https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits.git |
||||
``` |
||||
|
||||
The repo has the following structure: |
||||
|
||||
``` |
||||
github-heroku-attack-rabbits/ |
||||
LICENSE |
||||
README.md |
||||
mkdocs.yml |
||||
docs/ |
||||
index.md |
||||
... |
||||
mkdocs-material/ |
||||
... |
||||
``` |
||||
|
||||
Now, inside the repo, clone the repo again, |
||||
but this time clone the `heroku-pages` branch |
||||
to the `site/` directory: |
||||
|
||||
``` |
||||
$ cd github-heroku-attack-rabbits/ |
||||
$ git clone -b heroku-pages https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits.git site |
||||
``` |
||||
|
||||
|
||||
## Heroku Deploy Process |
||||
|
||||
To deploy content to Heroku, we add our Heroku project as a git remote |
||||
(see the [heroku](heroku.md) page for how to do that) and then push to |
||||
to the master branch of the heroku remote git repo. Changes are pulled |
||||
in by Heroku and the app is restarted each time you run `git push`. |
||||
|
||||
We walk through the steps below. |
||||
|
||||
|
||||
## Heroku Login |
||||
|
||||
From the `site/` directory containing the contents of the `heroku-pages` branch, |
||||
that is, containing the Python flask app, log in to Heroku: |
||||
|
||||
``` |
||||
$ heroku login |
||||
``` |
||||
|
||||
|
||||
## Add Heroku Remote to `heroku-pages` Branch |
||||
|
||||
Now have Heroku add the proper git remote address: |
||||
|
||||
``` |
||||
$ heroku git:remote -a <heroku-app-name> |
||||
``` |
||||
|
||||
Now you're ready to deploy to Heroku. |
||||
|
||||
|
||||
## Deploy to Heroku |
||||
|
||||
Double check your app is ready, then deploy: |
||||
|
||||
``` |
||||
$ git push heroku heroku-pages:master |
||||
``` |
||||
|
||||
This will push the local branch `heroku-pages` to |
||||
the remote branch `master` on the `heroku` remote. |
||||
This should begin a pre-commit hook where Heroku |
||||
compiles your Python app. You should get the green |
||||
light, if you tested your app locally and everything |
||||
was good to go. |
||||
|
||||
|
||||
## Check Your Heroku App |
||||
|
||||
Your Heroku app will be available at |
||||
|
||||
``` |
||||
https://<heroku-app-name>.herokuapp.com |
||||
``` |
||||
|
||||
You should see your Python flask app |
||||
show up shortly. |
||||
|
@ -1,42 +0,0 @@
@@ -1,42 +0,0 @@
|
||||
# Testing Flask App Locally |
||||
|
||||
We set up the Github App to use a callback of `https://localhost:5000/login/github/authorized` |
||||
so that we could test the app locally. Now it is time to test the app locally. |
||||
|
||||
The application needs access to your Github app id and token. Those are provided |
||||
via the `GITHUB_OAUTH_CLIENT_{ID,SECRET}` environment variables. Set these |
||||
when you run the actual python command to run the server: |
||||
|
||||
``` |
||||
$ GITHUB_OAUTH_CLIENT_ID="xxxxxxx" \ |
||||
GITHUB_OAUTH_CLIENT_SECRET="xxxxxxx" \ |
||||
OAUTHLIB_INSECURE_TRANSPORT=true \ |
||||
python github.py |
||||
``` |
||||
|
||||
This runs the Flask server on port 5000, where it will wait for a visitor. |
||||
The way we have our application written in this example, the main `/` route |
||||
will redirect the user to a Github login screen immediately, but you could |
||||
also present the user with a friendly welcome page when they go to `/`, |
||||
and only redirect them to the Github login prompt when they visit a |
||||
URL like `/login` or `/auth`. |
||||
|
||||
Once you run the above command, open the following URL in your browser: |
||||
|
||||
``` |
||||
http://localhost:5000/ |
||||
``` |
||||
|
||||
**NOTE: Make sure you are logged out of Github and that you clear your cookies |
||||
if you are already logged in as one user and wish to authenticate as another. |
||||
The login is _very_ persistent so you may need to close and re-open your browser.** |
||||
|
||||
Visiting the address above will result in your being redirected to a Github |
||||
login page. Once you login, Github will redirect the user back to the |
||||
github-heroku-attack-rabbits application with a token that the application |
||||
can use to perform actions on behalf of the user. |
||||
|
||||
## Next Step? |
||||
|
||||
If the app works, the next step is to deploy to Heroku. |
||||
|
@ -1,57 +0,0 @@
@@ -1,57 +0,0 @@
|
||||
## get started with github |
||||
|
||||
We mentioned on the [heroku](heroku.md) page that heroku |
||||
creates a remote git repository to hold the files you |
||||
want to host. |
||||
|
||||
To use the contents of a Github repository on Heroku, |
||||
just treat it like another git remote, no special setup |
||||
is needed. |
||||
|
||||
However, to set up your Github-Heroku attack rabbits to |
||||
authenticate a user via Github, and mercilessly attack |
||||
all intruders, you must create a Github OAuth App. |
||||
|
||||
### Creating Github OAuth App |
||||
|
||||
Log into Github |
||||
|
||||
Go to Settings |
||||
|
||||
Click "Developer Settings" on the left side |
||||
|
||||
Click "New OAuth App" button in upper right |
||||
|
||||
**What do these settings mean?** |
||||
|
||||
* **Application name** is what will be shown to users when they visit |
||||
a page protected by the attack rabbits and are prompted for |
||||
their password by Github. |
||||
|
||||
* **Homepage URL/Application description** are for users who want to know more |
||||
about your killer attack rabbit Github app |
||||
|
||||
* **Authorization callback URL** is the URL that the users will be sent to |
||||
once they authenticate with Github and they are granted an OAuth token. |
||||
This is the magic ingredient that allows you to take actions on behalf |
||||
of the account logging in. |
||||
|
||||
In this guide we'll cover the case of checking membership in organizations or teams, |
||||
but what your attack rabbits end up doing to determine if a user is allowed to |
||||
access your secret pages is up to you. |
||||
|
||||
## Values to use |
||||
|
||||
You should set your own values for the **name** and **description** fields. |
||||
|
||||
The **home URL** is not actually necessary - it is simply provided for users to |
||||
get more information about your app. |
||||
|
||||
The most important is the **callback URL**, which should be set to: |
||||
|
||||
http://localhost:5000/login/github/authorized |
||||
|
||||
This is for testing locally *only*. |
||||
|
||||
Don't use HTTPS in the callback URL! |
||||
|
@ -1,67 +0,0 @@
@@ -1,67 +0,0 @@
|
||||
# create heroku app |
||||
|
||||
|
||||
### Heroku toolbelt |
||||
|
||||
Heroku offers a really nice command line interface tool called |
||||
Heroku Toolbelt. It is available through Homebrew and Aptitude: |
||||
|
||||
``` |
||||
$ brew install heroku # from Mac |
||||
|
||||
$ apt-get install heroku # from Ubuntu |
||||
``` |
||||
|
||||
It is then available on the command line as `heroku`. |
||||
|
||||
The first thing you should do is authenticate with |
||||
your Heroku account by running |
||||
|
||||
``` |
||||
$ heroku login |
||||
``` |
||||
|
||||
We will use this command line application for the following tasks: |
||||
|
||||
* Create a git remote to point to the right Heroku git remote location |
||||
* Set environment variables (for e.g. secret keys) on the remote Heroku instance |
||||
* Get information (logs, status, etc.) about your Heroku app |
||||
|
||||
We will cover these commands as they come up. |
||||
|
||||
|
||||
### Create heroku app |
||||
|
||||
Start by creating a heroku app. |
||||
|
||||
* Each heroku app must have a unique name |
||||
* Each heroku app creates a remote git repo |
||||
* Master branch is what Heroku deploys publicly on herokuapps.com |
||||
* You will also need heroku CLI to link your github repo to your heroku app |
||||
|
||||
|
||||
### Where heroku app lives |
||||
|
||||
Suppose you are creating an app called `my-cool-app` |
||||
on heroku. Then your application will be hosted by |
||||
Heroku and will be available at the URL: |
||||
|
||||
``` |
||||
https://my-cool-app.herokuapp.com |
||||
``` |
||||
|
||||
### How heroku apps works |
||||
|
||||
If you have used Github Pages before, Heroku uses a similar |
||||
model (live hosting one particular branch of a git repository). |
||||
However, Heroku is different because you can run dynamic scripts |
||||
using Python, Ruby, PHP, etc. |
||||
|
||||
To change the content of your Heroku app, just change the contents |
||||
of the repository, and push to master (push to the master branch of |
||||
the remote Heroku repository). |
||||
|
||||
You will need to structure your repository carefully. |
||||
That's what this page is here to help you do. |
||||
Heroku can figure out the rest from there. |
||||
|
Before Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 46 KiB |
@ -1,92 +0,0 @@
@@ -1,92 +0,0 @@
|
||||
# github-heroku-attack-rabbits |
||||
|
||||
## What's this business all about, then? |
||||
|
||||
This repository helps you put access control into place to protect your secret pages by (deep breath): |
||||
|
||||
hosting your secret page of static and/or dynamic content by using a free Heroku app |
||||
running a Python Flask server that uses Flask-Dance to authenticate visitors |
||||
with Github using OAuth which allows you fine-grained access control for your pages |
||||
using user attributes like organization or team membership or even things like how many |
||||
vowels a user has in their username. |
||||
|
||||
Also, did I mention the attack rabbits? |
||||
|
||||
![warning: attack rabbits ahead](img/warning.png) |
||||
|
||||
|
||||
## Where is everything? |
||||
|
||||
Final pages: |
||||
|
||||
* The finished product (pages on Heroku protected by attack rabbits) |
||||
is at [github-heroku-attack-rabbits.herokuapp.com](https://github-heroku-attack-rabbits.herokuapp.com) |
||||
|
||||
* The documentation is at [pages.charlesreid1.com/github-heroku-attack-rabbits](https://pages.charlesreid1.com/github-heroku-attack-rabbits) |
||||
|
||||
Two branches in this repo compose the github-heroku-attack-rabbits documentation: |
||||
|
||||
* (**YOU ARE HERE**) The [docs](https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits/src/branch/docs) branch |
||||
contains the files needed to generate the |
||||
[github-heroku-attack-rabbits documentation site](https://pages.charlesreid1.com/github-heroku-attack-rabbits). |
||||
|
||||
* The [gh-pages](https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits/src/branch/gh-pages) branch |
||||
contains the static files generated from the documentation. |
||||
The contents of this branch compose the |
||||
[github-heroku-attack-rabbits documentation site](https://pages.charlesreid1.com/github-heroku-attack-rabbits). |
||||
|
||||
Two branches illustrate github-heroku-attack-rabbits in practice: |
||||
|
||||
* The [secret](https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits/src/branch/secret) branch contains the files needed to create the secret page. |
||||
This repository is public, so obviously these aren't *actually* secret, |
||||
but in practice this would be in a protected repository. |
||||
|
||||
* The [heroku-pages](https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits/src/branch/heroku-pages) branch |
||||
contains the content that is actually pushed to Heroku - that is, |
||||
the final Flask app. |
||||
|
||||
|
||||
## Contents |
||||
|
||||
An overview of the steps: |
||||
|
||||
[Get Started with Heroku](heroku.md) |
||||
|
||||
[Get Started with Github](github.md) |
||||
|
||||
[Initialize Repository: Branches](repo.md) |
||||
|
||||
[Create a Flask App using Flask-Dance](flask.md) |
||||
|
||||
* [Authenticate users based on Github membership only](flask_auth_github.md) |
||||
* [Authenticate users based on organization or team membership](flask_auth_org.md) |
||||
* [Authenticate users based on some other criteria](flask_auth_other.md) |
||||
* [Protection portions of the site](flask_auth_portions.md) |
||||
|
||||
[Test Flask App Locally](flask_local.md) |
||||
|
||||
[Deploying Flask App to Heroku](flask_heroku.md) |
||||
|
||||
[Custom Domains](custom_domains.md) |
||||
|
||||
|
||||
## Links |
||||
|
||||
Python software used: |
||||
|
||||
* [Flask](http://flask.pocoo.org/) |
||||
* [Flask-dance](https://github.com/singingwolfboy/flask-dance) |
||||
* [Flask-dance-github](https://github.com/singingwolfboy/flask-dance-github) |
||||
* [mkdocs-material (documentation theme)](https://github.com/squidfunk/mkdocs-material) |
||||
* [mkdocs (documentation)](http://www.mkdocs.org/) |
||||
|
||||
Commercial services: |
||||
|
||||
* [Heroku](https://heroku.com) |
||||
* [Github](https://github.com) |
||||
|
||||
|
||||
## License |
||||
|
||||
This is released under the [WTFPL](https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits/src/branch/docs/LICENSE). |
||||
|
@ -1,110 +0,0 @@
@@ -1,110 +0,0 @@
|
||||
# Initialize Git Repository |
||||
|
||||
Let's talk through how a repository should be laid out |
||||
if we're going to be hosting a Flask app on Heroku. |
||||
|
||||
## Branches |
||||
|
||||
We will need a minimum of two branches. Here we specify |
||||
the names that these branches will have in your Github repo, |
||||
**which is different from the names of the branches on Heroku**: |
||||
|
||||
Branches on Github: |
||||
|
||||
* `heroku-pages` - this branch contains the content that Heroku will host. |
||||
Specifically, it contains the Flask application in a `.py` file, |
||||
and a few other files to help Heroku determine how to run the app |
||||
and what to install. |
||||
|
||||
* `master` - this branch contains the content used to generate the documentation |
||||
and page content that is being hosted behind the Heroku attack sheep. |
||||
The documentation you are reading right now is from the master branch, |
||||
and was made with `mkdocs`. |
||||
|
||||
On Heroku, we only have a single branch: |
||||
|
||||
* `master` (Heroku) maps to `heroku-pages` (Github) |
||||
|
||||
## Repo Layout |
||||
|
||||
Let's talk about the layout of the repository. |
||||
|
||||
If you wish to build the site in order to deploy it to Heroku, |
||||
you should clone the `master` branch (preparing to make the |
||||
content for your attack sheep-protected page): |
||||
|
||||
``` |
||||
$ git clone -b master https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits.git |
||||
$ cd github-heroku-attack-rabbits |
||||
``` |
||||
|
||||
Once you are *inside* the master branch, clone the repo again, |
||||
but this time clone the `heroku-pages` branch, and clone it |
||||
to the `site/` folder: |
||||
|
||||
``` |
||||
$ git clone -b heroku-pages https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits.git site |
||||
$ cd site |
||||
``` |
||||
|
||||
Now you will want to set up the Heroku remote: |
||||
|
||||
``` |
||||
$ heroku git:remote -a my-cool-project |
||||
``` |
||||
|
||||
The layout should now be: |
||||
|
||||
``` |
||||
my-cool-project-repo/ <-- my-cool-project repo pointing to master branch |
||||
|
||||
docs/ \ |
||||
index.md | |
||||
heroku.md | <-- mkdocs files |
||||
... | (can use any static content generator: |
||||
| pelican, sphinx, etc.) |
||||
mkdocs.yml / |
||||
|
||||
site/ <-- my-cool-project repo pointing to heroku-pages branch |
||||
|
||||
Procfile \ |
||||
github.py | <-- heroku python app files |
||||
requirements.txt | (can also use ruby, php, js, etc.) |
||||
runtime.txt | (can also use ruby, php, js, etc.) |
||||
... / |
||||
|
||||
content/ \ |
||||
index.html | <-- static content hosted by Flask |
||||
sitemap.xml | |
||||
... / |
||||
``` |
||||
|
||||
|
||||
## Workflow |
||||
|
||||
Once you have things set up according to the instructions and diagram above, |
||||
you're ready to run the push-to-deploy workflow and start running your secret |
||||
site on Heroku. |
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,142 @@
@@ -0,0 +1,142 @@
|
||||
import os, json |
||||
from werkzeug.contrib.fixers import ProxyFix |
||||
from flask import Flask, redirect, url_for, send_from_directory |
||||
from flask_dance.contrib.github import make_github_blueprint, github |
||||
from os.path import join, isfile, isdir |
||||
|
||||
# helpful: |
||||
# https://tinyurl.com/yddsw4pg |
||||
|
||||
PROJECT_ROOT = os.path.dirname(os.path.realpath(__file__)) |
||||
STATIC_PATH = 'content' |
||||
|
||||
app = Flask(__name__) |
||||
|
||||
# this worked locally, but not on heroku |
||||
app.wsgi_app = ProxyFix(app.wsgi_app) |
||||
|
||||
app.secret_key = os.environ.get("FLASK_SECRET_KEY", "9502861d41e8729c5cae3225920b1b46") |
||||
|
||||
app.config["RESULT_STATIC_PATH"] = STATIC_PATH #os.path.join(PROJECT_ROOT,STATIC_PATH) |
||||
app.config["GITHUB_OAUTH_CLIENT_ID"] = os.environ.get("GITHUB_OAUTH_CLIENT_ID") |
||||
app.config["GITHUB_OAUTH_CLIENT_SECRET"] = os.environ.get("GITHUB_OAUTH_CLIENT_SECRET") |
||||
|
||||
github_bp = make_github_blueprint( |
||||
client_id = os.environ.get('GITHUB_OAUTH_CLIENT_ID'), |
||||
client_secret = os.environ.get('GITHUB_OAUTH_CLIENT_SECRET'), |
||||
scope='read:org') |
||||
|
||||
app.register_blueprint(github_bp, url_prefix="/login") |
||||
|
||||
contents200 = """ |
||||
<html><body> |
||||
<div class="header header-20x"> |
||||
<h1>Status: OK 200</h1> |
||||
</div> |
||||
<div class="body attack attack-rabbits"> |
||||
<p>You found a public page, so you're safe for now. Run away while you still can.</p> |
||||
<p>Otherwise... the attack rabbits may find you yet.</p> |
||||
<img src="../img/warning.png" /> |
||||
</div> |
||||
</body></html> |
||||
""" |
||||
|
||||
contents403 = """ |
||||
<html><body> |
||||
<div class="header header-40x"> |
||||
<h1>Status: Error 403 Access Denied</h1> |
||||
</div> |
||||
<div class="body attack attack-rabbits"> |
||||
<p>Access Denied!</p> |
||||
<p>Attack rabbits, attack! Prepare to meet your fate, |
||||
sneaky unauthorized intruder, at the hands of one of |
||||
the nastiest, most horrible, gnashing teeth, and fangs, |
||||
and little claws like daggers -</p> |
||||
<img src="../img/attack-rabbits.png" /> |
||||
</div> |
||||
</body></html> |
||||
""" |
||||
|
||||
contents404 = """ |
||||
<html><body> |
||||
<div class="header header-40x"> |
||||
<h1>Status: Error 404 Page Not Found</h1> |
||||
</div> |
||||
<div class="body attack attack-rabbits"> |
||||
<p>The resource you requested could not be found.</p> |
||||
<p>The attack rabbits are circling, eyeing you suspiciously.</p> |
||||
<img src="../img/attack-rabbit.png" /> |
||||
</div> |
||||
</body></html> |
||||
""" |
||||
|
||||
@app.route('/') |
||||
def index(): |
||||
""" |
||||
the index, anybody can use |
||||
""" |
||||
return send_from_directory(STATIC_PATH, 'index.html') |
||||
|
||||
|
||||
@app.route('/fishslap/') |
||||
def fishslap_even(): |
||||
if not github.authorized: |
||||
return redirect(url_for("github.login")) |
||||
|
||||
resp = github.get("/user") |
||||
if resp.ok: |
||||
username = resp.json()['login'] |
||||
if even_vowels(username): |
||||
#return "Hello {username}".format(username=username) |
||||
fishslap = os.path.join(STATIC_PATH,'fishslap') |
||||
return send_from_directory(fishslap, 'index.html') |
||||
|
||||
return contents403 |
||||
|
||||
|
||||
@app.route('/sillywalk/') |
||||
def sillywalk_odd(): |
||||
if not github.authorized: |
||||
return redirect(url_for("github.login")) |
||||
|
||||
resp = github.get("/user") |
||||
if resp.ok: |
||||
username = resp.json()['login'] |
||||
if not even_vowels(username): |
||||
#return "Hello {username}".format(username=username) |
||||
sillywalk = os.path.join(STATIC_PATH,'sillywalk') |
||||
return send_from_directory(sillywalk, 'index.html') |
||||
|
||||
return contents403 |
||||
|
||||
|
||||
@app.route('/<path:path>') |
||||
def all_github_users_welcome(path): |
||||
""" |
||||
all other paths are public for anybody too |
||||
""" |
||||
return send_from_directory(STATIC_PATH, path) |
||||
|
||||
|
||||
@app.errorhandler(404) |
||||
def oops(e): |
||||
return contents404 |
||||
|
||||
|
||||
def even_vowels(my_string): |
||||
""" |
||||
Boolean: are there an even number of vowels in my_string? |
||||
""" |
||||
i = 0 |
||||
for c in my_string: |
||||
if c in list('aeiou'): |
||||
i += 1 |
||||
if i%2==0: |
||||
return True |
||||
else: |
||||
return False |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
app.run() |
||||
|
@ -1 +0,0 @@
@@ -1 +0,0 @@
|
||||
Subproject commit ff95dcb8463eb5f8f65b14c3d145afae21671ad9 |
@ -1,50 +0,0 @@
@@ -1,50 +0,0 @@
|
||||
site_name: github-heroku-attack-rabbits |
||||
site_url: https://pages.charlesreid1.com/github-heroku-attack-rabbits |
||||
repo_name: charlesreid1/github-heroku-attack-rabbits |
||||
repo_url: https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits |
||||
edit_uri: "" |
||||
|
||||
|
||||
pages: |
||||
- 'Index' : 'index.md' |
||||
- 'Get Started with Heroku': 'heroku.md' |
||||
- 'Get Started with Github': 'github.md' |
||||
- 'Initialize Repository: Branches': 'repo.md' |
||||
- 'Create a Flask App using Flask-Dance': 'flask.md' |
||||
- 'Flask': |
||||
- 'Authenticate users based on Github membership only': 'flask_auth_github.md' |
||||
- 'Authenticate users based on organization or team membership': 'flask_auth_org.md' |
||||
- 'Authenticate users based on some other criteria': 'flask_auth_other.md' |
||||
- 'Protection portions of the site': 'flask_auth_portions.md' |
||||
- 'Test Flask App Locally': 'flask_local.md' |
||||
- 'Deploying Flask App to Heroku': 'flask_heroku.md' |
||||
- 'Custom Domains': 'custom_domains.md' |
||||
|
||||
copyright: 'Copyright © 2018 Charles Reid, released under the <a href="https://git.charlesreid1.com/charlesreid1/github-heroku-attack-rabbits/src/branch/docs/LICENSE">WTFPL</a>.<br /><br />Many Bothans died to bring us this documentation.<br /><br />' |
||||
|
||||
docs_dir: docs |
||||
site_dir: site |
||||
|
||||
theme: |
||||
|
||||
name: null |
||||
custom_dir: 'mkdocs-material/material' |
||||
|
||||
# pretty colors! see https://squidfunk.github.io/mkdocs-material/getting-started/#primary-colors |
||||
palette: |
||||
primary: 'blue grey' |
||||
accent: 'blue grey' |
||||
|
||||
logo: 'img/bunny.png' |
||||
|
||||
### # fun logos! see https://material.io/icons/ |
||||
### logo: |
||||
### icon: 'lock' |
||||
|
||||
font: |
||||
text: 'Questrial' |
||||
code: 'Roboto Mono' |
||||
|
||||
# this will add docs/css/custom.css to all your docs |
||||
extra_css: |
||||
- css/custom.css |