├── .babelrc ├── .eslintrc ├── .github └── workflows │ └── semgrep.yml ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── app ├── components │ ├── application │ │ ├── application.pug │ │ ├── application.styl │ │ └── index.js │ ├── base-component.js │ ├── base-screenshot │ │ ├── arrow-bottom-left.svg │ │ ├── arrow-bottom-right.svg │ │ ├── arrow-top-left.svg │ │ ├── arrow-top-right.svg │ │ ├── base-screenshot.pug │ │ ├── index.js │ │ ├── mixin-annotation-arrow.pug │ │ └── screenshot-iframe.styl │ ├── base-target │ │ ├── after-content.pug │ │ ├── base-target.pug │ │ ├── base-target.svg │ │ ├── before-content.pug │ │ ├── download-link.pug │ │ ├── index.js │ │ └── title.pug │ ├── icons │ │ ├── clear.svg │ │ ├── close.svg │ │ ├── collapse.svg │ │ ├── copy.svg │ │ ├── index.js │ │ ├── next.svg │ │ ├── previous.svg │ │ └── search.svg │ ├── target-search │ │ ├── index.js │ │ ├── target-search.pug │ │ └── target-search.styl │ └── target-wrapper │ │ ├── index.js │ │ ├── screenshot.styl │ │ ├── target-wrapper.pug │ │ └── target-wrapper.styl ├── embed-box-base.js ├── embed-box.styl ├── iframe.styl ├── lib │ ├── create-stylesheet-template.js │ ├── create-theme-stylesheet.js │ ├── custom-event.js │ ├── key-map.js │ ├── request-animation-frame.js │ ├── routing.js │ ├── store.js │ └── to-dash-case.js ├── shared.styl ├── site │ ├── assets │ │ ├── customize-feature.png │ │ ├── documentation-feature.png │ │ ├── dotted.png │ │ ├── eager-logo.svg │ │ ├── examples │ │ │ ├── drupal-plugin.zip │ │ │ ├── joomla-plugin.zip │ │ │ ├── library.js │ │ │ ├── library.zip │ │ │ ├── weebly-library.js │ │ │ └── wordpress-plugin.zip │ │ ├── favicon.png │ │ ├── got-plugins-feature.png │ │ └── logo.png │ ├── documentation.pug │ ├── examples │ │ ├── advanced.md │ │ ├── asset-path.md │ │ ├── auto-show.md │ │ ├── basic-node.md │ │ ├── basic-web.md │ │ ├── basic.md │ │ ├── container-element.md │ │ ├── container-selector.md │ │ ├── content-slots.md │ │ ├── custom-install.md │ │ ├── custom-stylesheet.md │ │ ├── custom-target.md │ │ ├── custom-theme.md │ │ ├── events.md │ │ ├── get-target-ids.md │ │ ├── initial-target.md │ │ ├── install-node.md │ │ ├── install-web.md │ │ ├── labels.md │ │ ├── ordering.md │ │ ├── plugin-url.md │ │ └── routing.md │ ├── globals │ │ ├── embed-box-custom-target.js │ │ ├── embed-box-custom.js │ │ └── embed-box.js │ ├── index.js │ ├── index.pug │ ├── lib │ │ ├── is-element-partially-in-viewport.js │ │ ├── load-scripts.js │ │ ├── render-support-table.js │ │ ├── render-toc.js │ │ └── user-simulator.js │ ├── segment.js │ ├── share-icons.pug │ └── site.styl ├── styl │ ├── body-typography.styl │ ├── box-sizing-border-box-all.styl │ ├── buttons.styl │ ├── code-colors.styl │ ├── code-syntax-highlighting.styl │ ├── colors.styl │ ├── copyable-code.styl │ ├── font-smoothing.styl │ ├── fonts.styl │ ├── inline-code.styl │ ├── inline-svg.styl │ ├── link-underlines.styl │ ├── loading-dots.styl │ ├── markdown.styl │ ├── media-query-variables.styl │ ├── more-links-and-buttons.styl │ ├── reset.styl │ └── sizes.styl └── targets │ ├── drupal │ ├── activate-plugin.png │ ├── drupal-7 │ │ ├── activate-module │ │ │ ├── activate-module.pug │ │ │ ├── activate-module.styl │ │ │ └── index.js │ │ ├── drupal-7.pug │ │ ├── index.js │ │ ├── install-new-modules │ │ │ ├── index.js │ │ │ ├── install-new-modules.pug │ │ │ └── install-new-modules.styl │ │ ├── installation-successful │ │ │ ├── index.js │ │ │ ├── installation-successful.pug │ │ │ └── installation-successful.styl │ │ ├── navigate-to-modules │ │ │ ├── index.js │ │ │ ├── navigate-to-modules.pug │ │ │ └── navigate-to-modules.styl │ │ └── upload-module │ │ │ ├── index.js │ │ │ ├── upload-modules.pug │ │ │ └── upload-modules.styl │ ├── drupal-8 │ │ ├── activate-module │ │ │ ├── activate-module.pug │ │ │ ├── activate-module.styl │ │ │ └── index.js │ │ ├── drupal-8.pug │ │ ├── index.js │ │ ├── install-new-modules │ │ │ ├── index.js │ │ │ ├── install-new-modules.pug │ │ │ └── install-new-modules.styl │ │ ├── navigate-to-modules │ │ │ ├── index.js │ │ │ ├── navigate-to-modules.pug │ │ │ └── navigate-to-modules.styl │ │ └── upload-module │ │ │ ├── index.js │ │ │ ├── upload-modules.pug │ │ │ └── upload-modules.styl │ ├── drupal.svg │ ├── index.js │ └── upload-plugin.png │ ├── generic │ ├── generic-latest │ │ ├── generic-latest.pug │ │ ├── index.js │ │ └── install-script │ │ │ ├── index.js │ │ │ ├── install-script.pug │ │ │ └── install-script.styl │ └── index.js │ ├── index.js │ ├── joomla │ ├── activate-plugin.png │ ├── choose-file.png │ ├── choose-template.png │ ├── index.js │ ├── insert-code-body.png │ ├── insert-code-head.png │ ├── joomla-3-6-x.pug │ ├── joomla.svg │ ├── open-templates.png │ ├── save.png │ └── upload-plugin.png │ ├── shopify │ ├── index.js │ ├── shopify-latest │ │ ├── choose-template.png │ │ ├── edit-template.png │ │ ├── index.js │ │ ├── install-script │ │ │ ├── index.js │ │ │ ├── install-script.pug │ │ │ └── install-script.styl │ │ └── shopify-latest.pug │ └── shopify.svg │ ├── squarespace │ ├── index.js │ ├── squarespace-latest │ │ ├── index.js │ │ ├── install-script │ │ │ ├── index.js │ │ │ ├── install-script.pug │ │ │ └── install-script.styl │ │ └── squarespace-latest.pug │ └── squarespace.svg │ ├── tumblr │ ├── index.js │ ├── tumblr-latest │ │ ├── edit-appearance.png │ │ ├── edit-html.png │ │ ├── edit-theme.png │ │ ├── index.js │ │ ├── install-script │ │ │ ├── index.js │ │ │ ├── install-script.pug │ │ │ └── install-script.styl │ │ └── tumblr-latest.pug │ └── tumblr.svg │ ├── weebly │ ├── index.js │ ├── paste-embed-code-footer.png │ ├── paste-embed-code-header.png │ ├── weebly-latest.pug │ └── weebly.svg │ └── wordpress │ ├── activate-plugin.png │ ├── index.js │ ├── upload-plugin.png │ ├── wordpress-4.pug │ └── wordpress.svg ├── bower.json ├── circle.yml ├── custom-target.js ├── custom.js ├── deploy.yaml ├── dist ├── assets.zip ├── assets │ └── app │ │ └── targets │ │ ├── joomla │ │ ├── activate-plugin.png │ │ ├── choose-file.png │ │ ├── choose-template.png │ │ ├── insert-code-body.png │ │ ├── insert-code-head.png │ │ ├── open-templates.png │ │ ├── save.png │ │ └── upload-plugin.png │ │ ├── shopify │ │ └── shopify-latest │ │ │ ├── choose-template.png │ │ │ └── edit-template.png │ │ ├── tumblr │ │ └── tumblr-latest │ │ │ ├── edit-appearance.png │ │ │ ├── edit-html.png │ │ │ └── edit-theme.png │ │ ├── weebly │ │ ├── paste-embed-code-footer.png │ │ └── paste-embed-code-header.png │ │ └── wordpress │ │ ├── activate-plugin.png │ │ └── upload-plugin.png ├── embed-box-custom-target.js ├── embed-box-custom-target.map ├── embed-box-custom-target.min.js ├── embed-box-custom-target.min.map ├── embed-box-custom.js ├── embed-box-custom.map ├── embed-box-custom.min.js ├── embed-box-custom.min.map ├── embed-box.js ├── embed-box.map ├── embed-box.min.js └── embed-box.min.map ├── docs ├── design-architecture.md └── new-targets.md ├── embed-box.js ├── media ├── annotation-assets.png ├── annotation-assets.sketch │ ├── Data │ ├── QuickLook │ │ ├── Preview.png │ │ └── Thumbnail.png │ ├── metadata │ └── version ├── customize-feature │ ├── assets │ │ ├── blue.png │ │ ├── green.png │ │ ├── orange.png │ │ ├── purple.png │ │ └── red.png │ └── customize-feature.sketch │ │ ├── Data │ │ ├── QuickLook │ │ ├── Preview.png │ │ └── Thumbnail.png │ │ ├── metadata │ │ └── version ├── favicon.sketch │ ├── Data │ ├── QuickLook │ │ ├── Preview.png │ │ └── Thumbnail.png │ ├── metadata │ └── version ├── generic.sketch │ ├── Data │ ├── QuickLook │ │ ├── Preview.png │ │ └── Thumbnail.png │ ├── metadata │ └── version ├── icons.sketch ├── logo-alt.png ├── logo-alt.sketch ├── logo.png ├── logo.sketch │ ├── Data │ ├── QuickLook │ │ ├── Preview.png │ │ └── Thumbnail.png │ ├── metadata │ └── version └── shopify-icon.sketch │ ├── Data │ ├── QuickLook │ ├── Preview.png │ └── Thumbnail.png │ ├── metadata │ └── version ├── modules ├── custom-target.js ├── custom.js ├── embed-box.js └── index.js ├── package.json ├── scripts ├── build-dist.js ├── build-site.js ├── release.js ├── validate.js └── watch.js ├── test ├── .babelrc ├── examples │ └── custom-target.html ├── package.json ├── test.js ├── test.pug ├── watch.js └── webpack.config.test.js ├── webpack.config.base.js └── webpack.config.site.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "babel-plugin-transform-decorators-legacy", 4 | "array-includes", 5 | "transform-object-assign", 6 | "transform-array-from", 7 | "transform-proto-to-assign" 8 | ], 9 | "presets": [["es2015", {"modules": false, "loose": true}], "stage-0"] 10 | } 11 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | }, 6 | "extends": "eslint:recommended", 7 | "ecmaVersion": 7, 8 | "globals": { 9 | "ASSET_CDN_URL": null, 10 | "ASSET_PATH": null, 11 | "PROJECT_URL": null, 12 | "process": null, 13 | "VERSION": null 14 | }, 15 | "parser": "babel-eslint", 16 | "plugins": [ 17 | "babel" 18 | ], 19 | "rules": { 20 | "babel/object-curly-spacing": ["warn", "never"], 21 | "array-bracket-spacing": ["warn", "never"], 22 | "arrow-parens": ["warn", "as-needed"], 23 | "arrow-spacing": ["warn", { 24 | "before": true, 25 | "after": true 26 | }], 27 | "block-spacing": ["warn", "always"], 28 | "brace-style": ["warn", "stroustrup", { "allowSingleLine": true }], 29 | "camelcase": "off", 30 | "comma-dangle": ["error", "never"], 31 | "comma-spacing": ["error", { 32 | "before": false, 33 | "after": true 34 | }], 35 | "comma-style": ["warn", "last"], 36 | "computed-property-spacing": ["warn", "never"], 37 | "consistent-this": ["error", "self"], 38 | "consistent-return": "off", 39 | "constructor-super": "error", 40 | "curly": "off", 41 | "default-case": "warn", 42 | "dot-location": ["error", "property"], 43 | "dot-notation": "warn", 44 | "eol-last": "warn", 45 | "eqeqeq": ["error", "allow-null"], 46 | "indent": ["warn", 2, { 47 | "SwitchCase": 1, 48 | "VariableDeclarator": 1 49 | }], 50 | "key-spacing": ["warn", { 51 | "afterColon": true, 52 | "beforeColon": false, 53 | "mode": "strict" 54 | }], 55 | "keyword-spacing": ["warn", { 56 | "after": true, 57 | "before": true 58 | }], 59 | "jsx-quotes": ["warn", "prefer-double"], 60 | "linebreak-style": ["error", "unix"], 61 | "max-nested-callbacks": ["warn", 6], 62 | "new-cap": "error", 63 | "no-cond-assign": ["error", "except-parens"], 64 | "new-parens": "error", 65 | "newline-after-var": "warn", 66 | "no-array-constructor": "error", 67 | "no-caller": "error", 68 | "no-class-assign": "error", 69 | "no-console": "off", 70 | "no-const-assign": "error", 71 | "no-constant-condition": "warn", 72 | "no-continue": "warn", 73 | "no-debugger": "warn", 74 | "no-delete-var": "error", 75 | "no-dupe-class-members": "error", 76 | "no-else-return": "warn", 77 | "no-eval": "error", 78 | "no-extend-native": "error", 79 | "no-extra-bind": "warn", 80 | "no-extra-parens": ["warn", "all"], 81 | "no-floating-decimal": "error", 82 | "no-implied-eval": "error", 83 | "no-inline-comments": "off", 84 | "no-labels": "warn", 85 | "no-label-var": "error", 86 | "no-lone-blocks": "error", 87 | "no-lonely-if": "warn", 88 | "no-loop-func": "error", 89 | "no-mixed-spaces-and-tabs": "warn", 90 | "no-multi-spaces": "warn", 91 | "no-multiple-empty-lines": "warn", 92 | "no-native-reassign": "error", 93 | "no-nested-ternary": "warn", 94 | "no-new": "off", 95 | "no-new-func": "error", 96 | "no-new-object": "error", 97 | "no-new-wrappers": "error", 98 | "no-restricted-syntax": ["error", 99 | "WithStatement" 100 | ], 101 | "no-redeclare": "error", 102 | "no-return-assign": "off", 103 | "no-script-url": "error", 104 | "no-self-compare": "error", 105 | "no-sequences": "error", 106 | "no-shadow-restricted-names": "error", 107 | "no-spaced-func": "warn", 108 | "no-this-before-super": "error", 109 | "no-throw-literal": "error", 110 | "no-undef": "error", 111 | "no-undef-init": "error", 112 | "no-undefined": "error", 113 | "no-unexpected-multiline": "error", 114 | "no-unused-vars": "warn", 115 | "no-use-before-define": "error", 116 | "no-useless-call": "warn", 117 | "no-useless-concat": "warn", 118 | "no-var": "error", 119 | "no-void": "warn", 120 | "no-with": "error", 121 | "object-shorthand": ["warn", "always"], 122 | "one-var": ["warn", { 123 | "let": "never", 124 | "const": "never" 125 | }], 126 | "operator-assignment": ["warn", "always"], 127 | "operator-linebreak": ["warn", "after"], 128 | "padded-blocks": ["warn", "never"], 129 | "prefer-arrow-callback": "warn", 130 | "prefer-const": "error", 131 | "prefer-reflect": "off", 132 | "prefer-spread": "warn", 133 | "quote-props": ["warn", "as-needed"], 134 | "quotes": ["warn", "double"], 135 | "semi": ["error", "never"], 136 | "semi-spacing": ["warn", { 137 | "after": true, 138 | "before": false 139 | }], 140 | "space-before-blocks": ["warn", "always"], 141 | "space-in-parens": ["warn", "never"], 142 | "space-infix-ops": ["warn"], 143 | "space-unary-ops": ["warn"], 144 | "spaced-comment": ["warn", "always"], 145 | "yoda": ["warn", "never", { 146 | "exceptRange": true 147 | }] 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /.github/workflows/semgrep.yml: -------------------------------------------------------------------------------- 1 | 2 | on: 3 | pull_request: {} 4 | workflow_dispatch: {} 5 | push: 6 | branches: 7 | - main 8 | - master 9 | schedule: 10 | - cron: '0 0 * * *' 11 | name: Semgrep config 12 | jobs: 13 | semgrep: 14 | name: semgrep/ci 15 | runs-on: ubuntu-20.04 16 | env: 17 | SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }} 18 | SEMGREP_URL: https://cloudflare.semgrep.dev 19 | SEMGREP_APP_URL: https://cloudflare.semgrep.dev 20 | SEMGREP_VERSION_CHECK_URL: https://cloudflare.semgrep.dev/api/check-version 21 | container: 22 | image: returntocorp/semgrep 23 | steps: 24 | - uses: actions/checkout@v3 25 | - run: semgrep ci 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .sass-cache 2 | .DS_Store 3 | node_modules 4 | bower_components 5 | *.log 6 | site-deploy 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | deploy.yaml 2 | circle.yml 3 | webpack.config.js 4 | webpack.site.js 5 | test/ 6 | media/ 7 | scripts/ 8 | examples/ 9 | app/ 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Eager Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EmbedBox 2 | 3 | [](https://circleci.com/gh/EagerIO/EmbedBox/tree/master) 4 | [](https://badge.fury.io/js/embed-box) 5 | [](https://github.com/ellerbrock/open-source-badge/) 6 | [](https://github.com/EagerIO/EmbedBox/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22) 7 | 8 | EmbedBox is an open-source UI you can simply drop in to provide instructions for installing your embed code or plugins on every major CMS. 9 | 10 | [Learn how to use EmbedBox in your own project](http://embedbox.io/). 11 | 12 | ## Contributing 13 | 14 | We would love for this to become a project of the community. 15 | Feel free to open an issue, submit a PR or contribute to the docs. 16 | 17 | ### Adding instructions for more CMS platforms 18 | 19 | EmbedBox currently has instructions for all of the most popular CMS platforms, including WordPress, Weebly, Joomla, and Drupal, and we plan to continue to add more over time. 20 | 21 | We’re planning to add more targets over time. You can track our progress in [Project #1](https://github.com/EagerIO/EmbedBox/projects/1). The more CMS instructions we can add, the more useful and powerful EmbedBox will become. Follow [these guidelines](https://github.com/EagerIO/EmbedBox/blob/master/docs/new-targets.md) when contributing docs for new “targets” (what the EmbedBox codebase calls CMS platforms). 22 | 23 | ### Requirements 24 | Node v5.0.0+ 25 | 26 | ### Development 27 | 28 | ```shell 29 | npm install 30 | npm start 31 | ``` 32 | 33 | Then navigate to http://localhost:9000 34 | 35 | For a breakdown of how EmbedBox is built, check out our [design architecture](https://github.com/EagerIO/EmbedBox/blob/master/docs/design-architecture.md) docs. 36 | 37 | ### Testing 38 | 39 | ```shell 40 | cd ./test 41 | npm install 42 | npm test 43 | ``` 44 | 45 | Then navigate to http://localhost:9001 46 | 47 | #### Releases 48 | 49 | EmbedBox uses [semantic versioning](http://semver.org/) 50 | 51 | ```shell 52 | npm run release 1.2.3 53 | git push && git push --tags && npm publish 54 | ``` 55 | 56 | ## Companies using EmbedBox 57 | 58 | - [Eager](https://eager.io) 59 | - [Rakam](https://rakam.io/integrate?part=website) 60 | 61 | Let us know if you’d like your product be added to this list! 62 | 63 | -------------------------------------------------------------------------------- /app/components/application/application.pug: -------------------------------------------------------------------------------- 1 | main( 2 | data-branding=(config.branding ? "visible" : "hidden") 3 | data-flow="column" 4 | data-component="application" 5 | data-target-count=this.targets.length 6 | data-mode=config.mode 7 | role="main") 8 | .surface(data-flow="column") 9 | header.header(role="menubar") 10 | .button( 11 | data-action="previous" 12 | data-ref="previousButton" 13 | tabindex="1" 14 | role="menuitem") 15 | 16 | span.title(data-ref="title") 17 | 18 | .button( 19 | data-action="close" 20 | data-ref="closeModalButton" 21 | tabindex="2" 22 | role="menuitem") 23 | 24 | .content(data-ref="content") 25 | 26 | .branding 27 | a.with-more-icon-after(href=config.projectUrl target="_blank") Powered by EmbedBox 28 | -------------------------------------------------------------------------------- /app/components/application/application.styl: -------------------------------------------------------------------------------- 1 | @import "~styl/colors" 2 | @import "~styl/sizes" 3 | @import "~styl/media-query-variables" 4 | 5 | [data-component="application"] 6 | align-items center 7 | justify-content center 8 | max-height 100% 9 | min-height 100% 10 | 11 | @media (max-height 24em) 12 | justify-content flex-start 13 | 14 | .surface, .branding 15 | width targetInstructionsMaxWidth 16 | max-width 100vw 17 | 18 | .surface 19 | background #fff 20 | min-height 18em 21 | max-width 100vw 22 | flex 1 1 auto 23 | overflow hidden 24 | position relative 25 | z-index 1 // Fix border radius overflow issues. 26 | 27 | .branding 28 | display none 29 | font-size .8em 30 | justify-content flex-end 31 | line-height 3 32 | margin-bottom 1em 33 | 34 | a 35 | color #fff 36 | -webkit-font-smoothing antialiased 37 | text-decoration none 38 | text-shadow 0 1px 2px rgba(#000, .7) 39 | padding 0 .5em 40 | 41 | &:after 42 | font-size 1.4em 43 | 44 | .header 45 | align-items center 46 | box-shadow 0 1px lightLineGrayRGBA 47 | flex 0 0 auto 48 | justify-content space-between 49 | width 100% 50 | max-width 100vw 51 | z-index 1 52 | 53 | > * 54 | display flex 55 | align-items center 56 | 57 | .title 58 | text-align center 59 | line-height 1.4 60 | white-space nowrap 61 | overflow hidden 62 | text-overflow ellipsis 63 | max-width 100% 64 | display inline-block 65 | 66 | @media (max-width 500px) 67 | &[data-title-char-length="long"] 68 | font-size .8em 69 | 70 | &[data-title-char-length="medium"] 71 | font-size .9em 72 | 73 | @media (max-width 400px) 74 | &[data-title-char-length="medium"] 75 | font-size .8em 76 | 77 | &[data-title-char-length="short"] 78 | font-size .9em 79 | 80 | @media (max-width 360px) 81 | &[data-title-char-length="short"] 82 | font-size .8em 83 | 84 | &[data-title-char-length="puny"] 85 | font-size .9em 86 | 87 | @media (max-width 340px) 88 | &[data-title-char-length="puny"] 89 | font-size .8em 90 | 91 | .button[data-action] 92 | appearance none 93 | background inherit 94 | border-radius 0 95 | color inherit 96 | flex 0 0 auto 97 | display flex 98 | height 4em 99 | justify-content center 100 | padding 0 101 | margin 0 102 | width 4em 103 | transition opacity .15s ease 104 | 105 | &:focus:before 106 | display none 107 | 108 | &:hover, &:focus 109 | background lightGrayRGBA 110 | box-shadow none 111 | 112 | > .icon 113 | flex 1 0 auto 114 | width 1em 115 | height 1em 116 | stroke currentColor 117 | 118 | &:not(:hover) .icon 119 | stroke-opacity .5 120 | 121 | .content 122 | transform translate3d(0, 0, 0) 123 | transition transform .2s ease 124 | flex 1 1 auto // Fill center unused space. 125 | 126 | > * 127 | margin 0 128 | flex 1 0 auto 129 | max-width 100vw 130 | width 100% 131 | 132 | &[data-route="home"], &[data-target-count="1"] 133 | 134 | .header [data-action="previous"] 135 | opacity 0 136 | pointer-events none 137 | 138 | &[data-route]:not([data-route="home"]) .content 139 | transform translate3d(-100%, 0, 0) 140 | 141 | &[data-transition-state="transitioned"] [inert] > * 142 | display none 143 | 144 | &[data-mode="modal"] 145 | background rgba(#000, .7) 146 | 147 | @media desktopMinWidthQuery 148 | padding 1.5em 0 149 | 150 | .surface 151 | max-height 38em 152 | border-radius .3125em 153 | 154 | @media desktopMinWidthQuery 155 | box-shadow 0 2px 8px rgba(#000, .4) 156 | 157 | @media mobileMaxWidthQuery 158 | max-height 100vh 159 | width 100% 160 | border-radius 0 161 | 162 | &[data-branding="visible"] 163 | padding-bottom 0 164 | 165 | @media desktopMinWidthQuery 166 | .branding 167 | display flex 168 | 169 | &[data-mode="inline"] 170 | background lightGray 171 | height 100vh 172 | min-height 20em 173 | 174 | .surface 175 | height 100vh 176 | max-height 100vh 177 | 178 | .header [data-action="close"] 179 | opacity 0 180 | pointer-events none 181 | -------------------------------------------------------------------------------- /app/components/base-component.js: -------------------------------------------------------------------------------- 1 | import autobind from "autobind-decorator" 2 | 3 | // Ends with brackets e.g. [data-ref="foo[]"] 4 | const ARRAY_REF_PATTERN = /([a-zA-Z\d]*)(\[?\]?)/ 5 | 6 | export default class BaseComponent { 7 | static template = null; 8 | static stylesheet = null; 9 | static store = null; 10 | 11 | constructor(spec = {}) { 12 | Object.assign(this, { 13 | store: null, 14 | element: null, 15 | refs: {} 16 | }, spec) 17 | 18 | this.appendStylesheet() 19 | } 20 | 21 | appendStylesheet() { 22 | const {stylesheet} = this.constructor 23 | 24 | if (!stylesheet) return 25 | if (!this.store) { 26 | console.error(this) 27 | throw new Error("Component attempted to mount stylesheet without a store reference") 28 | } 29 | 30 | const {element: iframeElement, document: iframeDocument} = this.store.iframe 31 | 32 | const onLoad = () => { 33 | if (iframeDocument.head.contains(this.constructor.styleElement)) return 34 | 35 | // Common style tag has yet to be inserted in iframe. 36 | const styleElement = iframeDocument.createElement("style") 37 | 38 | styleElement.innerHTML = stylesheet 39 | this.constructor.styleElement = iframeDocument.head.appendChild(styleElement) 40 | } 41 | 42 | if (iframeDocument.head) onLoad() 43 | else iframeElement.addEventListener("load", onLoad) 44 | } 45 | 46 | @autobind 47 | asset(path) { 48 | return `${this.store.assetPath}${path}` 49 | } 50 | 51 | autofocus() { 52 | if (this.store.mode === "inline") return 53 | 54 | const focusElement = this.element.querySelector("[autofocus]") 55 | 56 | if (focusElement) focusElement.focus() 57 | } 58 | 59 | // NOTE: Calling `updateRefs` multiple times from different tree depths may 60 | // allow parents to inherit a grandchild. 61 | updateRefs() { 62 | const {refs} = this 63 | 64 | Array 65 | .from(this.element.querySelectorAll("[data-ref]")) 66 | .forEach(element => { 67 | const attribute = element.getAttribute("data-ref") 68 | const [, key, arrayKey] = attribute.match(ARRAY_REF_PATTERN) 69 | 70 | if (arrayKey) { 71 | // Multiple elements 72 | if (!Array.isArray(refs[key])) refs[key] = [] 73 | 74 | refs[key].push(element) 75 | } 76 | else { 77 | // Single element 78 | refs[key] = element 79 | } 80 | 81 | element.removeAttribute("data-ref") 82 | }) 83 | } 84 | 85 | serialize(template, templateVars = {}) { 86 | // `document` is used instead of iframe's document to prevent `instanceof` reference errors. 87 | const serializer = document.createElement("div") 88 | 89 | if (typeof template === "function") { 90 | serializer.innerHTML = template.call(this, { 91 | asset: this.asset, 92 | config: this.store, 93 | label: this.label, 94 | ...templateVars 95 | }) 96 | } 97 | else { 98 | serializer.innerHTML = template 99 | } 100 | 101 | return serializer.firstChild 102 | } 103 | 104 | compileTemplate(templateVars = {}) { 105 | const {template} = this.constructor 106 | 107 | this.element = this.serialize(template, templateVars) 108 | this.updateRefs() 109 | 110 | return this.element 111 | } 112 | 113 | @autobind 114 | label(key) { 115 | const {store} = this 116 | const value = store.labels[key] 117 | 118 | return typeof value === "function" ? value(store) : value 119 | } 120 | 121 | insertBefore(sibling, element) { 122 | element.parentNode.insertBefore(sibling, element) 123 | } 124 | 125 | removeElement(element) { 126 | if (!element || !element.parentNode) return null 127 | 128 | return element.parentNode.removeChild(element) 129 | } 130 | 131 | render() { 132 | return this.compileTemplate() 133 | } 134 | 135 | replaceElement(current, next) { 136 | current.parentNode.insertBefore(next, current) 137 | current.parentNode.removeChild(current) 138 | 139 | next.tabIndex = current.tabIndex 140 | 141 | this.updateRefs() 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /app/components/base-screenshot/arrow-bottom-left.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /app/components/base-screenshot/arrow-bottom-right.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /app/components/base-screenshot/arrow-top-left.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /app/components/base-screenshot/arrow-top-right.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /app/components/base-screenshot/base-screenshot.pug: -------------------------------------------------------------------------------- 1 | figure(data-component="screenshot") 2 | iframe( 3 | frameBorder="0" 4 | srcdoc="
" 5 | src="about:blank") 6 | -------------------------------------------------------------------------------- /app/components/base-screenshot/index.js: -------------------------------------------------------------------------------- 1 | import iframeTemplate from "./base-screenshot.pug" 2 | import iframeStylesheet from "./screenshot-iframe.styl" 3 | 4 | import autobind from "autobind-decorator" 5 | import BaseComponent from "components/base-component" 6 | import createStylesheetTemplate from "lib/create-stylesheet-template" 7 | 8 | const toPrecision = (number, precision = 5) => parseFloat(number.toPrecision(precision)) 9 | 10 | export default class BaseScreenshot { 11 | static iframeTemplate = iframeTemplate; 12 | static iframeStylesheet = iframeStylesheet; 13 | 14 | serialize = BaseComponent.prototype.serialize; 15 | constructor(spec) { 16 | Object.assign(this, spec) 17 | } 18 | 19 | @autobind 20 | setScale() { 21 | const iframeDocument = this.iframe.contentDocument 22 | const {getComputedStyle} = iframeDocument.defaultView 23 | const {width: widthStyle, height: heightStyle} = getComputedStyle(iframeDocument.body) 24 | const width = parseInt(widthStyle, 10) 25 | const height = parseInt(heightStyle, 10) 26 | const intrinsicRatio = toPrecision(height / width) 27 | const paddingBottom = `${intrinsicRatio * 100}%` 28 | 29 | this.iframe.setAttribute("width", width) 30 | this.iframe.setAttribute("height", height) 31 | this.element.style.paddingBottom = paddingBottom 32 | 33 | requestAnimationFrame(() => { 34 | const scale = toPrecision(this.element.clientWidth / width) 35 | 36 | this.iframe.style.transform = `scale(${scale})` 37 | this.element.setAttribute("data-render-state", "scaled") 38 | }) 39 | } 40 | 41 | applyTheme() { 42 | const {iframeStylesheet, stylesheet} = this.constructor 43 | const iframeDocument = this.iframe.contentDocument 44 | const iframeStyle = iframeDocument.createElement("style") 45 | const style = iframeDocument.createElement("style") 46 | 47 | const stylesheetTemplate = createStylesheetTemplate(this.store.theme) 48 | 49 | const themeStyles = stylesheetTemplate` 50 | .screenshot .focal-point { 51 | box-shadow: 0 0 0 4px ${"screenshotAnnotationColor"} 52 | } 53 | 54 | .screenshot a.focal-point { 55 | background-color: ${"screenshotAnnotationColor"} 56 | } 57 | 58 | .screenshot [data-arrow]::before, 59 | .screenshot [data-arrow]::after { 60 | color: ${"screenshotAnnotationColor"} 61 | } 62 | ` 63 | 64 | iframeStyle.innerHTML = [iframeStylesheet, themeStyles].join(" ") 65 | iframeDocument.head.appendChild(iframeStyle) 66 | 67 | style.innerHTML = stylesheet 68 | iframeDocument.head.appendChild(style) 69 | } 70 | 71 | render(target) { 72 | const {iframeTemplate, template} = this.constructor 73 | const element = this.element = this.serialize(iframeTemplate) 74 | 75 | this.iframe = element.querySelector("iframe") 76 | 77 | this.iframe.onload = () => { 78 | this.applyTheme() 79 | 80 | const iframeDocument = this.iframe.contentDocument 81 | const element = this.serialize.call(target, template) 82 | 83 | iframeDocument.body.appendChild(element) 84 | 85 | requestAnimationFrame(() => { 86 | if (this.componentDidMount) this.componentDidMount(target) 87 | this.setScale() 88 | }) 89 | 90 | window.addEventListener("resize", () => { 91 | this.setScale() 92 | }) 93 | } 94 | 95 | return this.element 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /app/components/base-screenshot/mixin-annotation-arrow.pug: -------------------------------------------------------------------------------- 1 | mixin annotation-arrow(direction) 2 | - var firstDirection = direction.substr(0, 1) 3 | - var isNS = ["n", "s"].indexOf(firstDirection) !== -1 4 | - var viewBox = isNS ? "0 0 83 114" : "0 0 127 73" 5 | - var d = isNS ? "M39.3 5.4L0.9 0C0.3-0.1-0.1 0.3 0 0.9L9.1 37.8C9.2 38.4 9.7 38.5 10.1 38.1 10.1 38.1 18 29.8 19.8 27.9 19.9 27.7 20.1 27.4 20.5 27.4 20.9 27.3 21.3 27.4 21.4 27.5 68.7 64.9 76 103.8 77.1 109.6 78.2 115.4 84 114.4 82.9 108.6 81.8 102.8 75.7 53.3 30 17.7 29.9 17.6 29.6 17.4 29.6 17.1 29.6 16.9 29.9 16.6 30 16.5L39.6 6.4C40.1 5.9 39.9 5.5 39.3 5.4Z" : "M37.3 0.7C37.4 0.1 37.1-0.1 36.5 0.1L0.5 14.6C-0.1 14.9-0.2 15.4 0.3 15.8L26.5 43.2C27 43.7 27.4 43.6 27.6 43 27.6 43 30.2 31.8 30.8 29.3 30.9 29.1 30.9 28.7 31.2 28.5 31.5 28.3 31.9 28.1 32 28.2 91.7 36.9 117.5 67 121.4 71.4 125.2 75.9 129.7 72.1 125.8 67.6 122 63.1 92 23.4 34.6 15.4 34.5 15.4 34.1 15.3 33.9 15.1 33.8 14.9 34 14.5 34 14.3 35.2 9.4 37.3 0.7 37.3 0.7Z" 6 | 7 | - var transform = attributes.transform || "" 8 | 9 | if ["ne", "en"].indexOf(direction) !== -1 10 | - transform += " scale(-1, 1)" 11 | else if ["se", "es"].indexOf(direction) !== -1 12 | - transform += " scale(-1, -1)" 13 | else if ["sw", "ws"].indexOf(direction) !== -1 14 | - transform += " scale(1, -1)" 15 | 16 | if transform.length === 0 17 | - transform = "none" 18 | 19 | //- HTML necessary to workaround IE SVG scaling issues 20 | div(annotation-arrow data-svg-view-box=viewBox style=attributes.style) 21 | div 22 | svg(version="1.1" viewBox=viewBox style={transform: transform}) 23 | path(d=d) 24 | -------------------------------------------------------------------------------- /app/components/base-screenshot/screenshot-iframe.styl: -------------------------------------------------------------------------------- 1 | @import "~normalize.css/normalize.css" 2 | @import "~styl/reset" 3 | @import "~styl/box-sizing-border-box-all" 4 | @import "~styl/colors" 5 | @import "~highlight.js/styles/vs.css" 6 | 7 | baseFontSize = 16px 8 | 9 | html, body 10 | width 100% 11 | height 100% 12 | 13 | body 14 | font-size baseFontSize 15 | margin 0 16 | overflow hidden 17 | 18 | div, footer, header, main, section 19 | display flex 20 | 21 | [data-flow="column"] 22 | flex-flow column nowrap 23 | 24 | .screenshot 25 | flex-flow column 26 | position relative 27 | width 100% 28 | 29 | .arrow-parent, .relative-arrow 30 | position relative 31 | z-index 10 32 | 33 | > pre 34 | background lightGrayRGBA 35 | margin 0 36 | padding 1em 37 | 38 | .focal-point 39 | color #000 40 | font-weight bold 41 | padding .3em 42 | 43 | [data-arrow] 44 | align-items center 45 | display inline-flex 46 | 47 | &::before, &::after 48 | display inline-block 49 | font-family sans-serif 50 | font-weight normal 51 | font-size 4.4em 52 | line-height baseFontSize 53 | opacity .9 54 | text-shadow 0 1px 0 rgba(0,0,0,0.3) 55 | 56 | [data-arrow="left"]::before 57 | content "→" 58 | 59 | [data-arrow="right"]::after 60 | content "←" 61 | 62 | [data-arrow="above"]::before 63 | content "↓" 64 | right 0 !important 65 | top -0.6em 66 | 67 | .relative-arrow 68 | &::before, &::after 69 | position absolute 70 | 71 | .relative-arrow::before 72 | right 100% 73 | 74 | .relative-arrow::after 75 | left 100% 76 | 77 | -------------------------------------------------------------------------------- /app/components/base-target/after-content.pug: -------------------------------------------------------------------------------- 1 | if config.afterContent || this.config.afterContent 2 | div(data-content-slot="after") 3 | p!= config.afterContent 4 | p!= this.config.afterContent 5 | -------------------------------------------------------------------------------- /app/components/base-target/base-target.pug: -------------------------------------------------------------------------------- 1 | section 2 | != this.renderTitle() 3 | != this.renderBeforeContent() 4 | 5 | .steps-mount(data-ref="stepsMount") 6 | 7 | != this.renderAfterContent() 8 | -------------------------------------------------------------------------------- /app/components/base-target/base-target.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /app/components/base-target/before-content.pug: -------------------------------------------------------------------------------- 1 | if config.beforeContent || this.config.beforeContent 2 | div(data-content-slot="before") 3 | p!= config.beforeContent 4 | p!= this.config.beforeContent 5 | -------------------------------------------------------------------------------- /app/components/base-target/download-link.pug: -------------------------------------------------------------------------------- 1 | h2 2 | a.more(href=this.pluginURL download target="_blank")= this.downloadLabel 3 | div= this.autoDownloadLabel 4 | -------------------------------------------------------------------------------- /app/components/base-target/title.pug: -------------------------------------------------------------------------------- 1 | header.target-title(data-flow="column") 2 | .icon!= this.icon 3 | 4 | h1!= this.title 5 | 6 | if this.versionIDs.length > 1 7 | .versions 8 | .label!= this.instructionsLabel 9 | 10 | select(data-ref="versionSelector") 11 | each versionID in this.versionIDs 12 | option(selected=(versionID === this.versionID))= versionID 13 | -------------------------------------------------------------------------------- /app/components/icons/clear.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /app/components/icons/close.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /app/components/icons/collapse.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /app/components/icons/copy.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /app/components/icons/index.js: -------------------------------------------------------------------------------- 1 | import BaseComponent from "components/base-component" 2 | 3 | export const svgToComponent = template => { 4 | return class Icon extends BaseComponent { 5 | static template = template; 6 | 7 | constructor(attributes = {}) { 8 | super() 9 | 10 | this.attributes = {class: "icon", ...attributes} 11 | } 12 | 13 | render() { 14 | const element = this.compileTemplate() 15 | 16 | Object 17 | .keys(this.attributes) 18 | .forEach(key => element.setAttribute(key, this.attributes[key])) 19 | 20 | return element 21 | } 22 | } 23 | } 24 | 25 | import closeSVG from "./close.svg" 26 | export const close = svgToComponent(closeSVG) 27 | 28 | import previousSVG from "./previous.svg" 29 | export const previous = svgToComponent(previousSVG) 30 | 31 | import nextSVG from "./next.svg" 32 | export const next = svgToComponent(nextSVG) 33 | 34 | import searchSVG from "./search.svg" 35 | export const search = svgToComponent(searchSVG) 36 | 37 | import clearSVG from "./clear.svg" 38 | export const clear = svgToComponent(clearSVG) 39 | 40 | import copySVG from "./copy.svg" 41 | export const copy = svgToComponent(copySVG) 42 | 43 | import collapseSVG from "./collapse.svg" 44 | export const collapse = svgToComponent(collapseSVG) 45 | -------------------------------------------------------------------------------- /app/components/icons/next.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /app/components/icons/previous.svg: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /app/components/icons/search.svg: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /app/components/target-search/target-search.pug: -------------------------------------------------------------------------------- 1 | section(data-flow="column" data-component="target-search" data-event-receiver) 2 | header.header(data-flow="column") 3 | label(for="search-input")= label("searchHeader") 4 | .input-wrapper(data-ref="inputWrapper") 5 | input#search-input.search( 6 | data-ref="search" 7 | placeholder=label("searchPlaceholder") 8 | spellcheck="false" 9 | tabindex="3" 10 | type="text") 11 | .search-clear( 12 | data-ref="searchClear" 13 | tabindex="3") 14 | 15 | .entries(data-flow="column" data-ref="entriesContainer") 16 | -------------------------------------------------------------------------------- /app/components/target-wrapper/index.js: -------------------------------------------------------------------------------- 1 | import template from "./target-wrapper.pug" 2 | import stylesheet from "./target-wrapper.styl" 3 | 4 | import BaseComponent from "components/base-component" 5 | 6 | export default class TargetWrapper extends BaseComponent { 7 | static template = template; 8 | static stylesheet = stylesheet; 9 | 10 | render() { 11 | this.compileTemplate() 12 | 13 | const target = this.target.render() 14 | const {targetMount} = this.refs 15 | 16 | this.replaceElement(targetMount, target) 17 | 18 | return this.element 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/components/target-wrapper/screenshot.styl: -------------------------------------------------------------------------------- 1 | @import "~styl/colors" 2 | 3 | figure 4 | margin 2em 0 5 | position relative 6 | 7 | img, &[data-component="screenshot"] 8 | background #fff 9 | display block 10 | max-width 100% 11 | 12 | &[data-component="screenshot"] 13 | height 0 14 | overflow hidden 15 | // padding-bottom set by JS 16 | padding-left 0 17 | padding-right 0 18 | padding-top 0 19 | position relative 20 | width 100% 21 | 22 | iframe 23 | border 0 24 | left 0 25 | opacity 0 26 | position absolute 27 | top 0 28 | transform-origin 0 0 29 | 30 | &[data-render-state="scaled"] iframe 31 | opacity 1 32 | 33 | &::after 34 | content "" 35 | position absolute 36 | display block 37 | cursor default 38 | border 1px solid lightLineGrayRGBA 39 | z-index 1 40 | top 0 41 | right 0 42 | bottom 0 43 | left 0 44 | 45 | [data-component="application"][data-mode="modal"] & 46 | border-left 0 47 | border-right 0 48 | 49 | @media mobileMaxWidthQuery 50 | border 1px solid lightLineGrayRGBA 51 | 52 | @media smallerThanTargetInstructionsMaxWidthQuery 53 | border-left 0 54 | border-right 0 55 | 56 | @media smallerThanTargetInstructionsMaxWidthQuery 57 | border-left 0 58 | border-right 0 59 | 60 | [annotation-arrow] 61 | position absolute 62 | 63 | > div 64 | position absolute 65 | top 0 66 | left 0 67 | height 0 68 | width 100% 69 | 70 | svg 71 | position absolute 72 | top 0 73 | left 0 74 | width 100% 75 | height 100% 76 | 77 | &[data-svg-view-box="0 0 83 114"] > div 78 | padding-bottom 137.35% 79 | 80 | &[data-svg-view-box="0 0 127 73"] > div 81 | padding-bottom 57.48% 82 | -------------------------------------------------------------------------------- /app/components/target-wrapper/target-wrapper.pug: -------------------------------------------------------------------------------- 1 | section(data-flow="column" data-component="target-wrapper") 2 | .target-mount(data-ref="targetMount" tabindex="3") 3 | 4 | -------------------------------------------------------------------------------- /app/embed-box.styl: -------------------------------------------------------------------------------- 1 | @import "./styl/media-query-variables" 2 | 3 | .embed-box 4 | display block 5 | 6 | &[data-mode="inline"] 7 | width 100% 8 | height 576px 9 | min-height 320px 10 | max-height 100vh 11 | 12 | &[data-mode="modal"] 13 | position fixed !important 14 | z-index 100000 !important 15 | top 0 !important 16 | right 0 !important 17 | bottom 0 !important 18 | left 0 !important 19 | height 100% !important 20 | width 100% !important 21 | max-height 100vh !important 22 | max-width 100vw !important 23 | transition opacity .1s linear !important 24 | opacity 0 25 | 26 | @media mobileMaxWidthQuery 27 | position absolute !important 28 | 29 | body[data-embed-box-scroll-state="locked"] 30 | overflow hidden !important 31 | 32 | @media mobileMaxWidthQuery 33 | body, html 34 | &[data-embed-box-scroll-state="locked"] 35 | position fixed !important 36 | overflow hidden !important 37 | top 0 !important 38 | right 0 !important 39 | bottom 0 !important 40 | left 0 !important 41 | 42 | .embed-box-download-iframe 43 | position fixed 44 | z-index -99999 45 | visibility hidden 46 | width 1px 47 | height 1px 48 | 49 | [data-embed-box-scroll-state] 50 | height 100% 51 | -------------------------------------------------------------------------------- /app/iframe.styl: -------------------------------------------------------------------------------- 1 | @import "shared" 2 | 3 | html, body 4 | overflow hidden 5 | 6 | html 7 | background transparent 8 | 9 | main 10 | height 100% 11 | overflow hidden 12 | 13 | body 14 | background transparent 15 | cursor default 16 | user-select none 17 | -------------------------------------------------------------------------------- /app/lib/create-stylesheet-template.js: -------------------------------------------------------------------------------- 1 | export default function createStylesheetTemplate(definitions) { 2 | return function stylesheetTemplate(strings, ...values) { 3 | const merged = strings.slice() 4 | let offset = 0 5 | 6 | for (let i = 0; i < merged.length; i++) { 7 | if (i === values.length) break 8 | 9 | offset++ 10 | const value = `${definitions[values[i] || "inherit"]} !important;` 11 | 12 | merged.splice(i + offset, 0, value) 13 | } 14 | 15 | return merged.join("") 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/lib/create-theme-stylesheet.js: -------------------------------------------------------------------------------- 1 | import createStylesheetTemplate from "lib/create-stylesheet-template" 2 | 3 | export default function createThemeStylesheet(theme) { 4 | const stylesheetTemplate = createStylesheetTemplate(theme) 5 | 6 | return stylesheetTemplate` 7 | [data-component="application"] .surface { 8 | background-color: ${"backgroundColor"} 9 | color: ${"textColor"} 10 | } 11 | 12 | [data-component$="-target"] .copy-container[collapsed] button.collapse { 13 | background-color: ${"backgroundColor"} 14 | } 15 | 16 | .surface a, .accent-color { 17 | color: ${"accentColor"} 18 | } 19 | 20 | .button.primary, button.primary, 21 | [data-component="target-search"] .entries .entry[data-selected], 22 | [data-component="target-search"] .entries .entry:active, 23 | [data-component="application"][is-touch-device="true"] [data-component="target-search"] .entries .entry:hover, 24 | .accent-background-color { 25 | background: ${"accentColor"} 26 | } 27 | 28 | .target-instructions .steps li::before { 29 | background: ${"stepNumberColor"} 30 | } 31 | 32 | .target-instructions figure [annotation-arrow] svg { 33 | fill: ${"screenshotAnnotationColor"} 34 | } 35 | ` 36 | } 37 | -------------------------------------------------------------------------------- /app/lib/custom-event.js: -------------------------------------------------------------------------------- 1 | export default function polyfillCustomEvent({document: $document, window: $window}) { 2 | let supported = true 3 | 4 | try { 5 | // IE10 will fail to construct a CustomEvent 6 | new $window.CustomEvent() 7 | } 8 | catch (e) { 9 | supported = false 10 | } 11 | 12 | if (supported) { 13 | $window.PolyFilledCustomEvent = $window.CustomEvent 14 | return 15 | } 16 | 17 | function PolyFilledCustomEvent(event, {bubbles = false, cancelable = false, detail} = {}) { 18 | const shimEvent = $document.createEvent("CustomEvent") 19 | 20 | shimEvent.initCustomEvent(event, bubbles, cancelable, detail) 21 | 22 | return shimEvent 23 | } 24 | 25 | PolyFilledCustomEvent.prototype = $window.Event.prototype 26 | 27 | $window.PolyFilledCustomEvent = PolyFilledCustomEvent 28 | } 29 | -------------------------------------------------------------------------------- /app/lib/key-map.js: -------------------------------------------------------------------------------- 1 | export default { 2 | backspace: 8, 3 | enter: 13, 4 | esc: 27, 5 | spacebar: 32, 6 | left: 37, 7 | up: 38, 8 | right: 39, 9 | down: 40 10 | } 11 | -------------------------------------------------------------------------------- /app/lib/request-animation-frame.js: -------------------------------------------------------------------------------- 1 | export default function polyfillRequestAnimationFrame(window) { 2 | let lastTime = 0 3 | 4 | window.requestAnimationFrame = window.msRequestAnimationFrame 5 | window.cancelAnimationFrame = window.msCancelAnimationFrame || window.msCancelRequestAnimationFrame 6 | 7 | if (!window.requestAnimationFrame) { 8 | window.requestAnimationFrame = function(callback) { 9 | const currTime = new Date().getTime() 10 | const timeToCall = Math.max(0, 16 - (currTime - lastTime)) 11 | const id = window.setTimeout(() => callback(currTime + timeToCall), timeToCall) 12 | 13 | lastTime = currTime + timeToCall 14 | 15 | return id 16 | } 17 | } 18 | 19 | if (!window.cancelAnimationFrame) { 20 | window.cancelAnimationFrame = clearTimeout 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/lib/routing.js: -------------------------------------------------------------------------------- 1 | const ROUTE_PREFIX = "#!/embed/" 2 | 3 | export function getRoute() { 4 | return window.location.hash.split(ROUTE_PREFIX)[1] || null 5 | } 6 | 7 | export function setRoute(route = "") { 8 | const {pathname} = window.location 9 | 10 | if (route === "" && window.history.pushState && pathname !== "srcdoc") { 11 | window.history.pushState("", "", pathname) 12 | 13 | return 14 | } 15 | 16 | window.location.hash = ROUTE_PREFIX + route 17 | } 18 | -------------------------------------------------------------------------------- /app/lib/store.js: -------------------------------------------------------------------------------- 1 | const DEFAULT_THEME = { 2 | accentColor: "#2d88f3", 3 | backgroundColor: "#ffffff", 4 | screenshotAnnotationColor: "#fde757", 5 | textColor: "#000000" 6 | } 7 | 8 | const get = (value, fallback) => typeof value !== "undefined" ? value : fallback 9 | 10 | export function createStore(spec = {}) { 11 | const iframe = document.createElement("iframe") 12 | const {autoDownload = true, labels = {}} = spec 13 | 14 | const theme = {...DEFAULT_THEME, ...spec.theme} 15 | 16 | if (!theme.stepNumberColor) theme.stepNumberColor = theme.accentColor 17 | 18 | return { 19 | assetPath: get(spec.assetPath, ASSET_PATH), 20 | name: get(spec.name, "a plugin"), 21 | autoDownload, 22 | 23 | branding: get(spec.branding, true), 24 | 25 | beforeContent: get(spec.beforeContent, ""), 26 | afterContent: get(spec.afterContent, ""), 27 | 28 | embedCode: get(spec.embedCode, ""), 29 | 30 | fallbackID: get(spec.fallbackID, "generic"), 31 | 32 | iframe: { 33 | element: iframe, 34 | get document() { 35 | return iframe.contentDocument 36 | }, 37 | get window() { 38 | return iframe.contentWindow 39 | } 40 | }, 41 | 42 | insertInHead: get(spec.insertInHead, false), 43 | 44 | labels: { 45 | searchHeader: "Select the type of website you have.", 46 | searchPlaceholder: "Search...", 47 | clickHint: "Click to view instructions", 48 | clickHintShort: "Click to view", 49 | submitHint: "Press ENTER to view instructions", 50 | submitHintShort: "Press ENTER", 51 | title: config => `Add ${config.name} to your site`, 52 | ...labels 53 | }, 54 | 55 | route: "home", 56 | routing: get(spec.routing, false), 57 | projectUrl: PROJECT_URL, 58 | 59 | scrollIntoView: get(spec.scrollIntoView, true), 60 | 61 | theme 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/lib/to-dash-case.js: -------------------------------------------------------------------------------- 1 | const PATTERN = /([a-z])([A-Z])/g 2 | 3 | export default function toDashCase(string = "") { 4 | return string.replace(PATTERN, "$1-$2").toLowerCase() 5 | } 6 | -------------------------------------------------------------------------------- /app/shared.styl: -------------------------------------------------------------------------------- 1 | @import "styl/reset" 2 | @import "styl/box-sizing-border-box-all" 3 | @import "styl/body-typography" 4 | @import "styl/buttons" 5 | @import "styl/more-links-and-buttons" 6 | @import "styl/media-query-variables" 7 | @import "styl/colors" 8 | @import "styl/loading-dots" 9 | 10 | html, body 11 | height 100% 12 | 13 | body 14 | margin 0 15 | 16 | div, footer, header, main, section 17 | display flex 18 | 19 | [data-flow="column"] 20 | flex-flow column nowrap 21 | 22 | [data-action] 23 | cursor pointer 24 | 25 | [data-action], [tabindex] 26 | -webkit-tap-highlight-color transparent 27 | 28 | [data-selectable], [contenteditable] 29 | cursor text 30 | user-select text 31 | -------------------------------------------------------------------------------- /app/site/assets/customize-feature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/app/site/assets/customize-feature.png -------------------------------------------------------------------------------- /app/site/assets/documentation-feature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/app/site/assets/documentation-feature.png -------------------------------------------------------------------------------- /app/site/assets/dotted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/app/site/assets/dotted.png -------------------------------------------------------------------------------- /app/site/assets/eager-logo.svg: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /app/site/assets/examples/drupal-plugin.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/app/site/assets/examples/drupal-plugin.zip -------------------------------------------------------------------------------- /app/site/assets/examples/joomla-plugin.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/app/site/assets/examples/joomla-plugin.zip -------------------------------------------------------------------------------- /app/site/assets/examples/library.js: -------------------------------------------------------------------------------- 1 | console.log("Mama always said life was like a box of chocolates. You never know what you're gonna get. - Forrest Gump") 2 | -------------------------------------------------------------------------------- /app/site/assets/examples/library.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/app/site/assets/examples/library.zip -------------------------------------------------------------------------------- /app/site/assets/examples/weebly-library.js: -------------------------------------------------------------------------------- 1 | console.log("There's no better feeling in the world than a warm pizza box on your lap. - Kevin James") 2 | -------------------------------------------------------------------------------- /app/site/assets/examples/wordpress-plugin.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/app/site/assets/examples/wordpress-plugin.zip -------------------------------------------------------------------------------- /app/site/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/app/site/assets/favicon.png -------------------------------------------------------------------------------- /app/site/assets/got-plugins-feature.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/app/site/assets/got-plugins-feature.png -------------------------------------------------------------------------------- /app/site/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/app/site/assets/logo.png -------------------------------------------------------------------------------- /app/site/examples/advanced.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | new EmbedBox({ 3 | name: "Example Plugin", 4 | embedCode: "", 5 | targets: { 6 | weebly: { 7 | embedCode: "" 8 | } 9 | } 10 | }) 11 | ``` 12 | -------------------------------------------------------------------------------- /app/site/examples/asset-path.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | new EmbedBox({ 3 | assetPath: "/assets/images/embed-box" 4 | }) 5 | ``` 6 | -------------------------------------------------------------------------------- /app/site/examples/auto-show.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | var embedBox = new EmbedBox({ 3 | autoShow: false, 4 | embedCode: "" 5 | }) 6 | 7 | embedBox.show() 8 | 9 | setTimeout(function () { 10 | embedBox.destroy() 11 | }, 5000) 12 | ``` 13 | -------------------------------------------------------------------------------- /app/site/examples/basic-node.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | import EmbedBox from "embed-box" 3 | 4 | new EmbedBox({ 5 | name: "Example Plugin", 6 | embedCode: "" 7 | }) 8 | ``` 9 | -------------------------------------------------------------------------------- /app/site/examples/basic-web.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | new EmbedBox({ 3 | name: "Example Plugin", 4 | embedCode: "" + 5 | "\n" + 6 | "" 12 | }) 13 | ``` 14 | -------------------------------------------------------------------------------- /app/site/examples/basic.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | new EmbedBox({ 3 | name: "Example Plugin", 4 | embedCode: "" 5 | }) 6 | ``` 7 | -------------------------------------------------------------------------------- /app/site/examples/container-element.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | new EmbedBox({ 3 | container: document.querySelector(".my-element") 4 | }) 5 | ``` 6 | -------------------------------------------------------------------------------- /app/site/examples/container-selector.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | new EmbedBox({ 3 | container: ".my-element" 4 | }) 5 | ``` 6 | -------------------------------------------------------------------------------- /app/site/examples/content-slots.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | new EmbedBox({ 3 | name: "FooBar", 4 | embedCode: "", 5 | beforeContent: "Having some trouble? " + 6 | "Call us at (555)-123-4567", 7 | afterContent: "You should receive an email with your account password.", 8 | targets: { 9 | wordpress: { 10 | beforeContent: "FooBar works best with WordPress 4.0 or higher." 11 | } 12 | } 13 | }) 14 | ``` 15 | -------------------------------------------------------------------------------- /app/site/examples/custom-install.md: -------------------------------------------------------------------------------- 1 | ```html 2 | 3 | ``` 4 | -------------------------------------------------------------------------------- /app/site/examples/custom-stylesheet.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | new EmbedBox({ 3 | embedCode: "", 4 | name: "Example Stylesheet App", 5 | style: "" + 6 | ".header .title {" + 7 | " color: hotpink;" + 8 | " text-shadow: 0 0 4px pink;" + 9 | "}" 10 | }) 11 | ``` 12 | -------------------------------------------------------------------------------- /app/site/examples/custom-target.md: -------------------------------------------------------------------------------- 1 | ```javascript 2 | var CustomTarget = EmbedBoxCustomTarget.extend({ 3 | id: "custom-test", 4 | label: "Custom Target", 5 | templateVars: { 6 | registerURL: "http://example.com/register" 7 | }, 8 | template: function(vars) { 9 | return "" + 10 | "" + 13 | " Register an account before installing." + 14 | "
" + 15 | "${Target.id}
`}].concat(targetToRow(Target))
27 |
28 | row.innerHTML = cells.reduce(rowTemplate, "")
29 |
30 | tableBody.appendChild(row)
31 | })
32 | }
33 |
--------------------------------------------------------------------------------
/app/site/lib/render-toc.js:
--------------------------------------------------------------------------------
1 | import createSticky from "stickyfill"
2 |
3 | export default function renderTOC() {
4 | const sticky = createSticky()
5 | const toc = document.querySelector(".table-of-contents")
6 | const docsNav = document.querySelector(".docs-nav > .sticky")
7 | const demoWrapper = document.querySelector(".demo-wrapper > .sticky")
8 |
9 | Array
10 | .from(document.querySelectorAll("h2.headline-with-anchor [name], h3.headline-with-anchor [name]"))
11 | .forEach(({parentNode, href, textContent}) => {
12 | const ul = parentNode.tagName === "H3" ? toc.lastChild.lastChild : toc
13 | const li = document.createElement("li")
14 | const a = document.createElement("a")
15 |
16 | Object.assign(a, {href, textContent})
17 |
18 | li.appendChild(a)
19 | ul.appendChild(li)
20 |
21 | if (parentNode.tagName === "H2") {
22 | const nextUl = document.createElement("ul")
23 |
24 | li.appendChild(nextUl)
25 | }
26 | })
27 |
28 | sticky.add(docsNav)
29 | sticky.add(demoWrapper)
30 | }
31 |
--------------------------------------------------------------------------------
/app/site/segment.js:
--------------------------------------------------------------------------------
1 | (function initSegment() {
2 | // Create a queue, but don't obliterate an existing one!
3 | const analytics = window.analytics = window.analytics || []
4 |
5 | // If the real analytics.js is already on the page return.
6 | if (analytics.initialize) return
7 |
8 | // If the snippet was invoked already show an error.
9 | if (analytics.invoked) {
10 | if (window.console && console.error) {
11 | console.error("Segment snippet included twice.")
12 | }
13 | return
14 | }
15 |
16 | // Invoked flag, to make sure the snippet
17 | // is never invoked twice.
18 | analytics.invoked = true
19 |
20 | // A list of the methods in Analytics.js to stub.
21 | analytics.methods = [
22 | "trackSubmit",
23 | "trackClick",
24 | "trackLink",
25 | "trackForm",
26 | "pageview",
27 | "identify",
28 | "reset",
29 | "group",
30 | "track",
31 | "ready",
32 | "alias",
33 | "page",
34 | "once",
35 | "off",
36 | "on"
37 | ]
38 |
39 | // Define a factory to create stubs. These are placeholders
40 | // for methods in Analytics.js so that you never have to wait
41 | // for it to load to actually record data. The `method` is
42 | // stored as the first argument, so we can replay the data.
43 | analytics.factory = function(method) {
44 | return function() {
45 | const args = Array.prototype.slice.call(arguments)
46 |
47 | args.unshift(method)
48 | analytics.push(args)
49 | return analytics
50 | }
51 | }
52 |
53 | // For each of our methods, generate a queueing stub.
54 | for (let i = 0; i < analytics.methods.length; i++) {
55 | const key = analytics.methods[i]
56 |
57 | analytics[key] = analytics.factory(key)
58 | }
59 |
60 | // Define a method to load Analytics.js from our CDN,
61 | // and that will be sure to only ever load it once.
62 | analytics.load = key => {
63 | // Create an async script element based on your key.
64 | const script = document.createElement("script")
65 |
66 | script.type = "text/javascript"
67 | script.async = true
68 | script.src = (document.location.protocol === "https:" ? "https://" : "http://") +
69 | `cdn.segment.com/analytics.js/v1/${key}/analytics.min.js`
70 |
71 | // Insert our script next to the first script element.
72 | const first = document.getElementsByTagName("script")[0]
73 |
74 | first.parentNode.insertBefore(script, first)
75 | }
76 |
77 | // Add a version to keep track of what"s in the wild.
78 | analytics.SNIPPET_VERSION = "3.1.0"
79 |
80 | // Load Analytics.js with your key, which will automatically
81 | // load the tools you"ve enabled for your account. Boosh!
82 | analytics.load("axfoaXqFtPG3M7fbktZPDXB2bHoph4dP")
83 |
84 | // Make the first page call to load the integrations. If
85 | // you"d like to manually name or tag the page, edit or
86 | // move this call however you"d like.
87 | analytics.page()
88 | }())
89 |
--------------------------------------------------------------------------------
/app/site/share-icons.pug:
--------------------------------------------------------------------------------
1 | .share-icons
2 | a.share-icon(href="http://www.facebook.com/sharer.php?u=http%3A%2F%2Fembedbox.io" target="_blank")
3 | svg(version="1.1" viewBox="0 0 16 16" fill="#3b5998")
4 | path(d="M4.025,5.291H5.68V4.541V3.805V3.683c0-0.708,0.018-1.802,0.533-2.479C6.755,0.487,7.5,0,8.781,0 c2.087,0,2.966,0.297,2.966,0.297l-0.414,2.451c0,0-0.689-0.199-1.333-0.199c-0.643,0-1.219,0.23-1.219,0.873v0.26v0.858v0.751 h2.638l-0.184,2.393H8.781V16H5.68V7.684H4.025V5.291")
5 | a.share-icon(href="https://twitter.com/intent/tweet/?text=An%20open-source%20UI%20which%20makes%20it%20easy%20for%20your%20users%20to%20install%20your%20embed%20code.&url=http%3A%2F%2Fembedbox.io&via=EmbedBox" target="_blank")
6 | svg(version="1.1" viewBox="0 0 16 16" fill="#00aced")
7 | path(d="M16,3.536c-0.589,0.261-1.221,0.438-1.885,0.517c0.678-0.406,1.198-1.05,1.443-1.816c-0.634,0.376-1.337,0.649-2.085,0.797 c-0.599-0.638-1.452-1.037-2.396-1.037c-1.813,0-3.283,1.47-3.283,3.282c0,0.257,0.029,0.508,0.085,0.748 c-2.728-0.137-5.147-1.444-6.766-3.43c-0.283,0.485-0.444,1.049-0.444,1.65c0,1.139,0.579,2.144,1.46,2.732 C1.592,6.963,1.086,6.816,0.643,6.57c0,0.014,0,0.027,0,0.041c0,1.59,1.132,2.917,2.633,3.219C3,9.905,2.71,9.945,2.411,9.945 c-0.212,0-0.417-0.021-0.618-0.059c0.418,1.304,1.63,2.253,3.066,2.28c-1.123,0.88-2.539,1.405-4.077,1.405 c-0.265,0-0.526-0.016-0.783-0.046C1.453,14.456,3.178,15,5.032,15c6.038,0,9.34-5.002,9.34-9.34c0-0.142-0.003-0.284-0.01-0.425 C15.003,4.773,15.56,4.195,16,3.536z")
8 | a.share-icon(href="mailto:?subject=An%20open-source%20UI%20which%20makes%20it%20easy%20for%20your%20users%20to%20install%20your%20embed%20code.&body=http%3A%2F%2Fembedbox.io" target="_blank")
9 | svg(version="1.1" viewBox="0 0 24 24" fill="#e92c63")
10 | path(d="M22,4H2C0.897,4,0,4.897,0,6v12c0,1.103,0.897,2,2,2h20c1.103,0,2-0.897,2-2V6C24,4.897,23.103,4,22,4z M7.248,14.434 l-3.5,2C3.67,16.479,3.584,16.5,3.5,16.5c-0.174,0-0.342-0.09-0.435-0.252c-0.137-0.239-0.054-0.545,0.186-0.682l3.5-2 c0.24-0.137,0.545-0.054,0.682,0.186C7.571,13.992,7.488,14.297,7.248,14.434z M12,14.5c-0.094,0-0.189-0.026-0.271-0.08l-8.5-5.5 C2.997,8.77,2.93,8.46,3.081,8.229c0.15-0.23,0.459-0.298,0.691-0.147L12,13.405l8.229-5.324c0.232-0.15,0.542-0.084,0.691,0.147 c0.15,0.232,0.083,0.542-0.148,0.691l-8.5,5.5C12.189,14.474,12.095,14.5,12,14.5z M20.934,16.248 C20.842,16.41,20.673,16.5,20.5,16.5c-0.084,0-0.169-0.021-0.248-0.065l-3.5-2c-0.24-0.137-0.323-0.442-0.186-0.682 s0.443-0.322,0.682-0.186l3.5,2C20.988,15.703,21.071,16.009,20.934,16.248z")
11 |
--------------------------------------------------------------------------------
/app/styl/body-typography.styl:
--------------------------------------------------------------------------------
1 | @import fonts
2 |
3 | html
4 | font-size 16px
5 |
6 | body
7 | font-family sansSerifFonts
8 |
--------------------------------------------------------------------------------
/app/styl/box-sizing-border-box-all.styl:
--------------------------------------------------------------------------------
1 | *, *:after, *:before
2 | box-sizing border-box
3 |
--------------------------------------------------------------------------------
/app/styl/buttons.styl:
--------------------------------------------------------------------------------
1 | @import "styl/fonts"
2 | @import "styl/font-smoothing"
3 | @import "styl/media-query-variables"
4 |
5 | .button
6 | subpixelAntialiasedFonts()
7 | position relative
8 | text-rendering optimizeLegibility
9 | -webkit-tap-highlight-color transparent
10 | user-select none
11 | appearance none
12 | display inline-block
13 | cursor pointer
14 | border 0
15 | border-radius .1875em
16 | font-size 1em
17 | padding .6em 2em
18 | margin 0
19 | text-align center
20 | font-family sansSerifFonts
21 | font-weight 300
22 | letter-spacing .04em
23 | text-indent @letter-spacing
24 | text-decoration none
25 |
26 | @media chromeAndSafariOnlyQuery
27 | font-weight 400
28 |
29 | @media chromeOnlyQuery
30 | font-weight 300
31 |
32 | &:hover
33 | text-decoration none
34 |
35 | &[disabled]
36 | opacity .7
37 |
38 | &:hover, &:focus, &:focus:hover
39 | box-shadow none !important
40 |
41 | &:hover
42 | box-shadow 0 .1875em .375em -.1875em rgba(#000, .325)
43 |
44 | &:hover:active
45 | box-shadow inset 0 .125em .375em rgba(#000, .325)
46 |
47 | &:focus
48 | outline none
49 |
50 | &::before
51 | // `px` below to avoid `em` rounding issues
52 | content ""
53 | position absolute
54 | z-index 1
55 | top 2px
56 | right 2px
57 | bottom 2px
58 | left 2px
59 | border-radius .1em
60 | box-shadow inset 0 0 0 1px currentColor
61 | pointer-events none
62 | transition opacity .3s ease-in-out
63 |
64 | &:active::before
65 | opacity 0
66 |
67 | &.primary
68 | background #000
69 | color #fff
70 |
--------------------------------------------------------------------------------
/app/styl/code-colors.styl:
--------------------------------------------------------------------------------
1 | codeGray = #75715e
2 | codeGold = #e6db74
3 | codeGreen = #a6e22e
4 | codeRed = #f92672
5 | codeBlue = #53d2d9
6 | codePurple = #9d60ff
7 |
--------------------------------------------------------------------------------
/app/styl/code-syntax-highlighting.styl:
--------------------------------------------------------------------------------
1 | @import colors
2 | @import code-colors
3 |
4 | code[class*="lang-"]
5 |
6 | > span
7 | color inherit
8 |
9 | .hljs-string
10 | color codeGold
11 |
12 | .hljs-comment
13 | color codeGray
14 |
15 | .hljs-atom, .hljs-number
16 | color codeGreen
17 |
18 | &.lang-css
19 |
20 | .hljs-selector-tag, .hljs-meta
21 | color codeRed
22 |
23 | .hljs-attribute
24 | color codeBlue
25 |
26 | .hljs-builtin, .hljs-selector-class, .hljs-selector-pseudo, .hljs-selector-attr, .hljs-selector-id
27 | color codeGreen
28 |
29 | .hljs-built_in
30 | color codeBlue
31 |
32 | .hljs-property, .hljs-number
33 | color codePurple
34 |
35 | .hljs-tag
36 | color codeGreen
37 |
38 | .hljs-qualifier, .hljs-def, .hljs-keyword
39 | color codeRed
40 |
41 | &.lang-stylus
42 |
43 | .hljs-qualifier
44 | color codeGreen
45 |
46 | .hljs-tag
47 | color codeRed
48 |
49 | &.lang-html
50 |
51 | .hljs-tag .hljs-name
52 | color codeRed
53 |
54 | .hljs-attribute, .hljs-attr
55 | color codeGreen
56 |
57 | .hljs-string
58 | color codeGold
59 |
60 | .css
61 |
62 | .hljs-selector-tag
63 | color codeRed
64 |
65 | .hljs-attribute
66 | color codeBlue
67 |
68 | &.lang-javascript
69 |
70 | .hljs-literal
71 | color codePurple
72 |
73 | .hljs-subst
74 | color inherit
75 |
76 | .hljs-keyword
77 | color codeRed
78 |
79 | .hljs-def, .hljs-variable-2
80 | color codeGreen
81 |
--------------------------------------------------------------------------------
/app/styl/colors.styl:
--------------------------------------------------------------------------------
1 | selectionColor = #b4d5fe
2 |
3 | darkGray = #2b2b2b
4 | gray = #e0e0e0
5 | lightGray = #ccc
6 | lightGrayRGBA = rgba(#000, .045)
7 | lightLineGrayRGBA = rgba(#000, .21)
8 |
9 | warnBackgroundColor = #fff5d6
10 | warnColor = #713b1b
11 |
12 | brandBlue = #2d88f3
13 | brandYellow = #e6db74
14 |
15 | errorRed = #c62838
16 | lightRed = hsl(0,100%,72%)
17 |
18 | lightGreen = hsl(100,100%,88%)
19 |
--------------------------------------------------------------------------------
/app/styl/copyable-code.styl:
--------------------------------------------------------------------------------
1 | @import colors
2 | @import fonts
3 | @import media-query-variables
4 |
5 | .copyable-code-wrapper
6 | display block
7 | position relative
8 | height 2.375em
9 |
10 | .copy-button.button.primary
11 | position relative
12 | height 100%
13 | vertical-align middle
14 | z-index 1
15 | font-weight normal
16 | padding-top .55em
17 | padding-bottom .6em
18 | padding-left 1.25em
19 | padding-right 1.25em
20 | border-top-right-radius 0
21 | border-bottom-right-radius 0
22 | width 5.4125rem
23 |
24 | @media mobileMaxWidthQuery
25 | display none
26 |
27 | &.zeroclipboard-is-hover
28 | box-shadow 0 .1875em .375em -.1875em rgba(#000, .325)
29 |
30 | &.zeroclipboard-is-active
31 | box-shadow inset 0 .125em .375em rgba(#000, .325)
32 |
33 | @media mobileMaxWidthQuery
34 | display none
35 |
36 | @media firefoxOnlyQuery
37 | top -.0625em
38 |
39 | .standard.code.embed-code
40 | height 100%
41 | margin-left -1px
42 | border-top-left-radius 0
43 | border-bottom-left-radius 0
44 | font-size .8em
45 | padding .75em .8em
46 | width calc(100% - 5.4125rem)
47 |
48 | @media mobileMaxWidthQuery
49 | width 100%
50 | overflow ellipsis
51 | margin-left 0
52 | border-radius .1875rem
53 |
54 | .copied-message
55 | position absolute
56 | top 100%
57 | left 0
58 | z-index 3
59 | background rgba(#000, .9)
60 | color #fff
61 | font-family sansSerifFonts
62 | border-radius .25em
63 | padding .5em 1em
64 | margin-top .6em
65 |
66 | &::before
67 | content ""
68 | position absolute
69 | overflow hidden
70 | bottom 100%
71 | left 1em
72 | border-left .5em solid transparent
73 | border-right .5em solid transparent
74 | border-bottom .5em solid rgba(#000, .9)
75 |
--------------------------------------------------------------------------------
/app/styl/font-smoothing.styl:
--------------------------------------------------------------------------------
1 | antialiasedFonts()
2 | -webkit-font-smoothing antialiased
3 | -moz-osx-font-smoothing grayscale
4 |
5 | subpixelAntialiasedFonts()
6 | -webkit-font-smoothing subpixel-antialiased
7 | -moz-osx-font-smoothing auto
8 |
9 | slightlyDarkerAntialiasedFonts()
10 | subpixelAntialiasedFonts()
11 | opacity .9999
12 |
--------------------------------------------------------------------------------
/app/styl/fonts.styl:
--------------------------------------------------------------------------------
1 | sansSerifFonts = "Avenir New", Avenir, "Helvetica Neue", sans-serif
2 | monospaceFonts = Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace
3 |
--------------------------------------------------------------------------------
/app/styl/inline-code.styl:
--------------------------------------------------------------------------------
1 | @import fonts
2 |
3 | code.inline
4 | font-family monospaceFonts
5 | font-size .727272em
6 | display inline-block
7 | vertical-align middle
8 | margin 0 .25em
9 | position relative
10 | top -1px
11 |
--------------------------------------------------------------------------------
/app/styl/inline-svg.styl:
--------------------------------------------------------------------------------
1 | SVG = {
2 | play: "data:image/svg+xml;utf8,"
3 | }
4 |
--------------------------------------------------------------------------------
/app/styl/link-underlines.styl:
--------------------------------------------------------------------------------
1 | @import colors
2 | @import media-query-variables
3 |
4 | textShadowToCropUnderline(color)
5 | text-shadow .03em 0 color, -.03em 0 color, 0 .03em color, 0 -.03em color, .06em 0 color, -.06em 0 color, .09em 0 color, -.09em 0 color, .12em 0 color, -.12em 0 color, .15em 0 color, -.15em 0 color
6 |
7 | linkUnderlines(backgroundColor, color, lineTop = 87%)
8 | color color
9 | text-decoration none
10 | background-image linear-gradient(backgroundColor, backgroundColor), linear-gradient(backgroundColor, backgroundColor), linear-gradient(color, color)
11 | background-size .1em 1px, .1em 1px, 1px 1px
12 | background-repeat no-repeat, no-repeat, repeat-x
13 | textShadowToCropUnderline backgroundColor
14 | background-position 0% lineTop, 100% lineTop, 0% lineTop
15 |
16 | @media retinaQuery
17 | background-image linear-gradient(transparent 50%, backgroundColor, backgroundColor), linear-gradient(transparent 50%, backgroundColor, backgroundColor), linear-gradient(transparent 50%, color, color)
18 | background-size .075em 1px, .075em 1px, 1px 1px
19 | background-position 0% (lineTop - 2%), 100% (lineTop - 2%), 0% (lineTop - 2%)
20 |
21 | &::selection
22 | text-shadow none
23 | background selectionColor
24 |
25 | &::before, &::after, *, *::before, *::after
26 | text-shadow none
27 |
28 | &:visited
29 | color color
30 |
--------------------------------------------------------------------------------
/app/styl/loading-dots.styl:
--------------------------------------------------------------------------------
1 | .loading-dots
2 | opacity 0
3 | animation loading-dots-fadein .5s linear forwards
4 |
5 | &[data-state="loaded"]
6 | i, i:first-child, i:last-child
7 | opacity 0
8 | animation-play-state paused
9 |
10 | i
11 | width .5em
12 | height .5em
13 | display inline-block
14 | vertical-align middle
15 | background currentColor
16 | border-radius 50%
17 | margin 0 .25em
18 | animation loading-dots-middle-dots .5s linear infinite
19 |
20 | &:first-child
21 | animation loading-dots-first-dot .5s linear infinite
22 | opacity 0
23 | transform translate(-1em)
24 |
25 | &:last-child
26 | animation loading-dots-last-dot .5s linear infinite
27 |
28 | @keyframes loading-dots-fadein
29 | 100%
30 | opacity 1
31 |
32 | @keyframes loading-dots-first-dot
33 | 100%
34 | transform translate(1em)
35 | opacity 1
36 |
37 | @keyframes loading-dots-middle-dots
38 | 100%
39 | transform translate(1em)
40 |
41 | @keyframes loading-dots-last-dot
42 | 100%
43 | transform translate(2em)
44 | opacity 0
45 |
--------------------------------------------------------------------------------
/app/styl/markdown.styl:
--------------------------------------------------------------------------------
1 | @import colors
2 |
3 | .markdown
4 |
5 | > *:first-child
6 | margin-top 0
7 |
8 | > *:last-child
9 | margin-bottom 0
10 |
11 | img
12 | margin 1em 0
13 | display block
14 | max-width 100%
15 |
16 | sup
17 | vertical-align baseline
18 | position relative
19 | top -.5em
20 | font-size .85em
21 |
22 | h1, h2, h3, h4, h5, h6
23 | font-weight 600
24 | line-height 1.2em
25 | margin-top 2rem
26 | margin-bottom .125em
27 |
28 | & + *
29 | margin-top 0
30 |
31 | h1 + h2, h2 + h3, h3 + h4, h4 + h5, h5 + h6
32 | margin-top 1em
33 |
34 | code, pre
35 | font-family monospaceFonts
36 |
37 | pre
38 | background-color lightGrayRGBA
39 | color #333
40 | padding .5em .666666em
41 | overflow-x auto
42 | overflow-scrolling touch
43 | font-size .8em
44 | line-height 1.71em
45 | white-space pre
46 | word-wrap normal
47 |
48 | code
49 | font-size .8em
50 | padding .15625em .3125em
51 | background lightGrayRGBA
52 | white-space nowrap
53 | font-style normal
54 |
55 | pre > code
56 | font-size inherit
57 | background transparent
58 | padding 0
59 | white-space inherit
60 |
61 | ol, ul, dl
62 |
63 | > li:not(.markdown-unstyled)
64 | margin-bottom .666em
65 |
66 | &:last-child
67 | margin-bottom 0
68 |
69 | > *:last-child
70 | margin-bottom 0
71 |
72 | dl
73 | margin 1.5em auto
74 |
75 | dt
76 | display inline-block
77 | line-height 2em
78 | border-left .25em solid
79 |
80 | hr
81 | border 0
82 | height 1px
83 | margin 1em 0
84 | background lightLineGrayRGBA
85 |
86 | &.with-no-margin
87 | margin-top 0
88 | margin-bottom 0
89 |
--------------------------------------------------------------------------------
/app/styl/media-query-variables.styl:
--------------------------------------------------------------------------------
1 | @import sizes
2 |
3 | desktopMinWidth = 769px
4 | desktopMinWidthQuery = "(min-width: " + desktopMinWidth + ")"
5 |
6 | largeDesktopWidth = 1400px
7 | largeDesktopMinWidthQuery = "(min-width: " + largeDesktopWidth + ")"
8 |
9 | mobileMaxWidth = 768px
10 | mobileMaxWidthQuery = "(max-width: " + mobileMaxWidth + ")"
11 |
12 | largerThanIphoneMinWidth = 569px
13 | largerThanIphoneMinWidthQuery = "(min-width: " + largerThanIphoneMinWidth + ")"
14 |
15 | iPhoneMaxWidth = 568px
16 | iPhoneMaxWidthQuery = "(max-width: " + iPhoneMaxWidth + ")"
17 | iPhoneUICompensation = 64px
18 |
19 | firefoxOnlyQuery = "screen and (min--moz-device-pixel-ratio: 0)"
20 |
21 | chromeAndSafariOnlyQuery = "screen and (-webkit-min-device-pixel-ratio: 0)"
22 |
23 | chromeOnlyQuery = "all and (-webkit-min-device-pixel-ratio: 0) and (min-resolution: .001dppx)"
24 |
25 | retinaQuery = "only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min--moz-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (min-device-pixel-ratio: 2), only screen and (min-resolution: 192dpi), only screen and (min-resolution: 2dppx)"
26 |
27 | smallerThanTargetInstructionsMaxWidthQuery = "(max-width: " + (targetInstructionsMaxWidth + .125rem) + ")"
28 |
--------------------------------------------------------------------------------
/app/styl/more-links-and-buttons.styl:
--------------------------------------------------------------------------------
1 | @css {
2 | @font-face {
3 | font-family: "embed-box-icons";
4 | font-style: normal;
5 | font-weight: normal;
6 | src: url(data:application/x-font-woff;charset=utf-8;base64,d09GRk9UVE8AAAQQAAoAAAAABewAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAABDRkYgAAAA9AAAARQAAAEw7LPuDUZGVE0AAAIIAAAAGgAAABx04jsnT1MvMgAAAiQAAABLAAAAYGFpBYRjbWFwAAACcAAAAEcAAAFOP7UHcGhlYWQAAAK4AAAALwAAADYGoUQqaGhlYQAAAugAAAAfAAAAJAe/AetobXR4AAADCAAAABAAAAAQCwoAAG1heHAAAAMYAAAABgAAAAYABFAAbmFtZQAAAyAAAADaAAABsE3GDFBwb3N0AAAD/AAAABMAAAAg/50AZnicTY69S8NQFMXvbV5aLI/4GXEIzSJYAh0dXPwXLNpgVymvH6AtpMHJsWglk06CuPXvEPyg9E9wt2SVt/huk2exWUo5HPgdONx7EBgDROTioiWCSqfR6/YBc4BwTKUcuQbtsTo3JGelIrBh2Y2iJfBCFM5GydB04GXdAdhwYLTpwJqDB1tgZjfysA0ueHAYdq5EP+i02mGl0RbXQa97KZpLXvm9OgMA73CI98AQ90+aN+onkgmXKCVpacjd2ST5+pvk5TywE056zgsW+XRrq281rX2m0zROYxXXTtU0zRS/n6lFVhl9mBY90sDW5/RUrZOnhV547JvWLw0Y+fp5/KbLJMgjUX1dlB92Zkd2xIv/gw+CN3icY2BgYGQAggsF9tdA9CWLvytgNABOBQe1AAB4nGNgZopgnMDAysDBasw6k4GBUQ5CM19nSGMSYgACVgYIaGBgYGJAAgFprikMDgzXFazY0v6lMexg/sIgDhRmhCtQAEJGABgTC0oAeJxjYGBgZoBgGQZGBhDwAPIYwXwWBh0gzQakGRmYGK4rWP3/D+RfV7D8//+/FpAFUsUC1s0E5LAxQA0YnoCZibAaAF3eCGYAeJxjYGRgYADisx3H/eL5bb4ycHMwgMAli78rEPT/l8wCzF+AXA4GJpAoAFzJDHkAeJxjYGRgYP7y/yXDDmYBBoZ/b4EkUAQFsAAAloYFrwAEAAAAAf0AAAH9AAADEAAAAABQAAAEAAB4nI2PvQ3CMBCFXyCJxI8oEaULJCpHTiRSMEBKSvoIWVGaWHKYgREYgzEYgDEYgJoXc0UKCizZ/u7euzsbwBI3RBhWhAU2whMkMMJT7HAVjul5CCfkl3CKRbSiM4pnzKxD1cATzLEVnuKIUjim5y6ckJ/CKfkNixoNTw+NFmc4dOgBWzfW6/bsOgajvGSqEF/C7UO9QoGM/1A4cP/u+tVK5nI6NSsMac92rrtUzjdWFZlRBzWazqjUudGFyWn857WnoPfUB1VxwvAunKzvW9epPDN/9fkAFF9DNgAAeJxjYGYAg/+zGNIYsAAALpkCAwA=) format("woff");
7 | }
8 | }
9 |
10 | chevronIconForMoreLinksAndButtons()
11 | font-family "embed-box-icons"
12 | position relative
13 | display inline-block
14 | vertical-align baseline
15 | color inherit
16 | font-style normal
17 | font-weight inherit
18 | font-size 1em
19 | line-height 1
20 | text-decoration none
21 |
22 | a.more, button.more, .with-more-icon-after
23 |
24 | &::after
25 | chevronIconForMoreLinksAndButtons()
26 | content "\203A" // ›
27 | padding-left .3em
28 |
29 | a.before, .with-before-icon-before
30 |
31 | &::before
32 | chevronIconForMoreLinksAndButtons()
33 | content "\2039" // ‹
34 | padding-right .3em
35 |
36 | a.more:not(.button)::after
37 | padding-right .3em
38 |
39 | .with-more-icon-after:empty::after
40 | padding-left .15em
41 | padding-right .15em
42 |
--------------------------------------------------------------------------------
/app/styl/reset.styl:
--------------------------------------------------------------------------------
1 | article, aside, details, figcaption, figure, footer, header, hgroup, nav, section, summary
2 | display block
3 |
4 | audio, canvas, video
5 | display inline
6 | zoom 1
7 |
8 | audio:not([controls])
9 | display none
10 | height 0
11 |
12 | [hidden]
13 | display none
14 |
15 | html
16 | font-size 100%
17 | text-size-adjust 100%
18 |
19 | body
20 | margin 0
21 | text-rendering optimizeLegibility
22 |
23 | button, input, select, textarea
24 | font-family inherit
25 | font-size inherit
26 | margin 0
27 |
28 | button, input
29 | line-height normal
30 |
31 | button, input[type="button"], input[type="reset"], input[type="submit"]
32 | cursor pointer
33 |
34 | &[disabled]
35 | cursor not-allowed
36 |
37 | button::-moz-focus-inner, input::-moz-focus-inner
38 | border 0
39 | padding 0
40 |
41 | a
42 |
43 | &:focus
44 | outline thin dotted
45 |
46 | &:active, &:hover
47 | outline 0
48 |
49 | abbr[title]
50 | border-bottom thin dotted
51 |
52 | b, strong
53 | font-weight 700
54 |
55 | dfn
56 | font-style italic
57 |
58 | pre
59 | white-space pre-wrap
60 | word-wrap break-word
61 |
62 | img
63 | border 0
64 | -ms-interpolation-mode bicubic
65 |
66 | svg:not(:root)
67 | overflow hidden
68 |
69 | textarea
70 | overflow auto
71 | -webkit-overflow-scrolling touch
72 | vertical-align top
73 | resize vertical
74 |
75 | table
76 | border-collapse collapse
77 | border-spacing 0
78 |
79 | figure, form
80 | margin 0
81 |
82 | p, pre, dl, menu, ol, ul
83 | margin 1em 0
84 |
--------------------------------------------------------------------------------
/app/styl/sizes.styl:
--------------------------------------------------------------------------------
1 | centeredPageWidth = 48em
2 | standardInputRadius = .1875em
3 |
4 | targetInstructionsMaxWidth = 35rem
5 |
--------------------------------------------------------------------------------
/app/targets/drupal/activate-plugin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/app/targets/drupal/activate-plugin.png
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-7/activate-module/activate-module.pug:
--------------------------------------------------------------------------------
1 | .screenshot
2 | .menu
3 | .item Dashboard
4 | .item Content
5 | .item Structure
6 | .item Appearance
7 | .item People
8 | .item.selected.focal-point.relative-arrow(data-arrow="left") Modules
9 | .item Configuration
10 |
11 | .menu.sub
12 | .item Add content
13 | .item Find content
14 |
15 | .modules-list(data-flow="column")
16 | .title User Interface
17 | table.entries
18 | thead
19 | th ENABLED
20 | th NAME
21 | th DESCRIPTION
22 | tbody
23 | tr
24 | td
25 | div.relative-arrow(data-arrow="right")
26 | input(type="checkbox" checked)
27 | td!= config.name
28 | td
29 |
30 | footer(data-arrow="right")
31 | button.focal-point Save configuration
32 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-7/activate-module/activate-module.styl:
--------------------------------------------------------------------------------
1 | body
2 | width 700px
3 | height 300px
4 |
5 | button
6 | background-color #ededed
7 | border none
8 | border-radius 1em
9 | line-height 2
10 | padding 0 .8em
11 |
12 | .menu
13 | align-items center
14 | flex-flow row wrap
15 | background black
16 | color white
17 | padding .5em
18 |
19 | &.sub
20 | background-color #666
21 |
22 | .item
23 | line-height 1.5
24 | padding 0 .8em
25 |
26 | &.selected
27 | background-color #999
28 | border-radius .8em
29 | text-shadow 0 1px 0 #000
30 |
31 | .modules-list
32 | border 1px #ccc solid
33 | margin 1em 1em
34 | padding 1em
35 |
36 | .entries
37 | border 1px #ccc solid
38 | width 100%
39 |
40 | th
41 | background-color #eee
42 | border 1px solid #ccc
43 | padding .2em 1em
44 | th:last-of-type
45 | width 100%
46 |
47 | td
48 | text-align center
49 |
50 | footer
51 | padding 1em
52 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-7/activate-module/index.js:
--------------------------------------------------------------------------------
1 | import template from "./activate-module.pug"
2 | import stylesheet from "./activate-module.styl"
3 |
4 | import BaseScreenshot from "components/base-screenshot"
5 |
6 | export default class Screenshot extends BaseScreenshot {
7 | static template = template;
8 | static stylesheet = stylesheet;
9 | }
10 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-7/drupal-7.pug:
--------------------------------------------------------------------------------
1 | ol.steps
2 | if this.pluginURL
3 | li
4 | != this.renderDownloadLink()
5 | p After downloading, don’t unzip the file.
6 |
7 | li
8 | h2 Login to your Drupal Administrator Dashboard and click Modules.
9 |
10 | figure(data-ref="screenshotMounts[]" data-screenshot="navigateToModules")
11 |
12 | li
13 | h2 Click Install New Module.
14 |
15 | figure(data-ref="screenshotMounts[]" data-screenshot="installNewModules")
16 |
17 | li
18 | h2 Upload the module.
19 | p Click Choose File and select the module you downloaded to your computer.
20 |
21 | figure(data-ref="screenshotMounts[]" data-screenshot="uploadModule")
22 |
23 | li
24 | h2 Click Enable newly added modules.
25 |
26 | figure(data-ref="screenshotMounts[]" data-screenshot="installationSuccessful")
27 |
28 | li
29 | h2 Activate the plugin and view your site.
30 |
31 | p On the Modules page, scroll down to find the new #{config.name} entry.
32 | p Check the Enabled. checkbox to activate the plugin, and click Save configuration.
33 |
34 | figure(data-ref="screenshotMounts[]" data-screenshot="activateModule")
35 |
36 | p You’re done!
37 |
38 | else
39 | if this.location === "body"
40 | li
41 | h2 In your Drupal site’s Admin interface, click the Structure link at the top of the page.
42 |
43 | li
44 | h2 Click Blocks.
45 |
46 | li
47 | h2 Click Add Block.
48 |
49 | li
50 | h2 Fill out the form.
51 |
52 | p As the Block Description enter “#{ config.name } Embed”.
53 | p As the Block Body paste this embed code:
54 |
55 | .copy-container(data-ref="copyContainers[]")
56 | button.button.primary.run(data-ref="copyButtons[]") Copy
57 | div.copyable(contenteditable)= this.copyText
58 |
59 | p As the Text Format select “Full HTML”.
60 |
61 | p Under Region Settings select “Footer” for every theme.
62 |
63 | p That’s it, there’s no need to select any other options
64 |
65 | li
66 | h2 Click Save block, you’re done!
67 | else
68 | li
69 | h2 If you don’t already have it installed, install the Add to Head module.
70 |
71 | p In your Drupal site’s Admin interface, click the Modules link at the top of the page
72 | p Click Install new module.
73 | p In the Install from a URL field enter:
74 |
75 | .copy-container(data-ref="copyContainers[]")
76 | button.button.primary.run(data-ref="copyButtons[]") Copy
77 | div.copyable(contenteditable) https://ftp.drupal.org/files/projects/add_to_head-7.x-1.2.tar.gz
78 |
79 | p Click Enable newly install modules.
80 | p Scroll down until you find the Add To Head module and check the box to the left of the name.
81 | p Click Save configuration.
82 |
83 | li
84 | h2 Install the embed code into the module
85 |
86 | p Click the Configuration button at the top of your Drupal site’s Admin interface.
87 | p Click the Add to Head link on the right side.
88 | p Click Add one now.
89 |
90 | li
91 | h2 Fill out the form.
92 |
93 | p For the Name enter “#{ config.name } Embed”.
94 | p For the Code enter:
95 |
96 | .copy-container(data-ref="copyContainers[]")
97 | button.button.primary.run(data-ref="copyButtons[]") Copy
98 | div.copyable(contenteditable)= this.copyText
99 |
100 | p For the Scope of addition, select “Head”.
101 | p There is no need to change any other fields.
102 |
103 | li
104 | h2 Click Save, you’re done!
105 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-7/index.js:
--------------------------------------------------------------------------------
1 | import template from "./drupal-7.pug"
2 | import activateModule from "./activate-module"
3 | import navigateToModules from "./navigate-to-modules"
4 | import installNewModules from "./install-new-modules"
5 | import uploadModule from "./upload-module"
6 | import installationSuccessful from "./installation-successful"
7 |
8 | export default {
9 | id: "7",
10 | template,
11 | screenshots: {
12 | activateModule,
13 | installationSuccessful,
14 | navigateToModules,
15 | installNewModules,
16 | uploadModule
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-7/install-new-modules/index.js:
--------------------------------------------------------------------------------
1 | import template from "./install-new-modules.pug"
2 | import stylesheet from "./install-new-modules.styl"
3 |
4 | import BaseScreenshot from "components/base-screenshot"
5 |
6 | export default class Screenshot extends BaseScreenshot {
7 | static template = template;
8 | static stylesheet = stylesheet;
9 | }
10 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-7/install-new-modules/install-new-modules.pug:
--------------------------------------------------------------------------------
1 | .screenshot
2 | h2 Modules
3 |
4 | .modal(data-flow="column")
5 | .nav
6 | .item
7 | a(href="#-") Home
8 | .item
9 | a(href="#-") Administration
10 |
11 | .instructions(data-flow="column")
12 | p(data-arrow="right")
13 | a.install.focal-point(href="#-") Install new module
14 |
15 | .modules(data-flow="column")
16 | header Core
17 |
18 | table
19 | thead
20 | th Enabled
21 | th Name
22 | th Version
23 | th Description
24 | th Operations
25 | tbody
26 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-7/install-new-modules/install-new-modules.styl:
--------------------------------------------------------------------------------
1 | body
2 | width 600px
3 | height 300px
4 | background #333
5 | font-family serif
6 | color white
7 | padding 0 1em
8 |
9 | a
10 | text-decoration none
11 |
12 | &.install::before
13 | content "+"
14 | display inline-block
15 | font-weight bold
16 | margin 0 .4em
17 |
18 | p
19 | margin .5em 0
20 |
21 | h2
22 | color #ebe3c5
23 | font-weight normal
24 | margin .4em 0
25 |
26 | .modal
27 | background white
28 | color black
29 | padding 1.5em 2em
30 |
31 | .nav
32 | align-items center
33 | margin-bottom 1em
34 |
35 | .item
36 | & + .item::before
37 | content "»"
38 | display inline-block
39 | margin 0 .4em
40 |
41 | .modules
42 | border 1px solid #dadada
43 | border-radius 5px
44 | color #6d6a67
45 | font-family sans-serif
46 | padding-bottom 4em
47 | margin-top .5em
48 |
49 | header
50 | background #dbdbdb
51 | padding .3em .8em
52 |
53 | table
54 | width 100%
55 | thead
56 | background-color #747474
57 | color #fff
58 |
59 | th
60 | border 1px solid #fff
61 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-7/installation-successful/index.js:
--------------------------------------------------------------------------------
1 | import template from "./installation-successful.pug"
2 | import stylesheet from "./installation-successful.styl"
3 |
4 | import BaseScreenshot from "components/base-screenshot"
5 |
6 | export default class Screenshot extends BaseScreenshot {
7 | static template = template;
8 | static stylesheet = stylesheet;
9 | }
10 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-7/installation-successful/installation-successful.pug:
--------------------------------------------------------------------------------
1 | .screenshot
2 | header
3 | h2 Update Manager
4 |
5 | .content(data-flow="column")
6 | .alert Installation was completed successfully.
7 |
8 | h4!= config.name
9 | ul
10 | li Installed “#{config.name}” successfully
11 |
12 | h4 Next steps
13 | ul
14 | li
15 | a(href="#-" data-arrow="right") Enable newly added
16 | li
17 | a(href="#-") Adminstrator pages
18 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-7/installation-successful/installation-successful.styl:
--------------------------------------------------------------------------------
1 | body
2 | width 470px
3 | height 300px
4 | font-family sans-serif
5 | padding 0 2em
6 |
7 | header
8 | background-color #e0e0d8
9 | padding .3em .8em
10 |
11 | a
12 | text-decoration none
13 |
14 | p
15 | margin .5em 0
16 |
17 | h2
18 | font-weight normal
19 | margin .4em 0
20 |
21 | h3
22 | margin 1em 0 0 0
23 |
24 | .alert
25 | background-color #f8fef0
26 | border 1px solid #d2e0b6
27 | margin-top 1em
28 | padding .3em .8em
29 |
30 | ul
31 | margin 0
32 | padding 0 1em
33 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-7/navigate-to-modules/index.js:
--------------------------------------------------------------------------------
1 | import template from "./navigate-to-modules.pug"
2 | import stylesheet from "./navigate-to-modules.styl"
3 |
4 | import BaseScreenshot from "components/base-screenshot"
5 |
6 | export default class Screenshot extends BaseScreenshot {
7 | static template = template;
8 | static stylesheet = stylesheet;
9 | }
10 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-7/navigate-to-modules/navigate-to-modules.pug:
--------------------------------------------------------------------------------
1 | .screenshot
2 | .menu
3 | .item Dashboard
4 | .item Content
5 | .item Structure
6 | .item Appearance
7 | .item People
8 | .item.focal-point.relative-arrow(data-arrow="left") Modules
9 | .item Configuration
10 |
11 | .menu.sub
12 | .item Add content
13 | .item Find content
14 |
15 | .content
16 | h1 Administration
17 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-7/navigate-to-modules/navigate-to-modules.styl:
--------------------------------------------------------------------------------
1 | body
2 | width 700px
3 | height 160px
4 |
5 | .menu
6 | align-items center
7 | flex-flow row wrap
8 | background black
9 | color white
10 | padding .5em
11 |
12 | &.sub
13 | background-color #666
14 |
15 | .item
16 | line-height 1.5
17 | padding 0 .8em
18 | border-radius .8em
19 |
20 | .content
21 | background-color #e0e0d8
22 | padding 0 1em
23 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-7/upload-module/index.js:
--------------------------------------------------------------------------------
1 | import template from "./upload-modules.pug"
2 | import stylesheet from "./upload-modules.styl"
3 |
4 | import BaseScreenshot from "components/base-screenshot"
5 |
6 | export default class Screenshot extends BaseScreenshot {
7 | static template = template;
8 | static stylesheet = stylesheet;
9 | }
10 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-7/upload-module/upload-modules.pug:
--------------------------------------------------------------------------------
1 | .screenshot
2 | h2 Modules
3 |
4 | .modal(data-flow="column")
5 | .nav
6 | .item
7 | a(href="#-") Home
8 | .item
9 | a(href="#-") Administration
10 | .item
11 | a(href="#-") Modules
12 |
13 | .instructions(data-flow="column")
14 | p
15 | | The following extensions are supported:
16 | i zip tar tgz bz2
17 |
18 | p
19 | strong Upload a module or theme archive to install
20 |
21 | p
22 | .upload(data-arrow="right")
23 | input.focal-point(type="file")
24 |
25 | .hint For example:
26 | i name.tar.zip
27 | | from your local computer
28 |
29 | footer(data-arrow="right")
30 | button.focal-point Install
31 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-7/upload-module/upload-modules.styl:
--------------------------------------------------------------------------------
1 | body
2 | width 500px
3 | height 300px
4 | background #333
5 | font-family serif
6 | color white
7 | padding 0 1em
8 |
9 | a
10 | text-decoration none
11 |
12 | p
13 | margin .5em 0
14 |
15 | button
16 | background-color #ededed
17 | border none
18 | border-radius 1em
19 | font-family sans-serif
20 | line-height 2
21 | padding 0 .8em
22 |
23 | h2
24 | color #ebe3c5
25 | font-weight normal
26 | margin .4em 0
27 |
28 | input[type="file"]
29 | width 14em
30 |
31 | .modal
32 | background white
33 | color black
34 | padding 1.5em 2em
35 |
36 | .nav
37 | align-items center
38 | margin-bottom 1em
39 |
40 | .item
41 | & + .item::before
42 | margin 0 .4em
43 | display inline-block
44 | content "»"
45 |
46 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-8/activate-module/activate-module.pug:
--------------------------------------------------------------------------------
1 | .screenshot
2 | h2 Extend
3 |
4 | .modal(data-flow="column")
5 | .nav
6 | .item
7 | a(href="#-") Home
8 | .item
9 | a(href="#-") Administration
10 |
11 | .instructions(data-flow="column")
12 | p Download additional contributed modules to extend Drupal's functionality.
13 | p
14 | | Regularly review and install available updates to maintain a secure and current site.
15 | | Always run the update script each time a module is updated.
16 |
17 | p
18 | button.install.with-plus-icon Install new module
19 |
20 | .modules(data-flow="column")
21 | header
22 | a(href="#-") Core
23 |
24 | table
25 | thead
26 | th
27 | th Name
28 | th Description
29 | tbody
30 | tr
31 | td.enable-toggle
32 | div.relative-arrow(data-arrow="right")
33 | input(type="checkbox" checked)
34 | td!= config.name
35 | td
36 |
37 | footer(data-arrow="right")
38 | button.focal-point Save configuration
39 |
40 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-8/activate-module/activate-module.styl:
--------------------------------------------------------------------------------
1 | body
2 | width 600px
3 | height 420px
4 | background #333
5 | color white
6 | padding 0 1em
7 |
8 | a
9 | text-decoration none
10 |
11 | button
12 |
13 | &.install
14 | background-color #4da5f0
15 | border 1px solid #2b69d2
16 | border-radius .7em
17 | color #fff
18 | padding .5em 1em .5em .7em
19 |
20 | &::before
21 | content "+"
22 | display inline-block
23 | font-weight bold
24 | margin-right .5em
25 |
26 | p
27 | margin .5em 0
28 |
29 | h2
30 | align-items center
31 | color #fff
32 | display flex
33 | font-weight normal
34 | margin .4em 0
35 |
36 | &::after
37 | border 1px solid #fff
38 | border-radius 50%
39 | content "+"
40 | display inline-block
41 | font-size 14px
42 | font-weight bold
43 | height 1em
44 | line-height .8
45 | margin 0 .4em
46 | text-align center
47 | width 1em
48 |
49 | .with-plus-icon::before
50 | content "+"
51 | display inline-block
52 | font-weight bold
53 | margin 0 .4em
54 |
55 | .modal
56 | background white
57 | color black
58 | padding 1.5em 2em
59 |
60 | .nav
61 | align-items center
62 | margin-bottom 1em
63 |
64 | .item
65 | & + .item::before
66 | content "»"
67 | display inline-block
68 | margin 0 .4em
69 |
70 | .modules
71 | border 1px solid #dadada
72 | font-family sans-serif
73 | padding-bottom 2em
74 | margin .5em 0
75 |
76 | header
77 | font-size 1.2em
78 | font-weight bold
79 | padding .3em .8em
80 |
81 | table
82 | width 100%
83 |
84 | thead, tr
85 | background-color #eee
86 |
87 | th, td
88 | border 1px solid #fff
89 | padding .5em 1em
90 | text-align left
91 |
92 | .enable-toggle
93 | text-align center
94 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-8/activate-module/index.js:
--------------------------------------------------------------------------------
1 | import template from "./activate-module.pug"
2 | import stylesheet from "./activate-module.styl"
3 |
4 | import BaseScreenshot from "components/base-screenshot"
5 |
6 | export default class Screenshot extends BaseScreenshot {
7 | static template = template;
8 | static stylesheet = stylesheet;
9 | }
10 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-8/drupal-8.pug:
--------------------------------------------------------------------------------
1 | ol.steps
2 | if this.pluginURL
3 | li
4 | != this.renderDownloadLink()
5 | p After downloading, don’t unzip the file.
6 |
7 | li
8 | h2 Login to your Drupal Administrator Dashboard and click Extend.
9 |
10 | figure(data-ref="screenshotMounts[]" data-screenshot="navigateToModules")
11 |
12 | li
13 | h2 Click Install new module.
14 |
15 | figure(data-ref="screenshotMounts[]" data-screenshot="installNewModules")
16 |
17 | li
18 | h2 Upload the module.
19 | p Click Choose File and select the module you downloaded to your computer.
20 |
21 | figure(data-ref="screenshotMounts[]" data-screenshot="uploadModule")
22 |
23 | li
24 | h2 Activate the module.
25 |
26 | p You should see the new module on the Extend page.
27 | p The module will appear in category set by the author.
28 | p Click the checkbox next to the module, then click Save Configuration.
29 |
30 | figure(data-ref="screenshotMounts[]" data-screenshot="activateModule")
31 |
32 | p You’re done!
33 |
34 | else
35 | li
36 | h2 In your Drupal site’s Admin interface, click the Structure Block layout Add custom block link in the navigation menu at the top of the page.
37 |
38 | li
39 | h2 Fill out the form.
40 |
41 | p As the Block Description enter “#{ config.name } Embed”.
42 |
43 | p Before pasting the embed code as the Text Format select “Full HTML”.
44 |
45 | p In the Body field click the Source button inside the editor.
46 | p Paste this embed code:
47 |
48 | .copy-container(data-ref="copyContainers[]")
49 | button.button.primary.run(data-ref="copyButtons[]") Copy
50 | div.copyable(contenteditable)= this.copyText
51 |
52 | p Click Save.
53 |
54 | p On the new page that opens, For the Region select “Footer first”.
55 |
56 | li
57 | h2 Click Save block, you’re done!
58 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-8/index.js:
--------------------------------------------------------------------------------
1 | import template from "./drupal-8.pug"
2 | import navigateToModules from "./navigate-to-modules"
3 | import installNewModules from "./install-new-modules"
4 | import uploadModule from "./upload-module"
5 | import activateModule from "./activate-module"
6 |
7 | export default {
8 | id: "8",
9 | template,
10 | screenshots: {
11 | activateModule,
12 | navigateToModules,
13 | installNewModules,
14 | uploadModule
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-8/install-new-modules/index.js:
--------------------------------------------------------------------------------
1 | import template from "./install-new-modules.pug"
2 | import stylesheet from "./install-new-modules.styl"
3 |
4 | import BaseScreenshot from "components/base-screenshot"
5 |
6 | export default class Screenshot extends BaseScreenshot {
7 | static template = template;
8 | static stylesheet = stylesheet;
9 | }
10 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-8/install-new-modules/install-new-modules.pug:
--------------------------------------------------------------------------------
1 | .screenshot
2 | h2 Extend
3 |
4 | .modal(data-flow="column")
5 | .nav
6 | .item
7 | a(href="#-") Home
8 | .item
9 | a(href="#-") Administration
10 |
11 | .instructions(data-flow="column")
12 | p Download additional contributed modules to extend Drupal's functionality.
13 | p
14 | | Regularly review and install available updates to maintain a secure and current site.
15 | | Always run the update script each time a module is updated.
16 |
17 | p(data-arrow="right")
18 | button.install.with-plus-icon Install new module
19 |
20 | .modules(data-flow="column")
21 | header
22 | a(href="#-") Core
23 |
24 | table
25 | thead
26 | th Name
27 | th Description
28 | tbody
29 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-8/install-new-modules/install-new-modules.styl:
--------------------------------------------------------------------------------
1 | body
2 | width 600px
3 | height 380px
4 | background #333
5 | color white
6 | padding 0 1em
7 |
8 | a
9 | text-decoration none
10 |
11 | button
12 |
13 | &.install
14 | background-color #4da5f0
15 | border 1px solid #2b69d2
16 | border-radius .7em
17 | color #fff
18 | padding .5em 1em .5em .7em
19 |
20 | &::before
21 | content "+"
22 | display inline-block
23 | font-weight bold
24 | margin-right .5em
25 |
26 | p
27 | margin .5em 0
28 |
29 | h2
30 | align-items center
31 | color #fff
32 | display flex
33 | font-weight normal
34 | margin .4em 0
35 |
36 | &::after
37 | border 1px solid #fff
38 | border-radius 50%
39 | content "+"
40 | display inline-block
41 | font-size 14px
42 | font-weight bold
43 | height 1em
44 | line-height .8
45 | margin 0 .4em
46 | text-align center
47 | width 1em
48 |
49 | .with-plus-icon::before
50 | content "+"
51 | display inline-block
52 | font-weight bold
53 | margin 0 .4em
54 |
55 | .modal
56 | background white
57 | color black
58 | padding 1.5em 2em
59 |
60 | .nav
61 | align-items center
62 | margin-bottom 1em
63 |
64 | .item + .item::before
65 | content "»"
66 | display inline-block
67 | margin 0 .4em
68 |
69 | .modules
70 | border 1px solid #dadada
71 | font-family sans-serif
72 | padding-bottom 4em
73 | margin-top .5em
74 |
75 | header
76 | font-size 1.2em
77 | font-weight bold
78 | padding .3em .8em
79 |
80 | table
81 | width 100%
82 |
83 | thead, tr
84 | background-color #eee
85 |
86 | th, td
87 | border 1px solid #fff
88 | padding .5em 1em
89 | text-align left
90 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-8/navigate-to-modules/index.js:
--------------------------------------------------------------------------------
1 | import template from "./navigate-to-modules.pug"
2 | import stylesheet from "./navigate-to-modules.styl"
3 |
4 | import BaseScreenshot from "components/base-screenshot"
5 |
6 | export default class Screenshot extends BaseScreenshot {
7 | static template = template;
8 | static stylesheet = stylesheet;
9 | }
10 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-8/navigate-to-modules/navigate-to-modules.pug:
--------------------------------------------------------------------------------
1 | .screenshot
2 | .menu
3 | .item Content
4 | .item Structure
5 | .item Appearance
6 | .item People
7 | .item.focal-point.relative-arrow(data-arrow="left") Extend
8 | .item Configuration
9 | .item People
10 |
11 | .content
12 | h1 Administration
13 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-8/navigate-to-modules/navigate-to-modules.styl:
--------------------------------------------------------------------------------
1 | body
2 | width 660px
3 | height 110px
4 |
5 | .menu
6 | align-items center
7 | flex-flow row wrap
8 | background #fff
9 | color #555
10 |
11 | &.sub
12 | background-color #666
13 |
14 | .item
15 | border-left 1px solid transparent
16 | line-height 1.5
17 | padding .3em .8em
18 |
19 | & + .item
20 | border-left-color #ccc
21 |
22 | .content
23 | background-color #e0e0d8
24 | padding 0 1em
25 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-8/upload-module/index.js:
--------------------------------------------------------------------------------
1 | import template from "./upload-modules.pug"
2 | import stylesheet from "./upload-modules.styl"
3 |
4 | import BaseScreenshot from "components/base-screenshot"
5 |
6 | export default class Screenshot extends BaseScreenshot {
7 | static template = template;
8 | static stylesheet = stylesheet;
9 | }
10 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-8/upload-module/upload-modules.pug:
--------------------------------------------------------------------------------
1 | .screenshot
2 | h2 Extend
3 |
4 | .modal(data-flow="column")
5 | .nav
6 | .item
7 | a(href="#-") Home
8 | .item
9 | a(href="#-") Administration
10 | .item
11 | a(href="#-") Modules
12 |
13 | .instructions(data-flow="column")
14 | p
15 | | You can find modules and themes on drupal.org
16 |
17 | p
18 | | The following extensions are supported:
19 | i tar tgz bz2 zip
20 |
21 | p
22 | strong Upload a module or theme archive to install
23 |
24 | p
25 | .upload(data-arrow="right")
26 | input.focal-point(type="file")
27 |
28 | .hint For example:
29 | i name.tar.zip
30 | | from your local computer
31 |
32 | footer(data-arrow="right")
33 | button.focal-point Install
34 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal-8/upload-module/upload-modules.styl:
--------------------------------------------------------------------------------
1 | body
2 | width 600px
3 | height 350px
4 | background #333
5 | color white
6 | padding 0 1em
7 |
8 | a
9 | text-decoration none
10 |
11 | p
12 | margin .5em 0
13 |
14 | button
15 | background-color #ededed
16 | border none
17 | border-radius 1em
18 | font-family serif
19 | line-height 2
20 | padding 0 .8em
21 |
22 | h2
23 | align-items center
24 | color #fff
25 | display flex
26 | font-weight normal
27 | margin .4em 0
28 |
29 | &::after
30 | border 1px solid #fff
31 | border-radius 50%
32 | content "+"
33 | display inline-block
34 | font-size 14px
35 | font-weight bold
36 | height 1em
37 | line-height .8
38 | margin 0 .4em
39 | text-align center
40 | width 1em
41 |
42 | input[type="file"]
43 | width 14em
44 |
45 | .modal
46 | background white
47 | color black
48 | padding 1.5em 2em
49 |
50 | .nav
51 | align-items center
52 | margin-bottom 1em
53 |
54 | .item
55 | & + .item::before
56 | margin 0 .4em
57 | display inline-block
58 | content "»"
59 |
60 |
--------------------------------------------------------------------------------
/app/targets/drupal/drupal.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/app/targets/drupal/index.js:
--------------------------------------------------------------------------------
1 | import drupal7 from "./drupal-7"
2 | import drupal8 from "./drupal-8"
3 | import icon from "./drupal.svg"
4 |
5 | import BaseTarget from "components/base-target"
6 |
7 | export default class DrupalTarget extends BaseTarget {
8 | static icon = icon;
9 | static id = "drupal";
10 | static label = "Drupal";
11 | static supports = {embedCode: true, plugin: true, insertInto: {body: true}};
12 | static versions = [
13 | drupal8,
14 | drupal7
15 | ];
16 | }
17 |
--------------------------------------------------------------------------------
/app/targets/drupal/upload-plugin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/app/targets/drupal/upload-plugin.png
--------------------------------------------------------------------------------
/app/targets/generic/generic-latest/generic-latest.pug:
--------------------------------------------------------------------------------
1 | ol.steps
2 | li
3 | h2 Copy the code to your site’s <#{this.location}> tag.
4 |
5 | .copy-container(data-ref="copyContainers[]")
6 | button.button.primary.run(data-ref="copyButtons[]") Copy
7 | div.copyable(contenteditable)= this.copyText
8 |
9 | figure(data-ref="screenshotMounts[]" data-screenshot="installScript")
10 |
11 | li
12 | h2 Visit your site.
13 |
14 | p After saving the changes you made, visit your site in the browser.
15 | p You’re done!
16 |
--------------------------------------------------------------------------------
/app/targets/generic/generic-latest/index.js:
--------------------------------------------------------------------------------
1 | import template from "./generic-latest.pug"
2 | import installScript from "./install-script"
3 |
4 | export default {
5 | id: "latest",
6 | template,
7 | screenshots: {installScript}
8 | }
9 |
--------------------------------------------------------------------------------
/app/targets/generic/generic-latest/install-script/index.js:
--------------------------------------------------------------------------------
1 | import template from "./install-script.pug"
2 | import stylesheet from "./install-script.styl"
3 |
4 | import BaseScreenshot from "components/base-screenshot"
5 |
6 | export default class Screenshot extends BaseScreenshot {
7 | static template = template;
8 | static stylesheet = stylesheet;
9 |
10 | componentDidMount(target) {
11 | const {body} = this.iframe.contentDocument
12 | const escaper = this.iframe.contentDocument.createElement("textarea")
13 |
14 | escaper.textContent = target.copyText
15 | const escapedText = `<head>
tag.
65 | | There will be other similar tags, but you only want the one with that exact name.
66 | | It should be near the beginning of the file. Insert the embed code just after that tag.
67 |
68 | figure
69 | img(src=asset(require("./insert-code-head.png")))
70 | +annotation-arrow("ws")(style={top: "40.9%", left: "44%", width: "17.7%"})
71 |
72 | else
73 | p
74 | | Carefully search for the </body>
tag.
75 | | There will be other similar tags, but you only want the one with that exact name.
76 | | It should be near the end of the file.
77 | | Insert the embed code just before that tag.
78 |
79 | figure
80 | img(src=asset(require("./insert-code-body.png")))
81 | +annotation-arrow("wn")(style={top: "78.1%", left: "44%", width: "18.3%"})
82 |
83 | li
84 | h2 Click Save & Close.
85 |
86 | figure
87 | img(src=asset(require("./save.png")))
88 | +annotation-arrow("nw")(style={top: "36.6%", left: "31%", width: "14.3%"} transform="scale(-1, 1) rotate(60deg)")
89 |
90 | p You’re done!
91 |
--------------------------------------------------------------------------------
/app/targets/joomla/joomla.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/app/targets/joomla/open-templates.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/app/targets/joomla/open-templates.png
--------------------------------------------------------------------------------
/app/targets/joomla/save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/app/targets/joomla/save.png
--------------------------------------------------------------------------------
/app/targets/joomla/upload-plugin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/app/targets/joomla/upload-plugin.png
--------------------------------------------------------------------------------
/app/targets/shopify/index.js:
--------------------------------------------------------------------------------
1 | import shopifyLatest from "./shopify-latest"
2 | import icon from "./shopify.svg"
3 |
4 | import BaseTarget from "components/base-target"
5 |
6 | export default class ShopifyTarget extends BaseTarget {
7 | static icon = icon;
8 | static id = "shopify";
9 | static label = "Shopify";
10 | static supports = {embedCode: true, insertInto: {head: true, body: true}};
11 | static versions = [shopifyLatest];
12 | }
13 |
--------------------------------------------------------------------------------
/app/targets/shopify/shopify-latest/choose-template.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/app/targets/shopify/shopify-latest/choose-template.png
--------------------------------------------------------------------------------
/app/targets/shopify/shopify-latest/edit-template.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/app/targets/shopify/shopify-latest/edit-template.png
--------------------------------------------------------------------------------
/app/targets/shopify/shopify-latest/index.js:
--------------------------------------------------------------------------------
1 | import template from "./shopify-latest.pug"
2 | import installScript from "./install-script"
3 |
4 | export default {
5 | id: "latest",
6 | template,
7 | screenshots: {installScript}
8 | }
9 |
--------------------------------------------------------------------------------
/app/targets/shopify/shopify-latest/install-script/index.js:
--------------------------------------------------------------------------------
1 | import template from "./install-script.pug"
2 | import stylesheet from "./install-script.styl"
3 |
4 | import BaseScreenshot from "components/base-screenshot"
5 |
6 | export default class Screenshot extends BaseScreenshot {
7 | static template = template;
8 | static stylesheet = stylesheet;
9 |
10 | componentDidMount(target) {
11 | const {body} = this.iframe.contentDocument
12 | const escaper = this.iframe.contentDocument.createElement("textarea")
13 |
14 | escaper.textContent = target.copyText
15 | const escapedText = `[...]
button in the header.
12 |
13 | p Then click Edit HTML/CSS in the menu that appears.
14 |
15 | figure
16 | img(src=asset(require("./edit-template.png")))
17 | +annotation-arrow("nw")(style={top: "57%", left: "35.4%", width: "12%"} transform="rotate(60deg)")
18 | +annotation-arrow("ne")(style={top: "21%", left: "32.4%", width: "11%"} transform="rotate(41deg)")
19 |
20 | li
21 | p In the center column under Layout, Click theme.liquid
.
22 |
23 | figure
24 | img(src=asset(require("./choose-template.png")))
25 | +annotation-arrow("nw")(style={top: "48%", left: "35%", width: "16%"} transform="rotate(135deg)")
26 |
27 | li
28 | h2 Copy the code below.
29 |
30 | .copy-container(data-ref="copyContainers[]")
31 | button.button.primary.run(data-ref="copyButtons[]") Copy
32 | div.copyable(contenteditable)= this.copyText
33 |
34 | p Paste this code into your site’s <#{this.location}> tag.
35 |
36 | figure(data-ref="screenshotMounts[]" data-screenshot="installScript")
37 |
38 | li
39 | h2 Visit your site.
40 |
41 | p After saving the changes you made, visit your site in the browser.
42 | p You’re done!
43 |
--------------------------------------------------------------------------------
/app/targets/shopify/shopify.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/app/targets/squarespace/index.js:
--------------------------------------------------------------------------------
1 | import squarespaceLatest from "./squarespace-latest"
2 | import icon from "./squarespace.svg"
3 |
4 | import BaseTarget from "components/base-target"
5 |
6 | export default class SquarespaceTarget extends BaseTarget {
7 | static icon = icon;
8 | static id = "squarespace";
9 | static label = "Squarespace";
10 | static supports = {embedCode: true, insertInto: {head: true, body: true}};
11 | static versions = [squarespaceLatest];
12 | }
13 |
--------------------------------------------------------------------------------
/app/targets/squarespace/squarespace-latest/index.js:
--------------------------------------------------------------------------------
1 | import template from "./squarespace-latest.pug"
2 | import installScript from "./install-script"
3 |
4 | export default {
5 | id: "latest",
6 | template,
7 | screenshots: {installScript}
8 | }
9 |
--------------------------------------------------------------------------------
/app/targets/squarespace/squarespace-latest/install-script/index.js:
--------------------------------------------------------------------------------
1 | import template from "./install-script.pug"
2 | import stylesheet from "./install-script.styl"
3 |
4 | import BaseScreenshot from "components/base-screenshot"
5 |
6 | export default class Screenshot extends BaseScreenshot {
7 | static template = template;
8 | static stylesheet = stylesheet;
9 |
10 | componentDidMount(target) {
11 | const {body} = this.iframe.contentDocument
12 | const escaper = this.iframe.contentDocument.createElement("textarea")
13 |
14 | escaper.textContent = target.copyText
15 | const escapedText = `header.php
file from the menu on the right side.
45 |
46 | p
47 | | Carefully search for the <head>
tag.
48 | | There will be other similar tags, but you only want the one with that exact name.
49 | | It should be near the beginning of the file. Insert the embed code just after that tag.
50 |
51 | else
52 | p Locate the footer.php
file from the menu on the right side.
53 |
54 | p
55 | | Carefully search for the </body>
tag.
56 | | There will be other similar tags, but you only want the one with that exact name.
57 | | It should be near the end of the file.
58 | | Insert the embed code just before that tag.
59 | li
60 | h2 Click Update File.
61 |
62 | p You’re done!
63 |
--------------------------------------------------------------------------------
/app/targets/wordpress/wordpress.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "embed-box",
3 | "version": "2.0.3",
4 | "ignore": [
5 | "deploy.yaml",
6 | "circle.yml",
7 | "webpack.config.js",
8 | "webpack.site.js",
9 | "test/",
10 | "media/",
11 | "scripts/",
12 | "examples/",
13 | "app/site/"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/circle.yml:
--------------------------------------------------------------------------------
1 | machine:
2 | node:
3 | version: v5.4.0
4 |
5 | dependencies:
6 | cache_directories:
7 | - "~/nvm/v5.4.0/lib/node_modules"
8 | - "~/nvm/v5.4.0/lib/bin"
9 | - "~/.npm"
10 | - "cache"
11 |
12 | post:
13 | - npm run build-site
14 | - curl https://s3.amazonaws.com/stout-builds/install | sh
15 |
16 | test:
17 | override:
18 | - echo "No tests!"
19 |
20 | deployment:
21 | production:
22 | branch: master
23 | commands:
24 | - stout deploy --env production --key $ACCESS_KEY_ID --secret $ACCESS_KEY_SECRET
25 |
--------------------------------------------------------------------------------
/custom-target.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node, es6 */
2 | module.exports = require("./dist/embed-box-custom-target")
3 |
--------------------------------------------------------------------------------
/custom.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node, es6 */
2 | module.exports = require("./dist/embed-box-custom")
3 |
--------------------------------------------------------------------------------
/deploy.yaml:
--------------------------------------------------------------------------------
1 | default:
2 | root: 'site-deploy'
3 |
4 | production:
5 | bucket: 'embedbox.io'
6 |
--------------------------------------------------------------------------------
/dist/assets.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/dist/assets.zip
--------------------------------------------------------------------------------
/dist/assets/app/targets/joomla/activate-plugin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/dist/assets/app/targets/joomla/activate-plugin.png
--------------------------------------------------------------------------------
/dist/assets/app/targets/joomla/choose-file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/dist/assets/app/targets/joomla/choose-file.png
--------------------------------------------------------------------------------
/dist/assets/app/targets/joomla/choose-template.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/dist/assets/app/targets/joomla/choose-template.png
--------------------------------------------------------------------------------
/dist/assets/app/targets/joomla/insert-code-body.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/dist/assets/app/targets/joomla/insert-code-body.png
--------------------------------------------------------------------------------
/dist/assets/app/targets/joomla/insert-code-head.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/dist/assets/app/targets/joomla/insert-code-head.png
--------------------------------------------------------------------------------
/dist/assets/app/targets/joomla/open-templates.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/dist/assets/app/targets/joomla/open-templates.png
--------------------------------------------------------------------------------
/dist/assets/app/targets/joomla/save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/dist/assets/app/targets/joomla/save.png
--------------------------------------------------------------------------------
/dist/assets/app/targets/joomla/upload-plugin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/dist/assets/app/targets/joomla/upload-plugin.png
--------------------------------------------------------------------------------
/dist/assets/app/targets/shopify/shopify-latest/choose-template.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/dist/assets/app/targets/shopify/shopify-latest/choose-template.png
--------------------------------------------------------------------------------
/dist/assets/app/targets/shopify/shopify-latest/edit-template.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/dist/assets/app/targets/shopify/shopify-latest/edit-template.png
--------------------------------------------------------------------------------
/dist/assets/app/targets/tumblr/tumblr-latest/edit-appearance.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/dist/assets/app/targets/tumblr/tumblr-latest/edit-appearance.png
--------------------------------------------------------------------------------
/dist/assets/app/targets/tumblr/tumblr-latest/edit-html.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/dist/assets/app/targets/tumblr/tumblr-latest/edit-html.png
--------------------------------------------------------------------------------
/dist/assets/app/targets/tumblr/tumblr-latest/edit-theme.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/dist/assets/app/targets/tumblr/tumblr-latest/edit-theme.png
--------------------------------------------------------------------------------
/dist/assets/app/targets/weebly/paste-embed-code-footer.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/dist/assets/app/targets/weebly/paste-embed-code-footer.png
--------------------------------------------------------------------------------
/dist/assets/app/targets/weebly/paste-embed-code-header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/dist/assets/app/targets/weebly/paste-embed-code-header.png
--------------------------------------------------------------------------------
/dist/assets/app/targets/wordpress/activate-plugin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/dist/assets/app/targets/wordpress/activate-plugin.png
--------------------------------------------------------------------------------
/dist/assets/app/targets/wordpress/upload-plugin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/dist/assets/app/targets/wordpress/upload-plugin.png
--------------------------------------------------------------------------------
/docs/design-architecture.md:
--------------------------------------------------------------------------------
1 | # Design Architecture
2 |
3 | The internal design of EmbedBox focuses on two goals in particular.
4 |
5 | 1. Ease of extension.
6 | 2. File size.
7 |
8 | The first is sought using components. The second is sought using our build system.
9 |
10 | EmbedBox components are similar to React components, but without the virtual DOM.
11 | Almost every UI component inherits methods from the `BaseComponent` class and mounts itself in a similar fashion.
12 |
13 | `BaseComponent` contains a collection of common component concerns.
14 |
15 | ## Component Lifecycle
16 |
17 | ## Authoring
18 | Components usually exist in their own directory along with their internal dependencies
19 | such as stylesheets, images, and templates.
20 |
21 | When creating a component the common features are:
22 |
23 | ## `component-name.styl`
24 | This file holds your component specific styles.
25 | The following elements are set to flex positioning by the inherited styles.
26 |
27 | ```styl
28 | div, footer, header, main, section
29 | display flex
30 |
31 | [data-flow="column"]
32 | flex-flow column nowrap
33 | ```
34 |
35 | All styles should be namespaced to prevent undesired CSS inheritance.
36 |
37 | ```styl
38 | [data-component="target-search"]
39 | .title
40 | color blue
41 |
42 | .details
43 | font-size .9em
44 | ```
45 | ## `component-name.pug`
46 | A Pug template for the static portion of the HTML.
47 |
48 | Rather than clutter the component logic with `element.querySelector(".foo-bar")` multiple times,
49 | Components templates can add the `data-ref="fooBar"` attribute to an element.
50 |
51 | ```pug
52 | section(data-flow="column" data-component="target-search" data-event-receiver)
53 | header.header(data-flow="column")
54 | label(for="search-input")= label("searchHeader")
55 | .input-wrapper(data-ref="inputWrapper")
56 | input#search-input.search(
57 | data-ref="search"
58 | placeholder=label("searchPlaceholder")
59 | spellcheck="false"
60 | tabindex="3"
61 | type="text")
62 | .search-clear(
63 | data-ref="searchClear"
64 | tabindex="3")
65 |
66 | .entries(data-flow="column" data-ref="entriesContainer")
67 |
68 | ```
69 |
70 | The component instance can then reference the element with `this.refs.fooBar`.
71 | Append `[]` to a `data-ref` if the ref should be an array, (e.g. `data-ref="items[]`)
72 |
73 |
74 | ## `index.js`
75 | This is the heart of your component where the dependencies are imported and assigned to the class declaration.
76 | The WebPack build system will convert the template and stylesheet into resources the browser can interpret.
77 | The `BaseComponent` class does provides most of the common utilities such as inserting stylesheets or compiling templates.
78 |
79 | ```javascript
80 | import template from "./target-search.pug"
81 | import stylesheet from "./target-search.styl"
82 |
83 | import autobind from "autobind-decorator"
84 | import BaseComponent from "components/base-component"
85 |
86 | export default class TargetSearch extends BaseComponent {
87 | static template = template;
88 | static stylesheet = stylesheet;
89 |
90 | query = ""; // Instance property.
91 |
92 | @autobind // Decorator to use consistent `this` on event handlers.
93 | handleSearchInput() {
94 | const {search} = this.refs
95 |
96 | this.query = search.value.toLowerCase()
97 | }
98 |
99 | render() {
100 | this.compileTemplate() // Implemented in BaseComponent.
101 |
102 | // Refs declared in template.
103 | const {inputWrapper, search, searchClear} = this.refs
104 |
105 | search.addEventListener("input", this.handleSearchInput)
106 |
107 | return this.element // Returning the element is expected.
108 | }
109 | }
110 | ```
111 |
--------------------------------------------------------------------------------
/embed-box.js:
--------------------------------------------------------------------------------
1 | /* eslint-env node, es6 */
2 | module.exports = require("./dist/embed-box")
3 |
--------------------------------------------------------------------------------
/media/annotation-assets.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/media/annotation-assets.png
--------------------------------------------------------------------------------
/media/annotation-assets.sketch/Data:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/media/annotation-assets.sketch/Data
--------------------------------------------------------------------------------
/media/annotation-assets.sketch/QuickLook/Preview.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/media/annotation-assets.sketch/QuickLook/Preview.png
--------------------------------------------------------------------------------
/media/annotation-assets.sketch/QuickLook/Thumbnail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudflare/embed-box/14e8488bf334bb34038a4129d39d6ecd32078179/media/annotation-assets.sketch/QuickLook/Thumbnail.png
--------------------------------------------------------------------------------
/media/annotation-assets.sketch/metadata:
--------------------------------------------------------------------------------
1 |
2 |
3 | This domain is established to be used for illustrative examples in documents. You may use this 89 | domain in examples without prior coordination or asking for permission.
90 | 91 |92 | Embed Drift 93 | <--- Click me to get a demo of EmbedBox 94 |
95 |