Repository: hackmdio/codimd Branch: develop Commit: b1188a5648a2 Files: 284 Total size: 2.2 MB Directory structure: gitextract_04c_nojh/ ├── .buildpacks ├── .devcontainer/ │ ├── Dockerfile │ ├── devcontainer.json │ └── docker-compose.yml ├── .dockerignore ├── .editorconfig ├── .github/ │ ├── tests/ │ │ ├── README.md │ │ └── pull-request.json │ └── workflows/ │ ├── build.yml │ ├── check-release.yml │ └── push-image.yml ├── .gitignore ├── .mailmap ├── .nvmrc ├── .sequelizerc.example ├── AUTHORS ├── Aptfile ├── CONTRIBUTING.md ├── FUNDING.json ├── LICENSE ├── Procfile ├── README.md ├── app.js ├── app.json ├── babel.config.js ├── bin/ │ ├── heroku │ ├── heroku_start.sh │ ├── manage_users │ └── setup ├── config.js ├── config.json.example ├── contribute/ │ └── developer-certificate-of-origin ├── deployments/ │ ├── Dockerfile │ ├── build.sh │ ├── docker-compose.yml │ └── docker-entrypoint.sh ├── lib/ │ ├── auth/ │ │ ├── bitbucket/ │ │ │ └── index.js │ │ ├── dropbox/ │ │ │ └── index.js │ │ ├── email/ │ │ │ └── index.js │ │ ├── facebook/ │ │ │ └── index.js │ │ ├── github/ │ │ │ └── index.js │ │ ├── gitlab/ │ │ │ └── index.js │ │ ├── google/ │ │ │ └── index.js │ │ ├── index.js │ │ ├── ldap/ │ │ │ └── index.js │ │ ├── mattermost/ │ │ │ └── index.js │ │ ├── oauth2/ │ │ │ ├── index.js │ │ │ └── strategy.js │ │ ├── openid/ │ │ │ └── index.js │ │ ├── saml/ │ │ │ └── index.js │ │ ├── twitter/ │ │ │ └── index.js │ │ └── utils.js │ ├── config/ │ │ ├── default.js │ │ ├── defaultSSL.js │ │ ├── dockerSecret.js │ │ ├── enum.js │ │ ├── environment.js │ │ ├── index.js │ │ └── utils.js │ ├── csp.js │ ├── errorPage/ │ │ └── index.js │ ├── history/ │ │ └── index.js │ ├── homepage/ │ │ └── index.js │ ├── imageRouter/ │ │ ├── azure.js │ │ ├── filesystem.js │ │ ├── imgur.js │ │ ├── index.js │ │ ├── lutim.js │ │ ├── minio.js │ │ └── s3.js │ ├── letter-avatars.js │ ├── logger.js │ ├── metrics.js │ ├── middleware/ │ │ ├── checkURIValid.js │ │ ├── codiMDVersion.js │ │ ├── redirectWithoutTrailingSlashes.js │ │ └── tooBusy.js │ ├── migrations/ │ │ ├── 20150504155329-create-users.js │ │ ├── 20150508114741-create-notes.js │ │ ├── 20150515125813-create-temp.js │ │ ├── 20150702001020-update-to-0_3_1.js │ │ ├── 20150915153700-change-notes-title-to-text.js │ │ ├── 20160112220142-note-add-lastchange.js │ │ ├── 20160420180355-note-add-alias.js │ │ ├── 20160515114000-user-add-tokens.js │ │ ├── 20160607060246-support-revision.js │ │ ├── 20160703062241-support-authorship.js │ │ ├── 20161009040430-support-delete-note.js │ │ ├── 20161201050312-support-email-signin.js │ │ ├── 20171009121200-longtext-for-mysql.js │ │ ├── 20180209120907-longtext-of-authorship.js │ │ ├── 20180306150303-fix-enum.js │ │ ├── 20180326103000-use-text-in-tokens.js │ │ ├── 20180525153000-user-add-delete-token.js │ │ ├── 20200104215332-remove-temp-table.js │ │ └── 20240114120250-revision-add-index.js │ ├── models/ │ │ ├── author.js │ │ ├── index.js │ │ ├── note.js │ │ ├── revision.js │ │ └── user.js │ ├── note/ │ │ ├── index.js │ │ └── noteActions.js │ ├── ot/ │ │ ├── client.js │ │ ├── editor-socketio-server.js │ │ ├── index.js │ │ ├── selection.js │ │ ├── server.js │ │ ├── simple-text-operation.js │ │ ├── text-operation.js │ │ └── wrapped-operation.js │ ├── realtime/ │ │ ├── processQueue.js │ │ ├── realtime.js │ │ ├── realtimeCleanDanglingUserJob.js │ │ ├── realtimeClientConnection.js │ │ ├── realtimeSaveRevisionJob.js │ │ └── realtimeUpdateDirtyNoteJob.js │ ├── response.js │ ├── routes.js │ ├── status/ │ │ └── index.js │ ├── user/ │ │ └── index.js │ ├── utils.js │ ├── web/ │ │ └── middleware/ │ │ └── checkVersion.js │ └── workers/ │ └── dmpWorker.js ├── locales/ │ ├── ca.json │ ├── da.json │ ├── de.json │ ├── el.json │ ├── en.json │ ├── eo.json │ ├── es.json │ ├── fr.json │ ├── hi.json │ ├── hr.json │ ├── id.json │ ├── it.json │ ├── ja.json │ ├── ko.json │ ├── nl.json │ ├── pl.json │ ├── pt.json │ ├── ru.json │ ├── sr.json │ ├── sv.json │ ├── tr.json │ ├── uk.json │ ├── zh-CN.json │ └── zh-TW.json ├── package.json ├── public/ │ ├── .eslintrc.js │ ├── css/ │ │ ├── bootstrap-social.css │ │ ├── center.css │ │ ├── codemirror-extend/ │ │ │ ├── ayu-dark.css │ │ │ ├── ayu-mirage.css │ │ │ ├── one-dark.css │ │ │ ├── tomorrow-night-bright.css │ │ │ └── tomorrow-night-eighties.css │ │ ├── cover.css │ │ ├── extra.css │ │ ├── font.css │ │ ├── github-extract.css │ │ ├── google-font.css │ │ ├── index.css │ │ ├── markdown.css │ │ ├── mermaid.css │ │ ├── site.css │ │ ├── slide-preview.css │ │ └── slide.css │ ├── default.md │ ├── docs/ │ │ ├── features.md │ │ ├── privacy.md.example │ │ ├── release-notes.md │ │ ├── slide-example.md │ │ └── yaml-metadata.md │ ├── js/ │ │ ├── cover.js │ │ ├── extra.js │ │ ├── history.js │ │ ├── htmlExport.js │ │ ├── index.js │ │ ├── lib/ │ │ │ ├── appState.js │ │ │ ├── common/ │ │ │ │ ├── constant.ejs │ │ │ │ ├── login.js │ │ │ │ └── metrics.ejs │ │ │ ├── config/ │ │ │ │ └── index.js │ │ │ ├── editor/ │ │ │ │ ├── config.js │ │ │ │ ├── constants.js │ │ │ │ ├── index.js │ │ │ │ ├── markdown-lint/ │ │ │ │ │ └── index.js │ │ │ │ ├── spellcheck.js │ │ │ │ ├── statusbar.html │ │ │ │ ├── table-editor.js │ │ │ │ ├── toolbar.html │ │ │ │ ├── ui-elements.js │ │ │ │ └── utils.js │ │ │ ├── markdown/ │ │ │ │ └── utils.js │ │ │ ├── modeType.js │ │ │ ├── renderer/ │ │ │ │ ├── csvpreview.js │ │ │ │ ├── fretboard/ │ │ │ │ │ ├── css/ │ │ │ │ │ │ └── i.css │ │ │ │ │ └── fretboard.js │ │ │ │ └── lightbox/ │ │ │ │ ├── index.js │ │ │ │ └── lightbox.css │ │ │ └── syncscroll.js │ │ ├── locale.js │ │ ├── mathjax-config-extra.js │ │ ├── pretty.js │ │ ├── render.js │ │ ├── reveal-markdown.js │ │ ├── revealjs-plugins/ │ │ │ ├── elapsed-time-bar/ │ │ │ │ └── elapsed-time-bar.js │ │ │ └── spotlight/ │ │ │ └── spotlight.js │ │ ├── slide.js │ │ └── utils.js │ ├── markdown-lint/ │ │ └── css/ │ │ └── lint.css │ ├── uploads/ │ │ └── .gitkeep │ ├── vendor/ │ │ ├── abcjs_basic_3.1.1-min.js │ │ ├── codemirror-spell-checker/ │ │ │ ├── en_US.aff │ │ │ └── en_US.dic │ │ ├── inlineAttachment/ │ │ │ ├── codemirror.inline-attachment.js │ │ │ └── inline-attachment.js │ │ ├── jquery-textcomplete/ │ │ │ └── jquery.textcomplete.js │ │ ├── md-toc.js │ │ ├── ot/ │ │ │ ├── ajax-adapter.js │ │ │ ├── client.js │ │ │ ├── codemirror-adapter.js │ │ │ ├── compress.sh │ │ │ ├── editor-client.js │ │ │ ├── selection.js │ │ │ ├── socketio-adapter.js │ │ │ ├── text-operation.js │ │ │ ├── undo-manager.js │ │ │ └── wrapped-operation.js │ │ └── showup/ │ │ ├── showup.css │ │ └── showup.js │ └── views/ │ ├── codimd/ │ │ ├── body.ejs │ │ ├── foot.ejs │ │ ├── footer.ejs │ │ ├── head.ejs │ │ └── header.ejs │ ├── codimd.ejs │ ├── error.ejs │ ├── html.hbs │ ├── includes/ │ │ ├── header.ejs │ │ └── scripts.ejs │ ├── index/ │ │ ├── body.ejs │ │ ├── foot.ejs │ │ ├── footer.ejs │ │ ├── head.ejs │ │ └── header.ejs │ ├── index.ejs │ ├── pretty.ejs │ ├── shared/ │ │ ├── disqus.ejs │ │ ├── ga.ejs │ │ ├── help-modal.ejs │ │ ├── pandoc-export-modal.ejs │ │ ├── polyfill.ejs │ │ ├── refresh-modal.ejs │ │ ├── revision-modal.ejs │ │ └── signin-modal.ejs │ └── slide.ejs ├── scalingo.json ├── test/ │ ├── auth/ │ │ └── oauth2/ │ │ └── strategy.test.js │ ├── connectionQueue.test.js │ ├── csp.js │ ├── letter-avatars.js │ ├── realtime/ │ │ ├── cleanDanglingUser.test.js │ │ ├── connection.test.js │ │ ├── dirtyNoteUpdate.test.js │ │ ├── disconnect-process.test.js │ │ ├── extractNoteIdFromSocket.test.js │ │ ├── ifMayEdit.test.js │ │ ├── parseNoteIdFromSocket.test.js │ │ ├── realtime.test.js │ │ ├── saveRevisionJob.test.js │ │ ├── socket-events.test.js │ │ ├── updateNote.test.js │ │ └── utils.js │ └── testDoubles/ │ ├── ProcessQueueFake.js │ ├── loggerFake.js │ ├── otFake.js │ └── realtimeJobStub.js ├── utils/ │ └── string.js ├── webpack.common.js ├── webpack.dev.js ├── webpack.htmlexport.js └── webpack.prod.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .buildpacks ================================================ https://github.com/Scalingo/apt-buildpack https://github.com/Scalingo/nodejs-buildpack ================================================ FILE: .devcontainer/Dockerfile ================================================ # [Choice] Node.js version: 16, 14 ARG VARIANT=14-bullseye FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-${VARIANT} # [Optional] Uncomment this section to install additional OS packages. # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ # && apt-get -y install --no-install-recommends # [Optional] Uncomment if you want to install an additional version of node using nvm # ARG EXTRA_NODE_VERSION=10 # RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}" # [Optional] Uncomment if you want to install more global node modules RUN su node -c "npm install -g npm@6" ================================================ FILE: .devcontainer/devcontainer.json ================================================ { "name": "CodiMD", "dockerComposeFile": "docker-compose.yml", "service": "app", "workspaceFolder": "/workspace", // Set *default* container specific settings.json values on container create. "settings": { "terminal.integrated.shell.linux": "/bin/zsh", "sqltools.connections": [{ "name": "Container Database", "driver": "PostgreSQL", "previewLimit": 50, "server": "localhost", "port": 5432, "database": "codimd", "username": "codimd", "password": "codimd" }], }, // Add the IDs of extensions you want installed when the container is created. "extensions": [ "dbaeumer.vscode-eslint", "visualstudioexptteam.vscodeintellicode", "christian-kohler.path-intellisense", "standard.vscode-standard", "mtxr.sqltools", "mtxr.sqltools-driver-pg", "eamodio.gitlens", "codestream.codestream", "github.vscode-pull-request-github", "cschleiden.vscode-github-actions", "hbenl.vscode-mocha-test-adapter", "hbenl.vscode-test-explorer" ], // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], "portsAttributes": { "3000": { "label": "CodiMD server", "onAutoForward": "notify" }, "5432": { "label": "PostgreSQL", "onAutoForward": "notify" } }, // Use 'postCreateCommand' to run commands after the container is created. // "postCreateCommand": "yarn install", "postCreateCommand": "sudo chown -R node:node node_modules && /workspace/bin/setup", // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. "remoteUser": "node" } ================================================ FILE: .devcontainer/docker-compose.yml ================================================ version: '3' services: app: build: context: .. dockerfile: .devcontainer/Dockerfile args: VARIANT: 14-bullseye environment: - CMD_DB_URL=postgres://codimd:codimd@localhost/codimd - CMD_USECDN=false volumes: - ..:/workspace:cached - node_modules:/workspace/node_modules:cached # Overrides default command so things don't shut down after the process ends. command: sleep infinity # Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function. network_mode: service:db # Runs app on the same network as the database container, allows "forwardPorts" in devcontainer.json function. # Uncomment the next line to use a non-root user for all processes. # user: vscode # Use "forwardPorts" in **devcontainer.json** to forward an app port locally. # (Adding the "ports" property to this file will not forward from a Codespace.) db: image: postgres:12.7-alpine restart: unless-stopped volumes: - postgres-data:/var/lib/postgresql/data environment: - POSTGRES_USER=codimd - POSTGRES_PASSWORD=codimd - POSTGRES_DB=codimd # Add "forwardPorts": ["5432"] to **devcontainer.json** to forward PostgreSQL locally. # (Adding the "ports" property to this file will not forward from a Codespace.) volumes: node_modules: postgres-data: ================================================ FILE: .dockerignore ================================================ .idea coverage node_modules/ # ignore config files config.json .sequelizerc # ignore webpack build public/build public/views/build .nyc_output coverage/ ================================================ FILE: .editorconfig ================================================ root = true [*] indent_style = space indent_size = 2 trim_trailing_whitespace = true insert_final_newline = true [{*.html,*.ejs}] indent_style = space indent_size = 4 trim_trailing_whitespace = true [*.md] trim_trailing_whitespace = false [{.travis.yml,npm-shrinkwrap.json,package.json}] indent_style = space indent_size = 2 ================================================ FILE: .github/tests/README.md ================================================ # Test github actions with act ```bash act pull_request --container-architecture linux/arm64 -e .github/tests/pull-request.json -j ch eck-release-pr -P ubuntu-latest=catthehacker/ubuntu:act-latest ``` ================================================ FILE: .github/tests/pull-request.json ================================================ { "pull_request": { "head": { "ref": "release/1.2.3" }, "base": { "ref": "master" } } } ================================================ FILE: .github/workflows/build.yml ================================================ name: 'Test and Build' on: push: pull_request: workflow_dispatch: jobs: test-and-build: runs-on: ubuntu-latest strategy: matrix: node-version: [16.x] steps: - uses: actions/checkout@v4 # from https://stackoverflow.com/a/69649733 - name: Reconfigure git to use HTTP authentication run: > git config --global url."https://github.com/".insteadOf ssh://git@github.com/ - uses: actions/cache@v4 with: path: ~/.npm key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} restore-keys: | ${{ runner.os }}-node- - uses: actions/setup-node@v4 name: Use Node.js ${{ matrix.node-version }} with: node-version: ${{ matrix.node-version }} check-latest: true - run: npm ci - run: npm run test:ci - run: npm run build doctoc: runs-on: ubuntu-latest if: github.ref == 'refs/heads/master' || github.event.pull_request steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 name: Use Node.js 14 with: node-version: 14 check-latest: true - name: Install doctoc-check run: | npm install -g doctoc cp README.md README.md.orig npm run doctoc diff -q README.md README.md.orig ================================================ FILE: .github/workflows/check-release.yml ================================================ name: Release PR Checks on: workflow_dispatch: pull_request: branches: - master jobs: check-release-pr: if: startsWith(github.head_ref, 'release/') runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@v4 with: fetch-depth: 0 - name: Check for release-notes updates run: | if ! git diff --exit-code origin/develop -- public/docs/release-notes.md; then echo "Release notes updated." else echo "Error: Release notes not updated in the PR." exit 1 fi - name: Compare package.json version with master run: | git fetch origin master MASTER_PACKAGE_VERSION=$(git show origin/master:package.json | jq -r '.version') BRANCH_PACKAGE_VERSION=$(jq -r '.version' package.json) if [ "$BRANCH_PACKAGE_VERSION" != "$MASTER_PACKAGE_VERSION" ]; then echo "Version bumped in package.json." else echo "Error: Version in package.json has not been bumped." exit 1 fi ================================================ FILE: .github/workflows/push-image.yml ================================================ name: Build and push image on: release: types: [published] workflow_dispatch: inputs: runtime: description: 'Runtime image' required: true default: 'hackmdio/runtime:16.20.2-35fe7e39' buildpack: description: 'Buildpack image' required: true default: 'hackmdio/buildpack:16.20.2-35fe7e39' env: REGISTRY_IMAGE: hackmdio/hackmd jobs: build: runs-on: ubuntu-latest strategy: fail-fast: false matrix: platform: - linux/amd64 - linux/arm64 steps: - name: Prepare run: | platform=${{ matrix.platform }} echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV - name: Checkout uses: actions/checkout@v4 - name: Docker meta id: meta uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY_IMAGE }} - name: Set up QEMU uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push by digest id: build uses: docker/build-push-action@v5 with: context: . file: ./deployments/Dockerfile platforms: ${{ matrix.platform }} labels: ${{ steps.meta.outputs.labels }} outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true build-args: | RUNTIME=${{ github.event.inputs.runtime || 'hackmdio/runtime:16.20.2-35fe7e39' }} BUILDPACK=${{ github.event.inputs.buildpack || 'hackmdio/buildpack:16.20.2-35fe7e39' }} - name: Export digest run: | mkdir -p /tmp/digests digest="${{ steps.build.outputs.digest }}" touch "/tmp/digests/${digest#sha256:}" - name: Upload digest uses: actions/upload-artifact@v4 with: name: digests-${{ env.PLATFORM_PAIR }} path: /tmp/digests/* if-no-files-found: error retention-days: 1 merge: runs-on: ubuntu-latest needs: - build steps: - name: Download digests uses: actions/download-artifact@v4 with: path: /tmp/digests pattern: digests-* merge-multiple: true - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - name: Docker meta id: meta uses: docker/metadata-action@v5 with: images: ${{ env.REGISTRY_IMAGE }} tags: | type=match,pattern=\d.\d.\d type=sha,prefix= - name: Login to Docker Hub uses: docker/login-action@v3 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Create manifest list and push working-directory: /tmp/digests run: | docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ $(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *) - name: Inspect image run: | docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }} ================================================ FILE: .gitignore ================================================ node_modules composer.phar composer.lock .env.*.php .env.php .DS_Store .idea/ Thumbs.db npm-debug.log hackmd_io newrelic_agent.log logs/ tmp/ backups/ *.pid *.log *.sqlite # ignore config files config.json .sequelizerc # ignore webpack build public/build public/views/build public/uploads/* !public/uploads/.gitkeep /.nyc_output /coverage/ .vscode/settings.json ================================================ FILE: .mailmap ================================================ Max Wu Wu Cheng-Han Max Wu Cheng-Han, Wu Max Wu jackycute Max Wu Wu, Cheng-Han Max Wu jackycute Sheogorath Christoph (Sheogorath) Kern Raccoon Raccoon Li Raccoon Raccoon Peter Dave Hello Peter Dave Hello Claudius Coenen Claudius Coenen ================================================ FILE: .nvmrc ================================================ v16.20.2 ================================================ FILE: .sequelizerc.example ================================================ const path = require('path') const config = require('./lib/config') module.exports = { config: path.resolve('config.js'), 'migrations-path': path.resolve('lib', 'migrations'), 'models-path': path.resolve('lib', 'models'), url: config.dbURL } ================================================ FILE: AUTHORS ================================================ alecdwm bananaappletw Bartlomiej Szala BoHong Li Bryan Davis butlerx Cheng-Han, Wu Christian Schuhmann Colin Maudry Dmytro Kytsmen Fabien Meghazi Florian Rhiem geekyd GhiMax greenkeeperio-bot Himura Kazuto Ho33e5 Ian Dees Ikumi Shimizu <193s@users.noreply.github.com> ivanorsolic jackycute jackycute Jakub Sygnowski James Stephenson Jan Kunzmann Jannik Lorenz Jason Croft Johannes Weißl Jordan Matelsky Jun SAKATA Kaiyu Shi knjcode Kotaro Yamamoto Lars Karlsson Laura Kyle LluisArevalo Marcelo Alencar Martijnpold Max Wu neopostmodern NV Ömer Erdinç Yağmurlu p0v1n0m Pablo Guerrero Pablo Guerrero Paras Patrick Andersen Peter Dave Hello Peter Dave Hello Philipp Zumstein Raccoon Li robert Sergio Valverde Sheogorath Simon Joda Stößer S.Noda Stratos Gerakakis The Gitter Badger tkqubo tkykm Tom Wyckhuys Wonder Chang Wu Cheng-Han Xavier Marques xnum Yukai Huang zachariast Zankio 蒼時弦也 ================================================ FILE: Aptfile ================================================ libvips-dev ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing When contributing to this repository, please first discuss the change you wish to make via issue, email, or any other method with the owners of this repository before making a change. Please note we have a code of conduct, please follow it in all your interactions with the project. ## Pull Request Process 1. Ensure you signed all your commits with Developer Certificate of Origin (DCO). 2. Ensure any install or build dependencies are removed before the end of the layer when doing a build. 3. Update the README.md with details of changes to the interface, this includes new environment variables, exposed ports, useful file locations and container parameters. 4. Increase the version numbers in any examples files and the README.md to the new version that this Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). 5. You may merge the Pull Request in once you have the sign-off of two other developers, or if you do not have permission to do that, you may request the second reviewer to merge it for you. ## Contributor Code of Conduct As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery * Personal attacks * Trolling or insulting/derogatory comments * Public or private harassment * Publishing other's private information, such as physical or electronic addresses, without explicit permission * Other unethical or unprofessional conduct. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team. This code of conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.2.0, available at [http://contributor-covenant.org/version/1/2/0/](http://contributor-covenant.org/version/1/2/0/) ### Sign your work We use the Developer Certificate of Origin (DCO) as a additional safeguard for the CodiMD project. This is a well established and widely used mechanism to assure contributors have confirmed their right to license their contribution under the project's license. Please read [contribute/developer-certificate-of-origin][dcofile]. If you can certify it, then just add a line to every git commit message: ```` Signed-off-by: Random J Developer ```` Use your real name (sorry, no pseudonyms or anonymous contributions). If you set your `user.name` and `user.email` git configs, you can sign your commit automatically with `git commit -s`. You can also use git [aliases](https://git-scm.com/book/tr/v2/Git-Basics-Git-Aliases) like `git config --global alias.ci 'commit -s'`. Now you can commit with `git ci` and the commit will be signed. [dcofile]: https://github.com/hackmdio/codimd/blob/develop/contribute/developer-certificate-of-origin ================================================ FILE: FUNDING.json ================================================ { "drips": { "ethereum": { "ownedBy": "0xEd37B84FD84A834886aC07693aF6A9cd35040002" } } } ================================================ FILE: LICENSE ================================================ GNU AFFERO GENERAL PUBLIC LICENSE Version 3, 19 November 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU Affero General Public License is a free, copyleft license for software and other kinds of works, specifically designed to ensure cooperation with the community in the case of network server software. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. Developers that use our General Public Licenses protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License which gives you legal permission to copy, distribute and/or modify the software. A secondary benefit of defending all users' freedom is that improvements made in alternate versions of the program, if they receive widespread use, become available for other developers to incorporate. Many developers of free software are heartened and encouraged by the resulting cooperation. However, in the case of software used on network servers, this result may fail to come about. The GNU General Public License permits making a modified version and letting the public access it on a server without ever releasing its source code to the public. The GNU Affero General Public License is designed specifically to ensure that, in such cases, the modified source code becomes available to the community. It requires the operator of a network server to provide the source code of the modified version running there to the users of that server. Therefore, public use of a modified version, on a publicly accessible server, gives the public access to the source code of the modified version. An older license, called the Affero General Public License and published by Affero, was designed to accomplish similar goals. This is a different license, not a version of the Affero GPL, but Affero has released a new version of the Affero GPL which permits relicensing under this license. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU Affero General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Remote Network Interaction; Use with the GNU General Public License. Notwithstanding any other provision of this License, if you modify the Program, your modified version must prominently offer all users interacting with it remotely through a computer network (if your version supports such interaction) an opportunity to receive the Corresponding Source of your version by providing access to the Corresponding Source from a network server at no charge, through some standard or customary means of facilitating copying of software. This Corresponding Source shall include the Corresponding Source for any work covered by version 3 of the GNU General Public License that is incorporated pursuant to the following paragraph. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the work with which it is combined will remain governed by version 3 of the GNU General Public License. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU Affero General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If your software can interact with users remotely through a computer network, you should also make sure that it provides a way for users to get its source. For example, if your program is a web application, its interface could display a "Source" link that leads users to an archive of the code. There are many ways you could offer source, and different solutions will be better for different programs; see section 13 for the specific requirements. You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU AGPL, see . ================================================ FILE: Procfile ================================================ web: ./bin/heroku_start.sh ================================================ FILE: README.md ================================================ CodiMD === [![build status][build-image]][build-url] [![version][github-version-badge]][github-release-page] [![Gitter][gitter-image]][gitter-url] [![Matrix][matrix-image]][matrix-url] [![POEditor][poeditor-image]][poeditor-url] CodiMD lets you collaborate in real-time with markdown. Built on [HackMD](https://hackmd.io) source code, CodiMD lets you host and control your team's content with speed and ease. ![screenshot](https://raw.githubusercontent.com/hackmdio/codimd/develop/public/screenshot.png) # Table of Contents - [HackMD](#hackmd) - [CodiMD - The Open Source HackMD](#codimd---the-open-source-hackmd) - [Documentation](#documentation) - [Deployment](#deployment) - [Configuration](#configuration) - [Upgrading and Migration](#upgrading-and-migration) - [Developer](#developer) - [Contribution and Discussion](#contribution-and-discussion) - [Browser Support](#browser-support) - [License](#license) ## HackMD [HackMD](https://hackmd.io) helps developers write better documents and build active communities with open collaboration. HackMD is built with one promise - **You own and control all your content**: - You should be able to easily [download all your online content at once](https://hackmd.io/c/news/%2Fs%2Fr1cx3a3SE). - Your content formatting should be portable as well. (That's why we choose [markdown](https://hackmd.io/features#Typography).) - You should be able to control your content's presentation with HTML, [slide mode](https://hackmd.io/p/slide-example), or [book mode](https://hackmd.io/c/book-example/). ## CodiMD - The Open Source HackMD CodiMD is the free software version of [HackMD](https://hackmd.io), developed and open sourced by the HackMD team with reduced features (without book mode), you can use CodiMD for your community and own all your data. *(See the [origin of the name CodiMD](https://github.com/hackmdio/hackmd/issues/720).)* CodiMD is perfect for open communities, while HackMD emphasizes on permission and access controls for commercial use cases. HackMD team is committed to keep CodiMD open source. All contributions are welcome! ## Documentation You would find all documentation here: [CodiMD Documentation](https://hackmd.io/c/codimd-documentation) ### Deployment If you want to spin up an instance and start using immediately, see [Docker deployment](https://hackmd.io/c/codimd-documentation/%2Fs%2Fcodimd-docker-deployment). If you want to contribute to the project, start with [manual deployment](https://hackmd.io/c/codimd-documentation/%2Fs%2Fcodimd-manual-deployment). ### Configuration CodiMD is highly customizable, learn about all configuration options of networking, security, performance, resources, privilege, privacy, image storage, and authentication in [CodiMD Configuration](https://hackmd.io/c/codimd-documentation/%2Fs%2Fcodimd-configuration). ### Upgrading and Migration Upgrade CodiMD from previous version? See [this guide](https://hackmd.io/c/codimd-documentation/%2Fs%2Fcodimd-upgrade)
Migrating from Etherpad? Follow [this guide](https://hackmd.io/c/codimd-documentation/%2Fs%2Fcodimd-migration-etherpad) ### Developer Join our contributor community! Start from deploying [CodiMD manually](https://hackmd.io/c/codimd-documentation/%2Fs%2Fcodimd-manual-deployment), [connecting to your own database](https://hackmd.io/c/codimd-documentation/%2Fs%2Fcodimd-db-connection), [learn about the project structure](https://hackmd.io/c/codimd-documentation/%2Fs%2Fcodimd-project-structure), to [build your changes](https://hackmd.io/c/codimd-documentation/%2Fs%2Fcodimd-webpack) with the help of webpack. ## Contribution and Discussion All contributions are welcome! Even asking a question helps. | Project | Contribution Types | Contribution Venue | | ------- | ------------------ | ------------------ | |**CodiMD**|:couple: Community chat|[Gitter][gitter-url]| ||:bug: Issues, bugs, and feature requests|[Issue tracker](https://github.com/hackmdio/codimd/issues)| ||:books: Improve documentation|[Documentations](https://hackmd.io/c/codimd-documentation)| ||:pencil: Translation|[POEditor][poeditor-url]| ||:coffee: Donation|[Buy us coffee](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=KDGS4PREHX6QQ&lc=US&item_name=HackMD¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donate_LG%2egif%3aNonHosted)| |**HackMD**|:question: Issues related to [HackMD](https://hackmd.io/)|[Issue tracker](https://github.com/hackmdio/hackmd-io-issues/issues)| ||:pencil2: Translation|[hackmd-locales](https://github.com/hackmdio/hackmd-locales/tree/master/locales)| ## Browser Support CodiMD is a service that runs on Node.js, while users use the service through browsers. We support your users using the following browsers: - Chrome Chrome >= 47, Chrome for Android >= 47 - Safari Safari >= 9, iOS Safari >= 8.4 - Firefox Firefox >= 44 - Edge Edge >= 12 - Opera Opera >= 34, Opera Mini not supported - Android Browser >= 4.4 To stay up to date with your installation it's recommended to subscribe the [release feed][github-release-feed]. ## License **License under AGPL.** [gitter-image]: https://img.shields.io/badge/gitter-hackmdio/codimd-blue.svg [gitter-url]: https://gitter.im/hackmdio/hackmd [build-image]: https://github.com/hackmdio/codimd/actions/workflows/build.yml/badge.svg [build-url]: https://github.com/hackmdio/codimd/actions/workflows/build.yml [github-version-badge]: https://img.shields.io/github/release/hackmdio/codimd.svg [github-release-page]: https://github.com/hackmdio/codimd/releases [github-release-feed]: https://github.com/hackmdio/codimd/releases.atom [poeditor-image]: https://img.shields.io/badge/POEditor-translate-blue.svg [poeditor-url]: https://poeditor.com/join/project/q0nuPWyztp [matrix-image]: https://img.shields.io/matrix/hackmdio_hackmd:gitter.im?color=blue&logo=matrix [matrix-url]: https://matrix.to/#/#hackmdio_hackmd:gitter.im ================================================ FILE: app.js ================================================ 'use strict' // app // external modules var express = require('express') var ejs = require('ejs') var passport = require('passport') var methodOverride = require('method-override') var cookieParser = require('cookie-parser') var session = require('express-session') var SequelizeStore = require('connect-session-sequelize')(session.Store) var fs = require('fs') var path = require('path') var morgan = require('morgan') var passportSocketIo = require('passport.socketio') var helmet = require('helmet') var i18n = require('i18n') var flash = require('connect-flash') var apiMetrics = require('prometheus-api-metrics') // core var config = require('./lib/config') var logger = require('./lib/logger') var response = require('./lib/response') var models = require('./lib/models') var csp = require('./lib/csp') const { Environment } = require('./lib/config/enum') const { versionCheckMiddleware, checkVersion } = require('./lib/web/middleware/checkVersion') function createHttpServer () { if (config.useSSL) { const ca = (function () { let i, len const results = [] for (i = 0, len = config.sslCAPath.length; i < len; i++) { results.push(fs.readFileSync(config.sslCAPath[i], 'utf8')) } return results })() const options = { key: fs.readFileSync(config.sslKeyPath, 'utf8'), cert: fs.readFileSync(config.sslCertPath, 'utf8'), ca: ca, dhparam: fs.readFileSync(config.dhParamPath, 'utf8'), requestCert: false, rejectUnauthorized: false } return require('https').createServer(options, app) } else { return require('http').createServer(app) } } // server setup var app = express() var server = createHttpServer() // API and process monitoring with Prometheus for Node.js micro-service app.use(apiMetrics({ metricsPath: '/metrics/router', excludeRoutes: ['/metrics/codimd'] })) // logger app.use(morgan('combined', { stream: logger.stream })) // socket io var io = require('socket.io')(server) io.engine.ws = new (require('ws').Server)({ noServer: true, perMessageDeflate: false }) // others var realtime = require('./lib/realtime/realtime.js') // assign socket io to realtime realtime.io = io // methodOverride app.use(methodOverride('_method')) // session store var sessionStore = new SequelizeStore({ db: models.sequelize }) // use hsts to tell https users stick to this if (config.hsts.enable) { app.use(helmet.hsts({ maxAge: config.hsts.maxAgeSeconds, includeSubdomains: config.hsts.includeSubdomains, preload: config.hsts.preload })) } else if (config.useSSL) { logger.info('Consider enabling HSTS for extra security:') logger.info('https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security') } // Add referrer policy to improve privacy app.use( helmet.referrerPolicy({ policy: 'same-origin' }) ) // Generate a random nonce per request, for CSP with inline scripts app.use(csp.addNonceToLocals) // use Content-Security-Policy to limit XSS, dangerous plugins, etc. // https://helmetjs.github.io/docs/csp/ if (config.csp.enable) { app.use(helmet.contentSecurityPolicy({ directives: csp.computeDirectives() })) } else { logger.info('Content-Security-Policy is disabled. This may be a security risk.') } i18n.configure({ locales: ['en', 'zh-CN', 'zh-TW', 'fr', 'de', 'ja', 'es', 'ca', 'el', 'pt', 'it', 'tr', 'ru', 'nl', 'hr', 'pl', 'uk', 'hi', 'sv', 'eo', 'da', 'ko', 'id', 'sr'], cookie: 'locale', directory: path.join(__dirname, '/locales'), updateFiles: config.updateI18nFiles }) app.use(cookieParser()) app.use(i18n.init) // routes without sessions // static files app.use('/', express.static(path.join(__dirname, '/public'), { maxAge: config.staticCacheTime, index: false })) app.use('/docs', express.static(path.resolve(__dirname, config.docsPath), { maxAge: config.staticCacheTime })) app.use('/uploads', express.static(path.resolve(__dirname, config.uploadsPath), { maxAge: config.staticCacheTime })) app.use('/default.md', express.static(path.resolve(__dirname, config.defaultNotePath), { maxAge: config.staticCacheTime })) app.use(require('./lib/metrics').router) // session app.use(session({ name: config.sessionName, secret: config.sessionSecret, resave: false, // don't save session if unmodified saveUninitialized: true, // always create session to ensure the origin rolling: true, // reset maxAge on every response cookie: { maxAge: config.sessionLife }, store: sessionStore })) // session resumption var tlsSessionStore = {} server.on('newSession', function (id, data, cb) { tlsSessionStore[id.toString('hex')] = data cb() }) server.on('resumeSession', function (id, cb) { cb(null, tlsSessionStore[id.toString('hex')] || null) }) // middleware which blocks requests when we're too busy app.use(require('./lib/middleware/tooBusy')) app.use(flash()) // passport app.use(passport.initialize()) app.use(passport.session()) // check uri is valid before going further app.use(require('./lib/middleware/checkURIValid')) // redirect url without trailing slashes app.use(require('./lib/middleware/redirectWithoutTrailingSlashes')) app.use(require('./lib/middleware/codiMDVersion')) if (config.autoVersionCheck && process.env.NODE_ENV === Environment.production) { checkVersion(app) app.use(versionCheckMiddleware) } // routes need sessions // template files app.set('views', config.viewPath) // set render engine app.engine('ejs', ejs.renderFile) // set view engine app.set('view engine', 'ejs') // set generally available variables for all views app.locals.useCDN = config.useCDN app.locals.serverURL = config.serverURL app.locals.sourceURL = config.sourceURL app.locals.privacyPolicyURL = config.privacyPolicyURL app.locals.allowAnonymous = config.allowAnonymous app.locals.allowAnonymousEdits = config.allowAnonymousEdits app.locals.permission = config.permission app.locals.allowPDFExport = config.allowPDFExport app.locals.authProviders = { facebook: config.isFacebookEnable, twitter: config.isTwitterEnable, github: config.isGitHubEnable, bitbucket: config.isBitbucketEnable, gitlab: config.isGitLabEnable, mattermost: config.isMattermostEnable, dropbox: config.isDropboxEnable, google: config.isGoogleEnable, ldap: config.isLDAPEnable, ldapProviderName: config.ldap.providerName, saml: config.isSAMLEnable, oauth2: config.isOAuth2Enable, oauth2ProviderName: config.oauth2.providerName, openID: config.isOpenIDEnable, email: config.isEmailEnable, allowEmailRegister: config.allowEmailRegister } app.locals.versionInfo = { latest: true, versionItem: null } // Export/Import menu items app.locals.enableDropBoxSave = config.isDropboxEnable app.locals.enableGitHubGist = config.isGitHubEnable app.locals.enableGitlabSnippets = config.isGitlabSnippetsEnable app.use(require('./lib/routes').router) // response not found if no any route matxches app.get('*', function (req, res) { response.errorNotFound(req, res) }) // socket.io secure io.use(realtime.secure) // socket.io auth io.use(passportSocketIo.authorize({ cookieParser: cookieParser, key: config.sessionName, secret: config.sessionSecret, store: sessionStore, success: realtime.onAuthorizeSuccess, fail: realtime.onAuthorizeFail })) // socket.io heartbeat io.set('heartbeat interval', config.heartbeatInterval) io.set('heartbeat timeout', config.heartbeatTimeout) // socket.io connection io.sockets.on('connection', realtime.connection) // listen function startListen () { var address var listenCallback = function () { var schema = config.useSSL ? 'HTTPS' : 'HTTP' logger.info('%s Server listening at %s', schema, address) realtime.maintenance = false } // use unix domain socket if 'path' is specified if (config.path) { address = config.path server.listen(config.path, listenCallback) } else { address = config.host + ':' + config.port server.listen(config.port, config.host, listenCallback) } } // sync db then start listen models.sequelize.sync().then(function () { // check if realtime is ready if (realtime.isReady()) { models.Revision.checkAllNotesRevision(function (err, notes) { if (err) throw new Error(err) if (!notes || notes.length <= 0) return startListen() }) } else { throw new Error('server still not ready after db synced') } }).catch(err => { logger.error('Can\'t sync database') logger.error(err.stack) logger.error('Process will exit now.') process.exit(1) }) // log uncaught exception process.on('uncaughtException', function (err) { logger.error('An uncaught exception has occured.') logger.error(err) console.error(err) logger.error('Process will exit now.') process.exit(1) }) // install exit handler function handleTermSignals () { logger.info('CodiMD has been killed by signal, try to exit gracefully...') realtime.maintenance = true realtime.terminate() // disconnect all socket.io clients Object.keys(io.sockets.sockets).forEach(function (key) { var socket = io.sockets.sockets[key] // notify client server going into maintenance status socket.emit('maintenance') setTimeout(function () { socket.disconnect(true) }, 0) }) var checkCleanTimer = setInterval(function () { if (realtime.isReady()) { models.Revision.checkAllNotesRevision(function (err, notes) { if (err) return logger.error(err) if (!notes || notes.length <= 0) { clearInterval(checkCleanTimer) return process.exit(0) } }) } }, 100) setTimeout(() => { process.exit(1) }, 5000) } process.on('SIGINT', handleTermSignals) process.on('SIGTERM', handleTermSignals) process.on('SIGQUIT', handleTermSignals) ================================================ FILE: app.json ================================================ { "name": "CodiMD", "description": "Realtime collaborative markdown notes on all platforms", "keywords": [ "Collaborative", "Markdown", "Notes" ], "website": "https://github.com/hackmdio/codimd", "repository": "https://github.com/hackmdio/codimd", "logo": "https://github.com/hackmdio/codimd/raw/master/public/codimd-icon-1024.png", "success_url": "/", "env": { "NPM_CONFIG_PRODUCTION": { "description": "Let npm also install development build tool", "value": "false" }, "CMD_SESSION_SECRET": { "description": "Secret used to secure session cookies.", "required": false }, "CMD_HSTS_ENABLE": { "description": "whether to also use HSTS if HTTPS is enabled", "required": false }, "CMD_HSTS_MAX_AGE": { "description": "max duration, in seconds, to tell clients to keep HSTS status", "required": false }, "CMD_HSTS_INCLUDE_SUBDOMAINS": { "description": "whether to tell clients to also regard subdomains as HSTS hosts", "required": false }, "CMD_HSTS_PRELOAD": { "description": "whether to allow at all adding of the site to HSTS preloads (e.g. in browsers)", "required": false }, "CMD_DOMAIN": { "description": "domain name", "required": false }, "CMD_URL_PATH": { "description": "sub url path, like `www.example.com/`", "required": false }, "CMD_ALLOW_ORIGIN": { "description": "domain name whitelist (use comma to separate)", "required": false, "value": "localhost" }, "CMD_PROTOCOL_USESSL": { "description": "set to use ssl protocol for resources path (only applied when domain is set)", "required": false }, "CMD_URL_ADDPORT": { "description": "set to add port on callback url (port 80 or 443 won't applied) (only applied when domain is set)", "required": false }, "CMD_FACEBOOK_CLIENTID": { "description": "Facebook API client id", "required": false }, "CMD_FACEBOOK_CLIENTSECRET": { "description": "Facebook API client secret", "required": false }, "CMD_TWITTER_CONSUMERKEY": { "description": "Twitter API consumer key", "required": false }, "CMD_TWITTER_CONSUMERSECRET": { "description": "Twitter API consumer secret", "required": false }, "CMD_GITHUB_CLIENTID": { "description": "GitHub API client id", "required": false }, "CMD_GITHUB_CLIENTSECRET": { "description": "GitHub API client secret", "required": false }, "CMD_GITHUB_ORGANIZATIONS": { "description": "GitHub whitelist of orgs", "required": false }, "CMD_GITHUB_SCOPES": { "description": "GitHub OAuth API scopes", "required": false }, "CMD_BITBUCKET_CLIENTID": { "description": "Bitbucket API client id", "required": false }, "CMD_BITBUCKET_CLIENTSECRET": { "description": "Bitbucket API client secret", "required": false }, "CMD_GITLAB_BASEURL": { "description": "GitLab authentication endpoint, set to use other endpoint than GitLab.com (optional)", "required": false }, "CMD_GITLAB_CLIENTID": { "description": "GitLab API client id", "required": false }, "CMD_GITLAB_CLIENTSECRET": { "description": "GitLab API client secret", "required": false }, "CMD_GITLAB_SCOPE": { "description": "GitLab API client scope (optional)", "required": false }, "CMD_MATTERMOST_BASEURL": { "description": "Mattermost authentication endpoint", "required": false }, "CMD_MATTERMOST_CLIENTID": { "description": "Mattermost API client id", "required": false }, "CMD_MATTERMOST_CLIENTSECRET": { "description": "Mattermost API client secret", "required": false }, "CMD_DROPBOX_CLIENTID": { "description": "Dropbox API client id", "required": false }, "CMD_DROPBOX_CLIENTSECRET": { "description": "Dropbox API client secret", "required": false }, "CMD_DROPBOX_APP_KEY": { "description": "Dropbox app key (for import/export)", "required": false }, "CMD_GOOGLE_CLIENTID": { "description": "Google API client id", "required": false }, "CMD_GOOGLE_CLIENTSECRET": { "description": "Google API client secret", "required": false }, "CMD_IMGUR_CLIENTID": { "description": "Imgur API client id", "required": false }, "CMD_ALLOW_PDF_EXPORT": { "description": "Enable or disable PDF exports", "required": false }, "PGSSLMODE": { "description": "Enforce PG SSL mode", "value": "require" } }, "addons": [ "heroku-postgresql" ], "buildpacks": [ { "url": "https://github.com/alex88/heroku-buildpack-vips" }, { "url": "https://github.com/heroku/heroku-buildpack-nodejs" } ] } ================================================ FILE: babel.config.js ================================================ module.exports = { presets: [ ['@babel/preset-env', { targets: { node: '14' }, useBuiltIns: 'usage', corejs: 3, modules: 'auto' }] ], plugins: [ ['@babel/plugin-transform-runtime', { corejs: 3 }], '@babel/plugin-transform-nullish-coalescing-operator', '@babel/plugin-transform-optional-chaining' ] } ================================================ FILE: bin/heroku ================================================ #!/bin/bash set -e if [ ! -z "$DYNO" ]; then # setup config files cp .sequelizerc.example .sequelizerc cat << EOF > config.json { "production": { } } EOF fi ================================================ FILE: bin/heroku_start.sh ================================================ #!/bin/bash set -euo pipefail CMD_DB_URL="$DATABASE_URL" CMD_PORT="$PORT" npm run start ================================================ FILE: bin/manage_users ================================================ #!/usr/bin/env node // First configure the logger so it does not spam the console const logger = require('../lib/logger') logger.transports.forEach((transport) => { transport.level = 'warning' }) const models = require('../lib/models/') const readline = require('readline-sync') const minimist = require('minimist') function showUsage (tips) { console.log(`${tips} Command-line utility to create users for email-signin. Usage: bin/manage_users [--pass password] (--add | --del) user-email Options: --add\tAdd user with the specified user-email --del\tDelete user with specified user-email --reset\tReset user password with specified user-email --pass\tUse password from cmdline rather than prompting `) process.exit(1) } function getPass (argv, action) { // Find whether we use cmdline or prompt password if (typeof argv['pass'] !== 'string') { return readline.question(`Password for ${argv[action]}:`, { hideEchoBack: true }) } console.log('Using password from commandline...') return argv['pass'] } // Using an async function to be able to use await inside async function createUser (argv) { const existingUser = await models.User.findOne({ where: { email: argv['add'] } }) // Cannot create already-existing users if (existingUser) { console.log(`User with e-mail ${existingUser.email} already exists! Aborting ...`) process.exit(2) } const pass = getPass(argv, 'add') // Lets try to create, and check success const ref = await models.User.create({ email: argv['add'], password: pass }) if (ref === undefined) { console.log(`Could not create user with email ${argv['add']}`) process.exit(1) } else { console.log(`Created user with email ${argv['add']}`) } } // Using an async function to be able to use await inside async function deleteUser (argv) { // Cannot delete non-existing users const existingUser = await models.User.findOne({ where: { email: argv['del'] } }) if (!existingUser) { console.log(`User with e-mail ${argv['del']} does not exist, cannot delete`) process.exit(1) } // Sadly .destroy() does not return any success value with all // backends. See sequelize #4124 await existingUser.destroy() console.log(`Deleted user ${argv['del']} ...`) } // Using an async function to be able to use await inside async function resetUser (argv) { const existingUser = await models.User.findOne({ where: { email: argv['reset'] } }) // Cannot reset non-existing users if (!existingUser) { console.log(`User with e-mail ${argv['reset']} does not exist, cannot reset`) process.exit(1) } const pass = getPass(argv, 'reset') // set password and save existingUser.password = pass await existingUser.save() console.log(`User with email ${argv['reset']} password has been reset`) } const options = { add: createUser, del: deleteUser, reset: resetUser } // Perform commandline-parsing const argv = minimist(process.argv.slice(2)) const keys = Object.keys(options) const opts = keys.filter((key) => argv[key] !== undefined) const action = opts[0] // Check for options missing if (opts.length === 0) { showUsage(`You did not specify either ${keys.map((key) => `--${key}`).join(' or ')}!`) } // Check if both are specified if (opts.length > 1) { showUsage(`You cannot ${action.join(' and ')} at the same time!`) } // Check if not string if (typeof argv[action] !== 'string') { showUsage(`You must follow an email after --${action}`) } // Call respective processing functions options[action](argv).then(function () { process.exit(0) }) ================================================ FILE: bin/setup ================================================ #!/bin/bash set -e # run command at repo root CURRENT_PATH=$PWD if [ -d .git ]; then cd "$(git rev-parse --show-toplevel)" fi if ! type npm > /dev/null then cat << EOF npm is not installed, please install Node.js and npm. Read more on Node.js official website: https://nodejs.org Setup will not be run EOF exit 0 fi echo "copy config files" if [ ! -f config.json ]; then cp config.json.example config.json fi if [ ! -f .sequelizerc ]; then cp .sequelizerc.example .sequelizerc fi echo "install packages" npm install cat << EOF Edit the following config file to setup CodiMD server and client. Read more info at https://hackmd.io/c/codimd-documentation/%2Fs%2Fcodimd-configuration * config.json -- CodiMD config * .sequelizerc -- db config EOF # change directory back cd "$CURRENT_PATH" ================================================ FILE: config.js ================================================ const config = require('./lib/config') module.exports = config.db ================================================ FILE: config.json.example ================================================ { "test": { "db": { "dialect": "sqlite", "storage": ":memory:" }, "linkifyHeaderStyle": "gfm" }, "development": { "loglevel": "debug", "hsts": { "enable": false }, "db": { "dialect": "sqlite", "storage": "./db.codimd.sqlite" }, "linkifyHeaderStyle": "gfm" }, "production": { "domain": "localhost", "loglevel": "info", "hsts": { "enable": true, "maxAgeSeconds": 31536000, "includeSubdomains": true, "preload": true }, "csp": { "enable": true, "directives": { }, "upgradeInsecureRequests": "auto", "addDefaults": true, "addDisqus": true, "addGoogleAnalytics": true }, "db": { "username": "", "password": "", "database": "codimd", "host": "localhost", "port": "5432", "dialect": "postgres" }, "facebook": { "clientID": "change this", "clientSecret": "change this" }, "twitter": { "consumerKey": "change this", "consumerSecret": "change this" }, "github": { "clientID": "change this", "clientSecret": "change this", "organizations": ["names of github organizations allowed, optional"], "scopes": ["defaults to 'read:user' scope for auth user"] }, "gitlab": { "baseURL": "change this", "clientID": "change this", "clientSecret": "change this", "scope": "use 'read_user' scope for auth user only or remove this property if you need gitlab snippet import/export support (will result to be default scope 'api')", "version": "use 'v4' if gitlab version > 11, 'v3' otherwise. Default to 'v4'" }, "mattermost": { "baseURL": "change this", "clientID": "change this", "clientSecret": "change this" }, "dropbox": { "clientID": "change this", "clientSecret": "change this", "appKey": "change this" }, "google": { "clientID": "change this", "clientSecret": "change this", "apiKey": "change this" }, "ldap": { "url": "ldap://change_this", "bindDn": null, "bindCredentials": null, "searchBase": "change this", "searchFilter": "change this", "searchAttributes": ["change this"], "usernameField": "change this e.g. cn", "useridField": "change this e.g. uid", "tlsOptions": { "changeme": "See https://nodejs.org/api/tls.html#tls_tls_connect_options_callback" } }, "saml": { "idpSsoUrl": "change: authentication endpoint of IdP", "idpCert": "change: certificate file path of IdP in PEM format", "issuer": "change or delete: identity of the service provider (default: serverurl)", "identifierFormat": "change or delete: name identifier format (default: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress')", "disableRequestedAuthnContext": "change or delete: true to allow any authentication method, false restricts to password authentication method (default: false)", "groupAttribute": "change or delete: attribute name for group list (ex: memberOf)", "requiredGroups": [ "change or delete: group names that allowed" ], "externalGroups": [ "change or delete: group names that not allowed" ], "attribute": { "id": "change or delete this: attribute map for `id` (default: NameID)", "username": "change or delete this: attribute map for `username` (default: NameID)", "email": "change or delete this: attribute map for `email` (default: NameID)" } }, "imgur": { "clientID": "change this" }, "minio": { "accessKey": "change this", "secretKey": "change this", "endPoint": "change this", "secure": true, "port": 9000 }, "s3": { "accessKeyId": "change this", "secretAccessKey": "change this", "region": "change this" }, "s3bucket": "change this", "azure": { "connectionString": "change this", "container": "change this" }, "plantuml": { "server": "https://www.plantuml.com/plantuml" }, "linkifyHeaderStyle": "gfm" } } ================================================ FILE: contribute/developer-certificate-of-origin ================================================ Developer Certificate of Origin Version 1.1 Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 660 York Street, Suite 102, San Francisco, CA 94110 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. ================================================ FILE: deployments/Dockerfile ================================================ ARG RUNTIME ARG BUILDPACK FROM $BUILDPACK as BUILD COPY --chown=hackmd:hackmd . . ENV QT_QPA_PLATFORM=offscreen RUN set -xe && \ git reset --hard && \ git clean -fx && \ npm install && \ npm run build && \ cp ./deployments/docker-entrypoint.sh ./ && \ cp .sequelizerc.example .sequelizerc && \ rm -rf .git .gitignore .travis.yml .dockerignore .editorconfig .babelrc .mailmap .sequelizerc.example \ test docs contribute \ package-lock.json webpack.prod.js webpack.htmlexport.js webpack.dev.js webpack.common.js \ config.json.example README.md CONTRIBUTING.md AUTHORS node_modules FROM $RUNTIME USER hackmd ENV QT_QPA_PLATFORM=offscreen WORKDIR /home/hackmd/app COPY --chown=1500:1500 --from=BUILD /home/hackmd/app . RUN npm install --production && npm cache clean --force && rm -rf /tmp/{core-js-banners,phantomjs} EXPOSE 3000 ENTRYPOINT ["/home/hackmd/app/docker-entrypoint.sh"] ================================================ FILE: deployments/build.sh ================================================ #!/usr/bin/env bash set -eo pipefail set -x if [[ -z $1 || -z $2 ]];then echo "build.sh [runtime image] [buildpack image]" exit 1 fi CURRENT_DIR=$(dirname "$BASH_SOURCE") GIT_SHA1="$(git rev-parse HEAD)" GIT_SHORT_ID="${GIT_SHA1:0:8}" GIT_TAG=$(git describe --exact-match --tags $(git log -n1 --pretty='%h') 2>/dev/null || echo "") DOCKER_TAG="${GIT_TAG:-$GIT_SHORT_ID}" docker build --build-arg RUNTIME=$1 --build-arg BUILDPACK=$2 -t "hackmdio/hackmd:$DOCKER_TAG" -f "$CURRENT_DIR/Dockerfile" "$CURRENT_DIR/.." ================================================ FILE: deployments/docker-compose.yml ================================================ version: "3" services: database: image: postgres:11.6-alpine environment: - POSTGRES_USER=codimd - POSTGRES_PASSWORD=change_password - POSTGRES_DB=codimd volumes: - "database-data:/var/lib/postgresql/data" restart: always codimd: # you can use image or custom build below, image: nabo.codimd.dev/hackmdio/hackmd:2.5.3 # Using the following command to trigger the build # docker-compose -f deployments/docker-compose.yml up --build # build: # context: .. # dockerfile: ./deployments/Dockerfile # args: # RUNTIME: hackmdio/runtime:16.20.2-35fe7e39 # BUILDPACK: hackmdio/buildpack:16.20.2-35fe7e39 environment: - CMD_DB_URL=postgres://codimd:change_password@database/codimd - CMD_USECDN=false depends_on: - database ports: - "3000:3000" volumes: - upload-data:/home/hackmd/app/public/uploads restart: always volumes: database-data: {} upload-data: {} ================================================ FILE: deployments/docker-entrypoint.sh ================================================ #!/usr/bin/env bash set -euo pipefail if [[ "$#" -gt 0 ]]; then exec "$@" exit $? fi # check database and redis is ready pcheck -env CMD_DB_URL # run DB migrate NEED_MIGRATE=${CMD_AUTO_MIGRATE:=true} if [[ "$NEED_MIGRATE" = "true" ]] && [[ -f .sequelizerc ]] ; then npx sequelize db:migrate fi # start application node app.js ================================================ FILE: lib/auth/bitbucket/index.js ================================================ 'use strict' const Router = require('express').Router const passport = require('passport') const BitbucketStrategy = require('passport-bitbucket-oauth2').Strategy const config = require('../../config') const { setReturnToFromReferer, passportGeneralCallback } = require('../utils') const bitbucketAuth = module.exports = Router() passport.use(new BitbucketStrategy({ clientID: config.bitbucket.clientID, clientSecret: config.bitbucket.clientSecret, callbackURL: config.serverURL + '/auth/bitbucket/callback', state: true }, passportGeneralCallback)) bitbucketAuth.get('/auth/bitbucket', function (req, res, next) { setReturnToFromReferer(req) passport.authenticate('bitbucket')(req, res, next) }) // bitbucket auth callback bitbucketAuth.get('/auth/bitbucket/callback', passport.authenticate('bitbucket', { successReturnToOrRedirect: config.serverURL + '/', failureRedirect: config.serverURL + '/' }) ) ================================================ FILE: lib/auth/dropbox/index.js ================================================ 'use strict' const Router = require('express').Router const passport = require('passport') const DropboxStrategy = require('passport-dropbox-oauth2').Strategy const config = require('../../config') const { setReturnToFromReferer, passportGeneralCallback } = require('../utils') const dropboxAuth = module.exports = Router() passport.use(new DropboxStrategy({ apiVersion: '2', clientID: config.dropbox.clientID, clientSecret: config.dropbox.clientSecret, callbackURL: config.serverURL + '/auth/dropbox/callback', state: true }, passportGeneralCallback)) dropboxAuth.get('/auth/dropbox', function (req, res, next) { setReturnToFromReferer(req) passport.authenticate('dropbox-oauth2')(req, res, next) }) // dropbox auth callback dropboxAuth.get('/auth/dropbox/callback', passport.authenticate('dropbox-oauth2', { successReturnToOrRedirect: config.serverURL + '/', failureRedirect: config.serverURL + '/' }) ) ================================================ FILE: lib/auth/email/index.js ================================================ 'use strict' const Router = require('express').Router const passport = require('passport') const sequelize = require('sequelize') const validator = require('validator') const LocalStrategy = require('passport-local').Strategy const config = require('../../config') const models = require('../../models') const logger = require('../../logger') const { setReturnToFromReferer } = require('../utils') const { urlencodedParser } = require('../../utils') const response = require('../../response') const emailAuth = module.exports = Router() passport.use(new LocalStrategy({ usernameField: 'email' }, async function (email, password, done) { if (!validator.isEmail(email)) return done(null, false) try { const user = await models.User.findOne({ where: { email: sequelize.where( sequelize.fn('LOWER', sequelize.col('email')), email.toLowerCase() ) } }) if (!user) return done(null, false) if (!await user.verifyPassword(password)) return done(null, false) return done(null, user) } catch (err) { logger.error(err) return done(err) } })) if (config.allowEmailRegister) { emailAuth.post('/register', urlencodedParser, async function (req, res, next) { if (!req.body.email || !req.body.password) return response.errorBadRequest(req, res) if (!validator.isEmail(req.body.email)) return response.errorBadRequest(req, res) try { const [user, created] = await models.User.findOrCreate({ where: { email: req.body.email }, defaults: { password: req.body.password } }) if (!user) { req.flash('error', 'Failed to register your account, please try again.') return res.redirect(config.serverURL + '/') } if (created) { logger.debug('user registered: ' + user.id) req.flash('info', "You've successfully registered, please signin.") } else { logger.debug('user found: ' + user.id) req.flash('error', 'This email has been used, please try another one.') } return res.redirect(config.serverURL + '/') } catch (err) { logger.error('auth callback failed: ' + err) return response.errorInternalError(req, res) } }) } emailAuth.post('/login', urlencodedParser, function (req, res, next) { if (!req.body.email || !req.body.password) return response.errorBadRequest(req, res) if (!validator.isEmail(req.body.email)) return response.errorBadRequest(req, res) setReturnToFromReferer(req) passport.authenticate('local', { successReturnToOrRedirect: config.serverURL + '/', failureRedirect: config.serverURL + '/', failureFlash: 'Invalid email or password.' })(req, res, next) }) ================================================ FILE: lib/auth/facebook/index.js ================================================ 'use strict' const Router = require('express').Router const passport = require('passport') const FacebookStrategy = require('passport-facebook').Strategy const config = require('../../config') const { setReturnToFromReferer, passportGeneralCallback } = require('../utils') const facebookAuth = module.exports = Router() passport.use(new FacebookStrategy({ clientID: config.facebook.clientID, clientSecret: config.facebook.clientSecret, callbackURL: config.serverURL + '/auth/facebook/callback', state: true }, passportGeneralCallback)) facebookAuth.get('/auth/facebook', function (req, res, next) { setReturnToFromReferer(req) passport.authenticate('facebook')(req, res, next) }) // facebook auth callback facebookAuth.get('/auth/facebook/callback', passport.authenticate('facebook', { successReturnToOrRedirect: config.serverURL + '/', failureRedirect: config.serverURL + '/' }) ) ================================================ FILE: lib/auth/github/index.js ================================================ 'use strict' const Router = require('express').Router const request = require('request') const passport = require('passport') const GithubStrategy = require('passport-github').Strategy const { InternalOAuthError } = require('passport-oauth2') const config = require('../../config') const response = require('../../response') const { setReturnToFromReferer, passportGeneralCallback } = require('../utils') const { URL } = require('url') const { promisify } = require('util') const rp = promisify(request) const githubAuth = module.exports = Router() function githubUrl (path) { return config.github.enterpriseURL && new URL(path, config.github.enterpriseURL).toString() } passport.use(new GithubStrategy({ scope: (config.github.organizations ? config.github.scopes.concat(['read:org']) : config.github.scope), clientID: config.github.clientID, clientSecret: config.github.clientSecret, callbackURL: config.serverURL + '/auth/github/callback', authorizationURL: githubUrl('login/oauth/authorize'), tokenURL: githubUrl('login/oauth/access_token'), userProfileURL: githubUrl('api/v3/user'), state: true }, async (accessToken, refreshToken, profile, done) => { if (!config.github.organizations) { return passportGeneralCallback(accessToken, refreshToken, profile, done) } const { statusCode, body: data } = await rp({ url: `https://api.github.com/user/orgs`, method: 'GET', json: true, timeout: 2000, headers: { Authorization: `token ${accessToken}`, 'User-Agent': 'nodejs-http' } }) if (statusCode !== 200) { return done(InternalOAuthError( `Failed to query organizations for user: ${profile.username}` )) } const orgs = data.map(({ login }) => login) for (const org of orgs) { if (config.github.organizations.includes(org)) { return passportGeneralCallback(accessToken, refreshToken, profile, done) } } return done(InternalOAuthError( `User orgs not whitelisted: ${profile.username} (${orgs.join(',')})` )) })) githubAuth.get('/auth/github', function (req, res, next) { setReturnToFromReferer(req) passport.authenticate('github')(req, res, next) }) githubAuth.get('/auth/github/callback', passport.authenticate('github', { successReturnToOrRedirect: config.serverURL + '/', failureRedirect: config.serverURL + '/' }) ) // github callback actions githubAuth.get('/auth/github/callback/:noteId/:action', response.githubActions) ================================================ FILE: lib/auth/gitlab/index.js ================================================ 'use strict' const Router = require('express').Router const passport = require('passport') const GitlabStrategy = require('passport-gitlab2').Strategy const config = require('../../config') const response = require('../../response') const { setReturnToFromReferer, passportGeneralCallback } = require('../utils') const HttpsProxyAgent = require('https-proxy-agent') const gitlabAuth = module.exports = Router() const gitlabAuthStrategy = new GitlabStrategy({ baseURL: config.gitlab.baseURL, clientID: config.gitlab.clientID, clientSecret: config.gitlab.clientSecret, scope: config.gitlab.scope, callbackURL: config.serverURL + '/auth/gitlab/callback', state: true }, passportGeneralCallback) if (process.env.https_proxy) { const httpsProxyAgent = new HttpsProxyAgent(process.env.https_proxy) gitlabAuthStrategy._oauth2.setAgent(httpsProxyAgent) } passport.use(gitlabAuthStrategy) gitlabAuth.get('/auth/gitlab', function (req, res, next) { setReturnToFromReferer(req) passport.authenticate('gitlab')(req, res, next) }) // gitlab auth callback gitlabAuth.get('/auth/gitlab/callback', passport.authenticate('gitlab', { successReturnToOrRedirect: config.serverURL + '/', failureRedirect: config.serverURL + '/' }) ) if (!config.gitlab.scope || config.gitlab.scope === 'api') { // gitlab callback actions gitlabAuth.get('/auth/gitlab/callback/:noteId/:action', response.gitlabActions) } ================================================ FILE: lib/auth/google/index.js ================================================ 'use strict' const Router = require('express').Router const passport = require('passport') var GoogleStrategy = require('passport-google-oauth20').Strategy const config = require('../../config') const { setReturnToFromReferer, passportGeneralCallback } = require('../utils') const googleAuth = module.exports = Router() passport.use(new GoogleStrategy({ clientID: config.google.clientID, clientSecret: config.google.clientSecret, callbackURL: config.serverURL + '/auth/google/callback', userProfileURL: 'https://www.googleapis.com/oauth2/v3/userinfo', state: true }, passportGeneralCallback)) googleAuth.get('/auth/google', function (req, res, next) { setReturnToFromReferer(req) passport.authenticate('google', { scope: ['profile'], hostedDomain: config.google.hostedDomain })(req, res, next) }) // google auth callback googleAuth.get('/auth/google/callback', passport.authenticate('google', { successReturnToOrRedirect: config.serverURL + '/', failureRedirect: config.serverURL + '/' }) ) ================================================ FILE: lib/auth/index.js ================================================ 'use strict' const Router = require('express').Router const passport = require('passport') const config = require('../config') const logger = require('../logger') const models = require('../models') const authRouter = module.exports = Router() // serialize and deserialize passport.serializeUser(function (user, done) { logger.info('serializeUser: ' + user.id) return done(null, user.id) }) passport.deserializeUser(function (id, done) { models.User.findOne({ where: { id: id } }).then(function (user) { // Don't die on non-existent user if (user == null) { return done(null, false, { message: 'Invalid UserID' }) } logger.info('deserializeUser: ' + user.id) return done(null, user) }).catch(function (err) { logger.error(err) return done(err, null) }) }) if (config.isFacebookEnable) authRouter.use(require('./facebook')) if (config.isTwitterEnable) authRouter.use(require('./twitter')) if (config.isGitHubEnable) authRouter.use(require('./github')) if (config.isBitbucketEnable) authRouter.use(require('./bitbucket')) if (config.isGitLabEnable) authRouter.use(require('./gitlab')) if (config.isMattermostEnable) authRouter.use(require('./mattermost')) if (config.isDropboxEnable) authRouter.use(require('./dropbox')) if (config.isGoogleEnable) authRouter.use(require('./google')) if (config.isLDAPEnable) authRouter.use(require('./ldap')) if (config.isSAMLEnable) authRouter.use(require('./saml')) if (config.isOAuth2Enable) authRouter.use(require('./oauth2')) if (config.isEmailEnable) authRouter.use(require('./email')) if (config.isOpenIDEnable) authRouter.use(require('./openid')) // logout authRouter.get('/logout', function (req, res, next) { if (config.debug && req.isAuthenticated()) { logger.debug('user logout: ' + req.user.id) } req.logout((err) => { if (err) { return next(err) } res.redirect(config.serverURL + '/') }) }) ================================================ FILE: lib/auth/ldap/index.js ================================================ 'use strict' const Router = require('express').Router const passport = require('passport') const LDAPStrategy = require('passport-ldapauth') const config = require('../../config') const models = require('../../models') const logger = require('../../logger') const { setReturnToFromReferer } = require('../utils') const { urlencodedParser } = require('../../utils') const response = require('../../response') const ldapAuth = module.exports = Router() passport.use(new LDAPStrategy({ server: { url: config.ldap.url || null, bindDN: config.ldap.bindDn || null, bindCredentials: config.ldap.bindCredentials || null, searchBase: config.ldap.searchBase || null, searchFilter: config.ldap.searchFilter || null, searchAttributes: config.ldap.searchAttributes || null, tlsOptions: config.ldap.tlsOptions || null } }, function (user, done) { var uuid = user.uidNumber || user.uid || user.sAMAccountName || undefined if (config.ldap.useridField && user[config.ldap.useridField]) { uuid = user[config.ldap.useridField] } if (typeof uuid === 'undefined') { throw new Error('Could not determine UUID for LDAP user. Check that ' + 'either uidNumber, uid or sAMAccountName is set in your LDAP directory ' + 'or use another unique attribute and configure it using the ' + '"useridField" option in ldap settings.') } var username = uuid if (config.ldap.usernameField && user[config.ldap.usernameField]) { username = user[config.ldap.usernameField] } var profile = { id: 'LDAP-' + uuid, username: username, displayName: user.displayName, emails: user.mail ? Array.isArray(user.mail) ? user.mail : [user.mail] : [], avatarUrl: null, profileUrl: null, provider: 'ldap' } var stringifiedProfile = JSON.stringify(profile) models.User.findOrCreate({ where: { profileid: profile.id.toString() }, defaults: { profile: stringifiedProfile } }).spread(function (user, created) { if (user) { var needSave = false if (user.profile !== stringifiedProfile) { user.profile = stringifiedProfile needSave = true } if (needSave) { user.save().then(function () { if (config.debug) { logger.debug('user login: ' + user.id) } return done(null, user) }) } else { if (config.debug) { logger.debug('user login: ' + user.id) } return done(null, user) } } }).catch(function (err) { logger.error('ldap auth failed: ' + err) return done(err, null) }) })) ldapAuth.post('/auth/ldap', urlencodedParser, function (req, res, next) { if (!req.body.username || !req.body.password) return response.errorBadRequest(req, res) setReturnToFromReferer(req) passport.authenticate('ldapauth', { successReturnToOrRedirect: config.serverURL + '/', failureRedirect: config.serverURL + '/', failureFlash: true })(req, res, next) }) ================================================ FILE: lib/auth/mattermost/index.js ================================================ 'use strict' require('babel-polyfill') require('isomorphic-fetch') const Router = require('express').Router const passport = require('passport') const { Client4: MattermostClient } = require('@mattermost/client') const OAuthStrategy = require('passport-oauth2').Strategy const config = require('../../config') const { setReturnToFromReferer, passportGeneralCallback } = require('../utils') const mattermostAuth = module.exports = Router() const mattermostClient = new MattermostClient() const mattermostStrategy = new OAuthStrategy({ authorizationURL: config.mattermost.baseURL + '/oauth/authorize', tokenURL: config.mattermost.baseURL + '/oauth/access_token', clientID: config.mattermost.clientID, clientSecret: config.mattermost.clientSecret, callbackURL: config.serverURL + '/auth/mattermost/callback' }, passportGeneralCallback) mattermostStrategy.userProfile = (accessToken, done) => { mattermostClient.setUrl(config.mattermost.baseURL) mattermostClient.setToken(accessToken) mattermostClient.getMe() .then((data) => done(null, data)) .catch((err) => done(err)) } passport.use(mattermostStrategy) mattermostAuth.get('/auth/mattermost', function (req, res, next) { setReturnToFromReferer(req) passport.authenticate('oauth2')(req, res, next) }) // mattermost auth callback mattermostAuth.get('/auth/mattermost/callback', passport.authenticate('oauth2', { successReturnToOrRedirect: config.serverURL + '/', failureRedirect: config.serverURL + '/' }) ) ================================================ FILE: lib/auth/oauth2/index.js ================================================ 'use strict' const Router = require('express').Router const passport = require('passport') const config = require('../../config') const { setReturnToFromReferer, passportGeneralCallback } = require('../utils') const { OAuth2CustomStrategy } = require('./strategy') const oauth2Auth = module.exports = Router() passport.use(new OAuth2CustomStrategy({ authorizationURL: config.oauth2.authorizationURL, tokenURL: config.oauth2.tokenURL, clientID: config.oauth2.clientID, clientSecret: config.oauth2.clientSecret, callbackURL: config.serverURL + '/auth/oauth2/callback', userProfileURL: config.oauth2.userProfileURL, state: config.oauth2.state, scope: config.oauth2.scope }, passportGeneralCallback)) oauth2Auth.get('/auth/oauth2', function (req, res, next) { setReturnToFromReferer(req) passport.authenticate('oauth2')(req, res, next) }) // github auth callback oauth2Auth.get('/auth/oauth2/callback', passport.authenticate('oauth2', { successReturnToOrRedirect: config.serverURL + '/', failureRedirect: config.serverURL + '/' }) ) ================================================ FILE: lib/auth/oauth2/strategy.js ================================================ 'use strict' const { Strategy, InternalOAuthError } = require('passport-oauth2') const config = require('../../config') function parseProfile (data) { const id = extractProfileAttribute(data, config.oauth2.userProfileIdAttr) const username = extractProfileAttribute(data, config.oauth2.userProfileUsernameAttr) const displayName = extractProfileAttribute(data, config.oauth2.userProfileDisplayNameAttr) const email = extractProfileAttribute(data, config.oauth2.userProfileEmailAttr) const photo = extractProfileAttribute(data, config.oauth2.userProfilePhotoAttr) if (!username) { throw new Error('cannot fetch username: please set correct CMD_OAUTH2_USER_PROFILE_USERNAME_ATTR') } return { id: id || username, username: username, displayName: displayName, email: email, photo: photo } } function extractProfileAttribute (data, path) { if (!data) return undefined if (typeof path !== 'string') return undefined // can handle stuff like `attrs[0].name` path = path.split('.') for (const segment of path) { const m = segment.match(/([\d\w]+)\[(.*)\]/) if (!m) { data = data[segment] } else { if (m.length < 3) return undefined if (!data[m[1]]) return undefined data = data[m[1]][m[2]] } if (!data) return undefined } return data } function checkAuthorization (data, done) { const roles = extractProfileAttribute(data, config.oauth2.rolesClaim) if (config.oauth2.accessRole && roles) { if (!roles.includes(config.oauth2.accessRole)) { return done('Permission denied', null) } } } class OAuth2CustomStrategy extends Strategy { constructor (options, verify) { options.customHeaders = options.customHeaders || {} super(options, verify) this.name = 'oauth2' this._userProfileURL = options.userProfileURL this._oauth2.useAuthorizationHeaderforGET(true) } userProfile (accessToken, done) { this._oauth2.get(this._userProfileURL, accessToken, function (err, body, res) { if (err) { return done(new InternalOAuthError('Failed to fetch user profile', err)) } let profile, json try { json = JSON.parse(body) checkAuthorization(json, done) profile = parseProfile(json) } catch (ex) { return done(new InternalOAuthError('Failed to parse user profile' + ex.toString())) } profile.provider = 'oauth2' done(null, profile) }) } } exports.OAuth2CustomStrategy = OAuth2CustomStrategy exports.parseProfile = parseProfile exports.extractProfileAttribute = extractProfileAttribute ================================================ FILE: lib/auth/openid/index.js ================================================ 'use strict' const Router = require('express').Router const passport = require('passport') const OpenIDStrategy = require('@passport-next/passport-openid').Strategy const config = require('../../config') const models = require('../../models') const logger = require('../../logger') const { urlencodedParser } = require('../../utils') const { setReturnToFromReferer } = require('../utils') const openIDAuth = module.exports = Router() passport.use(new OpenIDStrategy({ returnURL: config.serverURL + '/auth/openid/callback', realm: config.serverURL, profile: true }, function (openid, profile, done) { var stringifiedProfile = JSON.stringify(profile) models.User.findOrCreate({ where: { profileid: openid }, defaults: { profile: stringifiedProfile } }).spread(function (user, created) { if (user) { var needSave = false if (user.profile !== stringifiedProfile) { user.profile = stringifiedProfile needSave = true } if (needSave) { user.save().then(function () { if (config.debug) { logger.info('user login: ' + user.id) } return done(null, user) }) } else { if (config.debug) { logger.info('user login: ' + user.id) } return done(null, user) } } }).catch(function (err) { logger.error('auth callback failed: ' + err) return done(err, null) }) })) openIDAuth.post('/auth/openid', urlencodedParser, function (req, res, next) { setReturnToFromReferer(req) passport.authenticate('openid')(req, res, next) }) // openID auth callback openIDAuth.get('/auth/openid/callback', passport.authenticate('openid', { successReturnToOrRedirect: config.serverURL + '/', failureRedirect: config.serverURL + '/' }) ) ================================================ FILE: lib/auth/saml/index.js ================================================ 'use strict' const Router = require('express').Router const passport = require('passport') const SamlStrategy = require('passport-saml').Strategy const config = require('../../config') const models = require('../../models') const logger = require('../../logger') const { urlencodedParser } = require('../../utils') const fs = require('fs') const intersection = function (array1, array2) { return array1.filter((n) => array2.includes(n)) } const samlAuth = module.exports = Router() passport.use(new SamlStrategy({ callbackUrl: config.serverURL + '/auth/saml/callback', entryPoint: config.saml.idpSsoUrl, issuer: config.saml.issuer || config.serverURL, cert: fs.readFileSync(config.saml.idpCert, 'utf-8'), identifierFormat: config.saml.identifierFormat, disableRequestedAuthnContext: config.saml.disableRequestedAuthnContext }, function (user, done) { // check authorization if needed if (config.saml.externalGroups && config.saml.groupAttribute) { var externalGroups = intersection(config.saml.externalGroups, user[config.saml.groupAttribute]) if (externalGroups.length > 0) { logger.error('saml permission denied: ' + externalGroups.join(', ')) return done('Permission denied', null) } } if (config.saml.requiredGroups && config.saml.groupAttribute) { if (intersection(config.saml.requiredGroups, user[config.saml.groupAttribute]).length === 0) { logger.error('saml permission denied') return done('Permission denied', null) } } // user creation var uuid = user[config.saml.attribute.id] || user.nameID var profile = { provider: 'saml', id: 'SAML-' + uuid, username: user[config.saml.attribute.username] || user.nameID, emails: user[config.saml.attribute.email] ? [user[config.saml.attribute.email]] : [] } if (profile.emails.length === 0 && config.saml.identifierFormat === 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress') { profile.emails.push(user.nameID) } var stringifiedProfile = JSON.stringify(profile) models.User.findOrCreate({ where: { profileid: profile.id.toString() }, defaults: { profile: stringifiedProfile } }).spread(function (user, created) { if (user) { var needSave = false if (user.profile !== stringifiedProfile) { user.profile = stringifiedProfile needSave = true } if (needSave) { user.save().then(function () { if (config.debug) { logger.debug('user login: ' + user.id) } return done(null, user) }) } else { if (config.debug) { logger.debug('user login: ' + user.id) } return done(null, user) } } }).catch(function (err) { logger.error('saml auth failed: ' + err) return done(err, null) }) })) samlAuth.get('/auth/saml', passport.authenticate('saml', { successReturnToOrRedirect: config.serverURL + '/', failureRedirect: config.serverURL + '/' }) ) samlAuth.post('/auth/saml/callback', urlencodedParser, passport.authenticate('saml', { successReturnToOrRedirect: config.serverURL + '/', failureRedirect: config.serverURL + '/' }) ) samlAuth.get('/auth/saml/metadata', function (req, res) { res.type('application/xml') res.send(passport._strategy('saml').generateServiceProviderMetadata()) }) ================================================ FILE: lib/auth/twitter/index.js ================================================ 'use strict' const Router = require('express').Router const passport = require('passport') const TwitterStrategy = require('passport-twitter').Strategy const config = require('../../config') const { setReturnToFromReferer, passportGeneralCallback } = require('../utils') const twitterAuth = module.exports = Router() passport.use(new TwitterStrategy({ consumerKey: config.twitter.consumerKey, consumerSecret: config.twitter.consumerSecret, callbackURL: config.serverURL + '/auth/twitter/callback', state: true }, passportGeneralCallback)) twitterAuth.get('/auth/twitter', function (req, res, next) { setReturnToFromReferer(req) passport.authenticate('twitter')(req, res, next) }) // twitter auth callback twitterAuth.get('/auth/twitter/callback', passport.authenticate('twitter', { successReturnToOrRedirect: config.serverURL + '/', failureRedirect: config.serverURL + '/' }) ) ================================================ FILE: lib/auth/utils.js ================================================ 'use strict' const models = require('../models') const config = require('../config') const logger = require('../logger') exports.setReturnToFromReferer = function setReturnToFromReferer (req) { if (!req.session) req.session = {} var referer = req.get('referer') var nextURL if (referer) { try { var refererSearchParams = new URLSearchParams(new URL(referer).search) nextURL = refererSearchParams.get('next') } catch (err) { logger.warn(err) } } if (nextURL) { var isRelativeNextURL = nextURL.indexOf('://') === -1 && !nextURL.startsWith('//') if (isRelativeNextURL) { req.session.returnTo = (new URL(nextURL, config.serverURL)).toString() } else { req.session.returnTo = config.serverURL } } else { req.session.returnTo = referer } } exports.passportGeneralCallback = function callback (accessToken, refreshToken, profile, done) { var stringifiedProfile = JSON.stringify(profile) models.User.findOrCreate({ where: { profileid: profile.id.toString() }, defaults: { profile: stringifiedProfile, accessToken: accessToken, refreshToken: refreshToken } }).spread(function (user, created) { if (user) { var needSave = false if (user.profile !== stringifiedProfile) { user.profile = stringifiedProfile needSave = true } if (user.accessToken !== accessToken) { user.accessToken = accessToken needSave = true } if (user.refreshToken !== refreshToken) { user.refreshToken = refreshToken needSave = true } if (needSave) { user.save().then(function () { if (config.debug) { logger.info('user login: ' + user.id) } return done(null, user) }) } else { if (config.debug) { logger.info('user login: ' + user.id) } return done(null, user) } } }).catch(function (err) { logger.error('auth callback failed: ' + err) return done(err, null) }) } ================================================ FILE: lib/config/default.js ================================================ 'use strict' const os = require('os') module.exports = { domain: '', urlPath: '', host: '0.0.0.0', port: 3000, loglevel: 'info', urlAddPort: false, allowOrigin: ['localhost'], useSSL: false, hsts: { enable: true, maxAgeSeconds: 60 * 60 * 24 * 365, includeSubdomains: false, preload: true }, csp: { enable: true, directives: { }, addDefaults: true, addDisqus: true, addGoogleAnalytics: true, upgradeInsecureRequests: 'auto', reportURI: undefined }, protocolUseSSL: false, useCDN: true, allowAnonymous: false, allowAnonymousEdits: true, allowAnonymousViews: true, allowFreeURL: false, forbiddenNoteIDs: ['robots.txt', 'favicon.ico', 'api'], defaultPermission: 'editable', dbURL: '', db: {}, privacyPolicyURL: '', // ssl path sslKeyPath: '', sslCertPath: '', sslCAPath: '', dhParamPath: '', // other path viewPath: './public/views', tmpPath: os.tmpdir(), defaultNotePath: './public/default.md', docsPath: './public/docs', uploadsPath: './public/uploads', // session sessionName: 'connect.sid', sessionSecret: 'secret', sessionSecretLen: 128, sessionLife: 14 * 24 * 60 * 60 * 1000, // 14 days staticCacheTime: 1 * 24 * 60 * 60 * 1000, // 1 day // socket.io heartbeatInterval: 5000, heartbeatTimeout: 10000, // toobusy-js responseMaxLag: 70, // document documentMaxLength: 100000, // image upload setting, available options are imgur/s3/filesystem/azure/lutim imageUploadType: 'filesystem', lutim: { url: 'https://framapic.org/' }, imgur: { clientID: undefined }, s3: { accessKeyId: undefined, secretAccessKey: undefined, region: undefined, endpoint: undefined, baseURL: undefined }, minio: { accessKey: undefined, secretKey: undefined, endPoint: undefined, secure: true, port: 9000 }, s3bucket: undefined, azure: { connectionString: undefined, container: undefined }, // authentication oauth2: { providerName: undefined, authorizationURL: undefined, tokenURL: undefined, clientID: undefined, clientSecret: undefined, baseURL: undefined, userProfileURL: undefined, userProfileUsernameAttr: 'username', userProfileDisplayNameAttr: 'displayName', userProfileEmailAttr: 'email', userProfilePhotoAttr: 'photo', state: true, scope: 'email' }, facebook: { clientID: undefined, clientSecret: undefined }, twitter: { consumerKey: undefined, consumerSecret: undefined }, github: { enterpriseURL: undefined, // if you use github.com, not need to specify clientID: undefined, clientSecret: undefined, organizations: [], scopes: ['read:user'] }, gitlab: { baseURL: undefined, clientID: undefined, clientSecret: undefined, scope: undefined, version: 'v4' }, mattermost: { baseURL: undefined, clientID: undefined, clientSecret: undefined }, dropbox: { clientID: undefined, clientSecret: undefined, appKey: undefined }, google: { clientID: undefined, clientSecret: undefined, hostedDomain: undefined }, ldap: { providerName: undefined, url: undefined, bindDn: undefined, bindCredentials: undefined, searchBase: undefined, searchFilter: undefined, searchAttributes: undefined, usernameField: undefined, useridField: undefined, tlsca: undefined }, saml: { idpSsoUrl: undefined, idpCert: undefined, issuer: undefined, identifierFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress', disableRequestedAuthnContext: false, groupAttribute: undefined, externalGroups: [], requiredGroups: [], attribute: { id: undefined, username: undefined, email: undefined } }, plantuml: { server: 'https://www.plantuml.com/plantuml' }, email: true, allowEmailRegister: true, allowGravatar: true, allowPDFExport: true, openID: false, defaultUseHardbreak: true, // linkifyHeaderStyle - How is a header text converted into a link id. // Header Example: "3.1. Good Morning my Friend! - Do you have 5$?" // * 'keep-case' is the legacy CodiMD value. // Generated id: "31-Good-Morning-my-Friend---Do-you-have-5" // * 'lower-case' is the same like legacy (see above), but converted to lower-case. // Generated id: "#31-good-morning-my-friend---do-you-have-5" // * 'gfm' _GitHub-Flavored Markdown_ style as described here: // https://gist.github.com/asabaylus/3071099#gistcomment-1593627 // It works like 'lower-case', but making sure the ID is unique. // This is What GitHub, GitLab and (hopefully) most other tools use. // Generated id: "31-good-morning-my-friend---do-you-have-5" // 2nd appearance: "31-good-morning-my-friend---do-you-have-5-1" // 3rd appearance: "31-good-morning-my-friend---do-you-have-5-2" linkifyHeaderStyle: 'keep-case', autoVersionCheck: true, defaultTocDepth: 3 } ================================================ FILE: lib/config/defaultSSL.js ================================================ 'use strict' const fs = require('fs') function getFile (path) { if (fs.existsSync(path)) { return path } return undefined } module.exports = { sslKeyPath: getFile('/run/secrets/key.pem'), sslCertPath: getFile('/run/secrets/cert.pem'), sslCAPath: getFile('/run/secrets/ca.pem') !== undefined ? [getFile('/run/secrets/ca.pem')] : [], dhParamPath: getFile('/run/secrets/dhparam.pem') } ================================================ FILE: lib/config/dockerSecret.js ================================================ 'use strict' const fs = require('fs') const path = require('path') const basePath = path.resolve('/var/run/secrets/') function getSecret (secret) { const filePath = path.join(basePath, secret) if (fs.existsSync(filePath)) return fs.readFileSync(filePath) return undefined } if (fs.existsSync(basePath)) { module.exports = { dbURL: getSecret('dburl'), // ssl path sslKeyPath: getSecret('sslkeypath'), sslCertPath: getSecret('sslcertpath'), sslCAPath: getSecret('sslcapath'), dhParamPath: getSecret('dhparampath'), // session sessionSecret: getSecret('sessionsecret'), imgur: { clientID: getSecret('imgur_clientid') }, s3: { accessKeyId: getSecret('s3_acccessKeyId'), secretAccessKey: getSecret('s3_secretAccessKey') }, minio: { accessKey: getSecret('minio_accessKey'), secretKey: getSecret('minio_secretKey') }, azure: { connectionString: getSecret('azure_connectionString') }, oauth2: { clientID: getSecret('oauth2_clientID'), clientSecret: getSecret('oauth2_clientSecret') }, facebook: { clientID: getSecret('facebook_clientID'), clientSecret: getSecret('facebook_clientSecret') }, twitter: { consumerKey: getSecret('twitter_consumerKey'), consumerSecret: getSecret('twitter_consumerSecret') }, github: { clientID: getSecret('github_clientID'), clientSecret: getSecret('github_clientSecret') }, gitlab: { clientID: getSecret('gitlab_clientID'), clientSecret: getSecret('gitlab_clientSecret') }, mattermost: { clientID: getSecret('mattermost_clientID'), clientSecret: getSecret('mattermost_clientSecret') }, dropbox: { clientID: getSecret('dropbox_clientID'), clientSecret: getSecret('dropbox_clientSecret'), appKey: getSecret('dropbox_appKey') }, google: { clientID: getSecret('google_clientID'), clientSecret: getSecret('google_clientSecret') }, ldap: { bindCredentials: getSecret('ldap_bindCredentials'), tlsca: getSecret('ldap_tlsca') }, saml: { idpCert: getSecret('saml_idpCert') } } } ================================================ FILE: lib/config/enum.js ================================================ 'use strict' exports.Environment = { development: 'development', production: 'production', test: 'test' } exports.Permission = { freely: 'freely', editable: 'editable', limited: 'limited', locked: 'locked', protected: 'protected', private: 'private' } ================================================ FILE: lib/config/environment.js ================================================ 'use strict' const { toBooleanConfig, toArrayConfig, toIntegerConfig } = require('./utils') module.exports = { sourceURL: process.env.CMD_SOURCE_URL, domain: process.env.CMD_DOMAIN, urlPath: process.env.CMD_URL_PATH, host: process.env.CMD_HOST, port: toIntegerConfig(process.env.CMD_PORT), path: process.env.CMD_PATH, loglevel: process.env.CMD_LOGLEVEL, urlAddPort: toBooleanConfig(process.env.CMD_URL_ADDPORT), useSSL: toBooleanConfig(process.env.CMD_USESSL), hsts: { enable: toBooleanConfig(process.env.CMD_HSTS_ENABLE), maxAgeSeconds: toIntegerConfig(process.env.CMD_HSTS_MAX_AGE), includeSubdomains: toBooleanConfig(process.env.CMD_HSTS_INCLUDE_SUBDOMAINS), preload: toBooleanConfig(process.env.CMD_HSTS_PRELOAD) }, csp: { enable: toBooleanConfig(process.env.CMD_CSP_ENABLE), reportURI: process.env.CMD_CSP_REPORTURI }, protocolUseSSL: toBooleanConfig(process.env.CMD_PROTOCOL_USESSL), allowOrigin: toArrayConfig(process.env.CMD_ALLOW_ORIGIN), useCDN: toBooleanConfig(process.env.CMD_USECDN), allowAnonymous: toBooleanConfig(process.env.CMD_ALLOW_ANONYMOUS), allowAnonymousEdits: toBooleanConfig(process.env.CMD_ALLOW_ANONYMOUS_EDITS), allowAnonymousViews: toBooleanConfig(process.env.CMD_ALLOW_ANONYMOUS_VIEWS), allowFreeURL: toBooleanConfig(process.env.CMD_ALLOW_FREEURL), forbiddenNoteIDs: toArrayConfig(process.env.CMD_FORBIDDEN_NOTE_IDS), defaultPermission: process.env.CMD_DEFAULT_PERMISSION, dbURL: process.env.CMD_DB_URL, sessionSecret: process.env.CMD_SESSION_SECRET, sessionLife: toIntegerConfig(process.env.CMD_SESSION_LIFE), responseMaxLag: toIntegerConfig(process.env.CMD_RESPONSE_MAX_LAG), privacyPolicyURL: process.env.CMD_PRIVACY_POLICY_URL, imageUploadType: process.env.CMD_IMAGE_UPLOAD_TYPE, imgur: { clientID: process.env.CMD_IMGUR_CLIENTID }, s3: { accessKeyId: process.env.CMD_S3_ACCESS_KEY_ID, secretAccessKey: process.env.CMD_S3_SECRET_ACCESS_KEY, region: process.env.CMD_S3_REGION, endpoint: process.env.CMD_S3_ENDPOINT, baseURL: process.env.CMD_S3_BASEURL }, minio: { accessKey: process.env.CMD_MINIO_ACCESS_KEY, secretKey: process.env.CMD_MINIO_SECRET_KEY, endPoint: process.env.CMD_MINIO_ENDPOINT, secure: toBooleanConfig(process.env.CMD_MINIO_SECURE), port: toIntegerConfig(process.env.CMD_MINIO_PORT) }, s3bucket: process.env.CMD_S3_BUCKET, azure: { connectionString: process.env.CMD_AZURE_CONNECTION_STRING, container: process.env.CMD_AZURE_CONTAINER }, facebook: { clientID: process.env.CMD_FACEBOOK_CLIENTID, clientSecret: process.env.CMD_FACEBOOK_CLIENTSECRET }, twitter: { consumerKey: process.env.CMD_TWITTER_CONSUMERKEY, consumerSecret: process.env.CMD_TWITTER_CONSUMERSECRET }, github: { enterpriseURL: process.env.CMD_GITHUB_ENTERPRISE_URL, clientID: process.env.CMD_GITHUB_CLIENTID, clientSecret: process.env.CMD_GITHUB_CLIENTSECRET, organizations: toArrayConfig(process.env.CMD_GITHUB_ORGANIZATIONS), scopes: toArrayConfig(process.env.CMD_GITHUB_SCOPES) }, bitbucket: { clientID: process.env.CMD_BITBUCKET_CLIENTID, clientSecret: process.env.CMD_BITBUCKET_CLIENTSECRET }, gitlab: { baseURL: process.env.CMD_GITLAB_BASEURL, clientID: process.env.CMD_GITLAB_CLIENTID, clientSecret: process.env.CMD_GITLAB_CLIENTSECRET, scope: process.env.CMD_GITLAB_SCOPE }, mattermost: { baseURL: process.env.CMD_MATTERMOST_BASEURL, clientID: process.env.CMD_MATTERMOST_CLIENTID, clientSecret: process.env.CMD_MATTERMOST_CLIENTSECRET }, oauth2: { providerName: process.env.CMD_OAUTH2_PROVIDERNAME, baseURL: process.env.CMD_OAUTH2_BASEURL, clientID: process.env.CMD_OAUTH2_CLIENT_ID, clientSecret: process.env.CMD_OAUTH2_CLIENT_SECRET, authorizationURL: process.env.CMD_OAUTH2_AUTHORIZATION_URL, tokenURL: process.env.CMD_OAUTH2_TOKEN_URL, userProfileURL: process.env.CMD_OAUTH2_USER_PROFILE_URL, scope: process.env.CMD_OAUTH2_SCOPE, state: process.env.CMD_OAUTH2_STATE, rolesClaim: process.env.CMD_OAUTH2_ROLES_CLAIM, accessRole: process.env.CMD_OAUTH2_ACCESS_ROLE, userProfileIdAttr: process.env.CMD_OAUTH2_USER_PROFILE_ID_ATTR, userProfileUsernameAttr: process.env.CMD_OAUTH2_USER_PROFILE_USERNAME_ATTR, userProfileDisplayNameAttr: process.env.CMD_OAUTH2_USER_PROFILE_DISPLAY_NAME_ATTR, userProfileEmailAttr: process.env.CMD_OAUTH2_USER_PROFILE_EMAIL_ATTR, userProfilePhotoAttr: process.env.CMD_OAUTH2_USER_PROFILE_PHOTO_ATTR }, dropbox: { clientID: process.env.CMD_DROPBOX_CLIENTID, clientSecret: process.env.CMD_DROPBOX_CLIENTSECRET, appKey: process.env.CMD_DROPBOX_APPKEY }, google: { clientID: process.env.CMD_GOOGLE_CLIENTID, clientSecret: process.env.CMD_GOOGLE_CLIENTSECRET, hostedDomain: process.env.CMD_GOOGLE_HOSTEDDOMAIN }, ldap: { providerName: process.env.CMD_LDAP_PROVIDERNAME, url: process.env.CMD_LDAP_URL, bindDn: process.env.CMD_LDAP_BINDDN, bindCredentials: process.env.CMD_LDAP_BINDCREDENTIALS, searchBase: process.env.CMD_LDAP_SEARCHBASE, searchFilter: process.env.CMD_LDAP_SEARCHFILTER, searchAttributes: toArrayConfig(process.env.CMD_LDAP_SEARCHATTRIBUTES), usernameField: process.env.CMD_LDAP_USERNAMEFIELD, useridField: process.env.CMD_LDAP_USERIDFIELD, tlsca: process.env.CMD_LDAP_TLS_CA }, saml: { idpSsoUrl: process.env.CMD_SAML_IDPSSOURL, idpCert: process.env.CMD_SAML_IDPCERT, issuer: process.env.CMD_SAML_ISSUER, identifierFormat: process.env.CMD_SAML_IDENTIFIERFORMAT, disableRequestedAuthnContext: toBooleanConfig(process.env.CMD_SAML_DISABLEREQUESTEDAUTHNCONTEXT), groupAttribute: process.env.CMD_SAML_GROUPATTRIBUTE, externalGroups: toArrayConfig(process.env.CMD_SAML_EXTERNALGROUPS, '|', []), requiredGroups: toArrayConfig(process.env.CMD_SAML_REQUIREDGROUPS, '|', []), attribute: { id: process.env.CMD_SAML_ATTRIBUTE_ID, username: process.env.CMD_SAML_ATTRIBUTE_USERNAME, email: process.env.CMD_SAML_ATTRIBUTE_EMAIL } }, plantuml: { server: process.env.CMD_PLANTUML_SERVER }, email: toBooleanConfig(process.env.CMD_EMAIL), allowEmailRegister: toBooleanConfig(process.env.CMD_ALLOW_EMAIL_REGISTER), allowGravatar: toBooleanConfig(process.env.CMD_ALLOW_GRAVATAR), allowPDFExport: toBooleanConfig(process.env.CMD_ALLOW_PDF_EXPORT), openID: toBooleanConfig(process.env.CMD_OPENID), defaultUseHardbreak: toBooleanConfig(process.env.CMD_DEFAULT_USE_HARD_BREAK), linkifyHeaderStyle: process.env.CMD_LINKIFY_HEADER_STYLE, autoVersionCheck: toBooleanConfig(process.env.CMD_AUTO_VERSION_CHECK), defaultTocDepth: toIntegerConfig(process.env.CMD_DEFAULT_TOC_DEPTH) } ================================================ FILE: lib/config/index.js ================================================ 'use strict' const crypto = require('crypto') const fs = require('fs') const path = require('path') const { merge } = require('lodash') const deepFreeze = require('deep-freeze') const { Environment, Permission } = require('./enum') const logger = require('../logger') const { getGitCommit, getGitHubURL } = require('./utils') const appRootPath = path.resolve(__dirname, '../../') const env = process.env.NODE_ENV || Environment.development const debugConfig = { debug: (env === Environment.development) } // Get version string from package.json const { version, repository } = require(path.join(appRootPath, 'package.json')) const commitID = getGitCommit(appRootPath) const sourceURL = getGitHubURL(repository.url, commitID || version) const fullversion = commitID ? `${version}-${commitID}` : version const packageConfig = { version: version, minimumCompatibleVersion: '0.5.0', fullversion: fullversion, sourceURL: sourceURL } const configFilePath = path.resolve(appRootPath, process.env.CMD_CONFIG_FILE || 'config.json') const fileConfig = fs.existsSync(configFilePath) ? require(configFilePath)[env] : undefined let config = require('./default') merge(config, require('./defaultSSL')) merge(config, debugConfig) merge(config, packageConfig) merge(config, fileConfig) merge(config, require('./environment')) merge(config, require('./dockerSecret')) if (['debug', 'verbose', 'info', 'warn', 'error'].includes(config.loglevel)) { logger.level = config.loglevel } else { logger.error('Selected loglevel %s doesn\'t exist, using default level \'debug\'. Available options: debug, verbose, info, warn, error', config.loglevel) } // load LDAP CA if (config.ldap.tlsca) { const certificateAuthorities = config.ldap.tlsca.split(',') const caContent = [] for (const ca of certificateAuthorities) { if (fs.existsSync(ca)) { caContent.push(fs.readFileSync(ca, 'utf8')) } } const tlsOptions = { ca: caContent } config.ldap.tlsOptions = config.ldap.tlsOptions ? Object.assign(config.ldap.tlsOptions, tlsOptions) : tlsOptions } // Permission config.permission = Permission let defaultPermission = config.permission.editable if (!config.allowAnonymous && !config.allowAnonymousViews) { delete config.permission.freely delete config.permission.editable delete config.permission.locked defaultPermission = config.permission.limited } else if (!config.allowAnonymous && !config.allowAnonymousEdits) { delete config.permission.freely } if (!(config.defaultPermission in config.permission)) { config.defaultPermission = defaultPermission } // cache result, cannot change config in runtime!!! config.isStandardHTTPsPort = (function isStandardHTTPsPort () { return config.useSSL && config.port === 443 })() config.isStandardHTTPPort = (function isStandardHTTPPort () { return !config.useSSL && config.port === 80 })() // cache serverURL config.serverURL = (function getserverurl () { var url = '' if (config.domain) { var protocol = config.protocolUseSSL ? 'https://' : 'http://' url = protocol + config.domain if (config.urlAddPort) { if (!config.isStandardHTTPPort || !config.isStandardHTTPsPort) { url += ':' + config.port } } } if (config.urlPath) { url += '/' + config.urlPath } return url })() if (config.serverURL === '') { logger.warn('Neither \'domain\' nor \'CMD_DOMAIN\' is configured. This can cause issues with various components.\nHint: Make sure \'protocolUseSSL\' and \'urlAddPort\' or \'CMD_PROTOCOL_USESSL\' and \'CMD_URL_ADDPORT\' are configured properly.') } config.Environment = Environment // auth method config.isFacebookEnable = config.facebook.clientID && config.facebook.clientSecret config.isGoogleEnable = config.google.clientID && config.google.clientSecret config.isDropboxEnable = config.dropbox.clientID && config.dropbox.clientSecret config.isTwitterEnable = config.twitter.consumerKey && config.twitter.consumerSecret config.isEmailEnable = config.email config.isOpenIDEnable = config.openID config.isGitHubEnable = config.github.clientID && config.github.clientSecret config.isBitbucketEnable = config.bitbucket.clientID && config.bitbucket.clientSecret config.isGitLabEnable = config.gitlab.clientID && config.gitlab.clientSecret config.isMattermostEnable = config.mattermost.clientID && config.mattermost.clientSecret config.isLDAPEnable = config.ldap.url config.isSAMLEnable = config.saml.idpSsoUrl config.isOAuth2Enable = config.oauth2.clientID && config.oauth2.clientSecret config.isPDFExportEnable = config.allowPDFExport // Check gitlab api version if (config.gitlab && config.gitlab.version !== 'v4' && config.gitlab.version !== 'v3') { logger.warn('config.js contains wrong version (' + config.gitlab.version + ') for gitlab api; it should be \'v3\' or \'v4\'. Defaulting to v4') config.gitlab.version = 'v4' } // If gitlab scope is api, enable snippets Export/import config.isGitlabSnippetsEnable = (!config.gitlab.scope || config.gitlab.scope === 'api') && config.isGitLabEnable // Only update i18n files in development setups config.updateI18nFiles = (env === Environment.development) // merge legacy values const keys = Object.keys(config) const uppercase = /[A-Z]/ for (let i = keys.length; i--;) { const lowercaseKey = keys[i].toLowerCase() // if the config contains uppercase letters // and a lowercase version of this setting exists // and the config with uppercase is not set // we set the new config using the old key. if (uppercase.test(keys[i]) && config[lowercaseKey] !== undefined && fileConfig[keys[i]] === undefined) { logger.warn('config.js contains deprecated lowercase setting for ' + keys[i] + '. Please change your config.js file to replace ' + lowercaseKey + ' with ' + keys[i]) config[keys[i]] = config[lowercaseKey] } } // Notify users about the prefix change and inform them they use legacy prefix for environment variables if (Object.keys(process.env).toString().indexOf('HMD_') !== -1) { logger.warn('Using legacy HMD prefix for environment variables. Please change your variables in future. For details see: https://hackmd.io/c/codimd-documentation/%2F%40codimd%2Fmigrate-2-0#1-Drop-old-environment-variables-support') } // Generate session secret if it stays on default values if (config.sessionSecret === 'secret') { logger.warn('Session secret not set. Using random generated one. Please set `sessionSecret` in your config.js file. All users will be logged out.') config.sessionSecret = crypto.randomBytes(Math.ceil(config.sessionSecretLen / 2)) // generate crypto graphic random number .toString('hex') // convert to hexadecimal format .slice(0, config.sessionSecretLen) // return required number of characters } // Validate upload upload providers if (['filesystem', 's3', 'minio', 'imgur', 'azure', 'lutim'].indexOf(config.imageUploadType) === -1) { logger.error('"imageuploadtype" is not correctly set. Please use "filesystem", "s3", "minio", "azure", "lutim" or "imgur". Defaulting to "filesystem"') config.imageUploadType = 'filesystem' } // figure out mime types for image uploads switch (config.imageUploadType) { case 'imgur': config.allowedUploadMimeTypes = [ 'image/jpeg', 'image/png', 'image/jpg', 'image/gif' ] break default: config.allowedUploadMimeTypes = [ 'image/jpeg', 'image/png', 'image/jpg', 'image/gif', 'image/svg+xml', 'image/bmp', 'image/tiff' ] } // generate correct path config.sslCAPath.forEach(function (capath, i, array) { array[i] = path.resolve(appRootPath, capath) }) config.appRootPath = appRootPath config.sslCertPath = path.resolve(appRootPath, config.sslCertPath) config.sslKeyPath = path.resolve(appRootPath, config.sslKeyPath) config.dhParamPath = path.resolve(appRootPath, config.dhParamPath) config.viewPath = path.resolve(appRootPath, config.viewPath) config.tmpPath = path.resolve(appRootPath, config.tmpPath) config.defaultNotePath = path.resolve(appRootPath, config.defaultNotePath) config.docsPath = path.resolve(appRootPath, config.docsPath) config.uploadsPath = path.resolve(appRootPath, config.uploadsPath) // make config readonly config = deepFreeze(config) module.exports = config ================================================ FILE: lib/config/utils.js ================================================ 'use strict' const fs = require('fs') const path = require('path') exports.toBooleanConfig = function toBooleanConfig (configValue) { if (configValue && typeof configValue === 'string') { return (configValue === 'true') } return configValue } exports.toArrayConfig = function toArrayConfig (configValue, separator = ',', fallback) { if (configValue && typeof configValue === 'string') { return (configValue.split(separator).map(arrayItem => arrayItem.trim())) } return fallback } exports.toIntegerConfig = function toIntegerConfig (configValue) { if (configValue && typeof configValue === 'string') { return parseInt(configValue) } return configValue } exports.getGitCommit = function getGitCommit (repodir) { if (!fs.existsSync(repodir + '/.git/HEAD')) { return undefined } let reference = fs.readFileSync(repodir + '/.git/HEAD', 'utf8') if (reference.startsWith('ref: ')) { reference = reference.substr(5).replace('\n', '') reference = fs.readFileSync(path.resolve(repodir + '/.git', reference), 'utf8') } reference = reference.replace('\n', '') return reference } exports.getGitHubURL = function getGitHubURL (repo, reference) { // if it's not a github reference, we handle handle that anyway if (!repo.startsWith('https://github.com') && !repo.startsWith('git@github.com')) { return repo } if (repo.startsWith('git@github.com') || repo.startsWith('ssh://git@github.com')) { repo = repo.replace(/^(ssh:\/\/)?git@github.com:/, 'https://github.com/') } if (repo.endsWith('.git')) { repo = repo.replace(/\.git$/, '/') } else if (!repo.endsWith('/')) { repo = repo + '/' } return repo + 'tree/' + reference } ================================================ FILE: lib/csp.js ================================================ var config = require('./config') var uuid = require('uuid') var CspStrategy = {} var defaultDirectives = { defaultSrc: ['\'self\''], scriptSrc: ['\'self\'', 'vimeo.com', 'https://gist.github.com', 'www.slideshare.net', 'https://query.yahooapis.com', '\'unsafe-eval\''], // ^ TODO: Remove unsafe-eval - webpack script-loader issues https://github.com/hackmdio/codimd/issues/594 imgSrc: ['*', 'data:'], styleSrc: ['\'self\'', '\'unsafe-inline\'', 'https://github.githubassets.com'], // unsafe-inline is required for some libs, plus used in views fontSrc: ['\'self\'', 'data:', 'https://public.slidesharecdn.com'], objectSrc: ['*'], // Chrome PDF viewer treats PDFs as objects :/ mediaSrc: ['*'], childSrc: ['*'], connectSrc: ['*'] } var dropboxDirectives = { scriptSrc: ['https://www.dropbox.com'] } var cdnDirectives = { scriptSrc: ['https://cdnjs.cloudflare.com', 'https://cdn.jsdelivr.net', 'https://cdn.mathjax.org'], styleSrc: ['https://cdnjs.cloudflare.com', 'https://cdn.jsdelivr.net', 'https://fonts.googleapis.com'], fontSrc: ['https://cdnjs.cloudflare.com', 'https://fonts.gstatic.com'] } var disqusDirectives = { scriptSrc: ['https://disqus.com', 'https://*.disqus.com', 'https://*.disquscdn.com'], styleSrc: ['https://*.disquscdn.com'], fontSrc: ['https://*.disquscdn.com'] } var googleAnalyticsDirectives = { scriptSrc: ['https://www.google-analytics.com'] } CspStrategy.computeDirectives = function () { var directives = {} mergeDirectives(directives, config.csp.directives) mergeDirectivesIf(config.csp.addDefaults, directives, defaultDirectives) mergeDirectivesIf(config.useCDN, directives, cdnDirectives) mergeDirectivesIf(config.dropbox && config.dropbox.appKey, directives, dropboxDirectives) mergeDirectivesIf(config.csp.addDisqus, directives, disqusDirectives) mergeDirectivesIf(config.csp.addGoogleAnalytics, directives, googleAnalyticsDirectives) if (!areAllInlineScriptsAllowed(directives)) { addInlineScriptExceptions(directives) } addUpgradeUnsafeRequestsOptionTo(directives) addReportURI(directives) return directives } function mergeDirectives (existingDirectives, newDirectives) { for (var propertyName in newDirectives) { var newDirective = newDirectives[propertyName] if (newDirective) { var existingDirective = existingDirectives[propertyName] || [] existingDirectives[propertyName] = existingDirective.concat(newDirective) } } } function mergeDirectivesIf (condition, existingDirectives, newDirectives) { if (condition) { mergeDirectives(existingDirectives, newDirectives) } } function areAllInlineScriptsAllowed (directives) { return directives.scriptSrc.indexOf('\'unsafe-inline\'') !== -1 } function addInlineScriptExceptions (directives) { directives.scriptSrc.push(getCspNonce) // TODO: This is the SHA-256 hash of the inline script in build/reveal.js/plugins/notes/notes.html // Any more clean solution appreciated. directives.scriptSrc.push('\'sha256-81acLZNZISnyGYZrSuoYhpzwDTTxi7vC1YM4uNxqWaM=\'') } function getCspNonce (req, res) { return "'nonce-" + res.locals.nonce + "'" } function addUpgradeUnsafeRequestsOptionTo (directives) { if (config.csp.upgradeInsecureRequests === 'auto' && config.useSSL) { directives.upgradeInsecureRequests = true } else if (config.csp.upgradeInsecureRequests === true) { directives.upgradeInsecureRequests = true } } function addReportURI (directives) { if (config.csp.reportURI) { directives.reportUri = config.csp.reportURI } } CspStrategy.addNonceToLocals = function (req, res, next) { res.locals.nonce = uuid.v4() next() } module.exports = CspStrategy ================================================ FILE: lib/errorPage/index.js ================================================ 'use strict' const config = require('../config') const { responseError } = require('../response') exports.errorForbidden = (req, res) => { if (req.user) { return responseError(res, '403', 'Forbidden', 'oh no.') } req.flash('error', 'You are not allowed to access this page. Maybe try logging in?') res.redirect(config.serverURL + '/') } exports.errorNotFound = (req, res) => { responseError(res, '404', 'Not Found', 'oops.') } exports.errorInternalError = (req, res) => { responseError(res, '500', 'Internal Error', 'wtf.') } ================================================ FILE: lib/history/index.js ================================================ 'use strict' // history // external modules var LZString = require('@hackmd/lz-string') // core var config = require('../config') var logger = require('../logger') var response = require('../response') var models = require('../models') function getHistory (userid, callback) { models.User.findOne({ where: { id: userid } }).then(function (user) { if (!user) { return callback(null, null) } var history = {} if (user.history) { history = JSON.parse(user.history) // migrate LZString encoded note id to base64url encoded note id for (let i = 0, l = history.length; i < l; i++) { // Calculate minimal string length for an UUID that is encoded // base64 encoded and optimize comparsion by using -1 // this should make a lot of LZ-String parsing errors obsolete // as we can assume that a nodeId that is 48 chars or longer is a // noteID. const base64UuidLength = ((4 * 36) / 3) - 1 if (!(history[i].id.length > base64UuidLength)) { continue } try { const id = LZString.decompressFromBase64(history[i].id) if (id && models.Note.checkNoteIdValid(id)) { history[i].id = models.Note.encodeNoteId(id) } } catch (err) { // most error here comes from LZString, ignore if (err.message === 'Cannot read property \'charAt\' of undefined') { logger.warning('Looks like we can not decode "' + history[i].id + '" with LZString. Can be ignored.') } else { logger.error(err) } } } history = parseHistoryToObject(history) } if (config.debug) { logger.info('read history success: ' + user.id) } return callback(null, history) }).catch(function (err) { logger.error('read history failed: ' + err) return callback(err, null) }) } function setHistory (userid, history, callback) { models.User.update({ history: JSON.stringify(parseHistoryToArray(history)) }, { where: { id: userid } }).then(function (count) { return callback(null, count) }).catch(function (err) { logger.error('set history failed: ' + err) return callback(err, null) }) } function updateHistory (userid, noteId, document, time) { if (userid && noteId && typeof document !== 'undefined') { getHistory(userid, function (err, history) { if (err || !history) return if (!history[noteId]) { history[noteId] = {} } var noteHistory = history[noteId] var noteInfo = models.Note.parseNoteInfo(document) noteHistory.id = noteId noteHistory.text = noteInfo.title noteHistory.time = time || Date.now() noteHistory.tags = noteInfo.tags setHistory(userid, history, function (err, count) { if (err) { logger.log(err) } }) }) } } function parseHistoryToArray (history) { var _history = [] Object.keys(history).forEach(function (key) { var item = history[key] _history.push(item) }) return _history } function parseHistoryToObject (history) { var _history = {} for (var i = 0, l = history.length; i < l; i++) { var item = history[i] _history[item.id] = item } return _history } function historyGet (req, res) { if (req.isAuthenticated()) { getHistory(req.user.id, function (err, history) { if (err) return response.errorInternalError(req, res) if (!history) return response.errorNotFound(req, res) res.send({ history: parseHistoryToArray(history) }) }) } else { return response.errorForbidden(req, res) } } function historyPost (req, res) { if (req.isAuthenticated()) { var noteId = req.params.noteId if (!noteId) { if (typeof req.body.history === 'undefined') return response.errorBadRequest(req, res) if (config.debug) { logger.info('SERVER received history from [' + req.user.id + ']: ' + req.body.history) } try { var history = JSON.parse(req.body.history) } catch (err) { return response.errorBadRequest(req, res) } if (Array.isArray(history)) { setHistory(req.user.id, history, function (err, count) { if (err) return response.errorInternalError(req, res) res.end() }) } else { return response.errorBadRequest(req, res) } } else { if (typeof req.body.pinned === 'undefined') return response.errorBadRequest(req, res) getHistory(req.user.id, function (err, history) { if (err) return response.errorInternalError(req, res) if (!history) return response.errorNotFound(req, res) if (!history[noteId]) return response.errorNotFound(req, res) if (req.body.pinned === 'true' || req.body.pinned === 'false') { history[noteId].pinned = (req.body.pinned === 'true') setHistory(req.user.id, history, function (err, count) { if (err) return response.errorInternalError(req, res) res.end() }) } else { return response.errorBadRequest(req, res) } }) } } else { return response.errorForbidden(req, res) } } function historyDelete (req, res) { if (req.isAuthenticated()) { var noteId = req.params.noteId if (!noteId) { setHistory(req.user.id, [], function (err, count) { if (err) return response.errorInternalError(req, res) res.end() }) } else { getHistory(req.user.id, function (err, history) { if (err) return response.errorInternalError(req, res) if (!history) return response.errorNotFound(req, res) delete history[noteId] setHistory(req.user.id, history, function (err, count) { if (err) return response.errorInternalError(req, res) res.end() }) }) } } else { return response.errorForbidden(req, res) } } // public exports.historyGet = historyGet exports.historyPost = historyPost exports.historyDelete = historyDelete exports.updateHistory = updateHistory ================================================ FILE: lib/homepage/index.js ================================================ 'use strict' const fs = require('fs') const path = require('path') const config = require('../config') const { User } = require('../models') const logger = require('../logger') exports.showIndex = async (req, res) => { const isLogin = req.isAuthenticated() const deleteToken = '' const data = { signin: isLogin, infoMessage: req.flash('info'), errorMessage: req.flash('error'), privacyStatement: fs.existsSync(path.join(config.docsPath, 'privacy.md')), termsOfUse: fs.existsSync(path.join(config.docsPath, 'terms-of-use.md')), deleteToken: deleteToken, csrfToken: req.csrfToken() } if (!isLogin) { return res.render('index.ejs', data) } const user = await User.findOne({ where: { id: req.user.id } }) if (user) { data.deleteToken = user.deleteToken return res.render('index.ejs', data) } logger.error(`error: user not found with id ${req.user.id}`) return res.render('index.ejs', data) } ================================================ FILE: lib/imageRouter/azure.js ================================================ 'use strict' const path = require('path') const config = require('../config') const logger = require('../logger') const azure = require('azure-storage') exports.uploadImage = function (imagePath, callback) { if (!imagePath || typeof imagePath !== 'string') { callback(new Error('Image path is missing or wrong'), null) return } if (!callback || typeof callback !== 'function') { logger.error('Callback has to be a function') return } var azureBlobService = azure.createBlobService(config.azure.connectionString) azureBlobService.createContainerIfNotExists(config.azure.container, { publicAccessLevel: 'blob' }, function (err, result, response) { if (err) { callback(new Error(err.message), null) } else { azureBlobService.createBlockBlobFromLocalFile(config.azure.container, path.basename(imagePath), imagePath, function (err, result, response) { if (err) { callback(new Error(err.message), null) } else { callback(null, azureBlobService.getUrl(config.azure.container, result.name)) } }) } }) } ================================================ FILE: lib/imageRouter/filesystem.js ================================================ 'use strict' const crypto = require('crypto') const fs = require('fs') const URL = require('url').URL const path = require('path') const config = require('../config') const logger = require('../logger') /** * generate a random filename for uploaded image */ function randomFilename () { const buf = crypto.randomBytes(16) return `upload_${buf.toString('hex')}` } /** * pick a filename not exist in filesystem * maximum attempt 5 times */ function pickFilename (defaultFilename) { let retryCounter = 5 const extname = path.extname(defaultFilename) let filename = `${randomFilename()}${extname}` while (retryCounter-- > 0) { if (fs.existsSync(path.join(config.uploadsPath, filename))) { filename = `${randomFilename()}${extname}` continue } return filename } throw new Error('file exists.') } exports.uploadImage = function (imagePath, callback) { if (!imagePath || typeof imagePath !== 'string') { callback(new Error('Image path is missing or wrong'), null) return } if (!callback || typeof callback !== 'function') { logger.error('Callback has to be a function') return } let filename = path.basename(imagePath) try { filename = pickFilename(path.basename(imagePath)) } catch (e) { return callback(e, null) } try { fs.copyFileSync(imagePath, path.join(config.uploadsPath, filename)) } catch (e) { return callback(e, null) } let url try { url = (new URL(filename, config.serverURL + '/uploads/')).href } catch (e) { url = config.serverURL + '/uploads/' + filename } callback(null, url) } ================================================ FILE: lib/imageRouter/imgur.js ================================================ 'use strict' const config = require('../config') const logger = require('../logger') const imgur = require('@hackmd/imgur') exports.uploadImage = function (imagePath, callback) { if (!imagePath || typeof imagePath !== 'string') { callback(new Error('Image path is missing or wrong'), null) return } if (!callback || typeof callback !== 'function') { logger.error('Callback has to be a function') return } imgur.setClientId(config.imgur.clientID) imgur.uploadFile(imagePath) .then(function (json) { if (config.debug) { logger.info('SERVER uploadimage success: ' + JSON.stringify(json)) } callback(null, json.data.link.replace(/^http:\/\//i, 'https://')) }).catch(function (err) { callback(new Error(err), null) }) } ================================================ FILE: lib/imageRouter/index.js ================================================ 'use strict' const fs = require('fs') const path = require('path') const Router = require('express').Router const formidable = require('formidable') const readChunk = require('read-chunk') const imageType = require('image-type') const mime = require('mime-types') const config = require('../config') const logger = require('../logger') const response = require('../response') const imageRouter = module.exports = Router() function checkImageValid (filepath) { try { const buffer = readChunk.sync(filepath, 0, 12) /** @type {{ ext: string, mime: string } | null} */ const mimetypeFromBuf = imageType(buffer) const mimeTypeFromExt = mime.lookup(path.extname(filepath)) return mimetypeFromBuf && config.allowedUploadMimeTypes.includes(mimetypeFromBuf.mime) && mimeTypeFromExt && config.allowedUploadMimeTypes.includes(mimeTypeFromExt) } catch (err) { logger.error(err) return false } } // upload image imageRouter.post('/uploadimage', function (req, res) { var form = new formidable.IncomingForm({ keepExtensions: true }) form.parse(req, function (err, fields, files) { if (err || !files.image || !files.image.filepath) { response.errorForbidden(req, res) } else { if (config.debug) { logger.info('SERVER received uploadimage: ' + JSON.stringify(files.image)) } if (!checkImageValid(files.image.filepath)) { return response.errorForbidden(req, res) } const uploadProvider = require('./' + config.imageUploadType) uploadProvider.uploadImage(files.image.filepath, function (err, url) { // remove temporary upload file, and ignore any error fs.unlink(files.image.filepath, () => {}) if (err !== null) { logger.error(err) return res.status(500).end('upload image error') } res.send({ link: url }) }) } }) }) ================================================ FILE: lib/imageRouter/lutim.js ================================================ 'use strict' const config = require('../config') const logger = require('../logger') const lutim = require('lutim') exports.uploadImage = function (imagePath, callback) { if (!imagePath || typeof imagePath !== 'string') { callback(new Error('Image path is missing or wrong'), null) return } if (!callback || typeof callback !== 'function') { logger.error('Callback has to be a function') return } if (config.lutim && config.lutim.url) { lutim.setAPIUrl(config.lutim.url) } lutim.uploadImage(imagePath) .then(function (json) { if (config.debug) { logger.info('SERVER uploadimage success: ' + JSON.stringify(json)) } callback(null, lutim.getAPIUrl() + json.msg.short) }).catch(function (err) { callback(new Error(err), null) }) } ================================================ FILE: lib/imageRouter/minio.js ================================================ 'use strict' const fs = require('fs') const path = require('path') const config = require('../config') const { getImageMimeType } = require('../utils') const logger = require('../logger') const Minio = require('minio') const minioClient = new Minio.Client({ endPoint: config.minio.endPoint, port: config.minio.port, useSSL: config.minio.secure, accessKey: config.minio.accessKey, secretKey: config.minio.secretKey }) exports.uploadImage = function (imagePath, callback) { if (!imagePath || typeof imagePath !== 'string') { callback(new Error('Image path is missing or wrong'), null) return } if (!callback || typeof callback !== 'function') { logger.error('Callback has to be a function') return } fs.readFile(imagePath, function (err, buffer) { if (err) { callback(new Error(err), null) return } const key = path.join('uploads', path.basename(imagePath)) const protocol = config.minio.secure ? 'https' : 'http' minioClient.putObject(config.s3bucket, key, buffer, buffer.size, getImageMimeType(imagePath), function (err, data) { if (err) { callback(new Error(err), null) return } const hidePort = [80, 443].includes(config.minio.port) const urlPort = hidePort ? '' : `:${config.minio.port}` callback(null, `${protocol}://${config.minio.endPoint}${urlPort}/${config.s3bucket}/${key}`) }) }) } ================================================ FILE: lib/imageRouter/s3.js ================================================ 'use strict' const fs = require('fs') const path = require('path') const config = require('../config') const { getImageMimeType } = require('../utils') const logger = require('../logger') const { S3Client } = require('@aws-sdk/client-s3-node/S3Client') const { PutObjectCommand } = require('@aws-sdk/client-s3-node/commands/PutObjectCommand') const credentials = { accessKeyId: config.s3.accessKeyId, secretAccessKey: config.s3.secretAccessKey } const s3 = new S3Client({ credentials, region: config.s3.region, endpoint: config.s3.endpoint }) exports.uploadImage = function (imagePath, callback) { if (!imagePath || typeof imagePath !== 'string') { callback(new Error('Image path is missing or wrong'), null) return } if (!callback || typeof callback !== 'function') { logger.error('Callback has to be a function') return } fs.readFile(imagePath, function (err, buffer) { if (err) { callback(new Error(err), null) return } const params = { Bucket: config.s3bucket, Key: path.join('uploads', path.basename(imagePath)), Body: buffer, ACL: 'public-read' } const mimeType = getImageMimeType(imagePath) if (mimeType) { params.ContentType = mimeType } const command = new PutObjectCommand(params) s3.send(command).then(data => { // default scheme settings to https let s3Endpoint = 'https://s3.amazonaws.com' if (config.s3.region && config.s3.region !== 'us-east-1') { s3Endpoint = `https://s3-${config.s3.region}.amazonaws.com` } // rewrite endpoint from config if (config.s3.endpoint) { s3Endpoint = config.s3.endpoint } if (config.s3.baseURL) { callback(null, `${config.s3.baseURL}/${params.Key}`) } else { callback(null, `${s3Endpoint}/${config.s3bucket}/${params.Key}`) } }).catch(err => { if (err) { callback(new Error(err), null) } }) }) } ================================================ FILE: lib/letter-avatars.js ================================================ 'use strict' // external modules const crypto = require('crypto') const randomcolor = require('randomcolor') const config = require('./config') // core exports.generateAvatar = function (name) { const color = randomcolor({ seed: name, luminosity: 'dark' }) const letter = name.substring(0, 1).toUpperCase() let svg = '' svg += '' svg += '' svg += '' svg += '' svg += '' + letter + '' svg += '' svg += '' svg += '' return svg } exports.generateAvatarURL = function (name, email = '', big = true) { let photo if (typeof email !== 'string') { email = '' + name + '@example.com' } name = encodeURIComponent(name) const hash = crypto.createHash('md5') hash.update(email.toLowerCase()) const hexDigest = hash.digest('hex') if (email !== '' && config.allowGravatar) { photo = 'https://www.gravatar.com/avatar/' + hexDigest if (big) { photo += '?s=400' } else { photo += '?s=96' } } else { photo = config.serverURL + '/user/' + (name || email.substring(0, email.lastIndexOf('@')) || hexDigest) + '/avatar.svg' } return photo } ================================================ FILE: lib/logger.js ================================================ 'use strict' const { createLogger, format, transports } = require('winston') const logger = createLogger({ level: 'debug', format: format.combine( format.uncolorize(), format.timestamp(), format.align(), format.splat(), format.printf(info => `${info.timestamp} ${info.level}: ${info.message}`) ), transports: [ new transports.Console({ handleExceptions: true }) ], exitOnError: false }) logger.stream = { write: function (message, encoding) { logger.info(message) } } module.exports = logger ================================================ FILE: lib/metrics.js ================================================ 'use strict' const { Router } = require('express') const { wrap } = require('./utils') // load controller const statusController = require('./status') const appRouter = Router() // register route appRouter.get('/status', wrap(statusController.getStatus)) appRouter.get('/metrics/codimd', wrap(statusController.getMetrics)) exports.router = appRouter ================================================ FILE: lib/middleware/checkURIValid.js ================================================ 'use strict' const logger = require('../logger') const response = require('../response') module.exports = function (req, res, next) { try { decodeURIComponent(req.path) } catch (err) { logger.error(err) return response.errorBadRequest(req, res) } next() } ================================================ FILE: lib/middleware/codiMDVersion.js ================================================ 'use strict' const config = require('../config') module.exports = function (req, res, next) { res.set({ 'CodiMD-Version': config.version }) return next() } ================================================ FILE: lib/middleware/redirectWithoutTrailingSlashes.js ================================================ 'use strict' const config = require('../config') module.exports = function (req, res, next) { if (req.method === 'GET' && req.path.substr(-1) === '/' && req.path.length > 1) { const queryString = req.url.slice(req.path.length) const urlPath = req.path.slice(0, -1) let serverURL = config.serverURL if (config.urlPath) { serverURL = serverURL.slice(0, -(config.urlPath.length + 1)) } res.redirect(301, serverURL + urlPath + queryString) } else { next() } } ================================================ FILE: lib/middleware/tooBusy.js ================================================ 'use strict' const toobusy = require('toobusy-js') const config = require('../config') const response = require('../response') toobusy.maxLag(config.responseMaxLag) module.exports = function (req, res, next) { if (toobusy()) { response.errorServiceUnavailable(req, res) } else { next() } } ================================================ FILE: lib/migrations/20150504155329-create-users.js ================================================ 'use strict' module.exports = { up: function (queryInterface, Sequelize) { return queryInterface.createTable('Users', { id: { type: Sequelize.UUID, primaryKey: true, defaultValue: Sequelize.UUIDV4 }, profileid: { type: Sequelize.STRING, unique: true }, profile: Sequelize.TEXT, history: Sequelize.TEXT, createdAt: Sequelize.DATE, updatedAt: Sequelize.DATE }) }, down: function (queryInterface, Sequelize) { return queryInterface.dropTable('Users') } } ================================================ FILE: lib/migrations/20150508114741-create-notes.js ================================================ 'use strict' module.exports = { up: function (queryInterface, Sequelize) { return queryInterface.createTable('Notes', { id: { type: Sequelize.UUID, primaryKey: true, defaultValue: Sequelize.UUIDV4 }, ownerId: Sequelize.UUID, content: Sequelize.TEXT, title: Sequelize.STRING, createdAt: Sequelize.DATE, updatedAt: Sequelize.DATE }) }, down: function (queryInterface, Sequelize) { return queryInterface.dropTable('Notes') } } ================================================ FILE: lib/migrations/20150515125813-create-temp.js ================================================ 'use strict' module.exports = { up: function (queryInterface, Sequelize) { return queryInterface.createTable('Temp', { id: { type: Sequelize.STRING, primaryKey: true }, date: Sequelize.TEXT, createdAt: Sequelize.DATE, updatedAt: Sequelize.DATE }) }, down: function (queryInterface, Sequelize) { return queryInterface.dropTable('Temp') } } ================================================ FILE: lib/migrations/20150702001020-update-to-0_3_1.js ================================================ 'use strict' module.exports = { up: function (queryInterface, Sequelize) { return queryInterface.addColumn('Notes', 'shortid', { type: Sequelize.STRING, defaultValue: '0000000000', allowNull: false }).then(function () { return queryInterface.addIndex('Notes', ['shortid'], { indicesType: 'UNIQUE' }) }).then(function () { return queryInterface.addColumn('Notes', 'permission', { type: Sequelize.STRING, defaultValue: 'private', allowNull: false }) }).then(function () { return queryInterface.addColumn('Notes', 'viewcount', { type: Sequelize.INTEGER, defaultValue: 0 }) }).catch(function (error) { if (error.message === 'SQLITE_ERROR: duplicate column name: shortid' || error.message === "ER_DUP_FIELDNAME: Duplicate column name 'shortid'" || error.message === 'column "shortid" of relation "Notes" already exists') { console.log('Migration has already run… ignoring.') } else { throw error } }) }, down: function (queryInterface, Sequelize) { return queryInterface.removeColumn('Notes', 'viewcount') .then(function () { return queryInterface.removeColumn('Notes', 'permission') }) .then(function () { return queryInterface.removeIndex('Notes', ['shortid']) }) .then(function () { return queryInterface.removeColumn('Notes', 'shortid') }) } } ================================================ FILE: lib/migrations/20150915153700-change-notes-title-to-text.js ================================================ 'use strict' const isSQLite = require('../utils').isSQLite module.exports = { up: function (queryInterface, Sequelize) { return queryInterface.changeColumn('Notes', 'title', { type: Sequelize.TEXT }).then(function () { if (isSQLite(queryInterface.sequelize)) { // manual added index will be removed in sqlite return queryInterface.addIndex('Notes', ['shortid']) } }) }, down: function (queryInterface, Sequelize) { return queryInterface.changeColumn('Notes', 'title', { type: Sequelize.STRING }).then(function () { if (isSQLite(queryInterface.sequelize)) { // manual added index will be removed in sqlite return queryInterface.addIndex('Notes', ['shortid']) } }) } } ================================================ FILE: lib/migrations/20160112220142-note-add-lastchange.js ================================================ 'use strict' module.exports = { up: function (queryInterface, Sequelize) { return queryInterface.addColumn('Notes', 'lastchangeuserId', { type: Sequelize.UUID }).then(function () { return queryInterface.addColumn('Notes', 'lastchangeAt', { type: Sequelize.DATE }) }).catch(function (error) { if (error.message === 'SQLITE_ERROR: duplicate column name: lastchangeuserId' || error.message === "ER_DUP_FIELDNAME: Duplicate column name 'lastchangeuserId'" || error.message === 'column "lastchangeuserId" of relation "Notes" already exists') { console.log('Migration has already run… ignoring.') } else { throw error } }) }, down: function (queryInterface, Sequelize) { return queryInterface.removeColumn('Notes', 'lastchangeAt') .then(function () { return queryInterface.removeColumn('Notes', 'lastchangeuserId') }) } } ================================================ FILE: lib/migrations/20160420180355-note-add-alias.js ================================================ 'use strict' module.exports = { up: function (queryInterface, Sequelize) { return queryInterface.addColumn('Notes', 'alias', { type: Sequelize.STRING }).then(function () { return queryInterface.addIndex('Notes', ['alias'], { indicesType: 'UNIQUE' }) }).catch(function (error) { if (error.message === 'SQLITE_ERROR: duplicate column name: alias' || error.message === "ER_DUP_FIELDNAME: Duplicate column name 'alias'" || error.message === 'column "alias" of relation "Notes" already exists') { console.log('Migration has already run… ignoring.') } else { throw error } }) }, down: function (queryInterface, Sequelize) { return queryInterface.removeColumn('Notes', 'alias').then(function () { return queryInterface.removeIndex('Notes', ['alias']) }) } } ================================================ FILE: lib/migrations/20160515114000-user-add-tokens.js ================================================ 'use strict' module.exports = { up: function (queryInterface, Sequelize) { return queryInterface.addColumn('Users', 'accessToken', Sequelize.STRING).then(function () { return queryInterface.addColumn('Users', 'refreshToken', Sequelize.STRING) }).catch(function (error) { if (error.message === 'SQLITE_ERROR: duplicate column name: accessToken' || error.message === "ER_DUP_FIELDNAME: Duplicate column name 'accessToken'" || error.message === 'column "accessToken" of relation "Users" already exists') { console.log('Migration has already run… ignoring.') } else { throw error } }) }, down: function (queryInterface, Sequelize) { return queryInterface.removeColumn('Users', 'accessToken').then(function () { return queryInterface.removeColumn('Users', 'refreshToken') }) } } ================================================ FILE: lib/migrations/20160607060246-support-revision.js ================================================ 'use strict' module.exports = { up: function (queryInterface, Sequelize) { return queryInterface.addColumn('Notes', 'savedAt', Sequelize.DATE).then(function () { return queryInterface.createTable('Revisions', { id: { type: Sequelize.UUID, primaryKey: true }, noteId: Sequelize.UUID, patch: Sequelize.TEXT, lastContent: Sequelize.TEXT, content: Sequelize.TEXT, length: Sequelize.INTEGER, createdAt: Sequelize.DATE, updatedAt: Sequelize.DATE }) }).catch(function (error) { if (error.message === 'SQLITE_ERROR: duplicate column name: savedAt' | error.message === "ER_DUP_FIELDNAME: Duplicate column name 'savedAt'" || error.message === 'column "savedAt" of relation "Notes" already exists') { console.log('Migration has already run… ignoring.') } else { throw error } }) }, down: function (queryInterface, Sequelize) { return queryInterface.dropTable('Revisions').then(function () { return queryInterface.removeColumn('Notes', 'savedAt') }) } } ================================================ FILE: lib/migrations/20160703062241-support-authorship.js ================================================ 'use strict' module.exports = { up: function (queryInterface, Sequelize) { return queryInterface.addColumn('Notes', 'authorship', Sequelize.TEXT).then(function () { return queryInterface.addColumn('Revisions', 'authorship', Sequelize.TEXT) }).then(function () { return queryInterface.createTable('Authors', { id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, color: Sequelize.STRING, noteId: Sequelize.UUID, userId: Sequelize.UUID, createdAt: Sequelize.DATE, updatedAt: Sequelize.DATE }) }).catch(function (error) { if (error.message === 'SQLITE_ERROR: duplicate column name: authorship' || error.message === "ER_DUP_FIELDNAME: Duplicate column name 'authorship'" || error.message === 'column "authorship" of relation "Notes" already exists') { console.log('Migration has already run… ignoring.') } else { throw error } }) }, down: function (queryInterface, Sequelize) { return queryInterface.dropTable('Authors').then(function () { return queryInterface.removeColumn('Revisions', 'authorship') }).then(function () { return queryInterface.removeColumn('Notes', 'authorship') }) } } ================================================ FILE: lib/migrations/20161009040430-support-delete-note.js ================================================ 'use strict' module.exports = { up: function (queryInterface, Sequelize) { return queryInterface.addColumn('Notes', 'deletedAt', Sequelize.DATE).catch(function (error) { if (error.message === 'SQLITE_ERROR: duplicate column name: deletedAt' || error.message === "ER_DUP_FIELDNAME: Duplicate column name 'deletedAt'" || error.message === 'column "deletedAt" of relation "Notes" already exists') { console.log('Migration has already run… ignoring.') } else { throw error } }) }, down: function (queryInterface, Sequelize) { return queryInterface.removeColumn('Notes', 'deletedAt') } } ================================================ FILE: lib/migrations/20161201050312-support-email-signin.js ================================================ 'use strict' module.exports = { up: function (queryInterface, Sequelize) { return queryInterface.addColumn('Users', 'email', Sequelize.TEXT).then(function () { return queryInterface.addColumn('Users', 'password', Sequelize.TEXT).catch(function (error) { if (error.message === "ER_DUP_FIELDNAME: Duplicate column name 'password'" || error.message === 'column "password" of relation "Users" already exists') { console.log('Migration has already run… ignoring.') } else { throw error } }) }).catch(function (error) { if (error.message === 'SQLITE_ERROR: duplicate column name: email' || error.message === "ER_DUP_FIELDNAME: Duplicate column name 'email'" || error.message === 'column "email" of relation "Users" already exists') { console.log('Migration has already run… ignoring.') } else { throw error } }) }, down: function (queryInterface, Sequelize) { return queryInterface.removeColumn('Users', 'email').then(function () { return queryInterface.removeColumn('Users', 'password') }) } } ================================================ FILE: lib/migrations/20171009121200-longtext-for-mysql.js ================================================ 'use strict' module.exports = { up: async function (queryInterface, Sequelize) { await queryInterface.changeColumn('Notes', 'content', { type: Sequelize.TEXT('long') }) await queryInterface.changeColumn('Revisions', 'patch', { type: Sequelize.TEXT('long') }) await queryInterface.changeColumn('Revisions', 'content', { type: Sequelize.TEXT('long') }) await queryInterface.changeColumn('Revisions', 'lastContent', { type: Sequelize.TEXT('long') }) }, down: async function (queryInterface, Sequelize) { await queryInterface.changeColumn('Notes', 'content', { type: Sequelize.TEXT }) await queryInterface.changeColumn('Revisions', 'patch', { type: Sequelize.TEXT }) await queryInterface.changeColumn('Revisions', 'content', { type: Sequelize.TEXT }) await queryInterface.changeColumn('Revisions', 'lastContent', { type: Sequelize.TEXT }) } } ================================================ FILE: lib/migrations/20180209120907-longtext-of-authorship.js ================================================ 'use strict' module.exports = { up: async function (queryInterface, Sequelize) { await queryInterface.changeColumn('Notes', 'authorship', { type: Sequelize.TEXT('long') }) await queryInterface.changeColumn('Revisions', 'authorship', { type: Sequelize.TEXT('long') }) }, down: async function (queryInterface, Sequelize) { await queryInterface.changeColumn('Notes', 'authorship', { type: Sequelize.TEXT }) await queryInterface.changeColumn('Revisions', 'authorship', { type: Sequelize.TEXT }) } } ================================================ FILE: lib/migrations/20180306150303-fix-enum.js ================================================ 'use strict' module.exports = { up: async function (queryInterface, Sequelize) { await queryInterface.changeColumn('Notes', 'permission', { type: Sequelize.ENUM('freely', 'editable', 'limited', 'locked', 'protected', 'private') }) }, down: async function (queryInterface, Sequelize) { await queryInterface.changeColumn('Notes', 'permission', { type: Sequelize.ENUM('freely', 'editable', 'locked', 'private') }) } } ================================================ FILE: lib/migrations/20180326103000-use-text-in-tokens.js ================================================ 'use strict' module.exports = { up: function (queryInterface, Sequelize) { return queryInterface.changeColumn('Users', 'accessToken', { type: Sequelize.TEXT }).then(function () { return queryInterface.changeColumn('Users', 'refreshToken', { type: Sequelize.TEXT }) }) }, down: function (queryInterface, Sequelize) { return queryInterface.changeColumn('Users', 'accessToken', { type: Sequelize.STRING }).then(function () { return queryInterface.changeColumn('Users', 'refreshToken', { type: Sequelize.STRING }) }) } } ================================================ FILE: lib/migrations/20180525153000-user-add-delete-token.js ================================================ 'use strict' module.exports = { up: function (queryInterface, Sequelize) { return queryInterface.addColumn('Users', 'deleteToken', { type: Sequelize.UUID, defaultValue: Sequelize.UUIDV4 }) }, down: function (queryInterface, Sequelize) { return queryInterface.removeColumn('Users', 'deleteToken') } } ================================================ FILE: lib/migrations/20200104215332-remove-temp-table.js ================================================ 'use strict' module.exports = { up: (queryInterface, Sequelize) => { return queryInterface.dropTable('Temp') /* Add altering commands here. Return a promise to correctly handle asynchronicity. Example: return queryInterface.createTable('users', { id: Sequelize.INTEGER }); */ }, down: (queryInterface, Sequelize) => { return queryInterface.createTable('Temp', { id: { type: Sequelize.STRING, primaryKey: true }, date: Sequelize.TEXT, createdAt: Sequelize.DATE, updatedAt: Sequelize.DATE }) } } ================================================ FILE: lib/migrations/20240114120250-revision-add-index.js ================================================ 'use strict' module.exports = { up: (queryInterface, Sequelize) => { return queryInterface.addIndex('Revisions', ['noteId'], {}) }, down: (queryInterface, Sequelize) => { return queryInterface.removeIndex('Revisions', 'noteId') } } ================================================ FILE: lib/models/author.js ================================================ 'use strict' // external modules var Sequelize = require('sequelize') module.exports = function (sequelize, DataTypes) { var Author = sequelize.define('Author', { id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, color: { type: DataTypes.STRING } }, { indexes: [ { unique: true, fields: ['noteId', 'userId'] } ] }) Author.associate = function (models) { Author.belongsTo(models.Note, { foreignKey: 'noteId', as: 'note', constraints: false, onDelete: 'CASCADE', hooks: true }) Author.belongsTo(models.User, { foreignKey: 'userId', as: 'user', constraints: false, onDelete: 'CASCADE', hooks: true }) } return Author } ================================================ FILE: lib/models/index.js ================================================ 'use strict' // external modules var fs = require('fs') var path = require('path') var Sequelize = require('sequelize') const { cloneDeep } = require('lodash') // core var config = require('../config') var logger = require('../logger') var dbconfig = cloneDeep(config.db) dbconfig.logging = config.debug ? (data) => { logger.info(data) } : false var sequelize = null // Heroku specific if (config.dbURL) { sequelize = new Sequelize(config.dbURL, dbconfig) } else { sequelize = new Sequelize(dbconfig.database, dbconfig.username, dbconfig.password, dbconfig) } // [Postgres] Handling NULL bytes // https://github.com/sequelize/sequelize/issues/6485 function stripNullByte (value) { value = '' + value // eslint-disable-next-line no-control-regex return value ? value.replace(/\u0000/g, '') : value } sequelize.stripNullByte = stripNullByte function processData (data, _default, process) { if (data === undefined) return data else return data === null ? _default : (process ? process(data) : data) } sequelize.processData = processData var db = {} fs.readdirSync(__dirname) .filter(function (file) { return (file.indexOf('.') !== 0) && (file !== 'index.js') }) .forEach(function (file) { var model = require(path.join(__dirname, file))(sequelize, Sequelize) db[model.name] = model }) Object.keys(db).forEach(function (modelName) { if ('associate' in db[modelName]) { db[modelName].associate(db) } }) db.sequelize = sequelize db.Sequelize = Sequelize module.exports = db ================================================ FILE: lib/models/note.js ================================================ 'use strict' // external modules var fs = require('fs') var path = require('path') var LZString = require('@hackmd/lz-string') var base64url = require('base64url') var md = require('markdown-it')() var metaMarked = require('@hackmd/meta-marked') var cheerio = require('cheerio') var shortId = require('shortid') var Sequelize = require('sequelize') var async = require('async') var moment = require('moment') var DiffMatchPatch = require('@hackmd/diff-match-patch') var dmp = new DiffMatchPatch() const { stripTags } = require('../../utils/string') // core var config = require('../config') var logger = require('../logger') // ot var ot = require('../ot') // permission types var permissionTypes = ['freely', 'editable', 'limited', 'locked', 'protected', 'private'] module.exports = function (sequelize, DataTypes) { var Note = sequelize.define('Note', { id: { type: DataTypes.UUID, primaryKey: true, defaultValue: Sequelize.UUIDV4 }, shortid: { type: DataTypes.STRING, unique: true, allowNull: false, defaultValue: shortId.generate }, alias: { type: DataTypes.STRING, unique: true }, permission: { type: DataTypes.ENUM, values: permissionTypes }, viewcount: { type: DataTypes.INTEGER, allowNull: false, defaultValue: 0 }, title: { type: DataTypes.TEXT, get: function () { return sequelize.processData(this.getDataValue('title'), '') }, set: function (value) { this.setDataValue('title', sequelize.stripNullByte(value)) } }, content: { type: DataTypes.TEXT('long'), get: function () { return sequelize.processData(this.getDataValue('content'), '') }, set: function (value) { this.setDataValue('content', sequelize.stripNullByte(value)) } }, authorship: { type: DataTypes.TEXT('long'), get: function () { return sequelize.processData(this.getDataValue('authorship'), [], JSON.parse) }, set: function (value) { this.setDataValue('authorship', JSON.stringify(value)) } }, lastchangeAt: { type: DataTypes.DATE }, savedAt: { type: DataTypes.DATE } }, { paranoid: false, hooks: { beforeCreate: function (note, options) { return new Promise(function (resolve, reject) { // if no content specified then use default note if (!note.content) { let filePath = config.defaultNotePath if (note.alias) { const notePathInDocPath = path.join(config.docsPath, path.basename(note.alias) + '.md') if (Note.checkFileExist(notePathInDocPath)) { filePath = notePathInDocPath } } if (Note.checkFileExist(filePath)) { const noteInFS = readFileSystemNote(filePath) note.title = noteInFS.title note.content = noteInFS.content if (filePath !== config.defaultNotePath) { note.createdAt = noteInFS.lastchangeAt note.lastchangeAt = noteInFS.lastchangeAt } } } // if no permission specified and have owner then give default permission in config, else default permission is freely if (!note.permission) { if (note.ownerId) { note.permission = config.defaultPermission } else { note.permission = 'freely' } } return resolve(note) }) }, afterCreate: function (note, options, callback) { return new Promise(function (resolve, reject) { sequelize.models.Revision.saveNoteRevision(note, function (err, revision) { if (err) { return reject(err) } return resolve(note) }) }) } } }) Note.associate = function (models) { Note.belongsTo(models.User, { foreignKey: 'ownerId', as: 'owner', constraints: false, onDelete: 'CASCADE', hooks: true }) Note.belongsTo(models.User, { foreignKey: 'lastchangeuserId', as: 'lastchangeuser', constraints: false }) Note.hasMany(models.Revision, { foreignKey: 'noteId', constraints: false }) Note.hasMany(models.Author, { foreignKey: 'noteId', as: 'authors', constraints: false }) } Note.checkFileExist = function (filePath) { try { return fs.statSync(filePath).isFile() } catch (err) { return false } } Note.encodeNoteId = function (id) { // remove dashes in UUID and encode in url-safe base64 const str = id.replace(/-/g, '') const hexStr = Buffer.from(str, 'hex') return base64url.encode(hexStr) } Note.decodeNoteId = function (encodedId) { // decode from url-safe base64 const id = base64url.toBuffer(encodedId).toString('hex') // add dashes between the UUID string parts const idParts = [] idParts.push(id.substr(0, 8)) idParts.push(id.substr(8, 4)) idParts.push(id.substr(12, 4)) idParts.push(id.substr(16, 4)) idParts.push(id.substr(20, 12)) return idParts.join('-') } Note.checkNoteIdValid = function (id) { var uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i var result = id.match(uuidRegex) if (result && result.length === 1) { return true } else { return false } } Note.parseNoteIdAsync = function (noteId) { return new Promise((resolve, reject) => { Note.parseNoteId(noteId, (err, id) => { if (err) { return reject(err) } resolve(id) }) }) } async function syncNote (noteInFS, note) { const contentLength = noteInFS.content.length let note2 = await note.update({ title: noteInFS.title, content: noteInFS.content, lastchangeAt: noteInFS.lastchangeAt }) const revision = await sequelize.models.Revision.saveNoteRevisionAsync(note2) // update authorship on after making revision of docs const patch = dmp.patch_fromText(revision.patch) const operations = Note.transformPatchToOperations(patch, contentLength) let authorship = note2.authorship for (let i = 0; i < operations.length; i++) { authorship = Note.updateAuthorshipByOperation(operations[i], null, authorship) } note2 = await note.update({ authorship: authorship }) return note2.id } Note.parseNoteId = function (noteId, callback) { async.series({ parseNoteIdByAlias: function (_callback) { // try to parse note id by alias (e.g. doc) Note.findOne({ where: { alias: noteId } }).then(async function (note) { const filePath = path.join(config.docsPath, path.basename(noteId) + '.md') if (Note.checkFileExist(filePath)) { try { if (note) { // if doc in filesystem have newer modified time than last change time // then will update the doc in db const noteInFS = readFileSystemNote(filePath) if (shouldSyncNote(note, noteInFS)) { const noteId = await syncNote(noteInFS, note) return callback(null, noteId) } } else { // create new note with alias, and will sync md file in beforeCreateHook const note = await Note.create({ alias: noteId, owner: null, permission: 'locked' }) return callback(null, note.id) } } catch (err) { return _callback(err, null) } } if (!note) { return _callback(null, null) } return callback(null, note.id) }).catch(function (err) { return _callback(err, null) }) }, // parse note id by LZString is deprecated, here for compability parseNoteIdByLZString: function (_callback) { // Calculate minimal string length for an UUID that is encoded // base64 encoded and optimize comparsion by using -1 // this should make a lot of LZ-String parsing errors obsolete // as we can assume that a nodeId that is 48 chars or longer is a // noteID. const base64UuidLength = ((4 * 36) / 3) - 1 if (!(noteId.length > base64UuidLength)) { return _callback(null, null) } // try to parse note id by LZString Base64 try { var id = LZString.decompressFromBase64(noteId) if (id && Note.checkNoteIdValid(id)) { return callback(null, id) } else { return _callback(null, null) } } catch (err) { if (err.message === 'Cannot read property \'charAt\' of undefined') { logger.warning('Looks like we can not decode "' + noteId + '" with LZString. Can be ignored.') } else { logger.error(err) } return _callback(null, null) } }, parseNoteIdByBase64Url: function (_callback) { // try to parse note id by base64url try { var id = Note.decodeNoteId(noteId) if (id && Note.checkNoteIdValid(id)) { return callback(null, id) } else { return _callback(null, null) } } catch (err) { logger.error(err) return _callback(null, null) } }, parseNoteIdByShortId: function (_callback) { // try to parse note id by shortId try { if (shortId.isValid(noteId)) { Note.findOne({ where: { shortid: noteId } }).then(function (note) { if (!note) return _callback(null, null) return callback(null, note.id) }).catch(function (err) { return _callback(err, null) }) } else { return _callback(null, null) } } catch (err) { return _callback(err, null) } } }, function (err, result) { if (err) { logger.error(err) return callback(err, null) } return callback(null, null) }) } Note.parseNoteInfo = function (body) { var parsed = Note.extractMeta(body) var $ = cheerio.load(md.render(parsed.markdown)) return { title: Note.extractNoteTitle(parsed.meta, $), tags: Note.extractNoteTags(parsed.meta, $) } } Note.parseNoteTitle = function (body) { var parsed = Note.extractMeta(body) var $ = cheerio.load(md.render(parsed.markdown)) return Note.extractNoteTitle(parsed.meta, $) } Note.extractNoteTitle = function (meta, $) { var title = '' if (meta.title && (typeof meta.title === 'string' || typeof meta.title === 'number')) { title = meta.title } else { var h1s = $('h1') if (h1s.length > 0 && h1s.first().text().split('\n').length === 1) { title = stripTags(h1s.first().text()) } } if (!title) title = 'Untitled' return title } Note.generateDescription = function (markdown) { return markdown.substr(0, 100).replace(/(?:\r\n|\r|\n)/g, ' ') } Note.decodeTitle = function (title) { return title || 'Untitled' } Note.generateWebTitle = function (title) { title = !title || title === 'Untitled' ? 'CodiMD - Collaborative markdown notes' : title + ' - CodiMD' return title } Note.extractNoteTags = function (meta, $) { var tags = [] var rawtags = [] var metaTags if (meta.tags && (typeof meta.tags === 'string' || typeof meta.tags === 'number')) { metaTags = ('' + meta.tags).split(',') } else if (meta.tags && (Array.isArray(meta.tags))) { metaTags = meta.tags } if (metaTags) { for (let i = 0; i < metaTags.length; i++) { var text = metaTags[i].trim() if (text) rawtags.push(text) } } else { var h6s = $('h6') h6s.each(function (key, value) { if (/^tags/gmi.test($(value).text())) { var codes = $(value).find('code') for (let i = 0; i < codes.length; i++) { var text = stripTags($(codes[i]).text().trim()) if (text) rawtags.push(text) } } }) } for (let i = 0; i < rawtags.length; i++) { var found = false for (let j = 0; j < tags.length; j++) { if (tags[j] === rawtags[i]) { found = true break } } if (!found) { tags.push(rawtags[i]) } } return tags } Note.extractMeta = function (content) { var obj = null try { obj = metaMarked(content) if (!obj.markdown) obj.markdown = '' if (!obj.meta) obj.meta = {} } catch (err) { obj = { markdown: content, meta: {} } } return obj } Note.parseMeta = function (meta) { var _meta = {} if (meta) { if (meta.title && (typeof meta.title === 'string' || typeof meta.title === 'number')) { _meta.title = meta.title } if (meta.description && (typeof meta.description === 'string' || typeof meta.description === 'number')) { _meta.description = meta.description } if (meta.robots && (typeof meta.robots === 'string' || typeof meta.robots === 'number')) { _meta.robots = meta.robots } if (meta.GA && (typeof meta.GA === 'string' || typeof meta.GA === 'number')) { _meta.GA = meta.GA } if (meta.disqus && (typeof meta.disqus === 'string' || typeof meta.disqus === 'number')) { _meta.disqus = meta.disqus } if (meta.slideOptions && (typeof meta.slideOptions === 'object')) { _meta.slideOptions = meta.slideOptions } } return _meta } Note.updateAuthorshipByOperation = function (operation, userId, authorships) { var index = 0 var timestamp = Date.now() for (let i = 0; i < operation.length; i++) { var op = operation[i] if (ot.TextOperation.isRetain(op)) { index += op } else if (ot.TextOperation.isInsert(op)) { const opStart = index const opEnd = index + op.length let inserted = false // authorship format: [userId, startPos, endPos, createdAt, updatedAt] if (authorships.length <= 0) authorships.push([userId, opStart, opEnd, timestamp, timestamp]) else { for (let j = 0; j < authorships.length; j++) { const authorship = authorships[j] if (!inserted) { const nextAuthorship = authorships[j + 1] || -1 if ((nextAuthorship !== -1 && nextAuthorship[1] >= opEnd) || j >= authorships.length - 1) { if (authorship[1] < opStart && authorship[2] > opStart) { // divide const postLength = authorship[2] - opStart authorship[2] = opStart authorship[4] = timestamp authorships.splice(j + 1, 0, [userId, opStart, opEnd, timestamp, timestamp]) authorships.splice(j + 2, 0, [authorship[0], opEnd, opEnd + postLength, authorship[3], timestamp]) j += 2 inserted = true } else if (authorship[1] >= opStart) { authorships.splice(j, 0, [userId, opStart, opEnd, timestamp, timestamp]) j += 1 inserted = true } else if (authorship[2] <= opStart) { authorships.splice(j + 1, 0, [userId, opStart, opEnd, timestamp, timestamp]) j += 1 inserted = true } } } if (authorship[1] >= opStart) { authorship[1] += op.length authorship[2] += op.length } } } index += op.length } else if (ot.TextOperation.isDelete(op)) { const opStart = index const opEnd = index - op if (operation.length === 1) { authorships = [] } else if (authorships.length > 0) { for (let j = 0; j < authorships.length; j++) { const authorship = authorships[j] if (authorship[1] >= opStart && authorship[1] <= opEnd && authorship[2] >= opStart && authorship[2] <= opEnd) { authorships.splice(j, 1) j -= 1 } else if (authorship[1] < opStart && authorship[1] < opEnd && authorship[2] > opStart && authorship[2] > opEnd) { authorship[2] += op authorship[4] = timestamp } else if (authorship[2] >= opStart && authorship[2] <= opEnd) { authorship[2] = opStart authorship[4] = timestamp } else if (authorship[1] >= opStart && authorship[1] <= opEnd) { authorship[1] = opEnd authorship[4] = timestamp } if (authorship[1] >= opEnd) { authorship[1] += op authorship[2] += op } } } index += op } } // merge for (let j = 0; j < authorships.length; j++) { const authorship = authorships[j] for (let k = j + 1; k < authorships.length; k++) { const nextAuthorship = authorships[k] if (nextAuthorship && authorship[0] === nextAuthorship[0] && authorship[2] === nextAuthorship[1]) { const minTimestamp = Math.min(authorship[3], nextAuthorship[3]) const maxTimestamp = Math.max(authorship[3], nextAuthorship[3]) authorships.splice(j, 1, [authorship[0], authorship[1], nextAuthorship[2], minTimestamp, maxTimestamp]) authorships.splice(k, 1) j -= 1 break } } } // clear for (let j = 0; j < authorships.length; j++) { const authorship = authorships[j] if (!authorship[0]) { authorships.splice(j, 1) j -= 1 } } return authorships } Note.transformPatchToOperations = function (patch, contentLength) { var operations = [] if (patch.length > 0) { // calculate original content length for (let j = patch.length - 1; j >= 0; j--) { var p = patch[j] for (let i = 0; i < p.diffs.length; i++) { var diff = p.diffs[i] switch (diff[0]) { case 1: // insert contentLength -= diff[1].length break case -1: // delete contentLength += diff[1].length break } } } // generate operations var bias = 0 var lengthBias = 0 for (let j = 0; j < patch.length; j++) { var operation = [] const p = patch[j] var currIndex = p.start1 var currLength = contentLength - bias for (let i = 0; i < p.diffs.length; i++) { const diff = p.diffs[i] switch (diff[0]) { case 0: // retain if (i === 0) { // first operation.push(currIndex + diff[1].length) } else if (i !== p.diffs.length - 1) { // mid operation.push(diff[1].length) } else { // last operation.push(currLength + lengthBias - currIndex) } currIndex += diff[1].length break case 1: // insert operation.push(diff[1]) lengthBias += diff[1].length currIndex += diff[1].length break case -1: // delete operation.push(-diff[1].length) bias += diff[1].length currIndex += diff[1].length break } } operations.push(operation) } } return operations } function readFileSystemNote (filePath) { const fsModifiedTime = moment(fs.statSync(filePath).mtime) const content = fs.readFileSync(filePath, 'utf8') return { lastchangeAt: fsModifiedTime, title: Note.parseNoteTitle(content), content: content } } function shouldSyncNote (note, noteInFS) { const dbModifiedTime = moment(note.lastchangeAt || note.createdAt) return noteInFS.lastchangeAt.isAfter(dbModifiedTime) && note.content !== noteInFS.content } return Note } ================================================ FILE: lib/models/revision.js ================================================ 'use strict' // external modules var Sequelize = require('sequelize') var async = require('async') var moment = require('moment') var childProcess = require('child_process') var shortId = require('shortid') var path = require('path') var util = require('util') var Op = Sequelize.Op // core var config = require('../config') var logger = require('../logger') var dmpWorker = createDmpWorker() var dmpCallbackCache = {} function createDmpWorker () { var worker = childProcess.fork(path.resolve(__dirname, '../workers/dmpWorker.js'), { stdio: 'ignore' }) if (config.debug) logger.info('dmp worker process started') worker.on('message', function (data) { if (!data || !data.msg || !data.cacheKey) { return logger.error('dmp worker error: not enough data on message') } var cacheKey = data.cacheKey switch (data.msg) { case 'error': dmpCallbackCache[cacheKey](data.error, null) break case 'check': dmpCallbackCache[cacheKey](null, data.result) break } delete dmpCallbackCache[cacheKey] }) worker.on('close', function (code) { dmpWorker = null if (config.debug) logger.info('dmp worker process exited with code ' + code) }) return worker } function sendDmpWorker (data, callback) { if (!dmpWorker) dmpWorker = createDmpWorker() var cacheKey = Date.now() + '_' + shortId.generate() dmpCallbackCache[cacheKey] = callback data = Object.assign(data, { cacheKey: cacheKey }) dmpWorker.send(data) } module.exports = function (sequelize, DataTypes) { var Revision = sequelize.define('Revision', { id: { type: DataTypes.UUID, primaryKey: true, defaultValue: Sequelize.UUIDV4 }, patch: { type: DataTypes.TEXT('long'), get: function () { return sequelize.processData(this.getDataValue('patch'), '') }, set: function (value) { this.setDataValue('patch', sequelize.stripNullByte(value)) } }, lastContent: { type: DataTypes.TEXT('long'), get: function () { return sequelize.processData(this.getDataValue('lastContent'), '') }, set: function (value) { this.setDataValue('lastContent', sequelize.stripNullByte(value)) } }, content: { type: DataTypes.TEXT('long'), get: function () { return sequelize.processData(this.getDataValue('content'), '') }, set: function (value) { this.setDataValue('content', sequelize.stripNullByte(value)) } }, length: { type: DataTypes.INTEGER }, authorship: { type: DataTypes.TEXT('long'), get: function () { return sequelize.processData(this.getDataValue('authorship'), [], JSON.parse) }, set: function (value) { this.setDataValue('authorship', value ? JSON.stringify(value) : value) } } }) Revision.associate = function (models) { Revision.belongsTo(models.Note, { foreignKey: 'noteId', as: 'note', constraints: false, onDelete: 'CASCADE', hooks: true }) } Revision.getNoteRevisions = function (note, callback) { Revision.findAll({ where: { noteId: note.id }, order: [['createdAt', 'DESC']] }).then(function (revisions) { var data = [] for (var i = 0, l = revisions.length; i < l; i++) { var revision = revisions[i] data.push({ time: moment(revision.createdAt).valueOf(), length: revision.length }) } callback(null, data) }).catch(function (err) { callback(err, null) }) } Revision.getPatchedNoteRevisionByTime = function (note, time, callback) { // find all revisions to prepare for all possible calculation Revision.findAll({ where: { noteId: note.id }, order: [['createdAt', 'DESC']] }).then(function (revisions) { if (revisions.length <= 0) return callback(null, null) // measure target revision position Revision.count({ where: { noteId: note.id, createdAt: { [Op.gte]: time } }, order: [['createdAt', 'DESC']] }).then(function (count) { if (count <= 0) return callback(null, null) sendDmpWorker({ msg: 'get revision', revisions: revisions, count: count }, callback) }).catch(function (err) { return callback(err, null) }) }).catch(function (err) { return callback(err, null) }) } Revision.checkAllNotesRevision = function (callback) { Revision.saveAllNotesRevision(function (err, notes) { if (err) return callback(err, null) if (!notes || notes.length <= 0) { return callback(null, notes) } else { Revision.checkAllNotesRevision(callback) } }) } Revision.saveAllNotesRevision = function (callback) { sequelize.models.Note.findAll({ // query all notes that need to save for revision where: { [Op.and]: [ { lastchangeAt: { [Op.or]: { [Op.eq]: null, [Op.and]: { [Op.ne]: null, [Op.gt]: sequelize.col('createdAt') } } } }, { savedAt: { [Op.or]: { [Op.eq]: null, [Op.lt]: sequelize.col('lastchangeAt') } } } ] } }).then(function (notes) { if (notes.length <= 0) return callback(null, notes) var savedNotes = [] async.each(notes, function (note, _callback) { // revision saving policy: note not been modified for 5 mins or not save for 10 mins if (note.lastchangeAt && note.savedAt) { var lastchangeAt = moment(note.lastchangeAt) var savedAt = moment(note.savedAt) if (moment().isAfter(lastchangeAt.add(5, 'minutes'))) { savedNotes.push(note) Revision.saveNoteRevision(note, _callback) } else if (lastchangeAt.isAfter(savedAt.add(10, 'minutes'))) { savedNotes.push(note) Revision.saveNoteRevision(note, _callback) } else { return _callback(null, null) } } else { savedNotes.push(note) Revision.saveNoteRevision(note, _callback) } }, function (err) { if (err) { return callback(err, null) } // return null when no notes need saving at this moment but have delayed tasks to be done var result = ((savedNotes.length === 0) && (notes.length > savedNotes.length)) ? null : savedNotes return callback(null, result) }) }).catch(function (err) { return callback(err, null) }) } Revision.saveNoteRevision = function (note, callback) { Revision.findAll({ where: { noteId: note.id }, order: [['createdAt', 'DESC']] }).then(function (revisions) { if (revisions.length <= 0) { // if no revision available Revision.create({ noteId: note.id, lastContent: note.content ? note.content : '', length: note.content ? note.content.length : 0, authorship: note.authorship }).then(function (revision) { Revision.finishSaveNoteRevision(note, revision, callback) }).catch(function (err) { return callback(err, null) }) } else { var latestRevision = revisions[0] var lastContent = latestRevision.content || latestRevision.lastContent var content = note.content sendDmpWorker({ msg: 'create patch', lastDoc: lastContent, currDoc: content }, function (err, patch) { if (err) logger.error('save note revision error', err) if (!patch) { // if patch is empty (means no difference) then just update the latest revision updated time latestRevision.changed('updatedAt', true) latestRevision.update({ updatedAt: Date.now() }).then(function (revision) { Revision.finishSaveNoteRevision(note, revision, callback) }).catch(function (err) { return callback(err, null) }) } else { Revision.create({ noteId: note.id, patch: patch, content: note.content, length: note.content.length, authorship: note.authorship }).then(function (revision) { // clear last revision content to reduce db size latestRevision.update({ content: null }).then(function () { Revision.finishSaveNoteRevision(note, revision, callback) }).catch(function (err) { return callback(err, null) }) }).catch(function (err) { return callback(err, null) }) } }) } }).catch(function (err) { return callback(err, null) }) } Revision.saveNoteRevisionAsync = util.promisify(Revision.saveNoteRevision) Revision.finishSaveNoteRevision = function (note, revision, callback) { note.update({ savedAt: revision.updatedAt }).then(function () { return callback(null, revision) }).catch(function (err) { return callback(err, null) }) } return Revision } ================================================ FILE: lib/models/user.js ================================================ 'use strict' // external modules var Sequelize = require('sequelize') var Scrypt = require('scrypt-kdf') // core var logger = require('../logger') var { generateAvatarURL } = require('../letter-avatars') module.exports = function (sequelize, DataTypes) { var User = sequelize.define('User', { id: { type: DataTypes.UUID, primaryKey: true, defaultValue: Sequelize.UUIDV4 }, profileid: { type: DataTypes.STRING, unique: true }, profile: { type: DataTypes.TEXT }, history: { type: DataTypes.TEXT }, accessToken: { type: DataTypes.TEXT }, refreshToken: { type: DataTypes.TEXT }, deleteToken: { type: DataTypes.UUID, defaultValue: Sequelize.UUIDV4 }, email: { type: Sequelize.TEXT, validate: { isEmail: true } }, password: { type: Sequelize.TEXT } }) User.hashPassword = async function (plain) { return (await Scrypt.kdf(plain, await Scrypt.pickParams(0.1))).toString('hex') } User.prototype.verifyPassword = async function (attempt) { if (await Scrypt.verify(Buffer.from(this.password, 'hex'), attempt)) { return this } return false } User.addHook('beforeCreate', async function (user) { // only do hash when password is presented if (user.password) { user.password = await User.hashPassword(user.password) } }) User.addHook('beforeUpdate', async function (user) { if (user.changed('password')) { user.password = await User.hashPassword(user.password) } }) User.associate = function (models) { User.hasMany(models.Note, { foreignKey: 'ownerId', constraints: false }) User.hasMany(models.Note, { foreignKey: 'lastchangeuserId', constraints: false }) } User.getProfile = function (user) { if (!user) { return null } return user.profile ? User.parseProfile(user.profile) : (user.email ? User.parseProfileByEmail(user.email) : null) } User.parseProfile = function (profile) { try { profile = JSON.parse(profile) } catch (err) { logger.error(err) profile = null } if (profile) { profile = { name: profile.displayName || profile.username, photo: User.parsePhotoByProfile(profile), biggerphoto: User.parsePhotoByProfile(profile, true) } } return profile } User.parsePhotoByProfile = function (profile, bigger) { var photo = null switch (profile.provider) { case 'facebook': photo = 'https://graph.facebook.com/' + profile.id + '/picture' if (bigger) photo += '?width=400' else photo += '?width=96' break case 'twitter': photo = 'https://twitter.com/' + profile.username + '/profile_image' if (bigger) photo += '?size=original' else photo += '?size=bigger' break case 'github': const photoURL = new URL(profile.photos && profile.photos[0] ? profile.photos[0].value : `https://avatars.githubusercontent.com/u/${profile.id}`) photoURL.searchParams.set('s', bigger ? 400 : 96) photo = photoURL.toString() break case 'gitlab': photo = profile.avatarUrl if (photo) { if (bigger) photo = photo.replace(/(\?s=)\d*$/i, '$1400') else photo = photo.replace(/(\?s=)\d*$/i, '$196') } else { photo = generateAvatarURL(profile.username) } break case 'mattermost': photo = profile.avatarUrl if (photo) { if (bigger) photo = photo.replace(/(\?s=)\d*$/i, '$1400') else photo = photo.replace(/(\?s=)\d*$/i, '$196') } else { photo = generateAvatarURL(profile.username) } break case 'dropbox': photo = generateAvatarURL('', profile.emails[0].value, bigger) break case 'google': photo = profile.photos[0].value if (bigger) photo = photo.replace(/(\?sz=)\d*$/i, '$1400') else photo = photo.replace(/(\?sz=)\d*$/i, '$196') break case 'ldap': photo = generateAvatarURL(profile.username, profile.emails[0], bigger) break case 'saml': photo = generateAvatarURL(profile.username, profile.emails[0], bigger) break case 'oauth2': photo = profile.photo if (!photo) photo = generateAvatarURL(profile.username, profile.email, bigger) break } return photo } User.parseProfileByEmail = function (email) { return { name: email.substring(0, email.lastIndexOf('@')), photo: generateAvatarURL('', email, false), biggerphoto: generateAvatarURL('', email, true) } } return User } ================================================ FILE: lib/note/index.js ================================================ 'use strict' const config = require('../config') const logger = require('../logger') const { Note, User, Revision } = require('../models') const { newCheckViewPermission, errorForbidden, responseCodiMD, errorNotFound, errorInternalError } = require('../response') const { updateHistory, historyDelete } = require('../history') const { actionPublish, actionSlide, actionInfo, actionDownload, actionPDF, actionGist, actionRevision, actionPandoc } = require('./noteActions') const realtime = require('../realtime/realtime') async function getNoteById (noteId, { includeUser } = { includeUser: false }) { const id = await Note.parseNoteIdAsync(noteId) const includes = [] if (includeUser) { includes.push({ model: User, as: 'owner' }, { model: User, as: 'lastchangeuser' }) } const note = await Note.findOne({ where: { id: id }, include: includes }) return note } async function createNote (userId, noteAlias) { if (!config.allowAnonymous && !userId) { throw new Error('can not create note') } const note = await Note.create({ ownerId: userId, alias: noteAlias }) if (userId) { updateHistory(userId, note) } return note } // controller async function showNote (req, res) { const noteId = req.params.noteId const userId = req.user ? req.user.id : null let note = await getNoteById(noteId) if (!note) { // if allow free url enable, auto create note if (!config.allowFreeURL || config.forbiddenNoteIDs.includes(noteId)) { return errorNotFound(req, res) } else if (!config.allowAnonymous && !userId) { return errorForbidden(req, res) } note = await createNote(userId, noteId) } if (!newCheckViewPermission(note, req.isAuthenticated(), userId)) { return errorForbidden(req, res) } // force to use note id const id = Note.encodeNoteId(note.id) if ((note.alias && noteId !== note.alias) || (!note.alias && noteId !== id)) { return res.redirect(config.serverURL + '/' + (note.alias || id)) } return responseCodiMD(res, note) } function canViewNote (note, isLogin, userId) { if (note.permission === 'private') { return note.ownerId === userId } if (note.permission === 'limited' || note.permission === 'protected') { return isLogin } return true } async function showPublishNote (req, res) { const shortid = req.params.shortid const note = await getNoteById(shortid, { includeUser: true }) if (!note) { return errorNotFound(req, res) } if (!canViewNote(note, req.isAuthenticated(), req.user ? req.user.id : null)) { return errorForbidden(req, res) } if ((note.alias && shortid !== note.alias) || (!note.alias && shortid !== note.shortid)) { return res.redirect(config.serverURL + '/s/' + (note.alias || note.shortid)) } await note.increment('viewcount') const body = note.content const extracted = Note.extractMeta(body) const markdown = extracted.markdown const meta = Note.parseMeta(extracted.meta) const createTime = note.createdAt const updateTime = note.lastchangeAt const title = Note.generateWebTitle(meta.title || Note.decodeTitle(note.title)) const data = { title: title, description: meta.description || (markdown ? Note.generateDescription(markdown) : null), image: meta.image, viewcount: note.viewcount, createtime: createTime, updatetime: updateTime, body: body, owner: note.owner ? note.owner.id : null, ownerprofile: note.owner ? User.getProfile(note.owner) : null, lastchangeuser: note.lastchangeuser ? note.lastchangeuser.id : null, lastchangeuserprofile: note.lastchangeuser ? User.getProfile(note.lastchangeuser) : null, robots: meta.robots || false, // default allow robots GA: meta.GA, disqus: meta.disqus, cspNonce: res.locals.nonce } res.set({ 'Cache-Control': 'private' // only cache by client }) res.render('pretty.ejs', data) } async function noteActions (req, res) { const noteId = req.params.noteId const note = await getNoteById(noteId) if (!note) { return errorNotFound(req, res) } if (!canViewNote(note, req.isAuthenticated(), req.user ? req.user.id : null)) { return errorForbidden(req, res) } const action = req.params.action switch (action) { case 'publish': case 'pretty': // pretty deprecated return actionPublish(req, res, note) case 'slide': return actionSlide(req, res, note) case 'download': actionDownload(req, res, note) break case 'info': actionInfo(req, res, note) break case 'pdf': if (config.allowPDFExport) { actionPDF(req, res, note) } else { logger.error('PDF export failed: Disabled by config. Set "allowPDFExport: true" to enable. Check the documentation for details') errorForbidden(req, res) } break case 'gist': actionGist(req, res, note) break case 'revision': actionRevision(req, res, note) break case 'pandoc': actionPandoc(req, res, note) break default: return res.redirect(config.serverURL + '/' + noteId) } } async function getMyNoteList (userId, callback) { const myNotes = await Note.findAll({ where: { ownerId: userId } }) if (!myNotes) { return callback(null, null) } try { const myNoteList = myNotes.map(note => ({ id: Note.encodeNoteId(note.id), text: note.title, tags: Note.parseNoteInfo(note.content).tags, createdAt: note.createdAt, lastchangeAt: note.lastchangeAt, shortId: note.shortid })) if (config.debug) { logger.info('Parse myNoteList success: ' + userId) } return callback(null, myNoteList) } catch (err) { logger.error('Parse myNoteList failed') return callback(err, null) } } function listMyNotes (req, res) { if (req.isAuthenticated()) { getMyNoteList(req.user.id, (err, myNoteList) => { if (err) return errorInternalError(req, res) if (!myNoteList) return errorNotFound(req, res) res.send({ myNotes: myNoteList }) }) } else { return errorForbidden(req, res) } } const deleteNote = async (req, res) => { if (req.isAuthenticated()) { const noteId = await Note.parseNoteIdAsync(req.params.noteId) try { const destroyed = await Note.destroy({ where: { id: noteId, ownerId: req.user.id } }) if (!destroyed) { logger.error('Delete note failed: Make sure the noteId and ownerId are correct.') return errorNotFound(req, res) } historyDelete(req, res) if (realtime.isNoteExistsInPool(noteId)) { const note = realtime.getNoteFromNotePool(noteId) realtime.disconnectSocketOnNote(note) } res.send({ status: 'ok' }) } catch (err) { logger.error('Delete note failed: Internal Error.') return errorInternalError(req, res) } } else { return errorForbidden(req, res) } } const updateNote = async (req, res) => { if (req.isAuthenticated() || config.allowAnonymousEdits) { const noteId = await Note.parseNoteIdAsync(req.params.noteId) try { const note = await Note.findOne({ where: { id: noteId } }) if (!note) { logger.error('Update note failed: Can\'t find the note.') return errorNotFound(req, res) } if (realtime.isNoteExistsInPool(noteId)) { logger.error('Update note failed: There are online users opening this note.') return res.status('403').json({ status: 'error', message: 'Update API can only be used when no users is online' }) } const now = Date.now() const content = req.body.content const updated = await note.update({ title: Note.parseNoteTitle(content), content: content, lastchangeAt: now, authorship: [ [ req.isAuthenticated() ? req.user.id : null, 0, content.length, now, now ] ] }) if (!updated) { logger.error('Update note failed: Write note content error.') return errorInternalError(req, res) } if (req.isAuthenticated()) { updateHistory(req.user.id, Note.encodeNoteId(noteId), content) } Revision.saveNoteRevision(note, (err, revision) => { if (err) { logger.error(err) return errorInternalError(req, res) } if (!revision) return errorNotFound(req, res) res.send({ status: 'ok' }) }) } catch (err) { logger.error(err.stack) logger.error('Update note failed: Internal Error.') return errorInternalError(req, res) } } else { return errorForbidden(req, res) } } exports.showNote = showNote exports.showPublishNote = showPublishNote exports.noteActions = noteActions exports.listMyNotes = listMyNotes exports.deleteNote = deleteNote exports.updateNote = updateNote ================================================ FILE: lib/note/noteActions.js ================================================ 'use strict' const fs = require('fs') const path = require('path') const markdownpdf = require('markdown-pdf') const shortId = require('shortid') const querystring = require('querystring') const moment = require('moment') const { Pandoc } = require('@hackmd/pandoc.js') const config = require('../config') const logger = require('../logger') const { Note, Revision } = require('../models') const { errorInternalError, errorNotFound } = require('../response') function actionPublish (req, res, note) { res.redirect(config.serverURL + '/s/' + (note.alias || note.shortid)) } function actionSlide (req, res, note) { res.redirect(config.serverURL + '/p/' + (note.alias || note.shortid)) } function actionDownload (req, res, note) { const body = note.content const title = Note.decodeTitle(note.title) const filename = encodeURIComponent(title) res.set({ 'Access-Control-Allow-Origin': '*', // allow CORS as API 'Access-Control-Allow-Headers': 'Range', 'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range', 'Content-Type': 'text/markdown; charset=UTF-8', 'Cache-Control': 'private', 'Content-disposition': 'attachment; filename=' + filename + '.md', 'X-Robots-Tag': 'noindex, nofollow' // prevent crawling }) res.send(body) } function actionInfo (req, res, note) { const body = note.content const extracted = Note.extractMeta(body) const markdown = extracted.markdown const meta = Note.parseMeta(extracted.meta) const createtime = note.createdAt const updatetime = note.lastchangeAt const title = Note.decodeTitle(note.title) const data = { title: meta.title || title, description: meta.description || (markdown ? Note.generateDescription(markdown) : null), viewcount: note.viewcount, createtime: createtime, updatetime: updatetime } res.set({ 'Access-Control-Allow-Origin': '*', // allow CORS as API 'Access-Control-Allow-Headers': 'Range', 'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range', 'Cache-Control': 'private', // only cache by client 'X-Robots-Tag': 'noindex, nofollow' // prevent crawling }) res.send(data) } function actionPDF (req, res, note) { const url = config.serverURL || 'http://' + req.get('host') const body = note.content const extracted = Note.extractMeta(body) let content = extracted.markdown const title = Note.decodeTitle(note.title) const highlightCssPath = path.join(config.appRootPath, '/node_modules/highlight.js/styles/github-gist.css') if (!fs.existsSync(config.tmpPath)) { fs.mkdirSync(config.tmpPath) } const pdfPath = config.tmpPath + '/' + Date.now() + '.pdf' content = content.replace(/\]\(\//g, '](' + url + '/') const markdownpdfOptions = { highlightCssPath: highlightCssPath } markdownpdf(markdownpdfOptions).from.string(content).to(pdfPath, function () { if (!fs.existsSync(pdfPath)) { logger.error('PDF seems to not be generated as expected. File doesn\'t exist: ' + pdfPath) return errorInternalError(req, res) } const stream = fs.createReadStream(pdfPath) let filename = title // Be careful of special characters filename = encodeURIComponent(filename) // Ideally this should strip them res.setHeader('Content-disposition', 'attachment; filename="' + filename + '.pdf"') res.setHeader('Cache-Control', 'private') res.setHeader('Content-Type', 'application/pdf; charset=UTF-8') res.setHeader('X-Robots-Tag', 'noindex, nofollow') // prevent crawling stream.on('end', () => { stream.close() fs.unlinkSync(pdfPath) }) stream.pipe(res) }) } const outputFormats = { asciidoc: 'text/plain', context: 'application/x-latex', epub: 'application/epub+zip', epub3: 'application/epub+zip', latex: 'application/x-latex', odt: 'application/vnd.oasis.opendocument.text', pdf: 'application/pdf', rst: 'text/plain', rtf: 'application/rtf', textile: 'text/plain', docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' } async function actionPandoc (req, res, note) { var url = config.serverURL || 'http://' + req.get('host') var body = note.content var extracted = Note.extractMeta(body) var content = extracted.markdown var title = Note.decodeTitle(note.title) if (!fs.existsSync(config.tmpPath)) { fs.mkdirSync(config.tmpPath) } const pandoc = new Pandoc() var path = config.tmpPath + '/' + Date.now() content = content.replace(/\]\(\//g, '](' + url + '/') const { exportType } = req.query if (typeof exportType !== 'string') { return res.sendStatus(400) } const contentType = outputFormats[exportType] if (!contentType) { return res.sendStatus(400) } try { // TODO: timeout rejection await pandoc.convertToFile(content, 'markdown', exportType, path, [ '--metadata', `title=${title}`, '--sandbox' ]) var stream = fs.createReadStream(path) var filename = title // Be careful of special characters filename = encodeURIComponent(filename) // Ideally this should strip them res.setHeader('Content-disposition', `attachment; filename="${filename}.${exportType}"`) res.setHeader('Cache-Control', 'private') res.setHeader('Content-Type', `${contentType}; charset=UTF-8`) res.setHeader('X-Robots-Tag', 'noindex, nofollow') // prevent crawling stream.pipe(res) } catch (err) { // TODO: handle error res.json({ message: err.message }) } } function actionGist (req, res, note) { const data = { client_id: config.github.clientID, redirect_uri: config.serverURL + '/auth/github/callback/' + Note.encodeNoteId(note.id) + '/gist', scope: 'gist', state: shortId.generate() } const query = querystring.stringify(data) res.redirect('https://github.com/login/oauth/authorize?' + query) } function actionRevision (req, res, note) { const actionId = req.params.actionId if (actionId) { const time = moment(parseInt(actionId)) if (!time.isValid()) { return errorNotFound(req, res) } Revision.getPatchedNoteRevisionByTime(note, time, function (err, content) { if (err) { logger.error(err) return errorInternalError(req, res) } if (!content) { return errorNotFound(req, res) } res.set({ 'Access-Control-Allow-Origin': '*', // allow CORS as API 'Access-Control-Allow-Headers': 'Range', 'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range', 'Cache-Control': 'private', // only cache by client 'X-Robots-Tag': 'noindex, nofollow' // prevent crawling }) res.send(content) }) } else { Revision.getNoteRevisions(note, function (err, data) { if (err) { logger.error(err) return errorInternalError(req, res) } const result = { revision: data } res.set({ 'Access-Control-Allow-Origin': '*', // allow CORS as API 'Access-Control-Allow-Headers': 'Range', 'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range', 'Cache-Control': 'private', // only cache by client 'X-Robots-Tag': 'noindex, nofollow' // prevent crawling }) res.send(result) }) } } exports.actionPublish = actionPublish exports.actionSlide = actionSlide exports.actionDownload = actionDownload exports.actionInfo = actionInfo exports.actionPDF = actionPDF exports.actionGist = actionGist exports.actionPandoc = actionPandoc exports.actionRevision = actionRevision ================================================ FILE: lib/ot/client.js ================================================ // translation of https://github.com/djspiewak/cccp/blob/master/agent/src/main/scala/com/codecommit/cccp/agent/state.scala if (typeof ot === 'undefined') { var ot = {}; } ot.Client = (function (global) { 'use strict'; // Client constructor function Client (revision) { this.revision = revision; // the next expected revision number this.setState(synchronized_); // start state } Client.prototype.setState = function (state) { this.state = state; }; // Call this method when the user changes the document. Client.prototype.applyClient = function (operation) { this.setState(this.state.applyClient(this, operation)); }; // Call this method with a new operation from the server Client.prototype.applyServer = function (revision, operation) { this.setState(this.state.applyServer(this, revision, operation)); }; Client.prototype.applyOperations = function (head, operations) { this.setState(this.state.applyOperations(this, head, operations)); }; Client.prototype.serverAck = function (revision) { this.setState(this.state.serverAck(this, revision)); }; Client.prototype.serverReconnect = function () { if (typeof this.state.resend === 'function') { this.state.resend(this); } }; // Transforms a selection from the latest known server state to the current // client state. For example, if we get from the server the information that // another user's cursor is at position 3, but the server hasn't yet received // our newest operation, an insertion of 5 characters at the beginning of the // document, the correct position of the other user's cursor in our current // document is 8. Client.prototype.transformSelection = function (selection) { return this.state.transformSelection(selection); }; // Override this method. Client.prototype.sendOperation = function (revision, operation) { throw new Error("sendOperation must be defined in child class"); }; // Override this method. Client.prototype.applyOperation = function (operation) { throw new Error("applyOperation must be defined in child class"); }; // In the 'Synchronized' state, there is no pending operation that the client // has sent to the server. function Synchronized () {} Client.Synchronized = Synchronized; Synchronized.prototype.applyClient = function (client, operation) { // When the user makes an edit, send the operation to the server and // switch to the 'AwaitingConfirm' state client.sendOperation(client.revision, operation); return new AwaitingConfirm(operation); }; Synchronized.prototype.applyServer = function (client, revision, operation) { if (revision - client.revision > 1) { throw new Error("Invalid revision."); } client.revision = revision; // When we receive a new operation from the server, the operation can be // simply applied to the current document client.applyOperation(operation); return this; }; Synchronized.prototype.serverAck = function (client, revision) { throw new Error("There is no pending operation."); }; // Nothing to do because the latest server state and client state are the same. Synchronized.prototype.transformSelection = function (x) { return x; }; // Singleton var synchronized_ = new Synchronized(); // In the 'AwaitingConfirm' state, there's one operation the client has sent // to the server and is still waiting for an acknowledgement. function AwaitingConfirm (outstanding) { // Save the pending operation this.outstanding = outstanding; } Client.AwaitingConfirm = AwaitingConfirm; AwaitingConfirm.prototype.applyClient = function (client, operation) { // When the user makes an edit, don't send the operation immediately, // instead switch to 'AwaitingWithBuffer' state return new AwaitingWithBuffer(this.outstanding, operation); }; AwaitingConfirm.prototype.applyServer = function (client, revision, operation) { if (revision - client.revision > 1) { throw new Error("Invalid revision."); } client.revision = revision; // This is another client's operation. Visualization: // // /\ // this.outstanding / \ operation // / \ // \ / // pair[1] \ / pair[0] (new outstanding) // (can be applied \/ // to the client's // current document) var pair = operation.constructor.transform(this.outstanding, operation); client.applyOperation(pair[1]); return new AwaitingConfirm(pair[0]); }; AwaitingConfirm.prototype.serverAck = function (client, revision) { if (revision - client.revision > 1) { return new Stale(this.outstanding, client, revision).getOperations(); } client.revision = revision; // The client's operation has been acknowledged // => switch to synchronized state return synchronized_; }; AwaitingConfirm.prototype.transformSelection = function (selection) { return selection.transform(this.outstanding); }; AwaitingConfirm.prototype.resend = function (client) { // The confirm didn't come because the client was disconnected. // Now that it has reconnected, we resend the outstanding operation. client.sendOperation(client.revision, this.outstanding); }; // In the 'AwaitingWithBuffer' state, the client is waiting for an operation // to be acknowledged by the server while buffering the edits the user makes function AwaitingWithBuffer (outstanding, buffer) { // Save the pending operation and the user's edits since then this.outstanding = outstanding; this.buffer = buffer; } Client.AwaitingWithBuffer = AwaitingWithBuffer; AwaitingWithBuffer.prototype.applyClient = function (client, operation) { // Compose the user's changes onto the buffer var newBuffer = this.buffer.compose(operation); return new AwaitingWithBuffer(this.outstanding, newBuffer); }; AwaitingWithBuffer.prototype.applyServer = function (client, revision, operation) { if (revision - client.revision > 1) { throw new Error("Invalid revision."); } client.revision = revision; // Operation comes from another client // // /\ // this.outstanding / \ operation // / \ // /\ / // this.buffer / \* / pair1[0] (new outstanding) // / \/ // \ / // pair2[1] \ / pair2[0] (new buffer) // the transformed \/ // operation -- can // be applied to the // client's current // document // // * pair1[1] var transform = operation.constructor.transform; var pair1 = transform(this.outstanding, operation); var pair2 = transform(this.buffer, pair1[1]); client.applyOperation(pair2[1]); return new AwaitingWithBuffer(pair1[0], pair2[0]); }; AwaitingWithBuffer.prototype.serverAck = function (client, revision) { if (revision - client.revision > 1) { return new StaleWithBuffer(this.outstanding, this.buffer, client, revision).getOperations(); } client.revision = revision; // The pending operation has been acknowledged // => send buffer client.sendOperation(client.revision, this.buffer); return new AwaitingConfirm(this.buffer); }; AwaitingWithBuffer.prototype.transformSelection = function (selection) { return selection.transform(this.outstanding).transform(this.buffer); }; AwaitingWithBuffer.prototype.resend = function (client) { // The confirm didn't come because the client was disconnected. // Now that it has reconnected, we resend the outstanding operation. client.sendOperation(client.revision, this.outstanding); }; function Stale(acknowlaged, client, revision) { this.acknowlaged = acknowlaged; this.client = client; this.revision = revision; } Client.Stale = Stale; Stale.prototype.applyClient = function (client, operation) { return new StaleWithBuffer(this.acknowlaged, operation, client, this.revision); }; Stale.prototype.applyServer = function (client, revision, operation) { throw new Error("Ignored server-side change."); }; Stale.prototype.applyOperations = function (client, head, operations) { var transform = this.acknowlaged.constructor.transform; for (var i = 0; i < operations.length; i++) { var op = ot.TextOperation.fromJSON(operations[i]); var pair = transform(this.acknowlaged, op); client.applyOperation(pair[1]); this.acknowlaged = pair[0]; } client.revision = this.revision; return synchronized_; }; Stale.prototype.serverAck = function (client, revision) { throw new Error("There is no pending operation."); }; Stale.prototype.transformSelection = function (selection) { return selection; }; Stale.prototype.getOperations = function () { this.client.getOperations(this.client.revision, this.revision - 1); // acknowlaged is the one at revision return this; }; function StaleWithBuffer(acknowlaged, buffer, client, revision) { this.acknowlaged = acknowlaged; this.buffer = buffer; this.client = client; this.revision = revision; } Client.StaleWithBuffer = StaleWithBuffer; StaleWithBuffer.prototype.applyClient = function (client, operation) { var buffer = this.buffer.compose(operation); return new StaleWithBuffer(this.acknowlaged, buffer, client, this.revision); }; StaleWithBuffer.prototype.applyServer = function (client, revision, operation) { throw new Error("Ignored server-side change."); }; StaleWithBuffer.prototype.applyOperations = function (client, head, operations) { var transform = this.acknowlaged.constructor.transform; for (var i = 0; i < operations.length; i++) { var op = ot.TextOperation.fromJSON(operations[i]); var pair1 = transform(this.acknowlaged, op); var pair2 = transform(this.buffer, pair1[1]); client.applyOperation(pair2[1]); this.acknowlaged = pair1[0]; this.buffer = pair2[0]; } client.revision = this.revision; client.sendOperation(client.revision, this.buffer); return new AwaitingConfirm(this.buffer); }; StaleWithBuffer.prototype.serverAck = function (client, revision) { throw new Error("There is no pending operation."); }; StaleWithBuffer.prototype.transformSelection = function (selection) { return selection; }; StaleWithBuffer.prototype.getOperations = function () { this.client.getOperations(this.client.revision, this.revision - 1); // acknowlaged is the one at revision return this; }; return Client; }(this)); if (typeof module === 'object') { module.exports = ot.Client; } ================================================ FILE: lib/ot/editor-socketio-server.js ================================================ 'use strict'; var EventEmitter = require('events').EventEmitter; var TextOperation = require('./text-operation'); var WrappedOperation = require('./wrapped-operation'); var Server = require('./server'); var Selection = require('./selection'); var util = require('util'); var logger = require('../logger'); function EditorSocketIOServer(document, operations, docId, mayWrite, operationCallback) { EventEmitter.call(this); Server.call(this, document, operations); this.users = {}; this.docId = docId; this.mayWrite = mayWrite || function (_, cb) { cb(true); }; this.operationCallback = operationCallback; } util.inherits(EditorSocketIOServer, Server); extend(EditorSocketIOServer.prototype, EventEmitter.prototype); function extend(target, source) { for (var key in source) { if (source.hasOwnProperty(key)) { target[key] = source[key]; } } } EditorSocketIOServer.prototype.addClient = function (socket) { var self = this; socket.join(this.docId); var docOut = { str: this.document, revision: this.operations.length, clients: this.users }; socket.emit('doc', docOut); socket.on('operation', function (revision, operation, selection) { socket.origin = 'operation'; self.mayWrite(socket, function (mayWrite) { if (!mayWrite) { logger.info("User doesn't have the right to edit."); return; } try { self.onOperation(socket, revision, operation, selection); if (typeof self.operationCallback === 'function') self.operationCallback(socket, operation); } catch (err) { setTimeout(function() { var docOut = { str: self.document, revision: self.operations.length, clients: self.users, force: true }; socket.emit('doc', docOut); }, 100); } }); }); socket.on('get_operations', function (base, head) { self.onGetOperations(socket, base, head); }); socket.on('selection', function (obj) { socket.origin = 'selection'; self.mayWrite(socket, function (mayWrite) { if (!mayWrite) { logger.info("User doesn't have the right to edit."); return; } self.updateSelection(socket, obj && Selection.fromJSON(obj)); }); }); socket.on('disconnect', function () { logger.debug("Disconnect"); socket.leave(self.docId); self.onDisconnect(socket); /* if (socket.manager && socket.manager.sockets.clients(self.docId).length === 0) { self.emit('empty-room'); } */ }); }; EditorSocketIOServer.prototype.onOperation = function (socket, revision, operation, selection) { var wrapped; try { wrapped = new WrappedOperation( TextOperation.fromJSON(operation), selection && Selection.fromJSON(selection) ); } catch (exc) { logger.error("Invalid operation received: "); logger.error(exc); throw new Error(exc); } try { var clientId = socket.id; var wrappedPrime = this.receiveOperation(revision, wrapped); if(!wrappedPrime) return; logger.debug("new operation: " + JSON.stringify(wrapped)); this.getClient(clientId).selection = wrappedPrime.meta; revision = this.operations.length; socket.emit('ack', revision); socket.broadcast.in(this.docId).emit( 'operation', clientId, revision, wrappedPrime.wrapped.toJSON(), wrappedPrime.meta ); //set document is dirty this.isDirty = true; } catch (exc) { logger.error(exc); throw new Error(exc); } }; EditorSocketIOServer.prototype.onGetOperations = function (socket, base, head) { var operations = this.operations.slice(base, head).map(function (op) { return op.wrapped.toJSON(); }); socket.emit('operations', head, operations); }; EditorSocketIOServer.prototype.updateSelection = function (socket, selection) { var clientId = socket.id; if (selection) { this.getClient(clientId).selection = selection; } else { delete this.getClient(clientId).selection; } socket.broadcast.to(this.docId).emit('selection', clientId, selection); }; EditorSocketIOServer.prototype.setName = function (socket, name) { var clientId = socket.id; this.getClient(clientId).name = name; socket.broadcast.to(this.docId).emit('set_name', clientId, name); }; EditorSocketIOServer.prototype.setColor = function (socket, color) { var clientId = socket.id; this.getClient(clientId).color = color; socket.broadcast.to(this.docId).emit('set_color', clientId, color); }; EditorSocketIOServer.prototype.getClient = function (clientId) { return this.users[clientId] || (this.users[clientId] = {}); }; EditorSocketIOServer.prototype.onDisconnect = function (socket) { var clientId = socket.id; delete this.users[clientId]; socket.broadcast.to(this.docId).emit('client_left', clientId); }; module.exports = EditorSocketIOServer; ================================================ FILE: lib/ot/index.js ================================================ exports.version = '0.0.15'; exports.TextOperation = require('./text-operation'); exports.SimpleTextOperation = require('./simple-text-operation'); exports.Client = require('./client'); exports.Server = require('./server'); exports.Selection = require('./selection'); exports.EditorSocketIOServer = require('./editor-socketio-server'); ================================================ FILE: lib/ot/selection.js ================================================ if (typeof ot === 'undefined') { // Export for browsers var ot = {}; } ot.Selection = (function (global) { 'use strict'; var TextOperation = global.ot ? global.ot.TextOperation : require('./text-operation'); // Range has `anchor` and `head` properties, which are zero-based indices into // the document. The `anchor` is the side of the selection that stays fixed, // `head` is the side of the selection where the cursor is. When both are // equal, the range represents a cursor. function Range (anchor, head) { this.anchor = anchor; this.head = head; } Range.fromJSON = function (obj) { return new Range(obj.anchor, obj.head); }; Range.prototype.equals = function (other) { return this.anchor === other.anchor && this.head === other.head; }; Range.prototype.isEmpty = function () { return this.anchor === this.head; }; Range.prototype.transform = function (other) { function transformIndex (index) { var newIndex = index; var ops = other.ops; for (var i = 0, l = other.ops.length; i < l; i++) { if (TextOperation.isRetain(ops[i])) { index -= ops[i]; } else if (TextOperation.isInsert(ops[i])) { newIndex += ops[i].length; } else { newIndex -= Math.min(index, -ops[i]); index += ops[i]; } if (index < 0) { break; } } return newIndex; } var newAnchor = transformIndex(this.anchor); if (this.anchor === this.head) { return new Range(newAnchor, newAnchor); } return new Range(newAnchor, transformIndex(this.head)); }; // A selection is basically an array of ranges. Every range represents a real // selection or a cursor in the document (when the start position equals the // end position of the range). The array must not be empty. function Selection (ranges) { this.ranges = ranges || []; } Selection.Range = Range; // Convenience method for creating selections only containing a single cursor // and no real selection range. Selection.createCursor = function (position) { return new Selection([new Range(position, position)]); }; Selection.fromJSON = function (obj) { var objRanges = obj.ranges || obj; for (var i = 0, ranges = []; i < objRanges.length; i++) { ranges[i] = Range.fromJSON(objRanges[i]); } return new Selection(ranges); }; Selection.prototype.equals = function (other) { if (this.position !== other.position) { return false; } if (this.ranges.length !== other.ranges.length) { return false; } // FIXME: Sort ranges before comparing them? for (var i = 0; i < this.ranges.length; i++) { if (!this.ranges[i].equals(other.ranges[i])) { return false; } } return true; }; Selection.prototype.somethingSelected = function () { for (var i = 0; i < this.ranges.length; i++) { if (!this.ranges[i].isEmpty()) { return true; } } return false; }; // Return the more current selection information. Selection.prototype.compose = function (other) { return other; }; // Update the selection with respect to an operation. Selection.prototype.transform = function (other) { for (var i = 0, newRanges = []; i < this.ranges.length; i++) { newRanges[i] = this.ranges[i].transform(other); } return new Selection(newRanges); }; return Selection; }(this)); // Export for CommonJS if (typeof module === 'object') { module.exports = ot.Selection; } ================================================ FILE: lib/ot/server.js ================================================ var config = require('../config'); if (typeof ot === 'undefined') { var ot = {}; } ot.Server = (function (global) { 'use strict'; // Constructor. Takes the current document as a string and optionally the array // of all operations. function Server (document, operations) { this.document = document; this.operations = operations || []; } // Call this method whenever you receive an operation from a client. Server.prototype.receiveOperation = function (revision, operation) { if (revision < 0 || this.operations.length < revision) { throw new Error("operation revision not in history"); } // Find all operations that the client didn't know of when it sent the // operation ... var concurrentOperations = this.operations.slice(revision); // ... and transform the operation against all these operations ... var transform = operation.constructor.transform; for (var i = 0; i < concurrentOperations.length; i++) { operation = transform(operation, concurrentOperations[i])[0]; } // ... and apply that on the document. var newDocument = operation.apply(this.document); // ignore if exceed the max length of document if(newDocument.length > config.documentMaxLength && newDocument.length > this.document.length) return; this.document = newDocument; // Store operation in history. this.operations.push(operation); // It's the caller's responsibility to send the operation to all connected // clients and an acknowledgement to the creator. return operation; }; return Server; }(this)); if (typeof module === 'object') { module.exports = ot.Server; } ================================================ FILE: lib/ot/simple-text-operation.js ================================================ if (typeof ot === 'undefined') { // Export for browsers var ot = {}; } ot.SimpleTextOperation = (function (global) { var TextOperation = global.ot ? global.ot.TextOperation : require('./text-operation'); function SimpleTextOperation () {} // Insert the string `str` at the zero-based `position` in the document. function Insert (str, position) { if (!this || this.constructor !== SimpleTextOperation) { // => function was called without 'new' return new Insert(str, position); } this.str = str; this.position = position; } Insert.prototype = new SimpleTextOperation(); SimpleTextOperation.Insert = Insert; Insert.prototype.toString = function () { return 'Insert(' + JSON.stringify(this.str) + ', ' + this.position + ')'; }; Insert.prototype.equals = function (other) { return other instanceof Insert && this.str === other.str && this.position === other.position; }; Insert.prototype.apply = function (doc) { return doc.slice(0, this.position) + this.str + doc.slice(this.position); }; // Delete `count` many characters at the zero-based `position` in the document. function Delete (count, position) { if (!this || this.constructor !== SimpleTextOperation) { return new Delete(count, position); } this.count = count; this.position = position; } Delete.prototype = new SimpleTextOperation(); SimpleTextOperation.Delete = Delete; Delete.prototype.toString = function () { return 'Delete(' + this.count + ', ' + this.position + ')'; }; Delete.prototype.equals = function (other) { return other instanceof Delete && this.count === other.count && this.position === other.position; }; Delete.prototype.apply = function (doc) { return doc.slice(0, this.position) + doc.slice(this.position + this.count); }; // An operation that does nothing. This is needed for the result of the // transformation of two deletions of the same character. function Noop () { if (!this || this.constructor !== SimpleTextOperation) { return new Noop(); } } Noop.prototype = new SimpleTextOperation(); SimpleTextOperation.Noop = Noop; Noop.prototype.toString = function () { return 'Noop()'; }; Noop.prototype.equals = function (other) { return other instanceof Noop; }; Noop.prototype.apply = function (doc) { return doc; }; var noop = new Noop(); SimpleTextOperation.transform = function (a, b) { if (a instanceof Noop || b instanceof Noop) { return [a, b]; } if (a instanceof Insert && b instanceof Insert) { if (a.position < b.position || (a.position === b.position && a.str < b.str)) { return [a, new Insert(b.str, b.position + a.str.length)]; } if (a.position > b.position || (a.position === b.position && a.str > b.str)) { return [new Insert(a.str, a.position + b.str.length), b]; } return [noop, noop]; } if (a instanceof Insert && b instanceof Delete) { if (a.position <= b.position) { return [a, new Delete(b.count, b.position + a.str.length)]; } if (a.position >= b.position + b.count) { return [new Insert(a.str, a.position - b.count), b]; } // Here, we have to delete the inserted string of operation a. // That doesn't preserve the intention of operation a, but it's the only // thing we can do to get a valid transform function. return [noop, new Delete(b.count + a.str.length, b.position)]; } if (a instanceof Delete && b instanceof Insert) { if (a.position >= b.position) { return [new Delete(a.count, a.position + b.str.length), b]; } if (a.position + a.count <= b.position) { return [a, new Insert(b.str, b.position - a.count)]; } // Same problem as above. We have to delete the string that was inserted // in operation b. return [new Delete(a.count + b.str.length, a.position), noop]; } if (a instanceof Delete && b instanceof Delete) { if (a.position === b.position) { if (a.count === b.count) { return [noop, noop]; } else if (a.count < b.count) { return [noop, new Delete(b.count - a.count, b.position)]; } return [new Delete(a.count - b.count, a.position), noop]; } if (a.position < b.position) { if (a.position + a.count <= b.position) { return [a, new Delete(b.count, b.position - a.count)]; } if (a.position + a.count >= b.position + b.count) { return [new Delete(a.count - b.count, a.position), noop]; } return [ new Delete(b.position - a.position, a.position), new Delete(b.position + b.count - (a.position + a.count), a.position) ]; } if (a.position > b.position) { if (a.position >= b.position + b.count) { return [new Delete(a.count, a.position - b.count), b]; } if (a.position + a.count <= b.position + b.count) { return [noop, new Delete(b.count - a.count, b.position)]; } return [ new Delete(a.position + a.count - (b.position + b.count), b.position), new Delete(a.position - b.position, b.position) ]; } } }; // Convert a normal, composable `TextOperation` into an array of // `SimpleTextOperation`s. SimpleTextOperation.fromTextOperation = function (operation) { var simpleOperations = []; var index = 0; for (var i = 0; i < operation.ops.length; i++) { var op = operation.ops[i]; if (TextOperation.isRetain(op)) { index += op; } else if (TextOperation.isInsert(op)) { simpleOperations.push(new Insert(op, index)); index += op.length; } else { simpleOperations.push(new Delete(Math.abs(op), index)); } } return simpleOperations; }; return SimpleTextOperation; })(this); // Export for CommonJS if (typeof module === 'object') { module.exports = ot.SimpleTextOperation; } ================================================ FILE: lib/ot/text-operation.js ================================================ if (typeof ot === 'undefined') { // Export for browsers var ot = {}; } ot.TextOperation = (function () { 'use strict'; // Constructor for new operations. function TextOperation () { if (!this || this.constructor !== TextOperation) { // => function was called without 'new' return new TextOperation(); } // When an operation is applied to an input string, you can think of this as // if an imaginary cursor runs over the entire string and skips over some // parts, deletes some parts and inserts characters at some positions. These // actions (skip/delete/insert) are stored as an array in the "ops" property. this.ops = []; // An operation's baseLength is the length of every string the operation // can be applied to. this.baseLength = 0; // The targetLength is the length of every string that results from applying // the operation on a valid input string. this.targetLength = 0; } TextOperation.prototype.equals = function (other) { if (this.baseLength !== other.baseLength) { return false; } if (this.targetLength !== other.targetLength) { return false; } if (this.ops.length !== other.ops.length) { return false; } for (var i = 0; i < this.ops.length; i++) { if (this.ops[i] !== other.ops[i]) { return false; } } return true; }; // Operation are essentially lists of ops. There are three types of ops: // // * Retain ops: Advance the cursor position by a given number of characters. // Represented by positive ints. // * Insert ops: Insert a given string at the current cursor position. // Represented by strings. // * Delete ops: Delete the next n characters. Represented by negative ints. var isRetain = TextOperation.isRetain = function (op) { return typeof op === 'number' && op > 0; }; var isInsert = TextOperation.isInsert = function (op) { return typeof op === 'string'; }; var isDelete = TextOperation.isDelete = function (op) { return typeof op === 'number' && op < 0; }; // After an operation is constructed, the user of the library can specify the // actions of an operation (skip/insert/delete) with these three builder // methods. They all return the operation for convenient chaining. // Skip over a given number of characters. TextOperation.prototype.retain = function (n) { if (typeof n !== 'number') { throw new Error("retain expects an integer"); } if (n === 0) { return this; } this.baseLength += n; this.targetLength += n; if (isRetain(this.ops[this.ops.length-1])) { // The last op is a retain op => we can merge them into one op. this.ops[this.ops.length-1] += n; } else { // Create a new op. this.ops.push(n); } return this; }; // Insert a string at the current position. TextOperation.prototype.insert = function (str) { if (typeof str !== 'string') { throw new Error("insert expects a string"); } if (str === '') { return this; } this.targetLength += str.length; var ops = this.ops; if (isInsert(ops[ops.length-1])) { // Merge insert op. ops[ops.length-1] += str; } else if (isDelete(ops[ops.length-1])) { // It doesn't matter when an operation is applied whether the operation // is delete(3), insert("something") or insert("something"), delete(3). // Here we enforce that in this case, the insert op always comes first. // This makes all operations that have the same effect when applied to // a document of the right length equal in respect to the `equals` method. if (isInsert(ops[ops.length-2])) { ops[ops.length-2] += str; } else { ops[ops.length] = ops[ops.length-1]; ops[ops.length-2] = str; } } else { ops.push(str); } return this; }; // Delete a string at the current position. TextOperation.prototype['delete'] = function (n) { if (typeof n === 'string') { n = n.length; } if (typeof n !== 'number') { throw new Error("delete expects an integer or a string"); } if (n === 0) { return this; } if (n > 0) { n = -n; } this.baseLength -= n; if (isDelete(this.ops[this.ops.length-1])) { this.ops[this.ops.length-1] += n; } else { this.ops.push(n); } return this; }; // Tests whether this operation has no effect. TextOperation.prototype.isNoop = function () { return this.ops.length === 0 || (this.ops.length === 1 && isRetain(this.ops[0])); }; // Pretty printing. TextOperation.prototype.toString = function () { // map: build a new array by applying a function to every element in an old // array. var map = Array.prototype.map || function (fn) { var arr = this; var newArr = []; for (var i = 0, l = arr.length; i < l; i++) { newArr[i] = fn(arr[i]); } return newArr; }; return map.call(this.ops, function (op) { if (isRetain(op)) { return "retain " + op; } else if (isInsert(op)) { return "insert '" + op + "'"; } else { return "delete " + (-op); } }).join(', '); }; // Converts operation into a JSON value. TextOperation.prototype.toJSON = function () { return this.ops; }; // Converts a plain JS object into an operation and validates it. TextOperation.fromJSON = function (ops) { var o = new TextOperation(); for (var i = 0, l = ops.length; i < l; i++) { var op = ops[i]; if (isRetain(op)) { o.retain(op); } else if (isInsert(op)) { o.insert(op); } else if (isDelete(op)) { o['delete'](op); } else { throw new Error("unknown operation: " + JSON.stringify(op)); } } return o; }; // Apply an operation to a string, returning a new string. Throws an error if // there's a mismatch between the input string and the operation. TextOperation.prototype.apply = function (str) { var operation = this; if (str.length !== operation.baseLength) { throw new Error("The operation's base length must be equal to the string's length."); } var newStr = [], j = 0; var strIndex = 0; var ops = this.ops; for (var i = 0, l = ops.length; i < l; i++) { var op = ops[i]; if (isRetain(op)) { if (strIndex + op > str.length) { throw new Error("Operation can't retain more characters than are left in the string."); } // Copy skipped part of the old string. newStr[j++] = str.slice(strIndex, strIndex + op); strIndex += op; } else if (isInsert(op)) { // Insert string. newStr[j++] = op; } else { // delete op strIndex -= op; } } if (strIndex !== str.length) { throw new Error("The operation didn't operate on the whole string."); } return newStr.join(''); }; // Computes the inverse of an operation. The inverse of an operation is the // operation that reverts the effects of the operation, e.g. when you have an // operation 'insert("hello "); skip(6);' then the inverse is 'delete("hello "); // skip(6);'. The inverse should be used for implementing undo. TextOperation.prototype.invert = function (str) { var strIndex = 0; var inverse = new TextOperation(); var ops = this.ops; for (var i = 0, l = ops.length; i < l; i++) { var op = ops[i]; if (isRetain(op)) { inverse.retain(op); strIndex += op; } else if (isInsert(op)) { inverse['delete'](op.length); } else { // delete op inverse.insert(str.slice(strIndex, strIndex - op)); strIndex -= op; } } return inverse; }; // Compose merges two consecutive operations into one operation, that // preserves the changes of both. Or, in other words, for each input string S // and a pair of consecutive operations A and B, // apply(apply(S, A), B) = apply(S, compose(A, B)) must hold. TextOperation.prototype.compose = function (operation2) { var operation1 = this; if (operation1.targetLength !== operation2.baseLength) { throw new Error("The base length of the second operation has to be the target length of the first operation"); } var operation = new TextOperation(); // the combined operation var ops1 = operation1.ops, ops2 = operation2.ops; // for fast access var i1 = 0, i2 = 0; // current index into ops1 respectively ops2 var op1 = ops1[i1++], op2 = ops2[i2++]; // current ops while (true) { // Dispatch on the type of op1 and op2 if (typeof op1 === 'undefined' && typeof op2 === 'undefined') { // end condition: both ops1 and ops2 have been processed break; } if (isDelete(op1)) { operation['delete'](op1); op1 = ops1[i1++]; continue; } if (isInsert(op2)) { operation.insert(op2); op2 = ops2[i2++]; continue; } if (typeof op1 === 'undefined') { throw new Error("Cannot compose operations: first operation is too short."); } if (typeof op2 === 'undefined') { throw new Error("Cannot compose operations: first operation is too long."); } if (isRetain(op1) && isRetain(op2)) { if (op1 > op2) { operation.retain(op2); op1 = op1 - op2; op2 = ops2[i2++]; } else if (op1 === op2) { operation.retain(op1); op1 = ops1[i1++]; op2 = ops2[i2++]; } else { operation.retain(op1); op2 = op2 - op1; op1 = ops1[i1++]; } } else if (isInsert(op1) && isDelete(op2)) { if (op1.length > -op2) { op1 = op1.slice(-op2); op2 = ops2[i2++]; } else if (op1.length === -op2) { op1 = ops1[i1++]; op2 = ops2[i2++]; } else { op2 = op2 + op1.length; op1 = ops1[i1++]; } } else if (isInsert(op1) && isRetain(op2)) { if (op1.length > op2) { operation.insert(op1.slice(0, op2)); op1 = op1.slice(op2); op2 = ops2[i2++]; } else if (op1.length === op2) { operation.insert(op1); op1 = ops1[i1++]; op2 = ops2[i2++]; } else { operation.insert(op1); op2 = op2 - op1.length; op1 = ops1[i1++]; } } else if (isRetain(op1) && isDelete(op2)) { if (op1 > -op2) { operation['delete'](op2); op1 = op1 + op2; op2 = ops2[i2++]; } else if (op1 === -op2) { operation['delete'](op2); op1 = ops1[i1++]; op2 = ops2[i2++]; } else { operation['delete'](op1); op2 = op2 + op1; op1 = ops1[i1++]; } } else { throw new Error( "This shouldn't happen: op1: " + JSON.stringify(op1) + ", op2: " + JSON.stringify(op2) ); } } return operation; }; function getSimpleOp (operation, fn) { var ops = operation.ops; var isRetain = TextOperation.isRetain; switch (ops.length) { case 1: return ops[0]; case 2: return isRetain(ops[0]) ? ops[1] : (isRetain(ops[1]) ? ops[0] : null); case 3: if (isRetain(ops[0]) && isRetain(ops[2])) { return ops[1]; } } return null; } function getStartIndex (operation) { if (isRetain(operation.ops[0])) { return operation.ops[0]; } return 0; } // When you use ctrl-z to undo your latest changes, you expect the program not // to undo every single keystroke but to undo your last sentence you wrote at // a stretch or the deletion you did by holding the backspace key down. This // This can be implemented by composing operations on the undo stack. This // method can help decide whether two operations should be composed. It // returns true if the operations are consecutive insert operations or both // operations delete text at the same position. You may want to include other // factors like the time since the last change in your decision. TextOperation.prototype.shouldBeComposedWith = function (other) { if (this.isNoop() || other.isNoop()) { return true; } var startA = getStartIndex(this), startB = getStartIndex(other); var simpleA = getSimpleOp(this), simpleB = getSimpleOp(other); if (!simpleA || !simpleB) { return false; } if (isInsert(simpleA) && isInsert(simpleB)) { return startA + simpleA.length === startB; } if (isDelete(simpleA) && isDelete(simpleB)) { // there are two possibilities to delete: with backspace and with the // delete key. return (startB - simpleB === startA) || startA === startB; } return false; }; // Decides whether two operations should be composed with each other // if they were inverted, that is // `shouldBeComposedWith(a, b) = shouldBeComposedWithInverted(b^{-1}, a^{-1})`. TextOperation.prototype.shouldBeComposedWithInverted = function (other) { if (this.isNoop() || other.isNoop()) { return true; } var startA = getStartIndex(this), startB = getStartIndex(other); var simpleA = getSimpleOp(this), simpleB = getSimpleOp(other); if (!simpleA || !simpleB) { return false; } if (isInsert(simpleA) && isInsert(simpleB)) { return startA + simpleA.length === startB || startA === startB; } if (isDelete(simpleA) && isDelete(simpleB)) { return startB - simpleB === startA; } return false; }; // Transform takes two operations A and B that happened concurrently and // produces two operations A' and B' (in an array) such that // `apply(apply(S, A), B') = apply(apply(S, B), A')`. This function is the // heart of OT. TextOperation.transform = function (operation1, operation2) { if (operation1.baseLength !== operation2.baseLength) { throw new Error("Both operations have to have the same base length"); } var operation1prime = new TextOperation(); var operation2prime = new TextOperation(); var ops1 = operation1.ops, ops2 = operation2.ops; var i1 = 0, i2 = 0; var op1 = ops1[i1++], op2 = ops2[i2++]; while (true) { // At every iteration of the loop, the imaginary cursor that both // operation1 and operation2 have that operates on the input string must // have the same position in the input string. if (typeof op1 === 'undefined' && typeof op2 === 'undefined') { // end condition: both ops1 and ops2 have been processed break; } // next two cases: one or both ops are insert ops // => insert the string in the corresponding prime operation, skip it in // the other one. If both op1 and op2 are insert ops, prefer op1. if (isInsert(op1)) { operation1prime.insert(op1); operation2prime.retain(op1.length); op1 = ops1[i1++]; continue; } if (isInsert(op2)) { operation1prime.retain(op2.length); operation2prime.insert(op2); op2 = ops2[i2++]; continue; } if (typeof op1 === 'undefined') { throw new Error("Cannot compose operations: first operation is too short."); } if (typeof op2 === 'undefined') { throw new Error("Cannot compose operations: first operation is too long."); } var minl; if (isRetain(op1) && isRetain(op2)) { // Simple case: retain/retain if (op1 > op2) { minl = op2; op1 = op1 - op2; op2 = ops2[i2++]; } else if (op1 === op2) { minl = op2; op1 = ops1[i1++]; op2 = ops2[i2++]; } else { minl = op1; op2 = op2 - op1; op1 = ops1[i1++]; } operation1prime.retain(minl); operation2prime.retain(minl); } else if (isDelete(op1) && isDelete(op2)) { // Both operations delete the same string at the same position. We don't // need to produce any operations, we just skip over the delete ops and // handle the case that one operation deletes more than the other. if (-op1 > -op2) { op1 = op1 - op2; op2 = ops2[i2++]; } else if (op1 === op2) { op1 = ops1[i1++]; op2 = ops2[i2++]; } else { op2 = op2 - op1; op1 = ops1[i1++]; } // next two cases: delete/retain and retain/delete } else if (isDelete(op1) && isRetain(op2)) { if (-op1 > op2) { minl = op2; op1 = op1 + op2; op2 = ops2[i2++]; } else if (-op1 === op2) { minl = op2; op1 = ops1[i1++]; op2 = ops2[i2++]; } else { minl = -op1; op2 = op2 + op1; op1 = ops1[i1++]; } operation1prime['delete'](minl); } else if (isRetain(op1) && isDelete(op2)) { if (op1 > -op2) { minl = -op2; op1 = op1 + op2; op2 = ops2[i2++]; } else if (op1 === -op2) { minl = op1; op1 = ops1[i1++]; op2 = ops2[i2++]; } else { minl = op1; op2 = op2 + op1; op1 = ops1[i1++]; } operation2prime['delete'](minl); } else { throw new Error("The two operations aren't compatible"); } } return [operation1prime, operation2prime]; }; return TextOperation; }()); // Export for CommonJS if (typeof module === 'object') { module.exports = ot.TextOperation; } ================================================ FILE: lib/ot/wrapped-operation.js ================================================ if (typeof ot === 'undefined') { // Export for browsers var ot = {}; } ot.WrappedOperation = (function (global) { 'use strict'; // A WrappedOperation contains an operation and corresponing metadata. function WrappedOperation (operation, meta) { this.wrapped = operation; this.meta = meta; } WrappedOperation.prototype.apply = function () { return this.wrapped.apply.apply(this.wrapped, arguments); }; WrappedOperation.prototype.invert = function () { var meta = this.meta; return new WrappedOperation( this.wrapped.invert.apply(this.wrapped, arguments), meta && typeof meta === 'object' && typeof meta.invert === 'function' ? meta.invert.apply(meta, arguments) : meta ); }; // Copy all properties from source to target. function copy (source, target) { for (var key in source) { if (source.hasOwnProperty(key)) { target[key] = source[key]; } } } function composeMeta (a, b) { if (a && typeof a === 'object') { if (typeof a.compose === 'function') { return a.compose(b); } var meta = {}; copy(a, meta); copy(b, meta); return meta; } return b; } WrappedOperation.prototype.compose = function (other) { return new WrappedOperation( this.wrapped.compose(other.wrapped), composeMeta(this.meta, other.meta) ); }; function transformMeta (meta, operation) { if (meta && typeof meta === 'object') { if (typeof meta.transform === 'function') { return meta.transform(operation); } } return meta; } WrappedOperation.transform = function (a, b) { var transform = a.wrapped.constructor.transform; var pair = transform(a.wrapped, b.wrapped); return [ new WrappedOperation(pair[0], transformMeta(a.meta, b.wrapped)), new WrappedOperation(pair[1], transformMeta(b.meta, a.wrapped)) ]; }; return WrappedOperation; }(this)); // Export for CommonJS if (typeof module === 'object') { module.exports = ot.WrappedOperation; } ================================================ FILE: lib/realtime/processQueue.js ================================================ 'use strict' const EventEmitter = require('events').EventEmitter /** * Queuing Class for connection queuing */ const QueueEvent = { Tick: 'Tick', Push: 'Push', Finish: 'Finish' } class ProcessQueue extends EventEmitter { constructor ({ maximumLength = 500, triggerTimeInterval = 5000, // execute on push proactiveMode = true, // execute next work on finish continuousMode = true }) { super() this.max = maximumLength this.triggerTime = triggerTimeInterval this.taskMap = new Map() this.queue = [] this.lock = false this.on(QueueEvent.Tick, this.onEventProcessFunc.bind(this)) if (proactiveMode) { this.on(QueueEvent.Push, this.onEventProcessFunc.bind(this)) } if (continuousMode) { this.on(QueueEvent.Finish, this.onEventProcessFunc.bind(this)) } } onEventProcessFunc () { if (this.lock) return this.lock = true setImmediate(() => { this.process() }) } start () { if (this.eventTrigger) return this.eventTrigger = setInterval(() => { this.emit(QueueEvent.Tick) }, this.triggerTime) } stop () { if (this.eventTrigger) { clearInterval(this.eventTrigger) this.eventTrigger = null } } checkTaskIsInQueue (id) { return this.taskMap.has(id) } /** * pushWithKey a promisify-task to queue * @param id {string} * @param processingFunc {Function} * @returns {boolean} if success return true, otherwise false */ push (id, processingFunc) { if (this.queue.length >= this.max) return false if (this.checkTaskIsInQueue(id)) return false const task = { id: id, processingFunc: processingFunc } this.taskMap.set(id, true) this.queue.push(task) this.start() this.emit(QueueEvent.Push) return true } process () { if (this.queue.length <= 0) { this.stop() this.lock = false return } const task = this.queue.shift() this.taskMap.delete(task.id) const finishTask = () => { this.lock = false setImmediate(() => { this.emit(QueueEvent.Finish) }) } task.processingFunc().then(finishTask).catch(finishTask) } } exports.ProcessQueue = ProcessQueue ================================================ FILE: lib/realtime/realtime.js ================================================ 'use strict' // realtime // external modules const cookie = require('cookie') const cookieParser = require('cookie-parser') const url = require('url') const randomcolor = require('randomcolor') const Chance = require('chance') const chance = new Chance() const moment = require('moment') const get = require('lodash/get') // core const config = require('../config') const logger = require('../logger') const history = require('../history') const models = require('../models') // ot const ot = require('../ot') const { ProcessQueue } = require('./processQueue') const { RealtimeClientConnection } = require('./realtimeClientConnection') const { UpdateDirtyNoteJob } = require('./realtimeUpdateDirtyNoteJob') const { CleanDanglingUserJob } = require('./realtimeCleanDanglingUserJob') const { SaveRevisionJob } = require('./realtimeSaveRevisionJob') // public const realtime = { io: null, onAuthorizeSuccess: onAuthorizeSuccess, onAuthorizeFail: onAuthorizeFail, secure: secure, connection: connection, getStatus: getStatus, isReady: isReady, maintenance: true } const connectProcessQueue = new ProcessQueue({}) const disconnectProcessQueue = new ProcessQueue({}) const updateDirtyNoteJob = new UpdateDirtyNoteJob(realtime) const cleanDanglingUserJob = new CleanDanglingUserJob(realtime) const saveRevisionJob = new SaveRevisionJob(realtime) // TODO: test it function onAuthorizeSuccess (data, accept) { accept() } // TODO: test it function onAuthorizeFail (data, message, error, accept) { accept() // accept whether authorize or not to allow anonymous usage } // TODO: test it // secure the origin by the cookie function secure (socket, next) { try { var handshakeData = socket.request if (handshakeData.headers.cookie) { handshakeData.cookie = cookie.parse(handshakeData.headers.cookie) handshakeData.sessionID = cookieParser.signedCookie(handshakeData.cookie[config.sessionName], config.sessionSecret) if (handshakeData.sessionID && handshakeData.cookie[config.sessionName] && handshakeData.cookie[config.sessionName] !== handshakeData.sessionID) { if (config.debug) { logger.info('AUTH success cookie: ' + handshakeData.sessionID) } return next() } else { next(new Error('AUTH failed: Cookie is invalid.')) } } else { next(new Error('AUTH failed: No cookie transmitted.')) } } catch (ex) { next(new Error('AUTH failed:' + JSON.stringify(ex))) } } // TODO: only use in `updateDirtyNote` // TODO: test it function emitCheck (note) { var out = { title: note.title, updatetime: note.updatetime, lastchangeuser: note.lastchangeuser, lastchangeuserprofile: note.lastchangeuserprofile, authors: note.authors, authorship: note.authorship } realtime.io.to(note.id).emit('check', out) } // actions var users = {} var notes = {} function getNotePool () { return notes } function isNoteExistsInPool (noteId) { return !!notes[noteId] } function addNote (note) { if (exports.isNoteExistsInPool(note.id)) return false notes[note.id] = note return true } function getNotePoolSize () { return Object.keys(notes).length } function deleteNoteFromPool (noteId) { delete notes[noteId] } function deleteAllNoteFromPool () { Object.keys(notes).forEach(noteId => { delete notes[noteId] }) } function getNoteFromNotePool (noteId) { return notes[noteId] } function getUserPool () { return users } function getUserFromUserPool (userId) { return users[userId] } disconnectProcessQueue.start() updateDirtyNoteJob.start() cleanDanglingUserJob.start() saveRevisionJob.start() function disconnectSocketOnNote (note) { note.socks.forEach((sock) => { if (sock) { sock.emit('delete') setImmediate(() => { sock.disconnect(true) }) } }) } function updateNote (note, callback) { _updateNoteAsync(note).then(_note => { callback(null, _note) }).catch((err) => { logger.error(err) return callback(err, null) }) } function findNoteByIdAsync (id) { return models.Note.findOne({ where: { id: id } }) } function updateHistoryForEveryUserCollaborateNote (note) { // update history to every user in this note const tempUsers = Object.assign({}, note.tempUsers) note.tempUsers = {} // update history should async function, but in there return values is not matter Object.keys(tempUsers).forEach(function (key) { exports.updateHistory(key, note, tempUsers[key]) }) } async function getUserProfileByIdAsync (id) { const user = await models.User.findOne({ where: { id: id } }) if (!user) return null return models.User.getProfile(user) } class UserNotFoundException extends Error { constructor () { super('user not found') this.name = this.constructor.name Error.captureStackTrace(this, this.constructor) } } async function getLastChangeUserProfileAsync (currentLastChangeUserId, lastChangeUserIdInDatabase, lastChangeUserProfileInDatabase) { if (!currentLastChangeUserId) return null if (currentLastChangeUserId === lastChangeUserIdInDatabase) return lastChangeUserProfileInDatabase const profile = await getUserProfileByIdAsync(currentLastChangeUserId) if (!profile) { throw new UserNotFoundException() } return profile } function buildNoteUpdateData (note) { const body = note.server.document const title = note.title = models.Note.parseNoteTitle(body) return { title: title, content: body, authorship: note.authorship, lastchangeuserId: note.lastchangeuser, lastchangeAt: Date.now() } } async function _updateNoteAsync (note) { let noteModel = await findNoteByIdAsync(note.id) if (!noteModel) return null updateHistoryForEveryUserCollaborateNote(note) try { note.lastchangeuserprofile = await getLastChangeUserProfileAsync( note.lastchangeuser, noteModel.lastchangeuserId, noteModel.lastchangeuserprofile ) } catch (err) { if (err instanceof UserNotFoundException) { return null } throw err } if (!note || !note.server) return null noteModel = await noteModel.update(buildNoteUpdateData(note)) saveRevisionJob.setSaverSleep(false) return noteModel } // TODO: test it function getStatus () { return models.Note.count() .then(function (notecount) { var distinctaddresses = [] var regaddresses = [] var distinctregaddresses = [] Object.keys(users).forEach(function (key) { var user = users[key] if (!user) return let found = false for (let i = 0; i < distinctaddresses.length; i++) { if (user.address === distinctaddresses[i]) { found = true break } } if (!found) { distinctaddresses.push(user.address) } if (user.login) { regaddresses.push(user.address) let found = false for (let i = 0; i < distinctregaddresses.length; i++) { if (user.address === distinctregaddresses[i]) { found = true break } } if (!found) { distinctregaddresses.push(user.address) } } }) return models.User.count() .then(function (regcount) { return { onlineNotes: Object.keys(notes).length, onlineUsers: Object.keys(users).length, distinctOnlineUsers: distinctaddresses.length, notesCount: notecount, registeredUsers: regcount, onlineRegisteredUsers: regaddresses.length, distinctOnlineRegisteredUsers: distinctregaddresses.length, isConnectionBusy: connectProcessQueue.lock, connectionSocketQueueLength: connectProcessQueue.queue.length, isDisconnectBusy: disconnectProcessQueue.lock, disconnectSocketQueueLength: disconnectProcessQueue.queue.length } }) .catch(function (err) { logger.error('count user failed: ' + err) return Promise.reject(new Error('count user failed: ' + err)) }) }).catch(function (err) { logger.error('count note failed: ' + err) return Promise.reject(new Error('count note failed: ' + err)) }) } // TODO: test it function isReady () { return realtime.io && Object.keys(notes).length === 0 && Object.keys(users).length === 0 && connectProcessQueue.queue.length === 0 && !connectProcessQueue.lock && disconnectProcessQueue.queue.length === 0 && !disconnectProcessQueue.lock } function parseUrl (data) { try { if (url.URL) { return new url.URL(data) } else { // fallback legacy api // eslint-disable-next-line return url.parse(data) } } catch (e) { } return null } function extractNoteIdFromSocket (socket) { function extractNoteIdFromReferer (referer) { if (referer) { const hostUrl = parseUrl(referer) if (!hostUrl) { return false } if (config.urlPath) { return hostUrl.pathname.slice(config.urlPath.length + 1, hostUrl.pathname.length).split('/')[1] } return hostUrl.pathname.split('/')[1] } return false } if (!socket || !socket.handshake) { return false } if (get(socket, 'handshake.query.noteId')) { return decodeURIComponent(socket.handshake.query.noteId) } const referer = get(socket, 'handshake.headers.referer') if (referer) { // this part is only for backward compatibility only; current code // should be using noteId query parameter instead. return extractNoteIdFromReferer(referer) } return false } async function parseNoteIdFromSocketAsync (socket) { const noteId = extractNoteIdFromSocket(socket) if (!noteId) { return null } return new Promise((resolve, reject) => { models.Note.parseNoteId(noteId, function (err, id) { if (err) { reject(err) } if (!id) { resolve(null) } resolve(id) }) }) } // TODO: test it function emitOnlineUsers (socket) { var noteId = socket.noteId if (!noteId || !notes[noteId]) return var users = [] Object.keys(notes[noteId].users).forEach(function (key) { var user = notes[noteId].users[key] if (user) { users.push(buildUserOutData(user)) } }) var out = { users: users } realtime.io.to(noteId).emit('online users', out) } // TODO: test it function emitUserStatus (socket) { var noteId = socket.noteId var user = users[socket.id] if (!noteId || !notes[noteId] || !user) return var out = buildUserOutData(user) socket.broadcast.to(noteId).emit('user status', out) } // TODO: test it function emitRefresh (socket) { var noteId = socket.noteId if (!noteId || !notes[noteId]) return var note = notes[noteId] var out = { title: note.title, docmaxlength: config.documentMaxLength, owner: note.owner, ownerprofile: note.ownerprofile, lastchangeuser: note.lastchangeuser, lastchangeuserprofile: note.lastchangeuserprofile, authors: note.authors, authorship: note.authorship, permission: note.permission, createtime: note.createtime, updatetime: note.updatetime } socket.emit('refresh', out) } function checkViewPermission (req, note) { if (note.permission === 'private') { if (req.user && req.user.logged_in && req.user.id === note.owner) { return true } else { return false } } else if (note.permission === 'limited' || note.permission === 'protected') { if (req.user && req.user.logged_in) { return true } else { return false } } else { return true } } // TODO: test it async function fetchFullNoteAsync (noteId) { return models.Note.findOne({ where: { id: noteId }, include: [{ model: models.User, as: 'owner' }, { model: models.User, as: 'lastchangeuser' }, { model: models.Author, as: 'authors', include: [{ model: models.User, as: 'user' }] }] }) } function buildAuthorProfilesFromNote (noteAuthors) { const authors = {} noteAuthors.forEach((author) => { const profile = models.User.getProfile(author.user) if (profile) { authors[author.userId] = { userid: author.userId, color: author.color, photo: profile.photo, name: profile.name } } }) return authors } function makeNewServerNote (note) { const authors = buildAuthorProfilesFromNote(note.authors) return { id: note.id, alias: note.alias, title: note.title, owner: note.ownerId, ownerprofile: note.owner ? models.User.getProfile(note.owner) : null, permission: note.permission, lastchangeuser: note.lastchangeuserId, lastchangeuserprofile: note.lastchangeuser ? models.User.getProfile(note.lastchangeuser) : null, socks: [], users: {}, tempUsers: {}, createtime: moment(note.createdAt).valueOf(), updatetime: moment(note.lastchangeAt).valueOf(), server: new ot.EditorSocketIOServer(note.content, [], note.id, ifMayEdit, operationCallback), authors: authors, authorship: note.authorship } } // TODO: test it function failConnection (code, err, socket) { logger.error(err) // emit error info socket.emit('info', { code: code }) return socket.disconnect(true) } function queueForDisconnect (socket) { disconnectProcessQueue.push(socket.id, async function () { if (users[socket.id]) { delete users[socket.id] } const noteId = socket.noteId const note = notes[noteId] if (note) { // delete user in users if (note.users[socket.id]) { delete note.users[socket.id] } // remove sockets in the note socks let index do { index = note.socks.indexOf(socket) if (index !== -1) { note.socks.splice(index, 1) } } while (index !== -1) // remove note in notes if no user inside if (Object.keys(note.users).length === 0) { if (note.server.isDirty) { exports.updateNote(note, function (err, _note) { if (err) return logger.error('disconnect note failed: ' + err) // clear server before delete to avoid memory leaks note.server.document = '' note.server.operations = [] delete note.server delete notes[noteId] }) } else { delete note.server delete notes[noteId] } } } exports.emitOnlineUsers(socket) }) } function buildUserOutData (user) { var out = { id: user.id, login: user.login, userid: user.userid, photo: user.photo, color: user.color, cursor: user.cursor, name: user.name, idle: user.idle, type: user.type } return out } // TODO: test it function updateUserData (socket, user) { // retrieve user data from passport if (socket.request.user && socket.request.user.logged_in) { var profile = models.User.getProfile(socket.request.user) user.photo = profile.photo user.name = profile.name user.userid = socket.request.user.id user.login = true } else { user.userid = null user.name = 'Guest ' + chance.animal() user.login = false } } function canEditNote (notePermission, noteOwnerId, currentUserId) { switch (notePermission) { case 'freely': return true case 'editable': case 'limited': // only login user can change return !!currentUserId case 'locked': case 'private': case 'protected': // only owner can change return noteOwnerId === currentUserId } } function ifMayEdit (socket, callback) { const note = getNoteFromNotePool(socket.noteId) if (!note) return const mayEdit = canEditNote(note.permission, note.owner, socket.request.user.id) // if user may edit and this is a text operation if (socket.origin === 'operation' && mayEdit) { // save for the last change user id if (socket.request.user && socket.request.user.logged_in) { note.lastchangeuser = socket.request.user.id } else { note.lastchangeuser = null } } return callback(mayEdit) } // TODO: test it function operationCallback (socket, operation) { var noteId = socket.noteId if (!noteId || !notes[noteId]) return var note = notes[noteId] var userId = null // save authors if (socket.request.user && socket.request.user.logged_in) { var user = users[socket.id] if (!user) return userId = socket.request.user.id if (!note.authors[userId]) { models.Author.findOrCreate({ where: { noteId: noteId, userId: userId }, defaults: { noteId: noteId, userId: userId, color: user.color } }).spread(function (author, created) { if (author) { note.authors[author.userId] = { userid: author.userId, color: author.color, photo: user.photo, name: user.name } } }).catch(function (err) { return logger.error('operation callback failed: ' + err) }) } note.tempUsers[userId] = Date.now() } // save authorship - use timer here because it's an O(n) complexity algorithm setImmediate(function () { note.authorship = models.Note.updateAuthorshipByOperation(operation, userId, note.authorship) }) } // TODO: test it function updateHistory (userId, note, time) { var noteId = note.alias ? note.alias : models.Note.encodeNoteId(note.id) if (note.server) history.updateHistory(userId, noteId, note.server.document, time) } function getUniqueColorPerNote (noteId, maxAttempt = 10) { // random color let color = randomcolor() if (!notes[noteId]) return color const maxrandomcount = maxAttempt let randomAttemp = 0 let found = false do { Object.keys(notes[noteId].users).forEach(userId => { if (notes[noteId].users[userId].color === color) { found = true } }) if (found) { color = randomcolor() randomAttemp++ } } while (found && randomAttemp < maxrandomcount) return color } function queueForConnect (socket) { connectProcessQueue.push(socket.id, async function () { try { const noteId = await exports.parseNoteIdFromSocketAsync(socket) if (!noteId) { return exports.failConnection(404, 'note id not found', socket) } // store noteId in this socket session socket.noteId = noteId // initialize user data // random color var color = getUniqueColorPerNote(noteId) // create user data users[socket.id] = { id: socket.id, address: socket.handshake.headers['x-forwarded-for'] || socket.handshake.address, 'user-agent': socket.handshake.headers['user-agent'], color: color, cursor: null, login: false, userid: null, name: null, idle: false, type: null } exports.updateUserData(socket, users[socket.id]) try { if (!isNoteExistsInPool(noteId)) { const note = await fetchFullNoteAsync(noteId) if (!note) { logger.error('note not found') // emit error info socket.emit('info', { code: 404 }) return socket.disconnect(true) } getNotePool()[noteId] = makeNewServerNote(note) } // if no valid info provided will drop the client if (!socket || !notes[noteId] || !users[socket.id]) { if (notes[noteId]) delete notes[noteId] if (users[socket.id]) delete users[socket.id] return } // check view permission if (!exports.checkViewPermission(socket.request, notes[noteId])) { if (notes[noteId]) delete notes[noteId] if (users[socket.id]) delete users[socket.id] logger.error('connection forbidden') // emit error info socket.emit('info', { code: 403 }) return socket.disconnect(true) } const note = notes[noteId] const user = users[socket.id] // update user color to author color if (note.authors[user.userid]) { user.color = users[socket.id].color = note.authors[user.userid].color } note.users[socket.id] = user note.socks.push(socket) note.server.addClient(socket) note.server.setName(socket, user.name) note.server.setColor(socket, user.color) // update user note history exports.updateHistory(user.userid, note) exports.emitOnlineUsers(socket) exports.emitRefresh(socket) const socketClient = new RealtimeClientConnection(socket) socketClient.registerEventHandler() if (config.debug) { const noteId = socket.noteId logger.info('SERVER connected a client to [' + noteId + ']:') logger.info(JSON.stringify(user)) getStatus().then(function (data) { logger.info(JSON.stringify(data)) }) } } catch (err) { logger.error(err) // emit error info socket.emit('info', { code: 500 }) return socket.disconnect(true) } } catch (err) { return exports.failConnection(500, err, socket) } }) } function connection (socket) { if (realtime.maintenance) return queueForConnect(socket) } // TODO: test it function terminate () { disconnectProcessQueue.stop() connectProcessQueue.stop() updateDirtyNoteJob.stop() } exports = module.exports = realtime exports.extractNoteIdFromSocket = extractNoteIdFromSocket exports.updateNote = updateNote exports.failConnection = failConnection exports.updateUserData = updateUserData exports.emitRefresh = emitRefresh exports.emitUserStatus = emitUserStatus exports.emitOnlineUsers = emitOnlineUsers exports.checkViewPermission = checkViewPermission exports.getUserFromUserPool = getUserFromUserPool exports.buildUserOutData = buildUserOutData exports.emitCheck = emitCheck exports.disconnectSocketOnNote = disconnectSocketOnNote exports.queueForDisconnect = queueForDisconnect exports.terminate = terminate exports.updateHistory = updateHistory exports.ifMayEdit = ifMayEdit exports.parseNoteIdFromSocketAsync = parseNoteIdFromSocketAsync exports.disconnectProcessQueue = disconnectProcessQueue exports.users = users exports.getUserPool = getUserPool exports.notes = notes exports.getNotePool = getNotePool exports.getNotePoolSize = getNotePoolSize exports.isNoteExistsInPool = isNoteExistsInPool exports.addNote = addNote exports.getNoteFromNotePool = getNoteFromNotePool exports.deleteNoteFromPool = deleteNoteFromPool exports.deleteAllNoteFromPool = deleteAllNoteFromPool exports.saveRevisionJob = saveRevisionJob ================================================ FILE: lib/realtime/realtimeCleanDanglingUserJob.js ================================================ 'use strict' const async = require('async') const config = require('../config') const logger = require('../logger') /** * clean when user not in any rooms or user not in connected list */ class CleanDanglingUserJob { constructor (realtime) { this.realtime = realtime } start () { if (this.timer) return this.timer = setInterval(this.cleanDanglingUser.bind(this), 60000) } stop () { if (!this.timer) return clearInterval(this.timer) this.timer = undefined } cleanDanglingUser () { const users = this.realtime.getUserPool() async.each(Object.keys(users), (key, callback) => { const socket = this.realtime.io.sockets.connected[key] if ((!socket && users[key]) || (socket && (!socket.rooms || socket.rooms.length <= 0))) { if (config.debug) { logger.info('cleaner found redundant user: ' + key) } if (!socket) { return callback(null, null) } if (!this.realtime.disconnectProcessQueue.checkTaskIsInQueue(socket.id)) { this.realtime.queueForDisconnect(socket) } } return callback(null, null) }, function (err) { if (err) return logger.error('cleaner error', err) }) } } exports.CleanDanglingUserJob = CleanDanglingUserJob ================================================ FILE: lib/realtime/realtimeClientConnection.js ================================================ 'use strict' const get = require('lodash/get') const config = require('../config') const models = require('../models') const logger = require('../logger') class RealtimeClientConnection { constructor (socket) { this.socket = socket this.realtime = require('./realtime') } registerEventHandler () { // received client refresh request this.socket.on('refresh', this.refreshEventHandler.bind(this)) // received user status this.socket.on('user status', this.userStatusEventHandler.bind(this)) // when a new client disconnect this.socket.on('disconnect', this.disconnectEventHandler.bind(this)) // received cursor focus this.socket.on('cursor focus', this.cursorFocusEventHandler.bind(this)) // received cursor activity this.socket.on('cursor activity', this.cursorActivityEventHandler.bind(this)) // received cursor blur this.socket.on('cursor blur', this.cursorBlurEventHandler.bind(this)) // check version this.socket.on('version', this.checkVersionEventHandler.bind(this)) // received sync of online users request this.socket.on('online users', this.onlineUsersEventHandler.bind(this)) // reveiced when user logout or changed this.socket.on('user changed', this.userChangedEventHandler.bind(this)) // delete a note this.socket.on('delete', this.deleteNoteEventHandler.bind(this)) // received note permission change request this.socket.on('permission', this.permissionChangeEventHandler.bind(this)) } isUserLoggedIn () { return this.socket.request.user && this.socket.request.user.logged_in } isNoteAndUserExists () { const note = this.realtime.getNoteFromNotePool(this.socket.noteId) const user = this.realtime.getUserFromUserPool(this.socket.id) return note && user } isNoteOwner () { const note = this.getCurrentNote() return get(note, 'owner') === this.getCurrentLoggedInUserId() } isAnonymousEnable () { // TODO: move this method to config module return config.allowAnonymous || config.allowAnonymousEdits } getAvailablePermissions () { // TODO: move this method to config module const availablePermission = Object.assign({}, config.permission) if (!config.allowAnonymous && !config.allowAnonymousViews) { delete availablePermission.freely delete availablePermission.editable delete availablePermission.locked } else if (!config.allowAnonymous && !config.allowAnonymousEdits) { delete availablePermission.freely } return availablePermission } getCurrentUser () { if (!this.socket.id) return return this.realtime.getUserFromUserPool(this.socket.id) } getCurrentLoggedInUserId () { return get(this.socket, 'request.user.id') } getCurrentNote () { if (!this.socket.noteId) return return this.realtime.getNoteFromNotePool(this.socket.noteId) } getNoteChannel () { return this.socket.broadcast.to(this.socket.noteId) } async destroyNote (id) { return models.Note.destroy({ where: { id: id } }) } async changeNotePermission (newPermission) { const [changedRows] = await models.Note.update({ permission: newPermission }, { where: { id: this.getCurrentNote().id } }) if (changedRows !== 1) { throw new Error(`updated permission failed, cannot set permission ${newPermission} to note ${this.getCurrentNote().id}`) } } notifyPermissionChanged () { this.realtime.io.to(this.getCurrentNote().id).emit('permission', { permission: this.getCurrentNote().permission }) this.getCurrentNote().socks.forEach((sock) => { if (sock) { if (!this.realtime.checkViewPermission(sock.request, this.getCurrentNote())) { sock.emit('info', { code: 403 }) setTimeout(function () { sock.disconnect(true) }, 0) } } }) } refreshEventHandler () { this.realtime.emitRefresh(this.socket) } checkVersionEventHandler () { this.socket.emit('version', { version: config.fullversion, minimumCompatibleVersion: config.minimumCompatibleVersion }) } userStatusEventHandler (data) { if (!this.isNoteAndUserExists()) return const user = this.getCurrentUser() if (config.debug) { logger.info('SERVER received [' + this.socket.noteId + '] user status from [' + this.socket.id + ']: ' + JSON.stringify(data)) } if (data) { user.idle = data.idle user.type = data.type } this.realtime.emitUserStatus(this.socket) } userChangedEventHandler () { logger.info('user changed') const note = this.getCurrentNote() if (!note) return const user = note.users[this.socket.id] if (!user) return this.realtime.updateUserData(this.socket, user) this.realtime.emitOnlineUsers(this.socket) } onlineUsersEventHandler () { if (!this.isNoteAndUserExists()) return const currentNote = this.getCurrentNote() const currentNoteOnlineUserList = Object.keys(currentNote.users) .map(key => this.realtime.buildUserOutData(currentNote.users[key])) this.socket.emit('online users', { users: currentNoteOnlineUserList }) } cursorFocusEventHandler (data) { if (!this.isNoteAndUserExists()) return const user = this.getCurrentUser() user.cursor = data const out = this.realtime.buildUserOutData(user) this.getNoteChannel().emit('cursor focus', out) } cursorActivityEventHandler (data) { if (!this.isNoteAndUserExists()) return const user = this.getCurrentUser() user.cursor = data const out = this.realtime.buildUserOutData(user) this.getNoteChannel().emit('cursor activity', out) } cursorBlurEventHandler () { if (!this.isNoteAndUserExists()) return const user = this.getCurrentUser() user.cursor = null this.getNoteChannel().emit('cursor blur', { id: this.socket.id }) } deleteNoteEventHandler () { // need login to do more actions if (this.isUserLoggedIn() && this.isNoteAndUserExists()) { const note = this.getCurrentNote() // Only owner can delete note if (note.owner && note.owner === this.getCurrentLoggedInUserId()) { this.destroyNote(note.id) .then((successRows) => { if (!successRows) return this.realtime.disconnectSocketOnNote(note) }) .catch(function (err) { return logger.error('delete note failed: ' + err) }) } } } permissionChangeEventHandler (permission) { if (!this.isUserLoggedIn()) return if (!this.isNoteAndUserExists()) return const note = this.getCurrentNote() // Only owner can change permission if (!this.isNoteOwner()) return if (!(permission in this.getAvailablePermissions())) return this.changeNotePermission(permission) .then(() => { note.permission = permission this.notifyPermissionChanged() }) .catch(err => logger.error('update note permission failed: ' + err)) } disconnectEventHandler () { if (this.realtime.disconnectProcessQueue.checkTaskIsInQueue(this.socket.id)) { return } this.realtime.queueForDisconnect(this.socket) } } exports.RealtimeClientConnection = RealtimeClientConnection ================================================ FILE: lib/realtime/realtimeSaveRevisionJob.js ================================================ 'use strict' const models = require('../models') const logger = require('../logger') /** * clean when user not in any rooms or user not in connected list */ class SaveRevisionJob { constructor (realtime) { this.realtime = realtime this.saverSleep = false } start () { if (this.timer) return this.timer = setInterval(this.saveRevision.bind(this), 5 * 60 * 1000) } stop () { if (!this.timer) return clearInterval(this.timer) this.timer = undefined } saveRevision () { if (this.getSaverSleep()) return models.Revision.saveAllNotesRevision((err, notes) => { if (err) return logger.error('revision saver failed: ' + err) if (notes && notes.length <= 0) { this.setSaverSleep(true) } }) } getSaverSleep () { return this.saverSleep } setSaverSleep (val) { this.saverSleep = val } } exports.SaveRevisionJob = SaveRevisionJob ================================================ FILE: lib/realtime/realtimeUpdateDirtyNoteJob.js ================================================ 'use strict' const config = require('../config') const logger = require('../logger') const moment = require('moment') class UpdateDirtyNoteJob { constructor (realtime) { this.realtime = realtime } start () { if (this.timer) return this.timer = setInterval(this.updateDirtyNotes.bind(this), 1000) } stop () { if (!this.timer) return clearInterval(this.timer) this.timer = undefined } updateDirtyNotes () { const notes = this.realtime.getNotePool() Object.keys(notes).forEach((key) => { const note = notes[key] this.updateDirtyNote(note) .catch((err) => { logger.error('updateDirtyNote: updater error', err) }) }) } async updateDirtyNote (note) { const notes = this.realtime.getNotePool() if (!note.server.isDirty) return if (config.debug) logger.info('updateDirtyNote: updater found dirty note: ' + note.id) note.server.isDirty = false try { const _note = await this.updateNoteAsync(note) // handle when note already been clean up if (!notes[note.id] || !notes[note.id].server) return if (!_note) { this.realtime.io.to(note.id).emit('info', { code: 404 }) logger.error('updateDirtyNote: note not found: ', note.id) this.realtime.disconnectSocketOnNote(note) } note.updatetime = moment(_note.lastchangeAt).valueOf() this.realtime.emitCheck(note) } catch (err) { logger.error('updateDirtyNote: note not found: ', note.id) this.realtime.io.to(note.id).emit('info', { code: 404 }) this.realtime.disconnectSocketOnNote(note) throw err } } updateNoteAsync (note) { return new Promise((resolve, reject) => { this.realtime.updateNote(note, (err, _note) => { if (err) { return reject(err) } return resolve(_note) }) }) } } exports.UpdateDirtyNoteJob = UpdateDirtyNoteJob ================================================ FILE: lib/response.js ================================================ 'use strict' // response // external modules const request = require('request') // core const config = require('./config') const logger = require('./logger') const models = require('./models') const utils = require('./utils') const history = require('./history') // public exports.responseError = responseError exports.errorForbidden = errorForbidden exports.errorNotFound = errorNotFound exports.errorBadRequest = errorBadRequest exports.errorTooLong = errorTooLong exports.errorInternalError = errorInternalError exports.errorServiceUnavailable = errorServiceUnavailable exports.newNote = newNote exports.showPublishSlide = showPublishSlide exports.publishNoteActions = publishNoteActions exports.publishSlideActions = publishSlideActions exports.githubActions = githubActions exports.gitlabActions = gitlabActions exports.checkViewPermission = checkViewPermission exports.newCheckViewPermission = newCheckViewPermission exports.responseCodiMD = responseCodiMD function errorForbidden (req, res) { if (req.user) { responseError(res, '403', 'Forbidden', 'oh no.') } else { var nextURL = new URL('', config.serverURL) nextURL.search = new URLSearchParams({ next: req.originalUrl }) req.flash('error', 'You are not allowed to access this page. Maybe try logging in?') res.redirect(nextURL.toString()) } } function errorNotFound (req, res) { responseError(res, '404', 'Not Found', 'oops.') } function errorBadRequest (req, res) { responseError(res, '400', 'Bad Request', 'something not right.') } function errorTooLong (req, res) { responseError(res, '413', 'Payload Too Large', 'Shorten your note!') } function errorInternalError (req, res) { responseError(res, '500', 'Internal Error', 'wtf.') } function errorServiceUnavailable (req, res) { res.status(503).send('I\'m busy right now, try again later.') } function responseError (res, code, detail, msg) { res.status(code).render('error.ejs', { title: code + ' ' + detail + ' ' + msg, code: code, detail: detail, msg: msg }) } function responseCodiMD (res, note) { var body = note.content var extracted = models.Note.extractMeta(body) var meta = models.Note.parseMeta(extracted.meta) var title = models.Note.decodeTitle(note.title) title = models.Note.generateWebTitle(meta.title || title) res.set({ 'Cache-Control': 'private', // only cache by client 'X-Robots-Tag': 'noindex, nofollow' // prevent crawling }) res.render('codimd.ejs', { title: title }) } function updateHistory (userId, note, document, time) { var noteId = note.alias ? note.alias : models.Note.encodeNoteId(note.id) history.updateHistory(userId, noteId, document, time) logger.info('history updated') } function newNote (req, res, next) { var owner = null var body = '' if (req.body && req.body.length > config.documentMaxLength) { return errorTooLong(req, res) } else if (req.body) { body = req.body } body = body.replace(/[\r]/g, '') if (req.isAuthenticated()) { owner = req.user.id } else if (!config.allowAnonymous) { return errorForbidden(req, res) } models.Note.create({ ownerId: owner, alias: req.alias ? req.alias : null, content: body }).then(function (note) { if (req.isAuthenticated()) { updateHistory(owner, note, body) } return res.redirect(config.serverURL + '/' + models.Note.encodeNoteId(note.id)) }).catch(function (err) { logger.error(err) return errorInternalError(req, res) }) } function newCheckViewPermission (note, isLogin, userId) { if (note.permission === 'private') { return note.ownerId === userId } if (note.permission === 'limited' || note.permission === 'protected') { return isLogin } return true } function checkViewPermission (req, note) { if (note.permission === 'private') { if (!req.isAuthenticated() || note.ownerId !== req.user.id) { return false } else { return true } } else if (note.permission === 'limited' || note.permission === 'protected') { if (!req.isAuthenticated()) { return false } else { return true } } else { return true } } function findNote (req, res, callback, include) { var noteId = req.params.noteId var id = req.params.noteId || req.params.shortid models.Note.parseNoteId(id, function (err, _id) { if (err) { logger.error(err) return errorInternalError(req, res) } models.Note.findOne({ where: { id: _id }, include: include || null }).then(function (note) { if (!note) { if (config.allowFreeURL && noteId && !config.forbiddenNoteIDs.includes(noteId)) { req.alias = noteId return newNote(req, res) } else { return errorNotFound(req, res) } } if (!checkViewPermission(req, note)) { return errorForbidden(req, res) } else { return callback(note) } }).catch(function (err) { logger.error(err) return errorInternalError(req, res) }) }) } function actionDownload (req, res, note) { var body = note.content var title = models.Note.decodeTitle(note.title) var filename = title filename = encodeURIComponent(filename) res.set({ 'Access-Control-Allow-Origin': '*', // allow CORS as API 'Access-Control-Allow-Headers': 'Range', 'Access-Control-Expose-Headers': 'Cache-Control, Content-Encoding, Content-Range', 'Content-Type': 'text/markdown; charset=UTF-8', 'Cache-Control': 'private', 'Content-disposition': 'attachment; filename=' + filename + '.md', 'X-Robots-Tag': 'noindex, nofollow' // prevent crawling }) res.send(body) } function publishNoteActions (req, res, next) { findNote(req, res, function (note) { var action = req.params.action switch (action) { case 'download': actionDownload(req, res, note) break case 'edit': res.redirect(config.serverURL + '/' + (note.alias ? note.alias : models.Note.encodeNoteId(note.id))) break default: res.redirect(config.serverURL + '/s/' + note.shortid) break } }) } function publishSlideActions (req, res, next) { findNote(req, res, function (note) { var action = req.params.action switch (action) { case 'edit': res.redirect(config.serverURL + '/' + (note.alias ? note.alias : models.Note.encodeNoteId(note.id))) break default: res.redirect(config.serverURL + '/p/' + note.shortid) break } }) } function githubActions (req, res, next) { var noteId = req.params.noteId findNote(req, res, function (note) { var action = req.params.action switch (action) { case 'gist': githubActionGist(req, res, note) break default: res.redirect(config.serverURL + '/' + noteId) break } }) } function githubActionGist (req, res, note) { var code = req.query.code var state = req.query.state if (!code || !state) { return errorForbidden(req, res) } else { var data = { client_id: config.github.clientID, client_secret: config.github.clientSecret, code: code, state: state } var authUrl = 'https://github.com/login/oauth/access_token' request({ url: authUrl, method: 'POST', json: data }, function (error, httpResponse, body) { if (!error && httpResponse.statusCode === 200) { var accessToken = body.access_token if (accessToken) { var content = note.content var title = models.Note.decodeTitle(note.title) var filename = title.replace('/', ' ') + '.md' var gist = { files: {} } gist.files[filename] = { content: content } var gistUrl = 'https://api.github.com/gists' request({ url: gistUrl, headers: { 'User-Agent': 'CodiMD', Authorization: 'token ' + accessToken }, method: 'POST', json: gist }, function (error, httpResponse, body) { if (!error && httpResponse.statusCode === 201) { res.setHeader('referer', '') res.redirect(body.html_url) } else { return errorForbidden(req, res) } }) } else { return errorForbidden(req, res) } } else { return errorForbidden(req, res) } }) } } function gitlabActions (req, res, next) { var noteId = req.params.noteId findNote(req, res, function (note) { var action = req.params.action switch (action) { case 'projects': gitlabActionProjects(req, res, note) break default: res.redirect(config.serverURL + '/' + noteId) break } }) } function gitlabActionProjects (req, res, note) { if (req.isAuthenticated()) { models.User.findOne({ where: { id: req.user.id } }).then(function (user) { if (!user) { return errorNotFound(req, res) } var ret = { baseURL: config.gitlab.baseURL, version: config.gitlab.version } ret.accesstoken = user.accessToken ret.profileid = user.profileid request( config.gitlab.baseURL + '/api/' + config.gitlab.version + '/projects?membership=yes&per_page=100&access_token=' + user.accessToken, function (error, httpResponse, body) { if (!error && httpResponse.statusCode === 200) { ret.projects = JSON.parse(body) return res.send(ret) } else { return res.send(ret) } } ) }).catch(function (err) { logger.error('gitlab action projects failed: ' + err) return errorInternalError(req, res) }) } else { return errorForbidden(req, res) } } function showPublishSlide (req, res, next) { var include = [{ model: models.User, as: 'owner' }, { model: models.User, as: 'lastchangeuser' }] findNote(req, res, function (note) { // force to use short id var shortid = req.params.shortid if ((note.alias && shortid !== note.alias) || (!note.alias && shortid !== note.shortid)) { return res.redirect(config.serverURL + '/p/' + (note.alias || note.shortid)) } note.increment('viewcount').then(function (note) { if (!note) { return errorNotFound(req, res) } var body = note.content var extracted = models.Note.extractMeta(body) var markdown = extracted.markdown var meta = models.Note.parseMeta(extracted.meta) var createtime = note.createdAt var updatetime = note.lastchangeAt var title = models.Note.decodeTitle(note.title) title = models.Note.generateWebTitle(meta.title || title) var data = { title: title, description: meta.description || (markdown ? models.Note.generateDescription(markdown) : null), viewcount: note.viewcount, createtime: createtime, updatetime: updatetime, body: markdown, theme: meta.slideOptions && utils.isRevealTheme(meta.slideOptions.theme), meta: JSON.stringify(extracted.meta), owner: note.owner ? note.owner.id : null, ownerprofile: note.owner ? models.User.getProfile(note.owner) : null, lastchangeuser: note.lastchangeuser ? note.lastchangeuser.id : null, lastchangeuserprofile: note.lastchangeuser ? models.User.getProfile(note.lastchangeuser) : null, robots: meta.robots || false, // default allow robots GA: meta.GA, disqus: meta.disqus, cspNonce: res.locals.nonce } res.set({ 'Cache-Control': 'private' // only cache by client }) res.render('slide.ejs', data) }).catch(function (err) { logger.error(err) return errorInternalError(req, res) }) }, include) } ================================================ FILE: lib/routes.js ================================================ 'use strict' const { Router } = require('express') const { wrap, urlencodedParser, markdownParser } = require('./utils') // load controller const indexController = require('./homepage') const errorPageController = require('./errorPage') const statusController = require('./status') const historyController = require('./history') const userController = require('./user') const noteController = require('./note') const response = require('./response') const bodyParser = require('body-parser') const appRouter = Router() // register route const csurf = require('csurf') const csurfMiddleware = csurf({ cookie: true }) // get index appRouter.get('/', csurfMiddleware, wrap(indexController.showIndex)) // ----- error page ----- // get 403 forbidden appRouter.get('/403', errorPageController.errorForbidden) // get 404 not found appRouter.get('/404', errorPageController.errorNotFound) // get 500 internal error appRouter.get('/500', errorPageController.errorInternalError) appRouter.get('/config', statusController.getConfig) // register auth module appRouter.use(require('./auth')) // get history appRouter.get('/history', historyController.historyGet) // post history appRouter.post('/history', urlencodedParser, historyController.historyPost) // post history by note id appRouter.post('/history/:noteId', urlencodedParser, historyController.historyPost) // delete history appRouter.delete('/history', historyController.historyDelete) // delete history by note id appRouter.delete('/history/:noteId', historyController.historyDelete) // user // get me info appRouter.get('/me', wrap(userController.getMe)) // delete the currently authenticated user appRouter.get('/me/delete/:token?', wrap(userController.deleteUser)) // export the data of the authenticated user appRouter.post('/me/export', urlencodedParser, csurfMiddleware, userController.exportMyData) appRouter.get('/user/:username/avatar.svg', userController.getMyAvatar) // register image upload module appRouter.use(require('./imageRouter')) // get new note appRouter.get('/new', response.newNote) // post new note with content appRouter.post('/new', markdownParser, response.newNote) // get publish note appRouter.get('/s/:shortid', noteController.showPublishNote) // publish note actions appRouter.get('/s/:shortid/:action', response.publishNoteActions) // get publish slide appRouter.get('/p/:shortid', response.showPublishSlide) // publish slide actions appRouter.get('/p/:shortid/:action', response.publishSlideActions) // gey my note list appRouter.get('/api/notes/myNotes', noteController.listMyNotes) // delete note by id appRouter.delete('/api/notes/:noteId', noteController.deleteNote) // update note content by id appRouter.put('/api/notes/:noteId', bodyParser.json(), noteController.updateNote) // get note by id appRouter.get('/:noteId', wrap(noteController.showNote)) // note actions appRouter.get('/:noteId/:action', noteController.noteActions) // note actions with action id appRouter.get('/:noteId/:action/:actionId', noteController.noteActions) exports.router = appRouter ================================================ FILE: lib/status/index.js ================================================ 'use strict' const realtime = require('../realtime/realtime') const config = require('../config') exports.getStatus = async (req, res) => { res.set({ 'Cache-Control': 'private', // only cache by client 'X-Robots-Tag': 'noindex, nofollow', // prevent crawling 'Content-Type': 'application/json' }) try { const data = await realtime.getStatus() res.send(data) } catch (e) { console.error(e) res.status(500).send(e.toString()) } } exports.getMetrics = async (req, res) => { const data = await realtime.getStatus() res.set({ 'Cache-Control': 'private', // only cache by client 'X-Robots-Tag': 'noindex, nofollow', // prevent crawling 'Content-Type': 'text/plain; charset=utf-8' }) res.render('../js/lib/common/metrics.ejs', data) } exports.getConfig = (req, res) => { const data = { domain: config.domain, urlpath: config.urlPath, debug: config.debug, version: config.fullversion, plantumlServer: config.plantuml.server, DROPBOX_APP_KEY: config.dropbox.appKey, allowedUploadMimeTypes: config.allowedUploadMimeTypes, defaultUseHardbreak: config.defaultUseHardbreak, linkifyHeaderStyle: config.linkifyHeaderStyle, useCDN: config.useCDN, defaultTocDepth: config.defaultTocDepth } res.set({ 'Cache-Control': 'private', // only cache by client 'X-Robots-Tag': 'noindex, nofollow', // prevent crawling 'Content-Type': 'application/javascript' }) res.render('../js/lib/common/constant.ejs', data) } ================================================ FILE: lib/user/index.js ================================================ 'use strict' const archiver = require('archiver') const async = require('async') const response = require('../response') const config = require('../config') const models = require('../models') const logger = require('../logger') const { generateAvatar } = require('../letter-avatars') exports.getMe = async (req, res) => { if (!req.isAuthenticated()) { return res.status(401).send({ status: 'forbidden' }) } const user = await models.User.findOne({ where: { id: req.user.id } }) if (!user) { return response.errorNotFound(req, res) } const profile = models.User.getProfile(user) res.send({ status: 'ok', id: req.user.id, name: profile.name, photo: profile.photo }) } exports.deleteUser = async (req, res) => { if (!req.isAuthenticated()) { return response.errorForbidden(req, res) } const user = await models.User.findOne({ where: { id: req.user.id } }) if (!user) { return response.errorNotFound(req, res) } if (user.deleteToken !== req.params.token) { return response.errorForbidden(req, res) } await user.destroy() return res.redirect(config.serverURL + '/') } exports.exportMyData = (req, res) => { if (!req.isAuthenticated()) { return response.errorForbidden(req, res) } const archive = archiver('zip', { zlib: { level: 3 } // Sets the compression level. }) res.setHeader('Content-Type', 'application/zip') res.attachment('archive.zip') archive.pipe(res) archive.on('error', function (err) { logger.error('export user data failed: ' + err) return response.errorInternalError(req, res) }) models.User.findOne({ where: { id: req.user.id } }).then(function (user) { models.Note.findAll({ where: { ownerId: user.id } }).then(function (notes) { const filenames = {} async.each(notes, function (note, callback) { const basename = note.title.replace(/\//g, '-') // Prevent subdirectories let filename let suffix = 0 do { if (suffix === 0) { filename = basename + '.md' } else { filename = basename + '-' + suffix + '.md' } suffix++ } while (filenames[filename]) filenames[filename] = true logger.debug('Write: ' + filename) archive.append(note.content, { name: filename, date: note.lastchangeAt }) callback(null, null) }, function (err) { if (err) { return response.errorInternalError(req, res) } archive.finalize() }) }) }).catch(function (err) { logger.error('export user data failed: ' + err) return response.errorInternalError(req, res) }) } exports.getMyAvatar = (req, res) => { res.setHeader('Content-Type', 'image/svg+xml') res.setHeader('Cache-Control', 'public, max-age=86400') res.send(generateAvatar(req.params.username)) } ================================================ FILE: lib/utils.js ================================================ 'use strict' const fs = require('fs') const path = require('path') const bodyParser = require('body-parser') const mime = require('mime-types') exports.isSQLite = function isSQLite (sequelize) { return sequelize.options.dialect === 'sqlite' } exports.getImageMimeType = function getImageMimeType (imagePath) { return mime.lookup(path.extname(imagePath)) } exports.isRevealTheme = function isRevealTheme (theme) { if (fs.existsSync(path.join(__dirname, '..', 'public', 'build', 'reveal.js', 'css', 'theme', theme + '.css'))) { return theme } return undefined } exports.wrap = innerHandler => (req, res, next) => innerHandler(req, res).catch(err => next(err)) // create application/x-www-form-urlencoded parser exports.urlencodedParser = bodyParser.urlencoded({ extended: false, limit: 1024 * 1024 * 10 // 10 mb }) // create text/markdown parser exports.markdownParser = bodyParser.text({ inflate: true, type: ['text/plain', 'text/markdown'], limit: 1024 * 1024 * 10 // 10 mb }) ================================================ FILE: lib/web/middleware/checkVersion.js ================================================ 'use strict' const { promisify } = require('util') const request = require('request') const logger = require('../../logger') const config = require('../../config') let lastCheckAt const VERSION_CHECK_ENDPOINT = 'https://evangelion.codimd.dev/' const CHECK_TIMEOUT = 1000 * 60 * 60 * 24 // 1 day const rp = promisify(request) exports.checkVersion = checkVersion /** * @param {Express.Application|Express.Request} ctx */ async function checkVersion (ctx) { if (lastCheckAt && (lastCheckAt + CHECK_TIMEOUT > Date.now())) { return } // update lastCheckAt whether the check would fail or not lastCheckAt = Date.now() try { const { statusCode, body: data } = await rp({ url: `${VERSION_CHECK_ENDPOINT}?v=${config.version}`, method: 'GET', json: true, timeout: 3000 }) if (statusCode !== 200 || data.status === 'error') { logger.warn('Version check failed.') return } const locals = ctx.locals ? ctx.locals : ctx.app.locals locals.versionInfo.latest = data.latest locals.versionInfo.versionItem = data.latest ? null : data.versionItem if (!data.latest) { const { version, link } = data.versionItem logger.info(`Your CodiMD version is out of date! The latest version is ${version}. Please see what's new on ${link}.`) } } catch (err) { // ignore and skip version check logger.warn('Version check failed.') logger.warn(err) } } exports.versionCheckMiddleware = function (req, res, next) { checkVersion(req) .then(() => { next() }) .catch((err) => { logger.error(err) next() }) } ================================================ FILE: lib/workers/dmpWorker.js ================================================ 'use strict' // external modules var DiffMatchPatch = require('@hackmd/diff-match-patch') var dmp = new DiffMatchPatch() // core var config = require('../config') var logger = require('../logger') process.on('message', function (data) { if (!data || !data.msg || !data.cacheKey) { return logger.error('dmp worker error: not enough data') } switch (data.msg) { case 'create patch': if (!Object.hasOwnProperty.call(data, 'lastDoc') || !Object.hasOwnProperty.call(data, 'currDoc')) { return logger.error('dmp worker error: not enough data on create patch') } try { var patch = createPatch(data.lastDoc, data.currDoc) process.send({ msg: 'check', result: patch, cacheKey: data.cacheKey }) } catch (err) { logger.error('dmp worker error', err) process.send({ msg: 'error', error: err, cacheKey: data.cacheKey }) } break case 'get revision': if (!Object.hasOwnProperty.call(data, 'revisions') || !Object.hasOwnProperty.call(data, 'count')) { return logger.error('dmp worker error: not enough data on get revision') } try { var result = getRevision(data.revisions, data.count) process.send({ msg: 'check', result: result, cacheKey: data.cacheKey }) } catch (err) { logger.error('dmp worker error', err) process.send({ msg: 'error', error: err, cacheKey: data.cacheKey }) } break } }) function createPatch (lastDoc, currDoc) { var msStart = (new Date()).getTime() var diff = dmp.diff_main(lastDoc, currDoc) var patch = dmp.patch_make(lastDoc, diff) patch = dmp.patch_toText(patch) var msEnd = (new Date()).getTime() if (config.debug) { logger.info(patch) logger.info((msEnd - msStart) + 'ms') } return patch } function getRevision (revisions, count) { var msStart = (new Date()).getTime() var startContent = null var lastPatch = [] var applyPatches = [] var authorship = [] if (count <= Math.round(revisions.length / 2)) { // start from top to target for (let i = 0; i < count; i++) { const revision = revisions[i] if (i === 0) { startContent = revision.content || revision.lastContent } if (i !== count - 1) { const patch = dmp.patch_fromText(revision.patch) applyPatches = applyPatches.concat(patch) } lastPatch = revision.patch authorship = revision.authorship } // swap DIFF_INSERT and DIFF_DELETE to achieve unpatching for (let i = 0, l = applyPatches.length; i < l; i++) { for (let j = 0, m = applyPatches[i].diffs.length; j < m; j++) { var diff = applyPatches[i].diffs[j] if (diff[0] === DiffMatchPatch.DIFF_INSERT) { diff[0] = DiffMatchPatch.DIFF_DELETE } else if (diff[0] === DiffMatchPatch.DIFF_DELETE) { diff[0] = DiffMatchPatch.DIFF_INSERT } } } } else { // start from bottom to target var l = revisions.length - 1 for (var i = l; i >= count - 1; i--) { const revision = revisions[i] if (i === l) { startContent = revision.lastContent authorship = revision.authorship } if (revision.patch) { const patch = dmp.patch_fromText(revision.patch) applyPatches = applyPatches.concat(patch) } lastPatch = revision.patch authorship = revision.authorship } } try { var finalContent = dmp.patch_apply(applyPatches, startContent)[0] } catch (err) { throw new Error(err) } var data = { content: finalContent, patch: dmp.patch_fromText(lastPatch), authorship: authorship } var msEnd = (new Date()).getTime() if (config.debug) { logger.info((msEnd - msStart) + 'ms') } return data } // log uncaught exception process.on('uncaughtException', function (err) { logger.error('An uncaught exception has occured.') logger.error(err) logger.error('Process will exit now.') process.exit(1) }) ================================================ FILE: locales/ca.json ================================================ { "Collaborative markdown notes": "Notes col·laboratives a Markdown", "Realtime collaborative markdown notes on all platforms.": "Notes col·laboratives a Markdown per a totes les plataformes.", "Best way to write and share your knowledge in markdown.": "La millor forma d'escriure i compartir el teu coneixement a Markdown.", "Intro": "Introducció", "History": "Història", "New guest note": "Nova nota com a convidat", "Collaborate with URL": "Col·laborar a través de URL", "Support charts and MathJax": "Soport per a gràfics i MathJax", "Support slide mode": "Soport per a diapositives", "Sign In": "Entrar", "Below is the history from browser": "A continuació es mostra l'historial del navegador", "Welcome!": "Benvingut!", "New note": "Nova nota", "or": "o", "Sign Out": "Sortir", "Explore all features": "Explorar totes les funcions", "Select tags...": "Seleccionar etiquetes...", "Search keyword...": "Buscar paraules clau...", "Sort by title": "Ordenar per títol", "Title": "Títol", "Sort by time": "Ordenar per hora", "Time": "Tiempo", "Export history": "Exportar historial", "Import history": "Importar historial", "Clear history": "Borrar historial", "Refresh history": "Actualitzar historial", "No history": "Cap historial", "Import from browser": "Importar del navegador", "Releases": "Versions", "Are you sure?": "Estas segur?", "Cancel": "Cancel·lar", "Yes, do it!": "Si, fes-ho!", "Choose method": "Triar mètode", "Sign in via %s": "Entrar a través de %s", "New": "Nou", "Publish": "Publicar", "Extra": "Extra", "Revision": "Revisió", "Slide Mode": "Mode presentació", "Export": "Exportar", "Import": "Importar", "Clipboard": "Portapapers", "Download": "Descarregar", "Raw HTML": "HTML pur", "Edit": "Editar", "View": "Veure", "Both": "Ambdós", "Help": "Ajuda", "Upload Image": "Pujar imatge", "Menu": "Menú", "This page need refresh": "Aquesta pàgina necessita ser refrescada", "You have an incompatible client version.": "Tens una versió del client incompatible.", "Refresh to update.": "Refrescar per actualitzar", "New version available!": "Nova versió disponible!", "See releases notes here": "Veure les notes de publicació aquí", "Refresh to enjoy new features.": "Actualitzar per fer servir les noves funcions.", "Your user state has changed.": "L'estat del teu usuari ha canviat.", "Refresh to load new user state.": "Refrescar per actualitzar l'estat del teu usuari.", "Refresh": "Refrescar", "Contacts": "Contactes", "Report an issue": "Reportar un problema", "Send us email": "Enviar-nos un email", "Documents": "Documents", "Features": "Funcions", "YAML Metadata": "Metadades de YAML", "Slide Example": "Exemple de diapositiva", "Cheatsheet": "Ajudamemories", "Example": "Exemple", "Syntax": "Sintaxis", "Header": "Capçelera", "Unordered List": "Llista desordenada", "Ordered List": "Llista ordenada", "Todo List": "Llista de tasques", "Blockquote": "Bloc de cita", "Bold font": "Font negreta", "Italics font": "Font itàlica", "Strikethrough": "Ratllat", "Inserted text": "Text subrallat", "Marked text": "Text marcat", "Link": "Enllaç", "Image": "Imatge", "Code": "Codi", "Externals": "Externs", "This is a alert area.": "Això és una àrea d'alerta.", "Revert": "Revertir", "Import from clipboard": "Importar del portapapers", "Paste your markdown or webpage here...": "Enganxa la teva markdown o pàgina web aquí...", "Clear": "Netejar", "This note is locked": "Aquesta nota està bloquejada", "Sorry, only owner can edit this note.": "Perdona, només l'amo pot editar aquesta nota.", "OK": "OK", "Reach the limit": "Ha arribat al límit", "Sorry, you've reached the max length this note can be.": "Perdona, ha arribat a la longitut màxima que pot tenir aquesta nota.", "Please reduce the content or divide it to more notes, thank you!": "Siusplau, redueix el contingut o divideix-la en més notes, gràcies!", "Import from Gist": "Importar d'un Gist", "Paste your gist url here...": "Enganxa l'URL del teu Gist aquí...", "Import from Snippet": "Importar d'Snippet", "Select From Available Projects": "Triar d'un projecte disponsible", "Select From Available Snippets": "Triar d'un Snippet disponible", "OR": "O", "Export to Snippet": "Exportar a Snippet", "Select Visibility Level": "Triar el nivell de visibilitat" } ================================================ FILE: locales/da.json ================================================ { "Collaborative markdown notes": "Kollaborative markdown-noter", "Realtime collaborative markdown notes on all platforms.": "Kollaborative markdown-noter i realtid på alle platforme.", "Best way to write and share your knowledge in markdown.": "Bedste måde at skrive og dele din viden i markdown.", "Intro": "Intro", "History": "Historik", "New guest note": "Ny gæstenote", "Collaborate with URL": "Samarbejd via URL", "Support charts and MathJax": "Mulighed for diagrammer og MathJax", "Support slide mode": "Mulighed for præsentationer", "Sign In": "Log Ind", "Below is the history from browser": "Forneden findes historikken fra browseren", "Welcome!": "Velkommen!", "New note": "Ny note", "or": "eller", "Sign Out": " Log Ud", "Explore all features": "Udforsk alle features", "Select tags...": "Vælg tags...", "Search keyword...": "Søg nøgleord...", "Sort by title": "Sortér titler", "Title": "Titel", "Sort by time": "Sortér kronologisk", "Time": "Tid", "Export history": "Eksportér historik", "Import history": "Importér historik", "Clear history": "Ryd hsitorik", "Refresh history": "Genindlæs historik", "No history": "Ingen historik", "Import from browser": "Importér fra browser", "Releases": "Releases", "Are you sure?": "Er du sikker?", "Cancel": "Afbryd", "Yes, do it!": "Ja, gør det!", "Choose method": "Vælg metode", "Sign in via %s": "Log ind med %s", "New": "Ny", "Publish": "Publicér", "Extra": "Ekstra", "Revision": "Revision", "Slide Mode": "Præsentationstilstand", "Export": "Eksportér", "Import": "Importér", "Clipboard": "Udklipsholder", "Download": "Download", "Raw HTML": "Rå HTML", "Edit": "Redigér", "View": "Vis", "Both": "Begge", "Help": "Hjælp", "Upload Image": "Upload billede", "Menu": "Menu", "This page need refresh": "Denne side skal genindlæses", "You have an incompatible client version.": "Din klientversion er inkompatibel.", "Refresh to update.": "Genindlæs for at opdatere.", "New version available!": "Ny version tilgængelig!", "See releases notes here": "Se release notes her", "Refresh to enjoy new features.": "Genindlæs for at anvende ny funktionalitet.", "Your user state has changed.": "Din brugerstatus er ændret.", "Refresh to load new user state.": "Genindlæs for at anvende ny brugerstatus.", "Refresh": "Genindlæs", "Contacts": "Kontakter", "Report an issue": "Rapportér en fejl", "Send us email": "Send os en email", "Documents": "Dokumenter", "Features": "Funktionalitet", "YAML Metadata": "YAML Metadata", "Slide Example": "Præsentationseksempel", "Cheatsheet": "Snydeark", "Example": "Eksempel", "Syntax": "Syntaks", "Header": "Overskrift", "Unordered List": "Uordnet Liste", "Ordered List": "Ordnet Liste", "Todo List": "Tjekliste", "Blockquote": "Blokcitat", "Bold font": "Fed skrift", "Italics font": "Kursiv skrift", "Strikethrough": "Gennemstregning", "Inserted text": "Indsat tekst", "Marked text": "Markeret tekst", "Link": "Link", "Image": "Billede", "Code": "Kode", "Externals": "Eksterne", "This is a alert area.": "Dette er et alarmområde.", "Revert": "Fortryd ændringer", "Import from clipboard": "Importér fra udklipsholder", "Paste your markdown or webpage here...": "Indsæt din markdown eller hjemmeside her...", "Clear": "Ryd", "This note is locked": "Denne note er låst", "Sorry, only owner can edit this note.": "Beklager, men kun ejeren kan redigere denne note.", "OK": "Okay", "Reach the limit": "Nå grænsen", "Sorry, you've reached the max length this note can be.": "Beklager, du har nået grænsen for den maksimale længde denne note må være.", "Please reduce the content or divide it to more notes, thank you!": "Vær venlig at begrænse indholdets mængde eller opdel det i flere noter, tak!", "Import from Gist": "Importér fra Gist", "Paste your gist url here...": "Indsæt din gist-url her...", "Import from Snippet": "Importér fra Snippet", "Select From Available Projects": "Vælg fra tilgængelige projekter", "Select From Available Snippets": "Vælg fra tilgængelige Snippets", "OR": "ELLER", "Export to Snippet": "Eksportér til Snippet", "Select Visibility Level": "Vælg synlighedsniveau" } ================================================ FILE: locales/de.json ================================================ { "Collaborative markdown notes": "Gemeinsame Markdown Notizen", "Realtime collaborative markdown notes on all platforms.": "Gemeinsame Notizen in Echtzeit auf allen Plattformen.", "Best way to write and share your knowledge in markdown.": "Der beste Weg, sein Wissen in Markdown zu schreiben und zu teilen.", "Intro": "Intro", "History": "Verlauf", "New guest note": "Neue Gastnotiz", "Collaborate with URL": "Zusammenarbeiten über URL", "Support charts and MathJax": "Unterstützt charts und Mathjax", "Support slide mode": "Unterstützt Präsentationsmodus", "Sign In": "Anmelden", "Below is the history from browser": "Im Folgenden finden Sie den Browserverlauf", "Welcome!": "Willkommen!", "New note": "Neue Notiz", "or": "oder", "Sign Out": "Ausloggen", "Explore all features": "Entdecke alle Funktionen", "Select tags...": "Stichwort auswählen ...", "Search keyword...": "Suche nach Stichwort ...", "Sort by title": "Nach Titel sortieren", "Title": "Titel", "Sort by time": "Nach Zeit sortieren", "Time": "Zeit", "Export history": "Verlauf exportieren", "Import history": "Verlauf importieren", "Clear history": "Verlauf löschen", "Refresh history": "Verlauf aktualisieren", "No history": "Kein Verlauf", "Import from browser": "Vom Browser importieren", "Releases": "Versionen", "Are you sure?": "Sind sie sicher?", "Do you really want to delete this note?": "Möchten Sie diese Notiz wirklich löschen?", "All users will lose their connection.": "Alle Benutzer werden ihre Verbindung verlieren.", "Cancel": "Abbrechen", "Yes, do it!": "Ja, mach es!", "Choose method": "Methode wählen", "Sign in via %s": "Einloggen über %s", "New": "Neu", "Publish": "Veröffentlichen", "Extra": "Extra", "Revision": "Version", "Slide Mode": "Präsentationsmodus", "Export": "Exportieren", "Import": "Importieren", "Clipboard": "Zwischenablage", "Download": "Download", "Raw HTML": "Reines HTML", "Edit": "Bearbeiten", "View": "Anzeigen", "Both": "Beides", "Help": "Hilfe", "Upload Image": "Foto hochladen", "Menu": "Menü", "This page need refresh": "Bitte laden Sie die Seite neu", "You have an incompatible client version.": "Ihre Client Version ist nicht kompatibel", "Refresh to update.": "Neu laden zum Updaten.", "New version available!": "Neue Version verfügbar!", "See releases notes here": "Versionshinweise hier ansehen", "Refresh to enjoy new features.": "Neu laden für neue Funktionen.", "Your user state has changed.": "Ihr Nutzerstatus hat sich geändert.", "Refresh to load new user state.": "Neu laden für neuen Nutzerstatus.", "Refresh": "Neu laden", "Contacts": "Kontakte", "Report an issue": "Fehlerbericht senden", "Meet us on %s": "Triff uns auf %s", "Send us email": "Sende uns eine Email", "Documents": "Dokumente", "Features": "Funktionen", "YAML Metadata": "YAML Metadaten", "Slide Example": "Beispiel Präsentation", "Cheatsheet": "Cheatsheet", "Example": "Beispiel", "Syntax": "Syntax", "Header": "Überschrift", "Unordered List": "Ungeordnete Liste", "Ordered List": "Geordnete Liste", "Todo List": "To-Do Liste", "Blockquote": "Zitat", "Bold font": "Fett", "Italics font": "Kursiv", "Strikethrough": "Durchgestrichen", "Inserted text": "Unterstrichen", "Marked text": "Markiert", "Link": "Link", "Image": "Foto", "Code": "Code", "Externals": "Extern", "This is a alert area.": "Dies ist ein Hinweisfeld.", "Revert": "Zurücksetzen", "Import from clipboard": "Importieren aus der Zwischenablage", "Paste your markdown or webpage here...": "Markdown oder Website hier einfügen...", "Clear": "Zurücksetzen", "This note is locked": "Diese Notiz ist gesperrt", "Sorry, only owner can edit this note.": "Entschuldigung, nur der Besitzer darf die Notiz bearbeiten.", "OK": "Ok", "Reach the limit": "Limit erreicht", "Sorry, you've reached the max length this note can be.": "Entschuldigung, die maximale Länge der Notiz ist erreicht.", "Please reduce the content or divide it to more notes, thank you!": "Bitte reduzieren Sie den Inhalt oder nutzen Sie mehrere Notizen, danke.", "Import from Gist": "Aus Gist importieren", "Paste your gist url here...": "gist URL hier einfügen ...", "Import from Snippet": "Aus Snippet importieren", "Select From Available Projects": "Aus verfügbaren Projekten wählen", "Select From Available Snippets": "Aus verfügbaren Snippets wählen", "OR": "Oder", "Export to Snippet": "Zu Snippet exportieren", "Select Visibility Level": "Sichtbarkeit bestimmen", "Night Theme": "Nachtmodus", "Follow us on %s and %s.": "Folge uns auf %s und %s.", "Privacy": "Privatsphäre", "Terms of Use": "Nutzungsbedingungen", "Do you really want to delete your user account?": "Möchten Sie wirklich Ihren Nutzeraccount löschen?", "This will delete your account, all notes that are owned by you and remove all references to your account from other notes.": "Hiermit löschen Sie Ihren Account, alle Ihre Notizen und alle Verweise auf Ihren Account aus anderen Notizen.", "Delete user": "Benutzer löschen", "Export user data": "Exportiere Nutzerdaten", "Help us translating on %s": "Hilf uns zu übersetzen auf %s", "Source Code": "Quelltext" } ================================================ FILE: locales/el.json ================================================ { "Collaborative markdown notes": "Συνεργατικές σημειώσεις markdown", "Realtime collaborative markdown notes on all platforms.": "Συνεργατική σημειώσεις markdown σε όλες τις πλατφόρμες σε πραγματικό χρόνο.", "Best way to write and share your knowledge in markdown.": "Ο καλύτερος τρόπος να γράψεις και να μοιραστείς την γνώση σου σε markdown.", "Intro": "Εισαγωγή", "History": "Ιστορία", "New guest note": "Νέα σημείωση επισκέπτη", "Collaborate with URL": "Συνεργαστείτε με URL", "Support charts and MathJax": "Υποστηρίζει διαγράμματα και MathJax", "Support slide mode": "Υποστηρίζει λειτουργία συρσίματος", "Sign In": "Eίσοδος", "Below is the history from browser": "Παρακάτω είναι η ιστορία του περιηγητή", "Welcome!": "Καλωσήρθατε!", "New note": "Νέα σημείωση", "or": "ή", "Sign Out": "Αποσύνδεση", "Explore all features": "Ανακαλύψτε όλες τις λειτουργίες", "Select tags...": "Επιλέξτε ετικέτα...", "Search keyword...": "Αναζήτηση λέξης-κλειδί...", "Sort by title": "Ταξινόμηση κατά τίτλο", "Title": "Τίτλος", "Sort by time": "Ταξινόμηση κατά ώρα", "Time": "Ώρα", "Export history": "Εξαγωγή ιστορίας", "Import history": "Εισαγωγή ιστορίας", "Clear history": "Καθαρισμός Ιστορίας", "Refresh history": "Ανανέωση ιστορίας", "No history": "Δεν υπάρχει ιστορία", "Import from browser": "Εισαγωγή απο τον περιηγητή", "Releases": "Κυκλοφορίες", "Are you sure?": "Είστε σίγουρος?", "Cancel": "Ακύρωση", "Yes, do it!": "Ναι, κάντο!", "Choose method": "Επιλογή μεθόδου", "Sign in via %s": "Σύνδεση μέσω %s", "New": "Νέο", "Publish": "Δημοσίευση", "Extra": "Επιπλέον", "Revision": "Αναθεώρηση", "Slide Mode": "Λειτουργία με σύρσιμο", "Export": "Εξαγωγή", "Import": "Εισαγωγή", "Clipboard": "Πρόχειρο", "Download": "Κατέβασμα", "Raw HTML": "Aκατέργαστο HTML", "Edit": "Επεξεργασία", "View": "Δες", "Both": "Και τα δύο", "Help": "Βοήθεια", "Upload Image": "Ανέβασμα φωτογραφίας", "Menu": "Μενού", "This page need refresh": "Η σελίδα χρειάζεται ανανέωση", "You have an incompatible client version.": "Έχετε μια μη συμβατή έκδοση.", "Refresh to update.": "Ανανεώστε για ενημέρωση", "New version available!": "Νέα διαθέσιμη έκδοση ", "See releases notes here": "Δείτε τις κυκλοφορίες της σημείωσης εδώ", "Refresh to enjoy new features.": "Ανανεώστε για να δείτε τις κανούργιες λειτουργίες", "Your user state has changed.": "Η κατάσταση χρήστη έχει αλλάξει.", "Refresh to load new user state.": "Ανανεώστε για να φορτώσετε την νέα κατάσταση χρήστη.", "Refresh": "Ανανέωση", "Contacts": "Επαφές", "Report an issue": "Αναφέρετε ένα θέμα", "Send us email": "Στείλτε μας email", "Documents": "Έγγραφα", "Features": "Λειτουργία", "YAML Metadata": "YAML μεταδεδομένα", "Slide Example": "Σύρετε για παράδειγμα", "Cheatsheet": "Σκονάκι", "Example": "Παράδειγμα", "Syntax": "Σύνταξη", "Header": "Επικεφαλίδα", "Unordered List": "Μη αριθμημένη λίστα", "Ordered List": "Αριθμημένη λίστα", "Todo List": "Todo List", "Blockquote": "Παράγραφος", "Bold font": "Εντονη γραμματοσειρά", "Italics font": "Πλάγια γραμματοσειρά", "Strikethrough": "Διαγραμένη γραμματοσειρά", "Inserted text": "Εισαγμένο κείμενο", "Marked text": "Επιλεγμένο κείμενο", "Link": "Σύνδεσμος", "Image": "Εικόνα", "Code": "Κώδικας", "Externals": "Εξωτερικά", "This is a alert area.": "Αυτή είναι μια περιοχή ειδοποίησης", "Revert": "Επαναστροφή", "Import from clipboard": "Εισαγωγή από πρόχειρο", "Paste your markdown or webpage here...": "Επικολλήστε markdown ή την ιστοσελίδα σας εδώ...", "Clear": "Καθαρισμός", "This note is locked": "Η σημείωση είναι κλειδωμένη", "Sorry, only owner can edit this note.": "Συγνώμη, μόνο ο ιδιοκτήτης μπορεί να επεξεργαστεί αυτη την σημείωση.", "OK": "Εντάξει", "Reach the limit": "Φτάσατε το όριο", "Sorry, you've reached the max length this note can be.": "Συγνώμη, φτάσατε το μέγιστο μέγεθος αυτής της σημείωσης.", "Please reduce the content or divide it to more notes, thank you!": "Παρακαλώ μειώστε το περιεχόμενο η διαιρέστε το σε περισσότερες σημειώσεις, ευχαριστώ!", "Import from Gist": "Εισαγωγή από Gist", "Paste your gist url here...": "Κάντε επικκόληση του gist url εδώ...", "Import from Snippet": "Εισαγωγή από Snippet", "Select From Available Projects": "Eπιλογή από διαθέσιμα Projects", "Select From Available Snippets": "Eπιλογή από διαθέσιμα Snippets", "OR": "Ή", "Export to Snippet": "Eξαγωγή σε Snippet", "Select Visibility Level": "Επιλέξτε επίπεδο ορατότητας" } ================================================ FILE: locales/en.json ================================================ { "Collaborative markdown notes": "Collaborative markdown notes", "Realtime collaborative markdown notes on all platforms.": "Collaborate on markdown notes on all platforms in realtime.", "Best way to write and share your knowledge in markdown.": "The best platform to write and share markdown.", "Intro": "Intro", "History": "History", "New guest note": "New guest note", "Collaborate with URL": "Real time collaboration", "Support charts and MathJax": "Works with charts and MathJax", "Support slide mode": "Supports slide mode", "Sign In": "Sign In", "Below is the history from browser": "Below is history from this browser", "Welcome!": "Welcome!", "New note": "New note", "or": "or", "Sign Out": "Sign Out", "Explore all features": "Explore all features", "Select tags...": "Select tags…", "Search keyword...": "Search keyword…", "Sort by title": "Sort by title", "Title": "Title", "Sort by time": "Sort by time", "Time": "Time", "Export history": "Export history", "Import history": "Import history", "Clear history": "Clear history", "Refresh history": "Refresh history", "No history": "No history", "Import from browser": "Import from browser", "Releases": "Releases", "Are you sure?": "Are you sure?", "Do you really want to delete this note?": "Do you really want to delete this note?", "All users will lose their connection.": "All users will lose their connection.", "Cancel": "Cancel", "Yes, do it!": "Yes, do it!", "Choose method": "Choose method", "Sign in via %s": "Sign in via %s", "New": "New", "Publish": "Publish", "Extra": "Extra", "Revision": "Revision", "Slide Mode": "Slide Mode", "Export": "Export", "Import": "Import", "Clipboard": "Clipboard", "Download": "Download", "Raw HTML": "Raw HTML", "Edit": "Edit", "View": "View", "Both": "Both", "Help": "Help", "Upload Image": "Upload Image", "Menu": "Menu", "This page need refresh": "This page needs to be refreshed", "You have an incompatible client version.": "Your client's version is incompatible.", "Refresh to update.": "Refresh to update.", "New version available!": "New version available!", "See releases notes here": "See releases notes here", "Refresh to enjoy new features.": "Refresh to enjoy new features.", "Your user state has changed.": "Your user state has changed.", "Refresh to load new user state.": "Refresh to load new user state.", "Refresh": "Refresh", "Contacts": "Contacts", "Report an issue": "Report an issue", "Meet us on %s": "Meet us on %s", "Send us email": "Send us email", "Documents": "Documents", "Features": "Features", "YAML Metadata": "YAML Metadata", "Slide Example": "Slide Example", "Cheatsheet": "Cheatsheet", "Example": "Example", "Syntax": "Syntax", "Header": "Header", "Unordered List": "Unordered List", "Ordered List": "Ordered List", "Todo List": "Checklist", "Blockquote": "Blockquote", "Bold font": "Bold", "Italics font": "Italicize", "Strikethrough": "Strikethrough", "Inserted text": "Underlined text", "Marked text": "Highlighted text", "Link": "Link", "Image": "Image", "Code": "Code", "Externals": "Externals", "This is a alert area.": "This is an alert area.", "Revert": "Revert", "Import from clipboard": "Import from clipboard", "Paste your markdown or webpage here...": "Paste your markdown or webpage here…", "Clear": "Clear", "This note is locked": "This note is locked", "Sorry, only owner can edit this note.": "Sorry, only the owner can edit this note.", "OK": "OK", "Reach the limit": "Reach the limit", "Sorry, you've reached the max length this note can be.": "Sorry, you've reached the maximum length this note can be.", "Please reduce the content or divide it to more notes, thank you!": "Please shorten the note.", "Import from Gist": "Import from Gist", "Paste your gist url here...": "Paste your gist url here…", "Import from Snippet": "Import from Snippet", "Select From Available Projects": "Select From Available Projects", "Select From Available Snippets": "Select From Available Snippets", "OR": "OR", "Export to Snippet": "Export to Snippet", "Select Visibility Level": "Select Visibility Level", "Night Theme": "Night Theme", "Follow us on %s and %s.": "Follow us on %s, and %s.", "Privacy": "Privacy", "Terms of Use": "Terms of Use", "Do you really want to delete your user account?": "Do you really want to delete your user account?", "This will delete your account, all notes that are owned by you and remove all references to your account from other notes.": "This will delete your account, all notes that are owned by you and remove all references to your account from other notes.", "Delete user": "Delete user", "Export user data": "Export user data", "Help us translating on %s": "Help us translating on %s", "Source Code": "Source Code", "Powered by %s": "Powered by %s", "Register": "Register", "Export with pandoc": "Export with pandoc", "Select output format": "Select output format" } ================================================ FILE: locales/eo.json ================================================ { "Collaborative markdown notes": "Kunlaborataj marksubenaj notoj", "Realtime collaborative markdown notes on all platforms.": "Tujkunlaborataj marksubenaj notoj ĉe ĉiuj sistemoj.", "Best way to write and share your knowledge in markdown.": "La plej bona maniero skribi kaj havigi vian scion marksubene.", "Intro": "Enkonduko", "History": "Historio", "New guest note": "Novan gastan noton", "Collaborate with URL": "Kunlaboru per URL", "Support charts and MathJax": "Ebleco por skemoj kaj MathJax", "Support slide mode": "Ebleco por bildvica modo", "Sign In": "Ensalutu", "Below is the history from browser": "Malsupre estas la historio de la retumilo", "Welcome!": "Bonvenon!", "New note": "Novan Noton", "or": "aŭ", "Sign Out": "Elsalutu", "Explore all features": "Esploru ĉiujn eblecojn", "Select tags...": "Elektu etikedojn..", "Search keyword...": "Serĉu ĉefvorton...", "Sort by title": "Ordigu laŭ titolo", "Title": "Titolo", "Sort by time": "Ordigu laŭ tempo", "Time": "Tempo", "Export history": "Elportu historion", "Import history": "Alportu historion", "Clear history": "Malplenigu historion", "Refresh history": "Refreŝigu historion", "No history": "Neniu historio", "Import from browser": "Alportu de retumilo", "Releases": "Eldonoj", "Are you sure?": "Ĉu vi certas?", "Cancel": "Nuligu", "Yes, do it!": "Jes, faru ĝin!", "Choose method": "Elektu metodon", "Sign in via %s": "Ensalutu per %s", "New": "Nova", "Publish": "Dissendu", "Extra": "Plia", "Revision": "Versio", "Slide Mode": "Bildvica modo", "Export": "Elportu", "Import": "Alportu", "Clipboard": "Poŝo", "Download": "Elŝuti", "Raw HTML": "Kruda HTML", "Edit": "Redaktu", "View": "Vidu", "Both": "Ambaŭ", "Help": "Helpo", "Upload Image": "Alŝutu bildon", "Menu": "Menuo", "This page need refresh": "Ĉi tiu paĝo bezonas refreŝiĝi", "You have an incompatible client version.": "Vi havas malkongruan klientversion.", "Refresh to update.": "Refreŝigu por ĝisdatigi", "New version available!": "Nova versio disponeblas!", "See releases notes here": "Vidu elsendajn notojn ĉi tie", "Refresh to enjoy new features.": "Refreŝigu por ĝui novajn eblecojn.", "Your user state has changed.": "Via uzantstato ŝanĝiĝis.", "Refresh to load new user state.": "Refreŝigu por ŝargi novan uzantstaton.", "Refresh": "Refreŝigu", "Contacts": "Kontaktuloj", "Report an issue": "Raportu problemon", "Send us email": "Sendu al ni retpoŝton", "Documents": "Dosieroj", "Features": "Eblecoj", "YAML Metadata": "YAML metadateno", "Slide Example": "Bildvica ekzemplo", "Cheatsheet": "Gvidfolio", "Example": "Ekzemplo", "Syntax": "Sintakso", "Header": "Paĝokapo", "Unordered List": "Neordita Listo", "Ordered List": "Ordita Listo", "Todo List": "Farenda Listo", "Blockquote": "Deŝovita cito", "Bold font": "Dika tiparo", "Italics font": "Kursiva tiparo", "Strikethrough": "Trastrekita", "Inserted text": "Enmetita teksto", "Marked text": "Markita teksto", "Link": "Ligilo", "Image": "Bildo", "Code": "Kodo", "Externals": "Eksteraĵoj", "This is a alert area.": "Ĉi tiu estas avertzono.", "Revert": "Malfaru ŝanĝojn", "Import from clipboard": "Alportu de la poŝo", "Paste your markdown or webpage here...": "Algluu vian marksubenon aŭ retpaĝaron ĉi tie...", "Clear": "Malplenigu", "This note is locked": "Ĉi tiu noto estas ŝlosita", "Sorry, only owner can edit this note.": "Bedaŭrinde, nur la proprulo povas redakti ĉi tiun noton.", "OK": "Bone", "Reach the limit": "Atingi la limigon", "Sorry, you've reached the max length this note can be.": "Pardonon, ĉi tiu noto jam atingis maksimuman longecon.", "Please reduce the content or divide it to more notes, thank you!": "Bonvolu malpligrandigi la enhavaĵon, aŭ dividi ĝin en pliajn notojn!", "Import from Gist": "Alportu el Gist", "Paste your gist url here...": "Algluu vian gist-an URL-n ĉi tie...", "Import from Snippet": "Alportu el tekstero", "Select From Available Projects": "Elektu el disponeblaj projektoj", "Select From Available Snippets": "Elektu el disponeblaj teksteroj", "OR": "AŬ", "Export to Snippet": "Elportu al Snippet", "Select Visibility Level": "Elektu videblecan nivelon" } ================================================ FILE: locales/es.json ================================================ { "Collaborative markdown notes": "Notas colaborativas en Markdown", "Realtime collaborative markdown notes on all platforms.": "Notas colaborativas en Markdown para todas las plataformas.", "Best way to write and share your knowledge in markdown.": "La mejor forma de escribir y compartir tu conocimiento en Markdown.", "Intro": "Introducción", "History": "Historia", "New guest note": "Nueva nota como invitado", "Collaborate with URL": "Colaborar via URL", "Support charts and MathJax": "Soporte para gráficos y MathJax", "Support slide mode": "Soporte para diapositivas", "Sign In": "Ingresar", "Below is the history from browser": "A continuación se muestra el historial del navegador", "Welcome!": "¡Bienvenido!", "New note": "Nueva nota", "or": "o", "Sign Out": "Salir", "Explore all features": "Explorar todas las funciones", "Select tags...": "Seleccionar etiquetas...", "Search keyword...": "Buscar palabras clave...", "Sort by title": "Ordenar por título", "Title": "Título", "Sort by time": "Ordenar por fecha", "Time": "Tiempo", "Export history": "Exportar historial", "Import history": "Importar historial", "Clear history": "Borrar historial", "Refresh history": "Actualizar historial", "No history": "Ningún historial", "Import from browser": "Importar del navegador", "Releases": "Versiones", "Are you sure?": "¿Estás seguro?", "Cancel": "Cancelar", "Yes, do it!": "Si, ¡hazlo!", "Choose method": "Elegir método", "Sign in via %s": "Ingresar via %s", "New": "Nuevo", "Publish": "Publicar", "Extra": "Extra", "Revision": "Revision", "Slide Mode": "Modo presentación", "Export": "Exportar", "Import": "Importar", "Clipboard": "Portapapeles", "Download": "Descargar", "Raw HTML": "HTML puro", "Edit": "Editar", "View": "Ver", "Both": "Ambos", "Help": "Ayuda", "Upload Image": "Subir imagen", "Menu": "Menú", "This page need refresh": "Esta página necesita ser cargada de nuevo", "You have an incompatible client version.": "Tienes una version del cliente incompatible.", "Refresh to update.": "Cargar de nuevo para actualizar.", "New version available!": "¡Nueva versión disponible!", "See releases notes here": "Ver aquí las notas de publicación", "Refresh to enjoy new features.": "Actualizar para usar las nuevas funciones.", "Your user state has changed.": "El estado de tu usuario ha cambiado.", "Refresh to load new user state.": "Recargar para actualizar el estado de tu usuario.", "Refresh": "Recargar", "Contacts": "Contactos", "Report an issue": "Reportar un problema", "Send us email": "Enviarnos un email", "Documents": "Documentos", "Features": "Funciones", "YAML Metadata": "Metadatos en YAML", "Slide Example": "Ejemplo de diapositiva", "Cheatsheet": "Ayudamemorias", "Example": "Ejemplo", "Syntax": "Sintaxis", "Header": "Cabecera", "Unordered List": "Lista desordenada", "Ordered List": "Lista ordenada", "Todo List": "Lista de tareas", "Blockquote": "Bloque de cita", "Bold font": "Fuente negrita", "Italics font": "Fuente itálica", "Strikethrough": "Tachado", "Inserted text": "Texto subrayado", "Marked text": "Texto marcado", "Link": "Enlace", "Image": "Imagen", "Code": "Código", "Externals": "Externos", "This is a alert area.": "Esto es un área de alerta.", "Revert": "Revertir", "Import from clipboard": "Importar del portapapeles", "Paste your markdown or webpage here...": "Pega tu markdown o página web aquí...", "Clear": "Limpiar", "This note is locked": "Esta nota está bloqueada", "Sorry, only owner can edit this note.": "Disculpa, solo el dueño puede editar esta nota.", "OK": "OK", "Reach the limit": "Haz alcanzado el límite", "Sorry, you've reached the max length this note can be.": "Disculpa, haz alcanzado la longitud máxima que puede tener esta nota.", "Please reduce the content or divide it to more notes, thank you!": "Por favor, reduce el contenido o dividela en mas notas, ¡gracias!", "Import from Gist": "Importar de un Gist", "Paste your gist url here...": "Pega el URL de tu Gist aquí...", "Import from Snippet": "Importar de Snippet", "Select From Available Projects": "Elegir de un proyecto disponible", "Select From Available Snippets": "Elegir de un Snippet disponible", "OR": "O", "Export to Snippet": "Exportar a Snippet", "Select Visibility Level": "Elegir el nivel de visibilidad" } ================================================ FILE: locales/fr.json ================================================ { "Collaborative markdown notes": "Notes collaboratives en markdown", "Realtime collaborative markdown notes on all platforms.": "Notes en markdown collaboratives en temps réel sur toutes les plateformes.", "Best way to write and share your knowledge in markdown.": "Le meilleur moyen d'écrire et partager votre savoir en markdown.", "Intro": "Intro", "History": "Historique", "New guest note": "Nouvelle note invité", "Collaborate with URL": "Collaborez avec l'URL", "Support charts and MathJax": "Supporte les graphiques et MathJax", "Support slide mode": "Supporte le mode présentation", "Sign In": "Se connecter", "Below is the history from browser": "Ci-dessous, l'historique du navigateur", "Welcome!": "Bienvenue !", "New note": "Nouvelle note", "or": "ou", "Sign Out": "Se déconnecter", "Explore all features": "Explorer toutes les fonctionnalités", "Select tags...": "Sélectionner les tags...", "Search keyword...": "Chercher un mot-clef...", "Sort by title": "Trier par titre", "Title": "Titre", "Sort by time": "Trier par date", "Time": "Date", "Export history": "Exporter l'historique", "Import history": "Importer l'historique", "Clear history": "Supprimer l'historique", "Refresh history": "Actualiser l'historique", "No history": "Pas d'historique", "Import from browser": "Importer depuis le navigateur", "Releases": "Versions", "Are you sure?": "Êtes-vous sûr ?", "Do you really want to delete this note?": "Voulez-vous vraiment supprimer cette note ?", "All users will lose their connection.": "Tous les utilisateurs perdront leur connexion.", "Cancel": "Annuler", "Yes, do it!": "Oui, je suis sûr !", "Choose method": "Choisir la méthode", "Sign in via %s": "Se connecter depuis %s", "New": "Nouvelle", "Publish": "Publier", "Extra": "Extra", "Revision": "Historique", "Slide Mode": "Mode présentation", "Export": "Exporter", "Import": "Importer", "Clipboard": "Presse-papier", "Download": "Télécharger", "Raw HTML": "HTML brut", "Edit": "Éditer", "View": "Voir", "Both": "Les deux", "Help": "Aide", "Upload Image": "Uploader une image", "Menu": "Menu", "This page need refresh": "Cette page doit être rechargée", "You have an incompatible client version.": "Vous avez une version client incompatible.", "Refresh to update.": "Recharger pour mettre à jour.", "New version available!": "Nouvelle version disponible !", "See releases notes here": "Voir les commentaires de version ici", "Refresh to enjoy new features.": "Recharger pour bénéficier des nouvelles fonctionnalités.", "Your user state has changed.": "Votre statut utilisateur a changé.", "Refresh to load new user state.": "Recharger pour avoir le nouveau statut utilisateur.", "Refresh": "Recharger", "Contacts": "Contacts", "Report an issue": "Signaler un problème", "Meet us on %s": "Rencontrez-nous sur %s", "Send us email": "Envoyez-nous un mail", "Documents": "Documents", "Features": "Fonctionnalités", "YAML Metadata": "Métadonnées YAML", "Slide Example": "Exemple de présentation", "Cheatsheet": "Pense-bête", "Example": "Exemple", "Syntax": "Syntaxe", "Header": "En-tête", "Unordered List": "Liste à puce", "Ordered List": "Liste numérotée", "Todo List": "Liste de tâches", "Blockquote": "Citation", "Bold font": "Gras", "Italics font": "Italique", "Strikethrough": "Barré", "Inserted text": "Souligné", "Marked text": "Surligné", "Link": "Lien", "Image": "Image", "Code": "Code", "Externals": "Contenus externes", "This is a alert area.": "Ceci est un texte d'alerte.", "Revert": "Revenir en arrière", "Import from clipboard": "Importer depuis le presse-papier", "Paste your markdown or webpage here...": "Collez votre markdown ou votre page web ici...", "Clear": "Vider", "This note is locked": "Cette note est verrouillée", "Sorry, only owner can edit this note.": "Désolé, seul le propriétaire peut éditer cette note.", "OK": "OK", "Reach the limit": "Atteindre la limite", "Sorry, you've reached the max length this note can be.": "Désolé, vous avez atteint la longueur maximale que cette note peut avoir.", "Please reduce the content or divide it to more notes, thank you!": "Merci de réduire le contenu ou de le diviser en plusieurs notes !", "Import from Gist": "Importer depuis Gist", "Paste your gist url here...": "Coller l'URL de votre Gist ici...", "Import from Snippet": "Importer depuis Snippet", "Select From Available Projects": "Sélectionner depuis les projets disponibles", "Select From Available Snippets": "Sélectionner depuis les Snippets disponibles", "OR": "OU", "Export to Snippet": "Exporter vers Snippet", "Select Visibility Level": "Sélectionner le niveau de visibilité", "Night Theme": "Thème Nuit", "Follow us on %s and %s.": "Suivez-nous sur %s, et %s.", "Privacy": "Confidentialité", "Terms of Use": "Conditions d'utilisation", "Do you really want to delete your user account?": "Voulez-vous vraiment supprimer votre compte utilisateur ?", "This will delete your account, all notes that are owned by you and remove all references to your account from other notes.": "Cela supprimera votre compte, toutes les notes dont vous êtes propriétaire et supprimera toute référence à votre compte dans les autres notes.", "Delete user": "Supprimer l'utilisateur", "Export user data": "Exporter les données utilisateur", "Help us translating on %s": "Aidez nous à traduire sur %s", "Source Code": "Code source", "Register": "S'enregistrer", "Powered by %s": "Propulsé par %s" } ================================================ FILE: locales/hi.json ================================================ { "Collaborative markdown notes": "सहयोगात्मक मार्कडॉऊन नोट्स", "Realtime collaborative markdown notes on all platforms.": "सभी प्लेटफार्मों पर वास्तविक समय सहयोगी मार्कडॉऊन के नोट्स", "Best way to write and share your knowledge in markdown.": "मार्कडॉऊन में लिखने के लिए और अपना ज्ञान शेयर करने का सबसे अच्छा तरीका", "Intro": "परिचय", "History": "इतिहास", "New guest note": "नया अतिथि नोट", "Collaborate with URL": "यूआरएल के साथ सहयोग करें", "Support charts and MathJax": "चार्ट और MathJax का समर्थन", "Support slide mode": "स्लाइड मोड का समर्थन", "Sign In": "साइन इन करें", "Below is the history from browser": "नीचे ब्राउज़र से इतिहास है", "Welcome!": "स्वागत हे!", "New note": "नया नोट", "or": "या", "Sign Out": "साइन आउट", "Explore all features": "सभी सुविधाओं का अन्वेषण करें", "Select tags...": "टैग का चयन करें ...", "Search keyword...": "मुख्य शब्द ढूंडो...", "Sort by title": "शीर्षक द्वारा क्रमबद्ध करें", "Title": "शीर्षक", "Sort by time": "समय के अनुसार क्रमबद्ध करें", "Time": "समय", "Export history": "इतिहास को निर्यात करें", "Import history": "इतिहास को आयात करें", "Clear history": "इतिहास मिटा दें", "Refresh history": "इतिहास ताज़ा करे", "No history": "इतिहास न रखें", "Import from browser": "ब्राउज़र से आयात", "Releases": "विज्ञप्ति", "Are you sure?": "क्या आपको यकीन है?", "Cancel": "रद्द करे", "Yes, do it!": "हाँ करो इसे!", "Choose method": "विधि चुनें", "Sign in via %s": "%s के माध्यम से साइन इन करें", "New": "नया", "Publish": "प्रकाशित करें", "Extra": "अतिरिक्त", "Revision": "संशोधन", "Slide Mode": "स्लाइड मोड", "Export": "निर्यात", "Import": "आयात", "Clipboard": "क्लिपबोर्ड", "Download": "डाउनलोड", "Raw HTML": "सिर्फ एच टी एम एल", "Edit": "संपादित करें", "View": "देखें", "Both": "दोनों", "Help": "मदद", "Upload Image": "तस्वीर डालिये", "Menu": "मेन्यू", "This page need refresh": "इस पेज को ताजा करने की जरूरत है", "You have an incompatible client version.": "आप एक असंगत ग्राहक संस्करण है।", "Refresh to update.": "अद्यतन करने के लिए ताज़ा करें।", "New version available!": "नया संस्करण उपलब्ध है!", "See releases notes here": "यहाँ रिलीज़ नोट देखें ", "Refresh to enjoy new features.": "नई सुविधाओं का आनंद करने के लिए ताज़ा करें।", "Your user state has changed.": "आपका उपयोगकर्ता राज्य बदल गया है।", "Refresh to load new user state.": "नई उपयोगकर्ता राज्य लोड करने के लिए ताज़ा करें।", "Refresh": "ताज़ा करे", "Contacts": "संपर्क", "Report an issue": "मामले की रिपोर्ट करें", "Send us email": "हमें ईमेल भेजें", "Documents": "दस्तावेज़", "Features": "विशेषताएं", "YAML Metadata": "YAML मेटाडाटा", "Slide Example": "स्लाइड उदाहरण", "Cheatsheet": "प्रवंचक पत्रक", "Example": "उदाहरण", "Syntax": "वाक्य - विन्यास", "Header": "हैडर", "Unordered List": "अव्यवस्थित सूची", "Ordered List": "आदेश सूची", "Todo List": "करने के लिए सूची", "Blockquote": "ब्लॉककोट", "Bold font": "बोल्ड फ़ॉन्ट", "Italics font": "इटालिक फ़ॉन्ट", "Strikethrough": "स्ट्राइकथ्रू", "Inserted text": "डाला गया टेक्स्ट", "Marked text": "निशान किया हुआ टेक्स्ट", "Link": "लिंक", "Image": "तस्वीर", "Code": "कोड", "Externals": "बाहरी", "This is a alert area.": "यह एक चेतावनी क्षेत्र है।", "Revert": "वापस करें", "Import from clipboard": "क्लिपबोर्ड से आयात", "Paste your markdown or webpage here...": "यहाँ अपने मार्कडॉऊन या वेब पेज पेस्ट करें ...", "Clear": "साफ़ करें", "This note is locked": "इस नोट को बंद कर दिया है", "Sorry, only owner can edit this note.": "क्षमा करें, केवल मालिक इस नोट को संपादित कर सकते हैं।", "OK": "ठीक", "Reach the limit": "सीमा तक पहुँचना", "Sorry, you've reached the max length this note can be.": "क्षमा करें, आप इस नोट की अधिकतम लंबाई तक पहुँच गए हैं।", "Please reduce the content or divide it to more notes, thank you!": "सामग्री को कम करें या इसे और अधिक नोटों में विभाजित करें, धन्यवाद!", "Import from Gist": "Gist से आयात करें", "Paste your gist url here...": "यहाँ अपना gist यूआरएल पेस्ट करें ...", "Import from Snippet": "स्निपेट से आयात करें", "Select From Available Projects": "उपलब्ध परियोजनाओं से चयन करें", "Select From Available Snippets": "उपलब्ध स्निपेट से चयन करें", "OR": "या", "Export to Snippet": "स्निपेट में निर्यात", "Select Visibility Level": "दृश्यता के स्तर का चयन" } ================================================ FILE: locales/hr.json ================================================ { "Collaborative markdown notes": "Kolaborativne markdown bilješke", "Realtime collaborative markdown notes on all platforms.": "Kolaborativne markdown bilješke na svim platformama u realnom vremenu.", "Best way to write and share your knowledge in markdown.": "Najbolji način za pisanje i dijeljenje svog znanja u markdown-u.", "Intro": "Uvod", "History": "Povijest", "New guest note": "Nova bilješka gosta", "Collaborate with URL": "Kolaboracija sa URL-om", "Support charts and MathJax": "Support charts and MathJax", "Support slide mode": "Način podrške slajda", "Sign In": "Prijavu se", "Below is the history from browser": "Ispod je povijest preglednika", "Welcome!": "Dobrodošli!", "New note": "Nova bilješka", "or": "ili", "Sign Out": "Odjavi se", "Explore all features": "Istraži sve značajke", "Select tags...": "Odaberi oznake...", "Search keyword...": "Pretraži ključnu riječ...", "Sort by title": "Sortiraj po naslovu", "Title": "Naslov", "Sort by time": "Sortiraj po vremenu", "Time": "Vrijeme", "Export history": "Izvezi povijest", "Import history": "Uvezi povijest", "Clear history": "Očisti povijest", "Refresh history": "Osvježi povijest", "No history": "Nema povijesti", "Import from browser": "Uvezi iz preglednika", "Releases": "Izdanja", "Are you sure?": "Jeste li sigurni?", "Cancel": "Odustani", "Yes, do it!": "Da, učini to!", "Choose method": "Izaberi metodu", "Sign in via %s": "Prijavi se pomoću %s", "New": "Novo", "Publish": "Objavi", "Extra": "Dodatno", "Revision": "Revizija", "Slide Mode": "Način slajda", "Export": "Izvoz", "Import": "Uvoz", "Clipboard": "Međuspremnik", "Download": "Preuzimanje", "Raw HTML": "Raw HTML", "Edit": "Uredi", "View": "Pregledaj", "Both": "Oboje", "Help": "Pomoć", "Upload Image": "Prenesi sliku", "Menu": "Meni", "This page need refresh": "Ovu stranicu je potrebno osvježiti", "You have an incompatible client version.": "Imate nekompatibilnu verziju klijenta.", "Refresh to update.": "Osvježite za ažuriranje.", "New version available!": "Nova verzija dostupna!", "See releases notes here": "Pogledajte bilješke izdanja ovdje", "Refresh to enjoy new features.": "Osvježi za nove značajke.", "Your user state has changed.": "Stanje Vašeg korisnika se promijenilo.", "Refresh to load new user state.": "Osvježi za učitavanje novog stanja korisnika.", "Refresh": "Osvježi", "Contacts": "Kontakti", "Report an issue": "Prijavi problem", "Send us email": "Pošalji nam email", "Documents": "Dokumenti", "Features": "Značajke", "YAML Metadata": "YAML Metadata", "Slide Example": "Primjer slajda", "Cheatsheet": "Cheatsheet", "Example": "Primjer", "Syntax": "Sintaksa", "Header": "Zaglavlje", "Unordered List": "Neuređeni popis", "Ordered List": "Uređeni popis", "Todo List": "Popis obaveza", "Blockquote": "Blockquote", "Bold font": "Bold font", "Italics font": "Kurzivan font", "Strikethrough": "Precrtano", "Inserted text": "Umetnuti tekst", "Marked text": "Označeni tekst", "Link": "Link", "Image": "Slika", "Code": "Kod", "Externals": "Vanjski izgled", "This is a alert area.": "Ovo je područje upozorenja.", "Revert": "Vrati", "Import from clipboard": "Uvezi iz međuspremnika", "Paste your markdown or webpage here...": "Zalijepi svoj markdown ili web stranicu ovdje...", "Clear": "Očisti", "This note is locked": "Ova bilješka je zaključana", "Sorry, only owner can edit this note.": "Žao nam je, samo vlasnik ove bilješke ju može uređivati.", "OK": "OK", "Reach the limit": "Dosegni granicu", "Sorry, you've reached the max length this note can be.": "Žao nam je, dosegli ste maksimalnu moguću duljinu ove bilješke.", "Please reduce the content or divide it to more notes, thank you!": "Molimo Vas smanjite sardžaj ili ga podijelite na više bilješki, hvala!", "Import from Gist": "Uvezi iz Gist-a", "Paste your gist url here...": "Zalijepi svoj gist url ovdje...", "Import from Snippet": "Uvezi iz isječka", "Select From Available Projects": "Odaberi iz raspoloživih projekta", "Select From Available Snippets": "Odaberi iz raspoloživih isječaka", "OR": "ILI", "Export to Snippet": "Izvoz u isječak", "Select Visibility Level": "Odaberi razinu vidljivosti" } ================================================ FILE: locales/id.json ================================================ { "Collaborative markdown notes": "Catatan markdown kolaboratif", "Realtime collaborative markdown notes on all platforms.": "Berkolaborasi di catatan markdown di semua platform secara realtime", "Best way to write and share your knowledge in markdown.": "Platform terbaik untuk menulis dan membagikan markdown", "Intro": "Perkenalan", "History": "Riwayat", "New guest note": "Catatan baru (sebagai tamu)", "Collaborate with URL": "Kolaborasi real-time", "Support charts and MathJax": "Mendukung charts dan MathJax", "Support slide mode": "Mendukung mode slide", "Sign In": "Masuk", "Below is the history from browser": "Dibawah ini adalah riwayat dari peramban ini", "Welcome!": "Selamat Datang", "New note": "Catatan Baru", "or": "atau", "Sign Out": "Keluar", "Explore all features": "Jelajahi semua fitur", "Select tags...": "Pilih tanda...", "Search keyword...": "Cari berdasarkan kata kunci...", "Sort by title": "Urutkan berdasarkan judul", "Title": "Judul", "Sort by time": "Urutkan berdasarkan waktu", "Time": "Waktu", "Export history": "Ekspor Riwayat", "Import history": "Impor Riwayat", "Clear history": "Bersihkan Riwayat", "Refresh history": "Muat-ulang Riwayat", "No history": "Tidak ada riwayat", "Import from browser": "Impor dari browser", "Releases": "Penerbitan", "Are you sure?": "Apakah anda yakin?", "Do you really want to delete this note?": "Apakah anda yakin ingin menghapus catatan ini?", "All users will lose their connection.": "Semua pengguna akan kehilangan koneksi nya", "Cancel": "Batal", "Yes, do it!": "Ya, lakukan!", "Choose method": "Pilih cara", "Sign in via %s": "Masuk menggunakan %s", "New": "Baru", "Publish": "Terbitkan", "Extra": "Tambahan", "Revision": "Revisi", "Slide Mode": "Mode Slide", "Export": "Ekspor", "Import": "Impor", "Clipboard": "Papan Klip", "Download": "Unduh", "Raw HTML": "File HTML", "Edit": "Ubah", "View": "Lihat", "Both": "Keduanya", "Help": "Bantuan", "Upload Image": "Unggah Gambar", "Menu": "Menu", "This page need refresh": "Halaman ini perlu dimuat ulang", "You have an incompatible client version.": "Versi pramban anda tidak kompatibel", "Refresh to update.": "Muat ulang untuk memperbarui", "New version available!": "Versi baru tersedia!", "See releases notes here": "Lihat catatan penerbitan", "Refresh to enjoy new features.": "Muat ulang untuk menikmati fitur baru.", "Your user state has changed.": "Data pengguna anda telah berubah.", "Refresh to load new user state.": "Muat ulang untuk memuat data baru pengguna.", "Refresh": "Muat ulang", "Contacts": "Kontak", "Report an issue": "Laporkan kesalahan", "Meet us on %s": "Temui kami di %s", "Send us email": "Kirim kami email", "Documents": "Dokumen", "Features": "Fitur", "YAML Metadata": "Metadata YML", "Slide Example": "Contoh Slide", "Cheatsheet": "Cheatsheet", "Example": "Contoh", "Syntax": "Sintaks", "Header": "Header", "Unordered List": "Daftar tak ber-urutan", "Ordered List": "Daftar ber-urutan", "Todo List": "Centang", "Blockquote": "Blok kutipan", "Bold font": "Tebal", "Italics font": "Miring", "Strikethrough": "Garis", "Inserted text": "Teks ber-garis bawah", "Marked text": "Teks yang disorot", "Link": "Link", "Image": "Gambar", "Code": "Kode", "Externals": "Eksternal", "This is a alert area.": "Ini adalah area alert.", "Revert": "Kembalikan", "Import from clipboard": "Impor dari papan klip", "Paste your markdown or webpage here...": "Tempel markdown atau halaman web disini", "Clear": "Bersihkan", "This note is locked": "Catatan ini terkunci", "Sorry, only owner can edit this note.": "Maaf, hanya pemilik yang bisa mengubah catatan ini", "OK": "OK", "Reach the limit": "Memenuhi batas", "Sorry, you've reached the max length this note can be.": "Maaf, anda telah memenuhi batas maksimum jumlah catatan ini", "Please reduce the content or divide it to more notes, thank you!": "Tolong persingkat catatan nya.", "Import from Gist": "Impor dari Gist", "Paste your gist url here...": "Templekan URL gist anda disini...", "Import from Snippet": "Impor dari Snippet", "Select From Available Projects": "Pilih dari Project yang tersedia", "Select From Available Snippets": "Pilih dari Snippet yang tersedia", "OR": "Atau", "Export to Snippet": "Ekspor ke Snippet", "Select Visibility Level": "Pilih tingkat penglihatan", "Night Theme": "Mode Malam", "Follow us on %s and %s.": "Ikuti kami di %s, dan %s.", "Privacy": "Privasi", "Terms of Use": "Aturan Penggunaan", "Do you really want to delete your user account?": "Apakah anda yakin ingin menghapus akun anda?", "This will delete your account, all notes that are owned by you and remove all references to your account from other notes.": "Ini akan menghapus akun anda, semua catatan yang dimiliki oleh anda akan dihapus dan menghapus semua referensi ke akun anda dari catatan lain.", "Delete user": "Hapus pengguna", "Export user data": "Ekspor data pengguna", "Help us translating on %s": "Bantu kami menerjemahkan di %s", "Source Code": "Sumber Kode", "Register": "Daftar", "Powered by %s": "Ditenagai oleh %s" } ================================================ FILE: locales/it.json ================================================ { "Collaborative markdown notes": "Note collaborative in markdown", "Realtime collaborative markdown notes on all platforms.": "Note markdown collaborative in tempo reale per tutte le piattaforme.", "Best way to write and share your knowledge in markdown.": "Miglior modo per scrivere e condividere le tue conoscenze in markdown.", "Intro": "Intro", "History": "Cronologia", "New guest note": "Nuova nota ospite", "Collaborate with URL": "Collabora tramite URL", "Support charts and MathJax": "Supporta grafici e MathJax", "Support slide mode": "Supporta modalità slide", "Sign In": "Registrati", "Below is the history from browser": "Qui sotto c'è la cronologia del browser", "Welcome!": "Benvenuto!", "New note": "Nuova nota", "or": "o", "Sign Out": "Disconettiti", "Explore all features": "Esplora tutte le funzioni", "Select tags...": "Seleziona tag...", "Search keyword...": "Cerca...", "Sort by title": "Ordina per titolo", "Title": "Titolo", "Sort by time": "Ordina per data", "Time": "Data", "Export history": "Esporta cronologia", "Import history": "Importa cronologia", "Clear history": "Cancella cronologia", "Refresh history": "Aggiorna cronologia", "No history": "Nessuna cronologia", "Import from browser": "Importa da browser", "Releases": "Versioni", "Are you sure?": "Sei sicuro?", "Do you really want to delete this note?": "Vuoi veramente eliminare questa nota?", "All users will lose their connection.": "Tutti gli utenti perderanno la loro connessione.", "Cancel": "Annulla", "Yes, do it!": "SI, fallo!", "Choose method": "Scegli metodo", "Sign in via %s": "Registrati con %s", "New": "Nuovo", "Publish": "Pubblica", "Extra": "Extra", "Revision": "Revisione", "Slide Mode": "Modalità slide", "Export": "Esporta", "Import": "Importa", "Clipboard": "Appunti", "Download": "Scarica", "Raw HTML": "Raw HTML", "Edit": "Modifica", "View": "Visualizza", "Both": "Entrambi", "Help": "Aiuto", "Upload Image": "Carica Immagine", "Menu": "Menu", "This page need refresh": "Questa pagina deve essere aggiornata", "You have an incompatible client version.": "La versione del tuo client è incompatibile.", "Refresh to update.": "Ricarica per aggiornare.", "New version available!": "Nuova versione disponibile!", "See releases notes here": "Vedi note di rilascio qui", "Refresh to enjoy new features.": "Ricarica per godere delle nuove funzioni.", "Your user state has changed.": "Il tuo stato utente è cambiato.", "Refresh to load new user state.": "Aggiorna per caricare il nuovo stato utente.", "Refresh": "Ricarica", "Contacts": "Contatti", "Report an issue": "Segnala un problema", "Meet us on %s": "Vieni a trovarci su %s", "Send us email": "Inviaci una email", "Documents": "Documenti", "Features": "Caratteristiche", "YAML Metadata": "YAML Metadata", "Slide Example": "Esempio Slide", "Cheatsheet": "Cheatsheet", "Example": "Esempio", "Syntax": "Sintassi", "Header": "Intestazione", "Unordered List": "Lista non ordinata", "Ordered List": "Lista ordinata", "Todo List": "Elenco", "Blockquote": "Citazione", "Bold font": "Grassetto", "Italics font": "Corsivo", "Strikethrough": "Barrato", "Inserted text": "Sottolineato", "Marked text": "Evidenziato", "Link": "Link", "Image": "Immagine", "Code": "Codice", "Externals": "Esterni", "This is a alert area.": "Questa è un area di avviso.", "Revert": "Annulla", "Import from clipboard": "Importa dagli appunti", "Paste your markdown or webpage here...": "Incollare il markdown o una pagina web qui...", "Clear": "Pulisci", "This note is locked": "Questa nota è bloccata", "Sorry, only owner can edit this note.": "Siamo spiacenti, solo il proprietario può modificare questa nota.", "OK": "OK", "Reach the limit": "Limite raggiunto", "Sorry, you've reached the max length this note can be.": "Siamo spiacenti, hai raggiunto la lunghezza massima per questa nota.", "Please reduce the content or divide it to more notes, thank you!": "Si prega di ridurre il contenuto o dividerlo in più note, grazie!", "Import from Gist": "Importa da Gist", "Paste your gist url here...": "Incolla il tuo link gist qui...", "Import from Snippet": "Importa da Snippet", "Select From Available Projects": "Seleziona da progetti disponibili", "Select From Available Snippets": "Seleziona da snippets disponibili", "OR": "O", "Export to Snippet": "Esporta Snippet", "Select Visibility Level": "Seleziona livello visibilità", "Night Theme": "Tema Scuro", "Follow us on %s and %s.": "Seguici su %s, e %s.", "Privacy": "Privacy", "Terms of Use": "Termini di Utilizzo", "Do you really want to delete your user account?": "Vuoi veramente cancellare il tuo account utente?", "This will delete your account, all notes that are owned by you and remove all references to your account from other notes.": "Questo cancellerà il tuo account, tutte le note di cui sei proprietario e rimuoverà i riferimenti al tuo account dalle altre note.", "Delete user": "Elimina utente", "Export user data": "Esporta dati utente", "Help us translating on %s": "Aiutaci nella traduzione su %s", "Source Code": "Codice Sorgente", "Register": "Registrati", "Powered by %s": "Alimentato da %s" } ================================================ FILE: locales/ja.json ================================================ { "Collaborative markdown notes": "共同編集できるMarkdownノート", "Realtime collaborative markdown notes on all platforms.": "マルチプラットフォーム、リアルタイムで共同編集できるMarkdownノート", "Best way to write and share your knowledge in markdown.": "Markdownでナレッジを蓄積・共有できるベストツール", "Intro": "サービスの紹介", "History": "履歴", "New guest note": "新規ゲストノート", "Collaborate with URL": "URLで共同編集", "Support charts and MathJax": "グラフとMathJaxのサポート", "Support slide mode": "スライドモードのサポート", "Sign In": "サインイン", "Below is the history from browser": "ブラウザからの履歴", "Welcome!": "ようこそ!", "New note": "新規ノート", "or": "または", "Sign Out": "サインアウト", "Explore all features": "すべての機能をチェック", "Select tags...": "タグで検索", "Search keyword...": "キーワードで検索", "Sort by title": "タイトル順でソート", "Title": "タイトル", "Sort by time": "日時順でソート", "Time": "日時", "Export history": "履歴をエクスポート", "Import history": "履歴をインポート", "Clear history": "履歴をクリア", "Refresh history": "履歴を更新", "No history": "履歴はありません", "Import from browser": "ブラウザからインポート", "Releases": "リリース", "Are you sure?": "本当にいいですか?", "Do you really want to delete this note?": "本当にこのノートを削除しますか?", "All users will lose their connection.": "すべてのユーザーの接続が切断されます。", "Cancel": "キャンセル", "Yes, do it!": "はい", "Choose method": "選択してください", "Sign in via %s": "%sでサインイン", "New": "新規作成", "Publish": "公開する", "Extra": "その他", "Revision": "編集履歴", "Slide Mode": "スライドモード", "Export": "エクスポート", "Import": "インポート", "Clipboard": "クリップボード", "Download": "ダウンロード", "Raw HTML": "HTMLパーツ", "Edit": "編集モード", "View": "表示モード", "Both": "分割モード", "Help": "ヘルプ", "Upload Image": "画像をアップロード", "Menu": "メニュー", "This page need refresh": "ページをリロードしてください", "You have an incompatible client version.": "クライアントのバージョンが一致しません", "Refresh to update.": "リロードして更新を反映させてください", "New version available!": "新しいバージョンが利用できます!", "See releases notes here": "リリースノートをご覧ください", "Refresh to enjoy new features.": "リロードして新しい機能を試してみましょう", "Your user state has changed.": "ユーザー情報が変更されました", "Refresh to load new user state.": "リロードすると最新のユーザー情報が反映されます", "Refresh": "リロード", "Contacts": "コンタクト", "Report an issue": "問題を報告する", "Meet us on %s": "%sでチャットする", "Send us email": "メールを送る", "Documents": "ドキュメント", "Features": "機能", "YAML Metadata": "YAMLメタデータ", "Slide Example": "スライドサンプル", "Cheatsheet": "チートシート", "Example": "例", "Syntax": "構文", "Header": "見出し", "Unordered List": "番号なしリスト", "Ordered List": "番号付きリスト", "Todo List": "TODOリスト", "Blockquote": "引用文", "Bold font": "太字", "Italics font": "斜体", "Strikethrough": "打ち消し線", "Inserted text": "挿入文", "Marked text": "マーカー", "Link": "リンク", "Image": "画像", "Code": "コード", "Externals": "モジュール", "This is a alert area.": "これはアラートエリアです", "Revert": "戻す", "Import from clipboard": "クリップボードからインポート", "Paste your markdown or webpage here...": "Markdownまたはウェブページを貼り付けてください", "Clear": "クリア", "This note is locked": "このノートはロックされています", "Sorry, only owner can edit this note.": "このノートはオーナーのみが編集できます", "OK": "OK", "Reach the limit": "上限に達しました", "Sorry, you've reached the max length this note can be.": "ノートの文字数が上限に達しました。", "Please reduce the content or divide it to more notes, thank you!": "内容を減らすか、別のノートに分けてください", "Import from Gist": "gistからインポート", "Paste your gist url here...": "gistのURLを貼り付けてください", "Import from Snippet": "スニペットからインポート", "Select From Available Projects": "プロジェクトを一覧から選択してください", "Select From Available Snippets": "スニペットを一覧から選択してください", "OR": "または", "Export to Snippet": "スニペットにエクスポート", "Select Visibility Level": "公開範囲を選んでください", "Night Theme": "ナイトテーマ", "Follow us on %s and %s.": "%s と %s でフォローしてください。", "Privacy": "プライバシー", "Terms of Use": "利用条件", "Do you really want to delete your user account?": "本当にアカウントを削除しますか?", "This will delete your account, all notes that are owned by you and remove all references to your account from other notes.": "この操作はあなたのアカウントとあなたの所有するすべてのノートを削除し、さらに他の人のノートからあなたのアカウントへの参照を除去します。", "Delete user": "ユーザーの削除", "Export user data": "ユーザーデータをエクスポート", "Help us translating on %s": "%s の翻訳にご協力ください", "Source Code": "ソースコード", "Register": "登録", "Powered by %s": "Powered by %s" } ================================================ FILE: locales/ko.json ================================================ { "Collaborative markdown notes": "협동 마크다운 노트", "Realtime collaborative markdown notes on all platforms.": "실시간으로 모든 플랫폼에서 마크다운 노트를 함께 작성해보세요.", "Best way to write and share your knowledge in markdown.": "마크다운을 쓰고 공유하는 최고의 플랫폼입니다.", "Intro": "소개", "History": "기록", "New guest note": "새 손님 노트", "Collaborate with URL": "URL을 통한 실시간 협업", "Support charts and MathJax": "차트와 MathJax 지원", "Support slide mode": "슬라이드 모드 지원", "Sign In": "로그인", "Below is the history from browser": "아래는 이 브라우저에서 찾은 기록입니다.", "Welcome!": "환영합니다!", "New note": "새 노트", "or": "또는", "Sign Out": "로그아웃", "Explore all features": "모든 기능 둘러보기", "Select tags...": "태그 선택하기", "Search keyword...": "키워드 검색하기", "Sort by title": "제목 기준 정렬", "Title": "제목", "Sort by time": "시간 기준 정렬", "Time": "시간", "Export history": "기록 내보내기", "Import history": "기록 불러오기", "Clear history": "기록 초기화", "Refresh history": "기록 새로고침", "No history": "기록 없음", "Import from browser": "브라우저에서 불러오기", "Releases": "릴리즈", "Are you sure?": "확실합니까?", "Do you really want to delete this note?": "정말로 이 노트를 삭제하시겠습니까?", "All users will lose their connection.": "모든 사용자의 연결이 끊어집니다.", "Cancel": "취소", "Yes, do it!": "네", "Choose method": "방법 선택", "Sign in via %s": "%s으로 로그인", "New": "새", "Publish": "공개하기", "Extra": "추가", "Revision": "기록", "Slide Mode": "슬라이드 모드", "Export": "내보내기", "Import": "들여오기", "Clipboard": "클립보드", "Download": "다운로드", "Raw HTML": "순수 HTML", "Edit": "수정", "View": "보기", "Both": "한번에", "Help": "도움말", "Upload Image": "이미지 업로드", "Menu": "메뉴", "This page need refresh": "새로고침이 필요합니다", "You have an incompatible client version.": "호환되지 않는 클라이언트입니다.", "Refresh to update.": "새로고침하기", "New version available!": "새로운 버전이 있습니다!", "See releases notes here": "릴리즈 노트를 읽어보세요", "Refresh to enjoy new features.": "새로운 기능을 즐기려면 새로고침하십시오", "Your user state has changed.": "유저 상태가 변경되었습니다.", "Refresh to load new user state.": "새로고침하여 새로운 유저 상태를 적용합니다.", "Refresh": "새로고침", "Contacts": "연락처", "Report an issue": "이슈 보고하기", "Meet us on %s": "%s에서 만나보세요", "Send us email": "이메일 보내기", "Documents": "문서", "Features": "기능", "YAML Metadata": "YAML 속성", "Slide Example": "슬라이드 예제", "Cheatsheet": "치트시트", "Example": "예시", "Syntax": "문법", "Header": "머리글", "Unordered List": "순서 없는 목록", "Ordered List": "순서 있는 목록", "Todo List": "체크리스트", "Blockquote": "인용문", "Bold font": "굵게", "Italics font": "기울임", "Strikethrough": "취소선", "Inserted text": "밑줄", "Marked text": "강조", "Link": "링크", "Image": "이미지", "Code": "코드", "Externals": "외부 서비스 연동", "This is a alert area.": "여기는 알림 공간입니다.", "Revert": "되돌리기", "Import from clipboard": "클립보드에서 불러오기", "Paste your markdown or webpage here...": "마크다운이나 웹페이지 붙여넣기", "Clear": "Clear", "This note is locked": "이 노트는 잠겨있습니다.", "Sorry, only owner can edit this note.": "죄송하지만 소유자만 이 노트를 수정할 수 있습니다.", "OK": "확인", "Reach the limit": "한계에 도달", "Sorry, you've reached the max length this note can be.": "죄송합니다. 노트 최대 길이를 초과하였습니다.", "Please reduce the content or divide it to more notes, thank you!": "노트 길이를 줄여주십시오.", "Import from Gist": "Gist에서 불러오기", "Paste your gist url here...": "Gist URL을 입력하세요", "Import from Snippet": "Import from Snippet", "Select From Available Projects": "가능한 프로젝트 중 선택", "Select From Available Snippets": "Select From Available Snippets", "OR": "또는", "Export to Snippet": "Export to Snippet", "Select Visibility Level": "Select Visibility Level", "Night Theme": "다크 테마", "Follow us on %s and %s.": "%s과 %s에서 저희를 팔로우해보세요", "Privacy": "Privacy", "Terms of Use": "Terms of Use", "Do you really want to delete your user account?": "Do you really want to delete your user account?", "This will delete your account, all notes that are owned by you and remove all references to your account from other notes.": "This will delete your account, all notes that are owned by you and remove all references to your account from other notes.", "Delete user": "Delete user", "Export user data": "Export user data", "Help us translating on %s": "%s에서 번역으로 저희를 도와주세요", "Source Code": "Source Code" } ================================================ FILE: locales/nl.json ================================================ { "Collaborative markdown notes": "Samenwerkende markdown notities", "Realtime collaborative markdown notes on all platforms.": "Realtime samenwerkende markdown notities.", "Best way to write and share your knowledge in markdown.": "De beste manier je kennis te delen en schrijven in markdown.", "Intro": "Introductie", "History": "Geschiedenis", "New guest note": "Nieuwe gastnotitie", "Collaborate with URL": "Samenwerken met URL", "Support charts and MathJax": "Ondersteunt grafieken en MathJax", "Support slide mode": "Ondersteunt presentatiemodus", "Sign In": "Inloggen", "Below is the history from browser": "Hier onder staat de browser geschiedenis", "Welcome!": "Welkom!", "New note": "Nieuwe notitie", "or": "of", "Sign Out": "Uitloggen", "Explore all features": "Ontdek alle features", "Select tags...": "Selecteer tags...", "Search keyword...": "Zoeken op keyword...", "Sort by title": "Sorteren op titel", "Title": "Titel", "Sort by time": "Sorteren op tijd", "Time": "Tijd", "Export history": "Exporteer geschiedenis", "Import history": "Importeer geschiedenis", "Clear history": "Verwijder geschiedenis", "Refresh history": "Ververs geschiedenis", "No history": "Geen geschidenis gevonden", "Import from browser": "Importeer van browser", "Releases": "Releases", "Are you sure?": "Weet je het zeker?", "Do you really want to delete this note?": "Will je deze notitie echt verwijderen?", "All users will lose their connection.": "Alle gebruikers zullen hun verbinding verliezen.", "Cancel": "Stoppen", "Yes, do it!": "Ja, doe het!", "Choose method": "Kies methode", "Sign in via %s": "Log in via %s", "New": "Nieuw", "Publish": "Publiceren", "Extra": "Extra", "Revision": "Versie", "Slide Mode": "Presentatiemodus", "Export": "Exporteren", "Import": "Importeren", "Clipboard": "Kladbord", "Download": "Downloaden", "Raw HTML": "Ruwe HTML", "Edit": "Aanpassen", "View": "Bekijken", "Both": "Beide", "Help": "Help", "Upload Image": "Afbeelding uploaden", "Menu": "Menu", "This page need refresh": "Deze pagina moet vernieuwd worden", "You have an incompatible client version.": "Je client is niet compatibel.", "Refresh to update.": "Ververs om te updaten.", "New version available!": "Nieuwe versie beschikbaar!", "See releases notes here": "Bekijk de release notes hier", "Refresh to enjoy new features.": "Ververs om de nieuwe features te zien.", "Your user state has changed.": "Je gebruikers-status is veranderd.", "Refresh to load new user state.": "Ververs om je nieuwe gebruikers-status te zien.", "Refresh": "Ververs", "Contacts": "Contacten", "Report an issue": "Probleem rapporteren", "Meet us on %s": "Ontmoet ons op %s", "Send us email": "Stuur ons een mail", "Documents": "Documenten", "Features": "Features", "YAML Metadata": "YAML Metadata", "Slide Example": "Slide Voorbeeld", "Cheatsheet": "Spiekbrief", "Example": "Voorbeeld", "Syntax": "Syntax", "Header": "Koptekst", "Unordered List": "Niet gesorteerde Lijst", "Ordered List": "Gesorteerde List", "Todo List": "Todo Lijst", "Blockquote": "Citaat", "Bold font": "Bold tekst", "Italics font": "Italics tekst", "Strikethrough": "Doorstreepte tekst", "Inserted text": "Bijgevoegde tekst", "Marked text": "Gemarkeerde tekst", "Link": "Link", "Image": "Afbeelding", "Code": "Code", "Externals": "Uiterlijkheden", "This is a alert area.": "Dit is een waarschuwingsgebied.", "Revert": "Terugzetten", "Import from clipboard": "Importeren from kladbord", "Paste your markdown or webpage here...": "Plak je markdown of webpagina hier...", "Clear": "Legen", "This note is locked": "Deze notitie zit op slot", "Sorry, only owner can edit this note.": "Sorry, alleen de eigenaar kan deze notitie aanpassen.", "OK": "OK", "Reach the limit": "Limiet bereikt", "Sorry, you've reached the max length this note can be.": "Sorry, je notitie heeft de maximale lengte bereikt.", "Please reduce the content or divide it to more notes, thank you!": "Verwijder alsjeblieft wat tekst of verdeel het over meerdere notities!", "Import from Gist": "Importeren vanaf een Gist", "Paste your gist url here...": "Plak je Gist URL hier...", "Import from Snippet": "Imporeren vanaf een Snippet", "Select From Available Projects": "Selecteer van beschikbare projecten", "Select From Available Snippets": "Selecteer van beschikbare Snippets", "OR": "OF", "Export to Snippet": "Exporteren naar Snippet", "Select Visibility Level": "Selecteer zichtbaarheids niveau", "Night Theme": "Nachtweergave", "Follow us on %s and %s.": "Volg ons op %s en %s.", "Privacy": "Privacy", "Terms of Use": "Gebruikersvoorwaarden", "Do you really want to delete your user account?": "Weet je zeker dat je je account wilt verwijderen?", "This will delete your account, all notes that are owned by you and remove all references to your account from other notes.": "Dit zal je account verwijderen. Alle notities waar je eigenaar van bent worden verwijderd, samen met alle verwijzingen naar je account.", "Delete user": "Gebruiker verwijderen", "Export user data": "Gebruikersdata exporteren", "Help us translating on %s": "Help ons vertalen op %s", "Source Code": "Broncode", "Register": "Registreren", "Powered by %s": "Powered by %s" } ================================================ FILE: locales/pl.json ================================================ { "Collaborative markdown notes": "Wspólne markdown notatki", "Realtime collaborative markdown notes on all platforms.": "Rzeczywiste wspólne markdown notatki dla wszystkich platform", "Best way to write and share your knowledge in markdown.": "Najlepszy sposób na pisanie i dzielenie się swoją wiedzą w markdown.", "Intro": "Intro", "History": "Historia", "New guest note": "Nowa notatka gościa", "Collaborate with URL": "Kolaboracja w czasie rzeczywistym", "Support charts and MathJax": "Kompatybilne z wykresami oraz MathJax", "Support slide mode": "Obsługuje tryb slajdów", "Sign In": "Zaloguj się", "Below is the history from browser": "Historia z przeglądarki poniżej", "Welcome!": "Witam!", "New note": "Nowa notatka", "or": "lub", "Sign Out": "Wyloguj się", "Explore all features": "Przeglądaj wszystkie funkcje", "Select tags...": "Wybierz tagi...", "Search keyword...": "Znajdź kluczowe słowo...", "Sort by title": "Sortuj według tytułu", "Title": "Tytuł", "Sort by time": "Sortuj według czasu", "Time": "Czas", "Export history": "Eksportuj historię", "Import history": "Importuj historię", "Clear history": "Wyczyść historię", "Refresh history": "Odśwież historię", "No history": "Brak historii", "Import from browser": "Importuj z przeglądarki", "Releases": "Wydania", "Are you sure?": "Jesteś pewny?", "Do you really want to delete this note?": "Do you really want to delete this note?", "All users will lose their connection.": "All users will lose their connection.", "Cancel": "Anuluj", "Yes, do it!": "Tak, zrób to!", "Choose method": "Wybierz metodę", "Sign in via %s": "Zaloguj się poprzez %s", "New": "Nowy", "Publish": "Publikuj", "Extra": "Ekstra", "Revision": "Korekta", "Slide Mode": "Tryb slajdów", "Export": "Eksport", "Import": "Import", "Clipboard": "Schowek", "Download": "Pobierz", "Raw HTML": "Raw HTML", "Edit": "Edytuj", "View": "Pogląd", "Both": "Both", "Help": "Pomoc", "Upload Image": "Prześlij zdjęcie", "Menu": "Menu", "This page need refresh": "Strona wymaga odświeżenia", "You have an incompatible client version.": "Posiadasz niezgodną wersję kliencką.", "Refresh to update.": "Odświerz aby zaktualizować.", "New version available!": "Nowa wersja dostępna!", "See releases notes here": "Zobacz informacje o wydaniach tutaj", "Refresh to enjoy new features.": "Odśwież, aby korzystać z nowych funkcji.", "Your user state has changed.": "Stan twojego użytkownika się zmienił.", "Refresh to load new user state.": "Odśwież aby załadować nowy stan użytkownika.", "Refresh": "Odśwież", "Contacts": "Kontakty", "Report an issue": "Zgłoś błąd", "Meet us on %s": "Meet us on %s", "Send us email": "Wyślij nam email", "Documents": "Dokumenty", "Features": "Funkcje", "YAML Metadata": "YAML Meta dane", "Slide Example": "Przykład slajdu", "Cheatsheet": "Ściągawka", "Example": "Przykład", "Syntax": "Składnia", "Header": "Nagłówek", "Unordered List": "Nie posortowana lista", "Ordered List": "Posortowana lista", "Todo List": "Todo lista", "Blockquote": "Cytat blokowy", "Bold font": "Czcionka pogrubiona", "Italics font": "Czcionka pochylona", "Strikethrough": "Przekreślenie", "Inserted text": "Wstawiony tekst", "Marked text": "Zaznaczony tekst", "Link": "Odnośnik", "Image": "Zdjęcie", "Code": "Kod", "Externals": "Zewnętrzne", "This is a alert area.": "This is a alert area.", "Revert": "Cofnij", "Import from clipboard": "Importuj ze schowka", "Paste your markdown or webpage here...": "Wklej markdown lub stronę tutaj...", "Clear": "Wyczyść", "This note is locked": "Notatka jest zablokowana", "Sorry, only owner can edit this note.": "Tylko właściciel może edytować tą notatkę.", "OK": "OK", "Reach the limit": "Osiągnięto limit", "Sorry, you've reached the max length this note can be.": "Niestety, osiągnięto maksymalną długość notatki.", "Please reduce the content or divide it to more notes, thank you!": "Proszę zmniejszyć zawartość notatki lub podzielić ją na kilka notatek, dziękuję!", "Import from Gist": "Importuj z Gist", "Paste your gist url here...": "Wklej gist url tutaj...", "Import from Snippet": "Importuj z Snippet", "Select From Available Projects": "Wybierz z dostępnych projektów", "Select From Available Snippets": "Wybierz z dostępnych Snippets", "OR": "LUB", "Export to Snippet": "Eksportuj do Snippet", "Select Visibility Level": "Wybierz poziom widoczności", "Night Theme": "Night Theme", "Follow us on %s and %s.": "Follow us on %s, and %s.", "Privacy": "Privacy", "Terms of Use": "Terms of Use", "Do you really want to delete your user account?": "Do you really want to delete your user account?", "This will delete your account, all notes that are owned by you and remove all references to your account from other notes.": "This will delete your account, all notes that are owned by you and remove all references to your account from other notes.", "Delete user": "Delete user", "Export user data": "Export user data" } ================================================ FILE: locales/pt.json ================================================ { "Collaborative markdown notes": "Notas em Markdown colaborativas", "Realtime collaborative markdown notes on all platforms.": "Notas colaborativas em Markdown para todas as plataformas.", "Best way to write and share your knowledge in markdown.": "A melhor forma de escrever e compartilhar seu conhecimento em Markdown.", "Intro": "Introdução", "History": "Histórico", "New guest note": "Nova nota como convidado", "Collaborate with URL": "Colaborar via URL", "Support charts and MathJax": "Suporte para gráficos e MathJax", "Support slide mode": "Suporte para modo apresentação", "Sign In": "Entrar", "Below is the history from browser": "A seguir está o histórico do navegador", "Welcome!": "Bem vindo!", "New note": "Nova nota", "or": "ou", "Sign Out": "Sair", "Explore all features": "Explore todas as funções", "Select tags...": "Selecionar etiquetas...", "Search keyword...": "Buscar palavra-chave...", "Sort by title": "Ordenar por título", "Title": "Título", "Sort by time": "Ordenar por hora", "Time": "Hora", "Export history": "Exportar histórico", "Import history": "Importar histórico", "Clear history": "Apagar histórico", "Refresh history": "Atualizar histórico", "No history": "Nenhum histórico", "Import from browser": "Importar do navegador", "Releases": "Lançamentos", "Are you sure?": "Tem certeza?", "Cancel": "Cancelar", "Yes, do it!": "Sim, faça!", "Choose method": "Escolher método", "Sign in via %s": "Entrar via %s", "New": "Novo", "Publish": "Publicar", "Extra": "Extra", "Revision": "Revisão", "Slide Mode": "Modo Apresentação", "Export": "Exportar", "Import": "Importar", "Clipboard": "Área de transferência", "Download": "Baixar", "Raw HTML": "HTML puro", "Edit": "Editar", "View": "Ver", "Both": "Ambos", "Help": "Ajuda", "Upload Image": "Carregar Imagem", "Menu": "Menu", "This page need refresh": "Esta página precisa ser recarregada", "You have an incompatible client version.": "Você tem uma versão incompatível do cliente.", "Refresh to update.": "Recarregar para atualizar.", "New version available!": "Nova versão disponível!", "See releases notes here": "Veja notas de lançamento aqui", "Refresh to enjoy new features.": "Atualize para usar as novas funções.", "Your user state has changed.": "O estado do seu usuário mudou.", "Refresh to load new user state.": "Atualize para carregar o novo estado do usuário.", "Refresh": "Recarregar", "Contacts": "Contatos", "Report an issue": "Relatar um problema", "Send us email": "Envie-nos um email", "Documents": "Documentos", "Features": "Funções", "YAML Metadata": "Metadados YAML", "Slide Example": "Exemplo de Apresentação", "Cheatsheet": "Dicas", "Example": "Exemplo", "Syntax": "Sintaxe", "Header": "Cabeçalho", "Unordered List": "Lista não ordenada", "Ordered List": "Lista ordenada", "Todo List": "Lista de tarefas", "Blockquote": "Citação", "Bold font": "Fonte negrito", "Italics font": "Fonte itálico", "Strikethrough": "Tachado", "Inserted text": "Texto inserido", "Marked text": "Texto marcado", "Link": "Ligação", "Image": "Imagem", "Code": "Código", "Externals": "Externos", "This is a alert area.": "Esta é uma área de alerta.", "Revert": "Reverter", "Import from clipboard": "Importar da área de transferência", "Paste your markdown or webpage here...": "Cole seu markdown ou página web aqui...", "Clear": "Limpar", "This note is locked": "Esta nota está bloqueada", "Sorry, only owner can edit this note.": "Desculpe, somente o dono pode editar esta nota.", "OK": "OK", "Reach the limit": "Alcançou o limite", "Sorry, you've reached the max length this note can be.": "Desculpe, você alcançou o tamanho máximo que esta nota pode ter.", "Please reduce the content or divide it to more notes, thank you!": "Por favor reduza o conteúdo ou divida em mais de uma nota, obrigado!", "Import from Gist": "Importar de um Gist", "Paste your gist url here...": "Cole a URL de seu Gist aqui...", "Import from Snippet": "Importar de Snippet", "Select From Available Projects": "Selecionar de Projetos Disponíveis", "Select From Available Snippets": "Selecionar de Snippets Disponíveis", "OR": "OU", "Export to Snippet": "Exportar para Snippet", "Select Visibility Level": "Selecionar Nível de Visibilidade" } ================================================ FILE: locales/ru.json ================================================ { "Collaborative markdown notes": "Совместные markdown заметки", "Realtime collaborative markdown notes on all platforms.": "Совместные markdown заметки в режиме реального времени на всех платформах.", "Best way to write and share your knowledge in markdown.": "Лучший способ записывать свои знания и делиться ими в формате markdown.", "Intro": "Введение", "History": "История", "New guest note": "Новая гостевая заметка", "Collaborate with URL": "Сотрудничество по ссылке", "Support charts and MathJax": "Поддержка графиков и MathJax", "Support slide mode": "Поддержка режима слайдера", "Sign In": "Войти", "Below is the history from browser": "Ниже приводится история браузера", "Welcome!": "Добро пожаловать!", "New note": "Новая заметка", "or": "или", "Sign Out": "Выйти", "Explore all features": "Изучите все возможности", "Select tags...": "Выберите теги...", "Search keyword...": "Поиск...", "Sort by title": "Сортировка по заголовку", "Title": "Заголовок", "Sort by time": "Сортировка по времени", "Time": "Время", "Export history": "Экспорт истории", "Import history": "Импорт истории", "Clear history": "Очистить историю", "Refresh history": "Обновить историю", "No history": "Нет истории", "Import from browser": "Импорт из браузера", "Releases": "Релизы", "Are you sure?": "Вы уверены?", "Do you really want to delete this note?": "Вы точно хотите удалить эту заметку?", "All users will lose their connection.": "Все пользователи потеряют соединение.", "Cancel": "Отмена", "Yes, do it!": "Да, сделать это!", "Choose method": "Выберите метод", "Sign in via %s": "Войти с помощью %s", "New": "Новая", "Publish": "Опубликовать", "Extra": "Дополнительно", "Revision": "Изменения", "Slide Mode": "Режим слайдера", "Export": "Экспорт", "Import": "Импорт", "Clipboard": "Буфер обмена", "Download": "Скачать", "Raw HTML": "Raw HTML", "Edit": "Редактировать", "View": "Посмотреть", "Both": "И то и другое", "Help": "Помощь", "Upload Image": "Загрузить изображение", "Menu": "Меню", "This page need refresh": "Эту страницу необходимо обновить", "You have an incompatible client version.": "Вы используете несовместимую версию клиента.", "Refresh to update.": "Обновите страницу для обновления клиента.", "New version available!": "Доступна новая версия!", "See releases notes here": "Смотрите подробности обновлений здесь", "Refresh to enjoy new features.": "Обновите, чтобы наслаждаться новыми возможностями.", "Your user state has changed.": "Ваш аккаунт изменен.", "Refresh to load new user state.": "Обновите, чтобы загрузить изменения аккаунта.", "Refresh": "Обновить", "Contacts": "Контакты", "Report an issue": "Сообщить о проблеме", "Meet us on %s": "Познакомьтесь с нами в %s", "Send us email": "Отправить нам письмо", "Documents": "Документы", "Features": "Особенности", "YAML Metadata": "Метаданные YAML", "Slide Example": "Пример слайдера", "Cheatsheet": "Шпаргалка", "Example": "Пример", "Syntax": "Синтаксис", "Header": "Заголовок", "Unordered List": "Маркированный список", "Ordered List": "Нумерованный список", "Todo List": "Список дел", "Blockquote": "Цитата", "Bold font": "Жирный шрифт", "Italics font": "Курсив", "Strikethrough": "Зачеркнутый", "Inserted text": "Подчеркнутый текст", "Marked text": "Выделенный текст", "Link": "Ссылка", "Image": "Изображение", "Code": "Код", "Externals": "Внешнее", "This is a alert area.": "Это уведомление.", "Revert": "Отменить", "Import from clipboard": "Импорт из буфера обмена", "Paste your markdown or webpage here...": "Вставьте ваш markdown код или веб-страницу здесь...", "Clear": "Очистить", "This note is locked": "Эта заметка заблокирована", "Sorry, only owner can edit this note.": "К сожалению, только автор может редактировать эту заметку.", "OK": "OK", "Reach the limit": "Вы достигли лимита", "Sorry, you've reached the max length this note can be.": "К сожалению, вы достигли максимальной длины заметки.", "Please reduce the content or divide it to more notes, thank you!": "Пожалуйста, уменьшите размер содержимого или разделите его на несколько заметок, спасибо!", "Import from Gist": "Импорт из Gist", "Paste your gist url here...": "Вставьте ссылку на ваш gist здесь...", "Import from Snippet": "Импорт фрагмента кода", "Select From Available Projects": "Выберите из доступных проектов", "Select From Available Snippets": "Выберите из доступных фрагментов кода", "OR": "ИЛИ", "Export to Snippet": "Экспорт фрагмента кода", "Select Visibility Level": "Выберите уровень видимости", "Night Theme": "Тёмная тема", "Follow us on %s and %s.": "Подпишитесь на нас в %s и %s.", "Privacy": "Безопасность", "Terms of Use": "Условия использования", "Do you really want to delete your user account?": "Вы точно хотите удалить свою учётную запись?", "This will delete your account, all notes that are owned by you and remove all references to your account from other notes.": "Это действие удалит вашу учётную запись, все ваши заметки и удалит все ссылки на вашу учетную запись из других заметок.", "Delete user": "Удалить пользователя", "Export user data": "Экспортировать данные пользователя", "Help us translating on %s": "Помогите нам перевести %s", "Source Code": "Исходный код" } ================================================ FILE: locales/sr.json ================================================ { "Collaborative markdown notes": "Дељене белешке у Markdown формату", "Realtime collaborative markdown notes on all platforms.": "Заједнички рад на markdown тексту у реалном времену, на свим платформама", "Best way to write and share your knowledge in markdown.": "Савршен начин за писање и дељење знања у markdown формату", "Intro": "Увод", "History": "Историја", "New guest note": "Нова белешка госта", "Collaborate with URL": "Сарадња уз помоћ URL-а", "Support charts and MathJax": "Подршка за графиконе и MathJax", "Support slide mode": "Подршка за слајдове и презентације", "Sign In": "Пријави се", "Below is the history from browser": "Ниже је историјат преузет из прегледача", "Welcome!": "Добродошли!", "New note": "Нова белешка", "or": "или", "Sign Out": "Одјави се", "Explore all features": "Истражи све могућности", "Select tags...": "Одабери тагове...", "Search keyword...": "Претрага по кључној речи...", "Sort by title": "Редослед по наслову", "Title": "Наслов", "Sort by time": "Редослед по времену", "Time": "време", "Export history": "Извези историјат", "Import history": "Увези историјат", "Clear history": "Очисти историју", "Refresh history": "Освежи историју", "No history": "Нема историје", "Import from browser": "Увези из прегледача", "Releases": "Издања", "Are you sure?": "Јесте ли сигурни?", "Do you really want to delete this note?": "Да ли заиста желите да обришете ову белешку?", "All users will lose their connection.": "Сви корисници ће изгубити везу у реалном времену.", "Cancel": "Одустани", "Yes, do it!": "Да, уради!", "Choose method": "Изаберите начин", "Sign in via %s": "Пријавите се помоћу %s", "New": "Ново", "Publish": "Објави", "Extra": "Додатно", "Revision": "Ревизија", "Slide Mode": "Презентациони мод", "Export": "Извоз", "Import": "Увоз", "Clipboard": "Клипборд", "Download": "Преузимање", "Raw HTML": "Сирови HTML", "Edit": "Измени", "View": "Прегледај", "Both": "Обоје", "Help": "Помоћ", "Upload Image": "Пошаљи слику", "Menu": "Мени", "This page need refresh": "Ову страну је неопходно освежити", "You have an incompatible client version.": "Ова верзија клијента није компатибилна.", "Refresh to update.": "Освежите за приказ измена.", "New version available!": "Доступна је нова верзија!", "See releases notes here": "Овде погледајте напомене о издањима", "Refresh to enjoy new features.": "Освежите како бисте уживали у новим функцијама.", "Your user state has changed.": "Ваше корисничко стање се променило.", "Refresh to load new user state.": "Освежите за учитавање новог корисничког стања.", "Refresh": "Освежи", "Contacts": "Контакти", "Report an issue": "Пријава проблема", "Meet us on %s": "Пронађите нас на %s", "Send us email": "Пошаљите нам имејл", "Documents": "Документи", "Features": "Могућности", "YAML Metadata": "YAML Метаподаци", "Slide Example": "Пример слајда", "Cheatsheet": "Трикови и форе", "Example": "Пример", "Syntax": "Синтакса", "Header": "Заглавље", "Unordered List": "Неуређени списак", "Ordered List": "Уређени списак", "Todo List": "Списак обавеза", "Blockquote": "Пасус са наводима", "Bold font": "Масна слова", "Italics font": "Закривљена слова", "Strikethrough": "Прецртано", "Inserted text": "Уметнут текст", "Marked text": "Означени текст", "Link": "Линк", "Image": "Слика", "Code": "Код", "Externals": "Спољни", "This is a alert area.": "Ово је пасус за упозорења.", "Revert": "Врати", "Import from clipboard": "Увези из клипборда", "Paste your markdown or webpage here...": "Залепи свој markdown или веб страну овде...", "Clear": "Очисти", "This note is locked": "Ова белешка је закључана", "Sorry, only owner can edit this note.": "Жао нам је, ову белешку може мењати само њен власник.", "OK": "OK", "Reach the limit": "Досегни лимит", "Sorry, you've reached the max length this note can be.": "Нажалост, досегли сте максималну дужину ове белешке.", "Please reduce the content or divide it to more notes, thank you!": "Молимо Вас да смањите количину текста или да га поделите на више белешки, хвала!", "Import from Gist": "Увези из Github Gist-а", "Paste your gist url here...": "Залепите Gist URL адресу овде...", "Import from Snippet": "Увези из \"исечака\"", "Select From Available Projects": "Изабери из доступних пројеката", "Select From Available Snippets": "Изабери из доступних исечака", "OR": "ИЛИ", "Export to Snippet": "Извези у \"исечак\"", "Select Visibility Level": "Изаберите ниво читкости", "Night Theme": "Ноћна тема", "Follow us on %s and %s.": "Пратите нас на %s и %s.", "Privacy": "Приватност", "Terms of Use": "Услови коришћења", "Do you really want to delete your user account?": "Да ли заиста желите да трајно обришете свој налог?", "This will delete your account, all notes that are owned by you and remove all references to your account from other notes.": "Ова операција ће избрисати ваш налог, све ваше белешке, а уклониће и све везе ка вашем налогу из других белешки.", "Delete user": "Брисање корисника", "Export user data": "Извоз свих корисничких података", "Help us translating on %s": "Помозите нам да преведемо на %s", "Source Code": "Изворни код" } ================================================ FILE: locales/sv.json ================================================ { "Collaborative markdown notes": "Kollaborativa markdownanteckningar", "Realtime collaborative markdown notes on all platforms.": "Kollaborativa markdownantackningar på alla plattformar.", "Best way to write and share your knowledge in markdown.": "Bästa sättet att skriva och dela din kunskap i markdown.", "Intro": "Intro", "History": "Historia", "New guest note": "Ny gästanteckning", "Collaborate with URL": "Samarbeta med URL", "Support charts and MathJax": "Stöd för diagram och MathJax", "Support slide mode": "Stöd för slide mode", "Sign In": "Logga in", "Below is the history from browser": "Nedanför finns historia från webbläsaren", "Welcome!": "Välkommen!", "New note": "Ny anteckning", "or": "eller", "Sign Out": "Logga ut", "Explore all features": "Upptäck alla funktioner", "Select tags...": "Välj taggar...", "Search keyword...": "Sök nyckelord...", "Sort by title": "Sortera titlar", "Title": "Titel", "Sort by time": "Sortera kronologiskt", "Time": "Tid", "Export history": "Exporthistorik", "Import history": "Importhistorik", "Clear history": "Rensa historik", "Refresh history": "Uppdatera historik", "No history": "Ingen historik", "Import from browser": "Importera från webbläsare", "Releases": "Lanseringar", "Are you sure?": "Är du säker?", "Cancel": "Avbryt", "Yes, do it!": "Ja, gör det!", "Choose method": "Välj metod", "Sign in via %s": "Logga in via %s", "New": "Ny", "Publish": "Publicera", "Extra": "Extra", "Revision": "Revision", "Slide Mode": "Slide Mode", "Export": "Exportera", "Import": "Importera", "Clipboard": "Urklipp", "Download": "Ladda ner", "Raw HTML": "Rå HTML", "Edit": "Redigera", "View": "Visa", "Both": "Båda", "Help": "Hjälp", "Upload Image": "Ladda upp bilder", "Menu": "Meny", "This page need refresh": "Den här sidan behöver laddas om", "You have an incompatible client version.": "Du har en inkompatibel klientversion.", "Refresh to update.": "Ladda om för att uppdatera.", "New version available!": "Ny version tillgänglig!", "See releases notes here": "Se releaseanteckningar här", "Refresh to enjoy new features.": "Ladda om för att använda de nya funktionerna.", "Your user state has changed.": "Din användarstatus har förändrats.", "Refresh to load new user state.": "Ladda om för att ladda ny användarstatus.", "Refresh": "Ladda om", "Contacts": "Kontakter", "Report an issue": "Rapportera ett fel", "Send us email": "Skicka e-post till oss", "Documents": "Dokument", "Features": "Funktioner", "YAML Metadata": "YAML Metadata", "Slide Example": "Slideexempel", "Cheatsheet": "Cheatsheet", "Example": "Exempel", "Syntax": "Syntax", "Header": "Huvud", "Unordered List": "Oordnad lists", "Ordered List": "Ordnad lista", "Todo List": "Todo-lista", "Blockquote": "Blockcitat", "Bold font": "Fet stil", "Italics font": "Kursiv stil", "Strikethrough": "Genomstrykning", "Inserted text": "Insatt text", "Marked text": "Markerad text", "Link": "Länk", "Image": "Bild", "Code": "Kod", "Externals": "Externa", "This is a alert area.": "Det här är ett varnande område.", "Revert": "Återgå", "Import from clipboard": "Importera från urklipp", "Paste your markdown or webpage here...": "Klipp in din markdown eller hemsida här...", "Clear": "Rensa", "This note is locked": "Anteckningen är låst", "Sorry, only owner can edit this note.": "Ursäkta, men endast ägaren kan redigera den här anteckningen.", "OK": "Okej", "Reach the limit": "Nå gränsen", "Sorry, you've reached the max length this note can be.": "Usräkta, men duhar nått maxlängden för vad en anteckning får vara.", "Please reduce the content or divide it to more notes, thank you!": "Var vänlig förkorta innehållet eller dela upp det i flera anteckningar, tack!", "Import from Gist": "Importera från Gist", "Paste your gist url here...": "Klipp in din gist-url här...", "Import from Snippet": "Importera från Snippet", "Select From Available Projects": "Välj från tillgängliga projekt", "Select From Available Snippets": "Välj från tillgängliga Snippets", "OR": "ELLER", "Export to Snippet": "Exportera till Snippet", "Select Visibility Level": "Välj synlighetsnivå" } ================================================ FILE: locales/tr.json ================================================ { "Collaborative markdown notes": "Ortak markdown notları", "Realtime collaborative markdown notes on all platforms.": "Tüm platformlarda gerçek zamanlı markdown notları", "Best way to write and share your knowledge in markdown.": "Markdownda bilginizi paylaşmanın en kolay yolu.", "Intro": "Giriş", "History": "Geçmiş", "New guest note": "Yeni kullanıcı notu", "Collaborate with URL": "URL ile katkıda bulun", "Support charts and MathJax": "Grafikler ve MathJax'ı destekle", "Support slide mode": "Sunum modunu destekle", "Sign In": "Kaydol", "Below is the history from browser": "Aşağıda tarayıcınızın geçmişi var", "Welcome!": "Hoşgeldiniz!", "New note": "Yeni not", "or": "veya", "Sign Out": "Çıkış Yap", "Explore all features": "Özellikleri keşfet", "Select tags...": "Etiketleri seçin...", "Search keyword...": "Anahtar kelimeleri arayın...", "Sort by title": "Başlığa göre sırala", "Title": "Başlık", "Sort by time": "Zamana göre sırala", "Time": "Zaman", "Export history": "Geçmişe dışa aktar", "Import history": "Geçmişi içe aktar", "Clear history": "Geçmişi temizle", "Refresh history": "Geçmişi yenile", "No history": "Geçmiş yok", "Import from browser": "Tarayıcıdan içe aktar", "Releases": "Sürümler", "Are you sure?": "Emin misiniz?", "Cancel": "İptal", "Yes, do it!": "Evet, devam et!", "Choose method": "Metot seçin", "Sign in via %s": "%s ile giriş yapın", "New": "Yeni", "Publish": "Yayınla", "Extra": "Ekstra", "Revision": "Sürüm", "Slide Mode": "Slayt Modu", "Export": "Dışa Aktar", "Import": "İçe Aktar", "Clipboard": "Pano", "Download": "İndir", "Raw HTML": "Kaynak HTML", "Edit": "Düzenle", "View": "İncele", "Both": "İkisi de", "Help": "Yardım", "Upload Image": "Resim Yükle", "Menu": "Menü", "This page need refresh": "Bu sayfayı yeniden yüklemek lazım", "You have an incompatible client version.": "Yerel uygulamanız uyumlu olmayan bir sürümde.", "Refresh to update.": "Güncellemek için yenileyin.", "New version available!": "Yeni versiyon kullanıma hazır!", "See releases notes here": "Değişikliklere burdan bakabilirsiniz", "Refresh to enjoy new features.": "Yeni özelliklere yenileyerek erişebilirsiniz.", "Your user state has changed.": "Kullanıcı durumunuz değişti.", "Refresh to load new user state.": "Yeni kullanıcı durumunuzu yüklemek için yenileyin.", "Refresh": "Yenile", "Contacts": "Kişiler", "Report an issue": "Hata bildir", "Send us email": "Bize email gönderin", "Documents": "Belgeler", "Features": "Özellikler", "YAML Metadata": "YAML Özbilgisi", "Slide Example": "Slayt Örneği", "Cheatsheet": "Cheatsheet", "Example": "Örnek", "Syntax": "Sentaks", "Header": "Başlık", "Unordered List": "Sırasız Liste", "Ordered List": "Sıralı Liste", "Todo List": "Yapılacaklar Listesi", "Blockquote": "Alıntı Bloğu", "Bold font": "Kalın yazı", "Italics font": "İtalik yazı", "Strikethrough": "Üstüçizili", "Inserted text": "Eklenmiş yazı", "Marked text": "Seçili yazı", "Link": "Link", "Image": "Resim", "Code": "Kod", "Externals": "Dış veriler", "This is a alert area.": "Burası bir uyarı bölgesi.", "Revert": "Geri al", "Import from clipboard": "Panodan içe aktar", "Paste your markdown or webpage here...": "Markdown veya web sayfanızı buraya yapıştırın...", "Clear": "Temizle", "This note is locked": "Burası kilitli", "Sorry, only owner can edit this note.": "Üzgünüm, bu not sadece sahibi tarafından düzenlenebilir.", "OK": "Tamam", "Reach the limit": "Limite eriş", "Sorry, you've reached the max length this note can be.": "Üzgünüm, bu not için maksimum harf sayısına ulaştınız", "Please reduce the content or divide it to more notes, thank you!": "Lütfen notu değiştirin veya birden fazla notlara bölün, teşekkürler!", "Import from Gist": "Gist'ten içe aktar", "Paste your gist url here...": "Gist URL'nizi buraya yapıştırın...", "Import from Snippet": "Snippet'ten içe aktar", "Select From Available Projects": "Uygun Projelerden Seçin", "Select From Available Snippets": "Uygun Snippet'lerden Seçin", "OR": "VEYA", "Export to Snippet": "Snippet olarak dışa aktarın", "Select Visibility Level": "Görünebilirlik seviyesini belirleyin" } ================================================ FILE: locales/uk.json ================================================ { "Collaborative markdown notes": "Спільні примітки щодо знижок", "Realtime collaborative markdown notes on all platforms.": "Спільні примітки щодо знижок в реальному часі на всіх платформах.", "Best way to write and share your knowledge in markdown.": "Кращий спосіб, щоб записувати і ділитись своїми знаннями щодо знижок в реальному часі.", "Intro": "Вступ", "History": "Історія", "New guest note": "Примітка нового гостя", "Collaborate with URL": "Спільна робота по URL", "Support charts and MathJax": "Підтримка графіків і MathJax", "Support slide mode": "Підтримка режиму слайдера", "Sign In": "Ввійти", "Below is the history from browser": "Нижче показана історія браузера", "Welcome!": "Ласкаво просимо!", "New note": "Нова примітка", "or": "або", "Sign Out": "Вийти", "Explore all features": "Дослідити всі можливості", "Select tags...": "Вибрати теги...", "Search keyword...": "Пошук...", "Sort by title": "Сортувати по заголовку", "Title": "Заголовок", "Sort by time": "Сортувати по часу", "Time": "Час", "Export history": "Еспортувати історію", "Import history": "Імпортувати історію", "Clear history": "Очистити історію", "Refresh history": "Оновити історію", "No history": "Історія відсутня", "Import from browser": "Імпортувати з браузера", "Releases": "Релізи", "Are you sure?": "Ви впевнені?", "Cancel": "Відмінити", "Yes, do it!": "Так, зробити це!", "Choose method": "Вибрати метод", "Sign in via %s": "Увійти за допомогою %s", "New": "Нова", "Publish": "Опублікувати", "Extra": "Дотатково", "Revision": "Ревізія", "Slide Mode": "Режим слайдера", "Export": "Експорт", "Import": "Імпорт", "Clipboard": "Буфер обміну", "Download": "Завантажити", "Raw HTML": "Raw HTML", "Edit": "Редагувати", "View": "Вигляд", "Both": "Обоє", "Help": "Допомога", "Upload Image": "Завантажити зображення", "Menu": "Меню", "This page need refresh": "Цю сторінку необхідно обновити", "You have an incompatible client version.": "Ви використовуєте несумісну версію клієнта.", "Refresh to update.": "Оновіть сторінку для оновлення.", "New version available!": "Нова версія доступна!", "See releases notes here": "Огляньте деталі оновлень тут", "Refresh to enjoy new features.": "Оновіть, щоб насолоджуватись новими можливостями.", "Your user state has changed.": "Ваш акаунт змінено.", "Refresh to load new user state.": "Оновіть, щоб завантажити зміни акаунта.", "Refresh": "Оновити", "Contacts": "Контакти", "Report an issue": "Повідомити про проблему", "Send us email": "Відправити нам лист", "Documents": "Документи", "Features": "Можливості", "YAML Metadata": "Метадані YAML", "Slide Example": "Приклад слайдера", "Cheatsheet": "Шпаргалка", "Example": "Приклад", "Syntax": "Синтаксис", "Header": "Заголовок", "Unordered List": "Маркований список", "Ordered List": "Нумерований список", "Todo List": "Список завдань", "Blockquote": "Цитата", "Bold font": "Жирний шрифт", "Italics font": "Курсив", "Strikethrough": "Перекреслений", "Inserted text": "Підкреслений текст", "Marked text": "Виділений текст", "Link": "Посилання", "Image": "Зображення", "Code": "Код", "Externals": "Зовнішнє", "This is a alert area.": "Це область повідомлення.", "Revert": "Відмінити", "Import from clipboard": "Імпорт з буферу обміну", "Paste your markdown or webpage here...": "Вставте ваш markdown або веб-сторінку тут...", "Clear": "Очистити", "This note is locked": "Ця замітка заблокована", "Sorry, only owner can edit this note.": "Вибачте, лише власник може редагувати цю замітку.", "OK": "OK", "Reach the limit": "Досягнено ліміту", "Sorry, you've reached the max length this note can be.": "Нажаль, ви досягли максимальної довжини замітки.", "Please reduce the content or divide it to more notes, thank you!": "Будь-ласка, зменшіть розмір вмісту або розділіть його на декілька заміток!", "Import from Gist": "Імпортувати з Gist", "Paste your gist url here...": "Вставте посилання на ваш gist тут...", "Import from Snippet": "Імпортувати фрагмент коду", "Select From Available Projects": "Виберіть з доступних проектів", "Select From Available Snippets": "Виберіть з доступних фрагментів коду", "OR": "АБО", "Export to Snippet": "Експорт фрагменту коду", "Select Visibility Level": "Вибрати рівень видимості" } ================================================ FILE: locales/zh-CN.json ================================================ { "Collaborative markdown notes": "Markdown 协作笔记", "Realtime collaborative markdown notes on all platforms.": "使用 Markdown 的跨平台即时协作笔记。", "Best way to write and share your knowledge in markdown.": "写作与分享 Markdown 的最佳平台。", "Intro": "简介", "History": "历史", "New guest note": "新建访客笔记", "Collaborate with URL": "实时协作", "Support charts and MathJax": "支持图表与 MathJax", "Support slide mode": "支持幻灯模式", "Sign In": "登录", "Below is the history from browser": "以下为来自浏览器的历史", "Welcome!": "欢迎!", "New note": "新建笔记", "or": "或", "Sign Out": "登出", "Explore all features": "探索所有功能", "Select tags...": "选择标签...", "Search keyword...": "搜索关键字...", "Sort by title": "按标题排序", "Title": "标题", "Sort by time": "按时间排序", "Time": "时间", "Export history": "导出历史", "Import history": "导入历史", "Clear history": "清空历史", "Refresh history": "刷新历史", "No history": "无历史记录", "Import from browser": "从浏览器导入", "Releases": "版本", "Are you sure?": "您确定吗?", "Do you really want to delete this note?": "您确定要删除这篇笔记吗?", "All users will lose their connection.": "所有用户将失去连接。", "Cancel": "取消", "Yes, do it!": "是的,就这样做!", "Choose method": "选择方式", "Sign in via %s": "通过 %s 登录", "New": "新建", "Publish": "发表", "Extra": "附加功能", "Revision": "修订版本", "Slide Mode": "幻灯模式", "Export": "导出", "Import": "导入", "Clipboard": "剪贴板", "Download": "下载", "Raw HTML": "原始 HTML", "Edit": "编辑", "View": "预览", "Both": "双栏", "Help": "帮助", "Upload Image": "上传图片", "Menu": "菜单", "This page need refresh": "此页面需要刷新", "You have an incompatible client version.": "您的客户端版本不兼容。", "Refresh to update.": "刷新页面以更新。", "New version available!": "新版本可用!", "See releases notes here": "在此查看更新记录", "Refresh to enjoy new features.": "刷新页面以体验新功能。", "Your user state has changed.": "您的用户状态已变更。", "Refresh to load new user state.": "刷新页面以加载新的用户状态。", "Refresh": "刷新", "Contacts": "联系我们", "Report an issue": "报告问题", "Meet us on %s": "在 %s 上联系我们", "Send us email": "给我们发送电子邮件", "Documents": "文档", "Features": "功能", "YAML Metadata": "YAML 元数据", "Slide Example": "幻灯范例", "Cheatsheet": "速查表", "Example": "范例", "Syntax": "语法", "Header": "标题", "Unordered List": "无序列表", "Ordered List": "有序列表", "Todo List": "清单", "Blockquote": "引用", "Bold font": "粗体", "Italics font": "斜体", "Strikethrough": "删除线", "Inserted text": "下划线文字", "Marked text": "高亮文字", "Link": "链接", "Image": "图片", "Code": "代码", "Externals": "外部扩展", "This is a alert area.": "这是一个警告区块。", "Revert": "还原", "Import from clipboard": "从剪贴板导入", "Paste your markdown or webpage here...": "在这里粘贴 Markdown 或网页内容...", "Clear": "清除", "This note is locked": "这篇笔记已被锁定", "Sorry, only owner can edit this note.": "抱歉,只有所有者可以编辑这篇笔记。", "OK": "好的", "Reach the limit": "达到上限", "Sorry, you've reached the max length this note can be.": "抱歉,您的这篇笔记已达到可用的最大长度。", "Please reduce the content or divide it to more notes, thank you!": "请减少笔记的内容。", "Import from Gist": "从 Gist 导入", "Paste your gist url here...": "在这里粘贴 Gist 网址...", "Import from Snippet": "从 Snippet 导入", "Select From Available Projects": "从可用的项目中选择", "Select From Available Snippets": "从可用的 Snippets 中选择", "OR": "或", "Export to Snippet": "导出到 Snippet", "Select Visibility Level": "选择可见层级", "Night Theme": "夜间主题", "Follow us on %s and %s.": "在 %s 和 %s 上关注我们", "Privacy": "隐私", "Terms of Use": "使用条款", "Do you really want to delete your user account?": "您确定要删除帐户吗?", "This will delete your account, all notes that are owned by you and remove all references to your account from other notes.": "您的帐户、您所拥有的笔记、他人笔记中对您帐户的引用都将被删除。", "Delete user": "删除帐户", "Export user data": "导出用户数据", "Help us translating on %s": "在 %s 上帮我们翻译", "Source Code": "源代码", "Powered by %s": "由 %s 驱动", "Register": "注册", "Export with pandoc": "使用 Pandoc 导出", "Select output format": "选择输出格式", "CONNECTED": "已连接", "ONLINE": "在线", "OFFLINE": "离线", "Anyone can edit": "任何人都可以编辑", "Freely": "自由地", "Signed-in people can edit": "登录的人可以编辑", "Editable": "可编辑", "Signed-in people can edit (forbid guests)": "登录的人可以编辑(禁止访客)", "Limited": "受限", "Only owner can edit": "只有所有者可以编辑", "Locked": "已锁定", "Only owner can edit (forbid guests)": "只有所有者可以编辑(禁止访客)", "Protected": "受保护", "Only owner can view & edit": "只有所有者可以查看和编辑", "Private": "私人", "Delete this note": "删除这个笔记", "owned this note": "拥有这个笔记", "Expand all": "全部展开", "Collapse all": "全部折叠", "Back to top": "回到顶部", "Go to bottom": "前往底部" } ================================================ FILE: locales/zh-TW.json ================================================ { "Collaborative markdown notes": "Markdown 協作筆記", "Realtime collaborative markdown notes on all platforms.": "使用 Markdown 的跨平台即時協作筆記", "Best way to write and share your knowledge in markdown.": "使用 Markdown 寫作與分享知識的最佳方式", "Intro": "簡介", "History": "歷史記錄", "New guest note": "建立訪客筆記", "Collaborate with URL": "使用網址協作", "Support charts and MathJax": "支援圖表與 MathJax", "Support slide mode": "支援簡報模式", "Sign In": "登入", "Below is the history from browser": "以下是從瀏覽器取得的歷史記錄", "Welcome!": "歡迎!", "New note": "建立筆記", "or": "或", "Sign Out": "登出", "Explore all features": "探索所有功能", "Select tags...": "選擇標籤…", "Search keyword...": "搜尋關鍵字…", "Sort by title": "依標題排序", "Title": "標題", "Sort by time": "依時間排序", "Time": "時間", "Export history": "匯出記錄", "Import history": "匯入記錄", "Clear history": "清空記錄", "Refresh history": "重新整理記錄", "No history": "沒有記錄", "Import from browser": "從瀏覽器匯入", "Releases": "版本", "Are you sure?": "您確定嗎?", "Do you really want to delete this note?": "確定刪除這則筆記?", "All users will lose their connection.": "所有使用者將會失去連線", "Cancel": "取消", "Yes, do it!": "沒錯,就這麼做!", "Choose method": "選擇方式", "Sign in via %s": "透過 %s 登入", "New": "新增", "Publish": "發佈", "Extra": "增益", "Revision": "修訂版本", "Slide Mode": "簡報模式", "Export": "匯出", "Import": "匯入", "Clipboard": "剪貼簿", "Download": "下載", "Raw HTML": "純 HTML", "Edit": "編輯", "View": "檢視", "Both": "雙欄", "Help": "協助", "Upload Image": "上傳圖片", "Menu": "選單", "This page need refresh": "此頁面需要重新整理", "You have an incompatible client version.": "您使用的是不相容的客戶端", "Refresh to update.": "請重新整理來更新", "New version available!": "新版本來了!", "See releases notes here": "請由此查閱更新記錄", "Refresh to enjoy new features.": "請重新整理來享受最新功能", "Your user state has changed.": "您的使用者狀態已變更", "Refresh to load new user state.": "請重新整理來載入新的使用者狀態", "Refresh": "重新整理", "Contacts": "聯絡方式", "Report an issue": "回報問題", "Meet us on %s": "透過 %s 聯絡我們", "Send us email": "寄信給我們", "Documents": "文件", "Features": "功能簡介", "YAML Metadata": "YAML 中繼資料", "Slide Example": "簡報範例", "Cheatsheet": "快速簡表", "Example": "範例", "Syntax": "語法", "Header": "標題", "Unordered List": "無序清單", "Ordered List": "有序清單", "Todo List": "待辦事項", "Blockquote": "引用", "Bold font": "粗體", "Italics font": "斜體", "Strikethrough": "刪除線", "Inserted text": "插入文字", "Marked text": "標記文字", "Link": "連結", "Image": "圖片", "Code": "程式碼", "Externals": "外部", "This is a alert area.": "這是警告區塊", "Revert": "還原", "Import from clipboard": "從剪貼簿匯入", "Paste your markdown or webpage here...": "在這裡貼上 Markdown 或是網頁內容…", "Clear": "清除", "This note is locked": "此份筆記已被鎖定", "Sorry, only owner can edit this note.": "抱歉,只有擁有者可以編輯此筆記", "OK": "好的", "Reach the limit": "到達上限", "Sorry, you've reached the max length this note can be.": "抱歉,您已使用到此份筆記可用的最大長度", "Please reduce the content or divide it to more notes, thank you!": "請減少內容或是將內容切成更多筆記,謝謝!", "Import from Gist": "從 Gist 匯入", "Paste your gist url here...": "在此處貼上 gist 網址…", "Import from Snippet": "從 Snippet 匯入", "Select From Available Projects": "從可用專案選擇", "Select From Available Snippets": "從可用的 Snippets 選擇", "OR": "或是", "Export to Snippet": "匯出至 Snippet", "Select Visibility Level": "選擇可見層級", "Night Theme": "夜間主題", "Follow us on %s and %s.": "來 %s 或 %s 和我們互動吧!", "Privacy": "隱私權政策", "Terms of Use": "使用條款", "Do you really want to delete your user account?": "你確定真的想要刪除帳戶?", "This will delete your account, all notes that are owned by you and remove all references to your account from other notes.": "我們將會刪除你的帳戶、你所擁有的筆記、以及你在別人筆記裡的作者記錄。", "Delete user": "刪除使用者", "Export user data": "匯出使用者資料", "Help us translating on %s": "來 %s 幫我們翻譯", "Source Code": "原始碼", "Powered by %s": "技術支援:%s", "Register": "註冊", "Export with pandoc": "使用 pandoc 匯出", "Select output format": "選擇輸出格式", "CONNECTED": "已連接", "ONLINE": "線上", "OFFLINE": "離線", "Anyone can edit": "任何人皆可編輯", "Freely": "自由地", "Signed-in people can edit": "登入的人可以編輯", "Editable": "可編輯", "Signed-in people can edit (forbid guests)": "登入的人可以編輯(禁止訪客)", "Limited": "受限", "Only owner can edit": "僅擁有者可以編輯", "Locked": "已鎖定", "Only owner can edit (forbid guests)": "僅擁有者可以編輯(禁止訪客)", "Protected": "受保護", "Only owner can view & edit": "僅擁有者可以檢視與編輯", "Private": "私人", "Delete this note": "刪除這個筆記", "owned this note": "擁有這個筆記", "Expand all": "全部展開", "Collapse all": "全部折疊", "Back to top": "回到頂端", "Go to bottom": "前往底端" } ================================================ FILE: package.json ================================================ { "name": "codimd", "version": "2.6.0", "description": "Realtime collaborative markdown notes on all platforms.", "keywords": [ "Collaborative", "Markdown", "Notes" ], "bugs": "https://github.com/hackmdio/codimd/issues", "repository": { "type": "git", "url": "https://github.com/hackmdio/codimd.git" }, "license": "AGPL-3.0", "main": "app.js", "scripts": { "build": "cross-env NODE_ENV=production webpack --config webpack.prod.js --display errors-only -p", "dev": "webpack --config webpack.dev.js --progress --colors --watch", "doctoc": "doctoc --title='# Table of Contents' README.md", "lint": "standard", "jsonlint": "find . -type f -not -ipath \"./.devcontainer/*\" -not -ipath \"./node_modules/*\" -not -ipath \"./.vscode/*\" \\( -name \"*.json\" -o -name \"*.json.*\" \\) | xargs -n 1 -I{} -- bash -c 'echo {}; jq . {} > /dev/null;'", "start": "sequelize db:migrate && node app.js", "mocha": "mocha --require intelli-espower-loader --exit ./test --recursive", "mocha:ci": "mocha --no-color -R dot --require intelli-espower-loader --exit ./test --recursive", "coverage": "nyc mocha --require intelli-espower-loader --exit --recursive ./test", "coverage:ci": "nyc mocha --no-color -R dot --require intelli-espower-loader --exit --recursive ./test", "test": "npm run-script lint && npm run-script jsonlint && npm run-script coverage", "test:ci": "npm run-script lint && npm run-script jsonlint && npm run-script coverage:ci", "heroku-postbuild": "npm run build && ./bin/heroku" }, "dependencies": { "@aws-sdk/client-s3-node": "0.1.0-preview.2", "@hackmd/diff-match-patch": "~1.1.3", "@hackmd/imgur": "~0.5.0", "@hackmd/lz-string": "~1.4.4", "@hackmd/meta-marked": "~0.5.0", "@hackmd/pandoc.js": "0.3.4", "@mattermost/client": "^9.2.0", "@passport-next/passport-openid": "~1.0.0", "archiver": "^7.0.1", "async": "^3.2.4", "azure-storage": "~2.10.3", "base64url": "~3.0.1", "body-parser": "^1.20.2", "chance": "~1.0.18", "cheerio": "~0.22.0", "connect-flash": "~0.1.1", "connect-session-sequelize": "~6.0.0", "cookie": "~0.4.0", "cookie-parser": "~1.4.4", "core-js": "^3.36.0", "csurf": "~1.11.0", "deep-freeze": "~0.0.1", "ejs": "~2.6.2", "express": "^4.18.2", "express-session": "~1.16.2", "formidable": "^2.1.1", "helmet": "~3.20.0", "https-proxy-agent": "^3.0.1", "i18n": "^0.15.1", "image-type": "^4.1.0", "isomorphic-fetch": "~2.2.1", "jsdom-nogyp": "~0.8.3", "lodash": "^4.17.21", "lutim": "~1.0.2", "markdown-it": "~10.0.0", "markdown-pdf": "~9.0.0", "method-override": "~3.0.0", "minimist": "^1.2.8", "minio": "^7.1.1", "moment": "^2.29.4", "morgan": "~1.9.1", "mysql": "^2.18.1", "mysql2": "^2.0.1", "passport": "^0.6.0", "passport-bitbucket-oauth2": "~0.1.2", "passport-dropbox-oauth2": "~1.1.0", "passport-facebook": "~2.1.1", "passport-github": "~1.1.0", "passport-gitlab2": "~4.0.0", "passport-google-oauth20": "~1.0.0", "passport-ldapauth": "~2.1.3", "passport-local": "~1.0.0", "passport-oauth2": "^1.6.1", "passport-saml": "~1.0.0", "passport-twitter": "~1.0.4", "passport.socketio": "~3.7.0", "pg": "~8.8.0", "pg-hstore": "~2.3.2", "prom-client": "^11.0.0", "prometheus-api-metrics": "^2.2.5", "randomcolor": "~0.5.4", "read-chunk": "^3.2.0", "readline-sync": "~1.4.7", "regenerator-runtime": "^0.14.0", "request": "~2.88.0", "scrypt-kdf": "^2.0.1", "sequelize": "5.21.13", "sequelize-cli": "~5.5.1", "shortid": "~2.2.14", "socket.io": "~2.2.0", "toobusy-js": "~0.5.1", "uuid": "~3.3.2", "validator": "~11.1.0", "winston": "^3.8.2", "ws": "~7.1.1" }, "devDependencies": { "@babel/core": "^7.24.0", "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", "@babel/plugin-transform-optional-chaining": "^7.24.0", "@babel/plugin-transform-runtime": "^7.24.0", "@babel/preset-env": "^7.24.0", "@babel/runtime": "^7.24.0", "@babel/runtime-corejs3": "^7.28.2", "@hackmd/codemirror": "^5.65.8", "@hackmd/emojify.js": "^2.1.0", "@hackmd/idle-js": "~1.0.1", "@hackmd/js-sequence-diagrams": "~0.0.1-alpha.3", "@susisu/mte-kernel": "^2.1.0", "acorn": "~6.1.1", "babel-loader": "^8.3.0", "bootstrap": "~3.4.0", "bootstrap-validator": "~0.11.8", "copy-webpack-plugin": "~4.5.2", "cross-env": "^7.0.3", "css-loader": "~1.0.0", "dictionary-de": "^2.0.3", "dictionary-de-at": "^2.0.3", "dictionary-de-ch": "^2.0.3", "dictionary-en-gb": "^2.2.2", "doctoc": "~1.4.0", "ejs-loader": "^0.5.0", "exports-loader": "~0.7.0", "expose-loader": "~0.7.5", "file-loader": "~2.0.0", "file-saver": "~2.0.2", "flowchart.js": "~1.15.0", "fork-awesome": "~1.1.7", "gist-embed": "~2.6.0", "graceful-fs": "~4.2.1", "handlebars": "^4.7.7", "highlight.js": "~9.15.9", "html-webpack-plugin": "~4.0.0-beta.2", "imports-loader": "~0.8.0", "intelli-espower-loader": "~1.0.1", "ionicons": "~2.0.1", "jquery": "^3.6.3", "jquery-mousewheel": "~3.1.13", "jquery-ui": "^1.13.2", "js-cookie": "~2.2.0", "js-yaml": "~3.13.1", "jsonlint": "~1.6.2", "keymaster": "~1.6.2", "leaflet": "~1.6.0", "less": "~3.9.0", "less-loader": "~4.1.0", "list.js": "~1.5.0", "markdown-it-abbr": "~1.0.4", "markdown-it-container": "~2.0.0", "markdown-it-deflist": "~2.0.3", "markdown-it-footnote": "~3.0.2", "markdown-it-imsize": "~2.0.1", "markdown-it-ins": "~2.0.0", "markdown-it-mark": "~2.0.0", "markdown-it-mathjax": "~2.0.0", "markdown-it-regexp": "~0.4.0", "markdown-it-ruby": "^0.1.1", "markdown-it-sub": "~1.0.0", "markdown-it-sup": "~1.0.0", "markdownlint": "^0.27.0", "markdownlint-rule-helpers": "^0.13.0", "markmap-common": "0.18.9", "markmap-lib": "0.18.12", "markmap-view": "0.18.12", "mathjax": "~2.7.5", "mermaid": "~11.5.0", "mini-css-extract-plugin": "~0.4.1", "mocha": "~5.2.0", "mock-require": "~3.0.3", "nyc": "~14.0.0", "optimize-css-assets-webpack-plugin": "~5.0.0", "papaparse": "^5.2.0", "pdfobject": "~2.2.4", "plantuml-encoder": "^1.2.5", "power-assert": "~1.6.1", "prismjs": "^1.29.0", "raphael": "~2.2.8", "reveal.js": "~3.9.2", "script-loader": "~0.7.2", "select2": "~3.5.2-browserify", "sinon": "~7.3.2", "socket.io-client": "~2.2.0", "spin.js": "~4.0.0", "standard": "~13.1.0", "store": "~2.0.12", "string-loader": "~0.0.1", "style-loader": "~0.23.1", "tedious": "~6.2.0", "turndown": "^7.1.1", "typo-js": "^1.0.3", "url-loader": "~1.0.1", "vega": "^5.25.0", "vega-embed": "~6.14.2", "vega-lite": "~4.17.0", "velocity-animate": "~1.5.2", "visibilityjs": "~2.0.2", "viz.js": "~2.1.2", "webpack": "~4.39.0", "webpack-cli": "~3.3.6", "webpack-merge": "~4.2.1", "webpack-parallel-uglify-plugin": "~1.1.0", "wurl": "~2.5.3", "xss": "~1.0.6" }, "optionalDependencies": { "bufferutil": "~4.0.0", "utf-8-validate": "~5.0.1" }, "engines": { "node": ">=14.0.0 <17.0.0" }, "maintainers": [ { "name": "Max Wu", "email": "max@hackmd.io" }, { "name": "Yukai Huang", "email": "yukai@hackmd.io" } ], "standard": { "ignore": [ "/public/build", "/public/vendor", "/lib/ot", "webpack.*" ] }, "nyc": { "all": true, "include": [ "app.js", "lib/**/*.js" ], "reporter": "lcov" } } ================================================ FILE: public/.eslintrc.js ================================================ // this config file is used in concert with the root .eslintrc.js in the root dir. module.exports = { "env": { "browser": true, "es6": true }, "parserOptions": { "ecmaVersion": 2018, "sourceType": "module" }, "globals": { "$": false, "CodeMirror": false, "Cookies": false, "moment": false, "editor": false, "ui": false, "modeType": false, "serverurl": false, "key": false, "gapi": false, "Dropbox": false, "FilePicker": false, "ot": false, "MediaUploader": false, "hex2rgb": false, "num_loaded": false, "Visibility": false, "inlineAttachment": false }, "rules": { "no-unused-vars": "warn" } }; ================================================ FILE: public/css/bootstrap-social.css ================================================ /* * Social Buttons for Bootstrap * * Copyright 2013-2014 Panayiotis Lipiridis * Licensed under the MIT License * * https://github.com/lipis/bootstrap-social */ .btn-social{position:relative;padding-left:44px;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.btn-social>:first-child{position:absolute;left:0;top:0;bottom:0;width:32px;line-height:34px;font-size:1.6em;text-align:center;border-right:1px solid rgba(0,0,0,0.2)} .btn-social.btn-lg{padding-left:61px}.btn-social.btn-lg :first-child{line-height:45px;width:45px;font-size:1.8em} .btn-social.btn-sm{padding-left:38px}.btn-social.btn-sm :first-child{line-height:28px;width:28px;font-size:1.4em} .btn-social.btn-xs{padding-left:30px}.btn-social.btn-xs :first-child{line-height:20px;width:20px;font-size:1.2em} .btn-social-icon{position:relative;padding-left:44px;text-align:left;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;height:34px;width:34px;padding:0}.btn-social-icon>:first-child{position:absolute;left:0;top:0;bottom:0;width:32px;line-height:34px;font-size:1.6em;text-align:center;border-right:1px solid rgba(0,0,0,0.2)} .btn-social-icon.btn-lg{padding-left:61px}.btn-social-icon.btn-lg :first-child{line-height:45px;width:45px;font-size:1.8em} .btn-social-icon.btn-sm{padding-left:38px}.btn-social-icon.btn-sm :first-child{line-height:28px;width:28px;font-size:1.4em} .btn-social-icon.btn-xs{padding-left:30px}.btn-social-icon.btn-xs :first-child{line-height:20px;width:20px;font-size:1.2em} .btn-social-icon :first-child{border:none;text-align:center;width:100% !important} .btn-social-icon.btn-lg{height:45px;width:45px;padding-left:0;padding-right:0} .btn-social-icon.btn-sm{height:30px;width:30px;padding-left:0;padding-right:0} .btn-social-icon.btn-xs{height:22px;width:22px;padding-left:0;padding-right:0} .btn-adn{color:#fff;background-color:#d87a68;border-color:rgba(0,0,0,0.2)}.btn-adn:hover,.btn-adn:focus,.btn-adn:active,.btn-adn.active,.open>.dropdown-toggle.btn-adn{color:#fff;background-color:#ce563f;border-color:rgba(0,0,0,0.2)} .btn-adn:active,.btn-adn.active,.open>.dropdown-toggle.btn-adn{background-image:none} .btn-adn.disabled,.btn-adn[disabled],fieldset[disabled] .btn-adn,.btn-adn.disabled:hover,.btn-adn[disabled]:hover,fieldset[disabled] .btn-adn:hover,.btn-adn.disabled:focus,.btn-adn[disabled]:focus,fieldset[disabled] .btn-adn:focus,.btn-adn.disabled:active,.btn-adn[disabled]:active,fieldset[disabled] .btn-adn:active,.btn-adn.disabled.active,.btn-adn[disabled].active,fieldset[disabled] .btn-adn.active{background-color:#d87a68;border-color:rgba(0,0,0,0.2)} .btn-adn .badge{color:#d87a68;background-color:#fff} .btn-bitbucket{color:#fff;background-color:#205081;border-color:rgba(0,0,0,0.2)}.btn-bitbucket:hover,.btn-bitbucket:focus,.btn-bitbucket:active,.btn-bitbucket.active,.open>.dropdown-toggle.btn-bitbucket{color:#fff;background-color:#163758;border-color:rgba(0,0,0,0.2)} .btn-bitbucket:active,.btn-bitbucket.active,.open>.dropdown-toggle.btn-bitbucket{background-image:none} .btn-bitbucket.disabled,.btn-bitbucket[disabled],fieldset[disabled] .btn-bitbucket,.btn-bitbucket.disabled:hover,.btn-bitbucket[disabled]:hover,fieldset[disabled] .btn-bitbucket:hover,.btn-bitbucket.disabled:focus,.btn-bitbucket[disabled]:focus,fieldset[disabled] .btn-bitbucket:focus,.btn-bitbucket.disabled:active,.btn-bitbucket[disabled]:active,fieldset[disabled] .btn-bitbucket:active,.btn-bitbucket.disabled.active,.btn-bitbucket[disabled].active,fieldset[disabled] .btn-bitbucket.active{background-color:#205081;border-color:rgba(0,0,0,0.2)} .btn-bitbucket .badge{color:#205081;background-color:#fff} .btn-dropbox{color:#fff;background-color:#1087dd;border-color:rgba(0,0,0,0.2)}.btn-dropbox:hover,.btn-dropbox:focus,.btn-dropbox:active,.btn-dropbox.active,.open>.dropdown-toggle.btn-dropbox{color:#fff;background-color:#0d6aad;border-color:rgba(0,0,0,0.2)} .btn-dropbox:active,.btn-dropbox.active,.open>.dropdown-toggle.btn-dropbox{background-image:none} .btn-dropbox.disabled,.btn-dropbox[disabled],fieldset[disabled] .btn-dropbox,.btn-dropbox.disabled:hover,.btn-dropbox[disabled]:hover,fieldset[disabled] .btn-dropbox:hover,.btn-dropbox.disabled:focus,.btn-dropbox[disabled]:focus,fieldset[disabled] .btn-dropbox:focus,.btn-dropbox.disabled:active,.btn-dropbox[disabled]:active,fieldset[disabled] .btn-dropbox:active,.btn-dropbox.disabled.active,.btn-dropbox[disabled].active,fieldset[disabled] .btn-dropbox.active{background-color:#1087dd;border-color:rgba(0,0,0,0.2)} .btn-dropbox .badge{color:#1087dd;background-color:#fff} .btn-facebook{color:#fff;background-color:#3b5998;border-color:rgba(0,0,0,0.2)}.btn-facebook:hover,.btn-facebook:focus,.btn-facebook:active,.btn-facebook.active,.open>.dropdown-toggle.btn-facebook{color:#fff;background-color:#2d4373;border-color:rgba(0,0,0,0.2)} .btn-facebook:active,.btn-facebook.active,.open>.dropdown-toggle.btn-facebook{background-image:none} .btn-facebook.disabled,.btn-facebook[disabled],fieldset[disabled] .btn-facebook,.btn-facebook.disabled:hover,.btn-facebook[disabled]:hover,fieldset[disabled] .btn-facebook:hover,.btn-facebook.disabled:focus,.btn-facebook[disabled]:focus,fieldset[disabled] .btn-facebook:focus,.btn-facebook.disabled:active,.btn-facebook[disabled]:active,fieldset[disabled] .btn-facebook:active,.btn-facebook.disabled.active,.btn-facebook[disabled].active,fieldset[disabled] .btn-facebook.active{background-color:#3b5998;border-color:rgba(0,0,0,0.2)} .btn-facebook .badge{color:#3b5998;background-color:#fff} .btn-flickr{color:#fff;background-color:#ff0084;border-color:rgba(0,0,0,0.2)}.btn-flickr:hover,.btn-flickr:focus,.btn-flickr:active,.btn-flickr.active,.open>.dropdown-toggle.btn-flickr{color:#fff;background-color:#cc006a;border-color:rgba(0,0,0,0.2)} .btn-flickr:active,.btn-flickr.active,.open>.dropdown-toggle.btn-flickr{background-image:none} .btn-flickr.disabled,.btn-flickr[disabled],fieldset[disabled] .btn-flickr,.btn-flickr.disabled:hover,.btn-flickr[disabled]:hover,fieldset[disabled] .btn-flickr:hover,.btn-flickr.disabled:focus,.btn-flickr[disabled]:focus,fieldset[disabled] .btn-flickr:focus,.btn-flickr.disabled:active,.btn-flickr[disabled]:active,fieldset[disabled] .btn-flickr:active,.btn-flickr.disabled.active,.btn-flickr[disabled].active,fieldset[disabled] .btn-flickr.active{background-color:#ff0084;border-color:rgba(0,0,0,0.2)} .btn-flickr .badge{color:#ff0084;background-color:#fff} .btn-foursquare{color:#fff;background-color:#f94877;border-color:rgba(0,0,0,0.2)}.btn-foursquare:hover,.btn-foursquare:focus,.btn-foursquare:active,.btn-foursquare.active,.open>.dropdown-toggle.btn-foursquare{color:#fff;background-color:#f71752;border-color:rgba(0,0,0,0.2)} .btn-foursquare:active,.btn-foursquare.active,.open>.dropdown-toggle.btn-foursquare{background-image:none} .btn-foursquare.disabled,.btn-foursquare[disabled],fieldset[disabled] .btn-foursquare,.btn-foursquare.disabled:hover,.btn-foursquare[disabled]:hover,fieldset[disabled] .btn-foursquare:hover,.btn-foursquare.disabled:focus,.btn-foursquare[disabled]:focus,fieldset[disabled] .btn-foursquare:focus,.btn-foursquare.disabled:active,.btn-foursquare[disabled]:active,fieldset[disabled] .btn-foursquare:active,.btn-foursquare.disabled.active,.btn-foursquare[disabled].active,fieldset[disabled] .btn-foursquare.active{background-color:#f94877;border-color:rgba(0,0,0,0.2)} .btn-foursquare .badge{color:#f94877;background-color:#fff} .btn-github{color:#fff;background-color:#444;border-color:rgba(0,0,0,0.2)}.btn-github:hover,.btn-github:focus,.btn-github:active,.btn-github.active,.open>.dropdown-toggle.btn-github{color:#fff;background-color:#2b2b2b;border-color:rgba(0,0,0,0.2)} .btn-github:active,.btn-github.active,.open>.dropdown-toggle.btn-github{background-image:none} .btn-github.disabled,.btn-github[disabled],fieldset[disabled] .btn-github,.btn-github.disabled:hover,.btn-github[disabled]:hover,fieldset[disabled] .btn-github:hover,.btn-github.disabled:focus,.btn-github[disabled]:focus,fieldset[disabled] .btn-github:focus,.btn-github.disabled:active,.btn-github[disabled]:active,fieldset[disabled] .btn-github:active,.btn-github.disabled.active,.btn-github[disabled].active,fieldset[disabled] .btn-github.active{background-color:#444;border-color:rgba(0,0,0,0.2)} .btn-github .badge{color:#444;background-color:#fff} .btn-google{color:#fff;background-color:#dd4b39;border-color:rgba(0,0,0,0.2)}.btn-google:hover,.btn-google:focus,.btn-google:active,.btn-google.active,.open>.dropdown-toggle.btn-google{color:#fff;background-color:#c23321;border-color:rgba(0,0,0,0.2)} .btn-google:active,.btn-google.active,.open>.dropdown-toggle.btn-google{background-image:none} .btn-google.disabled,.btn-google[disabled],fieldset[disabled] .btn-google,.btn-google.disabled:hover,.btn-google[disabled]:hover,fieldset[disabled] .btn-google:hover,.btn-google.disabled:focus,.btn-google[disabled]:focus,fieldset[disabled] .btn-google:focus,.btn-google.disabled:active,.btn-google[disabled]:active,fieldset[disabled] .btn-google:active,.btn-google.disabled.active,.btn-google[disabled].active,fieldset[disabled] .btn-google.active{background-color:#dd4b39;border-color:rgba(0,0,0,0.2)} .btn-google .badge{color:#dd4b39;background-color:#fff} .btn-instagram{color:#fff;background-color:#3f729b;border-color:rgba(0,0,0,0.2)}.btn-instagram:hover,.btn-instagram:focus,.btn-instagram:active,.btn-instagram.active,.open>.dropdown-toggle.btn-instagram{color:#fff;background-color:#305777;border-color:rgba(0,0,0,0.2)} .btn-instagram:active,.btn-instagram.active,.open>.dropdown-toggle.btn-instagram{background-image:none} .btn-instagram.disabled,.btn-instagram[disabled],fieldset[disabled] .btn-instagram,.btn-instagram.disabled:hover,.btn-instagram[disabled]:hover,fieldset[disabled] .btn-instagram:hover,.btn-instagram.disabled:focus,.btn-instagram[disabled]:focus,fieldset[disabled] .btn-instagram:focus,.btn-instagram.disabled:active,.btn-instagram[disabled]:active,fieldset[disabled] .btn-instagram:active,.btn-instagram.disabled.active,.btn-instagram[disabled].active,fieldset[disabled] .btn-instagram.active{background-color:#3f729b;border-color:rgba(0,0,0,0.2)} .btn-instagram .badge{color:#3f729b;background-color:#fff} .btn-linkedin{color:#fff;background-color:#007bb6;border-color:rgba(0,0,0,0.2)}.btn-linkedin:hover,.btn-linkedin:focus,.btn-linkedin:active,.btn-linkedin.active,.open>.dropdown-toggle.btn-linkedin{color:#fff;background-color:#005983;border-color:rgba(0,0,0,0.2)} .btn-linkedin:active,.btn-linkedin.active,.open>.dropdown-toggle.btn-linkedin{background-image:none} .btn-linkedin.disabled,.btn-linkedin[disabled],fieldset[disabled] .btn-linkedin,.btn-linkedin.disabled:hover,.btn-linkedin[disabled]:hover,fieldset[disabled] .btn-linkedin:hover,.btn-linkedin.disabled:focus,.btn-linkedin[disabled]:focus,fieldset[disabled] .btn-linkedin:focus,.btn-linkedin.disabled:active,.btn-linkedin[disabled]:active,fieldset[disabled] .btn-linkedin:active,.btn-linkedin.disabled.active,.btn-linkedin[disabled].active,fieldset[disabled] .btn-linkedin.active{background-color:#007bb6;border-color:rgba(0,0,0,0.2)} .btn-linkedin .badge{color:#007bb6;background-color:#fff} .btn-microsoft{color:#fff;background-color:#2672ec;border-color:rgba(0,0,0,0.2)}.btn-microsoft:hover,.btn-microsoft:focus,.btn-microsoft:active,.btn-microsoft.active,.open>.dropdown-toggle.btn-microsoft{color:#fff;background-color:#125acd;border-color:rgba(0,0,0,0.2)} .btn-microsoft:active,.btn-microsoft.active,.open>.dropdown-toggle.btn-microsoft{background-image:none} .btn-microsoft.disabled,.btn-microsoft[disabled],fieldset[disabled] .btn-microsoft,.btn-microsoft.disabled:hover,.btn-microsoft[disabled]:hover,fieldset[disabled] .btn-microsoft:hover,.btn-microsoft.disabled:focus,.btn-microsoft[disabled]:focus,fieldset[disabled] .btn-microsoft:focus,.btn-microsoft.disabled:active,.btn-microsoft[disabled]:active,fieldset[disabled] .btn-microsoft:active,.btn-microsoft.disabled.active,.btn-microsoft[disabled].active,fieldset[disabled] .btn-microsoft.active{background-color:#2672ec;border-color:rgba(0,0,0,0.2)} .btn-microsoft .badge{color:#2672ec;background-color:#fff} .btn-openid{color:#fff;background-color:#f7931e;border-color:rgba(0,0,0,0.2)}.btn-openid:hover,.btn-openid:focus,.btn-openid:active,.btn-openid.active,.open>.dropdown-toggle.btn-openid{color:#fff;background-color:#da7908;border-color:rgba(0,0,0,0.2)} .btn-openid:active,.btn-openid.active,.open>.dropdown-toggle.btn-openid{background-image:none} .btn-openid.disabled,.btn-openid[disabled],fieldset[disabled] .btn-openid,.btn-openid.disabled:hover,.btn-openid[disabled]:hover,fieldset[disabled] .btn-openid:hover,.btn-openid.disabled:focus,.btn-openid[disabled]:focus,fieldset[disabled] .btn-openid:focus,.btn-openid.disabled:active,.btn-openid[disabled]:active,fieldset[disabled] .btn-openid:active,.btn-openid.disabled.active,.btn-openid[disabled].active,fieldset[disabled] .btn-openid.active{background-color:#f7931e;border-color:rgba(0,0,0,0.2)} .btn-openid .badge{color:#f7931e;background-color:#fff} .btn-pinterest{color:#fff;background-color:#cb2027;border-color:rgba(0,0,0,0.2)}.btn-pinterest:hover,.btn-pinterest:focus,.btn-pinterest:active,.btn-pinterest.active,.open>.dropdown-toggle.btn-pinterest{color:#fff;background-color:#9f191f;border-color:rgba(0,0,0,0.2)} .btn-pinterest:active,.btn-pinterest.active,.open>.dropdown-toggle.btn-pinterest{background-image:none} .btn-pinterest.disabled,.btn-pinterest[disabled],fieldset[disabled] .btn-pinterest,.btn-pinterest.disabled:hover,.btn-pinterest[disabled]:hover,fieldset[disabled] .btn-pinterest:hover,.btn-pinterest.disabled:focus,.btn-pinterest[disabled]:focus,fieldset[disabled] .btn-pinterest:focus,.btn-pinterest.disabled:active,.btn-pinterest[disabled]:active,fieldset[disabled] .btn-pinterest:active,.btn-pinterest.disabled.active,.btn-pinterest[disabled].active,fieldset[disabled] .btn-pinterest.active{background-color:#cb2027;border-color:rgba(0,0,0,0.2)} .btn-pinterest .badge{color:#cb2027;background-color:#fff} .btn-reddit{color:#000;background-color:#eff7ff;border-color:rgba(0,0,0,0.2)}.btn-reddit:hover,.btn-reddit:focus,.btn-reddit:active,.btn-reddit.active,.open>.dropdown-toggle.btn-reddit{color:#000;background-color:#bcddff;border-color:rgba(0,0,0,0.2)} .btn-reddit:active,.btn-reddit.active,.open>.dropdown-toggle.btn-reddit{background-image:none} .btn-reddit.disabled,.btn-reddit[disabled],fieldset[disabled] .btn-reddit,.btn-reddit.disabled:hover,.btn-reddit[disabled]:hover,fieldset[disabled] .btn-reddit:hover,.btn-reddit.disabled:focus,.btn-reddit[disabled]:focus,fieldset[disabled] .btn-reddit:focus,.btn-reddit.disabled:active,.btn-reddit[disabled]:active,fieldset[disabled] .btn-reddit:active,.btn-reddit.disabled.active,.btn-reddit[disabled].active,fieldset[disabled] .btn-reddit.active{background-color:#eff7ff;border-color:rgba(0,0,0,0.2)} .btn-reddit .badge{color:#eff7ff;background-color:#000} .btn-soundcloud{color:#fff;background-color:#f50;border-color:rgba(0,0,0,0.2)}.btn-soundcloud:hover,.btn-soundcloud:focus,.btn-soundcloud:active,.btn-soundcloud.active,.open>.dropdown-toggle.btn-soundcloud{color:#fff;background-color:#c40;border-color:rgba(0,0,0,0.2)} .btn-soundcloud:active,.btn-soundcloud.active,.open>.dropdown-toggle.btn-soundcloud{background-image:none} .btn-soundcloud.disabled,.btn-soundcloud[disabled],fieldset[disabled] .btn-soundcloud,.btn-soundcloud.disabled:hover,.btn-soundcloud[disabled]:hover,fieldset[disabled] .btn-soundcloud:hover,.btn-soundcloud.disabled:focus,.btn-soundcloud[disabled]:focus,fieldset[disabled] .btn-soundcloud:focus,.btn-soundcloud.disabled:active,.btn-soundcloud[disabled]:active,fieldset[disabled] .btn-soundcloud:active,.btn-soundcloud.disabled.active,.btn-soundcloud[disabled].active,fieldset[disabled] .btn-soundcloud.active{background-color:#f50;border-color:rgba(0,0,0,0.2)} .btn-soundcloud .badge{color:#f50;background-color:#fff} .btn-tumblr{color:#fff;background-color:#2c4762;border-color:rgba(0,0,0,0.2)}.btn-tumblr:hover,.btn-tumblr:focus,.btn-tumblr:active,.btn-tumblr.active,.open>.dropdown-toggle.btn-tumblr{color:#fff;background-color:#1c2d3f;border-color:rgba(0,0,0,0.2)} .btn-tumblr:active,.btn-tumblr.active,.open>.dropdown-toggle.btn-tumblr{background-image:none} .btn-tumblr.disabled,.btn-tumblr[disabled],fieldset[disabled] .btn-tumblr,.btn-tumblr.disabled:hover,.btn-tumblr[disabled]:hover,fieldset[disabled] .btn-tumblr:hover,.btn-tumblr.disabled:focus,.btn-tumblr[disabled]:focus,fieldset[disabled] .btn-tumblr:focus,.btn-tumblr.disabled:active,.btn-tumblr[disabled]:active,fieldset[disabled] .btn-tumblr:active,.btn-tumblr.disabled.active,.btn-tumblr[disabled].active,fieldset[disabled] .btn-tumblr.active{background-color:#2c4762;border-color:rgba(0,0,0,0.2)} .btn-tumblr .badge{color:#2c4762;background-color:#fff} .btn-twitter{color:#fff;background-color:#55acee;border-color:rgba(0,0,0,0.2)}.btn-twitter:hover,.btn-twitter:focus,.btn-twitter:active,.btn-twitter.active,.open>.dropdown-toggle.btn-twitter{color:#fff;background-color:#2795e9;border-color:rgba(0,0,0,0.2)} .btn-twitter:active,.btn-twitter.active,.open>.dropdown-toggle.btn-twitter{background-image:none} .btn-twitter.disabled,.btn-twitter[disabled],fieldset[disabled] .btn-twitter,.btn-twitter.disabled:hover,.btn-twitter[disabled]:hover,fieldset[disabled] .btn-twitter:hover,.btn-twitter.disabled:focus,.btn-twitter[disabled]:focus,fieldset[disabled] .btn-twitter:focus,.btn-twitter.disabled:active,.btn-twitter[disabled]:active,fieldset[disabled] .btn-twitter:active,.btn-twitter.disabled.active,.btn-twitter[disabled].active,fieldset[disabled] .btn-twitter.active{background-color:#55acee;border-color:rgba(0,0,0,0.2)} .btn-twitter .badge{color:#55acee;background-color:#fff} .btn-vimeo{color:#fff;background-color:#1ab7ea;border-color:rgba(0,0,0,0.2)}.btn-vimeo:hover,.btn-vimeo:focus,.btn-vimeo:active,.btn-vimeo.active,.open>.dropdown-toggle.btn-vimeo{color:#fff;background-color:#1295bf;border-color:rgba(0,0,0,0.2)} .btn-vimeo:active,.btn-vimeo.active,.open>.dropdown-toggle.btn-vimeo{background-image:none} .btn-vimeo.disabled,.btn-vimeo[disabled],fieldset[disabled] .btn-vimeo,.btn-vimeo.disabled:hover,.btn-vimeo[disabled]:hover,fieldset[disabled] .btn-vimeo:hover,.btn-vimeo.disabled:focus,.btn-vimeo[disabled]:focus,fieldset[disabled] .btn-vimeo:focus,.btn-vimeo.disabled:active,.btn-vimeo[disabled]:active,fieldset[disabled] .btn-vimeo:active,.btn-vimeo.disabled.active,.btn-vimeo[disabled].active,fieldset[disabled] .btn-vimeo.active{background-color:#1ab7ea;border-color:rgba(0,0,0,0.2)} .btn-vimeo .badge{color:#1ab7ea;background-color:#fff} .btn-vk{color:#fff;background-color:#587ea3;border-color:rgba(0,0,0,0.2)}.btn-vk:hover,.btn-vk:focus,.btn-vk:active,.btn-vk.active,.open>.dropdown-toggle.btn-vk{color:#fff;background-color:#466482;border-color:rgba(0,0,0,0.2)} .btn-vk:active,.btn-vk.active,.open>.dropdown-toggle.btn-vk{background-image:none} .btn-vk.disabled,.btn-vk[disabled],fieldset[disabled] .btn-vk,.btn-vk.disabled:hover,.btn-vk[disabled]:hover,fieldset[disabled] .btn-vk:hover,.btn-vk.disabled:focus,.btn-vk[disabled]:focus,fieldset[disabled] .btn-vk:focus,.btn-vk.disabled:active,.btn-vk[disabled]:active,fieldset[disabled] .btn-vk:active,.btn-vk.disabled.active,.btn-vk[disabled].active,fieldset[disabled] .btn-vk.active{background-color:#587ea3;border-color:rgba(0,0,0,0.2)} .btn-vk .badge{color:#587ea3;background-color:#fff} .btn-yahoo{color:#fff;background-color:#720e9e;border-color:rgba(0,0,0,0.2)}.btn-yahoo:hover,.btn-yahoo:focus,.btn-yahoo:active,.btn-yahoo.active,.open>.dropdown-toggle.btn-yahoo{color:#fff;background-color:#500a6f;border-color:rgba(0,0,0,0.2)} .btn-yahoo:active,.btn-yahoo.active,.open>.dropdown-toggle.btn-yahoo{background-image:none} .btn-yahoo.disabled,.btn-yahoo[disabled],fieldset[disabled] .btn-yahoo,.btn-yahoo.disabled:hover,.btn-yahoo[disabled]:hover,fieldset[disabled] .btn-yahoo:hover,.btn-yahoo.disabled:focus,.btn-yahoo[disabled]:focus,fieldset[disabled] .btn-yahoo:focus,.btn-yahoo.disabled:active,.btn-yahoo[disabled]:active,fieldset[disabled] .btn-yahoo:active,.btn-yahoo.disabled.active,.btn-yahoo[disabled].active,fieldset[disabled] .btn-yahoo.active{background-color:#720e9e;border-color:rgba(0,0,0,0.2)} .btn-yahoo .badge{color:#720e9e;background-color:#fff} ================================================ FILE: public/css/center.css ================================================ html, body, .container-fluid { height: 98%; } .container-fluid { display: table; vertical-align: middle; } .vertical-center-row { display: table-cell; vertical-align: middle; } ================================================ FILE: public/css/codemirror-extend/ayu-dark.css ================================================ /* Based on https://github.com/dempfi/ayu */ .cm-s-ayu-dark.CodeMirror { background: #0a0e14; color: #b3b1ad; } .cm-s-ayu-dark div.CodeMirror-selected { background: #273747; } .cm-s-ayu-dark .CodeMirror-line::selection, .cm-s-ayu-dark .CodeMirror-line > span::selection, .cm-s-ayu-dark .CodeMirror-line > span > span::selection { background: rgba(39, 55, 71, 99); } .cm-s-ayu-dark .CodeMirror-line::-moz-selection, .cm-s-ayu-dark .CodeMirror-line > span::-moz-selection, .cm-s-ayu-dark .CodeMirror-line > span > span::-moz-selection { background: rgba(39, 55, 71, 99); } .cm-s-ayu-dark .CodeMirror-gutters { background: #0a0e14; border-right: 0px; } .cm-s-ayu-dark .CodeMirror-guttermarker { color: white; } .cm-s-ayu-dark .CodeMirror-guttermarker-subtle { color: #3d424d; } .cm-s-ayu-dark .CodeMirror-linenumber { color: #3d424d; } .cm-s-ayu-dark .CodeMirror-cursor { border-left: 1px solid #e6b450; } .cm-s-ayu-dark span.cm-comment { color: #626a73; } .cm-s-ayu-dark span.cm-atom { color: #ae81ff; } .cm-s-ayu-dark span.cm-number { color: #e6b450; } .cm-s-ayu-dark span.cm-comment.cm-attribute { color: #ffb454; } .cm-s-ayu-dark span.cm-comment.cm-def { color: rgba(57, 186, 230, 80); } .cm-s-ayu-dark span.cm-comment.cm-tag { color: #39bae6; } .cm-s-ayu-dark span.cm-comment.cm-type { color: #5998a6; } .cm-s-ayu-dark span.cm-property, .cm-s-ayu-dark span.cm-attribute { color: #ffb454; } .cm-s-ayu-dark span.cm-keyword { color: #ff8f40; } .cm-s-ayu-dark span.cm-builtin { color: #e6b450; } .cm-s-ayu-dark span.cm-string { color: #c2d94c; } .cm-s-ayu-dark span.cm-variable { color: #b3b1ad; } .cm-s-ayu-dark span.cm-variable-2 { color: #f07178; } .cm-s-ayu-dark span.cm-variable-3 { color: #39bae6; } .cm-s-ayu-dark span.cm-type { color: #ff8f40; } .cm-s-ayu-dark span.cm-def { color: #ffee99; } .cm-s-ayu-dark span.cm-bracket { color: #f8f8f2; } .cm-s-ayu-dark span.cm-tag { color: rgba(57, 186, 230, 80); } .cm-s-ayu-dark span.cm-header { color: #c2d94c; } .cm-s-ayu-dark span.cm-link { color: #39bae6; } .cm-s-ayu-dark span.cm-error { color: #ff3333; } .cm-s-ayu-dark .CodeMirror-activeline-background { background: #01060e; } .cm-s-ayu-dark .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; } ================================================ FILE: public/css/codemirror-extend/ayu-mirage.css ================================================ /* Based on https://github.com/dempfi/ayu */ .cm-s-ayu-mirage.CodeMirror { background: #1f2430; color: #cbccc6; } .cm-s-ayu-mirage div.CodeMirror-selected { background: #34455a; } .cm-s-ayu-mirage .CodeMirror-line::selection, .cm-s-ayu-mirage .CodeMirror-line > span::selection, .cm-s-ayu-mirage .CodeMirror-line > span > span::selection { background: #34455a; } .cm-s-ayu-mirage .CodeMirror-line::-moz-selection, .cm-s-ayu-mirage .CodeMirror-line > span::-moz-selection, .cm-s-ayu-mirage .CodeMirror-line > span > span::-moz-selection { background: rgba(25, 30, 42, 99); } .cm-s-ayu-mirage .CodeMirror-gutters { background: #1f2430; border-right: 0px; } .cm-s-ayu-mirage .CodeMirror-guttermarker { color: white; } .cm-s-ayu-mirage .CodeMirror-guttermarker-subtle { color: rgba(112, 122, 140, 66); } .cm-s-ayu-mirage .CodeMirror-linenumber { color: rgba(61, 66, 77, 99); } .cm-s-ayu-mirage .CodeMirror-cursor { border-left: 1px solid #ffcc66; } .cm-s-ayu-mirage span.cm-comment { color: #5c6773; font-style:italic; } .cm-s-ayu-mirage span.cm-atom { color: #ae81ff; } .cm-s-ayu-mirage span.cm-number { color: #ffcc66; } .cm-s-ayu-mirage span.cm-comment.cm-attribute { color: #ffd580; } .cm-s-ayu-mirage span.cm-comment.cm-def { color: #d4bfff; } .cm-s-ayu-mirage span.cm-comment.cm-tag { color: #5ccfe6; } .cm-s-ayu-mirage span.cm-comment.cm-type { color: #5998a6; } .cm-s-ayu-mirage span.cm-property { color: #f29e74; } .cm-s-ayu-mirage span.cm-attribute { color: #ffd580; } .cm-s-ayu-mirage span.cm-keyword { color: #ffa759; } .cm-s-ayu-mirage span.cm-builtin { color: #ffcc66; } .cm-s-ayu-mirage span.cm-string { color: #bae67e; } .cm-s-ayu-mirage span.cm-variable { color: #cbccc6; } .cm-s-ayu-mirage span.cm-variable-2 { color: #f28779; } .cm-s-ayu-mirage span.cm-variable-3 { color: #5ccfe6; } .cm-s-ayu-mirage span.cm-type { color: #ffa759; } .cm-s-ayu-mirage span.cm-def { color: #ffd580; } .cm-s-ayu-mirage span.cm-bracket { color: rgba(92, 207, 230, 80); } .cm-s-ayu-mirage span.cm-tag { color: #5ccfe6; } .cm-s-ayu-mirage span.cm-header { color: #bae67e; } .cm-s-ayu-mirage span.cm-link { color: #5ccfe6; } .cm-s-ayu-mirage span.cm-error { color: #ff3333; } .cm-s-ayu-mirage .CodeMirror-activeline-background { background: #191e2a; } .cm-s-ayu-mirage .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; } ================================================ FILE: public/css/codemirror-extend/one-dark.css ================================================ .cm-s-one-dark .CodeMirror-linenumber { color: #676767; } .cm-s-one-dark.CodeMirror-focused .CodeMirror-activeline .CodeMirror-gutter-elt { color: #b0b0b0; } .cm-s-one-dark .cm-comment { color: #818895; } ================================================ FILE: public/css/codemirror-extend/tomorrow-night-bright.css ================================================ /* Name: Tomorrow Night - Bright Author: Chris Kempson Port done by Gerard Braad */ .cm-s-tomorrow-night-bright.CodeMirror { background: #000000; color: #eaeaea; } .cm-s-tomorrow-night-bright div.CodeMirror-selected { background: #424242; } .cm-s-tomorrow-night-bright .CodeMirror-gutters { background: #000000; border-right: 0px; } .cm-s-tomorrow-night-bright .CodeMirror-guttermarker { color: #e78c45; } .cm-s-tomorrow-night-bright .CodeMirror-guttermarker-subtle { color: #777; } .cm-s-tomorrow-night-bright .CodeMirror-linenumber { color: #424242; } .cm-s-tomorrow-night-bright .CodeMirror-cursor { border-left: 1px solid #6A6A6A; } .cm-s-tomorrow-night-bright span.cm-comment { color: #d27b53; } .cm-s-tomorrow-night-bright span.cm-atom { color: #a16a94; } .cm-s-tomorrow-night-bright span.cm-number { color: #a16a94; } .cm-s-tomorrow-night-bright span.cm-property, .cm-s-tomorrow-night-bright span.cm-attribute { color: #99cc99; } .cm-s-tomorrow-night-bright span.cm-keyword { color: #d54e53; } .cm-s-tomorrow-night-bright span.cm-string { color: #e7c547; } .cm-s-tomorrow-night-bright span.cm-variable { color: #b9ca4a; } .cm-s-tomorrow-night-bright span.cm-variable-2 { color: #7aa6da; } .cm-s-tomorrow-night-bright span.cm-def { color: #e78c45; } .cm-s-tomorrow-night-bright span.cm-bracket { color: #eaeaea; } .cm-s-tomorrow-night-bright span.cm-tag { color: #d54e53; } .cm-s-tomorrow-night-bright span.cm-link { color: #a16a94; } .cm-s-tomorrow-night-bright span.cm-error { background: #d54e53; color: #6A6A6A; } .cm-s-tomorrow-night-bright .CodeMirror-activeline-background { background: #2a2a2a; } .cm-s-tomorrow-night-bright .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; } ================================================ FILE: public/css/codemirror-extend/tomorrow-night-eighties.css ================================================ /* Name: Tomorrow Night - Eighties Author: Chris Kempson CodeMirror template by Jan T. Sott (https://github.com/idleberg/base16-codemirror) Original Base16 color scheme by Chris Kempson (https://github.com/chriskempson/base16) */ .cm-s-tomorrow-night-eighties.CodeMirror { background: #000000; color: #CCCCCC; } .cm-s-tomorrow-night-eighties div.CodeMirror-selected { background: #2D2D2D; } .cm-s-tomorrow-night-eighties .CodeMirror-line::selection, .cm-s-tomorrow-night-eighties .CodeMirror-line > span::selection, .cm-s-tomorrow-night-eighties .CodeMirror-line > span > span::selection { background: rgba(45, 45, 45, 0.99); } .cm-s-tomorrow-night-eighties .CodeMirror-line::-moz-selection, .cm-s-tomorrow-night-eighties .CodeMirror-line > span::-moz-selection, .cm-s-tomorrow-night-eighties .CodeMirror-line > span > span::-moz-selection { background: rgba(45, 45, 45, 0.99); } .cm-s-tomorrow-night-eighties .CodeMirror-gutters { background: #000000; border-right: 0px; } .cm-s-tomorrow-night-eighties .CodeMirror-guttermarker { color: #f2777a; } .cm-s-tomorrow-night-eighties .CodeMirror-guttermarker-subtle { color: #777; } .cm-s-tomorrow-night-eighties .CodeMirror-linenumber { color: #515151; } .cm-s-tomorrow-night-eighties .CodeMirror-cursor { border-left: 1px solid #6A6A6A; } .cm-s-tomorrow-night-eighties span.cm-comment { color: #d27b53; } .cm-s-tomorrow-night-eighties span.cm-atom { color: #a16a94; } .cm-s-tomorrow-night-eighties span.cm-number { color: #a16a94; } .cm-s-tomorrow-night-eighties span.cm-property, .cm-s-tomorrow-night-eighties span.cm-attribute { color: #99cc99; } .cm-s-tomorrow-night-eighties span.cm-keyword { color: #f2777a; } .cm-s-tomorrow-night-eighties span.cm-string { color: #ffcc66; } .cm-s-tomorrow-night-eighties span.cm-variable { color: #99cc99; } .cm-s-tomorrow-night-eighties span.cm-variable-2 { color: #6699cc; } .cm-s-tomorrow-night-eighties span.cm-def { color: #f99157; } .cm-s-tomorrow-night-eighties span.cm-bracket { color: #CCCCCC; } .cm-s-tomorrow-night-eighties span.cm-tag { color: #f2777a; } .cm-s-tomorrow-night-eighties span.cm-link { color: #a16a94; } .cm-s-tomorrow-night-eighties span.cm-error { background: #f2777a; color: #6A6A6A; } .cm-s-tomorrow-night-eighties .CodeMirror-activeline-background { background: #343600; } .cm-s-tomorrow-night-eighties .CodeMirror-matchingbracket { text-decoration: underline; color: white !important; } ================================================ FILE: public/css/cover.css ================================================ /* * Globals */ /* Links */ a, a:focus, a:hover { color: #fff; } /* Custom default button */ .btn-default, .btn-default:hover, .btn-default:focus { color: #333; text-shadow: none; /* Prevent inheritence from `body` */ background-color: #fff; border: 1px solid #fff; } /* * Base structure */ html { height: 100%; } html, body { background-color: #333; } body { min-height: 100%; color: #fff; text-align: center; text-shadow: 0 1px 3px rgba(0, 0, 0, .5); } /* Extra markup and styles for table-esque vertical and horizontal centering */ .site-wrapper { padding: 10px; display: table; width: 100%; height: 100vh; /* For at least Firefox */ min-height: 100%; -webkit-box-shadow: inset 0 0 100px rgba(0, 0, 0, .5); box-shadow: inset 0 0 100px rgba(0, 0, 0, .5); } .site-wrapper-inner { display: table-cell; vertical-align: middle; } .cover-container { width: 100%; padding-top: 80px; margin-right: auto; margin-left: auto; } /* Padding for spacing */ .inner { padding: 10px; } /* * Header */ .masthead { position: absolute; top: 0; left: 0; width: 100%; } .masthead-brand { margin-top: 10px; margin-bottom: 10px; } .masthead-nav { text-align: left; max-width: 1000px; margin: 0 auto; padding-left: 10px; padding-right: 10px; } .masthead-nav > li { display: inline-block; } .masthead-nav > li + li { margin-left: 20px; } .masthead-nav > li > a { padding-right: 0; padding-left: 0; font-size: 16px; font-weight: bold; color: #fff; /* IE8 proofing */ color: rgba(255, 255, 255, .75); border-bottom: 2px solid transparent; } .masthead-nav > li > a:hover, .masthead-nav > li > a:focus { background-color: transparent; border-bottom-color: #a9a9a9; border-bottom-color: rgba(255, 255, 255, .25); } .masthead-nav > .active > a, .masthead-nav > .active > a:hover, .masthead-nav > .active > a:focus { color: #fff; border-bottom-color: #fff; } @media (min-width: 768px) { .masthead-brand { float: left; } .masthead-nav { float: none; } .inner { padding: 30px 25px; } } /* * Cover */ .cover { padding: 0 20px; } .cover .btn-lg { padding: 10px 20px; font-weight: bold; } /* * Footer */ .mastfoot { color: #999; /* IE8 proofing */ color: rgba(255, 255, 255, .5); } /* * Affix and center */ @media (min-width: 768px) { /* Pull out the header and footer */ .masthead { position: absolute; top: 0; left: 0; width: 100% !important; } .mastfoot { position: fixed; bottom: 0; } /* Start the vertical centering */ .site-wrapper-inner { vertical-align: middle; } /* Handle the widths */ .masthead, .mastfoot, .cover-container { width: 100%; /* Must be percentage or pixels for horizontal alignment */ } } @media (min-width: 992px) { .masthead, .mastfoot, .cover-container { width: 1000px; } } .section ul { list-style: none; } /* custom */ html, body { overflow-x: hidden; } input { color: black; } .mastfoot { position: relative; } .select2-container-multi .select2-choices .select2-search-field input { font-family: inherit; padding: 5px 12px; } .select2-container { margin: 0 auto !important; } .list { width: 100%; padding-left: 0; display: -webkit-inline-flex; display: -moz-inline-flex; display: -ms-inline-flex; display: -o-inline-flex; display: inline-flex; -webkit-flex-direction: row; -moz-flex-direction: row; -ms-flex-direction: row; flex-direction: row; -webkit-flex-flow: row wrap; -moz-flex-flow: row wrap; -ms-flex-flow: row wrap; flex-flow: row wrap; -webkit-justify-content: flex-start; -moz-justify-content: flex-start; -ms-justify-content: flex-start; justify-content: flex-start; } .list { margin: 20px 0; } .list li { padding: 0 10px; } .list li * { word-break: break-word; word-wrap: break-word; } .list li a { text-decoration: none; } .list li p { color: gray; } .list li .item { padding: 5px 25px; margin: 10px 0; background: white; border-radius: 5px; color: black; text-shadow: none; min-height: 134px; display: table; min-width: 100%; } .list li .item .content { display: table-cell; vertical-align: middle; } .list li .item .content .tags { line-height: 25px; } .list li .item .content .tags span { display: inline-block; line-height: 15px; } .form-inline { padding: 0 10px; } .sort.asc { text-decoration: overline; } .sort.desc { text-decoration: underline; } .ui-avatar { display: inline-block; overflow: hidden; line-height: 1; vertical-align: middle; border-radius: 3px; } .ui-avatar.circle { border-radius: 50%; } .ui-history-close { position: absolute; right: 14px; top: 15px; font-size: 16px; opacity: 0.5; } .ui-history-close:hover { opacity: 1; } .ui-history-pin { position: absolute; left: 14px; top: 15px; font-size: 16px; opacity: 0.2; transition: opacity 0.2s ease-in-out; -webkit-transition: opacity 0.2s ease-in-out; } .item:hover .ui-history-pin:hover { opacity: 1; } .item .ui-history-pin.active { opacity: 1; color: #d43f3a; } .ui-or { margin: 5px; } .ui-use-tags { min-width: 172px; max-width: 344px; } .modal-title { text-align: left; color: black; } .modal-body { color: black; } .btn-file { position: relative; overflow: hidden; } .btn-file input[type=file] { position: absolute; top: 0; right: 0; min-width: 100%; min-height: 100%; font-size: 100px; text-align: right; filter: alpha(opacity=0); opacity: 0; outline: none; background: white; cursor: inherit; display: block; } .social-foot { line-height: 30px; } .social-foot > * { line-height: 20px; vertical-align: middle !important; display: inline-block !important; } .btn-link, .btn-link:hover, .btn-link:focus, .btn-link:active { color: white; } .screenshot { margin: 30px auto; width: 100%; border-radius: 3px; } select { color: black; } @media (max-width: 768px) { span.ui-or { display: block; } .ui-use-tags { max-width: 100%; } } .btn-mattermost { background-color: #2179ec; border-color: rgba(0,0,0,0.2); color: #fff; } .btn-gitlab { background-color: #e35431; border-color: rgba(0,0,0,0.2); color: #fff; } .btn-mattermost:hover, .btn-mattermost:active { background-color: #105fc6; border-color: rgba(0,0,0,0.2); color: #fff; } .btn-gitlab:hover, .btn-gitlab:active { background-color: #c23b1a; border-color: rgba(0,0,0,0.2); color: #fff; } /* add btn-login-method replaced of btn-social, to avoid ad block delete the login button */ .btn-login-method { padding-left: 61px; position: relative; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .btn-login-method.btn-lg :first-child { line-height:45px; width:45px; font-size:1.8em; } .btn-login-method>:first-child {position:absolute;left:0;top:0;bottom:0;width:32px;line-height:34px;font-size:1.6em;text-align:center;border-right:1px solid rgba(0,0,0,0.2)} a.btn.btn-login-method > i.oauth-icon { display: inline-flex; height: 45px; width: 45px; line-height: inherit; padding: 6px; } a.btn.btn-login-method > i.oauth-icon > img { width: 100%; height: 100%; line-height: inherit; } ================================================ FILE: public/css/extra.css ================================================ /* for extra features should include this */ .vimeo, .youtube { position: relative; cursor: pointer; display: table; width: 100%; text-align: center; background-position: center center; background-repeat: no-repeat; background-size: contain; background-color: black; overflow: hidden; } /* youtube always use 16:9 aspect ratio video */ .youtube { position: relative; width: 100%; padding-bottom: 56.25%; } .vimeo img { width: 100%; object-fit: contain; z-index: 0; } .youtube img { width: 100%; height: 100%; object-fit: cover; position: absolute; top: 0; left: 0; z-index: 0; } .vimeo iframe, .youtube iframe { position: absolute; top: 0; left: 0; width: 100%; height: 100%; vertical-align: middle; z-index: 1; } .vimeo .icon, .youtube .icon { position: absolute; height: auto; width: auto; top: 50%; left: 50%; transform: translate(-50%, -50%); color: white; opacity: 0.3; -webkit-transition: opacity 0.2s; /* Safari */ transition: opacity 0.2s; z-index: 0; } .vimeo:hover .icon, .youtube:hover .icon { opacity: 0.6; -webkit-transition: opacity 0.2s; /* Safari */ transition: opacity 0.2s; } .slideshare .inner, .speakerdeck .inner { position: relative; width: 100%; } .slideshare .inner iframe, .speakerdeck .inner iframe { position: absolute; top: 0; bottom: 0; left: 0; right: 0; width: 100%; height: 100%; } .geo-map { width: 100%; height: 250px; } /* markmap */ .markmap-container { height: 300px; } .markmap-container > svg { width: 100%; height: 100%; } .MJX_Assistive_MathML { display: none; } .ui-infobar { position: relative; z-index: 2; max-width: 758px; margin-top: 25px; margin-bottom: -25px; color: #777; } .night .ui-infobar { color: #ededed; } .toc .invisable-node { list-style-type: none; } .ui-toc { position: fixed; bottom: 20px; z-index: 10000; } .ui-toc-label { opacity: 0.9; background-color: #ccc; border: none; -webkit-transition: opacity 0.2s; /* Safari */ transition: opacity 0.2s; } .ui-toc .open .ui-toc-label { opacity: 1; color: #5f5f5f; -webkit-transition: opacity 0.2s; /* Safari */ transition: opacity 0.2s; } .ui-toc-label:focus { opacity: 1; background-color: #ccc; color: black; } .ui-toc-label:hover { opacity: 1; background-color: #ccc; -webkit-transition: opacity 0.2s; /* Safari */ transition: opacity 0.2s; } .ui-toc-dropdown { margin-top: 23px; margin-bottom: 20px; padding-left: 10px; padding-right: 10px; max-width: 45vw; width: 25vw; max-height: 70vh; overflow: auto; text-align: inherit; } .ui-toc-dropdown > .toc { max-height: calc(70vh - 100px); overflow: auto; } .ui-toc-dropdown[dir='rtl'] .nav { padding-right: 0; letter-spacing: 0.0029em; } .ui-toc-dropdown a { overflow: hidden; text-overflow: ellipsis; white-space: pre; } .ui-toc-dropdown .nav>li>a { display: block; padding: 4px 20px; font-size: 13px; font-weight: 500; color: #767676; } .ui-toc-dropdown .toc.expand ul { display: block; } .ui-toc-dropdown .nav > li:first-child:nth-last-child(1) > ul { display: block; } .ui-toc-dropdown .nav>li>a:focus,.ui-toc-dropdown .nav>li>a:hover { padding-left: 19px; color: black; text-decoration: none; background-color: transparent; border-left: 1px solid black; } .night .ui-toc-dropdown .nav>li>a:focus, .night .ui-toc-dropdown .nav>li>a:hover{ color: white; border-left-color: white; } .ui-toc-dropdown[dir='rtl'] .nav>li>a:focus,.ui-toc-dropdown[dir='rtl'] .nav>li>a:hover { padding-right: 19px; border-left: none; border-right: 1px solid black; } .ui-toc-dropdown .nav>.active:focus>a,.ui-toc-dropdown .nav>.active:hover>a,.ui-toc-dropdown .nav>.active>a { padding-left: 18px; font-weight: 700; color: black; background-color: transparent; border-left: 2px solid black; } .night .ui-toc-dropdown .nav>.active:focus>a,.night .ui-toc-dropdown .nav>.active:hover>a,.night .ui-toc-dropdown .nav>.active>a { color: white; border-left: 2px solid white; } .ui-toc-dropdown[dir='rtl'] .nav>.active:focus>a,.ui-toc-dropdown[dir='rtl'] .nav>.active:hover>a,.ui-toc-dropdown[dir='rtl'] .nav>.active>a { padding-right: 18px; border-left: none; border-right: 2px solid black; } .ui-toc-dropdown .nav .nav { display: none; padding-bottom: 10px; } .ui-toc-dropdown .nav>.active>ul { display: block; } .ui-toc-dropdown .nav .nav>li>a { padding-top: 1px; padding-bottom: 1px; padding-left: 30px; font-size: 12px; font-weight: 400; } .night .ui-toc-dropdown .nav > li > a{ color: #aaa; } .ui-toc-dropdown[dir='rtl'] .nav .nav>li>a { padding-right: 30px; } .ui-toc-dropdown .nav .nav>li>ul>li>a { padding-top: 1px; padding-bottom: 1px; padding-left: 40px; font-size: 12px; font-weight: 400; } .ui-toc-dropdown[dir='rtl'] .nav .nav>li>ul>li>a { padding-right: 40px; } .ui-toc-dropdown .nav .nav>li>ul>li>ul>li>a { padding-top: 1px; padding-bottom: 1px; padding-left: 50px; font-size: 12px; font-weight: 400; } .ui-toc-dropdown[dir='rtl'] .nav .nav>li>ul>li>ul>li>a { padding-right: 50px; } .ui-toc-dropdown .nav .nav>li>ul>li>ul>li>ul>li>a { padding-top: 1px; padding-bottom: 1px; padding-left: 60px; font-size: 12px; font-weight: 400; } .ui-toc-dropdown[dir='rtl'] .nav .nav>li>ul>li>ul>li>ul>li>a { padding-right: 60px; } .ui-toc-dropdown .nav .nav>li>a:focus,.ui-toc-dropdown .nav .nav>li>a:hover { padding-left: 29px; } .ui-toc-dropdown[dir='rtl'] .nav .nav>li>a:focus,.ui-toc-dropdown[dir='rtl'] .nav .nav>li>a:hover { padding-right: 29px; } .ui-toc-dropdown .nav .nav>li>ul>li>a:focus,.ui-toc-dropdown .nav .nav>li>ul>li>a:hover { padding-left: 39px; } .ui-toc-dropdown[dir='rtl'] .nav .nav>li>ul>li>a:focus,.ui-toc-dropdown[dir='rtl'] .nav .nav>li>ul>li>a:hover { padding-right: 39px; } .ui-toc-dropdown .nav .nav>li>ul>li>ul>li>a:focus,.ui-toc-dropdown .nav .nav>li>ul>li>ul>li>a:hover { padding-left: 49px; } .ui-toc-dropdown[dir='rtl'] .nav .nav>li>ul>li>ul>li>a:focus,.ui-toc-dropdown[dir='rtl'] .nav .nav>li>ul>li>ul>li>a:hover { padding-right: 49px; } .ui-toc-dropdown .nav .nav>li>ul>li>ul>li>ul>li>a:focus,.ui-toc-dropdown .nav .nav>li>ul>li>ul>li>ul>li>a:hover { padding-left: 59px; } .ui-toc-dropdown[dir='rtl'] .nav .nav>li>ul>li>ul>li>ul>li>a:focus,.ui-toc-dropdown[dir='rtl'] .nav .nav>li>ul>li>ul>li>ul>li>a:hover { padding-right: 59px; } .ui-toc-dropdown .nav .nav>.active:focus>a,.ui-toc-dropdown .nav .nav>.active:hover>a,.ui-toc-dropdown .nav .nav>.active>a { padding-left: 28px; font-weight: 500; } .ui-toc-dropdown[dir='rtl'] .nav .nav>.active:focus>a,.ui-toc-dropdown[dir='rtl'] .nav .nav>.active:hover>a,.ui-toc-dropdown[dir='rtl'] .nav .nav>.active>a { padding-right: 28px; } .ui-toc-dropdown .nav .nav>.active>.nav>.active:focus>a,.ui-toc-dropdown .nav .nav>.active>.nav>.active:hover>a,.ui-toc-dropdown .nav .nav>.active>.nav>.active>a { padding-left: 38px; font-weight: 500; } .ui-toc-dropdown[dir='rtl'] .nav .nav>.active>.nav>.active:focus>a,.ui-toc-dropdown[dir='rtl'] .nav .nav>.active>.nav>.active:hover>a,.ui-toc-dropdown[dir='rtl'] .nav .nav>.active>.nav>.active>a { padding-right: 38px; } .ui-toc-dropdown .nav .nav>.active>.nav>.active>.nav>.active:focus>a,.ui-toc-dropdown .nav .nav>.active>.nav>.active>.nav>.active:hover>a,.ui-toc-dropdown .nav .nav>.active>.nav>.active>.nav>.active>a { padding-left: 48px; font-weight: 500; } .ui-toc-dropdown[dir='rtl'] .nav .nav>.active>.nav>.active>.nav>.active:focus>a,.ui-toc-dropdown[dir='rtl'] .nav .nav>.active>.nav>.active>.nav>.active:hover>a,.ui-toc-dropdown[dir='rtl'] .nav .nav>.active>.active>.nav>.nav>.active>a { padding-right: 48px; } .ui-toc-dropdown .nav .nav>.active>.nav>.active>.nav>.active>.nav>.active:focus>a,.ui-toc-dropdown .nav .nav>.active>.nav>.active>.nav>.active>.nav>.active:hover>a,.ui-toc-dropdown .nav .nav>.active>.nav>.active>.nav>.active>.nav>.active>a { padding-left: 58px; font-weight: 500; } .ui-toc-dropdown[dir='rtl'] .nav .nav>.active>.nav>.active>.nav>.active>.nav>.active:focus>a,.ui-toc-dropdown[dir='rtl'] .nav .nav>.active>.nav>.active>.nav>.active>.nav>.active:hover>a,.ui-toc-dropdown[dir='rtl'] .nav .nav>.active>.active>.nav>.nav>.active>.nav>.active>a { padding-right: 58px; } /* support japanese font */ .markdown-body[lang^="ja"] { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, Arial, "Hiragino Kaku Gothic Pro", "ヒラギノ角ゴ Pro W3", Osaka, Meiryo, "メイリオ", "MS Gothic", "MS ゴシック", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; } .ui-toc-dropdown[lang^="ja"] { font-family: "Source Sans Pro", Helvetica, Arial, "Meiryo UI", "MS PGothic", "MS Pゴシック", sans-serif; } /* support zh-tw font */ .markdown-body[lang="zh-tw"] { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, Arial, "PingFang TC", "Microsoft JhengHei", "微軟正黑", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; } .ui-toc-dropdown[lang="zh-tw"] { font-family: "Source Sans Pro", Helvetica, Arial, "Microsoft JhengHei UI", "微軟正黑UI", sans-serif; } /* support zh-cn font */ .markdown-body[lang="zh-cn"] { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, Arial, "PingFang SC", "Microsoft YaHei", "微软雅黑", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; } .ui-toc-dropdown[lang="zh-cn"] { font-family: "Source Sans Pro", Helvetica, Arial, "Microsoft YaHei UI", "微软雅黑UI", sans-serif; } .ui-affix-toc { position: fixed; top: 0; max-width: 15vw; max-height: 70vh; overflow: auto; } .expand-toggle, .back-to-top, .go-to-bottom { display: block; padding: 4px 10px; margin-top: 10px; margin-left: 10px; font-size: 12px; font-weight: 500; color: rgba(0, 0, 0, 0.85); } .expand-toggle:hover, .expand-toggle:focus, .back-to-top:hover, .back-to-top:focus, .go-to-bottom:hover, .go-to-bottom:focus { color: #563d7c; text-decoration: none; } .back-to-top, .go-to-bottom { margin-top: 0; } .ui-user-icon { width: 20px; height: 20px; display: block; border-radius: 3px; margin-top: 2px; margin-bottom: 2px; margin-right: 5px; background-position: center center; background-repeat: no-repeat; background-size: contain; } .ui-user-icon.small { width: 18px; height: 18px; display: inline-block; vertical-align: middle; margin: 0 0 0.2em 0; } small span { line-height: 22px; } small .dropdown { display: inline-block; } small .dropdown a:focus, small .dropdown a:hover { text-decoration: none; } .unselectable { -moz-user-select: none; -khtml-user-select: none; -webkit-user-select: none; -o-user-select: none; user-select: none; } .night .navbar{ background: #333; border-bottom-color: #333; color: #eee; } .night .navbar-default .navbar-nav > li > a, .night .navbar a { color: #eee; } @media print { div, table, img, pre, blockquote { page-break-inside: avoid !important; } a[href]:after { font-size: 12px !important; } } ================================================ FILE: public/css/font.css ================================================ /* latin-ext */ @font-face { font-family: 'Source Code Pro'; font-style: normal; font-weight: 300; src: local('Source Code Pro Light'), local('SourceCodePro-Light'), url('../fonts/SourceCodePro-Light.woff') format('woff'); unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { font-family: 'Source Code Pro'; font-style: normal; font-weight: 300; src: local('Source Code Pro Light'), local('SourceCodePro-Light'), url('../fonts/SourceCodePro-Light.woff') format('woff'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; } /* latin-ext */ @font-face { font-family: 'Source Code Pro'; font-style: normal; font-weight: 400; src: local('Source Code Pro'), local('SourceCodePro-Regular'), url('../fonts/SourceCodePro-Regular.woff') format('woff'); unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { font-family: 'Source Code Pro'; font-style: normal; font-weight: 400; src: local('Source Code Pro'), local('SourceCodePro-Regular'), url('../fonts/SourceCodePro-Regular.woff') format('woff'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; } /* latin-ext */ @font-face { font-family: 'Source Code Pro'; font-style: normal; font-weight: 500; src: local('Source Code Pro Medium'), local('SourceCodePro-Medium'), url('../fonts/SourceCodePro-Medium.woff') format('woff'); unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { font-family: 'Source Code Pro'; font-style: normal; font-weight: 500; src: local('Source Code Pro Medium'), local('SourceCodePro-Medium'), url('../fonts/SourceCodePro-Medium.woff') format('woff'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; } /* vietnamese */ @font-face { font-family: 'Source Sans Pro'; font-style: normal; font-weight: 300; src: local('Source Sans Pro Light'), local('SourceSansPro-Light'), url('../fonts/SourceCodePro-Medium.woff') format('woff'); unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @font-face { font-family: 'Source Sans Pro'; font-style: normal; font-weight: 300; src: local('Source Sans Pro Light'), local('SourceSansPro-Light'), url('../fonts/SourceSansPro-Light.woff') format('woff'); unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { font-family: 'Source Sans Pro'; font-style: normal; font-weight: 300; src: local('Source Sans Pro Light'), local('SourceSansPro-Light'), url('../fonts/SourceSansPro-Light.woff') format('woff'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; } /* vietnamese */ @font-face { font-family: 'Source Sans Pro'; font-style: normal; font-weight: 400; src: local('Source Sans Pro'), local('SourceSansPro-Regular'), url('../fonts/SourceSansPro-Regular.woff') format('woff'); unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @font-face { font-family: 'Source Sans Pro'; font-style: normal; font-weight: 400; src: local('Source Sans Pro'), local('SourceSansPro-Regular'), url('../fonts/SourceSansPro-Regular.woff') format('woff'); unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { font-family: 'Source Sans Pro'; font-style: normal; font-weight: 400; src: local('Source Sans Pro'), local('SourceSansPro-Regular'), url('../fonts/SourceSansPro-Regular.woff') format('woff'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; } /* vietnamese */ @font-face { font-family: 'Source Sans Pro'; font-style: normal; font-weight: 600; src: local('Source Sans Pro Semibold'), local('SourceSansPro-Semibold'), url('../fonts/SourceSansPro-Semibold.woff') format('woff'); unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @font-face { font-family: 'Source Sans Pro'; font-style: normal; font-weight: 600; src: local('Source Sans Pro Semibold'), local('SourceSansPro-Semibold'), url('../fonts/SourceSansPro-Semibold.woff') format('woff'); unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { font-family: 'Source Sans Pro'; font-style: normal; font-weight: 600; src: local('Source Sans Pro Semibold'), local('SourceSansPro-Semibold'), url('../fonts/SourceSansPro-Semibold.woff') format('woff'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; } /* vietnamese */ @font-face { font-family: 'Source Sans Pro'; font-style: italic; font-weight: 300; src: local('Source Sans Pro Light Italic'), local('SourceSansPro-LightIt'), url('../fonts/SourceSansPro-LightItalic.woff') format('woff'); unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @font-face { font-family: 'Source Sans Pro'; font-style: italic; font-weight: 300; src: local('Source Sans Pro Light Italic'), local('SourceSansPro-LightIt'), url('../fonts/SourceSansPro-LightItalic.woff') format('woff'); unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { font-family: 'Source Sans Pro'; font-style: italic; font-weight: 300; src: local('Source Sans Pro Light Italic'), local('SourceSansPro-LightIt'), url('../fonts/SourceSansPro-LightItalic.woff') format('woff'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; } /* vietnamese */ @font-face { font-family: 'Source Sans Pro'; font-style: italic; font-weight: 400; src: local('Source Sans Pro Italic'), local('SourceSansPro-It'), url('../fonts/SourceSansPro-Italic.woff') format('woff'); unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @font-face { font-family: 'Source Sans Pro'; font-style: italic; font-weight: 400; src: local('Source Sans Pro Italic'), local('SourceSansPro-It'), url('../fonts/SourceSansPro-Italic.woff') format('woff'); unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { font-family: 'Source Sans Pro'; font-style: italic; font-weight: 400; src: local('Source Sans Pro Italic'), local('SourceSansPro-It'), url('../fonts/SourceSansPro-Italic.woff') format('woff'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; } /* vietnamese */ @font-face { font-family: 'Source Sans Pro'; font-style: italic; font-weight: 600; src: local('Source Sans Pro Semibold Italic'), local('SourceSansPro-SemiboldIt'), url('../fonts/SourceSansPro-SemiboldItalic.woff') format('woff'); unicode-range: U+0102-0103, U+1EA0-1EF9, U+20AB; } /* latin-ext */ @font-face { font-family: 'Source Sans Pro'; font-style: italic; font-weight: 600; src: local('Source Sans Pro Semibold Italic'), local('SourceSansPro-SemiboldIt'), url('../fonts/SourceSansPro-SemiboldItalic.woff') format('woff'); unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { font-family: 'Source Sans Pro'; font-style: italic; font-weight: 600; src: local('Source Sans Pro Semibold Italic'), local('SourceSansPro-SemiboldIt'), url('../fonts/SourceSansPro-SemiboldItalic.woff') format('woff'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; } /* latin-ext */ @font-face { font-family: 'Source Serif Pro'; font-style: normal; font-weight: 400; src: local('Source Serif Pro'), local('SourceSerifPro-Regular'), url('../fonts/SourceSerifPro-Regular.woff') format('woff'); unicode-range: U+0100-024F, U+1E00-1EFF, U+20A0-20AB, U+20AD-20CF, U+2C60-2C7F, U+A720-A7FF; } /* latin */ @font-face { font-family: 'Source Serif Pro'; font-style: normal; font-weight: 400; src: local('Source Serif Pro'), local('SourceSerifPro-Regular'), url('../fonts/SourceSerifPro-Regular.woff') format('woff'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000; } ================================================ FILE: public/css/github-extract.css ================================================ .markdown-body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; font-size: 16px; line-height: 1.5; word-wrap: break-word; } .markdown-body::before { display: table; content: "" } .markdown-body::after { display: table; clear: both; content: ""; } .markdown-body>*:first-child { margin-top: 0 !important; } .markdown-body>*:last-child { margin-bottom: 0 !important; } .markdown-body a:not([href]) { color: inherit; text-decoration: none; } .markdown-body .absent { color: #c00; } .markdown-body .anchor { float: left; padding-right: 4px; margin-left: -20px; line-height: 1 } .markdown-body .anchor:focus { outline: none; } .markdown-body p, .markdown-body blockquote, .markdown-body ul, .markdown-body ol, .markdown-body dl, .markdown-body table, .markdown-body pre { margin-top: 0; margin-bottom: 16px; } .markdown-body hr { height: 0.25em; padding: 0; margin: 24px 0; background-color: #e7e7e7; border: 0; } .markdown-body blockquote { padding: 0 1em; color: #777; border-left: 0.25em solid #ddd; } .night .markdown-body blockquote{ color: #bcbcbc; } .markdown-body blockquote>:first-child { margin-top: 0; } .markdown-body blockquote>:last-child { margin-bottom: 0; } .markdown-body kbd { display: inline-block; padding: 3px 5px; font-size: 11px; line-height: 10px; color: #555; vertical-align: middle; background-color: #fcfcfc; border: solid 1px #ccc; border-bottom-color: #bbb; border-radius: 3px; box-shadow: inset 0 -1px 0 #bbb; } .markdown-body .loweralpha { list-style-type: lower-alpha; } .markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 { margin-top: 24px; margin-bottom: 16px; font-weight: 600; line-height: 1.25; } .night .markdown-body h1, .night .markdown-body h2, .night .markdown-body h3, .night .markdown-body h4, .night .markdown-body h5, .night .markdown-body h6 { color: #ddd; } .markdown-body h1 .fa-link, .markdown-body h2 .fa-link, .markdown-body h3 .fa-link, .markdown-body h4 .fa-link, .markdown-body h5 .fa-link, .markdown-body h6 .fa-link { color: #000; vertical-align: middle; visibility: hidden; font-size: 16px; } .night .markdown-body h1 .fa-link, .night .markdown-body h2 .fa-link, .night .markdown-body h3 .fa-link, .night .markdown-body h4 .fa-link, .night .markdown-body h5 .fa-link, .night .markdown-body h6 .fa-link { color: #fff; } .markdown-body h1:hover .anchor, .markdown-body h2:hover .anchor, .markdown-body h3:hover .anchor, .markdown-body h4:hover .anchor, .markdown-body h5:hover .anchor, .markdown-body h6:hover .anchor { text-decoration: none; } .markdown-body h1:hover .anchor .fa-link, .markdown-body h2:hover .anchor .fa-link, .markdown-body h3:hover .anchor .fa-link, .markdown-body h4:hover .anchor .fa-link, .markdown-body h5:hover .anchor .fa-link, .markdown-body h6:hover .anchor .fa-link { visibility: visible; } .markdown-body h1 tt, .markdown-body h1 code, .markdown-body h2 tt, .markdown-body h2 code, .markdown-body h3 tt, .markdown-body h3 code, .markdown-body h4 tt, .markdown-body h4 code, .markdown-body h5 tt, .markdown-body h5 code, .markdown-body h6 tt, .markdown-body h6 code { font-size: inherit; } .markdown-body h1 { padding-bottom: 0.3em; font-size: 2em; border-bottom: 1px solid #eee; } .markdown-body h2 { padding-bottom: 0.3em; font-size: 1.5em; border-bottom: 1px solid #eee; } .markdown-body h3 { font-size: 1.25em; } .markdown-body h4 { font-size: 1em; } .markdown-body h5 { font-size: 0.875em; } .markdown-body h6 { font-size: 0.85em; color: #777 } .markdown-body ul, .markdown-body ol { padding-left: 2em } .markdown-body ul.no-list, .markdown-body ol.no-list { padding: 0; list-style-type: none; } .markdown-body ul ul, .markdown-body ul ol, .markdown-body ol ol, .markdown-body ol ul { margin-top: 0; margin-bottom: 0 } .markdown-body li>p { margin-top: 16px } .markdown-body li+li { margin-top: 0.25em; } .markdown-body dl { padding: 0; } .markdown-body dl dt { padding: 0; margin-top: 16px; font-size: 1em; font-style: italic; font-weight: bold; } .markdown-body dl dd { padding: 0 16px; margin-bottom: 16px; } .markdown-body table { display: block; width: 100%; overflow: auto; word-break: normal; word-break: keep-all; } .markdown-body table th { font-weight: bold } .markdown-body table th, .markdown-body table td { padding: 6px 13px; border: 1px solid #ddd; } .markdown-body table tr { background-color: #fff; border-top: 1px solid #ccc; } .night .markdown-body table tr { background-color: #5f5f5f; } .markdown-body table tr:nth-child(2n) { background-color: #f8f8f8; } .night .markdown-body table tr:nth-child(2n){ background-color: #4f4f4f; } .markdown-body img { max-width: 100%; box-sizing: content-box; background-color: #fff; } .markdown-body img[align=right] { padding-left: 20px; } .markdown-body img[align=left] { padding-right: 20px; } .markdown-body .emoji { max-width: none; vertical-align: text-top; background-color: transparent; } .markdown-body span.frame { display: block; overflow: hidden; } .markdown-body span.frame>span { display: block; float: left; width: auto; padding: 7px; margin: 13px 0 0; overflow: hidden; border: 1px solid #ddd; } .markdown-body span.frame span img { display: block; float: left; } .markdown-body span.frame span span { display: block; padding: 5px 0 0; clear: both; color: #333; } .markdown-body span.align-center { display: block; overflow: hidden; clear: both; } .markdown-body span.align-center>span { display: block; margin: 13px auto 0; overflow: hidden; text-align: center; } .markdown-body span.align-center span img { margin: 0 auto; text-align: center; } .markdown-body span.align-right { display: block; overflow: hidden; clear: both; } .markdown-body span.align-right>span { display: block; margin: 13px 0 0; overflow: hidden; text-align: right; } .markdown-body span.align-right span img { margin: 0; text-align: right; } .markdown-body span.float-left { display: block; float: left; margin-right: 13px; overflow: hidden; } .markdown-body span.float-left span { margin: 13px 0 0; } .markdown-body span.float-right { display: block; float: right; margin-left: 13px; overflow: hidden; } .markdown-body span.float-right>span { display: block; margin: 13px auto 0; overflow: hidden; text-align: right; } .markdown-body code, .markdown-body tt { padding: 0; padding-top: 0.2em; padding-bottom: 0.2em; margin: 0; font-size: 85%; background-color: rgba(0, 0, 0, 0.04); border-radius: 3px; } .night .markdown-body code, .night .markdown-body tt { color: #eee; background-color: rgba(230, 230, 230, 0.36); } .markdown-body code::before, .markdown-body code::after, .markdown-body tt::before, .markdown-body tt::after { letter-spacing: -0.2em; content: "\00a0"; } .markdown-body code br, .markdown-body tt br { display: none; } .markdown-body del code { text-decoration: inherit; } .markdown-body pre { word-wrap: normal; } .markdown-body pre>code { padding: 0; margin: 0; font-size: 100%; word-break: normal; white-space: pre; background: transparent; border: 0; } .markdown-body .highlight { margin-bottom: 16px; } .markdown-body .highlight pre { margin-bottom: 0; word-break: normal; } .markdown-body .highlight pre, .markdown-body pre { padding: 16px; overflow: auto; font-size: 85%; line-height: 1.45; background-color: #f7f7f7; border-radius: 3px; } .markdown-body pre code, .markdown-body pre tt { display: inline; max-width: auto; padding: 0; margin: 0; overflow: visible; line-height: inherit; word-wrap: normal; background-color: transparent; border: 0; } .markdown-body pre code::before, .markdown-body pre code::after, .markdown-body pre tt::before, .markdown-body pre tt::after { content: normal; } .markdown-body .csv-data td, .markdown-body .csv-data th { padding: 5px; overflow: hidden; font-size: 12px; line-height: 1; text-align: left; white-space: nowrap; } .markdown-body .csv-data .blob-line-num { padding: 10px 8px 9px; text-align: right; background: #fff; border: 0; } .markdown-body .csv-data tr { border-top: 0; } .markdown-body .csv-data th { font-weight: bold; background: #f8f8f8; border-top: 0; } .markdown-body kbd { display: inline-block; padding: 3px 5px; font-size: 11px; line-height: 10px; color: #555; vertical-align: middle; background-color: #fcfcfc; border: solid 1px #ccc; border-bottom-color: #bbb; border-radius: 3px; box-shadow: inset 0 -1px 0 #bbb; } .news .alert .markdown-body blockquote { padding: 0 0 0 40px; border: 0 none; } .activity-tab .news .markdown-body blockquote, .activity-tab .news .alert .commits { padding-left: 0; } .task-list-item { list-style-type: none; } .task-list-item label { font-weight: normal; } .task-list-item.enabled label { cursor: pointer; } .task-list-item+.task-list-item { margin-top: 3px; } .task-list-item-checkbox { float: left; margin: 0.31em 0 0.2em -1.3em !important; vertical-align: middle; cursor: default !important; } ================================================ FILE: public/css/google-font.css ================================================ @import url(https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400italic,600,600italic,300italic,300|Source+Serif+Pro|Source+Code+Pro:400,300,500&subset=latin,latin-ext); ================================================ FILE: public/css/index.css ================================================ html, body { height: 100%; } body { margin: 0; padding: 0; max-width: inherit; min-width: 200px; /*margin-right: 15px;*/ padding-top: 51px; /*overflow: hidden;*/ } .night a, .night .open-files-container li.selected a { color: #5EB7E0; } body.night{ background: #333 !important; } .toolbar { background-color: #1c1c1e; border: 1px solid #343434; } .toolbar > .btn-toolbar { white-space: nowrap; overflow-y: auto; scrollbar-width: none; } .toolbar > .btn-toolbar::-webkit-scrollbar { display: none; } .toolbar > .btn-toolbar > .btn-group { float: none; } .toolbar > .btn-toolbar > .btn-group > span { display: inline-block; float: left; color: #fff; padding: 5px; line-height: 22px; } .toolbar > .btn-toolbar > .btn-group > span.separator { color: #4d4d4d; } .toolbar > .btn-toolbar > .btn-group > .btn { background-color: #1c1c1e; padding: 5px; font-size: 1em; } .toolbar > .btn-toolbar > .btn-group > .btn:hover { background-color: #383a3e; padding: 5px; } .CodeMirror { font-family: "Source Code Pro", Consolas, monaco, monospace; letter-spacing: 0.025em; line-height: 1.25; font-size: 18px; height: auto !important; overflow-y: hidden !important; -webkit-overflow-scrolling: touch; } /* support japanese font */ .CodeMirror[lang^="ja"] { font-family: "Source Code Pro", Consolas, monaco, Meiryo, "MS ゴシック", "MS Gothic", monospace; } /* support zh-tw font */ .CodeMirror[lang="zh-tw"] { font-family: "Source Code Pro", Consolas, monaco, "Microsoft JhengHei", "微軟正黑", monospace; } /* support zh-cn font */ .CodeMirror[lang="zh-cn"] { font-family: "Source Code Pro", Consolas, monaco, "Microsoft YaHei", "微软雅黑", monospace; } .CodeMirror-placeholder { color: #777 !important; } .CodeMirror-scroll { overflow-x: hidden !important; overflow-y: auto !important; } .CodeMirror-code { /*padding-bottom: 36px;*/ } .CodeMirror-gutter-elt { text-align: center; } .CodeMirror-linenumber { /* opacity: 0.5;*/ min-width: 1.5em; text-align: right; } .CodeMirror-gutter.authorship-gutters { width: 8px; } .CodeMirror-matchingtag { background: rgba(255, 255, 255, .1); line-height: 1em; } .CodeMirror-foldmarker { color: #d0d0d0; text-shadow: none; font-family: Arial; font-size: 1em; line-height: .3; cursor: pointer; margin: 2px; padding-bottom: 2px; } .CodeMirror-foldgutter { /*width: 1em;*/ cursor: default; line-height: 100%; } .CodeMirror-foldgutter-open, .CodeMirror-foldgutter-folded { line-height: 1em; cursor: pointer; } .CodeMirror-foldgutter-open { padding-top: 1px; } .CodeMirror-foldgutter-folded { padding-top: 2px; } .CodeMirror-foldgutter-open:after { content: "⌵"; font-size: 1em; /* opacity: 0.5;*/ } .CodeMirror-foldgutter-folded:after { content: "+"; font-size: 1em; font-weight: 700; } .CodeMirror-foldmarker, .CodeMirror-foldgutter-folded:after { color: #78B2F2 !important; } .CodeMirror-sizer { margin-bottom: 0px !important; } .CodeMirror-insert-match { background: lawngreen; border: 1px solid limegreen; -moz-box-sizing: border-box; box-sizing: border-box; opacity: .5; } .CodeMirror-delete-match { background: indianred; border: 1px solid darkred; -moz-box-sizing: border-box; box-sizing: border-box; opacity: .5; } .ui-content { height: 100%; margin-left: 0; margin-right: 0; } .night .ui-content{ background-color: #333; } .ui-edit-area { height: 100%; /*padding-left: 15px;*/ padding-left: 0 !important; padding-right: 0 !important; } .ui-edit-area .ui-resizable-handle.ui-resizable-e { cursor: col-resize; width: 8px; right: -8px; background-color: white; box-shadow: 3px 0px 6px #e7e7e7; } .ui-edit-area .ui-sync-toggle { width: 42px; height: 42px; padding: 2px 1px 0 0; border-radius: 50%; box-shadow: 2px 0px 2px #e7e7e7; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } .night .ui-edit-area .ui-sync-toggle { box-shadow: 2px 0px 2px #353535; } .ui-edit-area .ui-sync-toggle:active { box-shadow: inset 0 3px 5px rgba(0,0,0,.125), 2px 0px 2px #e7e7e7; } .night .ui-edit-area .ui-resizable-handle.ui-resizable-e{ background: #3c3c3c; box-shadow: 3px 0px 6px #353535; } .ui-view-area { /*overflow-y: scroll;*/ -webkit-overflow-scrolling: touch; } @media (min-width: 768px) { .ui-view-area { padding-left: 15px; padding-right: 15px; } } .night .ui-view-area{ background: #333; color: #ededed; } .ui-scrollable { height: 100%; overflow-x: hidden; overflow-y: auto; } .ui-status * { font-size: 16px !important; } .navbar-brand { font-weight: bold; } .nav-status .ui-status * { font-size: 14px; } .nav-mobile { position: inherit; margin-top: 8px; margin-bottom: 8px; } .nav-mobile .dropdown-menu { left: 40%; right: 6px; top: 42px; } .nav-status { float: right !important; padding: 7px 8px; } .ui-status { cursor: auto !important; min-width: 120px; background-color: transparent !important; } .ui-status span { cursor: pointer; } .ui-short-status { cursor: pointer; min-width: 40px; } .ui-short-status:hover { text-decoration: none; } .ui-user-item { /*na*/ } .ui-user-name { margin-top: 2px; } .ui-user-status { margin-top: 5px; } .ui-user-status-online { color: rgb(92,184,92); } .ui-user-status-idle { color: rgb(240,173,78); } .ui-user-status-offline { color: rgb(119,119,119); } .list > li > a { overflow: hidden; text-overflow: ellipsis; } #short-online-user-list .list .name { max-width: 65%; overflow: hidden; text-overflow: ellipsis; float: left; } #online-user-list .list .name { max-width: 110px; overflow: hidden; text-overflow: ellipsis; float: left; } .navbar-right { margin-right: 0; } .navbar-nav > li > a { cursor: pointer; } .night .navbar-default .navbar-nav > li > a:focus, .night .navbar-default .navbar-nav > li > a:hover, .night .navbar-default .navbar-brand:focus, .night .navbar-default .navbar-brand:hover{ color: #fff; } .night .navbar-default .navbar-nav > .open > a, .night .navbar-default .navbar-nav > .open > a:focus, .night .navbar-default .navbar-nav > .open > a:hover { color: white; background: #000; } .night .navbar-default .btn-link { color: #bbb; } .dropdown-menu > li > a { cursor: pointer; text-overflow: ellipsis; max-width: calc(100vw - 30px); overflow: hidden; } .night .dropdown-menu > li > a { color: #eee; } .night .dropdown-menu > li > a:focus, .night .dropdown-menu > li > a:hover { color: #262626 } .night .status-bar .dropdown-menu > li > a:focus, .status-bar .dropdown-menu > li > a:hover { color: #ccc; } .night .dropdown-menu { background: #222; } .night .modal-content, .night .panel, .night .panel-heading { color: #eee; background-color: #333; } .dropdown-menu.CodeMirror-other-cursor { transition: none; } .cursortag { cursor: pointer; background: black; position: absolute; padding: 2px 7px 2px 8px; font-size: 12px; max-width: 150px; text-overflow: ellipsis; overflow: hidden; font-family: inherit; border-radius: .25em; white-space: nowrap; transition: left 0.1s ease-in-out, top 0.1s ease-in-out; } .fixfixed .navbar-fixed-top { position: absolute !important; } div[contenteditable]:empty:not(:focus):before{ content:attr(data-ph); color: gray; } .dropdown-menu.list { max-height: 80vh; overflow: auto; } .dropdown-menu.list.small { max-height: 40vh; overflow: auto; } .dropdown-menu.list::-webkit-scrollbar { display: none; } .dropdown-menu .emoji { margin-bottom: 0 !important; } .dropdown-menu.CodeMirror-other-cursor { max-height: 50vh; width: auto !important; overflow: auto; margin-bottom: 30px; } .CodeMirror-scrollbar-filler { background: inherit; } .night .navbar .btn-default, .night .close { background-color: #333; border-color: #565656; color: #eee; } .night .btn.btn-default.active{ background: #202020; } .btn-file { position: relative; overflow: hidden; } .btn-file input[type=file] { position: absolute; top: 0; right: 0; min-width: 100%; min-height: 100%; font-size: 100px; text-align: right; filter: alpha(opacity=0); opacity: 0; outline: none; background: white; cursor: inherit; display: block; } .night .btn.focus, .night .btn:focus, .night .btn:hover, .night .close { color: #fff; background-color: #333; } .info-label { width: 36%; text-align: right; position: relative; display: inline-block; margin-right: 6px; } .popover { width: 100%; font-family: inherit !important; line-height: 25px; } .text-ellipsis { text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } .cm-trailing-space-a:before, .cm-trailing-space-b:before, .cm-trailing-space-new-line:before { font-weight: bold; color: hsl(30, 100%, 50%); /* a dark orange */ position: absolute; } .cm-trailing-space-a:before, .cm-trailing-space-b:before { content: '·'; } .cm-trailing-space-new-line:before { content: '↵'; } .cm-matchhighlight { /* border-radius: 5px;*/ /* box-shadow: 0 1px 0 0 #ccc;*/ border-bottom: 1px solid #ccc; } .snippet-import-or { text-align: center; width: 100%; } .status-bar { background: #1c1c1e; border-top: 1px solid #343434; color: #ccc; position: relative; display: block; box-sizing: border-box; font-size: 11px; line-height: 25px; height: 26px; } .status-bar .status-info { color: #ccc; left: 10px; position: absolute; white-space: nowrap; max-width: 65%; overflow: hidden; text-overflow: ellipsis; } .status-bar .status-info div { display: inline; } .status-bar .status-file { color: #9a9a9a; } .status-bar .status-indicators { background: #1c1c1e; color: #ccc; position: absolute; right: 0; text-align: right; white-space: nowrap; text-overflow: ellipsis; } .status-bar .status-indicators > div { float: right; padding: 0 10px; border-left: 1px solid #343434; } .status-bar .status-indicators .status-keymap > a, .status-bar .status-indicators .status-theme > a, .status-bar .status-indicators .status-spellcheck > a, .status-bar .status-indicators .status-linter > a, .status-bar .status-indicators .status-preferences > a { color: inherit; text-decoration: none; } .status-bar .status-indicators .status-theme, .status-bar .status-indicators .status-spellcheck, .status-bar .status-indicators .status-linter, .status-bar .status-indicators .status-preferences { padding: 0 4.3px; } .status-bar .status-indicators .status-preferences .dropdown-menu > li > a { cursor: default; } .status-bar .status-indicators .status-preferences .dropdown-menu label { font: inherit; margin-bottom: 0; cursor: pointer; } .status-bar .status-indicators .status-preferences .dropdown-menu label > input[type="checkbox"] { vertical-align: middle; margin: -3px 0 0; } .ui-theme-toggle, .ui-linter-toggle, .ui-spellcheck-toggle { opacity: 0.5; cursor: pointer; } .ui-theme-toggle.active, .ui-linter-toggle.active, .ui-spellcheck-toggle.active { opacity: 1; } .ui-theme-toggle:hover, .ui-linter-toggle:hover, .ui-spellcheck-toggle:hover { opacity: 0.8; } .status-bar .indent-type, .status-bar .indent-width-label { cursor: pointer; /* margin-right: 3px;*/ } .status-bar .indent-width-input { font-size: 12px; font-weight: 500; height: 18px; line-height: 1; vertical-align: middle; color: #ccc; margin: 0; padding: 0 0 2px; position: relative; left: 0; top: -1px; width: 18px; transition: .1s linear all; background-color: #555; border: 1px solid #202020; color: #fff; box-shadow: inset 0 1px 0 rgba(0,0,0,0.06); border-radius: 3px; text-align: center; } .status-bar .indent-width-input:focus { border: 1px solid #2893ef; } .status-bar .indent-width-input::-webkit-inner-spin-button, .status-bar .indent-width-input::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; } .status-bar .status-indent > * { display: inline-block; } .status-bar .status-indent > *.hidden { display: none; } .status-bar .status-overwrite:hover, .status-bar .indent-type:hover, .status-bar .indent-width-label:hover { text-decoration: underline; } .status-bar .dropdown-menu { background-color: #000; color: #fff; border: 1px solid rgba(255,255,255,0.09) !important; } .status-bar .dropdown-menu .divider { background-color: #343434; } .status-bar .dropdown-menu > li > a { color: #ccc; } .status-bar .dropdown-menu > li > a:focus, .status-bar .dropdown-menu > li > a:hover { background-color: #212121; } @media print { body { padding-top: 0 !important; } .CodeMirror { height: auto !important; } } ================================================ FILE: public/css/markdown.css ================================================ /* for markdown-body */ .markdown-body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; padding-top: 40px; padding-bottom: 40px; max-width: 758px; overflow: visible !important; } /*fixed style for bootstrap comflict*/ .markdown-body pre { border: inherit !important; } .night .markdown-body pre { filter: invert(100%); } .markdown-body code { color: inherit !important; } .markdown-body pre code .wrapper { display: -webkit-inline-flex; display: -moz-inline-flex; display: -ms-inline-flex; display: -o-inline-flex; display: inline-flex; } .markdown-body pre code .gutter { float: left; overflow: hidden; -webkit-user-select: none; user-select: none; } .markdown-body pre code .gutter.linenumber { text-align: right; position: relative; display: inline-block; cursor: default; z-index: 4; padding: 0 8px 0 0; min-width: 20px; box-sizing: content-box; color: #afafaf !important; border-right: 3px solid #6ce26c !important; } .markdown-body pre code .gutter.linenumber > span:before { content: attr(data-linenumber); } .markdown-body pre code .code { float: left; margin: 0 0 0 16px; } .markdown-body .gist .line-numbers { border-left: none; border-top: none; border-bottom: none; } .markdown-body .gist .line-data { border: none; } .markdown-body .gist table { border-spacing: 0; border-collapse: inherit !important; } .night .markdown-body .gist table tr:nth-child(2n){ background-color: #ddd; } .markdown-body code[data-gist-id] { background: none; padding: 0; filter: invert(100%); } .markdown-body code[data-gist-id]:before { content: '' } .markdown-body code[data-gist-id]:after { content: '' } .markdown-body code[data-gist-id] .blob-num { border: unset; } .markdown-body code[data-gist-id] table { overflow: unset; margin-bottom: unset; } .markdown-body code[data-gist-id] table tr { background: unset; } /*fixed style for rtl in pre and code*/ .markdown-body[dir='rtl'] pre { direction: ltr; } .markdown-body[dir='rtl'] code { direction: ltr; unicode-bidi: embed; } .markdown-body .alert > p { margin-bottom: 0; } .markdown-body pre.flow-chart, .markdown-body pre.sequence-diagram, .markdown-body pre.graphviz, .markdown-body pre.mermaid, .markdown-body pre.abc, .markdown-body pre.geo, .markdown-body pre.vega { text-align: center; background-color: inherit; border-radius: 0; white-space: inherit; } .night .markdown-body pre.graphviz .graph > polygon{ fill: #333; } .night .markdown-body pre.mermaid .titleText, .night .markdown-body pre.mermaid text, .night .markdown-body pre.mermaid .sectionTitle{ fill: white; } .markdown-body pre.flow-chart > code, .markdown-body pre.sequence-diagram > code, .markdown-body pre.graphviz > code, .markdown-body pre.mermaid > code, .markdown-body pre.abc > code, .markdown-body pre.vega > code { text-align: left; } .markdown-body pre.flow-chart > svg, .markdown-body pre.sequence-diagram > svg, .markdown-body pre.graphviz > svg, .markdown-body pre.mermaid > svg, .markdown-body pre.abc > svg, .markdown-body pre.vega > svg { max-width: 100%; height: 100%; } .night .markdown-body .abc path{ fill: #eee; } .night .markdown-body .abc path.note_selected{ fill: ##4DD0E1; } .night tspan{ fill: #fefefe; } .night pre rect{ fill: transparent; } .night pre.flow-chart rect, .night pre.flow-chart path{ stroke: white; } .markdown-body pre > code.wrap { white-space: pre-wrap; /* Since CSS 2.1 */ white-space: -moz-pre-wrap; /* Mozilla, since 1999 */ white-space: -pre-wrap; /* Opera 4-6 */ white-space: -o-pre-wrap; /* Opera 7 */ word-wrap: break-word; /* Internet Explorer 5.5+ */ } .markdown-body .alert > p, .markdown-body .alert > ul { margin-bottom: 0; } /* Make details boxes look like on GitHub */ .markdown-body summary { display: list-item; } .markdown-body summary:focus { outline: none; } .markdown-body details summary { cursor: pointer; } .markdown-body details:not([open]) > *:not(summary) { display: none; } .markdown-body figure { margin: 1em 40px; } .markdown-body img { background-color: transparent; } ================================================ FILE: public/css/mermaid.css ================================================ /* Flowchart variables */ /* Sequence Diagram variables */ /* Gantt chart variables */ .mermaid .label { color: #333; } /* workaround to solve conflict bootstrap styles */ .mermaid .label { display: unset; padding: unset; font-size: unset; font-weight: unset; line-height: unset; text-align: unset; white-space: unset; vertical-align: unset; border-radius: unset; } .mermaid .node rect, .mermaid .node circle, .mermaid .node ellipse, .mermaid .node polygon { fill: #ECECFF; stroke: #CCCCFF; stroke-width: 1px; } .mermaid .edgePath .path { stroke: #333333; } .mermaid .edgeLabel { background-color: #e8e8e8; } .mermaid .cluster rect { fill: #ffffde !important; rx: 4 !important; stroke: #aaaa33 !important; stroke-width: 1px !important; } .mermaid .cluster text { fill: #333; } .mermaid .actor { stroke: #CCCCFF; fill: #ECECFF; } .mermaid text.actor { fill: black; stroke: none; } .mermaid .actor-line { stroke: grey; } .mermaid .messageLine0 { stroke-width: 1.5; stroke-dasharray: "2 2"; marker-end: "url(#arrowhead)"; stroke: #333; } .mermaid .messageLine1 { stroke-width: 1.5; stroke-dasharray: "2 2"; stroke: #333; } .mermaid #arrowhead { fill: #333; } .mermaid #crosshead path { fill: #333 !important; stroke: #333 !important; } .mermaid .messageText { fill: #333; stroke: none; } .mermaid .labelBox { stroke: #CCCCFF; fill: #ECECFF; } .mermaid .labelText { fill: black; stroke: none; } .mermaid .loopText { fill: black; stroke: none; } .mermaid .loopLine { stroke-width: 2; stroke-dasharray: "2 2"; marker-end: "url(#arrowhead)"; stroke: #CCCCFF; } .mermaid .note { stroke: #aaaa33; fill: #fff5ad; } .mermaid .noteText { fill: black; stroke: none; font-family: 'trebuchet ms', verdana, arial; font-size: 14px; } /** Section styling */ .mermaid .section { stroke: none; opacity: 0.2; } .mermaid .section0 { fill: rgba(102, 102, 255, 0.49); } .mermaid .section2 { fill: #fff400; } .mermaid .section1, .mermaid .section3 { fill: white; opacity: 0.2; } .mermaid .sectionTitle0 { fill: #333; } .mermaid .sectionTitle1 { fill: #333; } .mermaid .sectionTitle2 { fill: #333; } .mermaid .sectionTitle3 { fill: #333; } .mermaid .sectionTitle { text-anchor: start; font-size: 11px; text-height: 14px; } /* Grid and axis */ .mermaid .grid .tick { stroke: lightgrey; opacity: 0.3; shape-rendering: crispEdges; } .mermaid .grid path { stroke-width: 0; } /* Today line */ .mermaid .today { fill: none; stroke: red; stroke-width: 2px; } /* Task styling */ /* Default task */ .mermaid .task { stroke-width: 2; } .mermaid .taskText { text-anchor: middle; font-size: 11px; } .mermaid .taskTextOutsideRight { fill: black; text-anchor: start; font-size: 11px; } .mermaid .taskTextOutsideLeft { fill: black; text-anchor: end; font-size: 11px; } /* Specific task settings for the sections*/ .mermaid .taskText0, .mermaid .taskText1, .mermaid .taskText2, .mermaid .taskText3 { fill: white; } .mermaid .task0, .mermaid .task1, .mermaid .task2, .mermaid .task3 { fill: #8a90dd; stroke: #534fbc; } .mermaid .taskTextOutside0, .mermaid .taskTextOutside2 { fill: black; } .mermaid .taskTextOutside1, .mermaid .taskTextOutside3 { fill: black; } /* Active task */ .mermaid .active0, .mermaid .active1, .mermaid .active2, .mermaid .active3 { fill: #bfc7ff; stroke: #534fbc; } .mermaid .activeText0, .mermaid .activeText1, .mermaid .activeText2, .mermaid .activeText3 { fill: black !important; } /* Completed task */ .mermaid .done0, .mermaid .done1, .mermaid .done2, .mermaid .done3 { stroke: grey; fill: lightgrey; stroke-width: 2; } .mermaid .doneText0, .mermaid .doneText1, .mermaid .doneText2, .mermaid .doneText3 { fill: black !important; } /* Tasks on the critical line */ .mermaid .crit0, .mermaid .crit1, .mermaid .crit2, .mermaid .crit3 { stroke: #ff8888; fill: red; stroke-width: 2; } .mermaid .activeCrit0, .mermaid .activeCrit1, .mermaid .activeCrit2, .mermaid .activeCrit3 { stroke: #ff8888; fill: #bfc7ff; stroke-width: 2; } .mermaid .doneCrit0, .mermaid .doneCrit1, .mermaid .doneCrit2, .mermaid .doneCrit3 { stroke: #ff8888; fill: lightgrey; stroke-width: 2; cursor: pointer; shape-rendering: crispEdges; } .mermaid .doneCritText0, .mermaid .doneCritText1, .mermaid .doneCritText2, .mermaid .doneCritText3 { fill: black !important; } .mermaid .activeCritText0, .mermaid .activeCritText1, .mermaid .activeCritText2, .mermaid .activeCritText3 { fill: black !important; } .mermaid .titleText { text-anchor: middle; font-size: 18px; fill: black; } /* */ .mermaid .node text { font-family: 'trebuchet ms', verdana, arial; font-size: 14px; } .mermaid div.mermaidTooltip { position: absolute; text-align: center; max-width: 200px; padding: 2px; font-family: 'trebuchet ms', verdana, arial; font-size: 12px; background: #ffffde; border: 1px solid #aaaa33; border-radius: 2px; pointer-events: none; z-index: 100; } ================================================ FILE: public/css/site.css ================================================ /* for all pages should include this */ body { font-smoothing: subpixel-antialiased !important; -webkit-font-smoothing: subpixel-antialiased !important; -moz-osx-font-smoothing: auto !important; text-shadow: 0 0 1em transparent, 1px 1px 1.2px rgba(0, 0, 0, 0.004); /*text-rendering: optimizeLegibility;*/ -webkit-overflow-scrolling: touch; font-family: "Source Sans Pro", Helvetica, Arial, sans-serif; letter-spacing: 0.025em; } :focus, .focus { outline: none !important; } ::-moz-focus-inner { border: 0 !important; } /* manual fix for bootstrap issue 14040, there is an unnecessary padding-right on modal open */ body.modal-open { overflow-y: auto; padding-right: 0 !important; } ================================================ FILE: public/css/slide-preview.css ================================================ .markdown-body.slides { position: relative; z-index: 1; color: #222; } .markdown-body.slides::before { content: ''; display: block; position: absolute; top: 0; left: 0; right: 0; bottom: 0; z-index: -1; background-color: currentColor; box-shadow: 0 0 0 50vw; } .markdown-body.slides section[data-markdown] { position: relative; margin-bottom: 1.5em; background-color: #fff; text-align: center; } .markdown-body.slides section[data-markdown] code { text-align: left; } .markdown-body.slides section[data-markdown]::before { content: ''; display: block; padding-bottom: 56.23%; } .markdown-body.slides section[data-markdown] > div:first-child { position: absolute; top: 50%; left: 1em; right: 1em; transform: translateY(-50%); max-height: 100%; overflow: hidden; } .markdown-body.slides section[data-markdown] > ul { display: inline-block; } .markdown-body.slides > section > section + section::after { content: ''; position: absolute; top: -1.5em; right: 1em; height: 1.5em; border: 3px solid #777; } ================================================ FILE: public/css/slide.css ================================================ .reveal { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; } .reveal h1, .reveal h2, .reveal h3, .reveal h4, .reveal h5, .reveal h6 { font-family: inherit; text-transform: initial; } .reveal pre, .reveal code { font-family: Menlo, Monaco, Consolas, "Courier New", monospace; } .reveal.fade { opacity: unset; -webkit-transition: unset; transition: unset; } .vimeo, .youtube { position: relative !important; cursor: pointer; display: table; width: 100% !important; text-align: center; background-position: center center; background-repeat: no-repeat; background-size: contain; background-color: black; overflow: hidden; font-size: 24px !important; } /* youtube always use 16:9 aspect ratio video */ .vimeo, .youtube { position: relative; width: 100%; padding-bottom: 56.25% !important; } .vimeo > img, .youtube > img { max-width: 100%; max-height: 100%; margin: 0 !important; border: 0 !important; width: 100%; height: 100%; object-fit: cover; position: absolute; top: 0; left: 0; z-index: 0; } .vimeo > iframe, .youtube > iframe { max-width: 100%; max-height: 100%; } .slideshare .inner, .speakerdeck .inner { padding-bottom: 62% !important; } .pdfobject { height: 85vh !important; } .task-list-item-checkbox { font-size: inherit; height: 1em; transform: scale(2); margin: 0.15em 0 0.15em -0.84em !important; } pre code .wrapper { display: -webkit-inline-flex; display: -moz-inline-flex; display: -ms-inline-flex; display: -o-inline-flex; display: inline-flex; } pre code .gutter { float: left; overflow: hidden; -webkit-user-select: none; user-select: none; } pre code .gutter.linenumber { text-align: right; position: relative; display: inline-block; cursor: default; z-index: 4; padding: 0 8px 0 0; min-width: 20px; box-sizing: content-box; color: #afafaf !important; border-right: 3px solid #6ce26c !important; } pre code .gutter.linenumber > span:before { content: attr(data-linenumber); } pre code .code { float: left; margin: 0 0 0 16px; } .gist .line-numbers { border-left: none; border-top: none; border-bottom: none; } .gist .line-data { border: none; } .gist table { border-spacing: 0; border-collapse: inherit !important; } code[data-gist-id] { background: none; padding: 0; } code[data-gist-id]:before { content: '' } code[data-gist-id]:after { content: '' } .alert { border-radius: 4px; } .alert h4 { margin-top: 0; color: inherit; } .alert > p { padding: 5px !important; } .alert-success { color: #3c763d; background-color: #dff0d8; border-color: #d6e9c6; } .alert-success hr { border-top-color: #c9e2b3; } .alert-success .alert-link { color: #2b542c; } .alert-info { color: #31708f; background-color: #d9edf7; border-color: #bce8f1; } .alert-info hr { border-top-color: #a6e1ec; } .alert-info .alert-link { color: #245269; } .alert-warning { color: #8a6d3b; background-color: #fcf8e3; border-color: #faebcc; } .alert-warning hr { border-top-color: #f7e1b5; } .alert-warning .alert-link { color: #66512c; } .alert-danger { color: #a94442; background-color: #f2dede; border-color: #ebccd1; } .alert-danger hr { border-top-color: #e4b9c0; } .alert-danger .alert-link { color: #843534; } pre.flow-chart, pre.sequence-diagram, pre.graphviz, pre.mermaid, pre.abc, pre.geo, pre.vega { text-align: center; background-color: white; border-radius: 0; white-space: inherit; } pre.flow-chart > code, pre.sequence-diagram > code, pre.graphviz > code, pre.mermaid > code, pre.abc > code, pre.vega > code { text-align: left; } pre.flow-chart > svg, pre.sequence-diagram > svg, pre.graphviz > svg, pre.mermaid > svg, pre.abc > svg, pre.vega > svg { max-width: 100%; height: 100%; } .emoji { margin: 0 !important; background: transparent !important; border: none !important; box-shadow: none !important; margin-bottom: -.25em !important; } .slides, #meta { display: none; } .reveal.rtl .slides, .reveal.rtl .slides h1, .reveal.rtl .slides h2, .reveal.rtl .slides h3, .reveal.rtl .slides h4, .reveal.rtl .slides h5, .reveal.rtl .slides h6 { direction: rtl; font-family: inherit; } .text-uppercase { text-transform: uppercase; } .footer { background-color: white; padding: 25px 15px; } .footer .gray-font { color: #777; } .footer > * { margin-left: auto; margin-right: auto; max-width: 758px; } .footer .ui-no-lastchangeuser { width: 18px; } .footer .slides-disqus { margin-top: 25px; margin-bottom: 15px; } html, body { height: 100%; width: 100%; overflow: hidden; } .container { position: absolute; top: 0; bottom: 0; left: 0; right: 0; overflow-y: scroll; } .container.hidescrollbar { right: -17px; } .reveal { height: 100vh; } .reveal .progress, .reveal .slide-number, .reveal .playback, .reveal .controls { position: absolute; } .print-pdf .container { position: relative; overflow-y: hidden; } .print-pdf .container.hidescrollbar { right: 0; } .print-pdf .footer { display: none; } .markmap-container { background: #f7f7f7; } ================================================ FILE: public/default.md ================================================ ================================================ FILE: public/docs/features.md ================================================ Features === Introduction === **CodiMD** is a real-time, multi-platform collaborative markdown note editor. This means that you can write notes with other people on your **desktop**, **tablet** or even on the **phone**. You can sign-in via multiple auth providers like **Facebook**, **Twitter**, **GitHub** and many more on the [_homepage_](/). If you experience any _issues_, feel free to report it on [**GitHub**](https://github.com/hackmdio/codimd/issues). Or meet us on [**Gitter**](https://gitter.im/hackmdio/hackmd) for dev-talk and interactive help. **Thank you very much!** Workspace === ## Modes **Desktop & Tablet** Edit: See only the editor. View: See only the result. Both: See both in split view. **Mobile** View: See only the result. Edit: See only the editor. ## Night Mode: When you are tired of a white screen and like a night mode, click on the little moon and turn on the night view of CodiMD. The editor view, which is in night mode by default, can also be toggled between night and day view using the the little sun. ## Image Upload: You can upload an image simply by clicking on the camera button . Alternatively, you can **drag-n-drop** an image into the editor. Even **pasting** images is possible! This will automatically upload the image to **[imgur](http://imgur.com)**, **[Amazon S3](https://aws.amazon.com/s3/)**, **[Minio](https://minio.io)** or **local filesystem**, nothing to worry about. :tada: ![imgur](https://i.imgur.com/9cgQVqD.png) ## Share Notes: If you want to share an **editable** note, just copy the URL. If you want to share a **read-only** note, simply press publish button and copy the URL. ## Save a Note: Currently, you can save to **Dropbox** or save an `.md` file locally. ## Import Notes: Similarly to the _save_ feature, you can also import an `.md` file from **Dropbox** , or import content from your **clipboard** , and that can parse some **html** which might be useful :smiley: ## Permissions: It is possible to change the access permission to a note through the little button on the top right of the view. There are four possible options: | |Owner read/write|Signed-in read|Signed-in write|Guest read|Guest write| |:-----------------------------|:--------------:|:------------:|:-------------:|:--------:|:---------:| | **Freely** |✔|✔|✔|✔|✔| | **Editable** |✔|✔|✔|✔|✖| | **Limited** |✔|✔|✔|✖|✖| | **Locked** |✔|✔|✖|✔|✖| | **Protected** |✔|✔|✖|✖|✖| | **Private** |✔|✖|✖|✖|✖| **Only the owner of the note can change the note's permissions.** ## Embed a Note: Notes can be embedded as follows: ```xml ``` ## [Slide Mode](./slide-example): You can use a special syntax to organize your note into slides. After that, you can use the **[Slide Mode](./slide-example)** to make a presentation. Visit the above link for details. To switch the editor into slide mode, set the [document type](./yaml-metadata#type) to `slide`. View === ## Table of Contents: You can look at the bottom right section of the view area, there is a _ToC_ button . Pressing that button will show you a current _Table of Contents_, and will highlight which section you're at. ToCs support up to **five header levels**, the **default** is **set to three**. The maxLevel can be set for each note by using [YAML Metadata](./yaml-metadata) ## Permalink Every header will automatically add a permalink on the right side. You can hover and click to anchor on it. Edit: === ## Editor Modes: You can look in the bottom right section of the editor area, there you'll find a button with `sublime` on it. When you click it, you can select 3 editor modes: - sublime (default) - emacs - vim ## Shortcut Keys: The shortcut keys depend on your selected editor mode. By default they are just like Sublime text, which is pretty quick and convenient. > For more information, see [here](https://codemirror.net/demo/sublime.html). For emacs: > For more information, see [here](https://codemirror.net/demo/emacs.html). For vim: > For more information, see [here](https://codemirror.net/demo/vim.html). ## Auto-Complete: This editor provides full auto-complete hints in markdown. - Emojis: type `:` to show hints. - Code blocks: type ` ``` ` and plus a character to show hint. - Headers: type `#` to show hint. - Referrals: type `[]` to show hint. - Externals: type `{}` to show hint. - Images: type `!` to show hint. ## Title: This will take the first **level 1 header** as the note title. ## Tags: Using tags as follows, the specified tags will show in your **history**. ###### tags: `features` `cool` `updated` ## [YAML Metadata](./yaml-metadata) You can provide advanced note information to set the browser behavior (visit above link for details): - robots: set web robots meta - lang: set browser language - dir: set text direction - breaks: set to use line breaks - GA: set to use Google Analytics - disqus: set to use Disqus - slideOptions: setup slide mode options - toc: set options of the Table of Contents. ## ToC: Use the syntax `[TOC]` to embed table of content into your note. By default, three header levels are displayed. This can also be specified by using [YAML Metadata](./yaml-metadata). [TOC] You can also specify the number of header levels by specifying the `maxLevel` like this: `[TOC maxLevel=1]` [TOC maxLevel=1] ## Emoji You can type any emoji like this :smile: :smiley: :cry: :wink: > See full emoji list [here](http://www.emoji-cheat-sheet.com/). ## ToDo List: - [ ] ToDos - [x] Buy some salad - [ ] Brush teeth - [x] Drink some water ## Code Block: We support many programming languages, use the auto complete function to see the entire list. ```javascript= var s = "JavaScript syntax highlighting"; alert(s); function $initHighlight(block, cls) { try { if (cls.search(/\bno\-highlight\b/) != -1) return process(block, true, 0x0F) + ' class=""'; } catch (e) { /* handle exception */ } for (var i = 0 / 2; i < classes.length; i++) { if (checkCondition(classes[i]) === undefined) return /\d+[\s/]/g; } } ``` > If you want **line numbers**, type `=` after specifying the code block languagues. > Also, you can specify the start line number. > Like below, the line number starts from 101: ```javascript=101 var s = "JavaScript syntax highlighting"; alert(s); function $initHighlight(block, cls) { try { if (cls.search(/\bno\-highlight\b/) != -1) return process(block, true, 0x0F) + ' class=""'; } catch (e) { /* handle exception */ } for (var i = 0 / 2; i < classes.length; i++) { if (checkCondition(classes[i]) === undefined) return /\d+[\s/]/g; } } ``` > Or you might want to continue the previous code block's line number, use `=+` ```javascript=+ var s = "JavaScript syntax highlighting"; alert(s); ``` > Somtimes you have a super long text without breaks. It's time to use `!` to wrap your code. ```! When you’re a carpenter making a beautiful chest of drawers, you’re not going to use a piece of plywood on the back. ``` ### Blockquote Tags: > Using the syntax below to specifiy your **name, time and color** to vary the blockquotes. > [name=ChengHan Wu] [time=Sun, Jun 28, 2015 9:59 PM] [color=#907bf7] > > Even support the nest blockquotes! > > [name=ChengHan Wu] [time=Sun, Jun 28, 2015 10:00 PM] [color=red] ### Render CSV as table You can use write csv in the codeblock: ~~~md ```csvpreview {header="true"} firstName,lastName,email,phoneNumber John,Doe,john@doe.com,0123456789 Jane,Doe,jane@doe.com,9876543210 James,Bond,james.bond@mi6.co.uk,0612345678 ``` ~~~ which rendered to: ```csvpreview {header="true"} firstName,lastName,email,phoneNumber John,Doe,john@doe.com,0123456789 Jane,Doe,jane@doe.com,9876543210 James,Bond,james.bond@mi6.co.uk,0612345678 ``` We use [Papa Parse](https://www.papaparse.com/) for parsing csv. The parsing option is given in braces: `{}`, and multiple options are seperated by a space. e.g. `{header="true" delimiter="."}`. Please read [their documentation](https://www.papaparse.com/docs#config) as reference. ## Externals ### YouTube {%youtube aqz-KE-bpKQ %} ### Vimeo {%vimeo 124148255 %} ### Gist {%gist schacon/4277%} ### SlideShare {%slideshare briansolis/26-disruptive-technology-trends-2016-2018-56796196 %} ### PDF **Caution: this might be blocked by your browser if not using an `https` URL.** {%pdf https://www.w3.org/TR/WAI-WEBCONTENT/wai-pageauth.pdf %} ## MathJax You can render *LaTeX* mathematical expressions using **MathJax**, as on [math.stackexchange.com](http://math.stackexchange.com/): The *Gamma function* satisfying $\Gamma(n) = (n-1)!\quad\forall n\in\mathbb N$ is via the Euler integral $$ x = {-b \pm \sqrt{b^2-4ac} \over 2a}. $$ $$ \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. $$ > More information about **LaTeX** mathematical expressions [here](http://meta.math.stackexchange.com/questions/5020/mathjax-basic-tutorial-and-quick-reference). ## UML Diagrams ### Sequence Diagrams You can render sequence diagrams like this: ```sequence Alice->Bob: Hello Bob, how are you? Note right of Bob: Bob thinks Bob-->Alice: I am good thanks! Note left of Alice: Alice responds Alice->Bob: Where have you been? ``` ### Flow Charts Flow charts can be specified like this: ```flow st=>start: Start e=>end: End op=>operation: My Operation op2=>operation: lalala cond=>condition: Yes or No? st->op->op2->cond cond(yes)->e cond(no)->op2 ``` ### Graphviz ```graphviz digraph hierarchy { nodesep=1.0 // increases the separation between nodes node [color=Red,fontname=Courier,shape=box] //All nodes will this shape and colour edge [color=Blue, style=dashed] //All the lines look like this Headteacher->{Deputy1 Deputy2 BusinessManager} Deputy1->{Teacher1 Teacher2} BusinessManager->ITManager {rank=same;ITManager Teacher1 Teacher2} // Put them on the same level } ``` ### Mermaid ```mermaid gantt title A Gantt Diagram section Section A task :a1, 2014-01-01, 30d Another task :after a1 , 20d section Another Task in sec :2014-01-12 , 12d anther task : 24d ``` ### PlantUML ```plantuml start if (condition A) then (yes) :Text 1; elseif (condition B) then (yes) :Text 2; stop elseif (condition C) then (yes) :Text 3; elseif (condition D) then (yes) :Text 4; else (nothing) :Text else; endif stop ``` ### Vega-Lite ```vega { "$schema": "https://vega.github.io/schema/vega-lite/v4.json", "data": {"url": "https://vega.github.io/editor/data/barley.json"}, "mark": "bar", "encoding": { "x": {"aggregate": "sum", "field": "yield", "type": "quantitative"}, "y": {"field": "variety", "type": "nominal"}, "color": {"field": "site", "type": "nominal"} } } ``` ### Mindmap ```markmap # markmap-lib ## Links - - [GitHub](https://github.com/gera2ld/markmap-lib) ## Related - [coc-markmap](https://github.com/gera2ld/coc-markmap) - [gatsby-remark-markmap](https://github.com/gera2ld/gatsby-remark-markmap) ## Features - links - **inline** ~~text~~ *styles* - multiline text ``` > More information about **sequence diagrams** syntax [here](http://bramp.github.io/js-sequence-diagrams/). > More information about **flow charts** syntax [here](http://adrai.github.io/flowchart.js/). > More information about **graphviz** syntax [here](http://www.tonyballantyne.com/graphs.html) > More information about **mermaid** syntax [here](http://mermaid-js.github.io/mermaid) > More information about **abc** syntax [here](http://abcnotation.com/learn) > More information about **plantuml** syntax [here](http://plantuml.com/index) > More information about **vega** syntax [here](https://vega.github.io/vega-lite/docs) > More information about **fretboard** syntax [here](https://hackmd.io/@docs/fretboard-syntax) Alert Area --- :::success Yes :tada: ::: :::info This is a message :mega: ::: :::warning Watch out :zap: ::: :::danger Oh No! :fire: ::: :::spoiler Click to show details You found me :stuck_out_tongue_winking_eye: ::: ## Music ### Abc ```abc X:1 T:Speed the Plough M:4/4 C:Trad. K:G |:GABc dedB|dedB dedB|c2ec B2dB|c2A2 A2BA| GABc dedB|dedB dedB|c2ec B2dB|A2F2 G4:| |:g2gf gdBd|g2f2 e2d2|c2ec B2dB|c2A2 A2df| g2gf g2Bd|g2f2 e2d2|c2ec B2dB|A2F2 G4:| ``` ### Fretboard ```fretboard {title="horizontal, 6 frets, with nut", type="h6"} -oO-*- --o-o- -o-oo- -o-oO- -oo-o- -*O-o- 3 ``` ## Typography ### Headers ``` # h1 Heading ## h2 Heading ### h3 Heading #### h4 Heading ##### h5 Heading ###### h6 Heading ``` ### Horizontal Rules ___ --- *** ### Typographic Replacements Enable typographer option to see result. (c) (C) (r) (R) (tm) (TM) (p) (P) +- test.. test... test..... test?..... test!.... !!!!!! ???? ,, Remarkable -- awesome "Smartypants, double quotes" 'Smartypants, single quotes' ### Emphasis **This is bold text** __This is bold text__ *This is italic text* _This is italic text_ ~~Deleted text~~ lu~lala~ Superscript: 19^th^ Subscript: H~2~O ++Inserted text++ ==Marked text== {ruby base|rubytext} ### Blockquotes > Blockquotes can also be nested... >> ...by using additional greater-than signs right next to each other... > > > ...or with spaces between arrows. ### Lists #### Unordered + Create a list by starting a line with `+`, `-`, or `*` + Sub-lists are made by indenting 2 spaces: - Marker character change forces new list start: * Ac tristique libero volutpat at + Facilisis in pretium nisl aliquet - Nulla volutpat aliquam velit + Very easy! #### Ordered 1. Lorem ipsum dolor sit amet 2. Consectetur adipiscing elit 3. Integer molestie lorem at massa 1. You can use sequential numbers... 1. ...or keep all the numbers as `1.` 1. feafw 2. 332 3. 242 4. 2552 1. e2 Start numbering with offset: 57. foo 1. bar ### Code Inline `code` Indented code // Some comments line 1 of code line 2 of code line 3 of code Block code "fences" ``` Sample text here... ``` Syntax highlighting ``` js var foo = function (bar) { return bar++; }; console.log(foo(5)); ``` ### Tables | Option | Description | | ------ | ----------- | | data | path to data files to supply the data that will be passed into templates. | | engine | engine to be used for processing templates. Handlebars is the default. | | ext | extension to be used for dest files. | Right aligned columns | Option | Description | | ------:| -----------:| | data | path to data files to supply the data that will be passed into templates. | | engine | engine to be used for processing templates. Handlebars is the default. | | ext | extension to be used for dest files. | Left aligned columns | Option | Description | |:------ |:----------- | | data | path to data files to supply the data that will be passed into templates. | | engine | engine to be used for processing templates. Handlebars is the default. | | ext | extension to be used for dest files. | Center aligned columns | Option | Description | |:------:|:-----------:| | data | path to data files to supply the data that will be passed into templates. | | engine | engine to be used for processing templates. Handlebars is the default. | | ext | extension to be used for dest files. | ### Links [link text](http://dev.nodeca.com) [link with title](http://nodeca.github.io/pica/demo/ "title text!") Autoconverted link https://github.com/nodeca/pica ### Images ![Minion](https://octodex.github.com/images/minion.png) ![Stormtroopocat](https://octodex.github.com/images/stormtroopocat.jpg "The Stormtroopocat") Like links, Images also have a footnote style syntax ![Alt text][id] With a reference later in the document defining the URL location: [id]: https://octodex.github.com/images/dojocat.jpg "The Dojocat" ![Minion](https://octodex.github.com/images/minion.png =200x200) Show the image with given size ### Footnotes Footnote 1 link[^first]. Footnote 2 link[^second]. Inline footnote^[Text of inline footnote] definition. Duplicated footnote reference[^second]. [^first]: Footnote **can have markup** and multiple paragraphs. [^second]: Footnote text. ### Definition Lists Term 1 : Definition 1 with lazy continuation. Term 2 with *inline markup* : Definition 2 { some code, part of Definition 2 } Third paragraph of definition 2. _Compact style:_ Term 1 ~ Definition 1 Term 2 ~ Definition 2a ~ Definition 2b ### Abbreviations This is an HTML abbreviation example. It converts "HTML", but keeps intact partial entries like "xxxHTMLyyy" and so on. *[HTML]: Hyper Text Markup Language ================================================ FILE: public/docs/privacy.md.example ================================================ Privacy === We process the following data, for the following purposes: |your data|our usage| |---------|---------| |IP-Address|Used to communicate with your browser and our servers. It's may exposed to third-parties which provide resources for this service. These services are, depending on your login method, the document you visit and the setup of this instance: Google, Disqus, MathJax, GitHub, SlideShare/LinkedIn, yahoo, Gravatar, Imgur, Amazon, and Cloudflare.| |Usernames and profiles|Your username as well as user profiles that are connected with it are transmitted and stored by us to provide a useful login integration with services like GitHub, Facebook, Twitter, GitLab, Dropbox, Google. Depending on the setup of this CodiMD instance there are maybe other third-parties involved using SAML, LDAP or the integration with a Mattermost instance.| |Profile pictures| Your profile picture is either loaded from the service you used to login, the CodiMD instance or Gravatar.| |Uploaded pictures| Pictures that are uploaded for documents are either uploaded to Amazon S3, Imgur, a minio instance or the local filesystem of the CodiMD server.| All account data and notes are stored in a mysql/postgres/sqlite database. Besides the user accounts and the document themselves also relationships between the documents and the user accounts are stored. This includes ownership, authorship and revisions of all changes made during the creation of a note. To delete your account and all your notes owned by your user account, you can find a button in the drop down menu on the front page. The deletion of guest notes is not possible. These don't have any ownership and this means we can't connect these to you or anyone else. If you participated in a guest note or a note owned by someone else, your authorship for the revisions is removed from these notes as well. But the content you created will stay in place as the integrity of these notes has to stay untouched. ================================================ FILE: public/docs/release-notes.md ================================================ Release Notes === 2.6.0 2025-06-10 --- [Check out the complete release note][v2_6_0]. [v2_6_0]: https://hackmd.io/@codimd/release-notes/%2F%40codimd%2Fv2_6_0 This minor release includes several enhancements and fixes to improve the overall functionality and performance of CodiMD. ## Enhancements - Update GitHub Actions to use the latest versions [#1895](https://github.com/hackmdio/codimd/pull/1895) - Update ESLint configuration for ES6 support [#1900](https://github.com/hackmdio/codimd/pull/1900) - Add preference to disable table editor shortcuts and migrate preferences to localStorage [#1901](https://github.com/hackmdio/codimd/pull/1901) - Add PDF URL validation and content type check [#1896](https://github.com/hackmdio/codimd/pull/1896) - Upgrade mermaid to the latest version [#1894](https://github.com/hackmdio/codimd/pull/1894) - Add baseURL configuration option to S3 modules [#1876](https://github.com/hackmdio/codimd/pull/1876) - Improve contrast on certain elements [#1899](https://github.com/hackmdio/codimd/pull/1899) - Bump codemirror version to 5.65.8 [#1908](https://github.com/hackmdio/codimd/pull/1908) - Add state parameter to OAuth strategies for enhanced security [#1902](https://github.com/hackmdio/codimd/pull/1902) - Configure MiniCssExtractPlugin for production environment in webpack [#1897](https://github.com/hackmdio/codimd/pull/1897) - Refactor plugin paths and move custom reveal plugin [#1898](https://github.com/hackmdio/codimd/pull/1898) - Update dependencies for abcjs and mermaid.js [#1914](https://github.com/hackmdio/codimd/pull/1914) - VSCode: update image to allow development on MBP ARM chips [#1913](https://github.com/hackmdio/codimd/pull/1913) ## Fixes - Fix: login email should be case insensitive [#1911](https://github.com/hackmdio/codimd/pull/1911) - Fix: Lost schema of S3 URL [#1893](https://github.com/hackmdio/codimd/pull/1893) - Chore: update docker-compose.yml with build instructions for codimd service [#1917](https://github.com/hackmdio/codimd/pull/1917) 2.5.4 2024-06-06 --- [Check out the complete release note][v2_5_4]. Thank you CodiMD community and all our contributors. ❤️ [v2_5_4]: https://hackmd.io/@codimd/release-notes/%2F%40codimd%2Fv2_5_4 ## Enhancements - Add index at revision table for improving system performance [#1856](https://github.com/hackmdio/codimd/pull/1856) - Refactor to reuse random filename in filesystem image provider [#1867](https://github.com/hackmdio/codimd/pull/1867) ## Fixes - Fix exclusion of name attribute from iframe filterXSS allowlist [#1865](https://github.com/hackmdio/codimd/pull/1865) - Fix typo: "opened source" -> "open sourced" [#1869](https://github.com/hackmdio/codimd/pull/1869) 2.5.3 2024-01-08 --- [Check out the complete release note][v2_5_3]. Thank you CodiMD community and all our contributors. ❤️ [v2_5_3]: https://hackmd.io/@codimd/release-notes/%2F%40codimd%2Fv2_5_3 It's a minor fix release that bumps the version number in `package.json` ## Enhancements - Add `codeium-chrome` extension support [#1851](https://github.com/hackmdio/codimd/pull/1851) - Add `check-release` GitHub action workflow that prevents us from forgetting to update the `package.json` during releases again [#1852](https://github.com/hackmdio/codimd/pull/1852) 2.5.2 2024-01-05 --- [Check out the complete release note][v2_5_2]. Thank you CodiMD community and all our contributors. ❤️ [v2_5_2]: https://hackmd.io/@codimd/release-notes/%2F%40codimd%2Fv2_5_2 This is another recovery release that fixes the pandoc DoS issue. ## Enhancements - Build docker image using github actions ## Fixes - **\[Security Issue]** address denial of service issue in actionPandoc 2.5.1 2024-01-03 --- [Check out the complete release note][v2_5_1]. Thank you CodiMD community and all our contributors. ❤️ [v2_5_1]: https://hackmd.io/@codimd/release-notes/%2F%40codimd%2Fv2_5_1 ## Security Fixes - **\[Security Issue]** Bump `@hackmd/pandoc.js` version to 0.2.0 ## Fixes - Replace mattermost-redux with mattermost/client - Fix dependency resolving with prom-client v12 2.5.0 The Formosan hare 2023-12-26 ---
The Formosan hare
> The Formosan hare (scientific name: Lepus sinensis formosus), a species of the rabbit family, is a subspecies unique to Taiwan. It measures 30-40 centimeters in length, with a tail that's 5-6 centimeters long and ears that are 8-10 centimeters long. Smaller than the Chinese hare, it has brownish eyes. > [Wikipedia](https://zh.wikipedia.org/wiki/%E5%8F%B0%E7%81%A3%E9%87%8E%E5%85%94?oldformat=true) [Check out the complete release note][v2_5_0]. Thank you CodiMD community and all our contributors. ❤️ [v2_5_0]: https://hackmd.io/@codimd/release-notes/%2F%40codimd%2Fv2_5_0 ## Security Fixes * **\[Security Issue]** Strip HTML tags for gist id to avoid stored XSS on showing error [Security Issue] * **\[Security Issue]** Upgrade mermaid to version 8.10.2 to avoid prototype pollution * **\[Security Issue]** potential XSS in vimeo embed * **\[Security Issue]** FIX: pandoc security issue * **\[Security Issue]** fix: sanitize pdf url to prevent XSS on inline PDFs ## Fixes * Avoid append zero suffix on exporting user data * Handle when request url has no valid referer * Fix S3 client config passing for image upload * Set a proper "lang" attribute on * Fix matchInContainer false positives * Convert "include" directives to functions * Move HTML-related code from JS to EJS to enable more i18n * fix: may referernce out of bound index in clearDuplicatedHistory * Feat/csrf export user data * sequelize.import deprecation * chore: remove unused uglifyjs-webpack-plugin dep * fix: should not clear guest history when guest pin note * Fix: s3 api supported multiple cloud providers. fixes: https://github.com/hackmdio/codimd/issues/1761 * Fix: Code Fence parameter parsing * Update README.md to remove IE from supporting list * FIX: server crash when filename too long * fix: use encoded note id to update history * 🐛 [fix] modify replacement rule for disqus short-name * Fix history page nav * Fix the uploadimage form * Add the logout callback to prevent exception ## Enhancements * Add TeX mhchem extensions for MathJax * Upgrade flowchart.js to version 1.15.0 * Upgrade codemirror to 5.63.2 * Update de.json in * Documentation - add Music section and move abc abd fretboard to this section * chore: bump meta-marked to 0.5.0 * Typos + Better translation for "Externals" * feat: Migrate to gtag and support GA4 * 【fix】reword japanese * upgrading pg to 8.8.0 to support new scram-sha-256 authentication * feat: add organizations whitelist to GitHub OAuth * Add oauth2 authorization * Update both Traditional and Simplified Chinese locales ## DX * Run CI with GitHub Actions * Add dev container for GitHub Codespaces and VSCode remote container * Add arm64 docker image build. * fix(buildpacks): replace custom buildpack with APT buildpack * Update minimum required node.js version to v12 with npm package dependencies * Upgrade Node.js version * Update node.js version in .nvmrc * Update npm dependencies 2.4.2 2022-08-06 --- ## Security Fixes - **[Security Issue]** Upgrade flowchart.js to version 1.15.0 - **[Security Issue]** Upgrade mermaid to 8.10.2 - **[Security Issue]** Strip HTML tags for gist id to avoid stored XSS on showing error - **[Security Issue]** Add CSRF token in export API to prevent security issue - **[Security Issue]** Upgrade CodeMirror to 5.63.2 ## Fixes - Fix container syntax not parsed correctly - Handle request url has no valid referer case - Fix S3 client config passing for image upload - Fix array access index may out of bound - Remove unused uglifyjs webpack plugin dependency ## Enhancements - Set lang attributes via user locale - Use include function instead of directives - Extract more keyword for i18n translate - Avoid append zero suffix on exporting user data - Add TeX mhchem extensions for MathJax - Support arm64 docker image - Refactor Sequelize model import mechanism due to sequelize.import is deprecated - Better german translation ## DX - Support DevContainer for GitHub Codespaces and VSCode remote container - Run CI with GitHub Actions 2.4.0 Papilio maraho 2021-05-11 ---
Papilio maraho
> Papilio maraho is a species of butterfly in the family Papilionidae. It is endemic to Taiwan. > \- Wikipedia [Papilio maraho](https://en.wikipedia.org/wiki/Papilio_maraho) [Check out the complete release note][v2_4_0]. Thank you CodiMD community and all our contributors. ❤️ [v2_4_0]: https://hackmd.io/@codimd/release-notes/%2F%40codimd%2Fv2_4_0 ## Enhancements - Support autofix linter errors - Support anonymous updates via API - Support mediawiki export format in pandoc export - Add some help strings to Prometheus metrics - Allow more syntax highlight modes in editor - Support TOC level customization - Follow Google guidelines to use Google OAuth ## Fixes - Vimeo won't show up due to the jsonp callback data unable be parsed with jQuery - Fix slide mode stored XSS - Enforce PG ssl require mode on heroku - Webpack exclude path should support windows path - Free url can read any md in file system - Use encoded noteId when calling updateHistory ## Docs - Add matrix badge and links to README [#1629](https://github.com/hackmdio/codimd/pull/1629) [@a-andreyev](https://github.com/a-andreyev) 2.3.1 Isoetes taiwanensis 2021-01-04 --- ### Fixes * Upgrade mermaid to 8.6.4 to make the previous fix works 2.3.0 Isoetes taiwanensis 2020-12-30 ---
Isoetes taiwanensis
> Isoetes taiwanensis is a species of plant in the family Isoetaceae. It is endemic to Taiwan, and the only species of quillwort there. As other quillworts, it is relatively small, with erect leaves 7–24 cm (2.8–9.4 in) long. It grows submersed in shallow ponds for most of the year. IUCN considers it critically endangered because of habitat loss. > \- Wikipedia [Isoetes taiwanensis](https://en.wikipedia.org/wiki/Isoetes_taiwanensis) In this release, we focus on polishing existing features, fixing bugs and patching security issues. We continue to expand the APIs. CodiMD now supports ["Update note's content" and "delete note"](https://github.com/hackmdio/codimd/pull/1559) RESTful APIs. Thanks for the great works from [@JamesCamel](https://github.com/JamesCamel) :heart_eyes: We also fix several XSS security issues, including [mermaid](https://github.com/hackmdio/codimd/pull/1633), [vega](https://github.com/hackmdio/codimd/pull/1637) and [image lightbox](https://github.com/hackmdio/codimd/pull/1632). We appreciate the security report from [@msrkp](https://github.com/@msrkp), [@Alemmi](https://github.com/Alemmi), and [@nename0 ](https://github.com/nename0). This is the last release before the end of this year. Merry Christmas to everyone! Let's look forward and see you in the next year. :tada: [Check out the complete release note][v2_3_0]. Thank you CodiMD community and all our contributors. ❤️ [v2_3_0]: https://hackmd.io/@codimd/release-notes/%2F%40codimd%2Fv2_3_0 ### Enhancements - Fretboard improvements - Update and delete note api - Allow Sequelize CLI to use options set in config.json - Allow specifying option for graphviz - Spellcheck: add en_GB dictionary ### Fixes - Fix ui-edit and ui-both buttons in night mode - Don't run jsonlint on .vscode jsonc files - Fix image lightbox xss issue - Fix mermaid xss issue - Check upload image mime type - Vega syntax XSS dependencies 2.2.0 Diploderma swinhonis 2020-07-20 ---
Diploderma swinhonis
> Diploderma swinhonis, also known as the Taiwan japalure, Swinhoe's japalure, and Swinhoe's tree lizard, is a species of lizard in the family Agamidae. The species is endemic to Taiwan. > \- Wikipedia [Diploderma swinhonis](https://en.wikipedia.org/wiki/Diploderma_swinhonis) In this release, we've added some Markdown renderer plugins, including fretboard guitar, Mindmap, and CSV. We believe the simplicity and the extensibility of markdown can bring more possibilities to you and your workflow. So let's find out more about what we can do with markdown. :100: We also fixed a long-lasting issue: CodiMD cannot be hosted under URL subpath perfectly. Check PR [#1551](https://github.com/hackmdio/codimd/pull/1551) for details. Last but not least, we start standarizing CodiMD API. We drafted [`List my notes`](https://github.com/hackmdio/codimd/pull/1548) API in this release. Stay tuned. :person_in_lotus_position: Here are some highlights from this release: - [Fretboard Guitar tab renderer](#Fretboard-Guitar-tab-renderer) - [Mindmap rendrer](#Mindmap) - [Image Lightbox](#Image-Lightbox-Support) - [CSV renderer](#Render-csv-codeblock-as-table) [Check out the complete release note][v2_2_0]. Thank you CodiMD community and all our contributors. ❤️ [v2_2_0]: https://hackmd.io/@codimd/release-notes/%2F%40codimd%2Fv2_2_0 ### Enhancements - Use array for tags when available - Replace btn-social with btn-login-method - Set html image meta tag with YAML metadata - List my note API ### Fixes - Update Simplified Chinese translation and fix typography - Fix webpack urlpath font loading error 2.1.0 Zhangixalus prasinatus 2020-05-18 ---
Zhangixalus prasinatus
> Zhangixalus prasinatus is a species of frog in the family Rhacophoridae endemic to northern Taiwan. It is the largest tree frog in Taiwan; females can reach 7 cm (2.8 in) in snout-vent length. It is known from Taipei, Yilan, and Taoyuan. > \- Wikipedia [Zhangixalus prasinatus](https://en.wikipedia.org/wiki/Zhangixalus_prasinatus) During this hard time of COVID-19, it's a pleasure to help people collaborate better with CodiMD. We hope the world will recover from this situation soon. :sunrise: Good news, we have some goodies for CodiMD including: - [Support Prometheus metrics](https://hackmd.io/@codimd/v2_1_0#Support-Prometheus-metrics) - [Cut docker image size by 57%](https://hackmd.io/@codimd/v2_1_0#Cut-docker-image-size-by-57) - [Drop Node 8 Support](https://hackmd.io/@codimd/v2_1_0#Drop-Node-8-Support) [Check out the complete release note][v2_1_0]. Thank you CodiMD community and all our contributors. ❤️ [v2_1_0]: https://hackmd.io/@codimd/release-notes/%2F%40codimd%2Fv2_1_0 ### Enhancements - Optimize module size - Support brace wrapped param in fence lang - Upgrade Node.JS version to 10.20.1 ### Fixes - Fix getStatus caused "TypeError: Converting circular structure to JSON" 2.0.1 Urocissa caerulea 2020-04-09 --- [CodiMD 2.0.1](https://github.com/hackmdio/codimd/releases/tag/2.0.1) is a minor release fixing bugs introduced in 2.0.0 and earlier versions along with some enhancements. We encourage everyone to upgrade to 2.0.1 now. See how things are going on [GitHub](https://github.com/hackmdio/codimd/pulls?q=is%3Apr+is%3Aopen+sort%3Aupdated-desc+milestone%3ANext). Stay tuned and healthy, and we hope you love it! ### Enhancements - Allow inline markdown in spoiler summary syntax - Improve visibility of some UI elements - Support avatar for OAuth users ### Fixes - Fix to add missing configs in docker secret - Fix not able to upload image using imgur - Fix to improve version checker behavior - Fix Wikipedia link in 2.0.0 release notes - Fix require path for minio - Fix check for creating free url notes [Check out the complete release note][v2_0_1]. Thank you CodiMD community and all our contributors. ❤️ [v2_0_1]: https://hackmd.io/@codimd/release-notes/%2F%40codimd%2Fv2_0_1 2.0.0 Urocissa caerulea 2020-03-02 ---
Urocissa caerulea
> The Taiwan blue magpie (Urocissa caerulea), also called the Taiwan magpie, Formosan blue magpie , or the "long-tailed mountain lady", is a species of bird of the crow family. It is endemic to Taiwan. > > \- Wikipedia [Taiwan blue magpie](https://en.wikipedia.org/wiki/Taiwan_blue_magpie) In the past few months, we delivered not only a bunch of awesome features but also some critical bug fixes. Moreover, we refactored CodiMD's backend and started to write new tests. To make it easier to maintain, we dropped legacy code, reorganize the repository, and add new documentation. For these reasons, we decided to make a huge leap toward version 2.0.0. Here are the highlights coming from version 2.0.0: - [Multilanguage spellchecker][multilanguage-spellchecker] - [Customize editor color schemes][customize-editor-color-schemes] - [Export note with Pandoc][support-pandoc-export] - [Embed Geolocation data][embedding-geolocation-data] - [Version checking][auto-version-check-for-site-admin] - [Meet the new CodiMD CLI][meet-the-new-codimd-cli] - [Refreshed Documentation structure and new Markdown guides](refreshed-documentation-structure-and-new-markdown-guides) It's our pleasure to announce CodiMD `2.0.0`. Enjoy as always :heartpulse:. [Check out the complete release note][v2_0_0]. Thank you CodiMD community and all our contributors. ❤️ [multilanguage-spellchecker]: https://hackmd.io/@codimd/v2_0_0#Multilanguage-spellchecker [customize-editor-color-schemes]: https://hackmd.io/@codimd/v2_0_0#Customize-editor-color-schemes [support-pandoc-export]: https://hackmd.io/@codimd/v2_0_0#Support-Pandoc-export [embedding-geolocation-data]: https://hackmd.io/@codimd/v2_0_0#Embedding-Geolocation-data [auto-version-check-for-site-admin]: https://hackmd.io/@codimd/v2_0_0#Auto-version-check-for-site-admin [meet-the-new-codimd-cli]: https://hackmd.io/@codimd/v2_0_0#Meet-the-new-CodiMD-CLI [refreshed-documentation-structure-and-new-markdown-guides]: https://hackmd.io/@codimd/v2_0_0#Refreshed-Documentation-structure-and-new-Markdown-guides [v2_0_0]: https://hackmd.io/@codimd/release-notes/%2F%40codimd%2Fv2_0_0 1.4.1 2019-12-13 --- [CodiMD 1.4.1](https://github.com/hackmdio/codimd/releases/tag/1.4.1) is a minor release including bug fixes introduced in 1.4.0 and earlier versions. We encourage everyone to upgrade to 1.4.1 now. Also, we're preparing for the next major release, and you can see how things are going on [GitHub](https://github.com/hackmdio/codimd/pulls?q=is%3Apr+is%3Aopen+sort%3Aupdated-desc+milestone%3ANext). Stay tuned, and we hope you love it! ### Fixes - Fix urlpath in webpack build - Fix mysql not bundled in package.json - Fix minio image uploading - Fix pcheck not parsed url correctly - Fix manage_users script ### Enhancement - Added Scalingo compatible deployment [Check out the complete release note][v1_4_1]. Thank you CodiMD community and all our contributors. ❤️ [v1_4_1]: https://hackmd.io/@codimd/release-notes/%2F%40codimd%2Fv1_4_1 1.4.0 Syrmaticus mikado 2019-11-11 ---
Mikado Pheasant, photo credits to Snowyowls from wikipedia
Starting from version 1.4.0, we'll pick one species from [_the endemic species of Taiwan_](https://en.wikipedia.org/wiki/List_of_endemic_species_of_Taiwan) as version name. Is there anyone still remember we've once used type of coffee as our version name? It's time to revive that good convention, but this time we don't need coffee to stay up all night. 💤 It has been over 200+ commits since our last release. These are the highlights from version 1.4.0: - [New table tools][table-tools] - Create table with auto-formatting and keyboard shortcut - [Markdownlint integration][markdownlint] - Lint you markdown document - [Support PlantUML, vega-lite renderer][more-renderers] - More renderers to come - [Support spoiler container, ruby markdown syntax][more-syntax] - [New Emoji sets][new-emoji] - [Slide mode plugins][slide-mode-plugins]: Elapsed time bar and Spotlight [Check out the complete release note][v1_4_0]. Thank you CodiMD community and all our contributors. ❤️ [table-tools]: https://hackmd.io/@codimd/v1_4_0#New-Table-Tools [markdownlint]: https://hackmd.io/@codimd/v1_4_0#Markdownlint-integration [more-renderers]: https://hackmd.io/@codimd/v1_4_0#Support-2-new-render-engines-PlantUML-and-Vega-lite [more-syntax]: https://hackmd.io/@codimd/v1_4_0#Suppport-2-New-markdown-syntax-Spoiler-and-Ruby [new-emoji]: https://hackmd.io/@codimd/v1_4_0#New-emoji-sets [slide-mode-plugins]: https://hackmd.io/@codimd/v1_4_0#Slide-mode-enhancement [v1_4_0]: https://hackmd.io/@codimd/release-notes/%2F%40codimd%2Fv1_4_0 1.3.1 2019-03-23 00:00 --- ### Enhancements * Add some missing translations * Add Serbian language ### Fixes * Fix broken redirect for empty `serverURL` * Fix wrong variable type for HSTS maxAge * Fix GitLab snippets showing up without being configured * Fix Google's API after disabling Google+ * Fix broken PDF export ### Contributors * atachibana (translator) * [Aurélien JANVIER](https://github.com/ajanvier) (translator) * [Daan Sprenkels](https://github.com/dsprenkels) (translator) * Farizrizaldy (translator) * [Luclu7](https://github.com/Luclu7) * Sylke Vicious (translator) * [toshi0123](https://github.com/toshi0123) & okochi-toshiki * [Turakar](https://github.com/Turakar) * [Vladan](https://github.com/cvladan) (translator) 1.3.0 2019-03-03 00:00 --- ### Enhancements * Run db migrations on `npm start` * Add documentation about integration with AD LDAP * Add `rel="noopener"` to all links * Add documentation about integration with Nextcloud for authentication * Update URL on frontpage to point to codimd.org * Replace Fontawesome with Forkawesome * Add OpenID support * Add print icon to slide view * Add auto-complete for language names that are highlighted in codeblocks * Improve translations for Chinese, Dutch, French, German, Italien, Korean, Polish, and Russian language * Add Download action to published document API * Add reset password feature to `manage_users` script * Move from own `./tmp` directory to system temp directory * Add Etherpad migration guide * Move XSS library to a more native position * Use full version string to determine changes from the backend * Update winston (logging library) * Use slide preview in slide example * Improve migration handling * Update reveal.js to version 3.7.0 * Replace scrypt library with its successor * Replace `to-markdown` with `turndown` (successor library) * Update socket.io * Add warning on missing base URL * Update bootstrap to version 3.4.0 * Update handlebar ### Fixes * Fix paths in GitLab documentation * Fix missing `data:` URL in CSP * Fix oAuth2 name/label field * Fix GitLab API integration * Fix auto-completed but not rendered emojis * Fix menu organization depending on enabled services * Fix some logging in the OT module * Fix some unhandled internalOAuthError exception * Fix unwanted creation of robots.txt document in "freeurl-mode" * Fix some links on index page to lead to the right sections on feature page * Fix document breaking, empty headlines * Fix wrong multiplication for HSTS header seconds * Fix wrong subdirectories in exported user data * Fix CSP for speaker notes * Fix CSP for disqus * Fix URL API usage * Fix Gist embedding * Fix upload provider error message * Fix unescaped disqus user names * Fix SAML vulnerability * Fix link to SAML guide * Fix deep dependency problem with node 6.x * Fix broken PDF export by wrong unlink call * Fix possible XSS attack in MathJax ### Refactors * Refactor to use `ws` instead of the the no longer supported `uws` * Refactor frontend build system to use webpack version 4 * Refactor file path configuration (views, uploads, …) * Refactor `manage_users` script * Refactor handling of template variables * Refactor linting to use eslint ### Removes * Remove no longer working Octicons * Remove links to our old Gitter channel * Remove unused library node-uuid * Remove unneeded blueimp-md5 dependency * Remove speakerdeck due to broken implementation ### Contributors * Adam.emts (translator) * [Alex Garcia](https://github.com/asg017) * [Cédric Couralet (micedre)](https://github.com/micedre) * [Claudius Coenen](https://github.com/ccoenen) * [Daan Sprenkels](https://github.com/dsprenkels) * [David Mehren](https://github.com/davidmehren) * [Erona](https://github.com/Eronana) * [Felix Yan](https://github.com/felixonmars) * [Jonathan](https://github.com/phrix32) * Jong-kai Yang (translator) * [MartB](https://github.com/MartB) * [Max Wu (jackycute)](https://github.com/jackycute) * [mcnesium](https://github.com/mcnesium) * Nullnine (translator) * RanoIP (translator) * [SuNbiT](https://github.com/sunbit) * Sylke Vicious (translator) * Timothee (translator) * [WilliButz](https://github.com/WilliButz) * [Xaver Maierhofer](https://github.com/xf-) * [云屿](https://github.com/cloudyu) 1.2.1 2018-09-26 00:00 --- ### Enhancements * Update Italian translations * Update Japanese translations * Update markdown-pdf * Add support for unix sockets * Update "follow us" information to Community channel and translation * Add Cloudron installation method * Add guide for Mattermost authentication * Update various packages * Add Indonesian language as new translation ### Fixes * Fix content types in status router * Fix some modal colors in night mode * Fix CSP to allow usage of speaker notes * Fix some wrong title attributes in the editor toolbar * Fix some confusion about the default location of images. It's always the local filesystem now * Fix object handling in avatar generation code * Finally fix error handling of LZ-String by using self-maintained version * Fix migration handling * Fix gitlab API version * Fix some server crashes caused by PDF creation * Fix document length limit on post to `/new` * Fix broken youtube embedding on `/features` page ### Refactors * Refactor generation of table of contents * Refactor "copyright"-section to be a "Powered by" ### Removes * Remove unneeded inline styling ### Deprecations * NodeJS version 6 * Mattermost login integration (is replaced by [generic oAuth2 module](https://github.com/hackmdio/codimd/blob/6ce7b20a7f92ccff2f7f870ff5d116d685310cfd/docs/guides/auth/mattermost-self-hosted.md)) ### Honorable mentions * [Alex Hesse (Pingu501)](https://github.com/Pingu501) * [Alexander Wellbrock (w4tsn)](https://github.com/w4tsn) * [Cédric Couralet (micedre)](https://github.com/micedre) * [Girish Ramakrishnan (gramakri)](https://github.com/gramakri) * [maahl](https://github.com/maahl) * [Max Wu (jackycute)](https://github.com/jackycute) * [Miranda (ahihi)](https://github.com/ahihi) * [Ondřej Slabý (maxer456)](https://github.com/maxer456) 1.2.0 2018-06-28 00:00 --- ### Announcement * HackMD CE is renamed to CodiMD to prevent confusion. [For details see here](https://github.com/hackmdio/codimd#hackmd-ce-became-codimd) ### Enhancements * Show full title by hovering over to table of contents entries * Add generic OAUTH2 support for authentication * Redirect unauthenticated user to login page on "forbidden" pages * Add ability to add ToS and privacy documents without code changes * Add account deletion as part of user self-management * Add download of all own notes * Add privacy policy example (no legal advice) * Increase checkbox size on slides * Add support for Azure blob storage for image uploads * Add Korean translation * Add note about official K8s chart for deployment * Add toolbar for markdown shortcuts in editor * Add ability to disable Gravatar integration * Add print icon to slide menu which leads to the print view. * Add sequelize to setup instructions * Update various packages ### Fixes * Fix local writes for non-existing translations in production * Fix wrong documentation about default image upload type * Fix possible error if CodiMD is started with wrong working directory * Fix issues caused by cached/cacheeable client config * Fix issues caused by notes created via curl/API with CRLF line endings * Fix broken images for downloaded PDFs while using `filesystem` as `imageUploadType` * Fix Unicode URLs when using `allowFreeURL=true` ### Refactors * Split auth documentation into multiple documents ### Removes * Remove polyfill for `useCDN=false` setups * Remove unused and no longer needed symlink from translations ### Honorable mentions * [Adam Hoka (ahoka)](https://github.com/ahoka) * [Edgar Z. Alvarenga (aivuk)](https://github.com/aivuk) * [Jacob Burden (jekrb)](https://github.com/jekrb) * [Pedro Ferreira (pferreir)](https://github.com/pferreir) * [TC Liu (liuderchi)](https://github.com/liuderchi) 1.1.1-ce 2018-05-23 12:00 --- ### Security * Fix Google Drive integration leaked `clientSecret` for Google integration * Update base64url package ### Fixes * Fix typos in integrations * Fix high need of file descriptors during build * Fix heroku deployment by limiting node version to <10.x ### Refactors * Refactor letterAvatars to be compliant with CSP ### Removes * Google Drive integration ### Honorable mentions * [Max Wu (jackycute)](https://github.com/jackycute) 1.1.0-ce 2018-04-06 12:00 --- ### Security * Adding CSP headers * Prevent data-leak by wrong LDAP config * Generate dynamic `sessionSecret` if none is specified ### Enhancements * Add Minio support * Allow posting content to new notes by API * Add anonymous edit function in restricted mode * Add support for more Mimetypes on S3, Minio and local filesystem uploads * Add basic CLI tooling for local user management * Add referrer policy * Add more usable HTML5 tags * Add `useridField` in LDAP config * Add option for ReportURI for CSP violations * Add persistance for night mode * Allow setting of `sessionSecret` by environment variable * Add night mode to features page * Add Riot / Matrix - Community link to help page ### Fixes * Fix ToDo-toggle function * Fix LDAP provider name in front-end * Fix errors on authenticated sessions for deleted users * Fix typo in database migration * Fix possible data truncation of authorship * Minor fixes in README.md * Allow usage of ESC-key by codemirror * Fix array of emails in LDAP * Fix type errors by environment configs * Fix error message on some file API errors * Fix minor CSS issues in night mode ### Refactors * Refactor contact * Refactor social media integration on main page * Refactor socket.io code to no longer use referrer * Refactor webpack config to need less dependencies in package.json * Refactor imageRouter for modularity * Refactor configs to be camel case ### Removes * Remove unused `tokenSecret` from LDAP config ### Deprecations * All non-camelcase config ### Honorable mentions * [Dario Ernst (Nebukadneza)](https://github.com/Nebukadneza) * [David Mehren (davidmehren)](https://github.com/davidmehren) * [Dustin Frisch (fooker)](https://github.com/fooker) * [Felix Schäfer (thegcat)](https://github.com/thegcat) * [Literallie (xxyy)](https://github.com/xxyy) * [Marc Deop (marcdeop)](https://github.com/marcdeop) * [Max Wu (jackycute)](https://github.com/jackycute) * [Robin Naundorf (senk)](https://github.com/senk) * [Stefan Bühler (stbuehler)](https://github.com/stbuehler) * [Takeaki Matsumoto (takmatsu)](https://github.com/takmatsu) * [Tang TsungYi (vazontang)](https://github.com/vazontang) * [Zearin (Zearin)](https://github.com/Zearin) 1.0.1-ce 2018-01-19 15:00 --- ### Security * Fix Dropbox client secret leak ### Enhancements * Improve version handling * It's 2018! ### Fixes * Fix image alt-tag rendering * Fix Dropbox appkey 1.0.0-ce 2018-01-18 12:00 --- ### License * Switch from MIT to AGPL ### Enhancements * Improve language support * Allow themes for reveal * Add dark theme for editor and view * Add danish translation * Add simplified chinese translation * Provide new permission table * Make HSTS configurable * Make PDF export configurable * Add Mattermost auth support * Add SAML support ### Fixes * Fix regex for speaker notes * Fix S3 endpoint support * Fix German translation * Fix English translation * Fix broken profile images * Fix XSS attacks * Fix history order * Fix missing boolean settings * Fix LDAP auth * Fix too long notes droping content * Fix mermaid compatiblity with new version * Fix SSL CA path parsing ### Refactors * Refactor main page * Refactor status pages * Refactor config handling * Refactor auth backend * Refactor code styling * Refactor middleware to modules 0.5.1 `Doppio` 2017-03-23 00:20 --- ### Enhancements * Update to indicate version in status API header * Update to generate front-end constants on server startup * Update to add gitlab api scope option and auto adapt gitlab snippet feature on it * Update to add default permission config option * Update to add basics for secret management by Docker 1.13 * Update webpack config to use parallel uglify plugin to speed up production build * Update realtime to use timer to avoid memory leaks on busy tick * Update to remove history cache to lower application coupling * Update to add screenshot on index page * Update index layout to add profile on navbar * Update to support allow email register option * Update to support disable anonymous view option * Update to add limited and protected permission * Update to allow displaying LDAP provider name on sign-in modal * Update to show yaml-metadata and diagram parsing error in the view ### Fixes * Fix XSS vulnerability in link regex [Security Issue] * Fix todo list item class might add in wrong element * Fix pagination error in list.js over v1.5.0 * Fix update doc from filesystem cause redundant authorship stringify * Fix export html to replace fallen cdn tortue.me to cdnjs * Fix rendering might result XSS attribute on self closing tag [Security Issue] * Fix out of sync when deleting on same cursor position on several clients * Fix not determine OT have pending operations properly * Fix to keep selections on save and restore info * Fix image path problem when using filesystem backend * Fix meta error not clear on before rendering * Fix duplicated headers anchor link not been updated properly * Fix checkLoginStateChanged might fall into infinite loop while calling loginStateChangeEvent * Fix to workaround text shadow for font antialias might cause cut off in Edge * Fix and refactor extracting content using metaMarked directly might lead in invalid object ### Refactors * Refactor editor related code * Refactor code with JavaScript Standard Style * Refactor templates, partials and rearrange its path * Refactor front-end code with more modular concepts * Refactor front-end code using ES6 (also unify configs to `config.json`) ### Removes - Removed UTF-8 BOM in download function 0.5.0 `Ristretto` 2017-01-02 02:35 --- ### Enhancements * Update year to 2017 (Happy New Year!) * Update to improve editor performance by debounce checkEditorScrollbar event * Refactor data processing to model definition * Update to remove null byte on editor changes * Update to remove null byte before saving to DB * Update to support Esperanto locale * Little improvements (typos, uppercase + accents, better case) for French locale * Update features.md publish button name and icon ### Fixes * Fix authorship might losing update event because of throttling * Fix migration script of revision lacks of definition of primary key * Fix to not use diff_cleanupSemantic * Fix URL concatenation when uploading images to local filesystem * Fix js-url not import correctly * Fixed typo: anonmyous * Fix codemirror spell checker not considering abbreviation which contain apostrophe in word * Fix possible user is undefined in realtime events * Fix wrong package name reference in webpack config for bootstrap-validator * Fix email option in config not parse correctly * Fix mathjax not able to render issue ### Removes - Remove LZString compression for data storage - Remove LZString compression for some socket.io event data 0.4.6 `Melya` 2016-12-19 17:20 --- ### Features + Add support of allow free url config option + Add support of allow anonymous config option + Add preferences to editor status bar and add allow override browser keymap option + Add support of s3 and local filesystem for image uploading + Add of support optional email register and signin + Use uWebSocket to improve websocket performance + Use CDNJS by default with https and SRI support + Use Webpack to bundle frontend code ### Enhancements * Update to make TOC syntax be case-insensitive * Update to handle request with invalid uri * Update to auto generate meta description based on content in publish note and slide * Update to support haskell, go, typescript and jsx syntax highlighting in code block * Update to use workers to leverage intensive work loading * Update to support summary tag * Change use cdn config option default to be true * Update to retry when anytime the socket io disconnect * Change to raise socket io timeout, heartbeat interval and timeout to lower offline period * Update emoji parser using markdown-it-emoji instead of emojify * Optimize finishView selector performance by avoid universal selector * Config heroku deployment * Update to support Hindi, Swedish locale * Update to support wrap syntax for code block * Update to support pagination for history list ### Fixes * Fix slide mode on print pdf not finish view rendering * Fix when server have heavy loading cache might not update to db properly * Fix redirection to url without trailing slashes not considering about config urlpath * Fix header id and text might affects by mathjax tags * Fix possible meta XSS in history list [Security Issue] * Fix possible XSS in yaml-metadata and turn using ejs escape syntax than external lib [Security Issue] * Fix to allow data attribute of section tag in slide * Fix slide might able to add unsafe attribute on section tag which cause XSS [Security Issue] * Fix slide might trigger script when processing markdown which cause XSS [Security Issue] * Fix published note won't scroll to hash on load * Fix mathjax with blockquote might have race condition * Fix server reconnect might not resend pending operations * Fix slide export pdf styles not applied issue * Fix possible unclose HTML and leaked html tags when fail to parse diagrams * Fix typos in the `slide-example.md` * Fix socket io doc event should setDoc when revision mismatch and no outstanding operation * Fix markdown styles conflicting bootstrap on p and ul under alert area * Fix finishView mermaid might select and replace whole markdown-body issue * Fix code block which in deeper level will not be parsed issue * Fix code block highlighting html not escaped when no languages specified * Fix client socket on delete event might not delete corresponding history record correctly * Fix to handle name or color is undefined error * Fix history item event not bind properly on pagination change * Fix history time should save in UNIX timestamp to avoid time offset issue ### Removes - Drop bower the package manager - Remove auto linkify image 0.4.5 `latte` 2016-10-11 01:22 --- ### Features + Add more environment variables for server configuration + Add setup script for getting started + Add support of deleting note + Add support of shortcut keys which can add and remove symbol surround text + Add support of shortcut keys for changing mode + Add support of i18n (English, Chinese, French, German, Japanese, Spanish, Portuguese, Greek, Italian, Turkish, Russian, Dutch, Croatian, Polish, Ukrainian) + Add support of note info API + Add support of disqus via yaml-metadata ### Enhancements * Optimize png images by using zopflipng * Update CodeMirror to 5.19.0 and rename jade to pug * Update to add cache to history and improve its performance * Update default indent to use spaces instead of tabs * Improve syntax highlighting performance * Update to make client handle syncing error better, use delay to avoid wrong document revision * Update to allow CORS as API on revision actions * Update to support showing owner on the infobar * Update to prevent duplicate client push in queue to lower down server loading * Reduce update view debounce time to make preview refresh quicker * Update help modal cheatsheet font styles to make it more clear on spaces * Update to add revision saving policy * Update to support tiddlywiki and mediawiki syntax highlighting in editor * Update to support save mode to url and vise versa * Update edit and publish icon and change toggle icon for UX * Improve authorship markers update performance * Update slide mode to show extra info and support url actions * Change the last change user saving strategy * Update to support data uri in src attribute of image tag * Improve index layout and UX with UI adjustments * Update XSS policy to allow iframe and link with custom protocol * Update markdown styles to follow github latest layout styles * Update slide mode, now respect all meta settings and update default styles * Update to make ToC menu always accessible without scrolling * Update to make doc only update while filesystem content not match db content ### Fixes * Fix README and features document format and grammar issues * Fix some potential memory leaks bugs * Fix history storage might not fallback correctly * Fix to make mathjax expression display in editor correctly (not italic) * Fix note title might have unstriped html tags * Fix client reconnect should resend last operation * Fix a bug when setting both maxAge and expires may cause user can't signin * Fix text complete extra tags for blockquote and referrals * Fix bug that when window close will make ajax fail and cause cookies set to wrong state * Fix markdown render might fall into regex infinite loop * Fix syntax error caused by element contain special characters * Fix reference error caused by some scripts loading order * Fix ToC id naming to avoid possible overlap with user ToC * Fix header nav bar rwd detect element should use div tag or it might glitch the layout * Fix textcomplete of extra tags for blockquote not match space character in the between * Fix text-shadow for text antialiased might cause IE or Edge text cutoff ### Removes - Cancel updating history on page unload 0.4.4 `mocha` 2016-08-02 17:10 --- ### Features * Add support of showing authorship in editor * Add support of saving authorship * Add support of saving authors * Add support of slide preview in both mode * Add support of all extra syntax in slide mode ### Enhancements * Update realtime check and refresh event, compress data to minimize network transfer delay * Update to keep showing second level TOC if there is only one first level TOC * Update to add expand and collapse toggle for TOC * Update to make help modal and text complete hint using consistent reminder text * Update to support slideOptions in the yaml metadata for customize slides * Update to support redirect back to previous url after signin * Update to avoid duplicated rendering slides and reduce DOM wrap * Update CodeMirror to version 5.17.1 * Update to make random color more discrete * Update user icon styles to make avatar more obvious * Update Bootstrap to 3.3.7 and jQuery to 3.1.0 with related patches * Update spell checker to ignore non-english or numeric alphabets * Update to auto rolling session for auto extending cookies expiration * Update some menu items and UIs * Update to reduce realtime timeout and heartbeat interval to handle stale clients quicker * Update to force note, publish note, publish slide redirect to their expected url * Update to change server pre-rendering engine to markdown-it ### Fixes * Workaround vim mode might overwrite copy keyMap on Windows * Fix TOC might not update after changeMode * Workaround slide mode gets glitch and blurry text on Firefox 47+ * Fix idle.js not change isAway property on onAway and onAwayBack events * Fix http body request entity too large issue * Fix google-diff-match-patch encodeURI exception issue * Fix yaml metadata title should pass to generateWebTitle * Fix spellcheck settings from cookies might not a boolean in string type * Fix cookies might not in boolean type cause page refresh loop * Fix the signin and logout redirect url might be empty * Fix realtime might not clear or remove invalid sockets in queue * Fix slide not refresh layout on ajax item loaded * Fix retryOnDisconnect not clean up after reconnected * Fix some potential memory leaks 0.4.3 `espresso` 2016-06-28 02:04 --- ### Features * Add support of spellcheck * Add support of light editor theme * Add support of embed pdf * Add support of exporting raw html * Add revision modal with UIs and support marking patch diff texts * Add support of saving note revision ### Enhancements * Update to extend login info cookies to 365 days to reduce reductant page refresh * Update to support new metadata: title, description, tags and google-analytics * Prevent crawling editing note to enhance privacy * Update to remove all data lines attributes to gain better update performance * Update refresh modal to show more detail informations * Update to make cursor tag default as hover mode to prevent tag overlay other lines * Update highlight.js to version 9.4.0 and use bower dependency * Improve history performance ### Fixes * Fix history filter tags and search keyword might not apply after refresh * Fix part class in list item might infect buildMap process * Fix pdf tmp path is missing a folder slash before timestamp * Fix realtime connection get stock when lots of client try to connect at same moment * Fix locked or private permission should block any operation if owner is null * Add back missing support of image size syntax in 0.4.2 * Fix update permission might cause duplicate view rendering * Fix on paste long document to editor might cause scroll not syncing * Workaround CodeMirror won't draw selections outside of the viewport * Fix to make socket keep retry after disconnect on server maintenance ### Removes - Remove metadata spellcheck support - Remove robot meta on note edit page and html template 0.4.2 `cappuccino` 2016-04-22 10:43 --- ### Features + Support sync scrolling to edit area + Support import and export with GitLab snippet + Support GitLab signin + Add cheatsheet and help modal ### Enhancements * Upgrade CodeMirror to version 5.15.3 * Support maintenance mode and gracefully exit process on signal * Update to update doc in db when doc in filesystem have newer modified time * Update to replace animation acceleration library from gsap to velocity * Support image syntax with size * Update textcomplete rules to support more conditions * Update to use bigger user profile image * Support showing signin button only when needed ### Fixes * Fix other clients' cursor might disappear or move out of bound * Fix to handle user profile image not exists * Fix potential toolbar layout glitch * Fix imgur uploads should always use https to avoid mix-content warning * Fix to change fullscreen key to avoid OS key conflicts * Fix and change ESC key in Vim mode 0.4.1 2016-04-22 10:43 --- ### Enhancements * Support when client domain not provided will use window.location variable * Support when domain not provided will use relative path * Support DOMAIN and URL_PATH environment variables 0.4.0 `first-year` 2016-04-20 14:30 --- ### Features + Support docs + Support Ionicons and Octicons + Support mermaid diagram + Support import and export with Gist + Support import and export with Google Drive + Support more options in YAML metadata + Support change keymap and indentation size/type ### Enhancements * Change header anchor styles * Refactor server code and configs * Support experimental spell checking * Upgrade CodeMirror to 5.13.5 * Update to emit info and disconnect clients if updater get errors * Support to indicate if the note status is created or updated * Support more DB types * Server now use ORM for DBs * Support static file cache * Support more ssl settings * Improve server stablilty * Improve server performance * Support Ionicons * Support container syntax and styles * Improve input performance * Change markdown engine from remarkable to markdown-it * Server now support set sub url path * Support textcomplete in multiple editing * Update to filter XSS on rendering * Update to make sync scroll lerp on last line * Update to make continue list in todo list default as unchecked * Support auto indent whole line in list or blockquote ### Fixes * Fix status bar might be inserted before loaded * Fix mobile layout and focus issues * Fix editor layout and styles might not handle correctly * Fix all diagram rendering method and styles to avoid partial update gets wrong * Fix to ignore process image which already wrapped by link node * Fix when cut or patse scroll map might get wrong * Fix to handle more socket error and info status * Fix textcomplete not matching properly * Fix and refactor cursor tag and cursor menu * Fix Japanese, Chinese font styles * Fix minor bugs of UI and seletor syntaxes 0.3.4 `techstars` 2016-01-19 00:22 --- ### Features + Beta Support slide mode + Beta Support export to PDF + Support TOC syntax + Support embed slideshare and speakerdeck + Support Graphviz charts + Support YAML metadata + Support private permission ### Enhancements * Support pin note in history * Support IE9 and above * Support specify and continue line number in code block * Changed all embed layout to 100% width * Added auto detect default mode * Support show last change note user * Upgrade CodeMirror to 5.10.1 with some manual patches * Improved server performance * Support autocomplete for code block languages of charts ### Fixes * Fixed some server connection issues * Fixed several issues cause scrollMap incorrect * Fixed cursor animation should not apply on scroll * Fixed a possible bug in partial update * Fixed internal href should not link out * Fixed dropbox saver url not correct * Fixed mathjax might not parse properly * Fixed sequence diagram might render multiple times 0.3.3 `moon-festival` 2015-09-27 14:00 --- ### Features + Added status bar below editor + Added resizable grid in both mode + Added title reminder if have unread changes + Support todo list change in the view mode + Support export to HTML + Changed to a new theme, One Dark(modified version) ### Enhancements * Support extra tags in todo list * Changed overall font styles * Optimized build sync scroll map, gain lots better performance * Support and improved print styles * Support to use CDN * Image and link will href to new tab ors window * Support auto scroll to corresponding position when change mode from view to edit * Minor UI/UX tweaks ### Fixes * Change DB schema to support long title * Change editable permission icon to avoid misunderstanding * Fixed some issues in OT and reconnection * Fixed cursor menu and cursor tag are not calculate doc height properly * Fixed scroll top might not animate * Fixed scroll top not save and restore properly * Fixed history might not delete or clear properly * Fixed server might not clean client properly 0.3.2 `typhoon` 2015-07-11 12:30 --- ### Features + Support operational transformation + Support show other user selections + Support show user profile image if available ### Enhancements * Updated editor to 5.4.0 * Change UI share to publish to avoid misleading * Added random color in blockquote tag * Optimized image renderer, avoid duplicated rendering * Optimized building syncscroll map, make it faster * Optimized SEO on publish and edit note 0.3.1 `clearsky` 2015-06-30 16:00 --- ### Features + Added auto table of content + Added basic permission control + Added view count in share note ### Enhancements * Toolbar now will hide in single view * History time now will auto update * Smooth scroll on anchor changed * Updated video style ### Fixes * Note might not clear when all users disconnect * Blockquote tag not parsed properly * History style not correct 0.3.0 `sunrise` 2015-06-15 24:00 --- ### Enhancements * Used short url in share notes * Added upload image button on toolbar * Share notes are now SEO and mobile friendly * Updated code block style * Newline now will cause line breaks * Image now will link out * Used otk to avoid race condition * Used hash to avoid data inconsistency * Optimized server realtime script ### Fixes * Composition input might lost or duplicated when other input involved * Note title might not save properly * Todo list not render properly 0.2.9 `wildfire` 2015-05-30 14:00 --- ### Features + Support text auto complete + Support cursor tag and random last name + Support online user list + Support show user info in blockquote ### Enhancements * Added more code highlighting support * Added more continue list support * Adjust menu and history filter UI for better UX * Adjust sync scoll animte to gain performance * Change compression method of dynamic data * Optimized render script ### Fixes * Access history fallback might get wrong * Sync scroll not accurate * Sync scroll reach bottom range too much * Detect login state change not accurate * Detect editor focus not accurate * Server not handle some editor events 0.2.8 `flame` 2015-05-15 12:00 --- ### Features + Support drag-n-drop(exclude firefox) and paste image inline + Support tags filter in history + Support sublime-like shortcut keys ### Enhancements * Adjust index description * Adjust toolbar ui and view font * Remove scroll sync delay and gain accuracy ### Fixes * Partial update in the front and the end might not render properly * Server not handle some editor events 0.2.7 `fuel` 2015-05-03 12:00 --- ### Features + Support facebook, twitter, github, dropbox login + Support own history ### Enhancements * Adjust history ui * Upgrade realtime package * Upgrade editor package, now support composition input better ### Fixes * Partial update might not render properly * Cursor focus might not at correct position 0.2.6 `zippo` 2015-04-24 16:00 --- ### Features + Support sync scroll + Support partial update ### Enhancements * Added feedback ui * Adjust animations and delays * Adjust editor viewportMargin for performance * Adjust emit refresh event occasion * Added editor fallback fonts * Index page auto focus at history if valid ### Fixes * Server might not disconnect client properly * Resume connection might restore wrong info 0.2.5 `lightning` 2015-04-14 21:10 --- ### Features + Support import from dropbox and clipboard + Support more code highlighting + Support mathjax, sequence diagram and flow chart ### Enhancements * Adjust toolbar and layout style * Adjust mobile layout style * Adjust history layout style * Server using heartbeat to gain accuracy of online users ### Fixes * Virtual keyboard might broken the navbar * Adjust editor viewportMargin for preloading content 0.2.4 `flint` 2015-04-10 12:40 --- ### Features + Support save to dropbox + Show other users' cursor with light color ### Enhancements * Adjust toolbar layout style for future ### Fixes * Title might not render properly * Code border style might not show properly * Server might not connect concurrent client properly 0.2.3 `light` 2015-04-06 20:30 --- ### Features + Support youtube, vimeo + Support gist + Added quick link in pretty + Added font-smoothing style ### Enhancements * Change the rendering engine to remarkable * Adjust view, todo list layout style for UX + Added responsive layout check + Auto reload if client version mismatch + Keep history stack after reconnect if nothing changed + Added features page ### Fixes * Closetags auto input might not have proper origin * Autofocus on editor only if it's on desktop * Prevent using real script and iframe tags * Sorting in history by time not percise 0.2.2 `fire` 2015-03-27 21:10 --- ### Features + Support smartLists, smartypants + Support line number on code block + Support tags and search or sort history ### Enhancements + Added delay on socket change + Updated markdown-body width to match github style + Socket changes now won't add to editor's history + Reduce redundant server events ### Fixes * Toolbar links might get wrong * Wrong action redirections 0.2.1 `spark` 2015-03-17 13:40 --- ### Features + Support github-like todo-list + Support emoji ### Enhancements + Added more effects on transition + Reduced rendering delay + Auto close and match brackets + Auto close and match tags + Added code fold and fold gutters + Added continue listing of markdown 0.2.0 `launch-day` 2015-03-14 20:20 --- ### Features + Markdown editor + Preview html + Realtime collaborate + Cross-platformed + Recently used history ================================================ FILE: public/docs/slide-example.md ================================================ --- type: slide slideOptions: transition: slide --- # Slide example This feature still in beta, may have some issues. For details please visit: https://github.com/hakimel/reveal.js/ You can use `URL query` or `slideOptions` of the YAML metadata to customize your slides. --- ## First slide `---` Is the divider of slides ---- ### First branch of first the slide `----` Is the divider of branches Use the _Space_ key to navigate through all slides. ---- ### Second branch of first the slide Nested slides are useful for adding additional detail underneath a high-level horizontal slide. --- ## Point of View Press **ESC** to enter the slide overview. --- ## Touch Optimized Presentations look great on touch devices, like mobile phones and tablets. Simply swipe through your slides. --- ## Fragments `` Is the fragment syntax Hit the next arrow... ... to step through ... ... a fragmented slide. Note: This slide has fragments which are also stepped through in the notes window. --- ## Fragment Styles There are different types of fragments, like: grow shrink fade-out fade-up (also down, left and right!) current-visible Highlight red blue green --- ## Transition Styles Different background transitions are available via the transition option. This one's called "zoom". `` Is the transition syntax You can use: none/fade/slide/convex/concave/zoom --- `` Also, you can set different in/out transition You can use: none/fade/slide/convex/concave/zoom postfix with `-in` or `-out` --- `` Custom the transition speed! You can use: default/fast/slow --- ## Themes reveal.js comes with a few themes built in: Black (default) - White - League - Sky - Beige - Simple Serif - Blood - Night - Moon - Solarized It can be set in YAML slideOptions --- `` Is the background syntax ---
## Image Backgrounds ``
----
## Tiled Backgrounds ``
----
## Video Backgrounds ``
---- ## ... and GIFs! --- ## Pretty Code ``` javascript function linkify( selector ) { if( supports3DTransforms ) { const nodes = document.querySelectorAll( selector ); for( const i = 0, len = nodes.length; i < len; i++ ) { var node = nodes[i]; if( !node.className ) { node.className += ' roll'; } } } } ``` Code syntax highlighting courtesy of [highlight.js](http://softwaremaniacs.org/soft/highlight/en/description/). --- ## Marvelous List - No order here - Or here - Or here - Or here --- ## Fantastic Ordered List 1. One is smaller than... 2. Two is smaller than... 3. Three! --- ## Tabular Tables | Item | Value | Quantity | | ---- | ----- | -------- | | Apples | $1 | 7 | | Lemonade | $2 | 18 | | Bread | $3 | 2 | --- ## Clever Quotes > “For years there has been a theory that millions of monkeys typing at random on millions of typewriters would reproduce the entire works of Shakespeare. The Internet has proven this theory to be untrue.” --- ## Intergalactic Interconnections You can link between slides internally, [like this](#/1/3). --- ## Speaker There's a [speaker view](https://github.com/hakimel/reveal.js#speaker-notes). It includes a timer, preview of the upcoming slide as well as your speaker notes. Press the _S_ key to try it out. Note: Oh hey, these are some notes. They'll be hidden in your presentation, but you can see them if you open the speaker notes window (hit `s` on your keyboard). --- ## Take a Moment Press `B` or `.` on your keyboard to pause the presentation. This is helpful when you're on stage and want to take distracting slides off the screen. --- ## Print your Slides Down below you can find a print icon. After you click on it, use the print function of your browser (either CTRL+P or cmd+P) to print the slides as PDF. [See official reveal.js instructions for details](https://github.com/hakimel/reveal.js#instructions-1) --- # The End ================================================ FILE: public/docs/yaml-metadata.md ================================================ --- robots: index, follow lang: en dir: ltr breaks: true --- Supported YAML metadata === First you need to insert syntax like this at the **start** of the note: ``` --- YAML metas --- ``` Replace the "YAML metas" in this section with any YAML options as below. You can also refer to this note's source code. title --- This option will set the note title which prior than content title. > default: not set **Example** ```yml title: meta title ``` description --- This option will set the note description. > default: not set **Example** ```yml description: meta description ``` image --- This option will set the html meta tag 'image'. > default: not set **Example** ```yml image: https://raw.githubusercontent.com/hackmdio/codimd/develop/public/screenshot.png ``` tags --- This option will set the tags which prior than content tags. > default: not set **Example** ```yml tags: features, cool, updated ``` robots --- This option will give below meta in the note head meta: ```xml ``` So you can prevent any search engine index your note by set `noindex, nofollow`. > default: not set **Example** ```yml robots: noindex, nofollow ``` lang --- This option will set the language of the note, that might alter some typography of it. You can find your the language code in ISO 639-1 standard: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes > default: not set (which will be en) **Example** ```yml langs: ja-jp ``` dir --- This option specifies the direction of the text in this note. You can only use whether `rtl` or `ltr`. Look more at here: http://www.w3.org/International/questions/qa-html-dir > default: not set (which will be ltr) **Example** ```yml dir: rtl ``` breaks --- This option means the hardbreaks in the note will be parsed or be ignore. The original markdown syntax breaks only if you put space twice, but CodiMD choose to breaks every time you enter a break. You can only use whether `true` or `false`. > default: not set (which will be true) **Example** ```yml breaks: false ``` GA --- This option allows you to enable Google Analytics with your ID. > default: not set (which won't enable) **Example** ```yml GA: UA-12345667-8 ``` disqus --- This option allows you to enable Disqus with your shortname. > default: not set (which won't enable) **Example** ```yml disqus: codimd ``` toc --- This option allows you to set options regarding the table of contents (toc). Currently, its only option is to set the maxDepth. **Notice: always use two spaces as indention in YAML metadata!** > **maxDepth:** > default: not set (whioch will show everything until level 3 (h1 -- h3)) > max: 5 (as defined by md-toc.js) **Example** type --- This option allows you to switch the document view to the slide preview, to simplify live editing of presentations. > default: not set **Example:** ```yml type: slide ``` slideOptions --- This option allows you to provide custom options to slide mode. Please below document for more details: https://github.com/hakimel/reveal.js/#configuration You could also set slide theme which named in below css files: https://github.com/hakimel/reveal.js/tree/master/css/theme **Notice: always use two spaces as indention in YAML metadata!** > default: not set (which use default slide options) **Example** ```yml slideOptions: transition: fade theme: white ``` ================================================ FILE: public/js/cover.js ================================================ /* eslint-env browser, jquery */ /* global moment, serverurl */ import { checkIfAuth, clearLoginState, getLoginState, resetCheckAuth, setloginStateChangeEvent } from './lib/common/login' import { clearDuplicatedHistory, deleteServerHistory, getHistory, getStorageHistory, parseHistory, parseServerToHistory, parseStorageToHistory, postHistoryToServer, removeHistory, saveHistory, saveStorageHistoryToServer } from './history' import { saveAs } from 'file-saver' import List from 'list.js' import unescapeHTML from 'lodash/unescape' require('./locale') require('../css/cover.css') require('../css/site.css') const options = { valueNames: ['id', 'text', 'timestamp', 'fromNow', 'time', 'tags', 'pinned'], item: `
  • visited

  • `, page: 18, pagination: [{ outerWindow: 1 }] } const historyList = new List('history', options) window.migrateHistoryFromTempCallback = pageInit setloginStateChangeEvent(pageInit) pageInit() function pageInit () { checkIfAuth( data => { $('.ui-signin').hide() $('.ui-or').hide() $('.ui-welcome').show() if (data.photo) $('.ui-avatar').prop('src', data.photo).show() else $('.ui-avatar').prop('src', '').hide() $('.ui-name').html(data.name) $('.ui-signout').show() $('.ui-history').click() parseServerToHistory(historyList, parseHistoryCallback) }, () => { $('.ui-signin').show() $('.ui-or').show() $('.ui-welcome').hide() $('.ui-avatar').prop('src', '').hide() $('.ui-name').html('') $('.ui-signout').hide() parseStorageToHistory(historyList, parseHistoryCallback) } ) } $('.masthead-nav > li').click(function () { $(this).siblings().removeClass('active') $(this).addClass('active') }) // prevent empty link change hash $('a[href="#"]').click(function (e) { e.preventDefault() }) $('.ui-home').click(function (e) { if (!$('#home').is(':visible')) { $('.section:visible').hide() $('#home').fadeIn() } }) $('.ui-history').click(() => { if (!$('#history').is(':visible')) { $('.section:visible').hide() $('#history').fadeIn() } }) function checkHistoryList () { if ($('#history-list').children().length > 0) { $('.pagination').show() $('.ui-nohistory').hide() $('.ui-import-from-browser').hide() } else if ($('#history-list').children().length === 0) { $('.pagination').hide() $('.ui-nohistory').slideDown() getStorageHistory(data => { if (data && data.length > 0 && getLoginState() && historyList.items.length === 0) { $('.ui-import-from-browser').slideDown() } }) } } function parseHistoryCallback (list, notehistory) { checkHistoryList() // sort by pinned then timestamp list.sort('', { sortFunction (a, b) { const notea = a.values() const noteb = b.values() if (notea.pinned && !noteb.pinned) { return -1 } else if (!notea.pinned && noteb.pinned) { return 1 } else { if (notea.timestamp > noteb.timestamp) { return -1 } else if (notea.timestamp < noteb.timestamp) { return 1 } else { return 0 } } } }) // parse filter tags const filtertags = [] for (let i = 0, l = list.items.length; i < l; i++) { const tags = list.items[i]._values.tags if (tags && tags.length > 0) { for (let j = 0; j < tags.length; j++) { // push info filtertags if not found let found = false if (filtertags.includes(tags[j])) { found = true } if (!found) { filtertags.push(tags[j]) } } } } buildTagsFilter(filtertags) } // update items whenever list updated historyList.on('updated', e => { for (let i = 0, l = e.items.length; i < l; i++) { const item = e.items[i] if (item.visible()) { const itemEl = $(item.elm) const values = item._values const a = itemEl.find('a') const pin = itemEl.find('.ui-history-pin') const tagsEl = itemEl.find('.tags') // parse link to element a a.attr('href', `${serverurl}/${values.id}`) // parse pinned if (values.pinned) { pin.addClass('active') } else { pin.removeClass('active') } // parse tags const tags = values.tags if (tags && tags.length > 0 && tagsEl.children().length <= 0) { const labels = [] for (let j = 0; j < tags.length; j++) { // push into the item label labels.push(`${tags[j]}`) } tagsEl.html(labels.join(' ')) } } } $('.ui-history-close').off('click') $('.ui-history-close').on('click', historyCloseClick) $('.ui-history-pin').off('click') $('.ui-history-pin').on('click', historyPinClick) }) function historyCloseClick (e) { e.preventDefault() const id = $(this).closest('a').siblings('span').html() const value = historyList.get('id', id)[0]._values $('.ui-delete-history-modal-msg').text('Do you really want to delete below history?') $('.ui-delete-history-modal-item').html(` ${value.text}
    ${value.time}`) clearHistory = false deleteId = id } function historyPinClick (e) { e.preventDefault() const $this = $(this) const id = $this.closest('a').siblings('span').html() const item = historyList.get('id', id)[0] const values = item._values let pinned = values.pinned if (!values.pinned) { pinned = true item._values.pinned = true } else { pinned = false item._values.pinned = false } checkIfAuth(() => { postHistoryToServer(id, { pinned }, (err, result) => { if (!err) { if (pinned) { $this.addClass('active') } else { $this.removeClass('active') } } }) }, () => { getHistory(notehistory => { for (let i = 0; i < notehistory.length; i++) { if (notehistory[i].id === id) { notehistory[i].pinned = pinned break } } saveHistory(notehistory) if (pinned) { $this.addClass('active') } else { $this.removeClass('active') } }) }) } // auto update item fromNow every minutes setInterval(updateItemFromNow, 60000) function updateItemFromNow () { const items = $('.item').toArray() for (let i = 0; i < items.length; i++) { const item = $(items[i]) const timestamp = parseInt(item.find('.timestamp').text()) item.find('.fromNow').text(moment(timestamp).fromNow()) } } var clearHistory = false var deleteId = null function deleteHistory () { checkIfAuth(() => { deleteServerHistory(deleteId, (err, result) => { if (!err) { if (clearHistory) { historyList.clear() checkHistoryList() } else { historyList.remove('id', deleteId) checkHistoryList() } } $('.delete-history-modal').modal('hide') deleteId = null clearHistory = false }) }, () => { if (clearHistory) { saveHistory([]) historyList.clear() checkHistoryList() deleteId = null } else { if (!deleteId) return getHistory(notehistory => { const newnotehistory = removeHistory(deleteId, notehistory) saveHistory(newnotehistory) historyList.remove('id', deleteId) checkHistoryList() deleteId = null }) } $('.delete-history-modal').modal('hide') clearHistory = false }) } $('.ui-delete-history-modal-confirm').click(() => { deleteHistory() }) $('.ui-import-from-browser').click(() => { saveStorageHistoryToServer(() => { parseStorageToHistory(historyList, parseHistoryCallback) }) }) $('.ui-save-history').click(() => { getHistory(data => { const history = JSON.stringify(data) const blob = new Blob([history], { type: 'application/json;charset=utf-8' }) saveAs(blob, `codimd_history_${moment().format('YYYYMMDDHHmmss')}`, true) }) }) $('.ui-open-history').bind('change', e => { const files = e.target.files || e.dataTransfer.files const file = files[0] const reader = new FileReader() reader.onload = () => { const notehistory = JSON.parse(reader.result) // console.log(notehistory); if (!reader.result) return getHistory(data => { let mergedata = data.concat(notehistory) mergedata = clearDuplicatedHistory(mergedata) saveHistory(mergedata) parseHistory(historyList, parseHistoryCallback) }) $('.ui-open-history').replaceWith($('.ui-open-history').val('').clone(true)) } reader.readAsText(file) }) $('.ui-clear-history').click(() => { $('.ui-delete-history-modal-msg').text('Do you really want to clear all history?') $('.ui-delete-history-modal-item').html('There is no turning back.') clearHistory = true deleteId = null }) $('.ui-refresh-history').click(() => { const lastTags = $('.ui-use-tags').select2('val') $('.ui-use-tags').select2('val', '') historyList.filter() const lastKeyword = $('.search').val() $('.search').val('') historyList.search() $('#history-list').slideUp('fast') $('.pagination').hide() resetCheckAuth() historyList.clear() parseHistory(historyList, (list, notehistory) => { parseHistoryCallback(list, notehistory) $('.ui-use-tags').select2('val', lastTags) $('.ui-use-tags').trigger('change') historyList.search(lastKeyword) $('.search').val(lastKeyword) checkHistoryList() $('#history-list').slideDown('fast') }) }) $('.ui-delete-user-modal-cancel').click(() => { $('.ui-delete-user').parent().removeClass('active') }) $('.ui-logout').click(() => { clearLoginState() location.href = `${serverurl}/logout` }) let filtertags = [] $('.ui-use-tags').select2({ placeholder: $('.ui-use-tags').attr('placeholder'), multiple: true, data () { return { results: filtertags } } }) $('.select2-input').css('width', 'inherit') buildTagsFilter([]) function buildTagsFilter (tags) { for (let i = 0; i < tags.length; i++) { tags[i] = { id: i, text: unescapeHTML(tags[i]) } } filtertags = tags } $('.ui-use-tags').on('change', function () { const tags = [] const data = $(this).select2('data') for (let i = 0; i < data.length; i++) { tags.push(data[i].text) } if (tags.length > 0) { historyList.filter(item => { const values = item.values() if (!values.tags) return false let found = false for (let i = 0; i < tags.length; i++) { if (values.tags.includes(tags[i])) { found = true break } } return found }) } else { historyList.filter() } checkHistoryList() }) $('.search').keyup(() => { checkHistoryList() }) $('.ui-export-user-data').click(function (e) { document.exportNoteData.submit() }) ================================================ FILE: public/js/extra.js ================================================ /* eslint-env browser, jquery */ /* global moment, serverurl, plantumlServer, L */ import Prism from 'prismjs' import hljs from 'highlight.js' import PDFObject from 'pdfobject' import { saveAs } from 'file-saver' import escapeHTML from 'lodash/escape' import unescapeHTML from 'lodash/unescape' import isURL from 'validator/lib/isURL' import { Transformer } from 'markmap-lib' import { Markmap, loadCSS, loadJS } from 'markmap-view' import { stripTags } from '../../utils/string' import getUIElements from './lib/editor/ui-elements' import { emojifyImageDir } from './lib/editor/constants' import { parseFenceCodeParams, serializeParamToAttribute, deserializeParamAttributeFromElement } from './lib/markdown/utils' import { renderFretBoard } from './lib/renderer/fretboard/fretboard' import './lib/renderer/lightbox' import { renderCSVPreview } from './lib/renderer/csvpreview' import { escapeAttrValue } from './render' import { sanitizeUrl, isPdfUrl } from './utils' import markdownit from 'markdown-it' import markdownitContainer from 'markdown-it-container' /* Defined regex markdown it plugins */ import Plugin from 'markdown-it-regexp' require('prismjs/themes/prism.css') require('prismjs/components/prism-wiki') require('prismjs/components/prism-haskell') require('prismjs/components/prism-go') require('prismjs/components/prism-typescript') require('prismjs/components/prism-jsx') require('prismjs/components/prism-makefile') require('prismjs/components/prism-gherkin') require('./lib/common/login') require('../vendor/md-toc') let viz = new window.Viz() const plantumlEncoder = require('plantuml-encoder') const ui = getUIElements() // Initialize markmap transformer const markmapTransformer = new Transformer() // auto update last change window.createtime = null window.lastchangetime = null window.lastchangeui = { status: $('.ui-status-lastchange'), time: $('.ui-lastchange'), user: $('.ui-lastchangeuser'), nouser: $('.ui-no-lastchangeuser') } const ownerui = $('.ui-owner') export function updateLastChange () { if (!window.lastchangeui) return if (window.createtime) { if (window.createtime && !window.lastchangetime) { window.lastchangeui.status.text('created') } else { window.lastchangeui.status.text('changed') } const time = window.lastchangetime || window.createtime window.lastchangeui.time.html(moment(time).fromNow()) window.lastchangeui.time.attr('title', moment(time).format('llll')) } } setInterval(updateLastChange, 60000) window.lastchangeuser = null window.lastchangeuserprofile = null export function updateLastChangeUser () { if (window.lastchangeui) { if (window.lastchangeuser && window.lastchangeuserprofile) { const icon = window.lastchangeui.user.children('i') icon.attr('title', window.lastchangeuserprofile.name).tooltip('fixTitle') if (window.lastchangeuserprofile.photo) { icon.attr('style', `background-image:url(${window.lastchangeuserprofile.photo})`) } window.lastchangeui.user.show() window.lastchangeui.nouser.hide() } else { window.lastchangeui.user.hide() window.lastchangeui.nouser.show() } } } window.owner = null window.ownerprofile = null export function updateOwner () { if (ownerui) { if (window.owner && window.ownerprofile && window.owner !== window.lastchangeuser) { const icon = ownerui.children('i') icon.attr('title', window.ownerprofile.name).tooltip('fixTitle') const styleString = `background-image:url(${window.ownerprofile.photo})` if (window.ownerprofile.photo && icon.attr('style') !== styleString) { icon.attr('style', styleString) } ownerui.show() } else { ownerui.hide() } } } // get title function getTitle (view) { let title = '' if (md && md.meta && md.meta.title && (typeof md.meta.title === 'string' || typeof md.meta.title === 'number')) { title = md.meta.title } else { const h1s = view.find('h1') if (h1s.length > 0) { title = h1s.first().text() } else { title = null } } return title } // render title export function renderTitle (view) { let title = getTitle(view) if (title) { title += ' - CodiMD' } else { title = 'CodiMD - Collaborative markdown notes' } return title } // render filename export function renderFilename (view) { let filename = getTitle(view) if (!filename) { filename = 'Untitled' } return filename } // render tags export function renderTags (view) { const tags = [] const rawtags = [] if (md && md.meta && md.meta.tags && (typeof md.meta.tags === 'string' || typeof md.meta.tags === 'number')) { const metaTags = (`${md.meta.tags}`).split(',') for (let i = 0; i < metaTags.length; i++) { const text = metaTags[i].trim() if (text) rawtags.push(text) } } else { view.find('h6').each((key, value) => { if (/^tags/gmi.test($(value).text())) { const codes = $(value).find('code') for (let i = 0; i < codes.length; i++) { const text = codes[i].innerHTML.trim() if (text) rawtags.push(text) } } }) } for (let i = 0; i < rawtags.length; i++) { let found = false for (let j = 0; j < tags.length; j++) { if (tags[j] === rawtags[i]) { found = true break } } if (!found) { tags.push(rawtags[i]) } } return tags } function slugifyWithUTF8 (text) { // remove HTML tags and trim spaces let newText = stripTags(text.toString().trim()) // replace space between words with dashes newText = newText.replace(/\s+/g, '-') // slugify string to make it valid as an attribute newText = newText.replace(/([!"#$%&'()*+,./:;<=>?@[\\\]^`{|}~])/g, '') return newText } // parse meta export function parseMeta (md, edit, view, toc, tocAffix) { let lang = null let dir = null let breaks = window.defaultUseHardbreak if (md && md.meta) { const meta = md.meta lang = meta.lang dir = meta.dir breaks = meta.breaks } if (!lang || typeof lang !== 'string') { lang = 'en' } // text language view.attr('lang', lang) toc.attr('lang', lang) tocAffix.attr('lang', lang) if (edit) { edit.attr('lang', lang) } // text direction if (dir && typeof dir === 'string') { view.attr('dir', dir) toc.attr('dir', dir) tocAffix.attr('dir', dir) } else { view.removeAttr('dir') toc.removeAttr('dir') tocAffix.removeAttr('dir') } // breaks if (typeof breaks === 'boolean') { md.options.breaks = breaks } else { md.options.breaks = window.defaultUseHardbreak } } window.viewAjaxCallback = null // regex for extra tags const spaceregex = /\s*/ const notinhtmltagregex = /(?![^<]*>|[^<>]*<\/)/ let coloregex = /\[color=([#|(|)|\s|,|\w]*?)\]/ coloregex = new RegExp(coloregex.source + notinhtmltagregex.source, 'g') let nameregex = /\[name=(.*?)\]/ let timeregex = /\[time=([:|,|+|-|(|)|\s|\w]*?)\]/ const nameandtimeregex = new RegExp(nameregex.source + spaceregex.source + timeregex.source + notinhtmltagregex.source, 'g') nameregex = new RegExp(nameregex.source + notinhtmltagregex.source, 'g') timeregex = new RegExp(timeregex.source + notinhtmltagregex.source, 'g') function replaceExtraTags (html) { html = html.replace(coloregex, '') html = html.replace(nameandtimeregex, ' $1 $2') html = html.replace(nameregex, ' $1') html = html.replace(timeregex, ' $1') return html } if (typeof window.mermaid !== 'undefined' && window.mermaid) { window.mermaid.initialize({ startOnLoad: false }) window.mermaid.parseError = function (err, hash) { console.warn(err) } } function jsonp (url, callback) { const callbackName = 'jsonp_callback_' + Math.round(1000000000 * Math.random()) window[callbackName] = function (data) { delete window[callbackName] document.body.removeChild(script) callback(data) } const script = document.createElement('script') script.src = url + (url.indexOf('?') >= 0 ? '&' : '?') + 'callback=' + callbackName document.body.appendChild(script) script.onerror = function (e) { console.error(e) script.remove() } } // dynamic event or object binding here export function finishView (view) { // todo list const lis = view.find('li.raw').removeClass('raw').sortByDepth().toArray() for (let li of lis) { let html = $(li).clone()[0].innerHTML const p = $(li).children('p') if (p.length === 1) { html = p.html() li = p[0] } html = replaceExtraTags(html) li.innerHTML = html let disabled = 'disabled' if (typeof editor !== 'undefined' && window.havePermission()) { disabled = '' } if (/^\s*\[[x ]]\s*/.test(html)) { li.innerHTML = html.replace(/^\s*\[ ]\s*/, ``) .replace(/^\s*\[x]\s*/, ``) if (li.tagName.toLowerCase() !== 'li') { li.parentElement.setAttribute('class', 'task-list-item') } else { li.setAttribute('class', 'task-list-item') } } if (typeof editor !== 'undefined' && window.havePermission()) { $(li).find('input').change(toggleTodoEvent) } // color tag in list will convert it to tag icon with color const tagColor = $(li).closest('ul').find('.color') tagColor.each((key, value) => { $(value).addClass('fa fa-tag').css('color', $(value).attr('data-color')) }) } // youtube view.find('div.youtube.raw').removeClass('raw') .click(function () { imgPlayiframe(this, '//www.youtube.com/embed/') }) // vimeo view.find('div.vimeo.raw').removeClass('raw') .click(function () { imgPlayiframe(this, '//player.vimeo.com/video/') }) .each((key, value) => { const videoId = $(value).attr('data-videoid') let urlForJsonp = '' try { const url = new URL(`https://vimeo.com/api/v2/video/${videoId}.json`) if (!url.pathname.startsWith('/api/v2/video/')) { throw new Error(`Invalid vimeo video id: ${videoId}`) } urlForJsonp = `//${url.origin}${url.pathname}` } catch (err) { console.error(err) return } jsonp(urlForJsonp, function (data) { const thumbnailSrc = data[0].thumbnail_large const image = `` $(value).prepend(image) if (window.viewAjaxCallback) window.viewAjaxCallback() }) }) // gist view.find('code[data-gist-id]').each((key, value) => { if ($(value).children().length === 0) { // strip HTML tags to avoid stored XSS const gistid = value.getAttribute('data-gist-id') value.setAttribute('data-gist-id', stripTags(gistid)) const gistfile = value.getAttribute('data-gist-file') if (gistfile) value.setAttribute('data-gist-file', stripTags(gistfile)) const gistline = value.getAttribute('data-gist-line') if (gistline) value.setAttribute('data-gist-line', stripTags(gistline)) const gisthighlightline = value.getAttribute('data-gist-highlight-line') if (gisthighlightline) value.setAttribute('data-gist-highlight-line', stripTags(gisthighlightline)) const gistshowloading = value.getAttribute('data-gist-show-loading') if (gistshowloading) value.setAttribute('data-gist-show-loading', stripTags(gistshowloading)) $(value).gist(window.viewAjaxCallback) } }) // sequence diagram const sequences = view.find('div.sequence-diagram.raw').removeClass('raw') sequences.each((key, value) => { try { var $value = $(value) const $ele = $(value).parent().parent() const sequence = $value sequence.sequenceDiagram({ theme: 'simple' }) $ele.addClass('sequence-diagram') $value.children().unwrap().unwrap() const svg = $ele.find('> svg') svg[0].setAttribute('viewBox', `0 0 ${svg.attr('width')} ${svg.attr('height')}`) svg[0].setAttribute('preserveAspectRatio', 'xMidYMid meet') } catch (err) { $value.unwrap() $value.parent().append(`
    ${escapeHTML(err)}
    `) console.warn(err) } }) // flowchart const flow = view.find('div.flow-chart.raw').removeClass('raw') flow.each((key, value) => { try { var $value = $(value) const $ele = $(value).parent().parent() const chart = window.flowchart.parse($value.text()) $value.html('') chart.drawSVG(value, { 'line-width': 2, fill: 'none', 'font-size': '16px', 'font-family': "'Andale Mono', monospace" }) $ele.addClass('flow-chart') $value.children().unwrap().unwrap() } catch (err) { $value.unwrap() $value.parent().append(`
    ${escapeHTML(err)}
    `) console.warn(err) } }) // graphviz var graphvizs = view.find('div.graphviz.raw').removeClass('raw') graphvizs.each(function (key, value) { try { var $value = $(value) const options = deserializeParamAttributeFromElement(value) var $ele = $(value).parent().parent() $value.unwrap() viz.renderString($value.text(), options) .then(graphviz => { if (!graphviz) throw Error('viz.js output empty graph') $value.html(graphviz) $ele.addClass('graphviz') $value.children().unwrap() }) .catch(err => { viz = new window.Viz() $value.parent().append(`
    ${escapeHTML(err)}
    `) console.warn(err) }) } catch (err) { viz = new window.Viz() $value.parent().append(`
    ${escapeHTML(err)}
    `) console.warn(err) } }) // mermaid const mermaids = view.find('div.mermaid.raw').removeClass('raw') mermaids.each(async (key, value) => { const $value = $(value) const $ele = $value.closest('pre') try { const text = $value.text() // validate the syntax first if (window.mermaid.parse(text)) { $ele.addClass('mermaid') $ele.text(text) // render the diagram const id = `mermaid-${Date.now()}-${Math.random().toString(36).substr(2, 9)}` const { svg, bindFunctions } = await window.mermaid.render(id, text, $ele[0]) $ele.html(svg) if (bindFunctions) { bindFunctions($ele[0]) } } } catch (err) { $value.unwrap() $ele.append(`
    ${escapeHTML(err)}
    `) console.warn(err) } }) // abc.js const abcs = view.find('div.abc.raw').removeClass('raw') abcs.each((key, value) => { try { var $value = $(value) var $ele = $(value).parent().parent() window.ABCJS.renderAbc(value, $value.text()) $ele.addClass('abc') $value.children().unwrap().unwrap() const svg = $ele.find('> svg') svg[0].setAttribute('viewBox', `0 0 ${svg.attr('width')} ${svg.attr('height')}`) svg[0].setAttribute('preserveAspectRatio', 'xMidYMid meet') } catch (err) { $value.unwrap() $value.parent().append(`
    ${escapeHTML(err)}
    `) console.warn(err) } }) // vega-lite const vegas = view.find('div.vega.raw').removeClass('raw') vegas.each((key, value) => { try { var $value = $(value) var $ele = $(value).parent().parent() const specText = $value.text() $value.unwrap() window.vegaEmbed($ele[0], JSON.parse(specText), { renderer: 'svg' }) .then(result => { $ele.addClass('vega') }) .catch(err => { $ele.append(`
    ${escapeHTML(err)}
    `) console.warn(err) }) .finally(() => { if (window.viewAjaxCallback) window.viewAjaxCallback() }) } catch (err) { $ele.append(`
    ${escapeHTML(err)}
    `) console.warn(err) } }) // geo map view.find('div.geo.raw').removeClass('raw').each(async function (key, value) { const $elem = $(value).parent().parent() const $value = $(value) const content = $value.text() $value.unwrap() try { let position, zoom if (content.match(/^[-\d.,\s]+$/)) { const [lng, lat, zoo] = content.split(',').map(parseFloat) zoom = zoo position = [lat, lng] } else { // parse value as address const data = await fetch(`https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(content)}&format=json`).then(r => r.json()) if (!data || !data.length) { throw new Error('Location not found') } const { lat, lon } = data[0] position = [lat, lon] } $elem.html('
    ') const map = L.map($elem.find('.geo-map')[0]).setView(position, zoom || 16) L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: 'OSM', maxZoom: 18 }).addTo(map) L.marker(position, { icon: L.icon({ iconUrl: `${serverurl}/build/leaflet/images/marker-icon.png`, shadowUrl: `${serverurl}/build/leaflet/images/marker-shadow.png` }) }).addTo(map) $elem.addClass('geo') } catch (err) { $elem.append(`
    ${escapeHTML(err)}
    `) console.warn(err) } }) // fretboard const fretboard = view.find('div.fretboard_instance.raw').removeClass('raw') fretboard.each((key, value) => { const params = deserializeParamAttributeFromElement(value) const $value = $(value) try { const $ele = $(value).parent().parent() $ele.html(renderFretBoard($value.text(), params)) $ele.addClass('fretboard') } catch (err) { $value.unwrap() $value.parent().append(`
    ${escapeHTML(err)}
    `) console.warn(err) } }) // markmap view.find('div.markmap.raw').removeClass('raw').each(async (key, value) => { const $elem = $(value).parent().parent() const $value = $(value) const content = $value.text() $value.unwrap() try { const { root, features } = markmapTransformer.transform(content) // Sanitize node contents to prevent XSS before rendering sanitizeMarkmapNode(root) // Load required assets const { styles, scripts } = markmapTransformer.getUsedAssets(features) if (styles) loadCSS(styles) if (scripts) loadJS(scripts, { getMarkmap: () => ({ Markmap }) }) $elem.html('
    ') Markmap.create($elem.find('svg')[0], { duration: 0, maxWidth: 0 }, root) } catch (err) { $elem.html(`
    ${escapeHTML(err)}
    `) console.warn(err) } }) // image href new window(emoji not included) const images = view.find('img.raw[src]').removeClass('raw') images.each((key, value) => { // if it's already wrapped by link, then ignore const $value = $(value) $value[0].onload = e => { if (window.viewAjaxCallback) window.viewAjaxCallback() } }) // blockquote const blockquote = view.find('blockquote.raw').removeClass('raw') const blockquoteP = blockquote.find('p') blockquoteP.each((key, value) => { let html = $(value).html() html = replaceExtraTags(html) $(value).html(html) }) // color tag in blockquote will change its left border color const blockquoteColor = blockquote.find('.color') blockquoteColor.each((key, value) => { $(value).closest('blockquote').css('border-left-color', $(value).attr('data-color')) }) // slideshare view.find('div.slideshare.raw').removeClass('raw') .each((key, value) => { $.ajax({ type: 'GET', url: `//www.slideshare.net/api/oembed/2?url=http://www.slideshare.net/${$(value).attr('data-slideshareid')}&format=json`, jsonp: 'callback', dataType: 'jsonp', success (data) { const $html = $(data.html) const iframe = $html.closest('iframe') const caption = $html.closest('div') const inner = $('
    ').append(iframe) const height = iframe.attr('height') const width = iframe.attr('width') const ratio = (height / width) * 100 inner.css('padding-bottom', `${ratio}%`) $(value).html(inner).append(caption) if (window.viewAjaxCallback) window.viewAjaxCallback() } }) }) // speakerdeck view.find('div.speakerdeck.raw').removeClass('raw') .each((key, value) => { const url = `https://speakerdeck.com/${$(value).attr('data-speakerdeckid')}` const inner = $('Speakerdeck') inner.attr('href', url) inner.attr('rel', 'noopener noreferrer') inner.attr('target', '_blank') $(value).append(inner) }) // pdf view.find('div.pdf.raw').removeClass('raw') .each(function (key, value) { const url = $(value).attr('data-pdfurl') const cleanUrl = sanitizeUrl(url) const inner = $('
    ') $(this).append(inner) // First check URL format const isPDFByExtension = /\.pdf(\?.*)?$/i.test(cleanUrl) || cleanUrl.includes('pdf') if (isPDFByExtension) { // Show loading message while we check content type const loadingMessage = $('
    Verifying PDF file...
    ') inner.html(loadingMessage) // Perform additional validation with HEAD request isPdfUrl(cleanUrl).then(isPDFByContentType => { if (isPDFByContentType) { // Valid PDF by content type, embed it PDFObject.embed(cleanUrl, inner, { height: '400px' }) } else { // URL format looks like PDF but content type doesn't match inner.html('
    The URL looks like a PDF but the server didn\'t confirm it has a PDF content type.
    ') console.warn('URL has PDF extension but content type is not application/pdf:', cleanUrl) // Try to embed anyway as a fallback setTimeout(() => { PDFObject.embed(cleanUrl, inner, { height: '400px', fallbackLink: 'This doesn\'t appear to be a valid PDF. Click here to try downloading it directly.' }) }, 1) } }) } else { // Not a valid PDF URL by extension inner.html('
    Invalid PDF URL. The URL must point to a PDF file.
    ') console.warn('Invalid PDF URL format:', cleanUrl) } }) // syntax highlighting view.find('code.raw').removeClass('raw') .each((key, value) => { const langDiv = $(value) if (langDiv.length > 0) { const reallang = langDiv[0].className.replace(/hljs|wrap/g, '').trim() const codeDiv = langDiv.find('.code') let code = '' if (codeDiv.length > 0) code = codeDiv.html() else code = langDiv.html() var result if (!reallang) { result = { value: code } } else if (reallang === 'haskell' || reallang === 'go' || reallang === 'typescript' || reallang === 'jsx' || reallang === 'gherkin') { code = unescapeHTML(code) result = { value: Prism.highlight(code, Prism.languages[reallang]) } } else if (reallang === 'tiddlywiki' || reallang === 'mediawiki') { code = unescapeHTML(code) result = { value: Prism.highlight(code, Prism.languages.wiki) } } else if (reallang === 'cmake') { code = unescapeHTML(code) result = { value: Prism.highlight(code, Prism.languages.makefile) } } else { code = unescapeHTML(code) const languages = hljs.listLanguages() if (!languages.includes(reallang)) { result = hljs.highlightAuto(code) } else { result = hljs.highlight(reallang, code) } } if (codeDiv.length > 0) codeDiv.html(result.value) else langDiv.html(result.value) } }) // mathjax const mathjaxdivs = view.find('span.mathjax.raw').removeClass('raw').toArray() try { if (mathjaxdivs.length > 1) { window.MathJax.Hub.Queue(['Typeset', window.MathJax.Hub, mathjaxdivs]) window.MathJax.Hub.Queue(window.viewAjaxCallback) } else if (mathjaxdivs.length > 0) { window.MathJax.Hub.Queue(['Typeset', window.MathJax.Hub, mathjaxdivs[0]]) window.MathJax.Hub.Queue(window.viewAjaxCallback) } } catch (err) { console.warn(err) } // register details toggle for scrollmap recalulation view.find('details.raw').removeClass('raw').each(function (key, val) { $(val).on('toggle', window.viewAjaxCallback) }) // render title document.title = renderTitle(view) } // only static transform should be here export function postProcess (code) { const result = $(`
    ${code}
    `) // process style tags result.find('style').each((key, value) => { let html = $(value).html() // unescape > symbel inside the style tags html = html.replace(/>/g, '>') // remove css @import to prevent XSS html = html.replace(/@import url\(([^)]*)\);?/gi, '') $(value).html(html) }) // link should open in new window or tab // also add noopener to prevent clickjacking // See details: https://mathiasbynens.github.io/rel-noopener/ result.find('a:not([href^="#"]):not([target])').attr('target', '_blank').attr('rel', 'noopener') // update continue line numbers const linenumberdivs = result.find('.gutter.linenumber').toArray() for (let i = 0; i < linenumberdivs.length; i++) { if ($(linenumberdivs[i]).hasClass('continue')) { const startnumber = linenumberdivs[i - 1] ? parseInt($(linenumberdivs[i - 1]).find('> span').last().attr('data-linenumber')) : 0 $(linenumberdivs[i]).find('> span').each((key, value) => { $(value).attr('data-linenumber', startnumber + key + 1) }) } } // show yaml meta paring error if (md.metaError) { var warning = result.find('div#meta-error') if (warning && warning.length > 0) { warning.text(md.metaError) } else { warning = $(`
    ${escapeHTML(md.metaError)}
    `) result.prepend(warning) } } return result } window.postProcess = postProcess var domevents = Object.getOwnPropertyNames(document).concat(Object.getOwnPropertyNames(Object.getPrototypeOf(Object.getPrototypeOf(document)))).concat(Object.getOwnPropertyNames(Object.getPrototypeOf(window))).filter(function (i) { return !i.indexOf('on') && (document[i] === null || typeof document[i] === 'function') }).filter(function (elem, pos, self) { return self.indexOf(elem) === pos }) export function removeDOMEvents (view) { for (var i = 0, l = domevents.length; i < l; i++) { view.find('[' + domevents[i] + ']').removeAttr(domevents[i]) } } window.removeDOMEvents = removeDOMEvents function generateCleanHTML (view) { const src = view.clone() const eles = src.find('*') // remove syncscroll parts eles.removeClass('part') src.find('*[class=""]').removeAttr('class') eles.removeAttr('data-startline data-endline') src.find("a[href^='#'][smoothhashscroll]").removeAttr('smoothhashscroll') // remove gist content src.find('code[data-gist-id]').children().remove() // disable todo list src.find('input.task-list-item-checkbox').attr('disabled', '') // replace emoji image path src.find('img.emoji').each((key, value) => { let name = $(value).attr('alt') name = name.substr(1) name = name.slice(0, name.length - 1) $(value).attr('src', `https://cdn.jsdelivr.net/npm/@hackmd/emojify.js@2.1.0/dist/images/basic/${name}.png`) }) // replace video to iframe src.find('div[data-videoid]').each((key, value) => { const id = $(value).attr('data-videoid') const style = $(value).attr('style') let url = null if ($(value).hasClass('youtube')) { url = 'https://www.youtube.com/embed/' } else if ($(value).hasClass('vimeo')) { url = 'https://player.vimeo.com/video/' } if (url) { const iframe = $('') iframe.attr('src', url + id) iframe.attr('style', style) $(value).html(iframe) } }) return src } export function exportToRawHTML (view) { const filename = `${renderFilename(ui.area.markdown)}.html` const src = generateCleanHTML(view) $(src).find('a.anchor').remove() const html = src[0].outerHTML const blob = new Blob([html], { type: 'text/html;charset=utf-8' }) saveAs(blob, filename, true) } // extract markdown body to html and compile to template export function exportToHTML (view) { const title = renderTitle(ui.area.markdown) const filename = `${renderFilename(ui.area.markdown)}.html` const src = generateCleanHTML(view) // generate toc const toc = $('#ui-toc').clone() toc.find('*').removeClass('active').find("a[href^='#'][smoothhashscroll]").removeAttr('smoothhashscroll') const tocAffix = $('#ui-toc-affix').clone() tocAffix.find('*').removeClass('active').find("a[href^='#'][smoothhashscroll]").removeAttr('smoothhashscroll') // generate html via template $.get(`${serverurl}/build/html.min.css`, css => { $.get(`${serverurl}/views/html.hbs`, data => { const template = window.Handlebars.compile(data) const context = { url: serverurl, title, css, html: src[0].outerHTML, 'ui-toc': toc.html(), 'ui-toc-affix': tocAffix.html(), lang: (md && md.meta && md.meta.lang) ? `lang="${escapeAttrValue(md.meta.lang)}"` : null, dir: (md && md.meta && md.meta.dir) ? `dir="${escapeAttrValue(md.meta.dir)}"` : null } const html = template(context) // console.log(html); const blob = new Blob([html], { type: 'text/html;charset=utf-8' }) saveAs(blob, filename, true) }) }) } // jQuery sortByDepth $.fn.sortByDepth = function () { const ar = this.map(function () { return { length: $(this).parents().length, elt: this } }).get() const result = [] let i = ar.length ar.sort((a, b) => a.length - b.length) while (i--) { result.push(ar[i].elt) } return $(result) } function toggleTodoEvent (e) { const startline = $(this).closest('li').attr('data-startline') - 1 const line = window.editor.getLine(startline) const matches = line.match(/^[>\s-]*[-+*]\s\[([x ])\]/) if (matches && matches.length >= 2) { let checked = null if (matches[1] === 'x') { checked = true } else if (matches[1] === ' ') { checked = false } const replacements = matches[0].match(/(^[>\s-]*[-+*]\s\[)([x ])(\])/) window.editor.replaceRange(checked ? ' ' : 'x', { line: startline, ch: replacements[1].length }, { line: startline, ch: replacements[1].length + 1 }, '+input') } } // remove hash function removeHash () { history.pushState('', document.title, window.location.pathname + window.location.search) } let tocExpand = false function checkExpandToggle () { const toc = $('.ui-toc-dropdown .toc') const expand = $('.expand-toggle.expand-all') const collapse = $('.expand-toggle.collapse-all') if (!tocExpand) { toc.removeClass('expand') expand.show() collapse.hide() } else { toc.addClass('expand') expand.hide() collapse.show() } } // toc export function generateToc (id) { const target = $(`#${id}`) target.html('') /* eslint-disable no-unused-vars */ var tocOptions = md.meta.toc || {} var maxLevel = (typeof tocOptions.maxLevel === 'number' && tocOptions.maxLevel > 0) ? tocOptions.maxLevel : window.defaultTocDepth var toc = new window.Toc('doc', { level: maxLevel, top: -1, class: 'toc', ulClass: 'nav', targetId: id, process: getHeaderContent }) /* eslint-enable no-unused-vars */ if (target.text() === 'undefined') { target.html('') } checkExpandToggle() const tocMenu = $('body').children('.toc-menu') target.append(tocMenu.clone().show()) const toggle = $('.expand-toggle', target) const backtotop = $('.back-to-top', target) const gotobottom = $('.go-to-bottom', target) toggle.click(e => { e.preventDefault() e.stopPropagation() tocExpand = !tocExpand checkExpandToggle() }) backtotop.click(e => { e.preventDefault() e.stopPropagation() if (window.scrollToTop) { window.scrollToTop() } removeHash() }) gotobottom.click(e => { e.preventDefault() e.stopPropagation() if (window.scrollToBottom) { window.scrollToBottom() } removeHash() }) } // smooth all hash trigger scrolling export function smoothHashScroll () { const hashElements = $("a[href^='#']:not([smoothhashscroll])").toArray() for (const element of hashElements) { const $element = $(element) const hash = element.hash if (hash) { $element.on('click', function (e) { // store hash const hash = decodeURIComponent(this.hash) // escape special characters in jquery selector const $hash = $(hash.replace(/(:|\.|\[|\]|,)/g, '\\$1')) // return if no element been selected if ($hash.length <= 0) return // prevent default anchor click behavior e.preventDefault() // animate $('body, html').stop(true, true).animate({ scrollTop: $hash.offset().top }, 100, 'linear', () => { // when done, add hash to url // (default click behaviour) window.location.hash = hash }) }) $element.attr('smoothhashscroll', '') } } } function imgPlayiframe (element, src) { if (!$(element).attr('data-videoid')) return const iframe = $("") $(iframe).attr('src', `${src + $(element).attr('data-videoid')}?autoplay=1`) $(element).find('img').css('visibility', 'hidden') $(element).append(iframe) } const anchorForId = id => { const anchor = document.createElement('a') anchor.className = 'anchor hidden-xs' anchor.href = `#${id}` anchor.innerHTML = '' anchor.title = id return anchor } const createHeaderId = (headerContent, headerIds = null) => { // to escape characters not allow in css and humanize const slug = slugifyWithUTF8(headerContent) let id if (window.linkifyHeaderStyle === 'keep-case') { id = slug } else if (window.linkifyHeaderStyle === 'lower-case') { // to make compatible with GitHub, GitLab, Pandoc and many more id = slug.toLowerCase() } else if (window.linkifyHeaderStyle === 'gfm') { // see GitHub implementation reference: // https://gist.github.com/asabaylus/3071099#gistcomment-1593627 // it works like 'lower-case', but ... const idBase = slug.toLowerCase() id = idBase if (headerIds !== null) { // ... making sure the id is unique let i = 1 while (headerIds.has(id)) { id = idBase + '-' + i i++ } headerIds.add(id) } } else { throw new Error('Unknown linkifyHeaderStyle value "' + window.linkifyHeaderStyle + '"') } return id } const linkifyAnchors = (level, containingElement) => { const headers = containingElement.getElementsByTagName(`h${level}`) for (let i = 0, l = headers.length; i < l; i++) { const header = headers[i] if (header.getElementsByClassName('anchor').length === 0) { if (typeof header.id === 'undefined' || header.id === '') { header.id = createHeaderId(getHeaderContent(header)) } if (!(typeof header.id === 'undefined' || header.id === '')) { header.insertBefore(anchorForId(header.id), header.firstChild) } } } } export function autoLinkify (view) { const contentBlock = view[0] if (!contentBlock) { return } for (let level = 1; level <= 6; level++) { linkifyAnchors(level, contentBlock) } } function getHeaderContent (header) { const headerHTML = $(header).clone() headerHTML.find('.MathJax_Preview').remove() headerHTML.find('.MathJax').remove() return headerHTML[0].innerHTML } function changeHeaderId ($header, id, newId) { $header.attr('id', newId) const $headerLink = $header.find(`> a.anchor[href="#${id}"]`) $headerLink.attr('href', `#${newId}`) $headerLink.attr('title', newId) } export function deduplicatedHeaderId (view) { // headers contained in the last change const headers = view.find(':header.raw').removeClass('raw').toArray() if (headers.length === 0) { return } if (window.linkifyHeaderStyle === 'gfm') { // consistent with GitHub, GitLab, Pandoc & co. // all headers contained in the document, in order of appearance const allHeaders = view.find(':header').toArray() // list of finaly assigned header IDs const headerIds = new Set() for (let j = 0; j < allHeaders.length; j++) { const $header = $(allHeaders[j]) const id = $header.attr('id') const newId = createHeaderId(getHeaderContent($header), headerIds) changeHeaderId($header, id, newId) } } else { // the legacy way for (let i = 0; i < headers.length; i++) { const id = $(headers[i]).attr('id') if (!id) continue const duplicatedHeaders = view.find(`:header[id="${id}"]`).toArray() for (let j = 0; j < duplicatedHeaders.length; j++) { if (duplicatedHeaders[j] !== headers[i]) { const newId = id + j const $header = $(duplicatedHeaders[j]) changeHeaderId($header, id, newId) } } } } } export function renderTOC (view) { const tocs = view.find('.toc').toArray() for (let i = 0; i < tocs.length; i++) { const toc = $(tocs[i]) const id = `toc${i}` toc.attr('id', id) const target = $(`#${id}`) target.html('') /* eslint-disable no-unused-vars */ const specificDepth = parseInt(toc.data('toc-depth')) var tocOptions = md.meta.toc || {} var yamlMaxDepth = (typeof tocOptions.maxLevel === 'number' && tocOptions.maxLevel > 0) ? tocOptions.maxLevel : window.defaultTocDepth var maxLevel = specificDepth || yamlMaxDepth const TOC = new window.Toc('doc', { level: maxLevel, top: -1, class: 'toc', targetId: id, data: { tocDepth: specificDepth }, process: getHeaderContent }) /* eslint-enable no-unused-vars */ if (target.text() === 'undefined') { target.html('') } target.replaceWith(target.html()) } } export function scrollToHash () { const hash = location.hash location.hash = '' location.hash = hash } const fenceCodeAlias = { sequence: 'sequence-diagram', flow: 'flow-chart', graphviz: 'graphviz', mermaid: 'mermaid', abc: 'abc', vega: 'vega', geo: 'geo', fretboard: 'fretboard_instance', markmap: 'markmap' } function highlightRender (code, lang) { if (!lang || /no(-?)highlight|plain|text/.test(lang)) { return } const params = parseFenceCodeParams(lang) const attr = serializeParamToAttribute(params) lang = lang.split(/\s+/g)[0] code = escapeHTML(code) const langAlias = fenceCodeAlias[lang] if (langAlias) { return `
    ${code}
    ` } const result = { value: code } const showlinenumbers = /=$|=\d+$|=\+$/.test(lang) if (showlinenumbers) { let startnumber = 1 const matches = lang.match(/=(\d+)$/) if (matches) { startnumber = parseInt(matches[1]) } const lines = result.value.split('\n') const linenumbers = [] for (let i = 0; i < lines.length - 1; i++) { linenumbers[i] = `` } const continuelinenumber = /=\+$/.test(lang) const linegutter = `
    ${linenumbers.join('\n')}
    ` result.value = `
    ${linegutter}
    ${result.value}
    ` } return result.value } export const md = markdownit('default', { html: true, breaks: window.defaultUseHardbreak, langPrefix: '', linkify: true, typographer: true, highlight: highlightRender }) window.md = md md.use(require('markdown-it-abbr')) md.use(require('markdown-it-footnote')) md.use(require('markdown-it-deflist')) md.use(require('markdown-it-mark')) md.use(require('markdown-it-ins')) md.use(require('markdown-it-sub')) md.use(require('markdown-it-sup')) md.use(require('markdown-it-mathjax')({ beforeMath: '', afterMath: '', beforeInlineMath: '\\(', afterInlineMath: '\\)', beforeDisplayMath: '\\[', afterDisplayMath: '\\]' })) md.use(require('markdown-it-imsize')) md.use(require('markdown-it-ruby')) window.emojify.setConfig({ blacklist: { elements: ['script', 'textarea', 'a', 'pre', 'code', 'svg'], classes: ['no-emojify'] }, img_dir: emojifyImageDir, ignore_emoticons: true }) function renderContainer (tokens, idx, options, env, self) { tokens[idx].attrJoin('role', 'alert') tokens[idx].attrJoin('class', 'alert') tokens[idx].attrJoin('class', `alert-${tokens[idx].info.trim()}`) return self.renderToken(...arguments) } md.use(markdownitContainer, 'success', { render: renderContainer }) md.use(markdownitContainer, 'info', { render: renderContainer }) md.use(markdownitContainer, 'warning', { render: renderContainer }) md.use(markdownitContainer, 'danger', { render: renderContainer }) md.use(markdownitContainer, 'spoiler', { validate: function (params) { return params.trim().match(/^spoiler(\s+.*)?$/) }, render: function (tokens, idx) { const m = tokens[idx].info.trim().match(/^spoiler(\s+.*)?$/) if (tokens[idx].nesting === 1) { // opening tag const summary = m[1] && m[1].trim() if (summary) { return `
    ${md.renderInline(summary)}\n` } else { return '
    \n' } } else { // closing tag return '
    \n' } } }) const defaultImageRender = md.renderer.rules.image md.renderer.rules.image = function (tokens, idx, options, env, self) { tokens[idx].attrJoin('class', 'raw') tokens[idx].attrJoin('class', 'md-image') return defaultImageRender(...arguments) } md.renderer.rules.list_item_open = function (tokens, idx, options, env, self) { tokens[idx].attrJoin('class', 'raw') return self.renderToken(...arguments) } md.renderer.rules.blockquote_open = function (tokens, idx, options, env, self) { tokens[idx].attrJoin('class', 'raw') return self.renderToken(...arguments) } md.renderer.rules.heading_open = function (tokens, idx, options, env, self) { tokens[idx].attrJoin('class', 'raw') return self.renderToken(...arguments) } md.renderer.rules.fence = (tokens, idx, options, env, self) => { const token = tokens[idx] const info = token.info ? md.utils.unescapeAll(token.info).trim() : '' let langName = '' let highlighted if (info) { langName = info.split(/\s+/g)[0] if (langName === 'csvpreview') { const params = parseFenceCodeParams(info) return renderCSVPreview(token.content, params) } if (/!$/.test(info)) token.attrJoin('class', 'wrap') token.attrJoin('class', options.langPrefix + langName.replace(/=$|=\d+$|=\+$|!$|=!$/, '')) token.attrJoin('class', 'hljs') token.attrJoin('class', 'raw') } if (options.highlight) { highlighted = options.highlight(token.content, info) || md.utils.escapeHtml(token.content) } else { highlighted = md.utils.escapeHtml(token.content) } if (highlighted.indexOf('${highlighted}\n` } const makePlantumlURL = (umlCode) => { const format = 'svg' const code = plantumlEncoder.encode(umlCode) return `${plantumlServer}/${format}/${code}` } // https://github.com/qjebbs/vscode-plantuml/tree/master/src/markdown-it-plantuml md.renderer.rules.plantuml = (tokens, idx) => { const token = tokens[idx] if (token.type !== 'plantuml') { return tokens[idx].content } const url = makePlantumlURL(token.content) return `` } // https://github.com/qjebbs/vscode-plantuml/tree/master/src/markdown-it-plantuml md.core.ruler.push('plantuml', (state) => { const blockTokens = state.tokens for (const blockToken of blockTokens) { if (blockToken.type === 'fence' && blockToken.info === 'plantuml') { blockToken.type = 'plantuml' } } }) // youtube const youtubePlugin = new Plugin( // regexp to match /{%youtube\s*([\d\D]*?)\s*%}/, (match, utils) => { const videoid = match[1] if (!videoid) return const div = $('
    ') div.attr('data-videoid', videoid) const thumbnailSrc = `//img.youtube.com/vi/${videoid}/hqdefault.jpg` const image = `` div.append(image) const icon = '' div.append(icon) return div[0].outerHTML } ) // vimeo const vimeoPlugin = new Plugin( // regexp to match /{%vimeo\s*([\d\D]*?)\s*%}/, (match, utils) => { const videoid = match[1].split(/[?&=]+/)[0] if (!videoid) return const div = $('
    ') div.attr('data-videoid', videoid) const icon = '' div.append(icon) return div[0].outerHTML } ) // gist const gistPlugin = new Plugin( // regexp to match /{%gist\s*([\d\D]*?)\s*%}/, (match, utils) => { const gistid = match[1].split(/[?&=]+/)[0] const code = `` return code } ) // TOC const tocPlugin = new Plugin( // regexp to match /^\[TOC(|\s*maxLevel=\d+?)\]$/i, (match, utils) => { const tocDepth = match[1].split(/[?&=]+/)[1] return `
    ` } ) // slideshare const slidesharePlugin = new Plugin( // regexp to match /{%slideshare\s*([\d\D]*?)\s*%}/, (match, utils) => { const slideshareid = match[1].split(/[?&=]+/)[0] const div = $('
    ') div.attr('data-slideshareid', slideshareid) return div[0].outerHTML } ) // speakerdeck const speakerdeckPlugin = new Plugin( // regexp to match /{%speakerdeck\s*([\d\D]*?)\s*%}/, (match, utils) => { const speakerdeckid = match[1] const div = $('
    ') div.attr('data-speakerdeckid', speakerdeckid) return div[0].outerHTML } ) // pdf const pdfPlugin = new Plugin( // regexp to match /{%pdf\s*([\d\D]*?)\s*%}/, (match, utils) => { const pdfurl = match[1] if (!isURL(pdfurl)) return match[0] const div = $('
    ') div.attr('data-pdfurl', pdfurl) return div[0].outerHTML } ) const emojijsPlugin = new Plugin( // regexp to match emoji shortcodes :something: // We generate an universal regex that guaranteed only contains the // emojies we have available. This should prevent all false-positives new RegExp(':(' + window.emojify.emojiNames.map((item) => { return RegExp.escape(item) }).join('|') + '):', 'i'), (match, utils) => { const emoji = match[1].toLowerCase() const div = $(`:${emoji}:`) return div[0].outerHTML } ) // yaml meta, from https://github.com/eugeneware/remarkable-meta function get (state, line) { const pos = state.bMarks[line] const max = state.eMarks[line] return state.src.substr(pos, max - pos) } function meta (state, start, end, silent) { if (start !== 0 || state.blkIndent !== 0) return false if (state.tShift[start] < 0) return false if (!get(state, start).match(/^---$/)) return false const data = [] for (var line = start + 1; line < end; line++) { const str = get(state, line) if (str.match(/^(\.{3}|-{3})$/)) break if (state.tShift[line] < 0) break data.push(str) } if (line >= end) return false try { md.meta = window.jsyaml.safeLoad(data.join('\n')) || {} delete md.metaError } catch (err) { md.metaError = err console.warn(err) return false } state.line = line + 1 return true } function metaPlugin (md) { md.meta = md.meta || {} md.block.ruler.before('code', 'meta', meta, { alt: [] }) } md.use(metaPlugin) md.use(emojijsPlugin) md.use(youtubePlugin) md.use(vimeoPlugin) md.use(gistPlugin) md.use(tocPlugin) md.use(slidesharePlugin) md.use(speakerdeckPlugin) md.use(pdfPlugin) export default { md } // Add helper to sanitize markmap nodes against XSS using the global preventXSS function sanitizeMarkmapNode (node) { if (!node || typeof node !== 'object') return if (typeof node.content === 'string') { try { node.content = window.preventXSS(node.content) } catch (e) { // fallback: strip potentially dangerous characters node.content = node.content.replace(/[<>]/g, '') } } // remove dangerous href like javascript: if (node.payload && typeof node.payload === 'object' && typeof node.payload.href === 'string') { if (/^\s*javascript:/i.test(node.payload.href)) { delete node.payload.href } } if (Array.isArray(node.children)) { node.children.forEach(sanitizeMarkmapNode) } } ================================================ FILE: public/js/history.js ================================================ /* eslint-env browser, jquery */ /* global serverurl, moment */ import store from 'store' import LZString from '@hackmd/lz-string' import escapeHTML from 'lodash/escape' import { checkNoteIdValid, encodeNoteId } from './utils' import { checkIfAuth } from './lib/common/login' import { urlpath } from './lib/config' window.migrateHistoryFromTempCallback = null export function saveHistory (notehistory) { checkIfAuth( () => { saveHistoryToServer(notehistory) }, () => { saveHistoryToStorage(notehistory) } ) } function saveHistoryToStorage (notehistory) { store.set('notehistory', JSON.stringify(notehistory)) } function saveHistoryToServer (notehistory) { $.post(`${serverurl}/history`, { history: JSON.stringify(notehistory) }) } export function saveStorageHistoryToServer (callback) { const data = store.get('notehistory') if (data) { $.post(`${serverurl}/history`, { history: data }) .done(data => { callback(data) }) } } export function clearDuplicatedHistory (notehistory) { const newnotehistory = [] for (let i = 0; i < notehistory.length; i++) { let found = false for (let j = 0; j < newnotehistory.length; j++) { const id = notehistory[i].id.replace(/=+$/, '') const newId = newnotehistory[j].id.replace(/=+$/, '') if (id === newId || notehistory[i].id === newnotehistory[j].id || !notehistory[i].id || !newnotehistory[j].id) { const time = (typeof notehistory[i].time === 'number' ? moment(notehistory[i].time) : moment(notehistory[i].time, 'MMMM Do YYYY, h:mm:ss a')) const newTime = (typeof newnotehistory[j].time === 'number' ? moment(newnotehistory[j].time) : moment(newnotehistory[j].time, 'MMMM Do YYYY, h:mm:ss a')) if (time >= newTime) { newnotehistory[j] = notehistory[i] } found = true break } } if (!found) { newnotehistory.push(notehistory[i]) } } return newnotehistory } function addHistory (id, text, time, tags, pinned, notehistory) { // only add when note id exists if (id) { notehistory.push({ id, text, time, tags, pinned }) } return notehistory } export function removeHistory (id, notehistory) { for (let i = 0; i < notehistory.length; i++) { if (notehistory[i].id === id) { notehistory.splice(i, 1) i -= 1 } } return notehistory } // used for inner export function writeHistory (title, tags) { checkIfAuth( () => { // no need to do this anymore, this will count from server-side // writeHistoryToServer(title, tags); }, () => { writeHistoryToStorage(title, tags) } ) } function writeHistoryToStorage (title, tags) { const data = store.get('notehistory') let notehistory if (data && typeof data === 'string') { notehistory = JSON.parse(data) } else { notehistory = [] } const newnotehistory = generateHistory(title, tags, notehistory) saveHistoryToStorage(newnotehistory) } if (!Array.isArray) { Array.isArray = arg => Object.prototype.toString.call(arg) === '[object Array]' } function renderHistory (title, tags) { // console.debug(tags); const id = urlpath ? location.pathname.slice(urlpath.length + 1, location.pathname.length).split('/')[1] : location.pathname.split('/')[1] return { id, text: title, time: moment().valueOf(), tags } } function generateHistory (title, tags, notehistory) { const info = renderHistory(title, tags) // keep any pinned data let pinned = false for (let i = 0; i < notehistory.length; i++) { if (notehistory[i].id === info.id && notehistory[i].pinned) { pinned = true break } } notehistory = removeHistory(info.id, notehistory) notehistory = addHistory(info.id, info.text, info.time, info.tags, pinned, notehistory) notehistory = clearDuplicatedHistory(notehistory) return notehistory } // used for outer export function getHistory (callback) { checkIfAuth( () => { getServerHistory(callback) }, () => { getStorageHistory(callback) } ) } function getServerHistory (callback) { $.get(`${serverurl}/history`) .done(data => { if (data.history) { callback(data.history) } }) .fail((xhr, status, error) => { console.error(xhr.responseText) }) } export function getStorageHistory (callback) { let data = store.get('notehistory') if (data) { if (typeof data === 'string') { data = JSON.parse(data) } return callback(data) } // eslint-disable-next-line standard/no-callback-literal callback([]) } export function parseHistory (list, callback) { checkIfAuth( () => { parseServerToHistory(list, callback) }, () => { parseStorageToHistory(list, callback) } ) } export function parseServerToHistory (list, callback) { $.get(`${serverurl}/history`) .done(data => { if (data.history) { parseToHistory(list, data.history, callback) } }) .fail((xhr, status, error) => { console.error(xhr.responseText) }) } export function parseStorageToHistory (list, callback) { let data = store.get('notehistory') if (data) { if (typeof data === 'string') { data = JSON.parse(data) } parseToHistory(list, data, callback) } parseToHistory(list, [], callback) } function parseToHistory (list, notehistory, callback) { if (!callback) return else if (!list || !notehistory) callback(list, notehistory) else if (notehistory && notehistory.length > 0) { for (let i = 0; i < notehistory.length; i++) { // migrate LZString encoded id to base64url encoded id try { const id = LZString.decompressFromBase64(notehistory[i].id) if (id && checkNoteIdValid(id)) { notehistory[i].id = encodeNoteId(id) } } catch (err) { console.error(err) } // parse time to timestamp and fromNow const timestamp = (typeof notehistory[i].time === 'number' ? moment(notehistory[i].time) : moment(notehistory[i].time, 'MMMM Do YYYY, h:mm:ss a')) notehistory[i].timestamp = timestamp.valueOf() notehistory[i].fromNow = timestamp.fromNow() notehistory[i].time = timestamp.format('llll') // prevent XSS notehistory[i].text = escapeHTML(notehistory[i].text) notehistory[i].tags = (notehistory[i].tags && notehistory[i].tags.length > 0) ? escapeHTML(notehistory[i].tags).split(',') : [] // add to list if (notehistory[i].id && list.get('id', notehistory[i].id).length === 0) { list.add(notehistory[i]) } } } callback(list, notehistory) } export function postHistoryToServer (noteId, data, callback) { $.post(`${serverurl}/history/${noteId}`, data) .done(result => callback(null, result)) .fail((xhr, status, error) => { console.error(xhr.responseText) return callback(error, null) }) } export function deleteServerHistory (noteId, callback) { $.ajax({ url: `${serverurl}/history${noteId ? '/' + noteId : ''}`, type: 'DELETE' }) .done(result => callback(null, result)) .fail((xhr, status, error) => { console.error(xhr.responseText) return callback(error, null) }) } ================================================ FILE: public/js/htmlExport.js ================================================ require('../css/github-extract.css') require('../css/markdown.css') require('../css/extra.css') require('../css/slide-preview.css') require('../css/google-font.css') require('../css/site.css') ================================================ FILE: public/js/index.js ================================================ /* eslint-env browser, jquery */ /* global CodeMirror, Cookies, moment, serverurl, key, Dropbox, ot, hex2rgb, Visibility, inlineAttachment */ import TurndownService from 'turndown' import { saveAs } from 'file-saver' import randomColor from 'randomcolor' import store from 'store' import hljs from 'highlight.js' import isURL from 'validator/lib/isURL' import _ from 'lodash' import wurl from 'wurl' import List from 'list.js' import Idle from '@hackmd/idle-js' import { Spinner } from 'spin.js' import { checkLoginStateChanged, setloginStateChangeEvent } from './lib/common/login' import { debug, DROPBOX_APP_KEY, noteid, noteurl, urlpath, version } from './lib/config' import { autoLinkify, deduplicatedHeaderId, exportToHTML, exportToRawHTML, removeDOMEvents, finishView, generateToc, md, parseMeta, postProcess, renderFilename, renderTOC, renderTags, renderTitle, scrollToHash, smoothHashScroll, updateLastChange, updateLastChangeUser, updateOwner } from './extra' import { clearMap, setupSyncAreas, syncScrollToEdit, syncScrollToView } from './lib/syncscroll' import { writeHistory, deleteServerHistory, getHistory, saveHistory, removeHistory } from './history' import { preventXSS } from './render' import Editor from './lib/editor' import getUIElements from './lib/editor/ui-elements' import { emojifyImageDir } from './lib/editor/constants' import modeType from './lib/modeType' import appState from './lib/appState' require('../vendor/showup/showup') require('../css/index.css') require('../css/extra.css') require('../css/slide-preview.css') require('../css/site.css') require('spin.js/spin.css') require('highlight.js/styles/github-gist.css') var defaultTextHeight = 20 var viewportMargin = 20 var defaultEditorMode = 'gfm' var idleTime = 300000 // 5 mins var updateViewDebounce = 100 var cursorMenuThrottle = 50 var cursorActivityDebounce = 50 var cursorAnimatePeriod = 100 var supportContainers = ['success', 'info', 'warning', 'danger', 'spoiler'] var supportCodeModes = ['javascript', 'typescript', 'jsx', 'htmlmixed', 'htmlembedded', 'css', 'xml', 'clike', 'clojure', 'ruby', 'python', 'shell', 'php', 'sql', 'haskell', 'coffeescript', 'yaml', 'pug', 'lua', 'cmake', 'nginx', 'perl', 'sass', 'r', 'dockerfile', 'tiddlywiki', 'mediawiki', 'go', 'gherkin'].concat(hljs.listLanguages()) var supportCharts = ['sequence', 'flow', 'graphviz', 'mermaid', 'abc', 'plantuml', 'vega', 'geo', 'fretboard', 'markmap'] var supportHeaders = [ { text: '# h1', search: '#' }, { text: '## h2', search: '##' }, { text: '### h3', search: '###' }, { text: '#### h4', search: '####' }, { text: '##### h5', search: '#####' }, { text: '###### h6', search: '######' }, { text: '###### tags: `example`', search: '###### tags:' } ] const supportReferrals = [ { text: '[reference link]', search: '[]' }, { text: '[reference]: https:// "title"', search: '[]:' }, { text: '[^footnote link]', search: '[^]' }, { text: '[^footnote reference]: https:// "title"', search: '[^]:' }, { text: '^[inline footnote]', search: '^[]' }, { text: '[link text][reference]', search: '[][]' }, { text: '[link text](https:// "title")', search: '[]()' }, { text: '![image alt][reference]', search: '![][]' }, { text: '![image alt](https:// "title")', search: '![]()' }, { text: '![image alt](https:// "title" =WidthxHeight)', search: '![]()' }, { text: '[TOC]', search: '[]' } ] const supportExternals = [ { text: '{%youtube youtubeid %}', search: 'youtube' }, { text: '{%vimeo vimeoid %}', search: 'vimeo' }, { text: '{%gist gistid %}', search: 'gist' }, { text: '{%slideshare slideshareid %}', search: 'slideshare' }, { text: '{%speakerdeck speakerdeckid %}', search: 'speakerdeck' }, { text: '{%pdf pdfurl %}', search: 'pdf' } ] const supportExtraTags = [ { text: '[name tag]', search: '[]', command: function () { return '[name=' + personalInfo.name + ']' } }, { text: '[time tag]', search: '[]', command: function () { return '[time=' + moment().format('llll') + ']' } }, { text: '[my color tag]', search: '[]', command: function () { return '[color=' + personalInfo.color + ']' } }, { text: '[random color tag]', search: '[]', command: function () { var color = randomColor() return '[color=' + color + ']' } } ] const statusType = { connected: 1, online: 2, offline: 3 } // global vars window.loaded = false let needRefresh = false let isDirty = false let editShown = false let visibleXS = false let visibleSM = false let visibleMD = false let visibleLG = false const isTouchDevice = 'ontouchstart' in document.documentElement let currentStatus = statusType.offline const lastInfo = { needRestore: false, cursor: null, scroll: null, edit: { scroll: { left: null, top: null }, cursor: { line: null, ch: null }, selections: null }, view: { scroll: { left: null, top: null } }, history: null } let personalInfo = {} let onlineUsers = [] const fileTypes = { pl: 'perl', cgi: 'perl', js: 'javascript', php: 'php', sh: 'bash', rb: 'ruby', html: 'html', py: 'python' } // editor settings const textit = document.getElementById('textit') if (!textit) { throw new Error('There was no textit area!') } const editorInstance = new Editor() var editor = editorInstance.init(textit) // FIXME: global referncing in jquery-textcomplete patch window.editor = editor var inlineAttach = inlineAttachment.editors.codemirror4.attach(editor) defaultTextHeight = parseInt($('.CodeMirror').css('line-height')) // initalize ui reference const ui = getUIElements() // page actions var opts = { lines: 11, // The number of lines to draw length: 20, // The length of each line width: 2, // The line thickness radius: 30, // The radius of the inner circle corners: 0, // Corner roundness (0..1) rotate: 0, // The rotation offset direction: 1, // 1: clockwise, -1: counterclockwise color: '#000', // #rgb or #rrggbb or array of colors speed: 1.1, // Rounds per second trail: 60, // Afterglow percentage shadow: false, // Whether to render a shadow hwaccel: true, // Whether to use hardware acceleration className: 'spinner', // The CSS class to assign to the spinner zIndex: 2e9, // The z-index (defaults to 2000000000) top: '50%', // Top position relative to parent left: '50%' // Left position relative to parent } new Spinner(opts).spin(ui.spinner[0]) // idle var idle = new Idle({ onAway: function () { idle.isAway = true emitUserStatus() updateOnlineStatus() }, onAwayBack: function () { idle.isAway = false emitUserStatus() updateOnlineStatus() setHaveUnreadChanges(false) updateTitleReminder() }, awayTimeout: idleTime }) ui.area.codemirror.on('touchstart', function () { idle.onActive() }) var haveUnreadChanges = false function setHaveUnreadChanges (bool) { if (!window.loaded) return if (bool && (idle.isAway || Visibility.hidden())) { haveUnreadChanges = true } else if (!bool && !idle.isAway && !Visibility.hidden()) { haveUnreadChanges = false } } function updateTitleReminder () { if (!window.loaded) return if (haveUnreadChanges) { document.title = '• ' + renderTitle(ui.area.markdown) } else { document.title = renderTitle(ui.area.markdown) } } function setRefreshModal (status) { $('#refreshModal').modal('show') $('#refreshModal').find('.modal-body > div').hide() $('#refreshModal').find('.' + status).show() } function setNeedRefresh () { needRefresh = true editor.setOption('readOnly', true) socket.disconnect() showStatus(statusType.offline) } setloginStateChangeEvent(function () { setRefreshModal('user-state-changed') setNeedRefresh() }) // visibility var wasFocus = false Visibility.change(function (e, state) { var hidden = Visibility.hidden() if (hidden) { if (editorHasFocus()) { wasFocus = true editor.getInputField().blur() } } else { if (wasFocus) { if (!visibleXS) { editor.focus() editor.refresh() } wasFocus = false } setHaveUnreadChanges(false) } updateTitleReminder() }) // when page ready $(document).ready(function () { idle.checkAway() checkResponsive() // if in smaller screen, we don't need advanced scrollbar var scrollbarStyle if (visibleXS) { scrollbarStyle = 'native' } else { scrollbarStyle = 'overlay' } if (scrollbarStyle !== editor.getOption('scrollbarStyle')) { editor.setOption('scrollbarStyle', scrollbarStyle) clearMap() } checkEditorStyle() /* cache dom references */ var $body = $('body') /* we need this only on touch devices */ if (isTouchDevice) { /* bind events */ $(document) .on('focus', 'textarea, input', function () { $body.addClass('fixfixed') }) .on('blur', 'textarea, input', function () { $body.removeClass('fixfixed') }) } // Re-enable nightmode if (store.get('nightMode') || Cookies.get('nightMode')) { $body.addClass('night') ui.toolbar.night.addClass('active') } // showup $().showUp('.navbar', { upClass: 'navbar-hide', downClass: 'navbar-show' }) // tooltip $('[data-toggle="tooltip"]').tooltip() // shortcuts // allow on all tags key.filter = function (e) { return true } key('ctrl+alt+e', function (e) { changeMode(modeType.edit) }) key('ctrl+alt+v', function (e) { changeMode(modeType.view) }) key('ctrl+alt+b', function (e) { changeMode(modeType.both) }) // toggle-dropdown $(document).on('click', '.toggle-dropdown .dropdown-menu', function (e) { e.stopPropagation() }) }) // when page resize $(window).resize(function () { checkLayout() checkEditorStyle() checkTocStyle() checkCursorMenu() windowResize() }) // when page unload $(window).on('unload', function () { // updateHistoryInner(); }) $(window).on('error', function () { // setNeedRefresh(); }) setupSyncAreas(ui.area.codemirrorScroll, ui.area.view, ui.area.markdown, editor) function autoSyncscroll () { if (editorHasFocus()) { syncScrollToView() } else { syncScrollToEdit() } } var windowResizeDebounce = 200 var windowResize = _.debounce(windowResizeInner, windowResizeDebounce) function windowResizeInner (callback) { checkLayout() checkResponsive() checkEditorStyle() checkTocStyle() checkCursorMenu() // refresh editor if (window.loaded) { if (editor.getOption('scrollbarStyle') === 'native') { setTimeout(function () { clearMap() autoSyncscroll() updateScrollspy() if (callback && typeof callback === 'function') { callback() } }, 1) } else { // force it load all docs at once to prevent scroll knob blink editor.setOption('viewportMargin', Infinity) setTimeout(function () { clearMap() autoSyncscroll() editor.setOption('viewportMargin', viewportMargin) // add or update user cursors for (var i = 0; i < onlineUsers.length; i++) { if (onlineUsers[i].id !== personalInfo.id) { buildCursor(onlineUsers[i]) } } updateScrollspy() if (callback && typeof callback === 'function') { callback() } }, 1) } } } function checkLayout () { var navbarHieght = $('.navbar').outerHeight() $('body').css('padding-top', navbarHieght + 'px') } function editorHasFocus () { return $(editor.getInputField()).is(':focus') } // 768-792px have a gap function checkResponsive () { visibleXS = $('.visible-xs').is(':visible') visibleSM = $('.visible-sm').is(':visible') visibleMD = $('.visible-md').is(':visible') visibleLG = $('.visible-lg').is(':visible') if (visibleXS && appState.currentMode === modeType.both) { if (editorHasFocus()) { changeMode(modeType.edit) } else { changeMode(modeType.view) } } emitUserStatus() } var lastEditorWidth = 0 var previousFocusOnEditor = null function checkEditorStyle () { var desireHeight = editorInstance.statusBar ? (ui.area.edit.height() - editorInstance.statusBar.outerHeight()) : ui.area.edit.height() if (editorInstance.toolBar) { desireHeight = desireHeight - editorInstance.toolBar.outerHeight() } // set editor height and min height based on scrollbar style and mode var scrollbarStyle = editor.getOption('scrollbarStyle') if (scrollbarStyle === 'overlay' || appState.currentMode === modeType.both) { ui.area.codemirrorScroll.css('height', desireHeight + 'px') ui.area.codemirrorScroll.css('min-height', '') checkEditorScrollbar() } else if (scrollbarStyle === 'native') { ui.area.codemirrorScroll.css('height', '') ui.area.codemirrorScroll.css('min-height', desireHeight + 'px') } // workaround editor will have wrong doc height when editor height changed editor.setSize(null, ui.area.edit.height()) checkEditorScrollOverLines() // make editor resizable if (!ui.area.resize.handle.length) { ui.area.edit.resizable({ handles: 'e', maxWidth: $(window).width() * 0.7, minWidth: $(window).width() * 0.2, create: function (e, ui) { $(this).parent().on('resize', function (e) { e.stopPropagation() }) }, start: function (e) { editor.setOption('viewportMargin', Infinity) }, resize: function (e) { ui.area.resize.syncToggle.stop(true, true).show() checkTocStyle() }, stop: function (e) { lastEditorWidth = ui.area.edit.width() // workaround that scroll event bindings window.preventSyncScrollToView = 2 window.preventSyncScrollToEdit = true editor.setOption('viewportMargin', viewportMargin) if (editorHasFocus()) { windowResizeInner(function () { ui.area.codemirrorScroll.scroll() }) } else { windowResizeInner(function () { ui.area.view.scroll() }) } checkEditorScrollbar() } }) ui.area.resize.handle = $('.ui-resizable-handle') } if (!ui.area.resize.syncToggle.length) { ui.area.resize.syncToggle = $('') ui.area.resize.syncToggle.hover(function () { previousFocusOnEditor = editorHasFocus() }, function () { previousFocusOnEditor = null }) ui.area.resize.syncToggle.click(function () { appState.syncscroll = !appState.syncscroll checkSyncToggle() }) ui.area.resize.handle.append(ui.area.resize.syncToggle) ui.area.resize.syncToggle.hide() ui.area.resize.handle.hover(function () { ui.area.resize.syncToggle.stop(true, true).delay(200).fadeIn(100) }, function () { ui.area.resize.syncToggle.stop(true, true).delay(300).fadeOut(300) }) } } function checkSyncToggle () { if (appState.syncscroll) { if (previousFocusOnEditor) { window.preventSyncScrollToView = false syncScrollToView() } else { window.preventSyncScrollToEdit = false syncScrollToEdit() } ui.area.resize.syncToggle.find('i').removeClass('fa-unlink').addClass('fa-link') } else { ui.area.resize.syncToggle.find('i').removeClass('fa-link').addClass('fa-unlink') } } var checkEditorScrollbar = _.debounce(function () { editor.operation(checkEditorScrollbarInner) }, 50) function checkEditorScrollbarInner () { // workaround simple scroll bar knob // will get wrong position when editor height changed var scrollInfo = editor.getScrollInfo() editor.scrollTo(null, scrollInfo.top - 1) editor.scrollTo(null, scrollInfo.top) } function checkEditorScrollOverLines () { const desireHeight = parseInt(ui.area.codemirrorScroll[0].style.height) || parseInt(ui.area.codemirrorScroll[0].style.minHeight) // make editor have extra padding in the bottom (except small screen) const paddingBottom = editor.doc && editor.doc.height > defaultTextHeight ? (desireHeight - defaultTextHeight) : 0 if (parseInt(ui.area.codemirrorLines.css('padding-bottom')) !== paddingBottom) { ui.area.codemirrorLines.css('padding-bottom', paddingBottom + 'px') } } function checkTocStyle () { // toc right var paddingRight = parseFloat(ui.area.markdown.css('padding-right')) var right = ($(window).width() - (ui.area.markdown.offset().left + ui.area.markdown.outerWidth() - paddingRight)) ui.toc.toc.css('right', right + 'px') // affix toc left var newbool var rightMargin = (ui.area.markdown.parent().outerWidth() - ui.area.markdown.outerWidth()) / 2 // for ipad or wider device if (rightMargin >= 133) { newbool = true var affixLeftMargin = (ui.toc.affix.outerWidth() - ui.toc.affix.width()) / 2 var left = ui.area.markdown.offset().left + ui.area.markdown.outerWidth() - affixLeftMargin ui.toc.affix.css('left', left + 'px') ui.toc.affix.css('width', rightMargin + 'px') } else { newbool = false } // toc scrollspy ui.toc.toc.removeClass('scrollspy-body, scrollspy-view') ui.toc.affix.removeClass('scrollspy-body, scrollspy-view') if (appState.currentMode === modeType.both) { ui.toc.toc.addClass('scrollspy-view') ui.toc.affix.addClass('scrollspy-view') } else if (appState.currentMode !== modeType.both && !newbool) { ui.toc.toc.addClass('scrollspy-body') ui.toc.affix.addClass('scrollspy-body') } else { ui.toc.toc.addClass('scrollspy-view') ui.toc.affix.addClass('scrollspy-body') } if (newbool !== enoughForAffixToc) { enoughForAffixToc = newbool generateScrollspy() } } function showStatus (type, num) { currentStatus = type ui.toolbar.statusConnected.hide() ui.toolbar.statusOnline.hide() ui.toolbar.statusOffline.hide() switch (currentStatus) { case statusType.connected: ui.toolbar.statusConnected.show() break case statusType.online: ui.toolbar.statusShortMsg.text(num) ui.toolbar.statusOnline.show() break case statusType.offline: ui.toolbar.statusOffline.show() break } } function toggleMode () { switch (appState.currentMode) { case modeType.edit: changeMode(modeType.view) break case modeType.view: changeMode(modeType.edit) break case modeType.both: changeMode(modeType.view) break } } var lastMode = null function changeMode (type) { // lock navbar to prevent it hide after changeMode lockNavbar() saveInfo() if (type) { lastMode = appState.currentMode appState.currentMode = type } var responsiveClass = 'col-lg-6 col-md-6 col-sm-6' var scrollClass = 'ui-scrollable' ui.area.codemirror.removeClass(scrollClass) ui.area.edit.removeClass(responsiveClass) ui.area.view.removeClass(scrollClass) ui.area.view.removeClass(responsiveClass) switch (appState.currentMode) { case modeType.edit: ui.area.edit.show() ui.area.view.hide() if (!editShown) { editor.refresh() editShown = true } break case modeType.view: ui.area.edit.hide() ui.area.view.show() break case modeType.both: ui.area.codemirror.addClass(scrollClass) ui.area.edit.addClass(responsiveClass).show() ui.area.view.addClass(scrollClass) ui.area.view.show() break } // save mode to url if (history.replaceState && window.loaded) history.replaceState(null, '', serverurl + '/' + noteid + '?' + appState.currentMode.name) if (appState.currentMode === modeType.view) { editor.getInputField().blur() } if (appState.currentMode === modeType.edit || appState.currentMode === modeType.both) { ui.toolbar.uploadImage.fadeIn() // add and update status bar if (!editorInstance.statusBar) { editorInstance.addStatusBar() editorInstance.updateStatusBar() } // add and update tool bar if (!editorInstance.toolBar) { editorInstance.addToolBar() } // work around foldGutter might not init properly editor.setOption('foldGutter', false) editor.setOption('foldGutter', true) } else { ui.toolbar.uploadImage.fadeOut() } if (appState.currentMode !== modeType.edit) { $(document.body).css('background-color', 'white') updateView() } else { $(document.body).css('background-color', ui.area.codemirror.css('background-color')) } // check resizable editor style if (appState.currentMode === modeType.both) { if (lastEditorWidth > 0) { ui.area.edit.css('width', lastEditorWidth + 'px') } else { ui.area.edit.css('width', '') } ui.area.resize.handle.show() } else { ui.area.edit.css('width', '') ui.area.resize.handle.hide() } windowResizeInner() restoreInfo() if (lastMode === modeType.view && appState.currentMode === modeType.both) { window.preventSyncScrollToView = 2 syncScrollToEdit(null, true) } if (lastMode === modeType.edit && appState.currentMode === modeType.both) { window.preventSyncScrollToEdit = 2 syncScrollToView(null, true) } if (lastMode === modeType.both && appState.currentMode !== modeType.both) { window.preventSyncScrollToView = false window.preventSyncScrollToEdit = false } if (lastMode !== modeType.edit && appState.currentMode === modeType.edit) { editor.refresh() } $(document.body).scrollspy('refresh') ui.area.view.scrollspy('refresh') ui.toolbar.both.removeClass('active') ui.toolbar.edit.removeClass('active') ui.toolbar.view.removeClass('active') var modeIcon = ui.toolbar.mode.find('i') modeIcon.removeClass('fa-pencil').removeClass('fa-eye') if (ui.area.edit.is(':visible') && ui.area.view.is(':visible')) { // both ui.toolbar.both.addClass('active') modeIcon.addClass('fa-eye') } else if (ui.area.edit.is(':visible')) { // edit ui.toolbar.edit.addClass('active') modeIcon.addClass('fa-eye') } else if (ui.area.view.is(':visible')) { // view ui.toolbar.view.addClass('active') modeIcon.addClass('fa-pencil') } unlockNavbar() } function lockNavbar () { $('.navbar').addClass('locked') } var unlockNavbar = _.debounce(function () { $('.navbar').removeClass('locked') }, 200) function showMessageModal (title, header, href, text, success) { var modal = $('.message-modal') modal.find('.modal-title').html(title) modal.find('.modal-body h5').html(header) if (href) { modal.find('.modal-body a').attr('href', href).text(text) } else { modal.find('.modal-body a').removeAttr('href').text(text) } modal.find('.modal-footer button').removeClass('btn-default btn-success btn-danger') if (success) { modal.find('.modal-footer button').addClass('btn-success') } else { modal.find('.modal-footer button').addClass('btn-danger') } modal.modal('show') } // check if dropbox app key is set and load scripts if (DROPBOX_APP_KEY) { $('') var leadingWs = text.match(/^\n?(\s*)/)[1].length var leadingTabs = text.match(/^\n?(\t*)/)[1].length if (leadingTabs > 0) { text = text.replace(new RegExp('\\n?\\t{' + leadingTabs + '}', 'g'), '\n') } else if (leadingWs > 1) { text = text.replace(new RegExp('\\n? {' + leadingWs + '}', 'g'), '\n') } return text } /** * Given a markdown slide section element, this will * return all arguments that aren't related to markdown * parsing. Used to forward any other user-defined arguments * to the output markdown slide. */ function getForwardedAttributes (section) { var attributes = section.attributes var result = [] for (var i = 0, len = attributes.length; i < len; i++) { var name = attributes[i].name var value = attributes[i].value // disregard attributes that are used for markdown loading/parsing if (/data-(markdown|separator|vertical|notes)/gi.test(name)) continue if (value) { result.push(name + '="' + value + '"') } else { result.push(name) } } return result.join(' ') } /** * Inspects the given options and fills out default * values for what's not defined. */ function getSlidifyOptions (options) { options = options || {} options.separator = options.separator || DEFAULT_SLIDE_SEPARATOR options.notesSeparator = options.notesSeparator || DEFAULT_NOTES_SEPARATOR options.attributes = options.attributes || '' return options } /** * Helper function for constructing a markdown slide. */ function createMarkdownSlide (content, options) { options = getSlidifyOptions(options) var notesMatch = content.split(new RegExp(options.notesSeparator, 'mgi')) if (notesMatch.length === 2) { content = notesMatch[0] + '' } // prevent script end tags in the content from interfering // with parsing content = content.replace(/<\/script>/gi, SCRIPT_END_PLACEHOLDER) return '' } /** * Parses a data string into multiple slides based * on the passed in separator arguments. */ function slidify (markdown, options) { options = getSlidifyOptions(options) var separatorRegex = new RegExp(options.separator + (options.verticalSeparator ? '|' + options.verticalSeparator : ''), 'mg') var horizontalSeparatorRegex = new RegExp(options.separator) var matches var lastIndex = 0 var isHorizontal var wasHorizontal = true var content var sectionStack = [] // iterate until all blocks between separators are stacked up while ((matches = separatorRegex.exec(markdown)) !== null) { // determine direction (horizontal by default) isHorizontal = horizontalSeparatorRegex.test(matches[0]) if (!isHorizontal && wasHorizontal) { // create vertical stack sectionStack.push([]) } // pluck slide content from markdown input content = markdown.substring(lastIndex, matches.index) if (isHorizontal && wasHorizontal) { // add to horizontal stack sectionStack.push(content) } else { // add to vertical stack sectionStack[sectionStack.length - 1].push(content) } lastIndex = separatorRegex.lastIndex wasHorizontal = isHorizontal } // add the remaining slide (wasHorizontal ? sectionStack : sectionStack[sectionStack.length - 1]).push(markdown.substring(lastIndex)) var markdownSections = '' // flatten the hierarchical stack, and insert
    tags for (var i = 0, len = sectionStack.length; i < len; i++) { // vertical if (sectionStack[i] instanceof Array) { markdownSections += '
    ' sectionStack[i].forEach(function (child) { markdownSections += '
    ' + createMarkdownSlide(child, options) + '
    ' }) markdownSections += '
    ' } else { markdownSections += '
    ' + createMarkdownSlide(sectionStack[i], options) + '
    ' } } return markdownSections } /** * Parses any current data-markdown slides, splits * multi-slide markdown into separate sections and * handles loading of external markdown. */ function processSlides () { var sections = document.querySelectorAll('[data-markdown]') var section for (var i = 0, len = sections.length; i < len; i++) { section = sections[i] if (section.getAttribute('data-markdown').length) { var xhr = new XMLHttpRequest() var url = section.getAttribute('data-markdown') var datacharset = section.getAttribute('data-charset') // see https://developer.mozilla.org/en-US/docs/Web/API/element.getAttribute#Notes if (datacharset !== null && datacharset !== '') { xhr.overrideMimeType('text/html; charset=' + datacharset) } xhr.onreadystatechange = function () { if (xhr.readyState === 4) { // file protocol yields status code 0 (useful for local debug, mobile applications etc.) if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 0) { section.outerHTML = slidify(xhr.responseText, { separator: section.getAttribute('data-separator'), verticalSeparator: section.getAttribute('data-separator-vertical'), notesSeparator: section.getAttribute('data-separator-notes'), attributes: getForwardedAttributes(section) }) } else { section.outerHTML = '
    ' + 'ERROR: The attempt to fetch ' + url + ' failed with HTTP status ' + xhr.status + '.' + 'Check your browser\'s JavaScript console for more details.' + '

    Remember that you need to serve the presentation HTML from a HTTP server.

    ' + '
    ' } } } xhr.open('GET', url, false) try { xhr.send() } catch (e) { alert('Failed to get the Markdown file ' + url + '. Make sure that the presentation and the file are served by a HTTP server and the file can be found there. ' + e) } } else if (section.getAttribute('data-separator') || section.getAttribute('data-separator-vertical') || section.getAttribute('data-separator-notes')) { section.outerHTML = slidify(getMarkdownFromSlide(section), { separator: section.getAttribute('data-separator'), verticalSeparator: section.getAttribute('data-separator-vertical'), notesSeparator: section.getAttribute('data-separator-notes'), attributes: getForwardedAttributes(section) }) } else { section.innerHTML = createMarkdownSlide(getMarkdownFromSlide(section)) } } } /** * Check if a node value has the attributes pattern. * If yes, extract it and add that value as one or several attributes * the the terget element. * * You need Cache Killer on Chrome to see the effect on any FOM transformation * directly on refresh (F5) * http://stackoverflow.com/questions/5690269/disabling-chrome-cache-for-website-development/7000899#answer-11786277 */ function addAttributeInElement (node, elementTarget, separator) { var mardownClassesInElementsRegex = new RegExp(separator, 'mg') var mardownClassRegex = new RegExp('([^"= ]+?)="([^"=]+?)"', 'mg') var nodeValue = node.nodeValue var matches var matchesClass if ((matches = mardownClassesInElementsRegex.exec(nodeValue))) { var classes = matches[1] nodeValue = nodeValue.substring(0, matches.index) + nodeValue.substring(mardownClassesInElementsRegex.lastIndex) node.nodeValue = nodeValue while ((matchesClass = mardownClassRegex.exec(classes))) { var name = matchesClass[1] var value = matchesClass[2] if (name.substr(0, 5) === 'data-' || window.whiteListAttr.indexOf(name) !== -1) { elementTarget.setAttribute(name, escapeAttrValue(value)) } } return true } return false } /** * Add attributes to the parent element of a text node, * or the element of an attribute node. */ function addAttributes (section, element, previousElement, separatorElementAttributes, separatorSectionAttributes) { if (element != null && element.childNodes !== undefined && element.childNodes.length > 0) { var previousParentElement = element for (var i = 0; i < element.childNodes.length; i++) { var childElement = element.childNodes[i] if (i > 0) { let j = i - 1 while (j >= 0) { var aPreviousChildElement = element.childNodes[j] if (typeof aPreviousChildElement.setAttribute === 'function' && aPreviousChildElement.tagName !== 'BR') { previousParentElement = aPreviousChildElement break } j = j - 1 } } var parentSection = section if (childElement.nodeName === 'section') { parentSection = childElement previousParentElement = childElement } if (typeof childElement.setAttribute === 'function' || childElement.nodeType === Node.COMMENT_NODE) { addAttributes(parentSection, childElement, previousParentElement, separatorElementAttributes, separatorSectionAttributes) } } } if (element.nodeType === Node.COMMENT_NODE) { if (addAttributeInElement(element, previousElement, separatorElementAttributes) === false) { addAttributeInElement(element, section, separatorSectionAttributes) } } } /** * Converts any current data-markdown slides in the * DOM to HTML. */ function convertSlides () { var sections = document.querySelectorAll('[data-markdown]') for (var i = 0, len = sections.length; i < len; i++) { var section = sections[i] // Only parse the same slide once if (!section.getAttribute('data-markdown-parsed')) { section.setAttribute('data-markdown-parsed', true) var notes = section.querySelector('aside.notes') var markdown = getMarkdownFromSlide(section) markdown = markdown.replace(/</g, '<').replace(/>/g, '>') var rendered = md.render(markdown) rendered = preventXSS(rendered) var result = window.postProcess(rendered) section.innerHTML = result[0].outerHTML addAttributes(section, section, null, section.getAttribute('data-element-attributes') || section.parentNode.getAttribute('data-element-attributes') || DEFAULT_ELEMENT_ATTRIBUTES_SEPARATOR, section.getAttribute('data-attributes') || section.parentNode.getAttribute('data-attributes') || DEFAULT_SLIDE_ATTRIBUTES_SEPARATOR) // If there were notes, we need to re-add them after // having overwritten the section's HTML if (notes) { section.appendChild(notes) } } } } // API return { initialize: function () { processSlides() convertSlides() }, // TODO: Do these belong in the API? processSlides: processSlides, convertSlides: convertSlides, slidify: slidify } })) // ES Module export for modern imports const RevealMarkdownAPI = (function () { if (typeof window !== 'undefined' && window.RevealMarkdown) { return window.RevealMarkdown } // Fallback - shouldn't happen in normal usage return {} })() export default RevealMarkdownAPI ================================================ FILE: public/js/revealjs-plugins/elapsed-time-bar/elapsed-time-bar.js ================================================ /* eslint-disable */ var ElapsedTimeBar = { // default value barColor: 'rgb(200,0,0)', pausedBarColor: 'rgba(200,0,0,.6)', isPaused: false, isFinished: false, allottedTime: null, timeProgressBar: null, startTime: null, pauseTime: null, pauseTimeDuration: 0, /** * initialize elements */ handleReady() { var config = Reveal.getConfig(); // activate this plugin if config.allottedTime exists. if (!config.allottedTime) { console.warn('Failed to start ElapsedTimeBar plugin. "allottedTime" property is required.'); return; } // set configurations this.barColor = config.barColor || this.barColor; this.pausedBarColor = config.pausedBarColor || this.pausedBarColor; // calc barHeight from config.barHeight or page-progress container var barHeight; var pageProgressContainer = document.querySelector('.progress'); if (config.progressBarHeight) { barHeight = parseInt(config.progressBarHeight, 10) + 'px'; // override height of page-progress container pageProgressContainer && (pageProgressContainer.style.height = barHeight); } else if (config.progress && pageProgressContainer) { // get height from page-progress container barHeight = pageProgressContainer.getBoundingClientRect().height + 'px'; } else { // default barHeight = '3px'; } // create container of time-progress var timeProgressContainer = document.createElement('div'); timeProgressContainer.classList.add('progress'); Object.entries({ display: 'block', position: 'fixed', bottom: config.progress ? barHeight : 0, width: '100%', height: barHeight }).forEach(([k, v]) => { timeProgressContainer.style[k] = v; }); document.querySelector('.reveal').appendChild(timeProgressContainer); // create content of time-progress this.timeProgressBar = document.createElement('div'); Object.entries({ height: '100%', willChange: 'width' }).forEach(([k, v]) => { this.timeProgressBar.style[k] = v; }); timeProgressContainer.appendChild(this.timeProgressBar); // start timer this.start(config.allottedTime); }, /** * update repeatedly using requestAnimationFrame. */ loop() { if (this.isPaused) return; var now = +new Date(); var elapsedTime = now - this.startTime - this.pauseTimeDuration; if (elapsedTime > this.allottedTime) { this.timeProgressBar.style.width = '100%'; this.isFinished = true; } else { this.timeProgressBar.style.width = elapsedTime / this.allottedTime * 100 + '%'; requestAnimationFrame(this.loop.bind(this)); } }, /** * set color of progress bar */ setBarColor() { if (this.isPaused) { this.timeProgressBar.style.backgroundColor = this.pausedBarColor; } else { this.timeProgressBar.style.backgroundColor = this.barColor; } }, /** * start(reset) timer with new allotted time. * @param {number} allottedTime * @param {number} [elapsedTime=0] */ start(allottedTime, elapsedTime = 0) { this.isFinished = false; this.isPaused = false; this.allottedTime = allottedTime; this.startTime = +new Date() - elapsedTime; this.pauseTimeDuration = 0; this.setBarColor(); this.loop(); }, reset() { this.start(this.allottedTime); }, pause() { if (this.isPaused) return; this.isPaused = true; this.pauseTime = +new Date(); this.setBarColor(); }, resume() { if (!this.isPaused) return; // add paused time duration this.isPaused = false; this.pauseTimeDuration += new Date() - this.pauseTime; this.pauseTime = null; this.setBarColor(); this.loop(); } }; if (Reveal.isReady()) { ElapsedTimeBar.handleReady(); } else { Reveal.addEventListener('ready', () => ElapsedTimeBar.handleReady()); } ================================================ FILE: public/js/revealjs-plugins/spotlight/spotlight.js ================================================ /* eslint-disable */ var RevealSpotlight = window.RevealSpotlight || (function () { //configs var spotlightSize; var toggleOnMouseDown; var spotlightOnKeyPressAndHold; var presentingCursor; var spotlightCursor; var initialPresentationMode; var disablingUserSelect; var fadeInAndOut; var style; var lockPointerInsideCanvas; var getMousePos; var drawBoard; var isSpotlightOn = true; var isCursorOn = true; var lastMouseMoveEvent; function onRevealJsReady(event) { configure(); drawBoard = setupCanvas(); addWindowResizeListener(); addMouseMoveListener(); if (toggleOnMouseDown) { addMouseToggleSpotlightListener(); } if (spotlightOnKeyPressAndHold) { addKeyPressAndHoldSpotlightListener(spotlightOnKeyPressAndHold); } setCursor(!initialPresentationMode); setSpotlight(false); } function configure() { var config = Reveal.getConfig().spotlight || {}; spotlightSize = config.size || 60; presentingCursor = config.presentingCursor || "none"; spotlightCursor = config.spotlightCursor || "none"; var useAsPointer = config.useAsPointer || false; var pointerColor = config.pointerColor || 'red'; lockPointerInsideCanvas = config.lockPointerInsideCanvas || false; if(lockPointerInsideCanvas){ getMousePos = getMousePosByMovement; } else { getMousePos = getMousePosByBoundingClientRect; } // If using as pointer draw a transparent background and // the mouse pointer in the specified color or default var pointerStyle = { backgroundFillStyle : "rgba(0, 0, 0, 0)", mouseFillStyle : pointerColor }; var spotlightStyle = { backgroundFillStyle : "#000000A8", mouseFillStyle : "#FFFFFFFF" }; style = useAsPointer ? pointerStyle : spotlightStyle; if (config.hasOwnProperty("toggleSpotlightOnMouseDown")) { toggleOnMouseDown = config.toggleSpotlightOnMouseDown; } else { toggleOnMouseDown = true; } if (config.hasOwnProperty("initialPresentationMode")) { initialPresentationMode = config.initialPresentationMode; } else { initialPresentationMode = toggleOnMouseDown; } if (config.hasOwnProperty("spotlightOnKeyPressAndHold")) { spotlightOnKeyPressAndHold = config.spotlightOnKeyPressAndHold; } else { spotlightOnKeyPressAndHold = false; } if (config.hasOwnProperty("disablingUserSelect")) { disablingUserSelect = config.disablingUserSelect; } else { disablingUserSelect = true; } if (config.hasOwnProperty("fadeInAndOut")) { fadeInAndOut = config.fadeInAndOut; } else { fadeInAndOut = false; } } function setupCanvas() { var container = document.createElement('div'); container.id = "spotlight"; container.style.cssText = "position:absolute;top:0;left:0;bottom:0;right:0;z-index:99;"; if (fadeInAndOut) { container.style.cssText += "transition: " + fadeInAndOut + "ms opacity;"; } var canvas = document.createElement('canvas'); var context = canvas.getContext("2d"); canvas.width = window.innerWidth; canvas.height = window.innerHeight; container.appendChild(canvas); document.body.appendChild(container); container.style.opacity = 0; container.style['pointer-events'] = 'none'; return { container, canvas, context } } function addWindowResizeListener() { window.addEventListener('resize', function (e) { var canvas = drawBoard.canvas; canvas.width = window.innerWidth; canvas.height = window.innerHeight; }, false); } function addMouseMoveListener() { document.body.addEventListener('mousemove', function (e) { if(isSpotlightOn) { showSpotlight(e); } lastMouseMoveEvent = e; }, false); } function addMouseToggleSpotlightListener() { window.addEventListener("mousedown", function (e) { if (!isCursorOn) { setSpotlight(true, e); } }, false); window.addEventListener("mouseup", function (e) { if (!isCursorOn) { setSpotlight(false, e); } }, false); } function addKeyPressAndHoldSpotlightListener(keyCode) { window.addEventListener("keydown", function (e) { if (!isCursorOn && !isSpotlightOn && e.keyCode === keyCode) { setSpotlight(true, lastMouseMoveEvent); } }, false); window.addEventListener("keyup", function (e) { if (!isCursorOn && e.keyCode === keyCode) { setSpotlight(false); } }, false); } function toggleSpotlight() { setSpotlight(!isSpotlightOn, lastMouseMoveEvent); } function setSpotlight(isOn, mouseEvt) { isSpotlightOn = isOn; var container = drawBoard.container; if (isOn) { if (lockPointerInsideCanvas && document.pointerLockElement != drawBoard.canvas) { drawBoard.canvas.requestPointerLock(); } container.style.opacity = 1; container.style['pointer-events'] = null; document.body.style.cursor = spotlightCursor; if (mouseEvt) { showSpotlight(mouseEvt); } } else { container.style.opacity = 0; container.style['pointer-events'] = 'none'; document.body.style.cursor = presentingCursor; } } function togglePresentationMode() { setCursor(!isCursorOn); } function setCursor(isOn) { isCursorOn = isOn; if (isOn) { if (disablingUserSelect) { document.body.style.userSelect = null; } document.body.style.cursor = null; } else { if (disablingUserSelect) { document.body.style.userSelect = "none"; } document.body.style.cursor = presentingCursor; } } function showSpotlight(mouseEvt) { var canvas = drawBoard.canvas; var context = drawBoard.context; var mousePos = getMousePos(canvas, mouseEvt); context.clearRect(0, 0, canvas.width, canvas.height); // Create a canvas mask var maskCanvas = document.createElement('canvas'); maskCanvas.width = canvas.width; maskCanvas.height = canvas.height; var maskCtx = maskCanvas.getContext('2d'); maskCtx.fillStyle = style.backgroundFillStyle; maskCtx.fillRect(0, 0, maskCanvas.width, maskCanvas.height); maskCtx.globalCompositeOperation = 'xor'; maskCtx.fillStyle = style.mouseFillStyle; maskCtx.arc(mousePos.x, mousePos.y, spotlightSize, 0, 2 * Math.PI); maskCtx.fill(); context.drawImage(maskCanvas, 0, 0); } var mX = 0; var mY = 0; function getMousePosByMovement(canvas, evt) { var movementX = evt.movementX || 0; var movementY = evt.movementY || 0; mX += movementX; mY += movementY; if (mX > canvas.clientWidth) { mX = canvas.clientWidth; } if (mY > canvas.clientHeight) { mY = canvas.clientHeight; } if (mX < 0) { mX = 0; } if (mY < 0) { mY = 0; } return { x: mX, y: mY }; } function getMousePosByBoundingClientRect(canvas, evt) { var rect = canvas.getBoundingClientRect(); return { x: evt.clientX - rect.left, y: evt.clientY - rect.top }; } Reveal.addEventListener('ready', onRevealJsReady); this.toggleSpotlight = toggleSpotlight; this.togglePresentationMode = togglePresentationMode; return this; })(); ================================================ FILE: public/js/slide.js ================================================ /* eslint-env browser, jquery */ /* global serverurl, Reveal */ import RevealMarkdown from './reveal-markdown' import { preventXSS } from './render' import { md, updateLastChange, removeDOMEvents, finishView } from './extra' require('../css/extra.css') require('../css/site.css') const body = preventXSS($('.slides').text()) window.createtime = window.lastchangeui.time.attr('data-createtime') window.lastchangetime = window.lastchangeui.time.attr('data-updatetime') updateLastChange() const url = window.location.pathname $('.ui-edit').attr('href', `${url}/edit`) $('.ui-print').attr('href', `${url}?print-pdf`) $(document).ready(() => { // tooltip $('[data-toggle="tooltip"]').tooltip() }) function extend () { const target = {} for (const source of arguments) { for (const key in source) { if (Object.hasOwnProperty.call(source, key)) { target[key] = source[key] } } } return target } // Optional libraries used to extend on reveal.js const deps = [{ src: `${serverurl}/build/reveal.js/lib/js/classList.js`, condition () { return !document.body.classList } }, { src: `${serverurl}/build/reveal.js/plugin/notes/notes.js`, async: true, condition () { return !!document.body.classList } }] // options from yaml meta const meta = JSON.parse($('#meta').text()) // breaks if (typeof meta.breaks === 'boolean') { md.options.breaks = meta.breaks } else { md.options.breaks = window.defaultUseHardbreak } const slideOptions = { separator: '^(\r\n?|\n)---(\r\n?|\n)$', verticalSeparator: '^(\r\n?|\n)----(\r\n?|\n)$' } const slides = RevealMarkdown.slidify(body, slideOptions) $('.slides').html(slides) RevealMarkdown.initialize() removeDOMEvents($('.slides')) $('.slides').show() // default options to init reveal.js const defaultOptions = { controls: true, progress: true, slideNumber: true, history: true, center: true, transition: 'none', dependencies: deps } var options = meta.slideOptions || {} // delete dependencies to avoid import user defined external resources delete options.dependencies if (Object.hasOwnProperty.call(options, 'spotlight')) { defaultOptions.dependencies.push({ src: `${serverurl}/build/revealjs-plugins/spotlight/spotlight.js` }) } if (Object.hasOwnProperty.call(options, 'allottedTime') || Object.hasOwnProperty.call(options, 'allottedMinutes')) { defaultOptions.dependencies.push({ src: `${serverurl}/build/revealjs-plugins/elapsed-time-bar/elapsed-time-bar.js` }) if (Object.hasOwnProperty.call(options, 'allottedMinutes')) { options.allottedTime = options.allottedMinutes * 60 * 1000 } } const view = $('.reveal') // text language if (meta.lang && typeof meta.lang === 'string') { view.attr('lang', meta.lang) } else { view.removeAttr('lang') } // text direction if (meta.dir && typeof meta.dir === 'string' && meta.dir === 'rtl') { options.rtl = true } else { options.rtl = false } // options from URL query string const queryOptions = Reveal.getQueryHash() || {} options = extend(defaultOptions, options, queryOptions) Reveal.initialize(options) window.viewAjaxCallback = () => { Reveal.layout() } function renderSlide (event) { if (window.location.search.match(/print-pdf/gi)) { const slides = $('.slides') const title = document.title finishView(slides) document.title = title Reveal.layout() } else { const markdown = $(event.currentSlide) if (!markdown.attr('data-rendered')) { const title = document.title finishView(markdown) markdown.attr('data-rendered', 'true') document.title = title Reveal.layout() } } } Reveal.addEventListener('ready', event => { renderSlide(event) const markdown = $(event.currentSlide) // force browser redraw setTimeout(() => { markdown.hide().show(0) }, 0) }) Reveal.addEventListener('slidechanged', renderSlide) const isWinLike = navigator.platform.indexOf('Win') > -1 if (isWinLike) $('.container').addClass('hidescrollbar') ================================================ FILE: public/js/utils.js ================================================ /* global fetch */ import base64url from 'base64url' const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i export function checkNoteIdValid (id) { const result = id.match(uuidRegex) return !!(result && result.length === 1) } export function encodeNoteId (id) { // remove dashes in UUID and encode in url-safe base64 const str = id.replace(/-/g, '') const hexStr = Buffer.from(str, 'hex') return base64url.encode(hexStr) } export function decodeNoteId (encodedId) { // decode from url-safe base64 const id = base64url.toBuffer(encodedId).toString('hex') // add dashes between the UUID string parts const idParts = [] idParts.push(id.substr(0, 8)) idParts.push(id.substr(8, 4)) idParts.push(id.substr(12, 4)) idParts.push(id.substr(16, 4)) idParts.push(id.substr(20, 12)) return idParts.join('-') } /** * sanitize url to prevent XSS * @see {@link https://github.com/braintree/sanitize-url/issues/52#issue-1593777166} * * @param {string} rawUrl * @returns {string} sanitized url */ export function sanitizeUrl (rawUrl) { try { const url = new URL(rawUrl) if (url.protocol === 'http:' || url.protocol === 'https:') { return url.toString() } throw new Error('Invalid protocol') } catch (error) { return 'about:blank' } } // Check if URL is a PDF based on Content-Type header export async function isPdfUrl (url) { try { const response = await fetch(url, { method: 'HEAD' }) const contentType = response.headers.get('Content-Type') return contentType === 'application/pdf' } catch (error) { console.warn('Error checking PDF content type:', error) return false } } ================================================ FILE: public/markdown-lint/css/lint.css ================================================ /* The lint marker gutter */ .CodeMirror-lint-markers { width: 16px; } .CodeMirror-lint-tooltip { background-color: #333333; border: 1px solid #eeeeee; border-radius: 4px; color: white; font-family: "Source Code Pro", Consolas, monaco, monospace; font-size: 10pt; overflow: hidden; padding: 2px 5px; position: fixed; white-space: pre; white-space: pre-wrap; z-index: 100; max-width: 600px; opacity: 0; transition: opacity .4s; -moz-transition: opacity .4s; -webkit-transition: opacity .4s; -o-transition: opacity .4s; -ms-transition: opacity .4s; } .CodeMirror-lint-mark-error, .CodeMirror-lint-mark-warning { background-position: left bottom; background-repeat: repeat-x; } .CodeMirror-lint-mark-error { background-image: url(../images/mark-error.png); } .CodeMirror-lint-mark-warning { background-image: url(../images/mark-warning.png); } .CodeMirror-lint-marker-error, .CodeMirror-lint-marker-warning { background-position: center center; background-repeat: no-repeat; cursor: pointer; display: inline-block; height: 16px; width: 16px; vertical-align: middle; position: relative; margin-left: 5px; } .CodeMirror-lint-message-error, .CodeMirror-lint-message-warning { padding-left: 20px; background-position: top left; background-repeat: no-repeat; background-position-y: 2px; } .CodeMirror-lint-marker-error, .CodeMirror-lint-message-error { background-image: url(../images/message-error.png); } .CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning { background-image: url(../images/message-warning.png); } .CodeMirror-lint-marker-multiple { background-image: url(../images/mark-multiple.png); background-repeat: no-repeat; background-position: right bottom; width: 100%; height: 100%; } .CodeMirror-hints { background: #333; } .CodeMirror-hint { color: white; } ================================================ FILE: public/uploads/.gitkeep ================================================ ================================================ FILE: public/vendor/abcjs_basic_3.1.1-min.js ================================================ /*! abcjs v3.1.1 Copyright © 2009-2016 Paul Rosen and Gregory Dyke (http://abcjs.net) */ function calcHorizontalSpacing(isLastLine,stretchLast,targetWidth,lineWidth,spacing,spacingUnits,minSpace){if(isLastLine&&lineWidth/targetWidth<.66&&!stretchLast)return null;if(Math.abs(targetWidth-lineWidth)<2)return null;var relSpace=spacingUnits*spacing,constSpace=lineWidth-relSpace;return spacingUnits>0?(spacing=(targetWidth-constSpace)/spacingUnits,spacing*minSpace>50&&(spacing=50/minSpace),spacing):null}function centerWholeRests(voices){for(var i=0;i=1e3&&delete cache[count.shift()],count.push(args),cache[args]=f[apply](scope,arg),postprocessor?postprocessor(cache[args]):cache[args])}return newf}function clrToString(){return this.hex}function catmullRom2bezier(crp,z){for(var d=[],i=0,iLen=crp.length;iLen-2*!z>i;i+=2){var p=[{x:+crp[i-2],y:+crp[i-1]},{x:+crp[i],y:+crp[i+1]},{x:+crp[i+2],y:+crp[i+3]},{x:+crp[i+4],y:+crp[i+5]}];z?i?iLen-4==i?p[3]={x:+crp[0],y:+crp[1]}:iLen-2==i&&(p[2]={x:+crp[0],y:+crp[1]},p[3]={x:+crp[2],y:+crp[3]}):p[0]={x:+crp[iLen-2],y:+crp[iLen-1]}:iLen-4==i?p[3]=p[2]:i||(p[0]={x:+crp[i],y:+crp[i+1]}),d.push(["C",(-p[0].x+6*p[1].x+p[2].x)/6,(-p[0].y+6*p[1].y+p[2].y)/6,(p[1].x+6*p[2].x-p[3].x)/6,(p[1].y+6*p[2].y-p[3].y)/6,p[2].x,p[2].y])}return d}function base3(t,p1,p2,p3,p4){var t1=-3*p1+9*p2-9*p3+3*p4,t2=t*t1+6*p1-12*p2+6*p3;return t*t2-3*p1+3*p2}function bezlen(x1,y1,x2,y2,x3,y3,x4,y4,z){null==z&&(z=1),z=z>1?1:z<0?0:z;for(var z2=z/2,n=12,Tvalues=[-.1252,.1252,-.3678,.3678,-.5873,.5873,-.7699,.7699,-.9041,.9041,-.9816,.9816],Cvalues=[.2491,.2491,.2335,.2335,.2032,.2032,.1601,.1601,.1069,.1069,.0472,.0472],sum=0,i=0;ie;)step/=2,t2+=(lmmax(x3,x4)||mmax(y1,y2)mmax(y3,y4))){var nx=(x1*y2-y1*x2)*(x3-x4)-(x1-x2)*(x3*y4-y3*x4),ny=(x1*y2-y1*x2)*(y3-y4)-(y1-y2)*(x3*y4-y3*x4),denominator=(x1-x2)*(y3-y4)-(y1-y2)*(x3-x4);if(denominator){var px=nx/denominator,py=ny/denominator,px2=+px.toFixed(2),py2=+py.toFixed(2);if(!(px2<+mmin(x1,x2).toFixed(2)||px2>+mmax(x1,x2).toFixed(2)||px2<+mmin(x3,x4).toFixed(2)||px2>+mmax(x3,x4).toFixed(2)||py2<+mmin(y1,y2).toFixed(2)||py2>+mmax(y1,y2).toFixed(2)||py2<+mmin(y3,y4).toFixed(2)||py2>+mmax(y3,y4).toFixed(2)))return{x:px,y:py}}}}function interHelper(bez1,bez2,justCount){var bbox1=R.bezierBBox(bez1),bbox2=R.bezierBBox(bez2);if(!R.isBBoxIntersect(bbox1,bbox2))return justCount?0:[];for(var l1=bezlen.apply(0,bez1),l2=bezlen.apply(0,bez2),n1=mmax(~~(l1/5),1),n2=mmax(~~(l2/5),1),dots1=[],dots2=[],xy={},res=justCount?0:[],i=0;i=0&&t1<=1.001&&t2>=0&&t2<=1.001&&(justCount?res++:res.push({x:is.x,y:is.y,t1:mmin(t1,1),t2:mmin(t2,1)}))}}return res}function interPathHelper(path1,path2,justCount){path1=R._path2curve(path1),path2=R._path2curve(path2);for(var x1,y1,x2,y2,x1m,y1m,x2m,y2m,bez1,bez2,res=justCount?0:[],i=0,ii=path1.length;it1)return t1;for(;t0x2?t0=t2:t1=t2,t2=(t1-t0)/2+t0}return t2}var cx=3*p1x,bx=3*(p2x-p1x)-cx,ax=1-cx-bx,cy=3*p1y,by=3*(p2y-p1y)-cy,ay=1-cy-by;return solve(t,1/(200*duration))}function Animation(anim,ms){var percents=[],newAnim={};if(this.ms=ms,this.times=1,anim){for(var attr in anim)anim[has](attr)&&(newAnim[toFloat(attr)]=anim[attr],percents.push(toFloat(attr)));percents.sort(sortByNumber)}this.anim=newAnim,this.top=percents[percents.length-1],this.percents=percents}function runAnimation(anim,element,percent,status,totalOrigin,times){percent=toFloat(percent);var params,isInAnim,isInAnimSet,next,prev,timestamp,ms=anim.ms,from={},to={},diff={};if(status)for(i=0,ii=animationElements.length;istatus*anim.top){percent=anim.percents[i],prev=anim.percents[i-1]||0,ms=ms/anim.top*(percent-prev),next=anim.percents[i+1],params=anim.anim[percent];break}status&&element.attr(anim.anim[anim.percents[i]])}if(params){if(isInAnim)isInAnim.initstatus=status,isInAnim.start=new Date-isInAnim.ms*status;else{for(var attr in params)if(params[has](attr)&&(availableAnimAttrs[has](attr)||element.paper.customAttributes[has](attr)))switch(from[attr]=element.attr(attr),null==from[attr]&&(from[attr]=availableAttrs[attr]),to[attr]=params[attr],availableAnimAttrs[attr]){case nu:diff[attr]=(to[attr]-from[attr])/ms;break;case"colour":from[attr]=R.getRGB(from[attr]);var toColour=R.getRGB(to[attr]);diff[attr]={r:(toColour.r-from[attr].r)/ms,g:(toColour.g-from[attr].g)/ms,b:(toColour.b-from[attr].b)/ms};break;case"path":var pathes=path2curve(from[attr],to[attr]),toPath=pathes[1];for(from[attr]=pathes[0],diff[attr]=[],i=0,ii=from[attr].length;i',b=d.firstChild,b.style.behavior="url(#default#VML)",!b||"object"!=typeof b.adj)return R.type=E;d=null}R.svg=!(R.vml="VML"==R.type),R._Paper=Paper,R.fn=paperproto=Paper.prototype=R.prototype,R._id=0,R._oid=0,R.is=function(o,type){return type=lowerCase.call(type),"finite"==type?!isnan[has](+o):"array"==type?o instanceof Array:"null"==type&&null===o||type==typeof o&&null!==o||"object"==type&&o===Object(o)||"array"==type&&Array.isArray&&Array.isArray(o)||objectToString.call(o).slice(8,-1).toLowerCase()==type},R.angle=function(x1,y1,x2,y2,x3,y3){if(null==x3){var x=x1-x2,y=y1-y2;return x||y?(180+180*math.atan2(-y,-x)/PI+360)%360:0}return R.angle(x1,y1,x3,y3)-R.angle(x2,y2,x3,y3)},R.rad=function(deg){return deg%360*PI/180},R.deg=function(rad){return 180*rad/PI%360},R.snapTo=function(values,value,tolerance){if(tolerance=R.is(tolerance,"finite")?tolerance:10,R.is(values,array)){for(var i=values.length;i--;)if(abs(values[i]-value)<=tolerance)return values[i]}else{values=+values;var rem=value%values;if(remvalues-tolerance)return value-rem+values}return value};R.createUUID=function(uuidRegEx,uuidReplacer){return function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(uuidRegEx,uuidReplacer).toUpperCase()}}(/[xy]/g,function(c){var r=16*math.random()|0,v="x"==c?r:3&r|8;return v.toString(16)});R.setWindow=function(newwin){eve("raphael.setWindow",R,g.win,newwin),g.win=newwin,g.doc=g.win.document,R._engine.initWin&&R._engine.initWin(g.win)};var toHex=function(color){if(R.vml){var bod,trim=/^\s+|\s+$/g;try{var docum=new ActiveXObject("htmlfile");docum.write(""),docum.close(),bod=docum.body}catch(e){bod=createPopup().document.body}var range=bod.createTextRange();toHex=cacher(function(color){try{bod.style.color=Str(color).replace(trim,E);var value=range.queryCommandValue("ForeColor");return value=(255&value)<<16|65280&value|(16711680&value)>>>16,"#"+("000000"+value.toString(16)).slice(-6)}catch(e){return"none"}})}else{var i=g.doc.createElement("i");i.title="Raphaël Colour Picker",i.style.display="none",g.doc.body.appendChild(i),toHex=cacher(function(color){return i.style.color=color,g.doc.defaultView.getComputedStyle(i,E).getPropertyValue("color")})}return toHex(color)},hsbtoString=function(){return"hsb("+[this.h,this.s,this.b]+")"},hsltoString=function(){return"hsl("+[this.h,this.s,this.l]+")"},rgbtoString=function(){return this.hex},prepareRGB=function(r,g,b){if(null==g&&R.is(r,"object")&&"r"in r&&"g"in r&&"b"in r&&(b=r.b,g=r.g,r=r.r),null==g&&R.is(r,string)){var clr=R.getRGB(r);r=clr.r,g=clr.g,b=clr.b}return(r>1||g>1||b>1)&&(r/=255,g/=255,b/=255),[r,g,b]},packageRGB=function(r,g,b,o){r*=255,g*=255,b*=255;var rgb={r:r,g:g,b:b,hex:R.rgb(r,g,b),toString:rgbtoString};return R.is(o,"finite")&&(rgb.opacity=o),rgb};R.color=function(clr){var rgb;return R.is(clr,"object")&&"h"in clr&&"s"in clr&&"b"in clr?(rgb=R.hsb2rgb(clr),clr.r=rgb.r,clr.g=rgb.g,clr.b=rgb.b,clr.hex=rgb.hex):R.is(clr,"object")&&"h"in clr&&"s"in clr&&"l"in clr?(rgb=R.hsl2rgb(clr),clr.r=rgb.r,clr.g=rgb.g,clr.b=rgb.b,clr.hex=rgb.hex):(R.is(clr,"string")&&(clr=R.getRGB(clr)),R.is(clr,"object")&&"r"in clr&&"g"in clr&&"b"in clr?(rgb=R.rgb2hsl(clr),clr.h=rgb.h,clr.s=rgb.s,clr.l=rgb.l,rgb=R.rgb2hsb(clr),clr.v=rgb.b):(clr={hex:"none"},clr.r=clr.g=clr.b=clr.h=clr.s=clr.v=clr.l=-1)),clr.toString=rgbtoString,clr},R.hsb2rgb=function(h,s,v,o){this.is(h,"object")&&"h"in h&&"s"in h&&"b"in h&&(v=h.b,s=h.s,h=h.h,o=h.o),h*=360;var R,G,B,X,C;return h=h%360/60,C=v*s,X=C*(1-abs(h%2-1)),R=G=B=v-C,h=~~h,R+=[C,X,0,0,X,C][h],G+=[X,C,C,X,0,0][h],B+=[0,0,X,C,C,X][h],packageRGB(R,G,B,o)},R.hsl2rgb=function(h,s,l,o){this.is(h,"object")&&"h"in h&&"s"in h&&"l"in h&&(l=h.l,s=h.s,h=h.h),(h>1||s>1||l>1)&&(h/=360,s/=100,l/=100),h*=360;var R,G,B,X,C;return h=h%360/60,C=2*s*(l<.5?l:1-l),X=C*(1-abs(h%2-1)),R=G=B=l-C/2,h=~~h,R+=[C,X,0,0,X,C][h],G+=[X,C,C,X,0,0][h],B+=[0,0,X,C,C,X][h],packageRGB(R,G,B,o)},R.rgb2hsb=function(r,g,b){b=prepareRGB(r,g,b),r=b[0],g=b[1],b=b[2];var H,S,V,C;return V=mmax(r,g,b),C=V-mmin(r,g,b),H=0==C?null:V==r?(g-b)/C:V==g?(b-r)/C+2:(r-g)/C+4,H=(H+360)%6*60/360,S=0==C?0:C/V,{h:H,s:S,b:V,toString:hsbtoString}},R.rgb2hsl=function(r,g,b){b=prepareRGB(r,g,b),r=b[0],g=b[1],b=b[2];var H,S,L,M,m,C;return M=mmax(r,g,b),m=mmin(r,g,b),C=M-m,H=0==C?null:M==r?(g-b)/C:M==g?(b-r)/C+2:(r-g)/C+4,H=(H+360)%6*60/360,L=(M+m)/2,S=0==C?0:L<.5?C/(2*L):C/(2-2*L),{h:H,s:S,l:L,toString:hsltoString}},R._path2string=function(){return this.join(",").replace(p2s,"$1")};R._preload=function(src,f){var img=g.doc.createElement("img");img.style.cssText="position:absolute;left:-9999em;top:-9999em",img.onload=function(){f.call(this),this.onload=null,g.doc.body.removeChild(this)},img.onerror=function(){g.doc.body.removeChild(this)},g.doc.body.appendChild(img),img.src=src};R.getRGB=cacher(function(colour){if(!colour||(colour=Str(colour)).indexOf("-")+1)return{r:-1,g:-1,b:-1,hex:"none",error:1,toString:clrToString};if("none"==colour)return{r:-1,g:-1,b:-1,hex:"none",toString:clrToString};!(hsrg[has](colour.toLowerCase().substring(0,2))||"#"==colour.charAt())&&(colour=toHex(colour));var red,green,blue,opacity,t,values,rgb=colour.match(colourRegExp);return rgb?(rgb[2]&&(blue=toInt(rgb[2].substring(5),16),green=toInt(rgb[2].substring(3,5),16),red=toInt(rgb[2].substring(1,3),16)),rgb[3]&&(blue=toInt((t=rgb[3].charAt(3))+t,16),green=toInt((t=rgb[3].charAt(2))+t,16),red=toInt((t=rgb[3].charAt(1))+t,16)),rgb[4]&&(values=rgb[4][split](commaSpaces),red=toFloat(values[0]),"%"==values[0].slice(-1)&&(red*=2.55),green=toFloat(values[1]),"%"==values[1].slice(-1)&&(green*=2.55),blue=toFloat(values[2]),"%"==values[2].slice(-1)&&(blue*=2.55),"rgba"==rgb[1].toLowerCase().slice(0,4)&&(opacity=toFloat(values[3])),values[3]&&"%"==values[3].slice(-1)&&(opacity/=100)),rgb[5]?(values=rgb[5][split](commaSpaces),red=toFloat(values[0]),"%"==values[0].slice(-1)&&(red*=2.55),green=toFloat(values[1]),"%"==values[1].slice(-1)&&(green*=2.55),blue=toFloat(values[2]),"%"==values[2].slice(-1)&&(blue*=2.55),("deg"==values[0].slice(-3)||"°"==values[0].slice(-1))&&(red/=360),"hsba"==rgb[1].toLowerCase().slice(0,4)&&(opacity=toFloat(values[3])),values[3]&&"%"==values[3].slice(-1)&&(opacity/=100),R.hsb2rgb(red,green,blue,opacity)):rgb[6]?(values=rgb[6][split](commaSpaces),red=toFloat(values[0]),"%"==values[0].slice(-1)&&(red*=2.55),green=toFloat(values[1]),"%"==values[1].slice(-1)&&(green*=2.55),blue=toFloat(values[2]),"%"==values[2].slice(-1)&&(blue*=2.55),("deg"==values[0].slice(-3)||"°"==values[0].slice(-1))&&(red/=360),"hsla"==rgb[1].toLowerCase().slice(0,4)&&(opacity=toFloat(values[3])),values[3]&&"%"==values[3].slice(-1)&&(opacity/=100),R.hsl2rgb(red,green,blue,opacity)):(rgb={r:red,g:green,b:blue,toString:clrToString},rgb.hex="#"+(16777216|blue|green<<8|red<<16).toString(16).slice(1),R.is(opacity,"finite")&&(rgb.opacity=opacity),rgb)):{r:-1,g:-1,b:-1,hex:"none",error:1,toString:clrToString}},R),R.hsb=cacher(function(h,s,b){return R.hsb2rgb(h,s,b).hex}),R.hsl=cacher(function(h,s,l){return R.hsl2rgb(h,s,l).hex}),R.rgb=cacher(function(r,g,b){return"#"+(16777216|b|g<<8|r<<16).toString(16).slice(1)}),R.getColor=function(value){var start=this.getColor.start=this.getColor.start||{h:0,s:1,b:value||.75},rgb=this.hsb2rgb(start.h,start.s,start.b);return start.h+=.075,start.h>1&&(start.h=0,start.s-=.2,start.s<=0&&(this.getColor.start={h:0,s:1,b:start.b})),rgb.hex},R.getColor.reset=function(){delete this.start},R.parsePathString=function(pathString){if(!pathString)return null;var pth=paths(pathString);if(pth.arr)return pathClone(pth.arr);var paramCounts={a:7,c:6,h:1,l:2,m:2,r:4,q:4,s:4,t:2,v:1,z:0},data=[];return R.is(pathString,array)&&R.is(pathString[0],array)&&(data=pathClone(pathString)),data.length||Str(pathString).replace(pathCommand,function(a,b,c){var params=[],name=b.toLowerCase();if(c.replace(pathValues,function(a,b){b&¶ms.push(+b)}),"m"==name&¶ms.length>2&&(data.push([b][concat](params.splice(0,2))),name="l",b="m"==b?"l":"L"),"r"==name)data.push([b][concat](params));else for(;params.length>=paramCounts[name]&&(data.push([b][concat](params.splice(0,paramCounts[name]))),paramCounts[name]););}),data.toString=R._path2string,pth.arr=pathClone(data),data},R.parseTransformString=cacher(function(TString){if(!TString)return null;var data=[];return R.is(TString,array)&&R.is(TString[0],array)&&(data=pathClone(TString)),data.length||Str(TString).replace(tCommand,function(a,b,c){var params=[];lowerCase.call(b);c.replace(pathValues,function(a,b){b&¶ms.push(+b)}),data.push([b][concat](params))}),data.toString=R._path2string,data});var paths=function(ps){var p=paths.ps=paths.ps||{};return p[ps]?p[ps].sleep=1:p[ps]={sleep:1},setTimeout(function(){for(var key in p)p[has](key)&&key!=ps&&(p[key].sleep--,!p[key].sleep&&delete p[key])}),p[ps]};R.findDotsAtSegment=function(p1x,p1y,c1x,c1y,c2x,c2y,p2x,p2y,t){var t1=1-t,t13=pow(t1,3),t12=pow(t1,2),t2=t*t,t3=t2*t,x=t13*p1x+3*t12*t*c1x+3*t1*t*t*c2x+t3*p2x,y=t13*p1y+3*t12*t*c1y+3*t1*t*t*c2y+t3*p2y,mx=p1x+2*t*(c1x-p1x)+t2*(c2x-2*c1x+p1x),my=p1y+2*t*(c1y-p1y)+t2*(c2y-2*c1y+p1y),nx=c1x+2*t*(c2x-c1x)+t2*(p2x-2*c2x+c1x),ny=c1y+2*t*(c2y-c1y)+t2*(p2y-2*c2y+c1y),ax=t1*p1x+t*c1x,ay=t1*p1y+t*c1y,cx=t1*c2x+t*p2x,cy=t1*c2y+t*p2y,alpha=90-180*math.atan2(mx-nx,my-ny)/PI;return(mx>nx||my=bbox.x&&x<=bbox.x2&&y>=bbox.y&&y<=bbox.y2},R.isBBoxIntersect=function(bbox1,bbox2){var i=R.isPointInsideBBox;return i(bbox2,bbox1.x,bbox1.y)||i(bbox2,bbox1.x2,bbox1.y)||i(bbox2,bbox1.x,bbox1.y2)||i(bbox2,bbox1.x2,bbox1.y2)||i(bbox1,bbox2.x,bbox2.y)||i(bbox1,bbox2.x2,bbox2.y)||i(bbox1,bbox2.x,bbox2.y2)||i(bbox1,bbox2.x2,bbox2.y2)||(bbox1.xbbox2.x||bbox2.xbbox1.x)&&(bbox1.ybbox2.y||bbox2.ybbox1.y)},R.pathIntersection=function(path1,path2){return interPathHelper(path1,path2)},R.pathIntersectionNumber=function(path1,path2){return interPathHelper(path1,path2,1)},R.isPointInsidePath=function(path,x,y){var bbox=R.pathBBox(path);return R.isPointInsideBBox(bbox,x,y)&&interPathHelper(path,[["M",x,y],["H",bbox.x2+10]],1)%2==1},R._removedFactory=function(methodname){return function(){eve("raphael.log",null,"Raphaël: you are calling to method “"+methodname+"” of removed object",methodname)}};var pathDimensions=R.pathBBox=function(path){var pth=paths(path);if(pth.bbox)return clone(pth.bbox);if(!path)return{x:0,y:0,width:0,height:0,x2:0,y2:0};path=path2curve(path);for(var p,x=0,y=0,X=[],Y=[],i=0,ii=path.length;i1&&(h=math.sqrt(h),rx*=h,ry*=h);var rx2=rx*rx,ry2=ry*ry,k=(large_arc_flag==sweep_flag?-1:1)*math.sqrt(abs((rx2*ry2-rx2*y*y-ry2*x*x)/(rx2*y*y+ry2*x*x))),cx=k*rx*y/ry+(x1+x2)/2,cy=k*-ry*x/rx+(y1+y2)/2,f1=math.asin(((y1-cy)/ry).toFixed(9)),f2=math.asin(((y2-cy)/ry).toFixed(9));f1=x1f2&&(f1-=2*PI),!sweep_flag&&f2>f1&&(f2-=2*PI)}var df=f2-f1;if(abs(df)>_120){var f2old=f2,x2old=x2,y2old=y2;f2=f1+_120*(sweep_flag&&f2>f1?1:-1),x2=cx+rx*math.cos(f2),y2=cy+ry*math.sin(f2),res=a2c(x2,y2,rx,ry,angle,0,sweep_flag,x2old,y2old,[f2,f2old,cx,cy])}df=f2-f1;var c1=math.cos(f1),s1=math.sin(f1),c2=math.cos(f2),s2=math.sin(f2),t=math.tan(df/4),hx=4/3*rx*t,hy=4/3*ry*t,m1=[x1,y1],m2=[x1+hx*s1,y1-hy*c1],m3=[x2+hx*s2,y2-hy*c2],m4=[x2,y2];if(m2[0]=2*m1[0]-m2[0],m2[1]=2*m1[1]-m2[1],recursive)return[m2,m3,m4][concat](res);res=[m2,m3,m4][concat](res).join()[split](",");for(var newres=[],i=0,ii=res.length;i"1e12"&&(t1=.5),abs(t2)>"1e12"&&(t2=.5),t1>0&&t1<1&&(dot=findDotAtSegment(p1x,p1y,c1x,c1y,c2x,c2y,p2x,p2y,t1),x.push(dot.x),y.push(dot.y)),t2>0&&t2<1&&(dot=findDotAtSegment(p1x,p1y,c1x,c1y,c2x,c2y,p2x,p2y,t2),x.push(dot.x),y.push(dot.y)),a=c2y-2*c1y+p1y-(p2y-2*c2y+c1y),b=2*(c1y-p1y)-2*(c2y-c1y),c=p1y-c1y,t1=(-b+math.sqrt(b*b-4*a*c))/2/a,t2=(-b-math.sqrt(b*b-4*a*c))/2/a,abs(t1)>"1e12"&&(t1=.5),abs(t2)>"1e12"&&(t2=.5),t1>0&&t1<1&&(dot=findDotAtSegment(p1x,p1y,c1x,c1y,c2x,c2y,p2x,p2y,t1),x.push(dot.x),y.push(dot.y)),t2>0&&t2<1&&(dot=findDotAtSegment(p1x,p1y,c1x,c1y,c2x,c2y,p2x,p2y,t2),x.push(dot.x),y.push(dot.y)),{min:{x:mmin[apply](0,x),y:mmin[apply](0,y)},max:{x:mmax[apply](0,x),y:mmax[apply](0,y)}}}),path2curve=R._path2curve=cacher(function(path,path2){var pth=!path2&&paths(path);if(!path2&&pth.curve)return pathClone(pth.curve);for(var p=pathToAbsolute(path),p2=path2&&pathToAbsolute(path2),attrs={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},attrs2={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},processPath=(function(path,d,pcom){var nx,ny;if(!path)return["C",d.x,d.y,d.x,d.y,d.x,d.y];switch(!(path[0]in{T:1,Q:1})&&(d.qx=d.qy=null),path[0]){case"M":d.X=path[1],d.Y=path[2];break;case"A":path=["C"][concat](a2c[apply](0,[d.x,d.y][concat](path.slice(1))));break;case"S":"C"==pcom||"S"==pcom?(nx=2*d.x-d.bx,ny=2*d.y-d.by):(nx=d.x,ny=d.y),path=["C",nx,ny][concat](path.slice(1));break;case"T":"Q"==pcom||"T"==pcom?(d.qx=2*d.x-d.qx,d.qy=2*d.y-d.qy):(d.qx=d.x,d.qy=d.y),path=["C"][concat](q2c(d.x,d.y,d.qx,d.qy,path[1],path[2]));break;case"Q":d.qx=path[1],d.qy=path[2],path=["C"][concat](q2c(d.x,d.y,path[1],path[2],path[3],path[4]));break;case"L":path=["C"][concat](l2c(d.x,d.y,path[1],path[2]));break;case"H":path=["C"][concat](l2c(d.x,d.y,path[1],d.y));break;case"V":path=["C"][concat](l2c(d.x,d.y,d.x,path[1]));break;case"Z":path=["C"][concat](l2c(d.x,d.y,d.X,d.Y))}return path}),fixArc=function(pp,i){if(pp[i].length>7){pp[i].shift();for(var pi=pp[i];pi.length;)pp.splice(i++,0,["C"][concat](pi.splice(0,6)));pp.splice(i,1),ii=mmax(p.length,p2&&p2.length||0)}},fixM=function(path1,path2,a1,a2,i){path1&&path2&&"M"==path1[i][0]&&"M"!=path2[i][0]&&(path2.splice(i,0,["M",a2.x,a2.y]),a1.bx=0,a1.by=0,a1.x=path1[i][1],a1.y=path1[i][2],ii=mmax(p.length,p2&&p2.length||0))},i=0,ii=mmax(p.length,p2&&p2.length||0);ilength){if(subpath&&!subpaths.start){if(point=getPointAtSegmentLength(x,y,p[1],p[2],p[3],p[4],p[5],p[6],length-len),sp+=["C"+point.start.x,point.start.y,point.m.x,point.m.y,point.x,point.y],onlystart)return sp;subpaths.start=sp,sp=["M"+point.x,point.y+"C"+point.n.x,point.n.y,point.end.x,point.end.y,p[5],p[6]].join(),len+=l,x=+p[5],y=+p[6];continue}if(!istotal&&!subpath)return point=getPointAtSegmentLength(x,y,p[1],p[2],p[3],p[4],p[5],p[6],length-len),{x:point.x,y:point.y,alpha:point.alpha}}len+=l,x=+p[5],y=+p[6]}sp+=p.shift()+p}return subpaths.end=sp,point=istotal?len:subpath?subpaths:R.findDotsAtSegment(x,y,p[0],p[1],p[2],p[3],p[4],p[5],1),point.alpha&&(point={x:point.x,y:point.y,alpha:point.alpha}),point}},getTotalLength=getLengthFactory(1),getPointAtLength=getLengthFactory(),getSubpathsAtLength=getLengthFactory(0,1);R.getTotalLength=getTotalLength,R.getPointAtLength=getPointAtLength,R.getSubpath=function(path,from,to){if(this.getTotalLength(path)-to<1e-6)return getSubpathsAtLength(path,from).end;var a=getSubpathsAtLength(path,to,1);return from?getSubpathsAtLength(a,from).end:a},elproto.getTotalLength=function(){var path=this.getPath();if(path)return this.node.getTotalLength?this.node.getTotalLength():getTotalLength(path)},elproto.getPointAtLength=function(length){var path=this.getPath();if(path)return getPointAtLength(path,length)},elproto.getPath=function(){var path,getPath=R._getPath[this.type];if("text"!=this.type&&"set"!=this.type)return getPath&&(path=getPath(this)),path},elproto.getSubpath=function(from,to){var path=this.getPath();if(path)return R.getSubpath(path,from,to)};var ef=R.easing_formulas={linear:function(n){return n},"<":function(n){return pow(n,1.7)},">":function(n){return pow(n,.48)},"<>":function(n){var q=.48-n/1.04,Q=math.sqrt(.1734+q*q),x=Q-q,X=pow(abs(x),1/3)*(x<0?-1:1),y=-Q-q,Y=pow(abs(y),1/3)*(y<0?-1:1),t=X+Y+.5;return 3*(1-t)*t*t+t*t*t},backIn:function(n){var s=1.70158;return n*n*((s+1)*n-s)},backOut:function(n){n-=1;var s=1.70158;return n*n*((s+1)*n+s)+1},elastic:function(n){return n==!!n?n:pow(2,-10*n)*math.sin((n-.075)*(2*PI)/.3)+1},bounce:function(n){var l,s=7.5625,p=2.75;return n<1/p?l=s*n*n:n<2/p?(n-=1.5/p,l=s*n*n+.75):n<2.5/p?(n-=2.25/p,l=s*n*n+.9375):(n-=2.625/p,l=s*n*n+.984375),l}};ef.easeIn=ef["ease-in"]=ef["<"],ef.easeOut=ef["ease-out"]=ef[">"],ef.easeInOut=ef["ease-in-out"]=ef["<>"],ef["back-in"]=ef.backIn,ef["back-out"]=ef.backOut;var animationElements=[],requestAnimFrame=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(callback){setTimeout(callback,16)},animation=function(){for(var Now=+new Date,l=0;l1&&!e.next){for(key in to)to[has](key)&&(init[key]=e.totalOrigin[key]);e.el.attr(init),runAnimation(e.anim,e.el,e.anim.percents[0],null,e.totalOrigin,e.repeat-1)}e.next&&!e.stop&&runAnimation(e.anim,e.el,e.next,null,e.totalOrigin,e.repeat)}}}R.svg&&that&&that.paper&&that.paper.safari(),animationElements.length&&requestAnimFrame(animation)},upto255=function(color){return color>255?255:color<0?0:color};elproto.animateWith=function(el,anim,params,ms,easing,callback){var element=this;if(element.removed)return callback&&callback.call(element),element;var a=params instanceof Animation?params:R.animation(params,ms,easing,callback);runAnimation(a,element,a.percents[0],null,element.attr());for(var i=0,ii=animationElements.length;i.5)-1;pow(fx-.5,2)+pow(fy-.5,2)>.25&&(fy=math.sqrt(.25-pow(fx-.5,2))*dir+.5)&&.5!=fy&&(fy=fy.toFixed(5)-1e-5*dir)}return E}),gradient=gradient.split(/\s*\-\s*/),"linear"==type){var angle=gradient.shift();if(angle=-toFloat(angle),isNaN(angle))return null;var vector=[0,0,math.cos(R.rad(angle)),math.sin(R.rad(angle))],max=1/(mmax(abs(vector[2]),abs(vector[3]))||1);vector[2]*=max,vector[3]*=max,vector[2]<0&&(vector[0]=-vector[2],vector[2]=0),vector[3]<0&&(vector[1]=-vector[3],vector[3]=0)}var dots=R._parseDots(gradient);if(!dots)return null;if(id=id.replace(/[\(\)\s,\xb0#]/g,"_"),element.gradient&&id!=element.gradient.id&&(SVG.defs.removeChild(element.gradient),delete element.gradient),!element.gradient){el=$(type+"Gradient",{id:id}),element.gradient=el,$(el,"radial"==type?{fx:fx,fy:fy}:{x1:vector[0],y1:vector[1],x2:vector[2],y2:vector[3],gradientTransform:element.matrix.invert()}),SVG.defs.appendChild(el);for(var i=0,ii=dots.length;i1?clr.opacity/100:clr.opacity});case"stroke":clr=R.getRGB(value),node.setAttribute(att,clr.hex),"stroke"==att&&clr[has]("opacity")&&$(node,{"stroke-opacity":clr.opacity>1?clr.opacity/100:clr.opacity}),"stroke"==att&&o._.arrows&&("startString"in o._.arrows&&addArrow(o,o._.arrows.startString),"endString"in o._.arrows&&addArrow(o,o._.arrows.endString,1));break;case"gradient":("circle"==o.type||"ellipse"==o.type||"r"!=Str(value).charAt())&&addGradientFill(o,value);break;case"opacity":attrs.gradient&&!attrs[has]("stroke-opacity")&&$(node,{"stroke-opacity":value>1?value/100:value});case"fill-opacity":if(attrs.gradient){gradient=R._g.doc.getElementById(node.getAttribute("fill").replace(/^url\(#|\)$/g,E)),gradient&&(stops=gradient.getElementsByTagName("stop"),$(stops[stops.length-1],{"stop-opacity":value}));break}default:"font-size"==att&&(value=toInt(value,10)+"px");var cssrule=att.replace(/(\-.)/g,function(w){return w.substring(1).toUpperCase()});node.style[cssrule]=value,o._.dirty=1,node.setAttribute(att,value)}}tuneText(o,params),node.style.visibility=vis},leading=1.2,tuneText=function(el,params){if("text"==el.type&&(params[has]("text")||params[has]("font")||params[has]("font-size")||params[has]("x")||params[has]("y"))){var a=el.attrs,node=el.node,fontSize=node.firstChild?toInt(R._g.doc.defaultView.getComputedStyle(node.firstChild,E).getPropertyValue("font-size"),10):10;if(params[has]("text")){for(a.text=params.text;node.firstChild;)node.removeChild(node.firstChild);for(var tspan,texts=Str(params.text).split("\n"),tspans=[],i=0,ii=texts.length;i"));var brect=span.getBoundingClientRect();res.W=a.w=(brect.right-brect.left)/m,res.H=a.h=(brect.bottom-brect.top)/m,res.X=a.x,res.Y=a.y+res.H/2,("x"in params||"y"in params)&&(res.path.v=R.format("m{0},{1}l{2},{1}",round(a.x*zoom),round(a.y*zoom),round(a.x*zoom)+1));for(var dirtyattrs=["x","y","text","font","font-family","font-weight","font-style","font-size"],d=0,dd=dirtyattrs.length;d.25&&(fy=math.sqrt(.25-pow(fx-.5,2))*(2*(fy>.5)-1)+.5),fxfy=fx+S+fy),E}),gradient=gradient.split(/\s*\-\s*/),"linear"==type){var angle=gradient.shift();if(angle=-toFloat(angle),isNaN(angle))return null}var dots=R._parseDots(gradient);if(!dots)return null;if(o=o.shape||o.node,dots.length){o.removeChild(fill),fill.on=!0,fill.method="none",fill.color=dots[0].color,fill.color2=dots[dots.length-1].color;for(var clrs=[],i=0,ii=dots.length;i')}}catch(e){createNode=function(tagName){return doc.createElement("<"+tagName+' xmlns="urn:schemas-microsoft.com:vml" class="rvml">')}}},R._engine.initWin(R._g.win),R._engine.create=function(){var con=R._getContainer.apply(0,arguments),container=con.container,height=con.height,width=con.width,x=con.x,y=con.y;if(!container)throw new Error("VML container not found.");var res=new R._Paper,c=res.canvas=R._g.doc.createElement("div"),cs=c.style;return x=x||0,y=y||0,width=width||512,height=height||342,res.width=width,res.height=height,width==+width&&(width+="px"),height==+height&&(height+="px"),res.coordsize=1e3*zoom+S+1e3*zoom,res.coordorigin="0 0",res.span=R._g.doc.createElement("span"),res.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;",c.appendChild(res.span),cs.cssText=R.format("top:0;left:0;width:{0};height:{1};display:inline-block;position:relative;clip:rect(0 {0} {1} 0);overflow:hidden",width,height),1==container?(R._g.doc.body.appendChild(c),cs.left=x+"px",cs.top=y+"px",cs.position="absolute"):container.firstChild?container.insertBefore(c,container.firstChild):container.appendChild(c),res.renderfix=function(){},res},R.prototype.clear=function(){R.eve("raphael.clear",this),this.canvas.innerHTML=E,this.span=R._g.doc.createElement("span"),this.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;",this.canvas.appendChild(this.span),this.bottom=this.top=null},R.prototype.remove=function(){R.eve("raphael.remove",this),this.canvas.parentNode.removeChild(this.canvas);for(var i in this)this[i]="function"==typeof this[i]?R._removedFactory(i):null;return!0};var setproto=R.st;for(var method in elproto)elproto[has](method)&&!setproto[has](method)&&(setproto[method]=function(methodname){return function(){var arg=arguments;return this.forEach(function(el){el[methodname].apply(el,arg)})}}(method))}}(),oldRaphael.was?g.win.Raphael=R:Raphael=R,R}),window.ABCJS||(window.ABCJS={}),function(){"use strict";function hasClass(element,cls){var elClass=element.getAttribute("class"),rclass=/[\t\r\n\f]/g,className=" "+cls+" ";return 1===element.nodeType&&(" "+elClass+" ").replace(rclass," ").indexOf(className)>=0}function getAllElementsByClasses(startingEl,class1,class2){for(var els=startingEl.getElementsByClassName(class1),ret=[],i=0;iouter.offsetWidth-paper.scrollWidth&&(scrollTimer=setTimeout(scrolling,interval))}function processMeasureHider(lineNum,measureNum){var els=getAllElementsByClasses(paper,"l"+lineNum,"m"+measureNum);if(els.length>0)for(var i=0;i=0;e--){var ev=timingEvents[e];"bar"===ev.type?(ev.top=lastEventTop,ev.nextTop=lastBarTop,lastBarTop=lastEventTop,ev.bottom=lastEventBottom,ev.nextBottom=lastBarBottom,lastBarBottom=lastEventBottom):"event"===ev.type&&(lastEventTop=ev.top,lastEventBottom=ev.top+ev.height)}}function makeSortedArray(hash){var arr=[];for(var k in hash)hash.hasOwnProperty(k)&&arr.push(hash[k]);return arr=arr.sort(function(a,b){var diff=a.time-b.time;return 0!==diff?diff:"bar"===a.type?-1:1})}function setupEvents(engraver){for(var eventHash={},time=0,isTiedState=!1,line=0;line0){var isTiedToNext=element.startTie;isTiedState?isTiedToNext||(isTiedState=!1):(eventHash["event"+voiceTime]?eventHash["event"+voiceTime].left=Math.min(eventHash["event"+voiceTime].left,element.x):eventHash["event"+voiceTime]={type:"event",time:voiceTime,top:top,height:height,left:element.x,width:element.w},isTiedToNext&&(isTiedState=!0)),voiceTime+=element.duration}if("bar"===element.type&&(0===timingEvents.length||"bar"!==timingEvents[timingEvents.length-1])&&element.elemset&&element.elemset.length>0&&element.elemset[0].attrs){for(var lineNum,measureNum,klass=element.elemset[0].attrs.class,arr=klass.split(" "),i=0;i0?timingEvents[0].time/beatLength:0):(options.scrollHint&&lastTop!==currentNote.top&&(lastTop=currentNote.top,setMargin(lastTop)),options.showCursor&&cursor.css({left:currentNote.left+"px",top:currentNote.top+"px",width:currentNote.width+"px",height:currentNote.height+"px"}),timingEvents.length>0?timingEvents[0].time/beatLength:(stopNextTime=!0,0)):(stopNextTime=!0,0)}function processNext(){if(stopNextTime)return void ABCJS.stopAnimation();var currentTime=(new Date).getTime();if(isPaused)return void(pausedDifference=currentTime-pausedTime);var nextTimeInBeats=processShowCursor(),nextTimeInMilliseconds=nextTimeInBeats/beatsPerMillisecond,interval=startTime+nextTimeInMilliseconds-currentTime;interval<=0?processNext():animateTimer=setTimeout(processNext,interval)}if(void 0===paper.getElementsByClassName)return void console.error("ABCJS.startAnimation: The first parameter must be a regular DOM element. (Did you pass a jQuery object or an ID?)");if(void 0===tune.getBeatLength)return void console.error("ABCJS.startAnimation: The second parameter must be a single tune. (Did you pass the entire array of tunes?)");if((options.scrollHorizontal||options.scrollVertical||options.scrollHint)&&(hasClass(paper,"abcjs-inner")||(paper.scrollTop=0,paper.style.overflow="hidden",paper=paper.children[0]),!hasClass(paper,"abcjs-inner")))return void console.error("ABCJS.startAnimation: When using scrollHorizontal/scrollVertical/scrollHint, the music must have been rendered using viewportHorizontal/viewportVertical.");ABCJS.stopAnimation(),animationTarget=paper,shouldResetOverflow=options.scrollVertical||options.scrollHint,options.showCursor&&(cursor=$('
    '),$(paper).append(cursor),$(paper).css({position:"relative"})),stopNextTime=!1;var millisecondsPerHalfMeasure,beatsPerMinute=getBeatsPerMinute(tune,options),beatsPerMillisecond=beatsPerMinute/6e4,beatLength=tune.getBeatLength(),totalBeats=0;if(options.scrollVertical){var millisecondsPerBeat=1/beatsPerMillisecond,beatsPerMeasure=1/beatLength,millisecondsPerMeasure=millisecondsPerBeat*beatsPerMeasure;millisecondsPerHalfMeasure=millisecondsPerMeasure/2,cssRule=getCssRule(".abcjs-inner")}var startTime,pausedTime,pausedDifference,isPaused=!1,initialWait=2700,interval=11,distance=1,outer=paper.parentNode;options.scrollHorizontal&&(paper.style.marginLeft="0px",scrollTimer=setTimeout(scrolling,initialWait));var timingEvents=[];setupEvents(tune.engraver);var lastTop=-1;$(outer).find(".abcjs-inner");currentMargin=0,options.scrollVertical&&setMargin(0),startTime=new Date,startTime=startTime.getTime(),isPaused=!1,processNext(),ABCJS.pauseAnimation=function(pause){if(pause&&!isPaused)isPaused=!0,pausedTime=(new Date).getTime();else if(!pause&&isPaused){var nowTime=(new Date).getTime(),elapsedTimeWhenPaused=nowTime-pausedTime;startTime+=elapsedTimeWhenPaused,pausedTime=void 0,isPaused=!1,animateTimer=setTimeout(processNext,pausedDifference),pausedDifference=void 0}}},ABCJS.stopAnimation=function(){clearTimeout(animateTimer),clearTimeout(scrollTimer),cursor&&(cursor.remove(),cursor=null),shouldResetOverflow&&(animationTarget&&animationTarget.parentNode&&(animationTarget.parentNode.style.overflowY="auto"),setMargin(0))}}(),window.ABCJS||(window.ABCJS={}),function(){"use strict";function renderEngine(callback,output,abc,parserParams,renderParams){var ret=[],isArray=function(testObject){return testObject&&!testObject.propertyIsEnumerable("length")&&"object"==typeof testObject&&"number"==typeof testObject.length};if(void 0!==output&&void 0!==abc){isArray(output)||(output=[output]),void 0===parserParams&&(parserParams={}),void 0===renderParams&&(renderParams={});for(var currentTune=renderParams.startingTune?renderParams.startingTune:0,book=new ABCJS.TuneBook(abc),abcParser=new window.ABCJS.parse.Parse,i=0;i',renderParams.scrollHorizontal?(div.style.overflowX="auto",div.style.overflowY="hidden"):div.style.overflow="hidden",resizeDivs[div.id]=div,div=div.children[0]):renderParams.viewportVertical&&(div.innerHTML='
    ',div.style.overflowX="hidden",div.style.overflowY="auto",div=div.children[0]);var paper=Raphael(div,width,400);void 0===engraverParams&&(engraverParams={});var engraver_controller=new ABCJS.write.EngraverController(paper,engraverParams);if(engraver_controller.engraveABC(tune),tune.engraver=engraver_controller,renderParams.viewportVertical||renderParams.viewportHorizontal){var parent=div.parentNode;parent.style.width=div.style.width}}function renderEachLineSeparately(div,tune,renderParams,engraverParams){function initializeTuneLine(tune){return{formatting:tune.formatting,media:tune.media,version:tune.version,metaText:{},lines:[]}}for(var tuneLine,tunes=[],i=0;i1&&!window.ABCJS.parse.startsWith(This.tunes[0].abc,"X:")){var dir=This.tunes.shift(),arrDir=dir.abc.split("\n");window.ABCJS.parse.each(arrDir,function(line){window.ABCJS.parse.startsWith(line,"%%")&&(directives+=line+"\n")})}This.header=directives,window.ABCJS.parse.each(This.tunes,function(tune){var end=tune.abc.indexOf("\n\n");end>0&&(tune.abc=tune.abc.substring(0,end)),tune.pure=tune.abc,tune.abc=directives+tune.abc;var title=tune.pure.split("T:");title.length>1?(title=title[1].split("\n"),tune.title=title[0].replace(/^\s+|\s+$/g,"")):tune.title="";var id=tune.pure.substring(2,tune.pure.indexOf("\n"));tune.id=id.replace(/^\s+|\s+$/g,"")})},ABCJS.TuneBook.prototype.getTuneById=function(id){for(var i=0;i=0;e--){var ev=timingEvents[e];"bar"===ev.type?(ev.top=lastEventTop,ev.nextTop=lastBarTop,lastBarTop=lastEventTop,ev.bottom=lastEventBottom,ev.nextBottom=lastBarBottom,lastBarBottom=lastEventBottom):"event"===ev.type&&(lastEventTop=ev.top,lastEventBottom=ev.top+ev.height)}}function makeSortedArray(hash){var arr=[];for(var k in hash)hash.hasOwnProperty(k)&&arr.push(hash[k]);return arr=arr.sort(function(a,b){var diff=a.seconds-b.seconds;return 0!==diff?diff:"bar"===a.type?-1:1})}this.getBeatLength=function(){for(var i=0;i0){var num=parseInt(meter.value[0].num,10),den=parseInt(meter.value[0].den,10);return 6===num&&8===den?3/8:9===num&&8===den?3/8:12===num&&8===den?3/8:1/den}return null}return"cut_time"===meter.type?.5:.25}return null},this.reset=function(){this.version="1.0.1",this.media="screen",this.metaText={},this.formatting={},this.lines=[],this.staffNum=0,this.voiceNum=0,this.lineNum=0},this.cleanUp=function(defWidth,defLength,barsperstaff,staffnonote,currSlur){function cleanUpSlursInLine(line){for(var x,addEndSlur=function(obj,num,chordPos){if(void 0===currSlur[chordPos]){for(x=0;xcurrentLine;){if(lines[currentLine].staff)return lines[currentLine];currentLine++}return null}this.closeLine();var i,s,v,anyDeleted=!1;for(i=0;i=barsperstaff&&n0&&voice[voice.length-1].barNumber){var nextLine=getNextMusicLine(this.lines,this.lineNum);nextLine&&(nextLine.staff[0].barNumber=voice[voice.length-1].barNumber),delete voice[voice.length-1].barNumber}}}return this.formatting.pagewidth||(this.formatting.pagewidth=defWidth),this.formatting.pageheight||(this.formatting.pageheight=defLength),delete this.staffNum,delete this.voiceNum,delete this.lineNum,delete this.potentialStartBeam,delete this.potentialEndBeam,delete this.vskipPending,currSlur},this.reset(),this.getLastNote=function(){if(this.lines[this.lineNum]&&this.lines[this.lineNum].staff&&this.lines[this.lineNum].staff[this.staffNum]&&this.lines[this.lineNum].staff[this.staffNum].voices[this.voiceNum])for(var i=this.lines[this.lineNum].staff[this.staffNum].voices[this.voiceNum].length-1;i>=0;i--){var el=this.lines[this.lineNum].staff[this.staffNum].voices[this.voiceNum][i];if("note"===el.el_type)return el}return null},this.addTieToLastNote=function(){var el=this.getLastNote();return!!(el&&el.pitches&&el.pitches.length>0)&&(el.pitches[0].startTie={},!0)},this.getDuration=function(el){return el.duration?el.duration:0},this.closeLine=function(){this.potentialStartBeam&&this.potentialEndBeam&&(this.potentialStartBeam.startBeam=!0,this.potentialEndBeam.endBeam=!0),delete this.potentialStartBeam,delete this.potentialEndBeam},this.appendElement=function(type,startChar,endChar,hashParams){var This=this,pushNote=function(hp){if(void 0!==hp.pitches){var mid=This.lines[This.lineNum].staff[This.staffNum].workingClef.verticalPos;window.ABCJS.parse.each(hp.pitches,function(p){p.verticalPos=p.pitch-mid})}if(void 0!==hp.gracenotes){var mid2=This.lines[This.lineNum].staff[This.staffNum].workingClef.verticalPos;window.ABCJS.parse.each(hp.gracenotes,function(p){p.verticalPos=p.pitch-mid2})}This.lines[This.lineNum].staff[This.staffNum].voices[This.voiceNum].push(hp)};hashParams.el_type=type,null!==startChar&&(hashParams.startChar=startChar),null!==endChar&&(hashParams.endChar=endChar);var endBeamHere=function(){This.potentialStartBeam.startBeam=!0,hashParams.endBeam=!0,delete This.potentialStartBeam,delete This.potentialEndBeam},endBeamLast=function(){void 0!==This.potentialStartBeam&&void 0!==This.potentialEndBeam&&(This.potentialStartBeam.startBeam=!0,This.potentialEndBeam.endBeam=!0),delete This.potentialStartBeam,delete This.potentialEndBeam};if("note"===type){var dur=This.getDuration(hashParams);dur>=.25?endBeamLast():hashParams.force_end_beam_last&&void 0!==This.potentialStartBeam?endBeamLast():hashParams.end_beam&&void 0!==This.potentialStartBeam?void 0===hashParams.rest?endBeamHere():endBeamLast():void 0===hashParams.rest&&(void 0===This.potentialStartBeam?hashParams.end_beam||(This.potentialStartBeam=hashParams,delete This.potentialEndBeam):This.potentialEndBeam=hashParams)}else endBeamLast();delete hashParams.end_beam,delete hashParams.force_end_beam_last,pushNote(hashParams)},this.appendStartingElement=function(type,startChar,endChar,hashParams2){this.closeLine();var impliedNaturals;"key"===type&&(impliedNaturals=hashParams2.impliedNaturals,delete hashParams2.impliedNaturals);var hashParams=window.ABCJS.parse.clone(hashParams2);if(this.lines[this.lineNum].staff){"clef"===type&&(this.lines[this.lineNum].staff[this.staffNum].workingClef=hashParams),this.lines[this.lineNum].staff.length<=this.staffNum&&(this.lines[this.lineNum].staff[this.staffNum]={},this.lines[this.lineNum].staff[this.staffNum].clef=window.ABCJS.parse.clone(this.lines[this.lineNum].staff[0].clef),this.lines[this.lineNum].staff[this.staffNum].key=window.ABCJS.parse.clone(this.lines[this.lineNum].staff[0].key),this.lines[this.lineNum].staff[this.staffNum].meter=window.ABCJS.parse.clone(this.lines[this.lineNum].staff[0].meter),this.lines[this.lineNum].staff[this.staffNum].workingClef=window.ABCJS.parse.clone(this.lines[this.lineNum].staff[0].workingClef),this.lines[this.lineNum].staff[this.staffNum].voices=[[]]);for(var voice=this.lines[this.lineNum].staff[this.staffNum].voices[this.voiceNum],i=0;i0){if(void 0!==This.lines[This.lineNum].staff[This.staffNum].voices[0]){for(var found=!1,i=0;i=0;i--)if(void 0!==this.lines[i].staff)return!1;return!0},this.getMeter=function(){for(var i=0;i0){var isTiedToNext=element.startTie;isTiedState?isTiedToNext||(isTiedState=!1):(eventHash["event"+voiceTime]?(eventHash["event"+voiceTime].left=Math.min(eventHash["event"+voiceTime].left,element.x),eventHash["event"+voiceTime].elements.push(element.elemset)):eventHash["event"+voiceTime]={type:"event",seconds:voiceTime,top:top,height:height,left:element.x,width:element.w,elements:[element.elemset]},isTiedToNext&&(isTiedState=!0)),voiceTime+=element.duration/timeDivider}}maxVoiceTime=Math.max(maxVoiceTime,voiceTime)}time=maxVoiceTime}return timingEvents=makeSortedArray(eventHash),addVerticalInfo(timingEvents),timingEvents},this.setTiming=function(bpm,measuresOfDelay){var meter=this.getMeter();this.barTimings=[];var measureLength,beatLength=this.getBeatLength(),beatsPerSecond=bpm/60;switch(meter.type){case"common_time":measureLength=1,this.meter={num:4,den:4};break;case"cut_time":measureLength=1,this.meter={num:2,den:2};break;default:measureLength=meter.value[0].num/meter.value[0].den,this.meter={num:meter.value[0].num,den:meter.value[0].den}}var startingDelay=measureLength/beatLength*measuresOfDelay/beatsPerSecond,timeDivider=beatLength*beatsPerSecond;this.noteTimings=this.setupEvents(startingDelay,timeDivider)}},window.ABCJS||(window.ABCJS={}),window.ABCJS.midi||(window.ABCJS.midi={}),function(){"use strict";function isFunction(functionToCheck){var getType={};return functionToCheck&&"[object Function]"===getType.toString.call(functionToCheck)}function preprocessLabel(label,title){return label.replace(/%T/g,title)}function hasClass(element,cls){return!!element&&(" "+element.className+" ").indexOf(" "+cls+" ")>-1}function addClass(element,cls){element&&(hasClass(element,cls)||(element.className=element.className+" "+cls))}function removeClass(element,cls){element&&(element.className=element.className.replace(cls,"").trim().replace(" "," "))}function toggleClass(element,cls){element&&(hasClass(element,cls)?removeClass(element,cls):addClass(element,cls))}function closest(element,cls){if(!element)return null;for(;element!==document.body;){if(hasClass(element,cls))return element;element=element.parentNode}return null}function find(element,cls){if(!element)return null;var els=element.getElementsByClassName(cls);return 0===els.length?null:els[0]}function addLoadEvent(func){var oldOnLoad=window.onload;"function"!=typeof window.onload?window.onload=func:window.onload=function(){oldOnLoad&&oldOnLoad(),func()}}function afterSetup(timeWarp,data,onSuccess){MIDI.player.currentTime=0,MIDI.player.warp=timeWarp,MIDI.player.load({events:data}),onSuccess()}function setCurrentMidiTune(timeWarp,data,onSuccess){midiJsInitialized?afterSetup(timeWarp,data,onSuccess):MIDI.setup({debug:!0,soundfontUrl:window.ABCJS.midi.soundfontUrl}).then(function(){midiJsInitialized=!0,afterSetup(timeWarp,data,onSuccess)})}function startCurrentlySelectedTune(){MIDI.player.start(MIDI.player.currentTime)}function stopCurrentlyPlayingTune(){MIDI.player.stop()}function pauseCurrentlyPlayingTune(){MIDI.player.pause()}function setMidiCallback(midiJsListener){MIDI.player.setAnimation(midiJsListener)}function jumpToMidiPosition(play,offset,width){var ratio=offset/width,endTime=MIDI.player.duration;play&&pauseCurrentlyPlayingTune(),MIDI.player.currentTime=endTime*ratio,play&&startCurrentlySelectedTune()}function setTimeWarp(percent){MIDI.player.warp=percent>0?100/percent:1}function loadMidi(target,onSuccess){var dataEl=find(target,"abcjs-data"),data=JSON.parse(dataEl.innerHTML),timeWarp=1,tempoEl=find(target,"abcjs-midi-tempo");if(tempoEl){var percent=parseInt(tempoEl.value,10);percent>0&&(timeWarp=100/percent)}setCurrentMidiTune(timeWarp,data,onSuccess)}function deselectMidiControl(){var otherMidi=find(document,"abcjs-midi-current");if(otherMidi){stopCurrentlyPlayingTune(),removeClass(otherMidi,"abcjs-midi-current");var otherMidiStart=find(otherMidi,"abcjs-midi-start");removeClass(otherMidiStart,"abcjs-pushed")}}function findElements(visualItems,currentTime,epsilon){for(var currentIndex,currentElement,minIndex=0,maxIndex=visualItems.length-1;minIndex<=maxIndex;)if(currentIndex=(minIndex+maxIndex)/2|0,currentElement=visualItems[currentIndex],currentElement.seconds-epsiloncurrentTime))return currentIndex;maxIndex=currentIndex-1}for(;visualItems[currentIndex].seconds-epsilon>=currentTime&¤tIndex>0;)currentIndex--;return 0===currentIndex&&visualItems[currentIndex].seconds-epsilon>=currentTime?-1:currentIndex}function midiJsListener(position){var midiControl;if(position.duration>0&&lastNow!==position.progress&&(lastNow=position.progress,midiControl=find(document,"abcjs-midi-current"))){var startButton=find(midiControl,"abcjs-midi-start");if(hasClass(startButton,"abcjs-pushed")){var progressBackground=find(midiControl,"abcjs-midi-progress-background"),totalWidth=progressBackground.offsetWidth,progressIndicator=find(midiControl,"abcjs-midi-progress-indicator"),scaled=totalWidth*lastNow;progressIndicator.style.left=scaled+"px";var clock=find(midiControl,"abcjs-midi-clock");if(clock){var seconds=Math.floor(position.currentTime),minutes=Math.floor(seconds/60);seconds%=60,seconds<10&&(seconds="0"+seconds),minutes<10&&(minutes=" "+minutes),clock.innerHTML=minutes+":"+seconds}var beatsPerSecond=parseInt(midiControl.abcjsQpm,10)/60,currentTime=position.currentTime;if(midiControl.abcjsListener){var thisBeat=Math.floor(currentTime/beatsPerSecond);position.newBeat=thisBeat!==midiControl.abcjsLastBeat,midiControl.abcjsLastBeat=thisBeat,midiControl.abcjsListener(midiControl,position)}if(midiControl.abcjsAnimate){var epsilon=beatsPerSecond/64,index=findElements(midiControl.abcjsTune.noteTimings,currentTime,epsilon);if(index!==midiControl.abcjsLastIndex){var last=midiControl.abcjsLastIndex>=0?midiControl.abcjsTune.noteTimings[midiControl.abcjsLastIndex]:null;midiControl.abcjsAnimate(last,midiControl.abcjsTune.noteTimings[index]),midiControl.abcjsLastIndex=index}}}}if(1===position.progress){midiControl=find(document,"abcjs-midi-current");var loopControl=find(midiControl,"abcjs-midi-loop"),finishedResetting=function(){loopControl&&hasClass(loopControl,"abcjs-pushed")&&onStart(find(midiControl,"abcjs-midi-start"))};setTimeout(function(){doReset(midiControl,finishedResetting),midiControl.abcjsAnimate&&midiControl.abcjsAnimate(midiControl.abcjsTune.noteTimings[midiControl.abcjsLastIndex],null)},1)}}function onStart(target){var parent=closest(target,"abcjs-inline-midi");if(hasClass(target,"abcjs-pushed"))pauseCurrentlyPlayingTune(),removeClass(target,"abcjs-pushed");else{if(hasClass(parent,"abcjs-midi-current"))startCurrentlySelectedTune();else{deselectMidiControl();var onSuccess=function(){startCurrentlySelectedTune(),addClass(parent,"abcjs-midi-current")};loadMidi(parent,onSuccess)}addClass(target,"abcjs-pushed")}parent.abcjsLastBeat=-1,parent.abcjsLastIndex=-1,setMidiCallback(midiJsListener)}function onSelection(target){toggleClass(target,"abcjs-pushed")}function onLoop(target){toggleClass(target,"abcjs-pushed")}function doReset(target,callback){function onSuccess(){addClass(parent,"abcjs-midi-current");var progressIndicator=find(parent,"abcjs-midi-progress-indicator");progressIndicator.style.left="0px";var clock=find(parent,"abcjs-midi-clock");clock.innerHTML=" 0:00",callback&&callback()}var parent=closest(target,"abcjs-inline-midi");deselectMidiControl(),parent&&loadMidi(parent,onSuccess)}function onReset(target){function keepPlaying(){play&&(startCurrentlySelectedTune(),addClass(playEl,"abcjs-pushed"))}var parent=closest(target,"abcjs-inline-midi"),playEl=find(parent,"abcjs-midi-start"),play=hasClass(playEl,"abcjs-pushed");hasClass(parent,"abcjs-midi-current")&&doReset(target,keepPlaying)}function relMouseX(target,event){var totalOffsetX=0;do totalOffsetX+=target.offsetLeft-target.scrollLeft,target=target.offsetParent;while(target);return event.pageX-totalOffsetX}function onProgress(target,event){var parent=closest(target,"abcjs-inline-midi");if(hasClass(parent,"abcjs-midi-current")){var play=find(parent,"abcjs-midi-start");play=hasClass(play,"abcjs-pushed");var width=target.offsetWidth,offset=relMouseX(target,event);jumpToMidiPosition(play,offset,width)}}function onTempo(el){for(var percent=parseInt(el.value,10),startTempo=parseInt(el.getAttribute("data-start-tempo"),10);el&&!hasClass(el,"abcjs-midi-current-tempo");)el=el.nextSibling;el.innerHTML=Math.floor(percent*startTempo/100),setTimeWarp(percent)}function addDelegates(){if(document.body.addEventListener("click",function(event){event=event||window.event;for(var target=event.target||event.srcElement;target&&target!==document.body;){if(hasClass(target,"abcjs-midi-start"))return void onStart(target,event);if(hasClass(target,"abcjs-midi-selection"))return void onSelection(target,event);if(hasClass(target,"abcjs-midi-loop"))return void onLoop(target,event);if(hasClass(target,"abcjs-midi-reset"))return void onReset(target,event);if(hasClass(target,"abcjs-midi-progress-background"))return void onProgress(target,event);target=target.parentNode}}),document.body.addEventListener("change",function(event){event=event||window.event;for(var target=event.target||event.srcElement;target!==document.body;)hasClass(target,"abcjs-midi-tempo")&&onTempo(target,event),target=target.parentNode}),void 0===window.MIDI){window.ABCJS.midi.midiInlineInitialized="not loaded";for(var els=document.getElementsByClassName("abcjs-inline-midi"),i=0;i';midiParams.preTextDownload&&(html+=midiParams.preTextDownload);var label,title=tune.metaText&&tune.metaText.title?tune.metaText.title:"Untitled";return label=midiParams.downloadLabel&&isFunction(midiParams.downloadLabel)?midiParams.downloadLabel(tune,index):midiParams.downloadLabel?midiParams.downloadLabel.replace(/%T/,title):'Download MIDI for "'+title+'"',title=title.toLowerCase().replace(/'/g,"").replace(/\W/g,"_").replace(/__/g,"_"),html+=''+label+"",midiParams.postTextDownload&&(html+=midiParams.postTextDownload),html+""},window.ABCJS.midi.generateMidiControls=function(tune,midiParams,midi,index){if("failed"===window.ABCJS.midiInlineInitialized)return'
    ERROR
    ';if("not loaded"===window.ABCJS.midiInlineInitialized)return'
    MIDI NOT PRESENT
    ';var title=tune.metaText&&tune.metaText.title?tune.metaText.title:"Untitled",options=midiParams.inlineControls||{};void 0===options.standard&&(options.standard=!0),void 0===options.tooltipSelection&&(options.tooltipSelection="Click to toggle play selection/play all."),void 0===options.tooltipLoop&&(options.tooltipLoop="Click to toggle play once/repeat."),void 0===options.tooltipReset&&(options.tooltipReset="Click to go to beginning."),void 0===options.tooltipPlay&&(options.tooltipPlay="Click to play/pause."),void 0===options.tooltipProgress&&(options.tooltipProgress="Click to change the playback position."), void 0===options.tooltipTempo&&(options.tooltipTempo="Change the playback speed.");var style="";options.hide&&(style='style="display:none;"');var html='
    ";if(html+='",midiParams.preTextInline&&(html+=''+preprocessLabel(midiParams.preTextInline,title)+""),options.selectionToggle&&(html+=''),options.loopToggle&&(html+=''),options.standard&&(html+=' 0:00'),options.tempo){var startTempo=tune&&tune.metaText&&tune.metaText.tempo?tune.metaText.tempo.bpm:180;html+='% ('+startTempo+" BPM)"}return midiParams.postTextInline&&(html+=''+preprocessLabel(midiParams.postTextInline,title)+""),html+"
    "},window.ABCJS.midi.soundfontUrl="/soundfont/";var lastNow,midiJsInitialized=!1;window.ABCJS.midi.startPlaying=function(target){onStart(target)},window.ABCJS.midi.stopPlaying=function(){stopCurrentlyPlayingTune()},addLoadEvent(addDelegates)}(),window.ABCJS||(window.ABCJS={}),window.ABCJS.midi||(window.ABCJS.midi={}),function(){"use strict";function convertPitch(pitch){return 60+pitch}var baseDuration=1920;window.ABCJS.midi.create=function(abcTune,options){void 0===options&&(options={});var sequence=window.ABCJS.midi.sequence(abcTune,options),commands=window.ABCJS.midi.flatten(sequence,options),midi=window.ABCJS.midi.rendererFactory(),midiJs=new window.ABCJS.midi.Preparer,title=abcTune.metaText?abcTune.metaText.title:void 0;title&&title.length>128&&(title=title.substring(0,124)+"..."),midi.setGlobalInfo(commands.tempo,title),midiJs.setGlobalInfo(commands.tempo,title),void 0!==commands.instrument&&(midi.setInstrument(commands.instrument),midiJs.setInstrument(commands.instrument)),void 0!==commands.channel&&(midi.setChannel(commands.channel),midiJs.setChannel(commands.channel));for(var i=0;i=0)return"break"}return null}function timeFromStart(){for(var distance=0,ct=0;ct0&&chordTrack.push({cmd:"move",duration:distance*tempoChangeFactor})}lastChord=c,currentChords.push({chord:lastChord,beat:barBeat})}}elem.startTriplet&&(multiplier=2===elem.startTriplet?1.5:(elem.startTriplet-1)/elem.startTriplet);var duration=elem.duration*multiplier;barBeat+=duration;var graces;if(elem.gracenotes){var stealFromCurrent=bagpipes||lastNoteDurationPosition<0||0===currentTrack.length,stealFromDuration=stealFromCurrent?duration:currentTrack[lastNoteDurationPosition].duration;graces=processGraceNotes(elem.gracenotes,stealFromDuration),bagpipes||(duration=writeGraceNotes(graces,stealFromCurrent,duration,null,velocity))}if(elem.pitches){graces&&bagpipes&&(duration=writeGraceNotes(graces,!0,duration,null,velocity));for(var pitches=[],i=0;icompanionDuration?companionDuration/(2*graceDuration):1;for(g=0;g.0078125){pattern=[];for(var beatsPresent=barBeat/beatLength,p=0;p0&&distance0&&(resolveChords(),currentChords=[]),barBeat=0,barAccidentals=[],0===i&&writeDrum();break;case"bagpipes":bagpipes=!0;break;case"instrument":instrument=element.program;break;case"channel":channel=element.channel;break;case"drum":drumDefinition=normalizeDrumDefinition(element.params);break;default:console.log("MIDI creation. Unknown el_type: "+element.el_type+"\n")}}tracks.push(currentTrack),chordTrack.length>0&&(chordTrackFinished=!0),drumTrack.length>0&&(drumTrackFinished=!0)}chordTrack.length>0&&tracks.push(chordTrack),drumTrack.length>0&&tracks.push(drumTrack);var num=parseInt(startingMeter.num,10),den=parseInt(startingMeter.den,10);return 2===den?startingTempo*=2:8===den?parseInt(num,10)%3===0?startingTempo*=1.5:startingTempo/=2:16===den&&(num%3===0?startingTempo*=.75:startingTempo/=4),{tempo:startingTempo,instrument:instrument,channel:channel,tracks:tracks}};var breakSynonyms=["break","(break)","no chord","n.c.","tacet"],scale=[0,2,4,5,7,9,11],graceDivider=8,basses={A:-27,B:-25,C:-24,D:-22,E:-20,F:-19,G:-17},chordIntervals={M:[0,4,7],6:[0,4,7,9],7:[0,4,7,10],"+7":[0,4,8,10],aug7:[0,4,8,10],maj7:[0,4,7,11],"∆7":[0,4,7,11],9:[0,4,7,10,14],11:[0,4,7,10,14,16],13:[0,4,7,10,14,18],"+":[0,4,8],"7#5":[0,4,8,10],"7+5":[0,4,8,10],"7b9":[0,4,7,10,13],"7b5":[0,4,6,10],"9#5":[0,4,8,10,14],"9+5":[0,4,8,10,14],m:[0,3,7],"-":[0,3,7],m6:[0,3,7,9],"-6":[0,3,7,9],m7:[0,3,7,10],"-7":[0,3,7,10],dim:[0,3,6],dim7:[0,3,6,9],"°7":[0,3,6,9],"ø7":[0,3,6,9],"7sus4":[0,5,7,10],m7sus4:[0,5,7,10],sus4:[0,5,7]},rhythmPatterns={"2/2":["boom","chick"],"2/4":["boom","chick"],"3/4":["boom","chick","chick"],"4/4":["boom","chick","boom2","chick"],"6/8":["boom","","chick","boom2","","chick"]}}(),window.ABCJS||(window.ABCJS={}),window.ABCJS.midi||(window.ABCJS.midi={}),function(){"use strict";function addAbsoluteTime(tracks){for(var i=0;ib.absTime?1:-1})}function adjustTime(output){for(var lastTime=0,i=0;i>=7;for(var i=a.length-1;i>=0;i--){res<<=8;var bits=a[i];0!==i&&(bits|=128),res|=bits}var padding=res.toString(16).length;return padding+=padding%2,toHex(res,padding)}Midi.prototype.setTempo=function(qpm){0===this.trackcount&&(this.startTrack(),this.track+="%00%FF%51%03"+toHex(Math.round(6e7/qpm),6),this.endTrack())},Midi.prototype.setGlobalInfo=function(qpm,name){if(0===this.trackcount){if(this.startTrack(),this.track+="%00%FF%51%03"+toHex(Math.round(6e7/qpm),6),name){this.track+="%00%FF%03"+toHex(name.length,2);for(var i=0;i1&&(channel=globals.program[1])),globals.transpose&&(transpose=globals.transpose[0]),globals.channel&&(channel=globals.channel[0]),globals.drum&&(drumPattern=globals.drum),globals.drumbars&&(drumBars=globals.drumbars[0]),globals.drumon&&(drumOn=!0)}abctune.metaText.tempo&&(qpm=interpretTempo(abctune.metaText.tempo)),options.qpm&&(qpm=parseInt(options.qpm,10));var startVoice=[];bagpipes&&startVoice.push({el_type:"bagpipes"}),startVoice.push({el_type:"instrument",program:program}),channel&&startVoice.push({el_type:"channel",channel:channel}),transpose&&startVoice.push({el_type:"transpose",transpose:transpose}),startVoice.push({el_type:"tempo",qpm:qpm});for(var voices=[],startRepeatPlaceholder=[],skipEndingPlaceholder=[],startingDrumSet=!1,i=0;i0&&voices[voiceNumber].push({el_type:"bar"}),noteEventsInBar=0;var endRepeat="bar_right_repeat"===elem.type||"bar_dbl_repeat"===elem.type,startEnding="1"===elem.startEnding,startRepeat="bar_left_repeat"===elem.type||"bar_dbl_repeat"===elem.type||"bar_thick_thin"===elem.type||"bar_thin_thick"===elem.type||"bar_thin_thin"===elem.type||"bar_right_repeat"===elem.type;if(endRepeat){var s=startRepeatPlaceholder[voiceNumber];s||(s=0);var e=skipEndingPlaceholder[voiceNumber];e||(e=voices[voiceNumber].length),voices[voiceNumber]=voices[voiceNumber].concat(voices[voiceNumber].slice(s,e)),skipEndingPlaceholder[voiceNumber]=void 0,startRepeatPlaceholder[voiceNumber]=void 0}startEnding&&(skipEndingPlaceholder[voiceNumber]=voices[voiceNumber].length),startRepeat&&(startRepeatPlaceholder[voiceNumber]=voices[voiceNumber].length);break;case"style":break;case"part":break;case"stem":case"scale":break;case"midi":var drumChange=!1;switch(elem.cmd){case"drumon":drumOn=!0,drumChange=!0;break;case"drumoff":drumOn=!1,drumChange=!0;break;case"drum":drumPattern=elem.params,drumChange=!0;break;case"drumbars":drumBars=elem.params[0],drumChange=!0}drumChange&&(voices[0].push({el_type:"drum",params:{pattern:drumPattern,bars:drumBars,intro:drumIntro,on:drumOn}}),startingDrumSet=!0);break;default:console.log("MIDI: element type "+elem.el_type+" not handled.")}}voiceNumber++}}if(drumIntro)for(var vv=0;vvinsertPoint;)insertPoint++;if(voices[vv].length>insertPoint)for(var w=0;w=0&&str.lastIndexOf(pattern)===d},window.ABCJS.parse.each=function(arr,iterator,context){for(var i=0,length=arr.length;i",">")},warn=function(str,line,col_num){line||(line=" ");var bad_char=line.charAt(col_num);" "===bad_char&&(bad_char="SPACE");var clean_line=encode(line.substring(0,col_num))+''+bad_char+""+encode(line.substring(col_num+1));addWarning("Music Line:"+tune.getNumLines()+":"+(col_num+1)+": "+str+": "+clean_line)},header=new window.ABCJS.parse.ParseHeader(tokenizer,warn,multilineVars,tune);this.getWarnings=function(){return multilineVars.warnings};var letter_to_chord=function(line,i){if('"'===line.charAt(i)){var chord=tokenizer.getBrackettedSubstring(line,i,5);if(chord[2]||warn("Missing the closing quote while parsing the chord symbol",line,i),chord[0]>0&&chord[1].length>0&&"^"===chord[1].charAt(0))chord[1]=chord[1].substring(1),chord[2]="above";else if(chord[0]>0&&chord[1].length>0&&"_"===chord[1].charAt(0))chord[1]=chord[1].substring(1),chord[2]="below";else if(chord[0]>0&&chord[1].length>0&&"<"===chord[1].charAt(0))chord[1]=chord[1].substring(1),chord[2]="left";else if(chord[0]>0&&chord[1].length>0&&">"===chord[1].charAt(0))chord[1]=chord[1].substring(1),chord[2]="right";else if(chord[0]>0&&chord[1].length>0&&"@"===chord[1].charAt(0)){chord[1]=chord[1].substring(1);var x=tokenizer.getFloat(chord[1]);0===x.digits&&warn("Missing first position in absolutely positioned annotation.",line,i),chord[1]=chord[1].substring(x.digits),","!==chord[1][0]&&warn("Missing comma absolutely positioned annotation.",line,i),chord[1]=chord[1].substring(1);var y=tokenizer.getFloat(chord[1]);0===y.digits&&warn("Missing second position in absolutely positioned annotation.",line,i),chord[1]=chord[1].substring(y.digits);var ws=tokenizer.skipWhiteSpace(chord[1]);chord[1]=chord[1].substring(ws),chord[2]=null,chord[3]={x:x.value,y:y.value}}else chord[1]=chord[1].replace(/([ABCDEFG])b/g,"$1♭"),chord[1]=chord[1].replace(/([ABCDEFG])#/g,"$1♯"),chord[2]="default";return chord}return[0,""]},legalAccents=["trill","lowermordent","uppermordent","mordent","pralltriller","accent","fermata","invertedfermata","tenuto","0","1","2","3","4","5","+","wedge","open","thumb","snap","turn","roll","breath","shortphrase","mediumphrase","longphrase","segno","coda","D.S.","D.C.","fine","slide","^","marcato","upbow","downbow","/","//","///","////","trem1","trem2","trem3","trem4","turnx","invertedturn","invertedturnx","trill(","trill)","arpeggio","xstem","mark","umarcato","style=normal","style=harmonic","style=rhythm","style=x"],volumeDecorations=["p","pp","f","ff","mf","mp","ppp","pppp","fff","ffff","sfz"],dynamicDecorations=["crescendo(","crescendo)","diminuendo(","diminuendo)"],accentPseudonyms=[["<","accent"],[">","accent"],["tr","trill"],["plus","+"],["emphasis","accent"],["^","umarcato"],["marcato","umarcato"]],accentDynamicPseudonyms=[["<(","crescendo("],["<)","crescendo)"],[">(","diminuendo("],[">)","diminuendo)"]],letter_to_accent=function(line,i){var macro=multilineVars.macros[line.charAt(i)];if(void 0!==macro)return"!"!==macro.charAt(0)&&"+"!==macro.charAt(0)||(macro=macro.substring(1)),"!"!==macro.charAt(macro.length-1)&&"+"!==macro.charAt(macro.length-1)||(macro=macro.substring(0,macro.length-1)),window.ABCJS.parse.detect(legalAccents,function(acc){return macro===acc})?[1,macro]:window.ABCJS.parse.detect(volumeDecorations,function(acc){return macro===acc})?("hidden"===multilineVars.volumePosition&&(macro=""),[1,macro]):window.ABCJS.parse.detect(dynamicDecorations,function(acc){return"hidden"===multilineVars.dynamicPosition&&(macro=""),macro===acc})?[1,macro]:(window.ABCJS.parse.detect(multilineVars.ignoredDecorations,function(dec){return macro===dec})||warn("Unknown macro: "+macro,line,i),[1,""]);switch(line.charAt(i)){case".":return[1,"staccato"];case"u":return[1,"upbow"];case"v":return[1,"downbow"];case"~":return[1,"irishroll"];case"!":case"+":var ret=tokenizer.getBrackettedSubstring(line,i,5);return ret[1].length>0&&("^"===ret[1].charAt(0)||"_"===ret[1].charAt(0))&&(ret[1]=ret[1].substring(1)),window.ABCJS.parse.detect(legalAccents,function(acc){return ret[1]===acc})?ret:window.ABCJS.parse.detect(volumeDecorations,function(acc){return ret[1]===acc})?("hidden"===multilineVars.volumePosition&&(ret[1]=""),ret):window.ABCJS.parse.detect(dynamicDecorations,function(acc){return ret[1]===acc})?("hidden"===multilineVars.dynamicPosition&&(ret[1]=""),ret):window.ABCJS.parse.detect(accentPseudonyms,function(acc){return ret[1]===acc[0]&&(ret[1]=acc[1],!0)})?ret:window.ABCJS.parse.detect(accentDynamicPseudonyms,function(acc){return ret[1]===acc[0]&&(ret[1]=acc[1],!0)})?("hidden"===multilineVars.dynamicPosition&&(ret[1]=""),ret):"!"!==line.charAt(i)||1!==ret[0]&&"!"===line.charAt(i+ret[0]-1)?(warn("Unknown decoration: "+ret[1],line,i),ret[1]="",ret):[1,null];case"H":return[1,"fermata"];case"J":return[1,"slide"];case"L":return[1,"accent"];case"M":return[1,"mordent"];case"O":return[1,"coda"];case"P":return[1,"pralltriller"];case"R":return[1,"roll"];case"S":return[1,"segno"];case"T":return[1,"trill"]}return[0,0]},letter_to_spacer=function(line,i){for(var start=i;tokenizer.isWhiteSpace(line.charAt(i));)i++;return[i-start]},letter_to_bar=function(line,curr_pos){var ret=tokenizer.getBarLine(line,curr_pos);if(0===ret.len)return[0,""];if(ret.warn)return warn(ret.warn,line,curr_pos),[ret.len,""];for(var ws=0;ws="2"&&line.charAt(i+1)<="9"?(void 0!==ret.triplet?warn("Can't nest triplets",line,i):(ret.triplet=line.charAt(i+1)-"0",i+2="1"&&line.charAt(i+4)<="9"?(ret.num_notes=line.charAt(i+4)-"0",i+=3):warn("expected number after the two colons after the triplet to mark the duration",line,i):i+3="1"&&line.charAt(i+3)<="9"?i+4="1"&&line.charAt(i+5)<="9"&&(ret.num_notes=line.charAt(i+5)-"0",i+=4):(ret.num_notes=ret.triplet,i+=3):warn("expected number after the triplet to mark the duration",line,i))),i++):void 0===ret.startSlur?ret.startSlur=1:ret.startSlur++),i++;return ret.consumed=i-start,ret},addWords=function(line,words){if(!line)return void warn("Can't add words before the first line of music",line,0);words=window.ABCJS.parse.strip(words),"-"!==words.charAt(words.length-1)&&(words+=" ");for(var word_list=[],last_divider=0,replace=!1,addWord=function(i){var word=window.ABCJS.parse.strip(words.substring(last_divider,i));if(last_divider=i+1,word.length>0){replace&&(word=window.ABCJS.parse.gsub(word,"~"," "));var div=words.charAt(i);return"_"!==div&&"-"!==div&&(div=" "),word_list.push({syllable:tokenizer.translateString(word),divider:div}),replace=!1,!0}return!1},i=0;i0&&(window.ABCJS.parse.last(word_list).divider="-",word_list.push({skip:!0,to:"next"}));break;case"_":addWord(i),word_list.push({skip:!0,to:"slur"});break;case"*":addWord(i),word_list.push({skip:!0,to:"next"});break;case"|":addWord(i),word_list.push({skip:!0,to:"bar"});break;case"~":replace=!0}var inSlur=!1;window.ABCJS.parse.each(line,function(el){if(0!==word_list.length)if(word_list[0].skip)switch(word_list[0].to){case"next":"note"!==el.el_type||null===el.pitches||inSlur||word_list.shift();break;case"slur":"note"===el.el_type&&null!==el.pitches&&word_list.shift();break;case"bar":"bar"===el.el_type&&word_list.shift()}else if("note"===el.el_type&&void 0===el.rest&&!inSlur){var lyric=word_list.shift();void 0===el.lyric?el.lyric=[lyric]:el.lyric.push(lyric)}})},addSymbols=function(line,words){if(!line)return void warn("Can't add symbols before the first line of music",line,0);words=window.ABCJS.parse.strip(words),"-"!==words.charAt(words.length-1)&&(words+=" ");for(var word_list=[],last_divider=0,replace=!1,addWord=function(i){var word=window.ABCJS.parse.strip(words.substring(last_divider,i));if(last_divider=i+1,word.length>0){replace&&(word=window.ABCJS.parse.gsub(word,"~"," "));var div=words.charAt(i);return"_"!==div&&"-"!==div&&(div=" "),word_list.push({syllable:tokenizer.translateString(word),divider:div}),replace=!1,!0}return!1},i=0;i0&&(window.ABCJS.parse.last(word_list).divider="-",word_list.push({skip:!0,to:"next"}));break;case"_":addWord(i),word_list.push({skip:!0,to:"slur"});break;case"*":addWord(i),word_list.push({skip:!0,to:"next"});break;case"|":addWord(i),word_list.push({skip:!0,to:"bar"});break;case"~":replace=!0}var inSlur=!1;window.ABCJS.parse.each(line,function(el){if(0!==word_list.length)if(word_list[0].skip)switch(word_list[0].to){case"next":"note"!==el.el_type||null===el.pitches||inSlur||word_list.shift();break;case"slur":"note"===el.el_type&&null!==el.pitches&&word_list.shift();break;case"bar":"bar"===el.el_type&&word_list.shift()}else if("note"===el.el_type&&void 0===el.rest&&!inSlur){var lyric=word_list.shift();void 0===el.lyric?el.lyric=[lyric]:el.lyric.push(lyric)}})},getBrokenRhythm=function(line,index){switch(line.charAt(index)){case">":return index"===line.charAt(index+1)?[2,1.75,.25]:[1,1.5,.5];case"<":return index"!==line.charAt(index))return el;index--,state="broken_rhythm";break;case">":case"<":if(!isComplete(state))return null;if(!canHaveBrokenRhythm)return el.endChar=index,el;var br2=getBrokenRhythm(line,index);index+=br2[0]-1,multilineVars.next_note_duration=br2[2],el.duration=br2[1]*el.duration,state="end_slur";break;default:return isComplete(state)?(el.endChar=index,el):null}if(index++,index===line.length)return isComplete(state)?(el.endChar=index,el):null}return null},letter_to_grace=function(line,i){if("{"===line.charAt(i)){var gra=tokenizer.getBrackettedSubstring(line,i,1,"}");gra[2]||warn("Missing the closing '}' while parsing grace note",line,i),")"===line[i+gra[0]]&&(gra[0]++,gra[1]+=")");for(var gracenotes=[],ii=0,inTie=!1;ii0&&(gracenotes[gracenotes.length-1].end_beam=!0):warn("Unknown character '"+gra[1].charAt(ii)+"' while parsing grace note",line,i),ii++)}if(gracenotes.length)return[gra[0],gracenotes]}return[0]},nonDecorations="ABCDEFGabcdefgxyzZ[]|^_{",parseRegularMusicLine=function(line){header.resolveTempo(),multilineVars.is_in_header=!1;for(var i=0,startOfLine=multilineVars.iChar;tokenizer.isWhiteSpace(line.charAt(i))&&i0&&(i+=retHeader[0]);for(var el={};i0)i+=retInlineHeader[0];else{delayStartNewLine&&(startNewLine(),delayStartNewLine=!1);for(var ret;;)if(ret=tokenizer.eatWhiteSpace(line,i),ret>0&&(i+=ret),i>0&&""===line.charAt(i-1)&&(ret=header.letter_to_body_header(line,i),ret[0]>0&&(i=ret[0],multilineVars.start_new_line=!1)),ret=letter_to_spacer(line,i),ret[0]>0&&(i+=ret[0]),ret=letter_to_chord(line,i),ret[0]>0){el.chord||(el.chord=[]);var chordName=tokenizer.translateString(ret[1]);chordName=chordName.replace(/;/g,"\n");for(var addedChord=!1,ci=0;ci0&&(el.force_end_beam_last=!0),i+=ii}else if(ret=nonDecorations.indexOf(line.charAt(i))===-1?letter_to_accent(line,i):[0],ret[0]>0)null===ret[1]?i+10&&(0===ret[1].indexOf("style=")?el.style=ret[1].substr(6):(void 0===el.decoration&&(el.decoration=[]),el.decoration.push(ret[1]))),i+=ret[0];else{if(ret=letter_to_grace(line,i),!(ret[0]>0))break;el.gracenotes=ret[1],i+=ret[0]}if(ret=letter_to_bar(line,i),ret[0]>0){void 0!==el.gracenotes&&(el.rest={type:"spacer"},el.duration=.125,multilineVars.addFormattingOptions(el,tune.formatting,"note"),tune.appendElement("note",startOfLine+i,startOfLine+i+ret[0],el),multilineVars.measureNotEmpty=!0,el={});var bar={type:ret[1]};if(0===bar.type.length)warn("Unknown bar type",line,i);else{if(multilineVars.inEnding&&"bar_thin"!==bar.type&&(bar.endEnding=!0,multilineVars.inEnding=!1),ret[2]&&(bar.startEnding=ret[2],multilineVars.inEnding&&(bar.endEnding=!0),multilineVars.inEnding=!0),void 0!==el.decoration&&(bar.decoration=el.decoration),void 0!==el.chord&&(bar.chord=el.chord),bar.startEnding&&void 0===multilineVars.barFirstEndingNum?multilineVars.barFirstEndingNum=multilineVars.currBarNumber:bar.startEnding&&bar.endEnding&&multilineVars.barFirstEndingNum?multilineVars.currBarNumber=multilineVars.barFirstEndingNum:bar.endEnding&&(multilineVars.barFirstEndingNum=void 0),"bar_invisible"!==bar.type&&multilineVars.measureNotEmpty){var isFirstVoice=void 0===multilineVars.currentVoice||0===multilineVars.currentVoice.staffNum&&0===multilineVars.currentVoice.index;isFirstVoice&&(multilineVars.currBarNumber++,multilineVars.barNumbers&&multilineVars.currBarNumber%multilineVars.barNumbers===0&&(bar.barNumber=multilineVars.currBarNumber))}multilineVars.addFormattingOptions(el,tune.formatting,"bar"),tune.appendElement("bar",startOfLine+i,startOfLine+i+ret[0],bar),multilineVars.measureNotEmpty=!1,el={}}i+=ret[0]}else if("&"===line[i])warn("Overlay not yet supported",line,i),i++;else{if(ret=letter_to_open_slurs_and_triplets(line,i),ret.consumed>0&&(void 0!==ret.startSlur&&(el.startSlur=ret.startSlur),void 0!==ret.triplet&&(tripletNotesLeft>0?warn("Can't nest triplets",line,i):(el.startTriplet=ret.triplet,tripletNotesLeft=void 0===ret.num_notes?ret.triplet:ret.num_notes)),i+=ret.consumed),"["===line.charAt(i)){var chordStartChar=i;i++;for(var chordDuration=null,done=!1;!done;){var chordNote=getCoreNote(line,i,{},!1);if(null!==chordNote)chordNote.end_beam&&(el.end_beam=!0,delete chordNote.end_beam),void 0===el.pitches?(el.duration=chordNote.duration,el.pitches=[chordNote]):el.pitches.push(chordNote),delete chordNote.duration,multilineVars.inTieChord[el.pitches.length]&&(chordNote.endTie=!0,multilineVars.inTieChord[el.pitches.length]=void 0),chordNote.startTie&&(multilineVars.inTieChord[el.pitches.length]=!0),i=chordNote.endChar,delete chordNote.endChar;else if(" "===line.charAt(i))warn("Spaces are not allowed in chords",line,i),i++;else{if(i0&&(tripletNotesLeft--,0===tripletNotesLeft&&(el.endTriplet=!0));for(var postChordDone=!1;i":case"<":var br2=getBrokenRhythm(line,i);i+=br2[0]-1,multilineVars.next_note_duration=br2[2],chordDuration?chordDuration*=br2[1]:chordDuration=br2[1];break;case"1":case"2":case"3":case"4":case"5":case"6":case"7":case"8":case"9":case"/":var fraction=tokenizer.getFraction(line,i);chordDuration=fraction.value,i=fraction.index,"-"===line.charAt(i)||")"===line.charAt(i)||" "===line.charAt(i)||"<"===line.charAt(i)||">"===line.charAt(i)?i--:postChordDone=!0;break;default:postChordDone=!0}postChordDone||i++}}else warn("Expected ']' to end the chords",line,i);void 0!==el.pitches&&(null!==chordDuration&&(el.duration=el.duration*chordDuration),multilineVars.addFormattingOptions(el,tune.formatting,"note"),tune.appendElement("note",startOfLine+chordStartChar,startOfLine+i,el),multilineVars.measureNotEmpty=!0,el={}),done=!0}}}else{var el2={},core=getCoreNote(line,i,el2,!0);void 0!==el2.endTie&&(multilineVars.inTie=!0),null!==core&&(void 0!==core.pitch?(el.pitches=[{}],void 0!==core.accidental&&(el.pitches[0].accidental=core.accidental),el.pitches[0].pitch=core.pitch,void 0!==core.endSlur&&(el.pitches[0].endSlur=core.endSlur),void 0!==core.endTie&&(el.pitches[0].endTie=core.endTie),void 0!==core.startSlur&&(el.pitches[0].startSlur=core.startSlur),void 0!==el.startSlur&&(el.pitches[0].startSlur=el.startSlur),void 0!==core.startTie&&(el.pitches[0].startTie=core.startTie),void 0!==el.startTie&&(el.pitches[0].startTie=el.startTie)):(el.rest=core.rest,void 0!==core.endSlur&&(el.endSlur=core.endSlur),void 0!==core.endTie&&(el.rest.endTie=core.endTie),void 0!==core.startSlur&&(el.startSlur=core.startSlur),void 0!==core.startTie&&(el.rest.startTie=core.startTie),void 0!==el.startTie&&(el.rest.startTie=el.startTie)),void 0!==core.chord&&(el.chord=core.chord),void 0!==core.duration&&(el.duration=core.duration),void 0!==core.decoration&&(el.decoration=core.decoration),void 0!==core.graceNotes&&(el.graceNotes=core.graceNotes),delete el.startSlur,multilineVars.inTie&&(void 0!==el.pitches?(el.pitches[0].endTie=!0,multilineVars.inTie=!1):"spacer"!==el.rest.type&&(el.rest.endTie=!0,multilineVars.inTie=!1)),(core.startTie||el.startTie)&&(multilineVars.inTie=!0),i=core.endChar,tripletNotesLeft>0&&(tripletNotesLeft--,0===tripletNotesLeft&&(el.endTriplet=!0)),core.end_beam&&addEndBeam(el),el.rest&&"rest"===el.rest.type&&1===el.duration&&(el.rest.type="whole",el.duration=durationOfMeasure(multilineVars)),multilineVars.addFormattingOptions(el,tune.formatting,"note"),tune.appendElement("note",startOfLine+startI,startOfLine+i,el),multilineVars.measureNotEmpty=!0,el={})}i===startI&&(" "!==line.charAt(i)&&"`"!==line.charAt(i)&&warn("Unknown character ignored",line,i),i++)}}}}},parseLine=function(line){var ret=header.parseHeader(line);ret.regular&&parseRegularMusicLine(ret.str),ret.newline&&void 0===multilineVars.continueall&&startNewLine(),ret.words&&addWords(tune.getCurrentVoice(),line.substring(2)),ret.symbols&&addSymbols(tune.getCurrentVoice(),line.substring(2)),ret.recurse&&parseLine(ret.str)};this.parse=function(strTune,switches){switches||(switches={}),tune.reset(),switches.print&&(tune.media="print"),multilineVars.reset(),header.reset(tokenizer,warn,multilineVars,tune),strTune=window.ABCJS.parse.gsub(strTune,"\r\n","\n"),strTune=window.ABCJS.parse.gsub(strTune,"\r","\n"),strTune+="\n",strTune=strTune.replace(/\n\\.*\n/g,"\n");var continuationReplacement=function(all,backslash,comment){var spaces=" ",padding=comment?spaces.substring(0,comment.length):"";return backslash+" "+padding};strTune=strTune.replace(/\\([ \t]*)(%.*)*\n/g,continuationReplacement);var lines=strTune.split("\n");0===window.ABCJS.parse.last(lines).length&&lines.pop();try{switches.format&&window.ABCJS.parse.parseDirective.globalFormatting(switches.format),window.ABCJS.parse.each(lines,function(line){if(switches.header_only&&multilineVars.is_in_header===!1)throw"normal_abort";if(switches.stop_on_warning&&multilineVars.warnings)throw"normal_abort";multilineVars.is_in_history?":"===line.charAt(1)?(multilineVars.is_in_history=!1,parseLine(line)):tune.addMetaText("history",tokenizer.translateString(tokenizer.stripComment(line))):multilineVars.inTextBlock?window.ABCJS.parse.startsWith(line,"%%endtext")?(tune.addText(multilineVars.textBlock),multilineVars.inTextBlock=!1):window.ABCJS.parse.startsWith(line,"%%")?multilineVars.textBlock+=" "+line.substring(2):multilineVars.textBlock+=" "+line:multilineVars.inPsBlock?window.ABCJS.parse.startsWith(line,"%%endps")?multilineVars.inPsBlock=!1:multilineVars.textBlock+=" "+line:parseLine(line),multilineVars.iChar+=line.length+1});var ph=792,pl=612;switch(multilineVars.papersize){case"legal":ph=1008,pl=612;break;case"A4":ph=842.4,pl=597.6}if(multilineVars.landscape){var x=ph;ph=pl,pl=x}multilineVars.openSlurs=tune.cleanUp(pl,ph,multilineVars.barsperstaff,multilineVars.staffnonote,multilineVars.openSlurs)}catch(err){if("normal_abort"!==err)throw err}switches.hint_measures&&addHintMeasures()}},window.ABCJS||(window.ABCJS={}),window.ABCJS.parse||(window.ABCJS.parse={}),window.ABCJS.parse.parseDirective={},function(){"use strict";function initializeFonts(){multilineVars.annotationfont={face:"Helvetica",size:12,weight:"normal",style:"normal",decoration:"none"},multilineVars.gchordfont={face:"Helvetica",size:12,weight:"normal",style:"normal",decoration:"none"},multilineVars.historyfont={face:'"Times New Roman"',size:16,weight:"normal",style:"normal",decoration:"none"},multilineVars.infofont={face:'"Times New Roman"',size:14,weight:"normal",style:"italic",decoration:"none"},multilineVars.measurefont={face:'"Times New Roman"',size:14,weight:"normal",style:"italic",decoration:"none"},multilineVars.partsfont={face:'"Times New Roman"',size:15,weight:"normal",style:"normal",decoration:"none"},multilineVars.repeatfont={face:'"Times New Roman"',size:13,weight:"normal",style:"normal",decoration:"none"},multilineVars.textfont={face:'"Times New Roman"',size:16,weight:"normal",style:"normal",decoration:"none"},multilineVars.vocalfont={face:'"Times New Roman"',size:13,weight:"bold",style:"normal",decoration:"none"},multilineVars.wordsfont={face:'"Times New Roman"',size:16,weight:"normal",style:"normal",decoration:"none"},tune.formatting.composerfont={face:'"Times New Roman"',size:14,weight:"normal",style:"italic",decoration:"none"},tune.formatting.subtitlefont={face:'"Times New Roman"',size:16,weight:"normal",style:"normal",decoration:"none"},tune.formatting.tempofont={face:'"Times New Roman"',size:15,weight:"bold",style:"normal",decoration:"none"},tune.formatting.titlefont={face:'"Times New Roman"',size:20,weight:"normal",style:"normal",decoration:"none"},tune.formatting.footerfont={face:'"Times New Roman"',size:12,weight:"normal",style:"normal",decoration:"none"},tune.formatting.headerfont={face:'"Times New Roman"',size:12,weight:"normal",style:"normal",decoration:"none"},tune.formatting.voicefont={face:'"Times New Roman"',size:13,weight:"bold",style:"normal",decoration:"none"},tune.formatting.annotationfont=multilineVars.annotationfont,tune.formatting.gchordfont=multilineVars.gchordfont,tune.formatting.historyfont=multilineVars.historyfont,tune.formatting.infofont=multilineVars.infofont, tune.formatting.measurefont=multilineVars.measurefont,tune.formatting.partsfont=multilineVars.partsfont,tune.formatting.repeatfont=multilineVars.repeatfont,tune.formatting.textfont=multilineVars.textfont,tune.formatting.vocalfont=multilineVars.vocalfont,tune.formatting.wordsfont=multilineVars.wordsfont}var tokenizer,warn,multilineVars,tune;window.ABCJS.parse.parseDirective.initialize=function(tokenizer_,warn_,multilineVars_,tune_){tokenizer=tokenizer_,warn=warn_,multilineVars=multilineVars_,tune=tune_,initializeFonts()};var fontTypeCanHaveBox={gchordfont:!0,measurefont:!0,partsfont:!0},fontTranslation=function(fontFace){switch(fontFace){case"Arial-Italic":return{face:"Arial",weight:"normal",style:"italic",decoration:"none"};case"Arial-Bold":return{face:"Arial",weight:"bold",style:"normal",decoration:"none"};case"Bookman-Demi":return{face:"Bookman,serif",weight:"bold",style:"normal",decoration:"none"};case"Bookman-DemiItalic":return{face:"Bookman,serif",weight:"bold",style:"italic",decoration:"none"};case"Bookman-Light":return{face:"Bookman,serif",weight:"normal",style:"normal",decoration:"none"};case"Bookman-LightItalic":return{face:"Bookman,serif",weight:"normal",style:"italic",decoration:"none"};case"Courier":return{face:'"Courier New"',weight:"normal",style:"normal",decoration:"none"};case"Courier-Oblique":return{face:'"Courier New"',weight:"normal",style:"italic",decoration:"none"};case"Courier-Bold":return{face:'"Courier New"',weight:"bold",style:"normal",decoration:"none"};case"Courier-BoldOblique":return{face:'"Courier New"',weight:"bold",style:"italic",decoration:"none"};case"AvantGarde-Book":return{face:"AvantGarde,Arial",weight:"normal",style:"normal",decoration:"none"};case"AvantGarde-BookOblique":return{face:"AvantGarde,Arial",weight:"normal",style:"italic",decoration:"none"};case"AvantGarde-Demi":case"Avant-Garde-Demi":return{face:"AvantGarde,Arial",weight:"bold",style:"normal",decoration:"none"};case"AvantGarde-DemiOblique":return{face:"AvantGarde,Arial",weight:"bold",style:"italic",decoration:"none"};case"Helvetica-Oblique":return{face:"Helvetica",weight:"normal",style:"italic",decoration:"none"};case"Helvetica-Bold":return{face:"Helvetica",weight:"bold",style:"normal",decoration:"none"};case"Helvetica-BoldOblique":return{face:"Helvetica",weight:"bold",style:"italic",decoration:"none"};case"Helvetica-Narrow":return{face:'"Helvetica Narrow",Helvetica',weight:"normal",style:"normal",decoration:"none"};case"Helvetica-Narrow-Oblique":return{face:'"Helvetica Narrow",Helvetica',weight:"normal",style:"italic",decoration:"none"};case"Helvetica-Narrow-Bold":return{face:'"Helvetica Narrow",Helvetica',weight:"bold",style:"normal",decoration:"none"};case"Helvetica-Narrow-BoldOblique":return{face:'"Helvetica Narrow",Helvetica',weight:"bold",style:"italic",decoration:"none"};case"Palatino-Roman":return{face:"Palatino",weight:"normal",style:"normal",decoration:"none"};case"Palatino-Italic":return{face:"Palatino",weight:"normal",style:"italic",decoration:"none"};case"Palatino-Bold":return{face:"Palatino",weight:"bold",style:"normal",decoration:"none"};case"Palatino-BoldItalic":return{face:"Palatino",weight:"bold",style:"italic",decoration:"none"};case"NewCenturySchlbk-Roman":return{face:'"New Century",serif',weight:"normal",style:"normal",decoration:"none"};case"NewCenturySchlbk-Italic":return{face:'"New Century",serif',weight:"normal",style:"italic",decoration:"none"};case"NewCenturySchlbk-Bold":return{face:'"New Century",serif',weight:"bold",style:"normal",decoration:"none"};case"NewCenturySchlbk-BoldItalic":return{face:'"New Century",serif',weight:"bold",style:"italic",decoration:"none"};case"Times":case"Times-Roman":case"Times-Narrow":case"Times-Courier":case"Times-New-Roman":return{face:'"Times New Roman"',weight:"normal",style:"normal",decoration:"none"};case"Times-Italic":case"Times-Italics":return{face:'"Times New Roman"',weight:"normal",style:"italic",decoration:"none"};case"Times-Bold":return{face:'"Times New Roman"',weight:"bold",style:"normal",decoration:"none"};case"Times-BoldItalic":return{face:'"Times New Roman"',weight:"bold",style:"italic",decoration:"none"};case"ZapfChancery-MediumItalic":return{face:'"Zapf Chancery",cursive,serif',weight:"normal",style:"normal",decoration:"none"};default:return null}},getFontParameter=function(tokens,currentSetting,str,position,cmd){function processNumberOnly(){var size=parseInt(tokens[0].token);return tokens.shift(),currentSetting?0===tokens.length?{face:currentSetting.face,weight:currentSetting.weight,style:currentSetting.style,decoration:currentSetting.decoration,size:size}:1===tokens.length&&"box"===tokens[0].token&&fontTypeCanHaveBox[cmd]?{face:currentSetting.face,weight:currentSetting.weight,style:currentSetting.style,decoration:currentSetting.decoration,size:size,box:!0}:(warn("Extra parameters in font definition.",str,position),{face:currentSetting.face,weight:currentSetting.weight,style:currentSetting.style,decoration:currentSetting.decoration,size:size}):(warn("Can't set just the size of the font since there is no default value.",str,position),{face:'"Times New Roman"',weight:"normal",style:"normal",decoration:"none",size:size})}if("*"===tokens[0].token){if(tokens.shift(),"number"===tokens[0].type)return processNumberOnly();warn("Expected font size number after *.",str,position)}if("number"===tokens[0].type)return processNumberOnly();for(var size,face=[],weight="normal",style="normal",decoration="none",box=!1,state="face",hyphenLast=!1;tokens.length;){var currToken=tokens.shift(),word=currToken.token.toLowerCase();switch(state){case"face":hyphenLast||"utf"!==word&&"number"!==currToken.type&&"bold"!==word&&"italic"!==word&&"underline"!==word&&"box"!==word?face.length>0&&"-"===currToken.token?(hyphenLast=!0,face[face.length-1]=face[face.length-1]+currToken.token):hyphenLast?(hyphenLast=!1,face[face.length-1]=face[face.length-1]+currToken.token):face.push(currToken.token):"number"===currToken.type?(size?warn("Font size specified twice in font definition.",str,position):size=currToken.token,state="modifier"):"bold"===word?weight="bold":"italic"===word?style="italic":"underline"===word?decoration="underline":"box"===word?(fontTypeCanHaveBox[cmd]?box=!0:warn('This font style doesn\'t support "box"',str,position),state="finished"):"utf"===word?(currToken=tokens.shift(),state="size"):warn("Unknown parameter "+currToken.token+" in font definition.",str,position);break;case"size":"number"===currToken.type?size?warn("Font size specified twice in font definition.",str,position):size=currToken.token:warn("Expected font size in font definition.",str,position),state="modifier";break;case"modifier":"bold"===word?weight="bold":"italic"===word?style="italic":"underline"===word?decoration="underline":"box"===word?(fontTypeCanHaveBox[cmd]?box=!0:warn('This font style doesn\'t support "box"',str,position),state="finished"):warn("Unknown parameter "+currToken.token+" in font definition.",str,position);break;case"finished":warn('Extra characters found after "box" in font definition.',str,position)}}void 0===size?currentSetting?size=currentSetting.size:(warn("Must specify the size of the font since there is no default value.",str,position),size=12):size=parseFloat(size),face=face.join(" ");var psFont=fontTranslation(face),font={};return psFont?(font.face=psFont.face,font.weight=psFont.weight,font.style=psFont.style,font.decoration=psFont.decoration,font.size=size,box&&(font.box=!0),font):(font.face=face,font.weight=weight,font.style=style,font.decoration=decoration,font.size=size,box&&(font.box=!0),font)},getChangingFont=function(cmd,tokens,str){return 0===tokens.length?'Directive "'+cmd+'" requires a font as a parameter.':(multilineVars[cmd]=getFontParameter(tokens,multilineVars[cmd],str,0,cmd),multilineVars.is_in_header&&(tune.formatting[cmd]=multilineVars[cmd]),null)},getGlobalFont=function(cmd,tokens,str){return 0===tokens.length?'Directive "'+cmd+'" requires a font as a parameter.':(tune.formatting[cmd]=getFontParameter(tokens,tune.formatting[cmd],str,0,cmd),null)},setScale=function(cmd,tokens){var scratch="";window.ABCJS.parse.each(tokens,function(tok){scratch+=tok.token});var num=parseFloat(scratch);return isNaN(num)||0===num?'Directive "'+cmd+'" requires a number as a parameter.':void(tune.formatting.scale=num)},getRequiredMeasurement=function(cmd,tokens){var points=tokenizer.getMeasurement(tokens);return 0===points.used||0!==tokens.length?{error:'Directive "'+cmd+'" requires a measurement as a parameter.'}:points.value},oneParameterMeasurement=function(cmd,tokens){var points=tokenizer.getMeasurement(tokens);return 0===points.used||0!==tokens.length?'Directive "'+cmd+'" requires a measurement as a parameter.':(tune.formatting[cmd]=points.value,null)},addMultilineVar=function(key,cmd,tokens,min,max){if(1!==tokens.length||"number"!==tokens[0].type)return'Directive "'+cmd+'" requires a number as a parameter.';var i=tokens[0].intt;return void 0!==min&&imax?'Directive "'+cmd+'" requires a number less than or equal to '+max+" as a parameter.":(multilineVars[key]=i,null)},addMultilineVarBool=function(key,cmd,tokens){var str=addMultilineVar(key,cmd,tokens,0,1);return null!==str?str:(multilineVars[key]=1===multilineVars[key],null)},addMultilineVarOneParamChoice=function(key,cmd,tokens,choices){if(1!==tokens.length)return'Directive "'+cmd+'" requires one of [ '+choices.join(", ")+" ] as a parameter.";for(var choice=tokens[0].token,found=!1,i=0;!found&&i=0)0!==midi.length&&warn("Unexpected parameter in MIDI "+midi_cmd,restOfString,0);else if(midiCmdParam1String.indexOf(midi_cmd)>=0)1!==midi.length?warn("Expected one parameter in MIDI "+midi_cmd,restOfString,0):midi_params.push(midi[0].token);else if(midiCmdParam1Integer.indexOf(midi_cmd)>=0)1!==midi.length?warn("Expected one parameter in MIDI "+midi_cmd,restOfString,0):"number"!==midi[0].type?warn("Expected one integer parameter in MIDI "+midi_cmd,restOfString,0):midi_params.push(midi[0].intt);else if(midiCmdParam1Integer1OptionalInteger.indexOf(midi_cmd)>=0)1!==midi.length&&2!==midi.length?warn("Expected one or two parameters in MIDI "+midi_cmd,restOfString,0):"number"!==midi[0].type?warn("Expected integer parameter in MIDI "+midi_cmd,restOfString,0):2===midi.length&&"number"!==midi[1].type?warn("Expected integer parameter in MIDI "+midi_cmd,restOfString,0):(midi_params.push(midi[0].intt),2===midi.length&&midi_params.push(midi[1].intt));else if(midiCmdParam2Integer.indexOf(midi_cmd)>=0)2!==midi.length?warn("Expected two parameters in MIDI "+midi_cmd,restOfString,0):"number"!==midi[0].type||"number"!==midi[1].type?warn("Expected two integer parameters in MIDI "+midi_cmd,restOfString,0):(midi_params.push(midi[0].intt),midi_params.push(midi[1].intt));else if(midiCmdParam1String1Integer.indexOf(midi_cmd)>=0)2!==midi.length?warn("Expected two parameters in MIDI "+midi_cmd,restOfString,0):"alpha"!==midi[0].type||"number"!==midi[1].type?warn("Expected one string and one integer parameters in MIDI "+midi_cmd,restOfString,0):(midi_params.push(midi[0].token),midi_params.push(midi[1].intt));else if(midiCmdParamFraction.indexOf(midi_cmd)>=0)3!==midi.length?warn("Expected fraction parameter in MIDI "+midi_cmd,restOfString,0):"number"!==midi[0].type||"/"!==midi[1].token||"number"!==midi[2].type?warn("Expected fraction parameter in MIDI "+midi_cmd,restOfString,0):(midi_params.push(midi[0].intt),midi_params.push(midi[2].intt));else if(midiCmdParam4Integer.indexOf(midi_cmd)>=0)4!==midi.length?warn("Expected four parameters in MIDI "+midi_cmd,restOfString,0):"number"!==midi[0].type||"number"!==midi[1].type||"number"!==midi[2].type||"number"!==midi[3].type?warn("Expected four integer parameters in MIDI "+midi_cmd,restOfString,0):(midi_params.push(midi[0].intt),midi_params.push(midi[1].intt),midi_params.push(midi[2].intt),midi_params.push(midi[3].intt));else if(midiCmdParam5Integer.indexOf(midi_cmd)>=0)5!==midi.length?warn("Expected five parameters in MIDI "+midi_cmd,restOfString,0):"number"!==midi[0].type||"number"!==midi[1].type||"number"!==midi[2].type||"number"!==midi[3].type||"number"!==midi[4].type?warn("Expected five integer parameters in MIDI "+midi_cmd,restOfString,0):(midi_params.push(midi[0].intt),midi_params.push(midi[1].intt),midi_params.push(midi[2].intt),midi_params.push(midi[3].intt),midi_params.push(midi[4].intt));else if(midiCmdParam1Integer1OptionalInteger.indexOf(midi_cmd)>=0)1!==midi.length||4!==midi.length?warn("Expected one or two parameters in MIDI "+midi_cmd,restOfString,0):"number"!==midi[0].type?warn("Expected integer parameter in MIDI "+midi_cmd,restOfString,0):4===midi.length?("octave"!==midi[1].token&&warn("Expected octave parameter in MIDI "+midi_cmd,restOfString,0),"="!==midi[2].token&&warn("Expected octave parameter in MIDI "+midi_cmd,restOfString,0),"number"!==midi[3].type&&warn("Expected integer parameter for octave in MIDI "+midi_cmd,restOfString,0)):(midi_params.push(midi[0].intt),4===midi.length&&midi_params.push(midi[3].intt));else if(midiCmdParam1StringVariableIntegers.indexOf(midi_cmd)>=0)if(midi.length<2)warn("Expected string parameter and at least one integer parameter in MIDI "+midi_cmd,restOfString,0);else if("alpha"!==midi[0].type)warn("Expected string parameter and at least one integer parameter in MIDI "+midi_cmd,restOfString,0);else{var p=midi.shift();for(midi_params.push(p.token);midi.length>0;)p=midi.shift(),"number"!==p.type&&warn("Expected integer parameter in MIDI "+midi_cmd,restOfString,0),midi_params.push(p.intt)}tune.hasBeginMusic()?tune.appendElement("midi",-1,-1,{cmd:midi_cmd,params:midi_params}):(void 0===tune.formatting.midi&&(tune.formatting.midi={}),tune.formatting.midi[midi_cmd]=midi_params)};window.ABCJS.parse.parseDirective.parseFontChangeLine=function(textstr){var textParts=textstr.split("$");if(textParts.length>1&&multilineVars.setfont){for(var textarr=[{text:textParts[0]}],i=1;i1)return textarr}return textstr};var positionChoices=["auto","above","below","hidden"];window.ABCJS.parse.parseDirective.addDirective=function(str){var tokens=tokenizer.tokenize(str,0,str.length);if(0===tokens.length||"alpha"!==tokens[0].type)return null;var restOfString=str.substring(str.indexOf(tokens[0].token)+tokens[0].token.length);restOfString=tokenizer.stripComment(restOfString);var cmd=tokens.shift().token.toLowerCase(),scratch="";switch(cmd){case"bagpipes":tune.formatting.bagpipes=!0;break;case"landscape":multilineVars.landscape=!0;break;case"papersize":multilineVars.papersize=restOfString;break;case"slurgraces":tune.formatting.slurgraces=!0;break;case"stretchlast":tune.formatting.stretchlast=!0;break;case"titlecaps":multilineVars.titlecaps=!0;break;case"titleleft":tune.formatting.titleleft=!0;break;case"measurebox":tune.formatting.measurebox=!0;break;case"vocal":return addMultilineVarOneParamChoice("vocalPosition",cmd,tokens,positionChoices);case"dynamic":return addMultilineVarOneParamChoice("dynamicPosition",cmd,tokens,positionChoices);case"gchord":return addMultilineVarOneParamChoice("chordPosition",cmd,tokens,positionChoices);case"ornament":return addMultilineVarOneParamChoice("ornamentPosition",cmd,tokens,positionChoices);case"volume":return addMultilineVarOneParamChoice("volumePosition",cmd,tokens,positionChoices);case"botmargin":case"botspace":case"composerspace":case"indent":case"leftmargin":case"linesep":case"musicspace":case"partsspace":case"pageheight":case"pagewidth":case"rightmargin":case"staffsep":case"staffwidth":case"subtitlespace":case"sysstaffsep":case"systemsep":case"textspace":case"titlespace":case"topmargin":case"topspace":case"vocalspace":case"wordsspace":return oneParameterMeasurement(cmd,tokens);case"vskip":var vskip=getRequiredMeasurement(cmd,tokens);return vskip.error?vskip.error:(tune.addSpacing(vskip),null);case"scale":setScale(cmd,tokens);break;case"sep":if(0===tokens.length)tune.addSeparator();else{var points=tokenizer.getMeasurement(tokens);if(0===points.used)return'Directive "'+cmd+'" requires 3 numbers: space above, space below, length of line';var spaceAbove=points.value;if(points=tokenizer.getMeasurement(tokens),0===points.used)return'Directive "'+cmd+'" requires 3 numbers: space above, space below, length of line';var spaceBelow=points.value;if(points=tokenizer.getMeasurement(tokens),0===points.used||0!==tokens.length)return'Directive "'+cmd+'" requires 3 numbers: space above, space below, length of line';var lenLine=points.value;tune.addSeparator(spaceAbove,spaceBelow,lenLine)}break;case"barsperstaff":if(scratch=addMultilineVar("barsperstaff",cmd,tokens),null!==scratch)return scratch;break;case"staffnonote":if(scratch=addMultilineVarBool("staffnonote",cmd,tokens),null!==scratch)return scratch;break;case"printtempo":if(scratch=addMultilineVarBool("printTempo",cmd,tokens),null!==scratch)return scratch;break;case"partsbox":if(scratch=addMultilineVarBool("partsBox",cmd,tokens),null!==scratch)return scratch;break;case"measurenb":case"barnumbers":if(scratch=addMultilineVar("barNumbers",cmd,tokens),null!==scratch)return scratch;break;case"begintext":multilineVars.inTextBlock=!0;break;case"continueall":multilineVars.continueall=!0;break;case"beginps":multilineVars.inPsBlock=!0,warn("Postscript ignored",str,0);break;case"deco":restOfString.length>0&&multilineVars.ignoredDecorations.push(restOfString.substring(0,restOfString.indexOf(" "))),warn("Decoration redefinition ignored",str,0);break;case"text":var textstr=tokenizer.translateString(restOfString);tune.addText(window.ABCJS.parse.parseDirective.parseFontChangeLine(textstr));break;case"center":var centerstr=tokenizer.translateString(restOfString);tune.addCentered(window.ABCJS.parse.parseDirective.parseFontChangeLine(centerstr));break;case"font":break;case"setfont":var sfTokens=tokenizer.tokenize(restOfString,0,restOfString.length);if(sfTokens.length>=4&&"-"===sfTokens[0].token&&"number"===sfTokens[1].type){var sfNum=parseInt(sfTokens[1].token);sfNum>=1&&sfNum<=4&&(multilineVars.setfont||(multilineVars.setfont=[]),sfTokens.shift(),sfTokens.shift(),multilineVars.setfont[sfNum]=getFontParameter(sfTokens,multilineVars.setfont[sfNum],str,0,"setfont"))}break;case"gchordfont":case"partsfont":case"vocalfont":case"textfont":case"annotationfont":case"historyfont":case"infofont":case"measurefont":case"repeatfont":case"wordsfont":return getChangingFont(cmd,tokens,str);case"composerfont":case"subtitlefont":case"tempofont":case"titlefont":case"voicefont":case"footerfont":case"headerfont":return getGlobalFont(cmd,tokens,str);case"barlabelfont":case"barnumberfont":case"barnumfont":return getChangingFont("measurefont",tokens,str);case"staves":case"score":multilineVars.score_is_present=!0;for(var lastVoice,addVoice=function(id,newStaff,bracket,brace,continueBar){(newStaff||0===multilineVars.staves.length)&&multilineVars.staves.push({index:multilineVars.staves.length,numVoices:0});var staff=window.ABCJS.parse.last(multilineVars.staves);void 0!==bracket&&(staff.bracket=bracket),void 0!==brace&&(staff.brace=brace),continueBar&&(staff.connectBarLines="end"),void 0===multilineVars.voices[id]&&(multilineVars.voices[id]={staffNum:staff.index,index:staff.numVoices},staff.numVoices++)},openParen=!1,openBracket=!1,openBrace=!1,justOpenParen=!1,justOpenBracket=!1,justOpenBrace=!1,continueBar=!1,addContinueBar=function(){if(continueBar=!0,lastVoice){var ty="start";lastVoice.staffNum>0&&("start"!==multilineVars.staves[lastVoice.staffNum-1].connectBarLines&&"continue"!==multilineVars.staves[lastVoice.staffNum-1].connectBarLines||(ty="continue")),multilineVars.staves[lastVoice.staffNum].connectBarLines=ty}};tokens.length;){var t=tokens.shift();switch(t.token){case"(":openParen?warn("Can't nest parenthesis in %%score",str,t.start):(openParen=!0,justOpenParen=!0);break;case")":!openParen||justOpenParen?warn("Unexpected close parenthesis in %%score",str,t.start):openParen=!1;break;case"[":openBracket?warn("Can't nest brackets in %%score",str,t.start):(openBracket=!0,justOpenBracket=!0);break;case"]":!openBracket||justOpenBracket?warn("Unexpected close bracket in %%score",str,t.start):(openBracket=!1,multilineVars.staves[lastVoice.staffNum].bracket="end");break;case"{":openBrace?warn("Can't nest braces in %%score",str,t.start):(openBrace=!0,justOpenBrace=!0);break;case"}":!openBrace||justOpenBrace?warn("Unexpected close brace in %%score",str,t.start):(openBrace=!1,multilineVars.staves[lastVoice.staffNum].brace="end");break;case"|":addContinueBar();break;default:for(var vc="";("alpha"===t.type||"number"===t.type)&&(vc+=t.token,t.continueId);)t=tokens.shift();var newStaff=!openParen||justOpenParen,bracket=justOpenBracket?"start":openBracket?"continue":void 0,brace=justOpenBrace?"start":openBrace?"continue":void 0;addVoice(vc,newStaff,bracket,brace,continueBar),justOpenParen=!1,justOpenBracket=!1,justOpenBrace=!1,continueBar=!1,lastVoice=multilineVars.voices[vc],"staves"===cmd&&addContinueBar()}}break;case"newpage":var pgNum=tokenizer.getInt(restOfString);tune.addNewPage(0===pgNum.digits?-1:pgNum.value);break;case"abc":var arr=restOfString.split(" ");switch(arr[0]){case"-copyright":case"-creator":case"-edited-by":case"-version":case"-charset":var subCmd=arr.shift();tune.addMetaText(cmd+subCmd,arr.join(" "));break;default:return"Unknown directive: "+cmd+arr[0]}break;case"header":case"footer":var footerStr=tokenizer.getMeat(restOfString,0,restOfString.length);footerStr=restOfString.substring(footerStr.start,footerStr.end),'"'===footerStr.charAt(0)&&'"'===footerStr.charAt(footerStr.length-1)&&(footerStr=footerStr.substring(1,footerStr.length-1));var footerArr=footerStr.split("\t"),footer={};footer=1===footerArr.length?{left:"",center:footerArr[0],right:""}:2===footerArr.length?{left:footerArr[0],center:footerArr[1],right:""}:{left:footerArr[0],center:footerArr[1],right:footerArr[2]},footerArr.length>3&&warn("Too many tabs in "+cmd+": "+footerArr.length+" found.",restOfString,0),tune.addMetaTextObj(cmd,footer);break;case"midi":var midi=tokenizer.tokenize(restOfString,0,restOfString.length,!0);midi.length>0&&"="===midi[0].token&&midi.shift(),0===midi.length?warn("Expected midi command",restOfString,0):parseMidiCommand(midi,tune,restOfString);break;case"playtempo":case"auquality":case"continuous":case"nobarcheck":tune.formatting[cmd]=restOfString;break;default:return"Unknown directive: "+cmd}return null},window.ABCJS.parse.parseDirective.globalFormatting=function(formatHash){for(var cmd in formatHash)if(formatHash.hasOwnProperty(cmd)){var scratch,value=""+formatHash[cmd],tokens=tokenizer.tokenize(value,0,value.length);switch(cmd){case"titlefont":case"gchordfont":getChangingFont(cmd,tokens,value);break;case"scale":setScale(cmd,tokens);break;case"partsbox":scratch=addMultilineVarBool("partsBox",cmd,tokens),null!==scratch&&warn(scratch);break;default:warn("Formatting directive unrecognized: ",cmd,0)}}}}(),window.ABCJS||(window.ABCJS={}),window.ABCJS.parse||(window.ABCJS.parse={}),window.ABCJS.parse.ParseHeader=function(tokenizer,warn,multilineVars,tune){this.reset=function(tokenizer,warn,multilineVars,tune){window.ABCJS.parse.parseKeyVoice.initialize(tokenizer,warn,multilineVars,tune),window.ABCJS.parse.parseDirective.initialize(tokenizer,warn,multilineVars,tune)},this.reset(tokenizer,warn,multilineVars,tune),this.setTitle=function(title){if(multilineVars.hasMainTitle)tune.addSubtitle(tokenizer.translateString(tokenizer.stripComment(title)));else{var titleStr=tokenizer.translateString(tokenizer.theReverser(tokenizer.stripComment(title)));multilineVars.titlecaps&&(titleStr=titleStr.toUpperCase()),tune.addMetaText("title",titleStr),multilineVars.hasMainTitle=!0}},this.setMeter=function(line){if(line=tokenizer.stripComment(line),"C"===line)return multilineVars.havent_set_length===!0&&(multilineVars.default_length=.125),{type:"common_time"};if("C|"===line)return multilineVars.havent_set_length===!0&&(multilineVars.default_length=.125),{type:"cut_time"};if("o"===line)return multilineVars.havent_set_length===!0&&(multilineVars.default_length=.125),{type:"tempus_perfectum"};if("c"===line)return multilineVars.havent_set_length===!0&&(multilineVars.default_length=.125),{type:"tempus_imperfectum"};if("o."===line)return multilineVars.havent_set_length===!0&&(multilineVars.default_length=.125),{type:"tempus_perfectum_prolatio"};if("c."===line)return multilineVars.havent_set_length===!0&&(multilineVars.default_length=.125),{type:"tempus_imperfectum_prolatio"};if(0===line.length||"none"===line.toLowerCase())return multilineVars.havent_set_length===!0&&(multilineVars.default_length=.125),null;var tokens=tokenizer.tokenize(line,0,line.length);try{var parseNum=function(){var ret={value:0,num:""},tok=tokens.shift();for("("===tok.token&&(tok=tokens.shift());;){if("number"!==tok.type)throw"Expected top number of meter";if(ret.value+=parseInt(tok.token),ret.num+=tok.token,0===tokens.length||"/"===tokens[0].token)return ret;if(tok=tokens.shift(),")"===tok.token){if(0===tokens.length||"/"===tokens[0].token)return ret;throw"Unexpected paren in meter"}if("."!==tok.token&&"+"!==tok.token)throw"Expected top number of meter";if(ret.num+=tok.token,0===tokens.length)throw"Expected top number of meter";tok=tokens.shift()}return ret},parseFraction=function(){var ret=parseNum();if(0===tokens.length)return ret;var tok=tokens.shift();if("/"!==tok.token)throw"Expected slash in meter";if(tok=tokens.shift(),"number"!==tok.type)throw"Expected bottom number of meter";return ret.den=tok.token,ret.value=ret.value/parseInt(ret.den),ret};if(0===tokens.length)throw"Expected meter definition in M: line";for(var meter={type:"specified",value:[]},totalLength=0;;){var ret=parseFraction();totalLength+=ret.value;var mv={num:ret.num};if(void 0!==ret.den&&(mv.den=ret.den),meter.value.push(mv),0===tokens.length)break}return multilineVars.havent_set_length===!0&&(multilineVars.default_length=totalLength<.75?.0625:.125),meter}catch(e){warn(e,line,0)}return null},this.calcTempo=function(relTempo){var dur=.25;multilineVars.meter&&"specified"===multilineVars.meter.type?dur=1/parseInt(multilineVars.meter.value[0].den):multilineVars.origMeter&&"specified"===multilineVars.origMeter.type&&(dur=1/parseInt(multilineVars.origMeter.value[0].den));for(var i=0;i0&&(multilineVars.default_length=n/d,multilineVars.havent_set_length=!1)}},this.setTempo=function(line,start,end){try{var tokens=tokenizer.tokenize(line,start,end);if(0===tokens.length)throw"Missing parameter in Q: field";var tempo={},delaySet=!0,token=tokens.shift();if("quote"===token.type&&(tempo.preString=token.token,token=tokens.shift(),0===tokens.length))return{type:"immediate",tempo:tempo};if("alpha"===token.type&&"C"===token.token){if(0===tokens.length)throw"Missing tempo after C in Q: field";if(token=tokens.shift(),"punct"===token.type&&"="===token.token){if(0===tokens.length)throw"Missing tempo after = in Q: field";if(token=tokens.shift(),"number"!==token.type)throw"Expected number after = in Q: field";tempo.duration=[1],tempo.bpm=parseInt(token.token)}else{if("number"!==token.type)throw"Expected number or equal after C in Q: field";if(tempo.duration=[parseInt(token.token)],0===tokens.length)throw"Missing = after duration in Q: field";if(token=tokens.shift(),"punct"!==token.type||"="!==token.token)throw"Expected = after duration in Q: field";if(0===tokens.length)throw"Missing tempo after = in Q: field";if(token=tokens.shift(),"number"!==token.type)throw"Expected number after = in Q: field";tempo.bpm=parseInt(token.token)}}else{if("number"!==token.type)throw"Unknown value in Q: field";var num=parseInt(token.token);if(0===tokens.length||"quote"===tokens[0].type)tempo.duration=[1],tempo.bpm=num;else{if(delaySet=!1,token=tokens.shift(),"punct"!==token.type&&"/"!==token.token)throw"Expected fraction in Q: field";if(token=tokens.shift(),"number"!==token.type)throw"Expected fraction in Q: field";var den=parseInt(token.token);for(tempo.duration=[num/den];tokens.length>0&&"="!==tokens[0].token&&"quote"!==tokens[0].type;){if(token=tokens.shift(),"number"!==token.type)throw"Expected fraction in Q: field";if(num=parseInt(token.token),token=tokens.shift(),"punct"!==token.type&&"/"!==token.token)throw"Expected fraction in Q: field";if(token=tokens.shift(),"number"!==token.type)throw"Expected fraction in Q: field";den=parseInt(token.token),tempo.duration.push(num/den)}if(token=tokens.shift(),"punct"!==token.type&&"="!==token.token)throw"Expected = in Q: field";if(token=tokens.shift(),"number"!==token.type)throw"Expected tempo in Q: field";tempo.bpm=parseInt(token.token)}}if(0!==tokens.length&&(token=tokens.shift(),"quote"===token.type&&(tempo.postString=token.token,token=tokens.shift()),0!==tokens.length))throw"Unexpected string at end of Q: field";return multilineVars.printTempo===!1&&(tempo.suppress=!0),{type:delaySet?"delaySet":"immediate",tempo:tempo}}catch(msg){return warn(msg,line,start),{type:"none"}}},this.letter_to_inline_header=function(line,i){var ws=tokenizer.eatWhiteSpace(line,i); if(i+=ws,line.length>=i+5&&"["===line.charAt(i)&&":"===line.charAt(i+2)){var e=line.indexOf("]",i);switch(line.substring(i,i+3)){case"[I:":var err=window.ABCJS.parse.parseDirective.addDirective(line.substring(i+3,e));return err&&warn(err,line,i),[e-i+1+ws];case"[M:":var meter=this.setMeter(line.substring(i+3,e));return tune.hasBeginMusic()&&meter?tune.appendStartingElement("meter",-1,-1,meter):multilineVars.meter=meter,[e-i+1+ws];case"[K:":var result=window.ABCJS.parse.parseKeyVoice.parseKey(line.substring(i+3,e));return result.foundClef&&tune.hasBeginMusic()&&tune.appendStartingElement("clef",-1,-1,multilineVars.clef),result.foundKey&&tune.hasBeginMusic()&&tune.appendStartingElement("key",-1,-1,window.ABCJS.parse.parseKeyVoice.fixKey(multilineVars.clef,multilineVars.key)),[e-i+1+ws];case"[P:":return tune.lines.length<=tune.lineNum?multilineVars.partForNextLine=line.substring(i+3,e):tune.appendElement("part",-1,-1,{title:line.substring(i+3,e)}),[e-i+1+ws];case"[L:":return this.setDefaultLength(line,i+3,e),[e-i+1+ws];case"[Q:":if(e>0){var tempo=this.setTempo(line,i+3,e);return"delaySet"===tempo.type?tune.appendElement("tempo",-1,-1,this.calcTempo(tempo.tempo)):"immediate"===tempo.type&&tune.appendElement("tempo",-1,-1,tempo.tempo),[e-i+1+ws,line.charAt(i+1),line.substring(i+3,e)]}break;case"[V:":if(e>0)return window.ABCJS.parse.parseKeyVoice.parseVoice(line,i+3,e),[e-i+1+ws,line.charAt(i+1),line.substring(i+3,e)]}}return[0]},this.letter_to_body_header=function(line,i){if(line.length>=i+3)switch(line.substring(i,i+2)){case"I:":var err=window.ABCJS.parse.parseDirective.addDirective(line.substring(i+2));return err&&warn(err,line,i),[line.length];case"M:":var meter=this.setMeter(line.substring(i+2));return tune.hasBeginMusic()&&meter&&tune.appendStartingElement("meter",-1,-1,meter),[line.length];case"K:":var result=window.ABCJS.parse.parseKeyVoice.parseKey(line.substring(i+2));return result.foundClef&&tune.hasBeginMusic()&&tune.appendStartingElement("clef",-1,-1,multilineVars.clef),result.foundKey&&tune.hasBeginMusic()&&tune.appendStartingElement("key",-1,-1,window.ABCJS.parse.parseKeyVoice.fixKey(multilineVars.clef,multilineVars.key)),[line.length];case"P:":return tune.hasBeginMusic()&&tune.appendElement("part",-1,-1,{title:line.substring(i+2)}),[line.length];case"L:":return this.setDefaultLength(line,i+2,line.length),[line.length];case"Q:":var e=line.indexOf("",i+2);e===-1&&(e=line.length);var tempo=this.setTempo(line,i+2,e);return"delaySet"===tempo.type?tune.appendElement("tempo",-1,-1,this.calcTempo(tempo.tempo)):"immediate"===tempo.type&&tune.appendElement("tempo",-1,-1,tempo.tempo),[e,line.charAt(i),window.ABCJS.parse.strip(line.substring(i+2))];case"V:":return window.ABCJS.parse.parseKeyVoice.parseVoice(line,2,line.length),[line.length,line.charAt(i),window.ABCJS.parse(line.substring(i+2))]}return[0]};var metaTextHeaders={A:"author",B:"book",C:"composer",D:"discography",F:"url",G:"group",I:"instruction",N:"notes",O:"origin",R:"rhythm",S:"source",W:"unalignedWords",Z:"transcription"};this.parseHeader=function(line){if(window.ABCJS.parse.startsWith(line,"%%")){var err=window.ABCJS.parse.parseDirective.addDirective(line.substring(2));return err&&warn(err,line,2),{}}var i=line.indexOf("%");if(i>=0&&(line=line.substring(0,i)),line=line.replace(/\s+$/,""),0===line.length)return{};if(line.length>=2&&":"===line.charAt(1)){var nextLine="";line.indexOf("")>=0&&"w"!==line.charAt(0)&&(nextLine=line.substring(line.indexOf("")+1),line=line.substring(0,line.indexOf("")));var field=metaTextHeaders[line.charAt(0)];if(void 0!==field)return"unalignedWords"===field?tune.addMetaTextArray(field,window.ABCJS.parse.parseDirective.parseFontChangeLine(tokenizer.translateString(tokenizer.stripComment(line.substring(2))))):tune.addMetaText(field,tokenizer.translateString(tokenizer.stripComment(line.substring(2)))),{};switch(line.charAt(0)){case"H":tune.addMetaText("history",tokenizer.translateString(tokenizer.stripComment(line.substring(2)))),multilineVars.is_in_history=!0;break;case"K":this.resolveTempo();var result=window.ABCJS.parse.parseKeyVoice.parseKey(line.substring(2));!multilineVars.is_in_header&&tune.hasBeginMusic()&&(result.foundClef&&tune.appendStartingElement("clef",-1,-1,multilineVars.clef),result.foundKey&&tune.appendStartingElement("key",-1,-1,window.ABCJS.parse.parseKeyVoice.fixKey(multilineVars.clef,multilineVars.key))),multilineVars.is_in_header=!1;break;case"L":this.setDefaultLength(line,2,line.length);break;case"M":multilineVars.origMeter=multilineVars.meter=this.setMeter(line.substring(2));break;case"P":multilineVars.is_in_header?tune.addMetaText("partOrder",tokenizer.translateString(tokenizer.stripComment(line.substring(2)))):multilineVars.partForNextLine=tokenizer.translateString(tokenizer.stripComment(line.substring(2)));break;case"Q":var tempo=this.setTempo(line,2,line.length);"delaySet"===tempo.type?multilineVars.tempo=tempo.tempo:"immediate"===tempo.type&&(tune.metaText.tempo=tempo.tempo);break;case"T":this.setTitle(line.substring(2));break;case"U":this.addUserDefinition(line,2,line.length);break;case"V":if(window.ABCJS.parse.parseKeyVoice.parseVoice(line,2,line.length),!multilineVars.is_in_header)return{newline:!0};break;case"s":return{symbols:!0};case"w":return{words:!0};case"X":break;case"E":case"m":warn("Ignored header",line,0);break;default:return nextLine.length&&(nextLine=""+nextLine),{regular:!0,str:line+nextLine}}return nextLine.length>0?{recurse:!0,str:nextLine}:{}}return{regular:!0,str:line}}},window.ABCJS||(window.ABCJS={}),window.ABCJS.parse||(window.ABCJS.parse={}),window.ABCJS.parse.parseKeyVoice={},function(){var tokenizer,warn,multilineVars,tune;window.ABCJS.parse.parseKeyVoice.initialize=function(tokenizer_,warn_,multilineVars_,tune_){tokenizer=tokenizer_,warn=warn_,multilineVars=multilineVars_,tune=tune_},window.ABCJS.parse.parseKeyVoice.standardKey=function(keyName){var key1sharp={acc:"sharp",note:"f"},key2sharp={acc:"sharp",note:"c"},key3sharp={acc:"sharp",note:"g"},key4sharp={acc:"sharp",note:"d"},key5sharp={acc:"sharp",note:"A"},key6sharp={acc:"sharp",note:"e"},key7sharp={acc:"sharp",note:"B"},key1flat={acc:"flat",note:"B"},key2flat={acc:"flat",note:"e"},key3flat={acc:"flat",note:"A"},key4flat={acc:"flat",note:"d"},key5flat={acc:"flat",note:"G"},key6flat={acc:"flat",note:"c"},key7flat={acc:"flat",note:"F"},keys={"C#":[key1sharp,key2sharp,key3sharp,key4sharp,key5sharp,key6sharp,key7sharp],"A#m":[key1sharp,key2sharp,key3sharp,key4sharp,key5sharp,key6sharp,key7sharp],"G#Mix":[key1sharp,key2sharp,key3sharp,key4sharp,key5sharp,key6sharp,key7sharp],"D#Dor":[key1sharp,key2sharp,key3sharp,key4sharp,key5sharp,key6sharp,key7sharp],"E#Phr":[key1sharp,key2sharp,key3sharp,key4sharp,key5sharp,key6sharp,key7sharp],"F#Lyd":[key1sharp,key2sharp,key3sharp,key4sharp,key5sharp,key6sharp,key7sharp],"B#Loc":[key1sharp,key2sharp,key3sharp,key4sharp,key5sharp,key6sharp,key7sharp],"F#":[key1sharp,key2sharp,key3sharp,key4sharp,key5sharp,key6sharp],"D#m":[key1sharp,key2sharp,key3sharp,key4sharp,key5sharp,key6sharp],"C#Mix":[key1sharp,key2sharp,key3sharp,key4sharp,key5sharp,key6sharp],"G#Dor":[key1sharp,key2sharp,key3sharp,key4sharp,key5sharp,key6sharp],"A#Phr":[key1sharp,key2sharp,key3sharp,key4sharp,key5sharp,key6sharp],BLyd:[key1sharp,key2sharp,key3sharp,key4sharp,key5sharp,key6sharp],"E#Loc":[key1sharp,key2sharp,key3sharp,key4sharp,key5sharp,key6sharp],B:[key1sharp,key2sharp,key3sharp,key4sharp,key5sharp],"G#m":[key1sharp,key2sharp,key3sharp,key4sharp,key5sharp],"F#Mix":[key1sharp,key2sharp,key3sharp,key4sharp,key5sharp],"C#Dor":[key1sharp,key2sharp,key3sharp,key4sharp,key5sharp],"D#Phr":[key1sharp,key2sharp,key3sharp,key4sharp,key5sharp],ELyd:[key1sharp,key2sharp,key3sharp,key4sharp,key5sharp],"A#Loc":[key1sharp,key2sharp,key3sharp,key4sharp,key5sharp],E:[key1sharp,key2sharp,key3sharp,key4sharp],"C#m":[key1sharp,key2sharp,key3sharp,key4sharp],BMix:[key1sharp,key2sharp,key3sharp,key4sharp],"F#Dor":[key1sharp,key2sharp,key3sharp,key4sharp],"G#Phr":[key1sharp,key2sharp,key3sharp,key4sharp],ALyd:[key1sharp,key2sharp,key3sharp,key4sharp],"D#Loc":[key1sharp,key2sharp,key3sharp,key4sharp],A:[key1sharp,key2sharp,key3sharp],"F#m":[key1sharp,key2sharp,key3sharp],EMix:[key1sharp,key2sharp,key3sharp],BDor:[key1sharp,key2sharp,key3sharp],"C#Phr":[key1sharp,key2sharp,key3sharp],DLyd:[key1sharp,key2sharp,key3sharp],"G#Loc":[key1sharp,key2sharp,key3sharp],D:[key1sharp,key2sharp],Bm:[key1sharp,key2sharp],AMix:[key1sharp,key2sharp],EDor:[key1sharp,key2sharp],"F#Phr":[key1sharp,key2sharp],GLyd:[key1sharp,key2sharp],"C#Loc":[key1sharp,key2sharp],G:[key1sharp],Em:[key1sharp],DMix:[key1sharp],ADor:[key1sharp],BPhr:[key1sharp],CLyd:[key1sharp],"F#Loc":[key1sharp],C:[],Am:[],GMix:[],DDor:[],EPhr:[],FLyd:[],BLoc:[],F:[key1flat],Dm:[key1flat],CMix:[key1flat],GDor:[key1flat],APhr:[key1flat],BbLyd:[key1flat],ELoc:[key1flat],Bb:[key1flat,key2flat],Gm:[key1flat,key2flat],FMix:[key1flat,key2flat],CDor:[key1flat,key2flat],DPhr:[key1flat,key2flat],EbLyd:[key1flat,key2flat],ALoc:[key1flat,key2flat],Eb:[key1flat,key2flat,key3flat],Cm:[key1flat,key2flat,key3flat],BbMix:[key1flat,key2flat,key3flat],FDor:[key1flat,key2flat,key3flat],GPhr:[key1flat,key2flat,key3flat],AbLyd:[key1flat,key2flat,key3flat],DLoc:[key1flat,key2flat,key3flat],Ab:[key1flat,key2flat,key3flat,key4flat],Fm:[key1flat,key2flat,key3flat,key4flat],EbMix:[key1flat,key2flat,key3flat,key4flat],BbDor:[key1flat,key2flat,key3flat,key4flat],CPhr:[key1flat,key2flat,key3flat,key4flat],DbLyd:[key1flat,key2flat,key3flat,key4flat],GLoc:[key1flat,key2flat,key3flat,key4flat],Db:[key1flat,key2flat,key3flat,key4flat,key5flat],Bbm:[key1flat,key2flat,key3flat,key4flat,key5flat],AbMix:[key1flat,key2flat,key3flat,key4flat,key5flat],EbDor:[key1flat,key2flat,key3flat,key4flat,key5flat],FPhr:[key1flat,key2flat,key3flat,key4flat,key5flat],GbLyd:[key1flat,key2flat,key3flat,key4flat,key5flat],CLoc:[key1flat,key2flat,key3flat,key4flat,key5flat],Gb:[key1flat,key2flat,key3flat,key4flat,key5flat,key6flat],Ebm:[key1flat,key2flat,key3flat,key4flat,key5flat,key6flat],DbMix:[key1flat,key2flat,key3flat,key4flat,key5flat,key6flat],AbDor:[key1flat,key2flat,key3flat,key4flat,key5flat,key6flat],BbPhr:[key1flat,key2flat,key3flat,key4flat,key5flat,key6flat],CbLyd:[key1flat,key2flat,key3flat,key4flat,key5flat,key6flat],FLoc:[key1flat,key2flat,key3flat,key4flat,key5flat,key6flat],Cb:[key1flat,key2flat,key3flat,key4flat,key5flat,key6flat,key7flat],Abm:[key1flat,key2flat,key3flat,key4flat,key5flat,key6flat,key7flat],GbMix:[key1flat,key2flat,key3flat,key4flat,key5flat,key6flat,key7flat],DbDor:[key1flat,key2flat,key3flat,key4flat,key5flat,key6flat,key7flat],EbPhr:[key1flat,key2flat,key3flat,key4flat,key5flat,key6flat,key7flat],FbLyd:[key1flat,key2flat,key3flat,key4flat,key5flat,key6flat,key7flat],BbLoc:[key1flat,key2flat,key3flat,key4flat,key5flat,key6flat,key7flat],"A#":[key1flat,key2flat],"B#":[],"D#":[key1flat,key2flat,key3flat],"E#":[key1flat],"G#":[key1flat,key2flat,key3flat,key4flat],Gbm:[key1sharp,key2sharp,key3sharp,key4sharp,key5sharp,key6sharp,key7sharp]};return keys[keyName]};var clefLines={treble:{clef:"treble",pitch:4,mid:0},"treble+8":{clef:"treble+8",pitch:4,mid:0},"treble-8":{clef:"treble-8",pitch:4,mid:0},treble1:{clef:"treble",pitch:2,mid:2},treble2:{clef:"treble",pitch:4,mid:0},treble3:{clef:"treble",pitch:6,mid:-2},treble4:{clef:"treble",pitch:8,mid:-4},treble5:{clef:"treble",pitch:10,mid:-6},perc:{clef:"perc",pitch:6,mid:0},none:{clef:"none",mid:0},bass:{clef:"bass",pitch:8,mid:-12},"bass+8":{clef:"bass+8",pitch:8,mid:-12},"bass-8":{clef:"bass-8",pitch:8,mid:-12},"bass+16":{clef:"bass",pitch:8,mid:-12},"bass-16":{clef:"bass",pitch:8,mid:-12},bass1:{clef:"bass",pitch:2,mid:-6},bass2:{clef:"bass",pitch:4,mid:-8},bass3:{clef:"bass",pitch:6,mid:-10},bass4:{clef:"bass",pitch:8,mid:-12},bass5:{clef:"bass",pitch:10,mid:-14},tenor:{clef:"alto",pitch:8,mid:-8},tenor1:{clef:"alto",pitch:2,mid:-2},tenor2:{clef:"alto",pitch:4,mid:-4},tenor3:{clef:"alto",pitch:6,mid:-6},tenor4:{clef:"alto",pitch:8,mid:-8},tenor5:{clef:"alto",pitch:10,mid:-10},alto:{clef:"alto",pitch:6,mid:-6},alto1:{clef:"alto",pitch:2,mid:-2},alto2:{clef:"alto",pitch:4,mid:-4},alto3:{clef:"alto",pitch:6,mid:-6},alto4:{clef:"alto",pitch:8,mid:-8},alto5:{clef:"alto",pitch:10,mid:-10},"alto+8":{clef:"alto+8",pitch:6,mid:-6},"alto-8":{clef:"alto-8",pitch:6,mid:-6}},calcMiddle=function(clef,oct){var value=clefLines[clef],mid=value?value.mid:0;return mid+oct};window.ABCJS.parse.parseKeyVoice.fixClef=function(clef){var value=clefLines[clef.type];value&&(clef.clefPos=value.pitch,clef.type=value.clef)},window.ABCJS.parse.parseKeyVoice.deepCopyKey=function(key){var ret={accidentals:[],root:key.root,acc:key.acc,mode:key.mode};return window.ABCJS.parse.each(key.accidentals,function(k){ret.accidentals.push(window.ABCJS.parse.clone(k))}),ret};var pitches={A:5,B:6,C:0,D:1,E:2,F:3,G:4,a:12,b:13,c:7,d:8,e:9,f:10,g:11};window.ABCJS.parse.parseKeyVoice.addPosToKey=function(clef,key){var mid=clef.verticalPos;window.ABCJS.parse.each(key.accidentals,function(acc){var pitch=pitches[acc.note];pitch-=mid,acc.verticalPos=pitch}),key.impliedNaturals&&window.ABCJS.parse.each(key.impliedNaturals,function(acc){var pitch=pitches[acc.note];pitch-=mid,acc.verticalPos=pitch}),mid<-10?(window.ABCJS.parse.each(key.accidentals,function(acc){acc.verticalPos-=7,(acc.verticalPos>=11||10===acc.verticalPos&&"flat"===acc.acc)&&(acc.verticalPos-=7),"A"===acc.note&&"sharp"===acc.acc&&(acc.verticalPos-=7),"G"!==acc.note&&"F"!==acc.note||"flat"!==acc.acc||(acc.verticalPos-=7)}),key.impliedNaturals&&window.ABCJS.parse.each(key.impliedNaturals,function(acc){acc.verticalPos-=7,(acc.verticalPos>=11||10===acc.verticalPos&&"flat"===acc.acc)&&(acc.verticalPos-=7),"A"===acc.note&&"sharp"===acc.acc&&(acc.verticalPos-=7),"G"!==acc.note&&"F"!==acc.note||"flat"!==acc.acc||(acc.verticalPos-=7)})):mid<-4?(window.ABCJS.parse.each(key.accidentals,function(acc){acc.verticalPos-=7,mid!==-8||"f"!==acc.note&&"g"!==acc.note||"sharp"!==acc.acc||(acc.verticalPos-=7)}),key.impliedNaturals&&window.ABCJS.parse.each(key.impliedNaturals,function(acc){acc.verticalPos-=7,mid!==-8||"f"!==acc.note&&"g"!==acc.note||"sharp"!==acc.acc||(acc.verticalPos-=7)})):mid>=7&&(window.ABCJS.parse.each(key.accidentals,function(acc){acc.verticalPos+=7}),key.impliedNaturals&&window.ABCJS.parse.each(key.impliedNaturals,function(acc){acc.verticalPos+=7}))},window.ABCJS.parse.parseKeyVoice.fixKey=function(clef,key){var fixedKey=window.ABCJS.parse.clone(key);return window.ABCJS.parse.parseKeyVoice.addPosToKey(clef,fixedKey),fixedKey};var parseMiddle=function(str){for(var mid=pitches[str.charAt(0)],i=1;i0){ret.foundKey=!0;var acc="",mode="";tokens[0].token.length>1?tokens[0].token=tokens[0].token.substring(1):tokens.shift();var key=retPitch.token;if(tokens.length>0){var retAcc=tokenizer.getSharpFlat(tokens[0].token);if(retAcc.len>0&&(tokens[0].token.length>1?tokens[0].token=tokens[0].token.substring(1):tokens.shift(),key+=retAcc.token,acc=retAcc.token),tokens.length>0){var retMode=tokenizer.getMode(tokens[0].token);retMode.len>0&&(tokens.shift(),key+=retMode.token,mode=retMode.token)}if(void 0===window.ABCJS.parse.parseKeyVoice.standardKey(key))return warn("Unsupported key signature: "+key,str,0),ret}var oldKey=window.ABCJS.parse.parseKeyVoice.deepCopyKey(multilineVars.key);if(multilineVars.key=window.ABCJS.parse.parseKeyVoice.deepCopyKey({accidentals:window.ABCJS.parse.parseKeyVoice.standardKey(key)}),multilineVars.key.root=retPitch.token,multilineVars.key.acc=acc,multilineVars.key.mode=mode,oldKey){for(var kk,k=0;k0;)switch(tokens[0].token){case"m":case"middle":if(tokens.shift(),0===tokens.length)return warn("Expected = after middle",str,0),ret;if(token=tokens.shift(),"="!==token.token){warn("Expected = after middle",str,token.start);break}if(0===tokens.length)return warn("Expected parameter after middle=",str,0),ret;var pitch=tokenizer.getPitchFromTokens(tokens);pitch.warn&&warn(pitch.warn,str,0),pitch.position&&(multilineVars.clef.verticalPos=pitch.position-6);break;case"transpose":if(tokens.shift(),0===tokens.length)return warn("Expected = after transpose",str,0),ret;if(token=tokens.shift(),"="!==token.token){warn("Expected = after transpose",str,token.start);break}if(0===tokens.length)return warn("Expected parameter after transpose=",str,0),ret;if("number"!==tokens[0].type){warn("Expected number after transpose",str,tokens[0].start);break}multilineVars.clef.transpose=tokens[0].intt,tokens.shift();break;case"stafflines":if(tokens.shift(),0===tokens.length)return warn("Expected = after stafflines",str,0),ret;if(token=tokens.shift(),"="!==token.token){warn("Expected = after stafflines",str,token.start);break}if(0===tokens.length)return warn("Expected parameter after stafflines=",str,0),ret;if("number"!==tokens[0].type){warn("Expected number after stafflines",str,tokens[0].start);break}multilineVars.clef.stafflines=tokens[0].intt,tokens.shift();break;case"staffscale":if(tokens.shift(),0===tokens.length)return warn("Expected = after staffscale",str,0),ret;if(token=tokens.shift(),"="!==token.token){warn("Expected = after staffscale",str,token.start);break}if(0===tokens.length)return warn("Expected parameter after staffscale=",str,0),ret;if("number"!==tokens[0].type){warn("Expected number after staffscale",str,tokens[0].start);break}multilineVars.clef.staffscale=tokens[0].floatt,tokens.shift();break;case"style":if(tokens.shift(),0===tokens.length)return warn("Expected = after style",str,0),ret;if(token=tokens.shift(),"="!==token.token){warn("Expected = after style",str,token.start);break}if(0===tokens.length)return warn("Expected parameter after style=",str,0),ret;switch(tokens[0].token){case"normal":case"harmonic":case"rhythm":case"x":multilineVars.style=tokens[0].token,tokens.shift();break;default:warn("error parsing style element: "+tokens[0].token,str,tokens[0].start)}break;case"clef":if(tokens.shift(),0===tokens.length)return warn("Expected = after clef",str,0),ret;if(token=tokens.shift(),"="!==token.token){warn("Expected = after clef",str,token.start);break}if(0===tokens.length)return warn("Expected parameter after clef=",str,0),ret;case"treble":case"bass":case"alto":case"tenor":case"perc":var clef=tokens.shift();switch(clef.token){case"treble":case"tenor":case"alto":case"bass":case"perc":case"none":break;case"C":clef.token="alto";break;case"F":clef.token="bass";break;case"G":clef.token="treble";break;case"c":clef.token="alto";break;case"f":clef.token="bass";break;case"g":clef.token="treble";break;default:warn("Expected clef name. Found "+clef.token,str,clef.start)}tokens.length>0&&"number"===tokens[0].type&&(clef.token+=tokens[0].token,tokens.shift()),tokens.length>1&&("-"===tokens[0].token||"+"===tokens[0].token)&&"8"===tokens[1].token&&(clef.token+=tokens[0].token+tokens[1].token,tokens.shift(),tokens.shift()),multilineVars.clef={type:clef.token,verticalPos:calcMiddle(clef.token,0)},multilineVars.currentVoice&&void 0!==multilineVars.currentVoice.transpose&&(multilineVars.clef.transpose=multilineVars.currentVoice.transpose),ret.foundClef=!0;break;default:warn("Unknown parameter: "+tokens[0].token,str,tokens[0].start),tokens.shift()}return ret};var setCurrentVoice=function(id){multilineVars.currentVoice=multilineVars.voices[id],tune.setCurrentVoice(multilineVars.currentVoice.staffNum,multilineVars.currentVoice.index)};window.ABCJS.parse.parseKeyVoice.parseVoice=function(line,i,e){var ret=tokenizer.getMeat(line,i,e),start=ret.start,end=ret.end,id=tokenizer.getToken(line,start,end);if(0===id.length)return void warn("Expected a voice id",line,start);var isNew=!1;void 0===multilineVars.voices[id]&&(multilineVars.voices[id]={},isNew=!0,multilineVars.score_is_present&&warn("Can't have an unknown V: id when the %score directive is present",line,start)),start+=id.length,start+=tokenizer.eatWhiteSpace(line,start);for(var staffInfo={startStaff:isNew},addNextTokenToStaffInfo=function(name){var attr=tokenizer.getVoiceToken(line,start,end);void 0!==attr.warn?warn("Expected value for "+name+" in voice: "+attr.warn,line,start):0===attr.token.length&&'"'!==line.charAt(start)?warn("Expected value for "+name+" in voice",line,start):staffInfo[name]=attr.token,start+=attr.len},addNextTokenToVoiceInfo=function(id,name,type){var attr=tokenizer.getVoiceToken(line,start,end);void 0!==attr.warn?warn("Expected value for "+name+" in voice: "+attr.warn,line,start):0===attr.token.length&&'"'!==line.charAt(start)?warn("Expected value for "+name+" in voice",line,start):("number"===type&&(attr.token=parseFloat(attr.token)),multilineVars.voices[id][name]=attr.token),start+=attr.len};start=str.length};this.eatWhiteSpace=function(line,index){for(var i=index;i="a"&&str.charAt(start)<="z"||str.charAt(start)>="A"&&str.charAt(start)<="Z");)start++;return start},i=this.skipWhiteSpace(str);if(finished(str,i))return{len:0};var firstThree=str.substring(i,i+3).toLowerCase();switch((firstThree.length>1&&" "===firstThree.charAt(1)||"^"===firstThree.charAt(1)||"_"===firstThree.charAt(1)||"="===firstThree.charAt(1))&&(firstThree=firstThree.charAt(0)),firstThree){case"mix":return{len:skipAlpha(str,i),token:"Mix"};case"dor":return{len:skipAlpha(str,i),token:"Dor"};case"phr":return{len:skipAlpha(str,i),token:"Phr"};case"lyd":return{len:skipAlpha(str,i),token:"Lyd"};case"loc":return{len:skipAlpha(str,i),token:"Loc"};case"aeo":return{len:skipAlpha(str,i),token:"m"};case"maj":return{len:skipAlpha(str,i),token:""};case"ion":return{len:skipAlpha(str,i),token:""};case"min":return{len:skipAlpha(str,i),token:"m"};case"m":return{len:skipAlpha(str,i),token:"m"}}return{len:0}},this.getClef=function(str,bExplicitOnly){var strOrig=str,i=this.skipWhiteSpace(str);if(finished(str,i))return{len:0};var needsClef=!1,strClef=str.substring(i);if(window.ABCJS.parse.startsWith(strClef,"clef=")&&(needsClef=!0,strClef=strClef.substring(5),i+=5),0===strClef.length&&needsClef)return{len:i+5,warn:"No clef specified: "+strOrig};var j=this.skipWhiteSpace(strClef);if(finished(strClef,j))return{len:0};j>0&&(i+=j,strClef=strClef.substring(j));var name=null;if(window.ABCJS.parse.startsWith(strClef,"treble"))name="treble";else if(window.ABCJS.parse.startsWith(strClef,"bass3"))name="bass3";else if(window.ABCJS.parse.startsWith(strClef,"bass"))name="bass";else if(window.ABCJS.parse.startsWith(strClef,"tenor"))name="tenor";else if(window.ABCJS.parse.startsWith(strClef,"alto2"))name="alto2";else if(window.ABCJS.parse.startsWith(strClef,"alto1"))name="alto1";else if(window.ABCJS.parse.startsWith(strClef,"alto"))name="alto";else if(!bExplicitOnly&&needsClef&&window.ABCJS.parse.startsWith(strClef,"none"))name="none";else if(window.ABCJS.parse.startsWith(strClef,"perc"))name="perc";else if(!bExplicitOnly&&needsClef&&window.ABCJS.parse.startsWith(strClef,"C"))name="tenor";else if(!bExplicitOnly&&needsClef&&window.ABCJS.parse.startsWith(strClef,"F"))name="bass";else{if(bExplicitOnly||!needsClef||!window.ABCJS.parse.startsWith(strClef,"G"))return{len:i+5,warn:"Unknown clef specified: "+strOrig};name="treble"}return strClef=strClef.substring(name.length),j=this.isMatch(strClef,"+8"),j>0?name+="+8":(j=this.isMatch(strClef,"-8"),j>0&&(name+="-8")),{len:i+name.length,token:name,explicit:needsClef}},this.getBarLine=function(line,i){switch(line.charAt(i)){case"]":switch(++i,line.charAt(i)){case"|":return{len:2,token:"bar_thick_thin"};case"[":return++i,line.charAt(i)>="1"&&line.charAt(i)<="9"||'"'===line.charAt(i)?{len:2,token:"bar_invisible"}:{len:1,warn:"Unknown bar symbol"};default:return{len:1,token:"bar_invisible"}}break;case":":switch(++i,line.charAt(i)){case":":return{len:2,token:"bar_dbl_repeat"};case"|":switch(++i,line.charAt(i)){case"]":switch(++i,line.charAt(i)){case"|":return++i,":"===line.charAt(i)?{len:5,token:"bar_dbl_repeat"}:{len:3,token:"bar_right_repeat"};default:return{len:3,token:"bar_right_repeat"}}break;case"|":return++i,":"===line.charAt(i)?{len:4,token:"bar_dbl_repeat"}:{len:3,token:"bar_right_repeat"};default:return{len:2,token:"bar_right_repeat"}}break;default:return{len:1,warn:"Unknown bar symbol"}}break;case"[":if(++i,"|"!==line.charAt(i))return line.charAt(i)>="1"&&line.charAt(i)<="9"||'"'===line.charAt(i)?{len:1,token:"bar_invisible"}:{len:0};switch(++i,line.charAt(i)){case":":return{len:3,token:"bar_left_repeat"};case"]":return{len:3,token:"bar_invisible"};default:return{len:2,token:"bar_thick_thin"}}break;case"|":switch(++i,line.charAt(i)){case"]":return{len:2,token:"bar_thin_thick"};case"|":return++i,":"===line.charAt(i)?{len:3,token:"bar_left_repeat"}:{len:2,token:"bar_thin_thin"};case":":for(var colons=0;":"===line.charAt(i+colons);)colons++;return{len:1+colons,token:"bar_left_repeat"};default:return{len:1,token:"bar_thin"}}}return{len:0}},this.getTokenOf=function(str,legalChars){for(var i=0;i0;){var acc;if("^"===tokens[0].token){if(acc="sharp",tokens.shift(),0===tokens.length)return{accs:accs,warn:"Expected note name after "+acc};switch(tokens[0].token){case"^":acc="dblsharp",tokens.shift();break;case"/":acc="quartersharp",tokens.shift()}}else if("="===tokens[0].token)acc="natural",tokens.shift();else{if("_"!==tokens[0].token)return{accs:accs};if(acc="flat",tokens.shift(),0===tokens.length)return{accs:accs,warn:"Expected note name after "+acc};switch(tokens[0].token){case"_":acc="dblflat",tokens.shift();break;case"/":acc="quarterflat",tokens.shift()}}if(0===tokens.length)return{accs:accs,warn:"Expected note name after "+acc};switch(tokens[0].token.charAt(0)){case"a":case"b":case"c":case"d":case"e":case"f":case"g":case"A":case"B":case"C":case"D":case"E":case"F":case"G":void 0===accs&&(accs=[]),accs.push({acc:acc,note:tokens[0].token.charAt(0)}),1===tokens[0].token.length?tokens.shift():tokens[0].token=tokens[0].token.substring(1);break;default:return{accs:accs,warn:"Expected note name after "+acc+" Found: "+tokens[0].token}}}return{accs:accs}},this.getKeyAccidental=function(str){var accTranslation={"^":"sharp","^^":"dblsharp","=":"natural",_:"flat",__:"dblflat","_/":"quarterflat","^/":"quartersharp"},i=this.skipWhiteSpace(str);if(finished(str,i))return{len:0};var acc=null;switch(str.charAt(i)){case"^":case"_":case"=":acc=str.charAt(i);break;default:return{len:0}}if(i++,finished(str,i))return{len:1,warn:"Expected note name after accidental"};switch(str.charAt(i)){case"a":case"b":case"c":case"d":case"e":case"f":case"g":case"A":case"B":case"C":case"D":case"E":case"F":case"G":return{len:i+1,token:{acc:accTranslation[acc],note:str.charAt(i)}};case"^":case"_":case"/":if(acc+=str.charAt(i),i++,finished(str,i))return{len:2,warn:"Expected note name after accidental"};switch(str.charAt(i)){case"a":case"b":case"c":case"d":case"e":case"f":case"g":case"A":case"B":case"C":case"D":case"E":case"F":case"G":return{len:i+1,token:{acc:accTranslation[acc],note:str.charAt(i)}};default:return{len:2,warn:"Expected note name after accidental"}}break;default:return{len:1,warn:"Expected note name after accidental"}}},this.isWhiteSpace=function(ch){return" "===ch||"\t"===ch||""===ch},this.getMeat=function(line,start,end){var comment=line.indexOf("%",start);for(comment>=0&&comment="A"&&ch<="Z"||ch>="a"&&ch<="z"},isNumber=function(ch){return ch>="0"&&ch<="9"};this.tokenize=function(line,start,end,alphaUntilWhiteSpace){var ret=this.getMeat(line,start,end);start=ret.start,end=ret.end;for(var i,tokens=[];start=end?{len:1,err:"Missing close quote"}:{len:close-start+1,token:this.translateString(line.substring(i+1,close))}}for(var ii=i;ii=0?window.ABCJS.parse.strip(str.substring(0,i)):window.ABCJS.parse.strip(str)},this.getInt=function(str){var x=parseInt(str);if(isNaN(x))return{digits:0};var s=""+x,i=str.indexOf(s);return{value:x,digits:i+s.length}},this.getFloat=function(str){var x=parseFloat(str);if(isNaN(x))return{digits:0};var s=""+x,i=str.indexOf(s);return{value:x,digits:i+s.length}},this.getMeasurement=function(tokens){if(0===tokens.length)return{used:0};var used=1,num="";if("-"===tokens[0].token)tokens.shift(),num="-",used++;else if("number"!==tokens[0].type)return{used:0};if(num+=tokens.shift().token,0===tokens.length)return{used:1,value:parseInt(num)};var x=tokens.shift();if("."===x.token){if(used++,0===tokens.length)return{used:used,value:parseInt(num)};if("number"===tokens[0].type&&(x=tokens.shift(),num=num+"."+x.token,used++,0===tokens.length))return{used:used,value:parseFloat(num)};x=tokens.shift()}switch(x.token){case"pt":return{used:used+1,value:parseFloat(num)};case"cm":return{used:used+1,value:parseFloat(num)/2.54*72};case"in":return{used:used+1,value:72*parseFloat(num)};default:return tokens.unshift(x),{used:used,value:parseFloat(num)}}return{used:0}};var substInChord=function(str){for(;str.indexOf("\\n")!==-1;)str=str.replace("\\n","\n");return str};this.getBrackettedSubstring=function(line,i,maxErrorChars,_matchChar){for(var matchChar=_matchChar||line.charAt(i),pos=i+1;posline.length-1&&(pos=line.length-1),[pos-i+1,substInChord(line.substring(i+1,pos)),!1])}},window.ABCJS||(window.ABCJS={}),window.ABCJS.write||(window.ABCJS.write={}),ABCJS.write.AbsoluteElement=function(abcelem,duration,minspacing,type,tuneNumber){this.tuneNumber=tuneNumber,this.abcelem=abcelem,this.duration=duration,this.minspacing=minspacing||0,this.x=0,this.children=[],this.heads=[],this.extra=[],this.extraw=0,this.w=0,this.right=[],this.invisible=!1,this.bottom=void 0,this.top=void 0,this.type=type,this.specialY={tempoHeightAbove:0,partHeightAbove:0,volumeHeightAbove:0,dynamicHeightAbove:0,endingHeightAbove:0,chordHeightAbove:0,lyricHeightAbove:0,lyricHeightBelow:0,chordHeightBelow:0,volumeHeightBelow:0,dynamicHeightBelow:0}},ABCJS.write.AbsoluteElement.prototype.setUpperAndLowerElements=function(specialYResolved){for(var i=0;ithis.w&&(this.w=right.dx+right.w),this.right[this.right.length]=right,this.addChild(right)},ABCJS.write.AbsoluteElement.prototype.addCentered=function(elem){var half=elem.w/2;-halfthis.w&&(this.w=elem.dx+half),this.right[this.right.length]=elem,this.addChild(elem)},ABCJS.write.AbsoluteElement.prototype.setLimit=function(member,child){child[member]&&(this.specialY[member]?this.specialY[member]=Math.max(this.specialY[member],child[member]):this.specialY[member]=child[member])},ABCJS.write.AbsoluteElement.prototype.addChild=function(child){child.parent=this,this.children[this.children.length]=child,this.pushTop(child.top),this.pushBottom(child.bottom),this.setLimit("tempoHeightAbove",child),this.setLimit("partHeightAbove",child),this.setLimit("volumeHeightAbove",child),this.setLimit("dynamicHeightAbove",child),this.setLimit("endingHeightAbove",child),this.setLimit("chordHeightAbove",child),this.setLimit("lyricHeightAbove",child),this.setLimit("lyricHeightBelow",child),this.setLimit("chordHeightBelow",child),this.setLimit("volumeHeightBelow",child),this.setLimit("dynamicHeightBelow",child)},ABCJS.write.AbsoluteElement.prototype.pushTop=function(top){void 0!==top&&(void 0===this.top?this.top=top:this.top=Math.max(top,this.top))},ABCJS.write.AbsoluteElement.prototype.pushBottom=function(bottom){void 0!==bottom&&(void 0===this.bottom?this.bottom=bottom:this.bottom=Math.min(bottom,this.bottom))},ABCJS.write.AbsoluteElement.prototype.setX=function(x){this.x=x;for(var i=0;i0&&(kls.length>0&&" "!==kls.charAt(kls.length-1)&&(kls+=" "),kls+=addClass),this.elemset[i][0].setAttribute("class",kls)}},ABCJS.write.AbsoluteElement.prototype.highlight=function(klass,color){void 0===klass&&(klass="note_selected"),void 0===color&&(color="#ff0000"),this.setClass(klass,"",color)},ABCJS.write.AbsoluteElement.prototype.unhighlight=function(klass,color){void 0===klass&&(klass="note_selected"),void 0===color&&(color="#000000"),this.setClass("",klass,color)},window.ABCJS||(window.ABCJS={}),window.ABCJS.write||(window.ABCJS.write={}),function(){"use strict";ABCJS.write.getDuration=function(elem){var d=0;return elem.duration&&(d=elem.duration),d},ABCJS.write.getDurlog=function(duration){return void 0===duration?0:Math.floor(Math.log(duration)/Math.log(2))},ABCJS.write.AbstractEngraver=function(bagpipes,renderer,tuneNumber){this.decoration=new ABCJS.write.Decoration,this.renderer=renderer,this.tuneNumber=tuneNumber,this.isBagpipes=bagpipes,this.chartable={rest:{0:"rests.whole",1:"rests.half",2:"rests.quarter",3:"rests.8th",4:"rests.16th",5:"rests.32nd",6:"rests.64th",7:"rests.128th"},note:{"-1":"noteheads.dbl",0:"noteheads.whole",1:"noteheads.half",2:"noteheads.quarter",3:"noteheads.quarter",4:"noteheads.quarter",5:"noteheads.quarter",6:"noteheads.quarter",7:"noteheads.quarter",nostem:"noteheads.quarter"},rhythm:{"-1":"noteheads.slash.whole",0:"noteheads.slash.whole",1:"noteheads.slash.whole",2:"noteheads.slash.quarter",3:"noteheads.slash.quarter",4:"noteheads.slash.quarter",5:"noteheads.slash.quarter",6:"noteheads.slash.quarter",7:"noteheads.slash.quarter",nostem:"noteheads.slash.nostem"},x:{"-1":"noteheads.indeterminate",0:"noteheads.indeterminate",1:"noteheads.indeterminate",2:"noteheads.indeterminate",3:"noteheads.indeterminate",4:"noteheads.indeterminate",5:"noteheads.indeterminate",6:"noteheads.indeterminate",7:"noteheads.indeterminate",nostem:"noteheads.indeterminate"},harmonic:{"-1":"noteheads.harmonic.quarter",0:"noteheads.harmonic.quarter",1:"noteheads.harmonic.quarter",2:"noteheads.harmonic.quarter",3:"noteheads.harmonic.quarter",4:"noteheads.harmonic.quarter",5:"noteheads.harmonic.quarter",6:"noteheads.harmonic.quarter",7:"noteheads.harmonic.quarter",nostem:"noteheads.harmonic.quarter"},uflags:{3:"flags.u8th",4:"flags.u16th",5:"flags.u32nd",6:"flags.u64th"},dflags:{3:"flags.d8th",4:"flags.d16th",5:"flags.d32nd",6:"flags.d64th"}},this.reset()},ABCJS.write.AbstractEngraver.prototype.reset=function(){this.slurs={},this.ties=[],this.slursbyvoice={},this.tiesbyvoice={},this.endingsbyvoice={},this.s=0,this.v=0,this.tripletmultiplier=1,this.abcline=void 0,this.accidentalSlot=void 0,this.accidentalshiftx=void 0,this.dotshiftx=void 0,this.hasVocals=!1,this.minY=void 0,this.partstartelem=void 0,this.pos=void 0,this.roomtaken=void 0,this.roomtakenright=void 0,this.staffgroup=void 0,this.startlimitelem=void 0,this.stemdir=void 0,this.voice=void 0},ABCJS.write.AbstractEngraver.prototype.setStemHeight=function(heightInPixels){this.stemHeight=heightInPixels/ABCJS.write.spacing.STEP},ABCJS.write.AbstractEngraver.prototype.getCurrentVoiceId=function(){return"s"+this.s+"v"+this.v},ABCJS.write.AbstractEngraver.prototype.pushCrossLineElems=function(){this.slursbyvoice[this.getCurrentVoiceId()]=this.slurs,this.tiesbyvoice[this.getCurrentVoiceId()]=this.ties,this.endingsbyvoice[this.getCurrentVoiceId()]=this.partstartelem},ABCJS.write.AbstractEngraver.prototype.popCrossLineElems=function(){this.slurs=this.slursbyvoice[this.getCurrentVoiceId()]||{},this.ties=this.tiesbyvoice[this.getCurrentVoiceId()]||[],this.partstartelem=this.endingsbyvoice[this.getCurrentVoiceId()]},ABCJS.write.AbstractEngraver.prototype.getElem=function(){return this.abcline.length<=this.pos?null:this.abcline[this.pos]},ABCJS.write.AbstractEngraver.prototype.getNextElem=function(){return this.abcline.length<=this.pos+1?null:this.abcline[this.pos+1]},ABCJS.write.AbstractEngraver.prototype.containsLyrics=function(staves){for(var i=0;ielem.pitches[p+1].pitch){sorted=!1;var tmp=elem.pitches[p];elem.pitches[p]=elem.pitches[p+1],elem.pitches[p+1]=tmp}}while(!sorted)},ABCJS.write.ledgerLines=function(abselem,minPitch,maxPitch,isRest,c,additionalLedgers,dir,dx,scale){for(var i=maxPitch;i>11;i--)i%2!==0||isRest||abselem.addChild(new ABCJS.write.RelativeElement(null,dx,(ABCJS.write.glyphs.getSymbolWidth(c)+4)*scale,i,{type:"ledger"}));for(i=minPitch;i<1;i++)i%2!==0||isRest||abselem.addChild(new ABCJS.write.RelativeElement(null,dx,(ABCJS.write.glyphs.getSymbolWidth(c)+4)*scale,i,{type:"ledger"}));for(i=0;i=6?"down":"up";this.stemdir&&(dir=this.stemdir);var style=elem.style?elem.style:this.style;style&&"normal"!==style||(style="note");var noteSymbol;for(noteSymbol=zeroDuration?this.chartable[style].nostem:this.chartable[style][-durlog],noteSymbol||console.log("noteSymbol:",style,durlog,zeroDuration),p="down"===dir?elem.pitches.length-2:1;"down"===dir?p>=0:p11||curr.verticalPos<1)&&additionalLedgers.push(curr.verticalPos-curr.verticalPos%2),"down"===dir?this.roomtaken=ABCJS.write.glyphs.getSymbolWidth(noteSymbol)+2:dotshiftx=ABCJS.write.glyphs.getSymbolWidth(noteSymbol)+2)}for(this.accidentalSlot=[],p=0;p6&&!this.stemdir&&(p1=6),p2="down"===dir?elem.maxpitch-1/3:elem.maxpitch+7,p2<6&&!this.stemdir&&(p2=6),dx="down"===dir||0===abselem.heads.length?0:abselem.heads[0].w,width="down"===dir?1:-1,"noteheads.slash.quarter"===notehead.c&&("down"===dir?p2-=1:p1+=1),abselem.addExtra(new ABCJS.write.RelativeElement(null,dx,0,p1,{type:"stem",pitch2:p2,linewidth:width})),this.minY=Math.min(p1,this.minY),this.minY=Math.min(p2,this.minY))}if(void 0!==elem.lyric){var lyricStr="";window.ABCJS.parse.each(elem.lyric,function(ly){lyricStr+=ly.syllable+ly.divider+"\n"});var lyricDim=this.renderer.getTextSize(lyricStr,"vocalfont","abc-lyric"),position=elem.positioning?elem.positioning.vocalPosition:"below";abselem.addCentered(new ABCJS.write.RelativeElement(lyricStr,0,lyricDim.width,void 0,{type:"lyric",position:position,height:lyricDim.height/ABCJS.write.spacing.STEP}))}if(!dontDraw&&void 0!==elem.gracenotes){var gracescale=.6,graceScaleStem=.7,gracebeam=null;elem.gracenotes.length>1&&(gracebeam=new ABCJS.write.BeamElem(this.stemHeight*graceScaleStem,"grace",this.isBagpipes), ABCJS.write.hint&&gracebeam.setHint(),gracebeam.mainNote=abselem);var graceoffsets=[];for(i=elem.gracenotes.length-1;i>=0;i--)this.roomtaken+=10,graceoffsets[i]=this.roomtaken,elem.gracenotes[i].accidental&&(this.roomtaken+=7);for(i=0;i6:pos<6)&&(pos=6);var xdelta="down"===dir?headx:headx+notehead.w-.6;abselem.addRight(new ABCJS.write.RelativeElement(flag,xdelta,ABCJS.write.glyphs.getSymbolWidth(flag)*scale,pos,{scalex:scale,scaley:scale}))}for(this.dotshiftx=notehead.w+dotshiftx-2+5*dot;dot>0;dot--){var dotadjusty=1-Math.abs(pitch)%2;abselem.addRight(new ABCJS.write.RelativeElement("dots.dot",notehead.w+dotshiftx-2+5*dot,ABCJS.write.glyphs.getSymbolWidth("dots.dot"),pitch+dotadjusty))}}if(notehead&&(notehead.highestVert=pitchelem.highestVert),pitchelem.accidental){var symb;switch(pitchelem.accidental){case"quartersharp":symb="accidentals.halfsharp";break;case"dblsharp":symb="accidentals.dblsharp";break;case"sharp":symb="accidentals.sharp";break;case"quarterflat":symb="accidentals.halfflat";break;case"flat":symb="accidentals.flat";break;case"dblflat":symb="accidentals.dblflat";break;case"natural":symb="accidentals.nat"}for(var accSlotFound=!1,accPlace=extrax,j=0;j=6){this.accidentalSlot[j][0]=pitch,accPlace=this.accidentalSlot[j][1],accSlotFound=!0;break}accSlotFound===!1&&(accPlace-=ABCJS.write.glyphs.getSymbolWidth(symb)*scale+2,this.accidentalSlot.push([pitch,accPlace]),this.accidentalshiftx=ABCJS.write.glyphs.getSymbolWidth(symb)*scale+2),abselem.addExtra(new ABCJS.write.RelativeElement(symb,accPlace,ABCJS.write.glyphs.getSymbolWidth(symb),pitch,{scalex:scale,scaley:scale}))}if(pitchelem.endTie&&this.ties[0]&&(this.ties[0].setEndAnchor(notehead),this.ties=this.ties.slice(1,this.ties.length)),pitchelem.startTie){var tie=new ABCJS.write.TieElem(notehead,null,("down"===this.stemdir||"down"===dir)&&"up"!==this.stemdir,"down"===this.stemdir||"up"===this.stemdir,!0);ABCJS.write.hint&&tie.setHint(),this.ties[this.ties.length]=tie,this.voice.addOther(tie),abselem.startTie=!0}if(pitchelem.endSlur)for(i=0;imaxSlant&&(slant=maxSlant),slant<-maxSlant&&(slant=-maxSlant),slant}function calcAverage(total,numElements){return numElements?total/numElements:0}function getBarYAt(startx,starty,endx,endy,x){return starty+(endy-starty)/(endx-startx)*(x-startx)}function calcDy(asc,isGrace){var dy=asc?ABCJS.write.spacing.STEP:-ABCJS.write.spacing.STEP;return isGrace&&(dy*=.4),dy}function drawBeam(renderer,startX,startY,endX,endY,dy,isHint){var klass="beam-elem";isHint&&(klass+=" abcjs-hint"),startY=renderer.calcY(startY),endY=renderer.calcY(endY);var pathString="M"+startX+" "+startY+" L"+endX+" "+endY+"L"+endX+" "+(endY+dy)+" L"+startX+" "+(startY+dy)+"z";renderer.printPath({path:pathString,stroke:"none",fill:"#000000",class:renderer.addClasses(klass)})}function calcXPos(asc,firstElement,lastElement){var starthead=firstElement.heads[asc?0:firstElement.heads.length-1],endhead=lastElement.heads[asc?0:lastElement.heads.length-1],startX=starthead.x;asc&&(startX+=starthead.w-.6);var endX=endhead.x;return asc&&(endX+=endhead.w),[startX,endX]}function calcYPos(total,numElements,stemHeight,asc,firstAveragePitch,lastAveragePitch,isFlat,minPitch,maxPitch,isGrace){var average=calcAverage(total,numElements),barpos=stemHeight-2,barminpos=stemHeight-2,pos=Math.round(asc?Math.max(average+barpos,maxPitch+barminpos):Math.min(average-barpos,minPitch-barminpos)),slant=calcSlant(firstAveragePitch,lastAveragePitch,numElements,isFlat),startY=pos+Math.floor(slant/2),endY=pos+Math.floor(-slant/2);return isGrace||(asc&&pos<6?(startY=6,endY=6):!asc&&pos>6&&(startY=6,endY=6)),[startY,endY]}function createStems(elems,asc,beam,dy,mainNote){for(var i=0;i=0;j--)if(i===elems.length-1||ABCJS.write.getDurlog(elems[i+1].abcelem.duration)>-j-4){var auxBeamEndX=x,auxBeamEndY=bary+sy*(j+1);auxBeams[j].single&&(auxBeamEndX=0===i?x+5:x-5,auxBeamEndY=getBarYAt(beam.startX,beam.startY,beam.endX,beam.endY,auxBeamEndX)+sy*(j+1)),beams.push({startX:auxBeams[j].x,endX:auxBeamEndX,startY:auxBeams[j].y,endY:auxBeamEndY,dy:dy}),auxBeams=auxBeams.slice(0,j)}}}return beams}ABCJS.write.BeamElem=function(stemHeight,type,flat){this.isflat=flat,this.isgrace=type&&"grace"===type,this.forceup=this.isgrace||type&&"up"===type,this.forcedown=type&&"down"===type,this.elems=[],this.total=0,this.allrests=!0,this.stemHeight=stemHeight,this.beams=[]},ABCJS.write.BeamElem.prototype.setHint=function(){this.hint=!0},ABCJS.write.BeamElem.prototype.add=function(abselem){var pitch=abselem.abcelem.averagepitch;void 0!==pitch&&(this.allrests=this.allrests&&abselem.abcelem.rest,abselem.beam=this,this.elems.push(abselem),this.total+=pitch,(!this.min||abselem.abcelem.minpitchthis.max)&&(this.max=abselem.abcelem.maxpitch))};var middleLine=6;ABCJS.write.BeamElem.prototype.calcDir=function(){if(this.forceup)return!0;if(this.forcedown)return!1;var average=calcAverage(this.total,this.elems.length);return average0?abselem.top+3:abselem.bottom-1,{scalex:scale,scaley:scale})),abselem.top+=2}return abselem}}(),window.ABCJS||(window.ABCJS={}),window.ABCJS.write||(window.ABCJS.write={}),function(){"use strict";ABCJS.write.createKeySignature=function(elem,tuneNumber){if(!elem.accidentals||0===elem.accidentals.length)return null;var abselem=new ABCJS.write.AbsoluteElement(elem,0,10,"staff-extra",tuneNumber),dx=0;return window.ABCJS.parse.each(elem.accidentals,function(acc){var symbol="sharp"===acc.acc?"accidentals.sharp":"natural"===acc.acc?"accidentals.nat":"accidentals.flat";abselem.addRight(new ABCJS.write.RelativeElement(symbol,dx,ABCJS.write.glyphs.getSymbolWidth(symbol),acc.verticalPos,{thickness:ABCJS.write.glyphs.symbolHeightInPitches(symbol)})),dx+=ABCJS.write.glyphs.getSymbolWidth(symbol)+2},this),abselem}}(),window.ABCJS||(window.ABCJS={}),window.ABCJS.write||(window.ABCJS.write={}),function(){"use strict";ABCJS.write.createTimeSignature=function(elem,tuneNumber){var abselem=new ABCJS.write.AbsoluteElement(elem,0,10,"staff-extra",tuneNumber);if("specified"===elem.type)for(var i=0;i9&&yPos++;var deltaX=width/2;"center"!==ABCJS.write.glyphs.getSymbolAlign(symbol)&&(deltaX-=ABCJS.write.glyphs.getSymbolWidth(symbol)/2),abselem.addChild(new ABCJS.write.RelativeElement(symbol,deltaX,ABCJS.write.glyphs.getSymbolWidth(symbol),yPos))}if("slide"===decoration[i]&&abselem.heads[0]){var yPos2=abselem.heads[0].pitch;yPos2-=2;var blank1=new ABCJS.write.RelativeElement("",-roomtaken-15,0,yPos2-1),blank2=new ABCJS.write.RelativeElement("",-roomtaken-5,0,yPos2+1);abselem.addChild(blank1),abselem.addChild(blank2),voice.addOther(new ABCJS.write.TieElem(blank1,blank2,!1,!1,!1))}}return void 0===yPos&&(yPos=pitch),{above:yPos,below:abselem.bottom}},volumeDecoration=function(voice,decoration,abselem,positioning){for(var i=0;iminBottom&&(y=minBottom)),y}function textDecoration(text,placement){var y=getPlacement(placement),textFudge=2,textHeight=5;abselem.addChild(new ABCJS.write.RelativeElement(text,width/2,0,y+textFudge,{type:"decoration",klass:"ornament",thickness:3})),incrementPlacement(placement,textHeight)}function symbolDecoration(symbol,placement){var deltaX=width/2;"center"!==ABCJS.write.glyphs.getSymbolAlign(symbol)&&(deltaX-=ABCJS.write.glyphs.getSymbolWidth(symbol)/2);var height=ABCJS.write.glyphs.symbolHeightInPitches(symbol)+1,y=getPlacement(placement);y="above"===placement?y+height/2:y-height/2,abselem.addChild(new ABCJS.write.RelativeElement(symbol,deltaX,ABCJS.write.glyphs.getSymbolWidth(symbol),y,{klass:"ornament",thickness:ABCJS.write.glyphs.symbolHeightInPitches(symbol)})),incrementPlacement(placement,height)}for(var symbolList={"+":"scripts.stopped",open:"scripts.open",snap:"scripts.snap",wedge:"scripts.wedge",thumb:"scripts.thumb",shortphrase:"scripts.shortphrase",mediumphrase:"scripts.mediumphrase",longphrase:"scripts.longphrase",trill:"scripts.trill",roll:"scripts.roll",irishroll:"scripts.roll",marcato:"scripts.umarcato",dmarcato:"scripts.dmarcato",umarcato:"scripts.umarcato",turn:"scripts.turn",uppermordent:"scripts.prall",pralltriller:"scripts.prall",mordent:"scripts.mordent",lowermordent:"scripts.mordent",downbow:"scripts.downbow",upbow:"scripts.upbow",fermata:"scripts.ufermata",invertedfermata:"scripts.dfermata",breath:",",coda:"scripts.coda",segno:"scripts.segno"},hasOne=!1,i=0;i",positioning)),crescendo&&voice.addOther(new ABCJS.write.CrescendoElem(crescendo.start,crescendo.stop,"<",positioning))},ABCJS.write.Decoration.prototype.createDecoration=function(voice,decoration,pitch,width,abselem,roomtaken,dir,minPitch,positioning,hasVocals){positioning||(positioning={ornamentPosition:"above",volumePosition:hasVocals?"above":"below",dynamicPosition:hasVocals?"above":"below"}),volumeDecoration(voice,decoration,abselem,positioning.volumePosition),this.dynamicDecoration(voice,decoration,abselem,positioning.dynamicPosition),compoundDecoration(decoration,pitch,width,abselem,dir);var yPos=closeDecoration(voice,decoration,pitch,width,abselem,roomtaken,dir,minPitch);yPos.above=Math.max(yPos.above,this.minTop);stackedDecoration(decoration,width,abselem,yPos,positioning.ornamentPosition,this.minTop,this.minBottom)}}(),window.ABCJS||(window.ABCJS={}),window.ABCJS.write||(window.ABCJS.write={}),ABCJS.write.DynamicDecoration=function(anchor,dec,position){this.anchor=anchor,this.dec=dec,"below"===position?this.volumeHeightBelow=5:this.volumeHeightAbove=5,this.pitch=void 0},ABCJS.write.DynamicDecoration.prototype.setUpperAndLowerElements=function(positionY){this.volumeHeightAbove?this.pitch=positionY.volumeHeightAbove:this.pitch=positionY.volumeHeightBelow},ABCJS.write.DynamicDecoration.prototype.draw=function(renderer,linestartx,lineendx){void 0===this.pitch&&window.console.error("Dynamic Element y-coordinate not set.");var scalex=1,scaley=1;renderer.printSymbol(this.anchor.x,this.pitch,this.dec,scalex,scaley,renderer.addClasses("decoration"))},window.ABCJS||(window.ABCJS={}),window.ABCJS.write||(window.ABCJS.write={}),ABCJS.write.EndingElem=function(text,anchor1,anchor2){this.text=text,this.anchor1=anchor1,this.anchor2=anchor2,this.endingHeightAbove=5,this.pitch=void 0},ABCJS.write.EndingElem.prototype.setUpperAndLowerElements=function(positionY){this.pitch=positionY.endingHeightAbove},ABCJS.write.EndingElem.prototype.draw=function(renderer,linestartx,lineendx){void 0===this.pitch&&window.console.error("Ending Element y-coordinate not set.");var pathString,y=renderer.calcY(this.pitch),height=20;this.anchor1&&(linestartx=this.anchor1.x+this.anchor1.w,pathString=ABCJS.write.sprintf("M %f %f L %f %f",linestartx,y,linestartx,y+height),renderer.printPath({path:pathString,stroke:"#000000",fill:"#000000",class:renderer.addClasses("ending")}),renderer.renderText(linestartx+5,renderer.calcY(this.pitch-.5),this.text,"repeatfont","ending","start")),this.anchor2&&(lineendx=this.anchor2.x,pathString=ABCJS.write.sprintf("M %f %f L %f %f",lineendx,y,lineendx,y+height),renderer.printPath({path:pathString,stroke:"#000000",fill:"#000000",class:renderer.addClasses("ending")})),pathString=ABCJS.write.sprintf("M %f %f L %f %f",linestartx,y,lineendx,y),renderer.printPath({path:pathString,stroke:"#000000",fill:"#000000",class:renderer.addClasses("ending")})},window.ABCJS||(window.ABCJS={}),window.ABCJS.write||(window.ABCJS.write={}),ABCJS.write.spacing=function(){},ABCJS.write.spacing.FONTEM=360,ABCJS.write.spacing.FONTSIZE=30,ABCJS.write.spacing.STEP=93*ABCJS.write.spacing.FONTSIZE/720,ABCJS.write.spacing.SPACE=10,ABCJS.write.spacing.TOPNOTE=15,ABCJS.write.spacing.STAVEHEIGHT=100,ABCJS.write.spacing.INDENT=50,ABCJS.write.debugPlacement=!1,ABCJS.write.EngraverController=function(paper,params){params=params||{},this.space=3*ABCJS.write.spacing.SPACE,this.scale=params.scale||void 0,params.staffwidth?(this.staffwidthScreen=params.staffwidth,this.staffwidthPrint=params.staffwidth):(this.staffwidthScreen=740,this.staffwidthPrint=680), this.editable=params.editable||!1,this.listeners=[],params.listener&&this.addSelectListener(params.listener),this.usingSvg=!(!window.SVGAngle&&!document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure","1.1")),this.usingSvg&¶ms.add_classes&&(Raphael._availableAttrs.class=""),Raphael._availableAttrs["text-decoration"]="",Raphael._availableAttrs["data-vertical"]="",this.renderer=new ABCJS.write.Renderer(paper,params.regression),this.renderer.setPaddingOverride(params),this.renderer.controller=this,this.reset()},ABCJS.write.EngraverController.prototype.reset=function(){this.selected=[],this.ingroup=!1,this.staffgroups=[],this.lastStaffGroupIndex=-1,this.engraver&&this.engraver.reset(),this.engraver=null,this.renderer.reset()},ABCJS.write.EngraverController.prototype.engraveABC=function(abctunes){void 0===abctunes[0]&&(abctunes=[abctunes]),this.reset();for(var i=0;imaxWidth&&(maxWidth=abcLine.staffGroup.w));for(i=0;i-1&&this.renderer.addStaffPadding(this.staffgroups[this.lastStaffGroupIndex],staffGroup),this.renderer.voiceNumber=null,staffGroup.draw(this.renderer);var height=staffGroup.height*ABCJS.write.spacing.STEP;this.staffgroups[this.staffgroups.length]=staffGroup,this.lastStaffGroupIndex=this.staffgroups.length-1,this.renderer.y+=height},ABCJS.write.EngraverController.prototype.notifySelect=function(abselem,tuneNumber){this.clearSelection(),abselem.highlight&&(this.selected=[abselem],abselem.highlight());for(var abcelem=abselem.abcelem||{},i=0;ielStart&&startthis.top&&(this.top=this.pitch2),this.bottom=pitch,void 0!==this.pitch2&&this.pitch20?this.top+=opt.stemHeight:this.bottom+=opt.stemHeight);var height=opt.height?opt.height:4;switch(this.centerVertically=!1,this.type){case"debug":this.chordHeightAbove=height;break;case"lyric":opt.position&&"below"===opt.position?this.lyricHeightBelow=height:this.lyricHeightAbove=height;break;case"chord":opt.position&&"below"===opt.position?this.chordHeightBelow=height:this.chordHeightAbove=height;break;case"text":void 0===this.pitch?opt.position&&"below"===opt.position?this.chordHeightBelow=height:this.chordHeightAbove=height:this.centerVertically=!0;break;case"part":this.partHeightAbove=height}},ABCJS.write.RelativeElement.prototype.setX=function(x){this.x=x+this.dx},ABCJS.write.RelativeElement.prototype.draw=function(renderer,bartop){void 0===this.pitch&&window.console.error(this.type+" Relative Element y-coordinate not set.");var y=renderer.calcY(this.pitch);switch(this.type){case"symbol":if(null===this.c)return null;var klass="symbol";this.klass&&(klass+=" "+this.klass),this.graphelem=renderer.printSymbol(this.x,this.pitch,this.c,this.scalex,this.scaley,renderer.addClasses(klass));break;case"debug":this.graphelem=renderer.renderText(this.x,renderer.calcY(15),""+this.c,"debugfont","debug-msg","start");break;case"barNumber":this.graphelem=renderer.renderText(this.x,y,""+this.c,"measurefont","bar-number","middle");break;case"lyric":this.graphelem=renderer.renderText(this.x,y,this.c,"vocalfont","abc-lyric","middle");break;case"chord":this.graphelem=renderer.renderText(this.x,y,this.c,"gchordfont","chord","middle");break;case"decoration":this.graphelem=renderer.renderText(this.x,y,this.c,"annotationfont","annotation","middle",!0);break;case"text":this.graphelem=renderer.renderText(this.x,y,this.c,"annotationfont","annotation","start",this.centerVertically);break;case"part":this.graphelem=renderer.renderText(this.x,y,this.c,"partsfont","part","start");break;case"bar":this.graphelem=renderer.printStem(this.x,this.linewidth,y,bartop?bartop:renderer.calcY(this.pitch2));break;case"stem":this.graphelem=renderer.printStem(this.x,this.linewidth,y,renderer.calcY(this.pitch2));break;case"ledger":this.graphelem=renderer.printStaveLine(this.x,this.x+this.w,this.pitch)}return 1!==this.scalex&&this.graphelem&&this.graphelem.scale(this.scalex,this.scaley,this.x,y),this.graphelem},window.ABCJS||(window.ABCJS={}),window.ABCJS.write||(window.ABCJS.write={}),ABCJS.write.Renderer=function(paper,doRegression){this.paper=paper,this.controller=null,this.space=3*ABCJS.write.spacing.SPACE,this.padding={},this.doRegression=doRegression,this.doRegression&&(this.regressionLines=[]),this.reset()},ABCJS.write.Renderer.prototype.reset=function(){this.paper.clear(),this.y=0,this.abctune=null,this.lastM=null,this.ingroup=!1,this.path=null,this.isPrint=!1,this.initVerticalSpace(),this.doRegression&&(this.regressionLines=[])},ABCJS.write.Renderer.prototype.setPrintMode=function(isPrint){this.isPrint=isPrint},ABCJS.write.Renderer.prototype.setPaperSize=function(maxwidth,scale){var w=(maxwidth+this.padding.right)*scale,h=(this.y+this.padding.bottom)*scale;this.isPrint&&(h=Math.max(h,1056)),this.doRegression&&this.regressionLines.push("PAPER SIZE: ("+w+","+h+")"),this.paper.setSize(w/scale,h/scale);var isIE=!1;isIE?(this.paper.canvas.parentNode.style.width=w+"px",this.paper.canvas.parentNode.style.height=""+h+"px"):this.paper.canvas.parentNode.setAttribute("style","width:"+w+"px"),1!==scale?(this.paper.canvas.style.transform="scale("+scale+","+scale+")",this.paper.canvas.style["-ms-transform"]="scale("+scale+","+scale+")",this.paper.canvas.style["-webkit-transform"]="scale("+scale+","+scale+")",this.paper.canvas.style["transform-origin"]="0 0",this.paper.canvas.style["-ms-transform-origin-x"]="0",this.paper.canvas.style["-ms-transform-origin-y"]="0",this.paper.canvas.style["-webkit-transform-origin-x"]="0",this.paper.canvas.style["-webkit-transform-origin-y"]="0"):(this.paper.canvas.style.transform="",this.paper.canvas.style["-ms-transform"]="",this.paper.canvas.style["-webkit-transform"]=""),this.paper.canvas.parentNode.style.overflow="hidden",this.paper.canvas.parentNode.style.height=""+h+"px"},ABCJS.write.Renderer.prototype.setPaddingOverride=function(params){this.paddingOverride={top:params.paddingtop,bottom:params.paddingbottom,right:params.paddingright,left:params.paddingleft}},ABCJS.write.Renderer.prototype.setPadding=function(abctune){function setPaddingVariable(self,paddingKey,formattingKey,printDefault,screenDefault){void 0!==abctune.formatting[formattingKey]?self.padding[paddingKey]=abctune.formatting[formattingKey]:void 0!==self.paddingOverride[paddingKey]?self.padding[paddingKey]=self.paddingOverride[paddingKey]:self.isPrint?self.padding[paddingKey]=printDefault:self.padding[paddingKey]=screenDefault}setPaddingVariable(this,"top","topmargin",38,15),setPaddingVariable(this,"bottom","botmargin",38,15),setPaddingVariable(this,"left","leftmargin",68,15),setPaddingVariable(this,"right","rightmargin",68,15)},ABCJS.write.Renderer.prototype.adjustNonScaledItems=function(scale){this.padding.top/=scale,this.padding.bottom/=scale,this.padding.left/=scale,this.padding.right/=scale,this.abctune.formatting.headerfont.size/=scale,this.abctune.formatting.footerfont.size/=scale},ABCJS.write.Renderer.prototype.initVerticalSpace=function(){this.spacing={composer:7.56,graceBefore:8.67,graceInside:10.67,graceAfter:16,info:0,lineSkipFactor:1.1,music:7.56,paragraphSkipFactor:.4,parts:11.33,slurHeight:1,staffSeparation:61.33,stemHeight:36.67,subtitle:3.78,systemStaffSeparation:48,text:18.9,title:7.56,top:30.24,vocal:30.67,words:0}},ABCJS.write.Renderer.prototype.setVerticalSpace=function(formatting){void 0!==formatting.staffsep&&(this.spacing.staffSeparation=4*formatting.staffsep/3),void 0!==formatting.composerspace&&(this.spacing.composer=4*formatting.composerspace/3),void 0!==formatting.partsspace&&(this.spacing.parts=4*formatting.partsspace/3),void 0!==formatting.textspace&&(this.spacing.text=4*formatting.textspace/3),void 0!==formatting.musicspace&&(this.spacing.music=4*formatting.musicspace/3),void 0!==formatting.titlespace&&(this.spacing.title=4*formatting.titlespace/3),void 0!==formatting.sysstaffsep&&(this.spacing.systemStaffSeparation=4*formatting.sysstaffsep/3),void 0!==formatting.subtitlespace&&(this.spacing.subtitle=4*formatting.subtitlespace/3),void 0!==formatting.topspace&&(this.spacing.top=4*formatting.topspace/3),void 0!==formatting.vocalspace&&(this.spacing.vocal=4*formatting.vocalspace/3),void 0!==formatting.wordsspace&&(this.spacing.words=4*formatting.wordsspace/3)},ABCJS.write.Renderer.prototype.topMargin=function(abctune){this.moveY(this.padding.top)},ABCJS.write.Renderer.prototype.addMusicPadding=function(){this.moveY(this.spacing.music)},ABCJS.write.Renderer.prototype.addStaffPadding=function(lastStaffGroup,thisStaffGroup){var lastStaff=lastStaffGroup.staffs[lastStaffGroup.staffs.length-1],lastBottomLine=-(lastStaff.bottom-2),nextTopLine=thisStaffGroup.staffs[0].top-10,naturalSeparation=nextTopLine+lastBottomLine,separationInPixels=naturalSeparation*ABCJS.write.spacing.STEP;separationInPixels0){var space=this.outputTextIf(this.padding.left+width,composerLine,"composerfont","meta-top",0,null,"end");this.moveY(space[1])}else this.moveY(rSpace[1]);this.moveY(-6)}this.outputTextIf(this.padding.left+width,abctune.metaText.author,"composerfont","meta-top",0,0,"end"),this.outputTextIf(this.padding.left,abctune.metaText.partOrder,"partsfont","meta-bottom",0,0,"start")},ABCJS.write.Renderer.prototype.engraveExtraText=function(width,abctune){this.lineNumber=null,this.measureNumber=null,this.voiceNumber=null;var extraText;if(abctune.metaText.unalignedWords){extraText="";for(var j=0;j0&&symbol.indexOf(".")<0){for(var elemset=this.paper.set(),dx=0,i=0;i=0;i--)this.printStaveLine(startx,endx,2*(i+1),klass),klass=void 0},ABCJS.write.Renderer.prototype.addClasses=function(c){var ret=[];return c.length>0&&ret.push(c),null!==this.lineNumber&&ret.push("l"+this.lineNumber),null!==this.measureNumber&&ret.push("m"+this.measureNumber),null!==this.voiceNumber&&ret.push("v"+this.voiceNumber),ret.join(" ")},ABCJS.write.Renderer.prototype.getFontAndAttr=function(type,klass){var font=this.abctune.formatting[type];font=font?{face:font.face,size:4*font.size/3,decoration:font.decoration,style:font.style,weight:font.weight,box:font.box}:{face:"Arial",size:16,decoration:"underline",style:"normal",weight:"normal"};var attr={"font-size":font.size,"font-style":font.style,"font-family":font.face,"font-weight":font.weight,"text-decoration":font.decoration,class:this.addClasses(klass)};return attr.font="",{font:font,attr:attr}},ABCJS.write.Renderer.prototype.getTextSize=function(text,type,klass){var hash=this.getFontAndAttr(type,klass),el=this.paper.text(0,0,text).attr(hash.attr),size=el.getBBox();return isNaN(size.height)&&(size={width:0,height:0}),el.remove(),size},ABCJS.write.Renderer.prototype.renderText=function(x,y,text,type,klass,anchor,centerVertically){var hash=this.getFontAndAttr(type,klass);anchor&&(hash.attr["text-anchor"]=anchor),text=text.replace(/\n\n/g,"\n \n");var el=this.paper.text(x,y,text).attr(hash.attr);if(!centerVertically){var size=el.getBBox();isNaN(size.height)?el.attr({y:y}):(el.attr({y:y+size.height/2}),hash.font.box&&this.paper.rect(size.x-1,size.y+size.height/2-1,size.width+2,size.height+2).attr({stroke:"#888888"}))}return"debugfont"===type&&(console.log("Debug msg: "+text),el.attr({stroke:"#ff0000"})),this.doRegression&&this.addToRegression(el),el},ABCJS.write.Renderer.prototype.moveY=function(em,numLines){void 0===numLines&&(numLines=1),this.y+=em*numLines},ABCJS.write.Renderer.prototype.skipSpaceY=function(){this.y+=this.space},ABCJS.write.Renderer.prototype.outputTextIf=function(x,str,kind,klass,marginTop,marginBottom,alignment){if(str){marginTop&&this.moveY(marginTop);var el=this.renderText(x,this.y,str,kind,klass,alignment),bb=el.getBBox(),width=isNaN(bb.width)?0:bb.width,height=isNaN(bb.height)?0:bb.height;if(null!==marginBottom){var numLines=str.split("\n").length;isNaN(bb.height)||this.moveY(height/numLines,numLines+marginBottom)}return[width,height]}return[0,0]},ABCJS.write.Renderer.prototype.addInvisibleMarker=function(className){var dy=.35,fill="rgba(0,0,0,0)",y=this.y;y=Math.round(y);var x1=0,x2=100,pathString=ABCJS.write.sprintf("M %f %f L %f %f L %f %f L %f %f z",x1,y-dy,x1+x2,y-dy,x2,y+dy,x1,y+dy);this.paper.path().attr({path:pathString,stroke:"none",fill:fill,class:this.addClasses(className),"data-vertical":y}).toBack()},ABCJS.write.Renderer.prototype.printHorizontalLine=function(width,vertical,comment){var dy=.35,fill="rgba(0,0,255,.4)",y=this.y;vertical&&(y=vertical),y=Math.round(y),this.paper.text(10,y,""+Math.round(y)).attr({"text-anchor":"start","font-size":"18px",fill:fill,stroke:fill});var x1=50,x2=width,pathString=ABCJS.write.sprintf("M %f %f L %f %f L %f %f L %f %f z",x1,y-dy,x1+x2,y-dy,x2,y+dy,x1,y+dy);this.paper.path().attr({path:pathString,stroke:"none",fill:fill,class:this.addClasses("staff")}).toBack();for(var i=1;i0&&(staff.top+=addedSpace)}lastStaffBottom=2-staff.bottom}};ABCJS.write.StaffGroupElement.prototype.finished=function(){for(var i=0;iepsilon?othervoices.push(this.voices[i]):(currentvoices.push(this.voices[i]),debug&&console.log("in: voice ",i))}spacingunit=0;var spacingduration=0;for(i=0;ix&&(x=currentvoices[i].getNextX(),spacingunit=currentvoices[i].getSpacingUnits(),spacingduration=currentvoices[i].spacingduration);for(this.spacingunits+=spacingunit,this.minspace=Math.min(this.minspace,spacingunit),i=0;i0){x=voicechildx;for(var j=0;jx&&(x=this.voices[i].getNextX(),spacingunit=this.voices[i].getSpacingUnits());for(this.spacingunits+=spacingunit,this.w=x,i=0;i6&&(colorIndex=0)}}}renderer.addInvisibleMarker("abcjs-top-of-system");for(var startY=renderer.y,j=0;j1&&renderer.printStem(this.startx,.6,topLine,bottomLine),renderer.y=startY},window.ABCJS||(window.ABCJS={}),window.ABCJS.write||(window.ABCJS.write={}),function(){"use strict";var totalHeightInPitches=5;ABCJS.write.TempoElement=function(tempo,tuneNumber){this.tempo=tempo,this.tuneNumber=tuneNumber,this.tempoHeightAbove=totalHeightInPitches,this.pitch=void 0},ABCJS.write.TempoElement.prototype.setUpperAndLowerElements=function(positionY){this.pitch=positionY.tempoHeightAbove},ABCJS.write.TempoElement.prototype.setX=function(x){this.x=x},ABCJS.write.TempoElement.prototype.draw=function(renderer){var x=this.x;void 0===this.pitch&&window.console.error("Tempo Element y-coordinate not set.");var text,y=renderer.calcY(this.pitch);if(this.tempo.preString){text=renderer.renderText(x,y,this.tempo.preString,"tempofont","tempo","start");var preWidth=text.getBBox().width,charWidth=preWidth/this.tempo.preString.length; x+=preWidth+charWidth}if(this.tempo.duration){var dot,flag,note,temposcale=.75,tempopitch=this.pitch-totalHeightInPitches+1,duration=this.tempo.duration[0],abselem=new ABCJS.write.AbsoluteElement(this.tempo,duration,1,"tempo",this.tuneNumber);duration<=1/32?(note="noteheads.quarter",flag="flags.u32nd",dot=0):duration<=1/16?(note="noteheads.quarter",flag="flags.u16th",dot=0):duration<=3/32?(note="noteheads.quarter",flag="flags.u16nd",dot=1):duration<=1/8?(note="noteheads.quarter",flag="flags.u8th",dot=0):duration<=3/16?(note="noteheads.quarter",flag="flags.u8th",dot=1):duration<=.25?(note="noteheads.quarter",dot=0):duration<=3/8?(note="noteheads.quarter",dot=1):duration<=.5?(note="noteheads.half",dot=0):duration<=.75?(note="noteheads.half",dot=1):duration<=1?(note="noteheads.whole",dot=0):duration<=1.5?(note="noteheads.whole",dot=1):duration<=2?(note="noteheads.dbl",dot=0):(note="noteheads.dbl",dot=1);var temponote=renderer.engraver.createNoteHead(abselem,note,{verticalPos:tempopitch},"up",0,0,flag,dot,0,temposcale);abselem.addHead(temponote);var stem;if("noteheads.whole"!==note&&"noteheads.dbl"!==note){var p1=tempopitch+1/3*temposcale,p2=tempopitch+7*temposcale,dx=temponote.dx+temponote.w,width=-.6;stem=new ABCJS.write.RelativeElement(null,dx,0,p1,{type:"stem",pitch2:p2,linewidth:width}),stem.setX(x),abselem.addExtra(stem)}abselem.setX(x);for(var i=0;i=this.children.length},ABCJS.write.VoiceElement.prototype.getDurationIndex=function(){return this.durationindex-(this.children[this.i]&&this.children[this.i].duration>0?0:5e-7)},ABCJS.write.VoiceElement.prototype.getSpacingUnits=function(){return Math.sqrt(8*this.spacingduration)},ABCJS.write.VoiceElement.prototype.getNextX=function(){return Math.max(this.minx,this.nextx)},ABCJS.write.VoiceElement.prototype.beginLayout=function(startx){this.i=0,this.durationindex=0,this.startx=startx,this.minx=startx,this.nextx=startx,this.spacingduration=0},ABCJS.write.VoiceElement.prototype.layoutOneItem=function(x,spacing){var child=this.children[this.i];if(!child)return 0;var er=x-this.minx;return er0?"+"+a:a,c=m[3]?"0"==m[3]?"0":m[3].charAt(1):" ",x=m[5]-String(a).length,p=m[5]?str_repeat(c,x):"",o.push(m[4]?a+p:p+a)}f=f.substring(m[0].length)}return o.join("")}; ================================================ FILE: public/vendor/codemirror-spell-checker/en_US.aff ================================================ SET UTF-8 TRY esianrtolcdugmphbyfvkwzESIANRTOLCDUGMPHBYFVKWZ' ICONV 1 ICONV ’ ' NOSUGGEST ! # ordinal numbers COMPOUNDMIN 1 # only in compounds: 1th, 2th, 3th ONLYINCOMPOUND c # compound rules: # 1. [0-9]*1[0-9]th (10th, 11th, 12th, 56714th, etc.) # 2. [0-9]*[02-9](1st|2nd|3rd|[4-9]th) (21st, 22nd, 123rd, 1234th, etc.) COMPOUNDRULE 2 COMPOUNDRULE n*1t COMPOUNDRULE n*mp WORDCHARS 0123456789 PFX A Y 1 PFX A 0 re . PFX I Y 1 PFX I 0 in . PFX U Y 1 PFX U 0 un . PFX C Y 1 PFX C 0 de . PFX E Y 1 PFX E 0 dis . PFX F Y 1 PFX F 0 con . PFX K Y 1 PFX K 0 pro . SFX V N 2 SFX V e ive e SFX V 0 ive [^e] SFX N Y 3 SFX N e ion e SFX N y ication y SFX N 0 en [^ey] SFX X Y 3 SFX X e ions e SFX X y ications y SFX X 0 ens [^ey] SFX H N 2 SFX H y ieth y SFX H 0 th [^y] SFX Y Y 1 SFX Y 0 ly . SFX G Y 2 SFX G e ing e SFX G 0 ing [^e] SFX J Y 2 SFX J e ings e SFX J 0 ings [^e] SFX D Y 4 SFX D 0 d e SFX D y ied [^aeiou]y SFX D 0 ed [^ey] SFX D 0 ed [aeiou]y SFX T N 4 SFX T 0 st e SFX T y iest [^aeiou]y SFX T 0 est [aeiou]y SFX T 0 est [^ey] SFX R Y 4 SFX R 0 r e SFX R y ier [^aeiou]y SFX R 0 er [aeiou]y SFX R 0 er [^ey] SFX Z Y 4 SFX Z 0 rs e SFX Z y iers [^aeiou]y SFX Z 0 ers [aeiou]y SFX Z 0 ers [^ey] SFX S Y 4 SFX S y ies [^aeiou]y SFX S 0 s [aeiou]y SFX S 0 es [sxzh] SFX S 0 s [^sxzhy] SFX P Y 3 SFX P y iness [^aeiou]y SFX P 0 ness [aeiou]y SFX P 0 ness [^y] SFX M Y 1 SFX M 0 's . SFX B Y 3 SFX B 0 able [^aeiou] SFX B 0 able ee SFX B e able [^aeiou]e SFX L Y 1 SFX L 0 ment . REP 90 REP a ei REP ei a REP a ey REP ey a REP ai ie REP ie ai REP alot a_lot REP are air REP are ear REP are eir REP air are REP air ere REP ere air REP ere ear REP ere eir REP ear are REP ear air REP ear ere REP eir are REP eir ere REP ch te REP te ch REP ch ti REP ti ch REP ch tu REP tu ch REP ch s REP s ch REP ch k REP k ch REP f ph REP ph f REP gh f REP f gh REP i igh REP igh i REP i uy REP uy i REP i ee REP ee i REP j di REP di j REP j gg REP gg j REP j ge REP ge j REP s ti REP ti s REP s ci REP ci s REP k cc REP cc k REP k qu REP qu k REP kw qu REP o eau REP eau o REP o ew REP ew o REP oo ew REP ew oo REP ew ui REP ui ew REP oo ui REP ui oo REP ew u REP u ew REP oo u REP u oo REP u oe REP oe u REP u ieu REP ieu u REP ue ew REP ew ue REP uff ough REP oo ieu REP ieu oo REP ier ear REP ear ier REP ear air REP air ear REP w qu REP qu w REP z ss REP ss z REP shun tion REP shun sion REP shun cion REP size cise ================================================ FILE: public/vendor/codemirror-spell-checker/en_US.dic ================================================ 48914 0/nm 0th/pt 1/n1 1st/p 1th/tc 2/nm 2nd/p 2th/tc 3/nm 3rd/p 3th/tc 4/nm 4th/pt 5/nm 5th/pt 6/nm 6th/pt 7/nm 7th/pt 8/nm 8th/pt 9/nm 9th/pt A/SM AA/M AAA AB/M ABA ABC/SM ABM/SM ABS AC/M ACLU/M ACT ACTH/M AD/M ADC ADD ADP/M AF AFAIK AFB AFC/M AFDC AFN AFT AI/SM AIDS/M AK AL AM/M AMA AMD/M ANSI/S ANZUS/M AOL/M AP/M APB APC API APO APR AR ARC ASAP ASCII/SM ASL/M ASPCA ATM/M ATP/M ATV AV AVI AWACS/M AWOL/M AZ/M AZT/M Aachen/M Aaliyah/M Aaron/M Abbas/M Abbasid/M Abbott/M Abby/M Abdul/M Abe/M Abel/M Abelard/M Abelson/M Aberdeen/M Abernathy/M Abidjan/M Abigail/M Abilene/M Abner/M Aborigine/MS Abraham/M Abram/MS Abrams/M Absalom/M Abuja/M Abyssinia/M Abyssinian/M Ac/M Acadia/M Acapulco/M Accenture/M Accra/M Acevedo/M Achaean/M Achebe/M Achernar/M Acheson/M Achilles/M Aconcagua/M Acosta/M Acropolis Acrux/M Actaeon/M Acton/M Acts/M Acuff/M Ada/SM Adam/SM Adams/M Adan/M Adana/M Adar/M Addams/M Adderley/M Addie/M Addison/M Adela/M Adelaide/M Adele/M Adeline/M Aden/M Adenauer/M Adhara/M Adidas/M Adirondack/SM Adirondacks/M Adkins/M Adler/M Adm Admiralty Adolf/M Adolfo/M Adolph/M Adonis/MS Adrenalin/MS Adrian/M Adriana/M Adriatic/M Adrienne/M Advent/MS Adventist/MS Advil/M Aegean/M Aelfric/M Aeneas/M Aeneid/M Aeolus/M Aeroflot/M Aeschylus/M Aesculapius/M Aesop/M Afghan/SM Afghani/M Afghanistan/M Afr Africa/M African/SM Afrikaans/M Afrikaner/SM Afro/SM Afrocentric Afrocentrism/M Ag/M Agamemnon/M Agana Agassi/M Agassiz/M Agatha/M Aggie/M Aglaia/M Agnes/M Agnew/M Agni/M Agra/M Agricola/M Agrippa/M Agrippina/M Aguascalientes Aguilar/M Aguinaldo/M Aguirre/M Agustin/M Ahab/M Ahmad/M Ahmadabad/M Ahmadinejad/M Ahmed/M Ahriman/M Aida/M Aiken/M Aileen/M Aimee/M Ainu/M Airedale/MS Aires/M Aisha/M Ajax/M Akbar/M Akhmatova/M Akihito/M Akita/M Akiva/M Akkad/M Akron/M Al/M Ala/S Alabama/M Alabaman/MS Alabamian/SM Aladdin/M Alamo/M Alamogordo/M Alan/M Alana/M Alar/M Alaric/M Alaska/M Alaskan/MS Alba/M Albania/M Albanian/MS Albany/M Albee/M Alberio/M Albert/M Alberta/M Albertan Alberto/M Albigensian/M Albion/M Albireo/M Albuquerque/M Alcatraz/M Alcestis/M Alcibiades/M Alcindor/M Alcmena/M Alcoa/M Alcott/M Alcuin/M Alcyone/M Aldan/M Aldebaran/M Alden/M Alderamin/M Aldo/M Aldrin/M Alec/M Aleichem/M Alejandra/M Alejandro/M Alembert/M Aleppo/M Aleut/MS Aleutian/SM Alex/M Alexander/MS Alexandra/M Alexandria/M Alexandrian Alexei/M Alexis/M Alfonso/M Alfonzo/M Alford/M Alfred/M Alfreda/M Alfredo/M Algenib/M Alger/M Algeria/M Algerian/SM Algieba/M Algiers/M Algol/M Algonquian/SM Algonquin/MS Alhambra/M Alhena/M Ali/M Alice/M Alicia/M Alighieri/M Aline/M Alioth/M Alisa/M Alisha/M Alison/M Alissa/M Alistair/M Alkaid/M Allah/M Allahabad/M Allan/M Alleghenies/M Allegheny/SM Allegra/M Allen/M Allende/M Allentown/M Allhallows/M Allie/MS Allison/M Allstate/M Allyson/M Alma/M Almach/M Almaty/M Almighty/M Almohad/M Almoravid/M Alnilam/M Alnitak/M Alonzo/M Alpert/M Alphard/M Alphecca/M Alpheratz/M Alphonse/M Alphonso/M Alpine/M Alpo/M Alps/M Alsace/M Alsatian/SM Alsop/M Alston/M Alta/M Altai/M Altaic/M Altair/M Altamira/M Althea/M Altiplano/M Altman/M Altoids/M Alton/M Aludra/M Alva/M Alvarado/M Alvarez/M Alvaro/M Alvin/M Alyce/M Alyson/M Alyssa/M Alzheimer/M Am/MNR Amadeus/M Amado/M Amalia/M Amanda/M Amarillo/M Amaru/M Amaterasu/M Amati/M Amazon/SM Amazonian Amber/M Amelia/M Amen/M Amenhotep/M Amerasian/M America/SM American/MS Americana/M Americanism/MS Americanization/MS Americanize/GDS Amerind/SM Amerindian/MS Ameslan/M Amharic/M Amherst/M Amie/M Amiga/M Amish/M Amman/M Amoco/M Amos/M Amparo/M Ampere/M Amritsar/M Amsterdam/M Amtrak/M Amundsen/M Amur/M Amway/M Amy/M Ana/M Anabaptist/M Anabel/M Anacin/M Anacreon/M Anaheim/M Analects/M Ananias/M Anasazi/M Anastasia/M Anatole/M Anatolia/M Anatolian/M Anaxagoras/M Anchorage/M Andalusia/M Andalusian/M Andaman/M Andean/M Andersen/M Anderson/M Andes/M Andorra/M Andorran/SM Andre/MS Andrea/M Andrei/M Andres/M Andretti/M Andrew/SM Andrews/M Andrianampoinimerina/M Android/M Andromache/M Andromeda/M Andropov/M Andy/M Angara/M Angel/M Angela/M Angeles/M Angelia/M Angelica/M Angelico/M Angelina/M Angeline/M Angelique/M Angelita/M Angelo/M Angelou/M Angevin/M Angie/M Angkor/M Angle/MS Anglia/M Anglican/SM Anglicanism/MS Anglicism/MS Anglicization Anglicize Anglo/M Anglophile/M Anglophobe Angola/M Angolan/MS Angora/SM Angstrom/M Anguilla/M Angus/M Aniakchak/M Anibal/M Anita/M Ankara/M Ann/M Anna/M Annabel/M Annabelle/M Annam/M Annapolis/M Annapurna/M Anne/M Annette/M Annie/M Annmarie/M Annunciation/SM Anouilh/M Anselm/M Anselmo/M Anshan/M Antaeus/M Antananarivo/M Antarctic/M Antarctica/M Antares/M Anthony/M Anthropocene Antichrist/SM Antietam/M Antigone/M Antigua/M Antillean Antilles/M Antioch/M Antipas/M Antipodes Antofagasta/M Antoine/M Antoinette/M Anton/M Antone/M Antonia/M Antoninus/M Antonio/M Antonius/M Antony/M Antwan/M Antwerp/M Anubis/M Anzac/M Apache/SM Apalachicola/M Apatosaurus Apennines/M Aphrodite/M Apia/M Apocalypse/M Apocrypha/M Apollinaire/M Apollo/SM Apollonian/M Appalachia/M Appalachian/SM Appalachians/M Appaloosa/SM Apple/M Appleseed/M Appleton/M Appomattox/M Apr/M April/MS Apuleius/M Aquafresh/M Aquarian Aquarius/MS Aquila/M Aquinas/M Aquino/M Aquitaine/M Ar/M Ara/M Arab/SM Arabia/M Arabian/MS Arabic/M Arabist/MS Araby/M Araceli/M Arafat/M Aragon Araguaya/M Aral/M Aramaic/M Aramco/M Arapaho/MS Arapahoes Ararat/M Araucanian/M Arawak/M Arawakan/M Arbitron/M Arcadia/M Arcadian/M Archean/M Archibald/M Archie/M Archimedes/M Arctic/M Arcturus/M Ardabil Arden/M Arequipa/M Ares/M Argentina/M Argentine/M Argentinean Argentinian/MS Argo/SM Argonaut/MS Argonne/M Argos/M Argus/M Ariadne/M Arianism/M Ariel/M Aries/MS Ariosto/M Aristarchus/M Aristides/M Aristophanes/M Aristotelian/M Aristotle/M Arius/M Ariz Arizona/M Arizonan/SM Arizonian/MS Arjuna/M Ark/M Arkansan/MS Arkansas/M Arkhangelsk/M Arkwright/M Arlene/M Arline/M Arlington/M Armageddon/SM Armagnac/M Armand/M Armando/M Armani/M Armenia/M Armenian/SM Arminius/M Armonk/M Armour/M Armstrong/M Arneb/M Arnhem/M Arno/M Arnold/M Arnulfo/M Aron/M Arrhenius/M Arron/M Art/M Artaxerxes/M Artemis/M Arthur/M Arthurian/M Artie/M Arturo/M Aruba/M Aryan/MS As/M Asama/M Ascella/M Ascension/M Asgard/M Ashanti/M Ashcroft/M Ashe/M Ashgabat Ashikaga/M Ashkenazim/M Ashkhabad/M Ashlee/M Ashley/M Ashmolean/M Ashurbanipal/M Asia/M Asiago Asian/MS Asiatic/SM Asimov/M Asmara/M Asoka/M Aspell/M Aspen/M Asperger/M Aspidiske/M Asquith/M Assad/M Assam/M Assamese/M Assembly Assisi/M Assyria/M Assyrian/SM Astaire/M Astana/M Astarte/M Aston/M Astor/M Astoria/M Astrakhan/M AstroTurf/M Asturias/M Asuncion/M Aswan/M At/SM Atacama/M Atahualpa/M Atalanta/M Atari/M Ataturk/M Athabasca/M Athabaskan/SM Athanasius Athena/M Athene/M Athenian/SM Athens/M Atkins/M Atkinson/M Atlanta/M Atlantes Atlantic/M Atlantis/M Atlas/MS Atman/M Atreus/M Atria/M Atropos/M Attic/M Attica/M Attila/M Attlee/M Attn Attucks/M Atwood/M Au/M Aubrey/M Auckland/M Auden/M Audi/M Audion/M Audra/M Audrey/M Audubon/M Aug/M Augean/M Augsburg/M August/MS Augusta/M Augustan/M Augustine/M Augustinian/MS Augustus/M Aurangzeb/M Aurelia/M Aurelio/M Aurelius/M Aureomycin/M Auriga/M Aurora/M Auschwitz/M Aussie/MS Austen/M Austerlitz/M Austin/MS Australasia/M Australasian Australia/M Australian/SM Australoid/M Australopithecus/M Austria/M Austrian/SM Austronesian/M Autumn/M Av/M Ava/M Avalon/M Ave/M Aventine/M Avernus/M Averroes/M Avery/M Avesta/M Avicenna/M Avignon/M Avila/M Avior/M Avis/M Avogadro/M Avon/M Axis Axum/M Ayala/M Ayers/M Aymara/M Ayrshire/M Ayurveda/M Ayyubid/M Azana/M Azania/M Azazel/M Azerbaijan/M Azerbaijani/MS Azores/M Azov/M Aztec/SM Aztecan/M Aztlan/M B/MNT BA/M BASIC/SM BB/M BBB/M BBC/M BBQ BBS BBSes BC/M BFF BIA BIOS BITNET BLT/SM BM/M BMW/M BO BP/M BPOE BR BS/M BSA BSD/SM BTU BTW BYOB Ba/M Baal/SM Baath/M Baathist/M Babbage/M Babbitt/M Babel/MS Babylon/MS Babylonia/M Babylonian/SM Bacall/M Bacardi/M Bacchanalia/M Bacchic Bacchus/M Bach/M Backus/M Bacon/M Bactria/M Baden/M Badlands/M Baedeker/MS Baez/M Baffin/M Baggies/M Baghdad/M Baguio/M Baha'i/M Baha'ullah/M Bahama/SM Bahamanian Bahamas/M Bahamian/MS Bahia/M Bahrain/M Baikal/M Bailey/M Baird/M Bakelite/M Baker/M Bakersfield/M Baku/M Bakunin/M Balanchine/M Balaton/M Balboa/M Balder/M Baldwin/SM Balearic/M Balfour/M Bali/M Balinese/M Balkan/MS Balkans/M Balkhash/M Ball/M Ballard/M Balthazar/M Baltic/M Baltimore/M Baluchistan/M Balzac/M Bamako/M Bambi/M Banach/M Bancroft/M Bandung/M Bangalore/M Bangkok/M Bangladesh/M Bangladeshi/SM Bangor/M Bangui/M Banjarmasin/M Banjul/M Banks/M Banneker/M Bannister/M Banting/M Bantu/MS Baotou/M Baptist/SM Baptiste/M Barabbas/M Barack/M Barbadian/SM Barbados/M Barbara/M Barbarella/M Barbarossa/M Barbary/M Barber/M Barbie/M Barbour/M Barbra/M Barbuda/M Barcelona/M Barclay/SM Barclays/M Bardeen/M Barents/M Barker/M Barkley/M Barlow/M Barnabas/M Barnaby/M Barnard/M Barnaul/M Barnes/M Barnett/M Barney/M Barnum/M Baroda/M Barquisimeto/M Barr/M Barranquilla/M Barrera/M Barrett/M Barrie/M Barron/M Barry/M Barrymore/M Bart/M Barth/MS Bartholdi/M Bartholomew/M Bartlett/M Bartok/M Barton/M Baruch/M Baryshnikov/M Basel/M Basho/M Basie/M Basil/M Basque/MS Basra/M Bass/M Basseterre/M Bastille/M Basutoland/M Bataan/M Bates/M Bathsheba/M Batista/M Batman/M Battle/M Batu/M Baudelaire/M Baudouin/M Baudrillard/M Bauer/M Bauhaus/M Baum/M Bavaria/M Bavarian/M Baxter/M Bayamon Bayer/M Bayes/M Bayesian/M Bayeux/M Baylor/M Bayonne/M Bayreuth/M Baywatch/M Be/MH Beach/M Beadle/M Bean/M Beard/M Beardmore/M Beardsley/M Bearnaise/M Beasley/M Beatlemania/M Beatles/M Beatrice/M Beatrix/M Beatriz/M Beatty/M Beau/M Beaufort/M Beaujolais/M Beaumarchais/M Beaumont/M Beauregard/M Beauvoir/M Bechtel/M Beck/MR Becker/M Becket/M Beckett/M Becky/M Becquerel/M Bede/M Bedouin/SM Beebe/M Beecher/M Beefaroni/M Beelzebub/M Beerbohm/M Beethoven/M Beeton/M Begin/M Behan/M Behring/M Beiderbecke/M Beijing/M Beirut/M Bekesy/M Bela/M Belarus/M Belau/M Belem/M Belfast/M Belg Belgian/SM Belgium/M Belgrade/M Belinda/M Belize/M Bell/M Bella/M Bellamy/M Bellatrix/M Belleek/M Bellini/M Bellow/M Belmont/M Belmopan/M Belorussian/MS Belshazzar/M Beltane/M Belushi/M Ben/M Benacerraf/M Benchley/M Bender/M Bendix/M Benedict/M Benedictine/MS Benelux/M Benet/M Benetton/M Bengal/SM Bengali/M Benghazi/M Benin/M Beninese/M Benita/M Benito/M Benjamin/M Bennett/M Bennie/M Benny/M Benson/M Bentham/M Bentley/M Benton/M Benz/M Benzedrine/M Beowulf/M Berber/SM Berenice/M Beretta/M Berg/MNR Bergen/M Berger/M Bergerac/M Bergman/M Bergson/M Beria/M Bering/M Berkeley/M Berkshire/SM Berkshires/M Berle/M Berlin/SZMR Berliner/M Berlioz/M Berlitz/M Bermuda/SM Bermudan/SM Bermudian/SM Bern/M Bernadette/M Bernadine/M Bernanke/M Bernard/M Bernardo/M Bernays/M Bernbach/M Bernese Bernhardt/M Bernice/M Bernie/M Bernini/M Bernoulli/M Bernstein/M Berra/M Berry/M Bert/M Berta/M Bertelsmann/M Bertha/M Bertie/M Bertillon/M Bertram/M Bertrand/M Beryl/M Berzelius/M Bess/M Bessel/M Bessemer/M Bessie/M Best/M Betelgeuse/M Beth/M Bethany/M Bethe/M Bethesda/M Bethlehem/M Bethune/M Betsy/M Bette/M Bettie/M Betty/M Bettye/M Beulah/M Beveridge Beverley/M Beverly/M Beyer/M Bharat/M Bhopal/M Bhutan/M Bhutanese/M Bhutto/M Bi/M Bialystok/M Bianca/M Bib Bible/MS Bic/M Biddle/M Biden/M Bierce/M Bigfoot/M Biggles/M Biko/M Bilbao/M Bilbo/M Bill/MJ Billie/M Billings/M Billy/M Bimini/M Bioko/M Bird/M Birdseye/M Birkenstock/M Birmingham/M Biro/M Biscay/M Biscayne/M Bishkek/M Bishop/M Bismarck/M Bismark/M Bisquick/M Bissau/M BitTorrent/M Bizet/M Bjerknes/M Bjork/M Bk/M BlackBerry/M Blackbeard/M Blackburn/M Blackfeet/M Blackfoot/M Blackpool/M Blackshirt/M Blackstone/M Blackwell/M Blaine/M Blair/M Blake/M Blanca/M Blanchard/M Blanche/M Blankenship/M Blantyre/M Blatz/M Blavatsky/M Blenheim/M Blevins/M Bligh/M Bloch/M Blockbuster/M Bloemfontein/M Blondel/M Blondie/M Bloom/MR Bloomer/M Bloomfield/M Bloomingdale/M Bloomsbury/M Blu Blucher/M Bluebeard/M Bluetooth/M Blvd Blythe/M Boadicea Boas/M Bob/M Bobbi/M Bobbie/M Bobbitt/M Bobby/M Boccaccio/M Bodhidharma/M Bodhisattva/M Bodleian Boeing/M Boeotia/M Boeotian/M Boer/SM Boethius/M Bogart/M Bogota/M Bohemia/M Bohemian/SM Bohr/M Boise/M Bojangles/M Boleyn/M Bolivar/M Bolivia/M Bolivian/MS Bollywood/M Bologna/M Bolshevik/SM Bolshevism/M Bolshevist/M Bolshoi/M Bolton/M Boltzmann/M Bombay/M Bonaparte/M Bonaventure/M Bond/M Bonhoeffer/M Boniface/M Bonita/M Bonn/MR Bonner/M Bonneville/M Bonnie/M Bono/M Booker/M Boole/M Boolean/M Boone/M Bootes/M Booth/M Bordeaux/M Borden/M Bordon/M Boreas/M Borg/SM Borges/M Borgia/M Borglum/M Boris/M Bork/M Borlaug/M Born/M Borneo/M Borobudur/M Borodin/M Boru/M Bosch/M Bose/M Bosnia/M Bosnian Bosporus/M Boston/MS Bostonian/M Boswell/M Botox Botswana/M Botticelli/M Boulder/M Boulez/M Bourbaki/M Bourbon/SM Bournemouth/M Bovary/M Bowditch/M Bowell/M Bowen/M Bowers/M Bowery/M Bowie/M Bowman/M Boyd/M Boyer/M Boyle/M Br/MT Brad/MY Bradbury/M Braddock/M Bradford/M Bradley/M Bradly/M Bradshaw/M Bradstreet/M Brady/M Bragg/M Brahe/M Brahma/MS Brahmagupta/M Brahman/MS Brahmani Brahmanism/SM Brahmaputra/M Brahms/M Braille/MS Brain/M Brampton/M Bran/M Branch/M Brandeis/M Branden/M Brandenburg/M Brandi/M Brandie/M Brando/M Brandon/M Brandt/M Brandy/M Brant/M Braque/M Brasilia/M Bratislava/M Brattain/M Bray/M Brazil/M Brazilian/MS Brazos/M Brazzaville/M Breakspear/M Breathalyzer Brecht/M Breckenridge/M Bremen/M Brenda/M Brendan/M Brennan/M Brenner/M Brent/M Brenton/M Brest/M Bret/M Breton/M Brett/M Brewer/M Brewster/M Brezhnev/M Brian/M Briana/M Brianna/M Brice/M Bridalveil/M Bridgeport/M Bridger/M Bridges/M Bridget/M Bridgetown/M Bridgett/M Bridgette/M Bridgman/M Brie/SM Brigadoon/M Briggs/M Brigham/M Bright/M Brighton/M Brigid/M Brigitte/M Brillo/M Brinkley/M Brisbane/M Bristol/M Brit/SM Britain/M Britannia/M Britannic/M Britannica/M Briticism/SM British/MRZ Britisher/M Britney/M Briton/MS Britt/MN Brittany/SM Britten/M Brittney/M Brno/M Broadway/SM Brobdingnag/M Brobdingnagian/M Brock/M Brokaw/M Bronson/M Bronte/M Brontosaurus Bronx/M Brooke/MS Brooklyn/M Brooks/M Bros Brown/MG Browne/M Brownian/M Brownie/S Browning/M Brownshirt/M Brownsville/M Brubeck/M Bruce/M Bruckner/M Bruegel Brummel/M Brunei/M Bruneian/MS Brunelleschi/M Brunhilde/M Bruno/M Brunswick/M Brussels/M Brut/M Brutus/M Bryan/M Bryant/M Bryce/M Brynner/M Bryon/M Brzezinski/M Btu/M Buber/M Buchanan/M Bucharest/M Buchenwald/M Buchwald/M Buck/M Buckingham/M Buckley/M Buckner/M Bud/M Budapest/M Buddha/SM Buddhism/SM Buddhist/SM Buddy/M Budweiser/M Buffalo/M Buffy/M Buford/M Bugatti/M Bugzilla/M Buick/M Bujumbura/M Bukhara/M Bukharin/M Bulawayo/M Bulfinch/M Bulganin/M Bulgar/M Bulgari/M Bulgaria/M Bulgarian/SM Bullock/M Bullwinkle/M Bultmann/M Bumppo/M Bunche/M Bundesbank/M Bundestag/M Bunin/M Bunker/M Bunsen/M Bunuel/M Bunyan/M Burbank/M Burberry/M Burch/M Burger/M Burgess/M Burgoyne/M Burgundian/M Burgundy/SM Burke/M Burks/M Burl/M Burlington/M Burma/M Burmese/M Burnett/M Burns/M Burnside/M Burr/M Burris/M Burroughs/M Bursa/M Burt/M Burton/M Burundi/M Burundian/MS Busch/M Bush/M Bushido/M Bushnell/M Butler/M Butterfingers/M Buxtehude/M Byblos/M Byers/M Byrd/M Byron/M Byronic/M Byzantine/MS Byzantium/M C/SM CA CAD/M CAI CAM CAP CARE CATV CB CBC/M CBS/M CCTV CCU CD/M CDC CDT CEO/M CF CFC/M CFO CGI CIA/M CID CNN/M CNS/M CO/M COBOL/SM COD COL COLA CPA/M CPI/M CPO CPR/M CPU/M CRT/SM CST/M CT/M CV CVS/M CZ Ca/M Cabernet/M Cabot/M Cabral/M Cabrera/M Cabrini/M Cadette Cadillac/M Cadiz/M Caedmon/M Caerphilly/M Caesar/SM Cage/M Cagney/M Cahokia/M Caiaphas/M Cain/SM Cairo/M Caitlin/M Cajun/MS Cal/M Calais/M Calcutta/M Calder/M Calderon/M Caldwell/M Caleb/M Caledonia/M Calgary/M Calhoun/M Cali/M Caliban/M Calif California/M Californian/SM Caligula/M Callaghan/M Callahan/M Callao/M Callas/M Callie/M Calliope/M Callisto/M Caloocan/M Calvary/M Calvert/M Calvin/M Calvinism/MS Calvinist/MS Calvinistic Camacho/M Cambodia/M Cambodian/SM Cambrian/SM Cambridge/M Camden/M Camel/M Camelopardalis/M Camelot/MS Camembert/MS Cameron/M Cameroon/SM Cameroonian/MS Camilla/M Camille/M Camoens/M Campanella/M Campbell/M Campinas/M Campos/M Camry/M Camus/M Can/M Canaan/M Canaanite/MS Canad Canada/M Canadian/SM Canadianism Canaletto/M Canaries/M Canaveral/M Canberra/M Cancer/SM Cancun/M Candace/M Candice/M Candide/M Candy/M Cannes/M Cannon/M Canon/M Canopus/M Cantabrigian/M Canterbury/M Canton/M Cantonese/M Cantor/M Cantrell/M Cantu/M Canute/M Capablanca/M Capek/M Capella/M Capet/M Capetian/M Capetown/M Caph/M Capistrano/M Capitol/SM Capitoline/M Capone/M Capote/M Capra/M Capri/M Capricorn/MS Capt Capuchin/M Capulet/M Cara/M Caracalla/M Caracas/M Caravaggio/M Carboloy/M Carboniferous/M Carborundum/M Cardenas/M Cardiff/M Cardin/M Cardozo/M Carey/M Carib/MS Caribbean/MS Carina/M Carissa/M Carl/M Carla/M Carlene/M Carlin/M Carlo/MS Carlos/M Carlsbad/M Carlson/M Carlton/M Carly/M Carlyle/M Carmela/M Carmella/M Carmelo/M Carmen/M Carmichael/M Carmine/M Carnap/M Carnation/M Carnegie/M Carney/M Carnot/M Carol/M Carole/M Carolina/M Caroline/M Carolingian/M Carolinian/M Carolyn/M Carpathian/SM Carpathians/M Carpenter/M Carr/M Carranza/M Carrie/RM Carrier/M Carrillo/M Carroll/M Carson/M Carter/M Cartesian/M Carthage/M Carthaginian/MS Cartier/M Cartwright/M Caruso/M Carver/M Cary/M Casablanca/M Casals/M Casandra/M Casanova/SM Cascades/M Case/M Casey/M Cash/M Casio/M Caspar/M Caspian/M Cassandra/SM Cassatt/M Cassidy/M Cassie/M Cassiopeia/M Cassius/M Castaneda/M Castilian Castillo/M Castlereagh/M Castor/M Castries/M Castro/M Catalan/SM Catalina/M Catalonia/M Catawba/M Caterpillar/M Cathay/M Cather/M Catherine/M Cathleen/M Catholic/MS Catholicism/MS Cathryn/M Cathy/M Catiline/M Cato/M Catskill/SM Catskills/M Catt/M Catullus/M Caucasian/MS Caucasoid Caucasus/M Cauchy/M Cavendish/M Cavour/M Caxton/M Cayenne/M Cayman/M Cayuga/SM Cayuse Cb Cd/M Ce/M Ceausescu/M Cebu/M Cebuano/M Cecelia/M Cecil/M Cecile/M Cecilia/M Cecily/M Cedric/M Celeste/M Celia/M Celina/M Cellini/M Celsius/M Celt/SM Celtic/SM Cenozoic/M Centaurus/M Centigrade Central Cepheid/M Cepheus/M Cerberus/M Cerenkov/M Ceres/M Cerf/M Cervantes/M Cesar/M Cesarean/M Cessna/M Cetus/M Ceylon/M Ceylonese Cezanne/M Cf/M Ch'in/M Ch/N Chablis/M Chad/M Chadian/MS Chadwick/M Chagall/M Chaitanya/M Chaitin/M Chaldea Chaldean/M Challenger/M Chalmers Chamberlain/M Chambers/M Champlain/M Champollion/M Chan/M Chance/M Chancellorsville/M Chandigarh/M Chandler/M Chandon/M Chandra/M Chandragupta/M Chandrasekhar/M Chanel/M Chaney/M Chang/M Changchun/M Changsha/M Chantilly/M Chaplin/M Chaplinesque Chapman/M Chappaquiddick/M Chapultepec/M Charbray/M Chardonnay/M Charity/M Charlemagne/M Charlene/M Charles/M Charleston/MS Charley/M Charlie/M Charlotte/M Charlottetown/M Charmaine/M Charmin/M Charolais/M Charon/M Chartism/M Chartres/M Charybdis/M Chase/M Chasity/M Chateaubriand/M Chattahoochee/M Chattanooga/M Chatterley/M Chatterton/M Chaucer/M Chauncey/M Chautauqua/M Chavez/M Chayefsky/M Che/M Chechen/M Chechnya/M Cheddar/M Cheer/M Cheerios/M Cheetos/M Cheever/M Chekhov/M Chekhovian Chelsea/M Chelyabinsk/M Chen/M Cheney/M Chengdu/M Chennai/M Cheops/M Cheri/M Cherie/M Chernenko/M Chernobyl/M Chernomyrdin/M Cherokee/MS Cherry/M Cheryl/M Chesapeake/M Cheshire/M Chester/M Chesterfield/M Chesterton/M Chevalier/M Cheviot/M Chevrolet/M Chevron/M Chevy/M Cheyenne/SM Chi/M Chianti/MS Chiba/M Chibcha/M Chicago/M Chicagoan/M Chicana/M Chicano/M Chickasaw/MS Chiclets/M Chihuahua/MS Chile/M Chilean/MS Chimborazo/M Chimera/MS Chimu/M Chin/M China/M Chinatown/M Chinese/M Chinook/MS Chipewyan/M Chippendale/M Chippewa/SM Chiquita/M Chirico/M Chisholm/M Chisinau/M Chittagong/M Chivas/M Chloe/M Choctaw/SM Chomsky/M Chongqing/M Chopin/M Chopra/M Chou/M Chretien/M Chris/M Christ/MS Christa/M Christchurch/M Christendom/MS Christensen/M Christi/M Christian/SM Christianity/SM Christianize Christie/M Christina/M Christine/M Christlike Christmas/MS Christmastide/MS Christmastime/MS Christoper/M Christopher/M Chronicles Chrysler/M Chrysostom/M Chrystal/M Chuck/M Chukchi/M Chumash/M Chung/M Church/M Churchill/M Churriguera/M Chuvash/M Ci/M Cicero/M Cid/M Cimabue/M Cincinnati/M Cinderella/MS Cindy/M CinemaScope/M Cinerama/M Cipro/M Circe/M Cisco/M Citibank/M Citigroup/M Citroen/M Cl/MV Claiborne/M Clair/M Claire/M Clairol/M Clancy/M Clapeyron/M Clapton/M Clara/M Clare/M Clarence/M Clarendon/M Clarice/M Clarissa/M Clark/M Clarke/M Claude/M Claudette/M Claudia/M Claudine/M Claudio/M Claudius/M Claus/M Clausewitz/M Clausius/M Clay/M Clayton/M Clearasil/M Clem/XM Clemenceau/M Clemens/M Clement/MS Clementine/M Clements/M Clemons/M Clemson/M Cleo/M Cleopatra/M Cleveland/M Cliburn/M Cliff/M Clifford/M Clifton/M Cline/M Clint/M Clinton/M Clio/M Clive/M Clorets/M Clorox/M Closure/M Clotho/M Clouseau/M Clovis/M Clyde/M Clydesdale/M Clytemnestra/M Cm/M Cmdr Co/M Cobain/M Cobb/M Cochabamba/M Cochin/M Cochise/M Cochran/M Cockney/M Cocteau/M Cod Cody/M Coffey/M Cognac/M Cohan/M Cohen/M Coimbatore/M Cointreau/M Coke/SM Col/M Colbert/M Colby/M Cole/M Coleen/M Coleman/M Coleridge/M Colette/M Colfax/M Colgate/M Colin/M Colleen/M Collier/M Collin/SM Collins/M Colo Cologne/M Colombia/M Colombian/MS Colombo/M Colon/M Coloradan/SM Colorado/M Coloradoan Colosseum/M Colt/M Coltrane/M Columbia/M Columbine/M Columbus/M Com Comanche/MS Combs/M Comdr Comintern/M Commons/M Commonwealth Communion/SM Communism Communist/SM Como/M Comoran Comoros/M Compaq/M Compton/M CompuServe/M Comte/M Conakry/M Conan/M Concepcion/M Concetta/M Concord/SM Concorde/M Condillac/M Condorcet/M Conestoga/M Confederacy/M Confederate/MS Confucian/SM Confucianism/MS Confucius/M Cong/M Congo/M Congolese/M Congregational Congregationalist/MS Congress/MS Congressional Congreve/M Conley/M Conn/MR Connecticut/M Connemara/M Conner/M Connery/M Connie/M Connolly/M Connors/M Conrad/M Conrail/M Conservative Constable/M Constance/M Constantine/M Constantinople/M Constitution Consuelo/M Continent/M Continental/M Contreras/M Conway/M Cook/M Cooke/M Cooley/M Coolidge/M Cooper/M Cooperstown/M Coors/M Copacabana/M Copeland/M Copenhagen/M Copernican/M Copernicus/M Copland/M Copley/M Copperfield/M Coppertone/M Coppola/M Coptic/M Cora/M Cordelia/M Cordilleras/M Cordoba/M Corey/M Corfu/M Corina/M Corine/M Corinne/M Corinth/M Corinthian/MS Corinthians/M Coriolanus/M Coriolis/M Cork Corleone/M Cormack/M Corneille/M Cornelia/M Cornelius/M Cornell/M Corning/M Cornish/MS Cornwall/M Cornwallis/M Coronado/M Corot/M Corp Correggio/M Corrine/M Corsica/M Corsican/M Cortes/MS Cortland/M Corvallis/M Corvette/M Corvus/M Cory/M Cosby/M Cossack/M Costco/M Costello/M Costner/M Cote/M Cotonou/M Cotopaxi/M Cotswold/M Cotton/M Coulomb/M Coulter/M Couperin/M Courbet/M Courtney/M Cousteau/M Coventry/SM Coward/M Cowell/M Cowley/M Cowper/M Cox/M Coy/M Cozumel/M Cpl Cr/MT Crabbe/M Craft/M Craig/M Cranach/M Crane/M Cranmer/M Crater/M Crawford/M Cray/M Crayola/M Creation/M Creator/M Crecy/M Cree/DSM Creek/SM Creighton/M Creole/SM Creon/M Cressida/M Crest/M Cretaceous/M Cretan/SM Crete/M Crichton/M Crick/M Crimea/M Crimean/M Criollo/M Crisco/M Cristina/M Croat/SM Croatia/M Croatian/MS Croce/M Crockett/M Croesus/M Cromwell/M Cromwellian/M Cronin/M Cronkite/M Cronus/M Crookes/M Crosby/M Cross/M Crow/SM Crowley/M Crucifixion/MS Cruikshank/M Cruise/M Crusades's Crusoe/M Crux/M Cruz/M Cryptozoic/M Crystal/M Csonka/M Ct Ctesiphon/M Cthulhu/M Cu/M Cuba/M Cuban/SM Cuchulain/M Cuisinart/M Culbertson/M Cullen/M Cumberland/M Cummings/M Cunard/M Cunningham/M Cupid/M Curacao/M Curie/M Curitiba/M Currier/M Curry/RM Curt/M Curtis/M Custer/M Cuvier/M Cuzco/M Cybele/M Cyclades/M Cyclopes/M Cyclops/M Cygnus/M Cymbeline/M Cynthia/M Cyprian/M Cypriot/MS Cyprus/M Cyrano/M Cyril/M Cyrillic/M Cyrus/M Czech/M Czechoslovak Czechoslovakia/M Czechoslovakian/SM Czechs Czerny/M D/M DA/M DAR DAT/M DBMS/M DC/M DD/M DDS/M DDT/S DE DEA DEC/SD DH DHS DI DJ DMCA DMD/M DMZ DNA/M DOA DOB DOD DOE DOS/M DOT DP/SM DPT DST DTP DUI DVD DVR/SM DWI Dachau/M Dacron/SM Dada/M Dadaism/M Daedalus/M Daguerre/M Dagwood/M Dahomey/M Daimler/M Daisy/M Dakar/M Dakota/SM Dakotan/M Dalai Dale/M Daley/M Dali/M Dalian/M Dallas/M Dalmatia/M Dalmatian/SM Dalton/M Damascus/M Dame/MN Damian/M Damien/M Damion/M Damocles/M Damon/M Dan/M Dana/M Danae/M Dane/SM Danelaw/M Dangerfield/M Danial/M Daniel/SM Danielle/M Daniels/M Danish/M Dannie/M Danny/M Danone/M Dante/M Danton/M Danube/M Danubian/M Daphne/M Darby/M Darcy/M Dardanelles/M Dare/M Daren/M Darfur/M Darin/M Dario/M Darius/M Darjeeling/M Darla/M Darlene/M Darling/M Darnell/M Darrel/M Darrell/M Darren/M Darrin/M Darrow/M Darryl/M Darth/M Dartmoor/M Dartmouth/M Darvon/M Darwin/M Darwinian/M Darwinism/SM Darwinist Daryl/M Datamation Daugherty/M Daumier/M Davao/M Dave/M Davenport/M David/MS Davidson/M Davies/M Davis/M Davy/SM Dawes/M Dawkins Dawn/M Dawson/M Day/M Dayan Dayton/M DeGeneres/M Deadhead/M Dean/M Deana/M Deandre/M Deann/M Deanna/M Deanne/M Debbie/M Debby/M Debian/M Debora/M Deborah/M Debouillet/M Debra/M Debs/M Debussy/M Dec/M Decalogue/M Decatur/M Decca/M Deccan/M December/SM Decker/M Dedekind/M Dee/M Deena/M Deere/M Defoe/M Degas/M Deidre/M Deimos/M Deirdre/M Deity Dejesus/M Del/M Delacroix/M Delacruz/M Delaney/M Delano/M Delaware/MS Delawarean/SM Delbert/M Deleon/M Delgado/M Delhi/M Delia/M Delibes/M Delicious/M Delilah/M Delilahs Delius/M Dell/M Della/M Delmar/M Delmarva/M Delmer/M Delmonico/M Delores/M Deloris/M Delphi/M Delphic/M Delphinus/M Delta/M Dem/G Demavend/M Demerol/M Demeter/M Demetrius/M Deming/M Democrat/SM Democratic Democritus/M Demosthenes/M Dempsey/M Dena/M Denali Deneb/M Denebola/M Deng/M Denis/M Denise/M Denmark/M Dennis/M Denny/M Denver/M Deon/M Depp/M Derby/M Derek/M Derick/M Dermot/M Derrick/M Derrida/M Descartes/M Desdemona/M Desiree/M Desmond/M Detroit/M Deuteronomy/M Devanagari/M Devi/M Devin/M Devon/M Devonian/M Dewar/M Dewayne/M Dewey/M Dewitt/M Dexedrine/M Dexter/M Dhaka/M Dhaulagiri/M Di/SM DiCaprio/M DiMaggio/M Diaghilev/M Dial/M Diana/M Diane/M Diann/M Dianna/M Dianne/M Dias Diaspora/MS Dick/XM Dickens/M Dickensian Dickerson/M Dickinson/M Dickson/M Dictaphone/SM Diderot/M Dido/M Didrikson/M Diefenbaker/M Diego/M Diem/M Dietrich/M Dijkstra/M Dijon/M Dilbert/MS Dillard/M Dillinger/M Dillon/M Dina/M Dinah/M Dino/M Diocletian/M Diogenes/M Dion/M Dionne/M Dionysian/M Dionysus/M Diophantine/M Dior/M Dipper/M Dir Dirac/M Dirichlet/M Dirk/M Dis/M Disney/M Disneyland/M Disraeli/M Divine/M Diwali/M Dix/M Dixie/M Dixiecrat/M Dixieland/SM Dixon/M Djibouti/M Dmitri/M Dnepropetrovsk/M Dniester/M Dobbin/M Doberman/M Dobro/M Doctor Doctorow/M Dodge/M Dodgson/M Dodoma/M Dodson/M Doe/M Doha/M Dolby/M Dole/M Dollie/M Dolly/M Dolores/M Domesday/M Domingo/M Dominguez/M Dominic/M Dominica/M Dominican/MS Dominick/M Dominion Dominique/M Domitian/M Don/SM Dona/M Donahue/M Donald/M Donaldson/M Donatello/M Donetsk/M Donizetti/M Donn/MR Donna/M Donne/M Donnell/M Donner/M Donnie/M Donny/M Donovan/M Dooley/M Doolittle/M Doonesbury/M Doppler/M Dora/M Dorcas/M Doreen/M Dorian/M Doric/M Doris/M Doritos/M Dorothea/M Dorothy/M Dorset/M Dorsey/M Dorthy/M Dortmund/M Dostoevsky/M Dot/M Dotson/M Douala/M Douay/M Doubleday/M Doug/M Douglas/M Douglass/M Douro/M Dover/M Dow/M Downs/M Downy/M Doyle/M Dr Draco/M Draconian/M Dracula/M Drake/M Dramamine/SM Drambuie/M Drano/M Dravidian/M Dreiser/M Dresden/M Drew/M Dreyfus/M Dristan/M Dropbox/M Drudge/M Dryden/M Dschubba/M Du DuPont/M Duane/M Dubai/M Dubcek/M Dubhe/M Dublin/M Dubrovnik/M Duchamp/M Dudley/M Duffy/M Duisburg/M Duke/M Dulles/M Duluth/M Dumas/M Dumbledore/M Dumbo/M Dumpster/M Dunant/M Dunbar/M Duncan/M Dundee Dunedin/M Dunkirk/M Dunlap/M Dunn/M Dunne/M Duracell/M Duran/M Durant/M Durante/M Durban/M Durer/M Durex/M Durham/MS Durkheim/M Duroc/M Durocher/M Duse/M Dushanbe/M Dusseldorf/M Dustbuster/M Dustin/M Dusty/M Dutch/M Dutchman/M Dutchmen/M Dutchwoman Duvalier/M Dvina/M Dvorak/M Dwayne/M Dwight/M Dy/M Dyer/M Dylan/M Dyson/M Dzerzhinsky/M Dzungaria/M E/SM EC ECG/M ECMAScript/M EDP/M EDT EEC/M EEG/M EEO EEOC EFL EFT EKG/M ELF/M EM EMT ENE/M EOE EPA/M ER ERA ESE/M ESL ESP/M ESPN/M ESR EST/M ET ETA ETD EU EULA/S Eakins/M Earhart/M Earl/M Earle/M Earlene/M Earline/M Earnest/M Earnestine/M Earnhardt/M Earp/M East/SZMR Easter/M Eastern/R Eastman/M Eastwood/M Eaton/M Eben/M Ebeneezer/M Ebert/M Ebola/M Ebonics/M Ebony/M Ebro/M Ecclesiastes/M Eco/M Ecstasy Ecuador/M Ecuadoran/SM Ecuadorean Ecuadorian/SM Ed/MNX Edam/SM Edda/M Eddie/M Eddington/M Eddy/M Eden/M Edgar/M Edgardo/M Edinburgh/M Edison/M Edith/M Edmond/M Edmonton/M Edmund/M Edna/M Edsel/M Eduardo/M Edward/SM Edwardian/M Edwardo/M Edwards/M Edwin/M Edwina/M Eeyore/M Effie/M Efrain/M Efren/M Eggo/M Egypt/M Egyptian/MS Egyptology/M Ehrenberg/M Ehrlich/M Eichmann/M Eiffel/M Eileen/M Einstein/MS Eire/M Eisenhower/M Eisenstein/M Eisner/M Elaine/M Elam/M Elanor/M Elastoplast/M Elba/M Elbe/M Elbert/M Elbrus/M Eldon/M Eleanor/M Eleazar/M Electra/M Elena/M Elgar/M Eli/M Elias/M Elijah/M Elinor/M Eliot/M Elisa/M Elisabeth/M Elise/M Eliseo/M Elisha/M Eliza/M Elizabeth/M Elizabethan/SM Ella/M Ellen/M Ellesmere/M Ellie/M Ellington/M Elliot/M Elliott/M Ellis/M Ellison/M Elma/M Elmer/M Elmo/M Elnath/M Elnora/M Elohim/M Eloise/M Eloy/M Elroy/M Elsa/M Elsie/M Elsinore/M Eltanin/M Elton/M Elul/M Elva/M Elvia/M Elvin/M Elvira/M Elvis/M Elway/M Elwood/M Elysee/M Elysian/M Elysium/SM Emacs/M Emanuel/M Emerson/M Emery/M Emil/M Emile/M Emilia/M Emilio/M Emily/M Eminem/M Eminence Emma/M Emmanuel/M Emmett/M Emmy/M Emory/M Encarta/M Endymion/M Eng/M Engels/M England/M English/MRS Englishman/M Englishmen/M Englishwoman/M Englishwomen/M Enid/M Enif/M Eniwetok/M Enkidu/M Enoch/M Enos/M Enrico/M Enrique/M Enron/M Enterprise/M Eocene/M Epcot/M Ephesian/MS Ephesus/M Ephraim/M Epictetus/M Epicurean/M Epicurus/M Epimethius/M Epiphany/SM Episcopal Episcopalian/MS Epistle Epsom/M Epson/M Epstein/M Equuleus/M Er/M Erasmus/M Erato/M Eratosthenes/M Erebus/M Erector/M Erewhon/M Erhard/M Eric/M Erica/M Erich/M Erick/M Ericka/M Erickson/M Eridanus/M Erie/M Erik/M Erika/M Erin/M Eris/MS Eritrea/M Eritrean/SM Erlenmeyer/M Erma/M Erna/M Ernest/M Ernestine/M Ernesto/M Ernie/M Ernst/M Eros/MS Errol/M Erse/M ErvIn/M Erwin/M Esau/M Escher/M Escherichia/M Escondido Eskimo/MS Esmeralda/M Esperanto/M Esperanza/M Espinoza/M Esq/M Esquire/MS Essen/M Essene/M Essequibo/M Essex/M Essie/M Establishment Esteban/M Estela/M Estella/M Estelle/M Ester/M Esterhazy/M Estes/M Esther/M Estonia/M Estonian/SM Estrada/M Ethan/M Ethel/M Ethelred/M Ethernet/M Ethiopia/M Ethiopian/SM Etna/M Eton/M Etruria/M Etruscan/M Etta/M Eu/M Eucharist/MS Eucharistic Euclid/M Eugene/M Eugenia/M Eugenie/M Eugenio/M Eula/M Euler/M Eumenides/M Eunice/M Euphrates/M Eur Eurasia/M Eurasian/MS Euripides/M Eurodollar/SM Europa/M Europe/M European/MS Eurydice/M Eustachian/M Euterpe/M Eva/M Evan/SM Evangelical Evangelina/M Evangeline/M Evangelist/M Evans/M Evansville/M Eve/M Evelyn/M Evenki/M EverReady/M Everest/M Everett/M Everette/M Everglades/M Evert/M Evian/M Evita/M Ewing/M Excalibur/M Excedrin/M Excellency/SM Exchequer Exercycle/M Exocet/M Exodus/M Exxon/M Eyck/M Eyre/M Eysenck/M Ezekiel/M Ezra/M F/MD FAA FAQ/SM FBI/M FCC FD FDA FDIC/M FDR/M FHA/M FICA/M FIFO FL FM/SM FNMA/M FOFL FORTRAN/M FPO FSF/M FSLIC FTC FUD/S FWD FWIW FY FYI Faberge/M Fabian/MS Facebook/M Faeroe/M Fafnir/M Fagin/M Fahd/M Fahrenheit/M Fairbanks/M Faisal/M Faisalabad/M Faith/M Falasha/M Falkland/SM Falklands/M Fallopian/M Falstaff/M Falwell/M Fannie/M Fanny/M Faraday/M Fargo/M Farley/M Farmer/M Farragut/M Farrakhan/M Farrell/M Farrow/M Farsi/M Fassbinder/M Fatah/M Fates/M Father/SM Fatima/M Fatimid/M Faulkner/M Faulknerian/M Fauntleroy/M Faust/M Faustian/M Faustino/M Faustus/M Fawkes/M Fay/M Faye/M Fe/M Feb/M February/SM Fed/SM FedEx/M Federal/MS Federalist/M Federico/M Feds/M Felecia/M Felice/M Felicia/M Felicity/M Felipe/M Felix/M Fellini/M Fenian/M Ferber/M Ferdinand/M Fergus/M Ferguson/M Ferlinghetti/M Fermat/M Fermi/M Fern/M Fernandez/M Fernando/M Ferrari/M Ferraro/M Ferrell/M Ferris/M Feynman/M Fez/M Fiat/M Fiberglas/M Fibonacci/M Fichte/M Fidel/M Fido/M Fielding/M Fields/M Figaro/M Figueroa/M Fiji/M Fijian/MS Filipino/MS Fillmore/M Filofax/M Finch/M Finland/M Finley/M Finn/SM Finnbogadottir/M Finnegan/M Finnish/M Fiona/M Firefox/M Firestone/M Fischer/M Fisher/M Fisk/M Fitch/M Fitzgerald/M Fitzpatrick/M Fitzroy/M Fizeau/M Fla Flanagan/M Flanders/M Flathead Flatt/M Flaubert/M Fleischer/M Fleming/M Flemish/M Fletcher/M Flint/M Flintstones/M Flo/M Flora/M Florence/M Florentine/M Flores/M Florida/M Floridan/M Floridian/SM Florine/M Florsheim/M Flory/M Flossie/M Flowers/M Floyd/M Flynn/M Fm/M Foch/M Fokker/M Foley/M Folgers/M Folsom/M Fomalhaut/M Fonda/M Foosball/M Forbes/M Ford/M Foreman/M Forest/MR Forester/M Formica/MS Formosa/M Formosan/M Forrest/M Forster/M Fortaleza/M Fosse/M Foster/M Fotomat/M Foucault/M Fourier/M Fourneyron/M Fourth Fowler/M Fox/MS Fr/MD Fragonard/M Fran/M France/SM Frances/M Francesca/M Francine/M Francis/M Francisca/M Franciscan/MS Francisco/M Franck/M Franco/M Francois/M Francoise/M Francophile Franglais/M Frank/SM Frankel/M Frankenstein/M Frankfort/M Frankfurt/MR Frankfurter/M Frankie/M Frankish Franklin/M Franks/M Franny/M Franz/M Fraser/M Frau/MN Fraulein Frazier/M Fred/M Freda/M Freddie/M Freddy/M Frederic/M Frederick/M Fredericton/M Fredric/M Fredrick/M Freeman/M Freemason/SM Freemasonry/SM Freetown/M Freida/M Fremont/M French/MS Frenchman/M Frenchmen/M Frenchwoman/M Frenchwomen/M Freon/M Fresnel/M Fresno/M Freud/M Freudian/M Frey/M Freya/M Fri/M Friday/SM Frieda/M Friedan/M Friedman/M Friend/SM Frigga/M Frigidaire/M Frisbee/M Frisco/M Frisian/MS Frito/M Fritz/M Frobisher/M Frodo/M Froissart/M Fromm/M Fronde/M Frontenac/M Frost/M Frostbelt/M Frunze/M Fry/M Frye/M Fuchs/M Fuentes/M Fugger/M Fuji/M Fujitsu/M Fujiwara/M Fujiyama/M Fukuoka/M Fukuyama/M Fulani/M Fulbright/M Fuller/M Fullerton/M Fulton/M Funafuti/M Fundy/M Furies/M Furtwangler/M Fushun/M Fuzhou/M Fuzzbuster/M G/MNRB GA GAO GATT/M GB/M GCC/M GDP/M GE/M GED GHQ/M GHz GI GIF GIGO GM/M GMAT GMT/M GNP/M GNU/M GOP/M GP/M GPA GPO GPS GPU GSA GTE/M GU GUI/M Ga/M Gable/M Gabon/M Gabonese/M Gaborone/M Gabriel/M Gabriela/M Gabrielle/M Gacrux/M Gadsden/M Gaea/M Gael/SM Gaelic/M Gagarin/M Gage/M Gaia/M Gail/M Gaiman/M Gaines/M Gainsborough/M Galahad/SM Galapagos/M Galatea/M Galatia/M Galatians/M Galaxy Galbraith/M Gale/M Galen/M Galibi/M Galilean/SM Galilee/M Galileo/M Gall/M Gallagher/M Gallegos/M Gallic/M Gallicism/SM Gallo/M Galloway/M Gallup/M Galois/M Galsworthy/M Galvani/M Galveston/M Gama Gamay/M Gambia/M Gambian/SM Gamble/M Gamow/M Gandalf/M Gandhi/M Gandhian/M Ganesha/M Ganges/M Gangtok/M Gantry/M Ganymede/M Gap/M Garbo/M Garcia/M Gardner/M Gareth/M Garfield/M Garfunkel/M Gargantua/M Garibaldi/M Garland/M Garner/M Garrett/M Garrick/M Garrison/M Garry/M Garth/M Garvey/M Gary/M Garza/M Gascony/M Gasser/M Gastroenterology Gates/M Gatling/M Gatorade/M Gatsby/M Gatun/M Gauguin/M Gaul/SM Gaulish Gauss/M Gaussian/M Gautama/M Gautier/M Gavin/M Gawain/M Gay/M Gayle/M Gaza/M Gaziantep/M Gd/M Gdansk/M Ge/M Geffen/M Gehenna/M Gehrig/M Geiger/M Gelbvieh/M Geller/M Gemini/MS Gen/M Gena/M Genaro/M Gene/M Genesis/M Genet/M Geneva/M Genevieve/M Genghis/M Genoa/SM Gentoo/M Gentry/M Geo/M Geoffrey/M George/MS Georgetown/M Georgette/M Georgia/M Georgian/MS Georgina/M Ger/M Gerald/M Geraldine/M Gerard/M Gerardo/M Gerber/M Gere/M Geritol/M German/MS Germanic/M Germany/M Geronimo/M Gerry/M Gershwin/M Gertrude/M Gestapo/SM Gethsemane/M Getty/M Gettysburg/M Gewurztraminer/M Ghana/M Ghanaian Ghats/M Ghazvanid/M Ghent/M Ghibelline/M Giacometti/M Giannini/M Giauque/M Gibbon/M Gibbs/M Gibraltar/MS Gibson/M Gide/M Gideon/M Gielgud/M Gienah/M Gil/M Gila/M Gilbert/M Gilberto/M Gilchrist/M Gilda/M Gilead/M Giles/M Gilgamesh/M Gill/M Gillespie/M Gillette/M Gilliam/M Gillian/M Gilligan/M Gilman Gilmore/M Gina/M Ginger/M Gingrich/M Ginny/M Gino/M Ginsberg/M Ginsburg/M Ginsu/M Giorgione/M Giotto/M Giovanni/M Giraudoux/M Giselle/M Gish/M GitHub/M Giuliani/M Giuseppe/M Giza/M Gk Gladstone/MS Gladys/M Glaser/M Glasgow/M Glass/M Glastonbury/M Glaswegian/SM Glaxo/M Gleason/M Glen/M Glenda/M Glendale Glenlivet/M Glenn/M Glenna/M Gloria/M Gloucester/M Glover/M Gnostic/M Gnosticism/M GnuPG Goa/M Gobi/M God/M Godard/M Goddard/M Godel/M Godhead/M Godiva/M Godot/M Godspeed/SM Godthaab/M Godunov/M Godzilla/M Goebbels/M Goering/M Goethals/M Goethe/M Goff/M Gog/M Gogol/M Goiania/M Golan/M Golconda/M Golda/M Goldberg/M Golden/M Goldie/M Goldilocks/M Golding/M Goldman/M Goldsmith/M Goldwater/M Goldwyn/M Golgi/M Golgotha/M Goliath/M Gomez/M Gomorrah/M Gompers/M Gomulka/M Gondwanaland/M Gonzales/M Gonzalez/M Gonzalo/M Good/M Goodall/M Goodman/M Goodrich/M Goodwill/M Goodwin/M Goodyear/M Google/M Goolagong/M Gopher Gorbachev/M Gordian/M Gordimer/M Gordon/M Gore/M Goren/M Gorey/M Gorgas/M Gorgon/M Gorgonzola/M Gorky/M Gospel/MS Goteborg/M Goth/M Gotham/M Gothic/MS Goths Gouda/SM Gould/M Gounod/M Governor Goya/M Gr/B Grable/M Gracchus/M Grace/M Graceland/M Gracie/M Graciela/M Grady/M Graffias/M Grafton/M Graham/M Grahame/M Grail/M Grammy/M Grampians/M Granada/M Grant/M Grass/M Graves/M Gray/M Grecian/M Greece/M Greek/SM Greeley/M Green/SM Greene/M Greenland/M Greenlandic Greenpeace/M Greensboro/M Greensleeves/M Greenspan/M Greenwich/M Greer/M Greg/M Gregg/M Gregorian/M Gregorio/M Gregory/M Grenada/M Grenadian/MS Grenadines/M Grendel/M Grenoble/M Gresham/M Greta/M Gretchen/M Gretel/M Gretzky/M Grey/M Grieg/M Griffin/M Griffith/M Grimes/M Grimm/M Grinch/M Gris/M Gromyko/M Gropius/M Gross/M Grosz/M Grotius/M Grover/M Grozny Grumman/M Grundy/M Grunewald/M Grus/M Gruyere/SM Guadalajara/M Guadalcanal/M Guadalquivir/M Guadalupe/M Guadeloupe/M Guallatiri/M Guam/M Guamanian Guangzhou/M Guantanamo/M Guarani/M Guarnieri/M Guatemala/M Guatemalan/MS Guayaquil/M Gucci/M Guelph/M Guernsey/MS Guerra/M Guerrero/M Guevara/M Guggenheim/M Guiana/M Guido Guillermo/M Guinea/M Guinean/MS Guinevere/M Guinness/M Guiyang/M Guizot/M Gujarat/M Gujarati/M Gujranwala/M Gullah/M Gulliver/M Gumbel/M Gunther/M Guofeng/M Gupta/M Gurkha/M Gus/M Gustav/M Gustavo/M Gustavus/M Gutenberg/M Guthrie/M Gutierrez/M Guy/M Guyana/M Guyanese/M Guzman/M Gwalior/M Gwen/M Gwendoline/M Gwendolyn/M Gwyn/M Gypsy/SM H/M HBO/M HDD HDMI HDTV HF/M HHS HI HIV/M HM HMO/M HMS HOV HP/M HPV HQ/M HR HRH HS HSBC/M HST HT HTML/M HTTP HUD/M Ha/M Haas/M Habakkuk/M Haber/M Hadar/M Hades/M Hadrian/M Hafiz/M Hagar/M Haggai/M Hagiographa/M Hague/M Hahn/M Haida/SM Haifa/M Haiphong/M Haiti/M Haitian/MS Hakka/M Hakluyt/M Hal/SM Haldane/M Hale/M Haleakala/M Haley/M Halifax/M Hall/M Halley/M Halliburton/M Hallie/M Hallmark/M Halloween/MS Hallstatt/M Halon/M Hals/M Halsey/M Ham/M Haman/M Hamburg/MS Hamhung/M Hamilcar/M Hamill/M Hamilton/M Hamiltonian/M Hamitic/M Hamlet/M Hamlin/M Hammarskjold/M Hammerstein/M Hammett/M Hammond/M Hammurabi/M Hampshire/M Hampton/M Hamsun/M Han/SM Hancock/M Handel/M Handy/M Haney/M Hangul/M Hangzhou/M Hank/M Hanna/M Hannah/M Hannibal/M Hanoi/M Hanover/M Hanoverian/M Hans/MN Hansel/M Hansen/M Hanson/M Hanuka Hanukkah/M Hanukkahs Hapsburg/M Harare/M Harbin/M Hardin/M Harding/M Hardy/M Hargreaves/M Harlan/M Harlem/M Harlequin/M Harley/M Harlow/M Harmon/M Harold/M Harper/M Harpy/SM Harrell/M Harriet/M Harriett/M Harrington/M Harris/M Harrisburg/M Harrison/M Harrods/M Harry/M Hart/M Harte/M Hartford/M Hartline/M Hartman/M Harvard/M Harvey/M Hasbro/M Hasidim/M Haskell/M Hastings/M Hatfield/M Hathaway/M Hatsheput/M Hatteras/M Hattie/M Hauptmann/M Hausa/M Hausdorff/M Havana/MS Havarti/M Havel/M Havoline/M Haw Hawaii/M Hawaiian/SM Hawking/M Hawkins/M Hawks Hawthorne/M Hay/SM Hayden/M Haydn/M Hayek/M Hayes/M Haynes/M Hays/M Hayward/M Haywood/M Hayworth/M Hazel/M Hazlitt/M He/M Head/M Hearst/M Heath/MR Heather/M Heaviside/M Heb Hebe/M Hebert/M Hebraic/M Hebraism/SM Hebrew/MS Hebrews/M Hebrides/M Hecate/M Hector/M Hecuba/M Heep/M Hefner/M Hegel/M Hegelian/M Hegira/M Heidegger/M Heidelberg/M Heidi/M Heifetz/M Heimlich/M Heine/M Heineken/M Heinlein/M Heinrich/M Heinz/M Heisenberg/M Heisman/M Helen/M Helena/M Helene/M Helga/M Helicon/M Heliopolis/M Helios/M Hellene/SM Hellenic/M Hellenism/MS Hellenist Hellenistic/M Hellenization/M Hellenize/M Heller/M Hellespont/M Hellman/M Helmholtz/M Heloise/M Helsinki/M Helvetian Helvetius/M Hemingway/M Hench/M Henderson/M Hendrick/MS Hendricks/M Hendrix/M Henley/M Hennessy/M Henri/M Henrietta/M Henrik/M Henry/M Hensley/M Henson/M Hepburn/M Hephaestus/M Hepplewhite/M Hera/M Heracles/M Heraclitus/M Herakles/M Herbart/M Herbert/M Herculaneum/M Herculean Hercules/M Herder/M Hereford/SM Herero/M Heriberto/M Herman/M Hermaphroditus/M Hermes/M Herminia/M Hermitage/M Hermite/M Hermosillo/M Hernandez/M Herod/M Herodotus/M Herr/MG Herrera/M Herrick/M Herring/M Herschel/M Hersey/M Hershel/M Hershey/M Hertz/M Hertzsprung/M Herzegovina/M Herzl/M Heshvan/M Hesiod/M Hesperus/M Hess/M Hesse/M Hessian/M Hester/M Heston/M Hettie/M Hewitt/M Hewlett/M Heyerdahl/M Heywood/M Hezbollah/M Hezekiah/M Hf/M Hg/M Hialeah/M Hiawatha/M Hibernia/M Hibernian Hickman/M Hickok/M Hicks/M Hieronymus/M Higashiosaka Higgins/M Highlander/SM Highlands Highness/M Hilario/M Hilary/M Hilbert/M Hilda/M Hildebrand/M Hilfiger/M Hill/M Hillary/M Hillel/M Hilton/M Himalaya/SM Himalayan Himalayas/M Himmler/M Hinayana/M Hindemith/M Hindenburg/M Hindi/M Hindu/SM Hinduism/SM Hindustan/M Hindustani/SM Hines/M Hinton/M Hipparchus/M Hippocrates/M Hippocratic/M Hiram/M Hirobumi/M Hirohito/M Hiroshima/M Hispanic/SM Hispaniola/M Hiss/M Hitachi/M Hitchcock/M Hitler/MS Hittite/SM Hmong/M Ho/M Hobart/M Hobbes/M Hobbs/M Hockney/M Hodge/SM Hodges/M Hodgkin/M Hoff/M Hoffa/M Hoffman/M Hofstadter/M Hogan/M Hogarth/M Hogwarts/M Hohenlohe/M Hohenstaufen/M Hohenzollern/M Hohhot/M Hohokam/M Hokkaido/M Hokusai/M Holbein/M Holcomb/M Holden/M Holder/M Holiday/M Holiness Holland/ZSMR Hollander/M Hollerith/M Holley/M Hollie/M Hollis/M Holloway/M Holly/M Hollywood/M Holman/M Holmes/M Holocaust/M Holocene/M Holst/M Holstein/SM Holt/M Homer/M Homeric/M Hon Honda/M Honduran/MS Honduras/M Honecker/M Honeywell/M Hong Honiara/M Honolulu/M Honorable Honshu/M Hood/M Hooke/RM Hooker/M Hooper/M Hoosier/MS Hooters/M Hoover/MS Hope/M Hopewell/M Hopi/SM Hopkins/M Hopper/M Horace/M Horacio/M Horatio/M Hormel/M Hormuz/M Horn/M Hornblower/M Horne/M Horowitz/M Horthy/M Horton/M Horus/M Hosea/M Host/SM Hotpoint/M Hottentot/SM Houdini/M House/M Housman/M Houston/M Houyhnhnm/M Hovhaness/M Howard/M Howe/M Howell/MS Howells/M Howrah Hoyle/M Hrothgar/M Hts Huang/M Hubbard/M Hubble/M Huber/M Hubert/M Huck/M Huddersfield Hudson/M Huerta/M Huey/M Huff/M Huffman/M Huggins/M Hugh/MS Hughes/M Hugo/M Huguenot/MS Hui/M Huitzilopotchli/M Hull/M Humberto/M Humboldt/M Hume/M Hummer/M Humphrey/SM Humvee/M Hun/SM Hung/M Hungarian/SM Hungary/M Hunspell/M Hunt/MR Hunter/M Huntington/M Huntley/M Huntsville/M Hurley/M Huron/M Hurst/M Hus/M Hussein/M Husserl/M Hussite/M Huston/M Hutchinson/M Hutton/M Hutu/M Huxley/M Huygens/M Hyades/M Hyde/M Hyderabad/M Hydra/M Hymen/M Hyperion/M Hyundai/M Hz/M I'd I'll I'm I've I/M IA IBM/M ICBM/SM ICC ICU ID/SM IDE IE IED IEEE IKEA/M IL IMF/M IMHO IMNSHO IMO IN ING/M INRI INS IOU/M IP IPA IPO IQ/M IRA/SM IRC IRS/M ISBN ISO/M ISP ISS IT IUD IV/SM IVF Ia Iaccoca/M Iago/M Ian/M Iapetus/M Ibadan/M Iberia/M Iberian/M Ibiza/M Iblis/M Ibo/M Ibsen/M Icahn/M Icarus/M Ice Iceland/MRZ Icelander/M Icelandic/M Ida/M Idaho/SM Idahoan/MS Idahoes Ieyasu/M Ignacio/M Ignatius/M Igor/M Iguassu/M Ijsselmeer/M Ike/M Ikhnaton/M Ila/M Ilene/M Iliad/SM Ill Illinois/M Illinoisan/MS Illuminati/M Ilyushin/M Imelda/M Imhotep/M Imodium/M Imogene/M Imus/M In/M Ina/M Inc Inca/SM Inchon/M Incorporated Ind Independence/M India/M Indian/MS Indiana/M Indianan/SM Indianapolis/M Indianian Indies/M Indira/M Indochina/M Indochinese/M Indonesia/M Indonesian/SM Indore/M Indra/M Indus/M Indy/SM Ines/M Inez/M Inge/M Inglewood Ingram/M Ingres/M Ingrid/M Innocent/M Innsbruck Inonu/M Inquisition/M Inst Instagram/M Instamatic/M Intel/M Intelsat/M Internationale/M Internet/SM Interpol/M Inuit/MS Inuktitut/M Invar/M Io/M Ionesco/M Ionian/MS Ionic/SM Iowa/SM Iowan/MS Iphigenia/M Ipswich Iqaluit/M Iqbal/M Iquitos/M Ir/M Ira/M Iran/M Iranian/SM Iraq/M Iraqi/MS Ireland/M Irene/M Iris/M Irish/MR Irishman/M Irishmen/M Irishwoman/M Irishwomen/M Irkutsk/M Irma/M Iroquoian/SM Iroquois/M Irrawaddy/M Irtish/M Irvin/M Irvine/M Irving/M Irwin/M Isaac/M Isabel/M Isabella/M Isabelle/M Isaiah/M Iscariot/M Isfahan/M Isherwood/M Ishim/M Ishmael/M Ishtar/M Isiah/M Isidro/M Isis/M Islam/MS Islamabad/M Islamic/M Islamism/M Islamist/M Islamophobia Islamophobic Ismael/M Ismail/M Isolde/M Ispell/M Israel/SM Israeli/SM Israelite/M Issac/M Issachar/M Istanbul/M Isuzu/M It Itaipu/M Ital Italian/SM Italianate Italy/M Itasca/M Ithaca/M Ithacan/M Ito/M Iva/M Ivan/M Ivanhoe/M Ives/M Ivorian Ivory/M Ivy/M Iyar/M Izaak/M Izanagi/M Izanami/M Izhevsk/M Izmir/M Izod/M Izvestia/M J/MD JCS JD JFK/M JP JPEG JV Jack/M Jackie/M Jacklyn/M Jackson/M Jacksonian/M Jacksonville/M Jacky/M Jaclyn/M Jacob/SM Jacobean/M Jacobi/M Jacobin/M Jacobite/M Jacobs/M Jacobson/M Jacquard/M Jacqueline/M Jacquelyn/M Jacques/M Jacuzzi/M Jagger/M Jagiellon/M Jaguar/M Jahangir/M Jaime/M Jain/M Jainism/M Jaipur/M Jakarta/M Jake/M Jamaal/M Jamaica/M Jamaican/SM Jamal/M Jamar/M Jame/SM Jamel/M James/M Jamestown/M Jami/M Jamie/M Jan/M Jana/M Janacek/M Jane/M Janell/M Janelle/M Janet/M Janette/M Janice/M Janie/M Janine/M Janis/M Janissary/M Janjaweed/M Janna/M Jannie/M Jansen/M Jansenist/M January/SM Janus/M Jap/SM Japan/M Japanese/MS Japura/M Jared/M Jarlsberg/M Jarred/M Jarrett/M Jarrod/M Jarvis/M Jasmine/M Jason/M Jasper/M Jataka/M Java/SM JavaScript/M Javanese/M Javier/M Jaxartes/M Jay/M Jayapura/M Jayawardene/M Jaycee/MS Jaycees/M Jayne/M Jayson/M Jean/M Jeanette/M Jeanie/M Jeanine/M Jeanne/M Jeannette/M Jeannie/M Jeannine/M Jed/M Jedi/M Jeep/M Jeeves/M Jeff/M Jefferey/M Jefferson/M Jeffersonian/M Jeffery/M Jeffrey/M Jeffry/M Jehoshaphat/M Jehovah/M Jekyll/M Jenifer/M Jenkins/M Jenna/M Jenner/M Jennie/M Jennifer/M Jennings/M Jenny/M Jensen/M Jephthah/M Jerald/M Jeremiah/M Jeremiahs Jeremy/M Jeri/M Jericho/M Jermaine/M Jeroboam/M Jerold/M Jerome/M Jerri/M Jerrod/M Jerrold/M Jerry/M Jersey/MS Jerusalem/M Jess/M Jesse/M Jessica/M Jessie/M Jesuit/MS Jesus/M Jetway/M Jew/SM Jewel/M Jewell/M Jewess/MS Jewish/PM Jewry/M Jezebel/SM Jidda/M Jilin/M Jill/M Jillian/M Jim/M Jimenez/M Jimmie/M Jimmy/M Jinan/M Jinnah/M Jinny/M Jivaro/M Jo/M Joan/M Joann/M Joanna/M Joanne/M Joaquin/M Job/SM Jobs/M Jocasta/M Jocelyn/M Jock/M Jockey/M Jodi/M Jodie/M Jody/M Joe/M Joel/M Joey/M Jogjakarta/M Johann/M Johanna/M Johannes/M Johannesburg/M John/SM Johnathan/M Johnathon/M Johnie/M Johnnie/M Johnny/M Johns/M Johnson/M Johnston/M Jolene/M Jolson/M Jon/M Jonah/M Jonahs Jonas/M Jonathan/M Jonathon/M Jones/M Joni/M Jonson/M Joplin/M Jordan/M Jordanian/MS Jorge/M Jose/M Josef/M Josefa/M Josefina/M Joseph/M Josephine/M Josephs Josephson/M Josephus/M Josh/M Joshua/M Josiah/M Josie/M Josue/M Joule/M Jove/M Jovian/M Joy/M Joyce/M Joycean/M Joyner/M Jpn Jr/M Juan/M Juana/M Juanita/M Juarez/M Jubal/M Judaeo Judah/M Judaic Judaical Judaism/MS Judas/MS Judd/M Jude/M Judea/M Judges Judith/M Judson/M Judy/M Juggernaut/M Jul Jules/M Julia/M Julian/M Juliana/M Julianne/M Julie/M Juliet/M Juliette/M Julio/M Julius/M Julliard/M July/SM Jun/M June/SM Juneau/M Jung/M Jungfrau/M Jungian/M Junior/SM Junker/SM Juno/M Jupiter/M Jurassic/M Jurua/M Justice/M Justin/M Justine/M Justinian/M Jutland/M Juvenal/M K/SMNGJ KB/M KC KFC/M KGB/M KIA KKK/M KO/M KP KS KY Kaaba/M Kabul/M Kafka/M Kafkaesque/M Kagoshima/M Kahlua/M Kaifeng/M Kaiser/MS Kaitlin/M Kalahari/M Kalamazoo/M Kalashnikov/M Kalb/M Kalevala/M Kalgoorlie/M Kali/M Kalmyk/M Kama/M Kamchatka/M Kamehameha/M Kampala/M Kampuchea/M Kan/SM Kanchenjunga/M Kandahar/M Kandinsky/M Kane/M Kannada/M Kano/M Kanpur/M Kansan/MS Kansas/M Kant/M Kantian/M Kaohsiung/M Kaposi/M Kara/M Karachi/M Karaganda/M Karakorum/M Karamazov/M Kareem/M Karen/M Karenina/M Kari/M Karin/M Karina/M Karl/M Karla/M Karloff/M Karo/M Karol/M Karroo/M Karyn/M Kasai/M Kasey/M Kashmir/SM Kasparov/M Kate/M Katelyn/M Katharine/M Katherine/M Katheryn/M Kathiawar/M Kathie/M Kathleen/M Kathmandu/M Kathrine/M Kathryn/M Kathy/M Katie/M Katina/M Katmai/M Katowice/M Katrina/M Katy/M Kauai/M Kaufman/M Kaunas/M Kaunda/M Kawabata/M Kawasaki/M Kay/M Kaye/M Kayla/M Kazakh/M Kazakhs Kazakhstan/M Kazan/M Kazantzakis/M Kb/M Keaton/M Keats/M Keck/M Keenan/M Keewatin/M Keillor/M Keisha/M Keith/M Keller/M Kelley/M Kelli/M Kellie/M Kellogg/M Kelly/M Kelsey/M Kelvin/M Kemerovo/M Kemp/M Kempis/M Ken/M Kendall/M Kendra/M Kendrick/M Kenmore/M Kennan/M Kennedy/M Kenneth/M Kennith/M Kenny/M Kent/M Kenton/M Kentuckian/MS Kentucky/M Kenya/M Kenyan/SM Kenyatta/M Kenyon/M Keogh/M Keokuk/M Kepler/M Kerensky/M Keri/M Kermit/M Kern/M Kerouac/M Kerr/M Kerri/M Kerry/M Kettering/M Keven/M Kevin/M Kevlar/M Kevorkian/M Kewpie/M Key/M Keynes/M Keynesian/M Khabarovsk/M Khachaturian/M Khalid/M Khan/M Kharkov/M Khartoum/M Khayyam/M Khazar/M Khmer/M Khoikhoi/M Khoisan/M Khomeini/M Khorana/M Khrushchev/M Khufu/M Khulna/M Khwarizmi/M Khyber/M Kickapoo/M Kidd/M Kiel/M Kierkegaard/M Kieth/M Kiev/M Kigali/M Kikuyu/M Kilauea/M Kilimanjaro/M Kilroy/M Kim/M Kimberley/M Kimberly/M King/M Kingston/M Kingstown/M Kinko's Kinney/M Kinsey/M Kinshasa/M Kiowa/MS Kip/M Kipling/M Kirby/M Kirchhoff/M Kirchner/M Kirghistan/M Kirghiz/M Kirghizia/M Kiribati/M Kirinyaga/M Kirk/M Kirkland/M Kirkpatrick/M Kirov/M Kirsten/M Kisangani/M Kishinev/M Kislev/M Kissinger/M Kit/M Kitakyushu/M Kitchener/M Kitty/M Kiwanis/M Klan/M Klansman/M Klaus/M Klee/M Kleenex/MS Klein/M Klimt/M Kline/M Klingon/M Klondike/MS Kmart/M Knapp/M Knesset/M Kngwarreye/M Knickerbocker/M Knievel/M Knight/M Knopf/M Knossos/M Knowles/M Knox/M Knoxville/M Knudsen/M Knuth/M Knuths Kobe/M Koch/M Kochab/M Kodachrome/M Kodak/M Kodaly/M Kodiak/M Koestler/M Kohinoor/M Kohl/M Koizumi/M Kojak/M Kolyma/M Kommunizma/M Kong/M Kongo/M Konrad/M Koontz/M Koppel/M Koran/MS Koranic Korea/M Korean/SM Kornberg/M Kory/M Korzybski/M Kosciusko/M Kossuth/M Kosygin/M Koufax/M Kowloon/M Kr/M Kraft/M Krakatoa/M Krakow/M Kramer/M Krasnodar/M Krasnoyarsk/M Krebs/M Kremlin/M Kremlinologist Kremlinology Kresge/M Kringle/M Kris/M Krishna/M Krishnamurti/M Krista/M Kristen/M Kristi/M Kristie/M Kristin/M Kristina/M Kristine/M Kristopher/M Kristy/M Kroc/M Kroger/M Kronecker/M Kropotkin/M Kruger/M Krugerrand/M Krupp/M Krystal/M Kshatriya/M Kublai/M Kubrick/M Kuhn/M Kuibyshev/M Kulthumm/M Kunming/M Kuomintang/M Kurd/M Kurdish/M Kurdistan/M Kurosawa/M Kurt/M Kurtis/M Kusch/M Kutuzov/M Kuwait/M Kuwaiti/SM Kuznets/M Kuznetsk/M Kwakiutl/M Kwan/M Kwangju/M Kwanzaa/MS Ky/MH Kyle/M Kyoto/M Kyrgyzstan/M Kyushu/M L'Amour/M L'Enfant L'Oreal/M L'Ouverture/M L/MN LA LAN/M LBJ/M LC LCD/M LCM LDC LED/M LG/M LGBT LIFO LL LLB/M LLD/M LNG LOGO LP/M LPG LPN/SM LSAT LSD/M LVN La/SM Lab Laban/M Labrador/SM Labradorean Lacey/M Lachesis/M Lacy/M Ladoga/M Ladonna/M Lady/M Ladyship/MS Lafayette/M Lafitte/M Lagos/M Lagrange/M Lagrangian/M Lahore/M Laius/M Lajos/M Lakeisha/M Lakewood Lakisha/M Lakota/M Lakshmi/M Lamaism/SM Lamar/M Lamarck/M Lamaze/M Lamb/M Lambert/M Lamborghini/M Lambrusco/M Lamentations Lamont/M Lana/M Lanai/M Lancashire/M Lancaster/M Lance/M Lancelot/M Land/M Landon/M Landry/M Landsat/M Landsteiner/M Lane/M Lang/M Langerhans/M Langland/M Langley/M Langmuir/M Lanka/M Lankan/M Lanny/M Lansing/M Lanzhou/M Lao/SM Laocoon/M Laos/M Laotian/SM Laplace/M Lapland/MR Lapp/SM Lara/M Laramie/M Lardner/M Laredo/M Larousse/M Larry/M Lars/MN Larsen/M Larson/M Lascaux/M Lassa/M Lassen/M Lassie/M Lat/M Latasha/M Lateran/M Latham/M Latin/MRS Latina Latino/SM Latisha/M Latonya/M Latoya/M Latrobe/M Latvia/M Latvian/MS Laud/MR Lauder/M Laue/M Laundromat/M Laura/M Laurasia/M Laurel/M Lauren/M Laurence/M Laurent/M Lauri/M Laurie/M Laval/M Lavern/M Laverne/M Lavoisier/M Lavonne/M Lawanda/M Lawrence/M Lawson/M Layamon/M Layla/M Lazaro/M Lazarus/M Le/SM Lea/M Leach/M Leadbelly/M Leah/M Leakey/M Lean/M Leander/M Leann/M Leanna/M Leanne/M Lear/M Learjet/M Leary/M Leavenworth/M Lebanese/M Lebanon/M Lebesgue/M Leblanc/M Leda/M Lederberg/M Lee/M Leeds/M Leeuwenhoek/M Leeward/M Left Legendre/M Leger/M Leghorn/M Lego/M Legree/M Lehman/M Leibniz/M Leicester/SM Leiden/M Leif/M Leigh/M Leila/M Leipzig/M Lela/M Leland/M Lelia/M Lemaitre/M Lemuel/M Lemuria/M Len/M Lena/M Lenard/M Lenin/M Leningrad/M Leninism/M Leninist/M Lennon/M Lenny/M Leno/M Lenoir/M Lenora/M Lenore/M Lent/SMN Lenten/M Leo/SM Leola/M Leon/M Leona/M Leonard/M Leonardo/M Leoncavallo/M Leonel/M Leonid/M Leonidas/M Leonor/M Leopold/M Leopoldo/M Lepidus/M Lepke/M Lepus/M Lerner/M Leroy/M Les/M Lesa/M Lesley/M Leslie/M Lesotho/M Lesseps/M Lessie/M Lester/M Lestrade/M Leta/M Letha/M Lethe/M Leticia/M Letitia/M Letterman/M Levant/M Levesque/M Levi/SM Leviathan/M Levine/M Leviticus/M Levitt/M Levy/M Lew/M Lewinsky/M Lewis/M Lexington/M Lexus/M Lhasa/MS Lhotse/M Li/MY Libby/M Liberace/M Liberia/M Liberian/SM Libra/MS LibreOffice/M Libreville/M Librium/M Libya/M Libyan/SM Lichtenstein/M Lidia/M Lie/M Lieberman/M Liebfraumilch/M Liechtenstein/ZMR Liechtensteiner/M Liege/M Lieut Lila/M Lilia/M Lilian/M Liliana/M Lilith/M Liliuokalani/M Lille/M Lillian/M Lillie/M Lilliput/M Lilliputian/MS Lilly/M Lilongwe/M Lily/M Lima/M Limbaugh/M Limbo Limburger/M Limoges/M Limousin/M Limpopo/M Lin/M Lina/M Lincoln/MS Lind/M Linda/M Lindbergh/M Lindsay/M Lindsey/M Lindy/M Linnaeus/M Linotype/M Linton/M Linus/M Linux/MS Linwood/M Lionel/M Lipizzaner/M Lippi/M Lippmann/M Lipscomb/M Lipton/M Lisa/M Lisbon/M Lissajous/M Lister/M Listerine/M Liston/M Liszt/M Lithuania/M Lithuanian/MS Little/M Litton/M Liverpool/M Liverpudlian/SM Livia/M Livingston/M Livingstone/M Livonia/M Livy/M Liz/M Liza/M Lizzie/M Lizzy/M Ljubljana/M Llewellyn/M Lloyd/M Ln Loafer/SM Lobachevsky/M Lochinvar/M Locke/M Lockean/M Lockheed/M Lockwood/M Lodge/M Lodz/M Loewe/M Loewi/M Loews/M Logan/M Lohengrin/M Loire/M Lois/M Loki/M Lola/M Lolita/M Lollard/M Lollobrigida/M Lombard/M Lombardi/M Lombardy/M Lome/M Lon/M London/MRZ Londoner/M Long/M Longfellow/M Longstreet/M Longueuil Lonnie/M Lopez/M Lora/M Loraine/M Lord/SM Lordship/SM Lorelei/M Loren/M Lorena/M Lorene/M Lorentz/M Lorenz/M Lorenzo/M Loretta/M Lori/M Lorie/M Lorna/M Lorraine/M Lorre/M Lorrie/M Los Lot/M Lothario/SM Lott/M Lottie/M Lou/M Louella/M Louie/M Louis/M Louisa/M Louise/M Louisiana/M Louisianan/MS Louisianian/MS Louisville/M Lourdes/M Louvre/M Love/M Lovecraft/M Lovelace/M Lowe/M Lowell/M Lowenbrau/M Lowery/M Lowlands Loyang/M Loyd/M Loyola/M Lr Lt Ltd Lu/M Luanda/M Luann/M Lubavitcher/M Lubbock/M Lubumbashi/M Lucas/M Luce/M Lucia/M Lucian/M Luciano/M Lucien/M Lucifer/M Lucile/M Lucille/M Lucinda/M Lucio/M Lucite/SM Lucius/M Lucknow/M Lucretia/M Lucretius/M Lucy/M Luddite/MS Ludhiana/M Ludwig/M Luella/M Lufthansa/M Luftwaffe/M Luger/M Lugosi/M Luigi/M Luis/M Luisa/M Luke/M Lula/M Lully/M Lulu/M Lumiere/M Luna/M Lupe/M Lupercalia/M Lupus/M Luria/M Lusaka/M Lusitania/M Luther/M Lutheran/SM Lutheranism/MS Luvs/M Luxembourg/ZMR Luxembourger/M Luxembourgian Luz/M Luzon/M Lvov/M LyX/M Lyallpur Lycra/M Lycurgus/M Lydia/M Lydian/SM Lyell/M Lyle/M Lyly/M Lyman/M Lyme/M Lynch/M Lynda/M Lyndon/M Lynette/M Lynn/M Lynne/M Lynnette/M Lyon/SM Lyons/M Lyra/M Lysenko/M Lysistrata/M Lysol/M M/SMGB MA/M MASH MB/M MBA/M MC MCI/M MD/M MDT ME MEGO/S MFA/M MGM/M MHz MI/M MIA MIDI/M MIPS MIRV MIT/M MM MN MO MOOC MP/M MPEG MRI/M MS/M MSG/M MST/M MSW MT/M MTV/M MVP/M MW Maalox/M Mabel/M Mable/M Mac/M MacArthur/M MacBride/M MacDonald/M MacLeish/M Macao/M Macaulay/M Macbeth/M Maccabees Maccabeus/M Mace/M Macedon/M Macedonia/M Macedonian/SM Mach/M Machiavelli/M Machiavellian/M Macias/M Macintosh/M Mack/M Mackenzie/M Mackinac/M Mackinaw/M Macmillan/M Macon/M Macumba/M Macy/M Madagascan/SM Madagascar/M Madden/M Maddox/M Madeira/SM Madeleine/M Madeline/M Madelyn/M Madge/M Madison/M Madonna/SM Madras/M Madrid/M Madurai/M Mae/M Maeterlinck/M Mafia/MS Mafioso/M Magdalena/M Magdalene/M Magellan/M Magellanic/M Maggie/M Maghreb/M Magi Maginot/M Magnitogorsk/M Magog/M Magoo/M Magritte/M Magsaysay/M Magus Magyar/SM Mahabharata/M Maharashtra/M Mahavira/M Mahayana/M Mahayanist/M Mahdi/M Mahfouz/M Mahican/SM Mahler/M Mai/M Maidenform/M Maigret/M Mailer/M Maillol/M Maiman/M Maimonides/M Maine/MZR Mainer/M Maisie/M Maitreya/M Maj Majesty Major/M Majorca/M Majuro/M Makarios/M Maker/M Malabar/M Malabo/M Malacca/M Malachi/M Malagasy/M Malamud/M Malaprop/M Malawi/M Malawian/SM Malay/MS Malaya/M Malayalam/M Malayan/MS Malaysia/M Malaysian/MS Malcolm/M Maldive/MS Maldives/M Maldivian/MS Maldonado/M Male/M Mali/M Malian/SM Malibu/M Malinda/M Malinowski/M Mallarme/M Mallomars/M Mallory/M Malone/M Malory/M Malplaquet/M Malraux/M Malta/M Maltese/M Malthus/M Malthusian/SM Mameluke/M Mamet/M Mamie/M Mamore/M Man/M Managua/M Manama/M Manasseh/M Manchester/M Manchu/SM Manchuria/M Manchurian/M Mancini/M Mancunian/MS Mandalay/M Mandarin/M Mandela/M Mandelbrot/M Mandingo/M Mandrell/M Mandy/M Manet/M Manfred/M Manhattan/SM Mani/M Manichean/M Manila/SM Manitoba/M Manitoulin/M Manley/M Mann/GM Mannheim/M Manning/M Mansfield/M Manson/M Mantegna/M Mantle/M Manuel/M Manuela/M Manx/M Mao/M Maoism/SM Maoist/SM Maori/MS Mapplethorpe/M Maputo/M Mar/SM Mara/M Maracaibo/M Marat/M Maratha/M Marathi/M Marathon/M Marc/M Marceau/M Marcel/M Marcelino/M Marcella/M Marcelo/M March/MS Marci/M Marcia/M Marciano/M Marcie/M Marco/MS Marconi/M Marcos/M Marcus/M Marcuse Marcy/M Marduk/M Margaret/M Margarita/M Margarito/M Marge/M Margery/M Margie/M Margo/M Margot Margret/M Margrethe/M Marguerite/M Mari/SM Maria/M Marian/M Mariana/SM Marianas/M Marianne/M Mariano/M Maribel/M Maricela/M Marie/M Marietta/M Marilyn/M Marin/M Marina/M Marine/SM Mario/M Marion/M Maris/M Marisa/M Marisol/M Marissa/M Maritain/M Maritza/M Mariupol Marius/M Marjorie/M Marjory/M Mark/SM Markab/M Markham/M Markov/M Marks/M Marla/M Marlboro/M Marlborough/M Marlene/M Marley/M Marlin/M Marlon/M Marlowe/M Marmara/M Marne/M Maronite/M Marple/M Marquesas/M Marquette/M Marquez/M Marquis/M Marquita/M Marrakesh/M Marriott/M Mars/MS Marsala/M Marseillaise/MS Marseilles/M Marsh/M Marsha/M Marshall/M Marta/M Martel/M Martha/M Martial/M Martian/SM Martin/M Martina/M Martinez/M Martinique/M Marty/M Marva/M Marvell/M Marvin/M Marx/M Marxian Marxism/SM Marxist/SM Mary/M Maryann/M Maryanne/M Maryellen/M Maryland/MR Marylander/M Marylou/M Masada/M Masai/M Masaryk/M Mascagni/M Masefield/M Maserati/M Maseru/M Mashhad/M Mason/MS Masonic/M Masonite/M Mass/MS Massachusetts/M Massasoit/M Massenet/M Massey/M Master/S MasterCard/M Masters/M Mather/M Mathew/SM Mathews/M Mathewson/M Mathias/M Mathis/M Matilda/M Matisse/M Matt/M Mattel/M Matterhorn/M Matthew/SM Matthews/M Matthias/M Mattie/M Maud/M Maude/M Maugham/M Maui/M Maupassant/M Maura/M Maureen/M Mauriac/M Maurice/M Mauricio/M Maurine/M Mauritania/M Mauritanian/SM Mauritian/SM Mauritius/M Mauro/M Maurois/M Mauryan/M Mauser/M Mavis/M Max/M Maximilian/M Maxine/M Maxwell/M May/SMR Maya/SM Mayan/MS Mayer/M Mayfair/M Mayflower/M Maynard/M Mayo/M Maypole Mayra/M Mays/M Maytag/M Mazama/M Mazarin/M Mazatlan/M Mazda/M Mazola/M Mazzini/M Mb/M Mbabane/M Mbini/M McAdam/M McBride/M McCain/M McCall/M McCarthy/M McCarthyism/M McCartney/M McCarty/M McClain/M McClellan/M McClure/M McConnell/M McCormick/M McCoy/M McCray/M McCullough/M McDaniel/M McDonald/M McDonnell/M McDowell/M McEnroe/M McFadden/M McFarland/M McGee/M McGovern/M McGowan/M McGuffey/M McGuire/M McIntosh/M McIntyre/M McKay/M McKee/M McKenzie/M McKinley/M McKinney/M McKnight/M McLaughlin/M McLean/M McLeod/M McLuhan/M McMahon/M McMillan/M McNamara/M McNaughton/M McNeil/M McPherson/M McQueen/M McVeigh/M Md/M Me Mead/M Meade/M Meadows/M Meagan/M Meany/M Mecca/MS Medan/M Medea/M Medellin/M Media/M Medicaid/SM Medicare/SM Medici/M Medina/M Mediterranean/MS Medusa/M Meg/M Megan/M Meghan/M Meier/M Meighen/M Meiji/M Meir/M Mejia/M Mekong/M Mel/M Melanesia/M Melanesian/M Melanie/M Melba/M Melbourne/M Melchior/M Melchizedek/M Melendez/M Melinda/M Melisa/M Melisande/M Melissa/M Mellon/M Melody/M Melpomene/M Melton/M Melva/M Melville/M Melvin/M Memling/M Memphis/M Menander/M Mencius/M Mencken/M Mendel/M Mendeleev/M Mendelian/M Mendelssohn/M Mendez/M Mendocino/M Mendoza/M Menelaus/M Menelik/M Menes/M Mengzi Menkalinan/M Menkar/M Menkent/M Mennen/M Mennonite/MS Menominee/M Menotti/M Mensa/M Mentholatum/M Menuhin/M Menzies/M Mephisto Mephistopheles/M Merak/M Mercado/M Mercator/M Mercedes/M Mercer/M Mercia/M Merck/M Mercurochrome/M Mercury/SM Meredith/M Merino/M Merle/M Merlin/M Merlot/M Merovingian/M Merriam/M Merrick/M Merrill/M Merrimack/M Merritt/M Merthiolate/M Merton/M Mervin/M Mesa/M Mesabi/M Mesmer/M Mesolithic/M Mesopotamia/M Mesopotamian Mesozoic/M Messerschmidt/M Messiaen/M Messiah/M Messiahs Messianic Metallica/M Metamucil/M Methodism/SM Methodist/SM Methuselah/M Metternich/M Meuse/M Mex Mexicali/M Mexican/MS Mexico/M Meyer/MS Meyerbeer/M Meyers/M Mfume/M Mg/M Mgr MiG/M Mia/M Miami/MS Miaplacidus/M Micah/M Micawber/M Mich/M Michael/M Michaelmas/MS Micheal/M Michel/M Michelangelo/M Michele/M Michelin/M Michelle/M Michelob/M Michelson/M Michigan/M Michigander/MS Michiganite Mick/M Mickey/M Mickie/M Micky/M Micmac/SM Micronesia/M Micronesian/M Microsoft/M Midas/M Middleton/M Mideast Mideastern Midland/MS Midway/M Midwest/M Midwestern/MR Miguel/M Mike/M Mikhail/M Mikoyan/M Milagros/M Milan/M Milanese Mildred/M Miles/M Milford/M Milken/M Mill/SMR Millard/M Millay/M Miller/M Millet/M Millicent/M Millie/M Millikan/M Mills/M Milne/M Milo/M Milosevic/M Milquetoast/M Miltiades/M Milton/M Miltonic/M Miltown/M Milwaukee/M Mimi/M Mimosa/M Min/M Minamoto/M Mindanao/M Mindoro/M Mindy/M Minerva/M Ming/M Mingus/M Minn Minneapolis/M Minnelli/M Minnesota/M Minnesotan/SM Minnie/M Minoan/MS Minolta/M Minos/M Minot/M Minotaur/M Minsk/M Minsky/M Mintaka/M Minuit/M Minuteman/M Miocene/M Mir/M Mira/M Mirabeau/M Mirach/M Miranda/M Mirfak/M Miriam/M Miro/M Mirzam/M Miskito/M Miss Mississauga/M Mississippi/M Mississippian/SM Missouri/M Missourian/MS Missy/M Mistassini/M Mister Mistress Misty/M Mitch/M Mitchel/M Mitchell/M Mitford/M Mithra/M Mithridates/M Mitsubishi/M Mitterrand/M Mitty/M Mitzi/M Mixtec/M Mizar/M Mk Mlle Mme/S Mn/M Mnemosyne/M Mo/M Mobil/M Mobile/M Mobutu/M Modesto/M Modigliani/M Moe/M Moet/M Mogadishu/M Mogul/MS Mohacs/M Mohamed/M Mohammad/M Mohammedan/SM Mohammedanism/SM Mohave/SM Mohawk/SM Mohegan Moho/M Mohorovicic/M Moira/M Moises/M Moiseyev/M Mojave/SM Moldavia/M Moldavian Moldova/M Moldovan Moliere/M Molina/M Moll/M Mollie/M Molly/M Molnar/M Moloch/M Molokai/M Molotov/M Moluccas/M Mombasa/M Mon/SM Mona/M Monacan Monaco/M Mondale/M Monday/SM Mondrian/M Monegasque/SM Monera/M Monet/M Mongol/SM Mongolia/M Mongolian/SM Mongolic/M Mongoloid Monica/M Monique/M Monk/M Monmouth/M Monongahela/M Monroe/M Monrovia/M Monsanto/M Monsignor/SM Mont/M Montague/M Montaigne/M Montana/M Montanan/SM Montcalm/M Monte/M Montenegrin/M Montenegro/M Monterrey/M Montesquieu/M Montessori/M Monteverdi/M Montevideo/M Montezuma/M Montgolfier/M Montgomery/M Monticello/M Montoya/M Montpelier/M Montrachet/M Montreal/M Montserrat/M Monty/M Moody/M Moog/M Moon/M Mooney/M Moor/SM Moore/M Moorish/M Morales/M Moran/M Moravia/M Moravian/M Mordred/M More/M Moreno/M Morgan/SM Moriarty/M Morin/M Morison/M Morita/M Morley/M Mormon/SM Mormonism/SM Moro/M Moroccan/SM Morocco/M Moroni/M Morpheus/M Morphy/M Morris/M Morrison/M Morrow/M Morse/M Mort/M Mortimer/M Morton/M Mosaic/M Moscow/M Moseley/M Moselle/M Moses/M Mosley/M Moss/M Mosul/M Motorola/M Motown/M Motrin/M Mott/M Moulton/M Mount/M Mountbatten/M Mountie/MS Moussorgsky/M Mouthe/M Mouton/M Mowgli/M Mozambican/SM Mozambique/M Mozart/M Mozilla/M Mr/SM Ms/S Msgr Mt Muawiya/M Mubarak/M Mueller/M Muenster/MS Mugabe/M Muhammad/M Muhammadan/MS Muhammadanism/SM Muir/M Mujib/M Mulder/M Mullen/M Muller/M Mulligan/M Mullikan/M Mullins/M Mulroney/M Multan/M Multics Mumbai/M Mumford/M Munch/M Munchhausen/M Munich/M Munoz/M Munro/M Munster/M Muppet/M Murasaki/M Murat/M Murchison/M Murcia Murdoch/M Muriel/M Murillo/M Murine/M Murmansk/M Murphy/M Murray/M Murrow/M Murrumbidgee/M Muscat/M Muscovite/M Muscovy/M Muse/M Musharraf/M Musial/M Muskogee/M Muslim/MS Mussolini/M Mussorgsky/M Mutsuhito/M Muzak/M MySpace/M Myanmar/M Mycenae/M Mycenaean/M Myers/M Mylar/MS Myles/M Myra/M Myrdal/M Myrna/M Myron/M Myrtle/M Mysore/M Myst/M N'Djamena N/MD NAACP/M NAFTA/M NASA/M NASCAR/M NASDAQ/M NATO/M NB NBA/M NBC/M NBS NC NCAA/M NCO ND NE/M NEH NF NFC NFL/M NH NHL/M NIH NIMBY NJ NLRB NM NORAD/M NOW NP NPR/M NR NRA NRC NS NSA/M NSC NSF NT NV NW/M NWT NY NYC NYSE NZ Na/M Nabisco/M Nabokov/M Nader/M Nadia/M Nadine/M Nagasaki/M Nagoya/M Nagpur/M Nagy/M Nahuatl/MS Nahum/M Naipaul/M Nair/M Nairobi/M Naismith/M Nam/M Namath/M Namibia/M Namibian/MS Nan/M Nanak/M Nanchang/M Nancy/M Nanette/M Nanjing/M Nannie/M Nanook/M Nansen/M Nantes/M Nantucket/M Naomi/M Naphtali/M Napier/M Naples/M Napoleon/MS Napoleonic/M Napster/M Narcissus/M Narmada/M Narnia/M Narraganset Narragansett/M Nash/M Nashua/M Nashville/M Nassau/M Nasser/M Nat/M Natalia/M Natalie/M Natasha/M Natchez/M Nate/MN Nathan/SM Nathaniel/M Nathans/M Nation/M Nationwide/M Nativity/M Naugahyde/M Nauru/M Nautilus/M Navajo/SM Navajoes Navarre/M Navarro/M Navratilova/M Navy Nazarene/M Nazareth/M Nazca/M Nazi/SM Nazism/MS Nb/M Nd/M Ndjamena/M Ne/M NeWS NeWSes Neal/M Neanderthal/SM Neapolitan/M Neb Nebr Nebraska/M Nebraskan/MS Nebuchadnezzar/M Ned/M Nefertiti/M Negev/M Negress/MS Negritude Negro/MS Negroes Negroid/SM Negros/M Nehemiah/M Nehru/M Neil/M Nelda/M Nell/M Nellie/M Nelly/M Nelsen/M Nelson/M Nembutal/M Nemesis/M Neogene/M Neolithic Nepal/M Nepalese/M Nepali/MS Neptune/M Nereid/M Nerf/M Nero/M Neruda/M Nescafe/M Nesselrode/M Nestle/M Nestor/M Nestorius/M Netflix/M Netherlander/SM Netherlands/M Netscape/M Nettie/M Netzahualcoyotl/M Nev/M Neva/M Nevada/M Nevadan/SM Nevadian Nevis/M Nevsky/M Newark/M Newcastle/M Newfoundland/MRS Newman/M Newport/M Newsweek/M Newton/M Newtonian/M Nexis/M Ngaliema/M Nguyen/M Ni/M Niagara/M Niamey/M Nibelung/M Nicaea/M Nicaragua/M Nicaraguan/SM Niccolo/M Nice/M Nicene/M Nichiren/M Nicholas/M Nichole/M Nichols/M Nicholson/M Nick/M Nickelodeon/M Nicklaus/M Nickolas/M Nicobar/M Nicodemus/M Nicola/SM Nicolas/M Nicole/M Nicosia/M Niebuhr/M Nielsen/M Nietzsche/M Nieves/M Nigel/M Niger/M Nigeria/M Nigerian/MS Nigerien/M Nightingale/M Nijinsky/M Nike/M Nikita/M Nikkei/M Nikki/M Nikolai/M Nikon/M Nile/M Nimitz/M Nimrod/M Nina/M Nineveh/M Nintendo/M Niobe/M Nippon/M Nipponese/M Nirenberg/M Nirvana/M Nisan/M Nisei/M Nissan/M Nita/M Nivea/M Nixon/M Nkrumah/M No/M NoDoz/M Noah/M Nobel/M Nobelist/MS Noble/M Noe/M Noel/SM Noelle/M Noemi/M Nokia/M Nola/M Nolan/M Nome/M Nona/M Nootka/M Nora/M Norbert/M Norberto/M Nordic/MS Noreen/M Norfolk/M Noriega/M Norma/M Norman/MS Normand/M Normandy/M Norplant/M Norris/M Norse/M Norseman/M Norsemen/M North/M Northampton/M Northeast/MS Northerner/M Northrop/M Northrup/M Norths Northwest/SM Norton/M Norw Norway/M Norwegian/SM Norwich/M Nosferatu/M Nostradamus/M Nottingham/M Nouakchott/M Noumea/M Nov/M Nova/M Novartis/M November/MS Novgorod/M Novocain/MS Novocaine Novokuznetsk/M Novosibirsk/M Noxzema/M Noyce/M Noyes/M Np/M Nubia/M Nubian/M Nukualofa/M Numbers/M Nunavut/M Nunez/M Nunki/M Nuremberg/M Nureyev/M NutraSweet/M NyQuil/M Nyasa/M Nyerere/M O'Brien/M O'Casey/M O'Connell/M O'Connor/M O'Donnell/M O'Hara/M O'Higgins/M O'Keeffe/M O'Neil/M O'Neill/M O'Rourke/M O'Toole/M O/SM OAS/M OB OCR OD/SM OE OED OH OHSA/M OJ OK/SMDG OMB/M ON OPEC/M OR OS/M OSHA/M OSes OT OTB OTC OTOH Oahu/M Oakland/M Oakley/M Oates/M Oaxaca/M Ob/M Obadiah/M Obama/M Obamacare Oberlin/M Oberon/M Occam/M Occident Occidental/MS Oceania/M Oceanside Oceanus/M Ochoa/M Oct/M Octavia/M Octavian/M Octavio/M October/SM Odell/M Oder/M Odessa/M Odets/M Odin/M Odis/M Odom/M Odysseus/M Odyssey/M Oedipal/M Oedipus/M Oersted/M Ofelia/M Offenbach/M OfficeMax/M Ogbomosho/M Ogden/M Ogilvy/M Oglethorpe/M Ohio/M Ohioan/SM Oise/M Ojibwa/SM Okayama Okeechobee/M Okefenokee/M Okhotsk/M Okinawa/M Okinawan Okla Oklahoma/M Oklahoman/M Oktoberfest/M Ola/M Olaf/M Olajuwon/M Olav/M Oldenburg/M Oldfield/M Oldsmobile/M Olduvai/M Olen/M Olenek/M Olga/M Oligocene/M Olin/M Olive/MR Oliver/M Olivetti/M Olivia/M Olivier/M Ollie/M Olmec/M Olmsted/M Olsen/M Olson/M Olympia/SM Olympiad/MS Olympian/MS Olympic/SM Olympics/M Olympus/M Omaha/MS Oman/M Omani/MS Omar/M Omayyad/M Omdurman/M Omnipotent Omsk/M Onassis/M Oneal/M Onega/M Onegin/M Oneida/MS Onion/M Ono/M Onondaga/MS Onsager/M Ont Ontarian Ontario/M Oort/M Opal/M Opel/M OpenOffice/M Ophelia/M Ophiuchus/M Oppenheimer/M Oprah/M Ora/M Oracle/M Oran/M Orange/M Oranjestad/M Orbison/M Ordovician/M Ore/N Oreg Oregon/M Oregonian/SM Oreo/M Orestes/M Orient/M Oriental/MS Orin/M Orinoco/M Orion/M Oriya/M Orizaba/M Orkney/M Orlando/M Orleans/M Orlon/MS Orly/M Orpheus/M Orphic/M Orr/M Ortega/M Orthodox Ortiz/M Orval/M Orville/M Orwell/M Orwellian/M Os/M Osage/MS Osaka/M Osbert/M Osborn/M Osborne/M Oscar/MS Osceola/M Osgood/M Oshawa/M Oshkosh/M Osiris/M Oslo/M Osman/M Ostrogoth/M Ostwald/M Osvaldo/M Oswald/M Othello/M Otis/M Ottawa/SM Otto/M Ottoman/M Ouagadougou/M Ouija/MS Ovid/M Owen/SM Owens/M Oxford/SM Oxnard/M Oxonian/M Oxus/M Oxycontin/M Oz/M Ozark/MS Ozarks/M Ozymandias/M Ozzie/M P/MN PA/M PAC/M PARC/S PASCAL PBS/M PBX PC/SM PCB PCMCIA PCP/M PD PDF PDQ PDT PE PET/M PFC PG PGP PIN PJ's PLO/M PM/SMDG PMS/M PO POW/M PP PPS PR PRC/M PRO PS/M PST/M PT PTA/M PTO PVC/M PW PX Pa/M Paar/M Pablo/M Pablum/M Pabst/M Pace/M Pacheco/M Pacific/M Pacino/M Packard/M Padang Paderewski/M Padilla/M Paganini/M Page/M Paglia/M Pahlavi/M Paige/M Paine/M Paiute/SM Pakistan/M Pakistani/SM Palembang/M Paleocene/M Paleogene/M Paleolithic/M Paleozoic/M Palermo/M Palestine/M Palestinian/SM Palestrina/M Paley/M Palikir/M Palisades/M Palladio/M Palmer/M Palmerston/M Palmolive/M Palmyra/M Palomar/M Pam/M Pamela/M Pamirs/M Pampers/M Pan/M Panama/SM Panamanian/MS Panasonic/M Pandora/M Pangaea/M Pankhurst/M Panmunjom/M Pansy/M Pantagruel/M Pantaloon/M Pantheon/M Panza/M Paracelsus/M Paraclete/M Paradise Paraguay/M Paraguayan/MS Paralympic/S Paramaribo/M Paramount/M Parana/M Parcheesi/M Pareto/M Paris/M Parisian/MS Park/SMR Parker/M Parkinson/M Parkman/M Parks/M Parliament/M Parmenides Parmesan/MS Parnassus/MS Parnell/M Parr/M Parrish/M Parsifal/M Parsons/M Parthenon/M Parthia/M Pasadena/M Pascal/SM Pasquale/M Passion/SM Passover/MS Pasternak/M Pasteur/M Pat/M Patagonia/M Patagonian/M Pate/M Patel/M Paterson/M Patna/M Patrica/M Patrice/M Patricia/M Patrick/M Patsy/M Patterson/M Patti/M Patton/M Patty/M Paul/GM Paula/M Paulette/M Pauli/M Pauline/M Pauling/M Pavarotti/M Pavlov/M Pavlova/M Pavlovian/M Pawnee/SM PayPal/M Payne/M Pb/M Pd/M Peabody/M Peace/M Peale/M Pearl/M Pearlie/M Pearson/M Peary/M Pechora/M Peck/M Peckinpah/M Pecos/M Pedro/M Peel/M Peg/M Pegasus/MS Peggy/M Pei/M Peiping/M Peking/SM Pekingese/SM Pele/M Pelee/M Peloponnese/M Pembroke/M Pen/M Pena/M Penderecki/M Penelope/M Penn/M Penna Penney/M Pennington/M Pennsylvania/M Pennsylvanian/MS Penny/M Pennzoil/M Pensacola/M Pentagon/M Pentateuch/M Pentax/M Pentecost/SM Pentecostal/MS Pentecostalism Pentium/SM Peoria/M Pepin/M Pepsi/M Pepys/M Pequot/M Percheron/M Percival/M Percy/M Perelman/M Perez/M Periclean/M Pericles/M Perkins/M Perl/SM Perm/M Permalloy/M Permian/M Pernod/M Peron/M Perot/M Perrier/M Perry/RM Perseid/M Persephone/M Persepolis/M Perseus/M Pershing/M Persia/M Persian/SM Perth/M Peru/M Peruvian/MS Peshawar/M Petain/M Pete/RMZ Peter/M Peters/MN Petersen/M Peterson/M Petra/M Petrarch/M Petty/M Peugeot/M Pfc Pfizer/M PhD/M Phaedra/M Phaethon/M Phanerozoic/M Pharaoh/M Pharaohs Pharisaic Pharisaical Pharisee/MS Phekda/M Phelps/M Phidias/M Phil/MY Philadelphia/M Philby/M Philemon/M Philip/MS Philippe/M Philippians/M Philippine/SM Philippines/M Philips/M Philistine/M Phillip/SM Phillipa/M Phillips/M Philly/M Phipps/M Phobos/M Phoebe/M Phoenicia/M Phoenician/SM Phoenix/M Photostat/MS Photostatted Photostatting Phrygia/M Phyllis/M Piaf/M Piaget/M Pianola/M Picasso/M Piccadilly/M Pickering/M Pickett/M Pickford/M Pickwick/M Pict/M Piedmont/M Pierce/M Pierre/M Pierrot/M Pike/M Pilate/MS Pilates/M Pilcomayo/M Pilgrim/SM Pillsbury/M Pinatubo/M Pincus/M Pindar/M Pinkerton/M Pinocchio/M Pinochet/M Pinter/M Pinyin Pippin/M Piraeus/M Pirandello/M Pisa/M Pisces/M Pisistratus/M Pissaro/M Pitcairn/M Pitt/SM Pittman/M Pitts/M Pittsburgh/M Pius/M Pizarro/M Pkwy Pl Planck/M Plano Plantagenet/M Plasticine/M Plataea/M Plath/M Plato/M Platonic Platonism/M Platonist/M Platte/M Plautus/M PlayStation/M Playboy/M Playtex/M Pleiades/M Pleistocene/M Plexiglas/MS Pliny/M Pliocene/SM Plutarch/M Pluto/M Plymouth/M Pm/M Po/M Pocahontas/M Pocono/SM Poconos/M Podgorica/M Podhoretz/M Podunk/M Poe/M Pogo/M Poincare/M Poiret/M Poirot/M Poisson/M Poitier/M Pokemon/M Pol/MY Poland/M Polanski/M Polaris/M Polaroid/MS Pole/SM Polish/M Politburo/M Polk/M Pollard/M Pollock/M Pollux/M Polly/M Pollyanna/M Polo/M Poltava/M Polyhymnia/M Polynesia/M Polynesian/MS Polyphemus/M Pomerania/M Pomeranian/M Pomona/M Pompadour/M Pompeian Pompeii/M Pompey/M Ponce/M Pontchartrain/M Pontiac/M Pontianak/M Pooh/M Poole/M Poona/M Pope/M Popeye/M Popocatepetl/M Popper/M Poppins/M Popsicle/M Porfirio/M Porrima/M Porsche/M Port/MR Porter/M Portia/M Portland/M Porto/M Portsmouth/M Portugal/M Portuguese/M Poseidon/M Post/M Potemkin/M Potomac/M Potsdam/M Pottawatomie/M Potter/M Potts/M Pound/M Poussin/M Powell/M PowerPC/M PowerPoint/M Powers/M Powhatan/M Poznan/M Pr/M Prada/M Prado/M Praetorian/M Prague/M Praia/M Prakrit/M Pratchett/M Pratt/M Pravda/M Praxiteles/M Preakness/M Precambrian/M Preminger/M Premyslid/M Prensa/M Prentice/M Pres Presbyterian/SM Presbyterianism/MS Prescott/M Presley/M Preston/M Pretoria/M Priam/M Pribilof/M Price/M Priestley/M Prince/M Princeton/M Principe/M Priscilla/M Prius/M Private Procrustean/M Procrustes/M Procter/M Procyon/M Prof Prohibition Prokofiev/M Promethean/M Prometheus/M Prophets Proserpina/M Proserpine/M Protagoras/M Proterozoic/M Protestant/MS Protestantism/SM Proteus/M Proudhon/M Proust/M Provencal/MS Provence/M Proverbs Providence/SM Provo/M Prozac/MS Prudence/M Prudential/M Pruitt/M Prussia/M Prussian/MS Prut/M Pryor/M Psalms/M Psalter/MS Psyche/M Pt/M Ptah/M Ptolemaic/M Ptolemy/SM Pu/M Puccini/M Puck/M Puckett/M Puebla/M Pueblo/M Puerto Puget/M Pugh/M Pulaski/M Pulitzer/M Pullman/MS Punch/M Punic/M Punjab/M Punjabi/M Purana/M Purcell/M Purdue/M Purim/MS Purina/M Puritan/M Puritanism/MS Purus/M Pusan/M Pusey/M Pushkin/M Pushtu/M Putin/M Putnam/M Puzo/M Pvt Pygmalion/M Pygmy/SM Pyle/M Pym/M Pynchon/M Pyongyang/M Pyotr/M Pyrenees/M Pyrex/MS Pyrrhic/M Pythagoras/M Pythagorean/M Pythias/M Python/M Q QA QB QC QED QM QWERTY Qaddafi/M Qantas/M Qatar/M Qatari/MS Qingdao/M Qiqihar/M Qom/M Quaalude/M Quaker/MS Quakerism/SM Quaoar/M Quasimodo/M Quaternary/M Quayle/M Que Quebec/M Quebecois/M Quechua/M Queen/MS Queens/M Queensland/M Quentin/M Quetzalcoatl/M Quezon/M Quincy/M Quinn/M Quintilian/M Quinton/M Quirinal/M Quisling/M Quito/M Quixote/M Quixotism/M Qumran/M Quonset/M R/M RAF/M RAM/SM RBI RC RCA/M RCMP RD RDA REIT REM/SM RF RFC/S RFD RI RIF RIP RISC RN/M RNA/M ROFL ROM/M ROTC/M RP RR RSFSR RSI RSV RSVP RTFM RV/SM Ra/M Rabat/M Rabelais/M Rabelaisian/M Rabin/M Rachael/M Rachel/M Rachelle/M Rachmaninoff/M Racine/M Radcliffe/M Rae/M Rafael/M Raffles/M Ragnarok/M Rainier/M Raleigh/M Ralph/M Rama/M Ramada/M Ramadan/MS Ramakrishna/M Ramanujan/M Ramayana/M Rambo/M Ramirez/M Ramiro/M Ramon/M Ramona/M Ramos/M Ramsay/M Ramses/M Ramsey/M Rand/M Randal/M Randall/M Randell/M Randi/M Randolph/M Randy/M Rangoon/M Rankin/M Rankine/M Raoul/M Raphael/M Rappaport/M Rapunzel/M Raquel/M Rasalgethi/M Rasalhague/M Rasmussen/M Rasputin/M Rasta Rastaban/M Rastafarian/MS Rastafarianism Rather/M Ratliff/M Raul/M Ravel/M Rawalpindi/M Ray/M RayBan/M Rayburn/M Rayleigh/M Raymond/M Raymundo/M Rb/M Rd Re/M Reading/M Reagan/M Reaganomics/M Realtor/M Reasoner/M Reba/M Rebekah/M Recife/M Reconstruction/M Redeemer/M Redford/M Redgrave/M Redmond/M Reebok/M Reed/M Reese/M Reeves/M Reformation/MS Refugio/M Reggie/M Regina/M Reginae/M Reginald/M Regor/M Regulus/M Rehnquist/M Reich/M Reichstag's Reid/M Reilly/M Reinaldo/M Reinhardt/M Reinhold/M Remarque/M Rembrandt/M Remington/M Remus/M Rena/M Renaissance/SM Renascence Renault/M Rene/M Renee/M Reno/M Renoir/M Rep Representative Republican/SM Republicanism Requiem/MS Resistance Restoration/M Resurrection Reuben/M Reunion/M Reuters/M Reuther/M Rev Reva/M Revelation/SM Revelations/M Revere/M Reverend/M Revlon/M Rex/M Reyes/M Reykjavik/M Reyna/M Reynaldo/M Reynolds/M Rf/M Rh/M Rhea/M Rhee/M Rheingau/M Rhenish/M Rhiannon/M Rhine/M Rhineland/M Rhoda/M Rhode/S Rhodes/M Rhodesia/M Rhodesian Rhonda/M Rhone/M Ribbentrop/M Ricardo/M Rice/M Rich/M Richard/MS Richards/M Richardson/M Richelieu/M Richie/M Richmond/M Richter/M Richthofen/M Rick/M Rickenbacker/M Rickey/M Rickie/M Rickover/M Ricky/M Rico/M Riddle/M Ride/M Riefenstahl/M Riel/M Riemann/M Riesling/MS Riga/M Rigel/M Riggs/M Right Rigoberto/M Rigoletto/M Riley/M Rilke/M Rimbaud/M Ringling/M Ringo/M Rio/SM Rios/M Ripley/M Risorgimento/M Rita/M Ritalin/M Ritz/M Rivas/M Rivera/M Rivers/M Riverside Riviera/MS Riyadh/M Rizal/M Rn/M Roach/M Roanoke/M Rob/M Robbie/M Robbin/MS Robbins/M Robby/M Roberson/M Robert/MS Roberta/M Roberto/M Roberts/M Robertson/M Robeson/M Robespierre/M Robin/M Robinson/M Robitussin/M Robles/M Robson/M Robt/M Robyn/M Rocco/M Rocha/M Rochambeau/M Roche/M Rochelle/M Rochester/M Rock/M Rockefeller/M Rockford/M Rockies/M Rockne/M Rockwell/M Rocky/SM Rod/M Roddenberry/M Roderick/M Rodger/MS Rodgers/M Rodin/M Rodney/M Rodolfo/M Rodrick/M Rodrigo/M Rodriguez/M Rodriquez/M Roeg/M Roentgen Rogelio/M Roger/MS Rogers/M Roget/M Rojas/M Roku/M Rolaids/M Roland/M Rolando/M Rolex/M Rolland/M Rollerblade/M Rollins/M Rolodex/M Rolvaag/M Rom Roman/MS Romanesque/MS Romania/M Romanian/MS Romano/M Romanov/M Romans/M Romansh/M Romanticism Romany/SM Rome/SM Romeo/M Romero/M Rommel/M Romney/M Romulus/M Ron/M Ronald/M Ronda/M Ronnie/M Ronny/M Ronstadt/M Rontgen Rooney/M Roosevelt/M Root/M Roquefort/SM Rorschach/M Rory/M Rosa/M Rosales/M Rosalie/M Rosalind/M Rosalinda/M Rosalyn/M Rosanna/M Rosanne/M Rosario/M Roscoe/M Rose/M Roseann/M Roseau/M Rosecrans/M Rosella/M Rosemarie/M Rosemary/M Rosenberg/M Rosendo/M Rosenzweig/M Rosetta/M Rosicrucian/M Rosie/M Roslyn/M Ross/M Rossetti/M Rossini/M Rostand/M Rostov/M Rostropovich/M Roswell/M Rotarian/M Roth/M Rothko/M Rothschild/M Rotterdam/M Rottweiler/M Rouault/M Rourke/M Rousseau/M Rove/RM Rover/M Rowe/M Rowena/M Rowland/M Rowling/M Roxanne/M Roxie/M Roxy/M Roy/M Royal/M Royce/M Rozelle/M Rte Ru/MH Rubaiyat/M Rubbermaid/M Ruben/SM Rubens/M Rubicon/MS Rubik/M Rubin/M Rubinstein/M Ruby/M Ruchbah/M Rudolf/M Rudolph/M Rudy/M Rudyard/M Rufus/M Ruhr/M Ruiz/M Rukeyser/M Rumpelstiltskin/M Rumsfeld/M Runnymede/M Runyon/M Rupert/M Rush/M Rushdie/M Rushmore/M Ruskin/M Russ/M Russel/M Russell/M Russia/M Russian/SM Russo/M Rustbelt/M Rusty/M Rutan/M Rutgers/M Ruth/M Rutherford/M Ruthie/M Rutledge/M Rwanda/MS Rwandan/SM Rwy Rx Ry Ryan/M Rydberg/M Ryder/M Ryukyu/M S/MN SA SAC SALT/M SAM/M SAP/M SARS/M SASE SAT SBA SC/M SCSI/M SD SDI SE/M SEATO SEC/M SF SGML/M SIDS/M SJ SK SLR SO/S SOB/M SOP/M SOS/M SOSes SPCA SPF SQL SRO SS SSA SSE/M SSS SST SSW/M ST STD STOL SUSE/M SUV SVN/M SW/M SWAK SWAT Saab/M Saar/M Saarinen/M Saatchi/M Sabbath/M Sabbaths Sabik/M Sabin/M Sabina/M Sabine/M Sabre/M Sabrina/M Sacajawea/M Sacco/M Sachs/M Sacramento/M Sadat/M Saddam/M Sadducee/M Sade/M Sadie/M Sadr/M Safavid/M Safeway/M Sagan/M Saginaw/M Sagittarius/MS Sahara/M Saharan/M Sahel/M Saigon/M Saiph/M Sakai/M Sakha/M Sakhalin/M Sakharov/M Saki/M Saks/M Sal/MY Saladin/M Salado/M Salamis/M Salas/M Salazar/M Salem/M Salerno/M Salinas/M Salinger/M Salisbury/M Salish/M Salk/M Sallie/M Sallust/M Sally/M Salome/M Salonika/M Salton/M Salvador/M Salvadoran/SM Salvadorean/MS Salvadorian/MS Salvatore/M Salween/M Salyut/M Sam/M Samantha/M Samar/M Samara/M Samaritan/MS Samarkand/M Sammie/M Sammy/M Samoa/M Samoan/SM Samoset/M Samoyed/M Sampson/M Samson/M Samsonite/M Samsung/M Samuel/M Samuelson/M San'a San/M Sana/M Sanchez/M Sancho/M Sand/ZM Sandburg/M Sanders/M Sandinista/M Sandoval/M Sandra/M Sandy/M Sanford/M Sanforized/M Sang/MR Sanger/M Sanhedrin/M Sanka/M Sankara/M Sanskrit/M Santa/M Santana/M Santayana/M Santeria/M Santiago/M Santos/M Sappho/M Sapporo/M Sara/M Saracen/MS Saragossa/M Sarah/M Sarajevo/M Saran/M Sarasota/M Saratov/M Sarawak/M Sardinia/M Sargasso/M Sargent/M Sargon/M Sarnoff/M Saroyan/M Sarto/M Sartre/M Sasha/M Sask Saskatchewan/M Saskatoon/M Sasquatch/MS Sassanian/M Sassoon/M Sat/M Satan/M Satanism/M Satanist/M Saturday/MS Saturn/M Saturnalia/M Saudi/MS Saul/M Saunders/M Saundra/M Saussure/M Sauternes Savage/M Savannah/M Savior/M Savonarola/M Savoy/M Savoyard/M Sawyer/M Saxon/MS Saxony/M Sayers/M Sb/M Sc/M Scala/M Scan Scandinavia/M Scandinavian/MS Scaramouch/M Scarborough/M Scarlatti/M Scheat/M Schedar/M Scheherazade/M Schelling/M Schenectady/M Schiaparelli/M Schick/M Schiller/M Schindler/M Schlesinger/M Schliemann/M Schlitz/M Schmidt/M Schnabel/M Schnauzer/M Schneider/M Schoenberg/M Schopenhauer/M Schrieffer/M Schrodinger/M Schroeder/M Schubert/M Schultz/M Schulz/M Schumann/M Schumpeter/M Schuyler/M Schuylkill/M Schwartz/M Schwarzenegger/M Schwarzkopf/M Schweitzer/M Schweppes/M Schwinger/M Schwinn/M Scientologist/SM Scientology/M Scipio/M Scopes/M Scorpio/SM Scorpius/M Scorsese/M Scot/SM Scotch/MS Scotchman/M Scotchmen/M Scotchwoman/M Scotchwomen/M Scotia/M Scotland/M Scotsman/M Scotsmen/M Scotswoman/M Scotswomen/M Scott/M Scottie/SM Scottish/M Scottsdale/M Scrabble/MS Scranton/M Scriabin/M Scribner/M Scripture/SM Scrooge/M Scruggs/M Scud/M Sculley/M Scylla/M Scythia/M Scythian/M Se/MH Seaborg/M Seagram/M Sean/M Sears/M Seattle/M Sebastian/M Sec Seconal/M Secretariat/M Secretary Seder/MS Sedna/M Seebeck/M Seeger/M Sega/M Segovia/M Segre/M Segundo/M Seiko/M Seine/M Seinfeld/M Sejong/M Selassie/M Selectric/M Selena/M Seleucid/M Seleucus/M Selim/M Seljuk/M Selkirk/M Sellers/M Selma/M Selznick/M Semarang/M Seminole/MS Semiramis/M Semite/MS Semitic/SM Semtex/M Senate/MS Sendai/M Seneca/MS Senegal/M Senegalese/M Senghor/M Senior/M Sennacherib/M Sennett/M Sensurround/M Seoul/M Sep Sephardi/M Sepoy/M Sept/M September/MS Septuagint/MS Sequoya/M Serb/SM Serbia/M Serbian/MS Serena/M Serengeti/M Sergei/M Sergio/M Serpens/M Serra/M Serrano/M Set/M Seth/M Seton/M Seurat/M Seuss/M Sevastopol/M Severn/M Severus/M Seville/M Sevres/M Seward/M Sextans/M Sexton/M Seychelles/M Seyfert/M Seymour/M Sgt Shackleton/M Shaffer/M Shaka/M Shaker Shakespeare/M Shakespearean/M Shana/M Shane/M Shanghai/M Shankara/M Shanna/M Shannon/M Shantung/M Shapiro/M Shari'a/M Shari/M Sharif/M Sharlene/M Sharon/M Sharp/M Sharpe/M Sharron/M Shasta/M Shaula/M Shaun/M Shauna/M Shavian/M Shavuot/M Shaw/M Shawn/M Shawna/M Shawnee/SM Shcharansky/M Shea/M Sheba/M Shebeli/M Sheena/M Sheetrock/M Sheffield/M Sheila/M Shelby/M Sheldon/M Shelia/M Shell/M Shelley/M Shelly/M Shelton/M Shenandoah/M Shenyang/M Sheol/M Shepard/M Shepherd/M Sheppard/M Sheratan/M Sheraton/M Sheree/M Sheri/M Sheridan/M Sherlock/M Sherman/M Sherpa/M Sherri/M Sherrie/M Sherry/M Sherwood/M Sheryl/M Shetland/SM Shetlands/M Shevardnadze/M Shevat/M Shi'ite/M Shields/M Shiite/MS Shijiazhuang/M Shikoku/M Shillong/M Shiloh/M Shinto/MS Shintoism/MS Shintoist/MS Shiraz/M Shirley/M Shiva/M Shockley/M Short/M Shorthorn/M Shoshone/SM Shostakovitch/M Shrek/M Shreveport/M Shriner/M Shropshire/M Shula/M Shylock/M Shylockian/M Si/M Siam/M Siamese/M Sibelius/M Siberia/M Siberian/MS Sibyl/M Sicilian/SM Sicily/M Sid/M Siddhartha/M Sidney/M Siegfried/M Siemens/M Sierpinski/M Sierras Sigismund/M Sigmund/M Sigurd/M Sihanouk/M Sikh/M Sikhism Sikhs Sikkim/M Sikkimese/M Sikorsky/M Silas/M Silesia/M Silurian/SM Silva/M Silvia/M Simenon/M Simmental/M Simmons/M Simon/M Simone/M Simpson/SM Simpsons/M Sims/M Sinai/M Sinatra/M Sinbad/M Sinclair/M Sindbad/M Sindhi/M Singapore/M Singaporean/SM Singer/M Singh/M Singleton/M Sinhalese/M Sinkiang/M Sioux/M Sir/SM Sirius/M Sistine/M Sisyphean/M Sisyphus/M Siva/M Sivan/M Sjaelland/M Skinner/M Skippy/M Skopje/M Skye/M Skylab/M Skype/M Slackware/M Slashdot/M Slater/M Slav/SM Slavic/M Slavonic/M Slinky/M Sloan/M Sloane/M Slocum/M Slovak/SM Slovakia/M Slovakian Slovene/SM Slovenia/M Slovenian/MS Slurpee/M Sm/M Small/M Smetana/M Smirnoff/M Smith/M Smithson/M Smithsonian/M Smokey/M Smolensk/M Smollett/M Smuts/M Smyrna Sn/M Snake/M Snapple/M Snead/M Snell/M Snickers/M Snider/M Snoopy/M Snow/M Snowbelt/M Snyder/M Soave/M Soc Socorro/M Socrates/M Socratic/M Soddy/M Sodom/M Sofia/M Soho/M Sol/M Solis/M Solomon/M Solon/M Solzhenitsyn/M Somali/SM Somalia/M Somalian/MS Somme/M Somoza/M Son/M Sondheim/M Sondra/M Songhai/M Songhua/M Sonia/M Sonja/M Sonny/M Sonora/M Sontag/M Sony/M Sonya/M Sophia/M Sophie/M Sophoclean/M Sophocles/M Sopwith/M Sorbonne/M Sosa/M Soto/M Souphanouvong/M Sourceforge/M Sousa/M South/M Southampton/M Southeast/MS Southerner/SM Southey/M Souths Southwest/MS Soviet/M Soweto/M Soyinka/M Soyuz/M Sp Spaatz/M Spackle/M Spahn/M Spain/M Spam/M Span Spanglish Spaniard/SM Spanish/M Sparks/M Sparta/M Spartacus/M Spartan/MS Spears/M Speer/M Spence/RM Spencer/M Spencerian/M Spengler/M Spenglerian/M Spenser/M Spenserian/M Sperry/M Sphinx/M Spica/M Spielberg/M Spillane/M Spinoza/M Spinx/M Spiro/M Spirograph/M Spitsbergen/M Spitz/M Spock/M Spokane/M Springfield/M Springsteen/M Sprint/M Sprite/M Sputnik/M Sq Squanto/M Squibb/M Sr/M Srinagar/M Srivijaya/M St Sta Stacey/M Staci/M Stacie/M Stacy/M Stael/M Stafford/M StairMaster/M Stalin/M Stalingrad/M Stalinist/M Stallone/M Stamford/M Stan/M Standish/M Stanford/M Stanislavsky/M Stanley/M Stanton/M Staples/M Starbucks/M Stark/M Starkey/M Starr/M Staten/M States Staubach/M Ste Steadicam/M Steele/M Stefan/M Stefanie/M Stein/MR Steinbeck/M Steinem/M Steiner/M Steinmetz/M Steinway/M Stella/M Stendhal/M Stengel/M Stephan/M Stephanie/M Stephen/MS Stephens/M Stephenson/M Sterling/M Stern/M Sterne/M Sterno/M Stetson/M Steuben/M Steve/M Steven/MS Stevens/M Stevenson/M Stevie/M Stewart/M Stieglitz/M Stilton/SM Stimson/M Stine/M Stirling/M Stockhausen/M Stockholm/M Stockton/M Stoic/SM Stoicism/MS Stokes/M Stolichnaya/M Stolypin/M Stone/M Stonehenge/M Stoppard/M Stout/M Stowe/M Strabo/M Stradivari Stradivarius/M Strasbourg/M Strauss/M Stravinsky/M Streisand/M Strickland/M Strindberg/M Stromboli/M Strong/M Stu/M Stuart/MS Studebaker/M Stuttgart/M Stuyvesant/M Stygian/M Styrofoam/SM Styron/M Styx/M Suarez/M Subaru/M Sucre/M Sucrets/M Sudan/M Sudanese/M Sudetenland/M Sudoku/M Sudra/M Sue/M Suetonius/M Suez/M Suffolk/M Sufi/M Sufism/M Suharto/M Sui/M Sukarno/M Sukkot Sulawesi/M Suleiman/M Sulla/M Sullivan/M Sumatra/M Sumatran/SM Sumeria/M Sumerian/SM Summer/MS Summers/M Sumner/M Sumter/M Sun/SM Sunbeam/M Sunbelt/M Sundanese/M Sundas/M Sunday/MS Sung/M Sunkist/M Sunni/SM Sunnite/MS Sunnyvale/M Superbowl/M Superfund/M Superglue/M Superior/M Superman/M Supt Surabaya/M Surat/M Suriname/M Surinamese Surya/M Susan/M Susana/M Susanna/M Susanne/M Susie/M Susquehanna/M Sussex/M Sutherland/M Sutton/M Suva/M Suwanee/M Suzanne/M Suzette/M Suzhou/M Suzuki/M Suzy/M Svalbard/M Sven/M Svengali/M Sverdlovsk Swahili/SM Swammerdam/M Swanee/M Swansea/M Swanson/M Swazi/SM Swaziland/M Swed/N Swede/SM Sweden/M Swedenborg/M Swedish/M Sweeney/M Sweet/M Swift/M Swinburne/M Swiss/MS Swissair/M Switz Switzerland/M Sybil/M Sydney/M Sykes/M Sylvester/M Sylvia/M Sylvie/M Synge/M Syracuse/M Syria/M Syriac/M Syrian/MS Szilard/M Szymborska/M T'ang/M T/MDG TA TARP TB/M TBA TD TDD TEFL TELNET/S TELNETTed TELNETTing TESL TESOL TGIF THC TKO/M TLC/M TM TN TNT/M TOEFL TQM TV/SM TVA TWA/M TWX TX Ta/M Tabasco/SM Tabatha/M Tabernacle/MS Tabitha/M Tabriz/MS Tacitus/M Tacoma/M Tad/M Tadzhik/M Taegu/M Taejon/M Taft/M Tagalog/SM Tagore/M Tagus/M Tahiti/M Tahitian/MS Tahoe/M Taichung/M Tainan Taine/M Taipei/M Taiping/M Taiwan/M Taiwanese/M Taiyuan/M Tajikistan/M Taklamakan/M Talbot/M Taliban/M Taliesin/M Tallahassee/M Tallchief/M Talley/M Talleyrand/M Tallinn/M Talmud/MS Talmudic Talmudist Tamara/M Tameka/M Tamera/M Tamerlane/M Tami/M Tamika/M Tamil/MS Tammany/M Tammi/M Tammie/M Tammuz/M Tammy/M Tampa/M Tampax/M Tamra/M Tamworth/M Tancred/M Taney/M Tanganyika/M Tangier/MS Tangshan/M Tania/M Tanisha/M Tanner/M Tannhauser/M Tantalus/M Tanya/M Tanzania/M Tanzanian/SM Tao/M Taoism/MS Taoist/MS Tara/M Tarantino/M Tarawa/M Tarazed/M Tarbell/M Target/M Tarim/M Tarkenton/M Tarkington/M Tartary/M Tartuffe/M Tarzan/M Tasha/M Tashkent/M Tasman/M Tasmania/M Tasmanian/M Tass/M Tatar/MS Tate/M Tatum/M Taurus/MS Tawney/M Taylor/M Tb/M Tbilisi/M Tc/M Tchaikovsky/M Te/M TeX TeXes Teasdale/M Technicolor/M Tecumseh/M Ted/M Teddy/M Teflon/MS Tegucigalpa/M Tehran TelePrompTer TelePrompter/M Telemachus/M Telemann/M Teletype Tell/MR Teller/M Telugu/M Tempe Templar/M Tenn/M Tennessean/SM Tennessee/M Tennyson/M Tenochtitlan/M Teotihuacan/M Terence/M Teresa/M Tereshkova/M Teri/M Terkel/M Terpsichore/M Terr/M Terra/M Terran/M Terrance/M Terrell/M Terrence/M Terri/M Terrie/M Terry/M Tertiary/M Tesla/M Tess/M Tessa/M Tessie/M Tet/M Tethys/M Tetons/M Teuton/MS Teutonic/M Tevet/M Tex/M Texaco/M Texan/MS Texas/M Th/M Thackeray/M Thad/M Thaddeus/M Thai/SM Thailand/M Thales/M Thalia/M Thames/M Thanh/M Thanksgiving/MS Thant/M Thar/M Tharp/M Thatcher/M Thea/M Thebes/M Theiler/M Thelma/M Themistocles/M Theocritus/M Theodora/M Theodore/M Theodoric/M Theodosius/M Theosophy/M Theravada/M Theresa/M Therese/M Thermopylae/M Thermos Theron/M Theseus/M Thespian/M Thespis/M Thessalonian/SM Thessaloniki/M Thessaly/M Thieu/M Thimbu/M Thimphu Thomas/M Thomism/M Thomistic/M Thompson/M Thomson/M Thor/M Thorazine/M Thoreau/M Thornton/M Thoroughbred/M Thorpe/M Thoth/M Thrace/M Thracian/M Thu Thucydides/M Thule/M Thunderbird/M Thur/S Thurber/M Thurman/M Thurmond/M Thursday/SM Thutmose/M Ti/M Tia/M Tianjin/M Tiber/M Tiberius/M Tibet/M Tibetan/MS Ticketmaster/M Ticonderoga/M Tide/M Tienanmen/M Tiffany/M Tigris/M Tijuana/M Tillich/M Tillman/M Tilsit/M Tim/M Timbuktu/M Timex/M Timmy/M Timon/M Timor/M Timothy/M Timur/M Timurid/M Tina/M Ting/M Tinkerbell/M Tinkertoy/M Tinseltown/M Tintoretto/M Tippecanoe/M Tipperary/M Tirane Tiresias/M Tirol/M Tirolean Tisha/M Tishri/M Titan/SM Titania/M Titanic/M Titian/M Titicaca/M Tito/M Titus/M Tl/M Tlaloc/M Tlingit/M Tm/M Tobago/M Tobit/M Toby/M Tocantins/M Tocqueville/M Tod/M Todd/M Togo/M Togolese/M Tojo/M Tokay/M Tokugawa/M Tokyo/M Tokyoite Toledo/MS Tolkien/M Tolstoy/M Toltec/M Tolyatti/M Tom/M Tomas/M Tombaugh/M Tomlin/M Tommie/M Tommy/M Tompkins/M Tomsk/M Tonga/M Tongan/MS Toni/M Tonia/M Tonto/M Tony/M Tonya/M Topeka/M Topsy/M Torah/M Torahs Toronto/M Torquemada/M Torrance/M Torrens/M Torres/M Torricelli/M Tortola/M Tortuga/M Torvalds/M Tory/SM Tosca/M Toscanini/M Toshiba/M Toto/M Toulouse/M Townes/M Townsend/M Toynbee/M Toyoda/M Toyota/M Tracey/M Traci/M Tracie/M Tracy/M Trafalgar/M Trailways/M Trajan/M Tran/M Transcaucasia/M Transvaal/M Transylvania/M Transylvanian/M Trappist/SM Travis/M Travolta/M Treasury/SM Treblinka/M Trekkie/M Trent/M Trenton/M Trevelyan/M Trevino/M Trevor/M Trey/M Triangulum/M Triassic/M Tricia/M Trident/M Trieste/M Trimurti/M Trina/M Trinidad/M Trinidadian/MS Trinity/SM Tripitaka/M Tripoli/M Trippe/M Trisha/M Tristan/M Triton/M Trobriand/M Troilus/M Trojan/MS Trollope/M Trondheim/M Tropicana/M Trotsky/M Troy/M Troyes Truckee/M Trudeau/M Trudy/M Truffaut/M Trujillo/M Truman/M Trumbull/M Trump/M Truth/M Tsimshian/M Tsiolkovsky/M Tsitsihar/M Tsongkhapa/M Tswana/M Tu/M Tuamotu/M Tuareg/M Tubman/M Tucker/M Tucson/M Tucuman/M Tudor/SM Tue/S Tues/M Tuesday/MS Tulane/M Tull/M Tulsa/M Tulsidas/M Tums/M Tungus/M Tunguska/M Tunis/M Tunisia/M Tunisian/MS Tunney/M Tupi/M Tupperware/M Tupungato/M Turgenev/M Turin/M Turing/M Turk/SM Turkestan/M Turkey/M Turkic/MS Turkish/M Turkmenistan/M Turner/M Turpin/M Tuscaloosa/M Tuscan/M Tuscany/M Tuscarora/MS Tuscon/M Tuskegee/M Tussaud/M Tut/M Tutankhamen/M Tutsi/M Tutu/M Tuvalu/M Tuvaluan Twain/M Tweed/M Tweedledee/M Tweedledum/M Twila/M Twinkies/M Twitter/M Twizzlers/M Twp Ty/M Tycho/M Tylenol/M Tyler/M Tyndale/M Tyndall/M Tyre/M Tyree/M Tyrolean Tyrone/M Tyson/M U/M UAR UAW UBS/M UCLA/M UFO/SM UHF/M UK/M UL UN/M UNESCO/M UNICEF/M UNIX/M UPC UPI/M UPS/M URL/S US/M USA/M USAF USB USCG USDA/M USIA USMC USN USO USP USPS USS USSR/M UT/M UTC UV/M Ubangi/M Ubuntu/M Ucayali/M Uccello/M Udall/M Ufa/M Uganda/M Ugandan/MS Uighur/M Ujungpandang/M Ukraine/M Ukrainian/SM Ulster/M Ultrasuede/M Ulyanovsk/M Ulysses/M Umbriel/M Underwood/M Ungava/M Unicode/M Unilever/M Union/SM Unionist Uniroyal/M Unitarian/MS Unitarianism/MS Unitas/M Unix/S Unukalhai/M Upanishads/M Updike/M Upjohn/M Upton/M Ur/M Ural/SM Urals/M Urania/M Uranus/M Urban/M Urdu/M Urey/M Uriah/M Uriel/M Uris/M Urquhart/M Ursa/M Ursula/M Ursuline/M Uruguay/M Uruguayan/MS Urumqi/M Usenet/MS Ustinov/M Utah/M Utahan/MS Ute/SM Utopia/SM Utopian/SM Utrecht/M Utrillo/M Uzbek/M Uzbekistan/M Uzi/SM V/M VA VAT/M VAX VAXes VCR/M VD/M VDT VDU VF VFW/M VG VGA VHF/M VHS VI/M VIP/SM VISTA VJ VLF/M VOA VP VT VTOL Va/M Vader/M Vaduz/M Val/M Valarie/M Valdez/M Valencia/SM Valenti/M Valentin/M Valentine/M Valentino/M Valenzuela/M Valeria/M Valerian/M Valerie/M Valery/M Valhalla/M Valium/MS Valkyrie/SM Vallejo Valletta/M Valois/M Valparaiso/M Valvoline/M Van/M Vance/M Vancouver/M Vandal/MS Vanderbilt/M Vandyke/M Vanessa/M Vang/M Vanuatu/M Vanzetti/M Varanasi/M Varese/M Vargas/M Vaseline/SM Vasquez/M Vassar/M Vatican/M Vauban/M Vaughan/M Vaughn/M Vazquez/M Veblen/M Veda/SM Vedanta/M Vega/SM Vegas/M Vegemite/M Vela/M Velasquez/M Velazquez/M Velcro/MS Velez/M Velma/M Velveeta/M Venetian/SM Venezuela/M Venezuelan/SM Venice/M Venn/M Ventolin/M Venus/MS Venusian/M Vera/M Veracruz/M Verde/M Verdi/M Verdun/M Verizon/M Verlaine/M Vermeer/M Vermont/ZMR Vermonter/M Vern/M Verna/M Verne/M Vernon/M Verona/M Veronese/M Veronica/M Versailles/M Vesalius/M Vespasian/M Vespucci/M Vesta/M Vesuvius/M Viacom/M Viagra/M Vic/M Vicente/M Vichy/M Vicki/M Vickie/M Vicksburg/M Vicky/M Victor/M Victoria/M Victorian/MS Victorianism Victrola/M Vidal/M Vienna/M Viennese/M Vientiane/M Vietcong/M Vietminh/M Vietnam/M Vietnamese/M Vijayanagar/M Vijayawada/M Viking/MS Vila/M Villa/M Villarreal/M Villon/M Vilma/M Vilnius/M Vilyui/M Vince/M Vincent/M Vindemiatrix/M Vinson/M Viola/M Violet/M Virgie/M Virgil/M Virginia/M Virginian/SM Virgo/SM Visa/M Visayans/M Vishnu/M Visigoth/M Visigoths Vistula/M Vitim/M Vito/M Vitus/M Vivaldi/M Vivekananda/M Vivian/M Vivienne/M Vlad/M Vladimir/M Vladivostok/M Vlaminck/M Vlasic/M Vogue/M Volcker/M Voldemort/M Volga/M Volgograd/M Volkswagen/M Volstead/M Volta/M Voltaire/M Volvo/M Vonda/M Vonnegut/M Voronezh/M Vorster/M Voyager/M Vt Vuitton/M Vulcan/M Vulg Vulgate/SM W/MDT WA WAC WASP/M WATS/M WC WHO/M WI WMD WNW/M WP WSW/M WTO WV WW WWI WWII WWW/M WY WYSIWYG Wabash/M Wac Waco/M Wade/M Wagner/M Wagnerian/M Wahhabi/M Waikiki/M Waite/M Wake/M Waksman/M Wald/MN Waldemar/M Walden/M Waldensian/M Waldheim/M Waldo/M Waldorf/M Wales/M Walesa/M Walgreen/M Walker/M Walkman/M Wall/SMR Wallace/M Wallenstein/M Waller/M Wallis/M Walloon/M Walls/M Walmart/M Walpole/M Walpurgisnacht/M Walsh/M Walt/MRZ Walter/M Walters/M Walton/M Wanamaker/M Wanda/M Wang/M Wankel/M Ward/M Ware/MG Warhol/M Waring/M Warner/M Warren/M Warsaw/M Warwick/M Wasatch/M Wash/M Washington/M Washingtonian/MS Wassermann/M Waterbury/M Waterford/M Watergate/M Waterloo/MS Waters/M Watkins/M Watson/M Watt/SM Watteau/M Watts/M Watusi/M Waugh/M Wave Wayne/M Weaver/M Web/MR Webb/M Weber/M Webern/M Webster/MS Wed/M Weddell/M Wedgwood/M Wednesday/MS Weeks/M Wehrmacht/M Wei/M Weierstrass/M Weill/M Weinberg/M Weiss/M Weissmuller/M Weizmann/M Weldon/M Welland/M Weller/M Welles/M Wellington/SM Wells/M Welsh/M Welshman/M Welshmen/M Welshwoman Wendell/M Wendi/M Wendy/M Wesak/M Wesley/M Wesleyan/M Wessex/M Wesson/M West/SM Western/MRS Westinghouse/M Westminster/M Weston/M Westphalia/M Weyden/M Wezen/M Wharton/M Wheaties/M Wheatstone/M Wheeler/M Wheeling/M Whig/SM Whipple/M Whirlpool/M Whistler/M Whitaker/M White/SM Whitefield/M Whitehall/M Whitehead/M Whitehorse/M Whiteley/M Whitfield/M Whitley/M Whitman/M Whitney/M Whitsunday/MS Whittier/M WiFi Wicca/M Wichita/M Wiemar/M Wiesel/M Wiesenthal/M Wiggins/M Wigner/M Wii/M Wikileaks Wikipedia/M Wilberforce/M Wilbert/M Wilbur/M Wilburn/M Wilcox/M Wilda/M Wilde/MR Wilder/M Wiles/M Wiley/M Wilford/M Wilfred/M Wilfredo/M Wilhelm/M Wilhelmina/M Wilkerson/M Wilkes/M Wilkins/M Wilkinson/M Will/M Willa/M Willamette/M Willard/M Willemstad/M William/SM Williams/M Williamson/M Willie/M Willis/M Willy/M Wilma/M Wilmer/M Wilmington/M Wilson/M Wilsonian/M Wilton/M Wimbledon/M Wimsey/M Winchell/M Winchester/MS Windbreaker/M Windex/M Windhoek/M Windows/M Windsor/SM Windward/M Winesap/M Winfred/M Winfrey/M Winifred/M Winkle/M Winnebago/M Winnie/M Winnipeg/M Winston/M Winters/M Winthrop/M Wis Wisc Wisconsin/M Wisconsinite/MS Wise/M Witt/M Wittgenstein/M Witwatersrand/M Wm/M Wobegon/M Wodehouse/M Wolf/M Wolfe/M Wolff/M Wolfgang/M Wollongong/M Wollstonecraft/M Wolsey/M Wolverhampton Wonder/M Wonderbra/M Wong/M Wood/SM Woodard/M Woodhull/M Woodrow/M Woods/M Woodstock/M Woodward/M Woolf/M Woolite/M Woolongong/M Woolworth/M Wooster/M Wooten/M Worcester/SM Worcestershire/M Wordsworth/M Workman/M Worms/M Wotan/M Wovoka/M Wozniak/M Wozzeck/M Wrangell/M Wren/M Wright/M Wrigley/M Wroclaw/M Wu/M Wuhan/M Wurlitzer/M Wyatt/M Wycherley/M Wycliffe/M Wyeth/M Wylie/M Wynn/M Wyo Wyoming/M Wyomingite/SM X/M XEmacs/M XL/M XML XS XXL Xanadu/M Xanthippe/M Xavier/M Xe/SM Xenakis/M Xenia/M Xenophon/M Xerox/MS Xerxes/M Xhosa/M Xi'an/M Xian/SM Xiaoping/M Ximenes/M Xingu/M Xiongnu/M Xmas/MS Xochipilli/M Xuzhou/M Y/M YMCA/M YMHA YMMV YT YWCA/M YWHA Yacc/M Yahoo/M Yahtzee/M Yahweh/M Yakima/M Yakut/M Yakutsk/M Yale/M Yalow/M Yalta/M Yalu/M Yamagata/M Yamaha/M Yamoussoukro/M Yang/M Yangon/M Yangtze/M Yank/SM Yankee/SM Yaobang/M Yaounde/M Yaqui/M Yaren Yaroslavl/M Yataro/M Yates/M Yb/M Yeager/M Yeats/M Yekaterinburg/M Yellowknife/M Yellowstone/M Yeltsin/M Yemen/M Yemeni/SM Yemenite Yenisei/M Yerevan/M Yerkes/M Yesenia/M Yevtushenko/M Yggdrasil/M Yiddish/M Ymir/M Yoda/M Yoknapatawpha/M Yoko/M Yokohama/M Yolanda/M Yong/M Yonkers/M York/M Yorkie/M Yorkshire/MS Yorktown/M Yoruba/M Yosemite/M Yossarian/M YouTube/M Young/M Youngstown/M Ypres/M Ypsilanti/M Yuan/M Yucatan/M Yugo/M Yugoslav/MS Yugoslavia/M Yugoslavian/SM Yukon/M Yule/SM Yuletide/MS Yuma/SM Yunnan/M Yuri/M Yves/M Yvette/M Yvonne/M Z/SMNXT Zachariah/M Zachary/M Zachery/M Zagreb/M Zaire/M Zairian Zambezi/M Zambia/M Zambian/SM Zamboni/M Zamenhof/M Zamora/M Zane/M Zanuck/M Zanzibar/M Zapata/M Zaporozhye/M Zapotec/M Zappa/M Zara/M Zarathustra/M Zealand/M Zebedee/M Zechariah/M Zedekiah/M Zedong/M Zeffirelli/M Zeke/M Zelig/M Zelma/M Zen/M Zenger/M Zeno/M Zephaniah/M Zephyrus/M Zest/M Zeus/M Zhdanov Zhengzhou/M Zhivago/M Zhukov/M Zibo/M Ziegfeld/M Ziegler/M Ziggy/M Zimbabwe/M Zimbabwean/SM Zimmerman/M Zinfandel/M Zion/SM Zionism/SM Zionist/SM Ziploc/M Zn/M Zoe/M Zola/M Zollverein/M Zoloft/M Zomba/M Zorn/M Zoroaster/M Zoroastrian/MS Zoroastrianism/SM Zorro/M Zosma/M Zr/M Zsigmondy/M Zubenelgenubi/M Zubeneschamali/M Zukor/M Zulu/SM Zululand Zuni/M Zurich/M Zwingli/M Zworykin/M Zyrtec/M Zyuganov/M Zzz a/S aah aardvark/SM ab/SDY aback abacus/MS abaft abalone/SM abandon/LSDG abandonment/M abase/LGDS abasement/M abash/GLDS abashed/UY abashment/M abate/LGDS abated/U abatement/M abattoir/MS abbe/SM abbess/MS abbey/MS abbot/MS abbr abbrev/S abbreviate/DSGNX abbreviation/M abdicate/GNDSX abdication/M abdomen/SM abdominal abduct/DSG abductee/MS abduction/SM abductor/MS abeam aberrant aberration/MS aberrational abet/S abetted abetting abettor/SM abeyance/M abhor/S abhorred abhorrence/M abhorrent/Y abhorring abidance/M abide/GS abiding/Y ability/IEMS abject/YP abjection/M abjectness/M abjuration/SM abjuratory abjure/ZGDRS abjurer/M ablate/XGNVDS ablation/M ablative/MS ablaze able/UT abler abloom ablution/SM abnegate/GNDS abnegation/M abnormal/Y abnormality/SM aboard abode/MS abolish/GDS abolition/M abolitionism/M abolitionist/SM abominable abominably abominate/DSGNX abomination/M aboriginal/MS aborigine/SM aborning abort/GVDS abortion/MS abortionist/MS abortive/Y abound/DSG about above/M aboveboard abracadabra/M abrade/GDS abrasion/MS abrasive/MYPS abrasiveness/M abreast abridge/DSG abridgment/MS abroad abrogate/XGNDS abrogation/M abrogator/MS abrupt/TPRY abruptness/M abs/M abscess/MDSG abscissa/SM abscission/M abscond/ZGSDR absconder/M abseil/MDSG absence/SM absent/DYSG absentee/MS absenteeism/M absentminded/YP absentmindedness/M absinthe/M absolute/PMYTNS absoluteness/M absolution/M absolutism/M absolutist/MS absolve/DSG absorb/AGDS absorbance absorbency/M absorbent/SM absorbing/Y absorption/M absorptive abstain/DRZGS abstainer/M abstemious/PY abstemiousness/M abstention/MS abstinence/M abstinent abstract/GSPMDY abstracted/YP abstractedness/M abstraction/SM abstractness/MS abstruse/YP abstruseness/M absurd/TPRY absurdist/MS absurdity/SM absurdness/M abundance/SM abundant/Y abuse's abuse/EGVDS abuser/MS abusive/YP abusiveness/M abut/SL abutment/MS abutted abutting abuzz abysmal/Y abyss/MS abyssal ac acacia/MS academe/M academia/M academic/SM academical/Y academician/MS academy/SM acanthus/MS accede/GDS accelerate/GNXDS acceleration/M accelerator/SM accent/MDSG accented/U accentual accentuate/GNDS accentuation/M accept/DSBG acceptability/M acceptableness/M acceptably/U acceptance/SM acceptation/MS accepted/U access/MDSG accessibility/IM accessible/I accessibly/I accession/MDGS accessorize/DSG accessory/SM accident/MS accidental/SMY acclaim/MDGS acclamation/M acclimate/DSGN acclimation/M acclimatization/M acclimatize/DSG acclivity/SM accolade/SM accommodate/XGNDS accommodating/Y accommodation/M accompanied/U accompaniment/MS accompanist/SM accompany/DSG accomplice/SM accomplish/DSLG accomplished/U accomplishment/MS accord/GMDS accordance/M accordant according/Y accordion/MS accordionist/MS accost/GMDS account/MDSBG accountability/M accountable/U accountancy/M accountant/MS accounted/U accounting/M accouter/SGD accouterments/M accredit/SGD accreditation/M accredited/U accretion/MS accrual/MS accrue/GDS acct acculturate/DSGN acculturation/M accumulate/XGNVDS accumulation/M accumulator/MS accuracy/IM accurate/IY accurateness/M accursed/P accursedness/M accusation/MS accusative/MS accusatory accuse/ZGDRS accuser/M accusing/Y accustom/DSG accustomed/U ace/DSMG acerbate/DSG acerbic acerbically acerbity/M acetaminophen/M acetate/MS acetic acetone/M acetonic acetyl acetylene/M ache/DSMG achene/MS achieve/BLZGDRS achievement/SM achiever/M aching/Y achoo/M achromatic achy/TR acid/SMY acidic acidify/GDS acidity/M acidosis/M acidulous acknowledge/DSG acknowledged/U acknowledgment/SM acme/SM acne/M acolyte/MS aconite/MS acorn/MS acoustic/S acoustical/Y acoustics/M acquaint/AGSD acquaintance/SM acquaintanceship/M acquainted/U acquiesce/DSG acquiescence/M acquiescent/Y acquire/ZGBDRSL acquirement/M acquisition/MS acquisitive/YP acquisitiveness/M acquit/S acquittal/MS acquitted acquitting acre/SM acreage/MS acrid/PTRY acridity/M acridness/M acrimonious/YP acrimoniousness/M acrimony/M acrobat/MS acrobatic/S acrobatically acrobatics/M acronym/MS acrophobia/M acropolis/MS across acrostic/SM acrylamide acrylic/MS act's act/ASDGV acting/M actinium/M action/ASM actionable activate/ICANGSD activation/ICAM activator/MS active's active/IKY activeness/M actives activism/M activist/MS activities activity/IM actor/AMS actress/MS actual/Y actuality/SM actualization/M actualize/GDS actuarial actuary/SM actuate/GNDS actuation/M actuator/SM acuity/M acumen/M acupressure/M acupuncture/M acupuncturist/SM acute/PMYTRS acuteness/M acyclovir/M acyl ad/SM adage/MS adagio/MS adamant/MY adapt/BZGVDRS adaptability/M adaptation/MS adapter/M adaption/S add/SDRBZG addend/MS addenda addendum/M adder/M addict/GVMDS addiction/SM addition/SM additional/Y additive/SM addle/GDS address's address/AGDS addressable addressed/U addressee/SM adduce/GDS adenine/M adenoid/SM adenoidal adept/MYPS adeptness/M adequacy/IM adequate/IY adequateness/M adhere/GDS adherence/M adherent/SM adhesion/M adhesive/PSM adhesiveness/M adiabatic adieu/MS adios adipose adj adjacency/M adjacent/Y adjectival/Y adjective/MS adjoin/GDS adjourn/DGLS adjournment/SM adjudge/GDS adjudicate/GNVXDS adjudication/M adjudicator/SM adjudicatory adjunct/MS adjuration/MS adjure/GDS adjust/AGDSL adjustable adjuster/SM adjustment/AMS adjutant/SM adman/M admen admin/S administer/DGS administrate/XDSGNV administration/M administrative/Y administrator/MS admirably admiral/MS admiralty/M admiration/M admire/BZGDRS admirer/M admiring/Y admissibility/IM admissible/I admissibly admission/AM admissions admit/AS admittance/M admitted/Y admitting/A admix/GDS admixture/SM admonish/LDSG admonishment/MS admonition/MS admonitory ado/M adobe/MS adolescence/SM adolescent/SM adopt/AGVDS adoptable adopter/MS adoption/SM adorableness/M adorably adoration/M adore/BZGDRS adorer/M adoring/Y adorn/LGDS adorned/U adornment/MS adrenal/MS adrenalin's adrenaline/M adrenergic adrift adroit/PY adroitness/M adsorb/SDG adsorbent/MS adsorption/SM adulate/DSGN adulation/M adulator/MS adulatory adult/MS adulterant/MS adulterate/GNDS adulterated/U adulteration/M adulterer/SM adulteress/MS adulterous adultery/SM adulthood/M adumbrate/GNDS adumbration/M adv advance/LDSMG advancement/SM advantage/EDSMG advantageous/EY advent/SM adventitious/Y adventure/DRSMZG adventurer/M adventuresome adventuress/MS adventurism adventurist/S adventurous/YP adventurousness/M adverb/SM adverbial/SMY adversarial adversary/SM adverse/PRYT adverseness/M adversity/SM advert/SMDG advertise/LZGDRS advertised/U advertisement/MS advertiser/M advertising/M advertorial/SM advice/M advisability/IM advisable/I advisably advise/LDRSZGB advised/UY advisement/M adviser/M advisory/SM advocacy/M advocate/MGDS advt adware adze/SM aegis/M aerate/DSGN aeration/M aerator/SM aerial/SMY aerialist/MS aerie/MS aerobatic/S aerobatics/M aerobic/S aerobically aerobics/M aerodrome/MS aerodynamic/S aerodynamically aerodynamics/M aerogram/S aeronautic/S aeronautical aeronautics/M aerosol/MS aerospace/M aesthete/MS aesthetic/S aesthetically aestheticism/M aesthetics/M afar affability/M affable affably affair/MS affect's affect/EGDS affectation/SM affected/UY affecting/Y affection/EM affectionate/Y affections afferent affiance/GDS affidavit/SM affiliate's affiliate/EGNDS affiliated/U affiliation/EM affiliations affinity/SM affirm/AGDS affirmation/AMS affirmative/MYS affix/GMDS afflatus/M afflict/GDS affliction/SM affluence/M affluent/Y afford/GDSB affordability affordably afforest/EGSD afforestation/M affray/MS affront/GMDS afghan/MS aficionado/MS afield afire aflame afloat aflutter afoot aforementioned aforesaid aforethought afoul afraid/U afresh aft/RZ afterbirth/M afterbirths afterburner/MS aftercare/M aftereffect/MS afterglow/SM afterimage/MS afterlife/M afterlives aftermarket/MS aftermath/M aftermaths afternoon/MS aftershave/SM aftershock/SM aftertaste/SM afterthought/SM afterward/S afterword/MS again against agape/M agar/M agate/MS agave/M age/DSMGJ ageism/M ageist/SM ageless/YP agelessness/M agency/SM agenda/SM agent/AMS ageratum/M agglomerate/DSMGNX agglomeration/M agglutinate/DSXGN agglutination/M aggrandize/GLDS aggrandizement/M aggravate/GNXDS aggravating/Y aggravation/M aggregate/MGNDSX aggregation/M aggression/M aggressive/PY aggressiveness/M aggressor/SM aggrieve/DSG aggro aghast agile/Y agility/M aging/M agitate/XGNDS agitation/M agitator/MS agitprop/M agleam aglitter aglow agnostic/MS agnosticism/M ago agog agonist/S agonize/GDS agonizing/Y agony/SM agoraphobia/M agoraphobic/MS agrarian/MS agrarianism/M agree/EBLDS agreeableness/EM agreeably/E agreeing/E agreement/ESM agribusiness/MS agricultural/Y agriculturalist/MS agriculture/M agriculturist/MS agronomic agronomist/MS agronomy/M aground ague/M ah aha ahchoo ahead ahem ahoy aid/SMDG aide/SM aided/U aigrette/MS ail/SDLG aileron/SM ailment/SM aim/SMDG aimless/YP aimlessness/M ain't air/SMDJG airbag/MS airbase/SM airbed/S airborne airbrush/MDSG airbus/MS aircraft/M aircraftman aircraftmen aircrew/S airdrome/S airdrop/SM airdropped airdropping airfare/SM airfield/SM airflow/M airfoil/SM airfreight/M airguns airhead/SM airily airiness/M airing/M airless/P airlessness/M airletters airlift/SGMD airline/RSMZ airliner/M airlock/SM airmail/GSMD airman/M airmen airplane/MS airplay/M airport/SM airship/SM airshow/S airsick/P airsickness/M airspace/M airspeed airstrike/MS airstrip/SM airtight airtime/M airwaves/M airway/MS airwoman airwomen airworthiness/M airworthy/P airy/PTR aisle/MS aitch/MS ajar aka akimbo akin alabaster/M alack alacrity/M alarm/GMDS alarming/Y alarmist/SM alas alb/SM albacore/SM albatross/MS albeit albinism/M albino/MS album/MNS albumen/M albumin/M albuminous alchemist/SM alchemy/M alcohol/SM alcoholic/MS alcoholically alcoholism/M alcove/MS alder/MS alderman/M aldermen alderwoman/M alderwomen ale/SMV aleatory alehouse/SM alembic/SM alert/GMDYPS alertness/M alewife/M alewives alfalfa/M alfresco alga/M algae algal algebra/SM algebraic algebraically algorithm/SM algorithmic alias/GMDS alibi/GMDS alien/BGMDS alienable/IU alienate/DSGN alienation/M alienist/SM alight/GDS align/ALGDS aligned/U aligner/MS alignment/AMS alike/U aliment/MDSG alimentary alimony/M aliveness/M aliyah/M aliyahs alkali/M alkalies alkaline alkalinity/M alkalize/DSG alkaloid/SM alkyd/MS all/M allay/GDS allegation/MS allege/GDS alleged/Y allegiance/MS allegoric allegorical/Y allegorist/MS allegory/SM allegretto/MS allegro/MS allele/MS alleluia/SM allergen/SM allergenic allergic allergically allergist/SM allergy/SM alleviate/DSGN alleviation/M alley/MS alleyway/SM alliance/SM alligator/MS alliterate/DSXGNV alliteration/M alliterative/Y allocate/ADSGN allocation/AM allocations allot/LS allotment/SM allotted allotting allover allow/EGDS allowable/U allowably allowance/SM alloy/GMDS alloyed/U allspice/M allude/GDS allure/MGLDS allurement/MS alluring/Y allusion/SM allusive/PY allusiveness/M alluvial/M alluvium/SM ally/GDSM almanac/SM almighty almond/MS almoner/SM almost alms/M almshouse/MS aloe/SM aloft aloha/MS alone along alongshore alongside aloof/PY aloofness/M aloud alp/SM alpaca/MS alpha/MS alphabet/SM alphabetic alphabetical/Y alphabetization/SM alphabetize/ZGDRS alphabetizer/M alphanumeric alphanumerical/Y alpine/S already alright also alt/S altar/MS altarpiece/SM alter/GDBS alterable/U alteration/MS altercation/SM altered/U alternate/DSMYGNVX alternation/M alternative/MYS alternator/SM although altimeter/MS altitude/MS alto/SM altogether altruism/M altruist/SM altruistic altruistically alum/SM alumina/M aluminum/M alumna/M alumnae alumni alumnus/M alveolar/S always am/N amalgam/SM amalgamate/XGNDS amalgamation/M amanuenses amanuensis/M amaranth/M amaranths amaretto/M amaryllis/MS amass/GDS amateur/SM amateurish/YP amateurishness/M amateurism/M amatory amaze/LMGDS amazement/M amazing/Y amazon/MS amazonian ambassador/SM ambassadorial ambassadorship/MS ambassadress/MS amber/M ambergris/M ambiance/MS ambidexterity/M ambidextrous/Y ambient ambiguity/SM ambiguous/UY ambit ambition/MS ambitious/YP ambitiousness/M ambivalence/M ambivalent/Y amble/MZGDRS ambler/M ambrosia/M ambrosial ambulance/MS ambulanceman ambulancemen ambulancewoman ambulancewomen ambulant ambulate/DSXGN ambulation/M ambulatory/SM ambuscade/MGDS ambush/GMDS ameliorate/GNVDS amelioration/M amen/B amenability/M amenably amend/BLGDS amendment/SM amenity/SM amerce/GLDS amercement/SM americium/M amethyst/SM amiability/M amiable amiably amicability/M amicable amicably amid amide/MS amidships amigo/MS amino amiss amity/M ammeter/SM ammo/M ammonia/M ammonium ammunition/M amnesia/M amnesiac/MS amnesic/SM amnesty/GDSM amniocenteses amniocentesis/M amnion/MS amniotic amoeba/MS amoebae amoebic amok among amontillado/SM amoral/Y amorality/M amorous/YP amorousness/M amorphous/PY amorphousness/M amortization/SM amortize/DSGB amount/GMDS amour/MS amoxicillin amp/SMY amperage/M ampere/MS ampersand/MS amphetamine/SM amphibian/MS amphibious/Y amphitheater/SM amphora/M amphorae ampicillin ample/TR amplification/M amplifier/M amplify/NDRSXZG amplitude/SM ampule/MS amputate/GNDSX amputation/M amputee/MS amt amulet/MS amuse/LGDS amusement/MS amusing/Y amylase/M an/CS anabolism/M anachronism/SM anachronistic anachronistically anaconda/SM anaerobe/SM anaerobic anaerobically anagram/MS anal/Y analgesia/M analgesic/SM analog/MS analogical/Y analogize/GDS analogous/YP analogousness/M analogue/SM analogy/SM analysand/MS analyses/A analysis/AM analyst/SM analytic analytical/Y analyzable analyze/ADSG analyzer/SM anapest/SM anapestic/MS anarchic anarchically anarchism/M anarchist/MS anarchistic anarchy/M anathema/SM anathematize/DSG anatomic anatomical/Y anatomist/SM anatomize/DSG anatomy/SM ancestor/SM ancestral/Y ancestress/MS ancestry/SM anchor/MDGS anchorage/MS anchorite/MS anchorman/M anchormen anchorpeople anchorperson/SM anchorwoman/M anchorwomen anchovy/SM ancient/SPMRYT ancientness/M ancillary/SM and andante/SM andiron/SM androgen/M androgenic androgynous androgyny/M android/SM anecdotal/Y anecdote/MS anemia/M anemic anemically anemometer/SM anemone/SM anent anesthesia/M anesthesiologist/SM anesthesiology/M anesthetic/SM anesthetist/MS anesthetization/M anesthetize/GDS aneurysm/SM anew angel/MS angelfish/MS angelic angelica/M angelical/Y anger/GMDS angina/M angioplasty/SM angiosperm/SM angle/MZGDRS angler/M angleworm/MS anglicism/S anglicize/GDS angling/M anglophile/S anglophone/S angora/MS angostura angrily angry/TR angst/M angstrom/MS anguish/GMDS angular angularity/SM angulation anhydrous aniline/M animadversion/MS animadvert/GSD animal/MS animalcule/SM animate/ADSGN animated/Y animation/AM animations animator/MS anime/M animism/M animist/SM animistic animosity/SM animus/M anion/MS anionic anise/M aniseed/M anisette/M ankh/M ankhs ankle/MS anklebone/MS anklet/MS annalist/SM annals/M anneal/GDS annelid/MS annex/GMDS annexation/MS annihilate/DSGN annihilation/M annihilator/SM anniversary/SM annotate/DSXGNV annotation/M annotator/MS announce/DRSLZG announced/U announcement/MS announcer/M annoy/GDS annoyance/MS annoying/Y annual/MYS annualized annuitant/SM annuity/SM annul/LS annular annulled annulling annulment/SM annunciation/SM anode/MS anodize/GDS anodyne/MS anoint/GDLS anointment/M anomalous/Y anomaly/SM anon/S anonymity/M anonymous/Y anopheles/M anorak/MS anorectic/SM anorexia/M anorexic/MS another answer/BMDGS answerable/U answered/U answerphone/S ant/SMD antacid/SM antagonism/SM antagonist/SM antagonistic antagonistically antagonize/DSG antarctic ante/SM anteater/MS antebellum antecedence/M antecedent/SM antechamber/SM antedate/GDS antediluvian anteing antelope/MS antenatal antenna/SM antennae anterior anteroom/MS anthem/MS anther/MS anthill/SM anthologist/SM anthologize/DSG anthology/SM anthracite/M anthrax/M anthropocentric anthropoid/MS anthropological/Y anthropologist/SM anthropology/M anthropomorphic anthropomorphically anthropomorphism/M anthropomorphize anthropomorphous anti/SM antiabortion antiabortionist/MS antiaircraft antibacterial/MS antibiotic/MS antibody/SM antic/MS anticancer anticipate/GNXDS anticipated/U anticipation/M anticipatory anticked anticking anticlerical anticlimactic anticlimactically anticlimax/MS anticline/SM anticlockwise anticoagulant/MS anticommunism/M anticommunist/SM anticyclone/SM anticyclonic antidemocratic antidepressant/MS antidote/MS antifascist/MS antifreeze/M antigen/SM antigenic antigenicity/M antihero/M antiheroes antihistamine/SM antiknock/M antilabor antilogarithm/SM antimacassar/MS antimalarial antimatter/M antimicrobial antimissile antimony/M antinuclear antioxidant/MS antiparticle/SM antipasti antipasto/MS antipathetic antipathy/SM antipersonnel antiperspirant/SM antiphon/SM antiphonal/MYS antipodal/S antipodean/MS antipodes/M antipollution antipoverty antiquarian/SM antiquarianism/M antiquary/SM antiquate/GDS antique/DSMG antiquity/SM antirrhinum/S antiscience antisemitic antisemitism/M antisepsis/M antiseptic/SM antiseptically antiserum/MS antislavery antisocial/Y antispasmodic/MS antisubmarine antitank antitheses antithesis/M antithetic antithetical/Y antitoxin/MS antitrust antivenin/MS antivenom antiviral/MS antivirus antivivisectionist/MS antiwar antler/MDS antonym/SM antonymous antrum antsy/TR anus/MS anvil/MS anxiety/SM anxious/YP anxiousness/M any anybody/SM anyhow anymore anyone/M anyplace anything/SM anytime anyway/S anywhere anywise aorta/MS aortic apace apart apartheid/M apartment/MS apathetic apathetically apathy/M apatite/M ape/DSMG apelike aperitif/MS aperture/SM apex/MS aphasia/M aphasic/MS aphelia aphelion/SM aphid/MS aphorism/MS aphoristic aphoristically aphrodisiac/SM apiarist/SM apiary/SM apical/Y apiece apish/Y aplenty aplomb/M apocalypse/SM apocalyptic apocrypha/M apocryphal/Y apogee/MS apolitical/Y apologetic/U apologetically apologia/SM apologist/MS apologize/GDS apology/SM apoplectic apoplexy/SM apoptosis apoptotic apostasy/SM apostate/SM apostatize/GDS apostle/MS apostleship/M apostolic apostrophe/MS apothecary/SM apothegm/SM apotheoses apotheosis/M app/SM appall/GDS appalling/Y appaloosa/MS apparatchik/S apparatus/MS apparel/MDGS apparent/Y apparition/SM appeal/GMDS appealing/UY appear/AESDG appearance/EAMS appease/LZGDRS appeasement/SM appeaser/M appellant/SM appellate/XN appellation/M append/GDS appendage/SM appendectomy/SM appendices appendicitis/M appendix/MS appertain/GDS appetite/SM appetizer/MS appetizing/Y applaud/ZGDRS applauder/M applause/M apple/MS applejack/M applesauce/M applet/MS appliance/SM applicability/M applicable/I applicably applicant/SM application/AM applicator/SM applier/MS applique/DSM appliqueing apply/ANXGDS appoint/AELSVGD appointee/SM appointment's/A appointment/ESM apportion/AGDLS apportionment/AM appose/GDS apposite/YNVP appositeness/M apposition/M appositive/SM appraisal/AMS appraise/ADSG appraiser/MS appreciable/I appreciably/I appreciate/DSXGNV appreciated/U appreciation/M appreciative/Y appreciator/MS appreciatory apprehend/GDS apprehension/MS apprehensive/YP apprehensiveness/M apprentice/DSMG apprenticeship/MS apprise/GDS approach/GBMDS approachable/UI approbation/EM approbations appropriate/PYGNXDS appropriated/U appropriateness/IM appropriation/M appropriator/SM approval/EM approvals approve/EGDS approved/U approving/EY approx approximate/DSXYGN approximation/M appurtenance/SM appurtenant apricot/MS apron/MS apropos apse/SM apt/IYPT apter aptitude/SM aptness/IM aqua/SM aquaculture/M aqualung/MS aquamarine/SM aquanaut/MS aquaplane/MGDS aquarium/MS aquatic/SM aquatically aquatics/M aquatint/S aquavit/M aqueduct/MS aqueous aquifer/SM aquiline arabesque/MS arability/M arachnid/MS arachnophobia arbiter/SM arbitrage/MZGDRS arbitrager/M arbitrageur/SM arbitrament/SM arbitrarily arbitrariness/M arbitrary/P arbitrate/GNDS arbitration/M arbitrator/MS arbor/MS arboreal arboretum/SM arborvitae/SM arbutus/MS arc/SMDG arcade/MS arcane arch/PZTGVMDRSY archaeological/Y archaeologist/SM archaeology/M archaic archaically archaism/MS archaist/MS archangel/MS archbishop/SM archbishopric/SM archdeacon/SM archdiocesan archdiocese/MS archduchess/MS archduke/MS archenemy/SM archer/M archery/M archetypal archetype/MS archfiend/MS archiepiscopal archipelago/MS architect/SM architectonic/S architectonics/M architectural/Y architecture/MS architrave/SM archival archive/DSMG archivist/MS archness/M archway/SM arctic/MS ardent/Y ardor/MS arduous/YP arduousness/M are/SMB area/SM areal aren't arena/MS argent/M argon/M argosy/SM argot/MS arguable/IU arguably/U argue/ZGDRS arguer/M argument/MS argumentation/M argumentative/PY argumentativeness/M argyle/MS aria/SM arid/Y aridity/M aright arise/GS arisen aristocracy/SM aristocrat/SM aristocratic aristocratically arithmetic/M arithmetical/Y arithmetician/MS ark/SM arm's arm/EAGDS armada/MS armadillo/SM armament/AEM armaments armature/MS armband/MS armchair/MS armed/U armful/MS armhole/SM armistice/SM armlet/MS armload/S armor/ZGMDRS armored/U armorer/M armorial armory/SM armpit/MS armrest/SM army/SM aroma/MS aromatherapist/MS aromatherapy/M aromatic/MS aromatically arose around arousal/M arouse/GDS arpeggio/MS arr arraign/DGSL arraignment/SM arrange/AESDLG arrangement's/E arrangement/ASM arranger/SM arrant arras/MS array/EGMDS arrears/M arrest/AGMDS arrhythmia/M arrhythmic arrhythmical arrival/MS arrive/GDS arrogance/M arrogant/Y arrogate/GNDS arrogation/M arrow/MS arrowhead/MS arrowroot/M arroyo/MS arsed arsenal/MS arsenic/M arsing arson/M arsonist/SM art/SM arterial arteriole/MS arteriosclerosis/M artery/SM artful/PY artfulness/M arthritic/MS arthritis/M arthropod/MS arthroscope/SM arthroscopic arthroscopy artichoke/SM article/MDS articulacy/I articular articulate/YGNPDSX articulateness/IM articulation/M artifact/SM artifice/RSMZ artificer/M artificial/Y artificiality/M artillery/M artilleryman/M artillerymen artiness/M artisan/MS artist/MS artiste/MS artistic/I artistically artistry/M artless/PY artlessness/M artsy/TR artwork/MS arty/PTR arugula arum/SM asap asbestos/M ascend/AGDS ascendance/M ascendancy/M ascendant/SM ascension/MS ascent/MS ascertain/GDSBL ascertainment/M ascetic/MS ascetically asceticism/M ascot/MS ascribe/GBDS ascription/M aseptic aseptically asexual/Y asexuality/M ash/MDNSG ashamed/UY ashcan/MS ashlar/MS ashore ashram/MS ashtray/SM ashy/TR aside/MS asinine/Y asininity/SM ask/SDG askance asked/U askew aslant asleep asocial asp/SMNX asparagus/M aspartame/M aspect/MS aspen/M asperity/SM aspersion/MS asphalt/MDGS asphodel/SM asphyxia/M asphyxiate/DSXGN asphyxiation/M aspic/MS aspidistra/MS aspirant/MS aspirate/MGNDSX aspiration/M aspirator/SM aspire/GDS aspirin/MS ass/MS assail/GBDS assailable/U assailant/SM assassin/SM assassinate/GNXDS assassination/M assault/MDRGS assay/ZGMDRS assayer/M assemblage/SM assemble/AEGSD assembler/MS assemblies assembly/AM assemblyman/M assemblymen assemblywoman/M assemblywomen assent/GMDS assert/AGVDS assertion/AM assertions assertive/YP assertiveness/M assess/ALGDS assessment/ASM assessor/MS asset/MS asseverate/DSGN asseveration/M asshole/MS! assiduity/M assiduous/PY assiduousness/M assign's assign/ALGDS assignable assignation/MS assigned/U assignee/M assigner/MS assignment/AMS assignor/MS assimilate/DSGN assimilation/M assist/GMDS assistance/M assistant/SM assisted/U assize/MS assn assoc associate's associate/EDSGNV association/EM associations assonance/M assonant/MS assort/GLDS assortment/MS asst assuage/GDS assume/BGDS assumption/SM assumptive assurance/ASM assure/AGDS assured/MYS astatine/M aster/EMS asterisk/GMDS astern asteroid/MS asthma/M asthmatic/SM asthmatically astigmatic astigmatism/SM astir astonish/DSLG astonishing/Y astonishment/M astound/GDS astounding/Y astraddle astrakhan/M astral astray astride astringency/M astringent/SMY astrolabe/SM astrologer/SM astrological/Y astrologist/MS astrology/M astronaut/MS astronautic/S astronautical astronautics/M astronomer/SM astronomic astronomical/Y astronomy/M astrophysical astrophysicist/MS astrophysics/M astute/PYTR astuteness/M asunder asylum/SM asymmetric asymmetrical/Y asymmetry/SM asymptomatic asymptotic asymptotically asynchronous/Y at atavism/M atavist/SM atavistic ataxia/M ataxic/MS ate atelier/SM atheism/M atheist/MS atheistic atherosclerosis/M atherosclerotic athirst athlete/MS athletic/S athletically athleticism athletics/M athwart atilt atishoo atlas/MS atmosphere/MS atmospheric/S atmospherically atmospherics/M atoll/MS atom/SM atomic atomically atomize/ZGDRS atomizer/M atonal/Y atonality/M atone/LGDS atonement/M atop atria atrial atrioventricular atrium/M atrocious/PY atrociousness/M atrocity/SM atrophy/DSMG atropine/M attach/ALGDS attache/BM attached/U attachment/AM attachments attack/ZGMDRS attacker/M attain/AGDS attainability/M attainable/U attainder/M attainment/SM attar/M attempt's attempt/ASDG attend/SDRZG attendance/SM attendant/SM attended/U attendee/SM attention/IM attentions attentive/IPY attentiveness/IM attenuate/DSGN attenuation/M attest/SDG attestation/SM attested/U attic/SM attire/DSMG attitude/SM attitudinal attitudinize/GDS attn attorney/MS attract/SGVDB attractant/MS attraction/MS attractive/UY attractiveness/M attribute/DSMGNVBX attributed/U attribution/M attributive/MYS attrition/M attune/DSG atty atwitter atypical/Y aubergine/S auburn/M auction/MDGS auctioneer/SM audacious/YP audaciousness/M audacity/M audibility/IM audible/MS audibly/I audience/MS audio/MS audiological audiologist/SM audiology/M audiometer/SM audiophile/SM audiotape/SM audiovisual/S audiovisuals/M audit/GMDS audition/SMDG auditor/MS auditorium/SM auditory auger/MS aught/MS augment/DRZGS augmentation/MS augmentative augmenter/M augur/GMDS augury/SM august/PTRY augustness/M auk/SM aunt/SM auntie/SM aura/MS aural/Y aureole/SM aureus auricle/SM auricular aurora/SM auscultate/GNDSX auscultation/M auspice/SM auspicious/IY auspiciousness/M austere/RYT austerity/SM austral authentic/IU authentically authenticate/XGNDS authenticated/U authentication/M authenticity/M author/SMDG authoress/MS authorial authoritarian/MS authoritarianism/M authoritative/YP authoritativeness/M authority/SM authorization/MS authorize/AGDS authorized/U authorship/M autism/M autistic auto/MS autobahn/SM autobiographer/SM autobiographic autobiographical/Y autobiography/SM autoclave/MS autocracy/SM autocrat/SM autocratic autocratically autocross autodidact/SM autograph/MDG autographs autoimmune autoimmunity/M automaker/SM automate/GNDS automatic/SM automatically automation/M automatism/M automatize/GDS automaton/SM automobile/DSMG automotive autonomic autonomous/Y autonomy/M autopilot/SM autopsy/GDSM autosuggestion autoworker/MS autumn/SM autumnal aux auxiliary/SM auxin/M av/RZ avail/BGMDS availability/UM available/U avalanche/SM avarice/M avaricious/Y avast avatar/MS avaunt avdp ave avenge/ZGDRS avenger/M avenue/MS average/MYGDS averred averring averse/XN aversion/M avert/GDS avg avian aviary/SM aviation/M aviator/MS aviatrices aviatrix/MS avid/Y avidity/M avionic/S avionics/M avitaminosis/M avocado/SM avocation/MS avocational avoid/SDGB avoidable/U avoidably/U avoidance/M avoirdupois/M avouch/DSG avow/EDGS avowal/ESM avowed/Y avuncular/Y aw await/GDS awake/GS awaken/AGDS awakening/SM award/GMDS awardee/S aware/UP awareness/UM awash away awe/DSMG aweigh awesome/YP awesomeness/M awestruck awful/YP awfuller awfullest awfulness/M awhile awkward/RYPT awkwardness/M awl/SM awn/GJSM awning/M awoke awoken awry ax/MDSG axial/Y axiom/SM axiomatic axiomatically axis/M axle/MS axletree/SM axolotl/SM axon/MS ayah/M ayahs ayatollah/M ayatollahs aye/SM azalea/SM azimuth/M azimuths azure/SM b/KDT baa/SMDG babble/MZGDRS babbler/M babe/SM babel/MS baboon/MS babushka/SM baby/TGDRSM babyhood/M babyish babysat babysit/S babysitter/MS babysitting/M baccalaureate/SM baccarat/M bacchanal/MS bacchanalia/M bacchanalian/MS baccy bachelor/SM bachelorhood/M bacillary bacilli bacillus/M back/SJZGMDR backache/MS backbench/S backbit backbite/ZGRS backbiter/M backbitten backboard/SM backbone/MS backbreaking backchat backcloth backcloths backcomb/DSG backdate/GDS backdoor backdrop/MS backer/M backfield/SM backfire/MGDS backgammon/M background/MRZS backgrounder/M backhand/MDRSZG backhanded/Y backhander/M backhoe/MS backing/M backlash/MS backless backlog/MS backlogged backlogging backpack/ZGMDRS backpacker/M backpacking/M backpedal/SDG backrest/SM backroom/S backscratching/M backseat/SM backside/SM backslapper/SM backslapping/M backslash/MS backslid backslide/RSZG backslider/M backspace/DSMG backspin/M backstabber/MS backstabbing backstage/M backstair/S backstop/SM backstopped backstopping backstory/S backstreet/S backstretch/MS backstroke/MGDS backtalk/M backtrack/SDG backup/MS backward/PSY backwardness/M backwash/M backwater/SM backwoods/M backwoodsman/M backwoodsmen backyard/SM bacon/M bacteria/M bacterial bactericidal bactericide/SM bacteriologic bacteriological bacteriologist/SM bacteriology/M bacterium/M bad/MYP badder baddest baddie/MS bade badge/MZRS badger/GMD badinage/M badlands/M badman/M badmen badminton/M badmouth/GD badmouths badness/M baffle/MZGDRSL bafflement/M baffler/M bag/SM bagatelle/SM bagel/MS bagful/MS baggage/M bagged baggie/M baggily bagginess/M bagging baggy/PTRS bagpipe/MZRS bagpiper/M baguette/MS bah baht/SM bail/SBGMD bailey/S bailiff/S bailiwick/MS bailout/SM bailsman/M bailsmen bairn/MS bait/SGMD baize/M bake/DRSMZG baked/U baker/M bakery/SM bakeshop/MS baklava/M baksheesh/M balaclava/MS balalaika/MS balance's balance/UDSG balboa/SM balcony/SM bald/STGPDRY balderdash/M baldfaced baldness/M baldric/SM baldy/S bale/DRSMZG baleen/M baleful/PY balefulness/M baler/M balk/SGMD balky/RT ball/SGMD ballad/SM balladeer/MS balladry/M ballast/GSMD ballcock/MS ballerina/SM ballet/SM balletic ballgame/MS ballgirl/S ballgown/S ballistic/S ballistics/M balloon/SGMD balloonist/MS ballot/SMDG ballpark/MS ballplayer/MS ballpoint/MS ballroom/MS balls/DSG ballsy/RT bally ballyhoo/SMDG balm/SM balminess/M balmy/RTP baloney/M balsa/MS balsam/SM balsamic baluster/SM balustrade/MS bamboo/SM bamboozle/DSG ban/SM banal/Y banality/SM banana/SM band's band/ESGD bandage/DSMG bandanna/MS bandbox/MS bandeau/M bandeaux bandit/SM banditry/M bandleader/S bandmaster/SM bandoleer/SM bandsman/M bandsmen bandstand/SM bandwagon/SM bandwidth bandwidths bandy/DRSTG bane/SM baneful bang/SGMDR bangle/SM bani banish/GLDS banishment/M banister/SM banjo/MS banjoist/SM bank/SZGBMDR bankbook/SM bankcard/SM banker/M banking/M banknote/SM bankroll/SGMD bankrupt/SGMD bankruptcy/SM banned banner/SM banning bannock/MS banns/M banquet/ZGMDRS banqueter/M banquette/SM banshee/MS bantam/SM bantamweight/SM banter/GSMD bantering/Y banyan/SM banzai/SM baobab/SM bap/S baptism/MS baptismal baptist/S baptistery/SM baptize/ZGDRS baptized/U baptizer/M bar's bar/ECUTS barb/SZGMDR barbarian/SM barbarianism/MS barbaric barbarically barbarism/SM barbarity/SM barbarize/DSG barbarous/Y barbecue/DSMG barbel/SM barbell/MS barber/GMD barberry/SM barbershop/MS barbie/S barbiturate/SM barbwire/M barcarole/SM bard/SM bardic bare/DRSPYG bareback/D barefaced/Y barefoot/D barehanded bareheaded barelegged bareness/M barf/SGMDY barfly/SM bargain/MDRZGS bargainer/M barge/MGDS bargeman/M bargemen barhop/S barhopped barhopping barista/MS baritone/MS barium/M bark's bark/CSGD barkeep/ZMRS barkeeper/M barker/SM barley/M barmaid/MS barman/M barmen barmy/RT barn/SM barnacle/MDS barney/S barnstorm/SDRZG barnstormer/M barnyard/SM barometer/MS barometric barometrically baron/MS baronage/MS baroness/MS baronet/MS baronetcy/SM baronial barony/SM baroque/M barque/SM barrack/MDGS barracuda/SM barrage/MGDS barre/MGJDS barred/UEC barrel/GSMD barren/TPSMR barrenness/M barrette/SM barricade/MGDS barrier/MS barring/ECU barrio/SM barrister/MS barroom/MS barrow/SM bartender/SM barter/ZGSMDR barterer/M baryon/SM basal/Y basalt/M basaltic base's base/CDSLTG baseball/SM baseboard/MS baseless baseline/MS basely baseman/M basemen basement/CMS baseness/M baser bash/GMDS bashful/PY bashfulness/M bashing/M basic/MS basically basil/M basilica/MS basilisk/MS basin/MS basinful/MS basis/M bask/SGD basket/SM basketball/MS basketry/M basketwork/M basque/S bass/MS basset/SM bassinet/MS bassist/MS basso/MS bassoon/MS bassoonist/SM basswood/MS bast/M bastard/MS bastardization/MS bastardize/GDS bastardy/M baste/ZGNXDRS baster/M bastion/M bat/SM batch/MDSG bate/KACGSD bath/ZGMDRS bathe/M bather/M bathetic bathhouse/MS bathing/M bathmat/MS bathos/M bathrobe/SM bathroom/SM baths bathtub/MS bathwater bathyscaphe/SM bathysphere/MS batik/MS batiste/M batman/M batmen baton/MS batsman/M batsmen battalion/SM batted batten/GSMD batter/JZGSMDR batterer/M battery/SM batting/M battle/LDRSMZG battleaxe/MS battledore/SM battledress battlefield/MS battlefront/MS battleground/MS battlement/SM battler/M battleship/SM batty/RT bauble/SM baud/SM bauxite/M bawd/SM bawdily bawdiness/M bawdy/PRT bawl/SGMD bay/SMDG bayberry/SM bayonet/SMDG bayou/MS bazaar/SM bazillion/S bazooka/SM bbl bdrm be beach/MDSG beachcomber/SM beachfront beachhead/MS beachwear/M beacon/SM bead/SGMD beading/M beadle/SM beady/RT beagle/SM beak/SZMDR beaker/M beam/SGMD bean/SGMD beanbag/MS beanfeast/S beanie/SM beanpole/MS beansprout/S beanstalk/MS bear/SZGBJMR bearable/U bearably/U beard/MDGS beardless bearer/M bearing/M bearish/PY bearishness/M bearlike bearskin/MS beast/MS beastliness/M beastly/TPRM beat/SZGBMNRJ beatable/U beaten/U beater/M beatific beatifically beatification/M beatify/GXNDS beating/M beatitude/SM beatnik/MS beau/SM beaut/MS beauteous/Y beautician/SM beautification/M beautifier/M beautiful/Y beautify/NDRSZG beauty/SM beaver/SGMD bebop/MS becalm/GSD became because beck/SM beckon/SGD becloud/GDS become/S becoming/UY becquerel/S bed/SM bedaub/GSD bedazzle/GDSL bedazzlement/M bedbug/SM bedchamber/S bedclothes/M bedded bedder bedding/M bedeck/GSD bedevil/LGDS bedevilment/M bedfellow/SM bedhead/S bedim/S bedimmed bedimming bedizen/GDS bedlam/SM bedpan/SM bedpost/SM bedraggle/GDS bedridden bedrock/SM bedroll/SM bedroom/SM bedside/SM bedsit/S bedsitter/S bedsore/SM bedspread/SM bedstead/SM bedtime/SM bee/RSMZGJ beebread/M beech/MS beechnut/MS beef/SGMD beefburger/SM beefcake/MS beefiness/M beefsteak/MS beefy/RPT beehive/MS beekeeper/MS beekeeping/M beeline/MS been beep/SZGMDR beeper/M beer/M beery/TR beeswax/M beet/SM beetle/MGDS beetroot/S beeves befall/SGN befell befit/S befitted befitting/Y befog/S befogged befogging before beforehand befoul/DGS befriend/SGD befuddle/GLDS befuddlement/M beg/S began begat beget/S begetter/S begetting beggar/MDYGS beggary/M begged begging begin/S beginner/SM beginning/MS begone begonia/SM begot begotten begrime/DSG begrudge/DSG begrudging/Y beguile/DRSZGL beguilement/M beguiler/M beguiling/Y beguine/SM begum/MS begun behalf/M behalves behave/GDS behavior/SM behavioral/Y behaviorism/M behaviorist/MS behead/DGS beheld behemoth/M behemoths behest/MS behind/MS behindhand behold/NRZGS beholder/M behoove/DSG beige/M being/M bejewel/SDG belabor/SDG belated/Y belay/GDS belch/GMDS beleaguer/GSD belfry/SM belie/DS belief/EUM beliefs believable/U believably/U believe/EDRSZG believer/EUMS believing/U belittle/LDSG belittlement/M bell/SGMD belladonna/M bellboy/SM belle/MS belled/A belletrist/MS belletristic bellhop/SM bellicose bellicosity/M belligerence/M belligerency/M belligerent/MYS belling/A bellman/M bellmen bellow/MDGS bellwether/MS belly/GDSM bellyache/MGDS bellybutton/SM bellyful/MS belong/JDGS belonging/M beloved/SM below belt/SGMD beltway/SM beluga/MS belying bemire/GDS bemoan/DGS bemuse/LGDS bemused/Y bemusement/M bench/GMDS benchmark/MS bend/BSZGMR bender/M bendy/TR beneath benedictine benediction/SM benedictory benefaction/SM benefactor/MS benefactress/MS benefice/SM beneficence/M beneficent/Y beneficial/Y beneficiary/SM benefit/SMDG benevolence/SM benevolent/Y benighted/Y benign/Y benignant benignity/M bent/SM bentwood/M benumb/DSG benzene/M benzine/M benzyl bequeath/DG bequeaths bequest/MS berate/GDS bereave/DSLG bereavement/MS bereft beret/MS berg/SM beriberi/M berk/S berkelium/M berm/SM berry/GDSM berrylike berserk berth/GMD berths beryl/MS beryllium/M beseech/ZGRS beseecher/M beseeching/Y beseem/DSG beset/S besetting beside/S besiege/ZGDRS besieger/M besmear/DSG besmirch/GDS besom/MS besot/S besotted besotting besought bespangle/DSG bespatter/GSD bespeak/SG bespectacled bespoke bespoken best/SGMD bestial/Y bestiality/M bestiary/SM bestir/S bestirred bestirring bestow/DGS bestowal/SM bestrew/SDG bestrewn bestridden bestride/SG bestrode bestseller/MS bestselling bet/SM beta/SM betake/GS betaken betcha betel/M bethink/SG bethought betide/GDS betimes betoken/GDS betook betray/DRZGS betrayal/SM betrayer/M betroth/DG betrothal/SM betrothed/M betroths better/MDGLS betterment/M betting bettor/MS between betwixt bevel/GMDS beverage/SM bevvy/S bevy/SM bewail/DGS beware/GDS bewhiskered bewigged bewilder/LSGD bewildering/Y bewilderment/M bewitch/GLDS bewitching/Y bewitchment/M bey/SM beyond bezel/MS bf bhaji bi/SMRZ biannual/Y bias/GMDS biased/U biathlon/SM bib/SM bible/MS biblical bibliographer/MS bibliographic bibliographical/Y bibliography/SM bibliophile/SM bibulous bicameral bicameralism/M bicarb/MS bicarbonate/MS bicentenary/SM bicentennial/SM bicep/MS biceps/M bicker/MDRZGS bickerer/M biconcave biconvex bicuspid/MS bicycle/DRSMZG bicycler/M bicyclist/SM bid/SMG biddable bidden/U bidder/MS bidding/M biddy/SM bide/S bidet/MS bidirectional/Y biennial/MYS biennium/MS bier/M biff/SGD bifocal/S bifocals/M bifurcate/XDSGN bifurcation/M big/P bigamist/SM bigamous bigamy/M bigger biggest biggie/MS biggish bighead/SM bighearted/P bigheartedness/M bighorn/SM bight/MS bigmouth/M bigmouths bigness/M bigot/MDS bigotry/SM bigwig/MS bijou/M bijoux bike/DRSMZG biker/M bikini/MS bilabial/MS bilateral/Y bilberry/S bile/M bilge/MS bilingual/SMY bilingualism/M bilious/P biliousness/M bilirubin bilk/SZGDR bilker/M bill/SBJGMD billboard/MS billet/GMDS billfold/SM billhook/S billiard/S billiards/M billing/M billingsgate/M billion/MHS billionaire/SM billionth/M billionths billow/GMDS billowy billy/SM billycan/S bimbo/MS bimetallic/SM bimetallism/M bimonthly/SM bin/SM binary/SM bind's bind/AUGS binder/MS bindery/SM binding/MS bindweed/M binge/MDS bingo/M binman binmen binnacle/SM binned binning binocular/MS binomial/SM bio/SM biochemical/SMY biochemist/MS biochemistry/M biodegradability/M biodegrade/DSGB biodiversity/M bioethics/M biofeedback/M biog biographer/SM biographic biographical/Y biography/SM biol biologic biological/Y biologist/MS biology/M biomass/M biomedical bionic/S bionically bionics/M biophysical biophysicist/MS biophysics/M biopic/MS biopsy/GDSM bioreactor/S biorhythm/MS biosensor/S biosphere/SM biosynthesis biotech biotechnological biotechnology/M biotin/M bipartisan bipartisanship/M bipartite biped/MS bipedal biplane/MS bipolar bipolarity/M biracial birch/GMDS bird/SZGMDR birdbath/M birdbaths birdbrain/SMD birdcage/S birder/M birdhouse/MS birdie/MDS birdieing birdlike birdlime/M birdseed/M birdsong birdwatcher/SM birdying biretta/SM birth/ZGMDR birthday/MS birther/M birthmark/MS birthplace/MS birthrate/MS birthright/MS births/A birthstone/SM biscuit/SM bisect/DGS bisection/MS bisector/SM bisexual/MYS bisexuality/M bishop/MS bishopric/SM bismuth/M bison/M bisque/M bistro/MS bit/CSMG bitch/GMDS bitchily bitchiness/M bitchy/PRT bitcoin/SM bite/RSMZ biter/M biting/Y bitmap/S bitten bitter/PMRYTS bittern/SM bitterness/M bitters/M bittersweet/MS bitty/TR bitumen/M bituminous bivalent bivalve/SM bivouac/MS bivouacked bivouacking biweekly/SM biyearly biz/M bizarre/Y bk bl/DG blab/SM blabbed blabber/DGS blabbermouth/M blabbermouths blabbing black/PXTGMDNRYS blackamoor/MS blackball/SGMD blackberry/GSM blackbird/SM blackboard/MS blackcurrant/S blacken/DG blackface blackguard/SM blackhead/MS blacking/M blackish blackjack/MDGS blackleg/S blacklist/MDSG blackmail/MDRSZG blackmailer/M blackness/M blackout/SM blacksmith/M blacksmiths blacksnake/SM blackthorn/SM blacktop/SM blacktopped blacktopping bladder/MS blade/MDS blag/S blagged blagging blah/M blahs/M blame/BMGDRS blameless/YP blamelessness/M blameworthiness/M blameworthy/P blammo blanch/GDS blancmange/MS bland/PTRY blandish/DSLG blandishment/SM blandness/M blank/TGPMDRYS blanket/GMDS blankness/M blare/MGDS blarney/SMDG blase blaspheme/ZGDRS blasphemer/M blasphemous/Y blasphemy/SM blast/ZGMDRS blaster/M blastoff/MS blat/S blatancy/SM blatant/Y blather/SMDG blaze/MZGDRS blazer/M blazon/MDGS bldg bleach/MDRSZG bleached/U bleacher/M bleak/TPRY bleakness/M blear blearily bleariness/M bleary/PRT bleat/GMDS bleed/ZGRS bleeder/M bleeding/M bleep/ZGMDRS bleeper/M blemish/GMDS blemished/U blench/DSG blend/ZGMDRS blender/M bless/GDSJ blessed/YP blessedness/M blessing/M bletch blew blight/ZGMDRS blimey blimp/MS blimpish blind/PZTGMDRYS blinder/M blindfold/SMDG blinding/Y blindness/M blindside/DSG blini/MS blink/ZGMDRS blinker/MDG blintz/MS blintze/M blip/SM bliss/M blissful/YP blissfulness/M blister/GMDS blistering/Y blistery blithe/PYTR blitheness/M blither/G blithesome blitz/GMDS blitzkrieg/MS blivet/S blizzard/SM bloat/ZGDRS bloatware blob/SM blobbed blobbing bloc/SM block's block/UGDS blockade/MZGDRS blockader/M blockage/MS blockbuster/SM blockbusting/M blocker/MS blockhead/SM blockhouse/MS blog/SM blogged blogger/MS blogging bloke/MS blokish blond/PTMRS blonde/MS blondish blondness/M blood/GMDS bloodbath/M bloodbaths bloodcurdling bloodhound/SM bloodily bloodiness/M bloodless/YP bloodlessness/M bloodletting/M bloodline/SM bloodmobile/MS bloodshed/M bloodshot bloodstain/SMD bloodstock/M bloodstream/SM bloodsucker/SM bloodsucking bloodthirstily bloodthirstiness/M bloodthirsty/RPT bloody/PTGDRS bloom/ZGMDRS bloomer/M bloop/ZGMDRS blooper/M blossom/GMDS blossomy blot/SM blotch/GMDS blotchy/TR blotted blotter/MS blotting blotto blouse/MGDS blow/SZGMR blower/M blowfly/SM blowgun/MS blowhard/MS blowhole/S blowjob blowlamp/S blown blowout/SM blowpipe/SM blowtorch/MS blowup/MS blowy/TR blowzy/RT blubber/GSMD blubbery bludgeon/MDGS blue/DRSPMTG bluebell/MS blueberry/SM bluebird/MS bluebonnet/SM bluebottle/SM bluefish/MS bluegill/MS bluegrass/M blueish bluejacket/SM bluejeans/M blueness/M bluenose/MS bluepoint/MS blueprint/MDGS bluestocking/SM bluesy/RT bluet/MS bluff/ZTGPMDRYS bluffer/M bluffness/M bluing/M bluish blunder/MDRZGS blunderbuss/MS blunderer/M blunt/PTGDRYS bluntness/M blur/SM blurb/MS blurred blurriness/M blurring blurry/TRP blurt/GDS blush/ZGMDRS blusher/M bluster/MDRSZG blusterer/M blusterous blustery blvd boa/SM boar/SM board/ZGMDRS boarder/M boarding/M boardinghouse/MS boardroom/MS boardwalk/MS boast/ZGMDRS boaster/M boastful/PY boastfulness/M boat/SZGMDR boater/M boathouse/MS boating/M boatload/S boatman/M boatmen boatswain/SM boatyard/S bob/SM bobbed bobbin/MS bobbing bobble/MGDS bobby/SM bobbysoxer/SM bobcat/MS bobolink/SM bobsled/SM bobsledded bobsledder/MS bobsledding bobsleigh/M bobsleighs bobtail/SM bobwhite/MS boccie/M bock/M bod/SMDG bodacious bode/S bodega/MS bodge/GDS bodice/MS bodily bodkin/MS body/DSM bodybuilder/SM bodybuilding/M bodyguard/MS bodysuit/SM bodywork/M boffin/S boffo bog/SM boga bogey/GMDS bogeyman/M bogeymen bogged bogging boggle/GDS boggy/TR bogie/MS bogon bogosity bogus bogyman/M bogymen bohemian/SM bohemianism/M boil/SJZGMDR boiler/M boilermaker/SM boilerplate/M boink/GDS boisterous/YP boisterousness/M bola/SM bold/PTRY boldface/DM boldness/M bole/SM bolero/MS bolivar/MS bolivares boll/SM bollard/S bollix/GMDS bollocking/S bollocks bologna/M bolshie bolster/GMDS bolt's bolt/USGD bolthole/S bolus/MS bomb/SJZGMDR bombard/GDLS bombardier/MS bombardment/SM bombast/M bombastic bombastically bomber/M bombproof bombshell/SM bombsite/S bonanza/MS bonbon/MS bonce/S bond/SGMD bondage/M bondholder/MS bonding/M bondman/M bondmen bondsman/M bondsmen bondwoman/M bondwomen bone/DRSMZG bonehead/SMD boneless boner/M boneshaker/S boneyard bonfire/MS bong/SGMD bongo/MS bonhomie/M boniness/M bonito/MS bonk/SZGD bonnet/MS bonny/TR bonobo/MS bonsai/M bonus/MS bony/PTR boo/SMDHG boob/SGMD booby/SM boodle/MS booger/S boogeyman/M boogeymen boogie/MDS boogieing boogieman/M boohoo/GMDS book/SBJGMD bookbinder/SM bookbindery/SM bookbinding/M bookcase/MS bookend/MS bookie/MS booking/M bookish bookkeeper/MS bookkeeping/M booklet/MS bookmaker/SM bookmaking/M bookmark/SMDG bookmobile/SM bookplate/MS bookseller/MS bookshelf/M bookshelves bookshop/SM bookstall/S bookstore/MS bookworm/SM boom/SZGMDR boombox/MS boomerang/MDGS boon/SM boondocks/M boondoggle/MZGDRS boondoggler/M boonies/M boor/SM boorish/PY boorishness/MS boost/ZGMDRS booster/M boot's boot/ASGD bootblack/SM bootee/MS booth/M booths bootlace/S bootleg/MS bootlegged bootlegger/MS bootlegging/M bootless bootstrap/MS bootstrapped bootstrapping booty/SM booze/MZGDRS boozer/M boozy/TR bop/SM bopped bopping borax/M bordello/MS border/GMDS borderland/MS borderline/MS bore/DRSMZG boredom/M borehole/S borer/M boring/Y born/IAU borne boron/M borough/M boroughs borrow/SDRZGJ borrower/M borrowing/M borscht/M borstal/S borzoi/SM bosh/M bosom's bosom/US bosomy boss/DSGM bossily bossiness/M bossism/M bossy/RTP bot/S botanic botanical/Y botanist/SM botany/M botch/DRSZGM botcher/M both bother/SMDG botheration bothersome botnet/SM bottle/DRSMZG bottleneck/MS bottler/M bottom/SMDG bottomless botulinum botulism/M boudoir/SM bouffant/SM bougainvillea/MS bough/M boughs bought bouillabaisse/SM bouillon/MS boulder/SM boules boulevard/SM bounce/DRSMZG bouncer/M bouncily bounciness/M bouncy/RTP bound/ASMGD boundary/SM bounden bounder/SM boundless/PY boundlessness/M bounteous/YP bounteousness/M bountiful/YP bountifulness/M bounty/SM bouquet/SM bourbon/SM bourgeois/M bourgeoisie/M boustrophedon bout/MS boutique/SM boutonniere/MS bouzouki/MS bovine/SM bovver bow/ZGSMDR bowdlerization/MS bowdlerize/DSG bowed/U bowel/SM bower/M bowl/MDRZGS bowleg/SM bowlegged bowler/M bowlful/SM bowline/SM bowling/M bowman/M bowmen bowsprit/SM bowstring/SM bowwow/SM box/ZGMDNRS boxcar/SM boxer/M boxing/M boxlike boxroom/S boxwood/M boxy/RT boy/SM boycott/SGMD boyfriend/MS boyhood/SM boyish/YP boyishness/M boysenberry/SM bozo/MS bpm bps bra/SM brace/MZGDRS bracelet/MS bracer/M bracero/MS bracken/M bracket/GMDS brackish/P brackishness/M bract/MS brad/SM bradawl/S bradycardia brae/SM brag/SM braggadocio/SM braggart/SM bragged bragger/MS bragging braid/GMDS braiding/M braille/M brain/GMDS brainchild/M brainchildren/M braininess/M brainless/Y brainpower brainstorm/SMDG brainstorming/M brainteaser/SM brainwash/DSG brainwashing/M brainwave/S brainy/PTR braise/GDS brake/MGDS brakeman/M brakemen bramble/MS brambly bran/M branch/GMDS branchlike brand/ZGMDRS branded/U brander/M brandish/DSG brandy/GDSM brash/PTRY brashness/M brass/MS brasserie/MS brassiere/MS brassily brassiness/M brassy/PTR brat/SM bratty/RT bratwurst/SM bravado/M brave/GPMYDTRS braveness/M bravery/M bravo/SM bravura/SM brawl/SDRZGM brawler/M brawn/M brawniness/M brawny/RTP bray/DGSM braze/DRSZG brazen/SDYGP brazenness/M brazer/M brazier/SM breach/GMDS bread/GMDHS breadbasket/SM breadboard/SM breadbox/MS breadcrumb/MS breadfruit/SM breadline/MS breadth/M breadths breadwinner/SM break/BMZGRS breakable/MS breakage/MS breakaway/MS breakdown/MS breaker/M breakfast/MDGS breakfront/MS breakneck breakout/MS breakpoints breakthrough/M breakthroughs breakup/SM breakwater/SM bream/MS breast/SMDG breastbone/MS breastfed breastfeed/GS breastplate/SM breaststroke/SM breastwork/MS breath/MDRSZGB breathalyze/ZGDRS breathe breather/M breathing/M breathless/PY breathlessness/M breaths breathtaking/Y breathy/RT bred/I breech/MS breed/SRZGM breeder/M breeding/IM breeze/DSMG breezeway/SM breezily breeziness/M breezy/RTP brethren breve/SM brevet/SM brevetted brevetting breviary/SM brevity/M brew/MDRZGS brewer/M brewery/SM brewpub/SM bribe/DRSMZG briber/M bribery/M brick/SMDG brickbat/SM brickie/S bricklayer/MS bricklaying/M brickwork/M brickyard/S bridal/SM bride/SM bridegroom/SM bridesmaid/MS bridge/DSMG bridgeable/U bridgehead/SM bridgework/M bridle/DSMG bridled/U bridleway/S brie/MZR brief's brief/CSDTGJ briefcase/SM briefer briefing/CM briefly briefness/M brier/M brig/MS brigade/SM brigadier/MS brigand/SM brigandage/M brigantine/MS bright/SPNRYXT brighten/DRZG brightener/M brightness/M brights/M brill brilliance/M brilliancy/M brilliant/MYS brilliantine/M brim/MS brimful brimless brimmed brimming brimstone/M brindle/DM brine/M bring/SRZG bringer/M brininess/M brink/SM brinkmanship/M briny/RTP brioche/SM briquette/MS brisk/SDRYTGP brisket/SM briskness/M bristle/DSMG bristly/TR britches/M brittle/PRMT brittleness/M bro/SMH broach/MDSG broad/SMNRYXTP broadband/M broadcast/AMGS broadcaster/MS broadcasting/M broadcloth/M broaden/DG broadloom/M broadminded broadness/M broadsheet/SM broadside/MGDS broadsword/SM brocade/DSMG broccoli/M brochette/SM brochure/MS brogan/SM brogue/SM broil/SMDRZG broiler/M broke broken/YP brokenhearted/Y brokenness/M broker/SMDG brokerage/MS brolly/S bromide/SM bromidic bromine/M bronc/SM bronchi bronchial bronchitic bronchitis/M bronchus/M bronco/SM broncobuster/SM brontosaur/MS brontosaurus/MS bronze/DSMG brooch/MS brood/SMDRZG brooder/M broodily brooding/MY broodmare/MS broody/RMPT brook/SMDG brooklet/SM broom/SM broomstick/MS broth/MRZ brothel/MS brother/MY brotherhood/MS brotherliness/M broths brougham/SM brought brouhaha/SM brow/MS browbeat/SNG brown/SMDRPTG brownfield brownie/MS brownish brownness/M brownout/SM brownstone/MS browse/DRSMZG browser/M brr bruin/SM bruise/DRSMZG bruiser/M bruising/M bruit/SDG brunch/MDSG brunet/SM brunette/MS brunt/M brush/MDSG brushoff/SM brushstroke/S brushwood/M brushwork/M brusque/RPYT brusqueness/M brutal/Y brutality/SM brutalization/M brutalize/GDS brute/SM brutish/PY brutishness/M bu bub/SM bubble/DSMG bubblegum/M bubbly/RMT bubo/M buboes buccaneer/SGMD buck/MDGS buckaroo/SM buckboard/MS bucket/SGMD bucketful/MS buckeye/MS buckle's buckle/UDSG buckler/MS buckram/M bucksaw/MS buckshot/M buckskin/MS buckteeth bucktooth/MD buckwheat/M buckyball/SM bucolic/MS bucolically bud/SM budded budding/S buddy/SM budge/DSG budgerigar/MS budget/SGMD budgetary budgie/SM buff/AMDGS buffalo/MDG buffaloes buffer/SMDG buffet/SMDGJ buffoon/SM buffoonery/M buffoonish bug's bug/CS bugaboo/SM bugbear/SM bugged/C bugger/SMDG buggery bugging/C buggy/RSMT bugle/DRSMZG bugler/M build/SMRZGJ builder/M building/M buildup/SM built/AI builtin bulb/MS bulbous bulge/DSMG bulgy/RT bulimarexia/M bulimia/M bulimic/SM bulk/MDGS bulkhead/MS bulkiness/M bulky/RTP bull/MDGS bulldog/SM bulldogged bulldogging bulldoze/ZGDRS bulldozer/M bullet/SM bulletin/MDGS bulletproof/SDG bullfight/SMRZG bullfighter/M bullfighting/M bullfinch/MS bullfrog/MS bullhead/MDS bullheaded/PY bullheadedness/M bullhorn/MS bullion/M bullish/YP bullishness/M bullock/SM bullpen/SM bullring/MS bullshit/MS! bullshitted/! bullshitter/SM! bullshitting/! bullwhip/S bully/DSMG bulrush/MS bulwark/MS bum/SM bumbag/S bumble/DRSZG bumblebee/SM bumbler/M bumf bummed bummer/SM bummest bumming bump/MDRZGS bumper/M bumph bumpiness/M bumpkin/MS bumptious/PY bumptiousness/M bumpy/PRT bun/SM bunch/MDSG bunchy/RT bunco/SMDG bundle/DSMG bung/MDGS bungalow/MS bungee/SM bunghole/MS bungle/DRSMZG bungler/M bunion/SM bunk's bunk/CDGS bunker/SM bunkhouse/SM bunkum/M bunny/SM bunt/MDGSJ bunting/M buoy/MDGS buoyancy/M buoyant/Y bur/SMY burble/DSMG burbs/M burden's burden/USGD burdensome burdock/M bureau/SM bureaucracy/SM bureaucrat/MS bureaucratic bureaucratically bureaucratization/M bureaucratize/GDS burg/MRZS burgeon/DSG burger/M burgh/MRZ burgher/M burghs burglar/MS burglarize/GDS burglarproof burglary/SM burgle/DSG burgomaster/SM burgundy/SM burial/ASM burka/S burl/MDS burlap/M burlesque/MGDS burliness/M burly/RPT burn/MDRZGSB burnable/SM burner/M burnish/ZGMDRS burnisher/M burnoose/MS burnout/MS burnt burp/MDGS burqa/S burr/MDGS burrito/MS burro/SM burrow/SMDRZG burrower/M bursa/M bursae bursar/SM bursary/SM bursitis/M burst/SMG bury/ADSG bus/AMS busboy/SM busby/SM bused busgirl/MS bush/MDSGJ bushel/SGMD bushiness/M bushing/M bushman/M bushmaster/SM bushmen bushwhack/DRSZG bushwhacker/M bushy/RPT busily business/MS businesslike businessman/M businessmen businessperson/SM businesswoman/M businesswomen busing/M busk/DRZGS buskin/SM busload/S buss/M bust/MDRZGS buster/M bustle/DSMG busty/RZT busy/DRSTGP busybody/SM busyness/M busywork/M but/ACS butane/M butch/MRSZ butcher/MDG butchery/SM butler/SM butt/MDRZGS butte/SM butted/A butter/MDG butterball/MS buttercream buttercup/SM butterfat/M butterfingered butterfingers/M butterfly/GDSM buttermilk/M butternut/SM butterscotch/M buttery/TRSM butting/A buttock/SM button's button/USDG buttonhole/DSMG buttonwood/MS buttress/MDSG butty/S buxom buy/ZGSMR buyback/SM buyer/M buyout/SM buzz/MDRSZG buzzard/MS buzzer/M buzzkill/SM buzzword/SM bx bxs by/M bye/SM bygone/SM bylaw/SM byline/SM bypass/GMDS bypath/M bypaths byplay/M byproduct/MS byre/S byroad/SM bystander/MS byte/MS byway/SM byword/SM byzantine c/IES ca cab/SMRZ cabal/MS cabala's caballero/MS cabana/SM cabaret/SM cabbage/MS cabbed cabbing cabby/SM cabdriver/SM cabin/MS cabinet/SM cabinetmaker/MS cabinetmaking/M cabinetry/M cabinetwork/M cable/MGDS cablecast/GMS cablegram/MS cabochon/SM caboodle/M caboose/SM cabriolet/SM cabstand/SM cacao/MS cache/MGDS cachepot/SM cachet/MS cackle/MZGDRS cackler/M cacophonous cacophony/SM cacti cactus/M cad/SM cadaver/SM cadaverous caddie/MDS caddish/YP caddishness/M caddying cadence/DSM cadenza/SM cadet/MS cadge/ZGDRS cadger/M cadmium/M cadre/MS caducei caduceus/M caesura/SM cafe/SM cafeteria/MS cafetiere/S caff/CS caffeinated caffeine/M caftan/MS cage/DSMG cagey cagier cagiest cagily caginess/M cagoule/S cahoot/MS caiman/MS cairn/MS caisson/SM caitiff/SM cajole/ZGLDRS cajolement/M cajoler/M cajolery/M cake/DSMG cakewalk/SM cal calabash/MS calaboose/SM calamari/SM calamine/M calamitous/Y calamity/SM calcareous calciferous calcification/M calcify/GNDS calcimine/DSMG calcine/DSG calcite/M calcium/M calculable/I calculate/AGNVDSX calculated/Y calculating/Y calculation/AM calculator/SM calculi calculus/M caldera/SM calendar/MDGS calender's calf/M calfskin/M caliber/SM calibrate/GNDSX calibration/M calibrator/SM calico/M calicoes californium/M caliper/SGMD caliph/M caliphate/MS caliphs calisthenic/S calisthenics/M calk/SGMD call/ASGMD calla/MS callable callback/MS called/U caller/MS calligrapher/SM calligraphic calligraphist/MS calligraphy/M calling/SM calliope/MS callosity/SM callous/PGDSY callousness/M callow/RPT callowness/M callus/MDSG calm/PSTGMDRY calmness/M caloric calorie/MS calorific calumet/MS calumniate/GNDS calumniation/M calumniator/MS calumnious calumny/SM calve/GDS calypso/MS calyx/MS cam/SM camaraderie/M camber/MDSG cambial cambium/SM cambric/M camcorder/SM came camel/MS camelhair camellia/MS cameo/MS camera/MS cameraman/M cameramen camerawoman/M camerawomen camerawork camiknickers camisole/SM camouflage/MZGDRS camouflager/M camp's camp/CSGD campaign/SMDRZG campaigner/M campanile/SM campanologist/MS campanology/M camper/MS campfire/SM campground/SM camphor/M camping/M campsite/SM campus/MS campy/TR camshaft/SM can't can/SMDRZG canal/MS canalization/M canalize/GDS canape/MS canard/MS canary/SM canasta/M cancan/MS cancel/DRSZG canceler/M cancellation/SM cancer/MS cancerous candelabra/SM candelabrum/M candid/YP candida candidacy/SM candidate/MS candidature/SM candidness/M candle/MZGDRS candlelight/M candlelit candlepower/M candler/M candlestick/MS candlewick/SM candor/M candy/GDSM candyfloss cane/SM canebrake/MS caner/M canine/MS canister/SM canker/GMDS cankerous cannabis/MS canned cannelloni/M cannery/SM cannibal/SM cannibalism/M cannibalistic cannibalization/M cannibalize/GDS cannily/U canniness/M canning cannon/GMDS cannonade/MGDS cannonball/SM cannot canny/UTR canoe/MDS canoeing canoeist/SM canola/M canon/MS canonical/Y canonization/SM canonize/DSG canoodle/DSG canopy/GDSM canst cant's cant/CZRDGS cantabile cantaloupe/SM cantankerous/PY cantankerousness/M cantata/MS canteen/MS canter/CM cantered cantering canticle/MS cantilever/MDGS canto/MS canton/MLS cantonal cantonment/MS cantor/MS canvas/MGDS canvasback/SM canvass/MDRSZG canvasser/M canyon/MGS cap/SMDRBZ capabilities capability/IM capable/I capably/I capacious/PY capaciousness/M capacitance/M capacities capacitor/SM capacity/IM caparison/MDGS cape/SM caper/GMD capeskin/M capillarity/M capillary/SM capital/MSY capitalism/M capitalist/SM capitalistic capitalistically capitalization/M capitalize/ADSG capitation/CSM capitol/SM capitulate/ADSXGN capitulation/AM caplet/MS capo/SM capon/MS capped/UA capping/UA cappuccino/SM caprice/SM capricious/PY capriciousness/M capsicum/SM capsize/DSG capstan/SM capstone/MS capsular capsule/DSMG capsulize/DSG capt captain/SMDG captaincy/SM caption/SMDG captious/YP captiousness/M captivate/DSGN captivation/M captivator/SM captive/SM captivity/SM captor/MS capture/ADSMG car/SMDRZG carafe/MS caramel/SM caramelize/DSG carapace/SM carat/MS caravan/SM caravansary/SM caravel/SM caraway/SM carbide/SM carbine/SM carbohydrate/SM carbolic carbon/MS carbonaceous carbonate/MGNDS carbonation/M carboniferous carbonize/GDS carborundum/M carboy/MS carbs carbuncle/SM carbuncular carburetor/SM carcass/MS carcinogen/SM carcinogenic/MS carcinogenicity/M carcinoma/MS card/ESGMD cardamom/SM cardamon/S cardboard/M carder/MS cardholder/S cardiac cardie/S cardigan/SM cardinal/SMY cardio cardiogram/SM cardiograph/M cardiographs cardiologist/MS cardiology/M cardiomyopathy cardiopulmonary cardiovascular cardsharp/MRZS cardsharper/M care/SM careen/DGS career/MDGS careerism careerist/SM carefree careful/YP carefuller carefullest carefulness/M caregiver/SM careless/PY carelessness/M carer/M caress/MDSG caret/MS caretaker/MS careworn carfare/M cargo/M cargoes carhop/MS caribou/SM caricature/MGDS caricaturist/SM caries/M carillon/SM caring/M carious carjack/JSDRZG carjacker/M carjacking/M carload/SM carmine/SM carnage/M carnal/Y carnality/M carnation/IMS carnelian/MS carnival/MS carnivora carnivore/SM carnivorous/PY carnivorousness/M carny/SM carob/MS carol/ZGMDRS caroler/M carom/GMDS carotene/M carotid/SM carousal/SM carouse/DRSMZG carousel/SM carouser/M carp/SZGMDR carpal/MS carpel/MS carpenter/MDGS carpentry/M carper/M carpet/MDGS carpetbag/MS carpetbagged carpetbagger/MS carpetbagging carpeting/M carpi carpool/SMDG carport/SM carpus/M carrel/MS carriage/SM carriageway/S carrier/M carrion/M carrot/MS carroty carry/ZGDRSM carryall/SM carrycot/S carryout carryover/MS carsick/P carsickness/M cart/SZGMDR cartage/M cartel/MS carter/M carthorse/SM cartilage/SM cartilaginous cartload/SM cartographer/SM cartographic cartography/M carton/MS cartoon/SMDG cartoonist/MS cartridge/MS cartwheel/GMDS carve/JZGDRS carver/M carvery/S carving/M caryatid/MS casaba/MS cascade/DSMG cascara/SM case/LDSJMG casebook/S cased/U caseharden/DGS casein/M caseload/MS casement/MS casework/ZMR caseworker/M cash/GMDS cashback/M cashbook/MS cashew/MS cashier/GSMD cashless cashmere/M casing/M casino/MS cask/SM casket/MS cassava/SM casserole/DSMG cassette/MS cassia/MS cassock/SM cassowary/SM cast/ASGM castanet/MS castaway/MS caste/JMZRS castellated caster/M castigate/DSGN castigation/M castigator/SM casting/AM castle/MGDS castoff/SM castor/MS castrate/GNXDS castration/M casual/PMYS casualness/M casualty/SM casuist/SM casuistic casuistry/M cat/SM cataclysm/MS cataclysmal cataclysmic catacomb/SM catafalque/MS catalepsy/M cataleptic/MS catalog/ZGSMDR cataloger/M catalpa/SM catalyses catalysis/M catalyst/MS catalytic/M catalyze/GDS catamaran/SM catapult/GMDS cataract/MS catarrh/M catastrophe/MS catastrophic catastrophically catatonia/M catatonic/SM catbird/SM catboat/SM catcall/GSMD catch/ZGJLMRS catchall/MS catcher/M catchment/MS catchpenny catchphrase/SM catchword/MS catchy/RT catechism/SM catechist/SM catechize/DSG categorical/Y categorization/MS categorize/GDS category/SM cater/ZGJDRS catercorner caterer/M caterpillar/MS caterwaul/SMDG catfish/MS catgut/M catharses catharsis/M cathartic/SM cathedral/SM catheter/SM catheterize/DSG cathode/SM cathodic catholic catholicity/M cation/MS catkin/MS catlike catnap/MS catnapped catnapping catnip/M catsuit/S cattail/SM catted cattery/S cattily cattiness/M catting cattle/M cattleman/M cattlemen catty/TPR catwalk/SM caucus/MDSG caudal/Y caught/U cauldron/MS cauliflower/SM caulk/ZGMDRS caulker/M causal/Y causality/SM causation/M causative cause/MZGDRS causeless causer/M causerie/SM causeway/SM caustic/SM caustically causticity/M cauterization/M cauterize/GDS caution/SMDG cautionary cautious/IY cautiousness/M cavalcade/MS cavalier/SMY cavalry/SM cavalryman/M cavalrymen cave/DRSMZG caveat/MS caveman/M cavemen cavern/MS cavernous/Y caviar/M cavil/ZGJMDRS caviler/M caving/M cavity/FSM cavort/DGS caw/SMDG cay/CSM cayenne/M cayuse/MS cc cease/CMGDS ceasefire/MS ceaseless/YP ceaselessness/M ceca cecal cecum/M cedar/MS cede/FAGSD ceder/MS cedilla/SM ceilidh ceilidhs ceiling/MS celandine/M celeb/S celebrant/SM celebrate/DSGNX celebration/M celebrator/SM celebratory celebrity/SM celeriac celerity/M celery/M celesta/MS celestial/Y celibacy/M celibate/MS cell/SMD cellar/MS cellist/SM cellmate/SM cello/MS cellophane/M cellphone/MS cellular/SM cellulite/M cellulitis celluloid/M cellulose/M cement/MDRZGS cementer/M cementum/M cemetery/SM cenobite/MS cenobitic cenotaph/M cenotaphs censer/MS censor/MDGS censored/U censorial censorious/PY censoriousness/M censorship/M censure/BDRSMZG censurer/M census/MDSG cent/SZMR centaur/SM centavo/SM centenarian/MS centenary/SM centennial/MYS center/MDG centerboard/SM centerfold/MS centerpiece/MS centigrade centigram/SM centiliter/MS centime/SM centimeter/MS centipede/SM central/SMY centralism centralist centrality/M centralization/CM centralize/CGDS centralizer/MS centrifugal/Y centrifuge/DSMG centripetal/Y centrism/M centrist/MS centurion/SM century/SM cephalic ceramic/SM ceramicist/SM ceramics/M ceramist/MS cereal/MS cerebellar cerebellum/SM cerebra cerebral cerebrate/GNDS cerebration/M cerebrovascular cerebrum/MS cerement/MS ceremonial/SMY ceremonious/UY ceremoniousness/M ceremony/SM cerise/M cerium/M cermet/M cert/S certain/UY certainty/USM certifiable certifiably certificate/MGNXDS certification/M certify/DSG certitude/IM certitudes cerulean/M cervical cervices cervix/M cesarean/MS cesium/M cessation/MS cession/KAFSM cesspit/S cesspool/MS cetacean/MS cf cg ch/IFVT chad/S chafe/GDS chaff/GMDS chaffinch/MS chagrin/GSMD chain's chain/UGDS chainsaw/MDGS chair/GMDS chairlift/MS chairman/M chairmanship/SM chairmen chairperson/SM chairwoman/M chairwomen chaise/MS chalcedony/M chalet/MS chalice/SM chalk/GMDS chalkboard/SM chalkiness/M chalky/PRT challenge/DRSMZG challenged/U challenger/M challis/M chamber/SMD chamberlain/MS chambermaid/MS chambray/M chameleon/SM chamois/M chamomile/MS champ/ZGMDS champagne/MS champion/GMDS championship/MS chance/MGDS chancel/SM chancellery/SM chancellor/MS chancellorship/M chancery/SM chanciness/M chancre/SM chancy/PRT chandelier/SM chandler/MS change/MZGDRS changeability/M changeable/P changeableness/M changeably changed/U changeless/Y changeling/SM changeover/SM changer/M changing/U channel/GSMD channelization/M channelize/DSG chanson/SM chant/ZGMDRS chanter/M chanteuse/MS chantey/SM chanticleer/MS chaos/M chaotic chaotically chap/SM chaparral/SM chapati/S chapatti/S chapbook/MS chapeau/SM chapel/MS chaperon/MDGS chaperonage/M chaperoned/U chaplain/MS chaplaincy/SM chaplet/SM chapped chapping chappy/S chapter/SM char/SM charabanc/MS character/MS characterful characteristic/SM characteristically/U characterization/MS characterize/DSG characterless charade/SM charbroil/GDS charcoal/MS chard/M chardonnay/SM charge/AESDGM chargeable/A charged/U charger/SM charily chariness/M chariot/SM charioteer/MS charisma/M charismatic/MS charitable/P charitableness/M charitably/U charity/SM charlady/S charlatan/SM charlatanism/M charlatanry/M charlie/S charm/ZGMDRS charmer/M charming/Y charmless charred charring chart/GMDS charted/U charter's charter/ASGD charterer/MS chartreuse/M charwoman/M charwomen chary/TRP chase/MZGDRS chaser/M chasm/MS chassis/M chaste/PYTR chasten/DGS chasteness/M chastise/DRSZGL chastisement/SM chastiser/M chastity/M chasuble/SM chat/SM chateau/SM chateaux chatelaine/SM chatline/S chatted chattel/MS chatter/MDRZGS chatterbox/MS chatterer/M chattily chattiness/M chatting chatty/TPR chauffeur/GMDS chauvinism/M chauvinist/SM chauvinistic chauvinistically cheap/PXTNRY cheapen/DG cheapness/M cheapo cheapskate/MS cheat/ZGMDRS cheater/M check/AGMDS checkbook/SM checkbox checked/U checker/MDGS checkerboard/SM checkers/M checklist/MS checkmate/MGDS checkoff/SM checkout/SM checkpoint/SM checkroom/MS checksum checkup/MS cheddar/M cheek/GMDS cheekbone/SM cheekily cheekiness/M cheeky/TPR cheep/GMDS cheer/ZGMDRS cheerer/M cheerful/YP cheerfuller cheerfullest cheerfulness/M cheerily cheeriness/M cheerio/MS cheerleader/SM cheerless/PY cheerlessness/M cheery/TPR cheese/MGDS cheeseboard/S cheeseburger/SM cheesecake/SM cheesecloth/M cheeseparing/M cheesiness/M cheesy/TPR cheetah/M cheetahs chef/SM chem chemical/SMY chemise/MS chemist/MS chemistry/M chemo/M chemotherapeutic chemotherapy/M chemurgy/M chenille/M cherish/DSG cheroot/MS cherry/SM chert/M cherub/MS cherubic cherubim chervil/M chess/M chessboard/MS chessman/M chessmen chest/MDS chesterfield/SM chestful/SM chestnut/SM chesty/TR chevalier/SM cheviot/M chevron/MS chew/SZGMDR chewer/M chewiness/M chewy/PTR chg chge chi/SM chiaroscuro/M chic/PTMR chicane/MS chicanery/SM chichi/MS chick/XMNS chickadee/SM chicken/MDG chickenfeed/M chickenhearted chickenpox/M chickenshit/S! chickpea/SM chickweed/M chicle/M chicness/M chicory/SM chide/GDS chiding/Y chief/TMRYS chiefdom/M chieftain/MS chieftainship/SM chiffon/M chiffonier/MS chigger/MS chignon/MS chihuahua/SM chilblain/SM child/M childbearing/M childbirth/M childbirths childcare/M childhood/SM childish/YP childishness/M childless/P childlessness/M childlike childminder/S childminding childproof/GSD children/M chili/M chilies chill/JPZTGMDRS chiller/M chilliness/M chilling/Y chillness/M chilly/TPR chime/MZGDRS chimer/M chimera/MS chimeric chimerical chimney/MS chimp/MS chimpanzee/SM chin/SM china/M chinaware/M chinchilla/MS chine/MS chink/GMDS chinless chinned chinning chino/MS chinstrap/MS chintz/M chintzy/RT chinwag/S chip/SM chipboard chipmunk/SM chipolata/S chipped chipper/MS chippie chipping/S chippy/S chirography/M chiropodist/MS chiropody/M chiropractic/SM chiropractor/SM chirp/GMDS chirpily chirpy/PTR chirrup/GMDS chisel/ZGMDRS chiseler/M chit/SM chitchat/SM chitchatted chitchatting chitin/M chitinous chitosan chitterlings/M chivalrous/PY chivalrousness/M chivalry/M chive/MS chivy/GDS chlamydia/MS chlamydiae chloral/M chlordane/M chloride/MS chlorinate/GNDS chlorination/M chlorine/M chlorofluorocarbon/SM chloroform/SGMD chlorophyll/M chloroplast/MS chm choc/S chock/GMDS chockablock chocoholic/SM chocolate/MS chocolaty choice/MTRS choir/MS choirboy/MS choirmaster/SM choke/MZGDRS chokecherry/SM choker/M cholecystectomy cholecystitis choler/M cholera/M choleric cholesterol/M chomp/ZGMDRS choose/ZGRS chooser/M choosiness/M choosy/TPR chop/SM chophouse/SM chopped chopper/MDGS choppily choppiness/M chopping choppy/TPR chopstick/SM choral/MYS chorale/MS chord/MS chordal chordate/SM chore/MS chorea/M choreograph/DRZG choreographer/M choreographic choreographically choreographs choreography/M chorister/SM choroid/MS chortle/MZGDRS chortler/M chorus/GMDS chose chosen chow/SGMD chowder/MS chrism/M christen/ASGD christening/MS christian/U christology chromatic chromatically chromatin/M chromatography chrome/MGDS chromium/M chromosomal chromosome/MS chronic chronically chronicle/DRSMZG chronicler/M chronograph/M chronographs chronological/Y chronologist/MS chronology/SM chronometer/SM chrysalis/MS chrysanthemum/MS chub/SM chubbiness/M chubby/TPR chuck/GMDS chuckhole/SM chuckle/MGDS chuffed chug/SM chugged chugging chukka/MS chum/SM chummed chummily chumminess/M chumming chummy/PTR chump/MS chunder/GDS chunk/GMDS chunkiness/M chunky/PTR chunter/DGS church/MS churchgoer/SM churchgoing/M churchman/M churchmen churchwarden/MS churchwoman churchwomen churchyard/SM churl/MS churlish/PY churlishness/M churn/ZGMDRS churner/M chute/MS chutney/MS chutzpah/M chyme/M ciabatta/SM ciao/S cicada/MS cicatrices cicatrix/M cicerone/SM ciceroni cider's cider/S cigar/MS cigarette/MS cigarillo/MS cilantro/M cilia cilium/M cinch/GMDS cinchona/SM cincture/SM cinder/GMDS cine cinema/MS cinematic cinematographer/MS cinematographic cinematography/M cinnabar/M cinnamon/M cipher's cipher/CGDS cir circa circadian circle/MGDS circlet/MS circuit/MDGS circuital circuitous/YP circuitousness/M circuitry/M circuity/M circular/SMY circularity/M circularize/DSG circulate/ADSG circulation/SM circulatory circumcise/XDSGN circumcised/U circumcision/M circumference/MS circumferential circumflex/MS circumlocution/MS circumlocutory circumnavigate/XGNDS circumnavigation/M circumpolar circumscribe/GDS circumscription/MS circumspect/Y circumspection/M circumstance/MGDS circumstantial/Y circumvent/DSG circumvention/M circus/MS cirque/MS cirrhosis/M cirrhotic/SM cirri cirrus/M cistern/MS cit citadel/MS citation/AMS cite's cite/IAGSD citified citizen/MS citizenry/M citizenship/M citric citron/MS citronella/M citrus/MS city/SM citywide civet/MS civic/S civics/M civil/UY civilian/MS civility/ISM civilization/MS civilize/GDS civilized/U civvies/M ck cl clack/GMDS clad/U cladding/M claim's claim/CKEAGDS claimable/A claimant/MS claimed/U claimer/ECSM clairvoyance/M clairvoyant/MS clam/SM clambake/MS clamber/ZGMDRS clamberer/M clammed clammily clamminess/M clamming clammy/PTR clamor/GMDS clamorous clamp/GMDS clampdown/MS clan/SM clandestine/Y clang/ZGMDRS clangor/M clangorous/Y clank/GMDS clannish/P clannishness/M clansman/M clansmen clanswoman clanswomen clap/SM clapboard/MDGS clapped clapper/MS clapperboard/S clapping/M claptrap/M claque/MS claret/MS clarification/M clarify/XDSNG clarinet/SM clarinetist/SM clarion/MDGS clarity/M clash/GMDS clasp's clasp/UGDS class/GMDS classic/MS classical/MY classicism/M classicist/MS classifiable classification/CAM classifications classified's classified/U classifieds classifier/MS classify/ACSDGN classiness/M classless/P classmate/MS classroom/MS classwork/M classy/TRP clatter/GMDS clausal clause/MS claustrophobia/M claustrophobic clavichord/SM clavicle/MS clavier/MS claw's claw/CSGD clay/M clayey clayier clayiest clean/BJPZTGDRYS cleaner/M cleaning/M cleanliness/UM cleanly/UTPR cleanness/UM cleanse/ZGDRS cleanser/M cleanup/MS clear/JPTGMDRYS clearance/SM clearheaded clearing/M clearinghouse/SM clearness/M clearway/S cleat/MS cleavage/MS cleave/ZGDRS cleaver/M clef/SM cleft/MS clematis/MS clemency/IM clement/Y clementine/S clench/GMDS clerestory/SM clergy/SM clergyman/M clergymen clergywoman/M clergywomen cleric/MS clerical/Y clericalism/M clerk/GMDS clerkship/M clever/PTRY cleverness/M clevis/MS clew/SGMD cliche/MDS click/BZGMDRS clicker/M client/MS clientele/MS cliff/MS cliffhanger/SM cliffhanging clifftop/S clii climacteric/M climactic climate/SM climatic climatically climatologist/SM climatology/M climax/MDSG climb/SMDRZGB climber/M climbing/M clime/SM clinch/MDRSZG clincher/M cling/SMRZG clinger/M clingfilm clingy/RT clinic/SM clinical/Y clinician/SM clink/SMDRZG clinker/M cliometric/S cliometrician/MS cliometrics/M clip/SM clipboard/MS clipped clipper/SM clipping/SM clique/SM cliquey cliquish/YP cliquishness/M clit/SM clitoral clitorides clitoris/MS clix cloaca/M cloacae cloak's cloak/USDG cloakroom/MS clobber/SMDG cloche/SM clock/SMDG clockwise clockwork/SM clod/MS cloddish clodhopper/MS clog's clog/US clogged/U clogging/U cloisonne/M cloister/SMDG cloistral clomp/SDG clonal clone/DSMG clonk/SMDG clop/MS clopped clopping close/DRSMYTGJP closefisted closemouthed closeness/M closeout/MS closet/SMDG closeup/SM closing/M closure/ESM clot/MS cloth/M clothe/UDSG clotheshorse/MS clothesline/SM clothespin/SM clothier/MS clothing/M cloths clotted clotting cloture/SM cloud/SMDG cloudburst/SM clouded/U cloudiness/M cloudless cloudy/RPT clout/SMDG clove/RSMZ cloven clover/M cloverleaf/SM cloverleaves clown/SMDG clownish/YP clownishness/M cloy/DGS cloying/Y club/MS clubbable clubbed clubber/S clubbing clubfeet clubfoot/MD clubhouse/SM clubland cluck/SMDG clue/MGDS clueless clump/SMDG clumpy/TR clumsily clumsiness/M clumsy/TRP clung clunk/SMDRZG clunker/M clunky/TR cluster/MDSG clutch/GMDS clutter/MDSG cluttered/U clvi clvii clxi clxii clxiv clxix clxvi clxvii cm cnidarian/MS co/ESD coach/MDSG coachload/S coachman/M coachmen coachwork coadjutor/MS coagulant/MS coagulate/GNDS coagulation/M coagulator/MS coal/MDGS coalesce/GDS coalescence/M coalescent coalface/MS coalfield/S coalition/MS coalitionist/MS coalmine/S coarse/RYTP coarsen/SDG coarseness/M coast/SMDRZG coastal coaster/M coastguard/S coastline/MS coat/MDGJS coating/M coatroom/S coattail/SM coauthor/MDGS coax/DRSZG coaxer/M coaxial coaxing/Y cob/SM cobalt/M cobber/S cobble/DRSMZG cobbler/M cobblestone/SM cobnut/S cobra/SM cobweb/SM cobwebbed cobwebby/RT coca/M cocaine/M cocci/S coccus/M coccyges coccyx/M cochineal/M cochlea/SM cochleae cochlear cock/MDGS cockade/SM cockamamie cockatoo/SM cockatrice/SM cockchafer/S cockcrow/SM cockerel/SM cockeyed cockfight/MGS cockfighting/M cockily cockiness/M cockle/SM cockleshell/SM cockney/SM cockpit/SM cockroach/MS cockscomb/SM cocksucker/MS! cocksure cocktail/MS cocky/RTP coco/MS cocoa/SM coconut/SM cocoon/SMDG cod/SM coda/MS codded codding coddle/DSG code's code/CZGDRS codeine/M codependency/M codependent/SM coder/CM codex/M codfish/MS codger/SM codices codicil/SM codification/M codifier/M codify/XDRSNZG codon/S codpiece/MS codswallop coed/MS coeducation/M coeducational coefficient/MS coelenterate/MS coequal/MYS coerce/DRSZGNV coercer/M coercion/M coeval/SMY coexist/DSG coexistence/M coexistent coextensive coffee/SM coffeecake/SM coffeehouse/MS coffeemaker/SM coffeepot/MS coffer/SM cofferdam/MS coffin/SMDG cog/SM cogency/M cogent/Y cogitate/DSXGNV cogitation/M cogitator/MS cognac/SM cognate/MS cognition/AM cognitional cognitive/Y cognizable cognizance/AM cognizant cognomen/SM cognoscente/M cognoscenti cogwheel/SM cohabit/SGD cohabitant/MS cohabitation/M coheir/SM cohere/DSG coherence/IM coherency/M coherent/IY cohesion/M cohesive/YP cohesiveness/M coho/MS cohort/SM coif/MS coiffed coiffing coiffure/DSMG coil's/A coil/UADGS coin/MDRZGS coinage/SM coincide/DSG coincidence/MS coincident coincidental/Y coiner/M coinsurance/M coir coital coitus/M coke/MGDS col/S cola/MS colander/SM cold/MRYTPS coldblooded coldness/M coleslaw/M coleus/MS coley/S colic/M colicky coliseum/MS colitis/M coll collaborate/DSXGNV collaboration/M collaborationist collaborative/Y collaborator/MS collage/SM collagen collapse/MGDS collapsible collar/SMDG collarbone/SM collard/SM collarless collate/DSXGN collateral/MY collateralize collation/M collator/MS colleague/MS collect's collect/ASGVD collected/U collectedly collectible/SM collection/AMS collective/MYS collectivism/M collectivist/SM collectivization/M collectivize/DSG collector/MS colleen/SM college/SM collegiality/M collegian/MS collegiate collide/DSG collie/RSMZ collier/M colliery/SM collision/SM collocate/MGNDSX collocation/M colloid/SM colloidal colloq colloquial/Y colloquialism/SM colloquies colloquium/MS colloquy/M collude/DSG collusion/M collusive cologne/SM colon/SM colonel/SM colonelcy/M colones colonial/SMY colonialism/M colonialist/MS colonist/SM colonization/ACM colonize/CAGSD colonizer/MS colonnade/MDS colonoscopy/SM colony/SM colophon/SM color's color/AEGDS colorant/SM coloration/EM coloratura/MS colorblind/P colorblindness/M colored's colored/U coloreds colorfast/P colorfastness/M colorful/PY colorfulness/M coloring's colorist/S colorization/M colorize/DSG colorless/PY colorlessness/M colorway/S colossal/Y colossi colossus/M colostomy/SM colostrum/M colt/MS coltish columbine/SM column/SMD columnar columnist/SM com/JL coma/MS comaker/SM comatose comb/MDRZGJS combat/SMDGV combatant/SM combativeness/M combed/U comber/M combination/SM combine's combine/ADSG combined/U combiner/MS combings/M combo/SM combustibility/M combustible/MS combustion/M combustive come/IMZGRS comeback/MS comedian/MS comedic comedienne/MS comedown/MS comedy/SM comeliness/M comely/RPT comer's comestible/SM comet/SM comeuppance/SM comfit's comfit/ES comfort/ESMDG comfortable/P comfortableness/M comfortably/U comforter/MS comforting/Y comfortless comfy/RT comic/SM comical/Y comicality/M coming/M comity/M comm comma/SM command/SMDRLZG commandant/MS commandeer/GDS commander/M commandment/MS commando/SM commemorate/XGNVDS commemoration/M commemorator/MS commence/ADSLG commencement/AM commencements commend/ASDBG commendably commendation/AMS commendatory commensurable commensurate/IY comment/GSMD commentary/SM commentate/DSG commentator/SM commerce/M commercial/SMY commercialism/M commercialization/M commercialize/GDS commie/SM commingle/DSG commiserate/GNVDSX commiseration/M commissar/SM commissariat/SM commissary/SM commission's commission/ACSGD commissionaire/S commissioner/SM commit/AS commitment/MS committal/SM committed/AU committee/SM committeeman/M committeemen committeewoman/M committeewomen committer/S committing/A commode's commode/EIS commodification commodious/Y commodity/SM commodore/SM common's common/UPRYT commonality/S commonalty/M commoner/MS commonness/UM commonplace/MS commons commonsense commonweal/MH commonwealth/M commonwealths commotion/SM communal/Y commune/XDSMGN communicability/M communicable/I communicably communicant/MS communicate/GNVDSX communication/M communicative/U communicator/SM communion/M communique/SM communism/M communist/SM communistic community/SM commutation/MS commutative commutator/SM commute/BDRSMZG commuter/M comp/MDYGS compact/TGSMDRYP compaction compactness/M compactor/SM companion/SBM companionably companionship/M companionway/MS company/SM comparability/M comparable/I comparably/I comparative/MYS compare/BDSMG comparison/MS compartment/SM compartmental compartmentalization/M compartmentalize/DSG compass/GMDS compassion/M compassionate/Y compatibility/IM compatible/IMS compatibly/I compatriot/MS compeer/SM compel/S compelled compelling/Y compendious compendium/SM compensate/DSXGN compensated/U compensation/M compensatory compere/DSG compete/DSG competence/IM competences competencies competency/IM competent/IY competition/SM competitive/PY competitiveness/M competitor/SM compilation/SM compile/DRSZG compiler/M complacence/M complacency/M complacent/Y complain/DRZGS complainant/MS complainer/M complaint/SM complaisance/M complaisant/Y complected complement/SGMD complementary complete/PYTGNXDRS completed/U completeness/IM completion/M complex/MSY complexion/MDS complexional complexity/SM compliance/M compliant/Y complicate/GDS complicated/Y complication/M complicit complicity/M compliment/MDGS complimentary/U comply/NDSXG compo/S component/SM comport/LSGD comportment/M compose/AECGSD composedly composer/MS composite/MYGNXDS composition/CM compositor/SM compost/SGMD composure/EM compote/SM compound/GMDBS compounded/U comprehend/SDG comprehensibility/IM comprehensible/I comprehensibly/I comprehension/IM comprehensions comprehensive/PMYS comprehensiveness/M compress's compress/CGDS compressed/U compressible compression/CM compressor/SM comprise/GDS compromise/MGDS comptroller/MS compulsion/MS compulsive/YP compulsiveness/M compulsorily compulsory/SM compunction/SM computation/SM computational/Y compute/ADSG computer/MS computerate computerization/M computerize/GDS computing/M comrade/SMY comradeship/M con/GSM concatenate/XDSGN concatenation/M concave/YP concaveness/M conceal/SDRZGBL concealed/U concealer/M concealment/M conceit/SMD conceited/PY conceitedness/M conceivable/I conceivably/I conceive/DSGB concentrate/DSMGNX concentration/M concentric concentrically concept/SM conception/SM conceptional conceptual/Y conceptualization/MS conceptualize/DSG concern/UMD concerned/UY concerning concerns concert's concert/ESDG concerted/Y concertgoer/S concertina/SGMD concertize/DSG concertmaster/MS concerto/SM concessionaire/MS concessional concessionary conch/M conchie/S conchs concierge/MS conciliate/DSGN conciliation/AM conciliator/SM conciliatory concise/RPYTN conciseness/M concision/M conclave/SM conclude/DSG conclusion/MS conclusive/IYP conclusiveness/IM concoct/SDG concoction/MS concomitant/MYS concord/M concordance/SM concordant concordat/SM concourse/SM concrete/DSPMYGNX concreteness/M concretion/M concubinage/M concubine/MS concupiscence/M concupiscent concur/S concurred concurrence/SM concurrency concurring concuss/V concussion/SM condemn/SDRZG condemnation/MS condemnatory condemner/M condensate/MNXS condensation/M condense/DRSZG condenser/M condescending/Y condescension/M condign condiment/MS condition's condition/AGSD conditional/SMY conditioned/U conditioner/SM conditioning/M condo/SM condolence/SM condom/SM condominium/MS condone/DSG condor/SM conduce/DSGV conduct/MDGV conductance/M conductibility/M conductible conduction/M conductivity/M conductor/MS conductress/MS conduit/SM cone/M coneys confab/SM confabbed confabbing confabulate/XDSGN confabulation/M confection/SZMR confectioner/M confectionery/SM confederacy/SM confederate/M confer/S conferee/SM conference/MGS conferrable conferral/M conferred conferrer/MS conferring confessed/Y confession/SM confessional/SM confessor/MS confetti/M confidant/MS confidante/SM confide/DRSZG confidence/SM confident/Y confidential/Y confidentiality/M confider/M confiding/Y configuration/S configure/B confined/U confinement/MS confirm/ASDG confirmation/ASM confirmatory confirmed/U confiscate/DSGNX confiscation/M confiscator/SM confiscatory conflagration/MS conflate/XDSGN conflation/M conflict/SGMD confluence/MS confluent conform/ZB conformable/U conformance/M conformism/M conformist/SM conformity/M confrere/MS confrontation/SM confrontational confuse/RZ confused/Y confusing/Y confutation/M confute/DSG conga/SMDG congeal/SLDG congealment/M conger/SM congeries/M congest/SDGV congestion/M conglomerate/DSXMGN conglomeration/M congrats/M congratulate/XGNDS congratulation/M congratulatory congregant/MS congregate/GNDSX congregation/M congregational congregationalism/M congregationalist/MS congress/MS congressional congressman/M congressmen congresspeople congressperson/MS congresswoman/M congresswomen congruence/M congruent/Y congruity/ISM congruous conic/SM conical/Y conifer/SM coniferous conjectural conjecture/MGDS conjoint conjugal/Y conjugate/DSXGN conjugation/M conjunct/VMS conjunctiva/SM conjunctive/SM conjunctivitis/M conjuration/MS conjure/DRSZG conjurer/M conk/MDRZ conman connect/AEDVGS connectable connected/U connection/EMS connective/MS connectivity/M connector/MS conned conning conniption/MS connivance/M connive/DRSZG conniver/M connoisseur/SM connotative connubial conquer/ASDG conquerable/U conquered/U conqueror/MS conquest/AM conquistador/SM cons/DSG consanguineous consanguinity/M conscienceless conscientious/PY conscientiousness/M conscious/UYP consciousness/UM consciousnesses conscription/M consecrate/ADSGN consecrated/U consecration/AM consecrations consecutive/Y consensual consensus/MS consent/SMDG consequence/SM consequent/Y consequential/IY conservancy/SM conservation/M conservationism/M conservationist/SM conservatism/M conservative/MYS conservatoire/S conservator/SM conservatory/SM consider/AGSD considerable/I considerably considerate/IPYN considerateness/IM consideration/AIM considerations considered/U consign/ASDG consignee/MS consignment/MS consist/SDG consistence/MS consistency/ISM consistent/IY consistory/SM consolable/I consolation/MS consolatory consolidate/XDSGN consolidated/U consolidation/M consolidator/MS consoling/Y consomme/M consonance/SM consonant/SMY consortia consortium/M conspectus/MS conspicuous/IPY conspicuousness/IM conspiracy/SM conspirator/MS conspiratorial/Y conspire/GD constable/SM constabulary/SM constancy/IM constant/MYS constellation/SM consternation/M constipate/GNDS constipation/M constituency/SM constituent/SM constitute/ADSGNV constitution/AM constitutional/MYS constitutionalism constitutionality/UM constitutions constrained/U constraint/SM constrict/GVSD constriction/SM constrictor/SM construable construct's construct/CADVGS construction/CAMS constructional constructionist's constructionist/CS constructive/YP constructiveness/M constructor/MS construe/GDS consul/KSM consular/K consulate/SM consulship/M consult/GSD consultancy/SM consultant/MS consultation/MS consultative consumable/SM consume/BDRSZG consumed/U consumer/M consumerism/M consumerist/MS consummate/YGNXDS consummated/U consumption/M consumptive/SM cont contact/ASDG contactable contagion/MS contagious/PY contagiousness/M contain/SBLDRZG container/M containerization/M containerize/DSG containment/M contaminant/SM contaminate/ACDSG contaminated/U contamination/CM contaminator/SM contd contemn/SDG contemplate/DSGNV contemplation/M contemplative/SMY contemporaneity/M contemporaneous/Y contempt/M contemptible contemptibly contemptuous/YP contemptuousness/M contender/MS content/ESLMDG contented/EY contentedness/M contention/SM contentious/YP contentiousness/M contently contentment/EM conterminous/Y contestable/I contestant/MS contested/U contextualization contextualize/DSG contiguity/M contiguous/Y continence/IM continent/SM continental/SM contingency/SM contingent/SMY continua continual/Y continuance/EMS continuation/EMS continue/EGDS continuity/ESM continuous/EY continuum/M contort/GD contortion/MS contortionist/SM contraband/M contrabassoon/S contraception/M contraceptive/SM contract/MDG contractible contractile contraction/S contractual/Y contradict/SDG contradiction/SM contradictory contradistinction/MS contraflow/S contrail/MS contraindicate/GNXDS contraindication/M contralto/SM contraption/SM contrapuntal/Y contrarian/SM contrarianism contrariety/M contrarily contrariness/M contrariwise contrary/PSM contrast/MDGS contravene/GDS contravention/SM contretemps/M contribute/XGND contribution/M contributor/MS contributory contrition/M contrivance/MS contrive/ZGDRS contriver/M control's control/CS controllable/U controlled/UC controller/MS controlling/C controversial/Y controversy/SM controvert/DSG controvertible/I contumacious/Y contumacy/M contumelious contumely/SM contuse/XDSGN contusion/M conundrum/SM conurbation/MS convalesce/DSG convalescence/MS convalescent/SM convection/M convectional convective convector/S convene/ADSG convener/MS convenience/IMS convenient/IY convent/SM conventicle/MS convention/SM conventional/UY conventionality/UM conventionalize/GDS conventioneer/S convergence/MS convergent conversant conversation/MS conversational/Y conversationalist/SM converse/Y convert's convert/AGSD converted/U converter/SM convertibility/M convertible/SM convex/Y convexity/M convey/SBDG conveyance/MGS conveyor/MS convict/GSMD conviction/MS convince/GDS convinced/U convincing/UY convivial/Y conviviality/M convoke/DSG convoluted convolution/MS convoy/SMDG convulse/GNVXDS convulsion/M convulsive/Y cony/M coo/GSMD cook's cook/ADGS cookbook/MS cooked/U cooker/SM cookery/SM cookhouse/S cookie/SM cooking/M cookout/SM cookware/SM cool/MDRYZTGPS coolant/SM cooler/M coolie/SM coolness/M coon/MS! coonskin/MS coop/MDRZGS cooper/MDG cooperage/M cooperate/DSGNV cooperation/M cooperative/PMYS cooperativeness/M cooperator/SM coordinate/DSMYGN coordinated/U coordination/M coordinator/MS coot/MS cootie/SM cop/GJSMD copacetic copay/M cope/MS copier/SM copilot/SM coping/M copious/PY copiousness/M copped copper/SM copperhead/SM copperplate/M coppery copping copra/M copse/SM copter/SM copula/SM copulate/GNVDS copulation/M copulative/SM copy's copy/ADSG copybook/SM copycat/MS copycatted copycatting copyist/MS copyleft copyright/GSMD copywriter/MS coquetry/SM coquette/DSMG coquettish/Y cor coracle/SM coral/SM corbel/SM cord/EASGDM cordage/M cordial/SMY cordiality/M cordillera/MS cordite/M cordless cordon/SMDG cordovan/M corduroy/MS corduroys/M core/MZGDRS coreligionist/S corer/M corespondent/MS corgi/SM coriander/M cork's cork/UDGS corkage corker/SM corkscrew/SMDG corm/MS cormorant/SM corn/MDRZGS cornball/MS cornbread/M corncob/MS corncrake/S cornea/SM corneal corner/GMD cornerstone/SM cornet/SM cornfield/S cornflakes/M cornflour cornflower/SM cornice/MS cornily corniness/M cornmeal/M cornrow/MDGS cornstalk/SM cornstarch/M cornucopia/MS corny/PRT corolla/MS corollary/SM corona/SM coronal/MS coronary/SM coronation/SM coroner/MS coronet/MS corp corpora corporal/SM corporate/XYN corporation/IM corporatism corporeal/Y corporeality/M corps/MS corpse/M corpsman/M corpsmen corpulence/M corpulent corpus/M corpuscle/MS corpuscular corr corral/SM corralled corralling correct/DRYTGVSBP corrected/U correction/SM correctional corrective/SM correctness/IM corrector correlate/XDSMGNV correlated/U correlation/M correlative/MS correspond/SDG correspondence/SM correspondent/SM corresponding/Y corridor/SM corrie/S corroborate/GNVDSX corroborated/U corroboration/M corroborator/SM corroboratory corrode/GDS corrosion/M corrosive/SMY corrugate/GNXDS corrugation/M corrupt/DRYPSTG corruptibility/IM corruptible/I corruption/MS corruptness/M corsage/MS corsair/MS corset/SGMD cortege/MS cortex/M cortical cortices cortisone/M corundum/M coruscate/GNDS coruscation/M corvette/SM cos/M cosh/DSG cosign/ZGSDR cosignatory/SM cosigner/M cosine/SM cosmetic/SM cosmetically cosmetician/MS cosmetologist/MS cosmetology/M cosmic cosmically cosmogonist/SM cosmogony/SM cosmological cosmologist/SM cosmology/SM cosmonaut/SM cosmopolitan/MS cosmopolitanism/M cosmos/MS cosplay cosponsor/GSMD cosset/SGD cossetted cossetting cost/MDYGSJ costar/SM costarred costarring costliness/M costly/PTR costume/MZGDRS costumer/M costumier/S cot/SM cotangent/MS cote/MS coterie/MS coterminous cotillion/SM cottage/MZGRS cottager/M cottar/SM cotter/SM cotton/SGMD cottonmouth/M cottonmouths cottonseed/MS cottontail/MS cottonwood/SM cottony cotyledon/MS couch/MDSG couchette/S cougar/SM cough/MDG coughs could could've couldn't coulee/SM coulis coulomb/MS council/MS councilman/M councilmen councilor/MS councilperson/SM councilwoman/M councilwomen counsel/JMDGS counselor/MS count/EASMDG countable/U countably countdown/MS counted/U countenance's countenance/EGDS counter/EMS counteract/SGVD counteraction/MS counterargument/S counterattack/GMDS counterbalance/MGDS counterblast/S counterclaim/GSMD counterclockwise counterculture/SM countered counterespionage/M counterexample/S counterfactual counterfeit/ZGMDRS counterfeiter/M counterfoil/MS countering counterinsurgency/SM counterintelligence/M counterman/M countermand/GMDS countermeasure/SM countermen counteroffensive/SM counteroffer/SM counterpane/SM counterpart/SM counterpetition counterpoint/MDGS counterpoise/MGDS counterproductive counterrevolution/SM counterrevolutionary/SM countersign/GSMD countersignature/MS countersink/GSM counterspy/SM countersunk countertenor/MS countervail/GSD counterweight/MS countess/MS countless countrified country/SM countryman/M countrymen countryside/MS countrywide countrywoman/M countrywomen county/SM countywide coup's coup/AS coupe/SM couple's couple/UCGSD couplet/MS coupling/SM coupon/SM courage/M courageous/YP courageousness/M courgette/S courier/MDSG course/EDGMS coursebook/S courser/MS coursework court/SMDYG courteous/EY courteousness/M courtesan/SM courtesy/ESM courthouse/MS courtier/SM courtliness/M courtly/PRT courtroom/MS courtship/MS courtyard/MS couscous/M cousin/SM couture/M couturier/MS cove/MS coven/SM covenant/MDSG cover's cover/AEUGDS coverage/M coverall/MS covering's coverings coverlet/MS covert/SPMY covertness/M covet/SDG covetous/YP covetousness/M covey/SM cow/ZGSMDR coward/SMY cowardice/M cowardliness/M cowbell/MS cowbird/MS cowboy/SM cowcatcher/MS cower/DG cowgirl/MS cowhand/MS cowherd/MS cowhide/MS cowl/MGSJ cowlick/MS cowling/M cowman/M cowmen coworker/MS cowpat/S cowpoke/MS cowpox/M cowpuncher/SM cowrie/SM cowshed/S cowslip/SM cox/GDS coxcomb/MS coxswain/MS coy/TPRY coyness/M coyote/SM coypu/SM cozen/SDG cozenage/M cozily coziness/M cozy/RSMTP cpd cpl cps crab/MS crabbed crabber/SM crabbily crabbiness/M crabbing crabby/PRT crabgrass/M crablike crabwise crack/SMDRYZGJ crackdown/MS cracker/M crackerjack/MS crackhead/MS crackle/DSJMG crackling/M crackpot/MS crackup/SM cradle/DSMG craft/SMDG craftily craftiness/M craftsman/M craftsmanship/M craftsmen craftspeople craftswoman/M craftswomen crafty/RTP crag/MS cragginess/M craggy/RPT cram/S crammed crammer/S cramming cramp/SMDG cramping/M crampon/SM cranberry/SM crane/DSMG cranial cranium/SM crank/SMDG crankcase/SM crankily crankiness/M crankshaft/MS cranky/PRT cranny/DSM crap/MS crape/SM crapped crapper/S crappie/RSMT crapping crappy craps/M crapshooter/MS crash/MDSG crass/RYTP crassness/M crate/DRSMZG crater/MDG cravat/SM crave/DSGJ craven/SMYP cravenness/M craving/M craw/MS crawdad/SM crawl/SMDRZG crawler/M crawlspace/SM crawly/TRSM cray/S crayfish/MS crayola/S crayon/GSMD craze/DSMG crazily craziness/M crazy/PRSMT creak/SMDG creakily creakiness/M creaky/RPT cream/SMDRZG creamer/M creamery/SM creamily creaminess/M creamy/RPT crease/ICGMSD create/KADSGNV creation's/K creation/ASM creationism/SM creationist/SM creative/SMYP creativeness/M creativity/M creator/MS creature/SM creche/SM cred credence/M credential/SGMD credenza/SM credibility/IM credible/I credibly/I credit/EGSBMD creditably/E creditor/SM creditworthy/P credo/SM credulity/IM credulous/IY credulousness/M creed/SM creek/SM creel/SM creep/SMRZG creeper/M creepily creepiness/M creepy/TPR cremains/M cremate/GNDSX cremation/M crematoria crematorium/MS crematory/SM creme/SM crenelate/XGNDS crenelation/M creole/SM creosote/MGDS crepe/SM crept crepuscular crescendo/CSM crescent/MS cress/M crest/SMDG crestfallen crestless cretaceous cretin/SM cretinism/M cretinous cretonne/M crevasse/SM crevice/MS crew/MDGS crewel/M crewelwork/M crewman/M crewmen crib/MS cribbage/M cribbed cribber/MS cribbing crick/SMDG cricket/MRSZG cricketer/M crier/M crikey crime/SM criminal/MYS criminality/M criminalize/CGDS criminologist/MS criminology/M crimp/SMDG crimson/SMDG cringe/DSMG crinkle/DSMG crinkly/RT crinoline/SM cripes cripple/DRSMZG crippler/M crippleware crippling/Y crises crisis/M crisp/SMDRYTGP crispbread/S crispiness/M crispness/M crispy/PRT crisscross/GMDS criteria criterion/M critic/SM critical/UY criticism/MS criticize/ZGDRS criticizer/M critique/MGDS critter/SM croak/SMDG croaky/RT crochet/SMDRZG crocheter/M crocheting/M crock/SMD crockery/M crocodile/SM crocus/MS croft/SRZG croissant/MS crone/SM crony/SM cronyism/M crook/SMDG crooked/PTRY crookedness/M crookneck/SM croon/SMDRZG crooner/M crop/MS cropland/SM cropped cropper/MS cropping croquet/M croquette/SM crosier/MS cross's cross/AUGTSD crossbar/SM crossbeam/MS crossbones/M crossbow/SM crossbowman/M crossbowmen crossbred crossbreed/SGM crosscheck/SMDG crosscurrent/MS crosscut/SM crosscutting crosser crossfire/MS crosshatch/GDS crossing/SM crossly crossness/M crossover/MS crosspatch/MS crosspiece/SM crossroad/MS crossroads/M crosstown crosswalk/MS crosswind/MS crosswise crossword/MS crotch/MS crotchet/SM crotchety crouch/GMDS croup/M croupier/M croupy/ZTR crouton/MS crow/MDGS crowbar/MS crowd/SMDG crowded/U crowdfund/SDG crowfeet crowfoot/SM crown/SMDG crowned/U crucial/Y crucible/SM crucifix/MS crucifixion/SM cruciform/SM crucify/DSG crud/M cruddy/TR crude/RMYTP crudeness/M crudites/M crudity/SM cruel/RYPT cruelness/M cruelty/SM cruet/SM cruft/SD crufty cruise/DRSMZG cruiser/M cruller/MS crumb/SMDYG crumble/MGDS crumbliness/M crumbly/TPR crumby/TR crumminess/M crummy/PTR crumpet/MS crumple/MGDS crunch/GMDRS crunchiness/M crunchy/TRP crupper/MS crusade/MZGDRS crusader/M cruse/SM crush/MDRSZG crusher/M crushing/Y crust/SMDG crustacean/SM crustal crustily crustiness/M crusty/TRP crutch/MS crux/MS cry/ZGJDRSM crybaby/SM cryogenic/S cryogenics/M cryonics cryosurgery/M crypt/SM cryptic cryptically cryptogram/SM cryptographer/SM cryptography/M crystal/SM crystalline crystallization/M crystallize/ADSG crystallographic crystallography ct ctn ctr cu cub/ZGSMDR cubbyhole/MS cube/MS cuber/M cubic cubical cubicle/MS cubism/M cubist/SM cubit/SM cuboid/S cuckold/MDSG cuckoldry/M cuckoo/SM cucumber/SM cud/SM cuddle/DSMG cuddly/TR cudgel/SGMDJ cue/DSMG cuff/MDGS cuisine/SM culinary cull/MDGS culminate/XDSGN culmination/M culotte/SM culpability/M culpable/I culpably culprit/SM cult/MS cultism/M cultist/MS cultivable cultivar/S cultivate/BDSGN cultivated/U cultivation/M cultivator/MS cultural/Y culture/MGDS cultured/U culvert/MS cum/SM cumber/SDG cumbersome/P cumbersomeness/M cumbrous cumin/M cummerbund/MS cumming cumulative/Y cumuli cumulonimbi cumulonimbus/M cumulus/M cuneiform/M cunnilingus/M cunning/MRYT cunt/MS! cup/SM cupboard/SM cupcake/MS cupful/SM cupid/SM cupidity/M cupola/SMD cuppa/S cupped cupping cupric cur/SMY curability/M curacao curacy/SM curare/M curate/DSMGV curative/MS curator/KMS curatorial curb/MDGS curbing/M curbside curbstone/SM curd/MS curdle/DSG cure's cure/KZGBDRS cured/U curer/KM curettage/M curfew/SM curia/M curiae curie/SM curio/SM curiosity/SM curious/YP curiousness/M curium/M curl's curl/UDGS curler/SM curlew/SM curlicue/DSMG curliness/M curling/M curly/RPT curmudgeon/MYS currant/MS currency/SM current's current/FAY currents curricula curricular curriculum/M curry/DSMG currycomb/SGMD curse/DSMGV cursed/Y cursive's cursive/EAY cursor/SM cursorily cursoriness/M cursory/P curt/RYTP curtail/GDSL curtailment/SM curtain/GMDS curtness/M curtsy/GDSM curvaceous/P curvaceousness/M curvature/SM curve/DSMG curvy/RT cushion/MDSG cushy/RT cusp/MS cuspid/SM cuspidor/SM cuss's cuss/FEGSD cussed/PY custard/MS custodial custodian/MS custodianship/M custody/M custom/SZMR customarily customary/U customer/M customhouse/SM customization/M customize/DSG cut/TSMR cutaneous cutaway/MS cutback/MS cute/YP cuteness/M cutesy/TR cutey/S cuticle/MS cutie/SM cutlass/MS cutler/SM cutlery/M cutlet/SM cutoff/SM cutout/SM cutter/SM cutthroat/SM cutting/MYS cuttlefish/MS cutup/SM cutworm/MS cw cwt cyan/M cyanide/M cyberbully/S cybercafe/S cybernetic/S cybernetics/M cyberpunk/SM cybersex cyberspace/MS cyborg/SM cyclamen/MS cycle/ADSMG cyclic cyclical/Y cyclist/MS cyclometer/MS cyclone/MS cyclonic cyclopedia/MS cyclopes cyclops/M cyclotron/MS cygnet/MS cylinder/MS cylindrical cymbal/MS cymbalist/MS cynic/SM cynical/Y cynicism/M cynosure/MS cypress/MS cyst/MS cystic cystitis cytologist/SM cytology/M cytoplasm/M cytoplasmic cytosine/M czar/MS czarina/SM czarism czarist/SM d'Arezzo/M d'Estaing/M d/NXGJ dB dab/SM dabbed dabber/MS dabbing dabble/ZGDRS dabbler/M dace/SM dacha/MS dachshund/MS dactyl/MS dactylic/MS dad/SM dadaism/M dadaist/MS daddy/SM dado/M dadoes daemon/MS daemonic daffiness/M daffodil/SM daffy/PTR daft/PTRY daftness/M dag/S dagger/MS dago/S dagoes daguerreotype/DSMG dahlia/MS dailiness/M daily/PSM daintily daintiness/M dainty/RSMTP daiquiri/MS dairy/GSM dairying/M dairymaid/MS dairyman/M dairymen dairywoman/M dairywomen dais/MS daisy/SM dale/SM dalliance/MS dallier/M dally/ZGDRS dalmatian/MS dam/SM damage/MGDS damageable damaged/U damages/M damask/MDGS dame/SM dammed damming dammit damn/SBGMD damnably damnation/M damned/T damp/SPXZTGMDNRY dampen/ZGDR dampener/M damper/M dampness/M damsel/MS damselfly/SM damson/MS dance/MZGDRS dancer/M dancing/M dandelion/SM dander/M dandify/GDS dandle/GDS dandruff/M dandy/TRSM dang/SZGDR danger/M dangerous/Y dangle/ZGDRS dangler/M danish/MS dank/PTRY dankness/M danseuse/MS dapper/TR dapple/MGDS dare/DRSMZG daredevil/MS daredevilry/M darer/M daresay daring/MY dark/PXTMNRY darken/ZGDR darkener/M darkie/S darkness/M darkroom/MS darling/MS darn/SZGMDR darned/TR darner/M dart/SZGMDR dartboard/MS darter/M dash/ZGMDRS dashboard/SM dasher/M dashiki/MS dashing/Y dastard/MYS dastardliness/M data database/SM datatype date/DRSMZGV datebook/S dated/U dateless dateline/MGDS dater/M dative/MS datum/M daub/SZGMDR dauber/M daughter/SMY daunt/GDS daunting/Y dauntless/YP dauntlessness/M dauphin/MS davenport/MS davit/MS dawdle/ZGDRS dawdler/M dawn/SGMD day/SM daybed/MS daybreak/M daycare/M daydream/MDRZGS daydreamer/M daylight/MS daylights/M daylong daytime/M daze/DSMG dazed/Y dazzle/MZGDRS dazzler/M dazzling/Y db dbl dc dd/SDG dded/K dding/K deacon/MS deaconess/MS dead/XTMNRY deadbeat/MS deadbolt/SM deaden/GD deadhead/SDG deadline/SM deadliness/M deadlock/GSMD deadly/TPR deadpan/MS deadpanned deadpanning deadwood/M deaf/PXTNR deafen/GD deafening/Y deafness/M deal/SJZGMR dealer/M dealership/SM dealing/M dealt dean/M deanery/SM deanship/M dear/SPTMRYH dearest/S dearness/M dearth/M dearths deary/SM death/MY deathbed/SM deathblow/MS deathless/Y deathlike deaths deathtrap/MS deathwatch/MS deaves deb/SM debacle/MS debarkation/M debarment/M debate/BMZR debater/M debating/M debauch/MDSG debauchee/MS debauchery/SM debenture/MS debilitate/DSGN debilitation/M debility/SM debit/D debonair/PY debonairness/M debouch/GDS debridement debris/M debt/SM debtor/MS debugger/S debut/GMD debutante/SM decade/MS decadence/M decadency/M decadent/MYS decaf/MS decaffeinate/DSG decagon/MS decal/MS decampment/M decapitate/XGNDS decapitator/MS decathlete/S decathlon/SM decay/GD deceased/M decedent/MS deceit/MS deceitful/YP deceitfulness/M deceive/UGDS deceiver/MS deceiving/Y decelerate/GNDS deceleration/M decelerator/SM decency/ISM decennial/SM decent/IY deception/MS deceptive/YP deceptiveness/M decibel/MS decidable/U decide/BZGDRS decided/Y deciduous deciliter/MS decimal/SM decimalization decimate/DSGN decimation/M decimeter/MS decipherable/UI decision/IM decisions decisive/IPY decisiveness/IM deck/SGMD deckchair/S deckhand/SM deckle/S declamation/MS declamatory declaration/MS declarative declaratory declare/DRSZGB declared/U declarer/M declension/SM declination/M decline/DRSMZG decliner/M declivity/SM decolletage/SM decollete decongestant/MS deconstructionism decor/MS decorate/AGNVDS decorating/M decoration/AM decorations decorative/Y decorator/MS decorous/IY decorousness/M decorum/M decoupage/DSMG decoy/GMDS decreasing/Y decree/MDS decreeing decremented decrements decrepit decrepitude/M decriminalization/M decry/GDS decryption dedicate/AGDS dedication/SM dedicator/SM dedicatory deduce/GDS deducible deduct/GVD deductible/SM deduction/SM deductive/Y deed/GD deejay/MS deem/ASGD deep/SPXTMNRY deepen/GD deepness/M deer/M deerskin/M deerstalker/S def/Z defacement/M defacer/SM defalcate/DSXGN defalcation/M defamation/M defamatory defame/ZGDRS defamer/M defaulter/SM defeat/MDRZGS defeated/U defeater/M defeatism/M defeatist/MS defecate/GNDS defecation/M defect/MDGVS defection/MS defective/MPYS defectiveness/M defector/MS defendant/SM defended/U defenestration/S defense/DSMGV defenseless/YP defenselessness/M defensible/I defensibly/I defensive/MYP defensiveness/M deference/M deferential/Y deferral/MS deferred deferring deffer deffest defiant/Y defibrillation defibrillator/S deficiency/SM deficient deficit/SM defilement/M definable/IU define/AGDS defined/U definer/MS definite/IYVP definiteness/IM definition/AM definitions definitive/Y deflate/GNDS deflation/M deflationary deflect/DGVS deflection/MS deflector/SM defogger/SM defoliant/SM defoliate/DSGN defoliation/M defoliator/MS deformity/SM defraud/DRZGS defrauder/M defrayal/M defrock/DG defroster/MS deft/PTRY deftness/M defunct defy/GDS deg degeneracy/M degenerate/MV degrade/B degree/MS dehydrator/SM dehydrogenase deicer/MS deification/M deify/NGDS deign/GDS deist/MS deistic deity/SM deject/GDS dejected/Y dejection/M delay/ZDR delectable delectably delectation/M delegate/GD delete/XGNDS deleterious deletion/M delft/M delftware/M deli/SM deliberate/XYVP deliberateness/M delicacy/ISM delicate/IY delicateness/M delicatessen/SM delicious/PY deliciousness/M delighted/Y delightful/Y deliminator delineate/GNXDS delineation/M delinquency/SM delinquent/SMY deliquesce/DSG deliquescent delirious/YP deliriousness/M delirium/SM deliver/ADGS deliverable deliverance/M delivered/U deliverer/SM dell/SM delphinium/MS delta/MS delude/GDS deluge/MGDS delusion/MS delusional delusive/Y deluxe delve/ZGDRS delver/M demagogic demagogically demagogue/SM demagoguery/M demagogy/M demand/GMDS demanding/U demarcate/DSGNX demarcation/M demean/GDS demeanor/M demented/Y dementia/M demesne/MS demigod/MS demigoddess/MS demijohn/SM demimondaine/SM demimonde/M demise/MGDS demitasse/MS demo/GMD democracy/SM democrat/MS democratic/U democratically democratization/M democratize/GDS demode demographer/SM demographic/SM demographically demographics/M demography/M demolish/DSG demolition/MS demon/MS demonetization/M demoniac demoniacal/Y demonic demonically demonize/GDS demonology/SM demonstrability demonstrable/I demonstrably demonstrate/XGNVDS demonstration/M demonstrative/MYSP demonstrativeness/M demonstrator/MS demote/GD demotic demount demulcent/SM demur/TMRS demure/PY demureness/M demurral/SM demurred demurrer/SM demurring den/M denationalization denaturation denature/DG dendrite/SM dengue/M deniability deniable/U denial/MS denier/M denigrate/DSGN denigration/M denim/MS denitrification denizen/MS denominational denotative denouement/MS denounce/LDSG denouncement/SM dense/PYTR denseness/M density/SM dent/ISGMD dental/Y dentifrice/SM dentin/M dentist/MS dentistry/M dentition/M denture/IMS denuclearize/GDS denudation/M denude/GDS denunciation/SM deny/ZGDRS deodorant/SM deodorization/M deodorize/DRSZG deodorizer/M departed/M department/MS departmental/Y departmentalization/M departmentalize/GDS departure/SM dependability/M dependable/U dependably dependence/IM dependency/SM dependent/IMYS depict/GDS depiction/MS depilatory/SM deplete/GNDS depletion/M deplorably deplore/BGDS deploy/ALGDS deployment/AM deployments deponent/MS deportation/MS deportee/MS deportment/M deposit/AGMDS depositor/MS depository/SM deprave/GDS depravity/SM deprecate/GNDS deprecating/Y deprecation/M deprecatory depreciate/DSGN depreciation/M depredation/SM depressant/SM depressing/Y depression/SM depressive/SM depressor/MS depressurization deprive/GDS deprogramming depth/M depths deputation/MS depute/DSG deputize/DSG deputy/SM derailleur/SM derailment/SM derangement/M derby/SM derelict/MS dereliction/M deride/GDS derision/M derisive/PY derisiveness/M derisory derivation/MS derivative/MS derive/B dermal dermatitis/M dermatological dermatologist/SM dermatology/M dermis/M derogate/DSGN derogation/M derogatorily derogatory derrick/SM derriere/SM derringer/SM derv dervish/MS desalinate/GNDS desalination/M desalinization/M desalinize/GDS descant/M descend/FGDS descendant/MS descender describable/I describe/BZGDR describer/M description/SM descriptive/PY descriptiveness/M descriptor/S descry/GDS desecrate/DSGN desecration/M deselection desert/SDRZGM deserter/M desertification desertion/SM deserved/UY deserving/U desiccant/SM desiccate/DSGN desiccation/M desiccator/SM desiderata desideratum/M design/ASDG designate/DSGNX designation/M desirability/UM desirableness/M desirably/U desire/B desired/U desirous desist/SDG desk/SM deskill/G desktop/SM desolate/PDSYGN desolateness/M desolation/M despair/SMDG despairing/Y desperado/M desperadoes desperate/YNP desperateness/M desperation/M despicable despicably despise/DSG despite despoilment/M despondence/M despondency/M despondent/Y despotic despotically despotism/M dessert/SM dessertspoon/S dessertspoonful/S destination/SM destine/DSG destiny/SM destitute/N destitution/M destroy/SZGDR destroyer/M destruct/GVMDS destructibility/IM destructible/I destruction/M destructive/PY destructiveness/M desuetude/M desultorily desultory detach/BLGDS detachment/MS detain/LGDS detainee/MS detainment/M detect/SDGVB detectable/U detected/U detection/M detective/SM detector/SM detente/SMNX detention/M deter/SL detergent/SM deteriorate/DSGN deterioration/M determent/M determinable/I determinant/SM determinate determine/AGDS determined/U determinedly determiner/SM determinism/M deterministic deterred/U deterrence/M deterrent/MS deterring detestably detestation/M dethrone/DSLG dethronement/M detonate/GNDSX detonation/M detonator/SM detox/MDSG detoxification/M detoxify/DSGN detract/GD detriment/SM detrimental/Y detritus/M deuce/SM deuterium/M devastate/GNDS devastating/Y devastation/M devastator/MS develop/ASGDL developed/U developer/SM development/ASM developmental/Y deviance/M deviancy/M deviant/SM deviate/DSMGNX deviating/U deviation/M devil/SMDGL devilish/YP devilishness/M devilment/M devilry/SM deviltry/SM devious/YP deviousness/M devoid devolution/M devolve/DSG devoted/Y devotee/SM devotion/MS devotional/SM devour/SDG devout/PRYT devoutness/M dew/M dewberry/SM dewclaw/SM dewdrop/SM dewiness/M dewlap/SM dewy/RTP dexterity/M dexterous/YP dexterousness/M dextrose/M dharma dhoti/SM dhow/MS diabetes/M diabetic/SM diabolic diabolical/Y diacritic/MS diacritical diadem/SM diaereses diaeresis/M diagnose/DSG diagnosis/M diagnostic/S diagnostically diagnostician/SM diagnostics/M diagonal/SMY diagram/SM diagrammatic diagrammatically diagrammed diagramming dial/AMDGS dialect/SM dialectal dialectic/SM dialectical dialectics/M dialing/S dialog dialogue/SM dialyses dialysis/M dialyzes diam diamante diameter/SM diametric diametrical/Y diamond/SM diamondback/MS diapason/SM diaper/SMDG diaphanous diaphragm/SM diaphragmatic diarist/SM diarrhea/M diary/SM diaspora diastase/M diastole/M diastolic diathermy/M diatom/SM diatomic diatonic diatribe/SM dibble/DSMG dibs/M dice/GDS dices/I dicey dichotomous dichotomy/SM dicier diciest dick/MRXZS dicker/DG dickey/SM dickhead/S dickybird/S dicotyledon/MS dicotyledonous dict dicta dictate/DSMGNX dictation/M dictator/SM dictatorial/Y dictatorship/SM diction/M dictionary/SM dictum/M did/AU didactic didactically diddle/DRSZG diddler/M diddly diddlysquat diddums didgeridoo/S didn't dido/M didoes didst die/DSM dielectric/MS diereses dieresis/M diesel/SMDG diet/MDRZGS dietary/SM dieter/M dietetic/S dietetics/M dietitian/MS diff/DRZGS differ/DG difference/IM differences different/IY differential/SM differentiate/DSGN differentiated/U differentiation/M difficult/Y difficulty/SM diffidence/M diffident/Y diffract/GSD diffraction/M diffuse/DSYGNVP diffuseness/M diffusion/M dig/SM digerati/M digest/SMDGV digested/U digestibility/M digestible/I digestion/IM digestions digestive/S digger/SM digging/S diggings/M digicam/S digit/SM digital/Y digitalis/M digitization digitize/GDS dignified/U dignify/DSG dignitary/SM dignity/ISM digraph/M digraphs digress/GVDS digression/MS dike/MGDS diktat/S dilapidated dilapidation/M dilatation/M dilate/DSGN dilation/M dilator/SM dilatory dildo/S dilemma/MS dilettante/SM dilettantish dilettantism/M diligence/M diligent/Y dill/MS dilly/SM dillydally/DSG dilute/DSGNX diluted/U dilution/M dim/PSRY dime/MS dimension/SM dimensional dimensionless diminish/GDS diminished/U diminuendo/SM diminution/SM diminutive/SM dimity/M dimmed/U dimmer/SM dimmest dimming dimness/M dimple/DSMG dimply dimwit/SM dimwitted din/ZGSMDR dinar/SM dine/S diner/M dinette/MS ding/MDG dingbat/MS dinghy/SM dingily dinginess/M dingle/SM dingo/M dingoes dingus/MS dingy/RPT dink/R dinky/RSMT dinned dinner/SMDG dinnertime/M dinnerware/M dinning dinosaur/SM dint/M diocesan/MS diocese/MS diode/SM diorama/SM dioxide/SM dioxin/SM dip/SM diphtheria/M diphthong/SM diploid/SM diploma/SM diplomacy/M diplomat/MS diplomata diplomatic/U diplomatically diplomatist/MS diplopia dipole/SM dipped dipper/SM dipping dippy/RT dipso/S dipsomania/M dipsomaniac/MS dipstick/SM dipterous diptych/M diptychs dire/YTR direct/ASDGVT directer direction/IM directional directionless directions directive/SM directly directness/IM director/MS directorate/SM directorial directorship/SM directory/SM direful dirge/SM dirigible/MS dirk/MS dirndl/SM dirt/M dirtball/S dirtily dirtiness/M dirty/DRSTGP dis/M disable/DSGL disablement/M disambiguate/N disappointing/Y disarming/Y disassembly disastrous/Y disbandment/M disbarment/M disbelieving/Y disbursal/M disburse/DSGL disbursement/MS disc/M discern/LSDG discernible/I discernibly discerning/Y discernment/M discharged/U disciple/SM discipleship/M disciplinarian/SM disciplinary discipline/DSMG disciplined/U disclose/DSG disclosed/U disco/MG discography/SM discoloration/S discombobulate/DSGN discombobulation/M discomfit/DG discomfiture/M discommode/DG disconcerting/Y disconnected/PY disconnectedness/M disconsolate/Y discordance/M discordant/Y discotheque/SM discourage/LGDS discouragement/SM discouraging/Y discover/ASDG discovered/U discoverer/MS discovery/ASM discreet/PRYT discreetness/M discrepancy/SM discrepant discrete/PYN discreteness/M discretion/IM discretionary discriminant discriminate/GNDS discriminating/U discrimination/M discriminator/MS discriminatory discursiveness/M discus/MS discussant/SM discussion/SM disdain/SMDG disdainful/Y disembowel/SDLG disembowelment/M disfigurement/SM disfranchisement/M disgorgement/M disgruntle/LGDS disgruntlement/M disguise/GD disguised/U disgusted/Y disgusting/Y dish/MDSG dishabille/M disharmonious dishcloth/M dishcloths disheartening/Y dishevel/DGLS dishevelment/M dishpan/SM dishrag/SM dishtowel/MS dishware/M dishwasher/MS dishwater/M dishy disillusion/GLD disillusionment/M disinfectant/MS disinfection/M disinterested/PY disinterestedness/M disjointed/YP disjointedness/M disjunctive disjuncture disk/MS diskette/MS dislodge/GDS dismal/Y dismantlement/M dismay/SMDG dismayed/U dismember/LGD dismemberment/M dismissive/Y disorder/Y disorganization/M disparage/DSGL disparagement/M disparaging/Y disparate/Y dispatcher/MS dispel/S dispelled dispelling dispensary/SM dispensation/MS dispense/BZGDRS dispenser/M dispersal/M disperse/GNDS dispersion/M dispirit/GDS displeasure/M disposable/SM disposal/SM disposed/I disposition/ISM dispossession/M disproof/SM disproportional disprove/B disputable/I disputably/I disputant/MS disputation/SM disputatious/Y dispute/DRSMZGB disputed/U disputer/M disquiet/GSMD disquisition/MS disregardful disrepair/M disrepute/MB disrupt/GVSD disruption/SM disruptive/Y dissect/SDG dissed dissemblance/M dissemble/ZGDRS dissembler/M disseminate/GNDS dissemination/M dissension/SM dissent/SMDRZG dissenter/M dissertation/SM dissidence/M dissident/MS dissimilar dissimilitude/S dissing dissipate/GNDS dissipation/M dissociate/GNDS dissociation/M dissoluble/I dissolute/YNP dissoluteness/M dissolve/AGDS dissolved/U dissonance/SM dissonant dissuade/GDS dissuasive dist distaff/SM distal/Y distance/DSMG distant/Y distaste/SM distemper/M distention/SM distillate/SMNX distillation/M distillery/SM distinct/IYTVP distincter distinction/SM distinctive/YP distinctiveness/M distinctness/IM distinguish/GDSB distinguishable/I distinguished/U distort/GDR distortion/MS distract/DG distracted/Y distraction/S distrait distraught distress/DG distressful distressing/Y distribute/AGNVDS distributed/U distribution/AM distributional distributions distributive/Y distributor's distributor/AS distributorship/S district's district/AS disturb/ZGSDR disturbance/SM disturbed/U disturber/M disturbing/Y disunion/M disyllabic ditch/MDSG dither/SMDRZG ditherer/M ditransitive ditsy ditto/SMDG ditty/SM ditz/MS diuretic/MS diurnal/Y div diva/MS divalent divan/SM dive/MZTGDRS diver/M diverge/DSG divergence/MS divergent diverse/XYNP diverseness/M diversification/M diversify/GNDS diversion/M diversionary diversity/SM divert/SDG diverticulitis/M divest/SLDG divestiture/MS divestment/M divide/DRSMZGB divided/U dividend/MS divider/M divination/M divine/DRSMYZTG diviner/M diving/M divinity/SM divisibility/IM divisible/I division/MS divisional divisive/PY divisiveness/M divisor/SM divorce/DSLMG divorcee/MS divorcement/MS divot/SM divulge/GDS divvy/DSMG dixieland/M dizzily dizziness/M dizzy/DRSPTG djellaba/MS do/SJMRHZG doable dob/S dobbed dobbin/SM dobbing doberman/MS dobro doc/SM docent/SM docile/Y docility/M dock/MDRZGS docket/SMDG dockland/S dockside dockworker/MS dockyard/MS doctor/SMDG doctoral doctorate/MS doctrinaire/MS doctrinal doctrine/MS docudrama/SM document/GMDS documentary/SM documentation/SM documented/U dodder/SMDG doddery doddle dodge/DRSMZG dodgem/S dodger/M dodgy/RT dodo/MS doe/SM doer/M does/AU doeskin/MS doesn't doff/DGS dog/SM dogcart/SM dogcatcher/SM doge/MS dogeared dogfight/SM dogfish/MS dogged/PY doggedness/M doggerel/M dogging doggone/TGRS doggy/RSMT doghouse/SM dogie/SM dogleg/SM doglegged doglegging doglike dogma/SM dogmatic dogmatically dogmatism/M dogmatist/SM dognapper dogsbody/S dogsled/S dogtrot/MS dogtrotted dogtrotting dogwood/MS doily/SM doing/USM doldrums/M dole's dole/FGDS doleful/YP dolefulness/M doll/MDGS dollar/SM dollhouse/SM dollop/SGMD dolly/SM dolmen/SM dolomite/M dolor/M dolorous/Y dolphin/MS dolt/MS doltish/YP doltishness/M domain/SM dome/MGDS domestic/SM domestically domesticate/DSGN domesticated/U domestication/M domesticity/M domicile/DSMG domiciliary dominance/M dominant/SMY dominate/DSGN domination/M dominatrices dominatrix/M domineer/SGD domineering/Y dominion/SM domino/M dominoes don't don/SM dona/MS donate/DSXGN donation/M done/FAU dong/MDGS dongle/SM donkey/SM donned donning donnish donnybrook/MS donor/SM donuts doodad/SM doodah doodahs doodle/DRSMZG doodlebug/SM doodler/M doohickey/SM doolally doom/MDGS doomsayer/MS doomsday/M doomster/S door's door/IS doorbell/MS doorjamb/S doorkeeper/MS doorknob/MS doorknocker/S doorman/M doormat/SM doormen doorplate/SM doorpost/S doorstep/MS doorstepped doorstepping doorstop/MS doorway/SM dooryard/MS dopa/M dopamine dope/MZGDRS doper/M dopey dopier dopiest dopiness/M doping/M doppelganger/S dork/MS dorky/RT dorm/MRZS dormancy/M dormant dormer/M dormice dormitory/SM dormouse/M dorsal/Y dory/SM dosage/SM dose/MGDS dosh dosimeter/SM doss/DRSZG dosshouse/S dossier/MS dost dot/ZGSMDR dotage/M dotard/SM dotcom/SM dote/S doter/M doting/Y dotted dotting dotty/RT double's double/ADSG doubleheader/MS doublespeak/M doublet/MS doubloon/SM doubly doubt/SMDRZG doubter/M doubtful/PY doubtfulness/M doubting/Y doubtless/Y douche/DSMG dough/M doughnut/SM doughty/RT doughy/TR dour/RYTP dourness/M douse/DSG dove/MS dovecot/S dovecote/SM dovetail/MDSG dovish dowager/MS dowdily dowdiness/M dowdy/RSPT dowel/SMDG dower/SMDG down/MDRZGS downbeat/SM downcast downdraft/MS downer/M downfall/SMN downfield downgrade/DSMG downhearted/PY downheartedness/M downhill/MS download/MDBSG downmarket downplay/DSG downpour/MS downrange downright downriver downscale downshift/SGD downside/MS downsize/GDS downsizing/M downspout/MS downstage downstairs/M downstate/M downstream downswing/MS downtime/M downtown/M downtrend/MS downtrodden downturn/MS downward/S downwind downy/RT dowry/SM dowse/DRSZG dowser/M doxology/SM doyen/SM doyenne/MS doz/XGDNS doze/M dozen/MH dozily dozy/RTP dpi dpt drab/MYSP drabber drabbest drabness/M drachma/MS draconian draft's draft/ASDG draftee/SM drafter/SM draftily draftiness/M drafting/M draftsman/M draftsmanship/M draftsmen draftswoman/M draftswomen drafty/RTP drag/MS dragged dragging draggy/TR dragnet/SM dragon/SM dragonfly/SM dragoon/SMDG dragster/S drain/SMDRZG drainage/M drainboard/SM drainer/M drainpipe/MS drake/SM dram/MS drama/SM dramatic/S dramatically dramatics/M dramatist/SM dramatization/SM dramatize/DSG drank drape/DRSMZG draper/M drapery/SM drastic drastically drat dratted draughtboard/S draw/MRZGSJ drawback/MS drawbridge/MS drawer/M drawing/M drawl/SMDG drawn/A drawstring/MS dray/MS dread/SMDG dreadful/PY dreadfulness/M dreadlocks/M dreadnought/MS dream/SMDRZG dreamboat/MS dreamed/U dreamer/M dreamily dreaminess/M dreamland/M dreamless dreamlike dreamworld/SM dreamy/RPT drear drearily dreariness/M dreary/RPT dredge/DRSMZG dredger/M dregs/M drench/GDS dress/AUGSDM dressage/M dresser/MS dressiness/M dressing/SM dressmaker/SM dressmaking/M dressy/TPR drew/A dribble/MZGDRS dribbler/M driblet/MS drier/M drift/SMDRZG drifter/M driftnet/S driftwood/M drill/SMDRZG driller/M drillmaster/SM drink/SMRBJZG drinkable/U drinker/M drip/MS dripped dripping/SM drippy/TR drive/RSMZGJ drivel/SZGMDR driveler/M driven driver/M driveshaft/SM driveway/MS drizzle/MGDS drizzly drogue/SM droid/S droll/RPT drollery/SM drollness/M drolly dromedary/SM drone/DSMG drool/SMDG droop/GSMD droopiness/M droopy/TPR drop/MS dropkick/MS droplet/SM dropout/SM dropped dropper/SM dropping/S droppings/M dropsical dropsy/M dross/M drought/SM drove/RSMZ drover/M drown/GSJD drowning/M drowse/MGDS drowsily drowsiness/M drowsy/RTP drub/S drubbed drubber/SM drubbing/MS drudge/MGDS drudgery/M drug/MS drugged druggie/SM drugging druggist/SM druggy drugstore/MS druid/SM druidism/M drum/MS drumbeat/SM drumlin/SM drummed drummer/SM drumming drumstick/SM drunk/STMNR drunkard/MS drunken/PY drunkenness/M drupe/SM druthers/M dry/ZTGDRSMY dryad/SM dryer/SM dryness/M drys drywall/M dual dualism/M duality/M dub/SM dubbed dubber/SM dubbin/M dubbing dubiety/M dubious/YP dubiousness/M ducal ducat/SM duchess/MS duchy/SM duck/MDGS duckbill/SM duckboards duckling/SM duckpins/M duckweed/M ducky/TRSM duct's/K duct/CKIFS ductile ductility/M ducting ductless dud/GSMD dude/MS dudgeon/M due/SM duel/MDRJZGS dueler/M duelist/SM duenna/MS duet/MS duff/MDRZGS duffer/M dug dugout/MS duh duke/MS dukedom/SM dulcet dulcimer/MS dull/DRPTGS dullard/SM dullness/M dully duly/U dumb/RYPT dumbbell/SM dumbfound/SDG dumbness/M dumbo/S dumbstruck dumbwaiter/SM dumdum/MS dummy/SM dump/MDRZGS dumpiness/M dumpling/SM dumpsite/S dumpster/SM dumpy/PTR dun/SM dunce/SM dunderhead/MS dune/MS dung/MDGS dungaree/MS dungeon/SM dunghill/MS dunk/MDGS dunned dunner dunnest dunning dunno duo/SM duodecimal duodena duodenal duodenum/M duopoly/S dupe/MZGDRS duper/M duple duplex/MS duplicate's duplicate/AGNDS duplication/AM duplicator/MS duplicitous duplicity/M durability/M durable durably durance/M duration/M duress/M during durst durum/M dusk/M duskiness/M dusky/RTP dust/MDRZGS dustbin/SM dustcart/S duster/M dustiness/M dustless dustman dustmen dustpan/SM dustsheet/S dusty/RTP dutch duteous/Y dutiable dutiful/YP dutifulness/M duty/SM duvet/SM dwarf/SGMD dwarfish dwarfism/M dweeb/SM dwell/SJZGR dweller/M dwelling/M dwelt/I dwindle/DSG dyadic dybbuk/SM dybbukim dye/DRSMZG dyeing/A dyer/M dyestuff/M dying/M dyke/MS dynamic/MS dynamical/Y dynamics/M dynamism/M dynamite/MZGDRS dynamiter/M dynamo/SM dynastic dynasty/SM dysentery/M dysfunction/MS dysfunctional dyslectic/SM dyslexia/M dyslexic/SM dyspepsia/M dyspeptic/MS dysphagia dysprosium/M dystonia dz e'en e'er e/FDST eBay/M eMusic/M ea each eager/PTRY eagerness/M eagle/MS eaglet/MS ear/SMDY earache/SM earbud/SM eardrum/SM earful/SM earl/MS earldom/SM earliness/M earlobe/SM early/RTP earmark/SMDG earmuff/SM earn/DRZTGJS earned/U earner/M earnest/SMYP earnestness/M earnings/M earphone/MS earpiece/S earplug/SM earring/SM earshot/M earsplitting earth's earth/UDYG earthbound earthen earthenware/M earthiness/M earthling/MS earthly/RT earthquake/SM earths/U earthshaking earthward/S earthwork/MS earthworm/MS earthy/RTP earwax/M earwig/SM ease/EDSM easel/SM easement/SM easily/U easiness/UM easing east/M eastbound easterly/SM eastern/ZR easterner/M easternmost eastward/S easy/URTP easygoing eat/ZGBSNR eatable/SM eaten/U eater/M eatery/SM eave/MS eavesdrop/S eavesdropped eavesdropper/SM eavesdropping ebb/SMDG ebony/SM ebullience/M ebullient/Y ebullition/M eccentric/SM eccentrically eccentricity/SM eccl ecclesial ecclesiastic/SM ecclesiastical/Y echelon/SM echinoderm/SM echo's echo/ADG echoes/A echoic echolocation/M echos eclair/SM eclat/M eclectic/SM eclectically eclecticism/M eclipse/DSMG ecliptic/M eclogue/SM ecocide/M ecol ecologic ecological/Y ecologist/MS ecology/M econ econometric economic/S economical/UY economics/M economist/SM economize/DRSZG economizer/M economy/SM ecosystem/MS ecotourism/M ecotourist/MS ecru/M ecstasy/SM ecstatic ecstatically ecu/S ecumenical/Y ecumenicism/M ecumenism/M eczema/M ed/ACSM edamame eddy/DSMG edelweiss/M edema/SM edge/MZGJDRS edger/M edgewise edgily edginess/M edging/M edgy/RTP edibility/M edible/SMP edibleness/M edict/SM edification/M edifice/SM edifier/M edify/DRSZGN edifying/U edit's edit/ADGS editable edited/U edition/MS editor/SM editorial/SMY editorialize/DSG editorship/M educ educability/M educable/I educate/ADSGNV educated/U education/AM educational/Y educationalist/S educationist/S educations educator/MS educe/DSGB edutainment/M eek eel/SM eerie/RT eerily eeriness/M eff/GSD efface/DSLG effacement/M effect/SMDGV effective/IPY effectiveness/IM effectual/IY effectuate/DSG effeminacy/M effeminate/Y effendi/SM efferent effervesce/GDS effervescence/M effervescent/Y effete/YP effeteness/M efficacious/Y efficacy/IM efficiency/IM efficient/IY effigy/SM efflorescence/M efflorescent effluence/M effluent/MS effluvia effluvium/M effort/SM effortless/YP effortlessness/M effrontery/M effulgence/M effulgent effuse/DSGNVX effusion/M effusive/YP effusiveness/M egad egalitarian/SM egalitarianism/M egg/GSMD eggbeater/MS eggcup/SM egghead/SM eggnog/M eggplant/MS eggshell/SM eglantine/SM ego/SM egocentric/MS egocentrically egocentricity/M egoism/M egoist/SM egoistic egoistical/Y egomania/M egomaniac/MS egotism/M egotist/SM egotistic egotistical/Y egregious/PY egregiousness/M egress/MS egret/SM eh eider/SM eiderdown/MS eigenvalue/S eight/SM eighteen/MHS eighteenth/M eighteenths eighth/M eighths eightieth/M eightieths eighty/SMH einsteinium/M eisteddfod/S either ejaculate/GNXDS ejaculation/M ejaculatory eject/SDG ejection/MS ejector/SM eke/DSG elaborate/YGNDSPX elaborateness/M elaboration/M elan/M eland/SM elapse/DSG elastic/MS elastically elasticated elasticity/M elasticize/DSG elate/DSGN elated/Y elation/M elbow/SMDG elbowroom/M elder/SMY elderberry/SM eldercare/M eldest eldritch elect's elect/ASDGV electable election/AMS electioneer/DGS elective/MS elector/MS electoral/Y electorate/MS electric/S electrical/Y electrician/MS electricity/M electrification/M electrifier/M electrify/ZGNDRS electrocardiogram/MS electrocardiograph/M electrocardiographs electrocardiography/M electrocute/DSXGN electrocution/M electrode/SM electrodynamics electroencephalogram/MS electroencephalograph/M electroencephalographic electroencephalographs electroencephalography/M electrologist/SM electrolysis/M electrolyte/MS electrolytic electromagnet/MS electromagnetic electromagnetically electromagnetism/M electromotive electron/MS electronic/S electronica/M electronically electronics/M electroplate/DSG electroscope/SM electroscopic electroshock/M electrostatic/S electrostatics/M electrotype/MS eleemosynary elegance/IM elegant/IY elegiac/MS elegiacal elegy/SM elem element/MS elemental/Y elementary elephant/SM elephantiasis/M elephantine elev elevate/XDSGN elevation/M elevator/MS eleven/SMH elevens/S eleventh/M elevenths elf/M elfin elfish elicit/SDG elicitation/M elide/DSG eligibility/IM eligible eliminate/XDSGN elimination/M eliminator/S elision/MS elite/SM elitism/M elitist/MS elixir/SM elk/SM ell/SM ellipse/MS ellipsis/M ellipsoid/SM ellipsoidal elliptic elliptical/Y elm/SM elocution/M elocutionary elocutionist/SM elodea/SM elongate/DSGNX elongation/M elope/DSGL elopement/MS eloquence/M eloquent/Y else elsewhere elucidate/DSGNX elucidation/M elude/DSG elusive/YP elusiveness/M elver/SM elves elvish em's em/S emaciate/GNDS emaciation/M email/SMDG emanate/XDSGN emanation/M emancipate/DSGN emancipation/M emancipator/MS emasculate/GNDS emasculation/M embalm/SZGDR embalmer/M embank/SLGD embankment/SM embargo/MDG embargoes embark/AEGDS embarkation/EM embarkations embarrass/GLDS embarrassed/U embarrassing/Y embarrassment/SM embassy/SM embattled embed/S embedded embedding embellish/LGDS embellishment/SM ember/SM embezzle/ZGLDRS embezzlement/M embezzler/M embitter/GLDS embitterment/M emblazon/GDLS emblazonment/M emblem/SM emblematic emblematically embodiment/EM embody/AEGSD embolden/DGS embolism/MS embolization emboss/DRSZG embosser/M embouchure/M embower/SGD embrace/DSMG embraceable embrasure/MS embrocation/MS embroider/SDRZG embroiderer/M embroidery/SM embroil/DGLS embroilment/M embryo/SM embryological embryologist/MS embryology/M embryonic emcee/DSM emceeing emend/SDG emendation/MS emerald/MS emerge/ADSG emergence/AM emergency/SM emergent emerita emeritus emery/M emetic/SM emf/S emigrant/SM emigrate/DSXGN emigration/M emigre/SM eminence/MS eminent/Y emir/MS emirate/MS emissary/SM emission/SM emit/S emitted emitter/MS emitting emo/SM emoji emollient/MS emolument/MS emote/XDSGNV emoticon/SM emotion/M emotional/UY emotionalism/M emotionalize/GDS emotionless emotive/Y empathetic empathize/DSG empathy/M emperor/MS emphases emphasis/M emphasize/AGDS emphatic/U emphatically emphysema/M empire/SM empiric empirical/Y empiricism/M empiricist/SM emplacement/SM employ's employ/ADGLS employable/U employee/SM employer/SM employment/UAM employments emporium/SM empower/SDGL empowerment/M empress/MS emptily emptiness/M empty/TGPDRSM empyrean/M emu/SM emulate/DSGNVX emulation/M emulator/SM emulsification/M emulsifier/M emulsify/NDRSZG emulsion/MS en/SM enable/DRSZG enabler/M enact/ASLDG enactment/ASM enamel/JSZGMDR enameler/M enamelware/M enamor/SGD enc encamp/LSGD encampment/MS encapsulate/XGNDS encapsulation/M encase/LDSG encasement/M encephalitic encephalitis/M enchain/DGS enchant/ELDGS enchanter/MS enchanting/Y enchantment/EM enchantments enchantress/MS enchilada/SM encipher/SGD encircle/DSGL encirclement/M encl enclave/MS enclose/GDS enclosed/U enclosure/SM encode/DRSZG encoder/M encomium/MS encompass/GDS encore/DSMG encounter/GSMD encourage/DSLG encouragement/SM encouraging/Y encroach/GLDS encroachment/SM encrust/DGS encrustation/SM encrypt/DGS encryption encumber/EGSD encumbered/U encumbrance/MS ency encyclical/SM encyclopedia/MS encyclopedic encyst/LSGD encystment/M end/GVSJMD endanger/SGDL endangerment/M endear/SGLD endearing/Y endearment/SM endeavor/GSMD endemic/MS endemically endgame/S ending/M endive/SM endless/PY endlessness/M endmost endocarditis endocrine/MS endocrinologist/MS endocrinology/M endogenous/Y endometrial endometriosis endometrium endorphin/M endorse/LZGDRS endorsement/MS endorser/M endoscope/MS endoscopic endoscopy/M endothelial endothermic endow/SDLG endowment/MS endpoint/SM endue/DSG endurable/U endurance/M endure/DSBG endways enema/SM enemy/SM energetic energetically energize/ZGDRS energizer/M energy/SM enervate/GNDS enervation/M enfeeble/GDSL enfeeblement/M enfilade/DSMG enfold/SGD enforce/LZGDRS enforceable/U enforced/U enforcement/M enforcer/M enfranchise/EGDSL enfranchisement/EM engage/EADSG engagement/EMS engagingly engender/SGD engine/SM engineer/MDGS engineering/M engorge/LGDS engorgement/M engram/SM engrave/ZGJDRS engraver/M engraving/M engross/GLDS engrossment/M engulf/SLGD engulfment/M enhance/LZGDRS enhancement/SM enigma/SM enigmatic enigmatically enjambment/SM enjoin/SGD enjoy/GBLSD enjoyably enjoyment/SM enlarge/LZGDRS enlargeable enlargement/MS enlarger/M enlighten/SGLD enlightened/U enlightenment/M enlist/ADGSL enlistee/SM enlistment/AM enlistments enliven/SLDG enlivenment/M enmesh/DSGL enmeshment/M enmity/SM ennoble/DSGL ennoblement/M ennui/M enormity/SM enormous/PY enormousness/M enough/M enplane/DSG enquirer/S enquiringly enrage/GDS enrapture/DSG enrich/DSLG enrichment/M enroll/DLSG enrollment/MS ensconce/DSG ensemble/SM enshrine/GLDS enshrinement/M enshroud/DGS ensign/MS ensilage/M enslave/DSGL enslavement/M ensnare/DSLG ensnarement/M ensue/DSG ensure/ZGDRS ensurer/M entail/DSGL entailment/M entangle/EDSLG entanglement/EM entanglements entente/SM enter/ASGD enteric enteritis/M enterprise/MGS enterprising/Y entertain/ZGDRSL entertainer/M entertaining/MY entertainment/MS enthrall/GDSL enthrallment/M enthrone/GDSL enthronement/SM enthuse/DSG enthusiasm/MS enthusiast/MS enthusiastic/U enthusiastically entice/GDSL enticement/MS enticing/Y entire/Y entirety/M entitle/DSGL entitlement/SM entity/SM entomb/DSGL entombment/M entomological entomologist/MS entomology/M entourage/SM entr'acte entrails/M entrance/LDSMG entrancement/M entrancing/Y entrant/SM entrap/LS entrapment/M entrapped entrapping entreat/GSD entreating/Y entreaty/SM entree/MS entrench/DSGL entrenchment/MS entrepreneur/SM entrepreneurial entrepreneurship entropy/M entrust/SGD entry/ASM entryphone/S entryway/MS entwine/DSG enumerable enumerate/DSGNX enumeration/M enumerator/SM enunciate/DSGN enunciation/M enuresis/M envelop/SLDRZG envelope/SM enveloper/M envelopment/M envenom/SDG enviable/U enviably envious/PY enviousness/M environment/MS environmental/Y environmentalism/M environmentalist/SM environs/M envisage/GDS envision/DGS envoy/SM envy/DSMG envying/Y enzymatic enzyme/SM eolian eon/SM eosinophil/S eosinophilic epaulet/SM epee/MS ephedrine/M ephemera/M ephemeral/Y epic/MS epicenter/MS epicure/SM epicurean/MS epidemic/SM epidemically epidemiological epidemiologist/SM epidemiology/M epidermal epidermic epidermis/MS epidural/S epiglottis/MS epigram/SM epigrammatic epigraph/M epigraphs epigraphy/M epilepsy/M epileptic/SM epilogue/MS epinephrine/M epiphany/SM episcopacy/M episcopal episcopate/M episode/SM episodic episodically epistemic epistemological epistemology epistle/SM epistolary epitaph/M epitaphs epithelial epithelium/M epithet/SM epitome/SM epitomize/GDS epoch/M epochal epochs eponymous epoxy/DSMG epsilon/SM equability/M equable equably equal/SMDYG equality/IM equalization/M equalize/ZGDRS equalizer/M equanimity/M equate/DSGNBX equation/M equator/SM equatorial equerry/SM equestrian/SM equestrianism/M equestrienne/SM equidistant/Y equilateral/SM equilibrium/EM equine/SM equinoctial equinox/MS equip/AS equipage/MS equipment/M equipoise/M equipped/UA equipping/A equitable/I equitably/I equitation/M equity/ISM equiv equivalence/MS equivalency/SM equivalent/MYS equivocal/UY equivocalness/M equivocate/GNXDS equivocation/M equivocator/SM er/C era/SM eradicable/I eradicate/DSGN eradication/M eradicator/MS erase/DRSBZG eraser/M erasure/SM erbium/M ere erect/PSGDY erectile erection/SM erectness/M erector/MS erelong eremite/MS erg/SM ergo ergonomic/S ergonomically ergonomics/M ergosterol/M ergot/M ermine/SM erode/DSG erodible erogenous erosion/M erosive erotic/S erotica/M erotically eroticism/M err/GSD errand/SM errant/I errata/SM erratic erratically erratum/M erroneous/Y error/SM ersatz/MS erst erstwhile eruct/SDG eructation/SM erudite/YN erudition/M erupt/SDGV eruption/MS erysipelas/M erythrocyte/SM erythromycin escalate/CDSGN escalation/CM escalations escalator/MS escallop/SGMD escalope/S escapade/MS escape/LMGDS escapee/MS escapement/SM escapism/M escapist/MS escapologist/S escapology escargot/MS escarole/MS escarpment/MS eschatological eschatology eschew/SDG escort/SMDG escritoire/MS escrow/SM escudo/SM escutcheon/SM esophageal esophagi esophagus/M esoteric esoterically esp espadrille/MS espalier/MDSG especial/Y espionage/M esplanade/MS espousal/M espouse/GDS espresso/MS esprit/M espy/DSG esquire/SM essay/SMDRZG essayer/M essayist/SM essence/SM essential/IMS essentially establish/AESDGL establishment/AEM establishments estate/SM esteem/ESMDG ester/SM estimable/I estimate/MGNDSX estimation/M estimator/SM estoppel estrange/LDSG estrangement/MS estrogen/M estrous estrus/MS estuary/SM eta/SM etc etch/DRSZGJ etcher/M etching/M eternal/YP eternalness/M eternity/SM ethane/M ethanol/M ether/M ethereal/Y ethic/SM ethical/UY ethics/M ethnic/SM ethnically ethnicity/M ethnocentric ethnocentrism/M ethnographer/S ethnographic ethnographically ethnography ethnological/Y ethnologist/SM ethnology/M ethological ethologist/MS ethology/M ethos/M ethyl/M ethylene/M etiolated etiologic etiological etiology/SM etiquette/M etude/SM etymological/Y etymologist/SM etymology/SM eucalypti eucalyptus/MS euchre/DSMG euclidean eugenic/S eugenically eugenicist/MS eugenics/M eulogist/MS eulogistic eulogize/ZGDRS eulogizer/M eulogy/SM eunuch/M eunuchs euphemism/SM euphemistic euphemistically euphonious/Y euphony/M euphoria/M euphoric euphorically eureka euro/MS europium/M eutectic euthanasia/M euthanize/DSG euthenics/M evacuate/XDSGN evacuation/M evacuee/MS evade/DRSZG evader/M evaluate/AGNVDSX evaluation/AM evanescence/M evanescent evangelic evangelical/SMY evangelicalism/M evangelism/M evangelist/MS evangelistic evangelize/GDS evaporate/GNDS evaporation/M evaporator/SM evasion/SM evasive/YP evasiveness/M eve/ASM even/MDRYTGSJP evenhanded/Y evening/M evenness/UM evensong/M event/SM eventful/UY eventfulness/M eventide/M eventual/Y eventuality/SM eventuate/GDS ever everglade/SM evergreen/SM everlasting/MYS evermore every everybody/M everyday everyone/M everyplace everything/M everywhere evict/SDG eviction/MS evidence/MGDS evident/Y evil/MRYTSP evildoer/SM evildoing/M eviller evillest evilness/M evince/DSG eviscerate/DSGN evisceration/M evocation/MS evocative/Y evoke/DSG evolution/M evolutionary evolutionist/SM evolve/DSG ewe/RSMZ ewer/M ex/MS exabyte/MS exacerbate/GNDS exacerbation/M exact/SPDRYTG exacting/Y exaction/M exactitude/M exactness/IM exaggerate/XDSGN exaggerated/Y exaggeration/M exaggerator/MS exalt/SDG exaltation/M exam/MS examination/AMS examine/AGDS examiner/MS example/MGDS exampled/U exasperate/DSGN exasperated/Y exasperating/Y exasperation/M excavate/GNDSX excavation/M excavator/SM exceed/GSD exceeding/Y excel/S excelled excellence/M excellency/SM excellent/Y excelling excelsior/M except/GSD exception/BSM exceptionable/U exceptional/UY exceptionalism excerpt/MDGS excess/VMS excessive/Y exchange/DSMG exchangeable exchequer/SM excise/XDSMGN excision/M excitability/M excitably excitation/M excite/BDRSLZG excited/Y excitement/SM exciter/M exciting/Y excl exclaim/DGS exclamation/SM exclamatory exclude/GDS exclusion/MS exclusionary exclusive/PMYS exclusiveness/M exclusivity/M excommunicate/GNDSX excommunication/M excoriate/DSGNX excoriation/M excrement/M excremental excrescence/MS excrescent excreta/M excrete/XGNDS excretion/M excretory excruciating/Y exculpate/DSGN exculpation/M exculpatory excursion/MS excursionist/MS excursive/YP excursiveness/M excusable/I excusably/I excuse/DSBMG excused/U exec/MS execrable execrably execrate/DSGN execration/M execute/BXGNVDS execution/ZMR executioner/M executive/SM executor/MS executrices executrix/M exegeses exegesis/M exegetic exegetical exemplar/SM exemplary exemplification/M exemplify/GDSXN exempt/SGD exemption/SM exercise/DRSMZG exerciser/M exert/SDG exertion/MS exeunt exfoliate/GNDS exhalation/MS exhale/DSG exhaust/GVMDS exhaustible/I exhaustion/M exhaustive/YP exhaustiveness/M exhibit/GMDS exhibition/MS exhibitionism/M exhibitionist/MS exhibitor/SM exhilarate/DSGN exhilaration/M exhort/SDG exhortation/MS exhumation/MS exhume/DSG exigence/MS exigency/SM exigent exiguity/M exiguous exile/DSMG exist/SDG existence/MS existent existential/Y existentialism/M existentialist/MS exit/MDGS exobiology/M exodus/MS exogenous exon exonerate/GNDS exoneration/M exoplanet/MS exorbitance/M exorbitant/Y exorcise/DSG exorcism/SM exorcist/SM exoskeleton/SM exosphere/SM exothermic exotic/SM exotica exotically exoticism/M exp expand/BGSD expanse/XMNVS expansible expansion/M expansionary expansionism/M expansionist/MS expansive/YP expansiveness/M expat/S expatiate/GNDS expatiation/M expatriate/DSMGN expatriation/M expect/GSD expectancy/M expectant/Y expectation/SM expectorant/SM expectorate/DSGN expectoration/M expedience/IM expediences expediencies expediency/IM expedient/SMY expedite/DRSZGNX expediter/M expedition/M expeditionary expeditious/PY expeditiousness/M expel/S expelled expelling expend/GSBD expendable/SM expenditure/SM expense/MS expensive/IYP expensiveness/IM experience/IMD experiences experiencing experiential experiment/MDRSZG experimental/Y experimentation/M experimenter/M expert/SPMY expertise/M expertness/M expiate/GNDS expiation/M expiatory expiration/M expire/DSG expired/U expiry/M explain/ADGS explainable explained/U explanation/MS explanatory expletive/MS explicable/I explicate/XGNDS explication/M explicit/PY explicitness/M explode/GDS exploit/ZGBMDRS exploitation/M exploitative exploited/U exploiter/M exploration/MS exploratory explore/ZGDRS explored/U explorer/M explosion/SM explosive/SPMY explosiveness/M expo/MS exponent/MS exponential/Y exponentiation export/BSZGMDR exportation/M exporter/M expose/DSMG exposed/U exposition/SM expositor/SM expository expostulate/GNXDS expostulation/M exposure/MS expound/ZGDRS expounder/M express/GVMDSY expressed/U expressible/I expression/SM expressionism/M expressionist/SM expressionistic expressionless/Y expressive/PY expressiveness/M expressway/SM expropriate/GNXDS expropriation/M expropriator/SM expulsion/MS expunge/GDS expurgate/DSGNX expurgated/U expurgation/M exquisite/YP exquisiteness/M ext extant extemporaneous/PY extemporaneousness/M extempore extemporization/M extemporize/GDS extend/SZGDRB extender/M extensible extension/SM extensional extensive/YP extensiveness/M extent/SM extenuate/DSGN extenuation/M exterior/MS exterminate/DSXGN extermination/M exterminator/MS external/MYS externalization/SM externalize/DSG extinct/GDS extinction/MS extinguish/ZGBDRS extinguishable/I extinguisher/M extirpate/GNDS extirpation/M extol/S extolled extolling extort/SGD extortion/MRZ extortionate/Y extortioner/M extortionist/MS extra/SM extracellular extract/MDGS extraction/SM extractor/MS extracurricular extradite/GNBXDS extradition/M extrajudicial extralegal extramarital extramural extraneous/Y extraordinaire extraordinarily extraordinary extrapolate/XGNDS extrapolation/M extrasensory extraterrestrial/MS extraterritorial extraterritoriality/M extravagance/MS extravagant/Y extravaganza/MS extravehicular extreme/PMYTRS extremeness/M extremism/M extremist/MS extremity/SM extricable/I extricate/GNDS extrication/M extrinsic extrinsically extroversion/M extrovert/SMD extrude/GDS extrusion/SM extrusive exuberance/M exuberant/Y exudation/M exude/DSG exult/SDG exultant/Y exultation/M exurb/SM exurban exurbanite/SM exurbia/M eye/DSM eyeball/GMDS eyebrow/SM eyedropper/SM eyeful/SM eyeglass/MS eyeing eyelash/MS eyeless eyelet/SM eyelid/SM eyeliner/MS eyeopener/MS eyeopening eyepiece/MS eyesight/M eyesore/MS eyestrain/M eyeteeth eyetooth/M eyewash/M eyewitness/MS f/CIAVTR fa/M fab fable/DSM fabric/SM fabricate/DSGNX fabrication/M fabricator/SM fabulous/Y facade/SM face's face/ACSDG facecloth/M facecloths faceless facet/SMDG facetious/YP facetiousness/M facial/SMY facile/Y facilitate/GNDS facilitation/M facilitator/MS facility/SM facing/SM facsimile/DSM facsimileing fact/MS faction/SM factional factionalism/M factious factitious factoid/SM factor's factor/ASDG factorial/MS factorization factorize/GDS factory/SM factotum/SM factual/Y faculty/SM fad/GSMD faddish/P faddist/MS faddy/P fade/MS fading/U faerie/SM faff/DGS fag/SM fagged fagging faggot/SM fagot/SMG faience/M fail/MDGJS failing/M faille/M failure/SM fain/RT faint/SMDRYTGP fainthearted faintness/M fair/MRYTGJPS fairground/MS fairing/M fairness/UM fairway/SM fairy/SM fairyland/SM faith/M faithful's faithful/UPY faithfulness/UM faithfuls faithless/PY faithlessness/M faiths fajita/SM fajitas/M fake/MZGDRS faker/M fakir/SM falcon/SMRZ falconer/M falconry/M fall/MNGS fallacious/Y fallacy/SM fallback fallibility/IM fallible/P fallibleness/M fallibly/I falloff/SM fallout/M fallow/SMDG false/PRYT falsehood/SM falseness/M falsetto/SM falsie/SM falsifiable falsification/M falsifier/M falsify/DRSZGNX falsity/SM falter/GSJMD faltering/Y fame's fame/D familial familiar/MYS familiarity/UM familiarization/M familiarize/GDS family/SM famine/SM famish/DSG famous/IY fan/SM fanatic/SM fanatical/Y fanaticism/M fanboy/SM fanciable fancier/M fanciful/YP fancifulness/M fancily fanciness/M fancy/DRSMZTGP fancywork/M fandango/MS fandom fanfare/SM fang/MDS fanlight/SM fanned fanning fanny/SM fantail/MS fantasia/SM fantasist/S fantasize/GDS fantastic fantastical/Y fantasy/DSMG fanzine/MS far farad/SM faradize/DG faraway farce/SM farcical/Y fare/MGDS farewell/SM farina/M farinaceous farm/MDRZGSJ farmer/M farmhand/SM farmhouse/SM farming/M farmland/MS farmstead/MS farmyard/MS faro/M farrago/M farragoes farrier/MS farrow/SMDG farseeing farsighted/P farsightedness/M fart/MDGS farther farthermost farthest farthing/SM fascia/SM fascicle/SM fascinate/GNDSX fascinating/Y fascination/M fascism/M fascist/MS fascistic fashion/ZGBMDRS fashionable/U fashionably/U fashioner/M fashionista/MS fast/MDRTGSP fastback/SM fastball/SM fasten/UAGDS fastener/SM fastening/MS fastidious/PY fastidiousness/M fastness/MS fat/GSPMD fatal/Y fatalism/M fatalist/SM fatalistic fatalistically fatality/SM fatback/M fate/MS fateful/YP fatefulness/M fathead/MDS father/SGMDY fatherhood/M fatherland/MS fatherless fathom/SMDGB fathomable/U fathomless fatigue/MDSG fatigues/M fatness/M fatso/S fatten/SDG fatter fattest fattiness/M fatty/RSMTP fatuity/M fatuous/YP fatuousness/M fatwa/SM faucet/SM fault/CSMDG faultfinder/SM faultfinding/M faultily faultiness/M faultless/PY faultlessness/M faulty/PRT faun/MS fauna/SM fauvism/M fauvist/SM faux fave/S favor/ESMDG favorable/U favorably/U favorite/SM favoritism/M fawn/MDRZGS fawner/M fax/GMDS fay/TSMR faze/GDS fazed/U fealty/M fear/MDGS fearful/YP fearfulness/M fearless/PY fearlessness/M fearsome feasibility/M feasible/IU feasibly feast/SMDG feat/MS feather/SGMD featherbedding/M featherbrained featherless featherweight/MS feathery/TR feature/DSMG featureless febrile fecal feces/M feckless/PY fecund fecundate/GNDS fecundation/M fecundity/M fed/SM federal/SMY federalism/M federalist/MS federalization/M federalize/GDS federate/FXDSGN federation/FM fedora/SM fee/SM feeble/RTP feebleness/M feebly feed/MRZGSJ feedback/M feedbag/SM feeder/M feeding/M feedlot/SM feel/MRZGSJ feeler/M feelgood feeling/MY feet feign/SDG feigned/U feint/SMDG feisty/TR feldspar/M felicitate/GNXDS felicitation/M felicitous/Y felicity/ISM feline/SM fell/MDRZTGS fella/S fellatio/M fellow/SM fellowman/M fellowmen fellowship/MS felon/SM felonious felony/SM felt/MDGS fem female/PSM femaleness/M feminine/SMY femininity/M feminism/M feminist/SM feminize/DSG femoral femur/SM fen/SM fence/DRSMZG fencer/M fencing/M fend/CDRZGS fender/CM fenestration/M fennel/M feral ferment/FCMS fermentation/M fermented fermenting fermium/M fern/MS ferny/RT ferocious/PY ferociousness/M ferocity/M ferret/GSMD ferric ferromagnetic ferrous ferrule/MS ferry/DSMG ferryboat/SM ferryman/M ferrymen fertile/I fertility/IM fertilization/M fertilize/DRSZG fertilized/U fertilizer/M ferule/SM fervency/M fervent/Y fervid/Y fervor/M fess/FKGSD fest/MRZVS festal fester/GMD festival/SM festive/YP festiveness/M festivity/SM festoon/GMDS feta/M fetal fetch/DRSZG fetcher/M fetching/Y fete/MGDS fetid/P fetidness/M fetish/MS fetishism/M fetishist/SM fetishistic fetlock/MS fetter's fetter/USGD fettle/M fettuccine/M fetus/MS feud/MDGS feudal feudalism/M feudalistic fever/SMD feverish/YP feverishness/M few/TPMR fewness/M fey fez/M fezzes ff fiance/CM fiancee/MS fiances fiasco/M fiascoes fiat/MS fib/ZSMR fibbed fibber/SM fibbing fiber/M fiberboard/M fiberfill/M fiberglass/M fibril/SM fibrillate/GNDS fibrillation/M fibrin/M fibroid fibrosis/M fibrous fibula/M fibulae fibular fiche/SM fichu/SM fickle/RPT fickleness/M fiction/MS fictional/Y fictionalization/SM fictionalize/DSG fictitious/Y fictive ficus/M fiddle/DRSMZG fiddler/M fiddlesticks fiddly/TR fidelity/IM fidget/SGMD fidgety fiduciary/SM fie fief/MS fiefdom/MS field/ISMRZ fielded fielder/IM fielding fieldsman fieldsmen fieldwork/MRZ fieldworker/M fiend/SM fiendish/Y fierce/PRYT fierceness/M fieriness/M fiery/RPT fiesta/SM fife/MZRS fifer/M fifteen/MHS fifteenth/M fifteenths fifth/MY fifths fiftieth/M fiftieths fifty/SMH fig/SLM fight/SMRZG fightback fighter/IMS fighting/IM figment/MS figuration/FM figurative/Y figure's figure/FEGSD figurehead/SM figurine/MS filament/MS filamentous filbert/MS filch/DSG file's/KC file/CAKGDS filename/S filer/CSM filet filial filibuster/MDRSZG filibusterer/M filigree/DSM filigreeing filing's filings fill's fill/AIDGS filled/U filler/MS fillet/MDGS filling/SM fillip/MDGS filly/SM film/MDGS filminess/M filmmaker/SM filmstrip/MS filmy/TPR filo filter/MDRBSZG filtered/U filterer/M filth/M filthily filthiness/M filthy/RPT filtrate's filtrate/IGNDS filtration/IM fin/SMR finagle/DRSZG finagler/M final/SMY finale/MS finalist/SM finality/M finalization/M finalize/DSG finance's finance/ADSG financial/Y financier/MS financing/M finch/MS find/JMRZGS finder/M finding/M findings/M fine's/F fine/CAFTGDS finely fineness/M finery/AM finespun finesse/DSMG finger/MDGSJ fingerboard/SM fingering/M fingerling/SM fingermark/S fingernail/SM fingerprint/SGMD fingertip/MS finial/MS finical finickiness/M finicky/RPT finis/MS finish's finish/ADSG finished/U finisher/MS finite/IY fink/MDGS finned finny fir/ZGSJMDRH fire/MS firearm/SM fireball/MS firebomb/MDSJG firebox/MS firebrand/SM firebreak/SM firebrick/SM firebug/SM firecracker/SM firedamp/M firefight/MRSZG firefighter/M firefighting/M firefly/SM fireguard/S firehouse/SM firelight/ZMR fireman/M firemen fireplace/SM fireplug/MS firepower/M fireproof/DSG firer/M firescreen/S fireside/MS firestorm/MS firetrap/MS firetruck/MS firewall/MS firewater/M firewood/M firework/SM firm/MDRYPTGS firmament/SM firmness/M firmware/M first/SMY firstborn/SM firsthand firth/M firths fiscal/MYS fish/MDRSZG fishbowl/SM fishcake/SM fisher/M fisherman/M fishermen fishery/SM fishhook/SM fishily fishiness/M fishing/M fishmonger/MS fishnet/SM fishpond/MS fishtail/DGS fishwife/M fishwives fishy/TRP fissile fission/BM fissure/SM fist/MS fistfight/MS fistful/SM fisticuffs/M fistula/SM fistulous/M fit/KAMS fitful/YP fitfulness/M fitly fitment/S fitness/UM fitted/UA fitter/MS fittest fitting/SMY five/MZRS fix/ZGBJMDRS fixate/GNVDSX fixation/M fixative/MS fixed/Y fixer/M fixings/M fixity/M fixture/MS fizz/MDSG fizzle/DSMG fizzy/RT fjord/SM fl/JDG flab/M flabbergast/SGD flabbily flabbiness/M flabby/RPT flaccid/Y flaccidity/M flack/SM flag/MS flagella flagellant/S flagellate/GNDS flagellation/M flagellum/M flagged flagging/U flagman/M flagmen flagon/MS flagpole/SM flagrance/M flagrancy/M flagrant/Y flagship/SM flagstaff/MS flagstone/MS flail/SGMD flair/SM flak/M flake/DSMG flakiness/M flaky/TRP flamage flambe/MS flambeed flambeing flamboyance/M flamboyancy/M flamboyant/Y flame/DRSJMZG flamenco/MS flameproof/DGS flamethrower/SM flamingo/MS flammability/IM flammable/SM flan/MS flange/MS flank/SZGMDR flanker/M flannel/SGMD flannelette/M flap/MS flapjack/MS flapped flapper/SM flapping flare/DSMG flareup/SM flash/ZTGMDRS flashback/SM flashbulb/SM flashcard/SM flashcube/SM flasher/M flashgun/SM flashily flashiness/M flashing/M flashlight/MS flashy/RTP flask/SM flat/MYPS flatbed/SM flatboat/SM flatbread flatcar/SM flatfeet flatfish/MS flatfoot/SMD flatiron/SM flatland/M flatlet/S flatmate/S flatness/M flatted flatten/SDG flatter/SDRZG flatterer/M flattering/Y flattery/M flattest flatting flattish flattop/SM flatulence/M flatulent flatus/M flatware/M flatworm/SM flaunt/MDSG flaunting/Y flavor/MDSGJ flavored/U flavorful flavoring/M flavorless flavorsome flaw/MDGS flawless/PY flawlessness/M flax/MN flay/DGS flea/MS fleabag/SM fleabite/S fleapit/S fleck/SGMD fledged/U fledgling/MS flee/S fleece/MZGDRS fleecer/M fleeciness/M fleecy/RTP fleeing fleet/STGMDRYP fleetingly/M fleetingness/M fleetness/M flesh/GMDSY fleshly/TR fleshpot/MS fleshy/RT flew flex/AMS flexed flexibility/IM flexible/I flexibly/I flexing flexion flextime/M flibbertigibbet/SM flick/SZGMDR flicker/GMD flier/M flight/MS flightiness/M flightless flighty/PTR flimflam/SM flimflammed flimflamming flimsily flimsiness/M flimsy/TRP flinch/GMDS fling/GM flint/SM flintlock/SM flinty/TR flip/MS flipflop/S flipflopped flipflopping flippancy/M flippant/Y flipped flipper/MS flippest flipping flippy/S flirt/SGMD flirtation/MS flirtatious/YP flirtatiousness/M flirty flit/MS flitted flitting float/SMDRZG floater/M flock/SMDG flocking/M floe/MS flog/S flogged flogger/SM flogging/MS flood/SMDRG floodgate/MS floodlight/MDSG floodlit floodplain/MS floodwater/MS floor/SMDG floorboard/MS flooring/M floorwalker/SM floozy/SM flop/MS flophouse/MS flopped floppily floppiness/M flopping floppy/PRSMT flora/SM floral florescence/IM florescent/I floret/SM florid/PY floridness/M florin/SM florist/SM floss/MDSG flossy/RT flotation/SM flotilla/MS flotsam/M flounce/DSMG flouncy flounder/MDSG flour/SMDG flourish/GMDS floury flout/SMDRZG flouter/M flow/MDGS flowchart/SM flower's flower/CSDG flowerbed/MS floweriness/M flowering/S flowerless flowerpot/MS flowery/PTR flown flt flu/M flub/MS flubbed flubbing fluctuate/GNDSX fluctuation/M flue/MS fluency/M fluent/Y fluff/SMDG fluffiness/M fluffy/RPT fluid/SMY fluidity/M fluke/SM fluky/RT flume/SM flummox/DSG flung flunk/SMDG flunky/SM fluoresce/DSG fluorescence/M fluorescent fluoridate/GNDS fluoridation/M fluoride/SM fluorine/M fluorite/M fluorocarbon/MS fluoroscope/SM fluoroscopic flurry/GDSM flush/MDRSTG fluster/MDSG flute/DSMG fluting/M flutist/MS flutter/MDSG fluttery fluvial flux/IMS fluxed fluxing fly/ZTGBDRSM flyaway flyblown flyby/M flybys flycatcher/MS flying/M flyleaf/M flyleaves flyover/MS flypaper/SM flypast/S flysheet/S flyspeck/GMDS flyswatter/MS flytrap/S flyway/SM flyweight/SM flywheel/MS foal/MDGS foam/MDGS foaminess/M foamy/RTP fob/SM fobbed fobbing focal/Y focus's focus/ADSG focused/U fodder/SM foe/SM fog's fog/CS fogbound fogged/C foggily fogginess/M fogging/C foggy/RTP foghorn/MS fogy/SM fogyish foible/SM foil/MDGS foist/SDG fol fold's fold/AUSGD foldaway folder/SM foldout/MS foliage/M folic folio/SM folk/MS folklore/M folkloric folklorist/MS folksiness/M folksinger/SM folksinging/M folksy/PTR folktale/MS folkway/MS foll follicle/MS follow/SDRZGJ follower/M following/M followup/S folly/SM foment/SGD fomentation/M fond/RYTP fondant/MS fondle/DSG fondness/M fondue/SM font/MS fontanel/MS foo foobar food/MS foodie/SM foodstuff/SM fool/MDGS foolery/SM foolhardily foolhardiness/M foolhardy/TPR foolish/YP foolishness/M foolproof foolscap/M foot/MDRZGSJ footage/M football/MRZGS footballer/M footbridge/SM footfall/MS foothill/MS foothold/MS footie footing/M footless footlights/M footling/MS footlocker/SM footloose footman/M footmen footnote/MGDS footpath/M footpaths footplate/S footprint/SM footrace/MS footrest/MS footsie/SM footslogging footsore footstep/MS footstool/SM footwear/M footwork/M footy fop/SM foppery/M foppish/P foppishness/M for/H fora forage/DRSMZG forager/M foray/SMDG forbade forbear/SMG forbearance/M forbid/S forbidden forbidding/YS forbore forborne force/DSMG forced/U forceful/PY forcefulness/M forceps/M forcible forcibly ford/MDGSB fore/MS forearm/GSMD forebear/MS forebode/GJDS foreboding/M forecast/MRZGS forecaster/M forecastle/MS foreclose/DSG foreclosure/MS forecourt/SM foredoom/DGS forefather/MS forefeet forefinger/SM forefoot/M forefront/SM forego/G foregoes foregone foreground/GMDS forehand/MS forehead/MS foreign/ZRP foreigner/M foreignness/M foreknew foreknow/GS foreknowledge/M foreknown foreleg/SM forelimb/MS forelock/MS foreman/M foremast/MS foremen foremost forename/MDS forenoon/MS forensic/MS forensically forensics/M foreordain/GSD forepart/MS foreperson/SM foreplay/M forequarter/MS forerunner/MS foresail/MS foresaw foresee/RSBZ foreseeable/U foreseeing foreseen/U foreseer/M foreshadow/GDS foreshore/S foreshorten/DSG foresight/MD foresightedness/M foreskin/MS forest's forest/ACGDS forestall/SGD forestation/ACM forester/MS forestland/M forestry/M foretaste/DSMG foretell/GS forethought/M foretold forever/M forevermore forewarn/DSG forewent forewoman/M forewomen foreword/MS forfeit/GSMD forfeiture/SM forgather/SDG forgave forge/DRSMZGVJ forger/M forgery/SM forget/S forgetful/YP forgetfulness/M forgettable/U forgetting forging/M forgivable/U forgive/BRSZGP forgiven forgiveness/M forgiver/M forgiving/U forgo/RZG forgoer/M forgoes forgone forgot forgotten/U fork/MDGS forkful/SM forklift/MS forlorn/Y form's form/CAIFDGS formal/SMY formaldehyde/M formalin formalism/M formalist/MS formalities formality/IM formalization/M formalize/GDS format/SMV formation/CFASM formatted/A formatting/M formed/U former/FIAM formerly formfitting formic formidable formidably formless/PY formlessness/M formula/MS formulaic formulate/ADSGNX formulated/U formulation/AM formulator/SM fornicate/GNDS fornication/M fornicator/MS forsake/GS forsaken forsook forsooth forswear/SG forswore forsworn forsythia/SM fort/MS forte/SM forthcoming/M forthright/YP forthrightness/M forthwith fortieth/M fortieths fortification/M fortified/U fortifier/M fortify/DRSNZGX fortissimo fortitude/M fortnight/MYS fortress/MS fortuitous/YP fortuitousness/M fortuity/M fortunate/UY fortune/MS fortuneteller/SM fortunetelling/M forty/SMH forum/SM forward/MDRYZTGSP forwarder/M forwardness/M forwent fossa fossil/SM fossilization/M fossilize/GDS foster/GSD fought foul/MDRYTGSP foulard/M foulmouthed foulness/M found/FSDG foundation/SM founded/U founder/GMDS foundling/SM foundry/SM fount/SM fountain/SM fountainhead/MS four/MHS fourfold fourposter/SM fourscore/M foursome/SM foursquare fourteen/SMH fourteenth/M fourteenths fourth/MY fourths fowl/MDGS fox/GMDS foxfire/M foxglove/SM foxhole/MS foxhound/SM foxhunt/GS foxily foxiness/M foxtrot/MS foxtrotted foxtrotting foxy/RTP foyer/SM fps fr fracas/MS frack/SDG fractal/SM fraction/ISM fractional/Y fractious/YP fractiousness/M fracture/MGDS frag/S fragile/RT fragility/M fragment/GMDS fragmentary/M fragmentation/M fragrance/MS fragrant/Y frail/RYTP frailness/M frailty/SM frame/DRSMZG framed/U framer/M framework/SM franc/SM franchise's franchise/EDSG franchisee/SM franchiser/SM francium/M francophone frangibility/M frangible frank/SMDRYTGP frankfurter/MS frankincense/M frankness/M frantic frantically frappe/SM frat/MS fraternal/Y fraternity/FSM fraternization/M fraternize/ZGDRS fraternizer/M fratricidal fratricide/MS fraud's fraud/S fraudster/S fraudulence/M fraudulent/Y fraught fray's fray/CDGS frazzle/MGDS freak/SMDG freakish/YP freakishness/M freaky/RT freckle/DSMG freckly free/YTDRS freebase/MGDS freebie/SM freebooter/SM freeborn freedman/M freedmen freedom/SM freehand freehold/ZMRS freeholder/M freeing freelance/DRSMZG freelancer/M freeload/SDRZG freeloader/M freeman/M freemasonry freemen freephone freesia/S freestanding freestone/SM freestyle/SM freethinker/SM freethinking/M freeware/M freeway/MS freewheel/DGS freewill freezable freeze's freeze/UAGS freezer/MS freezing's freight/MDRZGS freighter/M french frenetic frenetically frenzied/Y frenzy/DSM freq frequencies frequency/IM frequent/DRYSZTG frequented/U frequenter/M fresco/M frescoes fresh/PNRYXZT freshen/ZGDR freshener/M freshet/MS freshman/M freshmen freshness/M freshwater/M fret/MS fretful/YP fretfulness/M fretsaw/MS fretted fretting fretwork/M friable friar/SM friary/SM fricassee/DSM fricasseeing fricative/SM friction/SM frictional fridge/SM friedcake/MS friend's friend/US friendless friendlies friendliness/UM friendly's friendly/UPTR friendship/MS frieze/SM frig/S frigate/MS frigged frigging fright/SXGMDN frighten/DG frightening/Y frightful/PY frightfulness/M frigid/YP frigidity/M frigidness/M frill/SMD frilly/TR fringe's fringe/IDSG frippery/SM frisk/SDG friskily friskiness/M frisky/TRP frisson/S fritter/MDSG fritz/M frivolity/SM frivolous/PY frivolousness/M frizz/MDSYG frizzle/MGDS frizzy/TR fro frock's frock/CUS frog/MS frogging/S frogman/M frogmarch/GDS frogmen frogspawn frolic/SM frolicked frolicker/SM frolicking frolicsome from frond/SM front's front/FSDG frontage/MS frontal/Y frontbench/ZRS frontier/MS frontiersman/M frontiersmen frontierswoman frontierswomen frontispiece/MS frontward/S frosh/M frost's frost/CSDG frostbit frostbite/MGS frostbitten frostily frostiness/M frosting/M frosty/TPR froth/MDG frothiness/M froths frothy/TPR froufrou/M froward/P frowardness/M frown/SMDG frowzily frowziness/M frowzy/TPR froze/AU frozen/UA fructify/DSG fructose/M frugal/Y frugality/M fruit/SMDG fruitcake/MS fruiterer/S fruitful/YP fruitfulness/M fruitiness/M fruition/M fruitless/PY fruitlessness/M fruity/TPR frump/SM frumpish frumpy/TR frustrate/GNXDS frustrating/Y frustration/M frustum/MS fry/GDSM fryer/SM ft ftp/ZGS fuchsia/MS fuck/SMGDRZ! fucker/M! fuckhead/S! fuddle/DSMG fudge/DSMG fuehrer/MS fuel's fuel/ADGS fug fugal fuggy fugitive/MS fugue/SM fuhrer/SM fulcrum/MS fulfill/LDGS fulfilled/U fulfilling/U fulfillment/M full/MDRZTGSP fullback/MS fuller/M fullness/M fully fulminate/DSXGN fulmination/M fulsome/PY fulsomeness/M fum/S fumble/DRSMZG fumbler/M fumbling/Y fume/MGDS fumigant/MS fumigate/GNDS fumigation/M fumigator/SM fumy/RT fun/M function/MDGS functional/Y functionalism functionalist/S functionality/S functionary/SM fund/AMDGS fundamental/SMY fundamentalism/M fundamentalist/SM funded/U funding/M fundraiser/MS fundraising funeral/MS funerary funereal/Y funfair/S fungal fungi fungible/MS fungicidal fungicide/MS fungoid fungous fungus/M funicular/SM funk/MDGS funkiness/M funky/PRT funnel/MDGS funner funnest funnily funniness/M funny/TPRSM funnyman/M funnymen fur/SM furbelow/M furbish/ADSG furious/Y furl's furl/UDGS furlong/SM furlough/GMD furloughs furn furnace/SM furnish/ADSG furnished/U furnishings/M furniture/M furor/SM furred furrier/M furriness/M furring/M furrow/MDSG furry/ZTRP further/SGD furtherance/M furthermore furthermost furthest furtive/YP furtiveness/M fury/SM furze/M fuse's/A fuse/CAIFGDS fusee/SM fuselage/SM fusibility/M fusible fusilier/SM fusillade/MS fusion/IFKSM fuss/MDSG fussbudget/MS fussily fussiness/M fusspot/SM fussy/TRP fustian/M fustiness/M fusty/TRP fut futile/Y futility/M futon/SM future/MS futurism/M futurist/MS futuristic futurity/SM futurologist/MS futurology/M futz/DSG fuzz/MDSG fuzzball/S fuzzily fuzziness/M fuzzy/PTR fwd fwy g/SNXVB gab/SM gabardine/SM gabbed gabbiness/M gabbing gabble/DSMG gabby/RTP gaberdine/SM gabfest/MS gable/DSM gad/S gadabout/SM gadded gadder/SM gadding gadfly/SM gadget/SM gadgetry/M gadolinium/M gaff/MDRZGS gaffe/SM gaffer/M gag/SM gaga gagged gagging gaggle/SM gaiety/M gaily gain's gain/ADGS gainer/SM gainful/Y gainsaid gainsay/ZGRS gainsayer/M gait/MRZS gaiter/M gal/SM gala/MS galactic galaxy/SM gale's gale/AS galena/M gall/MDGS gallant/SMY gallantry/M gallbladder/MS galleon/SM galleria/MS gallery/SM galley/SM gallimaufry/SM gallium/M gallivant/GSD gallon/SM gallop/SMDG gallows/M gallstone/MS galoot/SM galore galosh/MS galumph/DG galumphs galvanic galvanism/M galvanization/M galvanize/DSG galvanometer/MS gambit/SM gamble/DRSMZG gambler/M gambling/M gambol/SMDG game/MYTGDRSP gamecock/MS gamekeeper/MS gameness/M gamesmanship/M gamester/MS gamete/SM gametic gamin/SM gamine/SM gaminess/M gaming/M gamma/SM gammon/M gammy gamut/SM gamy/RTP gander/SM gang/MDGS gangbusters/M gangland/M ganglia gangling ganglion/M ganglionic gangplank/SM gangrene/DSMG gangrenous gangsta/S gangster/SM gangway/MS ganja gannet/SM gantlet/MS gantry/SM gap/GSMD gape/MS gar/SLM garage/DSMG garb/MDGS garbage/M garbageman garbanzo/SM garble/DSG garcon/SM garden/SZGMDR gardener/M gardenia/MS gardening/M garfish/MS gargantuan gargle/DSMG gargoyle/SM garish/PY garishness/M garland/MDGS garlic/M garlicky garment/MS garner/SGD garnet/SM garnish/GLMDS garnishee/DSM garnisheeing garnishment/SM garret/SM garrison/MDSG garrote/MZGDRS garroter/M garrulity/M garrulous/PY garrulousness/M garter/SM gas's gas/CS gasbag/SM gaseous gash/MDSG gasholder/S gasket/SM gaslight/MS gasman gasmen gasohol/M gasoline/M gasometer/S gasp/MDGS gassed/C gasses gassing/C gassy/RT gastric gastritis/M gastroenteritis/M gastrointestinal gastronome/S gastronomic gastronomical/Y gastronomy/M gastropod/SM gasworks/M gate/MGDS gateau gateaux gatecrash/DRSZG gatecrasher/M gatehouse/SM gatekeeper/MS gatepost/MS gateway/MS gather/SJZGMDR gatherer/M gathering/M gator/SM gauche/RPYT gaucheness/M gaucherie/M gaucho/SM gaudily gaudiness/M gaudy/RPT gauge/DSMG gaunt/RPT gauntlet/MS gauntness/M gauze/M gauziness/M gauzy/RPT gave gavel/SM gavotte/MS gawd gawk/DGS gawkily gawkiness/M gawky/RPT gawp/DGS gay/TSPMR gayness/M gaze/MZGDRS gazebo/SM gazelle/MS gazer/M gazette/MGDS gazetteer/MS gazillion/S gazpacho/M gazump/DGS gear/MDGS gearbox/MS gearing/M gearshift/MS gearwheel/SM gecko/SM geddit gee/DS geeing geek/MS geeky/RT geese geezer/MS geisha/M gel/SM gelatin/M gelatinous gelcap/M geld/DJGS gelding/M gelid gelignite/M gelled gelling gem/SM gemological gemologist/MS gemology/M gemstone/MS gendarme/MS gender/MDS gene/MS genealogical/Y genealogist/MS genealogy/SM genera general/SMY generalissimo/MS generalist/MS generality/SM generalization/MS generalize/GDS generalship/M generate/CAVNGSD generation/ACM generational generations generator/SM generic/SM generically generosity/SM generous/PY generousness/M genes/S genesis/M genetic/S genetically geneticist/MS genetics/M genial/FY geniality/FM geniculate genie/SM genii genital/FY genitalia/M genitals/M genitive/MS genitourinary genius/MS genned genning genocidal genocide/MS genome/MS genomics genre/SM gent/AMS genteel/YP genteelness/M gentian/SM gentile/SM gentility/M gentle/TGDRSP gentlefolk/MS gentlefolks/M gentleman/MY gentlemanly/U gentlemen gentleness/M gentlewoman/M gentlewomen gently gentrification/M gentrify/DSGN gentry/SM genuflect/DGS genuflection/MS genuine/PY genuineness/M genus/M geocache/DSG geocentric geocentrically geochemistry/M geode/SM geodesic/SM geodesy/M geodetic geoengineering geog geographer/SM geographic geographical/Y geography/SM geologic geological/Y geologist/MS geology/SM geom geomagnetic geomagnetism/M geometer geometric geometrical/Y geometry/SM geophysical geophysicist/SM geophysics/M geopolitical geopolitics/M geostationary geosynchronous geosyncline/MS geothermal geothermic geranium/MS gerbil/MS geriatric/S geriatrician/S geriatrics/M germ/MS germane germanium/M germicidal germicide/MS germinal/M germinate/GNDS germination/M gerontological gerontologist/MS gerontology/M gerrymander/GMDS gerrymandering/M gerund/MS gestalt/S gestapo/MS gestate/GNDS gestation/M gestational gesticulate/DSGNX gesticulation/M gestural gesture/MGDS gesundheit get/S getaway/SM getting getup/M gewgaw/SM geyser/SM ghastliness/M ghastly/TPR ghat/MS ghee gherkin/MS ghetto/SM ghettoize/GDS ghost/SMDYG ghostliness/M ghostly/RTP ghostwrite/ZGRS ghostwriter/M ghostwritten ghostwrote ghoul/SM ghoulish/YP ghoulishness/M giant/SM giantess/MS gibber/GDS gibberish/M gibbet/GMDS gibbon/MS gibbous gibe/MGDS giblet/SM giddily giddiness/M giddy/RTP gift/MDGS gig/SM gigabit/SM gigabyte/MS gigahertz/M gigantic gigantically gigapixel/MS gigawatt/SM gigged gigging giggle/DRSMZG giggler/M giggly/RT gigolo/SM gild/MDRZGS gilder/M gilding/M gill/MS gillie/S gillion/S gilt/MS gimbals/M gimcrack/SM gimcrackery/M gimlet/GSMD gimme/SM gimmick/MS gimmickry/M gimmicky gimp/MDGS gimpy gin/SM ginger/GSMDY gingerbread/M gingersnap/SM gingery gingham/M gingivitis/M ginkgo/M ginkgoes ginned ginning ginormous ginseng/M giraffe/MS gird/DRZGS girder/M girdle/DSMG girl/MS girlfriend/MS girlhood/SM girlish/YP girlishness/M girly giro/S girt/MDGS girth/M girths gist/M git/S gite/S give/ZGJRS giveaway/MS giveback/MS given/SM giver/M gizmo/SM gizzard/MS glace/S glaceed glaceing glacial/Y glaciate/XGNDS glaciation/M glacier/MS glad/MYSP gladden/GDS gladder gladdest glade/SM gladiator/SM gladiatorial gladiola/SM gladioli gladiolus/M gladness/M gladsome glam glamorization/M glamorize/DSG glamorous/Y glamour/GMDS glance/DSMG gland/SM glandes glandular glans/M glare/DSMG glaring/Y glasnost/M glass/MDSG glassblower/MS glassblowing/M glassful/SM glasshouse/S glassily glassiness/M glassware/M glassy/RTP glaucoma/M glaze/DSMG glazier/SM glazing/M gleam/SMDGJ glean/SDRZGJ gleaner/M gleanings/M glee/M gleeful/YP gleefulness/M glen/MS glenohumeral glenoid glib/YP glibber glibbest glibness/M glide/DRSMZG glider/M gliding/M glimmer/MDGJS glimmering/M glimpse/MGDS glint/SMDG glissandi glissando/M glisten/MDSG glister/DSG glitch/GMDS glitter/MDSG glitterati glittery glitz/M glitzy/TR gloaming/SM gloat/SMDG gloating/Y glob/MDGS global/Y globalism/M globalist/MS globalization/M globalize/GDS globe/SM globetrotter/MS globetrotting globular globule/MS globulin/M glockenspiel/SM gloom/M gloomily gloominess/M gloomy/TRP glop/M gloppy glorification/M glorify/GDSN glorious/IY glory/DSMG gloss/MDSG glossary/SM glossily glossiness/M glossolalia/M glossy/PTRSM glottal glottis/MS glove/DSMG glow/MDRZGS glower/GMD glowing/Y glowworm/MS glucagon glucose/M glue/MGDS glued/U gluey gluier gluiest glum/YP glummer glummest glumness/M glut/MNS gluten/M glutenous glutinous/Y glutted glutting glutton/MS gluttonous/Y gluttony/M glycerin/M glycerol/M glycogen/M glyph gm gnarl/SMDG gnarly/TR gnash/MDSG gnat/MS gnaw/DGS gneiss/M gnocchi gnome/SM gnomic gnomish gnu/SM go/JMRHZG goad/MDGS goal/MS goalie/SM goalkeeper/MS goalkeeping/M goalless goalmouth goalmouths goalpost/MS goalscorer/S goaltender/MS goat/MS goatee/SM goatherd/MS goatskin/MS gob/SM gobbed gobbet/SM gobbing gobble/DRSMZG gobbledygook/M gobbler/M goblet/SM goblin/SM gobsmacked gobstopper/S god/SM godawful godchild/M godchildren/M goddammit goddamn/D goddaughter/MS goddess/MS godfather/SM godforsaken godhead/M godhood/M godless/PY godlessness/M godlike godliness/UM godly/URTP godmother/SM godparent/SM godsend/SM godson/SM godspeed goer/M goes gofer/SM goggle/DSMG goggles/M going/M goiter/SM gold/MNS goldbrick/ZGSMDR goldbricker/M golden/TR goldenrod/M goldfield/S goldfinch/MS goldfish/MS goldmine/SM goldsmith/M goldsmiths golf/MDRZGS golfer/M golliwog/S golly/SM gonad/SM gonadal gondola/MS gondolier/SM gone/ZR goner/M gong/MDGS gonk/S gonna gonorrhea/M gonorrheal gonzo goo/M goober/SM good/MYSP goodbye/MS goodhearted goodish goodly/TR goodness/M goodnight goods/M goodwill/M goody/SM gooey goof/MDGS goofball/SM goofiness/M goofy/RPT google/DSMG googly/S gooier gooiest gook/MS goon/MS goop/M goose/DSMG gooseberry/SM goosebumps/M goosestep/S goosestepped goosestepping gopher/SM gore/MGDS gorge's gorge/EDSG gorgeous/YP gorgeousness/M gorgon/SM gorilla/MS gorily goriness/M gormandize/DRSZG gormandizer/M gormless gorp/MS gorse/M gory/RTP gosh goshawk/MS gosling/SM gospel/MS gossamer/M gossip/MDRZGS gossiper/M gossipy got gotcha/S goths gotta gotten gouache/S gouge/DRSMZG gouger/M goulash/MS gourd/SM gourde/MS gourmand/SM gourmet/SM gout/M gouty/TR gov govern/DGSBL governable/U governance/M governed/U governess/MS government/MS governmental governor/SM governorship/M govt gown/MDGS gr grab/MS grabbed grabber/MS grabbing grabby/TR grace/EDSMG graceful/EPY gracefulness/EM graceless/PY gracelessness/M gracious/UY graciousness/M grackle/MS grad/MRZSB gradate/XGNDS gradation/CM grade's grade/CADSG graded/U grader/M gradient/MS gradual/PY gradualism/M gradualness/M graduate/XMGNDS graduation/M graffiti graffito/M graft/SMDRZG grafter/M graham/S grail grain/ISMD graininess/M grainy/PTR gram/KMS grammar/MS grammarian/SM grammatical/UY gramophone/MS grampus/MS gran/S granary/SM grand/SMRYPT grandam/MS grandaunt/MS grandchild/M grandchildren/M granddad/SM granddaddy/SM granddaughter/SM grandee/MS grandeur/M grandfather/GMDYS grandiloquence/M grandiloquent grandiose/Y grandiosity/M grandma/MS grandmother/MYS grandnephew/MS grandness/M grandniece/MS grandpa/MS grandparent/MS grandson/MS grandstand/SGMD granduncle/SM grange/SM granite/M granitic granny/SM granola/M grant/SMDRZG grantee/MS granter/M grantsmanship/M granular granularity/M granulate/GNDS granulation/M granule/MS grape/SM grapefruit/MS grapeshot/M grapevine/SM graph/MDG graphic/MS graphical/Y graphite/M graphologist/MS graphology/M graphs grapnel/MS grapple/MGDS grasp/SMDBG grass/MDSG grasshopper/MS grassland/MS grassroots grassy/TR grate/DRSMZGJ grateful/UYP gratefulness/UM grater/M gratification/M gratify/GNXDS gratifying/Y gratin/S grating/MY gratis gratitude/IM gratuitous/YP gratuitousness/M gratuity/SM gravamen/MS grave/DRSMYTGP gravedigger/SM gravel/SGMDY graven graveness/M graveside/MS gravestone/SM graveyard/MS gravid gravimeter/MS gravitas gravitate/GNDS gravitation/M gravitational gravity/M gravy/SM gray/MDRTGSP graybeard/SM grayish grayness/M graze/DRSMZG grazer/M grease/DRSMZG greasepaint/M greasily greasiness/M greasy/PTR great/SMRYPT greatcoat/SM greathearted greatness/M grebe/SM greed/M greedily greediness/M greedy/PTR green/GPSMDRYT greenback/MS greenbelt/MS greenery/M greenfield greenfly/S greengage/MS greengrocer/SM greenhorn/SM greenhouse/SM greenish greenmail/M greenness/M greenroom/SM greensward/M greenwood/M greet/ZGJSDR greeter/M greeting/M gregarious/PY gregariousness/M gremlin/SM grenade/SM grenadier/MS grenadine/M grep/S grepped grepping grew/A greyhound/SM gribble/S grid/MS griddle/SM griddlecake/SM gridiron/SM gridlock/SMD grief/SM grievance/MS grieve/ZGDRS griever/M grievous/PY grievousness/M griffin/SM griffon/SM grill/SGMDJ grille/MS grim/DYPG grimace/DSMG grime/SM griminess/M grimmer grimmest grimness/M grimy/TRP grin/MS grind/SZGMRJ grinder/M grindstone/MS gringo/MS grinned grinning grip/MDRSZG gripe/SM griper/M grippe/MZGDR gripper/M grisliness/M grisly/RTP grist/MY gristle/M gristmill/MS grit/MS grits/M gritted gritter/SM grittiness/M gritting gritty/RTP grizzle/DSG grizzly/TRSM groan/SGMD groat/SM grocer/MS grocery/SM grog/M groggily grogginess/M groggy/PRT groin/SM grok/S grokked grokking grommet/SM groom/SZGMDR groomer/M grooming/M groomsman/M groomsmen groove/MGDS groovy/RT grope/DRSMZG groper/M grosbeak/MS grosgrain/M gross/PTGMDRSY grossness/M grotesque/SPMY grotesqueness/M grotto/M grottoes grotty/TR grouch/GMDS grouchily grouchiness/M grouchy/RTP ground/ZGMDRJS groundbreaking/MS groundcloth groundcloths grounder/M groundhog/MS grounding/M groundless/Y groundnut/MS groundsheet/S groundskeeper/S groundsman groundsmen groundswell/SM groundwater/M groundwork/M group/JSZGMDR grouper/M groupie/MS grouping/M groupware/M grouse/MZGDRS grouser/M grout/SGMD grove/SM grovel/ZGDRS groveler/M grovelled grovelling grow/AHSG grower/MS growing/I growl/SZGMDR growler/M grown/AI grownup/MS growth/AM growths grub/MS grubbed grubber/MS grubbily grubbiness/M grubbing grubby/TRP grubstake/M grudge/MGDS grudging/Y grue/S gruel/GJM grueling/Y gruesome/RYTP gruesomeness/M gruff/TPRY gruffness/M grumble/DRSMZGJ grumbler/M grump/SM grumpily grumpiness/M grumpy/PRT grunge/MS grungy/RT grunion/SM grunt/SGMD gt guacamole/M guanine/M guano/M guarani/MS guarantee/MDS guaranteeing guarantor/MS guaranty/GDSM guard/SZGMDR guarded/Y guarder/M guardhouse/SM guardian/SM guardianship/M guardrail/SM guardroom/SM guardsman/M guardsmen guava/SM gubernatorial guerrilla/SM guess/ZGBMDRS guesser/M guesstimate/DSMG guesswork/M guest/SGMD guestbook guesthouse/S guestroom/S guff/M guffaw/MDGS guidance/M guide/DRSMZG guidebook/SM guided/U guideline/SM guidepost/SM guider/M guild/SZMR guilder/M guildhall/MS guile/M guileful guileless/YP guilelessness/M guillemot/S guillotine/DSMG guilt/M guiltily guiltiness/M guiltless guilty/PRT guinea/MS guise/ESM guitar/MS guitarist/SM gulag/SM gulch/MS gulden/MS gulf/MS gull/MDSG gullet/MS gullibility/M gullible gully/SM gulp/MDRSZG gulper/M gum/SM gumball/S gumbo/SM gumboil/SM gumboot/S gumdrop/SM gummed gumming gummy/TR gumption/M gumshoe/MDS gumshoeing gun/SM gunboat/SM gunfight/MRZS gunfighter/M gunfire/M gunge gungy gunk/M gunky gunman/M gunmen gunmetal/M gunned gunnel/MS gunner/MS gunnery/M gunning gunny/M gunnysack/MS gunpoint/M gunpowder/M gunrunner/MS gunrunning/M gunship/MS gunshot/MS gunslinger/SM gunsmith/M gunsmiths gunwale/MS guppy/SM gurgle/MGDS gurney/MS guru/MS gush/MDRSZG gusher/M gushing/Y gushy/TR gusset/MSDG gussy/DSG gust/EMDSG gustatory gustily gusto/M gusty/RT gut/SM gutless/P gutlessness/M gutsy/RT gutted gutter/SMDG guttersnipe/MS gutting guttural/MS gutty/RT guv/S guvnor/S guy/SGMD guzzle/DRSZG guzzler/M gym/SM gymkhana/MS gymnasium/MS gymnast/MS gymnastic/S gymnastically gymnastics/M gymnosperm/SM gymslip/S gynecologic gynecological gynecologist/SM gynecology/M gyp/SM gypped gypper/SM gypping gypster/SM gypsum/M gypsy/SM gyrate/DSGNX gyration/M gyrator/SM gyrfalcon/MS gyro/MS gyroscope/MS gyroscopic gyve/MGDS h'm h/NRSXZGVJ ha/SH haberdasher/SM haberdashery/SM habiliment/SM habit's habit/ISB habitability/M habitat/SM habitation/MS habitual/YP habitualness/M habituate/GNDS habituation/M habitue/SM hacienda/SM hack/MDRZGS hacker/M hacking/M hackish hackle/MS hackney/SMDG hacksaw/SM hacktivist/S hackwork/M had haddock/SM hadn't hadst hafnium/M haft/MS hag/SM haggard/YP haggardness/M haggis/MS haggish haggle/MZGDRS haggler/M hagiographer/SM hagiography/SM hahnium/M haiku/M hail/MDGS hailstone/MS hailstorm/MS hair/MDS hairball/MS hairband/S hairbreadth/M hairbreadths hairbrush/MS haircloth/M haircut/SM hairdo/MS hairdresser/SM hairdressing/M hairdryer/MS hairgrip/S hairiness/M hairless hairlike hairline/SM hairnet/SM hairpiece/MS hairpin/SM hairsbreadth/M hairsbreadths hairsplitter/SM hairsplitting/M hairspray/S hairspring/MS hairstyle/MS hairstylist/SM hairy/TRP haj hajj/M hajjes hajji/SM hake/MS halal/M halberd/SM halcyon hale/ITGDRS half/M halfback/SM halfhearted/PY halfheartedness/M halfpence halfpenny/SM halftime/MS halftone/MS halfway halfwit/SM halibut/SM halite/M halitosis/M hall/MS hallelujah/M hallelujahs hallmark/GMDS halloo/MSG hallow/DSG hallowed/U hallucinate/GNXDS hallucination/M hallucinatory hallucinogen/SM hallucinogenic/SM hallway/SM halo/MDGS halogen/SM halon halt/MDRZGS halter/GMD halterneck/S halting/Y halve/DSG halyard/MS ham/SM hamburg/SZMR hamburger/M hamlet/MS hammed hammer/MDRSJZG hammerer/M hammerhead/SM hammerlock/SM hammertoe/MS hamming hammock/SM hammy/TR hamper/GMDS hampered/U hamster/MS hamstring/GSM hamstrung hand's hand/UDGS handbag/SM handball/MS handbarrow/SM handbill/MS handbook/MS handbrake/S handcar/SM handcart/MS handclasp/MS handcraft/SMDG handcuff/MDGS handed/P handful/SM handgun/SM handheld/MS handhold/MS handicap/MS handicapped handicapper/MS handicapping handicraft/MS handily handiness/M handiwork/M handkerchief/MS handle/MZGDRS handlebar/MS handler/M handmade handmaid/XMNS handmaiden/M handout/SM handover/S handpick/GDS handrail/MS handsaw/SM handset/SM handshake/JMGS handsome/PYTR handsomeness/M handspring/MS handstand/SM handwork/M handwoven handwriting/M handwritten handy/UTR handyman/M handymen hang/MDRJZGS hangar/MS hangdog hanger/M hanging/M hangman/M hangmen hangnail/MS hangout/SM hangover/MS hangup/MS hank/MRZS hanker/GJD hankering/M hankie/MS hansom/MS hap/MY haphazard/YP haphazardness/M hapless/YP haplessness/M haploid/MS happen/SDGJ happening/M happenstance/SM happily/U happiness/UM happy/URTP harangue/MGDS harass/LZGDRS harasser/M harassment/M harbinger/SM harbor/GMDS harbormaster/S hard/NRYXTP hardback/MS hardball/M hardboard/M hardbound hardcore hardcover/SM harden/ZGDR hardened/U hardener/M hardhat/MS hardheaded/PY hardheadedness/M hardhearted/PY hardheartedness/M hardihood/M hardily hardiness/M hardliner/MS hardness/M hardscrabble hardship/SM hardstand/SM hardtack/M hardtop/SM hardware/M hardwired hardwood/SM hardworking hardy/PTR hare/MGDS harebell/MS harebrained harelip/SM harelipped harem/SM haricot/S hark/DGS harlequin/SM harlot/SM harlotry/M harm/MDGS harmed/U harmful/YP harmfulness/M harmless/PY harmlessness/M harmonic/SM harmonica/MS harmonically harmonies harmonious/PY harmoniousness/M harmonium/MS harmonization/M harmonize/ZGDRS harmonizer/M harmony/EM harness's harness/UDSG harp/MDGS harpist/SM harpoon/ZGSMDR harpooner/M harpsichord/MS harpsichordist/SM harpy/SM harridan/MS harrier/M harrow/SMDG harrumph/GD harrumphs harry/DRSZG harsh/RYTP harshness/M hart/MS harvest/SMDRZG harvested/U harvester/M hash/AMDSG hashish/M hashtag/SM hasn't hasp/MS hassle/DSMG hassock/SM hast/DNXG haste/SM hasten/DG hastily hastiness/M hasty/RTP hat/ZGSMDR hatband/S hatbox/MS hatch/MDSG hatchback/MS hatcheck/SM hatched/U hatchery/SM hatchet/SM hatching/M hatchway/SM hate/MS hateful/PY hatefulness/M hatemonger/MS hater/M hatpin/S hatred/SM hatstand/S hatted hatter/SM hatting hauberk/SM haughtily haughtiness/M haughty/PRT haul/MDRZGS haulage/M hauler/M haulier/S haunch/MS haunt/SMDRZG haunter/M haunting/Y hauteur/M have/MGS haven't haven/SM haversack/SM havoc/M haw/GSMD hawk/MDRZGS hawker/M hawkish/P hawkishness/M hawser/SM hawthorn/MS hay/GSMD haycock/SM hayloft/SM haymaking haymow/SM hayrick/MS hayride/MS hayseed/MS haystack/SM haywire hazard/SMDG hazardous/Y haze/MZGJDRS hazel/SM hazelnut/MS hazer/M hazily haziness/M hazing/M hazmat hazy/RTP hdqrs he'd he'll he/M head/MDRZGJS headache/MS headband/MS headbanger/S headbanging headboard/SM headbutt/DSG headcase/S headcheese headcount/S headdress/MS header/M headfirst headgear/M headhunt/DRSZG headhunter/M headhunting/M headily headiness/M heading/M headlamp/MS headland/MS headless headlight/MS headline/MZGDRS headliner/M headlock/MS headlong headman/M headmaster/SM headmen headmistress/MS headphone/MS headpiece/MS headpin/SM headquarter/SDG headquarters/M headrest/MS headroom/M headscarf headscarves headset/SM headship/SM headshrinker/SM headsman/M headsmen headstall/SM headstand/SM headstone/SM headstrong headteacher/S headwaiter/SM headwaters/M headway/M headwind/SM headword/SM heady/RTP heal/DRHZGS healed/U healer/M health/M healthcare healthful/PY healthfulness/M healthily/U healthiness/UM healthy/UTRP heap/MDGS hear/AHGJS heard/AU hearer/SM hearing/AM hearken/SGD hearsay/M hearse's hearse/AS heart/SM heartache/MS heartbeat/MS heartbreak/SMG heartbroken heartburn/M hearten/ESGD heartfelt hearth/M hearthrug/S hearths hearthstone/SM heartily heartiness/M heartland/MS heartless/PY heartlessness/M heartrending/Y heartsick/P heartsickness/M heartstrings/M heartthrob/MS heartwarming heartwood/M hearty/RSMPT heat's heat/ADGS heated/U heatedly heater/SM heath/MNRX heathen/M heathendom/M heathenish heathenism/M heather/M heaths heating/M heatproof heatstroke/M heatwave/S heave/DRSMZG heaven/SMY heavenly/TR heavens/M heavenward/S heaver/M heavily heaviness/M heavy/RSMTP heavyhearted heavyset heavyweight/MS heck/M heckle/DRSMZG heckler/M heckling/M hectare/SM hectic hectically hectogram/SM hectometer/MS hector/SMDG hedge/DRSMZG hedgehog/MS hedgehop/S hedgehopped hedgehopping hedger/M hedgerow/SM hedonism/M hedonist/MS hedonistic heed/MDGS heeded/U heedful/Y heedless/PY heedlessness/M heehaw/SMDG heel/MDGS heelless heft/MDGS heftily heftiness/M hefty/PRT hegemonic hegemony/M hegira/SM heifer/SM height/XSMN heighten/DG heinous/YP heinousness/M heir/MS heiress/MS heirloom/SM heist/SMDG held helical helices helicopter/SGMD heliocentric heliotrope/SM helipad/S heliport/MS helium/M helix/M hell/M hellbent hellcat/MS hellebore/M hellhole/MS hellion/MS hellish/YP hellishness/M hello/SM helluva helm/MS helmet/SMD helmsman/M helmsmen helot/SM help/MDRZGSJ helper/M helpful/UY helpfulness/M helping/M helpless/PY helplessness/M helpline/SM helpmate/SM helve/SM hem/SM hematite/M hematologic hematological hematologist/MS hematology/M heme/M hemisphere/SM hemispheric hemispherical hemline/SM hemlock/SM hemmed hemmer/SM hemming hemoglobin/M hemophilia/M hemophiliac/MS hemorrhage/MGDS hemorrhagic hemorrhoid/MS hemostat/MS hemp/MN hemstitch/MDSG hen/M hence henceforth henceforward henchman/M henchmen henna/SMDG henpeck/GSD hep heparin/M hepatic hepatitis/M hepatocyte/S hepper heppest heptagon/MS heptagonal heptathlon/SM herald/SMDG heralded/U heraldic heraldry/M herb/MS herbaceous herbage/M herbal/S herbalist/MS herbicidal herbicide/MS herbivore/SM herbivorous herculean herd/MDRZGS herder/M herdsman/M herdsmen here/M hereabout/S hereafter/SM hereby hereditary heredity/M herein hereinafter hereof hereon heresy/SM heretic/SM heretical hereto heretofore hereunto hereupon herewith heritable/I heritage/MS hermaphrodite/SM hermaphroditic hermetic hermetical/Y hermit/SM hermitage/MS hernia/SM hernial herniate/GNDS herniation/M hero/M heroes heroic/S heroically heroics/M heroin/SM heroine/SM heroism/M heron/SM herpes/M herpetologist/SM herpetology/M herring/MS herringbone/M herself hertz/M hesitance/M hesitancy/M hesitant/Y hesitate/DSGNX hesitating/UY hesitation/M hessian hetero/SM heterodox heterodoxy/M heterogeneity/M heterogeneous/Y heterosexual/MYS heterosexuality/M heuristic/MS heuristically heuristics/M hew/ZGSDR hewer/M hex/GMDS hexadecimal/S hexagon/MS hexagonal hexagram/SM hexameter/SM hey heyday/SM hf hgt hgwy hi/SD hiatus/MS hibachi/MS hibernate/GNDS hibernation/M hibernator/MS hibiscus/MS hiccough/DG hiccoughs hiccup/GSMD hick/MS hickey/SM hickory/SM hid hidden hide/MZGJDRS hideaway/SM hidebound hideous/YP hideousness/M hideout/MS hider/M hiding/M hie/S hieing hierarchic hierarchical/Y hierarchy/SM hieroglyph/M hieroglyphic/MS hieroglyphs high/MRYZTP highball/SM highborn highboy/MS highbrow/SM highchair/MS highfalutin highhanded/PY highhandedness/M highland/MRZS highlander/M highlight/SMDRZG highlighter/M highness/M highroad/MS highs hightail/DSG highway/MS highwayman/M highwaymen hijab hijack/SJZGMDR hijacker/M hijacking/M hike/MZGDRS hiker/M hiking/M hilarious/PY hilariousness/M hilarity/M hill/MS hillbilly/SM hilliness/M hillock/MS hillside/SM hilltop/MS hilly/PRT hilt/MS him/S himself hind/MRZS hinder/GD hindered/U hindmost hindquarter/MS hindrance/SM hindsight/M hinge's hinge/UDSG hint/MDRZGS hinter/M hinterland/SM hip/SPM hipbath hipbaths hipbone/MS hiphuggers hipness/M hipped hipper hippest hippie/SM hipping hippo/SM hippodrome/SM hippopotamus/MS hippy hipster/MS hiragana hire's hire/AGDS hireling/MS hirsute/P hirsuteness/M hiss/MDSG hist histamine/MS histogram/MS histologist/SM histology/M historian/MS historic historical/Y historicity/M historiographer/MS historiography/M history/SM histrionic/S histrionically histrionics/M hit/SM hitch's hitch/UDSG hitcher/MS hitchhike/DRSMZG hitchhiker/M hither hitherto hitter/SM hitting hive/MGDS hiya hmm ho/SMDRYZ hoagie/MS hoard/SZGMDRJ hoarder/M hoarding/M hoarfrost/M hoariness/M hoarse/YTRP hoarseness/M hoary/TRP hoax/MDRSZG hoaxer/M hob/SM hobbit/S hobble/MZGDRS hobbler/M hobby/SM hobbyhorse/MS hobbyist/SM hobgoblin/MS hobnail/SGMD hobnob/S hobnobbed hobnobbing hobo/MS hock/MDSG hockey/M hockshop/MS hod/SM hodgepodge/SM hoe/SM hoecake/SM hoedown/SM hoeing hoer/M hog/SM hogan/SM hogback/SM hogged hogging hoggish/Y hogshead/SM hogtie/DS hogtying hogwash/M hoick/SGD hoist/SGMD hoke/GDS hokey hokier hokiest hokum/M hold/MRJSZG holdall/S holder/M holding/M holdout/SM holdover/SM holdup/MS hole/MGDS holey holiday/SMDG holidaymaker/S holiness/UM holism holistic holistically holler/MDGS hollow/MDRYPSTG hollowness/M holly/SM hollyhock/MS holmium/M holocaust/SM hologram/MS holograph/M holographic holographs holography/M hols holster/SMDG holy/URPT homage/MS hombre/MS homburg/SM home/MYZGDRS homebody/SM homeboy/SM homecoming/SM homegrown homeland/MS homeless/MP homelessness/M homelike homeliness/M homely/PRT homemade homemaker/SM homemaking/M homeopath/M homeopathic homeopaths homeopathy/M homeostasis/M homeostatic homeowner/MS homepage/MS homer/GMD homeroom/MS homeschooling/M homesick/P homesickness/M homespun/M homestead/SMDRZG homesteader/M homestretch/MS hometown/MS homeward/S homework/MRZG homewrecker/SM homey/SMP homeyness/M homicidal homicide/MS homier homiest homiletic homily/SM hominid/SM hominoid/S hominy/M homo/MS homoerotic homogeneity/M homogeneous/Y homogenization/M homogenize/DSG homograph/M homographs homologous homonym/SM homophobia/M homophobic homophone/MS homosexual/SM homosexuality/M hon/SZTGMDR honcho/MS hone/MS honer/M honest/EYT honester honesty/EM honey/SGMD honeybee/SM honeycomb/MDSG honeydew/SM honeylocust/M honeymoon/ZGMDRS honeymooner/M honeypot/S honeysuckle/SM honk/MDRSZG honker/M honky/SM honor/ESGMDB honorableness/M honorably/E honorarily honorarium/MS honorary honoree/SM honorer/SM honorific/MS hooch/M hood/MDSG hoodie/MS hoodlum/SM hoodoo/MDSG hoodwink/DGS hooey/M hoof/MDRSZG hook's hook/UDSG hookah/M hookahs hooker/MS hookup/MS hookworm/MS hooky/M hooligan/MS hooliganism/M hoop/MDSG hoopla/M hooray hoosegow/SM hoot/MDRSZG hootenanny/SM hooter/M hoover/DSG hooves hop/SGMD hope/MS hopeful/PSMY hopefulness/M hopeless/YP hopelessness/M hopped hopper/MS hopping hopscotch/MDSG hora/MS horde/DSMG horehound/SM horizon/SM horizontal/SMY hormonal hormone/SM horn/MDS hornblende/M hornet/MS hornless hornlike hornpipe/MS horny/TR horologic horological horologist/MS horology/M horoscope/SM horrendous/Y horrible/P horribleness/M horribly horrid/Y horrific horrifically horrify/DSG horrifying/Y horror/MS horse's horse/UDSG horseback/M horsebox/S horseflesh/M horsefly/SM horsehair/M horsehide/M horselaugh/M horselaughs horseless horseman/M horsemanship/M horsemen horseplay/M horsepower/M horseradish/MS horseshit/! horseshoe/DSM horseshoeing horsetail/SM horsetrading horsewhip/SM horsewhipped horsewhipping horsewoman/M horsewomen horsey horsier horsiest hortatory horticultural horticulturalist/S horticulture/M horticulturist/MS hosanna/SM hose/MGDS hosepipe/S hosier/MS hosiery/M hosp hospholipase hospice/MS hospitable/I hospitably/I hospital/SM hospitality/M hospitalization/SM hospitalize/DSG host/MDSG hostage/MS hostel/ZGMDRS hosteler/M hostelry/SM hostess/MDSG hostile/MYS hostilities/M hostility/SM hostler/MS hot/SYP hotbed/MS hotblooded hotbox/MS hotcake/SM hotel/SM hotelier/MS hotfoot/MDGS hothead/DSM hotheaded/YP hotheadedness/M hothouse/SM hotkey/S hotlink/S hotness/M hotplate/SM hotpot/S hots/M hotshot/MS hotted hotter hottest hottie/S hotting hound/SGMD hour/MYS hourglass/MS houri/SM house's house/ADSG houseboat/SM housebound houseboy/SM housebreak/RSZG housebreaker/M housebreaking/M housebroke housebroken houseclean/DSG housecleaning/M housecoat/SM housefly/SM houseful/SM household/SMRZ householder/M househusband/SM housekeeper/MS housekeeping/M houselights/M housemaid/SM houseman/M housemaster/S housemate/S housemen housemistress/S housemother/SM houseparent/SM houseplant/MS houseproud houseroom housetop/SM housewares/M housewarming/SM housewife/MY housewives housework/M housing/MS hove hovel/SM hover/SGD hovercraft/M how'd how're how/SM howbeit howdah/M howdahs howdy however howitzer/SM howl/MDRSZG howler/M howsoever hoyden/MS hoydenish hp hr/S ht huarache/SM hub/SM hubbub/SM hubby/SM hubcap/SM hubris/M huckleberry/SM huckster/SGMD hucksterism/M huddle/DSMG hue/DSM huff/MDSG huffily huffiness/M huffy/PRT hug/STMR huge/YP hugeness/M hugged hugging huh hula/MS hulk/MSG hull/MDRSZG hullabaloo/SM huller/M hum/SM human/SMRYTP humane/PY humaneness/M humanism/M humanist/SM humanistic humanitarian/MS humanitarianism/M humanities/M humanity/ISM humanization/CM humanize/CDSG humanizer/SM humankind/M humanness/M humanoid/SM humble/DRSZTGJP humbleness/M humbler/M humbly humbug/SM humbugged humbugging humdinger/MS humdrum/M humeral humeri humerus/M humid/Y humidification/M humidifier/CM humidify/CZGDRS humidity/M humidor/SM humiliate/DSGNX humiliating/Y humiliation/M humility/M hummed hummer/SM humming hummingbird/SM hummock/SM hummocky hummus/M humongous humor/SMDG humoresque humorist/MS humorless/YP humorlessness/M humorous/PY humorousness/M hump/MDSG humpback/MDS humph/DG humphs humus/M hunch/MDSG hunchback/SMD hundred/SMH hundredfold hundredth/M hundredths hundredweight/SM hung hunger/SMDG hungover hungrily hungriness/M hungry/PRT hunk/MRSZ hunker/DG hunky/RT hunt/MDRSZG hunter/M hunting/M huntress/MS huntsman/M huntsmen hurdle/DRSMZG hurdler/M hurdling/M hurl/MDRSZG hurler/M hurling/M hurrah/GMD hurrahs hurricane/MS hurried/UY hurry/DSMG hurt/MSG hurtful/YP hurtfulness/M hurtle/DSG husband/GMDS husbandman/M husbandmen husbandry/M hush/MDSG husk/MDRSZG husker/M huskily huskiness/M husky/PRSMT hussar/SM hussy/SM hustings/M hustle/DRSMZG hustler/M hut/SM hutch/MS huzzah/MDG huzzahs hwy hyacinth/M hyacinths hybrid/SM hybridism/M hybridization/M hybridize/DSG hydra/SM hydrangea/SM hydrant/MS hydrate's hydrate/CGNDS hydration/CM hydraulic/S hydraulically hydraulics/M hydro/M hydrocarbon/MS hydrocephalus/M hydrodynamic/S hydrodynamics/M hydroelectric hydroelectrically hydroelectricity/M hydrofoil/MS hydrogen/M hydrogenate/CGDS hydrogenation/M hydrogenous hydrologist/MS hydrology/M hydrolyses hydrolysis/M hydrolyze/DSG hydrometer/SM hydrometry/M hydrophobia/M hydrophobic hydrophone/SM hydroplane/GDSM hydroponic/S hydroponically hydroponics/M hydrosphere/M hydrotherapy/M hydrous hydroxide/SM hyena/SM hygiene/M hygienic/U hygienically hygienist/MS hygrometer/SM hying hymen/SM hymeneal hymn/MDSG hymnal/MS hymnbook/SM hype/MGDRS hyperactive hyperactivity/M hyperbola/SM hyperbole/M hyperbolic hypercritical/Y hyperglycemia/M hyperinflation hyperlink/GSMD hypermarket/S hypermedia/M hyperparathyroidism hypersensitive/P hypersensitiveness/M hypersensitivity/SM hyperspace/S hypertension/M hypertensive/SM hypertext/M hyperthyroid/M hyperthyroidism/M hypertrophy/DSMG hyperventilate/GNDS hyperventilation/M hyphen/MDSG hyphenate/XDSMGN hyphenation/M hypnoses hypnosis/M hypnotherapist/S hypnotherapy/M hypnotic/SM hypnotically hypnotism/M hypnotist/MS hypnotize/GDS hypo/MS hypoallergenic hypochondria/M hypochondriac/SM hypocrisy/SM hypocrite/MS hypocritical/Y hypodermic/MS hypoglycemia/M hypoglycemic/SM hypotenuse/MS hypothalami hypothalamus/M hypothermia/M hypotheses hypothesis/M hypothesize/DSG hypothetical/Y hypothyroid/M hypothyroidism/M hyssop/M hysterectomy/SM hysteresis hysteria/M hysteric/SM hysterical/Y hysterics/M i/US iPad/M iPhone/M iPod/M iTunes/M iamb/MS iambi iambic/SM iambus/MS ibex/MS ibid ibidem ibis/MS ibuprofen/M ice's ice/CDSG iceberg/SM iceboat/SM icebound icebox/MS icebreaker/SM icecap/SM iceman/M icemen ichthyologist/MS ichthyology/M icicle/SM icily iciness/M icing/SM icky/RT icon/MS iconic iconoclasm/M iconoclast/SM iconoclastic iconography/M ictus/M icy/TPR id/SMY idea/MS ideal/SMY idealism/M idealist/SM idealistic idealistically idealization/MS idealize/DSG idem idempotent identical/Y identifiable/U identification/M identified/U identify/ZGNDRSX identikit/S identity/SM ideogram/SM ideograph/M ideographs ideological/Y ideologist/SM ideologue/MS ideology/SM ides/M idiocy/SM idiom/SM idiomatic/U idiomatically idiopathic idiosyncrasy/SM idiosyncratic idiosyncratically idiot/SM idiotic idiotically idle/MZTGDRSP idleness/M idler/M idol/MS idolater/SM idolatress/MS idolatrous idolatry/M idolization/M idolize/GDS idyll/SM idyllic idyllically if/SM iffiness/M iffy/RTP igloo/SM igneous ignitable ignite/AGDS ignition/MS ignoble ignobly ignominious/Y ignominy/SM ignoramus/MS ignorance/M ignorant/Y ignore/GDS iguana/MS ii iii ilea ileitis/M ileum/M ilia ilium/M ilk/SM ill/SMP illegal/MYS illegality/SM illegibility/M illegible illegibly illegitimacy/M illegitimate/Y illiberal/Y illiberality/M illicit/YP illicitness/M illimitable illiteracy/M illiterate/MYS illness/MS illogical/Y illogicality/M illuminate/GNXDS illuminating/Y illumination/M illumine/DSBG illus/V illusion/EMS illusionist/SM illusory illustrate/GNVXDS illustration/M illustrative/Y illustrator/SM illustrious/PY illustriousness/M image/DSMG imagery/M imaginable/U imaginably/U imaginal imaginary imagination/MS imaginative/UY imagine/DSBJG imago/M imagoes imam/MS imbalance/DSM imbecile/MS imbecilic imbecility/SM imbibe/ZGDRS imbiber/M imbrication/M imbroglio/SM imbue/DSG imitable/I imitate/DSGNVX imitation/M imitative/PY imitativeness/M imitator/SM immaculate/PY immaculateness/M immanence/M immanency/M immanent/Y immaterial/YP immateriality/M immaterialness/M immature/Y immaturity/M immeasurable immeasurably immediacies/M immediacy/SM immediate/PY immediateness/M immemorial/Y immense/Y immensity/SM immerse/XDSGNV immersible immersion/M immigrant/SM immigrate/DSGN immigration/M imminence/M imminent/Y immobile immobility/M immobilization/M immobilize/ZGDRS immoderate/Y immodest/Y immodesty/M immolate/DSGN immolation/M immoral/Y immorality/SM immortal/MYS immortality/M immortalize/DSG immovability/M immovable immovably immune immunity/M immunization/SM immunize/GDS immunodeficiency/M immunodeficient immunoglobulin/S immunologic immunological immunologist/MS immunology/M immure/DSG immutability/M immutable immutably imp/SMR impact/SMDG impair/SDGL impaired/U impairment/MS impala/SM impale/DSGL impalement/M impalpable impalpably impanel/SDG impart/SDG impartial/Y impartiality/M impassably impasse/BSMV impassibility/M impassible impassibly impassioned impassive/YP impassiveness/M impassivity/M impasto/M impatience/MS impatiens/M impatient/Y impeach/ZGBLDRS impeachable/U impeacher/M impeachment/SM impeccability/M impeccable impeccably impecunious/PY impecuniousness/M impedance/M impede/DSG impeded/U impediment/SM impedimenta/M impel/S impelled impeller/MS impelling impend/SDG impenetrability/M impenetrable impenetrably impenitence/M impenitent/Y imperative/SMY imperceptibility/M imperceptible imperceptibly imperceptive imperf imperfect/SMYP imperfection/MS imperfectness/M imperial/MYS imperialism/M imperialist/SM imperialistic imperialistically imperil/GSLD imperilment/M imperious/PY imperiousness/M imperishable imperishably impermanence/M impermanent/Y impermeability/M impermeable impermeably impermissible impersonal/Y impersonate/GNXDS impersonation/M impersonator/SM impertinence/MS impertinent/Y imperturbability/M imperturbable imperturbably impervious/Y impetigo/M impetuosity/M impetuous/YP impetuousness/M impetus/MS impiety/SM impinge/LDSG impingement/M impious/PY impiousness/M impish/YP impishness/M implacability/M implacable implacably implant/BSGMD implantation/M implausibility/SM implausible implausibly implement/GBMDRS implementable/U implementation/SM implemented/U implicate/DSG implication/M implicit/PY implicitness/M implode/DSG implore/DSG imploring/Y implosion/MS implosive imply/XDSGN impolite/YP impoliteness/MS impolitic imponderable/MS import/ZGBSMDR importance/M important/Y importation/MS importer/M importunate/Y importune/GDS importunity/M impose/ADSG imposer/MS imposing/U imposingly imposition/MS impossibility/SM impossible/S impossibly impost/SM impostor/SM imposture/MS impotence/M impotency/M impotent/Y impound/DGS impoverish/DSLG impoverishment/M impracticability impracticable impracticably impractical/Y impracticality/M imprecate/DSXGN imprecation/M imprecise/PYN impreciseness/M imprecision/M impregnability/M impregnable impregnably impregnate/GNDS impregnation/M impresario/SM impress/MDSGV impressed/U impressibility/M impressible impression/BSM impressionability/M impressionism/M impressionist/SM impressionistic impressive/PY impressiveness/M imprimatur/SM imprint/MDRZGS imprinter/M imprison/SDLG imprisonment/SM improbability/SM improbable improbably impromptu/SM improper/Y impropriety/SM improve/GBDSL improved/U improvement/MS improvidence/M improvident/Y improvisation/SM improvisational improvise/ZGDRS improviser/M imprudence/M imprudent/Y impudence/M impudent/Y impugn/ZGSDR impugner/M impulse/MGNVDS impulsion/M impulsive/PY impulsiveness/M impunity/M impure/RYT impurity/SM imputation/SM impute/BDSG in/ASM inaccuracy/S inaction/M inadequacy/S inadvertence/M inadvertent/Y inalienability/M inalienably inamorata/SM inane/RYT inanimate/PY inanimateness/M inanity/SM inappropriate/Y inarticulate/Y inasmuch inaudible inaugural/SM inaugurate/XGNDS inauguration/M inboard/MS inbound inbox/MS inbreed/S inc/TGD incalculably incandescence/M incandescent/Y incantation/SM incapacitate/GDS incarcerate/XDSGN incarceration/M incarnadine/DSG incarnate/AXGNDS incarnation/AM incendiary/SM incense/MGDS incentive's incentive/ES inception/SM incessant/Y incest/M incestuous/PY incestuousness/M inch/MDSG inchoate inchworm/SM incidence/SM incident/SM incidental/MYS incinerate/DSGN incineration/M incinerator/MS incipience/M incipient/Y incise/XGNVDS incision/M incisive/PY incisiveness/M incisor/MS incitement/MS inciter/MS incl inclement inclination/EM inclinations incline's incline/EGDS include/GDS inclusion/MS inclusive/YP inclusiveness/M incognito/MS incombustible incommode/GD incommodious incommunicado incompatibility/S incompetent/MS incomplete/Y inconceivability/M incongruous/PY incongruousness/M inconsolably inconstant/Y incontestability/M incontestably incontinent incontrovertibly inconvenience/GD incorporate/ADSGN incorporated/U incorporation/AM incorporeal incorrect/Y incorrigibility/M incorrigible incorrigibly incorruptibly increasing/Y increment/SMD incremental/Y incrementalism incrementalist/SM incriminate/GNDS incrimination/M incriminatory incrustation/SM incubate/GNDS incubation/M incubator/SM incubus/MS inculcate/DSGN inculcation/M inculpate/DSG incumbency/SM incumbent/SM incunabula incunabulum/M incur/SB incurable/MS incurably incurious incurred incurring incursion/MS ind indebted/P indebtedness/M indeed indefatigable indefatigably indefeasible indefeasibly indefinably indelible indelibly indemnification/M indemnify/GDSXN indemnity/SM indentation/MS indention/M indenture/DG indescribably indestructibly indeterminably indeterminacy/M indeterminate/Y index/ZGMDRS indexation/SM indexer/M indicate/XDSGNV indication/M indicative/SMY indicator/MS indict/GDSBL indictment/SM indie/S indigence/M indigenous indigent/SMY indignant/Y indignation/M indigo/M indirect/Y indiscipline indiscreet/Y indiscretion/S indiscriminate/Y indispensability/M indispensable/MS indispensably indissolubility indissolubly indistinguishably indite/GDS indium/M individual/MYS individualism/M individualist/MS individualistic individualistically individuality/M individualization/M individualize/GDS individuate/DSGN individuation/M indivisibly indoctrinate/GNDS indoctrination/M indolence/M indolent/Y indomitable indomitably indubitable indubitably induce/DRSZGL inducement/SM inducer/M induct/DGV inductance/M inductee/SM induction/MS inductive/Y indulge/DSG indulgence/SM indulgent/Y industrial/Y industrialism/M industrialist/SM industrialization/M industrialize/DSG industrious/YP industriousness/M industry/SM indwell/SG inebriate/MGNDS inebriation/M inedible ineffability/M ineffable ineffably inefficiency/S inelastic ineligible/MS ineligibly ineluctable ineluctably inept/YP ineptitude/M ineptness/M inequality/S inert/YP inertia/M inertial inertness/M inescapable inescapably inestimably inevitability/M inevitable/M inevitably inexact/Y inexhaustibly inexorability inexorable inexorably inexpedient inexpert/Y inexpiable inexplicably inexpressibly inexpressive inextricably inf/ZT infallible infamy/SM infancy/M infant/MS infanticide/MS infantile infantry/SM infantryman/M infantrymen infarct/MS infarction/M infatuate/DSXGN infatuation/M infect/AESDG infected/U infection/ASM infectious/PY infectiousness/M infelicitous inference/SM inferential inferior/MS inferiority/M infernal/Y inferno/MS inferred inferring infest/GDS infestation/MS infidel/MS infidelity/S infiltrator/SM infinite/MV infinitesimal/SMY infinitival infinitive/MS infinitude/M infinity/SM infirm infirmary/SM infirmity/SM infix inflame/DSG inflammable inflammation/SM inflammatory inflatable/SM inflate/DSGNB inflation/EM inflationary inflect/SDG inflection/MS inflectional inflict/SDGV infliction/M inflow/SM influence/MGDS influenced/U influential/Y influenza/M info/M infomercial/SM inform/Z informal/Y informant/SM information/EM informational informative/PY informativeness/M informed/U infotainment/M infra infrared/M infrasonic infrastructural infrastructure/SM infrequence/M infrequent/Y infringement/MS infuriate/GDS infuriating/Y infuser/SM ingenious/PY ingeniousness/M ingenue/SM ingenuity/M ingenuous/EY ingenuousness/M ingest/SDG ingestion/M inglenook/SM ingot/SM ingrain/G ingrate/SM ingratiate/GNDS ingratiating/Y ingratiation/M ingredient/MS ingress/MS inguinal inhabit/DG inhabitable/U inhabitant/SM inhabited/U inhalant/SM inhalation/MS inhalator/MS inhaler/SM inharmonious inhere/DSG inherent/Y inherit/EGSD inheritance/EM inheritances inheritor/SM inhibit/GSD inhibition/SM inhibitor/SM inhibitory inhuman/Y inhumane/Y inimical/Y inimitably iniquitous/Y iniquity/SM initial/SGMDY initialism initialization initialize/DSG initialized/AU initiate/XMGNVDS initiated/U initiation/M initiative/SM initiator/MS initiatory inject/SDG injection/SM injector/SM injure/DRSZG injured/U injurer/M injurious ink/MD inkblot/SM inkiness/M inkling/SM inkstand/SM inkwell/MS inky/RTP inland/M inline inmate/SM inmost inn/SGMRJ innards/M innate/PY innateness/M innermost innersole/SM innerspring innervate/GNDS innervation/M inning/M innit innkeeper/MS innocence/M innocent/MYS innocuous/PY innocuousness/M innovate/XDSGNV innovation/M innovator/MS innovatory innuendo/SM innumerably innumerate inoculate/AGDS inoculation/MS inoperative inordinate/Y inorganic inquire/ZGDR inquirer/M inquiring/Y inquiry/SM inquisition/MS inquisitional inquisitive/YP inquisitiveness/M inquisitor/SM inquisitorial inrush/MS insane/T insatiability/M insatiably inscribe/ZGDR inscriber/M inscription/MS inscrutability/M inscrutable/P inscrutableness/M inscrutably inseam/SM insecticidal insecticide/MS insectivore/MS insectivorous insecure/Y inseminate/DSGN insemination/M insensate insensible insensitive/Y inseparable/MS insert's insert/AGSD insertion/AM insertions insetting inshore inside/RSMZ insider/M insidious/YP insidiousness/M insight/MS insightful insignia/M insinuate/GNVDSX insinuation/M insinuator/SM insipid/PY insipidity/M insist/SGD insistence/M insistent/Y insisting/Y insofar insole/SM insolence/M insolent/Y insoluble insolubly insolvency/S insomnia/M insomniac/SM insomuch insouciance/M insouciant inspect/AGDS inspection/SM inspector/MS inspectorate/MS inspiration/MS inspirational inspired/U inspiring/U inst instability/S installation/MS installer/UMS installment/SM instance/GD instant/MRYS instantaneous/Y instantiate/DSG instar instate/AGDS instead instigate/DSGN instigation/M instigator/MS instillation/M instinct/VMS instinctive/Y instinctual institute/XMZGNDRS instituter/M institution/M institutional/Y institutionalization/M institutionalize/DSG instr instruct/SDGV instructed/U instruction/MS instructional instructive/Y instructor/MS instrument/MDSG instrumental/MYS instrumentalist/SM instrumentality/M instrumentation/M insubordinate insufferable insufferably insular insularity/M insulate/GNDS insulation/M insulator/MS insulin/M insult/SMDG insulting/Y insuperable insuperably insurance/SM insure/DRSZGB insured/SM insurer/M insurgence/SM insurgency/SM insurgent/MS insurmountably insurrection/SM insurrectionist/SM int intact intaglio/MS integer/MS integral/SMY integrate/AEVNGSD integration/EAM integrator integrity/M integument/SM intellect/MS intellectual/MYS intellectualism/M intellectualize/GDS intelligence/M intelligent/Y intelligentsia/M intelligibility/M intelligible/U intelligibly/U intended/SM intense/YTVR intensification/M intensifier/M intensify/DRSZGN intensity/S intensive/MYPS intensiveness/M intent/SMYP intention/MS intentional/UY intentness/M inter/ESL interact/SGVD interaction/SM interactive/Y interactivity interbred interbreed/GS intercede/GDS intercept/GMDS interception/MS interceptor/SM intercession/SM intercessor/MS intercessory interchange/DSMG interchangeability interchangeable interchangeably intercity intercollegiate intercom/SM intercommunicate/DSGN intercommunication/M interconnect/GDS interconnection/SM intercontinental intercourse/M intercultural interdenominational interdepartmental interdependence/M interdependent/Y interdict/GMDS interdiction/M interdisciplinary interest/ESMD interested/U interesting/Y interface/MGDS interfaith interfere/GDS interference/M interferon/M interfile/GDS intergalactic intergovernmental interim/M interior/SM interj interject/GDS interjection/SM interlace/GDS interlard/DGS interleave/DSG interleukin/M interline/GDSJ interlinear interlining/M interlink/DSG interlock/GMDS interlocutor/SM interlocutory interlope/ZGDRS interloper/M interlude/MGDS intermarriage/SM intermarry/GDS intermediary/SM intermediate/MYS interment/EM interments intermezzi intermezzo/MS interminably intermingle/DSG intermission/SM intermittent/Y intermix/GDS intern/GDL internal/SY internalization/M internalize/GDS international/SMY internationalism/M internationalist/SM internationalization internationalize/DSG internecine internee/SM internet internist/MS internment/M internship/MS interoffice interpenetrate/DSGN interpersonal interplanetary interplay/M interpolate/XDSGN interpolation/M interpose/GDS interposition/M interpret/AGVDS interpretation/AMS interpretative interpreted/U interpreter/MS interracial interred/E interregnum/SM interrelate/XDSGN interrelation/M interrelationship/MS interring/E interrogate/DSGNVX interrogation/M interrogative/MYS interrogator/SM interrogatory/SM interrupt/ZGMDRS interrupter/M interruption/MS interscholastic intersect/GDS intersection/SM intersession/SM intersex intersperse/GNDS interspersion/M interstate/MS interstellar interstice/MS interstitial intertwine/GDS interurban interval/SM intervene/GDS intervention/SM interventionism/M interventionist/SM interview/ZGMDRS interviewee/MS interviewer/M intervocalic interwar interweave/GS interwove interwoven intestacy/M intestate intestinal intestine/MS intimacy/SM intimate/MYGNDSX intimation/M intimidate/GNDS intimidating/Y intimidation/M intonation/SM intoxicant/SM intoxicate/DSGN intoxication/M intracranial intramural intramuscular intranet/MS intransigence/M intransigent/MYS intrastate intrauterine intravenous/MSY intrepid/Y intrepidity/M intricacy/SM intricate/Y intrigue/DRSMZG intriguer/M intriguing/Y intrinsic intrinsically intro/SM introduce/AGDS introduction/AM introductions introductory introit/SM introspect/GVDS introspection/M introspective/Y introversion/M introvert/MDS intrude/DRSZG intruder/M intrusion/SM intrusive/YP intrusiveness/M intuit/SDGV intuition/S intuitive/PY intuitiveness/M inundate/XDSGN inundation/M inure/DSG invade/DRSZG invader/M invalid/GMDYS invalidism/M invaluable invaluably invariant invasion/MS invasive invective/M inveigh/GD inveighs inveigle/ZGDRS inveigler/M invent/ASGVD invention/AMS inventive/PY inventiveness/M inventor/MS inventory/DSMG inverse/SMY invert/SMDG invest/ASDGL investigate/GNVDSX investigation/M investigator/SM investigatory investiture/MS investment/AEM investor/SM inveteracy/M inveterate invidious/YP invidiousness/M invigilate/GNDS invigilator/S invigorate/ADSG invigorating/Y invigoration/M invincibility/M invincibly inviolability/M inviolably inviolate invitation/SM invitational/SM invite/DSMG invited/U invitee/SM inviting/Y invoke/DSG involuntariness/M involuntary/P involution/M involve/LDSG involvement/SM inward/SY ioctl iodide/SM iodine/M iodize/DSG ion/USM ionic ionization/UM ionize/UDSG ionizer/MS ionosphere/MS ionospheric iota/MS ipecac/SM irascibility/M irascible irascibly irate/YP irateness/M ire/M ireful irenic irides iridescence/M iridescent/Y iridium/M iris/MS irk/SGD irksome/YP irksomeness/M iron/MDSG ironclad/MS ironic ironical/Y ironing/M ironmonger/S ironmongery ironstone/M ironware/M ironwood/MS ironwork/M irony/SM irradiate/DSGN irradiation/M irrational/SMY irrationality/M irreclaimable irreconcilability/M irreconcilable irreconcilably irrecoverable irrecoverably irredeemable irredeemably irreducible irreducibly irrefutable irrefutably irregardless irregular/MYS irregularity/SM irrelevance/MS irrelevancy/MS irrelevant/Y irreligion irreligious irremediable irremediably irremovable irreparable irreparably irreplaceable irrepressible irrepressibly irreproachable irreproachably irresistible irresistibly irresolute/PYN irresoluteness/M irresolution/M irrespective irresponsibility/M irresponsible irresponsibly irretrievable irretrievably irreverence/M irreverent/Y irreversible irreversibly irrevocable irrevocably irrigable irrigate/DSGN irrigation/M irritability/M irritable irritably irritant/SM irritate/DSXGN irritating/Y irritation/M irrupt/DGVS irruption/SM ischemia ischemic isinglass/M isl island/SZMR islander/M isle/MS islet/SM ism/CM isms isn't isobar/MS isobaric isolate/DSMGN isolation/M isolationism/M isolationist/SM isomer/MS isomeric isomerism/M isometric/S isometrically isometrics/M isomorphic isosceles isotherm/SM isotope/SM isotopic isotropic issuance/M issue/ADSMG issuer/MS isthmian isthmus/MS it'd it'll it/USM ital italic/SM italicization/M italicize/GDS italics/M itch/MDSG itchiness/M itchy/RPT item/MS itemization/M itemize/GDS iterate/AXGNVDS iteration/AM iterator/S itinerant/SM itinerary/SM itself iv/U ivory/SM ivy/DSM ix j/F jab/SM jabbed jabber/SMDRZG jabberer/M jabbing jabot/SM jacaranda/MS jack/MDGS jackal/SM jackass/MS jackboot/SMD jackdaw/MS jacket/SMD jackhammer/MS jackknife/MGDS jackknives jackpot/MS jackrabbit/MS jackstraw/MS jacquard/M jade/MGDS jaded/PY jadedness/M jadeite/M jag/SM jagged/TPRY jaggedness/M jaggies jaguar/SM jail/MDRZGS jailbird/SM jailbreak/SM jailer/M jailhouse/S jalapeno/MS jalopy/SM jalousie/MS jam/SM jamb/MS jambalaya/M jamboree/MS jammed jamming jammy/RT jangle/DRSMZG jangler/M janitor/SM janitorial japan/SM japanned japanning jape/MGDS jar/SM jardiniere/SM jarful/MS jargon/M jarred jarring/Y jasmine/SM jasper/M jato/MS jaundice/DSMG jaunt/SGMD jauntily jauntiness/M jaunty/RPT java/M javelin/SM jaw/SGMD jawbone/DSMG jawbreaker/MS jawline/S jay/SM jaybird/SM jaywalk/DRSZG jaywalker/M jaywalking/M jazz/MDSG jazzy/TR jct jealous/Y jealousy/SM jean/MS jeans/M jeep/MS jeer/MDSG jeering/MY jeez jejuna jejune jejunum/M jell/DSG jello/S jelly/GDSM jellybean/MS jellyfish/MS jellylike jellyroll/SM jemmy/GDS jennet/MS jenny/SM jeopardize/GDS jeopardy/M jeremiad/MS jerk/MDSG jerkily jerkin/MS jerkiness/M jerkwater jerky/TRMP jeroboam/S jerrybuilt jerrycan/S jersey/MS jest/MDRSZG jester/M jesting/Y jet/SM jetliner/SM jetport/MS jetsam/M jetted jetting jettison/MDSG jetty/SM jewel/SZGMDR jeweler/M jewelry/SM jg jib/SGMD jibbed jibbing jibe/MS jiff/MS jiffy/SM jig's jig/AS jigged/A jigger's jigger/ASDG jigging/A jiggle/DSMG jiggly jigsaw/SMDG jihad/SM jihadist/SM jilt/MDSG jimmy/DSMG jimsonweed/M jingle/DSMG jingly jingoism/M jingoist/SM jingoistic jink/DSG jinn jinni/M jinrikisha/SM jinx/MDSG jitney/SM jitterbug/MS jitterbugged jitterbugger/M jitterbugging jitters/M jittery/RT jive/MGDS job/SM jobbed jobber/SM jobbing jobholder/MS jobless/P joblessness/M jobshare/S jobsworth jobsworths jock/MS jockey/SGMD jockstrap/MS jocose/PY jocoseness/M jocosity/M jocular/Y jocularity/M jocund/Y jocundity/M jodhpurs/M joey/S jog/SM jogged jogger/SM jogging/M joggle/DSMG john/MS johnny/SM johnnycake/MS join's join/AFDSG joiner/FMS joinery/M joint's joint/EGSD jointly/F joist/SM jojoba joke/MZGDRS joker/M jokey jokier jokiest joking/Y jollification/SM jollily jolliness/M jollity/M jolly/TGPDRSM jolt/MDRSZG jolter/M jonquil/SM josh/MDRSZG josher/M jostle/MGDS jot/SM jotted jotter/MS jotting/MS joule/SM jounce/MGDS jouncy journal/MS journalese/M journalism/M journalist/SM journalistic journey/ZGMDRS journeyer/M journeyman/M journeymen journo/S joust/SZGMDR jouster/M jousting/M jovial/Y joviality/M jowl/MS jowly/TR joy/SGMD joyful/YP joyfuller joyfullest joyfulness/M joyless/PY joylessness/M joyous/YP joyousness/M joyridden joyride/RSMZG joyrider/M joyriding/M joyrode joystick/SM jubilant/Y jubilation/M jubilee/SM judder/GDS judge's judge/ADSG judgeship/M judgment/SM judgmental/Y judicatory/SM judicature/M judicial/Y judiciary/SM judicious/IYP judiciousness/IM judo/M jug/SM jugful/MS jugged juggernaut/SM jugging juggle/MZGDRS juggler/M jugglery/M jugular/SM juice/DRSMZG juicer/M juicily juiciness/M juicy/PTR jujitsu/M jujube/MS jukebox/MS julep/SM julienne jumble/MGDS jumbo/SM jump/MDRSZG jumper/M jumpily jumpiness/M jumpsuit/MS jumpy/TRP jun junco/SM junction/FISM juncture/FMS jungle/MS junior/MS juniper/SM junk/MDRSZG junker/M junket/MDSG junketeer/MS junkie/MTRS junkyard/MS junta/SM juridic juridical/Y jurisdiction/SM jurisdictional jurisprudence/M jurist/MS juristic juror/SM jury/ISM juryman/M jurymen jurywoman/M jurywomen just/RYPT justice/IMS justifiable/U justifiably/U justification/M justified/U justify/XGDSN justness/M jut/SM jute/M jutted jutting juvenile/SM juxtapose/DSG juxtaposition/SM k/IFGS kHz kW kWh kabbalah kaboom kabuki/M kaddish/MS kaffeeklatch/MS kaffeeklatsch/MS kahuna/S kaiser/MS kale/M kaleidoscope/MS kaleidoscopic kaleidoscopically kamikaze/MS kana kangaroo/MS kanji kaolin/M kapok/M kappa/SM kaput karakul/M karaoke/MS karat/SM karate/M karma/M karmic kart/MS katakana katydid/SM kayak/SMDG kayaking/M kayo/MDSG kazoo/SM kc kebab/SM kedgeree keel/MDSG keelhaul/DGS keen/MDRYSTGP keenness/M keep/MRSZG keeper/M keeping/M keepsake/MS keg/SM kelp/M kelvin/SM ken/SM kenned kennel/SGMD kenning keno/M kepi/MS kept keratin/M keratitis kerbside kerchief/SM kerfuffle/S kernel/SM kerosene/M kestrel/MS ketch/MS ketchup/M kettle/SM kettledrum/SM key/SGMD keybinding/S keyboard/ZGSMDR keyboarder/M keyboardist/SM keyhole/MS keynote/MZGDRS keynoter/M keypad/SM keypunch/ZGMDRS keypuncher/M keystone/MS keystroke/SM keyword/MS kg khaki/SM khan/MS kibble/DSMG kibbutz/MS kibbutzim kibitz/ZGDRS kibitzer/M kibosh/M kick/MDRSZG kickback/SM kickball/M kickboxing kicker/M kickoff/MS kickstand/MS kicky/RT kid/SM kidded kidder/SM kiddie/SM kidding kiddish kiddo/SM kidnap/S kidnapped kidnapper/MS kidnapping/MS kidney/SM kidskin/M kielbasa/MS kielbasi kike/S kill/JMDRSZG killdeer/SM killer/M killing/M killjoy/SM kiln/MDSG kilo/MS kilobyte/SM kilocycle/SM kilogram/SM kilohertz/M kiloliter/MS kilometer/MS kiloton/SM kilowatt/SM kilt/MDRS kilter/M kimono/MS kin/M kinase kind's kind/UPRYT kinda kindergarten/MS kindergartner/SM kindhearted/PY kindheartedness/M kindle/AGDS kindliness/M kindling/M kindly/URT kindness/UM kindnesses kindred/M kinds kine/S kinematic/S kinematics/M kinetic/S kinetically kinetics/M kinfolk/SM kinfolks/M king/MYS kingdom/SM kingfisher/SM kingly/RT kingmaker/S kingpin/SM kingship/M kink/MDSG kinkily kinkiness/M kinky/TPR kinsfolk/M kinship/M kinsman/M kinsmen kinswoman/M kinswomen kiosk/SM kip/SM kipped kipper/MDGS kipping kirsch/MS kismet/M kiss/MDRSBZG kisser/M kissoff/SM kissogram/S kit/SGMD kitchen/SM kitchenette/MS kitchenware/M kite/MS kith/M kitsch/M kitschy kitted kitten/MS kittenish kitting kitty/SM kiwi/MS kiwifruit/MS kl klaxon/S kleptocracy kleptomania/M kleptomaniac/SM kludge/GDS kluge/DS klutz/MS klutziness/M klutzy/TRP km kn knack/SZMR knacker/GD knapsack/MS knave/SM knavery/M knavish/Y knead/SZGDR kneader/M knee/MDS kneecap/SM kneecapped kneecapping kneeing kneel/SG knell/SGMD knelt knew knicker/S knickerbockers/M knickers/M knickknack/MS knife/DSMG knight/MDYSG knighthood/MS knightliness/M knish/MS knit/MS knitted knitter/SM knitting/M knitwear/M knives knob/MS knobbly knobby/TR knock/SZGMDR knockabout knockdown/SM knocker/M knockoff/SM knockout/SM knockwurst/SM knoll/SM knot/MS knothole/SM knotted knotting knotty/TR know/SB knowing/UYS knowledge/M knowledgeable knowledgeably known knuckle/DSMG knuckleduster/S knucklehead/MS knurl/SGMD koala/SM koan/S kohl kohlrabi/M kohlrabies kola/MS kook/MS kookaburra/SM kookiness/M kooky/TPR kopeck/MS korma kosher/DSG kowtow/GMDS kph kraal/SM kraut/SM! krill/M krona/M krone/RM kronor kronur krypton/M kt kuchen/SM kudos/M kudzu/SM kumquat/MS kvetch/GMDS kw l/SDXTGJ la/M lab/SM label's label/ASDG labeled/U labia labial/SM labile labium/M labor/SMDRZG laboratory/SM laborer/M laborious/PY laboriousness/M laborsaving laburnum/MS labyrinth/M labyrinthine labyrinths lac/M lace's lace/UGDS lacerate/DSGNX laceration/M lacewing/SM lacework/M lachrymal lachrymose lack/MDSG lackadaisical/Y lackey/SM lackluster laconic laconically lacquer/GMDS lacrosse/M lactate/GNDS lactation/M lacteal lactic lactose/M lacuna/M lacunae lacy/RT lad/SGMDNJ ladder/GSMD laddie/SM laddish/P lade/S laden/U lading/M ladle/DSMG lady/SM ladybird/SM ladybug/MS ladyfinger/MS ladylike/U ladylove/MS ladyship/MS laetrile/M lag/SZMR lager/M laggard/MYS lagged lagging/M lagniappe/SM lagoon/SM laid/IA lain lair/MS laird/SM laity/M lake/MS lakefront/S lakeside lam/SM lama/MS lamasery/SM lamb/MDSG lambada/MS lambaste/GDS lambda/SM lambency/M lambent/Y lambkin/SM lambskin/SM lambswool lame/MYZTGDRSP lamebrain/MS lameness/M lament/BSMDG lamentably lamentation/MS lamina/M laminae laminar laminate/MGNDS lamination/M lammed lamming lamp/MS lampblack/M lamplight/MRZ lamplighter/M lampoon/SGMD lamppost/SM lamprey/MS lampshade/SM lanai/SM lance/DRSMZG lancer/M lancet/SM land/MDRSGJ landau/SM landfall/MS landfill/MS landholder/SM landholding/MS landing/M landlady/SM landless/M landline/MS landlocked landlord/MS landlubber/MS landmark/MS landmass/MS landmine/S landowner/MS landownership landowning/SM landscape/MZGDRS landscaper/M landslid landslide/MGS landslip/S landsman/M landsmen landward/S lane/MS language/MS languid/PY languidness/M languish/DSG languor/SM languorous/Y lank/RYTP lankiness/M lankness/M lanky/RTP lanolin/M lantern/MS lanthanum/M lanyard/MS lap/SM laparoscopic laparoscopy laparotomy lapboard/SM lapdog/SM lapel/SM lapidary/SM lapin/SM lapped lappet/SM lapping lapse/AKGMSD laptop/SM lapwing/MS larboard/SM larcenist/SM larcenous larceny/SM larch/MS lard/MDRSZG larder/M lardy/RT large/RSPMYT largehearted largeness/M largess/M largish largo/SM lariat/SM lark/MDSG larkspur/SM larva/M larvae larval laryngeal larynges laryngitis/M larynx/M lasagna/MS lascivious/YP lasciviousness/M lase/ZGDRS laser/M lash/MDSGJ lashing/M lass/MS lassie/SM lassitude/M lasso/SMDG last/MDYSG lasting/Y lat/S latch's latch/UDSG latchkey/SM late/YTRP latecomer/MS latency/M lateness/M latent lateral/MDYSG latest/M latex/M lath/MDRSZG lathe/M lather/GMD lathery laths latices latish latitude/MS latitudinal latitudinarian/MS latrine/MS latte/RSM latter/MY lattice/MDS latticework/SM laud/MDSGB laudably laudanum/M laudatory laugh/BMDG laughably laughing/MY laughingstock/SM laughs laughter/M launch/AGMDS launcher/SM launchpad/SM launder/DRZGS launderer/M launderette/SM laundress/MS laundromat/MS laundry/SM laundryman/M laundrymen laundrywoman/M laundrywomen laureate/MS laureateship/M laurel/SM lav/SGD lava/M lavage/M lavaliere/SM lavatorial lavatory/SM lave/S lavender/SM lavish/PTGDRSY lavishness/M law/SM lawbreaker/SM lawbreaking/M lawful/UPY lawfulness/UM lawgiver/MS lawless/PY lawlessness/M lawmaker/MS lawmaking/M lawman/M lawmen lawn/MS lawnmower/SM lawrencium/M lawsuit/MS lawyer/SM lax/TRYP laxative/MS laxity/M laxness/M lay/AICSGM layabout/S layaway/M layer/CSM layered layering/M layette/MS layman/M laymen layoff/SM layout/SM layover/MS laypeople layperson/MS layup/SM laywoman/M laywomen laze/MGDS lazily laziness/M lazy/DRSTGP lazybones/M lb/S lbw lea/SM leach/DSG lead/MDNRSZG leader/M leaderless leadership/SM leading/M leaf/MDSG leafage/M leafless leaflet/GMDS leafstalk/MS leafy/RT league/DSMG leak/MDSG leakage/MS leakiness/M leaky/PRT lean/MDRSTGJP leaning/M leanness/M leap/MDRSZG leaper/M leapfrog/MS leapfrogged leapfrogging learn/AUGDS learnedly learner/MS learning's lease/ADSMG leaseback/SM leasehold/MRSZ leaseholder/M leaser/SM leash's leash/UDSG least/M leastwise leather/MS leatherette/M leatherneck/MS leathery leave/DRSMZGJ leaven/SGMD leavened/U leavening/M leaver/M leavings/M lech/MDRSZG lecher/M lecherous/PY lecherousness/M lechery/M lecithin/M lectern/MS lecture/MZGDRS lecturer/M lectureship/SM ledge/RSMZ ledger/M lee/RSMZ leech/MDSG leek/MS leer/MDG leeriness/M leery/RPT leeward/SM leeway/M left/MRST leftism/M leftist/SM leftmost leftover/SM leftward/S lefty/SM leg/SM legacy/SM legal/SMY legalese/M legalism/MS legalistic legalistically legality/SM legalization/M legalize/GDS legate/CXMNS legatee/MS legation's/AC legato/SM legend/SM legendarily legendary legerdemain/M legged legginess/M legging/MS leggy/RPT leghorn/MS legibility/M legible legibly legion/SM legionary/SM legionnaire/SM legislate/DSGNV legislation/M legislative/Y legislator/MS legislature/SM legit legitimacy/M legitimate/DSYG legitimatize/GDS legitimization/M legitimize/DSG legless legman/M legmen legroom/SM legume/MS leguminous legwarmer/S legwork/M lei/SM leisure/DMY leisureliness/M leisurewear/M leitmotif/MS leitmotiv/MS lemma/S lemme/JG lemming/M lemon/SM lemonade/SM lemongrass lemony lemur/SM lend/RSZG lender/M length/MNX lengthen/GD lengthily lengthiness/M lengths lengthwise lengthy/PRT lenience/M leniency/M lenient/Y lenitive lens/MS lent lentil/MS lento leonine leopard/SM leopardess/MS leotard/SM leper/SM leprechaun/MS leprosy/M leprous lepta lepton/MS lesbian/SM lesbianism/M lesion/MS less/MNRX lessee/MS lessen/GD lesson/MS lessor/MS let/ISM letdown/SM lethal/Y lethargic lethargically lethargy/M letter/ZGMDRS letterbomb/S letterbox/S lettered/U letterer/M letterhead/MS lettering/M letterpress/M letting/S lettuce/MS letup/SM leucotomy/S leukemia/M leukemic/SM leukocyte/MS levee/SM level/PSZGMDRY leveler/M levelheaded/P levelheadedness/M levelness/M lever/SGMD leverage's leverage/CDSG leviathan/MS levier/M levitate/DSGN levitation/M levity/M levy/DRSMZG lewd/RYPT lewdness/M lexer/S lexical lexicographer/MS lexicographic lexicographical lexicography/M lexicon/SM lexis lg liabilities liability/AM liable/A liaise/GDS liaison/MS liar/MS lib/M libation/SM libber/MS libel/SZGMDR libeler/M libelous liberal/MYPS liberalism/M liberality/M liberalization/SM liberalize/GDS liberalness/M liberate/CDSGN liberation/CM liberator/MS libertarian/SM libertine/MS liberty/SM libidinal libidinous libido/MS librarian/MS librarianship library/SM librettist/MS libretto/SM lice license/MGDS licensed/U licensee/MS licentiate/SM licentious/YP licentiousness/M lichen/MS licit/Y lick/MDJSG licking/M licorice/SM lid/SM lidded lidless lido/MS lie/DSM lied/MR lief/RT liege/SM lien/MS lieu/M lieutenancy/M lieutenant/MS life/MZR lifebelt/S lifeblood/M lifeboat/MS lifebuoy/MS lifeforms lifeguard/SM lifeless/YP lifelessness/M lifelike lifeline/MS lifelong lifer/M lifesaver/SM lifesaving/M lifespan/S lifestyle/SM lifetime/MS lifework/MS lift/MDRSZG lifter/M liftoff/SM ligament/MS ligate/GNDS ligation/M ligature/MGDS light's/C light/CASTGD lighted/U lighten/SDRZG lightener/M lighter/SM lightface/MD lightheaded lighthearted/YP lightheartedness/M lighthouse/MS lighting's lightly lightness/M lightning/MDS lightproof lightship/MS lightweight/SM ligneous lignite/M lii likability/M likable/P likableness/M like/EMGDST likelihood/UM likelihoods likeliness/UM likely/UPRT liken/SGD likeness/UM likenesses liker likewise liking/M lilac/SM lilliputian lilo/S lilt/MDSG lily/SM limb/MS limber/UDSG limberness/M limbless limbo/SM lime/MGDS limeade/SM limelight/M limerick/SM limescale limestone/M limey/S limit's limit/CSZGDR limitation/CM limitations limited/U limiter's limiting/S limitless/P limitlessness/M limn/DSG limo/MS limousine/MS limp/MDRYSPTG limpet/MS limpid/YP limpidity/M limpidness/M limpness/M limy/RT linage/M linchpin/SM linden/MS line/MZGDRSJ lineage/MS lineal/Y lineament/SM linear/Y linearity/M linebacker/MS lined/U linefeed lineman/M linemen linen/SM linens/M liner/M linesman/M linesmen lineup/MS ling/M linger/ZGJDRS lingerer/M lingerie/M lingering/Y lingo/M lingoes lingual linguine/M linguist/SM linguistic/S linguistically linguistics/M liniment/SM lining/M link/MDRSG linkage/MS linkman linkmen linkup/MS linnet/MS lino linoleum/M linseed/M lint's lint/CDG lintel/MS lints linty/TR lion/MS lioness/MS lionhearted lionization/M lionize/GDS lip/SM lipid/SM liposuction/M lipped lippy lipread/GRS lipreader/M lipreading/M lipstick/MDSG liq liquefaction/M liquefy/DSG liqueur/SM liquid/MS liquidate/XGNDS liquidation/M liquidator/MS liquidity/M liquidize/ZGDRS liquidizer/M liquor/MDGS lira/M lire lisle/M lisp/MDRSZG lisper/M lissome list/MDNSJXG listed/U listen/BMDRZG listener/M listeria listing/M listless/YP listlessness/M lit/ZR litany/SM litchi/MS lite liter/M literacy/M literal/SMYP literalness/M literariness/M literary/P literate/SMY literati/M literature/M lithe/RPYT litheness/M lithesome lithium/M lithograph/MDRZG lithographer/M lithographic lithographically lithographs lithography/M lithosphere/SM litigant/SM litigate/DSGN litigation/M litigator/MS litigious/P litigiousness/M litmus/M litotes/M litter/MDRSZG litterateur/MS litterbug/MS litterer/M little/MTRP littleness/M littoral/SM liturgical/Y liturgist/SM liturgy/SM livability/M livable/U live/ATGDSB livelihood/SM liveliness/M livelong/S lively/PRT liven/SGD liver's liver/S liveried liverish liverwort/MS liverwurst/M livery/CSM liveryman/CM liverymen/C livestock/M liveware livid/Y living/MS lix/K lizard/MS ll llama/SM llano/SM lo load's load/AUGSD loadable loader/MS loading's loaf/MDRSZG loafer/M loam/M loamy/TR loan/MDRSZG loaner/M loansharking/M loanword/MS loath/JZGDRS loathe loather/M loathing/M loathsome/PY loathsomeness/M loaves lob/SMD lobar lobbed lobber/MS lobbing lobby/GDSM lobbyist/MS lobe/MS lobotomize/DSG lobotomy/SM lobster/MS local/SMY locale/MS locality/SM localization/M localize/DSG locate/EAGNDS location's/A location/ESM locator/MS locavore/SM loci lock/MDRSBZG locker/M locket/MS lockjaw/M lockout/MS locksmith/M locksmiths lockstep/M lockup/MS loco/S locomotion/M locomotive/MS locoweed/SM locum/S locus/M locust/SM locution/MS lode/MS lodestar/MS lodestone/MS lodge/DRSJMZG lodger/M lodging/M lodgings/M loft/MDSG loftily loftiness/M lofty/PRT log/SM loganberry/SM logarithm/SM logarithmic logbook/SM loge/MS logged logger/SM loggerhead/SM loggia/SM logging/M logic/M logical/Y logicality/M logician/MS login/S logistic/S logistical/Y logistics/M logjam/SM logo/MS logoff/S logon/S logotype/SM logout/S logrolling/M logy/RT loin/MS loincloth/M loincloths loiter/ZGSDR loiterer/M loitering/M lolcat/SM loll/DSG lollipop/SM lollop/GSD lolly/S lollygag/S lollygagged lollygagging lone/YZR loneliness/M lonely/PTR loner/M lonesome/YP lonesomeness/M long's long/KDSTG longboat/MS longbow/MS longer longevity/M longhair/MS longhand/M longhorn/MS longhouse/S longing/MYS longish longitude/MS longitudinal/Y longshoreman/M longshoremen longsighted longstanding longtime longueur/SM longways loo loofah/M loofahs look/MDRSZG lookalike/MS looker/M lookout/MS lookup loom/MDSG loon/MS loonie/M loony/RSMT loop/MDSG loophole/MS loopy/RT loos/NRX loose/UDSTG loosely loosen/UGSD looseness/M loot/MDRSZG looter/M looting/M lop/S lope/MGDS lopped lopping lopsided/YP lopsidedness/M loquacious/PY loquaciousness/M loquacity/M lord/MDYSG lordliness/M lordly/TPR lordship/SM lore/M lorgnette/SM loris/MS lorn lorry/SM lose/ZGRSJ loser/M losing/M loss/MS lossless lost lot/SM lotion/SM lottery/SM lotto/M lotus/MS louche loud/RYTP loudhailer/SM loudmouth/MD loudmouths loudness/M loudspeaker/MS lough loughs lounge/MZGDRS lounger/M lour/DSG louse's louse/CDSG lousily lousiness/M lousy/TPR lout/MS loutish/PY louver/MDS lovableness/M lovably love/MYZGDRSB lovebird/SM lovechild/M loved/U loveless loveliness/M lovelorn lovely/RSMTP lovemaking/M lover/M lovesick lovey/S loving/Y low/SZTGMDRYP lowborn lowboy/MS lowbrow/SM lowdown/M lower/GD lowercase/M lowermost lowish lowland/SZMR lowlander/M lowlife/SM lowliness/M lowly/TPR lowness/M lox/M loyal/ETY loyaler loyalism/M loyalist/SM loyalties loyalty/EM lozenge/SM ltd luau/MS lubber/MYS lube/MGDS lubricant/SM lubricate/DSGN lubrication/M lubricator/MS lubricious/Y lubricity/M lucid/PY lucidity/M lucidness/M luck/MDSG luckily/U luckiness/UM luckless lucky/UPTR lucrative/YP lucrativeness/M lucre/M lucubrate/GNDS lucubration/M ludicrous/YP ludicrousness/M ludo luff/DSG lug/SM luge/S luggage/M lugged lugger/MS lugging lughole/S lugsail/SM lugubrious/YP lugubriousness/M lukewarm/YP lukewarmness/M lull/MDSG lullaby/SM lulu/S lumbago/M lumbar lumber/MDRZGS lumberer/M lumbering/M lumberjack/SM lumberman/M lumbermen lumberyard/SM luminary/SM luminescence/M luminescent luminosity/M luminous/Y lummox/MS lump/MDNSG lumpectomy/S lumpiness/M lumpish lumpy/TRP lunacy/SM lunar lunatic/SM lunch/GMDS lunchbox/S luncheon/SM luncheonette/SM lunchroom/MS lunchtime/MS lung/MDSG lunge/SM lungfish/MS lungful/S lunkhead/MS lupine/MS lupus/M lurch/GMDS lure/MGDS lurgy lurid/PY luridness/M lurk/DRSZG luscious/PY lusciousness/M lush/MRSYPT lushness/M lust/MDRSG luster/M lusterless lustful/Y lustily lustiness/M lustrous/Y lusty/PTR lutanist/SM lute/MS lutenist/SM lutetium/M luxuriance/M luxuriant/Y luxuriate/DSGN luxuriation/M luxurious/PY luxuriousness/M luxury/SM lvi lvii lxi lxii lxiv lxix lxvi lxvii lyceum/MS lychgate/S lye/MG lying/M lymph/M lymphatic/SM lymphocyte/SM lymphoid lymphoma/SM lynch/JZGDRS lyncher/M lynching/M lynx/MS lyre/MS lyrebird/MS lyric/SM lyrical/Y lyricism/M lyricist/SM lysosomal lysosomes m/KAS ma'am ma/SMH mac/SGMD macabre macadam/M macadamia/SM macadamize/GDS macaque/MS macaroni/MS macaroon/MS macaw/SM mace/MS macerate/DSGN maceration/M mach/M machete/SM machinate/GNDSX machination/M machine/DSMGB machinery/M machinist/MS machismo/M macho/M mackerel/SM mackinaw/SM mackintosh/MS macrame/M macro/SM macrobiotic/S macrobiotics/M macrocosm/SM macroeconomic/S macroeconomics/M macrology/S macron/MS macrophages macroscopic mad/SMYP madam/SM madame/M madcap/MS madden/DGS maddening/Y madder/MS maddest madding made/AU mademoiselle/MS madhouse/SM madman/M madmen madness/M madras/MS madrasa/S madrassah madrassahs madrigal/SM madwoman/M madwomen maelstrom/SM maestro/SM mafia/SM mafiosi mafioso/M mag/SM magazine/SM mage/MS magenta/M maggot/MS maggoty magi/M magic/SM magical/Y magician/SM magicked magicking magisterial/Y magistracy/M magistrate/SM magma/M magnanimity/M magnanimous/Y magnate/SM magnesia/M magnesium/M magnet/MS magnetic magnetically magnetism/M magnetite/M magnetizable magnetization/CM magnetize/CGDS magneto/SM magnetometer/SM magnetosphere magnification/M magnificence/M magnificent/Y magnifier/M magnify/ZGXDRSN magniloquence/M magniloquent magnitude/SM magnolia/MS magnum/MS magpie/MS magus/M maharajah/M maharajahs maharani/SM maharishi/SM mahatma/SM mahogany/SM mahout/MS maid/MNSX maiden/MY maidenhair/M maidenhead/SM maidenhood/M maidservant/SM mail/JMDRSZG mailbag/SM mailbomb/GSD mailbox/MS mailer/M mailing/M maillot/SM mailman/M mailmen mailshot/S maim/DSG main/MYS mainframe/SM mainland/MS mainline/MGDS mainmast/MS mainsail/MS mainspring/MS mainstay/MS mainstream/SMDG maintain/ZGBDRS maintainability maintenance/M maintop/SM maisonette/MS maize/SM majestic majestically majesty/SM majolica/M major/SGMDY majordomo/MS majorette/MS majoritarian/SM majoritarianism majority/SM make's/A make/UAGS makeover/MS maker/SM makeshift/SM makeup/MS makeweight/S making/MS makings/M malachite/M maladjusted maladjustment/M maladministration maladroit/PY maladroitness/M malady/SM malaise/M malamute/MS malapropism/SM malaria/M malarial malarkey/M malathion/M malcontent/MS male/MPS malediction/SM malefaction/M malefactor/SM malefic maleficence/M maleficent maleness/M malevolence/M malevolent/Y malfeasance/M malformation/SM malformed malfunction/MDSG malice/M malicious/PY maliciousness/M malign/DSG malignancy/SM malignant/Y malignity/M malinger/ZGSDR malingerer/M mall/MS mallard/SM malleability/M malleable mallet/MS mallow/MS malnourished malnutrition/M malocclusion/M malodorous malpractice/SM malt/MDSG malted/MS maltose/M maltreat/GLDS maltreatment/M malty/TR malware/M mam/S mama/MS mamba/SM mambo/SGMD mamma/M mammal/MS mammalian/MS mammary mammogram/MS mammography/M mammon/M mammoth/M mammoths mammy/SM man's/F man/USY manacle/DSMG manage/ZGDRSL manageability/M manageable/U management/MS manager/M manageress/S managerial manana/MS manatee/SM mandala/SM mandamus/MS mandarin/MS mandate/DSMG mandatory mandible/MS mandibular mandolin/MS mandrake/MS mandrel/SM mandrill/MS mane/MDS manege/M maneuver/MDGSBJ maneuverability/M manful/Y manga/M manganese/M mange/DRMZ manger/M mangetout/S manginess/M mangle/MZGDRS mango/M mangoes mangrove/MS mangy/TRP manhandle/GDS manhole/SM manhood/M manhunt/SM mania/SM maniac/MS maniacal/Y manic/SM manically manicure/MGDS manicurist/MS manifest/MDYSG manifestation/SM manifesto/SM manifold/GMDS manikin/SM manila/M manioc/MS manipulable manipulate/XGNVDS manipulation/M manipulative/Y manipulator/MS mankind/M manky manlike manliness/M manly/UTR manna/M manned/U mannequin/SM manner/MDYS mannerism/SM mannerly/U manning/U mannish/YP mannishness/M manometer/SM manor/SM manorial manpower/M manque mansard/MS manse/SXMN manservant/M mansion/M manslaughter/M manta/SM mantel/MS mantelpiece/SM mantelshelf mantelshelves mantes mantilla/SM mantis/MS mantissa/SM mantle's mantle/EGDS mantra/MS manual/MYS manufacture/DRSMZG manufacturer/M manufacturing/M manumission/SM manumit/S manumitted manumitting manure/MGDS manuscript/MS many/M map's map/AS maple/SM mapmaker/SM mapped/A mapper/MS mapping/S mar/S marabou/MS marabout/SM maraca/MS maraschino/MS marathon/SMRZ marathoner/M maraud/ZGDRS marauder/M marble/MGDS marbleize/GDS marbling/M march/ZGMDRS marcher/M marchioness/MS mare/MS margarine/M margarita/MS marge margin/MS marginal/YS marginalia/M marginalization/M marginalize/GDS maria/M mariachi/MS marigold/MS marijuana/M marimba/SM marina/MS marinade/DSMG marinara/M marinate/DSGN marination/M marine/MZRS mariner/M marionette/MS marital/Y maritime marjoram/M mark/AMDSG markdown/SM marked/U markedly marker/MS market/MDRZGBS marketability/M marketable/U marketeer/SM marketer/M marketing/M marketplace/SM marking/SM markka/M markkaa marksman/M marksmanship/M marksmen markup/MS marl/M marlin/MS marlinespike/SM marmalade/M marmoreal marmoset/SM marmot/MS maroon/MDGS marque/MS marquee/SM marquess/MS marquetry/M marquis/MS marquise/M marquisette/M marred/U marriage/ASM marriageability/M marriageable married/SM marring marrow/MS marry/AGDS marsh/MS marshal/SMDG marshland/SM marshmallow/SM marshy/RT marsupial/MS mart/MNSX marten/M martensite martial/Y martian/S martin/MS martinet/MS martingale/MS martini/SM martyr/MDGS martyrdom/M marvel/MDGS marvelous/Y marzipan/M masc mascara/GMDS mascot/MS masculine/SM masculinity/M maser/SM mash/MDRSZG masher/M mashup/MS mask's mask/UDSG masker/MS masochism/M masochist/SM masochistic masochistically mason/SM masonic masonry/M masque/MS masquerade/DRSMZG masquerader/M mass/MDSGV massacre/MGDS massage/DSMG masseur/SM masseuse/MS massif/MS massive/PY massiveness/M mast/MDS mastectomy/SM master's master/ADGS masterclass/S masterful/Y masterly mastermind/SGMD masterpiece/MS masterstroke/SM masterwork/MS mastery/M masthead/MS mastic/M masticate/GNDS mastication/M mastiff/SM mastitis mastodon/SM mastoid/SM masturbate/GNDS masturbation/M masturbatory mat/SZGMDR matador/SM match/AMS matchbook/SM matchbox/MS matched/U matching matchless matchlock/SM matchmaker/MS matchmaking/M matchstick/MS matchwood/M mate/MS material/SMY materialism/M materialist/SM materialistic materialistically materialization/M materialize/DSG materiel/M maternal/Y maternity/M matey/S math/M mathematical/Y mathematician/SM mathematics/M maths matinee/SM mating/M matins/M matriarch/M matriarchal matriarchs matriarchy/SM matrices matricidal matricide/MS matriculate/DSGN matriculation/M matrimonial matrimony/M matrix/M matron/MYS matte/DRSMZG matter/MDG matting/M mattock/SM mattress/MS maturate/GNDS maturation/M mature/YTGDRS maturity/SM matzo/SMH matzoh/M matzohs matzot maudlin maul/MDRSZG mauler/M maunder/SDG mausoleum/SM mauve/M maven/SM maverick/SM maw/SM mawkish/PY mawkishness/M max/GMDS maxi/MS maxilla/M maxillae maxillary maxim/SM maximal/Y maximization/M maximize/GDS maximum/SM may/M maybe/SM mayday/MS mayflower/MS mayfly/SM mayhem/M mayn't mayo/M mayonnaise/M mayor/SM mayoral mayoralty/M mayoress/MS maypole/SM mayst maze/MS mazurka/MS mdse me/DSH mead/M meadow/MS meadowlark/MS meager/PY meagerness/M meal/MS mealiness/M mealtime/SM mealy/TPR mealybug/SM mealymouthed mean/MRYJPSTG meander/SMDJG meanderings/M meanie/M meaning/M meaningful/PY meaningfulness/M meaningless/YP meaninglessness/M meanness/M meant/U meantime/M meanwhile/M meany/SM meas measles/M measly/RT measurable measurably measure's measure/ADSG measured/U measureless measurement/MS meat/MS meatball/MS meathead/MS meatiness/M meatless meatloaf/M meatloaves meatpacking/M meaty/TPR mecca/SM mechanic/MS mechanical/Y mechanics/M mechanism/SM mechanistic mechanistically mechanization/M mechanize/DSG medal/SM medalist/MS medallion/SM meddle/ZGDRS meddler/M meddlesome media/SM medial/AY median/MS mediate/DSGN mediated/U mediation/AM mediator/MS medic/SM medicaid/M medical/SMY medicament/M medicare/M medicate/GNXDS medication/M medicinal/Y medicine/MS medico/MS medieval medievalist/MS mediocre mediocrity/SM meditate/DSGNVX meditation/M meditative/Y medium/MS medley/MS medulla/SM medusa medusae meed/M meek/RYPT meekness/M meerschaum/SM meet/MJSG meeting/M meetinghouse/SM meetup meg/S mega megabit/SM megabucks/M megabyte/MS megachurch/MS megacycle/SM megadeath/M megadeaths megahertz/M megalith/M megalithic megaliths megalomania/M megalomaniac/SM megalopolis/MS megaphone/DSMG megapixel/SM megastar/S megaton/SM megawatt/MS meh meiosis/M meiotic melamine/M melancholia/M melancholic/S melancholy/M melange/MS melanin/M melanoma/SM meld/MDSG melee/SM meliorate/GNVDS melioration/M mellifluous/PY mellifluousness/M mellow/PTGDRYS mellowness/M melodic melodically melodious/YP melodiousness/M melodrama/MS melodramatic/S melodramatically melodramatics/M melody/SM melon/SM melt's melt/ADSG meltdown/SM member's member/EAS membership/SM membrane/SM membranous meme/S memento/MS memo/MS memoir/MS memorabilia/M memorability/M memorable/U memorably memorandum/MS memorial/SM memorialize/DSG memorization/M memorize/DSG memory/SM memsahib/S men/M menace/MGDS menacing/Y menage/MS menagerie/MS mend/MDRSZG mendacious/Y mendacity/M mendelevium/M mender/M mendicancy/M mendicant/SM mending/M menfolk/MS menfolks/M menhaden/M menial/MYS meningeal meninges meningitis/M meninx/M menisci meniscus/M menopausal menopause/M menorah/M menorahs mensch/MS menservants menses/M menstrual menstruate/GNDS menstruation/M mensurable mensuration/M menswear/M mental/Y mentalist/SM mentality/SM menthol/M mentholated mention/GSMD mentioned/U mentor/MDSG menu/MS meow/MDSG mercantile mercantilism/M mercenary/SM mercer/MS mercerize/GDS merchandise/MZGDRS merchandiser/M merchandising/M merchant/MBS merchantman/M merchantmen merciful/UY merciless/PY mercilessness/M mercurial/Y mercuric mercury/M mercy/SM mere/MYTS meretricious/YP meretriciousness/M merganser/MS merge/DRSZG merger/M meridian/MS meringue/MS merino/MS merit/CSM merited/U meriting meritocracy/SM meritocratic meritorious/PY meritoriousness/M mermaid/SM merman/M mermen merrily merriment/M merriness/M merry/TRP merrymaker/MS merrymaking/M mesa/MS mescal/MS mescalin mescaline/M mesdames mesdemoiselles mesh/MDSG mesmeric mesmerism/M mesmerize/ZGDRS mesmerizer/M mesomorph/M mesomorphs meson/SM mesosphere/SM mesquite/SM mess/MDSG message/MGDS messeigneurs messenger/SM messiah/M messiahs messianic messieurs messily messiness/M messmate/SM messy/PTR mestizo/MS met meta metabolic metabolically metabolism/SM metabolite/SM metabolize/DSG metacarpal/SM metacarpi metacarpus/M metadata metal/SMD metalanguage/MS metallic metallurgic metallurgical metallurgist/MS metallurgy/M metalwork/MRZG metalworker/M metalworking/M metamorphic metamorphism/M metamorphose/GDS metamorphosis/M metaphor/MS metaphoric metaphorical/Y metaphysical/Y metaphysics/M metastases metastasis/M metastasize/DSG metastatic metatarsal/MS metatarsi metatarsus/M metatheses metathesis/M mete/MZGDRS metempsychoses metempsychosis/M meteor/MS meteoric meteorically meteorite/SM meteoroid/SM meteorologic meteorological meteorologist/SM meteorology/M meter/GMD methadone/M methamphetamine/M methane/M methanol/M methinks method/MS methodical/YP methodicalness/M methodological/Y methodology/SM methotrexate methought meths methyl/M meticulous/YP meticulousness/M metier/MS metric/S metrical/Y metricate/GNDS metrication/M metricize/GDS metro/SM metronome/MS metropolis/MS metropolitan mettle/M mettlesome mew/SGMD mewl/DSG mews/M mezzanine/MS mezzo/SM mfg mfr/S mg mgr mi/MNX miasma/MS mic/S mica/M mice mick/S mickey/MS micro/SM microbe/MS microbial microbiological microbiologist/MS microbiology/M microbrewery/SM microchip/MS microcircuit/SM microcode microcomputer/MS microcosm/MS microcosmic microdot/SM microeconomics/M microelectronic/S microelectronics/M microfiber/MS microfiche/M microfilm/GMDS microfloppies microgroove/SM microlight/MS microloan/MS micromanage/GDSL micromanagement/M micrometeorite/SM micrometer/MS micron/MS microorganism/MS microphone/SM microprocessor/MS microscope/SM microscopic microscopical/Y microscopy/M microsecond/MS microsurgery/M microwave/DSMGB microwaveable mid midair/M midday/M midden/MS middle/MGS middlebrow/SM middleman/M middlemen middlemost middleweight/MS middy/SM midfield/RZ midge/SM midget/MS midi/MS midland/MS midlife/M midmost midnight/M midpoint/MS midrib/MS midriff/MS midsection/MS midshipman/M midshipmen midships midsize midst/M midstream/M midsummer/M midterm/MS midtown/M midway/MS midweek/MS midwife/MGDS midwifery/SM midwinter/M midwives midyear/MS mien/M miff/DSG might've might/M mightily mightiness/M mightn't mighty/TRP mignonette/SM migraine/MS migrant/MS migrate/AGDS migration/SM migratory mikado/MS mike/MGDS mil/SZMR milady/SM milch mild/MRYTP mildew/SMDG mildness/M mile/MS mileage/SM milepost/MS miler/M milestone/MS milf/MS milieu/SM militancy/M militant/MYS militarily militarism/M militarist/SM militaristic militarization/CM militarize/CDSG military/M militate/GDS militia/SM militiaman/M militiamen milk/MDRSZG milker/M milkiness/M milkmaid/MS milkman/M milkmen milkshake/SM milksop/MS milkweed/SM milky/RTP mill/MDRSZGJ millage/M millennia millennial/MS millennium/MS miller/M millet/M milliard/MS millibar/MS milligram/MS milliliter/MS millimeter/MS milliner/MS millinery/M milling/M million/HSM millionaire/SM millionairess/S millionth/M millionths millipede/SM millisecond/SM millpond/SM millrace/SM millstone/SM millstream/MS millwright/SM milometer/S milquetoast/SM milt/MDSG mime/MGDS mimeograph/GMD mimeographs mimetic mimic/SM mimicked mimicker/SM mimicking mimicry/SM mimosa/SM min minaret/MS minatory mince/DRSMZG mincemeat/M mincer/M mind's mind/ADRSZG mindbogglingly minded/P mindful/YP mindfulness/M mindless/YP mindlessness/M mindset/MS mine/MZGNDRSX minefield/SM miner/M mineral/MS mineralogical mineralogist/MS mineralogy/M minestrone/M minesweeper/SM mingle/DSG mingy mini/MS miniature/MS miniaturist/MS miniaturization/M miniaturize/GDS minibar/S minibike/SM minibus/MS minicab/S minicam/MS minicomputer/SM minifloppies minim/SM minimal/Y minimalism/M minimalist/MS minimization minimize/DSG minimum/MS mining/M minion/M miniseries/M miniskirt/MS minister/SGMD ministerial ministrant/MS ministration/MS ministry/SM minivan/MS mink/MS minnesinger/MS minnow/SM minor/SMDG minority/SM minoxidil/M minster/MS minstrel/SM minstrelsy/M mint/MDRSZG mintage/M minter/M minty/RT minuend/MS minuet/SM minus/MS minuscule/MS minute/PDRSMYTG minuteman/M minutemen minuteness/M minutia/M minutiae minx/MS miracle/MS miraculous/Y mirage/SM mire/MGDS mirror/GSMD mirth/M mirthful/PY mirthfulness/M mirthless/Y miry/RT misaddress/DSG misadventure/MS misaligned misalignment/M misalliance/MS misanthrope/SM misanthropic misanthropically misanthropist/MS misanthropy/M misapplication/M misapply/DSGNX misapprehend/GSD misapprehension/MS misappropriate/XDSGN misappropriation/M misbegotten misbehave/GDS misbehavior/M misc miscalculate/DSXGN miscalculation/M miscall/DSG miscarriage/MS miscarry/GDS miscast/SG miscegenation/M miscellaneous/Y miscellany/SM mischance/SM mischief/M mischievous/YP mischievousness/M miscibility/M miscible miscommunication misconceive/GDS misconception/SM misconduct/MDGS misconstruction/MS misconstrue/GDS miscount/MDSG miscreant/SM miscue/DSMG misdeal/GMS misdealt misdeed/MS misdemeanor/MS misdiagnose/GDS misdiagnosis/M misdid misdirect/SDG misdirection/M misdo/JG misdoes misdoing/M misdone miser/SBMY miserableness/M miserably miserliness/M misery/SM misfeasance/M misfeature/S misfile/GDS misfire/MGDS misfit/SM misfitted misfitting misfortune/SM misgiving/MS misgovern/SDGL misgovernment/M misguidance/M misguide/DSG misguided/Y mishandle/DSG mishap/SM mishear/GS misheard mishit/S mishitting mishmash/MS misidentify/GDS misinform/DGS misinformation/M misinterpret/SGD misinterpretation/SM misjudge/DSG misjudgment/SM mislabel/GSD mislaid mislay/GS mislead/GS misleading/Y misled mismanage/LGDS mismanagement/M mismatch/GMDS misname/GDS misnomer/MS misogamist/MS misogamy/M misogynist/SM misogynistic misogynous misogyny/M misplace/GLDS misplacement/M misplay/GMDS misprint/GMDS misprision/M mispronounce/DSG mispronunciation/SM misquotation/MS misquote/MGDS misread/GJS misreading/M misreport/MDGS misrepresent/GDS misrepresentation/MS misrule/MGDS miss's miss/EDSGV missal/ESM missed/U misshape/GDS misshapen missile/MS missilery/M mission/AMS missionary/SM missioner/SM missive/MS misspeak/GS misspell/GDJS misspelling/M misspend/GS misspent misspoke misspoken misstate/GDSL misstatement/SM misstep/MS missus/MS mist's mist/CDRSZG mistakable/U mistake/BMGS mistaken/Y mister's mistily mistime/GDS mistiness/M mistletoe/M mistook mistral/MS mistranslated mistreat/LDGS mistreatment/M mistress/MS mistrial/MS mistrust/MDSG mistrustful/Y misty/PRT mistype/GS misunderstand/SGJ misunderstanding/M misunderstood misuse/DSMG mite/MZRS miter/MDG mitigate/DSGN mitigated/U mitigation/M mitochondria mitochondrial mitochondrion mitoses mitosis/M mitotic mitt/MNSX mitten/M mix/ZGMDRSB mixed/U mixer/M mixture/SM mizzen/MS mizzenmast/SM mks ml mm mnemonic/MS mnemonically mo/CKHS moan/MDRSZG moaner/M moat/MDS mob's mob/CS mobbed/C mobbing/C mobile/MS mobility/M mobilization/CM mobilizations mobilize/CDSG mobilizer/SM mobster/SM moccasin/SM mocha/M mock/DRSZG mocker/M mockery/SM mocking/Y mockingbird/SM mod/STM modal/SM modality/S modded modding mode/MS model/ZGSJMDR modeler/M modeling/M modem/SM moderate/MYGNPDS moderateness/M moderation/M moderator/SM modern/MYPS modernism/M modernist/SM modernistic modernity/M modernization/M modernize/DRSZG modernizer/M modernness/M modest/Y modesty/M modicum/SM modifiable modification/M modified/U modifier/M modify/DRSXZGN modish/YP modishness/M modular modulate/CGNDS modulation/CM modulations modulator/MS module/MS modulo modulus moggy mogul/SM mohair/M moi moiety/SM moil/MDSG moire/SM moist/XTPNRY moisten/DRZG moistener/M moistness/M moisture/M moisturize/ZGDRS moisturizer/M molar/SM molasses/M mold/MDRJSZG moldboard/SM molder/GMD moldiness/M molding/M moldy/TPR mole/MS molecular molecularity/M molecule/SM molehill/SM moleskin/M molest/DRZGS molestation/M molested/U molester/M moll/MS mollification/M mollify/DSNG molluscan mollusk/SM molly/SM mollycoddle/DSMG molt/MDNRSZG molter/M molybdenum/M mom/SM moment/MS momenta momentarily momentariness/M momentary/P momentous/PY momentousness/M momentum/M mommy/SM monarch/M monarchic monarchical monarchism/M monarchist/MS monarchistic monarchs monarchy/SM monastery/SM monastic/MS monastical/Y monasticism/M monaural monetarily monetarism/M monetarist/MS monetary monetize/CGDS money/SMD moneybag/MS moneybox/S moneylender/SM moneymaker/SM moneymaking/M monger/MDGS mongol/S mongolism/M mongoloid/MS mongoose/MS mongrel/SM monies moniker/SM monism/M monist/MS monition/SM monitor/SMDG monitory monk/MS monkey/MDGS monkeyshine/SM monkish monkshood/SM mono/M monochromatic monochrome/MS monocle/DSM monoclonal monocotyledon/SM monocotyledonous monocular monodic monodist/SM monody/SM monogamist/MS monogamous/Y monogamy/M monogram/SM monogrammed monogramming monograph/M monographs monolingual/MS monolith/M monolithic monoliths monologist/SM monologue/SM monomania/M monomaniac/MS monomaniacal monomer/SM mononucleosis/M monophonic monoplane/SM monopolist/SM monopolistic monopolization/M monopolize/DRSZG monopolizer/M monopoly/SM monorail/MS monosyllabic monosyllable/MS monotheism/M monotheist/SM monotheistic monotone/MS monotonic monotonically monotonous/PY monotonousness/M monotony/M monounsaturated monoxide/MS monseigneur/M monsieur/M monsignor/SM monsoon/SM monsoonal monster/SM monstrance/ASM monstrosity/SM monstrous/Y montage/SM month/MY monthly/SM months monument/MS monumental/Y moo/SGMD mooch/ZGMDRS moocher/M mood/MS moodily moodiness/M moody/TPR moon/MDSG moonbeam/MS moonless moonlight/SMDRZG moonlighter/M moonlighting/M moonlit moonscape/SM moonshine/MZRS moonshiner/M moonshot/MS moonstone/MS moonstruck moonwalk/MS moor/MDJSG moorhen/S mooring/M moorland/MS moose/M moot/DSG mop/SZGMDR mope/MS moped/SM moper/M mopey mopier mopiest mopish mopped moppet/MS mopping moraine/SM moral/SMY morale/M moralist/MS moralistic moralistically moralities morality/UM moralization/CM moralize/CGDS moralizer/MS morass/MS moratorium/SM moray/SM morbid/YP morbidity/M morbidness/M mordancy/M mordant/SMY more/MS moreish morel/SM moreover mores/M morgue/MS moribund morn/MJSG morning/M morocco/M moron/SM moronic moronically morose/YP moroseness/M morph/GD morpheme/MS morphemic morphia/M morphine/M morphing/M morphological morphology/M morphs morrow/MS morsel/MS mortal/MYS mortality/M mortar/MDSG mortarboard/SM mortgage's mortgage/AGDS mortgagee/MS mortgagor/MS mortician/MS mortification/M mortify/NGDS mortise/DSMG mortuary/SM mosaic/MS mosey/SGD mosh/DSG mosque/MS mosquito/M mosquitoes moss/MS mossback/SM mossy/TR most/MY mot/SM mote's mote/KCXSVN motel/SM motet/SM moth/M mothball/GMDS mother/MDYSG motherboard/SM motherfucker/MS! motherfucking/! motherhood/M motherland/MS motherless motherliness/M moths motif/SM motile/S motility/M motion/KCM motioned motioning motionless/YP motionlessness/M motivate/CDSG motivated/U motivation/SM motivational motivator/SM motive/MS motiveless motley/MS motlier motliest motocross/MS motor/SGMD motorbike/MGDS motorboat/MS motorcade/MS motorcar/SM motorcycle/DSMG motorcyclist/MS motorist/SM motorization/M motorize/DSG motorman/M motormen motormouth/M motormouths motorway/SM mottle/GDS motto/M mottoes moue/MS mound/SGMD mount/EASGMD mountable mountain/SM mountaineer/SMDG mountaineering/M mountainous mountainside/SM mountaintop/SM mountebank/MS mounted/U mounter/MS mounting/SM mourn/SZGDR mourned/U mourner/M mournful/YP mournfulness/M mourning/M mouse/DRSMZG mouser/M mousetrap/SM mousetrapped mousetrapping mousiness/M moussaka/S mousse/MGDS mousy/PTR mouth/GMD mouthful/MS mouthiness/M mouthpiece/MS mouths mouthwash/MS mouthwatering mouthy/PTR mouton/M movable/SM move/AMZGDRSB moved/U movement/SM mover/AM movie/SM moviegoer/SM moving/Y mow/SZGMDR mower/M moxie/M mozzarella/M mp mpg mph mt mtg mtge mu/SM much/M mucilage/M mucilaginous muck/MDSG muckrake/DRSZG muckraker/M mucky/TR mucous mucus/M mud/M muddily muddiness/M muddle/MGDS muddleheaded muddy/PTGDRS mudflap/S mudflat/MS mudguard/SM mudpack/S mudroom/MS mudslide/MS mudslinger/SM mudslinging/M muenster/M muesli muezzin/MS muff/MDSG muffin/MS muffle/ZGDRS muffler/M mufti/SM mug/SM mugful/MS mugged mugger/MS mugginess/M mugging/MS muggins muggle/MS muggy/PTR mugshot/MS mugwump/MS mujaheddin mukluk/MS mulatto/M mulattoes mulberry/SM mulch/GMDS mulct/SGMD mule/MS muleskinner/MS muleteer/MS mulish/PY mulishness/M mull/DSG mullah/M mullahs mullein/M mullet/MS mulligan/SM mulligatawny/M mullion/SMD multi multicolored multicultural multiculturalism/M multidimensional multidisciplinary multifaceted multifamily multifarious/PY multifariousness/M multiform multilateral/Y multilayered multilevel multilingual multilingualism/M multimedia/M multimillionaire/SM multinational/SM multiparty multiplayer/M multiple/MS multiplex/ZGMDRS multiplexer/M multiplicand/MS multiplication/M multiplicative multiplicity/SM multiplier/M multiply/NZGDRSX multiprocessing multiprocessor/SM multipurpose multiracial multistage multistory multitask/GS multitasking/M multitude/SM multitudinous multivariate multiverse/SM multivitamin/MS multiyear mum/SM mumble/MZGDRS mumbler/M mumbletypeg/M mummer/MS mummery/M mummification/M mummify/GNDS mummy/SM mumps/M mun munch/GDS munchies/M munchkin/SM mundane/SY mung/DSG municipal/SMY municipality/SM munificence/M munificent/Y munition/MDGS mural/SM muralist/SM murder/ZGMDRS murderer/M murderess/MS murderous/Y murk/MS murkily murkiness/M murky/PTR murmur/ZGJMDRS murmurer/M murmuring/M murmurous murrain/M muscat/MS muscatel/SM muscle/MGDS musclebound muscleman musclemen muscly muscular/Y muscularity/M musculature/M muse/MGDSJ musette/MS museum/MS mush/MDRSZG mushiness/M mushroom/GSMD mushy/PTR music/SM musical/MYS musicale/MS musicality/M musician/SMY musicianship/M musicological musicologist/MS musicology/M musing/MY musk/M muskeg/MS muskellunge/MS musket/MS musketeer/MS musketry/M muskie/M muskiness/M muskmelon/SM muskox/MN muskrat/MS musky/PTRS muslin/M muss/MDSG mussel/MS mussy/TR must've must/MRSZ mustache/MDS mustachio/SMD mustang/MS mustard/M muster/GMD mustily mustiness/M mustn't musty/PTR mutability/M mutably mutagen/MS mutant/MS mutate/XGNVDS mutation/M mutational mute/MYTGDRSPB muteness/M mutilate/DSGNX mutilation/M mutilator/SM mutineer/SM mutinous/Y mutiny/GDSM mutt/MS mutter/ZGJMDRS mutterer/M muttering/M mutton/M muttonchops/M mutual/Y mutuality/M muumuu/MS muzak muzzily muzzle/DSMG muzzy/P my mycologist/SM mycology/M myelitis/M myna/MS myocardial myocardium myopia/M myopic myopically myriad/SM myrmidon/MS myrrh/M myrtle/SM mys myself mysterious/PY mysteriousness/M mystery/SM mystic/SM mystical/Y mysticism/M mystification/CM mystify/CDSGN mystique/M myth/M mythic mythical mythological mythologist/SM mythologize/DSG mythology/SM myths myxomatosis n/IKTH naan/S nab/S nabbed nabbing nabob/SM nacelle/SM nacho/SM nacre/M nacreous nadir/SM nae naff/RT nag/SM nagged nagger/MS nagging nagware nah naiad/SM naif/MS nail/MDSG nailbrush/MS naive/RYT naivete/M naivety/M naked/PY nakedness/M name's name/AGDS nameable/U named/U namedrop namedropping/M nameless/Y namely nameplate/MS namesake/SM nanny/SM nanobot/S nanosecond/SM nanotechnology/SM nap/SM napalm/MDSG nape/MS naphtha/M naphthalene/M napkin/MS napless napoleon/SM napped napper/MS napping nappy/TRSM narc/MS narcissism/M narcissist/MS narcissistic narcissus/M narcolepsy/M narcoleptic narcoses narcosis/M narcotic/SM narcotization/M narcotize/GDS nark narky narrate/GNVDSX narration/M narrative/SM narrator/SM narrow/PTGMDRYS narrowness/M narwhal/MS nary nasal/SMY nasality/M nasalization/M nasalize/DSG nascence/AM nascent/A nastily nastiness/M nasturtium/SM nasty/PTR natal natch nation/MS national/MYS nationalism/M nationalist/SM nationalistic nationalistically nationality/SM nationalization/MS nationalize/CDSG nationhood/M nationwide native/MS nativity/SM natl natter/GMDS nattily nattiness/M natty/PTR natural's natural/UPY naturalism/M naturalist/SM naturalistic naturalization/M naturalize/DSG naturalness/UM naturals nature's nature/CS naturism naturist/S naught/MS naughtily naughtiness/M naughty/PTR nausea/M nauseate/GDS nauseating/Y nauseous/PY nauseousness/M nautical/Y nautilus/MS naval nave/MS navel/SM navigability/M navigable navigate/DSGN navigation/M navigational navigator/MS navvy/S navy/SM nay/SM naysayer/MS ne'er neanderthal/MS neap/MS near/DRYSPTG nearby nearness/M nearshore nearside nearsighted/YP nearsightedness/M neat/NRYPXT neaten/GD neath neatness/M nebula/M nebulae nebular nebulous/PY nebulousness/M necessarily/U necessary/SM necessitate/DSG necessitous necessity/SM neck/MDSG neckband/S neckerchief/MS necking/M necklace/MGDSJ neckline/MS necktie/MS necrology/M necromancer/SM necromancy/M necrophilia necrophiliac/S necropolis/MS necroses necrosis/M necrotic nectar/M nectarine/MS nee need/MDSG needed/U needful/Y neediness/M needle/MGDS needlepoint/M needless/YP needlessness/M needlewoman/M needlewomen needlework/M needn't needy/PTR nefarious/YP nefariousness/M neg negate/DSGNVX negation/M negative/MYGPDS negativeness/M negativism/M negativity/M neglect/SGMD neglectful/YP neglectfulness/M negligee/MS negligence/M negligent/Y negligible negligibly negotiability/M negotiable/A negotiate/ADSGN negotiation/AM negotiations negotiator/MS negritude/M negro negroid neigh/MDG neighbor/SMDYG neighborhood/SM neighborliness/M neighs neither nelson/SM nematode/SM nemeses nemesis/M neoclassic neoclassical neoclassicism/M neocolonialism/M neocolonialist/MS neocon/S neoconservative/SM neodymium/M neolithic neologism/SM neon/M neonatal neonate/MS neophilia neophyte/MS neoplasm/MS neoplastic neoprene/M nepenthe/M nephew/SM nephrite/M nephritic nephritis/M nepotism/M nepotist/SM nepotistic neptunium/M nerd/MS nerdy/RT nerve's nerve/UDSG nerveless/YP nervelessness/M nerviness/M nervous/YP nervousness/M nervy/TPR nest/MDSG nestle/GJDS nestling/M net/SM netball netbook/MS nether nethermost netherworld/M netiquette/S netted netter/S netting/M nettle/MGDS nettlesome network/SGMD networking/M neural/Y neuralgia/M neuralgic neurasthenia/M neurasthenic/MS neuritic/MS neuritis/M neurological/Y neurologist/SM neurology/M neuron/MS neuronal neuroses neurosis/M neurosurgeon/MS neurosurgery/M neurosurgical neurotic/MS neurotically neurotransmitter/SM neut neuter/MDGS neutral/SMY neutralism/M neutralist/SM neutrality/M neutralization/M neutralize/DRSZG neutralizer/M neutrino/SM neutron/SM never nevermore nevertheless nevi nevus/M new/STMRYP newbie/MS newborn/SM newcomer/SM newel/SM newfangled newfound newline/S newlywed/SM newness/M news/M newsagent/S newsboy/SM newscast/SMRZ newscaster/M newsdealer/SM newsflash/S newsgirl/SM newsgroup/MS newshound/S newsletter/MS newsman/M newsmen newspaper/MS newspaperman/M newspapermen newspaperwoman/M newspaperwomen newspeak newsprint/M newsreader/S newsreel/MS newsroom/MS newsstand/SM newsweekly/SM newswoman/M newswomen newsworthiness/M newsworthy/P newsy/TR newt/MS newton/MS next/M nexus/MS niacin/M nib/SM nibble/MZGDRS nibbler/M nice/PYTR niceness/M nicety/SM niche/SM nick/MDRSZG nickel/MS nickelodeon/SM nicker/MDG nickle/S nickname/DSMG nicotine/M niece/SM niff niffy nifty/TR nigga/S niggard/SMY niggardliness/M niggaz nigger/SM! niggle/MZGDRS niggler/M nigh/RT night/SMY nightcap/SM nightclothes/M nightclub/SM nightclubbed nightclubbing nightdress/MS nightfall/M nightgown/SM nighthawk/SM nightie/SM nightingale/SM nightlife/M nightlight/S nightlong nightmare/SM nightmarish nightshade/SM nightshirt/SM nightspot/MS nightstand/SM nightstick/SM nighttime/M nightwatchman nightwatchmen nightwear/M nihilism/M nihilist/MS nihilistic nil/M nimbi nimble/TPR nimbleness/M nimbly nimbus/M nimby nimrod/MS nincompoop/SM nine/MS ninepin/MS ninepins/M nineteen/SMH nineteenth/M nineteenths ninetieth/M ninetieths ninety/HSM ninja/SM ninny/SM ninth/M ninths niobium/M nip/SM nipped nipper/MS nippiness/M nipping nipple/MS nippy/TPR nirvana/M nisei/M nit/SMR niter/M nitpick/SZGDR nitpicker/M nitpicking/M nitrate/DSMGN nitration/M nitrification/M nitrite/SM nitro nitrocellulose/M nitrogen/M nitrogenous nitroglycerin/M nitwit/MS nix/GMDS no/SM nob/SY nobble/GDS nobelium/M nobility/M noble/RSPMT nobleman/M noblemen nobleness/M noblewoman/M noblewomen nobody/SM nocturnal/Y nocturne/MS nod/SM nodal nodded nodding noddle/MS noddy node/MS nodular nodule/MS noel/MS noes noggin/MS nohow noise/DSMG noiseless/PY noiselessness/M noisemaker/MS noisily noisiness/M noisome noisy/PTR nomad/SM nomadic nomenclature/MS nominal/Y nominate/ACGNVDS nomination's/A nomination/CSM nominative/SM nominator/CSM nominee/MS non nonabrasive nonabsorbent/SM nonacademic nonacceptance/M nonacid nonactive/MS nonaddictive nonadhesive nonadjacent nonadjustable nonadministrative nonage/MS nonagenarian/MS nonaggression/M nonalcoholic nonaligned nonalignment/M nonallergic nonappearance/MS nonassignable nonathletic nonattendance/M nonautomotive nonavailability/M nonbasic nonbeliever/MS nonbelligerent/MS nonbinding nonbreakable nonburnable noncaloric noncancerous nonce/M nonchalance/M nonchalant/Y nonchargeable nonclerical/MS nonclinical noncollectable noncom/MS noncombat noncombatant/MS noncombustible noncommercial/MS noncommittal/Y noncommunicable noncompeting noncompetitive noncompliance/M noncomplying noncomprehending nonconducting nonconductor/MS nonconforming nonconformism nonconformist/MS nonconformity/M nonconsecutive nonconstructive noncontagious noncontinuous noncontributing noncontributory noncontroversial nonconvertible noncooperation/M noncorroding noncorrosive noncredit noncriminal/SM noncritical noncrystalline noncumulative noncustodial nondairy nondeductible/M nondelivery/SM nondemocratic nondenominational nondepartmental nondepreciating nondescript nondestructive nondetachable nondisciplinary nondisclosure/M nondiscrimination/M nondiscriminatory nondramatic nondrinker/MS nondrying none noneducational noneffective nonelastic nonelectric nonelectrical nonempty nonenforceable nonentity/SM nonequivalent/MS nonessential nonesuch/MS nonetheless nonevent/MS nonexchangeable nonexclusive nonexempt/M nonexistence/M nonexistent nonexplosive/MS nonfactual nonfading nonfat nonfatal nonfattening nonferrous nonfiction/M nonfictional nonflammable nonflowering nonfluctuating nonflying nonfood/M nonfreezing nonfunctional nongovernmental nongranular nonhazardous nonhereditary nonhuman nonidentical noninclusive nonindependent nonindustrial noninfectious noninflammatory noninflationary noninflected nonintellectual/MS noninterchangeable noninterference/M nonintervention/M nonintoxicating noninvasive nonirritating nonissue nonjudgmental nonjudicial nonlegal nonlethal nonlinear nonliterary nonliving/M nonmagnetic nonmalignant nonmember/MS nonmetal/SM nonmetallic nonmigratory nonmilitant nonmilitary nonnarcotic/SM nonnative/MS nonnegotiable nonnuclear nonnumerical nonobjective nonobligatory nonobservance/M nonobservant nonoccupational nonoccurrence/M nonofficial nonoperational nonoperative nonparallel/MS nonpareil/MS nonparticipant/MS nonparticipating nonpartisan/SM nonpaying nonpayment/SM nonperformance/M nonperforming nonperishable nonperson/MS nonphysical/Y nonplus/S nonplussed nonplussing nonpoisonous nonpolitical nonpolluting nonporous nonpracticing nonprejudicial nonprescription nonproductive nonprofessional/SM nonprofit/SMB nonproliferation/M nonpublic nonpunishable nonracial nonradioactive nonrandom nonreactive nonreciprocal/SM nonreciprocating nonrecognition/M nonrecoverable nonrecurring nonredeemable nonrefillable nonrefundable nonreligious nonrenewable nonrepresentational nonresident/MS nonresidential nonresidual/M nonresistance/M nonresistant nonrestrictive nonreturnable/MS nonrhythmic nonrigid nonsalaried nonscheduled nonscientific nonscoring nonseasonal nonsectarian nonsecular nonsegregated nonsense/M nonsensical/Y nonsensitive nonsexist nonsexual nonskid nonslip nonsmoker/SM nonsmoking nonsocial nonspeaking nonspecialist/MS nonspecializing nonspecific nonspiritual/SM nonstaining nonstandard nonstarter/MS nonstick nonstop nonstrategic nonstriking nonstructural nonsuccessive nonsupport/GM nonsurgical nonsustaining nonsympathizer/M nontarnishable nontaxable nontechnical nontenured nontheatrical nonthinking nonthreatening nontoxic nontraditional nontransferable nontransparent nontrivial nontropical nonuniform nonunion nonuser/MS nonvenomous nonverbal nonviable nonviolence/M nonviolent/Y nonvirulent nonvocal nonvocational nonvolatile nonvoter/MS nonvoting nonwhite/MS nonworking nonyielding nonzero noodle/MGDS nook/MS nookie nooky noon/M noonday/M noontide/M noontime/M noose/SM nope nor nor'easter norm/MS normal/MY normalcy/M normality/M normalization/M normalize/DSG normative north/ZMR northbound northeast/MRZ northeaster/MY northeastern northeastward/S norther/MY northerly/SM northern/ZR northerner/M northernmost northward/S northwest/ZMR northwester/MY northwestern northwestward/S nose/MGDS nosebag/S nosebleed/MS nosecone/SM nosedive/DSMG nosegay/SM nosh/MDRSZG nosher/M nosily nosiness/M nostalgia/M nostalgic nostalgically nostril/MS nostrum/MS nosy/RPT not/B notability/SM notable/SM notably notarial notarization/M notarize/GDS notary/SM notate/GDS notation/FCSM notch/GMDS note's note/FCSDG notebook/MS notelet/S notepad/S notepaper/M noteworthiness/M noteworthy/P nothing/PSM nothingness/M notice/MGDS noticeable/U noticeably noticeboard/S noticed/U notifiable notification/M notifier/M notify/NDRSXZG notion/MS notional/Y notoriety/M notorious/Y notwithstanding notwork/S nougat/MS noun/KMS nourish/DSLG nourishment/M nous nova/MS novae novel/SM novelette/SM novelist/SM novelization/MS novelize/DSG novella/MS novelty/SM novena/MS novene novice/MS novitiate/MS now/M nowadays/M noway/S nowhere/M nowise nowt noxious nozzle/MS nu/SM nuance/MDS nub/SM nubbin/MS nubby/TR nubile nuclear/K nucleate/DSGN nucleation/M nuclei nucleic nucleoli nucleolus/M nucleon/SM nucleoside nucleotide nucleus/M nude/MTRS nudge/GDSM nudism/M nudist/SM nudity/M nugatory nugget/SM nuisance/MS nuke/MGDS null/S nullification/M nullify/NDSG nullity/M numb/ZTGPDRYS number's number/ASDG numbered/U numberless numbness/M numerable/I numeracy/IM numeral/SM numerate/XGNDS numeration/M numerator/MS numeric numerical/Y numerologist/MS numerology/M numerous/Y numinous numismatic/S numismatics/M numismatist/SM numskull/MS nun/SM nuncio/SM nunnery/SM nuptial/MS nurse/MZGDRS nurselings nursemaid/MS nurser/M nursery/SM nurseryman/M nurserymen nursing/M nursling/SM nurture/DRSMZG nurturer/M nut/SM nutcase/S nutcracker/MS nuthatch/MS nuthouse/S nutmeat/SM nutmeg/SM nutpick/SM nutria/SM nutrient/MS nutriment/MS nutrition/M nutritional/Y nutritionist/SM nutritious/YP nutritiousness/M nutritive nutshell/MS nutted nutter/S nuttiness/M nutting nutty/RTP nuzzle/DRSMZG nuzzler/M nybble/S nylon/MS nylons/M nymph/M nymphet/MS nympho/S nymphomania/M nymphomaniac/SM nymphs o o'clock o'er oaf/SM oafish/PY oafishness/M oak/SMN oakum/M oar/SGMD oarlock/SM oarsman/M oarsmen oarswoman/M oarswomen oases oasis/M oat/SMN oatcake/SM oath/M oaths oatmeal/M oats/M ob/S obbligato/MS obduracy/M obdurate/PY obdurateness/M obedience/EM obedient/EY obeisance/SM obeisant obelisk/MS obese obesity/M obey/EDSG obfuscate/GNXDS obfuscation/M obi/SM obit/MS obituary/SM obj object/SGVMD objectify/NGDS objection/SMB objectionable/U objectionably objective/SMYP objectiveness/M objectivity/M objector/MS objurgate/XGNDS objurgation/M oblate/NX oblation/M obligate/DSXGN obligation/M obligatorily obligatory oblige/EGDS obliging/Y oblique/SMYP obliqueness/M obliquity/M obliterate/DSGN obliteration/M oblivion/M oblivious/YP obliviousness/M oblong/MS obloquy/M obnoxious/YP obnoxiousness/M oboe/MS oboist/MS obscene/RYT obscenity/SM obscurantism/M obscurantist/SM obscure/DRSYTG obscurity/SM obsequies obsequious/PY obsequiousness/M obsequy/M observably observance/MS observant/Y observation/SM observational observatory/SM observe/DRSBZG observed/U observer/M obsess/DSGV obsession/SM obsessional/Y obsessive/PSMY obsessiveness/M obsidian/M obsolesce/DSG obsolescence/M obsolescent obsolete/GDS obstacle/MS obstetric/S obstetrical obstetrician/SM obstetrics/M obstinacy/M obstinate/Y obstreperous/YP obstreperousness/M obstruct/DGVS obstructed/U obstruction/SM obstructionism/M obstructionist/MS obstructive/YP obstructiveness/M obtain/DBLGS obtainable/U obtainment/M obtrude/DSG obtrusion/M obtrusive/UPY obtrusiveness/UM obtuse/YTRP obtuseness/M obverse/SM obviate/DSGN obviation/M obvious/PY obviousness/M ocarina/MS occasion/GMDS occasional/Y occidental/SM occlude/GDS occlusion/SM occlusive occult/M occultism/M occultist/SM occupancy/M occupant/SM occupation/AM occupational/Y occupations occupied/U occupier/SM occupy/ADSG occur/AS occurred/A occurrence/SM occurring/A ocean/SM oceanfront/SM oceangoing oceanic/M oceanographer/SM oceanographic oceanography/M oceanology/M ocelot/MS och/R ocher/M ocker/S octagon/MS octagonal octal octane/MS octave/MS octavo/MS octet/SM octogenarian/SM octopus/MS ocular/MS oculist/SM odalisque/SM odd/STRYLP oddball/SM oddity/SM oddment/SM oddness/M odds/M ode/SM odious/YP odiousness/M odium/M odometer/MS odor/MDS odoriferous odorless odorous odyssey/MS oedipal oenology/M oenophile/SM oeuvre/MS of off/SZGDRJ offal/M offbeat/MS offend/ZGDRS offender/M offense/MS offensive's offensive/IPY offensiveness/IM offensives offer/JGMD offering/M offertory/SM offhand offhanded/PY offhandedness/M office/MZRS officeholder/SM officer/M official/MYS officialdom/M officialese officialism/M officiant/SM officiate/DSG officiator/MS officious/PY officiousness/M offing/M offish offline offload/SDG offprint/SM offset/MS offsetting offshoot/MS offshore/G offside offsite offspring/M offstage/S offtrack oft often/TR oftentimes ofttimes ogle/MZGDRS ogler/M ogre/MS ogreish ogress/MS oh/M ohm/SM ohmmeter/MS oho ohs oi oik/S oil/SGMD oilcan/S oilcloth/M oilcloths oilfield/S oiliness/M oilman oilmen oilskin/MS oilskins/M oily/RPT oink/MDSG ointment/SM okapi/SM okay/MSG okra/MS old/TMNRP oldie/SM oldish oldness/M oldster/MS ole/SMV oleaginous oleander/MS oleo/M oleomargarine/M olfactory/SM oligarch/M oligarchic oligarchical oligarchs oligarchy/SM oligonucleotide/S oligopoly/SM olive/SM om/SMNX ombudsman/M ombudsmen omega/SM omelet/MS omen/M omicron/MS ominous/YP ominousness/M omission/MS omit/S omitted omitting omnibus/MS omnipotence/M omnipotent omnipresence/M omnipresent omniscience/M omniscient omnivore/MS omnivorous/PY omnivorousness/M on/Y once/M oncogene/SM oncologist/SM oncology/M oncoming one/SXMNP oneness/M onerous/PY onerousness/M oneself onetime ongoing onion/M onionskin/M online onlooker/SM onlooking onomatopoeia/M onomatopoeic onomatopoetic onrush/MSG onscreen onset/MS onshore onside onsite onslaught/MS onstage onto ontogeny/M ontological ontology/M onus/MS onward onyx/MS oodles/M ooh/GD oohs oomph oops ooze/MGDS oozy/TR op/SMDG opacity/M opal/MS opalescence/M opalescent opaque/PYTGDRS opaqueness/M opcode/S ope/S open/ZTGJPMDRYS opencast opened/U opener/M openhanded/P openhandedness/M openhearted opening/M openness/M openwork/M opera/MS operable/I operand/S operate/DSGNVX operatic operatically operation/M operational/Y operative/SM operator/SM operetta/SM ophthalmic ophthalmologist/SM ophthalmology/M opiate/SM opine/GNXDS opinion/M opinionated opium/M opossum/MS opp opponent/SM opportune/IY opportunism/M opportunist/SM opportunistic opportunistically opportunity/SM oppose/DSG opposed/U opposite/SMYNX opposition/M oppress/DSGV oppression/M oppressive/YP oppressiveness/M oppressor/MS opprobrious/Y opprobrium/M opt/SGD optic/MS optical/Y optician/SM optics/M optima optimal/Y optimism/SM optimist/SM optimistic optimistically optimization/MS optimize/DRSG optimum/SM option/SMDG optional/Y optometrist/MS optometry/M opulence/M opulent/Y opus/MS or oracle/SM oracular oral/MYS orange/SMP orangeade/MS orangery/SM orangutan/SM orate/GNXDS oration/M orator/SM oratorical/Y oratorio/MS oratory/SM orb/SM orbicular orbit/MDRZGS orbital/SM orbiter/M orc/S orchard/SM orchestra/MS orchestral orchestrate/DSXGN orchestration/M orchid/SM ordain/SDLG ordainment/M ordeal/SM order/EAMDGS orderings orderliness/EM orderly/PSM ordinal/SM ordinance/SM ordinarily ordinariness/M ordinary/SMP ordinate/MNSX ordination/M ordnance/M ordure/M ore/SM oregano/M org organ/MS organdy/M organelle/MS organic/SM organically/I organism/MS organismic organist/MS organization/ASM organizational/Y organize/AESDG organized/U organizer/MS organza/M orgasm/SM orgasmic orgiastic orgy/SM oriel/MS orient's orient/AEDGS oriental/MS orientalist/S orientate/EDSGN orientation/AEM orientations orienteering orifice/MS orig origami/M origin/SM original/MYS originality/M originate/DSGN origination/M originator/SM oriole/SM orison/SM ormolu/M ornament/SGMD ornamental ornamentation/M ornate/YP ornateness/M orneriness/M ornery/PRT ornithological ornithologist/MS ornithology/M orotund orotundity/SM orphan/SMDG orphanage/MS orris/MS orthodontia/M orthodontic/S orthodontics/M orthodontist/SM orthodox/U orthodoxy/SM orthogonal orthogonality orthographic orthographically orthography/SM orthopedic/S orthopedics/M orthopedist/MS orzo/M oscillate/GNDSX oscillation/M oscillator/SM oscillatory oscilloscope/MS osculate/DSXGN osculation/M osier/MS osmium/M osmosis/M osmotic osprey/SM ossification/M ossify/NGDS ostensible ostensibly ostentation/M ostentatious/Y osteoarthritis/M osteopath/M osteopathic osteopaths osteopathy/M osteoporosis/M ostler/S ostracism/M ostracize/GDS ostrich/MS other/SP otherwise otherworldly otiose otter/MS ottoman/MS oubliette/MS ouch ought oughtn't ounce/MS our/S ourselves oust/ZGDRS ouster/M out/SJGMDR outage/SM outargue/GDS outback/MS outbalance/DSG outbid/S outbidding outboard/MS outboast/DSG outbound outbox/MS outbreak/MS outbuilding/MS outburst/SM outcast/MS outclass/DSG outcome/MS outcrop/MS outcropped outcropping/SM outcry/SM outdated outdid outdistance/GDS outdo/G outdoes outdone outdoor/S outdoors/M outdoorsy outdraw/GS outdrawn outdrew outermost outerwear/M outface/GDS outfall/S outfield/SMRZ outfielder/M outfight/SG outfit/SM outfitted outfitter/MS outfitting outflank/GSD outflow/MS outfought outfox/GDS outgo/MJG outgoes outgrew outgrow/HGS outgrown outgrowth/M outgrowths outguess/GDS outgun/S outgunned outgunning outhit/S outhitting outhouse/SM outing/M outlaid outlandish/PY outlandishness/M outlast/DSG outlaw/SGMD outlay/SGM outlet/SM outline/MGDS outlive/GDS outlook/MS outlying outmaneuver/GDS outmatch/GDS outmoded outnumber/DSG outpace/GDS outpatient/MS outperform/GSD outplace/L outplacement/M outplay/GDS outpoint/DGS outpost/MS outpouring/MS outproduce/DSG output/SM outputted outputting outrace/GDS outrage/MGDS outrageous/Y outran outrank/GDS outre outreach/MDSG outrider/MS outrigger/SM outright outrun/S outrunning outscore/GDS outsell/GS outset/SM outshine/GS outshone outshout/GDS outside/MZRS outsider/M outsize/MS outskirt/MS outsmart/GDS outsold outsource/DSG outsourcing/M outspend/SG outspent outspoken/YP outspokenness/M outspread/GS outstanding/Y outstation/MS outstay/DGS outstretch/DSG outstrip/S outstripped outstripping outta outtake/MS outvote/GDS outward/YS outwear/GS outweigh/GD outweighs outwit/S outwith outwitted outwitting outwore outwork/MDRSZG outworn ouzo/MS ova oval/MS ovarian ovary/SM ovate/NX ovation/M oven/MS ovenbird/SM ovenproof ovenware over/MYS overabundance/M overabundant overachieve/ZGDRS overachiever/M overact/GVSD overage/SM overaggressive overall/SM overalls/M overambitious overanxious overarching overarm/GSD overate overattentive overawe/DSG overbalance/MGDS overbear/GS overbearing/Y overbid/SM overbidding overbite/MS overblown overboard overbold overbook/DGS overbore overborne overbought overbuild/SG overbuilt overburden/GSD overbuy/GS overcame overcapacity/M overcapitalize/DSG overcareful overcast/MGS overcautious overcharge/DSMG overclock/GD overcloud/SGD overcoat/MS overcome/GS overcompensate/DSGN overcompensation/M overconfidence/M overconfident overconscientious overcook/DGS overcritical overcrowd/SDG overcrowding/M overdecorate/DSG overdependent overdevelop/SDG overdid overdo/G overdoes overdone overdose/MGDS overdraft/SM overdraw/GS overdrawn overdress/GMDS overdrew overdrive/SM overdub/SM overdubbed overdubbing overdue overeager overeat/GSN overemotional overemphasis/M overemphasize/GDS overenthusiastic overestimate/MGNDS overestimation/M overexcite/DSG overexercise/GDS overexert/SDG overexertion/M overexpose/GDS overexposure/M overextend/DGS overfed overfeed/GS overfill/DGS overflew overflight/MS overflow/MDSG overflown overfly/GS overfond overfull overgeneralize/DSG overgenerous overgraze/DSG overgrew overground overgrow/HSG overgrown overgrowth/M overhand/MDS overhang/MSG overhasty overhaul/MDSG overhead/MS overhear/SG overheard overheat/DSG overhung overindulge/GDS overindulgence/M overindulgent overjoy/GSD overkill/M overladen overlaid overlain overland overlap/SM overlapped overlapping overlarge overlay/GSM overleaf overlie overload/GMDS overlong overlook/GMDS overlord/MS overly/SG overmanned overmanning overmaster/SDG overmodest overmuch/S overnice overnight/MS overoptimism/M overoptimistic overpaid overparticular overpass/MS overpay/GS overplay/GDS overpopulate/GNDS overpopulation/M overpower/SDG overpowering/Y overpraise/DSG overprecise overprice/DSG overprint/SMDG overproduce/GDS overproduction/M overprotect/SDGV overqualified overran overrate/GDS overreach/GDS overreact/SDG overreaction/SM overrefined overridden override/MGS overripe/M overrode overrule/GDS overrun/SM overrunning oversampling oversaw oversea/S oversee/RSZ overseeing overseen overseer/M oversell/GS oversensitive/P oversensitiveness/M oversexed overshadow/DSG overshare/DSG overshoe/MS overshoot/GS overshot oversight/SM oversimple oversimplification/M oversimplify/DSNGX oversize oversleep/GS overslept oversold overspecialization/M overspecialize/GDS overspend/SG overspent overspread/GS overstaffed overstate/DSLG overstatement/MS overstay/DSG overstep/S overstepped overstepping overstimulate/DSG overstock/GSD overstretch/GDS overstrict overstrung overstuffed oversubscribe/DSG oversubtle oversupply/GDS oversuspicious overt/Y overtake/GS overtaken overtax/GDS overthink/SG overthought overthrew overthrow/SMG overthrown overtime/MS overtire/GDS overtone/MS overtook overture/MS overturn/DSG overuse/DSMG overvaluation/S overvalue/DSG overview/MS overweening/Y overweight/M overwhelm/SGD overwhelming/Y overwinter/SDG overwork/GMDS overwrite/GS overwritten overwrote overwrought overzealous oviduct/SM oviparous ovoid/MS ovular ovulate/DSGN ovulation/M ovule/MS ovum/M ow owe/DSG owl/SM owlet/MS owlish/Y own/ESGD owner/MS ownership/M ox/MN oxblood/M oxbow/MS oxcart/SM oxford/SM oxidant/MS oxidase oxidation/M oxide/MS oxidization/M oxidize/ZGDRS oxidizer/M oxtail/S oxyacetylene/M oxygen/M oxygenate/DSGN oxygenation/M oxymora oxymoron/M oyster/SM oz ozone/M p/NRXTGJ pH pa/SMH pablum/M pabulum/M pace/MZGDRS pacemaker/SM pacer/M pacesetter/SM pacey pachyderm/MS pachysandra/MS pacific pacifically pacification/M pacifier/M pacifism/M pacifist/SM pacifistic pacify/ZGDRSN pack's pack/AUGSD package's package/AGDS packager/SM packaging/M packer/MS packet/MS packing's packinghouse/SM packsaddle/MS pact/MS pacy/RT pad/SM padded padding/M paddle/MZGDRS paddler/M paddock/MDGS paddy/SM padlock/MDSG padre/SM paean/SM paella/MS pagan/SM paganism/M page/MZGDRS pageant/MS pageantry/M pageboy/SM pager/M paginate/DSGN pagination/M pagoda/MS pah paid/AU pail/MS pailful/SM pain/MDSG painful/PY painfuller painfullest painfulness/M painkiller/MS painkilling painless/PY painlessness/M painstaking/MY paint/SZGJMDR paintball paintbox/MS paintbrush/MS painted/U painter/MY painting/M paintwork pair/AMDSG paired/U pairing/S pairwise paisley/SM pajama/S pajamas/M pal/SMY palace/MS paladin/SM palanquin/SM palatable/U palatal/SM palatalization/M palatalize/GDS palate/MBS palatial/Y palatinate/MS palatine/MS palaver/GSMD palazzi palazzo pale/MYTGPDRSJ paleface/MS paleness/M paleographer/MS paleography/M paleolithic paleontologist/SM paleontology/M palette/SM palfrey/SM palimony/M palimpsest/MS palindrome/MS palindromic paling/M palisade/SM palish pall/MDSG palladium/M pallbearer/MS pallet/MS palliate/DSGNV palliation/M palliative/SM pallid/YP pallidness/M pallor/M palm/MDSG palmate palmetto/SM palmist/SM palmistry/M palmtop/SM palmy/TR palomino/MS palpable palpably palpate/DSGN palpation/M palpitate/XGNDS palpitation/M palsy/GDSM paltriness/M paltry/RPT pampas/M pamper/DSG pamphlet/MS pamphleteer/MS pan/SM panacea/SM panache/M panama/MS panatella/S pancake/DSMG panchromatic pancreas/MS pancreatic pancreatitis panda/SM pandemic/SM pandemonium/M pander/MDRZGS panderer/M pane/KM panegyric/SM panel/SGJMD paneling/M panelist/MS panes pang/MS panhandle/DRSMZG panhandler/M panic/SM panicked panicking panicky panned pannier/SM panning panoply/SM panorama/SM panoramic panpipes/M pansy/SM pant/MDSG pantaloons/M pantechnicon/S pantheism/M pantheist/SM pantheistic pantheon/SM panther/MS pantie/MS panto/S pantomime/MGDS pantomimic pantomimist/SM pantry/SM pantsuit/SM pantyhose/M pantyliner/M pantywaist/SM pap/SM papa/MS papacy/SM papal paparazzi/M paparazzo papaya/MS paper/SZGMDR paperback/SM paperbark/S paperboard/M paperboy/SM paperclip/S paperer/M papergirl/SM paperhanger/SM paperhanging/M paperless paperweight/MS paperwork/M papery papilla/M papillae papillary papist/MS papoose/MS pappy/SM paprika/M papyri papyrus/M par/SZGMDRBJ para/MS parable/MS parabola/SM parabolic paracetamol/S parachute/DSMG parachutist/MS parade/MZGDRS parader/M paradigm/SM paradigmatic paradisaical paradise/SM paradox/MS paradoxical/Y paraffin/M paragliding paragon/MS paragraph/GMD paragraphs parakeet/SM paralegal/MS parallax/MS parallel/SGMD paralleled/U parallelism/MS parallelogram/SM paralyses paralysis/M paralytic/SM paralyze/DSG paralyzing/Y paramecia paramecium/M paramedic/MS paramedical/MS parameter/MS parametric paramilitary/SM paramount paramountcy paramour/SM paranoia/M paranoiac/MS paranoid/SM paranormal parapet/MS paraphernalia/M paraphrase/DSMG paraplegia/M paraplegic/SM paraprofessional/MS parapsychologist/MS parapsychology/M paraquat/M parasailing parascending parasite/SM parasitic parasitical/Y parasitism/M parasol/MS parasympathetic/S parathion/M parathyroid/MS paratroop/RZS paratrooper/M paratroops/M paratyphoid/M parboil/DSG parcel/GMDS parch/LGDS parchment/SM pardner/S pardon/ZGMDRBS pardonable/U pardonably/U pardoner/M pare/S paregoric/M parent/GMDS parentage/M parental parentheses parenthesis/M parenthesize/DSG parenthetic parenthetical/Y parenthood/M parenting/M parer/M pares/S paresis/M parfait/MS pariah/M pariahs parietal parimutuel/MS paring/M parish/MS parishioner/MS parity/ESM park/MDSG parka/SM parking/M parkland parkway/MS parky parlance/M parlay/GMDS parley/GMDS parliament/SM parliamentarian/SM parliamentary parlor/MS parlous parmigiana parochial/Y parochialism/M parodist/SM parody/GDSM parole/MGDS parolee/MS paroxysm/SM paroxysmal parquet/MDSG parquetry/M parred parricidal parricide/MS parring parrot/GMDS parry/GDSM parse/DRSG parsec/MS parsimonious/Y parsimony/M parsley/M parsnip/MS parson/MS parsonage/MS part's part/CDSG partake/ZGRS partaken partaker/M parterre/SM parthenogenesis/M partial/MYS partiality/M participant/SM participate/DSGN participation/M participator/MS participatory participial/M participle/MS particle/SM particleboard/M particular/SMY particularity/SM particularization/M particularize/DSG particulate/SM parting/MS partisan/SM partisanship/M partition/GMDS partitive/MS partly partner/MDSG partnership/MS partook partridge/SM parturition/M partway party/GDSM parvenu/MS pascal/MS paschal pasha/SM pass/M passably passage/MS passageway/MS passbook/MS passe/DRSBXZGNV passel/MS passenger/SM passer/M passerby/M passersby passim passing/MY passion/EM passionate/EY passionflower/SM passionless passive/PMYS passiveness/M passivity/M passivization passivize/DSG passkey/MS passphrase/S passport/MS password/MS past/AMS pasta/SM paste/DSMG pasteboard/M pastel/MS pastern/MS pasteurization/M pasteurize/ZGDRS pasteurized/U pasteurizer/M pastiche/MS pastie pastille/MS pastime/MS pastiness/M pastor/MS pastoral/MS pastorate/MS pastrami/M pastry/SM pasturage/M pasture/DSMG pastureland/M pasty/PTRSM pat/SM patch/EGMDS patchily patchiness/M patchouli patchwork/SM patchy/TPR pate/MS patella/MS patellae patent/GMDYS paterfamilias/MS paternal/Y paternalism/M paternalist/S paternalistic paternity/M paternoster/MS path/M pathetic pathetically pathfinder/SM pathless pathogen/SM pathogenic pathological/Y pathologist/SM pathology/M pathos/M paths pathway/MS patience/M patient/IMST patienter patiently patina/MS patine patio/SM patisserie/S patois/M patresfamilias patriarch/M patriarchal patriarchate/MS patriarchs patriarchy/SM patrician/SM patricide/SM patrimonial patrimony/SM patriot/SM patriotic/U patriotically patriotism/M patrol/MS patrolled patrolling patrolman/M patrolmen patrolwoman/M patrolwomen patron/MS patronage/MS patroness/MS patronize/ZGDRS patronizer/M patronizing/Y patronymic/SM patronymically patroon/SM patsy/SM patted patter/MDGS pattern/SMDG patting patty/SM paucity/M paunch/MS paunchy/RT pauper/MS pauperism/M pauperize/DSG pause/DSMG pave/AGDS paved/U pavement/MS pavilion/SM paving/MS pavlova/S paw/SGMD pawl/MS pawn/MDSG pawnbroker/MS pawnbroking/M pawnshop/MS pawpaw/MS pay's pay/ASGBL payback/SM paycheck/MS payday/MS payed payee/SM payer/SM payload/SM paymaster/SM payment/ASM payoff/MS payola/M payout/MS payphone/S payroll/SM payslip/SM paywall/SM payware pct pd pea/SM peace/SM peaceable peaceably peaceful/PY peacefulness/M peacekeeper/SM peacekeeping/M peacemaker/MS peacemaking/M peacetime/M peach/MS peachy/TR peacock/MS peafowl/MS peahen/MS peak/MDSG peaky peal/AMDSG peanut/MS pear/MYS pearl/SGMD pearly/RT peasant/SM peasantry/M peashooter/SM peat/M peaty/TR pebble/MGDS pebbly pecan/SM peccadillo/M peccadilloes peccary/SM peck/MDRSZG peckish pecs pectic pectin/M pectoral/MS peculate/GNDS peculation/M peculator/SM peculiar/Y peculiarity/SM pecuniary pedagogic pedagogical/Y pedagogue/SM pedagogy/M pedal/SGMD pedalo/S pedant/MS pedantic pedantically pedantry/M peddle/ZGDRS peddler/M pederast/MS pederasty/M pedestal/MS pedestrian/SM pedestrianization pedestrianize/GDS pediatric/S pediatrician/MS pediatrics/M pedicab/SM pedicure/MGDS pedicurist/MS pedigree/MDS pediment/MS pedometer/MS pedophile/S pedophilia peduncle/MS pee/DRSMZ peeing peek/MDSG peekaboo/M peel/MDRSJZG peeled/U peeler/M peeling/M peen/MS peep/MDRSZG peepbo peeper/M peephole/MS peepshow/MS peer/MDG peerage/SM peeress/MS peerless peeve/DSMG peevish/PY peevishness/M peewee/MS peewit/S peg/SM pegboard/MS pegged pegging peignoir/SM pejoration/M pejorative/SMY peke/MS pekineses pekingese/SM pekoe/M pelagic pelf/M pelican/MS pellagra/M pellet/GMDS pellucid pelmet/S pelt/MDSG pelvic pelvis/MS pemmican/M pen/M penal penalization/M penalize/DSG penalty/SM penance/MS pence penchant/SM pencil/GMDJS pend/CDSG pendant/MS pendent/MS pendulous pendulum/MS penetrability/M penetrable penetrate/DSGNVX penetrating/Y penetration/M penfriend/S penguin/MS penicillin/M penile peninsula/SM peninsular penis/MS penitence/M penitent/SMY penitential penitentiary/SM penknife/M penknives penlight/SM penman/M penmanship/M penmen pennant/MS penned penniless penning pennon/MS penny/SM pennyweight/MS pennyworth penologist/MS penology/M pension/BZGMDRS pensioner/M pensive/PY pensiveness/M pent pentacle/MS pentagon/MS pentagonal pentagram/SM pentameter/SM pentathlete/MS pentathlon/MS penthouse/SM penuche/M penultimate/SM penumbra/MS penumbrae penurious/PY penuriousness/M penury/M peon/MS peonage/M peony/SM people/MGDS pep/SM pepped pepper/GMDS peppercorn/SM peppermint/SM pepperoni/MS peppery peppiness/M pepping peppy/TPR pepsin/M peptic/MS peptide/S peradventure/M perambulate/XGNDS perambulation/M perambulator/MS percale/MS perceive/BGDS perceived/U percent/MS percentage/SM percentile/SM perceptible perceptibly perception/SM perceptional perceptive/PY perceptiveness/M perceptual/Y perch/GMDS perchance percipience/M percipient percolate/GNDS percolation/M percolator/SM percussion/AM percussionist/MS percussive perdition/M perdurable peregrinate/DSXGN peregrination/M peregrine/MS peremptorily peremptory perennial/SMY perestroika/M perfect/PTGMDRYS perfecta/MS perfectibility/M perfectible perfection/SM perfectionism/M perfectionist/SM perfectness/M perfidious/Y perfidy/SM perforate/GNXDS perforation/M perforce perform/SDRZG performance/SM performative performed/U performer/M perfume/DRSMZG perfumer/M perfumery/SM perfunctorily perfunctory perfusion pergola/SM perhaps pericardia pericardial pericarditis pericardium/M perigee/SM perihelia perihelion/M peril/SGMD perilous/Y perimeter/SM perinatal perinea perineum/M period/MS periodic periodical/SMY periodicity/M periodontal periodontics/M periodontist/SM peripatetic/MS peripheral/MYS periphery/SM periphrases periphrasis/M periphrastic periscope/SM perish/BDRSZG perishable/MS peristalses peristalsis/M peristaltic peristyle/SM peritoneal peritoneum/MS peritonitis/M periwig/SM periwinkle/SM perjure/DRSZG perjurer/M perjury/SM perk/MDSG perkily perkiness/M perky/TPR perm/MDSG permafrost/M permanence/M permanency/M permanent/SMY permeability/M permeable permeate/GNDS permeation/M permissible permissibly permission/MS permissive/PY permissiveness/M permit/MS permitted permitting permutation/SM permute/DSG pernicious/YP perniciousness/M peroration/MS peroxide/MGDS perpendicular/SMY perpendicularity/M perpetrate/DSGN perpetration/M perpetrator/MS perpetual/SMY perpetuate/DSGN perpetuation/M perpetuity/M perplex/GDS perplexed/Y perplexity/SM perquisite/SM persecute/GNXDS persecution/M persecutor/SM perseverance/M persevere/DSG persiflage/M persimmon/SM persist/SGD persistence/M persistent/Y persnickety person/UMS persona/SM personable personae personage/MS personal/MYS personality/SM personalize/CDSG personalty/M personification/M personify/GDSNX personnel/M perspective/MS perspex perspicacious/Y perspicacity/M perspicuity/M perspicuous perspiration/M perspire/GDS persuade/BZGDRS persuaded/U persuader/M persuasion/SM persuasive/PY persuasiveness/M pert/RYPT pertain/GSD pertinacious/Y pertinacity/M pertinence/M pertinent/Y pertness/M perturb/DGS perturbation/SM perturbed/U pertussis/M peruke/MS perusal/MS peruse/GDS perv/S pervade/DSG pervasive/PY pervasiveness/M perverse/PXYN perverseness/M perversion/M perversity/M pervert/SGMD peseta/MS peskily peskiness/M pesky/TPR peso/MS pessary/S pessimal pessimism/M pessimist/SM pessimistic pessimistically pest/MRSZ pester/GD pesticide/MS pestiferous pestilence/SM pestilent pestilential pestle/MGDS pesto/M pet/SZMR petabyte/MS petal/SMD petard/MS petcock/SM peter/GMD petiole/SM petite/MS petition/ZGMDRS petitioner/M petrel/MS petrifaction/M petrify/DSG petrochemical/SM petrodollar/MS petrol/M petrolatum/M petroleum/M petrologist/SM petrology/M petted petticoat/MS pettifog/S pettifogged pettifogger/SM pettifoggery/M pettifogging pettily pettiness/M petting/M pettish/Y petty/PTR petulance/M petulant/Y petunia/MS pew/SM pewee/SM pewit/SM pewter/MS peyote/M pf pfennig/MS pg phaeton/MS phage/S phagocyte/SM phalanger/SM phalanges phalanx/MS phalli phallic phallus/M phantasm/MS phantasmagoria/MS phantasmagorical phantasmal phantom/SM pharaoh/M pharaohs pharisaic pharisee/SM pharmaceutic/MS pharmaceutical/SM pharmaceutics/M pharmacist/MS pharmacological pharmacologist/SM pharmacology/M pharmacopoeia/MS pharmacy/SM pharyngeal pharynges pharyngitis/M pharynx/M phase/DSMG phaseout/SM phat pheasant/MS phenacetin/M phenobarbital/M phenol/M phenom/MS phenomena phenomenal/Y phenomenological phenomenology phenomenon/MS phenotype pheromone/MS phew phi/SM phial/SM philander/ZGDRS philanderer/M philandering/M philanthropic philanthropically philanthropist/MS philanthropy/SM philatelic philatelist/MS philately/M philharmonic/SM philippic/MS philistine/MS philistinism/M philodendron/SM philological philologist/MS philology/M philosopher/MS philosophic philosophical/Y philosophize/DRSZG philosophizer/M philosophy/SM philter/MS phish/ZGDR phisher/M phlebitis/M phlegm/M phlegmatic phlegmatically phloem/M phlox/M phobia/MS phobic/MS phoebe/MS phoenix/MS phone/DSMG phonecard/S phoneme/MS phonemic phonemically phonetic/S phonetically phonetician/SM phonetics/M phoneyed phoneying phonic/S phonically phonics/M phoniness/M phonograph/M phonographic phonographs phonological/Y phonologist/MS phonology/M phony/PTGDRSM phooey phosphate/MS phosphodiesterase phosphor/MS phosphorescence/M phosphorescent/Y phosphoric phosphorous phosphorus/M photo/SGMD photobomb/DGS photocell/MS photocopier/M photocopy/DRSMZG photoelectric photoelectrically photoengrave/DRSJZG photoengraver/M photoengraving/M photofinishing/M photogenic photogenically photograph/MDRZG photographer/M photographic photographically photographs/A photography/M photojournalism/M photojournalist/SM photometer/MS photon/MS photosensitive photostat/SM photostatic photostatted photostatting photosynthesis/M photosynthesize/GDS photosynthetic phototropic phototropism phototypesetter phototypesetting phrasal phrase's phrase/AGDS phrasebook/S phraseology/M phrasing/MS phreaking phrenologist/SM phrenology/M phyla phylactery/SM phylogeny/M phylum/M phys physic/SM physical/MYS physicality physician/SM physicist/SM physicked physicking physics/M physio/S physiognomy/SM physiography/M physiologic physiological/Y physiologist/MS physiology/M physiotherapist/MS physiotherapy/M physique/MS pi/SMDRHZG pianissimo/SM pianist/MS piano/SM pianoforte/SM pianola/S piaster/MS piazza/MS pibroch/M pibrochs pic/SM pica/M picador/MS picante picaresque picayune piccalilli/M piccolo/MS pick/MDRSJZG pickax/GMDS picker/M pickerel/MS picket/ZGMDRS pickings/M pickle/MGDS pickpocket/SM pickup/MS picky/PTR picnic/MS picnicked picnicker/SM picnicking picot/SM pictograph/M pictographs pictorial/MYS picture/MGDS picturesque/PY picturesqueness/M piddle/MGDS piddly pidgin/MS pie/SM piebald/MS piece/DSMG piecemeal piecework/MRZ pieceworker/M piecrust/SM pieing pier/M pierce/JGDS piercing/MY piety/M piezoelectric piffle/MG pig/SML pigeon/MS pigeonhole/DSMG pigged piggery/S pigging piggish/PY piggishness/M piggy/TRSM piggyback/MDSG pigheaded/PY pigheadedness/M piglet/MS pigment/MDS pigmentation/M pigpen/MS pigskin/MS pigsty/SM pigswill pigtail/MS pike/MZGDRS piker/M pikestaff/SM pilaf/SM pilaster/MS pilchard/MS pile/MGDSJ pileup/MS pilfer/ZGDRS pilferage/M pilferer/M pilgrim/MS pilgrimage/MS piling/M pill/MDSG pillage/MZGDRS pillager/M pillar/MDS pillbox/MS pillion/MS pillock/S pillory/GDSM pillow/GMDS pillowcase/MS pillowslip/MS pilot/DGSM pilothouse/SM pimento/MS pimiento/MS pimp/GMDYS pimpernel/MS pimple/DSM pimply/RT pin/SM pinafore/MS pinata/MS pinball/M pincer/MS pinch/GMDS pincushion/MS pine's pine/AGDS pineapple/MS pinewood/S piney pinfeather/SM ping/GMD pinhead/SM pinhole/SM pinier piniest pinion/SMDG pink/TGPMDRS pinkeye/M pinkie/SM pinkish pinkness/M pinko/MS pinnacle/SM pinnate pinned/U pinning/U pinny/S pinochle/M pinon/MS pinpoint/SGMD pinprick/MS pinsetter/SM pinstripe/DSM pint/MS pinto/MS pinup/MS pinwheel/GSMD pinyin/M pinyon/SM pioneer/SGMD pious/YP piousness/M pip/SZGMDR pipe/MS pipeline/SM piper/M pipette/SM pipework piping/M pipit/MS pipped pippin/SM pipping pipsqueak/SM piquancy/M piquant/Y pique/MGDS piracy/M piranha/SM pirate/DSMG piratical/Y pirogi/M piroshki/M pirouette/DSMG piscatorial pismire/SM piss/ZGMDRS pissoir/S pistachio/SM piste/S pistil/SM pistillate pistol/SM piston/SM pit/SM pita/MS pitapat/SM pitch/MDRSZG pitchblende/M pitcher/M pitchfork/MDSG pitchman/M pitchmen piteous/YP piteousness/M pitfall/SM pith/M pithead/S pithily pithiness/M pithy/RTP pitiable pitiably pitiful/Y pitiless/PY pitilessness/M piton/MS pitta/S pittance/MS pitted pitting pituitary/SM pity/GDSM pitying/Y pivot/MDGS pivotal pix/M pixel/MS pixie/MS pizza/MS pizzazz/M pizzeria/SM pizzicati pizzicato/M pj's pk pkg pkt pkwy pl placard/SMDG placate/DSGN placation/M placatory place's place/AESDLG placebo/SM placed/U placeholder/MS placekick/MDRZGS placekicker/M placement/EASM placenta/SM placental/S placer/SM placid/Y placidity/M placings placket/SM plagiarism/SM plagiarist/SM plagiarize/DRSZG plagiarizer/M plagiary/M plague/DSMG plaice plaid/MS plain/MRYTSP plainchant plainclothes plainclothesman/M plainclothesmen plainness/M plainsman/M plainsmen plainsong/M plainspoken plaint/SMV plaintiff/SM plaintive/Y plait/MDGS plan/ZMRS planar plane's plane/CGDS planeload/MS planer/M planet/SM planetarium/SM planetary plangency/M plangent plank/MDGS planking/M plankton/M planned/U planner/SM planning/S plant/MDRZGSJ plantain/SM plantar plantation/MS planter/M planting/M plantlike plaque/SM plash/MDSG plasma/M plaster/SZGMDR plasterboard/M plasterer/M plastic/SM plasticity/M plasticize/DSG plastique plat/XGMDNS plate/MS plateau/SMDG plateful/SM platelet/SM platen/M platform/SGMD plating/M platinum/M platitude/SM platitudinous platonic platoon/SGMD platted platter/SM platting platy/M platypus/MS platys plaudit/SM plausibility/M plausible plausibly play/AEGMDS playable/EU playact/SGD playacting/M playback/MS playbill/MS playbook/MS playboy/SM player/SM playfellow/SM playful/PY playfulness/M playgirl/MS playgoer/MS playground/SM playgroup/S playhouse/MS playlist/MS playmate/MS playoff/SM playpen/SM playroom/SM playschool/S plaything/SM playtime/M playwright/SM plaza/MS plea/MS plead/DRZGSJ pleader/M pleading/MY pleasant/UTYP pleasanter pleasantness/UM pleasantry/SM please/EDSG pleasing/YS pleasurably pleasure/MGDSB pleasureful pleat/MDGS pleb/S plebby plebe/MS plebeian/MS plebiscite/MS plectra plectrum/MS pledge/DSMG plenary/SM plenipotentiary/SM plenitude/SM plenteous plentiful/Y plenty/M plenum/S pleonasm/MS plethora/M pleura/M pleurae pleurisy/M plexus/MS pliability/M pliable pliancy/M pliant/Y pliers/M plight/SMDG plimsoll/S plinth/M plinths plod/S plodded plodder/MS plodding/S plonk/DRSZG plop/MS plopped plopping plosive/S plot/MS plotted plotter/SM plotting plover/SM plow/GMDS plowman/M plowmen plowshare/MS ploy's ploy/S pluck/MDSG pluckily pluckiness/M plucky/RPT plug's plug/US plugged/U plugging/U plughole/S plugin/SM plum/GMDS plumage/M plumb/MDRSZGJ plumbed/U plumber/M plumbing/M plume/MS plummer plummest plummet/SGMD plummy plump/MDRYSTGP plumpness/M plumy/RT plunder/SZGMDR plunderer/M plunge/DRSMZG plunger/M plunk/MDSG pluperfect/SM plural/SM pluralism/M pluralist/MS pluralistic plurality/SM pluralization/M pluralize/GDS plus/MS plush/MRYTP plushness/M plushy/RT plutocracy/SM plutocrat/SM plutocratic plutonium/M pluvial ply/AGDSM plywood/M pm pneumatic pneumatically pneumococcal pneumococci pneumococcus pneumonia/M poach/DRSZG poacher/M poaching/M pock/GMDS pocket/SMDG pocketbook/SM pocketful/SM pocketknife/M pocketknives pockmark/MDGS pod/SM podcast/SM podded podding podiatrist/SM podiatry/M podium/SM poem/MS poesy/M poet/MS poetaster/MS poetess/MS poetic/S poetical/Y poetry/M pogrom/SM poi/M poignancy/M poignant/Y poinciana/SM poinsettia/SM point/MDRSZG pointblank pointed/Y pointer/M pointillism/M pointillist/SM pointless/PY pointlessness/M pointy/TR poise/MGDS poison/SJZGMDR poisoner/M poisoning/M poisonous/Y poke/MZGDRS poker/M pokey/MS poky/TR pol/SGMD polar polarity/SM polarization/CM polarize/CDSG pole/MS poleaxe/GDS polecat/MS polemic/MS polemical/Y polemicist/SM polemics/M polestar/SM police/DSMG policeman/M policemen policewoman/M policewomen policy/SM policyholder/MS policymaker/S polio/MS poliomyelitis/M polish/ZGMDRS polished/U polisher/M politburo/MS polite/RYTP politeness/M politesse/M politic/S political/Y politician/SM politicization/M politicize/CDSG politicking/M politico/SM politics/M polity/SM polka/MDSG poll/GMDNS pollack/MS pollard/S pollen/M pollinate/GNDS pollination/M pollinator/SM polling/M polliwog/SM pollster/SM pollutant/MS pollute/ZGNDRS polluted/U polluter/M pollution/M polo/M polonaise/SM polonium/M poltergeist/MS poltroon/SM poly polyacrylamide polyamory/S polyandrous polyandry/M polyclinic/SM polyester/MS polyethylene/M polygamist/MS polygamous polygamy/M polyglot/SM polygon/SM polygonal polygraph/GMD polygraphs polyhedral polyhedron/SM polymath/M polymaths polymer/SM polymeric polymerization/M polymerize/GDS polymorphic polymorphous polynomial/MS polyp/MS polyphonic polyphony/M polypropylene/M polys polysemous polystyrene/M polysyllabic polysyllable/MS polytechnic/MS polytheism/M polytheist/SM polytheistic polythene polyunsaturate/DS polyurethane/MS polyvinyl pom/S pomade/DSMG pomander/SM pomegranate/MS pommel/SGMD pommy/S pomp/M pompadour/SMD pompano/MS pompom/SM pomposity/M pompous/YP pompousness/M ponce/GDS poncho/SM poncy pond/MS ponder/SZGDR ponderer/M ponderous/YP ponderousness/M pone/MS pong/GDS pongee/M poniard/MS pontiff/SM pontifical/Y pontificate/DSMG pontoon/SM pony/GDSM ponytail/MS poo/SGD pooch/MDSG poodle/SM poof/MS poofter/S pooh/GMD poohs pool/GMDS poolroom/MS poolside/S poop/GMDS poor/TRYP poorboy/M poorhouse/SM poorness/M pop/SM popcorn/M pope/MS popgun/SM popinjay/MS poplar/SM poplin/M popover/SM poppa/MS poppadom/S popped popper/SM poppet/S popping poppy/SM poppycock/M populace/MS popular/Y popularity/UM popularization/M popularize/DSG populate/ACGDS population/CM populations populism/M populist/MS populous/P populousness/M popup/MS porcelain/SM porch/MS porcine porcupine/SM pore/MGDS porgy/SM pork/ZMR porker/M porky/RSMT porn/M porno/M pornographer/MS pornographic pornographically pornography/M porosity/M porous/P porousness/M porphyritic porphyry/M porpoise/MGDS porridge/M porringer/SM port's/A port/CAEGDS portability/M portable/MS portage/DSMG portal/SM portcullis/MS portend/SGD portent/SM portentous/YP porter/ASM porterhouse/SM portfolio/MS porthole/MS portico/M porticoes portiere/MS portion/KSGMD portliness/M portly/RPT portmanteau/MS portrait/MS portraitist/SM portraiture/M portray/SGD portrayal/MS portulaca/M pose's/A pose/CAKEGDS poser/EKSM poseur/SM posh/TR posit/DSGV position/CKEMS positional/K positioned/K positioning/K positive/MYPS positiveness/M positivism positivist/S positron/MS poss posse/MS possess/AEVGSD possession/ASM possessive/SMYP possessiveness/M possessor/SM possibility/SM possible/SM possibly possum/SM post/ZGMDRSJ postage/M postal postbag/S postbox/S postcard/SM postcode/S postcolonial postconsonantal postdate/DSG postdoc/MS postdoctoral poster/M posterior/SM posterity/M postgraduate/SM posthaste posthumous/Y posthypnotic postie/S postilion/SM postindustrial posting/M postlude/SM postman/M postmark/SMDG postmaster/MS postmen postmenopausal postmeridian postmistress/MS postmodern postmodernism/M postmodernist/MS postmortem/SM postnasal postnatal postoperative postpaid postpartum postpone/DSGL postponement/SM postprandial postscript/SM postseason/SM postulate/XDSMGN postulation/M postural posture/MGJDS posturing/M postwar postwoman postwomen posy/SM pot/CSM potability/M potable/SM potash/M potassium/M potato/M potatoes potbelly/DSM potboiler/SM potency/M potent/Y potentate/MS potential/MYS potentiality/SM potentiate/GDS potful/SM pothead/SM pother/SMDG potherb/SM potholder/MS pothole/DRSMZG pothook/SM potion/SM potluck/MS potpie/SM potpourri/SM potsherd/SM potshot/MS pottage/M potted potter/GSMD pottery/SM potting potty/PRSMT pouch/MDSG pouf/S pouffe/S poulterer/MS poultice/DSMG poultry/M pounce/DSMG pound's pound/KDSG poundage/M pounding/SM pour/GDSJ pout/ZGMDRS pouter/M poverty/M pow powder/GSMD powdery power/MDSG powerboat/MS powerful/Y powerhouse/SM powerless/PY powerlessness/M powwow/SGMD pox/MS pp ppm ppr pr practicability/M practicably practical/SMY practicality/SM practice/DSMGB practiced/U practicum/SM practitioner/SM praetor/SM praetorian pragmatic/MS pragmatical/Y pragmatism/M pragmatist/MS prairie/SM praise/EDSMG praiseworthiness/M praiseworthy/P praline/SM pram/MS prance/DRSMZG prancer/M prancing/Y prang/DSG prank/MS prankster/SM praseodymium/M prat/S prate/MZGDRS prater/M pratfall/SM prattle/DRSMZG prattler/M prawn/MDSG pray/ZGDRS prayer/M prayerful/Y preach/DRSZGL preacher/M preachment/M preachy/RT preadolescence/SM preamble/MGDS prearrange/LGDS prearrangement/M preassigned precancel/SMDG precancerous precarious/PY precariousness/M precast precaution/MS precautionary precede/DSG precedence/M precedent/SM precept/SM preceptor/SM precinct/MS preciosity/M precious/YP preciousness/M precipice/SM precipitant/MS precipitate/XMYGNDS precipitation/M precipitous/Y precis/M precise/DRSYTGNP preciseness/M precision/M preclude/GDS preclusion/M precocious/YP precociousness/M precocity/M precognition/M precognitive precolonial preconceive/GDS preconception/SM precondition/MDGS precook/GSD precursor/SM precursory predate/DSG predator/MS predatory predawn predecease/GDS predecessor/SM predefined predesignate/GDS predestination/M predestine/DSG predetermination/M predetermine/ZGDRS predeterminer/M predicable predicament/MS predicate/MGNVDS predication/M predicative/Y predict/BGVSD predictability/UM predictable/U predictably/U prediction/SM predictor/MS predigest/GDS predilection/SM predispose/GDS predisposition/MS predominance/M predominant/Y predominate/YGDS preemie/SM preeminence/M preeminent/Y preempt/GVSD preemption/M preemptive/Y preen/DSG preexist/DGS preexistence/M pref prefab/SM prefabbed prefabbing prefabricate/DSGN prefabrication/M preface/DSMG prefatory prefect/SM prefecture/MS prefer/SBL preferably preference/MS preferential/Y preferment/M preferred preferring prefigure/GDS prefix/MDSG preform/GSD pregame/SM pregnancy/SM pregnant preheat/GSD prehensile prehistorian/S prehistoric prehistorical/Y prehistory/M prehuman prejudge/GDS prejudgment/SM prejudice/MGDS prejudiced/U prejudicial prekindergarten/SM prelacy/M prelate/SM prelim/SM preliminary/SM preliterate prelude/MS premarital premature/Y premed/SM premedical premeditate/DSGN premeditated/U premeditation/M premenstrual premier/SGMD premiere/MS premiership/MS premise/DSMG premium/SM premix/GDS premolar/SM premonition/MS premonitory prenatal/Y prenup/SM prenuptial preoccupation/SM preoccupy/DSG preoperative preordain/GDS prep/MS prepackage/DSG prepacked prepaid preparation/SM preparatory prepare/GDS prepared/UP preparedness/UM prepay/GSL prepayment/MS preponderance/SM preponderant/Y preponderate/GDS preposition/SM prepositional/Y prepossess/GDS prepossessing/U prepossession/SM preposterous/Y prepped prepping preppy/TRSM prepubescence/M prepubescent/SM prepuce/MS prequel/MS prerecord/GSD preregister/SGD preregistration/M prerequisite/MS prerogative/SM pres presage/MGDS presbyopia/M presbyter/SM presbytery/SM preschool/SZMR preschooler/M prescience/M prescient/Y prescribe/DSG prescript/SVM prescription/SM prescriptive/Y preseason/SM presence/SM present/LMDRYZGSB presentably presentation/ASM presenter/M presentiment/SM presentment/SM preservation/M preservationist/SM preservative/SM preserve/BDRSMZG preserver/M preset/S presetting preshrank preshrink/GS preshrunk preside/GDS presidency/SM president/MS presidential presidium/M presort/DGS press's press/ACGSD pressed/U presser/MS pressie/S pressing/SMY pressman/M pressmen pressure/DSMG pressurization/M pressurize/CGDS pressurizer/SM prestidigitation/M prestige/M prestigious presto/SM presumably presume/GDSB presumption/SM presumptive presumptuous/YP presumptuousness/M presuppose/DSG presupposition/MS pretax preteen/MS pretend/DRZGS pretender/M pretense/SXMN pretension/M pretentious/UY pretentiousness/M preterit/SM preterm preternatural/Y pretest/DGS pretext/MS pretrial/S prettify/GDS prettily prettiness/M pretty/TGDRSMP pretzel/MS prevail/DGS prevalence/M prevalent prevaricate/DSGNX prevarication/M prevaricator/SM prevent/DBSGV preventable/U preventative/MS prevention/M preventive/SM preview/MDRSZG previous/Y prevision/MS prewar prey/GMDS prezzie/S priapic price's price/AGDS priceless pricey pricier priciest prick/MDRYSZG pricker/M prickle/MGDS prickliness/M prickly/PRT pride/MGDS prideful/Y prier/M priest/SMY priestess/MS priesthood/SM priestliness/M priestly/RTP prig/MS priggish/P priggishness/M prim/ZGDRYP primacy/M primal primarily primary/SM primate/MS prime/MS primer/M primeval priming/M primitive/SPMY primitiveness/M primmer primmest primness/M primogenitor/SM primogeniture/M primordial/Y primp/DSG primrose/SM primula/S prince/SMY princedom/SM princeliness/M princely/PRT princess/MS principal/SMY principality/SM principle/DSM principled/U print/AMDSG printable/U printer/MS printing/SM printmaking printout/SM prion/S prior/MS prioress/MS prioritization prioritize/DSG priority/SM priory/SM prism/MS prismatic prison/SZMR prisoner/M prissily prissiness/M prissy/PTR pristine prithee privacy/M private/XMYTNRS privateer/SM privation/CSM privatization/SM privatize/DSG privet/SM privilege/DSMG privileged/U privily privy/RSMT prize/MGDS prized/A prizefight/ZGSMR prizefighter/M prizefighting/M prizewinner/MS prizewinning pro/SM probabilistic probability/SM probable/SM probably probate/MN probation/ZMR probational probationary probationer/M probe/MGDSBJ probity/M problem/MS problematic problematical/Y probosces proboscis/MS procaine/M procedural procedure/SM proceed/GJDS proceeding/M proceeds/M process's process/AGDS processed/U procession/GD processional/MS processor/SM proclamation/MS proclivity/SM procrastinate/DSGN procrastination/M procrastinator/MS procreate/V proctor/GMDS procurement/M prod/MS prodigal/MYS prodigality/M prodigious/Y prodigy/SM produce's produce/AZGDRS producer/AM producible/A production/ASM productive/UY productiveness/M productivity/M prof/MS profanation/MS profane/PYGDS profaneness/M profanity/SM professed/Y profession/SM professional/MYS professionalism/M professionalization professionalize/DSG professor/SM professorial/Y professorship/SM proffer/GMDS proficiency/M proficient/MYS profit/BGD profitability/M profitable/U profitably/U profiteer/MDGS profiteering/M profiterole/SM profitless profligacy/M profligate/SMY proforma profound/RYTP profoundness/M profundity/SM profuse/PY profuseness/M progenitor/SM progeny/M progesterone/M progestin/S prognathous prognoses prognosis/M prognostic/MS prognosticate/XGNDS prognostication/M prognosticator/MS program/CAS programmable/MS programmatic programmed/AC programmer/MS programming/SM progress/MDSGV progression/MS progressive/PMYS progressiveness/M prohibit/DGVS prohibition/SM prohibitionist/MS prohibitive/Y prohibitory project/GMDS projectile/SM projection/SM projectionist/SM projector/MS prokaryotic prole/S proletarian/MS proletariat/M proliferate/DSGN proliferation/M prolific prolifically prolix/Y prolixity/M prologue/SM prolongation/SM prom/M promenade/MGDS promethium/M prominence/M prominent/Y promiscuity/M promiscuous/Y promise/DSMG promising/Y promissory promo/M promontory/SM promote/DRZG promoter/M promotional prompt/JPSMDRYZTG prompted/U prompter/M prompting/M promptitude/M promptness/M promulgate/GNDS promulgation/M promulgator/MS prone/P proneness/M prong/MDS pronghorn/MS pronominal/M pronounce/DSLG pronounceable/U pronouncement/SM pronto pronunciation/MS proof/ADGSM proofread/SRZG proofreader/M prop/MS propaganda/M propagandist/MS propagandize/GDS propagate/DSGN propagation/M propagator/SM propel/S propellant/MS propelled propeller/SM propelling propensity/SM proper/MRYT property/DSM prophecy/SM prophesier/M prophesy/DRSMZG prophet/SM prophetess/MS prophetic prophetical/Y prophylactic/SM prophylaxes prophylaxis/M propinquity/M propitiate/DSGN propitiation/M propitiatory propitious/Y proponent/SM proportion/ESM proportional/YS proportionality proportionate/EY proposal/MS propped propping proprietary/SM proprieties/M proprietor/SM proprietorial/Y proprietorship/M proprietress/MS propriety/SM propulsion/M propulsive prorate/DSG prorogation/M prorogue/GD prosaic prosaically proscenium/SM prosciutto/M proscribe/DG proscription/MS prose/M prosecute/DSXGN prosecution/M prosecutor/MS proselyte/DSMG proselytism/M proselytize/DRSZG proselytizer/M prosody/SM prospect/MDGVS prospective/Y prospector/SM prospectus/MS prosper/GSD prosperity/M prosperous/Y prostate/MS prostheses prosthesis/M prosthetic prostitute/MGNDS prostitution/M prostrate/GNXDS prostration/M prosy/RT protactinium/M protagonist/SM protean protect/GVSD protected/U protection/SM protectionism/M protectionist/MS protective/PY protectiveness/M protector/MS protectorate/MS protege/SM protegee/S protein/SM protestant/S protestation/MS protocol/MS proton/SM protoplasm/M protoplasmic prototype/MGS prototypical protozoa protozoan/MS protozoic protract/GD protrude/GDS protrusile protrusion/MS protuberance/MS protuberant proud/RYT prov/NB provability/M provably prove/EAGDS proved/U proven/U provenance/SM provender/M provenience/M proverbial/Y provide/DRSZG provided/U providence/M provident/Y providential/Y provider/M province/MS provincial/SMY provincialism/M provisional/Y proviso/SM provocateur/S provocative/PY provocativeness/M provoke/DRSZG provoked/U provoker/M provoking/Y provolone/M provost/SM prow/MS prowess/M prowl/MDRSZG prowler/M proximal proximate proximity/M proxy/SM prude/MS prudence/M prudent/Y prudential/Y prudery/M prudish/YP prudishness/M prune/MZGDRS pruner/M prurience/M prurient/Y pry/ZTGDRSM psalm/MS psalmist/SM psaltery/SM psephologist/S psephology pseud/S pseudo/S pseudonym/SM pseudonymous pseudoscience/MS pseudy pshaw/MS psi/SM psittacosis/M psoriasis/M psst psych/MDSG psyche/M psychedelia psychedelic/SM psychedelically psychiatric psychiatrist/SM psychiatry/M psychic/MS psychical/Y psycho/SM psychoactive psychoanalyses psychoanalysis/M psychoanalyst/SM psychoanalytic psychoanalytical/Y psychoanalyze/DSG psychobabble/M psychodrama/MS psychogenic psychokinesis psychokinetic psychological/Y psychologist/MS psychology/SM psychometric psychoneuroses psychoneurosis/M psychopath/M psychopathic psychopathology psychopaths psychopathy/M psychopharmacology psychos/S psychosis/M psychosomatic psychotherapist/MS psychotherapy/SM psychotic/SM psychotically psychotropic/MS psychs pt/C ptarmigan/MS pterodactyl/MS ptomaine/SM pub/SM pubertal puberty/M pubes/M pubescence/M pubescent pubic pubis/M public/AM publican/AMS publication/ASM publicist/MS publicity/M publicize/GDS publicly publish/AGDS publishable published/U publisher/MS publishing/M puce/M puck/ZMRS pucker/MDG puckish/YP puckishness/M pud/S pudding/SM puddle/DSMG puddling/M pudenda pudendum/M pudginess/M pudgy/PRT pueblo/SM puerile puerility/M puerperal puff/ZGMDRS puffball/SM puffer/M puffin/SM puffiness/M puffy/PRT pug/SM pugilism/M pugilist/SM pugilistic pugnacious/YP pugnaciousness/M pugnacity/M puke/MGDS pukka pulchritude/M pulchritudinous pule/GDS pull/ZGMDRS pullback/MS puller/M pullet/SM pulley/SM pullout/MS pullover/SM pulmonary pulp/GMDS pulpiness/M pulpit/SM pulpwood/M pulpy/RPT pulsar/SM pulsate/XGNDS pulsation/M pulse/AMGDS pulverization/M pulverize/DSG puma/MS pumice/SM pummel/SGD pump/ZGMDRS pumper/M pumpernickel/M pumpkin/MS pun/SM punch/MDRSZG punchbag/S puncheon/MS puncher/M punchline/S punchy/TR punctilio/M punctilious/PY punctiliousness/M punctual/Y punctuality/M punctuate/GNDS punctuation/M puncture/DSMG pundit/SM punditry/M pungency/M pungent/Y puniness/M punish/BLGDS punished/U punishing/Y punishment/MS punitive/Y punk/TMRS punned punnet/S punning punster/SM punt/ZGMDRS punter/M puny/TRP pup/SM pupa/M pupae pupal pupate/DSG pupil/MS pupped puppet/MS puppeteer/SM puppetry/M pupping puppy/SM purblind purchase/DRSMZGB purchaser/M purdah/M pure/PYTR purebred/SM puree/MDS pureeing pureness/M purgative/SM purgatorial purgatory/SM purge/MZGDRS purger/M purification/M purifier/M purify/NDRSZG purine/MS purism/M purist/MS puristic puritan/SM puritanical/Y puritanism/M purity/M purl/GMDS purlieu/SM purloin/SGD purple/MTRS purplish purport/SMDG purported/Y purpose/DSMYG purposeful/YP purposefulness/M purposeless/PY purr/GMDS purse/MZGDRS purser/M pursuance/M pursuant pursue/ZGDRS pursuer/M pursuit/SM purulence/M purulent purvey/DSG purveyance/M purveyor/SM purview/M pus/M push/ZGMDRS pushbike/S pushcart/SM pushchair/S pusher/M pushily pushiness/M pushover/MS pushpin/S pushy/TRP pusillanimity/M pusillanimous/Y puss/MS pussy/TRSM pussycat/MS pussyfoot/DSG pustular pustule/SM put/ISM putative putout/MS putrefaction/M putrefactive putrefy/GDS putrescence/M putrescent putrid putsch/MS putt/ZGMDRS putted/I puttee/MS putter/MDRZG putterer/M putting/I putty/GDSM putz/S puzzle/MZGDRSL puzzlement/M puzzler/M pvt pwn/SGD pyelonephritis pygmy/SM pylon/SM pylori pyloric pylorus/M pyorrhea/M pyramid/GSMD pyramidal pyre/MS pyrimidine/MS pyrite/SM pyrites/M pyromania/M pyromaniac/SM pyrotechnic/S pyrotechnical pyrotechnics/M python/SM pyx/MS pzazz q qr qt qty qua quack/GMDS quackery/M quad/MS quadrangle/SM quadrangular quadrant/MS quadraphonic quadratic/MS quadrature quadrennial quadrennium/MS quadriceps/MS quadrilateral/SM quadrille/XMNS quadrillion/M quadriplegia/M quadriplegic/SM quadrivium/M quadruped/MS quadrupedal quadruple/MGDS quadruplet/MS quadruplicate/MGNDS quadruplication/M quaff/GMDS quagmire/SM quahog/MS quail/GMDS quaint/PRYT quaintness/M quake/MGDS quaky qualification/EM qualified/U qualifier/SM qualify/EGXNDS qualitative/Y quality/SM qualm/MS qualmish quandary/SM quango/S quanta quantifiable quantification/M quantifier/M quantify/NDRSZG quantitative/Y quantity/SM quantum/M quarantine/MGDS quark/MS quarrel/SZGMDR quarreler/M quarrelsome/P quarrelsomeness/M quarry/DSMG quart/MS quarter/SGMDY quarterback/GMDS quarterdeck/MS quarterfinal/SM quarterly/SM quartermaster/MS quarterstaff/M quarterstaves quartet/SM quarto/MS quartz/M quasar/MS quash/GDS quasi quatrain/MS quaver/MDSG quavery quay/MS quayside/S queasily queasiness/M queasy/TPR queen/GMDYS queenly/RT queer/PTGMDRYS queerness/M quell/GDS quench/ZGDRSB quenchable/U quencher/M quenchless querulous/YP querulousness/M query/DSMG ques quesadilla/MS quest/IFAMS quested questing question/SMDRZGBJ questionable/U questionably/U questioned/U questioner/M questioning/MY questionnaire/SM queue/MDSG quibble/DRSMZG quibbler/M quiche/SM quick/MNRYXTP quicken/DG quickfire quickie/SM quicklime/M quickness/M quicksand/MS quicksilver/M quickstep/MS quid/MS quiescence/M quiescent/Y quiet/SMDNRYXTGP quieten/DG quietism quietness/M quietude/IEM quietus/MS quiff/S quill/SM quilt/SMDRZG quilter/M quilting/M quin/S quince/SM quine/S quinine/M quinoa quinsy/M quint/SM quintessence/SM quintessential/Y quintet/SM quintuple/MGDS quintuplet/MS quip/MS quipped quipping quipster/SM quire's quire/IAS quirk/SMDG quirkiness/M quirky/RTP quirt/SM quisling/SM quit/S quitclaim/MS quite quittance/M quitter/SM quitting quiver/SMDG quivery quixotic quixotically quiz/M quizzed quizzer/SM quizzes quizzical/Y quizzing quo/H quoin/SM quoit/SMDG quondam quorate/I quorum/SM quot/B quota/SM quotability/M quotation/SM quote's quote/UDSG quotidian quotient/SM qwerty r/S rabbet/GMDS rabbi/SM rabbinate/M rabbinic rabbinical rabbit/GMDS rabble/MS rabid/PY rabidness/M rabies/M raccoon/M race/MZGDRS racecourse/SM racegoer/S racehorse/MS raceme/MS racer/M racetrack/MS raceway/MS racial/Y racialism/M racialist/MS racily raciness/M racing/M racism/M racist/SM rack/GMDS racket/SMDG racketeer/SMDG racketeering/M raconteur/SM racquetball/SM racy/PRT rad/SM radar/SM radarscope/SM raddled radial/SMY radiance/M radiant/Y radiate/DSGNX radiation/M radiator/SM radical/SMY radicalism/M radicalization/M radicalize/DSG radicchio/M radii radio/MDGS radioactive/Y radioactivity/M radiocarbon/M radiogram/MS radiographer/SM radiography/M radioisotope/MS radiologist/SM radiology/M radioman/M radiomen radiometer/MS radiometric radiometry/M radiophone/SM radioscopy/M radiosonde/SM radiosurgery radiotelegraph/M radiotelegraphs radiotelegraphy/M radiotelephone/MS radiotherapist/MS radiotherapy/M radish/MS radium/M radius/M radon/M raffia/M raffish/YP raffishness/M raffle/DSMG raft/ZGMDRS rafter/M rafting/M rag/SGMD raga/MS ragamuffin/MS ragbag/M rage/MS ragga ragged/RYTP raggedness/M raggedy/RT ragging raging/Y raglan/SM ragout/SM ragtag/S ragtime/M ragweed/M ragwort rah raid/ZGMDRS raider/M rail's rail/CGDS railcard/S railing/SM raillery/SM railroad/SZGMDR railroader/M railroading/M railway/SM railwayman railwaymen raiment/M rain/GMDS rainbow/SM raincoat/SM raindrop/SM rainfall/SM rainmaker/SM rainmaking/M rainproof rainstorm/MS rainwater/M rainy/RT raise/MZGDRS raiser/M raisin/SM rajah/M rajahs rake/MGDS rakish/YP rakishness/M rally/DSMG ram/SM ramble/DRSMZGJ rambler/M rambunctious/PY rambunctiousness/M ramekin/SM ramie/M ramification/M ramify/DSXNG ramjet/SM rammed ramming ramp/GMS rampage/DSMG rampancy/M rampant/Y rampart/SM ramrod/SM ramrodded ramrodding ramshackle ran/A ranch/MDRSZG rancher/M ranching/M rancid/P rancidity/M rancidness/M rancor/M rancorous/Y rand/M randiness/M random/PSY randomization/M randomize/DSG randomness/MS randy/RTP ranee/MS rang/ZR range's range/CGDS rangefinder/S ranger/M ranginess/M rangy/RTP rank/TGJPMDRYS ranking/M rankle/DSG rankness/M ransack/SGD ransom/SZGMDR ransomer/M rant/ZGMDJRS ranter/M rap/SZGMDR rapacious/PY rapaciousness/M rapacity/M rape/MS raper/M rapeseed/M rapid/PMRYTS rapidity/M rapidness/M rapier/SM rapine/M rapist/SM rapped rappel/SM rappelled rappelling rapper/SM rapping rapport/MS rapporteur/S rapprochement/SM rapscallion/MS rapt/YP raptness/M raptor/S rapture/MS rapturous/Y rare/YTGPDRS rarebit/MS rarefaction/M rarefy/GDS rareness/M rarity/SM rascal/SMY rash/ZTMRSYP rasher/M rashness/M rasp/GMDS raspberry/SM raspy/RT raster rat/SM ratatouille/M ratbag/S ratchet/GMDS rate/JXMZGNDRS rated/U ratepayer/S rater/M rather rathskeller/SM ratification/M ratifier/M ratify/NDRSZG rating/M ratio/MS ratiocinate/GNDS ratiocination/M ration/MDG rational/SMY rationale/MS rationalism/M rationalist/SM rationalistic rationality/M rationalization/MS rationalize/DSG ratlike ratline/SM rattan/SM ratted ratter/SM ratting rattle/DRSMZGJ rattlebrain/SMD rattler/M rattlesnake/SM rattletrap/SM rattly rattrap/SM ratty/RT raucous/YP raucousness/M raunchily raunchiness/M raunchy/TRP ravage/DRSMZG ravager/M ravages/M rave/JMZGDRS ravel's ravel/UDSG raveling/S raven/MDSG ravenous/Y ravine/SM raving/M ravioli/SM ravish/DRSZGL ravisher/M ravishing/Y ravishment/M raw/PTMR rawboned rawhide/M rawness/M ray/SM rayon/M raze/GDS razor/MS razorback/MS razz/GMDS razzmatazz/M rcpt rd re/DSMYTGVJ reach/MDSGB reachable/U reacquire/DSG react/V reactant/SM reactionary/SM reactivity read/ZGMRBJS readability/SM reader/M readership/SM readily readiness/M reading/M readmitted readout/SM ready/DRSTGP reafforestation real/TMRYPS realism/M realist/SM realistic/U realistically/U realities reality/UM realization/MS realize/DSBG realized/U realm/MS realness/M realpolitik/M realty/M ream/ZGMDRS reamer/M reap/ZGDRS reaper/M rear/GMDS rearguard/MS rearmost rearward/S reason/SMDRZGB reasonable/UP reasonableness/UM reasonably/U reasoner/M reasoning/M reassuring/Y rebate/M rebel/MS rebellion/MS rebellious/YP rebelliousness/M rebid/S rebidding rebirth/M reboil/SDG rebuild/SG rebuke/DSMG rebuking/Y rebuttal/MS rec'd rec/M recalcitrance/M recalcitrant recant/SDG recantation/SM recap/MS recapitalization recce/S recd receipt/SMDG receivables/M receive/DRSZGB receiver/M receivership/M recent/RYTP recentness/M receptacle/SM reception/MS receptionist/SM receptive/PY receptiveness/M receptivity/M receptor/SM recess/MDSGV recessional/SM recessionary recessive/SM recherche recidivism/M recidivist/SM recipe/SM recipient/SM reciprocal/SMY reciprocate/GNDS reciprocation/M reciprocity/M recital/SM recitalist/MS recitative/MS reciter/SM reckless/YP recklessness/M reckon/SJDG reckoning/M reclamation/M recline/DRSZG recliner/M recluse/SMV recognizable/U recognizably recognize/DRSGB recognized/U recombination recompense/DSMG recompilation recompile/GD recon/S reconcile/GDSB reconciliation/S recondite reconfiguration reconfigure/D reconnaissance/MS reconnoiter/DGS reconstruct/V reconstructed/U recorded/U recorder/MS recording/MS recoup/DG recourse/M recoverable/U recovery/SM recreant/MS recreational recriminate/DSGNX recrimination/M recriminatory recrudesce/GDS recrudescence/M recrudescent recruit/LSMDRZG recruiter/M recruitment/M rectal/Y rectangle/MS rectangular rectifiable rectification/M rectifier/M rectify/XNDRSZG rectilinear rectitude/M recto/MS rector/SM rectory/SM rectum/SM recumbent recuperate/GNVDS recuperation/M recur/S recurred recurrence/SM recurring recursion/S recuse recyclable/SM recycling/M red/PSM redact/SDG redaction/M redactor/SM redbird/SM redbreast/MS redbrick redcap/SM redcoat/SM redcurrant/S redden/SDG redder reddest reddish redeem/RZB redeemer/M redemption/M redemptive redhead/SMD redirection redistrict/GD redivide/GDS redlining/M redneck/SM redness/M redo/G redolence/M redolent redoubt/SBM redoubtably redound/SDG redraw/SG redskin/SM reduce/DRSZG reducer/M reducible reductase reduction/SM reductionist reductive redundancy/SM redundant/Y redwood/SM redye/DS reediness/M reedy/RTP reef/ZGMDRS reefer/M reek/GMDS reel's reel/UGDS reeve/G reexport/SDG ref/SZM refashion/DGS refection/M refectory/SM refer/B referee/DSM refereeing reference/MGDS referendum/MS referent/SM referential referral/SM referred referrer/SM referring reffed reffing refill/BM refined/U refinement/SM refiner/SM refinery/S refitting reflate/XDSGN reflationary reflect/GVSD reflection/MS reflective/Y reflector/MS reflexive/SMY reflexology reforge/DSG reform/MZ reformat/V reformatory/SM reformatting reformed/U reformist/S refortify/GDS refract/SGVD refraction/M refractory/SM refrain/SGMD refresh/ZGLDRS refresher/M refreshing/Y refreshment/SM refreshments/M refrigerant/SM refrigerate/DSGN refrigeration/M refrigerator/MS refuge/SM refugee/SM refulgence/M refulgent refund/B refurbishment/MS refusal/MS refutation/MS refute/BDRSZG refuter/M reg regal/DYG regalement/M regalia/M regard/ESMDG regardless regards/M regather/DGS regatta/SM regency/SM regeneracy/M regenerate/V regex/M regexp/S reggae/M regicide/MS regime/SM regimen/SM regiment/MDGS regimental regimentation/M region/SM regional/Y regionalism/MS register/GMDS registered/U registrant/MS registrar/MS registration/SM registry/SM regnant regress/MDSGV regression/MS regret/SM regretful/Y regrettable regrettably regretted regretting regrind/GS reground regroup/DGS regular/MYS regularity/SM regularization/M regularize/DSG regulate/CDSGNV regulated/U regulation/CM regulations regulator/MS regulatory regurgitate/DSGN regurgitation/M rehab/MS rehabbed rehabbing rehabilitate/GNVDS rehabilitation/M rehang/SDG rehears/GD rehearsal/MS rehearsed/U rehi rehung reign/MDSG reimburse/BDSGL reimbursement/MS rein/GD reindeer/M reinforce/LGDS reinforcement/SM reinitialize reinstall/DG reinstatement/M reinsurance reiterate/V reject/GSMD rejection/SM rejoice/JGDS rejoicing/M rejoinder/SM rejuvenate/DSGN rejuvenation/M rel relate/DRSXZGNV relatedness/M relater/M relation/M relational relationship/MS relative/MYS relativism/M relativist/S relativistic relativity/M relax/DRSZG relaxant/MS relaxation/SM relaxer/M relay/D release/B released/U relegate/GNDS relent/SGD relentless/PY relentlessness/M relevance/M relevancy/M relevant/Y reliability/UM reliable/U reliably/U reliance/M reliant relic/MS relief/SM relieve/ZGDRS reliever/M religion/SM religiosity religious/MYP religiousness/M reline/DSG relinquish/LDSG relinquishment/M reliquary/SM relish/GMDS relist/SGD relocate/B reluctance/M reluctant/Y rely/GDS rem/M remain/SGD remainder/GMDS remand/SGD remapping remark/B remarkableness/M remarkably remarked/U remediable remedy/GDSM remember/DG remembered/U remembrance/MS reminder/M reminisce/GDS reminiscence/MS reminiscent/Y remiss/PY remissness/M remit/S remittance/SM remitted remitting/U remix/DSG remnant/MS remodel/GDS remold/SGD remonstrant/SM remonstrate/DSG remorse/M remorseful/Y remorseless/PY remorselessness/M remote/RSMYTP remoteness/M removal/SM remunerate/GNVXDS remuneration/M renaissance/MS renal renascence/S rend/GS render/SGMDJ rendering/M rendezvous/GMDS rendition/MS renegade/DSMG renege/DRSZG reneger/M renew/DSBG renewal/MS rennet/M rennin/M renounce/LDSG renouncement/M renovate/DSXGN renovation/M renovator/MS renown/MD rent/ZGMDRS rental/SM renter/M renunciation/SM reopen/SDG reorg/DSG rep/SM repaint/GDS repair/BZR repairer/M repairman/M repairmen reparable reparation/MS reparations/M repartee/M repatriate/XDSMGN repatriation/M repeat/SMDRZGB repeatable/U repeatably repeated/Y repeater/M repeating/M repel/S repelled repellent/SM repelling repent/SDG repentance/M repentant/Y repercussion/S repertoire/MS repertory/SM repetition/MS repetitious/YP repetitiousness/M repetitive/YP repetitiveness/M rephotograph/DG replaceable replant/GSD replenish/LGDS replenishment/M replete/PDSGN repleteness/M repletion/M replica/SM replicate/DSGNX replication/M replicator/S reportage/M reported/Y reportorial reposeful repository/SM reprehend/DGS reprehensibility/M reprehensible reprehensibly reprehension/M represent/GDS representational representative/MS represented/U repression/MS repressive/PY reprieve/DSMG reprimand/GSMD reprisal/SM reprise/SMG reproach/GMDSB reproachful/Y reprobate/MS reproductive reprogramming reproving/Y reptile/SM reptilian/MS republic/S republicanism/M repudiate/XGNDS repudiation/M repudiator/MS repugnance/M repugnant repulsion/M repulsive/YP repulsiveness/M repurchase/GDS reputability/M reputably/E reputation/MS repute/DSMGB reputed/Y request/GDR requiem/SM require/LDG requirement/MS requisite/XMNS requisition/GMD requital/M requite/DRSZG requited/U requiter/M reread/SG rerecord/GDS rerunning resat rescind/SDG rescission/M rescue/DRSMZG rescuer/M reseal/B resell/SG resemble/DSG resend resent/LSDG resentful/YP resentfulness/M resentment/MS reserpine/M reservation/MS reserved/UY reservedness/M reservist/SM reservoir/SM resetting reshipping residence/SM residency/SM resident/MS residential residua residual/MS residue/SM residuum/M resignation/SM resigned/Y resilience/M resiliency/M resilient/Y resinous resist/SMDRZG resistance/SM resistant/U resistible resistless resistor/MS resit/S resitting resold resole/DSG resolute/PY resoluteness/M resolve/RBM resolved/U resonance/SM resonant/Y resonate/GDS resonator/SM resorption/M resound/SGD resounding/Y resourceful/YP resourcefulness/M resp respect/ESGVMD respectability/M respectable respectably respectful/EY respectfulness/M respective/Y respell/SGD respiration/M respirator/SM respiratory respire/DG resplendence/M resplendent/Y respond/SGD respondent/SM response/MS responsibility/SM responsible responsibly responsive/UYP responsiveness/UM rest/GVMDS restate/GDS restaurant/SM restaurateur/MS restful/YP restfuller restfullest restfulness/M restitution/M restive/YP restiveness/M restless/PY restlessness/M restoration/SM restorative/SM restorer/SM restrained/U restraint/MS restrict/SDGV restricted/U restriction/MS restrictive/YP restrictiveness/M restring/SG restroom/SM restructuring/SM result/GSMD resultant/SM resume/DSMG resumption/MS resupply/DSG resurgence/MS resurgent resurrect/GSD resurrection/MS resuscitate/GNDS resuscitation/M resuscitator/SM retailer/MS retain/SDRZG retainer/M retake/G retaliate/DSGNVX retaliation/M retaliatory retard/SMDRZG retardant/SM retardation/M retarder/M retch/DSG reteach/GS retention/M retentive/YP retentiveness/M rethink/SGM rethought reticence/M reticent/Y reticulated reticulation/MS retina/SM retinal retinue/SM retiree/SM retirement/MS retort/GMD retrace/GDS retract/DBG retractile retraction/S retrain/DGS retread/D retrenchment/MS retribution/MS retributive retrieval/SM retrieve/DRSMZGB retriever/M retro/MS retroactive/Y retrofire/GDS retrofit/SM retrofitted retrofitting retrograde/DSG retrogress/GVDS retrogression/M retrorocket/MS retrospect/MDSGV retrospection/M retrospective/MYS retrovirus/MS retsina/M returnable/SM returnee/SM rev/ZVM revamping/M reveal/GJSD revealed/U revealing/Y reveille/M revel/JMDRSZG revelation/SM reveler/M revelry/SM revenge/MGDS revenuer/SM reverb reverberate/DSGNX reverberation/M revere/DSG reverence/DSMG reverend/SM reverent/Y reverential/Y reverie/MS revers/M reversal/SM reverse/Y reversibility reversible reversibly revert/GSD revertible revetment/SM revile/DRSLZG revilement/M reviler/M reviser/MS revision/SM revisionism/M revisionist/SM revival/MS revivalism/M revivalist/SM revive/DSG revivification/M revocable revoke/DSG revolt/GD revolting/Y revolution/SM revolutionary/SM revolutionist/SM revolutionize/DSG revolve/BZGDRS revolver/M revue/MS revulsion/M revved revving rewarded/U rewarding/U rewarm/GSD rewash/GDS reweave/GS rewedding rewind/MB rewound rewrite/MGS rhapsodic rhapsodical rhapsodize/GDS rhapsody/SM rhea/MS rhenium/M rheostat/SM rhesus/MS rhetoric/M rhetorical/Y rhetorician/SM rheum/M rheumatic/MS rheumatically rheumatism/M rheumatoid rheumy rhinestone/SM rhinitis/M rhino/MS rhinoceros/MS rhinoplasty rhinovirus/MS rhizome/MS rho/SM rhodium/M rhododendron/SM rhomboid/SM rhomboidal rhombus/MS rhubarb/MS rhyme/MZGDRS rhymer/M rhymester/MS rhythm/SM rhythmic rhythmical/Y rial/MS rib/SM ribald ribaldry/M ribbed ribber/SM ribbing ribbon/SM riboflavin/M rice/MZGDRS ricer/M rich/TMRSYP richness/M rick/GMDS rickets/M rickety/RT rickrack/M rickshaw/MS ricochet/GMDS ricotta/M rid/S riddance/M ridden ridding riddle/DSMG ride/MZGRS rider/M riderless ridership/M ridge/MGDS ridgepole/SM ridgy ridicule/MGDS ridiculous/YP ridiculousness/M riding/M rife/TR riff/GMDS riffle/DSMG riffraff/M rifle/MZGDRS rifleman/M riflemen rifler/M rifling/M rift/GMDS rig/SM rigatoni/M rigged rigger/SM rigging/M right/MDRYSPTG righteous/UP righteously righteousness/UM rightful/PY rightfulness/M rightism/M rightist/SM rightmost rightness/M righto rightsize/DSG rightward/S rigid/YP rigidity/M rigidness/M rigmarole/MS rigor/MS rigorous/YP rigorousness/M rile/GDS rill/MS rim/SGMD rime/MS rimless rimmed rimming rind/MS ring/ZGMDRJ ringer/M ringgit/MS ringleader/MS ringlet/MS ringlike ringmaster/MS ringside/M ringtone/SM ringworm/M rink/MS rinse/MGDS riot/ZGMDRS rioter/M rioting/M riotous/PY rip/SXTMNR riparian ripcord/MS ripe/YP ripen/DG ripened/U ripeness/M ripoff/SM riposte/MGDS ripped ripper/SM ripping ripple/DSMG ripply ripsaw/SM riptide/MS rise/JMZGRS risen riser/M risibility/M risible rising/M risk/GMDS riskily riskiness/M risky/RPT risotto/MS risque rissole/S rite/MS ritual/SMY ritualism/M ritualistic ritualistically ritualized ritzy/RT riv/ZNR rival/MDSG rivaled/U rivalry/SM rive/CGDS river/M riverbank/SM riverbed/MS riverboat/SM riverfront riverside/MS rivet/MDRSZG riveter/M riviera/S rivulet/MS riyal/MS rm roach/GMDS road/IMS roadbed/SM roadblock/MDSG roadhouse/SM roadie/MS roadkill/M roadrunner/SM roadshow/SM roadside/SM roadster/SM roadway/SM roadwork/SM roadworthy roam/ZGDRS roamer/M roaming/M roan/MS roar/ZGMDRS roarer/M roaring/M roast/ZGMDRSJ roaster/M roasting/M rob/S robbed robber/MS robbery/SM robbing robe's robe/EGDS robin/MS robocall/SGD robot/MS robotic/S robotics/M robotize/GDS robust/RYPT robustness/M rock/ZGMDRS rockabilly/M rockbound rocker/M rockery/S rocket/MDSG rocketry/M rockfall/SM rockiness/M rocky/TRP rococo/M rod/SM rode rodent/MS rodeo/MS roe/SM roebuck/SM roentgen/MS roger/GDS rogue's rogue/KS roguery/M roguish/YP roguishness/M roil/GDS roister/ZGDRS roisterer/M role/MS roll/MDRZGJS rollback/SM roller/M rollerblading rollerskating/M rollick/SDG rollicking/M rollmop/S rollover/SM romaine/MS roman/M romance/MZGDRS romancer/M romantic/MS romantically romanticism/M romanticist/SM romanticize/DSG romeo/MS romp/MDRZGS romper/M rondo/SM rood/MS roof/MDRZGS roofer/M roofing/M roofless rooftop/SM rook/MDGS rookery/SM rookie/SM room/MDRZGS roomer/M roomette/SM roomful/SM roominess/M roommate/SM roomy/RTP roost/SMDRZG rooster/M root/MDRZGS rooter/M rootkit/SM rootless/P rootlet/SM rope/MZGDRS roper/M ropy/RT rosary/SM rose/MS roseate rosebud/SM rosebush/MS rosemary/M rosette/SM rosewater/M rosewood/MS rosily rosin/SMDG rosiness/M roster/SM rostrum/MS rosy/RTP rot/SM rota/S rotary/SM rotate/DSGNX rotation/M rotational rotatory rote/M rotgut/M rotisserie/SM rotogravure/MS rotor/SM rototiller/MS rotted rotten/TPRY rottenness/M rotter/S rotting rottweiler/S rotund/P rotunda/MS rotundity/M rotundness/M roue/MS rouge/DSMG rough/MDNRYXTGP roughage/M roughcast roughen/GD roughhouse/MGDS roughneck/GMDS roughness/M roughs roughshod roulette/M round/PSMDRYZTG roundabout/SM roundel/S roundelay/MS roundhouse/SM roundish roundness/M roundup/MS roundworm/SM rouse/DSG roust/SDG roustabout/SM rout/MRZS route's route/ADSG routeing router/M routine/MYS routinize/GDS roux rove/ZGDRS rover/M row/SZGMDR rowan/S rowboat/MS rowdily rowdiness/M rowdy/PRSMT rowdyism/M rowel/SMDG rower/M rowing/M rowlock/S royal/SMY royalist/SM royalties/M royalty/SM rpm rps rt rte rub/SM rubato/SM rubbed rubber/SM rubberize/GDS rubberneck/MDRSZG rubbernecker/M rubbery rubbing/S rubbish/MDSG rubbishy rubble/M rubdown/SM rube/MS rubella/M rubicund rubidium/M ruble/SM rubric/SM ruby/RSMT ruched ruck/DGS rucksack/MS ruckus/MS ructions rudder/SM rudderless ruddiness/M ruddy/RTP rude/YTRP rudeness/M rudiment/SM rudimentary rue/DSMG rueful/PY ruefulness/M ruff/MDYGS ruffian/MYS ruffle/DSMG ruffled/U rug/SM rugby/M rugged/PTRY ruggedness/M rugger rugrat/SM ruin/MDGS ruination/M ruinous/Y rule/MZGJDRS ruler/M ruling/M rum/SM rumba/SMDG rumble/DSJMG rumbling/M rumbustious ruminant/MS ruminate/XGNVDS rumination/M ruminative/Y rummage/DSMG rummer rummest rummy/M rumor/SMDG rumormonger/SM rump/MYS rumple/DSMG rumpus/MS run/ASM runabout/MS runaround/SM runaway/MS rundown/SM rune/MS rung/MS runic runlet/SM runnel/SM runner/SM running/M runny/RT runoff/SM runt/MS runty/RT runway/SM rupee/SM rupiah/M rupiahs rupture/MGDS rural ruse/MS rush/MDRSZG rusher/M rushy rusk/MS russet/SM rust/MDGS rustic/SM rustically rusticate/GDS rustication/M rusticity/M rustiness/M rustle/DRSJMZG rustler/M rustproof/SDG rusty/RPNT rut/SM rutabaga/SM ruthenium/M rutherfordium/M ruthless/YP ruthlessness/M rutted rutting rutty/RT rye/M s/NYXB sabbatical/SM saber/MS sable/MS sabot/MS sabotage/DSMG saboteur/SM sabra/MS sac/SM saccharin/M saccharine sacerdotal sachem/SM sachet/SM sack/GMDJS sackcloth/M sackful/MS sacking/M sacra sacrament/MS sacramental sacred/YP sacredness/M sacrifice/DSMG sacrificial/Y sacrilege/MS sacrilegious/Y sacristan/MS sacristy/SM sacroiliac/MS sacrosanct/P sacrosanctness/M sacrum/M sad/PY sadden/SDG sadder saddest saddle's saddle/UDSG saddlebag/MS saddler/S saddlery sades sadhu/S sadism/M sadist/SM sadistic sadistically sadness/M sadomasochism/M sadomasochist/MS sadomasochistic safari/SGMD safe/MYTPRS safeguard/SMDG safekeeping/M safeness/M safety/SM safflower/MS saffron/MS sag/SM saga/MS sagacious/Y sagacity/M sage/MYTRS sagebrush/M sagged sagging saggy/RT sago/M saguaro/MS sahib/MS said/U sail/GMDSJ sailboard/MRZGS sailboarder/M sailboarding/M sailboat/MS sailcloth/M sailfish/MS sailing/M sailor/SM sailplane/MS saint/MDYS sainthood/M saintlike saintliness/M saintly/PRT saith sake/M salaam/SMDG salable/U salacious/PY salaciousness/M salacity/M salad/MS salamander/SM salami/SM salary/DSM sale/ABMS saleroom/S salesclerk/SM salesgirl/SM saleslady/SM salesman/M salesmanship/M salesmen salespeople/M salesperson/MS salesroom/S saleswoman/M saleswomen salience/M salient/SMY saline/SM salinity/M saliva/M salivary salivate/GNDS salivation/M sallow/RTP sallowness/M sally/DSMG salmon/SM salmonella/M salmonellae salon/MS saloon/SM salsa/MS salt's salt/CTGDS saltbox/MS saltcellar/SM salted/U salter saltine/SM saltiness/M saltpeter/M saltshaker/SM saltwater/M salty/RTP salubrious/I salutary salutation/MS salutatorian/MS salutatory salute/DSMG salvage/DSMG salvageable salvation/M salve/MZGDRS salver/M salvo/MS samarium/M samba/MDSG same/SP sameness/M samey samizdat/S samosa/S samovar/SM sampan/SM sample/DRSMZGJ sampler/M samurai/SM sanatorium/SM sanctification/M sanctify/GDSN sanctimonious/YP sanctimoniousness/M sanctimony/M sanction/GSMD sanctioned/U sanctity/M sanctuary/SM sanctum/SM sand/ZGMDRS sandal/SM sandalwood/M sandbag/SM sandbagged sandbagger/SM sandbagging sandbank/MS sandbar/SM sandblast/ZGMDRS sandblaster/M sandbox/MS sandcastle/MS sander/M sandhog/SM sandiness/M sandlot/SM sandlotter/MS sandman/M sandmen sandpaper/GMDS sandpiper/MS sandpit/S sandstone/M sandstorm/SM sandwich/MDSG sandy/RTP sane/IYTR saneness/M sang/S sangfroid/M sangria/M sanguinary sanguine/Y sanitarian/SM sanitarium/SM sanitary/IU sanitation/M sanitize/GDS sanity/IM sank sans sanserif sap/SM sapience/M sapiens sapient sapless sapling/MS sapped sapper/S sapphire/SM sappiness/M sapping sappy/PRT saprophyte/SM saprophytic sapsucker/SM sapwood/M saran/M sarcasm/MS sarcastic sarcastically sarcoma/MS sarcophagi sarcophagus/M sardine/MS sardonic sardonically sarge/MS sari/MS sarky sarnie/S sarong/SM sarsaparilla/MS sartorial/Y sash/MS sashay/SGMD sass/GMDS sassafras/MS sassy/RT sat satanic satanical/Y satanism/M satanist/MS satay satchel/MS sate/GDS sateen/M satellite/DSMG satiable/I satiate/GNDS satiation/M satiety/M satin/M satinwood/SM satiny satire/SM satiric satirical/Y satirist/SM satirize/DSG satisfaction/EM satisfactions satisfactorily/U satisfactory/U satisfied/U satisfy/EDSG satisfying/U satisfyingly satori/M satrap/SM satsuma/S saturate/DSGN saturated/U saturation/M saturnine satyr/MS satyriasis/M satyric sauce/MZGDRS saucepan/SM saucer/M saucily sauciness/M saucy/RPT sauerkraut/M sauna/MDSG saunter/MDGS saurian sauropod/SM sausage/MS saute/MS sauteed sauteing savage/DRSMYTGP savageness/M savagery/SM savanna/MS savant/SM save/BJMZGDRS saved/U saver/M saving/M savings/M savior/SM savor/MDSG savoriness/M savory/PTRSM savoy/MS savvy/DRSMTG saw/SGMD sawbones/M sawbuck/MS sawdust/M sawfly/SM sawhorse/SM sawmill/MS sawyer/SM sax/MS saxifrage/SM saxophone/MS saxophonist/SM say's say/USG saying/SM scab/MS scabbard/MS scabbed scabbiness/M scabbing scabby/PTR scabies/M scabrous scad/MS scaffold/SMG scaffolding/M scag/S scagged scalar/S scalawag/MS scald/MDSG scale's scale/CGDS scaleless scalene scaliness/M scallion/MS scallop/GSMD scalp/MDRSZG scalpel/SM scalper/M scaly/RTP scam/MS scammed scammer/S scamming scamp/MRSZ scamper/GMD scampi/M scan/MS scandal/SM scandalize/DSG scandalmonger/SM scandalous/Y scandium/M scanned scanner/SM scanning scansion/M scant/CDSTG scanter scantily scantiness/M scantly scantness/M scanty/RSPT scapegoat/SGMD scapegrace/MS scapula/M scapulae scapular/SM scar/GMDS scarab/SM scarce/RYTP scarceness/M scarcity/SM scare/MS scarecrow/MS scaremonger/SMG scarf/MDSG scarification/M scarify/NDSG scarily scariness/M scarlatina/M scarlet/M scarp/MDRSZG scarper/DG scarred scarring scarves scary/RTP scat/MS scathing/Y scatological scatology/M scatted scatter/GJSMD scatterbrain/SMD scattering/M scattershot scatting scatty scavenge/ZGDRS scavenger/M scenario/MS scenarist/MS scene/MS scenery/M scenic scenically scent/CMS scented/U scenting scentless scepter/MS sch schadenfreude schedule's schedule/ADSG scheduled/U scheduler/S schema schemata schematic/SM schematically schematize/GDS scheme/DRSMZG schemer/M scherzo/MS schilling/MS schism/SM schismatic/SM schist/M schizo/SM schizoid/MS schizophrenia/M schizophrenic/SM schlemiel/SM schlep/SM schlepped schlepping schlock/M schmaltz/M schmaltzy/TR schmo/M schmoes schmooze/DRSZG schmuck/MS schnapps/M schnauzer/SM schnitzel/SM schnook/MS schnoz/MS schnozzle/SM scholar/MYS scholarship/MS scholastic scholastically scholasticism school/SGMD schoolbag/MS schoolbook/SM schoolboy/MS schoolchild/M schoolchildren/M schooldays schooled/U schoolfellow/SM schoolgirl/SM schoolhouse/SM schooling/M schoolkid/S schoolmarm/SM schoolmarmish schoolmaster/MS schoolmate/SM schoolmistress/MS schoolroom/SM schoolteacher/MS schoolwork/M schoolyard/SM schooner/SM schuss/GMDS schussboomer/MS schwa/MS sci sciatic sciatica/M science/FMS scientific/U scientifically/U scientist/SM scimitar/SM scintilla/MS scintillate/DSGN scintillation/M scion/MS scissor/GDS scleroses sclerosis/M sclerotic scoff/MDRSZG scoffer/M scofflaw/MS scold/MDSGJ scolding/M scoliosis/M sconce/SM scone/MS scoop/MDSG scoopful/MS scoot/DRSZG scooter/M scope/MGDS scorbutic scorch/MDRSZG scorcher/M score/MZGDRS scoreboard/SM scorecard/MS scorekeeper/MS scoreless scoreline/S scorer/M scorn/MDRSZG scorner/M scornful/Y scorpion/MS scotch/MDSG scotchs scoundrel/MS scour/DRSZG scourer/M scourge/DSMG scout/MDRSZG scouting/M scoutmaster/MS scow/MS scowl/MDSG scrabble/MZGDRS scrabbler/M scrag/MS scraggly/RT scraggy/TR scram/S scramble's scramble/UGDS scrambler/MS scrammed scramming scrap/MDRSZGJ scrapbook/SM scrape/SM scraper/M scrapheap/SM scrapie scrapped scrapper/MS scrapping scrappy/TR scrapyard/SM scratch/GMDS scratchcard/S scratched/U scratchily scratchiness/M scratchpad/S scratchy/PRT scrawl/SMDG scrawly scrawniness/M scrawny/PTR scream/SMDRZG screamer/M screaming/Y scree/MDS screech/GMDS screechy/TR screed/S screen/SJMDG screening/M screenplay/SM screensaver screenshot/S screenwriter/SM screenwriting/M screw's screw/UDSG screwball/MS screwdriver/MS screwiness/M screwworm/SM screwy/PRT scribal scribble/MZGDRS scribbler/M scribe's scribe/CKIS scrim/MS scrimmage/MGDS scrimp/SDG scrimshaw/MDGS scrip/MS script/FSMDG scripted/U scriptural scripture/MS scriptwriter/SM scrivener/SM scrod/M scrofula/M scrofulous scrog/S scroll/GSMD scrooge/MS scrota scrotal scrotum/M scrounge/DRSZG scrounger/M scroungy/TR scrub/MS scrubbed scrubber/SM scrubbing scrubby/RT scruff/SM scruffily scruffiness/M scruffy/RPT scrum/S scrumhalf scrumhalves scrummage/S scrummed scrumming scrump/SGD scrumptious/Y scrumpy scrunch/MDSG scrunchy/SM scruple/MGDS scrupulosity/M scrupulous/UPY scrupulousness/UM scrutineer/S scrutinize/GDS scrutiny/M scuba/MDSG scud/MS scudded scudding scuff/MDSG scuffle/MGDS scull/MDRSZG sculler/M scullery/SM scullion/SM sculpt/SGD sculptor/SM sculptress/MS sculptural sculpture/DSMG scum/MS scumbag/MS scummed scumming scummy/TR scupper/MDGS scurf/M scurfy scurrility/M scurrilous/PY scurrilousness/M scurry/GDSM scurvily scurvy/TRM scutcheon/SM scuttle/MGDS scuttlebutt/M scuzzy/TR scythe/DSMG sea/SM seabed/SM seabird/MS seaboard/SM seaborne seacoast/SM seafarer/SM seafaring/M seafloor/SM seafood/M seafront/SM seagoing seagull/MS seahorse/MS seal's seal/AUSDG sealant/MS sealer/SM sealskin/M seam/GMDNS seaman/M seamanship/M seamless/Y seamount/MS seamstress/MS seamy/RT seance/SM seaplane/SM seaport/MS sear/GMDS search/AZGMDRS searcher/AM searching/Y searchlight/MS searing/Y seascape/SM seashell/SM seashore/SM seasick/P seasickness/M seaside/MS season/SGMDBJ seasonable/U seasonably/U seasonal/Y seasonality seasoned/U seasoning/M seat's seat/UGDS seating/M seatmate/SM seawall/MS seaward/MS seawater/M seaway/SM seaweed/MS seaworthiness/M seaworthy/P sebaceous seborrhea/M sebum sec'y sec/SM secant/SM secateurs secede/DSG secession/M secessionist/MS seclude/GDS seclusion/M seclusive second/SLZGMDRY secondarily secondary/SM seconder/M secondhand secondment/S secrecy/M secret/SGVMDY secretarial secretariat/MS secretary/SM secretaryship/M secrete/XNS secretion/M secretive/PY secretiveness/M secretory sect/IMS sectarian/MS sectarianism/M sectary/SM section/AESM sectional/MS sectionalism/M sectioned sectioning sector/ESM secular secularism/M secularist/SM secularization/M secularize/DSG secure/DRSYTG secured/U security/ISM secy sedan/MS sedate/DRSYTGNVP sedateness/M sedation/M sedative/SM sedentary sedge/M sedgy sediment/MS sedimentary sedimentation/M sedition/M seditious seduce/DRSZG seducer/M seduction/SM seductive/YP seductiveness/M seductress/MS sedulous/Y see/RSMZ seed's seed/AGDS seedbed/MS seedcase/MS seeded/U seeder/SM seediness/M seedless seedling/MS seedpod/MS seedy/RPT seeing/S seek/ZGRS seeker/M seem/GDS seeming/Y seemliness/UM seemly/URTP seen/U seep/GDS seepage/M seer/M seersucker/M seesaw/SMDG seethe/DSG segfault/S segment/GSMD segmentation/M segmented/U segregate/CDSGN segregated/U segregation/CM segregationist/MS segue/MGDS segueing seigneur/SM seignior/SM seine/MZGDRS seiner/M seismic seismically seismograph/ZMR seismographer/M seismographic seismographs seismography/M seismologic seismological seismologist/MS seismology/M seize/GDS seizure/MS seldom select/CSGVD selection/SM selective/Y selectivity/M selectman/M selectmen selectness/M selector/MS selenium/M selenographer/MS selenography/M self/M selfie/SM selfish/UYP selfishness/UM selfless/PY selflessness/M selfsame sell/ZGMRS seller/M selloff/MS sellotape/DSG sellout/MS seltzer/MS selvage/MS selves semantic/S semantically semanticist/MS semantics/M semaphore/DSMG semblance/ASM semen/M semester/SM semi/MS semiannual/Y semiarid semiautomatic/MS semibreve/S semicircle/SM semicircular semicolon/MS semiconducting semiconductor/MS semiconscious semidarkness/M semidetached semifinal/SM semifinalist/MS semigloss/S semimonthly/SM seminal seminar/MS seminarian/SM seminary/SM semiofficial semiotic/S semiotics/M semipermeable semiprecious semiprivate semipro/S semiprofessional/SM semiquaver/S semiretired semiskilled semisolid semisweet semitone/SM semitrailer/MS semitransparent semitropical semivowel/SM semiweekly/SM semiyearly semolina/M sempstress/MS senate/SM senator/MS senatorial send/ZGRS sender/M sendoff/MS senescence/M senescent senile senility/M senior/SM seniority/M senna/M senor/MS senora/SM senorita/SM sensation/MS sensational/Y sensationalism/M sensationalist/MS sensationalize/GDS sense/MGDS senseless/PY senselessness/M sensibilities sensibility/IM sensible/P sensibleness/M sensibly/I sensitive/SMYP sensitiveness/M sensitivities sensitivity/IM sensitization/CM sensitize/CDSG sensor/SM sensory sensual/Y sensualist/SM sensuality/M sensuous/YP sensuousness/M sent/FAU sentence/MGDS sententious/Y sentience/IM sentient/I sentiment/SM sentimental/Y sentimentalism/M sentimentalist/MS sentimentality/M sentimentalization/M sentimentalize/GDS sentinel/MS sentry/SM sepal/MS separability/IM separable separably/I separate/XMYGNVDSP separateness/M separation/M separatism/M separatist/MS separator/MS sepia/M sepsis/M septa septal septet/SM septic septicemia/M septicemic septuagenarian/MS septum/M sepulcher/GMDS sepulchral seq sequel/SM sequence/MZGDRS sequencing/M sequential/FY sequester/SDG sequestrate/XGNDS sequestration/M sequin/SMD sequinned sequitur sequoia/MS seraglio/MS serape/SM seraph/M seraphic seraphs sere/TR serenade/MGDS serendipitous serendipity/M serene/RPYT sereneness/M serenity/M serf/MS serfdom/M serge/M sergeant/MS serial/SMY serialization/SM serialize/GDS series/M serif/MS serigraph/M serigraphs serine serious/PY seriousness/M sermon/SM sermonize/GDS serology/M serotonin serous serpent/MS serpentine/M serrate/XND serration/M serried serum/MS servant/MS serve's/AF serve/FACGDS server/SM servery/S service/EMS serviceability/M serviceable serviced serviceman/M servicemen servicewoman/M servicewomen servicing serviette/MS servile servility/M serving's servings servitor/MS servitude/M servo/MS servomechanism/SM servomotor/MS sesame/SM sesquicentennial/MS session/MS set/AISM setback/MS setscrew/SM setsquare/S sett/BJZGRS settee/MS setter/M setting/M settle's settle/AUGDS settlement/AM settlements settler/SM setup/MS seven/MHS seventeen/SMH seventeenth/M seventeenths seventh/M sevenths seventieth/M seventieths seventy/SMH sever/ETGDS several/MY severance/SM severe/YPR severeness/M severity/M sew/ASGD sewage/M sewer/MS sewerage/M sewing/M sewn/A sex/GMDS sexagenarian/SM sexily sexiness/M sexism/M sexist/MS sexless sexologist/SM sexology/M sexpot/MS sextant/SM sextet/MS sexting sexton/MS sextuplet/SM sexual/Y sexuality/M sexy/PTR sf sh shabbily shabbiness/M shabby/PTR shack/MDSG shackle's shackle/UGDS shad/GMDSJ shade/MS shadily shadiness/M shading/M shadow/SGMD shadowbox/GDS shadowy/RT shady/RPT shaft/MDSG shag/MS shagged shagginess/M shagging shaggy/TPR shah/M shahs shake/MZGRS shakedown/SM shaken/U shakeout/MS shaker/M shakeup/MS shakily shakiness/M shaky/RPT shale/M shall shallot/MS shallow/TPMRYS shallowness/M shalom shalt sham/GMDS shaman/SM shamanic shamanism shamanistic shamble/MGDS shambles/M shambolic shame/MS shamefaced/Y shameful/PY shamefulness/M shameless/YP shamelessness/M shammed shamming shampoo/ZGMDRS shampooer/M shamrock/MS shan't shandy/S shanghai/DSG shank/MS shantung/M shanty/SM shantytown/SM shape's shape/AGDS shaped/U shapeless/YP shapelessness/M shapeliness/M shapely/PTR shard/MS share/MZGDRS shareable sharecrop/S sharecropped sharecropper/MS sharecropping shareholder/SM shareholding/S sharer/M shareware/M sharia/M shariah shark/MDSG sharkskin/M sharp/MDNRYSPXZTG sharpen/ADGS sharpener/MS sharper/M sharpie/MS sharpish sharpness/M sharpshooter/SM sharpshooting/M shatter/GMDS shatterproof shave/MZGDRSJ shaven/U shaver/M shaving/M shawl/MS shay/MS she'd she'll she/DSM sheaf/M shear/MDRSZG shearer/M sheath/JM sheathe/UGDS sheathing/M sheaths sheave/DSMG shebang/MS shebeen/S shed/MS shedding sheen/M sheeny/TR sheep/M sheepdog/MS sheepfold/SM sheepherder/MS sheepish/YP sheepishness/M sheepskin/MS sheer/MDRSPTG sheerness/M sheet/MSG sheeting/M sheetlike sheikdom/MS sheikh/M sheikhs sheila/S shekel/SM shelf/M shell/MDRSG shellac/MS shellacked shellacking/MS shellfire/M shellfish/MS shelter/GMDS shelve/GDS shelving/M shenanigan/SM shepherd/SMDG shepherdess/MS sherbet/SM sheriff/SM sherry/SM shew/GDS shewn shh shiatsu/M shibboleth/M shibboleths shield/MDGS shift/GMDS shiftily shiftiness/M shiftless/PY shiftlessness/M shifty/RPT shiitake/S shill/GMDSJ shillelagh/M shillelaghs shilling/M shim/MS shimmed shimmer/SMDG shimmery shimming shimmy/DSMG shin/ZGMDRS shinbone/SM shindig/SM shine/MS shiner/M shingle/DSMG shinguard/M shininess/M shinned shinning shinny/DSG shinsplints/M shiny/TRP ship's ship/ALS shipboard/MS shipbuilder/SM shipbuilding/M shipload/SM shipmate/SM shipment/AM shipments shipowner/MS shipped/A shipper/SM shipping/M shipshape shipwreck/GMDS shipwright/MS shipyard/SM shire/MS shirk/ZGDRS shirker/M shirr/GMDSJ shirring/M shirt/GMDS shirtfront/SM shirting/M shirtless shirtsleeve/SM shirttail/SM shirtwaist/MS shirty shit/SM! shitfaced/! shithead/S! shitload/! shitted/! shitting/! shitty/RT! shiv/ZMRS shiver/MDG shivery shoal/GMDS shoat/MS shock/ZGMDRS shocker/M shocking/Y shockproof shod/U shoddily shoddiness/M shoddy/PRMT shoe/MS shoehorn/GMDS shoeing shoelace/MS shoemaker/SM shoeshine/SM shoestring/SM shoetree/MS shogun/MS shogunate/M shone shoo/GDS shook shoot/ZGMRSJ shooter/M shooting/M shootout/MS shop/MS shopaholic/S shopfitter/S shopfitting shopfront/S shopkeeper/MS shoplift/DRZGS shoplifter/M shoplifting/M shoppe/MZGDRS shopper/M shopping/M shoptalk/M shopworn shore/MGDS shorebird/SM shoreline/MS shoring/M short/XTGMDNRYSP shortage/MS shortbread/M shortcake/MS shortchange/DSG shortcoming/MS shortcrust shortcut/MS shorten/JGD shortening/M shortfall/MS shorthand/MD shorthorn/MS shortish shortlist/DGS shortness/M shortsighted/PY shortsightedness/M shortstop/MS shortwave/MS shorty/SM shot/MS shotgun/SM shotgunned shotgunning should should've shoulder/MDGS shouldn't shout/ZGMDRS shouter/M shove/MGDS shovel/MDSG shovelful/SM show/JZGMDRS showbiz/M showboat/MDGS showcase/MGDS showdown/MS shower/MDG showerproof showery showgirl/MS showground/S showily showiness/M showing/M showjumping showman/M showmanship/M showmen shown showoff/SM showpiece/SM showplace/SM showroom/MS showstopper/MS showstopping showtime showy/TRP shpt shrank shrapnel/M shred/MS shredded shredder/MS shredding shrew/MS shrewd/RYPT shrewdness/M shrewish shriek/MDSG shrift/M shrike/MS shrill/DRSPTG shrillness/M shrilly shrimp/MDRSZG shrine/MS shrink/MSBG shrinkage/M shrive/GDS shrivel/SGD shriven shroud/GMDS shrub/MS shrubbery/SM shrubby/RT shrug/MS shrugged shrugging shrunk/N shtick/MS shuck/GMDS shucks/S shudder/MDSG shuffle/AMGDS shuffleboard/SM shuffler/SM shun/S shunned shunning shunt/MSDG shush/DSG shut/S shutdown/SM shuteye/M shutoff/SM shutout/SM shutter/SMDG shutterbug/MS shutting shuttle/DSMG shuttlecock/GMDS shy/TGDRSMY shyer shyest shyness/M shyster/SM sibilant/SM sibling/SM sibyl/MS sibylline sic/S sicced siccing sick/PXTGDNRYS sickbay/S sickbed/SM sicken/DG sickening/Y sickie/MS sickish sickle/MS sickly/RT sickness/MS sicko/MS sickout/SM sickroom/MS side's side/AGDS sidearm/SM sidebar/SM sideboard/SM sideburns/M sidecar/SM sidekick/SM sidelight/MS sideline/DSMG sidelong sideman/M sidemen sidepiece/MS sidereal sidesaddle/MS sideshow/MS sidesplitting sidestep/MS sidestepped sidestepping sidestroke/DSMG sideswipe/DSMG sidetrack/SMDG sidewalk/MS sidewall/MS sideways sidewinder/SM siding/MS sidle/MGDS siege/MS sienna/M sierra/MS siesta/MS sieve/MGDS sift/ZGDRS sifted/U sifter/M sigh/GMD sighs sight/GMDYSJ sighting/M sightless sightly/UTR sightread sightseeing/M sightseer/MS sigma/MS sign's/C sign/AFCGDS signage/M signal/MDRYSZG signaler/M signalization/M signalize/GDS signalman/M signalmen signatory/SM signature/MS signboard/MS signed/U signer/CMS signet/MS significance/IM significant/IY signification/M signify/XDSNG signing's/C signings signor/FMS signora/SM signore signori signorina/MS signorine signpost/GSMD silage/M silence/DRSMZG silencer/M silent/MRYST silhouette/DSMG silica/M silicate/MS siliceous silicon/SM silicone/M silicosis/M silk/MNS silkily silkiness/M silkscreen/SM silkworm/MS silky/TRP sill/MS silliness/M silly/TRSMP silo/MS silt/GMDS silty/TR silver/GMDS silverfish/MS silversmith/M silversmiths silverware/M silvery sim/S simian/MS similar/Y similarity/ESM simile/MS similitude/EM simmer/GMDS simonize/DSG simony/M simpatico simper/GMDS simpering/Y simple/TRP simpleminded simpleness/M simpleton/SM simplex simplicity/M simplification/M simplify/DSXNG simplistic simplistically simply simulacra simulacrum/S simulate/EDSGN simulation/EM simulations simulator/EMS simulcast/GMDS simultaneity/M simultaneous/Y sin/ASM since sincere/IYT sincerer sincerity/IM sine/MS sinecure/MS sinew/MS sinewy sinful/PY sinfulness/M sing/BZGMDRYS singalong/S singe/MS singeing singer/M singing/M single/PMGDS singleness/M singles/M singlet/S singleton/SM singletree/SM singsong/SMDG singular/SMY singularity/SM sinister sink/BZGMRS sinkable/U sinker/M sinkhole/SM sinless sinned sinner/MS sinning sinology sinuosity/M sinuous/Y sinus/MS sinusitis/M sinusoidal sip/SM siphon/GMDS sipped sipper/SM sipping sir/SXMN sire/CMGDS siren/M sirloin/SM sirocco/SM sirrah sirree/M sis/MS sisal/M sissified sissy/RSMT sister/ASM sisterhood/MS sisterliness/M sisterly/P sit/S sitar/SM sitarist/MS sitcom/SM site/MGDS sitemap/SM sitter/SM sitting/SM situate/DSXGN situation/M situational six/MSH sixfold sixpence/MS sixshooter/M sixteen/SMH sixteenth/M sixteenths sixth/M sixths sixtieth/M sixtieths sixty/SMH sizable size's size/AGDS sizer sizing/M sizzle/DRSMZG ska/M skate/MZGDRS skateboard/MDRSZG skateboarder/M skateboarding/M skater/M skating/M skedaddle/MGDS skeet/ZMR skein/MS skeletal skeleton/SM skeptic/SM skeptical/Y skepticism/M sketch/MDRSZG sketchbook/S sketcher/M sketchily sketchiness/M sketchpad/S sketchy/RTP skew/MDRZGS skewbald/S skewer/MDG ski/SZGMDR skibob/S skid/MS skidded skidding skidpan/S skier/M skiff/SM skiffle skiing/M skilfully skill's skill/CSD skilled/U skillet/SM skillful/UY skillfulness/M skim/MS skimmed skimmer/SM skimming skimp/SDG skimpily skimpiness/M skimpy/RTP skin/MS skincare/M skinflint/MS skinful skinhead/MS skinless skinned skinniness/M skinning skinny/RMTP skint skintight skip/MS skipped skipper/SMDG skipping skirmish/ZGMDRS skirt/SMDG skit/MS skitter/GSD skittish/YP skittishness/M skittle/S skive/DRSZG skivvy/DSMG skoal/SM skua/S skulduggery/M skulk/SDRZG skulker/M skull/SM skullcap/MS skunk/SMDG sky/GSM skycap/SM skydive/DRSZG skydiver/M skydiving/M skyjack/JZGSDR skyjacker/M skyjacking/M skylark/SGMD skylight/MS skyline/SM skyrocket/GSMD skyscraper/SM skyward/S skywriter/SM skywriting/M slab/MS slabbed slabbing slack/PXZTGMDNRYS slacken/DG slacker/M slackness/M slacks/M slag/MS slagged slagging slagheap/S slain slake/GDS slalom/MSDG slam/MS slammed slammer/SM slamming slander/MZGDRS slanderer/M slanderous slang/M slangy/RT slant/MSDG slanting/Y slantwise slap/MS slapdash slaphappy slapped slapper/S slapping slapstick/M slash/MDRSZG slasher/M slat/MDGS slate/SM slather/SDG slatted slattern/SMY slaughter/MDRZGS slaughterer/M slaughterhouse/MS slave/DRSMZG slaveholder/MS slaver/MDG slavery/M slavish/PY slavishness/M slaw/M slay/DRZGJS slayer/M slaying/M sleaze/SM sleazebag/S sleazeball/S sleazily sleaziness/M sleazy/PRT sled/MS sledded sledder/SM sledding sledge/DSMG sledgehammer/GSMD sleek/SDRYTGP sleekness/M sleep/SMRZG sleeper/M sleepily sleepiness/M sleepless/PY sleeplessness/M sleepover/SM sleepwalk/ZGSDR sleepwalker/M sleepwalking/M sleepwear/M sleepy/RPT sleepyhead/MS sleet/SMDG sleety sleeve/DSM sleeveless sleigh/MDG sleighs sleight/SM slender/PRT slenderize/DSG slenderness/M slept sleuth/MG sleuths slew/MDGS slice/DRSMZG slicer/M slick/SMDRYZTGP slicker/M slickness/M slid slide/RSMZG slider/M slideshow/MS slight/SMDRYTGP slightness/M slim/PS slime/M sliminess/M slimline slimmed slimmer/S slimmest slimming/M slimness/M slimy/RTP sling/SMG slingback/S slingshot/SM slink/SG slinky/RT slip/MS slipcase/MS slipcover/MS slipknot/MS slippage/MS slipped slipper/SM slipperiness/M slippery/PRT slipping slippy slipshod slipstream/SM slipway/SM slit/MS slither/SGMD slithery slitter slitting sliver/GSMD slob/MS slobbed slobber/MDSG slobbery slobbing sloe/MS slog/MS slogan/SM sloganeering slogged slogging sloop/SM slop/MDGS slope/SM slopped sloppily sloppiness/M slopping sloppy/PTR slops/M slosh/DSG slot/MS sloth/M slothful/YP slothfulness/M sloths slotted slotting slouch/ZGMDRS sloucher/M slouchy/TR slough/GMD sloughs sloven/SMY slovenliness/M slovenly/PTR slow/DRYTGSP slowcoach/S slowdown/SM slowness/M slowpoke/SM sludge/M sludgy/RT slue/MGDS slug/MS sluggard/MS slugged slugger/SM slugging sluggish/PY sluggishness/M sluice/DSMG slum/MS slumber/GSMD slumberous slumdog/SM slumlord/MS slummed slummer slumming slummy/RT slump/SMDG slung slunk slur/MS slurp/SMDG slurred slurring slurry/M slush/M slushiness/M slushy/RPT slut/MS sluttish slutty/RT sly/TRY slyness/M smack/SMDRZG smacker/M small/SMRTP smallholder/S smallholding/S smallish smallness/M smallpox/M smarmy/RT smart/SMDNRYXTGP smarten/DG smartness/M smartphone/SM smarts/M smartwatch/MS smarty/SM smartypants/M smash/MDRSZG smasher/M smashup/SM smattering/MS smear/SMDG smeary/RT smell/SMDG smelliness/M smelly/RPT smelt/SMDRZG smelter/M smidgen/MS smilax/M smile/DSMG smiley/SM smiling/Y smirch/GMDS smirk/SMDG smite/SG smith/M smithereens/M smiths smithy/SM smitten smock/SMDG smocking/M smog/MS smoggy/RT smoke/DRSMZG smokehouse/MS smokeless smoker/M smokescreen/SM smokestack/SM smokey smokiness/M smoking/M smoky/RTP smolder/SGMD smooch/MDSG smoochy smooth/PDRYTG smoothie/MS smoothness/M smooths smorgasbord/SM smote smother/GSMD smudge/DSMG smudgy/TR smug/YP smugger smuggest smuggle/ZGDRS smuggler/M smuggling/M smugness/M smurf/S smut/MS smuttiness/M smutty/TRP snack/SMDG snaffle/DSMG snafu/SM snag/MS snagged snagging snail/SMDG snake/DSMG snakebite/MS snakelike snakeskin snaky/RT snap's snap/US snapdragon/SM snapped/U snapper/MS snappily snappiness/M snapping/U snappish/YP snappishness/M snappy/TRP snapshot/SM snare/DSMG snarf/SDG snark/S snarky/TR snarl's snarl/USDG snarling/Y snarly/TR snatch/ZGMDRS snatcher/M snazzily snazzy/TR sneak/SMDRZG sneaker/M sneakily sneakiness/M sneaking/Y sneaky/TRP sneer/SJMDG sneering/Y sneeze/DSMG snick/SDRZG snicker/MDG snide/RYT sniff/SMDRZG sniffer/M sniffle/DSMG sniffy/RT snifter/SM snip/MDRZGS snipe/SM sniper/M snipped snippet/SM snipping snippy/RT snips/M snit/MS snitch/MDSG snivel/SMDRZG sniveler/M snob/MS snobbery/M snobbish/PY snobbishness/M snobby/RT snog/S snogged snogging snood/SM snooker/MDSG snoop/SMDRZG snooper/M snoopy/TR snoot/SM snootily snootiness/M snooty/PTR snooze/DSMG snore/DRSMZG snorer/M snorkel/ZGMDRS snorkeler/M snorkeling/M snort/SMDRZG snorter/M snot/MS snottily snottiness/M snotty/TPR snout/SM snow/MDGS snowball/GSMD snowbank/SM snowbird/SM snowblower/MS snowboard/ZGMDRS snowboarder/M snowboarding/M snowbound snowdrift/SM snowdrop/SM snowfall/SM snowfield/SM snowflake/SM snowiness/M snowline snowman/M snowmen snowmobile/DSMG snowplow/SGMD snowshed snowshoe/SM snowshoeing snowstorm/SM snowsuit/SM snowy/PRT snub/MS snubbed snubbing snuff/SMDRYZG snuffbox/MS snuffer/M snuffle/MGDS snug/MYSP snugged snugger snuggest snugging snuggle/MGDS snugness/M so soak/MDGSJ soaking/M soap/MDGS soapbox/MS soapiness/M soapstone/M soapsuds/M soapy/RPT soar/MDGS sob/SM sobbed sobbing/Y sober/SDRYPTG soberness/M sobriety/IM sobriquet/SM soc soccer/M sociability/M sociable/SM sociably social/SMY socialism/M socialist/SM socialistic socialite/SM socialization/M socialize/DSG societal society/SM socioeconomic socioeconomically sociological/Y sociologist/SM sociology/M sociopath/M sociopaths sociopolitical sock/MDGS socket/SM sockeye/SM sod/SM soda/MS sodded sodden/Y sodding sodium/M sodomite/MS sodomize/GDS sodomy/M soever sofa/MS soft/NRYXTP softback softball/MS softbound softcover soften/DRZG softener/M softhearted softness/M software/M softwood/SM softy/SM soggily sogginess/M soggy/RTP soigne soignee soil/MDGS soiled/U soiree/SM sojourn/ZGMDRS sojourner/M sol/SM solace/DSMG solar solaria solarium/M sold solder/ZGSMDR solderer/M soldier/MDYSG soldiery/M sole/FSDGM solecism/SM solely solemn/PTRY solemness/M solemnify/DSG solemnity/SM solemnization/M solemnize/DSG solemnness/M solenoid/MS solicit/GDS solicitation/SM solicited/U solicitor/SM solicitous/PY solicitousness/M solicitude/M solid/PSMRYT solidarity/M solidi solidification/M solidify/DSNG solidity/M solidness/M solidus/M soliloquies soliloquize/DSG soliloquy/M solipsism/M solipsistic solitaire/MS solitariness/M solitary/SMP solitude/M solo/MDGS soloist/MS solstice/MS solubility/IM soluble/MS solute's solute/AXN solutes solution's/AE solvable/IU solve/EADSG solved/U solvency/IM solvent/IMS solver/SM somatic somber/PY somberness/M sombrero/MS some somebody/SM someday somehow someone/MS someplace somersault/MDGS somerset/SM somersetted somersetting something/SM sometime/S someway/S somewhat/S somewhere somnambulism/M somnambulist/SM somnolence/M somnolent son/SM sonar/SM sonata/SM sonatina/SM song/MS songbird/SM songbook/SM songfest/SM songster/MS songstress/MS songwriter/SM songwriting sonic sonnet/SM sonny/SM sonogram/SM sonority/M sonorous/YP sonorousness/M sonsofbitches soon/RT soot/M sooth/MDRSZG soothe soother/M soothing/Y soothsayer/MS soothsaying/M sooty/RT sop/SM sophism/M sophist/MS sophistic sophistical sophisticate/DSMGN sophisticated/U sophistication/M sophistry/SM sophomore/MS sophomoric soporific/MS soporifically sopped sopping soppy/RT soprano/MS sorbet/SM sorcerer/MS sorceress/MS sorcery/M sordid/PY sordidness/M sore/MYTRSP sorehead/MS soreness/M sorghum/M sorority/SM sorrel/SM sorrily sorriness/M sorrow/SMDG sorrowful/YP sorrowfulness/M sorry/RTP sort/FASGDM sorta sorted/U sorter/SM sortie/DSM sortieing sot/SM sottish sou'wester sou/SMH souffle/SM sough/MDG soughs sought/U souk/S soul/MS soulful/YP soulfulness/M soulless/YP soulmate/S sound/JPSMDRYZTG soundalike/S soundbar/S soundbite/S soundboard/MS soundcheck/S sounder/M sounding/M soundless/Y soundness/UM soundproof/GDS soundproofing/M soundscape/S soundtrack/SM soup/MDGS soupcon/MS soupy/RT sour/MDRYTGSP source/ADSMG sourdough/M sourdoughs sourish sourness/M sourpuss/MS sousaphone/MS souse/DSMG south/M southbound southeast/ZMR southeaster/MY southeastern southeastward/S southerly/SM southern/SZMR southerner/M southernmost southpaw/SM southward/MS southwest/ZMR southwester/MY southwestern southwestward/S souvenir/SM sovereign/SM sovereignty/M soviet/SM sow's sow/ASGD sower/SM sown/A soy/M soybean/MS sozzled spa/SM space/DRSMZG spacecraft/MS spaceflight/MS spaceman/M spacemen spaceport/SM spacer/M spaceship/SM spacesuit/SM spacewalk/SGMD spacewoman/M spacewomen spacey spacial spacier spaciest spaciness/M spacing/M spacious/YP spaciousness/M spade/DSMG spadeful/MS spadework/M spadices spadix/M spaghetti/M spake spam/MS spammed spammer/SM spamming span/MS spandex/M spangle/DSMG spangly spaniel/SM spank/SMDGJ spanking/M spanned spanner/SM spanning spar/MS spare/DRSMYTGP spareness/M spareribs/M sparing/UY spark/SMDYG sparkle/DRSMZG sparkler/M sparky/RT sparred sparring sparrow/SM sparrowhawk/S sparse/RYTP sparseness/M sparsity/M spartan spasm/SM spasmodic spasmodically spastic/SM spat/MS spate/SM spathe/SM spatial/Y spatted spatter/SGMD spatting spatula/SM spavin/MD spawn/SMDG spay/DGS speak/SRZGJ speakeasy/SM speaker/M speakerphone/S spear/SMDG spearfish/GMDS speargun spearhead/GMDS spearmint/M spec/MS special/SMY specialism/S specialist/MS specialization/MS specialize/GDS specialty/SM specie/SM species/M specif specifiable specific/MS specifically specification/M specificity/M specified/U specify/XNZDRSG specimen/SM specious/YP speciousness/M speck/SMDG speckle/MGDS specs/M spectacle/SM spectacles/M spectacular/MYS spectate/DSG spectator/SM specter/AMS spectra spectral spectrometer/MS spectroscope/MS spectroscopic spectroscopy/M spectrum/M speculate/DSXGNV speculation/M speculative/Y speculator/MS sped speech/MS speechify/DSG speechless/YP speechlessness/M speechwriter/S speed/SMRZG speedboat/SM speeder/M speedily speediness/M speeding/M speedometer/MS speedster/SM speedup/MS speedway/SM speedwell/M speedy/TPR speleological speleologist/MS speleology/M spell/JSMDRZG spellbind/ZGRS spellbinder/M spellbound spellcheck/DRZGS spellchecker/M spelldown/SM speller/M spelling/M spelunker/MS spelunking/M spend/BSRZG spender/M spending/M spendthrift/MS spent/U sperm/SM spermatozoa spermatozoon/M spermicidal spermicide/MS spew/MDRZGS spewer/M sphagnum/MS sphere/SM spherical/Y spheroid/SM spheroidal sphincter/MS sphinx/MS spic/S spice/DSMG spicily spiciness/M spicule/MS spicy/PRT spider/SM spiderweb/MS spidery spiel/SMDG spiff/SDG spiffy/TR spigot/SM spike/DSMG spikiness/M spiky/RPT spill/SMDG spillage/MS spillover/SM spillway/MS spin/MS spinach/M spinal/SMY spindle/MGDS spindly/TR spine/SM spineless/YP spinet/SM spinnaker/SM spinner/MS spinneret/SM spinney/S spinning/M spinster/SM spinsterhood/M spinsterish spiny/RT spiracle/SM spiral/SGMDY spire's spire/IFAS spirea/SM spirit's spirit/ISGD spirited/Y spiritless spiritual/MYS spiritualism/M spiritualist/MS spiritualistic spirituality/M spirituous spirochete/SM spiry spit/MDGS spitball/SM spite/ASM spiteful/PY spitefuller spitefullest spitefulness/M spitfire/SM spitted spitting spittle/M spittoon/MS spiv/S splash/GMDS splashdown/MS splashily splashiness/M splashy/RTP splat/SM splatted splatter/GSMD splatting splay/SMDG splayfeet splayfoot/MD spleen/SM splendid/RYT splendor/MS splendorous splenectomy splenetic splice/DRSMZG splicer/M spliff/S spline/S splint/SZGMDR splinter/MDG splintery split/SM splitting/MS splodge/S splosh/DSG splotch/MDSG splotchy/TR splurge/DSMG splutter/GMDS spoil's spoil/CSDRZG spoilage/M spoiled/U spoiler/CM spoilsport/MS spoke/SM spoken/U spokesman/M spokesmen spokespeople spokesperson/MS spokeswoman/M spokeswomen spoliation/CM sponge/DRSMZG spongecake/M sponger/M sponginess/M spongy/RPT sponsor/MDGS sponsorship/M spontaneity/M spontaneous/Y spoof/SMDG spook/SMDG spookiness/M spooky/RPT spool/SMDG spoon/SMDG spoonbill/MS spoonerism/MS spoonful/SM spoor/SMDG sporadic sporadically spore/DSMG sporran/S sport/SMDGV sportiness/M sporting/Y sportive/Y sportscast/MRZGS sportscaster/M sportsman/M sportsmanlike/U sportsmanship/M sportsmen sportspeople sportsperson sportswear/M sportswoman/M sportswomen sportswriter/SM sporty/TPR spot/CMS spotless/PY spotlessness/M spotlight/GSMD spotlit spotted spotter/MS spottily spottiness/M spotting spotty/TPR spousal/MS spouse/SM spout/SMDG sprain/GSMD sprang sprat/SM sprawl/GSMD spray's spray/ASDG sprayer/MS spread/ZGBSMR spreadeagled spreader/M spreadsheet/MS spree/DSM spreeing sprig/SM sprigged sprightliness/M sprightly/RTP spring/GSM springboard/MS springbok/MS springily springiness/M springlike springtime/M springy/RPT sprinkle/DRSJMZG sprinkler/M sprinkling/M sprint/ZGSMDR sprinter/M sprite/SM spritz/ZGMDRS spritzer/M sprocket/MS sprog/S sprout/GSMD spruce/DRSPMYTG spruceness/M sprung spry/RYT spryness/M spud/MS spume/DSMG spumoni/M spumy spun spunk/SM spunky/TR spur/MS spurge/M spurious/PY spuriousness/M spurn/SDG spurred spurring spurt/SMDG sputa sputnik/MS sputter/MDGS sputum/M spy/GDSM spyglass/MS spymaster/S spyware/M sq sqq squab/SM squabble/MZGDRS squabbler/M squad/SM squadron/MS squalid/PTRY squalidness/M squall/SGMD squally squalor/M squamous squander/GDS square/PDRSMYTG squareness/M squarish squash/GMDS squashy/TR squat/SMP squatness/M squatted squatter/MS squattest squatting squaw/SM squawk/SZGMDR squawker/M squeak/SZGMDR squeaker/M squeakily squeakiness/M squeaky/TRP squeal/SZGMDR squealer/M squeamish/PY squeamishness/M squeegee/MDS squeegeeing squeeze/BMZGDRS squeezebox/S squeezer/M squelch/GMDS squelchy squib/SM squid/SM squidgy squiffy squiggle/DSMG squiggly squint/STGMDR squire/DSMG squirm/SGMD squirmy/RT squirrel/SGMD squirt/SGMD squish/GMDS squishy/RT sriracha ssh st stab/MYS stabbed stabber/MS stabbing/MS stability/IM stabilization/CM stabilize/CDSG stabilizer/MS stable/DRSMTG stableman/M stablemate/S stablemen stably/U staccato/MS stack/SMDG stadium/MS staff's staff/ASDG staffer/MS staffing/M stag/MDGSJ stage/SM stagecoach/MS stagecraft/M stagehand/MS stagestruck stagflation/M stagger/MDGS staggering/Y staging/M stagnancy/M stagnant/Y stagnate/DSGN stagnation/M stagy/RT staid/PRYT staidness/M stain/SMDG stained/U stainless/M stair/SM staircase/MS stairway/MS stairwell/SM stake/DSMG stakeholder/MS stakeout/SM stalactite/MS stalagmite/MS stale/DRSTGP stalemate/DSMG staleness/M stalk/SMDRJZG stalker/M stalking/M stall's stall/ISDG stallholder/S stallion/MS stalwart/MYS stamen/SM stamina/M stammer/ZGMDRS stammerer/M stammering/Y stamp/SMDRZG stampede/MGDS stamper/M stance/ISM stanch/TGDRS stanchion/SM stand/SMRJZG standalone standard/MS standardization/M standardize/DSG standby/M standbys standee/MS stander/M standing/M standoff/MS standoffish standout/MS standpipe/SM standpoint/MS standstill/MS stank stanza/SM staph/M staphylococcal staphylococci staphylococcus/M staple/DRSMZG stapler/M star/MDRZGS starboard/M starburst/S starch/GMDS starchily starchiness/M starchy/PTR stardom/M stardust/M stare/SM starer/M starfish/MS starfruit stargaze/DRSZG stargazer/M stark/RYPZT starkness/M starless starlet/MS starlight/M starling/SM starlit starred starring starry/TR starstruck start/ASMDG starter/MS startle/GDS startling/Y startup/MS starvation/M starve/DSJG starveling/MS stash/MDSG stasis stat/MS state/DRSMYGNLX statecraft/M stated/U statehood/M statehouse/MS stateless/P statelessness/M stateliness/M stately/PRT statement/AMS statemented statementing stateroom/MS stateside statesman/M statesmanlike statesmanship/M statesmen stateswoman/M stateswomen statewide static/SM statically station/MDRZG stationary stationer/M stationery/M stationmaster/S statistic/MS statistical/Y statistician/SM statuary/M statue/SM statuesque statuette/MS stature/MS status/MS statute/MS statutorily statutory staunch/PDRSYTG staunchness/M stave/DSMG stay/MDRZGS std stdio stead/SM steadfast/YP steadfastness/M steadily/U steadiness/UM steady/TGPDRSM steak/SM steakhouse/SM steal/SMHG stealth/M stealthily stealthiness/M stealthy/TPR steam/SMDRZG steamboat/MS steamer/M steamfitter/SM steamfitting/M steaminess/M steampunk steamroll/ZGDRS steamroller/MDG steamship/MS steamy/TPR steed/SM steel/SMDG steeliness/M steelmaker/S steelworker/SM steelworks/M steely/PTR steelyard/SM steep/SMDNRYPXTG steepen/GD steeple/MS steeplechase/MS steeplejack/SM steepness/M steer/SMDBG steerage/M steering/M steersman/M steersmen stegosauri stegosaurus/MS stein/SM stellar stem/MS stemless stemmed stemming stemware/M stench/MS stencil/GMDS steno/SM stenographer/SM stenographic stenography/M stenosis stent/S stentorian step/IMS stepbrother/SM stepchild/M stepchildren/M stepdad/MS stepdaughter/SM stepfather/SM stepladder/MS stepmom/MS stepmother/SM stepparent/SM steppe/DRSMZG stepper/M steppingstone/SM stepsister/MS stepson/MS stereo/SM stereophonic stereoscope/MS stereoscopic stereotype/DSMG stereotypical sterile sterility/M sterilization/SM sterilize/DRSZG sterilizer/M sterling/M stern/SMRYPT sternness/M sternum/MS steroid/MS steroidal stertorous stet/S stethoscope/MS stetson/MS stetted stetting stevedore/SM stew/MDGS steward/GMDS stewardess/MS stewardship/M stick/SMRZG sticker/M stickily stickiness/M stickleback/SM stickler/MS stickpin/MS stickup/MS sticky/PTRSM stiff/SMDNRYPXTG stiffen/ZGDR stiffener/M stiffening/M stiffness/M stifle/DSJG stifling/Y stigma/SM stigmata stigmatic stigmatization/M stigmatize/GDS stile/SM stiletto/SM still's still/ITGSD stillbirth/M stillbirths stillborn stiller stillness/M stilt/SMD stilted/Y stimulant/SM stimulate/DSGNV stimulation/M stimuli stimulus/M sting/ZGSMR stinger/M stingily stinginess/M stingray/SM stingy/RTP stink/ZGSMR stinkbug/SM stinker/M stinky/RT stint/GSMD stipend/SM stipendiary/S stipple/DSMG stippling/M stipulate/XDSGN stipulation/M stir/MS stirred stirrer/SM stirring/SY stirrup/SM stitch's stitch/ADSG stitchery/M stitching/M stoat/SM stochastic stock's stock/AGSD stockade/DSMG stockbreeder/MS stockbroker/SM stockbroking/M stockholder/SM stockily stockiness/M stockinette/M stocking/SM stockist/S stockpile/MGDS stockpot/SM stockroom/MS stocktaking/M stocky/RTP stockyard/MS stodge stodgily stodginess/M stodgy/RTP stogie/MS stoic/SM stoical/Y stoicism/M stoke/DRSZG stoker/M stole/SM stolen stolid/RYTP stolidity/M stolidness/M stolon/MS stomach/MDRZG stomachache/SM stomacher/M stomachs stomp/GSMD stone/DRSMZG stonemason/MS stoner/M stonewall/GSD stoneware/M stonewashed stonework/M stonily stoniness/M stonkered stonking stony/TRP stood stooge/MS stool/SM stoop/GSMD stop's stop/US stopcock/SM stopgap/SM stoplight/MS stopover/MS stoppable/U stoppage/MS stopped/U stopper/GSMD stopping/U stopple/DSMG stopwatch/MS storage/M store's store/ADSG storefront/MS storehouse/MS storekeeper/SM storeroom/SM stork/SM storm/GSMD stormily storminess/M stormy/RPT story/DSM storyboard/MS storybook/SM storyteller/MS storytelling/M stoup/SM stout/TSMRYP stouthearted stoutness/M stove/SM stovepipe/SM stow/DGS stowage/M stowaway/MS straddle/DRSMZG straddler/M strafe/MGDS straggle/DRSZG straggler/M straggly/TR straight/SPXTMNRY straightaway/SM straightedge/SM straighten/ZGDR straightener/M straightforward/YPS straightforwardness/M straightness/M straightway strain's strain/FADSG strainer/ASM strait/MNSX straiten/GD straitjacket/SGMD straitlaced strand/MDSG strange/PRYZT strangeness/M stranger/M strangle/ZGDRS stranglehold/SM strangler/M strangulate/GNDS strangulation/M strap's strap/US strapless/MS strapped/U strapping/M strata stratagem/SM strategic/S strategical/Y strategics/M strategist/SM strategy/SM strati stratification/M stratify/DSGN stratosphere/SM stratospheric stratum/M stratus/M straw/GSMD strawberry/SM stray/GSMD streak/MDRSZG streaker/M streaky/TR stream/MDRSZG streamer/M streamline/DSG street/MS streetcar/MS streetlamp/S streetlight/SM streetwalker/SM streetwise strength/M strengthen/AGDS strengthener/MS strengths strenuous/PY strenuousness/M strep/M streptococcal streptococci streptococcus/M streptomycin/M stress/MDSG stressed/U stressful stretch/BZGMDRS stretcher/MDG stretchmarks stretchy/TR strew/GSDH strewn stria/M striae striated striation/MS stricken strict/RYPT strictness/M stricture/SM stridden stride/MGS stridency/M strident/Y strife/M strike/MZGRSJ strikebound strikebreaker/SM strikebreaking strikeout/MS striker/M striking/Y string/MDRSZG stringency/M stringent/Y stringer/M stringiness/M stringy/PTR strip/GSMD stripe/MS stripey stripling/MS stripped stripper/MS stripping striptease/MZGDRS stripteaser/M stripy strive/GS striven strobe/MS stroboscope/MS stroboscopic strode stroke/MGDS stroll/MDRSZG stroller/M strong/RYT strongbox/MS stronghold/MS strongman/M strongmen strongroom/S strontium/M strop/SM strophe/SM strophic stropped stroppily stropping stroppy/TRP strove struck structural/Y structuralism structuralist/S structure's structure/AGDS structured/U strudel/SM struggle/MGDS strum/SM strummed strumming strumpet/MS strung/UA strut/SM strutted strutting strychnine/M stub/MS stubbed stubbing stubble/M stubbly stubborn/RYPT stubbornness/M stubby/RT stucco/MDG stuccoes stuck/U stud/MYS studbook/MS studded studding/M student/SM studentship/S studied/U studiedly studio/MS studious/PY studiousness/M studly/RT study's study/AGDS stuff/GSMD stuffily stuffiness/M stuffing/M stuffy/RPT stultification/M stultify/DSNG stumble/DRSMZG stumbler/M stump/GSMD stumpy/TR stun/S stung stunk stunned stunner/S stunning/Y stunt/GSMD stuntman stuntmen stupefaction/M stupefy/DSG stupendous/Y stupid/TMRYS stupidity/SM stupor/MS sturdily sturdiness/M sturdy/TRP sturgeon/SM stutter/MDRSZG stutterer/M sty/SM style's style/ADSG styli stylish/PY stylishness/M stylist/SM stylistic/S stylistically stylize/DSG stylus/MS stymie/MDS stymieing styptic/SM suasion/EM suave/RYTP suaveness/M suavity/M sub/SM subaltern/MS subaqua subarctic subarea/MS subatomic subbasement/SM subbed subbing subbranch/MS subcategory/SM subclass subcommittee/SM subcompact/SM subconscious/PMY subconsciousness/M subcontinent/SM subcontinental subcontract/MDSG subcontractor/MS subculture/MS subcutaneous/Y subdivide/GDS subdivision/SM subdominant subdue/DSG subeditor/S subfamily/SM subfreezing subgroup/MS subhead/GJMS subheading/M subhuman/MS subj subject/GVMDS subjection/M subjective/Y subjectivity/M subjoin/GDS subjugate/GNDS subjugation/M subjunctive/SM sublease/MGDS sublet/SM subletting sublieutenant/S sublimate/GNDS sublimation/M sublime/YTGDRS subliminal/Y sublimity/M sublingual submarginal submarine/MZRS submariner/M submerge/GDS submergence/M submerse/GNDS submersible/MS submersion/M submicroscopic submission/MS submissive/PY submissiveness/M submit/AS submitted/A submitter submitting/A subnormal suborbital suborder/MS subordinate/DSMGN subordination/IM suborn/SGD subornation/M subparagraph subplot/MS subpoena/GMDS subprime subprofessional/SM subprogram/S subroutine/SM subscribe/UASDG subscriber/MS subscript/MS subscription/MS subsection/MS subsequent/Y subservience/M subservient/Y subset/SM subside/GDS subsidence/M subsidiarity subsidiary/SM subsidization/M subsidize/ZGDRS subsidizer/M subsidy/SM subsist/SDG subsistence/M subsoil/M subsonic subspace subspecies/M substance/SM substandard substantial/IY substantiate/GNDSX substantiated/U substantiation/FM substantive/SMY substation/MS substituent substitute/XMGNDS substitution/M substrata substrate/MS substratum/M substructure/SM subsume/DSG subsumption subsurface/M subsystem/SM subteen/SM subtenancy/M subtenant/SM subtend/SDG subterfuge/SM subterranean subtext/SM subtitle/DSMG subtle/TR subtlety/SM subtly subtopic/SM subtotal/SGMD subtract/GSD subtraction/SM subtrahend/SM subtropic/S subtropical subtropics/M suburb/MS suburban/SM suburbanite/SM suburbia/M subvention/SM subversion/M subversive/SPMY subversiveness/M subvert/SDG subway/MS subzero succeed/GDS success/VMS successful/UY succession/SM successive/Y successor/SM succinct/RYTP succinctness/M succor/SGMD succotash/M succubi succubus succulence/M succulency/M succulent/SM succumb/GDS such suchlike suck/MDRZGS sucker/GMD suckle/DSJG suckling/M sucrose/M suction/SMDG sudden/PY suddenness/M suds/M sudsy/TR sue/DSG suede/M suet/M suety suffer/DRZGSJ sufferance/M sufferer/M suffering/M suffice/DSG sufficiency/IM sufficient/IY suffix/MDSG suffixation/M suffocate/GNDS suffocation/M suffragan/MS suffrage/M suffragette/SM suffragist/MS suffuse/DSGN suffusion/M sugar/GSMD sugarcane/M sugarcoat/GDS sugarless sugarplum/MS sugary/RT suggest/GVSDR suggestibility/M suggestible suggestion/SM suggestive/YP suggestiveness/M suicidal suicide/SM suit/BMDGS suitability/UM suitableness/M suitably/U suitcase/SM suite/SM suited/U suiting/M suitor/MS sukiyaki/M sulfa/M sulfate/SM sulfide/SM sulfonamides sulfur/MDSG sulfuric sulfurous sulk/MDGS sulkily sulkiness/M sulky/TRSMP sullen/RYPT sullenness/M sullied/U sully/GDS sultan/MS sultana/SM sultanate/MS sultrily sultriness/M sultry/RPT sum/SM sumac/M summarily summarize/GDS summary/SM summat summation/FMS summed summer/MDSG summerhouse/SM summertime/M summery summing summit/MS summitry/M summon/DRSZG summoner/M summons/GMDS sumo/M sump/MS sumptuous/PY sumptuousness/M sun/SM sunbath/ZGMDRS sunbathe sunbather/M sunbathing/M sunbaths sunbeam/SM sunbed/S sunbelt sunblock/MS sunbonnet/SM sunburn/SGMD sunburst/MS sundae/MS sundeck/S sunder/DSG sundial/SM sundown/SM sundress/S sundries/M sundry/S sunfish/MS sunflower/MS sung/U sunglasses/M sunhat/S sunk/N sunlamp/SM sunless sunlight/M sunlit sunned sunniness/M sunning sunny/TRP sunrise/SM sunroof/SM sunscreen/MS sunset/MS sunshade/MS sunshine/M sunshiny sunspot/SM sunstroke/M suntan/MS suntanned suntanning suntrap/S sunup/M sup/SZMR super/M superabundance/MS superabundant superannuate/GNDS superannuation/M superb/RYT supercargo/M supercargoes supercharge/ZGDRS supercharger/M supercilious/PY superciliousness/M supercity/SM supercomputer/MS superconducting superconductive superconductivity/M superconductor/SM superego/MS supererogation/M supererogatory superficial/Y superficiality/M superfine superfluity/M superfluous/YP superfluousness/M superglue supergrass/S superhero/MS superheroes superhighway/SM superhuman superimpose/GDS superimposition/M superintend/DSG superintendence/M superintendency/M superintendent/SM superior/MS superiority/M superlative/SMY superman/M supermarket/SM supermen supermodel/SM supermom/MS supernal supernatural/SY supernova/MS supernovae supernumerary/SM superpose/GDS superposition/M superpower/SM supersaturate/GNDS supersaturation/M superscribe/GDS superscript/MS superscription/M supersede/GDS supersize/GDS supersonic superstar/MS superstate/S superstition/MS superstitious/Y superstore/MS superstructure/MS supertanker/MS superuser/S supervene/GDS supervention/M supervise/XGNDS supervised/U supervision/M supervisor/MS supervisory superwoman/M superwomen supine/Y supp/DRZG supper/M suppertime suppl supplant/SDG supple/TLPR supplement/MDGS supplemental supplementary supplementation/M suppleness/M suppliant/SM supplicant/MS supplicate/GDS supplication/M supplier/M supply/ZGDRSMXN support/MDRSBZGV supportable/UI supported/U supporter/M suppose/GDS supposed/Y supposition/MS suppository/SM suppress/GDS suppressant/MS suppressible suppression/M suppressor/SM suppurate/DSGN suppuration/M supra supranational supremacist/MS supremacy/M supreme/Y supremo/S supt surcease/DSMG surcharge/DSMG surcingle/SM sure/PYTR surefire surefooted sureness/M surety/SM surf/MDRZGS surface's surface/AGDS surfboard/MDSG surfeit/MDSG surfer/M surfing/M surge/DSMG surgeon/MS surgery/SM surgical/Y surliness/M surly/PTR surmise/MGDS surmount/DGSB surmountable/I surname/MS surpass/GDS surpassed/U surplice/MS surplus/MS surplussed surplussing surprise/DSMGJ surprising/UY surreal surrealism/M surrealist/SM surrealistic surrealistically surrender/MDSG surreptitious/PY surreptitiousness/M surrey/MS surrogacy/M surrogate/SM surround/GSDJ surrounding/M surroundings/M surtax/MDSG surtitle/S surveillance/M survey's survey/ADGS surveying/M surveyor/SM survival/SM survivalist/SM survive/DSGB survivor/SM susceptibility/SM susceptible/I sushi/M suspect/SMDG suspected/U suspend/SDRZG suspender/M suspense/XMN suspenseful suspension/M suspicion/SM suspicious/Y suss/DSG sustain/SDBG sustainability sustainable/U sustenance/M sutler/MS suttee suture/MGDS suzerain/MS suzerainty/M svelte/TR swab/MS swabbed swabbing swaddle/DSG swag/MS swagged swagger/SMDRG swagging swain/SM swallow/GSMD swallowtail/MS swam swami/SM swamp/GSMD swampland/M swampy/RT swan/MS swank/TGSMDR swankily swankiness/M swanky/RPT swanned swanning swansong/S swap/MS swapped swapping sward/SM swarm/GSMD swarthy/TR swash/GMDS swashbuckler/SM swashbuckling/M swastika/SM swat/MS swatch/MS swath/GMDS swathe/M swaths swatted swatter/SMDG swatting sway/MDGS swayback/MD swayed/U swear/ZGSR swearer/M swearword/MS sweat/ZGSMDR sweatband/MS sweater/M sweatpants/M sweats/M sweatshirt/SM sweatshop/MS sweatsuit/S sweaty/RT swede/SM sweep/ZGSMRJ sweeper/M sweeping/MY sweepings/M sweepstakes/M sweet/XTSMNRYP sweetbread/SM sweetbrier/SM sweetcorn sweetened/U sweetener/MS sweetening/M sweetheart/SM sweetie/SM sweetish sweetmeat/MS sweetness/M swell/TGSMDRJ swellhead/MDS swelling/M swelter/SGMD swept sweptback swerve/MGDS swerving/U swift/PTSMRY swiftness/M swig/MS swigged swigging swill/GSMD swim/MS swimmer/SM swimming/MY swimsuit/SM swimwear swindle/DRSMZG swindler/M swine/SM swineherd/SM swing/ZGSMR swingeing swinger/M swinish swipe/DSMG swirl/GSMD swirly swish/TGMDRS switch/MDRSZGB switchback/MS switchblade/SM switchboard/SM switcher/M swivel/MDGS swiz swizz swizzle/DSG swollen swoon/SGMD swoop/SGMD swoosh/MDSG sword/SM swordfish/MS swordplay/M swordsman/M swordsmanship/M swordsmen swore sworn swot/S swotted swotting swum swung sybarite/SM sybaritic sycamore/MS sycophancy/M sycophant/SM sycophantic syllabic syllabicate/GNDS syllabication/M syllabification/M syllabify/DSNG syllable/MS syllabub/S syllabus/MS syllogism/MS syllogistic sylph/M sylphic sylphlike sylphs sylvan symbioses symbiosis/M symbiotic symbiotically symbol/MS symbolic symbolical/Y symbolism/M symbolization/M symbolize/DSG symmetric symmetrical/Y symmetry/SM sympathetic/U sympathetically/U sympathies/M sympathize/ZGDRS sympathizer/M sympathy/SM symphonic symphony/SM symposium/MS symptom/MS symptomatic symptomatically syn/H synagogal synagogue/SM synapse/MS synaptic sync/MDSG synchronicity synchronization/SM synchronize/GDS synchronous/Y syncopate/DSGN syncopation/M syncope/M syndicalism syndicalist/S syndicate/DSMGN syndication/M syndrome/SM synergism/M synergistic synergy/SM synfuel/MS synod/SM synonym/SM synonymous synonymy/M synopses synopsis/M synoptic synovial syntactic syntactical/Y syntax/M syntheses synthesis/M synthesize/ZGDRS synthesizer/M synthetic/SM synthetically synths syphilis/M syphilitic/SM syringe/DSMG syrup/SM syrupy sysadmin/S sysop/S system/SM systematic/U systematical/Y systematization/M systematize/GDS systemic/MS systemically systole/SM systolic t/SDNXGBJ ta tab/SM tabbed tabbing tabbouleh/M tabby/SM tabernacle/SM tabla/MS table/MGDS tableau/M tableaux tablecloth/M tablecloths tableland/SM tablespoon/SM tablespoonful/SM tablet/SM tabletop/MS tableware/M tabloid/SM taboo/MDSG tabor/MS tabular tabulate/DSGNX tabulation/M tabulator/SM tachograph tachographs tachometer/SM tachycardia/M tachyon tacit/PY tacitness/M taciturn/Y taciturnity/M tack/ZGMDRS tacker/M tackiness/M tackle/DRSMZG tackler/M tacky/RTP taco/MS tact/FM tactful/YP tactfulness/M tactic/SM tactical/Y tactician/MS tactile tactility/M tactless/PY tactlessness/M tad/SM tadpole/MS taffeta/M taffrail/SM taffy/SM tag/SM tagged tagger/SM tagging tagliatelle tagline/MS taiga/MS tail/ACSDMG tailback/MS tailboard/S tailbone/S tailcoat/MS tailgate/MZGDRS tailgater/M tailless taillight/MS tailor/SGMD tailoring/M tailpiece/S tailpipe/SM tailspin/SM tailwind/SM taint/MDSG tainted/U take/AIMS takeaway/S taken/A takeoff/MS takeout/MS takeover/SM taker/MS taking/SM takings/M talc/M talcum/M tale/MS talebearer/MS talent/SMD talented/U tali talisman/MS talk/ZGMDRS talkative/PY talkativeness/M talker/M talkie/RSMT talky tall/TRP tallboy/MS tallier/M tallish tallness/M tallow/M tallowy tally/DRSMZG tallyho/MDGS talon/MS talus/MS tam/SM tamale/SM tamarack/MS tamarind/MS tambourine/MS tame/BYZTGDRSP tamed/U tameness/M tamer/M tamoxifen tamp/ZGDRS tamper/ZGDR tamperer/M tampon/SM tan/SM tanager/MS tanbark/M tandem/SM tandoori/M tang/MS tangelo/MS tangent/MS tangential/Y tangerine/MS tangibility/IM tangible/IMS tangibleness/M tangibly/I tangle's tangle/UDSG tango/MDSG tangy/RT tank/ZGMDRS tankard/MS tanker/M tankful/MS tanned/U tanner/SM tannery/SM tannest tannin/M tanning/M tansy/M tantalization/M tantalize/ZGDRS tantalizer/M tantalizing/Y tantalum/M tantamount tantra/M tantrum/SM tap/SZGMDR tapas tape/MS tapeline/MS taper/MDG tapestry/SM tapeworm/MS tapioca/M tapir/MS tapped/U tapper/MS tappet/MS tapping taproom/SM taproot/SM tar/SGMD taramasalata tarantella/MS tarantula/SM tarball/S tardily tardiness/M tardy/TPR tare/MS target/MDGS tariff/MS tarmac/MS tarmacadam tarmacked tarmacking tarn/MS tarnish/GMDS tarnished/U taro/MS tarot/MS tarp/MS tarpaulin/MS tarpon/MS tarragon/SM tarred tarring tarry/TGDRS tarsal/MS tarsi tarsus/M tart/PTGMDRYS tartan/MS tartar/MS tartaric tartness/M tarty/T taser/GMDS task/GMDS taskmaster/MS taskmistress/MS tassel/MDSG taste/JMZGDRS tasted/U tasteful/EPY tastefulness/EM tasteless/PY tastelessness/M taster/M tastily tastiness/M tasting/M tasty/TRP tat/SZR tatami/MS tater/M tatted tatter/MDSG tatterdemalion/MS tattie tatting/M tattle/MZGDRS tattler/M tattletale/MS tattoo/MDRSZG tattooer/M tattooist/SM tatty/TRS tau/SM taught/UA taunt/ZGMDRS taunter/M taunting/Y taupe/M taut/PXTNRY tauten/DG tautness/M tautological/Y tautologous tautology/SM tavern/MS tawdrily tawdriness/M tawdry/RTP tawny/TRM tax/BZGMDRS taxa taxation/M taxer/M taxi/GMDS taxicab/SM taxidermist/SM taxidermy/M taximeter/MS taxiway/S taxman taxmen taxon taxonomic taxonomist/MS taxonomy/SM taxpayer/MS taxpaying tbs tbsp tea/SM teabag/S teacake/SM teach/ZGRSBJ teachable/U teacher/M teaching/M teacup/MS teacupful/MS teak/MS teakettle/SM teal/MS tealight/MS team/GMDS teammate/MS teamster/MS teamwork/M teapot/MS tear/GMDS tearaway/S teardrop/SM tearful/Y teargas/MS teargassed teargassing tearjerker/MS tearoom/SM teary/TR tease/MZGDRS teasel/MS teaser/M teasing/Y teaspoon/SM teaspoonful/SM teat/MS teatime/S tech/M techie/S technetium/M technical/Y technicality/SM technician/SM technicolor technique/SM techno technobabble technocracy/SM technocrat/MS technocratic technological/Y technologist/MS technology/SM technophobe/S techs tectonic/S tectonics/M ted/S teddy/S tedious/PY tediousness/M tedium/M tee/DSMH teeing teem/GDS teen/MS teenage/RZ teenager/M teeny/TR teenybopper/MS teeter/MDSG teethe/GDS teething/M teetotal/RZ teetotaler/M teetotalism/M tektite/SM tel telecast/SZGMR telecaster/M telecommunication/MS telecommunications/M telecommute/ZGDRS telecommuter/M telecommuting/M teleconference/MGDS teleconferencing/M telegenic telegram/MS telegraph/MDRZG telegrapher/M telegraphese telegraphic telegraphically telegraphist/SM telegraphs telegraphy/M telekinesis/M telekinetic telemarketer/SM telemarketing/M telemeter/SM telemetry/SM teleological teleology telepathic telepathically telepathy/M telephone/DRSMZG telephoner/M telephonic telephonist/S telephony/M telephoto/SM telephotography/M teleplay/MS teleport teleportation teleprinter/MS teleprocessing/M teleprompter/SM telesales telescope/DSMG telescopic telescopically teletext/MS telethon/MS teletype/S teletypewriter/MS televangelism/M televangelist/MS televise/XGNDS television/M teleworker/S teleworking telex/MDSG tell/AGS teller/SM telling/Y telltale/SM tellurium/M telly/SM telnet temblor/MS temerity/M temp/MDRZTGS temper/MDG tempera/LSM temperament/MS temperamental/Y temperance/IM temperate/IY temperateness/M temperature/SM tempest/SM tempestuous/YP tempestuousness/M template's template/S temple/SM tempo/SM temporal/Y temporarily temporariness/M temporary/FSM temporize/ZGDRS temporizer/M tempt/SDRZG temptation/MS tempter/M tempting/Y temptress/MS tempura/M ten/BMH tenability/M tenable/U tenably tenacious/YP tenaciousness/M tenacity/M tenancy/SM tenant/SMDG tenanted/U tenantry/M tench tend/IEFDGS tended/U tendency/SM tendentious/YP tendentiousness/M tender/SMDRYTGP tenderfoot/MS tenderhearted/PY tenderheartedness/M tenderize/ZGDRS tenderizer/M tenderloin/SM tenderness/M tendinitis/M tendon/SM tendril/SM tenement/SM tenet/SM tenfold tenner/S tennis/M tenon/SMDG tenor/SM tenpin/SM tenpins/M tense/DRSMYTGNXP tenseness/M tensile tension/ESM tensity/IM tensor/S tent/DGSM tentacle/DSM tentative/PY tentativeness/M tenterhook/MS tenth/MY tenths tenuity/M tenuous/PY tenuousness/M tenure/DSMG tepee/SM tepid/YP tepidity/M tepidness/M tequila/SM terabit/SM terabyte/MS terahertz/M terapixel/MS terbium/M tercentenary/SM tercentennial/SM teriyaki term/MDYGS termagant/MS terminable/IC terminal/MYS terminate/DSGNX termination/CSM terminator/S termini terminological/Y terminology/SM terminus/M termite/SM tern/IMS ternary/SM terr terrace/DSMG terracotta/M terrain/SM terrapin/MS terrarium/SM terrazzo/MS terrestrial/SMY terrible/P terribleness/M terribly terrier/M terrific terrifically terrify/GDS terrifying/Y terrine/S territorial/MS territoriality territory/SM terror/SM terrorism/M terrorist/SM terrorize/DSG terry/RMZ terrycloth/M terse/RYTP terseness/M tertiary tessellate/DSXGN tessellation/M test's/AFK test/AKFCDGS testable/CF testament/MS testamentary testate/S testator/MS testatrices testatrix/M tested/U tester/KSM testes testicle/MS testicular testifier/M testify/ZGDRS testily testimonial/MS testimony/SM testiness/M testings testis/M testosterone/M testy/PRT tetanus/M tetchily tetchy/PRT tether/SMDG tetra/SM tetracycline/M tetrahedral tetrahedron/MS tetrameter/SM text/FMS textbook/SM texted textile/MS texting textual/FY textural texture/MGDS thalami thalamus/M thalidomide/M thallium/M than thane/SM thank/SDG thankful/YP thankfulness/M thankless/PY thanklessness/M thanksgiving/SM that/M thatch/MDRSZG thatcher/M thatching/M thaw/MDGS the/JG theater/SM theatergoer/SM theatrical/YS theatricality/M theatricals/M theatrics/M thee/S theft/SM their/S theism/M theist/SM theistic them thematic thematically theme/DSM themselves then/M thence thenceforth thenceforward theocracy/SM theocratic theodolite/S theologian/SM theological/Y theology/SM theorem/MS theoretic theoretical/Y theoretician/SM theorist/SM theorize/DSG theory/SM theosophic theosophical theosophist/SM theosophy/M therapeutic/S therapeutically therapeutics/M therapist/SM therapy/SM there/M thereabout/S thereafter thereat thereby therefor therefore therefrom therein theremin/SM thereof thereon thereto theretofore thereunto thereupon therewith therm/SM thermal/MYS thermionic thermodynamic/S thermodynamics/M thermometer/MS thermometric thermonuclear thermoplastic/SM thermos/MS thermostat/MS thermostatic thermostatically thesauri thesaurus/MS these/S thesis/M thespian/SM theta/SM thew/MS they they'd they'll they're they've thiamine/M thick/PMNRYXT thicken/DRJZG thickener/M thickening/M thicket/MS thickheaded/M thickness/MS thicko/S thickset thief/M thieve/DSG thievery/M thieving/M thievish thigh/M thighbone/MS thighs thimble/MS thimbleful/SM thin/YSP thine thing/M thingamabob/SM thingamajig/SM thingumabob/S thingummy/S thingy/S think/SRBZG thinkable/U thinker/M thinking's thinned thinner/MS thinness/M thinnest thinning third/SMY thirst/SGMD thirstily thirstiness/M thirsty/TPR thirteen/SMH thirteenth/M thirteenths thirtieth/M thirtieths thirty/HSM this thistle/MS thistledown/M thither tho thole/SM thong/SM thoracic thorax/MS thorium/M thorn/SM thorniness/M thorny/PRT thorough/RYPT thoroughbred/MS thoroughfare/MS thoroughgoing thoroughness/M those thou/MS though thought/SM thoughtful/YP thoughtfulness/M thoughtless/PY thoughtlessness/M thousand/MHS thousandfold thousandth/M thousandths thrall/SMDG thralldom/M thrash/JMDRSZG thrasher/M thrashing/M thread/SMDRZG threadbare threader/M threadlike thready/TR threat/SMNX threaten/DG threatening/Y three/SM threefold threepence/M threescore/MS threesome/SM threnody/SM thresh/MDRSZG thresher/M threshold/SM threw thrice thrift/SM thriftily thriftiness/M thriftless thrifty/PTR thrill/SMDRZG thriller/M thrilling/Y thrive/DSG throat/SM throatily throatiness/M throaty/RTP throb/SM throbbed throbbing throe/SM thrombi thrombolytic thromboses thrombosis/M thrombotic thrombus/M throne's throne/S throng/GSMD throttle/DRSMZG throttler/M through throughout throughput/M throw/SMRZG throwaway/SM throwback/SM thrower/M thrown thru thrum/SM thrummed thrumming thrush/MS thrust/GSM thruway/MS thud/MS thudded thudding thug/MS thuggery/M thuggish thulium/M thumb/SMDG thumbnail/SM thumbprint/SM thumbscrew/SM thumbtack/SM thump/SMDG thumping/M thunder/ZGMDRS thunderbolt/SM thunderclap/SM thundercloud/MS thunderer/M thunderhead/SM thunderous/Y thundershower/SM thunderstorm/SM thunderstruck thundery thunk/S thus thwack/ZGSMDR thwacker/M thwart/GSMD thy thyme/M thymine/M thymus/MS thyroid/MS thyroidal thyself ti/MRZ tiara/SM tibia/M tibiae tibial tic/SM tick/MDRZGS ticker/M ticket/GSMD ticking/M tickle/DRSMZG tickler/M ticklish/YP ticklishness/M ticktacktoe/M ticktock/MS tidal/Y tidbit/SM tiddler/S tiddly tiddlywink/S tiddlywinks/M tide/MGJDS tideland/SM tidemark/S tidewater/MS tideway/MS tidily/U tidiness/UM tidings/M tidy/DRSMTGP tie's tie/AUSD tieback/MS tiebreak/RSZ tiebreaker/M tiepin/S tier/MD tiff/MDGS tiger/SM tigerish tight/SNRYPXT tighten/ZGDR tightener/M tightfisted tightness/M tightrope/MS tights/M tightwad/MS tigress/MS til tilde/SM tile/MZGDRS tiler/M tiling/M till's till/EDRZGS tillable tillage/M tiller/EM tilt/MDGS timber/SMDG timberland/M timberline/MS timbre/SM timbrel/SM time/MYZGJDRS timekeeper/MS timekeeping/M timeless/PY timelessness/M timeline/MS timeliness/UM timely/UPRT timeout/SM timepiece/MS timer/M timescale/S timeserver/SM timeserving/M timeshare/S timetable/DSMG timeworn timezone timid/RYTP timidity/M timidness/M timing/M timorous/PY timorousness/M timothy/M timpani/M timpanist/SM tin/SM tincture/MGDS tinder/M tinderbox/MS tine/MS tinfoil/M ting/MDYG tinge/SM tingeing tingle/DSMGJ tingling/M tininess/M tinker/ZGSMDR tinkerer/M tinkle/DSMG tinned tinniness/M tinning tinnitus/M tinny/PRT tinplate/M tinpot tinsel/GSMD tinsmith/M tinsmiths tint/MDGS tintinnabulation/MS tintype/MS tinware/M tiny/RTP tip/SM tipped tipper/SM tippet/SM tippex/GDS tipping tipple/DRSMZG tippler/M tipsily tipsiness/M tipster/MS tipsy/RPT tiptoe/DSM tiptoeing tiptop/SM tirade/SM tiramisu/MS tire's tire/AGDS tired/PRYT tiredness/M tireless/YP tirelessness/M tiresome/PY tiresomeness/M tissue/SM tit/SM titan/SM titanic titanium/M titch/S titchy tithe/DRSMZG tither/M titian/M titillate/DSGN titillating/Y titillation/M titivate/DSGN titivation/M title/DSMG titled/U titleholder/MS titlist/MS titmice titmouse/M titter/SGMD tittle/SM titty/S titular tizz tizzy/SM tn tnpk to/IU toad/MS toadstool/MS toady/DSMG toadyism/M toast/SMDRZG toaster/M toastmaster/SM toastmistress/MS toasty/TRS tobacco/MS tobacconist/SM toboggan/ZGSMDR tobogganer/M tobogganing/M toccata/S tocsin/SM today/M toddle/DRSMZG toddler/M toddy/SM toe/DSM toecap/SM toehold/MS toeing toenail/MS toerag/S toff/S toffee/SM tofu/M tog/SM toga/MDS together/P togetherness/M togged togging toggle/DSMG togs/M toil/MDRZGS toiler/M toilet/MDGS toiletry/SM toilette/M toilsome toke/MGDS token/SM tokenism/M told/AU tole/M tolerable/I tolerably/I tolerance/IM tolerances tolerant/IY tolerate/GNDS toleration/M toll/MDGS tollbooth/M tollbooths tollgate/SM tollway/SM toluene/M tom/SM tomahawk/SGMD tomato/M tomatoes tomb/MDGS tombola/S tomboy/MS tomboyish tombstone/MS tomcat/MS tome/MS tomfoolery/SM tomographic tomography/M tomorrow/MS tomtit/MS ton/SM tonal/Y tonality/SM tone's tone/IZGDRS tonearm/SM toneless/Y toner/IM tong/MDGS tongue/MGDS tongueless tonic/SM tonight/M tonnage/SM tonne/SM tonsil/MS tonsillectomy/SM tonsillitis/M tonsorial tonsure/DSMG tony/RT too took/A tool's tool/ADGS toolbar/M toolbox/MS toolkit toolmaker/MS toot/MDRZGS tooter/M tooth/MD toothache/MS toothbrush/MS toothily toothless toothpaste/SM toothpick/SM toothsome toothy/RT tootle/GDS tootsie/S top/SM topaz/MS topcoat/SM topdressing/SM topee/S topflight topi topiary/M topic/SM topical/Y topicality/M topknot/SM topless topmast/SM topmost topnotch topographer/SM topographic topographical/Y topography/SM topological/Y topology topped topper/MS topping/SM topple/GDS topsail/SM topside/SM topsoil/M topspin/M toque/SM tor/SM torch/GMDS torchbearer/MS torchlight/M tore toreador/MS torment/SMDG tormenting/Y tormentor/MS torn tornado/M tornadoes torpedo/GMD torpedoes torpid/Y torpidity/M torpor/M torque/MGDS torrent/SM torrential torrid/YP torridity/M torridness/M torsion/M torsional torso/SM tort's tort/FEAS torte/SM tortellini/M tortilla/MS tortoise/MS tortoiseshell/SM tortoni/M tortuous/PY tortuousness/M torture/DRSMZG torturer/M torturous torus tosh toss/MDRSZG tossup/MS tot/SGMD total/GSMDY totalitarian/SM totalitarianism/M totality/SM totalizator/SM tote/MS totem/SM totemic totted totter/ZGMDRS totterer/M totting toucan/MS touch/AGMDS touchdown/SM touche/BJ touched/U touchily touchiness/M touching/Y touchline/S touchpaper/S touchscreen/MS touchstone/MS touchy/RPT tough/XTGMDNRYP toughen/ZGDR toughener/M toughie/SM toughness/M toughs toupee/MS tour/CFSGDM tourism/M tourist/MS touristic touristy tourmaline/M tournament/SM tourney/MS tourniquet/MS tousle/GDS tout/MDGS tow/SZGMDR toward/S towboat/MS towel/JGSMD towelette/SM toweling/M tower/GMD towhead/MDS towhee/MS towline/MS town/MS townee/S townhouse/MS townie/MS townsfolk/M township/MS townsman/M townsmen townspeople/M townswoman/M townswomen towpath/M towpaths towrope/SM toxemia/M toxic toxicity/SM toxicological toxicologist/SM toxicology/M toxin/SM toy/SGMD toyboy/S tr trabecula trabecular trabecule trace/JDRSMZG traceable/U tracer/M tracery/SM trachea/M tracheae tracheal tracheotomy/SM tracing/M track/ZGSMDR trackball/SM tracker/M trackless tracksuit/S tract's tract/CEKFAS tractability/IM tractable/I tractably/I traction/EFACKM tractor/FCKMS trad trade/JDRSMZG trademark/SGMD trader/M tradesman/M tradesmen tradespeople/M tradeswoman/M tradeswomen trading/M tradition/MS traditional/Y traditionalism/M traditionalist/SM traduce/DRSZG traducer/M traffic/SM trafficked trafficker/SM trafficking/M tragedian/SM tragedienne/MS tragedy/SM tragic tragically tragicomedy/SM tragicomic trail/ZGSMDR trailblazer/MS trailblazing/M trailer/M train/ZGSMDRB trained/U trainee/SM trainer/M training/M trainload/MS trainman/M trainmen trainspotter/S trainspotting traipse/DSMG trait/SM traitor/SM traitorous/Y trajectory/SM tram/MS tramcar/S tramlines trammed trammel/SGMD trammeled/U tramming tramp/ZGSMDR tramper/M trample/DRSMZG trampler/M trampoline/MGDS tramway/S trance/MS tranche/S tranquil/RYT tranquility/M tranquilize/ZGDRS tranquilizer/M trans/I transact/DGS transaction/SM transactor/MS transatlantic transceiver/SM transcend/GSD transcendence/M transcendent transcendental/Y transcendentalism/M transcendentalist/SM transcontinental transcribe/ZGDRS transcriber/M transcript/MS transcription/SM transducer/MS transect/DSG transept/MS transfer/MBS transferal/MS transference/M transferred transferring transfiguration/M transfigure/GDS transfinite transfix/DSG transform/BSZGMDR transformation/SM transformer/M transfuse/DSXGN transfusion/M transgender/S transgenic transgress/GDS transgression/SM transgressor/SM transience/M transiency/M transient/SMY transistor/SM transistorize/DSG transit/SGMD transition/GSMD transitional/Y transitive/ISMY transitiveness/M transitivity/M transitory transl translatable/U translate/DSGNBX translated/U translation/M translator/SM transliterate/DSGNX transliteration/M translocation translucence/M translucency/M translucent/Y transmigrate/GNDS transmigration/M transmissible transmission/MS transmit/S transmittable transmittal/M transmittance/M transmitted transmitter/SM transmitting transmogrification/M transmogrify/DSNG transmutation/SM transmute/BDSG transnational/MS transoceanic transom/SM transpacific transparency/SM transparent/Y transpiration/M transpire/DSG transplant/MDGS transplantation/M transpolar transponder/SM transport/BSZGMDR transportation/M transporter/M transpose/DSG transposition/MS transsexual/SM transsexualism/M transship/SL transshipment/M transshipped transshipping transubstantiation/M transverse/MYS transvestism/M transvestite/MS trap/MS trapdoor/MS trapeze/SM trapezium/SM trapezoid/SM trapezoidal trappable trapped trapper/SM trapping/S trappings/M trapshooting/M trash/GMDS trashcan/MS trashiness/M trashy/RPT trauma/MS traumatic traumatically traumatize/GDS travail/SGMD travel/MDRSZGJ traveled/U traveler/M traveling/M travelogue/MS traversal/SM traverse/DSMG travesty/GDSM trawl/ZGSMDR trawler/M tray/MS treacherous/PY treacherousness/M treachery/SM treacle/M treacly tread/AGSM treadle/DSMG treadmill/MS treas treason/BM treasonous treasure/DRSMZG treasurer/M treasury/SM treat/AGSMD treatable treated/U treatise/SM treatment/MS treaty/SM treble/MGDS tree/MDS treeing treeless treelike treeline treetop/SM trefoil/SM trek/MS trekked trekker/SM trekking trellis/GMDS trematode/MS tremble/DSMG tremendous/Y tremolo/SM tremor/MS tremulous/PY tremulousness/M trench's trench/ADSG trenchancy/M trenchant/Y trencher/MS trencherman/M trenchermen trend/GSMD trendily trendiness/M trendsetter/S trendsetting trendy/RSMPT trepidation/M trespass/MDRSZG trespasser/M tress/EMS trestle/MS trews trey/MS triad/SM triage/M trial/ASM trialed trialing triangle/SM triangular/Y triangulate/GNDS triangulation/M triathlete/S triathlon/SM tribal tribalism/M tribe/SM tribesman/M tribesmen tribeswoman/M tribeswomen tribulation/SM tribunal/SM tribune/MS tributary/SM tribute's tribute/FS trice/M tricentennial/MS triceps/MS triceratops/M trichina/M trichinae trichinosis/M trick/GSMD trickery/M trickily trickiness/M trickle/MGDS trickster/SM tricky/TRP tricolor/SM tricycle/SM trident/MS tried/U triennial/MYS trier/SM trifecta/SM trifle/MZGDRS trifler/M trifocals/M trig/M trigger/MDSG triglyceride/MS trigonometric trigonometrical trigonometry/M trike/SM trilateral/S trilby/SM trill/GSMD trillion/SMH trillionth/M trillionths trillium/M trilobite/SM trilogy/SM trim/PMYS trimaran/MS trimester/SM trimmed/U trimmer/SM trimmest trimming/SM trimmings/M trimness/M trimonthly trinitrotoluene/M trinity/SM trinket/SM trio/MS trip/MYS tripartite tripe/M triple/MGDS triplet/SM triplex/MS triplicate/MGDS tripod/MS tripodal tripos tripped tripper/SM tripping triptych/M triptychs tripwire/S trireme/SM trisect/SDG trisection/M trite/FPYT triteness/FM triter tritium/M triumph/GMD triumphal triumphalism triumphalist triumphant/Y triumphs triumvir/MS triumvirate/SM trivalent trivet/MS trivia/M trivial/Y triviality/SM trivialization/M trivialize/GDS trivium/M trochaic trochee/SM trod/AU trodden/A troglodyte/SM troika/MS troll/SGMD trolley/SM trolleybus/MS trollop/SM trombone/MS trombonist/MS tromp/SGD tron/S troop/SZGMDR trooper/M troopship/MS trope/SM trophy/SM tropic/MS tropical/Y tropics/M tropism/SM troposphere/SM trot/MS troth/M trotted trotter/SM trotting troubadour/MS trouble/DSMG troubled/U troublemaker/MS troubleshoot/DRZGS troubleshooter/M troubleshooting/M troubleshot troublesome/Y trough/M troughs trounce/DRSZG trouncer/M troupe/MZGDRS trouper/M trouser/SM trousers/M trousseau/M trousseaux trout/SM trove/SM trow/DSG trowel/MDSG troy/S truancy/M truant/GMDS truce/SM truck/SZGMDR trucker/M trucking/M truckle/MGDS truckload/SM truculence/M truculent/Y trudge/MGDS true/MTGDRS truelove/SM truffle/MS trug/S truism/MS truly/U trump/SGMD trumpery/M trumpet/ZGMDRS trumpeter/M truncate/GNDS truncation/M truncheon/SM trundle/MZGDRS trundler/M trunk/SGM truss/GMDS trust/ESGMD trustee/MS trusteeship/SM trustful/EY trustfulness/M trusting/Y trustworthiness/M trustworthy/TPR trusty/TRSM truth/UM truthful/UYP truthfulness/UM truthiness truths/U try's try/AGDS trying/Y tryout/SM tryst/SMDG tsarists tsetse/MS tsp tsunami/SM ttys tub/SZGMDR tuba/MS tubal tubby/TR tube/MS tubeless/M tuber/M tubercle/SM tubercular tuberculin/M tuberculosis/M tuberculous tuberose/M tuberous tubful/MS tubing/M tubular tubule/MS tuck/MDRSZG tucker/MDG tuft/MDRSZG tufter/M tug/SM tugboat/MS tugged tugging tuition/IM tularemia/M tulip/SM tulle/M tum/S tumble/DRSMZG tumbledown tumbler/M tumbleweed/SM tumbling/M tumbrel/SM tumescence/M tumescent tumid tumidity/M tummy/SM tumor/SM tumorous tumult/SM tumultuous/Y tun/SZGMDR tuna/MS tundra/SM tune/MS tuneful/YP tunefulness/M tuneless/Y tuner/M tuneup/SM tungsten/M tunic/SM tunnel/JSMDRZG tunneler/M tunny/SM tuple/S tuppence tuppenny tuque/SM turban/SMD turbid turbidity/M turbine/SM turbo/SM turbocharge/ZGDRS turbocharger/M turbofan/SM turbojet/SM turboprop/SM turbot/SM turbulence/M turbulent/Y turd/MS turducken/SM tureen/SM turf/MDSG turfy turgid/Y turgidity/M turkey/SM turmeric/SM turmoil/MS turn/AMDRSZG turnabout/SM turnaround/SM turnbuckle/SM turncoat/SM turner/AM turning/MS turnip/SM turnkey/MS turnoff/MS turnout/MS turnover/MS turnpike/MS turnstile/SM turntable/SM turpentine/M turpitude/M turps turquoise/SM turret/SMD turtle/SM turtledove/SM turtleneck/SMD tush/MS tusk/MDS tussle/DSMG tussock/MS tussocky tut/SM tutelage/M tutelary tutor/SMDG tutored/U tutorial/SM tutorship/M tutted tutti/SM tutting tutu/MS tux/MS tuxedo/SM twaddle/MZGDRS twaddler/M twain/M twang/SMDG twangy/RT twas twat/S tweak/SMDG twee tweed/SM tweeds/M tweedy/RT tween tweet/SMDRZG tweeter/M tweezers/M twelfth/M twelfths twelve/SM twelvemonth/M twelvemonths twentieth/M twentieths twenty/SMH twerk/SDG twerp/SM twice twiddle/MGDS twiddly twig/MS twigged twigging twiggy/TR twilight/M twilit twill/MD twin/MDRSZG twine/SM twiner/M twinge/DSMG twink/SY twinkle/MGJDS twinkling/M twinned twinning twinset/S twirl/SMDRZG twirler/M twirly twist's twist/USDG twister/MS twisty/TR twit/MS twitch/GMDS twitchy/RT twitted twitter/MDSG twittery twitting twixt two/SM twofer/SM twofold twopence/SM twopenny twosome/SM twp tycoon/SM tying/AU tyke/MS tympani/M tympanic tympanist/MS tympanum/SM type's type/AGDS typecast/GS typeface/MS typescript/MS typeset/S typesetter/MS typesetting/M typewrite/RSZG typewriter/M typewriting/M typewritten typewrote typhoid/M typhoon/MS typhus/M typical/UY typicality/M typification/M typify/NGDS typing/M typist/SM typo/MS typographer/SM typographic typographical/Y typography/M typology/SM tyrannic tyrannical/Y tyrannize/GDS tyrannosaur/MS tyrannosaurus/MS tyrannous tyranny/SM tyrant/SM tyro/MS u/S ubiquitous/Y ubiquity/M udder/SM ufologist/SM ufology/M ugh ugliness/M ugly/RTP uh ukase/SM ukulele/SM ulcer/SM ulcerate/DSGN ulceration/M ulcerous ulna/M ulnae ulnar ulster/MS ult ulterior ultimate/MY ultimatum/MS ultimo ultra/SM ultraconservative/SM ultrahigh ultralight/SM ultramarine/M ultramodern ultrasonic ultrasonically ultrasound/MS ultraviolet/M ululate/DSGNX ululation/M um umbel/SM umber/M umbilical umbilici umbilicus/M umbra/SM umbrage/M umbrella/SM umiak/SM umlaut/MS ump/SGMD umpire/MGDS umpteen/H unabridged/MS unacceptability unacceptable unaccommodating unaccountably unadventurous unaesthetic unalterably unambitious unanimity/M unanimous/Y unapparent unappetizing unappreciative unassertive unassuming/Y unavailing/Y unaware/S unbeknownst unbend/SG unbent unbid unblinking/Y unblushing/Y unbosom/DG unbound/D unbreakable unbroken uncanny/T uncap/S uncaring uncatalogued unceasing/Y unchangeable uncharacteristic uncharitable unchaste/RT uncial/M uncle/SM unclean/DRPT uncleanly/T unclear/DRT uncomfortable uncommon/T uncomplaining/Y uncomplicated uncomprehending/Y uncompromising/Y unconditional/Y uncongenial unconscionable unconscionably unconscious/M unconstitutional/Y uncontrollably uncontroversial uncool uncooperative uncouth/Y uncrushable unction/SM unctuous/YP unctuousness/M uncut undaunted/Y undecided/SM undemonstrative/Y undeniably under underachieve/LZGDRS underachiever/M underact/SDG underage underappreciated underarm/SM underbelly/SM underbid/S underbidding underbrush/M undercarriage/MS undercharge/MGDS underclass/MS underclassman/M underclassmen underclothes/M underclothing/M undercoat/GJSMD undercoating/M undercover undercurrent/SM undercut/SM undercutting underdeveloped underdevelopment/M underdog/SM underdone underemployed underemployment/M underestimate/DSMGNX underestimation/M underexpose/GDS underexposure/MS underfed underfeed/GS underfloor underflow underfoot underfunded underfur/M undergarment/SM undergo/G undergoes undergone undergrad/S undergraduate/SM underground/MS undergrowth/M underhand underhanded/PY underhandedness/M underlain underlay/SM underlie/S underline/MGDS underling/MS underlip/SM underlying undermanned undermentioned undermine/GDS undermost underneath/M underneaths undernourished undernourishment/M underpaid underpants/M underpart/MS underpass/MS underpay/GSL underpayment/SM underpin/S underpinned underpinning/MS underplay/DGS underpopulated underprivileged underproduction/M underrate/GDS underrepresented underscore/DSMG undersea/S undersecretary/SM undersell/GS undersexed undershirt/SM undershoot/SG undershorts/M undershot underside/MS undersign/DGS undersigned/M undersized underskirt/SM undersold understaffed understand/SGBJ understandably understanding/MY understate/DSLG understatement/SM understood understudy/GDSM undertake/ZGJRS undertaken undertaker/M undertaking/M underthings/M undertone/MS undertook undertow/SM underused underutilized undervaluation/M undervalue/DSG underwater underway underwear/M underweight/M underwent underwhelm/DGS underwire/DS underworld/MS underwrite/ZGRS underwriter/M underwritten underwrote undesirable/MS undies/M undo undoubted/Y undramatic undue undulant undulate/DSXGN undulation/M undying unearthliness/M unease/M uneasy/T uneatable uneconomic unemployed/M unending unenterprising unequal/DY unerring/Y unessential uneven/Y unexceptionably unexcited unexciting unexpected/YP unexpectedness/M unfailing/Y unfair/PTRY unfaltering unfamiliar unfathomably unfed unfeeling/Y unfeminine unfit/S unfitting unfix/GDS unflagging/Y unflappability/M unflappable unflappably unflattering unflinching/Y unforgettably unforgivably unfortunate/MS unfriend/GD unfriendly/T unfrock/DG unfruitful unfunny ungainliness/M ungainly/RPT ungenerous ungentle ungodly/T ungraceful/Y ungrudging unguarded unguent/SM ungulate/MS unhandy/T unhappy/T unhealthful unhealthy/T unhistorical unholy/T unhurt unicameral unicellular unicorn/SM unicycle/SM unidirectional unification/AM uniform/SMDYG uniformity/M unify/AGDSN unilateral/Y unilateralism unimportant unimpressive uninformative uninhibited/Y uninstall/BSZGDR uninsured unintelligent unintended uninteresting uninterrupted/Y uninterruptible uninviting union/ASM unionism/M unionist/MS unique/YTRP uniqueness/M unisex/M unison/M unitary unite/AEGSD unitedly unities unitize/DSG unity/EM univalent univalve/SM universal/MYS universality/M universalize/DSG universe/SM university/SM unjust/Y unkempt unkind/T unkindly/T unknowable/M unknown/SM unleaded/M unless unlike/PB unlikely/T unlit unlock/DSG unlovable unlovely/TR unloving unlucky/T unmanly/T unmarried unmeaning unmentionable/MS unmentionables/M unmet unmindful unmissable unmistakably unmoral unmovable unmusical unnecessary unnerving/Y unobservant unoffensive unofficial/Y unoriginal unpeople unperceptive unpersuasive unpick/GDS unpin/S unpleasing unpolitical unpopular unpractical unprecedented/Y unprofessional/Y unpromising unpropitious unquestioning/Y unquiet/TR unread/B unready unreal unreasoning unregenerate unrelated unrelenting/Y unrelieved/Y unremarkable unremitting/Y unrepentant unreported unrepresentative unrest/M unripe/TR unroll/GDS unromantic unruliness/M unruly/RTP unsafe/YTR unsaleable unsavory unscathed unsearchable unseeing/Y unseemly/T unseen/M unsentimental unset unshakable unshakably unshapely unshockable unshorn unsightliness/M unsightly/PT unsmiling unsociable unsocial unsold unsound/PRYT unspeakable unspeakably unspecific unspectacular unsporting unstable unsteady/TRP unstinting/Y unstrapping unsubstantial unsubtle unsuitable unsure unsuspecting/Y unsymmetrical untactful unthinkably unthinking/Y untidy/PTR until untimely/T untiring/Y untouchable/MS untoward untrue/RT untrustworthy unutterable unutterably unwarrantable unwary/T unwavering unwed unwelcome/G unwell unwieldiness/M unwieldy/TRP unwise/RYT unworried unworthy/T unwound unwrapping unyielding up/S upbeat/MS upbraid/SGD upbringing/MS upchuck/SGD upcoming upcountry/M update/MGDRS updraft/MS upend/SGD upfront upgrade/MGDS upheaval/MS upheld uphill/MS uphold/ZGRS upholder/M upholster/ASGD upholsterer/MS upholstery/M upkeep/M upland/MS uplift/JSMDG upload/SDG upmarket upon upped upper/SM uppercase/M upperclassman/M upperclassmen upperclasswoman upperclasswomen uppercut/MS uppercutting uppermost upping uppish uppity upraise/DSG uprear/GSD upright/MYPS uprightness/M uprising/SM upriver uproar/SM uproarious/Y uproot/GSD upscale upset/SM upsetting upshot/SM upside/SM upsilon/MS upstage/GDS upstairs upstanding upstart/MDSG upstate/M upstream upstroke/SM upsurge/MGDS upswing/MS uptake/SM uptempo upthrust/GSM uptick/SM uptight uptown/M uptrend upturn/GSMD upward/SY upwind uracil/M uranium/M urban urbane/RYT urbanity/M urbanization/M urbanize/DSG urbanologist/MS urbanology/M urchin/SM urea/M uremia/M uremic ureter/SM urethane/M urethra/M urethrae urethral urge/MGDS urgency/M urgent/Y uric urinal/SM urinalyses urinalysis/M urinary urinate/GNDS urination/M urine/M urn/SM urogenital urological urologist/MS urology/M ursine urticaria/M usability/M usable/UA usage/SM use/AEDSMG used/U useful/PY usefulness/M useless/YP uselessness/M user/MS username/S usher/SMDG usherette/SM usu usual's usual/UY usurer/SM usurious usurp/SDRZG usurpation/M usurper/M usury/M utensil/SM uteri uterine uterus/M utilitarian/MS utilitarianism/M utility/SM utilization/M utilize/GBDS utmost/M utopia/SM utter/SDYG utterance/SM uttermost/M uveitis uvula/SM uvular/MS uxorious v/AS vac/S vacancy/SM vacant/Y vacate/DSG vacation/ZGMDRS vacationer/M vacationist/SM vaccinate/GNDSX vaccination/M vaccine/SM vacillate/XGNDS vacillation/M vacuity/M vacuole/MS vacuous/YP vacuousness/M vacuum/GSMD vagabond/SMDG vagabondage/M vagarious vagary/SM vagina/SM vaginae vaginal/Y vagrancy/M vagrant/MS vague/RYTP vagueness/M vagus vain/RYT vainglorious/Y vainglory/M val valance/MS vale/MS valediction/MS valedictorian/SM valedictory/SM valence/MS valency/SM valentine/SM valet/SMDG valetudinarian/MS valetudinarianism/M valiance/M valiant/Y valid/Y validate/IGNDS validation/IM validations validity/IM validness/M valise/SM valley/SM valor/M valorous/Y valuable/MS valuate/DSG valuation/CAMS value's value/CAGSD valueless valuer/SM valve/DSMG valveless valvular vamoose/DSG vamp/AMDGS vampire/SM van/SM vanadium/M vandal/SM vandalism/M vandalize/DSG vane/MS vanguard/MS vanilla/SM vanish/JDSG vanity/SM vanned vanning vanquish/ZGDRS vanquisher/M vantage/SM vape/GDS vapid/YP vapidity/M vapidness/M vapor/SM vaporization/M vaporize/DRSZG vaporizer/M vaporous vaporware vapory vaquero/MS var/S variability/IM variable/ISM variably/I variance/SM variant/MS variate/NX variation/M varicolored varicose varied/U variegate/DSGN variegation/M varietal/SM variety/SM various/Y varlet/SM varmint/MS varnish/GMDS varnished/U varsity/SM vary/DSG varying/U vascular vase/MS vasectomy/SM vasoconstriction vasomotor vassal/SM vassalage/M vast/MRYTSP vastness/M vat/SM vatted vatting vaudeville/M vaudevillian/MS vault/SMDRZG vaulter/M vaulting/M vaunt/SMDG vb veal/M vector/SGMD veejay/SM veep/MS veer/MDGS veg/M vegan/SM veganism vegeburger/S veges vegetable/SM vegetarian/SM vegetarianism/M vegetate/GNVDS vegetation/M vegged vegges veggie/SM veggieburger/S vegging vehemence/M vehemency/M vehement/Y vehicle/MS vehicular veil's veil/UDGS vein/MDGS vela velar/SM veld/MS vellum/M velocipede/MS velocity/SM velodrome/S velour/MS velum/M velvet/M velveteen/M velvety venal/Y venality/M venation/M vend/DGS vendetta/SM vendible vendor/MS veneer/MDGS venerability/M venerable venerate/DSGN veneration/M venereal vengeance/M vengeful/AY venial venireman/M veniremen venison/M venom/M venomous/Y venous vent's vent/DGS ventilate/GNDS ventilation/M ventilator/SM ventral ventricle/SM ventricular ventriloquism/M ventriloquist/SM ventriloquy/M venture/DSMG venturesome/PY venturesomeness/M venturous/PY venturousness/M venue/ASM veracious/Y veracity/M veranda/SM verb/KMS verbal/MYS verbalization/M verbalize/GDS verbatim verbena/SM verbiage/MS verbose/Y verbosity/M verboten verdant/Y verdict/SM verdigris/GMDS verdure/M verge's verge/FDSG verger/MS verifiable/U verification/M verified/U verify/DSNG verily verisimilitude/M veritable veritably verity/SM vermicelli/M vermiculite/M vermiform vermilion/M vermin/M verminous vermouth/M vernacular/MS vernal vernier/SM veronica/M verruca/SM verrucae versa versatile versatility/M verse/AFNGMSDX versed/U versification/M versifier/M versify/ZGNDRS version/AFIMS versioned versioning verso/SM versus vert/A vertebra/M vertebrae vertebral vertebrate/IMS vertex/MS vertical/MYS vertices vertiginous vertigo/M verve/M very/RT vesicle/SM vesicular vesiculate vesper/MS vessel/MS vest's vest/ILDGS vestal/MS vestibule/MS vestige/SM vestigial/Y vesting/M vestment/IMS vestry/SM vestryman/M vestrymen vet/SM vetch/MS veteran/SM veterinarian/MS veterinary/SM veto/MDG vetoes vetted vetting vex/GDS vexation/SM vexatious/Y vhf vi via viability/M viable viably viaduct/SM vial/MS viand/SM vibe/MS vibes/M vibraharp/SM vibrancy/M vibrant/Y vibraphone/MS vibraphonist/MS vibrate/GNDSX vibration/M vibrato/MS vibrator/SM vibratory viburnum/SM vicar/SM vicarage/SM vicarious/YP vicariousness/M vice/CMS viced vicegerent/SM vicennial viceregal viceroy/MS vichyssoise/M vicing vicinity/M vicious/YP viciousness/M vicissitude/SM victim/MS victimization/M victimize/GDS victor/MS victorious/Y victory/SM victual/SMDG vicuna/MS videlicet video/GSMD videocassette/SM videoconferencing videodisc/MS videophone/MS videotape/DSMG videotex vie/DS view/AMDRSZG viewer/AM viewership/M viewfinder/SM viewing/SM viewpoint/MS vigesimal vigil/SM vigilance/M vigilant/Y vigilante/SM vigilantism/M vigilantist/M vignette/DSMG vignettist/MS vigor/M vigorous/Y vii viii viking/MS vile/YTPR vileness/M vilification/M vilify/DSNG villa/SM village/RSMZ villager/M villain/SM villainous villainy/SM villein/SM villeinage/M villi villus/M vim/M vinaigrette/M vincible/I vindicate/XDSGN vindication/M vindicator/MS vindictive/PY vindictiveness/M vine/MS vinegar/M vinegary vineyard/MS vino/M vinous vintage/MS vintner/MS vinyl/SM viol/MBS viola/SM violable/I violate/GNDSX violation/M violator/SM violence/M violent/Y violet/MS violin/MS violincello/S violinist/SM violist/MS violoncellist/SM violoncello/MS viper/SM viperous virago/M viragoes viral vireo/SM virgin/MS virginal/SM virginity/M virgule/MS virile virility/M virologist/SM virology/M virtual/Y virtualization virtue/SM virtuosity/M virtuoso/M virtuous/YP virtuousness/M virulence/M virulent/Y virus/MS visa/MDSG visage/MS viscera visceral/Y viscid viscose/M viscosity/M viscount/SM viscountcy/SM viscountess/MS viscous viscus/M vise/ACMGDS visibility/IM visible/I visibly/I vision/KGDSM visionary/SM visit's visit/ASGD visitant/MS visitation/MS visitor/MS visor/SM vista/SM visual/SMY visualization/SM visualize/DRSZG visualizer/M vita/M vitae vital/SY vitality/M vitalization/AM vitalize/CAGSD vitals/M vitamin/MS vitiate/GNDS vitiation/M viticulture/M viticulturist/MS vitreous vitrifaction/M vitrification/M vitrify/GNDS vitrine/SM vitriol/M vitriolic vitriolically vittles/M vituperate/GNVDS vituperation/M viva/MS vivace vivacious/PY vivaciousness/M vivacity/M vivaria vivarium/SM vivid/RYTP vividness/M vivify/ADSG viviparous vivisect/DGS vivisection/M vivisectional vivisectionist/SM vixen/SM vixenish/Y viz vizier/SM vocab vocable/MS vocabulary/SM vocal/SMY vocalic vocalist/SM vocalization/MS vocalize/DSG vocation/FIKASM vocational/Y vocative/MS vociferate/DSGN vociferation/M vociferous/YP vociferousness/M vodka/SM vogue/SM voguish voice/IDSMG voiced/U voiceless/PY voicelessness/M voicemail/M void/MDSGB voila voile/M vol/S volatile volatility/M volatilize/DSG volcanic volcano/M volcanoes vole/MS volition/M volitional volley/GSMD volleyball/MS volt/AMS voltage/MS voltaic voltmeter/SM volubility/M voluble volubly volume/SM voluminous/YP voluminousness/M voluntarily/I voluntarism/M voluntary/SM volunteer/SGMD volunteerism/M voluptuary/SM voluptuous/PY voluptuousness/M volute/SM vomit/SMDG voodoo/GSMD voodooism/M voracious/PY voraciousness/M voracity/M vortex/MS votary/SM vote's vote/CGVDS voter/SM vouch/DRSZG voucher/M vouchsafe/DSG vow/SGMD vowel/SM voyage/MZGDRS voyager/M voyageur/SM voyeur/MS voyeurism/M voyeuristic vulcanization/M vulcanize/GDS vulgar/RYT vulgarian/MS vulgarism/MS vulgarity/SM vulgarization/M vulgarize/ZGDRS vulgarizer/M vulnerabilities vulnerability/IM vulnerable/I vulnerably/I vulpine vulture/SM vulturous vulva/M vulvae vuvuzela/MS vying w/DNXTGVJ wabbit/S wack/RTS wackiness/M wacko/SM wacky/RPT wad/SZGMDR wadded wadding/M waddle/DSMG wade/MS wader/M waders/M wadge/S wadi/MS wafer/SM waffle/MZGDRS waffler/M waft/MDGS wag/SZGMDR wage/MS waged/U wager/ZGMDR wagerer/M wagged waggery/SM wagging waggish/YP waggishness/M waggle/MGDS wagon/ZSMR wagoner/M wagtail/SM waif/MS wail/MDRZGS wailer/M wailing/M wain/MS wainscot/SJMDG wainscoting/M wainwright/MS waist/SM waistband/MS waistcoat/MS waistline/MS wait/MDRZGS waiter/M waiting/M waitperson/MS waitress/MS waitstaff/M waive/DRSZG waiver/M wake/MGJDS wakeful/PY wakefulness/M waken/GSD waldo/S waldoes wale/MGDS walk/MDRZGS walkabout/S walkaway/MS walker/M walkies walking/M walkout/SM walkover/MS walkway/SM wall/MDGS wallaby/SM wallah wallahs wallboard/M wallet/MS walleye/DSM wallflower/MS wallop/MDSJG walloping/M wallow/MDSG wallpaper/SMDG wally/S walnut/MS walrus/MS waltz/ZGMDRS waltzer/M wampum/M wan/GPDY wand/MS wander/DRSJZG wanderer/M wanderings/M wanderlust/SM wane/MS wangle/MZGDRS wangler/M wank/DRZGS wanna wannabe/SM wannabee/S wanner wanness/M wannest want/MDGS wanted/U wanton/MDYSPG wantonness/M wapiti/MS war/SM warble/MZGDRS warbler/M warbonnet/SM ward/AMDGS warden/MS warder/MS wardress/S wardrobe/SM wardroom/SM ware/MS warehouse/DSMG warez warfare/M warhead/MS warhorse/SM warily/U wariness/UM warlike warlock/MS warlord/MS warm/PDRYHZTGS warmblooded warmer/M warmhearted/P warmheartedness/M warmish warmness/M warmonger/SMG warmongering/M warmth/M warn/JDGS warning/M warp/MDGS warpaint warpath/M warpaths warplane/MS warrant/GMDS warranted/U warranty/DSMG warred warren/MS warring warrior/SM warship/SM wart/MS warthog/SM wartime/M warty/TR wary/UPRT was wasabi wash/BMDRSZG washable/SM washbasin/SM washboard/SM washbowl/SM washcloth/M washcloths washed/U washer/M washerwoman/M washerwomen washing/M washout/MS washrag/MS washroom/MS washstand/SM washtub/MS washy/TR wasn't wasp/MS waspish/YP waspishness/M wassail/SMDG wast wastage/M waste/DRSMZG wastebasket/MS wasteful/PY wastefulness/M wasteland/SM wastepaper/M waster/M wastewater wastrel/SM watch/BZGMDRS watchable/U watchband/MS watchdog/SM watcher/M watchful/YP watchfulness/M watchmaker/MS watchmaking/M watchman/M watchmen watchstrap/S watchtower/SM watchword/MS water/GSMD waterbed/MS waterbird/SM waterboard/DJSG waterborne watercolor/MS watercourse/SM watercraft/M watercress/M waterfall/SM waterfowl/SM waterfront/MS waterhole/SM wateriness/M waterlily/SM waterline/MS waterlogged watermark/MDGS watermelon/SM watermill/MS waterproof/SMDG waterproofing/M waters/M watershed/MS waterside/MS waterspout/SM watertight waterway/MS waterwheel/SM waterworks/M watery/PTR watt/MS wattage/M wattle/MGDS wave/MZGDRS waveband/S waveform wavelength/M wavelengths wavelet/SM wavelike waver/ZGMDR waverer/M wavering/Y waviness/M wavy/PRT wax/GMDNS waxiness/M waxwing/SM waxwork/SM waxy/RPT way/SM waybill/SM wayfarer/MS wayfaring/SM waylaid waylay/RSZG waylayer/M wayside/SM wayward/PY waywardness/M wazoo/S we we'd we'll we're we've weak/PNRYXT weaken/DRZG weakener/M weakfish/MS weakish weakling/SM weakness/MS weal/MHS wealth/M wealthiness/M wealthy/TRP wean/DGS weapon/MS weaponize weaponless weaponry/M wear/MRBJSZG wearable/U wearer/M wearied/U wearily weariness/M wearisome/Y weary/TGDRSP weasel/MDYSG weather/SMDG weatherboard/SG weathercock/MS weathering/M weatherization/M weatherize/DSG weatherman/M weathermen weatherperson/MS weatherproof/GSD weatherstrip/S weatherstripped weatherstripping/M weave/DRSMZG weaver/M weaving/M web/SM webbed webbing/M webcam/MS webcast/SMG webfeet webfoot/M webinar/SM webisode/MS weblog/MS webmaster/SM webmistress/MS webpage/SM website/SM wed/AS wedded/A wedder wedding/SM wedge/DSMG wedgie/MS wedlock/M wee/RSMT weed/MDRSZG weeder/M weedkiller/S weedless weedy/TR weeing week/MYS weekday/SM weekend/SZGMDR weekly/SM weeknight/SM ween/DSG weenie/MTRS weensy/RT weeny weep/MRJSZG weeper/M weepie weepy/TRSM weevil/MS weft/MS weigh's weigh/AGD weighbridge/S weighs/A weight/MDSJG weighted/U weightily weightiness/M weightless/YP weightlessness/M weightlifter/MS weightlifting/M weighty/PTR weir/MS weird/PTRY weirdie/MS weirdness/M weirdo/MS welcome/MGDS weld/MDRBSZG welder/M welfare/M welkin/M well/MDPSG wellhead/SM wellie wellington/MS wellness/M wellspring/MS welly/S welsh/ZGDRS welsher/M welt/MDRSZG welter/GMD welterweight/SM wen/M wench/MS wend/DSG went wept were weren't werewolf/M werewolves west/M westbound westerly/SM western/SZMR westerner/M westernization/M westernize/GDS westernmost westward/S wet/SMYP wetback/SM wetland/SM wetness/M wetter/SM wettest wetting wetware/S whack/SJZGMDR whacker/M whale/DRSMZG whaleboat/MS whalebone/M whaler/M whaling/M wham/MS whammed whamming whammy/SM wharf/M wharves what/MS whatchamacallit/MS whatever whatnot/M whatshername whatshisname whatsit/S whatsoever wheal/SM wheat/MN wheatgerm wheatmeal whee wheedle/DRSZG wheedler/M wheel/SMDRG wheelbarrow/SM wheelbase/SM wheelchair/SM wheelhouse/MS wheelie/SM wheelwright/MS wheeze/DSMG wheezily wheeziness/M wheezy/PRT whelk/SMD whelm/SDG whelp/SMDG when/MS whence whenever whensoever where/SM whereabouts/M whereas whereat whereby wherefore/MS wherein whereof whereon wheresoever whereto whereupon wherever wherewith wherewithal/M wherry/SM whet/S whether whetstone/SM whetted whetting whew whey/M which whichever whiff/SMDG whiffletree/MS while/DSMG whilom whilst whim/MS whimper/MDGS whimsical/Y whimsicality/M whimsy/SM whine/DRSMZG whiner/M whinge/DRSZG whingeing whinny/GDSM whiny/RT whip/MS whipcord/M whiplash/MS whipped whipper/MS whippersnapper/MS whippet/MS whipping/SM whippletree/SM whippoorwill/MS whipsaw/MDGS whir/MS whirl/SMDG whirligig/MS whirlpool/MS whirlwind/MS whirlybird/SM whirred whirring whisk/SMDRZG whisker/MD whiskery whiskey/MS whiskys whisper/MDRSZG whisperer/M whist/M whistle/MZGDRS whistler/M whit/MDNRSXTGJ white/SPM whitebait whiteboard/S whitecap/SM whitefish/MS whitehead/MS whiten/ZGDRJ whitener/M whiteness/M whitening/M whiteout/SM whitetail/MS whitewall/SM whitewash/MDSG whitewater/M whitey/SM whither whiting/M whitish whittle/ZGDRS whittler/M whiz/M whizkid/M whizzbang/MS whizzed whizzes whizzing who'd who'll who're who've who/M whoa whodunit/MS whoever whole/SMP wholefood/S wholegrain wholehearted/YP wholeheartedness/M wholemeal wholeness/M wholesale/MZGDRS wholesaler/M wholesome/UP wholesomely wholesomeness/UM wholewheat wholly whom whomever whomsoever whoop/SMDRZG whoopee/S whooper/M whoosh/MDSG whop/S whopped whopper/SM whopping whore/SMG whorehouse/MS whoreish whorish whorl/SMD whose whoso whosoever whup/S whupped whupping why'd why/M whys wick/MDRSZ wicked/TPRY wickedness/M wicker/M wickerwork/M wicket/SM wide/YTRP widemouthed widen/SDRZG widener/M wideness/M widescreen/S widespread widget/S widow/SMDRZG widower/M widowhood/M width/M widths wield/SDRZG wielder/M wiener/SM wienie/SM wife/MY wifeless wig/SM wigeon/M wigged wigging wiggle/DRSMZG wiggler/M wiggly/TR wight/SM wiglet/SM wigwag/SM wigwagged wigwagging wigwam/SM wiki/MS wild/MRYSTP wildcard wildcat/MS wildcatted wildcatter/MS wildcatting wildebeest/MS wilderness/MS wildfire/MS wildflower/SM wildfowl/M wildlife/M wildness/M wilds/M wile/MGDS wiliness/M will/MDS willful/PY willfulness/M willies/M willing/UPY willingness/UM williwaw/MS willow/SM willowy willpower/M willy/S wilt/MDSG wily/RTP wimp/MDSG wimpish wimple/DSMG wimpy/RT win/SGMD wince/DSMG winch/MDSG wind's wind/UASG windbag/SM windblown windbreak/SZMR windbreaker/M windburn/MD windcheater/S windchill/M winded winder/SM windfall/MS windflower/MS windily windiness/M winding's windjammer/SM windlass/MS windless windmill/MDGS window/SMDG windowless windowpane/SM windowsill/SM windpipe/MS windproof windrow/SM windscreen/SM windshield/SM windsock/MS windstorm/MS windsurf/ZGDRS windsurfer/M windsurfing/M windswept windup/SM windward/M windy/RTP wine/MS wineglass/MS winegrower/MS winemaker/MS winery/SM wing/MDRZG wingding/MS wingless winglike wingnut/SM wingspan/MS wingspread/SM wingtip/SM wink/MDRSZG winker/M winkle/DSMG winnable/U winner/SM winning/MYS winnow/ZGSDR winnower/M wino/MS winsome/YTRP winsomeness/M winter/GSMD wintergreen/M winterize/GDS wintertime/M wintry/TR winy/RT wipe/MZGDRS wiper/M wire's wire/AGDS wired/S wirehair/MS wireless/MS wiretap/MS wiretapped wiretapper/SM wiretapping/M wiriness/M wiring/M wiry/RTP wisdom/M wise/MYTGDRS wiseacre/SM wisecrack/MDSG wiseguy/S wish/MDRSZG wishbone/SM wisher/M wishful/Y wishlist/SM wisp/MS wispy/RT wist wisteria/SM wistful/YP wistfulness/M wit/SM witch/MDSG witchcraft/M witchery/M with withal withdraw/SG withdrawal/MS withdrawn withdrew withe/DRSMZG wither/JGD withering/Y withers/M withheld withhold/SG withholding/M within/M without withstand/GS withstood witless/PY witlessness/M witness/MDSG wits/M witted witter/SGD witticism/SM wittily wittiness/M witting/UY witty/RPT wive/GDS wiz wizard/SMY wizardry/M wizened wk/Y woad/M wobble/MGDS wobbliness/M wobbly/RTP wodge/S woe/SM woebegone woeful/YP woefuller woefullest woefulness/M wog/S wok/SMN woke wold/MS wolf/MDSG wolfhound/SM wolfish wolfram/M wolverine/SM wolves woman/M womanhood/M womanish womanize/DRSZG womanizer/M womankind/M womanlike/M womanliness/M womanly/RPT womb/MS wombat/MS womble/S women/M womenfolk/SM womenfolks/M won't won/M wonder/MDGLS wonderful/YP wonderfulness/M wondering/Y wonderland/MS wonderment/M wondrous/Y wonk/MS wonky/TR wont/MD wonted/U woo/SZGDR wood/MDNSG woodbine/M woodblock/MS woodcarver/MS woodcarving/SM woodchuck/MS woodcock/SM woodcraft/M woodcut/SM woodcutter/SM woodcutting/M wooden/RYTP woodenness/M woodiness/M woodland/SM woodlice woodlot/SM woodlouse woodman/M woodmen woodpecker/MS woodpile/SM woods/M woodshed/SM woodsiness/M woodsman/M woodsmen woodsy/RTP woodwind/MS woodwork/MRZG woodworker/M woodworking/M woodworm/S woody/TPRSM wooer/M woof/MDRSZG woofer/M wool/MNX woolen/M woolgathering/M wooliness woolliness/M woolly/RSMPT woozily wooziness/M woozy/TRP wop/S! word's word/ADSG wordage/M wordbook/SM wordily wordiness/M wording/SM wordless/Y wordplay/M wordsmith wordsmiths wordy/TPR wore work's work/ADJSG workable/U workaday workaholic/SM workaround/S workbasket/S workbench/MS workbook/MS workday/SM worker/MS workfare/M workflow/MS workforce/M workhorse/SM workhouse/SM working's workingman/M workingmen workings/M workingwoman/M workingwomen workload/MS workman/M workmanlike workmanship/M workmate/S workmen workout/SM workplace/MS workroom/MS works/M worksheet/MS workshop/MS workshy workstation/MS worktable/MS worktop/S workup/MS workweek/SM world/SM worldlier worldliness/UM worldly/UTP worldview/SM worldwide worm/MDSG wormhole/MS wormwood/M wormy/TR worn/U worried/Y worrier/M worriment/M worrisome worry/ZGDRSMJ worrying/Y worrywart/SM worse/M worsen/DSG worship/ZGSMDR worshiper/M worshipful worst/SGMD worsted/M wort/M worth/M worthies worthily/U worthiness/UM worthless/PY worthlessness/M worthwhile worthy's worthy/UPRT wot wotcha would've would/S wouldn't wouldst wound/SGMDR wove/A woven/AU wow/SGMD wpm wrack/GSMD wraith/M wraiths wrangle/DRSMZGJ wrangler/M wrap's wrap/US wraparound/SM wrapped/U wrapper/SM wrapping/MS wrasse/MS wrath/M wrathful/Y wreak/SGD wreath/MDSG wreathe wreaths wreck/SZGMDR wreckage/M wrecker/M wren/MS wrench/MDSG wrest/SGMD wrestle/MZGDRS wrestler/M wrestling/M wretch/MS wretched/TPRY wretchedness/M wriggle/MZGDRS wriggler/M wriggly wright/MS wring/SZGMR wringer/M wrinkle/MGDS wrinkled/U wrinkly/TRSM wrist/SM wristband/MS wristwatch/MS writ/MRBJSZG write/S writer/M writhe/MGDS writing/M written/AU wrong/STGMPDRY wrongdoer/SM wrongdoing/SM wrongful/PY wrongfulness/M wrongheaded/YP wrongheadedness/M wrongness/M wrote/A wroth wrought wrung wry/Y wryer wryest wryness/M wt wunderkind/S wurst/SM wuss/MS wussy/RSMT x xci xcii xciv xcix xcvi xcvii xenon/M xenophobe/MS xenophobia/M xenophobic xerographic xerography/M xerox/MDSG xi/SM xii xiii xiv xix xor xref/S xterm/M xv xvi xvii xviii xx xxi xxii xxiii xxiv xxix xxv xxvi xxvii xxviii xxx xxxi xxxii xxxiii xxxiv xxxix xxxv xxxvi xxxvii xxxviii xylem/M xylene xylophone/SM xylophonist/MS y'all y/F ya yacht/SMDG yachting/M yachtsman/M yachtsmen yachtswoman/M yachtswomen yahoo/SM yak/SM yakked yakking yam/SM yammer/SZGMDR yammerer/M yang/M yank/MDSG yap/SM yapped yapping yard/MS yardage/MS yardarm/MS yardman/M yardmaster/MS yardmen yardstick/MS yarmulke/SM yarn/MS yarrow/M yashmak/S yaw/SGMD yawl/MS yawn/MDRSZG yawner/M yaws/M yd ye/RST yea/SM yeah/M yeahs year/MYS yearbook/MS yearling/MS yearlong yearly/SM yearn/GSJD yearning/M yeast/SM yeasty/RT yegg/MS yell/MDSG yellow/MDRTGPS yellowhammer/S yellowish yellowness/M yellowy yelp/MDSG yen/SM yeoman/M yeomanry/M yeomen yep/SM yes/MS yeshiva/SM yessed yessing yesterday/MS yesteryear/M yet yeti/MS yew/SM yid/S yield/JSGMD yikes yin/M yip/SM yipe yipped yippee yipping yo yob/S yobbo/S yodel/SMDRZG yodeler/M yoga/M yogi/MS yogic yogurt/SM yoke's yoke/UGDS yokel/SM yolk/MDS yon yonder yonks yore/M you'd you'll you're you've you/SMH young/TMR youngish youngster/MS your/S yourself yourselves youth/M youthful/YP youthfulness/M youths yow yowl/MDSG yr/S ytterbium/M yttrium/M yuan/M yucca/SM yuck yucky/TR yuk/SM yukked yukking yukky yule/M yuletide/M yum yummy/TR yup/SM yuppie/MS yuppify/GDS yurt/MS z/DNXTGJ zaniness/M zany/RSMPT zap/SM zapped zapper/MS zapping zappy zeal/M zealot/MS zealotry/M zealous/YP zealousness/M zebra/SM zebu/MS zed/SM zeitgeist/SM zenith/M zeniths zenned zeolite/S zephyr/MS zeppelin/MS zero/MDHSG zeroes zest/MS zestful/YP zestfulness/M zesty/RT zeta/MS zigzag/SM zigzagged zigzagging zilch/M zillion/MS zinc/MS zincked zincking zine/S zinfandel/M zing/MDRZG zinger/M zingy/RT zinnia/MS zip's zip/US zipped/U zipper/MDGS zipping/U zippy/TR zircon/MS zirconium/M zit/SM zither/MS zloty/SM zlotys zodiac/MS zodiacal zombie/MS zonal/Y zone's zone/AGDS zoning/M zonked zoo/SM zookeeper/SM zoological/Y zoologist/SM zoology/M zoom/MDSG zoophyte/SM zoophytic zooplankton zorch zoster zounds zucchini/MS zwieback/M zydeco/M zygote/SM zygotic zymurgy/M ================================================ FILE: public/vendor/inlineAttachment/codemirror.inline-attachment.js ================================================ /*jslint newcap: true */ /*global inlineAttachment: false */ /** * CodeMirror version for inlineAttachment * * Call inlineAttachment.attach(editor) to attach to a codemirror instance */ (function() { 'use strict'; var codeMirrorEditor = function(instance) { if (!instance.getWrapperElement) { throw "Invalid CodeMirror object given"; } this.codeMirror = instance; }; codeMirrorEditor.prototype.getValue = function() { return this.codeMirror.getValue(); }; codeMirrorEditor.prototype.insertValue = function(val) { this.codeMirror.replaceSelection(val); }; codeMirrorEditor.prototype.setValue = function(val) { var cursor = this.codeMirror.getCursor(); this.codeMirror.setValue(val); this.codeMirror.setCursor(cursor); }; codeMirrorEditor.prototype.replaceRange = function(val) { this.codeMirror.replaceRange(val.replacement, val.from, val.to, "+input"); }; /** * Attach InlineAttachment to CodeMirror * * @param {CodeMirror} codeMirror */ codeMirrorEditor.attach = function(codeMirror, options) { options = options || {}; var editor = new codeMirrorEditor(codeMirror), inlineattach = new inlineAttachment(options, editor), el = codeMirror.getWrapperElement(); el.addEventListener('paste', function(e) { inlineattach.onPaste(e); }, false); codeMirror.setOption('onDragEvent', function(data, e) { if (e.type === "drop") { e.stopPropagation(); e.preventDefault(); return inlineattach.onDrop(e); } }); }; inlineAttachment.editors.codemirror3 = codeMirrorEditor; var codeMirrorEditor4 = function(instance) { codeMirrorEditor.call(this, instance); }; codeMirrorEditor4.attach = function(codeMirror, options) { options = options || {}; var editor = new codeMirrorEditor(codeMirror), inlineattach = new inlineAttachment(options, editor), el = codeMirror.getWrapperElement(); el.addEventListener('paste', function(e) { inlineattach.onPaste(e); }, false); codeMirror.on('drop', function(data, e) { if (inlineattach.onDrop(e)) { e.stopPropagation(); e.preventDefault(); return true; } else { return false; } }); return inlineattach; }; inlineAttachment.editors.codemirror4 = codeMirrorEditor4; })(); ================================================ FILE: public/vendor/inlineAttachment/inline-attachment.js ================================================ /*jslint newcap: true */ /*global XMLHttpRequest: false, FormData: false */ /* * Inline Text Attachment * * Author: Roy van Kaathoven * Contact: ik@royvankaathoven.nl */ (function(document, window) { 'use strict'; var inlineAttachment = function(options, instance) { this.settings = inlineAttachment.util.merge(options, inlineAttachment.defaults); this.editor = instance; this.filenameTag = '{filename}'; this.lastValue = null; }; /** * Will holds the available editors * * @type {Object} */ inlineAttachment.editors = {}; /** * Utility functions */ inlineAttachment.util = { /** * Simple function to merge the given objects * * @param {Object[]} object Multiple object parameters * @returns {Object} */ merge: function() { var result = {}; for (var i = arguments.length - 1; i >= 0; i--) { var obj = arguments[i]; for (var k in obj) { if (obj.hasOwnProperty(k)) { result[k] = obj[k]; } } } return result; }, /** * Append a line of text at the bottom, ensuring there aren't unnecessary newlines * * @param {String} appended Current content * @param {String} previous Value which should be appended after the current content */ appendInItsOwnLine: function(previous, appended) { return (previous + "\n\n[[D]]" + appended) .replace(/(\n{2,})\[\[D\]\]/, "\n\n") .replace(/^(\n*)/, ""); }, /** * Inserts the given value at the current cursor position of the textarea element * * @param {HtmlElement} el * @param {String} value Text which will be inserted at the cursor position */ insertTextAtCursor: function(el, text) { var scrollPos = el.scrollTop, strPos = 0, browser = false, range; if ((el.selectionStart || el.selectionStart === '0')) { browser = "ff"; } else if (document.selection) { browser = "ie"; } if (browser === "ie") { el.focus(); range = document.selection.createRange(); range.moveStart('character', -el.value.length); strPos = range.text.length; } else if (browser === "ff") { strPos = el.selectionStart; } var front = (el.value).substring(0, strPos); var back = (el.value).substring(strPos, el.value.length); el.value = front + text + back; strPos = strPos + text.length; if (browser === "ie") { el.focus(); range = document.selection.createRange(); range.moveStart('character', -el.value.length); range.moveStart('character', strPos); range.moveEnd('character', 0); range.select(); } else if (browser === "ff") { el.selectionStart = strPos; el.selectionEnd = strPos; el.focus(); } el.scrollTop = scrollPos; } }; /** * Default configuration options * * @type {Object} */ inlineAttachment.defaults = { /** * URL where the file will be send */ uploadUrl: 'uploadimage', /** * Which method will be used to send the file to the upload URL */ uploadMethod: 'POST', /** * Name in which the file will be placed */ uploadFieldName: 'image', /** * Extension which will be used when a file extension could not * be detected */ defaultExtension: 'png', /** * JSON field which refers to the uploaded file URL */ jsonFieldName: 'link', /** * Allowed MIME types */ allowedTypes: window.allowedUploadMimeTypes, /** * Text which will be inserted when dropping or pasting a file. * Acts as a placeholder which will be replaced when the file is done with uploading */ progressText: '![Uploading file...{filename}]()', /** * When a file has successfully been uploaded the progressText * will be replaced by the urlText, the {filename} tag will be replaced * by the filename that has been returned by the server */ urlText: "![]({filename})", /** * Text which will be used when uploading has failed */ errorText: "Error uploading file", /** * Extra parameters which will be send when uploading a file */ extraParams: {}, /** * Extra headers which will be send when uploading a file */ extraHeaders: {}, /** * Before the file is send */ beforeFileUpload: function() { return true; }, /** * Triggers when a file is dropped or pasted */ onFileReceived: function() {}, /** * Custom upload handler * * @return {Boolean} when false is returned it will prevent default upload behavior */ onFileUploadResponse: function() { return true; }, /** * Custom error handler. Runs after removing the placeholder text and before the alert(). * Return false from this function to prevent the alert dialog. * * @return {Boolean} when false is returned it will prevent default error behavior */ onFileUploadError: function() { return true; }, /** * When a file has succesfully been uploaded */ onFileUploaded: function() {} }; /** * Uploads the blob * * @param {Blob} file blob data received from event.dataTransfer object * @return {XMLHttpRequest} request object which sends the file */ inlineAttachment.prototype.uploadFile = function(file, id) { var me = this, formData = new FormData(), xhr = new XMLHttpRequest(), id = id, settings = this.settings, extension = settings.defaultExtension; if (typeof settings.setupFormData === 'function') { settings.setupFormData(formData, file); } // Attach the file. If coming from clipboard, add a default filename (only works in Chrome for now) // http://stackoverflow.com/questions/6664967/how-to-give-a-blob-uploaded-as-formdata-a-file-name if (file.name) { var fileNameMatches = file.name.match(/\.(.+)$/); if (fileNameMatches) { extension = fileNameMatches[1]; } } var remoteFilename = "image-" + Date.now() + "." + extension; if (typeof settings.remoteFilename === 'function') { remoteFilename = settings.remoteFilename(file); } formData.append(settings.uploadFieldName, file, remoteFilename); // Append the extra parameters to the formdata if (typeof settings.extraParams === "object") { for (var key in settings.extraParams) { if (settings.extraParams.hasOwnProperty(key)) { formData.append(key, settings.extraParams[key]); } } } xhr.open('POST', settings.uploadUrl); // Add any available extra headers if (typeof settings.extraHeaders === "object") { for (var header in settings.extraHeaders) { if (settings.extraHeaders.hasOwnProperty(header)) { xhr.setRequestHeader(header, settings.extraHeaders[header]); } } } xhr.onload = function() { // If HTTP status is OK or Created if (xhr.status === 200 || xhr.status === 201) { me.onFileUploadResponse(xhr, id); } else { me.onFileUploadError(xhr, id); } }; if (settings.beforeFileUpload(xhr) !== false) { xhr.send(formData); } return xhr; }; /** * Returns if the given file is allowed to handle * * @param {File} clipboard data file */ inlineAttachment.prototype.isFileAllowed = function(file) { if (this.settings.allowedTypes.indexOf('*') === 0){ return true; } else { return this.settings.allowedTypes.indexOf(file.type) >= 0; } }; /** * Handles upload response * * @param {XMLHttpRequest} xhr * @return {Void} */ inlineAttachment.prototype.onFileUploadResponse = function(xhr, id) { if (this.settings.onFileUploadResponse.call(this, xhr) !== false) { var result = JSON.parse(xhr.responseText), filename = result[this.settings.jsonFieldName]; if (result && filename) { var replacements = []; var string = this.settings.progressText.replace(this.filenameTag, id); var lines = this.editor.getValue().split('\n'); var newValue = this.settings.urlText.replace(this.filenameTag, filename); for(var i = 0; i < lines.length; i++) { var ch = lines[i].indexOf(string); if(ch != -1) replacements.push({replacement:newValue, from:{line:i, ch:ch}, to:{line:i, ch:ch + string.length}}); } for(var i = 0; i < replacements.length; i++) this.editor.replaceRange(replacements[i]); } } }; /** * Called when a file has failed to upload * * @param {XMLHttpRequest} xhr * @return {Void} */ inlineAttachment.prototype.onFileUploadError = function(xhr, id) { if (this.settings.onFileUploadError.call(this, xhr) !== false) { var replacements = []; var string = this.settings.progressText.replace(this.filenameTag, id); var lines = this.editor.getValue().split('\n'); for(var i = 0; i < lines.length; i++) { var ch = lines[i].indexOf(this.lastValue); if(ch != -1) replacements.push({replacement:"", from:{line:i, ch:ch}, to:{line:i, ch:ch + string.length}}); } for(var i = 0; i < replacements.length; i++) this.editor.replaceRange(replacements[i]); } }; /** * Called when a file has been inserted, either by drop or paste * * @param {File} file * @return {Void} */ inlineAttachment.prototype.onFileInserted = function(file, id) { if (this.settings.onFileReceived.call(this, file) !== false) { this.lastValue = this.settings.progressText.replace(this.filenameTag, id); this.editor.insertValue(this.lastValue + "\n"); } }; /** * Called when a paste event occured * @param {Event} e * @return {Boolean} if the event was handled */ inlineAttachment.prototype.onPaste = function(e) { var result = false, clipboardData = e.clipboardData, items; if (typeof clipboardData === "object") { items = clipboardData.items || clipboardData.files || []; for (var i = 0; i < items.length; i++) { var item = items[i]; if (this.isFileAllowed(item)) { result = true; var id = ID(); var file = item.getAsFile(); if (file !== null) { this.onFileInserted(file, id); this.uploadFile(file, id); } } } } if (result) { e.preventDefault(); } return result; }; /** * Called when a drop event occures * @param {Event} e * @return {Boolean} if the event was handled */ inlineAttachment.prototype.onDrop = function(e) { var result = false; for (var i = 0; i < e.dataTransfer.files.length; i++) { var file = e.dataTransfer.files[i]; if (this.isFileAllowed(file)) { result = true; var id = ID(); this.onFileInserted(file, id); this.uploadFile(file, id); } } return result; }; window.inlineAttachment = inlineAttachment; })(document, window); // Generate unique IDs for use as pseudo-private/protected names. // Similar in concept to // . // // The goals of this function are twofold: // // * Provide a way to generate a string guaranteed to be unique when compared // to other strings generated by this function. // * Make the string complex enough that it is highly unlikely to be // accidentally duplicated by hand (this is key if you're using `ID` // as a private/protected name on an object). // // Use: // // var privateName = ID(); // var o = { 'public': 'foo' }; // o[privateName] = 'bar'; var ID = function () { // Math.random should be unique because of its seeding algorithm. // Convert it to base 36 (numbers + letters), and grab the first 9 characters // after the decimal. return '_' + Math.random().toString(36).substr(2, 9); }; ================================================ FILE: public/vendor/jquery-textcomplete/jquery.textcomplete.js ================================================ /*! * jQuery.textcomplete * * Repository: https://github.com/yuku-t/jquery-textcomplete * License: MIT (https://github.com/yuku-t/jquery-textcomplete/blob/master/LICENSE) * Author: Yuku Takahashi */ if (typeof jQuery === 'undefined') { throw new Error('jQuery.textcomplete requires jQuery'); } +function ($) { 'use strict'; var warn = function (message) { if (console.warn) { console.warn(message); } }; $.fn.textcomplete = function (strategies, option) { var args = Array.prototype.slice.call(arguments); return this.each(function () { var $this = $(this); var completer = $this.data('textComplete'); if (!completer) { completer = new $.fn.textcomplete.Completer(this, option || {}); $this.data('textComplete', completer); } if (typeof strategies === 'string') { if (!completer) return; args.shift() completer[strategies].apply(completer, args); if (strategies === 'destroy') { $this.removeData('textComplete'); } } else { // For backward compatibility. // TODO: Remove at v0.4 $.each(strategies, function (obj) { $.each(['header', 'footer', 'placement', 'maxCount'], function (name) { if (obj[name]) { completer.option[name] = obj[name]; warn(name + 'as a strategy param is deprecated. Use option.'); delete obj[name]; } }); }); completer.register($.fn.textcomplete.Strategy.parse(strategies)); } }); }; }(jQuery); +function ($) { 'use strict'; // Exclusive execution control utility. // // func - The function to be locked. It is executed with a function named // `free` as the first argument. Once it is called, additional // execution are ignored until the free is invoked. Then the last // ignored execution will be replayed immediately. // // Examples // // var lockedFunc = lock(function (free) { // setTimeout(function { free(); }, 1000); // It will be free in 1 sec. // console.log('Hello, world'); // }); // lockedFunc(); // => 'Hello, world' // lockedFunc(); // none // lockedFunc(); // none // // 1 sec past then // // => 'Hello, world' // lockedFunc(); // => 'Hello, world' // lockedFunc(); // none // // Returns a wrapped function. var lock = function (func) { var locked, queuedArgsToReplay; return function () { // Convert arguments into a real array. var args = Array.prototype.slice.call(arguments); if (locked) { // Keep a copy of this argument list to replay later. // OK to overwrite a previous value because we only replay // the last one. queuedArgsToReplay = args; return; } locked = true; var self = this; args.unshift(function replayOrFree() { if (queuedArgsToReplay) { // Other request(s) arrived while we were locked. // Now that the lock is becoming available, replay // the latest such request, then call back here to // unlock (or replay another request that arrived // while this one was in flight). var replayArgs = queuedArgsToReplay; queuedArgsToReplay = undefined; replayArgs.unshift(replayOrFree); func.apply(self, replayArgs); } else { locked = false; } }); func.apply(this, args); }; }; var isString = function (obj) { return Object.prototype.toString.call(obj) === '[object String]'; }; var uniqueId = 0; function Completer(element, option) { this.$el = $(element); this.id = 'textcomplete' + uniqueId++; this.strategies = []; this.views = []; this.option = $.extend({}, Completer._getDefaults(), option); if (!this.$el.is('input[type=text]') && !this.$el.is('textarea') && !element.isContentEditable && element.contentEditable != 'true') { throw new Error('textcomplete must be called on a Textarea or a ContentEditable.'); } if (element === document.activeElement) { // element has already been focused. Initialize view objects immediately. this.initialize() } else { // Initialize view objects lazily. var self = this; this.$el.one('focus.' + this.id, function () { self.initialize(); }); } } Completer._getDefaults = function () { if (!Completer.DEFAULTS) { Completer.DEFAULTS = { appendTo: $('body'), zIndex: '100' }; } return Completer.DEFAULTS; } $.extend(Completer.prototype, { // Public properties // ----------------- id: null, option: null, strategies: null, adapter: null, dropdown: null, $el: null, // Public methods // -------------- initialize: function () { var element = this.$el.get(0); // Initialize view objects. this.dropdown = new $.fn.textcomplete.Dropdown(element, this, this.option); this.dropdown.upSideDown = false; var Adapter, viewName; if (this.option.adapter) { Adapter = this.option.adapter; } else { if (this.$el.is('textarea') || this.$el.is('input[type=text]')) { viewName = typeof element.selectionEnd === 'number' ? 'Textarea' : 'IETextarea'; } else { viewName = 'ContentEditable'; } Adapter = $.fn.textcomplete[viewName]; } this.adapter = new Adapter(element, this, this.option); }, destroy: function () { this.$el.off('.' + this.id); if (this.adapter) { this.adapter.destroy(); } if (this.dropdown) { this.dropdown.destroy(); } this.$el = this.adapter = this.dropdown = null; }, // Invoke textcomplete. trigger: function (text, skipUnchangedTerm) { if (!this.dropdown) { this.initialize(); } text != null || (text = this.adapter.getTextFromHeadToCaret()); var searchQuery = this._extractSearchQuery(text); if (searchQuery.length) { var term = searchQuery[1]; // Ignore shift-key, ctrl-key and so on. if (skipUnchangedTerm && this._term === term) { return; } this._term = term; this.fire('textComplete:beforeSearch'); this._search.apply(this, searchQuery); this.fire('textComplete:afterSearch'); } else { this._term = null; this.dropdown.deactivate(); } }, fire: function (eventName) { var args = Array.prototype.slice.call(arguments, 1); this.$el.trigger(eventName, args); return this; }, register: function (strategies) { Array.prototype.push.apply(this.strategies, strategies); }, // Insert the value into adapter view. It is called when the dropdown is clicked // or selected. // // value - The selected element of the array callbacked from search func. // strategy - The Strategy object. select: function (value, strategy) { this.adapter.select(value, strategy); this.fire('change').fire('textComplete:select', value, strategy); this.adapter.focus(); }, // Private properties // ------------------ _clearAtNext: true, _term: null, // Private methods // --------------- // Parse the given text and extract the first matching strategy. // // Returns an array including the strategy, the query term and the match // object if the text matches an strategy; otherwise returns an empty array. _extractSearchQuery: function (text) { for (var i = 0; i < this.strategies.length; i++) { var strategy = this.strategies[i]; var context = strategy.context(text); if (context || context === '') { if (isString(context)) { text = context; } var cursor = editor.getCursor(); var line = editor.getLine(cursor.line); var linematch = line.match(strategy.match); if(linematch) { text = line.slice(0, cursor.ch); var textmatch = text.match(strategy.match); if (textmatch) { return [strategy, textmatch[strategy.index], textmatch]; } } } } return [] }, // Call the search method of selected strategy.. _search: lock(function (free, strategy, term, match) { var self = this; strategy.search(term, function (data, stillSearching) { if (!self.dropdown.shown) { self.dropdown.activate(); self.dropdown.setPosition(self.adapter.getCaretPosition()); } if (self._clearAtNext) { // The first callback in the current lock. self.dropdown.clear(); self._clearAtNext = false; } self.dropdown.render(self._zip(data, strategy)); if (!stillSearching) { // The last callback in the current lock. free(); self._clearAtNext = true; // Call dropdown.clear at the next time. } }, match); }), // Build a parameter for Dropdown#render. // // Examples // // this._zip(['a', 'b'], 's'); // //=> [{ value: 'a', strategy: 's' }, { value: 'b', strategy: 's' }] _zip: function (data, strategy) { return $.map(data, function (value) { return { value: value, strategy: strategy }; }); } }); $.fn.textcomplete.Completer = Completer; }(jQuery); +function ($) { 'use strict'; var include = function (zippedData, datum) { var i, elem; var idProperty = datum.strategy.idProperty for (i = 0; i < zippedData.length; i++) { elem = zippedData[i]; if (elem.strategy !== datum.strategy) continue; if (idProperty) { if (elem.value[idProperty] === datum.value[idProperty]) return true; } else { if (elem.value === datum.value) return true; } } return false; }; var dropdownViews = {}; /* $(document).on('mousedown', function (e) { var id = e.originalEvent && e.originalEvent.keepTextCompleteDropdown; $.each(dropdownViews, function (key, view) { if (key !== id) { view.deactivate(); } }); }); */ // Dropdown view // ============= // Construct Dropdown object. // // element - Textarea or contenteditable element. function Dropdown(element, completer, option) { this.$el = Dropdown.findOrCreateElement(option); this.completer = completer; this.id = completer.id + 'dropdown'; this._data = []; // zipped data. this.$inputEl = $(element); this.option = option; this.tap = false; // Override setPosition method. if (option.listPosition) { this.setPosition = option.listPosition; } if (option.height) { this.$el.height(option.height); } var self = this; $.each(['maxCount', 'placement', 'footer', 'header', 'className'], function (_i, name) { if (option[name] != null) { self[name] = option[name]; } }); this._bindEvents(element); dropdownViews[this.id] = this; } $.extend(Dropdown, { // Class methods // ------------- findOrCreateElement: function (option) { var $parent = option.appendTo; if (!($parent instanceof $)) { $parent = $($parent); } var $el = $parent.children('.dropdown-menu') if (!$el.length) { $el = $('').css({ display: 'none', left: 0, position: 'absolute', zIndex: option.zIndex }).appendTo($parent); } return $el; } }); $.extend(Dropdown.prototype, { // Public properties // ----------------- $el: null, // jQuery object of ul.dropdown-menu element. $inputEl: null, // jQuery object of target textarea. completer: null, footer: null, header: null, id: null, maxCount: 10, placement: '', shown: false, data: [], // Shown zipped data. className: '', // Public methods // -------------- destroy: function () { // Don't remove $el because it may be shared by several textcompletes. this.deactivate(); this.$el.off('.' + this.id); this.$inputEl.off('.' + this.id); this.clear(); this.$el = this.$inputEl = this.completer = null; delete dropdownViews[this.id] }, render: function (zippedData) { var contentsHtml = this._buildContents(zippedData); var unzippedData = $.map(this.data, function (d) { return d.value; }); if (this.data.length) { this._renderHeader(unzippedData); this._renderFooter(unzippedData); if (contentsHtml) { this._renderContents(contentsHtml); this._activateIndexedItem(); } this._setScroll(); } else if (this.shown) { this.deactivate(); } }, setPosition: function (position) { this.$el.css(this._applyPlacement(position)); // Make the dropdown fixed if the input is also fixed // This can't be done during init, as textcomplete may be used on multiple elements on the same page // Because the same dropdown is reused behind the scenes, we need to recheck every time the dropdown is showed var position = 'absolute'; // Check if input or one of its parents has positioning we need to care about this.$inputEl.add(this.$inputEl.parents()).each(function() { if($(this).css('position') === 'absolute') // The element has absolute positioning, so it's all OK return false; if($(this).css('position') === 'fixed') { position = 'fixed'; return false; } }); this.$el.css({ position: position }); // Update positioning return this; }, clear: function () { this.$el.html(''); this.data = []; this._index = 0; this._$header = this._$footer = null; }, activate: function () { if (!this.shown) { this.clear(); this.$el.show(); if (this.className) { this.$el.addClass(this.className); } this.completer.fire('textComplete:show'); this.shown = true; } return this; }, deactivate: function () { if (this.shown) { this.$el.hide(); if (this.className) { this.$el.removeClass(this.className); } this.completer.fire('textComplete:hide'); this.shown = false; } return this; }, isUp: function (e) { return e.keyCode === 38 || (e.ctrlKey && e.keyCode === 80); // UP, Ctrl-P }, isDown: function (e) { return e.keyCode === 40 || (e.ctrlKey && e.keyCode === 78); // DOWN, Ctrl-N }, isEnter: function (e) { var modifiers = e.ctrlKey || e.altKey || e.metaKey || e.shiftKey; return !modifiers && (e.keyCode === 13 || e.keyCode === 9 || (this.option.completeOnSpace === true && e.keyCode === 32)) // ENTER, TAB }, isPageup: function (e) { return e.keyCode === 33; // PAGEUP }, isPagedown: function (e) { return e.keyCode === 34; // PAGEDOWN }, isEscape: function (e) { return e.keyCode === 27; // ESCAPE }, // Private properties // ------------------ _data: null, // Currently shown zipped data. _index: null, _$header: null, _$footer: null, // Private methods // --------------- _bindEvents: function () { this.$inputEl.on('blur', $.proxy(this.deactivate, this)); this.$el.on('touchstart.' + this.id, '.textcomplete-item', $.proxy(function(e) { this.tap = true; }, this)); this.$el.on('touchmove.' + this.id, '.textcomplete-item', $.proxy(function(e) { this.tap = false; }, this)); this.$el.on('touchend.' + this.id, '.textcomplete-item', $.proxy(function(e) { if(e.cancelable && this.tap) { this._onClick(e); } }, this)); this.$el.on('mousedown.' + this.id, '.textcomplete-item', $.proxy(this._onClick, this)); this.$el.on('mouseover.' + this.id, '.textcomplete-item', $.proxy(this._onMouseover, this)); this.$inputEl.on('keydown.' + this.id, $.proxy(this._onKeydown, this)); }, _onClick: function (e) { var $el = $(e.target); e.stopPropagation(); e.preventDefault(); e.originalEvent.keepTextCompleteDropdown = this.id; if (!$el.hasClass('textcomplete-item')) { $el = $el.closest('.textcomplete-item'); } var datum = this.data[parseInt($el.data('index'), 10)]; this.completer.select(datum.value, datum.strategy); var self = this; // Deactive at next tick to allow other event handlers to know whether // the dropdown has been shown or not. setTimeout(function () { self.deactivate(); }, 0); }, // Activate hovered item. _onMouseover: function (e) { var $el = $(e.target); e.preventDefault(); if (!$el.hasClass('textcomplete-item')) { $el = $el.closest('.textcomplete-item'); } this._index = parseInt($el.data('index'), 10); this._activateIndexedItem(); }, _onKeydown: function (e) { if (!this.shown) { return; } if (this.isUp(e)) { e.preventDefault(); if(this.upSideDown) this._down(); else this._up(); } else if (this.isDown(e)) { e.preventDefault(); if(this.upSideDown) this._up(); else this._down(); } else if (this.isEnter(e)) { e.preventDefault(); this._enter(); } else if (this.isPageup(e)) { e.preventDefault(); this._pageup(); } else if (this.isPagedown(e)) { e.preventDefault(); this._pagedown(); } else if (this.isEscape(e)) { e.preventDefault(); this.deactivate(); } }, _up: function () { if (this._index === 0) { this._index = this.data.length - 1; } else { this._index -= 1; } this._activateIndexedItem(); this._setScroll(); }, _down: function () { if (this._index === this.data.length - 1) { this._index = 0; } else { this._index += 1; } this._activateIndexedItem(); this._setScroll(); }, _enter: function () { var datum = this.data[parseInt(this._getActiveElement().data('index'), 10)]; this.completer.select(datum.value, datum.strategy); this.deactivate(); }, _pageup: function () { var target = 0; var threshold = this._getActiveElement().position().top - this.$el.innerHeight(); this.$el.children().each(function (i) { if ($(this).position().top + $(this).outerHeight() > threshold) { target = i; return false; } }); this._index = target; this._activateIndexedItem(); this._setScroll(); }, _pagedown: function () { var target = this.data.length - 1; var threshold = this._getActiveElement().position().top + this.$el.innerHeight(); this.$el.children().each(function (i) { if ($(this).position().top > threshold) { target = i; return false } }); this._index = target; this._activateIndexedItem(); this._setScroll(); }, _activateIndexedItem: function () { this.$el.find('.textcomplete-item.active').removeClass('active'); this._getActiveElement().addClass('active'); }, _getActiveElement: function () { return this.$el.children('.textcomplete-item[data-index=' + this._index + ']'); }, _setScroll: function () { var $activeEl = this._getActiveElement(); var itemTop = $activeEl.position().top; var itemHeight = $activeEl.outerHeight(); var visibleHeight = this.$el.innerHeight(); var visibleTop = this.$el.scrollTop(); if (this._index === 0 || this._index == this.data.length - 1 || itemTop < 0) { this.$el.scrollTop(itemTop + visibleTop); } else if (itemTop + itemHeight > visibleHeight) { this.$el.scrollTop(itemTop + itemHeight + visibleTop - visibleHeight); } }, _buildContents: function (zippedData) { var datum, i, index; var item = []; var html = ''; for (i = 0; i < zippedData.length; i++) { if (this.data.length === this.maxCount) break; datum = zippedData[i]; if (include(this.data, datum)) { continue; } index = this.data.length; this.data.push(datum); item.push(datum.strategy.template(datum.value)); } for (i = 0; i < item.length; i++) { html += '
  • '; html += item[i]; html += '
  • '; } return html; }, _renderHeader: function (unzippedData) { if (this.header) { if (!this._$header) { this._$header = $('
  • ').prependTo(this.$el); } var html = $.isFunction(this.header) ? this.header(unzippedData) : this.header; this._$header.html(html); } }, _renderFooter: function (unzippedData) { if (this.footer) { if (!this._$footer) { this._$footer = $('').appendTo(this.$el); } var html = $.isFunction(this.footer) ? this.footer(unzippedData) : this.footer; this._$footer.html(html); } }, _renderContents: function (html) { if (this._$footer) { this._$footer.before(html); } else { this.$el.append(html); } }, _applyPlacement: function (position) { // If the 'placement' option set to 'top', move the position above the element. if (this.placement.indexOf('top') !== -1) { // Overwrite the position object to set the 'bottom' property instead of the top. position = { top: 'auto', bottom: this.$el.parent().height() - position.top + position.lineHeight, left: position.left }; } else { position.bottom = 'auto'; delete position.lineHeight; } if (this.placement.indexOf('absleft') !== -1) { position.left = 0; } else if (this.placement.indexOf('absright') !== -1) { position.right = 0; position.left = 'auto'; } return position; } }); $.fn.textcomplete.Dropdown = Dropdown; }(jQuery); +function ($) { 'use strict'; // Memoize a search function. var memoize = function (func) { var memo = {}; return function (term, callback) { if (memo[term]) { callback(memo[term]); } else { func.call(this, term, function (data) { memo[term] = (memo[term] || []).concat(data); callback.apply(null, arguments); }); } }; }; function Strategy(options) { $.extend(this, options); if (this.cache) { this.search = memoize(this.search); } } Strategy.parse = function (optionsArray) { return $.map(optionsArray, function (options) { return new Strategy(options); }); }; $.extend(Strategy.prototype, { // Public properties // ----------------- // Required match: null, replace: null, search: null, // Optional cache: false, context: function () { return true; }, index: 2, template: function (obj) { return obj; }, idProperty: null }); $.fn.textcomplete.Strategy = Strategy; }(jQuery); +function ($) { 'use strict'; var now = Date.now || function () { return new Date().getTime(); }; // Returns a function, that, as long as it continues to be invoked, will not // be triggered. The function will be called after it stops being called for // `wait` msec. // // This utility function was originally implemented at Underscore.js. var debounce = function (func, wait) { var timeout, args, context, timestamp, result; var later = function () { var last = now() - timestamp; if (last < wait) { timeout = setTimeout(later, wait - last); } else { timeout = null; result = func.apply(context, args); context = args = null; } }; return function () { context = this; args = arguments; timestamp = now(); if (!timeout) { timeout = setTimeout(later, wait); } return result; }; }; function Adapter () {} $.extend(Adapter.prototype, { // Public properties // ----------------- id: null, // Identity. completer: null, // Completer object which creates it. el: null, // Textarea element. $el: null, // jQuery object of the textarea. option: null, // Public methods // -------------- initialize: function (element, completer, option) { this.el = element; this.$el = $(element); this.id = completer.id + this.constructor.name; this.completer = completer; this.option = option; this.lastCurosr = null; if (this.option.debounce) { this._onKeyup = debounce(this._onKeyup, this.option.debounce); } this._bindEvents(); }, destroy: function () { this.$el.off('.' + this.id); // Remove all event handlers. this.$el = this.el = this.completer = null; }, // Update the element with the given value and strategy. // // value - The selected object. It is one of the item of the array // which was callbacked from the search function. // strategy - The Strategy associated with the selected value. select: function (/* value, strategy */) { throw new Error('Not implemented'); }, // Returns the caret's relative coordinates from body's left top corner. // // FIXME: Calculate the left top corner of `this.option.appendTo` element. getCaretPosition: function () { if ($('.CodeMirror-cursor').length > 0) { var position = $('.CodeMirror-cursor').position(); var menu = $('.cursor-menu .dropdown-menu'); var offsetLeft = parseFloat(menu.attr('data-offset-left')); var offsetTop = parseFloat(menu.attr('data-offset-top')); position.left += offsetLeft; position.top += offsetTop; return position; } else { var position = this._getCaretRelativePosition(); var offset = this.$el.offset(); // Calculate the left top corner of `this.option.appendTo` element. var $parent = this.option.appendTo; if ($parent) { if (!($parent instanceof $)) { $parent = $($parent); } var parentOffset = $parent.offsetParent().offset(); offset.top -= parentOffset.top; offset.left -= parentOffset.left; } position.top += offset.top; position.left += offset.left; return position; } }, // Focus on the element. focus: function () { this.$el.focus(); }, // Private methods // --------------- _bindEvents: function () { editor.on('cursorActivity', $.proxy(this._onKeyup, this)); $('.CodeMirror').on('touchend.' + this.id, $.proxy(this._onKeyup, this)); $('.CodeMirror').on('mouseup.' + this.id, $.proxy(this._onKeyup, this)); $(editor.getInputField()).on('focus', $.proxy(this._onKeyup, this)); this.$el.on('keyup.' + this.id, $.proxy(this._onKeyup, this)); }, _onKeyup: function (e) { var focus = (e.type == 'focus'); var cursor = editor.getCursor(); var samePos = (cursor == this.lastCursor); if (this._skipSearch(e)) { return; } this.completer.trigger(this.getTextFromHeadToCaret(), focus ? false : samePos); this.lastCursor = cursor; }, // Suppress searching if it returns true. _skipSearch: function (clickEvent) { switch (clickEvent.keyCode) { case 13: // ENTER case 40: // DOWN case 38: // UP return true; } if (clickEvent.ctrlKey) switch (clickEvent.keyCode) { case 78: // Ctrl-N case 80: // Ctrl-P return true; } } }); $.fn.textcomplete.Adapter = Adapter; }(jQuery); +function ($) { 'use strict'; // Textarea adapter // ================ // // Managing a textarea. It doesn't know a Dropdown. function Textarea(element, completer, option) { this.initialize(element, completer, option); } Textarea.DIV_PROPERTIES = { left: -9999, position: 'absolute', top: 0, whiteSpace: 'pre-wrap' } Textarea.COPY_PROPERTIES = [ 'border-width', 'font-family', 'font-size', 'font-style', 'font-variant', 'font-weight', 'height', 'letter-spacing', 'word-spacing', 'line-height', 'text-decoration', 'text-align', 'width', 'padding-top', 'padding-right', 'padding-bottom', 'padding-left', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left', 'border-style', 'box-sizing', 'tab-size' ]; $.extend(Textarea.prototype, $.fn.textcomplete.Adapter.prototype, { // Public methods // -------------- // Update the textarea with the given value and strategy. select: function (value, strategy) { /* var pre = this.getTextFromHeadToCaret(); var post = this.el.value.substring(this.el.selectionEnd); var newSubstr = strategy.replace(value); if ($.isArray(newSubstr)) { post = newSubstr[1] + post; newSubstr = newSubstr[0]; } pre = pre.replace(strategy.match, newSubstr); this.$el.val(pre + post); this.el.selectionStart = this.el.selectionEnd = pre.length; */ var cursor = editor.getCursor(); var pre = editor.getLine(cursor.line).slice(0, cursor.ch); var match = pre.match(strategy.match); if (!match) return; pre = match[0]; var newSubstr = strategy.replace(value); newSubstr = pre.replace(strategy.match, newSubstr); editor.operation(function() { var selections = editor.listSelections(); var bias = match.index - cursor.ch; for (var i = 0, l = selections.length; i < l; i++) { var sel = selections[i]; var start = { line: sel.head.line, ch: sel.head.ch + bias }; var end = { line: sel.head.line, ch: sel.head.ch + bias + match[0].length }; editor.replaceRange(newSubstr, start, end, "+input"); } }); if(strategy.done) strategy.done(); }, // Private methods // --------------- // Returns the caret's relative coordinates from textarea's left top corner. // // Browser native API does not provide the way to know the position of // caret in pixels, so that here we use a kind of hack to accomplish // the aim. First of all it puts a dummy div element and completely copies // the textarea's style to the element, then it inserts the text and a // span element into the textarea. // Consequently, the span element's position is the thing what we want. _getCaretRelativePosition: function () { var dummyDiv = $('
    ').css(this._copyCss()) .text(this.getTextFromHeadToCaret()); var span = $('').text('.').appendTo(dummyDiv); this.$el.before(dummyDiv); var position = span.position(); position.top += span.height() - this.$el.scrollTop(); position.lineHeight = span.height(); dummyDiv.remove(); return position; }, _copyCss: function () { return $.extend({ // Set 'scroll' if a scrollbar is being shown; otherwise 'auto'. overflow: this.el.scrollHeight > this.el.offsetHeight ? 'scroll' : 'auto' }, Textarea.DIV_PROPERTIES, this._getStyles()); }, _getStyles: (function ($) { var color = $('
    ').css(['color']).color; if (typeof color !== 'undefined') { return function () { return this.$el.css(Textarea.COPY_PROPERTIES); }; } else { // jQuery < 1.8 return function () { var $el = this.$el; var styles = {}; $.each(Textarea.COPY_PROPERTIES, function (i, property) { styles[property] = $el.css(property); }); return styles; }; } })($), getTextFromHeadToCaret: function () { return this.el.value.substring(0, this.el.selectionEnd); } }); $.fn.textcomplete.Textarea = Textarea; }(jQuery); +function ($) { 'use strict'; var sentinelChar = '吶'; function IETextarea(element, completer, option) { this.initialize(element, completer, option); $('' + sentinelChar + '').css({ position: 'absolute', top: -9999, left: -9999 }).insertBefore(element); } $.extend(IETextarea.prototype, $.fn.textcomplete.Textarea.prototype, { // Public methods // -------------- select: function (value, strategy) { var pre = this.getTextFromHeadToCaret(); var post = this.el.value.substring(pre.length); var newSubstr = strategy.replace(value); if ($.isArray(newSubstr)) { post = newSubstr[1] + post; newSubstr = newSubstr[0]; } pre = pre.replace(strategy.match, newSubstr); this.$el.val(pre + post); this.el.focus(); var range = this.el.createTextRange(); range.collapse(true); range.moveEnd('character', pre.length); range.moveStart('character', pre.length); range.select(); }, getTextFromHeadToCaret: function () { this.el.focus(); var range = document.selection.createRange(); range.moveStart('character', -this.el.value.length); var arr = range.text.split(sentinelChar) return arr.length === 1 ? arr[0] : arr[1]; } }); $.fn.textcomplete.IETextarea = IETextarea; }(jQuery); // NOTE: TextComplete plugin has contenteditable support but it does not work // fine especially on old IEs. // Any pull requests are REALLY welcome. +function ($) { 'use strict'; // ContentEditable adapter // ======================= // // Adapter for contenteditable elements. function ContentEditable (element, completer, option) { this.initialize(element, completer, option); } $.extend(ContentEditable.prototype, $.fn.textcomplete.Adapter.prototype, { // Public methods // -------------- // Update the content with the given value and strategy. // When an dropdown item is selected, it is executed. select: function (value, strategy) { var pre = this.getTextFromHeadToCaret(); var sel = window.getSelection() var range = sel.getRangeAt(0); var selection = range.cloneRange(); selection.selectNodeContents(range.startContainer); var content = selection.toString(); var post = content.substring(range.startOffset); var newSubstr = strategy.replace(value); if ($.isArray(newSubstr)) { post = newSubstr[1] + post; newSubstr = newSubstr[0]; } pre = pre.replace(strategy.match, newSubstr); range.selectNodeContents(range.startContainer); range.deleteContents(); var node = document.createTextNode(pre + post); range.insertNode(node); range.setStart(node, pre.length); range.collapse(true); sel.removeAllRanges(); sel.addRange(range); }, // Private methods // --------------- // Returns the caret's relative position from the contenteditable's // left top corner. // // Examples // // this._getCaretRelativePosition() // //=> { top: 18, left: 200, lineHeight: 16 } // // Dropdown's position will be decided using the result. _getCaretRelativePosition: function () { var range = window.getSelection().getRangeAt(0).cloneRange(); var node = document.createElement('span'); range.insertNode(node); range.selectNodeContents(node); range.deleteContents(); var $node = $(node); var position = $node.offset(); position.left -= this.$el.offset().left; position.top += $node.height() - this.$el.offset().top; position.lineHeight = $node.height(); $node.remove(); var dir = this.$el.attr('dir') || this.$el.css('direction'); if (dir === 'rtl') { position.left -= this.listView.$el.width(); } return position; }, // Returns the string between the first character and the caret. // Completer will be triggered with the result for start autocompleting. // // Example // // // Suppose the html is 'hello wor|ld' and | is the caret. // this.getTextFromHeadToCaret() // // => ' wor' // not 'hello wor' getTextFromHeadToCaret: function () { var range = window.getSelection().getRangeAt(0); var selection = range.cloneRange(); selection.selectNodeContents(range.startContainer); return selection.toString().substring(0, range.startOffset); } }); $.fn.textcomplete.ContentEditable = ContentEditable; }(jQuery); ================================================ FILE: public/vendor/md-toc.js ================================================ /* eslint-env browser, jquery */ /** * md-toc.js v1.0.2 * https://github.com/yijian166/md-toc.js * * Adapted to accept data attributes */ (function (window) { function Toc (id, options) { this.el = document.getElementById(id) if (!this.el) return this.options = options || {} this.tocLevel = parseInt(options.level) || 0 this.tocClass = options['class'] || 'toc' this.ulClass = options['ulClass'] this.tocTop = parseInt(options.top) || 0 this.elChilds = this.el.children this.process = options['process'] this.data = options.data || {} if (!this.elChilds.length) return this._init() } Toc.prototype._init = function () { this._collectTitleElements() this._createTocContent() this._showToc() } Toc.prototype._collectTitleElements = function () { this._elTitlesNames = [] this.elTitleElements = [] for (var i = 1; i < 6; i++) { if (this.el.getElementsByTagName('h' + i).length) { this._elTitlesNames.push('h' + i) } } this._elTitlesNames.length = this._elTitlesNames.length > this.tocLevel ? this.tocLevel : this._elTitlesNames.length for (var j = 0; j < this.elChilds.length; j++) { this._elChildName = this.elChilds[j].tagName.toLowerCase() if (this._elTitlesNames.toString().match(this._elChildName)) { this.elTitleElements.push(this.elChilds[j]) } } } Toc.prototype._createTocContent = function recursiveToc(level = 0, titleElements = [], titleNames = [], ulClass = undefined, index = 0) { // Inititalize our elements from the toc object // which is only available on level 0 if (level === 0) { titleElements = this.elTitleElements titleNames = this._elTitlesNames ulClass = this.ulClass } // No need to do anything for an empty ToC if (!titleElements.length) return var content = '/gm, '') var id = element.getAttribute('id') if (!id) { element.setAttribute('id', 'tip' + ++index) id = '#tip' + index } else { id = '#' + id } if (openTag) { content += '\n' openTag = false } content += '
  • ' + elementText + '' // Reset recursion. We need it for the next subsections recurse = false openTag = true // Check if the current element has a lower level than ours, if so, we have to go down the rabbithole! } else if (!recurse && titleNames.indexOf(elementTag.toLowerCase()) > level) { recurse = true if (!openTag) { content += '
  • ' openTag = true } // This element is for the lower lever, we have to re-add it before we send the list down there. titleElements.unshift(element) // Let's call ourself and get to the next level content += recursiveToc(level + 1, titleElements, titleNames, ulClass, index) } else { // When we end up here, met a higher level element // This is not our business so back into the list with the element and let's end this loop titleElements.unshift(element) break } } if (openTag) { content += '
  • \n' } content += '\n' // Set ToC content of the level 0 everything else pass things to the upper level! if (level === 0) { this.tocContent = content } else { return content } } Toc.prototype._showToc = function () { this.toc = document.createElement('div') this.toc.innerHTML = this.tocContent this.toc.setAttribute('class', this.tocClass) if (this.data.tocDepth) { this.toc.dataset.tocDepth = this.data.tocDepth } if (!this.options.targetId) { this.el.appendChild(this.toc) } else { document.getElementById(this.options.targetId).appendChild(this.toc) } var self = this if (this.tocTop > -1) { window.onscroll = function () { var t = document.documentElement.scrollTop || document.body.scrollTop if (t < self.tocTop) { self.toc.setAttribute('style', 'position:absolute;top:' + self.tocTop + 'px;') } else { self.toc.setAttribute('style', 'position:fixed;top:10px;') } } } } window.Toc = Toc })(window) ================================================ FILE: public/vendor/ot/ajax-adapter.js ================================================ /*global ot, $ */ ot.AjaxAdapter = (function () { 'use strict'; function AjaxAdapter (path, ownUserName, revision) { if (path[path.length - 1] !== '/') { path += '/'; } this.path = path; this.ownUserName = ownUserName; this.majorRevision = revision.major || 0; this.minorRevision = revision.minor || 0; this.poll(); } AjaxAdapter.prototype.renderRevisionPath = function () { return 'revision/' + this.majorRevision + '-' + this.minorRevision; }; AjaxAdapter.prototype.handleResponse = function (data) { var i; var operations = data.operations; for (i = 0; i < operations.length; i++) { if (operations[i].user === this.ownUserName) { this.trigger('ack'); } else { this.trigger('operation', operations[i].operation); } } if (operations.length > 0) { this.majorRevision += operations.length; this.minorRevision = 0; } var events = data.events; if (events) { for (i = 0; i < events.length; i++) { var user = events[i].user; if (user === this.ownUserName) { continue; } switch (events[i].event) { case 'joined': this.trigger('set_name', user, user); break; case 'left': this.trigger('client_left', user); break; case 'selection': this.trigger('selection', user, events[i].selection); break; } } this.minorRevision += events.length; } var users = data.users; if (users) { delete users[this.ownUserName]; this.trigger('clients', users); } if (data.revision) { this.majorRevision = data.revision.major; this.minorRevision = data.revision.minor; } }; AjaxAdapter.prototype.poll = function () { var self = this; $.ajax({ url: this.path + this.renderRevisionPath(), type: 'GET', dataType: 'json', timeout: 5000, success: function (data) { self.handleResponse(data); self.poll(); }, error: function () { setTimeout(function () { self.poll(); }, 500); } }); }; AjaxAdapter.prototype.sendOperation = function (revision, operation, selection) { if (revision !== this.majorRevision) { throw new Error("Revision numbers out of sync"); } var self = this; $.ajax({ url: this.path + this.renderRevisionPath(), type: 'POST', data: JSON.stringify({ operation: operation, selection: selection }), contentType: 'application/json', processData: false, success: function (data) {}, error: function () { setTimeout(function () { self.sendOperation(revision, operation, selection); }, 500); } }); }; AjaxAdapter.prototype.sendSelection = function (obj) { $.ajax({ url: this.path + this.renderRevisionPath() + '/selection', type: 'POST', data: JSON.stringify(obj), contentType: 'application/json', processData: false, timeout: 1000 }); }; AjaxAdapter.prototype.registerCallbacks = function (cb) { this.callbacks = cb; }; AjaxAdapter.prototype.trigger = function (event) { var args = Array.prototype.slice.call(arguments, 1); var action = this.callbacks && this.callbacks[event]; if (action) { action.apply(this, args); } }; return AjaxAdapter; })(); ================================================ FILE: public/vendor/ot/client.js ================================================ // translation of https://github.com/djspiewak/cccp/blob/master/agent/src/main/scala/com/codecommit/cccp/agent/state.scala if (typeof ot === 'undefined') { var ot = {}; } ot.Client = (function (global) { 'use strict'; // Client constructor function Client (revision) { this.revision = revision; // the next expected revision number this.setState(synchronized_); // start state } Client.prototype.setState = function (state) { this.state = state; }; // Call this method when the user changes the document. Client.prototype.applyClient = function (operation) { this.setState(this.state.applyClient(this, operation)); }; // Call this method with a new operation from the server Client.prototype.applyServer = function (revision, operation) { this.setState(this.state.applyServer(this, revision, operation)); }; Client.prototype.applyOperations = function (head, operations) { this.setState(this.state.applyOperations(this, head, operations)); }; Client.prototype.serverAck = function (revision) { this.setState(this.state.serverAck(this, revision)); }; Client.prototype.serverReconnect = function () { if (typeof this.state.resend === 'function') { this.state.resend(this); } }; // Transforms a selection from the latest known server state to the current // client state. For example, if we get from the server the information that // another user's cursor is at position 3, but the server hasn't yet received // our newest operation, an insertion of 5 characters at the beginning of the // document, the correct position of the other user's cursor in our current // document is 8. Client.prototype.transformSelection = function (selection) { return this.state.transformSelection(selection); }; // Override this method. Client.prototype.sendOperation = function (revision, operation) { throw new Error("sendOperation must be defined in child class"); }; // Override this method. Client.prototype.applyOperation = function (operation) { throw new Error("applyOperation must be defined in child class"); }; // In the 'Synchronized' state, there is no pending operation that the client // has sent to the server. function Synchronized () {} Client.Synchronized = Synchronized; Synchronized.prototype.applyClient = function (client, operation) { // When the user makes an edit, send the operation to the server and // switch to the 'AwaitingConfirm' state client.sendOperation(client.revision, operation); return new AwaitingConfirm(operation); }; Synchronized.prototype.applyServer = function (client, revision, operation) { if (revision - client.revision > 1) { throw new Error("Invalid revision."); } client.revision = revision; // When we receive a new operation from the server, the operation can be // simply applied to the current document client.applyOperation(operation); return this; }; Synchronized.prototype.serverAck = function (client, revision) { throw new Error("There is no pending operation."); }; // Nothing to do because the latest server state and client state are the same. Synchronized.prototype.transformSelection = function (x) { return x; }; // Singleton var synchronized_ = new Synchronized(); // In the 'AwaitingConfirm' state, there's one operation the client has sent // to the server and is still waiting for an acknowledgement. function AwaitingConfirm (outstanding) { // Save the pending operation this.outstanding = outstanding; } Client.AwaitingConfirm = AwaitingConfirm; AwaitingConfirm.prototype.applyClient = function (client, operation) { // When the user makes an edit, don't send the operation immediately, // instead switch to 'AwaitingWithBuffer' state return new AwaitingWithBuffer(this.outstanding, operation); }; AwaitingConfirm.prototype.applyServer = function (client, revision, operation) { if (revision - client.revision > 1) { throw new Error("Invalid revision."); } client.revision = revision; // This is another client's operation. Visualization: // // /\ // this.outstanding / \ operation // / \ // \ / // pair[1] \ / pair[0] (new outstanding) // (can be applied \/ // to the client's // current document) var pair = operation.constructor.transform(this.outstanding, operation); client.applyOperation(pair[1]); return new AwaitingConfirm(pair[0]); }; AwaitingConfirm.prototype.serverAck = function (client, revision) { if (revision - client.revision > 1) { return new Stale(this.outstanding, client, revision).getOperations(); } client.revision = revision; // The client's operation has been acknowledged // => switch to synchronized state return synchronized_; }; AwaitingConfirm.prototype.transformSelection = function (selection) { return selection.transform(this.outstanding); }; AwaitingConfirm.prototype.resend = function (client) { // The confirm didn't come because the client was disconnected. // Now that it has reconnected, we resend the outstanding operation. client.sendOperation(client.revision, this.outstanding); }; // In the 'AwaitingWithBuffer' state, the client is waiting for an operation // to be acknowledged by the server while buffering the edits the user makes function AwaitingWithBuffer (outstanding, buffer) { // Save the pending operation and the user's edits since then this.outstanding = outstanding; this.buffer = buffer; } Client.AwaitingWithBuffer = AwaitingWithBuffer; AwaitingWithBuffer.prototype.applyClient = function (client, operation) { // Compose the user's changes onto the buffer var newBuffer = this.buffer.compose(operation); return new AwaitingWithBuffer(this.outstanding, newBuffer); }; AwaitingWithBuffer.prototype.applyServer = function (client, revision, operation) { if (revision - client.revision > 1) { throw new Error("Invalid revision."); } client.revision = revision; // Operation comes from another client // // /\ // this.outstanding / \ operation // / \ // /\ / // this.buffer / \* / pair1[0] (new outstanding) // / \/ // \ / // pair2[1] \ / pair2[0] (new buffer) // the transformed \/ // operation -- can // be applied to the // client's current // document // // * pair1[1] var transform = operation.constructor.transform; var pair1 = transform(this.outstanding, operation); var pair2 = transform(this.buffer, pair1[1]); client.applyOperation(pair2[1]); return new AwaitingWithBuffer(pair1[0], pair2[0]); }; AwaitingWithBuffer.prototype.serverAck = function (client, revision) { if (revision - client.revision > 1) { return new StaleWithBuffer(this.outstanding, this.buffer, client, revision).getOperations(); } client.revision = revision; // The pending operation has been acknowledged // => send buffer client.sendOperation(client.revision, this.buffer); return new AwaitingConfirm(this.buffer); }; AwaitingWithBuffer.prototype.transformSelection = function (selection) { return selection.transform(this.outstanding).transform(this.buffer); }; AwaitingWithBuffer.prototype.resend = function (client) { // The confirm didn't come because the client was disconnected. // Now that it has reconnected, we resend the outstanding operation. client.sendOperation(client.revision, this.outstanding); }; function Stale(acknowlaged, client, revision) { this.acknowlaged = acknowlaged; this.client = client; this.revision = revision; } Client.Stale = Stale; Stale.prototype.applyClient = function (client, operation) { return new StaleWithBuffer(this.acknowlaged, operation, client, this.revision); }; Stale.prototype.applyServer = function (client, revision, operation) { throw new Error("Ignored server-side change."); }; Stale.prototype.applyOperations = function (client, head, operations) { var transform = this.acknowlaged.constructor.transform; for (var i = 0; i < operations.length; i++) { var op = ot.TextOperation.fromJSON(operations[i]); var pair = transform(this.acknowlaged, op); client.applyOperation(pair[1]); this.acknowlaged = pair[0]; } client.revision = this.revision; return synchronized_; }; Stale.prototype.serverAck = function (client, revision) { throw new Error("There is no pending operation."); }; Stale.prototype.transformSelection = function (selection) { return selection; }; Stale.prototype.getOperations = function () { this.client.getOperations(this.client.revision, this.revision - 1); // acknowlaged is the one at revision return this; }; function StaleWithBuffer(acknowlaged, buffer, client, revision) { this.acknowlaged = acknowlaged; this.buffer = buffer; this.client = client; this.revision = revision; } Client.StaleWithBuffer = StaleWithBuffer; StaleWithBuffer.prototype.applyClient = function (client, operation) { var buffer = this.buffer.compose(operation); return new StaleWithBuffer(this.acknowlaged, buffer, client, this.revision); }; StaleWithBuffer.prototype.applyServer = function (client, revision, operation) { throw new Error("Ignored server-side change."); }; StaleWithBuffer.prototype.applyOperations = function (client, head, operations) { var transform = this.acknowlaged.constructor.transform; for (var i = 0; i < operations.length; i++) { var op = ot.TextOperation.fromJSON(operations[i]); var pair1 = transform(this.acknowlaged, op); var pair2 = transform(this.buffer, pair1[1]); client.applyOperation(pair2[1]); this.acknowlaged = pair1[0]; this.buffer = pair2[0]; } client.revision = this.revision; client.sendOperation(client.revision, this.buffer); return new AwaitingConfirm(this.buffer); }; StaleWithBuffer.prototype.serverAck = function (client, revision) { throw new Error("There is no pending operation."); }; StaleWithBuffer.prototype.transformSelection = function (selection) { return selection; }; StaleWithBuffer.prototype.getOperations = function () { this.client.getOperations(this.client.revision, this.revision - 1); // acknowlaged is the one at revision return this; }; return Client; }(this)); if (typeof module === 'object') { module.exports = ot.Client; } ================================================ FILE: public/vendor/ot/codemirror-adapter.js ================================================ /*global ot */ ot.CodeMirrorAdapter = (function (global) { 'use strict'; var TextOperation = ot.TextOperation; var Selection = ot.Selection; function CodeMirrorAdapter(cm) { this.cm = cm; this.ignoreNextChange = false; this.changeInProgress = false; this.selectionChanged = false; bind(this, 'onChanges'); bind(this, 'onChange'); bind(this, 'onCursorActivity'); bind(this, 'onFocus'); bind(this, 'onBlur'); cm.on('changes', this.onChanges); cm.on('change', this.onChange); cm.on('cursorActivity', this.onCursorActivity); cm.on('focus', this.onFocus); cm.on('blur', this.onBlur); } // Removes all event listeners from the CodeMirror instance. CodeMirrorAdapter.prototype.detach = function () { this.cm.off('changes', this.onChanges); this.cm.off('change', this.onChange); this.cm.off('cursorActivity', this.onCursorActivity); this.cm.off('focus', this.onFocus); this.cm.off('blur', this.onBlur); }; function cmpPos(a, b) { if (a.line < b.line) { return -1; } if (a.line > b.line) { return 1; } if (a.ch < b.ch) { return -1; } if (a.ch > b.ch) { return 1; } return 0; } function posEq(a, b) { return cmpPos(a, b) === 0; } function posLe(a, b) { return cmpPos(a, b) <= 0; } function minPos(a, b) { return posLe(a, b) ? a : b; } function maxPos(a, b) { return posLe(a, b) ? b : a; } function codemirrorDocLength(doc) { return doc.indexFromPos({ line: doc.lastLine(), ch: 0 }) + doc.getLine(doc.lastLine()).length; } // Converts a CodeMirror change array (as obtained from the 'changes' event // in CodeMirror v4) or single change or linked list of changes (as returned // by the 'change' event in CodeMirror prior to version 4) into a // TextOperation and its inverse and returns them as a two-element array. CodeMirrorAdapter.operationFromCodeMirrorChanges = function (changes, doc) { // Approach: Replay the changes, beginning with the most recent one, and // construct the operation and its inverse. We have to convert the position // in the pre-change coordinate system to an index. We have a method to // convert a position in the coordinate system after all changes to an index, // namely CodeMirror's `indexFromPos` method. We can use the information of // a single change object to convert a post-change coordinate system to a // pre-change coordinate system. We can now proceed inductively to get a // pre-change coordinate system for all changes in the linked list. // A disadvantage of this approach is its complexity `O(n^2)` in the length // of the linked list of changes. var docEndLength = codemirrorDocLength(doc); var operation = new TextOperation().retain(docEndLength); var inverse = new TextOperation().retain(docEndLength); var indexFromPos = function (pos) { return doc.indexFromPos(pos); }; function last(arr) { return arr[arr.length - 1]; } function sumLengths(strArr) { if (strArr.length === 0) { return 0; } var sum = 0; for (var i = 0; i < strArr.length; i++) { sum += strArr[i].length; } return sum + strArr.length - 1; } function updateIndexFromPos(indexFromPos, change) { return function (pos) { if (posLe(pos, change.from)) { return indexFromPos(pos); } if (posLe(change.to, pos)) { return indexFromPos({ line: pos.line + change.text.length - 1 - (change.to.line - change.from.line), ch: (change.to.line < pos.line) ? pos.ch : (change.text.length <= 1) ? pos.ch - (change.to.ch - change.from.ch) + sumLengths(change.text) : pos.ch - change.to.ch + last(change.text).length }) + sumLengths(change.removed) - sumLengths(change.text); } if (change.from.line === pos.line) { return indexFromPos(change.from) + pos.ch - change.from.ch; } return indexFromPos(change.from) + sumLengths(change.removed.slice(0, pos.line - change.from.line)) + 1 + pos.ch; }; } for (var i = changes.length - 1; i >= 0; i--) { var change = changes[i]; indexFromPos = updateIndexFromPos(indexFromPos, change); var fromIndex = indexFromPos(change.from); var restLength = docEndLength - fromIndex - sumLengths(change.text); operation = new TextOperation() .retain(fromIndex)['delete'](sumLengths(change.removed)) .insert(change.text.join('\n')) .retain(restLength) .compose(operation); inverse = inverse.compose(new TextOperation() .retain(fromIndex)['delete'](sumLengths(change.text)) .insert(change.removed.join('\n')) .retain(restLength) ); docEndLength += sumLengths(change.removed) - sumLengths(change.text); } return [operation, inverse]; }; // Singular form for backwards compatibility. CodeMirrorAdapter.operationFromCodeMirrorChange = CodeMirrorAdapter.operationFromCodeMirrorChanges; // Apply an operation to a CodeMirror instance. CodeMirrorAdapter.applyOperationToCodeMirror = function (operation, cm) { cm.operation(function () { var ops = operation.ops; var index = 0; // holds the current index into CodeMirror's content for (var i = 0, l = ops.length; i < l; i++) { var op = ops[i]; if (TextOperation.isRetain(op)) { index += op; } else if (TextOperation.isInsert(op)) { cm.replaceRange(op, cm.posFromIndex(index), null, 'ignoreHistory'); index += op.length; } else if (TextOperation.isDelete(op)) { var from = cm.posFromIndex(index); var to = cm.posFromIndex(index - op); cm.replaceRange('', from, to, 'ignoreHistory'); } } }); }; CodeMirrorAdapter.prototype.registerCallbacks = function (cb) { this.callbacks = cb; }; CodeMirrorAdapter.prototype.onChange = function () { // By default, CodeMirror's event order is the following: // 1. 'change', 2. 'cursorActivity', 3. 'changes'. // We want to fire the 'selectionChange' event after the 'change' event, // but need the information from the 'changes' event. Therefore, we detect // when a change is in progress by listening to the change event, setting // a flag that makes this adapter defer all 'cursorActivity' events. this.changeInProgress = true; }; CodeMirrorAdapter.prototype.onChanges = function (_, changes) { if (!this.ignoreNextChange) { var pair = CodeMirrorAdapter.operationFromCodeMirrorChanges(changes, this.cm); this.trigger('change', pair[0], pair[1]); } if (this.selectionChanged) { this.trigger('selectionChange'); } this.changeInProgress = false; this.ignoreNextChange = false; }; CodeMirrorAdapter.prototype.onCursorActivity = CodeMirrorAdapter.prototype.onFocus = function () { if (this.changeInProgress) { this.selectionChanged = true; } else { this.trigger('selectionChange'); } }; CodeMirrorAdapter.prototype.onBlur = function () { if (!this.cm.somethingSelected()) { this.trigger('blur'); } }; CodeMirrorAdapter.prototype.getValue = function () { return this.cm.getValue(); }; CodeMirrorAdapter.prototype.getSelection = function () { var cm = this.cm; var selectionList = cm.listSelections(); var ranges = []; for (var i = 0; i < selectionList.length; i++) { ranges[i] = new Selection.Range( cm.indexFromPos(selectionList[i].anchor), cm.indexFromPos(selectionList[i].head) ); } return new Selection(ranges); }; CodeMirrorAdapter.prototype.setSelection = function (selection) { var ranges = []; for (var i = 0; selection && i < selection.ranges.length; i++) { var range = selection.ranges[i]; ranges[i] = { anchor: this.cm.posFromIndex(range.anchor), head: this.cm.posFromIndex(range.head) }; } this.cm.setSelections(ranges); }; var addStyleRule = (function () { var added = {}; var styleElement = document.createElement('style'); document.documentElement.getElementsByTagName('head')[0].appendChild(styleElement); var styleSheet = styleElement.sheet; return function (css) { if (added[css]) { return; } added[css] = true; styleSheet.insertRule(css, (styleSheet.cssRules || styleSheet.rules).length); }; }()); CodeMirrorAdapter.prototype.setOtherCursor = function (position, color, clientId) { var cursorPos = this.cm.posFromIndex(position); var cursorCoords = this.cm.cursorCoords(cursorPos); var cursorEl = document.createElement('span'); cursorEl.className = 'other-client'; cursorEl.style.display = 'none'; /* cursorEl.style.padding = '0'; cursorEl.style.marginLeft = cursorEl.style.marginRight = '-1px'; cursorEl.style.borderLeftWidth = '2px'; cursorEl.style.borderLeftStyle = 'solid'; cursorEl.style.borderLeftColor = color; cursorEl.style.height = (cursorCoords.bottom - cursorCoords.top) * 0.9 + 'px'; cursorEl.style.zIndex = 0; */ cursorEl.setAttribute('data-clientid', clientId); return this.cm.setBookmark(cursorPos, { widget: cursorEl, insertLeft: true }); }; CodeMirrorAdapter.prototype.setOtherSelectionRange = function (range, color, clientId) { var match = /^#([0-9a-fA-F]{6})$/.exec(color); if (!match) { throw new Error("only six-digit hex colors are allowed."); } var selectionClassName = 'selection-' + match[1]; var rgbcolor = hex2rgb(color); var rule = '.' + selectionClassName + ' { background: rgba(' + rgbcolor.red + ',' + rgbcolor.green + ',' + rgbcolor.blue + ',0.2); }'; addStyleRule(rule); var anchorPos = this.cm.posFromIndex(range.anchor); var headPos = this.cm.posFromIndex(range.head); return this.cm.markText( minPos(anchorPos, headPos), maxPos(anchorPos, headPos), { className: selectionClassName } ); }; CodeMirrorAdapter.prototype.setOtherSelection = function (selection, color, clientId) { var selectionObjects = []; for (var i = 0; i < selection.ranges.length; i++) { var range = selection.ranges[i]; if (range.isEmpty()) { //selectionObjects[i] = this.setOtherCursor(range.head, color, clientId); } else { selectionObjects[i] = this.setOtherSelectionRange(range, color, clientId); } } return { clear: function () { for (var i = 0; i < selectionObjects.length; i++) { if (selectionObjects[i]) selectionObjects[i].clear(); } } }; }; CodeMirrorAdapter.prototype.trigger = function (event) { var args = Array.prototype.slice.call(arguments, 1); var action = this.callbacks && this.callbacks[event]; if (action) { action.apply(this, args); } }; CodeMirrorAdapter.prototype.applyOperation = function (operation) { if (!operation.isNoop()) { this.ignoreNextChange = true; } CodeMirrorAdapter.applyOperationToCodeMirror(operation, this.cm); }; CodeMirrorAdapter.prototype.registerUndo = function (undoFn) { this.cm.undo = undoFn; }; CodeMirrorAdapter.prototype.registerRedo = function (redoFn) { this.cm.redo = redoFn; }; // Throws an error if the first argument is falsy. Useful for debugging. function assert(b, msg) { if (!b) { throw new Error(msg || "assertion error"); } } // Bind a method to an object, so it doesn't matter whether you call // object.method() directly or pass object.method as a reference to another // function. function bind(obj, method) { var fn = obj[method]; obj[method] = function () { fn.apply(obj, arguments); }; } return CodeMirrorAdapter; }(this)); function hex2rgb(hex) { if (hex[0] == "#") hex = hex.substr(1); if (hex.length == 3) { var temp = hex; hex = ''; temp = /^([a-f0-9])([a-f0-9])([a-f0-9])$/i.exec(temp).slice(1); for (var i = 0; i < 3; i++) hex += temp[i] + temp[i]; } var triplets = /^([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})$/i.exec(hex).slice(1); return { red: parseInt(triplets[0], 16), green: parseInt(triplets[1], 16), blue: parseInt(triplets[2], 16) } } ================================================ FILE: public/vendor/ot/compress.sh ================================================ uglifyjs --compress --mangle --output ot.min.js \ ./text-operation.js \ ./selection.js \ ./wrapped-operation.js \ ./undo-manager.js \ ./client.js \ ./codemirror-adapter.js \ ./socketio-adapter.js \ ./ajax-adapter.js \ ./editor-client.js ================================================ FILE: public/vendor/ot/editor-client.js ================================================ /*global ot */ ot.EditorClient = (function () { 'use strict'; var Client = ot.Client; var Selection = ot.Selection; var UndoManager = ot.UndoManager; var TextOperation = ot.TextOperation; var WrappedOperation = ot.WrappedOperation; function SelfMeta (selectionBefore, selectionAfter) { this.selectionBefore = selectionBefore; this.selectionAfter = selectionAfter; } SelfMeta.prototype.invert = function () { return new SelfMeta(this.selectionAfter, this.selectionBefore); }; SelfMeta.prototype.compose = function (other) { return new SelfMeta(this.selectionBefore, other.selectionAfter); }; SelfMeta.prototype.transform = function (operation) { return new SelfMeta( (this.selectionBefore ? this.selectionBefore.transform(operation) : null), (this.selectionAfter ? this.selectionAfter.transform(operation) : null) ); }; function OtherMeta (clientId, selection) { this.clientId = clientId; this.selection = selection; } OtherMeta.fromJSON = function (obj) { return new OtherMeta( obj.clientId, obj.selection && Selection.fromJSON(obj.selection) ); }; OtherMeta.prototype.transform = function (operation) { return new OtherMeta( this.clientId, this.selection && this.selection.transform(operation) ); }; function OtherClient (id, listEl, editorAdapter, name, color, selection) { this.id = id; this.listEl = listEl; this.editorAdapter = editorAdapter; this.name = name; this.color = color; this.li = document.createElement('li'); if (name) { this.li.textContent = name; this.listEl.appendChild(this.li); } if(!color) this.setColor(name ? hueFromName(name) : Math.random()); else this.setForceColor(color); if (selection) { this.updateSelection(selection); } } OtherClient.prototype.setColor = function (hue) { this.hue = hue; this.color = hsl2hex(hue, 0.75, 0.5); this.lightColor = hsl2hex(hue, 0.5, 0.9); if (this.li) { this.li.style.color = this.color; } }; OtherClient.prototype.setForceColor = function (color) { this.hue = null; this.color = color; this.lightColor = color; if (this.li) { this.li.style.color = this.color; } }; OtherClient.prototype.setName = function (name) { if (this.name === name) { return; } this.name = name; this.li.textContent = name; if (!this.li.parentNode) { this.listEl.appendChild(this.li); } this.setColor(hueFromName(name)); }; OtherClient.prototype.updateSelection = function (selection) { this.removeSelection(); this.selection = selection; this.mark = this.editorAdapter.setOtherSelection( selection, selection.position === selection.selectionEnd ? this.color : this.lightColor, this.id ); }; OtherClient.prototype.remove = function () { if (this.li) { removeElement(this.li); } this.removeSelection(); }; OtherClient.prototype.removeSelection = function () { if (this.mark) { this.mark.clear(); this.mark = null; } }; function EditorClient (revision, clients, serverAdapter, editorAdapter) { Client.call(this, revision); this.serverAdapter = serverAdapter; this.editorAdapter = editorAdapter; this.undoManager = new UndoManager(); this.initializeClientList(); this.initializeClients(clients); var self = this; this.editorAdapter.registerCallbacks({ change: function (operation, inverse) { self.onChange(operation, inverse); }, selectionChange: function () { self.onSelectionChange(); }, blur: function () { self.onBlur(); } }); this.editorAdapter.registerUndo(function () { self.undo(); }); this.editorAdapter.registerRedo(function () { self.redo(); }); this.serverAdapter.registerCallbacks({ client_left: function (clientId) { self.onClientLeft(clientId); }, set_name: function (clientId, name) { self.getClientObject(clientId).setName(name); }, set_color: function (clientId, color) { self.getClientObject(clientId).setForceColor(color); }, ack: function (revision) { self.serverAck(revision); }, operation: function (revision, operation) { self.applyServer(revision, TextOperation.fromJSON(operation)); }, operations: function (head, operations) { self.applyOperations(head, operations); }, selection: function (clientId, selection) { if (selection) { self.getClientObject(clientId).updateSelection( self.transformSelection(Selection.fromJSON(selection)) ); } else { self.getClientObject(clientId).removeSelection(); } }, clients: function (clients) { var clientId; for (clientId in self.clients) { if (self.clients.hasOwnProperty(clientId) && !clients.hasOwnProperty(clientId)) { self.onClientLeft(clientId); } } for (clientId in clients) { if (clients.hasOwnProperty(clientId)) { var clientObject = self.getClientObject(clientId); if (clients[clientId].name) { clientObject.setName(clients[clientId].name); } var selection = clients[clientId].selection; if (selection) { self.clients[clientId].updateSelection( self.transformSelection(Selection.fromJSON(selection)) ); } else { self.clients[clientId].removeSelection(); } } } }, reconnect: function () { self.serverReconnect(); } }); } inherit(EditorClient, Client); EditorClient.prototype.addClient = function (clientId, clientObj) { this.clients[clientId] = new OtherClient( clientId, this.clientListEl, this.editorAdapter, clientObj.name || clientId, clientObj.color || null, clientObj.selection ? Selection.fromJSON(clientObj.selection) : null ); }; EditorClient.prototype.initializeClients = function (clients) { this.clients = {}; for (var clientId in clients) { if (clients.hasOwnProperty(clientId)) { this.addClient(clientId, clients[clientId]); } } }; EditorClient.prototype.getClientObject = function (clientId) { var client = this.clients[clientId]; if (client) { return client; } return this.clients[clientId] = new OtherClient( clientId, this.clientListEl, this.editorAdapter ); }; EditorClient.prototype.onClientLeft = function (clientId) { //console.log("User disconnected: " + clientId); var client = this.clients[clientId]; if (!client) { return; } client.remove(); delete this.clients[clientId]; }; EditorClient.prototype.initializeClientList = function () { this.clientListEl = document.createElement('ul'); }; EditorClient.prototype.applyUnredo = function (operation) { this.undoManager.add(operation.invert(this.editorAdapter.getValue())); this.editorAdapter.applyOperation(operation.wrapped); this.selection = operation.meta.selectionAfter; this.editorAdapter.setSelection(this.selection); this.applyClient(operation.wrapped); }; EditorClient.prototype.undo = function () { var self = this; if (!this.undoManager.canUndo()) { return; } this.undoManager.performUndo(function (o) { self.applyUnredo(o); }); }; EditorClient.prototype.redo = function () { var self = this; if (!this.undoManager.canRedo()) { return; } this.undoManager.performRedo(function (o) { self.applyUnredo(o); }); }; EditorClient.prototype.onChange = function (textOperation, inverse) { var selectionBefore = this.selection; this.updateSelection(); var meta = new SelfMeta(selectionBefore, this.selection); var operation = new WrappedOperation(textOperation, meta); var compose = this.undoManager.undoStack.length > 0 && inverse.shouldBeComposedWithInverted(last(this.undoManager.undoStack).wrapped); var inverseMeta = new SelfMeta(this.selection, selectionBefore); this.undoManager.add(new WrappedOperation(inverse, inverseMeta), compose); this.applyClient(textOperation); }; EditorClient.prototype.updateSelection = function () { this.selection = this.editorAdapter.getSelection(); }; EditorClient.prototype.onSelectionChange = function () { var oldSelection = this.selection; this.updateSelection(); if (oldSelection && this.selection.equals(oldSelection)) { return; } this.sendSelection(this.selection); }; EditorClient.prototype.onBlur = function () { this.selection = null; this.sendSelection(null); }; EditorClient.prototype.sendSelection = function (selection) { if (this.state instanceof Client.AwaitingWithBuffer) { return; } this.serverAdapter.sendSelection(selection); }; EditorClient.prototype.sendOperation = function (revision, operation) { this.serverAdapter.sendOperation(revision, operation.toJSON(), this.selection); }; EditorClient.prototype.getOperations = function (base, head) { this.serverAdapter.getOperations(base, head); }; EditorClient.prototype.applyOperation = function (operation) { this.editorAdapter.applyOperation(operation); this.updateSelection(); this.undoManager.transform(new WrappedOperation(operation, null)); }; function rgb2hex (r, g, b) { function digits (n) { var m = Math.round(255*n).toString(16); return m.length === 1 ? '0'+m : m; } return '#' + digits(r) + digits(g) + digits(b); } function hsl2hex (h, s, l) { if (s === 0) { return rgb2hex(l, l, l); } var var2 = l < 0.5 ? l * (1+s) : (l+s) - (s*l); var var1 = 2 * l - var2; var hue2rgb = function (hue) { if (hue < 0) { hue += 1; } if (hue > 1) { hue -= 1; } if (6*hue < 1) { return var1 + (var2-var1)*6*hue; } if (2*hue < 1) { return var2; } if (3*hue < 2) { return var1 + (var2-var1)*6*(2/3 - hue); } return var1; }; return rgb2hex(hue2rgb(h+1/3), hue2rgb(h), hue2rgb(h-1/3)); } function hueFromName (name) { var a = 1; for (var i = 0; i < name.length; i++) { a = 17 * (a+name.charCodeAt(i)) % 360; } return a/360; } // Set Const.prototype.__proto__ to Super.prototype function inherit (Const, Super) { function F () {} F.prototype = Super.prototype; Const.prototype = new F(); Const.prototype.constructor = Const; } function last (arr) { return arr[arr.length - 1]; } // Remove an element from the DOM. function removeElement (el) { if (el.parentNode) { el.parentNode.removeChild(el); } } return EditorClient; }()); ================================================ FILE: public/vendor/ot/selection.js ================================================ if (typeof ot === 'undefined') { // Export for browsers var ot = {}; } ot.Selection = (function (global) { 'use strict'; var TextOperation = global.ot ? global.ot.TextOperation : require('./text-operation'); // Range has `anchor` and `head` properties, which are zero-based indices into // the document. The `anchor` is the side of the selection that stays fixed, // `head` is the side of the selection where the cursor is. When both are // equal, the range represents a cursor. function Range (anchor, head) { this.anchor = anchor; this.head = head; } Range.fromJSON = function (obj) { return new Range(obj.anchor, obj.head); }; Range.prototype.equals = function (other) { return this.anchor === other.anchor && this.head === other.head; }; Range.prototype.isEmpty = function () { return this.anchor === this.head; }; Range.prototype.transform = function (other) { function transformIndex (index) { var newIndex = index; var ops = other.ops; for (var i = 0, l = other.ops.length; i < l; i++) { if (TextOperation.isRetain(ops[i])) { index -= ops[i]; } else if (TextOperation.isInsert(ops[i])) { newIndex += ops[i].length; } else { newIndex -= Math.min(index, -ops[i]); index += ops[i]; } if (index < 0) { break; } } return newIndex; } var newAnchor = transformIndex(this.anchor); if (this.anchor === this.head) { return new Range(newAnchor, newAnchor); } return new Range(newAnchor, transformIndex(this.head)); }; // A selection is basically an array of ranges. Every range represents a real // selection or a cursor in the document (when the start position equals the // end position of the range). The array must not be empty. function Selection (ranges) { this.ranges = ranges || []; } Selection.Range = Range; // Convenience method for creating selections only containing a single cursor // and no real selection range. Selection.createCursor = function (position) { return new Selection([new Range(position, position)]); }; Selection.fromJSON = function (obj) { var objRanges = obj.ranges || obj; for (var i = 0, ranges = []; i < objRanges.length; i++) { ranges[i] = Range.fromJSON(objRanges[i]); } return new Selection(ranges); }; Selection.prototype.equals = function (other) { if (this.position !== other.position) { return false; } if (this.ranges.length !== other.ranges.length) { return false; } // FIXME: Sort ranges before comparing them? for (var i = 0; i < this.ranges.length; i++) { if (!this.ranges[i].equals(other.ranges[i])) { return false; } } return true; }; Selection.prototype.somethingSelected = function () { for (var i = 0; i < this.ranges.length; i++) { if (!this.ranges[i].isEmpty()) { return true; } } return false; }; // Return the more current selection information. Selection.prototype.compose = function (other) { return other; }; // Update the selection with respect to an operation. Selection.prototype.transform = function (other) { for (var i = 0, newRanges = []; i < this.ranges.length; i++) { newRanges[i] = this.ranges[i].transform(other); } return new Selection(newRanges); }; return Selection; }(this)); // Export for CommonJS if (typeof module === 'object') { module.exports = ot.Selection; } ================================================ FILE: public/vendor/ot/socketio-adapter.js ================================================ /*global ot */ ot.SocketIOAdapter = (function () { 'use strict'; function SocketIOAdapter(socket) { this.socket = socket; var self = this; socket.on('client_left', function (clientId) { self.trigger('client_left', clientId); }); socket.on('set_name', function (clientId, name) { self.trigger('set_name', clientId, name); }); socket.on('set_color', function (clientId, color) { self.trigger('set_color', clientId, color); }); socket.on('ack', function (revision) { self.trigger('ack', revision); }); socket.on('operation', function (clientId, revision, operation, selection) { self.trigger('operation', revision, operation); self.trigger('selection', clientId, selection); }); socket.on('operations', function (head, operations) { self.trigger('operations', head, operations); }); socket.on('selection', function (clientId, selection) { self.trigger('selection', clientId, selection); }); socket.on('reconnect', function () { self.trigger('reconnect'); }); } SocketIOAdapter.prototype.sendOperation = function (revision, operation, selection) { this.socket.emit('operation', revision, operation, selection); }; SocketIOAdapter.prototype.sendSelection = function (selection) { this.socket.emit('selection', selection); }; SocketIOAdapter.prototype.getOperations = function (base, head) { this.socket.emit('get_operations', base, head); }; SocketIOAdapter.prototype.registerCallbacks = function (cb) { this.callbacks = cb; }; SocketIOAdapter.prototype.trigger = function (event) { var args = Array.prototype.slice.call(arguments, 1); var action = this.callbacks && this.callbacks[event]; if (action) { action.apply(this, args); } }; return SocketIOAdapter; }()); ================================================ FILE: public/vendor/ot/text-operation.js ================================================ if (typeof ot === 'undefined') { // Export for browsers var ot = {}; } ot.TextOperation = (function () { 'use strict'; // Constructor for new operations. function TextOperation () { if (!this || this.constructor !== TextOperation) { // => function was called without 'new' return new TextOperation(); } // When an operation is applied to an input string, you can think of this as // if an imaginary cursor runs over the entire string and skips over some // parts, deletes some parts and inserts characters at some positions. These // actions (skip/delete/insert) are stored as an array in the "ops" property. this.ops = []; // An operation's baseLength is the length of every string the operation // can be applied to. this.baseLength = 0; // The targetLength is the length of every string that results from applying // the operation on a valid input string. this.targetLength = 0; } TextOperation.prototype.equals = function (other) { if (this.baseLength !== other.baseLength) { return false; } if (this.targetLength !== other.targetLength) { return false; } if (this.ops.length !== other.ops.length) { return false; } for (var i = 0; i < this.ops.length; i++) { if (this.ops[i] !== other.ops[i]) { return false; } } return true; }; // Operation are essentially lists of ops. There are three types of ops: // // * Retain ops: Advance the cursor position by a given number of characters. // Represented by positive ints. // * Insert ops: Insert a given string at the current cursor position. // Represented by strings. // * Delete ops: Delete the next n characters. Represented by negative ints. var isRetain = TextOperation.isRetain = function (op) { return typeof op === 'number' && op > 0; }; var isInsert = TextOperation.isInsert = function (op) { return typeof op === 'string'; }; var isDelete = TextOperation.isDelete = function (op) { return typeof op === 'number' && op < 0; }; // After an operation is constructed, the user of the library can specify the // actions of an operation (skip/insert/delete) with these three builder // methods. They all return the operation for convenient chaining. // Skip over a given number of characters. TextOperation.prototype.retain = function (n) { if (typeof n !== 'number') { throw new Error("retain expects an integer"); } if (n === 0) { return this; } this.baseLength += n; this.targetLength += n; if (isRetain(this.ops[this.ops.length-1])) { // The last op is a retain op => we can merge them into one op. this.ops[this.ops.length-1] += n; } else { // Create a new op. this.ops.push(n); } return this; }; // Insert a string at the current position. TextOperation.prototype.insert = function (str) { if (typeof str !== 'string') { throw new Error("insert expects a string"); } if (str === '') { return this; } this.targetLength += str.length; var ops = this.ops; if (isInsert(ops[ops.length-1])) { // Merge insert op. ops[ops.length-1] += str; } else if (isDelete(ops[ops.length-1])) { // It doesn't matter when an operation is applied whether the operation // is delete(3), insert("something") or insert("something"), delete(3). // Here we enforce that in this case, the insert op always comes first. // This makes all operations that have the same effect when applied to // a document of the right length equal in respect to the `equals` method. if (isInsert(ops[ops.length-2])) { ops[ops.length-2] += str; } else { ops[ops.length] = ops[ops.length-1]; ops[ops.length-2] = str; } } else { ops.push(str); } return this; }; // Delete a string at the current position. TextOperation.prototype['delete'] = function (n) { if (typeof n === 'string') { n = n.length; } if (typeof n !== 'number') { throw new Error("delete expects an integer or a string"); } if (n === 0) { return this; } if (n > 0) { n = -n; } this.baseLength -= n; if (isDelete(this.ops[this.ops.length-1])) { this.ops[this.ops.length-1] += n; } else { this.ops.push(n); } return this; }; // Tests whether this operation has no effect. TextOperation.prototype.isNoop = function () { return this.ops.length === 0 || (this.ops.length === 1 && isRetain(this.ops[0])); }; // Pretty printing. TextOperation.prototype.toString = function () { // map: build a new array by applying a function to every element in an old // array. var map = Array.prototype.map || function (fn) { var arr = this; var newArr = []; for (var i = 0, l = arr.length; i < l; i++) { newArr[i] = fn(arr[i]); } return newArr; }; return map.call(this.ops, function (op) { if (isRetain(op)) { return "retain " + op; } else if (isInsert(op)) { return "insert '" + op + "'"; } else { return "delete " + (-op); } }).join(', '); }; // Converts operation into a JSON value. TextOperation.prototype.toJSON = function () { return this.ops; }; // Converts a plain JS object into an operation and validates it. TextOperation.fromJSON = function (ops) { var o = new TextOperation(); for (var i = 0, l = ops.length; i < l; i++) { var op = ops[i]; if (isRetain(op)) { o.retain(op); } else if (isInsert(op)) { o.insert(op); } else if (isDelete(op)) { o['delete'](op); } else { throw new Error("unknown operation: " + JSON.stringify(op)); } } return o; }; // Apply an operation to a string, returning a new string. Throws an error if // there's a mismatch between the input string and the operation. TextOperation.prototype.apply = function (str) { var operation = this; if (str.length !== operation.baseLength) { throw new Error("The operation's base length must be equal to the string's length."); } var newStr = [], j = 0; var strIndex = 0; var ops = this.ops; for (var i = 0, l = ops.length; i < l; i++) { var op = ops[i]; if (isRetain(op)) { if (strIndex + op > str.length) { throw new Error("Operation can't retain more characters than are left in the string."); } // Copy skipped part of the old string. newStr[j++] = str.slice(strIndex, strIndex + op); strIndex += op; } else if (isInsert(op)) { // Insert string. newStr[j++] = op; } else { // delete op strIndex -= op; } } if (strIndex !== str.length) { throw new Error("The operation didn't operate on the whole string."); } return newStr.join(''); }; // Computes the inverse of an operation. The inverse of an operation is the // operation that reverts the effects of the operation, e.g. when you have an // operation 'insert("hello "); skip(6);' then the inverse is 'delete("hello "); // skip(6);'. The inverse should be used for implementing undo. TextOperation.prototype.invert = function (str) { var strIndex = 0; var inverse = new TextOperation(); var ops = this.ops; for (var i = 0, l = ops.length; i < l; i++) { var op = ops[i]; if (isRetain(op)) { inverse.retain(op); strIndex += op; } else if (isInsert(op)) { inverse['delete'](op.length); } else { // delete op inverse.insert(str.slice(strIndex, strIndex - op)); strIndex -= op; } } return inverse; }; // Compose merges two consecutive operations into one operation, that // preserves the changes of both. Or, in other words, for each input string S // and a pair of consecutive operations A and B, // apply(apply(S, A), B) = apply(S, compose(A, B)) must hold. TextOperation.prototype.compose = function (operation2) { var operation1 = this; if (operation1.targetLength !== operation2.baseLength) { throw new Error("The base length of the second operation has to be the target length of the first operation"); } var operation = new TextOperation(); // the combined operation var ops1 = operation1.ops, ops2 = operation2.ops; // for fast access var i1 = 0, i2 = 0; // current index into ops1 respectively ops2 var op1 = ops1[i1++], op2 = ops2[i2++]; // current ops while (true) { // Dispatch on the type of op1 and op2 if (typeof op1 === 'undefined' && typeof op2 === 'undefined') { // end condition: both ops1 and ops2 have been processed break; } if (isDelete(op1)) { operation['delete'](op1); op1 = ops1[i1++]; continue; } if (isInsert(op2)) { operation.insert(op2); op2 = ops2[i2++]; continue; } if (typeof op1 === 'undefined') { throw new Error("Cannot compose operations: first operation is too short."); } if (typeof op2 === 'undefined') { throw new Error("Cannot compose operations: first operation is too long."); } if (isRetain(op1) && isRetain(op2)) { if (op1 > op2) { operation.retain(op2); op1 = op1 - op2; op2 = ops2[i2++]; } else if (op1 === op2) { operation.retain(op1); op1 = ops1[i1++]; op2 = ops2[i2++]; } else { operation.retain(op1); op2 = op2 - op1; op1 = ops1[i1++]; } } else if (isInsert(op1) && isDelete(op2)) { if (op1.length > -op2) { op1 = op1.slice(-op2); op2 = ops2[i2++]; } else if (op1.length === -op2) { op1 = ops1[i1++]; op2 = ops2[i2++]; } else { op2 = op2 + op1.length; op1 = ops1[i1++]; } } else if (isInsert(op1) && isRetain(op2)) { if (op1.length > op2) { operation.insert(op1.slice(0, op2)); op1 = op1.slice(op2); op2 = ops2[i2++]; } else if (op1.length === op2) { operation.insert(op1); op1 = ops1[i1++]; op2 = ops2[i2++]; } else { operation.insert(op1); op2 = op2 - op1.length; op1 = ops1[i1++]; } } else if (isRetain(op1) && isDelete(op2)) { if (op1 > -op2) { operation['delete'](op2); op1 = op1 + op2; op2 = ops2[i2++]; } else if (op1 === -op2) { operation['delete'](op2); op1 = ops1[i1++]; op2 = ops2[i2++]; } else { operation['delete'](op1); op2 = op2 + op1; op1 = ops1[i1++]; } } else { throw new Error( "This shouldn't happen: op1: " + JSON.stringify(op1) + ", op2: " + JSON.stringify(op2) ); } } return operation; }; function getSimpleOp (operation, fn) { var ops = operation.ops; var isRetain = TextOperation.isRetain; switch (ops.length) { case 1: return ops[0]; case 2: return isRetain(ops[0]) ? ops[1] : (isRetain(ops[1]) ? ops[0] : null); case 3: if (isRetain(ops[0]) && isRetain(ops[2])) { return ops[1]; } } return null; } function getStartIndex (operation) { if (isRetain(operation.ops[0])) { return operation.ops[0]; } return 0; } // When you use ctrl-z to undo your latest changes, you expect the program not // to undo every single keystroke but to undo your last sentence you wrote at // a stretch or the deletion you did by holding the backspace key down. This // This can be implemented by composing operations on the undo stack. This // method can help decide whether two operations should be composed. It // returns true if the operations are consecutive insert operations or both // operations delete text at the same position. You may want to include other // factors like the time since the last change in your decision. TextOperation.prototype.shouldBeComposedWith = function (other) { if (this.isNoop() || other.isNoop()) { return true; } var startA = getStartIndex(this), startB = getStartIndex(other); var simpleA = getSimpleOp(this), simpleB = getSimpleOp(other); if (!simpleA || !simpleB) { return false; } if (isInsert(simpleA) && isInsert(simpleB)) { return startA + simpleA.length === startB; } if (isDelete(simpleA) && isDelete(simpleB)) { // there are two possibilities to delete: with backspace and with the // delete key. return (startB - simpleB === startA) || startA === startB; } return false; }; // Decides whether two operations should be composed with each other // if they were inverted, that is // `shouldBeComposedWith(a, b) = shouldBeComposedWithInverted(b^{-1}, a^{-1})`. TextOperation.prototype.shouldBeComposedWithInverted = function (other) { if (this.isNoop() || other.isNoop()) { return true; } var startA = getStartIndex(this), startB = getStartIndex(other); var simpleA = getSimpleOp(this), simpleB = getSimpleOp(other); if (!simpleA || !simpleB) { return false; } if (isInsert(simpleA) && isInsert(simpleB)) { return startA + simpleA.length === startB || startA === startB; } if (isDelete(simpleA) && isDelete(simpleB)) { return startB - simpleB === startA; } return false; }; // Transform takes two operations A and B that happened concurrently and // produces two operations A' and B' (in an array) such that // `apply(apply(S, A), B') = apply(apply(S, B), A')`. This function is the // heart of OT. TextOperation.transform = function (operation1, operation2) { if (operation1.baseLength !== operation2.baseLength) { throw new Error("Both operations have to have the same base length"); } var operation1prime = new TextOperation(); var operation2prime = new TextOperation(); var ops1 = operation1.ops, ops2 = operation2.ops; var i1 = 0, i2 = 0; var op1 = ops1[i1++], op2 = ops2[i2++]; while (true) { // At every iteration of the loop, the imaginary cursor that both // operation1 and operation2 have that operates on the input string must // have the same position in the input string. if (typeof op1 === 'undefined' && typeof op2 === 'undefined') { // end condition: both ops1 and ops2 have been processed break; } // next two cases: one or both ops are insert ops // => insert the string in the corresponding prime operation, skip it in // the other one. If both op1 and op2 are insert ops, prefer op1. if (isInsert(op1)) { operation1prime.insert(op1); operation2prime.retain(op1.length); op1 = ops1[i1++]; continue; } if (isInsert(op2)) { operation1prime.retain(op2.length); operation2prime.insert(op2); op2 = ops2[i2++]; continue; } if (typeof op1 === 'undefined') { throw new Error("Cannot compose operations: first operation is too short."); } if (typeof op2 === 'undefined') { throw new Error("Cannot compose operations: first operation is too long."); } var minl; if (isRetain(op1) && isRetain(op2)) { // Simple case: retain/retain if (op1 > op2) { minl = op2; op1 = op1 - op2; op2 = ops2[i2++]; } else if (op1 === op2) { minl = op2; op1 = ops1[i1++]; op2 = ops2[i2++]; } else { minl = op1; op2 = op2 - op1; op1 = ops1[i1++]; } operation1prime.retain(minl); operation2prime.retain(minl); } else if (isDelete(op1) && isDelete(op2)) { // Both operations delete the same string at the same position. We don't // need to produce any operations, we just skip over the delete ops and // handle the case that one operation deletes more than the other. if (-op1 > -op2) { op1 = op1 - op2; op2 = ops2[i2++]; } else if (op1 === op2) { op1 = ops1[i1++]; op2 = ops2[i2++]; } else { op2 = op2 - op1; op1 = ops1[i1++]; } // next two cases: delete/retain and retain/delete } else if (isDelete(op1) && isRetain(op2)) { if (-op1 > op2) { minl = op2; op1 = op1 + op2; op2 = ops2[i2++]; } else if (-op1 === op2) { minl = op2; op1 = ops1[i1++]; op2 = ops2[i2++]; } else { minl = -op1; op2 = op2 + op1; op1 = ops1[i1++]; } operation1prime['delete'](minl); } else if (isRetain(op1) && isDelete(op2)) { if (op1 > -op2) { minl = -op2; op1 = op1 + op2; op2 = ops2[i2++]; } else if (op1 === -op2) { minl = op1; op1 = ops1[i1++]; op2 = ops2[i2++]; } else { minl = op1; op2 = op2 + op1; op1 = ops1[i1++]; } operation2prime['delete'](minl); } else { throw new Error("The two operations aren't compatible"); } } return [operation1prime, operation2prime]; }; return TextOperation; }()); // Export for CommonJS if (typeof module === 'object') { module.exports = ot.TextOperation; } ================================================ FILE: public/vendor/ot/undo-manager.js ================================================ if (typeof ot === 'undefined') { // Export for browsers var ot = {}; } ot.UndoManager = (function () { 'use strict'; var NORMAL_STATE = 'normal'; var UNDOING_STATE = 'undoing'; var REDOING_STATE = 'redoing'; // Create a new UndoManager with an optional maximum history size. function UndoManager (maxItems) { this.maxItems = maxItems || 50; this.state = NORMAL_STATE; this.dontCompose = false; this.undoStack = []; this.redoStack = []; } // Add an operation to the undo or redo stack, depending on the current state // of the UndoManager. The operation added must be the inverse of the last // edit. When `compose` is true, compose the operation with the last operation // unless the last operation was alread pushed on the redo stack or was hidden // by a newer operation on the undo stack. UndoManager.prototype.add = function (operation, compose) { if (this.state === UNDOING_STATE) { this.redoStack.push(operation); this.dontCompose = true; } else if (this.state === REDOING_STATE) { this.undoStack.push(operation); this.dontCompose = true; } else { var undoStack = this.undoStack; if (!this.dontCompose && compose && undoStack.length > 0) { undoStack.push(operation.compose(undoStack.pop())); } else { undoStack.push(operation); if (undoStack.length > this.maxItems) { undoStack.shift(); } } this.dontCompose = false; this.redoStack = []; } }; function transformStack (stack, operation) { var newStack = []; var Operation = operation.constructor; for (var i = stack.length - 1; i >= 0; i--) { var pair = Operation.transform(stack[i], operation); if (typeof pair[0].isNoop !== 'function' || !pair[0].isNoop()) { newStack.push(pair[0]); } operation = pair[1]; } return newStack.reverse(); } // Transform the undo and redo stacks against a operation by another client. UndoManager.prototype.transform = function (operation) { this.undoStack = transformStack(this.undoStack, operation); this.redoStack = transformStack(this.redoStack, operation); }; // Perform an undo by calling a function with the latest operation on the undo // stack. The function is expected to call the `add` method with the inverse // of the operation, which pushes the inverse on the redo stack. UndoManager.prototype.performUndo = function (fn) { this.state = UNDOING_STATE; if (this.undoStack.length === 0) { throw new Error("undo not possible"); } fn(this.undoStack.pop()); this.state = NORMAL_STATE; }; // The inverse of `performUndo`. UndoManager.prototype.performRedo = function (fn) { this.state = REDOING_STATE; if (this.redoStack.length === 0) { throw new Error("redo not possible"); } fn(this.redoStack.pop()); this.state = NORMAL_STATE; }; // Is the undo stack not empty? UndoManager.prototype.canUndo = function () { return this.undoStack.length !== 0; }; // Is the redo stack not empty? UndoManager.prototype.canRedo = function () { return this.redoStack.length !== 0; }; // Whether the UndoManager is currently performing an undo. UndoManager.prototype.isUndoing = function () { return this.state === UNDOING_STATE; }; // Whether the UndoManager is currently performing a redo. UndoManager.prototype.isRedoing = function () { return this.state === REDOING_STATE; }; return UndoManager; }()); // Export for CommonJS if (typeof module === 'object') { module.exports = ot.UndoManager; } ================================================ FILE: public/vendor/ot/wrapped-operation.js ================================================ if (typeof ot === 'undefined') { // Export for browsers var ot = {}; } ot.WrappedOperation = (function (global) { 'use strict'; // A WrappedOperation contains an operation and corresponing metadata. function WrappedOperation (operation, meta) { this.wrapped = operation; this.meta = meta; } WrappedOperation.prototype.apply = function () { return this.wrapped.apply.apply(this.wrapped, arguments); }; WrappedOperation.prototype.invert = function () { var meta = this.meta; return new WrappedOperation( this.wrapped.invert.apply(this.wrapped, arguments), meta && typeof meta === 'object' && typeof meta.invert === 'function' ? meta.invert.apply(meta, arguments) : meta ); }; // Copy all properties from source to target. function copy (source, target) { for (var key in source) { if (source.hasOwnProperty(key)) { target[key] = source[key]; } } } function composeMeta (a, b) { if (a && typeof a === 'object') { if (typeof a.compose === 'function') { return a.compose(b); } var meta = {}; copy(a, meta); copy(b, meta); return meta; } return b; } WrappedOperation.prototype.compose = function (other) { return new WrappedOperation( this.wrapped.compose(other.wrapped), composeMeta(this.meta, other.meta) ); }; function transformMeta (meta, operation) { if (meta && typeof meta === 'object') { if (typeof meta.transform === 'function') { return meta.transform(operation); } } return meta; } WrappedOperation.transform = function (a, b) { var transform = a.wrapped.constructor.transform; var pair = transform(a.wrapped, b.wrapped); return [ new WrappedOperation(pair[0], transformMeta(a.meta, b.wrapped)), new WrappedOperation(pair[1], transformMeta(b.meta, a.wrapped)) ]; }; return WrappedOperation; }(this)); // Export for CommonJS if (typeof module === 'object') { module.exports = ot.WrappedOperation; } ================================================ FILE: public/vendor/showup/showup.css ================================================ /* * Showup.js jQuery Plugin * http://github.com/jonschlinkert/showup * * Copyright (c) 2013 Jon Schlinkert, contributors * Licensed under the MIT License (MIT). */ /** * Docs navbar transitions effects */ .navbar-tall, .navbar-show { -webkit-transition: -webkit-transform .3s; -moz-transition: -moz-transform .3s; -o-transition: -o-transform .3s; transition: transform .3s; -webkit-transform: translate(0, 0); -ms-transform: translate(0, 0); transform: translate(0, 0); } .navbar-hide { -webkit-transition: -webkit-transform .2s; -moz-transition: -moz-transform .2s; -o-transition: -o-transform .2s; transition: transform .2s; -webkit-transform: translate(0, -60px); -ms-transform: translate(0, -60px); transform: translate(0, -60px); } .navbar-tall, .navbar-short, .navbar-tall .navbar-brand, .navbar-short .navbar-brand, .navbar-tall .navbar-nav > li > a, .navbar-short .navbar-nav > li > a { -webkit-transition: all 0.2s linear; transition: all 0.2s linear; } .navbar-short { min-height: 40px; } .navbar-short .navbar-brand { font-size: 16px; padding: 13px 15px 10px; } .navbar-short .navbar-nav > li > a { padding-top: 12px; padding-bottom: 12px; } .navbar-tall { min-height: 70px; } .navbar-tall .navbar-brand { font-size: 24px; padding: 25px 15px; } .navbar-tall .navbar-nav > li > a { padding-top: 25px; } /** * Docs Buttons */ /* Fixed button, bottom right */ .btn-fixed-bottom { position: fixed; bottom: 30px; display: none; z-index: 5; width: 40px; height: 40px; } /* Toggles navbar classes */ .btn-hide-show { margin-right: 10px; } /* Light theme */ .btn-light { color: #555; background-color: rgba(0, 0, 0,.1); } .btn-light:hover { color: #111; background-color: rgba(0, 0, 0,.25); } /* Dark theme */ .btn-dark { color: #fff; background-color: rgba(0, 0, 0,.5); } .btn-dark:hover { color: #fff; background-color: rgba(0, 0, 0,.9); } /* Buttons displayed throughout the content */ .btn-showup { position: relative; color: #fff; font-weight: normal; background-color: #463265; border-color: #3F2961; } .btn-showup:hover, .btn-showup:focus { color: #fff; outline: none; background-color: #39235A; border-color: #39235A; } ================================================ FILE: public/vendor/showup/showup.js ================================================ /* * Showup.js jQuery Plugin * http://github.com/jonschlinkert/showup * * Copyright (c) 2013 Jon Schlinkert, contributors * Licensed under the MIT License (MIT). */ (function( $ ) { $.fn.showUp = function(ele, options) { options = options || {}; var target = $(ele); var down = options.down || 'navbar-hide'; var up = options.up || 'navbar-show'; var btnHideShow = options.btnHideShow || '.btn-hide-show'; var hideOffset = options.offset || 60; var previousScroll = 0; var isHide = false; $(window).scroll(function () { checkScrollTop(); }); $(window).resize(function () { checkScrollTop(); }); $(window).mousewheel(function () { checkScrollTop(); }); function checkScrollTop() { target.clearQueue(); target.stop(); var currentScroll = $(this).scrollTop(); if (currentScroll > hideOffset && !target.hasClass('locked')) { if(Math.abs(previousScroll - currentScroll) < 50) return; if (currentScroll > previousScroll) { // Action on scroll down target.removeClass(up).addClass(down); } else if (currentScroll < previousScroll) { // Action on scroll up target.removeClass(down).addClass(up); } } else { target.removeClass(down).addClass(up); } previousScroll = $(this).scrollTop(); } // Toggle visibility of target on click $(btnHideShow).click(function () { if (target.hasClass(down)) { target.removeClass(down).addClass(up); } else { target.removeClass(up).addClass(down); } }); }; })( jQuery ); // TODO: make customizable $(document).ready(function () { var duration = 420; var showOffset = 220; var btnFixed = '.btn-fixed-bottom'; var btnToTopClass = '.back-to-top'; $(window).scroll(function () { if ($(this).scrollTop() > showOffset) { $(btnFixed).fadeIn(duration); } else { $(btnFixed).fadeOut(duration); } }); $(btnToTopClass).click(function (event) { event.preventDefault(); $('html, body').animate({ scrollTop: 0 }, duration); return false; }); }); ================================================ FILE: public/views/codimd/body.ejs ================================================ <%- include('../shared/refresh-modal') %> <%- include('../shared/signin-modal') %> <%- include('../shared/help-modal') %> <%- include('../shared/revision-modal') %> <%- include('../shared/pandoc-export-modal') %> ================================================ FILE: public/views/codimd/foot.ejs ================================================ <% if(useCDN) { %> <%- include('../build/index-scripts') %> <% } else { %> <%- include('../build/index-pack-scripts') %> <% } %> ================================================ FILE: public/views/codimd/footer.ejs ================================================ ================================================ FILE: public/views/codimd/head.ejs ================================================ <%= title %> <% if(useCDN) { %> <%- include('../build/index-header') %> <%- include('../shared/polyfill') %> <% } else { %> <%- include('../build/index-pack-header') %> <% } %> ================================================ FILE: public/views/codimd/header.ejs ================================================
    ================================================ FILE: public/views/codimd.ejs ================================================ <%- include('codimd/head') %> <%- include('codimd/header') %> <%- include('codimd/body') %> <%- include('codimd/footer') %> <%- include('codimd/foot') %> ================================================ FILE: public/views/error.ejs ================================================ <%- include('codimd/head') %> <%- include('codimd/header') %>

    <%- code %> <%- detail %> <%- msg %>

    <%- include('codimd/footer') %> ================================================ FILE: public/views/html.hbs ================================================ {{title}} {{{html}}} ================================================ FILE: public/views/includes/header.ejs ================================================ <% for (var css in htmlWebpackPlugin.files.css) { %> <% } %> ================================================ FILE: public/views/includes/scripts.ejs ================================================ <% for (var js in htmlWebpackPlugin.files.js) { %> <% } %> ================================================ FILE: public/views/index/body.ejs ================================================

    style="display:none;"<% } %>>

    CodiMD

    <%= __('Best way to write and share your knowledge in markdown.') %>

    <% if (infoMessage && infoMessage.length > 0) { %>
    <%= infoMessage %>
    <% } %> <% if (errorMessage && errorMessage.length > 0) { %>
    <%= errorMessage %>
    <% } %> <% if (authProviders.facebook || authProviders.twitter || authProviders.github|| authProviders.bitbucket || authProviders.gitlab || authProviders.mattermost || authProviders.dropbox || authProviders.google || authProviders.ldap || authProviders.saml || authProviders.oauth2 || authProviders.email) { %> <%= __('or') %> <% } %>
    style="display:none;"<% } %>>
    <%= __('Title') %> <%= __('Time') %>

      <%- __('Powered by %s', 'CodiMD') %> | <%= __('Releases') %> | <%= __('Source Code') %><% if(privacyStatement) { %> | <%= __('Privacy') %><% } %><% if(termsOfUse) { %> | <%= __('Terms of Use') %><% } %>

      <% if(privacyPolicyURL && privacyPolicyURL.length > 0) { %>

      <%= __('Privacy Policy') %>

      <% } %>
      <%- include('../shared/signin-modal') %> ================================================ FILE: public/views/index/foot.ejs ================================================ <% if(useCDN) { %> <%- include('../build/cover-scripts') %> <% } else { %> <%- include('../build/cover-pack-scripts') %> <% } %> ================================================ FILE: public/views/index/footer.ejs ================================================ ================================================ FILE: public/views/index/head.ejs ================================================ CodiMD - <%= __('Collaborative markdown notes') %> <% if(useCDN) { %> <%- include('../build/cover-header') %> <%- include('../shared/polyfill') %> <% } else { %> <%- include('../build/cover-pack-header') %> <% } %> ================================================ FILE: public/views/index/header.ejs ================================================ ================================================ FILE: public/views/index.ejs ================================================ <%- include('index/head') %> <%- include('index/header') %> <%- include('index/body') %> <%- include('index/footer') %> <%- include('index/foot') %> ================================================ FILE: public/views/pretty.ejs ================================================ <% if(typeof robots !== 'undefined' && robots) { %> <% } %> <% if(typeof image !== 'undefined' && image) { %> <% } %> <% if(typeof description !== 'undefined' && description) { %> <% } %> <%= title %> <% if(useCDN) { %> <%- include('build/pretty-header') %> <%- include('shared/polyfill') %> <% } else { %> <%- include('build/pretty-pack-header') %> <% } %>
      <% if(lastchangeuserprofile) { %> <% } else { %> <% } %>   <%- viewcount %> views
      <% if(ownerprofile && owner !== lastchangeuser) { %>  owned this note <% } %>
      <%= body %>
      <% if(typeof disqus !== 'undefined' && disqus) { %>
      <%- include('shared/disqus') %>
      <% } %> <% if(useCDN) { %> <%- include('build/pretty-scripts') %> <% } else { %> <%- include('build/pretty-pack-scripts') %> <% } %> <%- include('shared/ga') %> ================================================ FILE: public/views/shared/disqus.ejs ================================================
      ================================================ FILE: public/views/shared/ga.ejs ================================================ <% if(typeof GA !== 'undefined' && GA) { %> <% } %> ================================================ FILE: public/views/shared/help-modal.ejs ================================================ ================================================ FILE: public/views/shared/pandoc-export-modal.ejs ================================================ ================================================ FILE: public/views/shared/polyfill.ejs ================================================ ================================================ FILE: public/views/shared/refresh-modal.ejs ================================================ ================================================ FILE: public/views/shared/revision-modal.ejs ================================================ ================================================ FILE: public/views/shared/signin-modal.ejs ================================================ ================================================ FILE: public/views/slide.ejs ================================================ <% if(typeof robots !== 'undefined' && robots) { %> <% } %> <% if(typeof description !== 'undefined' && description) { %> <% } %> <%= title %> <% if(useCDN) { %> <%- include('build/slide-header') %> <%- include('shared/polyfill') %> <% } else { %> <%- include('build/slide-pack-header') %> <% } %> <% if(typeof theme !== 'undefined' && theme) { %> <% } else { %> <% } %>
      <%= body %>
      <%= meta %>
      <% if(useCDN) { %> <%- include('build/slide-scripts') %> <% } else { %> <%- include('build/slide-pack-scripts') %> <% } %> <%- include('shared/ga') %> ================================================ FILE: scalingo.json ================================================ { "name": "CodiMD", "description": "Realtime collaborative markdown notes on all platforms", "keywords": [ "Collaborative", "Markdown", "Notes" ], "website": "https://github.com/hackmdio/codimd", "repository": "https://github.com/hackmdio/codimd", "logo": "https://github.com/hackmdio/codimd/raw/master/public/codimd-icon-1024.png", "success_url": "/", "env": { "NPM_CONFIG_PRODUCTION": { "description": "Let npm also install development build tool", "value": "false" }, "CMD_SESSION_SECRET": { "description": "Secret used to secure session cookies.", "required": false }, "CMD_HSTS_ENABLE": { "description": "whether to also use HSTS if HTTPS is enabled", "required": false }, "CMD_HSTS_MAX_AGE": { "description": "max duration, in seconds, to tell clients to keep HSTS status", "required": false }, "CMD_HSTS_INCLUDE_SUBDOMAINS": { "description": "whether to tell clients to also regard subdomains as HSTS hosts", "required": false }, "CMD_HSTS_PRELOAD": { "description": "whether to allow at all adding of the site to HSTS preloads (e.g. in browsers)", "required": false }, "CMD_DOMAIN": { "description": "domain name", "required": false }, "CMD_URL_PATH": { "description": "sub url path, like `www.example.com/`", "required": false }, "CMD_ALLOW_ORIGIN": { "description": "domain name whitelist (use comma to separate)", "required": false, "value": "localhost" }, "CMD_PROTOCOL_USESSL": { "description": "set to use ssl protocol for resources path (only applied when domain is set)", "required": false }, "CMD_URL_ADDPORT": { "description": "set to add port on callback url (port 80 or 443 won't applied) (only applied when domain is set)", "required": false }, "CMD_FACEBOOK_CLIENTID": { "description": "Facebook API client id", "required": false }, "CMD_FACEBOOK_CLIENTSECRET": { "description": "Facebook API client secret", "required": false }, "CMD_TWITTER_CONSUMERKEY": { "description": "Twitter API consumer key", "required": false }, "CMD_TWITTER_CONSUMERSECRET": { "description": "Twitter API consumer secret", "required": false }, "CMD_GITHUB_CLIENTID": { "description": "GitHub API client id", "required": false }, "CMD_GITHUB_CLIENTSECRET": { "description": "GitHub API client secret", "required": false }, "CMD_GITHUB_ORGANIZATIONS": { "description": "GitHub whitelist of orgs", "required": false }, "CMD_GITHUB_SCOPES": { "description": "GitHub OAuth API scopes", "required": false }, "CMD_BITBUCKET_CLIENTID": { "description": "Bitbucket API client id", "required": false }, "CMD_BITBUCKET_CLIENTSECRET": { "description": "Bitbucket API client secret", "required": false }, "CMD_GITLAB_BASEURL": { "description": "GitLab authentication endpoint, set to use other endpoint than GitLab.com (optional)", "required": false }, "CMD_GITLAB_CLIENTID": { "description": "GitLab API client id", "required": false }, "CMD_GITLAB_CLIENTSECRET": { "description": "GitLab API client secret", "required": false }, "CMD_GITLAB_SCOPE": { "description": "GitLab API client scope (optional)", "required": false }, "CMD_MATTERMOST_BASEURL": { "description": "Mattermost authentication endpoint", "required": false }, "CMD_MATTERMOST_CLIENTID": { "description": "Mattermost API client id", "required": false }, "CMD_MATTERMOST_CLIENTSECRET": { "description": "Mattermost API client secret", "required": false }, "CMD_DROPBOX_CLIENTID": { "description": "Dropbox API client id", "required": false }, "CMD_DROPBOX_CLIENTSECRET": { "description": "Dropbox API client secret", "required": false }, "CMD_DROPBOX_APP_KEY": { "description": "Dropbox app key (for import/export)", "required": false }, "CMD_GOOGLE_CLIENTID": { "description": "Google API client id", "required": false }, "CMD_GOOGLE_CLIENTSECRET": { "description": "Google API client secret", "required": false }, "CMD_IMGUR_CLIENTID": { "description": "Imgur API client id", "required": false }, "CMD_ALLOW_PDF_EXPORT": { "description": "Enable or disable PDF exports", "required": false }, "CMD_DB_URL": { "description": "Database query url", "value": "$DATABASE_URL" }, "CMD_PORT": { "description": "Listening Port", "value": "$PORT" }, "DYNO": { "description": "Require this env var for deploy correctly the app", "value": "Scalingo" } }, "formation": { "web": { "amount": 1, "size": "S" } }, "addons": [ "postgresql:postgresql-sandbox" ] } ================================================ FILE: test/auth/oauth2/strategy.test.js ================================================ /* eslint-env node, mocha */ 'use strict' const assert = require('assert') const chance = require('chance')() const { extractProfileAttribute } = require('../../../lib/auth/oauth2/strategy') describe('OAuth2CustomStrategy', function () { describe('#extractProfileAttribute', function () { const data = { user: { email: chance.email() }, arrayData: [ { email: chance.email() }, { email: chance.email() } ] } it('should parse normal attribute correctly', function () { assert(extractProfileAttribute(data, 'user.email') === data.user.email) }) it('should return undefined when nested object key not exists', function () { assert(extractProfileAttribute(data, 'user.profile') === undefined) }) it('should return undefined when whole object key not exists', function () { assert(extractProfileAttribute(data, 'profile.email') === undefined) }) it('should return attribute in array correct', function () { assert(extractProfileAttribute(data, 'arrayData[0].email') === data.arrayData[0].email) assert(extractProfileAttribute(data, 'arrayData[1].email') === data.arrayData[1].email) }) it('should return undefined when array index out of bound', function () { assert(extractProfileAttribute(data, 'arrayData[3].email') === undefined) }) it('should return undefined when array key not exists', function () { assert(extractProfileAttribute(data, 'notExistsArray[5].email') === undefined) }) it('should return undefined when data is undefined', function () { assert(extractProfileAttribute(undefined, 'email') === undefined) assert(extractProfileAttribute(null, 'email') === undefined) assert(extractProfileAttribute({}, 'email') === undefined) }) }) }) ================================================ FILE: test/connectionQueue.test.js ================================================ /* eslint-env node, mocha */ 'use strict' const assert = require('assert') const sinon = require('sinon') const { ProcessQueue } = require('../lib/realtime/processQueue') describe('ProcessQueue', function () { let clock const waitTimeForCheckResult = 50 beforeEach(() => { clock = sinon.useFakeTimers({ toFake: ['setInterval'] }) }) afterEach(() => { clock.restore() sinon.restore() }) it('should not accept more than maximum task', () => { const queue = new ProcessQueue({ maximumLength: 2 }) queue.start() assert(queue.push(1, () => (Promise.resolve()))) assert(queue.push(1, () => (Promise.resolve())) === false) }) it('should run task every interval', (done) => { const runningClock = [] const queue = new ProcessQueue({ maximumLength: 2 }) const task = async () => { runningClock.push(clock.now) } queue.start() assert(queue.push(1, task)) assert(queue.push(2, task)) clock.tick(5) setTimeout(() => { clock.tick(5) }, 1) setTimeout(() => { clock.tick(5) }, 2) setTimeout(() => { clock.tick(5) }, 3) setTimeout(() => { queue.stop() assert(runningClock.length === 2) done() }, waitTimeForCheckResult) }) it('should not crash when repeat stop queue', () => { const queue = new ProcessQueue({ maximumLength: 2, triggerTimeInterval: 10 }) try { queue.stop() queue.stop() queue.stop() assert.ok(true) } catch (e) { assert.fail(e) } }) it('should run process when queue is empty', (done) => { const queue = new ProcessQueue({ maximumLength: 2, triggerTimeInterval: 100 }) const processSpy = sinon.spy(queue, 'process') queue.start() clock.tick(100) setTimeout(() => { assert(processSpy.called) done() }, waitTimeForCheckResult) }) it('should run process although error occurred', (done) => { const queue = new ProcessQueue({ maximumLength: 2, triggerTimeInterval: 100 }) const failedTask = sinon.spy(async () => { throw new Error('error') }) const normalTask = sinon.spy(async () => { }) queue.start() assert(queue.push(1, failedTask)) assert(queue.push(2, normalTask)) clock.tick(100) setTimeout(() => { clock.tick(100) }, 1) setTimeout(() => { // assert(queue.queue.length === 0) assert(failedTask.called) assert(normalTask.called) done() }, waitTimeForCheckResult) }) it('should ignore trigger when event not complete', (done) => { const queue = new ProcessQueue({ maximumLength: 2, triggerTimeInterval: 10 }) const processSpy = sinon.spy(queue, 'process') const longTask = async () => { return new Promise((resolve) => { setInterval(() => { resolve() }, 50) }) } queue.start() queue.push(1, longTask) clock.tick(10) setTimeout(() => { clock.tick(10) }, 0) setTimeout(() => { clock.tick(10) }, 1) setTimeout(() => { assert(processSpy.callCount === 1) assert(processSpy.calledOnce) done() }, waitTimeForCheckResult) }) }) ================================================ FILE: test/csp.js ================================================ /* eslint-env node, mocha */ 'use strict' const assert = require('assert') const crypto = require('crypto') const fs = require('fs') const path = require('path') const mock = require('mock-require') describe('Content security policies', function () { let defaultConfig, csp before(function () { csp = require('../lib/csp') }) beforeEach(function () { // Reset config to make sure we don't influence other tests defaultConfig = { csp: { enable: true, directives: { }, addDefaults: true, addDisqus: true, addGoogleAnalytics: true, upgradeInsecureRequests: 'auto', reportURI: undefined }, useCDN: true } }) afterEach(function () { mock.stop('../lib/config') csp = mock.reRequire('../lib/csp') }) after(function () { mock.stopAll() csp = mock.reRequire('../lib/csp') }) // beginnging Tests it('Disable CDN', function () { const testconfig = defaultConfig testconfig.useCDN = false mock('../lib/config', testconfig) csp = mock.reRequire('../lib/csp') assert(!csp.computeDirectives().scriptSrc.includes('https://cdnjs.cloudflare.com')) assert(!csp.computeDirectives().scriptSrc.includes('https://cdn.jsdelivr.net')) assert(!csp.computeDirectives().scriptSrc.includes('https://cdn.mathjax.org')) assert(!csp.computeDirectives().styleSrc.includes('https://cdnjs.cloudflare.com')) assert(!csp.computeDirectives().styleSrc.includes('https://cdn.jsdelivr.net')) assert(!csp.computeDirectives().styleSrc.includes('https://fonts.googleapis.com')) assert(!csp.computeDirectives().fontSrc.includes('https://cdnjs.cloudflare.com')) assert(!csp.computeDirectives().fontSrc.includes('https://fonts.gstatic.com')) }) it('Disable Google Analytics', function () { const testconfig = defaultConfig testconfig.csp.addGoogleAnalytics = false mock('../lib/config', testconfig) csp = mock.reRequire('../lib/csp') assert(!csp.computeDirectives().scriptSrc.includes('https://www.google-analytics.com')) }) it('Disable Disqus', function () { const testconfig = defaultConfig testconfig.csp.addDisqus = false mock('../lib/config', testconfig) csp = mock.reRequire('../lib/csp') assert(!csp.computeDirectives().scriptSrc.includes('https://disqus.com')) assert(!csp.computeDirectives().scriptSrc.includes('https://*.disqus.com')) assert(!csp.computeDirectives().scriptSrc.includes('https://*.disquscdn.com')) assert(!csp.computeDirectives().styleSrc.includes('https://*.disquscdn.com')) assert(!csp.computeDirectives().fontSrc.includes('https://*.disquscdn.com')) }) it('Set ReportURI', function () { const testconfig = defaultConfig testconfig.csp.reportURI = 'https://example.com/reportURI' mock('../lib/config', testconfig) csp = mock.reRequire('../lib/csp') assert.strictEqual(csp.computeDirectives().reportUri, 'https://example.com/reportURI') }) it('Set own directives', function () { const testconfig = defaultConfig mock('../lib/config', defaultConfig) csp = mock.reRequire('../lib/csp') const unextendedCSP = csp.computeDirectives() testconfig.csp.directives = { defaultSrc: ['https://default.example.com'], scriptSrc: ['https://script.example.com'], imgSrc: ['https://img.example.com'], styleSrc: ['https://style.example.com'], fontSrc: ['https://font.example.com'], objectSrc: ['https://object.example.com'], mediaSrc: ['https://media.example.com'], childSrc: ['https://child.example.com'], connectSrc: ['https://connect.example.com'] } mock('../lib/config', testconfig) csp = mock.reRequire('../lib/csp') const variations = ['default', 'script', 'img', 'style', 'font', 'object', 'media', 'child', 'connect'] for (let i = 0; i < variations.length; i++) { assert.strictEqual(csp.computeDirectives()[variations[i] + 'Src'].toString(), ['https://' + variations[i] + '.example.com'].concat(unextendedCSP[variations[i] + 'Src']).toString()) } }) /* * This test reminds us to update the CSP hash for the speaker notes */ it('Unchanged hash for reveal.js speaker notes plugin', function () { const hash = crypto.createHash('sha1') hash.update(fs.readFileSync(path.resolve(__dirname, '../node_modules/reveal.js/plugin/notes/notes.html'), 'utf8'), 'utf8') assert.strictEqual(hash.digest('hex'), 'd5d872ae49b5db27f638b152e6e528837204d380') }) }) ================================================ FILE: test/letter-avatars.js ================================================ /* eslint-env node, mocha */ 'use strict' const assert = require('assert') const mock = require('mock-require') describe('generateAvatarURL() gravatar enabled', function () { let avatars beforeEach(function () { // Reset config to make sure we don't influence other tests const testconfig = { allowGravatar: true, serverURL: 'http://localhost:3000', port: 3000 } mock('../lib/config', testconfig) avatars = mock.reRequire('../lib/letter-avatars') }) it('should return correct urls', function () { assert.strictEqual(avatars.generateAvatarURL('Daan Sprenkels', 'hello@dsprenkels.com', true), 'https://www.gravatar.com/avatar/d41b5f3508cc3f31865566a47dd0336b?s=400') assert.strictEqual(avatars.generateAvatarURL('Daan Sprenkels', 'hello@dsprenkels.com', false), 'https://www.gravatar.com/avatar/d41b5f3508cc3f31865566a47dd0336b?s=96') }) it('should return correct urls for names with spaces', function () { assert.strictEqual(avatars.generateAvatarURL('Daan Sprenkels'), 'http://localhost:3000/user/Daan%20Sprenkels/avatar.svg') }) }) describe('generateAvatarURL() gravatar disabled', function () { let avatars beforeEach(function () { // Reset config to make sure we don't influence other tests const testconfig = { allowGravatar: false, serverURL: 'http://localhost:3000', port: 3000 } mock('../lib/config', testconfig) avatars = mock.reRequire('../lib/letter-avatars') }) it('should return correct urls', function () { assert.strictEqual(avatars.generateAvatarURL('Daan Sprenkels', 'hello@dsprenkels.com', true), 'http://localhost:3000/user/Daan%20Sprenkels/avatar.svg') assert.strictEqual(avatars.generateAvatarURL('Daan Sprenkels', 'hello@dsprenkels.com', false), 'http://localhost:3000/user/Daan%20Sprenkels/avatar.svg') }) it('should return correct urls for names with spaces', function () { assert.strictEqual(avatars.generateAvatarURL('Daan Sprenkels'), 'http://localhost:3000/user/Daan%20Sprenkels/avatar.svg') }) }) ================================================ FILE: test/realtime/cleanDanglingUser.test.js ================================================ /* eslint-env node, mocha */ 'use strict' const assert = require('assert') const mock = require('mock-require') const sinon = require('sinon') const { removeModuleFromRequireCache, makeMockSocket } = require('./utils') describe('cleanDanglingUser', function () { let clock beforeEach(() => { clock = sinon.useFakeTimers() mock('../../lib/processQueue', require('../testDoubles/ProcessQueueFake')) mock('../../lib/logger', { error: () => {}, info: () => {} }) mock('../../lib/history', {}) mock('../../lib/models', { Revision: { saveAllNotesRevision: () => { } } }) mock('../../lib/config', { debug: true }) mock('../../lib/realtimeUpdateDirtyNoteJob', require('../testDoubles/realtimeJobStub')) mock('../../lib/realtimeSaveRevisionJob', require('../testDoubles/realtimeJobStub')) }) afterEach(() => { clock.restore() removeModuleFromRequireCache('../../lib/realtime/realtime') mock.stopAll() sinon.restore() }) it('should call queueForDisconnectSpy when user is dangling', (done) => { const realtime = require('../../lib/realtime/realtime') const queueForDisconnectSpy = sinon.spy(realtime, 'queueForDisconnect') realtime.io = { to: sinon.stub().callsFake(function () { return { emit: sinon.fake() } }), sockets: { connected: {} } } const user1Socket = makeMockSocket() const user2Socket = makeMockSocket() user1Socket.rooms.push('room1') realtime.io.sockets.connected[user1Socket.id] = user1Socket realtime.io.sockets.connected[user2Socket.id] = user2Socket realtime.users[user1Socket.id] = user1Socket realtime.users[user2Socket.id] = user2Socket clock.tick(60000) clock.restore() setTimeout(() => { assert(queueForDisconnectSpy.called) done() }, 50) }) }) ================================================ FILE: test/realtime/connection.test.js ================================================ /* eslint-env node, mocha */ 'use strict' const assert = require('assert') const mock = require('mock-require') const sinon = require('sinon') const { createFakeLogger } = require('../testDoubles/loggerFake') const { removeLibModuleCache, makeMockSocket } = require('./utils') const realtimeJobStub = require('../testDoubles/realtimeJobStub') describe('realtime#connection', function () { describe('connection', function () { let realtime let modelStub beforeEach(() => { removeLibModuleCache() modelStub = { Note: { findOne: sinon.stub() }, User: {}, Author: {} } mock('../../lib/logger', createFakeLogger()) mock('../../lib/history', {}) mock('../../lib/models', modelStub) mock('../../lib/config', {}) mock('../../lib/realtimeUpdateDirtyNoteJob', realtimeJobStub) mock('../../lib/realtimeCleanDanglingUserJob', realtimeJobStub) mock('../../lib/realtimeSaveRevisionJob', realtimeJobStub) mock('../../lib/ot', require('../testDoubles/otFake')) realtime = require('../../lib/realtime/realtime') }) afterEach(() => { mock.stopAll() sinon.restore() }) describe('fail', function () { it('should fast return when server not start', () => { const mockSocket = makeMockSocket() realtime.maintenance = true const spy = sinon.spy(realtime, 'parseNoteIdFromSocketAsync') realtime.connection(mockSocket) assert(!spy.called) }) it('should failed when parse noteId occur error', (done) => { const mockSocket = makeMockSocket() realtime.maintenance = false const parseNoteIdFromSocketSpy = sinon.stub(realtime, 'parseNoteIdFromSocketAsync').callsFake(async (socket) => { /* eslint-disable-next-line */ throw 'error' }) const failConnectionSpy = sinon.stub(realtime, 'failConnection') realtime.connection(mockSocket) setTimeout(() => { assert(parseNoteIdFromSocketSpy.called) assert(failConnectionSpy.calledOnce) assert.deepStrictEqual(failConnectionSpy.lastCall.args, [500, 'error', mockSocket]) done() }, 50) }) it('should failed when noteId not exists', (done) => { const mockSocket = makeMockSocket() realtime.maintenance = false const parseNoteIdFromSocketSpy = sinon.stub(realtime, 'parseNoteIdFromSocketAsync').callsFake(async (socket) => { return null }) const failConnectionSpy = sinon.stub(realtime, 'failConnection') realtime.connection(mockSocket) setTimeout(() => { assert(parseNoteIdFromSocketSpy.called) assert(failConnectionSpy.calledOnce) assert.deepStrictEqual(failConnectionSpy.lastCall.args, [404, 'note id not found', mockSocket]) done() }, 50) }) }) it('should success connect', function (done) { const mockSocket = makeMockSocket() const noteId = 'note123' realtime.maintenance = false const parseNoteIdFromSocketSpy = sinon.stub(realtime, 'parseNoteIdFromSocketAsync').callsFake(async (socket) => { return noteId }) const updateUserDataStub = sinon.stub(realtime, 'updateUserData') realtime.connection(mockSocket) setTimeout(() => { assert.ok(parseNoteIdFromSocketSpy.calledOnce) assert(updateUserDataStub.calledOnce) done() }, 50) }) describe('flow', function () { it('should establish connection', function (done) { const noteId = 'note123' const mockSocket = makeMockSocket(null, { noteId: noteId }) mockSocket.request.user.logged_in = true mockSocket.request.user.id = 'user1' mockSocket.noteId = noteId realtime.maintenance = false sinon.stub(realtime, 'parseNoteIdFromSocketAsync').callsFake(async (socket) => { return noteId }) const updateHistoryStub = sinon.stub(realtime, 'updateHistory') const emitOnlineUsersStub = sinon.stub(realtime, 'emitOnlineUsers') const emitRefreshStub = sinon.stub(realtime, 'emitRefresh') const failConnectionSpy = sinon.spy(realtime, 'failConnection') const note = { id: noteId, authors: [ { userId: 'user1', color: 'red', user: { id: 'user1', name: 'Alice' } }, { userId: 'user2', color: 'blue', user: { id: 'user2', name: 'Bob' } } ] } modelStub.Note.findOne.returns(Promise.resolve(note)) modelStub.User.getProfile = sinon.stub().callsFake((user) => { return user }) sinon.stub(realtime, 'checkViewPermission').returns(true) realtime.connection(mockSocket) setTimeout(() => { assert(modelStub.Note.findOne.calledOnce) assert.deepStrictEqual(modelStub.Note.findOne.lastCall.args[0].include, [ { model: modelStub.User, as: 'owner' }, { model: modelStub.User, as: 'lastchangeuser' }, { model: modelStub.Author, as: 'authors', include: [{ model: modelStub.User, as: 'user' }] } ]) assert(modelStub.Note.findOne.lastCall.args[0].where.id === noteId) assert(updateHistoryStub.calledOnce) assert(emitOnlineUsersStub.calledOnce) assert(emitRefreshStub.calledOnce) assert(failConnectionSpy.callCount === 0) assert(realtime.getNotePool()[noteId].id === noteId) assert(realtime.getNotePool()[noteId].socks.length === 1) assert.deepStrictEqual(realtime.getNotePool()[noteId].authors, { user1: { userid: 'user1', color: 'red', photo: undefined, name: 'Alice' }, user2: { userid: 'user2', color: 'blue', photo: undefined, name: 'Bob' } }) assert(Object.keys(realtime.getNotePool()[noteId].users).length === 1) done() }, 50) }) }) }) }) ================================================ FILE: test/realtime/dirtyNoteUpdate.test.js ================================================ /* eslint-env node, mocha */ 'use strict' const assert = require('assert') const mock = require('mock-require') const sinon = require('sinon') const { removeModuleFromRequireCache, makeMockSocket, removeLibModuleCache } = require('./utils') describe('realtime#update note is dirty timer', function () { let realtime let clock beforeEach(() => { removeLibModuleCache() clock = sinon.useFakeTimers({ toFake: ['setInterval'] }) mock('../../lib/logger', { error: () => { } }) mock('../../lib/history', {}) mock('../../lib/models', { Revision: { saveAllNotesRevision: () => { } } }) mock('../../lib/config', {}) realtime = require('../../lib/realtime/realtime') realtime.io = { to: sinon.stub().callsFake(function () { return { emit: sinon.fake() } }) } }) afterEach(() => { removeModuleFromRequireCache('../../lib/realtime/realtimeUpdateDirtyNoteJob') removeModuleFromRequireCache('../../lib/realtime/realtime') mock.stopAll() clock.restore() }) it('should update note when note is dirty', (done) => { sinon.stub(realtime, 'updateNote').callsFake(function (note, callback) { callback(null, note) }) realtime.notes.note1 = { server: { isDirty: false }, socks: [] } const note2 = { server: { isDirty: true }, socks: [] } realtime.notes.note2 = note2 clock.tick(1000) setTimeout(() => { assert(note2.server.isDirty === false) done() }, 10) }) it('should not do anything when note missing', function (done) { sinon.stub(realtime, 'updateNote').callsFake(function (note, callback) { delete realtime.notes.note callback(null, note) }) const note = { server: { isDirty: true }, socks: [makeMockSocket()] } realtime.notes.note = note clock.tick(1000) setTimeout(() => { assert(note.server.isDirty === false) assert(note.socks[0].disconnect.called === false) done() }, 50) }) it('should disconnect all clients when update note error', function (done) { sinon.stub(realtime, 'updateNote').callsFake(function (note, callback) { callback(new Error('some error'), null) }) realtime.io = { to: sinon.stub().callsFake(function () { return { emit: sinon.fake() } }) } const note = { server: { isDirty: true }, socks: [makeMockSocket(), undefined, makeMockSocket()] } realtime.notes.note = note clock.tick(1000) setTimeout(() => { assert(note.server.isDirty === false) assert(note.socks[0].disconnect.called) assert(note.socks[2].disconnect.called) done() }, 50) }) }) ================================================ FILE: test/realtime/disconnect-process.test.js ================================================ /* eslint-env node, mocha */ 'use strict' const assert = require('assert') const mock = require('mock-require') const sinon = require('sinon') const { makeMockSocket, removeModuleFromRequireCache } = require('./utils') describe('realtime#disconnect', function () { const noteId = 'note1_id' let realtime let updateNoteStub let emitOnlineUsersStub let client beforeEach(() => { mock('../../lib/logger', { error: () => { } }) mock('../../lib/history', {}) mock('../../lib/models', { Revision: { saveAllNotesRevision: () => { } } }) mock('../../lib/config', {}) realtime = require('../../lib/realtime/realtime') updateNoteStub = sinon.stub(realtime, 'updateNote').callsFake((note, callback) => { callback(null, note) }) emitOnlineUsersStub = sinon.stub(realtime, 'emitOnlineUsers') client = makeMockSocket() client.noteId = noteId realtime.users[client.id] = { id: client.id, color: '#ff0000', cursor: null, login: false, userid: null, name: null, idle: false, type: null } realtime.getNotePool()[noteId] = { id: noteId, server: { isDirty: true }, users: { [client.id]: realtime.users[client.id] }, socks: [client] } }) afterEach(() => { removeModuleFromRequireCache('../../lib/realtime/realtime') mock.stopAll() sinon.restore() }) it('should disconnect success', function (done) { realtime.queueForDisconnect(client) setTimeout(() => { assert(typeof realtime.users[client.id] === 'undefined') assert(emitOnlineUsersStub.called) assert(updateNoteStub.called) assert(Object.keys(realtime.users).length === 0) assert(Object.keys(realtime.notes).length === 0) done() }, 5) }) it('should disconnect success when note is not dirty', function (done) { realtime.notes[noteId].server.isDirty = false realtime.queueForDisconnect(client) setTimeout(() => { assert(typeof realtime.users[client.id] === 'undefined') assert(emitOnlineUsersStub.called) assert(updateNoteStub.called === false) assert(Object.keys(realtime.users).length === 0) assert(Object.keys(realtime.notes).length === 0) done() }, 5) }) }) ================================================ FILE: test/realtime/extractNoteIdFromSocket.test.js ================================================ /* eslint-env node, mocha */ 'use strict' const mock = require('mock-require') const assert = require('assert') const { makeMockSocket } = require('./utils') describe('realtime#extractNoteIdFromSocket', function () { beforeEach(() => { mock('../../lib/logger', {}) mock('../../lib/history', {}) mock('../../lib/models', {}) }) afterEach(() => { delete require.cache[require.resolve('../../lib/realtime/realtime')] mock.stopAll() }) describe('urlPath not set', function () { beforeEach(() => { mock('../../lib/config', {}) realtime = require('../../lib/realtime/realtime') }) let realtime it('return false if socket or socket.handshake not exists', function () { let noteId = realtime.extractNoteIdFromSocket() assert.strictEqual(false, noteId) noteId = realtime.extractNoteIdFromSocket({}) assert.strictEqual(false, noteId) }) it('return false if query not set and referer not set', function () { const noteId = realtime.extractNoteIdFromSocket(makeMockSocket({ otherHeader: 1 }, { otherQuery: 1 })) assert.strictEqual(false, noteId) }) it('return noteId from query', function () { // Arrange const incomingNoteId = 'myNoteId' const incomingSocket = makeMockSocket(undefined, { noteId: incomingNoteId }) // Act const noteId = realtime.extractNoteIdFromSocket(incomingSocket) // Assert assert.strictEqual(noteId, incomingNoteId) }) it('return noteId from old method (referer)', function () { // Arrange const incomingNoteId = 'myNoteId' const incomingSocket = makeMockSocket({ referer: `https://localhost:3000/${incomingNoteId}` }) // Act const noteId = realtime.extractNoteIdFromSocket(incomingSocket) // Assert assert.strictEqual(noteId, incomingNoteId) }) }) describe('urlPath is set', function () { let realtime it('return noteId from old method (referer) and urlPath set', function () { // Arrange const urlPath = 'hello' mock('../../lib/config', { urlPath: urlPath }) realtime = require('../../lib/realtime/realtime') const incomingNoteId = 'myNoteId' const incomingSocket = makeMockSocket({ referer: `https://localhost:3000/${urlPath}/${incomingNoteId}` }) // Act const noteId = realtime.extractNoteIdFromSocket(incomingSocket) // Assert assert.strictEqual(noteId, incomingNoteId) }) }) }) ================================================ FILE: test/realtime/ifMayEdit.test.js ================================================ /* eslint-env node, mocha */ 'use strict' const assert = require('assert') const mock = require('mock-require') const sinon = require('sinon') const { createFakeLogger } = require('../testDoubles/loggerFake') const realtimeJobStub = require('../testDoubles/realtimeJobStub') const { removeLibModuleCache, makeMockSocket } = require('./utils') describe('realtime#ifMayEdit', function () { let modelsStub beforeEach(() => { removeLibModuleCache() mock('../../lib/config', {}) mock('../../lib/logger', createFakeLogger()) mock('../../lib/models', modelsStub) mock('../../lib/realtimeUpdateDirtyNoteJob', realtimeJobStub) mock('../../lib/realtimeCleanDanglingUserJob', realtimeJobStub) mock('../../lib/realtimeSaveRevisionJob', realtimeJobStub) }) afterEach(() => { mock.stopAll() sinon.restore() }) const Role = { Guest: 'guest', LoggedIn: 'LoggedIn', Owner: 'Owner' } const Permission = { Freely: 'freely', Editable: 'editable', Limited: 'limited', Locked: 'locked', Protected: 'protected', Private: 'private' } const testcases = [ { role: Role.Guest, permission: Permission.Freely, canEdit: true }, { role: Role.LoggedIn, permission: Permission.Freely, canEdit: true }, { role: Role.Owner, permission: Permission.Freely, canEdit: true }, { role: Role.Guest, permission: Permission.Editable, canEdit: false }, { role: Role.LoggedIn, permission: Permission.Editable, canEdit: true }, { role: Role.Owner, permission: Permission.Editable, canEdit: true }, { role: Role.Guest, permission: Permission.Limited, canEdit: false }, { role: Role.LoggedIn, permission: Permission.Limited, canEdit: true }, { role: Role.Owner, permission: Permission.Limited, canEdit: true }, { role: Role.Guest, permission: Permission.Locked, canEdit: false }, { role: Role.LoggedIn, permission: Permission.Locked, canEdit: false }, { role: Role.Owner, permission: Permission.Locked, canEdit: true }, { role: Role.Guest, permission: Permission.Protected, canEdit: false }, { role: Role.LoggedIn, permission: Permission.Protected, canEdit: false }, { role: Role.Owner, permission: Permission.Protected, canEdit: true }, { role: Role.Guest, permission: Permission.Private, canEdit: false }, { role: Role.LoggedIn, permission: Permission.Private, canEdit: false }, { role: Role.Owner, permission: Permission.Private, canEdit: true } ] const noteOwnerId = 'owner' const loggedInUserId = 'user1' const noteId = 'noteId' testcases.forEach((tc) => { it(`${tc.role} ${tc.canEdit ? 'can' : 'can\'t'} edit note with permission ${tc.permission}`, function () { const client = makeMockSocket() const note = { permission: tc.permission, owner: noteOwnerId } if (tc.role === Role.LoggedIn) { client.request.user.logged_in = true client.request.user.id = loggedInUserId } else if (tc.role === Role.Owner) { client.request.user.logged_in = true client.request.user.id = noteOwnerId } client.noteId = noteId const realtime = require('../../lib/realtime/realtime') realtime.getNotePool()[noteId] = note const callback = sinon.stub() realtime.ifMayEdit(client, callback) assert(callback.calledOnce) assert(callback.lastCall.args[0] === tc.canEdit) }) }) it('should set lsatchangeuser to null if guest edit operation', function () { const note = { permission: Permission.Freely } const client = makeMockSocket() client.noteId = noteId const callback = sinon.stub() client.origin = 'operation' const realtime = require('../../lib/realtime/realtime') realtime.getNotePool()[noteId] = note realtime.ifMayEdit(client, callback) assert(callback.calledOnce) assert(callback.lastCall.args[0]) assert(note.lastchangeuser === null) }) it('should set lastchangeuser to logged_in user id if user edit', function () { const note = { permission: Permission.Freely } const client = makeMockSocket() client.noteId = noteId client.request.user.logged_in = true client.request.user.id = loggedInUserId const callback = sinon.stub() client.origin = 'operation' const realtime = require('../../lib/realtime/realtime') realtime.getNotePool()[noteId] = note realtime.ifMayEdit(client, callback) assert(callback.calledOnce) assert(callback.lastCall.args[0]) assert(note.lastchangeuser === loggedInUserId) }) }) ================================================ FILE: test/realtime/parseNoteIdFromSocket.test.js ================================================ /* eslint-env node, mocha */ 'use strict' const assert = require('assert') const mock = require('mock-require') const { makeMockSocket, removeModuleFromRequireCache } = require('./utils') describe('realtime#parseNoteIdFromSocketAsync', function () { let realtime beforeEach(() => { mock('../../lib/logger', {}) mock('../../lib/history', {}) mock('../../lib/models', { Note: { parseNoteId: function (noteId, callback) { callback(null, noteId) } } }) mock('../../lib/config', {}) }) afterEach(() => { removeModuleFromRequireCache('../../lib/realtime/realtime') mock.stopAll() }) it('should return null when socket not send noteId', async function () { realtime = require('../../lib/realtime/realtime') const mockSocket = makeMockSocket() try { const notes = await realtime.parseNoteIdFromSocketAsync(mockSocket) assert(notes === null) } catch (err) { assert.fail('should not occur any error') } }) describe('noteId exists', function () { beforeEach(() => { mock('../../lib/models', { Note: { parseNoteId: function (noteId, callback) { callback(null, noteId) } } }) }) it('should return noteId when noteId exists', async function () { realtime = require('../../lib/realtime/realtime') const noteId = '123456' const mockSocket = makeMockSocket(undefined, { noteId: noteId }) realtime = require('../../lib/realtime/realtime') let parsedNoteId try { parsedNoteId = await realtime.parseNoteIdFromSocketAsync(mockSocket) } catch (err) { assert.fail(`should not occur any error ${err} `) } assert(parsedNoteId === noteId) }) }) describe('noteId not exists', function () { beforeEach(() => { mock('../../lib/models', { Note: { parseNoteId: function (noteId, callback) { callback(null, null) } } }) }) it('should return null when noteId not exists', async function () { realtime = require('../../lib/realtime/realtime') const noteId = '123456' const mockSocket = makeMockSocket(undefined, { noteId: noteId }) realtime = require('../../lib/realtime/realtime') const parsedNoteId = await realtime.parseNoteIdFromSocketAsync(mockSocket) assert(parsedNoteId === null) }) }) describe('parse note error', function () { beforeEach(() => { mock('../../lib/models', { Note: { parseNoteId: function (noteId, callback) { /* eslint-disable-next-line */ callback('error', null) } } }) }) it('should return error when noteId parse error', async function () { realtime = require('../../lib/realtime/realtime') const noteId = '123456' const mockSocket = makeMockSocket(undefined, { noteId: noteId }) realtime = require('../../lib/realtime/realtime') try { await realtime.parseNoteIdFromSocketAsync(mockSocket) } catch (err) { assert(err === 'error') } }) }) }) ================================================ FILE: test/realtime/realtime.test.js ================================================ 'use strict' /* eslint-env node, mocha */ const mock = require('mock-require') const assert = require('assert') describe('realtime', function () { describe('checkViewPermission', function () { // role -> guest, loggedInUser, loggedInOwner const viewPermission = { freely: [true, true, true], editable: [true, true, true], limited: [false, true, true], locked: [true, true, true], protected: [false, true, true], private: [false, false, true] } const loggedInUserId = 'user1_id' const ownerUserId = 'user2_id' const guestReq = {} const loggedInUserReq = { user: { id: loggedInUserId, logged_in: true } } const loggedInOwnerReq = { user: { id: ownerUserId, logged_in: true } } const note = { owner: ownerUserId } let realtime beforeEach(() => { mock('../../lib/logger', { error: () => { } }) mock('../../lib/history', {}) mock('../../lib/models', { Note: { parseNoteTitle: (data) => (data) } }) mock('../../lib/config', {}) realtime = require('../../lib/realtime/realtime') }) Object.keys(viewPermission).forEach(function (permission) { describe(permission, function () { beforeEach(() => { note.permission = permission }) it('guest permission test', function () { assert(realtime.checkViewPermission(guestReq, note) === viewPermission[permission][0]) }) it('loggedIn User permission test', function () { assert(realtime.checkViewPermission(loggedInUserReq, note) === viewPermission[permission][1]) }) it('loggedIn Owner permission test', function () { assert(realtime.checkViewPermission(loggedInOwnerReq, note) === viewPermission[permission][2]) }) }) }) }) }) ================================================ FILE: test/realtime/saveRevisionJob.test.js ================================================ /* eslint-env node, mocha */ 'use strict' const assert = require('assert') const mock = require('mock-require') const sinon = require('sinon') const { removeModuleFromRequireCache, removeLibModuleCache } = require('./utils') describe('save revision job', function () { let clock let mockModels let realtime beforeEach(() => { removeLibModuleCache() mockModels = { Revision: { saveAllNotesRevision: sinon.stub() } } clock = sinon.useFakeTimers() mock('../../lib/processQueue', require('../testDoubles/ProcessQueueFake')) mock('../../lib/logger', { error: () => {}, info: () => {} }) mock('../../lib/history', {}) mock('../../lib/models', mockModels) mock('../../lib/config', { debug: true }) mock('../../lib/realtimeUpdateDirtyNoteJob', require('../testDoubles/realtimeJobStub')) mock('../../lib/realtimeCleanDanglingUserJob', require('../testDoubles/realtimeJobStub')) }) afterEach(() => { clock.restore() removeModuleFromRequireCache('../../lib/realtime/realtime') removeModuleFromRequireCache('../../lib/realtime/realtimeSaveRevisionJob') mock.stopAll() sinon.restore() }) it('should execute save revision job every 5 min', (done) => { mockModels.Revision.saveAllNotesRevision.callsFake((callback) => { callback(null, []) }) realtime = require('../../lib/realtime/realtime') clock.tick(5 * 60 * 1000) clock.restore() setTimeout(() => { assert(mockModels.Revision.saveAllNotesRevision.called) assert(realtime.saveRevisionJob.getSaverSleep() === true) done() }, 50) }) it('should not set saverSleep when more than 1 note save revision', (done) => { mockModels.Revision.saveAllNotesRevision.callsFake((callback) => { callback(null, [1]) }) realtime = require('../../lib/realtime/realtime') clock.tick(5 * 60 * 1000) clock.restore() setTimeout(() => { assert(mockModels.Revision.saveAllNotesRevision.called) assert(realtime.saveRevisionJob.getSaverSleep() === false) done() }, 50) }) }) ================================================ FILE: test/realtime/socket-events.test.js ================================================ /* eslint-env node, mocha */ 'use strict' const assert = require('assert') const mock = require('mock-require') const sinon = require('sinon') const { makeMockSocket, removeModuleFromRequireCache } = require('./utils') describe('realtime#socket event', function () { const noteId = 'note123' const note = { id: noteId, authors: [ { userId: 'user1', color: 'red', user: { id: 'user1', name: 'Alice' } }, { userId: 'user2', color: 'blue', user: { id: 'user2', name: 'Bob' } } ] } let realtime let clientSocket let modelsMock let eventFuncMap let configMock let clock beforeEach(function (done) { clock = sinon.useFakeTimers({ toFake: ['setInterval'] }) eventFuncMap = new Map() modelsMock = { Note: { parseNoteTitle: (data) => (data), destroy: sinon.stub().returns(Promise.resolve(1)), update: sinon.stub().returns(Promise.resolve([1])), findOne: sinon.stub().returns(Promise.resolve(note)) }, User: {} } modelsMock.User.getProfile = sinon.stub().callsFake((user) => { return user }) configMock = { fullversion: '1.5.0', minimumCompatibleVersion: '1.0.0', permission: { freely: 'freely', editable: 'editable', limited: 'limited', locked: 'locked', protected: 'protected', private: 'private' } } mock('../../lib/logger', { error: () => { }, info: () => { } }) mock('../../lib/history', {}) mock('../../lib/models', modelsMock) mock('../../lib/config', configMock) mock('../../lib/ot', require('../testDoubles/otFake')) realtime = require('../../lib/realtime/realtime') // get all socket event handler clientSocket = makeMockSocket(null, { noteId: noteId }) clientSocket.request.user.logged_in = true clientSocket.request.user.id = 'user1' // clientSocket.noteId = noteId clientSocket.on = function (event, func) { eventFuncMap.set(event, func) } realtime.maintenance = false realtime.io = (function () { const roomMap = new Map() return { to: function (roomId) { if (!roomMap.has(roomId)) { roomMap.set(roomId, { emit: sinon.stub() }) } return roomMap.get(roomId) } } }()) const wrappedFuncs = [] wrappedFuncs.push(sinon.stub(realtime, 'updateUserData')) wrappedFuncs.push(sinon.stub(realtime, 'emitOnlineUsers')) wrappedFuncs.push(sinon.stub(realtime, 'parseNoteIdFromSocketAsync').returns(Promise.resolve(noteId))) wrappedFuncs.push(sinon.stub(realtime, 'updateHistory')) wrappedFuncs.push(sinon.stub(realtime, 'emitRefresh')) realtime.connection(clientSocket) setTimeout(() => { wrappedFuncs.forEach((wrappedFunc) => { wrappedFunc.restore() }) done() }, 50) }) afterEach(function () { removeModuleFromRequireCache('../../lib/realtime/realtime') removeModuleFromRequireCache('../../lib/realtime/realtimeClientConnection') mock.stopAll() sinon.restore() clock.restore() clientSocket = null }) describe('refresh', function () { it('should call refresh', () => { const refreshFunc = eventFuncMap.get('refresh') const emitRefreshStub = sinon.stub(realtime, 'emitRefresh') refreshFunc() assert(emitRefreshStub.calledOnce) assert.deepStrictEqual(emitRefreshStub.lastCall.args[0], clientSocket) }) }) describe('user status', function () { it('should call emitUserStatus and update user data', () => { const userStatusFunc = eventFuncMap.get('user status') const emitUserStatusStub = sinon.stub(realtime, 'emitUserStatus') realtime.notes[noteId] = {} const userData = { idle: true, type: 'xs' } userStatusFunc(userData) assert(emitUserStatusStub.calledOnce) assert.deepStrictEqual(emitUserStatusStub.lastCall.args[0], clientSocket) assert(realtime.users[clientSocket.id].idle === true) assert(realtime.users[clientSocket.id].type === 'xs') }) it('should call emitUserStatus without userdata', () => { const userStatusFunc = eventFuncMap.get('user status') const emitUserStatusStub = sinon.stub(realtime, 'emitUserStatus') realtime.notes[noteId] = {} userStatusFunc() assert(emitUserStatusStub.calledOnce) assert.deepStrictEqual(emitUserStatusStub.lastCall.args[0], clientSocket) assert(realtime.users[clientSocket.id].idle === false) assert(realtime.users[clientSocket.id].type === null) }) it('should not call emitUserStatus when user not exists', () => { const userStatusFunc = eventFuncMap.get('user status') const emitUserStatusStub = sinon.stub(realtime, 'emitUserStatus') realtime.notes[noteId] = {} delete realtime.users[clientSocket.id] const userData = { idle: true, type: 'xs' } userStatusFunc(userData) assert(emitUserStatusStub.called === false) }) it('should not call emitUserStatus when note not exists', () => { const userStatusFunc = eventFuncMap.get('user status') const emitUserStatusStub = sinon.stub(realtime, 'emitUserStatus') realtime.deleteAllNoteFromPool() realtime.users[clientSocket.id] = {} const userData = { idle: true, type: 'xs' } userStatusFunc(userData) assert(emitUserStatusStub.called === false) }) }) describe('disconnect', function () { it('should push socket to disconnect queue and call disconnect function', () => { const disconnectFunc = eventFuncMap.get('disconnect') const queueForDisconnectStub = sinon.stub(realtime, 'queueForDisconnect') disconnectFunc() assert(queueForDisconnectStub.calledOnce) }) it('should quick return when socket is in disconnect queue', () => { const disconnectFunc = eventFuncMap.get('disconnect') const queueForDisconnectStub = sinon.stub(realtime, 'queueForDisconnect') realtime.disconnectProcessQueue.push(clientSocket.id, async () => {}) disconnectFunc() assert(queueForDisconnectStub.called === false) }) }) ;['cursor focus', 'cursor activity', 'cursor blur'].forEach((event) => { describe(event, function () { let cursorFocusFunc const cursorData = { cursor: 10 } beforeEach(() => { cursorFocusFunc = eventFuncMap.get(event) realtime.notes[noteId] = {} }) it('should broadcast to all client', () => { cursorFocusFunc(cursorData) const broadChannelEmitFake = clientSocket.broadcast.to(noteId).emit assert(broadChannelEmitFake.calledOnce) assert(broadChannelEmitFake.lastCall.args[0] === event) if (event === 'cursor blur') { assert(broadChannelEmitFake.lastCall.args[1].id === clientSocket.id) } else { assert.deepStrictEqual(broadChannelEmitFake.lastCall.args[1].cursor, cursorData) } }) it('should not broadcast when note not exists', () => { delete realtime.notes[noteId] cursorFocusFunc(cursorData) const broadChannelEmitFake = clientSocket.broadcast.to(noteId).emit assert(broadChannelEmitFake.called === false) }) it('should not broadcast when user not exists', () => { delete realtime.users[clientSocket.id] cursorFocusFunc(cursorData) const broadChannelEmitFake = clientSocket.broadcast.to(noteId).emit assert(broadChannelEmitFake.called === false) }) }) }) describe('version', function () { it('should emit server version ', () => { const versionFunc = eventFuncMap.get('version') versionFunc() assert(clientSocket.emit.called) assert(clientSocket.emit.lastCall.args[0], 'version') assert.deepStrictEqual(clientSocket.emit.lastCall.args[1], { version: '1.5.0', minimumCompatibleVersion: '1.0.0' }) }) }) describe('online users', function () { it('should return online user list', function () { const onlineUsersFunc = eventFuncMap.get('online users') realtime.notes[noteId] = { users: { 10: { id: 10 }, 20: { id: 20 } } } onlineUsersFunc() assert(clientSocket.emit.called) assert(clientSocket.emit.lastCall.args[0] === 'online users') const returnUserList = clientSocket.emit.lastCall.args[1].users assert(returnUserList.length === 2) assert(returnUserList[0].id === 10) assert(returnUserList[1].id === 20) }) it('should not return user list when note not exists', function () { const onlineUsersFunc = eventFuncMap.get('online users') realtime.deleteAllNoteFromPool() onlineUsersFunc() assert(clientSocket.emit.called === false) }) }) describe('user changed', function () { it('should call updateUserData', () => { const userChangedFunc = eventFuncMap.get('user changed') realtime.notes[noteId] = { users: { [clientSocket.id]: {} } } const updateUserDataStub = sinon.stub(realtime, 'updateUserData') const emitOnlineUsersStub = sinon.stub(realtime, 'emitOnlineUsers') userChangedFunc() assert(updateUserDataStub.calledOnce) assert(emitOnlineUsersStub.calledOnce) }) it('should direct return when note not exists', function () { const userChangedFunc = eventFuncMap.get('user changed') const updateUserDataStub = sinon.stub(realtime, 'updateUserData') const emitOnlineUsersStub = sinon.stub(realtime, 'emitOnlineUsers') realtime.deleteAllNoteFromPool() userChangedFunc() assert(updateUserDataStub.called === false) assert(emitOnlineUsersStub.called === false) }) it('should direct return when note\'s users not exists', function () { const userChangedFunc = eventFuncMap.get('user changed') realtime.notes[noteId] = { users: {} } delete realtime.users[clientSocket.id] const updateUserDataStub = sinon.stub(realtime, 'updateUserData') const emitOnlineUsersStub = sinon.stub(realtime, 'emitOnlineUsers') userChangedFunc() assert(updateUserDataStub.called === false) assert(emitOnlineUsersStub.called === false) }) }) describe('delete', function () { it('should delete note when owner request', function (done) { const currentUserId = 'user1_id' const noteOwnerId = 'user1_id' const otherClient = makeMockSocket() clientSocket.request = { user: { logged_in: true, id: currentUserId } } realtime.notes[noteId] = { owner: noteOwnerId, socks: [clientSocket, undefined, otherClient] } const deleteFunc = eventFuncMap.get('delete') deleteFunc() setTimeout(() => { assert(otherClient.disconnect.calledOnce) assert(otherClient.emit.calledOnce) assert(otherClient.emit.lastCall.args[0] === 'delete') assert(clientSocket.disconnect.calledOnce) assert(clientSocket.emit.calledOnce) assert(clientSocket.emit.lastCall.args[0] === 'delete') assert(modelsMock.Note.destroy.calledOnce) done() }, 10) }) it('should not do anything when user not login', function (done) { const noteOwnerId = 'user1_id' clientSocket.request = {} realtime.notes[noteId] = { owner: noteOwnerId, socks: [clientSocket] } const deleteFunc = eventFuncMap.get('delete') deleteFunc() setTimeout(() => { assert(modelsMock.Note.destroy.called === false) assert(clientSocket.disconnect.called === false) done() }, 10) }) it('should not do anything when note not exists', function (done) { const currentUserId = 'user1_id' clientSocket.request = { user: { logged_in: true, id: currentUserId } } const deleteFunc = eventFuncMap.get('delete') deleteFunc() setTimeout(() => { assert(modelsMock.Note.destroy.called === false) assert(clientSocket.disconnect.called === false) done() }, 10) }) it('should not do anything when note owner is not me', function (done) { const currentUserId = 'user1_id' const noteOwnerId = 'user2_id' const otherClient = makeMockSocket() clientSocket.request = { user: { logged_in: true, id: currentUserId } } realtime.notes[noteId] = { owner: noteOwnerId, socks: [clientSocket, otherClient] } const deleteFunc = eventFuncMap.get('delete') deleteFunc() setTimeout(() => { assert(clientSocket.disconnect.called === false) assert(modelsMock.Note.destroy.called === false) done() }, 10) }) it('should not do anything when note destroy fail', function (done) { const currentUserId = 'user1_id' const noteOwnerId = 'user1_id' modelsMock.Note.destroy.withArgs({ where: { id: noteId } }).returns(Promise.resolve(0)) const otherClient = makeMockSocket() clientSocket.request = { user: { logged_in: true, id: currentUserId } } realtime.notes[noteId] = { id: noteId, owner: noteOwnerId, socks: [clientSocket, otherClient] } const deleteFunc = eventFuncMap.get('delete') deleteFunc() setTimeout(() => { assert(modelsMock.Note.destroy.calledOnce) assert(clientSocket.disconnect.called === false) done() }, 10) }) }) describe('permission', function () { const ownerId = 'user1_id' const otherSignInUserId = 'user2_id' let otherClient let checkViewPermissionSpy let permissionFunc beforeEach(function () { otherClient = makeMockSocket() clientSocket.request = { user: { id: ownerId, logged_in: true } } otherClient.request = { user: { id: otherSignInUserId, logged_in: true } } realtime.deleteAllNoteFromPool() realtime.addNote({ id: noteId, owner: ownerId }) checkViewPermissionSpy = sinon.spy(realtime, 'checkViewPermission') permissionFunc = eventFuncMap.get('permission') }) it('should disconnect when lose view permission', function (done) { realtime.getNoteFromNotePool(noteId).permission = 'editable' realtime.getNoteFromNotePool(noteId).socks = [clientSocket, undefined, otherClient] permissionFunc('private') setTimeout(() => { // should change note permission to private assert(modelsMock.Note.update.calledOnce) assert(modelsMock.Note.update.lastCall.args[0].permission === 'private') assert(modelsMock.Note.update.lastCall.args[1].where.id === noteId) // should check all connected client assert(checkViewPermissionSpy.callCount === 2) assert(otherClient.emit.calledOnce) assert(otherClient.disconnect.calledOnce) done() }, 5) }) it('should not do anything when user not logged in', function (done) { clientSocket.request = {} permissionFunc('private') setTimeout(() => { assert(modelsMock.Note.update.called === false) done() }, 5) }) it('should not do anything when note not exists', function (done) { delete realtime.notes[noteId] permissionFunc('private') setTimeout(() => { assert(modelsMock.Note.update.called === false) done() }, 5) }) it('should not do anything when not note owner', function (done) { clientSocket.request.user.id = 'other_user_id' permissionFunc('private') setTimeout(() => { assert(modelsMock.Note.update.called === false) done() }, 5) }) it('should change permission to freely when config allowAnonymous, allowAnonymousEdits and allowAnonymousViews are true', function (done) { configMock.allowAnonymous = true configMock.allowAnonymousEdits = true configMock.allowAnonymousViews = true realtime.notes[noteId].socks = [clientSocket, undefined, otherClient] permissionFunc('freely') setTimeout(() => { assert(checkViewPermissionSpy.callCount === 2) assert(otherClient.emit.called === false) assert(otherClient.disconnect.called === false) assert(clientSocket.emit.called === false) assert(clientSocket.disconnect.called === false) done() }, 5) }) it('should not change permission to freely when config allowAnonymous, allowAnonymousEdits and allowAnonymousViews are false', function (done) { configMock.allowAnonymous = false configMock.allowAnonymousEdits = false configMock.allowAnonymousViews = false realtime.notes[noteId].socks = [clientSocket, undefined, otherClient] permissionFunc('freely') setTimeout(() => { assert(modelsMock.Note.update.called === false) assert(checkViewPermissionSpy.called === false) done() }, 5) }) it('should change permission to freely when config allowAnonymous is true', function (done) { configMock.allowAnonymous = true configMock.allowAnonymousEdits = false configMock.allowAnonymousViews = false realtime.notes[noteId].socks = [clientSocket, undefined, otherClient] permissionFunc('freely') setTimeout(() => { assert(checkViewPermissionSpy.callCount === 2) assert(otherClient.emit.called === false) assert(otherClient.disconnect.called === false) assert(clientSocket.emit.called === false) assert(clientSocket.disconnect.called === false) done() }, 5) }) it('should not change permission to freely when config allowAnonymousEdits is true', function (done) { configMock.allowAnonymous = false configMock.allowAnonymousEdits = true configMock.allowAnonymousViews = false realtime.notes[noteId].socks = [clientSocket, undefined, otherClient] permissionFunc('freely') setTimeout(() => { assert(modelsMock.Note.update.called === false) assert(checkViewPermissionSpy.called === false) done() }, 5) }) it('should not change permission to freely when config allowAnonymousViews is true', function (done) { configMock.allowAnonymous = false configMock.allowAnonymousEdits = false configMock.allowAnonymousViews = true realtime.notes[noteId].socks = [clientSocket, undefined, otherClient] permissionFunc('freely') setTimeout(() => { assert(modelsMock.Note.update.called === false) assert(checkViewPermissionSpy.called === false) done() }, 5) }) it('should change permission to editable when config allowAnonymousViews is true', function (done) { configMock.allowAnonymous = false configMock.allowAnonymousEdits = false configMock.allowAnonymousViews = true realtime.notes[noteId].socks = [clientSocket, undefined, otherClient] permissionFunc('editable') setTimeout(() => { assert(checkViewPermissionSpy.callCount === 2) assert(otherClient.emit.called === false) assert(otherClient.disconnect.called === false) assert(clientSocket.emit.called === false) assert(clientSocket.disconnect.called === false) done() }, 5) }) it('should change permission to freely when config allowAnonymousEdits and allowAnonymousViews are false true', function (done) { configMock.allowAnonymous = false configMock.allowAnonymousEdits = true configMock.allowAnonymousViews = true realtime.notes[noteId].socks = [clientSocket, undefined, otherClient] permissionFunc('freely') setTimeout(() => { assert(checkViewPermissionSpy.callCount === 2) assert(otherClient.emit.called === false) assert(otherClient.disconnect.called === false) assert(clientSocket.emit.called === false) assert(clientSocket.disconnect.called === false) done() }, 5) }) it('should change permission to editable when config allowAnonymousEdits and allowAnonymousViews are false true', function (done) { configMock.allowAnonymous = false configMock.allowAnonymousEdits = true configMock.allowAnonymousViews = true realtime.notes[noteId].socks = [clientSocket, undefined, otherClient] permissionFunc('editable') setTimeout(() => { assert(checkViewPermissionSpy.callCount === 2) assert(otherClient.emit.called === false) assert(otherClient.disconnect.called === false) assert(clientSocket.emit.called === false) assert(clientSocket.disconnect.called === false) done() }, 5) }) }) }) ================================================ FILE: test/realtime/updateNote.test.js ================================================ /* eslint-env node, mocha */ 'use strict' const assert = require('assert') const mock = require('mock-require') const sinon = require('sinon') const { removeLibModuleCache } = require('./utils') const { createFakeLogger } = require('../testDoubles/loggerFake') const realtimeJobStub = require('../testDoubles/realtimeJobStub') describe('realtime#updateNote', function () { let modelsStub let realtime const now = 1546300800000 let clock beforeEach(() => { removeLibModuleCache() clock = sinon.useFakeTimers({ now, toFake: ['Date'] }) modelsStub = { Note: { findOne: sinon.stub() }, User: { findOne: sinon.stub() } } mock('../../lib/config', {}) mock('../../lib/logger', createFakeLogger()) mock('../../lib/models', modelsStub) mock('../../lib/realtimeUpdateDirtyNoteJob', realtimeJobStub) mock('../../lib/realtimeCleanDanglingUserJob', realtimeJobStub) // mock('../../lib/realtimeSaveRevisionJob', realtimeJobStub) }) afterEach(() => { mock.stopAll() clock.restore() sinon.restore() removeLibModuleCache() }) it('should save history to each edited user', function (done) { modelsStub.Note.findOne.returns(Promise.resolve({})) realtime = require('../../lib/realtime/realtime') const updateHistoryStub = sinon.stub(realtime, 'updateHistory') const callback = sinon.stub() const note = { tempUsers: { user1: Date.now() } } realtime.updateNote(note, callback) clock.restore() setTimeout(() => { assert(updateHistoryStub.calledOnce) assert(updateHistoryStub.lastCall.calledWith('user1', note, now)) done() }, 50) }) it('should set lastchangeprofile when lastchangeuser is set', function (done) { const callback = sinon.stub() const note = { lastchangeuser: 'user1' } modelsStub.Note.findOne.returns(Promise.resolve({})) modelsStub.User.findOne.withArgs({ where: { id: 'user1' } }).returns(Promise.resolve({ id: 'user1', profile: '{ "displayName": "User 01" }' })) modelsStub.User.getProfile = sinon.stub().returns({ name: 'User 01' }) realtime = require('../../lib/realtime/realtime') realtime.updateNote(note, callback) clock.restore() setTimeout(() => { assert(note.lastchangeuserprofile.name === 'User 01') done() }, 50) }) it('should save note with new data', function (done) { const callback = sinon.stub() const note = { lastchangeuser: 'user1', server: { document: '# title\n\n## test2' }, authorship: [] } modelsStub.Note.parseNoteTitle = sinon.stub().returns('title') const updateNoteStub = sinon.stub().returns(Promise.resolve({})) modelsStub.Note.findOne.returns(Promise.resolve({ update: updateNoteStub })) modelsStub.User.findOne.withArgs({ where: { id: 'user1' } }).returns(Promise.resolve({ id: 'user1', profile: '{ "displayName": "User 01" }' })) modelsStub.User.getProfile = sinon.stub().returns({ name: 'User 01' }) clock.tick(1000) realtime = require('../../lib/realtime/realtime') realtime.updateNote(note, callback) setTimeout(() => { assert(note.lastchangeuserprofile.name === 'User 01') assert(callback.calledOnce) assert(callback.lastCall.args[0] === null) assert(updateNoteStub.calledOnce) assert(updateNoteStub.lastCall.args[0].lastchangeAt === now + 1000) assert(updateNoteStub.lastCall.args[0].title === 'title') assert(updateNoteStub.lastCall.args[0].content === '# title\n\n## test2') done() }, 50) }) it('should save note when lsatChangeUser is guest', function (done) { const callback = sinon.stub() const note = { server: { document: '# title\n\n## test2' }, authorship: [] } modelsStub.Note.parseNoteTitle = sinon.stub().returns('title') const updateNoteStub = sinon.stub().returns(Promise.resolve({})) modelsStub.Note.findOne.returns(Promise.resolve({ update: updateNoteStub })) modelsStub.User.getProfile = sinon.stub().returns({ name: 'User 01' }) clock.tick(1000) realtime = require('../../lib/realtime/realtime') realtime.updateNote(note, callback) setTimeout(() => { assert(modelsStub.User.findOne.callCount === 0) assert(note.lastchangeuserprofile === null) assert(callback.calledOnce) assert(callback.lastCall.args[0] === null) assert(updateNoteStub.calledOnce) assert(updateNoteStub.lastCall.args[0].lastchangeAt === now + 1000) assert(updateNoteStub.lastCall.args[0].title === 'title') assert(updateNoteStub.lastCall.args[0].content === '# title\n\n## test2') done() }, 50) }) it('should save note when lastChangeUser as same as database', function (done) { const callback = sinon.stub() const note = { lastchangeuser: 'user1', server: { document: '# title\n\n## test2' }, authorship: [] } modelsStub.Note.parseNoteTitle = sinon.stub().returns('title') const updateNoteStub = sinon.stub().returns(Promise.resolve({})) modelsStub.Note.findOne.returns(Promise.resolve({ update: updateNoteStub, lastchangeuserId: 'user1' })) modelsStub.User.getProfile = sinon.stub().returns({ name: 'User 01' }) clock.tick(1000) realtime = require('../../lib/realtime/realtime') realtime.updateNote(note, callback) setTimeout(() => { assert(modelsStub.User.findOne.callCount === 0) assert(modelsStub.User.getProfile.callCount === 0) assert(callback.calledOnce) assert(callback.lastCall.args[0] === null) assert(updateNoteStub.calledOnce) assert(updateNoteStub.lastCall.args[0].lastchangeAt === now + 1000) assert(updateNoteStub.lastCall.args[0].lastchangeuserId === 'user1') assert(updateNoteStub.lastCall.args[0].title === 'title') assert(updateNoteStub.lastCall.args[0].content === '# title\n\n## test2') done() }, 50) }) it('should not save note when lastChangeUser not found in database', function (done) { const callback = sinon.stub() const note = { lastchangeuser: 'user1', server: { document: '# title\n\n## test2' }, authorship: [] } modelsStub.Note.parseNoteTitle = sinon.stub().returns('title') const updateNoteStub = sinon.stub().returns(Promise.resolve({})) modelsStub.Note.findOne.returns(Promise.resolve({ update: updateNoteStub })) modelsStub.User.findOne.returns(Promise.resolve(null)) modelsStub.User.getProfile = sinon.stub().returns({ name: 'User 01' }) clock.tick(1000) realtime = require('../../lib/realtime/realtime') realtime.updateNote(note, callback) setTimeout(() => { assert(modelsStub.User.findOne.called) assert(modelsStub.User.getProfile.callCount === 0) assert(callback.calledOnce) assert(callback.lastCall.args[0] === null) assert(callback.lastCall.args[1] === null) assert(updateNoteStub.callCount === 0) done() }, 50) }) it('should not save note when note.server not exists', function (done) { const callback = sinon.stub() const note = { lastchangeuser: 'user1', authorship: [] } modelsStub.Note.parseNoteTitle = sinon.stub().returns('title') const updateNoteStub = sinon.stub().returns(Promise.resolve({})) modelsStub.Note.findOne.returns(Promise.resolve({ update: updateNoteStub })) modelsStub.User.findOne.withArgs({ where: { id: 'user1' } }).returns(Promise.resolve({ id: 'user1', profile: '{ "displayName": "User 01" }' })) modelsStub.User.getProfile = sinon.stub().returns({ name: 'User 01' }) clock.tick(1000) realtime = require('../../lib/realtime/realtime') realtime.updateNote(note, callback) setTimeout(() => { assert(note.lastchangeuserprofile.name === 'User 01') assert(callback.calledOnce) assert(callback.lastCall.args[0] === null) assert(callback.lastCall.args[1] === null) assert(updateNoteStub.callCount === 0) done() }, 50) }) }) ================================================ FILE: test/realtime/utils.js ================================================ 'use strict' const sinon = require('sinon') const path = require('path') function makeMockSocket (headers, query) { const broadCastChannelCache = {} return { id: Math.round(Math.random() * 10000), request: { user: {} }, handshake: { headers: Object.assign({}, headers), query: Object.assign({}, query) }, on: sinon.fake(), emit: sinon.fake(), broadCastChannelCache: {}, broadcast: { to: (channel) => { if (!broadCastChannelCache[channel]) { broadCastChannelCache[channel] = { channel: channel, emit: sinon.fake() } } return broadCastChannelCache[channel] } }, disconnect: sinon.fake(), rooms: [] } } function removeModuleFromRequireCache (modulePath) { delete require.cache[require.resolve(modulePath)] } function removeLibModuleCache () { const libPath = path.resolve(path.join(__dirname, '../../lib')) Object.keys(require.cache).forEach(key => { if (key.startsWith(libPath)) { delete require.cache[require.resolve(key)] } }) } exports.makeMockSocket = makeMockSocket exports.removeModuleFromRequireCache = removeModuleFromRequireCache exports.removeLibModuleCache = removeLibModuleCache ================================================ FILE: test/testDoubles/ProcessQueueFake.js ================================================ 'use strict' class ProcessQueueFake { constructor () { this.taskMap = new Map() this.queue = [] } start () { } stop () { } checkTaskIsInQueue (id) { return this.taskMap.has(id) } push (id, processFunc) { this.queue.push({ id: id, processFunc: processFunc }) this.taskMap.set(id, true) } process () { } } exports.ProcessQueueFake = ProcessQueueFake exports.ProcessQueue = ProcessQueueFake ================================================ FILE: test/testDoubles/loggerFake.js ================================================ 'use strict' const sinon = require('sinon') function createFakeLogger () { return { error: sinon.stub(), warn: sinon.stub(), info: sinon.stub(), debug: sinon.stub(), log: sinon.stub() } } exports.createFakeLogger = createFakeLogger ================================================ FILE: test/testDoubles/otFake.js ================================================ 'use strict' const sinon = require('sinon') class EditorSocketIOServerFake { constructor () { this.addClient = sinon.stub() this.onOperation = sinon.stub() this.onGetOperations = sinon.stub() this.updateSelection = sinon.stub() this.setName = sinon.stub() this.setColor = sinon.stub() this.getClient = sinon.stub() this.onDisconnect = sinon.stub() } } exports.EditorSocketIOServer = EditorSocketIOServerFake ================================================ FILE: test/testDoubles/realtimeJobStub.js ================================================ 'use strict' class realtimeJobStub { start () { } stop () { } } exports.realtimeJobStub = realtimeJobStub exports.UpdateDirtyNoteJob = realtimeJobStub exports.CleanDanglingUserJob = realtimeJobStub exports.SaveRevisionJob = realtimeJobStub ================================================ FILE: utils/string.js ================================================ 'use strict' function stripTags (s) { return s.replace(RegExp(']*>', 'gi'), '') } exports.stripTags = stripTags ================================================ FILE: webpack.common.js ================================================ const webpack = require('webpack') const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') const CopyWebpackPlugin = require('copy-webpack-plugin') const MiniCssExtractPlugin = require('mini-css-extract-plugin') // Fix possible nofile-issues const fs = require('fs') const gracefulFs = require('graceful-fs') gracefulFs.gracefulify(fs) module.exports = { name: 'app', plugins: [ new webpack.ProvidePlugin({ Visibility: 'visibilityjs', Cookies: 'js-cookie', key: 'keymaster', $: 'jquery', jQuery: 'jquery', 'window.jQuery': 'jquery', 'moment': 'moment', 'Handlebars': 'handlebars' }), new HtmlWebpackPlugin({ template: 'public/views/includes/header.ejs', chunks: ['font', 'index-styles', 'index'], filename: path.join(__dirname, 'public/views/build/index-header.ejs'), inject: false, chunksSortMode: 'manual' }), new HtmlWebpackPlugin({ template: 'public/views/includes/header.ejs', chunks: ['index-styles-pack', 'index-styles', 'index'], filename: path.join(__dirname, 'public/views/build/index-pack-header.ejs'), inject: false, chunksSortMode: 'manual' }), new HtmlWebpackPlugin({ template: 'public/views/includes/scripts.ejs', chunks: ['index'], filename: path.join(__dirname, 'public/views/build/index-scripts.ejs'), inject: false }), new HtmlWebpackPlugin({ template: 'public/views/includes/scripts.ejs', chunks: ['common', 'index-pack'], filename: path.join(__dirname, 'public/views/build/index-pack-scripts.ejs'), inject: false, chunksSortMode: 'manual' }), new HtmlWebpackPlugin({ template: 'public/views/includes/header.ejs', chunks: ['font', 'cover'], filename: path.join(__dirname, 'public/views/build/cover-header.ejs'), inject: false, chunksSortMode: 'manual' }), new HtmlWebpackPlugin({ template: 'public/views/includes/header.ejs', chunks: ['cover-styles-pack', 'cover'], filename: path.join(__dirname, 'public/views/build/cover-pack-header.ejs'), inject: false, chunksSortMode: 'manual' }), new HtmlWebpackPlugin({ template: 'public/views/includes/scripts.ejs', chunks: ['cover'], filename: path.join(__dirname, 'public/views/build/cover-scripts.ejs'), inject: false }), new HtmlWebpackPlugin({ template: 'public/views/includes/scripts.ejs', chunks: ['common', 'cover-pack'], filename: path.join(__dirname, 'public/views/build/cover-pack-scripts.ejs'), inject: false, chunksSortMode: 'manual' }), new HtmlWebpackPlugin({ template: 'public/views/includes/header.ejs', chunks: ['font', 'pretty-styles', 'pretty'], filename: path.join(__dirname, 'public/views/build/pretty-header.ejs'), inject: false, chunksSortMode: 'manual' }), new HtmlWebpackPlugin({ template: 'public/views/includes/header.ejs', chunks: ['pretty-styles-pack', 'pretty-styles', 'pretty'], filename: path.join(__dirname, 'public/views/build/pretty-pack-header.ejs'), inject: false, chunksSortMode: 'manual' }), new HtmlWebpackPlugin({ template: 'public/views/includes/scripts.ejs', chunks: ['pretty'], filename: path.join(__dirname, 'public/views/build/pretty-scripts.ejs'), inject: false }), new HtmlWebpackPlugin({ template: 'public/views/includes/scripts.ejs', chunks: ['common', 'pretty-pack'], filename: path.join(__dirname, 'public/views/build/pretty-pack-scripts.ejs'), inject: false, chunksSortMode: 'manual' }), new HtmlWebpackPlugin({ template: 'public/views/includes/header.ejs', chunks: ['font', 'slide-styles', 'slide'], filename: path.join(__dirname, 'public/views/build/slide-header.ejs'), inject: false, chunksSortMode: 'manual' }), new HtmlWebpackPlugin({ template: 'public/views/includes/header.ejs', chunks: ['slide-styles-pack', 'slide-styles', 'slide'], filename: path.join(__dirname, 'public/views/build/slide-pack-header.ejs'), inject: false, chunksSortMode: 'manual' }), new HtmlWebpackPlugin({ template: 'public/views/includes/scripts.ejs', chunks: ['slide'], filename: path.join(__dirname, 'public/views/build/slide-scripts.ejs'), inject: false }), new HtmlWebpackPlugin({ template: 'public/views/includes/scripts.ejs', chunks: ['slide-pack'], filename: path.join(__dirname, 'public/views/build/slide-pack-scripts.ejs'), inject: false }), new CopyWebpackPlugin([ { context: path.join(__dirname, 'node_modules/mathjax'), from: { glob: '**/*', dot: false }, to: 'MathJax/' }, { context: path.join(__dirname, 'node_modules/@hackmd/emojify.js'), from: { glob: 'dist/**/*', dot: false }, to: 'emojify.js/' }, { context: path.join(__dirname, 'node_modules/reveal.js'), from: 'js', to: 'reveal.js/js' }, { context: path.join(__dirname, 'node_modules/reveal.js'), from: 'css', to: 'reveal.js/css' }, { context: path.join(__dirname, 'node_modules/reveal.js'), from: 'lib', to: 'reveal.js/lib' }, { context: path.join(__dirname, 'node_modules/reveal.js'), from: 'plugin', to: 'reveal.js/plugin' }, { context: path.join(__dirname, 'public/js'), from: 'revealjs-plugins', to: 'revealjs-plugins' }, { context: path.join(__dirname, 'node_modules/dictionary-de'), from: '*', to: 'dictionary-de/' }, { context: path.join(__dirname, 'node_modules/dictionary-de-at'), from: '*', to: 'dictionary-de-at/' }, { context: path.join(__dirname, 'node_modules/dictionary-de-ch'), from: '*', to: 'dictionary-de-ch/' }, { context: path.join(__dirname, 'node_modules/dictionary-en-gb'), from: '*', to: 'dictionary-en-gb/' }, { context: path.join(__dirname, 'node_modules/leaflet'), from: 'dist', to: 'leaflet' }, { context: path.join(__dirname, 'node_modules/fork-awesome'), from: 'fonts', to: 'fork-awesome/fonts' }, { context: path.join(__dirname, 'node_modules/fork-awesome'), from: 'css', to: 'fork-awesome/css' }, { context: path.join(__dirname, 'node_modules/mermaid'), from: 'dist/mermaid.min.js', to: 'mermaid/mermaid.min.js' } ]), new MiniCssExtractPlugin( process.env.NODE_ENV === 'production' ? { filename: '[name].[contenthash].css', chunkFilename: '[name].[contenthash].css', } : {}, ) ], entry: { font: path.join(__dirname, 'public/css/google-font.css'), common: [ 'expose-loader?jQuery!expose-loader?$!jquery', 'velocity-animate', 'imports-loader?$=jquery!jquery-mousewheel', 'bootstrap' ], cover: [ 'core-js/stable', 'regenerator-runtime/runtime', path.join(__dirname, 'public/js/cover.js') ], 'cover-styles-pack': [ path.join(__dirname, 'node_modules/bootstrap/dist/css/bootstrap.min.css'), path.join(__dirname, 'public/css/bootstrap-social.css'), path.join(__dirname, 'node_modules/select2/select2.css'), path.join(__dirname, 'node_modules/select2/select2-bootstrap.css') ], 'cover-pack': [ 'core-js/stable', 'regenerator-runtime/runtime', 'bootstrap-validator', 'expose-loader?select2!select2', 'expose-loader?moment!moment', path.join(__dirname, 'public/js/cover.js') ], index: [ 'core-js/stable', 'regenerator-runtime/runtime', 'script-loader!jquery-ui-resizable', 'script-loader!codemirror', 'script-loader!inlineAttachment', 'script-loader!jqueryTextcomplete', 'script-loader!codemirrorInlineAttachment', 'script-loader!ot', 'flowchart.js', 'imports-loader?Raphael=raphael!js-sequence-diagrams', path.join(__dirname, 'public/js/index.js') ], 'index-styles': [ path.join(__dirname, 'public/vendor/jquery-ui/jquery-ui.min.css'), path.join(__dirname, 'public/vendor/codemirror-spell-checker/spell-checker.min.css'), path.join(__dirname, 'node_modules/@hackmd/codemirror/lib/codemirror.css'), path.join(__dirname, 'node_modules/@hackmd/codemirror/addon/fold/foldgutter.css'), path.join(__dirname, 'node_modules/@hackmd/codemirror/addon/display/fullscreen.css'), path.join(__dirname, 'node_modules/@hackmd/codemirror/addon/dialog/dialog.css'), path.join(__dirname, 'node_modules/@hackmd/codemirror/addon/scroll/simplescrollbars.css'), path.join(__dirname, 'node_modules/@hackmd/codemirror/addon/search/matchesonscrollbar.css'), path.join(__dirname, 'node_modules/@hackmd/codemirror/theme/monokai.css'), path.join(__dirname, 'node_modules/@hackmd/codemirror/theme/one-dark.css'), path.join(__dirname, 'node_modules/@hackmd/codemirror/theme/dracula.css'), path.join(__dirname, 'node_modules/@hackmd/codemirror/theme/material.css'), path.join(__dirname, 'node_modules/@hackmd/codemirror/theme/nord.css'), path.join(__dirname, 'node_modules/@hackmd/codemirror/theme/panda-syntax.css'), path.join(__dirname, 'node_modules/@hackmd/codemirror/theme/solarized.css'), path.join(__dirname, 'public/css/codemirror-extend/ayu-dark.css'), path.join(__dirname, 'public/css/codemirror-extend/ayu-mirage.css'), path.join(__dirname, 'public/css/codemirror-extend/tomorrow-night-bright.css'), path.join(__dirname, 'public/css/codemirror-extend/tomorrow-night-eighties.css'), path.join(__dirname, 'public/css/codemirror-extend/one-dark.css'), path.join(__dirname, 'node_modules/@hackmd/codemirror/mode/tiddlywiki/tiddlywiki.css'), path.join(__dirname, 'node_modules/@hackmd/codemirror/mode/mediawiki/mediawiki.css'), path.join(__dirname, 'public/css/github-extract.css'), path.join(__dirname, 'public/vendor/showup/showup.css'), path.join(__dirname, 'public/css/mermaid.css'), path.join(__dirname, 'public/css/markdown.css'), path.join(__dirname, 'public/css/slide-preview.css') ], 'index-styles-pack': [ path.join(__dirname, 'node_modules/bootstrap/dist/css/bootstrap.min.css'), path.join(__dirname, 'public/css/bootstrap-social.css'), path.join(__dirname, 'node_modules/ionicons/css/ionicons.min.css'), path.join(__dirname, 'node_modules/leaflet/dist/leaflet.css') ], 'index-pack': [ 'core-js/stable', 'regenerator-runtime/runtime', 'script-loader!jquery-ui-resizable', 'bootstrap-validator', 'expose-loader?jsyaml!js-yaml', 'expose-loader?moment!moment', 'script-loader!handlebars', 'expose-loader?hljs!highlight.js', 'emojify.js', 'script-loader!gist-embed', 'script-loader!codemirror', 'script-loader!inlineAttachment', 'script-loader!jqueryTextcomplete', 'script-loader!codemirrorInlineAttachment', 'script-loader!ot', 'flowchart.js', 'imports-loader?Raphael=raphael!js-sequence-diagrams', 'script-loader!viz.js', 'script-loader!viz.render.js', 'script-loader!abcjs', 'script-loader!vega', 'script-loader!vega-lite', 'script-loader!vega-embed', 'expose-loader?io!socket.io-client', 'expose-loader?L!leaflet', path.join(__dirname, 'public/js/index.js') ], pretty: [ 'core-js/stable', 'regenerator-runtime/runtime', 'flowchart.js', 'imports-loader?Raphael=raphael!js-sequence-diagrams', path.join(__dirname, 'public/js/pretty.js') ], 'pretty-styles': [ path.join(__dirname, 'public/css/github-extract.css'), path.join(__dirname, 'public/css/mermaid.css'), path.join(__dirname, 'public/css/markdown.css'), path.join(__dirname, 'public/css/slide-preview.css') ], 'pretty-styles-pack': [ path.join(__dirname, 'node_modules/bootstrap/dist/css/bootstrap.min.css'), path.join(__dirname, 'node_modules/ionicons/css/ionicons.min.css'), path.join(__dirname, 'node_modules/leaflet/dist/leaflet.css') ], 'pretty-pack': [ 'core-js/stable', 'regenerator-runtime/runtime', 'expose-loader?jsyaml!js-yaml', 'expose-loader?moment!moment', 'script-loader!handlebars', 'expose-loader?hljs!highlight.js', 'emojify.js', 'script-loader!gist-embed', 'flowchart.js', 'imports-loader?Raphael=raphael!js-sequence-diagrams', 'script-loader!viz.js', 'script-loader!viz.render.js', 'script-loader!abcjs', 'script-loader!vega', 'script-loader!vega-lite', 'script-loader!vega-embed', 'expose-loader?L!leaflet', path.join(__dirname, 'public/js/pretty.js') ], slide: [ 'core-js/stable', 'regenerator-runtime/runtime', 'bootstrap-tooltip', 'flowchart.js', 'imports-loader?Raphael=raphael!js-sequence-diagrams', path.join(__dirname, 'public/js/slide.js') ], 'slide-styles': [ path.join(__dirname, 'public/vendor/bootstrap/tooltip.min.css'), path.join(__dirname, 'public/css/github-extract.css'), path.join(__dirname, 'public/css/mermaid.css'), path.join(__dirname, 'public/css/markdown.css') ], 'slide-styles-pack': [ path.join(__dirname, 'node_modules/ionicons/css/ionicons.min.css'), path.join(__dirname, 'node_modules/leaflet/dist/leaflet.css') ], 'slide-pack': [ 'core-js/stable', 'regenerator-runtime/runtime', 'expose-loader?jQuery!expose-loader?$!jquery', 'velocity-animate', 'imports-loader?$=jquery!jquery-mousewheel', 'bootstrap-tooltip', 'expose-loader?jsyaml!js-yaml', 'expose-loader?moment!moment', 'script-loader!handlebars', 'expose-loader?hljs!highlight.js', 'emojify.js', 'script-loader!gist-embed', 'flowchart.js', 'imports-loader?Raphael=raphael!js-sequence-diagrams', 'script-loader!viz.js', 'script-loader!viz.render.js', 'script-loader!abcjs', 'script-loader!vega', 'script-loader!vega-lite', 'script-loader!vega-embed', 'expose-loader?Reveal!reveal.js', 'expose-loader?L!leaflet', path.join(__dirname, 'public/js/slide.js') ] }, output: { path: path.join(__dirname, 'public/build'), publicPath: '/build/', filename: '[name].js' }, resolve: { modules: ['node_modules'], extensions: ['.js', '.mjs'], alias: { codemirror: path.join(__dirname, 'node_modules/@hackmd/codemirror/codemirror.min.js'), inlineAttachment: path.join(__dirname, 'public/vendor/inlineAttachment/inline-attachment.js'), jqueryTextcomplete: path.join(__dirname, 'public/vendor/jquery-textcomplete/jquery.textcomplete.js'), codemirrorInlineAttachment: path.join(__dirname, 'public/vendor/inlineAttachment/codemirror.inline-attachment.js'), ot: path.join(__dirname, 'public/vendor/ot/ot.min.js'), handlebars: path.join(__dirname, 'node_modules/handlebars/dist/handlebars.min.js'), 'jquery-ui-resizable': path.join(__dirname, 'public/vendor/jquery-ui/jquery-ui.min.js'), 'gist-embed': path.join(__dirname, 'node_modules/gist-embed/gist-embed.min.js'), 'bootstrap-tooltip': path.join(__dirname, 'public/vendor/bootstrap/tooltip.min.js'), 'reveal-markdown': path.join(__dirname, 'public/js/reveal-markdown.js'), abcjs: path.join(__dirname, 'public/vendor/abcjs_basic_3.1.1-min.js'), raphael: path.join(__dirname, 'node_modules/raphael/raphael.min.js'), 'js-sequence-diagrams': path.join(__dirname, 'node_modules/@hackmd/js-sequence-diagrams/build/main.js'), vega: path.join(__dirname, 'node_modules/vega/build/vega.min.js'), 'vega-lite': path.join(__dirname, 'node_modules/vega-lite/build/vega-lite.min.js'), 'vega-embed': path.join(__dirname, 'node_modules/vega-embed/build/vega-embed.min.js'), 'emojify.js': path.join(__dirname, 'node_modules/@hackmd/emojify.js/dist/js/emojify-browser.min.js'), 'markdown-it': path.join(__dirname, 'node_modules/markdown-it/dist/markdown-it.js'), 'viz.js': path.join(__dirname, 'node_modules/viz.js/viz.js'), 'viz.render.js': path.join(__dirname, 'node_modules/viz.js/full.render.js'), markdownlint: path.join(__dirname, 'node_modules/markdownlint/demo/markdownlint-browser.js') } }, externals: { 'viz.js': 'Viz', 'socket.io-client': 'io', 'jquery': '$', 'moment': 'moment', 'handlebars': 'Handlebars', 'highlight.js': 'hljs', 'select2': 'select2' }, module: { rules: [{ test: /\.mjs$/, type: 'javascript/auto', include: /node_modules/ }, { test: /node_modules\/markmap.*\/.*\.mjs$/, use: [{ loader: 'babel-loader' }], type: 'javascript/auto' }, { test: /node_modules\/markmap.*\/.*\.js$/, use: [{ loader: 'babel-loader', options: { presets: [ ['@babel/preset-env', { modules: 'cjs' }] ] } }] }, { test: /node_modules\/yaml\/browser\/dist\/.*\.js$/, use: [{ loader: 'babel-loader' }] }, { test: /node_modules\/@vscode\/markdown-it-katex\/.*\.js$/, use: [{ loader: 'babel-loader', options: { presets: [ ['@babel/preset-env', { modules: 'cjs' }] ], plugins: [ '@babel/plugin-transform-optional-chaining' ] } }] }, { test: /\.js$/, use: [{ loader: 'babel-loader' }], exclude: [ path.resolve(__dirname, 'node_modules'), path.resolve(__dirname, 'public/vendor') ] }, { test: /\.css$/, use: [ MiniCssExtractPlugin.loader, 'css-loader' ] }, { test: /\.scss$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { importLoaders: 1 } }, 'sass-loader' ] }, { test: /\.less$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader', options: { importLoaders: 1 } }, 'less-loader' ] }, { test: /\.eot(\?v=\d+\.\d+\.\d+)?$/, use: [{ loader: 'file-loader' }] }, { test: /\.html$/, use: [{ loader: 'string-loader' }] }, { test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/, use: [{ loader: 'url-loader', options: { prefix: 'font/', limit: '5000' } }] }, { test: /\.ttf(\?v=\d+\.\d+\.\d+)?$/, use: [{ loader: 'url-loader', options: { limit: '5000', mimetype: 'application/octet-stream' } }] }, { test: /\.svg(\?v=\d+\.\d+\.\d+)?$/, exclude: path.resolve(__dirname, 'public/js/lib/renderer/fretboard'), use: [{ loader: 'url-loader', options: { limit: '10000', mimetype: 'svg+xml' } }] }, { test: /.*\/fretboard\/svg\/.*\.svg$/, loader: 'string-loader' }, { test: /\.png(\?v=\d+\.\d+\.\d+)?$/, use: [{ loader: 'url-loader', options: { limit: '10000', mimetype: 'image/png' } }] }, { test: /\.gif(\?v=\d+\.\d+\.\d+)?$/, use: [{ loader: 'url-loader', options: { limit: '10000', mimetype: 'image/gif' } }] }, { test: /@hackmd\/codemirror\/addon\/lint\/lint/, use: [{ loader: 'script-loader' }] }] }, node: { fs: 'empty', os: 'empty' }, stats: { assets: false } } ================================================ FILE: webpack.dev.js ================================================ const common = require('./webpack.common.js') const htmlexport = require('./webpack.htmlexport') const merge = require('webpack-merge') module.exports = [ // merge common config merge(common, { mode: 'development', devtool: 'cheap-module-eval-source-map' }), merge(htmlexport, { mode: 'development', devtool: 'cheap-module-eval-source-map' })] ================================================ FILE: webpack.htmlexport.js ================================================ const MiniCssExtractPlugin = require('mini-css-extract-plugin') const path = require('path') module.exports = { name: 'save-as-html', entry: { htmlExport: path.join(__dirname, 'public/js/htmlExport.js') }, module: { rules: [{ test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'] }] }, output: { path: path.join(__dirname, 'public/build'), publicPath: '/build/', filename: '[name].js' }, plugins: [ new MiniCssExtractPlugin({ filename: 'html.min.css' }) ] } ================================================ FILE: webpack.prod.js ================================================ const common = require('./webpack.common.js') const htmlexport = require('./webpack.htmlexport') const merge = require('webpack-merge') const path = require('path') const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin') module.exports = [ merge(common, { mode: 'production', output: { path: path.join(__dirname, 'public/build'), publicPath: '/build/', filename: '[name].[contenthash].js' } }), merge(htmlexport, { mode: 'production', optimization: { minimizer: [ new OptimizeCSSAssetsPlugin({}) ] } })]