├── .bundle └── config ├── docs ├── binder-32.png ├── fonts │ ├── AtlasGrotesk-Thin.otf │ ├── SharpGroteskBook20.otf │ ├── SharpGroteskBook22.otf │ ├── SharpGroteskBook25.otf │ ├── SharpGroteskDBBlack23.otf │ ├── SharpGroteskDBBook20.ttf │ ├── SharpGroteskDBBook22.ttf │ ├── SharpGroteskDBLight20.otf │ ├── SharpGroteskDBMedium12.otf │ ├── SharpGroteskDBMedium20.ttf │ ├── SharpGroteskDBMedium22.ttf │ ├── SharpGroteskDBSmBold20.ttf │ ├── SharpGroteskDBSmBold22.ttf │ ├── SharpGroteskMedium20.otf │ ├── SharpGroteskMedium22.otf │ ├── SharpGroteskMedium25.otf │ ├── SharpGroteskSmBold22.otf │ ├── AtlasGrotesk-Light-Web.woff2 │ ├── AtlasGrotesk-Medium-Web.woff2 │ ├── AtlasGrotesk-Regular-Web.woff2 │ ├── SharpGroteskDBBookItalic20.ttf │ ├── SharpGroteskDBMediumItalic20.ttf │ ├── SharpGroteskDBSmBoldItalic20.ttf │ └── AtlasGrotesk-RegularItalic-Web.woff2 ├── font-sharp-grotesk.css ├── featherlight.min.css ├── featherlight.gallery.min.css ├── featherlight.gallery.min.js ├── featherlight.min.js ├── binder.css ├── sre-_appendix.html ├── glossary.html └── swe-_appendix.html ├── .gitignore ├── README.md ├── allow-list.txt ├── clean.py └── LICENSE /.bundle/config: -------------------------------------------------------------------------------- 1 | --- 2 | BUNDLE_PATH: "vendor/bundle" 3 | -------------------------------------------------------------------------------- /docs/binder-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-career-framework/HEAD/docs/binder-32.png -------------------------------------------------------------------------------- /docs/fonts/AtlasGrotesk-Thin.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-career-framework/HEAD/docs/fonts/AtlasGrotesk-Thin.otf -------------------------------------------------------------------------------- /docs/fonts/SharpGroteskBook20.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-career-framework/HEAD/docs/fonts/SharpGroteskBook20.otf -------------------------------------------------------------------------------- /docs/fonts/SharpGroteskBook22.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-career-framework/HEAD/docs/fonts/SharpGroteskBook22.otf -------------------------------------------------------------------------------- /docs/fonts/SharpGroteskBook25.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-career-framework/HEAD/docs/fonts/SharpGroteskBook25.otf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | _site 2 | .sass-cache 3 | .jekyll-cache 4 | .jekyll-metadata 5 | vendor 6 | .DS_Store 7 | 8 | /env/ 9 | /raw/ 10 | -------------------------------------------------------------------------------- /docs/fonts/SharpGroteskDBBlack23.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-career-framework/HEAD/docs/fonts/SharpGroteskDBBlack23.otf -------------------------------------------------------------------------------- /docs/fonts/SharpGroteskDBBook20.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-career-framework/HEAD/docs/fonts/SharpGroteskDBBook20.ttf -------------------------------------------------------------------------------- /docs/fonts/SharpGroteskDBBook22.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-career-framework/HEAD/docs/fonts/SharpGroteskDBBook22.ttf -------------------------------------------------------------------------------- /docs/fonts/SharpGroteskDBLight20.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-career-framework/HEAD/docs/fonts/SharpGroteskDBLight20.otf -------------------------------------------------------------------------------- /docs/fonts/SharpGroteskDBMedium12.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-career-framework/HEAD/docs/fonts/SharpGroteskDBMedium12.otf -------------------------------------------------------------------------------- /docs/fonts/SharpGroteskDBMedium20.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-career-framework/HEAD/docs/fonts/SharpGroteskDBMedium20.ttf -------------------------------------------------------------------------------- /docs/fonts/SharpGroteskDBMedium22.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-career-framework/HEAD/docs/fonts/SharpGroteskDBMedium22.ttf -------------------------------------------------------------------------------- /docs/fonts/SharpGroteskDBSmBold20.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-career-framework/HEAD/docs/fonts/SharpGroteskDBSmBold20.ttf -------------------------------------------------------------------------------- /docs/fonts/SharpGroteskDBSmBold22.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-career-framework/HEAD/docs/fonts/SharpGroteskDBSmBold22.ttf -------------------------------------------------------------------------------- /docs/fonts/SharpGroteskMedium20.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-career-framework/HEAD/docs/fonts/SharpGroteskMedium20.otf -------------------------------------------------------------------------------- /docs/fonts/SharpGroteskMedium22.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-career-framework/HEAD/docs/fonts/SharpGroteskMedium22.otf -------------------------------------------------------------------------------- /docs/fonts/SharpGroteskMedium25.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-career-framework/HEAD/docs/fonts/SharpGroteskMedium25.otf -------------------------------------------------------------------------------- /docs/fonts/SharpGroteskSmBold22.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-career-framework/HEAD/docs/fonts/SharpGroteskSmBold22.otf -------------------------------------------------------------------------------- /docs/fonts/AtlasGrotesk-Light-Web.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-career-framework/HEAD/docs/fonts/AtlasGrotesk-Light-Web.woff2 -------------------------------------------------------------------------------- /docs/fonts/AtlasGrotesk-Medium-Web.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-career-framework/HEAD/docs/fonts/AtlasGrotesk-Medium-Web.woff2 -------------------------------------------------------------------------------- /docs/fonts/AtlasGrotesk-Regular-Web.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-career-framework/HEAD/docs/fonts/AtlasGrotesk-Regular-Web.woff2 -------------------------------------------------------------------------------- /docs/fonts/SharpGroteskDBBookItalic20.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-career-framework/HEAD/docs/fonts/SharpGroteskDBBookItalic20.ttf -------------------------------------------------------------------------------- /docs/fonts/SharpGroteskDBMediumItalic20.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-career-framework/HEAD/docs/fonts/SharpGroteskDBMediumItalic20.ttf -------------------------------------------------------------------------------- /docs/fonts/SharpGroteskDBSmBoldItalic20.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-career-framework/HEAD/docs/fonts/SharpGroteskDBSmBoldItalic20.ttf -------------------------------------------------------------------------------- /docs/fonts/AtlasGrotesk-RegularItalic-Web.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dropbox/dbx-career-framework/HEAD/docs/fonts/AtlasGrotesk-RegularItalic-Web.woff2 -------------------------------------------------------------------------------- /docs/font-sharp-grotesk.css: -------------------------------------------------------------------------------- 1 | @font-face {font-family: 'Sharp Grotesk Book 20'; font-weight: 300; src: url("../fonts/SharpGroteskDBBook20.ttf");} 2 | @font-face {font-family: 'Sharp Grotesk Book 22'; font-weight: 300; src: url("../fonts/SharpGroteskDBBook22.ttf");} 3 | @font-face {font-family: 'Sharp Grotesk Medium 20'; font-weight: 500; src: url("../fonts/SharpGroteskDBMedium20.ttf");} 4 | @font-face {font-family: 'Sharp Grotesk Medium 22'; font-weight: 500; src: url("../fonts/SharpGroteskDBMedium22.ttf");} 5 | @font-face {font-family: 'Sharp Grotesk SemiBold 20'; font-weight: 500; src: url("../fonts/SharpGroteskDBSmBold20.ttf");} 6 | @font-face {font-family: 'Sharp Grotesk SemiBold 22'; font-weight: 500; src: url("../fonts/SharpGroteskDBSmBold22.ttf");} 7 | 8 | @font-face {font-family: 'Sharp Grotesk DB Black 23'; font-weight: 500; src: url("../fonts/SharpGroteskDBBlack23.otf");} 9 | @font-face {font-family: 'Sharp Grotesk DB Light 20'; font-weight: 500; src: url("../fonts/SharpGroteskDBLight20.otf");} 10 | @font-face {font-family: 'Sharp Grotesk DB Medium 12'; font-weight: 500; src: url("../fonts/SharpGroteskDBMedium12.otf");} 11 | -------------------------------------------------------------------------------- /docs/featherlight.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Featherlight - ultra slim jQuery lightbox 3 | * Version 1.7.1 - http://noelboss.github.io/featherlight/ 4 | * 5 | * Copyright 2017, Noël Raoul Bossart (http://www.noelboss.com) 6 | * MIT Licensed. 7 | **/ 8 | @media all{.featherlight{display:none;position:fixed;top:0;right:0;bottom:0;left:0;z-index:2147483647;text-align:center;white-space:nowrap;cursor:pointer;background:#333;background:rgba(0,0,0,0)}.featherlight:last-of-type{background:rgba(0,0,0,.8)}.featherlight:before{content:'';display:inline-block;height:100%;vertical-align:middle}.featherlight .featherlight-content{position:relative;text-align:left;vertical-align:middle;display:inline-block;overflow:auto;padding:25px 25px 0;border-bottom:25px solid transparent;margin-left:5%;margin-right:5%;max-height:95%;background:#fff;cursor:auto;white-space:normal}.featherlight .featherlight-inner{display:block}.featherlight .featherlight-close-icon{position:absolute;z-index:9999;top:0;right:0;line-height:25px;width:25px;cursor:pointer;text-align:center;font-family:Arial,sans-serif;background:#fff;background:rgba(255,255,255,.3);color:#000;border:0;padding:0}.featherlight .featherlight-close-icon::-moz-focus-inner{border:0;padding:0}.featherlight .featherlight-image{width:100%}.featherlight-iframe .featherlight-content{border-bottom:0;padding:0;-webkit-overflow-scrolling:touch;overflow-y:scroll}.featherlight iframe{border:0}.featherlight *{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}}@media only screen and (max-width:1024px){.featherlight .featherlight-content{margin-left:0;margin-right:0;max-height:98%;padding:10px 10px 0;border-bottom:10px solid transparent}} -------------------------------------------------------------------------------- /docs/featherlight.gallery.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Featherlight Gallery – an extension for the ultra slim jQuery lightbox 3 | * Version 1.7.1 - http://noelboss.github.io/featherlight/ 4 | * 5 | * Copyright 2017, Noël Raoul Bossart (http://www.noelboss.com) 6 | * MIT Licensed. 7 | **/ 8 | @media all{.featherlight-next,.featherlight-previous{display:block;position:absolute;top:25px;right:25px;bottom:0;left:80%;cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background:rgba(0,0,0,0)}.featherlight-previous{left:25px;right:80%}.featherlight-next:hover,.featherlight-previous:hover{background:rgba(255,255,255,.25)}.featherlight-next span,.featherlight-previous span{display:none;position:absolute;top:50%;left:5%;width:82%;text-align:center;font-size:80px;line-height:80px;margin-top:-40px;text-shadow:0 0 5px #fff;color:#fff;font-style:normal;font-weight:400}.featherlight-next span{right:5%;left:auto}.featherlight-next:hover span,.featherlight-previous:hover span{display:inline-block}.featherlight-loading .featherlight-next,.featherlight-loading .featherlight-previous,.featherlight-swipe-aware .featherlight-next,.featherlight-swipe-aware .featherlight-previous{display:none}}@media only screen and (max-device-width:1024px){.featherlight-next:hover,.featherlight-previous:hover{background:0 0}.featherlight-next span,.featherlight-previous span{display:block}}@media only screen and (max-width:1024px){.featherlight-next,.featherlight-previous{top:10px;right:10px;left:85%}.featherlight-previous{left:10px;right:85%}.featherlight-next span,.featherlight-previous span{margin-top:-30px;font-size:40px}} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dropbox Engineering Career Framework 2 | 3 | Visit [our career framework site](https://dropbox.github.io/dbx-career-framework/). 4 | 5 | # Background 6 | 7 | This repo has two parts: 8 | 9 | 1. A public version of drl/eng-career, located in the `docs` directory 10 | 2. A `cleaner.py` script that converts drl/eng-career into the [public 11 | version](https://dropbox.github.io/dbx-career-framework/) 12 | 13 | For complete instructions on updating and publishing the framework, 14 | visit the appbox repo containing the source and internally hosted version. 15 | 16 | # Setup 17 | 18 | To run `cleaner.py`: 19 | 20 | 1. (One time) Create a virtual env. for the project: `python3 -m venv env` 21 | 2. Active the virtual env: `source env/bin/activate` 22 | 3. (One time) `python3 -m pip install beautifulsoup4` 23 | 3. Go to drl/eng-career and get your session cookie (using the browser's developer tools) 24 | 4. Download the binder: `wget --header "Cookie: sessionid=SESSION_ID" --mirror --convert-links 25 | --adjust-extension --page-requisites --no-parent -P raw --cut-dirs=3 26 | https://app.dropboxer.net/binder/eng-career/` 27 | 5. Run the "cleaner": `python3 clean.py raw/app.dropboxer.net ./docs --allow_file allow-list.txt` 28 | 29 | Review any changes to the site made by the script and open a pull request. 30 | 31 | # License 32 | 33 | Unless otherwise noted: 34 | 35 | ``` 36 | Copyright (c) 2021 Dropbox, Inc. 37 | 38 | Licensed under the Apache License, Version 2.0 (the "License"); 39 | you may not use this file except in compliance with the License. 40 | You may obtain a copy of the License at 41 | 42 | http://www.apache.org/licenses/LICENSE-2.0 43 | 44 | Unless required by applicable law or agreed to in writing, software 45 | distributed under the License is distributed on an "AS IS" BASIS, 46 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 47 | See the License for the specific language governing permissions and 48 | limitations under the License. 49 | 50 | ``` 51 | -------------------------------------------------------------------------------- /allow-list.txt: -------------------------------------------------------------------------------- 1 | index.js 2 | index.html 3 | overview.html 4 | what_is_impact.html 5 | ic1_software_engineer.html 6 | ic2_software_engineer.html 7 | ic3_software_engineer.html 8 | ic4_software_engineer.html 9 | ic5_staff_software_engineer.html 10 | ic6_principal_software_engineer.html 11 | ic7_senior_principal_software_engineer.html 12 | swe-_appendix.html 13 | ic2_quality_engineer.html 14 | ic3_quality_engineer.html 15 | ic4_quality_engineer.html 16 | ic5_staff_quality_engineer.html 17 | qe-_appendix.html 18 | ic2_sdet.html 19 | ic3_sdet.html 20 | ic4_sdet.html 21 | ic5_staff_sdet.html 22 | ic1_reliability_engineer.html 23 | ic2_reliability_engineer.html 24 | ic3_reliability_engineer.html 25 | ic4_reliability_engineer.html 26 | ic5_staff_reliability_engineer.html 27 | ic6_principal_reliability_engineer.html 28 | ic7_senior_principal_reliability_engineer.html 29 | sre-_appendix.html 30 | ic1_machine_learning_engineer.html 31 | ic2_machine_learning_engineer.html 32 | ic3_machine_learning_engineer.html 33 | ic4_machine_learning_engineer.html 34 | ic5_staff_machine_learning_engineer.html 35 | ic1_data_engineer.html 36 | ic2_data_engineer.html 37 | ic3_data_engineer.html 38 | ic4_data_engineer.html 39 | ic5_staff_data_engineer.html 40 | de-_appendix.html 41 | mle-_appendix.html 42 | ic2_security_engineer.html 43 | ic3_security_engineer.html 44 | ic4_security_engineer.html 45 | ic5_staff_security_engineer.html 46 | ic6_principal_security_engineer.html 47 | se-_appendix.html 48 | ic1_technical_program_manager.html 49 | ic2_technical_program_manager.html 50 | ic3_technical_program_manager.html 51 | ic4_technical_program_manager.html 52 | ic5_staff_technical_program_manager.html 53 | ic6_principal_technical_program_manager.html 54 | tpm-_appendix.html 55 | m3_engineering_manager.html 56 | m4_engineering_manager.html 57 | m5_senior_engineering_manager.html 58 | m6_engineering_director.html 59 | m7_senior_engineering_director.html 60 | promotion_principles.html 61 | promotion_guidelines.html 62 | archetypes_behaviors.html 63 | cr_clarifications_and_myths.html 64 | dropbox_values.html 65 | glossary.html 66 | -------------------------------------------------------------------------------- /docs/featherlight.gallery.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Featherlight Gallery – an extension for the ultra slim jQuery lightbox 3 | * Version 1.7.1 - http://noelboss.github.io/featherlight/ 4 | * 5 | * Copyright 2017, Noël Raoul Bossart (http://www.noelboss.com) 6 | * MIT Licensed. 7 | **/!function(a){"use strict";function b(c,d){if(!(this instanceof b)){var e=new b(a.extend({$source:c,$currentTarget:c.first()},d));return e.open(),e}a.featherlight.apply(this,arguments),this.chainCallbacks(h)}var c=function(a){window.console&&window.console.warn&&window.console.warn("FeatherlightGallery: "+a)};if("undefined"==typeof a)return c("Too much lightness, Featherlight needs jQuery.");if(!a.featherlight)return c("Load the featherlight plugin before the gallery plugin");var d="ontouchstart"in window||window.DocumentTouch&&document instanceof DocumentTouch,e=a.event&&a.event.special.swipeleft&&a,f=window.Hammer&&function(a){var b=new window.Hammer.Manager(a[0]);return b.add(new window.Hammer.Swipe),b},g=d&&(e||f);d&&!g&&c("No compatible swipe library detected; one must be included before featherlightGallery for swipe motions to navigate the galleries.");var h={afterClose:function(a,b){var c=this;return c.$instance.off("next."+c.namespace+" previous."+c.namespace),c._swiper&&(c._swiper.off("swipeleft",c._swipeleft).off("swiperight",c._swiperight),c._swiper=null),a(b)},beforeOpen:function(a,b){var c=this;return c.$instance.on("next."+c.namespace+" previous."+c.namespace,function(a){var b="next"===a.type?1:-1;c.navigateTo(c.currentNavigation()+b)}),g&&(c._swiper=g(c.$instance).on("swipeleft",c._swipeleft=function(){c.$instance.trigger("next")}).on("swiperight",c._swiperight=function(){c.$instance.trigger("previous")}),c.$instance.addClass(this.namespace+"-swipe-aware",g)),c.$instance.find("."+c.namespace+"-content").append(c.createNavigation("previous")).append(c.createNavigation("next")),a(b)},beforeContent:function(a,b){var c=this.currentNavigation(),d=this.slides().length;return this.$instance.toggleClass(this.namespace+"-first-slide",0===c).toggleClass(this.namespace+"-last-slide",c===d-1),a(b)},onKeyUp:function(a,b){var c={37:"previous",39:"next"}[b.keyCode];return c?(this.$instance.trigger(c),!1):a(b)}};a.featherlight.extend(b,{autoBind:"[data-featherlight-gallery]"}),a.extend(b.prototype,{previousIcon:"◀",nextIcon:"▶",galleryFadeIn:100,galleryFadeOut:300,slides:function(){return this.filter?this.$source.find(this.filter):this.$source},images:function(){return c("images is deprecated, please use slides instead"),this.slides()},currentNavigation:function(){return this.slides().index(this.$currentTarget)},navigateTo:function(b){var c=this,d=c.slides(),e=d.length,f=c.$instance.find("."+c.namespace+"-inner");return b=(b%e+e)%e,c.$currentTarget=d.eq(b),c.beforeContent(),a.when(c.getContent(),f.fadeTo(c.galleryFadeOut,.2)).always(function(a){c.setContent(a),c.afterContent(),a.fadeTo(c.galleryFadeIn,1)})},createNavigation:function(b){var c=this;return a(''+this[b+"Icon"]+"").click(function(){a(this).trigger(b+"."+c.namespace)})}}),a.featherlightGallery=b,a.fn.featherlightGallery=function(a){return b.attach(this,a),this},a(document).ready(function(){b._onReady()})}(jQuery); -------------------------------------------------------------------------------- /clean.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import os 3 | from bs4 import BeautifulSoup 4 | 5 | def dir_path(dir): 6 | if os.path.isdir(dir): 7 | return dir 8 | else: 9 | raise NotADirectoryError(dir) 10 | 11 | def parse_args(): 12 | parser = argparse.ArgumentParser(description='Massages Binder Output') 13 | parser.add_argument('in_dir', help='The directory containing files to clean', type=dir_path) 14 | parser.add_argument('out_dir', help='The directory to output to', type=dir_path) 15 | parser.add_argument('--allow_file', help='A file containing components to include, one per line') 16 | return parser.parse_args() 17 | 18 | def read_allow_list(args): 19 | if args.allow_file: 20 | with open(args.allow_file) as f: 21 | allow_list = [] 22 | 23 | for line in f: 24 | allow_list.append(line.strip()) 25 | 26 | return allow_list 27 | else: 28 | return [] 29 | 30 | def clean(dir_entry, args, allow_list): 31 | with open(dir_entry.path) as fp: 32 | soup = BeautifulSoup(fp) 33 | 34 | # Remove search 35 | for el in soup.find_all(id='toc-search'): 36 | el.extract() 37 | 38 | # remove edit links 39 | for el in soup.find_all('a', 'edit'): 40 | el.extract() 41 | 42 | # remove unnecessary scripts 43 | for el in soup.find_all('script'): 44 | # Google analytics 45 | if el.string and 'GoogleAnalyticsObject' in el.string: 46 | el.extract() 47 | 48 | # Search 49 | if el.string and 'function search' in el.string: 50 | el.extract() 51 | 52 | # Feedback prompt 53 | if el.string and 'feedback-button' in el.string: 54 | el.extract() 55 | 56 | # Revision check 57 | if el.string and 'checkRevisions' in el.string: 58 | el.extract() 59 | 60 | # binder JS 61 | if 'src' in el.attrs and 'binder.js' in el['src']: 62 | el.extract() 63 | 64 | if el.string and 'enable_safeWindowOpen' in el.string: 65 | el.extract() 66 | 67 | if el.string and 'loadLocalLink' in el.string: 68 | el.extract() 69 | 70 | for el in soup.find_all('a'): 71 | # remove external links 72 | if el['href'].startswith('http'): 73 | el.unwrap() 74 | # remove footer nav links 75 | elif 'id' in el.attrs and el['id'] in ['next-link', 'prev-link', 'footer-next-link']: 76 | el.extract() 77 | # handle binder-internal links 78 | elif len(allow_list) > 0 and el['href'] not in allow_list: 79 | # nav links get deleted 80 | if 'original-href' in el.attrs: 81 | el.extract() 82 | # links in content get converted to plain text 83 | else: 84 | el.unwrap() 85 | 86 | # Remove empty TOC entries 87 | for el in soup.find_all('div', 'ace-line selectable'): 88 | if len(el.contents) == 0: 89 | el.extract() 90 | 91 | for el in soup.find_all('div', 'child-container'): 92 | if len(el.contents) == 0: 93 | el.parent.extract() 94 | 95 | for el in soup.find_all('div', 'ace-line hidden'): 96 | el.extract() 97 | 98 | # remove original-href and data-doc-id attributes from anchors 99 | for el in soup.find_all('a'): 100 | del el['data-doc-id'] 101 | del el['original-href'] 102 | del el['prev-name'] 103 | del el['prev-href'] 104 | del el['next-name'] 105 | del el['next-href'] 106 | 107 | # Rewrite static link/script urls 108 | for el in soup.find_all('link'): 109 | if 'dropbox-appbox-static' in el['href']: 110 | el['href'] = 'static/' + el['href'].split('/')[-1] 111 | 112 | for el in soup.find_all('script'): 113 | if 'src' in el.attrs and 'dropbox-appbox-static' in el['src']: 114 | el['src'] = 'static/' + el['src'].split('/')[-1] 115 | 116 | # Insert needed JS 117 | s = soup.new_tag('script') 118 | s.string = 'function toggleLeftnav() { document.body.classList.toggle("nav-visible") }' 119 | s['type'] = 'text/javascript' 120 | soup.append(s) 121 | 122 | # Insert analytics 123 | s = soup.new_tag('script') 124 | s['type'] = 'text/javascript' 125 | s['async'] = None 126 | s['src'] = 'https://www.googletagmanager.com/gtag/js?id=G-H85723BFKM' 127 | soup.append(s) 128 | 129 | s = soup.new_tag('script') 130 | s['type'] = 'text/javascript' 131 | s.string = ''' 132 | window.dataLayer = window.dataLayer || []; 133 | function gtag(){dataLayer.push(arguments);} 134 | gtag('js', new Date()); 135 | gtag('config', 'G-H85723BFKM'); 136 | ''' 137 | soup.append(s) 138 | 139 | # Output 140 | with open(os.path.join(args.out_dir, dir_entry.name), mode='w') as wp: 141 | print(str(soup), file=wp) 142 | 143 | 144 | def main(): 145 | args = parse_args() 146 | allow_list = read_allow_list(args) 147 | 148 | for file in os.scandir(args.in_dir): 149 | if len(allow_list) == 0 or file.name in allow_list: 150 | # FIXME parse and pass allow list 151 | clean(file, args, allow_list) 152 | 153 | 154 | if __name__ == "__main__": 155 | main() 156 | -------------------------------------------------------------------------------- /docs/featherlight.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Featherlight - ultra slim jQuery lightbox 3 | * Version 1.7.1 - http://noelboss.github.io/featherlight/ 4 | * 5 | * Copyright 2017, Noël Raoul Bossart (http://www.noelboss.com) 6 | * MIT Licensed. 7 | **/ 8 | !function(a){"use strict";function b(a,c){if(!(this instanceof b)){var d=new b(a,c);return d.open(),d}this.id=b.id++,this.setup(a,c),this.chainCallbacks(b._callbackChain)}function c(a,b){var c={};for(var d in a)d in b&&(c[d]=a[d],delete a[d]);return c}function d(a,b){var c={},d=new RegExp("^"+b+"([A-Z])(.*)");for(var e in a){var f=e.match(d);if(f){var g=(f[1]+f[2].replace(/([A-Z])/g,"-$1")).toLowerCase();c[g]=a[e]}}return c}if("undefined"==typeof a)return void("console"in window&&window.console.info("Too much lightness, Featherlight needs jQuery."));var e=[],f=function(b){return e=a.grep(e,function(a){return a!==b&&a.$instance.closest("body").length>0})},g={allowfullscreen:1,frameborder:1,height:1,longdesc:1,marginheight:1,marginwidth:1,name:1,referrerpolicy:1,scrolling:1,sandbox:1,src:1,srcdoc:1,width:1},h={keyup:"onKeyUp",resize:"onResize"},i=function(c){a.each(b.opened().reverse(),function(){return c.isDefaultPrevented()||!1!==this[h[c.type]](c)?void 0:(c.preventDefault(),c.stopPropagation(),!1)})},j=function(c){if(c!==b._globalHandlerInstalled){b._globalHandlerInstalled=c;var d=a.map(h,function(a,c){return c+"."+b.prototype.namespace}).join(" ");a(window)[c?"on":"off"](d,i)}};b.prototype={constructor:b,namespace:"featherlight",targetAttr:"data-featherlight",variant:null,resetCss:!1,background:null,openTrigger:"click",closeTrigger:"click",filter:null,root:"body",openSpeed:250,closeSpeed:250,closeOnClick:"background",closeOnEsc:!0,closeIcon:"✕",loading:"",persist:!1,otherClose:null,beforeOpen:a.noop,beforeContent:a.noop,beforeClose:a.noop,afterOpen:a.noop,afterContent:a.noop,afterClose:a.noop,onKeyUp:a.noop,onResize:a.noop,type:null,contentFilters:["jquery","image","html","ajax","iframe","text"],setup:function(b,c){"object"!=typeof b||b instanceof a!=!1||c||(c=b,b=void 0);var d=a.extend(this,c,{target:b}),e=d.resetCss?d.namespace+"-reset":d.namespace,f=a(d.background||['
','
','",'
'+d.loading+"
","
","
"].join("")),g="."+d.namespace+"-close"+(d.otherClose?","+d.otherClose:"");return d.$instance=f.clone().addClass(d.variant),d.$instance.on(d.closeTrigger+"."+d.namespace,function(b){var c=a(b.target);("background"===d.closeOnClick&&c.is("."+d.namespace)||"anywhere"===d.closeOnClick||c.closest(g).length)&&(d.close(b),b.preventDefault())}),this},getContent:function(){if(this.persist!==!1&&this.$content)return this.$content;var b=this,c=this.constructor.contentFilters,d=function(a){return b.$currentTarget&&b.$currentTarget.attr(a)},e=d(b.targetAttr),f=b.target||e||"",g=c[b.type];if(!g&&f in c&&(g=c[f],f=b.target&&e),f=f||d("href")||"",!g)for(var h in c)b[h]&&(g=c[h],f=b[h]);if(!g){var i=f;if(f=null,a.each(b.contentFilters,function(){return g=c[this],g.test&&(f=g.test(i)),!f&&g.regex&&i.match&&i.match(g.regex)&&(f=i),!f}),!f)return"console"in window&&window.console.error("Featherlight: no content filter found "+(i?' for "'+i+'"':" (no target specified)")),!1}return g.process.call(b,f)},setContent:function(b){var c=this;return b.is("iframe")&&c.$instance.addClass(c.namespace+"-iframe"),c.$instance.removeClass(c.namespace+"-loading"),c.$instance.find("."+c.namespace+"-inner").not(b).slice(1).remove().end().replaceWith(a.contains(c.$instance[0],b[0])?"":b),c.$content=b.addClass(c.namespace+"-inner"),c},open:function(b){var c=this;if(c.$instance.hide().appendTo(c.root),!(b&&b.isDefaultPrevented()||c.beforeOpen(b)===!1)){b&&b.preventDefault();var d=c.getContent();if(d)return e.push(c),j(!0),c.$instance.fadeIn(c.openSpeed),c.beforeContent(b),a.when(d).always(function(a){c.setContent(a),c.afterContent(b)}).then(c.$instance.promise()).done(function(){c.afterOpen(b)})}return c.$instance.detach(),a.Deferred().reject().promise()},close:function(b){var c=this,d=a.Deferred();return c.beforeClose(b)===!1?d.reject():(0===f(c).length&&j(!1),c.$instance.fadeOut(c.closeSpeed,function(){c.$instance.detach(),c.afterClose(b),d.resolve()})),d.promise()},resize:function(a,b){if(a&&b){this.$content.css("width","").css("height","");var c=Math.max(a/(parseInt(this.$content.parent().css("width"),10)-1),b/(parseInt(this.$content.parent().css("height"),10)-1));c>1&&(c=b/Math.floor(b/c),this.$content.css("width",""+a/c+"px").css("height",""+b/c+"px"))}},chainCallbacks:function(b){for(var c in b)this[c]=a.proxy(b[c],this,a.proxy(this[c],this))}},a.extend(b,{id:0,autoBind:"[data-featherlight]",defaults:b.prototype,contentFilters:{jquery:{regex:/^[#.]\w/,test:function(b){return b instanceof a&&b},process:function(b){return this.persist!==!1?a(b):a(b).clone(!0)}},image:{regex:/\.(png|jpg|jpeg|gif|tiff|bmp|svg)(\?\S*)?$/i,process:function(b){var c=this,d=a.Deferred(),e=new Image,f=a('');return e.onload=function(){f.naturalWidth=e.width,f.naturalHeight=e.height,d.resolve(f)},e.onerror=function(){d.reject(f)},e.src=b,d.promise()}},html:{regex:/^\s*<[\w!][^<]*>/,process:function(b){return a(b)}},ajax:{regex:/./,process:function(b){var c=a.Deferred(),d=a("
").load(b,function(a,b){"error"!==b&&c.resolve(d.contents()),c.fail()});return c.promise()}},iframe:{process:function(b){var e=new a.Deferred,f=a("