diff --git a/.gitignore b/.gitignore
index fa6cbb454b..773b4726c0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -77,3 +77,4 @@ prime/
 *.snap
 *.snap-build
 *_source.tar.bz2
+.DS_Store
\ No newline at end of file
diff --git a/.golangci.yml b/.golangci.yml
index 9c78a83451..fd7393372b 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -19,6 +19,9 @@ linters:
   disable-all: true
   fast: false
 
+run:
+  timeout: 3m
+
 linters-settings:
   gocritic:
     disabled-checks:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 540b1a9790..2a88eb1035 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,291 @@ This changelog goes through all the changes that have been made in each release
 without substantial changes to our git log; to see the highlights of what has
 been added to each release, please refer to the [blog](https://blog.gitea.io).
 
+## [1.10.0-RC1](https://github.com/go-gitea/gitea/releases/tag/v1.10.0-rc1) - 2019-10-14
+* BREAKING
+  * Remove legacy handling of drone token (#8191)
+  * Change repo search to use exact match for topic search. (#7941)
+  * Add pagination for admin api get orgs and fix only list public orgs bug (#7742)
+  * Implement the ability to change the ssh port to match what is in the gitea config (#7286)
+* FEATURE
+  * Org/Members: display 2FA members states + optimize sql requests (#7621)
+  * SetDefaultBranch on pushing to empty repository (#7610)
+  * Adds side-by-side diff for images (#6784)
+  * API method to list all commits of a repository (#6408)
+  * Password Complexity Checks  (#6230)
+  * Add option to initialize repository with labels (#6061)
+  * Add additional password hash algorithms (#6023)
+* BUGFIXES
+  * Fix errors in create org UI regarding team access permission (#8506)
+  * Fix bug on FindExternalUsersByProvider (#8504)
+  * Create .ssh dir as necessary (#8486)
+  * IsBranchExist: return false if provided name is empty (#8485)
+  * Making openssh listen on SSH_LISTEN_PORT not SSH_PORT (#8477)
+  * Add check for empty set when dropping indexes during migration (#8471)
+  * LFS files are relative to LFS content path, ensure that when deleting they are made relative to this (#8455)
+  * Ensure Request Body Readers are closed in LFS server (#8454)
+  * Fix template bug on mirror repository setting page (#8438)
+  * Fix migration v96 to keep issue attachments (#8435)
+  * Update strk.kbt.io/projects/go/libravatar to latest (#8429)
+  * Singular form for files that has only one line (#8416)
+  * Check for either escaped or unescaped wiki filenames (#8408)
+  * Allow users with explicit read access to give approvals (#8382)
+  * Fix editor commit to new branch if PR disabled (#8375)
+  * readd .markdown class to all markup renderers (#8357)
+  * Upgrade xorm to v0.7.9 to fix some bugs (#8354)
+  * Fix column name ambiguity in GetUserIssueStats() (#8347)
+  * Change general form binding to gogs form (#8334)
+  * Fix pull request commit status in user dashboard list (#8321)
+  * Fix repo_admin_change_team_access always checked in org settings (#8319)
+  * Update to github.com/lafriks/xormstore@v1.3.0 (#8317)
+  * Show correct commit status in PR list (#8316)
+  * Bugfix for image compare and minor improvements to image compare (#8289)
+  * Update xorm (#8286)
+  * Fix API for edit and delete release attachment (#8285)
+  * Fix nil object access in some conditions when parsing cross references (#8281)
+  * Fix label count (#8267)
+  * Only show teams access for organization repositories on collaboration setting page (#8265)
+  * Test more reserved usernames (#8263)
+  * Rewrite reference processing code in preparation for opening/closing from comment references (#8261)
+  * Fix assets key on release webhook (#8253)
+  * Allow registration when button is hidden (#8237)
+  * Fix release API URL generation (#8234)
+  * Fix milestone num_issues (#8221)
+  * MS Teams webhook misses commit messages (#8209)
+  * Fix data race (#8204)
+  * Fix team user api (#8172)
+  * Fix pull merge 500 error caused by git-fetch breaking behaviors (#8161)
+  * Make show private icon when repo avatar set (#8144)
+  * Add reviewers as participants (#8121)
+  * Fix Go 1.13 private repository go get issue (#8112)
+  * feat: highlight issue references with : (#8101)
+  * Make AllowedUsers configurable in sshd_config (#8094)
+  * Strict name matching for Repository.GetTagID() (#8074)
+  * Avoid ambiguity of branch/directory names for the git-diff-tree command (#8066)
+  * Add change title notification for issues (#8061)
+  * [ssh] fix the config specification in the authorized_keys template (#8031)
+  * Fix reading git notes from nested trees (#8026)
+  * Fixes synchronize tags to releases for repository - makes sure we are only getting tag refs (#7990)
+  * Fix adding default Telegram webhook (#7972)
+  * Run CORS handler first for /api routes (#7967)
+  * Abort synchronization from LDAP source if there is some error. (#7960)
+  * Fix wrong sender when send slack webhook (#7918)
+  * Fix bug when migrating a private repository (#7917)
+  * Evaluate emojis in commit messages in list view (#7906)
+  * Fix upload file type check (#7890)
+  * lfs/lock: round locked_at timestamp to second (#7872)
+  * fix non existent milestone with 500 error instead of 404 (#7867)
+  * gpg/bugfix: Use .ExpiredUnix.IsZero to display green color of forever valid gpg key (#7846)
+  * Fix duplicate call of webhook (#7821)
+  * Enable switching to a different source branch when PR already exists (#7819)
+  * Convert files to utf-8 for indexing (#7814)
+  * Do not fetch all refs in pull-request compare (#7797)
+  * Fix multiple bugs with statuses endpoints at API (#7785)
+  * Restore functionality for early gits (#7775)
+  * Fix Slack webhook fork message (#7774)
+  * Rewrite existing repo units if setting is not included in api body (#7763)
+  * Fix rename failed when rewrite public keys (#7761)
+  * Fix approvals counting (#7757)
+  * Add migration step to remove old repo_indexer_status orphaned records (#7746)
+  * Fix repo_index_status lingering when deleting a repository (#7734)
+  * Remove camel case tokenization from repo indexer (#7733)
+  * Fix milestone completness calculation when migrating (#7725)
+  * Regression: Include "executable" files in the index, as they are not necessarily … (#7718)
+  * Fixes indexed repos keeping outdated indexes when files grow too large (#7712)
+  * Skip non-regular files (e.g. submodules) on repo indexing (#7711)
+  * Fix dropTableColumns sqlite implementation (#7710)
+  * Update gopkg.in/src-d/go-git.v4 to v4.13.1 (#7705)
+  * improve branches list performance and fix protected branch icon when no-login (#7695)
+  * Correct wrong datetime format for git (#7689)
+  * Move add to hook queue for created repo to outside xorm session. (#7675)
+  * sugestion to use range .Branches (#7674)
+  * Fix bug on migrating milestone from github (#7665)
+  * hide delete/restore button on archived repos (#7658)
+  * css: use flex to fix floating paginate (#7656)
+  * Fix syntax highlight initialization (#7617)
+  * Fix panic on push at - Merging pull request causes 500 error (#7615)
+  * Make PKCS8, PEM and SSH2 keys work (#7600)
+  * Fix mistake in arc-green.less split-diff css code. (#7587)
+  * Handle ErrUserProhibitLogin in http git (#7586)
+  * Fix bug create/edit wiki pages when code master branch protected (#7580)
+  * Fixes Malformed URLs in API git/commits response (#7565)
+  * Fix file header overflow in file and blame views (#7562)
+  * Improve SSH key parser to handle newlines in keys (#7522)
+  * Fix empty commits now showing in repo overview (#7521)
+  * Fix repository's pull request count error (#7518)
+  * Fix markdown invoke sequence (#7513)
+  * Remove duplicated webhook trigger (#7511)
+  * Update User.NumRepos atomically in createRepository (#7493)
+  * Fix settings page of repo you aren't admin print error - Settings pages giving UnitType error message (#7482)
+  * Fix redirection after file edit - Handles all redirects for Web UI File CRUD (#7478)
+  * cmd/serv: actually exit after fatal errors (#7458)
+  * Fix an issue with some pages throwing 'not defined' js exceptions (#7450)
+  * fix Dropzone.js integration (#7445)
+  * Fix regex for issues in commit messages (#7444)
+  * Diff: Fix indentation on unhighlighted code (#7435)
+  * Only show "New Pull Request" button if repo allows pulls (#7426)
+  * Upgrade macaron/captcha to fix random error problem (#7407)
+  * create class for inline positioned lists (#7393)
+  * Fetch refs for successful testing for tag (#7388)
+  * add missing template variable on organisation settings (#7385)
+  * fix post parameter - on issue list - unset assignee (#7380)
+  * fix/define autochecked checkboxes on issue list in firefox (#7320)
+  * only return head: null if source branch was deleted (#6705)
+* ENHANCEMENT
+  * Add nofollow to sign in links (#8509)
+  * vendor: update mvdan.cc/xurls/v2 to v2.1.0 (#8495)
+  * Update milestone issues numbers when save milestone and other code improvements (#8411)
+  * Add extra user information when migrating release (#8331)
+  * Require overall success if no context is given for status check (#8318)
+  * Transaction-aware retry create issue to cope with duplicate keys (#8307)
+  * Change link on issue milestone (#8246)
+  * Alwaywas return local url for users avatar (#8245)
+  * Move some milestone functions to a standalone package (#8213)
+  * Move create issue comment to comments package (#8212)
+  * Disable max height property of comment textarea (#8203)
+  * Add 'Mentioning you' group to /issues page (#8201)
+  * oauth2 with remote Gitea (#8149)
+  * Reference issues from pull requests and other issues (#8137)
+  * Fix webhooks to use proxy from environment (#8116)
+  * Add merged commit id on pull view when it's merged (#8062)
+  * Add teams to repo on collaboration page. (#8045)
+  * Update swagger to 0.20.1  (#8010)
+  * Make link last commit massages in repository home page and commit tables (#8006)
+  * Add API endpoint for accessing repo topics (#7963)
+  * Include description in repository search (#7942)
+  * Use gitea forked macaron (#7933)
+  * Fix pull creation with empty changes (#7920)
+  * Allow token as authorization for accessing attachments (#7909)
+  * Retry create issue to cope with duplicate keys (#7898)
+  * Move git diff codes from models to services/gitdiff (#7889)
+  * migrate gplus to google oauth2 provider (#7885)
+  * Remove unique filter from repo indexer analyzer. (#7878)
+  * Detect delimiter in CSV rendering (#7869)
+  * Import topics during migration (#7851)
+  * Move CreateReview to modules/pull (#7841)
+  * vendor: update pdf.js to v2.1.266 (#7834)
+  * Support SSH_LISTEN_PORT env var in docker app.ini template (#7829)
+  * Add Ability for User to Customize Email Notification Frequency (#7813)
+  * Move database settings from models to setting (#7806)
+  * Display ui time with customize time location (#7792)
+  * Implement webhook branch filter (#7791)
+  * Restrict repository indexing by glob match (#7767)
+  * Api: advanced settings for repository (external wiki, issue tracker etc.) (#7756)
+  * Update migrated repositories' issues/comments/prs poster id if user has a github external user saved (#7751)
+  * deps: Upgrade gopkg.in/editorconfig/editorconfig-core-go.v1 (#7749)
+  * Apply emoji on commit graph page (#7743)
+  * Add a lot of extension to language mappings for syntax highlights (#7741)
+  * Add SQL execution on log and indexes on table repository and comment (#7740)
+  * Set DB connection error level to error (#7724)
+  * Check commit message hashes before making links (#7713)
+  * remove unnecessary fmt on generate bindata (#7706)
+  * Fix specific highlighting (CMakeLists.txt ...) (#7686)
+  * Add file status on API (#7671)
+  * Add support for DEFAULT_ORG_MEMBER_VISIBLE (#7669)
+  * Provide links in commit summaries in commits table/view list (#7659)
+  * Change length of some repository's columns (#7652)
+  * Move commit repo action from models to repofiles package (#7645)
+  * fix wrong email when use gitea as OAuth2 provider (#7640)
+  * [Branch View] add download button (#7604)
+  * Update to xorm@v0.7.4 (#7596)
+  * use 403 instead of 401 for ErrUserProhibitLogin (#7591)
+  * Removed unnecessary conversions (#7557)
+  * Un-lambda base.FileSize (#7556)
+  * Added missing error checks in tests (#7554)
+  * Move create release from models to a standalone package (#7539)
+  * Make default branch name link to default branch (#7519)
+  * Added total count of contributions to heatmap (#7517)
+  * Move mirror to a standalone package from models (#7486)
+  * Move models.PushUpdate to repofiles.PushUpdate (#7485)
+  * Include thread related headers in issue/coment mail (#7484)
+  * Refuse merge until all required status checks success (#7481)
+  * convert all js var to let/const (#7464)
+  * Only create branches for opened pull requestes when migrating from github (#7463)
+  * jQuery 3 (#7425)
+  * Add notification placeholder (#7409)
+  * Search Commits via Commit Hash (#7400)
+  * Move status table to cron package (#7370)
+  * wiki - page revisions list  (#7369)
+  * Display original author and URL information when showing migrated issues/comments (#7352)
+  * Refactor filetype is not allowed errors (#7309)
+  * switch to use gliderlabs/ssh for builtin server (#7250)
+  * Remove settting dependency on modules/session (#7237)
+  * Move all mail related codes from models to services/mailer (#7200)
+  * Support git.PATH entry in app.ini (#6772)
+  * Support setting cookie domain (#6288)
+  * Move migrating repository from frontend to backend (#6200)
+  * Delete releases attachments if release is deleted (#6068)
+* SECURITY
+  * Ignore mentions for users with no access (#8395)
+  * Be more strict with git arguments (#7715)
+  * reserve .well-known username (#7637)
+* TRANSLATION
+  * Latvian translation for home page (#8468)
+  * Add home template italian translation (#8352)
+  * fix misprint (#7452)
+* BUILD
+  * use go 1.13 (#8088)
+* MISC
+  * add file line count info on UI (#8396)
+  * Make issues page left menu 100% width and add reponame as title attribute (#8359)
+  * [arc-green] white on hover for active menu items (#8344)
+  * Move ref (branch or tag) location on issue list page (#8157)
+  * apply emoji on dashboard issue list labels (#8156)
+  * 1148: Take up the full width when viewing the diff in split view. (#8114)
+  * Display description of 'make this repo private' as help text, not as tooltip (#8097)
+  * Fixes deformed emoji in pull request reviews (#8047)
+  * Add strike to old header on comment (#8046)
+  * Add tooltip for the visibility checkbox in /repo/create (#8025)
+  * Update github.com/lafriks/xormstore and tidy up mod.go (#8020)
+  * keep blame view buttons sequence consistent with normal view when view a file (#8007)
+  * Use "Pull Request" instead of "Merge Request" (#8003)
+  * Move line number to :before attr to hide from search on browser (#8002)
+  * Changed black color to white for (read) number label on issue list page (#8000)
+  * [Branch View] show "New Pull Request" Button only if posible (#7977)
+  * Fix hook problem by only setting the git environment variables if we are passed them (#7854)
+  * Prevent Commit Status and Message From Overflowing On Branch Page (#7800)
+  * Fix global search result CSS, misc CSS tweaks (#7789)
+  * Tweak label border CSS (#7739)
+  * Fix create menu item widths (#7708)
+  * Extract the username and password from the mirror url (#7651)
+  * [Branch View] Delete duplicate protection symbol (#7624)
+  * [Branch View] Delete Table Header (#7622)
+  * [Branch View] icons to buttons (#7602)
+  * update js dependencies (#7462)
+  * Add Extra Info to Branches Page (#7461)
+  * Bump lodash from 4.17.11 to 4.17.14 (#7459)
+  * wiki history improvements (#7391)
+  * ui fixes - compare view and archieved repo issues (#7345)
+  * dark theme scrollbars (#7269)
+  * wiki - editor - add buttons 'inline code', 'empty checkbox', 'checked checkbox' (#7243)
+  * Fix Statuses API only shows first 10 statuses: Add paging and extend API GetCommitStatuses (#7141)
+
+## [1.9.4](https://github.com/go-gitea/gitea/releases/tag/v1.9.4) - 2019-10-08
+* BUGFIXES
+  * Highlight issue references (#8101) (#8404)
+  * Fix bug when migrating a private repository #7917 (#8403)
+  * Change general form binding to gogs form (#8334) (#8402)
+  * Fix editor commit to new branch if PR disabled (#8375) (#8401)
+  * Fix milestone num_issues (#8221) (#8400)
+  * Allow users with explicit read access to give approvals (#8398)
+  * Fix commit status in PR #8316 and PR #8321 (#8339)
+  * Fix API for edit and delete release attachment (#8290)
+  * Fix assets on release webhook (#8283)
+  * Fix release API URL generation (#8239)
+  * Allow registration when button is hidden (#8238)
+  * MS Teams webhook misses commit messages (backport v1.9) (#8225)
+  * Fix data race (#8206)
+  * Fix pull merge 500 error caused by git-fetch breaking behaviors (#8194)
+  * Fix the SSH config specification in the authorized_keys template (#8193)
+  * Fix reading git notes from nested trees (#8189)
+  * Fix team user api (#8172) (#8188)
+  * Add reviewers as participants (#8124)
+* BUILD
+  * Use vendored go-swagger (#8087) (#8165)
+  * Fix version-validation for GO 1.13 (go-macaron/cors) (#8389)
+* MISC
+  * Make show private icon when repo avatar set (#8144) (#8175)
+
 ## [1.9.3](https://github.com/go-gitea/gitea/releases/tag/v1.9.3) - 2019-09-06
 * BUGFIXES
   * Fix go get from a private repository with Go 1.13 (#8100)
@@ -96,20 +381,20 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
   * Move add to hook queue for created repo to outside xorm session. (#7682) (#7675)
   * Show protection symbol if needed on default branch (#7660) (#7668)
   * Hide delete/restore button on archived repos (#7660)
-  * Fix bug on migrating milestone from github (#7665) (#7666) 
+  * Fix bug on migrating milestone from github (#7665) (#7666)
   * Use flex to fix floating paginate (#7656) (#7662)
   * Change length of some repository's columns (#7652) (#7655)
   * Fix wrong email when use gitea as OAuth2 provider (#7640) (#7647)
-  * Fix syntax highlight initialization (#7617) (#7626) 
+  * Fix syntax highlight initialization (#7617) (#7626)
   * Fix bug create/edit wiki pages when code master branch protected (#7580) (#7623)
   * Fix panic on push at #7611 (#7615) (#7618)
-  * Handle ErrUserProhibitLogin in http git (#7586, #7591) (#7590) 
+  * Handle ErrUserProhibitLogin in http git (#7586, #7591) (#7590)
   * Fix color of split-diff view in dark theme (#7587) (#7589)
-  * Fix file header overflow in file and blame views (#7562) (#7579) 
+  * Fix file header overflow in file and blame views (#7562) (#7579)
   * Malformed URLs in API git/commits response (#7565) (#7567)
   * Fix empty commits now showing in repo overview (#7521) (#7563)
-  * Fix repository's pull request count error (#7518) (#7524) 
-  * Remove duplicated webhook trigger (#7511) (#7516) 
+  * Fix repository's pull request count error (#7518) (#7524)
+  * Remove duplicated webhook trigger (#7511) (#7516)
   * Handles all redirects for Web UI File CRUD (#7478) (#7507)
   * Fix regex for issues in commit messages (#7444) (#7466)
   * cmd/serv: actually exit after fatal errors (#7458) (#7460)
@@ -710,7 +995,7 @@ been added to each release, please refer to the [blog](https://blog.gitea.io).
 ## [1.7.5](https://github.com/go-gitea/gitea/releases/tag/v1.7.5) - 2019-03-27  
 * BUGFIXES
   * Fix unitTypeCode not being used in accessLevelUnit (#6419) (#6423)
-  * Fix bug where manifest.json was being requested without cookies and continuously creating new sessions (#6372) (#6383) 
+  * Fix bug where manifest.json was being requested without cookies and continuously creating new sessions (#6372) (#6383)
   * Fix ParsePatch function to work with quoted diff --git strings (#6323) (#6332)
 
 ## [1.7.4](https://github.com/go-gitea/gitea/releases/tag/v1.7.4) - 2019-03-12  
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 95799e92b3..04ffebe628 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -11,6 +11,7 @@
   - [Translation](#translation)
   - [Code review](#code-review)
   - [Styleguide](#styleguide)
+  - [Design guideline](#design-guideline)
   - [Developer Certificate of Origin (DCO)](#developer-certificate-of-origin-dco)
   - [Release Cycle](#release-cycle)
   - [Maintainers](#maintainers)
@@ -71,13 +72,15 @@ Here's how to run the test suite:
 
 - Install the correct version of the drone-cli package.  As of this
   writing, the correct drone-cli version is
-  [1.1.0](https://docs.drone.io/cli/install/).
+  [1.2.0](https://docs.drone.io/cli/install/).
 - Ensure you have enough free disk space.  You will need at least
   15-20 Gb of free disk space to hold all of the containers drone
   creates (a default AWS or GCE disk size won't work -- see
   [#6243](https://github.com/go-gitea/gitea/issues/6243)).
 - Change into the base directory of your copy of the gitea repository,
   and run `drone exec --event pull_request`.
+- At the moment `drone exec` doesn't support the Docker Toolbox on Windows 10
+  (see [drone-cli#135](https://github.com/drone/drone-cli/issues/135))
 
 The drone version, command line, and disk requirements do change over
 time (see [#4053](https://github.com/go-gitea/gitea/issues/4053) and
@@ -118,6 +121,8 @@ An exception are the tools to build the CSS and images.
 - To build Images: ImageMagick, inkscape and zopflipng binaries must be
   available in your `PATH` to run `make generate-images`.
 
+For more details on how to generate files, build and test Gitea, see the [hacking instructions](https://docs.gitea.io/en-us/hacking-on-gitea/)
+
 ## Code review
 
 Changes to Gitea must be reviewed before they are accepted—no matter who
@@ -157,6 +162,22 @@ import (
 )
 ```
 
+## Design guideline
+
+To maintain understandable code and avoid circular dependencies it is important to have a good structure of the code. The gitea code is divided into the following parts:
+
+- **integration:** Integrations tests 
+- **models:** Contains the data structures used by xorm to construct database tables. It also contains supporting functions to query and update the database. Dependecies to other code in Gitea should be avoided although some modules might be needed (for example for logging).
+- **models/fixtures:** Sample model data used in integration tests.
+- **models/migrations:** Handling of database migrations between versions. PRs that changes a database structure shall also have a migration step.
+- **modules:** Different modules to handle specific functionality in Gitea.
+- **public:** Frontend files (javascript, images, css, etc.)
+- **routers:** Handling of server requests. As it uses other Gitea packages to serve the request, other packages (models, modules or services) shall not depend on routers
+- **services:** Support functions for common routing operations. Uses models and modules to handle the request.
+- **templates:** Golang templates for generating the html output.
+- **vendor:** External code that Gitea depends on.
+
+
 ## Developer Certificate of Origin (DCO)
 
 We consider the act of contributing to the code by submitting a Pull
@@ -283,7 +304,7 @@ be reviewed by two maintainers and must pass the automatic tests.
 * Add a tag as `git tag -s -F release.notes v$vmaj.$vmin.$`, release.notes file could be a temporary file to only include the changelog this version which you added to `CHANGELOG.md`.
 * And then push the tag as `git push origin v$vmaj.$vmin.$`. Drone CI will automatically created a release and upload all the compiled binary. (But currently it didn't add the release notes automatically. Maybe we should fix that.)
 * If needed send PR for changelog on branch `master`.
-* Send PR to [blog repository](https://github.com/go-gitea/blog) announcing the release.
+* Send PR to [blog repository](https://gitea.com/gitea/blog) announcing the release.
 
 ## Copyright
 
diff --git a/MAINTAINERS b/MAINTAINERS
index bf657fabe2..9d3e4bc848 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -33,3 +33,4 @@ silverwind <me@silverwind.io> (@silverwind)
 Gary Kim <gary@garykim.dev> (@gary-kim)
 Guillermo Prandi <gitea.maint@mailfilter.com.ar> (@guillep2k)
 Mura Li <typeless@ctli.io> (@typeless)
+6543 <6543@obermui.de> (@6543)
diff --git a/Makefile b/Makefile
index b881bc9553..ebcfadb21d 100644
--- a/Makefile
+++ b/Makefile
@@ -168,6 +168,10 @@ fmt-check:
 test:
 	GO111MODULE=on $(GO) test -mod=vendor -tags='sqlite sqlite_unlock_notify' $(PACKAGES)
 
+.PHONY: test\#%
+test\#%:
+	GO111MODULE=on $(GO) test -mod=vendor -tags='sqlite sqlite_unlock_notify' -run $* $(PACKAGES)
+
 .PHONY: coverage
 coverage:
 	@hash gocovmerge > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
@@ -515,6 +519,6 @@ pr:
 golangci-lint:
 	@hash golangci-lint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
 		export BINARY="golangci-lint"; \
-		curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(GOPATH)/bin v1.18.0; \
+		curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(GOPATH)/bin v1.20.0; \
 	fi
-	golangci-lint run --deadline=3m
+	golangci-lint run
diff --git a/README.md b/README.md
index 92ed78a497..96f755107a 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 [简体中文](https://github.com/go-gitea/gitea/blob/master/README_ZH.md)
 
-# Gitea - Git with a cup of tea
+<h1> <img src="https://raw.githubusercontent.com/go-gitea/gitea/master/public/img/gitea-192.png" alt="logo" width="30" height="30"> Gitea - Git with a cup of tea</h1>
 
 [![Build Status](https://drone.gitea.io/api/badges/go-gitea/gitea/status.svg)](https://drone.gitea.io/go-gitea/gitea)
 [![Join the Discord chat at https://discord.gg/NsatcWJ](https://img.shields.io/discord/322538954119184384.svg)](https://discord.gg/NsatcWJ)
@@ -10,8 +10,9 @@
 [![GoDoc](https://godoc.org/code.gitea.io/gitea?status.svg)](https://godoc.org/code.gitea.io/gitea)
 [![GitHub release](https://img.shields.io/github/release/go-gitea/gitea.svg)](https://github.com/go-gitea/gitea/releases/latest)
 [![Help Contribute to Open Source](https://www.codetriage.com/go-gitea/gitea/badges/users.svg)](https://www.codetriage.com/go-gitea/gitea)
-[![Become a backer/sponsor of gitea](https://opencollective.com/gitea/tiers/backers/badge.svg?label=backer&color=brightgreen)](https://opencollective.com/gitea)
+[![Become a backer/sponsor of gitea](https://opencollective.com/gitea/tiers/backers/badge.svg?label=backers&color=brightgreen)](https://opencollective.com/gitea)
 [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
+[![Crowdin](https://badges.crowdin.net/gitea/localized.svg)](https://crowdin.com/project/gitea)
 
 ## Purpose
 
diff --git a/README_ZH.md b/README_ZH.md
index e143f23b41..0d9d6d27da 100644
--- a/README_ZH.md
+++ b/README_ZH.md
@@ -9,8 +9,9 @@
 [![Go Report Card](https://goreportcard.com/badge/code.gitea.io/gitea)](https://goreportcard.com/report/code.gitea.io/gitea)
 [![GoDoc](https://godoc.org/code.gitea.io/gitea?status.svg)](https://godoc.org/code.gitea.io/gitea)
 [![GitHub release](https://img.shields.io/github/release/go-gitea/gitea.svg)](https://github.com/go-gitea/gitea/releases/latest)
-[![Become a backer/sponsor of gitea](https://opencollective.com/gitea/tiers/backers/badge.svg?label=backer&color=brightgreen)](https://opencollective.com/gitea)
+[![Become a backer/sponsor of gitea](https://opencollective.com/gitea/tiers/backers/badge.svg?label=backers&color=brightgreen)](https://opencollective.com/gitea)
 [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
+[![Crowdin](https://badges.crowdin.net/gitea/localized.svg)](https://crowdin.com/project/gitea)
 
 ## 目标
 
diff --git a/cmd/admin.go b/cmd/admin.go
index 4c4d6f9b66..4346159feb 100644
--- a/cmd/admin.go
+++ b/cmd/admin.go
@@ -13,9 +13,9 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/auth/oauth2"
-	"code.gitea.io/gitea/modules/generate"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
+	pwd "code.gitea.io/gitea/modules/password"
 	"code.gitea.io/gitea/modules/setting"
 
 	"github.com/urfave/cli"
@@ -233,7 +233,9 @@ func runChangePassword(c *cli.Context) error {
 	if err := initDB(); err != nil {
 		return err
 	}
-
+	if !pwd.IsComplexEnough(c.String("password")) {
+		return errors.New("Password does not meet complexity requirements")
+	}
 	uname := c.String("username")
 	user, err := models.GetUserByName(uname)
 	if err != nil {
@@ -243,6 +245,7 @@ func runChangePassword(c *cli.Context) error {
 		return err
 	}
 	user.HashPassword(c.String("password"))
+
 	if err := models.UpdateUserCols(user, "passwd", "salt"); err != nil {
 		return err
 	}
@@ -275,26 +278,24 @@ func runCreateUser(c *cli.Context) error {
 		fmt.Fprintf(os.Stderr, "--name flag is deprecated. Use --username instead.\n")
 	}
 
-	var password string
+	if err := initDB(); err != nil {
+		return err
+	}
 
+	var password string
 	if c.IsSet("password") {
 		password = c.String("password")
 	} else if c.IsSet("random-password") {
 		var err error
-		password, err = generate.GetRandomString(c.Int("random-password-length"))
+		password, err = pwd.Generate(c.Int("random-password-length"))
 		if err != nil {
 			return err
 		}
-
 		fmt.Printf("generated random password is '%s'\n", password)
 	} else {
 		return errors.New("must set either password or random-password flag")
 	}
 
-	if err := initDB(); err != nil {
-		return err
-	}
-
 	// always default to true
 	var changePassword = true
 
diff --git a/cmd/hook.go b/cmd/hook.go
index f5b7962aab..f07568dd8b 100644
--- a/cmd/hook.go
+++ b/cmd/hook.go
@@ -66,6 +66,7 @@ func runHookPreReceive(c *cli.Context) error {
 	reponame := os.Getenv(models.EnvRepoName)
 	userID, _ := strconv.ParseInt(os.Getenv(models.EnvPusherID), 10, 64)
 	prID, _ := strconv.ParseInt(os.Getenv(models.ProtectedBranchPRID), 10, 64)
+	isDeployKey, _ := strconv.ParseBool(os.Getenv(models.EnvIsDeployKey))
 
 	buf := bytes.NewBuffer(nil)
 	scanner := bufio.NewScanner(os.Stdin)
@@ -98,6 +99,7 @@ func runHookPreReceive(c *cli.Context) error {
 				GitObjectDirectory:              os.Getenv(private.GitObjectDirectory),
 				GitQuarantinePath:               os.Getenv(private.GitQuarantinePath),
 				ProtectedBranchID:               prID,
+				IsDeployKey:                     isDeployKey,
 			})
 			switch statusCode {
 			case http.StatusInternalServerError:
diff --git a/cmd/serv.go b/cmd/serv.go
index 6533b0371c..1ac6b21e53 100644
--- a/cmd/serv.go
+++ b/cmd/serv.go
@@ -191,6 +191,8 @@ func runServ(c *cli.Context) error {
 	os.Setenv(models.EnvPusherID, strconv.FormatInt(results.UserID, 10))
 	os.Setenv(models.ProtectedBranchRepoID, strconv.FormatInt(results.RepoID, 10))
 	os.Setenv(models.ProtectedBranchPRID, fmt.Sprintf("%d", 0))
+	os.Setenv(models.EnvIsDeployKey, fmt.Sprintf("%t", results.IsDeployKey))
+	os.Setenv(models.EnvKeyID, fmt.Sprintf("%d", results.KeyID))
 
 	//LFS token authentication
 	if verb == lfsAuthenticateVerb {
diff --git a/cmd/web.go b/cmd/web.go
index 9a5ce5d2b6..3ca4041a7d 100644
--- a/cmd/web.go
+++ b/cmd/web.go
@@ -13,6 +13,7 @@ import (
 	"os"
 	"strings"
 
+	"code.gitea.io/gitea/modules/graceful"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/routers"
@@ -75,17 +76,13 @@ func runLetsEncrypt(listenAddr, domain, directory, email string, m http.Handler)
 	}
 	go func() {
 		log.Info("Running Let's Encrypt handler on %s", setting.HTTPAddr+":"+setting.PortToRedirect)
-		var err = http.ListenAndServe(setting.HTTPAddr+":"+setting.PortToRedirect, certManager.HTTPHandler(http.HandlerFunc(runLetsEncryptFallbackHandler))) // all traffic coming into HTTP will be redirect to HTTPS automatically (LE HTTP-01 validation happens here)
+		// all traffic coming into HTTP will be redirect to HTTPS automatically (LE HTTP-01 validation happens here)
+		var err = runHTTP(setting.HTTPAddr+":"+setting.PortToRedirect, certManager.HTTPHandler(http.HandlerFunc(runLetsEncryptFallbackHandler)))
 		if err != nil {
 			log.Fatal("Failed to start the Let's Encrypt handler on port %s: %v", setting.PortToRedirect, err)
 		}
 	}()
-	server := &http.Server{
-		Addr:      listenAddr,
-		Handler:   m,
-		TLSConfig: certManager.TLSConfig(),
-	}
-	return server.ListenAndServeTLS("", "")
+	return runHTTPSWithTLSConfig(listenAddr, certManager.TLSConfig(), context2.ClearHandler(m))
 }
 
 func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) {
@@ -101,12 +98,21 @@ func runLetsEncryptFallbackHandler(w http.ResponseWriter, r *http.Request) {
 }
 
 func runWeb(ctx *cli.Context) error {
+	if os.Getppid() > 1 && len(os.Getenv("LISTEN_FDS")) > 0 {
+		log.Info("Restarting Gitea on PID: %d from parent PID: %d", os.Getpid(), os.Getppid())
+	} else {
+		log.Info("Starting Gitea on PID: %d", os.Getpid())
+	}
+
+	// Set pid file setting
 	if ctx.IsSet("pid") {
 		setting.CustomPID = ctx.String("pid")
 	}
 
+	// Perform global initialization
 	routers.GlobalInit()
 
+	// Set up Macaron
 	m := routes.NewMacaron()
 	routes.RegisterRoutes(m)
 
@@ -164,6 +170,7 @@ func runWeb(ctx *cli.Context) error {
 	var err error
 	switch setting.Protocol {
 	case setting.HTTP:
+		NoHTTPRedirector()
 		err = runHTTP(listenAddr, context2.ClearHandler(m))
 	case setting.HTTPS:
 		if setting.EnableLetsEncrypt {
@@ -172,9 +179,15 @@ func runWeb(ctx *cli.Context) error {
 		}
 		if setting.RedirectOtherPort {
 			go runHTTPRedirector()
+		} else {
+			NoHTTPRedirector()
 		}
 		err = runHTTPS(listenAddr, setting.CertFile, setting.KeyFile, context2.ClearHandler(m))
 	case setting.FCGI:
+		NoHTTPRedirector()
+		// FCGI listeners are provided as stdin - this is orthogonal to the LISTEN_FDS approach
+		// in graceful and systemD
+		NoMainListener()
 		var listener net.Listener
 		listener, err = net.Listen("tcp", listenAddr)
 		if err != nil {
@@ -187,6 +200,10 @@ func runWeb(ctx *cli.Context) error {
 		}()
 		err = fcgi.Serve(listener, context2.ClearHandler(m))
 	case setting.UnixSocket:
+		// This could potentially be inherited using LISTEN_FDS but currently
+		// these cannot be inherited
+		NoHTTPRedirector()
+		NoMainListener()
 		if err := os.Remove(listenAddr); err != nil && !os.IsNotExist(err) {
 			log.Fatal("Failed to remove unix socket directory %s: %v", listenAddr, err)
 		}
@@ -207,8 +224,10 @@ func runWeb(ctx *cli.Context) error {
 	}
 
 	if err != nil {
-		log.Fatal("Failed to start server: %v", err)
+		log.Critical("Failed to start server: %v", err)
 	}
-
+	log.Info("HTTP Listener: %s Closed", listenAddr)
+	graceful.WaitForServers()
+	log.Close()
 	return nil
 }
diff --git a/cmd/web_graceful.go b/cmd/web_graceful.go
index 53f407ce9e..07b5a964c5 100644
--- a/cmd/web_graceful.go
+++ b/cmd/web_graceful.go
@@ -10,36 +10,28 @@ import (
 	"crypto/tls"
 	"net/http"
 
-	"code.gitea.io/gitea/modules/log"
-
-	"github.com/facebookgo/grace/gracehttp"
+	"code.gitea.io/gitea/modules/graceful"
 )
 
 func runHTTP(listenAddr string, m http.Handler) error {
-	return gracehttp.Serve(&http.Server{
-		Addr:    listenAddr,
-		Handler: m,
-	})
+	return graceful.HTTPListenAndServe("tcp", listenAddr, m)
 }
 
 func runHTTPS(listenAddr, certFile, keyFile string, m http.Handler) error {
-	config := &tls.Config{
-		MinVersion: tls.VersionTLS10,
-	}
-	if config.NextProtos == nil {
-		config.NextProtos = []string{"http/1.1"}
-	}
-
-	config.Certificates = make([]tls.Certificate, 1)
-	var err error
-	config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
-	if err != nil {
-		log.Fatal("Failed to load https cert file %s: %v", listenAddr, err)
-	}
-
-	return gracehttp.Serve(&http.Server{
-		Addr:      listenAddr,
-		Handler:   m,
-		TLSConfig: config,
-	})
+	return graceful.HTTPListenAndServeTLS("tcp", listenAddr, certFile, keyFile, m)
+}
+
+func runHTTPSWithTLSConfig(listenAddr string, tlsConfig *tls.Config, m http.Handler) error {
+	return graceful.HTTPListenAndServeTLSConfig("tcp", listenAddr, tlsConfig, m)
+}
+
+// NoHTTPRedirector tells our cleanup routine that we will not be using a fallback http redirector
+func NoHTTPRedirector() {
+	graceful.InformCleanup()
+}
+
+// NoMainListener tells our cleanup routine that we will not be using a possibly provided listener
+// for our main HTTP/HTTPS service
+func NoMainListener() {
+	graceful.InformCleanup()
 }
diff --git a/cmd/web_windows.go b/cmd/web_windows.go
index 0fc6cbea0d..cdd2cc513b 100644
--- a/cmd/web_windows.go
+++ b/cmd/web_windows.go
@@ -7,6 +7,7 @@
 package cmd
 
 import (
+	"crypto/tls"
 	"net/http"
 )
 
@@ -17,3 +18,20 @@ func runHTTP(listenAddr string, m http.Handler) error {
 func runHTTPS(listenAddr, certFile, keyFile string, m http.Handler) error {
 	return http.ListenAndServeTLS(listenAddr, certFile, keyFile, m)
 }
+
+func runHTTPSWithTLSConfig(listenAddr string, tlsConfig *tls.Config, m http.Handler) error {
+	server := &http.Server{
+		Addr:      listenAddr,
+		Handler:   m,
+		TLSConfig: tlsConfig,
+	}
+	return server.ListenAndServeTLS("", "")
+}
+
+// NoHTTPRedirector is a no-op on Windows
+func NoHTTPRedirector() {
+}
+
+// NoMainListener is a no-op on Windows
+func NoMainListener() {
+}
diff --git a/contrib/pr/checkout.go b/contrib/pr/checkout.go
index 490d6760c8..9c06357295 100644
--- a/contrib/pr/checkout.go
+++ b/contrib/pr/checkout.go
@@ -27,13 +27,13 @@ import (
 	"code.gitea.io/gitea/routers"
 	"code.gitea.io/gitea/routers/routes"
 
-	"github.com/go-xorm/xorm"
 	context2 "github.com/gorilla/context"
 	"github.com/unknwon/com"
 	"gopkg.in/src-d/go-git.v4"
 	"gopkg.in/src-d/go-git.v4/config"
 	"gopkg.in/src-d/go-git.v4/plumbing"
 	"gopkg.in/testfixtures.v2"
+	"xorm.io/xorm"
 )
 
 var codeFilePath = "contrib/pr/checkout.go"
diff --git a/contrib/systemd/gitea.service b/contrib/systemd/gitea.service
index d88df4a037..b5aa6ffcb5 100644
--- a/contrib/systemd/gitea.service
+++ b/contrib/systemd/gitea.service
@@ -2,11 +2,41 @@
 Description=Gitea (Git with a cup of tea)
 After=syslog.target
 After=network.target
+###
+# Don't forget to add the database service requirements
+###
+#
 #Requires=mysql.service
 #Requires=mariadb.service
 #Requires=postgresql.service
 #Requires=memcached.service
 #Requires=redis.service
+#
+###
+# If using socket activation for main http/s
+###
+#
+#After=gitea.main.socket
+#Requires=gitea.main.socket
+#
+###
+# (You can also provide gitea an http fallback and/or ssh socket too)
+#
+# An example of /etc/systemd/system/gitea.main.socket
+###
+##
+## [Unit]
+## Description=Gitea Web Socket
+## PartOf=gitea.service
+##
+## [Socket]
+## ListenStream=
+## NoDelay=true
+##
+## [Install]
+## WantedBy=sockets.target
+##
+###
 
 [Service]
 # Modify these two values and uncomment them if you have
@@ -20,14 +50,18 @@ Type=simple
 User=git
 Group=git
 WorkingDirectory=/var/lib/gitea/
+# If using unix socket: Tells Systemd to create /run/gitea folder to home gitea.sock
+# Manual cration would vanish after reboot.
+#RuntimeDirectory=gitea
 ExecStart=/usr/local/bin/gitea web -c /etc/gitea/app.ini
 Restart=always
 Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea
-# If you want to bind Gitea to a port below 1024 uncomment
-# the two values below
+# If you want to bind Gitea to a port below 1024, uncomment
+# the two values below, or use socket activation to pass Gitea its ports as above
 ###
 #CapabilityBoundingSet=CAP_NET_BIND_SERVICE
 #AmbientCapabilities=CAP_NET_BIND_SERVICE
+###
 
 [Install]
 WantedBy=multi-user.target
diff --git a/custom/conf/app.ini.sample b/custom/conf/app.ini.sample
index 9bfddc97e8..f0204bb06e 100644
--- a/custom/conf/app.ini.sample
+++ b/custom/conf/app.ini.sample
@@ -74,6 +74,37 @@ WORK_IN_PROGRESS_PREFIXES=WIP:,[WIP]
 ; List of reasons why a Pull Request or Issue can be locked
 LOCK_REASONS=Too heated,Off-topic,Resolved,Spam
 
+[repository.signing]
+; GPG key to use to sign commits, Defaults to the default - that is the value of git config --get user.signingkey
+; run in the context of the RUN_USER
+; Switch to none to stop signing completely
+SIGNING_KEY = default
+; If a SIGNING_KEY ID is provided and is not set to default, use the provided Name and Email address as the signer.
+; These should match a publicized name and email address for the key. (When SIGNING_KEY is default these are set to
+; the results of git config --get user.name and git config --get user.email respectively and can only be overrided
+; by setting the SIGNING_KEY ID to the correct ID.)
+SIGNING_NAME =
+SIGNING_EMAIL =
+; Determines when gitea should sign the initial commit when creating a repository
+; Either:
+; - never
+; - pubkey: only sign if the user has a pubkey
+; - twofa: only sign if the user has logged in with twofa
+; - always
+; options other than none and always can be combined as comma separated list
+INITIAL_COMMIT = always
+; Determines when to sign for CRUD actions
+; - as above
+; - parentsigned: requires that the parent commit is signed.
+CRUD_ACTIONS = pubkey, twofa, parentsigned
+; Determines when to sign Wiki commits
+; - as above
+WIKI = never
+; Determines when to sign on merges
+; - basesigned: require that the parent of commit on the base repo is signed.
+; - commitssigned: require that all the commits in the head branch are signed.
+MERGES = pubkey, twofa, basesigned, commitssigned
+
 [cors]
 ; More information about CORS can be found here: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#The_HTTP_response_headers
 ; enable cors headers (disabled by default)
@@ -141,8 +172,9 @@ KEYWORDS = go,git,self-hosted,gitea
 [markdown]
 ; Enable hard line break extension
 ENABLE_HARD_LINE_BREAK = false
-; List of custom URL-Schemes that are allowed as links when rendering Markdown
-; for example git,magnet
+; Comma separated list of custom URL-Schemes that are allowed as links when rendering Markdown
+; for example git,magnet,ftp (more at https://en.wikipedia.org/wiki/List_of_URI_schemes)
+; URLs starting with http and https are always displayed, whatever is put in this entry.
 CUSTOM_URL_SCHEMES =
 ; List of file extensions that should be rendered/edited as Markdown
 ; Separate the extensions with a comma. To render files without any extension as markdown, just put a comma
@@ -153,6 +185,8 @@ FILE_EXTENSIONS = .md,.markdown,.mdown,.mkd
 PROTOCOL = http
 DOMAIN = localhost
 ROOT_URL = %(PROTOCOL)s://%(DOMAIN)s:%(HTTP_PORT)s/
+; when STATIC_URL_PREFIX is empty it will follow APP_URL
+STATIC_URL_PREFIX = 
 ; The address to listen on. Either a IPv4/IPv6 address or the path to a unix socket.
 HTTP_ADDR = 0.0.0.0
 HTTP_PORT = 3000
@@ -243,6 +277,14 @@ LFS_CONTENT_PATH = data/lfs
 LFS_JWT_SECRET =
 ; LFS authentication validity period (in time.Duration), pushes taking longer than this may fail.
 LFS_HTTP_AUTH_EXPIRY = 20m
+; Allow graceful restarts using SIGHUP to fork
+ALLOW_GRACEFUL_RESTARTS = true
+; After a restart the parent will finish ongoing requests before
+; shutting down. Force shutdown if this process takes longer than this delay.
+; set to a negative value to disable
+GRACEFUL_HAMMER_TIME = 60s
+; Static resources, includes resources on custom/, public/ and all uploaded avatars web browser cache time, default is 6h
+STATIC_CACHE_TIME = 6h
 
 ; Define allowed algorithms and their minimum key length (use -1 to disable a type)
 [ssh.minimum_key_sizes]
@@ -277,10 +319,12 @@ LOG_SQL = true
 DB_RETRIES = 10
 ; Backoff time per DB retry (time.Duration)
 DB_RETRY_BACKOFF = 3s
-; Max idle database connections on connnection pool, default is 0
-MAX_IDLE_CONNS = 0
-; Database connection max life time, default is 3s
+; Max idle database connections on connnection pool, default is 2
+MAX_IDLE_CONNS = 2
+; Database connection max life time, default is 0 or 3s mysql (See #6804 & #7071 for reasoning)
 CONN_MAX_LIFETIME = 3s
+; Database maximum number of open connections, default is 0 meaning no maximum
+MAX_OPEN_CONNS = 0
 
 [indexer]
 ; Issue indexer type, currently support: bleve or db, default is bleve
@@ -296,6 +340,9 @@ ISSUE_INDEXER_QUEUE_DIR = indexers/issues.queue
 ISSUE_INDEXER_QUEUE_CONN_STR = "addrs=127.0.0.1:6379 db=0"
 ; Batch queue number, default is 20
 ISSUE_INDEXER_QUEUE_BATCH_NUMBER = 20
+; Timeout the indexer if it takes longer than this to start.
+; Set to zero to disable timeout.
+STARTUP_TIMEOUT=30s
 
 ; repo indexer by default disabled, since it uses a lot of disk space
 REPO_INDEXER_ENABLED = false
@@ -332,6 +379,10 @@ MIN_PASSWORD_LENGTH = 6
 IMPORT_LOCAL_PATHS = false
 ; Set to true to prevent all users (including admin) from creating custom git hooks
 DISABLE_GIT_HOOKS = false
+;Comma separated list of character classes required to pass minimum complexity.
+;If left empty or no valid values are specified, the default values ("lower,upper,digit,spec") will be used.
+;Use "off" to disable checking.
+PASSWORD_COMPLEXITY = lower,upper,digit,spec
 ; Password Hash algorithm, either "pbkdf2", "argon2", "scrypt" or "bcrypt"
 PASSWORD_HASH_ALGO = pbkdf2
 ; Set false to allow JavaScript to read CSRF cookie
@@ -389,6 +440,10 @@ ALLOW_ONLY_EXTERNAL_REGISTRATION = false
 REQUIRE_SIGNIN_VIEW = false
 ; Mail notification
 ENABLE_NOTIFY_MAIL = false
+; This setting enables gitea to be signed in with HTTP BASIC Authentication using the user's password
+; If you set this to false you will not be able to access the tokens endpoints on the API with your password
+; Please note that setting this to false will not disable OAuth Basic or Basic authentication using a token
+ENABLE_BASIC_AUTHENTICATION = true
 ; More detail: https://github.com/gogits/gogs/issues/165
 ENABLE_REVERSE_PROXY_AUTHENTICATION = false
 ENABLE_REVERSE_PROXY_AUTO_REGISTRATION = false
@@ -415,7 +470,7 @@ DEFAULT_ALLOW_CREATE_ORGANIZATION = true
 ; Public is for everyone
 DEFAULT_ORG_VISIBILITY = public
 ; Default value for DefaultOrgMemberVisible
-; True will make the membership of the users visible when added to the organisation  
+; True will make the membership of the users visible when added to the organisation
 DEFAULT_ORG_MEMBER_VISIBLE = false
 ; Default value for EnableDependencies
 ; Repositories will use dependencies by default depending on this setting
@@ -690,6 +745,11 @@ SCHEDULE = @every 24h
 ;   or only create new users if UPDATE_EXISTING is set to false
 UPDATE_EXISTING = true
 
+; Update migrated repositories' issues and comments' posterid, it will always attempt synchronization when the instance starts.
+[cron.update_migration_post_id]
+; Interval as a duration between each synchronization. (default every 24h)
+SCHEDULE = @every 24h
+
 [git]
 ; The path of git executable. If empty, Gitea searches through the PATH environment.
 PATH =
@@ -808,3 +868,12 @@ IS_INPUT_FILE = false
 ENABLED = false
 ; If you want to add authorization, specify a token here
 TOKEN =
+
+[task]
+; Task queue type, could be `channel` or `redis`.
+QUEUE_TYPE = channel
+; Task queue length, available only when `QUEUE_TYPE` is `channel`.
+QUEUE_LENGTH = 1000
+; Task queue connection string, available only when `QUEUE_TYPE` is `redis`.
+; If there is a password of redis, use `addrs=127.0.0.1:6379 password=123 db=0`.
+QUEUE_CONN_STR = "addrs=127.0.0.1:6379 db=0"
diff --git a/docker/root/etc/s6/openssh/setup b/docker/root/etc/s6/openssh/setup
index 10d195b74f..2a5eb9b09f 100755
--- a/docker/root/etc/s6/openssh/setup
+++ b/docker/root/etc/s6/openssh/setup
@@ -26,6 +26,7 @@ fi
 
 if [ -d /etc/ssh ]; then
     SSH_PORT=${SSH_PORT:-"22"} \
+    SSH_LISTEN_PORT=${SSH_LISTEN_PORT:-"${SSH_PORT}"} \
     envsubst < /etc/templates/sshd_config > /etc/ssh/sshd_config
 
     chmod 0644 /etc/ssh/sshd_config
diff --git a/docker/root/etc/templates/sshd_config b/docker/root/etc/templates/sshd_config
index bf0b936d7c..20e0b36012 100644
--- a/docker/root/etc/templates/sshd_config
+++ b/docker/root/etc/templates/sshd_config
@@ -1,4 +1,4 @@
-Port ${SSH_PORT}
+Port ${SSH_LISTEN_PORT}
 Protocol 2
 
 AddressFamily any
@@ -30,4 +30,4 @@ AllowUsers ${USER}
 Banner none
 Subsystem sftp /usr/lib/ssh/sftp-server
 
-AcceptEnv GIT_PROTOCOL
\ No newline at end of file
+AcceptEnv GIT_PROTOCOL
diff --git a/docs/content/doc/advanced/api-usage.en-us.md b/docs/content/doc/advanced/api-usage.en-us.md
index 8e0b43ec24..624d639545 100644
--- a/docs/content/doc/advanced/api-usage.en-us.md
+++ b/docs/content/doc/advanced/api-usage.en-us.md
@@ -68,6 +68,14 @@ curl -X POST "http://localhost:4000/api/v1/repos/test1/test1/issues" \
 As mentioned above, the token used is the same one you would use in
 the `token=` string in a GET request.
 
+## API Guide:
+
+API Reference guide is auto-generated by swagger and available on: 
+    `https://gitea.your.host/api/swagger`
+    or on 
+    [gitea demo instance](https://try.gitea.io/api/swagger)
+
+
 ## Listing your issued tokens via the API
 
 As mentioned in
diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md
index 198cff6f04..c2744b2958 100644
--- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md
+++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md
@@ -76,6 +76,25 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
 
 - `LOCK_REASONS`: **Too heated,Off-topic,Resolved,Spam**: A list of reasons why a Pull Request or Issue can be locked
 
+### Repository - Signing (`repository.signing`)
+
+- `SIGNING_KEY`: **default**: \[none, KEYID, default \]: Key to sign with.
+- `SIGNING_NAME` &amp; `SIGNING_EMAIL`: if a KEYID is provided as the `SIGNING_KEY`, use these as the Name and Email address of the signer. These should match publicized name and email address for the key.
+- `INITIAL_COMMIT`: **always**: \[never, pubkey, twofa, always\]: Sign initial commit.
+  - `never`: Never sign
+  - `pubkey`: Only sign if the user has a public key
+  - `twofa`: Only sign if the user is logged in with twofa
+  - `always`: Always sign
+  - Options other than `never` and `always` can be combined as a comma separated list.
+- `WIKI`: **never**: \[never, pubkey, twofa, always, parentsigned\]: Sign commits to wiki.
+- `CRUD_ACTIONS`: **pubkey, twofa, parentsigned**: \[never, pubkey, twofa, parentsigned, always\]: Sign CRUD actions.
+  - Options as above, with the addition of:
+  - `parentsigned`: Only sign if the parent commit is signed.
+- `MERGES`: **pubkey, twofa, basesigned, commitssigned**: \[never, pubkey, twofa, basesigned, commitssigned, always\]: Sign merges.
+  - `basesigned`: Only sign if the parent commit in the base repo is signed.
+  - `headsigned`: Only sign if the head commit in the head branch is signed.
+  - `commitssigned`: Only sign if all the commits in the head branch to the merge point are signed.
+
 ## CORS (`cors`)
 
 - `ENABLED`: **false**: enable cors headers (disabled by default)
@@ -108,6 +127,9 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
 ## Markdown (`markdown`)
 
 - `ENABLE_HARD_LINE_BREAK`: **false**: Enable Markdown's hard line break extension.
+- `CUSTOM_URL_SCHEMES`: Use a comma separated list (ftp,git,svn) to indicate additional
+  URL hyperlinks to be rendered in Markdown. URLs beginning in http and https are
+  always displayed
 
 ## Server (`server`)
 
@@ -116,6 +138,13 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
 - `ROOT_URL`: **%(PROTOCOL)s://%(DOMAIN)s:%(HTTP\_PORT)s/**:
    Overwrite the automatically generated public URL.
    This is useful if the internal and the external URL don't match (e.g. in Docker).
+- `STATIC_URL_PREFIX`: **\<empty\>**:
+   Overwrite this option to request static resources from a different URL.
+   This includes CSS files, images, JS files and web fonts.
+   Avatar images are dynamic resources and still served by gitea.
+   The option can be just a different path, as in `/static`, or another domain, as in `https://cdn.example.com`.
+   Requests are then made as `%(ROOT_URL)s/static/css/index.css` and `https://cdn.example.com/css/index.css` respective.
+   The static files are located in the `public/` directory of the gitea source repository.
 - `HTTP_ADDR`: **0.0.0.0**: HTTP listen address.
    - If `PROTOCOL` is set to `fcgi`, Gitea will listen for FastCGI requests on TCP socket
      defined by `HTTP_ADDR` and `HTTP_PORT` configuration settings.
@@ -140,6 +169,7 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
 - `CERT_FILE`: **custom/https/cert.pem**: Cert file path used for HTTPS.
 - `KEY_FILE`: **custom/https/key.pem**: Key file path used for HTTPS.
 - `STATIC_ROOT_PATH`: **./**: Upper level of template and static files path.
+- `STATIC_CACHE_TIME`: **6h**: Web browser cache time for static resources on `custom/`, `public/` and all uploaded avatars.
 - `ENABLE_GZIP`: **false**: Enables application-level GZIP support.
 - `LANDING_PAGE`: **home**: Landing page for unauthenticated users  \[home, explore\].
 - `LFS_START_SERVER`: **false**: Enables git-lfs support.
@@ -153,6 +183,8 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
 - `LETSENCRYPT_ACCEPTTOS`: **false**: This is an explicit check that you accept the terms of service for Let's Encrypt.
 - `LETSENCRYPT_DIRECTORY`: **https**: Directory that Letsencrypt will use to cache information such as certs and private keys.
 - `LETSENCRYPT_EMAIL`: **email@example.com**: Email used by Letsencrypt to notify about problems with issued certificates. (No default)
+- `ALLOW_GRACEFUL_RESTARTS`: **true**: Perform a graceful restart on SIGHUP
+- `GRACEFUL_HAMMER_TIME`: **60s**: After a restart the parent process will stop accepting new connections and will allow requests to finish before stopping. Shutdown will be forced if it takes longer than this time.
 
 ## Database (`database`)
 
@@ -167,8 +199,12 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
 - `LOG_SQL`: **true**: Log the executed SQL.
 - `DB_RETRIES`: **10**: How many ORM init / DB connect attempts allowed.
 - `DB_RETRY_BACKOFF`: **3s**: time.Duration to wait before trying another ORM init / DB connect attempt, if failure occured.
-- `MAX_IDLE_CONNS` **0**: Max idle database connections on connnection pool, default is 0
-- `CONN_MAX_LIFETIME` **3s**: Database connection max lifetime
+- `MAX_OPEN_CONNS` **0**: Database maximum open connections - default is 0, meaning there is no limit.
+- `MAX_IDLE_CONNS` **2**: Max idle database connections on connnection pool, default is 2 - this will be capped to `MAX_OPEN_CONNS`.
+- `CONN_MAX_LIFETIME` **0 or 3s**: Sets the maximum amount of time a DB connection may be reused - default is 0, meaning there is no limit (except on MySQL where it is 3s - see #6804 & #7071).
+  
+Please see #8540 & #8273 for further discussion of the appropriate values for `MAX_OPEN_CONNS`, `MAX_IDLE_CONNS` & `CONN_MAX_LIFETIME` and their
+relation to port exhaustion.
 
 ## Indexer (`indexer`)
 
@@ -185,6 +221,7 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
 - `REPO_INDEXER_EXCLUDE`: **empty**: A comma separated list of glob patterns (see https://github.com/gobwas/glob) to **exclude** from the index. Files that match this list will not be indexed, even if they match in `REPO_INDEXER_INCLUDE`.
 - `UPDATE_BUFFER_LEN`: **20**: Buffer length of index request.
 - `MAX_FILE_SIZE`: **1048576**: Maximum size in bytes of files to be indexed.
+- `STARTUP_TIMEOUT`: **30s**: If the indexer takes longer than this timeout to start - fail. (This timeout will be added to the hammer time above for child processes - as bleve will not start until the previous parent is shutdown.) Set to zero to never timeout.
 
 ## Admin (`admin`)
 - `DEFAULT_EMAIL_NOTIFICATIONS`: **enabled**: Default configuration for email notifications for users (user configurable). Options: enabled, onmention, disabled
@@ -208,6 +245,12 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
 - `INTERNAL_TOKEN_URI`: **<empty>**: Instead of defining internal token in the configuration, this configuration option can be used to give Gitea a path to a file that contains the internal token (example value: `file:/etc/gitea/internal_token`)
 - `PASSWORD_HASH_ALGO`: **pbkdf2**: The hash algorithm to use \[pbkdf2, argon2, scrypt, bcrypt\].
 - `CSRF_COOKIE_HTTP_ONLY`: **true**: Set false to allow JavaScript to read CSRF cookie.
+- `PASSWORD_COMPLEXITY`: **lower,upper,digit,spec**: Comma separated list of character classes required to pass minimum complexity. If left empty or no valid values are specified, the default values will be used. Possible values are: 
+    - lower - use one or more lower latin characters
+    - upper - use one or more upper latin characters
+    - digit - use one or more digits
+    - spec - use one or more special characters as ``!"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~``
+    - off - do not check password complexity
 
 ## OpenID (`openid`)
 
@@ -233,6 +276,10 @@ Values containing `#` or `;` must be quoted using `` ` `` or `"""`.
 - `REQUIRE_SIGNIN_VIEW`: **false**: Enable this to force users to log in to view any page.
 - `ENABLE_NOTIFY_MAIL`: **false**: Enable this to send e-mail to watchers of a repository when
    something happens, like creating issues. Requires `Mailer` to be enabled.
+- `ENABLE_BASIC_AUTHENTICATION`: **true**: Disable this to disallow authenticaton using HTTP
+   BASIC and the user's password. Please note if you disable this you will not be able to access the
+   tokens API endpoints using a password. Further, this only disables BASIC authentication using the
+   password - not tokens or OAuth Basic.
 - `ENABLE_REVERSE_PROXY_AUTHENTICATION`: **false**: Enable this to allow reverse proxy authentication.
 - `ENABLE_REVERSE_PROXY_AUTO_REGISTRATION`: **false**: Enable this to allow auto-registration
    for reverse authentication.
@@ -419,6 +466,10 @@ NB: You must `REDIRECT_MACARON_LOG` and have `DISABLE_ROUTER_LOG` set to `false`
 - `RUN_AT_START`: **true**: Run repository statistics check at start time.
 - `SCHEDULE`: **@every 24h**: Cron syntax for scheduling repository statistics check.
 
+### Cron - Update Migration Poster ID (`cron.update_migration_post_id`)
+
+- `SCHEDULE`: **@every 24h** : Interval as a duration between each synchronization, it will always attempt synchronization when the instance starts.
+
 ## Git (`git`)
 
 - `PATH`: **""**: The path of git executable. If empty, Gitea searches through the PATH environment.
@@ -514,9 +565,16 @@ Two special environment variables are passed to the render command:
 - `GITEA_PREFIX_RAW`, which contains the current URL prefix in the `raw` path tree. To be used as prefix for image paths.
 
 ## Time (`time`)
+
 - `FORMAT`: Time format to diplay on UI. i.e. RFC1123 or 2006-01-02 15:04:05
 - `DEFAULT_UI_LOCATION`: Default location of time on the UI, so that we can display correct user's time on UI. i.e. Shanghai/Asia
 
+## Task (`task`)
+
+- `QUEUE_TYPE`: **channel**: Task queue type, could be `channel` or `redis`.
+- `QUEUE_LENGTH`: **1000**: Task queue length, available only when `QUEUE_TYPE` is `channel`.
+- `QUEUE_CONN_STR`: **addrs=127.0.0.1:6379 db=0**: Task queue connection string, available only when `QUEUE_TYPE` is `redis`. If there redis needs a password, use `addrs=127.0.0.1:6379 password=123 db=0`.
+
 ## Other (`other`)
 
 - `SHOW_FOOTER_BRANDING`: **false**: Show Gitea branding in the footer.
diff --git a/docs/content/doc/advanced/config-cheat-sheet.zh-cn.md b/docs/content/doc/advanced/config-cheat-sheet.zh-cn.md
index 541d66f4e9..a0e33c6370 100644
--- a/docs/content/doc/advanced/config-cheat-sheet.zh-cn.md
+++ b/docs/content/doc/advanced/config-cheat-sheet.zh-cn.md
@@ -65,6 +65,7 @@ menu:
 - `CERT_FILE`: 启用HTTPS的证书文件。
 - `KEY_FILE`: 启用HTTPS的密钥文件。
 - `STATIC_ROOT_PATH`: 存放模板和静态文件的根目录,默认是 Gitea 的根目录。
+- `STATIC_CACHE_TIME`: **6h**: 静态资源文件,包括 `custom/`, `public/` 和所有上传的头像的浏览器缓存时间。
 - `ENABLE_GZIP`: 启用应用级别的 GZIP 压缩。
 - `LANDING_PAGE`: 未登录用户的默认页面,可选 `home` 或 `explore`。
 - `LFS_START_SERVER`: 是否启用 git-lfs 支持. 可以为 `true` 或 `false`, 默认是 `false`。
@@ -196,7 +197,11 @@ menu:
 ### Cron - Repository Statistics Check (`cron.check_repo_stats`)
 
 - `RUN_AT_START`: 是否启动时自动运行仓库统计。
-- `SCHEDULE`: 藏亏统计时的Cron 语法,比如:`@every 24h`.
+- `SCHEDULE`: 仓库统计时的Cron 语法,比如:`@every 24h`.
+
+### Cron - Update Migration Poster ID (`cron.update_migration_post_id`)
+
+- `SCHEDULE`: **@every 24h** : 每次同步的间隔时间。此任务总是在启动时自动进行。
 
 ## Git (`git`)
 
@@ -241,9 +246,16 @@ IS_INPUT_FILE = false
 - IS_INPUT_FILE: 输入方式是最后一个参数为文件路径还是从标准输入读取。
 
 ## Time (`time`)
+
 - `FORMAT`: 显示在界面上的时间格式。比如: RFC1123 或者 2006-01-02 15:04:05
 - `DEFAULT_UI_LOCATION`: 默认显示在界面上的时区,默认为本地时区。比如: Asia/Shanghai
 
+## Task (`task`)
+
+- `QUEUE_TYPE`: **channel**: 任务队列类型,可以为 `channel` 或 `redis`。
+- `QUEUE_LENGTH`: **1000**: 任务队列长度,当 `QUEUE_TYPE` 为 `channel` 时有效。
+- `QUEUE_CONN_STR`: **addrs=127.0.0.1:6379 db=0**: 任务队列连接字符串,当 `QUEUE_TYPE` 为 `redis` 时有效。如果redis有密码,则可以 `addrs=127.0.0.1:6379 password=123 db=0`。
+
 ## Other (`other`)
 
 - `SHOW_FOOTER_BRANDING`: 为真则在页面底部显示Gitea的字样。
diff --git a/docs/content/doc/advanced/external-renderers.en-us.md b/docs/content/doc/advanced/external-renderers.en-us.md
index e3a122448d..a14f344e63 100644
--- a/docs/content/doc/advanced/external-renderers.en-us.md
+++ b/docs/content/doc/advanced/external-renderers.en-us.md
@@ -51,7 +51,7 @@ add one `[markup.XXXXX]` section per external renderer on your custom `app.ini`:
 [markup.asciidoc]
 ENABLED = true
 FILE_EXTENSIONS = .adoc,.asciidoc
-RENDER_COMMAND = "asciidoctor --out-file=- -"
+RENDER_COMMAND = "asciidoctor -e -a leveloffset=-1 --out-file=- -"
 ; Input is not a standard input but a file
 IS_INPUT_FILE = false
 
diff --git a/docs/content/doc/advanced/signing.en-us.md b/docs/content/doc/advanced/signing.en-us.md
new file mode 100644
index 0000000000..b6c99e269e
--- /dev/null
+++ b/docs/content/doc/advanced/signing.en-us.md
@@ -0,0 +1,162 @@
+---
+date: "2019-08-17T10:20:00+01:00"
+title: "GPG Commit Signatures"
+slug: "signing"
+weight: 20
+toc: false
+draft: false
+menu:
+  sidebar:
+    parent: "advanced"
+    name: "GPG Commit Signatures"
+    weight: 20
+    identifier: "signing"
+---
+
+# GPG Commit Signatures
+
+Gitea will verify GPG commit signatures in the provided tree by
+checking if the commits are signed by a key within the gitea database,
+or if the commit matches the default key for git.
+
+Keys are not checked to determine if they have expired or revoked.
+Keys are also not checked with keyservers.
+
+A commit will be marked with a grey unlocked icon if no key can be
+found to verify it. If a commit is marked with a red unlocked icon,
+it is reported to be signed with a key with an id.
+
+Please note: The signer of a commit does not have to be an author or
+committer of a commit.
+
+This functionality requires git >= 1.7.9 but for full functionality
+this requires git >= 2.0.0.
+
+## Automatic Signing
+
+There are a number of places where Gitea will generate commits itself:
+
+* Repository Initialisation
+* Wiki Changes
+* CRUD actions using the editor or the API
+* Merges from Pull Requests
+
+Depending on configuration and server trust you may want Gitea to
+sign these commits.
+
+## General Configuration
+
+Gitea's configuration for signing can be found with the
+`[repository.signing]` section of `app.ini`:
+
+```ini
+...
+[repository.signing]
+SIGNING_KEY = default
+SIGNING_NAME =
+SIGNING_EMAIL =
+INITIAL_COMMIT = always
+CRUD_ACTIONS = pubkey, twofa, parentsigned
+WIKI = never
+MERGES = pubkey, twofa, basesigned, commitssigned
+
+...
+```
+
+### `SIGNING_KEY`
+
+The first option to discuss is the `SIGNING_KEY`. There are three main
+options:
+
+* `none` - this prevents Gitea from signing any commits
+* `default` - Gitea will default to the key configured within
+`git config`
+* `KEYID` - Gitea will sign commits with the gpg key with the ID
+`KEYID`. In this case you should provide a `SIGNING_NAME` and
+`SIGNING_EMAIL` to be displayed for this key.
+
+The `default` option will interrogate `git config` for
+`commit.gpgsign` option - if this is set, then it will use the results
+of the `user.signingkey`, `user.name` and `user.email` as appropriate.
+
+Please note: by adjusting git's `config` file within Gitea's
+repositories, `SIGNING_KEY=default` could be used to provide different
+signing keys on a per-repository basis. However, this is cleary not an
+ideal UI and therefore subject to change.
+
+### `INITIAL_COMMIT`
+
+This option determines whether Gitea should sign the initial commit
+when creating a repository. The possible values are:
+
+* `never`: Never sign
+* `pubkey`: Only sign if the user has a public key
+* `twofa`: Only sign if the user logs in with two factor authentication
+* `always`: Always sign
+
+Options other than `never` and `always` can be combined as a comma
+separated list.
+
+### `WIKI`
+
+This options determines if Gitea should sign commits to the Wiki.
+The possible values are:
+
+* `never`: Never sign
+* `pubkey`: Only sign if the user has a public key
+* `twofa`: Only sign if the user logs in with two factor authentication
+* `parentsigned`: Only sign if the parent commit is signed.
+* `always`: Always sign
+
+Options other than `never` and `always` can be combined as a comma
+separated list.
+
+### `CRUD_ACTIONS`
+
+This option determines if Gitea should sign commits from the web
+editor or API CRUD actions. The possible values are:
+
+* `never`: Never sign
+* `pubkey`: Only sign if the user has a public key
+* `twofa`: Only sign if the user logs in with two factor authentication
+* `parentsigned`: Only sign if the parent commit is signed.
+* `always`: Always sign
+
+Options other than `never` and `always` can be combined as a comma
+separated list.
+
+### `MERGES`
+
+This option determines if Gitea should sign merge commits from PRs.
+The possible options are:
+
+* `never`: Never sign
+* `pubkey`: Only sign if the user has a public key
+* `twofa`: Only sign if the user logs in with two factor authentication
+* `basesigned`: Only sign if the parent commit in the base repo is signed.
+* `headsigned`: Only sign if the head commit in the head branch is signed.
+* `commitssigned`: Only sign if all the commits in the head branch to the merge point are signed.
+* `always`: Always sign
+
+Options other than `never` and `always` can be combined as a comma
+separated list.
+
+## Installing and generating a GPG key for Gitea
+
+It is up to a server administrator to determine how best to install
+a signing key. Gitea generates all its commits using the server `git`
+command at present - and therefore the server `gpg` will be used for
+signing (if configured.) Administrators should review best-practices
+for gpg - in particular it is probably advisable to only install a
+signing secret subkey without the master signing and certifying secret
+key.
+
+## Obtaining the Public Key of the Signing Key
+
+The public key used to sign Gitea's commits can be obtained from the API at:
+
+```/api/v1/signing-key.gpg```
+
+In cases where there is a repository specific key this can be obtained from:
+
+```/api/v1/repos/:username/:reponame/signing-key.gpg```
diff --git a/docs/content/doc/features/comparison.en-us.md b/docs/content/doc/features/comparison.en-us.md
index 6bbbfba3b4..1d36d855c2 100644
--- a/docs/content/doc/features/comparison.en-us.md
+++ b/docs/content/doc/features/comparison.en-us.md
@@ -105,6 +105,7 @@ _Symbols used in table:_
 | Revert specific commits or a merge request | [✘](https://github.com/go-gitea/gitea/issues/5158) | ✘ | ✓ | ✓ | ✓ | ✓ | ✘ |
 | Pull/Merge requests templates | ✓ | ✓ | ✓ | ✓ | ✓ | ✘ | ✘ |
 | Cherry-picking changes | [✘](https://github.com/go-gitea/gitea/issues/5158) | ✘ | ✘ | ✓ | ✓ | ✘ | ✘ |
+| Download Patch | ✓ | ✘ | ✓ | ✓ | ✓ | [/](https://jira.atlassian.com/plugins/servlet/mobile#issue/BCLOUD-8323) | ✘ |
 
 
 #### 3rd-party integrations
diff --git a/docs/content/doc/features/webhooks.en-us.md b/docs/content/doc/features/webhooks.en-us.md
index 628afb7356..1a0a180e7a 100644
--- a/docs/content/doc/features/webhooks.en-us.md
+++ b/docs/content/doc/features/webhooks.en-us.md
@@ -17,7 +17,15 @@ menu:
 
 Gitea supports web hooks for repository events. This can be found in the settings
 page `/:username/:reponame/settings/hooks`. All event pushes are POST requests.
-The two methods currently supported are Gitea and Slack.
+The methods currently supported are:
+
+- Gitea
+- Gogs
+- Slack
+- Discord
+- Dingtalk
+- Telegram
+- Microsoft Teams
 
 ### Event information
 
@@ -104,3 +112,75 @@ X-Gitea-Event: push
   }
 }
 ```
+
+### Example
+
+This is an example of how to use webhooks to run a php script upon push requests to the repository.
+In your repository Settings, under Webhooks, Setup a Gitea webhook as follows:
+
+- Target URL: http://mydomain.com/webhook.php
+- HTTP Method: POST
+- POST Content Type: application/json
+- Secret: 123
+- Trigger On: Push Events
+- Active: Checked
+
+Now on your server create the php file webhook.php
+
+```
+<?php
+
+$secret_key = '123';
+
+// check for POST request
+if ($_SERVER['REQUEST_METHOD'] != 'POST') {
+    error_log('FAILED - not POST - '. $_SERVER['REQUEST_METHOD']);
+    exit();
+}
+
+// get content type
+$content_type = isset($_SERVER['CONTENT_TYPE']) ? strtolower(trim($_SERVER['CONTENT_TYPE'])) : '';
+
+if ($content_type != 'application/json') {
+    error_log('FAILED - not application/json - '. $content_type);
+    exit();
+}
+
+// get payload
+$payload = trim(file_get_contents("php://input"));
+
+if (empty($payload)) {
+    error_log('FAILED - no payload');
+    exit();
+}
+
+// get header signature
+$header_signature = isset($_SERVER['HTTP_X_GITEA_SIGNATURE']) ? $_SERVER['HTTP_X_GITEA_SIGNATURE'] : '';
+
+if (empty($header_signature)) {
+    error_log('FAILED - header signature missing');
+    exit();
+}
+
+// calculate payload signature
+$payload_signature = hash_hmac('sha256', $payload, $secret_key, false);
+
+// check payload signature against header signature
+if ($header_signature != $payload_signature) {
+    error_log('FAILED - payload signature');
+    exit();
+}
+
+// convert json to array
+$decoded = json_decode($payload, true);
+
+// check for json decode errors
+if (json_last_error() !== JSON_ERROR_NONE) {
+    error_log('FAILED - json decode - '. json_last_error());
+    exit();
+}
+
+// success, do something
+```
+
+There is a Test Delivery button in the webhook settings that allows to test the configuration as well as a list of the most Recent Deliveries.
diff --git a/docs/content/doc/help/seek-help.en-us.md b/docs/content/doc/help/seek-help.en-us.md
index e9d0211029..058a5bb592 100644
--- a/docs/content/doc/help/seek-help.en-us.md
+++ b/docs/content/doc/help/seek-help.en-us.md
@@ -33,4 +33,4 @@ If you found a bug, please create an [issue on GitHub](https://github.com/go-git
 
 ## Chinese Support
 
-Support for the Chinese language is provided at [gocn.io](https://gocn.io/topic/Gitea).
+Support for the Chinese language is provided at [gocn.vip](https://gocn.vip/topic/gitea).
diff --git a/docs/content/doc/help/seek-help.zh-cn.md b/docs/content/doc/help/seek-help.zh-cn.md
index ac62b9b8cf..7e4e2f1beb 100644
--- a/docs/content/doc/help/seek-help.zh-cn.md
+++ b/docs/content/doc/help/seek-help.zh-cn.md
@@ -18,6 +18,6 @@ menu:
 如果您在使用或者开发过程中遇到问题,请到以下渠道咨询:
 
 - 到[Github issue](https://github.com/go-gitea/gitea/issues)提问(因为项目维护人员来自世界各地,为保证沟通顺畅,请使用英文提问)
-- 中文问题到[gocn.io](https://gocn.io/topic/Gitea)提问
+- 中文问题到[gocn.vip](https://gocn.vip/topic/gitea)提问
 - 访问 [Discord server - 英文](https://discord.gg/NsatcWJ)
 - 加入 QQ群 328432459 获得进一步的支持
diff --git a/docs/content/doc/installation/from-binary.en-us.md b/docs/content/doc/installation/from-binary.en-us.md
index c93973f222..10f0ff15b6 100644
--- a/docs/content/doc/installation/from-binary.en-us.md
+++ b/docs/content/doc/installation/from-binary.en-us.md
@@ -44,7 +44,7 @@ location. When launched manually, Gitea can be killed using `Ctrl+C`.
 
 ## Recommended server configuration
 
-**NOTE:** Many of the following directories can be configured using [Environment Variables]({{< relref "doc/advanced/specific-variables.en-us.md" >}}) as well!  
+**NOTE:** Many of the following directories can be configured using [Environment Variables]({{< relref "doc/advanced/specific-variables.en-us.md" >}}) as well!
 Of note, configuring `GITEA_WORK_DIR` will tell Gitea where to base its working directory, as well as ease installation.
 
 ### Prepare environment
@@ -80,7 +80,7 @@ chmod 770 /etc/gitea
 **NOTE:** `/etc/gitea` is temporary set with write rights for user `git` so that Web installer could write configuration file. After installation is done, it is recommended to set rights to read-only using:
 ```
 chmod 750 /etc/gitea
-chmod 644 /etc/gitea/app.ini
+chmod 640 /etc/gitea/app.ini
 ```
 If you don't want the web installer to be able to write the config file at all, it is also possible to make the config file read-only for the gitea user (owner/group `root:root`, mode `0660`), and set `INSTALL_LOCK = true`. In that case all database configuration details must be set beforehand in the config file, as well as the `SECRET_KEY` and `INTERNAL_TOKEN` values. See the [command line documentation]({{< relref "doc/usage/command-line.en-us.md" >}}) for information on using `gitea generate secret INTERNAL_TOKEN`.
 
@@ -113,16 +113,16 @@ GITEA_WORK_DIR=/var/lib/gitea/ /usr/local/bin/gitea web -c /etc/gitea/app.ini
 
 ## Updating to a new version
 
-You can update to a new version of Gitea by stopping Gitea, replacing the binary at `/usr/local/bin/gitea` and restarting the instance. 
-The binary file name should not be changed during the update to avoid problems 
-in existing repositories. 
+You can update to a new version of Gitea by stopping Gitea, replacing the binary at `/usr/local/bin/gitea` and restarting the instance.
+The binary file name should not be changed during the update to avoid problems
+in existing repositories.
 
 It is recommended you do a [backup]({{< relref "doc/usage/backup-and-restore.en-us.md" >}}) before updating your installation.
 
-If you have carried out the installation steps as described above, the binary should 
-have the generic name `gitea`. Do not change this, i.e. to include the version number. 
+If you have carried out the installation steps as described above, the binary should
+have the generic name `gitea`. Do not change this, i.e. to include the version number.
 
-See below for troubleshooting instructions to repair broken repositories after 
+See below for troubleshooting instructions to repair broken repositories after
 an update of your Gitea version.
 
 ## Troubleshooting
@@ -145,7 +145,7 @@ is already running.
 
 ### Running Gitea on Raspbian
 
-As of v1.8, there is a problem with the arm7 version of Gitea and it doesn't run on Raspberry Pi and similar devices. 
+As of v1.8, there is a problem with the arm7 version of Gitea and it doesn't run on Raspberry Pi and similar devices.
 
 It is therefore recommended to switch to the arm6 version which has been tested and shown to work on Raspberry Pi and similar devices.
 
@@ -154,18 +154,18 @@ please remove after fixing the arm7 bug
 --->
 ### Git error after updating to a new version of Gitea
 
-If the binary file name has been changed during the update to a new version of Gitea, 
-git hooks in existing repositories will not work any more. In that case, a git 
+If the binary file name has been changed during the update to a new version of Gitea,
+git hooks in existing repositories will not work any more. In that case, a git
 error will be displayed when pushing to the repository.
 
 ```
 remote: ./hooks/pre-receive.d/gitea: line 2: [...]: No such file or directory
 ```
 
-The `[...]` part of the error message will contain the path to your previous Gitea 
+The `[...]` part of the error message will contain the path to your previous Gitea
 binary.
 
-To solve this, go to the admin options and run the task `Resynchronize pre-receive, 
+To solve this, go to the admin options and run the task `Resynchronize pre-receive,
 update and post-receive hooks of all repositories` to update all hooks to contain
 the new binary path. Please note that this overwrite all git hooks including ones
 with customizations made.
diff --git a/docs/content/doc/installation/from-source.en-us.md b/docs/content/doc/installation/from-source.en-us.md
index 9455f93d80..25ed5b790a 100644
--- a/docs/content/doc/installation/from-source.en-us.md
+++ b/docs/content/doc/installation/from-source.en-us.md
@@ -118,12 +118,12 @@ launched manually from command line, it can be killed by pressing `Ctrl + C`.
 ./gitea web
 ```
 
-## Changing the default CustomPath, CustomConf and AppWorkDir
+## Changing the default CustomPath, CustomConf and AppWorkPath
 
 Gitea will search for a number of things from the `CustomPath`. By default this is
 the `custom/` directory in the current working directory when running Gitea. It will also
 look for its configuration file `CustomConf` in `$CustomPath/conf/app.ini`, and will use the
-current working directory as the relative base path `AppWorkDir` for a number configurable
+current working directory as the relative base path `AppWorkPath` for a number configurable
 values.
 
 These values, although useful when developing, may conflict with downstream users preferences.
@@ -134,7 +134,7 @@ using the `LDFLAGS` environment variable for `make`. The appropriate settings ar
 
 * To set the `CustomPath` use `LDFLAGS="-X \"code.gitea.io/gitea/modules/setting.CustomPath=custom-path\""`
 * For `CustomConf` you should use `-X \"code.gitea.io/gitea/modules/setting.CustomConf=conf.ini\"`
-* For `AppWorkDir` you should use `-X \"code.gitea.io/gitea/modules/setting.AppWorkDir=working-directory\"`
+* For `AppWorkPath` you should use `-X \"code.gitea.io/gitea/modules/setting.AppWorkPath=working-path\"`
 
 Add as many of the strings with their preceding `-X` to the `LDFLAGS` variable and run `make build`
 with the appropriate `TAGS` as above.
diff --git a/docs/content/doc/usage/email-setup.md b/docs/content/doc/usage/email-setup.md
new file mode 100644
index 0000000000..6fc9de81dd
--- /dev/null
+++ b/docs/content/doc/usage/email-setup.md
@@ -0,0 +1,33 @@
+---
+date: "2019-10-15T10:10:00+05:00"
+title: "Usage: Email setup"
+slug: "email-setup"
+weight: 12
+toc: true
+draft: false
+menu:
+  sidebar:
+    parent: "usage"
+    name: "Email setup"
+    weight: 12
+    identifier: "email-setup"
+---
+
+# Email setup
+
+- To use Gitea's built-in Email support, update the `app.ini` config file [mailer] section:
+
+```ini
+[mailer]
+ENABLED = true
+HOST    = mail.mydomain.com:587
+FROM    = gitea@mydomain.com
+USER    = gitea@mydomain.com
+PASSWD  = `password`
+```
+
+- Restart Gitea for the configuration changes to take effect.
+
+- To send a test email to validate the settings, go to Gitea > Site Administration > Configuration > SMTP Mailer Configuration.
+
+For the full list of options check the [Config Cheat Sheet]({{< relref "doc/advanced/config-cheat-sheet.en-us.md" >}})
\ No newline at end of file
diff --git a/docs/content/doc/usage/fail2ban-setup.md b/docs/content/doc/usage/fail2ban-setup.md
index 28c4874da2..922c71f93d 100644
--- a/docs/content/doc/usage/fail2ban-setup.md
+++ b/docs/content/doc/usage/fail2ban-setup.md
@@ -26,7 +26,7 @@ on a bad authentication:
 2018/04/26 18:15:54 [I] Failed authentication attempt for user from xxx.xxx.xxx.xxx
 ```
 
-So we set our filter in `/etc/fail2ban/filter.d/gitea.conf`:
+Add our filter in `/etc/fail2ban/filter.d/gitea.conf`:
 
 ```ini
 # gitea.conf
@@ -35,12 +35,11 @@ failregex =  .*Failed authentication attempt for .* from <HOST>
 ignoreregex =
 ```
 
-And configure it in `/etc/fail2ban/jail.d/jail.local`:
+Add our jail in `/etc/fail2ban/jail.d/gitea.conf`:
 
 ```ini
 [gitea]
 enabled = true
-port = http,https
 filter = gitea
 logpath = /home/git/gitea/log/gitea.log
 maxretry = 10
@@ -49,6 +48,23 @@ bantime = 900
 action = iptables-allports
 ```
 
+If you're using Docker, you'll also need to add an additional jail to handle the **FORWARD** 
+chain in **iptables**. Configure it in `/etc/fail2ban/jail.d/gitea-docker.conf`:
+
+```ini
+[gitea-docker]
+enabled = true
+filter = gitea
+logpath = /home/git/gitea/log/gitea.log
+maxretry = 10
+findtime = 3600
+bantime = 900
+action = iptables-allports[chain="FORWARD"]
+```
+
+Then simply run `service fail2ban restart` to apply your changes. You can check to see if 
+fail2ban has accepted your configuration using `service fail2ban status`.
+
 Make sure and read up on fail2ban and configure it to your needs, this bans someone 
 for **15 minutes** (from all ports) when they fail authentication 10 times in an hour.
 
diff --git a/docs/content/doc/usage/git-lfs-support.md b/docs/content/doc/usage/git-lfs-support.md
new file mode 100644
index 0000000000..2d5fab3cb3
--- /dev/null
+++ b/docs/content/doc/usage/git-lfs-support.md
@@ -0,0 +1,26 @@
+---
+date: "2019-10-06T08:00:00+05:00"
+title: "Usage: Git LFS setup"
+slug: "git-lfs-setup"
+weight: 12
+toc: true
+draft: false
+menu:
+  sidebar:
+    parent: "usage"
+    name: "Git LFS setup"
+    weight: 12
+    identifier: "git-lfs-setup"
+---
+
+# Git Large File Storage setup
+
+To use Gitea's built-in LFS support, you must update the `app.ini` file:
+
+```ini
+[server]
+; Enables git-lfs support. true or false, default is false.
+LFS_START_SERVER = true
+; Where your lfs files reside, default is data/lfs.
+LFS_CONTENT_PATH = /home/gitea/data/lfs
+```
\ No newline at end of file
diff --git a/docs/content/doc/usage/https-support.md b/docs/content/doc/usage/https-support.md
index 22cbc684aa..e2b5332c05 100644
--- a/docs/content/doc/usage/https-support.md
+++ b/docs/content/doc/usage/https-support.md
@@ -20,6 +20,8 @@ menu:
 Before you enable HTTPS, make sure that you have valid SSL/TLS certificates.
 You could use self-generated certificates for evaluation and testing. Please run `gitea cert --host [HOST]` to generate a self signed certificate.
 
+If you are using Apache or nginx on the server, it's recommended to check the [reverse proxy guide]({{< relref "doc/usage/reverse-proxies.en-us.md" >}}).
+
 To use Gitea's built-in HTTPS support, you must change your `app.ini` file:
 
 ```ini
diff --git a/docs/content/doc/usage/reverse-proxies.en-us.md b/docs/content/doc/usage/reverse-proxies.en-us.md
index 47a5b95572..55c8bb9710 100644
--- a/docs/content/doc/usage/reverse-proxies.en-us.md
+++ b/docs/content/doc/usage/reverse-proxies.en-us.md
@@ -44,6 +44,74 @@ server {
 
 Then set `[server] ROOT_URL = http://git.example.com/git/` in your configuration.
 
+##  Using Nginx as a reverse proxy and serve static resources directly
+We can tune the performance in splitting requests into categories static and dynamic. 
+
+CSS files, JavaScript files, images and web fonts are static content.
+The front page, a repository view or issue list is dynamic content.
+
+Nginx can serve static resources directly and proxy only the dynamic requests to gitea.
+Nginx is optimized for serving static content, while the proxying of large responses might be the opposite of that
+ (see https://serverfault.com/q/587386).
+
+Download a snap shot of the gitea source repository to `/path/to/gitea/`.
+
+We are only interested in the `public/` directory and you can delete the rest.
+
+Depending on the scale of your user base, you might want to split the traffic to two distinct servers,
+ or use a cdn for the static files.
+
+### using a single node and a single domain
+
+Set `[server] STATIC_URL_PREFIX = /_/static` in your configuration.
+
+```
+server {
+    listen 80;
+    server_name git.example.com;
+
+    location /_/static {
+        alias /path/to/gitea/public;
+    }
+
+    location / {
+        proxy_pass http://localhost:3000;
+    }
+}
+```
+
+### using two nodes and two domains
+
+Set `[server] STATIC_URL_PREFIX = http://cdn.example.com/gitea` in your configuration.
+
+```
+# application server running gitea
+server {
+    listen 80;
+    server_name git.example.com;
+
+    location / {
+        proxy_pass http://localhost:3000;
+    }
+}
+```
+
+```
+# static content delivery server
+server {
+    listen 80;
+    server_name cdn.example.com;
+
+    location /gitea {
+        alias /path/to/gitea/public;
+    }
+
+    location / {
+        return 404;
+    }
+}
+```
+
 ## Using Apache HTTPD as a reverse proxy
 
 If you want Apache HTTPD to serve your Gitea instance, you can add the following to your Apache HTTPD configuration (usually located at `/etc/apache2/httpd.conf` in Ubuntu):
diff --git a/go.mod b/go.mod
index e1a2b7b404..e1bbd9ac89 100644
--- a/go.mod
+++ b/go.mod
@@ -29,16 +29,12 @@ require (
 	github.com/cznic/strutil v0.0.0-20181122101858-275e90344537 // indirect
 	github.com/denisenkom/go-mssqldb v0.0.0-20190924004331-208c0a498538
 	github.com/dgrijalva/jwt-go v3.2.0+incompatible
+	github.com/editorconfig/editorconfig-core-go/v2 v2.1.1
 	github.com/emirpasic/gods v1.12.0
 	github.com/etcd-io/bbolt v1.3.2 // indirect
 	github.com/ethantkoenig/rupture v0.0.0-20180203182544-0a76f03a811a
-	github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a // indirect
 	github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 // indirect
-	github.com/facebookgo/freeport v0.0.0-20150612182905-d4adf43b75b9 // indirect
-	github.com/facebookgo/grace v0.0.0-20160926231715-5729e484473f
-	github.com/facebookgo/httpdown v0.0.0-20160323221027-a3b1354551a2 // indirect
 	github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 // indirect
-	github.com/facebookgo/stats v0.0.0-20151006221625-1b76add642e4 // indirect
 	github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 // indirect
 	github.com/gliderlabs/ssh v0.2.2
 	github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd // indirect
@@ -48,7 +44,6 @@ require (
 	github.com/go-redis/redis v6.15.2+incompatible
 	github.com/go-sql-driver/mysql v1.4.1
 	github.com/go-swagger/go-swagger v0.20.1
-	github.com/go-xorm/xorm v0.7.9
 	github.com/gobwas/glob v0.2.3
 	github.com/gogits/chardet v0.0.0-20150115103509-2404f7772561
 	github.com/gogs/cron v0.0.0-20171120032916-9f6c956d3e14
@@ -64,7 +59,7 @@ require (
 	github.com/klauspost/compress v0.0.0-20161025140425-8df558b6cb6f
 	github.com/klauspost/cpuid v0.0.0-20160302075316-09cded8978dc // indirect
 	github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6 // indirect
-	github.com/lafriks/xormstore v1.3.1
+	github.com/lafriks/xormstore v1.3.2
 	github.com/lib/pq v1.2.0
 	github.com/lunny/dingtalk_webhook v0.0.0-20171025031554-e3534c89ef96
 	github.com/lunny/levelqueue v0.0.0-20190217115915-02b525a4418e
@@ -112,16 +107,15 @@ require (
 	golang.org/x/tools v0.0.0-20190910221609-7f5965fd7709 // indirect
 	gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
 	gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175 // indirect
-	gopkg.in/editorconfig/editorconfig-core-go.v1 v1.3.0
 	gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
-	gopkg.in/ini.v1 v1.46.0
+	gopkg.in/ini.v1 v1.48.0
 	gopkg.in/ldap.v3 v3.0.2
 	gopkg.in/src-d/go-billy.v4 v4.3.2
 	gopkg.in/src-d/go-git.v4 v4.13.1
-	gopkg.in/stretchr/testify.v1 v1.2.2 // indirect
 	gopkg.in/testfixtures.v2 v2.5.0
-	mvdan.cc/xurls/v2 v2.0.0
-	strk.kbt.io/projects/go/libravatar v0.0.0-20160628055650-5eed7bff870a
+	mvdan.cc/xurls/v2 v2.1.0
+	strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
 	xorm.io/builder v0.3.6
 	xorm.io/core v0.7.2
+	xorm.io/xorm v0.8.0
 )
diff --git a/go.sum b/go.sum
index c068caa2f7..2eeaa79810 100644
--- a/go.sum
+++ b/go.sum
@@ -89,8 +89,6 @@ github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghf
 github.com/chaseadamsio/goorgeous v0.0.0-20170901132237-098da33fde5f h1:REH9VH5ubNR0skLaOxK7TRJeRbE2dDfvaouQo8FsRcA=
 github.com/chaseadamsio/goorgeous v0.0.0-20170901132237-098da33fde5f/go.mod h1:6QaC0vFoKWYDth94dHFNgRT2YkT5FHdQp/Yx15aAAi0=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
-github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
 github.com/corbym/gocrest v1.0.3 h1:gwEdq6RkTmq+09CTuM29DfKOCtZ7G7bcyxs3IZ6EVdU=
 github.com/corbym/gocrest v1.0.3/go.mod h1:maVFL5lbdS2PgfOQgGRWDYTeunSWQeiEgoNdTABShCs=
 github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
@@ -134,6 +132,8 @@ github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD
 github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
 github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
 github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
+github.com/editorconfig/editorconfig-core-go/v2 v2.1.1 h1:mhPg/0hGebcpiiQLqJD2PWWyoHRLEdZ3sXKaEvT1EQU=
+github.com/editorconfig/editorconfig-core-go/v2 v2.1.1/go.mod h1:/LuhWJiQ9Gvo1DhVpa4ssm5qeg8rrztdtI7j/iCie2k=
 github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw=
 github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
 github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
@@ -142,20 +142,10 @@ github.com/etcd-io/bbolt v1.3.2 h1:RLRQ0TKLX7DlBRXAJHvbmXL17Q3KNnTBtZ9B6Qo+/Y0=
 github.com/etcd-io/bbolt v1.3.2/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
 github.com/ethantkoenig/rupture v0.0.0-20180203182544-0a76f03a811a h1:M1bRpaZAn4GSsqu3hdK2R8H0AH9O6vqCTCbm2oAFGfE=
 github.com/ethantkoenig/rupture v0.0.0-20180203182544-0a76f03a811a/go.mod h1:MkKY/CB98aVE4VxO63X5vTQKUgcn+3XP15LMASe3lYs=
-github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw=
-github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a/go.mod h1:7Ga40egUymuWXxAe151lTNnCv97MddSOVsjpPPkityA=
 github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ=
 github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
-github.com/facebookgo/freeport v0.0.0-20150612182905-d4adf43b75b9 h1:wWke/RUCl7VRjQhwPlR/v0glZXNYzBHdNUzf/Am2Nmg=
-github.com/facebookgo/freeport v0.0.0-20150612182905-d4adf43b75b9/go.mod h1:uPmAp6Sws4L7+Q/OokbWDAK1ibXYhB3PXFP1kol5hPg=
-github.com/facebookgo/grace v0.0.0-20160926231715-5729e484473f h1:0mlfEUWnUDVZnqWEVHGerL5bKYDKMEmT/Qk/W/3nGuo=
-github.com/facebookgo/grace v0.0.0-20160926231715-5729e484473f/go.mod h1:KigFdumBXUPSwzLDbeuzyt0elrL7+CP7TKuhrhT4bcU=
-github.com/facebookgo/httpdown v0.0.0-20160323221027-a3b1354551a2 h1:3Zvf9wRhl1cOhckN1oRGWPOkIhOketmEcrQ4TeFAoR4=
-github.com/facebookgo/httpdown v0.0.0-20160323221027-a3b1354551a2/go.mod h1:TUV/fX3XrTtBQb5+ttSUJzcFgLNpILONFTKmBuk5RSw=
 github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
 github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
-github.com/facebookgo/stats v0.0.0-20151006221625-1b76add642e4 h1:0YtRCqIZs2+Tz49QuH6cJVw/IFqzo39gEqZ0iYLxD2M=
-github.com/facebookgo/stats v0.0.0-20151006221625-1b76add642e4/go.mod h1:vsJz7uE339KUCpBXx3JAJzSRH7Uk4iGGyJzR529qDIA=
 github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQDg5gKsWoLBOB0n+ZW8s599zru8FJ2/Y=
 github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
 github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
@@ -249,11 +239,8 @@ github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013 h1:l
 github.com/go-swagger/scan-repo-boundary v0.0.0-20180623220736-973b3573c013/go.mod h1:b65mBPzqzZWxOZGxSWrqs4GInLIn+u99Q9q7p+GKni0=
 github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y=
 github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM=
-github.com/go-xorm/xorm v0.7.9 h1:LZze6n1UvRmM5gpL9/U9Gucwqo6aWlFVlfcHKH10qA0=
-github.com/go-xorm/xorm v0.7.9/go.mod h1:XiVxrMMIhFkwSkh96BW7PACl7UhLtx2iJIHMdmjh5sQ=
 github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
 github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
-github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
 github.com/gogits/chardet v0.0.0-20150115103509-2404f7772561 h1:deE7ritpK04PgtpyVOS2TYcQEld9qLCD5b5EbVNOuLA=
 github.com/gogits/chardet v0.0.0-20150115103509-2404f7772561/go.mod h1:YgYOrVn3Nj9Tq0EvjmFbphRytDj7JNRoWSStJZWDJTQ=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -337,9 +324,6 @@ github.com/issue9/assert v1.3.2 h1:IaTa37u4m1fUuTH9K9ldO5IONKVDXjLiUO1T9vj0OF0=
 github.com/issue9/assert v1.3.2/go.mod h1:9Ger+iz8X7r1zMYYwEhh++2wMGWcNN2oVI+zIQXxcio=
 github.com/issue9/identicon v0.0.0-20160320065130-d36b54562f4c h1:A/PDn117UYld5mlxe58EpMguqpkeTMw5/FCo0ZPS/Ko=
 github.com/issue9/identicon v0.0.0-20160320065130-d36b54562f4c/go.mod h1:5mTb/PQNkqmq2x3IxlQZE0aSnTksJg7fg/oWmJ5SKXQ=
-github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc=
-github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
-github.com/jackc/pgx v3.6.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
 github.com/jarcoal/httpmock v0.0.0-20180424175123-9c70cfe4a1da/go.mod h1:ks+b9deReOc7jgqp+e7LuFiCBH6Rm5hL32cLcEAArb4=
 github.com/jaytaylor/html2text v0.0.0-20160923191438-8fb95d837f7d h1:ig/iUfDDg06RVW8OMby+GrmW6K2nPO3AFHlEIdvJSd4=
 github.com/jaytaylor/html2text v0.0.0-20160923191438-8fb95d837f7d/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
@@ -384,8 +368,8 @@ github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
 github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
 github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/lafriks/xormstore v1.3.1 h1:KpzRUamSV3zmA85Kzw+PZOU9wgMbYsNzuDzLuBMbxpA=
-github.com/lafriks/xormstore v1.3.1/go.mod h1:qALRD4Vto2Ic7/A5eplMpu5V62mugtSqFysRwz8FETs=
+github.com/lafriks/xormstore v1.3.2 h1:hqi3F8s/B4rz8GuEZZDuHuOxRjeuOpEI/cC7vcnWwH4=
+github.com/lafriks/xormstore v1.3.2/go.mod h1:mVNIwIa25QIr8rfR7YlVjrqN/apswHkVdtLCyVYBzXw=
 github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
 github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
@@ -511,8 +495,6 @@ github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
 github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
 github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
-github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE=
-github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
 github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b h1:4kg1wyftSKxLtnPAvcRWakIPpokB9w780/KwrNLnfPA=
 github.com/shurcooL/httpfs v0.0.0-20190527155220-6a4d4a70508b/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
 github.com/shurcooL/sanitized_anchor_name v0.0.0-20160918041101-1dba4b3954bc h1:3wIrJvFb3Pf6B/2mDBnN1G5IfUVev4X5apadQlWOczE=
@@ -532,6 +514,7 @@ github.com/smartystreets/assertions v0.0.0-20190116191733-b6c0e53d7304/go.mod h1
 github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w=
 github.com/smartystreets/assertions v1.0.1/go.mod h1:kHHU4qYBaI3q23Pp3VPrmWhuIUrLW/7eUrw0BU5VaoM=
 github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
+github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337 h1:WN9BUFbdyOsSH/XohnWpXOlq9NBD5sGAB2FciQMUEe8=
 github.com/smartystreets/goconvey v0.0.0-20190731233626-505e41936337/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
 github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
@@ -773,17 +756,18 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/editorconfig/editorconfig-core-go.v1 v1.3.0 h1:oxOEwvhxLMpWpN+0pb2r9TWrM0DCFBHxbuIlS27tmFg=
-gopkg.in/editorconfig/editorconfig-core-go.v1 v1.3.0/go.mod h1:s2mQFI9McjArkyCwyEwU//+luQENTnD/Lfb/7Sj3/kQ=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
 gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
 gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df h1:n7WqCuqOuCbNr617RXOY0AWRXxgwEyPp2z+p0+hgMuE=
 gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df/go.mod h1:LRQQ+SO6ZHR7tOkpBDuZnXENFzX8qRjMDMyPD6BRkCw=
+gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/ini.v1 v1.44.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/ini.v1 v1.44.2/go.mod h1:M3Cogqpuv0QCi3ExAY5V4uOt4qb/R3xZubo9m8lK5wg=
 gopkg.in/ini.v1 v1.46.0 h1:VeDZbLYGaupuvIrsYCEOe/L/2Pcs5n7hdO1ZTjporag=
 gopkg.in/ini.v1 v1.46.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
+gopkg.in/ini.v1 v1.48.0 h1:URjZc+8ugRY5mL5uUeQH/a63JcHwdX9xZaWvmNWD7z8=
+gopkg.in/ini.v1 v1.48.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
 gopkg.in/ldap.v3 v3.0.2 h1:R6RBtabK6e1GO0eQKtkyOFbAHO73QesLzI2w2DZ6b9w=
 gopkg.in/ldap.v3 v3.0.2/go.mod h1:oxD7NyBuxchC+SgJDE1Q5Od05eGt29SDQVBmV+HYbzw=
 gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
@@ -794,8 +778,6 @@ gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOA
 gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
 gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE=
 gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8=
-gopkg.in/stretchr/testify.v1 v1.2.2 h1:yhQC6Uy5CqibAIlk1wlusa/MJ3iAN49/BsR/dCCKz3M=
-gopkg.in/stretchr/testify.v1 v1.2.2/go.mod h1:QI5V/q6UbPmuhtm10CaFZxED9NreB8PnFYN9JcR6TxU=
 gopkg.in/testfixtures.v2 v2.5.0 h1:N08B7l2GzFQenyYbzqthDnKAA+cmb17iAZhhFxr7JHw=
 gopkg.in/testfixtures.v2 v2.5.0/go.mod h1:vyAq+MYCgNpR29qitQdLZhdbLFf4mR/2MFJRFoQZZ2M=
 gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
@@ -812,14 +794,14 @@ honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWh
 honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 honnef.co/go/tools v0.0.1-2019.2.2/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
-mvdan.cc/xurls/v2 v2.0.0 h1:r1zSOSNS/kqtpmATyMMMvaZ4/djsesbYz5kr0+qMRWc=
-mvdan.cc/xurls/v2 v2.0.0/go.mod h1:2/webFPYOXN9jp/lzuj0zuAVlF+9g4KPFJANH1oJhRU=
+mvdan.cc/xurls/v2 v2.1.0 h1:KaMb5GLhlcSX+e+qhbRJODnUUBvlw01jt4yrjFIHAuA=
+mvdan.cc/xurls/v2 v2.1.0/go.mod h1:5GrSd9rOnKOpZaji1OZLYL/yeAAtGDlo/cFe+8K5n8E=
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
-strk.kbt.io/projects/go/libravatar v0.0.0-20160628055650-5eed7bff870a h1:8q33ShxKXRwQ7JVd1ZnhIU3hZhwwn0Le+4fTeAackuM=
-strk.kbt.io/projects/go/libravatar v0.0.0-20160628055650-5eed7bff870a/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY=
+strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 h1:mUcz5b3FJbP5Cvdq7Khzn6J9OCUQJaBwgBkCR+MOwSs=
+strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY=
 xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8=
 xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=
-xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb h1:msX3zG3BPl8Ti+LDzP33/9K7BzO/WqFXk610K1kYKfo=
-xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
 xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw=
 xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
+xorm.io/xorm v0.8.0 h1:iALxgJrX8O00f8Jk22GbZwPmxJNgssV5Mv4uc2HL9PM=
+xorm.io/xorm v0.8.0/go.mod h1:ZkJLEYLoVyg7amJK/5r779bHyzs2AU8f8VMiP6BM7uY=
diff --git a/integrations/api_helper_for_declarative_test.go b/integrations/api_helper_for_declarative_test.go
index 805a986ae3..cae7691c4b 100644
--- a/integrations/api_helper_for_declarative_test.go
+++ b/integrations/api_helper_for_declarative_test.go
@@ -231,3 +231,38 @@ func doAPIMergePullRequest(ctx APITestContext, owner, repo string, index int64)
 		ctx.Session.MakeRequest(t, req, 200)
 	}
 }
+
+func doAPIGetBranch(ctx APITestContext, branch string, callback ...func(*testing.T, api.Branch)) func(*testing.T) {
+	return func(t *testing.T) {
+		req := NewRequestf(t, "GET", "/api/v1/repos/%s/%s/branches/%s?token=%s", ctx.Username, ctx.Reponame, branch, ctx.Token)
+		if ctx.ExpectedCode != 0 {
+			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
+			return
+		}
+		resp := ctx.Session.MakeRequest(t, req, http.StatusOK)
+
+		var branch api.Branch
+		DecodeJSON(t, resp, &branch)
+		if len(callback) > 0 {
+			callback[0](t, branch)
+		}
+	}
+}
+
+func doAPICreateFile(ctx APITestContext, treepath string, options *api.CreateFileOptions, callback ...func(*testing.T, api.FileResponse)) func(*testing.T) {
+	return func(t *testing.T) {
+		url := fmt.Sprintf("/api/v1/repos/%s/%s/contents/%s?token=%s", ctx.Username, ctx.Reponame, treepath, ctx.Token)
+		req := NewRequestWithJSON(t, "POST", url, &options)
+		if ctx.ExpectedCode != 0 {
+			ctx.Session.MakeRequest(t, req, ctx.ExpectedCode)
+			return
+		}
+		resp := ctx.Session.MakeRequest(t, req, http.StatusCreated)
+
+		var contents api.FileResponse
+		DecodeJSON(t, resp, &contents)
+		if len(callback) > 0 {
+			callback[0](t, contents)
+		}
+	}
+}
diff --git a/integrations/api_pull_test.go b/integrations/api_pull_test.go
index 8d24cdc188..ed5a55a9db 100644
--- a/integrations/api_pull_test.go
+++ b/integrations/api_pull_test.go
@@ -13,6 +13,7 @@ import (
 	"code.gitea.io/gitea/modules/auth"
 	"code.gitea.io/gitea/modules/setting"
 	api "code.gitea.io/gitea/modules/structs"
+	issue_service "code.gitea.io/gitea/services/issue"
 
 	"github.com/stretchr/testify/assert"
 )
@@ -40,7 +41,7 @@ func TestAPIMergePullWIP(t *testing.T) {
 	owner := models.AssertExistsAndLoadBean(t, &models.User{ID: repo.OwnerID}).(*models.User)
 	pr := models.AssertExistsAndLoadBean(t, &models.PullRequest{Status: models.PullRequestStatusMergeable}, models.Cond("has_merged = ?", false)).(*models.PullRequest)
 	pr.LoadIssue()
-	pr.Issue.ChangeTitle(owner, setting.Repository.PullRequest.WorkInProgressPrefixes[0]+" "+pr.Issue.Title)
+	issue_service.ChangeTitle(pr.Issue, owner, setting.Repository.PullRequest.WorkInProgressPrefixes[0]+" "+pr.Issue.Title)
 
 	// force reload
 	pr.LoadAttributes()
diff --git a/integrations/api_repo_file_create_test.go b/integrations/api_repo_file_create_test.go
index 42898bf259..4d76ff00ce 100644
--- a/integrations/api_repo_file_create_test.go
+++ b/integrations/api_repo_file_create_test.go
@@ -91,7 +91,7 @@ func getExpectedFileResponseForCreate(commitID, treePath string) *api.FileRespon
 		},
 		Verification: &api.PayloadCommitVerification{
 			Verified:  false,
-			Reason:    "unsigned",
+			Reason:    "gpg.error.not_signed_commit",
 			Signature: "",
 			Payload:   "",
 		},
diff --git a/integrations/api_repo_file_update_test.go b/integrations/api_repo_file_update_test.go
index 366eb5e918..bf695d4344 100644
--- a/integrations/api_repo_file_update_test.go
+++ b/integrations/api_repo_file_update_test.go
@@ -94,7 +94,7 @@ func getExpectedFileResponseForUpdate(commitID, treePath string) *api.FileRespon
 		},
 		Verification: &api.PayloadCommitVerification{
 			Verified:  false,
-			Reason:    "unsigned",
+			Reason:    "gpg.error.not_signed_commit",
 			Signature: "",
 			Payload:   "",
 		},
diff --git a/integrations/api_team_user_test.go b/integrations/api_team_user_test.go
index 70d52c1360..4df4dac016 100644
--- a/integrations/api_team_user_test.go
+++ b/integrations/api_team_user_test.go
@@ -29,7 +29,6 @@ func TestAPITeamUser(t *testing.T) {
 	var user2 *api.User
 	DecodeJSON(t, resp, &user2)
 	user2.Created = user2.Created.In(time.Local)
-	user2.LastLogin = user2.LastLogin.In(time.Local)
 	user := models.AssertExistsAndLoadBean(t, &models.User{Name: "user2"}).(*models.User)
 
 	assert.Equal(t, convert.ToUser(user, true, false), user2)
diff --git a/integrations/api_user_heatmap_test.go b/integrations/api_user_heatmap_test.go
index 5245bb0a26..2e2636ce94 100644
--- a/integrations/api_user_heatmap_test.go
+++ b/integrations/api_user_heatmap_test.go
@@ -26,7 +26,7 @@ func TestUserHeatmap(t *testing.T) {
 	var heatmap []*models.UserHeatmapData
 	DecodeJSON(t, resp, &heatmap)
 	var dummyheatmap []*models.UserHeatmapData
-	dummyheatmap = append(dummyheatmap, &models.UserHeatmapData{Timestamp: 1540080000, Contributions: 1})
+	dummyheatmap = append(dummyheatmap, &models.UserHeatmapData{Timestamp: 1571616000, Contributions: 1})
 
 	assert.Equal(t, dummyheatmap, heatmap)
 }
diff --git a/integrations/git_helper_for_declarative_test.go b/integrations/git_helper_for_declarative_test.go
index 628611d2d7..294f0bc5fe 100644
--- a/integrations/git_helper_for_declarative_test.go
+++ b/integrations/git_helper_for_declarative_test.go
@@ -12,7 +12,9 @@ import (
 	"net/http"
 	"net/url"
 	"os"
+	"path"
 	"path/filepath"
+	"strings"
 	"testing"
 	"time"
 
@@ -37,7 +39,12 @@ func withKeyFile(t *testing.T, keyname string, callback func(string)) {
 	err = ssh.GenKeyPair(keyFile)
 	assert.NoError(t, err)
 
+	err = ioutil.WriteFile(path.Join(tmpDir, "ssh"), []byte("#!/bin/bash\n"+
+		"ssh -o \"UserKnownHostsFile=/dev/null\" -o \"StrictHostKeyChecking=no\" -o \"IdentitiesOnly=yes\" -i \""+keyFile+"\" \"$@\""), 0700)
+	assert.NoError(t, err)
+
 	//Setup ssh wrapper
+	os.Setenv("GIT_SSH", path.Join(tmpDir, "ssh"))
 	os.Setenv("GIT_SSH_COMMAND",
 		"ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o IdentitiesOnly=yes -i \""+keyFile+"\"")
 	os.Setenv("GIT_SSH_VARIANT", "ssh")
@@ -54,6 +61,24 @@ func createSSHUrl(gitPath string, u *url.URL) *url.URL {
 	return &u2
 }
 
+func allowLFSFilters() []string {
+	// Now here we should explicitly allow lfs filters to run
+	globalArgs := git.GlobalCommandArgs
+	filteredLFSGlobalArgs := make([]string, len(git.GlobalCommandArgs))
+	j := 0
+	for _, arg := range git.GlobalCommandArgs {
+		if strings.Contains(arg, "lfs") {
+			j--
+		} else {
+			filteredLFSGlobalArgs[j] = arg
+			j++
+		}
+	}
+	filteredLFSGlobalArgs = filteredLFSGlobalArgs[:j]
+	git.GlobalCommandArgs = filteredLFSGlobalArgs
+	return globalArgs
+}
+
 func onGiteaRun(t *testing.T, callback func(*testing.T, *url.URL)) {
 	prepareTestEnv(t, 1)
 	s := http.Server{
@@ -79,7 +104,9 @@ func onGiteaRun(t *testing.T, callback func(*testing.T, *url.URL)) {
 
 func doGitClone(dstLocalPath string, u *url.URL) func(*testing.T) {
 	return func(t *testing.T) {
+		oldGlobals := allowLFSFilters()
 		assert.NoError(t, git.Clone(u.String(), dstLocalPath, git.CloneRepoOptions{}))
+		git.GlobalCommandArgs = oldGlobals
 		assert.True(t, com.IsExist(filepath.Join(dstLocalPath, "README.md")))
 	}
 }
@@ -140,7 +167,9 @@ func doGitCreateBranch(dstPath, branch string) func(*testing.T) {
 
 func doGitCheckoutBranch(dstPath string, args ...string) func(*testing.T) {
 	return func(t *testing.T) {
+		oldGlobals := allowLFSFilters()
 		_, err := git.NewCommand(append([]string{"checkout"}, args...)...).RunInDir(dstPath)
+		git.GlobalCommandArgs = oldGlobals
 		assert.NoError(t, err)
 	}
 }
@@ -154,7 +183,9 @@ func doGitMerge(dstPath string, args ...string) func(*testing.T) {
 
 func doGitPull(dstPath string, args ...string) func(*testing.T) {
 	return func(t *testing.T) {
+		oldGlobals := allowLFSFilters()
 		_, err := git.NewCommand(append([]string{"pull"}, args...)...).RunInDir(dstPath)
+		git.GlobalCommandArgs = oldGlobals
 		assert.NoError(t, err)
 	}
 }
diff --git a/integrations/git_test.go b/integrations/git_test.go
index 8578fb86d5..dbcc265227 100644
--- a/integrations/git_test.go
+++ b/integrations/git_test.go
@@ -19,6 +19,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/git"
+	"code.gitea.io/gitea/modules/setting"
 	api "code.gitea.io/gitea/modules/structs"
 
 	"github.com/stretchr/testify/assert"
@@ -135,6 +136,11 @@ func standardCommitAndPushTest(t *testing.T, dstPath string) (little, big string
 func lfsCommitAndPushTest(t *testing.T, dstPath string) (littleLFS, bigLFS string) {
 	t.Run("LFS", func(t *testing.T) {
 		PrintCurrentTest(t)
+		setting.CheckLFSVersion()
+		if !setting.LFS.StartServer {
+			t.Skip()
+			return
+		}
 		prefix := "lfs-data-file-"
 		_, err := git.NewCommand("lfs").AddArguments("install").RunInDir(dstPath)
 		assert.NoError(t, err)
@@ -142,6 +148,21 @@ func lfsCommitAndPushTest(t *testing.T, dstPath string) (littleLFS, bigLFS strin
 		assert.NoError(t, err)
 		err = git.AddChanges(dstPath, false, ".gitattributes")
 		assert.NoError(t, err)
+		oldGlobals := allowLFSFilters()
+		err = git.CommitChanges(dstPath, git.CommitChangesOptions{
+			Committer: &git.Signature{
+				Email: "user2@example.com",
+				Name:  "User Two",
+				When:  time.Now(),
+			},
+			Author: &git.Signature{
+				Email: "user2@example.com",
+				Name:  "User Two",
+				When:  time.Now(),
+			},
+			Message: fmt.Sprintf("Testing commit @ %v", time.Now()),
+		})
+		git.GlobalCommandArgs = oldGlobals
 
 		littleLFS, bigLFS = commitAndPushTest(t, dstPath, prefix)
 
@@ -185,20 +206,25 @@ func rawTest(t *testing.T, ctx *APITestContext, little, big, littleLFS, bigLFS s
 		resp := session.MakeRequest(t, req, http.StatusOK)
 		assert.Equal(t, littleSize, resp.Body.Len())
 
-		req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", littleLFS))
-		resp = session.MakeRequest(t, req, http.StatusOK)
-		assert.NotEqual(t, littleSize, resp.Body.Len())
-		assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
+		setting.CheckLFSVersion()
+		if setting.LFS.StartServer {
+			req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", littleLFS))
+			resp = session.MakeRequest(t, req, http.StatusOK)
+			assert.NotEqual(t, littleSize, resp.Body.Len())
+			assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
+		}
 
 		if !testing.Short() {
 			req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", big))
 			resp = session.MakeRequest(t, req, http.StatusOK)
 			assert.Equal(t, bigSize, resp.Body.Len())
 
-			req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", bigLFS))
-			resp = session.MakeRequest(t, req, http.StatusOK)
-			assert.NotEqual(t, bigSize, resp.Body.Len())
-			assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
+			if setting.LFS.StartServer {
+				req = NewRequest(t, "GET", path.Join("/", username, reponame, "/raw/branch/master/", bigLFS))
+				resp = session.MakeRequest(t, req, http.StatusOK)
+				assert.NotEqual(t, bigSize, resp.Body.Len())
+				assert.Contains(t, resp.Body.String(), models.LFSMetaFileIdentifier)
+			}
 		}
 	})
 }
@@ -217,18 +243,23 @@ func mediaTest(t *testing.T, ctx *APITestContext, little, big, littleLFS, bigLFS
 		resp := session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
 		assert.Equal(t, littleSize, resp.Length)
 
-		req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", littleLFS))
-		resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
-		assert.Equal(t, littleSize, resp.Length)
+		setting.CheckLFSVersion()
+		if setting.LFS.StartServer {
+			req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", littleLFS))
+			resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
+			assert.Equal(t, littleSize, resp.Length)
+		}
 
 		if !testing.Short() {
 			req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", big))
 			resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
 			assert.Equal(t, bigSize, resp.Length)
 
-			req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", bigLFS))
-			resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
-			assert.Equal(t, bigSize, resp.Length)
+			if setting.LFS.StartServer {
+				req = NewRequest(t, "GET", path.Join("/", username, reponame, "/media/branch/master/", bigLFS))
+				resp = session.MakeRequestNilResponseRecorder(t, req, http.StatusOK)
+				assert.Equal(t, bigSize, resp.Length)
+			}
 		}
 	})
 }
@@ -274,6 +305,8 @@ func generateCommitWithNewData(size int, repoPath, email, fullName, prefix strin
 	}
 
 	//Commit
+	// Now here we should explicitly allow lfs filters to run
+	oldGlobals := allowLFSFilters()
 	err = git.AddChanges(repoPath, false, filepath.Base(tmpFile.Name()))
 	if err != nil {
 		return "", err
@@ -291,6 +324,7 @@ func generateCommitWithNewData(size int, repoPath, email, fullName, prefix strin
 		},
 		Message: fmt.Sprintf("Testing commit @ %v", time.Now()),
 	})
+	git.GlobalCommandArgs = oldGlobals
 	return filepath.Base(tmpFile.Name()), err
 }
 
diff --git a/integrations/gpg_git_test.go b/integrations/gpg_git_test.go
new file mode 100644
index 0000000000..12f0a138c7
--- /dev/null
+++ b/integrations/gpg_git_test.go
@@ -0,0 +1,252 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package integrations
+
+import (
+	"encoding/base64"
+	"fmt"
+	"io/ioutil"
+	"net/url"
+	"os"
+	"path/filepath"
+	"testing"
+
+	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/modules/process"
+	"code.gitea.io/gitea/modules/setting"
+	api "code.gitea.io/gitea/modules/structs"
+	"github.com/stretchr/testify/assert"
+	"golang.org/x/crypto/openpgp"
+	"golang.org/x/crypto/openpgp/armor"
+)
+
+func TestGPGGit(t *testing.T) {
+	onGiteaRun(t, testGPGGit)
+}
+
+func testGPGGit(t *testing.T, u *url.URL) {
+	username := "user2"
+	baseAPITestContext := NewAPITestContext(t, username, "repo1")
+
+	u.Path = baseAPITestContext.GitPath()
+
+	// OK Set a new GPG home
+	tmpDir, err := ioutil.TempDir("", "temp-gpg")
+	assert.NoError(t, err)
+	defer os.RemoveAll(tmpDir)
+
+	err = os.Chmod(tmpDir, 0700)
+	assert.NoError(t, err)
+
+	oldGNUPGHome := os.Getenv("GNUPGHOME")
+	err = os.Setenv("GNUPGHOME", tmpDir)
+	assert.NoError(t, err)
+	defer os.Setenv("GNUPGHOME", oldGNUPGHome)
+
+	// Need to create a root key
+	rootKeyPair, err := createGPGKey(tmpDir, "gitea", "gitea@fake.local")
+	assert.NoError(t, err)
+
+	rootKeyID := rootKeyPair.PrimaryKey.KeyIdShortString()
+
+	oldKeyID := setting.Repository.Signing.SigningKey
+	oldName := setting.Repository.Signing.SigningName
+	oldEmail := setting.Repository.Signing.SigningEmail
+	defer func() {
+		setting.Repository.Signing.SigningKey = oldKeyID
+		setting.Repository.Signing.SigningName = oldName
+		setting.Repository.Signing.SigningEmail = oldEmail
+	}()
+
+	setting.Repository.Signing.SigningKey = rootKeyID
+	setting.Repository.Signing.SigningName = "gitea"
+	setting.Repository.Signing.SigningEmail = "gitea@fake.local"
+	user := models.AssertExistsAndLoadBean(t, &models.User{Name: username}).(*models.User)
+
+	t.Run("Unsigned-Initial", func(t *testing.T) {
+		PrintCurrentTest(t)
+		setting.Repository.Signing.InitialCommit = []string{"never"}
+		testCtx := NewAPITestContext(t, username, "initial-unsigned")
+		t.Run("CreateRepository", doAPICreateRepository(testCtx, false))
+		t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
+			assert.NotNil(t, branch.Commit)
+			assert.NotNil(t, branch.Commit.Verification)
+			assert.False(t, branch.Commit.Verification.Verified)
+			assert.Empty(t, branch.Commit.Verification.Signature)
+		}))
+		setting.Repository.Signing.CRUDActions = []string{"never"}
+		t.Run("CreateCRUDFile-Never", crudActionCreateFile(
+			t, testCtx, user, "master", "never", "unsigned-never.txt", func(t *testing.T, response api.FileResponse) {
+				assert.False(t, response.Verification.Verified)
+			}))
+		t.Run("CreateCRUDFile-Never", crudActionCreateFile(
+			t, testCtx, user, "never", "never2", "unsigned-never2.txt", func(t *testing.T, response api.FileResponse) {
+				assert.False(t, response.Verification.Verified)
+			}))
+		setting.Repository.Signing.CRUDActions = []string{"parentsigned"}
+		t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile(
+			t, testCtx, user, "master", "parentsigned", "signed-parent.txt", func(t *testing.T, response api.FileResponse) {
+				assert.False(t, response.Verification.Verified)
+			}))
+		t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile(
+			t, testCtx, user, "parentsigned", "parentsigned2", "signed-parent2.txt", func(t *testing.T, response api.FileResponse) {
+				assert.False(t, response.Verification.Verified)
+			}))
+		setting.Repository.Signing.CRUDActions = []string{"never"}
+		t.Run("CreateCRUDFile-Never", crudActionCreateFile(
+			t, testCtx, user, "parentsigned", "parentsigned-never", "unsigned-never2.txt", func(t *testing.T, response api.FileResponse) {
+				assert.False(t, response.Verification.Verified)
+			}))
+		setting.Repository.Signing.CRUDActions = []string{"always"}
+		t.Run("CreateCRUDFile-Always", crudActionCreateFile(
+			t, testCtx, user, "master", "always", "signed-always.txt", func(t *testing.T, response api.FileResponse) {
+				assert.True(t, response.Verification.Verified)
+				assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
+			}))
+		t.Run("CreateCRUDFile-ParentSigned-always", crudActionCreateFile(
+			t, testCtx, user, "parentsigned", "parentsigned-always", "signed-parent2.txt", func(t *testing.T, response api.FileResponse) {
+				assert.True(t, response.Verification.Verified)
+				assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
+			}))
+		setting.Repository.Signing.CRUDActions = []string{"parentsigned"}
+		t.Run("CreateCRUDFile-Always-ParentSigned", crudActionCreateFile(
+			t, testCtx, user, "always", "always-parentsigned", "signed-always-parentsigned.txt", func(t *testing.T, response api.FileResponse) {
+				assert.True(t, response.Verification.Verified)
+				assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
+			}))
+	})
+	t.Run("AlwaysSign-Initial", func(t *testing.T) {
+		PrintCurrentTest(t)
+		setting.Repository.Signing.InitialCommit = []string{"always"}
+		testCtx := NewAPITestContext(t, username, "initial-always")
+		t.Run("CreateRepository", doAPICreateRepository(testCtx, false))
+		t.Run("CheckMasterBranchSigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
+			assert.NotNil(t, branch.Commit)
+			assert.NotNil(t, branch.Commit.Verification)
+			assert.True(t, branch.Commit.Verification.Verified)
+			assert.Equal(t, "gitea@fake.local", branch.Commit.Verification.Signer.Email)
+		}))
+		setting.Repository.Signing.CRUDActions = []string{"never"}
+		t.Run("CreateCRUDFile-Never", crudActionCreateFile(
+			t, testCtx, user, "master", "never", "unsigned-never.txt", func(t *testing.T, response api.FileResponse) {
+				assert.False(t, response.Verification.Verified)
+			}))
+		setting.Repository.Signing.CRUDActions = []string{"parentsigned"}
+		t.Run("CreateCRUDFile-ParentSigned", crudActionCreateFile(
+			t, testCtx, user, "master", "parentsigned", "signed-parent.txt", func(t *testing.T, response api.FileResponse) {
+				assert.True(t, response.Verification.Verified)
+				assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
+			}))
+		setting.Repository.Signing.CRUDActions = []string{"always"}
+		t.Run("CreateCRUDFile-Always", crudActionCreateFile(
+			t, testCtx, user, "master", "always", "signed-always.txt", func(t *testing.T, response api.FileResponse) {
+				assert.True(t, response.Verification.Verified)
+				assert.Equal(t, "gitea@fake.local", response.Verification.Signer.Email)
+			}))
+
+	})
+	t.Run("UnsignedMerging", func(t *testing.T) {
+		PrintCurrentTest(t)
+		testCtx := NewAPITestContext(t, username, "initial-unsigned")
+		var pr api.PullRequest
+		var err error
+		t.Run("CreatePullRequest", func(t *testing.T) {
+			pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "never2")(t)
+			assert.NoError(t, err)
+		})
+		setting.Repository.Signing.Merges = []string{"commitssigned"}
+		t.Run("MergePR", doAPIMergePullRequest(testCtx, testCtx.Username, testCtx.Reponame, pr.Index))
+		t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
+			assert.NotNil(t, branch.Commit)
+			assert.NotNil(t, branch.Commit.Verification)
+			assert.False(t, branch.Commit.Verification.Verified)
+			assert.Empty(t, branch.Commit.Verification.Signature)
+		}))
+		setting.Repository.Signing.Merges = []string{"basesigned"}
+		t.Run("CreatePullRequest", func(t *testing.T) {
+			pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "parentsigned2")(t)
+			assert.NoError(t, err)
+		})
+		t.Run("MergePR", doAPIMergePullRequest(testCtx, testCtx.Username, testCtx.Reponame, pr.Index))
+		t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
+			assert.NotNil(t, branch.Commit)
+			assert.NotNil(t, branch.Commit.Verification)
+			assert.False(t, branch.Commit.Verification.Verified)
+			assert.Empty(t, branch.Commit.Verification.Signature)
+		}))
+		setting.Repository.Signing.Merges = []string{"commitssigned"}
+		t.Run("CreatePullRequest", func(t *testing.T) {
+			pr, err = doAPICreatePullRequest(testCtx, testCtx.Username, testCtx.Reponame, "master", "always-parentsigned")(t)
+			assert.NoError(t, err)
+		})
+		t.Run("MergePR", doAPIMergePullRequest(testCtx, testCtx.Username, testCtx.Reponame, pr.Index))
+		t.Run("CheckMasterBranchUnsigned", doAPIGetBranch(testCtx, "master", func(t *testing.T, branch api.Branch) {
+			assert.NotNil(t, branch.Commit)
+			assert.NotNil(t, branch.Commit.Verification)
+			assert.True(t, branch.Commit.Verification.Verified)
+		}))
+
+	})
+}
+
+func crudActionCreateFile(t *testing.T, ctx APITestContext, user *models.User, from, to, path string, callback ...func(*testing.T, api.FileResponse)) func(*testing.T) {
+	return doAPICreateFile(ctx, path, &api.CreateFileOptions{
+		FileOptions: api.FileOptions{
+			BranchName:    from,
+			NewBranchName: to,
+			Message:       fmt.Sprintf("from:%s to:%s path:%s", from, to, path),
+			Author: api.Identity{
+				Name:  user.FullName,
+				Email: user.Email,
+			},
+			Committer: api.Identity{
+				Name:  user.FullName,
+				Email: user.Email,
+			},
+		},
+		Content: base64.StdEncoding.EncodeToString([]byte("This is new text")),
+	}, callback...)
+}
+
+func createGPGKey(tmpDir, name, email string) (*openpgp.Entity, error) {
+	keyPair, err := openpgp.NewEntity(name, "test", email, nil)
+	if err != nil {
+		return nil, err
+	}
+
+	for _, id := range keyPair.Identities {
+		err := id.SelfSignature.SignUserId(id.UserId.Id, keyPair.PrimaryKey, keyPair.PrivateKey, nil)
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	keyFile := filepath.Join(tmpDir, "temporary.key")
+	keyWriter, err := os.Create(keyFile)
+	if err != nil {
+		return nil, err
+	}
+	defer keyWriter.Close()
+	defer os.Remove(keyFile)
+
+	w, err := armor.Encode(keyWriter, openpgp.PrivateKeyType, nil)
+	if err != nil {
+		return nil, err
+	}
+	defer w.Close()
+
+	keyPair.SerializePrivate(w, nil)
+	if err := w.Close(); err != nil {
+		return nil, err
+	}
+	if err := keyWriter.Close(); err != nil {
+		return nil, err
+	}
+
+	if _, _, err := process.GetManager().Exec("gpg --import temporary.key", "gpg", "--import", keyFile); err != nil {
+		return nil, err
+	}
+	return keyPair, nil
+}
diff --git a/integrations/issue_test.go b/integrations/issue_test.go
index 0b153607ee..aa17c44254 100644
--- a/integrations/issue_test.go
+++ b/integrations/issue_test.go
@@ -13,6 +13,7 @@ import (
 	"testing"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/modules/references"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/test"
 
@@ -207,7 +208,7 @@ func TestIssueCrossReference(t *testing.T) {
 		RefIssueID:   issueRef.ID,
 		RefCommentID: 0,
 		RefIsPull:    false,
-		RefAction:    models.XRefActionNone})
+		RefAction:    references.XRefActionNone})
 
 	// Edit title, neuter ref
 	testIssueChangeInfo(t, "user2", issueRefURL, "title", "Title no ref")
@@ -217,7 +218,7 @@ func TestIssueCrossReference(t *testing.T) {
 		RefIssueID:   issueRef.ID,
 		RefCommentID: 0,
 		RefIsPull:    false,
-		RefAction:    models.XRefActionNeutered})
+		RefAction:    references.XRefActionNeutered})
 
 	// Ref from issue content
 	issueRefURL, issueRef = testIssueWithBean(t, "user2", 1, "TitleXRef", fmt.Sprintf("Description ref #%d", issueBase.Index))
@@ -227,7 +228,7 @@ func TestIssueCrossReference(t *testing.T) {
 		RefIssueID:   issueRef.ID,
 		RefCommentID: 0,
 		RefIsPull:    false,
-		RefAction:    models.XRefActionNone})
+		RefAction:    references.XRefActionNone})
 
 	// Edit content, neuter ref
 	testIssueChangeInfo(t, "user2", issueRefURL, "content", "Description no ref")
@@ -237,7 +238,7 @@ func TestIssueCrossReference(t *testing.T) {
 		RefIssueID:   issueRef.ID,
 		RefCommentID: 0,
 		RefIsPull:    false,
-		RefAction:    models.XRefActionNeutered})
+		RefAction:    references.XRefActionNeutered})
 
 	// Ref from a comment
 	session := loginUser(t, "user2")
@@ -248,7 +249,7 @@ func TestIssueCrossReference(t *testing.T) {
 		RefIssueID:   issueRef.ID,
 		RefCommentID: commentID,
 		RefIsPull:    false,
-		RefAction:    models.XRefActionNone}
+		RefAction:    references.XRefActionNone}
 	models.AssertExistsAndLoadBean(t, comment)
 
 	// Ref from a different repository
@@ -259,7 +260,7 @@ func TestIssueCrossReference(t *testing.T) {
 		RefIssueID:   issueRef.ID,
 		RefCommentID: 0,
 		RefIsPull:    false,
-		RefAction:    models.XRefActionNone})
+		RefAction:    references.XRefActionNone})
 }
 
 func testIssueWithBean(t *testing.T, user string, repoID int64, title, content string) (string, *models.Issue) {
diff --git a/integrations/lfs_getobject_test.go b/integrations/lfs_getobject_test.go
index bb269d5eeb..373fffa445 100644
--- a/integrations/lfs_getobject_test.go
+++ b/integrations/lfs_getobject_test.go
@@ -58,6 +58,11 @@ func storeObjectInRepo(t *testing.T, repositoryID int64, content *[]byte) string
 
 func doLfs(t *testing.T, content *[]byte, expectGzip bool) {
 	prepareTestEnv(t)
+	setting.CheckLFSVersion()
+	if !setting.LFS.StartServer {
+		t.Skip()
+		return
+	}
 	repo, err := models.GetRepositoryByOwnerAndName("user2", "repo1")
 	assert.NoError(t, err)
 	oid := storeObjectInRepo(t, repo.ID, content)
diff --git a/integrations/migration-test/migration_test.go b/integrations/migration-test/migration_test.go
index 3b47f0d7fc..8dc366dc3f 100644
--- a/integrations/migration-test/migration_test.go
+++ b/integrations/migration-test/migration_test.go
@@ -23,8 +23,8 @@ import (
 	"code.gitea.io/gitea/modules/charset"
 	"code.gitea.io/gitea/modules/setting"
 
-	"github.com/go-xorm/xorm"
 	"github.com/stretchr/testify/assert"
+	"xorm.io/xorm"
 )
 
 var currentEngine *xorm.Engine
diff --git a/integrations/mssql.ini.tmpl b/integrations/mssql.ini.tmpl
index d38d038a4e..931e923cf4 100644
--- a/integrations/mssql.ini.tmpl
+++ b/integrations/mssql.ini.tmpl
@@ -21,6 +21,9 @@ ROOT = integrations/gitea-integration-mssql/gitea-repositories
 LOCAL_COPY_PATH = tmp/local-repo-mssql
 LOCAL_WIKI_PATH = tmp/local-wiki-mssql
 
+[repository.signing]
+SIGNING_KEY = none
+
 [server]
 SSH_DOMAIN       = localhost
 HTTP_PORT        = 3003
diff --git a/integrations/mysql.ini.tmpl b/integrations/mysql.ini.tmpl
index 6eed7e1578..4dde212798 100644
--- a/integrations/mysql.ini.tmpl
+++ b/integrations/mysql.ini.tmpl
@@ -21,6 +21,9 @@ ROOT = integrations/gitea-integration-mysql/gitea-repositories
 LOCAL_COPY_PATH = tmp/local-repo-mysql
 LOCAL_WIKI_PATH = tmp/local-wiki-mysql
 
+[repository.signing]
+SIGNING_KEY = none
+
 [server]
 SSH_DOMAIN       = localhost
 HTTP_PORT        = 3001
diff --git a/integrations/mysql8.ini.tmpl b/integrations/mysql8.ini.tmpl
index 1e14bc1356..1b1d3d2436 100644
--- a/integrations/mysql8.ini.tmpl
+++ b/integrations/mysql8.ini.tmpl
@@ -21,6 +21,9 @@ ROOT = integrations/gitea-integration-mysql8/gitea-repositories
 LOCAL_COPY_PATH = tmp/local-repo-mysql8
 LOCAL_WIKI_PATH = tmp/local-wiki-mysql8
 
+[repository.signing]
+SIGNING_KEY = none
+
 [server]
 SSH_DOMAIN       = localhost
 HTTP_PORT        = 3004
diff --git a/integrations/pgsql.ini.tmpl b/integrations/pgsql.ini.tmpl
index cd5dc44ea8..6265e0d98e 100644
--- a/integrations/pgsql.ini.tmpl
+++ b/integrations/pgsql.ini.tmpl
@@ -21,6 +21,9 @@ ROOT = integrations/gitea-integration-pgsql/gitea-repositories
 LOCAL_COPY_PATH = tmp/local-repo-pgsql
 LOCAL_WIKI_PATH = tmp/local-wiki-pgsql
 
+[repository.signing]
+SIGNING_KEY = none
+
 [server]
 SSH_DOMAIN       = localhost
 HTTP_PORT        = 3002
diff --git a/integrations/repofiles_delete_test.go b/integrations/repofiles_delete_test.go
index f4cb4510be..b4c535188b 100644
--- a/integrations/repofiles_delete_test.go
+++ b/integrations/repofiles_delete_test.go
@@ -53,7 +53,7 @@ func getExpectedDeleteFileResponse(u *url.URL) *api.FileResponse {
 		},
 		Verification: &api.PayloadCommitVerification{
 			Verified:  false,
-			Reason:    "",
+			Reason:    "gpg.error.not_signed_commit",
 			Signature: "",
 			Payload:   "",
 		},
diff --git a/integrations/repofiles_update_test.go b/integrations/repofiles_update_test.go
index a4ce16d847..c475c70008 100644
--- a/integrations/repofiles_update_test.go
+++ b/integrations/repofiles_update_test.go
@@ -108,7 +108,7 @@ func getExpectedFileResponseForRepofilesCreate(commitID string) *api.FileRespons
 		},
 		Verification: &api.PayloadCommitVerification{
 			Verified:  false,
-			Reason:    "unsigned",
+			Reason:    "gpg.error.not_signed_commit",
 			Signature: "",
 			Payload:   "",
 		},
@@ -175,7 +175,7 @@ func getExpectedFileResponseForRepofilesUpdate(commitID, filename string) *api.F
 		},
 		Verification: &api.PayloadCommitVerification{
 			Verified:  false,
-			Reason:    "unsigned",
+			Reason:    "gpg.error.not_signed_commit",
 			Signature: "",
 			Payload:   "",
 		},
diff --git a/integrations/sqlite.ini b/integrations/sqlite.ini
index b188406ee9..de3355c166 100644
--- a/integrations/sqlite.ini
+++ b/integrations/sqlite.ini
@@ -17,6 +17,9 @@ ROOT = integrations/gitea-integration-sqlite/gitea-repositories
 LOCAL_COPY_PATH = tmp/local-repo-sqlite
 LOCAL_WIKI_PATH = tmp/local-wiki-sqlite
 
+[repository.signing]
+SIGNING_KEY = none
+
 [server]
 SSH_DOMAIN       = localhost
 HTTP_PORT        = 3003
diff --git a/models/access.go b/models/access.go
index 3cdfc62f21..213efe08a6 100644
--- a/models/access.go
+++ b/models/access.go
@@ -246,6 +246,55 @@ func (repo *Repository) recalculateTeamAccesses(e Engine, ignTeamID int64) (err
 	return repo.refreshAccesses(e, accessMap)
 }
 
+// recalculateUserAccess recalculates new access for a single user
+// Usable if we know access only affected one user
+func (repo *Repository) recalculateUserAccess(e Engine, uid int64) (err error) {
+	minMode := AccessModeRead
+	if !repo.IsPrivate {
+		minMode = AccessModeWrite
+	}
+
+	accessMode := AccessModeNone
+	collaborator, err := repo.getCollaboration(e, uid)
+	if err != nil {
+		return err
+	} else if collaborator != nil {
+		accessMode = collaborator.Mode
+	}
+
+	if err = repo.getOwner(e); err != nil {
+		return err
+	} else if repo.Owner.IsOrganization() {
+		var teams []Team
+		if err := e.Join("INNER", "team_repo", "team_repo.team_id = team.id").
+			Join("INNER", "team_user", "team_user.team_id = team.id").
+			Where("team.org_id = ?", repo.OwnerID).
+			And("team_repo.repo_id=?", repo.ID).
+			And("team_user.uid=?", uid).
+			Find(&teams); err != nil {
+			return err
+		}
+
+		for _, t := range teams {
+			if t.IsOwnerTeam() {
+				t.Authorize = AccessModeOwner
+			}
+
+			accessMode = maxAccessMode(accessMode, t.Authorize)
+		}
+	}
+
+	// Delete old user accesses and insert new one for repository.
+	if _, err = e.Delete(&Access{RepoID: repo.ID, UserID: uid}); err != nil {
+		return fmt.Errorf("delete old user accesses: %v", err)
+	} else if accessMode >= minMode {
+		if _, err = e.Insert(&Access{RepoID: repo.ID, UserID: uid, Mode: accessMode}); err != nil {
+			return fmt.Errorf("insert new user accesses: %v", err)
+		}
+	}
+	return nil
+}
+
 func (repo *Repository) recalculateAccesses(e Engine) error {
 	if repo.Owner.IsOrganization() {
 		return repo.recalculateTeamAccesses(e, 0)
diff --git a/models/action.go b/models/action.go
index 87088101f9..b651c658d5 100644
--- a/models/action.go
+++ b/models/action.go
@@ -6,19 +6,17 @@
 package models
 
 import (
-	"encoding/json"
 	"fmt"
 	"html"
 	"path"
-	"regexp"
 	"strconv"
 	"strings"
 	"time"
-	"unicode"
 
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/references"
 	"code.gitea.io/gitea/modules/setting"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/timeutil"
@@ -54,29 +52,6 @@ const (
 	ActionMirrorSyncDelete                        // 20
 )
 
-var (
-	// Same as GitHub. See
-	// https://help.github.com/articles/closing-issues-via-commit-messages
-	issueCloseKeywords  = []string{"close", "closes", "closed", "fix", "fixes", "fixed", "resolve", "resolves", "resolved"}
-	issueReopenKeywords = []string{"reopen", "reopens", "reopened"}
-
-	issueCloseKeywordsPat, issueReopenKeywordsPat *regexp.Regexp
-	issueReferenceKeywordsPat                     *regexp.Regexp
-)
-
-const issueRefRegexpStr = `(?:([0-9a-zA-Z-_\.]+)/([0-9a-zA-Z-_\.]+))?(#[0-9]+)+`
-const issueRefRegexpStrNoKeyword = `(?:\s|^|\(|\[)(?:([0-9a-zA-Z-_\.]+)/([0-9a-zA-Z-_\.]+))?(#[0-9]+)(?:\s|$|\)|\]|:|\.(\s|$))`
-
-func assembleKeywordsPattern(words []string) string {
-	return fmt.Sprintf(`(?i)(?:%s)(?::?) %s`, strings.Join(words, "|"), issueRefRegexpStr)
-}
-
-func init() {
-	issueCloseKeywordsPat = regexp.MustCompile(assembleKeywordsPattern(issueCloseKeywords))
-	issueReopenKeywordsPat = regexp.MustCompile(assembleKeywordsPattern(issueReopenKeywords))
-	issueReferenceKeywordsPat = regexp.MustCompile(issueRefRegexpStrNoKeyword)
-}
-
 // Action represents user operation type and other information to
 // repository. It implemented interface base.Actioner so that can be
 // used in template render.
@@ -351,10 +326,6 @@ func RenameRepoAction(actUser *User, oldRepoName string, repo *Repository) error
 	return renameRepoAction(x, actUser, oldRepoName, repo)
 }
 
-func issueIndexTrimRight(c rune) bool {
-	return !unicode.IsDigit(c)
-}
-
 // PushCommit represents a commit in a push operation.
 type PushCommit struct {
 	Sha1           string
@@ -480,39 +451,9 @@ func (pc *PushCommits) AvatarLink(email string) string {
 }
 
 // getIssueFromRef returns the issue referenced by a ref. Returns a nil *Issue
-// if the provided ref is misformatted or references a non-existent issue.
-func getIssueFromRef(repo *Repository, ref string) (*Issue, error) {
-	ref = ref[strings.IndexByte(ref, ' ')+1:]
-	ref = strings.TrimRightFunc(ref, issueIndexTrimRight)
-
-	var refRepo *Repository
-	poundIndex := strings.IndexByte(ref, '#')
-	if poundIndex < 0 {
-		return nil, nil
-	} else if poundIndex == 0 {
-		refRepo = repo
-	} else {
-		slashIndex := strings.IndexByte(ref, '/')
-		if slashIndex < 0 || slashIndex >= poundIndex {
-			return nil, nil
-		}
-		ownerName := ref[:slashIndex]
-		repoName := ref[slashIndex+1 : poundIndex]
-		var err error
-		refRepo, err = GetRepositoryByOwnerAndName(ownerName, repoName)
-		if err != nil {
-			if IsErrRepoNotExist(err) {
-				return nil, nil
-			}
-			return nil, err
-		}
-	}
-	issueIndex, err := strconv.ParseInt(ref[poundIndex+1:], 10, 64)
-	if err != nil {
-		return nil, nil
-	}
-
-	issue, err := GetIssueByIndex(refRepo.ID, issueIndex)
+// if the provided ref references a non-existent issue.
+func getIssueFromRef(repo *Repository, index int64) (*Issue, error) {
+	issue, err := GetIssueByIndex(repo.ID, index)
 	if err != nil {
 		if IsErrIssueNotExist(err) {
 			return nil, nil
@@ -522,20 +463,7 @@ func getIssueFromRef(repo *Repository, ref string) (*Issue, error) {
 	return issue, nil
 }
 
-func changeIssueStatus(repo *Repository, doer *User, ref string, refMarked map[int64]bool, status bool) error {
-	issue, err := getIssueFromRef(repo, ref)
-	if err != nil {
-		return err
-	}
-
-	if issue == nil || refMarked[issue.ID] {
-		return nil
-	}
-	refMarked[issue.ID] = true
-
-	if issue.RepoID != repo.ID || issue.IsClosed == status {
-		return nil
-	}
+func changeIssueStatus(repo *Repository, issue *Issue, doer *User, status bool) error {
 
 	stopTimerIfAvailable := func(doer *User, issue *Issue) error {
 
@@ -549,7 +477,7 @@ func changeIssueStatus(repo *Repository, doer *User, ref string, refMarked map[i
 	}
 
 	issue.Repo = repo
-	if err = issue.ChangeStatus(doer, status); err != nil {
+	if err := issue.ChangeStatus(doer, status); err != nil {
 		// Don't return an error when dependencies are open as this would let the push fail
 		if IsErrDependenciesLeft(err) {
 			return stopTimerIfAvailable(doer, issue)
@@ -566,99 +494,67 @@ func UpdateIssuesCommit(doer *User, repo *Repository, commits []*PushCommit, bra
 	for i := len(commits) - 1; i >= 0; i-- {
 		c := commits[i]
 
-		refMarked := make(map[int64]bool)
+		type markKey struct {
+			ID     int64
+			Action references.XRefAction
+		}
+
+		refMarked := make(map[markKey]bool)
 		var refRepo *Repository
+		var refIssue *Issue
 		var err error
-		for _, m := range issueReferenceKeywordsPat.FindAllStringSubmatch(c.Message, -1) {
-			if len(m[3]) == 0 {
-				continue
-			}
-			ref := m[3]
+		for _, ref := range references.FindAllIssueReferences(c.Message) {
 
 			// issue is from another repo
-			if len(m[1]) > 0 && len(m[2]) > 0 {
-				refRepo, err = GetRepositoryFromMatch(m[1], m[2])
+			if len(ref.Owner) > 0 && len(ref.Name) > 0 {
+				refRepo, err = GetRepositoryFromMatch(ref.Owner, ref.Name)
 				if err != nil {
 					continue
 				}
 			} else {
 				refRepo = repo
 			}
-			issue, err := getIssueFromRef(refRepo, ref)
-			if err != nil {
+			if refIssue, err = getIssueFromRef(refRepo, ref.Index); err != nil {
 				return err
 			}
-
-			if issue == nil || refMarked[issue.ID] {
+			if refIssue == nil {
 				continue
 			}
-			refMarked[issue.ID] = true
-
-			message := fmt.Sprintf(`<a href="%s/commit/%s">%s</a>`, repo.Link(), c.Sha1, html.EscapeString(c.Message))
-			if err = CreateRefComment(doer, refRepo, issue, message, c.Sha1); err != nil {
-				return err
-			}
-		}
-
-		// Change issue status only if the commit has been pushed to the default branch.
-		// and if the repo is configured to allow only that
-		if repo.DefaultBranch != branchName && !repo.CloseIssuesViaCommitInAnyBranch {
-			continue
-		}
-		refMarked = make(map[int64]bool)
-		for _, m := range issueCloseKeywordsPat.FindAllStringSubmatch(c.Message, -1) {
-			if len(m[3]) == 0 {
-				continue
-			}
-			ref := m[3]
-
-			// issue is from another repo
-			if len(m[1]) > 0 && len(m[2]) > 0 {
-				refRepo, err = GetRepositoryFromMatch(m[1], m[2])
-				if err != nil {
-					continue
-				}
-			} else {
-				refRepo = repo
-			}
 
 			perm, err := GetUserRepoPermission(refRepo, doer)
 			if err != nil {
 				return err
 			}
-			// only close issues in another repo if user has push access
-			if perm.CanWrite(UnitTypeCode) {
-				if err := changeIssueStatus(refRepo, doer, ref, refMarked, true); err != nil {
+
+			key := markKey{ID: refIssue.ID, Action: ref.Action}
+			if refMarked[key] {
+				continue
+			}
+			refMarked[key] = true
+
+			// only create comments for issues if user has permission for it
+			if perm.IsAdmin() || perm.IsOwner() || perm.CanWrite(UnitTypeIssues) {
+				message := fmt.Sprintf(`<a href="%s/commit/%s">%s</a>`, repo.Link(), c.Sha1, html.EscapeString(c.Message))
+				if err = CreateRefComment(doer, refRepo, refIssue, message, c.Sha1); err != nil {
 					return err
 				}
 			}
-		}
 
-		// It is conflict to have close and reopen at same time, so refsMarked doesn't need to reinit here.
-		for _, m := range issueReopenKeywordsPat.FindAllStringSubmatch(c.Message, -1) {
-			if len(m[3]) == 0 {
+			// Process closing/reopening keywords
+			if ref.Action != references.XRefActionCloses && ref.Action != references.XRefActionReopens {
 				continue
 			}
-			ref := m[3]
 
-			// issue is from another repo
-			if len(m[1]) > 0 && len(m[2]) > 0 {
-				refRepo, err = GetRepositoryFromMatch(m[1], m[2])
-				if err != nil {
-					continue
-				}
-			} else {
-				refRepo = repo
+			// Change issue status only if the commit has been pushed to the default branch.
+			// and if the repo is configured to allow only that
+			// FIXME: we should be using Issue.ref if set instead of repo.DefaultBranch
+			if repo.DefaultBranch != branchName && !repo.CloseIssuesViaCommitInAnyBranch {
+				continue
 			}
 
-			perm, err := GetUserRepoPermission(refRepo, doer)
-			if err != nil {
-				return err
-			}
-
-			// only reopen issues in another repo if user has push access
-			if perm.CanWrite(UnitTypeCode) {
-				if err := changeIssueStatus(refRepo, doer, ref, refMarked, false); err != nil {
+			// only close issues in another repo if user has push access
+			if perm.IsAdmin() || perm.IsOwner() || perm.CanWrite(UnitTypeCode) {
+				if err := changeIssueStatus(refRepo, refIssue, doer, ref.Action == references.XRefActionCloses); err != nil {
 					return err
 				}
 			}
@@ -713,79 +609,6 @@ func MergePullRequestAction(actUser *User, repo *Repository, pull *Issue) error
 	return mergePullRequestAction(x, actUser, repo, pull)
 }
 
-func mirrorSyncAction(e Engine, opType ActionType, repo *Repository, refName string, data []byte) error {
-	if err := notifyWatchers(e, &Action{
-		ActUserID: repo.OwnerID,
-		ActUser:   repo.MustOwner(),
-		OpType:    opType,
-		RepoID:    repo.ID,
-		Repo:      repo,
-		IsPrivate: repo.IsPrivate,
-		RefName:   refName,
-		Content:   string(data),
-	}); err != nil {
-		return fmt.Errorf("notifyWatchers: %v", err)
-	}
-
-	defer func() {
-		go HookQueue.Add(repo.ID)
-	}()
-
-	return nil
-}
-
-// MirrorSyncPushActionOptions mirror synchronization action options.
-type MirrorSyncPushActionOptions struct {
-	RefName     string
-	OldCommitID string
-	NewCommitID string
-	Commits     *PushCommits
-}
-
-// MirrorSyncPushAction adds new action for mirror synchronization of pushed commits.
-func MirrorSyncPushAction(repo *Repository, opts MirrorSyncPushActionOptions) error {
-	if len(opts.Commits.Commits) > setting.UI.FeedMaxCommitNum {
-		opts.Commits.Commits = opts.Commits.Commits[:setting.UI.FeedMaxCommitNum]
-	}
-
-	apiCommits, err := opts.Commits.ToAPIPayloadCommits(repo.RepoPath(), repo.HTMLURL())
-	if err != nil {
-		return err
-	}
-
-	opts.Commits.CompareURL = repo.ComposeCompareURL(opts.OldCommitID, opts.NewCommitID)
-	apiPusher := repo.MustOwner().APIFormat()
-	if err := PrepareWebhooks(repo, HookEventPush, &api.PushPayload{
-		Ref:        opts.RefName,
-		Before:     opts.OldCommitID,
-		After:      opts.NewCommitID,
-		CompareURL: setting.AppURL + opts.Commits.CompareURL,
-		Commits:    apiCommits,
-		Repo:       repo.APIFormat(AccessModeOwner),
-		Pusher:     apiPusher,
-		Sender:     apiPusher,
-	}); err != nil {
-		return fmt.Errorf("PrepareWebhooks: %v", err)
-	}
-
-	data, err := json.Marshal(opts.Commits)
-	if err != nil {
-		return err
-	}
-
-	return mirrorSyncAction(x, ActionMirrorSyncPush, repo, opts.RefName, data)
-}
-
-// MirrorSyncCreateAction adds new action for mirror synchronization of new reference.
-func MirrorSyncCreateAction(repo *Repository, refName string) error {
-	return mirrorSyncAction(x, ActionMirrorSyncCreate, repo, refName, nil)
-}
-
-// MirrorSyncDeleteAction adds new action for mirror synchronization of delete reference.
-func MirrorSyncDeleteAction(repo *Repository, refName string) error {
-	return mirrorSyncAction(x, ActionMirrorSyncDelete, repo, refName, nil)
-}
-
 // GetFeedsOptions options for retrieving feeds
 type GetFeedsOptions struct {
 	RequestedUser    *User
diff --git a/models/action_test.go b/models/action_test.go
index c90538ebe6..df41556850 100644
--- a/models/action_test.go
+++ b/models/action_test.go
@@ -1,7 +1,6 @@
 package models
 
 import (
-	"fmt"
 	"path"
 	"strings"
 	"testing"
@@ -181,56 +180,6 @@ func TestPushCommits_AvatarLink(t *testing.T) {
 		pushCommits.AvatarLink("nonexistent@example.com"))
 }
 
-func TestRegExp_issueReferenceKeywordsPat(t *testing.T) {
-	trueTestCases := []string{
-		"#2",
-		"[#2]",
-		"please see go-gitea/gitea#5",
-		"#2:",
-	}
-	falseTestCases := []string{
-		"kb#2",
-		"#2xy",
-	}
-
-	for _, testCase := range trueTestCases {
-		assert.True(t, issueReferenceKeywordsPat.MatchString(testCase))
-	}
-	for _, testCase := range falseTestCases {
-		assert.False(t, issueReferenceKeywordsPat.MatchString(testCase))
-	}
-}
-
-func Test_getIssueFromRef(t *testing.T) {
-	assert.NoError(t, PrepareTestDatabase())
-	repo := AssertExistsAndLoadBean(t, &Repository{ID: 1}).(*Repository)
-	for _, test := range []struct {
-		Ref             string
-		ExpectedIssueID int64
-	}{
-		{"#2", 2},
-		{"reopen #2", 2},
-		{"user2/repo2#1", 4},
-		{"fixes user2/repo2#1", 4},
-		{"fixes: user2/repo2#1", 4},
-	} {
-		issue, err := getIssueFromRef(repo, test.Ref)
-		assert.NoError(t, err)
-		if assert.NotNil(t, issue) {
-			assert.EqualValues(t, test.ExpectedIssueID, issue.ID)
-		}
-	}
-
-	for _, badRef := range []string{
-		"doesnotexist/doesnotexist#1",
-		fmt.Sprintf("#%d", NonexistentID),
-	} {
-		issue, err := getIssueFromRef(repo, badRef)
-		assert.NoError(t, err)
-		assert.Nil(t, issue)
-	}
-}
-
 func TestUpdateIssuesCommit(t *testing.T) {
 	assert.NoError(t, PrepareTestDatabase())
 	pushCommits := []*PushCommit{
@@ -431,7 +380,7 @@ func TestUpdateIssuesCommit_AnotherRepoNoPermission(t *testing.T) {
 	AssertNotExistsBean(t, commentBean)
 	AssertNotExistsBean(t, issueBean, "is_closed=1")
 	assert.NoError(t, UpdateIssuesCommit(user, repo, pushCommits, repo.DefaultBranch))
-	AssertExistsAndLoadBean(t, commentBean)
+	AssertNotExistsBean(t, commentBean)
 	AssertNotExistsBean(t, issueBean, "is_closed=1")
 	CheckConsistencyFor(t, &Action{})
 }
diff --git a/models/attachment.go b/models/attachment.go
index a9032f1a86..f585bda8cb 100644
--- a/models/attachment.go
+++ b/models/attachment.go
@@ -14,8 +14,8 @@ import (
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/timeutil"
 
-	"github.com/go-xorm/xorm"
 	gouuid "github.com/satori/go.uuid"
+	"xorm.io/xorm"
 )
 
 // Attachment represent a attachment of issue/comment/release.
diff --git a/models/branches.go b/models/branches.go
index 9daaa487e7..c5f227f1e5 100644
--- a/models/branches.go
+++ b/models/branches.go
@@ -34,6 +34,7 @@ type ProtectedBranch struct {
 	WhitelistUserIDs          []int64            `xorm:"JSON TEXT"`
 	WhitelistTeamIDs          []int64            `xorm:"JSON TEXT"`
 	EnableMergeWhitelist      bool               `xorm:"NOT NULL DEFAULT false"`
+	WhitelistDeployKeys       bool               `xorm:"NOT NULL DEFAULT false"`
 	MergeWhitelistUserIDs     []int64            `xorm:"JSON TEXT"`
 	MergeWhitelistTeamIDs     []int64            `xorm:"JSON TEXT"`
 	EnableStatusCheck         bool               `xorm:"NOT NULL DEFAULT false"`
@@ -195,7 +196,7 @@ func UpdateProtectBranch(repo *Repository, protectBranch *ProtectedBranch, opts
 	}
 	protectBranch.MergeWhitelistUserIDs = whitelist
 
-	whitelist, err = updateUserWhitelist(repo, protectBranch.ApprovalsWhitelistUserIDs, opts.ApprovalsUserIDs)
+	whitelist, err = updateApprovalWhitelist(repo, protectBranch.ApprovalsWhitelistUserIDs, opts.ApprovalsUserIDs)
 	if err != nil {
 		return err
 	}
@@ -301,6 +302,27 @@ func (repo *Repository) IsProtectedBranchForMerging(pr *PullRequest, branchName
 	return false, nil
 }
 
+// updateApprovalWhitelist checks whether the user whitelist changed and returns a whitelist with
+// the users from newWhitelist which have explicit read or write access to the repo.
+func updateApprovalWhitelist(repo *Repository, currentWhitelist, newWhitelist []int64) (whitelist []int64, err error) {
+	hasUsersChanged := !util.IsSliceInt64Eq(currentWhitelist, newWhitelist)
+	if !hasUsersChanged {
+		return currentWhitelist, nil
+	}
+
+	whitelist = make([]int64, 0, len(newWhitelist))
+	for _, userID := range newWhitelist {
+		if reader, err := repo.IsReader(userID); err != nil {
+			return nil, err
+		} else if !reader {
+			continue
+		}
+		whitelist = append(whitelist, userID)
+	}
+
+	return
+}
+
 // updateUserWhitelist checks whether the user whitelist changed and returns a whitelist with
 // the users from newWhitelist which have write access to the repo.
 func updateUserWhitelist(repo *Repository, currentWhitelist, newWhitelist []int64) (whitelist []int64, err error) {
diff --git a/models/commit_status.go b/models/commit_status.go
index 6f6cbc387f..4e0f8166f3 100644
--- a/models/commit_status.go
+++ b/models/commit_status.go
@@ -16,7 +16,7 @@ import (
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/timeutil"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 // CommitStatusState holds the state of a Status
diff --git a/models/external_login_user.go b/models/external_login_user.go
index 21a3cbbd31..265d855ccf 100644
--- a/models/external_login_user.go
+++ b/models/external_login_user.go
@@ -4,13 +4,34 @@
 
 package models
 
-import "github.com/markbates/goth"
+import (
+	"time"
+
+	"code.gitea.io/gitea/modules/structs"
+
+	"github.com/markbates/goth"
+	"xorm.io/builder"
+)
 
 // ExternalLoginUser makes the connecting between some existing user and additional external login sources
 type ExternalLoginUser struct {
-	ExternalID    string `xorm:"pk NOT NULL"`
-	UserID        int64  `xorm:"INDEX NOT NULL"`
-	LoginSourceID int64  `xorm:"pk NOT NULL"`
+	ExternalID        string                 `xorm:"pk NOT NULL"`
+	UserID            int64                  `xorm:"INDEX NOT NULL"`
+	LoginSourceID     int64                  `xorm:"pk NOT NULL"`
+	RawData           map[string]interface{} `xorm:"TEXT JSON"`
+	Provider          string                 `xorm:"index VARCHAR(25)"`
+	Email             string
+	Name              string
+	FirstName         string
+	LastName          string
+	NickName          string
+	Description       string
+	AvatarURL         string
+	Location          string
+	AccessToken       string `xorm:"TEXT"`
+	AccessTokenSecret string `xorm:"TEXT"`
+	RefreshToken      string `xorm:"TEXT"`
+	ExpiresAt         time.Time
 }
 
 // GetExternalLogin checks if a externalID in loginSourceID scope already exists
@@ -32,23 +53,15 @@ func ListAccountLinks(user *User) ([]*ExternalLoginUser, error) {
 	return externalAccounts, nil
 }
 
-// LinkAccountToUser link the gothUser to the user
-func LinkAccountToUser(user *User, gothUser goth.User) error {
-	loginSource, err := GetActiveOAuth2LoginSourceByName(gothUser.Provider)
-	if err != nil {
-		return err
-	}
-
-	externalLoginUser := &ExternalLoginUser{
-		ExternalID:    gothUser.UserID,
-		UserID:        user.ID,
-		LoginSourceID: loginSource.ID,
-	}
-	has, err := x.Get(externalLoginUser)
+// LinkExternalToUser link the external user to the user
+func LinkExternalToUser(user *User, externalLoginUser *ExternalLoginUser) error {
+	has, err := x.Where("external_id=? AND login_source_id=?", externalLoginUser.ExternalID, externalLoginUser.LoginSourceID).
+		NoAutoCondition().
+		Exist(externalLoginUser)
 	if err != nil {
 		return err
 	} else if has {
-		return ErrExternalLoginUserAlreadyExist{gothUser.UserID, user.ID, loginSource.ID}
+		return ErrExternalLoginUserAlreadyExist{externalLoginUser.ExternalID, user.ID, externalLoginUser.LoginSourceID}
 	}
 
 	_, err = x.Insert(externalLoginUser)
@@ -72,3 +85,97 @@ func removeAllAccountLinks(e Engine, user *User) error {
 	_, err := e.Delete(&ExternalLoginUser{UserID: user.ID})
 	return err
 }
+
+// GetUserIDByExternalUserID get user id according to provider and userID
+func GetUserIDByExternalUserID(provider string, userID string) (int64, error) {
+	var id int64
+	_, err := x.Table("external_login_user").
+		Select("user_id").
+		Where("provider=?", provider).
+		And("external_id=?", userID).
+		Get(&id)
+	if err != nil {
+		return 0, err
+	}
+	return id, nil
+}
+
+// UpdateExternalUser updates external user's information
+func UpdateExternalUser(user *User, gothUser goth.User) error {
+	loginSource, err := GetActiveOAuth2LoginSourceByName(gothUser.Provider)
+	if err != nil {
+		return err
+	}
+	externalLoginUser := &ExternalLoginUser{
+		ExternalID:        gothUser.UserID,
+		UserID:            user.ID,
+		LoginSourceID:     loginSource.ID,
+		RawData:           gothUser.RawData,
+		Provider:          gothUser.Provider,
+		Email:             gothUser.Email,
+		Name:              gothUser.Name,
+		FirstName:         gothUser.FirstName,
+		LastName:          gothUser.LastName,
+		NickName:          gothUser.NickName,
+		Description:       gothUser.Description,
+		AvatarURL:         gothUser.AvatarURL,
+		Location:          gothUser.Location,
+		AccessToken:       gothUser.AccessToken,
+		AccessTokenSecret: gothUser.AccessTokenSecret,
+		RefreshToken:      gothUser.RefreshToken,
+		ExpiresAt:         gothUser.ExpiresAt,
+	}
+
+	has, err := x.Where("external_id=? AND login_source_id=?", gothUser.UserID, loginSource.ID).
+		NoAutoCondition().
+		Exist(externalLoginUser)
+	if err != nil {
+		return err
+	} else if !has {
+		return ErrExternalLoginUserNotExist{user.ID, loginSource.ID}
+	}
+
+	_, err = x.Where("external_id=? AND login_source_id=?", gothUser.UserID, loginSource.ID).AllCols().Update(externalLoginUser)
+	return err
+}
+
+// FindExternalUserOptions represents an options to find external users
+type FindExternalUserOptions struct {
+	Provider string
+	Limit    int
+	Start    int
+}
+
+func (opts FindExternalUserOptions) toConds() builder.Cond {
+	var cond = builder.NewCond()
+	if len(opts.Provider) > 0 {
+		cond = cond.And(builder.Eq{"provider": opts.Provider})
+	}
+	return cond
+}
+
+// FindExternalUsersByProvider represents external users via provider
+func FindExternalUsersByProvider(opts FindExternalUserOptions) ([]ExternalLoginUser, error) {
+	var users []ExternalLoginUser
+	err := x.Where(opts.toConds()).
+		Limit(opts.Limit, opts.Start).
+		OrderBy("login_source_id ASC, external_id ASC").
+		Find(&users)
+	if err != nil {
+		return nil, err
+	}
+	return users, nil
+}
+
+// UpdateMigrationsByType updates all migrated repositories' posterid from gitServiceType to replace originalAuthorID to posterID
+func UpdateMigrationsByType(tp structs.GitServiceType, externalUserID string, userID int64) error {
+	if err := UpdateIssuesMigrationsByType(tp, externalUserID, userID); err != nil {
+		return err
+	}
+
+	if err := UpdateCommentsMigrationsByType(tp, externalUserID, userID); err != nil {
+		return err
+	}
+
+	return UpdateReleasesMigrationsByType(tp, externalUserID, userID)
+}
diff --git a/models/fixtures/action.yml b/models/fixtures/action.yml
index 34a1a8b2be..e8a6d531f2 100644
--- a/models/fixtures/action.yml
+++ b/models/fixtures/action.yml
@@ -5,7 +5,7 @@
   act_user_id: 2
   repo_id: 2
   is_private: true
-  created_unix: 1540139562
+  created_unix: 1571686356
 
 -
   id: 2
diff --git a/models/fixtures/pull_request.yml b/models/fixtures/pull_request.yml
index baaaf6bb8a..505584ea18 100644
--- a/models/fixtures/pull_request.yml
+++ b/models/fixtures/pull_request.yml
@@ -6,7 +6,6 @@
   index: 2
   head_repo_id: 1
   base_repo_id: 1
-  head_user_name: user1
   head_branch: branch1
   base_branch: master
   merge_base: 1234567890abcdef
@@ -21,7 +20,6 @@
   index: 3
   head_repo_id: 1
   base_repo_id: 1
-  head_user_name: user1
   head_branch: branch2
   base_branch: master
   merge_base: fedcba9876543210
@@ -35,7 +33,6 @@
   index: 1
   head_repo_id: 11
   base_repo_id: 10
-  head_user_name: user13
   head_branch: branch2
   base_branch: master
   merge_base: 0abcb056019adb83
diff --git a/models/fixtures/repository.yml b/models/fixtures/repository.yml
index 2e38c5e1dd..cf7d24c6cd 100644
--- a/models/fixtures/repository.yml
+++ b/models/fixtures/repository.yml
@@ -11,6 +11,7 @@
   num_milestones: 3
   num_closed_milestones: 1
   num_watches: 3
+  status: 0
 
 -
   id: 2
@@ -24,6 +25,7 @@
   num_closed_pulls: 0
   num_stars: 1
   close_issues_via_commit_in_any_branch: true
+  status: 0
 
 -
   id: 3
@@ -36,6 +38,7 @@
   num_pulls: 0
   num_closed_pulls: 0
   num_watches: 0
+  status: 0
 
 -
   id: 4
@@ -48,6 +51,7 @@
   num_pulls: 0
   num_closed_pulls: 0
   num_stars: 1
+  status: 0
 
 -
   id: 5
@@ -61,6 +65,7 @@
   num_closed_pulls: 0
   num_watches: 0
   is_mirror: true
+  status: 0
 
 -
   id: 6
@@ -73,6 +78,7 @@
   num_pulls: 0
   num_closed_pulls: 0
   is_mirror: false
+  status: 0
 
 -
   id: 7
@@ -85,6 +91,7 @@
   num_pulls: 0
   num_closed_pulls: 0
   is_mirror: false
+  status: 0
 
 -
   id: 8
@@ -97,6 +104,7 @@
   num_pulls: 0
   num_closed_pulls: 0
   is_mirror: false
+  status: 0
 
 -
   id: 9
@@ -109,6 +117,7 @@
   num_pulls: 0
   num_closed_pulls: 0
   is_mirror: false
+  status: 0
 
 -
   id: 10
@@ -122,6 +131,7 @@
   num_closed_pulls: 0
   is_mirror: false
   num_forks: 1
+  status: 0
 
 -
   id: 11
@@ -135,6 +145,7 @@
   num_pulls: 0
   num_closed_pulls: 0
   is_mirror: false
+  status: 0
 
 -
   id: 12
@@ -147,6 +158,7 @@
   num_pulls: 0
   num_closed_pulls: 0
   is_mirror: false
+  status: 0
 
 -
   id: 13
@@ -159,6 +171,7 @@
   num_pulls: 0
   num_closed_pulls: 0
   is_mirror: false
+  status: 0
 
 -
   id: 14
@@ -172,6 +185,7 @@
   num_pulls: 0
   num_closed_pulls: 0
   is_mirror: false
+  status: 0
 
 -
   id: 15
@@ -179,6 +193,7 @@
   lower_name: repo15
   name: repo15
   is_empty: true
+  status: 0
 
 -
   id: 16
@@ -191,6 +206,7 @@
   num_pulls: 0
   num_closed_pulls: 0
   num_watches: 0
+  status: 0
 
 -
   id: 17
@@ -205,6 +221,7 @@
   num_watches: 0
   is_mirror: false
   is_fork: false
+  status: 0
 
 -
   id: 18
@@ -218,6 +235,7 @@
   num_closed_pulls: 0
   is_mirror: false
   is_fork: false
+  status: 0
 
 -
   id: 19
@@ -231,6 +249,7 @@
   num_closed_pulls: 0
   is_mirror: false
   is_fork: false
+  status: 0
 
 -
   id: 20
@@ -244,6 +263,7 @@
   num_closed_pulls: 0
   is_mirror: false
   is_fork: false
+  status: 0
 
 -
   id: 21
@@ -257,6 +277,7 @@
   num_closed_pulls: 0
   is_mirror: false
   is_fork: false
+  status: 0
 
 -
   id: 22
@@ -270,6 +291,7 @@
   num_closed_pulls: 0
   is_mirror: false
   is_fork: false
+  status: 0
 
 -
   id: 23
@@ -283,6 +305,7 @@
   num_closed_pulls: 0
   is_mirror: false
   is_fork: false
+  status: 0
 
 -
   id: 24
@@ -296,6 +319,7 @@
   num_closed_pulls: 0
   is_mirror: false
   is_fork: false
+  status: 0
 
 -
   id: 25
@@ -310,6 +334,7 @@
   num_watches: 0
   is_mirror: true
   is_fork: false
+  status: 0
 
 -
   id: 26
@@ -324,6 +349,7 @@
   num_watches: 0
   is_mirror: true
   is_fork: false
+  status: 0
 
 -
   id: 27
@@ -339,6 +365,7 @@
   is_mirror: true
   num_forks: 1
   is_fork: false
+  status: 0
 
 -
   id: 28
@@ -354,6 +381,7 @@
   is_mirror: true
   num_forks: 1
   is_fork: false
+  status: 0
 
 -
   id: 29
@@ -368,6 +396,7 @@
   num_closed_pulls: 0
   is_mirror: false
   is_fork: true
+  status: 0
 
 -
   id: 30
@@ -382,6 +411,7 @@
   num_closed_pulls: 0
   is_mirror: false
   is_fork: true
+  status: 0
 
 -
   id: 31
@@ -392,6 +422,7 @@
   num_forks: 0
   num_issues: 0
   is_mirror: false
+  status: 0
 
 -
   id: 32 # org public repo
@@ -403,6 +434,7 @@
   num_forks: 0
   num_issues: 0
   is_mirror: false
+  status: 0
 
 -
   id: 33
@@ -410,6 +442,7 @@
   lower_name: utf8
   name: utf8
   is_private: false
+  status: 0
 
 -
   id: 34
@@ -421,6 +454,7 @@
   num_forks: 0
   num_issues: 0
   is_mirror: false
+  status: 0
 
 -
   id: 35
@@ -432,6 +466,7 @@
   num_forks: 0
   num_issues: 0
   is_mirror: false
+  status: 0
 
 -
   id: 36
@@ -443,6 +478,7 @@
   num_forks: 0
   num_issues: 0
   is_mirror: false
+  status: 0
 
 -
   id: 37
@@ -454,6 +490,7 @@
   num_forks: 0
   num_issues: 0
   is_mirror: false
+  status: 0
 
 -
   id: 38
@@ -465,6 +502,7 @@
   num_forks: 0
   num_issues: 0
   is_mirror: false
+  status: 0
 
 -
   id: 39
@@ -476,6 +514,7 @@
   num_forks: 0
   num_issues: 0
   is_mirror: false
+  status: 0
 
 -
   id: 40
@@ -487,6 +526,7 @@
   num_forks: 0
   num_issues: 0
   is_mirror: false
+  status: 0
 
 -
   id: 41
@@ -519,4 +559,5 @@
   num_stars: 0
   num_forks: 0
   num_issues: 0
-  is_mirror: false
\ No newline at end of file
+  is_mirror: false
+  status: 0
diff --git a/models/gpg_key.go b/models/gpg_key.go
index 72c6891d4d..58eaa61e22 100644
--- a/models/gpg_key.go
+++ b/models/gpg_key.go
@@ -17,12 +17,13 @@ import (
 
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/timeutil"
 
-	"github.com/go-xorm/xorm"
 	"github.com/keybase/go-crypto/openpgp"
 	"github.com/keybase/go-crypto/openpgp/armor"
 	"github.com/keybase/go-crypto/openpgp/packet"
+	"xorm.io/xorm"
 )
 
 // GPGKey represents a GPG key.
@@ -80,6 +81,12 @@ func GetGPGKeyByID(keyID int64) (*GPGKey, error) {
 	return key, nil
 }
 
+// GetGPGKeysByKeyID returns public key by given ID.
+func GetGPGKeysByKeyID(keyID string) ([]*GPGKey, error) {
+	keys := make([]*GPGKey, 0, 1)
+	return keys, x.Where("key_id=?", keyID).Find(&keys)
+}
+
 // GetGPGImportByKeyID returns the import public armored key by given KeyID.
 func GetGPGImportByKeyID(keyID string) (*GPGKeyImport, error) {
 	key := new(GPGKeyImport)
@@ -355,10 +362,13 @@ func DeleteGPGKey(doer *User, id int64) (err error) {
 
 // CommitVerification represents a commit validation of signature
 type CommitVerification struct {
-	Verified    bool
-	Reason      string
-	SigningUser *User
-	SigningKey  *GPGKey
+	Verified       bool
+	Warning        bool
+	Reason         string
+	SigningUser    *User
+	CommittingUser *User
+	SigningEmail   string
+	SigningKey     *GPGKey
 }
 
 // SignCommit represents a commit with validation of signature.
@@ -367,6 +377,17 @@ type SignCommit struct {
 	*UserCommit
 }
 
+const (
+	// BadSignature is used as the reason when the signature has a KeyID that is in the db
+	// but no key that has that ID verifies the signature. This is a suspicious failure.
+	BadSignature = "gpg.error.probable_bad_signature"
+	// BadDefaultSignature is used as the reason when the signature has a KeyID that matches the
+	// default Key but is not verified by the default key. This is a suspicious failure.
+	BadDefaultSignature = "gpg.error.probable_bad_default_signature"
+	// NoKeyFound is used as the reason when no key can be found to verify the signature.
+	NoKeyFound = "gpg.error.no_gpg_keys_found"
+)
+
 func readerFromBase64(s string) (io.Reader, error) {
 	bs, err := base64.StdEncoding.DecodeString(s)
 	if err != nil {
@@ -424,49 +445,207 @@ func verifySign(s *packet.Signature, h hash.Hash, k *GPGKey) error {
 	return pkey.VerifySignature(h, s)
 }
 
-// ParseCommitWithSignature check if signature is good against keystore.
-func ParseCommitWithSignature(c *git.Commit) *CommitVerification {
-	if c.Signature != nil && c.Committer != nil {
-		//Parsing signature
-		sig, err := extractSignature(c.Signature.Signature)
-		if err != nil { //Skipping failed to extract sign
-			log.Error("SignatureRead err: %v", err)
-			return &CommitVerification{
-				Verified: false,
-				Reason:   "gpg.error.extract_sign",
+func hashAndVerify(sig *packet.Signature, payload string, k *GPGKey, committer, signer *User, email string) *CommitVerification {
+	//Generating hash of commit
+	hash, err := populateHash(sig.Hash, []byte(payload))
+	if err != nil { //Skipping failed to generate hash
+		log.Error("PopulateHash: %v", err)
+		return &CommitVerification{
+			CommittingUser: committer,
+			Verified:       false,
+			Reason:         "gpg.error.generate_hash",
+		}
+	}
+
+	if err := verifySign(sig, hash, k); err == nil {
+		return &CommitVerification{ //Everything is ok
+			CommittingUser: committer,
+			Verified:       true,
+			Reason:         fmt.Sprintf("%s <%s> / %s", signer.Name, signer.Email, k.KeyID),
+			SigningUser:    signer,
+			SigningKey:     k,
+			SigningEmail:   email,
+		}
+	}
+	return nil
+}
+
+func hashAndVerifyWithSubKeys(sig *packet.Signature, payload string, k *GPGKey, committer, signer *User, email string) *CommitVerification {
+	commitVerification := hashAndVerify(sig, payload, k, committer, signer, email)
+	if commitVerification != nil {
+		return commitVerification
+	}
+
+	//And test also SubsKey
+	for _, sk := range k.SubsKey {
+		commitVerification := hashAndVerify(sig, payload, sk, committer, signer, email)
+		if commitVerification != nil {
+			return commitVerification
+		}
+	}
+	return nil
+}
+
+func hashAndVerifyForKeyID(sig *packet.Signature, payload string, committer *User, keyID, name, email string) *CommitVerification {
+	if keyID == "" {
+		return nil
+	}
+	keys, err := GetGPGKeysByKeyID(keyID)
+	if err != nil {
+		log.Error("GetGPGKeysByKeyID: %v", err)
+		return &CommitVerification{
+			CommittingUser: committer,
+			Verified:       false,
+			Reason:         "gpg.error.failed_retrieval_gpg_keys",
+		}
+	}
+	if len(keys) == 0 {
+		return nil
+	}
+	for _, key := range keys {
+		activated := false
+		if len(email) != 0 {
+			for _, e := range key.Emails {
+				if e.IsActivated && strings.EqualFold(e.Email, email) {
+					activated = true
+					email = e.Email
+					break
+				}
+			}
+		} else {
+			for _, e := range key.Emails {
+				if e.IsActivated {
+					activated = true
+					email = e.Email
+					break
+				}
 			}
 		}
+		if !activated {
+			continue
+		}
+		signer := &User{
+			Name:  name,
+			Email: email,
+		}
+		if key.OwnerID != 0 {
+			owner, err := GetUserByID(key.OwnerID)
+			if err == nil {
+				signer = owner
+			} else if !IsErrUserNotExist(err) {
+				log.Error("Failed to GetUserByID: %d for key ID: %d (%s) %v", key.OwnerID, key.ID, key.KeyID, err)
+				return &CommitVerification{
+					CommittingUser: committer,
+					Verified:       false,
+					Reason:         "gpg.error.no_committer_account",
+				}
+			}
+		}
+		commitVerification := hashAndVerifyWithSubKeys(sig, payload, key, committer, signer, email)
+		if commitVerification != nil {
+			return commitVerification
+		}
+	}
+	// This is a bad situation ... We have a key id that is in our database but the signature doesn't match.
+	return &CommitVerification{
+		CommittingUser: committer,
+		Verified:       false,
+		Warning:        true,
+		Reason:         BadSignature,
+	}
+}
 
+// ParseCommitWithSignature check if signature is good against keystore.
+func ParseCommitWithSignature(c *git.Commit) *CommitVerification {
+	var committer *User
+	if c.Committer != nil {
+		var err error
 		//Find Committer account
-		committer, err := GetUserByEmail(c.Committer.Email) //This find the user by primary email or activated email so commit will not be valid if email is not
-		if err != nil {                                     //Skipping not user for commiter
+		committer, err = GetUserByEmail(c.Committer.Email) //This finds the user by primary email or activated email so commit will not be valid if email is not
+		if err != nil {                                    //Skipping not user for commiter
+			committer = &User{
+				Name:  c.Committer.Name,
+				Email: c.Committer.Email,
+			}
 			// We can expect this to often be an ErrUserNotExist. in the case
 			// it is not, however, it is important to log it.
 			if !IsErrUserNotExist(err) {
 				log.Error("GetUserByEmail: %v", err)
+				return &CommitVerification{
+					CommittingUser: committer,
+					Verified:       false,
+					Reason:         "gpg.error.no_committer_account",
+				}
 			}
-			return &CommitVerification{
-				Verified: false,
-				Reason:   "gpg.error.no_committer_account",
-			}
-		}
 
+		}
+	}
+
+	// If no signature just report the committer
+	if c.Signature == nil {
+		return &CommitVerification{
+			CommittingUser: committer,
+			Verified:       false,                         //Default value
+			Reason:         "gpg.error.not_signed_commit", //Default value
+		}
+	}
+
+	//Parsing signature
+	sig, err := extractSignature(c.Signature.Signature)
+	if err != nil { //Skipping failed to extract sign
+		log.Error("SignatureRead err: %v", err)
+		return &CommitVerification{
+			CommittingUser: committer,
+			Verified:       false,
+			Reason:         "gpg.error.extract_sign",
+		}
+	}
+
+	keyID := ""
+	if sig.IssuerKeyId != nil && (*sig.IssuerKeyId) != 0 {
+		keyID = fmt.Sprintf("%X", *sig.IssuerKeyId)
+	}
+	if keyID == "" && sig.IssuerFingerprint != nil && len(sig.IssuerFingerprint) > 0 {
+		keyID = fmt.Sprintf("%X", sig.IssuerFingerprint[12:20])
+	}
+
+	defaultReason := NoKeyFound
+
+	// First check if the sig has a keyID and if so just look at that
+	if commitVerification := hashAndVerifyForKeyID(
+		sig,
+		c.Signature.Payload,
+		committer,
+		keyID,
+		setting.AppName,
+		""); commitVerification != nil {
+		if commitVerification.Reason == BadSignature {
+			defaultReason = BadSignature
+		} else {
+			return commitVerification
+		}
+	}
+
+	// Now try to associate the signature with the committer, if present
+	if committer.ID != 0 {
 		keys, err := ListGPGKeys(committer.ID)
 		if err != nil { //Skipping failed to get gpg keys of user
 			log.Error("ListGPGKeys: %v", err)
 			return &CommitVerification{
-				Verified: false,
-				Reason:   "gpg.error.failed_retrieval_gpg_keys",
+				CommittingUser: committer,
+				Verified:       false,
+				Reason:         "gpg.error.failed_retrieval_gpg_keys",
 			}
 		}
 
 		for _, k := range keys {
 			//Pre-check (& optimization) that emails attached to key can be attached to the commiter email and can validate
 			canValidate := false
-			lowerCommiterEmail := strings.ToLower(c.Committer.Email)
+			email := ""
 			for _, e := range k.Emails {
-				if e.IsActivated && strings.ToLower(e.Email) == lowerCommiterEmail {
+				if e.IsActivated && strings.EqualFold(e.Email, c.Committer.Email) {
 					canValidate = true
+					email = e.Email
 					break
 				}
 			}
@@ -474,56 +653,104 @@ func ParseCommitWithSignature(c *git.Commit) *CommitVerification {
 				continue //Skip this key
 			}
 
-			//Generating hash of commit
-			hash, err := populateHash(sig.Hash, []byte(c.Signature.Payload))
-			if err != nil { //Skipping ailed to generate hash
-				log.Error("PopulateHash: %v", err)
-				return &CommitVerification{
-					Verified: false,
-					Reason:   "gpg.error.generate_hash",
-				}
+			commitVerification := hashAndVerifyWithSubKeys(sig, c.Signature.Payload, k, committer, committer, email)
+			if commitVerification != nil {
+				return commitVerification
 			}
-			//We get PK
-			if err := verifySign(sig, hash, k); err == nil {
-				return &CommitVerification{ //Everything is ok
-					Verified:    true,
-					Reason:      fmt.Sprintf("%s <%s> / %s", c.Committer.Name, c.Committer.Email, k.KeyID),
-					SigningUser: committer,
-					SigningKey:  k,
-				}
-			}
-			//And test also SubsKey
-			for _, sk := range k.SubsKey {
-
-				//Generating hash of commit
-				hash, err := populateHash(sig.Hash, []byte(c.Signature.Payload))
-				if err != nil { //Skipping ailed to generate hash
-					log.Error("PopulateHash: %v", err)
-					return &CommitVerification{
-						Verified: false,
-						Reason:   "gpg.error.generate_hash",
-					}
-				}
-				if err := verifySign(sig, hash, sk); err == nil {
-					return &CommitVerification{ //Everything is ok
-						Verified:    true,
-						Reason:      fmt.Sprintf("%s <%s> / %s", c.Committer.Name, c.Committer.Email, sk.KeyID),
-						SigningUser: committer,
-						SigningKey:  sk,
-					}
-				}
-			}
-		}
-		return &CommitVerification{ //Default at this stage
-			Verified: false,
-			Reason:   "gpg.error.no_gpg_keys_found",
 		}
 	}
 
-	return &CommitVerification{
-		Verified: false,                         //Default value
-		Reason:   "gpg.error.not_signed_commit", //Default value
+	if setting.Repository.Signing.SigningKey != "" && setting.Repository.Signing.SigningKey != "default" && setting.Repository.Signing.SigningKey != "none" {
+		// OK we should try the default key
+		gpgSettings := git.GPGSettings{
+			Sign:  true,
+			KeyID: setting.Repository.Signing.SigningKey,
+			Name:  setting.Repository.Signing.SigningName,
+			Email: setting.Repository.Signing.SigningEmail,
+		}
+		if err := gpgSettings.LoadPublicKeyContent(); err != nil {
+			log.Error("Error getting default signing key: %s %v", gpgSettings.KeyID, err)
+		} else if commitVerification := verifyWithGPGSettings(&gpgSettings, sig, c.Signature.Payload, committer, keyID); commitVerification != nil {
+			if commitVerification.Reason == BadSignature {
+				defaultReason = BadSignature
+			} else {
+				return commitVerification
+			}
+		}
 	}
+
+	defaultGPGSettings, err := c.GetRepositoryDefaultPublicGPGKey(false)
+	if err != nil {
+		log.Error("Error getting default public gpg key: %v", err)
+	} else if defaultGPGSettings == nil {
+		log.Warn("Unable to get defaultGPGSettings for unattached commit: %s", c.ID.String())
+	} else if defaultGPGSettings.Sign {
+		if commitVerification := verifyWithGPGSettings(defaultGPGSettings, sig, c.Signature.Payload, committer, keyID); commitVerification != nil {
+			if commitVerification.Reason == BadSignature {
+				defaultReason = BadSignature
+			} else {
+				return commitVerification
+			}
+		}
+	}
+
+	return &CommitVerification{ //Default at this stage
+		CommittingUser: committer,
+		Verified:       false,
+		Warning:        defaultReason != NoKeyFound,
+		Reason:         defaultReason,
+		SigningKey: &GPGKey{
+			KeyID: keyID,
+		},
+	}
+}
+
+func verifyWithGPGSettings(gpgSettings *git.GPGSettings, sig *packet.Signature, payload string, committer *User, keyID string) *CommitVerification {
+	// First try to find the key in the db
+	if commitVerification := hashAndVerifyForKeyID(sig, payload, committer, gpgSettings.KeyID, gpgSettings.Name, gpgSettings.Email); commitVerification != nil {
+		return commitVerification
+	}
+
+	// Otherwise we have to parse the key
+	ekey, err := checkArmoredGPGKeyString(gpgSettings.PublicKeyContent)
+	if err != nil {
+		log.Error("Unable to get default signing key: %v", err)
+		return &CommitVerification{
+			CommittingUser: committer,
+			Verified:       false,
+			Reason:         "gpg.error.generate_hash",
+		}
+	}
+	pubkey := ekey.PrimaryKey
+	content, err := base64EncPubKey(pubkey)
+	if err != nil {
+		return &CommitVerification{
+			CommittingUser: committer,
+			Verified:       false,
+			Reason:         "gpg.error.generate_hash",
+		}
+	}
+	k := &GPGKey{
+		Content: content,
+		CanSign: pubkey.CanSign(),
+		KeyID:   pubkey.KeyIdString(),
+	}
+	if commitVerification := hashAndVerifyWithSubKeys(sig, payload, k, committer, &User{
+		Name:  gpgSettings.Name,
+		Email: gpgSettings.Email,
+	}, gpgSettings.Email); commitVerification != nil {
+		return commitVerification
+	}
+	if keyID == k.KeyID {
+		// This is a bad situation ... We have a key id that matches our default key but the signature doesn't match.
+		return &CommitVerification{
+			CommittingUser: committer,
+			Verified:       false,
+			Warning:        true,
+			Reason:         BadSignature,
+		}
+	}
+	return nil
 }
 
 // ParseCommitsWithSignature checks if signaute of commits are corresponding to users gpg keys.
diff --git a/models/graph.go b/models/graph.go
index 5f68abaf74..0efb51b3fc 100644
--- a/models/graph.go
+++ b/models/graph.go
@@ -30,7 +30,7 @@ type GraphItem struct {
 type GraphItems []GraphItem
 
 // GetCommitGraph return a list of commit (GraphItems) from all branches
-func GetCommitGraph(r *git.Repository) (GraphItems, error) {
+func GetCommitGraph(r *git.Repository, page int) (GraphItems, error) {
 
 	var CommitGraph []GraphItem
 
@@ -43,6 +43,7 @@ func GetCommitGraph(r *git.Repository) (GraphItems, error) {
 		"-C",
 		"-M",
 		fmt.Sprintf("-n %d", setting.UI.GraphMaxCommitNum),
+		fmt.Sprintf("--skip=%d", setting.UI.GraphMaxCommitNum*(page-1)),
 		"--date=iso",
 		fmt.Sprintf("--pretty=format:%s", format),
 	)
diff --git a/models/graph_test.go b/models/graph_test.go
index 5c78e3877b..c1f0bc90d9 100644
--- a/models/graph_test.go
+++ b/models/graph_test.go
@@ -19,7 +19,7 @@ func BenchmarkGetCommitGraph(b *testing.B) {
 	}
 
 	for i := 0; i < b.N; i++ {
-		graph, err := GetCommitGraph(currentRepo)
+		graph, err := GetCommitGraph(currentRepo, 1)
 		if err != nil {
 			b.Error("Could get commit graph")
 		}
diff --git a/models/issue.go b/models/issue.go
index 9590bc04ff..0315580c31 100644
--- a/models/issue.go
+++ b/models/issue.go
@@ -14,13 +14,14 @@ import (
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/structs"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/timeutil"
 	"code.gitea.io/gitea/modules/util"
 
-	"github.com/go-xorm/xorm"
 	"github.com/unknwon/com"
 	"xorm.io/builder"
+	"xorm.io/xorm"
 )
 
 // Issue represents an issue or pull request of repository.
@@ -32,7 +33,7 @@ type Issue struct {
 	PosterID         int64       `xorm:"INDEX"`
 	Poster           *User       `xorm:"-"`
 	OriginalAuthor   string
-	OriginalAuthorID int64
+	OriginalAuthorID int64      `xorm:"index"`
 	Title            string     `xorm:"name"`
 	Content          string     `xorm:"TEXT"`
 	RenderedContent  string     `xorm:"-"`
@@ -427,52 +428,6 @@ func (issue *Issue) HasLabel(labelID int64) bool {
 	return issue.hasLabel(x, labelID)
 }
 
-func (issue *Issue) sendLabelUpdatedWebhook(doer *User) {
-	var err error
-
-	if err = issue.loadRepo(x); err != nil {
-		log.Error("loadRepo: %v", err)
-		return
-	}
-
-	if err = issue.loadPoster(x); err != nil {
-		log.Error("loadPoster: %v", err)
-		return
-	}
-
-	mode, _ := AccessLevel(issue.Poster, issue.Repo)
-	if issue.IsPull {
-		if err = issue.loadPullRequest(x); err != nil {
-			log.Error("loadPullRequest: %v", err)
-			return
-		}
-		if err = issue.PullRequest.LoadIssue(); err != nil {
-			log.Error("LoadIssue: %v", err)
-			return
-		}
-		err = PrepareWebhooks(issue.Repo, HookEventPullRequest, &api.PullRequestPayload{
-			Action:      api.HookIssueLabelUpdated,
-			Index:       issue.Index,
-			PullRequest: issue.PullRequest.APIFormat(),
-			Repository:  issue.Repo.APIFormat(AccessModeNone),
-			Sender:      doer.APIFormat(),
-		})
-	} else {
-		err = PrepareWebhooks(issue.Repo, HookEventIssues, &api.IssuePayload{
-			Action:     api.HookIssueLabelUpdated,
-			Index:      issue.Index,
-			Issue:      issue.APIFormat(),
-			Repository: issue.Repo.APIFormat(mode),
-			Sender:     doer.APIFormat(),
-		})
-	}
-	if err != nil {
-		log.Error("PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err)
-	} else {
-		go HookQueue.Add(issue.RepoID)
-	}
-}
-
 // ReplyReference returns tokenized address to use for email reply headers
 func (issue *Issue) ReplyReference() string {
 	var path string
@@ -489,30 +444,10 @@ func (issue *Issue) addLabel(e *xorm.Session, label *Label, doer *User) error {
 	return newIssueLabel(e, issue, label, doer)
 }
 
-// AddLabel adds a new label to the issue.
-func (issue *Issue) AddLabel(doer *User, label *Label) error {
-	if err := NewIssueLabel(issue, label, doer); err != nil {
-		return err
-	}
-
-	issue.sendLabelUpdatedWebhook(doer)
-	return nil
-}
-
 func (issue *Issue) addLabels(e *xorm.Session, labels []*Label, doer *User) error {
 	return newIssueLabels(e, issue, labels, doer)
 }
 
-// AddLabels adds a list of new labels to the issue.
-func (issue *Issue) AddLabels(doer *User, labels []*Label) error {
-	if err := NewIssueLabels(issue, labels, doer); err != nil {
-		return err
-	}
-
-	issue.sendLabelUpdatedWebhook(doer)
-	return nil
-}
-
 func (issue *Issue) getLabels(e Engine) (err error) {
 	if len(issue.Labels) > 0 {
 		return nil
@@ -529,28 +464,6 @@ func (issue *Issue) removeLabel(e *xorm.Session, doer *User, label *Label) error
 	return deleteIssueLabel(e, issue, label, doer)
 }
 
-// RemoveLabel removes a label from issue by given ID.
-func (issue *Issue) RemoveLabel(doer *User, label *Label) error {
-	if err := issue.loadRepo(x); err != nil {
-		return err
-	}
-
-	perm, err := GetUserRepoPermission(issue.Repo, doer)
-	if err != nil {
-		return err
-	}
-	if !perm.CanWriteIssuesOrPulls(issue.IsPull) {
-		return ErrLabelNotExist{}
-	}
-
-	if err := DeleteIssueLabel(issue, label, doer); err != nil {
-		return err
-	}
-
-	issue.sendLabelUpdatedWebhook(doer)
-	return nil
-}
-
 func (issue *Issue) clearLabels(e *xorm.Session, doer *User) (err error) {
 	if err = issue.getLabels(e); err != nil {
 		return fmt.Errorf("getLabels: %v", err)
@@ -595,40 +508,6 @@ func (issue *Issue) ClearLabels(doer *User) (err error) {
 	if err = sess.Commit(); err != nil {
 		return fmt.Errorf("Commit: %v", err)
 	}
-	sess.Close()
-
-	if err = issue.LoadPoster(); err != nil {
-		return fmt.Errorf("loadPoster: %v", err)
-	}
-
-	mode, _ := AccessLevel(issue.Poster, issue.Repo)
-	if issue.IsPull {
-		err = issue.PullRequest.LoadIssue()
-		if err != nil {
-			log.Error("LoadIssue: %v", err)
-			return
-		}
-		err = PrepareWebhooks(issue.Repo, HookEventPullRequest, &api.PullRequestPayload{
-			Action:      api.HookIssueLabelCleared,
-			Index:       issue.Index,
-			PullRequest: issue.PullRequest.APIFormat(),
-			Repository:  issue.Repo.APIFormat(mode),
-			Sender:      doer.APIFormat(),
-		})
-	} else {
-		err = PrepareWebhooks(issue.Repo, HookEventIssues, &api.IssuePayload{
-			Action:     api.HookIssueLabelCleared,
-			Index:      issue.Index,
-			Issue:      issue.APIFormat(),
-			Repository: issue.Repo.APIFormat(mode),
-			Sender:     doer.APIFormat(),
-		})
-	}
-	if err != nil {
-		log.Error("PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err)
-	} else {
-		go HookQueue.Add(issue.RepoID)
-	}
 
 	return nil
 }
@@ -714,11 +593,6 @@ func updateIssueCols(e Engine, issue *Issue, cols ...string) error {
 	return nil
 }
 
-// UpdateIssueCols only updates values of specific columns for given issue.
-func UpdateIssueCols(issue *Issue, cols ...string) error {
-	return updateIssueCols(x, issue, cols...)
-}
-
 func (issue *Issue) changeStatus(e *xorm.Session, doer *User, isClosed bool) (err error) {
 	// Reload the issue
 	currentIssue, err := getIssueByID(e, issue.ID)
@@ -766,7 +640,7 @@ func (issue *Issue) changeStatus(e *xorm.Session, doer *User, isClosed bool) (er
 	}
 
 	// Update issue count of milestone
-	if err = changeMilestoneIssueStats(e, issue); err != nil {
+	if err := updateMilestoneClosedNum(e, issue.MilestoneID); err != nil {
 		return err
 	}
 
@@ -844,9 +718,7 @@ func (issue *Issue) ChangeStatus(doer *User, isClosed bool) (err error) {
 }
 
 // ChangeTitle changes the title of this issue, as the given user.
-func (issue *Issue) ChangeTitle(doer *User, title string) (err error) {
-	oldTitle := issue.Title
-	issue.Title = title
+func (issue *Issue) ChangeTitle(doer *User, oldTitle string) (err error) {
 	sess := x.NewSession()
 	defer sess.Close()
 
@@ -862,7 +734,7 @@ func (issue *Issue) ChangeTitle(doer *User, title string) (err error) {
 		return fmt.Errorf("loadRepo: %v", err)
 	}
 
-	if _, err = createChangeTitleComment(sess, doer, issue.Repo, issue, oldTitle, title); err != nil {
+	if _, err = createChangeTitleComment(sess, doer, issue.Repo, issue, oldTitle, issue.Title); err != nil {
 		return fmt.Errorf("createChangeTitleComment: %v", err)
 	}
 
@@ -874,51 +746,7 @@ func (issue *Issue) ChangeTitle(doer *User, title string) (err error) {
 		return err
 	}
 
-	if err = sess.Commit(); err != nil {
-		return err
-	}
-	sess.Close()
-
-	mode, _ := AccessLevel(issue.Poster, issue.Repo)
-	if issue.IsPull {
-		if err = issue.loadPullRequest(sess); err != nil {
-			return fmt.Errorf("loadPullRequest: %v", err)
-		}
-		issue.PullRequest.Issue = issue
-		err = PrepareWebhooks(issue.Repo, HookEventPullRequest, &api.PullRequestPayload{
-			Action: api.HookIssueEdited,
-			Index:  issue.Index,
-			Changes: &api.ChangesPayload{
-				Title: &api.ChangesFromPayload{
-					From: oldTitle,
-				},
-			},
-			PullRequest: issue.PullRequest.APIFormat(),
-			Repository:  issue.Repo.APIFormat(mode),
-			Sender:      doer.APIFormat(),
-		})
-	} else {
-		err = PrepareWebhooks(issue.Repo, HookEventIssues, &api.IssuePayload{
-			Action: api.HookIssueEdited,
-			Index:  issue.Index,
-			Changes: &api.ChangesPayload{
-				Title: &api.ChangesFromPayload{
-					From: oldTitle,
-				},
-			},
-			Issue:      issue.APIFormat(),
-			Repository: issue.Repo.APIFormat(mode),
-			Sender:     issue.Poster.APIFormat(),
-		})
-	}
-
-	if err != nil {
-		log.Error("PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err)
-	} else {
-		go HookQueue.Add(issue.RepoID)
-	}
-
-	return nil
+	return sess.Commit()
 }
 
 // AddDeletePRBranchComment adds delete branch comment for pull request issue
@@ -939,6 +767,26 @@ func AddDeletePRBranchComment(doer *User, repo *Repository, issueID int64, branc
 	return sess.Commit()
 }
 
+// UpdateAttachments update attachments by UUIDs for the issue
+func (issue *Issue) UpdateAttachments(uuids []string) (err error) {
+	sess := x.NewSession()
+	defer sess.Close()
+	if err = sess.Begin(); err != nil {
+		return err
+	}
+	attachments, err := getAttachmentsByUUIDs(sess, uuids)
+	if err != nil {
+		return fmt.Errorf("getAttachmentsByUUIDs [uuids: %v]: %v", uuids, err)
+	}
+	for i := 0; i < len(attachments); i++ {
+		attachments[i].IssueID = issue.ID
+		if err := updateAttachment(sess, attachments[i]); err != nil {
+			return fmt.Errorf("update attachment [id: %d]: %v", attachments[i].ID, err)
+		}
+	}
+	return sess.Commit()
+}
+
 // ChangeContent changes issue content, as the given user.
 func (issue *Issue) ChangeContent(doer *User, content string) (err error) {
 	oldContent := issue.Content
@@ -1048,7 +896,6 @@ type NewIssueOptions struct {
 	Repo        *Repository
 	Issue       *Issue
 	LabelIDs    []int64
-	AssigneeIDs []int64
 	Attachments []string // In UUID format.
 	IsPull      bool
 }
@@ -1070,40 +917,7 @@ func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) {
 		}
 	}
 
-	// Keep the old assignee id thingy for compatibility reasons
-	if opts.Issue.AssigneeID > 0 {
-		isAdded := false
-		// Check if the user has already been passed to issue.AssigneeIDs, if not, add it
-		for _, aID := range opts.AssigneeIDs {
-			if aID == opts.Issue.AssigneeID {
-				isAdded = true
-				break
-			}
-		}
-
-		if !isAdded {
-			opts.AssigneeIDs = append(opts.AssigneeIDs, opts.Issue.AssigneeID)
-		}
-	}
-
-	// Check for and validate assignees
-	if len(opts.AssigneeIDs) > 0 {
-		for _, assigneeID := range opts.AssigneeIDs {
-			user, err := getUserByID(e, assigneeID)
-			if err != nil {
-				return fmt.Errorf("getUserByID [user_id: %d, repo_id: %d]: %v", assigneeID, opts.Repo.ID, err)
-			}
-			valid, err := canBeAssigned(e, user, opts.Repo)
-			if err != nil {
-				return fmt.Errorf("canBeAssigned [user_id: %d, repo_id: %d]: %v", assigneeID, opts.Repo.ID, err)
-			}
-			if !valid {
-				return ErrUserDoesNotHaveAccessToRepo{UserID: assigneeID, RepoName: opts.Repo.Name}
-			}
-		}
-	}
-
-	// Milestone and assignee validation should happen before insert actual object.
+	// Milestone validation should happen before insert actual object.
 	if _, err := e.SetExpr("`index`", "coalesce(MAX(`index`),0)+1").
 		Where("repo_id=?", opts.Issue.RepoID).
 		Insert(opts.Issue); err != nil {
@@ -1119,15 +933,11 @@ func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) {
 	opts.Issue.Index = inserted.Index
 
 	if opts.Issue.MilestoneID > 0 {
-		if err = changeMilestoneAssign(e, doer, opts.Issue, -1); err != nil {
+		if _, err = e.Exec("UPDATE `milestone` SET num_issues=num_issues+1 WHERE id=?", opts.Issue.MilestoneID); err != nil {
 			return err
 		}
-	}
 
-	// Insert the assignees
-	for _, assigneeID := range opts.AssigneeIDs {
-		err = opts.Issue.changeAssignee(e, doer, assigneeID, true)
-		if err != nil {
+		if _, err = createMilestoneComment(e, doer, opts.Repo, opts.Issue, 0, opts.Issue.MilestoneID); err != nil {
 			return err
 		}
 	}
@@ -1189,11 +999,11 @@ func newIssue(e *xorm.Session, doer *User, opts NewIssueOptions) (err error) {
 }
 
 // NewIssue creates new issue with labels for repository.
-func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, assigneeIDs []int64, uuids []string) (err error) {
+func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string) (err error) {
 	// Retry several times in case INSERT fails due to duplicate key for (repo_id, index); see #7887
 	i := 0
 	for {
-		if err = newIssueAttempt(repo, issue, labelIDs, assigneeIDs, uuids); err == nil {
+		if err = newIssueAttempt(repo, issue, labelIDs, uuids); err == nil {
 			return nil
 		}
 		if !IsErrNewIssueInsert(err) {
@@ -1207,7 +1017,7 @@ func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, assigneeIDs []in
 	return fmt.Errorf("NewIssue: too many errors attempting to insert the new issue. Last error was: %v", err)
 }
 
-func newIssueAttempt(repo *Repository, issue *Issue, labelIDs []int64, assigneeIDs []int64, uuids []string) (err error) {
+func newIssueAttempt(repo *Repository, issue *Issue, labelIDs []int64, uuids []string) (err error) {
 	sess := x.NewSession()
 	defer sess.Close()
 	if err = sess.Begin(); err != nil {
@@ -1219,7 +1029,6 @@ func newIssueAttempt(repo *Repository, issue *Issue, labelIDs []int64, assigneeI
 		Issue:       issue,
 		LabelIDs:    labelIDs,
 		Attachments: uuids,
-		AssigneeIDs: assigneeIDs,
 	}); err != nil {
 		if IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) {
 			return err
@@ -1400,8 +1209,12 @@ func (opts *IssuesOptions) setupSession(sess *xorm.Session) {
 
 	if opts.LabelIDs != nil {
 		for i, labelID := range opts.LabelIDs {
-			sess.Join("INNER", fmt.Sprintf("issue_label il%d", i),
-				fmt.Sprintf("issue.id = il%[1]d.issue_id AND il%[1]d.label_id = %[2]d", i, labelID))
+			if labelID > 0 {
+				sess.Join("INNER", fmt.Sprintf("issue_label il%d", i),
+					fmt.Sprintf("issue.id = il%[1]d.issue_id AND il%[1]d.label_id = %[2]d", i, labelID))
+			} else {
+				sess.Where("issue.id not in (select issue_id from issue_label where label_id = ?)", -labelID)
+			}
 		}
 	}
 }
@@ -1477,46 +1290,18 @@ func getParticipantsByIssueID(e Engine, issueID int64) ([]*User, error) {
 	return users, e.In("id", userIDs).Find(&users)
 }
 
-// UpdateIssueMentions extracts mentioned people from content and
-// updates issue-user relations for them.
-func UpdateIssueMentions(ctx DBContext, issueID int64, mentions []string) error {
+// UpdateIssueMentions updates issue-user relations for mentioned users.
+func UpdateIssueMentions(ctx DBContext, issueID int64, mentions []*User) error {
 	if len(mentions) == 0 {
 		return nil
 	}
-
-	for i := range mentions {
-		mentions[i] = strings.ToLower(mentions[i])
+	ids := make([]int64, len(mentions))
+	for i, u := range mentions {
+		ids[i] = u.ID
 	}
-	users := make([]*User, 0, len(mentions))
-
-	if err := ctx.e.In("lower_name", mentions).Asc("lower_name").Find(&users); err != nil {
-		return fmt.Errorf("find mentioned users: %v", err)
-	}
-
-	ids := make([]int64, 0, len(mentions))
-	for _, user := range users {
-		ids = append(ids, user.ID)
-		if !user.IsOrganization() || user.NumMembers == 0 {
-			continue
-		}
-
-		memberIDs := make([]int64, 0, user.NumMembers)
-		orgUsers, err := getOrgUsersByOrgID(ctx.e, user.ID)
-		if err != nil {
-			return fmt.Errorf("GetOrgUsersByOrgID [%d]: %v", user.ID, err)
-		}
-
-		for _, orgUser := range orgUsers {
-			memberIDs = append(memberIDs, orgUser.ID)
-		}
-
-		ids = append(ids, memberIDs...)
-	}
-
 	if err := UpdateIssueUsersByMentions(ctx, issueID, ids); err != nil {
 		return fmt.Errorf("UpdateIssueUsersByMentions: %v", err)
 	}
-
 	return nil
 }
 
@@ -1909,3 +1694,133 @@ func (issue *Issue) updateClosedNum(e Engine) (err error) {
 	}
 	return
 }
+
+// ResolveMentionsByVisibility returns the users mentioned in an issue, removing those that
+// don't have access to reading it. Teams are expanded into their users, but organizations are ignored.
+func (issue *Issue) ResolveMentionsByVisibility(ctx DBContext, doer *User, mentions []string) (users []*User, err error) {
+	if len(mentions) == 0 {
+		return
+	}
+	if err = issue.loadRepo(ctx.e); err != nil {
+		return
+	}
+	resolved := make(map[string]bool, 20)
+	names := make([]string, 0, 20)
+	resolved[doer.LowerName] = true
+	for _, name := range mentions {
+		name := strings.ToLower(name)
+		if _, ok := resolved[name]; ok {
+			continue
+		}
+		resolved[name] = false
+		names = append(names, name)
+	}
+
+	if err := issue.Repo.getOwner(ctx.e); err != nil {
+		return nil, err
+	}
+
+	if issue.Repo.Owner.IsOrganization() {
+		// Since there can be users with names that match the name of a team,
+		// if the team exists and can read the issue, the team takes precedence.
+		teams := make([]*Team, 0, len(names))
+		if err := ctx.e.
+			Join("INNER", "team_repo", "team_repo.team_id = team.id").
+			Where("team_repo.repo_id=?", issue.Repo.ID).
+			In("team.lower_name", names).
+			Find(&teams); err != nil {
+			return nil, fmt.Errorf("find mentioned teams: %v", err)
+		}
+		if len(teams) != 0 {
+			checked := make([]int64, 0, len(teams))
+			unittype := UnitTypeIssues
+			if issue.IsPull {
+				unittype = UnitTypePullRequests
+			}
+			for _, team := range teams {
+				if team.Authorize >= AccessModeOwner {
+					checked = append(checked, team.ID)
+					resolved[team.LowerName] = true
+					continue
+				}
+				has, err := ctx.e.Get(&TeamUnit{OrgID: issue.Repo.Owner.ID, TeamID: team.ID, Type: unittype})
+				if err != nil {
+					return nil, fmt.Errorf("get team units (%d): %v", team.ID, err)
+				}
+				if has {
+					checked = append(checked, team.ID)
+					resolved[team.LowerName] = true
+				}
+			}
+			if len(checked) != 0 {
+				teamusers := make([]*User, 0, 20)
+				if err := ctx.e.
+					Join("INNER", "team_user", "team_user.uid = `user`.id").
+					In("`team_user`.team_id", checked).
+					And("`user`.is_active = ?", true).
+					And("`user`.prohibit_login = ?", false).
+					Find(&teamusers); err != nil {
+					return nil, fmt.Errorf("get teams users: %v", err)
+				}
+				if len(teamusers) > 0 {
+					users = make([]*User, 0, len(teamusers))
+					for _, user := range teamusers {
+						if already, ok := resolved[user.LowerName]; !ok || !already {
+							users = append(users, user)
+							resolved[user.LowerName] = true
+						}
+					}
+				}
+			}
+		}
+
+		// Remove names already in the list to avoid querying the database if pending names remain
+		names = make([]string, 0, len(resolved))
+		for name, already := range resolved {
+			if !already {
+				names = append(names, name)
+			}
+		}
+		if len(names) == 0 {
+			return
+		}
+	}
+
+	unchecked := make([]*User, 0, len(names))
+	if err := ctx.e.
+		Where("`user`.is_active = ?", true).
+		And("`user`.prohibit_login = ?", false).
+		In("`user`.lower_name", names).
+		Find(&unchecked); err != nil {
+		return nil, fmt.Errorf("find mentioned users: %v", err)
+	}
+	for _, user := range unchecked {
+		if already := resolved[user.LowerName]; already || user.IsOrganization() {
+			continue
+		}
+		// Normal users must have read access to the referencing issue
+		perm, err := getUserRepoPermission(ctx.e, issue.Repo, user)
+		if err != nil {
+			return nil, fmt.Errorf("getUserRepoPermission [%d]: %v", user.ID, err)
+		}
+		if !perm.CanReadIssuesOrPulls(issue.IsPull) {
+			continue
+		}
+		users = append(users, user)
+	}
+
+	return
+}
+
+// UpdateIssuesMigrationsByType updates all migrated repositories' issues from gitServiceType to replace originalAuthorID to posterID
+func UpdateIssuesMigrationsByType(gitServiceType structs.GitServiceType, originalAuthorID string, posterID int64) error {
+	_, err := x.Table("issue").
+		Where("repo_id IN (SELECT id FROM repository WHERE original_service_type = ?)", gitServiceType).
+		And("original_author_id = ?", originalAuthorID).
+		Update(map[string]interface{}{
+			"poster_id":          posterID,
+			"original_author":    "",
+			"original_author_id": 0,
+		})
+	return err
+}
diff --git a/models/issue_assignees.go b/models/issue_assignees.go
index 1f504a9950..ed0576b38b 100644
--- a/models/issue_assignees.go
+++ b/models/issue_assignees.go
@@ -10,7 +10,7 @@ import (
 	"code.gitea.io/gitea/modules/log"
 	api "code.gitea.io/gitea/modules/structs"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 // IssueAssignees saves all issue assignees
@@ -58,8 +58,11 @@ func getAssigneesByIssue(e Engine, issue *Issue) (assignees []*User, err error)
 
 // IsUserAssignedToIssue returns true when the user is assigned to the issue
 func IsUserAssignedToIssue(issue *Issue, user *User) (isAssigned bool, err error) {
-	isAssigned, err = x.Exist(&IssueAssignees{IssueID: issue.ID, AssigneeID: user.ID})
-	return
+	return isUserAssignedToIssue(x, issue, user)
+}
+
+func isUserAssignedToIssue(e Engine, issue *Issue, user *User) (isAssigned bool, err error) {
+	return e.Get(&IssueAssignees{IssueID: issue.ID, AssigneeID: user.ID})
 }
 
 // DeleteNotPassedAssignee deletes all assignees who aren't passed via the "assignees" array
@@ -78,7 +81,7 @@ func DeleteNotPassedAssignee(issue *Issue, doer *User, assignees []*User) (err e
 
 		if !found {
 			// This function also does comments and hooks, which is why we call it seperatly instead of directly removing the assignees here
-			if err := UpdateAssignee(issue, doer, assignee.ID); err != nil {
+			if _, _, err := issue.ToggleAssignee(doer, assignee.ID); err != nil {
 				return err
 			}
 		}
@@ -110,73 +113,56 @@ func clearAssigneeByUserID(sess *xorm.Session, userID int64) (err error) {
 	return
 }
 
-// AddAssigneeIfNotAssigned adds an assignee only if he isn't aleady assigned to the issue
-func AddAssigneeIfNotAssigned(issue *Issue, doer *User, assigneeID int64) (err error) {
-	// Check if the user is already assigned
-	isAssigned, err := IsUserAssignedToIssue(issue, &User{ID: assigneeID})
-	if err != nil {
-		return err
-	}
-
-	if !isAssigned {
-		return issue.ChangeAssignee(doer, assigneeID)
-	}
-	return nil
-}
-
-// UpdateAssignee deletes or adds an assignee to an issue
-func UpdateAssignee(issue *Issue, doer *User, assigneeID int64) (err error) {
-	return issue.ChangeAssignee(doer, assigneeID)
-}
-
-// ChangeAssignee changes the Assignee of this issue.
-func (issue *Issue) ChangeAssignee(doer *User, assigneeID int64) (err error) {
+// ToggleAssignee changes a user between assigned and not assigned for this issue, and make issue comment for it.
+func (issue *Issue) ToggleAssignee(doer *User, assigneeID int64) (removed bool, comment *Comment, err error) {
 	sess := x.NewSession()
 	defer sess.Close()
 
 	if err := sess.Begin(); err != nil {
-		return err
+		return false, nil, err
 	}
 
-	if err := issue.changeAssignee(sess, doer, assigneeID, false); err != nil {
-		return err
+	removed, comment, err = issue.toggleAssignee(sess, doer, assigneeID, false)
+	if err != nil {
+		return false, nil, err
 	}
 
 	if err := sess.Commit(); err != nil {
-		return err
+		return false, nil, err
 	}
 
 	go HookQueue.Add(issue.RepoID)
-	return nil
+
+	return removed, comment, nil
 }
 
-func (issue *Issue) changeAssignee(sess *xorm.Session, doer *User, assigneeID int64, isCreate bool) (err error) {
-	// Update the assignee
-	removed, err := updateIssueAssignee(sess, issue, assigneeID)
+func (issue *Issue) toggleAssignee(sess *xorm.Session, doer *User, assigneeID int64, isCreate bool) (removed bool, comment *Comment, err error) {
+	removed, err = toggleUserAssignee(sess, issue, assigneeID)
 	if err != nil {
-		return fmt.Errorf("UpdateIssueUserByAssignee: %v", err)
+		return false, nil, fmt.Errorf("UpdateIssueUserByAssignee: %v", err)
 	}
 
 	// Repo infos
 	if err = issue.loadRepo(sess); err != nil {
-		return fmt.Errorf("loadRepo: %v", err)
+		return false, nil, fmt.Errorf("loadRepo: %v", err)
 	}
 
 	// Comment
-	if _, err = createAssigneeComment(sess, doer, issue.Repo, issue, assigneeID, removed); err != nil {
-		return fmt.Errorf("createAssigneeComment: %v", err)
+	comment, err = createAssigneeComment(sess, doer, issue.Repo, issue, assigneeID, removed)
+	if err != nil {
+		return false, nil, fmt.Errorf("createAssigneeComment: %v", err)
 	}
 
 	// if pull request is in the middle of creation - don't call webhook
 	if isCreate {
-		return nil
+		return removed, comment, err
 	}
 
 	if issue.IsPull {
 		mode, _ := accessLevelUnit(sess, doer, issue.Repo, UnitTypePullRequests)
 
 		if err = issue.loadPullRequest(sess); err != nil {
-			return fmt.Errorf("loadPullRequest: %v", err)
+			return false, nil, fmt.Errorf("loadPullRequest: %v", err)
 		}
 		issue.PullRequest.Issue = issue
 		apiPullRequest := &api.PullRequestPayload{
@@ -190,9 +176,10 @@ func (issue *Issue) changeAssignee(sess *xorm.Session, doer *User, assigneeID in
 		} else {
 			apiPullRequest.Action = api.HookIssueAssigned
 		}
+		// Assignee comment triggers a webhook
 		if err := prepareWebhooks(sess, issue.Repo, HookEventPullRequest, apiPullRequest); err != nil {
 			log.Error("PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, removed, err)
-			return nil
+			return false, nil, err
 		}
 	} else {
 		mode, _ := accessLevelUnit(sess, doer, issue.Repo, UnitTypeIssues)
@@ -208,67 +195,50 @@ func (issue *Issue) changeAssignee(sess *xorm.Session, doer *User, assigneeID in
 		} else {
 			apiIssue.Action = api.HookIssueAssigned
 		}
+		// Assignee comment triggers a webhook
 		if err := prepareWebhooks(sess, issue.Repo, HookEventIssues, apiIssue); err != nil {
 			log.Error("PrepareWebhooks [is_pull: %v, remove_assignee: %v]: %v", issue.IsPull, removed, err)
-			return nil
+			return false, nil, err
 		}
 	}
-	return nil
+	return removed, comment, nil
 }
 
-// UpdateAPIAssignee is a helper function to add or delete one or multiple issue assignee(s)
-// Deleting is done the GitHub way (quote from their api documentation):
-// https://developer.github.com/v3/issues/#edit-an-issue
-// "assignees" (array): Logins for Users to assign to this issue.
-// Pass one or more user logins to replace the set of assignees on this Issue.
-// Send an empty array ([]) to clear all assignees from the Issue.
-func UpdateAPIAssignee(issue *Issue, oneAssignee string, multipleAssignees []string, doer *User) (err error) {
-	var allNewAssignees []*User
+// toggles user assignee state in database
+func toggleUserAssignee(e *xorm.Session, issue *Issue, assigneeID int64) (removed bool, err error) {
 
-	// Keep the old assignee thingy for compatibility reasons
-	if oneAssignee != "" {
-		// Prevent double adding assignees
-		var isDouble bool
-		for _, assignee := range multipleAssignees {
-			if assignee == oneAssignee {
-				isDouble = true
-				break
-			}
-		}
+	// Check if the user exists
+	assignee, err := getUserByID(e, assigneeID)
+	if err != nil {
+		return false, err
+	}
 
-		if !isDouble {
-			multipleAssignees = append(multipleAssignees, oneAssignee)
+	// Check if the submitted user is already assigned, if yes delete him otherwise add him
+	var i int
+	for i = 0; i < len(issue.Assignees); i++ {
+		if issue.Assignees[i].ID == assigneeID {
+			break
 		}
 	}
 
-	// Loop through all assignees to add them
-	for _, assigneeName := range multipleAssignees {
-		assignee, err := GetUserByName(assigneeName)
+	assigneeIn := IssueAssignees{AssigneeID: assigneeID, IssueID: issue.ID}
+
+	toBeDeleted := i < len(issue.Assignees)
+	if toBeDeleted {
+		issue.Assignees = append(issue.Assignees[:i], issue.Assignees[i:]...)
+		_, err = e.Delete(assigneeIn)
 		if err != nil {
-			return err
+			return toBeDeleted, err
 		}
-
-		allNewAssignees = append(allNewAssignees, assignee)
-	}
-
-	// Delete all old assignees not passed
-	if err = DeleteNotPassedAssignee(issue, doer, allNewAssignees); err != nil {
-		return err
-	}
-
-	// Add all new assignees
-	// Update the assignee. The function will check if the user exists, is already
-	// assigned (which he shouldn't as we deleted all assignees before) and
-	// has access to the repo.
-	for _, assignee := range allNewAssignees {
-		// Extra method to prevent double adding (which would result in removing)
-		err = AddAssigneeIfNotAssigned(issue, doer, assignee.ID)
+	} else {
+		issue.Assignees = append(issue.Assignees, assignee)
+		_, err = e.Insert(assigneeIn)
 		if err != nil {
-			return err
+			return toBeDeleted, err
 		}
 	}
 
-	return
+	return toBeDeleted, nil
 }
 
 // MakeIDsFromAPIAssigneesToAdd returns an array with all assignee IDs
@@ -292,7 +262,7 @@ func MakeIDsFromAPIAssigneesToAdd(oneAssignee string, multipleAssignees []string
 	}
 
 	// Get the IDs of all assignees
-	assigneeIDs = GetUserIDsByNames(multipleAssignees)
+	assigneeIDs, err = GetUserIDsByNames(multipleAssignees, false)
 
 	return
 }
diff --git a/models/issue_assignees_test.go b/models/issue_assignees_test.go
index d32f41737a..1c5b5e7a22 100644
--- a/models/issue_assignees_test.go
+++ b/models/issue_assignees_test.go
@@ -20,17 +20,17 @@ func TestUpdateAssignee(t *testing.T) {
 	// Assign multiple users
 	user2, err := GetUserByID(2)
 	assert.NoError(t, err)
-	err = UpdateAssignee(issue, &User{ID: 1}, user2.ID)
+	_, _, err = issue.ToggleAssignee(&User{ID: 1}, user2.ID)
 	assert.NoError(t, err)
 
 	user3, err := GetUserByID(3)
 	assert.NoError(t, err)
-	err = UpdateAssignee(issue, &User{ID: 1}, user3.ID)
+	_, _, err = issue.ToggleAssignee(&User{ID: 1}, user3.ID)
 	assert.NoError(t, err)
 
 	user1, err := GetUserByID(1) // This user is already assigned (see the definition in fixtures), so running  UpdateAssignee should unassign him
 	assert.NoError(t, err)
-	err = UpdateAssignee(issue, &User{ID: 1}, user1.ID)
+	_, _, err = issue.ToggleAssignee(&User{ID: 1}, user1.ID)
 	assert.NoError(t, err)
 
 	// Check if he got removed
diff --git a/models/issue_comment.go b/models/issue_comment.go
index e8043c1ec7..d7128bdbac 100644
--- a/models/issue_comment.go
+++ b/models/issue_comment.go
@@ -13,12 +13,14 @@ import (
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/markup/markdown"
+	"code.gitea.io/gitea/modules/references"
+	"code.gitea.io/gitea/modules/structs"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/timeutil"
 
-	"github.com/go-xorm/xorm"
 	"github.com/unknwon/com"
 	"xorm.io/builder"
+	"xorm.io/xorm"
 )
 
 // CommentType defines whether a comment is just a simple comment, an action (like close) or a reference.
@@ -144,10 +146,10 @@ type Comment struct {
 
 	// Reference an issue or pull from another comment, issue or PR
 	// All information is about the origin of the reference
-	RefRepoID    int64      `xorm:"index"` // Repo where the referencing
-	RefIssueID   int64      `xorm:"index"`
-	RefCommentID int64      `xorm:"index"`    // 0 if origin is Issue title or content (or PR's)
-	RefAction    XRefAction `xorm:"SMALLINT"` // What hapens if RefIssueID resolves
+	RefRepoID    int64                 `xorm:"index"` // Repo where the referencing
+	RefIssueID   int64                 `xorm:"index"`
+	RefCommentID int64                 `xorm:"index"`    // 0 if origin is Issue title or content (or PR's)
+	RefAction    references.XRefAction `xorm:"SMALLINT"` // What hapens if RefIssueID resolves
 	RefIsPull    bool
 
 	RefRepo    *Repository `xorm:"-"`
@@ -355,6 +357,27 @@ func (c *Comment) LoadAttachments() error {
 	return nil
 }
 
+// UpdateAttachments update attachments by UUIDs for the comment
+func (c *Comment) UpdateAttachments(uuids []string) error {
+	sess := x.NewSession()
+	defer sess.Close()
+	if err := sess.Begin(); err != nil {
+		return err
+	}
+	attachments, err := getAttachmentsByUUIDs(sess, uuids)
+	if err != nil {
+		return fmt.Errorf("getAttachmentsByUUIDs [uuids: %v]: %v", uuids, err)
+	}
+	for i := 0; i < len(attachments); i++ {
+		attachments[i].IssueID = c.IssueID
+		attachments[i].CommentID = c.ID
+		if err := updateAttachment(sess, attachments[i]); err != nil {
+			return fmt.Errorf("update attachment [id: %d]: %v", attachments[i].ID, err)
+		}
+	}
+	return sess.Commit()
+}
+
 // LoadAssigneeUser if comment.Type is CommentTypeAssignees, then load assignees
 func (c *Comment) LoadAssigneeUser() error {
 	var err error
@@ -773,7 +796,7 @@ type CreateCommentOptions struct {
 	RefRepoID        int64
 	RefIssueID       int64
 	RefCommentID     int64
-	RefAction        XRefAction
+	RefAction        references.XRefAction
 	RefIsPull        bool
 }
 
@@ -1021,3 +1044,23 @@ func fetchCodeCommentsByReview(e Engine, issue *Issue, currentUser *User, review
 func FetchCodeComments(issue *Issue, currentUser *User) (CodeComments, error) {
 	return fetchCodeComments(x, issue, currentUser)
 }
+
+// UpdateCommentsMigrationsByType updates comments' migrations information via given git service type and original id and poster id
+func UpdateCommentsMigrationsByType(tp structs.GitServiceType, originalAuthorID string, posterID int64) error {
+	_, err := x.Table("comment").
+		Where(builder.In("issue_id",
+			builder.Select("issue.id").
+				From("issue").
+				InnerJoin("repository", "issue.repo_id = repository.id").
+				Where(builder.Eq{
+					"repository.original_service_type": tp,
+				}),
+		)).
+		And("comment.original_author_id = ?", originalAuthorID).
+		Update(map[string]interface{}{
+			"poster_id":          posterID,
+			"original_author":    "",
+			"original_author_id": 0,
+		})
+	return err
+}
diff --git a/models/issue_label.go b/models/issue_label.go
index dab5ba2827..1fc873cfd4 100644
--- a/models/issue_label.go
+++ b/models/issue_label.go
@@ -13,8 +13,8 @@ import (
 
 	api "code.gitea.io/gitea/modules/structs"
 
-	"github.com/go-xorm/xorm"
 	"xorm.io/builder"
+	"xorm.io/xorm"
 )
 
 var labelColorPattern = regexp.MustCompile("#([a-fA-F0-9]{6})")
@@ -68,10 +68,11 @@ type Label struct {
 	Color           string `xorm:"VARCHAR(7)"`
 	NumIssues       int
 	NumClosedIssues int
-	NumOpenIssues   int  `xorm:"-"`
-	IsChecked       bool `xorm:"-"`
-	QueryString     string
-	IsSelected      bool
+	NumOpenIssues   int    `xorm:"-"`
+	IsChecked       bool   `xorm:"-"`
+	QueryString     string `xorm:"-"`
+	IsSelected      bool   `xorm:"-"`
+	IsExcluded      bool   `xorm:"-"`
 }
 
 // APIFormat converts a Label to the api.Label format
@@ -97,7 +98,10 @@ func (label *Label) LoadSelectedLabelsAfterClick(currentSelectedLabels []int64)
 	for _, s := range currentSelectedLabels {
 		if s == label.ID {
 			labelSelected = true
-		} else if s > 0 {
+		} else if -s == label.ID {
+			labelSelected = true
+			label.IsExcluded = true
+		} else if s != 0 {
 			labelQuerySlice = append(labelQuerySlice, strconv.FormatInt(s, 10))
 		}
 	}
diff --git a/models/issue_lock.go b/models/issue_lock.go
index 5a2d996b64..dc6655ad3b 100644
--- a/models/issue_lock.go
+++ b/models/issue_lock.go
@@ -28,7 +28,6 @@ func updateIssueLock(opts *IssueLockOptions, lock bool) error {
 	}
 
 	opts.Issue.IsLocked = lock
-
 	var commentType CommentType
 	if opts.Issue.IsLocked {
 		commentType = CommentTypeLock
@@ -36,16 +35,26 @@ func updateIssueLock(opts *IssueLockOptions, lock bool) error {
 		commentType = CommentTypeUnlock
 	}
 
-	if err := UpdateIssueCols(opts.Issue, "is_locked"); err != nil {
+	sess := x.NewSession()
+	defer sess.Close()
+	if err := sess.Begin(); err != nil {
 		return err
 	}
 
-	_, err := CreateComment(&CreateCommentOptions{
+	if err := updateIssueCols(sess, opts.Issue, "is_locked"); err != nil {
+		return err
+	}
+
+	_, err := createComment(sess, &CreateCommentOptions{
 		Doer:    opts.Doer,
 		Issue:   opts.Issue,
 		Repo:    opts.Issue.Repo,
 		Type:    commentType,
 		Content: opts.Reason,
 	})
-	return err
+	if err != nil {
+		return err
+	}
+
+	return sess.Commit()
 }
diff --git a/models/issue_milestone.go b/models/issue_milestone.go
index f8f414e716..d32cb3c7d1 100644
--- a/models/issue_milestone.go
+++ b/models/issue_milestone.go
@@ -10,8 +10,9 @@ import (
 	"code.gitea.io/gitea/modules/setting"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/timeutil"
+	"xorm.io/builder"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 // Milestone represents a milestone of repository.
@@ -191,7 +192,6 @@ func (milestones MilestoneList) getMilestoneIDs() []int64 {
 
 // GetMilestonesByRepoID returns all opened milestones of a repository.
 func GetMilestonesByRepoID(repoID int64, state api.StateType) (MilestoneList, error) {
-
 	sess := x.Where("repo_id = ?", repoID)
 
 	switch state {
@@ -238,13 +238,34 @@ func GetMilestones(repoID int64, page int, isClosed bool, sortType string) (Mile
 }
 
 func updateMilestone(e Engine, m *Milestone) error {
-	_, err := e.ID(m.ID).AllCols().Update(m)
+	_, err := e.ID(m.ID).AllCols().
+		SetExpr("num_issues", builder.Select("count(*)").From("issue").Where(
+			builder.Eq{"milestone_id": m.ID},
+		)).
+		SetExpr("num_closed_issues", builder.Select("count(*)").From("issue").Where(
+			builder.Eq{
+				"milestone_id": m.ID,
+				"is_closed":    true,
+			},
+		)).
+		Update(m)
 	return err
 }
 
 // UpdateMilestone updates information of given milestone.
 func UpdateMilestone(m *Milestone) error {
-	return updateMilestone(x, m)
+	if err := updateMilestone(x, m); err != nil {
+		return err
+	}
+
+	return updateMilestoneCompleteness(x, m.ID)
+}
+
+func updateMilestoneCompleteness(e Engine, milestoneID int64) error {
+	_, err := e.Exec("UPDATE `milestone` SET completeness=100*num_closed_issues/(CASE WHEN num_issues > 0 THEN num_issues ELSE 1 END) WHERE id=?",
+		milestoneID,
+	)
+	return err
 }
 
 func countRepoMilestones(e Engine, repoID int64) (int64, error) {
@@ -278,11 +299,6 @@ func MilestoneStats(repoID int64) (open int64, closed int64, err error) {
 
 // ChangeMilestoneStatus changes the milestone open/closed status.
 func ChangeMilestoneStatus(m *Milestone, isClosed bool) (err error) {
-	repo, err := GetRepositoryByID(m.RepoID)
-	if err != nil {
-		return err
-	}
-
 	sess := x.NewSession()
 	defer sess.Close()
 	if err = sess.Begin(); err != nil {
@@ -290,92 +306,88 @@ func ChangeMilestoneStatus(m *Milestone, isClosed bool) (err error) {
 	}
 
 	m.IsClosed = isClosed
-	if err = updateMilestone(sess, m); err != nil {
+	if _, err := sess.ID(m.ID).Cols("is_closed").Update(m); err != nil {
 		return err
 	}
 
-	numMilestones, err := countRepoMilestones(sess, repo.ID)
-	if err != nil {
+	if err := updateRepoMilestoneNum(sess, m.RepoID); err != nil {
 		return err
 	}
-	numClosedMilestones, err := countRepoClosedMilestones(sess, repo.ID)
-	if err != nil {
-		return err
-	}
-	repo.NumMilestones = int(numMilestones)
-	repo.NumClosedMilestones = int(numClosedMilestones)
 
-	if _, err = sess.ID(repo.ID).Cols("num_milestones, num_closed_milestones").Update(repo); err != nil {
-		return err
-	}
 	return sess.Commit()
 }
 
-func changeMilestoneIssueStats(e *xorm.Session, issue *Issue) error {
-	if issue.MilestoneID == 0 {
-		return nil
+func updateRepoMilestoneNum(e Engine, repoID int64) error {
+	_, err := e.Exec("UPDATE `repository` SET num_milestones=(SELECT count(*) FROM milestone WHERE repo_id=?),num_closed_milestones=(SELECT count(*) FROM milestone WHERE repo_id=? AND is_closed=?) WHERE id=?",
+		repoID,
+		repoID,
+		true,
+		repoID,
+	)
+	return err
+}
+
+func updateMilestoneTotalNum(e Engine, milestoneID int64) (err error) {
+	if _, err = e.Exec("UPDATE `milestone` SET num_issues=(SELECT count(*) FROM issue WHERE milestone_id=?) WHERE id=?",
+		milestoneID,
+		milestoneID,
+	); err != nil {
+		return
 	}
 
-	m, err := getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID)
-	if err != nil {
-		return err
+	return updateMilestoneCompleteness(e, milestoneID)
+}
+
+func updateMilestoneClosedNum(e Engine, milestoneID int64) (err error) {
+	if _, err = e.Exec("UPDATE `milestone` SET num_closed_issues=(SELECT count(*) FROM issue WHERE milestone_id=? AND is_closed=?) WHERE id=?",
+		milestoneID,
+		true,
+		milestoneID,
+	); err != nil {
+		return
 	}
 
-	if issue.IsClosed {
-		m.NumOpenIssues--
-		m.NumClosedIssues++
-	} else {
-		m.NumOpenIssues++
-		m.NumClosedIssues--
-	}
-
-	return updateMilestone(e, m)
+	return updateMilestoneCompleteness(e, milestoneID)
 }
 
 func changeMilestoneAssign(e *xorm.Session, doer *User, issue *Issue, oldMilestoneID int64) error {
+	if err := updateIssueCols(e, issue, "milestone_id"); err != nil {
+		return err
+	}
+
 	if oldMilestoneID > 0 {
-		m, err := getMilestoneByRepoID(e, issue.RepoID, oldMilestoneID)
-		if err != nil {
+		if err := updateMilestoneTotalNum(e, oldMilestoneID); err != nil {
 			return err
 		}
-
-		m.NumIssues--
 		if issue.IsClosed {
-			m.NumClosedIssues--
-		}
-
-		if err = updateMilestone(e, m); err != nil {
-			return err
+			if err := updateMilestoneClosedNum(e, oldMilestoneID); err != nil {
+				return err
+			}
 		}
 	}
 
 	if issue.MilestoneID > 0 {
-		m, err := getMilestoneByRepoID(e, issue.RepoID, issue.MilestoneID)
-		if err != nil {
+		if err := updateMilestoneTotalNum(e, issue.MilestoneID); err != nil {
 			return err
 		}
-
-		m.NumIssues++
 		if issue.IsClosed {
-			m.NumClosedIssues++
+			if err := updateMilestoneClosedNum(e, issue.MilestoneID); err != nil {
+				return err
+			}
 		}
-
-		if err = updateMilestone(e, m); err != nil {
-			return err
-		}
-	}
-
-	if err := issue.loadRepo(e); err != nil {
-		return err
 	}
 
 	if oldMilestoneID > 0 || issue.MilestoneID > 0 {
+		if err := issue.loadRepo(e); err != nil {
+			return err
+		}
+
 		if _, err := createMilestoneComment(e, doer, issue.Repo, issue, oldMilestoneID, issue.MilestoneID); err != nil {
 			return err
 		}
 	}
 
-	return updateIssueCols(e, issue, "milestone_id")
+	return nil
 }
 
 // ChangeMilestoneAssign changes assignment of milestone for issue.
diff --git a/models/issue_milestone_test.go b/models/issue_milestone_test.go
index 09c6ff7595..6f8548ec67 100644
--- a/models/issue_milestone_test.go
+++ b/models/issue_milestone_test.go
@@ -231,7 +231,7 @@ func TestChangeMilestoneStatus(t *testing.T) {
 	CheckConsistencyFor(t, &Repository{ID: milestone.RepoID}, &Milestone{})
 }
 
-func TestChangeMilestoneIssueStats(t *testing.T) {
+func TestUpdateMilestoneClosedNum(t *testing.T) {
 	assert.NoError(t, PrepareTestDatabase())
 	issue := AssertExistsAndLoadBean(t, &Issue{MilestoneID: 1},
 		"is_closed=0").(*Issue)
@@ -240,14 +240,14 @@ func TestChangeMilestoneIssueStats(t *testing.T) {
 	issue.ClosedUnix = timeutil.TimeStampNow()
 	_, err := x.Cols("is_closed", "closed_unix").Update(issue)
 	assert.NoError(t, err)
-	assert.NoError(t, changeMilestoneIssueStats(x.NewSession(), issue))
+	assert.NoError(t, updateMilestoneClosedNum(x, issue.MilestoneID))
 	CheckConsistencyFor(t, &Milestone{})
 
 	issue.IsClosed = false
 	issue.ClosedUnix = 0
 	_, err = x.Cols("is_closed", "closed_unix").Update(issue)
 	assert.NoError(t, err)
-	assert.NoError(t, changeMilestoneIssueStats(x.NewSession(), issue))
+	assert.NoError(t, updateMilestoneClosedNum(x, issue.MilestoneID))
 	CheckConsistencyFor(t, &Milestone{})
 }
 
diff --git a/models/issue_reaction.go b/models/issue_reaction.go
index ab644b4b3e..4596d32d06 100644
--- a/models/issue_reaction.go
+++ b/models/issue_reaction.go
@@ -11,8 +11,8 @@ import (
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/timeutil"
 
-	"github.com/go-xorm/xorm"
 	"xorm.io/builder"
+	"xorm.io/xorm"
 )
 
 // Reaction represents a reactions on issues and comments.
diff --git a/models/issue_test.go b/models/issue_test.go
index 9cd9ff0ad9..592a0e3d77 100644
--- a/models/issue_test.go
+++ b/models/issue_test.go
@@ -84,53 +84,6 @@ func TestGetParticipantsByIssueID(t *testing.T) {
 	checkParticipants(1, []int{5})
 }
 
-func TestIssue_AddLabel(t *testing.T) {
-	var tests = []struct {
-		issueID int64
-		labelID int64
-		doerID  int64
-	}{
-		{1, 2, 2}, // non-pull-request, not-already-added label
-		{1, 1, 2}, // non-pull-request, already-added label
-		{2, 2, 2}, // pull-request, not-already-added label
-		{2, 1, 2}, // pull-request, already-added label
-	}
-	for _, test := range tests {
-		assert.NoError(t, PrepareTestDatabase())
-		issue := AssertExistsAndLoadBean(t, &Issue{ID: test.issueID}).(*Issue)
-		label := AssertExistsAndLoadBean(t, &Label{ID: test.labelID}).(*Label)
-		doer := AssertExistsAndLoadBean(t, &User{ID: test.doerID}).(*User)
-		assert.NoError(t, issue.AddLabel(doer, label))
-		AssertExistsAndLoadBean(t, &IssueLabel{IssueID: test.issueID, LabelID: test.labelID})
-	}
-}
-
-func TestIssue_AddLabels(t *testing.T) {
-	var tests = []struct {
-		issueID  int64
-		labelIDs []int64
-		doerID   int64
-	}{
-		{1, []int64{1, 2}, 2}, // non-pull-request
-		{1, []int64{}, 2},     // non-pull-request, empty
-		{2, []int64{1, 2}, 2}, // pull-request
-		{2, []int64{}, 1},     // pull-request, empty
-	}
-	for _, test := range tests {
-		assert.NoError(t, PrepareTestDatabase())
-		issue := AssertExistsAndLoadBean(t, &Issue{ID: test.issueID}).(*Issue)
-		labels := make([]*Label, len(test.labelIDs))
-		for i, labelID := range test.labelIDs {
-			labels[i] = AssertExistsAndLoadBean(t, &Label{ID: labelID}).(*Label)
-		}
-		doer := AssertExistsAndLoadBean(t, &User{ID: test.doerID}).(*User)
-		assert.NoError(t, issue.AddLabels(doer, labels))
-		for _, labelID := range test.labelIDs {
-			AssertExistsAndLoadBean(t, &IssueLabel{IssueID: test.issueID, LabelID: labelID})
-		}
-	}
-}
-
 func TestIssue_ClearLabels(t *testing.T) {
 	var tests = []struct {
 		issueID int64
@@ -160,7 +113,7 @@ func TestUpdateIssueCols(t *testing.T) {
 	issue.Content = "This should have no effect"
 
 	now := time.Now().Unix()
-	assert.NoError(t, UpdateIssueCols(issue, "name"))
+	assert.NoError(t, updateIssueCols(x, issue, "name"))
 	then := time.Now().Unix()
 
 	updatedIssue := AssertExistsAndLoadBean(t, &Issue{ID: issue.ID}).(*Issue)
@@ -344,7 +297,7 @@ func testInsertIssue(t *testing.T, title, content string) {
 		Title:    title,
 		Content:  content,
 	}
-	err := NewIssue(repo, &issue, nil, nil, nil)
+	err := NewIssue(repo, &issue, nil, nil)
 	assert.NoError(t, err)
 
 	var newIssue Issue
@@ -366,3 +319,35 @@ func TestIssue_InsertIssue(t *testing.T) {
 	testInsertIssue(t, "my issue1", "special issue's comments?")
 	testInsertIssue(t, `my issue2, this is my son's love \n \r \ `, "special issue's '' comments?")
 }
+
+func TestIssue_ResolveMentions(t *testing.T) {
+	assert.NoError(t, PrepareTestDatabase())
+
+	testSuccess := func(owner, repo, doer string, mentions []string, expected []int64) {
+		o := AssertExistsAndLoadBean(t, &User{LowerName: owner}).(*User)
+		r := AssertExistsAndLoadBean(t, &Repository{OwnerID: o.ID, LowerName: repo}).(*Repository)
+		issue := &Issue{RepoID: r.ID}
+		d := AssertExistsAndLoadBean(t, &User{LowerName: doer}).(*User)
+		resolved, err := issue.ResolveMentionsByVisibility(DefaultDBContext(), d, mentions)
+		assert.NoError(t, err)
+		ids := make([]int64, len(resolved))
+		for i, user := range resolved {
+			ids[i] = user.ID
+		}
+		sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
+		assert.EqualValues(t, expected, ids)
+	}
+
+	// Public repo, existing user
+	testSuccess("user2", "repo1", "user1", []string{"user5"}, []int64{5})
+	// Public repo, non-existing user
+	testSuccess("user2", "repo1", "user1", []string{"nonexisting"}, []int64{})
+	// Public repo, doer
+	testSuccess("user2", "repo1", "user1", []string{"user1"}, []int64{})
+	// Private repo, team member
+	testSuccess("user17", "big_test_private_4", "user20", []string{"user2"}, []int64{2})
+	// Private repo, not a team member
+	testSuccess("user17", "big_test_private_4", "user20", []string{"user5"}, []int64{})
+	// Private repo, whole team
+	testSuccess("user17", "big_test_private_4", "user15", []string{"owners"}, []int64{18})
+}
diff --git a/models/issue_tracked_time.go b/models/issue_tracked_time.go
index f9313b7653..f616836c85 100644
--- a/models/issue_tracked_time.go
+++ b/models/issue_tracked_time.go
@@ -10,8 +10,8 @@ import (
 	"code.gitea.io/gitea/modules/setting"
 	api "code.gitea.io/gitea/modules/structs"
 
-	"github.com/go-xorm/xorm"
 	"xorm.io/builder"
+	"xorm.io/xorm"
 )
 
 // TrackedTime represents a time that was spent for a specific issue.
diff --git a/models/issue_user.go b/models/issue_user.go
index d55a0dc2fb..67a118fe57 100644
--- a/models/issue_user.go
+++ b/models/issue_user.go
@@ -6,8 +6,6 @@ package models
 
 import (
 	"fmt"
-
-	"github.com/go-xorm/xorm"
 )
 
 // IssueUser represents an issue-user relation.
@@ -51,42 +49,6 @@ func newIssueUsers(e Engine, repo *Repository, issue *Issue) error {
 	return nil
 }
 
-func updateIssueAssignee(e *xorm.Session, issue *Issue, assigneeID int64) (removed bool, err error) {
-
-	// Check if the user exists
-	assignee, err := getUserByID(e, assigneeID)
-	if err != nil {
-		return false, err
-	}
-
-	// Check if the submitted user is already assigne, if yes delete him otherwise add him
-	var i int
-	for i = 0; i < len(issue.Assignees); i++ {
-		if issue.Assignees[i].ID == assigneeID {
-			break
-		}
-	}
-
-	assigneeIn := IssueAssignees{AssigneeID: assigneeID, IssueID: issue.ID}
-
-	toBeDeleted := i < len(issue.Assignees)
-	if toBeDeleted {
-		issue.Assignees = append(issue.Assignees[:i], issue.Assignees[i:]...)
-		_, err = e.Delete(assigneeIn)
-		if err != nil {
-			return toBeDeleted, err
-		}
-	} else {
-		issue.Assignees = append(issue.Assignees, assignee)
-		_, err = e.Insert(assigneeIn)
-		if err != nil {
-			return toBeDeleted, err
-		}
-	}
-
-	return toBeDeleted, nil
-}
-
 // UpdateIssueUserByRead updates issue-user relation for reading.
 func UpdateIssueUserByRead(uid, issueID int64) error {
 	_, err := x.Exec("UPDATE `issue_user` SET is_read=? WHERE uid=? AND issue_id=?", true, uid, issueID)
diff --git a/models/issue_xref.go b/models/issue_xref.go
index 1cc0bcfe6a..4b01022bc5 100644
--- a/models/issue_xref.go
+++ b/models/issue_xref.go
@@ -5,42 +5,16 @@
 package models
 
 import (
-	"regexp"
-	"strconv"
-
 	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/references"
 
-	"github.com/go-xorm/xorm"
 	"github.com/unknwon/com"
-)
-
-var (
-	// TODO: Unify all regexp treatment of cross references in one place
-
-	// issueNumericPattern matches string that references to a numeric issue, e.g. #1287
-	issueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[)(?:#)([0-9]+)(?:\s|$|\)|\]|:|\.(\s|$))`)
-	// crossReferenceIssueNumericPattern matches string that references a numeric issue in a different repository
-	// e.g. gogits/gogs#12345
-	crossReferenceIssueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([0-9a-zA-Z-_\.]+)/([0-9a-zA-Z-_\.]+)#([0-9]+)(?:\s|$|\)|\]|\.(\s|$))`)
-)
-
-// XRefAction represents the kind of effect a cross reference has once is resolved
-type XRefAction int64
-
-const (
-	// XRefActionNone means the cross-reference is a mention (commit, etc.)
-	XRefActionNone XRefAction = iota // 0
-	// XRefActionCloses means the cross-reference should close an issue if it is resolved
-	XRefActionCloses // 1 - not implemented yet
-	// XRefActionReopens means the cross-reference should reopen an issue if it is resolved
-	XRefActionReopens // 2 - Not implemented yet
-	// XRefActionNeutered means the cross-reference will no longer affect the source
-	XRefActionNeutered // 3
+	"xorm.io/xorm"
 )
 
 type crossReference struct {
 	Issue  *Issue
-	Action XRefAction
+	Action references.XRefAction
 }
 
 // crossReferencesContext is context to pass along findCrossReference functions
@@ -72,7 +46,7 @@ func newCrossReference(e *xorm.Session, ctx *crossReferencesContext, xref *cross
 
 func neuterCrossReferences(e Engine, issueID int64, commentID int64) error {
 	active := make([]*Comment, 0, 10)
-	sess := e.Where("`ref_action` IN (?, ?, ?)", XRefActionNone, XRefActionCloses, XRefActionReopens)
+	sess := e.Where("`ref_action` IN (?, ?, ?)", references.XRefActionNone, references.XRefActionCloses, references.XRefActionReopens)
 	if issueID != 0 {
 		sess = sess.And("`ref_issue_id` = ?", issueID)
 	}
@@ -86,7 +60,7 @@ func neuterCrossReferences(e Engine, issueID int64, commentID int64) error {
 	for i, c := range active {
 		ids[i] = c.ID
 	}
-	_, err := e.In("id", ids).Cols("`ref_action`").Update(&Comment{RefAction: XRefActionNeutered})
+	_, err := e.In("id", ids).Cols("`ref_action`").Update(&Comment{RefAction: references.XRefActionNeutered})
 	return err
 }
 
@@ -110,11 +84,11 @@ func (issue *Issue) addCrossReferences(e *xorm.Session, doer *User) error {
 		Doer:      doer,
 		OrigIssue: issue,
 	}
-	return issue.createCrossReferences(e, ctx, issue.Title+"\n"+issue.Content)
+	return issue.createCrossReferences(e, ctx, issue.Title, issue.Content)
 }
 
-func (issue *Issue) createCrossReferences(e *xorm.Session, ctx *crossReferencesContext, content string) error {
-	xreflist, err := ctx.OrigIssue.getCrossReferences(e, ctx, content)
+func (issue *Issue) createCrossReferences(e *xorm.Session, ctx *crossReferencesContext, plaincontent, mdcontent string) error {
+	xreflist, err := ctx.OrigIssue.getCrossReferences(e, ctx, plaincontent, mdcontent)
 	if err != nil {
 		return err
 	}
@@ -126,47 +100,43 @@ func (issue *Issue) createCrossReferences(e *xorm.Session, ctx *crossReferencesC
 	return nil
 }
 
-func (issue *Issue) getCrossReferences(e *xorm.Session, ctx *crossReferencesContext, content string) ([]*crossReference, error) {
+func (issue *Issue) getCrossReferences(e *xorm.Session, ctx *crossReferencesContext, plaincontent, mdcontent string) ([]*crossReference, error) {
 	xreflist := make([]*crossReference, 0, 5)
-	var xref *crossReference
+	var (
+		refRepo  *Repository
+		refIssue *Issue
+		err      error
+	)
 
-	// Issues in the same repository
-	// FIXME: Should we support IssueNameStyleAlphanumeric?
-	matches := issueNumericPattern.FindAllStringSubmatch(content, -1)
-	for _, match := range matches {
-		if index, err := strconv.ParseInt(match[1], 10, 64); err == nil {
-			if err = ctx.OrigIssue.loadRepo(e); err != nil {
+	allrefs := append(references.FindAllIssueReferences(plaincontent), references.FindAllIssueReferencesMarkdown(mdcontent)...)
+
+	for _, ref := range allrefs {
+		if ref.Owner == "" && ref.Name == "" {
+			// Issues in the same repository
+			if err := ctx.OrigIssue.loadRepo(e); err != nil {
 				return nil, err
 			}
-			if xref, err = ctx.OrigIssue.isValidCommentReference(e, ctx, issue.Repo, index); err != nil {
-				return nil, err
-			}
-			if xref != nil {
-				xreflist = ctx.OrigIssue.updateCrossReferenceList(xreflist, xref)
-			}
-		}
-	}
-
-	// Issues in other repositories
-	matches = crossReferenceIssueNumericPattern.FindAllStringSubmatch(content, -1)
-	for _, match := range matches {
-		if index, err := strconv.ParseInt(match[3], 10, 64); err == nil {
-			repo, err := getRepositoryByOwnerAndName(e, match[1], match[2])
+			refRepo = ctx.OrigIssue.Repo
+		} else {
+			// Issues in other repositories
+			refRepo, err = getRepositoryByOwnerAndName(e, ref.Owner, ref.Name)
 			if err != nil {
 				if IsErrRepoNotExist(err) {
 					continue
 				}
 				return nil, err
 			}
-			if err = ctx.OrigIssue.loadRepo(e); err != nil {
-				return nil, err
-			}
-			if xref, err = issue.isValidCommentReference(e, ctx, repo, index); err != nil {
-				return nil, err
-			}
-			if xref != nil {
-				xreflist = issue.updateCrossReferenceList(xreflist, xref)
-			}
+		}
+		if refIssue, err = ctx.OrigIssue.findReferencedIssue(e, ctx, refRepo, ref.Index); err != nil {
+			return nil, err
+		}
+		if refIssue != nil {
+			xreflist = ctx.OrigIssue.updateCrossReferenceList(xreflist, &crossReference{
+				Issue: refIssue,
+				// FIXME: currently ignore keywords
+				// Action: ref.Action,
+				Action: references.XRefActionNone,
+			})
 		}
 	}
 
@@ -179,7 +149,7 @@ func (issue *Issue) updateCrossReferenceList(list []*crossReference, xref *cross
 	}
 	for i, r := range list {
 		if r.Issue.ID == xref.Issue.ID {
-			if xref.Action != XRefActionNone {
+			if xref.Action != references.XRefActionNone {
 				list[i].Action = xref.Action
 			}
 			return list
@@ -188,7 +158,7 @@ func (issue *Issue) updateCrossReferenceList(list []*crossReference, xref *cross
 	return append(list, xref)
 }
 
-func (issue *Issue) isValidCommentReference(e Engine, ctx *crossReferencesContext, repo *Repository, index int64) (*crossReference, error) {
+func (issue *Issue) findReferencedIssue(e Engine, ctx *crossReferencesContext, repo *Repository, index int64) (*Issue, error) {
 	refIssue := &Issue{RepoID: repo.ID, Index: index}
 	if has, _ := e.Get(refIssue); !has {
 		return nil, nil
@@ -206,10 +176,7 @@ func (issue *Issue) isValidCommentReference(e Engine, ctx *crossReferencesContex
 			return nil, nil
 		}
 	}
-	return &crossReference{
-		Issue:  refIssue,
-		Action: XRefActionNone,
-	}, nil
+	return refIssue, nil
 }
 
 func (issue *Issue) neuterCrossReferences(e Engine) error {
@@ -237,7 +204,7 @@ func (comment *Comment) addCrossReferences(e *xorm.Session, doer *User) error {
 		OrigIssue:   comment.Issue,
 		OrigComment: comment,
 	}
-	return comment.Issue.createCrossReferences(e, ctx, comment.Content)
+	return comment.Issue.createCrossReferences(e, ctx, "", comment.Content)
 }
 
 func (comment *Comment) neuterCrossReferences(e Engine) error {
diff --git a/models/lfs_lock.go b/models/lfs_lock.go
index 7ea1dc8660..ba1a452815 100644
--- a/models/lfs_lock.go
+++ b/models/lfs_lock.go
@@ -14,7 +14,7 @@ import (
 	"code.gitea.io/gitea/modules/log"
 	api "code.gitea.io/gitea/modules/structs"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 // LFSLock represents a git lfs lock of repository.
diff --git a/models/login_source.go b/models/login_source.go
index 9381ed034f..ce03c4154f 100644
--- a/models/login_source.go
+++ b/models/login_source.go
@@ -21,9 +21,9 @@ import (
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/timeutil"
 
-	"github.com/go-xorm/xorm"
 	"github.com/unknwon/com"
 	"xorm.io/core"
+	"xorm.io/xorm"
 )
 
 // LoginType represents an login type.
diff --git a/models/migrate.go b/models/migrate.go
index 85be3a312c..53838fd65e 100644
--- a/models/migrate.go
+++ b/models/migrate.go
@@ -4,7 +4,7 @@
 
 package models
 
-import "github.com/go-xorm/xorm"
+import "xorm.io/xorm"
 
 // InsertMilestones creates milestones of repository.
 func InsertMilestones(ms ...*Milestone) (err error) {
diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go
index 89350ee89d..5ed70dc4f5 100644
--- a/models/migrations/migrations.go
+++ b/models/migrations/migrations.go
@@ -21,10 +21,10 @@ import (
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 
-	"github.com/go-xorm/xorm"
 	gouuid "github.com/satori/go.uuid"
 	"github.com/unknwon/com"
 	ini "gopkg.in/ini.v1"
+	"xorm.io/xorm"
 )
 
 const minDBVersion = 4
@@ -253,6 +253,18 @@ var migrations = []Migration{
 	// v98 -> v99
 	NewMigration("add original author name and id on migrated release", addOriginalAuthorOnMigratedReleases),
 	// v99 -> v100
+	NewMigration("add task table and status column for repository table", addTaskTable),
+	// v100 -> v101
+	NewMigration("update migration repositories' service type", updateMigrationServiceTypes),
+	// v101 -> v102
+	NewMigration("change length of some external login users columns", changeSomeColumnsLengthOfExternalLoginUser),
+	// v102 -> v103
+	NewMigration("update migration repositories' service type", dropColumnHeadUserNameOnPullRequest),
+	// v103 -> v104
+	NewMigration("Add WhitelistDeployKeys to protected branch", addWhitelistDeployKeysToBranches),
+	// v104 -> v105
+	NewMigration("remove unnecessary columns from label", removeLabelUneededCols),
+	// v105 -> v106
 	NewMigration("add includes_all_repositories to teams", addTeamIncludesAllRepositories),
 }
 
@@ -406,9 +418,11 @@ func dropTableColumns(sess *xorm.Session, tableName string, columnNames ...strin
 		}
 		for _, index := range res {
 			indexName := index["column_name"]
-			_, err := sess.Exec(fmt.Sprintf("DROP INDEX `%s` ON `%s`", indexName, tableName))
-			if err != nil {
-				return err
+			if len(indexName) > 0 {
+				_, err := sess.Exec(fmt.Sprintf("DROP INDEX `%s` ON `%s`", indexName, tableName))
+				if err != nil {
+					return err
+				}
 			}
 		}
 
diff --git a/models/migrations/v100.go b/models/migrations/v100.go
new file mode 100644
index 0000000000..6a4e98af1f
--- /dev/null
+++ b/models/migrations/v100.go
@@ -0,0 +1,83 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package migrations
+
+import (
+	"net/url"
+	"strings"
+	"time"
+
+	"xorm.io/xorm"
+)
+
+func updateMigrationServiceTypes(x *xorm.Engine) error {
+	type Repository struct {
+		ID                  int64
+		OriginalServiceType int    `xorm:"index default(0)"`
+		OriginalURL         string `xorm:"VARCHAR(2048)"`
+	}
+
+	if err := x.Sync2(new(Repository)); err != nil {
+		return err
+	}
+
+	var last int
+	const batchSize = 50
+	for {
+		var results = make([]Repository, 0, batchSize)
+		err := x.Where("original_url <> '' AND original_url IS NOT NULL").
+			And("original_service_type = 0 OR original_service_type IS NULL").
+			OrderBy("id").
+			Limit(batchSize, last).
+			Find(&results)
+		if err != nil {
+			return err
+		}
+		if len(results) == 0 {
+			break
+		}
+		last += len(results)
+
+		const PlainGitService = 1 // 1 plain git service
+		const GithubService = 2   // 2 github.com
+
+		for _, res := range results {
+			u, err := url.Parse(res.OriginalURL)
+			if err != nil {
+				return err
+			}
+			var serviceType = PlainGitService
+			if strings.EqualFold(u.Host, "github.com") {
+				serviceType = GithubService
+			}
+			_, err = x.Exec("UPDATE repository SET original_service_type = ? WHERE id = ?", serviceType, res.ID)
+			if err != nil {
+				return err
+			}
+		}
+	}
+
+	type ExternalLoginUser struct {
+		ExternalID        string                 `xorm:"pk NOT NULL"`
+		UserID            int64                  `xorm:"INDEX NOT NULL"`
+		LoginSourceID     int64                  `xorm:"pk NOT NULL"`
+		RawData           map[string]interface{} `xorm:"TEXT JSON"`
+		Provider          string                 `xorm:"index VARCHAR(25)"`
+		Email             string
+		Name              string
+		FirstName         string
+		LastName          string
+		NickName          string
+		Description       string
+		AvatarURL         string
+		Location          string
+		AccessToken       string
+		AccessTokenSecret string
+		RefreshToken      string
+		ExpiresAt         time.Time
+	}
+
+	return x.Sync2(new(ExternalLoginUser))
+}
diff --git a/models/migrations/v101.go b/models/migrations/v101.go
new file mode 100644
index 0000000000..9ef82a2933
--- /dev/null
+++ b/models/migrations/v101.go
@@ -0,0 +1,19 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package migrations
+
+import (
+	"xorm.io/xorm"
+)
+
+func changeSomeColumnsLengthOfExternalLoginUser(x *xorm.Engine) error {
+	type ExternalLoginUser struct {
+		AccessToken       string `xorm:"TEXT"`
+		AccessTokenSecret string `xorm:"TEXT"`
+		RefreshToken      string `xorm:"TEXT"`
+	}
+
+	return x.Sync2(new(ExternalLoginUser))
+}
diff --git a/models/migrations/v102.go b/models/migrations/v102.go
new file mode 100644
index 0000000000..74e8574ec3
--- /dev/null
+++ b/models/migrations/v102.go
@@ -0,0 +1,15 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package migrations
+
+import (
+	"xorm.io/xorm"
+)
+
+func dropColumnHeadUserNameOnPullRequest(x *xorm.Engine) error {
+	sess := x.NewSession()
+	defer sess.Close()
+	return dropTableColumns(sess, "pull_request", "head_user_name")
+}
diff --git a/models/migrations/v103.go b/models/migrations/v103.go
new file mode 100644
index 0000000000..fed025c5cd
--- /dev/null
+++ b/models/migrations/v103.go
@@ -0,0 +1,18 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package migrations
+
+import (
+	"xorm.io/xorm"
+)
+
+func addWhitelistDeployKeysToBranches(x *xorm.Engine) error {
+	type ProtectedBranch struct {
+		ID                  int64
+		WhitelistDeployKeys bool `xorm:"NOT NULL DEFAULT false"`
+	}
+
+	return x.Sync2(new(ProtectedBranch))
+}
diff --git a/models/migrations/v104.go b/models/migrations/v104.go
new file mode 100644
index 0000000000..f3ec3c88c8
--- /dev/null
+++ b/models/migrations/v104.go
@@ -0,0 +1,34 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package migrations
+
+import (
+	"xorm.io/xorm"
+)
+
+func removeLabelUneededCols(x *xorm.Engine) error {
+
+	// Make sure the columns exist before dropping them
+	type Label struct {
+		QueryString string
+		IsSelected  bool
+	}
+	if err := x.Sync2(new(Label)); err != nil {
+		return err
+	}
+
+	sess := x.NewSession()
+	defer sess.Close()
+	if err := sess.Begin(); err != nil {
+		return err
+	}
+	if err := dropTableColumns(sess, "label", "query_string"); err != nil {
+		return err
+	}
+	if err := dropTableColumns(sess, "label", "is_selected"); err != nil {
+		return err
+	}
+	return sess.Commit()
+}
diff --git a/models/migrations/v105.go b/models/migrations/v105.go
new file mode 100644
index 0000000000..6c9a5817af
--- /dev/null
+++ b/models/migrations/v105.go
@@ -0,0 +1,25 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package migrations
+
+import (
+	"xorm.io/xorm"
+)
+
+func addTeamIncludesAllRepositories(x *xorm.Engine) error {
+
+	type Team struct {
+		ID                      int64 `xorm:"pk autoincr"`
+		IncludesAllRepositories bool  `xorm:"NOT NULL DEFAULT false"`
+	}
+
+	if err := x.Sync2(new(Team)); err != nil {
+		return err
+	}
+
+	_, err := x.Exec("UPDATE `team` SET `includes_all_repositories` = ? WHERE `name`=?",
+		true, "Owners")
+	return err
+}
diff --git a/models/migrations/v13.go b/models/migrations/v13.go
index 8b6b38cadf..3c35b66ab9 100644
--- a/models/migrations/v13.go
+++ b/models/migrations/v13.go
@@ -9,8 +9,8 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/go-xorm/xorm"
 	"github.com/unknwon/com"
+	"xorm.io/xorm"
 )
 
 func ldapUseSSLToSecurityProtocol(x *xorm.Engine) error {
diff --git a/models/migrations/v14.go b/models/migrations/v14.go
index 392f9fdba6..675c7459dd 100644
--- a/models/migrations/v14.go
+++ b/models/migrations/v14.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"fmt"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func setCommentUpdatedWithCreated(x *xorm.Engine) (err error) {
diff --git a/models/migrations/v15.go b/models/migrations/v15.go
index 3492a7190b..8872f1e946 100644
--- a/models/migrations/v15.go
+++ b/models/migrations/v15.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"fmt"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func createAllowCreateOrganizationColumn(x *xorm.Engine) error {
diff --git a/models/migrations/v16.go b/models/migrations/v16.go
index 5b8ec19d32..a849205b55 100644
--- a/models/migrations/v16.go
+++ b/models/migrations/v16.go
@@ -10,7 +10,7 @@ import (
 
 	"code.gitea.io/gitea/modules/markup"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 // Enumerate all the unit types
diff --git a/models/migrations/v17.go b/models/migrations/v17.go
index 2986badc97..2907b009db 100644
--- a/models/migrations/v17.go
+++ b/models/migrations/v17.go
@@ -8,7 +8,7 @@ import (
 	"fmt"
 	"time"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func setProtectedBranchUpdatedWithCreated(x *xorm.Engine) (err error) {
diff --git a/models/migrations/v18.go b/models/migrations/v18.go
index 3b3cd23ccf..66a1de3499 100644
--- a/models/migrations/v18.go
+++ b/models/migrations/v18.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"fmt"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 // ExternalLoginUser makes the connecting between some existing user and additional external login sources
diff --git a/models/migrations/v19.go b/models/migrations/v19.go
index 7728f5add6..349d5850aa 100644
--- a/models/migrations/v19.go
+++ b/models/migrations/v19.go
@@ -13,8 +13,8 @@ import (
 
 	"code.gitea.io/gitea/modules/setting"
 
-	"github.com/go-xorm/xorm"
 	"github.com/unknwon/com"
+	"xorm.io/xorm"
 )
 
 func generateAndMigrateGitHooks(x *xorm.Engine) (err error) {
diff --git a/models/migrations/v20.go b/models/migrations/v20.go
index ded99e09ce..0897eada74 100644
--- a/models/migrations/v20.go
+++ b/models/migrations/v20.go
@@ -16,7 +16,7 @@ import (
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func useNewNameAvatars(x *xorm.Engine) error {
diff --git a/models/migrations/v21.go b/models/migrations/v21.go
index 65cae2ac03..2750725760 100644
--- a/models/migrations/v21.go
+++ b/models/migrations/v21.go
@@ -11,8 +11,8 @@ import (
 
 	"code.gitea.io/gitea/modules/setting"
 
-	"github.com/go-xorm/xorm"
 	"github.com/unknwon/com"
+	"xorm.io/xorm"
 )
 
 const (
diff --git a/models/migrations/v22.go b/models/migrations/v22.go
index faac74343b..eb37aec17f 100644
--- a/models/migrations/v22.go
+++ b/models/migrations/v22.go
@@ -13,8 +13,8 @@ import (
 
 	"code.gitea.io/gitea/modules/setting"
 
-	"github.com/go-xorm/xorm"
 	"github.com/unknwon/com"
+	"xorm.io/xorm"
 )
 
 func generateAndMigrateWikiGitHooks(x *xorm.Engine) (err error) {
diff --git a/models/migrations/v23.go b/models/migrations/v23.go
index 4aadf7ef0d..50dc6cd2c7 100644
--- a/models/migrations/v23.go
+++ b/models/migrations/v23.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"fmt"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 // UserOpenID is the list of all OpenID identities of a user.
diff --git a/models/migrations/v24.go b/models/migrations/v24.go
index 076c710cc3..20791d7981 100644
--- a/models/migrations/v24.go
+++ b/models/migrations/v24.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"time"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func changeGPGKeysColumns(x *xorm.Engine) error {
diff --git a/models/migrations/v25.go b/models/migrations/v25.go
index a8d746590a..da74e27c28 100644
--- a/models/migrations/v25.go
+++ b/models/migrations/v25.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"fmt"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addUserOpenIDShow(x *xorm.Engine) error {
diff --git a/models/migrations/v26.go b/models/migrations/v26.go
index 04277191f5..03ce2ef94b 100644
--- a/models/migrations/v26.go
+++ b/models/migrations/v26.go
@@ -16,8 +16,8 @@ import (
 
 	"code.gitea.io/gitea/modules/setting"
 
-	"github.com/go-xorm/xorm"
 	"github.com/unknwon/com"
+	"xorm.io/xorm"
 )
 
 func generateAndMigrateGitHookChains(x *xorm.Engine) (err error) {
diff --git a/models/migrations/v27.go b/models/migrations/v27.go
index 12e5fbcdbf..2bba0b7412 100644
--- a/models/migrations/v27.go
+++ b/models/migrations/v27.go
@@ -11,7 +11,7 @@ import (
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func convertIntervalToDuration(x *xorm.Engine) (err error) {
diff --git a/models/migrations/v28.go b/models/migrations/v28.go
index a30cbf2afb..587e944ce6 100644
--- a/models/migrations/v28.go
+++ b/models/migrations/v28.go
@@ -14,7 +14,7 @@ import (
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addRepoSize(x *xorm.Engine) (err error) {
diff --git a/models/migrations/v29.go b/models/migrations/v29.go
index eadb0f3d87..ea70a2dd77 100644
--- a/models/migrations/v29.go
+++ b/models/migrations/v29.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"fmt"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 // CommitStatus see models/status.go
diff --git a/models/migrations/v30.go b/models/migrations/v30.go
index 90047df8b6..5acdc5dac7 100644
--- a/models/migrations/v30.go
+++ b/models/migrations/v30.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"fmt"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addExternalLoginUserPK(x *xorm.Engine) error {
diff --git a/models/migrations/v31.go b/models/migrations/v31.go
index d6cea4c51b..b3aef0d665 100644
--- a/models/migrations/v31.go
+++ b/models/migrations/v31.go
@@ -8,8 +8,8 @@ import (
 	"fmt"
 	"time"
 
-	"github.com/go-xorm/xorm"
 	"xorm.io/core"
+	"xorm.io/xorm"
 )
 
 func addLoginSourceSyncEnabledColumn(x *xorm.Engine) error {
diff --git a/models/migrations/v32.go b/models/migrations/v32.go
index d209fc34f6..f5c021cccf 100644
--- a/models/migrations/v32.go
+++ b/models/migrations/v32.go
@@ -4,7 +4,7 @@
 
 package migrations
 
-import "github.com/go-xorm/xorm"
+import "xorm.io/xorm"
 
 func addUnitsToRepoTeam(x *xorm.Engine) error {
 	type Team struct {
diff --git a/models/migrations/v33.go b/models/migrations/v33.go
index 566951db96..625c5f4a53 100644
--- a/models/migrations/v33.go
+++ b/models/migrations/v33.go
@@ -10,7 +10,7 @@ import (
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func removeActionColumns(x *xorm.Engine) error {
diff --git a/models/migrations/v34.go b/models/migrations/v34.go
index 258da41c04..26f0f565f7 100644
--- a/models/migrations/v34.go
+++ b/models/migrations/v34.go
@@ -5,7 +5,7 @@
 package migrations
 
 import (
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 // Team see models/team.go
diff --git a/models/migrations/v35.go b/models/migrations/v35.go
index 7746663a40..d5059c7998 100644
--- a/models/migrations/v35.go
+++ b/models/migrations/v35.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"fmt"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addCommentIDToAction(x *xorm.Engine) error {
diff --git a/models/migrations/v36.go b/models/migrations/v36.go
index 06f76a26d6..729019925e 100644
--- a/models/migrations/v36.go
+++ b/models/migrations/v36.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"code.gitea.io/gitea/models"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func regenerateGitHooks36(x *xorm.Engine) (err error) {
diff --git a/models/migrations/v37.go b/models/migrations/v37.go
index 00653a780d..29e1c966f3 100644
--- a/models/migrations/v37.go
+++ b/models/migrations/v37.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"html"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func unescapeUserFullNames(x *xorm.Engine) (err error) {
diff --git a/models/migrations/v38.go b/models/migrations/v38.go
index 6060b70fe8..4e4e6628d3 100644
--- a/models/migrations/v38.go
+++ b/models/migrations/v38.go
@@ -9,8 +9,8 @@ import (
 
 	"code.gitea.io/gitea/models"
 
-	"github.com/go-xorm/xorm"
 	"xorm.io/core"
+	"xorm.io/xorm"
 )
 
 func removeCommitsUnitType(x *xorm.Engine) (err error) {
diff --git a/models/migrations/v39.go b/models/migrations/v39.go
index 1312cb3313..f3b32ea873 100644
--- a/models/migrations/v39.go
+++ b/models/migrations/v39.go
@@ -11,7 +11,7 @@ import (
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 // ReleaseV39 describes the added field for Release
diff --git a/models/migrations/v40.go b/models/migrations/v40.go
index fffe158bf9..944377ce9b 100644
--- a/models/migrations/v40.go
+++ b/models/migrations/v40.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"fmt"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func fixProtectedBranchCanPushValue(x *xorm.Engine) error {
diff --git a/models/migrations/v41.go b/models/migrations/v41.go
index 4de3ad4e99..928bb1cd3f 100644
--- a/models/migrations/v41.go
+++ b/models/migrations/v41.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"fmt"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func removeDuplicateUnitTypes(x *xorm.Engine) error {
diff --git a/models/migrations/v45.go b/models/migrations/v45.go
index 99baff2c8b..eb346d7b3a 100644
--- a/models/migrations/v45.go
+++ b/models/migrations/v45.go
@@ -8,7 +8,7 @@ import (
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func removeIndexColumnFromRepoUnitTable(x *xorm.Engine) (err error) {
diff --git a/models/migrations/v46.go b/models/migrations/v46.go
index b6dd059c94..3d9c1329d8 100644
--- a/models/migrations/v46.go
+++ b/models/migrations/v46.go
@@ -5,7 +5,7 @@
 package migrations
 
 import (
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func removeOrganizationWatchRepo(x *xorm.Engine) error {
diff --git a/models/migrations/v47.go b/models/migrations/v47.go
index 7a217e6f01..81f92e2f5a 100644
--- a/models/migrations/v47.go
+++ b/models/migrations/v47.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"fmt"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addDeletedBranch(x *xorm.Engine) (err error) {
diff --git a/models/migrations/v48.go b/models/migrations/v48.go
index 6cea66b5ac..6365feba89 100644
--- a/models/migrations/v48.go
+++ b/models/migrations/v48.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"fmt"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addRepoIndexerStatus(x *xorm.Engine) error {
diff --git a/models/migrations/v49.go b/models/migrations/v49.go
index 9e98de5cf2..4776125137 100644
--- a/models/migrations/v49.go
+++ b/models/migrations/v49.go
@@ -10,7 +10,7 @@ import (
 
 	"code.gitea.io/gitea/modules/setting"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addTimetracking(x *xorm.Engine) error {
diff --git a/models/migrations/v50.go b/models/migrations/v50.go
index 23b1bb526e..ddc378b432 100644
--- a/models/migrations/v50.go
+++ b/models/migrations/v50.go
@@ -10,7 +10,7 @@ import (
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func migrateProtectedBranchStruct(x *xorm.Engine) error {
diff --git a/models/migrations/v51.go b/models/migrations/v51.go
index 85e903bbe7..8dadcf3349 100644
--- a/models/migrations/v51.go
+++ b/models/migrations/v51.go
@@ -8,7 +8,7 @@ import (
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/log"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addDefaultValueToUserProhibitLogin(x *xorm.Engine) (err error) {
diff --git a/models/migrations/v52.go b/models/migrations/v52.go
index ab57d27de0..6547698d5b 100644
--- a/models/migrations/v52.go
+++ b/models/migrations/v52.go
@@ -10,7 +10,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addLFSLock(x *xorm.Engine) error {
diff --git a/models/migrations/v53.go b/models/migrations/v53.go
index 7437cace25..a3068cdb00 100644
--- a/models/migrations/v53.go
+++ b/models/migrations/v53.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"fmt"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addReactions(x *xorm.Engine) error {
diff --git a/models/migrations/v54.go b/models/migrations/v54.go
index 5194624f69..af1e287419 100644
--- a/models/migrations/v54.go
+++ b/models/migrations/v54.go
@@ -9,7 +9,7 @@ import (
 
 	"code.gitea.io/gitea/modules/timeutil"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addPullRequestOptions(x *xorm.Engine) error {
diff --git a/models/migrations/v55.go b/models/migrations/v55.go
index c20c51616e..a259e4f001 100644
--- a/models/migrations/v55.go
+++ b/models/migrations/v55.go
@@ -9,7 +9,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addModeToDeploKeys(x *xorm.Engine) error {
diff --git a/models/migrations/v56.go b/models/migrations/v56.go
index 79f8ce0ba5..4e1cafcca2 100644
--- a/models/migrations/v56.go
+++ b/models/migrations/v56.go
@@ -5,7 +5,7 @@
 package migrations
 
 import (
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func removeIsOwnerColumnFromOrgUser(x *xorm.Engine) (err error) {
diff --git a/models/migrations/v57.go b/models/migrations/v57.go
index fe4bf6b0ee..6c0ab6f496 100644
--- a/models/migrations/v57.go
+++ b/models/migrations/v57.go
@@ -9,7 +9,7 @@ import (
 
 	"code.gitea.io/gitea/modules/timeutil"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addIssueClosedTime(x *xorm.Engine) error {
diff --git a/models/migrations/v58.go b/models/migrations/v58.go
index 6ec24b08c8..0fa3bcfe2d 100644
--- a/models/migrations/v58.go
+++ b/models/migrations/v58.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"fmt"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addLabelsDescriptions(x *xorm.Engine) error {
diff --git a/models/migrations/v59.go b/models/migrations/v59.go
index 0a05495e76..d442f2569e 100644
--- a/models/migrations/v59.go
+++ b/models/migrations/v59.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"fmt"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addProtectedBranchMergeWhitelist(x *xorm.Engine) error {
diff --git a/models/migrations/v60.go b/models/migrations/v60.go
index 13ec38241a..6482e8e4a5 100644
--- a/models/migrations/v60.go
+++ b/models/migrations/v60.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"fmt"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addFsckEnabledToRepo(x *xorm.Engine) error {
diff --git a/models/migrations/v61.go b/models/migrations/v61.go
index 8d9b7e2d23..13affaf068 100644
--- a/models/migrations/v61.go
+++ b/models/migrations/v61.go
@@ -12,7 +12,7 @@ import (
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addSizeToAttachment(x *xorm.Engine) error {
diff --git a/models/migrations/v62.go b/models/migrations/v62.go
index 0c2966854b..e7f6cf6890 100644
--- a/models/migrations/v62.go
+++ b/models/migrations/v62.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"fmt"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addLastUsedPasscodeTOTP(x *xorm.Engine) error {
diff --git a/models/migrations/v63.go b/models/migrations/v63.go
index 6e7d940edc..62e8a299f6 100644
--- a/models/migrations/v63.go
+++ b/models/migrations/v63.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"fmt"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addLanguageSetting(x *xorm.Engine) error {
diff --git a/models/migrations/v64.go b/models/migrations/v64.go
index 00637ca046..623cceddbc 100644
--- a/models/migrations/v64.go
+++ b/models/migrations/v64.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"code.gitea.io/gitea/modules/timeutil"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addMultipleAssignees(x *xorm.Engine) error {
diff --git a/models/migrations/v65.go b/models/migrations/v65.go
index cc199d34e2..a87f8bc76c 100644
--- a/models/migrations/v65.go
+++ b/models/migrations/v65.go
@@ -3,7 +3,7 @@ package migrations
 import (
 	"code.gitea.io/gitea/modules/timeutil"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addU2FReg(x *xorm.Engine) error {
diff --git a/models/migrations/v66.go b/models/migrations/v66.go
index 43acfb4ea5..8e9df97fea 100644
--- a/models/migrations/v66.go
+++ b/models/migrations/v66.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"fmt"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addLoginSourceIDToPublicKeyTable(x *xorm.Engine) error {
diff --git a/models/migrations/v67.go b/models/migrations/v67.go
index 6cf3dd4d19..dee744e4d3 100644
--- a/models/migrations/v67.go
+++ b/models/migrations/v67.go
@@ -9,7 +9,7 @@ import (
 
 	"code.gitea.io/gitea/modules/setting"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func removeStaleWatches(x *xorm.Engine) error {
diff --git a/models/migrations/v68.go b/models/migrations/v68.go
index d9e80ca80e..41c1f8f71d 100644
--- a/models/migrations/v68.go
+++ b/models/migrations/v68.go
@@ -11,7 +11,7 @@ import (
 
 	"code.gitea.io/gitea/modules/log"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 var topicPattern = regexp.MustCompile(`^[a-z0-9][a-z0-9-]*$`)
diff --git a/models/migrations/v69.go b/models/migrations/v69.go
index 9a6e42e712..a08747edff 100644
--- a/models/migrations/v69.go
+++ b/models/migrations/v69.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"fmt"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func moveTeamUnitsToTeamUnitTable(x *xorm.Engine) error {
diff --git a/models/migrations/v70.go b/models/migrations/v70.go
index 4ce1d4ee53..ef8dd85d6d 100644
--- a/models/migrations/v70.go
+++ b/models/migrations/v70.go
@@ -10,7 +10,7 @@ import (
 
 	"code.gitea.io/gitea/modules/setting"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addIssueDependencies(x *xorm.Engine) (err error) {
diff --git a/models/migrations/v71.go b/models/migrations/v71.go
index 004f0a3f51..0b6aff61b2 100644
--- a/models/migrations/v71.go
+++ b/models/migrations/v71.go
@@ -11,8 +11,8 @@ import (
 	"code.gitea.io/gitea/modules/generate"
 	"code.gitea.io/gitea/modules/timeutil"
 
-	"github.com/go-xorm/xorm"
 	"golang.org/x/crypto/pbkdf2"
+	"xorm.io/xorm"
 )
 
 func addScratchHash(x *xorm.Engine) error {
diff --git a/models/migrations/v72.go b/models/migrations/v72.go
index c99b46afd2..612f58aab5 100644
--- a/models/migrations/v72.go
+++ b/models/migrations/v72.go
@@ -9,7 +9,7 @@ import (
 
 	"code.gitea.io/gitea/modules/timeutil"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addReview(x *xorm.Engine) error {
diff --git a/models/migrations/v73.go b/models/migrations/v73.go
index 1265b4519e..0c06e2ba5c 100644
--- a/models/migrations/v73.go
+++ b/models/migrations/v73.go
@@ -5,7 +5,7 @@
 package migrations
 
 import (
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addMustChangePassword(x *xorm.Engine) error {
diff --git a/models/migrations/v74.go b/models/migrations/v74.go
index 66e958c7fa..f3b38418b7 100644
--- a/models/migrations/v74.go
+++ b/models/migrations/v74.go
@@ -4,7 +4,7 @@
 
 package migrations
 
-import "github.com/go-xorm/xorm"
+import "xorm.io/xorm"
 
 func addApprovalWhitelistsToProtectedBranches(x *xorm.Engine) error {
 	type ProtectedBranch struct {
diff --git a/models/migrations/v75.go b/models/migrations/v75.go
index 58d1d34c98..208153b9b0 100644
--- a/models/migrations/v75.go
+++ b/models/migrations/v75.go
@@ -5,8 +5,8 @@
 package migrations
 
 import (
-	"github.com/go-xorm/xorm"
 	"xorm.io/builder"
+	"xorm.io/xorm"
 )
 
 func clearNonusedData(x *xorm.Engine) error {
diff --git a/models/migrations/v76.go b/models/migrations/v76.go
index e1fd6f100b..545bff64c5 100644
--- a/models/migrations/v76.go
+++ b/models/migrations/v76.go
@@ -9,7 +9,7 @@ import (
 
 	"code.gitea.io/gitea/modules/timeutil"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addPullRequestRebaseWithMerge(x *xorm.Engine) error {
diff --git a/models/migrations/v77.go b/models/migrations/v77.go
index c564d4cf54..d62fbe7fb6 100644
--- a/models/migrations/v77.go
+++ b/models/migrations/v77.go
@@ -5,7 +5,7 @@
 package migrations
 
 import (
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addUserDefaultTheme(x *xorm.Engine) error {
diff --git a/models/migrations/v78.go b/models/migrations/v78.go
index 8082996b6f..e4274ca605 100644
--- a/models/migrations/v78.go
+++ b/models/migrations/v78.go
@@ -5,7 +5,7 @@
 package migrations
 
 import (
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func renameRepoIsBareToIsEmpty(x *xorm.Engine) error {
diff --git a/models/migrations/v79.go b/models/migrations/v79.go
index e246393957..3c3e77b8db 100644
--- a/models/migrations/v79.go
+++ b/models/migrations/v79.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"code.gitea.io/gitea/modules/setting"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addCanCloseIssuesViaCommitInAnyBranch(x *xorm.Engine) error {
diff --git a/models/migrations/v80.go b/models/migrations/v80.go
index d9040da601..3c1b3315cf 100644
--- a/models/migrations/v80.go
+++ b/models/migrations/v80.go
@@ -4,7 +4,7 @@
 
 package migrations
 
-import "github.com/go-xorm/xorm"
+import "xorm.io/xorm"
 
 func addIsLockedToIssues(x *xorm.Engine) error {
 	// Issue see models/issue.go
diff --git a/models/migrations/v81.go b/models/migrations/v81.go
index 48e96508d9..271d479a64 100644
--- a/models/migrations/v81.go
+++ b/models/migrations/v81.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"fmt"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func changeU2FCounterType(x *xorm.Engine) error {
diff --git a/models/migrations/v82.go b/models/migrations/v82.go
index eb73f18343..3fb4b6c59e 100644
--- a/models/migrations/v82.go
+++ b/models/migrations/v82.go
@@ -10,7 +10,7 @@ import (
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/git"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func fixReleaseSha1OnReleaseTable(x *xorm.Engine) error {
diff --git a/models/migrations/v83.go b/models/migrations/v83.go
index cdc59292ab..6707dbdf81 100644
--- a/models/migrations/v83.go
+++ b/models/migrations/v83.go
@@ -7,7 +7,7 @@ package migrations
 import (
 	"code.gitea.io/gitea/modules/timeutil"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addUploaderIDForAttachment(x *xorm.Engine) error {
diff --git a/models/migrations/v84.go b/models/migrations/v84.go
index 4acb94b9ce..baab29fcd7 100644
--- a/models/migrations/v84.go
+++ b/models/migrations/v84.go
@@ -5,7 +5,7 @@
 package migrations
 
 import (
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addGPGKeyImport(x *xorm.Engine) error {
diff --git a/models/migrations/v85.go b/models/migrations/v85.go
index 6066d5ebe9..8c92f10b6e 100644
--- a/models/migrations/v85.go
+++ b/models/migrations/v85.go
@@ -11,7 +11,7 @@ import (
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/timeutil"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func hashAppToken(x *xorm.Engine) error {
diff --git a/models/migrations/v86.go b/models/migrations/v86.go
index 492a08c71e..39c196ca6a 100644
--- a/models/migrations/v86.go
+++ b/models/migrations/v86.go
@@ -5,7 +5,7 @@
 package migrations
 
 import (
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addHTTPMethodToWebhook(x *xorm.Engine) error {
diff --git a/models/migrations/v87.go b/models/migrations/v87.go
index c8c7011a08..6b5af5be33 100644
--- a/models/migrations/v87.go
+++ b/models/migrations/v87.go
@@ -5,7 +5,7 @@
 package migrations
 
 import (
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func addAvatarFieldToRepository(x *xorm.Engine) error {
diff --git a/models/migrations/v88.go b/models/migrations/v88.go
index fef425db0a..7318995a8c 100644
--- a/models/migrations/v88.go
+++ b/models/migrations/v88.go
@@ -8,7 +8,7 @@ import (
 	"crypto/sha1"
 	"fmt"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func hashContext(context string) string {
diff --git a/models/migrations/v89.go b/models/migrations/v89.go
index 83d0b1a8b9..a972b07b6d 100644
--- a/models/migrations/v89.go
+++ b/models/migrations/v89.go
@@ -4,7 +4,7 @@
 
 package migrations
 
-import "github.com/go-xorm/xorm"
+import "xorm.io/xorm"
 
 func addOriginalMigrationInfo(x *xorm.Engine) error {
 	// Issue see models/issue.go
diff --git a/models/migrations/v90.go b/models/migrations/v90.go
index 09aceae2f9..72f7534dc8 100644
--- a/models/migrations/v90.go
+++ b/models/migrations/v90.go
@@ -4,7 +4,7 @@
 
 package migrations
 
-import "github.com/go-xorm/xorm"
+import "xorm.io/xorm"
 
 func changeSomeColumnsLengthOfRepo(x *xorm.Engine) error {
 	type Repository struct {
diff --git a/models/migrations/v91.go b/models/migrations/v91.go
index fea71b5d3b..3c49d9b96a 100644
--- a/models/migrations/v91.go
+++ b/models/migrations/v91.go
@@ -4,7 +4,7 @@
 
 package migrations
 
-import "github.com/go-xorm/xorm"
+import "xorm.io/xorm"
 
 func addIndexOnRepositoryAndComment(x *xorm.Engine) error {
 	type Repository struct {
diff --git a/models/migrations/v92.go b/models/migrations/v92.go
index 090332f151..7ad5118176 100644
--- a/models/migrations/v92.go
+++ b/models/migrations/v92.go
@@ -5,8 +5,8 @@
 package migrations
 
 import (
-	"github.com/go-xorm/xorm"
 	"xorm.io/builder"
+	"xorm.io/xorm"
 )
 
 func removeLingeringIndexStatus(x *xorm.Engine) error {
diff --git a/models/migrations/v93.go b/models/migrations/v93.go
index 0b0441cd5d..0cb9d6631f 100644
--- a/models/migrations/v93.go
+++ b/models/migrations/v93.go
@@ -4,7 +4,7 @@
 
 package migrations
 
-import "github.com/go-xorm/xorm"
+import "xorm.io/xorm"
 
 func addEmailNotificationEnabledToUser(x *xorm.Engine) error {
 	// User see models/user.go
diff --git a/models/migrations/v94.go b/models/migrations/v94.go
index 5fe8c3fa12..8c1e33b647 100644
--- a/models/migrations/v94.go
+++ b/models/migrations/v94.go
@@ -4,7 +4,7 @@
 
 package migrations
 
-import "github.com/go-xorm/xorm"
+import "xorm.io/xorm"
 
 func addStatusCheckColumnsForProtectedBranches(x *xorm.Engine) error {
 	type ProtectedBranch struct {
diff --git a/models/migrations/v95.go b/models/migrations/v95.go
index f6e4e41c48..94787f7501 100644
--- a/models/migrations/v95.go
+++ b/models/migrations/v95.go
@@ -4,7 +4,7 @@
 
 package migrations
 
-import "github.com/go-xorm/xorm"
+import "xorm.io/xorm"
 
 func addCrossReferenceColumns(x *xorm.Engine) error {
 	// Comment see models/comment.go
diff --git a/models/migrations/v96.go b/models/migrations/v96.go
index 5c2135ffc6..b8eb201591 100644
--- a/models/migrations/v96.go
+++ b/models/migrations/v96.go
@@ -10,7 +10,7 @@ import (
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/setting"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 func deleteOrphanedAttachments(x *xorm.Engine) error {
@@ -27,7 +27,7 @@ func deleteOrphanedAttachments(x *xorm.Engine) error {
 	defer sess.Close()
 
 	err := sess.BufferSize(setting.Database.IterateBufferSize).
-		Where("`comment_id` = 0 and (`release_id` = 0 or `release_id` not in (select `id` from `release`))").Cols("uuid").
+		Where("`issue_id` = 0 and (`release_id` = 0 or `release_id` not in (select `id` from `release`))").Cols("uuid").
 		Iterate(new(Attachment),
 			func(idx int, bean interface{}) error {
 				attachment := bean.(*Attachment)
diff --git a/models/migrations/v97.go b/models/migrations/v97.go
index fa542f2ccd..8e58886e2e 100644
--- a/models/migrations/v97.go
+++ b/models/migrations/v97.go
@@ -4,7 +4,7 @@
 
 package migrations
 
-import "github.com/go-xorm/xorm"
+import "xorm.io/xorm"
 
 func addRepoAdminChangeTeamAccessColumnForUser(x *xorm.Engine) error {
 	type User struct {
diff --git a/models/migrations/v98.go b/models/migrations/v98.go
index 3b9fdbb1c5..617e1ec3d7 100644
--- a/models/migrations/v98.go
+++ b/models/migrations/v98.go
@@ -4,7 +4,7 @@
 
 package migrations
 
-import "github.com/go-xorm/xorm"
+import "xorm.io/xorm"
 
 func addOriginalAuthorOnMigratedReleases(x *xorm.Engine) error {
 	type Release struct {
diff --git a/models/migrations/v99.go b/models/migrations/v99.go
index 70a32f6566..00b4509721 100644
--- a/models/migrations/v99.go
+++ b/models/migrations/v99.go
@@ -5,21 +5,30 @@
 package migrations
 
 import (
-	"github.com/go-xorm/xorm"
+	"code.gitea.io/gitea/modules/structs"
+	"code.gitea.io/gitea/modules/timeutil"
+
+	"xorm.io/xorm"
 )
 
-func addTeamIncludesAllRepositories(x *xorm.Engine) error {
-
-	type Team struct {
-		ID                      int64 `xorm:"pk autoincr"`
-		IncludesAllRepositories bool  `xorm:"NOT NULL DEFAULT false"`
+func addTaskTable(x *xorm.Engine) error {
+	type Task struct {
+		ID             int64
+		DoerID         int64 `xorm:"index"` // operator
+		OwnerID        int64 `xorm:"index"` // repo owner id, when creating, the repoID maybe zero
+		RepoID         int64 `xorm:"index"`
+		Type           structs.TaskType
+		Status         structs.TaskStatus `xorm:"index"`
+		StartTime      timeutil.TimeStamp
+		EndTime        timeutil.TimeStamp
+		PayloadContent string             `xorm:"TEXT"`
+		Errors         string             `xorm:"TEXT"` // if task failed, saved the error reason
+		Created        timeutil.TimeStamp `xorm:"created"`
 	}
 
-	if err := x.Sync2(new(Team)); err != nil {
-		return err
+	type Repository struct {
+		Status int `xorm:"NOT NULL DEFAULT 0"`
 	}
 
-	_, err := x.Exec("UPDATE `team` SET `includes_all_repositories` = ? WHERE `name`=?",
-		true, "Owners")
-	return err
+	return x.Sync2(new(Task), new(Repository))
 }
diff --git a/models/models.go b/models/models.go
index e802a35a77..854cb33b14 100644
--- a/models/models.go
+++ b/models/models.go
@@ -14,8 +14,8 @@ import (
 
 	// Needed for the MySQL driver
 	_ "github.com/go-sql-driver/mysql"
-	"github.com/go-xorm/xorm"
 	"xorm.io/core"
+	"xorm.io/xorm"
 
 	// Needed for the Postgresql driver
 	_ "github.com/lib/pq"
@@ -112,6 +112,7 @@ func init() {
 		new(OAuth2Application),
 		new(OAuth2AuthorizationCode),
 		new(OAuth2Grant),
+		new(Task),
 	)
 
 	gonicNames := []string{"SSL", "UID"}
@@ -156,11 +157,9 @@ func SetEngine() (err error) {
 	// so use log file to instead print to stdout.
 	x.SetLogger(NewXORMLogger(setting.Database.LogSQL))
 	x.ShowSQL(setting.Database.LogSQL)
-	if setting.Database.UseMySQL {
-		x.SetMaxIdleConns(setting.Database.MaxIdleConns)
-		x.SetConnMaxLifetime(setting.Database.ConnMaxLifetime)
-	}
-
+	x.SetMaxOpenConns(setting.Database.MaxOpenConns)
+	x.SetMaxIdleConns(setting.Database.MaxIdleConns)
+	x.SetConnMaxLifetime(setting.Database.ConnMaxLifetime)
 	return nil
 }
 
diff --git a/models/oauth2_application.go b/models/oauth2_application.go
index 46355a0b3f..4df207ae16 100644
--- a/models/oauth2_application.go
+++ b/models/oauth2_application.go
@@ -16,10 +16,10 @@ import (
 	"code.gitea.io/gitea/modules/timeutil"
 
 	"github.com/dgrijalva/jwt-go"
-	"github.com/go-xorm/xorm"
 	uuid "github.com/satori/go.uuid"
 	"github.com/unknwon/com"
 	"golang.org/x/crypto/bcrypt"
+	"xorm.io/xorm"
 )
 
 // OAuth2Application represents an OAuth2 client (RFC 6749)
diff --git a/models/org.go b/models/org.go
index 07daeaeacc..78b035b101 100644
--- a/models/org.go
+++ b/models/org.go
@@ -14,9 +14,9 @@ import (
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/structs"
 
-	"github.com/go-xorm/xorm"
 	"github.com/unknwon/com"
 	"xorm.io/builder"
+	"xorm.io/xorm"
 )
 
 // IsOwnedBy returns true if given user is in the owner team.
diff --git a/models/org_team.go b/models/org_team.go
index 7430c65e4d..8ba5d2742d 100644
--- a/models/org_team.go
+++ b/models/org_team.go
@@ -14,8 +14,8 @@ import (
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 
-	"github.com/go-xorm/xorm"
 	"xorm.io/builder"
+	"xorm.io/xorm"
 )
 
 const ownerTeamName = "Owners"
@@ -341,7 +341,7 @@ func (t *Team) UnitEnabled(tp UnitType) bool {
 
 func (t *Team) unitEnabled(e Engine, tp UnitType) bool {
 	if err := t.getUnits(e); err != nil {
-		log.Warn("Error loading repository (ID: %d) units: %s", t.ID, err.Error())
+		log.Warn("Error loading team (ID: %d) units: %s", t.ID, err.Error())
 	}
 
 	for _, unit := range t.Units {
@@ -766,7 +766,7 @@ func AddTeamMember(team *Team, userID int64) error {
 
 	// Give access to team repositories.
 	for _, repo := range team.Repos {
-		if err := repo.recalculateTeamAccesses(sess, 0); err != nil {
+		if err := repo.recalculateUserAccess(sess, userID); err != nil {
 			return err
 		}
 		if setting.Service.AutoWatchNewRepos {
@@ -811,7 +811,7 @@ func removeTeamMember(e *xorm.Session, team *Team, userID int64) error {
 
 	// Delete access to team repositories.
 	for _, repo := range team.Repos {
-		if err := repo.recalculateTeamAccesses(e, 0); err != nil {
+		if err := repo.recalculateUserAccess(e, userID); err != nil {
 			return err
 		}
 
diff --git a/models/pull.go b/models/pull.go
index ff1f7b773b..c6da63ec55 100644
--- a/models/pull.go
+++ b/models/pull.go
@@ -25,8 +25,8 @@ import (
 	"code.gitea.io/gitea/modules/sync"
 	"code.gitea.io/gitea/modules/timeutil"
 
-	"github.com/go-xorm/xorm"
 	"github.com/unknwon/com"
+	"xorm.io/xorm"
 )
 
 var pullRequestQueue = sync.NewUniqueQueue(setting.Repository.PullRequestQueueLength)
@@ -66,7 +66,6 @@ type PullRequest struct {
 	HeadRepo        *Repository `xorm:"-"`
 	BaseRepoID      int64       `xorm:"INDEX"`
 	BaseRepo        *Repository `xorm:"-"`
-	HeadUserName    string
 	HeadBranch      string
 	BaseBranch      string
 	ProtectedBranch *ProtectedBranch `xorm:"-"`
@@ -79,6 +78,15 @@ type PullRequest struct {
 	MergedUnix     timeutil.TimeStamp `xorm:"updated INDEX"`
 }
 
+// MustHeadUserName returns the HeadRepo's username if failed return blank
+func (pr *PullRequest) MustHeadUserName() string {
+	if err := pr.LoadHeadRepo(); err != nil {
+		log.Error("LoadHeadRepo: %v", err)
+		return ""
+	}
+	return pr.HeadRepo.MustOwnerName()
+}
+
 // Note: don't try to get Issue because will end up recursive querying.
 func (pr *PullRequest) loadAttributes(e Engine) (err error) {
 	if pr.HasMerged && pr.Merger == nil {
@@ -102,6 +110,10 @@ func (pr *PullRequest) LoadAttributes() error {
 // LoadBaseRepo loads pull request base repository from database
 func (pr *PullRequest) LoadBaseRepo() error {
 	if pr.BaseRepo == nil {
+		if pr.HeadRepoID == pr.BaseRepoID && pr.HeadRepo != nil {
+			pr.BaseRepo = pr.HeadRepo
+			return nil
+		}
 		var repo Repository
 		if has, err := x.ID(pr.BaseRepoID).Get(&repo); err != nil {
 			return err
@@ -113,6 +125,24 @@ func (pr *PullRequest) LoadBaseRepo() error {
 	return nil
 }
 
+// LoadHeadRepo loads pull request head repository from database
+func (pr *PullRequest) LoadHeadRepo() error {
+	if pr.HeadRepo == nil {
+		if pr.HeadRepoID == pr.BaseRepoID && pr.BaseRepo != nil {
+			pr.HeadRepo = pr.BaseRepo
+			return nil
+		}
+		var repo Repository
+		if has, err := x.ID(pr.HeadRepoID).Get(&repo); err != nil {
+			return err
+		} else if !has {
+			return ErrRepoNotExist{ID: pr.BaseRepoID}
+		}
+		pr.HeadRepo = &repo
+	}
+	return nil
+}
+
 // LoadIssue loads issue information from database
 func (pr *PullRequest) LoadIssue() (err error) {
 	return pr.loadIssue(x)
@@ -152,7 +182,7 @@ func (pr *PullRequest) GetDefaultMergeMessage() string {
 			return ""
 		}
 	}
-	return fmt.Sprintf("Merge branch '%s' of %s/%s into %s", pr.HeadBranch, pr.HeadUserName, pr.HeadRepo.Name, pr.BaseBranch)
+	return fmt.Sprintf("Merge branch '%s' of %s/%s into %s", pr.HeadBranch, pr.MustHeadUserName(), pr.HeadRepo.Name, pr.BaseBranch)
 }
 
 // GetDefaultSquashMessage returns default message used when squash and merging pull request
@@ -656,11 +686,11 @@ func (pr *PullRequest) testPatch(e Engine) (err error) {
 }
 
 // NewPullRequest creates new pull request with labels for repository.
-func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []string, pr *PullRequest, patch []byte, assigneeIDs []int64) (err error) {
+func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []string, pr *PullRequest, patch []byte) (err error) {
 	// Retry several times in case INSERT fails due to duplicate key for (repo_id, index); see #7887
 	i := 0
 	for {
-		if err = newPullRequestAttempt(repo, pull, labelIDs, uuids, pr, patch, assigneeIDs); err == nil {
+		if err = newPullRequestAttempt(repo, pull, labelIDs, uuids, pr, patch); err == nil {
 			return nil
 		}
 		if !IsErrNewIssueInsert(err) {
@@ -674,7 +704,7 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
 	return fmt.Errorf("NewPullRequest: too many errors attempting to insert the new issue. Last error was: %v", err)
 }
 
-func newPullRequestAttempt(repo *Repository, pull *Issue, labelIDs []int64, uuids []string, pr *PullRequest, patch []byte, assigneeIDs []int64) (err error) {
+func newPullRequestAttempt(repo *Repository, pull *Issue, labelIDs []int64, uuids []string, pr *PullRequest, patch []byte) (err error) {
 	sess := x.NewSession()
 	defer sess.Close()
 	if err = sess.Begin(); err != nil {
@@ -687,7 +717,6 @@ func newPullRequestAttempt(repo *Repository, pull *Issue, labelIDs []int64, uuid
 		LabelIDs:    labelIDs,
 		Attachments: uuids,
 		IsPull:      true,
-		AssigneeIDs: assigneeIDs,
 	}); err != nil {
 		if IsErrUserDoesNotHaveAccessToRepo(err) || IsErrNewIssueInsert(err) {
 			return err
@@ -1072,105 +1101,6 @@ func (prs PullRequestList) InvalidateCodeComments(doer *User, repo *git.Reposito
 	return prs.invalidateCodeComments(x, doer, repo, branch)
 }
 
-func addHeadRepoTasks(prs []*PullRequest) {
-	for _, pr := range prs {
-		log.Trace("addHeadRepoTasks[%d]: composing new test task", pr.ID)
-		if err := pr.UpdatePatch(); err != nil {
-			log.Error("UpdatePatch: %v", err)
-			continue
-		} else if err := pr.PushToBaseRepo(); err != nil {
-			log.Error("PushToBaseRepo: %v", err)
-			continue
-		}
-
-		pr.AddToTaskQueue()
-	}
-}
-
-// AddTestPullRequestTask adds new test tasks by given head/base repository and head/base branch,
-// and generate new patch for testing as needed.
-func AddTestPullRequestTask(doer *User, repoID int64, branch string, isSync bool) {
-	log.Trace("AddTestPullRequestTask [head_repo_id: %d, head_branch: %s]: finding pull requests", repoID, branch)
-	prs, err := GetUnmergedPullRequestsByHeadInfo(repoID, branch)
-	if err != nil {
-		log.Error("Find pull requests [head_repo_id: %d, head_branch: %s]: %v", repoID, branch, err)
-		return
-	}
-
-	if isSync {
-		requests := PullRequestList(prs)
-		if err = requests.LoadAttributes(); err != nil {
-			log.Error("PullRequestList.LoadAttributes: %v", err)
-		}
-		if invalidationErr := checkForInvalidation(requests, repoID, doer, branch); invalidationErr != nil {
-			log.Error("checkForInvalidation: %v", invalidationErr)
-		}
-		if err == nil {
-			for _, pr := range prs {
-				pr.Issue.PullRequest = pr
-				if err = pr.Issue.LoadAttributes(); err != nil {
-					log.Error("LoadAttributes: %v", err)
-					continue
-				}
-				if err = PrepareWebhooks(pr.Issue.Repo, HookEventPullRequest, &api.PullRequestPayload{
-					Action:      api.HookIssueSynchronized,
-					Index:       pr.Issue.Index,
-					PullRequest: pr.Issue.PullRequest.APIFormat(),
-					Repository:  pr.Issue.Repo.APIFormat(AccessModeNone),
-					Sender:      doer.APIFormat(),
-				}); err != nil {
-					log.Error("PrepareWebhooks [pull_id: %v]: %v", pr.ID, err)
-					continue
-				}
-				go HookQueue.Add(pr.Issue.Repo.ID)
-			}
-		}
-
-	}
-
-	addHeadRepoTasks(prs)
-
-	log.Trace("AddTestPullRequestTask [base_repo_id: %d, base_branch: %s]: finding pull requests", repoID, branch)
-	prs, err = GetUnmergedPullRequestsByBaseInfo(repoID, branch)
-	if err != nil {
-		log.Error("Find pull requests [base_repo_id: %d, base_branch: %s]: %v", repoID, branch, err)
-		return
-	}
-	for _, pr := range prs {
-		pr.AddToTaskQueue()
-	}
-}
-
-func checkForInvalidation(requests PullRequestList, repoID int64, doer *User, branch string) error {
-	repo, err := GetRepositoryByID(repoID)
-	if err != nil {
-		return fmt.Errorf("GetRepositoryByID: %v", err)
-	}
-	gitRepo, err := git.OpenRepository(repo.RepoPath())
-	if err != nil {
-		return fmt.Errorf("git.OpenRepository: %v", err)
-	}
-	go func() {
-		err := requests.InvalidateCodeComments(doer, gitRepo, branch)
-		if err != nil {
-			log.Error("PullRequestList.InvalidateCodeComments: %v", err)
-		}
-	}()
-	return nil
-}
-
-// ChangeUsernameInPullRequests changes the name of head_user_name
-func ChangeUsernameInPullRequests(oldUserName, newUserName string) error {
-	pr := PullRequest{
-		HeadUserName: strings.ToLower(newUserName),
-	}
-	_, err := x.
-		Cols("head_user_name").
-		Where("head_user_name = ?", strings.ToLower(oldUserName)).
-		Update(pr)
-	return err
-}
-
 // checkAndUpdateStatus checks if pull request is possible to leaving checking status,
 // and set to be either conflict or mergeable.
 func (pr *PullRequest) checkAndUpdateStatus() {
diff --git a/models/pull_test.go b/models/pull_test.go
index df051d51bc..8e2436b1a2 100644
--- a/models/pull_test.go
+++ b/models/pull_test.go
@@ -232,20 +232,6 @@ func TestPullRequestList_LoadAttributes(t *testing.T) {
 
 // TODO TestAddTestPullRequestTask
 
-func TestChangeUsernameInPullRequests(t *testing.T) {
-	assert.NoError(t, PrepareTestDatabase())
-	const newUsername = "newusername"
-	assert.NoError(t, ChangeUsernameInPullRequests("user1", newUsername))
-
-	prs := make([]*PullRequest, 0, 10)
-	assert.NoError(t, x.Where("head_user_name = ?", newUsername).Find(&prs))
-	assert.Len(t, prs, 2)
-	for _, pr := range prs {
-		assert.Equal(t, newUsername, pr.HeadUserName)
-	}
-	CheckConsistencyFor(t, &PullRequest{})
-}
-
 func TestPullRequest_IsWorkInProgress(t *testing.T) {
 	assert.NoError(t, PrepareTestDatabase())
 
diff --git a/models/release.go b/models/release.go
index 243cc2fa3c..f43d81d822 100644
--- a/models/release.go
+++ b/models/release.go
@@ -12,6 +12,7 @@ import (
 
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/structs"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/timeutil"
 
@@ -366,3 +367,16 @@ func SyncReleasesWithTags(repo *Repository, gitRepo *git.Repository) error {
 	}
 	return nil
 }
+
+// UpdateReleasesMigrationsByType updates all migrated repositories' releases from gitServiceType to replace originalAuthorID to posterID
+func UpdateReleasesMigrationsByType(gitServiceType structs.GitServiceType, originalAuthorID string, posterID int64) error {
+	_, err := x.Table("release").
+		Where("repo_id IN (SELECT id FROM repository WHERE original_service_type = ?)", gitServiceType).
+		And("original_author_id = ?", originalAuthorID).
+		Update(map[string]interface{}{
+			"publisher_id":       posterID,
+			"original_author":    "",
+			"original_author_id": 0,
+		})
+	return err
+}
diff --git a/models/repo.go b/models/repo.go
index 53d568e70c..d90cdeb77f 100644
--- a/models/repo.go
+++ b/models/repo.go
@@ -32,14 +32,16 @@ import (
 	"code.gitea.io/gitea/modules/options"
 	"code.gitea.io/gitea/modules/process"
 	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/structs"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/sync"
 	"code.gitea.io/gitea/modules/timeutil"
 
-	"github.com/go-xorm/xorm"
+	"github.com/mcuadros/go-version"
 	"github.com/unknwon/com"
 	ini "gopkg.in/ini.v1"
 	"xorm.io/builder"
+	"xorm.io/xorm"
 )
 
 var repoWorkingPool = sync.NewExclusivePool()
@@ -126,18 +128,28 @@ func NewRepoContext() {
 	RemoveAllWithNotice("Clean up repository temporary data", filepath.Join(setting.AppDataPath, "tmp"))
 }
 
+// RepositoryStatus defines the status of repository
+type RepositoryStatus int
+
+// all kinds of RepositoryStatus
+const (
+	RepositoryReady         RepositoryStatus = iota // a normal repository
+	RepositoryBeingMigrated                         // repository is migrating
+)
+
 // Repository represents a git repository.
 type Repository struct {
-	ID            int64  `xorm:"pk autoincr"`
-	OwnerID       int64  `xorm:"UNIQUE(s) index"`
-	OwnerName     string `xorm:"-"`
-	Owner         *User  `xorm:"-"`
-	LowerName     string `xorm:"UNIQUE(s) INDEX NOT NULL"`
-	Name          string `xorm:"INDEX NOT NULL"`
-	Description   string `xorm:"TEXT"`
-	Website       string `xorm:"VARCHAR(2048)"`
-	OriginalURL   string `xorm:"VARCHAR(2048)"`
-	DefaultBranch string
+	ID                  int64                  `xorm:"pk autoincr"`
+	OwnerID             int64                  `xorm:"UNIQUE(s) index"`
+	OwnerName           string                 `xorm:"-"`
+	Owner               *User                  `xorm:"-"`
+	LowerName           string                 `xorm:"UNIQUE(s) INDEX NOT NULL"`
+	Name                string                 `xorm:"INDEX NOT NULL"`
+	Description         string                 `xorm:"TEXT"`
+	Website             string                 `xorm:"VARCHAR(2048)"`
+	OriginalServiceType structs.GitServiceType `xorm:"index"`
+	OriginalURL         string                 `xorm:"VARCHAR(2048)"`
+	DefaultBranch       string
 
 	NumWatches          int
 	NumStars            int
@@ -156,9 +168,9 @@ type Repository struct {
 	IsPrivate  bool `xorm:"INDEX"`
 	IsEmpty    bool `xorm:"INDEX"`
 	IsArchived bool `xorm:"INDEX"`
-
-	IsMirror bool `xorm:"INDEX"`
-	*Mirror  `xorm:"-"`
+	IsMirror   bool `xorm:"INDEX"`
+	*Mirror    `xorm:"-"`
+	Status     RepositoryStatus `xorm:"NOT NULL DEFAULT 0"`
 
 	ExternalMetas map[string]string `xorm:"-"`
 	Units         []*RepoUnit       `xorm:"-"`
@@ -197,6 +209,16 @@ func (repo *Repository) ColorFormat(s fmt.State) {
 		repo.Name)
 }
 
+// IsBeingMigrated indicates that repository is being migtated
+func (repo *Repository) IsBeingMigrated() bool {
+	return repo.Status == RepositoryBeingMigrated
+}
+
+// IsBeingCreated indicates that repository is being migrated or forked
+func (repo *Repository) IsBeingCreated() bool {
+	return repo.IsBeingMigrated()
+}
+
 // AfterLoad is invoked from XORM after setting the values of all fields of this object.
 func (repo *Repository) AfterLoad() {
 	// FIXME: use models migration to solve all at once.
@@ -735,11 +757,24 @@ func (repo *Repository) CanEnableEditor() bool {
 	return !repo.IsMirror
 }
 
+// GetReaders returns all users that have explicit read access or higher to the repository.
+func (repo *Repository) GetReaders() (_ []*User, err error) {
+	return repo.getUsersWithAccessMode(x, AccessModeRead)
+}
+
 // GetWriters returns all users that have write access to the repository.
 func (repo *Repository) GetWriters() (_ []*User, err error) {
 	return repo.getUsersWithAccessMode(x, AccessModeWrite)
 }
 
+// IsReader returns true if user has explicit read access or higher to the repository.
+func (repo *Repository) IsReader(userID int64) (bool, error) {
+	if repo.OwnerID == userID {
+		return true, nil
+	}
+	return x.Where("repo_id = ? AND user_id = ? AND mode >= ?", repo.ID, userID, AccessModeRead).Get(&Access{})
+}
+
 // getUsersWithAccessMode returns users that have at least given access mode to the repository.
 func (repo *Repository) getUsersWithAccessMode(e Engine, mode AccessMode) (_ []*User, err error) {
 	if err = repo.getOwner(e); err != nil {
@@ -871,18 +906,6 @@ func (repo *Repository) CloneLink() (cl *CloneLink) {
 	return repo.cloneLink(x, false)
 }
 
-// MigrateRepoOptions contains the repository migrate options
-type MigrateRepoOptions struct {
-	Name                 string
-	Description          string
-	OriginalURL          string
-	IsPrivate            bool
-	IsMirror             bool
-	RemoteAddr           string
-	Wiki                 bool // include wiki repository
-	SyncReleasesWithTags bool // sync releases from tags
-}
-
 /*
 	GitHub, GitLab, Gogs: *.wiki.git
 	BitBucket: *.git/wiki
@@ -902,20 +925,28 @@ func wikiRemoteURL(remote string) string {
 	return ""
 }
 
-// MigrateRepository migrates an existing repository from other project hosting.
-func MigrateRepository(doer, u *User, opts MigrateRepoOptions) (*Repository, error) {
-	repo, err := CreateRepository(doer, u, CreateRepoOptions{
-		Name:        opts.Name,
-		Description: opts.Description,
-		OriginalURL: opts.OriginalURL,
-		IsPrivate:   opts.IsPrivate,
-		IsMirror:    opts.IsMirror,
-	})
-	if err != nil {
-		return nil, err
+// CheckCreateRepository check if could created a repository
+func CheckCreateRepository(doer, u *User, name string) error {
+	if !doer.CanCreateRepo() {
+		return ErrReachLimitOfRepo{u.MaxRepoCreation}
 	}
 
-	repoPath := RepoPath(u.Name, opts.Name)
+	if err := IsUsableRepoName(name); err != nil {
+		return err
+	}
+
+	has, err := isRepositoryExist(x, u, name)
+	if err != nil {
+		return fmt.Errorf("IsRepositoryExist: %v", err)
+	} else if has {
+		return ErrRepoAlreadyExist{u.Name, name}
+	}
+	return nil
+}
+
+// MigrateRepositoryGitData starts migrating git related data after created migrating repository
+func MigrateRepositoryGitData(doer, u *User, repo *Repository, opts api.MigrateRepoOption) (*Repository, error) {
+	repoPath := RepoPath(u.Name, opts.RepoName)
 
 	if u.IsOrganization() {
 		t, err := u.GetOwnerTeam()
@@ -929,11 +960,12 @@ func MigrateRepository(doer, u *User, opts MigrateRepoOptions) (*Repository, err
 
 	migrateTimeout := time.Duration(setting.Git.Timeout.Migrate) * time.Second
 
-	if err := os.RemoveAll(repoPath); err != nil {
+	var err error
+	if err = os.RemoveAll(repoPath); err != nil {
 		return repo, fmt.Errorf("Failed to remove %s: %v", repoPath, err)
 	}
 
-	if err = git.Clone(opts.RemoteAddr, repoPath, git.CloneRepoOptions{
+	if err = git.Clone(opts.CloneAddr, repoPath, git.CloneRepoOptions{
 		Mirror:  true,
 		Quiet:   true,
 		Timeout: migrateTimeout,
@@ -942,8 +974,8 @@ func MigrateRepository(doer, u *User, opts MigrateRepoOptions) (*Repository, err
 	}
 
 	if opts.Wiki {
-		wikiPath := WikiPath(u.Name, opts.Name)
-		wikiRemotePath := wikiRemoteURL(opts.RemoteAddr)
+		wikiPath := WikiPath(u.Name, opts.RepoName)
+		wikiRemotePath := wikiRemoteURL(opts.CloneAddr)
 		if len(wikiRemotePath) > 0 {
 			if err := os.RemoveAll(wikiPath); err != nil {
 				return repo, fmt.Errorf("Failed to remove %s: %v", wikiPath, err)
@@ -973,7 +1005,7 @@ func MigrateRepository(doer, u *User, opts MigrateRepoOptions) (*Repository, err
 		return repo, fmt.Errorf("git.IsEmpty: %v", err)
 	}
 
-	if opts.SyncReleasesWithTags && !repo.IsEmpty {
+	if !opts.Releases && !repo.IsEmpty {
 		// Try to get HEAD branch and set it as default branch.
 		headBranch, err := gitRepo.GetHEADBranch()
 		if err != nil {
@@ -992,7 +1024,7 @@ func MigrateRepository(doer, u *User, opts MigrateRepoOptions) (*Repository, err
 		log.Error("Failed to update size for repository: %v", err)
 	}
 
-	if opts.IsMirror {
+	if opts.Mirror {
 		if _, err = x.InsertOne(&Mirror{
 			RepoID:         repo.ID,
 			Interval:       setting.Mirror.DefaultInterval,
@@ -1080,7 +1112,7 @@ func CleanUpMigrateInfo(repo *Repository) (*Repository, error) {
 		}
 	}
 
-	_, err := git.NewCommand("remote", "remove", "origin").RunInDir(repoPath)
+	_, err := git.NewCommand("remote", "rm", "origin").RunInDir(repoPath)
 	if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
 		return repo, fmt.Errorf("CleanUpMigrateInfo: %v", err)
 	}
@@ -1095,7 +1127,20 @@ func CleanUpMigrateInfo(repo *Repository) (*Repository, error) {
 }
 
 // initRepoCommit temporarily changes with work directory.
-func initRepoCommit(tmpPath string, sig *git.Signature) (err error) {
+func initRepoCommit(tmpPath string, u *User) (err error) {
+	commitTimeStr := time.Now().Format(time.RFC3339)
+
+	sig := u.NewGitSig()
+	// Because this may call hooks we should pass in the environment
+	env := append(os.Environ(),
+		"GIT_AUTHOR_NAME="+sig.Name,
+		"GIT_AUTHOR_EMAIL="+sig.Email,
+		"GIT_AUTHOR_DATE="+commitTimeStr,
+		"GIT_COMMITTER_NAME="+sig.Name,
+		"GIT_COMMITTER_EMAIL="+sig.Email,
+		"GIT_COMMITTER_DATE="+commitTimeStr,
+	)
+
 	var stderr string
 	if _, stderr, err = process.GetManager().ExecDir(-1,
 		tmpPath, fmt.Sprintf("initRepoCommit (git add): %s", tmpPath),
@@ -1103,10 +1148,29 @@ func initRepoCommit(tmpPath string, sig *git.Signature) (err error) {
 		return fmt.Errorf("git add: %s", stderr)
 	}
 
-	if _, stderr, err = process.GetManager().ExecDir(-1,
+	binVersion, err := git.BinVersion()
+	if err != nil {
+		return fmt.Errorf("Unable to get git version: %v", err)
+	}
+
+	args := []string{
+		"commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email),
+		"-m", "Initial commit",
+	}
+
+	if version.Compare(binVersion, "1.7.9", ">=") {
+		sign, keyID := SignInitialCommit(tmpPath, u)
+		if sign {
+			args = append(args, "-S"+keyID)
+		} else if version.Compare(binVersion, "2.0.0", ">=") {
+			args = append(args, "--no-gpg-sign")
+		}
+	}
+
+	if _, stderr, err = process.GetManager().ExecDirEnv(-1,
 		tmpPath, fmt.Sprintf("initRepoCommit (git commit): %s", tmpPath),
-		git.GitExecutable, "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email),
-		"-m", "Initial commit"); err != nil {
+		env,
+		git.GitExecutable, args...); err != nil {
 		return fmt.Errorf("git commit: %s", stderr)
 	}
 
@@ -1130,6 +1194,7 @@ type CreateRepoOptions struct {
 	IsPrivate   bool
 	IsMirror    bool
 	AutoInit    bool
+	Status      RepositoryStatus
 }
 
 func getRepoInitFile(tp, name string) ([]byte, error) {
@@ -1157,9 +1222,24 @@ func getRepoInitFile(tp, name string) ([]byte, error) {
 }
 
 func prepareRepoCommit(e Engine, repo *Repository, tmpDir, repoPath string, opts CreateRepoOptions) error {
+	commitTimeStr := time.Now().Format(time.RFC3339)
+	authorSig := repo.Owner.NewGitSig()
+
+	// Because this may call hooks we should pass in the environment
+	env := append(os.Environ(),
+		"GIT_AUTHOR_NAME="+authorSig.Name,
+		"GIT_AUTHOR_EMAIL="+authorSig.Email,
+		"GIT_AUTHOR_DATE="+commitTimeStr,
+		"GIT_COMMITTER_NAME="+authorSig.Name,
+		"GIT_COMMITTER_EMAIL="+authorSig.Email,
+		"GIT_COMMITTER_DATE="+commitTimeStr,
+	)
+
 	// Clone to temporary path and do the init commit.
-	_, stderr, err := process.GetManager().Exec(
+	_, stderr, err := process.GetManager().ExecDirEnv(
+		-1, "",
 		fmt.Sprintf("initRepository(git clone): %s", repoPath),
+		env,
 		git.GitExecutable, "clone", repoPath, tmpDir,
 	)
 	if err != nil {
@@ -1250,7 +1330,7 @@ func initRepository(e Engine, repoPath string, u *User, repo *Repository, opts C
 		}
 
 		// Apply changes and commit.
-		if err = initRepoCommit(tmpDir, u.NewGitSig()); err != nil {
+		if err = initRepoCommit(tmpDir, u); err != nil {
 			return fmt.Errorf("initRepoCommit: %v", err)
 		}
 	}
@@ -1402,6 +1482,8 @@ func CreateRepository(doer, u *User, opts CreateRepoOptions) (_ *Repository, err
 		IsPrivate:                       opts.IsPrivate,
 		IsFsckEnabled:                   !opts.IsMirror,
 		CloseIssuesViaCommitInAnyBranch: setting.Repository.DefaultCloseIssuesViaCommitsInAnyBranch,
+		Status:                          opts.Status,
+		IsEmpty:                         !opts.AutoInit,
 	}
 
 	sess := x.NewSession()
@@ -1852,6 +1934,7 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
 		&CommitStatus{RepoID: repoID},
 		&RepoIndexerStatus{RepoID: repoID},
 		&Comment{RefRepoID: repoID},
+		&Task{RepoID: repoID},
 	); err != nil {
 		return fmt.Errorf("deleteBeans: %v", err)
 	}
@@ -1942,12 +2025,11 @@ func DeleteRepository(doer *User, uid, repoID int64) error {
 		if err != nil {
 			return err
 		}
-
 		if count > 1 {
 			continue
 		}
 
-		oidPath := filepath.Join(v.Oid[0:2], v.Oid[2:4], v.Oid[4:len(v.Oid)])
+		oidPath := filepath.Join(setting.LFS.ContentPath, v.Oid[0:2], v.Oid[2:4], v.Oid[4:len(v.Oid)])
 		removeAllWithNotice(sess, "Delete orphaned LFS file", oidPath)
 	}
 
diff --git a/models/repo_activity.go b/models/repo_activity.go
index 04612ae1ef..aa5c2217e0 100644
--- a/models/repo_activity.go
+++ b/models/repo_activity.go
@@ -11,7 +11,7 @@ import (
 
 	"code.gitea.io/gitea/modules/git"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 // ActivityAuthorData represents statistical git commit count data
diff --git a/models/repo_collaboration.go b/models/repo_collaboration.go
index 40ddf6a28c..3d6447c196 100644
--- a/models/repo_collaboration.go
+++ b/models/repo_collaboration.go
@@ -41,12 +41,7 @@ func (repo *Repository) AddCollaborator(u *User) error {
 		return err
 	}
 
-	if repo.Owner.IsOrganization() {
-		err = repo.recalculateTeamAccesses(sess, 0)
-	} else {
-		err = repo.recalculateAccesses(sess)
-	}
-	if err != nil {
+	if err = repo.recalculateUserAccess(sess, u.ID); err != nil {
 		return fmt.Errorf("recalculateAccesses 'team=%v': %v", repo.Owner.IsOrganization(), err)
 	}
 
@@ -89,6 +84,18 @@ func (repo *Repository) GetCollaborators() ([]*Collaborator, error) {
 	return repo.getCollaborators(x)
 }
 
+func (repo *Repository) getCollaboration(e Engine, uid int64) (*Collaboration, error) {
+	collaboration := &Collaboration{
+		RepoID: repo.ID,
+		UserID: uid,
+	}
+	has, err := e.Get(collaboration)
+	if !has {
+		collaboration = nil
+	}
+	return collaboration, err
+}
+
 func (repo *Repository) isCollaborator(e Engine, userID int64) (bool, error) {
 	return e.Get(&Collaboration{RepoID: repo.ID, UserID: userID})
 }
diff --git a/models/repo_indexer.go b/models/repo_indexer.go
index b842a1c87f..9cc002a8ab 100644
--- a/models/repo_indexer.go
+++ b/models/repo_indexer.go
@@ -8,10 +8,12 @@ import (
 	"fmt"
 	"strconv"
 	"strings"
+	"time"
 
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/charset"
 	"code.gitea.io/gitea/modules/git"
+	"code.gitea.io/gitea/modules/graceful"
 	"code.gitea.io/gitea/modules/indexer"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
@@ -70,9 +72,30 @@ func InitRepoIndexer() {
 	if !setting.Indexer.RepoIndexerEnabled {
 		return
 	}
+	waitChannel := make(chan time.Duration)
 	repoIndexerOperationQueue = make(chan repoIndexerOperation, setting.Indexer.UpdateQueueLength)
-	indexer.InitRepoIndexer(populateRepoIndexerAsynchronously)
-	go processRepoIndexerOperationQueue()
+	go func() {
+		start := time.Now()
+		log.Info("Initializing Repository Indexer")
+		indexer.InitRepoIndexer(populateRepoIndexerAsynchronously)
+		go processRepoIndexerOperationQueue()
+		waitChannel <- time.Since(start)
+	}()
+	if setting.Indexer.StartupTimeout > 0 {
+		go func() {
+			timeout := setting.Indexer.StartupTimeout
+			if graceful.IsChild && setting.GracefulHammerTime > 0 {
+				timeout += setting.GracefulHammerTime
+			}
+			select {
+			case duration := <-waitChannel:
+				log.Info("Repository Indexer Initialization took %v", duration)
+			case <-time.After(timeout):
+				log.Fatal("Repository Indexer Initialization Timed-Out after: %v", timeout)
+			}
+		}()
+
+	}
 }
 
 // populateRepoIndexerAsynchronously asynchronously populates the repo indexer
diff --git a/models/repo_mirror.go b/models/repo_mirror.go
index 4e91ea296a..aa0ec26808 100644
--- a/models/repo_mirror.go
+++ b/models/repo_mirror.go
@@ -11,7 +11,7 @@ import (
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/timeutil"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 )
 
 // Mirror represents mirror information of a repository.
diff --git a/models/repo_permission.go b/models/repo_permission.go
index 916678d168..fad29bd169 100644
--- a/models/repo_permission.go
+++ b/models/repo_permission.go
@@ -329,10 +329,18 @@ func HasAccessUnit(user *User, repo *Repository, unitType UnitType, testMode Acc
 	return hasAccessUnit(x, user, repo, unitType, testMode)
 }
 
-// canBeAssigned return true if user could be assigned to a repo
+// CanBeAssigned return true if user can be assigned to issue or pull requests in repo
+// Currently any write access (code, issues or pr's) is assignable, to match assignee list in user interface.
 // FIXME: user could send PullRequest also could be assigned???
-func canBeAssigned(e Engine, user *User, repo *Repository) (bool, error) {
-	return hasAccessUnit(e, user, repo, UnitTypeCode, AccessModeWrite)
+func CanBeAssigned(user *User, repo *Repository, isPull bool) (bool, error) {
+	if user.IsOrganization() {
+		return false, fmt.Errorf("Organization can't be added as assignee [user_id: %d, repo_id: %d]", user.ID, repo.ID)
+	}
+	perm, err := GetUserRepoPermission(repo, user)
+	if err != nil {
+		return false, err
+	}
+	return perm.CanAccessAny(AccessModeWrite, UnitTypeCode, UnitTypeIssues, UnitTypePullRequests), nil
 }
 
 func hasAccess(e Engine, userID int64, repo *Repository) (bool, error) {
diff --git a/models/repo_sign.go b/models/repo_sign.go
new file mode 100644
index 0000000000..bac69f76a8
--- /dev/null
+++ b/models/repo_sign.go
@@ -0,0 +1,303 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package models
+
+import (
+	"strings"
+
+	"code.gitea.io/gitea/modules/git"
+	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/process"
+	"code.gitea.io/gitea/modules/setting"
+)
+
+type signingMode string
+
+const (
+	never         signingMode = "never"
+	always        signingMode = "always"
+	pubkey        signingMode = "pubkey"
+	twofa         signingMode = "twofa"
+	parentSigned  signingMode = "parentsigned"
+	baseSigned    signingMode = "basesigned"
+	headSigned    signingMode = "headsigned"
+	commitsSigned signingMode = "commitssigned"
+)
+
+func signingModeFromStrings(modeStrings []string) []signingMode {
+	returnable := make([]signingMode, 0, len(modeStrings))
+	for _, mode := range modeStrings {
+		signMode := signingMode(strings.ToLower(mode))
+		switch signMode {
+		case never:
+			return []signingMode{never}
+		case always:
+			return []signingMode{always}
+		case pubkey:
+			fallthrough
+		case twofa:
+			fallthrough
+		case parentSigned:
+			fallthrough
+		case baseSigned:
+			fallthrough
+		case headSigned:
+			fallthrough
+		case commitsSigned:
+			returnable = append(returnable, signMode)
+		}
+	}
+	if len(returnable) == 0 {
+		return []signingMode{never}
+	}
+	return returnable
+}
+
+func signingKey(repoPath string) string {
+	if setting.Repository.Signing.SigningKey == "none" {
+		return ""
+	}
+
+	if setting.Repository.Signing.SigningKey == "default" || setting.Repository.Signing.SigningKey == "" {
+		// Can ignore the error here as it means that commit.gpgsign is not set
+		value, _ := git.NewCommand("config", "--get", "commit.gpgsign").RunInDir(repoPath)
+		sign, valid := git.ParseBool(strings.TrimSpace(value))
+		if !sign || !valid {
+			return ""
+		}
+
+		signingKey, _ := git.NewCommand("config", "--get", "user.signingkey").RunInDir(repoPath)
+		return strings.TrimSpace(signingKey)
+	}
+
+	return setting.Repository.Signing.SigningKey
+}
+
+// PublicSigningKey gets the public signing key within a provided repository directory
+func PublicSigningKey(repoPath string) (string, error) {
+	signingKey := signingKey(repoPath)
+	if signingKey == "" {
+		return "", nil
+	}
+
+	content, stderr, err := process.GetManager().ExecDir(-1, repoPath,
+		"gpg --export -a", "gpg", "--export", "-a", signingKey)
+	if err != nil {
+		log.Error("Unable to get default signing key in %s: %s, %s, %v", repoPath, signingKey, stderr, err)
+		return "", err
+	}
+	return content, nil
+}
+
+// SignInitialCommit determines if we should sign the initial commit to this repository
+func SignInitialCommit(repoPath string, u *User) (bool, string) {
+	rules := signingModeFromStrings(setting.Repository.Signing.InitialCommit)
+	signingKey := signingKey(repoPath)
+	if signingKey == "" {
+		return false, ""
+	}
+
+	for _, rule := range rules {
+		switch rule {
+		case never:
+			return false, ""
+		case always:
+			break
+		case pubkey:
+			keys, err := ListGPGKeys(u.ID)
+			if err != nil || len(keys) == 0 {
+				return false, ""
+			}
+		case twofa:
+			twofa, err := GetTwoFactorByUID(u.ID)
+			if err != nil || twofa == nil {
+				return false, ""
+			}
+		}
+	}
+	return true, signingKey
+}
+
+// SignWikiCommit determines if we should sign the commits to this repository wiki
+func (repo *Repository) SignWikiCommit(u *User) (bool, string) {
+	rules := signingModeFromStrings(setting.Repository.Signing.Wiki)
+	signingKey := signingKey(repo.WikiPath())
+	if signingKey == "" {
+		return false, ""
+	}
+
+	for _, rule := range rules {
+		switch rule {
+		case never:
+			return false, ""
+		case always:
+			break
+		case pubkey:
+			keys, err := ListGPGKeys(u.ID)
+			if err != nil || len(keys) == 0 {
+				return false, ""
+			}
+		case twofa:
+			twofa, err := GetTwoFactorByUID(u.ID)
+			if err != nil || twofa == nil {
+				return false, ""
+			}
+		case parentSigned:
+			gitRepo, err := git.OpenRepository(repo.WikiPath())
+			if err != nil {
+				return false, ""
+			}
+			commit, err := gitRepo.GetCommit("HEAD")
+			if err != nil {
+				return false, ""
+			}
+			if commit.Signature == nil {
+				return false, ""
+			}
+			verification := ParseCommitWithSignature(commit)
+			if !verification.Verified {
+				return false, ""
+			}
+		}
+	}
+	return true, signingKey
+}
+
+// SignCRUDAction determines if we should sign a CRUD commit to this repository
+func (repo *Repository) SignCRUDAction(u *User, tmpBasePath, parentCommit string) (bool, string) {
+	rules := signingModeFromStrings(setting.Repository.Signing.CRUDActions)
+	signingKey := signingKey(repo.RepoPath())
+	if signingKey == "" {
+		return false, ""
+	}
+
+	for _, rule := range rules {
+		switch rule {
+		case never:
+			return false, ""
+		case always:
+			break
+		case pubkey:
+			keys, err := ListGPGKeys(u.ID)
+			if err != nil || len(keys) == 0 {
+				return false, ""
+			}
+		case twofa:
+			twofa, err := GetTwoFactorByUID(u.ID)
+			if err != nil || twofa == nil {
+				return false, ""
+			}
+		case parentSigned:
+			gitRepo, err := git.OpenRepository(tmpBasePath)
+			if err != nil {
+				return false, ""
+			}
+			commit, err := gitRepo.GetCommit(parentCommit)
+			if err != nil {
+				return false, ""
+			}
+			if commit.Signature == nil {
+				return false, ""
+			}
+			verification := ParseCommitWithSignature(commit)
+			if !verification.Verified {
+				return false, ""
+			}
+		}
+	}
+	return true, signingKey
+}
+
+// SignMerge determines if we should sign a merge commit to this repository
+func (repo *Repository) SignMerge(u *User, tmpBasePath, baseCommit, headCommit string) (bool, string) {
+	rules := signingModeFromStrings(setting.Repository.Signing.Merges)
+	signingKey := signingKey(repo.RepoPath())
+	if signingKey == "" {
+		return false, ""
+	}
+	var gitRepo *git.Repository
+	var err error
+
+	for _, rule := range rules {
+		switch rule {
+		case never:
+			return false, ""
+		case always:
+			break
+		case pubkey:
+			keys, err := ListGPGKeys(u.ID)
+			if err != nil || len(keys) == 0 {
+				return false, ""
+			}
+		case twofa:
+			twofa, err := GetTwoFactorByUID(u.ID)
+			if err != nil || twofa == nil {
+				return false, ""
+			}
+		case baseSigned:
+			if gitRepo == nil {
+				gitRepo, err = git.OpenRepository(tmpBasePath)
+				if err != nil {
+					return false, ""
+				}
+			}
+			commit, err := gitRepo.GetCommit(baseCommit)
+			if err != nil {
+				return false, ""
+			}
+			verification := ParseCommitWithSignature(commit)
+			if !verification.Verified {
+				return false, ""
+			}
+		case headSigned:
+			if gitRepo == nil {
+				gitRepo, err = git.OpenRepository(tmpBasePath)
+				if err != nil {
+					return false, ""
+				}
+			}
+			commit, err := gitRepo.GetCommit(headCommit)
+			if err != nil {
+				return false, ""
+			}
+			verification := ParseCommitWithSignature(commit)
+			if !verification.Verified {
+				return false, ""
+			}
+		case commitsSigned:
+			if gitRepo == nil {
+				gitRepo, err = git.OpenRepository(tmpBasePath)
+				if err != nil {
+					return false, ""
+				}
+			}
+			commit, err := gitRepo.GetCommit(headCommit)
+			if err != nil {
+				return false, ""
+			}
+			verification := ParseCommitWithSignature(commit)
+			if !verification.Verified {
+				return false, ""
+			}
+			// need to work out merge-base
+			mergeBaseCommit, _, err := gitRepo.GetMergeBase("", baseCommit, headCommit)
+			if err != nil {
+				return false, ""
+			}
+			commitList, err := commit.CommitsBeforeUntil(mergeBaseCommit)
+			if err != nil {
+				return false, ""
+			}
+			for e := commitList.Front(); e != nil; e = e.Next() {
+				commit = e.Value.(*git.Commit)
+				verification := ParseCommitWithSignature(commit)
+				if !verification.Verified {
+					return false, ""
+				}
+			}
+		}
+	}
+	return true, signingKey
+}
diff --git a/models/repo_unit.go b/models/repo_unit.go
index 2fc1c40fa2..a6162a65e5 100644
--- a/models/repo_unit.go
+++ b/models/repo_unit.go
@@ -9,9 +9,9 @@ import (
 
 	"code.gitea.io/gitea/modules/timeutil"
 
-	"github.com/go-xorm/xorm"
 	"github.com/unknwon/com"
 	"xorm.io/core"
+	"xorm.io/xorm"
 )
 
 // RepoUnit describes all units of a repository
diff --git a/models/review.go b/models/review.go
index 454d16ee88..58660b2e3d 100644
--- a/models/review.go
+++ b/models/review.go
@@ -10,9 +10,9 @@ import (
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/timeutil"
 
-	"github.com/go-xorm/xorm"
 	"xorm.io/builder"
 	"xorm.io/core"
+	"xorm.io/xorm"
 )
 
 // ReviewType defines the sort of feedback a review gives
diff --git a/models/ssh_key.go b/models/ssh_key.go
index b7c5b4fe6e..69699f24c1 100644
--- a/models/ssh_key.go
+++ b/models/ssh_key.go
@@ -28,10 +28,10 @@ import (
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/timeutil"
 
-	"github.com/go-xorm/xorm"
 	"github.com/unknwon/com"
 	"golang.org/x/crypto/ssh"
 	"xorm.io/builder"
+	"xorm.io/xorm"
 )
 
 const (
@@ -358,6 +358,18 @@ func appendAuthorizedKeysToFile(keys ...*PublicKey) error {
 	sshOpLocker.Lock()
 	defer sshOpLocker.Unlock()
 
+	if setting.SSH.RootPath != "" {
+		// First of ensure that the RootPath is present, and if not make it with 0700 permissions
+		// This of course doesn't guarantee that this is the right directory for authorized_keys
+		// but at least if it's supposed to be this directory and it doesn't exist and we're the
+		// right user it will at least be created properly.
+		err := os.MkdirAll(setting.SSH.RootPath, 0700)
+		if err != nil {
+			log.Error("Unable to MkdirAll(%s): %v", setting.SSH.RootPath, err)
+			return err
+		}
+	}
+
 	fPath := filepath.Join(setting.SSH.RootPath, "authorized_keys")
 	f, err := os.OpenFile(fPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
 	if err != nil {
@@ -645,6 +657,18 @@ func rewriteAllPublicKeys(e Engine) error {
 	sshOpLocker.Lock()
 	defer sshOpLocker.Unlock()
 
+	if setting.SSH.RootPath != "" {
+		// First of ensure that the RootPath is present, and if not make it with 0700 permissions
+		// This of course doesn't guarantee that this is the right directory for authorized_keys
+		// but at least if it's supposed to be this directory and it doesn't exist and we're the
+		// right user it will at least be created properly.
+		err := os.MkdirAll(setting.SSH.RootPath, 0700)
+		if err != nil {
+			log.Error("Unable to MkdirAll(%s): %v", setting.SSH.RootPath, err)
+			return err
+		}
+	}
+
 	fPath := filepath.Join(setting.SSH.RootPath, "authorized_keys")
 	tmpPath := fPath + ".tmp"
 	t, err := os.OpenFile(tmpPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600)
diff --git a/models/task.go b/models/task.go
new file mode 100644
index 0000000000..cb878d387c
--- /dev/null
+++ b/models/task.go
@@ -0,0 +1,240 @@
+// Copyright 2019 Gitea. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package models
+
+import (
+	"encoding/json"
+	"fmt"
+
+	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/migrations/base"
+	"code.gitea.io/gitea/modules/structs"
+	"code.gitea.io/gitea/modules/timeutil"
+
+	"xorm.io/builder"
+)
+
+// Task represents a task
+type Task struct {
+	ID             int64
+	DoerID         int64       `xorm:"index"` // operator
+	Doer           *User       `xorm:"-"`
+	OwnerID        int64       `xorm:"index"` // repo owner id, when creating, the repoID maybe zero
+	Owner          *User       `xorm:"-"`
+	RepoID         int64       `xorm:"index"`
+	Repo           *Repository `xorm:"-"`
+	Type           structs.TaskType
+	Status         structs.TaskStatus `xorm:"index"`
+	StartTime      timeutil.TimeStamp
+	EndTime        timeutil.TimeStamp
+	PayloadContent string             `xorm:"TEXT"`
+	Errors         string             `xorm:"TEXT"` // if task failed, saved the error reason
+	Created        timeutil.TimeStamp `xorm:"created"`
+}
+
+// LoadRepo loads repository of the task
+func (task *Task) LoadRepo() error {
+	return task.loadRepo(x)
+}
+
+func (task *Task) loadRepo(e Engine) error {
+	if task.Repo != nil {
+		return nil
+	}
+	var repo Repository
+	has, err := e.ID(task.RepoID).Get(&repo)
+	if err != nil {
+		return err
+	} else if !has {
+		return ErrRepoNotExist{
+			ID: task.RepoID,
+		}
+	}
+	task.Repo = &repo
+	return nil
+}
+
+// LoadDoer loads do user
+func (task *Task) LoadDoer() error {
+	if task.Doer != nil {
+		return nil
+	}
+
+	var doer User
+	has, err := x.ID(task.DoerID).Get(&doer)
+	if err != nil {
+		return err
+	} else if !has {
+		return ErrUserNotExist{
+			UID: task.DoerID,
+		}
+	}
+	task.Doer = &doer
+
+	return nil
+}
+
+// LoadOwner loads owner user
+func (task *Task) LoadOwner() error {
+	if task.Owner != nil {
+		return nil
+	}
+
+	var owner User
+	has, err := x.ID(task.OwnerID).Get(&owner)
+	if err != nil {
+		return err
+	} else if !has {
+		return ErrUserNotExist{
+			UID: task.OwnerID,
+		}
+	}
+	task.Owner = &owner
+
+	return nil
+}
+
+// UpdateCols updates some columns
+func (task *Task) UpdateCols(cols ...string) error {
+	_, err := x.ID(task.ID).Cols(cols...).Update(task)
+	return err
+}
+
+// MigrateConfig returns task config when migrate repository
+func (task *Task) MigrateConfig() (*structs.MigrateRepoOption, error) {
+	if task.Type == structs.TaskTypeMigrateRepo {
+		var opts structs.MigrateRepoOption
+		err := json.Unmarshal([]byte(task.PayloadContent), &opts)
+		if err != nil {
+			return nil, err
+		}
+		return &opts, nil
+	}
+	return nil, fmt.Errorf("Task type is %s, not Migrate Repo", task.Type.Name())
+}
+
+// ErrTaskDoesNotExist represents a "TaskDoesNotExist" kind of error.
+type ErrTaskDoesNotExist struct {
+	ID     int64
+	RepoID int64
+	Type   structs.TaskType
+}
+
+// IsErrTaskDoesNotExist checks if an error is a ErrTaskIsNotExist.
+func IsErrTaskDoesNotExist(err error) bool {
+	_, ok := err.(ErrTaskDoesNotExist)
+	return ok
+}
+
+func (err ErrTaskDoesNotExist) Error() string {
+	return fmt.Sprintf("task is not exist [id: %d, repo_id: %d, type: %d]",
+		err.ID, err.RepoID, err.Type)
+}
+
+// GetMigratingTask returns the migrating task by repo's id
+func GetMigratingTask(repoID int64) (*Task, error) {
+	var task = Task{
+		RepoID: repoID,
+		Type:   structs.TaskTypeMigrateRepo,
+	}
+	has, err := x.Get(&task)
+	if err != nil {
+		return nil, err
+	} else if !has {
+		return nil, ErrTaskDoesNotExist{0, repoID, task.Type}
+	}
+	return &task, nil
+}
+
+// FindTaskOptions find all tasks
+type FindTaskOptions struct {
+	Status int
+}
+
+// ToConds generates conditions for database operation.
+func (opts FindTaskOptions) ToConds() builder.Cond {
+	var cond = builder.NewCond()
+	if opts.Status >= 0 {
+		cond = cond.And(builder.Eq{"status": opts.Status})
+	}
+	return cond
+}
+
+// FindTasks find all tasks
+func FindTasks(opts FindTaskOptions) ([]*Task, error) {
+	var tasks = make([]*Task, 0, 10)
+	err := x.Where(opts.ToConds()).Find(&tasks)
+	return tasks, err
+}
+
+func createTask(e Engine, task *Task) error {
+	_, err := e.Insert(task)
+	return err
+}
+
+// CreateMigrateTask creates a migrate task
+func CreateMigrateTask(doer, u *User, opts base.MigrateOptions) (*Task, error) {
+	bs, err := json.Marshal(&opts)
+	if err != nil {
+		return nil, err
+	}
+
+	var task = Task{
+		DoerID:         doer.ID,
+		OwnerID:        u.ID,
+		Type:           structs.TaskTypeMigrateRepo,
+		Status:         structs.TaskStatusQueue,
+		PayloadContent: string(bs),
+	}
+
+	if err := createTask(x, &task); err != nil {
+		return nil, err
+	}
+
+	repo, err := CreateRepository(doer, u, CreateRepoOptions{
+		Name:        opts.RepoName,
+		Description: opts.Description,
+		OriginalURL: opts.CloneAddr,
+		IsPrivate:   opts.Private,
+		IsMirror:    opts.Mirror,
+		Status:      RepositoryBeingMigrated,
+	})
+	if err != nil {
+		task.EndTime = timeutil.TimeStampNow()
+		task.Status = structs.TaskStatusFailed
+		err2 := task.UpdateCols("end_time", "status")
+		if err2 != nil {
+			log.Error("UpdateCols Failed: %v", err2.Error())
+		}
+		return nil, err
+	}
+
+	task.RepoID = repo.ID
+	if err = task.UpdateCols("repo_id"); err != nil {
+		return nil, err
+	}
+
+	return &task, nil
+}
+
+// FinishMigrateTask updates database when migrate task finished
+func FinishMigrateTask(task *Task) error {
+	task.Status = structs.TaskStatusFinished
+	task.EndTime = timeutil.TimeStampNow()
+	sess := x.NewSession()
+	defer sess.Close()
+	if err := sess.Begin(); err != nil {
+		return err
+	}
+	if _, err := sess.ID(task.ID).Cols("status", "end_time").Update(task); err != nil {
+		return err
+	}
+	task.Repo.Status = RepositoryReady
+	if _, err := sess.ID(task.RepoID).Cols("status").Update(task.Repo); err != nil {
+		return err
+	}
+
+	return sess.Commit()
+}
diff --git a/models/unit_tests.go b/models/unit_tests.go
index b53302dad4..eb4da37fe5 100644
--- a/models/unit_tests.go
+++ b/models/unit_tests.go
@@ -17,11 +17,11 @@ import (
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/setting"
 
-	"github.com/go-xorm/xorm"
 	"github.com/stretchr/testify/assert"
 	"github.com/unknwon/com"
 	"gopkg.in/testfixtures.v2"
 	"xorm.io/core"
+	"xorm.io/xorm"
 )
 
 // NonexistentID an ID that will never exist
diff --git a/models/update.go b/models/update.go
index c6ea1a845e..5e941c22c4 100644
--- a/models/update.go
+++ b/models/update.go
@@ -22,6 +22,8 @@ const (
 	EnvPusherName   = "GITEA_PUSHER_NAME"
 	EnvPusherEmail  = "GITEA_PUSHER_EMAIL"
 	EnvPusherID     = "GITEA_PUSHER_ID"
+	EnvKeyID        = "GITEA_KEY_ID"
+	EnvIsDeployKey  = "GITEA_IS_DEPLOY_KEY"
 )
 
 // CommitToPushCommit transforms a git.Commit to PushCommit type.
diff --git a/models/user.go b/models/user.go
index 030e23c383..7aa1e143e8 100644
--- a/models/user.go
+++ b/models/user.go
@@ -33,7 +33,6 @@ import (
 	"code.gitea.io/gitea/modules/timeutil"
 	"code.gitea.io/gitea/modules/util"
 
-	"github.com/go-xorm/xorm"
 	"github.com/unknwon/com"
 	"golang.org/x/crypto/argon2"
 	"golang.org/x/crypto/bcrypt"
@@ -41,6 +40,7 @@ import (
 	"golang.org/x/crypto/scrypt"
 	"golang.org/x/crypto/ssh"
 	"xorm.io/builder"
+	"xorm.io/xorm"
 )
 
 // UserType defines the user type
@@ -994,10 +994,6 @@ func ChangeUserName(u *User, newUserName string) (err error) {
 		return ErrUserAlreadyExist{newUserName}
 	}
 
-	if err = ChangeUsernameInPullRequests(u.Name, newUserName); err != nil {
-		return fmt.Errorf("ChangeUsernameInPullRequests: %v", err)
-	}
-
 	// Do not fail if directory does not exist
 	if err = os.Rename(UserPath(u.Name), UserPath(newUserName)); err != nil && !os.IsNotExist(err) {
 		return fmt.Errorf("Rename user directory: %v", err)
@@ -1324,16 +1320,20 @@ func GetUsersByIDs(ids []int64) ([]*User, error) {
 }
 
 // GetUserIDsByNames returns a slice of ids corresponds to names.
-func GetUserIDsByNames(names []string) []int64 {
+func GetUserIDsByNames(names []string, ignoreNonExistent bool) ([]int64, error) {
 	ids := make([]int64, 0, len(names))
 	for _, name := range names {
 		u, err := GetUserByName(name)
 		if err != nil {
-			continue
+			if ignoreNonExistent {
+				continue
+			} else {
+				return nil, err
+			}
 		}
 		ids = append(ids, u.ID)
 	}
-	return ids
+	return ids, nil
 }
 
 // UserCommit represents a commit with validation of user.
diff --git a/models/user_heatmap_test.go b/models/user_heatmap_test.go
index a71202d857..f882b35247 100644
--- a/models/user_heatmap_test.go
+++ b/models/user_heatmap_test.go
@@ -17,7 +17,7 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
 		CountResult int
 		JSONResult  string
 	}{
-		{2, 1, `[{"timestamp":1540080000,"contributions":1}]`},
+		{2, 1, `[{"timestamp":1571616000,"contributions":1}]`},
 		{3, 0, `[]`},
 	}
 	// Prepare
@@ -41,7 +41,7 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
 		// Get the heatmap and compare
 		heatmap, err := GetUserHeatmapDataByUser(user)
 		assert.NoError(t, err)
-		assert.Equal(t, len(actions), len(heatmap))
+		assert.Equal(t, len(actions), len(heatmap), "invalid action count: did the test data became too old?")
 		assert.Equal(t, tc.CountResult, len(heatmap))
 
 		//Test JSON rendering
diff --git a/models/webhook.go b/models/webhook.go
index f657c18792..6f2162c799 100644
--- a/models/webhook.go
+++ b/models/webhook.go
@@ -833,6 +833,8 @@ func (t *HookTask) deliver() error {
 
 				return err
 			}
+
+			req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
 		}
 	case http.MethodGet:
 		u, err := url.Parse(t.URL)
diff --git a/models/webhook_dingtalk.go b/models/webhook_dingtalk.go
index 6a4bdaf06e..1c6c0a83b8 100644
--- a/models/webhook_dingtalk.go
+++ b/models/webhook_dingtalk.go
@@ -183,22 +183,36 @@ func getDingtalkIssuesPayload(p *api.IssuePayload) (*DingtalkPayload, error) {
 }
 
 func getDingtalkIssueCommentPayload(p *api.IssueCommentPayload) (*DingtalkPayload, error) {
-	title := fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title)
+	title := fmt.Sprintf("#%d: %s", p.Issue.Index, p.Issue.Title)
 	url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID))
 	var content string
 	switch p.Action {
 	case api.HookIssueCommentCreated:
-		title = "New comment: " + title
+		if p.IsPull {
+			title = "New comment on pull request " + title
+		} else {
+			title = "New comment on issue " + title
+		}
 		content = p.Comment.Body
 	case api.HookIssueCommentEdited:
-		title = "Comment edited: " + title
+		if p.IsPull {
+			title = "Comment edited on pull request " + title
+		} else {
+			title = "Comment edited on issue " + title
+		}
 		content = p.Comment.Body
 	case api.HookIssueCommentDeleted:
-		title = "Comment deleted: " + title
+		if p.IsPull {
+			title = "Comment deleted on pull request " + title
+		} else {
+			title = "Comment deleted on issue " + title
+		}
 		url = fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index)
 		content = p.Comment.Body
 	}
 
+	title = fmt.Sprintf("[%s] %s", p.Repository.FullName, title)
+
 	return &DingtalkPayload{
 		MsgType: "actionCard",
 		ActionCard: dingtalk.ActionCard{
@@ -282,7 +296,7 @@ func getDingtalkPullRequestApprovalPayload(p *api.PullRequestPayload, event Hook
 		}
 
 		title = fmt.Sprintf("[%s] Pull request review %s : #%d %s", p.Repository.FullName, action, p.Index, p.PullRequest.Title)
-		text = p.PullRequest.Body
+		text = p.Review.Content
 
 	}
 
diff --git a/models/webhook_discord.go b/models/webhook_discord.go
index d7a2de0d11..32039edc9d 100644
--- a/models/webhook_discord.go
+++ b/models/webhook_discord.go
@@ -75,9 +75,14 @@ func color(clr string) int {
 }
 
 var (
-	successColor = color("1ac600")
-	warnColor    = color("ffd930")
-	failedColor  = color("ff3232")
+	greenColor       = color("1ac600")
+	greenColorLight  = color("bfe5bf")
+	yellowColor      = color("ffd930")
+	greyColor        = color("4f545c")
+	purpleColor      = color("7289da")
+	orangeColor      = color("eb6420")
+	orangeColorLight = color("e68d60")
+	redColor         = color("ff3232")
 )
 
 // SetSecret sets the discord secret
@@ -104,7 +109,7 @@ func getDiscordCreatePayload(p *api.CreatePayload, meta *DiscordMeta) (*DiscordP
 			{
 				Title: title,
 				URL:   p.Repo.HTMLURL + "/src/" + refName,
-				Color: successColor,
+				Color: greenColor,
 				Author: DiscordEmbedAuthor{
 					Name:    p.Sender.UserName,
 					URL:     setting.AppURL + p.Sender.UserName,
@@ -127,7 +132,7 @@ func getDiscordDeletePayload(p *api.DeletePayload, meta *DiscordMeta) (*DiscordP
 			{
 				Title: title,
 				URL:   p.Repo.HTMLURL + "/src/" + refName,
-				Color: warnColor,
+				Color: redColor,
 				Author: DiscordEmbedAuthor{
 					Name:    p.Sender.UserName,
 					URL:     setting.AppURL + p.Sender.UserName,
@@ -149,7 +154,7 @@ func getDiscordForkPayload(p *api.ForkPayload, meta *DiscordMeta) (*DiscordPaylo
 			{
 				Title: title,
 				URL:   p.Repo.HTMLURL,
-				Color: successColor,
+				Color: greenColor,
 				Author: DiscordEmbedAuthor{
 					Name:    p.Sender.UserName,
 					URL:     setting.AppURL + p.Sender.UserName,
@@ -199,7 +204,7 @@ func getDiscordPushPayload(p *api.PushPayload, meta *DiscordMeta) (*DiscordPaylo
 				Title:       title,
 				Description: text,
 				URL:         titleLink,
-				Color:       successColor,
+				Color:       greenColor,
 				Author: DiscordEmbedAuthor{
 					Name:    p.Sender.UserName,
 					URL:     setting.AppURL + p.Sender.UserName,
@@ -218,48 +223,48 @@ func getDiscordIssuesPayload(p *api.IssuePayload, meta *DiscordMeta) (*DiscordPa
 	case api.HookIssueOpened:
 		title = fmt.Sprintf("[%s] Issue opened: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
 		text = p.Issue.Body
-		color = warnColor
+		color = orangeColor
 	case api.HookIssueClosed:
 		title = fmt.Sprintf("[%s] Issue closed: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-		color = failedColor
+		color = redColor
 		text = p.Issue.Body
 	case api.HookIssueReOpened:
 		title = fmt.Sprintf("[%s] Issue re-opened: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
 		text = p.Issue.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueEdited:
 		title = fmt.Sprintf("[%s] Issue edited: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
 		text = p.Issue.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueAssigned:
 		title = fmt.Sprintf("[%s] Issue assigned to %s: #%d %s", p.Repository.FullName,
 			p.Issue.Assignee.UserName, p.Index, p.Issue.Title)
 		text = p.Issue.Body
-		color = successColor
+		color = greenColor
 	case api.HookIssueUnassigned:
 		title = fmt.Sprintf("[%s] Issue unassigned: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
 		text = p.Issue.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueLabelUpdated:
 		title = fmt.Sprintf("[%s] Issue labels updated: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
 		text = p.Issue.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueLabelCleared:
 		title = fmt.Sprintf("[%s] Issue labels cleared: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
 		text = p.Issue.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueSynchronized:
 		title = fmt.Sprintf("[%s] Issue synchronized: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
 		text = p.Issue.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueMilestoned:
 		title = fmt.Sprintf("[%s] Issue milestone: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
 		text = p.Issue.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueDemilestoned:
 		title = fmt.Sprintf("[%s] Issue clear milestone: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
 		text = p.Issue.Body
-		color = warnColor
+		color = yellowColor
 	}
 
 	return &DiscordPayload{
@@ -282,26 +287,41 @@ func getDiscordIssuesPayload(p *api.IssuePayload, meta *DiscordMeta) (*DiscordPa
 }
 
 func getDiscordIssueCommentPayload(p *api.IssueCommentPayload, discord *DiscordMeta) (*DiscordPayload, error) {
-	title := fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title)
+	title := fmt.Sprintf("#%d: %s", p.Issue.Index, p.Issue.Title)
 	url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID))
 	content := ""
 	var color int
 	switch p.Action {
 	case api.HookIssueCommentCreated:
-		title = "New comment: " + title
+		if p.IsPull {
+			title = "New comment on pull request " + title
+			color = greenColorLight
+		} else {
+			title = "New comment on issue " + title
+			color = orangeColorLight
+		}
 		content = p.Comment.Body
-		color = successColor
 	case api.HookIssueCommentEdited:
-		title = "Comment edited: " + title
+		if p.IsPull {
+			title = "Comment edited on pull request " + title
+		} else {
+			title = "Comment edited on issue " + title
+		}
 		content = p.Comment.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueCommentDeleted:
-		title = "Comment deleted: " + title
+		if p.IsPull {
+			title = "Comment deleted on pull request " + title
+		} else {
+			title = "Comment deleted on issue " + title
+		}
 		url = fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index)
 		content = p.Comment.Body
-		color = warnColor
+		color = redColor
 	}
 
+	title = fmt.Sprintf("[%s] %s", p.Repository.FullName, title)
+
 	return &DiscordPayload{
 		Username:  discord.Username,
 		AvatarURL: discord.IconURL,
@@ -328,24 +348,24 @@ func getDiscordPullRequestPayload(p *api.PullRequestPayload, meta *DiscordMeta)
 	case api.HookIssueOpened:
 		title = fmt.Sprintf("[%s] Pull request opened: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
 		text = p.PullRequest.Body
-		color = warnColor
+		color = greenColor
 	case api.HookIssueClosed:
 		if p.PullRequest.HasMerged {
 			title = fmt.Sprintf("[%s] Pull request merged: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-			color = successColor
+			color = purpleColor
 		} else {
 			title = fmt.Sprintf("[%s] Pull request closed: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-			color = failedColor
+			color = redColor
 		}
 		text = p.PullRequest.Body
 	case api.HookIssueReOpened:
 		title = fmt.Sprintf("[%s] Pull request re-opened: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
 		text = p.PullRequest.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueEdited:
 		title = fmt.Sprintf("[%s] Pull request edited: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
 		text = p.PullRequest.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueAssigned:
 		list := make([]string, len(p.PullRequest.Assignees))
 		for i, user := range p.PullRequest.Assignees {
@@ -355,31 +375,31 @@ func getDiscordPullRequestPayload(p *api.PullRequestPayload, meta *DiscordMeta)
 			strings.Join(list, ", "),
 			p.Index, p.PullRequest.Title)
 		text = p.PullRequest.Body
-		color = successColor
+		color = greenColor
 	case api.HookIssueUnassigned:
 		title = fmt.Sprintf("[%s] Pull request unassigned: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
 		text = p.PullRequest.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueLabelUpdated:
 		title = fmt.Sprintf("[%s] Pull request labels updated: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
 		text = p.PullRequest.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueLabelCleared:
 		title = fmt.Sprintf("[%s] Pull request labels cleared: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
 		text = p.PullRequest.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueSynchronized:
 		title = fmt.Sprintf("[%s] Pull request synchronized: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
 		text = p.PullRequest.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueMilestoned:
 		title = fmt.Sprintf("[%s] Pull request milestone: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
 		text = p.PullRequest.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueDemilestoned:
 		title = fmt.Sprintf("[%s] Pull request clear milestone: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
 		text = p.PullRequest.Body
-		color = warnColor
+		color = yellowColor
 	}
 
 	return &DiscordPayload{
@@ -412,8 +432,18 @@ func getDiscordPullRequestApprovalPayload(p *api.PullRequestPayload, meta *Disco
 		}
 
 		title = fmt.Sprintf("[%s] Pull request review %s: #%d %s", p.Repository.FullName, action, p.Index, p.PullRequest.Title)
-		text = p.PullRequest.Body
-		color = warnColor
+		text = p.Review.Content
+
+		switch event {
+		case HookEventPullRequestApproved:
+			color = greenColor
+		case HookEventPullRequestRejected:
+			color = redColor
+		case HookEventPullRequestComment:
+			color = greyColor
+		default:
+			color = yellowColor
+		}
 	}
 
 	return &DiscordPayload{
@@ -442,10 +472,10 @@ func getDiscordRepositoryPayload(p *api.RepositoryPayload, meta *DiscordMeta) (*
 	case api.HookRepoCreated:
 		title = fmt.Sprintf("[%s] Repository created", p.Repository.FullName)
 		url = p.Repository.HTMLURL
-		color = successColor
+		color = greenColor
 	case api.HookRepoDeleted:
 		title = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName)
-		color = warnColor
+		color = redColor
 	}
 
 	return &DiscordPayload{
@@ -473,15 +503,15 @@ func getDiscordReleasePayload(p *api.ReleasePayload, meta *DiscordMeta) (*Discor
 	case api.HookReleasePublished:
 		title = fmt.Sprintf("[%s] Release created", p.Release.TagName)
 		url = p.Release.URL
-		color = successColor
+		color = greenColor
 	case api.HookReleaseUpdated:
 		title = fmt.Sprintf("[%s] Release updated", p.Release.TagName)
 		url = p.Release.URL
-		color = successColor
+		color = yellowColor
 	case api.HookReleaseDeleted:
 		title = fmt.Sprintf("[%s] Release deleted", p.Release.TagName)
 		url = p.Release.URL
-		color = successColor
+		color = redColor
 	}
 
 	return &DiscordPayload{
diff --git a/models/webhook_msteams.go b/models/webhook_msteams.go
index bdbcdbc9d3..e8cdcca3ca 100644
--- a/models/webhook_msteams.go
+++ b/models/webhook_msteams.go
@@ -74,7 +74,7 @@ func getMSTeamsCreatePayload(p *api.CreatePayload) (*MSTeamsPayload, error) {
 	return &MSTeamsPayload{
 		Type:       "MessageCard",
 		Context:    "https://schema.org/extensions",
-		ThemeColor: fmt.Sprintf("%x", successColor),
+		ThemeColor: fmt.Sprintf("%x", greenColor),
 		Title:      title,
 		Summary:    title,
 		Sections: []MSTeamsSection{
@@ -117,7 +117,7 @@ func getMSTeamsDeletePayload(p *api.DeletePayload) (*MSTeamsPayload, error) {
 	return &MSTeamsPayload{
 		Type:       "MessageCard",
 		Context:    "https://schema.org/extensions",
-		ThemeColor: fmt.Sprintf("%x", warnColor),
+		ThemeColor: fmt.Sprintf("%x", yellowColor),
 		Title:      title,
 		Summary:    title,
 		Sections: []MSTeamsSection{
@@ -159,7 +159,7 @@ func getMSTeamsForkPayload(p *api.ForkPayload) (*MSTeamsPayload, error) {
 	return &MSTeamsPayload{
 		Type:       "MessageCard",
 		Context:    "https://schema.org/extensions",
-		ThemeColor: fmt.Sprintf("%x", successColor),
+		ThemeColor: fmt.Sprintf("%x", greenColor),
 		Title:      title,
 		Summary:    title,
 		Sections: []MSTeamsSection{
@@ -228,7 +228,7 @@ func getMSTeamsPushPayload(p *api.PushPayload) (*MSTeamsPayload, error) {
 	return &MSTeamsPayload{
 		Type:       "MessageCard",
 		Context:    "https://schema.org/extensions",
-		ThemeColor: fmt.Sprintf("%x", successColor),
+		ThemeColor: fmt.Sprintf("%x", greenColor),
 		Title:      title,
 		Summary:    title,
 		Sections: []MSTeamsSection{
@@ -272,48 +272,48 @@ func getMSTeamsIssuesPayload(p *api.IssuePayload) (*MSTeamsPayload, error) {
 	case api.HookIssueOpened:
 		title = fmt.Sprintf("[%s] Issue opened: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
 		text = p.Issue.Body
-		color = warnColor
+		color = orangeColor
 	case api.HookIssueClosed:
 		title = fmt.Sprintf("[%s] Issue closed: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
-		color = failedColor
+		color = redColor
 		text = p.Issue.Body
 	case api.HookIssueReOpened:
 		title = fmt.Sprintf("[%s] Issue re-opened: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
 		text = p.Issue.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueEdited:
 		title = fmt.Sprintf("[%s] Issue edited: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
 		text = p.Issue.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueAssigned:
 		title = fmt.Sprintf("[%s] Issue assigned to %s: #%d %s", p.Repository.FullName,
 			p.Issue.Assignee.UserName, p.Index, p.Issue.Title)
 		text = p.Issue.Body
-		color = successColor
+		color = greenColor
 	case api.HookIssueUnassigned:
 		title = fmt.Sprintf("[%s] Issue unassigned: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
 		text = p.Issue.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueLabelUpdated:
 		title = fmt.Sprintf("[%s] Issue labels updated: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
 		text = p.Issue.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueLabelCleared:
 		title = fmt.Sprintf("[%s] Issue labels cleared: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
 		text = p.Issue.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueSynchronized:
 		title = fmt.Sprintf("[%s] Issue synchronized: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
 		text = p.Issue.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueMilestoned:
 		title = fmt.Sprintf("[%s] Issue milestone: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
 		text = p.Issue.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueDemilestoned:
 		title = fmt.Sprintf("[%s] Issue clear milestone: #%d %s", p.Repository.FullName, p.Index, p.Issue.Title)
 		text = p.Issue.Body
-		color = warnColor
+		color = yellowColor
 	}
 
 	return &MSTeamsPayload{
@@ -356,26 +356,41 @@ func getMSTeamsIssuesPayload(p *api.IssuePayload) (*MSTeamsPayload, error) {
 }
 
 func getMSTeamsIssueCommentPayload(p *api.IssueCommentPayload) (*MSTeamsPayload, error) {
-	title := fmt.Sprintf("#%d %s", p.Issue.Index, p.Issue.Title)
+	title := fmt.Sprintf("#%d: %s", p.Issue.Index, p.Issue.Title)
 	url := fmt.Sprintf("%s/issues/%d#%s", p.Repository.HTMLURL, p.Issue.Index, CommentHashTag(p.Comment.ID))
 	content := ""
 	var color int
 	switch p.Action {
 	case api.HookIssueCommentCreated:
-		title = "New comment: " + title
+		if p.IsPull {
+			title = "New comment on pull request " + title
+			color = greenColorLight
+		} else {
+			title = "New comment on issue " + title
+			color = orangeColorLight
+		}
 		content = p.Comment.Body
-		color = successColor
 	case api.HookIssueCommentEdited:
-		title = "Comment edited: " + title
+		if p.IsPull {
+			title = "Comment edited on pull request " + title
+		} else {
+			title = "Comment edited on issue " + title
+		}
 		content = p.Comment.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueCommentDeleted:
-		title = "Comment deleted: " + title
+		if p.IsPull {
+			title = "Comment deleted on pull request " + title
+		} else {
+			title = "Comment deleted on issue " + title
+		}
 		url = fmt.Sprintf("%s/issues/%d", p.Repository.HTMLURL, p.Issue.Index)
 		content = p.Comment.Body
-		color = warnColor
+		color = redColor
 	}
 
+	title = fmt.Sprintf("[%s] %s", p.Repository.FullName, title)
+
 	return &MSTeamsPayload{
 		Type:       "MessageCard",
 		Context:    "https://schema.org/extensions",
@@ -422,24 +437,24 @@ func getMSTeamsPullRequestPayload(p *api.PullRequestPayload) (*MSTeamsPayload, e
 	case api.HookIssueOpened:
 		title = fmt.Sprintf("[%s] Pull request opened: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
 		text = p.PullRequest.Body
-		color = warnColor
+		color = greenColor
 	case api.HookIssueClosed:
 		if p.PullRequest.HasMerged {
 			title = fmt.Sprintf("[%s] Pull request merged: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-			color = successColor
+			color = purpleColor
 		} else {
 			title = fmt.Sprintf("[%s] Pull request closed: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
-			color = failedColor
+			color = redColor
 		}
 		text = p.PullRequest.Body
 	case api.HookIssueReOpened:
 		title = fmt.Sprintf("[%s] Pull request re-opened: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
 		text = p.PullRequest.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueEdited:
 		title = fmt.Sprintf("[%s] Pull request edited: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
 		text = p.PullRequest.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueAssigned:
 		list := make([]string, len(p.PullRequest.Assignees))
 		for i, user := range p.PullRequest.Assignees {
@@ -449,31 +464,31 @@ func getMSTeamsPullRequestPayload(p *api.PullRequestPayload) (*MSTeamsPayload, e
 			strings.Join(list, ", "),
 			p.Index, p.PullRequest.Title)
 		text = p.PullRequest.Body
-		color = successColor
+		color = greenColor
 	case api.HookIssueUnassigned:
 		title = fmt.Sprintf("[%s] Pull request unassigned: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
 		text = p.PullRequest.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueLabelUpdated:
 		title = fmt.Sprintf("[%s] Pull request labels updated: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
 		text = p.PullRequest.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueLabelCleared:
 		title = fmt.Sprintf("[%s] Pull request labels cleared: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
 		text = p.PullRequest.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueSynchronized:
 		title = fmt.Sprintf("[%s] Pull request synchronized: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
 		text = p.PullRequest.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueMilestoned:
 		title = fmt.Sprintf("[%s] Pull request milestone: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
 		text = p.PullRequest.Body
-		color = warnColor
+		color = yellowColor
 	case api.HookIssueDemilestoned:
 		title = fmt.Sprintf("[%s] Pull request clear milestone: #%d %s", p.Repository.FullName, p.Index, p.PullRequest.Title)
 		text = p.PullRequest.Body
-		color = warnColor
+		color = yellowColor
 	}
 
 	return &MSTeamsPayload{
@@ -526,8 +541,18 @@ func getMSTeamsPullRequestApprovalPayload(p *api.PullRequestPayload, event HookE
 		}
 
 		title = fmt.Sprintf("[%s] Pull request review %s: #%d %s", p.Repository.FullName, action, p.Index, p.PullRequest.Title)
-		text = p.PullRequest.Body
-		color = warnColor
+		text = p.Review.Content
+
+		switch event {
+		case HookEventPullRequestApproved:
+			color = greenColor
+		case HookEventPullRequestRejected:
+			color = redColor
+		case HookEventPullRequestComment:
+			color = greyColor
+		default:
+			color = yellowColor
+		}
 	}
 
 	return &MSTeamsPayload{
@@ -576,10 +601,10 @@ func getMSTeamsRepositoryPayload(p *api.RepositoryPayload) (*MSTeamsPayload, err
 	case api.HookRepoCreated:
 		title = fmt.Sprintf("[%s] Repository created", p.Repository.FullName)
 		url = p.Repository.HTMLURL
-		color = successColor
+		color = greenColor
 	case api.HookRepoDeleted:
 		title = fmt.Sprintf("[%s] Repository deleted", p.Repository.FullName)
-		color = warnColor
+		color = yellowColor
 	}
 
 	return &MSTeamsPayload{
@@ -623,15 +648,15 @@ func getMSTeamsReleasePayload(p *api.ReleasePayload) (*MSTeamsPayload, error) {
 	case api.HookReleasePublished:
 		title = fmt.Sprintf("[%s] Release created", p.Release.TagName)
 		url = p.Release.URL
-		color = successColor
+		color = greenColor
 	case api.HookReleaseUpdated:
 		title = fmt.Sprintf("[%s] Release updated", p.Release.TagName)
 		url = p.Release.URL
-		color = successColor
+		color = greenColor
 	case api.HookReleaseDeleted:
 		title = fmt.Sprintf("[%s] Release deleted", p.Release.TagName)
 		url = p.Release.URL
-		color = successColor
+		color = greenColor
 	}
 
 	return &MSTeamsPayload{
diff --git a/models/wiki.go b/models/wiki.go
index 0460e0f079..858fe1d6d0 100644
--- a/models/wiki.go
+++ b/models/wiki.go
@@ -205,6 +205,13 @@ func (repo *Repository) updateWikiPage(doer *User, oldWikiName, newWikiName, con
 	commitTreeOpts := git.CommitTreeOpts{
 		Message: message,
 	}
+
+	sign, signingKey := repo.SignWikiCommit(doer)
+	if sign {
+		commitTreeOpts.KeyID = signingKey
+	} else {
+		commitTreeOpts.NoGPGSign = true
+	}
 	if hasMasterBranch {
 		commitTreeOpts.Parents = []string{"HEAD"}
 	}
@@ -307,11 +314,19 @@ func (repo *Repository) DeleteWikiPage(doer *User, wikiName string) (err error)
 		return err
 	}
 	message := "Delete page '" + wikiName + "'"
-
-	commitHash, err := gitRepo.CommitTree(doer.NewGitSig(), tree, git.CommitTreeOpts{
+	commitTreeOpts := git.CommitTreeOpts{
 		Message: message,
 		Parents: []string{"HEAD"},
-	})
+	}
+
+	sign, signingKey := repo.SignWikiCommit(doer)
+	if sign {
+		commitTreeOpts.KeyID = signingKey
+	} else {
+		commitTreeOpts.NoGPGSign = true
+	}
+
+	commitHash, err := gitRepo.CommitTree(doer.NewGitSig(), tree, commitTreeOpts)
 	if err != nil {
 		return err
 	}
diff --git a/modules/auth/auth.go b/modules/auth/auth.go
index 624bb15cbf..1ba149f0f8 100644
--- a/modules/auth/auth.go
+++ b/modules/auth/auth.go
@@ -224,6 +224,9 @@ func SignedInUser(ctx *macaron.Context, sess session.Store) (*models.User, bool)
 			}
 
 			if u == nil {
+				if !setting.Service.EnableBasicAuth {
+					return nil, false
+				}
 				u, err = models.UserSignIn(uname, passwd)
 				if err != nil {
 					if !models.IsErrUserNotExist(err) {
diff --git a/modules/auth/oauth2/oauth2.go b/modules/auth/oauth2/oauth2.go
index 242254e600..20dfb15e81 100644
--- a/modules/auth/oauth2/oauth2.go
+++ b/modules/auth/oauth2/oauth2.go
@@ -11,7 +11,6 @@ import (
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 
-	"github.com/go-xorm/xorm"
 	"github.com/lafriks/xormstore"
 	"github.com/markbates/goth"
 	"github.com/markbates/goth/gothic"
@@ -26,6 +25,7 @@ import (
 	"github.com/markbates/goth/providers/openidConnect"
 	"github.com/markbates/goth/providers/twitter"
 	"github.com/satori/go.uuid"
+	"xorm.io/xorm"
 )
 
 var (
diff --git a/modules/auth/org.go b/modules/auth/org.go
index 9384d3571d..028b05427a 100644
--- a/modules/auth/org.go
+++ b/modules/auth/org.go
@@ -22,8 +22,9 @@ import (
 
 // CreateOrgForm form for creating organization
 type CreateOrgForm struct {
-	OrgName    string `binding:"Required;AlphaDashDot;MaxSize(40)" locale:"org.org_name_holder"`
-	Visibility structs.VisibleType
+	OrgName                   string `binding:"Required;AlphaDashDot;MaxSize(40)" locale:"org.org_name_holder"`
+	Visibility                structs.VisibleType
+	RepoAdminChangeTeamAccess bool
 }
 
 // Validate validates the fields
diff --git a/modules/auth/repo_form.go b/modules/auth/repo_form.go
index 8d10fc1570..2280666114 100644
--- a/modules/auth/repo_form.go
+++ b/modules/auth/repo_form.go
@@ -152,6 +152,7 @@ type ProtectBranchForm struct {
 	EnableWhitelist         bool
 	WhitelistUsers          string
 	WhitelistTeams          string
+	WhitelistDeployKeys     bool
 	EnableMergeWhitelist    bool
 	MergeWhitelistUsers     string
 	MergeWhitelistTeams     string
@@ -557,7 +558,7 @@ func (f *NewWikiForm) Validate(ctx *macaron.Context, errs binding.Errors) bindin
 // EditRepoFileForm form for changing repository file
 type EditRepoFileForm struct {
 	TreePath      string `binding:"Required;MaxSize(500)"`
-	Content       string `binding:"Required"`
+	Content       string
 	CommitSummary string `binding:"MaxSize(100)"`
 	CommitMessage string
 	CommitChoice  string `binding:"Required;MaxSize(50)"`
diff --git a/modules/charset/charset_test.go b/modules/charset/charset_test.go
index 3c77f12789..a81a6e03ee 100644
--- a/modules/charset/charset_test.go
+++ b/modules/charset/charset_test.go
@@ -179,7 +179,8 @@ func TestToUTF8DropErrors(t *testing.T) {
 
 	// "Hola, así cómo ños"
 	res = ToUTF8DropErrors([]byte{0x48, 0x6F, 0x6C, 0x61, 0x2C, 0x20, 0x61, 0x73, 0xED, 0x20, 0x63, 0xF3, 0x6D, 0x6F, 0x20, 0xF1, 0x6F, 0x73})
-	assert.Equal(t, []byte{0x48, 0x6F, 0x6C, 0x61, 0x2C, 0x20, 0x61, 0x73, 0xC3, 0xAD, 0x20, 0x63, 0xC3, 0xB3, 0x6D, 0x6F, 0x20, 0xC3, 0xB1, 0x6F, 0x73}, res)
+	assert.Equal(t, []byte{0x48, 0x6F, 0x6C, 0x61, 0x2C, 0x20, 0x61, 0x73}, res[:8])
+	assert.Equal(t, []byte{0x73}, res[len(res)-1:])
 
 	// "Hola, así cómo "
 	minmatch := []byte{0x48, 0x6F, 0x6C, 0x61, 0x2C, 0x20, 0x61, 0x73, 0xC3, 0xAD, 0x20, 0x63, 0xC3, 0xB3, 0x6D, 0x6F, 0x20}
diff --git a/modules/context/org.go b/modules/context/org.go
index 4867474334..10791c9d01 100644
--- a/modules/context/org.go
+++ b/modules/context/org.go
@@ -63,7 +63,7 @@ func HandleOrgAssignment(ctx *Context, args ...bool) {
 
 	// Force redirection when username is actually a user.
 	if !org.IsOrganization() {
-		ctx.Redirect("/" + org.Name)
+		ctx.Redirect(setting.AppSubURL + "/" + org.Name)
 		return
 	}
 
diff --git a/modules/context/repo.go b/modules/context/repo.go
index 3caf583f83..8a9c9e4b8c 100644
--- a/modules/context/repo.go
+++ b/modules/context/repo.go
@@ -19,8 +19,8 @@ import (
 	"code.gitea.io/gitea/modules/setting"
 
 	"gitea.com/macaron/macaron"
+	"github.com/editorconfig/editorconfig-core-go/v2"
 	"github.com/unknwon/com"
-	"gopkg.in/editorconfig/editorconfig-core-go.v1"
 )
 
 // PullRequest contains informations to make a pull request
@@ -146,6 +146,9 @@ func (r *Repository) FileExists(path string, branch string) (bool, error) {
 // GetEditorconfig returns the .editorconfig definition if found in the
 // HEAD of the default repo branch.
 func (r *Repository) GetEditorconfig() (*editorconfig.Editorconfig, error) {
+	if r.GitRepo == nil {
+		return nil, nil
+	}
 	commit, err := r.GitRepo.GetBranchCommit(r.Repository.DefaultBranch)
 	if err != nil {
 		return nil, err
@@ -233,7 +236,7 @@ func RedirectToRepo(ctx *Context, redirectRepoID int64) {
 	if ctx.Req.URL.RawQuery != "" {
 		redirectPath += "?" + ctx.Req.URL.RawQuery
 	}
-	ctx.Redirect(redirectPath)
+	ctx.Redirect(path.Join(setting.AppSubURL, redirectPath))
 }
 
 func repoAssignment(ctx *Context, repo *models.Repository) {
@@ -358,12 +361,6 @@ func RepoAssignment() macaron.Handler {
 			return
 		}
 
-		gitRepo, err := git.OpenRepository(models.RepoPath(userName, repoName))
-		if err != nil {
-			ctx.ServerError("RepoAssignment Invalid repo "+models.RepoPath(userName, repoName), err)
-			return
-		}
-		ctx.Repo.GitRepo = gitRepo
 		ctx.Repo.RepoLink = repo.Link()
 		ctx.Data["RepoLink"] = ctx.Repo.RepoLink
 		ctx.Data["RepoRelPath"] = ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name
@@ -373,13 +370,6 @@ func RepoAssignment() macaron.Handler {
 			ctx.Data["RepoExternalIssuesLink"] = unit.ExternalTrackerConfig().ExternalTrackerURL
 		}
 
-		tags, err := ctx.Repo.GitRepo.GetTags()
-		if err != nil {
-			ctx.ServerError("GetTags", err)
-			return
-		}
-		ctx.Data["Tags"] = tags
-
 		count, err := models.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, models.FindReleasesOptions{
 			IncludeDrafts: false,
 			IncludeTags:   true,
@@ -424,13 +414,32 @@ func RepoAssignment() macaron.Handler {
 			}
 		}
 
-		// repo is empty and display enable
+		// Disable everything when the repo is being created
+		if ctx.Repo.Repository.IsBeingCreated() {
+			ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
+			return
+		}
+
+		gitRepo, err := git.OpenRepository(models.RepoPath(userName, repoName))
+		if err != nil {
+			ctx.ServerError("RepoAssignment Invalid repo "+models.RepoPath(userName, repoName), err)
+			return
+		}
+		ctx.Repo.GitRepo = gitRepo
+
+		// Stop at this point when the repo is empty.
 		if ctx.Repo.Repository.IsEmpty {
 			ctx.Data["BranchName"] = ctx.Repo.Repository.DefaultBranch
 			return
 		}
 
-		ctx.Data["TagName"] = ctx.Repo.TagName
+		tags, err := ctx.Repo.GitRepo.GetTags()
+		if err != nil {
+			ctx.ServerError("GetTags", err)
+			return
+		}
+		ctx.Data["Tags"] = tags
+
 		brs, err := ctx.Repo.GitRepo.GetBranches()
 		if err != nil {
 			ctx.ServerError("GetBranches", err)
@@ -439,6 +448,8 @@ func RepoAssignment() macaron.Handler {
 		ctx.Data["Branches"] = brs
 		ctx.Data["BranchesCount"] = len(brs)
 
+		ctx.Data["TagName"] = ctx.Repo.TagName
+
 		// If not branch selected, try default one.
 		// If default branch doesn't exists, fall back to some other branch.
 		if len(ctx.Repo.BranchName) == 0 {
diff --git a/modules/cron/cron.go b/modules/cron/cron.go
index 089f0fa767..795fafb51f 100644
--- a/modules/cron/cron.go
+++ b/modules/cron/cron.go
@@ -10,6 +10,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/migrations"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/sync"
 	mirror_service "code.gitea.io/gitea/services/mirror"
@@ -18,12 +19,13 @@ import (
 )
 
 const (
-	mirrorUpdate           = "mirror_update"
-	gitFsck                = "git_fsck"
-	checkRepos             = "check_repos"
-	archiveCleanup         = "archive_cleanup"
-	syncExternalUsers      = "sync_external_users"
-	deletedBranchesCleanup = "deleted_branches_cleanup"
+	mirrorUpdate            = "mirror_update"
+	gitFsck                 = "git_fsck"
+	checkRepos              = "check_repos"
+	archiveCleanup          = "archive_cleanup"
+	syncExternalUsers       = "sync_external_users"
+	deletedBranchesCleanup  = "deleted_branches_cleanup"
+	updateMigrationPosterID = "update_migration_post_id"
 )
 
 var c = cron.New()
@@ -117,6 +119,15 @@ func NewContext() {
 			go WithUnique(deletedBranchesCleanup, models.RemoveOldDeletedBranches)()
 		}
 	}
+
+	entry, err = c.AddFunc("Update migrated repositories' issues and comments' posterid", setting.Cron.UpdateMigrationPosterID.Schedule, WithUnique(updateMigrationPosterID, migrations.UpdateMigrationPosterID))
+	if err != nil {
+		log.Fatal("Cron[Update migrated repositories]: %v", err)
+	}
+	entry.Prev = time.Now()
+	entry.ExecTimes++
+	go WithUnique(updateMigrationPosterID, migrations.UpdateMigrationPosterID)()
+
 	c.Start()
 }
 
diff --git a/modules/git/commit.go b/modules/git/commit.go
index eb442f988d..45b943e79e 100644
--- a/modules/git/commit.go
+++ b/modules/git/commit.go
@@ -498,3 +498,11 @@ func GetFullCommitID(repoPath, shortID string) (string, error) {
 	}
 	return strings.TrimSpace(commitID), nil
 }
+
+// GetRepositoryDefaultPublicGPGKey returns the default public key for this commit
+func (c *Commit) GetRepositoryDefaultPublicGPGKey(forceUpdate bool) (*GPGSettings, error) {
+	if c.repo == nil {
+		return nil, nil
+	}
+	return c.repo.GetDefaultPublicGPGKey(forceUpdate)
+}
diff --git a/modules/git/commit_info.go b/modules/git/commit_info.go
index d8bf88a47c..e74ddbfb05 100644
--- a/modules/git/commit_info.go
+++ b/modules/git/commit_info.go
@@ -72,6 +72,7 @@ func (tes Entries) GetCommitsInfo(commit *Commit, treePath string, cache LastCom
 		treeCommit = commit
 	} else if rev, ok := revs[""]; ok {
 		treeCommit = convertCommit(rev)
+		treeCommit.repo = commit.repo
 	}
 	return commitsInfo, treeCommit, nil
 }
diff --git a/modules/git/repo.go b/modules/git/repo.go
index 1a9112132f..dd886f3a2e 100644
--- a/modules/git/repo.go
+++ b/modules/git/repo.go
@@ -32,6 +32,16 @@ type Repository struct {
 
 	gogitRepo    *gogit.Repository
 	gogitStorage *filesystem.Storage
+	gpgSettings  *GPGSettings
+}
+
+// GPGSettings represents the default GPG settings for this repository
+type GPGSettings struct {
+	Sign             bool
+	KeyID            string
+	Email            string
+	Name             string
+	PublicKeyContent string
 }
 
 const prettyLogFormat = `--pretty=format:%H`
diff --git a/modules/git/repo_branch.go b/modules/git/repo_branch.go
index 9209f4a764..a2bf9ac973 100644
--- a/modules/git/repo_branch.go
+++ b/modules/git/repo_branch.go
@@ -28,8 +28,14 @@ func IsBranchExist(repoPath, name string) bool {
 
 // IsBranchExist returns true if given branch exists in current repository.
 func (repo *Repository) IsBranchExist(name string) bool {
-	_, err := repo.gogitRepo.Reference(plumbing.ReferenceName(BranchPrefix+name), true)
-	return err == nil
+	if name == "" {
+		return false
+	}
+	reference, err := repo.gogitRepo.Reference(plumbing.ReferenceName(BranchPrefix+name), true)
+	if err != nil {
+		return false
+	}
+	return reference.Type() != plumbing.InvalidReference
 }
 
 // Branch represents a Git branch.
@@ -165,7 +171,7 @@ func (repo *Repository) AddRemote(name, url string, fetch bool) error {
 
 // RemoveRemote removes a remote from repository.
 func (repo *Repository) RemoveRemote(name string) error {
-	_, err := NewCommand("remote", "remove", name).RunInDir(repo.Path)
+	_, err := NewCommand("remote", "rm", name).RunInDir(repo.Path)
 	return err
 }
 
diff --git a/modules/git/repo_gpg.go b/modules/git/repo_gpg.go
new file mode 100644
index 0000000000..b4c3f3b431
--- /dev/null
+++ b/modules/git/repo_gpg.go
@@ -0,0 +1,59 @@
+// Copyright 2015 The Gogs Authors. All rights reserved.
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package git
+
+import (
+	"fmt"
+	"strings"
+
+	"code.gitea.io/gitea/modules/process"
+)
+
+// LoadPublicKeyContent will load the key from gpg
+func (gpgSettings *GPGSettings) LoadPublicKeyContent() error {
+	content, stderr, err := process.GetManager().Exec(
+		"gpg -a --export",
+		"gpg", "-a", "--export", gpgSettings.KeyID)
+	if err != nil {
+		return fmt.Errorf("Unable to get default signing key: %s, %s, %v", gpgSettings.KeyID, stderr, err)
+	}
+	gpgSettings.PublicKeyContent = content
+	return nil
+}
+
+// GetDefaultPublicGPGKey will return and cache the default public GPG settings for this repository
+func (repo *Repository) GetDefaultPublicGPGKey(forceUpdate bool) (*GPGSettings, error) {
+	if repo.gpgSettings != nil && !forceUpdate {
+		return repo.gpgSettings, nil
+	}
+
+	gpgSettings := &GPGSettings{
+		Sign: true,
+	}
+
+	value, _ := NewCommand("config", "--get", "commit.gpgsign").RunInDir(repo.Path)
+	sign, valid := ParseBool(strings.TrimSpace(value))
+	if !sign || !valid {
+		gpgSettings.Sign = false
+		repo.gpgSettings = gpgSettings
+		return gpgSettings, nil
+	}
+
+	signingKey, _ := NewCommand("config", "--get", "user.signingkey").RunInDir(repo.Path)
+	gpgSettings.KeyID = strings.TrimSpace(signingKey)
+
+	defaultEmail, _ := NewCommand("config", "--get", "user.email").RunInDir(repo.Path)
+	gpgSettings.Email = strings.TrimSpace(defaultEmail)
+
+	defaultName, _ := NewCommand("config", "--get", "user.name").RunInDir(repo.Path)
+	gpgSettings.Name = strings.TrimSpace(defaultName)
+
+	if err := gpgSettings.LoadPublicKeyContent(); err != nil {
+		return nil, err
+	}
+	repo.gpgSettings = gpgSettings
+	return repo.gpgSettings, nil
+}
diff --git a/modules/git/repo_tree.go b/modules/git/repo_tree.go
index b31e4330cd..8f91f4efac 100644
--- a/modules/git/repo_tree.go
+++ b/modules/git/repo_tree.go
@@ -6,10 +6,13 @@
 package git
 
 import (
+	"bytes"
 	"fmt"
 	"os"
 	"strings"
 	"time"
+
+	"github.com/mcuadros/go-version"
 )
 
 func (repo *Repository) getTree(id SHA1) (*Tree, error) {
@@ -53,14 +56,20 @@ func (repo *Repository) GetTree(idStr string) (*Tree, error) {
 
 // CommitTreeOpts represents the possible options to CommitTree
 type CommitTreeOpts struct {
-	Parents   []string
-	Message   string
-	KeyID     string
-	NoGPGSign bool
+	Parents    []string
+	Message    string
+	KeyID      string
+	NoGPGSign  bool
+	AlwaysSign bool
 }
 
 // CommitTree creates a commit from a given tree id for the user with provided message
 func (repo *Repository) CommitTree(sig *Signature, tree *Tree, opts CommitTreeOpts) (SHA1, error) {
+	binVersion, err := BinVersion()
+	if err != nil {
+		return SHA1{}, err
+	}
+
 	commitTimeStr := time.Now().Format(time.RFC3339)
 
 	// Because this may call hooks we should pass in the environment
@@ -78,20 +87,24 @@ func (repo *Repository) CommitTree(sig *Signature, tree *Tree, opts CommitTreeOp
 		cmd.AddArguments("-p", parent)
 	}
 
-	cmd.AddArguments("-m", opts.Message)
+	messageBytes := new(bytes.Buffer)
+	_, _ = messageBytes.WriteString(opts.Message)
+	_, _ = messageBytes.WriteString("\n")
 
-	if opts.KeyID != "" {
+	if version.Compare(binVersion, "1.7.9", ">=") && (opts.KeyID != "" || opts.AlwaysSign) {
 		cmd.AddArguments(fmt.Sprintf("-S%s", opts.KeyID))
 	}
 
-	if opts.NoGPGSign {
+	if version.Compare(binVersion, "2.0.0", ">=") && opts.NoGPGSign {
 		cmd.AddArguments("--no-gpg-sign")
 	}
 
-	res, err := cmd.RunInDirWithEnv(repo.Path, env)
+	stdout := new(bytes.Buffer)
+	stderr := new(bytes.Buffer)
+	err = cmd.RunInDirTimeoutEnvFullPipeline(env, -1, repo.Path, stdout, stderr, messageBytes)
 
 	if err != nil {
-		return SHA1{}, err
+		return SHA1{}, concatenateError(err, stderr.String())
 	}
-	return NewIDFromString(strings.TrimSpace(res))
+	return NewIDFromString(strings.TrimSpace(stdout.String()))
 }
diff --git a/modules/git/utils.go b/modules/git/utils.go
index 83cd21f34e..e791f16041 100644
--- a/modules/git/utils.go
+++ b/modules/git/utils.go
@@ -7,6 +7,7 @@ package git
 import (
 	"fmt"
 	"os"
+	"strconv"
 	"strings"
 	"sync"
 )
@@ -86,3 +87,30 @@ func RefEndName(refStr string) string {
 
 	return refStr
 }
+
+// ParseBool returns the boolean value represented by the string as per git's git_config_bool
+// true will be returned for the result if the string is empty, but valid will be false.
+// "true", "yes", "on" are all true, true
+// "false", "no", "off" are all false, true
+// 0 is false, true
+// Any other integer is true, true
+// Anything else will return false, false
+func ParseBool(value string) (result bool, valid bool) {
+	// Empty strings are true but invalid
+	if len(value) == 0 {
+		return true, false
+	}
+	// These are the git expected true and false values
+	if strings.EqualFold(value, "true") || strings.EqualFold(value, "yes") || strings.EqualFold(value, "on") {
+		return true, true
+	}
+	if strings.EqualFold(value, "false") || strings.EqualFold(value, "no") || strings.EqualFold(value, "off") {
+		return false, true
+	}
+	// Try a number
+	intValue, err := strconv.ParseInt(value, 10, 32)
+	if err != nil {
+		return false, false
+	}
+	return intValue != 0, true
+}
diff --git a/modules/graceful/cleanup.go b/modules/graceful/cleanup.go
new file mode 100644
index 0000000000..84355a9a70
--- /dev/null
+++ b/modules/graceful/cleanup.go
@@ -0,0 +1,40 @@
+// +build !windows
+
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package graceful
+
+import "sync"
+
+var cleanupWaitGroup sync.WaitGroup
+
+func init() {
+	cleanupWaitGroup = sync.WaitGroup{}
+
+	// There are three places that could inherit sockets:
+	//
+	// * HTTP or HTTPS main listener
+	// * HTTP redirection fallback
+	// * SSH
+	//
+	// If you add an additional place you must increment this number
+	// and add a function to call InformCleanup if it's not going to be used
+	cleanupWaitGroup.Add(3)
+
+	// Wait till we're done getting all of the listeners and then close
+	// the unused ones
+	go func() {
+		cleanupWaitGroup.Wait()
+		// Ignore the error here there's not much we can do with it
+		// They're logged in the CloseProvidedListeners function
+		_ = CloseProvidedListeners()
+	}()
+}
+
+// InformCleanup tells the cleanup wait group that we have either taken a listener
+// or will not be taking a listener
+func InformCleanup() {
+	cleanupWaitGroup.Done()
+}
diff --git a/modules/graceful/graceful_windows.go b/modules/graceful/graceful_windows.go
new file mode 100644
index 0000000000..281b255fb5
--- /dev/null
+++ b/modules/graceful/graceful_windows.go
@@ -0,0 +1,16 @@
+// +build windows
+
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+// This code is heavily inspired by the archived gofacebook/gracenet/net.go handler
+
+package graceful
+
+// This file contains shims for windows builds
+const IsChild = false
+
+// WaitForServers waits for all running servers to finish
+func WaitForServers() {
+
+}
diff --git a/modules/graceful/net.go b/modules/graceful/net.go
new file mode 100644
index 0000000000..af484641c6
--- /dev/null
+++ b/modules/graceful/net.go
@@ -0,0 +1,211 @@
+// +build !windows
+
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+// This code is heavily inspired by the archived gofacebook/gracenet/net.go handler
+
+package graceful
+
+import (
+	"fmt"
+	"net"
+	"os"
+	"strconv"
+	"strings"
+	"sync"
+
+	"code.gitea.io/gitea/modules/log"
+)
+
+const (
+	listenFDs = "LISTEN_FDS"
+	startFD   = 3
+)
+
+// In order to keep the working directory the same as when we started we record
+// it at startup.
+var originalWD, _ = os.Getwd()
+
+var (
+	once  = sync.Once{}
+	mutex = sync.Mutex{}
+
+	providedListeners = []net.Listener{}
+	activeListeners   = []net.Listener{}
+)
+
+func getProvidedFDs() (savedErr error) {
+	// Only inherit the provided FDS once but we will save the error so that repeated calls to this function will return the same error
+	once.Do(func() {
+		mutex.Lock()
+		defer mutex.Unlock()
+
+		numFDs := os.Getenv(listenFDs)
+		if numFDs == "" {
+			return
+		}
+		n, err := strconv.Atoi(numFDs)
+		if err != nil {
+			savedErr = fmt.Errorf("%s is not a number: %s. Err: %v", listenFDs, numFDs, err)
+			return
+		}
+
+		for i := startFD; i < n+startFD; i++ {
+			file := os.NewFile(uintptr(i), fmt.Sprintf("listener_FD%d", i))
+
+			l, err := net.FileListener(file)
+			if err == nil {
+				// Close the inherited file if it's a listener
+				if err = file.Close(); err != nil {
+					savedErr = fmt.Errorf("error closing provided socket fd %d: %s", i, err)
+					return
+				}
+				providedListeners = append(providedListeners, l)
+				continue
+			}
+
+			// If needed we can handle packetconns here.
+			savedErr = fmt.Errorf("Error getting provided socket fd %d: %v", i, err)
+			return
+		}
+	})
+	return savedErr
+}
+
+// CloseProvidedListeners closes all unused provided listeners.
+func CloseProvidedListeners() error {
+	mutex.Lock()
+	defer mutex.Unlock()
+	var returnableError error
+	for _, l := range providedListeners {
+		err := l.Close()
+		if err != nil {
+			log.Error("Error in closing unused provided listener: %v", err)
+			if returnableError != nil {
+				returnableError = fmt.Errorf("%v & %v", returnableError, err)
+			} else {
+				returnableError = err
+			}
+		}
+	}
+	providedListeners = []net.Listener{}
+
+	return returnableError
+}
+
+// GetListener obtains a listener for the local network address. The network must be
+// a stream-oriented network: "tcp", "tcp4", "tcp6", "unix" or "unixpacket". It
+// returns an provided net.Listener for the matching network and address, or
+// creates a new one using net.Listen.
+func GetListener(network, address string) (net.Listener, error) {
+	// Add a deferral to say that we've tried to grab a listener
+	defer InformCleanup()
+	switch network {
+	case "tcp", "tcp4", "tcp6":
+		tcpAddr, err := net.ResolveTCPAddr(network, address)
+		if err != nil {
+			return nil, err
+		}
+		return GetListenerTCP(network, tcpAddr)
+	case "unix", "unixpacket":
+		unixAddr, err := net.ResolveUnixAddr(network, address)
+		if err != nil {
+			return nil, err
+		}
+		return GetListenerUnix(network, unixAddr)
+	default:
+		return nil, net.UnknownNetworkError(network)
+	}
+}
+
+// GetListenerTCP announces on the local network address. The network must be:
+// "tcp", "tcp4" or "tcp6". It returns a provided net.Listener for the
+// matching network and address, or creates a new one using net.ListenTCP.
+func GetListenerTCP(network string, address *net.TCPAddr) (*net.TCPListener, error) {
+	if err := getProvidedFDs(); err != nil {
+		return nil, err
+	}
+
+	mutex.Lock()
+	defer mutex.Unlock()
+
+	// look for a provided listener
+	for i, l := range providedListeners {
+		if isSameAddr(l.Addr(), address) {
+			providedListeners = append(providedListeners[:i], providedListeners[i+1:]...)
+
+			activeListeners = append(activeListeners, l)
+			return l.(*net.TCPListener), nil
+		}
+	}
+
+	// no provided listener for this address -> make a fresh listener
+	l, err := net.ListenTCP(network, address)
+	if err != nil {
+		return nil, err
+	}
+	activeListeners = append(activeListeners, l)
+	return l, nil
+}
+
+// GetListenerUnix announces on the local network address. The network must be:
+// "unix" or "unixpacket". It returns a provided net.Listener for the
+// matching network and address, or creates a new one using net.ListenUnix.
+func GetListenerUnix(network string, address *net.UnixAddr) (*net.UnixListener, error) {
+	if err := getProvidedFDs(); err != nil {
+		return nil, err
+	}
+
+	mutex.Lock()
+	defer mutex.Unlock()
+
+	// look for a provided listener
+	for i, l := range providedListeners {
+		if isSameAddr(l.Addr(), address) {
+			providedListeners = append(providedListeners[:i], providedListeners[i+1:]...)
+			activeListeners = append(activeListeners, l)
+			return l.(*net.UnixListener), nil
+		}
+	}
+
+	// make a fresh listener
+	l, err := net.ListenUnix(network, address)
+	if err != nil {
+		return nil, err
+	}
+	activeListeners = append(activeListeners, l)
+	return l, nil
+}
+
+func isSameAddr(a1, a2 net.Addr) bool {
+	// If the addresses are not on the same network fail.
+	if a1.Network() != a2.Network() {
+		return false
+	}
+
+	// If the two addresses have the same string representation they're equal
+	a1s := a1.String()
+	a2s := a2.String()
+	if a1s == a2s {
+		return true
+	}
+
+	// This allows for ipv6 vs ipv4 local addresses to compare as equal. This
+	// scenario is common when listening on localhost.
+	const ipv6prefix = "[::]"
+	a1s = strings.TrimPrefix(a1s, ipv6prefix)
+	a2s = strings.TrimPrefix(a2s, ipv6prefix)
+	const ipv4prefix = "0.0.0.0"
+	a1s = strings.TrimPrefix(a1s, ipv4prefix)
+	a2s = strings.TrimPrefix(a2s, ipv4prefix)
+	return a1s == a2s
+}
+
+func getActiveListeners() []net.Listener {
+	mutex.Lock()
+	defer mutex.Unlock()
+	listeners := make([]net.Listener, len(activeListeners))
+	copy(listeners, activeListeners)
+	return listeners
+}
diff --git a/modules/graceful/restart.go b/modules/graceful/restart.go
new file mode 100644
index 0000000000..04ee072c80
--- /dev/null
+++ b/modules/graceful/restart.go
@@ -0,0 +1,85 @@
+// +build !windows
+
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+// This code is heavily inspired by the archived gofacebook/gracenet/net.go handler
+
+package graceful
+
+import (
+	"fmt"
+	"os"
+	"os/exec"
+	"strings"
+	"sync"
+	"syscall"
+)
+
+var killParent sync.Once
+
+// KillParent sends the kill signal to the parent process if we are a child
+func KillParent() {
+	killParent.Do(func() {
+		if IsChild {
+			ppid := syscall.Getppid()
+			if ppid > 1 {
+				_ = syscall.Kill(ppid, syscall.SIGTERM)
+			}
+		}
+	})
+}
+
+// RestartProcess starts a new process passing it the active listeners. It
+// doesn't fork, but starts a new process using the same environment and
+// arguments as when it was originally started. This allows for a newly
+// deployed binary to be started. It returns the pid of the newly started
+// process when successful.
+func RestartProcess() (int, error) {
+	listeners := getActiveListeners()
+
+	// Extract the fds from the listeners.
+	files := make([]*os.File, len(listeners))
+	for i, l := range listeners {
+		var err error
+		// Now, all our listeners actually have File() functions so instead of
+		// individually casting we just use a hacky interface
+		files[i], err = l.(filer).File()
+		if err != nil {
+			return 0, err
+		}
+		// Remember to close these at the end.
+		defer files[i].Close()
+	}
+
+	// Use the original binary location. This works with symlinks such that if
+	// the file it points to has been changed we will use the updated symlink.
+	argv0, err := exec.LookPath(os.Args[0])
+	if err != nil {
+		return 0, err
+	}
+
+	// Pass on the environment and replace the old count key with the new one.
+	var env []string
+	for _, v := range os.Environ() {
+		if !strings.HasPrefix(v, listenFDs+"=") {
+			env = append(env, v)
+		}
+	}
+	env = append(env, fmt.Sprintf("%s=%d", listenFDs, len(listeners)))
+
+	allFiles := append([]*os.File{os.Stdin, os.Stdout, os.Stderr}, files...)
+	process, err := os.StartProcess(argv0, os.Args, &os.ProcAttr{
+		Dir:   originalWD,
+		Env:   env,
+		Files: allFiles,
+	})
+	if err != nil {
+		return 0, err
+	}
+	return process.Pid, nil
+}
+
+type filer interface {
+	File() (*os.File, error)
+}
diff --git a/modules/graceful/server.go b/modules/graceful/server.go
new file mode 100644
index 0000000000..896d547b46
--- /dev/null
+++ b/modules/graceful/server.go
@@ -0,0 +1,274 @@
+// +build !windows
+
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+// This code is highly inspired by endless go
+
+package graceful
+
+import (
+	"crypto/tls"
+	"net"
+	"os"
+	"strings"
+	"sync"
+	"syscall"
+	"time"
+
+	"code.gitea.io/gitea/modules/log"
+)
+
+type state uint8
+
+const (
+	stateInit state = iota
+	stateRunning
+	stateShuttingDown
+	stateTerminate
+)
+
+var (
+	// RWMutex for when adding servers or shutting down
+	runningServerReg sync.RWMutex
+	runningServerWG  sync.WaitGroup
+	// ensure we only fork once
+	runningServersForked bool
+
+	// DefaultReadTimeOut default read timeout
+	DefaultReadTimeOut time.Duration
+	// DefaultWriteTimeOut default write timeout
+	DefaultWriteTimeOut time.Duration
+	// DefaultMaxHeaderBytes default max header bytes
+	DefaultMaxHeaderBytes int
+
+	// IsChild reports if we are a fork iff LISTEN_FDS is set and our parent PID is not 1
+	IsChild = len(os.Getenv(listenFDs)) > 0 && os.Getppid() > 1
+)
+
+func init() {
+	runningServerReg = sync.RWMutex{}
+	runningServerWG = sync.WaitGroup{}
+
+	DefaultMaxHeaderBytes = 0 // use http.DefaultMaxHeaderBytes - which currently is 1 << 20 (1MB)
+}
+
+// ServeFunction represents a listen.Accept loop
+type ServeFunction = func(net.Listener) error
+
+// Server represents our graceful server
+type Server struct {
+	network         string
+	address         string
+	listener        net.Listener
+	PreSignalHooks  map[os.Signal][]func()
+	PostSignalHooks map[os.Signal][]func()
+	wg              sync.WaitGroup
+	sigChan         chan os.Signal
+	state           state
+	lock            *sync.RWMutex
+	BeforeBegin     func(network, address string)
+	OnShutdown      func()
+}
+
+// WaitForServers waits for all running servers to finish
+func WaitForServers() {
+	runningServerWG.Wait()
+}
+
+// NewServer creates a server on network at provided address
+func NewServer(network, address string) *Server {
+	runningServerReg.Lock()
+	defer runningServerReg.Unlock()
+
+	if IsChild {
+		log.Info("Restarting new server: %s:%s on PID: %d", network, address, os.Getpid())
+	} else {
+		log.Info("Starting new server: %s:%s on PID: %d", network, address, os.Getpid())
+	}
+	srv := &Server{
+		wg:              sync.WaitGroup{},
+		sigChan:         make(chan os.Signal),
+		PreSignalHooks:  map[os.Signal][]func(){},
+		PostSignalHooks: map[os.Signal][]func(){},
+		state:           stateInit,
+		lock:            &sync.RWMutex{},
+		network:         network,
+		address:         address,
+	}
+
+	srv.BeforeBegin = func(network, addr string) {
+		log.Debug("Starting server on %s:%s (PID: %d)", network, addr, syscall.Getpid())
+	}
+
+	return srv
+}
+
+// ListenAndServe listens on the provided network address and then calls Serve
+// to handle requests on incoming connections.
+func (srv *Server) ListenAndServe(serve ServeFunction) error {
+	go srv.handleSignals()
+
+	l, err := GetListener(srv.network, srv.address)
+	if err != nil {
+		log.Error("Unable to GetListener: %v", err)
+		return err
+	}
+
+	srv.listener = newWrappedListener(l, srv)
+
+	KillParent()
+
+	srv.BeforeBegin(srv.network, srv.address)
+
+	return srv.Serve(serve)
+}
+
+// ListenAndServeTLS listens on the provided network address and then calls
+// Serve to handle requests on incoming TLS connections.
+//
+// Filenames containing a certificate and matching private key for the server must
+// be provided. If the certificate is signed by a certificate authority, the
+// certFile should be the concatenation of the server's certificate followed by the
+// CA's certificate.
+func (srv *Server) ListenAndServeTLS(certFile, keyFile string, serve ServeFunction) error {
+	config := &tls.Config{}
+	if config.NextProtos == nil {
+		config.NextProtos = []string{"http/1.1"}
+	}
+
+	config.Certificates = make([]tls.Certificate, 1)
+	var err error
+	config.Certificates[0], err = tls.LoadX509KeyPair(certFile, keyFile)
+	if err != nil {
+		log.Error("Failed to load https cert file %s for %s:%s: %v", certFile, srv.network, srv.address, err)
+		return err
+	}
+	return srv.ListenAndServeTLSConfig(config, serve)
+}
+
+// ListenAndServeTLSConfig listens on the provided network address and then calls
+// Serve to handle requests on incoming TLS connections.
+func (srv *Server) ListenAndServeTLSConfig(tlsConfig *tls.Config, serve ServeFunction) error {
+	go srv.handleSignals()
+
+	l, err := GetListener(srv.network, srv.address)
+	if err != nil {
+		log.Error("Unable to get Listener: %v", err)
+		return err
+	}
+
+	wl := newWrappedListener(l, srv)
+	srv.listener = tls.NewListener(wl, tlsConfig)
+
+	KillParent()
+	srv.BeforeBegin(srv.network, srv.address)
+
+	return srv.Serve(serve)
+}
+
+// Serve accepts incoming HTTP connections on the wrapped listener l, creating a new
+// service goroutine for each. The service goroutines read requests and then call
+// handler to reply to them. Handler is typically nil, in which case the
+// DefaultServeMux is used.
+//
+// In addition to the standard Serve behaviour each connection is added to a
+// sync.Waitgroup so that all outstanding connections can be served before shutting
+// down the server.
+func (srv *Server) Serve(serve ServeFunction) error {
+	defer log.Debug("Serve() returning... (PID: %d)", syscall.Getpid())
+	srv.setState(stateRunning)
+	runningServerWG.Add(1)
+	err := serve(srv.listener)
+	log.Debug("Waiting for connections to finish... (PID: %d)", syscall.Getpid())
+	srv.wg.Wait()
+	srv.setState(stateTerminate)
+	runningServerWG.Done()
+	// use of closed means that the listeners are closed - i.e. we should be shutting down - return nil
+	if err != nil && strings.Contains(err.Error(), "use of closed") {
+		return nil
+	}
+	return err
+}
+
+func (srv *Server) getState() state {
+	srv.lock.RLock()
+	defer srv.lock.RUnlock()
+
+	return srv.state
+}
+
+func (srv *Server) setState(st state) {
+	srv.lock.Lock()
+	defer srv.lock.Unlock()
+
+	srv.state = st
+}
+
+type wrappedListener struct {
+	net.Listener
+	stopped bool
+	server  *Server
+}
+
+func newWrappedListener(l net.Listener, srv *Server) *wrappedListener {
+	return &wrappedListener{
+		Listener: l,
+		server:   srv,
+	}
+}
+
+func (wl *wrappedListener) Accept() (net.Conn, error) {
+	var c net.Conn
+	// Set keepalive on TCPListeners connections.
+	if tcl, ok := wl.Listener.(*net.TCPListener); ok {
+		tc, err := tcl.AcceptTCP()
+		if err != nil {
+			return nil, err
+		}
+		_ = tc.SetKeepAlive(true)                  // see http.tcpKeepAliveListener
+		_ = tc.SetKeepAlivePeriod(3 * time.Minute) // see http.tcpKeepAliveListener
+		c = tc
+	} else {
+		var err error
+		c, err = wl.Listener.Accept()
+		if err != nil {
+			return nil, err
+		}
+	}
+
+	c = wrappedConn{
+		Conn:   c,
+		server: wl.server,
+	}
+
+	wl.server.wg.Add(1)
+	return c, nil
+}
+
+func (wl *wrappedListener) Close() error {
+	if wl.stopped {
+		return syscall.EINVAL
+	}
+
+	wl.stopped = true
+	return wl.Listener.Close()
+}
+
+func (wl *wrappedListener) File() (*os.File, error) {
+	// returns a dup(2) - FD_CLOEXEC flag *not* set so the listening socket can be passed to child processes
+	return wl.Listener.(filer).File()
+}
+
+type wrappedConn struct {
+	net.Conn
+	server *Server
+}
+
+func (w wrappedConn) Close() error {
+	err := w.Conn.Close()
+	if err == nil {
+		w.server.wg.Done()
+	}
+	return err
+}
diff --git a/modules/graceful/server_hooks.go b/modules/graceful/server_hooks.go
new file mode 100644
index 0000000000..b8ca20ddf5
--- /dev/null
+++ b/modules/graceful/server_hooks.go
@@ -0,0 +1,121 @@
+// +build !windows
+
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package graceful
+
+import (
+	"errors"
+	"fmt"
+	"os"
+	"runtime"
+	"time"
+
+	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/setting"
+)
+
+// shutdown closes the listener so that no new connections are accepted
+// and starts a goroutine that will hammer (stop all running requests) the server
+// after setting.GracefulHammerTime.
+func (srv *Server) shutdown() {
+	// only shutdown if we're running.
+	if srv.getState() != stateRunning {
+		return
+	}
+
+	srv.setState(stateShuttingDown)
+	if setting.GracefulHammerTime >= 0 {
+		go srv.hammerTime(setting.GracefulHammerTime)
+	}
+
+	if srv.OnShutdown != nil {
+		srv.OnShutdown()
+	}
+	err := srv.listener.Close()
+	if err != nil {
+		log.Error("PID: %d Listener.Close() error: %v", os.Getpid(), err)
+	} else {
+		log.Info("PID: %d Listener (%s) closed.", os.Getpid(), srv.listener.Addr())
+	}
+}
+
+// hammerTime forces the server to shutdown in a given timeout - whether it
+// finished outstanding requests or not. if Read/WriteTimeout are not set or the
+// max header size is very big a connection could hang...
+//
+// srv.Serve() will not return until all connections are served. this will
+// unblock the srv.wg.Wait() in Serve() thus causing ListenAndServe* functions to
+// return.
+func (srv *Server) hammerTime(d time.Duration) {
+	defer func() {
+		// We call srv.wg.Done() until it panics.
+		// This happens if we call Done() when the WaitGroup counter is already at 0
+		// So if it panics -> we're done, Serve() will return and the
+		// parent will goroutine will exit.
+		if r := recover(); r != nil {
+			log.Error("WaitGroup at 0: Error: %v", r)
+		}
+	}()
+	if srv.getState() != stateShuttingDown {
+		return
+	}
+	time.Sleep(d)
+	log.Warn("Forcefully shutting down parent")
+	for {
+		if srv.getState() == stateTerminate {
+			break
+		}
+		srv.wg.Done()
+
+		// Give other goroutines a chance to finish before we forcibly stop them.
+		runtime.Gosched()
+	}
+}
+
+func (srv *Server) fork() error {
+	runningServerReg.Lock()
+	defer runningServerReg.Unlock()
+
+	// only one server instance should fork!
+	if runningServersForked {
+		return errors.New("another process already forked. Ignoring this one")
+	}
+
+	runningServersForked = true
+
+	// We need to move the file logs to append pids
+	setting.RestartLogsWithPIDSuffix()
+
+	_, err := RestartProcess()
+
+	return err
+}
+
+// RegisterPreSignalHook registers a function to be run before the signal handler for
+// a given signal. These are not mutex locked and should therefore be only called before Serve.
+func (srv *Server) RegisterPreSignalHook(sig os.Signal, f func()) (err error) {
+	for _, s := range hookableSignals {
+		if s == sig {
+			srv.PreSignalHooks[sig] = append(srv.PreSignalHooks[sig], f)
+			return
+		}
+	}
+	err = fmt.Errorf("Signal %v is not supported", sig)
+	return
+}
+
+// RegisterPostSignalHook registers a function to be run after the signal handler for
+// a given signal. These are not mutex locked and should therefore be only called before Serve.
+func (srv *Server) RegisterPostSignalHook(sig os.Signal, f func()) (err error) {
+	for _, s := range hookableSignals {
+		if s == sig {
+			srv.PostSignalHooks[sig] = append(srv.PostSignalHooks[sig], f)
+			return
+		}
+	}
+	err = fmt.Errorf("Signal %v is not supported", sig)
+	return
+}
diff --git a/modules/graceful/server_http.go b/modules/graceful/server_http.go
new file mode 100644
index 0000000000..446f0f5551
--- /dev/null
+++ b/modules/graceful/server_http.go
@@ -0,0 +1,47 @@
+// +build !windows
+
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package graceful
+
+import (
+	"crypto/tls"
+	"net/http"
+)
+
+func newHTTPServer(network, address string, handler http.Handler) (*Server, ServeFunction) {
+	server := NewServer(network, address)
+	httpServer := http.Server{
+		ReadTimeout:    DefaultReadTimeOut,
+		WriteTimeout:   DefaultWriteTimeOut,
+		MaxHeaderBytes: DefaultMaxHeaderBytes,
+		Handler:        handler,
+	}
+	server.OnShutdown = func() {
+		httpServer.SetKeepAlivesEnabled(false)
+	}
+	return server, httpServer.Serve
+}
+
+// HTTPListenAndServe listens on the provided network address and then calls Serve
+// to handle requests on incoming connections.
+func HTTPListenAndServe(network, address string, handler http.Handler) error {
+	server, lHandler := newHTTPServer(network, address, handler)
+	return server.ListenAndServe(lHandler)
+}
+
+// HTTPListenAndServeTLS listens on the provided network address and then calls Serve
+// to handle requests on incoming connections.
+func HTTPListenAndServeTLS(network, address, certFile, keyFile string, handler http.Handler) error {
+	server, lHandler := newHTTPServer(network, address, handler)
+	return server.ListenAndServeTLS(certFile, keyFile, lHandler)
+}
+
+// HTTPListenAndServeTLSConfig listens on the provided network address and then calls Serve
+// to handle requests on incoming connections.
+func HTTPListenAndServeTLSConfig(network, address string, tlsConfig *tls.Config, handler http.Handler) error {
+	server, lHandler := newHTTPServer(network, address, handler)
+	return server.ListenAndServeTLSConfig(tlsConfig, lHandler)
+}
diff --git a/modules/graceful/server_signals.go b/modules/graceful/server_signals.go
new file mode 100644
index 0000000000..a4bcd00b16
--- /dev/null
+++ b/modules/graceful/server_signals.go
@@ -0,0 +1,95 @@
+// +build !windows
+
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package graceful
+
+import (
+	"os"
+	"os/signal"
+	"syscall"
+	"time"
+
+	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/setting"
+)
+
+var hookableSignals []os.Signal
+
+func init() {
+	hookableSignals = []os.Signal{
+		syscall.SIGHUP,
+		syscall.SIGUSR1,
+		syscall.SIGUSR2,
+		syscall.SIGINT,
+		syscall.SIGTERM,
+		syscall.SIGTSTP,
+	}
+}
+
+// handleSignals listens for os Signals and calls any hooked in function that the
+// user had registered with the signal.
+func (srv *Server) handleSignals() {
+	var sig os.Signal
+
+	signal.Notify(
+		srv.sigChan,
+		hookableSignals...,
+	)
+
+	pid := syscall.Getpid()
+	for {
+		sig = <-srv.sigChan
+		srv.preSignalHooks(sig)
+		switch sig {
+		case syscall.SIGHUP:
+			if setting.GracefulRestartable {
+				log.Info("PID: %d. Received SIGHUP. Forking...", pid)
+				err := srv.fork()
+				if err != nil && err.Error() != "another process already forked. Ignoring this one" {
+					log.Error("Error whilst forking from PID: %d : %v", pid, err)
+				}
+			} else {
+				log.Info("PID: %d. Received SIGHUP. Not set restartable. Shutting down...", pid)
+
+				srv.shutdown()
+			}
+		case syscall.SIGUSR1:
+			log.Info("PID %d. Received SIGUSR1.", pid)
+		case syscall.SIGUSR2:
+			log.Warn("PID %d. Received SIGUSR2. Hammering...", pid)
+			srv.hammerTime(0 * time.Second)
+		case syscall.SIGINT:
+			log.Warn("PID %d. Received SIGINT. Shutting down...", pid)
+			srv.shutdown()
+		case syscall.SIGTERM:
+			log.Warn("PID %d. Received SIGTERM. Shutting down...", pid)
+			srv.shutdown()
+		case syscall.SIGTSTP:
+			log.Info("PID %d. Received SIGTSTP.")
+		default:
+			log.Info("PID %d. Received %v.", sig)
+		}
+		srv.postSignalHooks(sig)
+	}
+}
+
+func (srv *Server) preSignalHooks(sig os.Signal) {
+	if _, notSet := srv.PreSignalHooks[sig]; !notSet {
+		return
+	}
+	for _, f := range srv.PreSignalHooks[sig] {
+		f()
+	}
+}
+
+func (srv *Server) postSignalHooks(sig os.Signal) {
+	if _, notSet := srv.PostSignalHooks[sig]; !notSet {
+		return
+	}
+	for _, f := range srv.PostSignalHooks[sig] {
+		f()
+	}
+}
diff --git a/modules/indexer/issues/indexer.go b/modules/indexer/issues/indexer.go
index df8bfd6305..4f410daf4c 100644
--- a/modules/indexer/issues/indexer.go
+++ b/modules/indexer/issues/indexer.go
@@ -5,9 +5,11 @@
 package issues
 
 import (
-	"fmt"
+	"sync"
+	"time"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/modules/graceful"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/util"
@@ -45,78 +47,143 @@ type Indexer interface {
 	Search(kw string, repoID int64, limit, start int) (*SearchResult, error)
 }
 
+type indexerHolder struct {
+	indexer Indexer
+	mutex   sync.RWMutex
+	cond    *sync.Cond
+}
+
+func newIndexerHolder() *indexerHolder {
+	h := &indexerHolder{}
+	h.cond = sync.NewCond(h.mutex.RLocker())
+	return h
+}
+
+func (h *indexerHolder) set(indexer Indexer) {
+	h.mutex.Lock()
+	defer h.mutex.Unlock()
+	h.indexer = indexer
+	h.cond.Broadcast()
+}
+
+func (h *indexerHolder) get() Indexer {
+	h.mutex.RLock()
+	defer h.mutex.RUnlock()
+	if h.indexer == nil {
+		h.cond.Wait()
+	}
+	return h.indexer
+}
+
 var (
+	issueIndexerChannel = make(chan *IndexerData, setting.Indexer.UpdateQueueLength)
 	// issueIndexerQueue queue of issue ids to be updated
 	issueIndexerQueue Queue
-	issueIndexer      Indexer
+	holder            = newIndexerHolder()
 )
 
 // InitIssueIndexer initialize issue indexer, syncReindex is true then reindex until
 // all issue index done.
-func InitIssueIndexer(syncReindex bool) error {
-	var populate bool
-	var dummyQueue bool
-	switch setting.Indexer.IssueType {
-	case "bleve":
-		issueIndexer = NewBleveIndexer(setting.Indexer.IssuePath)
-		exist, err := issueIndexer.Init()
-		if err != nil {
-			return err
-		}
-		populate = !exist
-	case "db":
-		issueIndexer = &DBIndexer{}
-		dummyQueue = true
-	default:
-		return fmt.Errorf("unknow issue indexer type: %s", setting.Indexer.IssueType)
-	}
-
-	if dummyQueue {
-		issueIndexerQueue = &DummyQueue{}
-		return nil
-	}
-
-	var err error
-	switch setting.Indexer.IssueQueueType {
-	case setting.LevelQueueType:
-		issueIndexerQueue, err = NewLevelQueue(
-			issueIndexer,
-			setting.Indexer.IssueQueueDir,
-			setting.Indexer.IssueQueueBatchNumber)
-		if err != nil {
-			return err
-		}
-	case setting.ChannelQueueType:
-		issueIndexerQueue = NewChannelQueue(issueIndexer, setting.Indexer.IssueQueueBatchNumber)
-	case setting.RedisQueueType:
-		addrs, pass, idx, err := parseConnStr(setting.Indexer.IssueQueueConnStr)
-		if err != nil {
-			return err
-		}
-		issueIndexerQueue, err = NewRedisQueue(addrs, pass, idx, issueIndexer, setting.Indexer.IssueQueueBatchNumber)
-		if err != nil {
-			return err
-		}
-	default:
-		return fmt.Errorf("Unsupported indexer queue type: %v", setting.Indexer.IssueQueueType)
-	}
-
+func InitIssueIndexer(syncReindex bool) {
+	waitChannel := make(chan time.Duration)
 	go func() {
-		err = issueIndexerQueue.Run()
-		if err != nil {
-			log.Error("issueIndexerQueue.Run: %v", err)
+		start := time.Now()
+		log.Info("Initializing Issue Indexer")
+		var populate bool
+		var dummyQueue bool
+		switch setting.Indexer.IssueType {
+		case "bleve":
+			issueIndexer := NewBleveIndexer(setting.Indexer.IssuePath)
+			exist, err := issueIndexer.Init()
+			if err != nil {
+				log.Fatal("Unable to initialize Bleve Issue Indexer: %v", err)
+			}
+			populate = !exist
+			holder.set(issueIndexer)
+		case "db":
+			issueIndexer := &DBIndexer{}
+			holder.set(issueIndexer)
+			dummyQueue = true
+		default:
+			log.Fatal("Unknown issue indexer type: %s", setting.Indexer.IssueType)
 		}
-	}()
 
-	if populate {
-		if syncReindex {
-			populateIssueIndexer()
+		if dummyQueue {
+			issueIndexerQueue = &DummyQueue{}
 		} else {
-			go populateIssueIndexer()
-		}
-	}
+			var err error
+			switch setting.Indexer.IssueQueueType {
+			case setting.LevelQueueType:
+				issueIndexerQueue, err = NewLevelQueue(
+					holder.get(),
+					setting.Indexer.IssueQueueDir,
+					setting.Indexer.IssueQueueBatchNumber)
+				if err != nil {
+					log.Fatal(
+						"Unable create level queue for issue queue dir: %s batch number: %d : %v",
+						setting.Indexer.IssueQueueDir,
+						setting.Indexer.IssueQueueBatchNumber,
+						err)
+				}
+			case setting.ChannelQueueType:
+				issueIndexerQueue = NewChannelQueue(holder.get(), setting.Indexer.IssueQueueBatchNumber)
+			case setting.RedisQueueType:
+				addrs, pass, idx, err := parseConnStr(setting.Indexer.IssueQueueConnStr)
+				if err != nil {
+					log.Fatal("Unable to parse connection string for RedisQueueType: %s : %v",
+						setting.Indexer.IssueQueueConnStr,
+						err)
+				}
+				issueIndexerQueue, err = NewRedisQueue(addrs, pass, idx, holder.get(), setting.Indexer.IssueQueueBatchNumber)
+				if err != nil {
+					log.Fatal("Unable to create RedisQueue: %s : %v",
+						setting.Indexer.IssueQueueConnStr,
+						err)
+				}
+			default:
+				log.Fatal("Unsupported indexer queue type: %v",
+					setting.Indexer.IssueQueueType)
+			}
 
-	return nil
+			go func() {
+				err = issueIndexerQueue.Run()
+				if err != nil {
+					log.Error("issueIndexerQueue.Run: %v", err)
+				}
+			}()
+		}
+
+		go func() {
+			for data := range issueIndexerChannel {
+				_ = issueIndexerQueue.Push(data)
+			}
+		}()
+
+		if populate {
+			if syncReindex {
+				populateIssueIndexer()
+			} else {
+				go populateIssueIndexer()
+			}
+		}
+		waitChannel <- time.Since(start)
+	}()
+	if syncReindex {
+		<-waitChannel
+	} else if setting.Indexer.StartupTimeout > 0 {
+		go func() {
+			timeout := setting.Indexer.StartupTimeout
+			if graceful.IsChild && setting.GracefulHammerTime > 0 {
+				timeout += setting.GracefulHammerTime
+			}
+			select {
+			case duration := <-waitChannel:
+				log.Info("Issue Indexer Initialization took %v", duration)
+			case <-time.After(timeout):
+				log.Fatal("Issue Indexer Initialization timed-out after: %v", timeout)
+			}
+		}()
+	}
 }
 
 // populateIssueIndexer populate the issue indexer with issue data
@@ -166,13 +233,13 @@ func UpdateIssueIndexer(issue *models.Issue) {
 			comments = append(comments, comment.Content)
 		}
 	}
-	_ = issueIndexerQueue.Push(&IndexerData{
+	issueIndexerChannel <- &IndexerData{
 		ID:       issue.ID,
 		RepoID:   issue.RepoID,
 		Title:    issue.Title,
 		Content:  issue.Content,
 		Comments: comments,
-	})
+	}
 }
 
 // DeleteRepoIssueIndexer deletes repo's all issues indexes
@@ -188,16 +255,16 @@ func DeleteRepoIssueIndexer(repo *models.Repository) {
 		return
 	}
 
-	_ = issueIndexerQueue.Push(&IndexerData{
+	issueIndexerChannel <- &IndexerData{
 		IDs:      ids,
 		IsDelete: true,
-	})
+	}
 }
 
 // SearchIssuesByKeyword search issue ids by keywords and repo id
 func SearchIssuesByKeyword(repoID int64, keyword string) ([]int64, error) {
 	var issueIDs []int64
-	res, err := issueIndexer.Search(keyword, repoID, 1000, 0)
+	res, err := holder.get().Search(keyword, repoID, 1000, 0)
 	if err != nil {
 		return nil, err
 	}
diff --git a/modules/indexer/issues/indexer_test.go b/modules/indexer/issues/indexer_test.go
index 59a7beed47..212c2edfbe 100644
--- a/modules/indexer/issues/indexer_test.go
+++ b/modules/indexer/issues/indexer_test.go
@@ -5,7 +5,6 @@
 package issues
 
 import (
-	"fmt"
 	"os"
 	"path/filepath"
 	"testing"
@@ -17,11 +16,6 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-func fatalTestError(fmtStr string, args ...interface{}) {
-	fmt.Fprintf(os.Stderr, fmtStr, args...)
-	os.Exit(1)
-}
-
 func TestMain(m *testing.M) {
 	models.MainTest(m, filepath.Join("..", "..", ".."))
 }
@@ -32,9 +26,7 @@ func TestBleveSearchIssues(t *testing.T) {
 	os.RemoveAll(setting.Indexer.IssueQueueDir)
 	os.RemoveAll(setting.Indexer.IssuePath)
 	setting.Indexer.IssueType = "bleve"
-	if err := InitIssueIndexer(true); err != nil {
-		fatalTestError("Error InitIssueIndexer: %v\n", err)
-	}
+	InitIssueIndexer(true)
 
 	time.Sleep(5 * time.Second)
 
@@ -59,9 +51,7 @@ func TestDBSearchIssues(t *testing.T) {
 	assert.NoError(t, models.PrepareTestDatabase())
 
 	setting.Indexer.IssueType = "db"
-	if err := InitIssueIndexer(true); err != nil {
-		fatalTestError("Error InitIssueIndexer: %v\n", err)
-	}
+	InitIssueIndexer(true)
 
 	ids, err := SearchIssuesByKeyword(1, "issue2")
 	assert.NoError(t, err)
diff --git a/modules/indexer/repo.go b/modules/indexer/repo.go
index 91ed173aa7..841f29acd7 100644
--- a/modules/indexer/repo.go
+++ b/modules/indexer/repo.go
@@ -6,6 +6,7 @@ package indexer
 
 import (
 	"strings"
+	"sync"
 
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
@@ -25,8 +26,36 @@ const (
 	repoIndexerLatestVersion = 4
 )
 
+type bleveIndexerHolder struct {
+	index bleve.Index
+	mutex sync.RWMutex
+	cond  *sync.Cond
+}
+
+func newBleveIndexerHolder() *bleveIndexerHolder {
+	b := &bleveIndexerHolder{}
+	b.cond = sync.NewCond(b.mutex.RLocker())
+	return b
+}
+
+func (r *bleveIndexerHolder) set(index bleve.Index) {
+	r.mutex.Lock()
+	defer r.mutex.Unlock()
+	r.index = index
+	r.cond.Broadcast()
+}
+
+func (r *bleveIndexerHolder) get() bleve.Index {
+	r.mutex.RLock()
+	defer r.mutex.RUnlock()
+	if r.index == nil {
+		r.cond.Wait()
+	}
+	return r.index
+}
+
 // repoIndexer (thread-safe) index for repository contents
-var repoIndexer bleve.Index
+var indexerHolder = newBleveIndexerHolder()
 
 // RepoIndexerOp type of operation to perform on repo indexer
 type RepoIndexerOp int
@@ -73,12 +102,12 @@ func (update RepoIndexerUpdate) AddToFlushingBatch(batch rupture.FlushingBatch)
 
 // InitRepoIndexer initialize repo indexer
 func InitRepoIndexer(populateIndexer func() error) {
-	var err error
-	repoIndexer, err = openIndexer(setting.Indexer.RepoPath, repoIndexerLatestVersion)
+	indexer, err := openIndexer(setting.Indexer.RepoPath, repoIndexerLatestVersion)
 	if err != nil {
 		log.Fatal("InitRepoIndexer: %v", err)
 	}
-	if repoIndexer != nil {
+	if indexer != nil {
+		indexerHolder.set(indexer)
 		return
 	}
 
@@ -92,7 +121,6 @@ func InitRepoIndexer(populateIndexer func() error) {
 
 // createRepoIndexer create a repo indexer if one does not already exist
 func createRepoIndexer(path string, latestVersion int) error {
-	var err error
 	docMapping := bleve.NewDocumentMapping()
 	numericFieldMapping := bleve.NewNumericFieldMapping()
 	numericFieldMapping.IncludeInAll = false
@@ -103,9 +131,9 @@ func createRepoIndexer(path string, latestVersion int) error {
 	docMapping.AddFieldMappingsAt("Content", textFieldMapping)
 
 	mapping := bleve.NewIndexMapping()
-	if err = addUnicodeNormalizeTokenFilter(mapping); err != nil {
+	if err := addUnicodeNormalizeTokenFilter(mapping); err != nil {
 		return err
-	} else if err = mapping.AddCustomAnalyzer(repoIndexerAnalyzer, map[string]interface{}{
+	} else if err := mapping.AddCustomAnalyzer(repoIndexerAnalyzer, map[string]interface{}{
 		"type":          custom.Name,
 		"char_filters":  []string{},
 		"tokenizer":     unicode.Name,
@@ -117,10 +145,12 @@ func createRepoIndexer(path string, latestVersion int) error {
 	mapping.AddDocumentMapping(repoIndexerDocType, docMapping)
 	mapping.AddDocumentMapping("_all", bleve.NewDocumentDisabledMapping())
 
-	repoIndexer, err = bleve.New(path, mapping)
+	indexer, err := bleve.New(path, mapping)
 	if err != nil {
 		return err
 	}
+	indexerHolder.set(indexer)
+
 	return rupture.WriteIndexMetadata(path, &rupture.IndexMetadata{
 		Version: latestVersion,
 	})
@@ -140,14 +170,14 @@ func filenameOfIndexerID(indexerID string) string {
 
 // RepoIndexerBatch batch to add updates to
 func RepoIndexerBatch() rupture.FlushingBatch {
-	return rupture.NewFlushingBatch(repoIndexer, maxBatchSize)
+	return rupture.NewFlushingBatch(indexerHolder.get(), maxBatchSize)
 }
 
 // DeleteRepoFromIndexer delete all of a repo's files from indexer
 func DeleteRepoFromIndexer(repoID int64) error {
 	query := numericEqualityQuery(repoID, "RepoID")
 	searchRequest := bleve.NewSearchRequestOptions(query, 2147483647, 0, false)
-	result, err := repoIndexer.Search(searchRequest)
+	result, err := indexerHolder.get().Search(searchRequest)
 	if err != nil {
 		return err
 	}
@@ -196,7 +226,7 @@ func SearchRepoByKeyword(repoIDs []int64, keyword string, page, pageSize int) (i
 	searchRequest.Fields = []string{"Content", "RepoID"}
 	searchRequest.IncludeLocations = true
 
-	result, err := repoIndexer.Search(searchRequest)
+	result, err := indexerHolder.get().Search(searchRequest)
 	if err != nil {
 		return 0, nil, err
 	}
diff --git a/modules/lfs/locks.go b/modules/lfs/locks.go
index d7b2429698..9ffe6b9d59 100644
--- a/modules/lfs/locks.go
+++ b/modules/lfs/locks.go
@@ -155,7 +155,9 @@ func PostLockHandler(ctx *context.Context) {
 	}
 
 	var req api.LFSLockRequest
-	dec := json.NewDecoder(ctx.Req.Body().ReadCloser())
+	bodyReader := ctx.Req.Body().ReadCloser()
+	defer bodyReader.Close()
+	dec := json.NewDecoder(bodyReader)
 	if err := dec.Decode(&req); err != nil {
 		writeStatus(ctx, 400)
 		return
@@ -269,7 +271,9 @@ func UnLockHandler(ctx *context.Context) {
 	}
 
 	var req api.LFSLockDeleteRequest
-	dec := json.NewDecoder(ctx.Req.Body().ReadCloser())
+	bodyReader := ctx.Req.Body().ReadCloser()
+	defer bodyReader.Close()
+	dec := json.NewDecoder(bodyReader)
 	if err := dec.Decode(&req); err != nil {
 		writeStatus(ctx, 400)
 		return
diff --git a/modules/lfs/server.go b/modules/lfs/server.go
index 652610acf4..6fa97a2894 100644
--- a/modules/lfs/server.go
+++ b/modules/lfs/server.go
@@ -327,7 +327,9 @@ func PutHandler(ctx *context.Context) {
 	}
 
 	contentStore := &ContentStore{BasePath: setting.LFS.ContentPath}
-	if err := contentStore.Put(meta, ctx.Req.Body().ReadCloser()); err != nil {
+	bodyReader := ctx.Req.Body().ReadCloser()
+	defer bodyReader.Close()
+	if err := contentStore.Put(meta, bodyReader); err != nil {
 		ctx.Resp.WriteHeader(500)
 		fmt.Fprintf(ctx.Resp, `{"message":"%s"}`, err)
 		if err = repository.RemoveLFSMetaObjectByOid(rv.Oid); err != nil {
@@ -434,7 +436,9 @@ func unpack(ctx *context.Context) *RequestVars {
 
 	if r.Method == "POST" { // Maybe also check if +json
 		var p RequestVars
-		dec := json.NewDecoder(r.Body().ReadCloser())
+		bodyReader := r.Body().ReadCloser()
+		defer bodyReader.Close()
+		dec := json.NewDecoder(bodyReader)
 		err := dec.Decode(&p)
 		if err != nil {
 			return rv
@@ -453,7 +457,9 @@ func unpackbatch(ctx *context.Context) *BatchVars {
 	r := ctx.Req
 	var bv BatchVars
 
-	dec := json.NewDecoder(r.Body().ReadCloser())
+	bodyReader := r.Body().ReadCloser()
+	defer bodyReader.Close()
+	dec := json.NewDecoder(bodyReader)
 	err := dec.Decode(&bv)
 	if err != nil {
 		return &bv
diff --git a/modules/markup/html.go b/modules/markup/html.go
index f07993bc4c..1ff7a41cbb 100644
--- a/modules/markup/html.go
+++ b/modules/markup/html.go
@@ -15,6 +15,7 @@ import (
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/references"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/util"
 
@@ -36,17 +37,6 @@ var (
 	// While fast, this is also incorrect and lead to false positives.
 	// TODO: fix invalid linking issue
 
-	// mentionPattern matches all mentions in the form of "@user"
-	mentionPattern = regexp.MustCompile(`(?:\s|^|\(|\[)(@[0-9a-zA-Z-_\.]+)(?:\s|$|\)|\])`)
-
-	// issueNumericPattern matches string that references to a numeric issue, e.g. #1287
-	issueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[)(#[0-9]+)(?:\s|$|\)|\]|:|\.(\s|$))`)
-	// issueAlphanumericPattern matches string that references to an alphanumeric issue, e.g. ABC-1234
-	issueAlphanumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([A-Z]{1,10}-[1-9][0-9]*)(?:\s|$|\)|\]|:|\.(\s|$))`)
-	// crossReferenceIssueNumericPattern matches string that references a numeric issue in a different repository
-	// e.g. gogits/gogs#12345
-	crossReferenceIssueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([0-9a-zA-Z-_\.]+/[0-9a-zA-Z-_\.]+#[0-9]+)(?:\s|$|\)|\]|\.(\s|$))`)
-
 	// sha1CurrentPattern matches string that represents a commit SHA, e.g. d8a994ef243349f321568f9e36d5c3f444b99cae
 	// Although SHA1 hashes are 40 chars long, the regex matches the hash from 7 to 40 chars in length
 	// so that abbreviated hash links can be used as well. This matches git and github useability.
@@ -70,6 +60,9 @@ var (
 	linkRegex, _ = xurls.StrictMatchingScheme("https?://")
 )
 
+// CSS class for action keywords (e.g. "closes: #1")
+const keywordClass = "issue-keyword"
+
 // regexp for full links to issues/pulls
 var issueFullPattern *regexp.Regexp
 
@@ -99,15 +92,30 @@ func getIssueFullPattern() *regexp.Regexp {
 	return issueFullPattern
 }
 
-// FindAllMentions matches mention patterns in given content
-// and returns a list of found user names without @ prefix.
-func FindAllMentions(content string) []string {
-	mentions := mentionPattern.FindAllStringSubmatch(content, -1)
-	ret := make([]string, len(mentions))
-	for i, val := range mentions {
-		ret[i] = val[1][1:]
+// CustomLinkURLSchemes allows for additional schemes to be detected when parsing links within text
+func CustomLinkURLSchemes(schemes []string) {
+	schemes = append(schemes, "http", "https")
+	withAuth := make([]string, 0, len(schemes))
+	validScheme := regexp.MustCompile(`^[a-z]+$`)
+	for _, s := range schemes {
+		if !validScheme.MatchString(s) {
+			continue
+		}
+		without := false
+		for _, sna := range xurls.SchemesNoAuthority {
+			if s == sna {
+				without = true
+				break
+			}
+		}
+		if without {
+			s += ":"
+		} else {
+			s += "://"
+		}
+		withAuth = append(withAuth, s)
 	}
-	return ret
+	linkRegex, _ = xurls.StrictMatchingScheme(strings.Join(withAuth, "|"))
 }
 
 // IsSameDomain checks if given url string has the same hostname as current Gitea instance
@@ -142,7 +150,6 @@ var defaultProcessors = []processor{
 	linkProcessor,
 	mentionProcessor,
 	issueIndexPatternProcessor,
-	crossReferenceIssueIndexPatternProcessor,
 	sha1CurrentPatternProcessor,
 	emailAddressProcessor,
 }
@@ -183,7 +190,6 @@ var commitMessageProcessors = []processor{
 	linkProcessor,
 	mentionProcessor,
 	issueIndexPatternProcessor,
-	crossReferenceIssueIndexPatternProcessor,
 	sha1CurrentPatternProcessor,
 	emailAddressProcessor,
 }
@@ -217,7 +223,6 @@ var commitMessageSubjectProcessors = []processor{
 	linkProcessor,
 	mentionProcessor,
 	issueIndexPatternProcessor,
-	crossReferenceIssueIndexPatternProcessor,
 	sha1CurrentPatternProcessor,
 }
 
@@ -330,6 +335,24 @@ func (ctx *postProcessCtx) textNode(node *html.Node) {
 	}
 }
 
+// createKeyword() renders a highlighted version of an action keyword
+func createKeyword(content string) *html.Node {
+	span := &html.Node{
+		Type: html.ElementNode,
+		Data: atom.Span.String(),
+		Attr: []html.Attribute{},
+	}
+	span.Attr = append(span.Attr, html.Attribute{Key: "class", Val: keywordClass})
+
+	text := &html.Node{
+		Type: html.TextNode,
+		Data: content,
+	}
+	span.AppendChild(text)
+
+	return span
+}
+
 func createLink(href, content, class string) *html.Node {
 	a := &html.Node{
 		Type: html.ElementNode,
@@ -377,10 +400,16 @@ func createCodeLink(href, content, class string) *html.Node {
 	return a
 }
 
-// replaceContent takes a text node, and in its content it replaces a section of
-// it with the specified newNode. An example to visualize how this can work can
-// be found here: https://play.golang.org/p/5zP8NnHZ03s
+// replaceContent takes text node, and in its content it replaces a section of
+// it with the specified newNode.
 func replaceContent(node *html.Node, i, j int, newNode *html.Node) {
+	replaceContentList(node, i, j, []*html.Node{newNode})
+}
+
+// replaceContentList takes text node, and in its content it replaces a section of
+// it with the specified newNodes. An example to visualize how this can work can
+// be found here: https://play.golang.org/p/5zP8NnHZ03s
+func replaceContentList(node *html.Node, i, j int, newNodes []*html.Node) {
 	// get the data before and after the match
 	before := node.Data[:i]
 	after := node.Data[j:]
@@ -392,7 +421,9 @@ func replaceContent(node *html.Node, i, j int, newNode *html.Node) {
 	// Get the current next sibling, before which we place the replaced data,
 	// and after that we place the new text node.
 	nextSibling := node.NextSibling
-	node.Parent.InsertBefore(newNode, nextSibling)
+	for _, n := range newNodes {
+		node.Parent.InsertBefore(n, nextSibling)
+	}
 	if after != "" {
 		node.Parent.InsertBefore(&html.Node{
 			Type: html.TextNode,
@@ -402,13 +433,13 @@ func replaceContent(node *html.Node, i, j int, newNode *html.Node) {
 }
 
 func mentionProcessor(_ *postProcessCtx, node *html.Node) {
-	m := mentionPattern.FindStringSubmatchIndex(node.Data)
-	if m == nil {
+	// We replace only the first mention; other mentions will be addressed later
+	found, loc := references.FindFirstMentionBytes([]byte(node.Data))
+	if !found {
 		return
 	}
-	// Replace the mention with a link to the specified user.
-	mention := node.Data[m[2]:m[3]]
-	replaceContent(node, m[2], m[3], createLink(util.URLJoin(setting.AppURL, mention[1:]), mention, "mention"))
+	mention := node.Data[loc.Start:loc.End]
+	replaceContent(node, loc.Start, loc.End, createLink(util.URLJoin(setting.AppURL, mention[1:]), mention, "mention"))
 }
 
 func shortLinkProcessor(ctx *postProcessCtx, node *html.Node) {
@@ -597,45 +628,44 @@ func issueIndexPatternProcessor(ctx *postProcessCtx, node *html.Node) {
 	if ctx.metas == nil {
 		return
 	}
-	// default to numeric pattern, unless alphanumeric is requested.
-	pattern := issueNumericPattern
+
+	var (
+		found bool
+		ref   *references.RenderizableReference
+	)
+
 	if ctx.metas["style"] == IssueNameStyleAlphanumeric {
-		pattern = issueAlphanumericPattern
-	}
-
-	match := pattern.FindStringSubmatchIndex(node.Data)
-	if match == nil {
-		return
-	}
-
-	id := node.Data[match[2]:match[3]]
-	var link *html.Node
-	if _, ok := ctx.metas["format"]; ok {
-		// Support for external issue tracker
-		if ctx.metas["style"] == IssueNameStyleAlphanumeric {
-			ctx.metas["index"] = id
-		} else {
-			ctx.metas["index"] = id[1:]
-		}
-		link = createLink(com.Expand(ctx.metas["format"], ctx.metas), id, "issue")
+		found, ref = references.FindRenderizableReferenceAlphanumeric(node.Data)
 	} else {
-		link = createLink(util.URLJoin(setting.AppURL, ctx.metas["user"], ctx.metas["repo"], "issues", id[1:]), id, "issue")
+		found, ref = references.FindRenderizableReferenceNumeric(node.Data)
 	}
-	replaceContent(node, match[2], match[3], link)
-}
-
-func crossReferenceIssueIndexPatternProcessor(ctx *postProcessCtx, node *html.Node) {
-	m := crossReferenceIssueNumericPattern.FindStringSubmatchIndex(node.Data)
-	if m == nil {
+	if !found {
 		return
 	}
-	ref := node.Data[m[2]:m[3]]
 
-	parts := strings.SplitN(ref, "#", 2)
-	repo, issue := parts[0], parts[1]
+	var link *html.Node
+	reftext := node.Data[ref.RefLocation.Start:ref.RefLocation.End]
+	if _, ok := ctx.metas["format"]; ok {
+		ctx.metas["index"] = ref.Issue
+		link = createLink(com.Expand(ctx.metas["format"], ctx.metas), reftext, "issue")
+	} else if ref.Owner == "" {
+		link = createLink(util.URLJoin(setting.AppURL, ctx.metas["user"], ctx.metas["repo"], "issues", ref.Issue), reftext, "issue")
+	} else {
+		link = createLink(util.URLJoin(setting.AppURL, ref.Owner, ref.Name, "issues", ref.Issue), reftext, "issue")
+	}
 
-	replaceContent(node, m[2], m[3],
-		createLink(util.URLJoin(setting.AppURL, repo, "issues", issue), ref, issue))
+	if ref.Action == references.XRefActionNone {
+		replaceContent(node, ref.RefLocation.Start, ref.RefLocation.End, link)
+		return
+	}
+
+	// Decorate action keywords
+	keyword := createKeyword(node.Data[ref.ActionLocation.Start:ref.ActionLocation.End])
+	spaces := &html.Node{
+		Type: html.TextNode,
+		Data: node.Data[ref.ActionLocation.End:ref.RefLocation.Start],
+	}
+	replaceContentList(node, ref.ActionLocation.Start, ref.RefLocation.End, []*html.Node{keyword, spaces, link})
 }
 
 // fullSha1PatternProcessor renders SHA containing URLs
diff --git a/modules/markup/html_internal_test.go b/modules/markup/html_internal_test.go
index 2824ce3e68..9722063e17 100644
--- a/modules/markup/html_internal_test.go
+++ b/modules/markup/html_internal_test.go
@@ -239,34 +239,6 @@ func TestRender_FullIssueURLs(t *testing.T) {
 		`<a href="http://localhost:3000/gogits/gogs/issues/4" class="issue">#4</a>`)
 }
 
-func TestRegExp_issueNumericPattern(t *testing.T) {
-	trueTestCases := []string{
-		"#1234",
-		"#0",
-		"#1234567890987654321",
-		"  #12",
-		"#12:",
-		"ref: #12: msg",
-	}
-	falseTestCases := []string{
-		"# 1234",
-		"# 0",
-		"# ",
-		"#",
-		"#ABC",
-		"#1A2B",
-		"",
-		"ABC",
-	}
-
-	for _, testCase := range trueTestCases {
-		assert.True(t, issueNumericPattern.MatchString(testCase))
-	}
-	for _, testCase := range falseTestCases {
-		assert.False(t, issueNumericPattern.MatchString(testCase))
-	}
-}
-
 func TestRegExp_sha1CurrentPattern(t *testing.T) {
 	trueTestCases := []string{
 		"d8a994ef243349f321568f9e36d5c3f444b99cae",
@@ -325,70 +297,6 @@ func TestRegExp_anySHA1Pattern(t *testing.T) {
 	}
 }
 
-func TestRegExp_mentionPattern(t *testing.T) {
-	trueTestCases := []string{
-		"@Unknwon",
-		"@ANT_123",
-		"@xxx-DiN0-z-A..uru..s-xxx",
-		"   @lol   ",
-		" @Te-st",
-		"(@gitea)",
-		"[@gitea]",
-	}
-	falseTestCases := []string{
-		"@ 0",
-		"@ ",
-		"@",
-		"",
-		"ABC",
-		"/home/gitea/@gitea",
-		"\"@gitea\"",
-	}
-
-	for _, testCase := range trueTestCases {
-		res := mentionPattern.MatchString(testCase)
-		assert.True(t, res)
-	}
-	for _, testCase := range falseTestCases {
-		res := mentionPattern.MatchString(testCase)
-		assert.False(t, res)
-	}
-}
-
-func TestRegExp_issueAlphanumericPattern(t *testing.T) {
-	trueTestCases := []string{
-		"ABC-1234",
-		"A-1",
-		"RC-80",
-		"ABCDEFGHIJ-1234567890987654321234567890",
-		"ABC-123.",
-		"(ABC-123)",
-		"[ABC-123]",
-		"ABC-123:",
-	}
-	falseTestCases := []string{
-		"RC-08",
-		"PR-0",
-		"ABCDEFGHIJK-1",
-		"PR_1",
-		"",
-		"#ABC",
-		"",
-		"ABC",
-		"GG-",
-		"rm-1",
-		"/home/gitea/ABC-1234",
-		"MY-STRING-ABC-123",
-	}
-
-	for _, testCase := range trueTestCases {
-		assert.True(t, issueAlphanumericPattern.MatchString(testCase))
-	}
-	for _, testCase := range falseTestCases {
-		assert.False(t, issueAlphanumericPattern.MatchString(testCase))
-	}
-}
-
 func TestRegExp_shortLinkPattern(t *testing.T) {
 	trueTestCases := []string{
 		"[[stuff]]",
diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go
index 66e56f71a7..91ef320b40 100644
--- a/modules/markup/html_test.go
+++ b/modules/markup/html_test.go
@@ -89,6 +89,11 @@ func TestRender_links(t *testing.T) {
 	}
 	// Text that should be turned into URL
 
+	defaultCustom := setting.Markdown.CustomURLSchemes
+	setting.Markdown.CustomURLSchemes = []string{"ftp", "magnet"}
+	ReplaceSanitizer()
+	CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes)
+
 	test(
 		"https://www.example.com",
 		`<p><a href="https://www.example.com" rel="nofollow">https://www.example.com</a></p>`)
@@ -131,6 +136,12 @@ func TestRender_links(t *testing.T) {
 	test(
 		"https://username:password@gitea.com",
 		`<p><a href="https://username:password@gitea.com" rel="nofollow">https://username:password@gitea.com</a></p>`)
+	test(
+		"ftp://gitea.com/file.txt",
+		`<p><a href="ftp://gitea.com/file.txt" rel="nofollow">ftp://gitea.com/file.txt</a></p>`)
+	test(
+		"magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcadabcde&dn=download",
+		`<p><a href="magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcadabcde&amp;dn=download" rel="nofollow">magnet:?xt=urn:btih:5dee65101db281ac9c46344cd6b175cdcadabcde&amp;dn=download</a></p>`)
 
 	// Test that should *not* be turned into URL
 	test(
@@ -154,6 +165,14 @@ func TestRender_links(t *testing.T) {
 	test(
 		"www",
 		`<p>www</p>`)
+	test(
+		"ftps://gitea.com",
+		`<p>ftps://gitea.com</p>`)
+
+	// Restore previous settings
+	setting.Markdown.CustomURLSchemes = defaultCustom
+	ReplaceSanitizer()
+	CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes)
 }
 
 func TestRender_email(t *testing.T) {
diff --git a/modules/markup/markup.go b/modules/markup/markup.go
index dc43b533c0..008b21ab97 100644
--- a/modules/markup/markup.go
+++ b/modules/markup/markup.go
@@ -9,12 +9,16 @@ import (
 	"strings"
 
 	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/setting"
 )
 
 // Init initialize regexps for markdown parsing
 func Init() {
 	getIssueFullPattern()
 	NewSanitizer()
+	if len(setting.Markdown.CustomURLSchemes) > 0 {
+		CustomLinkURLSchemes(setting.Markdown.CustomURLSchemes)
+	}
 
 	// since setting maybe changed extensions, this will reload all parser extensions mapping
 	extParsers = make(map[string]Parser)
diff --git a/modules/markup/mdstripper/mdstripper.go b/modules/markup/mdstripper/mdstripper.go
new file mode 100644
index 0000000000..7a901b17a9
--- /dev/null
+++ b/modules/markup/mdstripper/mdstripper.go
@@ -0,0 +1,260 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package mdstripper
+
+import (
+	"bytes"
+
+	"github.com/russross/blackfriday"
+)
+
+// MarkdownStripper extends blackfriday.Renderer
+type MarkdownStripper struct {
+	blackfriday.Renderer
+	links     []string
+	coallesce bool
+}
+
+const (
+	blackfridayExtensions = 0 |
+		blackfriday.EXTENSION_NO_INTRA_EMPHASIS |
+		blackfriday.EXTENSION_TABLES |
+		blackfriday.EXTENSION_FENCED_CODE |
+		blackfriday.EXTENSION_STRIKETHROUGH |
+		blackfriday.EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK |
+		blackfriday.EXTENSION_DEFINITION_LISTS |
+		blackfriday.EXTENSION_FOOTNOTES |
+		blackfriday.EXTENSION_HEADER_IDS |
+		blackfriday.EXTENSION_AUTO_HEADER_IDS |
+		// Not included in modules/markup/markdown/markdown.go;
+		// required here to process inline links
+		blackfriday.EXTENSION_AUTOLINK
+)
+
+//revive:disable:var-naming Implementing the Rendering interface requires breaking some linting rules
+
+// StripMarkdown parses markdown content by removing all markup and code blocks
+//	in order to extract links and other references
+func StripMarkdown(rawBytes []byte) (string, []string) {
+	stripper := &MarkdownStripper{
+		links: make([]string, 0, 10),
+	}
+	body := blackfriday.Markdown(rawBytes, stripper, blackfridayExtensions)
+	return string(body), stripper.GetLinks()
+}
+
+// StripMarkdownBytes parses markdown content by removing all markup and code blocks
+//	in order to extract links and other references
+func StripMarkdownBytes(rawBytes []byte) ([]byte, []string) {
+	stripper := &MarkdownStripper{
+		links: make([]string, 0, 10),
+	}
+	body := blackfriday.Markdown(rawBytes, stripper, blackfridayExtensions)
+	return body, stripper.GetLinks()
+}
+
+// block-level callbacks
+
+// BlockCode dummy function to proceed with rendering
+func (r *MarkdownStripper) BlockCode(out *bytes.Buffer, text []byte, infoString string) {
+	// Not rendered
+	r.coallesce = false
+}
+
+// BlockQuote dummy function to proceed with rendering
+func (r *MarkdownStripper) BlockQuote(out *bytes.Buffer, text []byte) {
+	// FIXME: perhaps it's better to leave out block quote for this?
+	r.processString(out, text, false)
+}
+
+// BlockHtml dummy function to proceed with rendering
+func (r *MarkdownStripper) BlockHtml(out *bytes.Buffer, text []byte) { //nolint
+	// Not rendered
+	r.coallesce = false
+}
+
+// Header dummy function to proceed with rendering
+func (r *MarkdownStripper) Header(out *bytes.Buffer, text func() bool, level int, id string) {
+	text()
+	r.coallesce = false
+}
+
+// HRule dummy function to proceed with rendering
+func (r *MarkdownStripper) HRule(out *bytes.Buffer) {
+	// Not rendered
+	r.coallesce = false
+}
+
+// List dummy function to proceed with rendering
+func (r *MarkdownStripper) List(out *bytes.Buffer, text func() bool, flags int) {
+	text()
+	r.coallesce = false
+}
+
+// ListItem dummy function to proceed with rendering
+func (r *MarkdownStripper) ListItem(out *bytes.Buffer, text []byte, flags int) {
+	r.processString(out, text, false)
+}
+
+// Paragraph dummy function to proceed with rendering
+func (r *MarkdownStripper) Paragraph(out *bytes.Buffer, text func() bool) {
+	text()
+	r.coallesce = false
+}
+
+// Table dummy function to proceed with rendering
+func (r *MarkdownStripper) Table(out *bytes.Buffer, header []byte, body []byte, columnData []int) {
+	r.processString(out, header, false)
+	r.processString(out, body, false)
+}
+
+// TableRow dummy function to proceed with rendering
+func (r *MarkdownStripper) TableRow(out *bytes.Buffer, text []byte) {
+	r.processString(out, text, false)
+}
+
+// TableHeaderCell dummy function to proceed with rendering
+func (r *MarkdownStripper) TableHeaderCell(out *bytes.Buffer, text []byte, flags int) {
+	r.processString(out, text, false)
+}
+
+// TableCell dummy function to proceed with rendering
+func (r *MarkdownStripper) TableCell(out *bytes.Buffer, text []byte, flags int) {
+	r.processString(out, text, false)
+}
+
+// Footnotes dummy function to proceed with rendering
+func (r *MarkdownStripper) Footnotes(out *bytes.Buffer, text func() bool) {
+	text()
+}
+
+// FootnoteItem dummy function to proceed with rendering
+func (r *MarkdownStripper) FootnoteItem(out *bytes.Buffer, name, text []byte, flags int) {
+	r.processString(out, text, false)
+}
+
+// TitleBlock dummy function to proceed with rendering
+func (r *MarkdownStripper) TitleBlock(out *bytes.Buffer, text []byte) {
+	r.processString(out, text, false)
+}
+
+// Span-level callbacks
+
+// AutoLink dummy function to proceed with rendering
+func (r *MarkdownStripper) AutoLink(out *bytes.Buffer, link []byte, kind int) {
+	r.processLink(out, link, []byte{})
+}
+
+// CodeSpan dummy function to proceed with rendering
+func (r *MarkdownStripper) CodeSpan(out *bytes.Buffer, text []byte) {
+	// Not rendered
+	r.coallesce = false
+}
+
+// DoubleEmphasis dummy function to proceed with rendering
+func (r *MarkdownStripper) DoubleEmphasis(out *bytes.Buffer, text []byte) {
+	r.processString(out, text, false)
+}
+
+// Emphasis dummy function to proceed with rendering
+func (r *MarkdownStripper) Emphasis(out *bytes.Buffer, text []byte) {
+	r.processString(out, text, false)
+}
+
+// Image dummy function to proceed with rendering
+func (r *MarkdownStripper) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {
+	// Not rendered
+	r.coallesce = false
+}
+
+// LineBreak dummy function to proceed with rendering
+func (r *MarkdownStripper) LineBreak(out *bytes.Buffer) {
+	// Not rendered
+	r.coallesce = false
+}
+
+// Link dummy function to proceed with rendering
+func (r *MarkdownStripper) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) {
+	r.processLink(out, link, content)
+}
+
+// RawHtmlTag dummy function to proceed with rendering
+func (r *MarkdownStripper) RawHtmlTag(out *bytes.Buffer, tag []byte) { //nolint
+	// Not rendered
+	r.coallesce = false
+}
+
+// TripleEmphasis dummy function to proceed with rendering
+func (r *MarkdownStripper) TripleEmphasis(out *bytes.Buffer, text []byte) {
+	r.processString(out, text, false)
+}
+
+// StrikeThrough dummy function to proceed with rendering
+func (r *MarkdownStripper) StrikeThrough(out *bytes.Buffer, text []byte) {
+	r.processString(out, text, false)
+}
+
+// FootnoteRef dummy function to proceed with rendering
+func (r *MarkdownStripper) FootnoteRef(out *bytes.Buffer, ref []byte, id int) {
+	// Not rendered
+	r.coallesce = false
+}
+
+// Low-level callbacks
+
+// Entity dummy function to proceed with rendering
+func (r *MarkdownStripper) Entity(out *bytes.Buffer, entity []byte) {
+	// FIXME: literal entities are not parsed; perhaps they should
+	r.coallesce = false
+}
+
+// NormalText dummy function to proceed with rendering
+func (r *MarkdownStripper) NormalText(out *bytes.Buffer, text []byte) {
+	r.processString(out, text, true)
+}
+
+// Header and footer
+
+// DocumentHeader dummy function to proceed with rendering
+func (r *MarkdownStripper) DocumentHeader(out *bytes.Buffer) {
+	r.coallesce = false
+}
+
+// DocumentFooter dummy function to proceed with rendering
+func (r *MarkdownStripper) DocumentFooter(out *bytes.Buffer) {
+	r.coallesce = false
+}
+
+// GetFlags returns rendering flags
+func (r *MarkdownStripper) GetFlags() int {
+	return 0
+}
+
+//revive:enable:var-naming
+
+func doubleSpace(out *bytes.Buffer) {
+	if out.Len() > 0 {
+		out.WriteByte('\n')
+	}
+}
+
+func (r *MarkdownStripper) processString(out *bytes.Buffer, text []byte, coallesce bool) {
+	// Always break-up words
+	if !coallesce || !r.coallesce {
+		doubleSpace(out)
+	}
+	out.Write(text)
+	r.coallesce = coallesce
+}
+func (r *MarkdownStripper) processLink(out *bytes.Buffer, link []byte, content []byte) {
+	// Links are processed out of band
+	r.links = append(r.links, string(link))
+	r.coallesce = false
+}
+
+// GetLinks returns the list of link data collected while parsing
+func (r *MarkdownStripper) GetLinks() []string {
+	return r.links
+}
diff --git a/modules/markup/mdstripper/mdstripper_test.go b/modules/markup/mdstripper/mdstripper_test.go
new file mode 100644
index 0000000000..157fe1975b
--- /dev/null
+++ b/modules/markup/mdstripper/mdstripper_test.go
@@ -0,0 +1,71 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package mdstripper
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestMarkdownStripper(t *testing.T) {
+	type testItem struct {
+		markdown      string
+		expectedText  []string
+		expectedLinks []string
+	}
+
+	list := []testItem{
+		{
+			`
+## This is a title
+
+This is [one](link) to paradise.
+This **is emphasized**.
+This: should coallesce.
+
+` + "```" + `
+This is a code block.
+This should not appear in the output at all.
+` + "```" + `
+
+* Bullet 1
+* Bullet 2
+
+A HIDDEN ` + "`" + `GHOST` + "`" + ` IN THIS LINE.
+		`,
+			[]string{
+				"This is a title",
+				"This is",
+				"to paradise.",
+				"This",
+				"is emphasized",
+				".",
+				"This: should coallesce.",
+				"Bullet 1",
+				"Bullet 2",
+				"A HIDDEN",
+				"IN THIS LINE.",
+			},
+			[]string{
+				"link",
+			}},
+	}
+
+	for _, test := range list {
+		text, links := StripMarkdown([]byte(test.markdown))
+		rawlines := strings.Split(text, "\n")
+		lines := make([]string, 0, len(rawlines))
+		for _, line := range rawlines {
+			line := strings.TrimSpace(line)
+			if line != "" {
+				lines = append(lines, line)
+			}
+		}
+		assert.EqualValues(t, test.expectedText, lines)
+		assert.EqualValues(t, test.expectedLinks, links)
+	}
+}
diff --git a/modules/markup/sanitizer.go b/modules/markup/sanitizer.go
index 2ec43cf4fd..f873e8105e 100644
--- a/modules/markup/sanitizer.go
+++ b/modules/markup/sanitizer.go
@@ -28,19 +28,28 @@ var sanitizer = &Sanitizer{}
 // entire application lifecycle.
 func NewSanitizer() {
 	sanitizer.init.Do(func() {
-		sanitizer.policy = bluemonday.UGCPolicy()
-		// We only want to allow HighlightJS specific classes for code blocks
-		sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`^language-\w+$`)).OnElements("code")
-
-		// Checkboxes
-		sanitizer.policy.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input")
-		sanitizer.policy.AllowAttrs("checked", "disabled").OnElements("input")
-
-		// Custom URL-Schemes
-		sanitizer.policy.AllowURLSchemes(setting.Markdown.CustomURLSchemes...)
+		ReplaceSanitizer()
 	})
 }
 
+// ReplaceSanitizer replaces the current sanitizer to account for changes in settings
+func ReplaceSanitizer() {
+	sanitizer = &Sanitizer{}
+	sanitizer.policy = bluemonday.UGCPolicy()
+	// We only want to allow HighlightJS specific classes for code blocks
+	sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`^language-\w+$`)).OnElements("code")
+
+	// Checkboxes
+	sanitizer.policy.AllowAttrs("type").Matching(regexp.MustCompile(`^checkbox$`)).OnElements("input")
+	sanitizer.policy.AllowAttrs("checked", "disabled").OnElements("input")
+
+	// Custom URL-Schemes
+	sanitizer.policy.AllowURLSchemes(setting.Markdown.CustomURLSchemes...)
+
+	// Allow keyword markup
+	sanitizer.policy.AllowAttrs("class").Matching(regexp.MustCompile(`^` + keywordClass + `$`)).OnElements("span")
+}
+
 // Sanitize takes a string that contains a HTML fragment or document and applies policy whitelist.
 func Sanitize(s string) string {
 	NewSanitizer()
diff --git a/modules/migrations/base/downloader.go b/modules/migrations/base/downloader.go
index ab5ca6dec8..69c2adb9e9 100644
--- a/modules/migrations/base/downloader.go
+++ b/modules/migrations/base/downloader.go
@@ -5,6 +5,8 @@
 
 package base
 
+import "code.gitea.io/gitea/modules/structs"
+
 // Downloader downloads the site repo informations
 type Downloader interface {
 	GetRepoInfo() (*Repository, error)
@@ -21,4 +23,5 @@ type Downloader interface {
 type DownloaderFactory interface {
 	Match(opts MigrateOptions) (bool, error)
 	New(opts MigrateOptions) (Downloader, error)
+	GitServiceType() structs.GitServiceType
 }
diff --git a/modules/migrations/base/options.go b/modules/migrations/base/options.go
index ba7fdc6815..2d180b61d9 100644
--- a/modules/migrations/base/options.go
+++ b/modules/migrations/base/options.go
@@ -5,22 +5,7 @@
 
 package base
 
-// MigrateOptions defines the way a repository gets migrated
-type MigrateOptions struct {
-	RemoteURL    string
-	AuthUsername string
-	AuthPassword string
-	Name         string
-	Description  string
-	OriginalURL  string
+import "code.gitea.io/gitea/modules/structs"
 
-	Wiki         bool
-	Issues       bool
-	Milestones   bool
-	Labels       bool
-	Releases     bool
-	Comments     bool
-	PullRequests bool
-	Private      bool
-	Mirror       bool
-}
+// MigrateOptions defines the way a repository gets migrated
+type MigrateOptions = structs.MigrateRepoOption
diff --git a/modules/migrations/gitea.go b/modules/migrations/gitea.go
index 1edac47a6e..a095751c6b 100644
--- a/modules/migrations/gitea.go
+++ b/modules/migrations/gitea.go
@@ -22,6 +22,7 @@ import (
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/migrations/base"
 	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/timeutil"
 
 	gouuid "github.com/satori/go.uuid"
@@ -33,15 +34,17 @@ var (
 
 // GiteaLocalUploader implements an Uploader to gitea sites
 type GiteaLocalUploader struct {
-	doer        *models.User
-	repoOwner   string
-	repoName    string
-	repo        *models.Repository
-	labels      sync.Map
-	milestones  sync.Map
-	issues      sync.Map
-	gitRepo     *git.Repository
-	prHeadCache map[string]struct{}
+	doer           *models.User
+	repoOwner      string
+	repoName       string
+	repo           *models.Repository
+	labels         sync.Map
+	milestones     sync.Map
+	issues         sync.Map
+	gitRepo        *git.Repository
+	prHeadCache    map[string]struct{}
+	userMap        map[int64]int64 // external user id mapping to user id
+	gitServiceType structs.GitServiceType
 }
 
 // NewGiteaLocalUploader creates an gitea Uploader via gitea API v1
@@ -51,6 +54,7 @@ func NewGiteaLocalUploader(doer *models.User, repoOwner, repoName string) *Gitea
 		repoOwner:   repoOwner,
 		repoName:    repoName,
 		prHeadCache: make(map[string]struct{}),
+		userMap:     make(map[int64]int64),
 	}
 }
 
@@ -90,16 +94,35 @@ func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.Migrate
 		remoteAddr = u.String()
 	}
 
-	r, err := models.MigrateRepository(g.doer, owner, models.MigrateRepoOptions{
-		Name:                 g.repoName,
-		Description:          repo.Description,
-		OriginalURL:          repo.OriginalURL,
-		IsMirror:             repo.IsMirror,
-		RemoteAddr:           remoteAddr,
-		IsPrivate:            repo.IsPrivate,
-		Wiki:                 opts.Wiki,
-		SyncReleasesWithTags: !opts.Releases, // if didn't get releases, then sync them from tags
+	var r *models.Repository
+	if opts.MigrateToRepoID <= 0 {
+		r, err = models.CreateRepository(g.doer, owner, models.CreateRepoOptions{
+			Name:        g.repoName,
+			Description: repo.Description,
+			OriginalURL: repo.OriginalURL,
+			IsPrivate:   opts.Private,
+			IsMirror:    opts.Mirror,
+			Status:      models.RepositoryBeingMigrated,
+		})
+	} else {
+		r, err = models.GetRepositoryByID(opts.MigrateToRepoID)
+	}
+	if err != nil {
+		return err
+	}
+
+	r, err = models.MigrateRepositoryGitData(g.doer, owner, r, structs.MigrateRepoOption{
+		RepoName:       g.repoName,
+		Description:    repo.Description,
+		OriginalURL:    repo.OriginalURL,
+		GitServiceType: opts.GitServiceType,
+		Mirror:         repo.IsMirror,
+		CloneAddr:      remoteAddr,
+		Private:        repo.IsPrivate,
+		Wiki:           opts.Wiki,
+		Releases:       opts.Releases, // if didn't get releases, then sync them from tags
 	})
+
 	g.repo = r
 	if err != nil {
 		return err
@@ -175,20 +198,38 @@ func (g *GiteaLocalUploader) CreateReleases(releases ...*base.Release) error {
 	var rels = make([]*models.Release, 0, len(releases))
 	for _, release := range releases {
 		var rel = models.Release{
-			RepoID:           g.repo.ID,
-			PublisherID:      g.doer.ID,
-			TagName:          release.TagName,
-			LowerTagName:     strings.ToLower(release.TagName),
-			Target:           release.TargetCommitish,
-			Title:            release.Name,
-			Sha1:             release.TargetCommitish,
-			Note:             release.Body,
-			IsDraft:          release.Draft,
-			IsPrerelease:     release.Prerelease,
-			IsTag:            false,
-			CreatedUnix:      timeutil.TimeStamp(release.Created.Unix()),
-			OriginalAuthor:   release.PublisherName,
-			OriginalAuthorID: release.PublisherID,
+			RepoID:       g.repo.ID,
+			TagName:      release.TagName,
+			LowerTagName: strings.ToLower(release.TagName),
+			Target:       release.TargetCommitish,
+			Title:        release.Name,
+			Sha1:         release.TargetCommitish,
+			Note:         release.Body,
+			IsDraft:      release.Draft,
+			IsPrerelease: release.Prerelease,
+			IsTag:        false,
+			CreatedUnix:  timeutil.TimeStamp(release.Created.Unix()),
+		}
+
+		userid, ok := g.userMap[release.PublisherID]
+		tp := g.gitServiceType.Name()
+		if !ok && tp != "" {
+			var err error
+			userid, err = models.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", release.PublisherID))
+			if err != nil {
+				log.Error("GetUserIDByExternalUserID: %v", err)
+			}
+			if userid > 0 {
+				g.userMap[release.PublisherID] = userid
+			}
+		}
+
+		if userid > 0 {
+			rel.PublisherID = userid
+		} else {
+			rel.PublisherID = g.doer.ID
+			rel.OriginalAuthor = release.PublisherName
+			rel.OriginalAuthorID = release.PublisherID
 		}
 
 		// calc NumCommits
@@ -266,20 +307,39 @@ func (g *GiteaLocalUploader) CreateIssues(issues ...*base.Issue) error {
 		}
 
 		var is = models.Issue{
-			RepoID:           g.repo.ID,
-			Repo:             g.repo,
-			Index:            issue.Number,
-			PosterID:         g.doer.ID,
-			OriginalAuthor:   issue.PosterName,
-			OriginalAuthorID: issue.PosterID,
-			Title:            issue.Title,
-			Content:          issue.Content,
-			IsClosed:         issue.State == "closed",
-			IsLocked:         issue.IsLocked,
-			MilestoneID:      milestoneID,
-			Labels:           labels,
-			CreatedUnix:      timeutil.TimeStamp(issue.Created.Unix()),
+			RepoID:      g.repo.ID,
+			Repo:        g.repo,
+			Index:       issue.Number,
+			Title:       issue.Title,
+			Content:     issue.Content,
+			IsClosed:    issue.State == "closed",
+			IsLocked:    issue.IsLocked,
+			MilestoneID: milestoneID,
+			Labels:      labels,
+			CreatedUnix: timeutil.TimeStamp(issue.Created.Unix()),
 		}
+
+		userid, ok := g.userMap[issue.PosterID]
+		tp := g.gitServiceType.Name()
+		if !ok && tp != "" {
+			var err error
+			userid, err = models.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", issue.PosterID))
+			if err != nil {
+				log.Error("GetUserIDByExternalUserID: %v", err)
+			}
+			if userid > 0 {
+				g.userMap[issue.PosterID] = userid
+			}
+		}
+
+		if userid > 0 {
+			is.PosterID = userid
+		} else {
+			is.PosterID = g.doer.ID
+			is.OriginalAuthor = issue.PosterName
+			is.OriginalAuthorID = issue.PosterID
+		}
+
 		if issue.Closed != nil {
 			is.ClosedUnix = timeutil.TimeStamp(issue.Closed.Unix())
 		}
@@ -313,15 +373,35 @@ func (g *GiteaLocalUploader) CreateComments(comments ...*base.Comment) error {
 			issueID = issueIDStr.(int64)
 		}
 
-		cms = append(cms, &models.Comment{
-			IssueID:          issueID,
-			Type:             models.CommentTypeComment,
-			PosterID:         g.doer.ID,
-			OriginalAuthor:   comment.PosterName,
-			OriginalAuthorID: comment.PosterID,
-			Content:          comment.Content,
-			CreatedUnix:      timeutil.TimeStamp(comment.Created.Unix()),
-		})
+		userid, ok := g.userMap[comment.PosterID]
+		tp := g.gitServiceType.Name()
+		if !ok && tp != "" {
+			var err error
+			userid, err = models.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", comment.PosterID))
+			if err != nil {
+				log.Error("GetUserIDByExternalUserID: %v", err)
+			}
+			if userid > 0 {
+				g.userMap[comment.PosterID] = userid
+			}
+		}
+
+		cm := models.Comment{
+			IssueID:     issueID,
+			Type:        models.CommentTypeComment,
+			Content:     comment.Content,
+			CreatedUnix: timeutil.TimeStamp(comment.Created.Unix()),
+		}
+
+		if userid > 0 {
+			cm.PosterID = userid
+		} else {
+			cm.PosterID = g.doer.ID
+			cm.OriginalAuthor = comment.PosterName
+			cm.OriginalAuthorID = comment.PosterID
+		}
+
+		cms = append(cms, &cm)
 
 		// TODO: Reactions
 	}
@@ -337,6 +417,28 @@ func (g *GiteaLocalUploader) CreatePullRequests(prs ...*base.PullRequest) error
 		if err != nil {
 			return err
 		}
+
+		userid, ok := g.userMap[pr.PosterID]
+		tp := g.gitServiceType.Name()
+		if !ok && tp != "" {
+			var err error
+			userid, err = models.GetUserIDByExternalUserID(tp, fmt.Sprintf("%v", pr.PosterID))
+			if err != nil {
+				log.Error("GetUserIDByExternalUserID: %v", err)
+			}
+			if userid > 0 {
+				g.userMap[pr.PosterID] = userid
+			}
+		}
+
+		if userid > 0 {
+			gpr.Issue.PosterID = userid
+		} else {
+			gpr.Issue.PosterID = g.doer.ID
+			gpr.Issue.OriginalAuthor = pr.PosterName
+			gpr.Issue.OriginalAuthorID = pr.PosterID
+		}
+
 		gprs = append(gprs, gpr)
 	}
 	if err := models.InsertPullRequests(gprs...); err != nil {
@@ -442,32 +544,50 @@ func (g *GiteaLocalUploader) newPullRequest(pr *base.PullRequest) (*models.PullR
 		head = pr.Head.Ref
 	}
 
-	var pullRequest = models.PullRequest{
-		HeadRepoID:   g.repo.ID,
-		HeadBranch:   head,
-		HeadUserName: g.repoOwner,
-		BaseRepoID:   g.repo.ID,
-		BaseBranch:   pr.Base.Ref,
-		MergeBase:    pr.Base.SHA,
-		Index:        pr.Number,
-		HasMerged:    pr.Merged,
+	var issue = models.Issue{
+		RepoID:      g.repo.ID,
+		Repo:        g.repo,
+		Title:       pr.Title,
+		Index:       pr.Number,
+		Content:     pr.Content,
+		MilestoneID: milestoneID,
+		IsPull:      true,
+		IsClosed:    pr.State == "closed",
+		IsLocked:    pr.IsLocked,
+		Labels:      labels,
+		CreatedUnix: timeutil.TimeStamp(pr.Created.Unix()),
+	}
 
-		Issue: &models.Issue{
-			RepoID:           g.repo.ID,
-			Repo:             g.repo,
-			Title:            pr.Title,
-			Index:            pr.Number,
-			PosterID:         g.doer.ID,
-			OriginalAuthor:   pr.PosterName,
-			OriginalAuthorID: pr.PosterID,
-			Content:          pr.Content,
-			MilestoneID:      milestoneID,
-			IsPull:           true,
-			IsClosed:         pr.State == "closed",
-			IsLocked:         pr.IsLocked,
-			Labels:           labels,
-			CreatedUnix:      timeutil.TimeStamp(pr.Created.Unix()),
-		},
+	userid, ok := g.userMap[pr.PosterID]
+	if !ok {
+		var err error
+		userid, err = models.GetUserIDByExternalUserID("github", fmt.Sprintf("%v", pr.PosterID))
+		if err != nil {
+			log.Error("GetUserIDByExternalUserID: %v", err)
+		}
+		if userid > 0 {
+			g.userMap[pr.PosterID] = userid
+		}
+	}
+
+	if userid > 0 {
+		issue.PosterID = userid
+	} else {
+		issue.PosterID = g.doer.ID
+		issue.OriginalAuthor = pr.PosterName
+		issue.OriginalAuthorID = pr.PosterID
+	}
+
+	var pullRequest = models.PullRequest{
+		HeadRepoID: g.repo.ID,
+		HeadBranch: head,
+		BaseRepoID: g.repo.ID,
+		BaseBranch: pr.Base.Ref,
+		MergeBase:  pr.Base.SHA,
+		Index:      pr.Number,
+		HasMerged:  pr.Merged,
+
+		Issue: &issue,
 	}
 
 	if pullRequest.Issue.IsClosed && pr.Closed != nil {
diff --git a/modules/migrations/gitea_test.go b/modules/migrations/gitea_test.go
index 88a3a6d218..73c119a15d 100644
--- a/modules/migrations/gitea_test.go
+++ b/modules/migrations/gitea_test.go
@@ -10,6 +10,7 @@ import (
 	"time"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/util"
 
 	"github.com/stretchr/testify/assert"
@@ -29,9 +30,9 @@ func TestGiteaUploadRepo(t *testing.T) {
 		uploader   = NewGiteaLocalUploader(user, user.Name, repoName)
 	)
 
-	err := migrateRepository(downloader, uploader, MigrateOptions{
-		RemoteURL:    "https://github.com/go-xorm/builder",
-		Name:         repoName,
+	err := migrateRepository(downloader, uploader, structs.MigrateRepoOption{
+		CloneAddr:    "https://github.com/go-xorm/builder",
+		RepoName:     repoName,
 		AuthUsername: "",
 
 		Wiki:         true,
diff --git a/modules/migrations/github.go b/modules/migrations/github.go
index 754f98941c..00d137a3de 100644
--- a/modules/migrations/github.go
+++ b/modules/migrations/github.go
@@ -14,6 +14,7 @@ import (
 
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/migrations/base"
+	"code.gitea.io/gitea/modules/structs"
 
 	"github.com/google/go-github/v24/github"
 	"golang.org/x/oauth2"
@@ -34,17 +35,17 @@ type GithubDownloaderV3Factory struct {
 
 // Match returns ture if the migration remote URL matched this downloader factory
 func (f *GithubDownloaderV3Factory) Match(opts base.MigrateOptions) (bool, error) {
-	u, err := url.Parse(opts.RemoteURL)
+	u, err := url.Parse(opts.CloneAddr)
 	if err != nil {
 		return false, err
 	}
 
-	return u.Host == "github.com" && opts.AuthUsername != "", nil
+	return strings.EqualFold(u.Host, "github.com") && opts.AuthUsername != "", nil
 }
 
 // New returns a Downloader related to this factory according MigrateOptions
 func (f *GithubDownloaderV3Factory) New(opts base.MigrateOptions) (base.Downloader, error) {
-	u, err := url.Parse(opts.RemoteURL)
+	u, err := url.Parse(opts.CloneAddr)
 	if err != nil {
 		return nil, err
 	}
@@ -58,6 +59,11 @@ func (f *GithubDownloaderV3Factory) New(opts base.MigrateOptions) (base.Download
 	return NewGithubDownloaderV3(opts.AuthUsername, opts.AuthPassword, oldOwner, oldName), nil
 }
 
+// GitServiceType returns the type of git service
+func (f *GithubDownloaderV3Factory) GitServiceType() structs.GitServiceType {
+	return structs.GithubService
+}
+
 // GithubDownloaderV3 implements a Downloader interface to get repository informations
 // from github via APIv3
 type GithubDownloaderV3 struct {
diff --git a/modules/migrations/migrate.go b/modules/migrations/migrate.go
index 27782cb940..bbc1dc2d56 100644
--- a/modules/migrations/migrate.go
+++ b/modules/migrations/migrate.go
@@ -6,9 +6,12 @@
 package migrations
 
 import (
+	"fmt"
+
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/migrations/base"
+	"code.gitea.io/gitea/modules/structs"
 )
 
 // MigrateOptions is equal to base.MigrateOptions
@@ -27,7 +30,8 @@ func RegisterDownloaderFactory(factory base.DownloaderFactory) {
 func MigrateRepository(doer *models.User, ownerName string, opts base.MigrateOptions) (*models.Repository, error) {
 	var (
 		downloader base.Downloader
-		uploader   = NewGiteaLocalUploader(doer, ownerName, opts.Name)
+		uploader   = NewGiteaLocalUploader(doer, ownerName, opts.RepoName)
+		theFactory base.DownloaderFactory
 	)
 
 	for _, factory := range factories {
@@ -38,6 +42,7 @@ func MigrateRepository(doer *models.User, ownerName string, opts base.MigrateOpt
 			if err != nil {
 				return nil, err
 			}
+			theFactory = factory
 			break
 		}
 	}
@@ -50,14 +55,22 @@ func MigrateRepository(doer *models.User, ownerName string, opts base.MigrateOpt
 		opts.Comments = false
 		opts.Issues = false
 		opts.PullRequests = false
-		downloader = NewPlainGitDownloader(ownerName, opts.Name, opts.RemoteURL)
-		log.Trace("Will migrate from git: %s", opts.RemoteURL)
+		opts.GitServiceType = structs.PlainGitService
+		downloader = NewPlainGitDownloader(ownerName, opts.RepoName, opts.CloneAddr)
+		log.Trace("Will migrate from git: %s", opts.CloneAddr)
+	} else if opts.GitServiceType == structs.NotMigrated {
+		opts.GitServiceType = theFactory.GitServiceType()
 	}
 
+	uploader.gitServiceType = opts.GitServiceType
 	if err := migrateRepository(downloader, uploader, opts); err != nil {
 		if err1 := uploader.Rollback(); err1 != nil {
 			log.Error("rollback failed: %v", err1)
 		}
+
+		if err2 := models.CreateRepositoryNotice(fmt.Sprintf("Migrate repository from %s failed: %v", opts.CloneAddr, err)); err2 != nil {
+			log.Error("create respotiry notice failed: ", err2)
+		}
 		return nil, err
 	}
 
diff --git a/modules/migrations/update.go b/modules/migrations/update.go
new file mode 100644
index 0000000000..d1465b2baf
--- /dev/null
+++ b/modules/migrations/update.go
@@ -0,0 +1,53 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package migrations
+
+import (
+	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/structs"
+)
+
+// UpdateMigrationPosterID updates all migrated repositories' issues and comments posterID
+func UpdateMigrationPosterID() {
+	for _, gitService := range structs.SupportedFullGitService {
+		if err := updateMigrationPosterIDByGitService(gitService); err != nil {
+			log.Error("updateMigrationPosterIDByGitService failed: %v", err)
+		}
+	}
+}
+
+func updateMigrationPosterIDByGitService(tp structs.GitServiceType) error {
+	provider := tp.Name()
+	if len(provider) == 0 {
+		return nil
+	}
+
+	const batchSize = 100
+	var start int
+	for {
+		users, err := models.FindExternalUsersByProvider(models.FindExternalUserOptions{
+			Provider: provider,
+			Start:    start,
+			Limit:    batchSize,
+		})
+		if err != nil {
+			return err
+		}
+
+		for _, user := range users {
+			externalUserID := user.ExternalID
+			if err := models.UpdateMigrationsByType(tp, externalUserID, user.UserID); err != nil {
+				log.Error("UpdateMigrationsByType type %s external user id %v to local user id %v failed: %v", tp.Name(), user.ExternalID, user.UserID, err)
+			}
+		}
+
+		if len(users) < batchSize {
+			break
+		}
+		start += len(users)
+	}
+	return nil
+}
diff --git a/modules/notification/base/notifier.go b/modules/notification/base/notifier.go
index e44f3cc632..c74bb52014 100644
--- a/modules/notification/base/notifier.go
+++ b/modules/notification/base/notifier.go
@@ -21,7 +21,7 @@ type Notifier interface {
 	NotifyNewIssue(*models.Issue)
 	NotifyIssueChangeStatus(*models.User, *models.Issue, bool)
 	NotifyIssueChangeMilestone(doer *models.User, issue *models.Issue)
-	NotifyIssueChangeAssignee(doer *models.User, issue *models.Issue, removed bool)
+	NotifyIssueChangeAssignee(doer *models.User, issue *models.Issue, assignee *models.User, removed bool, comment *models.Comment)
 	NotifyIssueChangeContent(doer *models.User, issue *models.Issue, oldContent string)
 	NotifyIssueClearLabels(doer *models.User, issue *models.Issue)
 	NotifyIssueChangeTitle(doer *models.User, issue *models.Issue, oldTitle string)
diff --git a/modules/notification/base/null.go b/modules/notification/base/null.go
index 12be1999f9..9fb08884a1 100644
--- a/modules/notification/base/null.go
+++ b/modules/notification/base/null.go
@@ -83,7 +83,7 @@ func (*NullNotifier) NotifyIssueChangeContent(doer *models.User, issue *models.I
 }
 
 // NotifyIssueChangeAssignee places a place holder function
-func (*NullNotifier) NotifyIssueChangeAssignee(doer *models.User, issue *models.Issue, removed bool) {
+func (*NullNotifier) NotifyIssueChangeAssignee(doer *models.User, issue *models.Issue, assignee *models.User, removed bool, comment *models.Comment) {
 }
 
 // NotifyIssueClearLabels places a place holder function
diff --git a/modules/notification/mail/mail.go b/modules/notification/mail/mail.go
index e1ae391f78..0900c6dcdf 100644
--- a/modules/notification/mail/mail.go
+++ b/modules/notification/mail/mail.go
@@ -5,6 +5,8 @@
 package mail
 
 import (
+	"fmt"
+
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/notification/base"
@@ -88,3 +90,11 @@ func (m *mailNotifier) NotifyPullRequestReview(pr *models.PullRequest, r *models
 		log.Error("MailParticipants: %v", err)
 	}
 }
+
+func (m *mailNotifier) NotifyIssueChangeAssignee(doer *models.User, issue *models.Issue, assignee *models.User, removed bool, comment *models.Comment) {
+	// mail only sent to added assignees and not self-assignee
+	if !removed && doer.ID != assignee.ID && assignee.EmailNotifications() == models.EmailNotificationsEnabled {
+		ct := fmt.Sprintf("Assigned #%d.", issue.Index)
+		mailer.SendIssueAssignedMail(issue, doer, ct, comment, []string{assignee.Email})
+	}
+}
diff --git a/modules/notification/notification.go b/modules/notification/notification.go
index e0de88346d..0f1b63cf67 100644
--- a/modules/notification/notification.go
+++ b/modules/notification/notification.go
@@ -11,6 +11,8 @@ import (
 	"code.gitea.io/gitea/modules/notification/indexer"
 	"code.gitea.io/gitea/modules/notification/mail"
 	"code.gitea.io/gitea/modules/notification/ui"
+	"code.gitea.io/gitea/modules/notification/webhook"
+	"code.gitea.io/gitea/modules/setting"
 )
 
 var (
@@ -23,10 +25,14 @@ func RegisterNotifier(notifier base.Notifier) {
 	notifiers = append(notifiers, notifier)
 }
 
-func init() {
+// NewContext registers notification handlers
+func NewContext() {
 	RegisterNotifier(ui.NewNotifier())
-	RegisterNotifier(mail.NewNotifier())
+	if setting.Service.EnableNotifyMail {
+		RegisterNotifier(mail.NewNotifier())
+	}
 	RegisterNotifier(indexer.NewNotifier())
+	RegisterNotifier(webhook.NewNotifier())
 }
 
 // NotifyCreateIssueComment notifies issue comment related message to notifiers
@@ -136,9 +142,9 @@ func NotifyIssueChangeContent(doer *models.User, issue *models.Issue, oldContent
 }
 
 // NotifyIssueChangeAssignee notifies change content to notifiers
-func NotifyIssueChangeAssignee(doer *models.User, issue *models.Issue, removed bool) {
+func NotifyIssueChangeAssignee(doer *models.User, issue *models.Issue, assignee *models.User, removed bool, comment *models.Comment) {
 	for _, notifier := range notifiers {
-		notifier.NotifyIssueChangeAssignee(doer, issue, removed)
+		notifier.NotifyIssueChangeAssignee(doer, issue, assignee, removed, comment)
 	}
 }
 
diff --git a/modules/notification/webhook/webhook.go b/modules/notification/webhook/webhook.go
new file mode 100644
index 0000000000..33adfaa739
--- /dev/null
+++ b/modules/notification/webhook/webhook.go
@@ -0,0 +1,67 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package webhook
+
+import (
+	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/notification/base"
+	api "code.gitea.io/gitea/modules/structs"
+)
+
+type webhookNotifier struct {
+	base.NullNotifier
+}
+
+var (
+	_ base.Notifier = &webhookNotifier{}
+)
+
+// NewNotifier create a new webhookNotifier notifier
+func NewNotifier() base.Notifier {
+	return &webhookNotifier{}
+}
+
+func (m *webhookNotifier) NotifyIssueClearLabels(doer *models.User, issue *models.Issue) {
+	if err := issue.LoadPoster(); err != nil {
+		log.Error("loadPoster: %v", err)
+		return
+	}
+
+	if err := issue.LoadRepo(); err != nil {
+		log.Error("LoadRepo: %v", err)
+		return
+	}
+
+	mode, _ := models.AccessLevel(issue.Poster, issue.Repo)
+	var err error
+	if issue.IsPull {
+		if err = issue.LoadPullRequest(); err != nil {
+			log.Error("LoadPullRequest: %v", err)
+			return
+		}
+
+		err = models.PrepareWebhooks(issue.Repo, models.HookEventPullRequest, &api.PullRequestPayload{
+			Action:      api.HookIssueLabelCleared,
+			Index:       issue.Index,
+			PullRequest: issue.PullRequest.APIFormat(),
+			Repository:  issue.Repo.APIFormat(mode),
+			Sender:      doer.APIFormat(),
+		})
+	} else {
+		err = models.PrepareWebhooks(issue.Repo, models.HookEventIssues, &api.IssuePayload{
+			Action:     api.HookIssueLabelCleared,
+			Index:      issue.Index,
+			Issue:      issue.APIFormat(),
+			Repository: issue.Repo.APIFormat(mode),
+			Sender:     doer.APIFormat(),
+		})
+	}
+	if err != nil {
+		log.Error("PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err)
+	} else {
+		go models.HookQueue.Add(issue.RepoID)
+	}
+}
diff --git a/modules/password/password.go b/modules/password/password.go
new file mode 100644
index 0000000000..92986977ec
--- /dev/null
+++ b/modules/password/password.go
@@ -0,0 +1,88 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package password
+
+import (
+	"crypto/rand"
+	"math/big"
+	"strings"
+	"sync"
+
+	"code.gitea.io/gitea/modules/setting"
+)
+
+var (
+	matchComplexityOnce sync.Once
+	validChars          string
+	requiredChars       []string
+
+	charComplexities = map[string]string{
+		"lower": `abcdefghijklmnopqrstuvwxyz`,
+		"upper": `ABCDEFGHIJKLMNOPQRSTUVWXYZ`,
+		"digit": `0123456789`,
+		"spec":  ` !"#$%&'()*+,-./:;<=>?@[\]^_{|}~` + "`",
+	}
+)
+
+// NewComplexity for preparation
+func NewComplexity() {
+	matchComplexityOnce.Do(func() {
+		setupComplexity(setting.PasswordComplexity)
+	})
+}
+
+func setupComplexity(values []string) {
+	if len(values) != 1 || values[0] != "off" {
+		for _, val := range values {
+			if chars, ok := charComplexities[val]; ok {
+				validChars += chars
+				requiredChars = append(requiredChars, chars)
+			}
+		}
+		if len(requiredChars) == 0 {
+			// No valid character classes found; use all classes as default
+			for _, chars := range charComplexities {
+				validChars += chars
+				requiredChars = append(requiredChars, chars)
+			}
+		}
+	}
+	if validChars == "" {
+		// No complexities to check; provide a sensible default for password generation
+		validChars = charComplexities["lower"] + charComplexities["upper"] + charComplexities["digit"]
+	}
+}
+
+// IsComplexEnough return True if password meets complexity settings
+func IsComplexEnough(pwd string) bool {
+	NewComplexity()
+	if len(validChars) > 0 {
+		for _, req := range requiredChars {
+			if !strings.ContainsAny(req, pwd) {
+				return false
+			}
+		}
+	}
+	return true
+}
+
+// Generate  a random password
+func Generate(n int) (string, error) {
+	NewComplexity()
+	buffer := make([]byte, n)
+	max := big.NewInt(int64(len(validChars)))
+	for {
+		for j := 0; j < n; j++ {
+			rnd, err := rand.Int(rand.Reader, max)
+			if err != nil {
+				return "", err
+			}
+			buffer[j] = validChars[rnd.Int64()]
+		}
+		if IsComplexEnough(string(buffer)) && string(buffer[0]) != " " && string(buffer[n-1]) != " " {
+			return string(buffer), nil
+		}
+	}
+}
diff --git a/modules/password/password_test.go b/modules/password/password_test.go
new file mode 100644
index 0000000000..d46a6d1571
--- /dev/null
+++ b/modules/password/password_test.go
@@ -0,0 +1,75 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package password
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestComplexity_IsComplexEnough(t *testing.T) {
+	matchComplexityOnce.Do(func() {})
+
+	testlist := []struct {
+		complexity  []string
+		truevalues  []string
+		falsevalues []string
+	}{
+		{[]string{"lower"}, []string{"abc", "abc!"}, []string{"ABC", "123", "=!$", ""}},
+		{[]string{"upper"}, []string{"ABC"}, []string{"abc", "123", "=!$", "abc!", ""}},
+		{[]string{"digit"}, []string{"123"}, []string{"abc", "ABC", "=!$", "abc!", ""}},
+		{[]string{"spec"}, []string{"=!$", "abc!"}, []string{"abc", "ABC", "123", ""}},
+		{[]string{"off"}, []string{"abc", "ABC", "123", "=!$", "abc!", ""}, nil},
+		{[]string{"lower", "spec"}, []string{"abc!"}, []string{"abc", "ABC", "123", "=!$", "abcABC123", ""}},
+		{[]string{"lower", "upper", "digit"}, []string{"abcABC123"}, []string{"abc", "ABC", "123", "=!$", "abc!", ""}},
+	}
+
+	for _, test := range testlist {
+		testComplextity(test.complexity)
+		for _, val := range test.truevalues {
+			assert.True(t, IsComplexEnough(val))
+		}
+		for _, val := range test.falsevalues {
+			assert.False(t, IsComplexEnough(val))
+		}
+	}
+
+	// Remove settings for other tests
+	testComplextity([]string{"off"})
+}
+
+func TestComplexity_Generate(t *testing.T) {
+	matchComplexityOnce.Do(func() {})
+
+	const maxCount = 50
+	const pwdLen = 50
+
+	test := func(t *testing.T, modes []string) {
+		testComplextity(modes)
+		for i := 0; i < maxCount; i++ {
+			pwd, err := Generate(pwdLen)
+			assert.NoError(t, err)
+			assert.Equal(t, pwdLen, len(pwd))
+			assert.True(t, IsComplexEnough(pwd), "Failed complexities with modes %+v for generated: %s", modes, pwd)
+		}
+	}
+
+	test(t, []string{"lower"})
+	test(t, []string{"upper"})
+	test(t, []string{"lower", "upper", "spec"})
+	test(t, []string{"off"})
+	test(t, []string{""})
+
+	// Remove settings for other tests
+	testComplextity([]string{"off"})
+}
+
+func testComplextity(values []string) {
+	// Cleanup previous values
+	validChars = ""
+	requiredChars = make([]string, 0, len(values))
+	setupComplexity(values)
+}
diff --git a/modules/private/hook.go b/modules/private/hook.go
index 67496b5132..cc9703cc77 100644
--- a/modules/private/hook.go
+++ b/modules/private/hook.go
@@ -31,11 +31,12 @@ type HookOptions struct {
 	GitAlternativeObjectDirectories string
 	GitQuarantinePath               string
 	ProtectedBranchID               int64
+	IsDeployKey                     bool
 }
 
 // HookPreReceive check whether the provided commits are allowed
 func HookPreReceive(ownerName, repoName string, opts HookOptions) (int, string) {
-	reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/pre-receive/%s/%s?old=%s&new=%s&ref=%s&userID=%d&gitObjectDirectory=%s&gitAlternativeObjectDirectories=%s&gitQuarantinePath=%s&prID=%d",
+	reqURL := setting.LocalURL + fmt.Sprintf("api/internal/hook/pre-receive/%s/%s?old=%s&new=%s&ref=%s&userID=%d&gitObjectDirectory=%s&gitAlternativeObjectDirectories=%s&gitQuarantinePath=%s&prID=%d&isDeployKey=%t",
 		url.PathEscape(ownerName),
 		url.PathEscape(repoName),
 		url.QueryEscape(opts.OldCommitID),
@@ -46,6 +47,7 @@ func HookPreReceive(ownerName, repoName string, opts HookOptions) (int, string)
 		url.QueryEscape(opts.GitAlternativeObjectDirectories),
 		url.QueryEscape(opts.GitQuarantinePath),
 		opts.ProtectedBranchID,
+		opts.IsDeployKey,
 	)
 
 	resp, err := newInternalRequest(reqURL, "GET").Response()
diff --git a/modules/process/manager.go b/modules/process/manager.go
index 9ac3af86f1..3e77c0a6a9 100644
--- a/modules/process/manager.go
+++ b/modules/process/manager.go
@@ -1,4 +1,5 @@
 // Copyright 2014 The Gogs Authors. All rights reserved.
+// Copyright 2019 The Gitea Authors. All rights reserved.
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
@@ -9,6 +10,7 @@ import (
 	"context"
 	"errors"
 	"fmt"
+	"io"
 	"os/exec"
 	"sync"
 	"time"
@@ -93,6 +95,14 @@ func (pm *Manager) ExecDir(timeout time.Duration, dir, desc, cmdName string, arg
 // Returns its complete stdout and stderr
 // outputs and an error, if any (including timeout)
 func (pm *Manager) ExecDirEnv(timeout time.Duration, dir, desc string, env []string, cmdName string, args ...string) (string, string, error) {
+	return pm.ExecDirEnvStdIn(timeout, dir, desc, env, nil, cmdName, args...)
+}
+
+// ExecDirEnvStdIn runs a command in given path and environment variables with provided stdIN, and waits for its completion
+// up to the given timeout (or DefaultTimeout if -1 is given).
+// Returns its complete stdout and stderr
+// outputs and an error, if any (including timeout)
+func (pm *Manager) ExecDirEnvStdIn(timeout time.Duration, dir, desc string, env []string, stdIn io.Reader, cmdName string, args ...string) (string, string, error) {
 	if timeout == -1 {
 		timeout = 60 * time.Second
 	}
@@ -108,6 +118,10 @@ func (pm *Manager) ExecDirEnv(timeout time.Duration, dir, desc string, env []str
 	cmd.Env = env
 	cmd.Stdout = stdOut
 	cmd.Stderr = stdErr
+	if stdIn != nil {
+		cmd.Stdin = stdIn
+	}
+
 	if err := cmd.Start(); err != nil {
 		return "", "", err
 	}
diff --git a/modules/references/references.go b/modules/references/references.go
new file mode 100644
index 0000000000..9c74d0d081
--- /dev/null
+++ b/modules/references/references.go
@@ -0,0 +1,322 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package references
+
+import (
+	"net/url"
+	"regexp"
+	"strconv"
+	"strings"
+	"sync"
+
+	"code.gitea.io/gitea/modules/markup/mdstripper"
+	"code.gitea.io/gitea/modules/setting"
+)
+
+var (
+	// validNamePattern performs only the most basic validation for user or repository names
+	// Repository name should contain only alphanumeric, dash ('-'), underscore ('_') and dot ('.') characters.
+	validNamePattern = regexp.MustCompile(`^[a-z0-9_.-]+$`)
+
+	// NOTE: All below regex matching do not perform any extra validation.
+	// Thus a link is produced even if the linked entity does not exist.
+	// While fast, this is also incorrect and lead to false positives.
+	// TODO: fix invalid linking issue
+
+	// mentionPattern matches all mentions in the form of "@user"
+	mentionPattern = regexp.MustCompile(`(?:\s|^|\(|\[)(@[0-9a-zA-Z-_\.]+)(?:\s|$|\)|\])`)
+	// issueNumericPattern matches string that references to a numeric issue, e.g. #1287
+	issueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[)(#[0-9]+)(?:\s|$|\)|\]|:|\.(\s|$))`)
+	// issueAlphanumericPattern matches string that references to an alphanumeric issue, e.g. ABC-1234
+	issueAlphanumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([A-Z]{1,10}-[1-9][0-9]*)(?:\s|$|\)|\]|:|\.(\s|$))`)
+	// crossReferenceIssueNumericPattern matches string that references a numeric issue in a different repository
+	// e.g. gogits/gogs#12345
+	crossReferenceIssueNumericPattern = regexp.MustCompile(`(?:\s|^|\(|\[)([0-9a-zA-Z-_\.]+/[0-9a-zA-Z-_\.]+#[0-9]+)(?:\s|$|\)|\]|\.(\s|$))`)
+
+	// Same as GitHub. See
+	// https://help.github.com/articles/closing-issues-via-commit-messages
+	issueCloseKeywords  = []string{"close", "closes", "closed", "fix", "fixes", "fixed", "resolve", "resolves", "resolved"}
+	issueReopenKeywords = []string{"reopen", "reopens", "reopened"}
+
+	issueCloseKeywordsPat, issueReopenKeywordsPat *regexp.Regexp
+
+	giteaHostInit sync.Once
+	giteaHost     string
+)
+
+// XRefAction represents the kind of effect a cross reference has once is resolved
+type XRefAction int64
+
+const (
+	// XRefActionNone means the cross-reference is simply a comment
+	XRefActionNone XRefAction = iota // 0
+	// XRefActionCloses means the cross-reference should close an issue if it is resolved
+	XRefActionCloses // 1
+	// XRefActionReopens means the cross-reference should reopen an issue if it is resolved
+	XRefActionReopens // 2
+	// XRefActionNeutered means the cross-reference will no longer affect the source
+	XRefActionNeutered // 3
+)
+
+// IssueReference contains an unverified cross-reference to a local issue or pull request
+type IssueReference struct {
+	Index  int64
+	Owner  string
+	Name   string
+	Action XRefAction
+}
+
+// RenderizableReference contains an unverified cross-reference to with rendering information
+type RenderizableReference struct {
+	Issue          string
+	Owner          string
+	Name           string
+	RefLocation    *RefSpan
+	Action         XRefAction
+	ActionLocation *RefSpan
+}
+
+type rawReference struct {
+	index          int64
+	owner          string
+	name           string
+	action         XRefAction
+	issue          string
+	refLocation    *RefSpan
+	actionLocation *RefSpan
+}
+
+func rawToIssueReferenceList(reflist []*rawReference) []IssueReference {
+	refarr := make([]IssueReference, len(reflist))
+	for i, r := range reflist {
+		refarr[i] = IssueReference{
+			Index:  r.index,
+			Owner:  r.owner,
+			Name:   r.name,
+			Action: r.action,
+		}
+	}
+	return refarr
+}
+
+// RefSpan is the position where the reference was found within the parsed text
+type RefSpan struct {
+	Start int
+	End   int
+}
+
+func makeKeywordsPat(keywords []string) *regexp.Regexp {
+	return regexp.MustCompile(`(?i)(?:\s|^|\(|\[)(` + strings.Join(keywords, `|`) + `):? $`)
+}
+
+func init() {
+	issueCloseKeywordsPat = makeKeywordsPat(issueCloseKeywords)
+	issueReopenKeywordsPat = makeKeywordsPat(issueReopenKeywords)
+}
+
+// getGiteaHostName returns a normalized string with the local host name, with no scheme or port information
+func getGiteaHostName() string {
+	giteaHostInit.Do(func() {
+		if uapp, err := url.Parse(setting.AppURL); err == nil {
+			giteaHost = strings.ToLower(uapp.Host)
+		} else {
+			giteaHost = ""
+		}
+	})
+	return giteaHost
+}
+
+// FindAllMentionsMarkdown matches mention patterns in given content and
+// returns a list of found unvalidated user names **not including** the @ prefix.
+func FindAllMentionsMarkdown(content string) []string {
+	bcontent, _ := mdstripper.StripMarkdownBytes([]byte(content))
+	locations := FindAllMentionsBytes(bcontent)
+	mentions := make([]string, len(locations))
+	for i, val := range locations {
+		mentions[i] = string(bcontent[val.Start+1 : val.End])
+	}
+	return mentions
+}
+
+// FindAllMentionsBytes matches mention patterns in given content
+// and returns a list of locations for the unvalidated user names, including the @ prefix.
+func FindAllMentionsBytes(content []byte) []RefSpan {
+	mentions := mentionPattern.FindAllSubmatchIndex(content, -1)
+	ret := make([]RefSpan, len(mentions))
+	for i, val := range mentions {
+		ret[i] = RefSpan{Start: val[2], End: val[3]}
+	}
+	return ret
+}
+
+// FindFirstMentionBytes matches the first mention in then given content
+// and returns the location of the unvalidated user name, including the @ prefix.
+func FindFirstMentionBytes(content []byte) (bool, RefSpan) {
+	mention := mentionPattern.FindSubmatchIndex(content)
+	if mention == nil {
+		return false, RefSpan{}
+	}
+	return true, RefSpan{Start: mention[2], End: mention[3]}
+}
+
+// FindAllIssueReferencesMarkdown strips content from markdown markup
+// and returns a list of unvalidated references found in it.
+func FindAllIssueReferencesMarkdown(content string) []IssueReference {
+	return rawToIssueReferenceList(findAllIssueReferencesMarkdown(content))
+}
+
+func findAllIssueReferencesMarkdown(content string) []*rawReference {
+	bcontent, links := mdstripper.StripMarkdownBytes([]byte(content))
+	return findAllIssueReferencesBytes(bcontent, links)
+}
+
+// FindAllIssueReferences returns a list of unvalidated references found in a string.
+func FindAllIssueReferences(content string) []IssueReference {
+	return rawToIssueReferenceList(findAllIssueReferencesBytes([]byte(content), []string{}))
+}
+
+// FindRenderizableReferenceNumeric returns the first unvalidated reference found in a string.
+func FindRenderizableReferenceNumeric(content string) (bool, *RenderizableReference) {
+	match := issueNumericPattern.FindStringSubmatchIndex(content)
+	if match == nil {
+		if match = crossReferenceIssueNumericPattern.FindStringSubmatchIndex(content); match == nil {
+			return false, nil
+		}
+	}
+	r := getCrossReference([]byte(content), match[2], match[3], false)
+	if r == nil {
+		return false, nil
+	}
+
+	return true, &RenderizableReference{
+		Issue:          r.issue,
+		Owner:          r.owner,
+		Name:           r.name,
+		RefLocation:    r.refLocation,
+		Action:         r.action,
+		ActionLocation: r.actionLocation,
+	}
+}
+
+// FindRenderizableReferenceAlphanumeric returns the first alphanumeric unvalidated references found in a string.
+func FindRenderizableReferenceAlphanumeric(content string) (bool, *RenderizableReference) {
+	match := issueAlphanumericPattern.FindStringSubmatchIndex(content)
+	if match == nil {
+		return false, nil
+	}
+
+	action, location := findActionKeywords([]byte(content), match[2])
+
+	return true, &RenderizableReference{
+		Issue:          string(content[match[2]:match[3]]),
+		RefLocation:    &RefSpan{Start: match[2], End: match[3]},
+		Action:         action,
+		ActionLocation: location,
+	}
+}
+
+// FindAllIssueReferencesBytes returns a list of unvalidated references found in a byte slice.
+func findAllIssueReferencesBytes(content []byte, links []string) []*rawReference {
+
+	ret := make([]*rawReference, 0, 10)
+
+	matches := issueNumericPattern.FindAllSubmatchIndex(content, -1)
+	for _, match := range matches {
+		if ref := getCrossReference(content, match[2], match[3], false); ref != nil {
+			ret = append(ret, ref)
+		}
+	}
+
+	matches = crossReferenceIssueNumericPattern.FindAllSubmatchIndex(content, -1)
+	for _, match := range matches {
+		if ref := getCrossReference(content, match[2], match[3], false); ref != nil {
+			ret = append(ret, ref)
+		}
+	}
+
+	localhost := getGiteaHostName()
+	for _, link := range links {
+		if u, err := url.Parse(link); err == nil {
+			// Note: we're not attempting to match the URL scheme (http/https)
+			host := strings.ToLower(u.Host)
+			if host != "" && host != localhost {
+				continue
+			}
+			parts := strings.Split(u.EscapedPath(), "/")
+			// /user/repo/issues/3
+			if len(parts) != 5 || parts[0] != "" {
+				continue
+			}
+			if parts[3] != "issues" && parts[3] != "pulls" {
+				continue
+			}
+			// Note: closing/reopening keywords not supported with URLs
+			bytes := []byte(parts[1] + "/" + parts[2] + "#" + parts[4])
+			if ref := getCrossReference(bytes, 0, len(bytes), true); ref != nil {
+				ref.refLocation = nil
+				ret = append(ret, ref)
+			}
+		}
+	}
+
+	return ret
+}
+
+func getCrossReference(content []byte, start, end int, fromLink bool) *rawReference {
+	refid := string(content[start:end])
+	parts := strings.Split(refid, "#")
+	if len(parts) != 2 {
+		return nil
+	}
+	repo, issue := parts[0], parts[1]
+	index, err := strconv.ParseInt(issue, 10, 64)
+	if err != nil {
+		return nil
+	}
+	if repo == "" {
+		if fromLink {
+			// Markdown links must specify owner/repo
+			return nil
+		}
+		action, location := findActionKeywords(content, start)
+		return &rawReference{
+			index:          index,
+			action:         action,
+			issue:          issue,
+			refLocation:    &RefSpan{Start: start, End: end},
+			actionLocation: location,
+		}
+	}
+	parts = strings.Split(strings.ToLower(repo), "/")
+	if len(parts) != 2 {
+		return nil
+	}
+	owner, name := parts[0], parts[1]
+	if !validNamePattern.MatchString(owner) || !validNamePattern.MatchString(name) {
+		return nil
+	}
+	action, location := findActionKeywords(content, start)
+	return &rawReference{
+		index:          index,
+		owner:          owner,
+		name:           name,
+		action:         action,
+		issue:          issue,
+		refLocation:    &RefSpan{Start: start, End: end},
+		actionLocation: location,
+	}
+}
+
+func findActionKeywords(content []byte, start int) (XRefAction, *RefSpan) {
+	m := issueCloseKeywordsPat.FindSubmatchIndex(content[:start])
+	if m != nil {
+		return XRefActionCloses, &RefSpan{Start: m[2], End: m[3]}
+	}
+	m = issueReopenKeywordsPat.FindSubmatchIndex(content[:start])
+	if m != nil {
+		return XRefActionReopens, &RefSpan{Start: m[2], End: m[3]}
+	}
+	return XRefActionNone, nil
+}
diff --git a/modules/references/references_test.go b/modules/references/references_test.go
new file mode 100644
index 0000000000..f8153ffe36
--- /dev/null
+++ b/modules/references/references_test.go
@@ -0,0 +1,296 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package references
+
+import (
+	"testing"
+
+	"code.gitea.io/gitea/modules/setting"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestFindAllIssueReferences(t *testing.T) {
+
+	type result struct {
+		Index          int64
+		Owner          string
+		Name           string
+		Issue          string
+		Action         XRefAction
+		RefLocation    *RefSpan
+		ActionLocation *RefSpan
+	}
+
+	type testFixture struct {
+		input    string
+		expected []result
+	}
+
+	fixtures := []testFixture{
+		{
+			"Simply closes: #29 yes",
+			[]result{
+				{29, "", "", "29", XRefActionCloses, &RefSpan{Start: 15, End: 18}, &RefSpan{Start: 7, End: 13}},
+			},
+		},
+		{
+			"#123 no, this is a title.",
+			[]result{},
+		},
+		{
+			" #124 yes, this is a reference.",
+			[]result{
+				{124, "", "", "124", XRefActionNone, &RefSpan{Start: 0, End: 4}, nil},
+			},
+		},
+		{
+			"```\nThis is a code block.\n#723 no, it's a code block.```",
+			[]result{},
+		},
+		{
+			"This `#724` no, it's inline code.",
+			[]result{},
+		},
+		{
+			"This user3/repo4#200 yes.",
+			[]result{
+				{200, "user3", "repo4", "200", XRefActionNone, &RefSpan{Start: 5, End: 20}, nil},
+			},
+		},
+		{
+			"This [one](#919) no, this is a URL fragment.",
+			[]result{},
+		},
+		{
+			"This [two](/user2/repo1/issues/921) yes.",
+			[]result{
+				{921, "user2", "repo1", "921", XRefActionNone, nil, nil},
+			},
+		},
+		{
+			"This [three](/user2/repo1/pulls/922) yes.",
+			[]result{
+				{922, "user2", "repo1", "922", XRefActionNone, nil, nil},
+			},
+		},
+		{
+			"This [four](http://gitea.com:3000/user3/repo4/issues/203) yes.",
+			[]result{
+				{203, "user3", "repo4", "203", XRefActionNone, nil, nil},
+			},
+		},
+		{
+			"This [five](http://github.com/user3/repo4/issues/204) no.",
+			[]result{},
+		},
+		{
+			"This http://gitea.com:3000/user4/repo5/201 no, bad URL.",
+			[]result{},
+		},
+		{
+			"This http://gitea.com:3000/user4/repo5/pulls/202 yes.",
+			[]result{
+				{202, "user4", "repo5", "202", XRefActionNone, nil, nil},
+			},
+		},
+		{
+			"This http://GiTeA.COM:3000/user4/repo6/pulls/205 yes.",
+			[]result{
+				{205, "user4", "repo6", "205", XRefActionNone, nil, nil},
+			},
+		},
+		{
+			"Reopens #15 yes",
+			[]result{
+				{15, "", "", "15", XRefActionReopens, &RefSpan{Start: 8, End: 11}, &RefSpan{Start: 0, End: 7}},
+			},
+		},
+		{
+			"This closes #20 for you yes",
+			[]result{
+				{20, "", "", "20", XRefActionCloses, &RefSpan{Start: 12, End: 15}, &RefSpan{Start: 5, End: 11}},
+			},
+		},
+		{
+			"Do you fix user6/repo6#300 ? yes",
+			[]result{
+				{300, "user6", "repo6", "300", XRefActionCloses, &RefSpan{Start: 11, End: 26}, &RefSpan{Start: 7, End: 10}},
+			},
+		},
+		{
+			"For 999 #1235 no keyword, but yes",
+			[]result{
+				{1235, "", "", "1235", XRefActionNone, &RefSpan{Start: 8, End: 13}, nil},
+			},
+		},
+		{
+			"Which abc. #9434 same as above",
+			[]result{
+				{9434, "", "", "9434", XRefActionNone, &RefSpan{Start: 11, End: 16}, nil},
+			},
+		},
+		{
+			"This closes #600 and reopens #599",
+			[]result{
+				{600, "", "", "600", XRefActionCloses, &RefSpan{Start: 12, End: 16}, &RefSpan{Start: 5, End: 11}},
+				{599, "", "", "599", XRefActionReopens, &RefSpan{Start: 29, End: 33}, &RefSpan{Start: 21, End: 28}},
+			},
+		},
+	}
+
+	// Save original value for other tests that may rely on it
+	prevURL := setting.AppURL
+	setting.AppURL = "https://gitea.com:3000/"
+
+	for _, fixture := range fixtures {
+		expraw := make([]*rawReference, len(fixture.expected))
+		for i, e := range fixture.expected {
+			expraw[i] = &rawReference{
+				index:          e.Index,
+				owner:          e.Owner,
+				name:           e.Name,
+				action:         e.Action,
+				issue:          e.Issue,
+				refLocation:    e.RefLocation,
+				actionLocation: e.ActionLocation,
+			}
+		}
+		expref := rawToIssueReferenceList(expraw)
+		refs := FindAllIssueReferencesMarkdown(fixture.input)
+		assert.EqualValues(t, expref, refs, "Failed to parse: {%s}", fixture.input)
+		rawrefs := findAllIssueReferencesMarkdown(fixture.input)
+		assert.EqualValues(t, expraw, rawrefs, "Failed to parse: {%s}", fixture.input)
+	}
+
+	// Restore for other tests that may rely on the original value
+	setting.AppURL = prevURL
+
+	type alnumFixture struct {
+		input          string
+		issue          string
+		refLocation    *RefSpan
+		action         XRefAction
+		actionLocation *RefSpan
+	}
+
+	alnumFixtures := []alnumFixture{
+		{
+			"This ref ABC-123 is alphanumeric",
+			"ABC-123", &RefSpan{Start: 9, End: 16},
+			XRefActionNone, nil,
+		},
+		{
+			"This closes ABCD-1234 alphanumeric",
+			"ABCD-1234", &RefSpan{Start: 12, End: 21},
+			XRefActionCloses, &RefSpan{Start: 5, End: 11},
+		},
+	}
+
+	for _, fixture := range alnumFixtures {
+		found, ref := FindRenderizableReferenceAlphanumeric(fixture.input)
+		if fixture.issue == "" {
+			assert.False(t, found, "Failed to parse: {%s}", fixture.input)
+		} else {
+			assert.True(t, found, "Failed to parse: {%s}", fixture.input)
+			assert.Equal(t, fixture.issue, ref.Issue, "Failed to parse: {%s}", fixture.input)
+			assert.Equal(t, fixture.refLocation, ref.RefLocation, "Failed to parse: {%s}", fixture.input)
+			assert.Equal(t, fixture.action, ref.Action, "Failed to parse: {%s}", fixture.input)
+			assert.Equal(t, fixture.actionLocation, ref.ActionLocation, "Failed to parse: {%s}", fixture.input)
+		}
+	}
+}
+
+func TestRegExp_mentionPattern(t *testing.T) {
+	trueTestCases := []string{
+		"@Unknwon",
+		"@ANT_123",
+		"@xxx-DiN0-z-A..uru..s-xxx",
+		"   @lol   ",
+		" @Te-st",
+		"(@gitea)",
+		"[@gitea]",
+	}
+	falseTestCases := []string{
+		"@ 0",
+		"@ ",
+		"@",
+		"",
+		"ABC",
+		"/home/gitea/@gitea",
+		"\"@gitea\"",
+	}
+
+	for _, testCase := range trueTestCases {
+		res := mentionPattern.MatchString(testCase)
+		assert.True(t, res)
+	}
+	for _, testCase := range falseTestCases {
+		res := mentionPattern.MatchString(testCase)
+		assert.False(t, res)
+	}
+}
+
+func TestRegExp_issueNumericPattern(t *testing.T) {
+	trueTestCases := []string{
+		"#1234",
+		"#0",
+		"#1234567890987654321",
+		"  #12",
+		"#12:",
+		"ref: #12: msg",
+	}
+	falseTestCases := []string{
+		"# 1234",
+		"# 0",
+		"# ",
+		"#",
+		"#ABC",
+		"#1A2B",
+		"",
+		"ABC",
+	}
+
+	for _, testCase := range trueTestCases {
+		assert.True(t, issueNumericPattern.MatchString(testCase))
+	}
+	for _, testCase := range falseTestCases {
+		assert.False(t, issueNumericPattern.MatchString(testCase))
+	}
+}
+
+func TestRegExp_issueAlphanumericPattern(t *testing.T) {
+	trueTestCases := []string{
+		"ABC-1234",
+		"A-1",
+		"RC-80",
+		"ABCDEFGHIJ-1234567890987654321234567890",
+		"ABC-123.",
+		"(ABC-123)",
+		"[ABC-123]",
+		"ABC-123:",
+	}
+	falseTestCases := []string{
+		"RC-08",
+		"PR-0",
+		"ABCDEFGHIJK-1",
+		"PR_1",
+		"",
+		"#ABC",
+		"",
+		"ABC",
+		"GG-",
+		"rm-1",
+		"/home/gitea/ABC-1234",
+		"MY-STRING-ABC-123",
+	}
+
+	for _, testCase := range trueTestCases {
+		assert.True(t, issueAlphanumericPattern.MatchString(testCase))
+	}
+	for _, testCase := range falseTestCases {
+		assert.False(t, issueAlphanumericPattern.MatchString(testCase))
+	}
+}
diff --git a/modules/repofiles/content.go b/modules/repofiles/content.go
index 9637658e78..d7d43ef9d1 100644
--- a/modules/repofiles/content.go
+++ b/modules/repofiles/content.go
@@ -38,6 +38,9 @@ func (ct *ContentType) String() string {
 // GetContentsOrList gets the meta data of a file's contents (*ContentsResponse) if treePath not a tree
 // directory, otherwise a listing of file contents ([]*ContentsResponse). Ref can be a branch, commit or tag
 func GetContentsOrList(repo *models.Repository, treePath, ref string) (interface{}, error) {
+	if repo.IsEmpty {
+		return make([]interface{}, 0), nil
+	}
 	if ref == "" {
 		ref = repo.DefaultBranch
 	}
diff --git a/modules/repofiles/content_test.go b/modules/repofiles/content_test.go
index ef6c5eafc2..cd98c54ea6 100644
--- a/modules/repofiles/content_test.go
+++ b/modules/repofiles/content_test.go
@@ -190,3 +190,19 @@ func TestGetContentsOrListErrors(t *testing.T) {
 		assert.Nil(t, fileContentResponse)
 	})
 }
+
+func TestGetContentsOrListOfEmptyRepos(t *testing.T) {
+	models.PrepareTestEnv(t)
+	ctx := test.MockContext(t, "user2/repo15")
+	ctx.SetParams(":id", "15")
+	test.LoadRepo(t, ctx, 15)
+	test.LoadUser(t, ctx, 2)
+	test.LoadGitRepo(t, ctx)
+	repo := ctx.Repo.Repository
+
+	t.Run("empty repo", func(t *testing.T) {
+		contents, err := GetContentsOrList(repo, "", "")
+		assert.NoError(t, err)
+		assert.Empty(t, contents)
+	})
+}
diff --git a/modules/repofiles/file_test.go b/modules/repofiles/file_test.go
index 7c45139dd9..95ec175ed4 100644
--- a/modules/repofiles/file_test.go
+++ b/modules/repofiles/file_test.go
@@ -73,7 +73,7 @@ func getExpectedFileResponse() *api.FileResponse {
 		},
 		Verification: &api.PayloadCommitVerification{
 			Verified:  false,
-			Reason:    "",
+			Reason:    "gpg.error.not_signed_commit",
 			Signature: "",
 			Payload:   "",
 		},
diff --git a/modules/repofiles/temp_repo.go b/modules/repofiles/temp_repo.go
index f791c3cb96..b07d2a8973 100644
--- a/modules/repofiles/temp_repo.go
+++ b/modules/repofiles/temp_repo.go
@@ -21,6 +21,8 @@ import (
 	"code.gitea.io/gitea/modules/process"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/services/gitdiff"
+
+	"github.com/mcuadros/go-version"
 )
 
 // TemporaryUploadRepository is a type to wrap our upload repositories as a shallow clone
@@ -254,7 +256,11 @@ func (t *TemporaryUploadRepository) CommitTree(author, committer *models.User, t
 	authorSig := author.NewGitSig()
 	committerSig := committer.NewGitSig()
 
-	// FIXME: Should we add SSH_ORIGINAL_COMMAND to this
+	binVersion, err := git.BinVersion()
+	if err != nil {
+		return "", fmt.Errorf("Unable to get git version: %v", err)
+	}
+
 	// Because this may call hooks we should pass in the environment
 	env := append(os.Environ(),
 		"GIT_AUTHOR_NAME="+authorSig.Name,
@@ -264,11 +270,29 @@ func (t *TemporaryUploadRepository) CommitTree(author, committer *models.User, t
 		"GIT_COMMITTER_EMAIL="+committerSig.Email,
 		"GIT_COMMITTER_DATE="+commitTimeStr,
 	)
-	commitHash, stderr, err := process.GetManager().ExecDirEnv(5*time.Minute,
+
+	messageBytes := new(bytes.Buffer)
+	_, _ = messageBytes.WriteString(message)
+	_, _ = messageBytes.WriteString("\n")
+
+	args := []string{"commit-tree", treeHash, "-p", "HEAD"}
+
+	// Determine if we should sign
+	if version.Compare(binVersion, "1.7.9", ">=") {
+		sign, keyID := t.repo.SignCRUDAction(author, t.basePath, "HEAD")
+		if sign {
+			args = append(args, "-S"+keyID)
+		} else if version.Compare(binVersion, "2.0.0", ">=") {
+			args = append(args, "--no-gpg-sign")
+		}
+	}
+
+	commitHash, stderr, err := process.GetManager().ExecDirEnvStdIn(5*time.Minute,
 		t.basePath,
 		fmt.Sprintf("commitTree (git commit-tree): %s", t.basePath),
 		env,
-		git.GitExecutable, "commit-tree", treeHash, "-p", "HEAD", "-m", message)
+		messageBytes,
+		git.GitExecutable, args...)
 	if err != nil {
 		return "", fmt.Errorf("git commit-tree: %s", stderr)
 	}
@@ -328,6 +352,12 @@ func (t *TemporaryUploadRepository) DiffIndex() (diff *gitdiff.Diff, err error)
 
 // CheckAttribute checks the given attribute of the provided files
 func (t *TemporaryUploadRepository) CheckAttribute(attribute string, args ...string) (map[string]map[string]string, error) {
+	binVersion, err := git.BinVersion()
+	if err != nil {
+		log.Error("Error retrieving git version: %v", err)
+		return nil, err
+	}
+
 	stdOut := new(bytes.Buffer)
 	stdErr := new(bytes.Buffer)
 
@@ -335,7 +365,14 @@ func (t *TemporaryUploadRepository) CheckAttribute(attribute string, args ...str
 	ctx, cancel := context.WithTimeout(context.Background(), timeout)
 	defer cancel()
 
-	cmdArgs := []string{"check-attr", "-z", attribute, "--cached", "--"}
+	cmdArgs := []string{"check-attr", "-z", attribute}
+
+	// git check-attr --cached first appears in git 1.7.8
+	if version.Compare(binVersion, "1.7.8", ">=") {
+		cmdArgs = append(cmdArgs, "--cached")
+	}
+	cmdArgs = append(cmdArgs, "--")
+
 	for _, arg := range args {
 		if arg != "" {
 			cmdArgs = append(cmdArgs, arg)
@@ -353,7 +390,7 @@ func (t *TemporaryUploadRepository) CheckAttribute(attribute string, args ...str
 	}
 
 	pid := process.GetManager().Add(desc, cmd)
-	err := cmd.Wait()
+	err = cmd.Wait()
 	process.GetManager().Remove(pid)
 
 	if err != nil {
diff --git a/modules/repofiles/update.go b/modules/repofiles/update.go
index ee1b16bce9..8a1e51730b 100644
--- a/modules/repofiles/update.go
+++ b/modules/repofiles/update.go
@@ -19,6 +19,7 @@ import (
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/structs"
+	pull_service "code.gitea.io/gitea/services/pull"
 
 	stdcharset "golang.org/x/net/html/charset"
 	"golang.org/x/text/transform"
@@ -313,12 +314,6 @@ func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *Up
 		}
 	}
 
-	// Check there is no way this can return multiple infos
-	filename2attribute2info, err := t.CheckAttribute("filter", treePath)
-	if err != nil {
-		return nil, err
-	}
-
 	content := opts.Content
 	if bom {
 		content = string(charset.UTF8BOM) + content
@@ -341,16 +336,23 @@ func CreateOrUpdateRepoFile(repo *models.Repository, doer *models.User, opts *Up
 	opts.Content = content
 	var lfsMetaObject *models.LFSMetaObject
 
-	if setting.LFS.StartServer && filename2attribute2info[treePath] != nil && filename2attribute2info[treePath]["filter"] == "lfs" {
-		// OK so we are supposed to LFS this data!
-		oid, err := models.GenerateLFSOid(strings.NewReader(opts.Content))
+	if setting.LFS.StartServer {
+		// Check there is no way this can return multiple infos
+		filename2attribute2info, err := t.CheckAttribute("filter", treePath)
 		if err != nil {
 			return nil, err
 		}
-		lfsMetaObject = &models.LFSMetaObject{Oid: oid, Size: int64(len(opts.Content)), RepositoryID: repo.ID}
-		content = lfsMetaObject.Pointer()
-	}
 
+		if filename2attribute2info[treePath] != nil && filename2attribute2info[treePath]["filter"] == "lfs" {
+			// OK so we are supposed to LFS this data!
+			oid, err := models.GenerateLFSOid(strings.NewReader(opts.Content))
+			if err != nil {
+				return nil, err
+			}
+			lfsMetaObject = &models.LFSMetaObject{Oid: oid, Size: int64(len(opts.Content)), RepositoryID: repo.ID}
+			content = lfsMetaObject.Pointer()
+		}
+	}
 	// Add the object to the database
 	objectHash, err := t.HashObject(strings.NewReader(content))
 	if err != nil {
@@ -497,7 +499,7 @@ func PushUpdate(repo *models.Repository, branch string, opts models.PushUpdateOp
 
 	log.Trace("TriggerTask '%s/%s' by %s", repo.Name, branch, pusher.Name)
 
-	go models.AddTestPullRequestTask(pusher, repo.ID, branch, true)
+	go pull_service.AddTestPullRequestTask(pusher, repo.ID, branch, true)
 
 	if opts.RefFullName == git.BranchPrefix+repo.DefaultBranch {
 		models.UpdateRepoIndexer(repo)
diff --git a/modules/repofiles/upload.go b/modules/repofiles/upload.go
index f2ffec7ebc..202e66b89a 100644
--- a/modules/repofiles/upload.go
+++ b/modules/repofiles/upload.go
@@ -74,9 +74,12 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep
 		infos[i] = uploadInfo{upload: upload}
 	}
 
-	filename2attribute2info, err := t.CheckAttribute("filter", names...)
-	if err != nil {
-		return err
+	var filename2attribute2info map[string]map[string]string
+	if setting.LFS.StartServer {
+		filename2attribute2info, err = t.CheckAttribute("filter", names...)
+		if err != nil {
+			return err
+		}
 	}
 
 	// Copy uploaded files into repository.
@@ -88,7 +91,7 @@ func UploadRepoFiles(repo *models.Repository, doer *models.User, opts *UploadRep
 		defer file.Close()
 
 		var objectHash string
-		if filename2attribute2info[uploadInfo.upload.Name] != nil && filename2attribute2info[uploadInfo.upload.Name]["filter"] == "lfs" {
+		if setting.LFS.StartServer && filename2attribute2info[uploadInfo.upload.Name] != nil && filename2attribute2info[uploadInfo.upload.Name]["filter"] == "lfs" {
 			// Handle LFS
 			// FIXME: Inefficient! this should probably happen in models.Upload
 			oid, err := models.GenerateLFSOid(file)
diff --git a/modules/repofiles/verification.go b/modules/repofiles/verification.go
index 9fc084daaf..3889b7993c 100644
--- a/modules/repofiles/verification.go
+++ b/modules/repofiles/verification.go
@@ -18,10 +18,16 @@ func GetPayloadCommitVerification(commit *git.Commit) *structs.PayloadCommitVeri
 		verification.Signature = commit.Signature.Signature
 		verification.Payload = commit.Signature.Payload
 	}
-	if verification.Reason != "" {
-		verification.Reason = commitVerification.Reason
-	} else if verification.Verified {
-		verification.Reason = "unsigned"
+	if commitVerification.SigningUser != nil {
+		verification.Signer = &structs.PayloadUser{
+			Name:  commitVerification.SigningUser.Name,
+			Email: commitVerification.SigningUser.Email,
+		}
+	}
+	verification.Verified = commitVerification.Verified
+	verification.Reason = commitVerification.Reason
+	if verification.Reason == "" && !verification.Verified {
+		verification.Reason = "gpg.error.not_signed_commit"
 	}
 	return verification
 }
diff --git a/modules/setting/cron.go b/modules/setting/cron.go
index c544c6c228..77f55168aa 100644
--- a/modules/setting/cron.go
+++ b/modules/setting/cron.go
@@ -49,6 +49,9 @@ var (
 			Schedule   string
 			OlderThan  time.Duration
 		} `ini:"cron.deleted_branches_cleanup"`
+		UpdateMigrationPosterID struct {
+			Schedule string
+		} `ini:"cron.update_migration_poster_id"`
 	}{
 		UpdateMirror: struct {
 			Enabled    bool
@@ -114,6 +117,11 @@ var (
 			Schedule:   "@every 24h",
 			OlderThan:  24 * time.Hour,
 		},
+		UpdateMigrationPosterID: struct {
+			Schedule string
+		}{
+			Schedule: "@every 24h",
+		},
 	}
 )
 
diff --git a/modules/setting/database.go b/modules/setting/database.go
index 2cac4824df..8c49ba3c5a 100644
--- a/modules/setting/database.go
+++ b/modules/setting/database.go
@@ -42,12 +42,11 @@ var (
 		DBConnectRetries  int
 		DBConnectBackoff  time.Duration
 		MaxIdleConns      int
+		MaxOpenConns      int
 		ConnMaxLifetime   time.Duration
 		IterateBufferSize int
 	}{
-		Timeout:         500,
-		MaxIdleConns:    0,
-		ConnMaxLifetime: 3 * time.Second,
+		Timeout: 500,
 	}
 )
 
@@ -80,8 +79,13 @@ func InitDBConfig() {
 	Database.Charset = sec.Key("CHARSET").In("utf8", []string{"utf8", "utf8mb4"})
 	Database.Path = sec.Key("PATH").MustString(filepath.Join(AppDataPath, "gitea.db"))
 	Database.Timeout = sec.Key("SQLITE_TIMEOUT").MustInt(500)
-	Database.MaxIdleConns = sec.Key("MAX_IDLE_CONNS").MustInt(0)
-	Database.ConnMaxLifetime = sec.Key("CONN_MAX_LIFE_TIME").MustDuration(3 * time.Second)
+	Database.MaxIdleConns = sec.Key("MAX_IDLE_CONNS").MustInt(2)
+	if Database.UseMySQL {
+		Database.ConnMaxLifetime = sec.Key("CONN_MAX_LIFE_TIME").MustDuration(3 * time.Second)
+	} else {
+		Database.ConnMaxLifetime = sec.Key("CONN_MAX_LIFE_TIME").MustDuration(0)
+	}
+	Database.MaxOpenConns = sec.Key("MAX_OPEN_CONNS").MustInt(0)
 
 	Database.IterateBufferSize = sec.Key("ITERATE_BUFFER_SIZE").MustInt(50)
 	Database.LogSQL = sec.Key("LOG_SQL").MustBool(true)
diff --git a/modules/setting/indexer.go b/modules/setting/indexer.go
index 30c670d407..fbaef3fcf2 100644
--- a/modules/setting/indexer.go
+++ b/modules/setting/indexer.go
@@ -8,6 +8,7 @@ import (
 	"path"
 	"path/filepath"
 	"strings"
+	"time"
 
 	"code.gitea.io/gitea/modules/log"
 
@@ -34,6 +35,7 @@ var (
 		IssueQueueDir         string
 		IssueQueueConnStr     string
 		IssueQueueBatchNumber int
+		StartupTimeout        time.Duration
 		IncludePatterns       []glob.Glob
 		ExcludePatterns       []glob.Glob
 	}{
@@ -67,6 +69,7 @@ func newIndexerService() {
 	Indexer.IssueQueueDir = sec.Key("ISSUE_INDEXER_QUEUE_DIR").MustString(path.Join(AppDataPath, "indexers/issues.queue"))
 	Indexer.IssueQueueConnStr = sec.Key("ISSUE_INDEXER_QUEUE_CONN_STR").MustString(path.Join(AppDataPath, ""))
 	Indexer.IssueQueueBatchNumber = sec.Key("ISSUE_INDEXER_QUEUE_BATCH_NUMBER").MustInt(20)
+	Indexer.StartupTimeout = sec.Key("STARTUP_TIMEOUT").MustDuration(30 * time.Second)
 }
 
 // IndexerGlobFromString parses a comma separated list of patterns and returns a glob.Glob slice suited for repo indexing
diff --git a/modules/setting/log.go b/modules/setting/log.go
index 5e2d2d769d..cb8f142084 100644
--- a/modules/setting/log.go
+++ b/modules/setting/log.go
@@ -6,6 +6,7 @@ package setting
 
 import (
 	"encoding/json"
+	"fmt"
 	golog "log"
 	"os"
 	"path"
@@ -17,6 +18,8 @@ import (
 	ini "gopkg.in/ini.v1"
 )
 
+var filenameSuffix = ""
+
 type defaultLogOptions struct {
 	levelName      string // LogLevel
 	flags          string
@@ -112,7 +115,7 @@ func generateLogConfig(sec *ini.Section, name string, defaults defaultLogOptions
 			panic(err.Error())
 		}
 
-		logConfig["filename"] = logPath
+		logConfig["filename"] = logPath + filenameSuffix
 		logConfig["rotate"] = sec.Key("LOG_ROTATE").MustBool(true)
 		logConfig["maxsize"] = 1 << uint(sec.Key("MAX_SIZE_SHIFT").MustInt(28))
 		logConfig["daily"] = sec.Key("DAILY_ROTATE").MustBool(true)
@@ -277,6 +280,12 @@ func newLogService() {
 	golog.SetOutput(log.NewLoggerAsWriter("INFO", log.GetLogger(log.DEFAULT)))
 }
 
+// RestartLogsWithPIDSuffix restarts the logs with a PID suffix on files
+func RestartLogsWithPIDSuffix() {
+	filenameSuffix = fmt.Sprintf(".%d", os.Getpid())
+	NewLogServices(false)
+}
+
 // NewLogServices creates all the log services
 func NewLogServices(disableConsole bool) {
 	newLogService()
diff --git a/modules/setting/repository.go b/modules/setting/repository.go
index 728741576d..19c68d003f 100644
--- a/modules/setting/repository.go
+++ b/modules/setting/repository.go
@@ -65,6 +65,16 @@ var (
 		Issue struct {
 			LockReasons []string
 		} `ini:"repository.issue"`
+
+		Signing struct {
+			SigningKey    string
+			SigningName   string
+			SigningEmail  string
+			InitialCommit []string
+			CRUDActions   []string `ini:"CRUD_ACTIONS"`
+			Merges        []string
+			Wiki          []string
+		} `ini:"repository.signing"`
 	}{
 		AnsiCharset:                             "",
 		ForcePrivate:                            false,
@@ -122,6 +132,25 @@ var (
 		}{
 			LockReasons: strings.Split("Too heated,Off-topic,Spam,Resolved", ","),
 		},
+
+		// Signing settings
+		Signing: struct {
+			SigningKey    string
+			SigningName   string
+			SigningEmail  string
+			InitialCommit []string
+			CRUDActions   []string `ini:"CRUD_ACTIONS"`
+			Merges        []string
+			Wiki          []string
+		}{
+			SigningKey:    "default",
+			SigningName:   "",
+			SigningEmail:  "",
+			InitialCommit: []string{"always"},
+			CRUDActions:   []string{"pubkey", "twofa", "parentsigned"},
+			Merges:        []string{"pubkey", "twofa", "basesigned", "commitssigned"},
+			Wiki:          []string{"never"},
+		},
 	}
 	RepoRootPath string
 	ScriptType   = "bash"
diff --git a/modules/setting/service.go b/modules/setting/service.go
index 905b1326f7..dea4081ee8 100644
--- a/modules/setting/service.go
+++ b/modules/setting/service.go
@@ -23,6 +23,7 @@ var Service struct {
 	ShowRegistrationButton                  bool
 	RequireSignInView                       bool
 	EnableNotifyMail                        bool
+	EnableBasicAuth                         bool
 	EnableReverseProxyAuth                  bool
 	EnableReverseProxyAutoRegister          bool
 	EnableReverseProxyEmail                 bool
@@ -60,6 +61,7 @@ func newService() {
 	Service.EmailDomainWhitelist = sec.Key("EMAIL_DOMAIN_WHITELIST").Strings(",")
 	Service.ShowRegistrationButton = sec.Key("SHOW_REGISTRATION_BUTTON").MustBool(!(Service.DisableRegistration || Service.AllowOnlyExternalRegistration))
 	Service.RequireSignInView = sec.Key("REQUIRE_SIGNIN_VIEW").MustBool()
+	Service.EnableBasicAuth = sec.Key("ENABLE_BASIC_AUTHENTICATION").MustBool(true)
 	Service.EnableReverseProxyAuth = sec.Key("ENABLE_REVERSE_PROXY_AUTHENTICATION").MustBool()
 	Service.EnableReverseProxyAutoRegister = sec.Key("ENABLE_REVERSE_PROXY_AUTO_REGISTRATION").MustBool()
 	Service.EnableReverseProxyEmail = sec.Key("ENABLE_REVERSE_PROXY_EMAIL").MustBool()
diff --git a/modules/setting/setting.go b/modules/setting/setting.go
index 5e476854b2..f3dd45d7bf 100644
--- a/modules/setting/setting.go
+++ b/modules/setting/setting.go
@@ -87,6 +87,7 @@ var (
 	CertFile             string
 	KeyFile              string
 	StaticRootPath       string
+	StaticCacheTime      time.Duration
 	EnableGzip           bool
 	LandingPageURL       LandingPage
 	UnixSocketPermission uint32
@@ -96,6 +97,9 @@ var (
 	LetsEncryptTOS       bool
 	LetsEncryptDirectory string
 	LetsEncryptEmail     string
+	GracefulRestartable  bool
+	GracefulHammerTime   time.Duration
+	StaticURLPrefix      string
 
 	SSH = struct {
 		Disabled                 bool           `ini:"DISABLE_SSH"`
@@ -146,6 +150,7 @@ var (
 	MinPasswordLength     int
 	ImportLocalPaths      bool
 	DisableGitHooks       bool
+	PasswordComplexity    []string
 	PasswordHashAlgo      string
 
 	// UI settings
@@ -515,7 +520,7 @@ func NewContext() {
 	} else {
 		log.Warn("Custom config '%s' not found, ignore this if you're running first time", CustomConf)
 	}
-	Cfg.NameMapper = ini.AllCapsUnderscore
+	Cfg.NameMapper = ini.SnackCase
 
 	homeDir, err := com.HomeDir()
 	if err != nil {
@@ -561,13 +566,15 @@ func NewContext() {
 	Domain = sec.Key("DOMAIN").MustString("localhost")
 	HTTPAddr = sec.Key("HTTP_ADDR").MustString("0.0.0.0")
 	HTTPPort = sec.Key("HTTP_PORT").MustString("3000")
+	GracefulRestartable = sec.Key("ALLOW_GRACEFUL_RESTARTS").MustBool(true)
+	GracefulHammerTime = sec.Key("GRACEFUL_HAMMER_TIME").MustDuration(60 * time.Second)
 
 	defaultAppURL := string(Protocol) + "://" + Domain
 	if (Protocol == HTTP && HTTPPort != "80") || (Protocol == HTTPS && HTTPPort != "443") {
 		defaultAppURL += ":" + HTTPPort
 	}
 	AppURL = sec.Key("ROOT_URL").MustString(defaultAppURL)
-	AppURL = strings.TrimRight(AppURL, "/") + "/"
+	AppURL = strings.TrimSuffix(AppURL, "/") + "/"
 
 	// Check if has app suburl.
 	appURL, err := url.Parse(AppURL)
@@ -577,6 +584,7 @@ func NewContext() {
 	// Suburl should start with '/' and end without '/', such as '/{subpath}'.
 	// This value is empty if site does not have sub-url.
 	AppSubURL = strings.TrimSuffix(appURL.Path, "/")
+	StaticURLPrefix = strings.TrimSuffix(sec.Key("STATIC_URL_PREFIX").MustString(AppSubURL), "/")
 	AppSubURLDepth = strings.Count(AppSubURL, "/")
 	// Check if Domain differs from AppURL domain than update it to AppURL's domain
 	// TODO: Can be replaced with url.Hostname() when minimal GoLang version is 1.8
@@ -606,6 +614,7 @@ func NewContext() {
 	OfflineMode = sec.Key("OFFLINE_MODE").MustBool()
 	DisableRouterLog = sec.Key("DISABLE_ROUTER_LOG").MustBool()
 	StaticRootPath = sec.Key("STATIC_ROOT_PATH").MustString(AppWorkPath)
+	StaticCacheTime = sec.Key("STATIC_CACHE_TIME").MustDuration(6 * time.Hour)
 	AppDataPath = sec.Key("APP_DATA_PATH").MustString(path.Join(AppWorkPath, "data"))
 	EnableGzip = sec.Key("ENABLE_GZIP").MustBool()
 	EnablePprof = sec.Key("ENABLE_PPROF").MustBool(false)
@@ -774,6 +783,15 @@ func NewContext() {
 
 	InternalToken = loadInternalToken(sec)
 
+	cfgdata := sec.Key("PASSWORD_COMPLEXITY").Strings(",")
+	PasswordComplexity = make([]string, 0, len(cfgdata))
+	for _, name := range cfgdata {
+		name := strings.ToLower(strings.Trim(name, `"`))
+		if name != "" {
+			PasswordComplexity = append(PasswordComplexity, name)
+		}
+	}
+
 	sec = Cfg.Section("attachment")
 	AttachmentPath = sec.Key("PATH").MustString(path.Join(AppDataPath, "attachments"))
 	if !filepath.IsAbs(AttachmentPath) {
@@ -1043,4 +1061,5 @@ func NewServices() {
 	newNotifyMailService()
 	newWebhookService()
 	newIndexerService()
+	newTaskService()
 }
diff --git a/modules/setting/task.go b/modules/setting/task.go
new file mode 100644
index 0000000000..97704d4a4d
--- /dev/null
+++ b/modules/setting/task.go
@@ -0,0 +1,25 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package setting
+
+var (
+	// Task settings
+	Task = struct {
+		QueueType    string
+		QueueLength  int
+		QueueConnStr string
+	}{
+		QueueType:    ChannelQueueType,
+		QueueLength:  1000,
+		QueueConnStr: "addrs=127.0.0.1:6379 db=0",
+	}
+)
+
+func newTaskService() {
+	sec := Cfg.Section("task")
+	Task.QueueType = sec.Key("QUEUE_TYPE").MustString(ChannelQueueType)
+	Task.QueueLength = sec.Key("QUEUE_LENGTH").MustInt(1000)
+	Task.QueueConnStr = sec.Key("QUEUE_CONN_STR").MustString("addrs=127.0.0.1:6379 db=0")
+}
diff --git a/modules/ssh/ssh.go b/modules/ssh/ssh.go
index 7ff0c32326..e7a694683a 100644
--- a/modules/ssh/ssh.go
+++ b/modules/ssh/ssh.go
@@ -183,12 +183,7 @@ func Listen(host string, port int, ciphers []string, keyExchanges []string, macs
 		log.Error("Failed to set Host Key. %s", err)
 	}
 
-	go func() {
-		err := srv.ListenAndServe()
-		if err != nil {
-			log.Error("Failed to serve with builtin SSH server. %s", err)
-		}
-	}()
+	go listen(&srv)
 
 }
 
diff --git a/modules/ssh/ssh_graceful.go b/modules/ssh/ssh_graceful.go
new file mode 100644
index 0000000000..d66c7d6540
--- /dev/null
+++ b/modules/ssh/ssh_graceful.go
@@ -0,0 +1,30 @@
+// +build !windows
+
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package ssh
+
+import (
+	"code.gitea.io/gitea/modules/graceful"
+	"code.gitea.io/gitea/modules/log"
+
+	"github.com/gliderlabs/ssh"
+)
+
+func listen(server *ssh.Server) {
+	gracefulServer := graceful.NewServer("tcp", server.Addr)
+
+	err := gracefulServer.ListenAndServe(server.Serve)
+	if err != nil {
+		log.Critical("Failed to start SSH server: %v", err)
+	}
+	log.Info("SSH Listener: %s Closed", server.Addr)
+
+}
+
+// Unused informs our cleanup routine that we will not be using a ssh port
+func Unused() {
+	graceful.InformCleanup()
+}
diff --git a/modules/ssh/ssh_windows.go b/modules/ssh/ssh_windows.go
new file mode 100644
index 0000000000..55032e17cd
--- /dev/null
+++ b/modules/ssh/ssh_windows.go
@@ -0,0 +1,24 @@
+// +build windows
+
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package ssh
+
+import (
+	"code.gitea.io/gitea/modules/log"
+	"github.com/gliderlabs/ssh"
+)
+
+func listen(server *ssh.Server) {
+	err := server.ListenAndServe()
+	if err != nil {
+		log.Critical("Failed to serve with builtin SSH server. %s", err)
+	}
+}
+
+// Unused does nothing on windows
+func Unused() {
+	// Do nothing
+}
diff --git a/modules/structs/hook.go b/modules/structs/hook.go
index 9a25219e36..e036442904 100644
--- a/modules/structs/hook.go
+++ b/modules/structs/hook.go
@@ -91,10 +91,11 @@ type PayloadCommit struct {
 
 // PayloadCommitVerification represents the GPG verification of a commit
 type PayloadCommitVerification struct {
-	Verified  bool   `json:"verified"`
-	Reason    string `json:"reason"`
-	Signature string `json:"signature"`
-	Payload   string `json:"payload"`
+	Verified  bool         `json:"verified"`
+	Reason    string       `json:"reason"`
+	Signature string       `json:"signature"`
+	Signer    *PayloadUser `json:"signer"`
+	Payload   string       `json:"payload"`
 }
 
 var (
@@ -235,6 +236,7 @@ type IssueCommentPayload struct {
 	Changes    *ChangesPayload        `json:"changes,omitempty"`
 	Repository *Repository            `json:"repository"`
 	Sender     *User                  `json:"sender"`
+	IsPull     bool                   `json:"is_pull"`
 }
 
 // SetSecret modifies the secret of the IssueCommentPayload
@@ -418,6 +420,7 @@ type PullRequestPayload struct {
 	PullRequest *PullRequest    `json:"pull_request"`
 	Repository  *Repository     `json:"repository"`
 	Sender      *User           `json:"sender"`
+	Review      *ReviewPayload  `json:"review"`
 }
 
 // SetSecret modifies the secret of the PullRequestPayload.
@@ -430,6 +433,12 @@ func (p *PullRequestPayload) JSONPayload() ([]byte, error) {
 	return json.MarshalIndent(p, "", "  ")
 }
 
+// ReviewPayload FIXME
+type ReviewPayload struct {
+	Type    string `json:"type"`
+	Content string `json:"content"`
+}
+
 //__________                           .__  __
 //\______   \ ____ ______   ____  _____|__|/  |_  ___________ ___.__.
 // |       _// __ \\____ \ /  _ \/  ___/  \   __\/  _ \_  __ <   |  |
diff --git a/modules/structs/repo.go b/modules/structs/repo.go
index 87396d6ce9..be6a3d4b43 100644
--- a/modules/structs/repo.go
+++ b/modules/structs/repo.go
@@ -153,6 +153,43 @@ type EditRepoOption struct {
 	Archived *bool `json:"archived,omitempty"`
 }
 
+// GitServiceType represents a git service
+type GitServiceType int
+
+// enumerate all GitServiceType
+const (
+	NotMigrated     GitServiceType = iota // 0 not migrated from external sites
+	PlainGitService                       // 1 plain git service
+	GithubService                         // 2 github.com
+	GiteaService                          // 3 gitea service
+	GitlabService                         // 4 gitlab service
+	GogsService                           // 5 gogs service
+)
+
+// Name represents the service type's name
+// WARNNING: the name have to be equal to that on goth's library
+func (gt GitServiceType) Name() string {
+	switch gt {
+	case GithubService:
+		return "github"
+	case GiteaService:
+		return "gitea"
+	case GitlabService:
+		return "gitlab"
+	case GogsService:
+		return "gogs"
+	}
+	return ""
+}
+
+var (
+	// SupportedFullGitService represents all git services supported to migrate issues/labels/prs and etc.
+	// TODO: add to this list after new git service added
+	SupportedFullGitService = []GitServiceType{
+		GithubService,
+	}
+)
+
 // MigrateRepoOption options for migrating a repository from an external service
 type MigrateRepoOption struct {
 	// required: true
@@ -162,8 +199,18 @@ type MigrateRepoOption struct {
 	// required: true
 	UID int `json:"uid" binding:"Required"`
 	// required: true
-	RepoName    string `json:"repo_name" binding:"Required"`
-	Mirror      bool   `json:"mirror"`
-	Private     bool   `json:"private"`
-	Description string `json:"description"`
+	RepoName        string `json:"repo_name" binding:"Required"`
+	Mirror          bool   `json:"mirror"`
+	Private         bool   `json:"private"`
+	Description     string `json:"description"`
+	OriginalURL     string
+	GitServiceType  GitServiceType
+	Wiki            bool
+	Issues          bool
+	Milestones      bool
+	Labels          bool
+	Releases        bool
+	Comments        bool
+	PullRequests    bool
+	MigrateToRepoID int64
 }
diff --git a/modules/structs/task.go b/modules/structs/task.go
new file mode 100644
index 0000000000..e83d0437ce
--- /dev/null
+++ b/modules/structs/task.go
@@ -0,0 +1,34 @@
+// Copyright 2019 Gitea. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package structs
+
+// TaskType defines task type
+type TaskType int
+
+// all kinds of task types
+const (
+	TaskTypeMigrateRepo TaskType = iota // migrate repository from external or local disk
+)
+
+// Name returns the task type name
+func (taskType TaskType) Name() string {
+	switch taskType {
+	case TaskTypeMigrateRepo:
+		return "Migrate Repository"
+	}
+	return ""
+}
+
+// TaskStatus defines task status
+type TaskStatus int
+
+// enumerate all the kinds of task status
+const (
+	TaskStatusQueue    TaskStatus = iota // 0 task is queue
+	TaskStatusRunning                    // 1 task is running
+	TaskStatusStopped                    // 2 task is stopped
+	TaskStatusFailed                     // 3 task is failed
+	TaskStatusFinished                   // 4 task is finished
+)
diff --git a/modules/task/migrate.go b/modules/task/migrate.go
new file mode 100644
index 0000000000..5d15a506d7
--- /dev/null
+++ b/modules/task/migrate.go
@@ -0,0 +1,120 @@
+// Copyright 2019 Gitea. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package task
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"strings"
+
+	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/migrations"
+	"code.gitea.io/gitea/modules/notification"
+	"code.gitea.io/gitea/modules/structs"
+	"code.gitea.io/gitea/modules/timeutil"
+	"code.gitea.io/gitea/modules/util"
+)
+
+func handleCreateError(owner *models.User, err error, name string) error {
+	switch {
+	case models.IsErrReachLimitOfRepo(err):
+		return fmt.Errorf("You have already reached your limit of %d repositories", owner.MaxCreationLimit())
+	case models.IsErrRepoAlreadyExist(err):
+		return errors.New("The repository name is already used")
+	case models.IsErrNameReserved(err):
+		return fmt.Errorf("The repository name '%s' is reserved", err.(models.ErrNameReserved).Name)
+	case models.IsErrNamePatternNotAllowed(err):
+		return fmt.Errorf("The pattern '%s' is not allowed in a repository name", err.(models.ErrNamePatternNotAllowed).Pattern)
+	default:
+		return err
+	}
+}
+
+func runMigrateTask(t *models.Task) (err error) {
+	defer func() {
+		if e := recover(); e != nil {
+			var buf bytes.Buffer
+			fmt.Fprintf(&buf, "Handler crashed with error: %v", log.Stack(2))
+
+			err = errors.New(buf.String())
+		}
+
+		if err == nil {
+			err = models.FinishMigrateTask(t)
+			if err == nil {
+				notification.NotifyMigrateRepository(t.Doer, t.Owner, t.Repo)
+				return
+			}
+
+			log.Error("FinishMigrateTask failed: %s", err.Error())
+		}
+
+		t.EndTime = timeutil.TimeStampNow()
+		t.Status = structs.TaskStatusFailed
+		t.Errors = err.Error()
+		if err := t.UpdateCols("status", "errors", "end_time"); err != nil {
+			log.Error("Task UpdateCols failed: %s", err.Error())
+		}
+
+		if t.Repo != nil {
+			if errDelete := models.DeleteRepository(t.Doer, t.OwnerID, t.Repo.ID); errDelete != nil {
+				log.Error("DeleteRepository: %v", errDelete)
+			}
+		}
+	}()
+
+	if err := t.LoadRepo(); err != nil {
+		return err
+	}
+
+	// if repository is ready, then just finsih the task
+	if t.Repo.Status == models.RepositoryReady {
+		return nil
+	}
+
+	if err := t.LoadDoer(); err != nil {
+		return err
+	}
+	if err := t.LoadOwner(); err != nil {
+		return err
+	}
+	t.StartTime = timeutil.TimeStampNow()
+	t.Status = structs.TaskStatusRunning
+	if err := t.UpdateCols("start_time", "status"); err != nil {
+		return err
+	}
+
+	var opts *structs.MigrateRepoOption
+	opts, err = t.MigrateConfig()
+	if err != nil {
+		return err
+	}
+
+	opts.MigrateToRepoID = t.RepoID
+	repo, err := migrations.MigrateRepository(t.Doer, t.Owner.Name, *opts)
+	if err == nil {
+		notification.NotifyMigrateRepository(t.Doer, t.Owner, repo)
+
+		log.Trace("Repository migrated [%d]: %s/%s", repo.ID, t.Owner.Name, repo.Name)
+		return nil
+	}
+
+	if models.IsErrRepoAlreadyExist(err) {
+		return errors.New("The repository name is already used")
+	}
+
+	// remoteAddr may contain credentials, so we sanitize it
+	err = util.URLSanitizedError(err, opts.CloneAddr)
+	if strings.Contains(err.Error(), "Authentication failed") ||
+		strings.Contains(err.Error(), "could not read Username") {
+		return fmt.Errorf("Authentication failed: %v", err.Error())
+	} else if strings.Contains(err.Error(), "fatal:") {
+		return fmt.Errorf("Migration failed: %v", err.Error())
+	}
+
+	return handleCreateError(t.Owner, err, "MigratePost")
+}
diff --git a/modules/task/queue.go b/modules/task/queue.go
new file mode 100644
index 0000000000..ddee0b3d46
--- /dev/null
+++ b/modules/task/queue.go
@@ -0,0 +1,14 @@
+// Copyright 2019 Gitea. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package task
+
+import "code.gitea.io/gitea/models"
+
+// Queue defines an interface to run task queue
+type Queue interface {
+	Run() error
+	Push(*models.Task) error
+	Stop()
+}
diff --git a/modules/task/queue_channel.go b/modules/task/queue_channel.go
new file mode 100644
index 0000000000..da541f4755
--- /dev/null
+++ b/modules/task/queue_channel.go
@@ -0,0 +1,48 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package task
+
+import (
+	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/modules/log"
+)
+
+var (
+	_ Queue = &ChannelQueue{}
+)
+
+// ChannelQueue implements
+type ChannelQueue struct {
+	queue chan *models.Task
+}
+
+// NewChannelQueue create a memory channel queue
+func NewChannelQueue(queueLen int) *ChannelQueue {
+	return &ChannelQueue{
+		queue: make(chan *models.Task, queueLen),
+	}
+}
+
+// Run starts to run the queue
+func (c *ChannelQueue) Run() error {
+	for task := range c.queue {
+		err := Run(task)
+		if err != nil {
+			log.Error("Run task failed: %s", err.Error())
+		}
+	}
+	return nil
+}
+
+// Push will push the task ID to queue
+func (c *ChannelQueue) Push(task *models.Task) error {
+	c.queue <- task
+	return nil
+}
+
+// Stop stop the queue
+func (c *ChannelQueue) Stop() {
+	close(c.queue)
+}
diff --git a/modules/task/queue_redis.go b/modules/task/queue_redis.go
new file mode 100644
index 0000000000..127de0cdbf
--- /dev/null
+++ b/modules/task/queue_redis.go
@@ -0,0 +1,130 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package task
+
+import (
+	"encoding/json"
+	"errors"
+	"strconv"
+	"strings"
+	"time"
+
+	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/modules/log"
+
+	"github.com/go-redis/redis"
+)
+
+var (
+	_ Queue = &RedisQueue{}
+)
+
+type redisClient interface {
+	RPush(key string, args ...interface{}) *redis.IntCmd
+	LPop(key string) *redis.StringCmd
+	Ping() *redis.StatusCmd
+}
+
+// RedisQueue redis queue
+type RedisQueue struct {
+	client    redisClient
+	queueName string
+	closeChan chan bool
+}
+
+func parseConnStr(connStr string) (addrs, password string, dbIdx int, err error) {
+	fields := strings.Fields(connStr)
+	for _, f := range fields {
+		items := strings.SplitN(f, "=", 2)
+		if len(items) < 2 {
+			continue
+		}
+		switch strings.ToLower(items[0]) {
+		case "addrs":
+			addrs = items[1]
+		case "password":
+			password = items[1]
+		case "db":
+			dbIdx, err = strconv.Atoi(items[1])
+			if err != nil {
+				return
+			}
+		}
+	}
+	return
+}
+
+// NewRedisQueue creates single redis or cluster redis queue
+func NewRedisQueue(addrs string, password string, dbIdx int) (*RedisQueue, error) {
+	dbs := strings.Split(addrs, ",")
+	var queue = RedisQueue{
+		queueName: "task_queue",
+		closeChan: make(chan bool),
+	}
+	if len(dbs) == 0 {
+		return nil, errors.New("no redis host found")
+	} else if len(dbs) == 1 {
+		queue.client = redis.NewClient(&redis.Options{
+			Addr:     strings.TrimSpace(dbs[0]), // use default Addr
+			Password: password,                  // no password set
+			DB:       dbIdx,                     // use default DB
+		})
+	} else {
+		// cluster will ignore db
+		queue.client = redis.NewClusterClient(&redis.ClusterOptions{
+			Addrs:    dbs,
+			Password: password,
+		})
+	}
+	if err := queue.client.Ping().Err(); err != nil {
+		return nil, err
+	}
+	return &queue, nil
+}
+
+// Run starts to run the queue
+func (r *RedisQueue) Run() error {
+	for {
+		select {
+		case <-r.closeChan:
+			return nil
+		case <-time.After(time.Millisecond * 100):
+		}
+
+		bs, err := r.client.LPop(r.queueName).Bytes()
+		if err != nil {
+			if err != redis.Nil {
+				log.Error("LPop failed: %v", err)
+			}
+			time.Sleep(time.Millisecond * 100)
+			continue
+		}
+
+		var task models.Task
+		err = json.Unmarshal(bs, &task)
+		if err != nil {
+			log.Error("Unmarshal task failed: %s", err.Error())
+		} else {
+			err = Run(&task)
+			if err != nil {
+				log.Error("Run task failed: %s", err.Error())
+			}
+		}
+	}
+}
+
+// Push implements Queue
+func (r *RedisQueue) Push(task *models.Task) error {
+	bs, err := json.Marshal(task)
+	if err != nil {
+		return err
+	}
+	return r.client.RPush(r.queueName, bs).Err()
+}
+
+// Stop stop the queue
+func (r *RedisQueue) Stop() {
+	r.closeChan <- true
+}
diff --git a/modules/task/task.go b/modules/task/task.go
new file mode 100644
index 0000000000..64744afe7a
--- /dev/null
+++ b/modules/task/task.go
@@ -0,0 +1,66 @@
+// Copyright 2019 Gitea. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package task
+
+import (
+	"fmt"
+
+	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/migrations/base"
+	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/structs"
+)
+
+// taskQueue is a global queue of tasks
+var taskQueue Queue
+
+// Run a task
+func Run(t *models.Task) error {
+	switch t.Type {
+	case structs.TaskTypeMigrateRepo:
+		return runMigrateTask(t)
+	default:
+		return fmt.Errorf("Unknow task type: %d", t.Type)
+	}
+}
+
+// Init will start the service to get all unfinished tasks and run them
+func Init() error {
+	switch setting.Task.QueueType {
+	case setting.ChannelQueueType:
+		taskQueue = NewChannelQueue(setting.Task.QueueLength)
+	case setting.RedisQueueType:
+		var err error
+		addrs, pass, idx, err := parseConnStr(setting.Task.QueueConnStr)
+		if err != nil {
+			return err
+		}
+		taskQueue, err = NewRedisQueue(addrs, pass, idx)
+		if err != nil {
+			return err
+		}
+	default:
+		return fmt.Errorf("Unsupported task queue type: %v", setting.Task.QueueType)
+	}
+
+	go func() {
+		if err := taskQueue.Run(); err != nil {
+			log.Error("taskQueue.Run end failed: %v", err)
+		}
+	}()
+
+	return nil
+}
+
+// MigrateRepository add migration repository to task
+func MigrateRepository(doer, u *models.User, opts base.MigrateOptions) error {
+	task, err := models.CreateMigrateTask(doer, u, opts)
+	if err != nil {
+		return err
+	}
+
+	return taskQueue.Push(task)
+}
diff --git a/modules/templates/helper.go b/modules/templates/helper.go
index b40f7117f5..b5287bf971 100644
--- a/modules/templates/helper.go
+++ b/modules/templates/helper.go
@@ -30,7 +30,7 @@ import (
 	"code.gitea.io/gitea/services/gitdiff"
 	mirror_service "code.gitea.io/gitea/services/mirror"
 
-	"gopkg.in/editorconfig/editorconfig-core-go.v1"
+	"github.com/editorconfig/editorconfig-core-go/v2"
 )
 
 // NewFuncMap returns functions for injecting to templates
@@ -48,6 +48,9 @@ func NewFuncMap() []template.FuncMap {
 		"AppSubUrl": func() string {
 			return setting.AppSubURL
 		},
+		"StaticUrlPrefix": func() string {
+			return setting.StaticURLPrefix
+		},
 		"AppUrl": func() string {
 			return setting.AppURL
 		},
@@ -145,7 +148,11 @@ func NewFuncMap() []template.FuncMap {
 		},
 		"TabSizeClass": func(ec *editorconfig.Editorconfig, filename string) string {
 			if ec != nil {
-				def := ec.GetDefinitionForFilename(filename)
+				def, err := ec.GetDefinitionForFilename(filename)
+				if err != nil {
+					log.Error("tab size class: getting definition for filename: %v", err)
+					return "tab-size-8"
+				}
 				if def.TabWidth > 0 {
 					return fmt.Sprintf("tab-size-%d", def.TabWidth)
 				}
@@ -236,6 +243,8 @@ func NewFuncMap() []template.FuncMap {
 		"CommentMustAsDiff": gitdiff.CommentMustAsDiff,
 		"MirrorAddress":     mirror_service.Address,
 		"MirrorFullAddress": mirror_service.AddressNoCredentials,
+		"MirrorUserName":    mirror_service.Username,
+		"MirrorPassword":    mirror_service.Password,
 	}}
 }
 
diff --git a/modules/util/compare.go b/modules/util/compare.go
index c61e7965ae..f1d1e5718e 100644
--- a/modules/util/compare.go
+++ b/modules/util/compare.go
@@ -35,6 +35,16 @@ func ExistsInSlice(target string, slice []string) bool {
 	return i < len(slice)
 }
 
+// IsStringInSlice sequential searches if string exists in slice.
+func IsStringInSlice(target string, slice []string) bool {
+	for i := 0; i < len(slice); i++ {
+		if slice[i] == target {
+			return true
+		}
+	}
+	return false
+}
+
 // IsEqualSlice returns true if slices are equal.
 func IsEqualSlice(target []string, source []string) bool {
 	if len(target) != len(source) {
diff --git a/options/locale/TRANSLATORS b/options/locale/TRANSLATORS
index c413626ec1..98a47a6c53 100644
--- a/options/locale/TRANSLATORS
+++ b/options/locale/TRANSLATORS
@@ -73,5 +73,6 @@ Toni Villena Jiménez <tonivj5 AT gmail DOT com>
 Viktor Sperl <viktike32 AT gmail DOT com>
 Vladimir Jigulin mogaika AT yandex DOT ru
 Vladimir Vissoultchev <wqweto AT gmail DOT com>
+Yaşar Çiv <yasarciv67 AT gmail DOT com>
 YJSoft <yjsoft AT yjsoft DOT pe DOT kr>
 Łukasz Jan Niemier <lukasz AT niemier DOT pl>
diff --git a/options/locale/locale_bg-BG.ini b/options/locale/locale_bg-BG.ini
index 48c007b60d..40dabfae9f 100644
--- a/options/locale/locale_bg-BG.ini
+++ b/options/locale/locale_bg-BG.ini
@@ -1,4 +1,3 @@
-
 home=Начало
 dashboard=Табло
 explore=Каталог
@@ -40,6 +39,8 @@ issues=Задачи
 cancel=Отказ
 
 
+[startpage]
+
 [install]
 install=Инсталация
 db_title=Настройки на базата данни
@@ -472,7 +473,6 @@ settings.choose_branch=Изберете клон…
 diff.browse_source=Преглед на файлове
 diff.parent=родител
 diff.commit=ревизия
-diff.show_diff_stats=Покажи статистика за разликите
 diff.show_split_view=Разделен изглед
 diff.show_unified_view=Обединен изглед
 diff.stats_desc=променени са <strong>%d файла</strong>, в които са <strong>добавени %d</strong> реда и са <strong>изтрити %d</strong> реда
diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini
index a103f423d9..24680cea5b 100644
--- a/options/locale/locale_cs-CZ.ini
+++ b/options/locale/locale_cs-CZ.ini
@@ -1,5 +1,3 @@
-app_desc=Snadno přístupný vlastní Git
-
 home=Domů
 dashboard=Přehled
 explore=Procházet
@@ -75,6 +73,8 @@ write=Zapsat
 preview=Náhled
 loading=Načítá se…
 
+[startpage]
+
 [install]
 install=Instalace
 title=Výchozí konfigurace
@@ -250,7 +250,6 @@ openid_signin_desc=Zadejte své OpenID URI. Například: https://anne.me, bob.op
 disable_forgot_password_mail=Obnovení účtu je zakázané. Prosíme, kontaktujte správce systému.
 email_domain_blacklisted=Nemůžete se registrovat s vaší e-mailovou adresou.
 authorize_application=Autorizovat aplikaci
-authroize_redirect_notice=Budete přesměrováni na %s, pokud autorizujete tuto aplikaci.
 authorize_application_created_by=Tuto aplikaci vytvořil %s.
 authorize_application_description=Pokud povolíte přístup, bude moci přistupovat a zapisovat do všech vašich informací o účtu včetně soukromých repozitářů a organizací.
 authorize_title=Autorizovat „%s“ pro přístup k vašemu účtu?
@@ -1357,7 +1356,6 @@ diff.parent=rodič
 diff.commit=revize
 diff.git-notes=Poznámky
 diff.data_not_available=Rozdílový obsah není dostupný
-diff.show_diff_stats=Ukázat statistiku rozdílových dat
 diff.show_split_view=Rozdělené zobrazení
 diff.show_unified_view=Jednotný pohled
 diff.whitespace_button=Bílý znak
diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini
index 3add4be1a9..b692e26c06 100644
--- a/options/locale/locale_de-DE.ini
+++ b/options/locale/locale_de-DE.ini
@@ -1,5 +1,3 @@
-app_desc=Ein einfacher, selbst gehosteter Git-Service
-
 home=Startseite
 dashboard=Übersicht
 explore=Erkunden
@@ -52,7 +50,7 @@ new_mirror=Neuer Mirror
 new_fork=Neuer Fork
 new_org=Neue Organisation
 manage_org=Organisationen verwalten
-admin_panel=Website-Administration
+admin_panel=Administration
 account_settings=Kontoeinstellungen
 settings=Einstellungen
 your_profile=Profil
@@ -75,6 +73,17 @@ write=Verfassen
 preview=Vorschau
 loading=Laden…
 
+[startpage]
+app_desc=Ein einfacher, selbst gehosteter Git-Service
+install=Einfach zu installieren
+install_desc=Starte einfach <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-binary/">die Anwendung</a> für deine Plattform. Gitea gibt es auch für <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a>, <a target="_blank" rel="noopener noreferrer" href="https://github.com/alvaroaleman/ansible-gitea/blob/master/Vagrantfile">Vagrant</a> oder als <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-package/">Installationspaket</a>.
+platform=Plattformübergreifend
+platform_desc=Gitea läuft überall, wo <a target="_blank" rel="noopener noreferrer" href="http://golang.org/">Go</a> kompiliert: Windows, macOS, Linux, ARM, etc. Wähle das System, das dir am meisten gefällt!
+lightweight=Leichtgewicht
+lightweight_desc=Gitea hat minimale Systemanforderungen und kann selbst auf einem günstigen und stromsparenden Raspberry Pi betrieben werden!
+license=Quelloffen
+license_desc=Der komplette Code befindet sich auf <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/">GitHub</a>! Unterstütze uns bei der Verbesserung dieses Projekts. Trau dich!
+
 [install]
 install=Installation
 title=Erstkonfiguration
@@ -250,7 +259,6 @@ openid_signin_desc=Gib deine OpenID-URI ein. Zum Beispiel: https://anne.me, bob.
 disable_forgot_password_mail=Die Kontowiederherstellung ist deaktiviert. Bitte wende dich an den Administrator.
 email_domain_blacklisted=Du kannst dich nicht mit deiner E-Mail-Adresse registrieren.
 authorize_application=Anwendung autorisieren
-authroize_redirect_notice=Wenn du diese Anwendung autorisierst, wirst du zu %s weitergeleitet.
 authorize_application_created_by=Diese Anwendung wurde von %s erstellt.
 authorize_application_description=Wenn du diese Anwendung autorisierst, wird sie die Berechtigung erhalten, alle Informationen zu deinem Account zu bearbeiten oder zu lesen. Dies beinhaltet auch private Repositories und Organisationen.
 authorize_title="%s" den Zugriff auf deinen Account gestatten?
@@ -314,10 +322,12 @@ team_no_units_error=Das Team muss auf mindestens einen Bereich Zugriff haben.
 email_been_used=Die E-Mail-Adresse wird bereits verwendet.
 openid_been_used=Die OpenID-Adresse „%s“ wird bereits verwendet.
 username_password_incorrect=Benutzername oder Passwort ist falsch.
+password_complexity=Das Passwort erfüllt nicht die Komplexitätsanforderungen.
 enterred_invalid_repo_name=Der eingegebenen Repository-Name ist falsch.
 enterred_invalid_owner_name=Der Name des neuen Besitzers ist ungültig.
 enterred_invalid_password=Das eingegebene Passwort ist falsch.
 user_not_exist=Dieser Benutzer ist nicht vorhanden.
+team_not_exist=Dieses Team existiert nicht.
 last_org_owner=Du kannst den letzten Benutzer nicht aus dem „Besitzer“-Team entfernen. Es muss mindestens einen Besitzer in einer Organisation geben.
 cannot_add_org_to_team=Eine Organisation kann nicht als Teammitglied hinzugefügt werden.
 
@@ -631,6 +641,8 @@ migrate.lfs_mirror_unsupported=Spiegeln von LFS-Objekten wird nicht unterstützt
 migrate.migrate_items_options=Wenn du von GitHub migrierst und einen Benutzernamen eingegeben hast, werden die Migrationsoptionen angezeigt.
 migrated_from=Migriert von <a href="%[1]s">%[2]s</a>
 migrated_from_fake=Migriert von %[1]s
+migrate.migrating=Migriere von <b>%s</b> ...
+migrate.migrating_failed=Migrieren von <b>%s</b> fehlgeschlagen.
 
 mirror_from=Mirror von
 forked_from=geforkt von
@@ -679,6 +691,8 @@ stored_lfs=Gespeichert mit Git LFS
 commit_graph=Commit graph
 blame=Blame
 normal_view=Normale Ansicht
+line=zeile
+lines=Zeilen
 
 editor.new_file=Neue Datei
 editor.upload_file=Datei hochladen
@@ -704,6 +718,7 @@ editor.delete=„%s“ löschen
 editor.commit_message_desc=Eine ausführlichere (optionale) Beschreibung hinzufügen…
 editor.commit_directly_to_this_branch=Direkt in den Branch „<strong class="branch-name">%s</strong>“ einchecken.
 editor.create_new_branch=Einen <strong>neuen Branch</strong> für diesen Commit erstellen und einen Pull Request starten.
+editor.create_new_branch_np=Erstelle einen <strong>neuen Branch</strong> für diesen Commit.
 editor.propose_file_change=Dateiänderung vorschlagen
 editor.new_branch_name_desc=Neuer Branchname…
 editor.cancel=Abbrechen
@@ -718,6 +733,8 @@ editor.file_editing_no_longer_exists=Die bearbeitete Datei „%s“ existiert ni
 editor.file_deleting_no_longer_exists=Die Datei '%s' existiert in diesem Repository nicht mehr.
 editor.file_changed_while_editing=Der Inhalt der Datei hat sich seit dem Beginn der Bearbeitung geändert. <a target="_blank" rel="noopener noreferrer" href="%s">Hier klicken</a>, um die Änderungen anzusehen, oder <strong>Änderungen erneut comitten</strong>, um sie zu überschreiben.
 editor.file_already_exists=Eine Datei mit dem Namen „%s“ ist bereits in diesem Repository vorhanden.
+editor.commit_empty_file_header=Leere Datei committen
+editor.commit_empty_file_text=Die Datei, die du gerade commitest ist leer! Fortfahren?
 editor.no_changes_to_show=Keine Änderungen vorhanden.
 editor.fail_to_update_file=Fehler beim Ändern/Erstellen der Datei „%s“. Fehler: %v
 editor.add_subdir=Verzeichnis erstellen…
@@ -835,6 +852,10 @@ issues.create_comment=Kommentieren
 issues.closed_at=`hat <a id="%[1]s" href="#%[1]s">%[2]s</a> geschlossen`
 issues.reopened_at=`hat <a id="%[1]s" href="#%[1]s">%[2]s</a> wieder geöffnet`
 issues.commit_ref_at=`hat dieses Issue <a id="%[1]s" href="#%[1]s">%[2]s</a> aus einem Commit referenziert`
+issues.ref_issue_at=`hat dieses Issue %[1]s referenziert`
+issues.ref_pull_at=`hat diesen Pull-Request %[1]s referenziert`
+issues.ref_issue_ext_at=`hat dieses Issue %[2]s von %[1]s referenziert`
+issues.ref_pull_ext_at=`hat diesen Pull-Request %[2]s von %[1]s referenziert`
 issues.poster=Ersteller
 issues.collaborator=Mitarbeiter
 issues.owner=Besitzer
@@ -1131,6 +1152,7 @@ settings.collaboration=Mitarbeiter
 settings.collaboration.admin=Administrator
 settings.collaboration.write=Schreibrechte
 settings.collaboration.read=Leserechte
+settings.collaboration.owner=Besitzer
 settings.collaboration.undefined=Nicht definiert
 settings.hooks=Webhooks
 settings.githooks=Git-Hooks
@@ -1212,6 +1234,11 @@ settings.collaborator_deletion_desc=Nach dem Löschen wird dieser Mitarbeiter ke
 settings.remove_collaborator_success=Der Mitarbeiter wurde entfernt.
 settings.search_user_placeholder=Benutzer suchen…
 settings.org_not_allowed_to_be_collaborator=Organisationen können nicht als Mitarbeiter hinzugefügt werden.
+settings.change_team_access_not_allowed=Nur der Besitzer der Organisation kann die Zugangsrechte des Teams ändern
+settings.team_not_in_organization=Das Team ist nicht in der gleichen Organisation wie das Repository
+settings.add_team_duplicate=Das Team ist dem Repository schon zugeordnet
+settings.add_team_success=Das Team hat nun Zugriff auf das Repository.
+settings.remove_team_success=Der Zugriff des Teams auf das Repository wurde zurückgezogen.
 settings.add_webhook=Webhook hinzufügen
 settings.add_webhook.invalid_channel_name=Der Name des Webhook-Kanals darf nicht leer sein und darf nicht nur das Zeichen # enthalten.
 settings.hooks_desc=Webhooks senden bei bestimmten Gitea-Events automatisch „HTTP POST“-Anfragen an einen Server. Lies mehr in unserer <a target="_blank" rel="noopener noreferrer" href="%s">Anleitung zu Webhooks (auf Englisch)</a>.
@@ -1314,6 +1341,7 @@ settings.protect_merge_whitelist_committers_desc=Erlaube Nutzern oder Teams auf
 settings.protect_merge_whitelist_users=Nutzer, die mergen dürfen:
 settings.protect_merge_whitelist_teams=Teams, die mergen dürfen:
 settings.protect_check_status_contexts=Statusprüfungen aktivieren
+settings.protect_check_status_contexts_desc=Statusprüfungen vor dem Zusammenführen erforderlich. Wähle aus, welche Statusprüfungen durchgeführt werden müssen, bevor Branches zu einem Zweig zusammengeführt werden können, der dieser Regel entspricht. Wenn aktiviert, müssen Bestätigungen zuerst auf einen anderen Zweig verschoben werden, dann nach bestandener Statusprüfung zusammengeführt oder direkt auf einen Zweig verschoben werden, der dieser Regel entspricht. Wenn keine Kontext ausgewählt ist, muss der letzte Commit unabhängig vom Kontext erfolgreich sein.
 settings.protect_check_status_contexts_list=Statusprüfungen, die in der letzten Woche für dieses Repository gefunden wurden
 settings.protect_required_approvals=Erforderliche Zustimmungen:
 settings.protect_required_approvals_desc=Erlaube das Zusammenführen des Pull-Requests nur bei ausreichend positiven Zustimmungen von dafür freigeschalteten Nutzern oder Teams.
@@ -1351,7 +1379,10 @@ diff.parent=Ursprung
 diff.commit=Commit
 diff.git-notes=Hinweise
 diff.data_not_available=Keine Diff-Daten verfügbar
-diff.show_diff_stats=Diff-Statistik anzeigen
+diff.options_button=Diff-Optionen
+diff.show_diff_stats=Statistiken anzeigen
+diff.download_patch=Patch-Datei herunterladen
+diff.download_diff=Vergleichs-Datei herunterladen
 diff.show_split_view=Geteilte Ansicht
 diff.show_unified_view=Gesamtansicht
 diff.whitespace_button=Leerzeichen
@@ -1434,6 +1465,8 @@ branch.restore_failed=Wiederherstellung des Branches „%s“ fehlgeschlagen.
 branch.protected_deletion_failed=Branch „%s“ ist geschützt und kann nicht gelöscht werden.
 branch.restore=Branch „%s“ wiederherstellen
 branch.download=Branch „%s“ herunterladen
+branch.included_desc=Dieser Branch ist im Standard-Branch enthalten
+branch.included=Enthalten
 
 topic.manage_topics=Themen verwalten
 topic.done=Fertig
@@ -1469,6 +1502,8 @@ settings.options=Organisation
 settings.full_name=Vollständiger Name
 settings.website=Webseite
 settings.location=Standort
+settings.permission=Berechtigungen
+settings.repoadminchangeteam=Der Repository-Administrator kann den Zugriff für Teams hinzufügen und zurückziehen
 settings.visibility=Sichtbarkeit
 settings.visibility.public=Öffentlich
 settings.visibility.limited=Eingeschränkt (nur für angemeldete Nutzer sichtbar)
@@ -1948,12 +1983,15 @@ mark_as_unread=Als ungelesen markieren
 mark_all_as_read=Alle als gelesen markieren
 
 [gpg]
+default_key=Mit Standardschlüssel signiert
 error.extract_sign=Die Signatur konnte nicht extrahiert werden
 error.generate_hash=Es konnte kein Hash vom Commit generiert werden
 error.no_committer_account=Es ist kein Account mit der E-Mail-Adresse des Committers verbunden
 error.no_gpg_keys_found=Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden
 error.not_signed_commit=Kein signierter Commit
 error.failed_retrieval_gpg_keys=Fehler beim Abrufen eines Keys des Commiter-Kontos
+error.probable_bad_signature=WARNHINWEIS! Obwohl es einen Schlüssel mit dieser ID in der Datenbank gibt, wird dieser Commit nicht verifiziert! Dieser Commit ist nicht vertrauenswürdig.
+error.probable_bad_default_signature=WARNHINWEIS! Obwohl der Standardschlüssel diese ID hat, wird der Commit nicht verifiziert! Dieser Commit ist NICHT vertrauenswürdig.
 
 [units]
 error.no_unit_allowed_repo=Du hast keine Berechtigung, um auf irgendeinen Bereich dieses Repositories zuzugreifen.
diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini
index 978a1dd5b1..8e83213fd7 100644
--- a/options/locale/locale_en-US.ini
+++ b/options/locale/locale_en-US.ini
@@ -1,5 +1,3 @@
-app_desc = A painless, self-hosted Git service
-
 home = Home
 dashboard = Dashboard
 explore = Explore
@@ -75,6 +73,17 @@ write = Write
 preview = Preview
 loading = Loading…
 
+[startpage]
+app_desc = A painless, self-hosted Git service
+install = Easy to install
+install_desc = Simply <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-binary/">run the binary</a> for your platform. Or ship Gitea with <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a> or <a target="_blank" rel="noopener noreferrer" href="https://github.com/alvaroaleman/ansible-gitea/blob/master/Vagrantfile">Vagrant</a>, or get it <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-package/">packaged</a>.
+platform = Cross-platform
+platform_desc = Gitea runs anywhere <a target="_blank" rel="noopener noreferrer" href="http://golang.org/">Go</a> can compile for: Windows, macOS, Linux, ARM, etc. Choose the one you love!
+lightweight = Lightweight
+lightweight_desc = Gitea has low minimal requirements and can run on an inexpensive Raspberry Pi. Save your machine energy!
+license = Open Source
+license_desc = Go get <a target="_blank" rel="noopener noreferrer" href="https://code.gitea.io/gitea">code.gitea.io/gitea</a>! Join us by <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea">contributing</a> to make this project even better. Don't be shy to be a contributor!
+
 [install]
 install = Installation
 title = Initial Configuration
@@ -250,7 +259,7 @@ openid_signin_desc = Enter your OpenID URI. For example: https://anne.me, bob.op
 disable_forgot_password_mail = Account recovery is disabled. Please contact your site administrator.
 email_domain_blacklisted = You cannot register with your email address.
 authorize_application = Authorize Application
-authroize_redirect_notice = You will be redirected to %s if you authorize this application.
+authorize_redirect_notice = You will be redirected to %s if you authorize this application.
 authorize_application_created_by = This application was created by %s.
 authorize_application_description = If you grant the access, it will be able to access and write to all your account information, including private repos and organisations.
 authorize_title = Authorize "%s" to access your account?
@@ -315,6 +324,7 @@ team_no_units_error = Allow access to at least one repository section.
 email_been_used = The email address is already used.
 openid_been_used = The OpenID address '%s' is already used.
 username_password_incorrect = Username or password is incorrect.
+password_complexity = Password does not pass complexity requirements.
 enterred_invalid_repo_name = The repository name you entered is incorrect.
 enterred_invalid_owner_name = The new owner name is not valid.
 enterred_invalid_password = The password you entered is incorrect.
@@ -633,6 +643,8 @@ migrate.lfs_mirror_unsupported = Mirroring LFS objects is not supported - use 'g
 migrate.migrate_items_options = When migrating from github, input a username and migration options will be displayed.
 migrated_from = Migrated from <a href="%[1]s">%[2]s</a>
 migrated_from_fake = Migrated From %[1]s
+migrate.migrating = Migrating from <b>%s</b> ...
+migrate.migrating_failed = Migrating from <b>%s</b> failed.
 
 mirror_from = mirror of
 forked_from = forked from
@@ -681,6 +693,8 @@ stored_lfs = Stored with Git LFS
 commit_graph = Commit Graph
 blame = Blame
 normal_view = Normal View
+line = line
+lines = lines
 
 editor.new_file = New File
 editor.upload_file = Upload File
@@ -721,6 +735,8 @@ editor.file_editing_no_longer_exists = The file being edited, '%s', no longer ex
 editor.file_deleting_no_longer_exists = The file being deleted, '%s', no longer exists in this repository.
 editor.file_changed_while_editing = The file contents have changed since you started editing. <a target="_blank" rel="noopener noreferrer" href="%s">Click here</a> to see them or <strong>Commit Changes again</strong> to overwrite them.
 editor.file_already_exists = A file named '%s' already exists in this repository.
+editor.commit_empty_file_header = Commit an empty file
+editor.commit_empty_file_text = The file you're about commit is empty. Proceed?
 editor.no_changes_to_show = There are no changes to show.
 editor.fail_to_update_file = Failed to update/create file '%s' with error: %v
 editor.add_subdir = Add a directory…
@@ -786,6 +802,7 @@ issues.delete_branch_at = `deleted branch <b>%s</b> %s`
 issues.open_tab = %d Open
 issues.close_tab = %d Closed
 issues.filter_label = Label
+issues.filter_label_exclude = `Use <code>alt</code> + <code>click/enter</code> to exclude labels`
 issues.filter_label_no_select = All labels
 issues.filter_milestone = Milestone
 issues.filter_milestone_no_select = All milestones
@@ -960,6 +977,7 @@ issues.review.review = Review
 issues.review.reviewers = Reviewers
 issues.review.show_outdated = Show outdated
 issues.review.hide_outdated = Hide outdated
+issues.assignee.error = Not all assignees was added due to an unexpected error.
 
 pulls.desc = Enable pull requests and code reviews.
 pulls.new = New Pull Request
@@ -1318,6 +1336,7 @@ settings.protect_this_branch = Enable Branch Protection
 settings.protect_this_branch_desc = Prevent deletion and disable any Git pushing to the branch.
 settings.protect_whitelist_committers = Enable Push Whitelist
 settings.protect_whitelist_committers_desc = Allow whitelisted users or teams to push to this branch (but not force push).
+settings.protect_whitelist_deploy_keys = Whitelist deploy keys with write access to push
 settings.protect_whitelist_users = Whitelisted users for pushing:
 settings.protect_whitelist_search_users = Search users…
 settings.protect_whitelist_teams = Whitelisted teams for pushing:
@@ -1365,7 +1384,10 @@ diff.parent = parent
 diff.commit = commit
 diff.git-notes = Notes
 diff.data_not_available = Diff Content Not Available
-diff.show_diff_stats = Show Diff Stats
+diff.options_button = Diff Options
+diff.show_diff_stats = Show Stats
+diff.download_patch = Download Patch File
+diff.download_diff = Download Diff File
 diff.show_split_view = Split View
 diff.show_unified_view = Unified View
 diff.whitespace_button = Whitespace
@@ -1448,6 +1470,8 @@ branch.restore_failed = Failed to restore branch '%s'.
 branch.protected_deletion_failed = Branch '%s' is protected. It cannot be deleted.
 branch.restore = Restore Branch '%s'
 branch.download = Download Branch '%s'
+branch.included_desc = This branch is part of the default branch
+branch.included = Included
 
 topic.manage_topics = Manage Topics
 topic.done = Done
@@ -1969,12 +1993,15 @@ mark_as_unread = Mark as unread
 mark_all_as_read = Mark all as read
 
 [gpg]
+default_key=Signed with default key
 error.extract_sign = Failed to extract signature
 error.generate_hash = Failed to generate hash of commit
 error.no_committer_account = No account linked to committer's email address
 error.no_gpg_keys_found = "No known key found for this signature in database"
 error.not_signed_commit = "Not a signed commit"
 error.failed_retrieval_gpg_keys = "Failed to retrieve any key attached to the committer's account"
+error.probable_bad_signature = "WARNING! Although there is a key with this ID in the database it does not verify this commit! This commit is SUSPICIOUS."
+error.probable_bad_default_signature = "WARNING! Although the default key has this ID it does not verify this commit! This commit is SUSPICIOUS."
 
 [units]
 error.no_unit_allowed_repo = You are not allowed to access any section of this repository.
diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini
index 675de019b0..1488cf9ba2 100644
--- a/options/locale/locale_es-ES.ini
+++ b/options/locale/locale_es-ES.ini
@@ -1,5 +1,3 @@
-app_desc=Un servicio de Git auto alojado y sin complicaciones
-
 home=Inicio
 dashboard=Panel de control
 explore=Explorar
@@ -66,7 +64,7 @@ collaborative=Colaborativos
 forks=Forks
 
 activities=Actividad
-pull_requests=Peticiones pull
+pull_requests=Pull Requests
 issues=Incidencias
 
 cancel=Cancelar
@@ -75,6 +73,17 @@ write=Escribir
 preview=Vista previa
 loading=Cargando…
 
+[startpage]
+app_desc=Un servicio de Git autoalojado y sin complicaciones
+install=Fácil de instalar
+install_desc=Simplemente <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-binary/">arranca el binario</a> para tu plataforma. O usa Gitea con <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a> o <a target="_blank" rel="noopener noreferrer" href="https://github.com/alvaroaleman/ansible-gitea/blob/master/Vagrantfile">Vagrant</a>, o utilice el <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-package/">paquete</a>.
+platform=Multiplataforma
+platform_desc=Gitea funciona en cualquier platforma <a target="_blank" rel="noopener noreferrer" href="http://golang.org/">Go</a> puede compilarlo en: Windows, macOS, Linux, ARM, etc. ¡Elige tu favorita!
+lightweight=Ligero
+lightweight_desc=Gitea tiene pocos requisitos y puede funcionar en una Raspberry Pi barata. ¡Ahorra energía!
+license=Código abierto
+license_desc=¡Está todo en <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/">GitHub</a>! Uniros contribuyendo a hacer este proyecto todavía mejor. ¡No seas tímido y colabora!
+
 [install]
 install=Instalación
 title=Configuración inicial
@@ -250,7 +259,6 @@ openid_signin_desc=Introduzca su URI OpenID. Por ejemplo: https://anne.me, bob.o
 disable_forgot_password_mail=La recuperación de cuentas está desactivada. Por favor, contacte con el administrador del sitio.
 email_domain_blacklisted=No puede registrarse con su correo electrónico.
 authorize_application=Autorizar aplicación
-authroize_redirect_notice=Será redirigido a %s si autoriza esta aplicación.
 authorize_application_created_by=Esta aplicación fue creada por %s.
 authorize_application_description=Si concede el acceso, podrá acceder y escribir a toda la información de su cuenta, incluyendo repositorios privado y organizaciones.
 authorize_title=¿Autorizar a "%s" a acceder a su cuenta?
@@ -299,6 +307,7 @@ max_size_error=` debe contener como máximo %s caracteres.`
 email_error=` no es una dirección de correo válida.`
 url_error=` no es una URL válida.`
 include_error=` debe contener la subcadena '%s'.`
+glob_pattern_error=` el patrón globo no es válido: %s.`
 unknown_error=Error desconocido:
 captcha_incorrect=El código CAPTCHA no es correcto.
 password_not_match=Las contraseñas no coinciden.
@@ -313,10 +322,12 @@ team_no_units_error=Permitir el acceso a por lo menos una sección del repositor
 email_been_used=La dirección de correo electrónico ya está usada.
 openid_been_used=La dirección OpenID '%s' ya está usada.
 username_password_incorrect=El nombre de usuario o la contraseña son incorrectos.
+password_complexity=La contraseña no pasa por los requisitos de complejidad.
 enterred_invalid_repo_name=El nombre de repositorio que ha entrado es incorrecto.
 enterred_invalid_owner_name=El nuevo nombre de usuario no es válido.
 enterred_invalid_password=La contraseña que ha introducido es incorrecta.
 user_not_exist=Este usuario no existe.
+team_not_exist=Este equipo no existe.
 last_org_owner=No puedes eliminar al último usuario del equipo de 'propietarios'. Debe haber al menos un propietario en ningún equipo dado.
 cannot_add_org_to_team=Una organización no puede ser añadida como miembro de un equipo.
 
@@ -556,12 +567,17 @@ confirm_delete_account=Confirmar Eliminación
 delete_account_title=Eliminar cuenta de usuario
 delete_account_desc=¿Está seguro que desea eliminar permanentemente esta cuenta de usuario?
 
+email_notifications.enable=Habilitar notificaciones por correo electrónico
+email_notifications.onmention=Enviar correo sólo al ser mencionado
+email_notifications.disable=Deshabilitar las notificaciones por correo electrónico
+email_notifications.submit=Establecer preferencias de correo electrónico
 
 [repo]
 owner=Propietario
 repo_name=Nombre del repositorio
 repo_name_helper=Un buen nombre de repositorio está compuesto por palabras clave cortas, memorables y únicas.
 visibility=Visibilidad
+visibility_description=Sólo el propietario o los miembros de la organización -si tienen derechos- podrán verlo.
 visibility_helper=Hacer repositorio privado
 visibility_helper_forced=El administrador de su sitio obliga a nuevos repositorios a ser privados.
 visibility_fork_helper=(Cambiar esto afectará a todos los forks)
@@ -572,6 +588,8 @@ fork_visibility_helper=La visibilidad de un repositorio del cual se ha hecho for
 repo_desc=Descripción
 repo_lang=Idioma
 repo_gitignore_helper=Seleccionar plantillas de .gitignore.
+issue_labels=Etiquetas de incidencia
+issue_labels_helper=Seleccione un conjunto de etiquetas de incidencia.
 license=Licencia
 license_helper=Seleccione un archivo de licencia.
 readme=LÉAME
@@ -584,6 +602,7 @@ mirror_prune_desc=Eliminar referencias de seguimiento de remotes obsoletas
 mirror_interval=Intervalo de réplica (Las unidades de tiempo válidas son 'h', 'm', 's'). Pone 0 para deshabilitar la sincronización automática.
 mirror_interval_invalid=El intervalo de réplica no es válido.
 mirror_address=Clonar desde URL
+mirror_address_desc=Agregue las credenciales que sean necesarias en la sección de Autorización de Clonado.
 mirror_address_url_invalid=La url proporcionada no es válida. Debe escapar correctamente de todos los componentes de la url.
 mirror_address_protocol_invalid=La url proporcionada no es válida. Sólo las ubicaciones http(s):// o git:// pueden ser replicadas desde.
 mirror_last_synced=Sincronizado por última vez
@@ -622,6 +641,8 @@ migrate.lfs_mirror_unsupported=La replicación de objetos LFS no está soportada
 migrate.migrate_items_options=Cuando migra desde github, se mostrarán un nombre de usuario y se mostrarán opciones de migración.
 migrated_from=Migrado desde <a href="%[1]s">%[2]s</a>
 migrated_from_fake=Migrado desde %[1]s
+migrate.migrating=Migrando desde <b>%s</b>...
+migrate.migrating_failed=La migración desde <b>%s</b> ha fallado.
 
 mirror_from=réplica de
 forked_from=forkeado de
@@ -653,7 +674,7 @@ filter_branch_and_tag=Filtrar por rama o etiqueta
 branches=Ramas
 tags=Etiquetas
 issues=Incidencias
-pulls=Peticiones pull
+pulls=Pull Requests
 labels=Etiquetas
 milestones=Hitos
 commits=Commits
@@ -670,6 +691,8 @@ stored_lfs=Almacenados con Git LFS
 commit_graph=Gráfico de commits
 blame=Blame
 normal_view=Vista normal
+line=línea
+lines=líneas
 
 editor.new_file=Nuevo Archivo
 editor.upload_file=Subir archivo
@@ -695,6 +718,7 @@ editor.delete=Eliminar '%s'
 editor.commit_message_desc=Añadir una descripción extendida opcional…
 editor.commit_directly_to_this_branch=Hacer commit directamente en la rama <strong class="branch-name">%s</strong>.
 editor.create_new_branch=Crear una <strong>nueva rama</strong> para este commit y hacer un pull request.
+editor.create_new_branch_np=Crear una <strong>nueva rama</strong> para este commit.
 editor.propose_file_change=Proponer cambio de archivo
 editor.new_branch_name_desc=Nombre de la rama nueva…
 editor.cancel=Cancelar
@@ -709,6 +733,8 @@ editor.file_editing_no_longer_exists=El archivo que está editando, '%s', ya no
 editor.file_deleting_no_longer_exists=El archivo que se está eliminando, '%s', ya no existe en este repositorio.
 editor.file_changed_while_editing=Desde que comenzó a editar, el contenido del archivo ha sido cambiado. <a target="_blank" rel="noopener noreferrer" href="%s">Clic aquí</a> para ver qué ha cambiado o <strong>presione confirmar de nuevo</strong> para sobrescribir los cambios.
 editor.file_already_exists=Ya existe un archivo con nombre '%s' en este repositorio.
+editor.commit_empty_file_header=Commit un archivo vacío
+editor.commit_empty_file_text=El archivo que estás tratando de commit está vacío. ¿Proceder?
 editor.no_changes_to_show=No existen cambios para mostrar.
 editor.fail_to_update_file=Error al actualizar/crear el archivo '%s', error: %v
 editor.add_subdir=Añadir un directorio…
@@ -769,6 +795,7 @@ issues.self_assign_at=`auto asignado este %s`
 issues.add_assignee_at='fue asignado por <b>%s</b> %s'
 issues.remove_assignee_at=`fue desasignado por <b>%s</b> %s`
 issues.remove_self_assignment=`eliminado su asignación %s`
+issues.change_title_at=`cambió el título de <b><strike>%s</strike></b> a <b>%s</b> %s`
 issues.delete_branch_at=`rama eliminada <b>%s</b> %s`
 issues.open_tab=%d abiertas
 issues.close_tab=%d cerradas
@@ -825,6 +852,10 @@ issues.create_comment=Comentar
 issues.closed_at=`cerró <a id="%[1]s" href="#%[1]s">%[2]s</a>`
 issues.reopened_at=`reabrió <a id="%[1]s" href="#%[1]s">%[2]s</a>`
 issues.commit_ref_at=`mencionada esta incidencia en un commit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
+issues.ref_issue_at=`referenciada esta incidencia %[1]s`
+issues.ref_pull_at=`refereciada este Pull Request %[1]s`
+issues.ref_issue_ext_at=`referenciada esta incidencia desde %[1]s %[2]s`
+issues.ref_pull_ext_at=`refereciada este Pull Request desde %[1]s %[2]s`
 issues.poster=Autor
 issues.collaborator=Colaborador
 issues.owner=Propietario
@@ -944,6 +975,7 @@ issues.review.reviewers=Revisores
 issues.review.show_outdated=Mostrar obsoletos
 issues.review.hide_outdated=Ocultar obsoletos
 
+pulls.desc=Activar Pull Requests y revisiones de código.
 pulls.new=Nuevo Pull Request
 pulls.compare_changes=Nuevo pull request
 pulls.compare_changes_desc=Seleccione la rama en la que se fusiona y la rama a recuperar.
@@ -959,15 +991,18 @@ pulls.merged_title_desc=fusionados %[1]d commits de <code>%[2]s</code> en <code>
 pulls.tab_conversation=Conversación
 pulls.tab_commits=Commits
 pulls.tab_files=Archivos modificados
-pulls.reopen_to_merge=Vuelva a abrir la solicitud de "pull" para realizar una fusión.
+pulls.reopen_to_merge=Vuelva a abrir este Pull Request para realizar una fusión.
 pulls.cant_reopen_deleted_branch=Este pull request no se puede reabrir porque la rama fue eliminada.
 pulls.merged=Fusionado
+pulls.merged_as=El Pull Request se ha fusionado como <a rel="nofollow" class="ui sha" href="%[1]s"><code>%[2]s</code></a>.
 pulls.has_merged=El pull request ha sido fusionado.
 pulls.title_wip_desc=`<a href="#">Comience el título con <strong>%s</strong></a> para prevenir que el pull request se fusione accidentalmente.`
 pulls.cannot_merge_work_in_progress=Este pull request está marcado como un trabajo en progreso. Elimine el prefijo <strong>%s</strong> del título cuando esté listo
 pulls.data_broken=Este pull request está rota debido a que falta información del fork.
 pulls.files_conflicted=Este pull request tiene cambios en conflicto con la rama de destino.
 pulls.is_checking=La comprobación de conflicto de fusión está en progreso. Inténtalo de nuevo en unos momentos.
+pulls.required_status_check_failed=Algunos controles requeridos no han tenido éxito.
+pulls.required_status_check_administrator=Como administrador, aún puede fusionar este Pull Request.
 pulls.blocked_by_approvals=Este pull request aún no tiene suficientes aprobaciones. %d de %d autorizaciones concedidas.
 pulls.can_auto_merge_desc=Este Pull Request puede ser fusionado automáticamente.
 pulls.cannot_auto_merge_desc=Este pull request no se puede combinar automáticamente debido a conflictos.
@@ -975,6 +1010,7 @@ pulls.cannot_auto_merge_helper=Combinar manualmente para resolver los conflictos
 pulls.no_merge_desc=Este pull request no se puede combinar porque todas las opciones de combinación del repositorio están deshabilitadas.
 pulls.no_merge_helper=Habilite las opciones de combinación en la configuración del repositorio o fusione el pull request manualmente.
 pulls.no_merge_wip=Este pull request no se puede combinar porque está marcada como un trabajo en progreso.
+pulls.no_merge_status_check=No se puede fusionar este pull request porque no todas las comprobaciones de estado requeridas resultaron exitosas.
 pulls.merge_pull_request=Fusionar Pull Request
 pulls.rebase_merge_pull_request=Hacer Rebase y Fusionar
 pulls.rebase_merge_commit_pull_request=Hacer Rebase y Fusionar (--no-ff)
@@ -1116,6 +1152,7 @@ settings.collaboration=Colaboradores
 settings.collaboration.admin=Administrador
 settings.collaboration.write=Escritura
 settings.collaboration.read=Lectura
+settings.collaboration.owner=Propietario
 settings.collaboration.undefined=Indefinido
 settings.hooks=Webhooks
 settings.githooks=Git Hooks
@@ -1123,6 +1160,10 @@ settings.basic_settings=Configuración Básica
 settings.mirror_settings=Configuración de réplica
 settings.sync_mirror=Sincronizar ahora
 settings.mirror_sync_in_progress=La sincronización del repositorio replicado está en curso. Vuelva a intentarlo más tarde.
+settings.email_notifications.enable=Habilitar las notificaciones por correo electrónico
+settings.email_notifications.onmention=Enviar correo sólo al mencionar
+settings.email_notifications.disable=Deshabilitar las notificaciones por correo electrónico
+settings.email_notifications.submit=Establecer Preferencia de correo electrónico
 settings.site=Sitio web
 settings.update_settings=Actualizar configuración
 settings.advanced_settings=Ajustes avanzados
@@ -1193,6 +1234,11 @@ settings.collaborator_deletion_desc=Eliminar un colaborador revocará su acceso
 settings.remove_collaborator_success=El colaborador ha sido eliminado.
 settings.search_user_placeholder=Buscar usuario…
 settings.org_not_allowed_to_be_collaborator=Las organizaciones no pueden ser añadidas como colaboradoras.
+settings.change_team_access_not_allowed=Cambiar el acceso del equipo al repositorio se ha restringido al propietario de la organización
+settings.team_not_in_organization=El equipo no pertenece a la misma organización que el repositorio
+settings.add_team_duplicate=El equipo ya tiene acceso al repositorio
+settings.add_team_success=Ahora el equipo ya tiene acceso al repositorio.
+settings.remove_team_success=Se ha eliminado el acceso del equipo al repositorio.
 settings.add_webhook=Añadir Webhook
 settings.add_webhook.invalid_channel_name=El nombre del canal Webhook no puede estar vacío y no puede contener sólo un # carácter.
 settings.hooks_desc=Los webhooks automáticamente hacen peticiones HTTP POST a un servidor cuando ciertos eventos de Gitea se activan. Lee más en <a target="_blank" rel="noopener noreferrer" href="%s">la guía de webhooks</a>.
@@ -1242,6 +1288,8 @@ settings.event_pull_request=Pull Request
 settings.event_pull_request_desc=Pull Request abierta, cerrada, reabierta, editada, aprobada, rechazada, comentario de revisión, asignada, no asignada, etiqueta actualizada, etiqueta borrada o sincronizada.
 settings.event_push=Push
 settings.event_push_desc=Git push a un repositorio.
+settings.branch_filter=Filtro de rama
+settings.branch_filter_desc=Lista blanca de rama para eventos de push, creación de rama y eliminación de rama, especificados como patrón globo. Si está vacío o <code>*</code>, se reportan eventos para todas las ramas. Ver <a href="https://godoc.org/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a> documentación para la sintaxis. Ejemplos: <code>master</code>, <code>{master,release*}</code>.
 settings.event_repository=Repositorio
 settings.event_repository_desc=Repositorio creado o eliminado.
 settings.active=Activo
@@ -1260,20 +1308,20 @@ settings.add_discord_hook_desc=Integrar <a href="%s">Discord</a> en su repositor
 settings.add_dingtalk_hook_desc=Integrar <a href="%s">Dingtalk</a> en su repositorio.
 settings.add_telegram_hook_desc=Integrar <a href="%s">Telegrama</a> en tu repositorio.
 settings.add_msteams_hook_desc=Integrar <a href="%s">Microsoft Teams</a> en tu repositorio.
-settings.deploy_keys=Claves de Despliegue
-settings.add_deploy_key=Añadir Clave de Despliegue
-settings.deploy_key_desc=Las claves de despliegue tienen acceso de sólo lectura al repositorio.
+settings.deploy_keys=Claves de Implementación
+settings.add_deploy_key=Añadir Clave de Implementación
+settings.deploy_key_desc=Las claves de implementación tienen acceso de sólo lectura al repositorio.
 settings.is_writable=Habilitar acceso de escritura
-settings.is_writable_info=Permitir que esta clave de despliegue pueda hacer <strong>push</strong> a este repositorio.
-settings.no_deploy_keys=Aún no existen claves de despliegue.
+settings.is_writable_info=Permitir que esta clave de implementación pueda hacer <strong>push</strong> a este repositorio.
+settings.no_deploy_keys=Aún no existen claves de implementación.
 settings.title=Título
 settings.deploy_key_content=Contenido
-settings.key_been_used=Una clave de despliegue con contenido idéntico ya se encuentra en uso.
-settings.key_name_used=Ya existe una clave de despliegue con el mismo nombre.
-settings.add_key_success=La clave de despliegue '%s' ha sido añadida.
-settings.deploy_key_deletion=Eliminar clave de despliegue
-settings.deploy_key_deletion_desc=Eliminar una clave de despliegue revocará el acceso de la misma a este repositorio. ¿Continuar?
-settings.deploy_key_deletion_success=La clave de despliegue ha sido eliminada.
+settings.key_been_used=Una clave de implementación con contenido idéntico ya se encuentra en uso.
+settings.key_name_used=Ya existe una clave de implementación con el mismo nombre.
+settings.add_key_success=La clave de implementación '%s' ha sido añadida.
+settings.deploy_key_deletion=Eliminar clave de implementación
+settings.deploy_key_deletion_desc=Eliminar una clave de implementación revocará el acceso de la misma a este repositorio. ¿Continuar?
+settings.deploy_key_deletion_success=La clave de implementación ha sido eliminada.
 settings.branches=Ramas
 settings.protected_branch=Protección de rama
 settings.protected_branch_can_push=¿Permitir hacer push?
@@ -1284,6 +1332,7 @@ settings.protect_this_branch=Activar protección de rama
 settings.protect_this_branch_desc=Prevenir eliminar y desactivar hacer git push en esta rama.
 settings.protect_whitelist_committers=Activar lista blanca para hacer push
 settings.protect_whitelist_committers_desc=Permitir hacer push en esta rama a los usuarios o equipos en la lista blanca (pero no hacer push forzado).
+settings.protect_whitelist_deploy_keys=Poner en lista blanca las claves de implementación con permisos de hacer push
 settings.protect_whitelist_users=Usuarios en la lista blanca para hacer push:
 settings.protect_whitelist_search_users=Buscar usuarios…
 settings.protect_whitelist_teams=Equipos en la lista blanca para hacer push:
@@ -1292,6 +1341,9 @@ settings.protect_merge_whitelist_committers=Activar lista blanca para fusionar
 settings.protect_merge_whitelist_committers_desc=Permitir a los usuarios o equipos de la lista a fusionar peticiones pull dentro de esta rama.
 settings.protect_merge_whitelist_users=Usuarios en la lista blanca para fusionar:
 settings.protect_merge_whitelist_teams=Equipos en la lista blanca para fusionar:
+settings.protect_check_status_contexts=Habilitar comprobación de estado
+settings.protect_check_status_contexts_desc=Requiere comprobaciones de estado para pasar antes de fusionar. Elija qué los controles de estado deben pasar antes de que las ramas puedan ser fusionadas en una rama que coincida con esta regla. Cuando está habilitada, los commits deben ser primero empujados a otra rama y luego fusionados, o empujados directamente a una rama que coincida con esta regla después de que hayan pasado los comprobaciones de estado. Si no se selecciona ningún contexto, la última confirmación debe tener éxito independientemente del contexto.
+settings.protect_check_status_contexts_list=Comprobaciones de estado para este repositorio encontradas durante la semana pasada
 settings.protect_required_approvals=Aprobaciones requeridas:
 settings.protect_required_approvals_desc=Permitir solo fusionar un pull request con suficientes revisiones positivas de usuarios o equipos en la lista blanca.
 settings.protect_approvals_whitelist_users=Lista blanca de usuarios revisores:
@@ -1328,7 +1380,10 @@ diff.parent=padre
 diff.commit=commit
 diff.git-notes=Notas
 diff.data_not_available=El contenido del Diff no está disponible
-diff.show_diff_stats=Mostrar estadísticas de diff
+diff.options_button=Opciones de diferencias
+diff.show_diff_stats=Mostrar estadísticas
+diff.download_patch=Descargar archivo de parche
+diff.download_diff=Descargar archivo de diferencias
 diff.show_split_view=Dividir vista
 diff.show_unified_view=Unificar vista
 diff.whitespace_button=Espacio blanco
@@ -1339,6 +1394,11 @@ diff.whitespace_ignore_at_eol=Ignorar cambios en espacios en blanco al final de
 diff.stats_desc=Se han <strong>modificado %d ficheros</strong> con <strong>%d adiciones</strong> y <strong>%d borrados</strong>
 diff.bin=BIN
 diff.view_file=Ver fichero
+diff.file_before=Antes
+diff.file_after=Después
+diff.file_image_width=Anchura
+diff.file_image_height=Altura
+diff.file_byte_size=Tamaño
 diff.file_suppressed=La diferencia del archivo ha sido suprimido porque es demasiado grande
 diff.too_many_files=Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio
 diff.comment.placeholder=Deja un comentario
@@ -1406,6 +1466,8 @@ branch.restore_failed=Fallo al restaurar la rama %s.
 branch.protected_deletion_failed=La rama '%s' está protegida. No se puede eliminar.
 branch.restore=Restaurar rama '%s'
 branch.download=Descargar rama '%s'
+branch.included_desc=Esta rama forma parte de la predeterminada
+branch.included=Incluida
 
 topic.manage_topics=Administrar temas
 topic.done=Hecho
@@ -1441,6 +1503,8 @@ settings.options=Organización
 settings.full_name=Nombre completo
 settings.website=Página web
 settings.location=Localización
+settings.permission=Permisos
+settings.repoadminchangeteam=El administrador del repositorio puede añadir y eliminar el acceso a equipos
 settings.visibility=Visibilidad
 settings.visibility.public=Público
 settings.visibility.limited=Limitado (Visible sólo para los usuarios registrados)
@@ -1672,7 +1736,7 @@ auths.oauth2_use_custom_url=Utilizar URLs personalizadas en vez de direcciones U
 auths.oauth2_tokenURL=URL del token
 auths.oauth2_authURL=URL de Autorización
 auths.oauth2_profileURL=URL del perfil
-auths.oauth2_emailURL=URL de email
+auths.oauth2_emailURL=URL de correo
 auths.enable_auto_register=Hablilitar Auto-Registro
 auths.tips=Consejos
 auths.tips.oauth2.general=Autenticación OAuth2
@@ -1687,6 +1751,7 @@ auths.tip.google_plus=Obtener credenciales de cliente OAuth2 desde la consola AP
 auths.tip.openid_connect=Use el OpenID Connect Discovery URL (<server>/.well-known/openid-configuration) para especificar los puntos finales
 auths.tip.twitter=Ir a https://dev.twitter.com/apps, crear una aplicación y asegurarse de que la opción "Permitir que esta aplicación sea usada para iniciar sesión con Twitter" está activada
 auths.tip.discord=Registrar una nueva aplicación en https://discordapp.com/developers/applications/me
+auths.tip.gitea=Registra una nueva aplicación OAuth2. La guía puede ser encontrada en https://docs.gitea.io/es-us/oauth2-provider/
 auths.edit=Editar origen de autenticación
 auths.activated=Este origen de autenticación ha sido activado
 auths.new_success=Se agregó la autenticación '%s'.
@@ -1919,12 +1984,15 @@ mark_as_unread=Marcar como no leído
 mark_all_as_read=Marcar todo como leído
 
 [gpg]
+default_key=Firmado con clave predeterminada
 error.extract_sign=Error al extraer la firma
 error.generate_hash=Error al generar hash of commit
 error.no_committer_account=Ninguna cuenta vinculada a la dirección de correo electrónico del committer
 error.no_gpg_keys_found=No se encontró ninguna clave conocida en la base de datos para esta firma
 error.not_signed_commit=No es un commit firmado
 error.failed_retrieval_gpg_keys=No se pudo recuperar cualquier clave adjunta a la cuenta del committer
+error.probable_bad_signature=¡ADVERTENCIA! ¡Hay una clave con este ID en la base de datos, pero esa clave no verifica este commit! Este commit es SOSPECHOSO.
+error.probable_bad_default_signature=¡ADVERTENCIA! ¡La clave por defecto tiene este ID pero esa clave no verifica este commit! Este commit es SOSPECHOSO.
 
 [units]
 error.no_unit_allowed_repo=No tiene permisos para acceder a ninguna sección de este repositorio.
diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini
index 0ff49d19d3..a0551ed71d 100644
--- a/options/locale/locale_fa-IR.ini
+++ b/options/locale/locale_fa-IR.ini
@@ -1,5 +1,3 @@
-app_desc=یک سرویس گیت بی‌درد سر و راحت
-
 home=خانه
 dashboard=میز کار
 explore=گشت‌و‌گذار
@@ -75,6 +73,17 @@ write=نوشتن
 preview=پیش نمایش
 loading=بارگذاری…
 
+[startpage]
+app_desc=یک سرویس گیت بی‌درد سر و راحت
+install=راه‌اندازی ساده
+install_desc=به سادگی <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-binary/"> دودویی را اجرا </a> بر روی سکوی خود کنید. یا shop Gitea زا یل <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker"> داکر </a> یا <a target="_blank" rel="noopener noreferrer" href="https://github.com/alvaroaleman/ansible-gitea/blob/master/Vagrantfile">Vagrant </a>,یا این را دریافت کنید.<a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-package/"> بسته بندی شده </a>
+platform=مستقل از سکو
+platform_desc=گیت همه جا اجرا می‌شود <a target="_blank" rel="noopener noreferrer" href="http://golang.org/"> بریم!</a> می‌توانید Windows, macOS, Linux, ARM و ... هر کدام را دوست داشتید انتخاب کنید!
+lightweight=ابزارک سبک
+lightweight_desc=گیتی با حداقل منابع میتوانید برای روی دستگاه Raspberry Pi اجرا شود و مصرف انرژی شما را کاهش دهد!
+license=متن باز
+license_desc=برو به <a target="_blank" rel="noopener noreferrer" href="https://code.gitea.io/gitea">code.gitea.io/gitea</a>! به ملحق شوید با <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea"> مشارکت کردن</a> برای این که این پروژه بهتر شود. برای مشارکت کردن خجالت نکشید!
+
 [install]
 install=نصب و راه اندازی
 title=تنظیمات اولیه
@@ -250,7 +259,7 @@ openid_signin_desc=نوع حساب کاربری خود را وارد کنید. 
 disable_forgot_password_mail=بازیابی حساب غیر فعال شده است. لطفا با مدیر سایت تماس بگیرید.
 email_domain_blacklisted=شما نمیتوانید با ایمیل خود ثبت نام کنید.
 authorize_application=برنامه احراز هویت
-authroize_redirect_notice=اگر شما این برنامه را تایید کنید، به %s منتقل خواهید شد.
+authorize_redirect_notice=اگر شما این برنامه را تایید کنید، به %s منتقل خواهید شد.
 authorize_application_created_by=این برنامه توسط %s ساخته شده است.
 authorize_application_description=اگر شما دسترسی داشته باشید. میتوانید تمامی فیلد های حساب کاربری خود را تغییر دهید. از جمله مخازن و سازمان های خصوصی.
 authorize_title=تاییدیه "%s" برای دسترسی به اکانت شما؟
@@ -314,10 +323,12 @@ team_no_units_error=اجازه دسترسی به حداقل یک بخش مخزن
 email_been_used=این ایمیل قبلا ثبت شده.
 openid_been_used=آدرس OpenID %s قبلا ثبت شده است.
 username_password_incorrect=نام کاربری یا گذرواژه صحیح نیست.
+password_complexity=پسورد ضرورت به پیچیدگی نداشته باشد.
 enterred_invalid_repo_name=نام مخزنی که وارد کرده اید صحیح نمی باشد.
 enterred_invalid_owner_name=نام مالک جدید معتبر نیست.
 enterred_invalid_password=گذرواژه وارد شده صحیح نیست.
 user_not_exist=کاربر وجود ندارد.
+team_not_exist=تیم وجود ندارد.
 last_org_owner=قادر به حذف کاربر آخر از تیم "صاحبان" نیست. باید حداقل یک مالک در هر تیم باشد.
 cannot_add_org_to_team=یک سازمان را نمی توان به عنوان عضو تیم اضافه کرد.
 
@@ -634,6 +645,8 @@ migrate.lfs_mirror_unsupported=قرینه سازی LFS اشیا پشتیبانی
 migrate.migrate_items_options=زمانی که از github مهاجرت می‌کنید. ورودی نام‌کاربری و گزینه‌های مهاجرت نمایش داده می‌شوند.
 migrated_from=مهاجرت از <a href="%[1]s">%[2]s</a>
 migrated_from_fake=مهاجرت از %[1]s
+migrate.migrating=مهاجرت از <b>%s</b> ...
+migrate.migrating_failed=مهاجرت از <b>%s</b> ناموفق بود.
 
 mirror_from=قرینه از
 forked_from=انشعاب شده از
@@ -682,6 +695,8 @@ stored_lfs=ذخیره شده با GIT LFS
 commit_graph=نمودار کامیت
 blame=سرزنش
 normal_view=نمایش عادی
+line=خط
+lines=خطوط
 
 editor.new_file=پرونده جدید
 editor.upload_file=بارگذاری پرونده
@@ -707,6 +722,7 @@ editor.delete=حذف '%s'
 editor.commit_message_desc=توضیحی تخصصی به دلخواه اضافه نمایید…
 editor.commit_directly_to_this_branch=ثبت کامیت به صورت مستقیم در انشعاب <strong class="branch-name">%s</strong>.
 editor.create_new_branch=یک <strong> شاخه جدید</strong> برای این commit ایجاد کنید و تقاضای واکشی را شروع کنید.
+editor.create_new_branch_np=یک <strong> شاخه جدید </strong> برای کامیت بسازید.
 editor.propose_file_change=پیشنهاد تغییر پرونده
 editor.new_branch_name_desc=نام شاخه ی جدید…
 editor.cancel=انصراف
@@ -721,6 +737,8 @@ editor.file_editing_no_longer_exists=فایل آماده ویرایش شده ا
 editor.file_deleting_no_longer_exists=فایل آماده حذف می‌شود '%s'، مدتی بعد در مخزن از دسترس خارج می‎شود.
 editor.file_changed_while_editing=محتوای پرونده تغییر میکند از زمانی که شما شروع به ویرایش می‌کنید.<a target="_blank" rel="noopener noreferrer" href="%s">اینجا کلیک کنید</a> تا ببنید آن را یا <strong>یا کامیت تغییرات را دوباره</strong> اعمال کنید تا روی آن بازنویسی شود.
 editor.file_already_exists=فایلی با نام %s از قبل در مخزن موجود است.
+editor.commit_empty_file_header=کامیت کردن یک پرونده خالی
+editor.commit_empty_file_text=فایل کامیت شده شما تقریبا خالیست. پردازش شود؟
 editor.no_changes_to_show=تغییری برای نمایش وجود ندارد.
 editor.fail_to_update_file=خطا در ساخت/به‌روزرسانی پرونده %s. خطای رخ داده: %v
 editor.add_subdir=افزودن پوشه…
@@ -786,6 +804,7 @@ issues.delete_branch_at=`حذف شاخه <b>%s</b> %s`
 issues.open_tab=%d باز
 issues.close_tab=%d بسته
 issues.filter_label=برچسب
+issues.filter_label_exclude=`از<code>alt</code> + <code>click/enter</code> برای رد کردن برچسب‌ها استفاده کنید `
 issues.filter_label_no_select=تمامی برچسب‎ها
 issues.filter_milestone=نقطه عطف
 issues.filter_milestone_no_select=تمام نقاط عطف
@@ -988,6 +1007,7 @@ pulls.data_broken=این تقاضای واکشی به دلیل از دست رف
 pulls.files_conflicted=این تقاضای واکشی دارای تغییراتی است که با شاخه هدف تداخل دارد.
 pulls.is_checking=در حال پردازش تداخل در ادغام می‌باشد. لطفاً لحظاتی بعد امتحان کنید.
 pulls.required_status_check_failed=برخی بررسی های ضروری موفقیت آمیز نبود.
+pulls.required_status_check_administrator=مثل یک مدیر، ممکن است شما این تقاضای واکشی را مسکوت بگذارید.
 pulls.blocked_by_approvals=این تقاضای واکشی هنوز به اندازه کافی مورد مورد تایید نیست. %d از %d مورد آن قابل تایید می‌باشد.
 pulls.can_auto_merge_desc=این تقاضا واکشی می تواند به صورت خودکار ادغام شود.
 pulls.cannot_auto_merge_desc=این تقاضای واکشی به علت تداخل نمی تواند به صورت خودکار ادغام شود.
@@ -1137,6 +1157,7 @@ settings.collaboration=همكار
 settings.collaboration.admin=مدیر
 settings.collaboration.write=نوشتن
 settings.collaboration.read=خواندن
+settings.collaboration.owner=مالک
 settings.collaboration.undefined=تعریف نشده
 settings.hooks=Webhooks
 settings.githooks=Git Hooks
@@ -1218,6 +1239,11 @@ settings.collaborator_deletion_desc=حذف یک همکار از مخزن دست
 settings.remove_collaborator_success=همكار حذف شد.
 settings.search_user_placeholder=جستجوی کاربر…
 settings.org_not_allowed_to_be_collaborator=سازمان ها را نمیتوان به عنوان همکار افزود.
+settings.change_team_access_not_allowed=تغییر دسترسی های تیم برای این مخزن توسط مالک ارگان محدود شده است
+settings.team_not_in_organization=تیم همانند ارگان برای این مخزن نیست
+settings.add_team_duplicate=تیم پیش از این مخزن داشته
+settings.add_team_success=تیم هم‌اکنون به مخزن دسترسی دارد.
+settings.remove_team_success=دسترسی تیم به مخزن حذف شد.
 settings.add_webhook=اضافه‌کردن Webhook
 settings.add_webhook.invalid_channel_name=کانال هوک تحت وب نمی‌تواند خالی باشد و نمی‌توانید تنها حاوی این حرف # باشد.
 settings.hooks_desc=هوک تحت وب به صورت خودکار درخواست POST HTTP را به سمت سرور روانه می‌کند زمانی که ماشه رخداد Gitea کشیده شود. برای اطلاعات بیشتر به <a target="_blank" rel="noopener noreferrer" href="%s"> راهنمای هوک تحت وب </a> مراجعه کنید.
@@ -1311,6 +1337,7 @@ settings.protect_this_branch=فعال کردن حفاظت از شاخه
 settings.protect_this_branch_desc=جلوگیری از هر گونه حذف یا غیرفعال کردن هر درج شدنی در این شاخه.
 settings.protect_whitelist_committers=فعال کردن لیست سفید درج
 settings.protect_whitelist_committers_desc=اجازه به کاربران یا تیم‌های موجود لیست سفید برای درج در این شاخه (اما نه درج اجباری).
+settings.protect_whitelist_deploy_keys=لیست سفید کلید های انتشار و دسترسی نوشتن برای درج
 settings.protect_whitelist_users=کاربران لیست سفید برای درج در مخزن:
 settings.protect_whitelist_search_users=جستجوی کاربر…
 settings.protect_whitelist_teams=تیم‌های لیست سفید برای درج در مخزن:
@@ -1320,6 +1347,7 @@ settings.protect_merge_whitelist_committers_desc=اجازه به کاربران
 settings.protect_merge_whitelist_users=کاربران لیست سفید برای ادغام:
 settings.protect_merge_whitelist_teams=تیم‌های لیست سفید برای ادغام:
 settings.protect_check_status_contexts=فعال کردن حالات بررسی
+settings.protect_check_status_contexts_desc=حالات ضروری را میتوانید برای کنترل قبل از ادغام فعال کنید تا با قوانین شما اگر سازگار بود شاخه بتواند ادغام شود. زمانی که فعال شود، کامیت باید ابتدا در شاخه دیگری درج شود.
 settings.protect_check_status_contexts_list=آخرین بررسی حالات این مخزن در هفته گذشته اتفاق افتاده است
 settings.protect_required_approvals=نیازمند تاییدیه:
 settings.protect_required_approvals_desc=فقط اجازه تقاضای ادغام واکشی برای بازبینی‌کننده‌گانی که دارای امتیاز مثبت کافی که از لیست سفید کاربران یا تیم‌ها باشند.
@@ -1357,7 +1385,10 @@ diff.parent=والد
 diff.commit=کامیت
 diff.git-notes=یادداشت‌ها
 diff.data_not_available=محتوای تفاوت ها در دسترس نیست
-diff.show_diff_stats=نمایش آمار تفاوت ها
+diff.options_button=تنظیمات (diff) تغییرات
+diff.show_diff_stats=نمایش وضعیت
+diff.download_patch=دانلود پرونده وصله
+diff.download_diff=دانلود فایل تغییرات diff
 diff.show_split_view=مشاهده تقسیم شده
 diff.show_unified_view=نمای یکپارچه
 diff.whitespace_button=فضای خالی
@@ -1440,6 +1471,8 @@ branch.restore_failed=ناموفق در بازگرانی شاخه '%s'.
 branch.protected_deletion_failed=شاخه «%s» محافظت شده است. نمی‌توانید آن‌را حذف کنید.
 branch.restore=بازگردانی شاخه '%s'
 branch.download=بارگیری یا دریافت شاخه '%s'
+branch.included_desc=این شاخه بخشی از شاخه پیش فرض است
+branch.included=مشمول شده
 
 topic.manage_topics=مدیریت موضوعات
 topic.done=انجام شد
@@ -1475,6 +1508,8 @@ settings.options=سازمان
 settings.full_name=نام کامل
 settings.website=تارنما
 settings.location=موقعیت مکانی
+settings.permission=مجوزها
+settings.repoadminchangeteam=مدیر مخزن می‌تواند دسترسی برای تیم اضافه یا حذف کند
 settings.visibility=پدیداری
 settings.visibility.public=عمومی
 settings.visibility.limited=محدود شده (پدیدار برای کاربر وارد شده فقط)
@@ -1955,12 +1990,15 @@ mark_as_unread=علامتگذاری بعنوان خوانده نشده
 mark_all_as_read=علامت همه به عنوان خوانده شده
 
 [gpg]
+default_key=ثبت شده با کلید پیش فرض
 error.extract_sign=خطا در استخراج امضا
 error.generate_hash=خطا در ساختن هش کامیت
 error.no_committer_account=هیچ ایمیلی به حساب کاربری صاحب کامیت پیونده داده نشده است
 error.no_gpg_keys_found=هیچ کلید شناخته شده ای برای این امضا در پایگاه داده ها یافت نشد
 error.not_signed_commit=هیچ کامیتی تکلیف نشده است
 error.failed_retrieval_gpg_keys=بازیابی هر کلیدی که به حساب کاربری کامیت دهنده پیوست شده بود ناموفق بود
+error.probable_bad_signature=هشدار! اگرچه اینجا یک کلید با ID در پایگاه داده است این کامیت تایید نشده است! این کامیت مشـــکــــوک است.
+error.probable_bad_default_signature=هشدار! اگرچه اینجا یک کلید پیش فرض با ID است این اما کامیت تایید نشده است! این کامیت مشـــکــــوک است.
 
 [units]
 error.no_unit_allowed_repo=شما اجازه دسترسی به هیچ قسمت از این مخزن را ندارید.
diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini
index 12829c527b..75a80de528 100644
--- a/options/locale/locale_fi-FI.ini
+++ b/options/locale/locale_fi-FI.ini
@@ -1,5 +1,3 @@
-app_desc=Ongelmaton, itsehostattu Git-palvelu
-
 home=Etusivu
 dashboard=Kojelauta
 explore=Tutki
@@ -61,6 +59,8 @@ issues=Ongelmat
 cancel=Peruuta
 
 
+[startpage]
+
 [install]
 install=Asennus
 title=Alkuperäiset asetukset
@@ -538,7 +538,6 @@ settings.edit_protected_branch=Muokkaa
 
 diff.browse_source=Selaa lähdekoodia
 diff.parent=vanhempi
-diff.show_diff_stats=Näytä diff tilastot
 diff.show_split_view=Jaettu näkymä
 diff.show_unified_view=Yhdistetty näkymä
 diff.stats_desc=<strong>%d muutettua tiedostoa</strong> jossa <strong>%d lisäystä</strong> ja <strong>%d poistoa</strong>
diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini
index e0e04f86a7..c9a8711533 100644
--- a/options/locale/locale_fr-FR.ini
+++ b/options/locale/locale_fr-FR.ini
@@ -1,5 +1,3 @@
-app_desc=Un service Git auto-hébergé sans prise de tête
-
 home=Accueil
 dashboard=Tableau de bord
 explore=Explorateur
@@ -75,6 +73,15 @@ write=Écrire
 preview=Aperçu
 loading=Chargement…
 
+[startpage]
+app_desc=Un service Git auto-hébergé sans prise de tête
+install=Facile à installer
+install_desc=Il suffit de <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-binary/">lancer l'exécutable</a> correspondant à votre système. Ou d'utiliser Gitea avec <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a> ou <a target="_blank" rel="noopener noreferrer" href="https://github.com/alvaroaleman/ansible-gitea/blob/master/Vagrantfile">Vagrant</a> ou en l'installant depuis un <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-package/">package</a>.
+platform=Multi-plateforme
+platform_desc=Gitea tourne partout où <a target="_blank" rel="noopener noreferrer" href="http://golang.org/">Go</a> peut être compilé : Windows, macOS, Linux, ARM, etc. Choisissez votre préféré !
+lightweight=Léger
+license=Open Source
+
 [install]
 install=Installation
 title=Configuration initiale
@@ -250,7 +257,6 @@ openid_signin_desc=Veuillez entrer votre URI OpenID. Par exemple: https://anne.m
 disable_forgot_password_mail=La récupération de compte est désactivée. Veuillez contacter l'administrateur du site.
 email_domain_blacklisted=Vous ne pouvez pas vous enregistrer avec votre adresse e-mail.
 authorize_application=Autoriser l'application
-authroize_redirect_notice=Vous serez redirigé vers %s si vous autorisez cette application.
 authorize_application_created_by=Cette application a été créée par %s.
 authorize_application_description=Si vous accordez l'accès, il sera en mesure d'accéder et d'écrire toutes les informations de votre compte, y compris les dépôts privés et les organisations.
 authorize_title=Autoriser "%s" à accéder à votre compte ?
@@ -299,6 +305,7 @@ max_size_error=` %s caractères maximum `
 email_error=` adresse e-mail invalide `
 url_error=` URL invalide `
 include_error=`doit contenir la sous-chaîne '%s'.`
+glob_pattern_error=` le motif de développement est invalide : %s.`
 unknown_error=Erreur inconnue :
 captcha_incorrect=Le code CAPTCHA est incorrect.
 password_not_match=Les mots de passe ne correspondent pas.
@@ -313,10 +320,12 @@ team_no_units_error=Autoriser l’accès à au moins une section du dépôt.
 email_been_used=Cette adresse e-mail est déjà utilisée.
 openid_been_used=Adresse OpenID '%s' déjà utilisée.
 username_password_incorrect=Identifiant ou mot de passe invalide.
+password_complexity=Le mot de passe ne respecte pas les exigences de complexité.
 enterred_invalid_repo_name=Le nom de dépôt saisi est incorrect.
 enterred_invalid_owner_name=Le nom du nouveau propriétaire est invalide.
 enterred_invalid_password=Le mot de passe saisi est incorrect.
 user_not_exist=Cet utilisateur n'existe pas.
+team_not_exist=L'équipe n'existe pas.
 last_org_owner=Vous ne pouvez pas supprimer le dernier utilisateur de l’équipe « propriétaires ». Il doit y avoir au moins un propriétaire dans chaque équipe.
 cannot_add_org_to_team=Une organisation ne peut être ajoutée comme membre d'une équipe.
 
@@ -557,6 +566,7 @@ delete_account_title=Supprimer un compte
 delete_account_desc=Êtes-vous sûr de vouloir supprimer définitivement ce compte ?
 
 email_notifications.enable=Activer les notifications par e-mail
+email_notifications.onmention=N'envoyer un e-mail que si vous êtes mentionné
 email_notifications.disable=Désactiver les notifications par email
 email_notifications.submit=Définir la préférence e-mail
 
@@ -565,6 +575,7 @@ owner=Propriétaire
 repo_name=Nom du dépôt
 repo_name_helper=Idéalement, le nom d'un dépôt devrait être court, mémorisable et unique.
 visibility=Visibilité
+visibility_description=Seul le propriétaire ou les membres de l'organisation s'ils ont des droits, seront en mesure de le voir.
 visibility_helper=Rendre le dépôt privé
 visibility_helper_forced=L'administrateur de votre serveur impose que les nouveaux dépôts soient privés.
 visibility_fork_helper=(Changer ceci affectera toutes les bifurcations.)
@@ -587,6 +598,7 @@ mirror_prune_desc=Supprimer les références externes obsolètes
 mirror_interval=Intervalle de synchronisation ('h', 'm', et 's' sont des unités valides), 0 pour désactiver.
 mirror_interval_invalid=L'intervalle de synchronisation est invalide.
 mirror_address=Cloner depuis une URL
+mirror_address_desc=Mettez les identifiants requis dans la section Autorisation de Clonage.
 mirror_address_url_invalid=L'url fournie est invalide. Vous devez échapper tous les composants de l'url correctement.
 mirror_address_protocol_invalid=L'url fournie est invalide. Seuls les protocoles http(s):// ou git:// peuvent être la source du miroir.
 mirror_last_synced=Dernière synchronisation
@@ -625,6 +637,8 @@ migrate.lfs_mirror_unsupported=La synchronisation des objets LFS n'est pas suppo
 migrate.migrate_items_options=Quand vous migrez depuis github, saisissez un nom d'utilisateur et des options de migration seront affichées.
 migrated_from=Migré de <a href="%[1]s">%[2]s</a>
 migrated_from_fake=Migré de %[1]s
+migrate.migrating=Migration de <b>%s</b> ...
+migrate.migrating_failed=La migration de <b>%s</b> a échoué.
 
 mirror_from=miroir de
 forked_from=bifurqué depuis
@@ -673,6 +687,8 @@ stored_lfs=Stocké avec Git LFS
 commit_graph=Graphique des révisions
 blame=Annotations
 normal_view=Vue normale
+line=ligne
+lines=lignes
 
 editor.new_file=Nouveau fichier
 editor.upload_file=Téléverser un fichier
@@ -698,6 +714,7 @@ editor.delete=Supprimer '%s'
 editor.commit_message_desc=Ajouter une description détaillée facultative…
 editor.commit_directly_to_this_branch=Soumettre directement dans la branche <strong class="branch-name">%s</strong>.
 editor.create_new_branch=Créer une <strong>nouvelle branche</strong> pour cette révision et envoyer une nouvelle demande d'ajout.
+editor.create_new_branch_np=Créer une <strong>nouvelle branche</strong> pour cette révision.
 editor.propose_file_change=Proposer une modification du fichier
 editor.new_branch_name_desc=Nouveau nom de la branche…
 editor.cancel=Annuler
@@ -973,6 +990,8 @@ pulls.cannot_merge_work_in_progress=Cette demande d'ajout est marquée comme en
 pulls.data_broken=Cette demande de fusion est impossible par manque d'informations de bifurcation.
 pulls.files_conflicted=Cette demande d'ajout contient des modifications en conflit avec la branche ciblée.
 pulls.is_checking=Vérification des conflits de fusion en cours. Réessayez dans quelques instants.
+pulls.required_status_check_failed=Certains contrôles requis n'ont pas réussi.
+pulls.required_status_check_administrator=En tant qu'administrateur, vous pouvez toujours fusionner cette requête de pull.
 pulls.blocked_by_approvals=Cette demande d'ajout n'a pas assez d'approbation. %d sur %d approbations accordées.
 pulls.can_auto_merge_desc=Cette demande d'ajout peut être fusionnée automatiquement.
 pulls.cannot_auto_merge_desc=Cette demande de fusion ne peut être appliquée automatiquement en raison de conflits de fusion.
@@ -980,6 +999,7 @@ pulls.cannot_auto_merge_helper=Fusionner manuellement pour résoudre les conflit
 pulls.no_merge_desc=Cette demande de fusion ne peut être appliquée directement car toutes les options de fusion du dépôt sont désactivées.
 pulls.no_merge_helper=Activez des options de fusion dans les paramètres du dépôt ou fusionnez la demande manuellement.
 pulls.no_merge_wip=Cette demande d'ajout ne peut pas être fusionnée car elle est marquée comme en cours de chantier.
+pulls.no_merge_status_check=Cette demande de pull ne peut pas être fusionnée car tous les contrôles de statut requis ne sont pas réussis.
 pulls.merge_pull_request=Fusionner la demande d'ajout
 pulls.rebase_merge_pull_request=Rebase et fusionner
 pulls.rebase_merge_commit_pull_request=Rebase et Fusion (--no-ff)
@@ -1121,6 +1141,7 @@ settings.collaboration=Collaborateurs
 settings.collaboration.admin=Administrateur
 settings.collaboration.write=Écriture
 settings.collaboration.read=Lecture
+settings.collaboration.owner=Propriétaire
 settings.collaboration.undefined=Indéfini
 settings.hooks=Déclencheurs Web
 settings.githooks=Déclencheurs Git
@@ -1129,7 +1150,9 @@ settings.mirror_settings=Réglages Miroir
 settings.sync_mirror=Synchroniser maintenant
 settings.mirror_sync_in_progress=La synchronisation est en cours. Revenez dans une minute.
 settings.email_notifications.enable=Activer les notifications par e-mail
+settings.email_notifications.onmention=N'envoyer un e-mail que si vous êtes mentionné
 settings.email_notifications.disable=Désactiver les notifications par e-mail
+settings.email_notifications.submit=Définir la préférence e-mail
 settings.site=Site Web
 settings.update_settings=Valider
 settings.advanced_settings=Paramètres avancés
@@ -1200,6 +1223,11 @@ settings.collaborator_deletion_desc=La suppression d'un collaborateur révoque s
 settings.remove_collaborator_success=Le collaborateur a été retiré.
 settings.search_user_placeholder=Rechercher un utilisateur…
 settings.org_not_allowed_to_be_collaborator=Les organisations ne peuvent être ajoutées en tant que collaborateur.
+settings.change_team_access_not_allowed=La modification de l'accès de l'équipe au dépôt a été limitée au propriétaire de l'organisation
+settings.team_not_in_organization=L'équipe n'est pas dans la même organisation que le dépôt
+settings.add_team_duplicate=L'équipe a déjà le dépôt
+settings.add_team_success=L'équipe a maintenant accès au dépôt.
+settings.remove_team_success=L'accès de l'équipe au dépôt a été supprimé.
 settings.add_webhook=Ajouter un déclencheur Web
 settings.add_webhook.invalid_channel_name=Le nom du canal Webhook ne peut pas être vide et ne peut pas contenir seulement un caractère #.
 settings.hooks_desc=Les Webhooks font automatiquement des requêtes HTTP POST à un serveur lorsque certains événements Gitea se déclenchent. Lire la suite dans le <a target="_blank" rel="noopener noreferrer" href="%s">guide des Webhooks</a>.
@@ -1249,6 +1277,8 @@ settings.event_pull_request=Demande d'ajout
 settings.event_pull_request_desc=Demande d'ajout ouverte, fermée, réouverte, éditée, approuvée, rejetée, commentaire revu, assigné, non attribué, étiquette mise à jour, effacée ou synchronisée.
 settings.event_push=Pousser
 settings.event_push_desc=Git push vers un dépôt.
+settings.branch_filter=Filtre de branche
+settings.branch_filter_desc=Liste blanche pour les évènements de poussage, de création et de suppression de branche, spécifiés par un motif de développement. Si ce champ est vide ou vaut <code>*</code>, ces évènements sont rapportés pour toutes les branches . Voir la documentation pour la syntaxe sur <a href="https://godoc.org/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a>. Exemples : <code>master</code>, <code>{master,release*}</code>.
 settings.event_repository=Dépôt
 settings.event_repository_desc=Dépôt créé ou supprimé.
 settings.active=Actif
@@ -1299,6 +1329,7 @@ settings.protect_merge_whitelist_committers=Activer la liste blanche pour la fus
 settings.protect_merge_whitelist_committers_desc=N'autoriser que les utilisateurs et les équipes en liste blanche d'appliquer les demandes de fusion sur cette branche.
 settings.protect_merge_whitelist_users=Utilisateurs en liste blanche de fusion :
 settings.protect_merge_whitelist_teams=Équipes en liste blanche de fusion :
+settings.protect_check_status_contexts=Activer le Contrôle Qualité
 settings.protect_required_approvals=Agréments nécessaires :
 settings.protect_required_approvals_desc=N'autoriser la fusion qu'avec suffisamment de revues positives d'utilisateurs ou équipes sur liste blanche.
 settings.protect_approvals_whitelist_users=Réviseurs sur liste blanche :
@@ -1335,7 +1366,6 @@ diff.parent=Parent
 diff.commit=révision
 diff.git-notes=Notes
 diff.data_not_available=Contenu de la comparaison indisponible
-diff.show_diff_stats=Afficher les stats Diff
 diff.show_split_view=Vue séparée
 diff.show_unified_view=Vue unifiée
 diff.whitespace_button=Espace
diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini
index 05a83a8b07..2d90909d1e 100644
--- a/options/locale/locale_hu-HU.ini
+++ b/options/locale/locale_hu-HU.ini
@@ -1,4 +1,3 @@
-
 home=Főoldal
 dashboard=Műszerfal
 explore=Felfedezés
@@ -50,6 +49,8 @@ issues=Hibajegyek
 cancel=Mégse
 
 
+[startpage]
+
 [install]
 install=Telepítés
 db_title=Adatbázis beállítások
@@ -612,7 +613,6 @@ diff.browse_source=Forráskód böngészése
 diff.parent=szülő
 diff.commit=commit
 diff.data_not_available=A különbségek nem megjeleníthetőek
-diff.show_diff_stats=Különbségstatisztika
 diff.show_split_view=Osztott nézet
 diff.show_unified_view=Egyesített nézet
 diff.stats_desc=<strong>%d fájl</strong> változott, egészen pontosan <strong>%d új sor hozzáadva</strong> és <strong>%d régi sor törölve</strong>
diff --git a/options/locale/locale_id-ID.ini b/options/locale/locale_id-ID.ini
index b7690f97b7..bd7d0eeb63 100644
--- a/options/locale/locale_id-ID.ini
+++ b/options/locale/locale_id-ID.ini
@@ -1,5 +1,3 @@
-app_desc=Sebuah layanan Git hosting pribadi yang mudah
-
 home=Beranda
 dashboard=Dasbor
 explore=Jelajahi
@@ -64,6 +62,8 @@ issues=Masalah
 cancel=Batal
 
 
+[startpage]
+
 [install]
 install=Pemasangan
 title=Konfigurasi Awal
@@ -661,7 +661,6 @@ diff.browse_source=Telusuri Sumber
 diff.parent=orang tua
 diff.commit=melakukan
 diff.data_not_available=Konten Diff Tidak Tersedia
-diff.show_diff_stats=Menunjukkan Perbedaan Statistik
 diff.show_split_view=Tampilan split
 diff.show_unified_view=Pandangan Terpadu
 diff.stats_desc=<strong> %d mengubah file</strong> dengan <strong>%d tambahan</strong> dan <strong>%d penghapusan</strong>
diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini
index 06044101de..7829406297 100644
--- a/options/locale/locale_it-IT.ini
+++ b/options/locale/locale_it-IT.ini
@@ -1,5 +1,3 @@
-app_desc=Un servizio auto-ospitato per Git pronto all'uso
-
 home=Home
 dashboard=Pannello di controllo
 explore=Esplora
@@ -64,6 +62,8 @@ issues=Problemi
 cancel=Annulla
 
 
+[startpage]
+
 [install]
 install=Installazione
 title=Configurazione Iniziale
@@ -1055,7 +1055,6 @@ diff.browse_source=Sfoglia il codice sorgente
 diff.parent=parent
 diff.commit=commit
 diff.data_not_available=Dati Diff non disponibili
-diff.show_diff_stats=Mostra Diff Stats
 diff.show_split_view=Visualizzazione separata
 diff.show_unified_view=Visualizzazione unificata
 diff.stats_desc=<strong>%d ha cambiato i file</strong> con <strong>%d aggiunte</strong> e <strong>%d eliminazioni</strong>
diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini
index ca38757e1c..f903f543e1 100644
--- a/options/locale/locale_ja-JP.ini
+++ b/options/locale/locale_ja-JP.ini
@@ -1,5 +1,3 @@
-app_desc=痛みのない、自己ホスト型のGitサービス
-
 home=ホーム
 dashboard=ダッシュボード
 explore=エクスプローラー
@@ -75,6 +73,17 @@ write=書き込み
 preview=プレビュー
 loading=読み込み中…
 
+[startpage]
+app_desc=痛みのない、自己ホスト型のGitサービス
+install=簡単インストール
+install_desc=プラットフォームにあわせて<a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-binary/">バイナリを実行する</a>だけ。 <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a>や<a target="_blank" rel="noopener noreferrer" href="https://github.com/alvaroaleman/ansible-gitea/blob/master/Vagrantfile">Vagrant</a>でGiteaを<ruby>出荷<rp>(</rp><rt>シップ</rt><rp>)</rp></ruby>することも、<a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-package/">パッケージ</a>で入手することもできます。
+platform=クロスプラットフォーム
+platform_desc=Giteaは<a target="_blank" rel="noopener noreferrer" href="http://golang.org/">Go</a>でコンパイルできる環境ならどこでも動きます: Windows、macOS、Linux、ARM等々、お好みの環境でどうぞ!
+lightweight=軽量
+lightweight_desc=Giteaの最小動作要件は低く、安価なRaspberry Piでも動きます。 電力を節約しよう!
+license=オープンソース
+license_desc=Go get <a target="_blank" rel="noopener noreferrer" href="https://code.gitea.io/gitea">code.gitea.io/gitea</a>! <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea">コントリビュートして</a>私たちと一緒にこのプロジェクトをより良くしていきましょう。 コントリビューターになるのに臆することはありません!
+
 [install]
 install=インストール
 title=初期設定
@@ -250,7 +259,6 @@ openid_signin_desc=あなたのOpenID URIを入力してください。 例: htt
 disable_forgot_password_mail=アカウント回復機能は無効になっています。 サイト管理者にお問い合わせください。
 email_domain_blacklisted=あなたのメールアドレスでは登録することはできません。
 authorize_application=アプリケーションを許可
-authroize_redirect_notice=このアプリケーションを許可したときに %s にリダイレクトされます。
 authorize_application_created_by=このアプリケーションは %s が作成しました。
 authorize_application_description=アクセスを許可すると、このアプリケーションは、プライベート リポジトリや組織を含むあなたのすべてのアカウント情報に対して、アクセスと書き込みができるようになります。
 authorize_title=%s"にあなたのアカウントへのアクセスを許可しますか?
@@ -314,6 +322,7 @@ team_no_units_error=少なくともひとつのリポジトリセクションへ
 email_been_used=メールアドレスが既に使用されています。
 openid_been_used=OpenIDのアドレス '%s' は既に使用されています。
 username_password_incorrect=ユーザー名またはパスワードが間違っています。
+password_complexity=パスワードが複雑性の要件を満たしていません。
 enterred_invalid_repo_name=入力したリポジトリ名が間違っています。
 enterred_invalid_owner_name=新しいオーナーの名前が正しくありません。
 enterred_invalid_password=入力されたパスワードが間違っています。
@@ -632,6 +641,8 @@ migrate.lfs_mirror_unsupported=LFSオブジェクトのミラーはサポート
 migrate.migrate_items_options=移行元がGitHubの場合は、ユーザー名を入力すると移行オプションが表示されます。
 migrated_from=<a href="%[1]s">%[2]s</a>から移行
 migrated_from_fake=%[1]sから移行
+migrate.migrating=<b>%s</b> から移行しています ...
+migrate.migrating_failed=<b>%s</b> からの移行が失敗しました。
 
 mirror_from=ミラー元
 forked_from=フォーク元
@@ -680,6 +691,8 @@ stored_lfs=Git LFSで保管されています
 commit_graph=コミットグラフ
 blame=Blame
 normal_view=通常表示
+line=行
+lines=行
 
 editor.new_file=新規ファイル
 editor.upload_file=ファイルをアップロード
@@ -705,6 +718,7 @@ editor.delete='%s' を削除
 editor.commit_message_desc=詳細な説明を追加…
 editor.commit_directly_to_this_branch=ブランチ<strong class="branch-name">%s</strong>へ直接コミットする。
 editor.create_new_branch=<strong>新しいブランチ</strong>にコミットしてプルリクエストを作成する。
+editor.create_new_branch_np=<strong>新しいブランチ</strong>にコミットする。
 editor.propose_file_change=ファイル修正を提案
 editor.new_branch_name_desc=新しいブランチ名…
 editor.cancel=キャンセル
@@ -719,6 +733,8 @@ editor.file_editing_no_longer_exists=編集中のファイル '%s' が、もう
 editor.file_deleting_no_longer_exists=削除しようとしたファイル '%s' が、すでにリポジトリ内にありません。
 editor.file_changed_while_editing=あなたが編集を開始したあと、ファイルの内容が変更されました。 <a target="_blank" rel="noopener noreferrer" href="%s">ここをクリック</a>して何が変更されたか確認するか、<strong>もう一度"変更をコミット"をクリック</strong>して上書きします。
 editor.file_already_exists=ファイル '%s' は、このリポジトリに既に存在します。
+editor.commit_empty_file_header=空ファイルのコミット
+editor.commit_empty_file_text=コミットしようとしているファイルは空です。 続行しますか?
 editor.no_changes_to_show=表示する変更箇所はありません。
 editor.fail_to_update_file=ファイル '%s' を作成または変更できませんでした: %v
 editor.add_subdir=ディレクトリを追加…
@@ -1316,6 +1332,7 @@ settings.protect_this_branch=ブランチの保護を有効にする
 settings.protect_this_branch_desc=ブランチの削除を防ぎ、ブランチへのいかなるプッシュも無効にします。
 settings.protect_whitelist_committers=プッシュ・ホワイトリストを有効にする
 settings.protect_whitelist_committers_desc=ホワイトリストに登録したユーザーまたはチームに、このブランチへのプッシュを許可します。(強制プッシュ以外)
+settings.protect_whitelist_deploy_keys=プッシュ可能な書き込み権限を持つデプロイキーをホワイトリストに含める
 settings.protect_whitelist_users=プッシュ・ホワイトリストに含むユーザー:
 settings.protect_whitelist_search_users=ユーザーを検索…
 settings.protect_whitelist_teams=プッシュ・ホワイトリストに含むチーム:
@@ -1363,7 +1380,10 @@ diff.parent=親
 diff.commit=コミット
 diff.git-notes=Notes
 diff.data_not_available=差分はありません
-diff.show_diff_stats=差分情報を表示
+diff.options_button=差分オプション
+diff.show_diff_stats=統計情報を表示
+diff.download_patch=Patchファイルをダウンロード
+diff.download_diff=Diffファイルをダウンロード
 diff.show_split_view=分割表示
 diff.show_unified_view=ユニファイド表示
 diff.whitespace_button=空白
@@ -1446,6 +1466,8 @@ branch.restore_failed=ブランチ '%s' の復元に失敗しました。
 branch.protected_deletion_failed=ブランチ '%s' は保護されています。 削除できません。
 branch.restore=ブランチ '%s' の復元
 branch.download=ブランチ '%s' をダウンロード
+branch.included_desc=このブランチはデフォルトブランチに含まれています
+branch.included=埋没
 
 topic.manage_topics=トピックの管理
 topic.done=完了
@@ -1962,12 +1984,15 @@ mark_as_unread=未読にする
 mark_all_as_read=すべて既読にする
 
 [gpg]
+default_key=デフォルト鍵で署名
 error.extract_sign=署名の抽出に失敗しました
 error.generate_hash=コミットのハッシュ生成に失敗しました
 error.no_committer_account=コミッターのメールアドレスに関連付けられたアカウントが存在しません
 error.no_gpg_keys_found=この署名に対応する既知のキーがデータベースに存在しません
 error.not_signed_commit=署名されたコミットではありません
 error.failed_retrieval_gpg_keys=コミッターのアカウントに登録されたキーを取得できませんでした
+error.probable_bad_signature=警告! このIDの鍵はデータベースに登録されていますが、その鍵でコミットの検証が通りません! これは疑わしいコミットです。
+error.probable_bad_default_signature=警告! これはデフォルト鍵のIDですが、デフォルト鍵ではコミットの検証が通りません! これは疑わしいコミットです。
 
 [units]
 error.no_unit_allowed_repo=このリポジトリのどのセクションにもアクセスが許可されていません。
diff --git a/options/locale/locale_ko-KR.ini b/options/locale/locale_ko-KR.ini
index fe2f0c7fc2..f0ce4cdb30 100644
--- a/options/locale/locale_ko-KR.ini
+++ b/options/locale/locale_ko-KR.ini
@@ -1,4 +1,3 @@
-
 home=홈
 dashboard=대시보드
 explore=탐색
@@ -45,6 +44,8 @@ issues=이슈들
 cancel=취소
 
 
+[startpage]
+
 [install]
 install=설치
 db_title=데이터베이스 설정
@@ -496,7 +497,6 @@ diff.browse_source=소스 검색
 diff.parent=부모
 diff.commit=커밋
 diff.data_not_available=변경 데이터를 사용할 수 없습니다.
-diff.show_diff_stats=변경상태 보기
 diff.show_split_view=분할 보기
 diff.show_unified_view=통합 보기
 diff.stats_desc=<strong>%d개의 변경된 파일</strong>과 <strong>%d개의 추가작업</strong> 그리고 <strong>%d개의 파일을 삭제</strong>
diff --git a/options/locale/locale_lt-LT.ini b/options/locale/locale_lt-LT.ini
index b0597cd1c3..b23bf5808d 100644
--- a/options/locale/locale_lt-LT.ini
+++ b/options/locale/locale_lt-LT.ini
@@ -1,4 +1,3 @@
-
 home=Pagrindinis
 dashboard=Skydelis
 explore=Naršyti
@@ -38,6 +37,8 @@ issues=Problemos
 cancel=Atšaukti
 
 
+[startpage]
+
 [install]
 install=Diegimas
 db_title=Duombazės nustatymai
diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini
index ff930d8da7..96fe30a7a4 100644
--- a/options/locale/locale_lv-LV.ini
+++ b/options/locale/locale_lv-LV.ini
@@ -1,5 +1,3 @@
-app_desc=Viegli uzstādāms Git serviss
-
 home=Sākums
 dashboard=Infopanelis
 explore=Izpētīt
@@ -75,6 +73,8 @@ write=Rakstīt
 preview=Priekšskatītījums
 loading=Notiek ielāde…
 
+[startpage]
+
 [install]
 install=Instalācija
 title=Sākotnējā konfigurācija
@@ -250,7 +250,6 @@ openid_signin_desc=Ievadiet savu OpenID URI, piemēram: https://anna.me, peteris
 disable_forgot_password_mail=Paroles atjaunošanas iespēja ir atslēgta. Sazinieties ar lapas administratoru.
 email_domain_blacklisted=Nav atļauts reģistrēties ar šādu e-pasta adresi.
 authorize_application=Autorizēt lietotni
-authroize_redirect_notice=Jūs tiksiet nosūtīts uz %s, ja autorizēsiet šo lietotni.
 authorize_application_created_by=Šo lietotni izveidoja %s.
 authorize_application_description=Ja piešķirsiet tiesības, tā varēs piekļūt un mainīt Jūsu konta informāciju, ieskaitot privātos repozitorijus un organizācijas.
 authorize_title=Autorizēt "%s" piekļuvi jūsu kontam?
@@ -299,6 +298,7 @@ max_size_error=` jabūt ne mazāk kā %s simbolu garumā.`
 email_error=` nav derīga e-pasta adrese.`
 url_error=` nav korekts URL.`
 include_error=` ir jāsatur tekstu '%s'.`
+glob_pattern_error=` glob izteiksme nav korekta: %s.`
 unknown_error=Nezināma kļūda:
 captcha_incorrect=Ievadīts nepareizs drošības kods.
 password_not_match=Izvēlētā parole nesakrīt ar atkārtoti ievadīto.
@@ -317,6 +317,7 @@ enterred_invalid_repo_name=Pārliecinieties, vai ievadītā repozitorija nosauku
 enterred_invalid_owner_name=Pārliecinieties, vai ievadītā īpašnieka vārds ir pareizs.
 enterred_invalid_password=Pārliecinieties, vai ievadītā parole ir pareiza.
 user_not_exist=Lietotājs neeksistē.
+team_not_exist=Komanda neeksistē.
 last_org_owner=Nevar noņemt pēdējo īpašnieku komandas lietotāju, jo organizācijām ir jābūt vismaz vienam īpašniekam.
 cannot_add_org_to_team=Organizāciju nevar pievienot kā komandas biedru.
 
@@ -577,6 +578,8 @@ fork_visibility_helper=Atdalītam repozitorijam nav iespējams mainīt tā redza
 repo_desc=Apraksts
 repo_lang=Valoda
 repo_gitignore_helper=Izvēlieties .gitignore sagatavi.
+issue_labels=Problēmu etiķetes
+issue_labels_helper=Izvēlieties problēmu etiķešu kopu.
 license=Licence
 license_helper=Izvēlieties licences failu.
 readme=LASIMANI
@@ -628,6 +631,8 @@ migrate.lfs_mirror_unsupported=LFS objektu spoguļošana netiek atbalstīta - t
 migrate.migrate_items_options=Pārņemot datus no GitHub, ievadiet lietotāja vārdu, lai redzētu papildus iestatījumus.
 migrated_from=Migrēts no <a href="%[1]s">%[2]s</a>
 migrated_from_fake=Migrēts no %[1]s
+migrate.migrating=Migrācija no <b>%s</b> ...
+migrate.migrating_failed=Migrācija no <b>%s</b> neizdevās.
 
 mirror_from=spogulis no
 forked_from=atdalīts no
@@ -676,6 +681,8 @@ stored_lfs=Saglabāts Git LFS
 commit_graph=Revīziju grafs
 blame=Vainot
 normal_view=Parastais skats
+line=rinda
+lines=rindas
 
 editor.new_file=Jauna datne
 editor.upload_file=Augšupielādēt failu
@@ -701,6 +708,7 @@ editor.delete=Dzēst '%s'
 editor.commit_message_desc=Pievienot neobligātu paplašinātu aprakstu…
 editor.commit_directly_to_this_branch=Apstiprināt revīzijas izmaiņas atzarā <strong class="branch-name">%s</strong>.
 editor.create_new_branch=Izveidot <strong>jaunu atzaru</strong> un izmaiņu pieprasījumu šai revīzijai.
+editor.create_new_branch_np=Izveidot <strong>jaunu atzaru</strong> šai revīzijai.
 editor.propose_file_change=Ieteikt faila izmaiņas
 editor.new_branch_name_desc=Jaunā atzara nosaukums…
 editor.cancel=Atcelt
@@ -832,6 +840,10 @@ issues.create_comment=Komentēt
 issues.closed_at=`aizvērts <a id="%[1]s" href="#%[1]s">%[2]s</a>`
 issues.reopened_at=`atvērts atkārtoti <a id="%[1]s" href="#%[1]s">%[2]s</a>`
 issues.commit_ref_at=`pieminēja šo problēmu revīzijā <a id="%[1]s" href="#%[1]s">%[2]s</a>`
+issues.ref_issue_at=`atsaucās uz šo problēmu %[1]s`
+issues.ref_pull_at=`atsaucās uz šo izmaiņu pieprasījumu %[1]s`
+issues.ref_issue_ext_at=`atsaucās uz šo problēmu no %[1]s %[2]s`
+issues.ref_pull_ext_at=`atsaucās uz šo izmaiņu pieprasījumu no %[1]s %[2]s`
 issues.poster=Autors
 issues.collaborator=Līdzstrādnieks
 issues.owner=Īpašnieks
@@ -977,6 +989,8 @@ pulls.cannot_merge_work_in_progress=Šis izmaiņu pieprasījums ir atzīmēts, k
 pulls.data_broken=Izmaiņu pieprasījums ir bojāts, jo dzēsta informācija no atdalītā repozitorija.
 pulls.files_conflicted=Šīs izmaiņu pieprasījuma izmaiņas konfliktē ar mērķa atzaru.
 pulls.is_checking=Notiek konfliktu pārbaude, mirkli uzgaidiet un atjaunojiet lapu.
+pulls.required_status_check_failed=Dažas no pārbaudēm nebija veiksmīgas.
+pulls.required_status_check_administrator=Kā administrators Jūs varat sapludināt šo izmaiņu pieprasījumu.
 pulls.blocked_by_approvals=Šim izmaiņu pieprasījumam nav nepieciešamais apstiprinājumu daudzums. %d no %d apstiprinājumi piešķirti.
 pulls.can_auto_merge_desc=Šo izmaiņu pieprasījumu var automātiski sapludināt.
 pulls.cannot_auto_merge_desc=Šis izmaiņu pieprasījums nevar tikt automātiski sapludināts konfliktu dēļ.
@@ -984,6 +998,7 @@ pulls.cannot_auto_merge_helper=Sapludiniet manuāli, lai atrisinātu konfliktus.
 pulls.no_merge_desc=Šo izmaiņu pieprasījumu nav iespējams sapludināt, jo nav atļauts neviens sapludināšanas veids.
 pulls.no_merge_helper=Lai sapludinātu šo izmaiņu pieprasījumu, iespējojiet vismaz vienu sapludināšanas veidu repozitorija iestatījumos vai sapludiniet to manuāli.
 pulls.no_merge_wip=Šo izmaiņu pieprasījumu nav iespējams sapludināt, jo tas ir atzīmēts, ka darbs pie tā vēl nav pabeigts.
+pulls.no_merge_status_check=Šo izmaiņu pieprasījumu nevar saplusināt, jo nav veiksmīgi izildītas visas obligātas statusa pārbaudes.
 pulls.merge_pull_request=Izmaiņu pieprasījuma sapludināšana
 pulls.rebase_merge_pull_request=Pārbāzēt un sapludināt
 pulls.rebase_merge_commit_pull_request=Pārbāzēt un sapludināt (--no-ff)
@@ -1125,6 +1140,7 @@ settings.collaboration=Līdzstrādnieks
 settings.collaboration.admin=Administrators
 settings.collaboration.write=Rakstīšanas
 settings.collaboration.read=Skatīšanās
+settings.collaboration.owner=Īpašnieks
 settings.collaboration.undefined=Nedefinētas
 settings.hooks=Tīmekļa āķi
 settings.githooks=Git āķi
@@ -1206,6 +1222,11 @@ settings.collaborator_deletion_desc=Noņemot līdzstrādnieku, tam tiks liegta p
 settings.remove_collaborator_success=Līdzstrādnieks tika noņemts.
 settings.search_user_placeholder=Meklēt lietotāju…
 settings.org_not_allowed_to_be_collaborator=Organizācijas nevar tikt pievienotas kā līdzstrādnieki.
+settings.change_team_access_not_allowed=Iespēja mainīt komandu piekļuvi repozitorijam ir organizācijas īpašniekam
+settings.team_not_in_organization=Komanda nav tajā pašā organizācijā kā repozitorijs
+settings.add_team_duplicate=Komandai jau ir piekļuve šim repozitorijam
+settings.add_team_success=Komandai tagad ir piekļuve šim repozitorijam.
+settings.remove_team_success=Komandas piekļuve šim repozitorijam ir noņemta.
 settings.add_webhook=Pievienot tīmekļa āķi
 settings.add_webhook.invalid_channel_name=Tīmekļa āķa kanāla nosaukums nevar būt tukšs vai saturēt tikai # simbolu.
 settings.hooks_desc=Tīmekļa āķi ļauj paziņot ārējiem servisiem par noteiktiem notikumiem, kas notiek Gitea. Kad iestāsies kāds notikums, katram ārējā servisa URL tiks nosūtīts POST pieprasījums. Lai uzzinātu sīkāk skatieties <a target="_blank" rel="noopener noreferrer" href="%s">tīmekļa āķu rokasgrāmatā</a>.
@@ -1255,6 +1276,8 @@ settings.event_pull_request=Izmaiņu pieprasījums
 settings.event_pull_request_desc=Izmaiņu pieprasījums izveidots, slēgts, atkārtoti atvērts, labots, apstiprināts, noraidīts, recenzēts, piešķirts, pievienots vai noņemts atbildīgais, pievienota etiķete, noņemta etiķete, pievienots vai noņemts atskaites punkts.
 settings.event_push=Izmaiņu nosūtīšana
 settings.event_push_desc=Git izmaiņu nosūtīšana uz repozitoriju.
+settings.branch_filter=Atzaru filtrs
+settings.branch_filter_desc=Atzaru ierobežojumi izmaiņu iesūtīšanas, zaru izveidošanas vai dzēšanas notikumien, izmantojot, glob izteiksmi. Ja norādīts tukšs vai <code>*</code>, notikumi uz visiem zariem tiks nosūtīti. Skatieties <a href="https://godoc.org/github.com/gobwas/glob#Compile">github.com/gobwas/glob</a> pieraksta dokumentāciju. Piemērs: <code>master</code>, <code>{master,release*}</code>.
 settings.event_repository=Repozitorijs
 settings.event_repository_desc=Repozitorijs izveidots vai dzēsts.
 settings.active=Aktīvs
@@ -1305,6 +1328,9 @@ settings.protect_merge_whitelist_committers=Iespējot sapludināšanas ierobežo
 settings.protect_merge_whitelist_committers_desc=Atļaut tikai noteiktiem lietotājiem vai komandām sapludināt izmaiņu pieprasījumus šajā atzarā.
 settings.protect_merge_whitelist_users=Lietotāji, kas var veikt izmaiņu sapludināšanu:
 settings.protect_merge_whitelist_teams=Komandas, kas var veikt izmaiņu sapludināšanu:
+settings.protect_check_status_contexts=Iespējot statusu pārbaudi
+settings.protect_check_status_contexts_desc=Nepieciešamas veiksmīgas statusa pārbaudes pirms sapludināšanas. Izvēlieties, kurām statusa pārbaudēm ir jāizpildās pirms ir iespejams tās sapludināt. Ja iespējots, tad revīzijas sākotnēji jānosūta uz atsevišķu atzaru, pēc kā var tikt saplusinātas vai tieši nosūtītas uz atzariem, kas atbildst veiksmīgām norādītajām stautsa pārbaudēm. Ja konteksts nav norādīts, pēdējai revīzijai ir jābūt veiksmīga neatkarīgi no konteksta.
+settings.protect_check_status_contexts_list=Statusu pārbaudes, kas šim repozitorijam bijušas pēdējās nedēļas laikā
 settings.protect_required_approvals=Vajadzīgi apstiprinājumi:
 settings.protect_required_approvals_desc=Atļaut tikai noteiktiem lietotājiem vai komandām sapludināt izmaiņu pieprasījumu, kam veikts noteikts daudzums pozitīvu recenziju.
 settings.protect_approvals_whitelist_users=Lietotāji, kas var veikt recenzijas:
@@ -1341,7 +1367,6 @@ diff.parent=vecāks
 diff.commit=revīzija
 diff.git-notes=Piezīmes
 diff.data_not_available=Satura salīdzināšana nav pieejama
-diff.show_diff_stats=Rādīt salīdzināšanas statistiku
 diff.show_split_view=Dalītais skats
 diff.show_unified_view=Apvienotais skats
 diff.whitespace_button=Atstarpes
@@ -1352,6 +1377,11 @@ diff.whitespace_ignore_at_eol=Ignorēt atstarpju izmaiņas rindu beigās
 diff.stats_desc=<strong>%d mainītis faili</strong> ar <strong>%d papildinājumiem</strong> un <strong>%d dzēšanām</strong>
 diff.bin=Binārs
 diff.view_file=Parādīt failu
+diff.file_before=Pirms
+diff.file_after=Pēc
+diff.file_image_width=Platums
+diff.file_image_height=Augstums
+diff.file_byte_size=Izmērs
 diff.file_suppressed=Failā izmaiņas netiks attēlotas, jo tās ir par lielu
 diff.too_many_files=Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels
 diff.comment.placeholder=Ievadiet komentāru
@@ -1454,6 +1484,8 @@ settings.options=Organizācija
 settings.full_name=Pilns vārds, uzvārds
 settings.website=Mājas lapa
 settings.location=Atrašanās vieta
+settings.permission=Tiesības
+settings.repoadminchangeteam=Repozitorija administrators var pievienot vain noņemt piekļuvi komandām
 settings.visibility=Redzamība
 settings.visibility.public=Publiska
 settings.visibility.limited=Ierobežota (redzama tikai autorizētiem lietotājiem)
@@ -1700,6 +1732,7 @@ auths.tip.google_plus=Iegūstiet OAuth2 klienta pilnvaru no Google API konsoles
 auths.tip.openid_connect=Izmantojiet OpenID pieslēgšanās atklāšanas URL (<serveris>/.well-known/openid-configuration), lai norādītu galapunktus
 auths.tip.twitter=Dodieties uz adresi https://dev.twitter.com/apps, izveidojiet aplikāciju un pārliecinieties, ka ir atzīmēts “Allow this application to be used to Sign in with Twitter”
 auths.tip.discord=Reģistrējiet jaunu aplikāciju adresē https://discordapp.com/developers/applications/me
+auths.tip.gitea=Reģistrēt jaunu OAuth2 lietojumprogrammu. Pamācību iespējams atrast https://docs.gitea.io/en-us/oauth2-provider/
 auths.edit=Labot autentifikācijas avotu
 auths.activated=Autentifikācijas avots ir atkivizēts
 auths.new_success=Jauna autentifikācija'%s' tika pievienota.
diff --git a/options/locale/locale_ml-IN.ini b/options/locale/locale_ml-IN.ini
index f2571ce9c0..35af054c15 100644
--- a/options/locale/locale_ml-IN.ini
+++ b/options/locale/locale_ml-IN.ini
@@ -6,6 +6,7 @@
 
 
 
+[startpage]
 
 [install]
 
diff --git a/options/locale/locale_nb-NO.ini b/options/locale/locale_nb-NO.ini
index 763bfec6a6..d83f5829de 100644
--- a/options/locale/locale_nb-NO.ini
+++ b/options/locale/locale_nb-NO.ini
@@ -1,5 +1,3 @@
-app_desc=En smertefri Git-tjeneste du kan kjøre selv
-
 home=Startside
 dashboard=Skrivebord
 explore=Utforsk
@@ -75,6 +73,8 @@ write=Skriv
 preview=Forhåndsvis
 loading=Laster inn…
 
+[startpage]
+
 [install]
 install=Installasjon
 title=Standard konfigurasjon
diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini
index 92ea18dd91..66f8efe031 100644
--- a/options/locale/locale_nl-NL.ini
+++ b/options/locale/locale_nl-NL.ini
@@ -1,5 +1,3 @@
-app_desc=Een eenvoudige, self-hosted Git service
-
 home=Beginscherm
 dashboard=Overzicht
 explore=Verkennen
@@ -75,6 +73,8 @@ write=Schrijf
 preview=Voorbeeld
 loading=Laden…
 
+[startpage]
+
 [install]
 install=Installatie
 title=Initiële configuratie
@@ -1034,7 +1034,6 @@ diff.browse_source=Bladeren bron
 diff.parent=bovenliggende
 diff.commit=commit
 diff.data_not_available=Diff gegevens niet beschikbaar
-diff.show_diff_stats=Toon Diff Stats
 diff.show_split_view=Zij-aan-zij weergave
 diff.show_unified_view=Gecombineerde weergave
 diff.whitespace_button=Witregel
diff --git a/options/locale/locale_nn-NO.ini b/options/locale/locale_nn-NO.ini
index f2571ce9c0..35af054c15 100644
--- a/options/locale/locale_nn-NO.ini
+++ b/options/locale/locale_nn-NO.ini
@@ -6,6 +6,7 @@
 
 
 
+[startpage]
 
 [install]
 
diff --git a/options/locale/locale_no-NO.ini b/options/locale/locale_no-NO.ini
index 79198d129b..326ee445e5 100644
--- a/options/locale/locale_no-NO.ini
+++ b/options/locale/locale_no-NO.ini
@@ -1,4 +1,3 @@
-
 home=Startside
 dashboard=Skrivebord
 explore=Utforsk
@@ -36,6 +35,8 @@ issues=Problemer
 cancel=Avbryt
 
 
+[startpage]
+
 [install]
 install=Installasjon
 db_title=Databaseinnstillinger
diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini
index 52cddf4735..cf8d8ed59c 100644
--- a/options/locale/locale_pl-PL.ini
+++ b/options/locale/locale_pl-PL.ini
@@ -1,5 +1,3 @@
-app_desc=Bezbolesna usługa Git na własnym serwerze
-
 home=Strona główna
 dashboard=Pulpit
 explore=Odkrywaj
@@ -75,6 +73,8 @@ write=Napisz
 preview=Podgląd
 loading=Ładowanie…
 
+[startpage]
+
 [install]
 install=Instalacja
 title=Wstępna konfiguracja
@@ -250,7 +250,6 @@ openid_signin_desc=Wpisz swój URI OpenID. Na przykład: https://anne.me, bob.op
 disable_forgot_password_mail=Odzyskiwanie konta jest wyłączone. Skontaktuj się z administratorem strony.
 email_domain_blacklisted=Nie możesz zarejestrować się za pomocą tego adresu e-mail.
 authorize_application=Autoryzuj aplikację
-authroize_redirect_notice=Zostaniesz przekierowany(-a) do %s, jeśli autoryzujesz tę aplikację.
 authorize_application_created_by=Ta aplikacja została stworzona przez %s.
 authorize_application_description=Jeżeli udzielisz dostępu, aplikacja uzyska dostęp z zapisem do wszystkich informacji o Twoim koncie, wraz z prywatnymi repozytoriami i organizacjami.
 authorize_title=Zezwolić "%s" na dostęp do Twojego konta?
@@ -1303,7 +1302,6 @@ diff.parent=rodzic
 diff.commit=commit
 diff.git-notes=Notatki
 diff.data_not_available=Informacje nt. zmian nie są dostępne
-diff.show_diff_stats=Pokaż statystyki zmian
 diff.show_split_view=Widok podzielony
 diff.show_unified_view=Zunifikowany widok
 diff.whitespace_button=Znaki białe
@@ -1797,7 +1795,7 @@ notices.delete_success=Powiadomienia systemu zostały usunięte.
 [action]
 create_repo=tworzy repozytorium <a href="%s">%s</a>
 rename_repo=zmienia nazwę repozytorium <code>%[1]s</code> na <a href="%[2]s">%[3]s</a>
-commit_repo=wypycha do <a href="%[1]s/src/branch/%[2]s">%[3]s</a> w<a href="%[1]s">[4]s</a>
+commit_repo=wypycha do <a href="%[1]s/src/branch/%[2]s">%[3]s</a> w<a href="%[1]s">%[4]s</a>
 create_issue=`otwiera zgłoszenie <a href="%s/issues/%s">%s#%[2]s</a>`
 close_issue=`zamyka zgłoszenie <a href="%s/issues/%s">%s#%[2]s</a>`
 reopen_issue=`ponownie otwiera zgłoszenie <a href="%s/issues/%s">%s#%[2]s</a>`
diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini
index 66c2d9fd4c..30549ba69d 100644
--- a/options/locale/locale_pt-BR.ini
+++ b/options/locale/locale_pt-BR.ini
@@ -1,5 +1,3 @@
-app_desc=Um serviço de hospedagem Git amigável
-
 home=Página inicial
 dashboard=Painel
 explore=Explorar
@@ -75,6 +73,17 @@ write=Escrever
 preview=Pré-visualização
 loading=Carregando…
 
+[startpage]
+app_desc=Um serviço de hospedagem Git amigável
+install=Fácil de instalar
+install_desc=Simplesmente <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-binary/">rode o executável</a> para o seu sistema operacional. Ou obtenha o Gitea com o <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a> ou <a target="_blank" rel="noopener noreferrer" href="https://github.com/alvaroaleman/ansible-gitea/blob/master/Vagrantfile">Vagrant</a>, ou baixe o <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-package/">pacote</a>.
+platform=Multi-plataforma
+platform_desc=Gitea roda em qualquer sistema operacional em que <a target="_blank" rel="noopener noreferrer" href="http://golang.org/">Go</a> consegue compilar: Windows, macOS, Linux, ARM, etc. Escolha qual você gosta mais!
+lightweight=Leve e rápido
+lightweight_desc=Gitea utiliza poucos recursos e consegue mesmo rodar no barato Raspberry Pi. Economize energia elétrica da sua máquina!
+license=Código aberto
+license_desc=Está tudo no <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/">GitHub</a>! Contribua e torne este projeto ainda melhor. Não tenha vergonha de contribuir!
+
 [install]
 install=Instalação
 title=Configuração inicial
@@ -250,7 +259,7 @@ openid_signin_desc=Digite a URI do seu OpenID. Por exemplo: https://anne.me, bob
 disable_forgot_password_mail=Recuperação de conta está desativada. Por favor, contate o administrador do servidor.
 email_domain_blacklisted=Você não pode se cadastrar com seu endereço de e-mail.
 authorize_application=Autorizar aplicativo
-authroize_redirect_notice=Você será redirecionado para %s se você autorizar este aplicativo.
+authorize_redirect_notice=Você será redirecionado para %s se você autorizar este aplicativo.
 authorize_application_created_by=Este aplicativo foi criado por %s.
 authorize_application_description=Se você conceder o acesso, ele será capaz de acessar e escrever em todas as informações da sua conta, incluindo repositórios privados e organizações.
 authorize_title=Autorizar "%s" para acessar sua conta?
@@ -314,6 +323,7 @@ team_no_units_error=Permitir acesso a pelo menos uma seção de repositório.
 email_been_used=Este endereço de e-mail já está sendo usado.
 openid_been_used=O endereço OpenID '%s' já está sendo usado.
 username_password_incorrect=Nome de usuário ou senha incorretos.
+password_complexity=A senha não passa nos requisitos de complexidade.
 enterred_invalid_repo_name=O nome do repositório que você digitou está incorreto.
 enterred_invalid_owner_name=O nome do novo proprietário não é válido.
 enterred_invalid_password=A senha que você digitou está incorreta.
@@ -632,6 +642,8 @@ migrate.lfs_mirror_unsupported=Espelhamento de objetos Git LFS não é suportado
 migrate.migrate_items_options=Ao migrar do github, insira um nome de usuário e as opções de migração serão exibidas.
 migrated_from=Migrado de <a href="%[1]s">%[2]s</a>
 migrated_from_fake=Migrado de %[1]s
+migrate.migrating=Migrando a partir de <b>%s</b> ...
+migrate.migrating_failed=Migração a partir de <b>%s</b> falhou.
 
 mirror_from=espelhamento de
 forked_from=feito fork de
@@ -680,6 +692,8 @@ stored_lfs=Armazenado com Git LFS
 commit_graph=Gráfico de commits
 blame=Anotar
 normal_view=Visão normal
+line=linha
+lines=linhas
 
 editor.new_file=Novo arquivo
 editor.upload_file=Enviar arquivo
@@ -705,6 +719,7 @@ editor.delete=Excluir '%s'
 editor.commit_message_desc=Adicione uma descrição detalhada (opcional)...
 editor.commit_directly_to_this_branch=Commit diretamente no branch <strong class="branch-name">%s</strong>.
 editor.create_new_branch=Crie um <strong>novo branch</strong> para este commit e crie um pull request.
+editor.create_new_branch_np=Crie um <strong>novo branch</strong> para este commit.
 editor.propose_file_change=Propor alteração de arquivo
 editor.new_branch_name_desc=Novo nome do branch...
 editor.cancel=Cancelar
@@ -719,6 +734,8 @@ editor.file_editing_no_longer_exists=O arquivo que está sendo editado, '%s', n
 editor.file_deleting_no_longer_exists=O arquivo a ser excluído, '%s', não existe mais neste repositório.
 editor.file_changed_while_editing=O conteúdo do arquivo mudou desde que você começou a editar. <a target="_blank" rel="noopener noreferrer" href="%s">Clique aqui</a> para ver o que foi editado ou <strong>clique em Aplicar commit das alterações novamemente</strong> para sobreescrever estas alterações.
 editor.file_already_exists=Um arquivo com nome '%s' já existe neste repositório.
+editor.commit_empty_file_header=Fazer commit de um arquivo vazio
+editor.commit_empty_file_text=O arquivo que você está prestes fazer commit está vazio. Continuar?
 editor.no_changes_to_show=Nenhuma alteração a mostrar.
 editor.fail_to_update_file=Houve erro ao criar ou atualizar arquivo '%s': %v
 editor.add_subdir=Adicionar um subdiretório...
@@ -784,6 +801,7 @@ issues.delete_branch_at=`excluiu branch <b>%s</b> %s`
 issues.open_tab=%d aberto
 issues.close_tab=%d fechado
 issues.filter_label=Etiqueta
+issues.filter_label_exclude=`Use <code>alt</code> + <code>clique/enter</code> para excluir etiquetas`
 issues.filter_label_no_select=Todas as etiquetas
 issues.filter_milestone=Marco
 issues.filter_milestone_no_select=Todos os marcos
@@ -1316,6 +1334,7 @@ settings.protect_this_branch=Habilitar proteção de branch
 settings.protect_this_branch_desc=Prevenir exclusão e desabilitar qualquer push neste branch.
 settings.protect_whitelist_committers=Habilitar controle de permissão de push
 settings.protect_whitelist_committers_desc=Permitir que usuários ou times realizem push neste branch (exceto push forçado).
+settings.protect_whitelist_deploy_keys=Lista de chaves de implantação com acesso de escrita para realizar push
 settings.protect_whitelist_users=Usuários com permissão para realizar push:
 settings.protect_whitelist_search_users=Pesquisar usuários...
 settings.protect_whitelist_teams=Equipes com permissão para realizar push:
@@ -1363,7 +1382,10 @@ diff.parent=pai
 diff.commit=commit
 diff.git-notes=Notas
 diff.data_not_available=Conteúdo de diff não disponível
-diff.show_diff_stats=Mostrar estatísticas do Diff
+diff.options_button=Opções de diferenças
+diff.show_diff_stats=Mostrar estatísticas
+diff.download_patch=Baixar arquivo de patch
+diff.download_diff=Baixar arquivo de diferenças
 diff.show_split_view=Visão dividida
 diff.show_unified_view=Visão unificada
 diff.whitespace_button=Espaço em branco
@@ -1446,6 +1468,8 @@ branch.restore_failed=Falha ao restaurar a branch %s.
 branch.protected_deletion_failed=A branch '%s' está protegida. Ela não pode ser excluída.
 branch.restore=Restaurar branch '%s'
 branch.download=Baixar branch '%s'
+branch.included_desc=Este branch faz parte do branch padrão
+branch.included=Incluído
 
 topic.manage_topics=Gerenciar Tópicos
 topic.done=Feito
@@ -1962,12 +1986,15 @@ mark_as_unread=Marcar como não lida
 mark_all_as_read=Marcar todas como lidas
 
 [gpg]
+default_key=Assinado com a chave padrão
 error.extract_sign=Falha ao extrair assinatura
 error.generate_hash=Falha ao gerar hash de commit
 error.no_committer_account=Nenhuma conta vinculada ao e-mail do autor do commit
 error.no_gpg_keys_found=Nenhuma chave conhecida encontrada para esta assinatura no banco de dados
 error.not_signed_commit=Não é um commit assinado
 error.failed_retrieval_gpg_keys=Falha em obter qualquer chave anexada à conta do autor do commit
+error.probable_bad_signature=AVISO! Embora exista uma chave com este ID no banco de dados, ela não verifica este commit! Este commit é SUSPEITO.
+error.probable_bad_default_signature=AVISO! Embora a chave padrão tenha este ID, ela não verifica este commit! Este commit é SUSPEITO.
 
 [units]
 error.no_unit_allowed_repo=Você não tem permissão para acessar nenhuma seção deste repositório.
diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini
index 475129ce36..9102603425 100644
--- a/options/locale/locale_ru-RU.ini
+++ b/options/locale/locale_ru-RU.ini
@@ -1,5 +1,3 @@
-app_desc=Удобная служба для собственного Git-репозитория
-
 home=Главная
 dashboard=Панель управления
 explore=Обзор
@@ -75,6 +73,8 @@ write=Редактирование
 preview=Предпросмотр
 loading=Загрузка…
 
+[startpage]
+
 [install]
 install=Установка
 title=Начальная конфигурация
@@ -245,7 +245,6 @@ openid_register_desc=Выбранный OpenID URI неизвестен. Свя
 openid_signin_desc=Введите свой OpenID URI. Например: https://anne.me, bob.openid.org.cn или gnusocial.net/carry.
 disable_forgot_password_mail=Восстановление аккаунта отключено. Пожалуйста, свяжитесь с администратором сайта.
 authorize_application=Авторизация приложения
-authroize_redirect_notice=Вы будете перенаправлены на %s, если вы авторизуете это приложение.
 authorize_application_created_by=Это приложение было создано %s.
 authorize_application_description=Если вы предоставите доступ, оно сможет получить доступ и редактировать любую информацию о вашей учетной записи, включая содержимое частных репозиториев и организаций.
 authorize_title=Разрешить «%s» доступ к вашей учетной записи?
@@ -1265,7 +1264,6 @@ diff.browse_source=Просмотр исходного кода
 diff.parent=Родитель
 diff.commit=Сommit
 diff.data_not_available=Разница недоступна
-diff.show_diff_stats=Показать статистику Diff
 diff.show_split_view=Разделённый вид
 diff.show_unified_view=Единый вид
 diff.whitespace_button=Пробелы
diff --git a/options/locale/locale_sr-SP.ini b/options/locale/locale_sr-SP.ini
index 8b0d5743fb..a4072b7dfa 100644
--- a/options/locale/locale_sr-SP.ini
+++ b/options/locale/locale_sr-SP.ini
@@ -1,4 +1,3 @@
-
 home=Почетна
 dashboard=Контролни панел
 explore=Преглед
@@ -36,6 +35,8 @@ issues=Дискусије
 cancel=Откажи
 
 
+[startpage]
+
 [install]
 install=Инсталација
 db_title=Подешавања базе
@@ -418,7 +419,6 @@ settings.deploy_key_content=Садржај
 diff.browse_source=Преглед изворни кода
 diff.parent=родитељ
 diff.commit=комит
-diff.show_diff_stats=Покажи статистику Diff
 diff.show_split_view=Подељен поглед
 diff.show_unified_view=Један поглед
 diff.stats_desc=<strong> %d измењених фајлова</strong> са <strong>%d додато</strong> и <strong>%d уклоњено</strong>
diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini
index a954fd2a9a..37d6621642 100644
--- a/options/locale/locale_sv-SE.ini
+++ b/options/locale/locale_sv-SE.ini
@@ -1,5 +1,3 @@
-app_desc=En smidig, självhostad Git-tjänst
-
 home=Startsida
 dashboard=Instrumentpanel
 explore=Utforska
@@ -71,6 +69,8 @@ cancel=Avbryt
 preview=Förhandsgranska
 loading=Laddar…
 
+[startpage]
+
 [install]
 install=Installation
 title=Ursprunglig konfiguration
@@ -1086,7 +1086,6 @@ diff.browse_source=Bläddra i källkod
 diff.parent=förälder
 diff.commit=incheckning
 diff.data_not_available=Diff Content ej tillgänglig
-diff.show_diff_stats=Visa Diff Statistik
 diff.show_split_view=Delad Vy
 diff.show_unified_view=Unifierad Vy
 diff.whitespace_show_everything=Visa alla ändringar
diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini
index 9fa1b7d42b..5100f84319 100644
--- a/options/locale/locale_tr-TR.ini
+++ b/options/locale/locale_tr-TR.ini
@@ -1,5 +1,3 @@
-app_desc=Zahmetsiz, kendi sunucunuzda barındırabileceğiniz Git hizmeti
-
 home=Ana Sayfa
 dashboard=Pano
 explore=Keşfet
@@ -66,7 +64,7 @@ forks=Çatallar
 
 activities=Aktiviteler
 pull_requests=Değişiklik İsteği
-issues=Sorunlar
+issues=Konular
 
 cancel=İptal
 
@@ -74,6 +72,16 @@ write=Yaz
 preview=Önizleme
 loading=Yükleniyor…
 
+[startpage]
+app_desc=Zahmetsiz, kendi sunucunuzda barındırabileceğiniz Git servisi
+install=Kurulumu kolay
+install_desc=Basitçe, platformunuz için uygun <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-binary/">program dosyasını çalıştırın</a>. Ya da Gitea'yı <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a> veya <a target="_blank" rel="noopener noreferrer" href="https://github.com/alvaroaleman/ansible-gitea/blob/master/Vagrantfile">Vagrant</a> ile sunun, ya da <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-package/">paketlenmiş</a> olarak edinin.
+platform=Farklı platformlarda çalışablir
+platform_desc=Gitea <a target="_blank" rel="noopener noreferrer" href="http://golang.org/">Go</a> ile derleme yapılabilecek her yerde çalışmaktadır: Windows, macOS, Linux, ARM, vb. Hangisini seviyorsanız onu seçin!
+lightweight=Hafif
+lightweight_desc=Gitea'nın minimal gereksinimleri çok düşüktür ve ucuz bir Raspberry Pi üzerinde çalışabilmektedir. Makine enerjinizden tasarruf edin!
+license=Açık Kaynak
+
 [install]
 install=Kurulum
 title=Başlangıç Yapılandırması
@@ -112,6 +120,7 @@ domain_helper=SSH kopyalama URL'leri için alan adı veya sunucu adresi.
 ssh_port=SSH Sunucu Portu
 http_port=Gitea HTTP Dinleme Portu
 http_port_helper=Gitea'nın web sunucusunun dinleyeceği port numarası.
+app_url=Gitea Kök URL
 log_root_path=Günlük Dosyaları Yolu
 log_root_path_helper=Günlük dosyaları bu dizine kaydedilecektir.
 
@@ -124,6 +133,7 @@ mailer_user=SMTP Kullanıcı Adı
 mailer_password=SMTP Parolası
 register_confirm=Kayıt için E-posta Doğrulaması Gereksin
 mail_notify=E-Posta Bildirimlerini Etkinleştir
+server_service_title=Sunucu ve Diğer Servis Ayarları
 offline_mode=Yerel Kipi Etkinleştir
 disable_gravatar=Gravatar'ı Devre Dışı Bırak
 federated_avatar_lookup_popup=Enable federated avatars lookup to use federated open source service based on libravatar.
@@ -149,6 +159,7 @@ test_git_failed='git' komut testi başarısız: %v
 sqlite3_not_available=Bu Gieta sürümü SQLite3 desteklemiyor. Lütfen %s adresinden resmi çalışır sürümü ('gobuild' sürümünü değil) indirin.
 invalid_db_setting=Veritabanı ayarları geçersiz: %v
 invalid_repo_path=Depo kök dizini geçersiz: %v
+run_user_not_match='Birlikte çalıştır' kullanıcı adı şimdiki kullanıcı adından farklıdır: %s -> %s
 save_config_failed=%v Yapılandırması kaydedilirken hata oluştu
 invalid_admin_setting=Yönetici hesap ayarları geçersiz: %v
 install_success=Hoşgeldiniz! Gitea'yı seçtiğiniz için teşekkür ederiz. Eğlenin ve kendinize iyi bakın!
@@ -157,6 +168,8 @@ default_keep_email_private=E-posta adreslerini varsayılan olarak gizle
 default_keep_email_private_popup=Yeni kullanıcı hesaplarının e-posta adreslerini varsayılan olarak gizle.
 default_allow_create_organization=Varsayılan Olarak Organizasyon Oluşturmaya İzin Ver
 default_allow_create_organization_popup=Varsayılan olarak yeni kullanıcı hesaplarının organizasyon oluşturmasına izin ver.
+default_enable_timetracking=Varsayılan Olarak Zaman Takibini Etkinleştir
+default_enable_timetracking_popup=Yeni depolar için zaman takibini varsayılan olarak etkinleştir.
 no_reply_address=Gizlenecek E-Posta Alan Adı
 no_reply_address_helper=Gizlenmiş e-posta adresine sahip kullanıcılar için alan adı. Örneğin 'ali' kullanıcı adı, gizlenmiş e-postalar için alan adı 'yanityok.ornek.org' olarak ayarlandığında Git günlüğüne 'ali@yanityok.ornek.org' olarak kaydedilecektir.
 
@@ -203,6 +216,8 @@ allow_password_change=Kullanıcıyı parola değiştirmeye zorla (önerilen)
 reset_password_mail_sent_prompt=<b>%s</b> adresine bir onay e-postası gönderildi. Hesap kurtarma işlemini tamamlamak için lütfen gelen kutunuzu sonraki %s içinde kontrol edin.
 active_your_account=Hesabınızı Aktifleştirin
 account_activated=Hesap etkinleştirildi
+prohibit_login=Oturum Açma Yasağı
+prohibit_login_desc=Hesabınız ile oturum açmanız yasaklanmış, lütfen site yöneticinizle iletişime geçin.
 resent_limit_prompt=Zaten bir doğrulama e-postası talep ettiniz. Lütfen 3 dakika bekleyip tekrar deneyin.
 has_unconfirmed_mail=Merhaba %s, doğrulanmamış bir e-posta adresin var (<b>%s</b>). Bir doğrulama e-postası almadıysanız ya da yenisine ihtiyacınız varsa lütfen aşağıdaki düğmeye tıklayın.
 resend_mail=Doğrulama e-postasını tekrar almak için buraya tıklayın
@@ -235,7 +250,6 @@ openid_register_desc=Seçilen OpenID URI'si bilinmiyor. Burada yeni bir hesapla
 disable_forgot_password_mail=Hesap kurtarma devre dışı. Lütfen site yöneticinizle iletişime geçin.
 email_domain_blacklisted=Bu e-posta adresinizle kayıt olamazsınız.
 authorize_application=Uygulamayı Yetkilendir
-authroize_redirect_notice=Bu uygulamayı yetkilendirirseniz %s adresine yönlendirileceksiniz.
 authorize_application_created_by=Bu uygulama %s tarafından oluşturuldu.
 authorize_application_description=Erişime izin verirseniz, özel depolar ve organizasyonlar da dahil olmak üzere tüm hesap bilgilerinize erişebilir ve yazabilir.
 authorize_title=Hesabınıza erişmesi için "%s" yetkilendirilsin mi?
@@ -455,11 +469,11 @@ unbind=Bağlantıyı Kaldır
 unbind_success=Sosyal hesabın bağlantısı Gitea hesabınızdan kaldırılmıştır.
 
 manage_access_token=Erişim Jetonlarını Yönet
-generate_new_token=Yeni Erişim Anahtarı Üret
+generate_new_token=Yeni Jeton Üret
 tokens_desc=Bu jetonlar Gitea API'sini kullanarak hesabınıza erişim sağlar.
 new_token_desc=Jeton kullanan uygulamalar hesabınıza tam erişime sahiptir.
-token_name=Erişim Anahtarı İsmi
-generate_token=Erişim Anahtarı Üret
+token_name=Jeton İsmi
+generate_token=Jeton Üret
 generate_token_success=Yeni bir jeton oluşturuldu. Tekrar gösterilmeyeceği için şimdi kopyalayın.
 delete_token=Sil
 access_token_deletion=Erişim Jetonunu Sil
@@ -585,7 +599,10 @@ migrate.invalid_local_path=Yerel yol geçersiz. Mevcut değil veya bir dizin de
 migrate.failed=Göç başarısız: %v
 migrate.lfs_mirror_unsupported=LFS nesnelerini yansılama desteklenmiyor - yerine 'git lfs fetch --all' ve 'git lfs push --all' kullanın.
 migrate.migrate_items_options=Github'dan göç yaparken, bir kullanıcı adı girin ve göç seçenekleri görüntülenecektir.
+migrated_from=<a href="%[1]s">%[2]s</a> konumundan taşındı
 migrated_from_fake=%[1]s Konumundan Taşındı
+migrate.migrating=<b>%s</b> konumundan taşınıyor ...
+migrate.migrating_failed=<b>%s</b> konumundan taşıma başarısız oldu.
 
 mirror_from=şunun yansıması
 forked_from=şundan çatallanmış
@@ -616,7 +633,7 @@ tree=Ağaç
 filter_branch_and_tag=Dal veya biçim imini filtrele
 branches=Dallar
 tags=Biçim İmleri
-issues=Sorunlar
+issues=Konular
 pulls=Değişiklik İstekleri
 labels=Etiketler
 milestones=Kilometre Taşları
@@ -632,13 +649,16 @@ video_not_supported_in_browser=Tarayıcınız HTML5 'video' etiketini desteklemi
 audio_not_supported_in_browser=Tarayıcınız HTML5 'audio' etiketini desteklemiyor.
 stored_lfs=Git LFS ile depolandı
 commit_graph=İşleme Grafiği
-blame=Bahset
+blame=Suçlama
 normal_view=Normal Görünüm
+line=satır
+lines=satır
 
 editor.new_file=Yeni dosya
 editor.upload_file=Dosya Yükle
 editor.edit_file=Dosyayı Düzenle
 editor.preview_changes=Değişiklikleri Önizle
+editor.cannot_edit_lfs_files=LFS dosyaları web arayüzünde düzenlenemez.
 editor.cannot_edit_non_text_files=Bu tür dosyalar web arayüzünden düzenlenemez.
 editor.edit_this_file=Dosyayı Düzenle
 editor.must_be_on_a_branch=Bu dosyada değişiklik yapmak veya önermek için bir dalda olmalısınız.
@@ -657,6 +677,7 @@ editor.delete='%s' sil
 editor.commit_message_desc=İsteğe bağlı uzun bir açıklama ekleyin…
 editor.commit_directly_to_this_branch=Doğrudan <strong class="branch-name">%s</strong> bölümüne uygula.
 editor.create_new_branch=Bu işlem için bir <strong>yeni branş</strong> oluşturun ve bir çekme istediği başlatın.
+editor.create_new_branch_np=Bu işleme için <strong>yeni bir dal</strong> oluştur.
 editor.propose_file_change=Dosya değişikliği öner
 editor.new_branch_name_desc=Yeni dal ismi…
 editor.cancel=İptal
@@ -671,6 +692,8 @@ editor.file_editing_no_longer_exists=Düzenlenmekte olan '%s' dosyası artık bu
 editor.file_deleting_no_longer_exists=Silinen '%s' dosyası bu depoda artık yer almıyor değil.
 editor.file_changed_while_editing=Düzenlemeye başladığınızdan beri dosya içeriği değişti. Görmek için <a target="_blank" rel="noopener noreferrer" href="%s">burayı tıklayın</a> veya üzerine yazmak için <strong>değişiklikleri yine de işleyin</strong>.
 editor.file_already_exists=Bu depoda '%s' isimli bir dosya zaten mevcut.
+editor.commit_empty_file_header=Boş bir dosya işle
+editor.commit_empty_file_text=İşleme yaptığınız dosya boş. Devam edilsin mi?
 editor.no_changes_to_show=Gösterilecek değişiklik yok.
 editor.fail_to_update_file=Şu hata ile '%s' dosyasını güncelleme/oluşturma başarısız oldu: %v
 editor.add_subdir=Bir dizin ekle…
@@ -696,7 +719,7 @@ ext_issues=Dışsal Konular
 ext_issues.desc=Dışsal konu takip sistemine bağla.
 
 issues.desc=Hata raporlarını, görevleri ve kilometre taşlarını yönetmenizi sağlar.
-issues.new=Yeni Sorun
+issues.new=Yeni Konu
 issues.new.title_empty=Başlık boş olamaz
 issues.new.labels=Etiketler
 issues.new.no_label=Etiket Yok
@@ -710,7 +733,7 @@ issues.new.assignees=Atananlar
 issues.new.clear_assignees=Atamaları Temizle
 issues.new.no_assignees=Atanan Kişi Yok
 issues.no_ref=Bölüm/Etiket Belirtilmedi
-issues.create=Sorun Oluştur
+issues.create=Konu Oluştur
 issues.new_label=Yeni Etiket
 issues.new_label_placeholder=Etiket adı
 issues.new_label_desc_placeholder=Açıklama
@@ -720,28 +743,29 @@ issues.label_templates.info=Henüz bir etiket yok. 'Yeni Etiket' ile bir etiket
 issues.label_templates.helper=Bir etiket seti seçin
 issues.label_templates.use=Etiket Kümesi Kullan
 issues.label_templates.fail_to_load_file=Etiket şablon dosyası yüklemesi başarısız oldu '%s':%v
-issues.add_label_at=<div class="ui label has-emoji" style="color: %s\; background-color: %s">%s</div> etiketi eklendi %s
+issues.add_label_at= %[4]s <div class="ui label has-emoji" style="color: %[1]s\; background-color: %[2]s">%[3]s</div> etiketini eklendi
 issues.remove_label_at=<div class="ui label has-emoji" style="color: %s\; background-color: %s">%s</div> etiketi silindi %s
-issues.add_milestone_at=`bu <b>%s</b> yol taşına eklendi %s`
+issues.add_milestone_at=`%[2]s <b>%[1]s</b> kilometre taşına ekledi`
 issues.change_milestone_at=` <b>%s</b>den<b>%s</b>ye yol taşı düzenlendi %s`
 issues.remove_milestone_at=`bu dosya <b>%s</b> yol taşından kaldırıldı %s`
 issues.deleted_milestone=`(silindi)`
-issues.self_assign_at=`kendiliğinden atanmış bu %s`
-issues.add_assignee_at=`<b>%s</b> tarafından atandı %s`
-issues.remove_assignee_at=`<b>%s</b> tarafından atama kaldırıldı %s`
+issues.self_assign_at=`%s kendini atadı`
+issues.add_assignee_at=`%[2]s <b>%[1]s</b> tarafından atandı`
+issues.remove_assignee_at=`ataması %[2]s <b>%[1]s</b> tarafından kaldırıldı`
 issues.remove_self_assignment=`atamalarını kaldırdı %s`
 issues.change_title_at=`başlık <b><strike>%s</strike></b> iken <b>%s</b> olarak değiştirildi %s`
 issues.delete_branch_at=`branş <b>%s</b> silindi %s`
 issues.open_tab=%d açık
 issues.close_tab=%d kapanmış
 issues.filter_label=Etiket
+issues.filter_label_exclude=`Etiketleri hariç tutmak için <code>alt</code> + <code>tıkla/enter</code> kullanın`
 issues.filter_label_no_select=Tüm etiketler
 issues.filter_milestone=Kilometre Taşı
 issues.filter_milestone_no_select=Tüm kilometre taşları
 issues.filter_assignee=Atanan
 issues.filter_assginee_no_select=Tüm atananlar
 issues.filter_type=Tür
-issues.filter_type.all_issues=Tüm Sorunlar
+issues.filter_type.all_issues=Tüm Konular
 issues.filter_type.assigned_to_you=Size atanan
 issues.filter_type.created_by_you=Sizin oluşturduklarınız
 issues.filter_type.mentioning_you=Sizden bahsedilen
@@ -766,6 +790,8 @@ issues.action_milestone_no_select=Kilometre Taşı Yok
 issues.action_assignee=Vekil
 issues.action_assignee_no_select=Vekil yok
 issues.opened_by=<a href="%[2]s">%[3]s</a> tarafından %[1]s açıldı
+pulls.merged_by=<a href="%[2]s">%[3]s</a> tarafından %[1]s birleştirildi
+pulls.merged_by_fake=%[2]s tarafından %[1]s birleştirildi
 issues.closed_by=<a href="%[2]s">%[3]s</a> tarafından %[1]s kapatıldı
 issues.opened_by_fake=%[2]s tarafından %[1]s açıldı
 issues.closed_by_fake=%[2]s tarafından %[1]s kapatıldı
@@ -774,7 +800,7 @@ issues.next=Sonraki
 issues.open_title=Açık
 issues.closed_title=Kapalı
 issues.num_comments=%d yorum
-issues.commented_at=`<a href="#%s">%s</a> olarak yorumlandı`
+issues.commented_at=`<a href="#%s">%s</a> yorum yaptı`
 issues.delete_comment_confirm=Bu yorumu silmek istediğinizden emin misiniz?
 issues.no_content=Henüz bir içerik yok.
 issues.close_issue=Kapat
@@ -784,11 +810,11 @@ issues.reopen_comment_issue=Yorum Yap ve Yeniden Aç
 issues.create_comment=Yorum yap
 issues.closed_at=`<a id="%[1]s" href="#%[1]s">%[2]s</a> kapattı`
 issues.reopened_at=`<a id="%[1]s" href="#%[1]s">%[2]s</a> yeniden açtı`
-issues.commit_ref_at=`<a id="%[1]s" href="#%[1]s">%[2]s</a> işlemesinde bu sorunu işaret etti`
-issues.ref_issue_at=`bu konudan bahsetti %[1]s`
-issues.ref_pull_at=`bu değişiklik isteğinden bahsetti %[1]s`
-issues.ref_issue_ext_at=`%[1]s'den bu konuya değinildi %[2]s`
-issues.ref_pull_ext_at=`%[1]s'den bu değişiklik isteğine değinildi %[2]s`
+issues.commit_ref_at=`<a id="%[1]s" href="#%[1]s">%[2]s</a> işlemesinde bu konuyu işaret etti`
+issues.ref_issue_at=`%[1]s bu konudan bahsetti`
+issues.ref_pull_at=`%[1]s bu değişiklik isteğinden bahsetti`
+issues.ref_issue_ext_at=`%[2]s %[1]s'den bu konuya değinildi`
+issues.ref_pull_ext_at=`%[2]s %[1]s'den bu değişiklik isteğine değinildi`
 issues.poster=Poster
 issues.collaborator=Katkıcı
 issues.owner=Sahibi
@@ -800,7 +826,7 @@ issues.label_title=Etiket adı
 issues.label_description=Etiket açıklaması
 issues.label_color=Etiket rengi
 issues.label_count=%d etiket
-issues.label_open_issues=%d açık sorun
+issues.label_open_issues=%d açık konu
 issues.label_edit=Düzenle
 issues.label_delete=Sil
 issues.label_modify=Etiketi Düzenle
@@ -835,11 +861,15 @@ issues.lock.reason=Kilitleme nedeni
 issues.lock.title=Konuşmayı kilitle.
 issues.unlock.title=Konuşmanın kilidini aç.
 issues.comment_on_locked=Kilitli bir konuya yorum yapamazsınız.
+issues.tracker=Zaman İzleyici
 issues.start_tracking_short=Başlat
+issues.start_tracking=Zaman İzlemeyi Başlat
 issues.start_tracking_history=`%s çalışması başlatıldı`
+issues.tracker_auto_close=Bu konu kapatıldığında zamanlayıcı otomatik olarak durur
 issues.tracking_already_started=`Bu <a href="%s">konuda</a> zaten zaman izleyicisini başlattınız!`
 issues.stop_tracking=Durdur
 issues.stop_tracking_history=`%s çalışması durduruldu`
+issues.add_time=El ile Zaman Ekle
 issues.add_time_short=Zaman Ekle
 issues.add_time_cancel=İptal
 issues.add_time_history=`%s harcanan zaman eklendi`
@@ -860,6 +890,10 @@ issues.due_date_form_edit=Düzenle
 issues.due_date_form_remove=Kaldır
 issues.due_date_not_writer=Bir konunun bitiş tarihini değiştirmek için depoda yazma hakkınız olmalıdır.
 issues.due_date_not_set=Bitiş tarihi atanmadı.
+issues.due_date_added=%[2]s %[1]s bitiş tarihini ekledi
+issues.due_date_modified=%[3]s bitiş tarihi %[2]s iken %[1]s olarak değiştirildi
+issues.due_date_remove=%[2]s %[1]s bitiş tarihini kaldırdı
+issues.due_date_invalid=Bitiş tarihi geçersiz veya aralık dışında. Lütfen 'yyyy-aa-gg' biçimini kullanın.
 issues.dependency.title=Bağımlılıklar
 issues.dependency.issue_no_dependencies=Bu konu henüz bir bağımlılık içermiyor.
 issues.dependency.pr_no_dependencies=Bu çekme isteği henüz bir bağımlılık içermiyor.
@@ -867,29 +901,32 @@ issues.dependency.add=Bağımlılık ekle…
 issues.dependency.cancel=İptal
 issues.dependency.remove=Kaldır
 issues.dependency.remove_info=Bu bağımlılığı kaldır
-issues.dependency.added_dependency=`<a href="%[1]s">%[2]s</a> yeni bir bağımlık ekledi %[3]s`
-issues.dependency.removed_dependency=`<a href="%[1]s">%[2]s</a> bir bağımlılığı kaldırdı %[3]s`
-issues.dependency.issue_closing_blockedby=Bu değişiklik isteğinin kapatılması aşağıdaki sorunlar nedeniyle engelleniyor
-issues.dependency.pr_closing_blockedby=Bu sorunun kapatılması aşağıdaki sorunlar nedeniyle engelleniyor
-issues.dependency.issue_close_blocks=Bu sorun aşağıdaki sorunların kapatılmasını engelliyor
+issues.dependency.added_dependency=`<a href="%[1]s">%[2]s</a> %[3]s yeni bir bağımlık ekledi`
+issues.dependency.removed_dependency=`<a href="%[1]s">%[2]s</a> %[3]s bir bağımlılığı kaldırdı`
+issues.dependency.issue_closing_blockedby=Bu değişiklik isteğinin kapatılması aşağıdaki konular nedeniyle engelleniyor
+issues.dependency.pr_closing_blockedby=Bu konunun kapatılması aşağıdaki konular tarafından engelleniyor
+issues.dependency.issue_close_blocks=Bu konu aşağıdaki konuların kapatılmasını engelliyor
 issues.dependency.pr_close_blocks=Bu değişiklik isteği aşağıdaki sorunların kapatılmasını engelliyor
-issues.dependency.issue_close_blocked=Kapatmadan önce bu sorunu engelleyen tüm sorunları kapatmanız gerekir.
-issues.dependency.pr_close_blocked=Birleştirme işleminden önce, bu değişiklik isteğini engelleyen tüm sorunları kapatmanız gerekir.
+issues.dependency.issue_close_blocked=Kapatmadan önce bu konuyu engelleyen tüm konuları kapatmanız gerekir.
+issues.dependency.pr_close_blocked=Birleştirme işleminden önce, bu değişiklik isteğini engelleyen tüm konuları kapatmanız gerekir.
 issues.dependency.blocks_short=Engeller
 issues.dependency.blocked_by_short=Bağımlılıklar
 issues.dependency.remove_header=Bağımlılığı Kaldır
 issues.dependency.issue_remove_text=Bu işlem, bu konudaki bağımlılığı kaldıracaktır. Devam edilsin mi?
 issues.dependency.pr_remove_text=Bu işlem, bu değişiklik isteğindeki bağımlılığı kaldıracaktır. Devam edilsin mi?
 issues.dependency.setting=Konular ve Değişiklik İstekleri İçin Bağımlılıkları Etkinleştir
-issues.dependency.add_error_same_issue=Bir sorunu kendine bağımlı yapamazsınız.
-issues.dependency.add_error_dep_issue_not_exist=Bağımlı sorun mevcut değil.
+issues.dependency.add_error_same_issue=Bir konuyu kendine bağımlı yapamazsınız.
+issues.dependency.add_error_dep_issue_not_exist=Bağımlı konu mevcut değil.
 issues.dependency.add_error_dep_not_exist=Bağımlılık mevcut değil.
 issues.dependency.add_error_dep_exists=Bağımlılık zaten var.
-issues.dependency.add_error_cannot_create_circular=Birbirini engelleyen iki sorunla bir bağımlılık oluşturamazsınız.
+issues.dependency.add_error_cannot_create_circular=Birbirini engelleyen iki konu arasında bağımlılık oluşturamazsınız.
 issues.dependency.add_error_dep_not_same_repo=Her iki konu da aynı depoda olmalıdır.
 issues.review.self.approval=Kendi değişiklik isteğinizi onaylayamazsınız.
 issues.review.self.rejection=Kendi değişiklik isteğinizde değişiklik isteyemezsiniz.
+issues.review.approve=%s bu değişiklikleri onayladı
+issues.review.comment=%s incelendi
 issues.review.content.empty=İstenen değişiklik(ler)i belirten bir yorum bırakmanız gerekir.
+issues.review.reject=%s değişiklik istedi
 issues.review.pending=Beklemede
 issues.review.review=Gözden Geçir
 issues.review.reviewers=Gözden Geçirenler
@@ -897,6 +934,7 @@ issues.review.reviewers=Gözden Geçirenler
 pulls.desc=Değişiklik isteklerini ve kod incelemelerini etkinleştir.
 pulls.new=Yeni Değişiklik İsteği
 pulls.compare_changes=Yeni Değişiklik İsteği
+pulls.compare_changes_desc=Birleştirmek için hedef ve kaynak dalı seçin.
 pulls.compare_compare=şuradan çek
 pulls.filter_branch=Dal filtrele
 pulls.no_results=Sonuç bulunamadı.
@@ -904,7 +942,7 @@ pulls.nothing_to_compare=Bu dallar eşit. Değişiklik isteği oluşturmaya gere
 pulls.has_pull_request=`Bu dallar arasında bir değişiklik isteği zaten var: <a href="%[1]s/pulls/%[3]d">%[2]s#%[3]d</a>`
 pulls.create=Değişiklik İsteği Oluştur
 pulls.title_desc=<code>%[3]s</code> içindeki <code>%[2]s</code> işlemelerini %[1]d ile birleştirmek istiyor
-pulls.merged_title_desc=<code>%[3]s</code> %[4]s içindeki <code>%[2]s</code> işlemelerini %[1]d ile birleştirdi
+pulls.merged_title_desc=%[4]s <code>%[2]s</code> içindeki %[1]d işleme <code>%[3]s</code> ile birleştirdi
 pulls.tab_conversation=Sohbet
 pulls.tab_commits=İşlemeler
 pulls.tab_files=Değiştirilen Dosyalar
@@ -966,8 +1004,8 @@ milestones.filter_sort.closest_due_date=En yakın zamanı gelmiş tarih
 milestones.filter_sort.furthest_due_date=En uzak zamanı gelmiş tarih
 milestones.filter_sort.least_complete=En az tamamlama
 milestones.filter_sort.most_complete=En çok tamamlama
-milestones.filter_sort.most_issues=En çok sorun
-milestones.filter_sort.least_issues=En az sorun
+milestones.filter_sort.most_issues=En çok konu
+milestones.filter_sort.least_issues=En az konu
 
 ext_wiki=Harici Wiki
 ext_wiki.desc=Harici bir wiki'ye bağlantı.
@@ -985,8 +1023,11 @@ wiki.save_page=Sayfayı Kaydet
 wiki.last_commit_info=%s bu sayfayı düzenledi %s
 wiki.edit_page_button=Düzenle
 wiki.new_page_button=Yeni Sayfa
+wiki.file_revision=Sayfa Revizyonu
+wiki.wiki_page_revisions=Wiki Sayfası Revizyonları
 wiki.back_to_wiki=Wiki sayfasına geri dön
 wiki.delete_page_button=Sayfayı Sil
+wiki.delete_page_notice_1='%s' wiki sayfasını silmek geri alınamaz. Devam edilsin mi?
 wiki.page_already_exists=Aynı isimde bir Wiki sayfası zaten var.
 wiki.reserved_page='%s' wiki sayfa adı rezerve edilmiştir.
 wiki.pages=Sayfalar
@@ -1113,10 +1154,13 @@ settings.transfer_notices_1=- Bireysel bir kullanıcıya aktarırsanız depoya e
 settings.transfer_form_title=Onaylamak için depo adını girin:
 settings.wiki_delete=Wiki Verisini Sil
 settings.wiki_delete_desc=Depo wiki verilerini silmek kalıcıdır ve geri alınamaz.
+settings.wiki_delete_notices_1=- Bu işlem, %s için depo wiki'sini kalıcı olarak siler ve devre dışı bırakır.
 settings.confirm_wiki_delete=Wiki Verisini Sil
 settings.wiki_deletion_success=Depo wiki verisi silindi.
 settings.delete=Bu Depoyu Sil
+settings.delete_desc=Bir depoyu silmek kalıcıdır ve geri alınamaz.
 settings.delete_notices_1=- Bu işlem geri <strong>ALINAMAZ</strong>.
+settings.delete_notices_fork_1=- Silme işleminden sonra bu deponun çatalları bağımsız hale gelecektir.
 settings.deletion_success=Depo silindi.
 settings.update_settings_success=Depo ayarları güncellendi.
 settings.transfer_owner=Yeni Sahip
@@ -1137,7 +1181,9 @@ settings.change_team_access_not_allowed=Depo için takım erişimini değiştirm
 settings.team_not_in_organization=Takım, depo ile aynı organizasyonda değil
 settings.add_team_duplicate=Takım zaten bu depoya sahip
 settings.add_team_success=Takım artık bu depoya erişebilir.
+settings.remove_team_success=Takımın depoya erişimi kaldırıldı.
 settings.add_webhook=Web İsteği Ekle
+settings.add_webhook.invalid_channel_name=Web istemci kanal adı boş olamaz ve yalnızca bir # karakteri içeremez.
 settings.hooks_desc=Web istemcileri, belirli Gitea olayları tetiklendiğinde otomatik olarak bir sunucuya HTTP POST isteği yapar. <a target="_blank" rel="noopener noreferrer" href="%s">Web istekleri kılavuzundan</a> daha fazla bilgi edinebilirsiniz.
 settings.webhook_deletion=Web İsteğini Sil
 settings.webhook_deletion_desc=Bir web isteğini kaldırmak, ayarlarını ve teslimat geçmişini siler. Devam edilsin mi?
@@ -1274,7 +1320,9 @@ diff.parent=ebeveyn
 diff.commit=işleme
 diff.git-notes=Notlar
 diff.data_not_available=Farklı İçerik Mevut Değil
-diff.show_diff_stats=Farklılık Durumunu Göster
+diff.options_button=Diff Seçenekleri
+diff.download_patch=Yama Dosyasını İndir
+diff.download_diff=Diff Dosyasını İndir
 diff.show_split_view=Görünümü Böl
 diff.show_unified_view=Birleşik Görünüm
 diff.whitespace_button=Boşluk
@@ -1299,10 +1347,13 @@ diff.comment.add_review_comment=Yorum ekle
 diff.comment.start_review=İncelemeye başla
 diff.comment.reply=Yanıtla
 diff.review=İncele
+diff.review.header=İnceleme gönder
+diff.review.placeholder=İnceleme yorumu
 diff.review.comment=Yorum Yap
 diff.review.approve=Onayla
 diff.review.reject=Değişiklik iste
 
+releases.desc=Proje sürümlerini ve indirmeleri takip edin.
 release.releases=Sürümler
 release.new_release=Yeni Sürüm
 release.draft=Taslak
@@ -1354,6 +1405,7 @@ branch.restore_failed='%s' dalı geri yüklenemedi.
 branch.protected_deletion_failed='%s' dalı korunuyor. Silinemez.
 branch.restore='%s' Dalını Geri Yükle
 branch.download='%s' Dalını İndir
+branch.included_desc=Bu dal varsayılan dalın bir parçasıdır
 
 topic.done=Bitti
 
@@ -1560,7 +1612,7 @@ repos.private=Özel
 repos.watches=İzlemeler
 repos.stars=Yıldızlar
 repos.forks=Çatallar
-repos.issues=Sorunlar
+repos.issues=Konular
 repos.size=Boyut
 
 hooks.add_webhook=Varsayılan Web İstemcisi Ekle
@@ -1623,7 +1675,14 @@ auths.tip.gitea=Yeni bir OAuth2 uygulaması kaydedin. Rehber https://docs.gitea.
 auths.edit=Kimlik Doğrulama Kaynağı Düzenle
 auths.activated=Bu Kimlik Doğrulama Kaynağı Etkinleştirildi
 auths.new_success=Kimlik doğrulama '%s' eklendi.
+auths.update_success=Kimlik doğrulama kaynağı güncellendi.
+auths.update=Kimlik Doğrulama Kaynağını Güncelle
+auths.delete=Kimlik Doğrulama Kaynağını Sil
 auths.delete_auth_title=Kimlik Doğrulama Kaynağını Sil
+auths.delete_auth_desc=Bir kimlik doğrulama kaynağını silmek, kullanıcıların oturum açmak için kullanmalarını engeller. Devam edilsin mi?
+auths.still_in_used=Kimlik doğrulama kaynağı hala kullanımda. Önce bu kimlik doğrulama kaynağını kullanan tüm kullanıcıları dönüştürün veya silin.
+auths.deletion_success=Kimlik doğrulama kaynağı silindi.
+auths.login_source_exist='%s' kimlik doğrulama kaynağı zaten var.
 
 config.server_config=Sunucu Yapılandırması
 config.app_name=Site Başlığı
@@ -1633,7 +1692,7 @@ config.offline_mode=Yerel Kip
 config.disable_router_log=Yönlendirici Log'larını Devre Dışı Bırak
 config.run_user=Şu Kullanıcı Olarak Çalıştır
 config.run_mode=Çalıştırma Modu
-config.git_version=Git Sürüm
+config.git_version=Git Sürümü
 config.repo_root_path=Depo Kök Yolu
 config.lfs_root_path=LFS Kök Dizini
 config.static_file_root_path=Sabit Dosya Kök Yolu
@@ -1675,17 +1734,22 @@ config.disable_key_size_check=Minimum Anahtar Uzunluğu Kontrolünü Devre Dış
 config.enable_captcha=CAPTCHA'yı Etkinleştir
 config.active_code_lives=Kod Yaşamlarını Aktifleştir
 config.default_keep_email_private=E-posta Adreslerini Varsayılan Olarak Gizle
+config.default_visibility_organization=Yeni organizasyonlar için varsayılan görünürlük
+config.default_enable_dependencies=Konu Bağımlılıklarını Varsayılan Olarak Etkinleştir
 
 config.webhook_config=Web İstekleri Yapılandırması
 config.queue_length=Kuyruk Uzunluğu
 config.deliver_timeout=Dağıtım Zaman Aşımı
 config.skip_tls_verify=TLS Doğrulamasını Geç
 
+config.mailer_config=SMTP Mailer Yapılandırması
 config.mailer_enabled=Aktif
 config.mailer_disable_helo=HELO'yu Devre Dışı Bırak
 config.mailer_name=İsim
 config.mailer_host=Sunucu
 config.mailer_user=Kullanıcı
+config.mailer_use_sendmail=Sendmail Kullan
+config.mailer_sendmail_path=Sendmail Yolu
 config.send_test_mail=Test E-postası Gönder
 config.test_mail_failed='%s' adresine test e-postası gönderilemedi: %v
 config.test_mail_sent='%s' adresine bir test e-postası gönderildi.
@@ -1708,6 +1772,7 @@ config.session_life_time=Oturum Yaşam Zamanı
 config.https_only=Yalnız HTTPS
 config.cookie_life_time=Çerez Yaşam Zamanı
 
+config.picture_config=Resim ve Avatar Yapılandırması
 config.picture_service=Resim Servisi
 config.disable_gravatar=Gravatar Hizmet Dışı
 
@@ -1727,6 +1792,8 @@ config.log_config=Log Yapılandırması
 config.log_mode=Log Modu
 config.macaron_log_mode=Macaron Günlük Kipi
 config.own_named_logger=Adlandırılmış Günlük Kaydedicisi
+config.routes_to_default_logger=Varsayılan Günlük Kaydedicisine Yönlendirir
+config.go_log=Go Log kullanılır (varsayılana yönlendirilir)
 config.router_log_mode=Yönlendirici Günlük Kipi
 config.disabled_logger=Devre Dışı
 config.access_log_mode=Erişim Günlüğü Kipi
@@ -1763,13 +1830,13 @@ notices.delete_success=Sistem bildirimleri silindi.
 create_repo=depo <a href="%s">%s</a> oluşturuldu
 rename_repo=<code>%[1]s</code> olan depo adını <a href="%[2]s">%[3]s</a> buna çevirdi
 commit_repo=<a href="%[1]s">%[4]s</a> deposunda <a href="%[1]s/src/branch/%[2]s">%[3]s</a> dalını itti
-create_issue=`<a href="%s/issues/%s">%s#%[2]s</a> sorununu açtı`
-close_issue=`<a href="%s/issues/%s">%s#%[2]s</a> sorununu kapattı`
-reopen_issue=`<a href="%s/issues/%s">%s#%[2]s</a> sorununu tekrar açtı`
+create_issue=`<a href="%s/issues/%s">%s#%[2]s</a> konusunu açtı`
+close_issue=`<a href="%s/issues/%s">%s#%[2]s</a> konusunu kapattı`
+reopen_issue=`<a href="%s/issues/%s">%s#%[2]s</a> konusunu tekrar açtı`
 create_pull_request=`<a href="%s/pulls/%s">%s#%[2]s</a> değişiklik isteğini oluşturdu`
 close_pull_request=`<a href="%s/pulls/%s">%s#%[2]s</a> değişiklik isteğini kapattı`
 reopen_pull_request=`<a href="%s/pulls/%s">%s#%[2]s</a> değişiklik isteğini tekrar açtı`
-comment_issue=`<a href="%s/issues/%s">%s#%[2]s</a> sorununa yorum yazdı`
+comment_issue=`<a href="%s/issues/%s">%s#%[2]s</a> konusuna yorum yazdı`
 merge_pull_request=`<a href="%s/pulls/%s">%s#%[2]s</a> değişim isteğini birleştirdi`
 transfer_repo=depo <code>%s</code> <a href="%s">%s</a>'a aktarıldı
 push_tag=<a href="%[1]s">%[3]s</a> deposuna <a href="%s/src/tag/%s">%[2]s</a> etiketi itildi
@@ -1778,6 +1845,7 @@ delete_branch=<a href="%[1]s">%[3]s</a> deposundan %[2]s dalı silindi
 compare_commits=%d işlemeyi karşılaştır
 compare_commits_general=İşlemeleri karşılaştır
 mirror_sync_push=işlemeler yansıdan <a href="%[1]s">%[4]s</a> deposundaki <a href="%[1]s/src/%[2]s">%[3]s</a> dalına eşitlendi
+mirror_sync_delete=<a href="%[1]s">%[3]s</a> adresindeki <code>%[2]s</code> referansı eşitlendi ve silindi
 
 [tool]
 ago=%s önce
@@ -1819,12 +1887,15 @@ mark_as_unread=Okunmadı olarak işaretle
 mark_all_as_read=Tümünü okundu olarak işaretle
 
 [gpg]
+default_key=Varsayılan anahtarla imzalanmış
 error.extract_sign=İmza çıkarılamadı
 error.generate_hash=İşlemenin sağlama kodu oluşturulamadı
 error.no_committer_account=İşleme yapanın e-posta adresine bağlı hesap yok
 error.no_gpg_keys_found=Veri tabanında bu imza için bilinen anahtar bulunamadı
 error.not_signed_commit=İmzalı bir işleme değil
 error.failed_retrieval_gpg_keys=İşleme yapanın hesabına bağlı herhangi bir anahtar alınamadı
+error.probable_bad_signature=UYARI! Veritabanında bu kimliğe sahip bir anahtar olmasına rağmen, bu işlemeyi doğrulamaz! Bu işleme ŞÜPHELİDİR.
+error.probable_bad_default_signature=UYARI! Varsayılan anahtarın bu kimliği olmasına rağmen, bu işlemeyi doğrulamaz! Bu işleme ŞÜPHELİDİR.
 
 [units]
 error.no_unit_allowed_repo=Bu deponun hiçbir bölümüne erişme izniniz yok.
diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini
index 22d30705db..51e52b2785 100644
--- a/options/locale/locale_uk-UA.ini
+++ b/options/locale/locale_uk-UA.ini
@@ -1,5 +1,3 @@
-app_desc=Зручний сервіс, власного Git хостингу
-
 home=Головна
 dashboard=Панель управління
 explore=Огляд
@@ -75,6 +73,8 @@ write=Писати
 preview=Попередній перегляд
 loading=Завантаження…
 
+[startpage]
+
 [install]
 install=Встановлення
 title=Початкова конфігурація
@@ -1136,7 +1136,6 @@ diff.browse_source=Переглянути джерело
 diff.parent=джерело
 diff.commit=коміт
 diff.data_not_available=Різниця недоступна
-diff.show_diff_stats=Показати статистику Diff
 diff.show_split_view=Розділений перегляд
 diff.show_unified_view=Об'єднаний перегляд
 diff.whitespace_button=Пробіли
diff --git a/options/locale/locale_vi-VN.ini b/options/locale/locale_vi-VN.ini
index f2571ce9c0..35af054c15 100644
--- a/options/locale/locale_vi-VN.ini
+++ b/options/locale/locale_vi-VN.ini
@@ -6,6 +6,7 @@
 
 
 
+[startpage]
 
 [install]
 
diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini
index 7667609793..6a0ebed2be 100644
--- a/options/locale/locale_zh-CN.ini
+++ b/options/locale/locale_zh-CN.ini
@@ -1,5 +1,3 @@
-app_desc=一款极易搭建的自助 Git 服务
-
 home=首页
 dashboard=首页
 explore=探索
@@ -75,6 +73,17 @@ write=撰写
 preview=预览
 loading=正在加载...
 
+[startpage]
+app_desc=一款极易搭建的自助 Git 服务
+install=易安装
+install_desc=您除了可以根据操作系统平台通过 <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/zh-cn/install-from-binary/">二进制运行</a>,还可以通过 <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a> 或 <a target="_blank" rel="noopener noreferrer" href="https://github.com/alvaroaleman/ansible-gitea/blob/master/Vagrantfile">Vagrant</a>,以及 <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/zh-cn/install-from-package/">包管理</a> 安装。
+platform=跨平台
+platform_desc=任何 <a target="_blank" rel="noopener noreferrer" href="http://golang.org/">Go 语言</a> 支持的平台都可以运行 Gitea,包括 Windows、Mac、Linux 以及 ARM。挑一个您喜欢的就行!
+lightweight=轻量级
+lightweight_desc=一个廉价的树莓派的配置足以满足 Gitea 的最低系统硬件要求。最大程度上节省您的服务器资源!
+license=开源化
+license_desc=所有的代码都开源在 <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/">GitHub</a> 上,赶快加入我们来共同发展这个伟大的项目!还等什么?成为贡献者吧!
+
 [install]
 install=安装页面
 title=初始配置
@@ -250,7 +259,6 @@ openid_signin_desc=输入您的 OpenID URI。例如: https://anne.me、bob.openi
 disable_forgot_password_mail=帐户恢复功能已被禁用。请与网站管理员联系。
 email_domain_blacklisted=您不能使用您的电子邮件地址注册。
 authorize_application=应用授权
-authroize_redirect_notice=如果您授权此应用,您将会被重定向到 %s。
 authorize_application_created_by=此应用由%s创建。
 authorize_application_description=如果您允许,它将能够读取和修改您的所有帐户信息,包括私人仓库和组织。
 authorize_title=授权 %s 访问您的帐户?
@@ -314,6 +322,7 @@ team_no_units_error=至少选择一项仓库单元。
 email_been_used=该电子邮件地址已在使用中。
 openid_been_used=OpenID 地址 '%s' 已被使用。
 username_password_incorrect=用户名或密码不正确。
+password_complexity=密码未达到复杂程度要求
 enterred_invalid_repo_name=输入的仓库名称不正确
 enterred_invalid_owner_name=新的所有者名称无效。
 enterred_invalid_password=输入的密码不正确
@@ -632,6 +641,8 @@ migrate.lfs_mirror_unsupported=不支持镜像 LFS 对象 - 使用 'git lfs fetc
 migrate.migrate_items_options=当从 github 迁移并且输入了用户名时,迁移选项将会显示。
 migrated_from=从 <a href="%[1]s">%[2]s</a> 迁移
 migrated_from_fake=从 %[1]s 迁移成功
+migrate.migrating=正在从 <b>%s</b> 迁移...
+migrate.migrating_failed=从 <b>%s</b> 迁移失败。
 
 mirror_from=镜像自地址
 forked_from=派生自
@@ -680,6 +691,8 @@ stored_lfs=存储到Git LFS
 commit_graph=提交图
 blame=Blame
 normal_view=普通视图
+line=行
+lines=行
 
 editor.new_file=新建文件
 editor.upload_file=上传文件
@@ -705,6 +718,7 @@ editor.delete=删除 '%s'
 editor.commit_message_desc=添加一个可选的扩展描述...
 editor.commit_directly_to_this_branch=直接提交至 <strong class="branch-name">%s</strong> 分支。
 editor.create_new_branch=为此提交创建一个 <strong>新的分支</strong> 并发起合并请求。
+editor.create_new_branch_np=为此提交创建 <strong>新分支</strong>。
 editor.propose_file_change=提议文件更改
 editor.new_branch_name_desc=新的分支名称...
 editor.cancel=取消
@@ -719,6 +733,8 @@ editor.file_editing_no_longer_exists=正在编辑的文件 '%s' 已不存在。
 editor.file_deleting_no_longer_exists=仓库中不存在将被删除的文件‘%s’。
 editor.file_changed_while_editing=文件内容在您进行编辑时已经发生变动。<a target="_blank" rel="noopener noreferrer" href="%s">单击此处</a> 查看变动的具体内容,或者 <strong>再次提交</strong> 覆盖已发生的变动。
 editor.file_already_exists=此仓库已经存在名为 '%s' 的文件。
+editor.commit_empty_file_header=提交一个空文件
+editor.commit_empty_file_text=您要提交的文件是空的,继续吗?
 editor.no_changes_to_show=没有可以显示的变更。
 editor.fail_to_update_file=更新/创建文件 '%s' 时发生错误:%v
 editor.add_subdir=添加目录
@@ -1363,7 +1379,10 @@ diff.parent=父节点
 diff.commit=当前提交
 diff.git-notes=Notes
 diff.data_not_available=比较内容不可用
-diff.show_diff_stats=显示文件统计
+diff.options_button=Diff 选项
+diff.show_diff_stats=显示统计
+diff.download_patch=下载 Patch 文件
+diff.download_diff=下载 Diff 文件
 diff.show_split_view=分列视图
 diff.show_unified_view=合并视图
 diff.whitespace_button=空白符号
@@ -1446,6 +1465,8 @@ branch.restore_failed=未能还原分支%s。
 branch.protected_deletion_failed=分支 '%s' 已被保护,不可删除。
 branch.restore=恢复分支 '%s'
 branch.download=下载分支 '%s'
+branch.included_desc=此分支是默认分支的一部分
+branch.included=已包含
 
 topic.manage_topics=管理主题
 topic.done=保存
@@ -1962,12 +1983,15 @@ mark_as_unread=标记为未读
 mark_all_as_read=全部标记为已读
 
 [gpg]
+default_key=使用默认密钥签名
 error.extract_sign=无法提取签名
 error.generate_hash=无法生成提交的哈希
 error.no_committer_account=没有帐户链接到提交者的电子邮件
 error.no_gpg_keys_found=找不到此签名对应的密钥
 error.not_signed_commit=未签名的提交
 error.failed_retrieval_gpg_keys=找不到任何与该提交者账号相关的密钥
+error.probable_bad_signature=警告!虽然数据库中有一个此ID的密钥,但它没有验证此提交!此提交是有疑问的。
+error.probable_bad_default_signature=警告!虽然默认密钥拥有此ID,但它没有验证此提交!此提交是有疑问的。
 
 [units]
 error.no_unit_allowed_repo=您没有被允许访问此仓库的任何单元。
diff --git a/options/locale/locale_zh-HK.ini b/options/locale/locale_zh-HK.ini
index ab97fccb6b..43e9b3eea5 100644
--- a/options/locale/locale_zh-HK.ini
+++ b/options/locale/locale_zh-HK.ini
@@ -1,4 +1,3 @@
-
 home=首頁
 dashboard=控制面版
 explore=探索
@@ -43,6 +42,8 @@ issues=問題
 cancel=取消
 
 
+[startpage]
+
 [install]
 install=安裝頁面
 db_title=資料庫設定
@@ -513,7 +514,6 @@ diff.browse_source=瀏覽代碼
 diff.parent=父節點
 diff.commit=當前提交
 diff.data_not_available=沒有內容比較可以使用
-diff.show_diff_stats=顯示文件統計
 diff.show_split_view=分割檢視
 diff.show_unified_view=統一視圖
 diff.stats_desc=共有 <strong> %d 個文件被更改</strong>,包括 <strong>%d 次插入</strong> 和 <strong>%d 次删除</strong>
diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini
index 796906312b..de787b6b47 100644
--- a/options/locale/locale_zh-TW.ini
+++ b/options/locale/locale_zh-TW.ini
@@ -1,5 +1,3 @@
-app_desc=一個無痛、自託管的 Git 服務
-
 home=首頁
 dashboard=控制面版
 explore=探索
@@ -64,6 +62,8 @@ issues=問題
 cancel=取消
 
 
+[startpage]
+
 [install]
 install=安裝頁面
 title=初始設定
@@ -919,7 +919,6 @@ diff.browse_source=瀏覽代碼
 diff.parent=父節點
 diff.commit=當前提交
 diff.data_not_available=沒有內容比較可以使用
-diff.show_diff_stats=顯示文件統計
 diff.show_split_view=分割檢視
 diff.show_unified_view=統一視圖
 diff.stats_desc=共有 <strong> %d 個檔案被更改</strong>,包括 <strong>%d 行新增</strong> 和 <strong>%d 行删除</strong>
diff --git a/public/css/index.css b/public/css/index.css
index 496194decc..e404c1fec6 100644
--- a/public/css/index.css
+++ b/public/css/index.css
@@ -225,6 +225,10 @@ footer .ui.left,footer .ui.right{line-height:40px}
 .inline-grouped-list{display:inline-block;vertical-align:top}
 .inline-grouped-list>.ui{display:block;margin-top:5px;margin-bottom:10px}
 .inline-grouped-list>.ui:first-child{margin-top:1px}
+i.icons .icon:first-child{margin-right:0}
+i.icon.centerlock{top:1.5em}
+.ui.label>.detail .icons{margin-right:.25em}
+.ui.label>.detail .icons .icon{margin-right:0}
 .lines-num{vertical-align:top;text-align:right!important;color:#999;background:#f5f5f5;width:1%;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
 .lines-num span:before{content:attr(data-line-number);line-height:20px!important;padding:0 10px;cursor:pointer;display:block}
 .lines-code,.lines-num{padding:0!important}
@@ -239,7 +243,7 @@ footer .ui.left,footer .ui.right{line-height:40px}
 .lines-commit .ui.avatar.image{height:18px;width:18px}
 .lines-code .bottom-line,.lines-commit .bottom-line,.lines-num .bottom-line{border-bottom:1px solid #eaecef}
 .code-view{overflow:auto;overflow-x:auto;overflow-y:hidden}
-.code-view *{font-size:12px;font-family:'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace;line-height:20px}
+.code-view :not(.fa):not(.octicon):not(.icon){font-size:12px;font-family:'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace;line-height:20px}
 .code-view table{width:100%}
 .code-view .active{background:#fff866}
 .markdown:not(code){overflow:hidden;font-size:16px;line-height:1.6!important;word-wrap:break-word}
@@ -392,6 +396,8 @@ footer .ui.left,footer .ui.right{line-height:40px}
 .user.activate form .inline.field>label,.user.forgot.password form .inline.field>label,.user.reset.password form .inline.field>label,.user.signin form .inline.field>label,.user.signup form .inline.field>label{width:200px}
 @media only screen and (max-width:768px){.user.activate form .inline.field>label,.user.activate form input,.user.forgot.password form .inline.field>label,.user.forgot.password form input,.user.reset.password form .inline.field>label,.user.reset.password form input,.user.signin form .inline.field>label,.user.signin form input,.user.signup form .inline.field>label,.user.signup form input{width:100%!important}
 }
+.user.activate form input[type=number],.user.forgot.password form input[type=number],.user.reset.password form input[type=number],.user.signin form input[type=number],.user.signup form input[type=number]{-moz-appearance:textfield}
+.user.activate form input::-webkit-inner-spin-button,.user.activate form input::-webkit-outer-spin-button,.user.forgot.password form input::-webkit-inner-spin-button,.user.forgot.password form input::-webkit-outer-spin-button,.user.reset.password form input::-webkit-inner-spin-button,.user.reset.password form input::-webkit-outer-spin-button,.user.signin form input::-webkit-inner-spin-button,.user.signin form input::-webkit-outer-spin-button,.user.signup form input::-webkit-inner-spin-button,.user.signup form input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}
 .repository.new.fork form,.repository.new.migrate form,.repository.new.repo form{margin:auto}
 .repository.new.fork form .ui.message,.repository.new.migrate form .ui.message,.repository.new.repo form .ui.message{text-align:center}
 @media only screen and (min-width:768px){.repository.new.fork form,.repository.new.migrate form,.repository.new.repo form{width:800px!important}
@@ -452,6 +458,8 @@ footer .ui.left,footer .ui.right{line-height:40px}
 .repository .filter.menu .label.color{border-radius:3px;margin-left:15px;padding:0 8px}
 .repository .filter.menu .octicon{float:left;margin:5px -7px 0 -5px;width:16px}
 .repository .filter.menu.labels .octicon{margin:-2px -7px 0 -5px}
+.repository .filter.menu.labels .label-filter .menu .info{display:inline-block;padding:9px 7px 7px 7px;text-align:center;border-bottom:1px solid #ccc;font-size:12px}
+.repository .filter.menu.labels .label-filter .menu .info code{border:1px solid #ccc;border-radius:3px;padding:3px 2px 1px 2px;font-size:11px}
 .repository .filter.menu .text{margin-left:.9em}
 .repository .filter.menu .menu{max-height:300px;overflow-x:auto;right:0!important;left:auto!important}
 .repository .filter.menu .dropdown.item{margin:1px;padding-right:0}
@@ -494,7 +502,7 @@ footer .ui.left,footer .ui.right{line-height:40px}
 .repository.file.list #repo-files-table tr:hover{background-color:#ffe}
 .repository.file.list #repo-files-table .jumpable-path{color:#888}
 .repository.file.list .non-diff-file-content .header .icon{font-size:1em}
-.repository.file.list .non-diff-file-content .header .file-actions{margin-top:0;margin-bottom:-5px;padding-left:20px}
+.repository.file.list .non-diff-file-content .header .file-actions{margin-bottom:-5px}
 .repository.file.list .non-diff-file-content .header .file-actions .btn-octicon{display:inline-block;padding:5px;margin-left:5px;line-height:1;color:#767676;vertical-align:middle;background:0 0;border:0;outline:0}
 .repository.file.list .non-diff-file-content .header .file-actions .btn-octicon:hover{color:#4078c0}
 .repository.file.list .non-diff-file-content .header .file-actions .btn-octicon-danger:hover{color:#bd2c00}
@@ -654,13 +662,13 @@ footer .ui.left,footer .ui.right{line-height:40px}
 .repository #commits-table.ui.basic.striped.table tbody tr:nth-child(2n){background-color:rgba(0,0,0,.02)!important}
 .repository #commits-table td.sha .sha.label,.repository #repo-files-table .sha.label{border:1px solid #bbb}
 .repository #commits-table td.sha .sha.label .detail.icon,.repository #repo-files-table .sha.label .detail.icon{background:#fafafa;margin:-6px -10px -4px 0;padding:5px 3px 5px 6px;border-left:1px solid #bbb;border-top-left-radius:0;border-bottom-left-radius:0}
+.repository #commits-table td.sha .sha.label.isSigned.isWarning,.repository #repo-files-table .sha.label.isSigned.isWarning{border:1px solid #db2828;background:rgba(219,40,40,.1)}
+.repository #commits-table td.sha .sha.label.isSigned.isWarning .detail.icon,.repository #repo-files-table .sha.label.isSigned.isWarning .detail.icon{border-left:1px solid rgba(219,40,40,.5)}
 .repository #commits-table td.sha .sha.label.isSigned.isVerified,.repository #repo-files-table .sha.label.isSigned.isVerified{border:1px solid #21ba45;background:rgba(33,186,69,.1)}
 .repository #commits-table td.sha .sha.label.isSigned.isVerified .detail.icon,.repository #repo-files-table .sha.label.isSigned.isVerified .detail.icon{border-left:1px solid #21ba45}
 .repository #commits-table td.sha .sha.label.isSigned.isVerified:hover,.repository #repo-files-table .sha.label.isSigned.isVerified:hover{background:rgba(33,186,69,.3)!important}
 .repository .diff-detail-box{padding:7px 0;background:#fff;line-height:30px}
 .repository .diff-detail-box>div:after{clear:both;content:"";display:block}
-.repository .diff-detail-box ol{clear:both;padding-left:0;margin-top:5px;margin-bottom:28px}
-.repository .diff-detail-box ol li{list-style:none;padding-bottom:4px;margin-bottom:4px;border-bottom:1px dashed #ddd;padding-left:6px}
 .repository .diff-detail-box span.status{display:inline-block;width:12px;height:12px;margin-right:8px;vertical-align:middle}
 .repository .diff-detail-box span.status.modify{background-color:#f0db88}
 .repository .diff-detail-box span.status.add{background-color:#b4e2b4}
@@ -697,6 +705,11 @@ footer .ui.left,footer .ui.right{line-height:40px}
 .repository .diff-file-box.file-content{clear:right}
 .repository .diff-file-box.file-content img{max-width:100%;padding:5px 5px 0 5px}
 .repository .diff-file-box.file-content img.emoji{padding:0}
+.repository .diff-stats{clear:both;margin-bottom:5px;max-height:400px;overflow:auto;padding-left:0}
+.repository .diff-stats li{list-style:none;padding-bottom:4px;margin-bottom:4px;border-bottom:1px dashed #ddd;padding-left:6px}
+.repository .diff-stats .diff-counter{margin-right:15px}
+.repository .diff-stats .diff-counter .del{color:red}
+.repository .diff-stats .diff-counter .add{color:green}
 .repository .repo-search-result{padding-top:10px;padding-bottom:10px}
 .repository .repo-search-result .lines-num a{color:inherit}
 .repository.quickstart .guide .item{padding:1em}
@@ -878,6 +891,10 @@ tbody.commit-list{vertical-align:baseline}
 .repo-buttons .disabled-repo-button a.button:hover{background:0 0!important;color:rgba(0,0,0,.6)!important;box-shadow:0 0 0 1px rgba(34,36,38,.15) inset!important}
 .repo-buttons .ui.labeled.button>.label{border-left:0!important;margin:0!important}
 .tag-code,.tag-code td{background-color:#f0f0f0!important;border-color:#d3cfcf!important;padding-top:8px;padding-bottom:8px}
+.issue-keyword{border-bottom:1px dotted #959da5;display:inline-block}
+.file-header{display:flex;justify-content:space-between;align-items:center;padding:8px 12px!important}
+.file-info{display:flex;align-items:center}
+.file-info-entry+.file-info-entry{border-left:1px solid currentColor;margin-left:8px;padding-left:8px}
 .CodeMirror{font:14px 'SF Mono',Consolas,Menlo,'Liberation Mono',Monaco,'Lucida Console',monospace}
 .CodeMirror.cm-s-default{border-radius:3px;padding:0!important}
 .CodeMirror .cm-comment{background:inherit!important}
diff --git a/public/img/loading.png b/public/img/loading.png
new file mode 100644
index 0000000000..aac702cfd6
Binary files /dev/null and b/public/img/loading.png differ
diff --git a/public/js/index.js b/public/js/index.js
index 8a85ad9157..e76e993a1d 100644
--- a/public/js/index.js
+++ b/public/js/index.js
@@ -241,6 +241,41 @@ function updateIssuesMeta(url, action, issueIds, elementId) {
     })
 }
 
+function initRepoStatusChecker() {
+    const migrating = $("#repo_migrating");
+    $('#repo_migrating_failed').hide();
+    if (migrating) {
+        const repo_name = migrating.attr('repo');
+        if (typeof repo_name === 'undefined') {
+            return
+        }
+        $.ajax({
+            type: "GET",
+            url: suburl +"/"+repo_name+"/status",
+            data: {
+                "_csrf": csrf,
+            },
+            complete: function(xhr) {
+                if (xhr.status == 200) {
+                    if (xhr.responseJSON) {
+                        if (xhr.responseJSON["status"] == 0) {
+                            location.reload();
+                            return
+                        }
+
+                        setTimeout(function () {
+                            initRepoStatusChecker()
+                        }, 2000);
+                        return
+                    }
+                }
+                $('#repo_migrating_progress').hide();
+                $('#repo_migrating_failed').show();
+            }
+        })
+    }
+}
+
 function initReactionSelector(parent) {
     let reactions = '';
     if (!parent) {
@@ -271,7 +306,7 @@ function initReactionSelector(parent) {
             if (resp && (resp.html || resp.empty)) {
                 const content = $(vm).closest('.content');
                 let react = content.find('.segment.reactions');
-                if (react.length > 0) {
+                if (!resp.empty && react.length > 0) {
                     react.remove();
                 }
                 if (!resp.empty) {
@@ -830,6 +865,73 @@ function initRepository() {
                 issuesTribute.attach($textarea.get());
                 emojiTribute.attach($textarea.get());
 
+                const $dropzone = $editContentZone.find('.dropzone');
+                $dropzone.data("saved", false);
+                const $files = $editContentZone.find('.comment-files');
+                if ($dropzone.length > 0) {
+                    const filenameDict = {};
+                    $dropzone.dropzone({
+                        url: $dropzone.data('upload-url'),
+                        headers: {"X-Csrf-Token": csrf},
+                        maxFiles: $dropzone.data('max-file'),
+                        maxFilesize: $dropzone.data('max-size'),
+                        acceptedFiles: ($dropzone.data('accepts') === '*/*') ? null : $dropzone.data('accepts'),
+                        addRemoveLinks: true,
+                        dictDefaultMessage: $dropzone.data('default-message'),
+                        dictInvalidFileType: $dropzone.data('invalid-input-type'),
+                        dictFileTooBig: $dropzone.data('file-too-big'),
+                        dictRemoveFile: $dropzone.data('remove-file'),
+                        init: function () {
+                            this.on("success", function (file, data) {
+                                filenameDict[file.name] = {
+                                    "uuid": data.uuid,
+                                    "submitted": false
+                                }
+                                const input = $('<input id="' + data.uuid + '" name="files" type="hidden">').val(data.uuid);
+                                $files.append(input);
+                            });
+                            this.on("removedfile", function (file) {
+                                if (!(file.name in filenameDict)) {
+                                    return;
+                                }
+                                $('#' + filenameDict[file.name].uuid).remove();
+                                if ($dropzone.data('remove-url') && $dropzone.data('csrf') && !filenameDict[file.name].submitted) {
+                                    $.post($dropzone.data('remove-url'), {
+                                        file: filenameDict[file.name].uuid,
+                                        _csrf: $dropzone.data('csrf')
+                                    });
+                                }
+                            });
+                            this.on("submit", function () {
+                                $.each(filenameDict, function(name){
+                                    filenameDict[name].submitted = true;
+                                });
+                            });
+                            this.on("reload", function (){
+                                $.getJSON($editContentZone.data('attachment-url'), function(data){
+                                    const drop = $dropzone.get(0).dropzone;
+                                    drop.removeAllFiles(true);
+                                    $files.empty();
+                                    $.each(data, function(){
+                                        const imgSrc =  $dropzone.data('upload-url') + "/" + this.uuid;
+                                        drop.emit("addedfile", this);
+                                        drop.emit("thumbnail", this, imgSrc);
+                                        drop.emit("complete", this);
+                                        drop.files.push(this);
+                                        filenameDict[this.name] = {
+                                            "submitted": true,
+                                            "uuid": this.uuid
+                                        }
+                                        $dropzone.find("img[src='" + imgSrc + "']").css("max-width", "100%");
+                                        const input = $('<input id="' + this.uuid + '" name="files" type="hidden">').val(this.uuid);
+                                        $files.append(input);
+                                    });
+                                });
+                            });
+                        }
+                    });
+                    $dropzone.get(0).dropzone.emit("reload");
+                }
                 // Give new write/preview data-tab name to distinguish from others
                 const $editContentForm = $editContentZone.find('.ui.comment.form');
                 const $tabMenu = $editContentForm.find('.tabular.menu');
@@ -845,27 +947,49 @@ function initRepository() {
                 $editContentZone.find('.cancel.button').click(function () {
                     $renderContent.show();
                     $editContentZone.hide();
+                    $dropzone.get(0).dropzone.emit("reload");
                 });
                 $editContentZone.find('.save.button').click(function () {
                     $renderContent.show();
                     $editContentZone.hide();
-
+                    const $attachments = $files.find("[name=files]").map(function(){
+                        return $(this).val();
+                    }).get();
                     $.post($editContentZone.data('update-url'), {
-                            "_csrf": csrf,
-                            "content": $textarea.val(),
-                            "context": $editContentZone.data('context')
-                        },
-                        function (data) {
-                            if (data.length == 0) {
-                                $renderContent.html($('#no-content').html());
-                            } else {
-                                $renderContent.html(data.content);
-                                emojify.run($renderContent[0]);
-                                $('pre code', $renderContent[0]).each(function () {
-                                    hljs.highlightBlock(this);
-                                });
+                        "_csrf": csrf,
+                        "content": $textarea.val(),
+                        "context": $editContentZone.data('context'),
+                        "files": $attachments
+                    },
+                    function (data) {
+                        if (data.length == 0) {
+                            $renderContent.html($('#no-content').html());
+                        } else {
+                            $renderContent.html(data.content);
+                            emojify.run($renderContent[0]);
+                            $('pre code', $renderContent[0]).each(function () {
+                                hljs.highlightBlock(this);
+                            });
+                        }
+                        const $content = $segment.parent();
+                        if(!$content.find(".ui.small.images").length){
+                            if(data.attachments != ""){
+                                $content.append(
+                                '<div class="ui bottom attached segment">' +
+                                '    <div class="ui small images">' +
+                                '    </div>' +
+                                '</div>'
+                                );
+                                $content.find(".ui.small.images").html(data.attachments);
                             }
-                        });
+                        } else if (data.attachments == "") {
+                            $content.find(".ui.small.images").parent().remove();
+                        } else {
+                            $content.find(".ui.small.images").html(data.attachments);
+                        }
+                        $dropzone.get(0).dropzone.emit("submit");
+                        $dropzone.get(0).dropzone.emit("reload");
+                    });
                 });
             } else {
                 $textarea = $segment.find('textarea');
@@ -1447,6 +1571,38 @@ function initEditor() {
             codeMirrorEditor.setOption("tabSize", editorconfig.tab_width || 4);
         });
     }).trigger('keyup');
+
+    // Using events from https://github.com/codedance/jquery.AreYouSure#advanced-usage
+    // to enable or disable the commit button
+    const $commitButton = $('#commit-button');
+    const $editForm = $('.ui.edit.form');
+    const dirtyFileClass = 'dirty-file';
+
+    // Disabling the button at the start
+    $commitButton.prop('disabled', true);
+
+    // Registering a custom listener for the file path and the file content
+    $editForm.areYouSure({
+        silent: true,
+        dirtyClass: dirtyFileClass,
+        fieldSelector: ':input:not(.commit-form-wrapper :input)',
+        change: function () {
+            const dirty = $(this).hasClass(dirtyFileClass);
+            $commitButton.prop('disabled', !dirty);
+        }
+    });
+
+    $commitButton.click(function (event) {
+        // A modal which asks if an empty file should be committed
+        if ($editArea.val().length === 0) {
+            $('#edit-empty-content-modal').modal({
+                onApprove: function () {
+                    $('.edit.form').submit();
+                }
+            }).modal('show');
+            event.preventDefault();
+        }
+    });
 }
 
 function initOrganization() {
@@ -2219,6 +2375,7 @@ $(document).ready(function () {
     initIssueList();
     initWipTitle();
     initPullRequestReview();
+    initRepoStatusChecker();
 
     // Repo clone url.
     if ($('#repo-clone-url').length > 0) {
@@ -3121,8 +3278,41 @@ function initIssueList() {
             },
 
             fullTextSearch: true
-        })
-    ;
+        });
+
+    $(".menu a.label-filter-item").each(function() {
+        $(this).click(function(e) {
+            if (e.altKey) {
+                e.preventDefault();
+
+                const href = $(this).attr("href");
+                const id = $(this).data("label-id");
+
+                const regStr = "labels=(-?[0-9]+%2c)*(" + id + ")(%2c-?[0-9]+)*&";
+                const newStr = "labels=$1-$2$3&";
+
+                window.location = href.replace(new RegExp(regStr), newStr);
+            }
+        });
+    });
+
+    $(".menu .ui.dropdown.label-filter").keydown(function(e) {
+        if (e.altKey && e.keyCode == 13) {
+            const selectedItems = $(".menu .ui.dropdown.label-filter .menu .item.selected");
+
+            if (selectedItems.length > 0) {
+                const item = $(selectedItems[0]);
+
+                const href = item.attr("href");
+                const id = item.data("label-id");
+
+                const regStr = "labels=(-?[0-9]+%2c)*(" + id + ")(%2c-?[0-9]+)*&";
+                const newStr = "labels=$1-$2$3&";
+
+                window.location = href.replace(new RegExp(regStr), newStr);
+            }
+        }
+    });
 }
 function cancelCodeComment(btn) {
     const form = $(btn).closest("form");
diff --git a/public/less/_base.less b/public/less/_base.less
index e295be368d..1a386f17b0 100644
--- a/public/less/_base.less
+++ b/public/less/_base.less
@@ -950,6 +950,22 @@ footer {
     }
 }
 
+i.icons .icon:first-child {
+    margin-right: 0;
+}
+
+i.icon.centerlock {
+    top: 1.5em;
+}
+
+.ui.label > .detail .icons {
+    margin-right: 0.25em;
+}
+
+.ui.label > .detail .icons .icon {
+    margin-right: 0;
+}
+
 .lines-num {
     vertical-align: top;
     text-align: right !important;
@@ -1047,7 +1063,7 @@ footer {
     overflow-x: auto;
     overflow-y: hidden;
 
-    * {
+    *:not(.fa):not(.octicon):not(.icon) {
         font-size: 12px;
         font-family: @monospaced-fonts, monospace;
         line-height: 20px;
diff --git a/public/less/_form.less b/public/less/_form.less
index c1a4f80c10..2f37666fb6 100644
--- a/public/less/_form.less
+++ b/public/less/_form.less
@@ -140,6 +140,16 @@
                 width: 100% !important;
             }
         }
+
+        input[type=number] {
+            -moz-appearance: textfield;
+        }
+
+        input::-webkit-outer-spin-button,
+        input::-webkit-inner-spin-button {
+            -webkit-appearance: none;
+            margin: 0;
+        }
     }
 }
 
diff --git a/public/less/_repository.less b/public/less/_repository.less
index ade3477ccc..48a1214c07 100644
--- a/public/less/_repository.less
+++ b/public/less/_repository.less
@@ -158,6 +158,23 @@
             margin: -2px -7px 0 -5px;
         }
 
+        &.labels {
+            .label-filter .menu .info {
+                display: inline-block;
+                padding: 9px 7px 7px 7px;
+                text-align: center;
+                border-bottom: 1px solid #cccccc;
+                font-size: 12px;
+
+                code {
+                    border: 1px solid #cccccc;
+                    border-radius: 3px;
+                    padding: 3px 2px 1px 2px;
+                    font-size: 11px;
+                }
+            }
+        }
+
         .text {
             margin-left: 0.9em;
         }
@@ -371,9 +388,7 @@
                 }
 
                 .file-actions {
-                    margin-top: 0;
                     margin-bottom: -5px;
-                    padding-left: 20px;
 
                     .btn-octicon {
                         display: inline-block;
@@ -1214,6 +1229,15 @@
             border-bottom-left-radius: 0;
         }
 
+        &.isSigned.isWarning {
+            border: 1px solid #db2828;
+            background: fade(#db2828, 10%);
+
+            .detail.icon {
+                border-left: 1px solid fade(#db2828, 50%);
+            }
+        }
+
         &.isSigned.isVerified {
             border: 1px solid #21ba45;
             background: fade(#21ba45, 10%);
@@ -1239,21 +1263,6 @@
             display: block;
         }
 
-        ol {
-            clear: both;
-            padding-left: 0;
-            margin-top: 5px;
-            margin-bottom: 28px;
-
-            li {
-                list-style: none;
-                padding-bottom: 4px;
-                margin-bottom: 4px;
-                border-bottom: 1px dashed #dddddd;
-                padding-left: 6px;
-            }
-        }
-
         span.status {
             display: inline-block;
             width: 12px;
@@ -1468,6 +1477,34 @@
         }
     }
 
+    .diff-stats {
+
+        clear: both;
+        margin-bottom: 5px;
+        max-height: 400px;
+        overflow: auto;
+        padding-left: 0;
+
+        li {
+            list-style: none;
+            padding-bottom: 4px;
+            margin-bottom: 4px;
+            border-bottom: 1px dashed #dddddd;
+            padding-left: 6px;
+        }
+
+        .diff-counter {
+            margin-right: 15px;
+
+            .del {
+                color: red;
+            }
+            .add {
+                color: green;
+            }
+        }
+    }
+
     .repo-search-result {
         padding-top: 10px;
         padding-bottom: 10px;
@@ -2385,3 +2422,26 @@ tbody.commit-list {
     padding-top: 8px;
     padding-bottom: 8px;
 }
+
+.issue-keyword {
+    border-bottom: 1px dotted #959da5;
+    display: inline-block;
+}
+
+.file-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding: 8px 12px !important;
+}
+
+.file-info {
+    display: flex;
+    align-items: center;
+}
+
+.file-info-entry + .file-info-entry {
+    border-left: 1px solid currentColor;
+    margin-left: 8px;
+    padding-left: 8px;
+}
diff --git a/public/vendor/VERSIONS b/public/vendor/VERSIONS
index 6c5f10424f..8afae309fe 100644
--- a/public/vendor/VERSIONS
+++ b/public/vendor/VERSIONS
@@ -42,7 +42,7 @@ File(s): /vendor/plugins/jquery.minicolors/jquery.minicolors.min.js
 Version: 2.2.3
 
 File(s): /vendor/plugins/codemirror/
-Version: 5.17.0
+Version: 5.49.0
 
 File(s): /vendor/plugins/simplemde/simplemde.min.js
 Version: 1.10.1
diff --git a/public/vendor/librejs.html b/public/vendor/librejs.html
index c17c2f14e8..336cdbb721 100644
--- a/public/vendor/librejs.html
+++ b/public/vendor/librejs.html
@@ -93,12 +93,12 @@
         <tr>
           <td><a href="./plugins/codemirror/addon/mode/loadmode.js">loadmode.js</a></td>
           <td><a href="https://github.com/codemirror/CodeMirror/blob/master/LICENSE">Expat</a></td>
-          <td><a href="https://github.com/codemirror/CodeMirror/archive/5.17.0.tar.gz">codemirror-5.17.0.tar.gz</a></td>
+          <td><a href="https://github.com/codemirror/CodeMirror/archive/5.49.0.tar.gz">codemirror-5.49.0.tar.gz</a></td>
         </tr>
         <tr>
           <td><a href="./plugins/codemirror/mode/meta.js">meta.js</a></td>
           <td><a href="https://github.com/codemirror/CodeMirror/blob/master/LICENSE">Expat</a></td>
-          <td><a href="https://github.com/codemirror/CodeMirror/archive/5.17.0.tar.gz">codemirror-5.17.0.tar.gz</a></td>
+          <td><a href="https://github.com/codemirror/CodeMirror/archive/5.49.0.tar.gz">codemirror-5.49.0.tar.gz</a></td>
         </tr>
         <tr>
           <td><a href="./plugins/simplemde/simplemde.min.js">simplemde.min.js</a></td>
diff --git a/public/vendor/plugins/codemirror/addon/comment/comment.js b/public/vendor/plugins/codemirror/addon/comment/comment.js
new file mode 100644
index 0000000000..8394e85a4d
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/comment/comment.js
@@ -0,0 +1,209 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  var noOptions = {};
+  var nonWS = /[^\s\u00a0]/;
+  var Pos = CodeMirror.Pos;
+
+  function firstNonWS(str) {
+    var found = str.search(nonWS);
+    return found == -1 ? 0 : found;
+  }
+
+  CodeMirror.commands.toggleComment = function(cm) {
+    cm.toggleComment();
+  };
+
+  CodeMirror.defineExtension("toggleComment", function(options) {
+    if (!options) options = noOptions;
+    var cm = this;
+    var minLine = Infinity, ranges = this.listSelections(), mode = null;
+    for (var i = ranges.length - 1; i >= 0; i--) {
+      var from = ranges[i].from(), to = ranges[i].to();
+      if (from.line >= minLine) continue;
+      if (to.line >= minLine) to = Pos(minLine, 0);
+      minLine = from.line;
+      if (mode == null) {
+        if (cm.uncomment(from, to, options)) mode = "un";
+        else { cm.lineComment(from, to, options); mode = "line"; }
+      } else if (mode == "un") {
+        cm.uncomment(from, to, options);
+      } else {
+        cm.lineComment(from, to, options);
+      }
+    }
+  });
+
+  // Rough heuristic to try and detect lines that are part of multi-line string
+  function probablyInsideString(cm, pos, line) {
+    return /\bstring\b/.test(cm.getTokenTypeAt(Pos(pos.line, 0))) && !/^[\'\"\`]/.test(line)
+  }
+
+  function getMode(cm, pos) {
+    var mode = cm.getMode()
+    return mode.useInnerComments === false || !mode.innerMode ? mode : cm.getModeAt(pos)
+  }
+
+  CodeMirror.defineExtension("lineComment", function(from, to, options) {
+    if (!options) options = noOptions;
+    var self = this, mode = getMode(self, from);
+    var firstLine = self.getLine(from.line);
+    if (firstLine == null || probablyInsideString(self, from, firstLine)) return;
+
+    var commentString = options.lineComment || mode.lineComment;
+    if (!commentString) {
+      if (options.blockCommentStart || mode.blockCommentStart) {
+        options.fullLines = true;
+        self.blockComment(from, to, options);
+      }
+      return;
+    }
+
+    var end = Math.min(to.ch != 0 || to.line == from.line ? to.line + 1 : to.line, self.lastLine() + 1);
+    var pad = options.padding == null ? " " : options.padding;
+    var blankLines = options.commentBlankLines || from.line == to.line;
+
+    self.operation(function() {
+      if (options.indent) {
+        var baseString = null;
+        for (var i = from.line; i < end; ++i) {
+          var line = self.getLine(i);
+          var whitespace = line.slice(0, firstNonWS(line));
+          if (baseString == null || baseString.length > whitespace.length) {
+            baseString = whitespace;
+          }
+        }
+        for (var i = from.line; i < end; ++i) {
+          var line = self.getLine(i), cut = baseString.length;
+          if (!blankLines && !nonWS.test(line)) continue;
+          if (line.slice(0, cut) != baseString) cut = firstNonWS(line);
+          self.replaceRange(baseString + commentString + pad, Pos(i, 0), Pos(i, cut));
+        }
+      } else {
+        for (var i = from.line; i < end; ++i) {
+          if (blankLines || nonWS.test(self.getLine(i)))
+            self.replaceRange(commentString + pad, Pos(i, 0));
+        }
+      }
+    });
+  });
+
+  CodeMirror.defineExtension("blockComment", function(from, to, options) {
+    if (!options) options = noOptions;
+    var self = this, mode = getMode(self, from);
+    var startString = options.blockCommentStart || mode.blockCommentStart;
+    var endString = options.blockCommentEnd || mode.blockCommentEnd;
+    if (!startString || !endString) {
+      if ((options.lineComment || mode.lineComment) && options.fullLines != false)
+        self.lineComment(from, to, options);
+      return;
+    }
+    if (/\bcomment\b/.test(self.getTokenTypeAt(Pos(from.line, 0)))) return
+
+    var end = Math.min(to.line, self.lastLine());
+    if (end != from.line && to.ch == 0 && nonWS.test(self.getLine(end))) --end;
+
+    var pad = options.padding == null ? " " : options.padding;
+    if (from.line > end) return;
+
+    self.operation(function() {
+      if (options.fullLines != false) {
+        var lastLineHasText = nonWS.test(self.getLine(end));
+        self.replaceRange(pad + endString, Pos(end));
+        self.replaceRange(startString + pad, Pos(from.line, 0));
+        var lead = options.blockCommentLead || mode.blockCommentLead;
+        if (lead != null) for (var i = from.line + 1; i <= end; ++i)
+          if (i != end || lastLineHasText)
+            self.replaceRange(lead + pad, Pos(i, 0));
+      } else {
+        self.replaceRange(endString, to);
+        self.replaceRange(startString, from);
+      }
+    });
+  });
+
+  CodeMirror.defineExtension("uncomment", function(from, to, options) {
+    if (!options) options = noOptions;
+    var self = this, mode = getMode(self, from);
+    var end = Math.min(to.ch != 0 || to.line == from.line ? to.line : to.line - 1, self.lastLine()), start = Math.min(from.line, end);
+
+    // Try finding line comments
+    var lineString = options.lineComment || mode.lineComment, lines = [];
+    var pad = options.padding == null ? " " : options.padding, didSomething;
+    lineComment: {
+      if (!lineString) break lineComment;
+      for (var i = start; i <= end; ++i) {
+        var line = self.getLine(i);
+        var found = line.indexOf(lineString);
+        if (found > -1 && !/comment/.test(self.getTokenTypeAt(Pos(i, found + 1)))) found = -1;
+        if (found == -1 && nonWS.test(line)) break lineComment;
+        if (found > -1 && nonWS.test(line.slice(0, found))) break lineComment;
+        lines.push(line);
+      }
+      self.operation(function() {
+        for (var i = start; i <= end; ++i) {
+          var line = lines[i - start];
+          var pos = line.indexOf(lineString), endPos = pos + lineString.length;
+          if (pos < 0) continue;
+          if (line.slice(endPos, endPos + pad.length) == pad) endPos += pad.length;
+          didSomething = true;
+          self.replaceRange("", Pos(i, pos), Pos(i, endPos));
+        }
+      });
+      if (didSomething) return true;
+    }
+
+    // Try block comments
+    var startString = options.blockCommentStart || mode.blockCommentStart;
+    var endString = options.blockCommentEnd || mode.blockCommentEnd;
+    if (!startString || !endString) return false;
+    var lead = options.blockCommentLead || mode.blockCommentLead;
+    var startLine = self.getLine(start), open = startLine.indexOf(startString)
+    if (open == -1) return false
+    var endLine = end == start ? startLine : self.getLine(end)
+    var close = endLine.indexOf(endString, end == start ? open + startString.length : 0);
+    var insideStart = Pos(start, open + 1), insideEnd = Pos(end, close + 1)
+    if (close == -1 ||
+        !/comment/.test(self.getTokenTypeAt(insideStart)) ||
+        !/comment/.test(self.getTokenTypeAt(insideEnd)) ||
+        self.getRange(insideStart, insideEnd, "\n").indexOf(endString) > -1)
+      return false;
+
+    // Avoid killing block comments completely outside the selection.
+    // Positions of the last startString before the start of the selection, and the first endString after it.
+    var lastStart = startLine.lastIndexOf(startString, from.ch);
+    var firstEnd = lastStart == -1 ? -1 : startLine.slice(0, from.ch).indexOf(endString, lastStart + startString.length);
+    if (lastStart != -1 && firstEnd != -1 && firstEnd + endString.length != from.ch) return false;
+    // Positions of the first endString after the end of the selection, and the last startString before it.
+    firstEnd = endLine.indexOf(endString, to.ch);
+    var almostLastStart = endLine.slice(to.ch).lastIndexOf(startString, firstEnd - to.ch);
+    lastStart = (firstEnd == -1 || almostLastStart == -1) ? -1 : to.ch + almostLastStart;
+    if (firstEnd != -1 && lastStart != -1 && lastStart != to.ch) return false;
+
+    self.operation(function() {
+      self.replaceRange("", Pos(end, close - (pad && endLine.slice(close - pad.length, close) == pad ? pad.length : 0)),
+                        Pos(end, close + endString.length));
+      var openEnd = open + startString.length;
+      if (pad && startLine.slice(openEnd, openEnd + pad.length) == pad) openEnd += pad.length;
+      self.replaceRange("", Pos(start, open), Pos(start, openEnd));
+      if (lead) for (var i = start + 1; i <= end; ++i) {
+        var line = self.getLine(i), found = line.indexOf(lead);
+        if (found == -1 || nonWS.test(line.slice(0, found))) continue;
+        var foundEnd = found + lead.length;
+        if (pad && line.slice(foundEnd, foundEnd + pad.length) == pad) foundEnd += pad.length;
+        self.replaceRange("", Pos(i, found), Pos(i, foundEnd));
+      }
+    });
+    return true;
+  });
+});
diff --git a/public/vendor/plugins/codemirror/addon/comment/continuecomment.js b/public/vendor/plugins/codemirror/addon/comment/continuecomment.js
new file mode 100644
index 0000000000..a5f957b73b
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/comment/continuecomment.js
@@ -0,0 +1,78 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  function continueComment(cm) {
+    if (cm.getOption("disableInput")) return CodeMirror.Pass;
+    var ranges = cm.listSelections(), mode, inserts = [];
+    for (var i = 0; i < ranges.length; i++) {
+      var pos = ranges[i].head
+      if (!/\bcomment\b/.test(cm.getTokenTypeAt(pos))) return CodeMirror.Pass;
+      var modeHere = cm.getModeAt(pos)
+      if (!mode) mode = modeHere;
+      else if (mode != modeHere) return CodeMirror.Pass;
+
+      var insert = null;
+      if (mode.blockCommentStart && mode.blockCommentContinue) {
+        var line = cm.getLine(pos.line).slice(0, pos.ch)
+        var end = line.lastIndexOf(mode.blockCommentEnd), found
+        if (end != -1 && end == pos.ch - mode.blockCommentEnd.length) {
+          // Comment ended, don't continue it
+        } else if ((found = line.lastIndexOf(mode.blockCommentStart)) > -1 && found > end) {
+          insert = line.slice(0, found)
+          if (/\S/.test(insert)) {
+            insert = ""
+            for (var j = 0; j < found; ++j) insert += " "
+          }
+        } else if ((found = line.indexOf(mode.blockCommentContinue)) > -1 && !/\S/.test(line.slice(0, found))) {
+          insert = line.slice(0, found)
+        }
+        if (insert != null) insert += mode.blockCommentContinue
+      }
+      if (insert == null && mode.lineComment && continueLineCommentEnabled(cm)) {
+        var line = cm.getLine(pos.line), found = line.indexOf(mode.lineComment);
+        if (found > -1) {
+          insert = line.slice(0, found);
+          if (/\S/.test(insert)) insert = null;
+          else insert += mode.lineComment + line.slice(found + mode.lineComment.length).match(/^\s*/)[0];
+        }
+      }
+      if (insert == null) return CodeMirror.Pass;
+      inserts[i] = "\n" + insert;
+    }
+
+    cm.operation(function() {
+      for (var i = ranges.length - 1; i >= 0; i--)
+        cm.replaceRange(inserts[i], ranges[i].from(), ranges[i].to(), "+insert");
+    });
+  }
+
+  function continueLineCommentEnabled(cm) {
+    var opt = cm.getOption("continueComments");
+    if (opt && typeof opt == "object")
+      return opt.continueLineComment !== false;
+    return true;
+  }
+
+  CodeMirror.defineOption("continueComments", null, function(cm, val, prev) {
+    if (prev && prev != CodeMirror.Init)
+      cm.removeKeyMap("continueComment");
+    if (val) {
+      var key = "Enter";
+      if (typeof val == "string")
+        key = val;
+      else if (typeof val == "object" && val.key)
+        key = val.key;
+      var map = {name: "continueComment"};
+      map[key] = continueComment;
+      cm.addKeyMap(map);
+    }
+  });
+});
diff --git a/public/vendor/plugins/codemirror/addon/dialog/dialog.css b/public/vendor/plugins/codemirror/addon/dialog/dialog.css
new file mode 100644
index 0000000000..677c078387
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/dialog/dialog.css
@@ -0,0 +1,32 @@
+.CodeMirror-dialog {
+  position: absolute;
+  left: 0; right: 0;
+  background: inherit;
+  z-index: 15;
+  padding: .1em .8em;
+  overflow: hidden;
+  color: inherit;
+}
+
+.CodeMirror-dialog-top {
+  border-bottom: 1px solid #eee;
+  top: 0;
+}
+
+.CodeMirror-dialog-bottom {
+  border-top: 1px solid #eee;
+  bottom: 0;
+}
+
+.CodeMirror-dialog input {
+  border: none;
+  outline: none;
+  background: transparent;
+  width: 20em;
+  color: inherit;
+  font-family: monospace;
+}
+
+.CodeMirror-dialog button {
+  font-size: 70%;
+}
diff --git a/public/vendor/plugins/codemirror/addon/dialog/dialog.js b/public/vendor/plugins/codemirror/addon/dialog/dialog.js
new file mode 100644
index 0000000000..23b06a8323
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/dialog/dialog.js
@@ -0,0 +1,161 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// Open simple dialogs on top of an editor. Relies on dialog.css.
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  function dialogDiv(cm, template, bottom) {
+    var wrap = cm.getWrapperElement();
+    var dialog;
+    dialog = wrap.appendChild(document.createElement("div"));
+    if (bottom)
+      dialog.className = "CodeMirror-dialog CodeMirror-dialog-bottom";
+    else
+      dialog.className = "CodeMirror-dialog CodeMirror-dialog-top";
+
+    if (typeof template == "string") {
+      dialog.innerHTML = template;
+    } else { // Assuming it's a detached DOM element.
+      dialog.appendChild(template);
+    }
+    CodeMirror.addClass(wrap, 'dialog-opened');
+    return dialog;
+  }
+
+  function closeNotification(cm, newVal) {
+    if (cm.state.currentNotificationClose)
+      cm.state.currentNotificationClose();
+    cm.state.currentNotificationClose = newVal;
+  }
+
+  CodeMirror.defineExtension("openDialog", function(template, callback, options) {
+    if (!options) options = {};
+
+    closeNotification(this, null);
+
+    var dialog = dialogDiv(this, template, options.bottom);
+    var closed = false, me = this;
+    function close(newVal) {
+      if (typeof newVal == 'string') {
+        inp.value = newVal;
+      } else {
+        if (closed) return;
+        closed = true;
+        CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');
+        dialog.parentNode.removeChild(dialog);
+        me.focus();
+
+        if (options.onClose) options.onClose(dialog);
+      }
+    }
+
+    var inp = dialog.getElementsByTagName("input")[0], button;
+    if (inp) {
+      inp.focus();
+
+      if (options.value) {
+        inp.value = options.value;
+        if (options.selectValueOnOpen !== false) {
+          inp.select();
+        }
+      }
+
+      if (options.onInput)
+        CodeMirror.on(inp, "input", function(e) { options.onInput(e, inp.value, close);});
+      if (options.onKeyUp)
+        CodeMirror.on(inp, "keyup", function(e) {options.onKeyUp(e, inp.value, close);});
+
+      CodeMirror.on(inp, "keydown", function(e) {
+        if (options && options.onKeyDown && options.onKeyDown(e, inp.value, close)) { return; }
+        if (e.keyCode == 27 || (options.closeOnEnter !== false && e.keyCode == 13)) {
+          inp.blur();
+          CodeMirror.e_stop(e);
+          close();
+        }
+        if (e.keyCode == 13) callback(inp.value, e);
+      });
+
+      if (options.closeOnBlur !== false) CodeMirror.on(inp, "blur", close);
+    } else if (button = dialog.getElementsByTagName("button")[0]) {
+      CodeMirror.on(button, "click", function() {
+        close();
+        me.focus();
+      });
+
+      if (options.closeOnBlur !== false) CodeMirror.on(button, "blur", close);
+
+      button.focus();
+    }
+    return close;
+  });
+
+  CodeMirror.defineExtension("openConfirm", function(template, callbacks, options) {
+    closeNotification(this, null);
+    var dialog = dialogDiv(this, template, options && options.bottom);
+    var buttons = dialog.getElementsByTagName("button");
+    var closed = false, me = this, blurring = 1;
+    function close() {
+      if (closed) return;
+      closed = true;
+      CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');
+      dialog.parentNode.removeChild(dialog);
+      me.focus();
+    }
+    buttons[0].focus();
+    for (var i = 0; i < buttons.length; ++i) {
+      var b = buttons[i];
+      (function(callback) {
+        CodeMirror.on(b, "click", function(e) {
+          CodeMirror.e_preventDefault(e);
+          close();
+          if (callback) callback(me);
+        });
+      })(callbacks[i]);
+      CodeMirror.on(b, "blur", function() {
+        --blurring;
+        setTimeout(function() { if (blurring <= 0) close(); }, 200);
+      });
+      CodeMirror.on(b, "focus", function() { ++blurring; });
+    }
+  });
+
+  /*
+   * openNotification
+   * Opens a notification, that can be closed with an optional timer
+   * (default 5000ms timer) and always closes on click.
+   *
+   * If a notification is opened while another is opened, it will close the
+   * currently opened one and open the new one immediately.
+   */
+  CodeMirror.defineExtension("openNotification", function(template, options) {
+    closeNotification(this, close);
+    var dialog = dialogDiv(this, template, options && options.bottom);
+    var closed = false, doneTimer;
+    var duration = options && typeof options.duration !== "undefined" ? options.duration : 5000;
+
+    function close() {
+      if (closed) return;
+      closed = true;
+      clearTimeout(doneTimer);
+      CodeMirror.rmClass(dialog.parentNode, 'dialog-opened');
+      dialog.parentNode.removeChild(dialog);
+    }
+
+    CodeMirror.on(dialog, 'click', function(e) {
+      CodeMirror.e_preventDefault(e);
+      close();
+    });
+
+    if (duration)
+      doneTimer = setTimeout(close, duration);
+
+    return close;
+  });
+});
diff --git a/public/vendor/plugins/codemirror/addon/display/autorefresh.js b/public/vendor/plugins/codemirror/addon/display/autorefresh.js
new file mode 100644
index 0000000000..37014dc31d
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/display/autorefresh.js
@@ -0,0 +1,47 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"))
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod)
+  else // Plain browser env
+    mod(CodeMirror)
+})(function(CodeMirror) {
+  "use strict"
+
+  CodeMirror.defineOption("autoRefresh", false, function(cm, val) {
+    if (cm.state.autoRefresh) {
+      stopListening(cm, cm.state.autoRefresh)
+      cm.state.autoRefresh = null
+    }
+    if (val && cm.display.wrapper.offsetHeight == 0)
+      startListening(cm, cm.state.autoRefresh = {delay: val.delay || 250})
+  })
+
+  function startListening(cm, state) {
+    function check() {
+      if (cm.display.wrapper.offsetHeight) {
+        stopListening(cm, state)
+        if (cm.display.lastWrapHeight != cm.display.wrapper.clientHeight)
+          cm.refresh()
+      } else {
+        state.timeout = setTimeout(check, state.delay)
+      }
+    }
+    state.timeout = setTimeout(check, state.delay)
+    state.hurry = function() {
+      clearTimeout(state.timeout)
+      state.timeout = setTimeout(check, 50)
+    }
+    CodeMirror.on(window, "mouseup", state.hurry)
+    CodeMirror.on(window, "keyup", state.hurry)
+  }
+
+  function stopListening(_cm, state) {
+    clearTimeout(state.timeout)
+    CodeMirror.off(window, "mouseup", state.hurry)
+    CodeMirror.off(window, "keyup", state.hurry)
+  }
+});
diff --git a/public/vendor/plugins/codemirror/addon/display/fullscreen.css b/public/vendor/plugins/codemirror/addon/display/fullscreen.css
new file mode 100644
index 0000000000..437acd89be
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/display/fullscreen.css
@@ -0,0 +1,6 @@
+.CodeMirror-fullscreen {
+  position: fixed;
+  top: 0; left: 0; right: 0; bottom: 0;
+  height: auto;
+  z-index: 9;
+}
diff --git a/public/vendor/plugins/codemirror/addon/display/fullscreen.js b/public/vendor/plugins/codemirror/addon/display/fullscreen.js
new file mode 100644
index 0000000000..eda7300f12
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/display/fullscreen.js
@@ -0,0 +1,41 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  CodeMirror.defineOption("fullScreen", false, function(cm, val, old) {
+    if (old == CodeMirror.Init) old = false;
+    if (!old == !val) return;
+    if (val) setFullscreen(cm);
+    else setNormal(cm);
+  });
+
+  function setFullscreen(cm) {
+    var wrap = cm.getWrapperElement();
+    cm.state.fullScreenRestore = {scrollTop: window.pageYOffset, scrollLeft: window.pageXOffset,
+                                  width: wrap.style.width, height: wrap.style.height};
+    wrap.style.width = "";
+    wrap.style.height = "auto";
+    wrap.className += " CodeMirror-fullscreen";
+    document.documentElement.style.overflow = "hidden";
+    cm.refresh();
+  }
+
+  function setNormal(cm) {
+    var wrap = cm.getWrapperElement();
+    wrap.className = wrap.className.replace(/\s*CodeMirror-fullscreen\b/, "");
+    document.documentElement.style.overflow = "";
+    var info = cm.state.fullScreenRestore;
+    wrap.style.width = info.width; wrap.style.height = info.height;
+    window.scrollTo(info.scrollLeft, info.scrollTop);
+    cm.refresh();
+  }
+});
diff --git a/public/vendor/plugins/codemirror/addon/display/panel.js b/public/vendor/plugins/codemirror/addon/display/panel.js
new file mode 100644
index 0000000000..5faf1d560e
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/display/panel.js
@@ -0,0 +1,127 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  CodeMirror.defineExtension("addPanel", function(node, options) {
+    options = options || {};
+
+    if (!this.state.panels) initPanels(this);
+
+    var info = this.state.panels;
+    var wrapper = info.wrapper;
+    var cmWrapper = this.getWrapperElement();
+    var replace = options.replace instanceof Panel && !options.replace.cleared;
+
+    if (options.after instanceof Panel && !options.after.cleared) {
+      wrapper.insertBefore(node, options.before.node.nextSibling);
+    } else if (options.before instanceof Panel && !options.before.cleared) {
+      wrapper.insertBefore(node, options.before.node);
+    } else if (replace) {
+      wrapper.insertBefore(node, options.replace.node);
+      info.panels++;
+      options.replace.clear();
+    } else if (options.position == "bottom") {
+      wrapper.appendChild(node);
+    } else if (options.position == "before-bottom") {
+      wrapper.insertBefore(node, cmWrapper.nextSibling);
+    } else if (options.position == "after-top") {
+      wrapper.insertBefore(node, cmWrapper);
+    } else {
+      wrapper.insertBefore(node, wrapper.firstChild);
+    }
+
+    var height = (options && options.height) || node.offsetHeight;
+    this._setSize(null, info.heightLeft -= height);
+    if (!replace) {
+      info.panels++;
+    }
+    if (options.stable && isAtTop(this, node))
+      this.scrollTo(null, this.getScrollInfo().top + height)
+
+    return new Panel(this, node, options, height);
+  });
+
+  function Panel(cm, node, options, height) {
+    this.cm = cm;
+    this.node = node;
+    this.options = options;
+    this.height = height;
+    this.cleared = false;
+  }
+
+  Panel.prototype.clear = function() {
+    if (this.cleared) return;
+    this.cleared = true;
+    var info = this.cm.state.panels;
+    this.cm._setSize(null, info.heightLeft += this.height);
+    if (this.options.stable && isAtTop(this.cm, this.node))
+      this.cm.scrollTo(null, this.cm.getScrollInfo().top - this.height)
+    info.wrapper.removeChild(this.node);
+    if (--info.panels == 0) removePanels(this.cm);
+  };
+
+  Panel.prototype.changed = function(height) {
+    var newHeight = height == null ? this.node.offsetHeight : height;
+    var info = this.cm.state.panels;
+    this.cm._setSize(null, info.heightLeft -= (newHeight - this.height));
+    this.height = newHeight;
+  };
+
+  function initPanels(cm) {
+    var wrap = cm.getWrapperElement();
+    var style = window.getComputedStyle ? window.getComputedStyle(wrap) : wrap.currentStyle;
+    var height = parseInt(style.height);
+    var info = cm.state.panels = {
+      setHeight: wrap.style.height,
+      heightLeft: height,
+      panels: 0,
+      wrapper: document.createElement("div")
+    };
+    wrap.parentNode.insertBefore(info.wrapper, wrap);
+    var hasFocus = cm.hasFocus();
+    info.wrapper.appendChild(wrap);
+    if (hasFocus) cm.focus();
+
+    cm._setSize = cm.setSize;
+    if (height != null) cm.setSize = function(width, newHeight) {
+      if (newHeight == null) return this._setSize(width, newHeight);
+      info.setHeight = newHeight;
+      if (typeof newHeight != "number") {
+        var px = /^(\d+\.?\d*)px$/.exec(newHeight);
+        if (px) {
+          newHeight = Number(px[1]);
+        } else {
+          info.wrapper.style.height = newHeight;
+          newHeight = info.wrapper.offsetHeight;
+          info.wrapper.style.height = "";
+        }
+      }
+      cm._setSize(width, info.heightLeft += (newHeight - height));
+      height = newHeight;
+    };
+  }
+
+  function removePanels(cm) {
+    var info = cm.state.panels;
+    cm.state.panels = null;
+
+    var wrap = cm.getWrapperElement();
+    info.wrapper.parentNode.replaceChild(wrap, info.wrapper);
+    wrap.style.height = info.setHeight;
+    cm.setSize = cm._setSize;
+    cm.setSize();
+  }
+
+  function isAtTop(cm, dom) {
+    for (var sibling = dom.nextSibling; sibling; sibling = sibling.nextSibling)
+      if (sibling == cm.getWrapperElement()) return true
+    return false
+  }
+});
diff --git a/public/vendor/plugins/codemirror/addon/display/placeholder.js b/public/vendor/plugins/codemirror/addon/display/placeholder.js
new file mode 100644
index 0000000000..4eabe3d901
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/display/placeholder.js
@@ -0,0 +1,63 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  CodeMirror.defineOption("placeholder", "", function(cm, val, old) {
+    var prev = old && old != CodeMirror.Init;
+    if (val && !prev) {
+      cm.on("blur", onBlur);
+      cm.on("change", onChange);
+      cm.on("swapDoc", onChange);
+      onChange(cm);
+    } else if (!val && prev) {
+      cm.off("blur", onBlur);
+      cm.off("change", onChange);
+      cm.off("swapDoc", onChange);
+      clearPlaceholder(cm);
+      var wrapper = cm.getWrapperElement();
+      wrapper.className = wrapper.className.replace(" CodeMirror-empty", "");
+    }
+
+    if (val && !cm.hasFocus()) onBlur(cm);
+  });
+
+  function clearPlaceholder(cm) {
+    if (cm.state.placeholder) {
+      cm.state.placeholder.parentNode.removeChild(cm.state.placeholder);
+      cm.state.placeholder = null;
+    }
+  }
+  function setPlaceholder(cm) {
+    clearPlaceholder(cm);
+    var elt = cm.state.placeholder = document.createElement("pre");
+    elt.style.cssText = "height: 0; overflow: visible";
+    elt.style.direction = cm.getOption("direction");
+    elt.className = "CodeMirror-placeholder CodeMirror-line-like";
+    var placeHolder = cm.getOption("placeholder")
+    if (typeof placeHolder == "string") placeHolder = document.createTextNode(placeHolder)
+    elt.appendChild(placeHolder)
+    cm.display.lineSpace.insertBefore(elt, cm.display.lineSpace.firstChild);
+  }
+
+  function onBlur(cm) {
+    if (isEmpty(cm)) setPlaceholder(cm);
+  }
+  function onChange(cm) {
+    var wrapper = cm.getWrapperElement(), empty = isEmpty(cm);
+    wrapper.className = wrapper.className.replace(" CodeMirror-empty", "") + (empty ? " CodeMirror-empty" : "");
+
+    if (empty) setPlaceholder(cm);
+    else clearPlaceholder(cm);
+  }
+
+  function isEmpty(cm) {
+    return (cm.lineCount() === 1) && (cm.getLine(0) === "");
+  }
+});
diff --git a/public/vendor/plugins/codemirror/addon/display/rulers.js b/public/vendor/plugins/codemirror/addon/display/rulers.js
new file mode 100644
index 0000000000..0bb83bb022
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/display/rulers.js
@@ -0,0 +1,51 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  CodeMirror.defineOption("rulers", false, function(cm, val) {
+    if (cm.state.rulerDiv) {
+      cm.state.rulerDiv.parentElement.removeChild(cm.state.rulerDiv)
+      cm.state.rulerDiv = null
+      cm.off("refresh", drawRulers)
+    }
+    if (val && val.length) {
+      cm.state.rulerDiv = cm.display.lineSpace.parentElement.insertBefore(document.createElement("div"), cm.display.lineSpace)
+      cm.state.rulerDiv.className = "CodeMirror-rulers"
+      drawRulers(cm)
+      cm.on("refresh", drawRulers)
+    }
+  });
+
+  function drawRulers(cm) {
+    cm.state.rulerDiv.textContent = ""
+    var val = cm.getOption("rulers");
+    var cw = cm.defaultCharWidth();
+    var left = cm.charCoords(CodeMirror.Pos(cm.firstLine(), 0), "div").left;
+    cm.state.rulerDiv.style.minHeight = (cm.display.scroller.offsetHeight + 30) + "px";
+    for (var i = 0; i < val.length; i++) {
+      var elt = document.createElement("div");
+      elt.className = "CodeMirror-ruler";
+      var col, conf = val[i];
+      if (typeof conf == "number") {
+        col = conf;
+      } else {
+        col = conf.column;
+        if (conf.className) elt.className += " " + conf.className;
+        if (conf.color) elt.style.borderColor = conf.color;
+        if (conf.lineStyle) elt.style.borderLeftStyle = conf.lineStyle;
+        if (conf.width) elt.style.borderLeftWidth = conf.width;
+      }
+      elt.style.left = (left + col * cw) + "px";
+      cm.state.rulerDiv.appendChild(elt)
+    }
+  }
+});
diff --git a/public/vendor/plugins/codemirror/addon/edit/closebrackets.js b/public/vendor/plugins/codemirror/addon/edit/closebrackets.js
new file mode 100644
index 0000000000..4415c39381
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/edit/closebrackets.js
@@ -0,0 +1,191 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  var defaults = {
+    pairs: "()[]{}''\"\"",
+    closeBefore: ")]}'\":;>",
+    triples: "",
+    explode: "[]{}"
+  };
+
+  var Pos = CodeMirror.Pos;
+
+  CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) {
+    if (old && old != CodeMirror.Init) {
+      cm.removeKeyMap(keyMap);
+      cm.state.closeBrackets = null;
+    }
+    if (val) {
+      ensureBound(getOption(val, "pairs"))
+      cm.state.closeBrackets = val;
+      cm.addKeyMap(keyMap);
+    }
+  });
+
+  function getOption(conf, name) {
+    if (name == "pairs" && typeof conf == "string") return conf;
+    if (typeof conf == "object" && conf[name] != null) return conf[name];
+    return defaults[name];
+  }
+
+  var keyMap = {Backspace: handleBackspace, Enter: handleEnter};
+  function ensureBound(chars) {
+    for (var i = 0; i < chars.length; i++) {
+      var ch = chars.charAt(i), key = "'" + ch + "'"
+      if (!keyMap[key]) keyMap[key] = handler(ch)
+    }
+  }
+  ensureBound(defaults.pairs + "`")
+
+  function handler(ch) {
+    return function(cm) { return handleChar(cm, ch); };
+  }
+
+  function getConfig(cm) {
+    var deflt = cm.state.closeBrackets;
+    if (!deflt || deflt.override) return deflt;
+    var mode = cm.getModeAt(cm.getCursor());
+    return mode.closeBrackets || deflt;
+  }
+
+  function handleBackspace(cm) {
+    var conf = getConfig(cm);
+    if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
+
+    var pairs = getOption(conf, "pairs");
+    var ranges = cm.listSelections();
+    for (var i = 0; i < ranges.length; i++) {
+      if (!ranges[i].empty()) return CodeMirror.Pass;
+      var around = charsAround(cm, ranges[i].head);
+      if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass;
+    }
+    for (var i = ranges.length - 1; i >= 0; i--) {
+      var cur = ranges[i].head;
+      cm.replaceRange("", Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1), "+delete");
+    }
+  }
+
+  function handleEnter(cm) {
+    var conf = getConfig(cm);
+    var explode = conf && getOption(conf, "explode");
+    if (!explode || cm.getOption("disableInput")) return CodeMirror.Pass;
+
+    var ranges = cm.listSelections();
+    for (var i = 0; i < ranges.length; i++) {
+      if (!ranges[i].empty()) return CodeMirror.Pass;
+      var around = charsAround(cm, ranges[i].head);
+      if (!around || explode.indexOf(around) % 2 != 0) return CodeMirror.Pass;
+    }
+    cm.operation(function() {
+      var linesep = cm.lineSeparator() || "\n";
+      cm.replaceSelection(linesep + linesep, null);
+      cm.execCommand("goCharLeft");
+      ranges = cm.listSelections();
+      for (var i = 0; i < ranges.length; i++) {
+        var line = ranges[i].head.line;
+        cm.indentLine(line, null, true);
+        cm.indentLine(line + 1, null, true);
+      }
+    });
+  }
+
+  function contractSelection(sel) {
+    var inverted = CodeMirror.cmpPos(sel.anchor, sel.head) > 0;
+    return {anchor: new Pos(sel.anchor.line, sel.anchor.ch + (inverted ? -1 : 1)),
+            head: new Pos(sel.head.line, sel.head.ch + (inverted ? 1 : -1))};
+  }
+
+  function handleChar(cm, ch) {
+    var conf = getConfig(cm);
+    if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
+
+    var pairs = getOption(conf, "pairs");
+    var pos = pairs.indexOf(ch);
+    if (pos == -1) return CodeMirror.Pass;
+
+    var closeBefore = getOption(conf,"closeBefore");
+
+    var triples = getOption(conf, "triples");
+
+    var identical = pairs.charAt(pos + 1) == ch;
+    var ranges = cm.listSelections();
+    var opening = pos % 2 == 0;
+
+    var type;
+    for (var i = 0; i < ranges.length; i++) {
+      var range = ranges[i], cur = range.head, curType;
+      var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1));
+      if (opening && !range.empty()) {
+        curType = "surround";
+      } else if ((identical || !opening) && next == ch) {
+        if (identical && stringStartsAfter(cm, cur))
+          curType = "both";
+        else if (triples.indexOf(ch) >= 0 && cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == ch + ch + ch)
+          curType = "skipThree";
+        else
+          curType = "skip";
+      } else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 &&
+                 cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch) {
+        if (cur.ch > 2 && /\bstring/.test(cm.getTokenTypeAt(Pos(cur.line, cur.ch - 2)))) return CodeMirror.Pass;
+        curType = "addFour";
+      } else if (identical) {
+        var prev = cur.ch == 0 ? " " : cm.getRange(Pos(cur.line, cur.ch - 1), cur)
+        if (!CodeMirror.isWordChar(next) && prev != ch && !CodeMirror.isWordChar(prev)) curType = "both";
+        else return CodeMirror.Pass;
+      } else if (opening && (next.length === 0 || /\s/.test(next) || closeBefore.indexOf(next) > -1)) {
+        curType = "both";
+      } else {
+        return CodeMirror.Pass;
+      }
+      if (!type) type = curType;
+      else if (type != curType) return CodeMirror.Pass;
+    }
+
+    var left = pos % 2 ? pairs.charAt(pos - 1) : ch;
+    var right = pos % 2 ? ch : pairs.charAt(pos + 1);
+    cm.operation(function() {
+      if (type == "skip") {
+        cm.execCommand("goCharRight");
+      } else if (type == "skipThree") {
+        for (var i = 0; i < 3; i++)
+          cm.execCommand("goCharRight");
+      } else if (type == "surround") {
+        var sels = cm.getSelections();
+        for (var i = 0; i < sels.length; i++)
+          sels[i] = left + sels[i] + right;
+        cm.replaceSelections(sels, "around");
+        sels = cm.listSelections().slice();
+        for (var i = 0; i < sels.length; i++)
+          sels[i] = contractSelection(sels[i]);
+        cm.setSelections(sels);
+      } else if (type == "both") {
+        cm.replaceSelection(left + right, null);
+        cm.triggerElectric(left + right);
+        cm.execCommand("goCharLeft");
+      } else if (type == "addFour") {
+        cm.replaceSelection(left + left + left + left, "before");
+        cm.execCommand("goCharRight");
+      }
+    });
+  }
+
+  function charsAround(cm, pos) {
+    var str = cm.getRange(Pos(pos.line, pos.ch - 1),
+                          Pos(pos.line, pos.ch + 1));
+    return str.length == 2 ? str : null;
+  }
+
+  function stringStartsAfter(cm, pos) {
+    var token = cm.getTokenAt(Pos(pos.line, pos.ch + 1))
+    return /\bstring/.test(token.type) && token.start == pos.ch &&
+      (pos.ch == 0 || !/\bstring/.test(cm.getTokenTypeAt(pos)))
+  }
+});
diff --git a/public/vendor/plugins/codemirror/addon/edit/closetag.js b/public/vendor/plugins/codemirror/addon/edit/closetag.js
new file mode 100644
index 0000000000..4f5aae49d9
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/edit/closetag.js
@@ -0,0 +1,184 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+/**
+ * Tag-closer extension for CodeMirror.
+ *
+ * This extension adds an "autoCloseTags" option that can be set to
+ * either true to get the default behavior, or an object to further
+ * configure its behavior.
+ *
+ * These are supported options:
+ *
+ * `whenClosing` (default true)
+ *   Whether to autoclose when the '/' of a closing tag is typed.
+ * `whenOpening` (default true)
+ *   Whether to autoclose the tag when the final '>' of an opening
+ *   tag is typed.
+ * `dontCloseTags` (default is empty tags for HTML, none for XML)
+ *   An array of tag names that should not be autoclosed.
+ * `indentTags` (default is block tags for HTML, none for XML)
+ *   An array of tag names that should, when opened, cause a
+ *   blank line to be added inside the tag, and the blank line and
+ *   closing line to be indented.
+ * `emptyTags` (default is none)
+ *   An array of XML tag names that should be autoclosed with '/>'.
+ *
+ * See demos/closetag.html for a usage example.
+ */
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror", "../fold/xml-fold"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  CodeMirror.defineOption("autoCloseTags", false, function(cm, val, old) {
+    if (old != CodeMirror.Init && old)
+      cm.removeKeyMap("autoCloseTags");
+    if (!val) return;
+    var map = {name: "autoCloseTags"};
+    if (typeof val != "object" || val.whenClosing)
+      map["'/'"] = function(cm) { return autoCloseSlash(cm); };
+    if (typeof val != "object" || val.whenOpening)
+      map["'>'"] = function(cm) { return autoCloseGT(cm); };
+    cm.addKeyMap(map);
+  });
+
+  var htmlDontClose = ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param",
+                       "source", "track", "wbr"];
+  var htmlIndent = ["applet", "blockquote", "body", "button", "div", "dl", "fieldset", "form", "frameset", "h1", "h2", "h3", "h4",
+                    "h5", "h6", "head", "html", "iframe", "layer", "legend", "object", "ol", "p", "select", "table", "ul"];
+
+  function autoCloseGT(cm) {
+    if (cm.getOption("disableInput")) return CodeMirror.Pass;
+    var ranges = cm.listSelections(), replacements = [];
+    var opt = cm.getOption("autoCloseTags");
+    for (var i = 0; i < ranges.length; i++) {
+      if (!ranges[i].empty()) return CodeMirror.Pass;
+      var pos = ranges[i].head, tok = cm.getTokenAt(pos);
+      var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
+      var tagInfo = inner.mode.xmlCurrentTag && inner.mode.xmlCurrentTag(state)
+      var tagName = tagInfo && tagInfo.name
+      if (!tagName) return CodeMirror.Pass
+
+      var html = inner.mode.configuration == "html";
+      var dontCloseTags = (typeof opt == "object" && opt.dontCloseTags) || (html && htmlDontClose);
+      var indentTags = (typeof opt == "object" && opt.indentTags) || (html && htmlIndent);
+
+      if (tok.end > pos.ch) tagName = tagName.slice(0, tagName.length - tok.end + pos.ch);
+      var lowerTagName = tagName.toLowerCase();
+      // Don't process the '>' at the end of an end-tag or self-closing tag
+      if (!tagName ||
+          tok.type == "string" && (tok.end != pos.ch || !/[\"\']/.test(tok.string.charAt(tok.string.length - 1)) || tok.string.length == 1) ||
+          tok.type == "tag" && tagInfo.close ||
+          tok.string.indexOf("/") == (tok.string.length - 1) || // match something like <someTagName />
+          dontCloseTags && indexOf(dontCloseTags, lowerTagName) > -1 ||
+          closingTagExists(cm, inner.mode.xmlCurrentContext && inner.mode.xmlCurrentContext(state) || [], tagName, pos, true))
+        return CodeMirror.Pass;
+
+      var emptyTags = typeof opt == "object" && opt.emptyTags;
+      if (emptyTags && indexOf(emptyTags, tagName) > -1) {
+        replacements[i] = { text: "/>", newPos: CodeMirror.Pos(pos.line, pos.ch + 2) };
+        continue;
+      }
+
+      var indent = indentTags && indexOf(indentTags, lowerTagName) > -1;
+      replacements[i] = {indent: indent,
+                         text: ">" + (indent ? "\n\n" : "") + "</" + tagName + ">",
+                         newPos: indent ? CodeMirror.Pos(pos.line + 1, 0) : CodeMirror.Pos(pos.line, pos.ch + 1)};
+    }
+
+    var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnAutoClose);
+    for (var i = ranges.length - 1; i >= 0; i--) {
+      var info = replacements[i];
+      cm.replaceRange(info.text, ranges[i].head, ranges[i].anchor, "+insert");
+      var sel = cm.listSelections().slice(0);
+      sel[i] = {head: info.newPos, anchor: info.newPos};
+      cm.setSelections(sel);
+      if (!dontIndentOnAutoClose && info.indent) {
+        cm.indentLine(info.newPos.line, null, true);
+        cm.indentLine(info.newPos.line + 1, null, true);
+      }
+    }
+  }
+
+  function autoCloseCurrent(cm, typingSlash) {
+    var ranges = cm.listSelections(), replacements = [];
+    var head = typingSlash ? "/" : "</";
+    var opt = cm.getOption("autoCloseTags");
+    var dontIndentOnAutoClose = (typeof opt == "object" && opt.dontIndentOnSlash);
+    for (var i = 0; i < ranges.length; i++) {
+      if (!ranges[i].empty()) return CodeMirror.Pass;
+      var pos = ranges[i].head, tok = cm.getTokenAt(pos);
+      var inner = CodeMirror.innerMode(cm.getMode(), tok.state), state = inner.state;
+      if (typingSlash && (tok.type == "string" || tok.string.charAt(0) != "<" ||
+                          tok.start != pos.ch - 1))
+        return CodeMirror.Pass;
+      // Kludge to get around the fact that we are not in XML mode
+      // when completing in JS/CSS snippet in htmlmixed mode. Does not
+      // work for other XML embedded languages (there is no general
+      // way to go from a mixed mode to its current XML state).
+      var replacement, mixed = inner.mode.name != "xml" && cm.getMode().name == "htmlmixed"
+      if (mixed && inner.mode.name == "javascript") {
+        replacement = head + "script";
+      } else if (mixed && inner.mode.name == "css") {
+        replacement = head + "style";
+      } else {
+        var context = inner.mode.xmlCurrentContext && inner.mode.xmlCurrentContext(state)
+        if (!context || (context.length && closingTagExists(cm, context, context[context.length - 1], pos)))
+          return CodeMirror.Pass;
+        replacement = head + context[context.length - 1]
+      }
+      if (cm.getLine(pos.line).charAt(tok.end) != ">") replacement += ">";
+      replacements[i] = replacement;
+    }
+    cm.replaceSelections(replacements);
+    ranges = cm.listSelections();
+    if (!dontIndentOnAutoClose) {
+        for (var i = 0; i < ranges.length; i++)
+            if (i == ranges.length - 1 || ranges[i].head.line < ranges[i + 1].head.line)
+                cm.indentLine(ranges[i].head.line);
+    }
+  }
+
+  function autoCloseSlash(cm) {
+    if (cm.getOption("disableInput")) return CodeMirror.Pass;
+    return autoCloseCurrent(cm, true);
+  }
+
+  CodeMirror.commands.closeTag = function(cm) { return autoCloseCurrent(cm); };
+
+  function indexOf(collection, elt) {
+    if (collection.indexOf) return collection.indexOf(elt);
+    for (var i = 0, e = collection.length; i < e; ++i)
+      if (collection[i] == elt) return i;
+    return -1;
+  }
+
+  // If xml-fold is loaded, we use its functionality to try and verify
+  // whether a given tag is actually unclosed.
+  function closingTagExists(cm, context, tagName, pos, newTag) {
+    if (!CodeMirror.scanForClosingTag) return false;
+    var end = Math.min(cm.lastLine() + 1, pos.line + 500);
+    var nextClose = CodeMirror.scanForClosingTag(cm, pos, null, end);
+    if (!nextClose || nextClose.tag != tagName) return false;
+    // If the immediate wrapping context contains onCx instances of
+    // the same tag, a closing tag only exists if there are at least
+    // that many closing tags of that type following.
+    var onCx = newTag ? 1 : 0
+    for (var i = context.length - 1; i >= 0; i--) {
+      if (context[i] == tagName) ++onCx
+      else break
+    }
+    pos = nextClose.to;
+    for (var i = 1; i < onCx; i++) {
+      var next = CodeMirror.scanForClosingTag(cm, pos, null, end);
+      if (!next || next.tag != tagName) return false;
+      pos = next.to;
+    }
+    return true;
+  }
+});
diff --git a/public/vendor/plugins/codemirror/addon/edit/continuelist.js b/public/vendor/plugins/codemirror/addon/edit/continuelist.js
new file mode 100644
index 0000000000..fb5f03735d
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/edit/continuelist.js
@@ -0,0 +1,99 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  var listRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]\s|[*+-]\s|(\d+)([.)]))(\s*)/,
+      emptyListRE = /^(\s*)(>[> ]*|[*+-] \[[x ]\]|[*+-]|(\d+)[.)])(\s*)$/,
+      unorderedListRE = /[*+-]\s/;
+
+  CodeMirror.commands.newlineAndIndentContinueMarkdownList = function(cm) {
+    if (cm.getOption("disableInput")) return CodeMirror.Pass;
+    var ranges = cm.listSelections(), replacements = [];
+    for (var i = 0; i < ranges.length; i++) {
+      var pos = ranges[i].head;
+
+      // If we're not in Markdown mode, fall back to normal newlineAndIndent
+      var eolState = cm.getStateAfter(pos.line);
+      var inner = CodeMirror.innerMode(cm.getMode(), eolState);
+      if (inner.mode.name !== "markdown") {
+        cm.execCommand("newlineAndIndent");
+        return;
+      } else {
+        eolState = inner.state;
+      }
+
+      var inList = eolState.list !== false;
+      var inQuote = eolState.quote !== 0;
+
+      var line = cm.getLine(pos.line), match = listRE.exec(line);
+      var cursorBeforeBullet = /^\s*$/.test(line.slice(0, pos.ch));
+      if (!ranges[i].empty() || (!inList && !inQuote) || !match || cursorBeforeBullet) {
+        cm.execCommand("newlineAndIndent");
+        return;
+      }
+      if (emptyListRE.test(line)) {
+        if (!/>\s*$/.test(line)) cm.replaceRange("", {
+          line: pos.line, ch: 0
+        }, {
+          line: pos.line, ch: pos.ch + 1
+        });
+        replacements[i] = "\n";
+      } else {
+        var indent = match[1], after = match[5];
+        var numbered = !(unorderedListRE.test(match[2]) || match[2].indexOf(">") >= 0);
+        var bullet = numbered ? (parseInt(match[3], 10) + 1) + match[4] : match[2].replace("x", " ");
+        replacements[i] = "\n" + indent + bullet + after;
+
+        if (numbered) incrementRemainingMarkdownListNumbers(cm, pos);
+      }
+    }
+
+    cm.replaceSelections(replacements);
+  };
+
+  // Auto-updating Markdown list numbers when a new item is added to the
+  // middle of a list
+  function incrementRemainingMarkdownListNumbers(cm, pos) {
+    var startLine = pos.line, lookAhead = 0, skipCount = 0;
+    var startItem = listRE.exec(cm.getLine(startLine)), startIndent = startItem[1];
+
+    do {
+      lookAhead += 1;
+      var nextLineNumber = startLine + lookAhead;
+      var nextLine = cm.getLine(nextLineNumber), nextItem = listRE.exec(nextLine);
+
+      if (nextItem) {
+        var nextIndent = nextItem[1];
+        var newNumber = (parseInt(startItem[3], 10) + lookAhead - skipCount);
+        var nextNumber = (parseInt(nextItem[3], 10)), itemNumber = nextNumber;
+
+        if (startIndent === nextIndent && !isNaN(nextNumber)) {
+          if (newNumber === nextNumber) itemNumber = nextNumber + 1;
+          if (newNumber > nextNumber) itemNumber = newNumber + 1;
+          cm.replaceRange(
+            nextLine.replace(listRE, nextIndent + itemNumber + nextItem[4] + nextItem[5]),
+          {
+            line: nextLineNumber, ch: 0
+          }, {
+            line: nextLineNumber, ch: nextLine.length
+          });
+        } else {
+          if (startIndent.length > nextIndent.length) return;
+          // This doesn't run if the next line immediatley indents, as it is
+          // not clear of the users intention (new indented item or same level)
+          if ((startIndent.length < nextIndent.length) && (lookAhead === 1)) return;
+          skipCount += 1;
+        }
+      }
+    } while (nextItem);
+  }
+});
diff --git a/public/vendor/plugins/codemirror/addon/edit/matchbrackets.js b/public/vendor/plugins/codemirror/addon/edit/matchbrackets.js
new file mode 100644
index 0000000000..2a147282c4
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/edit/matchbrackets.js
@@ -0,0 +1,150 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  var ie_lt8 = /MSIE \d/.test(navigator.userAgent) &&
+    (document.documentMode == null || document.documentMode < 8);
+
+  var Pos = CodeMirror.Pos;
+
+  var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<", "<": ">>", ">": "<<"};
+
+  function bracketRegex(config) {
+    return config && config.bracketRegex || /[(){}[\]]/
+  }
+
+  function findMatchingBracket(cm, where, config) {
+    var line = cm.getLineHandle(where.line), pos = where.ch - 1;
+    var afterCursor = config && config.afterCursor
+    if (afterCursor == null)
+      afterCursor = /(^| )cm-fat-cursor($| )/.test(cm.getWrapperElement().className)
+    var re = bracketRegex(config)
+
+    // A cursor is defined as between two characters, but in in vim command mode
+    // (i.e. not insert mode), the cursor is visually represented as a
+    // highlighted box on top of the 2nd character. Otherwise, we allow matches
+    // from before or after the cursor.
+    var match = (!afterCursor && pos >= 0 && re.test(line.text.charAt(pos)) && matching[line.text.charAt(pos)]) ||
+        re.test(line.text.charAt(pos + 1)) && matching[line.text.charAt(++pos)];
+    if (!match) return null;
+    var dir = match.charAt(1) == ">" ? 1 : -1;
+    if (config && config.strict && (dir > 0) != (pos == where.ch)) return null;
+    var style = cm.getTokenTypeAt(Pos(where.line, pos + 1));
+
+    var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style || null, config);
+    if (found == null) return null;
+    return {from: Pos(where.line, pos), to: found && found.pos,
+            match: found && found.ch == match.charAt(0), forward: dir > 0};
+  }
+
+  // bracketRegex is used to specify which type of bracket to scan
+  // should be a regexp, e.g. /[[\]]/
+  //
+  // Note: If "where" is on an open bracket, then this bracket is ignored.
+  //
+  // Returns false when no bracket was found, null when it reached
+  // maxScanLines and gave up
+  function scanForBracket(cm, where, dir, style, config) {
+    var maxScanLen = (config && config.maxScanLineLength) || 10000;
+    var maxScanLines = (config && config.maxScanLines) || 1000;
+
+    var stack = [];
+    var re = bracketRegex(config)
+    var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1)
+                          : Math.max(cm.firstLine() - 1, where.line - maxScanLines);
+    for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) {
+      var line = cm.getLine(lineNo);
+      if (!line) continue;
+      var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1;
+      if (line.length > maxScanLen) continue;
+      if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0);
+      for (; pos != end; pos += dir) {
+        var ch = line.charAt(pos);
+        if (re.test(ch) && (style === undefined || cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style)) {
+          var match = matching[ch];
+          if (match && (match.charAt(1) == ">") == (dir > 0)) stack.push(ch);
+          else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch};
+          else stack.pop();
+        }
+      }
+    }
+    return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null;
+  }
+
+  function matchBrackets(cm, autoclear, config) {
+    // Disable brace matching in long lines, since it'll cause hugely slow updates
+    var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000;
+    var marks = [], ranges = cm.listSelections();
+    for (var i = 0; i < ranges.length; i++) {
+      var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, config);
+      if (match && cm.getLine(match.from.line).length <= maxHighlightLen) {
+        var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
+        marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style}));
+        if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen)
+          marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style}));
+      }
+    }
+
+    if (marks.length) {
+      // Kludge to work around the IE bug from issue #1193, where text
+      // input stops going to the textare whever this fires.
+      if (ie_lt8 && cm.state.focused) cm.focus();
+
+      var clear = function() {
+        cm.operation(function() {
+          for (var i = 0; i < marks.length; i++) marks[i].clear();
+        });
+      };
+      if (autoclear) setTimeout(clear, 800);
+      else return clear;
+    }
+  }
+
+  function doMatchBrackets(cm) {
+    cm.operation(function() {
+      if (cm.state.matchBrackets.currentlyHighlighted) {
+        cm.state.matchBrackets.currentlyHighlighted();
+        cm.state.matchBrackets.currentlyHighlighted = null;
+      }
+      cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets);
+    });
+  }
+
+  CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) {
+    if (old && old != CodeMirror.Init) {
+      cm.off("cursorActivity", doMatchBrackets);
+      if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) {
+        cm.state.matchBrackets.currentlyHighlighted();
+        cm.state.matchBrackets.currentlyHighlighted = null;
+      }
+    }
+    if (val) {
+      cm.state.matchBrackets = typeof val == "object" ? val : {};
+      cm.on("cursorActivity", doMatchBrackets);
+    }
+  });
+
+  CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
+  CodeMirror.defineExtension("findMatchingBracket", function(pos, config, oldConfig){
+    // Backwards-compatibility kludge
+    if (oldConfig || typeof config == "boolean") {
+      if (!oldConfig) {
+        config = config ? {strict: true} : null
+      } else {
+        oldConfig.strict = config
+        config = oldConfig
+      }
+    }
+    return findMatchingBracket(this, pos, config)
+  });
+  CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){
+    return scanForBracket(this, pos, dir, style, config);
+  });
+});
diff --git a/public/vendor/plugins/codemirror/addon/edit/matchtags.js b/public/vendor/plugins/codemirror/addon/edit/matchtags.js
new file mode 100644
index 0000000000..2203d9390d
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/edit/matchtags.js
@@ -0,0 +1,66 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"), require("../fold/xml-fold"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror", "../fold/xml-fold"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  CodeMirror.defineOption("matchTags", false, function(cm, val, old) {
+    if (old && old != CodeMirror.Init) {
+      cm.off("cursorActivity", doMatchTags);
+      cm.off("viewportChange", maybeUpdateMatch);
+      clear(cm);
+    }
+    if (val) {
+      cm.state.matchBothTags = typeof val == "object" && val.bothTags;
+      cm.on("cursorActivity", doMatchTags);
+      cm.on("viewportChange", maybeUpdateMatch);
+      doMatchTags(cm);
+    }
+  });
+
+  function clear(cm) {
+    if (cm.state.tagHit) cm.state.tagHit.clear();
+    if (cm.state.tagOther) cm.state.tagOther.clear();
+    cm.state.tagHit = cm.state.tagOther = null;
+  }
+
+  function doMatchTags(cm) {
+    cm.state.failedTagMatch = false;
+    cm.operation(function() {
+      clear(cm);
+      if (cm.somethingSelected()) return;
+      var cur = cm.getCursor(), range = cm.getViewport();
+      range.from = Math.min(range.from, cur.line); range.to = Math.max(cur.line + 1, range.to);
+      var match = CodeMirror.findMatchingTag(cm, cur, range);
+      if (!match) return;
+      if (cm.state.matchBothTags) {
+        var hit = match.at == "open" ? match.open : match.close;
+        if (hit) cm.state.tagHit = cm.markText(hit.from, hit.to, {className: "CodeMirror-matchingtag"});
+      }
+      var other = match.at == "close" ? match.open : match.close;
+      if (other)
+        cm.state.tagOther = cm.markText(other.from, other.to, {className: "CodeMirror-matchingtag"});
+      else
+        cm.state.failedTagMatch = true;
+    });
+  }
+
+  function maybeUpdateMatch(cm) {
+    if (cm.state.failedTagMatch) doMatchTags(cm);
+  }
+
+  CodeMirror.commands.toMatchingTag = function(cm) {
+    var found = CodeMirror.findMatchingTag(cm, cm.getCursor());
+    if (found) {
+      var other = found.at == "close" ? found.open : found.close;
+      if (other) cm.extendSelection(other.to, other.from);
+    }
+  };
+});
diff --git a/public/vendor/plugins/codemirror/addon/edit/trailingspace.js b/public/vendor/plugins/codemirror/addon/edit/trailingspace.js
new file mode 100644
index 0000000000..c39c310a99
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/edit/trailingspace.js
@@ -0,0 +1,27 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  CodeMirror.defineOption("showTrailingSpace", false, function(cm, val, prev) {
+    if (prev == CodeMirror.Init) prev = false;
+    if (prev && !val)
+      cm.removeOverlay("trailingspace");
+    else if (!prev && val)
+      cm.addOverlay({
+        token: function(stream) {
+          for (var l = stream.string.length, i = l; i && /\s/.test(stream.string.charAt(i - 1)); --i) {}
+          if (i > stream.pos) { stream.pos = i; return null; }
+          stream.pos = l;
+          return "trailingspace";
+        },
+        name: "trailingspace"
+      });
+  });
+});
diff --git a/public/vendor/plugins/codemirror/addon/fold/brace-fold.js b/public/vendor/plugins/codemirror/addon/fold/brace-fold.js
new file mode 100644
index 0000000000..654d1fb691
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/fold/brace-fold.js
@@ -0,0 +1,105 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.registerHelper("fold", "brace", function(cm, start) {
+  var line = start.line, lineText = cm.getLine(line);
+  var tokenType;
+
+  function findOpening(openCh) {
+    for (var at = start.ch, pass = 0;;) {
+      var found = at <= 0 ? -1 : lineText.lastIndexOf(openCh, at - 1);
+      if (found == -1) {
+        if (pass == 1) break;
+        pass = 1;
+        at = lineText.length;
+        continue;
+      }
+      if (pass == 1 && found < start.ch) break;
+      tokenType = cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1));
+      if (!/^(comment|string)/.test(tokenType)) return found + 1;
+      at = found - 1;
+    }
+  }
+
+  var startToken = "{", endToken = "}", startCh = findOpening("{");
+  if (startCh == null) {
+    startToken = "[", endToken = "]";
+    startCh = findOpening("[");
+  }
+
+  if (startCh == null) return;
+  var count = 1, lastLine = cm.lastLine(), end, endCh;
+  outer: for (var i = line; i <= lastLine; ++i) {
+    var text = cm.getLine(i), pos = i == line ? startCh : 0;
+    for (;;) {
+      var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos);
+      if (nextOpen < 0) nextOpen = text.length;
+      if (nextClose < 0) nextClose = text.length;
+      pos = Math.min(nextOpen, nextClose);
+      if (pos == text.length) break;
+      if (cm.getTokenTypeAt(CodeMirror.Pos(i, pos + 1)) == tokenType) {
+        if (pos == nextOpen) ++count;
+        else if (!--count) { end = i; endCh = pos; break outer; }
+      }
+      ++pos;
+    }
+  }
+  if (end == null || line == end) return;
+  return {from: CodeMirror.Pos(line, startCh),
+          to: CodeMirror.Pos(end, endCh)};
+});
+
+CodeMirror.registerHelper("fold", "import", function(cm, start) {
+  function hasImport(line) {
+    if (line < cm.firstLine() || line > cm.lastLine()) return null;
+    var start = cm.getTokenAt(CodeMirror.Pos(line, 1));
+    if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1));
+    if (start.type != "keyword" || start.string != "import") return null;
+    // Now find closing semicolon, return its position
+    for (var i = line, e = Math.min(cm.lastLine(), line + 10); i <= e; ++i) {
+      var text = cm.getLine(i), semi = text.indexOf(";");
+      if (semi != -1) return {startCh: start.end, end: CodeMirror.Pos(i, semi)};
+    }
+  }
+
+  var startLine = start.line, has = hasImport(startLine), prev;
+  if (!has || hasImport(startLine - 1) || ((prev = hasImport(startLine - 2)) && prev.end.line == startLine - 1))
+    return null;
+  for (var end = has.end;;) {
+    var next = hasImport(end.line + 1);
+    if (next == null) break;
+    end = next.end;
+  }
+  return {from: cm.clipPos(CodeMirror.Pos(startLine, has.startCh + 1)), to: end};
+});
+
+CodeMirror.registerHelper("fold", "include", function(cm, start) {
+  function hasInclude(line) {
+    if (line < cm.firstLine() || line > cm.lastLine()) return null;
+    var start = cm.getTokenAt(CodeMirror.Pos(line, 1));
+    if (!/\S/.test(start.string)) start = cm.getTokenAt(CodeMirror.Pos(line, start.end + 1));
+    if (start.type == "meta" && start.string.slice(0, 8) == "#include") return start.start + 8;
+  }
+
+  var startLine = start.line, has = hasInclude(startLine);
+  if (has == null || hasInclude(startLine - 1) != null) return null;
+  for (var end = startLine;;) {
+    var next = hasInclude(end + 1);
+    if (next == null) break;
+    ++end;
+  }
+  return {from: CodeMirror.Pos(startLine, has + 1),
+          to: cm.clipPos(CodeMirror.Pos(end))};
+});
+
+});
diff --git a/public/vendor/plugins/codemirror/addon/fold/comment-fold.js b/public/vendor/plugins/codemirror/addon/fold/comment-fold.js
new file mode 100644
index 0000000000..836101d8b0
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/fold/comment-fold.js
@@ -0,0 +1,59 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.registerGlobalHelper("fold", "comment", function(mode) {
+  return mode.blockCommentStart && mode.blockCommentEnd;
+}, function(cm, start) {
+  var mode = cm.getModeAt(start), startToken = mode.blockCommentStart, endToken = mode.blockCommentEnd;
+  if (!startToken || !endToken) return;
+  var line = start.line, lineText = cm.getLine(line);
+
+  var startCh;
+  for (var at = start.ch, pass = 0;;) {
+    var found = at <= 0 ? -1 : lineText.lastIndexOf(startToken, at - 1);
+    if (found == -1) {
+      if (pass == 1) return;
+      pass = 1;
+      at = lineText.length;
+      continue;
+    }
+    if (pass == 1 && found < start.ch) return;
+    if (/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found + 1))) &&
+        (found == 0 || lineText.slice(found - endToken.length, found) == endToken ||
+         !/comment/.test(cm.getTokenTypeAt(CodeMirror.Pos(line, found))))) {
+      startCh = found + startToken.length;
+      break;
+    }
+    at = found - 1;
+  }
+
+  var depth = 1, lastLine = cm.lastLine(), end, endCh;
+  outer: for (var i = line; i <= lastLine; ++i) {
+    var text = cm.getLine(i), pos = i == line ? startCh : 0;
+    for (;;) {
+      var nextOpen = text.indexOf(startToken, pos), nextClose = text.indexOf(endToken, pos);
+      if (nextOpen < 0) nextOpen = text.length;
+      if (nextClose < 0) nextClose = text.length;
+      pos = Math.min(nextOpen, nextClose);
+      if (pos == text.length) break;
+      if (pos == nextOpen) ++depth;
+      else if (!--depth) { end = i; endCh = pos; break outer; }
+      ++pos;
+    }
+  }
+  if (end == null || line == end && endCh == startCh) return;
+  return {from: CodeMirror.Pos(line, startCh),
+          to: CodeMirror.Pos(end, endCh)};
+});
+
+});
diff --git a/public/vendor/plugins/codemirror/addon/fold/foldcode.js b/public/vendor/plugins/codemirror/addon/fold/foldcode.js
new file mode 100644
index 0000000000..e146fb9f3e
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/fold/foldcode.js
@@ -0,0 +1,152 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  function doFold(cm, pos, options, force) {
+    if (options && options.call) {
+      var finder = options;
+      options = null;
+    } else {
+      var finder = getOption(cm, options, "rangeFinder");
+    }
+    if (typeof pos == "number") pos = CodeMirror.Pos(pos, 0);
+    var minSize = getOption(cm, options, "minFoldSize");
+
+    function getRange(allowFolded) {
+      var range = finder(cm, pos);
+      if (!range || range.to.line - range.from.line < minSize) return null;
+      var marks = cm.findMarksAt(range.from);
+      for (var i = 0; i < marks.length; ++i) {
+        if (marks[i].__isFold && force !== "fold") {
+          if (!allowFolded) return null;
+          range.cleared = true;
+          marks[i].clear();
+        }
+      }
+      return range;
+    }
+
+    var range = getRange(true);
+    if (getOption(cm, options, "scanUp")) while (!range && pos.line > cm.firstLine()) {
+      pos = CodeMirror.Pos(pos.line - 1, 0);
+      range = getRange(false);
+    }
+    if (!range || range.cleared || force === "unfold") return;
+
+    var myWidget = makeWidget(cm, options);
+    CodeMirror.on(myWidget, "mousedown", function(e) {
+      myRange.clear();
+      CodeMirror.e_preventDefault(e);
+    });
+    var myRange = cm.markText(range.from, range.to, {
+      replacedWith: myWidget,
+      clearOnEnter: getOption(cm, options, "clearOnEnter"),
+      __isFold: true
+    });
+    myRange.on("clear", function(from, to) {
+      CodeMirror.signal(cm, "unfold", cm, from, to);
+    });
+    CodeMirror.signal(cm, "fold", cm, range.from, range.to);
+  }
+
+  function makeWidget(cm, options) {
+    var widget = getOption(cm, options, "widget");
+    if (typeof widget == "string") {
+      var text = document.createTextNode(widget);
+      widget = document.createElement("span");
+      widget.appendChild(text);
+      widget.className = "CodeMirror-foldmarker";
+    } else if (widget) {
+      widget = widget.cloneNode(true)
+    }
+    return widget;
+  }
+
+  // Clumsy backwards-compatible interface
+  CodeMirror.newFoldFunction = function(rangeFinder, widget) {
+    return function(cm, pos) { doFold(cm, pos, {rangeFinder: rangeFinder, widget: widget}); };
+  };
+
+  // New-style interface
+  CodeMirror.defineExtension("foldCode", function(pos, options, force) {
+    doFold(this, pos, options, force);
+  });
+
+  CodeMirror.defineExtension("isFolded", function(pos) {
+    var marks = this.findMarksAt(pos);
+    for (var i = 0; i < marks.length; ++i)
+      if (marks[i].__isFold) return true;
+  });
+
+  CodeMirror.commands.toggleFold = function(cm) {
+    cm.foldCode(cm.getCursor());
+  };
+  CodeMirror.commands.fold = function(cm) {
+    cm.foldCode(cm.getCursor(), null, "fold");
+  };
+  CodeMirror.commands.unfold = function(cm) {
+    cm.foldCode(cm.getCursor(), null, "unfold");
+  };
+  CodeMirror.commands.foldAll = function(cm) {
+    cm.operation(function() {
+      for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++)
+        cm.foldCode(CodeMirror.Pos(i, 0), null, "fold");
+    });
+  };
+  CodeMirror.commands.unfoldAll = function(cm) {
+    cm.operation(function() {
+      for (var i = cm.firstLine(), e = cm.lastLine(); i <= e; i++)
+        cm.foldCode(CodeMirror.Pos(i, 0), null, "unfold");
+    });
+  };
+
+  CodeMirror.registerHelper("fold", "combine", function() {
+    var funcs = Array.prototype.slice.call(arguments, 0);
+    return function(cm, start) {
+      for (var i = 0; i < funcs.length; ++i) {
+        var found = funcs[i](cm, start);
+        if (found) return found;
+      }
+    };
+  });
+
+  CodeMirror.registerHelper("fold", "auto", function(cm, start) {
+    var helpers = cm.getHelpers(start, "fold");
+    for (var i = 0; i < helpers.length; i++) {
+      var cur = helpers[i](cm, start);
+      if (cur) return cur;
+    }
+  });
+
+  var defaultOptions = {
+    rangeFinder: CodeMirror.fold.auto,
+    widget: "\u2194",
+    minFoldSize: 0,
+    scanUp: false,
+    clearOnEnter: true
+  };
+
+  CodeMirror.defineOption("foldOptions", null);
+
+  function getOption(cm, options, name) {
+    if (options && options[name] !== undefined)
+      return options[name];
+    var editorOptions = cm.options.foldOptions;
+    if (editorOptions && editorOptions[name] !== undefined)
+      return editorOptions[name];
+    return defaultOptions[name];
+  }
+
+  CodeMirror.defineExtension("foldOption", function(options, name) {
+    return getOption(this, options, name);
+  });
+});
diff --git a/public/vendor/plugins/codemirror/addon/fold/foldgutter.css b/public/vendor/plugins/codemirror/addon/fold/foldgutter.css
new file mode 100644
index 0000000000..ad19ae2d3e
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/fold/foldgutter.css
@@ -0,0 +1,20 @@
+.CodeMirror-foldmarker {
+  color: blue;
+  text-shadow: #b9f 1px 1px 2px, #b9f -1px -1px 2px, #b9f 1px -1px 2px, #b9f -1px 1px 2px;
+  font-family: arial;
+  line-height: .3;
+  cursor: pointer;
+}
+.CodeMirror-foldgutter {
+  width: .7em;
+}
+.CodeMirror-foldgutter-open,
+.CodeMirror-foldgutter-folded {
+  cursor: pointer;
+}
+.CodeMirror-foldgutter-open:after {
+  content: "\25BE";
+}
+.CodeMirror-foldgutter-folded:after {
+  content: "\25B8";
+}
diff --git a/public/vendor/plugins/codemirror/addon/fold/foldgutter.js b/public/vendor/plugins/codemirror/addon/fold/foldgutter.js
new file mode 100644
index 0000000000..e57a1df35d
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/fold/foldgutter.js
@@ -0,0 +1,151 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"), require("./foldcode"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror", "./foldcode"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  CodeMirror.defineOption("foldGutter", false, function(cm, val, old) {
+    if (old && old != CodeMirror.Init) {
+      cm.clearGutter(cm.state.foldGutter.options.gutter);
+      cm.state.foldGutter = null;
+      cm.off("gutterClick", onGutterClick);
+      cm.off("changes", onChange);
+      cm.off("viewportChange", onViewportChange);
+      cm.off("fold", onFold);
+      cm.off("unfold", onFold);
+      cm.off("swapDoc", onChange);
+    }
+    if (val) {
+      cm.state.foldGutter = new State(parseOptions(val));
+      updateInViewport(cm);
+      cm.on("gutterClick", onGutterClick);
+      cm.on("changes", onChange);
+      cm.on("viewportChange", onViewportChange);
+      cm.on("fold", onFold);
+      cm.on("unfold", onFold);
+      cm.on("swapDoc", onChange);
+    }
+  });
+
+  var Pos = CodeMirror.Pos;
+
+  function State(options) {
+    this.options = options;
+    this.from = this.to = 0;
+  }
+
+  function parseOptions(opts) {
+    if (opts === true) opts = {};
+    if (opts.gutter == null) opts.gutter = "CodeMirror-foldgutter";
+    if (opts.indicatorOpen == null) opts.indicatorOpen = "CodeMirror-foldgutter-open";
+    if (opts.indicatorFolded == null) opts.indicatorFolded = "CodeMirror-foldgutter-folded";
+    return opts;
+  }
+
+  function isFolded(cm, line) {
+    var marks = cm.findMarks(Pos(line, 0), Pos(line + 1, 0));
+    for (var i = 0; i < marks.length; ++i) {
+      if (marks[i].__isFold) {
+        var fromPos = marks[i].find(-1);
+        if (fromPos && fromPos.line === line)
+          return marks[i];
+      }
+    }
+  }
+
+  function marker(spec) {
+    if (typeof spec == "string") {
+      var elt = document.createElement("div");
+      elt.className = spec + " CodeMirror-guttermarker-subtle";
+      return elt;
+    } else {
+      return spec.cloneNode(true);
+    }
+  }
+
+  function updateFoldInfo(cm, from, to) {
+    var opts = cm.state.foldGutter.options, cur = from;
+    var minSize = cm.foldOption(opts, "minFoldSize");
+    var func = cm.foldOption(opts, "rangeFinder");
+    cm.eachLine(from, to, function(line) {
+      var mark = null;
+      if (isFolded(cm, cur)) {
+        mark = marker(opts.indicatorFolded);
+      } else {
+        var pos = Pos(cur, 0);
+        var range = func && func(cm, pos);
+        if (range && range.to.line - range.from.line >= minSize)
+          mark = marker(opts.indicatorOpen);
+      }
+      cm.setGutterMarker(line, opts.gutter, mark);
+      ++cur;
+    });
+  }
+
+  function updateInViewport(cm) {
+    var vp = cm.getViewport(), state = cm.state.foldGutter;
+    if (!state) return;
+    cm.operation(function() {
+      updateFoldInfo(cm, vp.from, vp.to);
+    });
+    state.from = vp.from; state.to = vp.to;
+  }
+
+  function onGutterClick(cm, line, gutter) {
+    var state = cm.state.foldGutter;
+    if (!state) return;
+    var opts = state.options;
+    if (gutter != opts.gutter) return;
+    var folded = isFolded(cm, line);
+    if (folded) folded.clear();
+    else cm.foldCode(Pos(line, 0), opts);
+  }
+
+  function onChange(cm) {
+    var state = cm.state.foldGutter;
+    if (!state) return;
+    var opts = state.options;
+    state.from = state.to = 0;
+    clearTimeout(state.changeUpdate);
+    state.changeUpdate = setTimeout(function() { updateInViewport(cm); }, opts.foldOnChangeTimeSpan || 600);
+  }
+
+  function onViewportChange(cm) {
+    var state = cm.state.foldGutter;
+    if (!state) return;
+    var opts = state.options;
+    clearTimeout(state.changeUpdate);
+    state.changeUpdate = setTimeout(function() {
+      var vp = cm.getViewport();
+      if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) {
+        updateInViewport(cm);
+      } else {
+        cm.operation(function() {
+          if (vp.from < state.from) {
+            updateFoldInfo(cm, vp.from, state.from);
+            state.from = vp.from;
+          }
+          if (vp.to > state.to) {
+            updateFoldInfo(cm, state.to, vp.to);
+            state.to = vp.to;
+          }
+        });
+      }
+    }, opts.updateViewportTimeSpan || 400);
+  }
+
+  function onFold(cm, from) {
+    var state = cm.state.foldGutter;
+    if (!state) return;
+    var line = from.line;
+    if (line >= state.from && line < state.to)
+      updateFoldInfo(cm, line, line + 1);
+  }
+});
diff --git a/public/vendor/plugins/codemirror/addon/fold/indent-fold.js b/public/vendor/plugins/codemirror/addon/fold/indent-fold.js
new file mode 100644
index 0000000000..0cc1126440
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/fold/indent-fold.js
@@ -0,0 +1,48 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+function lineIndent(cm, lineNo) {
+  var text = cm.getLine(lineNo)
+  var spaceTo = text.search(/\S/)
+  if (spaceTo == -1 || /\bcomment\b/.test(cm.getTokenTypeAt(CodeMirror.Pos(lineNo, spaceTo + 1))))
+    return -1
+  return CodeMirror.countColumn(text, null, cm.getOption("tabSize"))
+}
+
+CodeMirror.registerHelper("fold", "indent", function(cm, start) {
+  var myIndent = lineIndent(cm, start.line)
+  if (myIndent < 0) return
+  var lastLineInFold = null
+
+  // Go through lines until we find a line that definitely doesn't belong in
+  // the block we're folding, or to the end.
+  for (var i = start.line + 1, end = cm.lastLine(); i <= end; ++i) {
+    var indent = lineIndent(cm, i)
+    if (indent == -1) {
+    } else if (indent > myIndent) {
+      // Lines with a greater indent are considered part of the block.
+      lastLineInFold = i;
+    } else {
+      // If this line has non-space, non-comment content, and is
+      // indented less or equal to the start line, it is the start of
+      // another block.
+      break;
+    }
+  }
+  if (lastLineInFold) return {
+    from: CodeMirror.Pos(start.line, cm.getLine(start.line).length),
+    to: CodeMirror.Pos(lastLineInFold, cm.getLine(lastLineInFold).length)
+  };
+});
+
+});
diff --git a/public/vendor/plugins/codemirror/addon/fold/markdown-fold.js b/public/vendor/plugins/codemirror/addon/fold/markdown-fold.js
new file mode 100644
index 0000000000..6a551786d1
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/fold/markdown-fold.js
@@ -0,0 +1,49 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.registerHelper("fold", "markdown", function(cm, start) {
+  var maxDepth = 100;
+
+  function isHeader(lineNo) {
+    var tokentype = cm.getTokenTypeAt(CodeMirror.Pos(lineNo, 0));
+    return tokentype && /\bheader\b/.test(tokentype);
+  }
+
+  function headerLevel(lineNo, line, nextLine) {
+    var match = line && line.match(/^#+/);
+    if (match && isHeader(lineNo)) return match[0].length;
+    match = nextLine && nextLine.match(/^[=\-]+\s*$/);
+    if (match && isHeader(lineNo + 1)) return nextLine[0] == "=" ? 1 : 2;
+    return maxDepth;
+  }
+
+  var firstLine = cm.getLine(start.line), nextLine = cm.getLine(start.line + 1);
+  var level = headerLevel(start.line, firstLine, nextLine);
+  if (level === maxDepth) return undefined;
+
+  var lastLineNo = cm.lastLine();
+  var end = start.line, nextNextLine = cm.getLine(end + 2);
+  while (end < lastLineNo) {
+    if (headerLevel(end + 1, nextLine, nextNextLine) <= level) break;
+    ++end;
+    nextLine = nextNextLine;
+    nextNextLine = cm.getLine(end + 2);
+  }
+
+  return {
+    from: CodeMirror.Pos(start.line, firstLine.length),
+    to: CodeMirror.Pos(end, cm.getLine(end).length)
+  };
+});
+
+});
diff --git a/public/vendor/plugins/codemirror/addon/fold/xml-fold.js b/public/vendor/plugins/codemirror/addon/fold/xml-fold.js
new file mode 100644
index 0000000000..13bc3838b2
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/fold/xml-fold.js
@@ -0,0 +1,184 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  var Pos = CodeMirror.Pos;
+  function cmp(a, b) { return a.line - b.line || a.ch - b.ch; }
+
+  var nameStartChar = "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD";
+  var nameChar = nameStartChar + "\-\:\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
+  var xmlTagStart = new RegExp("<(/?)([" + nameStartChar + "][" + nameChar + "]*)", "g");
+
+  function Iter(cm, line, ch, range) {
+    this.line = line; this.ch = ch;
+    this.cm = cm; this.text = cm.getLine(line);
+    this.min = range ? Math.max(range.from, cm.firstLine()) : cm.firstLine();
+    this.max = range ? Math.min(range.to - 1, cm.lastLine()) : cm.lastLine();
+  }
+
+  function tagAt(iter, ch) {
+    var type = iter.cm.getTokenTypeAt(Pos(iter.line, ch));
+    return type && /\btag\b/.test(type);
+  }
+
+  function nextLine(iter) {
+    if (iter.line >= iter.max) return;
+    iter.ch = 0;
+    iter.text = iter.cm.getLine(++iter.line);
+    return true;
+  }
+  function prevLine(iter) {
+    if (iter.line <= iter.min) return;
+    iter.text = iter.cm.getLine(--iter.line);
+    iter.ch = iter.text.length;
+    return true;
+  }
+
+  function toTagEnd(iter) {
+    for (;;) {
+      var gt = iter.text.indexOf(">", iter.ch);
+      if (gt == -1) { if (nextLine(iter)) continue; else return; }
+      if (!tagAt(iter, gt + 1)) { iter.ch = gt + 1; continue; }
+      var lastSlash = iter.text.lastIndexOf("/", gt);
+      var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
+      iter.ch = gt + 1;
+      return selfClose ? "selfClose" : "regular";
+    }
+  }
+  function toTagStart(iter) {
+    for (;;) {
+      var lt = iter.ch ? iter.text.lastIndexOf("<", iter.ch - 1) : -1;
+      if (lt == -1) { if (prevLine(iter)) continue; else return; }
+      if (!tagAt(iter, lt + 1)) { iter.ch = lt; continue; }
+      xmlTagStart.lastIndex = lt;
+      iter.ch = lt;
+      var match = xmlTagStart.exec(iter.text);
+      if (match && match.index == lt) return match;
+    }
+  }
+
+  function toNextTag(iter) {
+    for (;;) {
+      xmlTagStart.lastIndex = iter.ch;
+      var found = xmlTagStart.exec(iter.text);
+      if (!found) { if (nextLine(iter)) continue; else return; }
+      if (!tagAt(iter, found.index + 1)) { iter.ch = found.index + 1; continue; }
+      iter.ch = found.index + found[0].length;
+      return found;
+    }
+  }
+  function toPrevTag(iter) {
+    for (;;) {
+      var gt = iter.ch ? iter.text.lastIndexOf(">", iter.ch - 1) : -1;
+      if (gt == -1) { if (prevLine(iter)) continue; else return; }
+      if (!tagAt(iter, gt + 1)) { iter.ch = gt; continue; }
+      var lastSlash = iter.text.lastIndexOf("/", gt);
+      var selfClose = lastSlash > -1 && !/\S/.test(iter.text.slice(lastSlash + 1, gt));
+      iter.ch = gt + 1;
+      return selfClose ? "selfClose" : "regular";
+    }
+  }
+
+  function findMatchingClose(iter, tag) {
+    var stack = [];
+    for (;;) {
+      var next = toNextTag(iter), end, startLine = iter.line, startCh = iter.ch - (next ? next[0].length : 0);
+      if (!next || !(end = toTagEnd(iter))) return;
+      if (end == "selfClose") continue;
+      if (next[1]) { // closing tag
+        for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == next[2]) {
+          stack.length = i;
+          break;
+        }
+        if (i < 0 && (!tag || tag == next[2])) return {
+          tag: next[2],
+          from: Pos(startLine, startCh),
+          to: Pos(iter.line, iter.ch)
+        };
+      } else { // opening tag
+        stack.push(next[2]);
+      }
+    }
+  }
+  function findMatchingOpen(iter, tag) {
+    var stack = [];
+    for (;;) {
+      var prev = toPrevTag(iter);
+      if (!prev) return;
+      if (prev == "selfClose") { toTagStart(iter); continue; }
+      var endLine = iter.line, endCh = iter.ch;
+      var start = toTagStart(iter);
+      if (!start) return;
+      if (start[1]) { // closing tag
+        stack.push(start[2]);
+      } else { // opening tag
+        for (var i = stack.length - 1; i >= 0; --i) if (stack[i] == start[2]) {
+          stack.length = i;
+          break;
+        }
+        if (i < 0 && (!tag || tag == start[2])) return {
+          tag: start[2],
+          from: Pos(iter.line, iter.ch),
+          to: Pos(endLine, endCh)
+        };
+      }
+    }
+  }
+
+  CodeMirror.registerHelper("fold", "xml", function(cm, start) {
+    var iter = new Iter(cm, start.line, 0);
+    for (;;) {
+      var openTag = toNextTag(iter)
+      if (!openTag || iter.line != start.line) return
+      var end = toTagEnd(iter)
+      if (!end) return
+      if (!openTag[1] && end != "selfClose") {
+        var startPos = Pos(iter.line, iter.ch);
+        var endPos = findMatchingClose(iter, openTag[2]);
+        return endPos && cmp(endPos.from, startPos) > 0 ? {from: startPos, to: endPos.from} : null
+      }
+    }
+  });
+  CodeMirror.findMatchingTag = function(cm, pos, range) {
+    var iter = new Iter(cm, pos.line, pos.ch, range);
+    if (iter.text.indexOf(">") == -1 && iter.text.indexOf("<") == -1) return;
+    var end = toTagEnd(iter), to = end && Pos(iter.line, iter.ch);
+    var start = end && toTagStart(iter);
+    if (!end || !start || cmp(iter, pos) > 0) return;
+    var here = {from: Pos(iter.line, iter.ch), to: to, tag: start[2]};
+    if (end == "selfClose") return {open: here, close: null, at: "open"};
+
+    if (start[1]) { // closing tag
+      return {open: findMatchingOpen(iter, start[2]), close: here, at: "close"};
+    } else { // opening tag
+      iter = new Iter(cm, to.line, to.ch, range);
+      return {open: here, close: findMatchingClose(iter, start[2]), at: "open"};
+    }
+  };
+
+  CodeMirror.findEnclosingTag = function(cm, pos, range, tag) {
+    var iter = new Iter(cm, pos.line, pos.ch, range);
+    for (;;) {
+      var open = findMatchingOpen(iter, tag);
+      if (!open) break;
+      var forward = new Iter(cm, pos.line, pos.ch, range);
+      var close = findMatchingClose(forward, open.tag);
+      if (close) return {open: open, close: close};
+    }
+  };
+
+  // Used by addon/edit/closetag.js
+  CodeMirror.scanForClosingTag = function(cm, pos, name, end) {
+    var iter = new Iter(cm, pos.line, pos.ch, end ? {from: 0, to: end} : null);
+    return findMatchingClose(iter, name);
+  };
+});
diff --git a/public/vendor/plugins/codemirror/addon/hint/anyword-hint.js b/public/vendor/plugins/codemirror/addon/hint/anyword-hint.js
new file mode 100644
index 0000000000..d27a9ec018
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/hint/anyword-hint.js
@@ -0,0 +1,41 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  var WORD = /[\w$]+/, RANGE = 500;
+
+  CodeMirror.registerHelper("hint", "anyword", function(editor, options) {
+    var word = options && options.word || WORD;
+    var range = options && options.range || RANGE;
+    var cur = editor.getCursor(), curLine = editor.getLine(cur.line);
+    var end = cur.ch, start = end;
+    while (start && word.test(curLine.charAt(start - 1))) --start;
+    var curWord = start != end && curLine.slice(start, end);
+
+    var list = options && options.list || [], seen = {};
+    var re = new RegExp(word.source, "g");
+    for (var dir = -1; dir <= 1; dir += 2) {
+      var line = cur.line, endLine = Math.min(Math.max(line + dir * range, editor.firstLine()), editor.lastLine()) + dir;
+      for (; line != endLine; line += dir) {
+        var text = editor.getLine(line), m;
+        while (m = re.exec(text)) {
+          if (line == cur.line && m[0] === curWord) continue;
+          if ((!curWord || m[0].lastIndexOf(curWord, 0) == 0) && !Object.prototype.hasOwnProperty.call(seen, m[0])) {
+            seen[m[0]] = true;
+            list.push(m[0]);
+          }
+        }
+      }
+    }
+    return {list: list, from: CodeMirror.Pos(cur.line, start), to: CodeMirror.Pos(cur.line, end)};
+  });
+});
diff --git a/public/vendor/plugins/codemirror/addon/hint/css-hint.js b/public/vendor/plugins/codemirror/addon/hint/css-hint.js
new file mode 100644
index 0000000000..6cdf728195
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/hint/css-hint.js
@@ -0,0 +1,60 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"), require("../../mode/css/css"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror", "../../mode/css/css"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  var pseudoClasses = {link: 1, visited: 1, active: 1, hover: 1, focus: 1,
+                       "first-letter": 1, "first-line": 1, "first-child": 1,
+                       before: 1, after: 1, lang: 1};
+
+  CodeMirror.registerHelper("hint", "css", function(cm) {
+    var cur = cm.getCursor(), token = cm.getTokenAt(cur);
+    var inner = CodeMirror.innerMode(cm.getMode(), token.state);
+    if (inner.mode.name != "css") return;
+
+    if (token.type == "keyword" && "!important".indexOf(token.string) == 0)
+      return {list: ["!important"], from: CodeMirror.Pos(cur.line, token.start),
+              to: CodeMirror.Pos(cur.line, token.end)};
+
+    var start = token.start, end = cur.ch, word = token.string.slice(0, end - start);
+    if (/[^\w$_-]/.test(word)) {
+      word = ""; start = end = cur.ch;
+    }
+
+    var spec = CodeMirror.resolveMode("text/css");
+
+    var result = [];
+    function add(keywords) {
+      for (var name in keywords)
+        if (!word || name.lastIndexOf(word, 0) == 0)
+          result.push(name);
+    }
+
+    var st = inner.state.state;
+    if (st == "pseudo" || token.type == "variable-3") {
+      add(pseudoClasses);
+    } else if (st == "block" || st == "maybeprop") {
+      add(spec.propertyKeywords);
+    } else if (st == "prop" || st == "parens" || st == "at" || st == "params") {
+      add(spec.valueKeywords);
+      add(spec.colorKeywords);
+    } else if (st == "media" || st == "media_parens") {
+      add(spec.mediaTypes);
+      add(spec.mediaFeatures);
+    }
+
+    if (result.length) return {
+      list: result,
+      from: CodeMirror.Pos(cur.line, start),
+      to: CodeMirror.Pos(cur.line, end)
+    };
+  });
+});
diff --git a/public/vendor/plugins/codemirror/addon/hint/html-hint.js b/public/vendor/plugins/codemirror/addon/hint/html-hint.js
new file mode 100644
index 0000000000..d0cca4f6a2
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/hint/html-hint.js
@@ -0,0 +1,350 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"), require("./xml-hint"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror", "./xml-hint"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  var langs = "ab aa af ak sq am ar an hy as av ae ay az bm ba eu be bn bh bi bs br bg my ca ch ce ny zh cv kw co cr hr cs da dv nl dz en eo et ee fo fj fi fr ff gl ka de el gn gu ht ha he hz hi ho hu ia id ie ga ig ik io is it iu ja jv kl kn kr ks kk km ki rw ky kv kg ko ku kj la lb lg li ln lo lt lu lv gv mk mg ms ml mt mi mr mh mn na nv nb nd ne ng nn no ii nr oc oj cu om or os pa pi fa pl ps pt qu rm rn ro ru sa sc sd se sm sg sr gd sn si sk sl so st es su sw ss sv ta te tg th ti bo tk tl tn to tr ts tt tw ty ug uk ur uz ve vi vo wa cy wo fy xh yi yo za zu".split(" ");
+  var targets = ["_blank", "_self", "_top", "_parent"];
+  var charsets = ["ascii", "utf-8", "utf-16", "latin1", "latin1"];
+  var methods = ["get", "post", "put", "delete"];
+  var encs = ["application/x-www-form-urlencoded", "multipart/form-data", "text/plain"];
+  var media = ["all", "screen", "print", "embossed", "braille", "handheld", "print", "projection", "screen", "tty", "tv", "speech",
+               "3d-glasses", "resolution [>][<][=] [X]", "device-aspect-ratio: X/Y", "orientation:portrait",
+               "orientation:landscape", "device-height: [X]", "device-width: [X]"];
+  var s = { attrs: {} }; // Simple tag, reused for a whole lot of tags
+
+  var data = {
+    a: {
+      attrs: {
+        href: null, ping: null, type: null,
+        media: media,
+        target: targets,
+        hreflang: langs
+      }
+    },
+    abbr: s,
+    acronym: s,
+    address: s,
+    applet: s,
+    area: {
+      attrs: {
+        alt: null, coords: null, href: null, target: null, ping: null,
+        media: media, hreflang: langs, type: null,
+        shape: ["default", "rect", "circle", "poly"]
+      }
+    },
+    article: s,
+    aside: s,
+    audio: {
+      attrs: {
+        src: null, mediagroup: null,
+        crossorigin: ["anonymous", "use-credentials"],
+        preload: ["none", "metadata", "auto"],
+        autoplay: ["", "autoplay"],
+        loop: ["", "loop"],
+        controls: ["", "controls"]
+      }
+    },
+    b: s,
+    base: { attrs: { href: null, target: targets } },
+    basefont: s,
+    bdi: s,
+    bdo: s,
+    big: s,
+    blockquote: { attrs: { cite: null } },
+    body: s,
+    br: s,
+    button: {
+      attrs: {
+        form: null, formaction: null, name: null, value: null,
+        autofocus: ["", "autofocus"],
+        disabled: ["", "autofocus"],
+        formenctype: encs,
+        formmethod: methods,
+        formnovalidate: ["", "novalidate"],
+        formtarget: targets,
+        type: ["submit", "reset", "button"]
+      }
+    },
+    canvas: { attrs: { width: null, height: null } },
+    caption: s,
+    center: s,
+    cite: s,
+    code: s,
+    col: { attrs: { span: null } },
+    colgroup: { attrs: { span: null } },
+    command: {
+      attrs: {
+        type: ["command", "checkbox", "radio"],
+        label: null, icon: null, radiogroup: null, command: null, title: null,
+        disabled: ["", "disabled"],
+        checked: ["", "checked"]
+      }
+    },
+    data: { attrs: { value: null } },
+    datagrid: { attrs: { disabled: ["", "disabled"], multiple: ["", "multiple"] } },
+    datalist: { attrs: { data: null } },
+    dd: s,
+    del: { attrs: { cite: null, datetime: null } },
+    details: { attrs: { open: ["", "open"] } },
+    dfn: s,
+    dir: s,
+    div: s,
+    dl: s,
+    dt: s,
+    em: s,
+    embed: { attrs: { src: null, type: null, width: null, height: null } },
+    eventsource: { attrs: { src: null } },
+    fieldset: { attrs: { disabled: ["", "disabled"], form: null, name: null } },
+    figcaption: s,
+    figure: s,
+    font: s,
+    footer: s,
+    form: {
+      attrs: {
+        action: null, name: null,
+        "accept-charset": charsets,
+        autocomplete: ["on", "off"],
+        enctype: encs,
+        method: methods,
+        novalidate: ["", "novalidate"],
+        target: targets
+      }
+    },
+    frame: s,
+    frameset: s,
+    h1: s, h2: s, h3: s, h4: s, h5: s, h6: s,
+    head: {
+      attrs: {},
+      children: ["title", "base", "link", "style", "meta", "script", "noscript", "command"]
+    },
+    header: s,
+    hgroup: s,
+    hr: s,
+    html: {
+      attrs: { manifest: null },
+      children: ["head", "body"]
+    },
+    i: s,
+    iframe: {
+      attrs: {
+        src: null, srcdoc: null, name: null, width: null, height: null,
+        sandbox: ["allow-top-navigation", "allow-same-origin", "allow-forms", "allow-scripts"],
+        seamless: ["", "seamless"]
+      }
+    },
+    img: {
+      attrs: {
+        alt: null, src: null, ismap: null, usemap: null, width: null, height: null,
+        crossorigin: ["anonymous", "use-credentials"]
+      }
+    },
+    input: {
+      attrs: {
+        alt: null, dirname: null, form: null, formaction: null,
+        height: null, list: null, max: null, maxlength: null, min: null,
+        name: null, pattern: null, placeholder: null, size: null, src: null,
+        step: null, value: null, width: null,
+        accept: ["audio/*", "video/*", "image/*"],
+        autocomplete: ["on", "off"],
+        autofocus: ["", "autofocus"],
+        checked: ["", "checked"],
+        disabled: ["", "disabled"],
+        formenctype: encs,
+        formmethod: methods,
+        formnovalidate: ["", "novalidate"],
+        formtarget: targets,
+        multiple: ["", "multiple"],
+        readonly: ["", "readonly"],
+        required: ["", "required"],
+        type: ["hidden", "text", "search", "tel", "url", "email", "password", "datetime", "date", "month",
+               "week", "time", "datetime-local", "number", "range", "color", "checkbox", "radio",
+               "file", "submit", "image", "reset", "button"]
+      }
+    },
+    ins: { attrs: { cite: null, datetime: null } },
+    kbd: s,
+    keygen: {
+      attrs: {
+        challenge: null, form: null, name: null,
+        autofocus: ["", "autofocus"],
+        disabled: ["", "disabled"],
+        keytype: ["RSA"]
+      }
+    },
+    label: { attrs: { "for": null, form: null } },
+    legend: s,
+    li: { attrs: { value: null } },
+    link: {
+      attrs: {
+        href: null, type: null,
+        hreflang: langs,
+        media: media,
+        sizes: ["all", "16x16", "16x16 32x32", "16x16 32x32 64x64"]
+      }
+    },
+    map: { attrs: { name: null } },
+    mark: s,
+    menu: { attrs: { label: null, type: ["list", "context", "toolbar"] } },
+    meta: {
+      attrs: {
+        content: null,
+        charset: charsets,
+        name: ["viewport", "application-name", "author", "description", "generator", "keywords"],
+        "http-equiv": ["content-language", "content-type", "default-style", "refresh"]
+      }
+    },
+    meter: { attrs: { value: null, min: null, low: null, high: null, max: null, optimum: null } },
+    nav: s,
+    noframes: s,
+    noscript: s,
+    object: {
+      attrs: {
+        data: null, type: null, name: null, usemap: null, form: null, width: null, height: null,
+        typemustmatch: ["", "typemustmatch"]
+      }
+    },
+    ol: { attrs: { reversed: ["", "reversed"], start: null, type: ["1", "a", "A", "i", "I"] } },
+    optgroup: { attrs: { disabled: ["", "disabled"], label: null } },
+    option: { attrs: { disabled: ["", "disabled"], label: null, selected: ["", "selected"], value: null } },
+    output: { attrs: { "for": null, form: null, name: null } },
+    p: s,
+    param: { attrs: { name: null, value: null } },
+    pre: s,
+    progress: { attrs: { value: null, max: null } },
+    q: { attrs: { cite: null } },
+    rp: s,
+    rt: s,
+    ruby: s,
+    s: s,
+    samp: s,
+    script: {
+      attrs: {
+        type: ["text/javascript"],
+        src: null,
+        async: ["", "async"],
+        defer: ["", "defer"],
+        charset: charsets
+      }
+    },
+    section: s,
+    select: {
+      attrs: {
+        form: null, name: null, size: null,
+        autofocus: ["", "autofocus"],
+        disabled: ["", "disabled"],
+        multiple: ["", "multiple"]
+      }
+    },
+    small: s,
+    source: { attrs: { src: null, type: null, media: null } },
+    span: s,
+    strike: s,
+    strong: s,
+    style: {
+      attrs: {
+        type: ["text/css"],
+        media: media,
+        scoped: null
+      }
+    },
+    sub: s,
+    summary: s,
+    sup: s,
+    table: s,
+    tbody: s,
+    td: { attrs: { colspan: null, rowspan: null, headers: null } },
+    textarea: {
+      attrs: {
+        dirname: null, form: null, maxlength: null, name: null, placeholder: null,
+        rows: null, cols: null,
+        autofocus: ["", "autofocus"],
+        disabled: ["", "disabled"],
+        readonly: ["", "readonly"],
+        required: ["", "required"],
+        wrap: ["soft", "hard"]
+      }
+    },
+    tfoot: s,
+    th: { attrs: { colspan: null, rowspan: null, headers: null, scope: ["row", "col", "rowgroup", "colgroup"] } },
+    thead: s,
+    time: { attrs: { datetime: null } },
+    title: s,
+    tr: s,
+    track: {
+      attrs: {
+        src: null, label: null, "default": null,
+        kind: ["subtitles", "captions", "descriptions", "chapters", "metadata"],
+        srclang: langs
+      }
+    },
+    tt: s,
+    u: s,
+    ul: s,
+    "var": s,
+    video: {
+      attrs: {
+        src: null, poster: null, width: null, height: null,
+        crossorigin: ["anonymous", "use-credentials"],
+        preload: ["auto", "metadata", "none"],
+        autoplay: ["", "autoplay"],
+        mediagroup: ["movie"],
+        muted: ["", "muted"],
+        controls: ["", "controls"]
+      }
+    },
+    wbr: s
+  };
+
+  var globalAttrs = {
+    accesskey: ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"],
+    "class": null,
+    contenteditable: ["true", "false"],
+    contextmenu: null,
+    dir: ["ltr", "rtl", "auto"],
+    draggable: ["true", "false", "auto"],
+    dropzone: ["copy", "move", "link", "string:", "file:"],
+    hidden: ["hidden"],
+    id: null,
+    inert: ["inert"],
+    itemid: null,
+    itemprop: null,
+    itemref: null,
+    itemscope: ["itemscope"],
+    itemtype: null,
+    lang: ["en", "es"],
+    spellcheck: ["true", "false"],
+    autocorrect: ["true", "false"],
+    autocapitalize: ["true", "false"],
+    style: null,
+    tabindex: ["1", "2", "3", "4", "5", "6", "7", "8", "9"],
+    title: null,
+    translate: ["yes", "no"],
+    onclick: null,
+    rel: ["stylesheet", "alternate", "author", "bookmark", "help", "license", "next", "nofollow", "noreferrer", "prefetch", "prev", "search", "tag"]
+  };
+  function populate(obj) {
+    for (var attr in globalAttrs) if (globalAttrs.hasOwnProperty(attr))
+      obj.attrs[attr] = globalAttrs[attr];
+  }
+
+  populate(s);
+  for (var tag in data) if (data.hasOwnProperty(tag) && data[tag] != s)
+    populate(data[tag]);
+
+  CodeMirror.htmlSchema = data;
+  function htmlHint(cm, options) {
+    var local = {schemaInfo: data};
+    if (options) for (var opt in options) local[opt] = options[opt];
+    return CodeMirror.hint.xml(cm, local);
+  }
+  CodeMirror.registerHelper("hint", "html", htmlHint);
+});
diff --git a/public/vendor/plugins/codemirror/addon/hint/javascript-hint.js b/public/vendor/plugins/codemirror/addon/hint/javascript-hint.js
new file mode 100644
index 0000000000..96a7fe01c2
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/hint/javascript-hint.js
@@ -0,0 +1,157 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  var Pos = CodeMirror.Pos;
+
+  function forEach(arr, f) {
+    for (var i = 0, e = arr.length; i < e; ++i) f(arr[i]);
+  }
+
+  function arrayContains(arr, item) {
+    if (!Array.prototype.indexOf) {
+      var i = arr.length;
+      while (i--) {
+        if (arr[i] === item) {
+          return true;
+        }
+      }
+      return false;
+    }
+    return arr.indexOf(item) != -1;
+  }
+
+  function scriptHint(editor, keywords, getToken, options) {
+    // Find the token at the cursor
+    var cur = editor.getCursor(), token = getToken(editor, cur);
+    if (/\b(?:string|comment)\b/.test(token.type)) return;
+    var innerMode = CodeMirror.innerMode(editor.getMode(), token.state);
+    if (innerMode.mode.helperType === "json") return;
+    token.state = innerMode.state;
+
+    // If it's not a 'word-style' token, ignore the token.
+    if (!/^[\w$_]*$/.test(token.string)) {
+      token = {start: cur.ch, end: cur.ch, string: "", state: token.state,
+               type: token.string == "." ? "property" : null};
+    } else if (token.end > cur.ch) {
+      token.end = cur.ch;
+      token.string = token.string.slice(0, cur.ch - token.start);
+    }
+
+    var tprop = token;
+    // If it is a property, find out what it is a property of.
+    while (tprop.type == "property") {
+      tprop = getToken(editor, Pos(cur.line, tprop.start));
+      if (tprop.string != ".") return;
+      tprop = getToken(editor, Pos(cur.line, tprop.start));
+      if (!context) var context = [];
+      context.push(tprop);
+    }
+    return {list: getCompletions(token, context, keywords, options),
+            from: Pos(cur.line, token.start),
+            to: Pos(cur.line, token.end)};
+  }
+
+  function javascriptHint(editor, options) {
+    return scriptHint(editor, javascriptKeywords,
+                      function (e, cur) {return e.getTokenAt(cur);},
+                      options);
+  };
+  CodeMirror.registerHelper("hint", "javascript", javascriptHint);
+
+  function getCoffeeScriptToken(editor, cur) {
+  // This getToken, it is for coffeescript, imitates the behavior of
+  // getTokenAt method in javascript.js, that is, returning "property"
+  // type and treat "." as indepenent token.
+    var token = editor.getTokenAt(cur);
+    if (cur.ch == token.start + 1 && token.string.charAt(0) == '.') {
+      token.end = token.start;
+      token.string = '.';
+      token.type = "property";
+    }
+    else if (/^\.[\w$_]*$/.test(token.string)) {
+      token.type = "property";
+      token.start++;
+      token.string = token.string.replace(/\./, '');
+    }
+    return token;
+  }
+
+  function coffeescriptHint(editor, options) {
+    return scriptHint(editor, coffeescriptKeywords, getCoffeeScriptToken, options);
+  }
+  CodeMirror.registerHelper("hint", "coffeescript", coffeescriptHint);
+
+  var stringProps = ("charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " +
+                     "toUpperCase toLowerCase split concat match replace search").split(" ");
+  var arrayProps = ("length concat join splice push pop shift unshift slice reverse sort indexOf " +
+                    "lastIndexOf every some filter forEach map reduce reduceRight ").split(" ");
+  var funcProps = "prototype apply call bind".split(" ");
+  var javascriptKeywords = ("break case catch class const continue debugger default delete do else export extends false finally for function " +
+                  "if in import instanceof new null return super switch this throw true try typeof var void while with yield").split(" ");
+  var coffeescriptKeywords = ("and break catch class continue delete do else extends false finally for " +
+                  "if in instanceof isnt new no not null of off on or return switch then throw true try typeof until void while with yes").split(" ");
+
+  function forAllProps(obj, callback) {
+    if (!Object.getOwnPropertyNames || !Object.getPrototypeOf) {
+      for (var name in obj) callback(name)
+    } else {
+      for (var o = obj; o; o = Object.getPrototypeOf(o))
+        Object.getOwnPropertyNames(o).forEach(callback)
+    }
+  }
+
+  function getCompletions(token, context, keywords, options) {
+    var found = [], start = token.string, global = options && options.globalScope || window;
+    function maybeAdd(str) {
+      if (str.lastIndexOf(start, 0) == 0 && !arrayContains(found, str)) found.push(str);
+    }
+    function gatherCompletions(obj) {
+      if (typeof obj == "string") forEach(stringProps, maybeAdd);
+      else if (obj instanceof Array) forEach(arrayProps, maybeAdd);
+      else if (obj instanceof Function) forEach(funcProps, maybeAdd);
+      forAllProps(obj, maybeAdd)
+    }
+
+    if (context && context.length) {
+      // If this is a property, see if it belongs to some object we can
+      // find in the current environment.
+      var obj = context.pop(), base;
+      if (obj.type && obj.type.indexOf("variable") === 0) {
+        if (options && options.additionalContext)
+          base = options.additionalContext[obj.string];
+        if (!options || options.useGlobalScope !== false)
+          base = base || global[obj.string];
+      } else if (obj.type == "string") {
+        base = "";
+      } else if (obj.type == "atom") {
+        base = 1;
+      } else if (obj.type == "function") {
+        if (global.jQuery != null && (obj.string == '$' || obj.string == 'jQuery') &&
+            (typeof global.jQuery == 'function'))
+          base = global.jQuery();
+        else if (global._ != null && (obj.string == '_') && (typeof global._ == 'function'))
+          base = global._();
+      }
+      while (base != null && context.length)
+        base = base[context.pop().string];
+      if (base != null) gatherCompletions(base);
+    } else {
+      // If not, just look in the global object and any local scope
+      // (reading into JS mode internals to get at the local and global variables)
+      for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
+      for (var v = token.state.globalVars; v; v = v.next) maybeAdd(v.name);
+      if (!options || options.useGlobalScope !== false)
+        gatherCompletions(global);
+      forEach(keywords, maybeAdd);
+    }
+    return found;
+  }
+});
diff --git a/public/vendor/plugins/codemirror/addon/hint/show-hint.css b/public/vendor/plugins/codemirror/addon/hint/show-hint.css
new file mode 100644
index 0000000000..5617ccca2b
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/hint/show-hint.css
@@ -0,0 +1,36 @@
+.CodeMirror-hints {
+  position: absolute;
+  z-index: 10;
+  overflow: hidden;
+  list-style: none;
+
+  margin: 0;
+  padding: 2px;
+
+  -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+  -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+  box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+  border-radius: 3px;
+  border: 1px solid silver;
+
+  background: white;
+  font-size: 90%;
+  font-family: monospace;
+
+  max-height: 20em;
+  overflow-y: auto;
+}
+
+.CodeMirror-hint {
+  margin: 0;
+  padding: 0 4px;
+  border-radius: 2px;
+  white-space: pre;
+  color: black;
+  cursor: pointer;
+}
+
+li.CodeMirror-hint-active {
+  background: #08f;
+  color: white;
+}
diff --git a/public/vendor/plugins/codemirror/addon/hint/show-hint.js b/public/vendor/plugins/codemirror/addon/hint/show-hint.js
new file mode 100644
index 0000000000..d70b2ab173
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/hint/show-hint.js
@@ -0,0 +1,460 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  var HINT_ELEMENT_CLASS        = "CodeMirror-hint";
+  var ACTIVE_HINT_ELEMENT_CLASS = "CodeMirror-hint-active";
+
+  // This is the old interface, kept around for now to stay
+  // backwards-compatible.
+  CodeMirror.showHint = function(cm, getHints, options) {
+    if (!getHints) return cm.showHint(options);
+    if (options && options.async) getHints.async = true;
+    var newOpts = {hint: getHints};
+    if (options) for (var prop in options) newOpts[prop] = options[prop];
+    return cm.showHint(newOpts);
+  };
+
+  CodeMirror.defineExtension("showHint", function(options) {
+    options = parseOptions(this, this.getCursor("start"), options);
+    var selections = this.listSelections()
+    if (selections.length > 1) return;
+    // By default, don't allow completion when something is selected.
+    // A hint function can have a `supportsSelection` property to
+    // indicate that it can handle selections.
+    if (this.somethingSelected()) {
+      if (!options.hint.supportsSelection) return;
+      // Don't try with cross-line selections
+      for (var i = 0; i < selections.length; i++)
+        if (selections[i].head.line != selections[i].anchor.line) return;
+    }
+
+    if (this.state.completionActive) this.state.completionActive.close();
+    var completion = this.state.completionActive = new Completion(this, options);
+    if (!completion.options.hint) return;
+
+    CodeMirror.signal(this, "startCompletion", this);
+    completion.update(true);
+  });
+
+  CodeMirror.defineExtension("closeHint", function() {
+    if (this.state.completionActive) this.state.completionActive.close()
+  })
+
+  function Completion(cm, options) {
+    this.cm = cm;
+    this.options = options;
+    this.widget = null;
+    this.debounce = 0;
+    this.tick = 0;
+    this.startPos = this.cm.getCursor("start");
+    this.startLen = this.cm.getLine(this.startPos.line).length - this.cm.getSelection().length;
+
+    var self = this;
+    cm.on("cursorActivity", this.activityFunc = function() { self.cursorActivity(); });
+  }
+
+  var requestAnimationFrame = window.requestAnimationFrame || function(fn) {
+    return setTimeout(fn, 1000/60);
+  };
+  var cancelAnimationFrame = window.cancelAnimationFrame || clearTimeout;
+
+  Completion.prototype = {
+    close: function() {
+      if (!this.active()) return;
+      this.cm.state.completionActive = null;
+      this.tick = null;
+      this.cm.off("cursorActivity", this.activityFunc);
+
+      if (this.widget && this.data) CodeMirror.signal(this.data, "close");
+      if (this.widget) this.widget.close();
+      CodeMirror.signal(this.cm, "endCompletion", this.cm);
+    },
+
+    active: function() {
+      return this.cm.state.completionActive == this;
+    },
+
+    pick: function(data, i) {
+      var completion = data.list[i];
+      if (completion.hint) completion.hint(this.cm, data, completion);
+      else this.cm.replaceRange(getText(completion), completion.from || data.from,
+                                completion.to || data.to, "complete");
+      CodeMirror.signal(data, "pick", completion);
+      this.close();
+    },
+
+    cursorActivity: function() {
+      if (this.debounce) {
+        cancelAnimationFrame(this.debounce);
+        this.debounce = 0;
+      }
+
+      var pos = this.cm.getCursor(), line = this.cm.getLine(pos.line);
+      if (pos.line != this.startPos.line || line.length - pos.ch != this.startLen - this.startPos.ch ||
+          pos.ch < this.startPos.ch || this.cm.somethingSelected() ||
+          (!pos.ch || this.options.closeCharacters.test(line.charAt(pos.ch - 1)))) {
+        this.close();
+      } else {
+        var self = this;
+        this.debounce = requestAnimationFrame(function() {self.update();});
+        if (this.widget) this.widget.disable();
+      }
+    },
+
+    update: function(first) {
+      if (this.tick == null) return
+      var self = this, myTick = ++this.tick
+      fetchHints(this.options.hint, this.cm, this.options, function(data) {
+        if (self.tick == myTick) self.finishUpdate(data, first)
+      })
+    },
+
+    finishUpdate: function(data, first) {
+      if (this.data) CodeMirror.signal(this.data, "update");
+
+      var picked = (this.widget && this.widget.picked) || (first && this.options.completeSingle);
+      if (this.widget) this.widget.close();
+
+      this.data = data;
+
+      if (data && data.list.length) {
+        if (picked && data.list.length == 1) {
+          this.pick(data, 0);
+        } else {
+          this.widget = new Widget(this, data);
+          CodeMirror.signal(data, "shown");
+        }
+      }
+    }
+  };
+
+  function parseOptions(cm, pos, options) {
+    var editor = cm.options.hintOptions;
+    var out = {};
+    for (var prop in defaultOptions) out[prop] = defaultOptions[prop];
+    if (editor) for (var prop in editor)
+      if (editor[prop] !== undefined) out[prop] = editor[prop];
+    if (options) for (var prop in options)
+      if (options[prop] !== undefined) out[prop] = options[prop];
+    if (out.hint.resolve) out.hint = out.hint.resolve(cm, pos)
+    return out;
+  }
+
+  function getText(completion) {
+    if (typeof completion == "string") return completion;
+    else return completion.text;
+  }
+
+  function buildKeyMap(completion, handle) {
+    var baseMap = {
+      Up: function() {handle.moveFocus(-1);},
+      Down: function() {handle.moveFocus(1);},
+      PageUp: function() {handle.moveFocus(-handle.menuSize() + 1, true);},
+      PageDown: function() {handle.moveFocus(handle.menuSize() - 1, true);},
+      Home: function() {handle.setFocus(0);},
+      End: function() {handle.setFocus(handle.length - 1);},
+      Enter: handle.pick,
+      Tab: handle.pick,
+      Esc: handle.close
+    };
+
+    var mac = /Mac/.test(navigator.platform);
+
+    if (mac) {
+      baseMap["Ctrl-P"] = function() {handle.moveFocus(-1);};
+      baseMap["Ctrl-N"] = function() {handle.moveFocus(1);};
+    }
+
+    var custom = completion.options.customKeys;
+    var ourMap = custom ? {} : baseMap;
+    function addBinding(key, val) {
+      var bound;
+      if (typeof val != "string")
+        bound = function(cm) { return val(cm, handle); };
+      // This mechanism is deprecated
+      else if (baseMap.hasOwnProperty(val))
+        bound = baseMap[val];
+      else
+        bound = val;
+      ourMap[key] = bound;
+    }
+    if (custom)
+      for (var key in custom) if (custom.hasOwnProperty(key))
+        addBinding(key, custom[key]);
+    var extra = completion.options.extraKeys;
+    if (extra)
+      for (var key in extra) if (extra.hasOwnProperty(key))
+        addBinding(key, extra[key]);
+    return ourMap;
+  }
+
+  function getHintElement(hintsElement, el) {
+    while (el && el != hintsElement) {
+      if (el.nodeName.toUpperCase() === "LI" && el.parentNode == hintsElement) return el;
+      el = el.parentNode;
+    }
+  }
+
+  function Widget(completion, data) {
+    this.completion = completion;
+    this.data = data;
+    this.picked = false;
+    var widget = this, cm = completion.cm;
+    var ownerDocument = cm.getInputField().ownerDocument;
+    var parentWindow = ownerDocument.defaultView || ownerDocument.parentWindow;
+
+    var hints = this.hints = ownerDocument.createElement("ul");
+    var theme = completion.cm.options.theme;
+    hints.className = "CodeMirror-hints " + theme;
+    this.selectedHint = data.selectedHint || 0;
+
+    var completions = data.list;
+    for (var i = 0; i < completions.length; ++i) {
+      var elt = hints.appendChild(ownerDocument.createElement("li")), cur = completions[i];
+      var className = HINT_ELEMENT_CLASS + (i != this.selectedHint ? "" : " " + ACTIVE_HINT_ELEMENT_CLASS);
+      if (cur.className != null) className = cur.className + " " + className;
+      elt.className = className;
+      if (cur.render) cur.render(elt, data, cur);
+      else elt.appendChild(ownerDocument.createTextNode(cur.displayText || getText(cur)));
+      elt.hintId = i;
+    }
+
+    var container = completion.options.container || ownerDocument.body;
+    var pos = cm.cursorCoords(completion.options.alignWithWord ? data.from : null);
+    var left = pos.left, top = pos.bottom, below = true;
+    var offsetLeft = 0, offsetTop = 0;
+    if (container !== ownerDocument.body) {
+      // We offset the cursor position because left and top are relative to the offsetParent's top left corner.
+      var isContainerPositioned = ['absolute', 'relative', 'fixed'].indexOf(parentWindow.getComputedStyle(container).position) !== -1;
+      var offsetParent = isContainerPositioned ? container : container.offsetParent;
+      var offsetParentPosition = offsetParent.getBoundingClientRect();
+      var bodyPosition = ownerDocument.body.getBoundingClientRect();
+      offsetLeft = (offsetParentPosition.left - bodyPosition.left - offsetParent.scrollLeft);
+      offsetTop = (offsetParentPosition.top - bodyPosition.top - offsetParent.scrollTop);
+    }
+    hints.style.left = (left - offsetLeft) + "px";
+    hints.style.top = (top - offsetTop) + "px";
+
+    // If we're at the edge of the screen, then we want the menu to appear on the left of the cursor.
+    var winW = parentWindow.innerWidth || Math.max(ownerDocument.body.offsetWidth, ownerDocument.documentElement.offsetWidth);
+    var winH = parentWindow.innerHeight || Math.max(ownerDocument.body.offsetHeight, ownerDocument.documentElement.offsetHeight);
+    container.appendChild(hints);
+    var box = hints.getBoundingClientRect(), overlapY = box.bottom - winH;
+    var scrolls = hints.scrollHeight > hints.clientHeight + 1
+    var startScroll = cm.getScrollInfo();
+
+    if (overlapY > 0) {
+      var height = box.bottom - box.top, curTop = pos.top - (pos.bottom - box.top);
+      if (curTop - height > 0) { // Fits above cursor
+        hints.style.top = (top = pos.top - height - offsetTop) + "px";
+        below = false;
+      } else if (height > winH) {
+        hints.style.height = (winH - 5) + "px";
+        hints.style.top = (top = pos.bottom - box.top - offsetTop) + "px";
+        var cursor = cm.getCursor();
+        if (data.from.ch != cursor.ch) {
+          pos = cm.cursorCoords(cursor);
+          hints.style.left = (left = pos.left - offsetLeft) + "px";
+          box = hints.getBoundingClientRect();
+        }
+      }
+    }
+    var overlapX = box.right - winW;
+    if (overlapX > 0) {
+      if (box.right - box.left > winW) {
+        hints.style.width = (winW - 5) + "px";
+        overlapX -= (box.right - box.left) - winW;
+      }
+      hints.style.left = (left = pos.left - overlapX - offsetLeft) + "px";
+    }
+    if (scrolls) for (var node = hints.firstChild; node; node = node.nextSibling)
+      node.style.paddingRight = cm.display.nativeBarWidth + "px"
+
+    cm.addKeyMap(this.keyMap = buildKeyMap(completion, {
+      moveFocus: function(n, avoidWrap) { widget.changeActive(widget.selectedHint + n, avoidWrap); },
+      setFocus: function(n) { widget.changeActive(n); },
+      menuSize: function() { return widget.screenAmount(); },
+      length: completions.length,
+      close: function() { completion.close(); },
+      pick: function() { widget.pick(); },
+      data: data
+    }));
+
+    if (completion.options.closeOnUnfocus) {
+      var closingOnBlur;
+      cm.on("blur", this.onBlur = function() { closingOnBlur = setTimeout(function() { completion.close(); }, 100); });
+      cm.on("focus", this.onFocus = function() { clearTimeout(closingOnBlur); });
+    }
+
+    cm.on("scroll", this.onScroll = function() {
+      var curScroll = cm.getScrollInfo(), editor = cm.getWrapperElement().getBoundingClientRect();
+      var newTop = top + startScroll.top - curScroll.top;
+      var point = newTop - (parentWindow.pageYOffset || (ownerDocument.documentElement || ownerDocument.body).scrollTop);
+      if (!below) point += hints.offsetHeight;
+      if (point <= editor.top || point >= editor.bottom) return completion.close();
+      hints.style.top = newTop + "px";
+      hints.style.left = (left + startScroll.left - curScroll.left) + "px";
+    });
+
+    CodeMirror.on(hints, "dblclick", function(e) {
+      var t = getHintElement(hints, e.target || e.srcElement);
+      if (t && t.hintId != null) {widget.changeActive(t.hintId); widget.pick();}
+    });
+
+    CodeMirror.on(hints, "click", function(e) {
+      var t = getHintElement(hints, e.target || e.srcElement);
+      if (t && t.hintId != null) {
+        widget.changeActive(t.hintId);
+        if (completion.options.completeOnSingleClick) widget.pick();
+      }
+    });
+
+    CodeMirror.on(hints, "mousedown", function() {
+      setTimeout(function(){cm.focus();}, 20);
+    });
+
+    CodeMirror.signal(data, "select", completions[this.selectedHint], hints.childNodes[this.selectedHint]);
+    return true;
+  }
+
+  Widget.prototype = {
+    close: function() {
+      if (this.completion.widget != this) return;
+      this.completion.widget = null;
+      this.hints.parentNode.removeChild(this.hints);
+      this.completion.cm.removeKeyMap(this.keyMap);
+
+      var cm = this.completion.cm;
+      if (this.completion.options.closeOnUnfocus) {
+        cm.off("blur", this.onBlur);
+        cm.off("focus", this.onFocus);
+      }
+      cm.off("scroll", this.onScroll);
+    },
+
+    disable: function() {
+      this.completion.cm.removeKeyMap(this.keyMap);
+      var widget = this;
+      this.keyMap = {Enter: function() { widget.picked = true; }};
+      this.completion.cm.addKeyMap(this.keyMap);
+    },
+
+    pick: function() {
+      this.completion.pick(this.data, this.selectedHint);
+    },
+
+    changeActive: function(i, avoidWrap) {
+      if (i >= this.data.list.length)
+        i = avoidWrap ? this.data.list.length - 1 : 0;
+      else if (i < 0)
+        i = avoidWrap ? 0  : this.data.list.length - 1;
+      if (this.selectedHint == i) return;
+      var node = this.hints.childNodes[this.selectedHint];
+      if (node) node.className = node.className.replace(" " + ACTIVE_HINT_ELEMENT_CLASS, "");
+      node = this.hints.childNodes[this.selectedHint = i];
+      node.className += " " + ACTIVE_HINT_ELEMENT_CLASS;
+      if (node.offsetTop < this.hints.scrollTop)
+        this.hints.scrollTop = node.offsetTop - 3;
+      else if (node.offsetTop + node.offsetHeight > this.hints.scrollTop + this.hints.clientHeight)
+        this.hints.scrollTop = node.offsetTop + node.offsetHeight - this.hints.clientHeight + 3;
+      CodeMirror.signal(this.data, "select", this.data.list[this.selectedHint], node);
+    },
+
+    screenAmount: function() {
+      return Math.floor(this.hints.clientHeight / this.hints.firstChild.offsetHeight) || 1;
+    }
+  };
+
+  function applicableHelpers(cm, helpers) {
+    if (!cm.somethingSelected()) return helpers
+    var result = []
+    for (var i = 0; i < helpers.length; i++)
+      if (helpers[i].supportsSelection) result.push(helpers[i])
+    return result
+  }
+
+  function fetchHints(hint, cm, options, callback) {
+    if (hint.async) {
+      hint(cm, callback, options)
+    } else {
+      var result = hint(cm, options)
+      if (result && result.then) result.then(callback)
+      else callback(result)
+    }
+  }
+
+  function resolveAutoHints(cm, pos) {
+    var helpers = cm.getHelpers(pos, "hint"), words
+    if (helpers.length) {
+      var resolved = function(cm, callback, options) {
+        var app = applicableHelpers(cm, helpers);
+        function run(i) {
+          if (i == app.length) return callback(null)
+          fetchHints(app[i], cm, options, function(result) {
+            if (result && result.list.length > 0) callback(result)
+            else run(i + 1)
+          })
+        }
+        run(0)
+      }
+      resolved.async = true
+      resolved.supportsSelection = true
+      return resolved
+    } else if (words = cm.getHelper(cm.getCursor(), "hintWords")) {
+      return function(cm) { return CodeMirror.hint.fromList(cm, {words: words}) }
+    } else if (CodeMirror.hint.anyword) {
+      return function(cm, options) { return CodeMirror.hint.anyword(cm, options) }
+    } else {
+      return function() {}
+    }
+  }
+
+  CodeMirror.registerHelper("hint", "auto", {
+    resolve: resolveAutoHints
+  });
+
+  CodeMirror.registerHelper("hint", "fromList", function(cm, options) {
+    var cur = cm.getCursor(), token = cm.getTokenAt(cur)
+    var term, from = CodeMirror.Pos(cur.line, token.start), to = cur
+    if (token.start < cur.ch && /\w/.test(token.string.charAt(cur.ch - token.start - 1))) {
+      term = token.string.substr(0, cur.ch - token.start)
+    } else {
+      term = ""
+      from = cur
+    }
+    var found = [];
+    for (var i = 0; i < options.words.length; i++) {
+      var word = options.words[i];
+      if (word.slice(0, term.length) == term)
+        found.push(word);
+    }
+
+    if (found.length) return {list: found, from: from, to: to};
+  });
+
+  CodeMirror.commands.autocomplete = CodeMirror.showHint;
+
+  var defaultOptions = {
+    hint: CodeMirror.hint.auto,
+    completeSingle: true,
+    alignWithWord: true,
+    closeCharacters: /[\s()\[\]{};:>,]/,
+    closeOnUnfocus: true,
+    completeOnSingleClick: true,
+    container: null,
+    customKeys: null,
+    extraKeys: null
+  };
+
+  CodeMirror.defineOption("hintOptions", null);
+});
diff --git a/public/vendor/plugins/codemirror/addon/hint/sql-hint.js b/public/vendor/plugins/codemirror/addon/hint/sql-hint.js
new file mode 100644
index 0000000000..444eba8b15
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/hint/sql-hint.js
@@ -0,0 +1,304 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"), require("../../mode/sql/sql"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror", "../../mode/sql/sql"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  var tables;
+  var defaultTable;
+  var keywords;
+  var identifierQuote;
+  var CONS = {
+    QUERY_DIV: ";",
+    ALIAS_KEYWORD: "AS"
+  };
+  var Pos = CodeMirror.Pos, cmpPos = CodeMirror.cmpPos;
+
+  function isArray(val) { return Object.prototype.toString.call(val) == "[object Array]" }
+
+  function getKeywords(editor) {
+    var mode = editor.doc.modeOption;
+    if (mode === "sql") mode = "text/x-sql";
+    return CodeMirror.resolveMode(mode).keywords;
+  }
+
+  function getIdentifierQuote(editor) {
+    var mode = editor.doc.modeOption;
+    if (mode === "sql") mode = "text/x-sql";
+    return CodeMirror.resolveMode(mode).identifierQuote || "`";
+  }
+
+  function getText(item) {
+    return typeof item == "string" ? item : item.text;
+  }
+
+  function wrapTable(name, value) {
+    if (isArray(value)) value = {columns: value}
+    if (!value.text) value.text = name
+    return value
+  }
+
+  function parseTables(input) {
+    var result = {}
+    if (isArray(input)) {
+      for (var i = input.length - 1; i >= 0; i--) {
+        var item = input[i]
+        result[getText(item).toUpperCase()] = wrapTable(getText(item), item)
+      }
+    } else if (input) {
+      for (var name in input)
+        result[name.toUpperCase()] = wrapTable(name, input[name])
+    }
+    return result
+  }
+
+  function getTable(name) {
+    return tables[name.toUpperCase()]
+  }
+
+  function shallowClone(object) {
+    var result = {};
+    for (var key in object) if (object.hasOwnProperty(key))
+      result[key] = object[key];
+    return result;
+  }
+
+  function match(string, word) {
+    var len = string.length;
+    var sub = getText(word).substr(0, len);
+    return string.toUpperCase() === sub.toUpperCase();
+  }
+
+  function addMatches(result, search, wordlist, formatter) {
+    if (isArray(wordlist)) {
+      for (var i = 0; i < wordlist.length; i++)
+        if (match(search, wordlist[i])) result.push(formatter(wordlist[i]))
+    } else {
+      for (var word in wordlist) if (wordlist.hasOwnProperty(word)) {
+        var val = wordlist[word]
+        if (!val || val === true)
+          val = word
+        else
+          val = val.displayText ? {text: val.text, displayText: val.displayText} : val.text
+        if (match(search, val)) result.push(formatter(val))
+      }
+    }
+  }
+
+  function cleanName(name) {
+    // Get rid name from identifierQuote and preceding dot(.)
+    if (name.charAt(0) == ".") {
+      name = name.substr(1);
+    }
+    // replace doublicated identifierQuotes with single identifierQuotes
+    // and remove single identifierQuotes
+    var nameParts = name.split(identifierQuote+identifierQuote);
+    for (var i = 0; i < nameParts.length; i++)
+      nameParts[i] = nameParts[i].replace(new RegExp(identifierQuote,"g"), "");
+    return nameParts.join(identifierQuote);
+  }
+
+  function insertIdentifierQuotes(name) {
+    var nameParts = getText(name).split(".");
+    for (var i = 0; i < nameParts.length; i++)
+      nameParts[i] = identifierQuote +
+        // doublicate identifierQuotes
+        nameParts[i].replace(new RegExp(identifierQuote,"g"), identifierQuote+identifierQuote) +
+        identifierQuote;
+    var escaped = nameParts.join(".");
+    if (typeof name == "string") return escaped;
+    name = shallowClone(name);
+    name.text = escaped;
+    return name;
+  }
+
+  function nameCompletion(cur, token, result, editor) {
+    // Try to complete table, column names and return start position of completion
+    var useIdentifierQuotes = false;
+    var nameParts = [];
+    var start = token.start;
+    var cont = true;
+    while (cont) {
+      cont = (token.string.charAt(0) == ".");
+      useIdentifierQuotes = useIdentifierQuotes || (token.string.charAt(0) == identifierQuote);
+
+      start = token.start;
+      nameParts.unshift(cleanName(token.string));
+
+      token = editor.getTokenAt(Pos(cur.line, token.start));
+      if (token.string == ".") {
+        cont = true;
+        token = editor.getTokenAt(Pos(cur.line, token.start));
+      }
+    }
+
+    // Try to complete table names
+    var string = nameParts.join(".");
+    addMatches(result, string, tables, function(w) {
+      return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
+    });
+
+    // Try to complete columns from defaultTable
+    addMatches(result, string, defaultTable, function(w) {
+      return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
+    });
+
+    // Try to complete columns
+    string = nameParts.pop();
+    var table = nameParts.join(".");
+
+    var alias = false;
+    var aliasTable = table;
+    // Check if table is available. If not, find table by Alias
+    if (!getTable(table)) {
+      var oldTable = table;
+      table = findTableByAlias(table, editor);
+      if (table !== oldTable) alias = true;
+    }
+
+    var columns = getTable(table);
+    if (columns && columns.columns)
+      columns = columns.columns;
+
+    if (columns) {
+      addMatches(result, string, columns, function(w) {
+        var tableInsert = table;
+        if (alias == true) tableInsert = aliasTable;
+        if (typeof w == "string") {
+          w = tableInsert + "." + w;
+        } else {
+          w = shallowClone(w);
+          w.text = tableInsert + "." + w.text;
+        }
+        return useIdentifierQuotes ? insertIdentifierQuotes(w) : w;
+      });
+    }
+
+    return start;
+  }
+
+  function eachWord(lineText, f) {
+    var words = lineText.split(/\s+/)
+    for (var i = 0; i < words.length; i++)
+      if (words[i]) f(words[i].replace(/[,;]/g, ''))
+  }
+
+  function findTableByAlias(alias, editor) {
+    var doc = editor.doc;
+    var fullQuery = doc.getValue();
+    var aliasUpperCase = alias.toUpperCase();
+    var previousWord = "";
+    var table = "";
+    var separator = [];
+    var validRange = {
+      start: Pos(0, 0),
+      end: Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).length)
+    };
+
+    //add separator
+    var indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV);
+    while(indexOfSeparator != -1) {
+      separator.push(doc.posFromIndex(indexOfSeparator));
+      indexOfSeparator = fullQuery.indexOf(CONS.QUERY_DIV, indexOfSeparator+1);
+    }
+    separator.unshift(Pos(0, 0));
+    separator.push(Pos(editor.lastLine(), editor.getLineHandle(editor.lastLine()).text.length));
+
+    //find valid range
+    var prevItem = null;
+    var current = editor.getCursor()
+    for (var i = 0; i < separator.length; i++) {
+      if ((prevItem == null || cmpPos(current, prevItem) > 0) && cmpPos(current, separator[i]) <= 0) {
+        validRange = {start: prevItem, end: separator[i]};
+        break;
+      }
+      prevItem = separator[i];
+    }
+
+    if (validRange.start) {
+      var query = doc.getRange(validRange.start, validRange.end, false);
+
+      for (var i = 0; i < query.length; i++) {
+        var lineText = query[i];
+        eachWord(lineText, function(word) {
+          var wordUpperCase = word.toUpperCase();
+          if (wordUpperCase === aliasUpperCase && getTable(previousWord))
+            table = previousWord;
+          if (wordUpperCase !== CONS.ALIAS_KEYWORD)
+            previousWord = word;
+        });
+        if (table) break;
+      }
+    }
+    return table;
+  }
+
+  CodeMirror.registerHelper("hint", "sql", function(editor, options) {
+    tables = parseTables(options && options.tables)
+    var defaultTableName = options && options.defaultTable;
+    var disableKeywords = options && options.disableKeywords;
+    defaultTable = defaultTableName && getTable(defaultTableName);
+    keywords = getKeywords(editor);
+    identifierQuote = getIdentifierQuote(editor);
+
+    if (defaultTableName && !defaultTable)
+      defaultTable = findTableByAlias(defaultTableName, editor);
+
+    defaultTable = defaultTable || [];
+
+    if (defaultTable.columns)
+      defaultTable = defaultTable.columns;
+
+    var cur = editor.getCursor();
+    var result = [];
+    var token = editor.getTokenAt(cur), start, end, search;
+    if (token.end > cur.ch) {
+      token.end = cur.ch;
+      token.string = token.string.slice(0, cur.ch - token.start);
+    }
+
+    if (token.string.match(/^[.`"\w@]\w*$/)) {
+      search = token.string;
+      start = token.start;
+      end = token.end;
+    } else {
+      start = end = cur.ch;
+      search = "";
+    }
+    if (search.charAt(0) == "." || search.charAt(0) == identifierQuote) {
+      start = nameCompletion(cur, token, result, editor);
+    } else {
+      var objectOrClass = function(w, className) {
+        if (typeof w === "object") {
+          w.className = className;
+        } else {
+          w = { text: w, className: className };
+        }
+        return w;
+      };
+    addMatches(result, search, defaultTable, function(w) {
+        return objectOrClass(w, "CodeMirror-hint-table CodeMirror-hint-default-table");
+    });
+    addMatches(
+        result,
+        search,
+        tables, function(w) {
+          return objectOrClass(w, "CodeMirror-hint-table");
+        }
+    );
+    if (!disableKeywords)
+      addMatches(result, search, keywords, function(w) {
+          return objectOrClass(w.toUpperCase(), "CodeMirror-hint-keyword");
+      });
+  }
+
+    return {list: result, from: Pos(cur.line, start), to: Pos(cur.line, end)};
+  });
+});
diff --git a/public/vendor/plugins/codemirror/addon/hint/xml-hint.js b/public/vendor/plugins/codemirror/addon/hint/xml-hint.js
new file mode 100644
index 0000000000..7575b3707e
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/hint/xml-hint.js
@@ -0,0 +1,123 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  var Pos = CodeMirror.Pos;
+
+  function matches(hint, typed, matchInMiddle) {
+    if (matchInMiddle) return hint.indexOf(typed) >= 0;
+    else return hint.lastIndexOf(typed, 0) == 0;
+  }
+
+  function getHints(cm, options) {
+    var tags = options && options.schemaInfo;
+    var quote = (options && options.quoteChar) || '"';
+    var matchInMiddle = options && options.matchInMiddle;
+    if (!tags) return;
+    var cur = cm.getCursor(), token = cm.getTokenAt(cur);
+    if (token.end > cur.ch) {
+      token.end = cur.ch;
+      token.string = token.string.slice(0, cur.ch - token.start);
+    }
+    var inner = CodeMirror.innerMode(cm.getMode(), token.state);
+    if (!inner.mode.xmlCurrentTag) return
+    var result = [], replaceToken = false, prefix;
+    var tag = /\btag\b/.test(token.type) && !/>$/.test(token.string);
+    var tagName = tag && /^\w/.test(token.string), tagStart;
+
+    if (tagName) {
+      var before = cm.getLine(cur.line).slice(Math.max(0, token.start - 2), token.start);
+      var tagType = /<\/$/.test(before) ? "close" : /<$/.test(before) ? "open" : null;
+      if (tagType) tagStart = token.start - (tagType == "close" ? 2 : 1);
+    } else if (tag && token.string == "<") {
+      tagType = "open";
+    } else if (tag && token.string == "</") {
+      tagType = "close";
+    }
+
+    var tagInfo = inner.mode.xmlCurrentTag(inner.state)
+    if (!tag && !tagInfo || tagType) {
+      if (tagName)
+        prefix = token.string;
+      replaceToken = tagType;
+      var context = inner.mode.xmlCurrentContext ? inner.mode.xmlCurrentContext(inner.state) : []
+      var inner = context.length && context[context.length - 1]
+      var curTag = inner && tags[inner]
+      var childList = inner ? curTag && curTag.children : tags["!top"];
+      if (childList && tagType != "close") {
+        for (var i = 0; i < childList.length; ++i) if (!prefix || matches(childList[i], prefix, matchInMiddle))
+          result.push("<" + childList[i]);
+      } else if (tagType != "close") {
+        for (var name in tags)
+          if (tags.hasOwnProperty(name) && name != "!top" && name != "!attrs" && (!prefix || matches(name, prefix, matchInMiddle)))
+            result.push("<" + name);
+      }
+      if (inner && (!prefix || tagType == "close" && matches(inner, prefix, matchInMiddle)))
+        result.push("</" + inner + ">");
+    } else {
+      // Attribute completion
+      var curTag = tagInfo && tags[tagInfo.name], attrs = curTag && curTag.attrs;
+      var globalAttrs = tags["!attrs"];
+      if (!attrs && !globalAttrs) return;
+      if (!attrs) {
+        attrs = globalAttrs;
+      } else if (globalAttrs) { // Combine tag-local and global attributes
+        var set = {};
+        for (var nm in globalAttrs) if (globalAttrs.hasOwnProperty(nm)) set[nm] = globalAttrs[nm];
+        for (var nm in attrs) if (attrs.hasOwnProperty(nm)) set[nm] = attrs[nm];
+        attrs = set;
+      }
+      if (token.type == "string" || token.string == "=") { // A value
+        var before = cm.getRange(Pos(cur.line, Math.max(0, cur.ch - 60)),
+                                 Pos(cur.line, token.type == "string" ? token.start : token.end));
+        var atName = before.match(/([^\s\u00a0=<>\"\']+)=$/), atValues;
+        if (!atName || !attrs.hasOwnProperty(atName[1]) || !(atValues = attrs[atName[1]])) return;
+        if (typeof atValues == 'function') atValues = atValues.call(this, cm); // Functions can be used to supply values for autocomplete widget
+        if (token.type == "string") {
+          prefix = token.string;
+          var n = 0;
+          if (/['"]/.test(token.string.charAt(0))) {
+            quote = token.string.charAt(0);
+            prefix = token.string.slice(1);
+            n++;
+          }
+          var len = token.string.length;
+          if (/['"]/.test(token.string.charAt(len - 1))) {
+            quote = token.string.charAt(len - 1);
+            prefix = token.string.substr(n, len - 2);
+          }
+          if (n) { // an opening quote
+            var line = cm.getLine(cur.line);
+            if (line.length > token.end && line.charAt(token.end) == quote) token.end++; // include a closing quote
+          }
+          replaceToken = true;
+        }
+        for (var i = 0; i < atValues.length; ++i) if (!prefix || matches(atValues[i], prefix, matchInMiddle))
+          result.push(quote + atValues[i] + quote);
+      } else { // An attribute name
+        if (token.type == "attribute") {
+          prefix = token.string;
+          replaceToken = true;
+        }
+        for (var attr in attrs) if (attrs.hasOwnProperty(attr) && (!prefix || matches(attr, prefix, matchInMiddle)))
+          result.push(attr);
+      }
+    }
+    return {
+      list: result,
+      from: replaceToken ? Pos(cur.line, tagStart == null ? token.start : tagStart) : cur,
+      to: replaceToken ? Pos(cur.line, token.end) : cur
+    };
+  }
+
+  CodeMirror.registerHelper("hint", "xml", getHints);
+});
diff --git a/public/vendor/plugins/codemirror/addon/lint/coffeescript-lint.js b/public/vendor/plugins/codemirror/addon/lint/coffeescript-lint.js
new file mode 100644
index 0000000000..a54c703516
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/lint/coffeescript-lint.js
@@ -0,0 +1,47 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// Depends on coffeelint.js from http://www.coffeelint.org/js/coffeelint.js
+
+// declare global: coffeelint
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.registerHelper("lint", "coffeescript", function(text) {
+  var found = [];
+  if (!window.coffeelint) {
+    if (window.console) {
+      window.console.error("Error: window.coffeelint not defined, CodeMirror CoffeeScript linting cannot run.");
+    }
+    return found;
+  }
+  var parseError = function(err) {
+    var loc = err.lineNumber;
+    found.push({from: CodeMirror.Pos(loc-1, 0),
+                to: CodeMirror.Pos(loc, 0),
+                severity: err.level,
+                message: err.message});
+  };
+  try {
+    var res = coffeelint.lint(text);
+    for(var i = 0; i < res.length; i++) {
+      parseError(res[i]);
+    }
+  } catch(e) {
+    found.push({from: CodeMirror.Pos(e.location.first_line, 0),
+                to: CodeMirror.Pos(e.location.last_line, e.location.last_column),
+                severity: 'error',
+                message: e.message});
+  }
+  return found;
+});
+
+});
diff --git a/public/vendor/plugins/codemirror/addon/lint/css-lint.js b/public/vendor/plugins/codemirror/addon/lint/css-lint.js
new file mode 100644
index 0000000000..6058a73eb1
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/lint/css-lint.js
@@ -0,0 +1,40 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// Depends on csslint.js from https://github.com/stubbornella/csslint
+
+// declare global: CSSLint
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.registerHelper("lint", "css", function(text, options) {
+  var found = [];
+  if (!window.CSSLint) {
+    if (window.console) {
+        window.console.error("Error: window.CSSLint not defined, CodeMirror CSS linting cannot run.");
+    }
+    return found;
+  }
+  var results = CSSLint.verify(text, options), messages = results.messages, message = null;
+  for ( var i = 0; i < messages.length; i++) {
+    message = messages[i];
+    var startLine = message.line -1, endLine = message.line -1, startCol = message.col -1, endCol = message.col;
+    found.push({
+      from: CodeMirror.Pos(startLine, startCol),
+      to: CodeMirror.Pos(endLine, endCol),
+      message: message.message,
+      severity : message.type
+    });
+  }
+  return found;
+});
+
+});
diff --git a/public/vendor/plugins/codemirror/addon/lint/html-lint.js b/public/vendor/plugins/codemirror/addon/lint/html-lint.js
new file mode 100644
index 0000000000..5295c33331
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/lint/html-lint.js
@@ -0,0 +1,59 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// Depends on htmlhint.js from http://htmlhint.com/js/htmlhint.js
+
+// declare global: HTMLHint
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"), require("htmlhint"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror", "htmlhint"], mod);
+  else // Plain browser env
+    mod(CodeMirror, window.HTMLHint);
+})(function(CodeMirror, HTMLHint) {
+  "use strict";
+
+  var defaultRules = {
+    "tagname-lowercase": true,
+    "attr-lowercase": true,
+    "attr-value-double-quotes": true,
+    "doctype-first": false,
+    "tag-pair": true,
+    "spec-char-escape": true,
+    "id-unique": true,
+    "src-not-empty": true,
+    "attr-no-duplication": true
+  };
+
+  CodeMirror.registerHelper("lint", "html", function(text, options) {
+    var found = [];
+    if (HTMLHint && !HTMLHint.verify) {
+      if(typeof HTMLHint.default !== 'undefined') {
+        HTMLHint = HTMLHint.default;
+      } else {
+        HTMLHint = HTMLHint.HTMLHint;
+      }
+    }
+    if (!HTMLHint) HTMLHint = window.HTMLHint;
+    if (!HTMLHint) {
+      if (window.console) {
+          window.console.error("Error: HTMLHint not found, not defined on window, or not available through define/require, CodeMirror HTML linting cannot run.");
+      }
+      return found;
+    }
+    var messages = HTMLHint.verify(text, options && options.rules || defaultRules);
+    for (var i = 0; i < messages.length; i++) {
+      var message = messages[i];
+      var startLine = message.line - 1, endLine = message.line - 1, startCol = message.col - 1, endCol = message.col;
+      found.push({
+        from: CodeMirror.Pos(startLine, startCol),
+        to: CodeMirror.Pos(endLine, endCol),
+        message: message.message,
+        severity : message.type
+      });
+    }
+    return found;
+  });
+});
diff --git a/public/vendor/plugins/codemirror/addon/lint/javascript-lint.js b/public/vendor/plugins/codemirror/addon/lint/javascript-lint.js
new file mode 100644
index 0000000000..cc132d7f82
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/lint/javascript-lint.js
@@ -0,0 +1,63 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+  // declare global: JSHINT
+
+  function validator(text, options) {
+    if (!window.JSHINT) {
+      if (window.console) {
+        window.console.error("Error: window.JSHINT not defined, CodeMirror JavaScript linting cannot run.");
+      }
+      return [];
+    }
+    if (!options.indent) // JSHint error.character actually is a column index, this fixes underlining on lines using tabs for indentation
+      options.indent = 1; // JSHint default value is 4
+    JSHINT(text, options, options.globals);
+    var errors = JSHINT.data().errors, result = [];
+    if (errors) parseErrors(errors, result);
+    return result;
+  }
+
+  CodeMirror.registerHelper("lint", "javascript", validator);
+
+  function parseErrors(errors, output) {
+    for ( var i = 0; i < errors.length; i++) {
+      var error = errors[i];
+      if (error) {
+        if (error.line <= 0) {
+          if (window.console) {
+            window.console.warn("Cannot display JSHint error (invalid line " + error.line + ")", error);
+          }
+          continue;
+        }
+
+        var start = error.character - 1, end = start + 1;
+        if (error.evidence) {
+          var index = error.evidence.substring(start).search(/.\b/);
+          if (index > -1) {
+            end += index;
+          }
+        }
+
+        // Convert to format expected by validation service
+        var hint = {
+          message: error.reason,
+          severity: error.code ? (error.code.startsWith('W') ? "warning" : "error") : "error",
+          from: CodeMirror.Pos(error.line - 1, start),
+          to: CodeMirror.Pos(error.line - 1, end)
+        };
+
+        output.push(hint);
+      }
+    }
+  }
+});
diff --git a/public/vendor/plugins/codemirror/addon/lint/json-lint.js b/public/vendor/plugins/codemirror/addon/lint/json-lint.js
new file mode 100644
index 0000000000..ac1d6ec28c
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/lint/json-lint.js
@@ -0,0 +1,40 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// Depends on jsonlint.js from https://github.com/zaach/jsonlint
+
+// declare global: jsonlint
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.registerHelper("lint", "json", function(text) {
+  var found = [];
+  if (!window.jsonlint) {
+    if (window.console) {
+      window.console.error("Error: window.jsonlint not defined, CodeMirror JSON linting cannot run.");
+    }
+    return found;
+  }
+  // for jsonlint's web dist jsonlint is exported as an object with a single property parser, of which parseError
+  // is a subproperty
+  var jsonlint = window.jsonlint.parser || window.jsonlint
+  jsonlint.parseError = function(str, hash) {
+    var loc = hash.loc;
+    found.push({from: CodeMirror.Pos(loc.first_line - 1, loc.first_column),
+                to: CodeMirror.Pos(loc.last_line - 1, loc.last_column),
+                message: str});
+  };
+  try { jsonlint.parse(text); }
+  catch(e) {}
+  return found;
+});
+
+});
diff --git a/public/vendor/plugins/codemirror/addon/lint/lint.css b/public/vendor/plugins/codemirror/addon/lint/lint.css
new file mode 100644
index 0000000000..f097cfe345
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/lint/lint.css
@@ -0,0 +1,73 @@
+/* The lint marker gutter */
+.CodeMirror-lint-markers {
+  width: 16px;
+}
+
+.CodeMirror-lint-tooltip {
+  background-color: #ffd;
+  border: 1px solid black;
+  border-radius: 4px 4px 4px 4px;
+  color: black;
+  font-family: 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("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJDw4cOCW1/KIAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAHElEQVQI12NggIL/DAz/GdA5/xkY/qPKMDAwAADLZwf5rvm+LQAAAABJRU5ErkJggg==")
+  ;
+}
+
+.CodeMirror-lint-mark-warning {
+  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAADCAYAAAC09K7GAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9sJFhQXEbhTg7YAAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAAMklEQVQI12NkgIIvJ3QXMjAwdDN+OaEbysDA4MPAwNDNwMCwiOHLCd1zX07o6kBVGQEAKBANtobskNMAAAAASUVORK5CYII=");
+}
+
+.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;
+}
+
+.CodeMirror-lint-message-error, .CodeMirror-lint-message-warning {
+  padding-left: 18px;
+  background-position: top left;
+  background-repeat: no-repeat;
+}
+
+.CodeMirror-lint-marker-error, .CodeMirror-lint-message-error {
+  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAHlBMVEW7AAC7AACxAAC7AAC7AAAAAAC4AAC5AAD///+7AAAUdclpAAAABnRSTlMXnORSiwCK0ZKSAAAATUlEQVR42mWPOQ7AQAgDuQLx/z8csYRmPRIFIwRGnosRrpamvkKi0FTIiMASR3hhKW+hAN6/tIWhu9PDWiTGNEkTtIOucA5Oyr9ckPgAWm0GPBog6v4AAAAASUVORK5CYII=");
+}
+
+.CodeMirror-lint-marker-warning, .CodeMirror-lint-message-warning {
+  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAANlBMVEX/uwDvrwD/uwD/uwD/uwD/uwD/uwD/uwD/uwD6twD/uwAAAADurwD2tQD7uAD+ugAAAAD/uwDhmeTRAAAADHRSTlMJ8mN1EYcbmiixgACm7WbuAAAAVklEQVR42n3PUQqAIBBFUU1LLc3u/jdbOJoW1P08DA9Gba8+YWJ6gNJoNYIBzAA2chBth5kLmG9YUoG0NHAUwFXwO9LuBQL1giCQb8gC9Oro2vp5rncCIY8L8uEx5ZkAAAAASUVORK5CYII=");
+}
+
+.CodeMirror-lint-marker-multiple {
+  background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAcAAAAHCAMAAADzjKfhAAAACVBMVEUAAAAAAAC/v7914kyHAAAAAXRSTlMAQObYZgAAACNJREFUeNo1ioEJAAAIwmz/H90iFFSGJgFMe3gaLZ0od+9/AQZ0ADosbYraAAAAAElFTkSuQmCC");
+  background-repeat: no-repeat;
+  background-position: right bottom;
+  width: 100%; height: 100%;
+}
diff --git a/public/vendor/plugins/codemirror/addon/lint/lint.js b/public/vendor/plugins/codemirror/addon/lint/lint.js
new file mode 100644
index 0000000000..aa75ba0e8a
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/lint/lint.js
@@ -0,0 +1,252 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+  var GUTTER_ID = "CodeMirror-lint-markers";
+
+  function showTooltip(e, content) {
+    var tt = document.createElement("div");
+    tt.className = "CodeMirror-lint-tooltip";
+    tt.appendChild(content.cloneNode(true));
+    document.body.appendChild(tt);
+
+    function position(e) {
+      if (!tt.parentNode) return CodeMirror.off(document, "mousemove", position);
+      tt.style.top = Math.max(0, e.clientY - tt.offsetHeight - 5) + "px";
+      tt.style.left = (e.clientX + 5) + "px";
+    }
+    CodeMirror.on(document, "mousemove", position);
+    position(e);
+    if (tt.style.opacity != null) tt.style.opacity = 1;
+    return tt;
+  }
+  function rm(elt) {
+    if (elt.parentNode) elt.parentNode.removeChild(elt);
+  }
+  function hideTooltip(tt) {
+    if (!tt.parentNode) return;
+    if (tt.style.opacity == null) rm(tt);
+    tt.style.opacity = 0;
+    setTimeout(function() { rm(tt); }, 600);
+  }
+
+  function showTooltipFor(e, content, node) {
+    var tooltip = showTooltip(e, content);
+    function hide() {
+      CodeMirror.off(node, "mouseout", hide);
+      if (tooltip) { hideTooltip(tooltip); tooltip = null; }
+    }
+    var poll = setInterval(function() {
+      if (tooltip) for (var n = node;; n = n.parentNode) {
+        if (n && n.nodeType == 11) n = n.host;
+        if (n == document.body) return;
+        if (!n) { hide(); break; }
+      }
+      if (!tooltip) return clearInterval(poll);
+    }, 400);
+    CodeMirror.on(node, "mouseout", hide);
+  }
+
+  function LintState(cm, options, hasGutter) {
+    this.marked = [];
+    this.options = options;
+    this.timeout = null;
+    this.hasGutter = hasGutter;
+    this.onMouseOver = function(e) { onMouseOver(cm, e); };
+    this.waitingFor = 0
+  }
+
+  function parseOptions(_cm, options) {
+    if (options instanceof Function) return {getAnnotations: options};
+    if (!options || options === true) options = {};
+    return options;
+  }
+
+  function clearMarks(cm) {
+    var state = cm.state.lint;
+    if (state.hasGutter) cm.clearGutter(GUTTER_ID);
+    for (var i = 0; i < state.marked.length; ++i)
+      state.marked[i].clear();
+    state.marked.length = 0;
+  }
+
+  function makeMarker(labels, severity, multiple, tooltips) {
+    var marker = document.createElement("div"), inner = marker;
+    marker.className = "CodeMirror-lint-marker-" + severity;
+    if (multiple) {
+      inner = marker.appendChild(document.createElement("div"));
+      inner.className = "CodeMirror-lint-marker-multiple";
+    }
+
+    if (tooltips != false) CodeMirror.on(inner, "mouseover", function(e) {
+      showTooltipFor(e, labels, inner);
+    });
+
+    return marker;
+  }
+
+  function getMaxSeverity(a, b) {
+    if (a == "error") return a;
+    else return b;
+  }
+
+  function groupByLine(annotations) {
+    var lines = [];
+    for (var i = 0; i < annotations.length; ++i) {
+      var ann = annotations[i], line = ann.from.line;
+      (lines[line] || (lines[line] = [])).push(ann);
+    }
+    return lines;
+  }
+
+  function annotationTooltip(ann) {
+    var severity = ann.severity;
+    if (!severity) severity = "error";
+    var tip = document.createElement("div");
+    tip.className = "CodeMirror-lint-message-" + severity;
+    if (typeof ann.messageHTML != 'undefined') {
+        tip.innerHTML = ann.messageHTML;
+    } else {
+        tip.appendChild(document.createTextNode(ann.message));
+    }
+    return tip;
+  }
+
+  function lintAsync(cm, getAnnotations, passOptions) {
+    var state = cm.state.lint
+    var id = ++state.waitingFor
+    function abort() {
+      id = -1
+      cm.off("change", abort)
+    }
+    cm.on("change", abort)
+    getAnnotations(cm.getValue(), function(annotations, arg2) {
+      cm.off("change", abort)
+      if (state.waitingFor != id) return
+      if (arg2 && annotations instanceof CodeMirror) annotations = arg2
+      cm.operation(function() {updateLinting(cm, annotations)})
+    }, passOptions, cm);
+  }
+
+  function startLinting(cm) {
+    var state = cm.state.lint, options = state.options;
+    /*
+     * Passing rules in `options` property prevents JSHint (and other linters) from complaining
+     * about unrecognized rules like `onUpdateLinting`, `delay`, `lintOnChange`, etc.
+     */
+    var passOptions = options.options || options;
+    var getAnnotations = options.getAnnotations || cm.getHelper(CodeMirror.Pos(0, 0), "lint");
+    if (!getAnnotations) return;
+    if (options.async || getAnnotations.async) {
+      lintAsync(cm, getAnnotations, passOptions)
+    } else {
+      var annotations = getAnnotations(cm.getValue(), passOptions, cm);
+      if (!annotations) return;
+      if (annotations.then) annotations.then(function(issues) {
+        cm.operation(function() {updateLinting(cm, issues)})
+      });
+      else cm.operation(function() {updateLinting(cm, annotations)})
+    }
+  }
+
+  function updateLinting(cm, annotationsNotSorted) {
+    clearMarks(cm);
+    var state = cm.state.lint, options = state.options;
+
+    var annotations = groupByLine(annotationsNotSorted);
+
+    for (var line = 0; line < annotations.length; ++line) {
+      var anns = annotations[line];
+      if (!anns) continue;
+
+      var maxSeverity = null;
+      var tipLabel = state.hasGutter && document.createDocumentFragment();
+
+      for (var i = 0; i < anns.length; ++i) {
+        var ann = anns[i];
+        var severity = ann.severity;
+        if (!severity) severity = "error";
+        maxSeverity = getMaxSeverity(maxSeverity, severity);
+
+        if (options.formatAnnotation) ann = options.formatAnnotation(ann);
+        if (state.hasGutter) tipLabel.appendChild(annotationTooltip(ann));
+
+        if (ann.to) state.marked.push(cm.markText(ann.from, ann.to, {
+          className: "CodeMirror-lint-mark-" + severity,
+          __annotation: ann
+        }));
+      }
+
+      if (state.hasGutter)
+        cm.setGutterMarker(line, GUTTER_ID, makeMarker(tipLabel, maxSeverity, anns.length > 1,
+                                                       state.options.tooltips));
+    }
+    if (options.onUpdateLinting) options.onUpdateLinting(annotationsNotSorted, annotations, cm);
+  }
+
+  function onChange(cm) {
+    var state = cm.state.lint;
+    if (!state) return;
+    clearTimeout(state.timeout);
+    state.timeout = setTimeout(function(){startLinting(cm);}, state.options.delay || 500);
+  }
+
+  function popupTooltips(annotations, e) {
+    var target = e.target || e.srcElement;
+    var tooltip = document.createDocumentFragment();
+    for (var i = 0; i < annotations.length; i++) {
+      var ann = annotations[i];
+      tooltip.appendChild(annotationTooltip(ann));
+    }
+    showTooltipFor(e, tooltip, target);
+  }
+
+  function onMouseOver(cm, e) {
+    var target = e.target || e.srcElement;
+    if (!/\bCodeMirror-lint-mark-/.test(target.className)) return;
+    var box = target.getBoundingClientRect(), x = (box.left + box.right) / 2, y = (box.top + box.bottom) / 2;
+    var spans = cm.findMarksAt(cm.coordsChar({left: x, top: y}, "client"));
+
+    var annotations = [];
+    for (var i = 0; i < spans.length; ++i) {
+      var ann = spans[i].__annotation;
+      if (ann) annotations.push(ann);
+    }
+    if (annotations.length) popupTooltips(annotations, e);
+  }
+
+  CodeMirror.defineOption("lint", false, function(cm, val, old) {
+    if (old && old != CodeMirror.Init) {
+      clearMarks(cm);
+      if (cm.state.lint.options.lintOnChange !== false)
+        cm.off("change", onChange);
+      CodeMirror.off(cm.getWrapperElement(), "mouseover", cm.state.lint.onMouseOver);
+      clearTimeout(cm.state.lint.timeout);
+      delete cm.state.lint;
+    }
+
+    if (val) {
+      var gutters = cm.getOption("gutters"), hasLintGutter = false;
+      for (var i = 0; i < gutters.length; ++i) if (gutters[i] == GUTTER_ID) hasLintGutter = true;
+      var state = cm.state.lint = new LintState(cm, parseOptions(cm, val), hasLintGutter);
+      if (state.options.lintOnChange !== false)
+        cm.on("change", onChange);
+      if (state.options.tooltips != false && state.options.tooltips != "gutter")
+        CodeMirror.on(cm.getWrapperElement(), "mouseover", state.onMouseOver);
+
+      startLinting(cm);
+    }
+  });
+
+  CodeMirror.defineExtension("performLint", function() {
+    if (this.state.lint) startLinting(this);
+  });
+});
diff --git a/public/vendor/plugins/codemirror/addon/lint/yaml-lint.js b/public/vendor/plugins/codemirror/addon/lint/yaml-lint.js
new file mode 100644
index 0000000000..b4ac5abc4e
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/lint/yaml-lint.js
@@ -0,0 +1,41 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+// Depends on js-yaml.js from https://github.com/nodeca/js-yaml
+
+// declare global: jsyaml
+
+CodeMirror.registerHelper("lint", "yaml", function(text) {
+  var found = [];
+  if (!window.jsyaml) {
+    if (window.console) {
+      window.console.error("Error: window.jsyaml not defined, CodeMirror YAML linting cannot run.");
+    }
+    return found;
+  }
+  try { jsyaml.loadAll(text); }
+  catch(e) {
+      var loc = e.mark,
+          // js-yaml YAMLException doesn't always provide an accurate lineno
+          // e.g., when there are multiple yaml docs
+          // ---
+          // ---
+          // foo:bar
+          from = loc ? CodeMirror.Pos(loc.line, loc.column) : CodeMirror.Pos(0, 0),
+          to = from;
+      found.push({ from: from, to: to, message: e.message });
+  }
+  return found;
+});
+
+});
diff --git a/public/vendor/plugins/codemirror/addon/merge/merge.css b/public/vendor/plugins/codemirror/addon/merge/merge.css
new file mode 100644
index 0000000000..dadd7f59c7
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/merge/merge.css
@@ -0,0 +1,119 @@
+.CodeMirror-merge {
+  position: relative;
+  border: 1px solid #ddd;
+  white-space: pre;
+}
+
+.CodeMirror-merge, .CodeMirror-merge .CodeMirror {
+  height: 350px;
+}
+
+.CodeMirror-merge-2pane .CodeMirror-merge-pane { width: 47%; }
+.CodeMirror-merge-2pane .CodeMirror-merge-gap { width: 6%; }
+.CodeMirror-merge-3pane .CodeMirror-merge-pane { width: 31%; }
+.CodeMirror-merge-3pane .CodeMirror-merge-gap { width: 3.5%; }
+
+.CodeMirror-merge-pane {
+  display: inline-block;
+  white-space: normal;
+  vertical-align: top;
+}
+.CodeMirror-merge-pane-rightmost {
+  position: absolute;
+  right: 0px;
+  z-index: 1;
+}
+
+.CodeMirror-merge-gap {
+  z-index: 2;
+  display: inline-block;
+  height: 100%;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+  overflow: hidden;
+  border-left: 1px solid #ddd;
+  border-right: 1px solid #ddd;
+  position: relative;
+  background: #f8f8f8;
+}
+
+.CodeMirror-merge-scrolllock-wrap {
+  position: absolute;
+  bottom: 0; left: 50%;
+}
+.CodeMirror-merge-scrolllock {
+  position: relative;
+  left: -50%;
+  cursor: pointer;
+  color: #555;
+  line-height: 1;
+}
+.CodeMirror-merge-scrolllock:after {
+  content: "\21db\00a0\00a0\21da";
+}
+.CodeMirror-merge-scrolllock.CodeMirror-merge-scrolllock-enabled:after {
+  content: "\21db\21da";
+}
+
+.CodeMirror-merge-copybuttons-left, .CodeMirror-merge-copybuttons-right {
+  position: absolute;
+  left: 0; top: 0;
+  right: 0; bottom: 0;
+  line-height: 1;
+}
+
+.CodeMirror-merge-copy {
+  position: absolute;
+  cursor: pointer;
+  color: #44c;
+  z-index: 3;
+}
+
+.CodeMirror-merge-copy-reverse {
+  position: absolute;
+  cursor: pointer;
+  color: #44c;
+}
+
+.CodeMirror-merge-copybuttons-left .CodeMirror-merge-copy { left: 2px; }
+.CodeMirror-merge-copybuttons-right .CodeMirror-merge-copy { right: 2px; }
+
+.CodeMirror-merge-r-inserted, .CodeMirror-merge-l-inserted {
+  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12MwuCXy3+CWyH8GBgYGJgYkAABZbAQ9ELXurwAAAABJRU5ErkJggg==);
+  background-position: bottom left;
+  background-repeat: repeat-x;
+}
+
+.CodeMirror-merge-r-deleted, .CodeMirror-merge-l-deleted {
+  background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAACCAYAAACddGYaAAAAGUlEQVQI12M4Kyb2/6yY2H8GBgYGJgYkAABURgPz6Ks7wQAAAABJRU5ErkJggg==);
+  background-position: bottom left;
+  background-repeat: repeat-x;
+}
+
+.CodeMirror-merge-r-chunk { background: #ffffe0; }
+.CodeMirror-merge-r-chunk-start { border-top: 1px solid #ee8; }
+.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #ee8; }
+.CodeMirror-merge-r-connect { fill: #ffffe0; stroke: #ee8; stroke-width: 1px; }
+
+.CodeMirror-merge-l-chunk { background: #eef; }
+.CodeMirror-merge-l-chunk-start { border-top: 1px solid #88e; }
+.CodeMirror-merge-l-chunk-end { border-bottom: 1px solid #88e; }
+.CodeMirror-merge-l-connect { fill: #eef; stroke: #88e; stroke-width: 1px; }
+
+.CodeMirror-merge-l-chunk.CodeMirror-merge-r-chunk { background: #dfd; }
+.CodeMirror-merge-l-chunk-start.CodeMirror-merge-r-chunk-start { border-top: 1px solid #4e4; }
+.CodeMirror-merge-l-chunk-end.CodeMirror-merge-r-chunk-end { border-bottom: 1px solid #4e4; }
+
+.CodeMirror-merge-collapsed-widget:before {
+  content: "(...)";
+}
+.CodeMirror-merge-collapsed-widget {
+  cursor: pointer;
+  color: #88b;
+  background: #eef;
+  border: 1px solid #ddf;
+  font-size: 90%;
+  padding: 0 3px;
+  border-radius: 4px;
+}
+.CodeMirror-merge-collapsed-line .CodeMirror-gutter-elt { display: none; }
diff --git a/public/vendor/plugins/codemirror/addon/merge/merge.js b/public/vendor/plugins/codemirror/addon/merge/merge.js
new file mode 100644
index 0000000000..8296540a05
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/merge/merge.js
@@ -0,0 +1,1002 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// declare global: diff_match_patch, DIFF_INSERT, DIFF_DELETE, DIFF_EQUAL
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror")); // Note non-packaged dependency diff_match_patch
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror", "diff_match_patch"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+  var Pos = CodeMirror.Pos;
+  var svgNS = "http://www.w3.org/2000/svg";
+
+  function DiffView(mv, type) {
+    this.mv = mv;
+    this.type = type;
+    this.classes = type == "left"
+      ? {chunk: "CodeMirror-merge-l-chunk",
+         start: "CodeMirror-merge-l-chunk-start",
+         end: "CodeMirror-merge-l-chunk-end",
+         insert: "CodeMirror-merge-l-inserted",
+         del: "CodeMirror-merge-l-deleted",
+         connect: "CodeMirror-merge-l-connect"}
+      : {chunk: "CodeMirror-merge-r-chunk",
+         start: "CodeMirror-merge-r-chunk-start",
+         end: "CodeMirror-merge-r-chunk-end",
+         insert: "CodeMirror-merge-r-inserted",
+         del: "CodeMirror-merge-r-deleted",
+         connect: "CodeMirror-merge-r-connect"};
+  }
+
+  DiffView.prototype = {
+    constructor: DiffView,
+    init: function(pane, orig, options) {
+      this.edit = this.mv.edit;
+      ;(this.edit.state.diffViews || (this.edit.state.diffViews = [])).push(this);
+      this.orig = CodeMirror(pane, copyObj({value: orig, readOnly: !this.mv.options.allowEditingOriginals}, copyObj(options)));
+      if (this.mv.options.connect == "align") {
+        if (!this.edit.state.trackAlignable) this.edit.state.trackAlignable = new TrackAlignable(this.edit)
+        this.orig.state.trackAlignable = new TrackAlignable(this.orig)
+      }
+      this.lockButton.title = this.edit.phrase("Toggle locked scrolling");
+
+      this.orig.state.diffViews = [this];
+      var classLocation = options.chunkClassLocation || "background";
+      if (Object.prototype.toString.call(classLocation) != "[object Array]") classLocation = [classLocation]
+      this.classes.classLocation = classLocation
+
+      this.diff = getDiff(asString(orig), asString(options.value), this.mv.options.ignoreWhitespace);
+      this.chunks = getChunks(this.diff);
+      this.diffOutOfDate = this.dealigned = false;
+      this.needsScrollSync = null
+
+      this.showDifferences = options.showDifferences !== false;
+    },
+    registerEvents: function(otherDv) {
+      this.forceUpdate = registerUpdate(this);
+      setScrollLock(this, true, false);
+      registerScroll(this, otherDv);
+    },
+    setShowDifferences: function(val) {
+      val = val !== false;
+      if (val != this.showDifferences) {
+        this.showDifferences = val;
+        this.forceUpdate("full");
+      }
+    }
+  };
+
+  function ensureDiff(dv) {
+    if (dv.diffOutOfDate) {
+      dv.diff = getDiff(dv.orig.getValue(), dv.edit.getValue(), dv.mv.options.ignoreWhitespace);
+      dv.chunks = getChunks(dv.diff);
+      dv.diffOutOfDate = false;
+      CodeMirror.signal(dv.edit, "updateDiff", dv.diff);
+    }
+  }
+
+  var updating = false;
+  function registerUpdate(dv) {
+    var edit = {from: 0, to: 0, marked: []};
+    var orig = {from: 0, to: 0, marked: []};
+    var debounceChange, updatingFast = false;
+    function update(mode) {
+      updating = true;
+      updatingFast = false;
+      if (mode == "full") {
+        if (dv.svg) clear(dv.svg);
+        if (dv.copyButtons) clear(dv.copyButtons);
+        clearMarks(dv.edit, edit.marked, dv.classes);
+        clearMarks(dv.orig, orig.marked, dv.classes);
+        edit.from = edit.to = orig.from = orig.to = 0;
+      }
+      ensureDiff(dv);
+      if (dv.showDifferences) {
+        updateMarks(dv.edit, dv.diff, edit, DIFF_INSERT, dv.classes);
+        updateMarks(dv.orig, dv.diff, orig, DIFF_DELETE, dv.classes);
+      }
+
+      if (dv.mv.options.connect == "align")
+        alignChunks(dv);
+      makeConnections(dv);
+      if (dv.needsScrollSync != null) syncScroll(dv, dv.needsScrollSync)
+
+      updating = false;
+    }
+    function setDealign(fast) {
+      if (updating) return;
+      dv.dealigned = true;
+      set(fast);
+    }
+    function set(fast) {
+      if (updating || updatingFast) return;
+      clearTimeout(debounceChange);
+      if (fast === true) updatingFast = true;
+      debounceChange = setTimeout(update, fast === true ? 20 : 250);
+    }
+    function change(_cm, change) {
+      if (!dv.diffOutOfDate) {
+        dv.diffOutOfDate = true;
+        edit.from = edit.to = orig.from = orig.to = 0;
+      }
+      // Update faster when a line was added/removed
+      setDealign(change.text.length - 1 != change.to.line - change.from.line);
+    }
+    function swapDoc() {
+      dv.diffOutOfDate = true;
+      dv.dealigned = true;
+      update("full");
+    }
+    dv.edit.on("change", change);
+    dv.orig.on("change", change);
+    dv.edit.on("swapDoc", swapDoc);
+    dv.orig.on("swapDoc", swapDoc);
+    if (dv.mv.options.connect == "align") {
+      CodeMirror.on(dv.edit.state.trackAlignable, "realign", setDealign)
+      CodeMirror.on(dv.orig.state.trackAlignable, "realign", setDealign)
+    }
+    dv.edit.on("viewportChange", function() { set(false); });
+    dv.orig.on("viewportChange", function() { set(false); });
+    update();
+    return update;
+  }
+
+  function registerScroll(dv, otherDv) {
+    dv.edit.on("scroll", function() {
+      syncScroll(dv, true) && makeConnections(dv);
+    });
+    dv.orig.on("scroll", function() {
+      syncScroll(dv, false) && makeConnections(dv);
+      if (otherDv) syncScroll(otherDv, true) && makeConnections(otherDv);
+    });
+  }
+
+  function syncScroll(dv, toOrig) {
+    // Change handler will do a refresh after a timeout when diff is out of date
+    if (dv.diffOutOfDate) {
+      if (dv.lockScroll && dv.needsScrollSync == null) dv.needsScrollSync = toOrig
+      return false
+    }
+    dv.needsScrollSync = null
+    if (!dv.lockScroll) return true;
+    var editor, other, now = +new Date;
+    if (toOrig) { editor = dv.edit; other = dv.orig; }
+    else { editor = dv.orig; other = dv.edit; }
+    // Don't take action if the position of this editor was recently set
+    // (to prevent feedback loops)
+    if (editor.state.scrollSetBy == dv && (editor.state.scrollSetAt || 0) + 250 > now) return false;
+
+    var sInfo = editor.getScrollInfo();
+    if (dv.mv.options.connect == "align") {
+      targetPos = sInfo.top;
+    } else {
+      var halfScreen = .5 * sInfo.clientHeight, midY = sInfo.top + halfScreen;
+      var mid = editor.lineAtHeight(midY, "local");
+      var around = chunkBoundariesAround(dv.chunks, mid, toOrig);
+      var off = getOffsets(editor, toOrig ? around.edit : around.orig);
+      var offOther = getOffsets(other, toOrig ? around.orig : around.edit);
+      var ratio = (midY - off.top) / (off.bot - off.top);
+      var targetPos = (offOther.top - halfScreen) + ratio * (offOther.bot - offOther.top);
+
+      var botDist, mix;
+      // Some careful tweaking to make sure no space is left out of view
+      // when scrolling to top or bottom.
+      if (targetPos > sInfo.top && (mix = sInfo.top / halfScreen) < 1) {
+        targetPos = targetPos * mix + sInfo.top * (1 - mix);
+      } else if ((botDist = sInfo.height - sInfo.clientHeight - sInfo.top) < halfScreen) {
+        var otherInfo = other.getScrollInfo();
+        var botDistOther = otherInfo.height - otherInfo.clientHeight - targetPos;
+        if (botDistOther > botDist && (mix = botDist / halfScreen) < 1)
+          targetPos = targetPos * mix + (otherInfo.height - otherInfo.clientHeight - botDist) * (1 - mix);
+      }
+    }
+
+    other.scrollTo(sInfo.left, targetPos);
+    other.state.scrollSetAt = now;
+    other.state.scrollSetBy = dv;
+    return true;
+  }
+
+  function getOffsets(editor, around) {
+    var bot = around.after;
+    if (bot == null) bot = editor.lastLine() + 1;
+    return {top: editor.heightAtLine(around.before || 0, "local"),
+            bot: editor.heightAtLine(bot, "local")};
+  }
+
+  function setScrollLock(dv, val, action) {
+    dv.lockScroll = val;
+    if (val && action != false) syncScroll(dv, DIFF_INSERT) && makeConnections(dv);
+    (val ? CodeMirror.addClass : CodeMirror.rmClass)(dv.lockButton, "CodeMirror-merge-scrolllock-enabled");
+  }
+
+  // Updating the marks for editor content
+
+  function removeClass(editor, line, classes) {
+    var locs = classes.classLocation
+    for (var i = 0; i < locs.length; i++) {
+      editor.removeLineClass(line, locs[i], classes.chunk);
+      editor.removeLineClass(line, locs[i], classes.start);
+      editor.removeLineClass(line, locs[i], classes.end);
+    }
+  }
+
+  function clearMarks(editor, arr, classes) {
+    for (var i = 0; i < arr.length; ++i) {
+      var mark = arr[i];
+      if (mark instanceof CodeMirror.TextMarker)
+        mark.clear();
+      else if (mark.parent)
+        removeClass(editor, mark, classes);
+    }
+    arr.length = 0;
+  }
+
+  // FIXME maybe add a margin around viewport to prevent too many updates
+  function updateMarks(editor, diff, state, type, classes) {
+    var vp = editor.getViewport();
+    editor.operation(function() {
+      if (state.from == state.to || vp.from - state.to > 20 || state.from - vp.to > 20) {
+        clearMarks(editor, state.marked, classes);
+        markChanges(editor, diff, type, state.marked, vp.from, vp.to, classes);
+        state.from = vp.from; state.to = vp.to;
+      } else {
+        if (vp.from < state.from) {
+          markChanges(editor, diff, type, state.marked, vp.from, state.from, classes);
+          state.from = vp.from;
+        }
+        if (vp.to > state.to) {
+          markChanges(editor, diff, type, state.marked, state.to, vp.to, classes);
+          state.to = vp.to;
+        }
+      }
+    });
+  }
+
+  function addClass(editor, lineNr, classes, main, start, end) {
+    var locs = classes.classLocation, line = editor.getLineHandle(lineNr);
+    for (var i = 0; i < locs.length; i++) {
+      if (main) editor.addLineClass(line, locs[i], classes.chunk);
+      if (start) editor.addLineClass(line, locs[i], classes.start);
+      if (end) editor.addLineClass(line, locs[i], classes.end);
+    }
+    return line;
+  }
+
+  function markChanges(editor, diff, type, marks, from, to, classes) {
+    var pos = Pos(0, 0);
+    var top = Pos(from, 0), bot = editor.clipPos(Pos(to - 1));
+    var cls = type == DIFF_DELETE ? classes.del : classes.insert;
+    function markChunk(start, end) {
+      var bfrom = Math.max(from, start), bto = Math.min(to, end);
+      for (var i = bfrom; i < bto; ++i)
+        marks.push(addClass(editor, i, classes, true, i == start, i == end - 1));
+      // When the chunk is empty, make sure a horizontal line shows up
+      if (start == end && bfrom == end && bto == end) {
+        if (bfrom)
+          marks.push(addClass(editor, bfrom - 1, classes, false, false, true));
+        else
+          marks.push(addClass(editor, bfrom, classes, false, true, false));
+      }
+    }
+
+    var chunkStart = 0, pending = false;
+    for (var i = 0; i < diff.length; ++i) {
+      var part = diff[i], tp = part[0], str = part[1];
+      if (tp == DIFF_EQUAL) {
+        var cleanFrom = pos.line + (startOfLineClean(diff, i) ? 0 : 1);
+        moveOver(pos, str);
+        var cleanTo = pos.line + (endOfLineClean(diff, i) ? 1 : 0);
+        if (cleanTo > cleanFrom) {
+          if (pending) { markChunk(chunkStart, cleanFrom); pending = false }
+          chunkStart = cleanTo;
+        }
+      } else {
+        pending = true
+        if (tp == type) {
+          var end = moveOver(pos, str, true);
+          var a = posMax(top, pos), b = posMin(bot, end);
+          if (!posEq(a, b))
+            marks.push(editor.markText(a, b, {className: cls}));
+          pos = end;
+        }
+      }
+    }
+    if (pending) markChunk(chunkStart, pos.line + 1);
+  }
+
+  // Updating the gap between editor and original
+
+  function makeConnections(dv) {
+    if (!dv.showDifferences) return;
+
+    if (dv.svg) {
+      clear(dv.svg);
+      var w = dv.gap.offsetWidth;
+      attrs(dv.svg, "width", w, "height", dv.gap.offsetHeight);
+    }
+    if (dv.copyButtons) clear(dv.copyButtons);
+
+    var vpEdit = dv.edit.getViewport(), vpOrig = dv.orig.getViewport();
+    var outerTop = dv.mv.wrap.getBoundingClientRect().top
+    var sTopEdit = outerTop - dv.edit.getScrollerElement().getBoundingClientRect().top + dv.edit.getScrollInfo().top
+    var sTopOrig = outerTop - dv.orig.getScrollerElement().getBoundingClientRect().top + dv.orig.getScrollInfo().top;
+    for (var i = 0; i < dv.chunks.length; i++) {
+      var ch = dv.chunks[i];
+      if (ch.editFrom <= vpEdit.to && ch.editTo >= vpEdit.from &&
+          ch.origFrom <= vpOrig.to && ch.origTo >= vpOrig.from)
+        drawConnectorsForChunk(dv, ch, sTopOrig, sTopEdit, w);
+    }
+  }
+
+  function getMatchingOrigLine(editLine, chunks) {
+    var editStart = 0, origStart = 0;
+    for (var i = 0; i < chunks.length; i++) {
+      var chunk = chunks[i];
+      if (chunk.editTo > editLine && chunk.editFrom <= editLine) return null;
+      if (chunk.editFrom > editLine) break;
+      editStart = chunk.editTo;
+      origStart = chunk.origTo;
+    }
+    return origStart + (editLine - editStart);
+  }
+
+  // Combines information about chunks and widgets/markers to return
+  // an array of lines, in a single editor, that probably need to be
+  // aligned with their counterparts in the editor next to it.
+  function alignableFor(cm, chunks, isOrig) {
+    var tracker = cm.state.trackAlignable
+    var start = cm.firstLine(), trackI = 0
+    var result = []
+    for (var i = 0;; i++) {
+      var chunk = chunks[i]
+      var chunkStart = !chunk ? 1e9 : isOrig ? chunk.origFrom : chunk.editFrom
+      for (; trackI < tracker.alignable.length; trackI += 2) {
+        var n = tracker.alignable[trackI] + 1
+        if (n <= start) continue
+        if (n <= chunkStart) result.push(n)
+        else break
+      }
+      if (!chunk) break
+      result.push(start = isOrig ? chunk.origTo : chunk.editTo)
+    }
+    return result
+  }
+
+  // Given information about alignable lines in two editors, fill in
+  // the result (an array of three-element arrays) to reflect the
+  // lines that need to be aligned with each other.
+  function mergeAlignable(result, origAlignable, chunks, setIndex) {
+    var rI = 0, origI = 0, chunkI = 0, diff = 0
+    outer: for (;; rI++) {
+      var nextR = result[rI], nextO = origAlignable[origI]
+      if (!nextR && nextO == null) break
+
+      var rLine = nextR ? nextR[0] : 1e9, oLine = nextO == null ? 1e9 : nextO
+      while (chunkI < chunks.length) {
+        var chunk = chunks[chunkI]
+        if (chunk.origFrom <= oLine && chunk.origTo > oLine) {
+          origI++
+          rI--
+          continue outer;
+        }
+        if (chunk.editTo > rLine) {
+          if (chunk.editFrom <= rLine) continue outer;
+          break
+        }
+        diff += (chunk.origTo - chunk.origFrom) - (chunk.editTo - chunk.editFrom)
+        chunkI++
+      }
+      if (rLine == oLine - diff) {
+        nextR[setIndex] = oLine
+        origI++
+      } else if (rLine < oLine - diff) {
+        nextR[setIndex] = rLine + diff
+      } else {
+        var record = [oLine - diff, null, null]
+        record[setIndex] = oLine
+        result.splice(rI, 0, record)
+        origI++
+      }
+    }
+  }
+
+  function findAlignedLines(dv, other) {
+    var alignable = alignableFor(dv.edit, dv.chunks, false), result = []
+    if (other) for (var i = 0, j = 0; i < other.chunks.length; i++) {
+      var n = other.chunks[i].editTo
+      while (j < alignable.length && alignable[j] < n) j++
+      if (j == alignable.length || alignable[j] != n) alignable.splice(j++, 0, n)
+    }
+    for (var i = 0; i < alignable.length; i++)
+      result.push([alignable[i], null, null])
+
+    mergeAlignable(result, alignableFor(dv.orig, dv.chunks, true), dv.chunks, 1)
+    if (other)
+      mergeAlignable(result, alignableFor(other.orig, other.chunks, true), other.chunks, 2)
+
+    return result
+  }
+
+  function alignChunks(dv, force) {
+    if (!dv.dealigned && !force) return;
+    if (!dv.orig.curOp) return dv.orig.operation(function() {
+      alignChunks(dv, force);
+    });
+
+    dv.dealigned = false;
+    var other = dv.mv.left == dv ? dv.mv.right : dv.mv.left;
+    if (other) {
+      ensureDiff(other);
+      other.dealigned = false;
+    }
+    var linesToAlign = findAlignedLines(dv, other);
+
+    // Clear old aligners
+    var aligners = dv.mv.aligners;
+    for (var i = 0; i < aligners.length; i++)
+      aligners[i].clear();
+    aligners.length = 0;
+
+    var cm = [dv.edit, dv.orig], scroll = [];
+    if (other) cm.push(other.orig);
+    for (var i = 0; i < cm.length; i++)
+      scroll.push(cm[i].getScrollInfo().top);
+
+    for (var ln = 0; ln < linesToAlign.length; ln++)
+      alignLines(cm, linesToAlign[ln], aligners);
+
+    for (var i = 0; i < cm.length; i++)
+      cm[i].scrollTo(null, scroll[i]);
+  }
+
+  function alignLines(cm, lines, aligners) {
+    var maxOffset = 0, offset = [];
+    for (var i = 0; i < cm.length; i++) if (lines[i] != null) {
+      var off = cm[i].heightAtLine(lines[i], "local");
+      offset[i] = off;
+      maxOffset = Math.max(maxOffset, off);
+    }
+    for (var i = 0; i < cm.length; i++) if (lines[i] != null) {
+      var diff = maxOffset - offset[i];
+      if (diff > 1)
+        aligners.push(padAbove(cm[i], lines[i], diff));
+    }
+  }
+
+  function padAbove(cm, line, size) {
+    var above = true;
+    if (line > cm.lastLine()) {
+      line--;
+      above = false;
+    }
+    var elt = document.createElement("div");
+    elt.className = "CodeMirror-merge-spacer";
+    elt.style.height = size + "px"; elt.style.minWidth = "1px";
+    return cm.addLineWidget(line, elt, {height: size, above: above, mergeSpacer: true, handleMouseEvents: true});
+  }
+
+  function drawConnectorsForChunk(dv, chunk, sTopOrig, sTopEdit, w) {
+    var flip = dv.type == "left";
+    var top = dv.orig.heightAtLine(chunk.origFrom, "local", true) - sTopOrig;
+    if (dv.svg) {
+      var topLpx = top;
+      var topRpx = dv.edit.heightAtLine(chunk.editFrom, "local", true) - sTopEdit;
+      if (flip) { var tmp = topLpx; topLpx = topRpx; topRpx = tmp; }
+      var botLpx = dv.orig.heightAtLine(chunk.origTo, "local", true) - sTopOrig;
+      var botRpx = dv.edit.heightAtLine(chunk.editTo, "local", true) - sTopEdit;
+      if (flip) { var tmp = botLpx; botLpx = botRpx; botRpx = tmp; }
+      var curveTop = " C " + w/2 + " " + topRpx + " " + w/2 + " " + topLpx + " " + (w + 2) + " " + topLpx;
+      var curveBot = " C " + w/2 + " " + botLpx + " " + w/2 + " " + botRpx + " -1 " + botRpx;
+      attrs(dv.svg.appendChild(document.createElementNS(svgNS, "path")),
+            "d", "M -1 " + topRpx + curveTop + " L " + (w + 2) + " " + botLpx + curveBot + " z",
+            "class", dv.classes.connect);
+    }
+    if (dv.copyButtons) {
+      var copy = dv.copyButtons.appendChild(elt("div", dv.type == "left" ? "\u21dd" : "\u21dc",
+                                                "CodeMirror-merge-copy"));
+      var editOriginals = dv.mv.options.allowEditingOriginals;
+      copy.title = dv.edit.phrase(editOriginals ? "Push to left" : "Revert chunk");
+      copy.chunk = chunk;
+      copy.style.top = (chunk.origTo > chunk.origFrom ? top : dv.edit.heightAtLine(chunk.editFrom, "local") - sTopEdit) + "px";
+
+      if (editOriginals) {
+        var topReverse = dv.edit.heightAtLine(chunk.editFrom, "local") - sTopEdit;
+        var copyReverse = dv.copyButtons.appendChild(elt("div", dv.type == "right" ? "\u21dd" : "\u21dc",
+                                                         "CodeMirror-merge-copy-reverse"));
+        copyReverse.title = "Push to right";
+        copyReverse.chunk = {editFrom: chunk.origFrom, editTo: chunk.origTo,
+                             origFrom: chunk.editFrom, origTo: chunk.editTo};
+        copyReverse.style.top = topReverse + "px";
+        dv.type == "right" ? copyReverse.style.left = "2px" : copyReverse.style.right = "2px";
+      }
+    }
+  }
+
+  function copyChunk(dv, to, from, chunk) {
+    if (dv.diffOutOfDate) return;
+    var origStart = chunk.origTo > from.lastLine() ? Pos(chunk.origFrom - 1) : Pos(chunk.origFrom, 0)
+    var origEnd = Pos(chunk.origTo, 0)
+    var editStart = chunk.editTo > to.lastLine() ? Pos(chunk.editFrom - 1) : Pos(chunk.editFrom, 0)
+    var editEnd = Pos(chunk.editTo, 0)
+    var handler = dv.mv.options.revertChunk
+    if (handler)
+      handler(dv.mv, from, origStart, origEnd, to, editStart, editEnd)
+    else
+      to.replaceRange(from.getRange(origStart, origEnd), editStart, editEnd)
+  }
+
+  // Merge view, containing 0, 1, or 2 diff views.
+
+  var MergeView = CodeMirror.MergeView = function(node, options) {
+    if (!(this instanceof MergeView)) return new MergeView(node, options);
+
+    this.options = options;
+    var origLeft = options.origLeft, origRight = options.origRight == null ? options.orig : options.origRight;
+
+    var hasLeft = origLeft != null, hasRight = origRight != null;
+    var panes = 1 + (hasLeft ? 1 : 0) + (hasRight ? 1 : 0);
+    var wrap = [], left = this.left = null, right = this.right = null;
+    var self = this;
+
+    if (hasLeft) {
+      left = this.left = new DiffView(this, "left");
+      var leftPane = elt("div", null, "CodeMirror-merge-pane CodeMirror-merge-left");
+      wrap.push(leftPane);
+      wrap.push(buildGap(left));
+    }
+
+    var editPane = elt("div", null, "CodeMirror-merge-pane CodeMirror-merge-editor");
+    wrap.push(editPane);
+
+    if (hasRight) {
+      right = this.right = new DiffView(this, "right");
+      wrap.push(buildGap(right));
+      var rightPane = elt("div", null, "CodeMirror-merge-pane CodeMirror-merge-right");
+      wrap.push(rightPane);
+    }
+
+    (hasRight ? rightPane : editPane).className += " CodeMirror-merge-pane-rightmost";
+
+    wrap.push(elt("div", null, null, "height: 0; clear: both;"));
+
+    var wrapElt = this.wrap = node.appendChild(elt("div", wrap, "CodeMirror-merge CodeMirror-merge-" + panes + "pane"));
+    this.edit = CodeMirror(editPane, copyObj(options));
+
+    if (left) left.init(leftPane, origLeft, options);
+    if (right) right.init(rightPane, origRight, options);
+    if (options.collapseIdentical)
+      this.editor().operation(function() {
+        collapseIdenticalStretches(self, options.collapseIdentical);
+      });
+    if (options.connect == "align") {
+      this.aligners = [];
+      alignChunks(this.left || this.right, true);
+    }
+    if (left) left.registerEvents(right)
+    if (right) right.registerEvents(left)
+
+
+    var onResize = function() {
+      if (left) makeConnections(left);
+      if (right) makeConnections(right);
+    };
+    CodeMirror.on(window, "resize", onResize);
+    var resizeInterval = setInterval(function() {
+      for (var p = wrapElt.parentNode; p && p != document.body; p = p.parentNode) {}
+      if (!p) { clearInterval(resizeInterval); CodeMirror.off(window, "resize", onResize); }
+    }, 5000);
+  };
+
+  function buildGap(dv) {
+    var lock = dv.lockButton = elt("div", null, "CodeMirror-merge-scrolllock");
+    var lockWrap = elt("div", [lock], "CodeMirror-merge-scrolllock-wrap");
+    CodeMirror.on(lock, "click", function() { setScrollLock(dv, !dv.lockScroll); });
+    var gapElts = [lockWrap];
+    if (dv.mv.options.revertButtons !== false) {
+      dv.copyButtons = elt("div", null, "CodeMirror-merge-copybuttons-" + dv.type);
+      CodeMirror.on(dv.copyButtons, "click", function(e) {
+        var node = e.target || e.srcElement;
+        if (!node.chunk) return;
+        if (node.className == "CodeMirror-merge-copy-reverse") {
+          copyChunk(dv, dv.orig, dv.edit, node.chunk);
+          return;
+        }
+        copyChunk(dv, dv.edit, dv.orig, node.chunk);
+      });
+      gapElts.unshift(dv.copyButtons);
+    }
+    if (dv.mv.options.connect != "align") {
+      var svg = document.createElementNS && document.createElementNS(svgNS, "svg");
+      if (svg && !svg.createSVGRect) svg = null;
+      dv.svg = svg;
+      if (svg) gapElts.push(svg);
+    }
+
+    return dv.gap = elt("div", gapElts, "CodeMirror-merge-gap");
+  }
+
+  MergeView.prototype = {
+    constructor: MergeView,
+    editor: function() { return this.edit; },
+    rightOriginal: function() { return this.right && this.right.orig; },
+    leftOriginal: function() { return this.left && this.left.orig; },
+    setShowDifferences: function(val) {
+      if (this.right) this.right.setShowDifferences(val);
+      if (this.left) this.left.setShowDifferences(val);
+    },
+    rightChunks: function() {
+      if (this.right) { ensureDiff(this.right); return this.right.chunks; }
+    },
+    leftChunks: function() {
+      if (this.left) { ensureDiff(this.left); return this.left.chunks; }
+    }
+  };
+
+  function asString(obj) {
+    if (typeof obj == "string") return obj;
+    else return obj.getValue();
+  }
+
+  // Operations on diffs
+  var dmp;
+  function getDiff(a, b, ignoreWhitespace) {
+    if (!dmp) dmp = new diff_match_patch();
+
+    var diff = dmp.diff_main(a, b);
+    // The library sometimes leaves in empty parts, which confuse the algorithm
+    for (var i = 0; i < diff.length; ++i) {
+      var part = diff[i];
+      if (ignoreWhitespace ? !/[^ \t]/.test(part[1]) : !part[1]) {
+        diff.splice(i--, 1);
+      } else if (i && diff[i - 1][0] == part[0]) {
+        diff.splice(i--, 1);
+        diff[i][1] += part[1];
+      }
+    }
+    return diff;
+  }
+
+  function getChunks(diff) {
+    var chunks = [];
+    if (!diff.length) return chunks;
+    var startEdit = 0, startOrig = 0;
+    var edit = Pos(0, 0), orig = Pos(0, 0);
+    for (var i = 0; i < diff.length; ++i) {
+      var part = diff[i], tp = part[0];
+      if (tp == DIFF_EQUAL) {
+        var startOff = !startOfLineClean(diff, i) || edit.line < startEdit || orig.line < startOrig ? 1 : 0;
+        var cleanFromEdit = edit.line + startOff, cleanFromOrig = orig.line + startOff;
+        moveOver(edit, part[1], null, orig);
+        var endOff = endOfLineClean(diff, i) ? 1 : 0;
+        var cleanToEdit = edit.line + endOff, cleanToOrig = orig.line + endOff;
+        if (cleanToEdit > cleanFromEdit) {
+          if (i) chunks.push({origFrom: startOrig, origTo: cleanFromOrig,
+                              editFrom: startEdit, editTo: cleanFromEdit});
+          startEdit = cleanToEdit; startOrig = cleanToOrig;
+        }
+      } else {
+        moveOver(tp == DIFF_INSERT ? edit : orig, part[1]);
+      }
+    }
+    if (startEdit <= edit.line || startOrig <= orig.line)
+      chunks.push({origFrom: startOrig, origTo: orig.line + 1,
+                   editFrom: startEdit, editTo: edit.line + 1});
+    return chunks;
+  }
+
+  function endOfLineClean(diff, i) {
+    if (i == diff.length - 1) return true;
+    var next = diff[i + 1][1];
+    if ((next.length == 1 && i < diff.length - 2) || next.charCodeAt(0) != 10) return false;
+    if (i == diff.length - 2) return true;
+    next = diff[i + 2][1];
+    return (next.length > 1 || i == diff.length - 3) && next.charCodeAt(0) == 10;
+  }
+
+  function startOfLineClean(diff, i) {
+    if (i == 0) return true;
+    var last = diff[i - 1][1];
+    if (last.charCodeAt(last.length - 1) != 10) return false;
+    if (i == 1) return true;
+    last = diff[i - 2][1];
+    return last.charCodeAt(last.length - 1) == 10;
+  }
+
+  function chunkBoundariesAround(chunks, n, nInEdit) {
+    var beforeE, afterE, beforeO, afterO;
+    for (var i = 0; i < chunks.length; i++) {
+      var chunk = chunks[i];
+      var fromLocal = nInEdit ? chunk.editFrom : chunk.origFrom;
+      var toLocal = nInEdit ? chunk.editTo : chunk.origTo;
+      if (afterE == null) {
+        if (fromLocal > n) { afterE = chunk.editFrom; afterO = chunk.origFrom; }
+        else if (toLocal > n) { afterE = chunk.editTo; afterO = chunk.origTo; }
+      }
+      if (toLocal <= n) { beforeE = chunk.editTo; beforeO = chunk.origTo; }
+      else if (fromLocal <= n) { beforeE = chunk.editFrom; beforeO = chunk.origFrom; }
+    }
+    return {edit: {before: beforeE, after: afterE}, orig: {before: beforeO, after: afterO}};
+  }
+
+  function collapseSingle(cm, from, to) {
+    cm.addLineClass(from, "wrap", "CodeMirror-merge-collapsed-line");
+    var widget = document.createElement("span");
+    widget.className = "CodeMirror-merge-collapsed-widget";
+    widget.title = cm.phrase("Identical text collapsed. Click to expand.");
+    var mark = cm.markText(Pos(from, 0), Pos(to - 1), {
+      inclusiveLeft: true,
+      inclusiveRight: true,
+      replacedWith: widget,
+      clearOnEnter: true
+    });
+    function clear() {
+      mark.clear();
+      cm.removeLineClass(from, "wrap", "CodeMirror-merge-collapsed-line");
+    }
+    if (mark.explicitlyCleared) clear();
+    CodeMirror.on(widget, "click", clear);
+    mark.on("clear", clear);
+    CodeMirror.on(widget, "click", clear);
+    return {mark: mark, clear: clear};
+  }
+
+  function collapseStretch(size, editors) {
+    var marks = [];
+    function clear() {
+      for (var i = 0; i < marks.length; i++) marks[i].clear();
+    }
+    for (var i = 0; i < editors.length; i++) {
+      var editor = editors[i];
+      var mark = collapseSingle(editor.cm, editor.line, editor.line + size);
+      marks.push(mark);
+      mark.mark.on("clear", clear);
+    }
+    return marks[0].mark;
+  }
+
+  function unclearNearChunks(dv, margin, off, clear) {
+    for (var i = 0; i < dv.chunks.length; i++) {
+      var chunk = dv.chunks[i];
+      for (var l = chunk.editFrom - margin; l < chunk.editTo + margin; l++) {
+        var pos = l + off;
+        if (pos >= 0 && pos < clear.length) clear[pos] = false;
+      }
+    }
+  }
+
+  function collapseIdenticalStretches(mv, margin) {
+    if (typeof margin != "number") margin = 2;
+    var clear = [], edit = mv.editor(), off = edit.firstLine();
+    for (var l = off, e = edit.lastLine(); l <= e; l++) clear.push(true);
+    if (mv.left) unclearNearChunks(mv.left, margin, off, clear);
+    if (mv.right) unclearNearChunks(mv.right, margin, off, clear);
+
+    for (var i = 0; i < clear.length; i++) {
+      if (clear[i]) {
+        var line = i + off;
+        for (var size = 1; i < clear.length - 1 && clear[i + 1]; i++, size++) {}
+        if (size > margin) {
+          var editors = [{line: line, cm: edit}];
+          if (mv.left) editors.push({line: getMatchingOrigLine(line, mv.left.chunks), cm: mv.left.orig});
+          if (mv.right) editors.push({line: getMatchingOrigLine(line, mv.right.chunks), cm: mv.right.orig});
+          var mark = collapseStretch(size, editors);
+          if (mv.options.onCollapse) mv.options.onCollapse(mv, line, size, mark);
+        }
+      }
+    }
+  }
+
+  // General utilities
+
+  function elt(tag, content, className, style) {
+    var e = document.createElement(tag);
+    if (className) e.className = className;
+    if (style) e.style.cssText = style;
+    if (typeof content == "string") e.appendChild(document.createTextNode(content));
+    else if (content) for (var i = 0; i < content.length; ++i) e.appendChild(content[i]);
+    return e;
+  }
+
+  function clear(node) {
+    for (var count = node.childNodes.length; count > 0; --count)
+      node.removeChild(node.firstChild);
+  }
+
+  function attrs(elt) {
+    for (var i = 1; i < arguments.length; i += 2)
+      elt.setAttribute(arguments[i], arguments[i+1]);
+  }
+
+  function copyObj(obj, target) {
+    if (!target) target = {};
+    for (var prop in obj) if (obj.hasOwnProperty(prop)) target[prop] = obj[prop];
+    return target;
+  }
+
+  function moveOver(pos, str, copy, other) {
+    var out = copy ? Pos(pos.line, pos.ch) : pos, at = 0;
+    for (;;) {
+      var nl = str.indexOf("\n", at);
+      if (nl == -1) break;
+      ++out.line;
+      if (other) ++other.line;
+      at = nl + 1;
+    }
+    out.ch = (at ? 0 : out.ch) + (str.length - at);
+    if (other) other.ch = (at ? 0 : other.ch) + (str.length - at);
+    return out;
+  }
+
+  // Tracks collapsed markers and line widgets, in order to be able to
+  // accurately align the content of two editors.
+
+  var F_WIDGET = 1, F_WIDGET_BELOW = 2, F_MARKER = 4
+
+  function TrackAlignable(cm) {
+    this.cm = cm
+    this.alignable = []
+    this.height = cm.doc.height
+    var self = this
+    cm.on("markerAdded", function(_, marker) {
+      if (!marker.collapsed) return
+      var found = marker.find(1)
+      if (found != null) self.set(found.line, F_MARKER)
+    })
+    cm.on("markerCleared", function(_, marker, _min, max) {
+      if (max != null && marker.collapsed)
+        self.check(max, F_MARKER, self.hasMarker)
+    })
+    cm.on("markerChanged", this.signal.bind(this))
+    cm.on("lineWidgetAdded", function(_, widget, lineNo) {
+      if (widget.mergeSpacer) return
+      if (widget.above) self.set(lineNo - 1, F_WIDGET_BELOW)
+      else self.set(lineNo, F_WIDGET)
+    })
+    cm.on("lineWidgetCleared", function(_, widget, lineNo) {
+      if (widget.mergeSpacer) return
+      if (widget.above) self.check(lineNo - 1, F_WIDGET_BELOW, self.hasWidgetBelow)
+      else self.check(lineNo, F_WIDGET, self.hasWidget)
+    })
+    cm.on("lineWidgetChanged", this.signal.bind(this))
+    cm.on("change", function(_, change) {
+      var start = change.from.line, nBefore = change.to.line - change.from.line
+      var nAfter = change.text.length - 1, end = start + nAfter
+      if (nBefore || nAfter) self.map(start, nBefore, nAfter)
+      self.check(end, F_MARKER, self.hasMarker)
+      if (nBefore || nAfter) self.check(change.from.line, F_MARKER, self.hasMarker)
+    })
+    cm.on("viewportChange", function() {
+      if (self.cm.doc.height != self.height) self.signal()
+    })
+  }
+
+  TrackAlignable.prototype = {
+    signal: function() {
+      CodeMirror.signal(this, "realign")
+      this.height = this.cm.doc.height
+    },
+
+    set: function(n, flags) {
+      var pos = -1
+      for (; pos < this.alignable.length; pos += 2) {
+        var diff = this.alignable[pos] - n
+        if (diff == 0) {
+          if ((this.alignable[pos + 1] & flags) == flags) return
+          this.alignable[pos + 1] |= flags
+          this.signal()
+          return
+        }
+        if (diff > 0) break
+      }
+      this.signal()
+      this.alignable.splice(pos, 0, n, flags)
+    },
+
+    find: function(n) {
+      for (var i = 0; i < this.alignable.length; i += 2)
+        if (this.alignable[i] == n) return i
+      return -1
+    },
+
+    check: function(n, flag, pred) {
+      var found = this.find(n)
+      if (found == -1 || !(this.alignable[found + 1] & flag)) return
+      if (!pred.call(this, n)) {
+        this.signal()
+        var flags = this.alignable[found + 1] & ~flag
+        if (flags) this.alignable[found + 1] = flags
+        else this.alignable.splice(found, 2)
+      }
+    },
+
+    hasMarker: function(n) {
+      var handle = this.cm.getLineHandle(n)
+      if (handle.markedSpans) for (var i = 0; i < handle.markedSpans.length; i++)
+        if (handle.markedSpans[i].marker.collapsed && handle.markedSpans[i].to != null)
+          return true
+      return false
+    },
+
+    hasWidget: function(n) {
+      var handle = this.cm.getLineHandle(n)
+      if (handle.widgets) for (var i = 0; i < handle.widgets.length; i++)
+        if (!handle.widgets[i].above && !handle.widgets[i].mergeSpacer) return true
+      return false
+    },
+
+    hasWidgetBelow: function(n) {
+      if (n == this.cm.lastLine()) return false
+      var handle = this.cm.getLineHandle(n + 1)
+      if (handle.widgets) for (var i = 0; i < handle.widgets.length; i++)
+        if (handle.widgets[i].above && !handle.widgets[i].mergeSpacer) return true
+      return false
+    },
+
+    map: function(from, nBefore, nAfter) {
+      var diff = nAfter - nBefore, to = from + nBefore, widgetFrom = -1, widgetTo = -1
+      for (var i = 0; i < this.alignable.length; i += 2) {
+        var n = this.alignable[i]
+        if (n == from && (this.alignable[i + 1] & F_WIDGET_BELOW)) widgetFrom = i
+        if (n == to && (this.alignable[i + 1] & F_WIDGET_BELOW)) widgetTo = i
+        if (n <= from) continue
+        else if (n < to) this.alignable.splice(i--, 2)
+        else this.alignable[i] += diff
+      }
+      if (widgetFrom > -1) {
+        var flags = this.alignable[widgetFrom + 1]
+        if (flags == F_WIDGET_BELOW) this.alignable.splice(widgetFrom, 2)
+        else this.alignable[widgetFrom + 1] = flags & ~F_WIDGET_BELOW
+      }
+      if (widgetTo > -1 && nAfter)
+        this.set(from + nAfter, F_WIDGET_BELOW)
+    }
+  }
+
+  function posMin(a, b) { return (a.line - b.line || a.ch - b.ch) < 0 ? a : b; }
+  function posMax(a, b) { return (a.line - b.line || a.ch - b.ch) > 0 ? a : b; }
+  function posEq(a, b) { return a.line == b.line && a.ch == b.ch; }
+
+  function findPrevDiff(chunks, start, isOrig) {
+    for (var i = chunks.length - 1; i >= 0; i--) {
+      var chunk = chunks[i];
+      var to = (isOrig ? chunk.origTo : chunk.editTo) - 1;
+      if (to < start) return to;
+    }
+  }
+
+  function findNextDiff(chunks, start, isOrig) {
+    for (var i = 0; i < chunks.length; i++) {
+      var chunk = chunks[i];
+      var from = (isOrig ? chunk.origFrom : chunk.editFrom);
+      if (from > start) return from;
+    }
+  }
+
+  function goNearbyDiff(cm, dir) {
+    var found = null, views = cm.state.diffViews, line = cm.getCursor().line;
+    if (views) for (var i = 0; i < views.length; i++) {
+      var dv = views[i], isOrig = cm == dv.orig;
+      ensureDiff(dv);
+      var pos = dir < 0 ? findPrevDiff(dv.chunks, line, isOrig) : findNextDiff(dv.chunks, line, isOrig);
+      if (pos != null && (found == null || (dir < 0 ? pos > found : pos < found)))
+        found = pos;
+    }
+    if (found != null)
+      cm.setCursor(found, 0);
+    else
+      return CodeMirror.Pass;
+  }
+
+  CodeMirror.commands.goNextDiff = function(cm) {
+    return goNearbyDiff(cm, 1);
+  };
+  CodeMirror.commands.goPrevDiff = function(cm) {
+    return goNearbyDiff(cm, -1);
+  };
+});
diff --git a/public/vendor/plugins/codemirror/addon/mode/loadmode.js b/public/vendor/plugins/codemirror/addon/mode/loadmode.js
index 10117ec22f..4ce716a012 100644
--- a/public/vendor/plugins/codemirror/addon/mode/loadmode.js
+++ b/public/vendor/plugins/codemirror/addon/mode/loadmode.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/addon/mode/multiplex.js b/public/vendor/plugins/codemirror/addon/mode/multiplex.js
index 3d8b34c452..93fd9a5a46 100644
--- a/public/vendor/plugins/codemirror/addon/mode/multiplex.js
+++ b/public/vendor/plugins/codemirror/addon/mode/multiplex.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -50,7 +50,15 @@ CodeMirror.multiplexingMode = function(outer /*, others */) {
           if (found == stream.pos) {
             if (!other.parseDelimiters) stream.match(other.open);
             state.innerActive = other;
-            state.inner = CodeMirror.startState(other.mode, outer.indent ? outer.indent(state.outer, "") : 0);
+
+            // Get the outer indent, making sure to handle CodeMirror.Pass
+            var outerIndent = 0;
+            if (outer.indent) {
+              var possibleOuterIndent = outer.indent(state.outer, "", "");
+              if (possibleOuterIndent !== CodeMirror.Pass) outerIndent = possibleOuterIndent;
+            }
+
+            state.inner = CodeMirror.startState(other.mode, outerIndent);
             return other.delimStyle && (other.delimStyle + " " + other.delimStyle + "-open");
           } else if (found != -1 && found < cutOff) {
             cutOff = found;
@@ -88,10 +96,10 @@ CodeMirror.multiplexingMode = function(outer /*, others */) {
       }
     },
 
-    indent: function(state, textAfter) {
+    indent: function(state, textAfter, line) {
       var mode = state.innerActive ? state.innerActive.mode : outer;
       if (!mode.indent) return CodeMirror.Pass;
-      return mode.indent(state.innerActive ? state.inner : state.outer, textAfter);
+      return mode.indent(state.innerActive ? state.inner : state.outer, textAfter, line);
     },
 
     blankLine: function(state) {
@@ -104,7 +112,7 @@ CodeMirror.multiplexingMode = function(outer /*, others */) {
           var other = others[i];
           if (other.open === "\n") {
             state.innerActive = other;
-            state.inner = CodeMirror.startState(other.mode, mode.indent ? mode.indent(state.outer, "") : 0);
+            state.inner = CodeMirror.startState(other.mode, mode.indent ? mode.indent(state.outer, "", "") : 0);
           }
         }
       } else if (state.innerActive.close === "\n") {
diff --git a/public/vendor/plugins/codemirror/addon/mode/multiplex_test.js b/public/vendor/plugins/codemirror/addon/mode/multiplex_test.js
index 24e5e670de..c51cad45d5 100644
--- a/public/vendor/plugins/codemirror/addon/mode/multiplex_test.js
+++ b/public/vendor/plugins/codemirror/addon/mode/multiplex_test.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function() {
   CodeMirror.defineMode("markdown_with_stex", function(){
diff --git a/public/vendor/plugins/codemirror/addon/mode/overlay.js b/public/vendor/plugins/codemirror/addon/mode/overlay.js
index e1b9ed3753..016e3c28cc 100644
--- a/public/vendor/plugins/codemirror/addon/mode/overlay.js
+++ b/public/vendor/plugins/codemirror/addon/mode/overlay.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 // Utility function that allows modes to be combined. The mode given
 // as the base argument takes care of most of the normal mode
@@ -68,16 +68,21 @@ CodeMirror.overlayMode = function(base, overlay, combine) {
       else return state.overlayCur;
     },
 
-    indent: base.indent && function(state, textAfter) {
-      return base.indent(state.base, textAfter);
+    indent: base.indent && function(state, textAfter, line) {
+      return base.indent(state.base, textAfter, line);
     },
     electricChars: base.electricChars,
 
     innerMode: function(state) { return {state: state.base, mode: base}; },
 
     blankLine: function(state) {
-      if (base.blankLine) base.blankLine(state.base);
-      if (overlay.blankLine) overlay.blankLine(state.overlay);
+      var baseToken, overlayToken;
+      if (base.blankLine) baseToken = base.blankLine(state.base);
+      if (overlay.blankLine) overlayToken = overlay.blankLine(state.overlay);
+
+      return overlayToken == null ?
+        baseToken :
+        (combine && baseToken != null ? baseToken + " " + overlayToken : overlayToken);
     }
   };
 };
diff --git a/public/vendor/plugins/codemirror/addon/mode/simple.js b/public/vendor/plugins/codemirror/addon/mode/simple.js
index df663365e8..655f991475 100644
--- a/public/vendor/plugins/codemirror/addon/mode/simple.js
+++ b/public/vendor/plugins/codemirror/addon/mode/simple.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -77,6 +77,7 @@
 
   function asToken(val) {
     if (!val) return null;
+    if (val.apply) return val
     if (typeof val == "string") return val.replace(/\./g, " ");
     var result = [];
     for (var i = 0; i < val.length; i++)
@@ -133,17 +134,19 @@
             state.indent.push(stream.indentation() + config.indentUnit);
           if (rule.data.dedent)
             state.indent.pop();
-          if (matches.length > 2) {
+          var token = rule.token
+          if (token && token.apply) token = token(matches)
+          if (matches.length > 2 && rule.token && typeof rule.token != "string") {
             state.pending = [];
             for (var j = 2; j < matches.length; j++)
               if (matches[j])
                 state.pending.push({text: matches[j], token: rule.token[j - 1]});
             stream.backUp(matches[0].length - (matches[1] ? matches[1].length : 0));
-            return rule.token[0];
-          } else if (rule.token && rule.token.join) {
-            return rule.token[0];
+            return token[0];
+          } else if (token && token.join) {
+            return token[0];
           } else {
-            return rule.token;
+            return token;
           }
         }
       }
diff --git a/public/vendor/plugins/codemirror/addon/runmode/colorize.js b/public/vendor/plugins/codemirror/addon/runmode/colorize.js
new file mode 100644
index 0000000000..3be5411506
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/runmode/colorize.js
@@ -0,0 +1,40 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"), require("./runmode"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror", "./runmode"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  var isBlock = /^(p|li|div|h\\d|pre|blockquote|td)$/;
+
+  function textContent(node, out) {
+    if (node.nodeType == 3) return out.push(node.nodeValue);
+    for (var ch = node.firstChild; ch; ch = ch.nextSibling) {
+      textContent(ch, out);
+      if (isBlock.test(node.nodeType)) out.push("\n");
+    }
+  }
+
+  CodeMirror.colorize = function(collection, defaultMode) {
+    if (!collection) collection = document.body.getElementsByTagName("pre");
+
+    for (var i = 0; i < collection.length; ++i) {
+      var node = collection[i];
+      var mode = node.getAttribute("data-lang") || defaultMode;
+      if (!mode) continue;
+
+      var text = [];
+      textContent(node, text);
+      node.innerHTML = "";
+      CodeMirror.runMode(text.join(""), mode, node);
+
+      node.className += " cm-s-default";
+    }
+  };
+});
diff --git a/public/vendor/plugins/codemirror/addon/runmode/runmode-standalone.js b/public/vendor/plugins/codemirror/addon/runmode/runmode-standalone.js
new file mode 100644
index 0000000000..745eaf8440
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/runmode/runmode-standalone.js
@@ -0,0 +1,158 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+window.CodeMirror = {};
+
+(function() {
+"use strict";
+
+function splitLines(string){ return string.split(/\r?\n|\r/); };
+
+function StringStream(string) {
+  this.pos = this.start = 0;
+  this.string = string;
+  this.lineStart = 0;
+}
+StringStream.prototype = {
+  eol: function() {return this.pos >= this.string.length;},
+  sol: function() {return this.pos == 0;},
+  peek: function() {return this.string.charAt(this.pos) || null;},
+  next: function() {
+    if (this.pos < this.string.length)
+      return this.string.charAt(this.pos++);
+  },
+  eat: function(match) {
+    var ch = this.string.charAt(this.pos);
+    if (typeof match == "string") var ok = ch == match;
+    else var ok = ch && (match.test ? match.test(ch) : match(ch));
+    if (ok) {++this.pos; return ch;}
+  },
+  eatWhile: function(match) {
+    var start = this.pos;
+    while (this.eat(match)){}
+    return this.pos > start;
+  },
+  eatSpace: function() {
+    var start = this.pos;
+    while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
+    return this.pos > start;
+  },
+  skipToEnd: function() {this.pos = this.string.length;},
+  skipTo: function(ch) {
+    var found = this.string.indexOf(ch, this.pos);
+    if (found > -1) {this.pos = found; return true;}
+  },
+  backUp: function(n) {this.pos -= n;},
+  column: function() {return this.start - this.lineStart;},
+  indentation: function() {return 0;},
+  match: function(pattern, consume, caseInsensitive) {
+    if (typeof pattern == "string") {
+      var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
+      var substr = this.string.substr(this.pos, pattern.length);
+      if (cased(substr) == cased(pattern)) {
+        if (consume !== false) this.pos += pattern.length;
+        return true;
+      }
+    } else {
+      var match = this.string.slice(this.pos).match(pattern);
+      if (match && match.index > 0) return null;
+      if (match && consume !== false) this.pos += match[0].length;
+      return match;
+    }
+  },
+  current: function(){return this.string.slice(this.start, this.pos);},
+  hideFirstChars: function(n, inner) {
+    this.lineStart += n;
+    try { return inner(); }
+    finally { this.lineStart -= n; }
+  },
+  lookAhead: function() { return null }
+};
+CodeMirror.StringStream = StringStream;
+
+CodeMirror.startState = function (mode, a1, a2) {
+  return mode.startState ? mode.startState(a1, a2) : true;
+};
+
+var modes = CodeMirror.modes = {}, mimeModes = CodeMirror.mimeModes = {};
+CodeMirror.defineMode = function (name, mode) {
+  if (arguments.length > 2)
+    mode.dependencies = Array.prototype.slice.call(arguments, 2);
+  modes[name] = mode;
+};
+CodeMirror.defineMIME = function (mime, spec) { mimeModes[mime] = spec; };
+CodeMirror.resolveMode = function(spec) {
+  if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
+    spec = mimeModes[spec];
+  } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
+    spec = mimeModes[spec.name];
+  }
+  if (typeof spec == "string") return {name: spec};
+  else return spec || {name: "null"};
+};
+CodeMirror.getMode = function (options, spec) {
+  spec = CodeMirror.resolveMode(spec);
+  var mfactory = modes[spec.name];
+  if (!mfactory) throw new Error("Unknown mode: " + spec);
+  return mfactory(options, spec);
+};
+CodeMirror.registerHelper = CodeMirror.registerGlobalHelper = Math.min;
+CodeMirror.defineMode("null", function() {
+  return {token: function(stream) {stream.skipToEnd();}};
+});
+CodeMirror.defineMIME("text/plain", "null");
+
+CodeMirror.runMode = function (string, modespec, callback, options) {
+  var mode = CodeMirror.getMode({ indentUnit: 2 }, modespec);
+
+  if (callback.nodeType == 1) {
+    var tabSize = (options && options.tabSize) || 4;
+    var node = callback, col = 0;
+    node.innerHTML = "";
+    callback = function (text, style) {
+      if (text == "\n") {
+        node.appendChild(document.createElement("br"));
+        col = 0;
+        return;
+      }
+      var content = "";
+      // replace tabs
+      for (var pos = 0; ;) {
+        var idx = text.indexOf("\t", pos);
+        if (idx == -1) {
+          content += text.slice(pos);
+          col += text.length - pos;
+          break;
+        } else {
+          col += idx - pos;
+          content += text.slice(pos, idx);
+          var size = tabSize - col % tabSize;
+          col += size;
+          for (var i = 0; i < size; ++i) content += " ";
+          pos = idx + 1;
+        }
+      }
+
+      if (style) {
+        var sp = node.appendChild(document.createElement("span"));
+        sp.className = "cm-" + style.replace(/ +/g, " cm-");
+        sp.appendChild(document.createTextNode(content));
+      } else {
+        node.appendChild(document.createTextNode(content));
+      }
+    };
+  }
+
+  var lines = splitLines(string), state = (options && options.state) || CodeMirror.startState(mode);
+  for (var i = 0, e = lines.length; i < e; ++i) {
+    if (i) callback("\n");
+    var stream = new CodeMirror.StringStream(lines[i]);
+    if (!stream.string && mode.blankLine) mode.blankLine(state);
+    while (!stream.eol()) {
+      var style = mode.token(stream, state);
+      callback(stream.current(), style, i, stream.start, state);
+      stream.start = stream.pos;
+    }
+  }
+};
+})();
diff --git a/public/vendor/plugins/codemirror/addon/runmode/runmode.js b/public/vendor/plugins/codemirror/addon/runmode/runmode.js
new file mode 100644
index 0000000000..eb4cadf5b4
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/runmode/runmode.js
@@ -0,0 +1,72 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+"use strict";
+
+CodeMirror.runMode = function(string, modespec, callback, options) {
+  var mode = CodeMirror.getMode(CodeMirror.defaults, modespec);
+  var ie = /MSIE \d/.test(navigator.userAgent);
+  var ie_lt9 = ie && (document.documentMode == null || document.documentMode < 9);
+
+  if (callback.appendChild) {
+    var tabSize = (options && options.tabSize) || CodeMirror.defaults.tabSize;
+    var node = callback, col = 0;
+    node.innerHTML = "";
+    callback = function(text, style) {
+      if (text == "\n") {
+        // Emitting LF or CRLF on IE8 or earlier results in an incorrect display.
+        // Emitting a carriage return makes everything ok.
+        node.appendChild(document.createTextNode(ie_lt9 ? '\r' : text));
+        col = 0;
+        return;
+      }
+      var content = "";
+      // replace tabs
+      for (var pos = 0;;) {
+        var idx = text.indexOf("\t", pos);
+        if (idx == -1) {
+          content += text.slice(pos);
+          col += text.length - pos;
+          break;
+        } else {
+          col += idx - pos;
+          content += text.slice(pos, idx);
+          var size = tabSize - col % tabSize;
+          col += size;
+          for (var i = 0; i < size; ++i) content += " ";
+          pos = idx + 1;
+        }
+      }
+
+      if (style) {
+        var sp = node.appendChild(document.createElement("span"));
+        sp.className = "cm-" + style.replace(/ +/g, " cm-");
+        sp.appendChild(document.createTextNode(content));
+      } else {
+        node.appendChild(document.createTextNode(content));
+      }
+    };
+  }
+
+  var lines = CodeMirror.splitLines(string), state = (options && options.state) || CodeMirror.startState(mode);
+  for (var i = 0, e = lines.length; i < e; ++i) {
+    if (i) callback("\n");
+    var stream = new CodeMirror.StringStream(lines[i]);
+    if (!stream.string && mode.blankLine) mode.blankLine(state);
+    while (!stream.eol()) {
+      var style = mode.token(stream, state);
+      callback(stream.current(), style, i, stream.start, state);
+      stream.start = stream.pos;
+    }
+  }
+};
+
+});
diff --git a/public/vendor/plugins/codemirror/addon/runmode/runmode.node.js b/public/vendor/plugins/codemirror/addon/runmode/runmode.node.js
new file mode 100644
index 0000000000..53b6994c28
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/runmode/runmode.node.js
@@ -0,0 +1,197 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+/* Just enough of CodeMirror to run runMode under node.js */
+
+function splitLines(string){return string.split(/\r\n?|\n/);};
+
+// Counts the column offset in a string, taking tabs into account.
+// Used mostly to find indentation.
+var countColumn = exports.countColumn = function(string, end, tabSize, startIndex, startValue) {
+  if (end == null) {
+    end = string.search(/[^\s\u00a0]/);
+    if (end == -1) end = string.length;
+  }
+  for (var i = startIndex || 0, n = startValue || 0;;) {
+    var nextTab = string.indexOf("\t", i);
+    if (nextTab < 0 || nextTab >= end)
+      return n + (end - i);
+    n += nextTab - i;
+    n += tabSize - (n % tabSize);
+    i = nextTab + 1;
+  }
+};
+
+function StringStream(string, tabSize, context) {
+  this.pos = this.start = 0;
+  this.string = string;
+  this.tabSize = tabSize || 8;
+  this.lastColumnPos = this.lastColumnValue = 0;
+  this.lineStart = 0;
+  this.context = context
+};
+
+StringStream.prototype = {
+  eol: function() {return this.pos >= this.string.length;},
+  sol: function() {return this.pos == this.lineStart;},
+  peek: function() {return this.string.charAt(this.pos) || undefined;},
+  next: function() {
+    if (this.pos < this.string.length)
+      return this.string.charAt(this.pos++);
+  },
+  eat: function(match) {
+    var ch = this.string.charAt(this.pos);
+    if (typeof match == "string") var ok = ch == match;
+    else var ok = ch && (match.test ? match.test(ch) : match(ch));
+    if (ok) {++this.pos; return ch;}
+  },
+  eatWhile: function(match) {
+    var start = this.pos;
+    while (this.eat(match)){}
+    return this.pos > start;
+  },
+  eatSpace: function() {
+    var start = this.pos;
+    while (/[\s\u00a0]/.test(this.string.charAt(this.pos))) ++this.pos;
+    return this.pos > start;
+  },
+  skipToEnd: function() {this.pos = this.string.length;},
+  skipTo: function(ch) {
+    var found = this.string.indexOf(ch, this.pos);
+    if (found > -1) {this.pos = found; return true;}
+  },
+  backUp: function(n) {this.pos -= n;},
+  column: function() {
+    if (this.lastColumnPos < this.start) {
+      this.lastColumnValue = countColumn(this.string, this.start, this.tabSize, this.lastColumnPos, this.lastColumnValue);
+      this.lastColumnPos = this.start;
+    }
+    return this.lastColumnValue - (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
+  },
+  indentation: function() {
+    return countColumn(this.string, null, this.tabSize) -
+      (this.lineStart ? countColumn(this.string, this.lineStart, this.tabSize) : 0);
+  },
+  match: function(pattern, consume, caseInsensitive) {
+    if (typeof pattern == "string") {
+      var cased = function(str) {return caseInsensitive ? str.toLowerCase() : str;};
+      var substr = this.string.substr(this.pos, pattern.length);
+      if (cased(substr) == cased(pattern)) {
+        if (consume !== false) this.pos += pattern.length;
+        return true;
+      }
+    } else {
+      var match = this.string.slice(this.pos).match(pattern);
+      if (match && match.index > 0) return null;
+      if (match && consume !== false) this.pos += match[0].length;
+      return match;
+    }
+  },
+  current: function(){return this.string.slice(this.start, this.pos);},
+  hideFirstChars: function(n, inner) {
+    this.lineStart += n;
+    try { return inner(); }
+    finally { this.lineStart -= n; }
+  },
+  lookAhead: function(n) {
+    var line = this.context.line + n
+    return line >= this.context.lines.length ? null : this.context.lines[line]
+  }
+};
+exports.StringStream = StringStream;
+
+exports.startState = function(mode, a1, a2) {
+  return mode.startState ? mode.startState(a1, a2) : true;
+};
+
+var modes = exports.modes = {}, mimeModes = exports.mimeModes = {};
+exports.defineMode = function(name, mode) {
+  if (arguments.length > 2)
+    mode.dependencies = Array.prototype.slice.call(arguments, 2);
+  modes[name] = mode;
+};
+exports.defineMIME = function(mime, spec) { mimeModes[mime] = spec; };
+
+exports.defineMode("null", function() {
+  return {token: function(stream) {stream.skipToEnd();}};
+});
+exports.defineMIME("text/plain", "null");
+
+exports.resolveMode = function(spec) {
+  if (typeof spec == "string" && mimeModes.hasOwnProperty(spec)) {
+    spec = mimeModes[spec];
+  } else if (spec && typeof spec.name == "string" && mimeModes.hasOwnProperty(spec.name)) {
+    spec = mimeModes[spec.name];
+  }
+  if (typeof spec == "string") return {name: spec};
+  else return spec || {name: "null"};
+};
+
+function copyObj(obj, target, overwrite) {
+  if (!target) target = {};
+  for (var prop in obj)
+    if (obj.hasOwnProperty(prop) && (overwrite !== false || !target.hasOwnProperty(prop)))
+      target[prop] = obj[prop];
+  return target;
+}
+
+// This can be used to attach properties to mode objects from
+// outside the actual mode definition.
+var modeExtensions = exports.modeExtensions = {};
+exports.extendMode = function(mode, properties) {
+  var exts = modeExtensions.hasOwnProperty(mode) ? modeExtensions[mode] : (modeExtensions[mode] = {});
+  copyObj(properties, exts);
+};
+
+exports.getMode = function(options, spec) {
+  var spec = exports.resolveMode(spec);
+  var mfactory = modes[spec.name];
+  if (!mfactory) return exports.getMode(options, "text/plain");
+  var modeObj = mfactory(options, spec);
+  if (modeExtensions.hasOwnProperty(spec.name)) {
+    var exts = modeExtensions[spec.name];
+    for (var prop in exts) {
+      if (!exts.hasOwnProperty(prop)) continue;
+      if (modeObj.hasOwnProperty(prop)) modeObj["_" + prop] = modeObj[prop];
+      modeObj[prop] = exts[prop];
+    }
+  }
+  modeObj.name = spec.name;
+  if (spec.helperType) modeObj.helperType = spec.helperType;
+  if (spec.modeProps) for (var prop in spec.modeProps)
+    modeObj[prop] = spec.modeProps[prop];
+
+  return modeObj;
+};
+
+exports.innerMode = function(mode, state) {
+  var info;
+  while (mode.innerMode) {
+    info = mode.innerMode(state);
+    if (!info || info.mode == mode) break;
+    state = info.state;
+    mode = info.mode;
+  }
+  return info || {mode: mode, state: state};
+}
+
+exports.registerHelper = exports.registerGlobalHelper = Math.min;
+
+exports.runMode = function(string, modespec, callback, options) {
+  var mode = exports.getMode({indentUnit: 2}, modespec);
+  var lines = splitLines(string), state = (options && options.state) || exports.startState(mode);
+  var context = {lines: lines, line: 0}
+  for (var i = 0, e = lines.length; i < e; ++i, ++context.line) {
+    if (i) callback("\n");
+    var stream = new exports.StringStream(lines[i], 4, context);
+    if (!stream.string && mode.blankLine) mode.blankLine(state);
+    while (!stream.eol()) {
+      var style = mode.token(stream, state);
+      callback(stream.current(), style, i, stream.start, state);
+      stream.start = stream.pos;
+    }
+  }
+};
+
+require.cache[require.resolve("../../lib/codemirror")] = require.cache[require.resolve("./runmode.node")];
+require.cache[require.resolve("../../addon/runmode/runmode")] = require.cache[require.resolve("./runmode.node")];
diff --git a/public/vendor/plugins/codemirror/addon/scroll/annotatescrollbar.js b/public/vendor/plugins/codemirror/addon/scroll/annotatescrollbar.js
new file mode 100644
index 0000000000..9fe61ec1ff
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/scroll/annotatescrollbar.js
@@ -0,0 +1,122 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  CodeMirror.defineExtension("annotateScrollbar", function(options) {
+    if (typeof options == "string") options = {className: options};
+    return new Annotation(this, options);
+  });
+
+  CodeMirror.defineOption("scrollButtonHeight", 0);
+
+  function Annotation(cm, options) {
+    this.cm = cm;
+    this.options = options;
+    this.buttonHeight = options.scrollButtonHeight || cm.getOption("scrollButtonHeight");
+    this.annotations = [];
+    this.doRedraw = this.doUpdate = null;
+    this.div = cm.getWrapperElement().appendChild(document.createElement("div"));
+    this.div.style.cssText = "position: absolute; right: 0; top: 0; z-index: 7; pointer-events: none";
+    this.computeScale();
+
+    function scheduleRedraw(delay) {
+      clearTimeout(self.doRedraw);
+      self.doRedraw = setTimeout(function() { self.redraw(); }, delay);
+    }
+
+    var self = this;
+    cm.on("refresh", this.resizeHandler = function() {
+      clearTimeout(self.doUpdate);
+      self.doUpdate = setTimeout(function() {
+        if (self.computeScale()) scheduleRedraw(20);
+      }, 100);
+    });
+    cm.on("markerAdded", this.resizeHandler);
+    cm.on("markerCleared", this.resizeHandler);
+    if (options.listenForChanges !== false)
+      cm.on("changes", this.changeHandler = function() {
+        scheduleRedraw(250);
+      });
+  }
+
+  Annotation.prototype.computeScale = function() {
+    var cm = this.cm;
+    var hScale = (cm.getWrapperElement().clientHeight - cm.display.barHeight - this.buttonHeight * 2) /
+      cm.getScrollerElement().scrollHeight
+    if (hScale != this.hScale) {
+      this.hScale = hScale;
+      return true;
+    }
+  };
+
+  Annotation.prototype.update = function(annotations) {
+    this.annotations = annotations;
+    this.redraw();
+  };
+
+  Annotation.prototype.redraw = function(compute) {
+    if (compute !== false) this.computeScale();
+    var cm = this.cm, hScale = this.hScale;
+
+    var frag = document.createDocumentFragment(), anns = this.annotations;
+
+    var wrapping = cm.getOption("lineWrapping");
+    var singleLineH = wrapping && cm.defaultTextHeight() * 1.5;
+    var curLine = null, curLineObj = null;
+    function getY(pos, top) {
+      if (curLine != pos.line) {
+        curLine = pos.line;
+        curLineObj = cm.getLineHandle(curLine);
+      }
+      if ((curLineObj.widgets && curLineObj.widgets.length) ||
+          (wrapping && curLineObj.height > singleLineH))
+        return cm.charCoords(pos, "local")[top ? "top" : "bottom"];
+      var topY = cm.heightAtLine(curLineObj, "local");
+      return topY + (top ? 0 : curLineObj.height);
+    }
+
+    var lastLine = cm.lastLine()
+    if (cm.display.barWidth) for (var i = 0, nextTop; i < anns.length; i++) {
+      var ann = anns[i];
+      if (ann.to.line > lastLine) continue;
+      var top = nextTop || getY(ann.from, true) * hScale;
+      var bottom = getY(ann.to, false) * hScale;
+      while (i < anns.length - 1) {
+        if (anns[i + 1].to.line > lastLine) break;
+        nextTop = getY(anns[i + 1].from, true) * hScale;
+        if (nextTop > bottom + .9) break;
+        ann = anns[++i];
+        bottom = getY(ann.to, false) * hScale;
+      }
+      if (bottom == top) continue;
+      var height = Math.max(bottom - top, 3);
+
+      var elt = frag.appendChild(document.createElement("div"));
+      elt.style.cssText = "position: absolute; right: 0px; width: " + Math.max(cm.display.barWidth - 1, 2) + "px; top: "
+        + (top + this.buttonHeight) + "px; height: " + height + "px";
+      elt.className = this.options.className;
+      if (ann.id) {
+        elt.setAttribute("annotation-id", ann.id);
+      }
+    }
+    this.div.textContent = "";
+    this.div.appendChild(frag);
+  };
+
+  Annotation.prototype.clear = function() {
+    this.cm.off("refresh", this.resizeHandler);
+    this.cm.off("markerAdded", this.resizeHandler);
+    this.cm.off("markerCleared", this.resizeHandler);
+    if (this.changeHandler) this.cm.off("changes", this.changeHandler);
+    this.div.parentNode.removeChild(this.div);
+  };
+});
diff --git a/public/vendor/plugins/codemirror/addon/scroll/scrollpastend.js b/public/vendor/plugins/codemirror/addon/scroll/scrollpastend.js
new file mode 100644
index 0000000000..2ed9d95e84
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/scroll/scrollpastend.js
@@ -0,0 +1,48 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  CodeMirror.defineOption("scrollPastEnd", false, function(cm, val, old) {
+    if (old && old != CodeMirror.Init) {
+      cm.off("change", onChange);
+      cm.off("refresh", updateBottomMargin);
+      cm.display.lineSpace.parentNode.style.paddingBottom = "";
+      cm.state.scrollPastEndPadding = null;
+    }
+    if (val) {
+      cm.on("change", onChange);
+      cm.on("refresh", updateBottomMargin);
+      updateBottomMargin(cm);
+    }
+  });
+
+  function onChange(cm, change) {
+    if (CodeMirror.changeEnd(change).line == cm.lastLine())
+      updateBottomMargin(cm);
+  }
+
+  function updateBottomMargin(cm) {
+    var padding = "";
+    if (cm.lineCount() > 1) {
+      var totalH = cm.display.scroller.clientHeight - 30,
+          lastLineH = cm.getLineHandle(cm.lastLine()).height;
+      padding = (totalH - lastLineH) + "px";
+    }
+    if (cm.state.scrollPastEndPadding != padding) {
+      cm.state.scrollPastEndPadding = padding;
+      cm.display.lineSpace.parentNode.style.paddingBottom = padding;
+      cm.off("refresh", updateBottomMargin);
+      cm.setSize();
+      cm.on("refresh", updateBottomMargin);
+    }
+  }
+});
diff --git a/public/vendor/plugins/codemirror/addon/scroll/simplescrollbars.css b/public/vendor/plugins/codemirror/addon/scroll/simplescrollbars.css
new file mode 100644
index 0000000000..5eea7aa1b3
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/scroll/simplescrollbars.css
@@ -0,0 +1,66 @@
+.CodeMirror-simplescroll-horizontal div, .CodeMirror-simplescroll-vertical div {
+  position: absolute;
+  background: #ccc;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+  border: 1px solid #bbb;
+  border-radius: 2px;
+}
+
+.CodeMirror-simplescroll-horizontal, .CodeMirror-simplescroll-vertical {
+  position: absolute;
+  z-index: 6;
+  background: #eee;
+}
+
+.CodeMirror-simplescroll-horizontal {
+  bottom: 0; left: 0;
+  height: 8px;
+}
+.CodeMirror-simplescroll-horizontal div {
+  bottom: 0;
+  height: 100%;
+}
+
+.CodeMirror-simplescroll-vertical {
+  right: 0; top: 0;
+  width: 8px;
+}
+.CodeMirror-simplescroll-vertical div {
+  right: 0;
+  width: 100%;
+}
+
+
+.CodeMirror-overlayscroll .CodeMirror-scrollbar-filler, .CodeMirror-overlayscroll .CodeMirror-gutter-filler {
+  display: none;
+}
+
+.CodeMirror-overlayscroll-horizontal div, .CodeMirror-overlayscroll-vertical div {
+  position: absolute;
+  background: #bcd;
+  border-radius: 3px;
+}
+
+.CodeMirror-overlayscroll-horizontal, .CodeMirror-overlayscroll-vertical {
+  position: absolute;
+  z-index: 6;
+}
+
+.CodeMirror-overlayscroll-horizontal {
+  bottom: 0; left: 0;
+  height: 6px;
+}
+.CodeMirror-overlayscroll-horizontal div {
+  bottom: 0;
+  height: 100%;
+}
+
+.CodeMirror-overlayscroll-vertical {
+  right: 0; top: 0;
+  width: 6px;
+}
+.CodeMirror-overlayscroll-vertical div {
+  right: 0;
+  width: 100%;
+}
diff --git a/public/vendor/plugins/codemirror/addon/scroll/simplescrollbars.js b/public/vendor/plugins/codemirror/addon/scroll/simplescrollbars.js
new file mode 100644
index 0000000000..750a2bd399
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/scroll/simplescrollbars.js
@@ -0,0 +1,152 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  function Bar(cls, orientation, scroll) {
+    this.orientation = orientation;
+    this.scroll = scroll;
+    this.screen = this.total = this.size = 1;
+    this.pos = 0;
+
+    this.node = document.createElement("div");
+    this.node.className = cls + "-" + orientation;
+    this.inner = this.node.appendChild(document.createElement("div"));
+
+    var self = this;
+    CodeMirror.on(this.inner, "mousedown", function(e) {
+      if (e.which != 1) return;
+      CodeMirror.e_preventDefault(e);
+      var axis = self.orientation == "horizontal" ? "pageX" : "pageY";
+      var start = e[axis], startpos = self.pos;
+      function done() {
+        CodeMirror.off(document, "mousemove", move);
+        CodeMirror.off(document, "mouseup", done);
+      }
+      function move(e) {
+        if (e.which != 1) return done();
+        self.moveTo(startpos + (e[axis] - start) * (self.total / self.size));
+      }
+      CodeMirror.on(document, "mousemove", move);
+      CodeMirror.on(document, "mouseup", done);
+    });
+
+    CodeMirror.on(this.node, "click", function(e) {
+      CodeMirror.e_preventDefault(e);
+      var innerBox = self.inner.getBoundingClientRect(), where;
+      if (self.orientation == "horizontal")
+        where = e.clientX < innerBox.left ? -1 : e.clientX > innerBox.right ? 1 : 0;
+      else
+        where = e.clientY < innerBox.top ? -1 : e.clientY > innerBox.bottom ? 1 : 0;
+      self.moveTo(self.pos + where * self.screen);
+    });
+
+    function onWheel(e) {
+      var moved = CodeMirror.wheelEventPixels(e)[self.orientation == "horizontal" ? "x" : "y"];
+      var oldPos = self.pos;
+      self.moveTo(self.pos + moved);
+      if (self.pos != oldPos) CodeMirror.e_preventDefault(e);
+    }
+    CodeMirror.on(this.node, "mousewheel", onWheel);
+    CodeMirror.on(this.node, "DOMMouseScroll", onWheel);
+  }
+
+  Bar.prototype.setPos = function(pos, force) {
+    if (pos < 0) pos = 0;
+    if (pos > this.total - this.screen) pos = this.total - this.screen;
+    if (!force && pos == this.pos) return false;
+    this.pos = pos;
+    this.inner.style[this.orientation == "horizontal" ? "left" : "top"] =
+      (pos * (this.size / this.total)) + "px";
+    return true
+  };
+
+  Bar.prototype.moveTo = function(pos) {
+    if (this.setPos(pos)) this.scroll(pos, this.orientation);
+  }
+
+  var minButtonSize = 10;
+
+  Bar.prototype.update = function(scrollSize, clientSize, barSize) {
+    var sizeChanged = this.screen != clientSize || this.total != scrollSize || this.size != barSize
+    if (sizeChanged) {
+      this.screen = clientSize;
+      this.total = scrollSize;
+      this.size = barSize;
+    }
+
+    var buttonSize = this.screen * (this.size / this.total);
+    if (buttonSize < minButtonSize) {
+      this.size -= minButtonSize - buttonSize;
+      buttonSize = minButtonSize;
+    }
+    this.inner.style[this.orientation == "horizontal" ? "width" : "height"] =
+      buttonSize + "px";
+    this.setPos(this.pos, sizeChanged);
+  };
+
+  function SimpleScrollbars(cls, place, scroll) {
+    this.addClass = cls;
+    this.horiz = new Bar(cls, "horizontal", scroll);
+    place(this.horiz.node);
+    this.vert = new Bar(cls, "vertical", scroll);
+    place(this.vert.node);
+    this.width = null;
+  }
+
+  SimpleScrollbars.prototype.update = function(measure) {
+    if (this.width == null) {
+      var style = window.getComputedStyle ? window.getComputedStyle(this.horiz.node) : this.horiz.node.currentStyle;
+      if (style) this.width = parseInt(style.height);
+    }
+    var width = this.width || 0;
+
+    var needsH = measure.scrollWidth > measure.clientWidth + 1;
+    var needsV = measure.scrollHeight > measure.clientHeight + 1;
+    this.vert.node.style.display = needsV ? "block" : "none";
+    this.horiz.node.style.display = needsH ? "block" : "none";
+
+    if (needsV) {
+      this.vert.update(measure.scrollHeight, measure.clientHeight,
+                       measure.viewHeight - (needsH ? width : 0));
+      this.vert.node.style.bottom = needsH ? width + "px" : "0";
+    }
+    if (needsH) {
+      this.horiz.update(measure.scrollWidth, measure.clientWidth,
+                        measure.viewWidth - (needsV ? width : 0) - measure.barLeft);
+      this.horiz.node.style.right = needsV ? width + "px" : "0";
+      this.horiz.node.style.left = measure.barLeft + "px";
+    }
+
+    return {right: needsV ? width : 0, bottom: needsH ? width : 0};
+  };
+
+  SimpleScrollbars.prototype.setScrollTop = function(pos) {
+    this.vert.setPos(pos);
+  };
+
+  SimpleScrollbars.prototype.setScrollLeft = function(pos) {
+    this.horiz.setPos(pos);
+  };
+
+  SimpleScrollbars.prototype.clear = function() {
+    var parent = this.horiz.node.parentNode;
+    parent.removeChild(this.horiz.node);
+    parent.removeChild(this.vert.node);
+  };
+
+  CodeMirror.scrollbarModel.simple = function(place, scroll) {
+    return new SimpleScrollbars("CodeMirror-simplescroll", place, scroll);
+  };
+  CodeMirror.scrollbarModel.overlay = function(place, scroll) {
+    return new SimpleScrollbars("CodeMirror-overlayscroll", place, scroll);
+  };
+});
diff --git a/public/vendor/plugins/codemirror/addon/search/jump-to-line.js b/public/vendor/plugins/codemirror/addon/search/jump-to-line.js
new file mode 100644
index 0000000000..1f3526d247
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/search/jump-to-line.js
@@ -0,0 +1,50 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// Defines jumpToLine command. Uses dialog.js if present.
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"), require("../dialog/dialog"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror", "../dialog/dialog"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  function dialog(cm, text, shortText, deflt, f) {
+    if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true});
+    else f(prompt(shortText, deflt));
+  }
+
+  function getJumpDialog(cm) {
+    return cm.phrase("Jump to line:") + ' <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">' + cm.phrase("(Use line:column or scroll% syntax)") + '</span>';
+  }
+
+  function interpretLine(cm, string) {
+    var num = Number(string)
+    if (/^[-+]/.test(string)) return cm.getCursor().line + num
+    else return num - 1
+  }
+
+  CodeMirror.commands.jumpToLine = function(cm) {
+    var cur = cm.getCursor();
+    dialog(cm, getJumpDialog(cm), cm.phrase("Jump to line:"), (cur.line + 1) + ":" + cur.ch, function(posStr) {
+      if (!posStr) return;
+
+      var match;
+      if (match = /^\s*([\+\-]?\d+)\s*\:\s*(\d+)\s*$/.exec(posStr)) {
+        cm.setCursor(interpretLine(cm, match[1]), Number(match[2]))
+      } else if (match = /^\s*([\+\-]?\d+(\.\d+)?)\%\s*/.exec(posStr)) {
+        var line = Math.round(cm.lineCount() * Number(match[1]) / 100);
+        if (/^[-+]/.test(match[1])) line = cur.line + line + 1;
+        cm.setCursor(line - 1, cur.ch);
+      } else if (match = /^\s*\:?\s*([\+\-]?\d+)\s*/.exec(posStr)) {
+        cm.setCursor(interpretLine(cm, match[1]), cur.ch);
+      }
+    });
+  };
+
+  CodeMirror.keyMap["default"]["Alt-G"] = "jumpToLine";
+});
diff --git a/public/vendor/plugins/codemirror/addon/search/match-highlighter.js b/public/vendor/plugins/codemirror/addon/search/match-highlighter.js
new file mode 100644
index 0000000000..b344ac79e2
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/search/match-highlighter.js
@@ -0,0 +1,165 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// Highlighting text that matches the selection
+//
+// Defines an option highlightSelectionMatches, which, when enabled,
+// will style strings that match the selection throughout the
+// document.
+//
+// The option can be set to true to simply enable it, or to a
+// {minChars, style, wordsOnly, showToken, delay} object to explicitly
+// configure it. minChars is the minimum amount of characters that should be
+// selected for the behavior to occur, and style is the token style to
+// apply to the matches. This will be prefixed by "cm-" to create an
+// actual CSS class name. If wordsOnly is enabled, the matches will be
+// highlighted only if the selected text is a word. showToken, when enabled,
+// will cause the current token to be highlighted when nothing is selected.
+// delay is used to specify how much time to wait, in milliseconds, before
+// highlighting the matches. If annotateScrollbar is enabled, the occurences
+// will be highlighted on the scrollbar via the matchesonscrollbar addon.
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"), require("./matchesonscrollbar"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror", "./matchesonscrollbar"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  var defaults = {
+    style: "matchhighlight",
+    minChars: 2,
+    delay: 100,
+    wordsOnly: false,
+    annotateScrollbar: false,
+    showToken: false,
+    trim: true
+  }
+
+  function State(options) {
+    this.options = {}
+    for (var name in defaults)
+      this.options[name] = (options && options.hasOwnProperty(name) ? options : defaults)[name]
+    this.overlay = this.timeout = null;
+    this.matchesonscroll = null;
+    this.active = false;
+  }
+
+  CodeMirror.defineOption("highlightSelectionMatches", false, function(cm, val, old) {
+    if (old && old != CodeMirror.Init) {
+      removeOverlay(cm);
+      clearTimeout(cm.state.matchHighlighter.timeout);
+      cm.state.matchHighlighter = null;
+      cm.off("cursorActivity", cursorActivity);
+      cm.off("focus", onFocus)
+    }
+    if (val) {
+      var state = cm.state.matchHighlighter = new State(val);
+      if (cm.hasFocus()) {
+        state.active = true
+        highlightMatches(cm)
+      } else {
+        cm.on("focus", onFocus)
+      }
+      cm.on("cursorActivity", cursorActivity);
+    }
+  });
+
+  function cursorActivity(cm) {
+    var state = cm.state.matchHighlighter;
+    if (state.active || cm.hasFocus()) scheduleHighlight(cm, state)
+  }
+
+  function onFocus(cm) {
+    var state = cm.state.matchHighlighter
+    if (!state.active) {
+      state.active = true
+      scheduleHighlight(cm, state)
+    }
+  }
+
+  function scheduleHighlight(cm, state) {
+    clearTimeout(state.timeout);
+    state.timeout = setTimeout(function() {highlightMatches(cm);}, state.options.delay);
+  }
+
+  function addOverlay(cm, query, hasBoundary, style) {
+    var state = cm.state.matchHighlighter;
+    cm.addOverlay(state.overlay = makeOverlay(query, hasBoundary, style));
+    if (state.options.annotateScrollbar && cm.showMatchesOnScrollbar) {
+      var searchFor = hasBoundary ? new RegExp("\\b" + query.replace(/[\\\[.+*?(){|^$]/g, "\\$&") + "\\b") : query;
+      state.matchesonscroll = cm.showMatchesOnScrollbar(searchFor, false,
+        {className: "CodeMirror-selection-highlight-scrollbar"});
+    }
+  }
+
+  function removeOverlay(cm) {
+    var state = cm.state.matchHighlighter;
+    if (state.overlay) {
+      cm.removeOverlay(state.overlay);
+      state.overlay = null;
+      if (state.matchesonscroll) {
+        state.matchesonscroll.clear();
+        state.matchesonscroll = null;
+      }
+    }
+  }
+
+  function highlightMatches(cm) {
+    cm.operation(function() {
+      var state = cm.state.matchHighlighter;
+      removeOverlay(cm);
+      if (!cm.somethingSelected() && state.options.showToken) {
+        var re = state.options.showToken === true ? /[\w$]/ : state.options.showToken;
+        var cur = cm.getCursor(), line = cm.getLine(cur.line), start = cur.ch, end = start;
+        while (start && re.test(line.charAt(start - 1))) --start;
+        while (end < line.length && re.test(line.charAt(end))) ++end;
+        if (start < end)
+          addOverlay(cm, line.slice(start, end), re, state.options.style);
+        return;
+      }
+      var from = cm.getCursor("from"), to = cm.getCursor("to");
+      if (from.line != to.line) return;
+      if (state.options.wordsOnly && !isWord(cm, from, to)) return;
+      var selection = cm.getRange(from, to)
+      if (state.options.trim) selection = selection.replace(/^\s+|\s+$/g, "")
+      if (selection.length >= state.options.minChars)
+        addOverlay(cm, selection, false, state.options.style);
+    });
+  }
+
+  function isWord(cm, from, to) {
+    var str = cm.getRange(from, to);
+    if (str.match(/^\w+$/) !== null) {
+        if (from.ch > 0) {
+            var pos = {line: from.line, ch: from.ch - 1};
+            var chr = cm.getRange(pos, from);
+            if (chr.match(/\W/) === null) return false;
+        }
+        if (to.ch < cm.getLine(from.line).length) {
+            var pos = {line: to.line, ch: to.ch + 1};
+            var chr = cm.getRange(to, pos);
+            if (chr.match(/\W/) === null) return false;
+        }
+        return true;
+    } else return false;
+  }
+
+  function boundariesAround(stream, re) {
+    return (!stream.start || !re.test(stream.string.charAt(stream.start - 1))) &&
+      (stream.pos == stream.string.length || !re.test(stream.string.charAt(stream.pos)));
+  }
+
+  function makeOverlay(query, hasBoundary, style) {
+    return {token: function(stream) {
+      if (stream.match(query) &&
+          (!hasBoundary || boundariesAround(stream, hasBoundary)))
+        return style;
+      stream.next();
+      stream.skipTo(query.charAt(0)) || stream.skipToEnd();
+    }};
+  }
+});
diff --git a/public/vendor/plugins/codemirror/addon/search/matchesonscrollbar.css b/public/vendor/plugins/codemirror/addon/search/matchesonscrollbar.css
new file mode 100644
index 0000000000..77932cc908
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/search/matchesonscrollbar.css
@@ -0,0 +1,8 @@
+.CodeMirror-search-match {
+  background: gold;
+  border-top: 1px solid orange;
+  border-bottom: 1px solid orange;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+  opacity: .5;
+}
diff --git a/public/vendor/plugins/codemirror/addon/search/matchesonscrollbar.js b/public/vendor/plugins/codemirror/addon/search/matchesonscrollbar.js
new file mode 100644
index 0000000000..8a4a827584
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/search/matchesonscrollbar.js
@@ -0,0 +1,97 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"), require("./searchcursor"), require("../scroll/annotatescrollbar"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror", "./searchcursor", "../scroll/annotatescrollbar"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  CodeMirror.defineExtension("showMatchesOnScrollbar", function(query, caseFold, options) {
+    if (typeof options == "string") options = {className: options};
+    if (!options) options = {};
+    return new SearchAnnotation(this, query, caseFold, options);
+  });
+
+  function SearchAnnotation(cm, query, caseFold, options) {
+    this.cm = cm;
+    this.options = options;
+    var annotateOptions = {listenForChanges: false};
+    for (var prop in options) annotateOptions[prop] = options[prop];
+    if (!annotateOptions.className) annotateOptions.className = "CodeMirror-search-match";
+    this.annotation = cm.annotateScrollbar(annotateOptions);
+    this.query = query;
+    this.caseFold = caseFold;
+    this.gap = {from: cm.firstLine(), to: cm.lastLine() + 1};
+    this.matches = [];
+    this.update = null;
+
+    this.findMatches();
+    this.annotation.update(this.matches);
+
+    var self = this;
+    cm.on("change", this.changeHandler = function(_cm, change) { self.onChange(change); });
+  }
+
+  var MAX_MATCHES = 1000;
+
+  SearchAnnotation.prototype.findMatches = function() {
+    if (!this.gap) return;
+    for (var i = 0; i < this.matches.length; i++) {
+      var match = this.matches[i];
+      if (match.from.line >= this.gap.to) break;
+      if (match.to.line >= this.gap.from) this.matches.splice(i--, 1);
+    }
+    var cursor = this.cm.getSearchCursor(this.query, CodeMirror.Pos(this.gap.from, 0), {caseFold: this.caseFold, multiline: this.options.multiline});
+    var maxMatches = this.options && this.options.maxMatches || MAX_MATCHES;
+    while (cursor.findNext()) {
+      var match = {from: cursor.from(), to: cursor.to()};
+      if (match.from.line >= this.gap.to) break;
+      this.matches.splice(i++, 0, match);
+      if (this.matches.length > maxMatches) break;
+    }
+    this.gap = null;
+  };
+
+  function offsetLine(line, changeStart, sizeChange) {
+    if (line <= changeStart) return line;
+    return Math.max(changeStart, line + sizeChange);
+  }
+
+  SearchAnnotation.prototype.onChange = function(change) {
+    var startLine = change.from.line;
+    var endLine = CodeMirror.changeEnd(change).line;
+    var sizeChange = endLine - change.to.line;
+    if (this.gap) {
+      this.gap.from = Math.min(offsetLine(this.gap.from, startLine, sizeChange), change.from.line);
+      this.gap.to = Math.max(offsetLine(this.gap.to, startLine, sizeChange), change.from.line);
+    } else {
+      this.gap = {from: change.from.line, to: endLine + 1};
+    }
+
+    if (sizeChange) for (var i = 0; i < this.matches.length; i++) {
+      var match = this.matches[i];
+      var newFrom = offsetLine(match.from.line, startLine, sizeChange);
+      if (newFrom != match.from.line) match.from = CodeMirror.Pos(newFrom, match.from.ch);
+      var newTo = offsetLine(match.to.line, startLine, sizeChange);
+      if (newTo != match.to.line) match.to = CodeMirror.Pos(newTo, match.to.ch);
+    }
+    clearTimeout(this.update);
+    var self = this;
+    this.update = setTimeout(function() { self.updateAfterChange(); }, 250);
+  };
+
+  SearchAnnotation.prototype.updateAfterChange = function() {
+    this.findMatches();
+    this.annotation.update(this.matches);
+  };
+
+  SearchAnnotation.prototype.clear = function() {
+    this.cm.off("change", this.changeHandler);
+    this.annotation.clear();
+  };
+});
diff --git a/public/vendor/plugins/codemirror/addon/search/search.js b/public/vendor/plugins/codemirror/addon/search/search.js
new file mode 100644
index 0000000000..cecdd52ea1
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/search/search.js
@@ -0,0 +1,260 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// Define search commands. Depends on dialog.js or another
+// implementation of the openDialog method.
+
+// Replace works a little oddly -- it will do the replace on the next
+// Ctrl-G (or whatever is bound to findNext) press. You prevent a
+// replace by making sure the match is no longer selected when hitting
+// Ctrl-G.
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"), require("./searchcursor"), require("../dialog/dialog"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror", "./searchcursor", "../dialog/dialog"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  function searchOverlay(query, caseInsensitive) {
+    if (typeof query == "string")
+      query = new RegExp(query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"), caseInsensitive ? "gi" : "g");
+    else if (!query.global)
+      query = new RegExp(query.source, query.ignoreCase ? "gi" : "g");
+
+    return {token: function(stream) {
+      query.lastIndex = stream.pos;
+      var match = query.exec(stream.string);
+      if (match && match.index == stream.pos) {
+        stream.pos += match[0].length || 1;
+        return "searching";
+      } else if (match) {
+        stream.pos = match.index;
+      } else {
+        stream.skipToEnd();
+      }
+    }};
+  }
+
+  function SearchState() {
+    this.posFrom = this.posTo = this.lastQuery = this.query = null;
+    this.overlay = null;
+  }
+
+  function getSearchState(cm) {
+    return cm.state.search || (cm.state.search = new SearchState());
+  }
+
+  function queryCaseInsensitive(query) {
+    return typeof query == "string" && query == query.toLowerCase();
+  }
+
+  function getSearchCursor(cm, query, pos) {
+    // Heuristic: if the query string is all lowercase, do a case insensitive search.
+    return cm.getSearchCursor(query, pos, {caseFold: queryCaseInsensitive(query), multiline: true});
+  }
+
+  function persistentDialog(cm, text, deflt, onEnter, onKeyDown) {
+    cm.openDialog(text, onEnter, {
+      value: deflt,
+      selectValueOnOpen: true,
+      closeOnEnter: false,
+      onClose: function() { clearSearch(cm); },
+      onKeyDown: onKeyDown
+    });
+  }
+
+  function dialog(cm, text, shortText, deflt, f) {
+    if (cm.openDialog) cm.openDialog(text, f, {value: deflt, selectValueOnOpen: true});
+    else f(prompt(shortText, deflt));
+  }
+
+  function confirmDialog(cm, text, shortText, fs) {
+    if (cm.openConfirm) cm.openConfirm(text, fs);
+    else if (confirm(shortText)) fs[0]();
+  }
+
+  function parseString(string) {
+    return string.replace(/\\([nrt\\])/g, function(match, ch) {
+      if (ch == "n") return "\n"
+      if (ch == "r") return "\r"
+      if (ch == "t") return "\t"
+      if (ch == "\\") return "\\"
+      return match
+    })
+  }
+
+  function parseQuery(query) {
+    var isRE = query.match(/^\/(.*)\/([a-z]*)$/);
+    if (isRE) {
+      try { query = new RegExp(isRE[1], isRE[2].indexOf("i") == -1 ? "" : "i"); }
+      catch(e) {} // Not a regular expression after all, do a string search
+    } else {
+      query = parseString(query)
+    }
+    if (typeof query == "string" ? query == "" : query.test(""))
+      query = /x^/;
+    return query;
+  }
+
+  function startSearch(cm, state, query) {
+    state.queryText = query;
+    state.query = parseQuery(query);
+    cm.removeOverlay(state.overlay, queryCaseInsensitive(state.query));
+    state.overlay = searchOverlay(state.query, queryCaseInsensitive(state.query));
+    cm.addOverlay(state.overlay);
+    if (cm.showMatchesOnScrollbar) {
+      if (state.annotate) { state.annotate.clear(); state.annotate = null; }
+      state.annotate = cm.showMatchesOnScrollbar(state.query, queryCaseInsensitive(state.query));
+    }
+  }
+
+  function doSearch(cm, rev, persistent, immediate) {
+    var state = getSearchState(cm);
+    if (state.query) return findNext(cm, rev);
+    var q = cm.getSelection() || state.lastQuery;
+    if (q instanceof RegExp && q.source == "x^") q = null
+    if (persistent && cm.openDialog) {
+      var hiding = null
+      var searchNext = function(query, event) {
+        CodeMirror.e_stop(event);
+        if (!query) return;
+        if (query != state.queryText) {
+          startSearch(cm, state, query);
+          state.posFrom = state.posTo = cm.getCursor();
+        }
+        if (hiding) hiding.style.opacity = 1
+        findNext(cm, event.shiftKey, function(_, to) {
+          var dialog
+          if (to.line < 3 && document.querySelector &&
+              (dialog = cm.display.wrapper.querySelector(".CodeMirror-dialog")) &&
+              dialog.getBoundingClientRect().bottom - 4 > cm.cursorCoords(to, "window").top)
+            (hiding = dialog).style.opacity = .4
+        })
+      };
+      persistentDialog(cm, getQueryDialog(cm), q, searchNext, function(event, query) {
+        var keyName = CodeMirror.keyName(event)
+        var extra = cm.getOption('extraKeys'), cmd = (extra && extra[keyName]) || CodeMirror.keyMap[cm.getOption("keyMap")][keyName]
+        if (cmd == "findNext" || cmd == "findPrev" ||
+          cmd == "findPersistentNext" || cmd == "findPersistentPrev") {
+          CodeMirror.e_stop(event);
+          startSearch(cm, getSearchState(cm), query);
+          cm.execCommand(cmd);
+        } else if (cmd == "find" || cmd == "findPersistent") {
+          CodeMirror.e_stop(event);
+          searchNext(query, event);
+        }
+      });
+      if (immediate && q) {
+        startSearch(cm, state, q);
+        findNext(cm, rev);
+      }
+    } else {
+      dialog(cm, getQueryDialog(cm), "Search for:", q, function(query) {
+        if (query && !state.query) cm.operation(function() {
+          startSearch(cm, state, query);
+          state.posFrom = state.posTo = cm.getCursor();
+          findNext(cm, rev);
+        });
+      });
+    }
+  }
+
+  function findNext(cm, rev, callback) {cm.operation(function() {
+    var state = getSearchState(cm);
+    var cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo);
+    if (!cursor.find(rev)) {
+      cursor = getSearchCursor(cm, state.query, rev ? CodeMirror.Pos(cm.lastLine()) : CodeMirror.Pos(cm.firstLine(), 0));
+      if (!cursor.find(rev)) return;
+    }
+    cm.setSelection(cursor.from(), cursor.to());
+    cm.scrollIntoView({from: cursor.from(), to: cursor.to()}, 20);
+    state.posFrom = cursor.from(); state.posTo = cursor.to();
+    if (callback) callback(cursor.from(), cursor.to())
+  });}
+
+  function clearSearch(cm) {cm.operation(function() {
+    var state = getSearchState(cm);
+    state.lastQuery = state.query;
+    if (!state.query) return;
+    state.query = state.queryText = null;
+    cm.removeOverlay(state.overlay);
+    if (state.annotate) { state.annotate.clear(); state.annotate = null; }
+  });}
+
+
+  function getQueryDialog(cm)  {
+    return '<span class="CodeMirror-search-label">' + cm.phrase("Search:") + '</span> <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">' + cm.phrase("(Use /re/ syntax for regexp search)") + '</span>';
+  }
+  function getReplaceQueryDialog(cm) {
+    return ' <input type="text" style="width: 10em" class="CodeMirror-search-field"/> <span style="color: #888" class="CodeMirror-search-hint">' + cm.phrase("(Use /re/ syntax for regexp search)") + '</span>';
+  }
+  function getReplacementQueryDialog(cm) {
+    return '<span class="CodeMirror-search-label">' + cm.phrase("With:") + '</span> <input type="text" style="width: 10em" class="CodeMirror-search-field"/>';
+  }
+  function getDoReplaceConfirm(cm) {
+    return '<span class="CodeMirror-search-label">' + cm.phrase("Replace?") + '</span> <button>' + cm.phrase("Yes") + '</button> <button>' + cm.phrase("No") + '</button> <button>' + cm.phrase("All") + '</button> <button>' + cm.phrase("Stop") + '</button> ';
+  }
+
+  function replaceAll(cm, query, text) {
+    cm.operation(function() {
+      for (var cursor = getSearchCursor(cm, query); cursor.findNext();) {
+        if (typeof query != "string") {
+          var match = cm.getRange(cursor.from(), cursor.to()).match(query);
+          cursor.replace(text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
+        } else cursor.replace(text);
+      }
+    });
+  }
+
+  function replace(cm, all) {
+    if (cm.getOption("readOnly")) return;
+    var query = cm.getSelection() || getSearchState(cm).lastQuery;
+    var dialogText = '<span class="CodeMirror-search-label">' + (all ? cm.phrase("Replace all:") : cm.phrase("Replace:")) + '</span>';
+    dialog(cm, dialogText + getReplaceQueryDialog(cm), dialogText, query, function(query) {
+      if (!query) return;
+      query = parseQuery(query);
+      dialog(cm, getReplacementQueryDialog(cm), cm.phrase("Replace with:"), "", function(text) {
+        text = parseString(text)
+        if (all) {
+          replaceAll(cm, query, text)
+        } else {
+          clearSearch(cm);
+          var cursor = getSearchCursor(cm, query, cm.getCursor("from"));
+          var advance = function() {
+            var start = cursor.from(), match;
+            if (!(match = cursor.findNext())) {
+              cursor = getSearchCursor(cm, query);
+              if (!(match = cursor.findNext()) ||
+                  (start && cursor.from().line == start.line && cursor.from().ch == start.ch)) return;
+            }
+            cm.setSelection(cursor.from(), cursor.to());
+            cm.scrollIntoView({from: cursor.from(), to: cursor.to()});
+            confirmDialog(cm, getDoReplaceConfirm(cm), cm.phrase("Replace?"),
+                          [function() {doReplace(match);}, advance,
+                           function() {replaceAll(cm, query, text)}]);
+          };
+          var doReplace = function(match) {
+            cursor.replace(typeof query == "string" ? text :
+                           text.replace(/\$(\d)/g, function(_, i) {return match[i];}));
+            advance();
+          };
+          advance();
+        }
+      });
+    });
+  }
+
+  CodeMirror.commands.find = function(cm) {clearSearch(cm); doSearch(cm);};
+  CodeMirror.commands.findPersistent = function(cm) {clearSearch(cm); doSearch(cm, false, true);};
+  CodeMirror.commands.findPersistentNext = function(cm) {doSearch(cm, false, true, true);};
+  CodeMirror.commands.findPersistentPrev = function(cm) {doSearch(cm, true, true, true);};
+  CodeMirror.commands.findNext = doSearch;
+  CodeMirror.commands.findPrev = function(cm) {doSearch(cm, true);};
+  CodeMirror.commands.clearSearch = clearSearch;
+  CodeMirror.commands.replace = replace;
+  CodeMirror.commands.replaceAll = function(cm) {replace(cm, true);};
+});
diff --git a/public/vendor/plugins/codemirror/addon/search/searchcursor.js b/public/vendor/plugins/codemirror/addon/search/searchcursor.js
new file mode 100644
index 0000000000..aae36dfe53
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/search/searchcursor.js
@@ -0,0 +1,293 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"))
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod)
+  else // Plain browser env
+    mod(CodeMirror)
+})(function(CodeMirror) {
+  "use strict"
+  var Pos = CodeMirror.Pos
+
+  function regexpFlags(regexp) {
+    var flags = regexp.flags
+    return flags != null ? flags : (regexp.ignoreCase ? "i" : "")
+      + (regexp.global ? "g" : "")
+      + (regexp.multiline ? "m" : "")
+  }
+
+  function ensureFlags(regexp, flags) {
+    var current = regexpFlags(regexp), target = current
+    for (var i = 0; i < flags.length; i++) if (target.indexOf(flags.charAt(i)) == -1)
+      target += flags.charAt(i)
+    return current == target ? regexp : new RegExp(regexp.source, target)
+  }
+
+  function maybeMultiline(regexp) {
+    return /\\s|\\n|\n|\\W|\\D|\[\^/.test(regexp.source)
+  }
+
+  function searchRegexpForward(doc, regexp, start) {
+    regexp = ensureFlags(regexp, "g")
+    for (var line = start.line, ch = start.ch, last = doc.lastLine(); line <= last; line++, ch = 0) {
+      regexp.lastIndex = ch
+      var string = doc.getLine(line), match = regexp.exec(string)
+      if (match)
+        return {from: Pos(line, match.index),
+                to: Pos(line, match.index + match[0].length),
+                match: match}
+    }
+  }
+
+  function searchRegexpForwardMultiline(doc, regexp, start) {
+    if (!maybeMultiline(regexp)) return searchRegexpForward(doc, regexp, start)
+
+    regexp = ensureFlags(regexp, "gm")
+    var string, chunk = 1
+    for (var line = start.line, last = doc.lastLine(); line <= last;) {
+      // This grows the search buffer in exponentially-sized chunks
+      // between matches, so that nearby matches are fast and don't
+      // require concatenating the whole document (in case we're
+      // searching for something that has tons of matches), but at the
+      // same time, the amount of retries is limited.
+      for (var i = 0; i < chunk; i++) {
+        if (line > last) break
+        var curLine = doc.getLine(line++)
+        string = string == null ? curLine : string + "\n" + curLine
+      }
+      chunk = chunk * 2
+      regexp.lastIndex = start.ch
+      var match = regexp.exec(string)
+      if (match) {
+        var before = string.slice(0, match.index).split("\n"), inside = match[0].split("\n")
+        var startLine = start.line + before.length - 1, startCh = before[before.length - 1].length
+        return {from: Pos(startLine, startCh),
+                to: Pos(startLine + inside.length - 1,
+                        inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length),
+                match: match}
+      }
+    }
+  }
+
+  function lastMatchIn(string, regexp) {
+    var cutOff = 0, match
+    for (;;) {
+      regexp.lastIndex = cutOff
+      var newMatch = regexp.exec(string)
+      if (!newMatch) return match
+      match = newMatch
+      cutOff = match.index + (match[0].length || 1)
+      if (cutOff == string.length) return match
+    }
+  }
+
+  function searchRegexpBackward(doc, regexp, start) {
+    regexp = ensureFlags(regexp, "g")
+    for (var line = start.line, ch = start.ch, first = doc.firstLine(); line >= first; line--, ch = -1) {
+      var string = doc.getLine(line)
+      if (ch > -1) string = string.slice(0, ch)
+      var match = lastMatchIn(string, regexp)
+      if (match)
+        return {from: Pos(line, match.index),
+                to: Pos(line, match.index + match[0].length),
+                match: match}
+    }
+  }
+
+  function searchRegexpBackwardMultiline(doc, regexp, start) {
+    regexp = ensureFlags(regexp, "gm")
+    var string, chunk = 1
+    for (var line = start.line, first = doc.firstLine(); line >= first;) {
+      for (var i = 0; i < chunk; i++) {
+        var curLine = doc.getLine(line--)
+        string = string == null ? curLine.slice(0, start.ch) : curLine + "\n" + string
+      }
+      chunk *= 2
+
+      var match = lastMatchIn(string, regexp)
+      if (match) {
+        var before = string.slice(0, match.index).split("\n"), inside = match[0].split("\n")
+        var startLine = line + before.length, startCh = before[before.length - 1].length
+        return {from: Pos(startLine, startCh),
+                to: Pos(startLine + inside.length - 1,
+                        inside.length == 1 ? startCh + inside[0].length : inside[inside.length - 1].length),
+                match: match}
+      }
+    }
+  }
+
+  var doFold, noFold
+  if (String.prototype.normalize) {
+    doFold = function(str) { return str.normalize("NFD").toLowerCase() }
+    noFold = function(str) { return str.normalize("NFD") }
+  } else {
+    doFold = function(str) { return str.toLowerCase() }
+    noFold = function(str) { return str }
+  }
+
+  // Maps a position in a case-folded line back to a position in the original line
+  // (compensating for codepoints increasing in number during folding)
+  function adjustPos(orig, folded, pos, foldFunc) {
+    if (orig.length == folded.length) return pos
+    for (var min = 0, max = pos + Math.max(0, orig.length - folded.length);;) {
+      if (min == max) return min
+      var mid = (min + max) >> 1
+      var len = foldFunc(orig.slice(0, mid)).length
+      if (len == pos) return mid
+      else if (len > pos) max = mid
+      else min = mid + 1
+    }
+  }
+
+  function searchStringForward(doc, query, start, caseFold) {
+    // Empty string would match anything and never progress, so we
+    // define it to match nothing instead.
+    if (!query.length) return null
+    var fold = caseFold ? doFold : noFold
+    var lines = fold(query).split(/\r|\n\r?/)
+
+    search: for (var line = start.line, ch = start.ch, last = doc.lastLine() + 1 - lines.length; line <= last; line++, ch = 0) {
+      var orig = doc.getLine(line).slice(ch), string = fold(orig)
+      if (lines.length == 1) {
+        var found = string.indexOf(lines[0])
+        if (found == -1) continue search
+        var start = adjustPos(orig, string, found, fold) + ch
+        return {from: Pos(line, adjustPos(orig, string, found, fold) + ch),
+                to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold) + ch)}
+      } else {
+        var cutFrom = string.length - lines[0].length
+        if (string.slice(cutFrom) != lines[0]) continue search
+        for (var i = 1; i < lines.length - 1; i++)
+          if (fold(doc.getLine(line + i)) != lines[i]) continue search
+        var end = doc.getLine(line + lines.length - 1), endString = fold(end), lastLine = lines[lines.length - 1]
+        if (endString.slice(0, lastLine.length) != lastLine) continue search
+        return {from: Pos(line, adjustPos(orig, string, cutFrom, fold) + ch),
+                to: Pos(line + lines.length - 1, adjustPos(end, endString, lastLine.length, fold))}
+      }
+    }
+  }
+
+  function searchStringBackward(doc, query, start, caseFold) {
+    if (!query.length) return null
+    var fold = caseFold ? doFold : noFold
+    var lines = fold(query).split(/\r|\n\r?/)
+
+    search: for (var line = start.line, ch = start.ch, first = doc.firstLine() - 1 + lines.length; line >= first; line--, ch = -1) {
+      var orig = doc.getLine(line)
+      if (ch > -1) orig = orig.slice(0, ch)
+      var string = fold(orig)
+      if (lines.length == 1) {
+        var found = string.lastIndexOf(lines[0])
+        if (found == -1) continue search
+        return {from: Pos(line, adjustPos(orig, string, found, fold)),
+                to: Pos(line, adjustPos(orig, string, found + lines[0].length, fold))}
+      } else {
+        var lastLine = lines[lines.length - 1]
+        if (string.slice(0, lastLine.length) != lastLine) continue search
+        for (var i = 1, start = line - lines.length + 1; i < lines.length - 1; i++)
+          if (fold(doc.getLine(start + i)) != lines[i]) continue search
+        var top = doc.getLine(line + 1 - lines.length), topString = fold(top)
+        if (topString.slice(topString.length - lines[0].length) != lines[0]) continue search
+        return {from: Pos(line + 1 - lines.length, adjustPos(top, topString, top.length - lines[0].length, fold)),
+                to: Pos(line, adjustPos(orig, string, lastLine.length, fold))}
+      }
+    }
+  }
+
+  function SearchCursor(doc, query, pos, options) {
+    this.atOccurrence = false
+    this.doc = doc
+    pos = pos ? doc.clipPos(pos) : Pos(0, 0)
+    this.pos = {from: pos, to: pos}
+
+    var caseFold
+    if (typeof options == "object") {
+      caseFold = options.caseFold
+    } else { // Backwards compat for when caseFold was the 4th argument
+      caseFold = options
+      options = null
+    }
+
+    if (typeof query == "string") {
+      if (caseFold == null) caseFold = false
+      this.matches = function(reverse, pos) {
+        return (reverse ? searchStringBackward : searchStringForward)(doc, query, pos, caseFold)
+      }
+    } else {
+      query = ensureFlags(query, "gm")
+      if (!options || options.multiline !== false)
+        this.matches = function(reverse, pos) {
+          return (reverse ? searchRegexpBackwardMultiline : searchRegexpForwardMultiline)(doc, query, pos)
+        }
+      else
+        this.matches = function(reverse, pos) {
+          return (reverse ? searchRegexpBackward : searchRegexpForward)(doc, query, pos)
+        }
+    }
+  }
+
+  SearchCursor.prototype = {
+    findNext: function() {return this.find(false)},
+    findPrevious: function() {return this.find(true)},
+
+    find: function(reverse) {
+      var result = this.matches(reverse, this.doc.clipPos(reverse ? this.pos.from : this.pos.to))
+
+      // Implements weird auto-growing behavior on null-matches for
+      // backwards-compatiblity with the vim code (unfortunately)
+      while (result && CodeMirror.cmpPos(result.from, result.to) == 0) {
+        if (reverse) {
+          if (result.from.ch) result.from = Pos(result.from.line, result.from.ch - 1)
+          else if (result.from.line == this.doc.firstLine()) result = null
+          else result = this.matches(reverse, this.doc.clipPos(Pos(result.from.line - 1)))
+        } else {
+          if (result.to.ch < this.doc.getLine(result.to.line).length) result.to = Pos(result.to.line, result.to.ch + 1)
+          else if (result.to.line == this.doc.lastLine()) result = null
+          else result = this.matches(reverse, Pos(result.to.line + 1, 0))
+        }
+      }
+
+      if (result) {
+        this.pos = result
+        this.atOccurrence = true
+        return this.pos.match || true
+      } else {
+        var end = Pos(reverse ? this.doc.firstLine() : this.doc.lastLine() + 1, 0)
+        this.pos = {from: end, to: end}
+        return this.atOccurrence = false
+      }
+    },
+
+    from: function() {if (this.atOccurrence) return this.pos.from},
+    to: function() {if (this.atOccurrence) return this.pos.to},
+
+    replace: function(newText, origin) {
+      if (!this.atOccurrence) return
+      var lines = CodeMirror.splitLines(newText)
+      this.doc.replaceRange(lines, this.pos.from, this.pos.to, origin)
+      this.pos.to = Pos(this.pos.from.line + lines.length - 1,
+                        lines[lines.length - 1].length + (lines.length == 1 ? this.pos.from.ch : 0))
+    }
+  }
+
+  CodeMirror.defineExtension("getSearchCursor", function(query, pos, caseFold) {
+    return new SearchCursor(this.doc, query, pos, caseFold)
+  })
+  CodeMirror.defineDocExtension("getSearchCursor", function(query, pos, caseFold) {
+    return new SearchCursor(this, query, pos, caseFold)
+  })
+
+  CodeMirror.defineExtension("selectMatches", function(query, caseFold) {
+    var ranges = []
+    var cur = this.getSearchCursor(query, this.getCursor("from"), caseFold)
+    while (cur.findNext()) {
+      if (CodeMirror.cmpPos(cur.to(), this.getCursor("to")) > 0) break
+      ranges.push({anchor: cur.from(), head: cur.to()})
+    }
+    if (ranges.length)
+      this.setSelections(ranges, 0)
+  })
+});
diff --git a/public/vendor/plugins/codemirror/addon/selection/active-line.js b/public/vendor/plugins/codemirror/addon/selection/active-line.js
new file mode 100644
index 0000000000..c7b14ce07f
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/selection/active-line.js
@@ -0,0 +1,72 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+  var WRAP_CLASS = "CodeMirror-activeline";
+  var BACK_CLASS = "CodeMirror-activeline-background";
+  var GUTT_CLASS = "CodeMirror-activeline-gutter";
+
+  CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) {
+    var prev = old == CodeMirror.Init ? false : old;
+    if (val == prev) return
+    if (prev) {
+      cm.off("beforeSelectionChange", selectionChange);
+      clearActiveLines(cm);
+      delete cm.state.activeLines;
+    }
+    if (val) {
+      cm.state.activeLines = [];
+      updateActiveLines(cm, cm.listSelections());
+      cm.on("beforeSelectionChange", selectionChange);
+    }
+  });
+
+  function clearActiveLines(cm) {
+    for (var i = 0; i < cm.state.activeLines.length; i++) {
+      cm.removeLineClass(cm.state.activeLines[i], "wrap", WRAP_CLASS);
+      cm.removeLineClass(cm.state.activeLines[i], "background", BACK_CLASS);
+      cm.removeLineClass(cm.state.activeLines[i], "gutter", GUTT_CLASS);
+    }
+  }
+
+  function sameArray(a, b) {
+    if (a.length != b.length) return false;
+    for (var i = 0; i < a.length; i++)
+      if (a[i] != b[i]) return false;
+    return true;
+  }
+
+  function updateActiveLines(cm, ranges) {
+    var active = [];
+    for (var i = 0; i < ranges.length; i++) {
+      var range = ranges[i];
+      var option = cm.getOption("styleActiveLine");
+      if (typeof option == "object" && option.nonEmpty ? range.anchor.line != range.head.line : !range.empty())
+        continue
+      var line = cm.getLineHandleVisualStart(range.head.line);
+      if (active[active.length - 1] != line) active.push(line);
+    }
+    if (sameArray(cm.state.activeLines, active)) return;
+    cm.operation(function() {
+      clearActiveLines(cm);
+      for (var i = 0; i < active.length; i++) {
+        cm.addLineClass(active[i], "wrap", WRAP_CLASS);
+        cm.addLineClass(active[i], "background", BACK_CLASS);
+        cm.addLineClass(active[i], "gutter", GUTT_CLASS);
+      }
+      cm.state.activeLines = active;
+    });
+  }
+
+  function selectionChange(cm, sel) {
+    updateActiveLines(cm, sel.ranges);
+  }
+});
diff --git a/public/vendor/plugins/codemirror/addon/selection/mark-selection.js b/public/vendor/plugins/codemirror/addon/selection/mark-selection.js
new file mode 100644
index 0000000000..adfaa62d1a
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/selection/mark-selection.js
@@ -0,0 +1,119 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// Because sometimes you need to mark the selected *text*.
+//
+// Adds an option 'styleSelectedText' which, when enabled, gives
+// selected text the CSS class given as option value, or
+// "CodeMirror-selectedtext" when the value is not a string.
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  CodeMirror.defineOption("styleSelectedText", false, function(cm, val, old) {
+    var prev = old && old != CodeMirror.Init;
+    if (val && !prev) {
+      cm.state.markedSelection = [];
+      cm.state.markedSelectionStyle = typeof val == "string" ? val : "CodeMirror-selectedtext";
+      reset(cm);
+      cm.on("cursorActivity", onCursorActivity);
+      cm.on("change", onChange);
+    } else if (!val && prev) {
+      cm.off("cursorActivity", onCursorActivity);
+      cm.off("change", onChange);
+      clear(cm);
+      cm.state.markedSelection = cm.state.markedSelectionStyle = null;
+    }
+  });
+
+  function onCursorActivity(cm) {
+    if (cm.state.markedSelection)
+      cm.operation(function() { update(cm); });
+  }
+
+  function onChange(cm) {
+    if (cm.state.markedSelection && cm.state.markedSelection.length)
+      cm.operation(function() { clear(cm); });
+  }
+
+  var CHUNK_SIZE = 8;
+  var Pos = CodeMirror.Pos;
+  var cmp = CodeMirror.cmpPos;
+
+  function coverRange(cm, from, to, addAt) {
+    if (cmp(from, to) == 0) return;
+    var array = cm.state.markedSelection;
+    var cls = cm.state.markedSelectionStyle;
+    for (var line = from.line;;) {
+      var start = line == from.line ? from : Pos(line, 0);
+      var endLine = line + CHUNK_SIZE, atEnd = endLine >= to.line;
+      var end = atEnd ? to : Pos(endLine, 0);
+      var mark = cm.markText(start, end, {className: cls});
+      if (addAt == null) array.push(mark);
+      else array.splice(addAt++, 0, mark);
+      if (atEnd) break;
+      line = endLine;
+    }
+  }
+
+  function clear(cm) {
+    var array = cm.state.markedSelection;
+    for (var i = 0; i < array.length; ++i) array[i].clear();
+    array.length = 0;
+  }
+
+  function reset(cm) {
+    clear(cm);
+    var ranges = cm.listSelections();
+    for (var i = 0; i < ranges.length; i++)
+      coverRange(cm, ranges[i].from(), ranges[i].to());
+  }
+
+  function update(cm) {
+    if (!cm.somethingSelected()) return clear(cm);
+    if (cm.listSelections().length > 1) return reset(cm);
+
+    var from = cm.getCursor("start"), to = cm.getCursor("end");
+
+    var array = cm.state.markedSelection;
+    if (!array.length) return coverRange(cm, from, to);
+
+    var coverStart = array[0].find(), coverEnd = array[array.length - 1].find();
+    if (!coverStart || !coverEnd || to.line - from.line <= CHUNK_SIZE ||
+        cmp(from, coverEnd.to) >= 0 || cmp(to, coverStart.from) <= 0)
+      return reset(cm);
+
+    while (cmp(from, coverStart.from) > 0) {
+      array.shift().clear();
+      coverStart = array[0].find();
+    }
+    if (cmp(from, coverStart.from) < 0) {
+      if (coverStart.to.line - from.line < CHUNK_SIZE) {
+        array.shift().clear();
+        coverRange(cm, from, coverStart.to, 0);
+      } else {
+        coverRange(cm, from, coverStart.from, 0);
+      }
+    }
+
+    while (cmp(to, coverEnd.to) < 0) {
+      array.pop().clear();
+      coverEnd = array[array.length - 1].find();
+    }
+    if (cmp(to, coverEnd.to) > 0) {
+      if (to.line - coverEnd.from.line < CHUNK_SIZE) {
+        array.pop().clear();
+        coverRange(cm, coverEnd.from, to);
+      } else {
+        coverRange(cm, coverEnd.to, to);
+      }
+    }
+  }
+});
diff --git a/public/vendor/plugins/codemirror/addon/selection/selection-pointer.js b/public/vendor/plugins/codemirror/addon/selection/selection-pointer.js
new file mode 100644
index 0000000000..f0bd61a33e
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/selection/selection-pointer.js
@@ -0,0 +1,98 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  CodeMirror.defineOption("selectionPointer", false, function(cm, val) {
+    var data = cm.state.selectionPointer;
+    if (data) {
+      CodeMirror.off(cm.getWrapperElement(), "mousemove", data.mousemove);
+      CodeMirror.off(cm.getWrapperElement(), "mouseout", data.mouseout);
+      CodeMirror.off(window, "scroll", data.windowScroll);
+      cm.off("cursorActivity", reset);
+      cm.off("scroll", reset);
+      cm.state.selectionPointer = null;
+      cm.display.lineDiv.style.cursor = "";
+    }
+    if (val) {
+      data = cm.state.selectionPointer = {
+        value: typeof val == "string" ? val : "default",
+        mousemove: function(event) { mousemove(cm, event); },
+        mouseout: function(event) { mouseout(cm, event); },
+        windowScroll: function() { reset(cm); },
+        rects: null,
+        mouseX: null, mouseY: null,
+        willUpdate: false
+      };
+      CodeMirror.on(cm.getWrapperElement(), "mousemove", data.mousemove);
+      CodeMirror.on(cm.getWrapperElement(), "mouseout", data.mouseout);
+      CodeMirror.on(window, "scroll", data.windowScroll);
+      cm.on("cursorActivity", reset);
+      cm.on("scroll", reset);
+    }
+  });
+
+  function mousemove(cm, event) {
+    var data = cm.state.selectionPointer;
+    if (event.buttons == null ? event.which : event.buttons) {
+      data.mouseX = data.mouseY = null;
+    } else {
+      data.mouseX = event.clientX;
+      data.mouseY = event.clientY;
+    }
+    scheduleUpdate(cm);
+  }
+
+  function mouseout(cm, event) {
+    if (!cm.getWrapperElement().contains(event.relatedTarget)) {
+      var data = cm.state.selectionPointer;
+      data.mouseX = data.mouseY = null;
+      scheduleUpdate(cm);
+    }
+  }
+
+  function reset(cm) {
+    cm.state.selectionPointer.rects = null;
+    scheduleUpdate(cm);
+  }
+
+  function scheduleUpdate(cm) {
+    if (!cm.state.selectionPointer.willUpdate) {
+      cm.state.selectionPointer.willUpdate = true;
+      setTimeout(function() {
+        update(cm);
+        cm.state.selectionPointer.willUpdate = false;
+      }, 50);
+    }
+  }
+
+  function update(cm) {
+    var data = cm.state.selectionPointer;
+    if (!data) return;
+    if (data.rects == null && data.mouseX != null) {
+      data.rects = [];
+      if (cm.somethingSelected()) {
+        for (var sel = cm.display.selectionDiv.firstChild; sel; sel = sel.nextSibling)
+          data.rects.push(sel.getBoundingClientRect());
+      }
+    }
+    var inside = false;
+    if (data.mouseX != null) for (var i = 0; i < data.rects.length; i++) {
+      var rect = data.rects[i];
+      if (rect.left <= data.mouseX && rect.right >= data.mouseX &&
+          rect.top <= data.mouseY && rect.bottom >= data.mouseY)
+        inside = true;
+    }
+    var cursor = inside ? data.value : "";
+    if (cm.display.lineDiv.style.cursor != cursor)
+      cm.display.lineDiv.style.cursor = cursor;
+  }
+});
diff --git a/public/vendor/plugins/codemirror/addon/tern/tern.css b/public/vendor/plugins/codemirror/addon/tern/tern.css
new file mode 100644
index 0000000000..c4b8a2f77e
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/tern/tern.css
@@ -0,0 +1,87 @@
+.CodeMirror-Tern-completion {
+  padding-left: 22px;
+  position: relative;
+  line-height: 1.5;
+}
+.CodeMirror-Tern-completion:before {
+  position: absolute;
+  left: 2px;
+  bottom: 2px;
+  border-radius: 50%;
+  font-size: 12px;
+  font-weight: bold;
+  height: 15px;
+  width: 15px;
+  line-height: 16px;
+  text-align: center;
+  color: white;
+  -moz-box-sizing: border-box;
+  box-sizing: border-box;
+}
+.CodeMirror-Tern-completion-unknown:before {
+  content: "?";
+  background: #4bb;
+}
+.CodeMirror-Tern-completion-object:before {
+  content: "O";
+  background: #77c;
+}
+.CodeMirror-Tern-completion-fn:before {
+  content: "F";
+  background: #7c7;
+}
+.CodeMirror-Tern-completion-array:before {
+  content: "A";
+  background: #c66;
+}
+.CodeMirror-Tern-completion-number:before {
+  content: "1";
+  background: #999;
+}
+.CodeMirror-Tern-completion-string:before {
+  content: "S";
+  background: #999;
+}
+.CodeMirror-Tern-completion-bool:before {
+  content: "B";
+  background: #999;
+}
+
+.CodeMirror-Tern-completion-guess {
+  color: #999;
+}
+
+.CodeMirror-Tern-tooltip {
+  border: 1px solid silver;
+  border-radius: 3px;
+  color: #444;
+  padding: 2px 5px;
+  font-size: 90%;
+  font-family: monospace;
+  background-color: white;
+  white-space: pre-wrap;
+
+  max-width: 40em;
+  position: absolute;
+  z-index: 10;
+  -webkit-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+  -moz-box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+  box-shadow: 2px 3px 5px rgba(0,0,0,.2);
+
+  transition: opacity 1s;
+  -moz-transition: opacity 1s;
+  -webkit-transition: opacity 1s;
+  -o-transition: opacity 1s;
+  -ms-transition: opacity 1s;
+}
+
+.CodeMirror-Tern-hint-doc {
+  max-width: 25em;
+  margin-top: -3px;
+}
+
+.CodeMirror-Tern-fname { color: black; }
+.CodeMirror-Tern-farg { color: #70a; }
+.CodeMirror-Tern-farg-current { text-decoration: underline; }
+.CodeMirror-Tern-type { color: #07c; }
+.CodeMirror-Tern-fhint-guess { opacity: .7; }
diff --git a/public/vendor/plugins/codemirror/addon/tern/tern.js b/public/vendor/plugins/codemirror/addon/tern/tern.js
new file mode 100644
index 0000000000..253309d678
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/tern/tern.js
@@ -0,0 +1,718 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// Glue code between CodeMirror and Tern.
+//
+// Create a CodeMirror.TernServer to wrap an actual Tern server,
+// register open documents (CodeMirror.Doc instances) with it, and
+// call its methods to activate the assisting functions that Tern
+// provides.
+//
+// Options supported (all optional):
+// * defs: An array of JSON definition data structures.
+// * plugins: An object mapping plugin names to configuration
+//   options.
+// * getFile: A function(name, c) that can be used to access files in
+//   the project that haven't been loaded yet. Simply do c(null) to
+//   indicate that a file is not available.
+// * fileFilter: A function(value, docName, doc) that will be applied
+//   to documents before passing them on to Tern.
+// * switchToDoc: A function(name, doc) that should, when providing a
+//   multi-file view, switch the view or focus to the named file.
+// * showError: A function(editor, message) that can be used to
+//   override the way errors are displayed.
+// * completionTip: Customize the content in tooltips for completions.
+//   Is passed a single argument—the completion's data as returned by
+//   Tern—and may return a string, DOM node, or null to indicate that
+//   no tip should be shown. By default the docstring is shown.
+// * typeTip: Like completionTip, but for the tooltips shown for type
+//   queries.
+// * responseFilter: A function(doc, query, request, error, data) that
+//   will be applied to the Tern responses before treating them
+//
+//
+// It is possible to run the Tern server in a web worker by specifying
+// these additional options:
+// * useWorker: Set to true to enable web worker mode. You'll probably
+//   want to feature detect the actual value you use here, for example
+//   !!window.Worker.
+// * workerScript: The main script of the worker. Point this to
+//   wherever you are hosting worker.js from this directory.
+// * workerDeps: An array of paths pointing (relative to workerScript)
+//   to the Acorn and Tern libraries and any Tern plugins you want to
+//   load. Or, if you minified those into a single script and included
+//   them in the workerScript, simply leave this undefined.
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+  // declare global: tern
+
+  CodeMirror.TernServer = function(options) {
+    var self = this;
+    this.options = options || {};
+    var plugins = this.options.plugins || (this.options.plugins = {});
+    if (!plugins.doc_comment) plugins.doc_comment = true;
+    this.docs = Object.create(null);
+    if (this.options.useWorker) {
+      this.server = new WorkerServer(this);
+    } else {
+      this.server = new tern.Server({
+        getFile: function(name, c) { return getFile(self, name, c); },
+        async: true,
+        defs: this.options.defs || [],
+        plugins: plugins
+      });
+    }
+    this.trackChange = function(doc, change) { trackChange(self, doc, change); };
+
+    this.cachedArgHints = null;
+    this.activeArgHints = null;
+    this.jumpStack = [];
+
+    this.getHint = function(cm, c) { return hint(self, cm, c); };
+    this.getHint.async = true;
+  };
+
+  CodeMirror.TernServer.prototype = {
+    addDoc: function(name, doc) {
+      var data = {doc: doc, name: name, changed: null};
+      this.server.addFile(name, docValue(this, data));
+      CodeMirror.on(doc, "change", this.trackChange);
+      return this.docs[name] = data;
+    },
+
+    delDoc: function(id) {
+      var found = resolveDoc(this, id);
+      if (!found) return;
+      CodeMirror.off(found.doc, "change", this.trackChange);
+      delete this.docs[found.name];
+      this.server.delFile(found.name);
+    },
+
+    hideDoc: function(id) {
+      closeArgHints(this);
+      var found = resolveDoc(this, id);
+      if (found && found.changed) sendDoc(this, found);
+    },
+
+    complete: function(cm) {
+      cm.showHint({hint: this.getHint});
+    },
+
+    showType: function(cm, pos, c) { showContextInfo(this, cm, pos, "type", c); },
+
+    showDocs: function(cm, pos, c) { showContextInfo(this, cm, pos, "documentation", c); },
+
+    updateArgHints: function(cm) { updateArgHints(this, cm); },
+
+    jumpToDef: function(cm) { jumpToDef(this, cm); },
+
+    jumpBack: function(cm) { jumpBack(this, cm); },
+
+    rename: function(cm) { rename(this, cm); },
+
+    selectName: function(cm) { selectName(this, cm); },
+
+    request: function (cm, query, c, pos) {
+      var self = this;
+      var doc = findDoc(this, cm.getDoc());
+      var request = buildRequest(this, doc, query, pos);
+      var extraOptions = request.query && this.options.queryOptions && this.options.queryOptions[request.query.type]
+      if (extraOptions) for (var prop in extraOptions) request.query[prop] = extraOptions[prop];
+
+      this.server.request(request, function (error, data) {
+        if (!error && self.options.responseFilter)
+          data = self.options.responseFilter(doc, query, request, error, data);
+        c(error, data);
+      });
+    },
+
+    destroy: function () {
+      closeArgHints(this)
+      if (this.worker) {
+        this.worker.terminate();
+        this.worker = null;
+      }
+    }
+  };
+
+  var Pos = CodeMirror.Pos;
+  var cls = "CodeMirror-Tern-";
+  var bigDoc = 250;
+
+  function getFile(ts, name, c) {
+    var buf = ts.docs[name];
+    if (buf)
+      c(docValue(ts, buf));
+    else if (ts.options.getFile)
+      ts.options.getFile(name, c);
+    else
+      c(null);
+  }
+
+  function findDoc(ts, doc, name) {
+    for (var n in ts.docs) {
+      var cur = ts.docs[n];
+      if (cur.doc == doc) return cur;
+    }
+    if (!name) for (var i = 0;; ++i) {
+      n = "[doc" + (i || "") + "]";
+      if (!ts.docs[n]) { name = n; break; }
+    }
+    return ts.addDoc(name, doc);
+  }
+
+  function resolveDoc(ts, id) {
+    if (typeof id == "string") return ts.docs[id];
+    if (id instanceof CodeMirror) id = id.getDoc();
+    if (id instanceof CodeMirror.Doc) return findDoc(ts, id);
+  }
+
+  function trackChange(ts, doc, change) {
+    var data = findDoc(ts, doc);
+
+    var argHints = ts.cachedArgHints;
+    if (argHints && argHints.doc == doc && cmpPos(argHints.start, change.to) >= 0)
+      ts.cachedArgHints = null;
+
+    var changed = data.changed;
+    if (changed == null)
+      data.changed = changed = {from: change.from.line, to: change.from.line};
+    var end = change.from.line + (change.text.length - 1);
+    if (change.from.line < changed.to) changed.to = changed.to - (change.to.line - end);
+    if (end >= changed.to) changed.to = end + 1;
+    if (changed.from > change.from.line) changed.from = change.from.line;
+
+    if (doc.lineCount() > bigDoc && change.to - changed.from > 100) setTimeout(function() {
+      if (data.changed && data.changed.to - data.changed.from > 100) sendDoc(ts, data);
+    }, 200);
+  }
+
+  function sendDoc(ts, doc) {
+    ts.server.request({files: [{type: "full", name: doc.name, text: docValue(ts, doc)}]}, function(error) {
+      if (error) window.console.error(error);
+      else doc.changed = null;
+    });
+  }
+
+  // Completion
+
+  function hint(ts, cm, c) {
+    ts.request(cm, {type: "completions", types: true, docs: true, urls: true}, function(error, data) {
+      if (error) return showError(ts, cm, error);
+      var completions = [], after = "";
+      var from = data.start, to = data.end;
+      if (cm.getRange(Pos(from.line, from.ch - 2), from) == "[\"" &&
+          cm.getRange(to, Pos(to.line, to.ch + 2)) != "\"]")
+        after = "\"]";
+
+      for (var i = 0; i < data.completions.length; ++i) {
+        var completion = data.completions[i], className = typeToIcon(completion.type);
+        if (data.guess) className += " " + cls + "guess";
+        completions.push({text: completion.name + after,
+                          displayText: completion.displayName || completion.name,
+                          className: className,
+                          data: completion});
+      }
+
+      var obj = {from: from, to: to, list: completions};
+      var tooltip = null;
+      CodeMirror.on(obj, "close", function() { remove(tooltip); });
+      CodeMirror.on(obj, "update", function() { remove(tooltip); });
+      CodeMirror.on(obj, "select", function(cur, node) {
+        remove(tooltip);
+        var content = ts.options.completionTip ? ts.options.completionTip(cur.data) : cur.data.doc;
+        if (content) {
+          tooltip = makeTooltip(node.parentNode.getBoundingClientRect().right + window.pageXOffset,
+                                node.getBoundingClientRect().top + window.pageYOffset, content);
+          tooltip.className += " " + cls + "hint-doc";
+        }
+      });
+      c(obj);
+    });
+  }
+
+  function typeToIcon(type) {
+    var suffix;
+    if (type == "?") suffix = "unknown";
+    else if (type == "number" || type == "string" || type == "bool") suffix = type;
+    else if (/^fn\(/.test(type)) suffix = "fn";
+    else if (/^\[/.test(type)) suffix = "array";
+    else suffix = "object";
+    return cls + "completion " + cls + "completion-" + suffix;
+  }
+
+  // Type queries
+
+  function showContextInfo(ts, cm, pos, queryName, c) {
+    ts.request(cm, queryName, function(error, data) {
+      if (error) return showError(ts, cm, error);
+      if (ts.options.typeTip) {
+        var tip = ts.options.typeTip(data);
+      } else {
+        var tip = elt("span", null, elt("strong", null, data.type || "not found"));
+        if (data.doc)
+          tip.appendChild(document.createTextNode(" — " + data.doc));
+        if (data.url) {
+          tip.appendChild(document.createTextNode(" "));
+          var child = tip.appendChild(elt("a", null, "[docs]"));
+          child.href = data.url;
+          child.target = "_blank";
+        }
+      }
+      tempTooltip(cm, tip, ts);
+      if (c) c();
+    }, pos);
+  }
+
+  // Maintaining argument hints
+
+  function updateArgHints(ts, cm) {
+    closeArgHints(ts);
+
+    if (cm.somethingSelected()) return;
+    var state = cm.getTokenAt(cm.getCursor()).state;
+    var inner = CodeMirror.innerMode(cm.getMode(), state);
+    if (inner.mode.name != "javascript") return;
+    var lex = inner.state.lexical;
+    if (lex.info != "call") return;
+
+    var ch, argPos = lex.pos || 0, tabSize = cm.getOption("tabSize");
+    for (var line = cm.getCursor().line, e = Math.max(0, line - 9), found = false; line >= e; --line) {
+      var str = cm.getLine(line), extra = 0;
+      for (var pos = 0;;) {
+        var tab = str.indexOf("\t", pos);
+        if (tab == -1) break;
+        extra += tabSize - (tab + extra) % tabSize - 1;
+        pos = tab + 1;
+      }
+      ch = lex.column - extra;
+      if (str.charAt(ch) == "(") {found = true; break;}
+    }
+    if (!found) return;
+
+    var start = Pos(line, ch);
+    var cache = ts.cachedArgHints;
+    if (cache && cache.doc == cm.getDoc() && cmpPos(start, cache.start) == 0)
+      return showArgHints(ts, cm, argPos);
+
+    ts.request(cm, {type: "type", preferFunction: true, end: start}, function(error, data) {
+      if (error || !data.type || !(/^fn\(/).test(data.type)) return;
+      ts.cachedArgHints = {
+        start: start,
+        type: parseFnType(data.type),
+        name: data.exprName || data.name || "fn",
+        guess: data.guess,
+        doc: cm.getDoc()
+      };
+      showArgHints(ts, cm, argPos);
+    });
+  }
+
+  function showArgHints(ts, cm, pos) {
+    closeArgHints(ts);
+
+    var cache = ts.cachedArgHints, tp = cache.type;
+    var tip = elt("span", cache.guess ? cls + "fhint-guess" : null,
+                  elt("span", cls + "fname", cache.name), "(");
+    for (var i = 0; i < tp.args.length; ++i) {
+      if (i) tip.appendChild(document.createTextNode(", "));
+      var arg = tp.args[i];
+      tip.appendChild(elt("span", cls + "farg" + (i == pos ? " " + cls + "farg-current" : ""), arg.name || "?"));
+      if (arg.type != "?") {
+        tip.appendChild(document.createTextNode(":\u00a0"));
+        tip.appendChild(elt("span", cls + "type", arg.type));
+      }
+    }
+    tip.appendChild(document.createTextNode(tp.rettype ? ") ->\u00a0" : ")"));
+    if (tp.rettype) tip.appendChild(elt("span", cls + "type", tp.rettype));
+    var place = cm.cursorCoords(null, "page");
+    var tooltip = ts.activeArgHints = makeTooltip(place.right + 1, place.bottom, tip)
+    setTimeout(function() {
+      tooltip.clear = onEditorActivity(cm, function() {
+        if (ts.activeArgHints == tooltip) closeArgHints(ts) })
+    }, 20)
+  }
+
+  function parseFnType(text) {
+    var args = [], pos = 3;
+
+    function skipMatching(upto) {
+      var depth = 0, start = pos;
+      for (;;) {
+        var next = text.charAt(pos);
+        if (upto.test(next) && !depth) return text.slice(start, pos);
+        if (/[{\[\(]/.test(next)) ++depth;
+        else if (/[}\]\)]/.test(next)) --depth;
+        ++pos;
+      }
+    }
+
+    // Parse arguments
+    if (text.charAt(pos) != ")") for (;;) {
+      var name = text.slice(pos).match(/^([^, \(\[\{]+): /);
+      if (name) {
+        pos += name[0].length;
+        name = name[1];
+      }
+      args.push({name: name, type: skipMatching(/[\),]/)});
+      if (text.charAt(pos) == ")") break;
+      pos += 2;
+    }
+
+    var rettype = text.slice(pos).match(/^\) -> (.*)$/);
+
+    return {args: args, rettype: rettype && rettype[1]};
+  }
+
+  // Moving to the definition of something
+
+  function jumpToDef(ts, cm) {
+    function inner(varName) {
+      var req = {type: "definition", variable: varName || null};
+      var doc = findDoc(ts, cm.getDoc());
+      ts.server.request(buildRequest(ts, doc, req), function(error, data) {
+        if (error) return showError(ts, cm, error);
+        if (!data.file && data.url) { window.open(data.url); return; }
+
+        if (data.file) {
+          var localDoc = ts.docs[data.file], found;
+          if (localDoc && (found = findContext(localDoc.doc, data))) {
+            ts.jumpStack.push({file: doc.name,
+                               start: cm.getCursor("from"),
+                               end: cm.getCursor("to")});
+            moveTo(ts, doc, localDoc, found.start, found.end);
+            return;
+          }
+        }
+        showError(ts, cm, "Could not find a definition.");
+      });
+    }
+
+    if (!atInterestingExpression(cm))
+      dialog(cm, "Jump to variable", function(name) { if (name) inner(name); });
+    else
+      inner();
+  }
+
+  function jumpBack(ts, cm) {
+    var pos = ts.jumpStack.pop(), doc = pos && ts.docs[pos.file];
+    if (!doc) return;
+    moveTo(ts, findDoc(ts, cm.getDoc()), doc, pos.start, pos.end);
+  }
+
+  function moveTo(ts, curDoc, doc, start, end) {
+    doc.doc.setSelection(start, end);
+    if (curDoc != doc && ts.options.switchToDoc) {
+      closeArgHints(ts);
+      ts.options.switchToDoc(doc.name, doc.doc);
+    }
+  }
+
+  // The {line,ch} representation of positions makes this rather awkward.
+  function findContext(doc, data) {
+    var before = data.context.slice(0, data.contextOffset).split("\n");
+    var startLine = data.start.line - (before.length - 1);
+    var start = Pos(startLine, (before.length == 1 ? data.start.ch : doc.getLine(startLine).length) - before[0].length);
+
+    var text = doc.getLine(startLine).slice(start.ch);
+    for (var cur = startLine + 1; cur < doc.lineCount() && text.length < data.context.length; ++cur)
+      text += "\n" + doc.getLine(cur);
+    if (text.slice(0, data.context.length) == data.context) return data;
+
+    var cursor = doc.getSearchCursor(data.context, 0, false);
+    var nearest, nearestDist = Infinity;
+    while (cursor.findNext()) {
+      var from = cursor.from(), dist = Math.abs(from.line - start.line) * 10000;
+      if (!dist) dist = Math.abs(from.ch - start.ch);
+      if (dist < nearestDist) { nearest = from; nearestDist = dist; }
+    }
+    if (!nearest) return null;
+
+    if (before.length == 1)
+      nearest.ch += before[0].length;
+    else
+      nearest = Pos(nearest.line + (before.length - 1), before[before.length - 1].length);
+    if (data.start.line == data.end.line)
+      var end = Pos(nearest.line, nearest.ch + (data.end.ch - data.start.ch));
+    else
+      var end = Pos(nearest.line + (data.end.line - data.start.line), data.end.ch);
+    return {start: nearest, end: end};
+  }
+
+  function atInterestingExpression(cm) {
+    var pos = cm.getCursor("end"), tok = cm.getTokenAt(pos);
+    if (tok.start < pos.ch && tok.type == "comment") return false;
+    return /[\w)\]]/.test(cm.getLine(pos.line).slice(Math.max(pos.ch - 1, 0), pos.ch + 1));
+  }
+
+  // Variable renaming
+
+  function rename(ts, cm) {
+    var token = cm.getTokenAt(cm.getCursor());
+    if (!/\w/.test(token.string)) return showError(ts, cm, "Not at a variable");
+    dialog(cm, "New name for " + token.string, function(newName) {
+      ts.request(cm, {type: "rename", newName: newName, fullDocs: true}, function(error, data) {
+        if (error) return showError(ts, cm, error);
+        applyChanges(ts, data.changes);
+      });
+    });
+  }
+
+  function selectName(ts, cm) {
+    var name = findDoc(ts, cm.doc).name;
+    ts.request(cm, {type: "refs"}, function(error, data) {
+      if (error) return showError(ts, cm, error);
+      var ranges = [], cur = 0;
+      var curPos = cm.getCursor();
+      for (var i = 0; i < data.refs.length; i++) {
+        var ref = data.refs[i];
+        if (ref.file == name) {
+          ranges.push({anchor: ref.start, head: ref.end});
+          if (cmpPos(curPos, ref.start) >= 0 && cmpPos(curPos, ref.end) <= 0)
+            cur = ranges.length - 1;
+        }
+      }
+      cm.setSelections(ranges, cur);
+    });
+  }
+
+  var nextChangeOrig = 0;
+  function applyChanges(ts, changes) {
+    var perFile = Object.create(null);
+    for (var i = 0; i < changes.length; ++i) {
+      var ch = changes[i];
+      (perFile[ch.file] || (perFile[ch.file] = [])).push(ch);
+    }
+    for (var file in perFile) {
+      var known = ts.docs[file], chs = perFile[file];;
+      if (!known) continue;
+      chs.sort(function(a, b) { return cmpPos(b.start, a.start); });
+      var origin = "*rename" + (++nextChangeOrig);
+      for (var i = 0; i < chs.length; ++i) {
+        var ch = chs[i];
+        known.doc.replaceRange(ch.text, ch.start, ch.end, origin);
+      }
+    }
+  }
+
+  // Generic request-building helper
+
+  function buildRequest(ts, doc, query, pos) {
+    var files = [], offsetLines = 0, allowFragments = !query.fullDocs;
+    if (!allowFragments) delete query.fullDocs;
+    if (typeof query == "string") query = {type: query};
+    query.lineCharPositions = true;
+    if (query.end == null) {
+      query.end = pos || doc.doc.getCursor("end");
+      if (doc.doc.somethingSelected())
+        query.start = doc.doc.getCursor("start");
+    }
+    var startPos = query.start || query.end;
+
+    if (doc.changed) {
+      if (doc.doc.lineCount() > bigDoc && allowFragments !== false &&
+          doc.changed.to - doc.changed.from < 100 &&
+          doc.changed.from <= startPos.line && doc.changed.to > query.end.line) {
+        files.push(getFragmentAround(doc, startPos, query.end));
+        query.file = "#0";
+        var offsetLines = files[0].offsetLines;
+        if (query.start != null) query.start = Pos(query.start.line - -offsetLines, query.start.ch);
+        query.end = Pos(query.end.line - offsetLines, query.end.ch);
+      } else {
+        files.push({type: "full",
+                    name: doc.name,
+                    text: docValue(ts, doc)});
+        query.file = doc.name;
+        doc.changed = null;
+      }
+    } else {
+      query.file = doc.name;
+    }
+    for (var name in ts.docs) {
+      var cur = ts.docs[name];
+      if (cur.changed && cur != doc) {
+        files.push({type: "full", name: cur.name, text: docValue(ts, cur)});
+        cur.changed = null;
+      }
+    }
+
+    return {query: query, files: files};
+  }
+
+  function getFragmentAround(data, start, end) {
+    var doc = data.doc;
+    var minIndent = null, minLine = null, endLine, tabSize = 4;
+    for (var p = start.line - 1, min = Math.max(0, p - 50); p >= min; --p) {
+      var line = doc.getLine(p), fn = line.search(/\bfunction\b/);
+      if (fn < 0) continue;
+      var indent = CodeMirror.countColumn(line, null, tabSize);
+      if (minIndent != null && minIndent <= indent) continue;
+      minIndent = indent;
+      minLine = p;
+    }
+    if (minLine == null) minLine = min;
+    var max = Math.min(doc.lastLine(), end.line + 20);
+    if (minIndent == null || minIndent == CodeMirror.countColumn(doc.getLine(start.line), null, tabSize))
+      endLine = max;
+    else for (endLine = end.line + 1; endLine < max; ++endLine) {
+      var indent = CodeMirror.countColumn(doc.getLine(endLine), null, tabSize);
+      if (indent <= minIndent) break;
+    }
+    var from = Pos(minLine, 0);
+
+    return {type: "part",
+            name: data.name,
+            offsetLines: from.line,
+            text: doc.getRange(from, Pos(endLine, end.line == endLine ? null : 0))};
+  }
+
+  // Generic utilities
+
+  var cmpPos = CodeMirror.cmpPos;
+
+  function elt(tagname, cls /*, ... elts*/) {
+    var e = document.createElement(tagname);
+    if (cls) e.className = cls;
+    for (var i = 2; i < arguments.length; ++i) {
+      var elt = arguments[i];
+      if (typeof elt == "string") elt = document.createTextNode(elt);
+      e.appendChild(elt);
+    }
+    return e;
+  }
+
+  function dialog(cm, text, f) {
+    if (cm.openDialog)
+      cm.openDialog(text + ": <input type=text>", f);
+    else
+      f(prompt(text, ""));
+  }
+
+  // Tooltips
+
+  function tempTooltip(cm, content, ts) {
+    if (cm.state.ternTooltip) remove(cm.state.ternTooltip);
+    var where = cm.cursorCoords();
+    var tip = cm.state.ternTooltip = makeTooltip(where.right + 1, where.bottom, content);
+    function maybeClear() {
+      old = true;
+      if (!mouseOnTip) clear();
+    }
+    function clear() {
+      cm.state.ternTooltip = null;
+      if (tip.parentNode) fadeOut(tip)
+      clearActivity()
+    }
+    var mouseOnTip = false, old = false;
+    CodeMirror.on(tip, "mousemove", function() { mouseOnTip = true; });
+    CodeMirror.on(tip, "mouseout", function(e) {
+      var related = e.relatedTarget || e.toElement
+      if (!related || !CodeMirror.contains(tip, related)) {
+        if (old) clear();
+        else mouseOnTip = false;
+      }
+    });
+    setTimeout(maybeClear, ts.options.hintDelay ? ts.options.hintDelay : 1700);
+    var clearActivity = onEditorActivity(cm, clear)
+  }
+
+  function onEditorActivity(cm, f) {
+    cm.on("cursorActivity", f)
+    cm.on("blur", f)
+    cm.on("scroll", f)
+    cm.on("setDoc", f)
+    return function() {
+      cm.off("cursorActivity", f)
+      cm.off("blur", f)
+      cm.off("scroll", f)
+      cm.off("setDoc", f)
+    }
+  }
+
+  function makeTooltip(x, y, content) {
+    var node = elt("div", cls + "tooltip", content);
+    node.style.left = x + "px";
+    node.style.top = y + "px";
+    document.body.appendChild(node);
+    return node;
+  }
+
+  function remove(node) {
+    var p = node && node.parentNode;
+    if (p) p.removeChild(node);
+  }
+
+  function fadeOut(tooltip) {
+    tooltip.style.opacity = "0";
+    setTimeout(function() { remove(tooltip); }, 1100);
+  }
+
+  function showError(ts, cm, msg) {
+    if (ts.options.showError)
+      ts.options.showError(cm, msg);
+    else
+      tempTooltip(cm, String(msg), ts);
+  }
+
+  function closeArgHints(ts) {
+    if (ts.activeArgHints) {
+      if (ts.activeArgHints.clear) ts.activeArgHints.clear()
+      remove(ts.activeArgHints)
+      ts.activeArgHints = null
+    }
+  }
+
+  function docValue(ts, doc) {
+    var val = doc.doc.getValue();
+    if (ts.options.fileFilter) val = ts.options.fileFilter(val, doc.name, doc.doc);
+    return val;
+  }
+
+  // Worker wrapper
+
+  function WorkerServer(ts) {
+    var worker = ts.worker = new Worker(ts.options.workerScript);
+    worker.postMessage({type: "init",
+                        defs: ts.options.defs,
+                        plugins: ts.options.plugins,
+                        scripts: ts.options.workerDeps});
+    var msgId = 0, pending = {};
+
+    function send(data, c) {
+      if (c) {
+        data.id = ++msgId;
+        pending[msgId] = c;
+      }
+      worker.postMessage(data);
+    }
+    worker.onmessage = function(e) {
+      var data = e.data;
+      if (data.type == "getFile") {
+        getFile(ts, data.name, function(err, text) {
+          send({type: "getFile", err: String(err), text: text, id: data.id});
+        });
+      } else if (data.type == "debug") {
+        window.console.log(data.message);
+      } else if (data.id && pending[data.id]) {
+        pending[data.id](data.err, data.body);
+        delete pending[data.id];
+      }
+    };
+    worker.onerror = function(e) {
+      for (var id in pending) pending[id](e);
+      pending = {};
+    };
+
+    this.addFile = function(name, text) { send({type: "add", name: name, text: text}); };
+    this.delFile = function(name) { send({type: "del", name: name}); };
+    this.request = function(body, c) { send({type: "req", body: body}, c); };
+  }
+});
diff --git a/public/vendor/plugins/codemirror/addon/tern/worker.js b/public/vendor/plugins/codemirror/addon/tern/worker.js
new file mode 100644
index 0000000000..e134ad47d6
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/tern/worker.js
@@ -0,0 +1,44 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+// declare global: tern, server
+
+var server;
+
+this.onmessage = function(e) {
+  var data = e.data;
+  switch (data.type) {
+  case "init": return startServer(data.defs, data.plugins, data.scripts);
+  case "add": return server.addFile(data.name, data.text);
+  case "del": return server.delFile(data.name);
+  case "req": return server.request(data.body, function(err, reqData) {
+    postMessage({id: data.id, body: reqData, err: err && String(err)});
+  });
+  case "getFile":
+    var c = pending[data.id];
+    delete pending[data.id];
+    return c(data.err, data.text);
+  default: throw new Error("Unknown message type: " + data.type);
+  }
+};
+
+var nextId = 0, pending = {};
+function getFile(file, c) {
+  postMessage({type: "getFile", name: file, id: ++nextId});
+  pending[nextId] = c;
+}
+
+function startServer(defs, plugins, scripts) {
+  if (scripts) importScripts.apply(null, scripts);
+
+  server = new tern.Server({
+    getFile: getFile,
+    async: true,
+    defs: defs,
+    plugins: plugins
+  });
+}
+
+this.console = {
+  log: function(v) { postMessage({type: "debug", message: v}); }
+};
diff --git a/public/vendor/plugins/codemirror/addon/wrap/hardwrap.js b/public/vendor/plugins/codemirror/addon/wrap/hardwrap.js
new file mode 100644
index 0000000000..29cc15f01f
--- /dev/null
+++ b/public/vendor/plugins/codemirror/addon/wrap/hardwrap.js
@@ -0,0 +1,145 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function(mod) {
+  if (typeof exports == "object" && typeof module == "object") // CommonJS
+    mod(require("../../lib/codemirror"));
+  else if (typeof define == "function" && define.amd) // AMD
+    define(["../../lib/codemirror"], mod);
+  else // Plain browser env
+    mod(CodeMirror);
+})(function(CodeMirror) {
+  "use strict";
+
+  var Pos = CodeMirror.Pos;
+
+  function findParagraph(cm, pos, options) {
+    var startRE = options.paragraphStart || cm.getHelper(pos, "paragraphStart");
+    for (var start = pos.line, first = cm.firstLine(); start > first; --start) {
+      var line = cm.getLine(start);
+      if (startRE && startRE.test(line)) break;
+      if (!/\S/.test(line)) { ++start; break; }
+    }
+    var endRE = options.paragraphEnd || cm.getHelper(pos, "paragraphEnd");
+    for (var end = pos.line + 1, last = cm.lastLine(); end <= last; ++end) {
+      var line = cm.getLine(end);
+      if (endRE && endRE.test(line)) { ++end; break; }
+      if (!/\S/.test(line)) break;
+    }
+    return {from: start, to: end};
+  }
+
+  function findBreakPoint(text, column, wrapOn, killTrailingSpace) {
+    var at = column
+    while (at < text.length && text.charAt(at) == " ") at++
+    for (; at > 0; --at)
+      if (wrapOn.test(text.slice(at - 1, at + 1))) break;
+    for (var first = true;; first = false) {
+      var endOfText = at;
+      if (killTrailingSpace)
+        while (text.charAt(endOfText - 1) == " ") --endOfText;
+      if (endOfText == 0 && first) at = column;
+      else return {from: endOfText, to: at};
+    }
+  }
+
+  function wrapRange(cm, from, to, options) {
+    from = cm.clipPos(from); to = cm.clipPos(to);
+    var column = options.column || 80;
+    var wrapOn = options.wrapOn || /\s\S|-[^\.\d]/;
+    var killTrailing = options.killTrailingSpace !== false;
+    var changes = [], curLine = "", curNo = from.line;
+    var lines = cm.getRange(from, to, false);
+    if (!lines.length) return null;
+    var leadingSpace = lines[0].match(/^[ \t]*/)[0];
+    if (leadingSpace.length >= column) column = leadingSpace.length + 1
+
+    for (var i = 0; i < lines.length; ++i) {
+      var text = lines[i], oldLen = curLine.length, spaceInserted = 0;
+      if (curLine && text && !wrapOn.test(curLine.charAt(curLine.length - 1) + text.charAt(0))) {
+        curLine += " ";
+        spaceInserted = 1;
+      }
+      var spaceTrimmed = "";
+      if (i) {
+        spaceTrimmed = text.match(/^\s*/)[0];
+        text = text.slice(spaceTrimmed.length);
+      }
+      curLine += text;
+      if (i) {
+        var firstBreak = curLine.length > column && leadingSpace == spaceTrimmed &&
+          findBreakPoint(curLine, column, wrapOn, killTrailing);
+        // If this isn't broken, or is broken at a different point, remove old break
+        if (!firstBreak || firstBreak.from != oldLen || firstBreak.to != oldLen + spaceInserted) {
+          changes.push({text: [spaceInserted ? " " : ""],
+                        from: Pos(curNo, oldLen),
+                        to: Pos(curNo + 1, spaceTrimmed.length)});
+        } else {
+          curLine = leadingSpace + text;
+          ++curNo;
+        }
+      }
+      while (curLine.length > column) {
+        var bp = findBreakPoint(curLine, column, wrapOn, killTrailing);
+        changes.push({text: ["", leadingSpace],
+                      from: Pos(curNo, bp.from),
+                      to: Pos(curNo, bp.to)});
+        curLine = leadingSpace + curLine.slice(bp.to);
+        ++curNo;
+      }
+    }
+    if (changes.length) cm.operation(function() {
+      for (var i = 0; i < changes.length; ++i) {
+        var change = changes[i];
+        if (change.text || CodeMirror.cmpPos(change.from, change.to))
+          cm.replaceRange(change.text, change.from, change.to);
+      }
+    });
+    return changes.length ? {from: changes[0].from, to: CodeMirror.changeEnd(changes[changes.length - 1])} : null;
+  }
+
+  CodeMirror.defineExtension("wrapParagraph", function(pos, options) {
+    options = options || {};
+    if (!pos) pos = this.getCursor();
+    var para = findParagraph(this, pos, options);
+    return wrapRange(this, Pos(para.from, 0), Pos(para.to - 1), options);
+  });
+
+  CodeMirror.commands.wrapLines = function(cm) {
+    cm.operation(function() {
+      var ranges = cm.listSelections(), at = cm.lastLine() + 1;
+      for (var i = ranges.length - 1; i >= 0; i--) {
+        var range = ranges[i], span;
+        if (range.empty()) {
+          var para = findParagraph(cm, range.head, {});
+          span = {from: Pos(para.from, 0), to: Pos(para.to - 1)};
+        } else {
+          span = {from: range.from(), to: range.to()};
+        }
+        if (span.to.line >= at) continue;
+        at = span.from.line;
+        wrapRange(cm, span.from, span.to, {});
+      }
+    });
+  };
+
+  CodeMirror.defineExtension("wrapRange", function(from, to, options) {
+    return wrapRange(this, from, to, options || {});
+  });
+
+  CodeMirror.defineExtension("wrapParagraphsInRange", function(from, to, options) {
+    options = options || {};
+    var cm = this, paras = [];
+    for (var line = from.line; line <= to.line;) {
+      var para = findParagraph(cm, Pos(line, 0), options);
+      paras.push(para);
+      line = para.to;
+    }
+    var madeChange = false;
+    if (paras.length) cm.operation(function() {
+      for (var i = paras.length - 1; i >= 0; --i)
+        madeChange = madeChange || wrapRange(cm, Pos(paras[i].from, 0), Pos(paras[i].to - 1), options);
+    });
+    return madeChange;
+  });
+});
diff --git a/public/vendor/plugins/codemirror/mode/apl/apl.js b/public/vendor/plugins/codemirror/mode/apl/apl.js
index caafe4e913..b1955f6c94 100644
--- a/public/vendor/plugins/codemirror/mode/apl/apl.js
+++ b/public/vendor/plugins/codemirror/mode/apl/apl.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/apl/index.html b/public/vendor/plugins/codemirror/mode/apl/index.html
index 53dda6b586..56ab02ffba 100644
--- a/public/vendor/plugins/codemirror/mode/apl/index.html
+++ b/public/vendor/plugins/codemirror/mode/apl/index.html
@@ -12,7 +12,7 @@
 	.CodeMirror { border: 2px inset #dee; }
     </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/asciiarmor/asciiarmor.js b/public/vendor/plugins/codemirror/mode/asciiarmor/asciiarmor.js
index d830903767..f560f42423 100644
--- a/public/vendor/plugins/codemirror/mode/asciiarmor/asciiarmor.js
+++ b/public/vendor/plugins/codemirror/mode/asciiarmor/asciiarmor.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -68,6 +68,7 @@
   });
 
   CodeMirror.defineMIME("application/pgp", "asciiarmor");
+  CodeMirror.defineMIME("application/pgp-encrypted", "asciiarmor");
   CodeMirror.defineMIME("application/pgp-keys", "asciiarmor");
   CodeMirror.defineMIME("application/pgp-signature", "asciiarmor");
 });
diff --git a/public/vendor/plugins/codemirror/mode/asciiarmor/index.html b/public/vendor/plugins/codemirror/mode/asciiarmor/index.html
index 8ba1b5c76c..de39a18573 100644
--- a/public/vendor/plugins/codemirror/mode/asciiarmor/index.html
+++ b/public/vendor/plugins/codemirror/mode/asciiarmor/index.html
@@ -9,7 +9,7 @@
 <script src="asciiarmor.js"></script>
 <style>.CodeMirror {background: #f8f8f8;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -41,6 +41,6 @@ var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
 </script>
 
 <p><strong>MIME types
-defined:</strong> <code>application/pgp</code>, <code>application/pgp-keys</code>, <code>application/pgp-signature</code></p>
+defined:</strong> <code>application/pgp</code>, <code>application/pgp-encrypted</code>, <code>application/pgp-keys</code>, <code>application/pgp-signature</code></p>
 
 </article>
diff --git a/public/vendor/plugins/codemirror/mode/asn.1/asn.1.js b/public/vendor/plugins/codemirror/mode/asn.1/asn.1.js
index 9600247ea6..d3ecb08781 100644
--- a/public/vendor/plugins/codemirror/mode/asn.1/asn.1.js
+++ b/public/vendor/plugins/codemirror/mode/asn.1/asn.1.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/asn.1/index.html b/public/vendor/plugins/codemirror/mode/asn.1/index.html
index 699fd4473d..c2bc824a07 100644
--- a/public/vendor/plugins/codemirror/mode/asn.1/index.html
+++ b/public/vendor/plugins/codemirror/mode/asn.1/index.html
@@ -6,15 +6,16 @@
 
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
+<script src="../../addon/edit/matchbrackets.js"></script>
 <script src="asn.1.js"></script>
-<style type="text/css">
+<style>
     .CodeMirror {
         border-top: 1px solid black;
         border-bottom: 1px solid black;
     }
 </style>
 <div id=nav>
-    <a href="http://codemirror.net"><h1>CodeMirror</h1>
+    <a href="https://codemirror.net"><h1>CodeMirror</h1>
         <img id=logo src="../../doc/logo.png">
     </a>
 
diff --git a/public/vendor/plugins/codemirror/mode/asterisk/asterisk.js b/public/vendor/plugins/codemirror/mode/asterisk/asterisk.js
index b7ebfc5ad7..49a727019a 100644
--- a/public/vendor/plugins/codemirror/mode/asterisk/asterisk.js
+++ b/public/vendor/plugins/codemirror/mode/asterisk/asterisk.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 /*
  * =====================================================================================
@@ -9,7 +9,7 @@
  *    Description:  CodeMirror mode for Asterisk dialplan
  *
  *        Created:  05/17/2012 09:20:25 PM
- *       Revision:  none
+ *       Revision:  08/05/2019 AstLinux Project: Support block-comments
  *
  *         Author:  Stas Kobzar (stas@modulis.ca),
  *        Company:  Modulis.ca Inc.
@@ -67,7 +67,26 @@ CodeMirror.defineMode("asterisk", function() {
     var cur = '';
     var ch = stream.next();
     // comment
+    if (state.blockComment) {
+      if (ch == "-" && stream.match("-;", true)) {
+        state.blockComment = false;
+      } else if (stream.skipTo("--;")) {
+        stream.next();
+        stream.next();
+        stream.next();
+        state.blockComment = false;
+      } else {
+        stream.skipToEnd();
+      }
+      return "comment";
+    }
     if(ch == ";") {
+      if (stream.match("--", true)) {
+        if (!stream.match("-", false)) {  // Except ;--- is not a block comment
+          state.blockComment = true;
+          return "comment";
+        }
+      }
       stream.skipToEnd();
       return "comment";
     }
@@ -124,6 +143,7 @@ CodeMirror.defineMode("asterisk", function() {
   return {
     startState: function() {
       return {
+        blockComment: false,
         extenStart: false,
         extenSame:  false,
         extenInclude: false,
@@ -187,7 +207,11 @@ CodeMirror.defineMode("asterisk", function() {
       }
 
       return null;
-    }
+    },
+
+    blockCommentStart: ";--",
+    blockCommentEnd: "--;",
+    lineComment: ";"
   };
 });
 
diff --git a/public/vendor/plugins/codemirror/mode/asterisk/index.html b/public/vendor/plugins/codemirror/mode/asterisk/index.html
index 257bd39875..b9fc627dd2 100644
--- a/public/vendor/plugins/codemirror/mode/asterisk/index.html
+++ b/public/vendor/plugins/codemirror/mode/asterisk/index.html
@@ -6,13 +6,14 @@
 
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
+<script src="../../addon/edit/matchbrackets.js"></script>
 <script src="asterisk.js"></script>
 <style>
       .CodeMirror {border: 1px solid #999;}
       .cm-s-default span.cm-arrow { color: red; }
     </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -145,7 +146,7 @@ exten => 8500,n,Goto(s,6)
       var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
         mode: "text/x-asterisk",
         matchBrackets: true,
-        lineNumber: true
+        lineNumbers: true
       });
     </script>
 
diff --git a/public/vendor/plugins/codemirror/mode/brainfuck/brainfuck.js b/public/vendor/plugins/codemirror/mode/brainfuck/brainfuck.js
index 3becf2a5a3..af6d889aee 100644
--- a/public/vendor/plugins/codemirror/mode/brainfuck/brainfuck.js
+++ b/public/vendor/plugins/codemirror/mode/brainfuck/brainfuck.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 // Brainfuck mode created by Michael Kaminsky https://github.com/mkaminsky11
 
diff --git a/public/vendor/plugins/codemirror/mode/brainfuck/index.html b/public/vendor/plugins/codemirror/mode/brainfuck/index.html
index 6048fc2412..a04bddb14e 100644
--- a/public/vendor/plugins/codemirror/mode/brainfuck/index.html
+++ b/public/vendor/plugins/codemirror/mode/brainfuck/index.html
@@ -12,7 +12,7 @@
 	.CodeMirror { border: 2px inset #dee; }
     </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/clike/clike.js b/public/vendor/plugins/codemirror/mode/clike/clike.js
index a37921fdae..b3cf54e81b 100644
--- a/public/vendor/plugins/codemirror/mode/clike/clike.js
+++ b/public/vendor/plugins/codemirror/mode/clike/clike.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -21,7 +21,7 @@ function Context(indented, column, type, info, align, prev) {
 }
 function pushContext(state, col, type, info) {
   var indent = state.indented;
-  if (state.context && state.context.type != "statement" && type != "statement")
+  if (state.context && state.context.type == "statement" && type != "statement")
     indent = state.context.indented;
   return state.context = new Context(indent, col, type, info, null, state.context);
 }
@@ -33,7 +33,7 @@ function popContext(state) {
 }
 
 function typeBefore(stream, state, pos) {
-  if (state.prevToken == "variable" || state.prevToken == "variable-3") return true;
+  if (state.prevToken == "variable" || state.prevToken == "type") return true;
   if (/\S(?:[^- ]>|[*\]])\s*$|\*$/.test(stream.string.slice(0, pos))) return true;
   if (state.typeAtEndOfLine && stream.column() == stream.indentation()) return true;
 }
@@ -65,7 +65,10 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
       numberStart = parserConfig.numberStart || /[\d\.]/,
       number = parserConfig.number || /^(?:0x[a-f\d]+|0b[01]+|(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)(u|ll?|l|f)?/i,
       isOperatorChar = parserConfig.isOperatorChar || /[+\-*&%=<>!?|\/]/,
-      endStatement = parserConfig.endStatement || /^[;:,]$/;
+      isIdentifierChar = parserConfig.isIdentifierChar || /[\w\$_\xa1-\uffff]/,
+      // An optional function that takes a {string} token and returns true if it
+      // should be treated as a builtin.
+      isReservedIdentifier = parserConfig.isReservedIdentifier || false;
 
   var curPunc, isDefKeyword;
 
@@ -102,9 +105,9 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
       while (!stream.match(/^\/[\/*]/, false) && stream.eat(isOperatorChar)) {}
       return "operator";
     }
-    stream.eatWhile(/[\w\$_\xa1-\uffff]/);
+    stream.eatWhile(isIdentifierChar);
     if (namespaceSeparator) while (stream.match(namespaceSeparator))
-      stream.eatWhile(/[\w\$_\xa1-\uffff]/);
+      stream.eatWhile(isIdentifierChar);
 
     var cur = stream.current();
     if (contains(keywords, cur)) {
@@ -112,8 +115,9 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
       if (contains(defKeywords, cur)) isDefKeyword = true;
       return "keyword";
     }
-    if (contains(types, cur)) return "variable-3";
-    if (contains(builtin, cur)) {
+    if (contains(types, cur)) return "type";
+    if (contains(builtin, cur)
+        || (isReservedIdentifier && isReservedIdentifier(cur))) {
       if (contains(blockKeywords, cur)) curPunc = "newstatement";
       return "builtin";
     }
@@ -177,7 +181,8 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
       if (style == "comment" || style == "meta") return style;
       if (ctx.align == null) ctx.align = true;
 
-      if (endStatement.test(curPunc)) while (state.context.type == "statement") popContext(state);
+      if (curPunc == ";" || curPunc == ":" || (curPunc == "," && stream.match(/^\s*(?:\/\/.*)?$/, false)))
+        while (state.context.type == "statement") popContext(state);
       else if (curPunc == "{") pushContext(state, stream.column(), "}");
       else if (curPunc == "[") pushContext(state, stream.column(), "]");
       else if (curPunc == "(") pushContext(state, stream.column(), ")");
@@ -215,15 +220,15 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
     indent: function(state, textAfter) {
       if (state.tokenize != tokenBase && state.tokenize != null || state.typeAtEndOfLine) return CodeMirror.Pass;
       var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
+      var closing = firstChar == ctx.type;
       if (ctx.type == "statement" && firstChar == "}") ctx = ctx.prev;
       if (parserConfig.dontIndentStatements)
         while (ctx.type == "statement" && parserConfig.dontIndentStatements.test(ctx.info))
           ctx = ctx.prev
       if (hooks.indent) {
-        var hook = hooks.indent(state, ctx, textAfter);
+        var hook = hooks.indent(state, ctx, textAfter, indentUnit);
         if (typeof hook == "number") return hook
       }
-      var closing = firstChar == ctx.type;
       var switchBlock = ctx.prev && ctx.prev.info == "switch";
       if (parserConfig.allmanIndentation && /[{(]/.test(firstChar)) {
         while (ctx.type != "top" && ctx.type != "}") ctx = ctx.prev
@@ -243,6 +248,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
     electricInput: indentSwitch ? /^\s*(?:case .*?:|default:|\{\}?|\})$/ : /^\s*[{}]$/,
     blockCommentStart: "/*",
     blockCommentEnd: "*/",
+    blockCommentContinue: " * ",
     lineComment: "//",
     fold: "brace"
   };
@@ -261,8 +267,33 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
     }
   }
   var cKeywords = "auto if break case register continue return default do sizeof " +
-    "static else struct switch extern typedef union for goto while enum const volatile";
-  var cTypes = "int long char short double float unsigned signed void size_t ptrdiff_t";
+    "static else struct switch extern typedef union for goto while enum const " +
+    "volatile inline restrict asm fortran";
+
+  // Do not use this. Use the cTypes function below. This is global just to avoid
+  // excessive calls when cTypes is being called multiple times during a parse.
+  var basicCTypes = words("int long char short double float unsigned signed " +
+    "void bool");
+
+  // Do not use this. Use the objCTypes function below. This is global just to avoid
+  // excessive calls when objCTypes is being called multiple times during a parse.
+  var basicObjCTypes = words("SEL instancetype id Class Protocol BOOL");
+
+  // Returns true if identifier is a "C" type.
+  // C type is defined as those that are reserved by the compiler (basicTypes),
+  // and those that end in _t (Reserved by POSIX for types)
+  // http://www.gnu.org/software/libc/manual/html_node/Reserved-Names.html
+  function cTypes(identifier) {
+    return contains(basicCTypes, identifier) || /.+_t$/.test(identifier);
+  }
+
+  // Returns true if identifier is a "Objective C" type.
+  function objCTypes(identifier) {
+    return cTypes(identifier) || contains(basicObjCTypes, identifier);
+  }
+
+  var cBlockKeywords = "case do else for if switch while struct enum union";
+  var cDefKeywords = "struct enum union";
 
   function cppHook(stream, state) {
     if (!state.startOfLine) return false
@@ -280,10 +311,18 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
   }
 
   function pointerHook(_stream, state) {
-    if (state.prevToken == "variable-3") return "variable-3";
+    if (state.prevToken == "type") return "type";
     return false;
   }
 
+  // For C and C++ (and ObjC): identifiers starting with __
+  // or _ followed by a capital letter are reserved for the compiler.
+  function cIsReservedIdentifier(token) {
+    if (!token || token.length < 2) return false;
+    if (token[0] != '_') return false;
+    return (token[1] == '_') || (token[1] !== token[1].toLowerCase());
+  }
+
   function cpp14Literal(stream) {
     stream.eatWhile(/[\w\.']/);
     return "number";
@@ -314,7 +353,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
   }
 
   function cppLooksLikeConstructor(word) {
-    var lastTwo = /(\w+)::(\w+)$/.exec(word);
+    var lastTwo = /(\w+)::~?(\w+)$/.exec(word);
     return lastTwo && lastTwo[1] == lastTwo[2];
   }
 
@@ -366,30 +405,36 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
   def(["text/x-csrc", "text/x-c", "text/x-chdr"], {
     name: "clike",
     keywords: words(cKeywords),
-    types: words(cTypes + " bool _Complex _Bool float_t double_t intptr_t intmax_t " +
-                 "int8_t int16_t int32_t int64_t uintptr_t uintmax_t uint8_t uint16_t " +
-                 "uint32_t uint64_t"),
-    blockKeywords: words("case do else for if switch while struct"),
-    defKeywords: words("struct"),
+    types: cTypes,
+    blockKeywords: words(cBlockKeywords),
+    defKeywords: words(cDefKeywords),
     typeFirstDefinitions: true,
-    atoms: words("null true false"),
-    hooks: {"#": cppHook, "*": pointerHook},
+    atoms: words("NULL true false"),
+    isReservedIdentifier: cIsReservedIdentifier,
+    hooks: {
+      "#": cppHook,
+      "*": pointerHook,
+    },
     modeProps: {fold: ["brace", "include"]}
   });
 
   def(["text/x-c++src", "text/x-c++hdr"], {
     name: "clike",
-    keywords: words(cKeywords + " asm dynamic_cast namespace reinterpret_cast try explicit new " +
-                    "static_cast typeid catch operator template typename class friend private " +
-                    "this using const_cast inline public throw virtual delete mutable protected " +
-                    "alignas alignof constexpr decltype nullptr noexcept thread_local final " +
-                    "static_assert override"),
-    types: words(cTypes + " bool wchar_t"),
-    blockKeywords: words("catch class do else finally for if struct switch try while"),
-    defKeywords: words("class namespace struct enum union"),
+    // Keywords from https://en.cppreference.com/w/cpp/keyword includes C++20.
+    keywords: words(cKeywords + "alignas alignof and and_eq audit axiom bitand bitor catch " +
+                    "class compl concept constexpr const_cast decltype delete dynamic_cast " +
+                    "explicit export final friend import module mutable namespace new noexcept " +
+                    "not not_eq operator or or_eq override private protected public " +
+                    "reinterpret_cast requires static_assert static_cast template this " +
+                    "thread_local throw try typeid typename using virtual xor xor_eq"),
+    types: cTypes,
+    blockKeywords: words(cBlockKeywords + " class try catch"),
+    defKeywords: words(cDefKeywords + " class namespace"),
     typeFirstDefinitions: true,
-    atoms: words("true false null"),
+    atoms: words("true false NULL nullptr"),
     dontIndentStatements: /^template$/,
+    isIdentifierChar: /[\w\$_~\xa1-\uffff]/,
+    isReservedIdentifier: cIsReservedIdentifier,
     hooks: {
       "#": cppHook,
       "*": pointerHook,
@@ -422,20 +467,22 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
   def("text/x-java", {
     name: "clike",
     keywords: words("abstract assert break case catch class const continue default " +
-                    "do else enum extends final finally float for goto if implements import " +
+                    "do else enum extends final finally for goto if implements import " +
                     "instanceof interface native new package private protected public " +
                     "return static strictfp super switch synchronized this throw throws transient " +
-                    "try volatile while"),
+                    "try volatile while @interface"),
     types: words("byte short int long float double boolean char void Boolean Byte Character Double Float " +
                  "Integer Long Number Object Short String StringBuffer StringBuilder Void"),
     blockKeywords: words("catch class do else finally for if switch try while"),
-    defKeywords: words("class interface package enum"),
+    defKeywords: words("class interface enum @interface"),
     typeFirstDefinitions: true,
     atoms: words("true false null"),
-    endStatement: /^[;:]$/,
     number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+\.?\d*|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
     hooks: {
       "@": function(stream) {
+        // Don't match the @interface keyword.
+        if (stream.match('interface', false)) return false;
+
         stream.eatWhile(/[\w\$_]/);
         return "meta";
       }
@@ -484,21 +531,38 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
     return "string";
   }
 
+  function tokenNestedComment(depth) {
+    return function (stream, state) {
+      var ch
+      while (ch = stream.next()) {
+        if (ch == "*" && stream.eat("/")) {
+          if (depth == 1) {
+            state.tokenize = null
+            break
+          } else {
+            state.tokenize = tokenNestedComment(depth - 1)
+            return state.tokenize(stream, state)
+          }
+        } else if (ch == "/" && stream.eat("*")) {
+          state.tokenize = tokenNestedComment(depth + 1)
+          return state.tokenize(stream, state)
+        }
+      }
+      return "comment"
+    }
+  }
+
   def("text/x-scala", {
     name: "clike",
     keywords: words(
-
       /* scala */
       "abstract case catch class def do else extends final finally for forSome if " +
       "implicit import lazy match new null object override package private protected return " +
-      "sealed super this throw trait try type val var while with yield _ : = => <- <: " +
-      "<% >: # @ " +
+      "sealed super this throw trait try type val var while with yield _ " +
 
       /* package scala */
       "assert assume require print println printf readLine readBoolean readByte readShort " +
-      "readChar readInt readLong readFloat readDouble " +
-
-      ":: #:: "
+      "readChar readInt readLong readFloat readDouble"
     ),
     types: words(
       "AnyVal App Application Array BufferedIterator BigDecimal BigInt Char Console Either " +
@@ -514,11 +578,12 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
       "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
     ),
     multiLineStrings: true,
-    blockKeywords: words("catch class do else finally for forSome if match switch try while"),
-    defKeywords: words("class def object package trait type val var"),
+    blockKeywords: words("catch class enum do else finally for forSome if match switch try while"),
+    defKeywords: words("class enum def object package trait type val var"),
     atoms: words("true false null"),
     indentStatements: false,
     indentSwitch: false,
+    isOperatorChar: /[+\-*&%=<>!?|\/#:@]/,
     hooks: {
       "@": function(stream) {
         stream.eatWhile(/[\w\$_]/);
@@ -541,9 +606,15 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
         } else {
           return false
         }
+      },
+
+      "/": function(stream, state) {
+        if (!stream.eat("*")) return false
+        state.tokenize = tokenNestedComment(1)
+        return state.tokenize(stream, state)
       }
     },
-    modeProps: {closeBrackets: {triples: '"'}}
+    modeProps: {closeBrackets: {pairs: '()[]{}""', triples: '"'}}
   });
 
   function tokenKotlinString(tripleString){
@@ -567,33 +638,59 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
     name: "clike",
     keywords: words(
       /*keywords*/
-      "package as typealias class interface this super val " +
-      "var fun for is in This throw return " +
+      "package as typealias class interface this super val operator " +
+      "var fun for is in This throw return annotation " +
       "break continue object if else while do try when !in !is as? " +
 
       /*soft keywords*/
       "file import where by get set abstract enum open inner override private public internal " +
       "protected catch finally out final vararg reified dynamic companion constructor init " +
       "sealed field property receiver param sparam lateinit data inline noinline tailrec " +
-      "external annotation crossinline const operator infix"
+      "external annotation crossinline const operator infix suspend actual expect setparam"
     ),
     types: words(
       /* package java.lang */
       "Boolean Byte Character CharSequence Class ClassLoader Cloneable Comparable " +
       "Compiler Double Exception Float Integer Long Math Number Object Package Pair Process " +
       "Runtime Runnable SecurityManager Short StackTraceElement StrictMath String " +
-      "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void"
+      "StringBuffer System Thread ThreadGroup ThreadLocal Throwable Triple Void Annotation Any BooleanArray " +
+      "ByteArray Char CharArray DeprecationLevel DoubleArray Enum FloatArray Function Int IntArray Lazy " +
+      "LazyThreadSafetyMode LongArray Nothing ShortArray Unit"
     ),
     intendSwitch: false,
     indentStatements: false,
     multiLineStrings: true,
+    number: /^(?:0x[a-f\d_]+|0b[01_]+|(?:[\d_]+(\.\d+)?|\.\d+)(?:e[-+]?[\d_]+)?)(u|ll?|l|f)?/i,
     blockKeywords: words("catch class do else finally for if where try while enum"),
-    defKeywords: words("class val var object package interface fun"),
+    defKeywords: words("class val var object interface fun"),
     atoms: words("true false null this"),
     hooks: {
+      "@": function(stream) {
+        stream.eatWhile(/[\w\$_]/);
+        return "meta";
+      },
+      '*': function(_stream, state) {
+        return state.prevToken == '.' ? 'variable' : 'operator';
+      },
       '"': function(stream, state) {
         state.tokenize = tokenKotlinString(stream.match('""'));
         return state.tokenize(stream, state);
+      },
+      "/": function(stream, state) {
+        if (!stream.eat("*")) return false;
+        state.tokenize = tokenNestedComment(1);
+        return state.tokenize(stream, state)
+      },
+      indent: function(state, ctx, textAfter, indentUnit) {
+        var firstChar = textAfter && textAfter.charAt(0);
+        if ((state.prevToken == "}" || state.prevToken == ")") && textAfter == "")
+          return state.indented;
+        if ((state.prevToken == "operator" && textAfter != "}" && state.context.type != "}") ||
+          state.prevToken == "variable" && firstChar == "." ||
+          (state.prevToken == "}" || state.prevToken == ")") && firstChar == ".")
+          return indentUnit * 2 + ctx.indented;
+        if (ctx.align && ctx.type == "}")
+          return ctx.indented + (state.context.type == (textAfter || "").charAt(0) ? 0 : indentUnit);
       }
     },
     modeProps: {closeBrackets: {triples: '"'}}
@@ -660,11 +757,11 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
 
   def("text/x-nesc", {
     name: "clike",
-    keywords: words(cKeywords + "as atomic async call command component components configuration event generic " +
+    keywords: words(cKeywords + " as atomic async call command component components configuration event generic " +
                     "implementation includes interface module new norace nx_struct nx_union post provides " +
                     "signal task uses abstract extends"),
-    types: words(cTypes),
-    blockKeywords: words("case do else for if switch while struct"),
+    types: cTypes,
+    blockKeywords: words(cBlockKeywords),
     atoms: words("null true false"),
     hooks: {"#": cppHook},
     modeProps: {fold: ["brace", "include"]}
@@ -672,28 +769,34 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
 
   def("text/x-objectivec", {
     name: "clike",
-    keywords: words(cKeywords + "inline restrict _Bool _Complex _Imaginary BOOL Class bycopy byref id IMP in " +
-                    "inout nil oneway out Protocol SEL self super atomic nonatomic retain copy readwrite readonly"),
-    types: words(cTypes),
-    atoms: words("YES NO NULL NILL ON OFF true false"),
+    keywords: words(cKeywords + " bycopy byref in inout oneway out self super atomic nonatomic retain copy " +
+                    "readwrite readonly strong weak assign typeof nullable nonnull null_resettable _cmd " +
+                    "@interface @implementation @end @protocol @encode @property @synthesize @dynamic @class " +
+                    "@public @package @private @protected @required @optional @try @catch @finally @import " +
+                    "@selector @encode @defs @synchronized @autoreleasepool @compatibility_alias @available"),
+    types: objCTypes,
+    builtin: words("FOUNDATION_EXPORT FOUNDATION_EXTERN NS_INLINE NS_FORMAT_FUNCTION NS_RETURNS_RETAINED " +
+                   "NS_ERROR_ENUM NS_RETURNS_NOT_RETAINED NS_RETURNS_INNER_POINTER NS_DESIGNATED_INITIALIZER " +
+                   "NS_ENUM NS_OPTIONS NS_REQUIRES_NIL_TERMINATION NS_ASSUME_NONNULL_BEGIN " +
+                   "NS_ASSUME_NONNULL_END NS_SWIFT_NAME NS_REFINED_FOR_SWIFT"),
+    blockKeywords: words(cBlockKeywords + " @synthesize @try @catch @finally @autoreleasepool @synchronized"),
+    defKeywords: words(cDefKeywords + " @interface @implementation @protocol @class"),
+    dontIndentStatements: /^@.*$/,
+    typeFirstDefinitions: true,
+    atoms: words("YES NO NULL Nil nil true false nullptr"),
+    isReservedIdentifier: cIsReservedIdentifier,
     hooks: {
-      "@": function(stream) {
-        stream.eatWhile(/[\w\$]/);
-        return "keyword";
-      },
       "#": cppHook,
-      indent: function(_state, ctx, textAfter) {
-        if (ctx.type == "statement" && /^@\w/.test(textAfter)) return ctx.indented
-      }
+      "*": pointerHook,
     },
-    modeProps: {fold: "brace"}
+    modeProps: {fold: ["brace", "include"]}
   });
 
   def("text/x-squirrel", {
     name: "clike",
     keywords: words("base break clone continue const default delete enum extends function in class" +
                     " foreach local resume return this throw typeof yield constructor instanceof static"),
-    types: words(cTypes),
+    types: cTypes,
     blockKeywords: words("case catch class else for foreach if switch try while"),
     defKeywords: words("function local class"),
     typeFirstDefinitions: true,
@@ -771,7 +874,7 @@ CodeMirror.defineMode("clike", function(config, parserConfig) {
         return "atom";
       },
       token: function(_stream, state, style) {
-          if ((style == "variable" || style == "variable-3") &&
+          if ((style == "variable" || style == "type") &&
               state.prevToken == ".") {
             return "variable-2";
           }
diff --git a/public/vendor/plugins/codemirror/mode/clike/index.html b/public/vendor/plugins/codemirror/mode/clike/index.html
index 45c670ae58..ad3a807dd9 100644
--- a/public/vendor/plugins/codemirror/mode/clike/index.html
+++ b/public/vendor/plugins/codemirror/mode/clike/index.html
@@ -12,7 +12,7 @@
 <script src="clike.js"></script>
 <style>.CodeMirror {border: 2px inset #dee;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -147,16 +147,36 @@ This is a longer comment
 That spans two lines
 */
 
-#import <Test/Test.h>
+#import "MyClass.h"
+#import <AFramework/AFrameork.h>
+@import BFrameworkModule;
+
+NS_ENUM(SomeValues) {
+  aValue = 1;
+};
+
+// A Class Extension with some properties
+@interface MyClass ()<AProtocol>
+@property(atomic, readwrite, assign) NSInteger anInt;
+@property(nonatomic, strong, nullable) NSString *aString;
+@end
+
 @implementation YourAppDelegate
 
-// This is a one-line comment
-
-- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
-  char myString[] = "This is a C character array";
-  int test = 5;
-  return YES;
+- (instancetype)initWithString:(NSString *)aStringVar {
+  if ((self = [super init])) {
+    aString = aStringVar;
+  }
+  return self;
 }
+
+- (BOOL)doSomething:(float)progress {
+  NSString *myString = @"This is a ObjC string %f ";
+  myString = [[NSString stringWithFormat:myString, progress] stringByAppendingString:self.aString];
+  return myString.length > 100 ? NO : YES;
+}
+
+@end
 </textarea></div>
 
 <h2>Java example</h2>
diff --git a/public/vendor/plugins/codemirror/mode/clike/scala.html b/public/vendor/plugins/codemirror/mode/clike/scala.html
index aa04cf0f04..ddbd9a0cae 100644
--- a/public/vendor/plugins/codemirror/mode/clike/scala.html
+++ b/public/vendor/plugins/codemirror/mode/clike/scala.html
@@ -10,7 +10,7 @@
 <script src="../../addon/edit/matchbrackets.js"></script>
 <script src="clike.js"></script>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/clike/test.js b/public/vendor/plugins/codemirror/mode/clike/test.js
index bea85b8693..9441b95956 100644
--- a/public/vendor/plugins/codemirror/mode/clike/test.js
+++ b/public/vendor/plugins/codemirror/mode/clike/test.js
@@ -1,13 +1,13 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function() {
   var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-c");
   function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
 
   MT("indent",
-     "[variable-3 void] [def foo]([variable-3 void*] [variable a], [variable-3 int] [variable b]) {",
-     "  [variable-3 int] [variable c] [operator =] [variable b] [operator +]",
+     "[type void] [def foo]([type void*] [variable a], [type int] [variable b]) {",
+     "  [type int] [variable c] [operator =] [variable b] [operator +]",
      "    [number 1];",
      "  [keyword return] [operator *][variable a];",
      "}");
@@ -21,9 +21,11 @@
      "}");
 
   MT("def",
-     "[variable-3 void] [def foo]() {}",
+     "[type void] [def foo]() {}",
      "[keyword struct] [def bar]{}",
-     "[variable-3 int] [variable-3 *][def baz]() {}");
+     "[keyword enum] [def zot]{}",
+     "[keyword union] [def ugh]{}",
+     "[type int] [type *][def baz]() {}");
 
   MT("def_new_line",
      "::[variable std]::[variable SomeTerribleType][operator <][variable T][operator >]",
@@ -37,12 +39,32 @@
 
   MT("preprocessor",
      "[meta #define FOO 3]",
-     "[variable-3 int] [variable foo];",
+     "[type int] [variable foo];",
      "[meta #define BAR\\]",
      "[meta 4]",
-     "[variable-3 unsigned] [variable-3 int] [variable bar] [operator =] [number 8];",
+     "[type unsigned] [type int] [variable bar] [operator =] [number 8];",
      "[meta #include <baz> ][comment // comment]")
 
+  MT("c_underscores",
+     "[builtin __FOO];",
+     "[builtin _Complex];",
+     "[builtin __aName];",
+     "[variable _aName];");
+
+  MT("c_types",
+    "[type int];",
+    "[type long];",
+    "[type char];",
+    "[type short];",
+    "[type double];",
+    "[type float];",
+    "[type unsigned];",
+    "[type signed];",
+    "[type void];",
+    "[type bool];",
+    "[type foo_t];",
+    "[variable foo_T];",
+    "[variable _t];");
 
   var mode_cpp = CodeMirror.getMode({indentUnit: 2}, "text/x-c++src");
   function MTCPP(name) { test.mode(name, mode_cpp, Array.prototype.slice.call(arguments, 1)); }
@@ -52,4 +74,92 @@
     "[number 0b10'000];",
     "[number 0x10'000];",
     "[string '100000'];");
+
+  MTCPP("ctor_dtor",
+     "[def Foo::Foo]() {}",
+     "[def Foo::~Foo]() {}");
+
+  MTCPP("cpp_underscores",
+        "[builtin __FOO];",
+        "[builtin _Complex];",
+        "[builtin __aName];",
+        "[variable _aName];");
+
+  var mode_objc = CodeMirror.getMode({indentUnit: 2}, "text/x-objectivec");
+  function MTOBJC(name) { test.mode(name, mode_objc, Array.prototype.slice.call(arguments, 1)); }
+
+  MTOBJC("objc_underscores",
+         "[builtin __FOO];",
+         "[builtin _Complex];",
+         "[builtin __aName];",
+         "[variable _aName];");
+
+  MTOBJC("objc_interface",
+         "[keyword @interface] [def foo] {",
+         "  [type int] [variable bar];",
+         "}",
+         "[keyword @property] ([keyword atomic], [keyword nullable]) [variable NSString][operator *] [variable a];",
+         "[keyword @property] ([keyword nonatomic], [keyword assign]) [type int] [variable b];",
+         "[operator -]([type instancetype])[variable initWithFoo]:([type int])[variable a] " +
+           "[builtin NS_DESIGNATED_INITIALIZER];",
+         "[keyword @end]");
+
+  MTOBJC("objc_implementation",
+         "[keyword @implementation] [def foo] {",
+         "  [type int] [variable bar];",
+         "}",
+         "[keyword @property] ([keyword readwrite]) [type SEL] [variable a];",
+         "[operator -]([type instancetype])[variable initWithFoo]:([type int])[variable a] {",
+         "  [keyword if](([keyword self] [operator =] [[[keyword super] [variable init] ]])) {}",
+         "  [keyword return] [keyword self];",
+         "}",
+         "[keyword @end]");
+
+  MTOBJC("objc_types",
+         "[type int];",
+         "[type foo_t];",
+         "[variable foo_T];",
+         "[type id];",
+         "[type SEL];",
+         "[type instancetype];",
+         "[type Class];",
+         "[type Protocol];",
+         "[type BOOL];"
+         );
+
+  var mode_scala = CodeMirror.getMode({indentUnit: 2}, "text/x-scala");
+  function MTSCALA(name) { test.mode("scala_" + name, mode_scala, Array.prototype.slice.call(arguments, 1)); }
+  MTSCALA("nested_comments",
+     "[comment /*]",
+     "[comment But wait /* this is a nested comment */ for real]",
+     "[comment /**** let * me * show * you ****/]",
+     "[comment ///// let / me / show / you /////]",
+     "[comment */]");
+
+  var mode_java = CodeMirror.getMode({indentUnit: 2}, "text/x-java");
+  function MTJAVA(name) { test.mode("java_" + name, mode_java, Array.prototype.slice.call(arguments, 1)); }
+  MTJAVA("types",
+         "[type byte];",
+         "[type short];",
+         "[type int];",
+         "[type long];",
+         "[type float];",
+         "[type double];",
+         "[type boolean];",
+         "[type char];",
+         "[type void];",
+         "[type Boolean];",
+         "[type Byte];",
+         "[type Character];",
+         "[type Double];",
+         "[type Float];",
+         "[type Integer];",
+         "[type Long];",
+         "[type Number];",
+         "[type Object];",
+         "[type Short];",
+         "[type String];",
+         "[type StringBuffer];",
+         "[type StringBuilder];",
+         "[type Void];");
 })();
diff --git a/public/vendor/plugins/codemirror/mode/clojure/clojure.js b/public/vendor/plugins/codemirror/mode/clojure/clojure.js
index ed6af2c83c..25d308ab4c 100644
--- a/public/vendor/plugins/codemirror/mode/clojure/clojure.js
+++ b/public/vendor/plugins/codemirror/mode/clojure/clojure.js
@@ -1,15 +1,10 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
-
-/**
- * Author: Hans Engel
- * Branched from CodeMirror's Scheme mode (by Koh Zi Han, based on implementation by Koh Zi Chun)
- */
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
-  if (typeof exports == "object" && typeof module == "object") // CommonJS
+  if (typeof exports === "object" && typeof module === "object") // CommonJS
     mod(require("../../lib/codemirror"));
-  else if (typeof define == "function" && define.amd) // AMD
+  else if (typeof define === "function" && define.amd) // AMD
     define(["../../lib/codemirror"], mod);
   else // Plain browser env
     mod(CodeMirror);
@@ -17,286 +12,277 @@
 "use strict";
 
 CodeMirror.defineMode("clojure", function (options) {
-    var BUILTIN = "builtin", COMMENT = "comment", STRING = "string", CHARACTER = "string-2",
-        ATOM = "atom", NUMBER = "number", BRACKET = "bracket", KEYWORD = "keyword", VAR = "variable";
-    var INDENT_WORD_SKIP = options.indentUnit || 2;
-    var NORMAL_INDENT_UNIT = options.indentUnit || 2;
+  var atoms = ["false", "nil", "true"];
+  var specialForms = [".", "catch", "def", "do", "if", "monitor-enter",
+      "monitor-exit", "new", "quote", "recur", "set!", "throw", "try", "var"];
+  var coreSymbols = ["*", "*'", "*1", "*2", "*3", "*agent*",
+      "*allow-unresolved-vars*", "*assert*", "*clojure-version*",
+      "*command-line-args*", "*compile-files*", "*compile-path*",
+      "*compiler-options*", "*data-readers*", "*default-data-reader-fn*", "*e",
+      "*err*", "*file*", "*flush-on-newline*", "*fn-loader*", "*in*",
+      "*math-context*", "*ns*", "*out*", "*print-dup*", "*print-length*",
+      "*print-level*", "*print-meta*", "*print-namespace-maps*",
+      "*print-readably*", "*read-eval*", "*reader-resolver*", "*source-path*",
+      "*suppress-read*", "*unchecked-math*", "*use-context-classloader*",
+      "*verbose-defrecords*", "*warn-on-reflection*", "+", "+'", "-", "-'",
+      "->", "->>", "->ArrayChunk", "->Eduction", "->Vec", "->VecNode",
+      "->VecSeq", "-cache-protocol-fn", "-reset-methods", "..", "/", "<", "<=",
+      "=", "==", ">", ">=", "EMPTY-NODE", "Inst", "StackTraceElement->vec",
+      "Throwable->map", "accessor", "aclone", "add-classpath", "add-watch",
+      "agent", "agent-error", "agent-errors", "aget", "alength", "alias",
+      "all-ns", "alter", "alter-meta!", "alter-var-root", "amap", "ancestors",
+      "and", "any?", "apply", "areduce", "array-map", "as->", "aset",
+      "aset-boolean", "aset-byte", "aset-char", "aset-double", "aset-float",
+      "aset-int", "aset-long", "aset-short", "assert", "assoc", "assoc!",
+      "assoc-in", "associative?", "atom", "await", "await-for", "await1",
+      "bases", "bean", "bigdec", "bigint", "biginteger", "binding", "bit-and",
+      "bit-and-not", "bit-clear", "bit-flip", "bit-not", "bit-or", "bit-set",
+      "bit-shift-left", "bit-shift-right", "bit-test", "bit-xor", "boolean",
+      "boolean-array", "boolean?", "booleans", "bound-fn", "bound-fn*",
+      "bound?", "bounded-count", "butlast", "byte", "byte-array", "bytes",
+      "bytes?", "case", "cast", "cat", "char", "char-array",
+      "char-escape-string", "char-name-string", "char?", "chars", "chunk",
+      "chunk-append", "chunk-buffer", "chunk-cons", "chunk-first", "chunk-next",
+      "chunk-rest", "chunked-seq?", "class", "class?", "clear-agent-errors",
+      "clojure-version", "coll?", "comment", "commute", "comp", "comparator",
+      "compare", "compare-and-set!", "compile", "complement", "completing",
+      "concat", "cond", "cond->", "cond->>", "condp", "conj", "conj!", "cons",
+      "constantly", "construct-proxy", "contains?", "count", "counted?",
+      "create-ns", "create-struct", "cycle", "dec", "dec'", "decimal?",
+      "declare", "dedupe", "default-data-readers", "definline", "definterface",
+      "defmacro", "defmethod", "defmulti", "defn", "defn-", "defonce",
+      "defprotocol", "defrecord", "defstruct", "deftype", "delay", "delay?",
+      "deliver", "denominator", "deref", "derive", "descendants", "destructure",
+      "disj", "disj!", "dissoc", "dissoc!", "distinct", "distinct?", "doall",
+      "dorun", "doseq", "dosync", "dotimes", "doto", "double", "double-array",
+      "double?", "doubles", "drop", "drop-last", "drop-while", "eduction",
+      "empty", "empty?", "ensure", "ensure-reduced", "enumeration-seq",
+      "error-handler", "error-mode", "eval", "even?", "every-pred", "every?",
+      "ex-data", "ex-info", "extend", "extend-protocol", "extend-type",
+      "extenders", "extends?", "false?", "ffirst", "file-seq", "filter",
+      "filterv", "find", "find-keyword", "find-ns", "find-protocol-impl",
+      "find-protocol-method", "find-var", "first", "flatten", "float",
+      "float-array", "float?", "floats", "flush", "fn", "fn?", "fnext", "fnil",
+      "for", "force", "format", "frequencies", "future", "future-call",
+      "future-cancel", "future-cancelled?", "future-done?", "future?",
+      "gen-class", "gen-interface", "gensym", "get", "get-in", "get-method",
+      "get-proxy-class", "get-thread-bindings", "get-validator", "group-by",
+      "halt-when", "hash", "hash-combine", "hash-map", "hash-ordered-coll",
+      "hash-set", "hash-unordered-coll", "ident?", "identical?", "identity",
+      "if-let", "if-not", "if-some", "ifn?", "import", "in-ns", "inc", "inc'",
+      "indexed?", "init-proxy", "inst-ms", "inst-ms*", "inst?", "instance?",
+      "int", "int-array", "int?", "integer?", "interleave", "intern",
+      "interpose", "into", "into-array", "ints", "io!", "isa?", "iterate",
+      "iterator-seq", "juxt", "keep", "keep-indexed", "key", "keys", "keyword",
+      "keyword?", "last", "lazy-cat", "lazy-seq", "let", "letfn", "line-seq",
+      "list", "list*", "list?", "load", "load-file", "load-reader",
+      "load-string", "loaded-libs", "locking", "long", "long-array", "longs",
+      "loop", "macroexpand", "macroexpand-1", "make-array", "make-hierarchy",
+      "map", "map-entry?", "map-indexed", "map?", "mapcat", "mapv", "max",
+      "max-key", "memfn", "memoize", "merge", "merge-with", "meta",
+      "method-sig", "methods", "min", "min-key", "mix-collection-hash", "mod",
+      "munge", "name", "namespace", "namespace-munge", "nat-int?", "neg-int?",
+      "neg?", "newline", "next", "nfirst", "nil?", "nnext", "not", "not-any?",
+      "not-empty", "not-every?", "not=", "ns", "ns-aliases", "ns-imports",
+      "ns-interns", "ns-map", "ns-name", "ns-publics", "ns-refers",
+      "ns-resolve", "ns-unalias", "ns-unmap", "nth", "nthnext", "nthrest",
+      "num", "number?", "numerator", "object-array", "odd?", "or", "parents",
+      "partial", "partition", "partition-all", "partition-by", "pcalls", "peek",
+      "persistent!", "pmap", "pop", "pop!", "pop-thread-bindings", "pos-int?",
+      "pos?", "pr", "pr-str", "prefer-method", "prefers",
+      "primitives-classnames", "print", "print-ctor", "print-dup",
+      "print-method", "print-simple", "print-str", "printf", "println",
+      "println-str", "prn", "prn-str", "promise", "proxy",
+      "proxy-call-with-super", "proxy-mappings", "proxy-name", "proxy-super",
+      "push-thread-bindings", "pvalues", "qualified-ident?",
+      "qualified-keyword?", "qualified-symbol?", "quot", "rand", "rand-int",
+      "rand-nth", "random-sample", "range", "ratio?", "rational?",
+      "rationalize", "re-find", "re-groups", "re-matcher", "re-matches",
+      "re-pattern", "re-seq", "read", "read-line", "read-string",
+      "reader-conditional", "reader-conditional?", "realized?", "record?",
+      "reduce", "reduce-kv", "reduced", "reduced?", "reductions", "ref",
+      "ref-history-count", "ref-max-history", "ref-min-history", "ref-set",
+      "refer", "refer-clojure", "reify", "release-pending-sends", "rem",
+      "remove", "remove-all-methods", "remove-method", "remove-ns",
+      "remove-watch", "repeat", "repeatedly", "replace", "replicate", "require",
+      "reset!", "reset-meta!", "reset-vals!", "resolve", "rest",
+      "restart-agent", "resultset-seq", "reverse", "reversible?", "rseq",
+      "rsubseq", "run!", "satisfies?", "second", "select-keys", "send",
+      "send-off", "send-via", "seq", "seq?", "seqable?", "seque", "sequence",
+      "sequential?", "set", "set-agent-send-executor!",
+      "set-agent-send-off-executor!", "set-error-handler!", "set-error-mode!",
+      "set-validator!", "set?", "short", "short-array", "shorts", "shuffle",
+      "shutdown-agents", "simple-ident?", "simple-keyword?", "simple-symbol?",
+      "slurp", "some", "some->", "some->>", "some-fn", "some?", "sort",
+      "sort-by", "sorted-map", "sorted-map-by", "sorted-set", "sorted-set-by",
+      "sorted?", "special-symbol?", "spit", "split-at", "split-with", "str",
+      "string?", "struct", "struct-map", "subs", "subseq", "subvec", "supers",
+      "swap!", "swap-vals!", "symbol", "symbol?", "sync", "tagged-literal",
+      "tagged-literal?", "take", "take-last", "take-nth", "take-while", "test",
+      "the-ns", "thread-bound?", "time", "to-array", "to-array-2d",
+      "trampoline", "transduce", "transient", "tree-seq", "true?", "type",
+      "unchecked-add", "unchecked-add-int", "unchecked-byte", "unchecked-char",
+      "unchecked-dec", "unchecked-dec-int", "unchecked-divide-int",
+      "unchecked-double", "unchecked-float", "unchecked-inc",
+      "unchecked-inc-int", "unchecked-int", "unchecked-long",
+      "unchecked-multiply", "unchecked-multiply-int", "unchecked-negate",
+      "unchecked-negate-int", "unchecked-remainder-int", "unchecked-short",
+      "unchecked-subtract", "unchecked-subtract-int", "underive", "unquote",
+      "unquote-splicing", "unreduced", "unsigned-bit-shift-right", "update",
+      "update-in", "update-proxy", "uri?", "use", "uuid?", "val", "vals",
+      "var-get", "var-set", "var?", "vary-meta", "vec", "vector", "vector-of",
+      "vector?", "volatile!", "volatile?", "vreset!", "vswap!", "when",
+      "when-first", "when-let", "when-not", "when-some", "while",
+      "with-bindings", "with-bindings*", "with-in-str", "with-loading-context",
+      "with-local-vars", "with-meta", "with-open", "with-out-str",
+      "with-precision", "with-redefs", "with-redefs-fn", "xml-seq", "zero?",
+      "zipmap"];
+  var haveBodyParameter = [
+      "->", "->>", "as->", "binding", "bound-fn", "case", "catch", "comment",
+      "cond", "cond->", "cond->>", "condp", "def", "definterface", "defmethod",
+      "defn", "defmacro", "defprotocol", "defrecord", "defstruct", "deftype",
+      "do", "doseq", "dotimes", "doto", "extend", "extend-protocol",
+      "extend-type", "fn", "for", "future", "if", "if-let", "if-not", "if-some",
+      "let", "letfn", "locking", "loop", "ns", "proxy", "reify", "struct-map",
+      "some->", "some->>", "try", "when", "when-first", "when-let", "when-not",
+      "when-some", "while", "with-bindings", "with-bindings*", "with-in-str",
+      "with-loading-context", "with-local-vars", "with-meta", "with-open",
+      "with-out-str", "with-precision", "with-redefs", "with-redefs-fn"];
 
-    function makeKeywords(str) {
-        var obj = {}, words = str.split(" ");
-        for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
-        return obj;
+  CodeMirror.registerHelper("hintWords", "clojure",
+    [].concat(atoms, specialForms, coreSymbols));
+
+  var atom = createLookupMap(atoms);
+  var specialForm = createLookupMap(specialForms);
+  var coreSymbol = createLookupMap(coreSymbols);
+  var hasBodyParameter = createLookupMap(haveBodyParameter);
+  var delimiter = /^(?:[\\\[\]\s"(),;@^`{}~]|$)/;
+  var numberLiteral = /^(?:[+\-]?\d+(?:(?:N|(?:[eE][+\-]?\d+))|(?:\.?\d*(?:M|(?:[eE][+\-]?\d+))?)|\/\d+|[xX][0-9a-fA-F]+|r[0-9a-zA-Z]+)?(?=[\\\[\]\s"#'(),;@^`{}~]|$))/;
+  var characterLiteral = /^(?:\\(?:backspace|formfeed|newline|return|space|tab|o[0-7]{3}|u[0-9A-Fa-f]{4}|x[0-9A-Fa-f]{4}|.)?(?=[\\\[\]\s"(),;@^`{}~]|$))/;
+
+  // simple-namespace := /^[^\\\/\[\]\d\s"#'(),;@^`{}~][^\\\[\]\s"(),;@^`{}~]*/
+  // simple-symbol    := /^(?:\/|[^\\\/\[\]\d\s"#'(),;@^`{}~][^\\\[\]\s"(),;@^`{}~]*)/
+  // qualified-symbol := (<simple-namespace>(<.><simple-namespace>)*</>)?<simple-symbol>
+  var qualifiedSymbol = /^(?:(?:[^\\\/\[\]\d\s"#'(),;@^`{}~][^\\\[\]\s"(),;@^`{}~]*(?:\.[^\\\/\[\]\d\s"#'(),;@^`{}~][^\\\[\]\s"(),;@^`{}~]*)*\/)?(?:\/|[^\\\/\[\]\d\s"#'(),;@^`{}~][^\\\[\]\s"(),;@^`{}~]*)*(?=[\\\[\]\s"(),;@^`{}~]|$))/;
+
+  function base(stream, state) {
+    if (stream.eatSpace() || stream.eat(",")) return ["space", null];
+    if (stream.match(numberLiteral)) return [null, "number"];
+    if (stream.match(characterLiteral)) return [null, "string-2"];
+    if (stream.eat(/^"/)) return (state.tokenize = inString)(stream, state);
+    if (stream.eat(/^[(\[{]/)) return ["open", "bracket"];
+    if (stream.eat(/^[)\]}]/)) return ["close", "bracket"];
+    if (stream.eat(/^;/)) {stream.skipToEnd(); return ["space", "comment"];}
+    if (stream.eat(/^[#'@^`~]/)) return [null, "meta"];
+
+    var matches = stream.match(qualifiedSymbol);
+    var symbol = matches && matches[0];
+
+    if (!symbol) {
+      // advance stream by at least one character so we don't get stuck.
+      stream.next();
+      stream.eatWhile(function (c) {return !is(c, delimiter);});
+      return [null, "error"];
     }
 
-    var atoms = makeKeywords("true false nil");
+    if (symbol === "comment" && state.lastToken === "(")
+      return (state.tokenize = inComment)(stream, state);
+    if (is(symbol, atom) || symbol.charAt(0) === ":") return ["symbol", "atom"];
+    if (is(symbol, specialForm) || is(symbol, coreSymbol)) return ["symbol", "keyword"];
+    if (state.lastToken === "(") return ["symbol", "builtin"]; // other operator
 
-    var keywords = makeKeywords(
-      "defn defn- def def- defonce defmulti defmethod defmacro defstruct deftype defprotocol defrecord defproject deftest " +
-      "slice defalias defhinted defmacro- defn-memo defnk defnk defonce- defunbound defunbound- defvar defvar- let letfn " +
-      "do case cond condp for loop recur when when-not when-let when-first if if-let if-not . .. -> ->> doto and or dosync " +
-      "doseq dotimes dorun doall load import unimport ns in-ns refer try catch finally throw with-open with-local-vars " +
-      "binding gen-class gen-and-load-class gen-and-save-class handler-case handle");
+    return ["symbol", "variable"];
+  }
 
-    var builtins = makeKeywords(
-        "* *' *1 *2 *3 *agent* *allow-unresolved-vars* *assert* *clojure-version* *command-line-args* *compile-files* " +
-        "*compile-path* *compiler-options* *data-readers* *e *err* *file* *flush-on-newline* *fn-loader* *in* " +
-        "*math-context* *ns* *out* *print-dup* *print-length* *print-level* *print-meta* *print-readably* *read-eval* " +
-        "*source-path* *unchecked-math* *use-context-classloader* *verbose-defrecords* *warn-on-reflection* + +' - -' -> " +
-        "->> ->ArrayChunk ->Vec ->VecNode ->VecSeq -cache-protocol-fn -reset-methods .. / < <= = == > >= EMPTY-NODE accessor " +
-        "aclone add-classpath add-watch agent agent-error agent-errors aget alength alias all-ns alter alter-meta! " +
-        "alter-var-root amap ancestors and apply areduce array-map aset aset-boolean aset-byte aset-char aset-double " +
-        "aset-float aset-int aset-long aset-short assert assoc assoc! assoc-in associative? atom await await-for await1 " +
-        "bases bean bigdec bigint biginteger binding bit-and bit-and-not bit-clear bit-flip bit-not bit-or bit-set " +
-        "bit-shift-left bit-shift-right bit-test bit-xor boolean boolean-array booleans bound-fn bound-fn* bound? butlast " +
-        "byte byte-array bytes case cat cast char char-array char-escape-string char-name-string char? chars chunk chunk-append " +
-        "chunk-buffer chunk-cons chunk-first chunk-next chunk-rest chunked-seq? class class? clear-agent-errors " +
-        "clojure-version coll? comment commute comp comparator compare compare-and-set! compile complement completing concat cond condp " +
-        "conj conj! cons constantly construct-proxy contains? count counted? create-ns create-struct cycle dec dec' decimal? " +
-        "declare dedupe default-data-readers definline definterface defmacro defmethod defmulti defn defn- defonce defprotocol " +
-        "defrecord defstruct deftype delay delay? deliver denominator deref derive descendants destructure disj disj! dissoc " +
-        "dissoc! distinct distinct? doall dorun doseq dosync dotimes doto double double-array doubles drop drop-last " +
-        "drop-while eduction empty empty? ensure enumeration-seq error-handler error-mode eval even? every-pred every? ex-data ex-info " +
-        "extend extend-protocol extend-type extenders extends? false? ffirst file-seq filter filterv find find-keyword " +
-        "find-ns find-protocol-impl find-protocol-method find-var first flatten float float-array float? floats flush fn fn? " +
-        "fnext fnil for force format frequencies future future-call future-cancel future-cancelled? future-done? future? " +
-        "gen-class gen-interface gensym get get-in get-method get-proxy-class get-thread-bindings get-validator group-by hash " +
-        "hash-combine hash-map hash-set identical? identity if-let if-not ifn? import in-ns inc inc' init-proxy instance? " +
-        "int int-array integer? interleave intern interpose into into-array ints io! isa? iterate iterator-seq juxt keep " +
-        "keep-indexed key keys keyword keyword? last lazy-cat lazy-seq let letfn line-seq list list* list? load load-file " +
-        "load-reader load-string loaded-libs locking long long-array longs loop macroexpand macroexpand-1 make-array " +
-        "make-hierarchy map map-indexed map? mapcat mapv max max-key memfn memoize merge merge-with meta method-sig methods " +
-        "min min-key mod munge name namespace namespace-munge neg? newline next nfirst nil? nnext not not-any? not-empty " +
-        "not-every? not= ns ns-aliases ns-imports ns-interns ns-map ns-name ns-publics ns-refers ns-resolve ns-unalias " +
-        "ns-unmap nth nthnext nthrest num number? numerator object-array odd? or parents partial partition partition-all " +
-        "partition-by pcalls peek persistent! pmap pop pop! pop-thread-bindings pos? pr pr-str prefer-method prefers " +
-        "primitives-classnames print print-ctor print-dup print-method print-simple print-str printf println println-str " +
-        "prn prn-str promise proxy proxy-call-with-super proxy-mappings proxy-name proxy-super push-thread-bindings pvalues " +
-        "quot rand rand-int rand-nth random-sample range ratio? rational? rationalize re-find re-groups re-matcher re-matches re-pattern " +
-        "re-seq read read-line read-string realized? reduce reduce-kv reductions ref ref-history-count ref-max-history " +
-        "ref-min-history ref-set refer refer-clojure reify release-pending-sends rem remove remove-all-methods " +
-        "remove-method remove-ns remove-watch repeat repeatedly replace replicate require reset! reset-meta! resolve rest " +
-        "restart-agent resultset-seq reverse reversible? rseq rsubseq satisfies? second select-keys send send-off seq seq? " +
-        "seque sequence sequential? set set-error-handler! set-error-mode! set-validator! set? short short-array shorts " +
-        "shuffle shutdown-agents slurp some some-fn sort sort-by sorted-map sorted-map-by sorted-set sorted-set-by sorted? " +
-        "special-symbol? spit split-at split-with str string? struct struct-map subs subseq subvec supers swap! symbol " +
-        "symbol? sync take take-last take-nth take-while test the-ns thread-bound? time to-array to-array-2d trampoline transduce " +
-        "transient tree-seq true? type unchecked-add unchecked-add-int unchecked-byte unchecked-char unchecked-dec " +
-        "unchecked-dec-int unchecked-divide-int unchecked-double unchecked-float unchecked-inc unchecked-inc-int " +
-        "unchecked-int unchecked-long unchecked-multiply unchecked-multiply-int unchecked-negate unchecked-negate-int "+
-        "unchecked-remainder-int unchecked-short unchecked-subtract unchecked-subtract-int underive unquote " +
-        "unquote-splicing update update-in update-proxy use val vals var-get var-set var? vary-meta vec vector vector-of " +
-        "vector? volatile! volatile? vreset! vswap! when when-first when-let when-not while with-bindings with-bindings* with-in-str with-loading-context " +
-        "with-local-vars with-meta with-open with-out-str with-precision with-redefs with-redefs-fn xml-seq zero? zipmap " +
-        "*default-data-reader-fn* as-> cond-> cond->> reduced reduced? send-via set-agent-send-executor! " +
-        "set-agent-send-off-executor! some-> some->>");
+  function inString(stream, state) {
+    var escaped = false, next;
 
-    var indentKeys = makeKeywords(
-        // Built-ins
-        "ns fn def defn defmethod bound-fn if if-not case condp when while when-not when-first do future comment doto " +
-        "locking proxy with-open with-precision reify deftype defrecord defprotocol extend extend-protocol extend-type " +
-        "try catch " +
-
-        // Binding forms
-        "let letfn binding loop for doseq dotimes when-let if-let " +
-
-        // Data structures
-        "defstruct struct-map assoc " +
-
-        // clojure.test
-        "testing deftest " +
-
-        // contrib
-        "handler-case handle dotrace deftrace");
-
-    var tests = {
-        digit: /\d/,
-        digit_or_colon: /[\d:]/,
-        hex: /[0-9a-f]/i,
-        sign: /[+-]/,
-        exponent: /e/i,
-        keyword_char: /[^\s\(\[\;\)\]]/,
-        symbol: /[\w*+!\-\._?:<>\/\xa1-\uffff]/,
-        block_indent: /^(?:def|with)[^\/]+$|\/(?:def|with)/
-    };
-
-    function stateStack(indent, type, prev) { // represents a state stack object
-        this.indent = indent;
-        this.type = type;
-        this.prev = prev;
+    while (next = stream.next()) {
+      if (next === "\"" && !escaped) {state.tokenize = base; break;}
+      escaped = !escaped && next === "\\";
     }
 
-    function pushStack(state, indent, type) {
-        state.indentStack = new stateStack(indent, type, state.indentStack);
+    return [null, "string"];
+  }
+
+  function inComment(stream, state) {
+    var parenthesisCount = 1;
+    var next;
+
+    while (next = stream.next()) {
+      if (next === ")") parenthesisCount--;
+      if (next === "(") parenthesisCount++;
+      if (parenthesisCount === 0) {
+        stream.backUp(1);
+        state.tokenize = base;
+        break;
+      }
     }
 
-    function popStack(state) {
-        state.indentStack = state.indentStack.prev;
-    }
+    return ["space", "comment"];
+  }
 
-    function isNumber(ch, stream){
-        // hex
-        if ( ch === '0' && stream.eat(/x/i) ) {
-            stream.eatWhile(tests.hex);
-            return true;
+  function createLookupMap(words) {
+    var obj = {};
+
+    for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
+
+    return obj;
+  }
+
+  function is(value, test) {
+    if (test instanceof RegExp) return test.test(value);
+    if (test instanceof Object) return test.propertyIsEnumerable(value);
+  }
+
+  return {
+    startState: function () {
+      return {
+        ctx: {prev: null, start: 0, indentTo: 0},
+        lastToken: null,
+        tokenize: base
+      };
+    },
+
+    token: function (stream, state) {
+      if (stream.sol() && (typeof state.ctx.indentTo !== "number"))
+        state.ctx.indentTo = state.ctx.start + 1;
+
+      var typeStylePair = state.tokenize(stream, state);
+      var type = typeStylePair[0];
+      var style = typeStylePair[1];
+      var current = stream.current();
+
+      if (type !== "space") {
+        if (state.lastToken === "(" && state.ctx.indentTo === null) {
+          if (type === "symbol" && is(current, hasBodyParameter))
+            state.ctx.indentTo = state.ctx.start + options.indentUnit;
+          else state.ctx.indentTo = "next";
+        } else if (state.ctx.indentTo === "next") {
+          state.ctx.indentTo = stream.column();
         }
 
-        // leading sign
-        if ( ( ch == '+' || ch == '-' ) && ( tests.digit.test(stream.peek()) ) ) {
-          stream.eat(tests.sign);
-          ch = stream.next();
-        }
+        state.lastToken = current;
+      }
 
-        if ( tests.digit.test(ch) ) {
-            stream.eat(ch);
-            stream.eatWhile(tests.digit);
+      if (type === "open")
+        state.ctx = {prev: state.ctx, start: stream.column(), indentTo: null};
+      else if (type === "close") state.ctx = state.ctx.prev || state.ctx;
 
-            if ( '.' == stream.peek() ) {
-                stream.eat('.');
-                stream.eatWhile(tests.digit);
-            } else if ('/' == stream.peek() ) {
-                stream.eat('/');
-                stream.eatWhile(tests.digit);
-            }
+      return style;
+    },
 
-            if ( stream.eat(tests.exponent) ) {
-                stream.eat(tests.sign);
-                stream.eatWhile(tests.digit);
-            }
+    indent: function (state) {
+      var i = state.ctx.indentTo;
 
-            return true;
-        }
+      return (typeof i === "number") ?
+        i :
+        state.ctx.start + 1;
+    },
 
-        return false;
-    }
-
-    // Eat character that starts after backslash \
-    function eatCharacter(stream) {
-        var first = stream.next();
-        // Read special literals: backspace, newline, space, return.
-        // Just read all lowercase letters.
-        if (first && first.match(/[a-z]/) && stream.match(/[a-z]+/, true)) {
-            return;
-        }
-        // Read unicode character: \u1000 \uA0a1
-        if (first === "u") {
-            stream.match(/[0-9a-z]{4}/i, true);
-        }
-    }
-
-    return {
-        startState: function () {
-            return {
-                indentStack: null,
-                indentation: 0,
-                mode: false
-            };
-        },
-
-        token: function (stream, state) {
-            if (state.indentStack == null && stream.sol()) {
-                // update indentation, but only if indentStack is empty
-                state.indentation = stream.indentation();
-            }
-
-            // skip spaces
-            if (state.mode != "string" && stream.eatSpace()) {
-                return null;
-            }
-            var returnType = null;
-
-            switch(state.mode){
-                case "string": // multi-line string parsing mode
-                    var next, escaped = false;
-                    while ((next = stream.next()) != null) {
-                        if (next == "\"" && !escaped) {
-
-                            state.mode = false;
-                            break;
-                        }
-                        escaped = !escaped && next == "\\";
-                    }
-                    returnType = STRING; // continue on in string mode
-                    break;
-                default: // default parsing mode
-                    var ch = stream.next();
-
-                    if (ch == "\"") {
-                        state.mode = "string";
-                        returnType = STRING;
-                    } else if (ch == "\\") {
-                        eatCharacter(stream);
-                        returnType = CHARACTER;
-                    } else if (ch == "'" && !( tests.digit_or_colon.test(stream.peek()) )) {
-                        returnType = ATOM;
-                    } else if (ch == ";") { // comment
-                        stream.skipToEnd(); // rest of the line is a comment
-                        returnType = COMMENT;
-                    } else if (isNumber(ch,stream)){
-                        returnType = NUMBER;
-                    } else if (ch == "(" || ch == "[" || ch == "{" ) {
-                        var keyWord = '', indentTemp = stream.column(), letter;
-                        /**
-                        Either
-                        (indent-word ..
-                        (non-indent-word ..
-                        (;something else, bracket, etc.
-                        */
-
-                        if (ch == "(") while ((letter = stream.eat(tests.keyword_char)) != null) {
-                            keyWord += letter;
-                        }
-
-                        if (keyWord.length > 0 && (indentKeys.propertyIsEnumerable(keyWord) ||
-                                                   tests.block_indent.test(keyWord))) { // indent-word
-                            pushStack(state, indentTemp + INDENT_WORD_SKIP, ch);
-                        } else { // non-indent word
-                            // we continue eating the spaces
-                            stream.eatSpace();
-                            if (stream.eol() || stream.peek() == ";") {
-                                // nothing significant after
-                                // we restart indentation the user defined spaces after
-                                pushStack(state, indentTemp + NORMAL_INDENT_UNIT, ch);
-                            } else {
-                                pushStack(state, indentTemp + stream.current().length, ch); // else we match
-                            }
-                        }
-                        stream.backUp(stream.current().length - 1); // undo all the eating
-
-                        returnType = BRACKET;
-                    } else if (ch == ")" || ch == "]" || ch == "}") {
-                        returnType = BRACKET;
-                        if (state.indentStack != null && state.indentStack.type == (ch == ")" ? "(" : (ch == "]" ? "[" :"{"))) {
-                            popStack(state);
-                        }
-                    } else if ( ch == ":" ) {
-                        stream.eatWhile(tests.symbol);
-                        return ATOM;
-                    } else {
-                        stream.eatWhile(tests.symbol);
-
-                        if (keywords && keywords.propertyIsEnumerable(stream.current())) {
-                            returnType = KEYWORD;
-                        } else if (builtins && builtins.propertyIsEnumerable(stream.current())) {
-                            returnType = BUILTIN;
-                        } else if (atoms && atoms.propertyIsEnumerable(stream.current())) {
-                            returnType = ATOM;
-                        } else {
-                          returnType = VAR;
-                        }
-                    }
-            }
-
-            return returnType;
-        },
-
-        indent: function (state) {
-            if (state.indentStack == null) return state.indentation;
-            return state.indentStack.indent;
-        },
-
-        closeBrackets: {pairs: "()[]{}\"\""},
-        lineComment: ";;"
-    };
+    closeBrackets: {pairs: "()[]{}\"\""},
+    lineComment: ";;"
+  };
 });
 
 CodeMirror.defineMIME("text/x-clojure", "clojure");
diff --git a/public/vendor/plugins/codemirror/mode/clojure/index.html b/public/vendor/plugins/codemirror/mode/clojure/index.html
index 81294bc14a..ff9ac3ae62 100644
--- a/public/vendor/plugins/codemirror/mode/clojure/index.html
+++ b/public/vendor/plugins/codemirror/mode/clojure/index.html
@@ -6,10 +6,12 @@
 
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
+<script src="../../addon/edit/closebrackets.js"></script>
+<script src="../../addon/edit/matchbrackets.js"></script>
 <script src="clojure.js"></script>
 <style>.CodeMirror {background: #f8f8f8;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -25,65 +27,67 @@
 <article>
 <h2>Clojure mode</h2>
 <form><textarea id="code" name="code">
-; Conway's Game of Life, based on the work of:
-;; Laurent Petit https://gist.github.com/1200343
-;; Christophe Grand http://clj-me.cgrand.net/2011/08/19/conways-game-of-life
+(ns game-of-life
+  "Conway's Game of Life, based on the work of
+  Christophe Grand (http://clj-me.cgrand.net/2011/08/19/conways-game-of-life)
+  and Laurent Petit (https://gist.github.com/1200343).")
 
-(ns ^{:doc "Conway's Game of Life."}
- game-of-life)
+;;; Core game of life's algorithm functions
 
-;; Core game of life's algorithm functions
-
-(defn neighbours
-  "Given a cell's coordinates, returns the coordinates of its neighbours."
+(defn neighbors
+  "Given a cell's coordinates `[x y]`, returns the coordinates of its
+  neighbors."
   [[x y]]
-  (for [dx [-1 0 1] dy (if (zero? dx) [-1 1] [-1 0 1])]
+  (for [dx [-1 0 1]
+        dy (if (zero? dx)
+             [-1 1]
+             [-1 0 1])]
     [(+ dx x) (+ dy y)]))
 
 (defn step
-  "Given a set of living cells, computes the new set of living cells."
+  "Given a set of living `cells`, computes the new set of living cells."
   [cells]
-  (set (for [[cell n] (frequencies (mapcat neighbours cells))
-             :when (or (= n 3) (and (= n 2) (cells cell)))]
+  (set (for [[cell n] (frequencies (mapcat neighbors cells))
+             :when (or (= n 3)
+                       (and (= n 2)
+                            (cells cell)))]
          cell)))
 
-;; Utility methods for displaying game on a text terminal
+;;; Utility methods for displaying game on a text terminal
 
-(defn print-board
-  "Prints a board on *out*, representing a step in the game."
-  [board w h]
-  (doseq [x (range (inc w)) y (range (inc h))]
-    (if (= y 0) (print "\n"))
-    (print (if (board [x y]) "[X]" " . "))))
+(defn print-grid
+  "Prints a `grid` of `w` columns and `h` rows, on *out*, representing a
+  step in the game."
+  [grid w h]
+  (doseq [x (range (inc w))
+          y (range (inc h))]
+    (when (= y 0) (println))
+    (print (if (grid [x y])
+             "[X]"
+             " . "))))
 
-(defn display-grids
-  "Prints a squence of boards on *out*, representing several steps."
+(defn print-grids
+  "Prints a sequence of `grids` of `w` columns and `h` rows on *out*,
+  representing several steps."
   [grids w h]
-  (doseq [board grids]
-    (print-board board w h)
-    (print "\n")))
+  (doseq [grid grids]
+    (print-grid grid w h)
+    (println)))
 
-;; Launches an example board
+;;; Launches an example grid
 
-(def
-  ^{:doc "board represents the initial set of living cells"}
-   board #{[2 1] [2 2] [2 3]})
+(def grid
+  "`grid` represents the initial set of living cells"
+  #{[2 1] [2 2] [2 3]})
 
-(display-grids (take 3 (iterate step board)) 5 5)
-
-;; Let's play with characters
-(println \1 \a \# \\
-         \" \( \newline
-         \} \" \space
-         \tab \return \backspace
-         \u1000 \uAaAa \u9F9F)
-
-;; Let's play with numbers
-(+ 1 -1 1/2 -1/2 -0.5 0.5)
-
-</textarea></form>
+(print-grids (take 3 (iterate step grid)) 5 5)</textarea></form>
     <script>
-      var editor = CodeMirror.fromTextArea(document.getElementById("code"), {});
+      var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
+          autoCloseBrackets: true,
+          lineNumbers: true,
+          matchBrackets: true,
+          mode: 'text/x-clojure'
+      });
     </script>
 
     <p><strong>MIME types defined:</strong> <code>text/x-clojure</code>.</p>
diff --git a/public/vendor/plugins/codemirror/mode/clojure/test.js b/public/vendor/plugins/codemirror/mode/clojure/test.js
new file mode 100644
index 0000000000..642473b283
--- /dev/null
+++ b/public/vendor/plugins/codemirror/mode/clojure/test.js
@@ -0,0 +1,384 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function () {
+    var mode = CodeMirror.getMode({indentUnit: 2}, "clojure");
+
+    function MT(name) {
+        test.mode(name, mode, Array.prototype.slice.call(arguments, 1));
+    }
+
+    MT("atoms",
+        "[atom false]",
+        "[atom nil]",
+        "[atom true]"
+    );
+
+    MT("keywords",
+        "[atom :foo]",
+        "[atom ::bar]",
+        "[atom :foo/bar]",
+        "[atom :foo.bar/baz]"
+    );
+
+    MT("numbers",
+        "[number 42] [number +42] [number -421]",
+        "[number 42N] [number +42N] [number -42N]",
+        "[number 0.42] [number +0.42] [number -0.42]",
+        "[number 42M] [number +42M] [number -42M]",
+        "[number 42.42M] [number +42.42M] [number -42.42M]",
+        "[number 1/42] [number +1/42] [number -1/42]",
+        "[number 0x42af] [number +0x42af] [number -0x42af]",
+        "[number 0x42AF] [number +0x42AF] [number -0x42AF]",
+        "[number 1e2] [number 1e+2] [number 1e-2]",
+        "[number +1e2] [number +1e+2] [number +1e-2]",
+        "[number -1e2] [number -1e+2] [number -1e-2]",
+        "[number -1.0e2] [number -0.1e+2] [number -1.01e-2]",
+        "[number 1E2] [number 1E+2] [number 1E-2]",
+        "[number +1E2] [number +1E+2] [number +1E-2]",
+        "[number -1E2] [number -1E+2] [number -1E-2]",
+        "[number -1.0E2] [number -0.1E+2] [number -1.01E-2]",
+        "[number 2r101010] [number +2r101010] [number -2r101010]",
+        "[number 2r101010] [number +2r101010] [number -2r101010]",
+        "[number 8r52] [number +8r52] [number -8r52]",
+        "[number 36rhello] [number +36rhello] [number -36rhello]",
+        "[number 36rz] [number +36rz] [number -36rz]",
+        "[number 36rZ] [number +36rZ] [number -36rZ]",
+
+        // invalid numbers
+        "[error 42foo]",
+        "[error 42Nfoo]",
+        "[error 42Mfoo]",
+        "[error 42.42Mfoo]",
+        "[error 42.42M!]",
+        "[error 42!]",
+        "[error 0x42afm]"
+    );
+
+    MT("characters",
+        "[string-2 \\1]",
+        "[string-2 \\a]",
+        "[string-2 \\a\\b\\c]",
+        "[string-2 \\#]",
+        "[string-2 \\\\]",
+        "[string-2 \\\"]",
+        "[string-2 \\(]",
+        "[string-2 \\A]",
+        "[string-2 \\backspace]",
+        "[string-2 \\formfeed]",
+        "[string-2 \\newline]",
+        "[string-2 \\space]",
+        "[string-2 \\return]",
+        "[string-2 \\tab]",
+        "[string-2 \\u1000]",
+        "[string-2 \\uAaAa]",
+        "[string-2 \\u9F9F]",
+        "[string-2 \\o123]",
+        "[string-2 \\符]",
+        "[string-2 \\シ]",
+        "[string-2 \\ۇ]",
+        // FIXME
+        // "[string-2 \\🙂]",
+
+        // invalid character literals
+        "[error \\abc]",
+        "[error \\a123]",
+        "[error \\a!]",
+        "[error \\newlines]",
+        "[error \\NEWLINE]",
+        "[error \\u9F9FF]",
+        "[error \\o1234]"
+    );
+
+    MT("strings",
+        "[string \"I'm a teapot.\"]",
+        "[string \"I'm a \\\"teapot\\\".\"]",
+        "[string \"I'm]",       // this is
+        "[string a]",           // a multi-line
+        "[string teapot.\"]"    // string
+
+        // TODO unterminated (multi-line) strings?
+    );
+
+    MT("comments",
+        "[comment ; this is an in-line comment.]",
+        "[comment ;; this is a line comment.]",
+        "[keyword comment]",
+        "[bracket (][comment comment (foo 1 2 3)][bracket )]"
+    );
+
+    MT("reader macro characters",
+        "[meta #][variable _]",
+        "[meta #][variable -Inf]",
+        "[meta ##][variable Inf]",
+        "[meta ##][variable NaN]",
+        "[meta @][variable x]",
+        "[meta ^][bracket {][atom :tag] [variable String][bracket }]",
+        "[meta `][bracket (][builtin f] [variable x][bracket )]",
+        "[meta ~][variable foo#]",
+        "[meta '][number 1]",
+        "[meta '][atom :foo]",
+        "[meta '][string \"foo\"]",
+        "[meta '][variable x]",
+        "[meta '][bracket (][builtin a] [variable b] [variable c][bracket )]",
+        "[meta '][bracket [[][variable a] [variable b] [variable c][bracket ]]]",
+        "[meta '][bracket {][variable a] [number 1] [atom :foo] [number 2] [variable c] [number 3][bracket }]",
+        "[meta '#][bracket {][variable a] [number 1] [atom :foo][bracket }]"
+    );
+
+    MT("symbols",
+      "[variable foo!]",
+      "[variable foo#]",
+      "[variable foo$]",
+      "[variable foo&]",
+      "[variable foo']",
+      "[variable foo*]",
+      "[variable foo+]",
+      "[variable foo-]",
+      "[variable foo.]",
+      "[variable foo/bar]",
+      "[variable foo:bar]",
+      "[variable foo<]",
+      "[variable foo=]",
+      "[variable foo>]",
+      "[variable foo?]",
+      "[variable foo_]",
+      "[variable foo|]",
+      "[variable foobarBaz]",
+      "[variable foo¡]",
+      "[variable 符号]",
+      "[variable シンボル]",
+      "[variable ئۇيغۇر]",
+      "[variable 🙂❤🇺🇸]",
+
+      // invalid symbols
+      "[error 3foo]",
+      "[error 3+]",
+      "[error 3|]",
+      "[error 3_]"
+    );
+
+    MT("numbers and other forms",
+      "[number 42][bracket (][builtin foo][bracket )]",
+      "[number 42][bracket [[][variable foo][bracket ]]]",
+      "[number 42][meta #][bracket {][variable foo][bracket }]",
+      "[number 42][bracket {][atom :foo] [variable bar][bracket }]",
+      "[number 42][meta `][variable foo]",
+      "[number 42][meta ~][variable foo]",
+      "[number 42][meta #][variable foo]"
+    );
+
+    var specialForms = [".", "catch", "def", "do", "if", "monitor-enter",
+        "monitor-exit", "new", "quote", "recur", "set!", "throw", "try", "var"];
+
+    MT("should highlight special forms as keywords",
+        typeTokenPairs("keyword", specialForms)
+    );
+
+    var coreSymbols1 = [
+        "*", "*'", "*1", "*2", "*3", "*agent*", "*allow-unresolved-vars*", "*assert*",
+        "*clojure-version*", "*command-line-args*", "*compile-files*", "*compile-path*", "*compiler-options*",
+        "*data-readers*", "*default-data-reader-fn*", "*e", "*err*", "*file*", "*flush-on-newline*", "*fn-loader*",
+        "*in*", "*math-context*", "*ns*", "*out*", "*print-dup*", "*print-length*", "*print-level*", "*print-meta*",
+        "*print-namespace-maps*", "*print-readably*", "*read-eval*", "*reader-resolver*", "*source-path*",
+        "*suppress-read*", "*unchecked-math*", "*use-context-classloader*", "*verbose-defrecords*",
+        "*warn-on-reflection*", "+", "+'", "-", "-'", "->", "->>", "->ArrayChunk", "->Eduction", "->Vec", "->VecNode",
+        "->VecSeq", "-cache-protocol-fn", "-reset-methods", "..", "/", "<", "<=", "=", "==", ">", ">=",
+        "EMPTY-NODE", "Inst", "StackTraceElement->vec", "Throwable->map", "accessor", "aclone", "add-classpath",
+        "add-watch", "agent", "agent-error", "agent-errors", "aget", "alength", "alias", "all-ns", "alter",
+        "alter-meta!", "alter-var-root", "amap", "ancestors", "and", "any?", "apply", "areduce", "array-map",
+        "as->", "aset", "aset-boolean", "aset-byte", "aset-char", "aset-double", "aset-float", "aset-int",
+        "aset-long", "aset-short", "assert", "assoc", "assoc!", "assoc-in", "associative?", "atom", "await",
+        "await-for", "await1", "bases", "bean", "bigdec", "bigint", "biginteger", "binding", "bit-and", "bit-and-not",
+        "bit-clear", "bit-flip", "bit-not", "bit-or", "bit-set", "bit-shift-left", "bit-shift-right", "bit-test",
+        "bit-xor", "boolean", "boolean-array", "boolean?", "booleans", "bound-fn", "bound-fn*", "bound?",
+        "bounded-count", "butlast", "byte", "byte-array", "bytes", "bytes?", "case", "cast", "cat", "char",
+        "char-array", "char-escape-string", "char-name-string", "char?", "chars", "chunk", "chunk-append",
+        "chunk-buffer", "chunk-cons", "chunk-first", "chunk-next", "chunk-rest", "chunked-seq?", "class", "class?",
+        "clear-agent-errors", "clojure-version", "coll?", "comment", "commute", "comp", "comparator", "compare",
+        "compare-and-set!", "compile", "complement", "completing", "concat", "cond", "cond->", "cond->>", "condp",
+        "conj", "conj!", "cons", "constantly", "construct-proxy", "contains?", "count", "counted?", "create-ns",
+        "create-struct", "cycle", "dec", "dec'", "decimal?", "declare", "dedupe", "default-data-readers", "definline",
+        "definterface", "defmacro", "defmethod", "defmulti", "defn", "defn-", "defonce", "defprotocol", "defrecord",
+        "defstruct", "deftype", "delay", "delay?", "deliver", "denominator", "deref", "derive", "descendants",
+        "destructure", "disj", "disj!", "dissoc", "dissoc!", "distinct", "distinct?", "doall", "dorun", "doseq",
+        "dosync", "dotimes", "doto", "double", "double-array", "double?", "doubles", "drop", "drop-last", "drop-while",
+        "eduction", "empty", "empty?", "ensure", "ensure-reduced", "enumeration-seq", "error-handler", "error-mode",
+        "eval", "even?", "every-pred", "every?", "ex-data", "ex-info", "extend", "extend-protocol", "extend-type",
+        "extenders", "extends?", "false?", "ffirst", "file-seq", "filter", "filterv", "find", "find-keyword", "find-ns",
+        "find-protocol-impl", "find-protocol-method", "find-var", "first", "flatten", "float", "float-array", "float?",
+        "floats", "flush", "fn", "fn?", "fnext", "fnil", "for", "force", "format", "frequencies", "future", "future-call",
+        "future-cancel", "future-cancelled?", "future-done?", "future?", "gen-class", "gen-interface", "gensym", "get",
+        "get-in", "get-method", "get-proxy-class", "get-thread-bindings", "get-validator", "group-by", "halt-when", "hash",
+        "hash-combine", "hash-map", "hash-ordered-coll", "hash-set", "hash-unordered-coll", "ident?", "identical?",
+        "identity", "if-let", "if-not", "if-some", "ifn?", "import", "in-ns", "inc", "inc'", "indexed?", "init-proxy",
+        "inst-ms", "inst-ms*", "inst?", "instance?", "int", "int-array", "int?", "integer?", "interleave", "intern",
+        "interpose", "into", "into-array", "ints", "io!", "isa?", "iterate", "iterator-seq", "juxt", "keep", "keep-indexed",
+        "key", "keys", "keyword", "keyword?", "last", "lazy-cat", "lazy-seq", "let", "letfn", "line-seq", "list", "list*",
+        "list?", "load", "load-file", "load-reader", "load-string", "loaded-libs", "locking", "long", "long-array", "longs",
+        "loop", "macroexpand", "macroexpand-1", "make-array", "make-hierarchy", "map", "map-entry?", "map-indexed", "map?",
+        "mapcat", "mapv", "max", "max-key", "memfn", "memoize", "merge", "merge-with", "meta", "method-sig", "methods"];
+
+    var coreSymbols2 = [
+        "min", "min-key", "mix-collection-hash", "mod", "munge", "name", "namespace", "namespace-munge", "nat-int?",
+        "neg-int?", "neg?", "newline", "next", "nfirst", "nil?", "nnext", "not", "not-any?", "not-empty", "not-every?",
+        "not=", "ns", "ns-aliases", "ns-imports", "ns-interns", "ns-map", "ns-name", "ns-publics", "ns-refers", "ns-resolve",
+        "ns-unalias", "ns-unmap", "nth", "nthnext", "nthrest", "num", "number?", "numerator", "object-array", "odd?", "or",
+        "parents", "partial", "partition", "partition-all", "partition-by", "pcalls", "peek", "persistent!", "pmap", "pop",
+        "pop!", "pop-thread-bindings", "pos-int?", "pos?", "pr", "pr-str", "prefer-method", "prefers",
+        "primitives-classnames", "print", "print-ctor", "print-dup", "print-method", "print-simple", "print-str", "printf",
+        "println", "println-str", "prn", "prn-str", "promise", "proxy", "proxy-call-with-super", "proxy-mappings",
+        "proxy-name", "proxy-super", "push-thread-bindings", "pvalues", "qualified-ident?", "qualified-keyword?",
+        "qualified-symbol?", "quot", "rand", "rand-int", "rand-nth", "random-sample", "range", "ratio?", "rational?",
+        "rationalize", "re-find", "re-groups", "re-matcher", "re-matches", "re-pattern", "re-seq", "read", "read-line",
+        "read-string", "reader-conditional", "reader-conditional?", "realized?", "record?", "reduce", "reduce-kv", "reduced",
+        "reduced?", "reductions", "ref", "ref-history-count", "ref-max-history", "ref-min-history", "ref-set", "refer",
+        "refer-clojure", "reify", "release-pending-sends", "rem", "remove", "remove-all-methods", "remove-method", "remove-ns",
+        "remove-watch", "repeat", "repeatedly", "replace", "replicate", "require", "reset!", "reset-meta!", "reset-vals!",
+        "resolve", "rest", "restart-agent", "resultset-seq", "reverse", "reversible?", "rseq", "rsubseq", "run!", "satisfies?",
+        "second", "select-keys", "send", "send-off", "send-via", "seq", "seq?", "seqable?", "seque", "sequence", "sequential?",
+        "set", "set-agent-send-executor!", "set-agent-send-off-executor!", "set-error-handler!", "set-error-mode!",
+        "set-validator!", "set?", "short", "short-array", "shorts", "shuffle", "shutdown-agents", "simple-ident?",
+        "simple-keyword?", "simple-symbol?", "slurp", "some", "some->", "some->>", "some-fn", "some?", "sort", "sort-by",
+        "sorted-map", "sorted-map-by", "sorted-set", "sorted-set-by", "sorted?", "special-symbol?", "spit", "split-at",
+        "split-with", "str", "string?", "struct", "struct-map", "subs", "subseq", "subvec", "supers", "swap!", "swap-vals!",
+        "symbol", "symbol?", "sync", "tagged-literal", "tagged-literal?", "take", "take-last", "take-nth", "take-while", "test",
+        "the-ns", "thread-bound?", "time", "to-array", "to-array-2d", "trampoline", "transduce", "transient", "tree-seq",
+        "true?", "type", "unchecked-add", "unchecked-add-int", "unchecked-byte", "unchecked-char", "unchecked-dec",
+        "unchecked-dec-int", "unchecked-divide-int", "unchecked-double", "unchecked-float", "unchecked-inc", "unchecked-inc-int",
+        "unchecked-int", "unchecked-long", "unchecked-multiply", "unchecked-multiply-int", "unchecked-negate",
+        "unchecked-negate-int", "unchecked-remainder-int", "unchecked-short", "unchecked-subtract", "unchecked-subtract-int",
+        "underive", "unquote", "unquote-splicing", "unreduced", "unsigned-bit-shift-right", "update", "update-in",
+        "update-proxy", "uri?", "use", "uuid?", "val", "vals", "var-get", "var-set", "var?", "vary-meta", "vec", "vector",
+        "vector-of", "vector?", "volatile!", "volatile?", "vreset!", "vswap!", "when", "when-first", "when-let", "when-not",
+        "when-some", "while", "with-bindings", "with-bindings*", "with-in-str", "with-loading-context", "with-local-vars",
+        "with-meta", "with-open", "with-out-str", "with-precision", "with-redefs", "with-redefs-fn", "xml-seq", "zero?",
+        "zipmap"
+    ];
+
+    MT("should highlight core symbols as keywords (part 1/2)",
+        typeTokenPairs("keyword", coreSymbols1)
+    );
+
+    MT("should highlight core symbols as keywords (part 2/2)",
+        typeTokenPairs("keyword", coreSymbols2)
+    );
+
+    MT("should properly indent forms in list literals",
+        "[bracket (][builtin foo] [atom :a] [number 1] [atom true] [atom nil][bracket )]",
+        "",
+        "[bracket (][builtin foo] [atom :a]",
+        "     [number 1]",
+        "     [atom true]",
+        "     [atom nil][bracket )]",
+        "",
+        "[bracket (][builtin foo] [atom :a] [number 1]",
+        "     [atom true]",
+        "     [atom nil][bracket )]",
+        "",
+        "[bracket (]",
+        " [builtin foo]",
+        " [atom :a]",
+        " [number 1]",
+        " [atom true]",
+        " [atom nil][bracket )]",
+        "",
+        "[bracket (][builtin foo] [bracket [[][atom :a][bracket ]]]",
+        "     [number 1]",
+        "     [atom true]",
+        "     [atom nil][bracket )]"
+    );
+
+    MT("should properly indent forms in vector literals",
+        "[bracket [[][atom :a] [number 1] [atom true] [atom nil][bracket ]]]",
+        "",
+        "[bracket [[][atom :a]",
+        " [number 1]",
+        " [atom true]",
+        " [atom nil][bracket ]]]",
+        "",
+        "[bracket [[][atom :a] [number 1]",
+        " [atom true]",
+        " [atom nil][bracket ]]]",
+        "",
+        "[bracket [[]",
+        " [variable foo]",
+        " [atom :a]",
+        " [number 1]",
+        " [atom true]",
+        " [atom nil][bracket ]]]"
+    );
+
+    MT("should properly indent forms in map literals",
+        "[bracket {][atom :a] [atom :a] [atom :b] [number 1] [atom :c] [atom true] [atom :d] [atom nil] [bracket }]",
+        "",
+        "[bracket {][atom :a] [atom :a]",
+        " [atom :b] [number 1]",
+        " [atom :c] [atom true]",
+        " [atom :d] [atom nil][bracket }]",
+        "",
+        "[bracket {][atom :a]",
+        " [atom :a]",
+        " [atom :b]",
+        " [number 1]",
+        " [atom :c]",
+        " [atom true]",
+        " [atom :d]",
+        " [atom nil][bracket }]",
+        "",
+        "[bracket {]",
+        " [atom :a] [atom :a]",
+        " [atom :b] [number 1]",
+        " [atom :c] [atom true]",
+        " [atom :d] [atom nil][bracket }]"
+    );
+
+    MT("should properly indent forms in set literals",
+        "[meta #][bracket {][atom :a] [number 1] [atom true] [atom nil] [bracket }]",
+        "",
+        "[meta #][bracket {][atom :a]",
+        "  [number 1]",
+        "  [atom true]",
+        "  [atom nil][bracket }]",
+        "",
+        "[meta #][bracket {]",
+        "  [atom :a]",
+        "  [number 1]",
+        "  [atom true]",
+        "  [atom nil][bracket }]"
+    );
+
+    var haveBodyParameter = [
+        "->", "->>", "as->", "binding", "bound-fn", "case", "catch", "cond",
+        "cond->", "cond->>", "condp", "def", "definterface", "defmethod", "defn",
+        "defmacro", "defprotocol", "defrecord", "defstruct", "deftype", "do",
+        "doseq", "dotimes", "doto", "extend", "extend-protocol", "extend-type",
+        "fn", "for", "future", "if", "if-let", "if-not", "if-some", "let",
+        "letfn", "locking", "loop", "ns", "proxy", "reify", "some->", "some->>",
+        "struct-map", "try", "when", "when-first", "when-let", "when-not",
+        "when-some", "while", "with-bindings", "with-bindings*", "with-in-str",
+        "with-loading-context", "with-local-vars", "with-meta", "with-open",
+        "with-out-str", "with-precision", "with-redefs", "with-redefs-fn"];
+
+    function testFormsThatHaveBodyParameter(forms) {
+        for (var i = 0; i < forms.length; i++) {
+            MT("should indent body argument of `" + forms[i] + "` by `options.indentUnit` spaces",
+                "[bracket (][keyword " + forms[i] + "] [variable foo] [variable bar]",
+                "  [variable baz]",
+                "  [variable qux][bracket )]"
+            );
+        }
+    }
+
+    testFormsThatHaveBodyParameter(haveBodyParameter);
+
+    MT("should indent body argument of `comment` by `options.indentUnit` spaces",
+        "[bracket (][comment comment foo bar]",
+        "[comment  baz]",
+        "[comment  qux][bracket )]"
+    );
+
+    function typeTokenPairs(type, tokens) {
+        return "[" + type + " " + tokens.join("] [" + type + " ") + "]";
+    }
+})();
diff --git a/public/vendor/plugins/codemirror/mode/cmake/cmake.js b/public/vendor/plugins/codemirror/mode/cmake/cmake.js
index 9f9eda5417..496c71d1fd 100644
--- a/public/vendor/plugins/codemirror/mode/cmake/cmake.js
+++ b/public/vendor/plugins/codemirror/mode/cmake/cmake.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object")
diff --git a/public/vendor/plugins/codemirror/mode/cmake/index.html b/public/vendor/plugins/codemirror/mode/cmake/index.html
index ed114fece5..6dcfd7b4a8 100644
--- a/public/vendor/plugins/codemirror/mode/cmake/index.html
+++ b/public/vendor/plugins/codemirror/mode/cmake/index.html
@@ -13,7 +13,7 @@
       .cm-s-default span.cm-arrow { color: red; }
     </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/cobol/cobol.js b/public/vendor/plugins/codemirror/mode/cobol/cobol.js
index 897022b18c..275857b4bd 100644
--- a/public/vendor/plugins/codemirror/mode/cobol/cobol.js
+++ b/public/vendor/plugins/codemirror/mode/cobol/cobol.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 /**
  * Author: Gautam Mehta
diff --git a/public/vendor/plugins/codemirror/mode/cobol/index.html b/public/vendor/plugins/codemirror/mode/cobol/index.html
index 4352419a0c..1e08740529 100644
--- a/public/vendor/plugins/codemirror/mode/cobol/index.html
+++ b/public/vendor/plugins/codemirror/mode/cobol/index.html
@@ -39,7 +39,7 @@
         .CodeMirror-activeline-background {background: #555555 !important;}
     </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/coffeescript/coffeescript.js b/public/vendor/plugins/codemirror/mode/coffeescript/coffeescript.js
index adf2184fd7..a54e9d5ed0 100644
--- a/public/vendor/plugins/codemirror/mode/coffeescript/coffeescript.js
+++ b/public/vendor/plugins/codemirror/mode/coffeescript/coffeescript.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 /**
  * Link to the project's GitHub page:
@@ -349,6 +349,10 @@ CodeMirror.defineMode("coffeescript", function(conf, parserConf) {
   return external;
 });
 
+// IANA registered media type
+// https://www.iana.org/assignments/media-types/
+CodeMirror.defineMIME("application/vnd.coffeescript", "coffeescript");
+
 CodeMirror.defineMIME("text/x-coffeescript", "coffeescript");
 CodeMirror.defineMIME("text/coffeescript", "coffeescript");
 
diff --git a/public/vendor/plugins/codemirror/mode/coffeescript/index.html b/public/vendor/plugins/codemirror/mode/coffeescript/index.html
index 93a5f4f309..650ccf5db8 100644
--- a/public/vendor/plugins/codemirror/mode/coffeescript/index.html
+++ b/public/vendor/plugins/codemirror/mode/coffeescript/index.html
@@ -9,7 +9,7 @@
 <script src="coffeescript.js"></script>
 <style>.CodeMirror {border-top: 1px solid silver; border-bottom: 1px solid silver;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -733,7 +733,7 @@ wrapper::value = -> this._wrapped
       var editor = CodeMirror.fromTextArea(document.getElementById("code"), {});
     </script>
 
-    <p><strong>MIME types defined:</strong> <code>text/x-coffeescript</code>.</p>
+    <p><strong>MIME types defined:</strong> <code>application/vnd.coffeescript</code>, <code>text/coffeescript</code>, <code>text/x-coffeescript</code>.</p>
 
     <p>The CoffeeScript mode was written by Jeff Pickhardt.</p>
 
diff --git a/public/vendor/plugins/codemirror/mode/commonlisp/commonlisp.js b/public/vendor/plugins/codemirror/mode/commonlisp/commonlisp.js
index fb1f99c631..52abbb25c6 100644
--- a/public/vendor/plugins/codemirror/mode/commonlisp/commonlisp.js
+++ b/public/vendor/plugins/codemirror/mode/commonlisp/commonlisp.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -43,11 +43,12 @@ CodeMirror.defineMode("commonlisp", function (config) {
       else { stream.skipToEnd(); return "error"; }
     } else if (ch == "#") {
       var ch = stream.next();
-      if (ch == "[") { type = "open"; return "bracket"; }
+      if (ch == "(") { type = "open"; return "bracket"; }
       else if (/[+\-=\.']/.test(ch)) return null;
       else if (/\d/.test(ch) && stream.match(/^\d*#/)) return null;
       else if (ch == "|") return (state.tokenize = inComment)(stream, state);
       else if (ch == ":") { readSym(stream); return "meta"; }
+      else if (ch == "\\") { stream.next(); readSym(stream); return "string-2" }
       else return "error";
     } else {
       var name = readSym(stream);
diff --git a/public/vendor/plugins/codemirror/mode/commonlisp/index.html b/public/vendor/plugins/codemirror/mode/commonlisp/index.html
index f2bf4522d6..a5b907e831 100644
--- a/public/vendor/plugins/codemirror/mode/commonlisp/index.html
+++ b/public/vendor/plugins/codemirror/mode/commonlisp/index.html
@@ -9,7 +9,7 @@
 <script src="commonlisp.js"></script>
 <style>.CodeMirror {background: #f8f8f8;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/crystal/crystal.js b/public/vendor/plugins/codemirror/mode/crystal/crystal.js
index e63627cee8..5c601c6ab3 100644
--- a/public/vendor/plugins/codemirror/mode/crystal/crystal.js
+++ b/public/vendor/plugins/codemirror/mode/crystal/crystal.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -29,26 +29,22 @@
     var types = /^[A-Z_\u009F-\uFFFF][a-zA-Z0-9_\u009F-\uFFFF]*/;
     var keywords = wordRegExp([
       "abstract", "alias", "as", "asm", "begin", "break", "case", "class", "def", "do",
-      "else", "elsif", "end", "ensure", "enum", "extend", "for", "fun", "if", "ifdef",
+      "else", "elsif", "end", "ensure", "enum", "extend", "for", "fun", "if",
       "include", "instance_sizeof", "lib", "macro", "module", "next", "of", "out", "pointerof",
-      "private", "protected", "rescue", "return", "require", "sizeof", "struct",
-      "super", "then", "type", "typeof", "union", "unless", "until", "when", "while", "with",
-      "yield", "__DIR__", "__FILE__", "__LINE__"
+      "private", "protected", "rescue", "return", "require", "select", "sizeof", "struct",
+      "super", "then", "type", "typeof", "uninitialized", "union", "unless", "until", "when", "while", "with",
+      "yield", "__DIR__", "__END_LINE__", "__FILE__", "__LINE__"
     ]);
     var atomWords = wordRegExp(["true", "false", "nil", "self"]);
     var indentKeywordsArray = [
       "def", "fun", "macro",
       "class", "module", "struct", "lib", "enum", "union",
-      "if", "unless", "case", "while", "until", "begin", "then",
-      "do",
-      "for", "ifdef"
+      "do", "for"
     ];
     var indentKeywords = wordRegExp(indentKeywordsArray);
-    var dedentKeywordsArray = [
-      "end",
-      "else", "elsif",
-      "rescue", "ensure"
-    ];
+    var indentExpressionKeywordsArray = ["if", "unless", "case", "while", "until", "begin", "then"];
+    var indentExpressionKeywords = wordRegExp(indentExpressionKeywordsArray);
+    var dedentKeywordsArray = ["end", "else", "elsif", "rescue", "ensure"];
     var dedentKeywords = wordRegExp(dedentKeywordsArray);
     var dedentPunctualsArray = ["\\)", "\\}", "\\]"];
     var dedentPunctuals = new RegExp("^(?:" + dedentPunctualsArray.join("|") + ")$");
@@ -90,12 +86,15 @@
         } else if (state.lastToken == ".") {
           return "property";
         } else if (keywords.test(matched)) {
-          if (state.lastToken != "abstract" && indentKeywords.test(matched)) {
-            if (!(matched == "fun" && state.blocks.indexOf("lib") >= 0)) {
+          if (indentKeywords.test(matched)) {
+            if (!(matched == "fun" && state.blocks.indexOf("lib") >= 0) && !(matched == "def" && state.lastToken == "abstract")) {
               state.blocks.push(matched);
               state.currentIndent += 1;
             }
-          } else if (dedentKeywords.test(matched)) {
+          } else if ((state.lastStyle == "operator" || !state.lastStyle) && indentExpressionKeywords.test(matched)) {
+            state.blocks.push(matched);
+            state.currentIndent += 1;
+          } else if (matched == "end") {
             state.blocks.pop();
             state.currentIndent -= 1;
           }
@@ -124,12 +123,6 @@
         return "variable-2";
       }
 
-      // Global variables
-      if (stream.eat("$")) {
-        stream.eat(/[0-9]+|\?/) || stream.match(idents) || stream.match(types);
-        return "variable-3";
-      }
-
       // Constants and types
       if (stream.match(types)) {
         return "tag";
@@ -165,6 +158,9 @@
         } else if (stream.match("%w")) {
           embed = false;
           delim = stream.next();
+        } else if (stream.match("%q")) {
+          embed = false;
+          delim = stream.next();
         } else {
           if(delim = stream.match(/^%([^\w\s=])/)) {
             delim = delim[1];
@@ -183,6 +179,11 @@
         return chain(tokenQuote(delim, style, embed), stream, state);
       }
 
+      // Here Docs
+      if (matched = stream.match(/^<<-('?)([A-Z]\w*)\1/)) {
+        return chain(tokenHereDoc(matched[2], !matched[1]), stream, state)
+      }
+
       // Characters
       if (stream.eat("'")) {
         stream.match(/^(?:[^']|\\(?:[befnrtv0'"]|[0-7]{3}|u(?:[0-9a-fA-F]{4}|\{[0-9a-fA-F]{1,6}\})))/);
@@ -202,7 +203,7 @@
         return "number";
       }
 
-      if (stream.eat(/\d/)) {
+      if (stream.eat(/^\d/)) {
         stream.match(/^\d*(?:\.\d+)?(?:[eE][+-]?\d+)?/);
         return "number";
       }
@@ -339,7 +340,7 @@
               return style;
             }
 
-            escaped = ch == "\\";
+            escaped = embed && ch == "\\";
           } else {
             stream.next();
             escaped = false;
@@ -350,12 +351,52 @@
       };
     }
 
+    function tokenHereDoc(phrase, embed) {
+      return function (stream, state) {
+        if (stream.sol()) {
+          stream.eatSpace()
+          if (stream.match(phrase)) {
+            state.tokenize.pop();
+            return "string";
+          }
+        }
+
+        var escaped = false;
+        while (stream.peek()) {
+          if (!escaped) {
+            if (stream.match("{%", false)) {
+              state.tokenize.push(tokenMacro("%", "%"));
+              return "string";
+            }
+
+            if (stream.match("{{", false)) {
+              state.tokenize.push(tokenMacro("{", "}"));
+              return "string";
+            }
+
+            if (embed && stream.match("#{", false)) {
+              state.tokenize.push(tokenNest("#{", "}", "meta"));
+              return "string";
+            }
+
+            escaped = embed && stream.next() == "\\";
+          } else {
+            stream.next();
+            escaped = false;
+          }
+        }
+
+        return "string";
+      }
+    }
+
     return {
       startState: function () {
         return {
           tokenize: [tokenBase],
           currentIndent: 0,
           lastToken: null,
+          lastStyle: null,
           blocks: []
         };
       },
@@ -366,6 +407,7 @@
 
         if (style && style != "comment") {
           state.lastToken = token;
+          state.lastStyle = style;
         }
 
         return style;
diff --git a/public/vendor/plugins/codemirror/mode/crystal/index.html b/public/vendor/plugins/codemirror/mode/crystal/index.html
index ec03e25094..ae4218eec5 100644
--- a/public/vendor/plugins/codemirror/mode/crystal/index.html
+++ b/public/vendor/plugins/codemirror/mode/crystal/index.html
@@ -14,7 +14,7 @@
 </style>
 
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -48,8 +48,6 @@ puts "Listening on http://0.0.0.0:8080"
 server.listen
 
 module Foo
-  def initialize(@foo); end
-
   abstract def abstract_method : String
 
   @[AlwaysInline]
@@ -58,7 +56,8 @@ module Foo
   end
 
   struct Foo
-    def initialize(@foo); end
+    def initialize(@foo : ::Foo)
+    end
 
     def hello_world
       @foo.abstract_method
@@ -71,8 +70,7 @@ class Bar
 
   @@foobar = 12345
 
-  def initialize(@bar)
-    super(@bar.not_nil! + 100)
+  def initialize(@bar : Int32)
   end
 
   macro alias_method(name, method)
@@ -87,11 +85,10 @@ class Bar
 
   alias_method abstract_method, a_method
 
-  macro def show_instance_vars : Nil
+  def show_instance_vars : Nil
     {% for var in @type.instance_vars %}
       puts "@{{ var }} = #{ @{{ var }} }"
     {% end %}
-    nil
   end
 end
 
@@ -101,9 +98,9 @@ lib LibC
   fun c_puts = "puts"(str : Char*) : Int
 end
 
-$baz = Baz.new(100)
-$baz.show_instance_vars
-$baz.with_foofoo do
+baz = Baz.new(100)
+baz.show_instance_vars
+baz.with_foofoo do
   LibC.c_puts hello_world
 end
 </textarea></form>
diff --git a/public/vendor/plugins/codemirror/mode/css/css.js b/public/vendor/plugins/codemirror/mode/css/css.js
index ea7bd01d84..05742c5c45 100644
--- a/public/vendor/plugins/codemirror/mode/css/css.js
+++ b/public/vendor/plugins/codemirror/mode/css/css.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -28,6 +28,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
       colorKeywords = parserConfig.colorKeywords || {},
       valueKeywords = parserConfig.valueKeywords || {},
       allowNested = parserConfig.allowNested,
+      lineComment = parserConfig.lineComment,
       supportsAtComponent = parserConfig.supportsAtComponent === true;
 
   var type, override;
@@ -62,7 +63,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
       if (/[\d.]/.test(stream.peek())) {
         stream.eatWhile(/[\w.%]/);
         return ret("number", "unit");
-      } else if (stream.match(/^-[\w\\\-]+/)) {
+      } else if (stream.match(/^-[\w\\\-]*/)) {
         stream.eatWhile(/[\w\\\-]/);
         if (stream.match(/^\s*:/, false))
           return ret("variable-2", "variable-definition");
@@ -76,12 +77,11 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
       return ret("qualifier", "qualifier");
     } else if (/[:;{}\[\]\(\)]/.test(ch)) {
       return ret(null, ch);
-    } else if ((ch == "u" && stream.match(/rl(-prefix)?\(/)) ||
-               (ch == "d" && stream.match("omain(")) ||
-               (ch == "r" && stream.match("egexp("))) {
-      stream.backUp(1);
-      state.tokenize = tokenParenthesized;
-      return ret("property", "word");
+    } else if (stream.match(/[\w-.]+(?=\()/)) {
+      if (/^(url(-prefix)?|domain|regexp)$/.test(stream.current().toLowerCase())) {
+        state.tokenize = tokenParenthesized;
+      }
+      return ret("variable callee", "variable");
     } else if (/[\w\\\-]/.test(ch)) {
       stream.eatWhile(/[\w\\\-]/);
       return ret("property", "word");
@@ -161,16 +161,16 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
       return pushContext(state, stream, "block");
     } else if (type == "}" && state.context.prev) {
       return popContext(state);
-    } else if (supportsAtComponent && /@component/.test(type)) {
+    } else if (supportsAtComponent && /@component/i.test(type)) {
       return pushContext(state, stream, "atComponentBlock");
-    } else if (/^@(-moz-)?document$/.test(type)) {
+    } else if (/^@(-moz-)?document$/i.test(type)) {
       return pushContext(state, stream, "documentTypes");
-    } else if (/^@(media|supports|(-moz-)?document|import)$/.test(type)) {
+    } else if (/^@(media|supports|(-moz-)?document|import)$/i.test(type)) {
       return pushContext(state, stream, "atBlock");
-    } else if (/^@(font-face|counter-style)/.test(type)) {
+    } else if (/^@(font-face|counter-style)/i.test(type)) {
       state.stateArg = type;
       return "restricted_atBlock_before";
-    } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/.test(type)) {
+    } else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(type)) {
       return "keyframes";
     } else if (type && type.charAt(0) == "@") {
       return pushContext(state, stream, "at");
@@ -253,6 +253,8 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
   };
 
   states.pseudo = function(type, stream, state) {
+    if (type == "meta") return "pseudo";
+
     if (type == "word") {
       override = "variable-3";
       return state.context.type;
@@ -380,7 +382,8 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
         style = style[0];
       }
       override = style;
-      state.state = states[state.state](type, stream, state);
+      if (type != "comment")
+        state.state = states[state.state](type, stream, state);
       return override;
     },
 
@@ -398,7 +401,6 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
             ch == "{" && (cx.type == "at" || cx.type == "atBlock")) {
           // Dedent relative to current context.
           indent = Math.max(0, cx.indent - indentUnit);
-          cx = cx.prev;
         }
       }
       return indent;
@@ -407,6 +409,8 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
     electricChars: "}",
     blockCommentStart: "/*",
     blockCommentEnd: "*/",
+    blockCommentContinue: " * ",
+    lineComment: lineComment,
     fold: "brace"
   };
 });
@@ -414,7 +418,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
   function keySet(array) {
     var keys = {};
     for (var i = 0; i < array.length; ++i) {
-      keys[array[i]] = true;
+      keys[array[i].toLowerCase()] = true;
     }
     return keys;
   }
@@ -468,7 +472,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
     "border-top-left-radius", "border-top-right-radius", "border-top-style",
     "border-top-width", "border-width", "bottom", "box-decoration-break",
     "box-shadow", "box-sizing", "break-after", "break-before", "break-inside",
-    "caption-side", "clear", "clip", "color", "color-profile", "column-count",
+    "caption-side", "caret-color", "clear", "clip", "color", "color-profile", "column-count",
     "column-fill", "column-gap", "column-rule", "column-rule-color",
     "column-rule-style", "column-rule-width", "column-span", "column-width",
     "columns", "content", "counter-increment", "counter-reset", "crop", "cue",
@@ -489,14 +493,14 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
     "grid-row-start", "grid-template", "grid-template-areas", "grid-template-columns",
     "grid-template-rows", "hanging-punctuation", "height", "hyphens",
     "icon", "image-orientation", "image-rendering", "image-resolution",
-    "inline-box-align", "justify-content", "left", "letter-spacing",
+    "inline-box-align", "justify-content", "justify-items", "justify-self", "left", "letter-spacing",
     "line-break", "line-height", "line-stacking", "line-stacking-ruby",
     "line-stacking-shift", "line-stacking-strategy", "list-style",
     "list-style-image", "list-style-position", "list-style-type", "margin",
     "margin-bottom", "margin-left", "margin-right", "margin-top",
-    "marker-offset", "marks", "marquee-direction", "marquee-loop",
+    "marks", "marquee-direction", "marquee-loop",
     "marquee-play-count", "marquee-speed", "marquee-style", "max-height",
-    "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index",
+    "max-width", "min-height", "min-width", "mix-blend-mode", "move-to", "nav-down", "nav-index",
     "nav-left", "nav-right", "nav-up", "object-fit", "object-position",
     "opacity", "order", "orphans", "outline",
     "outline-color", "outline-offset", "outline-style", "outline-width",
@@ -504,7 +508,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
     "padding", "padding-bottom", "padding-left", "padding-right", "padding-top",
     "page", "page-break-after", "page-break-before", "page-break-inside",
     "page-policy", "pause", "pause-after", "pause-before", "perspective",
-    "perspective-origin", "pitch", "pitch-range", "play-during", "position",
+    "perspective-origin", "pitch", "pitch-range", "place-content", "place-items", "place-self", "play-during", "position",
     "presentation-level", "punctuation-trim", "quotes", "region-break-after",
     "region-break-before", "region-break-inside", "region-fragment",
     "rendering-intent", "resize", "rest", "rest-after", "rest-before", "richness",
@@ -522,9 +526,9 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
     "text-wrap", "top", "transform", "transform-origin", "transform-style",
     "transition", "transition-delay", "transition-duration",
     "transition-property", "transition-timing-function", "unicode-bidi",
-    "vertical-align", "visibility", "voice-balance", "voice-duration",
+    "user-select", "vertical-align", "visibility", "voice-balance", "voice-duration",
     "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress",
-    "voice-volume", "volume", "white-space", "widows", "width", "word-break",
+    "voice-volume", "volume", "white-space", "widows", "width", "will-change", "word-break",
     "word-spacing", "word-wrap", "z-index",
     // SVG-specific
     "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color",
@@ -589,7 +593,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
     "above", "absolute", "activeborder", "additive", "activecaption", "afar",
     "after-white-space", "ahead", "alias", "all", "all-scroll", "alphabetic", "alternate",
     "always", "amharic", "amharic-abegede", "antialiased", "appworkspace",
-    "arabic-indic", "armenian", "asterisks", "attr", "auto", "avoid", "avoid-column", "avoid-page",
+    "arabic-indic", "armenian", "asterisks", "attr", "auto", "auto-flow", "avoid", "avoid-column", "avoid-page",
     "avoid-region", "background", "backwards", "baseline", "below", "bidi-override", "binary",
     "bengali", "blink", "block", "block-axis", "bold", "bolder", "border", "border-box",
     "both", "bottom", "break", "break-all", "break-word", "bullets", "button", "button-bevel",
@@ -598,7 +602,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
     "cell", "center", "checkbox", "circle", "cjk-decimal", "cjk-earthly-branch",
     "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote",
     "col-resize", "collapse", "color", "color-burn", "color-dodge", "column", "column-reverse",
-    "compact", "condensed", "contain", "content",
+    "compact", "condensed", "contain", "content", "contents",
     "content-box", "context-menu", "continuous", "copy", "counter", "counters", "cover", "crop",
     "cross", "crosshair", "currentcolor", "cursive", "cyclic", "darken", "dashed", "decimal",
     "decimal-leading-zero", "default", "default-button", "dense", "destination-atop",
@@ -641,7 +645,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
     "mix", "mongolian", "monospace", "move", "multiple", "multiply", "myanmar", "n-resize",
     "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop",
     "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap",
-    "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote",
+    "ns-resize", "numbers", "numeric", "nw-resize", "nwse-resize", "oblique", "octal", "opacity", "open-quote",
     "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset",
     "outside", "outside-shape", "overlay", "overline", "padding", "padding-box",
     "painted", "page", "paused", "persian", "perspective", "plus-darker", "plus-lighter",
@@ -653,17 +657,17 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
     "rgb", "rgba", "ridge", "right", "rotate", "rotate3d", "rotateX", "rotateY",
     "rotateZ", "round", "row", "row-resize", "row-reverse", "rtl", "run-in", "running",
     "s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen",
-    "scroll", "scrollbar", "se-resize", "searchfield",
+    "scroll", "scrollbar", "scroll-position", "se-resize", "searchfield",
     "searchfield-cancel-button", "searchfield-decoration",
-    "searchfield-results-button", "searchfield-results-decoration",
+    "searchfield-results-button", "searchfield-results-decoration", "self-start", "self-end",
     "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama",
     "simp-chinese-formal", "simp-chinese-informal", "single",
     "skew", "skewX", "skewY", "skip-white-space", "slide", "slider-horizontal",
     "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow",
     "small", "small-caps", "small-caption", "smaller", "soft-light", "solid", "somali",
-    "source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "spell-out", "square",
+    "source-atop", "source-in", "source-out", "source-over", "space", "space-around", "space-between", "space-evenly", "spell-out", "square",
     "square-button", "start", "static", "status-bar", "stretch", "stroke", "sub",
-    "subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "table",
+    "subpixel-antialiased", "super", "sw-resize", "symbolic", "symbols", "system-ui", "table",
     "table-caption", "table-cell", "table-column", "table-column-group",
     "table-footer-group", "table-header-group", "table-row", "table-row-group",
     "tamil",
@@ -671,9 +675,9 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
     "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight",
     "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er",
     "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top",
-    "trad-chinese-formal", "trad-chinese-informal",
+    "trad-chinese-formal", "trad-chinese-informal", "transform",
     "translate", "translate3d", "translateX", "translateY", "translateZ",
-    "transparent", "ultra-condensed", "ultra-expanded", "underline", "up",
+    "transparent", "ultra-condensed", "ultra-expanded", "underline", "unset", "up",
     "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal",
     "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url",
     "var", "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted",
@@ -730,6 +734,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
     valueKeywords: valueKeywords,
     fontProperties: fontProperties,
     allowNested: true,
+    lineComment: "//",
     tokenHooks: {
       "/": function(stream, state) {
         if (stream.eat("/")) {
@@ -743,8 +748,8 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
         }
       },
       ":": function(stream) {
-        if (stream.match(/\s*\{/))
-          return [null, "{"];
+        if (stream.match(/\s*\{/, false))
+          return [null, null]
         return false;
       },
       "$": function(stream) {
@@ -772,6 +777,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
     valueKeywords: valueKeywords,
     fontProperties: fontProperties,
     allowNested: true,
+    lineComment: "//",
     tokenHooks: {
       "/": function(stream, state) {
         if (stream.eat("/")) {
@@ -786,7 +792,7 @@ CodeMirror.defineMode("css", function(config, parserConfig) {
       },
       "@": function(stream) {
         if (stream.eat("{")) return [null, "interpolation"];
-        if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/, false)) return false;
+        if (stream.match(/^(charset|document|font-face|import|(-(moz|ms|o|webkit)-)?keyframes|media|namespace|page|supports)\b/i, false)) return false;
         stream.eatWhile(/[\w\\\-]/);
         if (stream.match(/^\s*:/, false))
           return ["variable-2", "variable-definition"];
diff --git a/public/vendor/plugins/codemirror/mode/css/gss.html b/public/vendor/plugins/codemirror/mode/css/gss.html
index 232fe8c12b..447929bf21 100644
--- a/public/vendor/plugins/codemirror/mode/css/gss.html
+++ b/public/vendor/plugins/codemirror/mode/css/gss.html
@@ -8,11 +8,12 @@
 <link rel="stylesheet" href="../../addon/hint/show-hint.css">
 <script src="../../lib/codemirror.js"></script>
 <script src="css.js"></script>
+<script src="../../addon/edit/matchbrackets.js"></script>
 <script src="../../addon/hint/show-hint.js"></script>
 <script src="../../addon/hint/css-hint.js"></script>
 <style>.CodeMirror {background: #f8f8f8;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -90,7 +91,7 @@ body {
       var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
         extraKeys: {"Ctrl-Space": "autocomplete"},
         lineNumbers: true,
-        matchBrackets: "text/x-less",
+        matchBrackets: true,
         mode: "text/x-gss"
       });
     </script>
diff --git a/public/vendor/plugins/codemirror/mode/css/gss_test.js b/public/vendor/plugins/codemirror/mode/css/gss_test.js
index d56e592803..2401bc4968 100644
--- a/public/vendor/plugins/codemirror/mode/css/gss_test.js
+++ b/public/vendor/plugins/codemirror/mode/css/gss_test.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function() {
   "use strict";
diff --git a/public/vendor/plugins/codemirror/mode/css/index.html b/public/vendor/plugins/codemirror/mode/css/index.html
index 2d2b9b073c..b39f126597 100644
--- a/public/vendor/plugins/codemirror/mode/css/index.html
+++ b/public/vendor/plugins/codemirror/mode/css/index.html
@@ -12,7 +12,7 @@
 <script src="../../addon/hint/css-hint.js"></script>
 <style>.CodeMirror {background: #f8f8f8;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -64,7 +64,7 @@ code {
 </textarea></form>
     <script>
       var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
-        extraKeys: {"Ctrl-Space": "autocomplete"},
+        extraKeys: {"Ctrl-Space": "autocomplete"}
       });
     </script>
 
diff --git a/public/vendor/plugins/codemirror/mode/css/less.html b/public/vendor/plugins/codemirror/mode/css/less.html
index adf7427d30..6ceb7a0342 100644
--- a/public/vendor/plugins/codemirror/mode/css/less.html
+++ b/public/vendor/plugins/codemirror/mode/css/less.html
@@ -10,7 +10,7 @@
 <script src="css.js"></script>
 <style>.CodeMirror {border: 1px solid #ddd; line-height: 1.2;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -140,8 +140,8 @@ fieldset span button, fieldset span input[type="file"] {
 </textarea></form>
     <script>
       var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
-        lineNumbers : true,
-        matchBrackets : true,
+        lineNumbers: true,
+        matchBrackets: true,
         mode: "text/x-less"
       });
     </script>
diff --git a/public/vendor/plugins/codemirror/mode/css/less_test.js b/public/vendor/plugins/codemirror/mode/css/less_test.js
index dd821558b0..abeb6a2040 100644
--- a/public/vendor/plugins/codemirror/mode/css/less_test.js
+++ b/public/vendor/plugins/codemirror/mode/css/less_test.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function() {
   "use strict";
@@ -10,8 +10,8 @@
   MT("variable",
      "[variable-2 @base]: [atom #f04615];",
      "[qualifier .class] {",
-     "  [property width]: [variable percentage]([number 0.5]); [comment // returns `50%`]",
-     "  [property color]: [variable saturate]([variable-2 @base], [number 5%]);",
+     "  [property width]: [variable&callee percentage]([number 0.5]); [comment // returns `50%`]",
+     "  [property color]: [variable&callee saturate]([variable-2 @base], [number 5%]);",
      "}");
 
   MT("amp",
@@ -26,10 +26,10 @@
 
   MT("mixin",
      "[qualifier .mixin] ([variable dark]; [variable-2 @color]) {",
-     "  [property color]: [atom darken]([variable-2 @color], [number 10%]);",
+     "  [property color]: [variable&callee darken]([variable-2 @color], [number 10%]);",
      "}",
      "[qualifier .mixin] ([variable light]; [variable-2 @color]) {",
-     "  [property color]: [atom lighten]([variable-2 @color], [number 10%]);",
+     "  [property color]: [variable&callee lighten]([variable-2 @color], [number 10%]);",
      "}",
      "[qualifier .mixin] ([variable-2 @_]; [variable-2 @color]) {",
      "  [property display]: [atom block];",
diff --git a/public/vendor/plugins/codemirror/mode/css/scss.html b/public/vendor/plugins/codemirror/mode/css/scss.html
index f8e4d37368..3e42ea6535 100644
--- a/public/vendor/plugins/codemirror/mode/css/scss.html
+++ b/public/vendor/plugins/codemirror/mode/css/scss.html
@@ -6,10 +6,11 @@
 
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
+<script src="../../addon/edit/matchbrackets.js"></script>
 <script src="css.js"></script>
 <style>.CodeMirror {background: #f8f8f8;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/css/scss_test.js b/public/vendor/plugins/codemirror/mode/css/scss_test.js
index 785921b39c..68afc664b6 100644
--- a/public/vendor/plugins/codemirror/mode/css/scss_test.js
+++ b/public/vendor/plugins/codemirror/mode/css/scss_test.js
@@ -1,24 +1,24 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function() {
   var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-scss");
   function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1), "scss"); }
 
   MT('url_with_quotation',
-    "[tag foo] { [property background]:[atom url]([string test.jpg]) }");
+    "[tag foo] { [property background]:[variable&callee url]([string test.jpg]) }");
 
   MT('url_with_double_quotes',
-    "[tag foo] { [property background]:[atom url]([string \"test.jpg\"]) }");
+    "[tag foo] { [property background]:[variable&callee url]([string \"test.jpg\"]) }");
 
   MT('url_with_single_quotes',
-    "[tag foo] { [property background]:[atom url]([string \'test.jpg\']) }");
+    "[tag foo] { [property background]:[variable&callee url]([string \'test.jpg\']) }");
 
   MT('string',
     "[def @import] [string \"compass/css3\"]");
 
   MT('important_keyword',
-    "[tag foo] { [property background]:[atom url]([string \'test.jpg\']) [keyword !important] }");
+    "[tag foo] { [property background]:[variable&callee url]([string \'test.jpg\']) [keyword !important] }");
 
   MT('variable',
     "[variable-2 $blue]:[atom #333]");
@@ -95,7 +95,7 @@
 
   MT('indent_parentheses',
      "[tag foo] {",
-     "  [property color]: [atom darken]([variable-2 $blue],",
+     "  [property color]: [variable&callee darken]([variable-2 $blue],",
      "    [number 9%]);",
      "}");
 
diff --git a/public/vendor/plugins/codemirror/mode/css/test.js b/public/vendor/plugins/codemirror/mode/css/test.js
index 7a496fb091..64352d74fc 100644
--- a/public/vendor/plugins/codemirror/mode/css/test.js
+++ b/public/vendor/plugins/codemirror/mode/css/test.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function() {
   var mode = CodeMirror.getMode({indentUnit: 2}, "css");
@@ -24,6 +24,9 @@
   MT("atMediaUnknownFeatureValueKeyword",
      "[def @media] ([property orientation]: [error upsidedown]) { }");
 
+  MT("atMediaUppercase",
+     "[def @MEDIA] ([property orienTAtion]: [keyword landScape]) { }");
+
   MT("tagSelector",
      "[tag foo] { }");
 
@@ -86,11 +89,11 @@
      "[tag foo] { [property margin]: [number 0]; [property padding]: [number 0]; }");
 
   MT("tagTwoPropertiesURL",
-     "[tag foo] { [property background]: [atom url]([string //example.com/foo.png]); [property padding]: [number 0]; }");
+     "[tag foo] { [property background]: [variable&callee url]([string //example.com/foo.png]); [property padding]: [number 0]; }");
 
   MT("indent_tagSelector",
      "[tag strong], [tag em] {",
-     "  [property background]: [atom rgba](",
+     "  [property background]: [variable&callee rgba](",
      "    [number 255], [number 255], [number 0], [number .2]",
      "  );",
      "}");
@@ -111,7 +114,7 @@
 
   MT("indent_parentheses",
      "[tag foo]:[variable-3 before] {",
-     "  [property background]: [atom url](",
+     "  [property background]: [variable&callee url](",
      "[string     blahblah]",
      "[string     etc]",
      "[string   ]) [keyword !important];",
@@ -121,20 +124,20 @@
      "[def @font-face] {",
      "  [property font-family]: [string 'myfont'];",
      "  [error nonsense]: [string 'abc'];",
-     "  [property src]: [atom url]([string http://blah]),",
-     "    [atom url]([string http://foo]);",
+     "  [property src]: [variable&callee url]([string http://blah]),",
+     "    [variable&callee url]([string http://foo]);",
      "}");
 
   MT("empty_url",
-     "[def @import] [atom url]() [attribute screen];");
+     "[def @import] [variable&callee url]() [attribute screen];");
 
   MT("parens",
      "[qualifier .foo] {",
-     "  [property background-image]: [variable fade]([atom #000], [number 20%]);",
-     "  [property border-image]: [atom linear-gradient](",
+     "  [property background-image]: [variable&callee fade]([atom #000], [number 20%]);",
+     "  [property border-image]: [variable&callee linear-gradient](",
      "    [atom to] [atom bottom],",
-     "    [variable fade]([atom #000], [number 20%]) [number 0%],",
-     "    [variable fade]([atom #000], [number 20%]) [number 100%]",
+     "    [variable&callee fade]([atom #000], [number 20%]) [number 0%],",
+     "    [variable&callee fade]([atom #000], [number 20%]) [number 100%]",
      "  );",
      "}");
 
@@ -143,7 +146,15 @@
      "  [variable-2 --main-color]: [atom #06c];",
      "}",
      "[tag h1][builtin #foo] {",
-     "  [property color]: [atom var]([variable-2 --main-color]);",
+     "  [property color]: [variable&callee var]([variable-2 --main-color]);",
+     "}");
+
+  MT("blank_css_variable",
+     ":[variable-3 root] {",
+     "  [variable-2 --]: [atom #06c];",
+     "}",
+     "[tag h1][builtin #foo] {",
+     "  [property color]: [variable&callee var]([variable-2 --]);",
      "}");
 
   MT("supports",
@@ -152,10 +163,10 @@
      "}");
 
    MT("document",
-      "[def @document] [tag url]([string http://blah]),",
-      "  [tag url-prefix]([string https://]),",
-      "  [tag domain]([string blah.com]),",
-      "  [tag regexp]([string \".*blah.+\"]) {",
+      "[def @document] [variable&callee url]([string http://blah]),",
+      "  [variable&callee url-prefix]([string https://]),",
+      "  [variable&callee domain]([string blah.com]),",
+      "  [variable&callee regexp]([string \".*blah.+\"]) {",
       "    [builtin #id] {",
       "      [property background-color]: [keyword white];",
       "    }",
@@ -165,16 +176,16 @@
       "}");
 
    MT("document_url",
-      "[def @document] [tag url]([string http://blah]) { [qualifier .class] { } }");
+      "[def @document] [variable&callee url]([string http://blah]) { [qualifier .class] { } }");
 
    MT("document_urlPrefix",
-      "[def @document] [tag url-prefix]([string https://]) { [builtin #id] { } }");
+      "[def @document] [variable&callee url-prefix]([string https://]) { [builtin #id] { } }");
 
    MT("document_domain",
-      "[def @document] [tag domain]([string blah.com]) { [tag foo] { } }");
+      "[def @document] [variable&callee domain]([string blah.com]) { [tag foo] { } }");
 
    MT("document_regexp",
-      "[def @document] [tag regexp]([string \".*blah.+\"]) { [builtin #id] { } }");
+      "[def @document] [variable&callee regexp]([string \".*blah.+\"]) { [builtin #id] { } }");
 
    MT("counter-style",
       "[def @counter-style] [variable binary] {",
@@ -196,5 +207,11 @@
       "[tag ol][qualifier .roman] { [property list-style]: [variable simple-roman]; }");
 
    MT("counter-style-symbols",
-      "[tag ol] { [property list-style]: [atom symbols]([atom cyclic] [string \"*\"] [string \"\\2020\"] [string \"\\2021\"] [string \"\\A7\"]); }");
+      "[tag ol] { [property list-style]: [variable&callee symbols]([atom cyclic] [string \"*\"] [string \"\\2020\"] [string \"\\2021\"] [string \"\\A7\"]); }");
+
+  MT("comment-does-not-disrupt",
+     "[def @font-face] [comment /* foo */] {",
+     "  [property src]: [variable&callee url]([string x]);",
+     "  [property font-family]: [variable One];",
+     "}")
 })();
diff --git a/public/vendor/plugins/codemirror/mode/cypher/cypher.js b/public/vendor/plugins/codemirror/mode/cypher/cypher.js
index 107e4f6d21..8b22e65db8 100644
--- a/public/vendor/plugins/codemirror/mode/cypher/cypher.js
+++ b/public/vendor/plugins/codemirror/mode/cypher/cypher.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 // By the Neo4j Team and contributors.
 // https://github.com/neo4j-contrib/CodeMirror
@@ -20,8 +20,12 @@
   CodeMirror.defineMode("cypher", function(config) {
     var tokenBase = function(stream/*, state*/) {
       var ch = stream.next();
-      if (ch === "\"" || ch === "'") {
-        stream.match(/.+?["']/);
+      if (ch ==='"') {
+        stream.match(/.*?"/);
+        return "string";
+      }
+      if (ch === "'") {
+        stream.match(/.*?'/);
         return "string";
       }
       if (/[{}\(\),\.;\[\]]/.test(ch)) {
@@ -62,7 +66,7 @@
     var curPunc;
     var funcs = wordRegexp(["abs", "acos", "allShortestPaths", "asin", "atan", "atan2", "avg", "ceil", "coalesce", "collect", "cos", "cot", "count", "degrees", "e", "endnode", "exp", "extract", "filter", "floor", "haversin", "head", "id", "keys", "labels", "last", "left", "length", "log", "log10", "lower", "ltrim", "max", "min", "node", "nodes", "percentileCont", "percentileDisc", "pi", "radians", "rand", "range", "reduce", "rel", "relationship", "relationships", "replace", "reverse", "right", "round", "rtrim", "shortestPath", "sign", "sin", "size", "split", "sqrt", "startnode", "stdev", "stdevp", "str", "substring", "sum", "tail", "tan", "timestamp", "toFloat", "toInt", "toString", "trim", "type", "upper"]);
     var preds = wordRegexp(["all", "and", "any", "contains", "exists", "has", "in", "none", "not", "or", "single", "xor"]);
-    var keywords = wordRegexp(["as", "asc", "ascending", "assert", "by", "case", "commit", "constraint", "create", "csv", "cypher", "delete", "desc", "descending", "detach", "distinct", "drop", "else", "end", "ends", "explain", "false", "fieldterminator", "foreach", "from", "headers", "in", "index", "is", "join", "limit", "load", "match", "merge", "null", "on", "optional", "order", "periodic", "profile", "remove", "return", "scan", "set", "skip", "start", "starts", "then", "true", "union", "unique", "unwind", "using", "when", "where", "with"]);
+    var keywords = wordRegexp(["as", "asc", "ascending", "assert", "by", "case", "commit", "constraint", "create", "csv", "cypher", "delete", "desc", "descending", "detach", "distinct", "drop", "else", "end", "ends", "explain", "false", "fieldterminator", "foreach", "from", "headers", "in", "index", "is", "join", "limit", "load", "match", "merge", "null", "on", "optional", "order", "periodic", "profile", "remove", "return", "scan", "set", "skip", "start", "starts", "then", "true", "union", "unique", "unwind", "using", "when", "where", "with", "call", "yield"]);
     var operatorChars = /[*+\-<>=&|~%^]/;
 
     return {
diff --git a/public/vendor/plugins/codemirror/mode/cypher/index.html b/public/vendor/plugins/codemirror/mode/cypher/index.html
index b8bd75c8b3..0db7a6a7c6 100644
--- a/public/vendor/plugins/codemirror/mode/cypher/index.html
+++ b/public/vendor/plugins/codemirror/mode/cypher/index.html
@@ -7,6 +7,7 @@
 <link rel="stylesheet" href="../../lib/codemirror.css" />
 <link rel="stylesheet" href="../../theme/neo.css" />
 <script src="../../lib/codemirror.js"></script>
+<script src="../../addon/edit/matchbrackets.js"></script>
 <script src="cypher.js"></script>
 <style>
 .CodeMirror {
@@ -15,7 +16,7 @@
 }
         </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -53,7 +54,7 @@ window.onload = function() {
     indentWithTabs: true,
     smartIndent: true,
     lineNumbers: true,
-    matchBrackets : true,
+    matchBrackets: true,
     autofocus: true,
     theme: 'neo'
   });
diff --git a/public/vendor/plugins/codemirror/mode/cypher/test.js b/public/vendor/plugins/codemirror/mode/cypher/test.js
new file mode 100644
index 0000000000..691835596f
--- /dev/null
+++ b/public/vendor/plugins/codemirror/mode/cypher/test.js
@@ -0,0 +1,37 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function() {
+  var mode = CodeMirror.getMode({tabSize: 4, indentUnit: 2}, "cypher");
+  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
+
+  MT("unbalancedDoubledQuotedString",
+      "[string \"a'b\"][variable c]");
+
+  MT("unbalancedSingleQuotedString",
+      "[string 'a\"b'][variable c]");
+
+  MT("doubleQuotedString",
+      "[string \"a\"][variable b]");
+
+  MT("singleQuotedString",
+      "[string 'a'][variable b]");
+
+  MT("single attribute (with content)",
+      "[node {][atom a:][string 'a'][node }]");
+
+  MT("multiple attribute, singleQuotedString (with content)",
+      "[node {][atom a:][string 'a'][node ,][atom b:][string 'b'][node }]");
+
+  MT("multiple attribute, doubleQuotedString (with content)",
+      "[node {][atom a:][string \"a\"][node ,][atom b:][string \"b\"][node }]");
+
+  MT("single attribute (without content)",
+      "[node {][atom a:][string 'a'][node }]");
+
+  MT("multiple attribute, singleQuotedString (without content)",
+      "[node {][atom a:][string ''][node ,][atom b:][string ''][node }]");
+
+  MT("multiple attribute, doubleQuotedString (without content)",
+      "[node {][atom a:][string \"\"][node ,][atom b:][string \"\"][node }]");
+ })();
diff --git a/public/vendor/plugins/codemirror/mode/d/d.js b/public/vendor/plugins/codemirror/mode/d/d.js
index c927a7e358..efab570633 100644
--- a/public/vendor/plugins/codemirror/mode/d/d.js
+++ b/public/vendor/plugins/codemirror/mode/d/d.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -44,7 +44,7 @@ CodeMirror.defineMode("d", function(config, parserConfig) {
     }
     if (ch == "/") {
       if (stream.eat("+")) {
-        state.tokenize = tokenComment;
+        state.tokenize = tokenNestedComment;
         return tokenNestedComment(stream, state);
       }
       if (stream.eat("*")) {
@@ -182,7 +182,12 @@ CodeMirror.defineMode("d", function(config, parserConfig) {
       else return ctx.indented + (closing ? 0 : indentUnit);
     },
 
-    electricChars: "{}"
+    electricChars: "{}",
+    blockCommentStart: "/*",
+    blockCommentEnd: "*/",
+    blockCommentContinue: " * ",
+    lineComment: "//",
+    fold: "brace"
   };
 });
 
diff --git a/public/vendor/plugins/codemirror/mode/d/index.html b/public/vendor/plugins/codemirror/mode/d/index.html
index 08cabd8a2e..95437e5e7d 100644
--- a/public/vendor/plugins/codemirror/mode/d/index.html
+++ b/public/vendor/plugins/codemirror/mode/d/index.html
@@ -10,7 +10,7 @@
 <script src="d.js"></script>
 <style>.CodeMirror {border: 2px inset #dee;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/d/test.js b/public/vendor/plugins/codemirror/mode/d/test.js
new file mode 100644
index 0000000000..f8ac9de1c8
--- /dev/null
+++ b/public/vendor/plugins/codemirror/mode/d/test.js
@@ -0,0 +1,11 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function() {
+  var mode = CodeMirror.getMode({indentUnit: 2}, "d");
+  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
+
+  MT("nested_comments",
+     "[comment /+]","[comment comment]","[comment +/]","[variable void] [variable main](){}");
+
+})();
diff --git a/public/vendor/plugins/codemirror/mode/dart/dart.js b/public/vendor/plugins/codemirror/mode/dart/dart.js
index 8d383a95ef..7b154c65c0 100644
--- a/public/vendor/plugins/codemirror/mode/dart/dart.js
+++ b/public/vendor/plugins/codemirror/mode/dart/dart.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -12,8 +12,8 @@
   "use strict";
 
   var keywords = ("this super static final const abstract class extends external factory " +
-    "implements get native operator set typedef with enum throw rethrow " +
-    "assert break case continue default in return new deferred async await " +
+    "implements mixin get native set typedef with enum throw rethrow " +
+    "assert break case continue default in return new deferred async await covariant " +
     "try catch finally do else for if switch while import library export " +
     "part of show hide is as").split(" ");
   var blockKeywords = "try catch finally do else for if switch while".split(" ");
@@ -78,6 +78,15 @@
         if (!stream.eat("*")) return false
         state.tokenize = tokenNestedComment(1)
         return state.tokenize(stream, state)
+      },
+      token: function(stream, _, style) {
+        if (style == "variable") {
+          // Assume uppercase symbols are classes using variable-2
+          var isUpper = RegExp('^[_$]*[A-Z][a-zA-Z0-9_$]*$','g');
+          if (isUpper.test(stream.current())) {
+            return 'variable-2';
+          }
+        }
       }
     }
   });
diff --git a/public/vendor/plugins/codemirror/mode/dart/index.html b/public/vendor/plugins/codemirror/mode/dart/index.html
index e79da5a8b0..b68a4c5939 100644
--- a/public/vendor/plugins/codemirror/mode/dart/index.html
+++ b/public/vendor/plugins/codemirror/mode/dart/index.html
@@ -9,7 +9,7 @@
 <script src="dart.js"></script>
 <style>.CodeMirror {border: 1px solid #dee;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/diff/diff.js b/public/vendor/plugins/codemirror/mode/diff/diff.js
index fe0305e7b6..a30fd37a50 100644
--- a/public/vendor/plugins/codemirror/mode/diff/diff.js
+++ b/public/vendor/plugins/codemirror/mode/diff/diff.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/diff/index.html b/public/vendor/plugins/codemirror/mode/diff/index.html
index 0af611fa48..5c7ef7dee0 100644
--- a/public/vendor/plugins/codemirror/mode/diff/index.html
+++ b/public/vendor/plugins/codemirror/mode/diff/index.html
@@ -15,7 +15,7 @@
       span.cm-error.cm-tag { background-color: #2b2; }
     </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/django/django.js b/public/vendor/plugins/codemirror/mode/django/django.js
index 7b4ef3b566..07be123986 100644
--- a/public/vendor/plugins/codemirror/mode/django/django.js
+++ b/public/vendor/plugins/codemirror/mode/django/django.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/django/index.html b/public/vendor/plugins/codemirror/mode/django/index.html
index 41ea07c91b..384e0067be 100644
--- a/public/vendor/plugins/codemirror/mode/django/index.html
+++ b/public/vendor/plugins/codemirror/mode/django/index.html
@@ -11,9 +11,9 @@
 <script src="../xml/xml.js"></script>
 <script src="../htmlmixed/htmlmixed.js"></script>
 <script src="django.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/dockerfile/dockerfile.js b/public/vendor/plugins/codemirror/mode/dockerfile/dockerfile.js
index 4419009afa..983170f519 100644
--- a/public/vendor/plugins/codemirror/mode/dockerfile/dockerfile.js
+++ b/public/vendor/plugins/codemirror/mode/dockerfile/dockerfile.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -11,30 +11,64 @@
 })(function(CodeMirror) {
   "use strict";
 
+  var from = "from";
+  var fromRegex = new RegExp("^(\\s*)\\b(" + from + ")\\b", "i");
+
+  var shells = ["run", "cmd", "entrypoint", "shell"];
+  var shellsAsArrayRegex = new RegExp("^(\\s*)(" + shells.join('|') + ")(\\s+\\[)", "i");
+
+  var expose = "expose";
+  var exposeRegex = new RegExp("^(\\s*)(" + expose + ")(\\s+)", "i");
+
+  var others = [
+    "arg", "from", "maintainer", "label", "env",
+    "add", "copy", "volume", "user",
+    "workdir", "onbuild", "stopsignal", "healthcheck", "shell"
+  ];
+
   // Collect all Dockerfile directives
-  var instructions = ["from", "maintainer", "run", "cmd", "expose", "env",
-                      "add", "copy", "entrypoint", "volume", "user",
-                      "workdir", "onbuild"],
+  var instructions = [from, expose].concat(shells).concat(others),
       instructionRegex = "(" + instructions.join('|') + ")",
-      instructionOnlyLine = new RegExp(instructionRegex + "\\s*$", "i"),
-      instructionWithArguments = new RegExp(instructionRegex + "(\\s+)", "i");
+      instructionOnlyLine = new RegExp("^(\\s*)" + instructionRegex + "(\\s*)(#.*)?$", "i"),
+      instructionWithArguments = new RegExp("^(\\s*)" + instructionRegex + "(\\s+)", "i");
 
   CodeMirror.defineSimpleMode("dockerfile", {
     start: [
       // Block comment: This is a line starting with a comment
       {
-        regex: /#.*$/,
+        regex: /^\s*#.*$/,
+        sol: true,
         token: "comment"
       },
+      {
+        regex: fromRegex,
+        token: [null, "keyword"],
+        sol: true,
+        next: "from"
+      },
       // Highlight an instruction without any arguments (for convenience)
       {
         regex: instructionOnlyLine,
-        token: "variable-2"
+        token: [null, "keyword", null, "error"],
+        sol: true
+      },
+      {
+        regex: shellsAsArrayRegex,
+        token: [null, "keyword", null],
+        sol: true,
+        next: "array"
+      },
+      {
+        regex: exposeRegex,
+        token: [null, "keyword", null],
+        sol: true,
+        next: "expose"
       },
       // Highlight an instruction followed by arguments
       {
         regex: instructionWithArguments,
-        token: ["variable-2", null],
+        token: [null, "keyword", null],
+        sol: true,
         next: "arguments"
       },
       {
@@ -42,26 +76,21 @@
         token: null
       }
     ],
-    arguments: [
+    from: [
+      {
+        regex: /\s*$/,
+        token: null,
+        next: "start"
+      },
       {
         // Line comment without instruction arguments is an error
-        regex: /#.*$/,
-        token: "error",
+        regex: /(\s*)(#.*)$/,
+        token: [null, "error"],
         next: "start"
       },
       {
-        regex: /[^#]+\\$/,
-        token: null
-      },
-      {
-        // Match everything except for the inline comment
-        regex: /[^#]+/,
-        token: null,
-        next: "start"
-      },
-      {
-        regex: /$/,
-        token: null,
+        regex: /(\s*\S+\s+)(as)/i,
+        token: [null, "keyword"],
         next: "start"
       },
       // Fail safe return to start
@@ -70,9 +99,112 @@
         next: "start"
       }
     ],
-      meta: {
-          lineComment: "#"
+    single: [
+      {
+        regex: /(?:[^\\']|\\.)/,
+        token: "string"
+      },
+      {
+        regex: /'/,
+        token: "string",
+        pop: true
       }
+    ],
+    double: [
+      {
+        regex: /(?:[^\\"]|\\.)/,
+        token: "string"
+      },
+      {
+        regex: /"/,
+        token: "string",
+        pop: true
+      }
+    ],
+    array: [
+      {
+        regex: /\]/,
+        token: null,
+        next: "start"
+      },
+      {
+        regex: /"(?:[^\\"]|\\.)*"?/,
+        token: "string"
+      }
+    ],
+    expose: [
+      {
+        regex: /\d+$/,
+        token: "number",
+        next: "start"
+      },
+      {
+        regex: /[^\d]+$/,
+        token: null,
+        next: "start"
+      },
+      {
+        regex: /\d+/,
+        token: "number"
+      },
+      {
+        regex: /[^\d]+/,
+        token: null
+      },
+      // Fail safe return to start
+      {
+        token: null,
+        next: "start"
+      }
+    ],
+    arguments: [
+      {
+        regex: /^\s*#.*$/,
+        sol: true,
+        token: "comment"
+      },
+      {
+        regex: /"(?:[^\\"]|\\.)*"?$/,
+        token: "string",
+        next: "start"
+      },
+      {
+        regex: /"/,
+        token: "string",
+        push: "double"
+      },
+      {
+        regex: /'(?:[^\\']|\\.)*'?$/,
+        token: "string",
+        next: "start"
+      },
+      {
+        regex: /'/,
+        token: "string",
+        push: "single"
+      },
+      {
+        regex: /[^#"']+[\\`]$/,
+        token: null
+      },
+      {
+        regex: /[^#"']+$/,
+        token: null,
+        next: "start"
+      },
+      {
+        regex: /[^#"']+/,
+        token: null
+      },
+      // Fail safe return to start
+      {
+        token: null,
+        next: "start"
+      }
+    ],
+    meta: {
+      lineComment: "#"
+    }
   });
 
   CodeMirror.defineMIME("text/x-dockerfile", "dockerfile");
diff --git a/public/vendor/plugins/codemirror/mode/dockerfile/index.html b/public/vendor/plugins/codemirror/mode/dockerfile/index.html
index a31759bce1..63bb91e6ec 100644
--- a/public/vendor/plugins/codemirror/mode/dockerfile/index.html
+++ b/public/vendor/plugins/codemirror/mode/dockerfile/index.html
@@ -8,9 +8,9 @@
 <script src="../../lib/codemirror.js"></script>
 <script src="../../addon/mode/simple.js"></script>
 <script src="dockerfile.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/dockerfile/test.js b/public/vendor/plugins/codemirror/mode/dockerfile/test.js
new file mode 100644
index 0000000000..51add3ce9c
--- /dev/null
+++ b/public/vendor/plugins/codemirror/mode/dockerfile/test.js
@@ -0,0 +1,128 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function() {
+  var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-dockerfile");
+  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
+
+  MT("simple_nodejs_dockerfile",
+     "[keyword FROM] node:carbon",
+     "[comment # Create app directory]",
+     "[keyword WORKDIR] /usr/src/app",
+     "[comment # Install app dependencies]",
+     "[comment # A wildcard is used to ensure both package.json AND package-lock.json are copied]",
+     "[comment # where available (npm@5+)]",
+     "[keyword COPY] package*.json ./",
+     "[keyword RUN] npm install",
+     "[keyword COPY] . .",
+     "[keyword EXPOSE] [number 8080] [number 3000]",
+     "[keyword ENV] NODE_ENV development",
+     "[keyword CMD] [[ [string \"npm\"], [string \"start\"] ]]");
+
+  // Ideally the last space should not be highlighted.
+  MT("instruction_without_args_1",
+     "[keyword CMD] ");
+
+  MT("instruction_without_args_2",
+     "[comment # An instruction without args...]",
+     "[keyword ARG] [error #...is an error]");
+
+  MT("multiline",
+     "[keyword RUN] apt-get update && apt-get install -y \\",
+     "  mercurial \\",
+     "  subversion \\",
+     "  && apt-get clean \\",
+     "  && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*");
+
+  MT("from_comment",
+     "  [keyword FROM] debian:stretch # I tend to use stable as that is more stable",
+     "  [keyword FROM] debian:stretch [keyword AS] stable # I am even more stable",
+     " [keyword FROM] [error # this is an error]");
+
+  MT("from_as",
+     "[keyword FROM] golang:1.9.2-alpine3.6 [keyword AS] build",
+     "[keyword COPY] --from=build /bin/project /bin/project",
+     "[keyword ENTRYPOINT] [[ [string \"/bin/project\"] ]]",
+     "[keyword CMD] [[ [string \"--help\"] ]]");
+
+  MT("arg",
+     "[keyword ARG] VERSION=latest",
+     "[keyword FROM] busybox:$VERSION",
+     "[keyword ARG] VERSION",
+     "[keyword RUN] echo $VERSION > image_version");
+
+  MT("label",
+     "[keyword LABEL] com.example.label-with-value=[string \"foo\"]");
+
+  MT("label_multiline",
+     "[keyword LABEL] description=[string \"This text illustrates ]\\",
+     "[string that label-values can span multiple lines.\"]");
+
+  MT("maintainer",
+     "[keyword MAINTAINER] Foo Bar [string \"foo@bar.com\"] ",
+     "[keyword MAINTAINER] Bar Baz <bar@baz.com>");
+
+  MT("env",
+     "[keyword ENV] BUNDLE_PATH=[string \"$GEM_HOME\"] \\",
+     "  BUNDLE_APP_CONFIG=[string \"$GEM_HOME\"]");
+
+  MT("verify_keyword",
+     "[keyword RUN] add-apt-repository ppa:chris-lea/node.js");
+
+  MT("scripts",
+     "[comment # Set an entrypoint, to automatically install node modules]",
+     "[keyword ENTRYPOINT] [[ [string \"/bin/bash\"], [string \"-c\"], [string \"if [[ ! -d node_modules ]]; then npm install; fi; exec \\\"${@:0}\\\";\"] ]]",
+     "[keyword CMD] npm start",
+     "[keyword RUN] npm run build && \\",
+     "[comment # a comment between the shell commands]",
+     "  npm run test");
+
+  MT("strings_single",
+     "[keyword FROM] buildpack-deps:stretch",
+     "[keyword RUN] { \\",
+     "        echo [string 'install: --no-document']; \\",
+     "        echo [string 'update: --no-document']; \\",
+     "    } >> /usr/local/etc/gemrc");
+
+  MT("strings_single_multiline",
+     "[keyword RUN] set -ex \\",
+     "    \\",
+     "    && buildDeps=[string ' ]\\",
+     "[string        bison ]\\",
+     "[string        dpkg-dev ]\\",
+     "[string        libgdbm-dev ]\\",
+     "[string        ruby ]\\",
+     "[string    '] \\",
+     "    && apt-get update");
+
+  MT("strings_single_multiline_2",
+     "[keyword RUN] echo [string 'say \\' ]\\",
+     "[string   it works'] ");
+
+  MT("strings_double",
+     "[keyword RUN] apt-get install -y --no-install-recommends $buildDeps \\",
+     " \\",
+     " && wget -O ruby.tar.xz [string \"https://cache.ruby-lang.org/pub/ruby/${RUBY_MAJOR%-rc}/ruby-$RUBY_VERSION.tar.xz\"] \\",
+     " && echo [string \"$RUBY_DOWNLOAD_SHA256 *ruby.tar.xz\"] | sha256sum -c - ");
+
+  MT("strings_double_multiline",
+     "[keyword RUN] echo [string \"say \\\" ]\\",
+     "[string   it works\"] ");
+
+  MT("escape",
+     "[comment # escape=`]",
+     "[keyword FROM] microsoft/windowsservercore",
+     "[keyword RUN] powershell.exe -Command `",
+     "    $ErrorActionPreference = [string 'Stop']; `",
+     "    wget https://www.python.org/ftp/python/3.5.1/python-3.5.1.exe -OutFile c:\python-3.5.1.exe ; `",
+     "    Start-Process c:\python-3.5.1.exe -ArgumentList [string '/quiet InstallAllUsers=1 PrependPath=1'] -Wait ; `",
+     "    Remove-Item c:\python-3.5.1.exe -Force)");
+
+  MT("escape_strings",
+     "[comment # escape=`]",
+     "[keyword FROM] python:3.6-windowsservercore [keyword AS] python",
+     "[keyword RUN] $env:PATH = [string 'C:\\Python;C:\\Python\\Scripts;{0}'] -f $env:PATH ; `",
+     // It should not consider \' as escaped.
+     // "  Set-ItemProperty -Path [string 'HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment\\'] -Name Path -Value $env:PATH ;");
+     "  Set-ItemProperty -Path [string 'HKLM:\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Environment\\' -Name Path -Value $env:PATH ;]");
+})();
diff --git a/public/vendor/plugins/codemirror/mode/dtd/dtd.js b/public/vendor/plugins/codemirror/mode/dtd/dtd.js
index 52d76ee114..74b8c6bded 100644
--- a/public/vendor/plugins/codemirror/mode/dtd/dtd.js
+++ b/public/vendor/plugins/codemirror/mode/dtd/dtd.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 /*
   DTD mode
diff --git a/public/vendor/plugins/codemirror/mode/dtd/index.html b/public/vendor/plugins/codemirror/mode/dtd/index.html
index e6798a748a..959c346527 100644
--- a/public/vendor/plugins/codemirror/mode/dtd/index.html
+++ b/public/vendor/plugins/codemirror/mode/dtd/index.html
@@ -7,9 +7,9 @@
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
 <script src="dtd.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/dylan/dylan.js b/public/vendor/plugins/codemirror/mode/dylan/dylan.js
index 1b46bc8284..6725edccc0 100644
--- a/public/vendor/plugins/codemirror/mode/dylan/dylan.js
+++ b/public/vendor/plugins/codemirror/mode/dylan/dylan.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -11,6 +11,14 @@
 })(function(CodeMirror) {
 "use strict";
 
+function forEach(arr, f) {
+  for (var i = 0; i < arr.length; i++) f(arr[i], i)
+}
+function some(arr, f) {
+  for (var i = 0; i < arr.length; i++) if (f(arr[i], i)) return true
+  return false
+}
+
 CodeMirror.defineMode("dylan", function(_config) {
   // Words
   var words = {
@@ -136,13 +144,13 @@ CodeMirror.defineMode("dylan", function(_config) {
   var wordLookup = {};
   var styleLookup = {};
 
-  [
+  forEach([
     "keyword",
     "definition",
     "simpleDefinition",
     "signalingCalls"
-  ].forEach(function(type) {
-    words[type].forEach(function(word) {
+  ], function(type) {
+    forEach(words[type], function(word) {
       wordLookup[word] = type;
       styleLookup[word] = styles[type];
     });
@@ -258,7 +266,7 @@ CodeMirror.defineMode("dylan", function(_config) {
     for (var name in patterns) {
       if (patterns.hasOwnProperty(name)) {
         var pattern = patterns[name];
-        if ((pattern instanceof Array && pattern.some(function(p) {
+        if ((pattern instanceof Array && some(pattern, function(p) {
           return stream.match(p);
         })) || stream.match(pattern))
           return patternStyles[name];
@@ -273,7 +281,7 @@ CodeMirror.defineMode("dylan", function(_config) {
     } else {
       stream.eatWhile(/[\w\-]/);
       // Keyword
-      if (wordLookup[stream.current()]) {
+      if (wordLookup.hasOwnProperty(stream.current())) {
         return styleLookup[stream.current()];
       } else if (stream.current().match(symbol)) {
         return "variable";
diff --git a/public/vendor/plugins/codemirror/mode/dylan/index.html b/public/vendor/plugins/codemirror/mode/dylan/index.html
index ddf5ad067d..af259528cb 100644
--- a/public/vendor/plugins/codemirror/mode/dylan/index.html
+++ b/public/vendor/plugins/codemirror/mode/dylan/index.html
@@ -10,9 +10,9 @@
 <script src="../../addon/comment/continuecomment.js"></script>
 <script src="../../addon/comment/comment.js"></script>
 <script src="dylan.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/dylan/test.js b/public/vendor/plugins/codemirror/mode/dylan/test.js
index bf25be27e1..8b121b9133 100644
--- a/public/vendor/plugins/codemirror/mode/dylan/test.js
+++ b/public/vendor/plugins/codemirror/mode/dylan/test.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function() {
   var mode = CodeMirror.getMode({indentUnit: 2}, "dylan");
diff --git a/public/vendor/plugins/codemirror/mode/ebnf/ebnf.js b/public/vendor/plugins/codemirror/mode/ebnf/ebnf.js
index 9618f8e42b..238bbe23bf 100644
--- a/public/vendor/plugins/codemirror/mode/ebnf/ebnf.js
+++ b/public/vendor/plugins/codemirror/mode/ebnf/ebnf.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/ebnf/index.html b/public/vendor/plugins/codemirror/mode/ebnf/index.html
index 13845629b3..9bd28a4adf 100644
--- a/public/vendor/plugins/codemirror/mode/ebnf/index.html
+++ b/public/vendor/plugins/codemirror/mode/ebnf/index.html
@@ -9,11 +9,11 @@
     <script src="../../lib/codemirror.js"></script>
     <script src="../javascript/javascript.js"></script>
     <script src="ebnf.js"></script>
-    <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+    <style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
   </head>
   <body>
     <div id=nav>
-      <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+      <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
       <ul>
         <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/ecl/ecl.js b/public/vendor/plugins/codemirror/mode/ecl/ecl.js
index 8df7ebe4ab..9af8aee180 100644
--- a/public/vendor/plugins/codemirror/mode/ecl/ecl.js
+++ b/public/vendor/plugins/codemirror/mode/ecl/ecl.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/ecl/index.html b/public/vendor/plugins/codemirror/mode/ecl/index.html
index 2306860dcb..1acaec332f 100644
--- a/public/vendor/plugins/codemirror/mode/ecl/index.html
+++ b/public/vendor/plugins/codemirror/mode/ecl/index.html
@@ -9,7 +9,7 @@
 <script src="ecl.js"></script>
 <style>.CodeMirror {border: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/eiffel/eiffel.js b/public/vendor/plugins/codemirror/mode/eiffel/eiffel.js
index b8b70e36e5..f6f3f458b6 100644
--- a/public/vendor/plugins/codemirror/mode/eiffel/eiffel.js
+++ b/public/vendor/plugins/codemirror/mode/eiffel/eiffel.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/eiffel/index.html b/public/vendor/plugins/codemirror/mode/eiffel/index.html
index 108a71bec8..568fbb79bb 100644
--- a/public/vendor/plugins/codemirror/mode/eiffel/index.html
+++ b/public/vendor/plugins/codemirror/mode/eiffel/index.html
@@ -13,7 +13,7 @@
       .cm-s-default span.cm-arrow { color: red; }
     </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/elm/elm.js b/public/vendor/plugins/codemirror/mode/elm/elm.js
index b31e663757..fe8dd497f4 100644
--- a/public/vendor/plugins/codemirror/mode/elm/elm.js
+++ b/public/vendor/plugins/codemirror/mode/elm/elm.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -70,7 +70,7 @@
         if (smallRE.test(ch)) {
           var isDef = source.pos === 1;
           source.eatWhile(idRE);
-          return isDef ? "variable-3" : "variable";
+          return isDef ? "type" : "variable";
         }
 
         if (digitRE.test(ch)) {
diff --git a/public/vendor/plugins/codemirror/mode/elm/index.html b/public/vendor/plugins/codemirror/mode/elm/index.html
index d5cb16abfb..9bef08a8e3 100644
--- a/public/vendor/plugins/codemirror/mode/elm/index.html
+++ b/public/vendor/plugins/codemirror/mode/elm/index.html
@@ -7,9 +7,9 @@
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
 <script src="elm.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/erlang/erlang.js b/public/vendor/plugins/codemirror/mode/erlang/erlang.js
index 5aed76a526..f0541bb955 100644
--- a/public/vendor/plugins/codemirror/mode/erlang/erlang.js
+++ b/public/vendor/plugins/codemirror/mode/erlang/erlang.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 /*jshint unused:true, eqnull:true, curly:true, bitwise:true */
 /*jshint undef:true, latedef:true, trailing:true */
@@ -433,15 +433,16 @@ CodeMirror.defineMode("erlang", function(cmCfg) {
   }
 
   function maybe_drop_post(s) {
+    if (!s.length) return s
     var last = s.length-1;
 
     if (s[last].type === "dot") {
       return [];
     }
-    if (s[last].type === "fun" && s[last-1].token === "fun") {
+    if (last > 1 && s[last].type === "fun" && s[last-1].token === "fun") {
       return s.slice(0,last-1);
     }
-    switch (s[s.length-1].token) {
+    switch (s[last].token) {
       case "}":    return d(s,{g:["{"]});
       case "]":    return d(s,{i:["["]});
       case ")":    return d(s,{i:["("]});
diff --git a/public/vendor/plugins/codemirror/mode/erlang/index.html b/public/vendor/plugins/codemirror/mode/erlang/index.html
index 6d06a890a2..5f54c0fe98 100644
--- a/public/vendor/plugins/codemirror/mode/erlang/index.html
+++ b/public/vendor/plugins/codemirror/mode/erlang/index.html
@@ -9,9 +9,9 @@
 <script src="../../lib/codemirror.js"></script>
 <script src="../../addon/edit/matchbrackets.js"></script>
 <script src="erlang.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/factor/factor.js b/public/vendor/plugins/codemirror/mode/factor/factor.js
index 86d7adf627..7108278cca 100644
--- a/public/vendor/plugins/codemirror/mode/factor/factor.js
+++ b/public/vendor/plugins/codemirror/mode/factor/factor.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 // Factor syntax highlight - simple mode
 //
@@ -22,52 +22,54 @@
       {regex: /#?!.*/, token: "comment"},
       // strings """, multiline --> state
       {regex: /"""/, token: "string", next: "string3"},
-      {regex: /"/, token: "string", next: "string"},
+      {regex: /(STRING:)(\s)/, token: ["keyword", null], next: "string2"},
+      {regex: /\S*?"/, token: "string", next: "string"},
       // numbers: dec, hex, unicode, bin, fractional, complex
-      {regex: /(?:[+-]?)(?:0x[\d,a-f]+)|(?:0o[0-7]+)|(?:0b[0,1]+)|(?:\d+.?\d*)/, token: "number"},
+      {regex: /(?:0x[\d,a-f]+)|(?:0o[0-7]+)|(?:0b[0,1]+)|(?:\-?\d+.?\d*)(?=\s)/, token: "number"},
       //{regex: /[+-]?/} //fractional
       // definition: defining word, defined word, etc
-      {regex: /(\:)(\s+)(\S+)(\s+)(\()/, token: ["keyword", null, "def", null, "keyword"], next: "stack"},
+      {regex: /((?:GENERIC)|\:?\:)(\s+)(\S+)(\s+)(\()/, token: ["keyword", null, "def", null, "bracket"], next: "stack"},
+      // method definition: defining word, type, defined word, etc
+      {regex: /(M\:)(\s+)(\S+)(\s+)(\S+)/, token: ["keyword", null, "def", null, "tag"]},
       // vocabulary using --> state
       {regex: /USING\:/, token: "keyword", next: "vocabulary"},
       // vocabulary definition/use
-      {regex: /(USE\:|IN\:)(\s+)(\S+)/, token: ["keyword", null, "variable-2"]},
-      // <constructors>
-      {regex: /<\S+>/, token: "builtin"},
+      {regex: /(USE\:|IN\:)(\s+)(\S+)(?=\s|$)/, token: ["keyword", null, "tag"]},
+      // definition: a defining word, defined word
+      {regex: /(\S+\:)(\s+)(\S+)(?=\s|$)/, token: ["keyword", null, "def"]},
       // "keywords", incl. ; t f . [ ] { } defining words
-      {regex: /;|t|f|if|\.|\[|\]|\{|\}|MAIN:/, token: "keyword"},
+      {regex: /(?:;|\\|t|f|if|loop|while|until|do|PRIVATE>|<PRIVATE|\.|\S*\[|\]|\S*\{|\})(?=\s|$)/, token: "keyword"},
+      // <constructors> and the like
+      {regex: /\S+[\)>\.\*\?]+(?=\s|$)/, token: "builtin"},
+      {regex: /[\)><]+\S+(?=\s|$)/, token: "builtin"},
+      // operators
+      {regex: /(?:[\+\-\=\/\*<>])(?=\s|$)/, token: "keyword"},
       // any id (?)
       {regex: /\S+/, token: "variable"},
-
-      {
-        regex: /./,
-        token: null
-      }
+      {regex: /\s+|./, token: null}
     ],
     vocabulary: [
       {regex: /;/, token: "keyword", next: "start"},
-      {regex: /\S+/, token: "variable-2"},
-      {
-        regex: /./,
-        token: null
-      }
+      {regex: /\S+/, token: "tag"},
+      {regex: /\s+|./, token: null}
     ],
     string: [
       {regex: /(?:[^\\]|\\.)*?"/, token: "string", next: "start"},
       {regex: /.*/, token: "string"}
     ],
+    string2: [
+      {regex: /^;/, token: "keyword", next: "start"},
+      {regex: /.*/, token: "string"}
+    ],
     string3: [
       {regex: /(?:[^\\]|\\.)*?"""/, token: "string", next: "start"},
       {regex: /.*/, token: "string"}
     ],
     stack: [
-      {regex: /\)/, token: "meta", next: "start"},
-      {regex: /--/, token: "meta"},
-      {regex: /\S+/, token: "variable-3"},
-      {
-        regex: /./,
-        token: null
-      }
+      {regex: /\)/, token: "bracket", next: "start"},
+      {regex: /--/, token: "bracket"},
+      {regex: /\S+/, token: "meta"},
+      {regex: /\s+|./, token: null}
     ],
     // The meta property contains global information about the mode. It
     // can contain properties like lineComment, which are supported by
diff --git a/public/vendor/plugins/codemirror/mode/factor/index.html b/public/vendor/plugins/codemirror/mode/factor/index.html
index 9f15489140..13fe429783 100644
--- a/public/vendor/plugins/codemirror/mode/factor/index.html
+++ b/public/vendor/plugins/codemirror/mode/factor/index.html
@@ -16,7 +16,7 @@
 }
 </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/fcl/fcl.js b/public/vendor/plugins/codemirror/mode/fcl/fcl.js
index 518116976a..2d3f2001d3 100644
--- a/public/vendor/plugins/codemirror/mode/fcl/fcl.js
+++ b/public/vendor/plugins/codemirror/mode/fcl/fcl.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/fcl/index.html b/public/vendor/plugins/codemirror/mode/fcl/index.html
index 3c18d0b3c7..7ea0fd15f8 100644
--- a/public/vendor/plugins/codemirror/mode/fcl/index.html
+++ b/public/vendor/plugins/codemirror/mode/fcl/index.html
@@ -11,7 +11,7 @@
 <script src="fcl.js"></script>
 <style>.CodeMirror {border:1px solid #999; background:#ffc}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/forth/forth.js b/public/vendor/plugins/codemirror/mode/forth/forth.js
index 1f519d8862..f2caa27af8 100644
--- a/public/vendor/plugins/codemirror/mode/forth/forth.js
+++ b/public/vendor/plugins/codemirror/mode/forth/forth.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 // Author: Aliaksei Chapyzhenka
 
diff --git a/public/vendor/plugins/codemirror/mode/forth/index.html b/public/vendor/plugins/codemirror/mode/forth/index.html
index ae8cd34584..470363f48f 100644
--- a/public/vendor/plugins/codemirror/mode/forth/index.html
+++ b/public/vendor/plugins/codemirror/mode/forth/index.html
@@ -16,7 +16,7 @@
 }
 </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/fortran/fortran.js b/public/vendor/plugins/codemirror/mode/fortran/fortran.js
index 4d88f006aa..85bacc42c1 100644
--- a/public/vendor/plugins/codemirror/mode/fortran/fortran.js
+++ b/public/vendor/plugins/codemirror/mode/fortran/fortran.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/fortran/index.html b/public/vendor/plugins/codemirror/mode/fortran/index.html
index 9aed0efccc..a799e937f1 100644
--- a/public/vendor/plugins/codemirror/mode/fortran/index.html
+++ b/public/vendor/plugins/codemirror/mode/fortran/index.html
@@ -7,9 +7,9 @@
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
 <script src="fortran.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/gas/gas.js b/public/vendor/plugins/codemirror/mode/gas/gas.js
index 0c74bedc57..e34d7a7b61 100644
--- a/public/vendor/plugins/codemirror/mode/gas/gas.js
+++ b/public/vendor/plugins/codemirror/mode/gas/gas.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/gas/index.html b/public/vendor/plugins/codemirror/mode/gas/index.html
index df75ca2db7..34eea72da4 100644
--- a/public/vendor/plugins/codemirror/mode/gas/index.html
+++ b/public/vendor/plugins/codemirror/mode/gas/index.html
@@ -9,7 +9,7 @@
 <script src="gas.js"></script>
 <style>.CodeMirror {border: 2px inset #dee;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/gfm/gfm.js b/public/vendor/plugins/codemirror/mode/gfm/gfm.js
index 6e74ad4fd6..492c9487a6 100644
--- a/public/vendor/plugins/codemirror/mode/gfm/gfm.js
+++ b/public/vendor/plugins/codemirror/mode/gfm/gfm.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -81,7 +81,7 @@ CodeMirror.defineMode("gfm", function(config, modeConfig) {
       if (stream.sol() || state.ateSpace) {
         state.ateSpace = false;
         if (modeConfig.gitHubSpice !== false) {
-          if(stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+@)?(?:[a-f0-9]{7,40}\b)/)) {
+          if(stream.match(/^(?:[a-zA-Z0-9\-_]+\/)?(?:[a-zA-Z0-9\-_]+@)?(?=.{0,6}\d)(?:[a-f0-9]{7,40}\b)/)) {
             // User/Project@SHA
             // User@SHA
             // SHA
@@ -113,10 +113,9 @@ CodeMirror.defineMode("gfm", function(config, modeConfig) {
   };
 
   var markdownConfig = {
-    underscoresBreakWords: false,
     taskLists: true,
-    fencedCodeBlocks: '```',
-    strikethrough: true
+    strikethrough: true,
+    emoji: true
   };
   for (var attr in modeConfig) {
     markdownConfig[attr] = modeConfig[attr];
diff --git a/public/vendor/plugins/codemirror/mode/gfm/index.html b/public/vendor/plugins/codemirror/mode/gfm/index.html
index 24c90c068e..8f6fb8ea80 100644
--- a/public/vendor/plugins/codemirror/mode/gfm/index.html
+++ b/public/vendor/plugins/codemirror/mode/gfm/index.html
@@ -15,9 +15,12 @@
 <script src="../htmlmixed/htmlmixed.js"></script>
 <script src="../clike/clike.js"></script>
 <script src="../meta.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>
+  .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
+  .cm-s-default .cm-emoji {color: #009688;}
+</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -67,6 +70,10 @@ for (var i = 0; i &lt; items.length; i++) {
 
 ## A bit of GitHub spice
 
+See http://github.github.com/github-flavored-markdown/.
+
+(Set `gitHubSpice: false` in mode options to disable):
+
 * SHA: be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2
 * User@SHA ref: mojombo@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2
 * User/Project@SHA: mojombo/god@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2
@@ -74,13 +81,21 @@ for (var i = 0; i &lt; items.length; i++) {
 * User/#Num: mojombo#1
 * User/Project#Num: mojombo/god#1
 
-See http://github.github.com/github-flavored-markdown/.
+(Set `emoji: false` in mode options to disable):
+
+* emoji: :smile:
+
 
 </textarea></form>
 
     <script>
       var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
-        mode: 'gfm',
+        mode: {
+          name: "gfm",
+          tokenTypeOverrides: {
+            emoji: "emoji"
+          }
+        },
         lineNumbers: true,
         theme: "default"
       });
@@ -88,6 +103,34 @@ See http://github.github.com/github-flavored-markdown/.
 
     <p>Optionally depends on other modes for properly highlighted code blocks.</p>
 
+    <p>Gfm mode supports these options (apart those from base Markdown mode):</p>
+    <ul>
+      <li>
+        <d1>
+          <dt><code>gitHubSpice: boolean</code></dt>
+          <dd>Hashes, issues... (default: <code>true</code>).</dd>
+        </d1>
+      </li>
+      <li>
+        <d1>
+          <dt><code>taskLists: boolean</code></dt>
+          <dd><code>- [ ]</code> syntax (default: <code>true</code>).</dd>
+        </d1>
+      </li>
+      <li>
+        <d1>
+          <dt><code>strikethrough: boolean</code></dt>
+          <dd><code>~~foo~~</code> syntax (default: <code>true</code>).</dd>
+        </d1>
+      </li>
+      <li>
+        <d1>
+          <dt><code>emoji: boolean</code></dt>
+          <dd><code>:emoji:</code> syntax (default: <code>true</code>).</dd>
+        </d1>
+      </li>
+    </ul>
+
     <p><strong>Parsing/Highlighting Tests:</strong> <a href="../../test/index.html#gfm_*">normal</a>,  <a href="../../test/index.html#verbose,gfm_*">verbose</a>.</p>
 
   </article>
diff --git a/public/vendor/plugins/codemirror/mode/gfm/test.js b/public/vendor/plugins/codemirror/mode/gfm/test.js
index 7a1a4ccf21..d933896aa5 100644
--- a/public/vendor/plugins/codemirror/mode/gfm/test.js
+++ b/public/vendor/plugins/codemirror/mode/gfm/test.js
@@ -1,10 +1,11 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function() {
-  var mode = CodeMirror.getMode({tabSize: 4}, "gfm");
+  var config = {tabSize: 4, indentUnit: 2}
+  var mode = CodeMirror.getMode(config, "gfm");
   function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
-  var modeHighlightFormatting = CodeMirror.getMode({tabSize: 4}, {name: "gfm", highlightFormatting: true});
+  var modeHighlightFormatting = CodeMirror.getMode(config, {name: "gfm", highlightFormatting: true});
   function FT(name) { test.mode(name, modeHighlightFormatting, Array.prototype.slice.call(arguments, 1)); }
 
   FT("codeBackticks",
@@ -13,11 +14,6 @@
   FT("doubleBackticks",
      "[comment&formatting&formatting-code ``][comment foo ` bar][comment&formatting&formatting-code ``]");
 
-  FT("codeBlock",
-     "[comment&formatting&formatting-code-block ```css]",
-     "[tag foo]",
-     "[comment&formatting&formatting-code-block ```]");
-
   FT("taskList",
      "[variable-2&formatting&formatting-list&formatting-list-ul - ][meta&formatting&formatting-task [ ]]][variable-2  foo]",
      "[variable-2&formatting&formatting-list&formatting-list-ul - ][property&formatting&formatting-task [x]]][variable-2  foo]");
@@ -28,6 +24,9 @@
   FT("formatting_strikethrough",
      "foo [strikethrough&formatting&formatting-strikethrough ~~][strikethrough bar][strikethrough&formatting&formatting-strikethrough ~~]");
 
+  FT("formatting_emoji",
+     "foo [builtin&formatting&formatting-emoji :smile:] foo");
+
   MT("emInWordAsterisk",
      "foo[em *bar*]hello");
 
@@ -35,59 +34,31 @@
      "foo_bar_hello");
 
   MT("emStrongUnderscore",
-     "[strong __][em&strong _foo__][em _] bar");
-
-  MT("fencedCodeBlocks",
-     "[comment ```]",
-     "[comment foo]",
-     "",
-     "[comment ```]",
-     "bar");
-
-  MT("fencedCodeBlockModeSwitching",
-     "[comment ```javascript]",
-     "[variable foo]",
-     "",
-     "[comment ```]",
-     "bar");
-
-  MT("fencedCodeBlockModeSwitchingObjc",
-     "[comment ```objective-c]",
-     "[keyword @property] [variable NSString] [operator *] [variable foo];",
-     "[comment ```]",
-     "bar");
-
-  MT("fencedCodeBlocksNoTildes",
-     "~~~",
-     "foo",
-     "~~~");
+     "[em&strong ___foo___] bar");
 
   MT("taskListAsterisk",
-     "[variable-2 * []] foo]", // Invalid; must have space or x between []
-     "[variable-2 * [ ]]bar]", // Invalid; must have space after ]
-     "[variable-2 * [x]]hello]", // Invalid; must have space after ]
-     "[variable-2 * ][meta [ ]]][variable-2  [world]]]", // Valid; tests reference style links
+     "[variable-2 * ][link&variable-2 [[]]][variable-2 foo]", // Invalid; must have space or x between []
+     "[variable-2 * ][link&variable-2 [[ ]]][variable-2 bar]", // Invalid; must have space after ]
+     "[variable-2 * ][link&variable-2 [[x]]][variable-2 hello]", // Invalid; must have space after ]
+     "[variable-2 * ][meta [ ]]][variable-2  ][link&variable-2 [[world]]]", // Valid; tests reference style links
      "    [variable-3 * ][property [x]]][variable-3  foo]"); // Valid; can be nested
 
   MT("taskListPlus",
-     "[variable-2 + []] foo]", // Invalid; must have space or x between []
-     "[variable-2 + [ ]]bar]", // Invalid; must have space after ]
-     "[variable-2 + [x]]hello]", // Invalid; must have space after ]
-     "[variable-2 + ][meta [ ]]][variable-2  [world]]]", // Valid; tests reference style links
+     "[variable-2 + ][link&variable-2 [[]]][variable-2 foo]", // Invalid; must have space or x between []
+     "[variable-2 + ][link&variable-2 [[x]]][variable-2 hello]", // Invalid; must have space after ]
+     "[variable-2 + ][meta [ ]]][variable-2  ][link&variable-2 [[world]]]", // Valid; tests reference style links
      "    [variable-3 + ][property [x]]][variable-3  foo]"); // Valid; can be nested
 
   MT("taskListDash",
-     "[variable-2 - []] foo]", // Invalid; must have space or x between []
-     "[variable-2 - [ ]]bar]", // Invalid; must have space after ]
-     "[variable-2 - [x]]hello]", // Invalid; must have space after ]
-     "[variable-2 - ][meta [ ]]][variable-2  [world]]]", // Valid; tests reference style links
+     "[variable-2 - ][link&variable-2 [[]]][variable-2 foo]", // Invalid; must have space or x between []
+     "[variable-2 - ][link&variable-2 [[x]]][variable-2 hello]", // Invalid; must have space after ]
+     "[variable-2 - ][meta [ ]]][variable-2  world]", // Valid; tests reference style links
      "    [variable-3 - ][property [x]]][variable-3  foo]"); // Valid; can be nested
 
   MT("taskListNumber",
-     "[variable-2 1. []] foo]", // Invalid; must have space or x between []
-     "[variable-2 2. [ ]]bar]", // Invalid; must have space after ]
-     "[variable-2 3. [x]]hello]", // Invalid; must have space after ]
-     "[variable-2 4. ][meta [ ]]][variable-2  [world]]]", // Valid; tests reference style links
+     "[variable-2 1. ][link&variable-2 [[]]][variable-2 foo]", // Invalid; must have space or x between []
+     "[variable-2 2. ][link&variable-2 [[ ]]][variable-2 bar]", // Invalid; must have space after ]
+     "[variable-2 3. ][meta [ ]]][variable-2  world]", // Valid; tests reference style links
      "    [variable-3 1. ][property [x]]][variable-3  foo]"); // Valid; can be nested
 
   MT("SHA",
@@ -120,6 +91,9 @@
   MT("userProjectSHAEmphasis",
      "[em *foo ][em&link bar/hello@be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2][em *]");
 
+  MT("wordSHA",
+     "ask for feedbac")
+
   MT("num",
      "foo [link #1] bar");
 
@@ -165,11 +139,6 @@
   MT("notALink",
      "foo asfd:asdf bar");
 
-  MT("notALink",
-     "[comment ```css]",
-     "[tag foo] {[property color]:[keyword black];}",
-     "[comment ```][link http://www.example.com/]");
-
   MT("notALink",
      "[comment ``foo `bar` http://www.example.com/``] hello");
 
@@ -180,17 +149,6 @@
      "",
      "[link http://www.example.com/]");
 
-  MT("headerCodeBlockGithub",
-     "[header&header-1 # heading]",
-     "",
-     "[comment ```]",
-     "[comment code]",
-     "[comment ```]",
-     "",
-     "Commit: [link be6a8cc1c1ecfe9489fb51e4869af15a13fc2cd2]",
-     "Issue: [link #1]",
-     "Link: [link http://www.example.com/]");
-
   MT("strikethrough",
      "[strikethrough ~~foo~~]");
 
@@ -233,4 +191,8 @@
   MT("strikethroughStrong",
      "[strong **][strong&strikethrough ~~foo~~][strong **]");
 
+  MT("emoji",
+     "text [builtin :blush:] text [builtin :v:] text [builtin :+1:] text",
+     ":text text: [builtin :smiley_cat:]");
+
 })();
diff --git a/public/vendor/plugins/codemirror/mode/gherkin/gherkin.js b/public/vendor/plugins/codemirror/mode/gherkin/gherkin.js
index fc2ebee167..1b438b9057 100644
--- a/public/vendor/plugins/codemirror/mode/gherkin/gherkin.js
+++ b/public/vendor/plugins/codemirror/mode/gherkin/gherkin.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 /*
 Gherkin mode - http://www.cukes.info/
diff --git a/public/vendor/plugins/codemirror/mode/gherkin/index.html b/public/vendor/plugins/codemirror/mode/gherkin/index.html
index af8184c981..52e9ff67b2 100644
--- a/public/vendor/plugins/codemirror/mode/gherkin/index.html
+++ b/public/vendor/plugins/codemirror/mode/gherkin/index.html
@@ -9,7 +9,7 @@
 <script src="gherkin.js"></script>
 <style>.CodeMirror { border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; }</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/go/go.js b/public/vendor/plugins/codemirror/mode/go/go.js
index 3c9ef6b989..c005e42ddc 100644
--- a/public/vendor/plugins/codemirror/mode/go/go.js
+++ b/public/vendor/plugins/codemirror/mode/go/go.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -23,12 +23,13 @@ CodeMirror.defineMode("go", function(config) {
     "bool":true, "byte":true, "complex64":true, "complex128":true,
     "float32":true, "float64":true, "int8":true, "int16":true, "int32":true,
     "int64":true, "string":true, "uint8":true, "uint16":true, "uint32":true,
-    "uint64":true, "int":true, "uint":true, "uintptr":true, "error": true
+    "uint64":true, "int":true, "uint":true, "uintptr":true, "error": true,
+    "rune":true
   };
 
   var atoms = {
     "true":true, "false":true, "iota":true, "nil":true, "append":true,
-    "cap":true, "close":true, "complex":true, "copy":true, "imag":true,
+    "cap":true, "close":true, "complex":true, "copy":true, "delete":true, "imag":true,
     "len":true, "make":true, "new":true, "panic":true, "print":true,
     "println":true, "real":true, "recover":true
   };
@@ -154,14 +155,14 @@ CodeMirror.defineMode("go", function(config) {
       else if (curPunc == "[") pushContext(state, stream.column(), "]");
       else if (curPunc == "(") pushContext(state, stream.column(), ")");
       else if (curPunc == "case") ctx.type = "case";
-      else if (curPunc == "}" && ctx.type == "}") ctx = popContext(state);
+      else if (curPunc == "}" && ctx.type == "}") popContext(state);
       else if (curPunc == ctx.type) popContext(state);
       state.startOfLine = false;
       return style;
     },
 
     indent: function(state, textAfter) {
-      if (state.tokenize != tokenBase && state.tokenize != null) return 0;
+      if (state.tokenize != tokenBase && state.tokenize != null) return CodeMirror.Pass;
       var ctx = state.context, firstChar = textAfter && textAfter.charAt(0);
       if (ctx.type == "case" && /^(?:case|default)\b/.test(textAfter)) {
         state.context.type = "}";
@@ -173,6 +174,7 @@ CodeMirror.defineMode("go", function(config) {
     },
 
     electricChars: "{}):",
+    closeBrackets: "()[]{}''\"\"``",
     fold: "brace",
     blockCommentStart: "/*",
     blockCommentEnd: "*/",
diff --git a/public/vendor/plugins/codemirror/mode/go/index.html b/public/vendor/plugins/codemirror/mode/go/index.html
index 72e3b364c6..4489211043 100644
--- a/public/vendor/plugins/codemirror/mode/go/index.html
+++ b/public/vendor/plugins/codemirror/mode/go/index.html
@@ -11,7 +11,7 @@
 <script src="go.js"></script>
 <style>.CodeMirror {border:1px solid #999; background:#ffc}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/groovy/groovy.js b/public/vendor/plugins/codemirror/mode/groovy/groovy.js
index 721933b01c..2b90d01c43 100644
--- a/public/vendor/plugins/codemirror/mode/groovy/groovy.js
+++ b/public/vendor/plugins/codemirror/mode/groovy/groovy.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -21,9 +21,9 @@ CodeMirror.defineMode("groovy", function(config) {
     "abstract as assert boolean break byte case catch char class const continue def default " +
     "do double else enum extends final finally float for goto if implements import in " +
     "instanceof int interface long native new package private protected public return " +
-    "short static strictfp super switch synchronized threadsafe throw throws transient " +
+    "short static strictfp super switch synchronized threadsafe throw throws trait transient " +
     "try void volatile while");
-  var blockKeywords = words("catch class do else finally for if switch try while enum interface def");
+  var blockKeywords = words("catch class def do else enum finally for if interface switch trait try while");
   var standaloneKeywords = words("return break continue");
   var atoms = words("null true false this");
 
@@ -210,7 +210,7 @@ CodeMirror.defineMode("groovy", function(config) {
     },
 
     indent: function(state, textAfter) {
-      if (!state.tokenize[state.tokenize.length-1].isBase) return 0;
+      if (!state.tokenize[state.tokenize.length-1].isBase) return CodeMirror.Pass;
       var firstChar = textAfter && textAfter.charAt(0), ctx = state.context;
       if (ctx.type == "statement" && !expectExpression(state.lastToken, true)) ctx = ctx.prev;
       var closing = firstChar == ctx.type;
@@ -221,7 +221,10 @@ CodeMirror.defineMode("groovy", function(config) {
 
     electricChars: "{}",
     closeBrackets: {triples: "'\""},
-    fold: "brace"
+    fold: "brace",
+    blockCommentStart: "/*",
+    blockCommentEnd: "*/",
+    lineComment: "//"
   };
 });
 
diff --git a/public/vendor/plugins/codemirror/mode/groovy/index.html b/public/vendor/plugins/codemirror/mode/groovy/index.html
index bb0df078c3..3e022fdf09 100644
--- a/public/vendor/plugins/codemirror/mode/groovy/index.html
+++ b/public/vendor/plugins/codemirror/mode/groovy/index.html
@@ -10,7 +10,7 @@
 <script src="groovy.js"></script>
 <style>.CodeMirror {border-top: 1px solid #500; border-bottom: 1px solid #500;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/haml/haml.js b/public/vendor/plugins/codemirror/mode/haml/haml.js
index 20ae1e19ca..3c8f505eb5 100644
--- a/public/vendor/plugins/codemirror/mode/haml/haml.js
+++ b/public/vendor/plugins/codemirror/mode/haml/haml.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/haml/index.html b/public/vendor/plugins/codemirror/mode/haml/index.html
index 2894a938e8..ba3ea3062c 100644
--- a/public/vendor/plugins/codemirror/mode/haml/index.html
+++ b/public/vendor/plugins/codemirror/mode/haml/index.html
@@ -13,7 +13,7 @@
 <script src="haml.js"></script>
 <style>.CodeMirror {background: #f8f8f8;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/haml/test.js b/public/vendor/plugins/codemirror/mode/haml/test.js
index 508458a437..e051452738 100644
--- a/public/vendor/plugins/codemirror/mode/haml/test.js
+++ b/public/vendor/plugins/codemirror/mode/haml/test.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function() {
   var mode = CodeMirror.getMode({tabSize: 4, indentUnit: 2}, "haml");
diff --git a/public/vendor/plugins/codemirror/mode/handlebars/handlebars.js b/public/vendor/plugins/codemirror/mode/handlebars/handlebars.js
index 2174e53849..fe8b3b44e8 100644
--- a/public/vendor/plugins/codemirror/mode/handlebars/handlebars.js
+++ b/public/vendor/plugins/codemirror/mode/handlebars/handlebars.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -13,10 +13,14 @@
 
   CodeMirror.defineSimpleMode("handlebars-tags", {
     start: [
+      { regex: /\{\{\{/, push: "handlebars_raw", token: "tag" },
       { regex: /\{\{!--/, push: "dash_comment", token: "comment" },
       { regex: /\{\{!/,   push: "comment", token: "comment" },
       { regex: /\{\{/,    push: "handlebars", token: "tag" }
     ],
+    handlebars_raw: [
+      { regex: /\}\}\}/, pop: true, token: "tag" },
+    ],
     handlebars: [
       { regex: /\}\}/, pop: true, token: "tag" },
 
@@ -46,7 +50,11 @@
     comment: [
       { regex: /\}\}/, pop: true, token: "comment" },
       { regex: /./, token: "comment" }
-    ]
+    ],
+    meta: {
+      blockCommentStart: "{{--",
+      blockCommentEnd: "--}}"
+    }
   });
 
   CodeMirror.defineMode("handlebars", function(config, parserConfig) {
diff --git a/public/vendor/plugins/codemirror/mode/handlebars/index.html b/public/vendor/plugins/codemirror/mode/handlebars/index.html
index b1bfad1ca3..e2da2e766f 100644
--- a/public/vendor/plugins/codemirror/mode/handlebars/index.html
+++ b/public/vendor/plugins/codemirror/mode/handlebars/index.html
@@ -8,11 +8,12 @@
 <script src="../../lib/codemirror.js"></script>
 <script src="../../addon/mode/simple.js"></script>
 <script src="../../addon/mode/multiplex.js"></script>
+<script src="../../addon/edit/matchbrackets.js"></script>
 <script src="../xml/xml.js"></script>
 <script src="handlebars.js"></script>
 <style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -40,6 +41,8 @@
 
 {{! one line comment }}
 
+{{{propertyContainingRawHtml}}}
+
 {{#each articles}}
   {{~title}}
   <p>{{excerpt body size=120 ellipsis=true}}</p>
diff --git a/public/vendor/plugins/codemirror/mode/haskell-literate/haskell-literate.js b/public/vendor/plugins/codemirror/mode/haskell-literate/haskell-literate.js
index 906415b4c1..4bb9268491 100644
--- a/public/vendor/plugins/codemirror/mode/haskell-literate/haskell-literate.js
+++ b/public/vendor/plugins/codemirror/mode/haskell-literate/haskell-literate.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function (mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/haskell-literate/index.html b/public/vendor/plugins/codemirror/mode/haskell-literate/index.html
index 8c9bc60d15..aa3f3ea5b1 100644
--- a/public/vendor/plugins/codemirror/mode/haskell-literate/index.html
+++ b/public/vendor/plugins/codemirror/mode/haskell-literate/index.html
@@ -13,7 +13,7 @@
   border-bottom : 1px solid #DDDDDD;
 }</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo
                                                           src="../../doc/logo.png"></a>
 
   <ul>
diff --git a/public/vendor/plugins/codemirror/mode/haskell/haskell.js b/public/vendor/plugins/codemirror/mode/haskell/haskell.js
index fe0bab67ed..2e882dc600 100644
--- a/public/vendor/plugins/codemirror/mode/haskell/haskell.js
+++ b/public/vendor/plugins/codemirror/mode/haskell/haskell.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -56,7 +56,7 @@ CodeMirror.defineMode("haskell", function(_config, modeConfig) {
       if (source.eat('\'')) {
         return "string";
       }
-      return "error";
+      return "string error";
     }
 
     if (ch == '"') {
@@ -166,7 +166,7 @@ CodeMirror.defineMode("haskell", function(_config, modeConfig) {
       }
     }
     setState(normal);
-    return "error";
+    return "string error";
   }
 
   function stringGap(source, setState) {
@@ -194,16 +194,17 @@ CodeMirror.defineMode("haskell", function(_config, modeConfig) {
       "module", "newtype", "of", "then", "type", "where", "_");
 
     setType("keyword")(
-      "\.\.", ":", "::", "=", "\\", "\"", "<-", "->", "@", "~", "=>");
+      "\.\.", ":", "::", "=", "\\", "<-", "->", "@", "~", "=>");
 
     setType("builtin")(
-      "!!", "$!", "$", "&&", "+", "++", "-", ".", "/", "/=", "<", "<=", "=<<",
-      "==", ">", ">=", ">>", ">>=", "^", "^^", "||", "*", "**");
+      "!!", "$!", "$", "&&", "+", "++", "-", ".", "/", "/=", "<", "<*", "<=",
+      "<$>", "<*>", "=<<", "==", ">", ">=", ">>", ">>=", "^", "^^", "||", "*",
+      "*>", "**");
 
     setType("builtin")(
-      "Bool", "Bounded", "Char", "Double", "EQ", "Either", "Enum", "Eq",
-      "False", "FilePath", "Float", "Floating", "Fractional", "Functor", "GT",
-      "IO", "IOError", "Int", "Integer", "Integral", "Just", "LT", "Left",
+      "Applicative", "Bool", "Bounded", "Char", "Double", "EQ", "Either", "Enum",
+      "Eq", "False", "FilePath", "Float", "Floating", "Fractional", "Functor",
+      "GT", "IO", "IOError", "Int", "Integer", "Integral", "Just", "LT", "Left",
       "Maybe", "Monad", "Nothing", "Num", "Ord", "Ordering", "Rational", "Read",
       "ReadS", "Real", "RealFloat", "RealFrac", "Right", "Show", "ShowS",
       "String", "True");
@@ -223,7 +224,7 @@ CodeMirror.defineMode("haskell", function(_config, modeConfig) {
       "lcm", "length", "lex", "lines", "log", "logBase", "lookup", "map",
       "mapM", "mapM_", "max", "maxBound", "maximum", "maybe", "min", "minBound",
       "minimum", "mod", "negate", "not", "notElem", "null", "odd", "or",
-      "otherwise", "pi", "pred", "print", "product", "properFraction",
+      "otherwise", "pi", "pred", "print", "product", "properFraction", "pure",
       "putChar", "putStr", "putStrLn", "quot", "quotRem", "read", "readFile",
       "readIO", "readList", "readLn", "readParen", "reads", "readsPrec",
       "realToFrac", "recip", "rem", "repeat", "replicate", "return", "reverse",
diff --git a/public/vendor/plugins/codemirror/mode/haskell/index.html b/public/vendor/plugins/codemirror/mode/haskell/index.html
index 42240b0f2f..4d56682973 100644
--- a/public/vendor/plugins/codemirror/mode/haskell/index.html
+++ b/public/vendor/plugins/codemirror/mode/haskell/index.html
@@ -9,9 +9,9 @@
 <script src="../../lib/codemirror.js"></script>
 <script src="../../addon/edit/matchbrackets.js"></script>
 <script src="haskell.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/haxe/haxe.js b/public/vendor/plugins/codemirror/mode/haxe/haxe.js
index a9573dd71b..45376852a6 100644
--- a/public/vendor/plugins/codemirror/mode/haxe/haxe.js
+++ b/public/vendor/plugins/codemirror/mode/haxe/haxe.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -485,7 +485,7 @@ CodeMirror.defineMode("hxml", function () {
 
       if (state.inString == false && ch == "'") {
         state.inString = true;
-        ch = stream.next();
+        stream.next();
       }
 
       if (state.inString == true) {
diff --git a/public/vendor/plugins/codemirror/mode/haxe/index.html b/public/vendor/plugins/codemirror/mode/haxe/index.html
index d415b5e109..89dacdfb59 100644
--- a/public/vendor/plugins/codemirror/mode/haxe/index.html
+++ b/public/vendor/plugins/codemirror/mode/haxe/index.html
@@ -7,9 +7,9 @@
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
 <script src="haxe.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/htmlembedded/htmlembedded.js b/public/vendor/plugins/codemirror/mode/htmlembedded/htmlembedded.js
index 464dc57f83..439e63a427 100644
--- a/public/vendor/plugins/codemirror/mode/htmlembedded/htmlembedded.js
+++ b/public/vendor/plugins/codemirror/mode/htmlembedded/htmlembedded.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -14,7 +14,16 @@
   "use strict";
 
   CodeMirror.defineMode("htmlembedded", function(config, parserConfig) {
+    var closeComment = parserConfig.closeComment || "--%>"
     return CodeMirror.multiplexingMode(CodeMirror.getMode(config, "htmlmixed"), {
+      open: parserConfig.openComment || "<%--",
+      close: closeComment,
+      delimStyle: "comment",
+      mode: {token: function(stream) {
+        stream.skipTo(closeComment) || stream.skipToEnd()
+        return "comment"
+      }}
+    }, {
       open: parserConfig.open || parserConfig.scriptStartRegex || "<%",
       close: parserConfig.close || parserConfig.scriptEndRegex || "%>",
       mode: CodeMirror.getMode(config, parserConfig.scriptingModeSpec)
diff --git a/public/vendor/plugins/codemirror/mode/htmlembedded/index.html b/public/vendor/plugins/codemirror/mode/htmlembedded/index.html
index 9ed33cffef..4257237af1 100644
--- a/public/vendor/plugins/codemirror/mode/htmlembedded/index.html
+++ b/public/vendor/plugins/codemirror/mode/htmlembedded/index.html
@@ -12,9 +12,9 @@
 <script src="../htmlmixed/htmlmixed.js"></script>
 <script src="../../addon/mode/multiplex.js"></script>
 <script src="htmlembedded.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/htmlmixed/htmlmixed.js b/public/vendor/plugins/codemirror/mode/htmlmixed/htmlmixed.js
index d74083ee1a..8341ac8261 100644
--- a/public/vendor/plugins/codemirror/mode/htmlmixed/htmlmixed.js
+++ b/public/vendor/plugins/codemirror/mode/htmlmixed/htmlmixed.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -14,7 +14,7 @@
   var defaultTags = {
     script: [
       ["lang", /(javascript|babel)/i, "javascript"],
-      ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^$/i, "javascript"],
+      ["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i, "javascript"],
       ["type", /./, "text/plain"],
       [null, null, "javascript"]
     ],
@@ -46,7 +46,7 @@
 
   function getAttrValue(text, attr) {
     var match = text.match(getAttrRegexp(attr))
-    return match ? match[2] : ""
+    return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : ""
   }
 
   function getTagRegexp(tagName, anchored) {
@@ -105,7 +105,7 @@
           return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState));
         };
         state.localMode = mode;
-        state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, ""));
+        state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, "", ""));
       } else if (state.inTag) {
         state.inTag += stream.current()
         if (stream.eol()) state.inTag += " "
@@ -133,11 +133,11 @@
         return state.token(stream, state);
       },
 
-      indent: function (state, textAfter) {
+      indent: function (state, textAfter, line) {
         if (!state.localMode || /^\s*<\//.test(textAfter))
-          return htmlMode.indent(state.htmlState, textAfter);
+          return htmlMode.indent(state.htmlState, textAfter, line);
         else if (state.localMode.indent)
-          return state.localMode.indent(state.localState, textAfter);
+          return state.localMode.indent(state.localState, textAfter, line);
         else
           return CodeMirror.Pass;
       },
diff --git a/public/vendor/plugins/codemirror/mode/htmlmixed/index.html b/public/vendor/plugins/codemirror/mode/htmlmixed/index.html
index f94df9e21a..7de1e03503 100644
--- a/public/vendor/plugins/codemirror/mode/htmlmixed/index.html
+++ b/public/vendor/plugins/codemirror/mode/htmlmixed/index.html
@@ -14,7 +14,7 @@
 <script src="htmlmixed.js"></script>
 <style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -34,7 +34,7 @@
   <!-- this is a comment -->
   <head>
     <title>Mixed HTML Example</title>
-    <style type="text/css">
+    <style>
       h1 {font-family: comic sans; color: #f0f;}
       div {background: yellow !important;}
       body {
@@ -72,15 +72,26 @@
     <p>The HTML mixed mode depends on the XML, JavaScript, and CSS modes.</p>
 
     <p>It takes an optional mode configuration
-    option, <code>scriptTypes</code>, which can be used to add custom
-    behavior for specific <code>&lt;script type="..."></code> tags. If
-    given, it should hold an array of <code>{matches, mode}</code>
-    objects, where <code>matches</code> is a string or regexp that
-    matches the script type, and <code>mode</code> is
-    either <code>null</code>, for script types that should stay in
-    HTML mode, or a <a href="../../doc/manual.html#option_mode">mode
-    spec</a> corresponding to the mode that should be used for the
-    script.</p>
+    option, <code>tags</code>, which can be used to add custom
+    behavior for specific tags. When given, it should be an object
+    mapping tag names (for example <code>script</code>) to arrays or
+    three-element arrays. Those inner arrays indicate [attributeName,
+    valueRegexp, <a href="../../doc/manual.html#option_mode">modeSpec</a>]
+    specifications. For example, you could use <code>["type", /^foo$/,
+    "foo"]</code> to map the attribute <code>type="foo"</code> to
+    the <code>foo</code> mode. When the first two fields are null
+    (<code>[null, null, "mode"]</code>), the given mode is used for
+    any such tag that doesn't match any of the previously given
+    attributes. For example:</p>
+
+    <pre>var myModeSpec = {
+  name: "htmlmixed",
+  tags: {
+    style: [["type", /^text\/(x-)?scss$/, "text/x-scss"],
+            [null, null, "css"]],
+    custom: [[null, null, "customMode"]]
+  }
+}</pre>
 
     <p><strong>MIME types defined:</strong> <code>text/html</code>
     (redefined, only takes effect if you load this parser after the
diff --git a/public/vendor/plugins/codemirror/mode/http/http.js b/public/vendor/plugins/codemirror/mode/http/http.js
index 9a3c5f9fd8..092353259f 100644
--- a/public/vendor/plugins/codemirror/mode/http/http.js
+++ b/public/vendor/plugins/codemirror/mode/http/http.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/http/index.html b/public/vendor/plugins/codemirror/mode/http/index.html
index 0b8d5315da..b631ec5294 100644
--- a/public/vendor/plugins/codemirror/mode/http/index.html
+++ b/public/vendor/plugins/codemirror/mode/http/index.html
@@ -7,9 +7,9 @@
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
 <script src="http.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/idl/idl.js b/public/vendor/plugins/codemirror/mode/idl/idl.js
index 07308d71dc..168761cd88 100644
--- a/public/vendor/plugins/codemirror/mode/idl/idl.js
+++ b/public/vendor/plugins/codemirror/mode/idl/idl.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/idl/index.html b/public/vendor/plugins/codemirror/mode/idl/index.html
index 4c169e2d69..6ef17d093e 100644
--- a/public/vendor/plugins/codemirror/mode/idl/index.html
+++ b/public/vendor/plugins/codemirror/mode/idl/index.html
@@ -6,10 +6,11 @@
 
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
+<script src="../../addon/edit/matchbrackets.js"></script>
 <script src="idl.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/index.html b/public/vendor/plugins/codemirror/mode/index.html
index 732e0e52cd..858ba127f2 100644
--- a/public/vendor/plugins/codemirror/mode/index.html
+++ b/public/vendor/plugins/codemirror/mode/index.html
@@ -5,7 +5,7 @@
 <link rel=stylesheet href="../doc/docs.css">
 
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../doc/logo.png"></a>
 
   <ul>
     <li><a href="../index.html">Home</a>
@@ -56,6 +56,7 @@ option.</p>
       <li><a href="ebnf/index.html">EBNF</a></li>
       <li><a href="ecl/index.html">ECL</a></li>
       <li><a href="eiffel/index.html">Eiffel</a></li>
+      <li><a href="https://github.com/optick/codemirror-mode-elixir">Elixir</a></li>
       <li><a href="elm/index.html">Elm</a></li>
       <li><a href="erlang/index.html">Erlang</a></li>
       <li><a href="factor/index.html">Factor</a></li>
@@ -76,11 +77,10 @@ option.</p>
       <li><a href="http/index.html">HTTP</a></li>
       <li><a href="idl/index.html">IDL</a></li>
       <li><a href="clike/index.html">Java</a></li>
-      <li><a href="jade/index.html">Jade</a></li>
       <li><a href="javascript/index.html">JavaScript</a> (<a href="jsx/index.html">JSX</a>)</li>
       <li><a href="jinja2/index.html">Jinja2</a></li>
       <li><a href="julia/index.html">Julia</a></li>
-      <li><a href="kotlin/index.html">Kotlin</a></li>
+      <li><a href="clike/index.html">Kotlin</a></li>
       <li><a href="css/less.html">LESS</a></li>
       <li><a href="livescript/index.html">LiveScript</a></li>
       <li><a href="lua/index.html">Lua</a></li>
@@ -93,7 +93,7 @@ option.</p>
       <li><a href="mumps/index.html">MUMPS</a></li>
       <li><a href="nginx/index.html">Nginx</a></li>
       <li><a href="nsis/index.html">NSIS</a></li>
-      <li><a href="ntriples/index.html">NTriples</a></li>
+      <li><a href="ntriples/index.html">N-Triples/N-Quads</a></li>
       <li><a href="clike/index.html">Objective C</a></li>
       <li><a href="mllike/index.html">OCaml</a></li>
       <li><a href="octave/index.html">Octave</a> (MATLAB)</li>
@@ -107,6 +107,7 @@ option.</p>
       <li><a href="powershell/index.html">PowerShell</a></li>
       <li><a href="properties/index.html">Properties files</a></li>
       <li><a href="protobuf/index.html">ProtoBuf</a></li>
+      <li><a href="pug/index.html">Pug</a></li>
       <li><a href="puppet/index.html">Puppet</a></li>
       <li><a href="python/index.html">Python</a></li>
       <li><a href="q/index.html">Q</a></li>
diff --git a/public/vendor/plugins/codemirror/mode/javascript/index.html b/public/vendor/plugins/codemirror/mode/javascript/index.html
index 592a133d85..4eff2e28bc 100644
--- a/public/vendor/plugins/codemirror/mode/javascript/index.html
+++ b/public/vendor/plugins/codemirror/mode/javascript/index.html
@@ -10,9 +10,9 @@
 <script src="../../addon/comment/continuecomment.js"></script>
 <script src="../../addon/comment/comment.js"></script>
 <script src="javascript.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/javascript/javascript.js b/public/vendor/plugins/codemirror/mode/javascript/javascript.js
index 3909c85e6a..16943a9eb9 100644
--- a/public/vendor/plugins/codemirror/mode/javascript/javascript.js
+++ b/public/vendor/plugins/codemirror/mode/javascript/javascript.js
@@ -1,7 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
-
-// TODO actually recognize syntax of TypeScript constructs
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -13,11 +11,6 @@
 })(function(CodeMirror) {
 "use strict";
 
-function expressionAllowed(stream, state, backUp) {
-  return /^(?:operator|sof|keyword c|case|new|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
-    (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
-}
-
 CodeMirror.defineMode("javascript", function(config, parserConfig) {
   var indentUnit = config.indentUnit;
   var statementIndent = parserConfig.statementIndent;
@@ -30,55 +23,24 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
 
   var keywords = function(){
     function kw(type) {return {type: type, style: "keyword"};}
-    var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
+    var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c"), D = kw("keyword d");
     var operator = kw("operator"), atom = {type: "atom", style: "atom"};
 
-    var jsKeywords = {
+    return {
       "if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
-      "return": C, "break": C, "continue": C, "new": kw("new"), "delete": C, "throw": C, "debugger": C,
-      "var": kw("var"), "const": kw("var"), "let": kw("var"),
+      "return": D, "break": D, "continue": D, "new": kw("new"), "delete": C, "void": C, "throw": C,
+      "debugger": kw("debugger"), "var": kw("var"), "const": kw("var"), "let": kw("var"),
       "function": kw("function"), "catch": kw("catch"),
       "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
       "in": operator, "typeof": operator, "instanceof": operator,
       "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom,
       "this": kw("this"), "class": kw("class"), "super": kw("atom"),
       "yield": C, "export": kw("export"), "import": kw("import"), "extends": C,
-      "await": C, "async": kw("async")
+      "await": C
     };
-
-    // Extend the 'normal' keywords with the TypeScript language extensions
-    if (isTS) {
-      var type = {type: "variable", style: "variable-3"};
-      var tsKeywords = {
-        // object-like things
-        "interface": kw("class"),
-        "implements": C,
-        "namespace": C,
-        "module": kw("module"),
-        "enum": kw("module"),
-
-        // scope modifiers
-        "public": kw("modifier"),
-        "private": kw("modifier"),
-        "protected": kw("modifier"),
-        "abstract": kw("modifier"),
-
-        // operators
-        "as": operator,
-
-        // types
-        "string": type, "number": type, "boolean": type, "any": type
-      };
-
-      for (var attr in tsKeywords) {
-        jsKeywords[attr] = tsKeywords[attr];
-      }
-    }
-
-    return jsKeywords;
   }();
 
-  var isOperatorChar = /[+\-*&%=<>!?|~^]/;
+  var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
   var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
 
   function readRegexp(stream) {
@@ -105,7 +67,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     if (ch == '"' || ch == "'") {
       state.tokenize = tokenString(ch);
       return state.tokenize(stream, state);
-    } else if (ch == "." && stream.match(/^\d+(?:[eE][+\-]?\d+)?/)) {
+    } else if (ch == "." && stream.match(/^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/)) {
       return ret("number", "number");
     } else if (ch == "." && stream.match("..")) {
       return ret("spread", "meta");
@@ -113,17 +75,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
       return ret(ch);
     } else if (ch == "=" && stream.eat(">")) {
       return ret("=>", "operator");
-    } else if (ch == "0" && stream.eat(/x/i)) {
-      stream.eatWhile(/[\da-f]/i);
-      return ret("number", "number");
-    } else if (ch == "0" && stream.eat(/o/i)) {
-      stream.eatWhile(/[0-7]/i);
-      return ret("number", "number");
-    } else if (ch == "0" && stream.eat(/b/i)) {
-      stream.eatWhile(/[01]/i);
+    } else if (ch == "0" && stream.match(/^(?:x[\dA-Fa-f_]+|o[0-7_]+|b[01_]+)n?/)) {
       return ret("number", "number");
     } else if (/\d/.test(ch)) {
-      stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
+      stream.match(/^[\d_]*(?:n|(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)?/);
       return ret("number", "number");
     } else if (ch == "/") {
       if (stream.eat("*")) {
@@ -134,10 +89,10 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
         return ret("comment", "comment");
       } else if (expressionAllowed(stream, state, 1)) {
         readRegexp(stream);
-        stream.match(/^\b(([gimyu])(?![gimyu]*\2))+\b/);
+        stream.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/);
         return ret("regexp", "string-2");
       } else {
-        stream.eatWhile(isOperatorChar);
+        stream.eat("=");
         return ret("operator", "operator", stream.current());
       }
     } else if (ch == "`") {
@@ -146,14 +101,31 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     } else if (ch == "#") {
       stream.skipToEnd();
       return ret("error", "error");
+    } else if (ch == "<" && stream.match("!--") || ch == "-" && stream.match("->")) {
+      stream.skipToEnd()
+      return ret("comment", "comment")
     } else if (isOperatorChar.test(ch)) {
-      stream.eatWhile(isOperatorChar);
+      if (ch != ">" || !state.lexical || state.lexical.type != ">") {
+        if (stream.eat("=")) {
+          if (ch == "!" || ch == "=") stream.eat("=")
+        } else if (/[<>*+\-]/.test(ch)) {
+          stream.eat(ch)
+          if (ch == ">") stream.eat(ch)
+        }
+      }
       return ret("operator", "operator", stream.current());
     } else if (wordRE.test(ch)) {
       stream.eatWhile(wordRE);
-      var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
-      return (known && state.lastType != ".") ? ret(known.type, known.style, word) :
-                     ret("variable", "variable", word);
+      var word = stream.current()
+      if (state.lastType != ".") {
+        if (keywords.propertyIsEnumerable(word)) {
+          var kw = keywords[word]
+          return ret(kw.type, kw.style, word)
+        }
+        if (word == "async" && stream.match(/^(\s|\/\*.*?\*\/)*[\[\(\w]/, false))
+          return ret("async", "keyword", word)
+      }
+      return ret("variable", "variable", word)
     }
   }
 
@@ -210,19 +182,28 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     var arrow = stream.string.indexOf("=>", stream.start);
     if (arrow < 0) return;
 
+    if (isTS) { // Try to skip TypeScript return type declarations after the arguments
+      var m = /:\s*(?:\w+(?:<[^>]*>|\[\])?|\{[^}]*\})\s*$/.exec(stream.string.slice(stream.start, arrow))
+      if (m) arrow = m.index
+    }
+
     var depth = 0, sawSomething = false;
     for (var pos = arrow - 1; pos >= 0; --pos) {
       var ch = stream.string.charAt(pos);
       var bracket = brackets.indexOf(ch);
       if (bracket >= 0 && bracket < 3) {
         if (!depth) { ++pos; break; }
-        if (--depth == 0) break;
+        if (--depth == 0) { if (ch == "(") sawSomething = true; break; }
       } else if (bracket >= 3 && bracket < 6) {
         ++depth;
       } else if (wordRE.test(ch)) {
         sawSomething = true;
-      } else if (/["'\/]/.test(ch)) {
-        return;
+      } else if (/["'\/`]/.test(ch)) {
+        for (;; --pos) {
+          if (pos == 0) return
+          var next = stream.string.charAt(pos - 1)
+          if (next == ch && stream.string.charAt(pos - 2) != "\\") { pos--; break }
+        }
       } else if (sawSomething && !depth) {
         ++pos;
         break;
@@ -284,35 +265,68 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     pass.apply(null, arguments);
     return true;
   }
+  function inList(name, list) {
+    for (var v = list; v; v = v.next) if (v.name == name) return true
+    return false;
+  }
   function register(varname) {
-    function inList(list) {
-      for (var v = list; v; v = v.next)
-        if (v.name == varname) return true;
-      return false;
-    }
     var state = cx.state;
     cx.marked = "def";
     if (state.context) {
-      if (inList(state.localVars)) return;
-      state.localVars = {name: varname, next: state.localVars};
-    } else {
-      if (inList(state.globalVars)) return;
-      if (parserConfig.globalVars)
-        state.globalVars = {name: varname, next: state.globalVars};
+      if (state.lexical.info == "var" && state.context && state.context.block) {
+        // FIXME function decls are also not block scoped
+        var newContext = registerVarScoped(varname, state.context)
+        if (newContext != null) {
+          state.context = newContext
+          return
+        }
+      } else if (!inList(varname, state.localVars)) {
+        state.localVars = new Var(varname, state.localVars)
+        return
+      }
     }
+    // Fall through means this is global
+    if (parserConfig.globalVars && !inList(varname, state.globalVars))
+      state.globalVars = new Var(varname, state.globalVars)
+  }
+  function registerVarScoped(varname, context) {
+    if (!context) {
+      return null
+    } else if (context.block) {
+      var inner = registerVarScoped(varname, context.prev)
+      if (!inner) return null
+      if (inner == context.prev) return context
+      return new Context(inner, context.vars, true)
+    } else if (inList(varname, context.vars)) {
+      return context
+    } else {
+      return new Context(context.prev, new Var(varname, context.vars), false)
+    }
+  }
+
+  function isModifier(name) {
+    return name == "public" || name == "private" || name == "protected" || name == "abstract" || name == "readonly"
   }
 
   // Combinators
 
-  var defaultVars = {name: "this", next: {name: "arguments"}};
+  function Context(prev, vars, block) { this.prev = prev; this.vars = vars; this.block = block }
+  function Var(name, next) { this.name = name; this.next = next }
+
+  var defaultVars = new Var("this", new Var("arguments", null))
   function pushcontext() {
-    cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
-    cx.state.localVars = defaultVars;
+    cx.state.context = new Context(cx.state.context, cx.state.localVars, false)
+    cx.state.localVars = defaultVars
+  }
+  function pushblockcontext() {
+    cx.state.context = new Context(cx.state.context, cx.state.localVars, true)
+    cx.state.localVars = null
   }
   function popcontext() {
-    cx.state.localVars = cx.state.context.vars;
-    cx.state.context = cx.state.context.prev;
+    cx.state.localVars = cx.state.context.vars
+    cx.state.context = cx.state.context.prev
   }
+  popcontext.lex = true
   function pushlex(type, info) {
     var result = function() {
       var state = cx.state, indent = state.indented;
@@ -337,72 +351,99 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
   function expect(wanted) {
     function exp(type) {
       if (type == wanted) return cont();
-      else if (wanted == ";") return pass();
+      else if (wanted == ";" || type == "}" || type == ")" || type == "]") return pass();
       else return cont(exp);
     };
     return exp;
   }
 
   function statement(type, value) {
-    if (type == "var") return cont(pushlex("vardef", value.length), vardef, expect(";"), poplex);
-    if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
+    if (type == "var") return cont(pushlex("vardef", value), vardef, expect(";"), poplex);
+    if (type == "keyword a") return cont(pushlex("form"), parenExpr, statement, poplex);
     if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
-    if (type == "{") return cont(pushlex("}"), block, poplex);
+    if (type == "keyword d") return cx.stream.match(/^\s*$/, false) ? cont() : cont(pushlex("stat"), maybeexpression, expect(";"), poplex);
+    if (type == "debugger") return cont(expect(";"));
+    if (type == "{") return cont(pushlex("}"), pushblockcontext, block, poplex, popcontext);
     if (type == ";") return cont();
     if (type == "if") {
       if (cx.state.lexical.info == "else" && cx.state.cc[cx.state.cc.length - 1] == poplex)
         cx.state.cc.pop()();
-      return cont(pushlex("form"), expression, statement, poplex, maybeelse);
+      return cont(pushlex("form"), parenExpr, statement, poplex, maybeelse);
     }
     if (type == "function") return cont(functiondef);
     if (type == "for") return cont(pushlex("form"), forspec, statement, poplex);
-    if (type == "variable") return cont(pushlex("stat"), maybelabel);
-    if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
-                                      block, poplex, poplex);
+    if (type == "class" || (isTS && value == "interface")) {
+      cx.marked = "keyword"
+      return cont(pushlex("form", type == "class" ? type : value), className, poplex)
+    }
+    if (type == "variable") {
+      if (isTS && value == "declare") {
+        cx.marked = "keyword"
+        return cont(statement)
+      } else if (isTS && (value == "module" || value == "enum" || value == "type") && cx.stream.match(/^\s*\w/, false)) {
+        cx.marked = "keyword"
+        if (value == "enum") return cont(enumdef);
+        else if (value == "type") return cont(typename, expect("operator"), typeexpr, expect(";"));
+        else return cont(pushlex("form"), pattern, expect("{"), pushlex("}"), block, poplex, poplex)
+      } else if (isTS && value == "namespace") {
+        cx.marked = "keyword"
+        return cont(pushlex("form"), expression, statement, poplex)
+      } else if (isTS && value == "abstract") {
+        cx.marked = "keyword"
+        return cont(statement)
+      } else {
+        return cont(pushlex("stat"), maybelabel);
+      }
+    }
+    if (type == "switch") return cont(pushlex("form"), parenExpr, expect("{"), pushlex("}", "switch"), pushblockcontext,
+                                      block, poplex, poplex, popcontext);
     if (type == "case") return cont(expression, expect(":"));
     if (type == "default") return cont(expect(":"));
-    if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
-                                     statement, poplex, popcontext);
-    if (type == "class") return cont(pushlex("form"), className, poplex);
+    if (type == "catch") return cont(pushlex("form"), pushcontext, maybeCatchBinding, statement, poplex, popcontext);
     if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
     if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
-    if (type == "module") return cont(pushlex("form"), pattern, pushlex("}"), expect("{"), block, poplex, poplex)
     if (type == "async") return cont(statement)
+    if (value == "@") return cont(expression, statement)
     return pass(pushlex("stat"), expression, expect(";"), poplex);
   }
-  function expression(type) {
-    return expressionInner(type, false);
+  function maybeCatchBinding(type) {
+    if (type == "(") return cont(funarg, expect(")"))
   }
-  function expressionNoComma(type) {
-    return expressionInner(type, true);
+  function expression(type, value) {
+    return expressionInner(type, value, false);
   }
-  function expressionInner(type, noComma) {
+  function expressionNoComma(type, value) {
+    return expressionInner(type, value, true);
+  }
+  function parenExpr(type) {
+    if (type != "(") return pass()
+    return cont(pushlex(")"), expression, expect(")"), poplex)
+  }
+  function expressionInner(type, value, noComma) {
     if (cx.state.fatArrowAt == cx.stream.start) {
       var body = noComma ? arrowBodyNoComma : arrowBody;
-      if (type == "(") return cont(pushcontext, pushlex(")"), commasep(pattern, ")"), poplex, expect("=>"), body, popcontext);
+      if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, expect("=>"), body, popcontext);
       else if (type == "variable") return pass(pushcontext, pattern, expect("=>"), body, popcontext);
     }
 
     var maybeop = noComma ? maybeoperatorNoComma : maybeoperatorComma;
     if (atomicTypes.hasOwnProperty(type)) return cont(maybeop);
     if (type == "function") return cont(functiondef, maybeop);
-    if (type == "keyword c" || type == "async") return cont(noComma ? maybeexpressionNoComma : maybeexpression);
+    if (type == "class" || (isTS && value == "interface")) { cx.marked = "keyword"; return cont(pushlex("form"), classExpression, poplex); }
+    if (type == "keyword c" || type == "async") return cont(noComma ? expressionNoComma : expression);
     if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeop);
     if (type == "operator" || type == "spread") return cont(noComma ? expressionNoComma : expression);
     if (type == "[") return cont(pushlex("]"), arrayLiteral, poplex, maybeop);
     if (type == "{") return contCommasep(objprop, "}", null, maybeop);
     if (type == "quasi") return pass(quasi, maybeop);
     if (type == "new") return cont(maybeTarget(noComma));
+    if (type == "import") return cont(expression);
     return cont();
   }
   function maybeexpression(type) {
     if (type.match(/[;\}\)\],]/)) return pass();
     return pass(expression);
   }
-  function maybeexpressionNoComma(type) {
-    if (type.match(/[;\}\)\],]/)) return pass();
-    return pass(expressionNoComma);
-  }
 
   function maybeoperatorComma(type, value) {
     if (type == ",") return cont(expression);
@@ -413,7 +454,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     var expr = noComma == false ? expression : expressionNoComma;
     if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
     if (type == "operator") {
-      if (/\+\+|--/.test(value)) return cont(me);
+      if (/\+\+|--/.test(value) || isTS && value == "!") return cont(me);
+      if (isTS && value == "<" && cx.stream.match(/^([^>]|<.*?>)*>\s*\(/, false))
+        return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, me);
       if (value == "?") return cont(expression, expect(":"), expr);
       return cont(expr);
     }
@@ -422,6 +465,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     if (type == "(") return contCommasep(expressionNoComma, ")", "call", me);
     if (type == ".") return cont(property, me);
     if (type == "[") return cont(pushlex("]"), maybeexpression, expect("]"), poplex, me);
+    if (isTS && value == "as") { cx.marked = "keyword"; return cont(typeexpr, me) }
+    if (type == "regexp") {
+      cx.state.lastType = cx.marked = "operator"
+      cx.stream.backUp(cx.stream.pos - cx.stream.start - 1)
+      return cont(expr)
+    }
   }
   function quasi(type, value) {
     if (type != "quasi") return pass();
@@ -446,6 +495,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
   function maybeTarget(noComma) {
     return function(type) {
       if (type == ".") return cont(noComma ? targetNoComma : target);
+      else if (type == "variable" && isTS) return cont(maybeTypeArgs, noComma ? maybeoperatorNoComma : maybeoperatorComma)
       else return pass(noComma ? expressionNoComma : expression);
     };
   }
@@ -463,22 +513,33 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     if (type == "variable") {cx.marked = "property"; return cont();}
   }
   function objprop(type, value) {
-    if (type == "async") return cont(objprop);
-    if (type == "variable" || cx.style == "keyword") {
+    if (type == "async") {
+      cx.marked = "property";
+      return cont(objprop);
+    } else if (type == "variable" || cx.style == "keyword") {
       cx.marked = "property";
       if (value == "get" || value == "set") return cont(getterSetter);
+      var m // Work around fat-arrow-detection complication for detecting typescript typed arrow params
+      if (isTS && cx.state.fatArrowAt == cx.stream.start && (m = cx.stream.match(/^\s*:\s*/, false)))
+        cx.state.fatArrowAt = cx.stream.pos + m[0].length
       return cont(afterprop);
     } else if (type == "number" || type == "string") {
       cx.marked = jsonldMode ? "property" : (cx.style + " property");
       return cont(afterprop);
     } else if (type == "jsonld-keyword") {
       return cont(afterprop);
-    } else if (type == "modifier") {
+    } else if (isTS && isModifier(value)) {
+      cx.marked = "keyword"
       return cont(objprop)
     } else if (type == "[") {
-      return cont(expression, expect("]"), afterprop);
+      return cont(expression, maybetype, expect("]"), afterprop);
     } else if (type == "spread") {
-      return cont(expression);
+      return cont(expressionNoComma, afterprop);
+    } else if (value == "*") {
+      cx.marked = "keyword";
+      return cont(objprop);
+    } else if (type == ":") {
+      return pass(afterprop)
     }
   }
   function getterSetter(type) {
@@ -490,9 +551,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     if (type == ":") return cont(expressionNoComma);
     if (type == "(") return pass(functiondef);
   }
-  function commasep(what, end) {
+  function commasep(what, end, sep) {
     function proceed(type, value) {
-      if (type == ",") {
+      if (sep ? sep.indexOf(type) > -1 : type == ",") {
         var lex = cx.state.lexical;
         if (lex.info == "call") lex.pos = (lex.pos || 0) + 1;
         return cont(function(type, value) {
@@ -501,6 +562,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
         }, proceed);
       }
       if (type == end || value == end) return cont();
+      if (sep && sep.indexOf(";") > -1) return pass(what)
       return cont(expect(end));
     }
     return function(type, value) {
@@ -517,27 +579,91 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     if (type == "}") return cont();
     return pass(statement, block);
   }
-  function maybetype(type) {
-    if (isTS && type == ":") return cont(typeexpr);
+  function maybetype(type, value) {
+    if (isTS) {
+      if (type == ":") return cont(typeexpr);
+      if (value == "?") return cont(maybetype);
+    }
   }
-  function maybedefault(_, value) {
-    if (value == "=") return cont(expressionNoComma);
+  function maybetypeOrIn(type, value) {
+    if (isTS && (type == ":" || value == "in")) return cont(typeexpr)
   }
-  function typeexpr(type) {
-    if (type == "variable") {cx.marked = "variable-3"; return cont(afterType);}
+  function mayberettype(type) {
+    if (isTS && type == ":") {
+      if (cx.stream.match(/^\s*\w+\s+is\b/, false)) return cont(expression, isKW, typeexpr)
+      else return cont(typeexpr)
+    }
+  }
+  function isKW(_, value) {
+    if (value == "is") {
+      cx.marked = "keyword"
+      return cont()
+    }
+  }
+  function typeexpr(type, value) {
+    if (value == "keyof" || value == "typeof" || value == "infer") {
+      cx.marked = "keyword"
+      return cont(value == "typeof" ? expressionNoComma : typeexpr)
+    }
+    if (type == "variable" || value == "void") {
+      cx.marked = "type"
+      return cont(afterType)
+    }
+    if (value == "|" || value == "&") return cont(typeexpr)
+    if (type == "string" || type == "number" || type == "atom") return cont(afterType);
+    if (type == "[") return cont(pushlex("]"), commasep(typeexpr, "]", ","), poplex, afterType)
+    if (type == "{") return cont(pushlex("}"), commasep(typeprop, "}", ",;"), poplex, afterType)
+    if (type == "(") return cont(commasep(typearg, ")"), maybeReturnType, afterType)
+    if (type == "<") return cont(commasep(typeexpr, ">"), typeexpr)
+  }
+  function maybeReturnType(type) {
+    if (type == "=>") return cont(typeexpr)
+  }
+  function typeprop(type, value) {
+    if (type == "variable" || cx.style == "keyword") {
+      cx.marked = "property"
+      return cont(typeprop)
+    } else if (value == "?" || type == "number" || type == "string") {
+      return cont(typeprop)
+    } else if (type == ":") {
+      return cont(typeexpr)
+    } else if (type == "[") {
+      return cont(expect("variable"), maybetypeOrIn, expect("]"), typeprop)
+    } else if (type == "(") {
+      return pass(functiondecl, typeprop)
+    }
+  }
+  function typearg(type, value) {
+    if (type == "variable" && cx.stream.match(/^\s*[?:]/, false) || value == "?") return cont(typearg)
+    if (type == ":") return cont(typeexpr)
+    if (type == "spread") return cont(typearg)
+    return pass(typeexpr)
   }
   function afterType(type, value) {
-    if (value == "<") return cont(commasep(typeexpr, ">"), afterType)
-    if (type == "[") return cont(expect("]"), afterType)
+    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
+    if (value == "|" || type == "." || value == "&") return cont(typeexpr)
+    if (type == "[") return cont(typeexpr, expect("]"), afterType)
+    if (value == "extends" || value == "implements") { cx.marked = "keyword"; return cont(typeexpr) }
+    if (value == "?") return cont(typeexpr, expect(":"), typeexpr)
   }
-  function vardef() {
+  function maybeTypeArgs(_, value) {
+    if (value == "<") return cont(pushlex(">"), commasep(typeexpr, ">"), poplex, afterType)
+  }
+  function typeparam() {
+    return pass(typeexpr, maybeTypeDefault)
+  }
+  function maybeTypeDefault(_, value) {
+    if (value == "=") return cont(typeexpr)
+  }
+  function vardef(_, value) {
+    if (value == "enum") {cx.marked = "keyword"; return cont(enumdef)}
     return pass(pattern, maybetype, maybeAssign, vardefCont);
   }
   function pattern(type, value) {
-    if (type == "modifier") return cont(pattern)
+    if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(pattern) }
     if (type == "variable") { register(value); return cont(); }
     if (type == "spread") return cont(pattern);
-    if (type == "[") return contCommasep(pattern, "]");
+    if (type == "[") return contCommasep(eltpattern, "]");
     if (type == "{") return contCommasep(proppattern, "}");
   }
   function proppattern(type, value) {
@@ -548,8 +674,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     if (type == "variable") cx.marked = "property";
     if (type == "spread") return cont(pattern);
     if (type == "}") return pass();
+    if (type == "[") return cont(expression, expect(']'), expect(':'), proppattern);
     return cont(expect(":"), pattern, maybeAssign);
   }
+  function eltpattern() {
+    return pass(pattern, maybeAssign)
+  }
   function maybeAssign(_type, value) {
     if (value == "=") return cont(expressionNoComma);
   }
@@ -559,73 +689,109 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
   function maybeelse(type, value) {
     if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, poplex);
   }
-  function forspec(type) {
-    if (type == "(") return cont(pushlex(")"), forspec1, expect(")"), poplex);
+  function forspec(type, value) {
+    if (value == "await") return cont(forspec);
+    if (type == "(") return cont(pushlex(")"), forspec1, poplex);
   }
   function forspec1(type) {
-    if (type == "var") return cont(vardef, expect(";"), forspec2);
-    if (type == ";") return cont(forspec2);
-    if (type == "variable") return cont(formaybeinof);
-    return pass(expression, expect(";"), forspec2);
-  }
-  function formaybeinof(_type, value) {
-    if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
-    return cont(maybeoperatorComma, forspec2);
+    if (type == "var") return cont(vardef, forspec2);
+    if (type == "variable") return cont(forspec2);
+    return pass(forspec2)
   }
   function forspec2(type, value) {
-    if (type == ";") return cont(forspec3);
-    if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression); }
-    return pass(expression, expect(";"), forspec3);
-  }
-  function forspec3(type) {
-    if (type != ")") cont(expression);
+    if (type == ")") return cont()
+    if (type == ";") return cont(forspec2)
+    if (value == "in" || value == "of") { cx.marked = "keyword"; return cont(expression, forspec2) }
+    return pass(expression, forspec2)
   }
   function functiondef(type, value) {
     if (value == "*") {cx.marked = "keyword"; return cont(functiondef);}
     if (type == "variable") {register(value); return cont(functiondef);}
-    if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, maybetype, statement, popcontext);
+    if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, statement, popcontext);
+    if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef)
   }
-  function funarg(type) {
+  function functiondecl(type, value) {
+    if (value == "*") {cx.marked = "keyword"; return cont(functiondecl);}
+    if (type == "variable") {register(value); return cont(functiondecl);}
+    if (type == "(") return cont(pushcontext, pushlex(")"), commasep(funarg, ")"), poplex, mayberettype, popcontext);
+    if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondecl)
+  }
+  function typename(type, value) {
+    if (type == "keyword" || type == "variable") {
+      cx.marked = "type"
+      return cont(typename)
+    } else if (value == "<") {
+      return cont(pushlex(">"), commasep(typeparam, ">"), poplex)
+    }
+  }
+  function funarg(type, value) {
+    if (value == "@") cont(expression, funarg)
     if (type == "spread") return cont(funarg);
-    return pass(pattern, maybetype, maybedefault);
+    if (isTS && isModifier(value)) { cx.marked = "keyword"; return cont(funarg); }
+    if (isTS && type == "this") return cont(maybetype, maybeAssign)
+    return pass(pattern, maybetype, maybeAssign);
+  }
+  function classExpression(type, value) {
+    // Class expressions may have an optional name.
+    if (type == "variable") return className(type, value);
+    return classNameAfter(type, value);
   }
   function className(type, value) {
     if (type == "variable") {register(value); return cont(classNameAfter);}
   }
   function classNameAfter(type, value) {
-    if (value == "extends") return cont(expression, classNameAfter);
+    if (value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, classNameAfter)
+    if (value == "extends" || value == "implements" || (isTS && type == ",")) {
+      if (value == "implements") cx.marked = "keyword";
+      return cont(isTS ? typeexpr : expression, classNameAfter);
+    }
     if (type == "{") return cont(pushlex("}"), classBody, poplex);
   }
   function classBody(type, value) {
-    if (type == "variable" || cx.style == "keyword") {
-      if (value == "static") {
-        cx.marked = "keyword";
-        return cont(classBody);
-      }
-      cx.marked = "property";
-      if (value == "get" || value == "set") return cont(classGetterSetter, functiondef, classBody);
-      return cont(functiondef, classBody);
+    if (type == "async" ||
+        (type == "variable" &&
+         (value == "static" || value == "get" || value == "set" || (isTS && isModifier(value))) &&
+         cx.stream.match(/^\s+[\w$\xa1-\uffff]/, false))) {
+      cx.marked = "keyword";
+      return cont(classBody);
     }
+    if (type == "variable" || cx.style == "keyword") {
+      cx.marked = "property";
+      return cont(isTS ? classfield : functiondef, classBody);
+    }
+    if (type == "number" || type == "string") return cont(isTS ? classfield : functiondef, classBody);
+    if (type == "[")
+      return cont(expression, maybetype, expect("]"), isTS ? classfield : functiondef, classBody)
     if (value == "*") {
       cx.marked = "keyword";
       return cont(classBody);
     }
-    if (type == ";") return cont(classBody);
+    if (isTS && type == "(") return pass(functiondecl, classBody)
+    if (type == ";" || type == ",") return cont(classBody);
     if (type == "}") return cont();
+    if (value == "@") return cont(expression, classBody)
   }
-  function classGetterSetter(type) {
-    if (type != "variable") return pass();
-    cx.marked = "property";
-    return cont();
+  function classfield(type, value) {
+    if (value == "?") return cont(classfield)
+    if (type == ":") return cont(typeexpr, maybeAssign)
+    if (value == "=") return cont(expressionNoComma)
+    var context = cx.state.lexical.prev, isInterface = context && context.info == "interface"
+    return pass(isInterface ? functiondecl : functiondef)
   }
-  function afterExport(_type, value) {
+  function afterExport(type, value) {
     if (value == "*") { cx.marked = "keyword"; return cont(maybeFrom, expect(";")); }
     if (value == "default") { cx.marked = "keyword"; return cont(expression, expect(";")); }
+    if (type == "{") return cont(commasep(exportField, "}"), maybeFrom, expect(";"));
     return pass(statement);
   }
+  function exportField(type, value) {
+    if (value == "as") { cx.marked = "keyword"; return cont(expect("variable")); }
+    if (type == "variable") return pass(expressionNoComma, exportField);
+  }
   function afterImport(type) {
     if (type == "string") return cont();
-    return pass(importSpec, maybeFrom);
+    if (type == "(") return pass(expression);
+    return pass(importSpec, maybeMoreImports, maybeFrom);
   }
   function importSpec(type, value) {
     if (type == "{") return contCommasep(importSpec, "}");
@@ -633,6 +799,9 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     if (value == "*") cx.marked = "keyword";
     return cont(maybeAs);
   }
+  function maybeMoreImports(type) {
+    if (type == ",") return cont(importSpec, maybeMoreImports)
+  }
   function maybeAs(_type, value) {
     if (value == "as") { cx.marked = "keyword"; return cont(importSpec); }
   }
@@ -641,7 +810,13 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
   }
   function arrayLiteral(type) {
     if (type == "]") return cont();
-    return pass(expressionNoComma, commasep(expressionNoComma, "]"));
+    return pass(commasep(expressionNoComma, "]"));
+  }
+  function enumdef() {
+    return pass(pushlex("form"), pattern, expect("{"), pushlex("}"), commasep(enummember, "}"), poplex, poplex)
+  }
+  function enummember() {
+    return pass(pattern, maybeAssign);
   }
 
   function isContinuedStatement(state, textAfter) {
@@ -650,6 +825,12 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
       /[,.]/.test(textAfter.charAt(0));
   }
 
+  function expressionAllowed(stream, state, backUp) {
+    return state.tokenize == tokenBase &&
+      /^(?:operator|sof|keyword [bcd]|case|new|export|default|spread|[\[{}\(,;:]|=>)$/.test(state.lastType) ||
+      (state.lastType == "quasi" && /\{\s*$/.test(stream.string.slice(0, stream.pos - (backUp || 0))))
+  }
+
   // Interface
 
   return {
@@ -660,7 +841,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
         cc: [],
         lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
         localVars: parserConfig.localVars,
-        context: parserConfig.localVars && {vars: parserConfig.localVars},
+        context: parserConfig.localVars && new Context(null, null, false),
         indented: basecolumn || 0
       };
       if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
@@ -685,19 +866,23 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     indent: function(state, textAfter) {
       if (state.tokenize == tokenComment) return CodeMirror.Pass;
       if (state.tokenize != tokenBase) return 0;
-      var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
+      var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical, top
       // Kludge to prevent 'maybelse' from blocking lexical scope pops
       if (!/^\s*else\b/.test(textAfter)) for (var i = state.cc.length - 1; i >= 0; --i) {
         var c = state.cc[i];
         if (c == poplex) lexical = lexical.prev;
         else if (c != maybeelse) break;
       }
-      if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
+      while ((lexical.type == "stat" || lexical.type == "form") &&
+             (firstChar == "}" || ((top = state.cc[state.cc.length - 1]) &&
+                                   (top == maybeoperatorComma || top == maybeoperatorNoComma) &&
+                                   !/^[,\.=+\-*:?[\(]/.test(textAfter))))
+        lexical = lexical.prev;
       if (statementIndent && lexical.type == ")" && lexical.prev.type == "stat")
         lexical = lexical.prev;
       var type = lexical.type, closing = firstChar == type;
 
-      if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info + 1 : 0);
+      if (type == "vardef") return lexical.indented + (state.lastType == "operator" || state.lastType == "," ? lexical.info.length + 1 : 0);
       else if (type == "form" && firstChar == "{") return lexical.indented;
       else if (type == "form") return lexical.indented + indentUnit;
       else if (type == "stat")
@@ -711,6 +896,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
     blockCommentStart: jsonMode ? null : "/*",
     blockCommentEnd: jsonMode ? null : "*/",
+    blockCommentContinue: jsonMode ? null : " * ",
     lineComment: jsonMode ? null : "//",
     fold: "brace",
     closeBrackets: "()[]{}''\"\"``",
@@ -720,6 +906,7 @@ CodeMirror.defineMode("javascript", function(config, parserConfig) {
     jsonMode: jsonMode,
 
     expressionAllowed: expressionAllowed,
+
     skipExpression: function(state) {
       var top = state.cc[state.cc.length - 1]
       if (top == expression || top == expressionNoComma) state.cc.pop()
diff --git a/public/vendor/plugins/codemirror/mode/javascript/json-ld.html b/public/vendor/plugins/codemirror/mode/javascript/json-ld.html
index 3a37f0bce6..6a29c14453 100644
--- a/public/vendor/plugins/codemirror/mode/javascript/json-ld.html
+++ b/public/vendor/plugins/codemirror/mode/javascript/json-ld.html
@@ -10,9 +10,9 @@
 <script src="../../addon/comment/continuecomment.js"></script>
 <script src="../../addon/comment/comment.js"></script>
 <script src="javascript.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id="nav">
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"/></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"/></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/javascript/test.js b/public/vendor/plugins/codemirror/mode/javascript/test.js
index 8916b7558f..327eac76b9 100644
--- a/public/vendor/plugins/codemirror/mode/javascript/test.js
+++ b/public/vendor/plugins/codemirror/mode/javascript/test.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function() {
   var mode = CodeMirror.getMode({indentUnit: 2}, "javascript");
@@ -31,13 +31,29 @@
 
   MT("class",
      "[keyword class] [def Point] [keyword extends] [variable SuperThing] {",
-     "  [property get] [property prop]() { [keyword return] [number 24]; }",
+     "  [keyword get] [property prop]() { [keyword return] [number 24]; }",
      "  [property constructor]([def x], [def y]) {",
      "    [keyword super]([string 'something']);",
      "    [keyword this].[property x] [operator =] [variable-2 x];",
      "  }",
      "}");
 
+  MT("anonymous_class_expression",
+     "[keyword const] [def Adder] [operator =] [keyword class] [keyword extends] [variable Arithmetic] {",
+     "  [property add]([def a], [def b]) {}",
+     "};");
+
+  MT("named_class_expression",
+     "[keyword const] [def Subber] [operator =] [keyword class] [def Subtract] {",
+     "  [property sub]([def a], [def b]) {}",
+     "};");
+
+  MT("class_async_method",
+     "[keyword class] [def Foo] {",
+     "  [property sayName1]() {}",
+     "  [keyword async] [property sayName2]() {}",
+     "}");
+
   MT("import",
      "[keyword function] [def foo]() {",
      "  [keyword import] [def $] [keyword from] [string 'jquery'];",
@@ -47,6 +63,12 @@
   MT("import_trailing_comma",
      "[keyword import] {[def foo], [def bar],} [keyword from] [string 'baz']")
 
+  MT("import_dynamic",
+     "[keyword import]([string 'baz']).[property then]")
+
+  MT("import_dynamic",
+     "[keyword const] [def t] [operator =] [keyword import]([string 'baz']).[property then]")
+
   MT("const",
      "[keyword function] [def f]() {",
      "  [keyword const] [[ [def a], [def b] ]] [operator =] [[ [number 1], [number 2] ]];",
@@ -55,12 +77,44 @@
   MT("for/of",
      "[keyword for]([keyword let] [def of] [keyword of] [variable something]) {}");
 
+  MT("for await",
+     "[keyword for] [keyword await]([keyword let] [def of] [keyword of] [variable something]) {}");
+
   MT("generator",
      "[keyword function*] [def repeat]([def n]) {",
      "  [keyword for]([keyword var] [def i] [operator =] [number 0]; [variable-2 i] [operator <] [variable-2 n]; [operator ++][variable-2 i])",
      "    [keyword yield] [variable-2 i];",
      "}");
 
+  MT("let_scoping",
+     "[keyword function] [def scoped]([def n]) {",
+     "  { [keyword var] [def i]; } [variable-2 i];",
+     "  { [keyword let] [def j]; [variable-2 j]; } [variable j];",
+     "  [keyword if] ([atom true]) { [keyword const] [def k]; [variable-2 k]; } [variable k];",
+     "}");
+
+  MT("switch_scoping",
+     "[keyword switch] ([variable x]) {",
+     "  [keyword default]:",
+     "    [keyword let] [def j];",
+     "    [keyword return] [variable-2 j]",
+     "}",
+     "[variable j];")
+
+  MT("leaving_scope",
+     "[keyword function] [def a]() {",
+     "  {",
+     "    [keyword const] [def x] [operator =] [number 1]",
+     "    [keyword if] ([atom true]) {",
+     "      [keyword let] [def y] [operator =] [number 2]",
+     "      [keyword var] [def z] [operator =] [number 3]",
+     "      [variable console].[property log]([variable-2 x], [variable-2 y], [variable-2 z])",
+     "    }",
+     "    [variable console].[property log]([variable-2 x], [variable y], [variable-2 z])",
+     "  }",
+     "  [variable console].[property log]([variable x], [variable y], [variable-2 z])",
+     "}")
+
   MT("quotedStringAddition",
      "[keyword let] [def f] [operator =] [variable a] [operator +] [string 'fatarrow'] [operator +] [variable c];");
 
@@ -73,6 +127,9 @@
      "[keyword let] [def f] [operator =] ([[ [def a], [def b] ]], [def c]) [operator =>] [variable-2 a] [operator +] [variable-2 c];",
      "[variable c];");
 
+  MT("fatArrow_stringDefault",
+     "([def a], [def b] [operator =] [string 'x\\'y']) [operator =>] [variable-2 a] [operator +] [variable-2 b]")
+
   MT("spread",
      "[keyword function] [def f]([def a], [meta ...][def b]) {",
      "  [variable something]([variable-2 a], [meta ...][variable-2 b]);",
@@ -140,6 +197,19 @@
      "    [number 1];",
      "[number 2];");
 
+  MT("indent_semicolonless_if",
+     "[keyword function] [def foo]() {",
+     "  [keyword if] ([variable x])",
+     "    [variable foo]()",
+     "}")
+
+  MT("indent_semicolonless_if_with_statement",
+     "[keyword function] [def foo]() {",
+     "  [keyword if] ([variable x])",
+     "    [variable foo]()",
+     "  [variable bar]()",
+     "}")
+
   MT("multilinestring",
      "[keyword var] [def x] [operator =] [string 'foo\\]",
      "[string bar'];");
@@ -159,6 +229,12 @@
      "  [keyword return] [variable-2 x];",
      "}");
 
+  MT(
+    "param_destructuring",
+    "[keyword function] [def foo]([def x] [operator =] [string-2 `foo${][number 10][string-2 }bar`]) {",
+    "  [keyword return] [variable-2 x];",
+    "}");
+
   MT("new_target",
      "[keyword function] [def F]([def target]) {",
      "  [keyword if] ([variable-2 target] [operator &&] [keyword new].[keyword target].[property name]) {",
@@ -167,6 +243,236 @@
      "  }",
      "}");
 
+  MT("async",
+     "[keyword async] [keyword function] [def foo]([def args]) { [keyword return] [atom true]; }");
+
+  MT("async_assignment",
+     "[keyword const] [def foo] [operator =] [keyword async] [keyword function] ([def args]) { [keyword return] [atom true]; };");
+
+  MT("async_object",
+     "[keyword let] [def obj] [operator =] { [property async]: [atom false] };");
+
+  // async be highlighet as keyword and foo as def, but it requires potentially expensive look-ahead. See #4173
+  MT("async_object_function",
+     "[keyword let] [def obj] [operator =] { [property async] [property foo]([def args]) { [keyword return] [atom true]; } };");
+
+  MT("async_object_properties",
+     "[keyword let] [def obj] [operator =] {",
+     "  [property prop1]: [keyword async] [keyword function] ([def args]) { [keyword return] [atom true]; },",
+     "  [property prop2]: [keyword async] [keyword function] ([def args]) { [keyword return] [atom true]; },",
+     "  [property prop3]: [keyword async] [keyword function] [def prop3]([def args]) { [keyword return] [atom true]; },",
+     "};");
+
+  MT("async_arrow",
+     "[keyword const] [def foo] [operator =] [keyword async] ([def args]) [operator =>] { [keyword return] [atom true]; };");
+
+  MT("async_jquery",
+     "[variable $].[property ajax]({",
+     "  [property url]: [variable url],",
+     "  [property async]: [atom true],",
+     "  [property method]: [string 'GET']",
+     "});");
+
+  MT("async_variable",
+     "[keyword const] [def async] [operator =] {[property a]: [number 1]};",
+     "[keyword const] [def foo] [operator =] [string-2 `bar ${][variable async].[property a][string-2 }`];")
+
+  MT("bigint", "[number 1n] [operator +] [number 0x1afn] [operator +] [number 0o064n] [operator +] [number 0b100n];")
+
+  MT("async_comment",
+     "[keyword async] [comment /**/] [keyword function] [def foo]([def args]) { [keyword return] [atom true]; }");
+
+  MT("indent_switch",
+     "[keyword switch] ([variable x]) {",
+     "  [keyword default]:",
+     "    [keyword return] [number 2]",
+     "}")
+
+  MT("regexp_corner_case",
+     "[operator +]{} [operator /] [atom undefined];",
+     "[[[meta ...][string-2 /\\//] ]];",
+     "[keyword void] [string-2 /\\//];",
+     "[keyword do] [string-2 /\\//]; [keyword while] ([number 0]);",
+     "[keyword if] ([number 0]) {} [keyword else] [string-2 /\\//];",
+     "[string-2 `${][variable async][operator ++][string-2 }//`];",
+     "[string-2 `${]{} [operator /] [string-2 /\\//}`];")
+
+  MT("return_eol",
+     "[keyword return]",
+     "{} [string-2 /5/]")
+
+  MT("numeric separator",
+     "[number 123_456];",
+     "[number 0xdead_c0de];",
+     "[number 0o123_456];",
+     "[number 0b1101_1101];",
+     "[number .123_456e0_1];",
+     "[number 1E+123_456];",
+     "[number 12_34_56n];")
+
+  MT("underscore property",
+     "[variable something].[property _property];",
+     "[variable something].[property _123];",
+     "[variable something].[property _for];",
+     "[variable _for];",
+     "[variable _123];")
+
+  var ts_mode = CodeMirror.getMode({indentUnit: 2}, "application/typescript")
+  function TS(name) {
+    test.mode(name, ts_mode, Array.prototype.slice.call(arguments, 1))
+  }
+
+  TS("typescript_extend_type",
+     "[keyword class] [def Foo] [keyword extends] [type Some][operator <][type Type][operator >] {}")
+
+  TS("typescript_arrow_type",
+     "[keyword let] [def x]: ([variable arg]: [type Type]) [operator =>] [type ReturnType]")
+
+  TS("typescript_class",
+     "[keyword class] [def Foo] {",
+     "  [keyword public] [keyword static] [property main]() {}",
+     "  [keyword private] [property _foo]: [type string];",
+     "}")
+
+  TS("typescript_literal_types",
+     "[keyword import] [keyword *] [keyword as] [def Sequelize] [keyword from] [string 'sequelize'];",
+     "[keyword interface] [def MyAttributes] {",
+     "  [property truthy]: [string 'true'] [operator |] [number 1] [operator |] [atom true];",
+     "  [property falsy]: [string 'false'] [operator |] [number 0] [operator |] [atom false];",
+     "}",
+     "[keyword interface] [def MyInstance] [keyword extends] [type Sequelize].[type Instance] [operator <] [type MyAttributes] [operator >] {",
+     "  [property rawAttributes]: [type MyAttributes];",
+     "  [property truthy]: [string 'true'] [operator |] [number 1] [operator |] [atom true];",
+     "  [property falsy]: [string 'false'] [operator |] [number 0] [operator |] [atom false];",
+     "}")
+
+  TS("typescript_extend_operators",
+     "[keyword export] [keyword interface] [def UserModel] [keyword extends]",
+     "  [type Sequelize].[type Model] [operator <] [type UserInstance], [type UserAttributes] [operator >] {",
+     "    [property findById]: (",
+     "    [variable userId]: [type number]",
+     "    ) [operator =>] [type Promise] [operator <] [type Array] [operator <] { [property id], [property name] } [operator >>];",
+     "    [property updateById]: (",
+     "    [variable userId]: [type number],",
+     "    [variable isActive]: [type boolean]",
+     "    ) [operator =>] [type Promise] [operator <] [type AccountHolderNotificationPreferenceInstance] [operator >];",
+     "  }")
+
+  TS("typescript_interface_with_const",
+     "[keyword const] [def hello]: {",
+     "  [property prop1][operator ?]: [type string];",
+     "  [property prop2][operator ?]: [type string];",
+     "} [operator =] {};")
+
+  TS("typescript_double_extend",
+     "[keyword export] [keyword interface] [def UserAttributes] {",
+     "  [property id][operator ?]: [type number];",
+     "  [property createdAt][operator ?]: [type Date];",
+     "}",
+     "[keyword export] [keyword interface] [def UserInstance] [keyword extends] [type Sequelize].[type Instance][operator <][type UserAttributes][operator >], [type UserAttributes] {",
+     "  [property id]: [type number];",
+     "  [property createdAt]: [type Date];",
+     "}");
+
+  TS("typescript_index_signature",
+     "[keyword interface] [def A] {",
+     "  [[ [variable prop]: [type string] ]]: [type any];",
+     "  [property prop1]: [type any];",
+     "}");
+
+  TS("typescript_generic_class",
+     "[keyword class] [def Foo][operator <][type T][operator >] {",
+     "  [property bar]() {}",
+     "  [property foo](): [type Foo] {}",
+     "}")
+
+  TS("typescript_type_when_keyword",
+     "[keyword export] [keyword type] [type AB] [operator =] [type A] [operator |] [type B];",
+     "[keyword type] [type Flags] [operator =] {",
+     "  [property p1]: [type string];",
+     "  [property p2]: [type boolean];",
+     "};")
+
+  TS("typescript_type_when_not_keyword",
+     "[keyword class] [def HasType] {",
+     "  [property type]: [type string];",
+     "  [property constructor]([def type]: [type string]) {",
+     "    [keyword this].[property type] [operator =] [variable-2 type];",
+     "  }",
+     "  [property setType]({ [def type] }: { [property type]: [type string]; }) {",
+     "    [keyword this].[property type] [operator =] [variable-2 type];",
+     "  }",
+     "}")
+
+  TS("typescript_function_generics",
+     "[keyword function] [def a]() {}",
+     "[keyword function] [def b][operator <][type IA] [keyword extends] [type object], [type IB] [keyword extends] [type object][operator >]() {}",
+     "[keyword function] [def c]() {}")
+
+  TS("typescript_complex_return_type",
+     "[keyword function] [def A]() {",
+     "  [keyword return] [keyword this].[property property];",
+     "}",
+     "[keyword function] [def B](): [type Promise][operator <]{ [[ [variable key]: [type string] ]]: [type any] } [operator |] [atom null][operator >] {",
+     "  [keyword return] [keyword this].[property property];",
+     "}")
+
+  TS("typescript_complex_type_casting",
+     "[keyword const] [def giftpay] [operator =] [variable config].[property get]([string 'giftpay']) [keyword as] { [[ [variable platformUuid]: [type string] ]]: { [property version]: [type number]; [property apiCode]: [type string]; } };")
+
+  TS("typescript_keyof",
+     "[keyword function] [def x][operator <][type T] [keyword extends] [keyword keyof] [type X][operator >]([def a]: [type T]) {",
+     "  [keyword return]")
+
+  TS("typescript_new_typeargs",
+     "[keyword let] [def x] [operator =] [keyword new] [variable Map][operator <][type string], [type Date][operator >]([string-2 `foo${][variable bar][string-2 }`])")
+
+  TS("modifiers",
+     "[keyword class] [def Foo] {",
+     "  [keyword public] [keyword abstract] [property bar]() {}",
+     "  [property constructor]([keyword readonly] [keyword private] [def x]) {}",
+     "}")
+
+  TS("arrow prop",
+     "({[property a]: [def p] [operator =>] [variable-2 p]})")
+
+  TS("generic in function call",
+     "[keyword this].[property a][operator <][type Type][operator >]([variable foo]);",
+     "[keyword this].[property a][operator <][variable Type][operator >][variable foo];")
+
+  TS("type guard",
+     "[keyword class] [def Appler] {",
+     "  [keyword static] [property assertApple]([def fruit]: [type Fruit]): [variable-2 fruit] [keyword is] [type Apple] {",
+     "    [keyword if] ([operator !]([variable-2 fruit] [keyword instanceof] [variable Apple]))",
+     "      [keyword throw] [keyword new] [variable Error]();",
+     "  }",
+     "}")
+
+  TS("type as variable",
+     "[variable type] [operator =] [variable x] [keyword as] [type Bar];");
+
+  TS("enum body",
+     "[keyword export] [keyword const] [keyword enum] [def CodeInspectionResultType] {",
+     "  [def ERROR] [operator =] [string 'problem_type_error'],",
+     "  [def WARNING] [operator =] [string 'problem_type_warning'],",
+     "  [def META],",
+     "}")
+
+  TS("parenthesized type",
+     "[keyword class] [def Foo] {",
+     "  [property x] [operator =] [keyword new] [variable A][operator <][type B], [type string][operator |](() [operator =>] [type void])[operator >]();",
+     "  [keyword private] [property bar]();",
+     "}")
+
+  TS("abstract class",
+     "[keyword export] [keyword abstract] [keyword class] [def Foo] {}")
+
+  TS("interface without semicolons",
+     "[keyword interface] [def Foo] {",
+     "  [property greet]([def x]: [type int]): [type blah]",
+     "  [property bar]: [type void]",
+     "}")
+
   var jsonld_mode = CodeMirror.getMode(
     {indentUnit: 2},
     {name: "javascript", jsonld: true}
diff --git a/public/vendor/plugins/codemirror/mode/javascript/typescript.html b/public/vendor/plugins/codemirror/mode/javascript/typescript.html
index 2cfc5381fe..3b217a44f9 100644
--- a/public/vendor/plugins/codemirror/mode/javascript/typescript.html
+++ b/public/vendor/plugins/codemirror/mode/javascript/typescript.html
@@ -6,10 +6,11 @@
 
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
+<script src="../../addon/edit/matchbrackets.js"></script>
 <script src="javascript.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -28,13 +29,13 @@
 
 <div><textarea id="code" name="code">
 class Greeter {
-	greeting: string;
-	constructor (message: string) {
-		this.greeting = message;
-	}
-	greet() {
-		return "Hello, " + this.greeting;
-	}
+  greeting: string;
+  constructor (message: string) {
+    this.greeting = message;
+  }
+  greet() {
+    return "Hello, " + this.greeting;
+  }
 }   
 
 var greeter = new Greeter("world");
@@ -42,7 +43,7 @@ var greeter = new Greeter("world");
 var button = document.createElement('button')
 button.innerText = "Say Hello"
 button.onclick = function() {
-	alert(greeter.greet())
+  alert(greeter.greet())
 }
 
 document.body.appendChild(button)
diff --git a/public/vendor/plugins/codemirror/mode/jinja2/index.html b/public/vendor/plugins/codemirror/mode/jinja2/index.html
index 5a70e9153b..a942549dac 100644
--- a/public/vendor/plugins/codemirror/mode/jinja2/index.html
+++ b/public/vendor/plugins/codemirror/mode/jinja2/index.html
@@ -7,9 +7,9 @@
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
 <script src="jinja2.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/jinja2/jinja2.js b/public/vendor/plugins/codemirror/mode/jinja2/jinja2.js
index ed195581cf..77c9b22cae 100644
--- a/public/vendor/plugins/codemirror/mode/jinja2/jinja2.js
+++ b/public/vendor/plugins/codemirror/mode/jinja2/jinja2.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -107,7 +107,7 @@
         }
         return "variable";
       } else if (stream.eat("{")) {
-        if (ch = stream.eat("#")) {
+        if (stream.eat("#")) {
           state.incomment = true;
           if(!stream.skipTo("#}")) {
             stream.skipToEnd();
@@ -136,7 +136,11 @@
       },
       token: function (stream, state) {
         return state.tokenize(stream, state);
-      }
+      },
+      blockCommentStart: "{#",
+      blockCommentEnd: "#}"
     };
   });
+
+  CodeMirror.defineMIME("text/jinja2", "jinja2");
 });
diff --git a/public/vendor/plugins/codemirror/mode/jsx/index.html b/public/vendor/plugins/codemirror/mode/jsx/index.html
index cb51edb364..4df632e1f7 100644
--- a/public/vendor/plugins/codemirror/mode/jsx/index.html
+++ b/public/vendor/plugins/codemirror/mode/jsx/index.html
@@ -9,9 +9,9 @@
 <script src="../javascript/javascript.js"></script>
 <script src="../xml/xml.js"></script>
 <script src="jsx.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -84,6 +84,6 @@ var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
 <p>JSX Mode for <a href="http://facebook.github.io/react">React</a>'s
 JavaScript syntax extension.</p>
 
-<p><strong>MIME types defined:</strong> <code>text/jsx</code>.</p>
+<p><strong>MIME types defined:</strong> <code>text/jsx</code>, <code>text/typescript-jsx</code>.</p>
 
 </article>
diff --git a/public/vendor/plugins/codemirror/mode/jsx/jsx.js b/public/vendor/plugins/codemirror/mode/jsx/jsx.js
index aff01b8d35..889d3fe5e7 100644
--- a/public/vendor/plugins/codemirror/mode/jsx/jsx.js
+++ b/public/vendor/plugins/codemirror/mode/jsx/jsx.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -26,13 +26,13 @@
   }
 
   CodeMirror.defineMode("jsx", function(config, modeConfig) {
-    var xmlMode = CodeMirror.getMode(config, {name: "xml", allowMissing: true, multilineTagIndentPastTag: false})
+    var xmlMode = CodeMirror.getMode(config, {name: "xml", allowMissing: true, multilineTagIndentPastTag: false, allowMissingTagName: true})
     var jsMode = CodeMirror.getMode(config, modeConfig && modeConfig.base || "javascript")
 
     function flatXMLIndent(state) {
       var tagName = state.tagName
       state.tagName = null
-      var result = xmlMode.indent(state, "")
+      var result = xmlMode.indent(state, "", "")
       state.tagName = tagName
       return result
     }
@@ -105,7 +105,7 @@
     function jsToken(stream, state, cx) {
       if (stream.peek() == "<" && jsMode.expressionAllowed(stream, cx.state)) {
         jsMode.skipExpression(cx.state)
-        state.context = new Context(CodeMirror.startState(xmlMode, jsMode.indent(cx.state, "")),
+        state.context = new Context(CodeMirror.startState(xmlMode, jsMode.indent(cx.state, "", "")),
                                     xmlMode, 0, state.context)
         return null
       }
@@ -144,4 +144,5 @@
   }, "xml", "javascript")
 
   CodeMirror.defineMIME("text/jsx", "jsx")
+  CodeMirror.defineMIME("text/typescript-jsx", {name: "jsx", base: {name: "javascript", typescript: true}})
 });
diff --git a/public/vendor/plugins/codemirror/mode/jsx/test.js b/public/vendor/plugins/codemirror/mode/jsx/test.js
index c54a8b24c9..5ecd5a8b01 100644
--- a/public/vendor/plugins/codemirror/mode/jsx/test.js
+++ b/public/vendor/plugins/codemirror/mode/jsx/test.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function() {
   var mode = CodeMirror.getMode({indentUnit: 2}, "jsx")
@@ -11,6 +11,9 @@
   MT("openclose",
      "([bracket&tag <][tag foo][bracket&tag >]hello [atom &amp;][bracket&tag </][tag foo][bracket&tag >][operator ++])")
 
+  MT("openclosefragment",
+     "([bracket&tag <><][tag foo][bracket&tag >]hello [atom &amp;][bracket&tag </][tag foo][bracket&tag ></>][operator ++])")
+
   MT("attr",
      "([bracket&tag <][tag foo] [attribute abc]=[string 'value'][bracket&tag >]hello [atom &amp;][bracket&tag </][tag foo][bracket&tag >][operator ++])")
 
@@ -33,6 +36,9 @@
   MT("preserve_js_context",
      "[variable x] [operator =] [string-2 `quasi${][bracket&tag <][tag foo][bracket&tag />][string-2 }quoted`]")
 
+  MT("string_interpolation",
+    "[variable x] [operator =] [string-2 `quasi<code>${] [number 10] [string-2 }</code>`]")
+
   MT("line_comment",
      "([bracket&tag <][tag foo] [comment // hello]",
      "   [bracket&tag ></][tag foo][bracket&tag >][operator ++])")
@@ -66,4 +72,20 @@
 
   MT("tag_attribute",
      "([bracket&tag <][tag foo] [attribute bar]=[bracket&tag <][tag foo][bracket&tag />/>][operator ++])")
+
+  var ts_mode = CodeMirror.getMode({indentUnit: 2}, "text/typescript-jsx")
+  function TS(name) { test.mode(name, ts_mode, Array.prototype.slice.call(arguments, 1)) }
+
+  TS("tsx_react_integration",
+     "[keyword interface] [def Props] {",
+     "  [property foo]: [type string];",
+     "}",
+     "[keyword class] [def MyComponent] [keyword extends] [type React].[type Component] [operator <] [type Props], [type any] [operator >] {",
+     "  [property render]() {",
+     "    [keyword return] [bracket&tag <][tag span][bracket&tag >]{[keyword this].[property props].[property foo]}[bracket&tag </][tag span][bracket&tag >]",
+     "  }",
+     "}",
+     "[bracket&tag <][tag MyComponent] [attribute foo]=[string \"bar\"] [bracket&tag />]; [comment //ok]",
+     "[bracket&tag <][tag MyComponent] [attribute foo]={[number 0]} [bracket&tag />]; [comment //error]")
+
 })()
diff --git a/public/vendor/plugins/codemirror/mode/julia/index.html b/public/vendor/plugins/codemirror/mode/julia/index.html
index e1492c210f..0261c921f8 100644
--- a/public/vendor/plugins/codemirror/mode/julia/index.html
+++ b/public/vendor/plugins/codemirror/mode/julia/index.html
@@ -6,10 +6,11 @@
 
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
+<script src="../../addon/edit/matchbrackets.js"></script>
 <script src="julia.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/julia/julia.js b/public/vendor/plugins/codemirror/mode/julia/julia.js
index 004de4431c..547669afbc 100644
--- a/public/vendor/plugins/codemirror/mode/julia/julia.js
+++ b/public/vendor/plugins/codemirror/mode/julia/julia.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -11,51 +11,70 @@
 })(function(CodeMirror) {
 "use strict";
 
-CodeMirror.defineMode("julia", function(_conf, parserConf) {
-  var ERRORCLASS = 'error';
-
+CodeMirror.defineMode("julia", function(config, parserConf) {
   function wordRegexp(words, end) {
-    if (typeof end === 'undefined') { end = "\\b"; }
+    if (typeof end === "undefined") { end = "\\b"; }
     return new RegExp("^((" + words.join(")|(") + "))" + end);
   }
 
   var octChar = "\\\\[0-7]{1,3}";
   var hexChar = "\\\\x[A-Fa-f0-9]{1,2}";
-  var specialChar = "\\\\[abfnrtv0%?'\"\\\\]";
-  var singleChar = "([^\\u0027\\u005C\\uD800-\\uDFFF]|[\\uD800-\\uDFFF][\\uDC00-\\uDFFF])";
-  var operators = parserConf.operators || /^\.?[|&^\\%*+\-<>!=\/]=?|\?|~|:|\$|\.[<>]|<<=?|>>>?=?|\.[<>=]=|->?|\/\/|\bin\b(?!\()|[\u2208\u2209](?!\()/;
-  var delimiters = parserConf.delimiters || /^[;,()[\]{}]/;
-  var identifiers = parserConf.identifiers || /^[_A-Za-z\u00A1-\uFFFF][\w\u00A1-\uFFFF]*!*/;
-  var charsList = [octChar, hexChar, specialChar, singleChar];
-  var blockOpeners = ["begin", "function", "type", "immutable", "let", "macro", "for", "while", "quote", "if", "else", "elseif", "try", "finally", "catch", "do"];
-  var blockClosers = ["end", "else", "elseif", "catch", "finally"];
-  var keywordList = ['if', 'else', 'elseif', 'while', 'for', 'begin', 'let', 'end', 'do', 'try', 'catch', 'finally', 'return', 'break', 'continue', 'global', 'local', 'const', 'export', 'import', 'importall', 'using', 'function', 'macro', 'module', 'baremodule', 'type', 'immutable', 'quote', 'typealias', 'abstract', 'bitstype'];
-  var builtinList = ['true', 'false', 'nothing', 'NaN', 'Inf'];
+  var sChar = "\\\\[abefnrtv0%?'\"\\\\]";
+  var uChar = "([^\\u0027\\u005C\\uD800-\\uDFFF]|[\\uD800-\\uDFFF][\\uDC00-\\uDFFF])";
+
+  var operators = parserConf.operators || wordRegexp([
+        "[<>]:", "[<>=]=", "<<=?", ">>>?=?", "=>", "->", "\\/\\/",
+        "[\\\\%*+\\-<>!=\\/^|&\\u00F7\\u22BB]=?", "\\?", "\\$", "~", ":",
+        "\\u00D7", "\\u2208", "\\u2209", "\\u220B", "\\u220C", "\\u2218",
+        "\\u221A", "\\u221B", "\\u2229", "\\u222A", "\\u2260", "\\u2264",
+        "\\u2265", "\\u2286", "\\u2288", "\\u228A", "\\u22C5",
+        "\\b(in|isa)\\b(?!\.?\\()"], "");
+  var delimiters = parserConf.delimiters || /^[;,()[\]{}]/;
+  var identifiers = parserConf.identifiers ||
+        /^[_A-Za-z\u00A1-\u2217\u2219-\uFFFF][\w\u00A1-\u2217\u2219-\uFFFF]*!*/;
+
+  var chars = wordRegexp([octChar, hexChar, sChar, uChar], "'");
+
+  var openersList = ["begin", "function", "type", "struct", "immutable", "let",
+        "macro", "for", "while", "quote", "if", "else", "elseif", "try",
+        "finally", "catch", "do"];
+
+  var closersList = ["end", "else", "elseif", "catch", "finally"];
+
+  var keywordsList = ["if", "else", "elseif", "while", "for", "begin", "let",
+        "end", "do", "try", "catch", "finally", "return", "break", "continue",
+        "global", "local", "const", "export", "import", "importall", "using",
+        "function", "where", "macro", "module", "baremodule", "struct", "type",
+        "mutable", "immutable", "quote", "typealias", "abstract", "primitive",
+        "bitstype"];
+
+  var builtinsList = ["true", "false", "nothing", "NaN", "Inf"];
+
+  CodeMirror.registerHelper("hintWords", "julia", keywordsList.concat(builtinsList));
+
+  var openers = wordRegexp(openersList);
+  var closers = wordRegexp(closersList);
+  var keywords = wordRegexp(keywordsList);
+  var builtins = wordRegexp(builtinsList);
 
-  //var stringPrefixes = new RegExp("^[br]?('|\")")
-  var stringPrefixes = /^(`|"{3}|([brv]?"))/;
-  var chars = wordRegexp(charsList, "'");
-  var keywords = wordRegexp(keywordList);
-  var builtins = wordRegexp(builtinList);
-  var openers = wordRegexp(blockOpeners);
-  var closers = wordRegexp(blockClosers);
   var macro = /^@[_A-Za-z][\w]*/;
   var symbol = /^:[_A-Za-z\u00A1-\uFFFF][\w\u00A1-\uFFFF]*!*/;
-  var typeAnnotation = /^::[^,;"{()=$\s]+({[^}]*}+)*/;
+  var stringPrefixes = /^(`|([_A-Za-z\u00A1-\uFFFF]*"("")?))/;
 
   function inArray(state) {
-    var ch = currentScope(state);
-    if (ch == '[') {
-      return true;
-    }
-    return false;
+    return (state.nestedArrays > 0);
   }
 
-  function currentScope(state) {
-    if (state.scopes.length == 0) {
+  function inGenerator(state) {
+    return (state.nestedGenerators > 0);
+  }
+
+  function currentScope(state, n) {
+    if (typeof(n) === "undefined") { n = 0; }
+    if (state.scopes.length <= n) {
       return null;
     }
-    return state.scopes[state.scopes.length - 1];
+    return state.scopes[state.scopes.length - (n + 1)];
   }
 
   // tokenizers
@@ -72,14 +91,17 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
       leavingExpr = false;
     }
     state.leavingExpr = false;
+
     if (leavingExpr) {
       if (stream.match(/^'+/)) {
-        return 'operator';
+        return "operator";
       }
     }
 
-    if (stream.match(/^\.{2,3}/)) {
-      return 'operator';
+    if (stream.match(/\.{4,}/)) {
+      return "error";
+    } else if (stream.match(/\.{1,3}/)) {
+      return "operator";
     }
 
     if (stream.eatSpace()) {
@@ -91,98 +113,97 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
     // Handle single line comments
     if (ch === '#') {
       stream.skipToEnd();
-      return 'comment';
+      return "comment";
     }
 
     if (ch === '[') {
       state.scopes.push('[');
+      state.nestedArrays++;
     }
 
     if (ch === '(') {
       state.scopes.push('(');
+      state.nestedGenerators++;
     }
 
-    var scope = currentScope(state);
-
-    if (scope == '[' && ch === ']') {
+    if (inArray(state) && ch === ']') {
+      if (currentScope(state) === "if") { state.scopes.pop(); }
+      while (currentScope(state) === "for") { state.scopes.pop(); }
       state.scopes.pop();
+      state.nestedArrays--;
       state.leavingExpr = true;
     }
 
-    if (scope == '(' && ch === ')') {
+    if (inGenerator(state) && ch === ')') {
+      if (currentScope(state) === "if") { state.scopes.pop(); }
+      while (currentScope(state) === "for") { state.scopes.pop(); }
       state.scopes.pop();
+      state.nestedGenerators--;
       state.leavingExpr = true;
     }
 
-    var match;
-    if (!inArray(state) && (match=stream.match(openers, false))) {
-      state.scopes.push(match);
-    }
-
-    if (!inArray(state) && stream.match(closers, false)) {
-      state.scopes.pop();
-    }
-
     if (inArray(state)) {
-      if (state.lastToken == 'end' && stream.match(/^:/)) {
-        return 'operator';
+      if (state.lastToken == "end" && stream.match(/^:/)) {
+        return "operator";
       }
       if (stream.match(/^end/)) {
-        return 'number';
+        return "number";
       }
     }
 
-    if (stream.match(/^=>/)) {
-      return 'operator';
+    var match;
+    if (match = stream.match(openers, false)) {
+      state.scopes.push(match[0]);
+    }
+
+    if (stream.match(closers, false)) {
+      state.scopes.pop();
+    }
+
+    // Handle type annotations
+    if (stream.match(/^::(?![:\$])/)) {
+      state.tokenize = tokenAnnotation;
+      return state.tokenize(stream, state);
+    }
+
+    // Handle symbols
+    if (!leavingExpr && stream.match(symbol) ||
+        stream.match(/:([<>]:|<<=?|>>>?=?|->|\/\/|\.{2,3}|[\.\\%*+\-<>!\/^|&]=?|[~\?\$])/)) {
+      return "builtin";
+    }
+
+    // Handle parametric types
+    //if (stream.match(/^{[^}]*}(?=\()/)) {
+    //  return "builtin";
+    //}
+
+    // Handle operators and Delimiters
+    if (stream.match(operators)) {
+      return "operator";
     }
 
     // Handle Number Literals
-    if (stream.match(/^[0-9\.]/, false)) {
+    if (stream.match(/^\.?\d/, false)) {
       var imMatcher = RegExp(/^im\b/);
       var numberLiteral = false;
       // Floats
-      if (stream.match(/^\d*\.(?!\.)\d*([Eef][\+\-]?\d+)?/i)) { numberLiteral = true; }
-      if (stream.match(/^\d+\.(?!\.)\d*/)) { numberLiteral = true; }
-      if (stream.match(/^\.\d+/)) { numberLiteral = true; }
-      if (stream.match(/^0x\.[0-9a-f]+p[\+\-]?\d+/i)) { numberLiteral = true; }
+      if (stream.match(/^(?:(?:\d[_\d]*)?\.(?!\.)(?:\d[_\d]*)?|\d[_\d]*\.(?!\.)(?:\d[_\d]*))?([Eef][\+\-]?[_\d]+)?/i)) { numberLiteral = true; }
+      if (stream.match(/^0x\.[0-9a-f_]+p[\+\-]?[_\d]+/i)) { numberLiteral = true; }
       // Integers
-      if (stream.match(/^0x[0-9a-f]+/i)) { numberLiteral = true; } // Hex
-      if (stream.match(/^0b[01]+/i)) { numberLiteral = true; } // Binary
-      if (stream.match(/^0o[0-7]+/i)) { numberLiteral = true; } // Octal
-      if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) { numberLiteral = true; } // Decimal
+      if (stream.match(/^0x[0-9a-f_]+/i)) { numberLiteral = true; } // Hex
+      if (stream.match(/^0b[01_]+/i)) { numberLiteral = true; } // Binary
+      if (stream.match(/^0o[0-7_]+/i)) { numberLiteral = true; } // Octal
+      if (stream.match(/^[1-9][_\d]*(e[\+\-]?\d+)?/)) { numberLiteral = true; } // Decimal
       // Zero by itself with no other piece of number.
       if (stream.match(/^0(?![\dx])/i)) { numberLiteral = true; }
       if (numberLiteral) {
           // Integer literals may be "long"
           stream.match(imMatcher);
           state.leavingExpr = true;
-          return 'number';
+          return "number";
       }
     }
 
-    if (stream.match(/^<:/)) {
-      return 'operator';
-    }
-
-    if (stream.match(typeAnnotation)) {
-      return 'builtin';
-    }
-
-    // Handle symbols
-    if (!leavingExpr && stream.match(symbol) || stream.match(/:\./)) {
-      return 'builtin';
-    }
-
-    // Handle parametric types
-    if (stream.match(/^{[^}]*}(?=\()/)) {
-      return 'builtin';
-    }
-
-    // Handle operators and Delimiters
-    if (stream.match(operators)) {
-      return 'operator';
-    }
-
     // Handle Chars
     if (stream.match(/^'/)) {
       state.tokenize = tokenChar;
@@ -196,7 +217,7 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
     }
 
     if (stream.match(macro)) {
-      return 'meta';
+      return "meta";
     }
 
     if (stream.match(delimiters)) {
@@ -204,41 +225,40 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
     }
 
     if (stream.match(keywords)) {
-      return 'keyword';
+      return "keyword";
     }
 
     if (stream.match(builtins)) {
-      return 'builtin';
+      return "builtin";
     }
 
-    var isDefinition = state.isDefinition ||
-                       state.lastToken == 'function' ||
-                       state.lastToken == 'macro' ||
-                       state.lastToken == 'type' ||
-                       state.lastToken == 'immutable';
+    var isDefinition = state.isDefinition || state.lastToken == "function" ||
+                       state.lastToken == "macro" || state.lastToken == "type" ||
+                       state.lastToken == "struct" || state.lastToken == "immutable";
 
     if (stream.match(identifiers)) {
       if (isDefinition) {
         if (stream.peek() === '.') {
           state.isDefinition = true;
-          return 'variable';
+          return "variable";
         }
         state.isDefinition = false;
-        return 'def';
+        return "def";
       }
       if (stream.match(/^({[^}]*})*\(/, false)) {
-        return callOrDef(stream, state);
+        state.tokenize = tokenCallOrDef;
+        return state.tokenize(stream, state);
       }
       state.leavingExpr = true;
-      return 'variable';
+      return "variable";
     }
 
     // Handle non-detected items
     stream.next();
-    return ERRORCLASS;
+    return "error";
   }
 
-  function callOrDef(stream, state) {
+  function tokenCallOrDef(stream, state) {
     var match = stream.match(/^(\(\s*)/);
     if (match) {
       if (state.firstParenPos < 0)
@@ -250,13 +270,14 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
       state.scopes.pop();
       state.charsAdvanced += 1;
       if (state.scopes.length <= state.firstParenPos) {
-        var isDefinition = stream.match(/^\s*?=(?!=)/, false);
+        var isDefinition = stream.match(/^(\s*where\s+[^\s=]+)*\s*?=(?!=)/, false);
         stream.backUp(state.charsAdvanced);
         state.firstParenPos = -1;
         state.charsAdvanced = 0;
+        state.tokenize = tokenBase;
         if (isDefinition)
-          return 'def';
-        return 'builtin';
+          return "def";
+        return "builtin";
       }
     }
     // Unfortunately javascript does not support multiline strings, so we have
@@ -268,25 +289,41 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
         state.scopes.pop();
       state.firstParenPos = -1;
       state.charsAdvanced = 0;
-      return 'builtin';
+      state.tokenize = tokenBase;
+      return "builtin";
     }
     state.charsAdvanced += stream.match(/^([^()]*)/)[1].length;
-    return callOrDef(stream, state);
+    return state.tokenize(stream, state);
+  }
+
+  function tokenAnnotation(stream, state) {
+    stream.match(/.*?(?=,|;|{|}|\(|\)|=|$|\s)/);
+    if (stream.match(/^{/)) {
+      state.nestedParameters++;
+    } else if (stream.match(/^}/) && state.nestedParameters > 0) {
+      state.nestedParameters--;
+    }
+    if (state.nestedParameters > 0) {
+      stream.match(/.*?(?={|})/) || stream.next();
+    } else if (state.nestedParameters == 0) {
+      state.tokenize = tokenBase;
+    }
+    return "builtin";
   }
 
   function tokenComment(stream, state) {
     if (stream.match(/^#=/)) {
-      state.weakScopes++;
+      state.nestedComments++;
     }
     if (!stream.match(/.*?(?=(#=|=#))/)) {
       stream.skipToEnd();
     }
     if (stream.match(/^=#/)) {
-      state.weakScopes--;
-      if (state.weakScopes == 0)
+      state.nestedComments--;
+      if (state.nestedComments == 0)
         state.tokenize = tokenBase;
     }
-    return 'comment';
+    return "comment";
   }
 
   function tokenChar(stream, state) {
@@ -309,35 +346,32 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
     if (isChar) {
       state.leavingExpr = true;
       state.tokenize = tokenBase;
-      return 'string';
+      return "string";
     }
     if (!stream.match(/^[^']+(?=')/)) { stream.skipToEnd(); }
     if (stream.match(/^'/)) { state.tokenize = tokenBase; }
-    return ERRORCLASS;
+    return "error";
   }
 
   function tokenStringFactory(delimiter) {
-    while ('bruv'.indexOf(delimiter.charAt(0).toLowerCase()) >= 0) {
-      delimiter = delimiter.substr(1);
+    if (delimiter.substr(-3) === '"""') {
+      delimiter = '"""';
+    } else if (delimiter.substr(-1) === '"') {
+      delimiter = '"';
     }
-    var OUTCLASS = 'string';
-
     function tokenString(stream, state) {
-      while (!stream.eol()) {
-        stream.eatWhile(/[^"\\]/);
-        if (stream.eat('\\')) {
-            stream.next();
-        } else if (stream.match(delimiter)) {
-            state.tokenize = tokenBase;
-            state.leavingExpr = true;
-            return OUTCLASS;
-        } else {
-            stream.eat(/["]/);
-        }
+      if (stream.eat('\\')) {
+        stream.next();
+      } else if (stream.match(delimiter)) {
+        state.tokenize = tokenBase;
+        state.leavingExpr = true;
+        return "string";
+      } else {
+        stream.eat(/[`"]/);
       }
-      return OUTCLASS;
+      stream.eatWhile(/[^\\`"]/);
+      return "string";
     }
-    tokenString.isString = true;
     return tokenString;
   }
 
@@ -346,10 +380,13 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
       return {
         tokenize: tokenBase,
         scopes: [],
-        weakScopes: 0,
         lastToken: null,
         leavingExpr: false,
         isDefinition: false,
+        nestedArrays: 0,
+        nestedComments: 0,
+        nestedGenerators: 0,
+        nestedParameters: 0,
         charsAdvanced: 0,
         firstParenPos: -1
       };
@@ -363,24 +400,24 @@ CodeMirror.defineMode("julia", function(_conf, parserConf) {
         state.lastToken = current;
       }
 
-      // Handle '.' connected identifiers
-      if (current === '.') {
-        style = stream.match(identifiers, false) || stream.match(macro, false) ||
-                stream.match(/\(/, false) ? 'operator' : ERRORCLASS;
-      }
       return style;
     },
 
     indent: function(state, textAfter) {
       var delta = 0;
-      if (textAfter == "]" || textAfter == ")" || textAfter == "end" || textAfter == "else" || textAfter == "elseif" || textAfter == "catch" || textAfter == "finally") {
+      if ( textAfter === ']' || textAfter === ')' || textAfter === "end" ||
+           textAfter === "else" || textAfter === "catch" || textAfter === "elseif" ||
+           textAfter === "finally" ) {
         delta = -1;
       }
-      return (state.scopes.length + delta) * _conf.indentUnit;
+      return (state.scopes.length + delta) * config.indentUnit;
     },
 
-    electricInput: /(end|else(if)?|catch|finally)$/,
+    electricInput: /\b(end|else|catch|finally)\b/,
+    blockCommentStart: "#=",
+    blockCommentEnd: "=#",
     lineComment: "#",
+    closeBrackets: "()[]{}\"\"",
     fold: "indent"
   };
   return external;
diff --git a/public/vendor/plugins/codemirror/mode/livescript/index.html b/public/vendor/plugins/codemirror/mode/livescript/index.html
index f415479876..297b1c7bc2 100644
--- a/public/vendor/plugins/codemirror/mode/livescript/index.html
+++ b/public/vendor/plugins/codemirror/mode/livescript/index.html
@@ -10,7 +10,7 @@
 <script src="livescript.js"></script>
 <style>.CodeMirror {font-size: 80%;border-top: 1px solid silver; border-bottom: 1px solid silver;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/livescript/livescript.js b/public/vendor/plugins/codemirror/mode/livescript/livescript.js
index 4b26e04619..595e067d16 100644
--- a/public/vendor/plugins/codemirror/mode/livescript/livescript.js
+++ b/public/vendor/plugins/codemirror/mode/livescript/livescript.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 /**
  * Link to the project's GitHub page:
@@ -50,7 +50,7 @@
       startState: function(){
         return {
           next: 'start',
-          lastToken: null
+          lastToken: {style: null, indent: 0, content: ""}
         };
       },
       token: function(stream, state){
diff --git a/public/vendor/plugins/codemirror/mode/lua/index.html b/public/vendor/plugins/codemirror/mode/lua/index.html
index fc98b94468..59f4df3e32 100644
--- a/public/vendor/plugins/codemirror/mode/lua/index.html
+++ b/public/vendor/plugins/codemirror/mode/lua/index.html
@@ -11,7 +11,7 @@
 <script src="lua.js"></script>
 <style>.CodeMirror {border: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -75,7 +75,7 @@ end
     </script>
 
     <p>Loosely based on Franciszek
-    Wawrzak's <a href="http://codemirror.net/1/contrib/lua">CodeMirror
+    Wawrzak's <a href="https://codemirror.net/1/contrib/lua">CodeMirror
     1 mode</a>. One configuration parameter is
     supported, <code>specials</code>, to which you can provide an
     array of strings to have those identifiers highlighted with
diff --git a/public/vendor/plugins/codemirror/mode/lua/lua.js b/public/vendor/plugins/codemirror/mode/lua/lua.js
index 0b19abd304..202f373585 100644
--- a/public/vendor/plugins/codemirror/mode/lua/lua.js
+++ b/public/vendor/plugins/codemirror/mode/lua/lua.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 // LUA mode. Ported to CodeMirror 2 from Franciszek Wawrzak's
 // CodeMirror 1 mode.
diff --git a/public/vendor/plugins/codemirror/mode/markdown/index.html b/public/vendor/plugins/codemirror/mode/markdown/index.html
index 15660c2618..37203ef398 100644
--- a/public/vendor/plugins/codemirror/mode/markdown/index.html
+++ b/public/vendor/plugins/codemirror/mode/markdown/index.html
@@ -9,14 +9,14 @@
 <script src="../../addon/edit/continuelist.js"></script>
 <script src="../xml/xml.js"></script>
 <script src="markdown.js"></script>
-<style type="text/css">
+<style>
       .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
       .cm-s-default .cm-trailing-space-a:before,
       .cm-s-default .cm-trailing-space-b:before {position: absolute; content: "\00B7"; color: #777;}
       .cm-s-default .cm-trailing-space-new-line:before {position: absolute; content: "\21B5"; color: #777;}
     </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -87,7 +87,7 @@ Markdown:
 
     A First Level Header
     ====================
-    
+
     A Second Level Header
     ---------------------
 
@@ -97,11 +97,11 @@ Markdown:
 
     The quick brown fox jumped over the lazy
     dog's back.
-    
+
     ### Header 3
 
     &gt; This is a blockquote.
-    &gt; 
+    &gt;
     &gt; This is the second paragraph in the blockquote.
     &gt;
     &gt; ## This is an H2 in a blockquote
@@ -110,23 +110,23 @@ Markdown:
 Output:
 
     &lt;h1&gt;A First Level Header&lt;/h1&gt;
-    
+
     &lt;h2&gt;A Second Level Header&lt;/h2&gt;
-    
+
     &lt;p&gt;Now is the time for all good men to come to
     the aid of their country. This is just a
     regular paragraph.&lt;/p&gt;
-    
+
     &lt;p&gt;The quick brown fox jumped over the lazy
     dog's back.&lt;/p&gt;
-    
+
     &lt;h3&gt;Header 3&lt;/h3&gt;
-    
+
     &lt;blockquote&gt;
         &lt;p&gt;This is a blockquote.&lt;/p&gt;
-        
+
         &lt;p&gt;This is the second paragraph in the blockquote.&lt;/p&gt;
-        
+
         &lt;h2&gt;This is an H2 in a blockquote&lt;/h2&gt;
     &lt;/blockquote&gt;
 
@@ -140,7 +140,7 @@ Markdown:
 
     Some of these words *are emphasized*.
     Some of these words _are emphasized also_.
-    
+
     Use two asterisks for **strong emphasis**.
     Or, if you prefer, __use two underscores instead__.
 
@@ -148,10 +148,10 @@ Output:
 
     &lt;p&gt;Some of these words &lt;em&gt;are emphasized&lt;/em&gt;.
     Some of these words &lt;em&gt;are emphasized also&lt;/em&gt;.&lt;/p&gt;
-    
+
     &lt;p&gt;Use two asterisks for &lt;strong&gt;strong emphasis&lt;/strong&gt;.
     Or, if you prefer, &lt;strong&gt;use two underscores instead&lt;/strong&gt;.&lt;/p&gt;
-   
+
 
 
 ## Lists ##
@@ -204,7 +204,7 @@ list item text. You can create multi-paragraph list items by indenting
 the paragraphs by 4 spaces or 1 tab:
 
     *   A list item.
-    
+
         With multiple paragraphs.
 
     *   Another item in the list.
@@ -216,7 +216,7 @@ Output:
     &lt;p&gt;With multiple paragraphs.&lt;/p&gt;&lt;/li&gt;
     &lt;li&gt;&lt;p&gt;Another item in the list.&lt;/p&gt;&lt;/li&gt;
     &lt;/ul&gt;
-    
+
 
 
 ### Links ###
@@ -311,7 +311,7 @@ Output:
 
     &lt;p&gt;I strongly recommend against using any
     &lt;code&gt;&amp;lt;blink&amp;gt;&lt;/code&gt; tags.&lt;/p&gt;
-    
+
     &lt;p&gt;I wish SmartyPants used named entities like
     &lt;code&gt;&amp;amp;mdash;&lt;/code&gt; instead of decimal-encoded
     entites like &lt;code&gt;&amp;amp;#8212;&lt;/code&gt;.&lt;/p&gt;
@@ -334,11 +334,20 @@ Output:
 
     &lt;p&gt;If you want your page to validate under XHTML 1.0 Strict,
     you've got to put paragraph tags in your blockquotes:&lt;/p&gt;
-    
+
     &lt;pre&gt;&lt;code&gt;&amp;lt;blockquote&amp;gt;
         &amp;lt;p&amp;gt;For example.&amp;lt;/p&amp;gt;
     &amp;lt;/blockquote&amp;gt;
     &lt;/code&gt;&lt;/pre&gt;
+
+## Fenced code blocks (and syntax highlighting)
+
+```javascript
+for (var i = 0; i < items.length; i++) {
+    console.log(items[i], i); // log them
+}
+```
+
 </textarea></form>
 
     <script>
@@ -350,10 +359,51 @@ Output:
       });
     </script>
 
-    <p>You might want to use the <a href="../gfm/index.html">Github-Flavored Markdown mode</a> instead, which adds support for fenced code blocks and a few other things.</p>
+    <p>If you also want support <code>strikethrough</code>, <code>emoji</code> and few other goodies, check out <a href="../gfm/index.html">Github-Flavored Markdown mode</a>.</p>
+
+    <p>Optionally depends on other modes for properly highlighted code blocks,
+      and XML mode for properly highlighted inline XML blocks.</p>
+
+    <p>Markdown mode supports these options:</p>
+    <ul>
+      <li>
+        <d1>
+          <dt><code>highlightFormatting: boolean</code></dt>
+          <dd>Whether to separately highlight markdown meta characterts (<code>*[]()</code>etc.) (default: <code>false</code>).</dd>
+        </d1>
+      </li>
+      <li>
+        <d1>
+          <dt><code>maxBlockquoteDepth: boolean</code></dt>
+          <dd>Maximum allowed blockquote nesting (default: <code>0</code> - infinite nesting).</dd>
+        </d1>
+      </li>
+      <li>
+        <d1>
+          <dt><code>xml: boolean</code></dt>
+          <dd>Whether to highlight inline XML (default: <code>true</code>).</dd>
+        </d1>
+      </li>
+      <li>
+        <d1>
+          <dt><code>fencedCodeBlockHighlighting: boolean</code></dt>
+          <dd>Whether to syntax-highlight fenced code blocks, if given mode is included (default: <code>true</code>).</dd>
+        </d1>
+      </li>
+      <li>
+        <d1>
+          <dt><code>tokenTypeOverrides: Object</code></dt>
+          <dd>When you want ot override default token type names (e.g. <code>{code: "code"}</code>).</dd>
+        </d1>
+      </li>
+      <li>
+        <d1>
+          <dt><code>allowAtxHeaderWithoutSpace: boolean</code></dt>
+          <dd>Allow lazy headers without whitespace between hashtag and text (default: <code>false</code>).</dd>
+        </d1>
+      </li>
+    </ul>
 
-    <p>Optionally depends on the XML mode for properly highlighted inline XML blocks.</p>
-    
     <p><strong>MIME types defined:</strong> <code>text/x-markdown</code>.</p>
 
     <p><strong>Parsing/Highlighting Tests:</strong> <a href="../../test/index.html#markdown_*">normal</a>,  <a href="../../test/index.html#verbose,markdown_*">verbose</a>.</p>
diff --git a/public/vendor/plugins/codemirror/mode/markdown/markdown.js b/public/vendor/plugins/codemirror/mode/markdown/markdown.js
index 9dd44574fc..7aa3a3e17e 100644
--- a/public/vendor/plugins/codemirror/mode/markdown/markdown.js
+++ b/public/vendor/plugins/codemirror/mode/markdown/markdown.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -35,15 +35,6 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
   if (modeCfg.maxBlockquoteDepth === undefined)
     modeCfg.maxBlockquoteDepth = 0;
 
-  // Should underscores in words open/close em/strong?
-  if (modeCfg.underscoresBreakWords === undefined)
-    modeCfg.underscoresBreakWords = true;
-
-  // Use `fencedCodeBlocks` to configure fenced code blocks. false to
-  // disable, string to specify a precise regexp that the fence should
-  // match, and true to allow three or more backticks or tildes (as
-  // per CommonMark).
-
   // Turn on task lists? ("- [ ] " and "- [x] ")
   if (modeCfg.taskLists === undefined) modeCfg.taskLists = false;
 
@@ -51,6 +42,15 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
   if (modeCfg.strikethrough === undefined)
     modeCfg.strikethrough = false;
 
+  if (modeCfg.emoji === undefined)
+    modeCfg.emoji = false;
+
+  if (modeCfg.fencedCodeBlockHighlighting === undefined)
+    modeCfg.fencedCodeBlockHighlighting = true;
+
+  if (modeCfg.xml === undefined)
+    modeCfg.xml = true;
+
   // Allow token types to be overridden by user-provided token types.
   if (modeCfg.tokenTypeOverrides === undefined)
     modeCfg.tokenTypeOverrides = {};
@@ -73,7 +73,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
     linkHref: "string",
     em: "em",
     strong: "strong",
-    strikethrough: "strikethrough"
+    strikethrough: "strikethrough",
+    emoji: "builtin"
   };
 
   for (var tokenType in tokenTypes) {
@@ -83,14 +84,15 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
   }
 
   var hrRE = /^([*\-_])(?:\s*\1){2,}\s*$/
-  ,   ulRE = /^[*\-+]\s+/
-  ,   olRE = /^[0-9]+([.)])\s+/
-  ,   taskListRE = /^\[(x| )\](?=\s)/ // Must follow ulRE or olRE
+  ,   listRE = /^(?:[*\-+]|^[0-9]+([.)]))\s+/
+  ,   taskListRE = /^\[(x| )\](?=\s)/i // Must follow listRE
   ,   atxHeaderRE = modeCfg.allowAtxHeaderWithoutSpace ? /^(#+)/ : /^(#+)(?: |$)/
   ,   setextHeaderRE = /^ *(?:\={1,}|-{1,})\s*$/
-  ,   textRE = /^[^#!\[\]*_\\<>` "'(~]+/
-  ,   fencedCodeRE = new RegExp("^(" + (modeCfg.fencedCodeBlocks === true ? "~~~+|```+" : modeCfg.fencedCodeBlocks) +
-                                ")[ \\t]*([\\w+#\-]*)");
+  ,   textRE = /^[^#!\[\]*_\\<>` "'(~:]+/
+  ,   fencedCodeRE = /^(~~~+|```+)[ \t]*([\w+#-]*)[^\n`]*$/
+  ,   linkDefRE = /^\s*\[[^\]]+?\]:.*$/ // naive link-definition
+  ,   punctuation = /[!"#$%&'()*+,\-.\/:;<=>?@\[\\\]^_`{|}~\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u0AF0\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E42\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC9\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9]|\uD805[\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDF3C-\uDF3E]|\uD809[\uDC70-\uDC74]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]/
+  ,   expandedTab = "    " // CommonMark specifies tab as 4 spaces
 
   function switchInline(stream, state, f) {
     state.f = state.inline = f;
@@ -111,6 +113,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
   function blankLine(state) {
     // Reset linkTitle state
     state.linkTitle = false;
+    state.linkHref = false;
+    state.linkText = false;
     // Reset EM state
     state.em = false;
     // Reset STRONG state
@@ -121,91 +125,104 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
     state.quote = 0;
     // Reset state.indentedCode
     state.indentedCode = false;
-    if (htmlModeMissing && state.f == htmlBlock) {
-      state.f = inlineNormal;
-      state.block = blockNormal;
+    if (state.f == htmlBlock) {
+      var exit = htmlModeMissing
+      if (!exit) {
+        var inner = CodeMirror.innerMode(htmlMode, state.htmlState)
+        exit = inner.mode.name == "xml" && inner.state.tagStart === null &&
+          (!inner.state.context && inner.state.tokenize.isInText)
+      }
+      if (exit) {
+        state.f = inlineNormal;
+        state.block = blockNormal;
+        state.htmlState = null;
+      }
     }
     // Reset state.trailingSpace
     state.trailingSpace = 0;
     state.trailingSpaceNewLine = false;
     // Mark this line as blank
     state.prevLine = state.thisLine
-    state.thisLine = null
+    state.thisLine = {stream: null}
     return null;
   }
 
   function blockNormal(stream, state) {
-
-    var sol = stream.sol();
-
-    var prevLineIsList = state.list !== false,
-        prevLineIsIndentedCode = state.indentedCode;
+    var firstTokenOnLine = stream.column() === state.indentation;
+    var prevLineLineIsEmpty = lineIsEmpty(state.prevLine.stream);
+    var prevLineIsIndentedCode = state.indentedCode;
+    var prevLineIsHr = state.prevLine.hr;
+    var prevLineIsList = state.list !== false;
+    var maxNonCodeIndentation = (state.listStack[state.listStack.length - 1] || 0) + 3;
 
     state.indentedCode = false;
 
-    if (prevLineIsList) {
-      if (state.indentationDiff >= 0) { // Continued list
-        if (state.indentationDiff < 4) { // Only adjust indentation if *not* a code block
-          state.indentation -= state.indentationDiff;
+    var lineIndentation = state.indentation;
+    // compute once per line (on first token)
+    if (state.indentationDiff === null) {
+      state.indentationDiff = state.indentation;
+      if (prevLineIsList) {
+        // Reset inline styles which shouldn't propagate aross list items
+        state.em = false;
+        state.strong = false;
+        state.code = false;
+        state.strikethrough = false;
+
+        state.list = null;
+        // While this list item's marker's indentation is less than the deepest
+        //  list item's content's indentation,pop the deepest list item
+        //  indentation off the stack, and update block indentation state
+        while (lineIndentation < state.listStack[state.listStack.length - 1]) {
+          state.listStack.pop();
+          if (state.listStack.length) {
+            state.indentation = state.listStack[state.listStack.length - 1];
+          // less than the first list's indent -> the line is no longer a list
+          } else {
+            state.list = false;
+          }
+        }
+        if (state.list !== false) {
+          state.indentationDiff = lineIndentation - state.listStack[state.listStack.length - 1]
         }
-        state.list = null;
-      } else if (state.indentation > 0) {
-        state.list = null;
-      } else { // No longer a list
-        state.list = false;
       }
     }
 
+    // not comprehensive (currently only for setext detection purposes)
+    var allowsInlineContinuation = (
+        !prevLineLineIsEmpty && !prevLineIsHr && !state.prevLine.header &&
+        (!prevLineIsList || !prevLineIsIndentedCode) &&
+        !state.prevLine.fencedCodeEnd
+    );
+
+    var isHr = (state.list === false || prevLineIsHr || prevLineLineIsEmpty) &&
+      state.indentation <= maxNonCodeIndentation && stream.match(hrRE);
+
     var match = null;
-    if (state.indentationDiff >= 4) {
+    if (state.indentationDiff >= 4 && (prevLineIsIndentedCode || state.prevLine.fencedCodeEnd ||
+         state.prevLine.header || prevLineLineIsEmpty)) {
       stream.skipToEnd();
-      if (prevLineIsIndentedCode || lineIsEmpty(state.prevLine)) {
-        state.indentation -= 4;
-        state.indentedCode = true;
-        return tokenTypes.code;
-      } else {
-        return null;
-      }
+      state.indentedCode = true;
+      return tokenTypes.code;
     } else if (stream.eatSpace()) {
       return null;
-    } else if ((match = stream.match(atxHeaderRE)) && match[1].length <= 6) {
+    } else if (firstTokenOnLine && state.indentation <= maxNonCodeIndentation && (match = stream.match(atxHeaderRE)) && match[1].length <= 6) {
+      state.quote = 0;
       state.header = match[1].length;
+      state.thisLine.header = true;
       if (modeCfg.highlightFormatting) state.formatting = "header";
       state.f = state.inline;
       return getType(state);
-    } else if (!lineIsEmpty(state.prevLine) && !state.quote && !prevLineIsList &&
-               !prevLineIsIndentedCode && (match = stream.match(setextHeaderRE))) {
-      state.header = match[0].charAt(0) == '=' ? 1 : 2;
-      if (modeCfg.highlightFormatting) state.formatting = "header";
-      state.f = state.inline;
-      return getType(state);
-    } else if (stream.eat('>')) {
-      state.quote = sol ? 1 : state.quote + 1;
+    } else if (state.indentation <= maxNonCodeIndentation && stream.eat('>')) {
+      state.quote = firstTokenOnLine ? 1 : state.quote + 1;
       if (modeCfg.highlightFormatting) state.formatting = "quote";
       stream.eatSpace();
       return getType(state);
-    } else if (stream.peek() === '[') {
-      return switchInline(stream, state, footnoteLink);
-    } else if (stream.match(hrRE, true)) {
-      state.hr = true;
-      return tokenTypes.hr;
-    } else if ((lineIsEmpty(state.prevLine) || prevLineIsList) && (stream.match(ulRE, false) || stream.match(olRE, false))) {
-      var listType = null;
-      if (stream.match(ulRE, true)) {
-        listType = 'ul';
-      } else {
-        stream.match(olRE, true);
-        listType = 'ol';
-      }
-      state.indentation = stream.column() + stream.current().length;
-      state.list = true;
+    } else if (!isHr && !state.setext && firstTokenOnLine && state.indentation <= maxNonCodeIndentation && (match = stream.match(listRE))) {
+      var listType = match[1] ? "ol" : "ul";
 
-      // While this list item's marker's indentation
-      // is less than the deepest list item's content's indentation,
-      // pop the deepest list item indentation off the stack.
-      while (state.listStack && stream.column() < state.listStack[state.listStack.length - 1]) {
-        state.listStack.pop();
-      }
+      state.indentation = lineIndentation + stream.current().length;
+      state.list = true;
+      state.quote = 0;
 
       // Add this list item's content's indentation to the stack
       state.listStack.push(state.indentation);
@@ -216,15 +233,47 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
       state.f = state.inline;
       if (modeCfg.highlightFormatting) state.formatting = ["list", "list-" + listType];
       return getType(state);
-    } else if (modeCfg.fencedCodeBlocks && (match = stream.match(fencedCodeRE, true))) {
-      state.fencedChars = match[1]
+    } else if (firstTokenOnLine && state.indentation <= maxNonCodeIndentation && (match = stream.match(fencedCodeRE, true))) {
+      state.quote = 0;
+      state.fencedEndRE = new RegExp(match[1] + "+ *$");
       // try switching mode
-      state.localMode = getMode(match[2]);
+      state.localMode = modeCfg.fencedCodeBlockHighlighting && getMode(match[2]);
       if (state.localMode) state.localState = CodeMirror.startState(state.localMode);
       state.f = state.block = local;
       if (modeCfg.highlightFormatting) state.formatting = "code-block";
       state.code = -1
       return getType(state);
+    // SETEXT has lowest block-scope precedence after HR, so check it after
+    //  the others (code, blockquote, list...)
+    } else if (
+      // if setext set, indicates line after ---/===
+      state.setext || (
+        // line before ---/===
+        (!allowsInlineContinuation || !prevLineIsList) && !state.quote && state.list === false &&
+        !state.code && !isHr && !linkDefRE.test(stream.string) &&
+        (match = stream.lookAhead(1)) && (match = match.match(setextHeaderRE))
+      )
+    ) {
+      if ( !state.setext ) {
+        state.header = match[0].charAt(0) == '=' ? 1 : 2;
+        state.setext = state.header;
+      } else {
+        state.header = state.setext;
+        // has no effect on type so we can reset it now
+        state.setext = 0;
+        stream.skipToEnd();
+        if (modeCfg.highlightFormatting) state.formatting = "header";
+      }
+      state.thisLine.header = true;
+      state.f = state.inline;
+      return getType(state);
+    } else if (isHr) {
+      stream.skipToEnd();
+      state.hr = true;
+      state.thisLine.hr = true;
+      return tokenTypes.hr;
+    } else if (stream.peek() === '[') {
+      return switchInline(stream, state, footnoteLink);
     }
 
     return switchInline(stream, state, state.inline);
@@ -246,10 +295,21 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
   }
 
   function local(stream, state) {
-    if (state.fencedChars && stream.match(state.fencedChars, false)) {
+    var currListInd = state.listStack[state.listStack.length - 1] || 0;
+    var hasExitedList = state.indentation < currListInd;
+    var maxFencedEndInd = currListInd + 3;
+    if (state.fencedEndRE && state.indentation <= maxFencedEndInd && (hasExitedList || stream.match(state.fencedEndRE))) {
+      if (modeCfg.highlightFormatting) state.formatting = "code-block";
+      var returnType;
+      if (!hasExitedList) returnType = getType(state)
       state.localMode = state.localState = null;
-      state.f = state.block = leavingLocal;
-      return null;
+      state.block = blockNormal;
+      state.f = inlineNormal;
+      state.fencedEndRE = null;
+      state.code = 0
+      state.thisLine.fencedCodeEnd = true;
+      if (hasExitedList) return switchBlock(stream, state, state.block);
+      return returnType;
     } else if (state.localMode) {
       return state.localMode.token(stream, state.localState);
     } else {
@@ -258,18 +318,6 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
     }
   }
 
-  function leavingLocal(stream, state) {
-    stream.match(state.fencedChars);
-    state.block = blockNormal;
-    state.f = inlineNormal;
-    state.fencedChars = null;
-    if (modeCfg.highlightFormatting) state.formatting = "code-block";
-    state.code = 1
-    var returnType = getType(state);
-    state.code = 0
-    return returnType;
-  }
-
   // Inline
   function getType(state) {
     var styles = [];
@@ -313,6 +361,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
       if (state.strong) { styles.push(tokenTypes.strong); }
       if (state.em) { styles.push(tokenTypes.em); }
       if (state.strikethrough) { styles.push(tokenTypes.strikethrough); }
+      if (state.emoji) { styles.push(tokenTypes.emoji); }
       if (state.linkText) { styles.push(tokenTypes.linkText); }
       if (state.code) { styles.push(tokenTypes.code); }
       if (state.image) { styles.push(tokenTypes.image); }
@@ -371,7 +420,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
     }
 
     if (state.taskList) {
-      var taskOpen = stream.match(taskListRE, true)[1] !== "x";
+      var taskOpen = stream.match(taskListRE, true)[1] === " ";
       if (taskOpen) state.taskOpen = true;
       else state.taskClosed = true;
       if (modeCfg.highlightFormatting) state.formatting = "task";
@@ -387,9 +436,6 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
       return getType(state);
     }
 
-    // Get sol() value now, before character is consumed
-    var sol = stream.sol();
-
     var ch = stream.next();
 
     // Matches link titles present on next line
@@ -399,7 +445,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
       if (ch === '(') {
         matchCh = ')';
       }
-      matchCh = (matchCh+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
+      matchCh = (matchCh+'').replace(/([.?*+^\[\]\\(){}|-])/g, "\\$1");
       var regex = '^\\s*(?:[^' + matchCh + '\\\\]+|\\\\\\\\|\\\\.)' + matchCh;
       if (stream.match(new RegExp(regex), true)) {
         return tokenTypes.linkHref;
@@ -412,7 +458,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
       if (modeCfg.highlightFormatting) state.formatting = "code";
       stream.eatWhile('`');
       var count = stream.current().length
-      if (state.code == 0) {
+      if (state.code == 0 && (!state.quote || count == 1)) {
         state.code = count
         return getType(state)
       } else if (count == state.code) { // Must be exact
@@ -443,7 +489,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
       return getType(state);
     }
 
-    if (ch === '[' && state.imageMarker) {
+    if (ch === '[' && state.imageMarker && stream.match(/[^\]]*\](\(.*?\)| ?\[.*?\])/, false)) {
       state.imageMarker = false;
       state.imageAltText = true
       if (modeCfg.highlightFormatting) state.formatting = "image";
@@ -459,17 +505,18 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
       return type;
     }
 
-    if (ch === '[' && stream.match(/[^\]]*\](\(.*\)| ?\[.*?\])/, false) && !state.image) {
+    if (ch === '[' && !state.image) {
+      if (state.linkText && stream.match(/^.*?\]/)) return getType(state)
       state.linkText = true;
       if (modeCfg.highlightFormatting) state.formatting = "link";
       return getType(state);
     }
 
-    if (ch === ']' && state.linkText && stream.match(/\(.*?\)| ?\[.*?\]/, false)) {
+    if (ch === ']' && state.linkText) {
       if (modeCfg.highlightFormatting) state.formatting = "link";
       var type = getType(state);
       state.linkText = false;
-      state.inline = state.f = linkHref;
+      state.inline = state.f = stream.match(/\(.*?\)| ?\[.*?\]/, false) ? linkHref : inlineNormal
       return type;
     }
 
@@ -497,7 +544,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
       return type + tokenTypes.linkEmail;
     }
 
-    if (ch === '<' && stream.match(/^(!--|\w)/, false)) {
+    if (modeCfg.xml && ch === '<' && stream.match(/^(!--|\?|!\[CDATA\[|[a-z][a-z0-9-]*(?:\s+[a-z_:.\-]+(?:\s*=\s*[^>]+)?)*\s*(?:>|$))/i, false)) {
       var end = stream.string.indexOf(">", stream.pos);
       if (end != -1) {
         var atts = stream.string.substring(stream.start, end);
@@ -508,44 +555,37 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
       return switchBlock(stream, state, htmlBlock);
     }
 
-    if (ch === '<' && stream.match(/^\/\w*?>/)) {
+    if (modeCfg.xml && ch === '<' && stream.match(/^\/\w*?>/)) {
       state.md_inside = false;
       return "tag";
-    }
-
-    var ignoreUnderscore = false;
-    if (!modeCfg.underscoresBreakWords) {
-      if (ch === '_' && stream.peek() !== '_' && stream.match(/(\w)/, false)) {
-        var prevPos = stream.pos - 2;
-        if (prevPos >= 0) {
-          var prevCh = stream.string.charAt(prevPos);
-          if (prevCh !== '_' && prevCh.match(/(\w)/, false)) {
-            ignoreUnderscore = true;
-          }
-        }
+    } else if (ch === "*" || ch === "_") {
+      var len = 1, before = stream.pos == 1 ? " " : stream.string.charAt(stream.pos - 2)
+      while (len < 3 && stream.eat(ch)) len++
+      var after = stream.peek() || " "
+      // See http://spec.commonmark.org/0.27/#emphasis-and-strong-emphasis
+      var leftFlanking = !/\s/.test(after) && (!punctuation.test(after) || /\s/.test(before) || punctuation.test(before))
+      var rightFlanking = !/\s/.test(before) && (!punctuation.test(before) || /\s/.test(after) || punctuation.test(after))
+      var setEm = null, setStrong = null
+      if (len % 2) { // Em
+        if (!state.em && leftFlanking && (ch === "*" || !rightFlanking || punctuation.test(before)))
+          setEm = true
+        else if (state.em == ch && rightFlanking && (ch === "*" || !leftFlanking || punctuation.test(after)))
+          setEm = false
       }
-    }
-    if (ch === '*' || (ch === '_' && !ignoreUnderscore)) {
-      if (sol && stream.peek() === ' ') {
-        // Do nothing, surrounded by newline and space
-      } else if (state.strong === ch && stream.eat(ch)) { // Remove STRONG
-        if (modeCfg.highlightFormatting) state.formatting = "strong";
-        var t = getType(state);
-        state.strong = false;
-        return t;
-      } else if (!state.strong && stream.eat(ch)) { // Add STRONG
-        state.strong = ch;
-        if (modeCfg.highlightFormatting) state.formatting = "strong";
-        return getType(state);
-      } else if (state.em === ch) { // Remove EM
-        if (modeCfg.highlightFormatting) state.formatting = "em";
-        var t = getType(state);
-        state.em = false;
-        return t;
-      } else if (!state.em) { // Add EM
-        state.em = ch;
-        if (modeCfg.highlightFormatting) state.formatting = "em";
-        return getType(state);
+      if (len > 1) { // Strong
+        if (!state.strong && leftFlanking && (ch === "*" || !rightFlanking || punctuation.test(before)))
+          setStrong = true
+        else if (state.strong == ch && rightFlanking && (ch === "*" || !leftFlanking || punctuation.test(after)))
+          setStrong = false
+      }
+      if (setStrong != null || setEm != null) {
+        if (modeCfg.highlightFormatting) state.formatting = setEm == null ? "strong" : setStrong == null ? "em" : "strong em"
+        if (setEm === true) state.em = ch
+        if (setStrong === true) state.strong = ch
+        var t = getType(state)
+        if (setEm === false) state.em = false
+        if (setStrong === false) state.strong = false
+        return t
       }
     } else if (ch === ' ') {
       if (stream.eat('*') || stream.eat('_')) { // Probably surrounded by spaces
@@ -580,8 +620,16 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
       }
     }
 
+    if (modeCfg.emoji && ch === ":" && stream.match(/^(?:[a-z_\d+][a-z_\d+-]*|\-[a-z_\d+][a-z_\d+-]*):/)) {
+      state.emoji = true;
+      if (modeCfg.highlightFormatting) state.formatting = "emoji";
+      var retType = getType(state);
+      state.emoji = false;
+      return retType;
+    }
+
     if (ch === ' ') {
-      if (stream.match(/ +$/, false)) {
+      if (stream.match(/^ +$/, false)) {
         state.trailingSpace++;
       } else if (state.trailingSpace) {
         state.trailingSpaceNewLine = true;
@@ -618,7 +666,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
     }
     var ch = stream.next();
     if (ch === '(' || ch === '[') {
-      state.f = state.inline = getLinkHrefInside(ch === "(" ? ")" : "]", 0);
+      state.f = state.inline = getLinkHrefInside(ch === "(" ? ")" : "]");
       if (modeCfg.highlightFormatting) state.formatting = "link-string";
       state.linkHref = true;
       return getType(state);
@@ -628,7 +676,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
 
   var linkRE = {
     ")": /^(?:[^\\\(\)]|\\.|\((?:[^\\\(\)]|\\.)*\))*?(?=\))/,
-    "]": /^(?:[^\\\[\]]|\\.|\[(?:[^\\\[\\]]|\\.)*\])*?(?=\])/
+    "]": /^(?:[^\\\[\]]|\\.|\[(?:[^\\\[\]]|\\.)*\])*?(?=\])/
   }
 
   function getLinkHrefInside(endChar) {
@@ -696,8 +744,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
       return {
         f: blockNormal,
 
-        prevLine: null,
-        thisLine: null,
+        prevLine: {stream: null},
+        thisLine: {stream: null},
 
         block: blockNormal,
         htmlState: null,
@@ -714,6 +762,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
         em: false,
         strong: false,
         header: 0,
+        setext: 0,
         hr: false,
         taskList: false,
         list: false,
@@ -722,7 +771,8 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
         trailingSpace: 0,
         trailingSpaceNewLine: false,
         strikethrough: false,
-        fencedChars: null
+        emoji: false,
+        fencedEndRE: null
       };
     },
 
@@ -743,12 +793,16 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
         inline: s.inline,
         text: s.text,
         formatting: false,
+        linkText: s.linkText,
         linkTitle: s.linkTitle,
+        linkHref: s.linkHref,
         code: s.code,
         em: s.em,
         strong: s.strong,
         strikethrough: s.strikethrough,
+        emoji: s.emoji,
         header: s.header,
+        setext: s.setext,
         hr: s.hr,
         taskList: s.taskList,
         list: s.list,
@@ -758,7 +812,7 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
         trailingSpace: s.trailingSpace,
         trailingSpaceNewLine: s.trailingSpaceNewLine,
         md_inside: s.md_inside,
-        fencedChars: s.fencedChars
+        fencedEndRE: s.fencedEndRE
       };
     },
 
@@ -767,21 +821,17 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
       // Reset state.formatting
       state.formatting = false;
 
-      if (stream != state.thisLine) {
-        var forceBlankLine = state.header || state.hr;
-
-        // Reset state.header and state.hr
+      if (stream != state.thisLine.stream) {
         state.header = 0;
         state.hr = false;
 
-        if (stream.match(/^\s*$/, true) || forceBlankLine) {
+        if (stream.match(/^\s*$/, true)) {
           blankLine(state);
-          if (!forceBlankLine) return null
-          state.prevLine = null
+          return null;
         }
 
         state.prevLine = state.thisLine
-        state.thisLine = stream
+        state.thisLine = {stream: stream}
 
         // Reset state.taskList
         state.taskList = false;
@@ -790,11 +840,15 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
         state.trailingSpace = 0;
         state.trailingSpaceNewLine = false;
 
-        state.f = state.block;
-        var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, '    ').length;
-        state.indentationDiff = Math.min(indentation - state.indentation, 4);
-        state.indentation = state.indentation + state.indentationDiff;
-        if (indentation > 0) return null;
+        if (!state.localState) {
+          state.f = state.block;
+          if (state.f != htmlBlock) {
+            var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, expandedTab).length;
+            state.indentation = indentation;
+            state.indentationDiff = null;
+            if (indentation > 0) return null;
+          }
+        }
       }
       return state.f(stream, state);
     },
@@ -805,15 +859,26 @@ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
       return {state: state, mode: mode};
     },
 
+    indent: function(state, textAfter, line) {
+      if (state.block == htmlBlock && htmlMode.indent) return htmlMode.indent(state.htmlState, textAfter, line)
+      if (state.localState && state.localMode.indent) return state.localMode.indent(state.localState, textAfter, line)
+      return CodeMirror.Pass
+    },
+
     blankLine: blankLine,
 
     getType: getType,
 
+    blockCommentStart: "<!--",
+    blockCommentEnd: "-->",
+    closeBrackets: "()[]{}''\"\"``",
     fold: "markdown"
   };
   return mode;
 }, "xml");
 
+CodeMirror.defineMIME("text/markdown", "markdown");
+
 CodeMirror.defineMIME("text/x-markdown", "markdown");
 
 });
diff --git a/public/vendor/plugins/codemirror/mode/markdown/test.js b/public/vendor/plugins/codemirror/mode/markdown/test.js
index 2f43a170ca..aa1263e118 100644
--- a/public/vendor/plugins/codemirror/mode/markdown/test.js
+++ b/public/vendor/plugins/codemirror/mode/markdown/test.js
@@ -1,18 +1,22 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function() {
-  var mode = CodeMirror.getMode({tabSize: 4}, "markdown");
+  var config = {tabSize: 4, indentUnit: 2}
+  var mode = CodeMirror.getMode(config, "markdown");
   function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
-  var modeHighlightFormatting = CodeMirror.getMode({tabSize: 4}, {name: "markdown", highlightFormatting: true});
+  var modeHighlightFormatting = CodeMirror.getMode(config, {name: "markdown", highlightFormatting: true});
   function FT(name) { test.mode(name, modeHighlightFormatting, Array.prototype.slice.call(arguments, 1)); }
-  var modeAtxNoSpace = CodeMirror.getMode({tabSize: 4}, {name: "markdown", allowAtxHeaderWithoutSpace: true});
+  var modeMT_noXml = CodeMirror.getMode(config, {name: "markdown", xml: false});
+  function MT_noXml(name) { test.mode(name, modeMT_noXml, Array.prototype.slice.call(arguments, 1)); }
+  var modeMT_noFencedHighlight = CodeMirror.getMode(config, {name: "markdown", fencedCodeBlockHighlighting: false});
+  function MT_noFencedHighlight(name) { test.mode(name, modeMT_noFencedHighlight, Array.prototype.slice.call(arguments, 1)); }
+  var modeAtxNoSpace = CodeMirror.getMode(config, {name: "markdown", allowAtxHeaderWithoutSpace: true});
   function AtxNoSpaceTest(name) { test.mode(name, modeAtxNoSpace, Array.prototype.slice.call(arguments, 1)); }
-  var modeFenced = CodeMirror.getMode({tabSize: 4}, {name: "markdown", fencedCodeBlocks: true});
-  function FencedTest(name) { test.mode(name, modeFenced, Array.prototype.slice.call(arguments, 1)); }
-  var modeOverrideClasses = CodeMirror.getMode({tabsize: 4}, {
+  var modeOverrideClasses = CodeMirror.getMode(config, {
     name: "markdown",
     strikethrough: true,
+    emoji: true,
     tokenTypeOverrides: {
       "header" : "override-header",
       "code" : "override-code",
@@ -30,16 +34,19 @@
       "linkHref" : "override-link-href",
       "em" : "override-em",
       "strong" : "override-strong",
-      "strikethrough" : "override-strikethrough"
+      "strikethrough" : "override-strikethrough",
+      "emoji" : "override-emoji"
   }});
   function TokenTypeOverrideTest(name) { test.mode(name, modeOverrideClasses, Array.prototype.slice.call(arguments, 1)); }
-  var modeFormattingOverride = CodeMirror.getMode({tabsize: 4}, {
+  var modeFormattingOverride = CodeMirror.getMode(config, {
     name: "markdown",
     highlightFormatting: true,
     tokenTypeOverrides: {
       "formatting" : "override-formatting"
   }});
   function FormatTokenTypeOverrideTest(name) { test.mode(name, modeFormattingOverride, Array.prototype.slice.call(arguments, 1)); }
+  var modeET = CodeMirror.getMode(config, {name: "markdown", emoji: true});
+  function ET(name) { test.mode(name, modeET, Array.prototype.slice.call(arguments, 1)); }
 
 
   FT("formatting_emAsterisk",
@@ -64,7 +71,7 @@
      "[header&header-1&formatting&formatting-header&formatting-header-1 # ][header&header-1 foo # bar ][header&header-1&formatting&formatting-header&formatting-header-1 #]");
 
   FT("formatting_setextHeader",
-     "foo",
+     "[header&header-1 foo]",
      "[header&header-1&formatting&formatting-header&formatting-header-1 =]");
 
   FT("formatting_blockquote",
@@ -94,6 +101,11 @@
   FT("formatting_image",
      "[formatting&formatting-image&image&image-marker !][formatting&formatting-image&image&image-alt-text&link [[][image&image-alt-text&link alt text][formatting&formatting-image&image&image-alt-text&link ]]][formatting&formatting-link-string&string&url (][url&string http://link.to/image.jpg][formatting&formatting-link-string&string&url )]");
 
+  FT("codeBlock",
+     "[comment&formatting&formatting-code-block ```css]",
+     "[tag foo]",
+     "[comment&formatting&formatting-code-block ```]");
+
   MT("plainText",
      "foo");
 
@@ -141,6 +153,21 @@
      "Foo",
      "    Bar");
 
+  MT("codeBlocksAfterATX",
+     "[header&header-1 # foo]",
+     "    [comment code]");
+
+  MT("codeBlocksAfterSetext",
+     "[header&header-2 foo]",
+     "[header&header-2 ---]",
+     "    [comment code]");
+
+  MT("codeBlocksAfterFencedCode",
+     "[comment ```]",
+     "[comment foo]",
+     "[comment ```]",
+     "    [comment code]");
+
   // Inline code using backticks
   MT("inlineCodeUsingBackticks",
      "foo [comment `bar`]");
@@ -182,6 +209,10 @@
   MT("closedBackticks",
      "[comment ``foo ``` bar` hello``] world");
 
+  // info string cannot contain backtick, thus should result in inline code
+  MT("closingFencedMarksOnSameLine",
+     "[comment ``` code ```] foo");
+
   // atx headers
   // http://daringfireball.net/projects/markdown/syntax#header
 
@@ -225,6 +256,19 @@
   MT("atxH1inline",
      "[header&header-1 # foo ][header&header-1&em *bar*]");
 
+  MT("atxIndentedTooMuch",
+     "[header&header-1 # foo]",
+     "    [comment # bar]");
+
+  // disable atx inside blockquote until we implement proper blockquote inner mode
+  // TODO: fix to be CommonMark-compliant
+  MT("atxNestedInsideBlockquote",
+     "[quote&quote-1 > # foo]");
+
+  MT("atxAfterBlockquote",
+     "[quote&quote-1 > foo]",
+     "[header&header-1 # bar]");
+
   // Setext headers - H1, H2
   // Per documentation, "Any number of underlining =’s or -’s will work."
   // http://daringfireball.net/projects/markdown/syntax#header
@@ -234,27 +278,27 @@
   //
   // Check if single underlining = works
   MT("setextH1",
-     "foo",
+     "[header&header-1 foo]",
      "[header&header-1 =]");
 
   // Check if 3+ ='s work
   MT("setextH1",
-     "foo",
+     "[header&header-1 foo]",
      "[header&header-1 ===]");
 
   // Check if single underlining - works
   MT("setextH2",
-     "foo",
+     "[header&header-2 foo]",
      "[header&header-2 -]");
 
   // Check if 3+ -'s work
   MT("setextH2",
-     "foo",
+     "[header&header-2 foo]",
      "[header&header-2 ---]");
 
   // http://spec.commonmark.org/0.19/#example-45
   MT("setextH2AllowSpaces",
-     "foo",
+     "[header&header-2 foo]",
      "   [header&header-2 ----      ]");
 
   // http://spec.commonmark.org/0.19/#example-44
@@ -262,15 +306,86 @@
      "     [comment foo]",
      "[hr ---]");
 
+  MT("setextAfterFencedCode",
+     "[comment ```]",
+     "[comment foo]",
+     "[comment ```]",
+     "[header&header-2 bar]",
+     "[header&header-2 ---]");
+
+  MT("setextAferATX",
+     "[header&header-1 # foo]",
+     "[header&header-2 bar]",
+     "[header&header-2 ---]");
+
   // http://spec.commonmark.org/0.19/#example-51
   MT("noSetextAfterQuote",
      "[quote&quote-1 > foo]",
+     "[hr ---]",
+     "",
+     "[quote&quote-1 > foo]",
+     "[quote&quote-1 bar]",
      "[hr ---]");
 
   MT("noSetextAfterList",
      "[variable-2 - foo]",
      "[hr ---]");
 
+  MT("noSetextAfterList_listContinuation",
+     "[variable-2 - foo]",
+     "bar",
+     "[hr ---]");
+
+  MT("setextAfterList_afterIndentedCode",
+     "[variable-2 - foo]",
+     "",
+     "      [comment bar]",
+     "[header&header-2 baz]",
+     "[header&header-2 ---]");
+
+  MT("setextAfterList_afterFencedCodeBlocks",
+     "[variable-2 - foo]",
+     "",
+     "      [comment ```]",
+     "      [comment bar]",
+     "      [comment ```]",
+     "[header&header-2 baz]",
+     "[header&header-2 ---]");
+
+  MT("setextAfterList_afterHeader",
+     "[variable-2 - foo]",
+     "  [variable-2&header&header-1 # bar]",
+     "[header&header-2 baz]",
+     "[header&header-2 ---]");
+
+  MT("setextAfterList_afterHr",
+     "[variable-2 - foo]",
+     "",
+     "  [hr ---]",
+     "[header&header-2 bar]",
+     "[header&header-2 ---]");
+
+  MT("setext_nestedInlineMarkup",
+     "[header&header-1 foo ][em&header&header-1 *bar*]",
+     "[header&header-1 =]");
+
+  MT("setext_linkDef",
+     "[link [[aaa]]:] [string&url http://google.com 'title']",
+     "[hr ---]");
+
+  // currently, looks max one line ahead, thus won't catch valid CommonMark
+  //  markup
+  MT("setext_oneLineLookahead",
+     "foo",
+     "[header&header-1 bar]",
+     "[header&header-1 =]");
+
+  // ensure we don't regard space after dash as a list
+  MT("setext_emptyList",
+     "[header&header-2 foo]",
+     "[header&header-2 - ]",
+     "foo");
+
   // Single-line blockquote with trailing space
   MT("blockquoteSpace",
      "[quote&quote-1 > foo]");
@@ -284,12 +399,22 @@
      "foo",
      "[quote&quote-1 > bar]");
 
-  // Nested blockquote
-  MT("blockquoteSpace",
+  MT("blockquoteNested",
      "[quote&quote-1 > foo]",
      "[quote&quote-1 >][quote&quote-2 > foo]",
      "[quote&quote-1 >][quote&quote-2 >][quote&quote-3 > foo]");
 
+  // ensure quote-level is inferred correctly even if indented
+  MT("blockquoteNestedIndented",
+     " [quote&quote-1 > foo]",
+     " [quote&quote-1 >][quote&quote-2 > foo]",
+     " [quote&quote-1 >][quote&quote-2 >][quote&quote-3 > foo]");
+
+  // ensure quote-level is inferred correctly even if indented
+  MT("blockquoteIndentedTooMuch",
+     "foo",
+     "    > bar");
+
   // Single-line blockquote followed by normal paragraph
   MT("blockquoteThenParagraph",
      "[quote&quote-1 >foo]",
@@ -320,6 +445,20 @@
      "",
      "hello");
 
+  // disallow lists inside blockquote for now because it causes problems outside blockquote
+  // TODO: fix to be CommonMark-compliant
+  MT("listNestedInBlockquote",
+     "[quote&quote-1 > - foo]");
+
+  // disallow fenced blocks inside blockquote because it causes problems outside blockquote
+  // TODO: fix to be CommonMark-compliant
+  MT("fencedBlockNestedInBlockquote",
+     "[quote&quote-1 > ```]",
+     "[quote&quote-1 > code]",
+     "[quote&quote-1 > ```]",
+     // ensure we still allow inline code
+     "[quote&quote-1 > ][quote&quote-1&comment `code`]");
+
   // Header with leading space after continued blockquote (#3287, negative indentation)
   MT("headerAfterContinuedBlockquote",
      "[quote&quote-1 > foo]",
@@ -357,11 +496,10 @@
      "[variable-2 1. foo]",
      "[variable-2 2. bar]");
 
-  // Lists require a preceding blank line (per Dingus)
-  MT("listBogus",
+  MT("listFromParagraph",
      "foo",
-     "1. bar",
-     "2. hello");
+     "[variable-2 1. bar]",
+     "[variable-2 2. hello]");
 
   // List after hr
   MT("listAfterHr",
@@ -378,32 +516,53 @@
      "[variable-2 - foo]",
      "[hr -----]");
 
+  MT("hrAfterFencedCode",
+     "[comment ```]",
+     "[comment code]",
+     "[comment ```]",
+     "[hr ---]");
+
+  // allow hr inside lists
+  // (require prev line to be empty or hr, TODO: non-CommonMark-compliant)
+  MT("hrInsideList",
+     "[variable-2 - foo]",
+     "",
+     "  [hr ---]",
+     "     [hr ---]",
+     "",
+     "      [comment ---]");
+
+  MT("consecutiveHr",
+     "[hr ---]",
+     "[hr ---]",
+     "[hr ---]");
+
   // Formatting in lists (*)
   MT("listAsteriskFormatting",
      "[variable-2 * ][variable-2&em *foo*][variable-2  bar]",
      "[variable-2 * ][variable-2&strong **foo**][variable-2  bar]",
-     "[variable-2 * ][variable-2&strong **][variable-2&em&strong *foo**][variable-2&em *][variable-2  bar]",
+     "[variable-2 * ][variable-2&em&strong ***foo***][variable-2  bar]",
      "[variable-2 * ][variable-2&comment `foo`][variable-2  bar]");
 
   // Formatting in lists (+)
   MT("listPlusFormatting",
      "[variable-2 + ][variable-2&em *foo*][variable-2  bar]",
      "[variable-2 + ][variable-2&strong **foo**][variable-2  bar]",
-     "[variable-2 + ][variable-2&strong **][variable-2&em&strong *foo**][variable-2&em *][variable-2  bar]",
+     "[variable-2 + ][variable-2&em&strong ***foo***][variable-2  bar]",
      "[variable-2 + ][variable-2&comment `foo`][variable-2  bar]");
 
   // Formatting in lists (-)
   MT("listDashFormatting",
      "[variable-2 - ][variable-2&em *foo*][variable-2  bar]",
      "[variable-2 - ][variable-2&strong **foo**][variable-2  bar]",
-     "[variable-2 - ][variable-2&strong **][variable-2&em&strong *foo**][variable-2&em *][variable-2  bar]",
+     "[variable-2 - ][variable-2&em&strong ***foo***][variable-2  bar]",
      "[variable-2 - ][variable-2&comment `foo`][variable-2  bar]");
 
   // Formatting in lists (1.)
   MT("listNumberFormatting",
      "[variable-2 1. ][variable-2&em *foo*][variable-2  bar]",
      "[variable-2 2. ][variable-2&strong **foo**][variable-2  bar]",
-     "[variable-2 3. ][variable-2&strong **][variable-2&em&strong *foo**][variable-2&em *][variable-2  bar]",
+     "[variable-2 3. ][variable-2&em&strong ***foo***][variable-2  bar]",
      "[variable-2 4. ][variable-2&comment `foo`][variable-2  bar]");
 
   // Paragraph lists
@@ -469,6 +628,66 @@
      "   [variable-3 * Otherwise, it will be part of the level where it does meet this.]",
      " [variable-2 * World]");
 
+  // should handle nested and un-nested lists
+  MT("listCommonMark_MixedIndents",
+     "[variable-2 * list1]",
+     "    [variable-2 list1]",
+     "  [variable-2&header&header-1 # heading still part of list1]",
+     "  [variable-2 text after heading still part of list1]",
+     "",
+     "      [comment indented codeblock]",
+     "  [variable-2 list1 after code block]",
+     "  [variable-3 * list2]",
+     // amount of spaces on empty lines between lists doesn't matter
+     "              ",
+     // extra empty lines irrelevant
+     "",
+     "",
+     "    [variable-3 indented text part of list2]",
+     "    [keyword * list3]",
+     "",
+     "    [variable-3 text at level of list2]",
+     "",
+     "  [variable-2 de-indented text part of list1 again]",
+     "",
+     "  [variable-2&comment ```]",
+     "  [comment code]",
+     "  [variable-2&comment ```]",
+     "",
+     "  [variable-2 text after fenced code]");
+
+  // should correctly parse numbered list content indentation
+  MT("listCommonMark_NumeberedListIndent",
+     "[variable-2 1000. list with base indent of 6]",
+     "",
+     "      [variable-2 text must be indented 6 spaces at minimum]",
+     "",
+     "         [variable-2 9-spaces indented text still part of list]",
+     "",
+     "          [comment indented codeblock starts at 10 spaces]",
+     "",
+     "     [comment text indented by 5 spaces no longer belong to list]");
+
+  // should consider tab as 4 spaces
+  MT("listCommonMark_TabIndented",
+     "[variable-2 * list]",
+     "\t[variable-3 * list2]",
+     "",
+     "\t\t[variable-3 part of list2]");
+
+  MT("listAfterBlockquote",
+     "[quote&quote-1 > foo]",
+     "[variable-2 - bar]");
+
+  // shouldn't create sublist if it's indented more than allowed
+  MT("nestedListIndentedTooMuch",
+     "[variable-2 - foo]",
+     "          [variable-2 - bar]");
+
+  MT("listIndentedTooMuchAfterParagraph",
+     "foo",
+     "    - bar");
+
   // Blockquote
   MT("blockquote",
      "[variable-2 * foo]",
@@ -606,7 +825,7 @@
      "[image&image-marker !][image&image-alt-text&link [[][image-alt-text&strong&image&link **alt text**][image&image-alt-text&link ]]][string&url (http://link.to/image.jpg)]");
 
   MT("imageEmStrong",
-     "[image&image-marker !][image&image-alt-text&link [[][image-alt-text&image&strong&link **][image&image-alt-text&em&strong&link *alt text**][image&image-alt-text&em&link *][image&image-alt-text&link ]]][string&url (http://link.to/image.jpg)]");
+     "[image&image-marker !][image&image-alt-text&link [[][image&image-alt-text&em&strong&link ***alt text***][image&image-alt-text&link ]]][string&url (http://link.to/image.jpg)]");
 
   // Inline link with title
   MT("linkTitle",
@@ -630,7 +849,12 @@
 
   // Inline link with EmStrong
   MT("linkEmStrong",
-     "[link [[][link&strong **][link&em&strong *foo**][link&em *][link ]]][string&url (http://example.com/)] bar");
+     "[link [[][link&em&strong ***foo***][link ]]][string&url (http://example.com/)] bar");
+
+  MT("multilineLink",
+     "[link [[foo]",
+     "[link bar]]][string&url (https://foo#_a)]",
+     "should not be italics")
 
   // Image with title
   MT("imageTitle",
@@ -648,7 +872,7 @@
   // regularly in text, especially in quoted material, and no space is allowed
   // between square brackets and parentheses (per Dingus).
   MT("notALink",
-     "[[foo]] (bar)");
+     "[link [[foo]]] (bar)");
 
   // Reference-style links
   MT("linkReference",
@@ -664,7 +888,7 @@
 
   // Reference-style links with EmStrong
   MT("linkReferenceEmStrong",
-     "[link [[][link&strong **][link&em&strong *foo**][link&em *][link ]]][string&url [[bar]]] hello");
+     "[link [[][link&em&strong ***foo***][link ]]][string&url [[bar]]] hello");
 
   // Reference-style links with optional space separator (per documentation)
   // "You can optionally use a space to separate the sets of brackets"
@@ -673,7 +897,7 @@
 
   // Should only allow a single space ("...use *a* space...")
   MT("linkReferenceDoubleSpace",
-     "[[foo]]  [[bar]] hello");
+     "[link [[foo]]]  [link [[bar]]] hello");
 
   // Reference-style links with implicit link name
   MT("linkImplicit",
@@ -734,7 +958,7 @@
      "[link [[foo \\]]: bar]]:] [string&url http://example.com/]");
 
   MT("labelEscapeEnd",
-     "[[foo\\]]: http://example.com/");
+     "\\[[foo\\]]: http://example.com/");
 
   MT("linkWeb",
      "[link <http://example.com/>] foo");
@@ -758,7 +982,7 @@
      "foo[em *bar*]hello");
 
   MT("emInWordUnderscore",
-     "foo[em _bar_]hello");
+     "foo_bar_hello");
 
   // Per documentation: "...surround an * or _ with spaces, it’ll be
   // treated as a literal asterisk or underscore."
@@ -767,11 +991,11 @@
      "foo [em _bar _ hello_] world");
 
   MT("emEscapedBySpaceOut",
-     "foo _ bar[em _hello_]world");
+     "foo _ bar [em _hello_] world");
 
   MT("emEscapedByNewline",
      "foo",
-     "_ bar[em _hello_]world");
+     "_ bar [em _hello_] world");
 
   // Unclosed emphasis characters
   // Instead of simply marking as EM / STRONG, it would be nice to have an
@@ -792,14 +1016,14 @@
      "[em *foo][em&strong **bar*][strong hello**] world");
 
   MT("emStrongUnderscore",
-     "[em _foo][em&strong __bar_][strong hello__] world");
+     "[em _foo ][em&strong __bar_][strong  hello__] world");
 
   // "...same character must be used to open and close an emphasis span.""
   MT("emStrongMixed",
      "[em _foo][em&strong **bar*hello__ world]");
 
   MT("emStrongMixed",
-     "[em *foo][em&strong __bar_hello** world]");
+     "[em *foo ][em&strong __bar_hello** world]");
 
   MT("linkWithNestedParens",
      "[link [[foo]]][string&url (bar(baz))]")
@@ -914,26 +1138,77 @@
   TokenTypeOverrideTest("overrideStrikethrough",
     "[override-strikethrough ~~foo~~]");
 
+  TokenTypeOverrideTest("overrideEmoji",
+    "[override-emoji :foo:]");
+
   FormatTokenTypeOverrideTest("overrideFormatting",
     "[override-formatting-escape \\*]");
 
   // Tests to make sure GFM-specific things aren't getting through
 
   MT("taskList",
-     "[variable-2 * [ ]] bar]");
+     "[variable-2 * ][link&variable-2 [[ ]]][variable-2 bar]");
 
-  MT("noFencedCodeBlocks",
-     "~~~",
-     "foo",
-     "~~~");
-
-  FencedTest("fencedCodeBlocks",
+  MT("fencedCodeBlocks",
      "[comment ```]",
      "[comment foo]",
+     "",
+     "[comment bar]",
+     "[comment ```]",
+     "baz");
+
+  MT("fencedCodeBlocks_invalidClosingFence_trailingText",
+     "[comment ```]",
+     "[comment foo]",
+     "[comment ``` must not have trailing text]",
+     "[comment baz]");
+
+  MT("fencedCodeBlocks_invalidClosingFence_trailingTabs",
+     "[comment ```]",
+     "[comment foo]",
+     "[comment ```\t]",
+     "[comment baz]");
+
+  MT("fencedCodeBlocks_validClosingFence",
+     "[comment ```]",
+     "[comment foo]",
+     // may have trailing spaces
+     "[comment ```     ]",
+     "baz");
+
+  MT("fencedCodeBlocksInList_closingFenceIndented",
+     "[variable-2 - list]",
+     "    [variable-2&comment ```]",
+     "    [comment foo]",
+     "     [variable-2&comment ```]",
+     "    [variable-2 baz]");
+
+  MT("fencedCodeBlocksInList_closingFenceIndentedTooMuch",
+     "[variable-2 - list]",
+     "    [variable-2&comment ```]",
+     "    [comment foo]",
+     "      [comment ```]",
+     "    [comment baz]");
+
+  MT("fencedCodeBlockModeSwitching",
+     "[comment ```javascript]",
+     "[variable foo]",
+     "",
      "[comment ```]",
      "bar");
 
-  FencedTest("fencedCodeBlocksMultipleChars",
+  MT_noFencedHighlight("fencedCodeBlock_noHighlight",
+     "[comment ```javascript]",
+     "[comment foo]",
+     "[comment ```]");
+
+  MT("fencedCodeBlockModeSwitchingObjc",
+     "[comment ```objective-c]",
+     "[keyword @property] [variable NSString] [operator *] [variable foo];",
+     "[comment ```]",
+     "bar");
+
+  MT("fencedCodeBlocksMultipleChars",
      "[comment `````]",
      "[comment foo]",
      "[comment ```]",
@@ -941,20 +1216,20 @@
      "[comment `````]",
      "bar");
 
-  FencedTest("fencedCodeBlocksTildes",
+  MT("fencedCodeBlocksTildes",
      "[comment ~~~]",
      "[comment foo]",
      "[comment ~~~]",
      "bar");
 
-  FencedTest("fencedCodeBlocksTildesMultipleChars",
+  MT("fencedCodeBlocksTildesMultipleChars",
      "[comment ~~~~~]",
      "[comment ~~~]",
      "[comment foo]",
      "[comment ~~~~~]",
      "bar");
 
-  FencedTest("fencedCodeBlocksMultipleChars",
+  MT("fencedCodeBlocksMultipleChars",
      "[comment `````]",
      "[comment foo]",
      "[comment ```]",
@@ -962,19 +1237,42 @@
      "[comment `````]",
      "bar");
 
-  FencedTest("fencedCodeBlocksMixed",
+  MT("fencedCodeBlocksMixed",
      "[comment ~~~]",
      "[comment ```]",
      "[comment foo]",
      "[comment ~~~]",
      "bar");
 
+  MT("fencedCodeBlocksAfterBlockquote",
+     "[quote&quote-1 > foo]",
+     "[comment ```]",
+     "[comment bar]",
+     "[comment ```]");
+
+  // fencedCode indented too much should act as simple indentedCode
+  //  (hence has no highlight formatting)
+  FT("tooMuchIndentedFencedCode",
+     "    [comment ```]",
+     "    [comment code]",
+     "    [comment ```]");
+
+  MT("autoTerminateFencedCodeWhenLeavingList",
+     "[variable-2 - list1]",
+     "  [variable-3 - list2]",
+     "    [variable-3&comment ```]",
+     "    [comment code]",
+     "  [variable-3 - list2]",
+     "  [variable-2&comment ```]",
+     "  [comment code]",
+     "[quote&quote-1 > foo]");
+
   // Tests that require XML mode
 
   MT("xmlMode",
      "[tag&bracket <][tag div][tag&bracket >]",
-     "*foo*",
-     "[tag&bracket <][tag http://github.com][tag&bracket />]",
+     "  *foo*",
+     "  [tag&bracket <][tag http://github.com][tag&bracket />]",
      "[tag&bracket </][tag div][tag&bracket >]",
      "[link <http://github.com/>]");
 
@@ -987,4 +1285,33 @@
      "[tag&bracket <][tag div][tag&bracket >]",
      "[tag&bracket </][tag div][tag&bracket >]");
 
+  MT("xmlModeLineBreakInTags",
+     "[tag&bracket <][tag div] [attribute id]=[string \"1\"]",
+     "     [attribute class]=[string \"sth\"][tag&bracket >]xxx",
+     "[tag&bracket </][tag div][tag&bracket >]");
+
+  MT("xmlModeCommentWithBlankLine",
+     "[comment <!-- Hello]",
+     "",
+     "[comment World -->]");
+
+  MT("xmlModeCDATA",
+     "[atom <![CDATA[ Hello]",
+     "",
+     "[atom FooBar]",
+     "[atom Test ]]]]>]");
+
+  MT("xmlModePreprocessor",
+     "[meta <?php] [meta echo '1234'; ?>]");
+
+  MT_noXml("xmlHighlightDisabled",
+     "<div>foo</div>");
+
+  // Tests Emojis
+
+  ET("emojiDefault",
+    "[builtin :foobar:]");
+
+  ET("emojiTable",
+    " :--:");
 })();
diff --git a/public/vendor/plugins/codemirror/mode/mathematica/index.html b/public/vendor/plugins/codemirror/mode/mathematica/index.html
index 57c4298531..2e5a5748fc 100644
--- a/public/vendor/plugins/codemirror/mode/mathematica/index.html
+++ b/public/vendor/plugins/codemirror/mode/mathematica/index.html
@@ -12,7 +12,7 @@
   .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
 </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/mathematica/mathematica.js b/public/vendor/plugins/codemirror/mode/mathematica/mathematica.js
index d6977088ce..72b3492101 100644
--- a/public/vendor/plugins/codemirror/mode/mathematica/mathematica.js
+++ b/public/vendor/plugins/codemirror/mode/mathematica/mathematica.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 // Mathematica mode copyright (c) 2015 by Calin Barbat
 // Based on code by Patrick Scheibe (halirutan)
@@ -71,12 +71,12 @@ CodeMirror.defineMode('mathematica', function(_config, _parserConfig) {
     }
 
     // usage
-    if (stream.match(/([a-zA-Z\$]+(?:`?[a-zA-Z0-9\$])*::usage)/, true, false)) {
+    if (stream.match(/([a-zA-Z\$][a-zA-Z0-9\$]*(?:`[a-zA-Z0-9\$]+)*::usage)/, true, false)) {
       return 'meta';
     }
 
     // message
-    if (stream.match(/([a-zA-Z\$]+(?:`?[a-zA-Z0-9\$])*::[a-zA-Z\$][a-zA-Z0-9\$]*):?/, true, false)) {
+    if (stream.match(/([a-zA-Z\$][a-zA-Z0-9\$]*(?:`[a-zA-Z0-9\$]+)*::[a-zA-Z\$][a-zA-Z0-9\$]*):?/, true, false)) {
       return 'string-2';
     }
 
diff --git a/public/vendor/plugins/codemirror/mode/mbox/index.html b/public/vendor/plugins/codemirror/mode/mbox/index.html
index 248ea98e18..c70cf36074 100644
--- a/public/vendor/plugins/codemirror/mode/mbox/index.html
+++ b/public/vendor/plugins/codemirror/mode/mbox/index.html
@@ -9,7 +9,7 @@
 <script src="mbox.js"></script>
 <style>.CodeMirror { border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; }</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/mbox/mbox.js b/public/vendor/plugins/codemirror/mode/mbox/mbox.js
index ba2416ac81..640437ed64 100644
--- a/public/vendor/plugins/codemirror/mode/mbox/mbox.js
+++ b/public/vendor/plugins/codemirror/mode/mbox/mbox.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/meta.js b/public/vendor/plugins/codemirror/mode/meta.js
index eb25e242dd..e9155037a1 100644
--- a/public/vendor/plugins/codemirror/mode/meta.js
+++ b/public/vendor/plugins/codemirror/mode/meta.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -13,19 +13,19 @@
 
   CodeMirror.modeInfo = [
     {name: "APL", mime: "text/apl", mode: "apl", ext: ["dyalog", "apl"]},
-    {name: "PGP", mimes: ["application/pgp", "application/pgp-keys", "application/pgp-signature"], mode: "asciiarmor", ext: ["pgp"]},
+    {name: "PGP", mimes: ["application/pgp", "application/pgp-encrypted", "application/pgp-keys", "application/pgp-signature"], mode: "asciiarmor", ext: ["asc", "pgp", "sig"]},
     {name: "ASN.1", mime: "text/x-ttcn-asn", mode: "asn.1", ext: ["asn", "asn1"]},
     {name: "Asterisk", mime: "text/x-asterisk", mode: "asterisk", file: /^extensions\.conf$/i},
     {name: "Brainfuck", mime: "text/x-brainfuck", mode: "brainfuck", ext: ["b", "bf"]},
-    {name: "C", mime: "text/x-csrc", mode: "clike", ext: ["c", "h"]},
+    {name: "C", mime: "text/x-csrc", mode: "clike", ext: ["c", "h", "ino"]},
     {name: "C++", mime: "text/x-c++src", mode: "clike", ext: ["cpp", "c++", "cc", "cxx", "hpp", "h++", "hh", "hxx"], alias: ["cpp"]},
     {name: "Cobol", mime: "text/x-cobol", mode: "cobol", ext: ["cob", "cpy"]},
-    {name: "C#", mime: "text/x-csharp", mode: "clike", ext: ["cs"], alias: ["csharp"]},
+    {name: "C#", mime: "text/x-csharp", mode: "clike", ext: ["cs"], alias: ["csharp", "cs"]},
     {name: "Clojure", mime: "text/x-clojure", mode: "clojure", ext: ["clj", "cljc", "cljx"]},
     {name: "ClojureScript", mime: "text/x-clojurescript", mode: "clojure", ext: ["cljs"]},
     {name: "Closure Stylesheets (GSS)", mime: "text/x-gss", mode: "css", ext: ["gss"]},
     {name: "CMake", mime: "text/x-cmake", mode: "cmake", ext: ["cmake", "cmake.in"], file: /^CMakeLists.txt$/},
-    {name: "CoffeeScript", mime: "text/x-coffeescript", mode: "coffeescript", ext: ["coffee"], alias: ["coffee", "coffee-script"]},
+    {name: "CoffeeScript", mimes: ["application/vnd.coffeescript", "text/coffeescript", "text/x-coffeescript"], mode: "coffeescript", ext: ["coffee"], alias: ["coffee", "coffee-script"]},
     {name: "Common Lisp", mime: "text/x-common-lisp", mode: "commonlisp", ext: ["cl", "lisp", "el"], alias: ["lisp"]},
     {name: "Cypher", mime: "application/x-cypher-query", mode: "cypher", ext: ["cyp", "cypher"]},
     {name: "Cython", mime: "text/x-cython", mode: "python", ext: ["pyx", "pxd", "pxi"]},
@@ -47,26 +47,27 @@
     {name: "Embedded Javascript", mime: "application/x-ejs", mode: "htmlembedded", ext: ["ejs"]},
     {name: "Embedded Ruby", mime: "application/x-erb", mode: "htmlembedded", ext: ["erb"]},
     {name: "Erlang", mime: "text/x-erlang", mode: "erlang", ext: ["erl"]},
+    {name: "Esper", mime: "text/x-esper", mode: "sql"},
     {name: "Factor", mime: "text/x-factor", mode: "factor", ext: ["factor"]},
     {name: "FCL", mime: "text/x-fcl", mode: "fcl"},
     {name: "Forth", mime: "text/x-forth", mode: "forth", ext: ["forth", "fth", "4th"]},
-    {name: "Fortran", mime: "text/x-fortran", mode: "fortran", ext: ["f", "for", "f77", "f90"]},
+    {name: "Fortran", mime: "text/x-fortran", mode: "fortran", ext: ["f", "for", "f77", "f90", "f95"]},
     {name: "F#", mime: "text/x-fsharp", mode: "mllike", ext: ["fs"], alias: ["fsharp"]},
     {name: "Gas", mime: "text/x-gas", mode: "gas", ext: ["s"]},
     {name: "Gherkin", mime: "text/x-feature", mode: "gherkin", ext: ["feature"]},
     {name: "GitHub Flavored Markdown", mime: "text/x-gfm", mode: "gfm", file: /^(readme|contributing|history).md$/i},
     {name: "Go", mime: "text/x-go", mode: "go", ext: ["go"]},
-    {name: "Groovy", mime: "text/x-groovy", mode: "groovy", ext: ["groovy", "gradle"]},
+    {name: "Groovy", mime: "text/x-groovy", mode: "groovy", ext: ["groovy", "gradle"], file: /^Jenkinsfile$/},
     {name: "HAML", mime: "text/x-haml", mode: "haml", ext: ["haml"]},
     {name: "Haskell", mime: "text/x-haskell", mode: "haskell", ext: ["hs"]},
     {name: "Haskell (Literate)", mime: "text/x-literate-haskell", mode: "haskell-literate", ext: ["lhs"]},
     {name: "Haxe", mime: "text/x-haxe", mode: "haxe", ext: ["hx"]},
     {name: "HXML", mime: "text/x-hxml", mode: "haxe", ext: ["hxml"]},
     {name: "ASP.NET", mime: "application/x-aspx", mode: "htmlembedded", ext: ["aspx"], alias: ["asp", "aspx"]},
-    {name: "HTML", mime: "text/html", mode: "htmlmixed", ext: ["html", "htm"], alias: ["xhtml"]},
+    {name: "HTML", mime: "text/html", mode: "htmlmixed", ext: ["html", "htm", "handlebars", "hbs"], alias: ["xhtml"]},
     {name: "HTTP", mime: "message/http", mode: "http"},
     {name: "IDL", mime: "text/x-idl", mode: "idl", ext: ["pro"]},
-    {name: "Jade", mime: "text/x-jade", mode: "jade", ext: ["jade"]},
+    {name: "Pug", mime: "text/x-pug", mode: "pug", ext: ["jade", "pug"], alias: ["jade"]},
     {name: "Java", mime: "text/x-java", mode: "clike", ext: ["java"]},
     {name: "Java Server Pages", mime: "application/x-jsp", mode: "htmlembedded", ext: ["jsp"], alias: ["jsp"]},
     {name: "JavaScript", mimes: ["text/javascript", "text/ecmascript", "application/javascript", "application/x-javascript", "application/ecmascript"],
@@ -74,7 +75,7 @@
     {name: "JSON", mimes: ["application/json", "application/x-json"], mode: "javascript", ext: ["json", "map"], alias: ["json5"]},
     {name: "JSON-LD", mime: "application/ld+json", mode: "javascript", ext: ["jsonld"], alias: ["jsonld"]},
     {name: "JSX", mime: "text/jsx", mode: "jsx", ext: ["jsx"]},
-    {name: "Jinja2", mime: "null", mode: "jinja2"},
+    {name: "Jinja2", mime: "text/jinja2", mode: "jinja2", ext: ["j2", "jinja", "jinja2"]},
     {name: "Julia", mime: "text/x-julia", mode: "julia", ext: ["jl"]},
     {name: "Kotlin", mime: "text/x-kotlin", mode: "clike", ext: ["kt"]},
     {name: "LESS", mime: "text/x-less", mode: "css", ext: ["less"]},
@@ -83,7 +84,7 @@
     {name: "Markdown", mime: "text/x-markdown", mode: "markdown", ext: ["markdown", "md", "mkd"]},
     {name: "mIRC", mime: "text/mirc", mode: "mirc"},
     {name: "MariaDB SQL", mime: "text/x-mariadb", mode: "sql"},
-    {name: "Mathematica", mime: "text/x-mathematica", mode: "mathematica", ext: ["m", "nb"]},
+    {name: "Mathematica", mime: "text/x-mathematica", mode: "mathematica", ext: ["m", "nb", "wl", "wls"]},
     {name: "Modelica", mime: "text/x-modelica", mode: "modelica", ext: ["mo"]},
     {name: "MUMPS", mime: "text/x-mumps", mode: "mumps", ext: ["mps"]},
     {name: "MS SQL", mime: "text/x-mssql", mode: "sql"},
@@ -91,25 +92,27 @@
     {name: "MySQL", mime: "text/x-mysql", mode: "sql"},
     {name: "Nginx", mime: "text/x-nginx-conf", mode: "nginx", file: /nginx.*\.conf$/i},
     {name: "NSIS", mime: "text/x-nsis", mode: "nsis", ext: ["nsh", "nsi"]},
-    {name: "NTriples", mime: "text/n-triples", mode: "ntriples", ext: ["nt"]},
-    {name: "Objective C", mime: "text/x-objectivec", mode: "clike", ext: ["m", "mm"], alias: ["objective-c", "objc"]},
+    {name: "NTriples", mimes: ["application/n-triples", "application/n-quads", "text/n-triples"],
+     mode: "ntriples", ext: ["nt", "nq"]},
+    {name: "Objective-C", mime: "text/x-objectivec", mode: "clike", ext: ["m", "mm"], alias: ["objective-c", "objc"]},
     {name: "OCaml", mime: "text/x-ocaml", mode: "mllike", ext: ["ml", "mli", "mll", "mly"]},
     {name: "Octave", mime: "text/x-octave", mode: "octave", ext: ["m"]},
     {name: "Oz", mime: "text/x-oz", mode: "oz", ext: ["oz"]},
     {name: "Pascal", mime: "text/x-pascal", mode: "pascal", ext: ["p", "pas"]},
     {name: "PEG.js", mime: "null", mode: "pegjs", ext: ["jsonld"]},
     {name: "Perl", mime: "text/x-perl", mode: "perl", ext: ["pl", "pm"]},
-    {name: "PHP", mime: "application/x-httpd-php", mode: "php", ext: ["php", "php3", "php4", "php5", "phtml"]},
+    {name: "PHP", mimes: ["text/x-php", "application/x-httpd-php", "application/x-httpd-php-open"], mode: "php", ext: ["php", "php3", "php4", "php5", "php7", "phtml"]},
     {name: "Pig", mime: "text/x-pig", mode: "pig", ext: ["pig"]},
     {name: "Plain Text", mime: "text/plain", mode: "null", ext: ["txt", "text", "conf", "def", "list", "log"]},
     {name: "PLSQL", mime: "text/x-plsql", mode: "sql", ext: ["pls"]},
+    {name: "PostgreSQL", mime: "text/x-pgsql", mode: "sql"},
     {name: "PowerShell", mime: "application/x-powershell", mode: "powershell", ext: ["ps1", "psd1", "psm1"]},
     {name: "Properties files", mime: "text/x-properties", mode: "properties", ext: ["properties", "ini", "in"], alias: ["ini", "properties"]},
     {name: "ProtoBuf", mime: "text/x-protobuf", mode: "protobuf", ext: ["proto"]},
     {name: "Python", mime: "text/x-python", mode: "python", ext: ["BUILD", "bzl", "py", "pyw"], file: /^(BUCK|BUILD)$/},
     {name: "Puppet", mime: "text/x-puppet", mode: "puppet", ext: ["pp"]},
     {name: "Q", mime: "text/x-q", mode: "q", ext: ["q"]},
-    {name: "R", mime: "text/x-rsrc", mode: "r", ext: ["r"], alias: ["rscript"]},
+    {name: "R", mime: "text/x-rsrc", mode: "r", ext: ["r", "R"], alias: ["rscript"]},
     {name: "reStructuredText", mime: "text/x-rst", mode: "rst", ext: ["rst"], alias: ["rst"]},
     {name: "RPM Changes", mime: "text/x-rpm-changes", mode: "rpm"},
     {name: "RPM Spec", mime: "text/x-rpm-spec", mode: "rpm", ext: ["spec"]},
@@ -120,21 +123,24 @@
     {name: "Scala", mime: "text/x-scala", mode: "clike", ext: ["scala"]},
     {name: "Scheme", mime: "text/x-scheme", mode: "scheme", ext: ["scm", "ss"]},
     {name: "SCSS", mime: "text/x-scss", mode: "css", ext: ["scss"]},
-    {name: "Shell", mime: "text/x-sh", mode: "shell", ext: ["sh", "ksh", "bash"], alias: ["bash", "sh", "zsh"], file: /^PKGBUILD$/},
+    {name: "Shell", mimes: ["text/x-sh", "application/x-sh"], mode: "shell", ext: ["sh", "ksh", "bash"], alias: ["bash", "sh", "zsh"], file: /^PKGBUILD$/},
     {name: "Sieve", mime: "application/sieve", mode: "sieve", ext: ["siv", "sieve"]},
     {name: "Slim", mimes: ["text/x-slim", "application/x-slim"], mode: "slim", ext: ["slim"]},
     {name: "Smalltalk", mime: "text/x-stsrc", mode: "smalltalk", ext: ["st"]},
     {name: "Smarty", mime: "text/x-smarty", mode: "smarty", ext: ["tpl"]},
     {name: "Solr", mime: "text/x-solr", mode: "solr"},
+    {name: "SML", mime: "text/x-sml", mode: "mllike", ext: ["sml", "sig", "fun", "smackspec"]},
     {name: "Soy", mime: "text/x-soy", mode: "soy", ext: ["soy"], alias: ["closure template"]},
     {name: "SPARQL", mime: "application/sparql-query", mode: "sparql", ext: ["rq", "sparql"], alias: ["sparul"]},
     {name: "Spreadsheet", mime: "text/x-spreadsheet", mode: "spreadsheet", alias: ["excel", "formula"]},
     {name: "SQL", mime: "text/x-sql", mode: "sql", ext: ["sql"]},
+    {name: "SQLite", mime: "text/x-sqlite", mode: "sql"},
     {name: "Squirrel", mime: "text/x-squirrel", mode: "clike", ext: ["nut"]},
+    {name: "Stylus", mime: "text/x-styl", mode: "stylus", ext: ["styl"]},
     {name: "Swift", mime: "text/x-swift", mode: "swift", ext: ["swift"]},
     {name: "sTeX", mime: "text/x-stex", mode: "stex"},
-    {name: "LaTeX", mime: "text/x-latex", mode: "stex", ext: ["text", "ltx"], alias: ["tex"]},
-    {name: "SystemVerilog", mime: "text/x-systemverilog", mode: "verilog", ext: ["v"]},
+    {name: "LaTeX", mime: "text/x-latex", mode: "stex", ext: ["text", "ltx", "tex"], alias: ["tex"]},
+    {name: "SystemVerilog", mime: "text/x-systemverilog", mode: "verilog", ext: ["v", "sv", "svh"]},
     {name: "Tcl", mime: "text/x-tcl", mode: "tcl", ext: ["tcl"]},
     {name: "Textile", mime: "text/x-textile", mode: "textile", ext: ["textile"]},
     {name: "TiddlyWiki ", mime: "text/x-tiddlywiki", mode: "tiddlywiki"},
@@ -146,6 +152,7 @@
     {name: "TTCN_CFG", mime: "text/x-ttcn-cfg", mode: "ttcn-cfg", ext: ["cfg"]},
     {name: "Turtle", mime: "text/turtle", mode: "turtle", ext: ["ttl"]},
     {name: "TypeScript", mime: "application/typescript", mode: "javascript", ext: ["ts"], alias: ["ts"]},
+    {name: "TypeScript-JSX", mime: "text/typescript-jsx", mode: "jsx", ext: ["tsx"], alias: ["tsx"]},
     {name: "Twig", mime: "text/x-twig", mode: "twig"},
     {name: "Web IDL", mime: "text/x-webidl", mode: "webidl", ext: ["webidl"]},
     {name: "VB.NET", mime: "text/x-vb", mode: "vb", ext: ["vb"]},
@@ -153,10 +160,11 @@
     {name: "Velocity", mime: "text/velocity", mode: "velocity", ext: ["vtl"]},
     {name: "Verilog", mime: "text/x-verilog", mode: "verilog", ext: ["v"]},
     {name: "VHDL", mime: "text/x-vhdl", mode: "vhdl", ext: ["vhd", "vhdl"]},
-    {name: "XML", mimes: ["application/xml", "text/xml"], mode: "xml", ext: ["xml", "xsl", "xsd"], alias: ["rss", "wsdl", "xsd"]},
+    {name: "Vue.js Component", mimes: ["script/x-vue", "text/x-vue"], mode: "vue", ext: ["vue"]},
+    {name: "XML", mimes: ["application/xml", "text/xml"], mode: "xml", ext: ["xml", "xsl", "xsd", "svg"], alias: ["rss", "wsdl", "xsd"]},
     {name: "XQuery", mime: "application/xquery", mode: "xquery", ext: ["xy", "xquery"]},
     {name: "Yacas", mime: "text/x-yacas", mode: "yacas", ext: ["ys"]},
-    {name: "YAML", mime: "text/x-yaml", mode: "yaml", ext: ["yaml", "yml"], alias: ["yml"]},
+    {name: "YAML", mimes: ["text/x-yaml", "text/yaml"], mode: "yaml", ext: ["yaml", "yml"], alias: ["yml"]},
     {name: "Z80", mime: "text/x-z80", mode: "z80", ext: ["z80"]},
     {name: "mscgen", mime: "text/x-mscgen", mode: "mscgen", ext: ["mscgen", "mscin", "msc"]},
     {name: "xu", mime: "text/x-xu", mode: "mscgen", ext: ["xu"]},
@@ -176,6 +184,8 @@
       if (info.mimes) for (var j = 0; j < info.mimes.length; j++)
         if (info.mimes[j] == mime) return info;
     }
+    if (/\+xml$/.test(mime)) return CodeMirror.findModeByMIME("application/xml")
+    if (/\+json$/.test(mime)) return CodeMirror.findModeByMIME("application/json")
   };
 
   CodeMirror.findModeByExtension = function(ext) {
diff --git a/public/vendor/plugins/codemirror/mode/mirc/index.html b/public/vendor/plugins/codemirror/mode/mirc/index.html
index fd2f34e4ba..98f81750fa 100644
--- a/public/vendor/plugins/codemirror/mode/mirc/index.html
+++ b/public/vendor/plugins/codemirror/mode/mirc/index.html
@@ -7,10 +7,11 @@
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <link rel="stylesheet" href="../../theme/twilight.css">
 <script src="../../lib/codemirror.js"></script>
+<script src="../../addon/edit/matchbrackets.js"></script>
 <script src="mirc.js"></script>
 <style>.CodeMirror {border: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/mirc/mirc.js b/public/vendor/plugins/codemirror/mode/mirc/mirc.js
index f0d5c6ad50..d27b0152e9 100644
--- a/public/vendor/plugins/codemirror/mode/mirc/mirc.js
+++ b/public/vendor/plugins/codemirror/mode/mirc/mirc.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 //mIRC mode by Ford_Lawnmower :: Based on Velocity mode by Steve O'Hara
 
@@ -130,7 +130,7 @@ CodeMirror.defineMode("mirc", function() {
       }
     }
     else if (ch == "%") {
-      stream.eatWhile(/[^,^\s^\(^\)]/);
+      stream.eatWhile(/[^,\s()]/);
       state.beforeParams = true;
       return "string";
     }
diff --git a/public/vendor/plugins/codemirror/mode/mllike/index.html b/public/vendor/plugins/codemirror/mode/mllike/index.html
index 5923af8f87..491373171f 100644
--- a/public/vendor/plugins/codemirror/mode/mllike/index.html
+++ b/public/vendor/plugins/codemirror/mode/mllike/index.html
@@ -12,7 +12,7 @@
   .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
 </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -132,6 +132,25 @@ let () =
 
 (* A Hundred Lines of Caml - http://caml.inria.fr/about/taste.en.html *)
 (* OCaml page on Wikipedia - http://en.wikipedia.org/wiki/OCaml *)
+
+module type S = sig type t end
+
+let x = {| 
+  this is a long string 
+  with many lines and stuff
+  |}
+
+let b = 0b00110
+let h = 0x123abcd
+let e = 1e-10
+let i = 1.
+let x = 30_000
+let o = 0o1234
+
+[1; 2; 3] (* lists *)
+
+1 @ 2
+1. +. 2.
 </textarea>
 
 <h2>F# mode</h2>
diff --git a/public/vendor/plugins/codemirror/mode/mllike/mllike.js b/public/vendor/plugins/codemirror/mode/mllike/mllike.js
index bf0b8a674f..a1538f720d 100644
--- a/public/vendor/plugins/codemirror/mode/mllike/mllike.js
+++ b/public/vendor/plugins/codemirror/mode/mllike/mllike.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -13,31 +13,26 @@
 
 CodeMirror.defineMode('mllike', function(_config, parserConfig) {
   var words = {
-    'let': 'keyword',
-    'rec': 'keyword',
-    'in': 'keyword',
-    'of': 'keyword',
-    'and': 'keyword',
-    'if': 'keyword',
-    'then': 'keyword',
-    'else': 'keyword',
-    'for': 'keyword',
-    'to': 'keyword',
-    'while': 'keyword',
+    'as': 'keyword',
     'do': 'keyword',
-    'done': 'keyword',
+    'else': 'keyword',
+    'end': 'keyword',
+    'exception': 'keyword',
     'fun': 'keyword',
-    'function': 'keyword',
-    'val': 'keyword',
+    'functor': 'keyword',
+    'if': 'keyword',
+    'in': 'keyword',
+    'include': 'keyword',
+    'let': 'keyword',
+    'of': 'keyword',
+    'open': 'keyword',
+    'rec': 'keyword',
+    'struct': 'keyword',
+    'then': 'keyword',
     'type': 'keyword',
-    'mutable': 'keyword',
-    'match': 'keyword',
-    'with': 'keyword',
-    'try': 'keyword',
-    'open': 'builtin',
-    'ignore': 'builtin',
-    'begin': 'keyword',
-    'end': 'keyword'
+    'val': 'keyword',
+    'while': 'keyword',
+    'with': 'keyword'
   };
 
   var extraWords = parserConfig.extraWords || {};
@@ -46,6 +41,9 @@ CodeMirror.defineMode('mllike', function(_config, parserConfig) {
       words[prop] = parserConfig.extraWords[prop];
     }
   }
+  var hintWords = [];
+  for (var k in words) { hintWords.push(k); }
+  CodeMirror.registerHelper("hintWords", "mllike", hintWords);
 
   function tokenBase(stream, state) {
     var ch = stream.next();
@@ -54,6 +52,13 @@ CodeMirror.defineMode('mllike', function(_config, parserConfig) {
       state.tokenize = tokenString;
       return state.tokenize(stream, state);
     }
+    if (ch === '{') {
+      if (stream.eat('|')) {
+        state.longString = true;
+        state.tokenize = tokenLongString;
+        return state.tokenize(stream, state);
+      }
+    }
     if (ch === '(') {
       if (stream.eat('*')) {
         state.commentLevel++;
@@ -61,7 +66,7 @@ CodeMirror.defineMode('mllike', function(_config, parserConfig) {
         return state.tokenize(stream, state);
       }
     }
-    if (ch === '~') {
+    if (ch === '~' || ch === '?') {
       stream.eatWhile(/\w/);
       return 'variable-2';
     }
@@ -74,18 +79,32 @@ CodeMirror.defineMode('mllike', function(_config, parserConfig) {
       return 'comment';
     }
     if (/\d/.test(ch)) {
-      stream.eatWhile(/[\d]/);
-      if (stream.eat('.')) {
-        stream.eatWhile(/[\d]/);
+      if (ch === '0' && stream.eat(/[bB]/)) {
+        stream.eatWhile(/[01]/);
+      } if (ch === '0' && stream.eat(/[xX]/)) {
+        stream.eatWhile(/[0-9a-fA-F]/)
+      } if (ch === '0' && stream.eat(/[oO]/)) {
+        stream.eatWhile(/[0-7]/);
+      } else {
+        stream.eatWhile(/[\d_]/);
+        if (stream.eat('.')) {
+          stream.eatWhile(/[\d]/);
+        }
+        if (stream.eat(/[eE]/)) {
+          stream.eatWhile(/[\d\-+]/);
+        }
       }
       return 'number';
     }
-    if ( /[+\-*&%=<>!?|]/.test(ch)) {
+    if ( /[+\-*&%=<>!?|@\.~:]/.test(ch)) {
       return 'operator';
     }
-    stream.eatWhile(/\w/);
-    var cur = stream.current();
-    return words.hasOwnProperty(cur) ? words[cur] : 'variable';
+    if (/[\w\xa1-\uffff]/.test(ch)) {
+      stream.eatWhile(/[\w\xa1-\uffff]/);
+      var cur = stream.current();
+      return words.hasOwnProperty(cur) ? words[cur] : 'variable';
+    }
+    return null
   }
 
   function tokenString(stream, state) {
@@ -116,8 +135,20 @@ CodeMirror.defineMode('mllike', function(_config, parserConfig) {
     return 'comment';
   }
 
+  function tokenLongString(stream, state) {
+    var prev, next;
+    while (state.longString && (next = stream.next()) != null) {
+      if (prev === '|' && next === '}') state.longString = false;
+      prev = next;
+    }
+    if (!state.longString) {
+      state.tokenize = tokenBase;
+    }
+    return 'string';
+  }
+
   return {
-    startState: function() {return {tokenize: tokenBase, commentLevel: 0};},
+    startState: function() {return {tokenize: tokenBase, commentLevel: 0, longString: false};},
     token: function(stream, state) {
       if (stream.eatSpace()) return null;
       return state.tokenize(stream, state);
@@ -132,14 +163,64 @@ CodeMirror.defineMode('mllike', function(_config, parserConfig) {
 CodeMirror.defineMIME('text/x-ocaml', {
   name: 'mllike',
   extraWords: {
-    'succ': 'keyword',
+    'and': 'keyword',
+    'assert': 'keyword',
+    'begin': 'keyword',
+    'class': 'keyword',
+    'constraint': 'keyword',
+    'done': 'keyword',
+    'downto': 'keyword',
+    'external': 'keyword',
+    'function': 'keyword',
+    'initializer': 'keyword',
+    'lazy': 'keyword',
+    'match': 'keyword',
+    'method': 'keyword',
+    'module': 'keyword',
+    'mutable': 'keyword',
+    'new': 'keyword',
+    'nonrec': 'keyword',
+    'object': 'keyword',
+    'private': 'keyword',
+    'sig': 'keyword',
+    'to': 'keyword',
+    'try': 'keyword',
+    'value': 'keyword',
+    'virtual': 'keyword',
+    'when': 'keyword',
+
+    // builtins
+    'raise': 'builtin',
+    'failwith': 'builtin',
+    'true': 'builtin',
+    'false': 'builtin',
+
+    // Pervasives builtins
+    'asr': 'builtin',
+    'land': 'builtin',
+    'lor': 'builtin',
+    'lsl': 'builtin',
+    'lsr': 'builtin',
+    'lxor': 'builtin',
+    'mod': 'builtin',
+    'or': 'builtin',
+
+    // More Pervasives
+    'raise_notrace': 'builtin',
     'trace': 'builtin',
     'exit': 'builtin',
     'print_string': 'builtin',
     'print_endline': 'builtin',
-    'true': 'atom',
-    'false': 'atom',
-    'raise': 'keyword'
+
+     'int': 'type',
+     'float': 'type',
+     'bool': 'type',
+     'char': 'type',
+     'string': 'type',
+     'unit': 'type',
+
+     // Modules
+     'List': 'builtin'
   }
 });
 
@@ -147,18 +228,21 @@ CodeMirror.defineMIME('text/x-fsharp', {
   name: 'mllike',
   extraWords: {
     'abstract': 'keyword',
-    'as': 'keyword',
     'assert': 'keyword',
     'base': 'keyword',
+    'begin': 'keyword',
     'class': 'keyword',
     'default': 'keyword',
     'delegate': 'keyword',
+    'do!': 'keyword',
+    'done': 'keyword',
     'downcast': 'keyword',
     'downto': 'keyword',
     'elif': 'keyword',
-    'exception': 'keyword',
     'extern': 'keyword',
     'finally': 'keyword',
+    'for': 'keyword',
+    'function': 'keyword',
     'global': 'keyword',
     'inherit': 'keyword',
     'inline': 'keyword',
@@ -166,38 +250,108 @@ CodeMirror.defineMIME('text/x-fsharp', {
     'internal': 'keyword',
     'lazy': 'keyword',
     'let!': 'keyword',
-    'member' : 'keyword',
+    'match': 'keyword',
+    'member': 'keyword',
     'module': 'keyword',
+    'mutable': 'keyword',
     'namespace': 'keyword',
     'new': 'keyword',
     'null': 'keyword',
     'override': 'keyword',
     'private': 'keyword',
     'public': 'keyword',
-    'return': 'keyword',
     'return!': 'keyword',
+    'return': 'keyword',
     'select': 'keyword',
     'static': 'keyword',
-    'struct': 'keyword',
+    'to': 'keyword',
+    'try': 'keyword',
     'upcast': 'keyword',
-    'use': 'keyword',
     'use!': 'keyword',
-    'val': 'keyword',
+    'use': 'keyword',
+    'void': 'keyword',
     'when': 'keyword',
-    'yield': 'keyword',
     'yield!': 'keyword',
+    'yield': 'keyword',
 
+    // Reserved words
+    'atomic': 'keyword',
+    'break': 'keyword',
+    'checked': 'keyword',
+    'component': 'keyword',
+    'const': 'keyword',
+    'constraint': 'keyword',
+    'constructor': 'keyword',
+    'continue': 'keyword',
+    'eager': 'keyword',
+    'event': 'keyword',
+    'external': 'keyword',
+    'fixed': 'keyword',
+    'method': 'keyword',
+    'mixin': 'keyword',
+    'object': 'keyword',
+    'parallel': 'keyword',
+    'process': 'keyword',
+    'protected': 'keyword',
+    'pure': 'keyword',
+    'sealed': 'keyword',
+    'tailcall': 'keyword',
+    'trait': 'keyword',
+    'virtual': 'keyword',
+    'volatile': 'keyword',
+
+    // builtins
     'List': 'builtin',
     'Seq': 'builtin',
     'Map': 'builtin',
     'Set': 'builtin',
+    'Option': 'builtin',
     'int': 'builtin',
     'string': 'builtin',
-    'raise': 'builtin',
-    'failwith': 'builtin',
     'not': 'builtin',
     'true': 'builtin',
-    'false': 'builtin'
+    'false': 'builtin',
+
+    'raise': 'builtin',
+    'failwith': 'builtin'
+  },
+  slashComments: true
+});
+
+
+CodeMirror.defineMIME('text/x-sml', {
+  name: 'mllike',
+  extraWords: {
+    'abstype': 'keyword',
+    'and': 'keyword',
+    'andalso': 'keyword',
+    'case': 'keyword',
+    'datatype': 'keyword',
+    'fn': 'keyword',
+    'handle': 'keyword',
+    'infix': 'keyword',
+    'infixr': 'keyword',
+    'local': 'keyword',
+    'nonfix': 'keyword',
+    'op': 'keyword',
+    'orelse': 'keyword',
+    'raise': 'keyword',
+    'withtype': 'keyword',
+    'eqtype': 'keyword',
+    'sharing': 'keyword',
+    'sig': 'keyword',
+    'signature': 'keyword',
+    'structure': 'keyword',
+    'where': 'keyword',
+    'true': 'keyword',
+    'false': 'keyword',
+
+    // types
+    'int': 'builtin',
+    'real': 'builtin',
+    'string': 'builtin',
+    'char': 'builtin',
+    'bool': 'builtin'
   },
   slashComments: true
 });
diff --git a/public/vendor/plugins/codemirror/mode/modelica/index.html b/public/vendor/plugins/codemirror/mode/modelica/index.html
index 408c3b17e3..ed344e712f 100644
--- a/public/vendor/plugins/codemirror/mode/modelica/index.html
+++ b/public/vendor/plugins/codemirror/mode/modelica/index.html
@@ -12,7 +12,7 @@
 <script src="modelica.js"></script>
 <style>.CodeMirror {border: 2px inset #dee;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/modelica/modelica.js b/public/vendor/plugins/codemirror/mode/modelica/modelica.js
index 77ec7a3c18..a83a4135d0 100644
--- a/public/vendor/plugins/codemirror/mode/modelica/modelica.js
+++ b/public/vendor/plugins/codemirror/mode/modelica/modelica.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 // Modelica support for CodeMirror, copyright (c) by Lennart Ochel
 
diff --git a/public/vendor/plugins/codemirror/mode/mscgen/index.html b/public/vendor/plugins/codemirror/mode/mscgen/index.html
index 8c28ee6200..a35ffd4320 100644
--- a/public/vendor/plugins/codemirror/mode/mscgen/index.html
+++ b/public/vendor/plugins/codemirror/mode/mscgen/index.html
@@ -9,7 +9,7 @@
 <script src="mscgen.js"></script>
 <style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
   <ul>
     <li><a href="../../index.html">Home</a>
     <li><a href="../../doc/manual.html">Manual</a>
@@ -59,7 +59,7 @@ msc {
 # Xù - expansions to MscGen to support inline expressions
 #      https://github.com/sverweij/mscgen_js/blob/master/wikum/xu.md
 # More samples: https://sverweij.github.io/mscgen_js
-msc {
+xu {
   hscale="0.8",
   width="700";
 
diff --git a/public/vendor/plugins/codemirror/mode/mscgen/mscgen.js b/public/vendor/plugins/codemirror/mode/mscgen/mscgen.js
index d61b470652..6f4f9cd8f0 100644
--- a/public/vendor/plugins/codemirror/mode/mscgen/mscgen.js
+++ b/public/vendor/plugins/codemirror/mode/mscgen/mscgen.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 // mode(s) for the sequence chart dsl's mscgen, xù and msgenny
 // For more information on mscgen, see the site of the original author:
@@ -23,6 +23,7 @@
     mscgen: {
       "keywords" : ["msc"],
       "options" : ["hscale", "width", "arcgradient", "wordwraparcs"],
+      "constants" : ["true", "false", "on", "off"],
       "attributes" : ["label", "idurl", "id", "url", "linecolor", "linecolour", "textcolor", "textcolour", "textbgcolor", "textbgcolour", "arclinecolor", "arclinecolour", "arctextcolor", "arctextcolour", "arctextbgcolor", "arctextbgcolour", "arcskip"],
       "brackets" : ["\\{", "\\}"], // [ and  ] are brackets too, but these get handled in with lists
       "arcsWords" : ["note", "abox", "rbox", "box"],
@@ -31,9 +32,10 @@
       "operators" : ["="]
     },
     xu: {
-      "keywords" : ["msc"],
-      "options" : ["hscale", "width", "arcgradient", "wordwraparcs", "watermark"],
-      "attributes" : ["label", "idurl", "id", "url", "linecolor", "linecolour", "textcolor", "textcolour", "textbgcolor", "textbgcolour", "arclinecolor", "arclinecolour", "arctextcolor", "arctextcolour", "arctextbgcolor", "arctextbgcolour", "arcskip"],
+      "keywords" : ["msc", "xu"],
+      "options" : ["hscale", "width", "arcgradient", "wordwraparcs", "wordwrapentities", "watermark"],
+      "constants" : ["true", "false", "on", "off", "auto"],
+      "attributes" : ["label", "idurl", "id", "url", "linecolor", "linecolour", "textcolor", "textcolour", "textbgcolor", "textbgcolour", "arclinecolor", "arclinecolour", "arctextcolor", "arctextcolour", "arctextbgcolor", "arctextbgcolour", "arcskip", "title", "deactivate", "activate", "activation"],
       "brackets" : ["\\{", "\\}"],  // [ and  ] are brackets too, but these get handled in with lists
       "arcsWords" : ["note", "abox", "rbox", "box", "alt", "else", "opt", "break", "par", "seq", "strict", "neg", "critical", "ignore", "consider", "assert", "loop", "ref", "exc"],
       "arcsOthers" : ["\\|\\|\\|", "\\.\\.\\.", "---", "--", "<->", "==", "<<=>>", "<=>", "\\.\\.", "<<>>", "::", "<:>", "->", "=>>", "=>", ">>", ":>", "<-", "<<=", "<=", "<<", "<:", "x-", "-x"],
@@ -42,7 +44,8 @@
     },
     msgenny: {
       "keywords" : null,
-      "options" : ["hscale", "width", "arcgradient", "wordwraparcs", "watermark"],
+      "options" : ["hscale", "width", "arcgradient", "wordwraparcs", "wordwrapentities", "watermark"],
+      "constants" : ["true", "false", "on", "off", "auto"],
       "attributes" : null,
       "brackets" : ["\\{", "\\}"],
       "arcsWords" : ["note", "abox", "rbox", "box", "alt", "else", "opt", "break", "par", "seq", "strict", "neg", "critical", "ignore", "consider", "assert", "loop", "ref", "exc"],
@@ -146,6 +149,9 @@
       if (!!pConfig.operators && pStream.match(wordRegexp(pConfig.operators), true, true))
         return "operator";
 
+      if (!!pConfig.constants && pStream.match(wordRegexp(pConfig.constants), true, true))
+        return "variable";
+
       /* attribute lists */
       if (!pConfig.inAttributeList && !!pConfig.attributes && pStream.match(/\[/, true, true)) {
         pConfig.inAttributeList = true;
diff --git a/public/vendor/plugins/codemirror/mode/mscgen/mscgen_test.js b/public/vendor/plugins/codemirror/mode/mscgen/mscgen_test.js
index e319a3997e..ff3816d853 100644
--- a/public/vendor/plugins/codemirror/mode/mscgen/mscgen_test.js
+++ b/public/vendor/plugins/codemirror/mode/mscgen/mscgen_test.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function() {
   var mode = CodeMirror.getMode({indentUnit: 2}, "mscgen");
@@ -26,9 +26,18 @@
 
   MT("xù/ msgenny keywords classify as 'base'",
     "[base watermark]",
+    "[base wordwrapentities]",
     "[base alt loop opt ref else break par seq assert]"
   );
 
+  MT("xù/ msgenny constants classify as 'base'",
+    "[base auto]"
+  );
+
+  MT("mscgen constants classify as 'variable'",
+    "[variable true]", "[variable false]", "[variable on]", "[variable off]"
+  );
+
   MT("mscgen options classify as keyword",
     "[keyword hscale]", "[keyword width]", "[keyword arcgradient]", "[keyword wordwraparcs]"
   );
@@ -63,7 +72,7 @@
   MT("a typical program",
     "[comment # typical mscgen program]",
     "[keyword msc][base  ][bracket {]",
-    "[keyword wordwraparcs][operator =][string \"true\"][base , ][keyword hscale][operator =][string \"0.8\"][keyword arcgradient][operator =][base 30;]",
+    "[keyword wordwraparcs][operator =][variable true][base , ][keyword hscale][operator =][string \"0.8\"][base , ][keyword arcgradient][operator =][base 30;]",
     "[base   a][bracket [[][attribute label][operator =][string \"Entity A\"][bracket ]]][base ,]",
     "[base   b][bracket [[][attribute label][operator =][string \"Entity B\"][bracket ]]][base ,]",
     "[base   c][bracket [[][attribute label][operator =][string \"Entity C\"][bracket ]]][base ;]",
diff --git a/public/vendor/plugins/codemirror/mode/mscgen/msgenny_test.js b/public/vendor/plugins/codemirror/mode/mscgen/msgenny_test.js
index 80173de082..a3ed577dd2 100644
--- a/public/vendor/plugins/codemirror/mode/mscgen/msgenny_test.js
+++ b/public/vendor/plugins/codemirror/mode/mscgen/msgenny_test.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function() {
   var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-msgenny");
@@ -20,9 +20,15 @@
 
   MT("xù/ msgenny keywords classify as 'keyword'",
     "[keyword watermark]",
+    "[keyword wordwrapentities]",
     "[keyword alt]","[keyword loop]","[keyword opt]","[keyword ref]","[keyword else]","[keyword break]","[keyword par]","[keyword seq]","[keyword assert]"
   );
 
+  MT("xù/ msgenny constants classify as 'variable'",
+    "[variable auto]",
+    "[variable true]", "[variable false]", "[variable on]", "[variable off]"
+  );
+
   MT("mscgen options classify as keyword",
     "[keyword hscale]", "[keyword width]", "[keyword arcgradient]", "[keyword wordwraparcs]"
   );
@@ -56,7 +62,7 @@
 
   MT("a typical program",
     "[comment # typical msgenny program]",
-    "[keyword wordwraparcs][operator =][string \"true\"][base , ][keyword hscale][operator =][string \"0.8\"][base , ][keyword arcgradient][operator =][base 30;]",
+    "[keyword wordwraparcs][operator =][variable true][base , ][keyword hscale][operator =][string \"0.8\"][base , ][keyword arcgradient][operator =][base 30;]",
     "[base   a : ][string \"Entity A\"][base ,]",
     "[base   b : Entity B,]",
     "[base   c : Entity C;]",
diff --git a/public/vendor/plugins/codemirror/mode/mscgen/xu_test.js b/public/vendor/plugins/codemirror/mode/mscgen/xu_test.js
index f9a50f0af2..d65a058712 100644
--- a/public/vendor/plugins/codemirror/mode/mscgen/xu_test.js
+++ b/public/vendor/plugins/codemirror/mode/mscgen/xu_test.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function() {
   var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-xu");
@@ -9,7 +9,13 @@
      "[keyword msc][bracket {]",
      "[base   ]",
      "[bracket }]"
-   );
+  );
+
+  MT("empty chart",
+     "[keyword xu][bracket {]",
+     "[base   ]",
+     "[bracket }]"
+  );
 
   MT("comments",
     "[comment // a single line comment]",
@@ -29,6 +35,11 @@
     "[keyword alt]","[keyword loop]","[keyword opt]","[keyword ref]","[keyword else]","[keyword break]","[keyword par]","[keyword seq]","[keyword assert]"
   );
 
+  MT("xù/ msgenny constants classify as 'variable'",
+    "[variable auto]",
+    "[variable true]", "[variable false]", "[variable on]", "[variable off]"
+  );
+
   MT("mscgen options classify as keyword",
     "[keyword hscale]", "[keyword width]", "[keyword arcgradient]", "[keyword wordwraparcs]"
   );
@@ -49,7 +60,8 @@
     "[attribute id]","[attribute url]","[attribute idurl]",
     "[attribute linecolor]","[attribute linecolour]","[attribute textcolor]","[attribute textcolour]","[attribute textbgcolor]","[attribute textbgcolour]",
     "[attribute arclinecolor]","[attribute arclinecolour]","[attribute arctextcolor]","[attribute arctextcolour]","[attribute arctextbgcolor]","[attribute arctextbgcolour]",
-    "[attribute arcskip][bracket ]]]"
+    "[attribute arcskip]","[attribute title]",
+    "[attribute activate]","[attribute deactivate]","[attribute activation][bracket ]]]"
   );
 
   MT("outside an attribute list, attributes classify as base",
@@ -57,18 +69,18 @@
     "[base id]","[base url]","[base idurl]",
     "[base linecolor]","[base linecolour]","[base textcolor]","[base textcolour]","[base textbgcolor]","[base textbgcolour]",
     "[base arclinecolor]","[base arclinecolour]","[base arctextcolor]","[base arctextcolour]","[base arctextbgcolor]","[base arctextbgcolour]",
-    "[base arcskip]"
+    "[base arcskip]", "[base title]"
   );
 
   MT("a typical program",
-    "[comment # typical mscgen program]",
-    "[keyword msc][base  ][bracket {]",
-    "[keyword wordwraparcs][operator =][string \"true\"][keyword hscale][operator =][string \"0.8\"][keyword arcgradient][operator =][base 30;]",
+    "[comment # typical xu program]",
+    "[keyword xu][base  ][bracket {]",
+    "[keyword wordwraparcs][operator =][string \"true\"][base , ][keyword hscale][operator =][string \"0.8\"][base , ][keyword arcgradient][operator =][base 30, ][keyword width][operator =][variable auto][base ;]",
     "[base   a][bracket [[][attribute label][operator =][string \"Entity A\"][bracket ]]][base ,]",
     "[base   b][bracket [[][attribute label][operator =][string \"Entity B\"][bracket ]]][base ,]",
     "[base   c][bracket [[][attribute label][operator =][string \"Entity C\"][bracket ]]][base ;]",
     "[base   a ][keyword =>>][base  b][bracket [[][attribute label][operator =][string \"Hello entity B\"][bracket ]]][base ;]",
-    "[base   a ][keyword <<][base  b][bracket [[][attribute label][operator =][string \"Here's an answer dude!\"][bracket ]]][base ;]",
+    "[base   a ][keyword <<][base  b][bracket [[][attribute label][operator =][string \"Here's an answer dude!\"][base , ][attribute title][operator =][string \"This is a title for this message\"][bracket ]]][base ;]",
     "[base   c ][keyword :>][base  *][bracket [[][attribute label][operator =][string \"What about me?\"][base , ][attribute textcolor][operator =][base red][bracket ]]][base ;]",
     "[bracket }]"
   );
diff --git a/public/vendor/plugins/codemirror/mode/mumps/index.html b/public/vendor/plugins/codemirror/mode/mumps/index.html
index b1f92c213f..9340867eef 100644
--- a/public/vendor/plugins/codemirror/mode/mumps/index.html
+++ b/public/vendor/plugins/codemirror/mode/mumps/index.html
@@ -7,9 +7,9 @@
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
 <script src="mumps.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/mumps/mumps.js b/public/vendor/plugins/codemirror/mode/mumps/mumps.js
index 469f8c3d1c..3671c9cb36 100644
--- a/public/vendor/plugins/codemirror/mode/mumps/mumps.js
+++ b/public/vendor/plugins/codemirror/mode/mumps/mumps.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 /*
   This MUMPS Language script was constructed using vbscript.js as a template.
diff --git a/public/vendor/plugins/codemirror/mode/nginx/index.html b/public/vendor/plugins/codemirror/mode/nginx/index.html
index 03cf671498..7d4ad7e630 100644
--- a/public/vendor/plugins/codemirror/mode/nginx/index.html
+++ b/public/vendor/plugins/codemirror/mode/nginx/index.html
@@ -1,4 +1,4 @@
-<!doctype html>
+<!doctype html>
 <head>
 <title>CodeMirror: NGINX mode</title>
 <meta charset="utf-8"/>
@@ -21,7 +21,7 @@
     }
   </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -176,6 +176,6 @@ server {
       var editor = CodeMirror.fromTextArea(document.getElementById("code"), {});
     </script>
 
-    <p><strong>MIME types defined:</strong> <code>text/nginx</code>.</p>
+    <p><strong>MIME types defined:</strong> <code>text/x-nginx-conf</code>.</p>
 
   </article>
diff --git a/public/vendor/plugins/codemirror/mode/nginx/nginx.js b/public/vendor/plugins/codemirror/mode/nginx/nginx.js
index 00a3224922..a09f1501fa 100644
--- a/public/vendor/plugins/codemirror/mode/nginx/nginx.js
+++ b/public/vendor/plugins/codemirror/mode/nginx/nginx.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/nsis/index.html b/public/vendor/plugins/codemirror/mode/nsis/index.html
index 2afae87f08..4bd73650af 100644
--- a/public/vendor/plugins/codemirror/mode/nsis/index.html
+++ b/public/vendor/plugins/codemirror/mode/nsis/index.html
@@ -13,7 +13,7 @@
   .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
 </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/nsis/nsis.js b/public/vendor/plugins/codemirror/mode/nsis/nsis.js
index 172207c5a1..94b8eae24d 100644
--- a/public/vendor/plugins/codemirror/mode/nsis/nsis.js
+++ b/public/vendor/plugins/codemirror/mode/nsis/nsis.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 // Author: Jan T. Sott (http://github.com/idleberg)
 
@@ -24,20 +24,20 @@ CodeMirror.defineSimpleMode("nsis",{
     { regex: /`(?:[^\\`]|\\.)*`?/, token: "string" },
 
     // Compile Time Commands
-    {regex: /(?:\!(include|addincludedir|addplugindir|appendfile|cd|delfile|echo|error|execute|packhdr|finalize|getdllversion|system|tempfile|warning|verbose|define|undef|insertmacro|makensis|searchparse|searchreplace))\b/, token: "keyword"},
+    {regex: /^\s*(?:\!(include|addincludedir|addplugindir|appendfile|cd|delfile|echo|error|execute|packhdr|pragma|finalize|getdllversion|gettlbversion|system|tempfile|warning|verbose|define|undef|insertmacro|macro|macroend|makensis|searchparse|searchreplace))\b/, token: "keyword"},
 
     // Conditional Compilation
-    {regex: /(?:\!(if(?:n?def)?|ifmacron?def|macro))\b/, token: "keyword", indent: true},
-    {regex: /(?:\!(else|endif|macroend))\b/, token: "keyword", dedent: true},
+    {regex: /^\s*(?:\!(if(?:n?def)?|ifmacron?def|macro))\b/, token: "keyword", indent: true},
+    {regex: /^\s*(?:\!(else|endif|macroend))\b/, token: "keyword", dedent: true},
 
     // Runtime Commands
-    {regex: /\b(?:Abort|AddBrandingImage|AddSize|AllowRootDirInstall|AllowSkipFiles|AutoCloseWindow|BGFont|BGGradient|BrandingText|BringToFront|Call|CallInstDLL|Caption|ChangeUI|CheckBitmap|ClearErrors|CompletedText|ComponentText|CopyFiles|CRCCheck|CreateDirectory|CreateFont|CreateShortCut|Delete|DeleteINISec|DeleteINIStr|DeleteRegKey|DeleteRegValue|DetailPrint|DetailsButtonText|DirText|DirVar|DirVerify|EnableWindow|EnumRegKey|EnumRegValue|Exch|Exec|ExecShell|ExecWait|ExpandEnvStrings|File|FileBufSize|FileClose|FileErrorText|FileOpen|FileRead|FileReadByte|FileReadUTF16LE|FileReadWord|FileWriteUTF16LE|FileSeek|FileWrite|FileWriteByte|FileWriteWord|FindClose|FindFirst|FindNext|FindWindow|FlushINI|GetCurInstType|GetCurrentAddress|GetDlgItem|GetDLLVersion|GetDLLVersionLocal|GetErrorLevel|GetFileTime|GetFileTimeLocal|GetFullPathName|GetFunctionAddress|GetInstDirError|GetLabelAddress|GetTempFileName|Goto|HideWindow|Icon|IfAbort|IfErrors|IfFileExists|IfRebootFlag|IfSilent|InitPluginsDir|InstallButtonText|InstallColors|InstallDir|InstallDirRegKey|InstProgressFlags|InstType|InstTypeGetText|InstTypeSetText|IntCmp|IntCmpU|IntFmt|IntOp|IsWindow|LangString|LicenseBkColor|LicenseData|LicenseForceSelection|LicenseLangString|LicenseText|LoadLanguageFile|LockWindow|LogSet|LogText|ManifestDPIAware|ManifestSupportedOS|MessageBox|MiscButtonText|Name|Nop|OutFile|Page|PageCallbacks|Pop|Push|Quit|ReadEnvStr|ReadINIStr|ReadRegDWORD|ReadRegStr|Reboot|RegDLL|Rename|RequestExecutionLevel|ReserveFile|Return|RMDir|SearchPath|SectionGetFlags|SectionGetInstTypes|SectionGetSize|SectionGetText|SectionIn|SectionSetFlags|SectionSetInstTypes|SectionSetSize|SectionSetText|SendMessage|SetAutoClose|SetBrandingImage|SetCompress|SetCompressor|SetCompressorDictSize|SetCtlColors|SetCurInstType|SetDatablockOptimize|SetDateSave|SetDetailsPrint|SetDetailsView|SetErrorLevel|SetErrors|SetFileAttributes|SetFont|SetOutPath|SetOverwrite|SetPluginUnload|SetRebootFlag|SetRegView|SetShellVarContext|SetSilent|ShowInstDetails|ShowUninstDetails|ShowWindow|SilentInstall|SilentUnInstall|Sleep|SpaceTexts|StrCmp|StrCmpS|StrCpy|StrLen|SubCaption|Unicode|UninstallButtonText|UninstallCaption|UninstallIcon|UninstallSubCaption|UninstallText|UninstPage|UnRegDLL|Var|VIAddVersionKey|VIFileVersion|VIProductVersion|WindowIcon|WriteINIStr|WriteRegBin|WriteRegDWORD|WriteRegExpandStr|WriteRegStr|WriteUninstaller|XPStyle)\b/, token: "keyword"},
-    {regex: /\b(?:Function|PageEx|Section(?:Group)?)\b/, token: "keyword", indent: true},
-    {regex: /\b(?:(Function|PageEx|Section(?:Group)?)End)\b/, token: "keyword", dedent: true},
+    {regex: /^\s*(?:Abort|AddBrandingImage|AddSize|AllowRootDirInstall|AllowSkipFiles|AutoCloseWindow|BGFont|BGGradient|BrandingText|BringToFront|Call|CallInstDLL|Caption|ChangeUI|CheckBitmap|ClearErrors|CompletedText|ComponentText|CopyFiles|CRCCheck|CreateDirectory|CreateFont|CreateShortCut|Delete|DeleteINISec|DeleteINIStr|DeleteRegKey|DeleteRegValue|DetailPrint|DetailsButtonText|DirText|DirVar|DirVerify|EnableWindow|EnumRegKey|EnumRegValue|Exch|Exec|ExecShell|ExecShellWait|ExecWait|ExpandEnvStrings|File|FileBufSize|FileClose|FileErrorText|FileOpen|FileRead|FileReadByte|FileReadUTF16LE|FileReadWord|FileWriteUTF16LE|FileSeek|FileWrite|FileWriteByte|FileWriteWord|FindClose|FindFirst|FindNext|FindWindow|FlushINI|GetCurInstType|GetCurrentAddress|GetDlgItem|GetDLLVersion|GetDLLVersionLocal|GetErrorLevel|GetFileTime|GetFileTimeLocal|GetFullPathName|GetFunctionAddress|GetInstDirError|GetLabelAddress|GetTempFileName|Goto|HideWindow|Icon|IfAbort|IfErrors|IfFileExists|IfRebootFlag|IfSilent|InitPluginsDir|InstallButtonText|InstallColors|InstallDir|InstallDirRegKey|InstProgressFlags|InstType|InstTypeGetText|InstTypeSetText|Int64Cmp|Int64CmpU|Int64Fmt|IntCmp|IntCmpU|IntFmt|IntOp|IntPtrCmp|IntPtrCmpU|IntPtrOp|IsWindow|LangString|LicenseBkColor|LicenseData|LicenseForceSelection|LicenseLangString|LicenseText|LoadLanguageFile|LockWindow|LogSet|LogText|ManifestDPIAware|ManifestSupportedOS|MessageBox|MiscButtonText|Name|Nop|OutFile|Page|PageCallbacks|PEDllCharacteristics|PESubsysVer|Pop|Push|Quit|ReadEnvStr|ReadINIStr|ReadRegDWORD|ReadRegStr|Reboot|RegDLL|Rename|RequestExecutionLevel|ReserveFile|Return|RMDir|SearchPath|SectionGetFlags|SectionGetInstTypes|SectionGetSize|SectionGetText|SectionIn|SectionSetFlags|SectionSetInstTypes|SectionSetSize|SectionSetText|SendMessage|SetAutoClose|SetBrandingImage|SetCompress|SetCompressor|SetCompressorDictSize|SetCtlColors|SetCurInstType|SetDatablockOptimize|SetDateSave|SetDetailsPrint|SetDetailsView|SetErrorLevel|SetErrors|SetFileAttributes|SetFont|SetOutPath|SetOverwrite|SetRebootFlag|SetRegView|SetShellVarContext|SetSilent|ShowInstDetails|ShowUninstDetails|ShowWindow|SilentInstall|SilentUnInstall|Sleep|SpaceTexts|StrCmp|StrCmpS|StrCpy|StrLen|SubCaption|Unicode|UninstallButtonText|UninstallCaption|UninstallIcon|UninstallSubCaption|UninstallText|UninstPage|UnRegDLL|Var|VIAddVersionKey|VIFileVersion|VIProductVersion|WindowIcon|WriteINIStr|WriteRegBin|WriteRegDWORD|WriteRegExpandStr|WriteRegMultiStr|WriteRegNone|WriteRegStr|WriteUninstaller|XPStyle)\b/, token: "keyword"},
+    {regex: /^\s*(?:Function|PageEx|Section(?:Group)?)\b/, token: "keyword", indent: true},
+    {regex: /^\s*(?:(Function|PageEx|Section(?:Group)?)End)\b/, token: "keyword", dedent: true},
 
     // Command Options
-    {regex: /\b(?:ARCHIVE|FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_OFFLINE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_TEMPORARY|HIDDEN|HKCC|HKCR|HKCU|HKDD|HKEY_CLASSES_ROOT|HKEY_CURRENT_CONFIG|HKEY_CURRENT_USER|HKEY_DYN_DATA|HKEY_LOCAL_MACHINE|HKEY_PERFORMANCE_DATA|HKEY_USERS|HKLM|HKPD|HKU|IDABORT|IDCANCEL|IDD_DIR|IDD_INST|IDD_INSTFILES|IDD_LICENSE|IDD_SELCOM|IDD_UNINST|IDD_VERIFY|IDIGNORE|IDNO|IDOK|IDRETRY|IDYES|MB_ABORTRETRYIGNORE|MB_DEFBUTTON1|MB_DEFBUTTON2|MB_DEFBUTTON3|MB_DEFBUTTON4|MB_ICONEXCLAMATION|MB_ICONINFORMATION|MB_ICONQUESTION|MB_ICONSTOP|MB_OK|MB_OKCANCEL|MB_RETRYCANCEL|MB_RIGHT|MB_RTLREADING|MB_SETFOREGROUND|MB_TOPMOST|MB_USERICON|MB_YESNO|MB_YESNOCANCEL|NORMAL|OFFLINE|READONLY|SHCTX|SHELL_CONTEXT|SW_HIDE|SW_SHOWDEFAULT|SW_SHOWMAXIMIZED|SW_SHOWMINIMIZED|SW_SHOWNORMAL|SYSTEM|TEMPORARY)\b/, token: "atom"},
-    {regex: /\b(?:admin|all|auto|both|bottom|bzip2|components|current|custom|directory|force|hide|highest|ifdiff|ifnewer|instfiles|lastused|leave|left|license|listonly|lzma|nevershow|none|normal|notset|right|show|silent|silentlog|textonly|top|try|un\.components|un\.custom|un\.directory|un\.instfiles|un\.license|uninstConfirm|user|Win10|Win7|Win8|WinVista|zlib)\b/, token: "builtin"},
+    {regex: /\b(?:ARCHIVE|FILE_ATTRIBUTE_ARCHIVE|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_NORMAL|FILE_ATTRIBUTE_OFFLINE|FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_TEMPORARY|HIDDEN|HKCC|HKCR(32|64)?|HKCU(32|64)?|HKDD|HKEY_CLASSES_ROOT|HKEY_CURRENT_CONFIG|HKEY_CURRENT_USER|HKEY_DYN_DATA|HKEY_LOCAL_MACHINE|HKEY_PERFORMANCE_DATA|HKEY_USERS|HKLM(32|64)?|HKPD|HKU|IDABORT|IDCANCEL|IDD_DIR|IDD_INST|IDD_INSTFILES|IDD_LICENSE|IDD_SELCOM|IDD_UNINST|IDD_VERIFY|IDIGNORE|IDNO|IDOK|IDRETRY|IDYES|MB_ABORTRETRYIGNORE|MB_DEFBUTTON1|MB_DEFBUTTON2|MB_DEFBUTTON3|MB_DEFBUTTON4|MB_ICONEXCLAMATION|MB_ICONINFORMATION|MB_ICONQUESTION|MB_ICONSTOP|MB_OK|MB_OKCANCEL|MB_RETRYCANCEL|MB_RIGHT|MB_RTLREADING|MB_SETFOREGROUND|MB_TOPMOST|MB_USERICON|MB_YESNO|MB_YESNOCANCEL|NORMAL|OFFLINE|READONLY|SHCTX|SHELL_CONTEXT|SW_HIDE|SW_SHOWDEFAULT|SW_SHOWMAXIMIZED|SW_SHOWMINIMIZED|SW_SHOWNORMAL|SYSTEM|TEMPORARY)\b/, token: "atom"},
+    {regex: /\b(?:admin|all|auto|both|bottom|bzip2|components|current|custom|directory|false|force|hide|highest|ifdiff|ifnewer|instfiles|lastused|leave|left|license|listonly|lzma|nevershow|none|normal|notset|off|on|right|show|silent|silentlog|textonly|top|true|try|un\.components|un\.custom|un\.directory|un\.instfiles|un\.license|uninstConfirm|user|Win10|Win7|Win8|WinVista|zlib)\b/, token: "builtin"},
 
     // LogicLib.nsh
     {regex: /\$\{(?:And(?:If(?:Not)?|Unless)|Break|Case(?:Else)?|Continue|Default|Do(?:Until|While)?|Else(?:If(?:Not)?|Unless)?|End(?:If|Select|Switch)|Exit(?:Do|For|While)|For(?:Each)?|If(?:Cmd|Not(?:Then)?|Then)?|Loop(?:Until|While)?|Or(?:If(?:Not)?|Unless)|Select|Switch|Unless|While)\}/, token: "variable-2", indent: true},
@@ -71,13 +71,13 @@ CodeMirror.defineSimpleMode("nsis",{
     {regex: /[-+\/*=<>!]+/, token: "operator"},
 
     // Variable
-    {regex: /\$[\w]+/, token: "variable"},
+    {regex: /\$\w+/, token: "variable"},
 
     // Constant
-    {regex: /\${[\w]+}/,token: "variable-2"},
+    {regex: /\${[\w\.:-]+}/, token: "variable-2"},
 
     // Language String
-    {regex: /\$\([\w]+\)/,token: "variable-3"}
+    {regex: /\$\([\w\.:-]+\)/, token: "variable-3"}
   ],
   comment: [
     {regex: /.*?\*\//, token: "comment", next: "start"},
diff --git a/public/vendor/plugins/codemirror/mode/ntriples/index.html b/public/vendor/plugins/codemirror/mode/ntriples/index.html
index 1355e7189e..63a695d527 100644
--- a/public/vendor/plugins/codemirror/mode/ntriples/index.html
+++ b/public/vendor/plugins/codemirror/mode/ntriples/index.html
@@ -1,19 +1,20 @@
 <!doctype html>
 
-<title>CodeMirror: NTriples mode</title>
+<title>CodeMirror: N-Triples mode</title>
 <meta charset="utf-8"/>
 <link rel=stylesheet href="../../doc/docs.css">
 
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
 <script src="ntriples.js"></script>
-<style type="text/css">
+<style>
       .CodeMirror {
         border: 1px solid #eee;
+        height: auto;
       }
     </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -22,12 +23,15 @@
   </ul>
   <ul>
     <li><a href="../index.html">Language modes</a>
-    <li><a class=active href="#">NTriples</a>
+    <li><a class=active href="#">N-Triples/N-Quads</a>
   </ul>
 </div>
 
 <article>
-<h2>NTriples mode</h2>
+  <h2><a href="https://www.w3.org/TR/n-triples/">N-Triples</a> mode</h2>
+  <p>The N-Triples mode also works well with on
+    <a href="https://www.w3.org/TR/n-quads/">N-Quad</a> documents.
+  </p>
 <form>
 <textarea id="ntriples" name="ntriples">    
 <http://Sub1>     <http://pred1>     <http://obj> .
@@ -41,5 +45,26 @@ _:bnode5          <http://pred5>     "literal 3"^^<http://type> .
     <script>
       var editor = CodeMirror.fromTextArea(document.getElementById("ntriples"), {});
     </script>
-    <p><strong>MIME types defined:</strong> <code>text/n-triples</code>.</p>
+    <p><strong>MIME types defined:</strong> <code>application/n-triples</code>.</p>
+
+    <hr />
+    <p><a href="https://www.w3.org/TR/n-quads/">N-Quads</a> add a fourth
+    element to the statement to track which graph the statement is from.
+    Otherwise, it's identical to N-Triples.</p>
+    <form>
+    <textarea id="nquads" name="nquads">
+
+    <http://Sub1>     <http://pred1>     <http://obj>   <http://graph3> .
+    <http://Sub2>     <http://pred2#an2> "literal 1"    <http://graph2> .
+    <http://Sub3#an3> <http://pred3>     _:bnode3     <http://graph2> .
+    _:bnode4          <http://pred4>     "literal 2"@lang     <http://graph2> .
+    # if a graph labe
+    _:bnode5          <http://pred5>     "literal 3"^^<http://type> .
+    </textarea>
+    </form>
+
+    <script>
+      var nquads_editor = CodeMirror.fromTextArea(document.getElementById("nquads"), {});
+    </script>
+    <p><strong>MIME types defined:</strong> <code>application/n-quads</code>.</p>
   </article>
diff --git a/public/vendor/plugins/codemirror/mode/ntriples/ntriples.js b/public/vendor/plugins/codemirror/mode/ntriples/ntriples.js
index 0524b1e8ab..5dd027286a 100644
--- a/public/vendor/plugins/codemirror/mode/ntriples/ntriples.js
+++ b/public/vendor/plugins/codemirror/mode/ntriples/ntriples.js
@@ -1,11 +1,11 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 /**********************************************************
 * This script provides syntax highlighting support for
-* the Ntriples format.
-* Ntriples format specification:
-*     http://www.w3.org/TR/rdf-testcases/#ntriples
+* the N-Triples format.
+* N-Triples format specification:
+*     https://www.w3.org/TR/n-triples/
 ***********************************************************/
 
 /*
@@ -181,6 +181,15 @@ CodeMirror.defineMode("ntriples", function() {
   };
 });
 
+// define the registered Media Type for n-triples:
+// https://www.w3.org/TR/n-triples/#n-triples-mediatype
+CodeMirror.defineMIME("application/n-triples", "ntriples");
+
+// N-Quads is based on the N-Triples format (so same highlighting works)
+// https://www.w3.org/TR/n-quads/
+CodeMirror.defineMIME("application/n-quads", "ntriples");
+
+// previously used, though technically incorrect media type for n-triples
 CodeMirror.defineMIME("text/n-triples", "ntriples");
 
 });
diff --git a/public/vendor/plugins/codemirror/mode/octave/index.html b/public/vendor/plugins/codemirror/mode/octave/index.html
index 3490ee6371..698ae7df92 100644
--- a/public/vendor/plugins/codemirror/mode/octave/index.html
+++ b/public/vendor/plugins/codemirror/mode/octave/index.html
@@ -6,10 +6,11 @@
 
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
+<script src="../../addon/edit/matchbrackets.js"></script>
 <script src="octave.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/octave/octave.js b/public/vendor/plugins/codemirror/mode/octave/octave.js
index a7bec030c2..33a03368fa 100644
--- a/public/vendor/plugins/codemirror/mode/octave/octave.js
+++ b/public/vendor/plugins/codemirror/mode/octave/octave.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -17,7 +17,7 @@ CodeMirror.defineMode("octave", function() {
   }
 
   var singleOperators = new RegExp("^[\\+\\-\\*/&|\\^~<>!@'\\\\]");
-  var singleDelimiters = new RegExp('^[\\(\\[\\{\\},:=;]');
+  var singleDelimiters = new RegExp('^[\\(\\[\\{\\},:=;\\.]');
   var doubleOperators = new RegExp("^((==)|(~=)|(<=)|(>=)|(<<)|(>>)|(\\.[\\+\\-\\*/\\^\\\\]))");
   var doubleDelimiters = new RegExp("^((!=)|(\\+=)|(\\-=)|(\\*=)|(/=)|(&=)|(\\|=)|(\\^=))");
   var tripleDelimiters = new RegExp("^((>>=)|(<<=))");
@@ -90,8 +90,8 @@ CodeMirror.defineMode("octave", function() {
     if (stream.match(wordRegexp(['nan','NaN','inf','Inf']))) { return 'number'; };
 
     // Handle Strings
-    if (stream.match(/^"([^"]|(""))*"/)) { return 'string'; } ;
-    if (stream.match(/^'([^']|(''))*'/)) { return 'string'; } ;
+    var m = stream.match(/^"(?:[^"]|"")*("|$)/) || stream.match(/^'(?:[^']|'')*('|$)/)
+    if (m) { return m[1] ? 'string' : "string error"; }
 
     // Handle words
     if (stream.match(keywords)) { return 'keyword'; } ;
@@ -126,7 +126,11 @@ CodeMirror.defineMode("octave", function() {
         state.tokenize = tokenTranspose;
       }
       return style;
-    }
+    },
+
+    lineComment: '%',
+
+    fold: 'indent'
   };
 });
 
diff --git a/public/vendor/plugins/codemirror/mode/oz/index.html b/public/vendor/plugins/codemirror/mode/oz/index.html
index febd82a59a..fb9df83e1a 100644
--- a/public/vendor/plugins/codemirror/mode/oz/index.html
+++ b/public/vendor/plugins/codemirror/mode/oz/index.html
@@ -7,12 +7,12 @@
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
 <script src="oz.js"></script>
-<script type="text/javascript" src="../../addon/runmode/runmode.js"></script>
+<script src="../../addon/runmode/runmode.js"></script>
 <style>
   .CodeMirror {border: 1px solid #aaa;}
 </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
   <ul>
     <li><a href="../../index.html">Home</a>
     <li><a href="../../doc/manual.html">Manual</a>
@@ -49,7 +49,7 @@ end
 </textarea>
 <p>MIME type defined: <code>text/x-oz</code>.</p>
 
-<script type="text/javascript">
+<script>
 var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
     lineNumbers: true,
     mode: "text/x-oz",
diff --git a/public/vendor/plugins/codemirror/mode/oz/oz.js b/public/vendor/plugins/codemirror/mode/oz/oz.js
index ee8cb0ad15..a9738495b6 100644
--- a/public/vendor/plugins/codemirror/mode/oz/oz.js
+++ b/public/vendor/plugins/codemirror/mode/oz/oz.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -27,7 +27,7 @@ CodeMirror.defineMode("oz", function (conf) {
 
   var atoms = wordRegexp(["true", "false", "nil", "unit"]);
   var commonKeywords = wordRegexp(["andthen", "at", "attr", "declare", "feat", "from", "lex",
-    "mod", "mode", "orelse", "parser", "prod", "prop", "scanner", "self", "syn", "token"]);
+    "mod", "div", "mode", "orelse", "parser", "prod", "prop", "scanner", "self", "syn", "token"]);
   var openingKeywords = wordRegexp(["local", "proc", "fun", "case", "class", "if", "cond", "or", "dis",
     "choice", "not", "thread", "try", "raise", "lock", "for", "suchthat", "meth", "functor"]);
   var middleKeywords = wordRegexp(middle);
diff --git a/public/vendor/plugins/codemirror/mode/pascal/index.html b/public/vendor/plugins/codemirror/mode/pascal/index.html
index f8a99ad01e..f0c49f4708 100644
--- a/public/vendor/plugins/codemirror/mode/pascal/index.html
+++ b/public/vendor/plugins/codemirror/mode/pascal/index.html
@@ -7,9 +7,9 @@
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
 <script src="pascal.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/pascal/pascal.js b/public/vendor/plugins/codemirror/mode/pascal/pascal.js
index 2d0c3d4240..dc3b1a3a3e 100644
--- a/public/vendor/plugins/codemirror/mode/pascal/pascal.js
+++ b/public/vendor/plugins/codemirror/mode/pascal/pascal.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -17,9 +17,21 @@ CodeMirror.defineMode("pascal", function() {
     for (var i = 0; i < words.length; ++i) obj[words[i]] = true;
     return obj;
   }
-  var keywords = words("and array begin case const div do downto else end file for forward integer " +
-                       "boolean char function goto if in label mod nil not of or packed procedure " +
-                       "program record repeat set string then to type until var while with");
+  var keywords = words(
+    "absolute and array asm begin case const constructor destructor div do " +
+    "downto else end file for function goto if implementation in inherited " +
+    "inline interface label mod nil not object of operator or packed procedure " +
+    "program record reintroduce repeat self set shl shr string then to type " +
+    "unit until uses var while with xor as class dispinterface except exports " +
+    "finalization finally initialization inline is library on out packed " +
+    "property raise resourcestring threadvar try absolute abstract alias " +
+    "assembler bitpacked break cdecl continue cppdecl cvar default deprecated " +
+    "dynamic enumerator experimental export external far far16 forward generic " +
+    "helper implements index interrupt iocheck local message name near " +
+    "nodefault noreturn nostackframe oldfpccall otherwise overload override " +
+    "pascal platform private protected public published read register " +
+    "reintroduce result safecall saveregisters softfloat specialize static " +
+    "stdcall stored strict unaligned unimplemented varargs virtual write");
   var atoms = {"null": true};
 
   var isOperatorChar = /[+\-*&%=<>!?|\/]/;
diff --git a/public/vendor/plugins/codemirror/mode/pegjs/index.html b/public/vendor/plugins/codemirror/mode/pegjs/index.html
index 0c74604881..9d452aa9cc 100644
--- a/public/vendor/plugins/codemirror/mode/pegjs/index.html
+++ b/public/vendor/plugins/codemirror/mode/pegjs/index.html
@@ -9,11 +9,11 @@
     <script src="../../lib/codemirror.js"></script>
     <script src="../javascript/javascript.js"></script>
     <script src="pegjs.js"></script>
-    <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+    <style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
   </head>
   <body>
     <div id=nav>
-      <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+      <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
       <ul>
         <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/pegjs/pegjs.js b/public/vendor/plugins/codemirror/mode/pegjs/pegjs.js
index 6c72074666..19d5fa4c02 100644
--- a/public/vendor/plugins/codemirror/mode/pegjs/pegjs.js
+++ b/public/vendor/plugins/codemirror/mode/pegjs/pegjs.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/perl/index.html b/public/vendor/plugins/codemirror/mode/perl/index.html
index 8c1021c42b..901718a4c5 100644
--- a/public/vendor/plugins/codemirror/mode/perl/index.html
+++ b/public/vendor/plugins/codemirror/mode/perl/index.html
@@ -7,9 +7,9 @@
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
 <script src="perl.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/perl/perl.js b/public/vendor/plugins/codemirror/mode/perl/perl.js
index 66e4ed034c..a3101a7c5b 100644
--- a/public/vendor/plugins/codemirror/mode/perl/perl.js
+++ b/public/vendor/plugins/codemirror/mode/perl/perl.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 // CodeMirror2 mode/perl/perl.js (text/x-perl) beta 0.10 (2011-11-08)
 // This is a part of CodeMirror from https://github.com/sabaca/CodeMirror_mode_perl (mail@sabaca.com)
diff --git a/public/vendor/plugins/codemirror/mode/php/index.html b/public/vendor/plugins/codemirror/mode/php/index.html
index adf6b1be22..689764146a 100644
--- a/public/vendor/plugins/codemirror/mode/php/index.html
+++ b/public/vendor/plugins/codemirror/mode/php/index.html
@@ -13,9 +13,9 @@
 <script src="../css/css.js"></script>
 <script src="../clike/clike.js"></script>
 <script src="php.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/php/php.js b/public/vendor/plugins/codemirror/mode/php/php.js
index 57ba812d72..5f3a143999 100644
--- a/public/vendor/plugins/codemirror/mode/php/php.js
+++ b/public/vendor/plugins/codemirror/mode/php/php.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -151,7 +151,7 @@
   };
 
   CodeMirror.defineMode("php", function(config, parserConfig) {
-    var htmlMode = CodeMirror.getMode(config, "text/html");
+    var htmlMode = CodeMirror.getMode(config, (parserConfig && parserConfig.htmlMode) || "text/html");
     var phpMode = CodeMirror.getMode(config, phpConfig);
 
     function dispatch(stream, state) {
@@ -160,7 +160,7 @@
       if (!isPHP) {
         if (stream.match(/^<\?\w*/)) {
           state.curMode = phpMode;
-          if (!state.php) state.php = CodeMirror.startState(phpMode, htmlMode.indent(state.html, ""))
+          if (!state.php) state.php = CodeMirror.startState(phpMode, htmlMode.indent(state.html, "", ""))
           state.curState = state.php;
           return "meta";
         }
@@ -213,11 +213,11 @@
 
       token: dispatch,
 
-      indent: function(state, textAfter) {
+      indent: function(state, textAfter, line) {
         if ((state.curMode != phpMode && /^\s*<\//.test(textAfter)) ||
             (state.curMode == phpMode && /^\?>/.test(textAfter)))
-          return htmlMode.indent(state.html, textAfter);
-        return state.curMode.indent(state.curState, textAfter);
+          return htmlMode.indent(state.html, textAfter, line);
+        return state.curMode.indent(state.curState, textAfter, line);
       },
 
       blockCommentStart: "/*",
diff --git a/public/vendor/plugins/codemirror/mode/php/test.js b/public/vendor/plugins/codemirror/mode/php/test.js
index e2ecefc187..ec158145ce 100644
--- a/public/vendor/plugins/codemirror/mode/php/test.js
+++ b/public/vendor/plugins/codemirror/mode/php/test.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function() {
   var mode = CodeMirror.getMode({indentUnit: 2}, "php");
diff --git a/public/vendor/plugins/codemirror/mode/pig/index.html b/public/vendor/plugins/codemirror/mode/pig/index.html
index ea77f70441..80fed6e1c0 100644
--- a/public/vendor/plugins/codemirror/mode/pig/index.html
+++ b/public/vendor/plugins/codemirror/mode/pig/index.html
@@ -8,7 +8,7 @@
 <script src="pig.js"></script>
 <style>.CodeMirror {border: 2px inset #dee;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/pig/pig.js b/public/vendor/plugins/codemirror/mode/pig/pig.js
index 5b567272e6..3b9c7746bb 100644
--- a/public/vendor/plugins/codemirror/mode/pig/pig.js
+++ b/public/vendor/plugins/codemirror/mode/pig/pig.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 /*
  *      Pig Latin Mode for CodeMirror 2
diff --git a/public/vendor/plugins/codemirror/mode/powershell/index.html b/public/vendor/plugins/codemirror/mode/powershell/index.html
index 6b235df8f1..81c050a815 100644
--- a/public/vendor/plugins/codemirror/mode/powershell/index.html
+++ b/public/vendor/plugins/codemirror/mode/powershell/index.html
@@ -6,12 +6,13 @@
     <link rel="stylesheet" href="../../doc/docs.css">
     <link rel="stylesheet" href="../../lib/codemirror.css">
     <script src="../../lib/codemirror.js"></script>
+    <script src="../../addon/edit/matchbrackets.js"></script>
     <script src="powershell.js"></script>
     <style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
   </head>
   <body>
     <div id=nav>
-      <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+      <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
       <ul>
         <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/powershell/powershell.js b/public/vendor/plugins/codemirror/mode/powershell/powershell.js
index c443e7232d..85f89b9ec7 100644
--- a/public/vendor/plugins/codemirror/mode/powershell/powershell.js
+++ b/public/vendor/plugins/codemirror/mode/powershell/powershell.js
@@ -1,12 +1,12 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   'use strict';
   if (typeof exports == 'object' && typeof module == 'object') // CommonJS
-    mod(require('codemirror'));
+    mod(require('../../lib/codemirror'));
   else if (typeof define == 'function' && define.amd) // AMD
-    define(['codemirror'], mod);
+    define(['../../lib/codemirror'], mod);
   else // Plain browser env
     mod(window.CodeMirror);
 })(function(CodeMirror) {
@@ -222,6 +222,8 @@ CodeMirror.defineMode('powershell', function() {
         state.tokenize = tokenMultiString;
         state.startQuote = quoteMatch[0];
         return tokenMultiString(stream, state);
+      } else if (stream.eol()) {
+        return 'error';
       } else if (stream.peek().match(/[({]/)) {
         return 'punctuation';
       } else if (stream.peek().match(varNames)) {
diff --git a/public/vendor/plugins/codemirror/mode/powershell/test.js b/public/vendor/plugins/codemirror/mode/powershell/test.js
index 59b8e6fca9..ba1ecf194d 100644
--- a/public/vendor/plugins/codemirror/mode/powershell/test.js
+++ b/public/vendor/plugins/codemirror/mode/powershell/test.js
@@ -1,22 +1,24 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function() {
   var mode = CodeMirror.getMode({indentUnit: 2}, "powershell");
   function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
 
+  function forEach(arr, f) { for (var i = 0; i < arr.length; i++) f(arr[i], i) }
+
   MT('comment', '[number 1][comment # A]');
   MT('comment_multiline', '[number 1][comment <#]',
     '[comment ABC]',
   '[comment #>][number 2]');
 
-  [
+  forEach([
     '0', '1234',
     '12kb', '12mb', '12Gb', '12Tb', '12PB', '12L', '12D', '12lkb', '12dtb',
     '1.234', '1.234e56', '1.', '1.e2', '.2', '.2e34',
     '1.2MB', '1.kb', '.1dTB', '1.e1gb', '.2', '.2e34',
     '0x1', '0xabcdef', '0x3tb', '0xelmb'
-  ].forEach(function(number) {
+  ], function(number) {
     MT("number_" + number, "[number " + number + "]");
   });
 
@@ -60,9 +62,9 @@
   MT('operator_unary', "[operator +][number 3]");
   MT('operator_long', "[operator -match]");
 
-  [
+  forEach([
     '(', ')', '[[', ']]', '{', '}', ',', '`', ';', '.'
-  ].forEach(function(punctuation) {
+  ], function(punctuation) {
     MT("punctuation_" + punctuation.replace(/^[\[\]]/,''), "[punctuation " + punctuation + "]");
   });
 
diff --git a/public/vendor/plugins/codemirror/mode/properties/index.html b/public/vendor/plugins/codemirror/mode/properties/index.html
index f885302de2..eb49f92914 100644
--- a/public/vendor/plugins/codemirror/mode/properties/index.html
+++ b/public/vendor/plugins/codemirror/mode/properties/index.html
@@ -9,7 +9,7 @@
 <script src="properties.js"></script>
 <style>.CodeMirror {border-top: 1px solid #ddd; border-bottom: 1px solid #ddd;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/properties/properties.js b/public/vendor/plugins/codemirror/mode/properties/properties.js
index ef8bf37eea..02fd7fe5eb 100644
--- a/public/vendor/plugins/codemirror/mode/properties/properties.js
+++ b/public/vendor/plugins/codemirror/mode/properties/properties.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/protobuf/index.html b/public/vendor/plugins/codemirror/mode/protobuf/index.html
index cfe7b9dcd3..ee58cd9a46 100644
--- a/public/vendor/plugins/codemirror/mode/protobuf/index.html
+++ b/public/vendor/plugins/codemirror/mode/protobuf/index.html
@@ -9,7 +9,7 @@
 <script src="protobuf.js"></script>
 <style>.CodeMirror { border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; }</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -54,9 +54,49 @@ message Person {
    required bool deleted = 11 [default = false];
 }
 
-</textarea></form>
+</textarea>
+  <textarea id="code2" name="code2">
+syntax = "proto3";
+package tutorial;
+
+import "google/protobuf/timestamp.proto";
+option java_package = "com.example.tutorial";
+option java_outer_classname = "AddressBookProtos";
+option csharp_namespace = "Google.Protobuf.Examples.AddressBook";
+
+message Person {
+  string name = 1;
+  int32 id = 2;  // Unique ID number for this person.
+  string email = 3;
+
+  enum PhoneType {
+    MOBILE = 0;
+    HOME = 1;
+    WORK = 2;
+  }
+
+  message PhoneNumber {
+    string number = 1;
+    PhoneType type = 2;
+  }
+
+  repeated PhoneNumber phones = 4;
+
+  google.protobuf.Timestamp last_updated = 5;
+}
+
+// Our address book file is just one of these.
+message AddressBook {
+  repeated Person people = 1;
+}
+service Test {
+  rpc SayHello (HelloRequest) returns (HelloReply) {}
+  rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}
+}</textarea>
+</form>
     <script>
       var editor = CodeMirror.fromTextArea(document.getElementById("code"), {});
+      var editor = CodeMirror.fromTextArea(document.getElementById("code2"), {});
     </script>
 
     <p><strong>MIME types defined:</strong> <code>text/x-protobuf</code>.</p>
diff --git a/public/vendor/plugins/codemirror/mode/protobuf/protobuf.js b/public/vendor/plugins/codemirror/mode/protobuf/protobuf.js
index bcae276e8d..68b240a40e 100644
--- a/public/vendor/plugins/codemirror/mode/protobuf/protobuf.js
+++ b/public/vendor/plugins/codemirror/mode/protobuf/protobuf.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -19,7 +19,8 @@
     "package", "message", "import", "syntax",
     "required", "optional", "repeated", "reserved", "default", "extensions", "packed",
     "bool", "bytes", "double", "enum", "float", "string",
-    "int32", "int64", "uint32", "uint64", "sint32", "sint64", "fixed32", "fixed64", "sfixed32", "sfixed64"
+    "int32", "int64", "uint32", "uint64", "sint32", "sint64", "fixed32", "fixed64", "sfixed32", "sfixed64",
+    "option", "service", "rpc", "returns"
   ];
   var keywords = wordRegexp(keywordArray);
 
diff --git a/public/vendor/plugins/codemirror/mode/jade/index.html b/public/vendor/plugins/codemirror/mode/pug/index.html
similarity index 76%
rename from public/vendor/plugins/codemirror/mode/jade/index.html
rename to public/vendor/plugins/codemirror/mode/pug/index.html
index e534981b20..dfc8968dbc 100644
--- a/public/vendor/plugins/codemirror/mode/jade/index.html
+++ b/public/vendor/plugins/codemirror/mode/pug/index.html
@@ -1,6 +1,6 @@
 <!doctype html>
 
-<title>CodeMirror: Jade Templating Mode</title>
+<title>CodeMirror: Pug Templating Mode</title>
 <meta charset="utf-8"/>
 <link rel=stylesheet href="../../doc/docs.css">
 
@@ -10,10 +10,10 @@
 <script src="../css/css.js"></script>
 <script src="../xml/xml.js"></script>
 <script src="../htmlmixed/htmlmixed.js"></script>
-<script src="jade.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<script src="pug.js"></script>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -22,17 +22,17 @@
   </ul>
   <ul>
     <li><a href="../index.html">Language modes</a>
-    <li><a class=active href="#">Jade Templating Mode</a>
+    <li><a class=active href="#">Pug Templating Mode</a>
   </ul>
 </div>
 
 <article>
-<h2>Jade Templating Mode</h2>
+<h2>Pug Templating Mode</h2>
 <form><textarea id="code" name="code">
 doctype html
   html
     head
-      title= "Jade Templating CodeMirror Mode Example"
+      title= "Pug Templating CodeMirror Mode Example"
       link(rel='stylesheet', href='/css/bootstrap.min.css')
       link(rel='stylesheet', href='/css/index.css')
       script(type='text/javascript', src='/js/jquery-1.9.1.min.js')
@@ -60,11 +60,11 @@ doctype html
 </textarea></form>
     <script>
       var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
-        mode: {name: "jade", alignCDATA: true},
+        mode: {name: "pug", alignCDATA: true},
         lineNumbers: true
       });
     </script>
-    <h3>The Jade Templating Mode</h3>
+    <h3>The Pug Templating Mode</h3>
       <p> Created by Forbes Lindesay. Managed as part of a Brackets extension at <a href="https://github.com/ForbesLindesay/jade-brackets">https://github.com/ForbesLindesay/jade-brackets</a>.</p>
-    <p><strong>MIME type defined:</strong> <code>text/x-jade</code>.</p>
+    <p><strong>MIME type defined:</strong> <code>text/x-pug</code>, <code>text/x-jade</code>.</p>
   </article>
diff --git a/public/vendor/plugins/codemirror/mode/jade/jade.js b/public/vendor/plugins/codemirror/mode/pug/pug.js
similarity index 98%
rename from public/vendor/plugins/codemirror/mode/jade/jade.js
rename to public/vendor/plugins/codemirror/mode/pug/pug.js
index 51ed105aca..8701b02382 100644
--- a/public/vendor/plugins/codemirror/mode/jade/jade.js
+++ b/public/vendor/plugins/codemirror/mode/pug/pug.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -11,7 +11,7 @@
 })(function(CodeMirror) {
 "use strict";
 
-CodeMirror.defineMode('jade', function (config) {
+CodeMirror.defineMode("pug", function (config) {
   // token types
   var KEYWORD = 'keyword';
   var DOCTYPE = 'meta';
@@ -585,6 +585,7 @@ CodeMirror.defineMode('jade', function (config) {
   };
 }, 'javascript', 'css', 'htmlmixed');
 
-CodeMirror.defineMIME('text/x-jade', 'jade');
+CodeMirror.defineMIME('text/x-pug', 'pug');
+CodeMirror.defineMIME('text/x-jade', 'pug');
 
 });
diff --git a/public/vendor/plugins/codemirror/mode/puppet/index.html b/public/vendor/plugins/codemirror/mode/puppet/index.html
index 5614c3695a..757377b1d2 100644
--- a/public/vendor/plugins/codemirror/mode/puppet/index.html
+++ b/public/vendor/plugins/codemirror/mode/puppet/index.html
@@ -13,7 +13,7 @@
       .cm-s-default span.cm-arrow { color: red; }
     </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/puppet/puppet.js b/public/vendor/plugins/codemirror/mode/puppet/puppet.js
index 57041300af..364934209e 100644
--- a/public/vendor/plugins/codemirror/mode/puppet/puppet.js
+++ b/public/vendor/plugins/codemirror/mode/puppet/puppet.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/python/index.html b/public/vendor/plugins/codemirror/mode/python/index.html
index 6116a13b6b..90ee324a21 100644
--- a/public/vendor/plugins/codemirror/mode/python/index.html
+++ b/public/vendor/plugins/codemirror/mode/python/index.html
@@ -8,9 +8,9 @@
 <script src="../../lib/codemirror.js"></script>
 <script src="../../addon/edit/matchbrackets.js"></script>
 <script src="python.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -126,6 +126,15 @@ class ExampleClass(ParentClass):
     def __init__(self, mixin = 'Hello'):
         self.mixin = mixin
 
+# Python 3.6 f-strings (https://www.python.org/dev/peps/pep-0498/)
+f'My name is {name}, my age next year is {age+1}, my anniversary is {anniversary:%A, %B %d, %Y}.'
+f'He said his name is {name!r}.'
+f"""He said his name is {name!r}."""
+f'{"quoted string"}'
+f'{{ {4*10} }}'
+f'This is an error }'
+f'This is ok }}'
+fr'x={4*10}\n'
 </textarea></div>
 
 
@@ -176,7 +185,7 @@ def pairwise_cython(double[:, ::1] X):
     </script>
     <h2>Configuration Options for Python mode:</h2>
     <ul>
-      <li>version - 2/3 - The version of Python to recognize.  Default is 2.</li>
+      <li>version - 2/3 - The version of Python to recognize.  Default is 3.</li>
       <li>singleLineStringErrors - true/false - If you have a single-line string that is not terminated at the end of the line, this will show subsequent lines as errors if true, otherwise it will consider the newline as the end of the string. Default is false.</li>
       <li>hangingIndent - int - If you want to write long arguments to a function starting on a new line, how much that line should be indented. Defaults to one normal indentation unit.</li>
     </ul>
diff --git a/public/vendor/plugins/codemirror/mode/python/python.js b/public/vendor/plugins/codemirror/mode/python/python.js
index be65ad7687..9745103829 100644
--- a/public/vendor/plugins/codemirror/mode/python/python.js
+++ b/public/vendor/plugins/codemirror/mode/python/python.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -41,10 +41,11 @@
   CodeMirror.defineMode("python", function(conf, parserConf) {
     var ERRORCLASS = "error";
 
-    var singleDelimiters = parserConf.singleDelimiters || /^[\(\)\[\]\{\}@,:`=;\.]/;
-    var doubleOperators = parserConf.doubleOperators || /^([!<>]==|<>|<<|>>|\/\/|\*\*)/;
-    var doubleDelimiters = parserConf.doubleDelimiters || /^(\+=|\-=|\*=|%=|\/=|&=|\|=|\^=)/;
-    var tripleDelimiters = parserConf.tripleDelimiters || /^(\/\/=|>>=|<<=|\*\*=)/;
+    var delimiters = parserConf.delimiters || parserConf.singleDelimiters || /^[\(\)\[\]\{\}@,:`=;\.\\]/;
+    //               (Backwards-compatiblity with old, cumbersome config system)
+    var operators = [parserConf.singleOperators, parserConf.doubleOperators, parserConf.doubleDelimiters, parserConf.tripleDelimiters,
+                     parserConf.operators || /^([-+*/%\/&|^]=?|[<>=]+|\/\/=?|\*\*=?|!=|[~!@]|\.\.\.)/]
+    for (var i = 0; i < operators.length; i++) if (!operators[i]) operators.splice(i--, 1)
 
     var hangingIndent = parserConf.hangingIndent || conf.indentUnit;
 
@@ -55,37 +56,36 @@
     if (parserConf.extra_builtins != undefined)
       myBuiltins = myBuiltins.concat(parserConf.extra_builtins);
 
-    var py3 = parserConf.version && parseInt(parserConf.version, 10) == 3
+    var py3 = !(parserConf.version && Number(parserConf.version) < 3)
     if (py3) {
       // since http://legacy.python.org/dev/peps/pep-0465/ @ is also an operator
-      var singleOperators = parserConf.singleOperators || /^[\+\-\*\/%&|\^~<>!@]/;
       var identifiers = parserConf.identifiers|| /^[_A-Za-z\u00A1-\uFFFF][_A-Za-z0-9\u00A1-\uFFFF]*/;
       myKeywords = myKeywords.concat(["nonlocal", "False", "True", "None", "async", "await"]);
       myBuiltins = myBuiltins.concat(["ascii", "bytes", "exec", "print"]);
-      var stringPrefixes = new RegExp("^(([rbuf]|(br))?('{3}|\"{3}|['\"]))", "i");
+      var stringPrefixes = new RegExp("^(([rbuf]|(br)|(fr))?('{3}|\"{3}|['\"]))", "i");
     } else {
-      var singleOperators = parserConf.singleOperators || /^[\+\-\*\/%&|\^~<>!]/;
       var identifiers = parserConf.identifiers|| /^[_A-Za-z][_A-Za-z0-9]*/;
       myKeywords = myKeywords.concat(["exec", "print"]);
       myBuiltins = myBuiltins.concat(["apply", "basestring", "buffer", "cmp", "coerce", "execfile",
                                       "file", "intern", "long", "raw_input", "reduce", "reload",
                                       "unichr", "unicode", "xrange", "False", "True", "None"]);
-      var stringPrefixes = new RegExp("^(([rub]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i");
+      var stringPrefixes = new RegExp("^(([rubf]|(ur)|(br))?('{3}|\"{3}|['\"]))", "i");
     }
     var keywords = wordRegexp(myKeywords);
     var builtins = wordRegexp(myBuiltins);
 
     // tokenizers
     function tokenBase(stream, state) {
-      if (stream.sol()) state.indent = stream.indentation()
+      var sol = stream.sol() && state.lastToken != "\\"
+      if (sol) state.indent = stream.indentation()
       // Handle scope changes
-      if (stream.sol() && top(state).type == "py") {
+      if (sol && top(state).type == "py") {
         var scopeOffset = top(state).offset;
         if (stream.eatSpace()) {
           var lineOffset = stream.indentation();
           if (lineOffset > scopeOffset)
             pushPyScope(state);
-          else if (lineOffset < scopeOffset && dedent(stream, state))
+          else if (lineOffset < scopeOffset && dedent(stream, state) && stream.peek() != "#")
             state.errorToken = true;
           return null;
         } else {
@@ -101,20 +101,15 @@
     function tokenBaseInner(stream, state) {
       if (stream.eatSpace()) return null;
 
-      var ch = stream.peek();
-
       // Handle Comments
-      if (ch == "#") {
-        stream.skipToEnd();
-        return "comment";
-      }
+      if (stream.match(/^#.*/)) return "comment";
 
       // Handle Number Literals
       if (stream.match(/^[0-9\.]/, false)) {
         var floatLiteral = false;
         // Floats
-        if (stream.match(/^\d*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; }
-        if (stream.match(/^\d+\.\d*/)) { floatLiteral = true; }
+        if (stream.match(/^[\d_]*\.\d+(e[\+\-]?\d+)?/i)) { floatLiteral = true; }
+        if (stream.match(/^[\d_]+\.\d*/)) { floatLiteral = true; }
         if (stream.match(/^\.\d+/)) { floatLiteral = true; }
         if (floatLiteral) {
           // Float literals may be "imaginary"
@@ -124,13 +119,13 @@
         // Integers
         var intLiteral = false;
         // Hex
-        if (stream.match(/^0x[0-9a-f]+/i)) intLiteral = true;
+        if (stream.match(/^0x[0-9a-f_]+/i)) intLiteral = true;
         // Binary
-        if (stream.match(/^0b[01]+/i)) intLiteral = true;
+        if (stream.match(/^0b[01_]+/i)) intLiteral = true;
         // Octal
-        if (stream.match(/^0o[0-7]+/i)) intLiteral = true;
+        if (stream.match(/^0o[0-7_]+/i)) intLiteral = true;
         // Decimal
-        if (stream.match(/^[1-9]\d*(e[\+\-]?\d+)?/)) {
+        if (stream.match(/^[1-9][\d_]*(e[\+\-]?[\d_]+)?/)) {
           // Decimal literals may be "imaginary"
           stream.eat(/J/i);
           // TODO - Can you have imaginary longs?
@@ -147,19 +142,20 @@
 
       // Handle Strings
       if (stream.match(stringPrefixes)) {
-        state.tokenize = tokenStringFactory(stream.current());
-        return state.tokenize(stream, state);
+        var isFmtString = stream.current().toLowerCase().indexOf('f') !== -1;
+        if (!isFmtString) {
+          state.tokenize = tokenStringFactory(stream.current(), state.tokenize);
+          return state.tokenize(stream, state);
+        } else {
+          state.tokenize = formatStringFactory(stream.current(), state.tokenize);
+          return state.tokenize(stream, state);
+        }
       }
 
-      // Handle operators and Delimiters
-      if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters))
-        return "punctuation";
+      for (var i = 0; i < operators.length; i++)
+        if (stream.match(operators[i])) return "operator"
 
-      if (stream.match(doubleOperators) || stream.match(singleOperators))
-        return "operator";
-
-      if (stream.match(singleDelimiters))
-        return "punctuation";
+      if (stream.match(delimiters)) return "punctuation";
 
       if (state.lastToken == "." && stream.match(identifiers))
         return "property";
@@ -184,8 +180,69 @@
       return ERRORCLASS;
     }
 
-    function tokenStringFactory(delimiter) {
-      while ("rub".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)
+    function formatStringFactory(delimiter, tokenOuter) {
+      while ("rubf".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)
+        delimiter = delimiter.substr(1);
+
+      var singleline = delimiter.length == 1;
+      var OUTCLASS = "string";
+
+      function tokenNestedExpr(depth) {
+        return function(stream, state) {
+          var inner = tokenBaseInner(stream, state)
+          if (inner == "punctuation") {
+            if (stream.current() == "{") {
+              state.tokenize = tokenNestedExpr(depth + 1)
+            } else if (stream.current() == "}") {
+              if (depth > 1) state.tokenize = tokenNestedExpr(depth - 1)
+              else state.tokenize = tokenString
+            }
+          }
+          return inner
+        }
+      }
+
+      function tokenString(stream, state) {
+        while (!stream.eol()) {
+          stream.eatWhile(/[^'"\{\}\\]/);
+          if (stream.eat("\\")) {
+            stream.next();
+            if (singleline && stream.eol())
+              return OUTCLASS;
+          } else if (stream.match(delimiter)) {
+            state.tokenize = tokenOuter;
+            return OUTCLASS;
+          } else if (stream.match('{{')) {
+            // ignore {{ in f-str
+            return OUTCLASS;
+          } else if (stream.match('{', false)) {
+            // switch to nested mode
+            state.tokenize = tokenNestedExpr(0)
+            if (stream.current()) return OUTCLASS;
+            else return state.tokenize(stream, state)
+          } else if (stream.match('}}')) {
+            return OUTCLASS;
+          } else if (stream.match('}')) {
+            // single } in f-string is an error
+            return ERRORCLASS;
+          } else {
+            stream.eat(/['"]/);
+          }
+        }
+        if (singleline) {
+          if (parserConf.singleLineStringErrors)
+            return ERRORCLASS;
+          else
+            state.tokenize = tokenOuter;
+        }
+        return OUTCLASS;
+      }
+      tokenString.isString = true;
+      return tokenString;
+    }
+
+    function tokenStringFactory(delimiter, tokenOuter) {
+      while ("rubf".indexOf(delimiter.charAt(0).toLowerCase()) >= 0)
         delimiter = delimiter.substr(1);
 
       var singleline = delimiter.length == 1;
@@ -199,7 +256,7 @@
             if (singleline && stream.eol())
               return OUTCLASS;
           } else if (stream.match(delimiter)) {
-            state.tokenize = tokenBase;
+            state.tokenize = tokenOuter;
             return OUTCLASS;
           } else {
             stream.eat(/['"]/);
@@ -209,7 +266,7 @@
           if (parserConf.singleLineStringErrors)
             return ERRORCLASS;
           else
-            state.tokenize = tokenBase;
+            state.tokenize = tokenOuter;
         }
         return OUTCLASS;
       }
@@ -264,14 +321,16 @@
       if (current == ":" && !state.lambda && top(state).type == "py")
         pushPyScope(state);
 
-      var delimiter_index = current.length == 1 ? "[({".indexOf(current) : -1;
-      if (delimiter_index != -1)
-        pushBracketScope(stream, state, "])}".slice(delimiter_index, delimiter_index+1));
+      if (current.length == 1 && !/string|comment/.test(style)) {
+        var delimiter_index = "[({".indexOf(current);
+        if (delimiter_index != -1)
+          pushBracketScope(stream, state, "])}".slice(delimiter_index, delimiter_index+1));
 
-      delimiter_index = "])}".indexOf(current);
-      if (delimiter_index != -1) {
-        if (top(state).type == current) state.indent = state.scopes.pop().offset - hangingIndent
-        else return ERRORCLASS;
+        delimiter_index = "])}".indexOf(current);
+        if (delimiter_index != -1) {
+          if (top(state).type == current) state.indent = state.scopes.pop().offset - hangingIndent
+          else return ERRORCLASS;
+        }
       }
       if (state.dedent > 0 && stream.eol() && top(state).type == "py") {
         if (state.scopes.length > 1) state.scopes.pop();
@@ -332,8 +391,8 @@
 
   CodeMirror.defineMIME("text/x-cython", {
     name: "python",
-    extra_keywords: words("by cdef cimport cpdef ctypedef enum except"+
-                          "extern gil include nogil property public"+
+    extra_keywords: words("by cdef cimport cpdef ctypedef enum except "+
+                          "extern gil include nogil property public "+
                           "readonly struct union DEF IF ELIF ELSE")
   });
 
diff --git a/public/vendor/plugins/codemirror/mode/python/test.js b/public/vendor/plugins/codemirror/mode/python/test.js
index c1a9c6a990..2b605b8e62 100644
--- a/public/vendor/plugins/codemirror/mode/python/test.js
+++ b/public/vendor/plugins/codemirror/mode/python/test.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function() {
   var mode = CodeMirror.getMode({indentUnit: 4},
@@ -24,7 +24,21 @@
   MT("matmulWithSpace:", "[variable a] [operator @] [variable b]");
   MT("matmulWithoutSpace:", "[variable a][operator @][variable b]");
   MT("matmulSpaceBefore:", "[variable a] [operator @][variable b]");
+  var before_equal_sign = ["+", "-", "*", "/", "=", "!", ">", "<"];
+  for (var i = 0; i < before_equal_sign.length; ++i) {
+    var c = before_equal_sign[i]
+    MT("before_equal_sign_" + c, "[variable a] [operator " + c + "=] [variable b]");
+  }
 
-  MT("fValidStringPrefix", "[string f'this is a {formatted} string']");
+  MT("fValidStringPrefix", "[string f'this is a]{[variable formatted]}[string string']");
+  MT("fValidExpressioninFString", "[string f'expression ]{[number 100][operator *][number 5]}[string string']");
+  MT("fInvalidFString", "[error f'this is wrong}]");
+  MT("fNestedFString", "[string f'expression ]{[number 100] [operator +] [string f'inner]{[number 5]}[string ']}[string string']");
   MT("uValidStringPrefix", "[string u'this is an unicode string']");
+
+  MT("nestedString", "[string f']{[variable b][[ [string \"c\"] ]]}[string f'] [comment # oops]")
+
+  MT("bracesInFString", "[string f']{[variable x] [operator +] {}}[string !']")
+
+  MT("nestedFString", "[string f']{[variable b][[ [string f\"c\"] ]]}[string f'] [comment # oops]")
 })();
diff --git a/public/vendor/plugins/codemirror/mode/q/index.html b/public/vendor/plugins/codemirror/mode/q/index.html
index 72785ba3bf..930e133d49 100644
--- a/public/vendor/plugins/codemirror/mode/q/index.html
+++ b/public/vendor/plugins/codemirror/mode/q/index.html
@@ -8,9 +8,9 @@
 <script src="../../lib/codemirror.js"></script>
 <script src="../../addon/edit/matchbrackets.js"></script>
 <script src="q.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/q/q.js b/public/vendor/plugins/codemirror/mode/q/q.js
index a4af9383e7..c016a6aa65 100644
--- a/public/vendor/plugins/codemirror/mode/q/q.js
+++ b/public/vendor/plugins/codemirror/mode/q/q.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -25,7 +25,7 @@ CodeMirror.defineMode("q",function(config){
         return(state.tokenize=tokenLineComment)(stream,state);
       else if(c=="\\"){
         if(stream.eol()||/\s/.test(stream.peek()))
-          return stream.skipToEnd(),/^\\\s*$/.test(stream.current())?(state.tokenize=tokenCommentToEOF)(stream, state):state.tokenize=tokenBase,"comment";
+          return stream.skipToEnd(),/^\\\s*$/.test(stream.current())?(state.tokenize=tokenCommentToEOF)(stream):state.tokenize=tokenBase,"comment";
         else
           return state.tokenize=tokenBase,"builtin";
       }
@@ -34,25 +34,25 @@ CodeMirror.defineMode("q",function(config){
     if(c=='"')
       return(state.tokenize=tokenString)(stream,state);
     if(c=='`')
-      return stream.eatWhile(/[A-Z|a-z|\d|_|:|\/|\.]/),"symbol";
+      return stream.eatWhile(/[A-Za-z\d_:\/.]/),"symbol";
     if(("."==c&&/\d/.test(stream.peek()))||/\d/.test(c)){
       var t=null;
       stream.backUp(1);
-      if(stream.match(/^\d{4}\.\d{2}(m|\.\d{2}([D|T](\d{2}(:\d{2}(:\d{2}(\.\d{1,9})?)?)?)?)?)/)
+      if(stream.match(/^\d{4}\.\d{2}(m|\.\d{2}([DT](\d{2}(:\d{2}(:\d{2}(\.\d{1,9})?)?)?)?)?)/)
       || stream.match(/^\d+D(\d{2}(:\d{2}(:\d{2}(\.\d{1,9})?)?)?)/)
       || stream.match(/^\d{2}:\d{2}(:\d{2}(\.\d{1,9})?)?/)
       || stream.match(/^\d+[ptuv]{1}/))
         t="temporal";
       else if(stream.match(/^0[NwW]{1}/)
-      || stream.match(/^0x[\d|a-f|A-F]*/)
-      || stream.match(/^[0|1]+[b]{1}/)
+      || stream.match(/^0x[\da-fA-F]*/)
+      || stream.match(/^[01]+[b]{1}/)
       || stream.match(/^\d+[chijn]{1}/)
       || stream.match(/-?\d*(\.\d*)?(e[+\-]?\d+)?(e|f)?/))
         t="number";
       return(t&&(!(c=stream.peek())||E.test(c)))?t:(stream.next(),"error");
     }
-    if(/[A-Z|a-z]|\./.test(c))
-      return stream.eatWhile(/[A-Z|a-z|\.|_|\d]/),keywords.test(stream.current())?"keyword":"variable";
+    if(/[A-Za-z]|\./.test(c))
+      return stream.eatWhile(/[A-Za-z._\d]/),keywords.test(stream.current())?"keyword":"variable";
     if(/[|/&^!+:\\\-*%$=~#;@><\.,?_\']/.test(c))
       return null;
     if(/[{}\(\[\]\)]/.test(c))
diff --git a/public/vendor/plugins/codemirror/mode/r/index.html b/public/vendor/plugins/codemirror/mode/r/index.html
index 6dd9634659..28192dabc5 100644
--- a/public/vendor/plugins/codemirror/mode/r/index.html
+++ b/public/vendor/plugins/codemirror/mode/r/index.html
@@ -15,7 +15,7 @@
       .cm-s-default span.cm-arg-is { color: brown; }
     </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -31,47 +31,50 @@
 <article>
 <h2>R mode</h2>
 <form><textarea id="code" name="code">
-# Code from http://www.mayin.org/ajayshah/KB/R/
+X <- list(height = 5.4, weight = 54)
+cat("Printing objects: "); print(X)
+print("Accessing individual elements:")
+cat(sprintf("Your height is %s and your weight is %s\n", X$height, X$weight))
 
-# FIRST LEARN ABOUT LISTS --
-X = list(height=5.4, weight=54)
-print("Use default printing --")
-print(X)
-print("Accessing individual elements --")
-cat("Your height is ", X$height, " and your weight is ", X$weight, "\n")
-
-# FUNCTIONS --
+# Functions:
 square <- function(x) {
-  return(x*x)
+  return(x * x)
 }
-cat("The square of 3 is ", square(3), "\n")
-
-                 # default value of the arg is set to 5.
-cube <- function(x=5) {
-  return(x*x*x);
-}
-cat("Calling cube with 2 : ", cube(2), "\n")    # will give 2^3
-cat("Calling cube        : ", cube(), "\n")     # will default to 5^3.
-
-# LEARN ABOUT FUNCTIONS THAT RETURN MULTIPLE OBJECTS --
-powers <- function(x) {
-  parcel = list(x2=x*x, x3=x*x*x, x4=x*x*x*x);
-  return(parcel);
-}
-
-X = powers(3);
-print("Showing powers of 3 --"); print(X);
-
-# WRITING THIS COMPACTLY (4 lines instead of 7)
-
-powerful <- function(x) {
-  return(list(x2=x*x, x3=x*x*x, x4=x*x*x*x));
-}
-print("Showing powers of 3 --"); print(powerful(3));
+cat(sprintf("The square of 3 is %s\n", square(3)))
 
 # In R, the last expression in a function is, by default, what is
-# returned. So you could equally just say:
-powerful <- function(x) {list(x2=x*x, x3=x*x*x, x4=x*x*x*x)}
+# returned. The idiomatic way to write the function is:
+square <- function(x) {
+  x * x
+}
+# or, for functions with short content:
+square <- function(x) x * x
+
+# Function arguments with default values:
+cube <- function(x = 5) x * x * x
+cat(sprintf("Calling cube with 2 : %s\n", cube(2))  # will give 2^3
+cat(sprintf("Calling cube        : %s\n", cube())   # will default to 5^3.
+
+powers <- function(x) list(x2 = x*x, x3 = x*x*x, x4 = x*x*x*x)
+
+cat("Powers of 3: "); print(powers(3))
+
+# Data frames
+df <- data.frame(letters = letters[1:5], '#letter' = 1:5)
+print(df$letters)
+print(df$`#letter`)
+
+# Operators:
+m1 <- matrix(1:6, 2, 3)
+m2 <- m1 %*% t(m1)
+cat("Matrix product: "); print(m2)
+
+# Assignments:
+a <- 1
+b <<- 2
+c = 3
+4 -> d
+5 ->> e
 </textarea></form>
     <script>
       var editor = CodeMirror.fromTextArea(document.getElementById("code"), {});
diff --git a/public/vendor/plugins/codemirror/mode/r/r.js b/public/vendor/plugins/codemirror/mode/r/r.js
index d41d1c54c1..c422af9a4c 100644
--- a/public/vendor/plugins/codemirror/mode/r/r.js
+++ b/public/vendor/plugins/codemirror/mode/r/r.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -14,15 +14,22 @@
 CodeMirror.registerHelper("wordChars", "r", /[\w.]/);
 
 CodeMirror.defineMode("r", function(config) {
-  function wordObj(str) {
-    var words = str.split(" "), res = {};
+  function wordObj(words) {
+    var res = {};
     for (var i = 0; i < words.length; ++i) res[words[i]] = true;
     return res;
   }
-  var atoms = wordObj("NULL NA Inf NaN NA_integer_ NA_real_ NA_complex_ NA_character_");
-  var builtins = wordObj("list quote bquote eval return call parse deparse");
-  var keywords = wordObj("if else repeat while function for in next break");
-  var blockkeywords = wordObj("if else repeat while function for");
+  var commonAtoms = ["NULL", "NA", "Inf", "NaN", "NA_integer_", "NA_real_", "NA_complex_", "NA_character_", "TRUE", "FALSE"];
+  var commonBuiltins = ["list", "quote", "bquote", "eval", "return", "call", "parse", "deparse"];
+  var commonKeywords = ["if", "else", "repeat", "while", "function", "for", "in", "next", "break"];
+  var commonBlockKeywords = ["if", "else", "repeat", "while", "function", "for"];
+
+  CodeMirror.registerHelper("hintWords", "r", commonAtoms.concat(commonBuiltins, commonKeywords));
+
+  var atoms = wordObj(commonAtoms);
+  var builtins = wordObj(commonBuiltins);
+  var keywords = wordObj(commonKeywords);
+  var blockkeywords = wordObj(commonBlockKeywords);
   var opChars = /[+\-*\/^<>=!&|~$:]/;
   var curPunc;
 
@@ -44,6 +51,9 @@ CodeMirror.defineMode("r", function(config) {
     } else if (ch == "'" || ch == '"') {
       state.tokenize = tokenString(ch);
       return "string";
+    } else if (ch == "`") {
+      stream.match(/[^`]+`/);
+      return "variable-3";
     } else if (ch == "." && stream.match(/.[.\d]+/)) {
       return "keyword";
     } else if (/[\w\.]/.test(ch) && ch != "_") {
@@ -62,13 +72,17 @@ CodeMirror.defineMode("r", function(config) {
       return "variable";
     } else if (ch == "%") {
       if (stream.skipTo("%")) stream.next();
-      return "variable-2";
-    } else if (ch == "<" && stream.eat("-")) {
-      return "arrow";
+      return "operator variable-2";
+    } else if (
+        (ch == "<" && stream.eat("-")) ||
+        (ch == "<" && stream.match("<-")) ||
+        (ch == "-" && stream.match(/>>?/))
+      ) {
+      return "operator arrow";
     } else if (ch == "=" && state.ctx.argList) {
       return "arg-is";
     } else if (opChars.test(ch)) {
-      if (ch == "$") return "dollar";
+      if (ch == "$") return "operator dollar";
       stream.eatWhile(opChars);
       return "operator";
     } else if (/[\(\){}\[\];]/.test(ch)) {
@@ -101,13 +115,23 @@ CodeMirror.defineMode("r", function(config) {
     };
   }
 
+  var ALIGN_YES = 1, ALIGN_NO = 2, BRACELESS = 4
+
   function push(state, type, stream) {
     state.ctx = {type: type,
                  indent: state.indent,
-                 align: null,
+                 flags: 0,
                  column: stream.column(),
                  prev: state.ctx};
   }
+  function setFlag(state, flag) {
+    var ctx = state.ctx
+    state.ctx = {type: ctx.type,
+                 indent: ctx.indent,
+                 flags: ctx.flags | flag,
+                 column: ctx.column,
+                 prev: ctx.prev}
+  }
   function pop(state) {
     state.indent = state.ctx.indent;
     state.ctx = state.ctx.prev;
@@ -118,22 +142,22 @@ CodeMirror.defineMode("r", function(config) {
       return {tokenize: tokenBase,
               ctx: {type: "top",
                     indent: -config.indentUnit,
-                    align: false},
+                    flags: ALIGN_NO},
               indent: 0,
               afterIdent: false};
     },
 
     token: function(stream, state) {
       if (stream.sol()) {
-        if (state.ctx.align == null) state.ctx.align = false;
+        if ((state.ctx.flags & 3) == 0) state.ctx.flags |= ALIGN_NO
+        if (state.ctx.flags & BRACELESS) pop(state)
         state.indent = stream.indentation();
       }
       if (stream.eatSpace()) return null;
       var style = state.tokenize(stream, state);
-      if (style != "comment" && state.ctx.align == null) state.ctx.align = true;
+      if (style != "comment" && (state.ctx.flags & ALIGN_NO) == 0) setFlag(state, ALIGN_YES)
 
-      var ctype = state.ctx.type;
-      if ((curPunc == ";" || curPunc == "{" || curPunc == "}") && ctype == "block") pop(state);
+      if ((curPunc == ";" || curPunc == "{" || curPunc == "}") && state.ctx.type == "block") pop(state);
       if (curPunc == "{") push(state, "}", stream);
       else if (curPunc == "(") {
         push(state, ")", stream);
@@ -141,7 +165,8 @@ CodeMirror.defineMode("r", function(config) {
       }
       else if (curPunc == "[") push(state, "]", stream);
       else if (curPunc == "block") push(state, "block", stream);
-      else if (curPunc == ctype) pop(state);
+      else if (curPunc == state.ctx.type) pop(state);
+      else if (state.ctx.type == "block" && style != "comment") setFlag(state, BRACELESS)
       state.afterIdent = style == "variable" || style == "keyword";
       return style;
     },
@@ -150,8 +175,9 @@ CodeMirror.defineMode("r", function(config) {
       if (state.tokenize != tokenBase) return 0;
       var firstChar = textAfter && textAfter.charAt(0), ctx = state.ctx,
           closing = firstChar == ctx.type;
+      if (ctx.flags & BRACELESS) ctx = ctx.prev
       if (ctx.type == "block") return ctx.indent + (firstChar == "{" ? 0 : config.indentUnit);
-      else if (ctx.align) return ctx.column + (closing ? 0 : 1);
+      else if (ctx.flags & ALIGN_YES) return ctx.column + (closing ? 0 : 1);
       else return ctx.indent + (closing ? 0 : config.indentUnit);
     },
 
diff --git a/public/vendor/plugins/codemirror/mode/rpm/changes/index.html b/public/vendor/plugins/codemirror/mode/rpm/changes/index.html
index 6e5031bd1f..9d244ecc85 100644
--- a/public/vendor/plugins/codemirror/mode/rpm/changes/index.html
+++ b/public/vendor/plugins/codemirror/mode/rpm/changes/index.html
@@ -8,10 +8,10 @@
     <script src="../../../lib/codemirror.js"></script>
     <script src="changes.js"></script>
     <link rel="stylesheet" href="../../../doc/docs.css">
-    <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+    <style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/rpm/index.html b/public/vendor/plugins/codemirror/mode/rpm/index.html
index 9a34e6dfb8..e20e53debc 100644
--- a/public/vendor/plugins/codemirror/mode/rpm/index.html
+++ b/public/vendor/plugins/codemirror/mode/rpm/index.html
@@ -8,10 +8,10 @@
     <script src="../../lib/codemirror.js"></script>
     <script src="rpm.js"></script>
     <link rel="stylesheet" href="../../doc/docs.css">
-    <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+    <style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/rpm/rpm.js b/public/vendor/plugins/codemirror/mode/rpm/rpm.js
index 87cde591a3..2dece2eabd 100644
--- a/public/vendor/plugins/codemirror/mode/rpm/rpm.js
+++ b/public/vendor/plugins/codemirror/mode/rpm/rpm.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/rst/index.html b/public/vendor/plugins/codemirror/mode/rst/index.html
index 2902dea231..1ced39ca96 100644
--- a/public/vendor/plugins/codemirror/mode/rst/index.html
+++ b/public/vendor/plugins/codemirror/mode/rst/index.html
@@ -8,9 +8,9 @@
 <script src="../../lib/codemirror.js"></script>
 <script src="../../addon/mode/overlay.js"></script>
 <script src="rst.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/rst/rst.js b/public/vendor/plugins/codemirror/mode/rst/rst.js
index bcf110c1a5..f14eb270f9 100644
--- a/public/vendor/plugins/codemirror/mode/rst/rst.js
+++ b/public/vendor/plugins/codemirror/mode/rst/rst.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/ruby/index.html b/public/vendor/plugins/codemirror/mode/ruby/index.html
index 97544babc3..486b1e2454 100644
--- a/public/vendor/plugins/codemirror/mode/ruby/index.html
+++ b/public/vendor/plugins/codemirror/mode/ruby/index.html
@@ -13,7 +13,7 @@
       .cm-s-default span.cm-arrow { color: red; }
     </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/ruby/ruby.js b/public/vendor/plugins/codemirror/mode/ruby/ruby.js
index 10cad8d9f1..dd0e603e51 100644
--- a/public/vendor/plugins/codemirror/mode/ruby/ruby.js
+++ b/public/vendor/plugins/codemirror/mode/ruby/ruby.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -28,7 +28,8 @@ CodeMirror.defineMode("ruby", function(config) {
   var indentWords = wordObj(["def", "class", "case", "for", "while", "until", "module", "then",
                              "catch", "loop", "proc", "begin"]);
   var dedentWords = wordObj(["end", "until"]);
-  var matching = {"[": "]", "{": "}", "(": ")"};
+  var opening = {"[": "]", "{": "}", "(": ")"};
+  var closing = {"]": "[", "}": "{", ")": "("};
   var curPunc;
 
   function chain(newtok, stream, state) {
@@ -46,22 +47,10 @@ CodeMirror.defineMode("ruby", function(config) {
     if (ch == "`" || ch == "'" || ch == '"') {
       return chain(readQuoted(ch, "string", ch == '"' || ch == "`"), stream, state);
     } else if (ch == "/") {
-      var currentIndex = stream.current().length;
-      if (stream.skipTo("/")) {
-        var search_till = stream.current().length;
-        stream.backUp(stream.current().length - currentIndex);
-        var balance = 0;  // balance brackets
-        while (stream.current().length < search_till) {
-          var chchr = stream.next();
-          if (chchr == "(") balance += 1;
-          else if (chchr == ")") balance -= 1;
-          if (balance < 0) break;
-        }
-        stream.backUp(stream.current().length - currentIndex);
-        if (balance == 0)
-          return chain(readQuoted(ch, "string-2", true), stream, state);
-      }
-      return "operator";
+      if (regexpAhead(stream))
+        return chain(readQuoted(ch, "string-2", true), stream, state);
+      else
+        return "operator";
     } else if (ch == "%") {
       var style = "string", embed = true;
       if (stream.eat("s")) style = "atom";
@@ -70,13 +59,13 @@ CodeMirror.defineMode("ruby", function(config) {
       else if (stream.eat(/[wxq]/)) { style = "string"; embed = false; }
       var delim = stream.eat(/[^\w\s=]/);
       if (!delim) return "operator";
-      if (matching.propertyIsEnumerable(delim)) delim = matching[delim];
+      if (opening.propertyIsEnumerable(delim)) delim = opening[delim];
       return chain(readQuoted(delim, style, embed, true), stream, state);
     } else if (ch == "#") {
       stream.skipToEnd();
       return "comment";
-    } else if (ch == "<" && (m = stream.match(/^<-?[\`\"\']?([a-zA-Z_?]\w*)[\`\"\']?(?:;|$)/))) {
-      return chain(readHereDoc(m[1]), stream, state);
+    } else if (ch == "<" && (m = stream.match(/^<([-~])[\`\"\']?([a-zA-Z_?]\w*)[\`\"\']?(?:;|$)/))) {
+      return chain(readHereDoc(m[2], m[1]), stream, state);
     } else if (ch == "0") {
       if (stream.eat("x")) stream.eatWhile(/[\da-fA-F]/);
       else if (stream.eat("b")) stream.eatWhile(/[01]/);
@@ -148,6 +137,28 @@ CodeMirror.defineMode("ruby", function(config) {
     }
   }
 
+  function regexpAhead(stream) {
+    var start = stream.pos, depth = 0, next, found = false, escaped = false
+    while ((next = stream.next()) != null) {
+      if (!escaped) {
+        if ("[{(".indexOf(next) > -1) {
+          depth++
+        } else if ("]})".indexOf(next) > -1) {
+          depth--
+          if (depth < 0) break
+        } else if (next == "/" && depth == 0) {
+          found = true
+          break
+        }
+        escaped = next == "\\"
+      } else {
+        escaped = false
+      }
+    }
+    stream.backUp(stream.pos - start)
+    return found
+  }
+
   function tokenBaseUntilBrace(depth) {
     if (!depth) depth = 1;
     return function(stream, state) {
@@ -206,8 +217,9 @@ CodeMirror.defineMode("ruby", function(config) {
       return style;
     };
   }
-  function readHereDoc(phrase) {
+  function readHereDoc(phrase, mayIndent) {
     return function(stream, state) {
+      if (mayIndent) stream.eatSpace()
       if (stream.match(phrase)) state.tokenize.pop();
       else stream.skipToEnd();
       return "string";
@@ -266,17 +278,18 @@ CodeMirror.defineMode("ruby", function(config) {
     },
 
     indent: function(state, textAfter) {
-      if (state.tokenize[state.tokenize.length-1] != tokenBase) return 0;
+      if (state.tokenize[state.tokenize.length-1] != tokenBase) return CodeMirror.Pass;
       var firstChar = textAfter && textAfter.charAt(0);
       var ct = state.context;
-      var closing = ct.type == matching[firstChar] ||
+      var closed = ct.type == closing[firstChar] ||
         ct.type == "keyword" && /^(?:end|until|else|elsif|when|rescue)\b/.test(textAfter);
-      return ct.indented + (closing ? 0 : config.indentUnit) +
+      return ct.indented + (closed ? 0 : config.indentUnit) +
         (state.continuedLine ? config.indentUnit : 0);
     },
 
-    electricInput: /^\s*(?:end|rescue|\})$/,
-    lineComment: "#"
+    electricInput: /^\s*(?:end|rescue|elsif|else|\})$/,
+    lineComment: "#",
+    fold: "indent"
   };
 });
 
diff --git a/public/vendor/plugins/codemirror/mode/ruby/test.js b/public/vendor/plugins/codemirror/mode/ruby/test.js
index cade864ff9..905c0e4849 100644
--- a/public/vendor/plugins/codemirror/mode/ruby/test.js
+++ b/public/vendor/plugins/codemirror/mode/ruby/test.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function() {
   var mode = CodeMirror.getMode({indentUnit: 2}, "ruby");
@@ -11,4 +11,13 @@
   MT("divide_equal_operator_no_spacing",
      "[variable foo][operator /=][number 42]");
 
+  MT("complex_regexp",
+     "[keyword if] [variable cr] [operator =~] [string-2 /(?: \\( #{][tag RE_NOT][string-2 }\\( | #{][tag RE_NOT_PAR_OR][string-2 }* #{][tag RE_OPA_OR][string-2 } )/][variable x]")
+
+  MT("indented_heredoc",
+     "[keyword def] [def x]",
+     "  [variable y] [operator =] [string <<-FOO]",
+     "[string     bar]",
+     "[string   FOO]",
+     "[keyword end]")
 })();
diff --git a/public/vendor/plugins/codemirror/mode/rust/index.html b/public/vendor/plugins/codemirror/mode/rust/index.html
index 1fe0ad1e66..ce9789851f 100644
--- a/public/vendor/plugins/codemirror/mode/rust/index.html
+++ b/public/vendor/plugins/codemirror/mode/rust/index.html
@@ -8,9 +8,9 @@
 <script src="../../lib/codemirror.js"></script>
 <script src="../../addon/mode/simple.js"></script>
 <script src="rust.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/rust/rust.js b/public/vendor/plugins/codemirror/mode/rust/rust.js
index 8558b53fe4..6bcfbc4446 100644
--- a/public/vendor/plugins/codemirror/mode/rust/rust.js
+++ b/public/vendor/plugins/codemirror/mode/rust/rust.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -68,4 +68,5 @@ CodeMirror.defineSimpleMode("rust",{
 
 
 CodeMirror.defineMIME("text/x-rustsrc", "rust");
+CodeMirror.defineMIME("text/rust", "rust");
 });
diff --git a/public/vendor/plugins/codemirror/mode/rust/test.js b/public/vendor/plugins/codemirror/mode/rust/test.js
index eb256c47e8..36c5cdeba8 100644
--- a/public/vendor/plugins/codemirror/mode/rust/test.js
+++ b/public/vendor/plugins/codemirror/mode/rust/test.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function() {
   var mode = CodeMirror.getMode({indentUnit: 4}, "rust");
diff --git a/public/vendor/plugins/codemirror/mode/sas/index.html b/public/vendor/plugins/codemirror/mode/sas/index.html
index 636e06594b..b8627484e0 100644
--- a/public/vendor/plugins/codemirror/mode/sas/index.html
+++ b/public/vendor/plugins/codemirror/mode/sas/index.html
@@ -8,14 +8,14 @@
 <script src="../../lib/codemirror.js"></script>
 <script src="../xml/xml.js"></script>
 <script src="sas.js"></script>
-<style type="text/css">
+<style>
   .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
   .cm-s-default .cm-trailing-space-a:before,
   .cm-s-default .cm-trailing-space-b:before {position: absolute; content: "\00B7"; color: #777;}
   .cm-s-default .cm-trailing-space-new-line:before {position: absolute; content: "\21B5"; color: #777;}
 </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/sas/sas.js b/public/vendor/plugins/codemirror/mode/sas/sas.js
index fe114827c0..c6f528e0a9 100644
--- a/public/vendor/plugins/codemirror/mode/sas/sas.js
+++ b/public/vendor/plugins/codemirror/mode/sas/sas.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 
 // SAS mode copyright (c) 2016 Jared Dean, SAS Institute
@@ -11,7 +11,7 @@
 
 
 //Definitions
-//  comment -- text withing * ; or /* */
+//  comment -- text within * ; or /* */
 //  keyword -- SAS language variable
 //  variable -- macro variables starts with '&' or variable formats
 //  variable-2 -- DATA Step, proc, or macro names
@@ -116,33 +116,21 @@
         return "comment";
       }
 
+      if (ch == "*" && stream.column() == stream.indentation()) {
+        stream.skipToEnd()
+        return "comment"
+      }
+
       // DoubleOperator match
       var doubleOperator = ch + stream.peek();
 
-      // Match all line comments.
-      var myString = stream.string;
-      var myRegexp = /(?:^\s*|[;]\s*)(\*.*?);/ig;
-      var match = myRegexp.exec(myString);
-      if (match !== null) {
-        if (match.index === 0 && (stream.column() !== (match.index + match[0].length - 1))) {
-          stream.backUp(stream.column());
-          stream.skipTo(';');
-          stream.next();
-          return 'comment';
-        } else if (match.index + 1 < stream.column() && stream.column() < match.index + match[0].length - 1) {
-          // the ';' triggers the match so move one past it to start
-          // the comment block that is why match.index+1
-          stream.backUp(stream.column() - match.index - 1);
-          stream.skipTo(';');
-          stream.next();
-          return 'comment';
-        }
-      } else if (!state.continueString && (ch === '"' || ch === "'")) {
-        // Have we found a string?
-        state.continueString = ch; //save the matching quote in the state
-        return "string";
-      } else if (state.continueString !== null) {
-        if (stream.skipTo(state.continueString)) {
+      if ((ch === '"' || ch === "'") && !state.continueString) {
+        state.continueString = ch
+        return "string"
+      } else if (state.continueString) {
+        if (state.continueString == ch) {
+          state.continueString = null;
+        } else if (stream.skipTo(state.continueString)) {
           // quote found on this line
           stream.next();
           state.continueString = null;
@@ -187,12 +175,12 @@
         if (stream.peek() === '.') stream.skipTo(' ');
         state.nextword = false;
         return 'variable-2';
-
       }
 
+      word = word.toLowerCase()
       // Are we in a DATA Step?
       if (state.inDataStep) {
-        if (word.toLowerCase() === 'run;' || stream.match(/run\s;/)) {
+        if (word === 'run;' || stream.match(/run\s;/)) {
           state.inDataStep = false;
           return 'builtin';
         }
@@ -203,84 +191,84 @@
           else return 'variable';
         }
         // do we have a DATA Step keyword
-        if (word && words.hasOwnProperty(word.toLowerCase()) &&
-            (words[word.toLowerCase()].state.indexOf("inDataStep") !== -1 ||
-             words[word.toLowerCase()].state.indexOf("ALL") !== -1)) {
+        if (word && words.hasOwnProperty(word) &&
+            (words[word].state.indexOf("inDataStep") !== -1 ||
+             words[word].state.indexOf("ALL") !== -1)) {
           //backup to the start of the word
           if (stream.start < stream.pos)
             stream.backUp(stream.pos - stream.start);
           //advance the length of the word and return
           for (var i = 0; i < word.length; ++i) stream.next();
-          return words[word.toLowerCase()].style;
+          return words[word].style;
         }
       }
       // Are we in an Proc statement?
       if (state.inProc) {
-        if (word.toLowerCase() === 'run;' || word.toLowerCase() === 'quit;') {
+        if (word === 'run;' || word === 'quit;') {
           state.inProc = false;
           return 'builtin';
         }
         // do we have a proc keyword
-        if (word && words.hasOwnProperty(word.toLowerCase()) &&
-            (words[word.toLowerCase()].state.indexOf("inProc") !== -1 ||
-             words[word.toLowerCase()].state.indexOf("ALL") !== -1)) {
+        if (word && words.hasOwnProperty(word) &&
+            (words[word].state.indexOf("inProc") !== -1 ||
+             words[word].state.indexOf("ALL") !== -1)) {
           stream.match(/[\w]+/);
           return words[word].style;
         }
       }
       // Are we in a Macro statement?
       if (state.inMacro) {
-        if (word.toLowerCase() === '%mend') {
+        if (word === '%mend') {
           if (stream.peek() === ';') stream.next();
           state.inMacro = false;
           return 'builtin';
         }
-        if (word && words.hasOwnProperty(word.toLowerCase()) &&
-            (words[word.toLowerCase()].state.indexOf("inMacro") !== -1 ||
-             words[word.toLowerCase()].state.indexOf("ALL") !== -1)) {
+        if (word && words.hasOwnProperty(word) &&
+            (words[word].state.indexOf("inMacro") !== -1 ||
+             words[word].state.indexOf("ALL") !== -1)) {
           stream.match(/[\w]+/);
-          return words[word.toLowerCase()].style;
+          return words[word].style;
         }
 
         return 'atom';
       }
       // Do we have Keywords specific words?
-      if (word && words.hasOwnProperty(word.toLowerCase())) {
+      if (word && words.hasOwnProperty(word)) {
         // Negates the initial next()
         stream.backUp(1);
         // Actually move the stream
         stream.match(/[\w]+/);
-        if (word.toLowerCase() === 'data' && /=/.test(stream.peek()) === false) {
+        if (word === 'data' && /=/.test(stream.peek()) === false) {
           state.inDataStep = true;
           state.nextword = true;
           return 'builtin';
         }
-        if (word.toLowerCase() === 'proc') {
+        if (word === 'proc') {
           state.inProc = true;
           state.nextword = true;
           return 'builtin';
         }
-        if (word.toLowerCase() === '%macro') {
+        if (word === '%macro') {
           state.inMacro = true;
           state.nextword = true;
           return 'builtin';
         }
-        if (/title[1-9]/i.test(word)) return 'def';
+        if (/title[1-9]/.test(word)) return 'def';
 
-        if (word.toLowerCase() === 'footnote') {
+        if (word === 'footnote') {
           stream.eat(/[1-9]/);
           return 'def';
         }
 
         // Returns their value as state in the prior define methods
-        if (state.inDataStep === true && words[word.toLowerCase()].state.indexOf("inDataStep") !== -1)
-          return words[word.toLowerCase()].style;
-        if (state.inProc === true && words[word.toLowerCase()].state.indexOf("inProc") !== -1)
-          return words[word.toLowerCase()].style;
-        if (state.inMacro === true && words[word.toLowerCase()].state.indexOf("inMacro") !== -1)
-          return words[word.toLowerCase()].style;
-        if (words[word.toLowerCase()].state.indexOf("ALL") !== -1)
-          return words[word.toLowerCase()].style;
+        if (state.inDataStep === true && words[word].state.indexOf("inDataStep") !== -1)
+          return words[word].style;
+        if (state.inProc === true && words[word].state.indexOf("inProc") !== -1)
+          return words[word].style;
+        if (state.inMacro === true && words[word].state.indexOf("inMacro") !== -1)
+          return words[word].style;
+        if (words[word].state.indexOf("ALL") !== -1)
+          return words[word].style;
         return null;
       }
       // Unrecognized syntax
diff --git a/public/vendor/plugins/codemirror/mode/sass/index.html b/public/vendor/plugins/codemirror/mode/sass/index.html
index 9f4a790221..0db5eacbcf 100644
--- a/public/vendor/plugins/codemirror/mode/sass/index.html
+++ b/public/vendor/plugins/codemirror/mode/sass/index.html
@@ -7,10 +7,11 @@
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
 <script src="../../addon/edit/matchbrackets.js"></script>
+<script src="../css/css.js"></script>
 <script src="sass.js"></script>
 <style>.CodeMirror {border: 1px solid #ddd; font-size:12px; height: 400px}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -58,7 +59,8 @@ body
     <script>
       var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
         lineNumbers : true,
-        matchBrackets : true
+        matchBrackets : true,
+        mode: "sass"
       });
     </script>
 
diff --git a/public/vendor/plugins/codemirror/mode/sass/sass.js b/public/vendor/plugins/codemirror/mode/sass/sass.js
index 6973ece292..c37ab0b28f 100644
--- a/public/vendor/plugins/codemirror/mode/sass/sass.js
+++ b/public/vendor/plugins/codemirror/mode/sass/sass.js
@@ -1,17 +1,23 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
-    mod(require("../../lib/codemirror"));
+    mod(require("../../lib/codemirror"), require("../css/css"));
   else if (typeof define == "function" && define.amd) // AMD
-    define(["../../lib/codemirror"], mod);
+    define(["../../lib/codemirror", "../css/css"], mod);
   else // Plain browser env
     mod(CodeMirror);
 })(function(CodeMirror) {
 "use strict";
 
 CodeMirror.defineMode("sass", function(config) {
+  var cssMode = CodeMirror.mimeModes["text/css"];
+  var propertyKeywords = cssMode.propertyKeywords || {},
+      colorKeywords = cssMode.colorKeywords || {},
+      valueKeywords = cssMode.valueKeywords || {},
+      fontProperties = cssMode.fontProperties || {};
+
   function tokenRegexp(words) {
     return new RegExp("^" + words.join("|"));
   }
@@ -25,6 +31,12 @@ CodeMirror.defineMode("sass", function(config) {
 
   var pseudoElementsRegexp = /^::?[a-zA-Z_][\w\-]*/;
 
+  var word;
+
+  function isEndLine(stream) {
+    return !stream.peek() || stream.match(/\s+$/, false);
+  }
+
   function urlTokens(stream, state) {
     var ch = stream.peek();
 
@@ -76,6 +88,9 @@ CodeMirror.defineMode("sass", function(config) {
 
       if (endingString) {
         if (nextChar !== quote && greedy) { stream.next(); }
+        if (isEndLine(stream)) {
+          state.cursorHalf = 0;
+        }
         state.tokenizer = tokenBase;
         return "string";
       } else if (nextChar === "#" && peekChar === "{") {
@@ -147,14 +162,20 @@ CodeMirror.defineMode("sass", function(config) {
     // first half i.e. before : for key-value pairs
     // including selectors
 
+      if (ch === "-") {
+        if (stream.match(/^-\w+-/)) {
+          return "meta";
+        }
+      }
+
       if (ch === ".") {
         stream.next();
         if (stream.match(/^[\w-]+/)) {
           indent(state);
-          return "atom";
+          return "qualifier";
         } else if (stream.peek() === "#") {
           indent(state);
-          return "atom";
+          return "tag";
         }
       }
 
@@ -163,11 +184,11 @@ CodeMirror.defineMode("sass", function(config) {
         // ID selectors
         if (stream.match(/^[\w-]+/)) {
           indent(state);
-          return "atom";
+          return "builtin";
         }
         if (stream.peek() === "#") {
           indent(state);
-          return "atom";
+          return "tag";
         }
       }
 
@@ -220,37 +241,48 @@ CodeMirror.defineMode("sass", function(config) {
       // Indent Directives
       if (stream.match(/^@(else if|if|media|else|for|each|while|mixin|function)/)) {
         indent(state);
-        return "meta";
+        return "def";
       }
 
       // Other Directives
       if (ch === "@") {
         stream.next();
         stream.eatWhile(/[\w-]/);
-        return "meta";
+        return "def";
       }
 
       if (stream.eatWhile(/[\w-]/)){
         if(stream.match(/ *: *[\w-\+\$#!\("']/,false)){
-          return "property";
+          word = stream.current().toLowerCase();
+          var prop = state.prevProp + "-" + word;
+          if (propertyKeywords.hasOwnProperty(prop)) {
+            return "property";
+          } else if (propertyKeywords.hasOwnProperty(word)) {
+            state.prevProp = word;
+            return "property";
+          } else if (fontProperties.hasOwnProperty(word)) {
+            return "property";
+          }
+          return "tag";
         }
         else if(stream.match(/ *:/,false)){
           indent(state);
           state.cursorHalf = 1;
-          return "atom";
+          state.prevProp = stream.current().toLowerCase();
+          return "property";
         }
         else if(stream.match(/ *,/,false)){
-          return "atom";
+          return "tag";
         }
         else{
           indent(state);
-          return "atom";
+          return "tag";
         }
       }
 
       if(ch === ":"){
         if (stream.match(pseudoElementsRegexp)){ // could be a pseudo-element
-          return "keyword";
+          return "variable-3";
         }
         stream.next();
         state.cursorHalf=1;
@@ -264,7 +296,7 @@ CodeMirror.defineMode("sass", function(config) {
         stream.next();
         // Hex numbers
         if (stream.match(/[0-9a-fA-F]{6}|[0-9a-fA-F]{3}/)){
-          if(!stream.peek()){
+          if (isEndLine(stream)) {
             state.cursorHalf = 0;
           }
           return "number";
@@ -273,7 +305,7 @@ CodeMirror.defineMode("sass", function(config) {
 
       // Numbers
       if (stream.match(/^-?[0-9\.]+/)){
-        if(!stream.peek()){
+        if (isEndLine(stream)) {
           state.cursorHalf = 0;
         }
         return "number";
@@ -281,14 +313,14 @@ CodeMirror.defineMode("sass", function(config) {
 
       // Units
       if (stream.match(/^(px|em|in)\b/)){
-        if(!stream.peek()){
+        if (isEndLine(stream)) {
           state.cursorHalf = 0;
         }
         return "unit";
       }
 
       if (stream.match(keywordsRegexp)){
-        if(!stream.peek()){
+        if (isEndLine(stream)) {
           state.cursorHalf = 0;
         }
         return "keyword";
@@ -296,7 +328,7 @@ CodeMirror.defineMode("sass", function(config) {
 
       if (stream.match(/^url/) && stream.peek() === "(") {
         state.tokenizer = urlTokens;
-        if(!stream.peek()){
+        if (isEndLine(stream)) {
           state.cursorHalf = 0;
         }
         return "atom";
@@ -306,23 +338,21 @@ CodeMirror.defineMode("sass", function(config) {
       if (ch === "$") {
         stream.next();
         stream.eatWhile(/[\w-]/);
-        if(!stream.peek()){
+        if (isEndLine(stream)) {
           state.cursorHalf = 0;
         }
-        return "variable-3";
+        return "variable-2";
       }
 
       // bang character for !important, !default, etc.
       if (ch === "!") {
         stream.next();
-        if(!stream.peek()){
-          state.cursorHalf = 0;
-        }
+        state.cursorHalf = 0;
         return stream.match(/^[\w]+/) ? "keyword": "operator";
       }
 
       if (stream.match(opRegexp)){
-        if(!stream.peek()){
+        if (isEndLine(stream)) {
           state.cursorHalf = 0;
         }
         return "operator";
@@ -330,14 +360,24 @@ CodeMirror.defineMode("sass", function(config) {
 
       // attributes
       if (stream.eatWhile(/[\w-]/)) {
-        if(!stream.peek()){
+        if (isEndLine(stream)) {
           state.cursorHalf = 0;
         }
-        return "attribute";
+        word = stream.current().toLowerCase();
+        if (valueKeywords.hasOwnProperty(word)) {
+          return "atom";
+        } else if (colorKeywords.hasOwnProperty(word)) {
+          return "keyword";
+        } else if (propertyKeywords.hasOwnProperty(word)) {
+          state.prevProp = stream.current().toLowerCase();
+          return "property";
+        } else {
+          return "tag";
+        }
       }
 
       //stream.eatSpace();
-      if(!stream.peek()){
+      if (isEndLine(stream)) {
         state.cursorHalf = 0;
         return null;
       }
@@ -407,7 +447,7 @@ CodeMirror.defineMode("sass", function(config) {
       return state.scopes[0].offset;
     }
   };
-});
+}, "css");
 
 CodeMirror.defineMIME("text/x-sass", "sass");
 
diff --git a/public/vendor/plugins/codemirror/mode/sass/test.js b/public/vendor/plugins/codemirror/mode/sass/test.js
new file mode 100644
index 0000000000..63d79193b4
--- /dev/null
+++ b/public/vendor/plugins/codemirror/mode/sass/test.js
@@ -0,0 +1,122 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function() {
+  var mode = CodeMirror.getMode({indentUnit: 2}, "sass");
+  // Since Sass has an indent-based syntax, is almost impossible to test correctly the indentation in all cases.
+  // So disable it for tests.
+  mode.indent = undefined;
+  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
+
+  MT("comment",
+     "[comment // this is a comment]",
+     "[comment   also this is a comment]")
+
+  MT("comment_multiline",
+     "[comment /* this is a comment]",
+     "[comment   also this is a comment]")
+
+  MT("variable",
+     "[variable-2 $page-width][operator :] [number 800][unit px]")
+
+  MT("global_attributes",
+     "[tag body]",
+     "  [property font][operator :]",
+     "    [property family][operator :] [atom sans-serif]",
+     "    [property size][operator :] [number 30][unit em]",
+     "    [property weight][operator :] [atom bold]")
+
+  MT("scoped_styles",
+     "[builtin #contents]",
+     "  [property width][operator :] [variable-2 $page-width]",
+     "  [builtin #sidebar]",
+     "    [property float][operator :] [atom right]",
+     "    [property width][operator :] [variable-2 $sidebar-width]",
+     "  [builtin #main]",
+     "    [property width][operator :] [variable-2 $page-width] [operator -] [variable-2 $sidebar-width]",
+     "    [property background][operator :] [variable-2 $primary-color]",
+     "    [tag h2]",
+     "      [property color][operator :] [keyword blue]")
+
+  // Sass allows to write the colon as first char instead of a "separator".
+  //   :color red
+  // Not supported
+  // MT("property_syntax",
+  //    "[qualifier .foo]",
+  //    "  [operator :][property color] [keyword red]")
+
+  MT("import",
+     "[def @import] [string \"sass/variables\"]",
+     // Probably it should parsed as above: as a string even without the " or '
+     // "[def @import] [string sass/baz]"
+     "[def @import] [tag sass][operator /][tag baz]")
+
+  MT("def",
+     "[def @if] [variable-2 $foo] [def @else]")
+
+  MT("tag_on_more_lines",
+    "[tag td],",
+    "[tag th]",
+    "  [property font-family][operator :] [string \"Arial\"], [atom serif]")
+
+  MT("important",
+     "[qualifier .foo]",
+     "  [property text-decoration][operator :] [atom none] [keyword !important]",
+     "[tag h1]",
+     "  [property font-size][operator :] [number 2.5][unit em]")
+
+  MT("selector",
+     // SCSS doesn't highlight the :
+     // "[tag h1]:[variable-3 before],",
+     // "[tag h2]:[variable-3 before]",
+     "[tag h1][variable-3 :before],",
+     "[tag h2][variable-3 :before]",
+     "  [property content][operator :] [string \"::\"]")
+
+  MT("definition_mixin_equal",
+     "[variable-2 $defined-bs-type][operator :] [atom border-box] [keyword !default]",
+     "[meta =bs][operator (][variable-2 $bs-type][operator :] [variable-2 $defined-bs-type][operator )]",
+     "  [meta -webkit-][property box-sizing][operator :] [variable-2 $bs-type]",
+     "  [property box-sizing][operator :] [variable-2 $bs-type]")
+
+  MT("definition_mixin_with_space",
+     "[variable-2 $defined-bs-type][operator :] [atom border-box] [keyword !default]",
+     "[def @mixin] [tag bs][operator (][variable-2 $bs-type][operator :] [variable-2 $defined-bs-type][operator )] ",
+     "  [meta -moz-][property box-sizing][operator :] [variable-2 $bs-type]",
+     "  [property box-sizing][operator :] [variable-2 $bs-type]")
+
+  MT("numbers_start_dot_include_plus",
+     // The % is not highlighted correctly
+     // "[meta =button-links][operator (][variable-2 $button-base][operator :] [atom darken][operator (][variable-2 $color11], [number 10][unit %][operator )][operator )]",
+     "[meta =button-links][operator (][variable-2 $button-base][operator :] [atom darken][operator (][variable-2 $color11], [number 10][operator %))]",
+     "  [property padding][operator :] [number .3][unit em] [number .6][unit em]",
+     "  [variable-3 +border-radius][operator (][number 8][unit px][operator )]",
+     "  [property background-color][operator :] [variable-2 $button-base]")
+
+  MT("include",
+     "[qualifier .bar]",
+     "  [def @include] [tag border-radius][operator (][number 8][unit px][operator )]")
+
+  MT("reference_parent",
+     "[qualifier .col]",
+     "  [property clear][operator :] [atom both]",
+     // SCSS doesn't highlight the :
+     // "  &:[variable-3 after]",
+     "  &[variable-3 :after]",
+     "    [property content][operator :] [string '']",
+     "    [property clear][operator :] [atom both]")
+
+  MT("reference_parent_with_spaces",
+     "[tag section]",
+     "  [property border-left][operator :]  [number 20][unit px] [atom transparent] [atom solid] ",
+     "  &[qualifier .section3]",
+     "    [qualifier .title]",
+     "      [property color][operator :] [keyword white] ",
+     "    [qualifier .vermas]",
+     "      [property display][operator :] [atom none]")
+
+  MT("font_face",
+     "[def @font-face]",
+     "  [property font-family][operator :] [string 'icomoon']",
+     "  [property src][operator :] [atom url][operator (][string fonts/icomoon.ttf][operator )]")
+})();
diff --git a/public/vendor/plugins/codemirror/mode/scheme/index.html b/public/vendor/plugins/codemirror/mode/scheme/index.html
index 04d5c6a2a3..f2eda51d66 100644
--- a/public/vendor/plugins/codemirror/mode/scheme/index.html
+++ b/public/vendor/plugins/codemirror/mode/scheme/index.html
@@ -9,7 +9,7 @@
 <script src="scheme.js"></script>
 <style>.CodeMirror {background: #f8f8f8;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/scheme/scheme.js b/public/vendor/plugins/codemirror/mode/scheme/scheme.js
index 2234645911..56e4e332e9 100644
--- a/public/vendor/plugins/codemirror/mode/scheme/scheme.js
+++ b/public/vendor/plugins/codemirror/mode/scheme/scheme.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 /**
  * Author: Koh Zi Han, based on implementation by Koh Zi Chun
@@ -73,7 +73,8 @@ CodeMirror.defineMode("scheme", function () {
                 indentStack: null,
                 indentation: 0,
                 mode: false,
-                sExprComment: false
+                sExprComment: false,
+                sExprQuote: false
             };
         },
 
@@ -121,7 +122,7 @@ CodeMirror.defineMode("scheme", function () {
                         state.sExprComment = 0;
                     }else{
                         // if not we just comment the entire of the next token
-                        stream.eatWhile(/[^/s]/); // eat non spaces
+                        stream.eatWhile(/[^\s\(\)\[\]]/); // eat symbol atom
                         returnType = COMMENT;
                         break;
                     }
@@ -133,7 +134,15 @@ CodeMirror.defineMode("scheme", function () {
                         returnType = STRING;
 
                     } else if (ch == "'") {
-                        returnType = ATOM;
+                        if (stream.peek() == "(" || stream.peek() == "["){
+                            if (typeof state.sExprQuote != "number") {
+                                state.sExprQuote = 0;
+                            } // else already in a quoted expression
+                            returnType = ATOM;
+                        } else {
+                            stream.eatWhile(/[\w_\-!$%&*+\.\/:<=>?@\^~]/);
+                            returnType = ATOM;
+                        }
                     } else if (ch == '#') {
                         if (stream.eat("|")) {                    // Multi-line comment
                             state.mode = "comment"; // toggle to comment mode
@@ -209,6 +218,7 @@ CodeMirror.defineMode("scheme", function () {
                         stream.backUp(stream.current().length - 1); // undo all the eating
 
                         if(typeof state.sExprComment == "number") state.sExprComment++;
+                        if(typeof state.sExprQuote == "number") state.sExprQuote++;
 
                         returnType = BRACKET;
                     } else if (ch == ")" || ch == "]") {
@@ -222,16 +232,22 @@ CodeMirror.defineMode("scheme", function () {
                                     state.sExprComment = false; // turn off s-expr commenting mode
                                 }
                             }
+                            if(typeof state.sExprQuote == "number"){
+                                if(--state.sExprQuote == 0){
+                                    returnType = ATOM; // final closing bracket
+                                    state.sExprQuote = false; // turn off s-expr quote mode
+                                }
+                            }
                         }
                     } else {
-                        stream.eatWhile(/[\w\$_\-!$%&*+\.\/:<=>?@\^~]/);
+                        stream.eatWhile(/[\w_\-!$%&*+\.\/:<=>?@\^~]/);
 
                         if (keywords && keywords.propertyIsEnumerable(stream.current())) {
                             returnType = BUILTIN;
                         } else returnType = "variable";
                     }
             }
-            return (typeof state.sExprComment == "number") ? COMMENT : returnType;
+            return (typeof state.sExprComment == "number") ? COMMENT : ((typeof state.sExprQuote == "number") ? ATOM : returnType);
         },
 
         indent: function (state) {
diff --git a/public/vendor/plugins/codemirror/mode/shell/index.html b/public/vendor/plugins/codemirror/mode/shell/index.html
index 0b56300b12..a844c1a40b 100644
--- a/public/vendor/plugins/codemirror/mode/shell/index.html
+++ b/public/vendor/plugins/codemirror/mode/shell/index.html
@@ -12,7 +12,7 @@
   .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
 </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -62,5 +62,5 @@ exit 0</textarea>
   });
 </script>
 
-<p><strong>MIME types defined:</strong> <code>text/x-sh</code>.</p>
+<p><strong>MIME types defined:</strong> <code>text/x-sh</code>, <code>application/x-sh</code>.</p>
 </article>
diff --git a/public/vendor/plugins/codemirror/mode/shell/shell.js b/public/vendor/plugins/codemirror/mode/shell/shell.js
index a684e8c233..5af12413b0 100644
--- a/public/vendor/plugins/codemirror/mode/shell/shell.js
+++ b/public/vendor/plugins/codemirror/mode/shell/shell.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -14,26 +14,27 @@
 CodeMirror.defineMode('shell', function() {
 
   var words = {};
-  function define(style, string) {
-    var split = string.split(' ');
-    for(var i = 0; i < split.length; i++) {
-      words[split[i]] = style;
+  function define(style, dict) {
+    for(var i = 0; i < dict.length; i++) {
+      words[dict[i]] = style;
     }
   };
 
-  // Atoms
-  define('atom', 'true false');
+  var commonAtoms = ["true", "false"];
+  var commonKeywords = ["if", "then", "do", "else", "elif", "while", "until", "for", "in", "esac", "fi",
+    "fin", "fil", "done", "exit", "set", "unset", "export", "function"];
+  var commonCommands = ["ab", "awk", "bash", "beep", "cat", "cc", "cd", "chown", "chmod", "chroot", "clear",
+    "cp", "curl", "cut", "diff", "echo", "find", "gawk", "gcc", "get", "git", "grep", "hg", "kill", "killall",
+    "ln", "ls", "make", "mkdir", "openssl", "mv", "nc", "nl", "node", "npm", "ping", "ps", "restart", "rm",
+    "rmdir", "sed", "service", "sh", "shopt", "shred", "source", "sort", "sleep", "ssh", "start", "stop",
+    "su", "sudo", "svn", "tee", "telnet", "top", "touch", "vi", "vim", "wall", "wc", "wget", "who", "write",
+    "yes", "zsh"];
 
-  // Keywords
-  define('keyword', 'if then do else elif while until for in esac fi fin ' +
-    'fil done exit set unset export function');
+  CodeMirror.registerHelper("hintWords", "shell", commonAtoms.concat(commonKeywords, commonCommands));
 
-  // Commands
-  define('builtin', 'ab awk bash beep cat cc cd chown chmod chroot clear cp ' +
-    'curl cut diff echo find gawk gcc get git grep kill killall ln ls make ' +
-    'mkdir openssl mv nc node npm ping ps restart rm rmdir sed service sh ' +
-    'shopt shred source sort sleep ssh start stop su sudo tee telnet top ' +
-    'touch vi vim wall wc wget who write yes zsh');
+  define('atom', commonAtoms);
+  define('keyword', commonKeywords);
+  define('builtin', commonCommands);
 
   function tokenBase(stream, state) {
     if (stream.eatSpace()) return null;
@@ -46,7 +47,7 @@ CodeMirror.defineMode('shell', function() {
       return null;
     }
     if (ch === '\'' || ch === '"' || ch === '`') {
-      state.tokens.unshift(tokenString(ch));
+      state.tokens.unshift(tokenString(ch, ch === "`" ? "quote" : "string"));
       return tokenize(stream, state);
     }
     if (ch === '#') {
@@ -81,41 +82,49 @@ CodeMirror.defineMode('shell', function() {
     return words.hasOwnProperty(cur) ? words[cur] : null;
   }
 
-  function tokenString(quote) {
+  function tokenString(quote, style) {
+    var close = quote == "(" ? ")" : quote == "{" ? "}" : quote
     return function(stream, state) {
-      var next, end = false, escaped = false;
+      var next, escaped = false;
       while ((next = stream.next()) != null) {
-        if (next === quote && !escaped) {
-          end = true;
+        if (next === close && !escaped) {
+          state.tokens.shift();
           break;
-        }
-        if (next === '$' && !escaped && quote !== '\'') {
+        } else if (next === '$' && !escaped && quote !== "'" && stream.peek() != close) {
           escaped = true;
           stream.backUp(1);
           state.tokens.unshift(tokenDollar);
           break;
+        } else if (!escaped && quote !== close && next === quote) {
+          state.tokens.unshift(tokenString(quote, style))
+          return tokenize(stream, state)
+        } else if (!escaped && /['"]/.test(next) && !/['"]/.test(quote)) {
+          state.tokens.unshift(tokenStringStart(next, "string"));
+          stream.backUp(1);
+          break;
         }
         escaped = !escaped && next === '\\';
       }
-      if (end || !escaped) {
-        state.tokens.shift();
-      }
-      return (quote === '`' || quote === ')' ? 'quote' : 'string');
+      return style;
     };
   };
 
+  function tokenStringStart(quote, style) {
+    return function(stream, state) {
+      state.tokens[0] = tokenString(quote, style)
+      stream.next()
+      return tokenize(stream, state)
+    }
+  }
+
   var tokenDollar = function(stream, state) {
     if (state.tokens.length > 1) stream.eat('$');
-    var ch = stream.next(), hungry = /\w/;
-    if (ch === '{') hungry = /[^}]/;
-    if (ch === '(') {
-      state.tokens[0] = tokenString(')');
+    var ch = stream.next()
+    if (/['"({]/.test(ch)) {
+      state.tokens[0] = tokenString(ch, ch == "(" ? "quote" : ch == "{" ? "def" : "string");
       return tokenize(stream, state);
     }
-    if (!/\d/.test(ch)) {
-      stream.eatWhile(hungry);
-      stream.eat('}');
-    }
+    if (!/\d/.test(ch)) stream.eatWhile(/\w/);
     state.tokens.shift();
     return 'def';
   };
@@ -129,11 +138,15 @@ CodeMirror.defineMode('shell', function() {
     token: function(stream, state) {
       return tokenize(stream, state);
     },
+    closeBrackets: "()[]{}''\"\"``",
     lineComment: '#',
     fold: "brace"
   };
 });
 
 CodeMirror.defineMIME('text/x-sh', 'shell');
+// Apache uses a slightly different Media Type for Shell scripts
+// http://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types
+CodeMirror.defineMIME('application/x-sh', 'shell');
 
 });
diff --git a/public/vendor/plugins/codemirror/mode/shell/test.js b/public/vendor/plugins/codemirror/mode/shell/test.js
index a413b5a406..7571d907de 100644
--- a/public/vendor/plugins/codemirror/mode/shell/test.js
+++ b/public/vendor/plugins/codemirror/mode/shell/test.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function() {
   var mode = CodeMirror.getMode({}, "shell");
@@ -55,4 +55,19 @@
      "[builtin ls] [attribute -l] [attribute --human-readable]");
   MT("operator",
      "[def var][operator =]value");
+
+  MT("doubleParens",
+     "foo [quote $((bar))]")
+
+  MT("nested braces",
+     "[builtin echo] [def ${A[${B}]]}]")
+
+  MT("strings in parens",
+     "[def FOO][operator =]([quote $(<][string \"][def $MYDIR][string \"][quote /myfile grep ][string 'hello$'][quote )])")
+
+  MT ("string ending in dollar",
+     '[def a][operator =][string "xyz$"]; [def b][operator =][string "y"]')
+
+  MT ("quote ending in dollar",
+     "[quote $(echo a$)]")
 })();
diff --git a/public/vendor/plugins/codemirror/mode/sieve/index.html b/public/vendor/plugins/codemirror/mode/sieve/index.html
index 6f029b623e..36369acba2 100644
--- a/public/vendor/plugins/codemirror/mode/sieve/index.html
+++ b/public/vendor/plugins/codemirror/mode/sieve/index.html
@@ -9,7 +9,7 @@
 <script src="sieve.js"></script>
 <style>.CodeMirror {background: #f8f8f8;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/sieve/sieve.js b/public/vendor/plugins/codemirror/mode/sieve/sieve.js
index f67db2f553..f02a867e7a 100644
--- a/public/vendor/plugins/codemirror/mode/sieve/sieve.js
+++ b/public/vendor/plugins/codemirror/mode/sieve/sieve.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -170,7 +170,7 @@ CodeMirror.defineMode("sieve", function(config) {
       if (stream.eatSpace())
         return null;
 
-      return (state.tokenize || tokenBase)(stream, state);;
+      return (state.tokenize || tokenBase)(stream, state);
     },
 
     indent: function(state, _textAfter) {
diff --git a/public/vendor/plugins/codemirror/mode/slim/index.html b/public/vendor/plugins/codemirror/mode/slim/index.html
index 7fa4e50df3..7aebf0aef1 100644
--- a/public/vendor/plugins/codemirror/mode/slim/index.html
+++ b/public/vendor/plugins/codemirror/mode/slim/index.html
@@ -20,7 +20,7 @@
 <script src="slim.js"></script>
 <style>.CodeMirror {background: #f8f8f8;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/slim/slim.js b/public/vendor/plugins/codemirror/mode/slim/slim.js
index 991a97efcd..b8ccb1381f 100644
--- a/public/vendor/plugins/codemirror/mode/slim/slim.js
+++ b/public/vendor/plugins/codemirror/mode/slim/slim.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 // Slim Highlighting for CodeMirror copyright (c) HicknHack Software Gmbh
 
diff --git a/public/vendor/plugins/codemirror/mode/slim/test.js b/public/vendor/plugins/codemirror/mode/slim/test.js
index be4ddacb62..991797fc88 100644
--- a/public/vendor/plugins/codemirror/mode/slim/test.js
+++ b/public/vendor/plugins/codemirror/mode/slim/test.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 // Slim Highlighting for CodeMirror copyright (c) HicknHack Software Gmbh
 
diff --git a/public/vendor/plugins/codemirror/mode/smalltalk/index.html b/public/vendor/plugins/codemirror/mode/smalltalk/index.html
index 2155ebc2a0..2dd45a2060 100644
--- a/public/vendor/plugins/codemirror/mode/smalltalk/index.html
+++ b/public/vendor/plugins/codemirror/mode/smalltalk/index.html
@@ -14,7 +14,7 @@
       .CodeMirror-gutter pre {color: white; font-weight: bold;}
     </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/smalltalk/smalltalk.js b/public/vendor/plugins/codemirror/mode/smalltalk/smalltalk.js
index bb510ba2e1..5039fe2d15 100644
--- a/public/vendor/plugins/codemirror/mode/smalltalk/smalltalk.js
+++ b/public/vendor/plugins/codemirror/mode/smalltalk/smalltalk.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/smarty/index.html b/public/vendor/plugins/codemirror/mode/smarty/index.html
index b19c8f09b5..2082916d16 100644
--- a/public/vendor/plugins/codemirror/mode/smarty/index.html
+++ b/public/vendor/plugins/codemirror/mode/smarty/index.html
@@ -8,9 +8,9 @@
 <script src="../../lib/codemirror.js"></script>
 <script src="../xml/xml.js"></script>
 <script src="smarty.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/smarty/smarty.js b/public/vendor/plugins/codemirror/mode/smarty/smarty.js
index 6e0fbed422..57852feb0e 100644
--- a/public/vendor/plugins/codemirror/mode/smarty/smarty.js
+++ b/public/vendor/plugins/codemirror/mode/smarty/smarty.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 /**
  * Smarty 2 and 3 mode.
@@ -210,9 +210,9 @@
         state.last = last;
         return style;
       },
-      indent: function(state, text) {
+      indent: function(state, text, line) {
         if (state.tokenize == tokenTop && baseMode.indent)
-          return baseMode.indent(state.base, text);
+          return baseMode.indent(state.base, text, line);
         else
           return CodeMirror.Pass;
       },
diff --git a/public/vendor/plugins/codemirror/mode/solr/index.html b/public/vendor/plugins/codemirror/mode/solr/index.html
index 4b18c25b78..52eb87d99c 100644
--- a/public/vendor/plugins/codemirror/mode/solr/index.html
+++ b/public/vendor/plugins/codemirror/mode/solr/index.html
@@ -7,7 +7,7 @@
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
 <script src="solr.js"></script>
-<style type="text/css">
+<style>
   .CodeMirror {
     border-top: 1px solid black;
     border-bottom: 1px solid black;
@@ -18,7 +18,7 @@
   }
 </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/solr/solr.js b/public/vendor/plugins/codemirror/mode/solr/solr.js
index f7f7087891..eda4a7a17d 100644
--- a/public/vendor/plugins/codemirror/mode/solr/solr.js
+++ b/public/vendor/plugins/codemirror/mode/solr/solr.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -14,12 +14,12 @@
 CodeMirror.defineMode("solr", function() {
   "use strict";
 
-  var isStringChar = /[^\s\|\!\+\-\*\?\~\^\&\:\(\)\[\]\{\}\^\"\\]/;
+  var isStringChar = /[^\s\|\!\+\-\*\?\~\^\&\:\(\)\[\]\{\}\"\\]/;
   var isOperatorChar = /[\|\!\+\-\*\?\~\^\&]/;
   var isOperatorString = /^(OR|AND|NOT|TO)$/i;
 
   function isNumber(word) {
-    return parseFloat(word, 10).toString() === word;
+    return parseFloat(word).toString() === word;
   }
 
   function tokenString(quote) {
diff --git a/public/vendor/plugins/codemirror/mode/soy/index.html b/public/vendor/plugins/codemirror/mode/soy/index.html
index f0216f097d..718e955593 100644
--- a/public/vendor/plugins/codemirror/mode/soy/index.html
+++ b/public/vendor/plugins/codemirror/mode/soy/index.html
@@ -12,9 +12,9 @@
 <script src="../javascript/javascript.js"></script>
 <script src="../css/css.js"></script>
 <script src="soy.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/soy/soy.js b/public/vendor/plugins/codemirror/mode/soy/soy.js
index 580c306f15..c39516a9ad 100644
--- a/public/vendor/plugins/codemirror/mode/soy/soy.js
+++ b/public/vendor/plugins/codemirror/mode/soy/soy.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -11,9 +11,45 @@
 })(function(CodeMirror) {
   "use strict";
 
-  var indentingTags = ["template", "literal", "msg", "fallbackmsg", "let", "if", "elseif",
-                       "else", "switch", "case", "default", "foreach", "ifempty", "for",
-                       "call", "param", "deltemplate", "delcall", "log"];
+  var paramData = { noEndTag: true, soyState: "param-def" };
+  var tags = {
+    "alias": { noEndTag: true },
+    "delpackage": { noEndTag: true },
+    "namespace": { noEndTag: true, soyState: "namespace-def" },
+    "@param": paramData,
+    "@param?": paramData,
+    "@inject": paramData,
+    "@inject?": paramData,
+    "@state": paramData,
+    "@state?": paramData,
+    "template": { soyState: "templ-def", variableScope: true},
+    "literal": { },
+    "msg": {},
+    "fallbackmsg": { noEndTag: true, reduceIndent: true},
+    "select": {},
+    "plural": {},
+    "let": { soyState: "var-def" },
+    "if": {},
+    "elseif": { noEndTag: true, reduceIndent: true},
+    "else": { noEndTag: true, reduceIndent: true},
+    "switch": {},
+    "case": { noEndTag: true, reduceIndent: true},
+    "default": { noEndTag: true, reduceIndent: true},
+    "foreach": { variableScope: true, soyState: "var-def" },
+    "ifempty": { noEndTag: true, reduceIndent: true},
+    "for": { variableScope: true, soyState: "var-def" },
+    "call": { soyState: "templ-ref" },
+    "param": { soyState: "param-ref"},
+    "print": { noEndTag: true },
+    "deltemplate": { soyState: "templ-def", variableScope: true},
+    "delcall": { soyState: "templ-ref" },
+    "log": {},
+    "element": { variableScope: true },
+  };
+
+  var indentingTags = Object.keys(tags).filter(function(tag) {
+    return !tags[tag].noEndTag || tags[tag].reduceIndent;
+  });
 
   CodeMirror.defineMode("soy", function(config) {
     var textMode = CodeMirror.getMode(config, "text/plain");
@@ -22,6 +58,7 @@
       attributes: textMode,
       text: textMode,
       uri: textMode,
+      trusted_resource_uri: textMode,
       css: CodeMirror.getMode(config, "text/css"),
       js: CodeMirror.getMode(config, {name: "text/javascript", statementIndent: 2 * config.indentUnit})
     };
@@ -31,6 +68,12 @@
     }
 
     function tokenUntil(stream, state, untilRegExp) {
+      if (stream.sol()) {
+        for (var indent = 0; indent < state.indent; indent++) {
+          if (!stream.eat(/\s/)) break;
+        }
+        if (indent) return null;
+      }
       var oldString = stream.string;
       var match = untilRegExp.exec(oldString.substr(stream.pos));
       if (match) {
@@ -39,33 +82,82 @@
         stream.string = oldString.substr(0, stream.pos + match.index);
       }
       var result = stream.hideFirstChars(state.indent, function() {
-        return state.localMode.token(stream, state.localState);
+        var localState = last(state.localStates);
+        return localState.mode.token(stream, localState.state);
       });
       stream.string = oldString;
       return result;
     }
 
+    function contains(list, element) {
+      while (list) {
+        if (list.element === element) return true;
+        list = list.next;
+      }
+      return false;
+    }
+
+    function prepend(list, element) {
+      return {
+        element: element,
+        next: list
+      };
+    }
+
+    function popcontext(state) {
+      if (!state.context) return;
+      if (state.context.scope) {
+        state.variables = state.context.scope;
+      }
+      state.context = state.context.previousContext;
+    }
+
+    // Reference a variable `name` in `list`.
+    // Let `loose` be truthy to ignore missing identifiers.
+    function ref(list, name, loose) {
+      return contains(list, name) ? "variable-2" : (loose ? "variable" : "variable-2 error");
+    }
+
+    // Data for an open soy tag.
+    function Context(previousContext, tag, scope) {
+      this.previousContext = previousContext;
+      this.tag = tag;
+      this.kind = null;
+      this.scope = scope;
+    }
+
     return {
       startState: function() {
         return {
-          kind: [],
-          kindTag: [],
           soyState: [],
+          templates: null,
+          variables: prepend(null, 'ij'),
+          scopes: null,
           indent: 0,
-          localMode: modes.html,
-          localState: CodeMirror.startState(modes.html)
+          quoteKind: null,
+          context: null,
+          localStates: [{
+            mode: modes.html,
+            state: CodeMirror.startState(modes.html)
+          }]
         };
       },
 
       copyState: function(state) {
         return {
           tag: state.tag, // Last seen Soy tag.
-          kind: state.kind.concat([]), // Values of kind="" attributes.
-          kindTag: state.kindTag.concat([]), // Opened tags with kind="" attributes.
           soyState: state.soyState.concat([]),
+          templates: state.templates,
+          variables: state.variables,
+          context: state.context,
           indent: state.indent, // Indentation of the following line.
-          localMode: state.localMode,
-          localState: CodeMirror.copyState(state.localMode, state.localState)
+          quoteKind: state.quoteKind,
+          localStates: state.localStates.map(function(localState) {
+            return {
+              mode: localState.mode,
+              state: CodeMirror.copyState(localState.mode, localState.state)
+            };
+          })
         };
       },
 
@@ -79,36 +171,159 @@
             } else {
               stream.skipToEnd();
             }
+            if (!state.context || !state.context.scope) {
+              var paramRe = /@param\??\s+(\S+)/g;
+              var current = stream.current();
+              for (var match; (match = paramRe.exec(current)); ) {
+                state.variables = prepend(state.variables, match[1]);
+              }
+            }
             return "comment";
 
-          case "variable":
-            if (stream.match(/^}/)) {
-              state.indent -= 2 * config.indentUnit;
+          case "string":
+            var match = stream.match(/^.*?(["']|\\[\s\S])/);
+            if (!match) {
+              stream.skipToEnd();
+            } else if (match[1] == state.quoteKind) {
+              state.quoteKind = null;
               state.soyState.pop();
-              return "variable-2";
+            }
+            return "string";
+        }
+
+        if (!state.soyState.length || last(state.soyState) != "literal") {
+          if (stream.match(/^\/\*/)) {
+            state.soyState.push("comment");
+            return "comment";
+          } else if (stream.match(stream.sol() ? /^\s*\/\/.*/ : /^\s+\/\/.*/)) {
+            return "comment";
+          }
+        }
+
+        switch (last(state.soyState)) {
+          case "templ-def":
+            if (match = stream.match(/^\.?([\w]+(?!\.[\w]+)*)/)) {
+              state.templates = prepend(state.templates, match[1]);
+              state.soyState.pop();
+              return "def";
+            }
+            stream.next();
+            return null;
+
+          case "templ-ref":
+            if (match = stream.match(/(\.?[a-zA-Z_][a-zA-Z_0-9]+)+/)) {
+              state.soyState.pop();
+              // If the first character is '.', it can only be a local template.
+              if (match[0][0] == '.') {
+                return "variable-2"
+              }
+              // Otherwise
+              return "variable";
+            }
+            stream.next();
+            return null;
+
+          case "namespace-def":
+            if (match = stream.match(/^\.?([\w\.]+)/)) {
+              state.soyState.pop();
+              return "variable";
+            }
+            stream.next();
+            return null;
+
+          case "param-def":
+            if (match = stream.match(/^\w+/)) {
+              state.variables = prepend(state.variables, match[0]);
+              state.soyState.pop();
+              state.soyState.push("param-type");
+              return "def";
+            }
+            stream.next();
+            return null;
+
+          case "param-ref":
+            if (match = stream.match(/^\w+/)) {
+              state.soyState.pop();
+              return "property";
+            }
+            stream.next();
+            return null;
+
+          case "param-type":
+            if (stream.peek() == "}") {
+              state.soyState.pop();
+              return null;
+            }
+            if (stream.eatWhile(/^([\w]+|[?])/)) {
+              return "type";
+            }
+            stream.next();
+            return null;
+
+          case "var-def":
+            if (match = stream.match(/^\$([\w]+)/)) {
+              state.variables = prepend(state.variables, match[1]);
+              state.soyState.pop();
+              return "def";
             }
             stream.next();
             return null;
 
           case "tag":
+            var endTag = state.tag[0] == "/";
+            var tagName = endTag ? state.tag.substring(1) : state.tag;
+            var tag = tags[tagName];
             if (stream.match(/^\/?}/)) {
-              if (state.tag == "/template" || state.tag == "/deltemplate") state.indent = 0;
-              else state.indent -= (stream.current() == "/}" || indentingTags.indexOf(state.tag) == -1 ? 2 : 1) * config.indentUnit;
+              var selfClosed = stream.current() == "/}";
+              if (selfClosed && !endTag) {
+                popcontext(state);
+              }
+              if (state.tag == "/template" || state.tag == "/deltemplate") {
+                state.variables = prepend(null, 'ij');
+                state.indent = 0;
+              } else {
+                state.indent -= config.indentUnit *
+                    (selfClosed || indentingTags.indexOf(state.tag) == -1 ? 2 : 1);
+              }
               state.soyState.pop();
               return "keyword";
             } else if (stream.match(/^([\w?]+)(?==)/)) {
-              if (stream.current() == "kind" && (match = stream.match(/^="([^"]+)/, false))) {
+              if (state.context && state.context.tag == tagName && stream.current() == "kind" && (match = stream.match(/^="([^"]+)/, false))) {
                 var kind = match[1];
-                state.kind.push(kind);
-                state.kindTag.push(state.tag);
-                state.localMode = modes[kind] || modes.html;
-                state.localState = CodeMirror.startState(state.localMode);
+                state.context.kind = kind;
+                var mode = modes[kind] || modes.html;
+                var localState = last(state.localStates);
+                if (localState.mode.indent) {
+                  state.indent += localState.mode.indent(localState.state, "", "");
+                }
+                state.localStates.push({
+                  mode: mode,
+                  state: CodeMirror.startState(mode)
+                });
               }
               return "attribute";
-            } else if (stream.match(/^"/)) {
+            } else if (match = stream.match(/([\w]+)(?=\()/)) {
+              return "variable callee";
+            } else if (match = stream.match(/^["']/)) {
               state.soyState.push("string");
+              state.quoteKind = match;
               return "string";
             }
+            if (stream.match(/(null|true|false)(?!\w)/) ||
+              stream.match(/0x([0-9a-fA-F]{2,})/) ||
+              stream.match(/-?([0-9]*[.])?[0-9]+(e[0-9]*)?/)) {
+              return "atom";
+            }
+            if (stream.match(/(\||[+\-*\/%]|[=!]=|\?:|[<>]=?)/)) {
+              // Tokenize filter, binary, null propagator, and equality operators.
+              return "operator";
+            }
+            if (match = stream.match(/^\$([\w]+)/)) {
+              return ref(state.variables, match[1]);
+            }
+            if (match = stream.match(/^\w+/)) {
+              return /^(?:as|and|or|not|in)$/.test(match[0]) ? "keyword" : null;
+            }
             stream.next();
             return null;
 
@@ -119,41 +334,59 @@
               return this.token(stream, state);
             }
             return tokenUntil(stream, state, /\{\/literal}/);
-
-          case "string":
-            var match = stream.match(/^.*?("|\\[\s\S])/);
-            if (!match) {
-              stream.skipToEnd();
-            } else if (match[1] == "\"") {
-              state.soyState.pop();
-            }
-            return "string";
         }
 
-        if (stream.match(/^\/\*/)) {
-          state.soyState.push("comment");
-          return "comment";
-        } else if (stream.match(stream.sol() ? /^\s*\/\/.*/ : /^\s+\/\/.*/)) {
-          return "comment";
-        } else if (stream.match(/^\{\$[\w?]*/)) {
-          state.indent += 2 * config.indentUnit;
-          state.soyState.push("variable");
-          return "variable-2";
-        } else if (stream.match(/^\{literal}/)) {
+        if (stream.match(/^\{literal}/)) {
           state.indent += config.indentUnit;
           state.soyState.push("literal");
+          state.context = new Context(state.context, "literal", state.variables);
           return "keyword";
-        } else if (match = stream.match(/^\{([\/@\\]?[\w?]*)/)) {
-          if (match[1] != "/switch")
-            state.indent += (/^(\/|(else|elseif|case|default)$)/.test(match[1]) && state.tag != "switch" ? 1 : 2) * config.indentUnit;
+
+        // A tag-keyword must be followed by whitespace, comment or a closing tag.
+        } else if (match = stream.match(/^\{([/@\\]?\w+\??)(?=$|[\s}]|\/[/*])/)) {
+          var prevTag = state.tag;
           state.tag = match[1];
-          if (state.tag == "/" + last(state.kindTag)) {
-            // We found the tag that opened the current kind="".
-            state.kind.pop();
-            state.kindTag.pop();
-            state.localMode = modes[last(state.kind)] || modes.html;
-            state.localState = CodeMirror.startState(state.localMode);
+          var endTag = state.tag[0] == "/";
+          var indentingTag = !!tags[state.tag];
+          var tagName = endTag ? state.tag.substring(1) : state.tag;
+          var tag = tags[tagName];
+          if (state.tag != "/switch")
+            state.indent += ((endTag || tag && tag.reduceIndent) && prevTag != "switch" ? 1 : 2) * config.indentUnit;
+
+          state.soyState.push("tag");
+          var tagError = false;
+          if (tag) {
+            if (!endTag) {
+              if (tag.soyState) state.soyState.push(tag.soyState);
+            }
+            // If a new tag, open a new context.
+            if (!tag.noEndTag && (indentingTag || !endTag)) {
+              state.context = new Context(state.context, state.tag, tag.variableScope ? state.variables : null);
+            // Otherwise close the current context.
+            } else if (endTag) {
+              if (!state.context || state.context.tag != tagName) {
+                tagError = true;
+              } else if (state.context) {
+                if (state.context.kind) {
+                  state.localStates.pop();
+                  var localState = last(state.localStates);
+                  if (localState.mode.indent) {
+                    state.indent -= localState.mode.indent(localState.state, "", "");
+                  }
+                }
+                popcontext(state);
+              }
+            }
+          } else if (endTag) {
+            // Assume all tags with a closing tag are defined in the config.
+            tagError = true;
           }
+          return (tagError ? "error " : "") + "keyword";
+
+        // Not a tag-keyword; it's an implicit print tag.
+        } else if (stream.eat('{')) {
+          state.tag = "print";
+          state.indent += 2 * config.indentUnit;
           state.soyState.push("tag");
           return "keyword";
         }
@@ -161,7 +394,7 @@
         return tokenUntil(stream, state, /\{|\s+\/\/|\/\*/);
       },
 
-      indent: function(state, textAfter) {
+      indent: function(state, textAfter, line) {
         var indent = state.indent, top = last(state.soyState);
         if (top == "comment") return CodeMirror.Pass;
 
@@ -173,14 +406,16 @@
           if (state.tag != "switch" && /^\{(case|default)\b/.test(textAfter)) indent -= config.indentUnit;
           if (/^\{\/switch\b/.test(textAfter)) indent -= config.indentUnit;
         }
-        if (indent && state.localMode.indent)
-          indent += state.localMode.indent(state.localState, textAfter);
+        var localState = last(state.localStates);
+        if (indent && localState.mode.indent) {
+          indent += localState.mode.indent(localState.state, textAfter, line);
+        }
         return indent;
       },
 
       innerMode: function(state) {
         if (state.soyState.length && last(state.soyState) != "literal") return null;
-        else return {state: state.localState, mode: state.localMode};
+        else return last(state.localStates);
       },
 
       electricInput: /^\s*\{(\/|\/template|\/deltemplate|\/switch|fallbackmsg|elseif|else|case|default|ifempty|\/literal\})$/,
@@ -188,12 +423,15 @@
       blockCommentStart: "/*",
       blockCommentEnd: "*/",
       blockCommentContinue: " * ",
+      useInnerComments: false,
       fold: "indent"
     };
   }, "htmlmixed");
 
-  CodeMirror.registerHelper("hintWords", "soy", indentingTags.concat(
-      ["delpackage", "namespace", "alias", "print", "css", "debugger"]));
+  CodeMirror.registerHelper("wordChars", "soy", /[\w$]/);
+
+  CodeMirror.registerHelper("hintWords", "soy", Object.keys(tags).concat(
+      ["css", "debugger"]));
 
   CodeMirror.defineMIME("text/x-soy", "soy");
 });
diff --git a/public/vendor/plugins/codemirror/mode/soy/test.js b/public/vendor/plugins/codemirror/mode/soy/test.js
new file mode 100644
index 0000000000..6c1fd8d630
--- /dev/null
+++ b/public/vendor/plugins/codemirror/mode/soy/test.js
@@ -0,0 +1,204 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function() {
+  var mode = CodeMirror.getMode({indentUnit: 2}, "soy");
+  function MT(name) {test.mode(name, mode, Array.prototype.slice.call(arguments, 1));}
+
+  // Test of small keywords and words containing them.
+  MT('keywords-test',
+     '[keyword {] [keyword as] worrying [keyword and] notorious [keyword as]',
+     '    the Fandor[operator -]alias assassin, [keyword or]',
+     '    Corcand cannot fit [keyword in] [keyword }]');
+
+  MT('let-test',
+     '[keyword {template] [def .name][keyword }]',
+     '  [keyword {let] [def $name]: [string "world"][keyword /}]',
+     '  [tag&bracket <][tag h1][tag&bracket >]',
+     '    Hello, [keyword {][variable-2 $name][keyword }]',
+     '  [tag&bracket </][tag h1][tag&bracket >]',
+     '[keyword {/template}]',
+     '');
+
+  MT('function-test',
+     '[keyword {] [callee&variable css]([string "MyClass"])[keyword }]',
+     '[tag&bracket <][tag input] [attribute value]=[string "]' +
+     '[keyword {] [callee&variable index]([variable-2&error $list])[keyword }]' +
+        '[string "][tag&bracket />]');
+
+  MT('namespace-test',
+     '[keyword {namespace] [variable namespace][keyword }]')
+
+  MT('namespace-with-attribute-test',
+     '[keyword {namespace] [variable my.namespace.templates] ' +
+         '[attribute requirecss]=[string "my.namespace"][keyword }]');
+
+  MT('operators-test',
+     '[keyword {] [atom 1] [operator ==] [atom 1] [keyword }]',
+     '[keyword {] [atom 1] [operator !=] [atom 2] [keyword }]',
+     '[keyword {] [atom 2] [operator +] [atom 2] [keyword }]',
+     '[keyword {] [atom 2] [operator -] [atom 2] [keyword }]',
+     '[keyword {] [atom 2] [operator *] [atom 2] [keyword }]',
+     '[keyword {] [atom 2] [operator /] [atom 2] [keyword }]',
+     '[keyword {] [atom 2] [operator %] [atom 2] [keyword }]',
+     '[keyword {] [atom 2] [operator <=] [atom 2] [keyword }]',
+     '[keyword {] [atom 2] [operator >=] [atom 2] [keyword }]',
+     '[keyword {] [atom 3] [operator >] [atom 2] [keyword }]',
+     '[keyword {] [atom 2] [operator >] [atom 3] [keyword }]',
+     '[keyword {] [atom null] [operator ?:] [string ""] [keyword }]',
+     '[keyword {] [variable-2&error $variable] [operator |] safeHtml [keyword }]')
+
+  MT('primitive-test',
+     '[keyword {] [atom true] [keyword }]',
+     '[keyword {] [atom false] [keyword }]',
+     '[keyword {] truethy [keyword }]',
+     '[keyword {] falsey [keyword }]',
+     '[keyword {] [atom 42] [keyword }]',
+     '[keyword {] [atom .42] [keyword }]',
+     '[keyword {] [atom 0.42] [keyword }]',
+     '[keyword {] [atom -0.42] [keyword }]',
+     '[keyword {] [atom -.2] [keyword }]',
+     '[keyword {] [atom 6.03e23] [keyword }]',
+     '[keyword {] [atom -0.03e0] [keyword }]',
+     '[keyword {] [atom 0x1F] [keyword }]',
+     '[keyword {] [atom 0x1F00BBEA] [keyword }]');
+
+  MT('param-type-test',
+     '[keyword {@param] [def a]: ' +
+         '[type list]<[[[type a]: [type int], ' +
+         '[type b]: [type map]<[type string], ' +
+         '[type bool]>]]>][keyword }]',
+      '[keyword {@param] [def unknown]: [type ?][keyword }]',
+      '[keyword {@param] [def list]: [type list]<[type ?]>[keyword }]');
+
+  MT('undefined-var',
+     '[keyword {][variable-2&error $var]');
+
+  MT('param-scope-test',
+     '[keyword {template] [def .a][keyword }]',
+     '  [keyword {@param] [def x]: [type string][keyword }]',
+     '  [keyword {][variable-2 $x][keyword }]',
+     '[keyword {/template}]',
+     '',
+     '[keyword {template] [def .b][keyword }]',
+     '  [keyword {][variable-2&error $x][keyword }]',
+     '[keyword {/template}]',
+     '');
+
+  MT('if-variable-test',
+     '[keyword {if] [variable-2&error $showThing][keyword }]',
+     '  Yo!',
+     '[keyword {/if}]',
+     '');
+
+  MT('defined-if-variable-test',
+     '[keyword {template] [def .foo][keyword }]',
+     '  [keyword {@param?] [def showThing]: [type bool][keyword }]',
+     '  [keyword {if] [variable-2 $showThing][keyword }]',
+     '    Yo!',
+     '  [keyword {/if}]',
+     '[keyword {/template}]',
+     '');
+
+  MT('template-calls-test',
+     '[keyword {call] [variable-2 .foo][keyword /}]',
+     '[keyword {call] [variable foo][keyword /}]',
+     '[keyword {call] [variable foo][keyword }] [keyword {/call}]',
+     '[keyword {call] [variable first1.second.third_3][keyword /}]',
+     '[keyword {call] [variable first1.second.third_3] [keyword }] [keyword {/call}]',
+     '');
+
+  MT('foreach-scope-test',
+     '[keyword {@param] [def bar]: [type string][keyword }]',
+     '[keyword {foreach] [def $foo] [keyword in] [variable-2&error $foos][keyword }]',
+     '  [keyword {][variable-2 $foo][keyword }]',
+     '[keyword {/foreach}]',
+     '[keyword {][variable-2&error $foo][keyword }]',
+     '[keyword {][variable-2 $bar][keyword }]');
+
+  MT('foreach-ifempty-indent-test',
+     '[keyword {foreach] [def $foo] [keyword in] [variable-2&error $foos][keyword }]',
+     '  something',
+     '[keyword {ifempty}]',
+     '  nothing',
+     '[keyword {/foreach}]',
+     '');
+
+  MT('nested-kind-test',
+     '[keyword {template] [def .foo] [attribute kind]=[string "html"][keyword }]',
+     '  [tag&bracket <][tag div][tag&bracket >]',
+     '    [keyword {call] [variable-2 .bar][keyword }]',
+     '      [keyword {param] [property propertyName] [attribute kind]=[string "js"][keyword }]',
+     '        [keyword var] [def bar] [operator =] [number 5];',
+     '      [keyword {/param}]',
+     '    [keyword {/call}]',
+     '  [tag&bracket </][tag div][tag&bracket >]',
+     '[keyword {/template}]',
+     '');
+
+  MT('tag-starting-with-function-call-is-not-a-keyword',
+     '[keyword {][callee&variable index]([variable-2&error $foo])[keyword }]',
+     '[keyword {css] [string "some-class"][keyword }]',
+     '[keyword {][callee&variable css]([string "some-class"])[keyword }]',
+     '');
+
+  MT('allow-missing-colon-in-@param',
+     '[keyword {template] [def .foo][keyword }]',
+     '  [keyword {@param] [def showThing] [type bool][keyword }]',
+     '  [keyword {if] [variable-2 $showThing][keyword }]',
+     '    Yo!',
+     '  [keyword {/if}]',
+     '[keyword {/template}]',
+     '');
+
+  MT('single-quote-strings',
+     '[keyword {][string "foo"] [string \'bar\'][keyword }]',
+     '');
+
+  MT('literal-comments',
+     '[keyword {literal}]/* comment */ // comment[keyword {/literal}]');
+
+  MT('highlight-command-at-eol',
+     '[keyword {msg]',
+     '    [keyword }]');
+
+  MT('switch-indent-test',
+     '[keyword {let] [def $marbles]: [atom 5] [keyword /}]',
+     '[keyword {switch] [variable-2 $marbles][keyword }]',
+     '  [keyword {case] [atom 0][keyword }]',
+     '    No marbles',
+     '  [keyword {default}]',
+     '    At least 1 marble',
+     '[keyword {/switch}]',
+     '');
+
+  MT('if-elseif-else-indent',
+     '[keyword {if] [atom true][keyword }]',
+     '  [keyword {let] [def $a]: [atom 5] [keyword /}]',
+     '[keyword {elseif] [atom false][keyword }]',
+     '  [keyword {let] [def $bar]: [atom 5] [keyword /}]',
+     '[keyword {else}]',
+     '  [keyword {let] [def $bar]: [atom 5] [keyword /}]',
+     '[keyword {/if}]');
+
+  MT('msg-fallbackmsg-indent',
+     '[keyword {msg] [attribute desc]=[string "A message"][keyword }]',
+     '  A message',
+     '[keyword {fallbackmsg] [attribute desc]=[string "A message"][keyword }]',
+     '  Old message',
+     '[keyword {/msg}]');
+
+  MT('special-chars',
+     '[keyword {sp}]',
+     '[keyword {nil}]',
+     '[keyword {\\r}]',
+     '[keyword {\\n}]',
+     '[keyword {\\t}]',
+     '[keyword {lb}]',
+     '[keyword {rb}]');
+
+  MT('wrong-closing-tag',
+     '[keyword {if] [atom true][keyword }]',
+     '  Optional',
+     '[keyword&error {/badend][keyword }]');
+})();
diff --git a/public/vendor/plugins/codemirror/mode/sparql/index.html b/public/vendor/plugins/codemirror/mode/sparql/index.html
index 84ef4d3639..dfd3f9070a 100644
--- a/public/vendor/plugins/codemirror/mode/sparql/index.html
+++ b/public/vendor/plugins/codemirror/mode/sparql/index.html
@@ -10,7 +10,7 @@
 <script src="sparql.js"></script>
 <style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/sparql/sparql.js b/public/vendor/plugins/codemirror/mode/sparql/sparql.js
index 095dcca653..bb79abff7f 100644
--- a/public/vendor/plugins/codemirror/mode/sparql/sparql.js
+++ b/public/vendor/plugins/codemirror/mode/sparql/sparql.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -41,7 +41,7 @@ CodeMirror.defineMode("sparql", function(config) {
       if(ch == "?" && stream.match(/\s/, false)){
         return "operator";
       }
-      stream.match(/^[\w\d]*/);
+      stream.match(/^[A-Za-z0-9_\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][A-Za-z0-9_\u00B7\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u037D\u037F-\u1FFF\u200C-\u200D\u203F-\u2040\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]*/);
       return "variable-2";
     }
     else if (ch == "<" && !stream.match(/^[\s\u00a0=]/, false)) {
diff --git a/public/vendor/plugins/codemirror/mode/spreadsheet/index.html b/public/vendor/plugins/codemirror/mode/spreadsheet/index.html
index a52f76f050..6589bcc727 100644
--- a/public/vendor/plugins/codemirror/mode/spreadsheet/index.html
+++ b/public/vendor/plugins/codemirror/mode/spreadsheet/index.html
@@ -8,9 +8,9 @@
 <script src="../../lib/codemirror.js"></script>
 <script src="../../addon/edit/matchbrackets.js"></script>
 <script src="spreadsheet.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/spreadsheet/spreadsheet.js b/public/vendor/plugins/codemirror/mode/spreadsheet/spreadsheet.js
index 222f297668..d87f988d35 100644
--- a/public/vendor/plugins/codemirror/mode/spreadsheet/spreadsheet.js
+++ b/public/vendor/plugins/codemirror/mode/spreadsheet/spreadsheet.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/sql/index.html b/public/vendor/plugins/codemirror/mode/sql/index.html
index dba069dc81..5791c968c8 100644
--- a/public/vendor/plugins/codemirror/mode/sql/index.html
+++ b/public/vendor/plugins/codemirror/mode/sql/index.html
@@ -6,6 +6,7 @@
 
 <link rel="stylesheet" href="../../lib/codemirror.css" />
 <script src="../../lib/codemirror.js"></script>
+<script src="../../addon/edit/matchbrackets.js"></script>
 <script src="sql.js"></script>
 <link rel="stylesheet" href="../../addon/hint/show-hint.css" />
 <script src="../../addon/hint/show-hint.js"></script>
@@ -17,7 +18,7 @@
 }
         </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -49,7 +50,7 @@ SELECT SQL_NO_CACHE DISTINCT
 	LIMIT 1 OFFSET 0;
 </textarea>
             </form>
-            <p><strong>MIME types defined:</strong> 
+            <p><strong>MIME types defined:</strong>
             <code><a href="?mime=text/x-sql">text/x-sql</a></code>,
             <code><a href="?mime=text/x-mysql">text/x-mysql</a></code>,
             <code><a href="?mime=text/x-mariadb">text/x-mariadb</a></code>,
@@ -58,7 +59,9 @@ SELECT SQL_NO_CACHE DISTINCT
             <code><a href="?mime=text/x-mssql">text/x-mssql</a></code>,
             <code><a href="?mime=text/x-hive">text/x-hive</a></code>,
             <code><a href="?mime=text/x-pgsql">text/x-pgsql</a></code>,
-            <code><a href="?mime=text/x-gql">text/x-gql</a></code>.
+            <code><a href="?mime=text/x-gql">text/x-gql</a></code>,
+            <code><a href="?mime=text/x-gpsql">text/x-gpsql</a></code>.
+            <code><a href="?mime=text/x-esper">text/x-esper</a></code>.
         </p>
 <script>
 window.onload = function() {
@@ -76,8 +79,8 @@ window.onload = function() {
     autofocus: true,
     extraKeys: {"Ctrl-Space": "autocomplete"},
     hintOptions: {tables: {
-      users: {name: null, score: null, birthDate: null},
-      countries: {name: null, population: null, size: null}
+      users: ["name", "score", "birthDate"],
+      countries: ["name", "population", "size"]
     }}
   });
 };
diff --git a/public/vendor/plugins/codemirror/mode/sql/sql.js b/public/vendor/plugins/codemirror/mode/sql/sql.js
index 01ebd80ae1..35902bbab1 100644
--- a/public/vendor/plugins/codemirror/mode/sql/sql.js
+++ b/public/vendor/plugins/codemirror/mode/sql/sql.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -12,16 +12,17 @@
 "use strict";
 
 CodeMirror.defineMode("sql", function(config, parserConfig) {
-  "use strict";
-
   var client         = parserConfig.client || {},
       atoms          = parserConfig.atoms || {"false": true, "true": true, "null": true},
-      builtin        = parserConfig.builtin || {},
-      keywords       = parserConfig.keywords || {},
-      operatorChars  = parserConfig.operatorChars || /^[*+\-%<>!=&|~^]/,
+      builtin        = parserConfig.builtin || set(defaultBuiltin),
+      keywords       = parserConfig.keywords || set(sqlKeywords),
+      operatorChars  = parserConfig.operatorChars || /^[*+\-%<>!=&|~^\/]/,
       support        = parserConfig.support || {},
       hooks          = parserConfig.hooks || {},
-      dateSQL        = parserConfig.dateSQL || {"date" : true, "time" : true, "timestamp" : true};
+      dateSQL        = parserConfig.dateSQL || {"date" : true, "time" : true, "timestamp" : true},
+      backslashStringEscapes = parserConfig.backslashStringEscapes !== false,
+      brackets       = parserConfig.brackets || /^[\{}\(\)\[\]]/,
+      punctuation    = parserConfig.punctuation || /^[;.,:]/
 
   function tokenBase(stream, state) {
     var ch = stream.next();
@@ -32,13 +33,13 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
       if (result !== false) return result;
     }
 
-    if (support.hexNumber == true &&
+    if (support.hexNumber &&
       ((ch == "0" && stream.match(/^[xX][0-9a-fA-F]+/))
       || (ch == "x" || ch == "X") && stream.match(/^'[0-9a-fA-F]+'/))) {
       // hex
       // ref: http://dev.mysql.com/doc/refman/5.5/en/hexadecimal-literals.html
       return "number";
-    } else if (support.binaryNumber == true &&
+    } else if (support.binaryNumber &&
       (((ch == "b" || ch == "B") && stream.match(/^'[01]+'/))
       || (ch == "0" && stream.match(/^b[01]+/)))) {
       // bitstring
@@ -47,8 +48,8 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
     } else if (ch.charCodeAt(0) > 47 && ch.charCodeAt(0) < 58) {
       // numbers
       // ref: http://dev.mysql.com/doc/refman/5.5/en/number-literals.html
-          stream.match(/^[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?/);
-      support.decimallessFloat == true && stream.eat('.');
+      stream.match(/^[0-9]*(\.[0-9]+)?([eE][-+]?[0-9]+)?/);
+      support.decimallessFloat && stream.match(/^\.(?!\.)/);
       return "number";
     } else if (ch == "?" && (stream.eatSpace() || stream.eol() || stream.eat(";"))) {
       // placeholders
@@ -58,15 +59,12 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
       // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html
       state.tokenize = tokenLiteral(ch);
       return state.tokenize(stream, state);
-    } else if ((((support.nCharCast == true && (ch == "n" || ch == "N"))
-        || (support.charsetCast == true && ch == "_" && stream.match(/[a-z][a-z0-9]*/i)))
+    } else if ((((support.nCharCast && (ch == "n" || ch == "N"))
+        || (support.charsetCast && ch == "_" && stream.match(/[a-z][a-z0-9]*/i)))
         && (stream.peek() == "'" || stream.peek() == '"'))) {
       // charset casting: _utf8'str', N'str', n'str'
       // ref: http://dev.mysql.com/doc/refman/5.5/en/string-literals.html
       return "keyword";
-    } else if (/^[\(\),\;\[\]]/.test(ch)) {
-      // no highlighting
-      return null;
     } else if (support.commentSlashSlash && ch == "/" && stream.eat("/")) {
       // 1-line comment
       stream.skipToEnd();
@@ -80,22 +78,29 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
     } else if (ch == "/" && stream.eat("*")) {
       // multi-line comments
       // ref: https://kb.askmonty.org/en/comment-syntax/
-      state.tokenize = tokenComment;
+      state.tokenize = tokenComment(1);
       return state.tokenize(stream, state);
     } else if (ch == ".") {
       // .1 for 0.1
-      if (support.zerolessFloat == true && stream.match(/^(?:\d+(?:e[+-]?\d+)?)/i)) {
+      if (support.zerolessFloat && stream.match(/^(?:\d+(?:e[+-]?\d+)?)/i))
         return "number";
-      }
+      if (stream.match(/^\.+/))
+        return null
       // .table_name (ODBC)
       // // ref: http://dev.mysql.com/doc/refman/5.6/en/identifier-qualifiers.html
-      if (support.ODBCdotTable == true && stream.match(/^[a-zA-Z_]+/)) {
+      if (support.ODBCdotTable && stream.match(/^[\w\d_]+/))
         return "variable-2";
-      }
     } else if (operatorChars.test(ch)) {
       // operators
       stream.eatWhile(operatorChars);
-      return null;
+      return "operator";
+    } else if (brackets.test(ch)) {
+      // brackets
+      return "bracket";
+    } else if (punctuation.test(ch)) {
+      // punctuation
+      stream.eatWhile(punctuation);
+      return "punctuation";
     } else if (ch == '{' &&
         (stream.match(/^( )*(d|D|t|T|ts|TS)( )*'[^']*'( )*}/) || stream.match(/^( )*(d|D|t|T|ts|TS)( )*"[^"]*"( )*}/))) {
       // dates (weird ODBC syntax)
@@ -125,25 +130,20 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
           state.tokenize = tokenBase;
           break;
         }
-        escaped = !escaped && ch == "\\";
+        escaped = backslashStringEscapes && !escaped && ch == "\\";
       }
       return "string";
     };
   }
-  function tokenComment(stream, state) {
-    while (true) {
-      if (stream.skipTo("*")) {
-        stream.next();
-        if (stream.eat("/")) {
-          state.tokenize = tokenBase;
-          break;
-        }
-      } else {
-        stream.skipToEnd();
-        break;
-      }
+  function tokenComment(depth) {
+    return function(stream, state) {
+      var m = stream.match(/^.*?(\/\*|\*\/)/)
+      if (!m) stream.skipToEnd()
+      else if (m[1] == "/*") state.tokenize = tokenComment(depth + 1)
+      else if (depth > 1) state.tokenize = tokenComment(depth - 1)
+      else state.tokenize = tokenBase
+      return "comment"
     }
-    return "comment";
   }
 
   function pushContext(stream, state, type) {
@@ -170,7 +170,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
         if (state.context && state.context.align == null)
           state.context.align = false;
       }
-      if (stream.eatSpace()) return null;
+      if (state.tokenize == tokenBase && stream.eatSpace()) return null;
 
       var style = state.tokenize(stream, state);
       if (style == "comment") return style;
@@ -198,13 +198,11 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
 
     blockCommentStart: "/*",
     blockCommentEnd: "*/",
-    lineComment: support.commentSlashSlash ? "//" : support.commentHash ? "#" : null
+    lineComment: support.commentSlashSlash ? "//" : support.commentHash ? "#" : "--",
+    closeBrackets: "()[]{}''\"\"``"
   };
 });
 
-(function() {
-  "use strict";
-
   // `identifier`
   function hookIdentifier(stream) {
     // MySQL/MariaDB identifiers
@@ -217,6 +215,19 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
     return stream.eatWhile(/\w/) ? "variable-2" : null;
   }
 
+  // "identifier"
+  function hookIdentifierDoublequote(stream) {
+    // Standard SQL /SQLite identifiers
+    // ref: http://web.archive.org/web/20160813185132/http://savage.net.au/SQL/sql-99.bnf.html#delimited%20identifier
+    // ref: http://sqlite.org/lang_keywords.html
+    var ch;
+    while ((ch = stream.next()) != null) {
+      if (ch == "\"" && !stream.eat("\"")) return "variable-2";
+    }
+    stream.backUp(stream.current().length - 1);
+    return stream.eatWhile(/\w/) ? "variable-2" : null;
+  }
+
   // variable token
   function hookVar(stream) {
     // variables
@@ -266,24 +277,28 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
     return obj;
   }
 
+  var defaultBuiltin = "bool boolean bit blob enum long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision real date datetime year unsigned signed decimal numeric"
+
   // A generic SQL Mode. It's not a standard, it just try to support what is generally supported
   CodeMirror.defineMIME("text/x-sql", {
     name: "sql",
     keywords: set(sqlKeywords + "begin"),
-    builtin: set("bool boolean bit blob enum long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision real date datetime year unsigned signed decimal numeric"),
+    builtin: set(defaultBuiltin),
     atoms: set("false true null unknown"),
-    operatorChars: /^[*+\-%<>!=]/,
     dateSQL: set("date time timestamp"),
     support: set("ODBCdotTable doubleQuote binaryNumber hexNumber")
   });
 
   CodeMirror.defineMIME("text/x-mssql", {
     name: "sql",
-    client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"),
-    keywords: set(sqlKeywords + "begin trigger proc view index for add constraint key primary foreign collate clustered nonclustered declare"),
+    client: set("$partition binary_checksum checksum connectionproperty context_info current_request_id error_line error_message error_number error_procedure error_severity error_state formatmessage get_filestream_transaction_context getansinull host_id host_name isnull isnumeric min_active_rowversion newid newsequentialid rowcount_big xact_state object_id"),
+    keywords: set(sqlKeywords + "begin trigger proc view index for add constraint key primary foreign collate clustered nonclustered declare exec go if use index holdlock nolock nowait paglock readcommitted readcommittedlock readpast readuncommitted repeatableread rowlock serializable snapshot tablock tablockx updlock with"),
     builtin: set("bigint numeric bit smallint decimal smallmoney int tinyint money float real char varchar text nchar nvarchar ntext binary varbinary image cursor timestamp hierarchyid uniqueidentifier sql_variant xml table "),
-    atoms: set("false true null unknown"),
-    operatorChars: /^[*+\-%<>!=]/,
+    atoms: set("is not null like and or in left right between inner outer join all any some cross unpivot pivot exists"),
+    operatorChars: /^[*+\-%<>!=^\&|\/]/,
+    brackets: /^[\{}\(\)]/,
+    punctuation: /^[;.,:/]/,
+    backslashStringEscapes: false,
     dateSQL: set("date datetimeoffset datetime2 smalldatetime datetime time"),
     hooks: {
       "@":   hookVar
@@ -322,6 +337,36 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
     }
   });
 
+  // provided by the phpLiteAdmin project - phpliteadmin.org
+  CodeMirror.defineMIME("text/x-sqlite", {
+    name: "sql",
+    // commands of the official SQLite client, ref: https://www.sqlite.org/cli.html#dotcmd
+    client: set("auth backup bail binary changes check clone databases dbinfo dump echo eqp exit explain fullschema headers help import imposter indexes iotrace limit lint load log mode nullvalue once open output print prompt quit read restore save scanstats schema separator session shell show stats system tables testcase timeout timer trace vfsinfo vfslist vfsname width"),
+    // ref: http://sqlite.org/lang_keywords.html
+    keywords: set(sqlKeywords + "abort action add after all analyze attach autoincrement before begin cascade case cast check collate column commit conflict constraint cross current_date current_time current_timestamp database default deferrable deferred detach each else end escape except exclusive exists explain fail for foreign full glob if ignore immediate index indexed initially inner instead intersect isnull key left limit match natural no notnull null of offset outer plan pragma primary query raise recursive references regexp reindex release rename replace restrict right rollback row savepoint temp temporary then to transaction trigger unique using vacuum view virtual when with without"),
+    // SQLite is weakly typed, ref: http://sqlite.org/datatype3.html. This is just a list of some common types.
+    builtin: set("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text clob bigint int int2 int8 integer float double char varchar date datetime year unsigned signed numeric real"),
+    // ref: http://sqlite.org/syntax/literal-value.html
+    atoms: set("null current_date current_time current_timestamp"),
+    // ref: http://sqlite.org/lang_expr.html#binaryops
+    operatorChars: /^[*+\-%<>!=&|/~]/,
+    // SQLite is weakly typed, ref: http://sqlite.org/datatype3.html. This is just a list of some common types.
+    dateSQL: set("date time timestamp datetime"),
+    support: set("decimallessFloat zerolessFloat"),
+    identifierQuote: "\"",  //ref: http://sqlite.org/lang_keywords.html
+    hooks: {
+      // bind-parameters ref:http://sqlite.org/lang_expr.html#varparam
+      "@":   hookVar,
+      ":":   hookVar,
+      "?":   hookVar,
+      "$":   hookVar,
+      // The preferred way to escape Identifiers is using double quotes, ref: http://sqlite.org/lang_keywords.html
+      "\"":   hookIdentifierDoublequote,
+      // there is also support for backtics, ref: http://sqlite.org/lang_keywords.html
+      "`":   hookIdentifier
+    }
+  });
+
   // the query language used by Apache Cassandra is called CQL, but this mime type
   // is called Cassandra to avoid confusion with Contextual Query Language
   CodeMirror.defineMIME("text/x-cassandra", {
@@ -342,7 +387,7 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
     client:     set("appinfo arraysize autocommit autoprint autorecovery autotrace blockterminator break btitle cmdsep colsep compatibility compute concat copycommit copytypecheck define describe echo editfile embedded escape exec execute feedback flagger flush heading headsep instance linesize lno loboffset logsource long longchunksize markup native newpage numformat numwidth pagesize pause pno recsep recsepchar release repfooter repheader serveroutput shiftinout show showmode size spool sqlblanklines sqlcase sqlcode sqlcontinue sqlnumber sqlpluscompatibility sqlprefix sqlprompt sqlterminator suffix tab term termout time timing trimout trimspool ttitle underline verify version wrap"),
     keywords:   set("abort accept access add all alter and any array arraylen as asc assert assign at attributes audit authorization avg base_table begin between binary_integer body boolean by case cast char char_base check close cluster clusters colauth column comment commit compress connect connected constant constraint crash create current currval cursor data_base database date dba deallocate debugoff debugon decimal declare default definition delay delete desc digits dispose distinct do drop else elseif elsif enable end entry escape exception exception_init exchange exclusive exists exit external fast fetch file for force form from function generic goto grant group having identified if immediate in increment index indexes indicator initial initrans insert interface intersect into is key level library like limited local lock log logging long loop master maxextents maxtrans member minextents minus mislabel mode modify multiset new next no noaudit nocompress nologging noparallel not nowait number_base object of off offline on online only open option or order out package parallel partition pctfree pctincrease pctused pls_integer positive positiven pragma primary prior private privileges procedure public raise range raw read rebuild record ref references refresh release rename replace resource restrict return returning returns reverse revoke rollback row rowid rowlabel rownum rows run savepoint schema segment select separate session set share snapshot some space split sql start statement storage subtype successful synonym tabauth table tables tablespace task terminate then to trigger truncate type union unique unlimited unrecoverable unusable update use using validate value values variable view views when whenever where while with work"),
     builtin:    set("abs acos add_months ascii asin atan atan2 average bfile bfilename bigserial bit blob ceil character chartorowid chr clob concat convert cos cosh count dec decode deref dual dump dup_val_on_index empty error exp false float floor found glb greatest hextoraw initcap instr instrb int integer isopen last_day least length lengthb ln lower lpad ltrim lub make_ref max min mlslabel mod months_between natural naturaln nchar nclob new_time next_day nextval nls_charset_decl_len nls_charset_id nls_charset_name nls_initcap nls_lower nls_sort nls_upper nlssort no_data_found notfound null number numeric nvarchar2 nvl others power rawtohex real reftohex round rowcount rowidtochar rowtype rpad rtrim serial sign signtype sin sinh smallint soundex sqlcode sqlerrm sqrt stddev string substr substrb sum sysdate tan tanh to_char text to_date to_label to_multi_byte to_number to_single_byte translate true trunc uid unlogged upper user userenv varchar varchar2 variance varying vsize xml"),
-    operatorChars: /^[*+\-%<>!=~]/,
+    operatorChars: /^[*\/+\-%<>!=~]/,
     dateSQL:    set("date time timestamp"),
     support:    set("doubleQuote nCharCast zerolessFloat binaryNumber hexNumber")
   });
@@ -350,8 +395,8 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
   // Created to support specific hive keywords
   CodeMirror.defineMIME("text/x-hive", {
     name: "sql",
-    keywords: set("select alter $elem$ $key$ $value$ add after all analyze and archive as asc before between binary both bucket buckets by cascade case cast change cluster clustered clusterstatus collection column columns comment compute concatenate continue create cross cursor data database databases dbproperties deferred delete delimited desc describe directory disable distinct distribute drop else enable end escaped exclusive exists explain export extended external false fetch fields fileformat first format formatted from full function functions grant group having hold_ddltime idxproperties if import in index indexes inpath inputdriver inputformat insert intersect into is items join keys lateral left like limit lines load local location lock locks mapjoin materialized minus msck no_drop nocompress not of offline on option or order out outer outputdriver outputformat overwrite partition partitioned partitions percent plus preserve procedure purge range rcfile read readonly reads rebuild recordreader recordwriter recover reduce regexp rename repair replace restrict revoke right rlike row schema schemas semi sequencefile serde serdeproperties set shared show show_database sort sorted ssl statistics stored streamtable table tables tablesample tblproperties temporary terminated textfile then tmp to touch transform trigger true unarchive undo union uniquejoin unlock update use using utc utc_tmestamp view when where while with"),
-    builtin: set("bool boolean long timestamp tinyint smallint bigint int float double date datetime unsigned string array struct map uniontype"),
+    keywords: set("select alter $elem$ $key$ $value$ add after all analyze and archive as asc before between binary both bucket buckets by cascade case cast change cluster clustered clusterstatus collection column columns comment compute concatenate continue create cross cursor data database databases dbproperties deferred delete delimited desc describe directory disable distinct distribute drop else enable end escaped exclusive exists explain export extended external fetch fields fileformat first format formatted from full function functions grant group having hold_ddltime idxproperties if import in index indexes inpath inputdriver inputformat insert intersect into is items join keys lateral left like limit lines load local location lock locks mapjoin materialized minus msck no_drop nocompress not of offline on option or order out outer outputdriver outputformat overwrite partition partitioned partitions percent plus preserve procedure purge range rcfile read readonly reads rebuild recordreader recordwriter recover reduce regexp rename repair replace restrict revoke right rlike row schema schemas semi sequencefile serde serdeproperties set shared show show_database sort sorted ssl statistics stored streamtable table tables tablesample tblproperties temporary terminated textfile then tmp to touch transform trigger unarchive undo union uniquejoin unlock update use using utc utc_tmestamp view when where while with admin authorization char compact compactions conf cube current current_date current_timestamp day decimal defined dependency directories elem_type exchange file following for grouping hour ignore inner interval jar less logical macro minute month more none noscan over owner partialscan preceding pretty principals protection reload rewrite role roles rollup rows second server sets skewed transactions truncate unbounded unset uri user values window year"),
+    builtin: set("bool boolean long timestamp tinyint smallint bigint int float double date datetime unsigned string array struct map uniontype key_type utctimestamp value_type varchar"),
     atoms: set("false true null unknown"),
     operatorChars: /^[*+\-%<>!=]/,
     dateSQL: set("date timestamp"),
@@ -361,12 +406,13 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
   CodeMirror.defineMIME("text/x-pgsql", {
     name: "sql",
     client: set("source"),
-    // http://www.postgresql.org/docs/9.5/static/sql-keywords-appendix.html
-    keywords: set(sqlKeywords + "a abort abs absent absolute access according action ada add admin after aggregate all allocate also always analyse analyze any are array array_agg array_max_cardinality asensitive assertion assignment asymmetric at atomic attribute attributes authorization avg backward base64 before begin begin_frame begin_partition bernoulli binary bit_length blob blocked bom both breadth c cache call called cardinality cascade cascaded case cast catalog catalog_name ceil ceiling chain characteristics characters character_length character_set_catalog character_set_name character_set_schema char_length check checkpoint class class_origin clob close cluster coalesce cobol collate collation collation_catalog collation_name collation_schema collect column columns column_name command_function command_function_code comment comments commit committed concurrently condition condition_number configuration conflict connect connection connection_name constraint constraints constraint_catalog constraint_name constraint_schema constructor contains content continue control conversion convert copy corr corresponding cost covar_pop covar_samp cross csv cube cume_dist current current_catalog current_date current_default_transform_group current_path current_role current_row current_schema current_time current_timestamp current_transform_group_for_type current_user cursor cursor_name cycle data database datalink datetime_interval_code datetime_interval_precision day db deallocate dec declare default defaults deferrable deferred defined definer degree delimiter delimiters dense_rank depth deref derived describe descriptor deterministic diagnostics dictionary disable discard disconnect dispatch dlnewcopy dlpreviouscopy dlurlcomplete dlurlcompleteonly dlurlcompletewrite dlurlpath dlurlpathonly dlurlpathwrite dlurlscheme dlurlserver dlvalue do document domain dynamic dynamic_function dynamic_function_code each element else empty enable encoding encrypted end end-exec end_frame end_partition enforced enum equals escape event every except exception exclude excluding exclusive exec execute exists exp explain expression extension external extract false family fetch file filter final first first_value flag float floor following for force foreign fortran forward found frame_row free freeze fs full function functions fusion g general generated get global go goto grant granted greatest grouping groups handler header hex hierarchy hold hour id identity if ignore ilike immediate immediately immutable implementation implicit import including increment indent index indexes indicator inherit inherits initially inline inner inout input insensitive instance instantiable instead integrity intersect intersection invoker isnull isolation k key key_member key_type label lag language large last last_value lateral lead leading leakproof least left length level library like_regex link listen ln load local localtime localtimestamp location locator lock locked logged lower m map mapping match matched materialized max maxvalue max_cardinality member merge message_length message_octet_length message_text method min minute minvalue mod mode modifies module month more move multiset mumps name names namespace national natural nchar nclob nesting new next nfc nfd nfkc nfkd nil no none normalize normalized nothing notify notnull nowait nth_value ntile null nullable nullif nulls number object occurrences_regex octets octet_length of off offset oids old only open operator option options ordering ordinality others out outer output over overlaps overlay overriding owned owner p pad parameter parameter_mode parameter_name parameter_ordinal_position parameter_specific_catalog parameter_specific_name parameter_specific_schema parser partial partition pascal passing passthrough password percent percentile_cont percentile_disc percent_rank period permission placing plans pli policy portion position position_regex power precedes preceding prepare prepared preserve primary prior privileges procedural procedure program public quote range rank read reads reassign recheck recovery recursive ref references referencing refresh regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy regr_syy reindex relative release rename repeatable replace replica requiring reset respect restart restore restrict result return returned_cardinality returned_length returned_octet_length returned_sqlstate returning returns revoke right role rollback rollup routine routine_catalog routine_name routine_schema row rows row_count row_number rule savepoint scale schema schema_name scope scope_catalog scope_name scope_schema scroll search second section security selective self sensitive sequence sequences serializable server server_name session session_user setof sets share show similar simple size skip snapshot some source space specific specifictype specific_name sql sqlcode sqlerror sqlexception sqlstate sqlwarning sqrt stable standalone start state statement static statistics stddev_pop stddev_samp stdin stdout storage strict strip structure style subclass_origin submultiset substring substring_regex succeeds sum symmetric sysid system system_time system_user t tables tablesample tablespace table_name temp template temporary then ties timezone_hour timezone_minute to token top_level_count trailing transaction transactions_committed transactions_rolled_back transaction_active transform transforms translate translate_regex translation treat trigger trigger_catalog trigger_name trigger_schema trim trim_array true truncate trusted type types uescape unbounded uncommitted under unencrypted unique unknown unlink unlisten unlogged unnamed unnest until untyped upper uri usage user user_defined_type_catalog user_defined_type_code user_defined_type_name user_defined_type_schema using vacuum valid validate validator value value_of varbinary variadic var_pop var_samp verbose version versioning view views volatile when whenever whitespace width_bucket window within work wrapper write xmlagg xmlattributes xmlbinary xmlcast xmlcomment xmlconcat xmldeclaration xmldocument xmlelement xmlexists xmlforest xmliterate xmlnamespaces xmlparse xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltext xmlvalidate year yes loop repeat"),
-    // http://www.postgresql.org/docs/9.5/static/datatype.html
-    builtin: set("bigint int8 bigserial serial8 bit varying varbit boolean bool box bytea character char varchar cidr circle date double precision float8 inet integer int int4 interval json jsonb line lseg macaddr money numeric decimal path pg_lsn point polygon real float4 smallint int2 smallserial serial2 serial serial4 text time without zone with timetz timestamp timestamptz tsquery tsvector txid_snapshot uuid xml"),
+    // For PostgreSQL - https://www.postgresql.org/docs/11/sql-keywords-appendix.html
+    // For pl/pgsql lang - https://github.com/postgres/postgres/blob/REL_11_2/src/pl/plpgsql/src/pl_scanner.c
+    keywords: set(sqlKeywords + "a abort abs absent absolute access according action ada add admin after aggregate alias all allocate also alter always analyse analyze and any are array array_agg array_max_cardinality as asc asensitive assert assertion assignment asymmetric at atomic attach attribute attributes authorization avg backward base64 before begin begin_frame begin_partition bernoulli between bigint binary bit bit_length blob blocked bom boolean both breadth by c cache call called cardinality cascade cascaded case cast catalog catalog_name ceil ceiling chain char char_length character character_length character_set_catalog character_set_name character_set_schema characteristics characters check checkpoint class class_origin clob close cluster coalesce cobol collate collation collation_catalog collation_name collation_schema collect column column_name columns command_function command_function_code comment comments commit committed concurrently condition condition_number configuration conflict connect connection connection_name constant constraint constraint_catalog constraint_name constraint_schema constraints constructor contains content continue control conversion convert copy corr corresponding cost count covar_pop covar_samp create cross csv cube cume_dist current current_catalog current_date current_default_transform_group current_path current_role current_row current_schema current_time current_timestamp current_transform_group_for_type current_user cursor cursor_name cycle data database datalink datatype date datetime_interval_code datetime_interval_precision day db deallocate debug dec decimal declare default defaults deferrable deferred defined definer degree delete delimiter delimiters dense_rank depends depth deref derived desc describe descriptor detach detail deterministic diagnostics dictionary disable discard disconnect dispatch distinct dlnewcopy dlpreviouscopy dlurlcomplete dlurlcompleteonly dlurlcompletewrite dlurlpath dlurlpathonly dlurlpathwrite dlurlscheme dlurlserver dlvalue do document domain double drop dump dynamic dynamic_function dynamic_function_code each element else elseif elsif empty enable encoding encrypted end end_frame end_partition endexec enforced enum equals errcode error escape event every except exception exclude excluding exclusive exec execute exists exit exp explain expression extension external extract false family fetch file filter final first first_value flag float floor following for force foreach foreign fortran forward found frame_row free freeze from fs full function functions fusion g general generated get global go goto grant granted greatest group grouping groups handler having header hex hierarchy hint hold hour id identity if ignore ilike immediate immediately immutable implementation implicit import in include including increment indent index indexes indicator info inherit inherits initially inline inner inout input insensitive insert instance instantiable instead int integer integrity intersect intersection interval into invoker is isnull isolation join k key key_member key_type label lag language large last last_value lateral lead leading leakproof least left length level library like like_regex limit link listen ln load local localtime localtimestamp location locator lock locked log logged loop lower m map mapping match matched materialized max max_cardinality maxvalue member merge message message_length message_octet_length message_text method min minute minvalue mod mode modifies module month more move multiset mumps name names namespace national natural nchar nclob nesting new next nfc nfd nfkc nfkd nil no none normalize normalized not nothing notice notify notnull nowait nth_value ntile null nullable nullif nulls number numeric object occurrences_regex octet_length octets of off offset oids old on only open operator option options or order ordering ordinality others out outer output over overlaps overlay overriding owned owner p pad parallel parameter parameter_mode parameter_name parameter_ordinal_position parameter_specific_catalog parameter_specific_name parameter_specific_schema parser partial partition pascal passing passthrough password path percent percent_rank percentile_cont percentile_disc perform period permission pg_context pg_datatype_name pg_exception_context pg_exception_detail pg_exception_hint placing plans pli policy portion position position_regex power precedes preceding precision prepare prepared preserve primary print_strict_params prior privileges procedural procedure procedures program public publication query quote raise range rank read reads real reassign recheck recovery recursive ref references referencing refresh regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy regr_syy reindex relative release rename repeatable replace replica requiring reset respect restart restore restrict result result_oid return returned_cardinality returned_length returned_octet_length returned_sqlstate returning returns reverse revoke right role rollback rollup routine routine_catalog routine_name routine_schema routines row row_count row_number rows rowtype rule savepoint scale schema schema_name schemas scope scope_catalog scope_name scope_schema scroll search second section security select selective self sensitive sequence sequences serializable server server_name session session_user set setof sets share show similar simple size skip slice smallint snapshot some source space specific specific_name specifictype sql sqlcode sqlerror sqlexception sqlstate sqlwarning sqrt stable stacked standalone start state statement static statistics stddev_pop stddev_samp stdin stdout storage strict strip structure style subclass_origin submultiset subscription substring substring_regex succeeds sum symmetric sysid system system_time system_user t table table_name tables tablesample tablespace temp template temporary text then ties time timestamp timezone_hour timezone_minute to token top_level_count trailing transaction transaction_active transactions_committed transactions_rolled_back transform transforms translate translate_regex translation treat trigger trigger_catalog trigger_name trigger_schema trim trim_array true truncate trusted type types uescape unbounded uncommitted under unencrypted union unique unknown unlink unlisten unlogged unnamed unnest until untyped update upper uri usage use_column use_variable user user_defined_type_catalog user_defined_type_code user_defined_type_name user_defined_type_schema using vacuum valid validate validator value value_of values var_pop var_samp varbinary varchar variable_conflict variadic varying verbose version versioning view views volatile warning when whenever where while whitespace width_bucket window with within without work wrapper write xml xmlagg xmlattributes xmlbinary xmlcast xmlcomment xmlconcat xmldeclaration xmldocument xmlelement xmlexists xmlforest xmliterate xmlnamespaces xmlparse xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltext xmlvalidate year yes zone"),
+    // https://www.postgresql.org/docs/11/datatype.html
+    builtin: set("bigint int8 bigserial serial8 bit varying varbit boolean bool box bytea character char varchar cidr circle date double precision float8 inet integer int int4 interval json jsonb line lseg macaddr macaddr8 money numeric decimal path pg_lsn point polygon real float4 smallint int2 smallserial serial2 serial serial4 text time without zone with timetz timestamp timestamptz tsquery tsvector txid_snapshot uuid xml"),
     atoms: set("false true null unknown"),
-    operatorChars: /^[*+\-%<>!=&|^\/#@?~]/,
+    operatorChars: /^[*\/+\-%<>!=&|^\/#@?~]/,
     dateSQL: set("date time timestamp"),
     support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber nCharCast charsetCast")
   });
@@ -379,8 +425,43 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
     builtin: set("blob datetime first key __key__ string integer double boolean null"),
     operatorChars: /^[*+\-%<>!=]/
   });
-}());
 
+  // Greenplum
+  CodeMirror.defineMIME("text/x-gpsql", {
+    name: "sql",
+    client: set("source"),
+    //https://github.com/greenplum-db/gpdb/blob/master/src/include/parser/kwlist.h
+    keywords: set("abort absolute access action active add admin after aggregate all also alter always analyse analyze and any array as asc assertion assignment asymmetric at authorization backward before begin between bigint binary bit boolean both by cache called cascade cascaded case cast chain char character characteristics check checkpoint class close cluster coalesce codegen collate column comment commit committed concurrency concurrently configuration connection constraint constraints contains content continue conversion copy cost cpu_rate_limit create createdb createexttable createrole createuser cross csv cube current current_catalog current_date current_role current_schema current_time current_timestamp current_user cursor cycle data database day deallocate dec decimal declare decode default defaults deferrable deferred definer delete delimiter delimiters deny desc dictionary disable discard distinct distributed do document domain double drop dxl each else enable encoding encrypted end enum errors escape every except exchange exclude excluding exclusive execute exists explain extension external extract false family fetch fields filespace fill filter first float following for force foreign format forward freeze from full function global grant granted greatest group group_id grouping handler hash having header hold host hour identity if ignore ilike immediate immutable implicit in including inclusive increment index indexes inherit inherits initially inline inner inout input insensitive insert instead int integer intersect interval into invoker is isnull isolation join key language large last leading least left level like limit list listen load local localtime localtimestamp location lock log login mapping master match maxvalue median merge minute minvalue missing mode modifies modify month move name names national natural nchar new newline next no nocreatedb nocreateexttable nocreaterole nocreateuser noinherit nologin none noovercommit nosuperuser not nothing notify notnull nowait null nullif nulls numeric object of off offset oids old on only operator option options or order ordered others out outer over overcommit overlaps overlay owned owner parser partial partition partitions passing password percent percentile_cont percentile_disc placing plans position preceding precision prepare prepared preserve primary prior privileges procedural procedure protocol queue quote randomly range read readable reads real reassign recheck recursive ref references reindex reject relative release rename repeatable replace replica reset resource restart restrict returning returns revoke right role rollback rollup rootpartition row rows rule savepoint scatter schema scroll search second security segment select sequence serializable session session_user set setof sets share show similar simple smallint some split sql stable standalone start statement statistics stdin stdout storage strict strip subpartition subpartitions substring superuser symmetric sysid system table tablespace temp template temporary text then threshold ties time timestamp to trailing transaction treat trigger trim true truncate trusted type unbounded uncommitted unencrypted union unique unknown unlisten until update user using vacuum valid validation validator value values varchar variadic varying verbose version view volatile web when where whitespace window with within without work writable write xml xmlattributes xmlconcat xmlelement xmlexists xmlforest xmlparse xmlpi xmlroot xmlserialize year yes zone"),
+    builtin: set("bigint int8 bigserial serial8 bit varying varbit boolean bool box bytea character char varchar cidr circle date double precision float float8 inet integer int int4 interval json jsonb line lseg macaddr macaddr8 money numeric decimal path pg_lsn point polygon real float4 smallint int2 smallserial serial2 serial serial4 text time without zone with timetz timestamp timestamptz tsquery tsvector txid_snapshot uuid xml"),
+    atoms: set("false true null unknown"),
+    operatorChars: /^[*+\-%<>!=&|^\/#@?~]/,
+    dateSQL: set("date time timestamp"),
+    support: set("ODBCdotTable decimallessFloat zerolessFloat binaryNumber hexNumber nCharCast charsetCast")
+  });
+
+  // Spark SQL
+  CodeMirror.defineMIME("text/x-sparksql", {
+    name: "sql",
+    keywords: set("add after all alter analyze and anti archive array as asc at between bucket buckets by cache cascade case cast change clear cluster clustered codegen collection column columns comment commit compact compactions compute concatenate cost create cross cube current current_date current_timestamp database databases datata dbproperties defined delete delimited deny desc describe dfs directories distinct distribute drop else end escaped except exchange exists explain export extended external false fields fileformat first following for format formatted from full function functions global grant group grouping having if ignore import in index indexes inner inpath inputformat insert intersect interval into is items join keys last lateral lazy left like limit lines list load local location lock locks logical macro map minus msck natural no not null nulls of on optimize option options or order out outer outputformat over overwrite partition partitioned partitions percent preceding principals purge range recordreader recordwriter recover reduce refresh regexp rename repair replace reset restrict revoke right rlike role roles rollback rollup row rows schema schemas select semi separated serde serdeproperties set sets show skewed sort sorted start statistics stored stratify struct table tables tablesample tblproperties temp temporary terminated then to touch transaction transactions transform true truncate unarchive unbounded uncache union unlock unset use using values view when where window with"),
+    builtin: set("tinyint smallint int bigint boolean float double string binary timestamp decimal array map struct uniontype delimited serde sequencefile textfile rcfile inputformat outputformat"),
+    atoms: set("false true null"),
+    operatorChars: /^[*\/+\-%<>!=~&|^]/,
+    dateSQL: set("date time timestamp"),
+    support: set("ODBCdotTable doubleQuote zerolessFloat")
+  });
+
+  // Esper
+  CodeMirror.defineMIME("text/x-esper", {
+    name: "sql",
+    client: set("source"),
+    // http://www.espertech.com/esper/release-5.5.0/esper-reference/html/appendix_keywords.html
+    keywords: set("alter and as asc between by count create delete desc distinct drop from group having in insert into is join like not on or order select set table union update values where limit after all and as at asc avedev avg between by case cast coalesce count create current_timestamp day days delete define desc distinct else end escape events every exists false first from full group having hour hours in inner insert instanceof into irstream is istream join last lastweekday left limit like max match_recognize matches median measures metadatasql min minute minutes msec millisecond milliseconds not null offset on or order outer output partition pattern prev prior regexp retain-union retain-intersection right rstream sec second seconds select set some snapshot sql stddev sum then true unidirectional until update variable weekday when where window"),
+    builtin: {},
+    atoms: set("false true null"),
+    operatorChars: /^[*+\-%<>!=&|^\/#@?~]/,
+    dateSQL: set("time"),
+    support: set("decimallessFloat zerolessFloat binaryNumber hexNumber")
+  });
 });
 
 /*
diff --git a/public/vendor/plugins/codemirror/mode/stex/index.html b/public/vendor/plugins/codemirror/mode/stex/index.html
index 14679da4f3..225f485fe2 100644
--- a/public/vendor/plugins/codemirror/mode/stex/index.html
+++ b/public/vendor/plugins/codemirror/mode/stex/index.html
@@ -9,7 +9,7 @@
 <script src="stex.js"></script>
 <style>.CodeMirror {background: #f8f8f8;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -103,6 +103,12 @@
       var editor = CodeMirror.fromTextArea(document.getElementById("code"), {});
     </script>
 
+    <p>sTeX mode supports this option:</p>
+    <d1>
+      <dt><code>inMathMode: boolean</code></dt>
+      <dd>Whether to start parsing in math mode (default: <code>false</code>).</dd>
+    </d1>
+
     <p><strong>MIME types defined:</strong> <code>text/x-stex</code>.</p>
 
     <p><strong>Parsing/Highlighting Tests:</strong> <a href="../../test/index.html#stex_*">normal</a>,  <a href="../../test/index.html#verbose,stex_*">verbose</a>.</p>
diff --git a/public/vendor/plugins/codemirror/mode/stex/stex.js b/public/vendor/plugins/codemirror/mode/stex/stex.js
index 835ed46d1e..140e5a8af9 100644
--- a/public/vendor/plugins/codemirror/mode/stex/stex.js
+++ b/public/vendor/plugins/codemirror/mode/stex/stex.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 /*
  * Author: Constantin Jucovschi (c.jucovschi@jacobs-university.de)
@@ -16,7 +16,7 @@
 })(function(CodeMirror) {
   "use strict";
 
-  CodeMirror.defineMode("stex", function() {
+  CodeMirror.defineMode("stex", function(_config, parserConfig) {
     "use strict";
 
     function pushCommand(state, command) {
@@ -78,6 +78,14 @@
     plugins["begin"] = addPluginPattern("begin", "tag", ["atom"]);
     plugins["end"] = addPluginPattern("end", "tag", ["atom"]);
 
+    plugins["label"    ] = addPluginPattern("label"    , "tag", ["atom"]);
+    plugins["ref"      ] = addPluginPattern("ref"      , "tag", ["atom"]);
+    plugins["eqref"    ] = addPluginPattern("eqref"    , "tag", ["atom"]);
+    plugins["cite"     ] = addPluginPattern("cite"     , "tag", ["atom"]);
+    plugins["bibitem"  ] = addPluginPattern("bibitem"  , "tag", ["atom"]);
+    plugins["Bibitem"  ] = addPluginPattern("Bibitem"  , "tag", ["atom"]);
+    plugins["RBibitem" ] = addPluginPattern("RBibitem" , "tag", ["atom"]);
+
     plugins["DEFAULT"] = function () {
       this.name = "DEFAULT";
       this.style = "tag";
@@ -117,6 +125,10 @@
         setState(state, function(source, state){ return inMathMode(source, state, "\\]"); });
         return "keyword";
       }
+      if (source.match("\\(")) {
+        setState(state, function(source, state){ return inMathMode(source, state, "\\)"); });
+        return "keyword";
+      }
       if (source.match("$$")) {
         setState(state, function(source, state){ return inMathMode(source, state, "$$"); });
         return "keyword";
@@ -161,7 +173,7 @@
       if (source.eatSpace()) {
         return null;
       }
-      if (source.match(endModeSeq)) {
+      if (endModeSeq && source.match(endModeSeq)) {
         setState(state, normal);
         return "keyword";
       }
@@ -223,9 +235,10 @@
 
     return {
       startState: function() {
+        var f = parserConfig.inMathMode ? function(source, state){ return inMathMode(source, state); } : normal;
         return {
           cmdState: [],
-          f: normal
+          f: f
         };
       },
       copyState: function(s) {
diff --git a/public/vendor/plugins/codemirror/mode/stex/test.js b/public/vendor/plugins/codemirror/mode/stex/test.js
index 22f027ec7b..b1dbcf4cd5 100644
--- a/public/vendor/plugins/codemirror/mode/stex/test.js
+++ b/public/vendor/plugins/codemirror/mode/stex/test.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function() {
   var mode = CodeMirror.getMode({tabSize: 4}, "stex");
@@ -111,9 +111,18 @@
   MT("inlineMath",
      "[keyword $][number 3][variable-2 x][tag ^][number 2.45]-[tag \\sqrt][bracket {][tag \\$\\alpha][bracket }] = [number 2][keyword $] other text");
 
+  MT("inlineMathLatexStyle",
+     "[keyword \\(][number 3][variable-2 x][tag ^][number 2.45]-[tag \\sqrt][bracket {][tag \\$\\alpha][bracket }] = [number 2][keyword \\)] other text");
+
   MT("displayMath",
      "More [keyword $$]\t[variable-2 S][tag ^][variable-2 n][tag \\sum] [variable-2 i][keyword $$] other text");
 
+  MT("displayMath environment",
+     "[tag \\begin][bracket {][atom equation][bracket }] x [tag \\end][bracket {][atom equation][bracket }] other text");
+
+  MT("displayMath environment with label",
+     "[tag \\begin][bracket {][atom equation][bracket }][tag \\label][bracket {][atom eq1][bracket }] x [tag \\end][bracket {][atom equation][bracket }] other text~[tag \\ref][bracket {][atom eq1][bracket }]");
+
   MT("mathWithComment",
      "[keyword $][variable-2 x] [comment % $]",
      "[variable-2 y][keyword $] other text");
diff --git a/public/vendor/plugins/codemirror/mode/stylus/index.html b/public/vendor/plugins/codemirror/mode/stylus/index.html
index 862c18f250..491c14d55b 100644
--- a/public/vendor/plugins/codemirror/mode/stylus/index.html
+++ b/public/vendor/plugins/codemirror/mode/stylus/index.html
@@ -11,7 +11,7 @@
 <script src="../../addon/hint/css-hint.js"></script>
 <style>.CodeMirror {background: #f8f8f8;} form{margin-bottom: .7em;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/stylus/stylus.js b/public/vendor/plugins/codemirror/mode/stylus/stylus.js
index 662cd03c04..dbe241d613 100644
--- a/public/vendor/plugins/codemirror/mode/stylus/stylus.js
+++ b/public/vendor/plugins/codemirror/mode/stylus/stylus.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 // Stylus mode created by Dmitry Kiselyov http://git.io/AaRB
 
@@ -15,6 +15,7 @@
 
   CodeMirror.defineMode("stylus", function(config) {
     var indentUnit = config.indentUnit,
+        indentUnitString = '',
         tagKeywords = keySet(tagKeywords_),
         tagVariablesRegexp = /^(a|b|i|s|col|em)$/i,
         propertyKeywords = keySet(propertyKeywords_),
@@ -38,6 +39,8 @@
         type,
         override;
 
+    while (indentUnitString.length < indentUnit) indentUnitString += ' ';
+
     /**
      * Tokenizers
      */
@@ -73,7 +76,7 @@
       if (ch == "#") {
         stream.next();
         // Hex color
-        if (stream.match(/^[0-9a-f]{6}|[0-9a-f]{3}/i)) {
+        if (stream.match(/^[0-9a-f]{3}([0-9a-f]([0-9a-f]{2}){0,2})?\b/i)) {
           return ["atom", "atom"];
         }
         // ID selector
@@ -313,7 +316,7 @@
           return pushContext(state, stream, "block", 0);
         }
       }
-      if (typeIsBlock(type, stream, state)) {
+      if (typeIsBlock(type, stream)) {
         return pushContext(state, stream, "block");
       }
       if (type == "}" && endOfLine(stream)) {
@@ -513,7 +516,7 @@
      */
     states.atBlock = function(type, stream, state) {
       if (type == "(") return pushContext(state, stream, "atBlock_parens");
-      if (typeIsBlock(type, stream, state)) {
+      if (typeIsBlock(type, stream)) {
         return pushContext(state, stream, "block");
       }
       if (typeIsInterpolation(type, stream)) {
@@ -672,7 +675,7 @@
             ch = textAfter && textAfter.charAt(0),
             indent = cx.indent,
             lineFirstWord = firstWordOfLine(textAfter),
-            lineIndent = line.length - line.replace(/^\s*/, "").length,
+            lineIndent = line.match(/^\s*/)[0].replace(/\t/g, indentUnitString).length,
             prevLineFirstWord = state.context.prev ? state.context.prev.line.firstWord : "",
             prevLineIndent = state.context.prev ? state.context.prev.line.indent : lineIndent;
 
@@ -681,7 +684,6 @@
              ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") ||
              ch == "{" && (cx.type == "at"))) {
           indent = cx.indent - indentUnit;
-          cx = cx.prev;
         } else if (!(/(\})/.test(ch))) {
           if (/@|\$|\d/.test(ch) ||
               /^\{/.test(textAfter) ||
@@ -732,11 +734,11 @@
   var documentTypes_ = ["domain", "regexp", "url", "url-prefix"];
   var mediaTypes_ = ["all","aural","braille","handheld","print","projection","screen","tty","tv","embossed"];
   var mediaFeatures_ = ["width","min-width","max-width","height","min-height","max-height","device-width","min-device-width","max-device-width","device-height","min-device-height","max-device-height","aspect-ratio","min-aspect-ratio","max-aspect-ratio","device-aspect-ratio","min-device-aspect-ratio","max-device-aspect-ratio","color","min-color","max-color","color-index","min-color-index","max-color-index","monochrome","min-monochrome","max-monochrome","resolution","min-resolution","max-resolution","scan","grid"];
-  var propertyKeywords_ = ["align-content","align-items","align-self","alignment-adjust","alignment-baseline","anchor-point","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","appearance","azimuth","backface-visibility","background","background-attachment","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","baseline-shift","binding","bleed","bookmark-label","bookmark-level","bookmark-state","bookmark-target","border","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","clear","clip","color","color-profile","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","content","counter-increment","counter-reset","crop","cue","cue-after","cue-before","cursor","direction","display","dominant-baseline","drop-initial-after-adjust","drop-initial-after-align","drop-initial-before-adjust","drop-initial-before-align","drop-initial-size","drop-initial-value","elevation","empty-cells","fit","fit-position","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","float-offset","flow-from","flow-into","font","font-feature-settings","font-family","font-kerning","font-language-override","font-size","font-size-adjust","font-stretch","font-style","font-synthesis","font-variant","font-variant-alternates","font-variant-caps","font-variant-east-asian","font-variant-ligatures","font-variant-numeric","font-variant-position","font-weight","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-position","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphens","icon","image-orientation","image-rendering","image-resolution","inline-box-align","justify-content","left","letter-spacing","line-break","line-height","line-stacking","line-stacking-ruby","line-stacking-shift","line-stacking-strategy","list-style","list-style-image","list-style-position","list-style-type","margin","margin-bottom","margin-left","margin-right","margin-top","marker-offset","marks","marquee-direction","marquee-loop","marquee-play-count","marquee-speed","marquee-style","max-height","max-width","min-height","min-width","move-to","nav-down","nav-index","nav-left","nav-right","nav-up","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-style","overflow-wrap","overflow-x","overflow-y","padding","padding-bottom","padding-left","padding-right","padding-top","page","page-break-after","page-break-before","page-break-inside","page-policy","pause","pause-after","pause-before","perspective","perspective-origin","pitch","pitch-range","play-during","position","presentation-level","punctuation-trim","quotes","region-break-after","region-break-before","region-break-inside","region-fragment","rendering-intent","resize","rest","rest-after","rest-before","richness","right","rotation","rotation-point","ruby-align","ruby-overhang","ruby-position","ruby-span","shape-image-threshold","shape-inside","shape-margin","shape-outside","size","speak","speak-as","speak-header","speak-numeral","speak-punctuation","speech-rate","stress","string-set","tab-size","table-layout","target","target-name","target-new","target-position","text-align","text-align-last","text-decoration","text-decoration-color","text-decoration-line","text-decoration-skip","text-decoration-style","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-height","text-indent","text-justify","text-outline","text-overflow","text-shadow","text-size-adjust","text-space-collapse","text-transform","text-underline-position","text-wrap","top","transform","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","volume","white-space","widows","width","word-break","word-spacing","word-wrap","z-index","clip-path","clip-rule","mask","enable-background","filter","flood-color","flood-opacity","lighting-color","stop-color","stop-opacity","pointer-events","color-interpolation","color-interpolation-filters","color-rendering","fill","fill-opacity","fill-rule","image-rendering","marker","marker-end","marker-mid","marker-start","shape-rendering","stroke","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width","text-rendering","baseline-shift","dominant-baseline","glyph-orientation-horizontal","glyph-orientation-vertical","text-anchor","writing-mode","font-smoothing","osx-font-smoothing"];
+  var propertyKeywords_ = ["align-content","align-items","align-self","alignment-adjust","alignment-baseline","anchor-point","animation","animation-delay","animation-direction","animation-duration","animation-fill-mode","animation-iteration-count","animation-name","animation-play-state","animation-timing-function","appearance","azimuth","backface-visibility","background","background-attachment","background-clip","background-color","background-image","background-origin","background-position","background-repeat","background-size","baseline-shift","binding","bleed","bookmark-label","bookmark-level","bookmark-state","bookmark-target","border","border-bottom","border-bottom-color","border-bottom-left-radius","border-bottom-right-radius","border-bottom-style","border-bottom-width","border-collapse","border-color","border-image","border-image-outset","border-image-repeat","border-image-slice","border-image-source","border-image-width","border-left","border-left-color","border-left-style","border-left-width","border-radius","border-right","border-right-color","border-right-style","border-right-width","border-spacing","border-style","border-top","border-top-color","border-top-left-radius","border-top-right-radius","border-top-style","border-top-width","border-width","bottom","box-decoration-break","box-shadow","box-sizing","break-after","break-before","break-inside","caption-side","clear","clip","color","color-profile","column-count","column-fill","column-gap","column-rule","column-rule-color","column-rule-style","column-rule-width","column-span","column-width","columns","content","counter-increment","counter-reset","crop","cue","cue-after","cue-before","cursor","direction","display","dominant-baseline","drop-initial-after-adjust","drop-initial-after-align","drop-initial-before-adjust","drop-initial-before-align","drop-initial-size","drop-initial-value","elevation","empty-cells","fit","fit-position","flex","flex-basis","flex-direction","flex-flow","flex-grow","flex-shrink","flex-wrap","float","float-offset","flow-from","flow-into","font","font-feature-settings","font-family","font-kerning","font-language-override","font-size","font-size-adjust","font-stretch","font-style","font-synthesis","font-variant","font-variant-alternates","font-variant-caps","font-variant-east-asian","font-variant-ligatures","font-variant-numeric","font-variant-position","font-weight","grid","grid-area","grid-auto-columns","grid-auto-flow","grid-auto-position","grid-auto-rows","grid-column","grid-column-end","grid-column-start","grid-row","grid-row-end","grid-row-start","grid-template","grid-template-areas","grid-template-columns","grid-template-rows","hanging-punctuation","height","hyphens","icon","image-orientation","image-rendering","image-resolution","inline-box-align","justify-content","left","letter-spacing","line-break","line-height","line-stacking","line-stacking-ruby","line-stacking-shift","line-stacking-strategy","list-style","list-style-image","list-style-position","list-style-type","margin","margin-bottom","margin-left","margin-right","margin-top","marker-offset","marks","marquee-direction","marquee-loop","marquee-play-count","marquee-speed","marquee-style","max-height","max-width","min-height","min-width","move-to","nav-down","nav-index","nav-left","nav-right","nav-up","object-fit","object-position","opacity","order","orphans","outline","outline-color","outline-offset","outline-style","outline-width","overflow","overflow-style","overflow-wrap","overflow-x","overflow-y","padding","padding-bottom","padding-left","padding-right","padding-top","page","page-break-after","page-break-before","page-break-inside","page-policy","pause","pause-after","pause-before","perspective","perspective-origin","pitch","pitch-range","play-during","position","presentation-level","punctuation-trim","quotes","region-break-after","region-break-before","region-break-inside","region-fragment","rendering-intent","resize","rest","rest-after","rest-before","richness","right","rotation","rotation-point","ruby-align","ruby-overhang","ruby-position","ruby-span","shape-image-threshold","shape-inside","shape-margin","shape-outside","size","speak","speak-as","speak-header","speak-numeral","speak-punctuation","speech-rate","stress","string-set","tab-size","table-layout","target","target-name","target-new","target-position","text-align","text-align-last","text-decoration","text-decoration-color","text-decoration-line","text-decoration-skip","text-decoration-style","text-emphasis","text-emphasis-color","text-emphasis-position","text-emphasis-style","text-height","text-indent","text-justify","text-outline","text-overflow","text-shadow","text-size-adjust","text-space-collapse","text-transform","text-underline-position","text-wrap","top","transform","transform-origin","transform-style","transition","transition-delay","transition-duration","transition-property","transition-timing-function","unicode-bidi","vertical-align","visibility","voice-balance","voice-duration","voice-family","voice-pitch","voice-range","voice-rate","voice-stress","voice-volume","volume","white-space","widows","width","will-change","word-break","word-spacing","word-wrap","z-index","clip-path","clip-rule","mask","enable-background","filter","flood-color","flood-opacity","lighting-color","stop-color","stop-opacity","pointer-events","color-interpolation","color-interpolation-filters","color-rendering","fill","fill-opacity","fill-rule","image-rendering","marker","marker-end","marker-mid","marker-start","shape-rendering","stroke","stroke-dasharray","stroke-dashoffset","stroke-linecap","stroke-linejoin","stroke-miterlimit","stroke-opacity","stroke-width","text-rendering","baseline-shift","dominant-baseline","glyph-orientation-horizontal","glyph-orientation-vertical","text-anchor","writing-mode","font-smoothing","osx-font-smoothing"];
   var nonStandardPropertyKeywords_ = ["scrollbar-arrow-color","scrollbar-base-color","scrollbar-dark-shadow-color","scrollbar-face-color","scrollbar-highlight-color","scrollbar-shadow-color","scrollbar-3d-light-color","scrollbar-track-color","shape-inside","searchfield-cancel-button","searchfield-decoration","searchfield-results-button","searchfield-results-decoration","zoom"];
   var fontProperties_ = ["font-family","src","unicode-range","font-variant","font-feature-settings","font-stretch","font-weight","font-style"];
   var colorKeywords_ = ["aliceblue","antiquewhite","aqua","aquamarine","azure","beige","bisque","black","blanchedalmond","blue","blueviolet","brown","burlywood","cadetblue","chartreuse","chocolate","coral","cornflowerblue","cornsilk","crimson","cyan","darkblue","darkcyan","darkgoldenrod","darkgray","darkgreen","darkkhaki","darkmagenta","darkolivegreen","darkorange","darkorchid","darkred","darksalmon","darkseagreen","darkslateblue","darkslategray","darkturquoise","darkviolet","deeppink","deepskyblue","dimgray","dodgerblue","firebrick","floralwhite","forestgreen","fuchsia","gainsboro","ghostwhite","gold","goldenrod","gray","grey","green","greenyellow","honeydew","hotpink","indianred","indigo","ivory","khaki","lavender","lavenderblush","lawngreen","lemonchiffon","lightblue","lightcoral","lightcyan","lightgoldenrodyellow","lightgray","lightgreen","lightpink","lightsalmon","lightseagreen","lightskyblue","lightslategray","lightsteelblue","lightyellow","lime","limegreen","linen","magenta","maroon","mediumaquamarine","mediumblue","mediumorchid","mediumpurple","mediumseagreen","mediumslateblue","mediumspringgreen","mediumturquoise","mediumvioletred","midnightblue","mintcream","mistyrose","moccasin","navajowhite","navy","oldlace","olive","olivedrab","orange","orangered","orchid","palegoldenrod","palegreen","paleturquoise","palevioletred","papayawhip","peachpuff","peru","pink","plum","powderblue","purple","rebeccapurple","red","rosybrown","royalblue","saddlebrown","salmon","sandybrown","seagreen","seashell","sienna","silver","skyblue","slateblue","slategray","snow","springgreen","steelblue","tan","teal","thistle","tomato","turquoise","violet","wheat","white","whitesmoke","yellow","yellowgreen"];
-  var valueKeywords_ = ["above","absolute","activeborder","additive","activecaption","afar","after-white-space","ahead","alias","all","all-scroll","alphabetic","alternate","always","amharic","amharic-abegede","antialiased","appworkspace","arabic-indic","armenian","asterisks","attr","auto","avoid","avoid-column","avoid-page","avoid-region","background","backwards","baseline","below","bidi-override","binary","bengali","blink","block","block-axis","bold","bolder","border","border-box","both","bottom","break","break-all","break-word","bullets","button","button-bevel","buttonface","buttonhighlight","buttonshadow","buttontext","calc","cambodian","capitalize","caps-lock-indicator","caption","captiontext","caret","cell","center","checkbox","circle","cjk-decimal","cjk-earthly-branch","cjk-heavenly-stem","cjk-ideographic","clear","clip","close-quote","col-resize","collapse","column","compact","condensed","contain","content","content-box","context-menu","continuous","copy","counter","counters","cover","crop","cross","crosshair","currentcolor","cursive","cyclic","dashed","decimal","decimal-leading-zero","default","default-button","destination-atop","destination-in","destination-out","destination-over","devanagari","disc","discard","disclosure-closed","disclosure-open","document","dot-dash","dot-dot-dash","dotted","double","down","e-resize","ease","ease-in","ease-in-out","ease-out","element","ellipse","ellipsis","embed","end","ethiopic","ethiopic-abegede","ethiopic-abegede-am-et","ethiopic-abegede-gez","ethiopic-abegede-ti-er","ethiopic-abegede-ti-et","ethiopic-halehame-aa-er","ethiopic-halehame-aa-et","ethiopic-halehame-am-et","ethiopic-halehame-gez","ethiopic-halehame-om-et","ethiopic-halehame-sid-et","ethiopic-halehame-so-et","ethiopic-halehame-ti-er","ethiopic-halehame-ti-et","ethiopic-halehame-tig","ethiopic-numeric","ew-resize","expanded","extends","extra-condensed","extra-expanded","fantasy","fast","fill","fixed","flat","flex","footnotes","forwards","from","geometricPrecision","georgian","graytext","groove","gujarati","gurmukhi","hand","hangul","hangul-consonant","hebrew","help","hidden","hide","higher","highlight","highlighttext","hiragana","hiragana-iroha","horizontal","hsl","hsla","icon","ignore","inactiveborder","inactivecaption","inactivecaptiontext","infinite","infobackground","infotext","inherit","initial","inline","inline-axis","inline-block","inline-flex","inline-table","inset","inside","intrinsic","invert","italic","japanese-formal","japanese-informal","justify","kannada","katakana","katakana-iroha","keep-all","khmer","korean-hangul-formal","korean-hanja-formal","korean-hanja-informal","landscape","lao","large","larger","left","level","lighter","line-through","linear","linear-gradient","lines","list-item","listbox","listitem","local","logical","loud","lower","lower-alpha","lower-armenian","lower-greek","lower-hexadecimal","lower-latin","lower-norwegian","lower-roman","lowercase","ltr","malayalam","match","matrix","matrix3d","media-controls-background","media-current-time-display","media-fullscreen-button","media-mute-button","media-play-button","media-return-to-realtime-button","media-rewind-button","media-seek-back-button","media-seek-forward-button","media-slider","media-sliderthumb","media-time-remaining-display","media-volume-slider","media-volume-slider-container","media-volume-sliderthumb","medium","menu","menulist","menulist-button","menulist-text","menulist-textfield","menutext","message-box","middle","min-intrinsic","mix","mongolian","monospace","move","multiple","myanmar","n-resize","narrower","ne-resize","nesw-resize","no-close-quote","no-drop","no-open-quote","no-repeat","none","normal","not-allowed","nowrap","ns-resize","numbers","numeric","nw-resize","nwse-resize","oblique","octal","open-quote","optimizeLegibility","optimizeSpeed","oriya","oromo","outset","outside","outside-shape","overlay","overline","padding","padding-box","painted","page","paused","persian","perspective","plus-darker","plus-lighter","pointer","polygon","portrait","pre","pre-line","pre-wrap","preserve-3d","progress","push-button","radial-gradient","radio","read-only","read-write","read-write-plaintext-only","rectangle","region","relative","repeat","repeating-linear-gradient","repeating-radial-gradient","repeat-x","repeat-y","reset","reverse","rgb","rgba","ridge","right","rotate","rotate3d","rotateX","rotateY","rotateZ","round","row-resize","rtl","run-in","running","s-resize","sans-serif","scale","scale3d","scaleX","scaleY","scaleZ","scroll","scrollbar","se-resize","searchfield","searchfield-cancel-button","searchfield-decoration","searchfield-results-button","searchfield-results-decoration","semi-condensed","semi-expanded","separate","serif","show","sidama","simp-chinese-formal","simp-chinese-informal","single","skew","skewX","skewY","skip-white-space","slide","slider-horizontal","slider-vertical","sliderthumb-horizontal","sliderthumb-vertical","slow","small","small-caps","small-caption","smaller","solid","somali","source-atop","source-in","source-out","source-over","space","spell-out","square","square-button","start","static","status-bar","stretch","stroke","sub","subpixel-antialiased","super","sw-resize","symbolic","symbols","table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row","table-row-group","tamil","telugu","text","text-bottom","text-top","textarea","textfield","thai","thick","thin","threeddarkshadow","threedface","threedhighlight","threedlightshadow","threedshadow","tibetan","tigre","tigrinya-er","tigrinya-er-abegede","tigrinya-et","tigrinya-et-abegede","to","top","trad-chinese-formal","trad-chinese-informal","translate","translate3d","translateX","translateY","translateZ","transparent","ultra-condensed","ultra-expanded","underline","up","upper-alpha","upper-armenian","upper-greek","upper-hexadecimal","upper-latin","upper-norwegian","upper-roman","uppercase","urdu","url","var","vertical","vertical-text","visible","visibleFill","visiblePainted","visibleStroke","visual","w-resize","wait","wave","wider","window","windowframe","windowtext","words","x-large","x-small","xor","xx-large","xx-small","bicubic","optimizespeed","grayscale","row","row-reverse","wrap","wrap-reverse","column-reverse","flex-start","flex-end","space-between","space-around"];
+  var valueKeywords_ = ["above","absolute","activeborder","additive","activecaption","afar","after-white-space","ahead","alias","all","all-scroll","alphabetic","alternate","always","amharic","amharic-abegede","antialiased","appworkspace","arabic-indic","armenian","asterisks","attr","auto","avoid","avoid-column","avoid-page","avoid-region","background","backwards","baseline","below","bidi-override","binary","bengali","blink","block","block-axis","bold","bolder","border","border-box","both","bottom","break","break-all","break-word","bullets","button","button-bevel","buttonface","buttonhighlight","buttonshadow","buttontext","calc","cambodian","capitalize","caps-lock-indicator","caption","captiontext","caret","cell","center","checkbox","circle","cjk-decimal","cjk-earthly-branch","cjk-heavenly-stem","cjk-ideographic","clear","clip","close-quote","col-resize","collapse","column","compact","condensed","contain","content","contents","content-box","context-menu","continuous","copy","counter","counters","cover","crop","cross","crosshair","currentcolor","cursive","cyclic","dashed","decimal","decimal-leading-zero","default","default-button","destination-atop","destination-in","destination-out","destination-over","devanagari","disc","discard","disclosure-closed","disclosure-open","document","dot-dash","dot-dot-dash","dotted","double","down","e-resize","ease","ease-in","ease-in-out","ease-out","element","ellipse","ellipsis","embed","end","ethiopic","ethiopic-abegede","ethiopic-abegede-am-et","ethiopic-abegede-gez","ethiopic-abegede-ti-er","ethiopic-abegede-ti-et","ethiopic-halehame-aa-er","ethiopic-halehame-aa-et","ethiopic-halehame-am-et","ethiopic-halehame-gez","ethiopic-halehame-om-et","ethiopic-halehame-sid-et","ethiopic-halehame-so-et","ethiopic-halehame-ti-er","ethiopic-halehame-ti-et","ethiopic-halehame-tig","ethiopic-numeric","ew-resize","expanded","extends","extra-condensed","extra-expanded","fantasy","fast","fill","fixed","flat","flex","footnotes","forwards","from","geometricPrecision","georgian","graytext","groove","gujarati","gurmukhi","hand","hangul","hangul-consonant","hebrew","help","hidden","hide","higher","highlight","highlighttext","hiragana","hiragana-iroha","horizontal","hsl","hsla","icon","ignore","inactiveborder","inactivecaption","inactivecaptiontext","infinite","infobackground","infotext","inherit","initial","inline","inline-axis","inline-block","inline-flex","inline-table","inset","inside","intrinsic","invert","italic","japanese-formal","japanese-informal","justify","kannada","katakana","katakana-iroha","keep-all","khmer","korean-hangul-formal","korean-hanja-formal","korean-hanja-informal","landscape","lao","large","larger","left","level","lighter","line-through","linear","linear-gradient","lines","list-item","listbox","listitem","local","logical","loud","lower","lower-alpha","lower-armenian","lower-greek","lower-hexadecimal","lower-latin","lower-norwegian","lower-roman","lowercase","ltr","malayalam","match","matrix","matrix3d","media-controls-background","media-current-time-display","media-fullscreen-button","media-mute-button","media-play-button","media-return-to-realtime-button","media-rewind-button","media-seek-back-button","media-seek-forward-button","media-slider","media-sliderthumb","media-time-remaining-display","media-volume-slider","media-volume-slider-container","media-volume-sliderthumb","medium","menu","menulist","menulist-button","menulist-text","menulist-textfield","menutext","message-box","middle","min-intrinsic","mix","mongolian","monospace","move","multiple","myanmar","n-resize","narrower","ne-resize","nesw-resize","no-close-quote","no-drop","no-open-quote","no-repeat","none","normal","not-allowed","nowrap","ns-resize","numbers","numeric","nw-resize","nwse-resize","oblique","octal","open-quote","optimizeLegibility","optimizeSpeed","oriya","oromo","outset","outside","outside-shape","overlay","overline","padding","padding-box","painted","page","paused","persian","perspective","plus-darker","plus-lighter","pointer","polygon","portrait","pre","pre-line","pre-wrap","preserve-3d","progress","push-button","radial-gradient","radio","read-only","read-write","read-write-plaintext-only","rectangle","region","relative","repeat","repeating-linear-gradient","repeating-radial-gradient","repeat-x","repeat-y","reset","reverse","rgb","rgba","ridge","right","rotate","rotate3d","rotateX","rotateY","rotateZ","round","row-resize","rtl","run-in","running","s-resize","sans-serif","scale","scale3d","scaleX","scaleY","scaleZ","scroll","scrollbar","scroll-position","se-resize","searchfield","searchfield-cancel-button","searchfield-decoration","searchfield-results-button","searchfield-results-decoration","semi-condensed","semi-expanded","separate","serif","show","sidama","simp-chinese-formal","simp-chinese-informal","single","skew","skewX","skewY","skip-white-space","slide","slider-horizontal","slider-vertical","sliderthumb-horizontal","sliderthumb-vertical","slow","small","small-caps","small-caption","smaller","solid","somali","source-atop","source-in","source-out","source-over","space","spell-out","square","square-button","start","static","status-bar","stretch","stroke","sub","subpixel-antialiased","super","sw-resize","symbolic","symbols","table","table-caption","table-cell","table-column","table-column-group","table-footer-group","table-header-group","table-row","table-row-group","tamil","telugu","text","text-bottom","text-top","textarea","textfield","thai","thick","thin","threeddarkshadow","threedface","threedhighlight","threedlightshadow","threedshadow","tibetan","tigre","tigrinya-er","tigrinya-er-abegede","tigrinya-et","tigrinya-et-abegede","to","top","trad-chinese-formal","trad-chinese-informal","translate","translate3d","translateX","translateY","translateZ","transparent","ultra-condensed","ultra-expanded","underline","up","upper-alpha","upper-armenian","upper-greek","upper-hexadecimal","upper-latin","upper-norwegian","upper-roman","uppercase","urdu","url","var","vertical","vertical-text","visible","visibleFill","visiblePainted","visibleStroke","visual","w-resize","wait","wave","wider","window","windowframe","windowtext","words","x-large","x-small","xor","xx-large","xx-small","bicubic","optimizespeed","grayscale","row","row-reverse","wrap","wrap-reverse","column-reverse","flex-start","flex-end","space-between","space-around", "unset"];
 
   var wordOperatorKeywords_ = ["in","and","or","not","is not","is a","is","isnt","defined","if unless"],
       blockKeywords_ = ["for","if","else","unless", "from", "to"],
diff --git a/public/vendor/plugins/codemirror/mode/swift/index.html b/public/vendor/plugins/codemirror/mode/swift/index.html
index 109f3fdb07..ce2a3a02f9 100644
--- a/public/vendor/plugins/codemirror/mode/swift/index.html
+++ b/public/vendor/plugins/codemirror/mode/swift/index.html
@@ -12,7 +12,7 @@
 	.CodeMirror { border: 2px inset #dee; }
     </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -28,50 +28,32 @@
 <article>
 <h2>Swift mode</h2>
 <form><textarea id="code" name="code">
-//
-//  TipCalculatorModel.swift
-//  TipCalculator
-//
-//  Created by Main Account on 12/18/14.
-//  Copyright (c) 2014 Razeware LLC. All rights reserved.
-//
-
-import Foundation
-
-class TipCalculatorModel {
-
-  var total: Double
-  var taxPct: Double
-  var subtotal: Double {
-    get {
-      return total / (taxPct + 1)
-    }
-  }
-
-  init(total: Double, taxPct: Double) {
-    self.total = total
-    self.taxPct = taxPct
-  }
-
-  func calcTipWithTipPct(tipPct: Double) -> Double {
-    return subtotal * tipPct
-  }
-
-  func returnPossibleTips() -> [Int: Double] {
-
-    let possibleTipsInferred = [0.15, 0.18, 0.20]
-    let possibleTipsExplicit:[Double] = [0.15, 0.18, 0.20]
-
-    var retval = [Int: Double]()
-    for possibleTip in possibleTipsInferred {
-      let intPct = Int(possibleTip*100)
-      retval[intPct] = calcTipWithTipPct(possibleTip)
-    }
-    return retval
-
-  }
-
+protocol HeaderViewProtocol {
+    func setTitle(_ string: String)
 }
+
+struct AnyHeaderView {
+    let view: UIView
+    let headerView: HeaderViewProtocol
+    init<T: UIView>(view: T) where T: HeaderViewProtocol {
+        self.view = view
+        self.headerView = view
+    }
+}
+
+let header = AnyHeaderView(view: myView)
+header.headerView.setTitle("hi")
+
+struct HeaderView {
+    let view: UIView
+    let setTitle: (String) -> ()
+}
+
+var label = UILabel()
+let header = HeaderView(view: label) { str in
+    label.text = str
+}
+header.setTitle("hello")
 </textarea></form>
 
     <script>
diff --git a/public/vendor/plugins/codemirror/mode/swift/swift.js b/public/vendor/plugins/codemirror/mode/swift/swift.js
index 3c28ced329..55e31e2708 100644
--- a/public/vendor/plugins/codemirror/mode/swift/swift.js
+++ b/public/vendor/plugins/codemirror/mode/swift/swift.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 // Swift mode created by Michael Kaminsky https://github.com/mkaminsky11
 
@@ -19,25 +19,28 @@
     return set
   }
 
-  var keywords = wordSet(["var","let","class","deinit","enum","extension","func","import","init","protocol",
-                          "static","struct","subscript","typealias","as","dynamicType","is","new","super",
-                          "self","Self","Type","__COLUMN__","__FILE__","__FUNCTION__","__LINE__","break","case",
-                          "continue","default","do","else","fallthrough","if","in","for","return","switch",
-                          "where","while","associativity","didSet","get","infix","inout","left","mutating",
-                          "none","nonmutating","operator","override","postfix","precedence","prefix","right",
-                          "set","unowned","weak","willSet"])
-  var definingKeywords = wordSet(["var","let","class","enum","extension","func","import","protocol","struct",
-                                  "typealias","dynamicType","for"])
-  var atoms = wordSet(["Infinity","NaN","undefined","null","true","false","on","off","yes","no","nil","null",
-                       "this","super"])
-  var types = wordSet(["String","bool","int","string","double","Double","Int","Float","float","public",
-                       "private","extension"])
-  var operators = "+-/*%=|&<>#"
-  var punc = ";,.(){}[]"
-  var number = /^-?(?:(?:[\d_]+\.[_\d]*|\.[_\d]+|0o[0-7_\.]+|0b[01_\.]+)(?:e-?[\d_]+)?|0x[\d_a-f\.]+(?:p-?[\d_]+)?)/i
-  var identifier = /^[_A-Za-z$][_A-Za-z$0-9]*/
-  var property = /^[@\.][_A-Za-z$][_A-Za-z$0-9]*/
-  var regexp = /^\/(?!\s)(?:\/\/)?(?:\\.|[^\/])+\//
+  var keywords = wordSet(["_","var","let","class","enum","extension","import","protocol","struct","func","typealias","associatedtype",
+                          "open","public","internal","fileprivate","private","deinit","init","new","override","self","subscript","super",
+                          "convenience","dynamic","final","indirect","lazy","required","static","unowned","unowned(safe)","unowned(unsafe)","weak","as","is",
+                          "break","case","continue","default","else","fallthrough","for","guard","if","in","repeat","switch","where","while",
+                          "defer","return","inout","mutating","nonmutating","catch","do","rethrows","throw","throws","try","didSet","get","set","willSet",
+                          "assignment","associativity","infix","left","none","operator","postfix","precedence","precedencegroup","prefix","right",
+                          "Any","AnyObject","Type","dynamicType","Self","Protocol","__COLUMN__","__FILE__","__FUNCTION__","__LINE__"])
+  var definingKeywords = wordSet(["var","let","class","enum","extension","import","protocol","struct","func","typealias","associatedtype","for"])
+  var atoms = wordSet(["true","false","nil","self","super","_"])
+  var types = wordSet(["Array","Bool","Character","Dictionary","Double","Float","Int","Int8","Int16","Int32","Int64","Never","Optional","Set","String",
+                       "UInt8","UInt16","UInt32","UInt64","Void"])
+  var operators = "+-/*%=|&<>~^?!"
+  var punc = ":;,.(){}[]"
+  var binary = /^\-?0b[01][01_]*/
+  var octal = /^\-?0o[0-7][0-7_]*/
+  var hexadecimal = /^\-?0x[\dA-Fa-f][\dA-Fa-f_]*(?:(?:\.[\dA-Fa-f][\dA-Fa-f_]*)?[Pp]\-?\d[\d_]*)?/
+  var decimal = /^\-?\d[\d_]*(?:\.\d[\d_]*)?(?:[Ee]\-?\d[\d_]*)?/
+  var identifier = /^\$\d+|(`?)[_A-Za-z][_A-Za-z$0-9]*\1/
+  var property = /^\.(?:\$\d+|(`?)[_A-Za-z][_A-Za-z$0-9]*\1)/
+  var instruction = /^\#[A-Za-z]+/
+  var attribute = /^@(?:\$\d+|(`?)[_A-Za-z][_A-Za-z$0-9]*\1)/
+  //var regexp = /^\/(?!\s)(?:\/\/)?(?:\\.|[^\/])+\//
 
   function tokenBase(stream, state, prev) {
     if (stream.sol()) state.indented = stream.indentation()
@@ -53,8 +56,14 @@
         state.tokenize.push(tokenComment)
         return tokenComment(stream, state)
       }
-      if (stream.match(regexp)) return "string-2"
     }
+    if (stream.match(instruction)) return "builtin"
+    if (stream.match(attribute)) return "attribute"
+    if (stream.match(binary)) return "number"
+    if (stream.match(octal)) return "number"
+    if (stream.match(hexadecimal)) return "number"
+    if (stream.match(decimal)) return "number"
+    if (stream.match(property)) return "property"
     if (operators.indexOf(ch) > -1) {
       stream.next()
       return "operator"
@@ -64,25 +73,22 @@
       stream.match("..")
       return "punctuation"
     }
-    if (ch == '"' || ch == "'") {
-      stream.next()
-      var tokenize = tokenString(ch)
+    var stringMatch
+    if (stringMatch = stream.match(/("""|"|')/)) {
+      var tokenize = tokenString.bind(null, stringMatch[0])
       state.tokenize.push(tokenize)
       return tokenize(stream, state)
     }
 
-    if (stream.match(number)) return "number"
-    if (stream.match(property)) return "property"
-
     if (stream.match(identifier)) {
       var ident = stream.current()
+      if (types.hasOwnProperty(ident)) return "variable-2"
+      if (atoms.hasOwnProperty(ident)) return "atom"
       if (keywords.hasOwnProperty(ident)) {
         if (definingKeywords.hasOwnProperty(ident))
           state.prev = "define"
         return "keyword"
       }
-      if (types.hasOwnProperty(ident)) return "variable-2"
-      if (atoms.hasOwnProperty(ident)) return "atom"
       if (prev == "define") return "def"
       return "variable"
     }
@@ -110,30 +116,43 @@
     }
   }
 
-  function tokenString(quote) {
-    return function(stream, state) {
-      var ch, escaped = false
-      while (ch = stream.next()) {
-        if (escaped) {
-          if (ch == "(") {
-            state.tokenize.push(tokenUntilClosingParen())
-            return "string"
-          }
-          escaped = false
-        } else if (ch == quote) {
-          break
-        } else {
-          escaped = ch == "\\"
+  function tokenString(openQuote, stream, state) {
+    var singleLine = openQuote.length == 1
+    var ch, escaped = false
+    while (ch = stream.peek()) {
+      if (escaped) {
+        stream.next()
+        if (ch == "(") {
+          state.tokenize.push(tokenUntilClosingParen())
+          return "string"
         }
+        escaped = false
+      } else if (stream.match(openQuote)) {
+        state.tokenize.pop()
+        return "string"
+      } else {
+        stream.next()
+        escaped = ch == "\\"
       }
-      state.tokenize.pop()
-      return "string"
     }
+    if (singleLine) {
+      state.tokenize.pop()
+    }
+    return "string"
   }
 
   function tokenComment(stream, state) {
-    stream.match(/^(?:[^*]|\*(?!\/))*/)
-    if (stream.match("*/")) state.tokenize.pop()
+    var ch
+    while (true) {
+      stream.match(/^[^/*]+/, true)
+      ch = stream.next()
+      if (!ch) break
+      if (ch === "/" && stream.eat("*")) {
+        state.tokenize.push(tokenComment)
+      } else if (ch === "*" && stream.eat("/")) {
+        state.tokenize.pop()
+      }
+    }
     return "comment"
   }
 
@@ -194,7 +213,9 @@
 
       lineComment: "//",
       blockCommentStart: "/*",
-      blockCommentEnd: "*/"
+      blockCommentEnd: "*/",
+      fold: "brace",
+      closeBrackets: "()[]{}''\"\"``"
     }
   })
 
diff --git a/public/vendor/plugins/codemirror/mode/swift/test.js b/public/vendor/plugins/codemirror/mode/swift/test.js
new file mode 100644
index 0000000000..8c93721d12
--- /dev/null
+++ b/public/vendor/plugins/codemirror/mode/swift/test.js
@@ -0,0 +1,162 @@
+// CodeMirror, copyright (c) by Marijn Haverbeke and others
+// Distributed under an MIT license: https://codemirror.net/LICENSE
+
+(function() {
+  var mode = CodeMirror.getMode({indentUnit: 2}, "swift");
+  function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }
+
+  // Ensure all number types are properly represented.
+  MT("numbers",
+     "[keyword var] [def a] [operator =] [number 17]",
+     "[keyword var] [def b] [operator =] [number -0.5]",
+     "[keyword var] [def c] [operator =] [number 0.3456e-4]",
+     "[keyword var] [def d] [operator =] [number 345e2]",
+     "[keyword var] [def e] [operator =] [number 0o7324]",
+     "[keyword var] [def f] [operator =] [number 0b10010]",
+     "[keyword var] [def g] [operator =] [number -0x35ade]",
+     "[keyword var] [def h] [operator =] [number 0xaea.ep-13]",
+     "[keyword var] [def i] [operator =] [number 0x13ep6]");
+
+  // Variable/class/etc definition.
+  MT("definition",
+     "[keyword var] [def a] [operator =] [number 5]",
+     "[keyword let] [def b][punctuation :] [variable-2 Int] [operator =] [number 10]",
+     "[keyword class] [def C] [punctuation {] [punctuation }]",
+     "[keyword struct] [def D] [punctuation {] [punctuation }]",
+     "[keyword enum] [def E] [punctuation {] [punctuation }]",
+     "[keyword extension] [def F] [punctuation {] [punctuation }]",
+     "[keyword protocol] [def G] [punctuation {] [punctuation }]",
+     "[keyword func] [def h][punctuation ()] [punctuation {] [punctuation }]",
+     "[keyword import] [def Foundation]",
+     "[keyword typealias] [def NewString] [operator =] [variable-2 String]",
+     "[keyword associatedtype] [def I]",
+     "[keyword for] [def j] [keyword in] [number 0][punctuation ..][operator <][number 3] [punctuation {] [punctuation }]");
+
+  // Strings and string interpolation.
+  MT("strings",
+     "[keyword var] [def a][punctuation :] [variable-2 String] [operator =] [string \"test\"]",
+     "[keyword var] [def b][punctuation :] [variable-2 String] [operator =] [string \"\\(][variable a][string )\"]",
+     "[keyword var] [def c] [operator =] [string \"\"\"]",
+     "[string multi]",
+     "[string line]",
+     "[string \"test\"]",
+     "[string \"\"\"]",
+     "[variable print][punctuation (][string \"\"][punctuation )]");
+
+  // Comments.
+  MT("comments",
+     "[comment // This is a comment]",
+     "[comment /* This is another comment */]",
+     "[keyword var] [def a] [operator =] [number 5] [comment // Third comment]");
+
+  // Atoms.
+  MT("atoms",
+     "[keyword class] [def FooClass] [punctuation {]",
+     "  [keyword let] [def fooBool][punctuation :] [variable-2 Bool][operator ?]",
+     "  [keyword let] [def fooInt][punctuation :] [variable-2 Int][operator ?]",
+     "  [keyword func] [keyword init][punctuation (][variable fooBool][punctuation :] [variable-2 Bool][punctuation ,] [variable barBool][punctuation :] [variable-2 Bool][punctuation )] [punctuation {]",
+     "    [atom super][property .init][punctuation ()]",
+     "    [atom self][property .fooBool] [operator =] [variable fooBool]",
+     "    [variable fooInt] [operator =] [atom nil]",
+     "    [keyword if] [variable barBool] [operator ==] [atom true] [punctuation {]",
+     "      [variable print][punctuation (][string \"True!\"][punctuation )]",
+     "    [punctuation }] [keyword else] [keyword if] [variable barBool] [operator ==] [atom false] [punctuation {]",
+     "      [keyword for] [atom _] [keyword in] [number 0][punctuation ...][number 5] [punctuation {]",
+     "        [variable print][punctuation (][string \"False!\"][punctuation )]",
+     "      [punctuation }]",
+     "    [punctuation }]",
+     "  [punctuation }]",
+     "[punctuation }]");
+
+  // Types.
+  MT("types",
+     "[keyword var] [def a] [operator =] [variable-2 Array][operator <][variable-2 Int][operator >]",
+     "[keyword var] [def b] [operator =] [variable-2 Set][operator <][variable-2 Bool][operator >]",
+     "[keyword var] [def c] [operator =] [variable-2 Dictionary][operator <][variable-2 String][punctuation ,][variable-2 Character][operator >]",
+     "[keyword var] [def d][punctuation :] [variable-2 Int64][operator ?] [operator =] [variable-2 Optional][punctuation (][number 8][punctuation )]",
+     "[keyword func] [def e][punctuation ()] [operator ->] [variable-2 Void] [punctuation {]",
+     "  [keyword var] [def e1][punctuation :] [variable-2 Float] [operator =] [number 1.2]",
+     "[punctuation }]",
+     "[keyword func] [def f][punctuation ()] [operator ->] [variable-2 Never] [punctuation {]",
+     "  [keyword var] [def f1][punctuation :] [variable-2 Double] [operator =] [number 2.4]",
+     "[punctuation }]");
+
+  // Operators.
+  MT("operators",
+     "[keyword var] [def a] [operator =] [number 1] [operator +] [number 2]",
+     "[keyword var] [def b] [operator =] [number 1] [operator -] [number 2]",
+     "[keyword var] [def c] [operator =] [number 1] [operator *] [number 2]",
+     "[keyword var] [def d] [operator =] [number 1] [operator /] [number 2]",
+     "[keyword var] [def e] [operator =] [number 1] [operator %] [number 2]",
+     "[keyword var] [def f] [operator =] [number 1] [operator |] [number 2]",
+     "[keyword var] [def g] [operator =] [number 1] [operator &] [number 2]",
+     "[keyword var] [def h] [operator =] [number 1] [operator <<] [number 2]",
+     "[keyword var] [def i] [operator =] [number 1] [operator >>] [number 2]",
+     "[keyword var] [def j] [operator =] [number 1] [operator ^] [number 2]",
+     "[keyword var] [def k] [operator =] [operator ~][number 1]",
+     "[keyword var] [def l] [operator =] [variable foo] [operator ?] [number 1] [punctuation :] [number 2]",
+     "[keyword var] [def m][punctuation :] [variable-2 Int] [operator =] [variable-2 Optional][punctuation (][number 8][punctuation )][operator !]");
+
+  // Punctuation.
+  MT("punctuation",
+     "[keyword let] [def a] [operator =] [number 1][punctuation ;] [keyword let] [def b] [operator =] [number 2]",
+     "[keyword let] [def testArr][punctuation :] [punctuation [[][variable-2 Int][punctuation ]]] [operator =] [punctuation [[][variable a][punctuation ,] [variable b][punctuation ]]]",
+     "[keyword for] [def i] [keyword in] [number 0][punctuation ..][operator <][variable testArr][property .count] [punctuation {]",
+     "  [variable print][punctuation (][variable testArr][punctuation [[][variable i][punctuation ]])]",
+     "[punctuation }]");
+
+  // Identifiers.
+  MT("identifiers",
+     "[keyword let] [def abc] [operator =] [number 1]",
+     "[keyword let] [def ABC] [operator =] [number 2]",
+     "[keyword let] [def _123] [operator =] [number 3]",
+     "[keyword let] [def _$1$2$3] [operator =] [number 4]",
+     "[keyword let] [def A1$_c32_$_] [operator =] [number 5]",
+     "[keyword let] [def `var`] [operator =] [punctuation [[][number 1][punctuation ,] [number 2][punctuation ,] [number 3][punctuation ]]]",
+     "[keyword let] [def square$] [operator =] [variable `var`][property .map] [punctuation {][variable $0] [operator *] [variable $0][punctuation }]",
+     "$$ [number 1][variable a] $[atom _] [variable _$] [variable __] `[variable a] [variable b]`");
+
+  // Properties.
+  MT("properties",
+     "[variable print][punctuation (][variable foo][property .abc][punctuation )]",
+     "[variable print][punctuation (][variable foo][property .ABC][punctuation )]",
+     "[variable print][punctuation (][variable foo][property ._123][punctuation )]",
+     "[variable print][punctuation (][variable foo][property ._$1$2$3][punctuation )]",
+     "[variable print][punctuation (][variable foo][property .A1$_c32_$_][punctuation )]",
+     "[variable print][punctuation (][variable foo][property .`var`][punctuation )]",
+     "[variable print][punctuation (][variable foo][property .__][punctuation )]");
+
+  // Instructions or other things that start with #.
+  MT("instructions",
+     "[keyword if] [builtin #available][punctuation (][variable iOS] [number 9][punctuation ,] [operator *][punctuation )] [punctuation {}]",
+     "[variable print][punctuation (][builtin #file][punctuation ,] [builtin #function][punctuation )]",
+     "[variable print][punctuation (][builtin #line][punctuation ,] [builtin #column][punctuation )]",
+     "[builtin #if] [atom true]",
+     "[keyword import] [def A]",
+     "[builtin #elseif] [atom false]",
+     "[keyword import] [def B]",
+     "[builtin #endif]",
+     "[builtin #sourceLocation][punctuation (][variable file][punctuation :] [string \"file.swift\"][punctuation ,] [variable line][punctuation :] [number 2][punctuation )]");
+
+  // Attributes; things that start with @.
+  MT("attributes",
+     "[attribute @objc][punctuation (][variable objcFoo][punctuation :)]",
+     "[attribute @available][punctuation (][variable iOS][punctuation )]");
+
+  // Property/number edge case.
+  MT("property_number",
+     "[variable print][punctuation (][variable foo][property ._123][punctuation )]",
+     "[variable print][punctuation (]")
+
+  MT("nested_comments",
+     "[comment /*]",
+     "[comment But wait /* this is a nested comment */ for real]",
+     "[comment /**** let * me * show * you ****/]",
+     "[comment ///// let / me / show / you /////]",
+     "[comment */]");
+
+  // TODO: correctly identify when multiple variables are being declared
+  // by use of a comma-separated list.
+  // TODO: correctly identify when variables are being declared in a tuple.
+  // TODO: identify protocols as types when used before an extension?
+})();
diff --git a/public/vendor/plugins/codemirror/mode/tcl/index.html b/public/vendor/plugins/codemirror/mode/tcl/index.html
index ce4ad3423e..328c9eaf3a 100644
--- a/public/vendor/plugins/codemirror/mode/tcl/index.html
+++ b/public/vendor/plugins/codemirror/mode/tcl/index.html
@@ -10,7 +10,7 @@
 <script src="tcl.js"></script>
 <script src="../../addon/scroll/scrollpastend.js"></script>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/tcl/tcl.js b/public/vendor/plugins/codemirror/mode/tcl/tcl.js
index 8c76d52ca0..a7ec89c9e8 100644
--- a/public/vendor/plugins/codemirror/mode/tcl/tcl.js
+++ b/public/vendor/plugins/codemirror/mode/tcl/tcl.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 //tcl mode by Ford_Lawnmower :: Based on Velocity mode by Steve O'Hara
 
diff --git a/public/vendor/plugins/codemirror/mode/textile/index.html b/public/vendor/plugins/codemirror/mode/textile/index.html
index 42b156b1e2..e8d9b99a7c 100644
--- a/public/vendor/plugins/codemirror/mode/textile/index.html
+++ b/public/vendor/plugins/codemirror/mode/textile/index.html
@@ -9,7 +9,7 @@
 <script src="textile.js"></script>
 <style>.CodeMirror {background: #f8f8f8;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/textile/test.js b/public/vendor/plugins/codemirror/mode/textile/test.js
index 49cdaf9c91..754c5f6bd7 100644
--- a/public/vendor/plugins/codemirror/mode/textile/test.js
+++ b/public/vendor/plugins/codemirror/mode/textile/test.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function() {
   var mode = CodeMirror.getMode({tabSize: 4}, 'textile');
@@ -39,12 +39,12 @@
       '3 ** 3 = 27');
 
   MT('simpleLink',
-      '[link "CodeMirror":http://codemirror.net]');
+      '[link "CodeMirror":https://codemirror.net]');
 
   MT('referenceLink',
       '[link "CodeMirror":code_mirror]',
       'Normal Text.',
-      '[link [[code_mirror]]http://codemirror.net]');
+      '[link [[code_mirror]]https://codemirror.net]');
 
   MT('footCite',
       'foo bar[qualifier [[1]]]');
diff --git a/public/vendor/plugins/codemirror/mode/textile/textile.js b/public/vendor/plugins/codemirror/mode/textile/textile.js
index a6f7576582..b378fb61f5 100644
--- a/public/vendor/plugins/codemirror/mode/textile/textile.js
+++ b/public/vendor/plugins/codemirror/mode/textile/textile.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") { // CommonJS
@@ -203,7 +203,7 @@
     single: {
       bc: "bc",
       bq: "bq",
-      definitionList: /- [^(?::=)]+:=+/,
+      definitionList: /- .*?:=+/,
       definitionListEnd: /.*=:\s*$/,
       div: "div",
       drawTable: /\|.*\|/,
diff --git a/public/vendor/plugins/codemirror/mode/tiddlywiki/index.html b/public/vendor/plugins/codemirror/mode/tiddlywiki/index.html
index 77dd0457c1..8e1e5b1021 100644
--- a/public/vendor/plugins/codemirror/mode/tiddlywiki/index.html
+++ b/public/vendor/plugins/codemirror/mode/tiddlywiki/index.html
@@ -9,9 +9,9 @@
 <script src="../../lib/codemirror.js"></script>
 <script src="../../addon/edit/matchbrackets.js"></script>
 <script src="tiddlywiki.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/tiddlywiki/tiddlywiki.js b/public/vendor/plugins/codemirror/mode/tiddlywiki/tiddlywiki.js
index 1a3b3bc68a..a4fb89f658 100644
--- a/public/vendor/plugins/codemirror/mode/tiddlywiki/tiddlywiki.js
+++ b/public/vendor/plugins/codemirror/mode/tiddlywiki/tiddlywiki.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 /***
     |''Name''|tiddlywiki.js|
@@ -8,7 +8,7 @@
     |''Version''|0.1.7|
     |''Status''|''stable''|
     |''Source''|[[GitHub|https://github.com/pmario/CodeMirror2/blob/tw-syntax/mode/tiddlywiki]]|
-    |''Documentation''|http://codemirror.tiddlyspace.com/|
+    |''Documentation''|https://codemirror.tiddlyspace.com/|
     |''License''|[[MIT License|http://www.opensource.org/licenses/mit-license.php]]|
     |''CoreVersion''|2.5.0|
     |''Requires''|codemirror.js|
diff --git a/public/vendor/plugins/codemirror/mode/tiki/index.html b/public/vendor/plugins/codemirror/mode/tiki/index.html
index 091c5fb2a6..40e9855737 100644
--- a/public/vendor/plugins/codemirror/mode/tiki/index.html
+++ b/public/vendor/plugins/codemirror/mode/tiki/index.html
@@ -8,9 +8,9 @@
 <link rel="stylesheet" href="tiki.css">
 <script src="../../lib/codemirror.js"></script>
 <script src="tiki.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -85,7 +85,7 @@ Plugin (inline):
 {plugin attr="my attr"}
 </textarea></div>
 
-<script type="text/javascript">
+<script>
 	var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
         mode: 'tiki',      
         lineNumbers: true
diff --git a/public/vendor/plugins/codemirror/mode/tiki/tiki.js b/public/vendor/plugins/codemirror/mode/tiki/tiki.js
index 5e05b1ff01..092b859532 100644
--- a/public/vendor/plugins/codemirror/mode/tiki/tiki.js
+++ b/public/vendor/plugins/codemirror/mode/tiki/tiki.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -144,7 +144,7 @@ CodeMirror.defineMode('tiki', function(config) {
       type = "equals";
 
       if (peek == ">") {
-        ch = stream.next();
+        stream.next();
         peek = stream.peek();
       }
 
@@ -298,13 +298,13 @@ return {
     if (context && context.noIndent) return 0;
     if (context && /^{\//.test(textAfter))
         context = context.prev;
-        while (context && !context.startOfLine)
-          context = context.prev;
-        if (context) return context.indent + indentUnit;
-        else return 0;
-       },
-    electricChars: "/"
-  };
+    while (context && !context.startOfLine)
+        context = context.prev;
+    if (context) return context.indent + indentUnit;
+    else return 0;
+  },
+  electricChars: "/"
+};
 });
 
 CodeMirror.defineMIME("text/tiki", "tiki");
diff --git a/public/vendor/plugins/codemirror/mode/toml/index.html b/public/vendor/plugins/codemirror/mode/toml/index.html
index 90a2a0215b..5c359c79c0 100644
--- a/public/vendor/plugins/codemirror/mode/toml/index.html
+++ b/public/vendor/plugins/codemirror/mode/toml/index.html
@@ -7,9 +7,9 @@
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
 <script src="toml.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/toml/toml.js b/public/vendor/plugins/codemirror/mode/toml/toml.js
index baeca15568..891f384b5b 100644
--- a/public/vendor/plugins/codemirror/mode/toml/toml.js
+++ b/public/vendor/plugins/codemirror/mode/toml/toml.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/tornado/index.html b/public/vendor/plugins/codemirror/mode/tornado/index.html
index 8ee7ef56cc..6b261cf073 100644
--- a/public/vendor/plugins/codemirror/mode/tornado/index.html
+++ b/public/vendor/plugins/codemirror/mode/tornado/index.html
@@ -10,9 +10,9 @@
 <script src="../xml/xml.js"></script>
 <script src="../htmlmixed/htmlmixed.js"></script>
 <script src="tornado.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/tornado/tornado.js b/public/vendor/plugins/codemirror/mode/tornado/tornado.js
index dbfbc34890..aa589a08c3 100644
--- a/public/vendor/plugins/codemirror/mode/tornado/tornado.js
+++ b/public/vendor/plugins/codemirror/mode/tornado/tornado.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/troff/index.html b/public/vendor/plugins/codemirror/mode/troff/index.html
index 7c5a54e54f..1810764214 100644
--- a/public/vendor/plugins/codemirror/mode/troff/index.html
+++ b/public/vendor/plugins/codemirror/mode/troff/index.html
@@ -12,7 +12,7 @@
   .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
 </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/troff/troff.js b/public/vendor/plugins/codemirror/mode/troff/troff.js
index 86154b6e14..0c2220d2cc 100644
--- a/public/vendor/plugins/codemirror/mode/troff/troff.js
+++ b/public/vendor/plugins/codemirror/mode/troff/troff.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object")
diff --git a/public/vendor/plugins/codemirror/mode/ttcn-cfg/index.html b/public/vendor/plugins/codemirror/mode/ttcn-cfg/index.html
index 4a4cd4571c..0cc8048b25 100644
--- a/public/vendor/plugins/codemirror/mode/ttcn-cfg/index.html
+++ b/public/vendor/plugins/codemirror/mode/ttcn-cfg/index.html
@@ -6,15 +6,16 @@
 
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
+<script src="../../addon/edit/matchbrackets.js"></script>
 <script src="ttcn-cfg.js"></script>
-<style type="text/css">
+<style>
     .CodeMirror {
         border-top: 1px solid black;
         border-bottom: 1px solid black;
     }
 </style>
 <div id=nav>
-    <a href="http://codemirror.net"><h1>CodeMirror</h1>
+    <a href="https://codemirror.net"><h1>CodeMirror</h1>
         <img id=logo src="../../doc/logo.png">
     </a>
 
diff --git a/public/vendor/plugins/codemirror/mode/ttcn-cfg/ttcn-cfg.js b/public/vendor/plugins/codemirror/mode/ttcn-cfg/ttcn-cfg.js
index e10805119c..9d4b8405af 100644
--- a/public/vendor/plugins/codemirror/mode/ttcn-cfg/ttcn-cfg.js
+++ b/public/vendor/plugins/codemirror/mode/ttcn-cfg/ttcn-cfg.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/ttcn/index.html b/public/vendor/plugins/codemirror/mode/ttcn/index.html
index f1ef811310..d63043413d 100644
--- a/public/vendor/plugins/codemirror/mode/ttcn/index.html
+++ b/public/vendor/plugins/codemirror/mode/ttcn/index.html
@@ -6,15 +6,16 @@
 
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
+<script src="../../addon/edit/matchbrackets.js"></script>
 <script src="ttcn.js"></script>
-<style type="text/css">
+<style>
     .CodeMirror {
         border-top: 1px solid black;
         border-bottom: 1px solid black;
     }
 </style>
 <div id=nav>
-    <a href="http://codemirror.net"><h1>CodeMirror</h1>
+    <a href="https://codemirror.net"><h1>CodeMirror</h1>
         <img id=logo src="../../doc/logo.png">
     </a>
 
diff --git a/public/vendor/plugins/codemirror/mode/ttcn/ttcn.js b/public/vendor/plugins/codemirror/mode/ttcn/ttcn.js
index 3051851779..0304e7c53d 100644
--- a/public/vendor/plugins/codemirror/mode/ttcn/ttcn.js
+++ b/public/vendor/plugins/codemirror/mode/ttcn/ttcn.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/turtle/index.html b/public/vendor/plugins/codemirror/mode/turtle/index.html
index a4962b6174..79fe6dfdfe 100644
--- a/public/vendor/plugins/codemirror/mode/turtle/index.html
+++ b/public/vendor/plugins/codemirror/mode/turtle/index.html
@@ -6,10 +6,11 @@
 
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
+<script src="../../addon/edit/matchbrackets.js"></script>
 <script src="turtle.js"></script>
 <style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/turtle/turtle.js b/public/vendor/plugins/codemirror/mode/turtle/turtle.js
index 0988f0a442..695239661f 100644
--- a/public/vendor/plugins/codemirror/mode/turtle/turtle.js
+++ b/public/vendor/plugins/codemirror/mode/turtle/turtle.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/twig/index.html b/public/vendor/plugins/codemirror/mode/twig/index.html
index 02493a5cb8..12c8d6b0e4 100644
--- a/public/vendor/plugins/codemirror/mode/twig/index.html
+++ b/public/vendor/plugins/codemirror/mode/twig/index.html
@@ -7,9 +7,9 @@
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
 <script src="twig.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/twig/twig.js b/public/vendor/plugins/codemirror/mode/twig/twig.js
index 1f2854bef2..a6dd3f1a68 100644
--- a/public/vendor/plugins/codemirror/mode/twig/twig.js
+++ b/public/vendor/plugins/codemirror/mode/twig/twig.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -95,7 +95,7 @@
         }
         return "variable";
       } else if (stream.eat("{")) {
-        if (ch = stream.eat("#")) {
+        if (stream.eat("#")) {
           state.incomment = true;
           if (!stream.skipTo("#}")) {
             stream.skipToEnd();
diff --git a/public/vendor/plugins/codemirror/mode/vb/index.html b/public/vendor/plugins/codemirror/mode/vb/index.html
index adcc44fd39..83c7182aad 100644
--- a/public/vendor/plugins/codemirror/mode/vb/index.html
+++ b/public/vendor/plugins/codemirror/mode/vb/index.html
@@ -5,17 +5,17 @@
 <link rel=stylesheet href="../../doc/docs.css">
 
 <link rel="stylesheet" href="../../lib/codemirror.css">
-<link href="http://fonts.googleapis.com/css?family=Inconsolata" rel="stylesheet" type="text/css">
+<link href="http://fonts.googleapis.com/css?family=Inconsolata" rel="stylesheet">
 <script src="../../lib/codemirror.js"></script>
 <script src="vb.js"></script>
-<script type="text/javascript" src="../../addon/runmode/runmode.js"></script>
+<script src="../../addon/runmode/runmode.js"></script>
 <style>
       .CodeMirror {border: 1px solid #aaa; height:210px; height: auto;}
       .CodeMirror-scroll { overflow-x: auto; overflow-y: hidden;}
       .CodeMirror pre { font-family: Inconsolata; font-size: 14px}
     </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
@@ -30,73 +30,20 @@
 
 <article>
 <h2>VB.NET mode</h2>
-
-<script type="text/javascript">
-function test(golden, text) {
-  var ok = true;
-  var i = 0;
-  function callback(token, style, lineNo, pos){
-		//console.log(String(token) + " " + String(style) + " " + String(lineNo) + " " + String(pos));
-    var result = [String(token), String(style)];
-    if (golden[i][0] != result[0] || golden[i][1] != result[1]){
-      return "Error, expected: " + String(golden[i]) + ", got: " + String(result);
-      ok = false;
-    }
-    i++;
-  }
-  CodeMirror.runMode(text, "text/x-vb",callback); 
-
-  if (ok) return "Tests OK";
-}
-function testTypes() {
-  var golden = [['Integer','keyword'],[' ','null'],['Float','keyword']]
-  var text =  "Integer Float";
-  return test(golden,text);
-}
-function testIf(){
-  var golden = [['If','keyword'],[' ','null'],['True','keyword'],[' ','null'],['End','keyword'],[' ','null'],['If','keyword']];
-  var text = 'If True End If';
-  return test(golden, text);
-}
-function testDecl(){
-   var golden = [['Dim','keyword'],[' ','null'],['x','variable'],[' ','null'],['as','keyword'],[' ','null'],['Integer','keyword']];
-   var text = 'Dim x as Integer';
-   return test(golden, text);
-}
-function testAll(){
-  var result = "";
-
-  result += testTypes() + "\n";
-  result += testIf() + "\n";
-  result += testDecl() + "\n";
-  return result;
-
-}
-function initText(editor) {
-  var content = 'Class rocket\nPrivate quality as Double\nPublic Sub launch() as String\nif quality > 0.8\nlaunch = "Successful"\nElse\nlaunch = "Failed"\nEnd If\nEnd sub\nEnd class\n';
-  editor.setValue(content);
-  for (var i =0; i< editor.lineCount(); i++) editor.indentLine(i);
-}
-function init() {
-    editor = CodeMirror.fromTextArea(document.getElementById("solution"), {
-        lineNumbers: true,
-        mode: "text/x-vb",
-        readOnly: false
-    });
-    runTest();
-}
-function runTest() {
-	document.getElementById('testresult').innerHTML = testAll();
-  initText(editor);
-	
-}
-document.body.onload = init;
-</script>
-
   <div id="edit">
-  <textarea style="width:95%;height:200px;padding:5px;" name="solution" id="solution" ></textarea>
+  <textarea name="code" id="code" >
+Class rocket
+  Private quality as Double
+  Public Sub launch() as String
+    If quality > 0.8
+      launch = "Successful"
+    Else
+      launch = "Failed"
+    End If
+  End sub
+End class
+</textarea>
   </div>
-  <pre id="testresult"></pre>
   <p>MIME type defined: <code>text/x-vb</code>.</p>
-
+<script>CodeMirror.fromTextArea(document.getElementById("code"))</script>
 </article>
diff --git a/public/vendor/plugins/codemirror/mode/vb/vb.js b/public/vendor/plugins/codemirror/mode/vb/vb.js
index d78f91f701..6e4b476309 100644
--- a/public/vendor/plugins/codemirror/mode/vb/vb.js
+++ b/public/vendor/plugins/codemirror/mode/vb/vb.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -25,16 +25,16 @@ CodeMirror.defineMode("vb", function(conf, parserConf) {
     var tripleDelimiters = new RegExp("^((//=)|(>>=)|(<<=)|(\\*\\*=))");
     var identifiers = new RegExp("^[_A-Za-z][_A-Za-z0-9]*");
 
-    var openingKeywords = ['class','module', 'sub','enum','select','while','if','function',  'get','set','property', 'try'];
-    var middleKeywords = ['else','elseif','case', 'catch'];
+    var openingKeywords = ['class','module', 'sub','enum','select','while','if','function', 'get','set','property', 'try', 'structure', 'synclock', 'using', 'with'];
+    var middleKeywords = ['else','elseif','case', 'catch', 'finally'];
     var endKeywords = ['next','loop'];
 
-    var operatorKeywords = ['and', 'or', 'not', 'xor', 'in'];
+    var operatorKeywords = ['and', "andalso", 'or', 'orelse', 'xor', 'in', 'not', 'is', 'isnot', 'like'];
     var wordOperators = wordRegexp(operatorKeywords);
-    var commonKeywords = ['as', 'dim', 'break',  'continue','optional', 'then',  'until',
-                          'goto', 'byval','byref','new','handles','property', 'return',
-                          'const','private', 'protected', 'friend', 'public', 'shared', 'static', 'true','false'];
-    var commontypes = ['integer','string','double','decimal','boolean','short','char', 'float','single'];
+
+    var commonKeywords = ["#const", "#else", "#elseif", "#end", "#if", "#region", "addhandler", "addressof", "alias", "as", "byref", "byval", "cbool", "cbyte", "cchar", "cdate", "cdbl", "cdec", "cint", "clng", "cobj", "compare", "const", "continue", "csbyte", "cshort", "csng", "cstr", "cuint", "culng", "cushort", "declare", "default", "delegate", "dim", "directcast", "each", "erase", "error", "event", "exit", "explicit", "false", "for", "friend", "gettype", "goto", "handles", "implements", "imports", "infer", "inherits", "interface", "isfalse", "istrue", "lib", "me", "mod", "mustinherit", "mustoverride", "my", "mybase", "myclass", "namespace", "narrowing", "new", "nothing", "notinheritable", "notoverridable", "of", "off", "on", "operator", "option", "optional", "out", "overloads", "overridable", "overrides", "paramarray", "partial", "private", "protected", "public", "raiseevent", "readonly", "redim", "removehandler", "resume", "return", "shadows", "shared", "static", "step", "stop", "strict", "then", "throw", "to", "true", "trycast", "typeof", "until", "until", "when", "widening", "withevents", "writeonly"];
+
+    var commontypes = ['object', 'boolean', 'char', 'string', 'byte', 'sbyte', 'short', 'ushort', 'int16', 'uint16', 'integer', 'uinteger', 'int32', 'uint32', 'long', 'ulong', 'int64', 'uint64', 'decimal', 'single', 'double', 'float', 'date', 'datetime', 'intptr', 'uintptr'];
 
     var keywords = wordRegexp(commonKeywords);
     var types = wordRegexp(commontypes);
@@ -202,7 +202,6 @@ CodeMirror.defineMode("vb", function(conf, parserConf) {
         // Handle '.' connected identifiers
         if (current === '.') {
             style = state.tokenize(stream, state);
-            current = stream.current();
             if (style === 'variable') {
                 return 'variable';
             } else {
diff --git a/public/vendor/plugins/codemirror/mode/vbscript/index.html b/public/vendor/plugins/codemirror/mode/vbscript/index.html
index ad7532d7df..95a7da52fa 100644
--- a/public/vendor/plugins/codemirror/mode/vbscript/index.html
+++ b/public/vendor/plugins/codemirror/mode/vbscript/index.html
@@ -7,9 +7,9 @@
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
 <script src="vbscript.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/vbscript/vbscript.js b/public/vendor/plugins/codemirror/mode/vbscript/vbscript.js
index b66df2239a..0670c0ceef 100644
--- a/public/vendor/plugins/codemirror/mode/vbscript/vbscript.js
+++ b/public/vendor/plugins/codemirror/mode/vbscript/vbscript.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 /*
 For extra ASP classic objects, initialize CodeMirror instance with this option:
diff --git a/public/vendor/plugins/codemirror/mode/velocity/index.html b/public/vendor/plugins/codemirror/mode/velocity/index.html
index 7eba8f4185..b38c570df7 100644
--- a/public/vendor/plugins/codemirror/mode/velocity/index.html
+++ b/public/vendor/plugins/codemirror/mode/velocity/index.html
@@ -10,7 +10,7 @@
 <script src="velocity.js"></script>
 <style>.CodeMirror {border: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/velocity/velocity.js b/public/vendor/plugins/codemirror/mode/velocity/velocity.js
index 12ee221249..56caa671b3 100644
--- a/public/vendor/plugins/codemirror/mode/velocity/velocity.js
+++ b/public/vendor/plugins/codemirror/mode/velocity/velocity.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -82,7 +82,7 @@ CodeMirror.defineMode("velocity", function() {
         }
         // variable?
         else if (ch == "$") {
-            stream.eatWhile(/[\w\d\$_\.{}]/);
+            stream.eatWhile(/[\w\d\$_\.{}-]/);
             // is it one of the specials?
             if (specials && specials.propertyIsEnumerable(stream.current())) {
                 return "keyword";
diff --git a/public/vendor/plugins/codemirror/mode/verilog/index.html b/public/vendor/plugins/codemirror/mode/verilog/index.html
index 9c52722afb..e38cf276b5 100644
--- a/public/vendor/plugins/codemirror/mode/verilog/index.html
+++ b/public/vendor/plugins/codemirror/mode/verilog/index.html
@@ -8,9 +8,9 @@
 <script src="../../lib/codemirror.js"></script>
 <script src="../../addon/edit/matchbrackets.js"></script>
 <script src="verilog.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/verilog/test.js b/public/vendor/plugins/codemirror/mode/verilog/test.js
index 8334fab05b..bafe726db3 100644
--- a/public/vendor/plugins/codemirror/mode/verilog/test.js
+++ b/public/vendor/plugins/codemirror/mode/verilog/test.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function() {
   var mode = CodeMirror.getMode({indentUnit: 4}, "verilog");
diff --git a/public/vendor/plugins/codemirror/mode/verilog/verilog.js b/public/vendor/plugins/codemirror/mode/verilog/verilog.js
index 7513dcede2..2b6850659b 100644
--- a/public/vendor/plugins/codemirror/mode/verilog/verilog.js
+++ b/public/vendor/plugins/codemirror/mode/verilog/verilog.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -81,7 +81,7 @@ CodeMirror.defineMode("verilog", function(config, parserConfig) {
   // Block openings which are closed by a matching keyword in the form of ("end" + keyword)
   // E.g. "task" => "endtask"
   var blockKeywords = words(
-    "case checker class clocking config function generate interface module package" +
+    "case checker class clocking config function generate interface module package " +
     "primitive program property specify sequence table task"
   );
 
@@ -302,7 +302,13 @@ CodeMirror.defineMode("verilog", function(config, parserConfig) {
         state.indented = stream.indentation();
         state.startOfLine = true;
       }
-      if (hooks.token) hooks.token(stream, state);
+      if (hooks.token) {
+        // Call hook, with an optional return value of a style to override verilog styling.
+        var style = hooks.token(stream, state);
+        if (style !== undefined) {
+          return style;
+        }
+      }
       if (stream.eatSpace()) return null;
       curPunc = null;
       curKeyword = null;
@@ -375,163 +381,295 @@ CodeMirror.defineMode("verilog", function(config, parserConfig) {
     name: "verilog"
   });
 
-  // TLVVerilog mode
 
-  var tlvchScopePrefixes = {
-    ">": "property", "->": "property", "-": "hr", "|": "link", "?$": "qualifier", "?*": "qualifier",
-    "@-": "variable-3", "@": "variable-3", "?": "qualifier"
+
+  // TL-Verilog mode.
+  // See tl-x.org for language spec.
+  // See the mode in action at makerchip.com.
+  // Contact: steve.hoover@redwoodeda.com
+
+  // TLV Identifier prefixes.
+  // Note that sign is not treated separately, so "+/-" versions of numeric identifiers
+  // are included.
+  var tlvIdentifierStyle = {
+    "|": "link",
+    ">": "property",  // Should condition this off for > TLV 1c.
+    "$": "variable",
+    "$$": "variable",
+    "?$": "qualifier",
+    "?*": "qualifier",
+    "-": "hr",
+    "/": "property",
+    "/-": "property",
+    "@": "variable-3",
+    "@-": "variable-3",
+    "@++": "variable-3",
+    "@+=": "variable-3",
+    "@+=-": "variable-3",
+    "@--": "variable-3",
+    "@-=": "variable-3",
+    "%+": "tag",
+    "%-": "tag",
+    "%": "tag",
+    ">>": "tag",
+    "<<": "tag",
+    "<>": "tag",
+    "#": "tag",  // Need to choose a style for this.
+    "^": "attribute",
+    "^^": "attribute",
+    "^!": "attribute",
+    "*": "variable-2",
+    "**": "variable-2",
+    "\\": "keyword",
+    "\"": "comment"
   };
 
-  function tlvGenIndent(stream, state) {
-    var tlvindentUnit = 2;
-    var rtnIndent = -1, indentUnitRq = 0, curIndent = stream.indentation();
-    switch (state.tlvCurCtlFlowChar) {
-    case "\\":
-      curIndent = 0;
-      break;
-    case "|":
-      if (state.tlvPrevPrevCtlFlowChar == "@") {
-        indentUnitRq = -2; //-2 new pipe rq after cur pipe
-        break;
-      }
-      if (tlvchScopePrefixes[state.tlvPrevCtlFlowChar])
-        indentUnitRq = 1; // +1 new scope
-      break;
-    case "M":  // m4
-      if (state.tlvPrevPrevCtlFlowChar == "@") {
-        indentUnitRq = -2; //-2 new inst rq after  pipe
-        break;
-      }
-      if (tlvchScopePrefixes[state.tlvPrevCtlFlowChar])
-        indentUnitRq = 1; // +1 new scope
-      break;
-    case "@":
-      if (state.tlvPrevCtlFlowChar == "S")
-        indentUnitRq = -1; // new pipe stage after stmts
-      if (state.tlvPrevCtlFlowChar == "|")
-        indentUnitRq = 1; // 1st pipe stage
-      break;
-    case "S":
-      if (state.tlvPrevCtlFlowChar == "@")
-        indentUnitRq = 1; // flow in pipe stage
-      if (tlvchScopePrefixes[state.tlvPrevCtlFlowChar])
-        indentUnitRq = 1; // +1 new scope
-      break;
-    }
-    var statementIndentUnit = tlvindentUnit;
-    rtnIndent = curIndent + (indentUnitRq*statementIndentUnit);
-    return rtnIndent >= 0 ? rtnIndent : curIndent;
+  // Lines starting with these characters define scope (result in indentation).
+  var tlvScopePrefixChars = {
+    "/": "beh-hier",
+    ">": "beh-hier",
+    "-": "phys-hier",
+    "|": "pipe",
+    "?": "when",
+    "@": "stage",
+    "\\": "keyword"
+  };
+  var tlvIndentUnit = 3;
+  var tlvTrackStatements = false;
+  var tlvIdentMatch = /^([~!@#\$%\^&\*-\+=\?\/\\\|'"<>]+)([\d\w_]*)/;  // Matches an identifiere.
+  // Note that ':' is excluded, because of it's use in [:].
+  var tlvFirstLevelIndentMatch = /^[! ]  /;
+  var tlvLineIndentationMatch = /^[! ] */;
+  var tlvCommentMatch = /^\/[\/\*]/;
+
+
+  // Returns a style specific to the scope at the given indentation column.
+  // Type is one of: "indent", "scope-ident", "before-scope-ident".
+  function tlvScopeStyle(state, indentation, type) {
+    // Begin scope.
+    var depth = indentation / tlvIndentUnit;  // TODO: Pass this in instead.
+    return "tlv-" + state.tlvIndentationStyle[depth] + "-" + type;
+  }
+
+  // Return true if the next thing in the stream is an identifier with a mnemonic.
+  function tlvIdentNext(stream) {
+    var match;
+    return (match = stream.match(tlvIdentMatch, false)) && match[2].length > 0;
   }
 
   CodeMirror.defineMIME("text/x-tlv", {
     name: "verilog",
+
     hooks: {
-      "\\": function(stream, state) {
-        var vxIndent = 0, style = false;
-        var curPunc  = stream.string;
-        if ((stream.sol()) && ((/\\SV/.test(stream.string)) || (/\\TLV/.test(stream.string)))) {
-          curPunc = (/\\TLV_version/.test(stream.string))
-            ? "\\TLV_version" : stream.string;
-          stream.skipToEnd();
-          if (curPunc == "\\SV" && state.vxCodeActive) {state.vxCodeActive = false;};
-          if ((/\\TLV/.test(curPunc) && !state.vxCodeActive)
-            || (curPunc=="\\TLV_version" && state.vxCodeActive)) {state.vxCodeActive = true;};
-          style = "keyword";
-          state.tlvCurCtlFlowChar  = state.tlvPrevPrevCtlFlowChar
-            = state.tlvPrevCtlFlowChar = "";
-          if (state.vxCodeActive == true) {
-            state.tlvCurCtlFlowChar  = "\\";
-            vxIndent = tlvGenIndent(stream, state);
-          }
-          state.vxIndentRq = vxIndent;
-        }
-        return style;
-      },
-      tokenBase: function(stream, state) {
-        var vxIndent = 0, style = false;
-        var tlvisOperatorChar = /[\[\]=:]/;
-        var tlvkpScopePrefixs = {
-          "**":"variable-2", "*":"variable-2", "$$":"variable", "$":"variable",
-          "^^":"attribute", "^":"attribute"};
-        var ch = stream.peek();
-        var vxCurCtlFlowCharValueAtStart = state.tlvCurCtlFlowChar;
-        if (state.vxCodeActive == true) {
-          if (/[\[\]{}\(\);\:]/.test(ch)) {
-            // bypass nesting and 1 char punc
-            style = "meta";
-            stream.next();
-          } else if (ch == "/") {
-            stream.next();
-            if (stream.eat("/")) {
-              stream.skipToEnd();
-              style = "comment";
-              state.tlvCurCtlFlowChar = "S";
-            } else {
-              stream.backUp(1);
-            }
-          } else if (ch == "@") {
-            // pipeline stage
-            style = tlvchScopePrefixes[ch];
-            state.tlvCurCtlFlowChar = "@";
-            stream.next();
-            stream.eatWhile(/[\w\$_]/);
-          } else if (stream.match(/\b[mM]4+/, true)) { // match: function(pattern, consume, caseInsensitive)
-            // m4 pre proc
-            stream.skipTo("(");
-            style = "def";
-            state.tlvCurCtlFlowChar = "M";
-          } else if (ch == "!" && stream.sol()) {
-            // v stmt in tlv region
-            // state.tlvCurCtlFlowChar  = "S";
-            style = "comment";
-            stream.next();
-          } else if (tlvisOperatorChar.test(ch)) {
-            // operators
-            stream.eatWhile(tlvisOperatorChar);
-            style = "operator";
-          } else if (ch == "#") {
-            // phy hier
-            state.tlvCurCtlFlowChar  = (state.tlvCurCtlFlowChar == "")
-              ? ch : state.tlvCurCtlFlowChar;
-            stream.next();
-            stream.eatWhile(/[+-]\d/);
-            style = "tag";
-          } else if (tlvkpScopePrefixs.propertyIsEnumerable(ch)) {
-            // special TLV operators
-            style = tlvkpScopePrefixs[ch];
-            state.tlvCurCtlFlowChar = state.tlvCurCtlFlowChar == "" ? "S" : state.tlvCurCtlFlowChar;  // stmt
-            stream.next();
-            stream.match(/[a-zA-Z_0-9]+/);
-          } else if (style = tlvchScopePrefixes[ch] || false) {
-            // special TLV operators
-            state.tlvCurCtlFlowChar = state.tlvCurCtlFlowChar == "" ? ch : state.tlvCurCtlFlowChar;
-            stream.next();
-            stream.match(/[a-zA-Z_0-9]+/);
-          }
-          if (state.tlvCurCtlFlowChar != vxCurCtlFlowCharValueAtStart) { // flow change
-            vxIndent = tlvGenIndent(stream, state);
-            state.vxIndentRq = vxIndent;
-          }
-        }
-        return style;
-      },
+
+      electricInput: false,
+
+
+      // Return undefined for verilog tokenizing, or style for TLV token (null not used).
+      // Standard CM styles are used for most formatting, but some TL-Verilog-specific highlighting
+      // can be enabled with the definition of cm-tlv-* styles, including highlighting for:
+      //   - M4 tokens
+      //   - TLV scope indentation
+      //   - Statement delimitation (enabled by tlvTrackStatements)
       token: function(stream, state) {
-        if (state.vxCodeActive == true && stream.sol() && state.tlvCurCtlFlowChar != "") {
-          state.tlvPrevPrevCtlFlowChar = state.tlvPrevCtlFlowChar;
-          state.tlvPrevCtlFlowChar = state.tlvCurCtlFlowChar;
-          state.tlvCurCtlFlowChar = "";
+        var style = undefined;
+        var match;  // Return value of pattern matches.
+
+        // Set highlighting mode based on code region (TLV or SV).
+        if (stream.sol() && ! state.tlvInBlockComment) {
+          // Process region.
+          if (stream.peek() == '\\') {
+            style = "def";
+            stream.skipToEnd();
+            if (stream.string.match(/\\SV/)) {
+              state.tlvCodeActive = false;
+            } else if (stream.string.match(/\\TLV/)){
+              state.tlvCodeActive = true;
+            }
+          }
+          // Correct indentation in the face of a line prefix char.
+          if (state.tlvCodeActive && stream.pos == 0 &&
+              (state.indented == 0) && (match = stream.match(tlvLineIndentationMatch, false))) {
+            state.indented = match[0].length;
+          }
+
+          // Compute indentation state:
+          //   o Auto indentation on next line
+          //   o Indentation scope styles
+          var indented = state.indented;
+          var depth = indented / tlvIndentUnit;
+          if (depth <= state.tlvIndentationStyle.length) {
+            // not deeper than current scope
+
+            var blankline = stream.string.length == indented;
+            var chPos = depth * tlvIndentUnit;
+            if (chPos < stream.string.length) {
+              var bodyString = stream.string.slice(chPos);
+              var ch = bodyString[0];
+              if (tlvScopePrefixChars[ch] && ((match = bodyString.match(tlvIdentMatch)) &&
+                  tlvIdentifierStyle[match[1]])) {
+                // This line begins scope.
+                // Next line gets indented one level.
+                indented += tlvIndentUnit;
+                // Style the next level of indentation (except non-region keyword identifiers,
+                //   which are statements themselves)
+                if (!(ch == "\\" && chPos > 0)) {
+                  state.tlvIndentationStyle[depth] = tlvScopePrefixChars[ch];
+                  if (tlvTrackStatements) {state.statementComment = false;}
+                  depth++;
+                }
+              }
+            }
+            // Clear out deeper indentation levels unless line is blank.
+            if (!blankline) {
+              while (state.tlvIndentationStyle.length > depth) {
+                state.tlvIndentationStyle.pop();
+              }
+            }
+          }
+          // Set next level of indentation.
+          state.tlvNextIndent = indented;
         }
+
+        if (state.tlvCodeActive) {
+          // Highlight as TLV.
+
+          var beginStatement = false;
+          if (tlvTrackStatements) {
+            // This starts a statement if the position is at the scope level
+            // and we're not within a statement leading comment.
+            beginStatement =
+                   (stream.peek() != " ") &&   // not a space
+                   (style === undefined) &&    // not a region identifier
+                   !state.tlvInBlockComment && // not in block comment
+                   //!stream.match(tlvCommentMatch, false) && // not comment start
+                   (stream.column() == state.tlvIndentationStyle.length * tlvIndentUnit);  // at scope level
+            if (beginStatement) {
+              if (state.statementComment) {
+                // statement already started by comment
+                beginStatement = false;
+              }
+              state.statementComment =
+                   stream.match(tlvCommentMatch, false); // comment start
+            }
+          }
+
+          var match;
+          if (style !== undefined) {
+            // Region line.
+            style += " " + tlvScopeStyle(state, 0, "scope-ident")
+          } else if (((stream.pos / tlvIndentUnit) < state.tlvIndentationStyle.length) &&
+                     (match = stream.match(stream.sol() ? tlvFirstLevelIndentMatch : /^   /))) {
+            // Indentation
+            style = // make this style distinct from the previous one to prevent
+                    // codemirror from combining spans
+                    "tlv-indent-" + (((stream.pos % 2) == 0) ? "even" : "odd") +
+                    // and style it
+                    " " + tlvScopeStyle(state, stream.pos - tlvIndentUnit, "indent");
+            // Style the line prefix character.
+            if (match[0].charAt(0) == "!") {
+              style += " tlv-alert-line-prefix";
+            }
+            // Place a class before a scope identifier.
+            if (tlvIdentNext(stream)) {
+              style += " " + tlvScopeStyle(state, stream.pos, "before-scope-ident");
+            }
+          } else if (state.tlvInBlockComment) {
+            // In a block comment.
+            if (stream.match(/^.*?\*\//)) {
+              // Exit block comment.
+              state.tlvInBlockComment = false;
+              if (tlvTrackStatements && !stream.eol()) {
+                // Anything after comment is assumed to be real statement content.
+                state.statementComment = false;
+              }
+            } else {
+              stream.skipToEnd();
+            }
+            style = "comment";
+          } else if ((match = stream.match(tlvCommentMatch)) && !state.tlvInBlockComment) {
+            // Start comment.
+            if (match[0] == "//") {
+              // Line comment.
+              stream.skipToEnd();
+            } else {
+              // Block comment.
+              state.tlvInBlockComment = true;
+            }
+            style = "comment";
+          } else if (match = stream.match(tlvIdentMatch)) {
+            // looks like an identifier (or identifier prefix)
+            var prefix = match[1];
+            var mnemonic = match[2];
+            if (// is identifier prefix
+                tlvIdentifierStyle.hasOwnProperty(prefix) &&
+                // has mnemonic or we're at the end of the line (maybe it hasn't been typed yet)
+                (mnemonic.length > 0 || stream.eol())) {
+              style = tlvIdentifierStyle[prefix];
+              if (stream.column() == state.indented) {
+                // Begin scope.
+                style += " " + tlvScopeStyle(state, stream.column(), "scope-ident")
+              }
+            } else {
+              // Just swallow one character and try again.
+              // This enables subsequent identifier match with preceding symbol character, which
+              //   is legal within a statement.  (Eg, !$reset).  It also enables detection of
+              //   comment start with preceding symbols.
+              stream.backUp(stream.current().length - 1);
+              style = "tlv-default";
+            }
+          } else if (stream.match(/^\t+/)) {
+            // Highlight tabs, which are illegal.
+            style = "tlv-tab";
+          } else if (stream.match(/^[\[\]{}\(\);\:]+/)) {
+            // [:], (), {}, ;.
+            style = "meta";
+          } else if (match = stream.match(/^[mM]4([\+_])?[\w\d_]*/)) {
+            // m4 pre proc
+            style = (match[1] == "+") ? "tlv-m4-plus" : "tlv-m4";
+          } else if (stream.match(/^ +/)){
+            // Skip over spaces.
+            if (stream.eol()) {
+              // Trailing spaces.
+              style = "error";
+            } else {
+              // Non-trailing spaces.
+              style = "tlv-default";
+            }
+          } else if (stream.match(/^[\w\d_]+/)) {
+            // alpha-numeric token.
+            style = "number";
+          } else {
+            // Eat the next char w/ no formatting.
+            stream.next();
+            style = "tlv-default";
+          }
+          if (beginStatement) {
+            style += " tlv-statement";
+          }
+        } else {
+          if (stream.match(/^[mM]4([\w\d_]*)/)) {
+            // m4 pre proc
+            style = "tlv-m4";
+          }
+        }
+        return style;
       },
+
       indent: function(state) {
-        return (state.vxCodeActive == true) ? state.vxIndentRq : -1;
+        return (state.tlvCodeActive == true) ? state.tlvNextIndent : -1;
       },
+
       startState: function(state) {
-        state.tlvCurCtlFlowChar = "";
-        state.tlvPrevCtlFlowChar = "";
-        state.tlvPrevPrevCtlFlowChar = "";
-        state.vxCodeActive = true;
-        state.vxIndentRq = 0;
+        state.tlvIndentationStyle = [];  // Styles to use for each level of indentation.
+        state.tlvCodeActive = true;  // True when we're in a TLV region (and at beginning of file).
+        state.tlvNextIndent = -1;    // The number of spaces to autoindent the next line if tlvCodeActive.
+        state.tlvInBlockComment = false;  // True inside /**/ comment.
+        if (tlvTrackStatements) {
+          state.statementComment = false;  // True inside a statement's header comment.
+        }
       }
+
     }
   });
 });
diff --git a/public/vendor/plugins/codemirror/mode/vhdl/index.html b/public/vendor/plugins/codemirror/mode/vhdl/index.html
index 3051bc37e5..ed34c25968 100644
--- a/public/vendor/plugins/codemirror/mode/vhdl/index.html
+++ b/public/vendor/plugins/codemirror/mode/vhdl/index.html
@@ -8,9 +8,9 @@
 <script src="../../lib/codemirror.js"></script>
 <script src="../../addon/edit/matchbrackets.js"></script>
 <script src="vhdl.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/vhdl/vhdl.js b/public/vendor/plugins/codemirror/mode/vhdl/vhdl.js
index 97e086e42b..133e67a268 100644
--- a/public/vendor/plugins/codemirror/mode/vhdl/vhdl.js
+++ b/public/vendor/plugins/codemirror/mode/vhdl/vhdl.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 // Originally written by Alf Nielsen, re-written by Michael Zhou
 (function(mod) {
diff --git a/public/vendor/plugins/codemirror/mode/vue/index.html b/public/vendor/plugins/codemirror/mode/vue/index.html
index cccb9764f3..3888e3deb9 100644
--- a/public/vendor/plugins/codemirror/mode/vue/index.html
+++ b/public/vendor/plugins/codemirror/mode/vue/index.html
@@ -14,14 +14,14 @@
 <script src="../css/css.js"></script>
 <script src="../coffeescript/coffeescript.js"></script>
 <script src="../sass/sass.js"></script>
-<script src="../jade/jade.js"></script>
+<script src="../pug/pug.js"></script>
 
 <script src="../handlebars/handlebars.js"></script>
 <script src="../htmlmixed/htmlmixed.js"></script>
 <script src="vue.js"></script>
 <style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/vue/vue.js b/public/vendor/plugins/codemirror/mode/vue/vue.js
index d89a552387..b6e6cc5137 100644
--- a/public/vendor/plugins/codemirror/mode/vue/vue.js
+++ b/public/vendor/plugins/codemirror/mode/vue/vue.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function (mod) {
   "use strict";
@@ -12,7 +12,7 @@
         require("../css/css"),
         require("../sass/sass"),
         require("../stylus/stylus"),
-        require("../jade/jade"),
+        require("../pug/pug"),
         require("../handlebars/handlebars"));
   } else if (typeof define === "function" && define.amd) { // AMD
     define(["../../lib/codemirror",
@@ -23,7 +23,7 @@
             "../css/css",
             "../sass/sass",
             "../stylus/stylus",
-            "../jade/jade",
+            "../pug/pug",
             "../handlebars/handlebars"], mod);
   } else { // Plain browser env
     mod(CodeMirror);
@@ -32,19 +32,26 @@
   var tagLanguages = {
     script: [
       ["lang", /coffee(script)?/, "coffeescript"],
-      ["type", /^(?:text|application)\/(?:x-)?coffee(?:script)?$/, "coffeescript"]
+      ["type", /^(?:text|application)\/(?:x-)?coffee(?:script)?$/, "coffeescript"],
+      ["lang", /^babel$/, "javascript"],
+      ["type", /^text\/babel$/, "javascript"],
+      ["type", /^text\/ecmascript-\d+$/, "javascript"]
     ],
     style: [
       ["lang", /^stylus$/i, "stylus"],
       ["lang", /^sass$/i, "sass"],
+      ["lang", /^less$/i, "text/x-less"],
+      ["lang", /^scss$/i, "text/x-scss"],
       ["type", /^(text\/)?(x-)?styl(us)?$/i, "stylus"],
-      ["type", /^text\/sass/i, "sass"]
+      ["type", /^text\/sass/i, "sass"],
+      ["type", /^(text\/)?(x-)?scss$/i, "text/x-scss"],
+      ["type", /^(text\/)?(x-)?less$/i, "text/x-less"]
     ],
     template: [
       ["lang", /^vue-template$/i, "vue"],
-      ["lang", /^jade$/i, "jade"],
+      ["lang", /^pug$/i, "pug"],
       ["lang", /^handlebars$/i, "handlebars"],
-      ["type", /^(text\/)?(x-)?jade$/i, "jade"],
+      ["type", /^(text\/)?(x-)?pug$/i, "pug"],
       ["type", /^text\/x-handlebars-template$/i, "handlebars"],
       [null, null, "vue-template"]
     ]
@@ -63,7 +70,8 @@
 
   CodeMirror.defineMode("vue", function (config) {
     return CodeMirror.getMode(config, {name: "htmlmixed", tags: tagLanguages});
-  }, "htmlmixed", "xml", "javascript", "coffeescript", "css", "sass", "stylus", "jade", "handlebars");
+  }, "htmlmixed", "xml", "javascript", "coffeescript", "css", "sass", "stylus", "pug", "handlebars");
 
   CodeMirror.defineMIME("script/x-vue", "vue");
+  CodeMirror.defineMIME("text/x-vue", "vue");
 });
diff --git a/public/vendor/plugins/codemirror/mode/webidl/index.html b/public/vendor/plugins/codemirror/mode/webidl/index.html
index 1d4112e1c3..e9c325ed59 100644
--- a/public/vendor/plugins/codemirror/mode/webidl/index.html
+++ b/public/vendor/plugins/codemirror/mode/webidl/index.html
@@ -7,10 +7,10 @@
 <script src="../../lib/codemirror.js"></script>
 <script src="../../addon/edit/matchbrackets.js"></script>
 <script src="webidl.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 
 <div id="nav">
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id="logo" src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id="logo" src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/webidl/webidl.js b/public/vendor/plugins/codemirror/mode/webidl/webidl.js
index 814333620a..120de6bea7 100644
--- a/public/vendor/plugins/codemirror/mode/webidl/webidl.js
+++ b/public/vendor/plugins/codemirror/mode/webidl/webidl.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/xml/index.html b/public/vendor/plugins/codemirror/mode/xml/index.html
index c56b8b6eb7..42d7d86cdd 100644
--- a/public/vendor/plugins/codemirror/mode/xml/index.html
+++ b/public/vendor/plugins/codemirror/mode/xml/index.html
@@ -7,9 +7,9 @@
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
 <script src="xml.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/xml/test.js b/public/vendor/plugins/codemirror/mode/xml/test.js
index f48156b517..b586d2b459 100644
--- a/public/vendor/plugins/codemirror/mode/xml/test.js
+++ b/public/vendor/plugins/codemirror/mode/xml/test.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function() {
   var mode = CodeMirror.getMode({indentUnit: 2}, "xml"), mname = "xml";
diff --git a/public/vendor/plugins/codemirror/mode/xml/xml.js b/public/vendor/plugins/codemirror/mode/xml/xml.js
index f987a3a3ce..73c6e0e0dd 100644
--- a/public/vendor/plugins/codemirror/mode/xml/xml.js
+++ b/public/vendor/plugins/codemirror/mode/xml/xml.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -52,6 +52,7 @@ var xmlConfig = {
   doNotIndent: {},
   allowUnquoted: false,
   allowMissing: false,
+  allowMissingTagName: false,
   caseFold: false
 }
 
@@ -162,8 +163,9 @@ CodeMirror.defineMode("xml", function(editorConf, config_) {
         stream.next();
       }
       return style;
-    };
+    }
   }
+
   function doctype(depth) {
     return function(stream, state) {
       var ch;
@@ -226,6 +228,9 @@ CodeMirror.defineMode("xml", function(editorConf, config_) {
       state.tagName = stream.current();
       setStyle = "tag";
       return attrState;
+    } else if (config.allowMissingTagName && type == "endTag") {
+      setStyle = "tag bracket";
+      return attrState(type, stream, state);
     } else {
       setStyle = "error";
       return tagNameState;
@@ -244,6 +249,9 @@ CodeMirror.defineMode("xml", function(editorConf, config_) {
         setStyle = "tag error";
         return closeStateErr;
       }
+    } else if (config.allowMissingTagName && type == "endTag") {
+      setStyle = "tag bracket";
+      return closeState(type, stream, state);
     } else {
       setStyle = "error";
       return closeStateErr;
@@ -382,6 +390,17 @@ CodeMirror.defineMode("xml", function(editorConf, config_) {
     skipAttribute: function(state) {
       if (state.state == attrValueState)
         state.state = attrState
+    },
+
+    xmlCurrentTag: function(state) {
+      return state.tagName ? {name: state.tagName, close: state.type == "closeTag"} : null
+    },
+
+    xmlCurrentContext: function(state) {
+      var context = []
+      for (var cx = state.context; cx; cx = cx.prev)
+        if (cx.tagName) context.push(cx.tagName)
+      return context.reverse()
     }
   };
 });
diff --git a/public/vendor/plugins/codemirror/mode/xquery/index.html b/public/vendor/plugins/codemirror/mode/xquery/index.html
index 7ac5aaeff4..49960912ce 100644
--- a/public/vendor/plugins/codemirror/mode/xquery/index.html
+++ b/public/vendor/plugins/codemirror/mode/xquery/index.html
@@ -7,15 +7,16 @@
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <link rel="stylesheet" href="../../theme/xq-dark.css">
 <script src="../../lib/codemirror.js"></script>
+<script src="../../addon/edit/matchbrackets.js"></script>
 <script src="xquery.js"></script>
-<style type="text/css">
+<style>
 	.CodeMirror {
 	  border-top: 1px solid black; border-bottom: 1px solid black;
 	  height:400px;
 	}
     </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/xquery/test.js b/public/vendor/plugins/codemirror/mode/xquery/test.js
index 1f148cdbbd..f7ad3ac529 100644
--- a/public/vendor/plugins/codemirror/mode/xquery/test.js
+++ b/public/vendor/plugins/codemirror/mode/xquery/test.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 // Don't take these too seriously -- the expected results appear to be
 // based on the results of actual runs without any serious manual
@@ -20,14 +20,14 @@
      "          [def&variable fn:doc]()[[ [variable foo][keyword /][variable @bar] [keyword eq] [variable $let] ]],",
      "          [keyword //][variable x] }                 [comment (: a more 'evil' test :)]",
      "      [comment (: Modified Blakeley example (: with nested comment :) ... :)]",
-     "      [keyword declare] [keyword private] [keyword function] [def&variable local:declare]() {()}[variable ;]",
-     "      [keyword declare] [keyword private] [keyword function] [def&variable local:private]() {()}[variable ;]",
-     "      [keyword declare] [keyword private] [keyword function] [def&variable local:function]() {()}[variable ;]",
-     "      [keyword declare] [keyword private] [keyword function] [def&variable local:local]() {()}[variable ;]",
+     "      [keyword declare] [variable private] [keyword function] [def&variable local:declare]() {()}[variable ;]",
+     "      [keyword declare] [variable private] [keyword function] [def&variable local:private]() {()}[variable ;]",
+     "      [keyword declare] [variable private] [keyword function] [def&variable local:function]() {()}[variable ;]",
+     "      [keyword declare] [variable private] [keyword function] [def&variable local:local]() {()}[variable ;]",
      "      [keyword let] [variable $let] [keyword :=] [variable &lt;let&gt;let] [variable $let] [keyword :=] [variable &quot;let&quot;&lt;][keyword /let][variable &gt;]",
      "      [keyword return] [keyword element] [variable element] {",
      "          [keyword attribute] [variable attribute] { [keyword try] { [def&variable xdmp:version]() } [keyword catch]([variable $e]) { [def&variable xdmp:log]([variable $e]) } },",
-     "          [keyword attribute] [variable fn:doc] { [variable &quot;bar&quot;] [variable castable] [keyword as] [atom xs:string] },",
+     "          [keyword attribute] [variable fn:doc] { [variable &quot;bar&quot;] [keyword castable] [keyword as] [atom xs:string] },",
      "          [keyword element] [variable text] { [keyword text] { [variable &quot;text&quot;] } },",
      "          [def&variable fn:doc]()[[ [qualifier child::][variable eq][keyword /]([variable @bar] [keyword |] [qualifier attribute::][variable attribute]) [keyword eq] [variable $let] ]],",
      "          [keyword //][variable fn:doc]",
diff --git a/public/vendor/plugins/codemirror/mode/xquery/xquery.js b/public/vendor/plugins/codemirror/mode/xquery/xquery.js
index 75dcbee3ea..395b6a7014 100644
--- a/public/vendor/plugins/codemirror/mode/xquery/xquery.js
+++ b/public/vendor/plugins/codemirror/mode/xquery/xquery.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -19,41 +19,52 @@ CodeMirror.defineMode("xquery", function() {
   var keywords = function(){
     // convenience functions used to build keywords object
     function kw(type) {return {type: type, style: "keyword"};}
-    var A = kw("keyword a")
-      , B = kw("keyword b")
-      , C = kw("keyword c")
-      , operator = kw("operator")
+    var operator = kw("operator")
       , atom = {type: "atom", style: "atom"}
       , punctuation = {type: "punctuation", style: null}
       , qualifier = {type: "axis_specifier", style: "qualifier"};
 
     // kwObj is what is return from this function at the end
     var kwObj = {
-      'if': A, 'switch': A, 'while': A, 'for': A,
-      'else': B, 'then': B, 'try': B, 'finally': B, 'catch': B,
-      'element': C, 'attribute': C, 'let': C, 'implements': C, 'import': C, 'module': C, 'namespace': C,
-      'return': C, 'super': C, 'this': C, 'throws': C, 'where': C, 'private': C,
-      ',': punctuation,
-      'null': atom, 'fn:false()': atom, 'fn:true()': atom
+      ',': punctuation
     };
 
     // a list of 'basic' keywords. For each add a property to kwObj with the value of
     // {type: basic[i], style: "keyword"} e.g. 'after' --> {type: "after", style: "keyword"}
-    var basic = ['after','ancestor','ancestor-or-self','and','as','ascending','assert','attribute','before',
-    'by','case','cast','child','comment','declare','default','define','descendant','descendant-or-self',
-    'descending','document','document-node','element','else','eq','every','except','external','following',
-    'following-sibling','follows','for','function','if','import','in','instance','intersect','item',
-    'let','module','namespace','node','node','of','only','or','order','parent','precedes','preceding',
-    'preceding-sibling','processing-instruction','ref','return','returns','satisfies','schema','schema-element',
-    'self','some','sortby','stable','text','then','to','treat','typeswitch','union','variable','version','where',
-    'xquery', 'empty-sequence'];
+    var basic = ['after', 'all', 'allowing', 'ancestor', 'ancestor-or-self', 'any', 'array', 'as',
+    'ascending', 'at', 'attribute', 'base-uri', 'before', 'boundary-space', 'by', 'case', 'cast',
+    'castable', 'catch', 'child', 'collation', 'comment', 'construction', 'contains', 'content',
+    'context', 'copy', 'copy-namespaces', 'count', 'decimal-format', 'declare', 'default', 'delete',
+    'descendant', 'descendant-or-self', 'descending', 'diacritics', 'different', 'distance',
+    'document', 'document-node', 'element', 'else', 'empty', 'empty-sequence', 'encoding', 'end',
+    'entire', 'every', 'exactly', 'except', 'external', 'first', 'following', 'following-sibling',
+    'for', 'from', 'ftand', 'ftnot', 'ft-option', 'ftor', 'function', 'fuzzy', 'greatest', 'group',
+    'if', 'import', 'in', 'inherit', 'insensitive', 'insert', 'instance', 'intersect', 'into',
+    'invoke', 'is', 'item', 'language', 'last', 'lax', 'least', 'let', 'levels', 'lowercase', 'map',
+    'modify', 'module', 'most', 'namespace', 'next', 'no', 'node', 'nodes', 'no-inherit',
+    'no-preserve', 'not', 'occurs', 'of', 'only', 'option', 'order', 'ordered', 'ordering',
+    'paragraph', 'paragraphs', 'parent', 'phrase', 'preceding', 'preceding-sibling', 'preserve',
+    'previous', 'processing-instruction', 'relationship', 'rename', 'replace', 'return',
+    'revalidation', 'same', 'satisfies', 'schema', 'schema-attribute', 'schema-element', 'score',
+    'self', 'sensitive', 'sentence', 'sentences', 'sequence', 'skip', 'sliding', 'some', 'stable',
+    'start', 'stemming', 'stop', 'strict', 'strip', 'switch', 'text', 'then', 'thesaurus', 'times',
+    'to', 'transform', 'treat', 'try', 'tumbling', 'type', 'typeswitch', 'union', 'unordered',
+    'update', 'updating', 'uppercase', 'using', 'validate', 'value', 'variable', 'version',
+    'weight', 'when', 'where', 'wildcards', 'window', 'with', 'without', 'word', 'words', 'xquery'];
     for(var i=0, l=basic.length; i < l; i++) { kwObj[basic[i]] = kw(basic[i]);};
 
     // a list of types. For each add a property to kwObj with the value of
     // {type: "atom", style: "atom"}
-    var types = ['xs:string', 'xs:float', 'xs:decimal', 'xs:double', 'xs:integer', 'xs:boolean', 'xs:date', 'xs:dateTime',
-    'xs:time', 'xs:duration', 'xs:dayTimeDuration', 'xs:time', 'xs:yearMonthDuration', 'numeric', 'xs:hexBinary',
-    'xs:base64Binary', 'xs:anyURI', 'xs:QName', 'xs:byte','xs:boolean','xs:anyURI','xf:yearMonthDuration'];
+    var types = ['xs:anyAtomicType', 'xs:anySimpleType', 'xs:anyType', 'xs:anyURI',
+    'xs:base64Binary', 'xs:boolean', 'xs:byte', 'xs:date', 'xs:dateTime', 'xs:dateTimeStamp',
+    'xs:dayTimeDuration', 'xs:decimal', 'xs:double', 'xs:duration', 'xs:ENTITIES', 'xs:ENTITY',
+    'xs:float', 'xs:gDay', 'xs:gMonth', 'xs:gMonthDay', 'xs:gYear', 'xs:gYearMonth', 'xs:hexBinary',
+    'xs:ID', 'xs:IDREF', 'xs:IDREFS', 'xs:int', 'xs:integer', 'xs:item', 'xs:java', 'xs:language',
+    'xs:long', 'xs:Name', 'xs:NCName', 'xs:negativeInteger', 'xs:NMTOKEN', 'xs:NMTOKENS',
+    'xs:nonNegativeInteger', 'xs:nonPositiveInteger', 'xs:normalizedString', 'xs:NOTATION',
+    'xs:numeric', 'xs:positiveInteger', 'xs:precisionDecimal', 'xs:QName', 'xs:short', 'xs:string',
+    'xs:time', 'xs:token', 'xs:unsignedByte', 'xs:unsignedInt', 'xs:unsignedLong',
+    'xs:unsignedShort', 'xs:untyped', 'xs:untypedAtomic', 'xs:yearMonthDuration'];
     for(var i=0, l=types.length; i < l; i++) { kwObj[types[i]] = atom;};
 
     // each operator will add a property to kwObj with value of {type: "operator", style: "keyword"}
@@ -102,7 +113,7 @@ CodeMirror.defineMode("xquery", function() {
     }
     // start code block
     else if(ch == "{") {
-      pushStateStack(state,{ type: "codeblock"});
+      pushStateStack(state, { type: "codeblock"});
       return null;
     }
     // end code block
@@ -132,7 +143,7 @@ CodeMirror.defineMode("xquery", function() {
       return chain(stream, state, tokenComment);
     }
     // quoted string
-    else if (  !isEQName && (ch === '"' || ch === "'"))
+    else if (!isEQName && (ch === '"' || ch === "'"))
       return chain(stream, state, tokenString(ch));
     // variable
     else if(ch === "$") {
diff --git a/public/vendor/plugins/codemirror/mode/yacas/index.html b/public/vendor/plugins/codemirror/mode/yacas/index.html
index 8e52cafb10..52236fc97d 100644
--- a/public/vendor/plugins/codemirror/mode/yacas/index.html
+++ b/public/vendor/plugins/codemirror/mode/yacas/index.html
@@ -12,7 +12,7 @@
   .CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}
 </style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/yacas/yacas.js b/public/vendor/plugins/codemirror/mode/yacas/yacas.js
index 30bd60b2ff..b7ac96b71d 100644
--- a/public/vendor/plugins/codemirror/mode/yacas/yacas.js
+++ b/public/vendor/plugins/codemirror/mode/yacas/yacas.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 // Yacas mode copyright (c) 2015 by Grzegorz Mazur
 // Loosely based on mathematica mode by Calin Barbat
@@ -125,7 +125,7 @@ CodeMirror.defineMode('yacas', function(_config, _parserConfig) {
     }
 
     // operators; note that operators like @@ or /; are matched separately for each symbol.
-    if (stream.match(/(?:\\|\+|\-|\*|\/|,|;|\.|:|@|~|=|>|<|&|\||_|`|'|\^|\?|!|%)/, true, false)) {
+    if (stream.match(/(?:\\|\+|\-|\*|\/|,|;|\.|:|@|~|=|>|<|&|\||_|`|'|\^|\?|!|%|#)/, true, false)) {
       return 'operator';
     }
 
diff --git a/public/vendor/plugins/codemirror/mode/yaml-frontmatter/index.html b/public/vendor/plugins/codemirror/mode/yaml-frontmatter/index.html
index 30cb294e80..e6283fc678 100644
--- a/public/vendor/plugins/codemirror/mode/yaml-frontmatter/index.html
+++ b/public/vendor/plugins/codemirror/mode/yaml-frontmatter/index.html
@@ -13,7 +13,7 @@
 <script src="yaml-frontmatter.js"></script>
 <style>.CodeMirror { border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; }</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/yaml-frontmatter/yaml-frontmatter.js b/public/vendor/plugins/codemirror/mode/yaml-frontmatter/yaml-frontmatter.js
index 5f49772370..e9faefcf9b 100644
--- a/public/vendor/plugins/codemirror/mode/yaml-frontmatter/yaml-frontmatter.js
+++ b/public/vendor/plugins/codemirror/mode/yaml-frontmatter/yaml-frontmatter.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function (mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/public/vendor/plugins/codemirror/mode/yaml/index.html b/public/vendor/plugins/codemirror/mode/yaml/index.html
index be9b632368..bfea6111da 100644
--- a/public/vendor/plugins/codemirror/mode/yaml/index.html
+++ b/public/vendor/plugins/codemirror/mode/yaml/index.html
@@ -9,7 +9,7 @@
 <script src="yaml.js"></script>
 <style>.CodeMirror { border-top: 1px solid #ddd; border-bottom: 1px solid #ddd; }</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/yaml/yaml.js b/public/vendor/plugins/codemirror/mode/yaml/yaml.js
index b7015e599c..a29d7ea4a7 100644
--- a/public/vendor/plugins/codemirror/mode/yaml/yaml.js
+++ b/public/vendor/plugins/codemirror/mode/yaml/yaml.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
@@ -108,10 +108,13 @@ CodeMirror.defineMode("yaml", function() {
         literal: false,
         escaped: false
       };
-    }
+    },
+    lineComment: "#",
+    fold: "indent"
   };
 });
 
 CodeMirror.defineMIME("text/x-yaml", "yaml");
+CodeMirror.defineMIME("text/yaml", "yaml");
 
 });
diff --git a/public/vendor/plugins/codemirror/mode/z80/index.html b/public/vendor/plugins/codemirror/mode/z80/index.html
index a41b7473e2..8f97cce2b8 100644
--- a/public/vendor/plugins/codemirror/mode/z80/index.html
+++ b/public/vendor/plugins/codemirror/mode/z80/index.html
@@ -7,9 +7,9 @@
 <link rel="stylesheet" href="../../lib/codemirror.css">
 <script src="../../lib/codemirror.js"></script>
 <script src="z80.js"></script>
-<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+<style>.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
 <div id=nav>
-  <a href="http://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
+  <a href="https://codemirror.net"><h1>CodeMirror</h1><img id=logo src="../../doc/logo.png"></a>
 
   <ul>
     <li><a href="../../index.html">Home</a>
diff --git a/public/vendor/plugins/codemirror/mode/z80/z80.js b/public/vendor/plugins/codemirror/mode/z80/z80.js
index aae70216f8..8cea4ff90c 100644
--- a/public/vendor/plugins/codemirror/mode/z80/z80.js
+++ b/public/vendor/plugins/codemirror/mode/z80/z80.js
@@ -1,5 +1,5 @@
 // CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: http://codemirror.net/LICENSE
+// Distributed under an MIT license: https://codemirror.net/LICENSE
 
 (function(mod) {
   if (typeof exports == "object" && typeof module == "object") // CommonJS
diff --git a/routers/admin/users.go b/routers/admin/users.go
index 660f116682..fdc4e0e371 100644
--- a/routers/admin/users.go
+++ b/routers/admin/users.go
@@ -12,6 +12,7 @@ import (
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/password"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/routers"
 	"code.gitea.io/gitea/services/mailer"
@@ -94,7 +95,10 @@ func NewUserPost(ctx *context.Context, form auth.AdminCreateUserForm) {
 			u.LoginName = form.LoginName
 		}
 	}
-
+	if !password.IsComplexEnough(form.Password) {
+		ctx.RenderWithErr(ctx.Tr("form.password_complexity"), tplUserNew, &form)
+		return
+	}
 	if err := models.CreateUser(u); err != nil {
 		switch {
 		case models.IsErrUserAlreadyExist(err):
@@ -201,6 +205,10 @@ func EditUserPost(ctx *context.Context, form auth.AdminEditUserForm) {
 			ctx.ServerError("UpdateUser", err)
 			return
 		}
+		if !password.IsComplexEnough(form.Password) {
+			ctx.RenderWithErr(ctx.Tr("form.password_complexity"), tplUserEdit, &form)
+			return
+		}
 		u.HashPassword(form.Password)
 	}
 
diff --git a/routers/admin/users_test.go b/routers/admin/users_test.go
index e054524fd1..2b36b45d49 100644
--- a/routers/admin/users_test.go
+++ b/routers/admin/users_test.go
@@ -34,7 +34,7 @@ func TestNewUserPost_MustChangePassword(t *testing.T) {
 		LoginName:          "local",
 		UserName:           username,
 		Email:              email,
-		Password:           "xxxxxxxx",
+		Password:           "abc123ABC!=$",
 		SendNotify:         false,
 		MustChangePassword: true,
 	}
@@ -71,7 +71,7 @@ func TestNewUserPost_MustChangePasswordFalse(t *testing.T) {
 		LoginName:          "local",
 		UserName:           username,
 		Email:              email,
-		Password:           "xxxxxxxx",
+		Password:           "abc123ABC!=$",
 		SendNotify:         false,
 		MustChangePassword: false,
 	}
diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go
index 70076b626b..f35ad297b0 100644
--- a/routers/api/v1/admin/user.go
+++ b/routers/api/v1/admin/user.go
@@ -6,9 +6,12 @@
 package admin
 
 import (
+	"errors"
+
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/password"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/routers/api/v1/convert"
 	"code.gitea.io/gitea/routers/api/v1/user"
@@ -73,7 +76,11 @@ func CreateUser(ctx *context.APIContext, form api.CreateUserOption) {
 	if ctx.Written() {
 		return
 	}
-
+	if !password.IsComplexEnough(form.Password) {
+		err := errors.New("PasswordComplexity")
+		ctx.Error(400, "PasswordComplexity", err)
+		return
+	}
 	if err := models.CreateUser(u); err != nil {
 		if models.IsErrUserAlreadyExist(err) ||
 			models.IsErrEmailAlreadyUsed(err) ||
@@ -131,6 +138,11 @@ func EditUser(ctx *context.APIContext, form api.EditUserOption) {
 	}
 
 	if len(form.Password) > 0 {
+		if !password.IsComplexEnough(form.Password) {
+			err := errors.New("PasswordComplexity")
+			ctx.Error(400, "PasswordComplexity", err)
+			return
+		}
 		var err error
 		if u.Salt, err = models.GetUserSalt(); err != nil {
 			ctx.Error(500, "UpdateUser", err)
diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go
index 04ff91fbbf..f8ab9025b7 100644
--- a/routers/api/v1/api.go
+++ b/routers/api/v1/api.go
@@ -507,6 +507,7 @@ func RegisterRoutes(m *macaron.Macaron) {
 			m.Get("/swagger", misc.Swagger)
 		}
 		m.Get("/version", misc.Version)
+		m.Get("/signing-key.gpg", misc.SigningKey)
 		m.Post("/markdown", bind(api.MarkdownOption{}), misc.Markdown)
 		m.Post("/markdown/raw", misc.MarkdownRaw)
 
@@ -771,6 +772,7 @@ func RegisterRoutes(m *macaron.Macaron) {
 						m.Delete("", bind(api.DeleteFileOptions{}), repo.DeleteFile)
 					}, reqRepoWriter(models.UnitTypeCode), reqToken())
 				}, reqRepoReader(models.UnitTypeCode))
+				m.Get("/signing-key.gpg", misc.SigningKey)
 				m.Group("/topics", func() {
 					m.Combo("").Get(repo.ListTopics).
 						Put(reqToken(), reqAdmin(), bind(api.RepoTopicOptions{}), repo.UpdateTopics)
diff --git a/routers/api/v1/convert/convert.go b/routers/api/v1/convert/convert.go
index a6a6d9fa15..690e45c4e0 100644
--- a/routers/api/v1/convert/convert.go
+++ b/routers/api/v1/convert/convert.go
@@ -12,6 +12,7 @@ import (
 	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/markup"
+	"code.gitea.io/gitea/modules/structs"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/util"
 
@@ -84,17 +85,21 @@ func ToCommit(repo *models.Repository, c *git.Commit) *api.PayloadCommit {
 // ToVerification convert a git.Commit.Signature to an api.PayloadCommitVerification
 func ToVerification(c *git.Commit) *api.PayloadCommitVerification {
 	verif := models.ParseCommitWithSignature(c)
-	var signature, payload string
+	commitVerification := &api.PayloadCommitVerification{
+		Verified: verif.Verified,
+		Reason:   verif.Reason,
+	}
 	if c.Signature != nil {
-		signature = c.Signature.Signature
-		payload = c.Signature.Payload
+		commitVerification.Signature = c.Signature.Signature
+		commitVerification.Payload = c.Signature.Payload
 	}
-	return &api.PayloadCommitVerification{
-		Verified:  verif.Verified,
-		Reason:    verif.Reason,
-		Signature: signature,
-		Payload:   payload,
+	if verif.SigningUser != nil {
+		commitVerification.Signer = &structs.PayloadUser{
+			Name:  verif.SigningUser.Name,
+			Email: verif.SigningUser.Email,
+		}
 	}
+	return commitVerification
 }
 
 // ToPublicKey convert models.PublicKey to api.PublicKey
@@ -233,12 +238,9 @@ func ToTeam(team *models.Team) *api.Team {
 // ToUser convert models.User to api.User
 func ToUser(user *models.User, signed, authed bool) *api.User {
 	result := &api.User{
-		ID:        user.ID,
 		UserName:  user.Name,
 		AvatarURL: user.AvatarLink(),
 		FullName:  markup.Sanitize(user.FullName),
-		IsAdmin:   user.IsAdmin,
-		LastLogin: user.LastLoginUnix.AsTime(),
 		Created:   user.CreatedUnix.AsTime(),
 	}
 	// hide primary email if API caller isn't user itself or an admin
@@ -246,8 +248,11 @@ func ToUser(user *models.User, signed, authed bool) *api.User {
 		result.Email = ""
 	} else if user.KeepEmailPrivate && !authed {
 		result.Email = user.GetEmail()
-	} else {
+	} else { // only user himself and admin could visit these information
+		result.ID = user.ID
 		result.Email = user.Email
+		result.IsAdmin = user.IsAdmin
+		result.LastLogin = user.LastLoginUnix.AsTime()
 	}
 	return result
 }
diff --git a/routers/api/v1/misc/signing.go b/routers/api/v1/misc/signing.go
new file mode 100644
index 0000000000..f5428670af
--- /dev/null
+++ b/routers/api/v1/misc/signing.go
@@ -0,0 +1,62 @@
+package misc
+
+import (
+	"fmt"
+	"net/http"
+
+	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/modules/context"
+	"code.gitea.io/gitea/modules/log"
+)
+
+// SigningKey returns the public key of the default signing key if it exists
+func SigningKey(ctx *context.Context) {
+	// swagger:operation GET /signing-key.gpg miscellaneous getSigningKey
+	// ---
+	// summary: Get default signing-key.gpg
+	// produces:
+	//     - text/plain
+	// responses:
+	//   "200":
+	//     description: "GPG armored public key"
+	//     schema:
+	//       type: string
+
+	// swagger:operation GET /repos/{owner}/{repo}/signing-key.gpg repository repoSigningKey
+	// ---
+	// summary: Get signing-key.gpg for given repository
+	// produces:
+	//     - text/plain
+	// parameters:
+	// - name: owner
+	//   in: path
+	//   description: owner of the repo
+	//   type: string
+	//   required: true
+	// - name: repo
+	//   in: path
+	//   description: name of the repo
+	//   type: string
+	//   required: true
+	// responses:
+	//   "200":
+	//     description: "GPG armored public key"
+	//     schema:
+	//       type: string
+
+	path := ""
+	if ctx.Repo != nil && ctx.Repo.Repository != nil {
+		path = ctx.Repo.Repository.RepoPath()
+	}
+
+	content, err := models.PublicSigningKey(path)
+	if err != nil {
+		ctx.ServerError("gpg export", err)
+		return
+	}
+	_, err = ctx.Write([]byte(content))
+	if err != nil {
+		log.Error("Error writing key content %v", err)
+		ctx.Error(http.StatusInternalServerError, fmt.Sprintf("%v", err))
+	}
+}
diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go
index ae20e1e96b..f1a61bb0be 100644
--- a/routers/api/v1/repo/file.go
+++ b/routers/api/v1/repo/file.go
@@ -136,7 +136,7 @@ func GetEditorconfig(ctx *context.APIContext) {
 	}
 
 	fileName := ctx.Params("filename")
-	def := ec.GetDefinitionForFilename(fileName)
+	def, err := ec.GetDefinitionForFilename(fileName)
 	if def == nil {
 		ctx.NotFound(err)
 		return
diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go
index aab167bc68..9529e09b29 100644
--- a/routers/api/v1/repo/issue.go
+++ b/routers/api/v1/repo/issue.go
@@ -213,12 +213,31 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) {
 			}
 			return
 		}
+
+		// Check if the passed assignees is assignable
+		for _, aID := range assigneeIDs {
+			assignee, err := models.GetUserByID(aID)
+			if err != nil {
+				ctx.Error(500, "GetUserByID", err)
+				return
+			}
+
+			valid, err := models.CanBeAssigned(assignee, ctx.Repo.Repository, false)
+			if err != nil {
+				ctx.Error(500, "canBeAssigned", err)
+				return
+			}
+			if !valid {
+				ctx.Error(422, "canBeAssigned", models.ErrUserDoesNotHaveAccessToRepo{UserID: aID, RepoName: ctx.Repo.Repository.Name})
+				return
+			}
+		}
 	} else {
 		// setting labels is not allowed if user is not a writer
 		form.Labels = make([]int64, 0)
 	}
 
-	if err := issue_service.NewIssue(ctx.Repo.Repository, issue, form.Labels, assigneeIDs, nil); err != nil {
+	if err := issue_service.NewIssue(ctx.Repo.Repository, issue, form.Labels, nil); err != nil {
 		if models.IsErrUserDoesNotHaveAccessToRepo(err) {
 			ctx.Error(400, "UserDoesNotHaveAccessToRepo", err)
 			return
@@ -227,6 +246,11 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) {
 		return
 	}
 
+	if err := issue_service.AddAssignees(issue, ctx.User, assigneeIDs); err != nil {
+		ctx.ServerError("AddAssignees", err)
+		return
+	}
+
 	notification.NotifyNewIssue(issue)
 
 	if form.Closed {
@@ -336,9 +360,9 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
 			oneAssignee = *form.Assignee
 		}
 
-		err = models.UpdateAPIAssignee(issue, oneAssignee, form.Assignees, ctx.User)
+		err = issue_service.UpdateAssignees(issue, oneAssignee, form.Assignees, ctx.User)
 		if err != nil {
-			ctx.Error(500, "UpdateAPIAssignee", err)
+			ctx.Error(500, "UpdateAssignees", err)
 			return
 		}
 	}
diff --git a/routers/api/v1/repo/issue_label.go b/routers/api/v1/repo/issue_label.go
index e66bc35e90..e41c1512e2 100644
--- a/routers/api/v1/repo/issue_label.go
+++ b/routers/api/v1/repo/issue_label.go
@@ -9,6 +9,7 @@ import (
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/context"
 	api "code.gitea.io/gitea/modules/structs"
+	issue_service "code.gitea.io/gitea/services/issue"
 )
 
 // ListIssueLabels list all the labels of an issue
@@ -116,7 +117,7 @@ func AddIssueLabels(ctx *context.APIContext, form api.IssueLabelsOption) {
 		return
 	}
 
-	if err = issue.AddLabels(ctx.User, labels); err != nil {
+	if err = issue_service.AddLabels(issue, ctx.User, labels); err != nil {
 		ctx.Error(500, "AddLabels", err)
 		return
 	}
@@ -314,7 +315,7 @@ func ClearIssueLabels(ctx *context.APIContext) {
 		return
 	}
 
-	if err := issue.ClearLabels(ctx.User); err != nil {
+	if err := issue_service.ClearLabels(issue, ctx.User); err != nil {
 		ctx.Error(500, "ClearLabels", err)
 		return
 	}
diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go
index 978c8a3f1f..6b20844c51 100644
--- a/routers/api/v1/repo/pull.go
+++ b/routers/api/v1/repo/pull.go
@@ -17,6 +17,7 @@ import (
 	"code.gitea.io/gitea/modules/notification"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/timeutil"
+	issue_service "code.gitea.io/gitea/services/issue"
 	milestone_service "code.gitea.io/gitea/services/milestone"
 	pull_service "code.gitea.io/gitea/services/pull"
 )
@@ -190,7 +191,7 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
 	)
 
 	// Get repo/branch information
-	headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch := parseCompareInfo(ctx, form)
+	_, headRepo, headGitRepo, compareInfo, baseBranch, headBranch := parseCompareInfo(ctx, form)
 	if ctx.Written() {
 		return
 	}
@@ -265,15 +266,14 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
 		DeadlineUnix: deadlineUnix,
 	}
 	pr := &models.PullRequest{
-		HeadRepoID:   headRepo.ID,
-		BaseRepoID:   repo.ID,
-		HeadUserName: headUser.Name,
-		HeadBranch:   headBranch,
-		BaseBranch:   baseBranch,
-		HeadRepo:     headRepo,
-		BaseRepo:     repo,
-		MergeBase:    compareInfo.MergeBase,
-		Type:         models.PullRequestGitea,
+		HeadRepoID: headRepo.ID,
+		BaseRepoID: repo.ID,
+		HeadBranch: headBranch,
+		BaseBranch: baseBranch,
+		HeadRepo:   headRepo,
+		BaseRepo:   repo,
+		MergeBase:  compareInfo.MergeBase,
+		Type:       models.PullRequestGitea,
 	}
 
 	// Get all assignee IDs
@@ -286,8 +286,26 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
 		}
 		return
 	}
+	// Check if the passed assignees is assignable
+	for _, aID := range assigneeIDs {
+		assignee, err := models.GetUserByID(aID)
+		if err != nil {
+			ctx.Error(500, "GetUserByID", err)
+			return
+		}
 
-	if err := pull_service.NewPullRequest(repo, prIssue, labelIDs, []string{}, pr, patch, assigneeIDs); err != nil {
+		valid, err := models.CanBeAssigned(assignee, repo, true)
+		if err != nil {
+			ctx.Error(500, "canBeAssigned", err)
+			return
+		}
+		if !valid {
+			ctx.Error(422, "canBeAssigned", models.ErrUserDoesNotHaveAccessToRepo{UserID: aID, RepoName: repo.Name})
+			return
+		}
+	}
+
+	if err := pull_service.NewPullRequest(repo, prIssue, labelIDs, []string{}, pr, patch); err != nil {
 		if models.IsErrUserDoesNotHaveAccessToRepo(err) {
 			ctx.Error(400, "UserDoesNotHaveAccessToRepo", err)
 			return
@@ -299,6 +317,11 @@ func CreatePullRequest(ctx *context.APIContext, form api.CreatePullRequestOption
 		return
 	}
 
+	if err := issue_service.AddAssignees(prIssue, ctx.User, assigneeIDs); err != nil {
+		ctx.ServerError("AddAssignees", err)
+		return
+	}
+
 	notification.NotifyNewPullRequest(pr)
 
 	log.Trace("Pull request created: %d/%d", repo.ID, prIssue.ID)
@@ -388,12 +411,12 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
 	// Send an empty array ([]) to clear all assignees from the Issue.
 
 	if ctx.Repo.CanWrite(models.UnitTypePullRequests) && (form.Assignees != nil || len(form.Assignee) > 0) {
-		err = models.UpdateAPIAssignee(issue, form.Assignee, form.Assignees, ctx.User)
+		err = issue_service.UpdateAssignees(issue, form.Assignee, form.Assignees, ctx.User)
 		if err != nil {
 			if models.IsErrUserNotExist(err) {
 				ctx.Error(422, "", fmt.Sprintf("Assignee does not exist: [name: %s]", err))
 			} else {
-				ctx.Error(500, "UpdateAPIAssignee", err)
+				ctx.Error(500, "UpdateAssignees", err)
 			}
 			return
 		}
diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
index d8b06862a5..a4417107ee 100644
--- a/routers/api/v1/repo/repo.go
+++ b/routers/api/v1/repo/repo.go
@@ -8,6 +8,7 @@ package repo
 import (
 	"fmt"
 	"net/http"
+	"net/url"
 	"strings"
 
 	"code.gitea.io/gitea/models"
@@ -17,6 +18,7 @@ import (
 	"code.gitea.io/gitea/modules/migrations"
 	"code.gitea.io/gitea/modules/notification"
 	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/structs"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/modules/validation"
@@ -397,21 +399,28 @@ func Migrate(ctx *context.APIContext, form auth.MigrateRepoForm) {
 		return
 	}
 
+	var gitServiceType = structs.PlainGitService
+	u, err := url.Parse(remoteAddr)
+	if err == nil && strings.EqualFold(u.Host, "github.com") {
+		gitServiceType = structs.GithubService
+	}
+
 	var opts = migrations.MigrateOptions{
-		RemoteURL:    remoteAddr,
-		Name:         form.RepoName,
-		Description:  form.Description,
-		Private:      form.Private || setting.Repository.ForcePrivate,
-		Mirror:       form.Mirror,
-		AuthUsername: form.AuthUsername,
-		AuthPassword: form.AuthPassword,
-		Wiki:         form.Wiki,
-		Issues:       form.Issues,
-		Milestones:   form.Milestones,
-		Labels:       form.Labels,
-		Comments:     true,
-		PullRequests: form.PullRequests,
-		Releases:     form.Releases,
+		CloneAddr:      remoteAddr,
+		RepoName:       form.RepoName,
+		Description:    form.Description,
+		Private:        form.Private || setting.Repository.ForcePrivate,
+		Mirror:         form.Mirror,
+		AuthUsername:   form.AuthUsername,
+		AuthPassword:   form.AuthPassword,
+		Wiki:           form.Wiki,
+		Issues:         form.Issues,
+		Milestones:     form.Milestones,
+		Labels:         form.Labels,
+		Comments:       true,
+		PullRequests:   form.PullRequests,
+		Releases:       form.Releases,
+		GitServiceType: gitServiceType,
 	}
 	if opts.Mirror {
 		opts.Issues = false
diff --git a/routers/api/v1/user/repo.go b/routers/api/v1/user/repo.go
index 5848169088..99839b020a 100644
--- a/routers/api/v1/user/repo.go
+++ b/routers/api/v1/user/repo.go
@@ -52,7 +52,7 @@ func ListUserRepos(ctx *context.APIContext) {
 	if ctx.Written() {
 		return
 	}
-	private := ctx.IsSigned && (ctx.User.ID == user.ID || ctx.User.IsAdmin)
+	private := ctx.IsSigned
 	listUserRepos(ctx, user, private)
 }
 
diff --git a/routers/api/v1/user/user.go b/routers/api/v1/user/user.go
index bb1302077b..0639494c0e 100644
--- a/routers/api/v1/user/user.go
+++ b/routers/api/v1/user/user.go
@@ -104,7 +104,7 @@ func GetInfo(ctx *context.APIContext) {
 		return
 	}
 
-	ctx.JSON(200, convert.ToUser(u, ctx.IsSigned, ctx.User.ID == u.ID || ctx.User.IsAdmin))
+	ctx.JSON(200, convert.ToUser(u, ctx.IsSigned, ctx.User != nil && (ctx.User.ID == u.ID || ctx.User.IsAdmin)))
 }
 
 // GetAuthenticatedUser get current user's information
diff --git a/routers/home.go b/routers/home.go
index f8fb849c8e..eddff28ee9 100644
--- a/routers/home.go
+++ b/routers/home.go
@@ -273,7 +273,7 @@ func ExploreOrganizations(ctx *context.Context) {
 // ExploreCode render explore code page
 func ExploreCode(ctx *context.Context) {
 	if !setting.Indexer.RepoIndexerEnabled {
-		ctx.Redirect("/explore", 302)
+		ctx.Redirect(setting.AppSubURL+"/explore", 302)
 		return
 	}
 
diff --git a/routers/init.go b/routers/init.go
index 1efddcfaa6..bdd978d717 100644
--- a/routers/init.go
+++ b/routers/init.go
@@ -18,8 +18,10 @@ import (
 	"code.gitea.io/gitea/modules/log"
 	"code.gitea.io/gitea/modules/markup"
 	"code.gitea.io/gitea/modules/markup/external"
+	"code.gitea.io/gitea/modules/notification"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/ssh"
+	"code.gitea.io/gitea/modules/task"
 	"code.gitea.io/gitea/services/mailer"
 	mirror_service "code.gitea.io/gitea/services/mirror"
 
@@ -43,6 +45,7 @@ func NewServices() {
 	setting.NewServices()
 	mailer.NewContext()
 	_ = cache.NewContext()
+	notification.NewContext()
 }
 
 // In case of problems connecting to DB, retry connection. Eg, PGSQL in Docker Container on Synology
@@ -95,21 +98,29 @@ func GlobalInit() {
 
 		// Booting long running goroutines.
 		cron.NewContext()
-		if err := issue_indexer.InitIssueIndexer(false); err != nil {
-			log.Fatal("Failed to initialize issue indexer: %v", err)
-		}
+		issue_indexer.InitIssueIndexer(false)
 		models.InitRepoIndexer()
 		mirror_service.InitSyncMirrors()
 		models.InitDeliverHooks()
 		models.InitTestPullRequests()
+		if err := task.Init(); err != nil {
+			log.Fatal("Failed to initialize task scheduler: %v", err)
+		}
 	}
 	if setting.EnableSQLite3 {
 		log.Info("SQLite3 Supported")
 	}
 	checkRunMode()
 
-	if setting.InstallLock && setting.SSH.StartBuiltinServer {
-		ssh.Listen(setting.SSH.ListenHost, setting.SSH.ListenPort, setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs)
-		log.Info("SSH server started on %s:%d. Cipher list (%v), key exchange algorithms (%v), MACs (%v)", setting.SSH.ListenHost, setting.SSH.ListenPort, setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs)
+	// Now because Install will re-run GlobalInit once it has set InstallLock
+	// we can't tell if the ssh port will remain unused until that's done.
+	// However, see FIXME comment in install.go
+	if setting.InstallLock {
+		if setting.SSH.StartBuiltinServer {
+			ssh.Listen(setting.SSH.ListenHost, setting.SSH.ListenPort, setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs)
+			log.Info("SSH server started on %s:%d. Cipher list (%v), key exchange algorithms (%v), MACs (%v)", setting.SSH.ListenHost, setting.SSH.ListenPort, setting.SSH.ServerCiphers, setting.SSH.ServerKeyExchanges, setting.SSH.ServerMACs)
+		} else {
+			ssh.Unused()
+		}
 	}
 }
diff --git a/routers/install.go b/routers/install.go
index 16888adc82..53880d2c46 100644
--- a/routers/install.go
+++ b/routers/install.go
@@ -20,9 +20,9 @@ import (
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/user"
 
-	"github.com/go-xorm/xorm"
 	"github.com/unknwon/com"
 	"gopkg.in/ini.v1"
+	"xorm.io/xorm"
 )
 
 const (
@@ -386,6 +386,12 @@ func InstallPost(ctx *context.Context, form auth.InstallForm) {
 	}
 
 	log.Info("First-time run install finished!")
+	// FIXME: This isn't really enough to completely take account of new configuration
+	// We should really be restarting:
+	// - On windows this is probably just a simple restart
+	// - On linux we can't just use graceful.RestartProcess() everything that was passed in on LISTEN_FDS
+	//   (active or not) needs to be passed out and everything new passed out too.
+	//   This means we need to prevent the cleanup goroutine from running prior to the second GlobalInit
 	ctx.Flash.Success(ctx.Tr("install.install_success"))
 	ctx.Redirect(form.AppURL + "user/login")
 }
diff --git a/routers/org/org.go b/routers/org/org.go
index 3821b32216..85bc25217b 100644
--- a/routers/org/org.go
+++ b/routers/org/org.go
@@ -47,10 +47,11 @@ func CreatePost(ctx *context.Context, form auth.CreateOrgForm) {
 	}
 
 	org := &models.User{
-		Name:       form.OrgName,
-		IsActive:   true,
-		Type:       models.UserTypeOrganization,
-		Visibility: form.Visibility,
+		Name:                      form.OrgName,
+		IsActive:                  true,
+		Type:                      models.UserTypeOrganization,
+		Visibility:                form.Visibility,
+		RepoAdminChangeTeamAccess: form.RepoAdminChangeTeamAccess,
 	}
 
 	if err := models.CreateOrganization(org, ctx.User); err != nil {
diff --git a/routers/private/hook.go b/routers/private/hook.go
index 1f6ab2f673..074e3aef19 100644
--- a/routers/private/hook.go
+++ b/routers/private/hook.go
@@ -33,6 +33,7 @@ func HookPreReceive(ctx *macaron.Context) {
 	gitAlternativeObjectDirectories := ctx.QueryTrim("gitAlternativeObjectDirectories")
 	gitQuarantinePath := ctx.QueryTrim("gitQuarantinePath")
 	prID := ctx.QueryInt64("prID")
+	isDeployKey := ctx.QueryBool("isDeployKey")
 
 	branchName := strings.TrimPrefix(refFullName, git.BranchPrefix)
 	repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName)
@@ -95,7 +96,12 @@ func HookPreReceive(ctx *macaron.Context) {
 			}
 		}
 
-		canPush := protectBranch.CanUserPush(userID)
+		canPush := false
+		if isDeployKey {
+			canPush = protectBranch.WhitelistDeployKeys
+		} else {
+			canPush = protectBranch.CanUserPush(userID)
+		}
 		if !canPush && prID > 0 {
 			pr, err := models.GetPullRequestByID(prID)
 			if err != nil {
diff --git a/routers/private/serv.go b/routers/private/serv.go
index 71c0f6ea2c..c4508b4cb5 100644
--- a/routers/private/serv.go
+++ b/routers/private/serv.go
@@ -119,6 +119,15 @@ func ServCommand(ctx *macaron.Context) {
 	repo.OwnerName = ownerName
 	results.RepoID = repo.ID
 
+	if repo.IsBeingCreated() {
+		ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
+			"results": results,
+			"type":    "InternalServerError",
+			"err":     "Repository is being created, you could retry after it finished",
+		})
+		return
+	}
+
 	// We can shortcut at this point if the repo is a mirror
 	if mode > models.AccessModeRead && repo.IsMirror {
 		ctx.JSON(http.StatusUnauthorized, map[string]interface{}{
diff --git a/routers/repo/attachment.go b/routers/repo/attachment.go
index a07a2a8ace..0d496230e1 100644
--- a/routers/repo/attachment.go
+++ b/routers/repo/attachment.go
@@ -63,3 +63,25 @@ func UploadAttachment(ctx *context.Context) {
 		"uuid": attach.UUID,
 	})
 }
+
+// DeleteAttachment response for deleting issue's attachment
+func DeleteAttachment(ctx *context.Context) {
+	file := ctx.Query("file")
+	attach, err := models.GetAttachmentByUUID(file)
+	if !ctx.IsSigned || (ctx.User.ID != attach.UploaderID) {
+		ctx.Error(403)
+		return
+	}
+	if err != nil {
+		ctx.Error(400, err.Error())
+		return
+	}
+	err = models.DeleteAttachment(attach, true)
+	if err != nil {
+		ctx.Error(500, fmt.Sprintf("DeleteAttachment: %v", err))
+		return
+	}
+	ctx.JSON(200, map[string]string{
+		"uuid": attach.UUID,
+	})
+}
diff --git a/routers/repo/branch.go b/routers/repo/branch.go
index 5d78518491..0c06de3ea6 100644
--- a/routers/repo/branch.go
+++ b/routers/repo/branch.go
@@ -28,6 +28,7 @@ type Branch struct {
 	Commit            *git.Commit
 	IsProtected       bool
 	IsDeleted         bool
+	IsIncluded        bool
 	DeletedBranch     *models.DeletedBranch
 	CommitsAhead      int
 	CommitsBehind     int
@@ -203,10 +204,13 @@ func loadBranches(ctx *context.Context) []*Branch {
 			}
 		}
 
+		isIncluded := divergence.Ahead == 0 && ctx.Repo.Repository.DefaultBranch != branchName
+
 		branches[i] = &Branch{
 			Name:              branchName,
 			Commit:            commit,
 			IsProtected:       isProtected,
+			IsIncluded:        isIncluded,
 			CommitsAhead:      divergence.Ahead,
 			CommitsBehind:     divergence.Behind,
 			LatestPullRequest: pr,
diff --git a/routers/repo/commit.go b/routers/repo/commit.go
index 3cedf70319..550e4c3a9c 100644
--- a/routers/repo/commit.go
+++ b/routers/repo/commit.go
@@ -91,7 +91,9 @@ func Graph(ctx *context.Context) {
 		return
 	}
 
-	graph, err := models.GetCommitGraph(ctx.Repo.GitRepo)
+	page := ctx.QueryInt("page")
+
+	graph, err := models.GetCommitGraph(ctx.Repo.GitRepo, page)
 	if err != nil {
 		ctx.ServerError("GetCommitGraph", err)
 		return
@@ -103,8 +105,8 @@ func Graph(ctx *context.Context) {
 	ctx.Data["CommitCount"] = commitsCount
 	ctx.Data["Branch"] = ctx.Repo.BranchName
 	ctx.Data["RequireGitGraph"] = true
+	ctx.Data["Page"] = context.NewPagination(int(commitsCount), setting.UI.GraphMaxCommitNum, page, 5)
 	ctx.HTML(200, tplGraph)
-
 }
 
 // SearchCommits render commits filtered by keyword
diff --git a/routers/repo/download.go b/routers/repo/download.go
index 2da8b109ca..6f10fe36a3 100644
--- a/routers/repo/download.go
+++ b/routers/repo/download.go
@@ -84,6 +84,11 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob) error {
 		if err != nil {
 			return err
 		}
+		defer func() {
+			if err = lfsDataRc.Close(); err != nil {
+				log.Error("ServeBlobOrLFS: Close: %v", err)
+			}
+		}()
 		return ServeData(ctx, ctx.Repo.TreePath, lfsDataRc)
 	}
 
diff --git a/routers/repo/http.go b/routers/repo/http.go
index 09dd820585..d41c63ba35 100644
--- a/routers/repo/http.go
+++ b/routers/repo/http.go
@@ -263,6 +263,7 @@ func HTTP(ctx *context.Context) {
 			models.EnvPusherName + "=" + authUser.Name,
 			models.EnvPusherID + fmt.Sprintf("=%d", authUser.ID),
 			models.ProtectedBranchRepoID + fmt.Sprintf("=%d", repo.ID),
+			models.EnvIsDeployKey + "=false",
 		}
 
 		if !authUser.KeepEmailPrivate {
diff --git a/routers/repo/issue.go b/routers/repo/issue.go
index e4ef3b1dbb..94c39ae224 100644
--- a/routers/repo/issue.go
+++ b/routers/repo/issue.go
@@ -34,6 +34,8 @@ import (
 )
 
 const (
+	tplAttachment base.TplName = "repo/issue/view_content/attachments"
+
 	tplIssues    base.TplName = "repo/issue/list"
 	tplIssueNew  base.TplName = "repo/issue/new"
 	tplIssueView base.TplName = "repo/issue/view"
@@ -501,21 +503,21 @@ func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm, isPull b
 			return nil, nil, 0
 		}
 
-		// Check if the passed assignees actually exists and has write access to the repo
+		// Check if the passed assignees actually exists and is assignable
 		for _, aID := range assigneeIDs {
-			user, err := models.GetUserByID(aID)
+			assignee, err := models.GetUserByID(aID)
 			if err != nil {
 				ctx.ServerError("GetUserByID", err)
 				return nil, nil, 0
 			}
 
-			perm, err := models.GetUserRepoPermission(repo, user)
+			valid, err := models.CanBeAssigned(assignee, repo, isPull)
 			if err != nil {
-				ctx.ServerError("GetUserRepoPermission", err)
+				ctx.ServerError("canBeAssigned", err)
 				return nil, nil, 0
 			}
-			if !perm.CanWriteIssuesOrPulls(isPull) {
-				ctx.ServerError("CanWriteIssuesOrPulls", fmt.Errorf("No permission for %s", user.Name))
+			if !valid {
+				ctx.ServerError("canBeAssigned", models.ErrUserDoesNotHaveAccessToRepo{UserID: aID, RepoName: repo.Name})
 				return nil, nil, 0
 			}
 		}
@@ -572,7 +574,7 @@ func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) {
 		Content:     form.Content,
 		Ref:         form.Ref,
 	}
-	if err := issue_service.NewIssue(repo, issue, labelIDs, assigneeIDs, attachments); err != nil {
+	if err := issue_service.NewIssue(repo, issue, labelIDs, attachments); err != nil {
 		if models.IsErrUserDoesNotHaveAccessToRepo(err) {
 			ctx.Error(400, "UserDoesNotHaveAccessToRepo", err.Error())
 			return
@@ -581,6 +583,11 @@ func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) {
 		return
 	}
 
+	if err := issue_service.AddAssignees(issue, ctx.User, assigneeIDs); err != nil {
+		log.Error("AddAssignees: %v", err)
+		ctx.Flash.Error(ctx.Tr("issues.assignee.error"))
+	}
+
 	notification.NotifyNewIssue(issue)
 
 	log.Trace("Issue created: %d/%d", repo.ID, issue.ID)
@@ -1044,7 +1051,7 @@ func UpdateIssueTitle(ctx *context.Context) {
 	}
 
 	oldTitle := issue.Title
-	if err := issue.ChangeTitle(ctx.User, title); err != nil {
+	if err := issue_service.ChangeTitle(issue, ctx.User, title); err != nil {
 		ctx.ServerError("ChangeTitle", err)
 		return
 	}
@@ -1074,8 +1081,14 @@ func UpdateIssueContent(ctx *context.Context) {
 		return
 	}
 
+	files := ctx.QueryStrings("files[]")
+	if err := updateAttachments(issue, files); err != nil {
+		ctx.ServerError("UpdateAttachments", err)
+	}
+
 	ctx.JSON(200, map[string]interface{}{
-		"content": string(markdown.Render([]byte(issue.Content), ctx.Query("context"), ctx.Repo.Repository.ComposeMetas())),
+		"content":     string(markdown.Render([]byte(issue.Content), ctx.Query("context"), ctx.Repo.Repository.ComposeMetas())),
+		"attachments": attachmentsHTML(ctx, issue.Attachments),
 	})
 }
 
@@ -1104,7 +1117,7 @@ func UpdateIssueMilestone(ctx *context.Context) {
 	})
 }
 
-// UpdateIssueAssignee change issue's assignee
+// UpdateIssueAssignee change issue's or pull's assignee
 func UpdateIssueAssignee(ctx *context.Context) {
 	issues := getActionIssues(ctx)
 	if ctx.Written() {
@@ -1122,10 +1135,29 @@ func UpdateIssueAssignee(ctx *context.Context) {
 				return
 			}
 		default:
-			if err := issue.ChangeAssignee(ctx.User, assigneeID); err != nil {
-				ctx.ServerError("ChangeAssignee", err)
+			assignee, err := models.GetUserByID(assigneeID)
+			if err != nil {
+				ctx.ServerError("GetUserByID", err)
 				return
 			}
+
+			valid, err := models.CanBeAssigned(assignee, issue.Repo, issue.IsPull)
+			if err != nil {
+				ctx.ServerError("canBeAssigned", err)
+				return
+			}
+			if !valid {
+				ctx.ServerError("canBeAssigned", models.ErrUserDoesNotHaveAccessToRepo{UserID: assigneeID, RepoName: issue.Repo.Name})
+				return
+			}
+
+			removed, comment, err := issue.ToggleAssignee(ctx.User, assigneeID)
+			if err != nil {
+				ctx.ServerError("ToggleAssignee", err)
+				return
+			}
+
+			notification.NotifyIssueChangeAssignee(ctx.User, issue, assignee, removed, comment)
 		}
 	}
 	ctx.JSON(200, map[string]interface{}{
@@ -1325,6 +1357,13 @@ func UpdateCommentContent(ctx *context.Context) {
 		return
 	}
 
+	if comment.Type == models.CommentTypeComment {
+		if err := comment.LoadAttachments(); err != nil {
+			ctx.ServerError("LoadAttachments", err)
+			return
+		}
+	}
+
 	if !ctx.IsSigned || (ctx.User.ID != comment.PosterID && !ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull)) {
 		ctx.Error(403)
 		return
@@ -1346,10 +1385,16 @@ func UpdateCommentContent(ctx *context.Context) {
 		return
 	}
 
+	files := ctx.QueryStrings("files[]")
+	if err := updateAttachments(comment, files); err != nil {
+		ctx.ServerError("UpdateAttachments", err)
+	}
+
 	notification.NotifyUpdateComment(ctx.User, comment, oldContent)
 
 	ctx.JSON(200, map[string]interface{}{
-		"content": string(markdown.Render([]byte(comment.Content), ctx.Query("context"), ctx.Repo.Repository.ComposeMetas())),
+		"content":     string(markdown.Render([]byte(comment.Content), ctx.Query("context"), ctx.Repo.Repository.ComposeMetas())),
+		"attachments": attachmentsHTML(ctx, comment.Attachments),
 	})
 }
 
@@ -1603,3 +1648,88 @@ func filterXRefComments(ctx *context.Context, issue *models.Issue) error {
 	}
 	return nil
 }
+
+// GetIssueAttachments returns attachments for the issue
+func GetIssueAttachments(ctx *context.Context) {
+	issue := GetActionIssue(ctx)
+	var attachments = make([]*api.Attachment, len(issue.Attachments))
+	for i := 0; i < len(issue.Attachments); i++ {
+		attachments[i] = issue.Attachments[i].APIFormat()
+	}
+	ctx.JSON(200, attachments)
+}
+
+// GetCommentAttachments returns attachments for the comment
+func GetCommentAttachments(ctx *context.Context) {
+	comment, err := models.GetCommentByID(ctx.ParamsInt64(":id"))
+	if err != nil {
+		ctx.NotFoundOrServerError("GetCommentByID", models.IsErrCommentNotExist, err)
+		return
+	}
+	var attachments = make([]*api.Attachment, 0)
+	if comment.Type == models.CommentTypeComment {
+		if err := comment.LoadAttachments(); err != nil {
+			ctx.ServerError("LoadAttachments", err)
+			return
+		}
+		for i := 0; i < len(comment.Attachments); i++ {
+			attachments = append(attachments, comment.Attachments[i].APIFormat())
+		}
+	}
+	ctx.JSON(200, attachments)
+}
+
+func updateAttachments(item interface{}, files []string) error {
+	var attachments []*models.Attachment
+	switch content := item.(type) {
+	case *models.Issue:
+		attachments = content.Attachments
+	case *models.Comment:
+		attachments = content.Attachments
+	default:
+		return fmt.Errorf("Unknow Type")
+	}
+	for i := 0; i < len(attachments); i++ {
+		if util.IsStringInSlice(attachments[i].UUID, files) {
+			continue
+		}
+		if err := models.DeleteAttachment(attachments[i], true); err != nil {
+			return err
+		}
+	}
+	var err error
+	if len(files) > 0 {
+		switch content := item.(type) {
+		case *models.Issue:
+			err = content.UpdateAttachments(files)
+		case *models.Comment:
+			err = content.UpdateAttachments(files)
+		default:
+			return fmt.Errorf("Unknow Type")
+		}
+		if err != nil {
+			return err
+		}
+	}
+	switch content := item.(type) {
+	case *models.Issue:
+		content.Attachments, err = models.GetAttachmentsByIssueID(content.ID)
+	case *models.Comment:
+		content.Attachments, err = models.GetAttachmentsByCommentID(content.ID)
+	default:
+		return fmt.Errorf("Unknow Type")
+	}
+	return err
+}
+
+func attachmentsHTML(ctx *context.Context, attachments []*models.Attachment) string {
+	attachHTML, err := ctx.HTMLString(string(tplAttachment), map[string]interface{}{
+		"ctx":         ctx.Data,
+		"Attachments": attachments,
+	})
+	if err != nil {
+		ctx.ServerError("attachmentsHTML.HTMLString", err)
+		return ""
+	}
+	return attachHTML
+}
diff --git a/routers/repo/issue_label.go b/routers/repo/issue_label.go
index 452a0a4c0c..02568f77a6 100644
--- a/routers/repo/issue_label.go
+++ b/routers/repo/issue_label.go
@@ -10,6 +10,7 @@ import (
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/log"
+	issue_service "code.gitea.io/gitea/services/issue"
 )
 
 const (
@@ -132,7 +133,7 @@ func UpdateIssueLabel(ctx *context.Context) {
 	switch action := ctx.Query("action"); action {
 	case "clear":
 		for _, issue := range issues {
-			if err := issue.ClearLabels(ctx.User); err != nil {
+			if err := issue_service.ClearLabels(issue, ctx.User); err != nil {
 				ctx.ServerError("ClearLabels", err)
 				return
 			}
@@ -161,14 +162,14 @@ func UpdateIssueLabel(ctx *context.Context) {
 
 		if action == "attach" {
 			for _, issue := range issues {
-				if err = issue.AddLabel(ctx.User, label); err != nil {
+				if err = issue_service.AddLabel(issue, ctx.User, label); err != nil {
 					ctx.ServerError("AddLabel", err)
 					return
 				}
 			}
 		} else {
 			for _, issue := range issues {
-				if err = issue.RemoveLabel(ctx.User, label); err != nil {
+				if err = issue_service.RemoveLabel(issue, ctx.User, label); err != nil {
 					ctx.ServerError("RemoveLabel", err)
 					return
 				}
diff --git a/routers/repo/pull.go b/routers/repo/pull.go
index 7af01c46ba..23d97e7b7e 100644
--- a/routers/repo/pull.go
+++ b/routers/repo/pull.go
@@ -24,6 +24,7 @@ import (
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/util"
 	"code.gitea.io/gitea/services/gitdiff"
+	issue_service "code.gitea.io/gitea/services/issue"
 	pull_service "code.gitea.io/gitea/services/pull"
 
 	"github.com/unknwon/com"
@@ -242,6 +243,10 @@ func checkPullInfo(ctx *context.Context) *models.Issue {
 		ctx.ServerError("LoadPoster", err)
 		return nil
 	}
+	if err := issue.LoadRepo(); err != nil {
+		ctx.ServerError("LoadRepo", err)
+		return nil
+	}
 	ctx.Data["Title"] = fmt.Sprintf("#%d - %s", issue.Index, issue.Title)
 	ctx.Data["Issue"] = issue
 
@@ -272,12 +277,12 @@ func checkPullInfo(ctx *context.Context) *models.Issue {
 }
 
 func setMergeTarget(ctx *context.Context, pull *models.PullRequest) {
-	if ctx.Repo.Owner.Name == pull.HeadUserName {
+	if ctx.Repo.Owner.Name == pull.MustHeadUserName() {
 		ctx.Data["HeadTarget"] = pull.HeadBranch
 	} else if pull.HeadRepo == nil {
-		ctx.Data["HeadTarget"] = pull.HeadUserName + ":" + pull.HeadBranch
+		ctx.Data["HeadTarget"] = pull.MustHeadUserName() + ":" + pull.HeadBranch
 	} else {
-		ctx.Data["HeadTarget"] = pull.HeadUserName + "/" + pull.HeadRepo.Name + ":" + pull.HeadBranch
+		ctx.Data["HeadTarget"] = pull.MustHeadUserName() + "/" + pull.HeadRepo.Name + ":" + pull.HeadBranch
 	}
 	ctx.Data["BaseTarget"] = pull.BaseBranch
 }
@@ -440,7 +445,7 @@ func ViewPullCommits(ctx *context.Context) {
 			ctx.NotFound("ViewPullCommits", nil)
 			return
 		}
-		ctx.Data["Username"] = pull.HeadUserName
+		ctx.Data["Username"] = pull.MustHeadUserName()
 		ctx.Data["Reponame"] = pull.HeadRepo.Name
 		commits = prInfo.Commits
 	}
@@ -512,7 +517,7 @@ func ViewPullFiles(ctx *context.Context) {
 			return
 		}
 
-		headRepoPath := models.RepoPath(pull.HeadUserName, pull.HeadRepo.Name)
+		headRepoPath := pull.HeadRepo.RepoPath()
 
 		headGitRepo, err := git.OpenRepository(headRepoPath)
 		if err != nil {
@@ -531,8 +536,8 @@ func ViewPullFiles(ctx *context.Context) {
 		endCommitID = headCommitID
 		gitRepo = headGitRepo
 
-		headTarget = path.Join(pull.HeadUserName, pull.HeadRepo.Name)
-		ctx.Data["Username"] = pull.HeadUserName
+		headTarget = path.Join(pull.MustHeadUserName(), pull.HeadRepo.Name)
+		ctx.Data["Username"] = pull.MustHeadUserName()
 		ctx.Data["Reponame"] = pull.HeadRepo.Name
 	}
 
@@ -754,20 +759,19 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm)
 		Content:     form.Content,
 	}
 	pullRequest := &models.PullRequest{
-		HeadRepoID:   headRepo.ID,
-		BaseRepoID:   repo.ID,
-		HeadUserName: headUser.Name,
-		HeadBranch:   headBranch,
-		BaseBranch:   baseBranch,
-		HeadRepo:     headRepo,
-		BaseRepo:     repo,
-		MergeBase:    prInfo.MergeBase,
-		Type:         models.PullRequestGitea,
+		HeadRepoID: headRepo.ID,
+		BaseRepoID: repo.ID,
+		HeadBranch: headBranch,
+		BaseBranch: baseBranch,
+		HeadRepo:   headRepo,
+		BaseRepo:   repo,
+		MergeBase:  prInfo.MergeBase,
+		Type:       models.PullRequestGitea,
 	}
 	// FIXME: check error in the case two people send pull request at almost same time, give nice error prompt
 	// instead of 500.
 
-	if err := pull_service.NewPullRequest(repo, pullIssue, labelIDs, attachments, pullRequest, patch, assigneeIDs); err != nil {
+	if err := pull_service.NewPullRequest(repo, pullIssue, labelIDs, attachments, pullRequest, patch); err != nil {
 		if models.IsErrUserDoesNotHaveAccessToRepo(err) {
 			ctx.Error(400, "UserDoesNotHaveAccessToRepo", err.Error())
 			return
@@ -779,6 +783,11 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm)
 		return
 	}
 
+	if err := issue_service.AddAssignees(pullIssue, ctx.User, assigneeIDs); err != nil {
+		log.Error("AddAssignees: %v", err)
+		ctx.Flash.Error(ctx.Tr("issues.assignee.error"))
+	}
+
 	notification.NotifyNewPullRequest(pullRequest)
 
 	log.Trace("Pull request created: %d/%d", repo.ID, pullIssue.ID)
@@ -820,7 +829,7 @@ func TriggerTask(ctx *context.Context) {
 	log.Trace("TriggerTask '%s/%s' by %s", repo.Name, branch, pusher.Name)
 
 	go models.HookQueue.Add(repo.ID)
-	go models.AddTestPullRequestTask(pusher, repo.ID, branch, true)
+	go pull_service.AddTestPullRequestTask(pusher, repo.ID, branch, true)
 	ctx.Status(202)
 }
 
diff --git a/routers/repo/pull_review.go b/routers/repo/pull_review.go
index 5eb0dfe9a7..7d030988fd 100644
--- a/routers/repo/pull_review.go
+++ b/routers/repo/pull_review.go
@@ -157,7 +157,7 @@ func SubmitReview(ctx *context.Context, form auth.SubmitReviewForm) {
 			return
 		}
 		// No current review. Create a new one!
-		if review, err = models.CreateReview(models.CreateReviewOptions{
+		if review, err = pull_service.CreateReview(models.CreateReviewOptions{
 			Type:     reviewType,
 			Issue:    issue,
 			Reviewer: ctx.User,
@@ -169,7 +169,7 @@ func SubmitReview(ctx *context.Context, form auth.SubmitReviewForm) {
 	} else {
 		review.Content = form.Content
 		review.Type = reviewType
-		if err = models.UpdateReview(review); err != nil {
+		if err = pull_service.UpdateReview(review); err != nil {
 			ctx.ServerError("UpdateReview", err)
 			return
 		}
diff --git a/routers/repo/repo.go b/routers/repo/repo.go
index b67384d721..bfd0c771b0 100644
--- a/routers/repo/repo.go
+++ b/routers/repo/repo.go
@@ -19,6 +19,7 @@ import (
 	"code.gitea.io/gitea/modules/migrations"
 	"code.gitea.io/gitea/modules/notification"
 	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/task"
 	"code.gitea.io/gitea/modules/util"
 
 	"github.com/unknwon/com"
@@ -133,8 +134,6 @@ func Create(ctx *context.Context) {
 
 func handleCreateError(ctx *context.Context, owner *models.User, err error, name string, tpl base.TplName, form interface{}) {
 	switch {
-	case migrations.IsRateLimitError(err):
-		ctx.RenderWithErr(ctx.Tr("form.visit_rate_limit"), tpl, form)
 	case models.IsErrReachLimitOfRepo(err):
 		ctx.RenderWithErr(ctx.Tr("repo.form.reach_limit_of_creation", owner.MaxCreationLimit()), tpl, form)
 	case models.IsErrRepoAlreadyExist(err):
@@ -221,6 +220,40 @@ func Migrate(ctx *context.Context) {
 	ctx.HTML(200, tplMigrate)
 }
 
+func handleMigrateError(ctx *context.Context, owner *models.User, err error, name string, tpl base.TplName, form *auth.MigrateRepoForm) {
+	switch {
+	case migrations.IsRateLimitError(err):
+		ctx.RenderWithErr(ctx.Tr("form.visit_rate_limit"), tpl, form)
+	case migrations.IsTwoFactorAuthError(err):
+		ctx.RenderWithErr(ctx.Tr("form.2fa_auth_required"), tpl, form)
+	case models.IsErrReachLimitOfRepo(err):
+		ctx.RenderWithErr(ctx.Tr("repo.form.reach_limit_of_creation", owner.MaxCreationLimit()), tpl, form)
+	case models.IsErrRepoAlreadyExist(err):
+		ctx.Data["Err_RepoName"] = true
+		ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), tpl, form)
+	case models.IsErrNameReserved(err):
+		ctx.Data["Err_RepoName"] = true
+		ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), tpl, form)
+	case models.IsErrNamePatternNotAllowed(err):
+		ctx.Data["Err_RepoName"] = true
+		ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tpl, form)
+	default:
+		remoteAddr, _ := form.ParseRemoteAddr(owner)
+		err = util.URLSanitizedError(err, remoteAddr)
+		if strings.Contains(err.Error(), "Authentication failed") ||
+			strings.Contains(err.Error(), "Bad credentials") ||
+			strings.Contains(err.Error(), "could not read Username") {
+			ctx.Data["Err_Auth"] = true
+			ctx.RenderWithErr(ctx.Tr("form.auth_failed", err.Error()), tpl, form)
+		} else if strings.Contains(err.Error(), "fatal:") {
+			ctx.Data["Err_CloneAddr"] = true
+			ctx.RenderWithErr(ctx.Tr("repo.migrate.failed", err.Error()), tpl, form)
+		} else {
+			ctx.ServerError(name, err)
+		}
+	}
+}
+
 // MigratePost response for migrating from external git repository
 func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) {
 	ctx.Data["Title"] = ctx.Tr("new_migrate")
@@ -258,8 +291,8 @@ func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) {
 	}
 
 	var opts = migrations.MigrateOptions{
-		RemoteURL:    remoteAddr,
-		Name:         form.RepoName,
+		CloneAddr:    remoteAddr,
+		RepoName:     form.RepoName,
 		Description:  form.Description,
 		Private:      form.Private || setting.Repository.ForcePrivate,
 		Mirror:       form.Mirror,
@@ -282,47 +315,19 @@ func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) {
 		opts.Releases = false
 	}
 
-	repo, err := migrations.MigrateRepository(ctx.User, ctxUser.Name, opts)
-	if err == nil {
-		notification.NotifyCreateRepository(ctx.User, ctxUser, repo)
-
-		log.Trace("Repository migrated [%d]: %s/%s successfully", repo.ID, ctxUser.Name, form.RepoName)
-		ctx.Redirect(setting.AppSubURL + "/" + ctxUser.Name + "/" + form.RepoName)
+	err = models.CheckCreateRepository(ctx.User, ctxUser, opts.RepoName)
+	if err != nil {
+		handleMigrateError(ctx, ctxUser, err, "MigratePost", tplMigrate, &form)
 		return
 	}
 
-	switch {
-	case models.IsErrReachLimitOfRepo(err):
-		ctx.RenderWithErr(ctx.Tr("repo.form.reach_limit_of_creation", ctxUser.MaxCreationLimit()), tplMigrate, &form)
-	case models.IsErrNameReserved(err):
-		ctx.Data["Err_RepoName"] = true
-		ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), tplMigrate, &form)
-	case models.IsErrRepoAlreadyExist(err):
-		ctx.Data["Err_RepoName"] = true
-		ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), tplMigrate, &form)
-	case models.IsErrNamePatternNotAllowed(err):
-		ctx.Data["Err_RepoName"] = true
-		ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tplMigrate, &form)
-	case migrations.IsRateLimitError(err):
-		ctx.RenderWithErr(ctx.Tr("form.visit_rate_limit"), tplMigrate, &form)
-	case migrations.IsTwoFactorAuthError(err):
-		ctx.Data["Err_Auth"] = true
-		ctx.RenderWithErr(ctx.Tr("form.2fa_auth_required"), tplMigrate, &form)
-	default:
-		// remoteAddr may contain credentials, so we sanitize it
-		err = util.URLSanitizedError(err, remoteAddr)
-		if strings.Contains(err.Error(), "Authentication failed") ||
-			strings.Contains(err.Error(), "Bad credentials") ||
-			strings.Contains(err.Error(), "could not read Username") {
-			ctx.Data["Err_Auth"] = true
-			ctx.RenderWithErr(ctx.Tr("form.auth_failed", err.Error()), tplMigrate, &form)
-		} else if strings.Contains(err.Error(), "fatal:") {
-			ctx.Data["Err_CloneAddr"] = true
-			ctx.RenderWithErr(ctx.Tr("repo.migrate.failed", err.Error()), tplMigrate, &form)
-		} else {
-			ctx.ServerError("MigratePost", err)
-		}
+	err = task.MigrateRepository(ctx.User, ctxUser, opts)
+	if err == nil {
+		ctx.Redirect(setting.AppSubURL + "/" + ctxUser.Name + "/" + opts.RepoName)
+		return
 	}
+
+	handleMigrateError(ctx, ctxUser, err, "MigratePost", tplMigrate, &form)
 }
 
 // Action response for actions to a repository
@@ -460,3 +465,19 @@ func Download(ctx *context.Context) {
 
 	ctx.ServeFile(archivePath, ctx.Repo.Repository.Name+"-"+refName+ext)
 }
+
+// Status returns repository's status
+func Status(ctx *context.Context) {
+	task, err := models.GetMigratingTask(ctx.Repo.Repository.ID)
+	if err != nil {
+		ctx.JSON(500, map[string]interface{}{
+			"err": err,
+		})
+		return
+	}
+
+	ctx.JSON(200, map[string]interface{}{
+		"status": ctx.Repo.Repository.Status,
+		"err":    task.Errors,
+	})
+}
diff --git a/routers/repo/setting_protected_branch.go b/routers/repo/setting_protected_branch.go
index 80f44ead99..bc4d7c3a9e 100644
--- a/routers/repo/setting_protected_branch.go
+++ b/routers/repo/setting_protected_branch.go
@@ -117,9 +117,9 @@ func SettingsProtectedBranch(c *context.Context) {
 		}
 	}
 
-	users, err := c.Repo.Repository.GetWriters()
+	users, err := c.Repo.Repository.GetReaders()
 	if err != nil {
-		c.ServerError("Repo.Repository.GetWriters", err)
+		c.ServerError("Repo.Repository.GetReaders", err)
 		return
 	}
 	c.Data["Users"] = users
@@ -213,6 +213,7 @@ func SettingsProtectedBranchPost(ctx *context.Context, f auth.ProtectBranchForm)
 
 		protectBranch.EnableStatusCheck = f.EnableStatusCheck
 		protectBranch.StatusCheckContexts = f.StatusCheckContexts
+		protectBranch.WhitelistDeployKeys = f.WhitelistDeployKeys
 
 		protectBranch.RequiredApprovals = f.RequiredApprovals
 		if strings.TrimSpace(f.ApprovalsWhitelistUsers) != "" {
diff --git a/routers/repo/view.go b/routers/repo/view.go
index 00790a4ef3..a2e431e435 100644
--- a/routers/repo/view.go
+++ b/routers/repo/view.go
@@ -11,6 +11,7 @@ import (
 	"fmt"
 	gotemplate "html/template"
 	"io/ioutil"
+	"net/url"
 	"path"
 	"strings"
 
@@ -31,6 +32,7 @@ const (
 	tplRepoHome  base.TplName = "repo/home"
 	tplWatchers  base.TplName = "repo/watchers"
 	tplForks     base.TplName = "repo/forks"
+	tplMigrating base.TplName = "repo/migrating"
 )
 
 func renderDirectory(ctx *context.Context, treeLink string) {
@@ -304,6 +306,8 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
 
 			var output bytes.Buffer
 			lines := strings.Split(fileContent, "\n")
+			ctx.Data["NumLines"] = len(lines)
+
 			//Remove blank line at the end of file
 			if len(lines) > 0 && lines[len(lines)-1] == "" {
 				lines = lines[:len(lines)-1]
@@ -342,6 +346,20 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
 		ctx.Data["IsAudioFile"] = true
 	case base.IsImageFile(buf):
 		ctx.Data["IsImageFile"] = true
+	default:
+		if fileSize >= setting.UI.MaxDisplayFileSize {
+			ctx.Data["IsFileTooLarge"] = true
+			break
+		}
+
+		if markupType := markup.Type(blob.Name()); markupType != "" {
+			d, _ := ioutil.ReadAll(dataRc)
+			buf = append(buf, d...)
+			ctx.Data["IsMarkup"] = true
+			ctx.Data["MarkupType"] = markupType
+			ctx.Data["FileContent"] = string(markup.Render(blob.Name(), buf, path.Dir(treeLink), ctx.Repo.Repository.ComposeMetas()))
+		}
+
 	}
 
 	if ctx.Repo.CanEnableEditor() {
@@ -354,9 +372,37 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry, treeLink, rawLink st
 	}
 }
 
+func safeURL(address string) string {
+	u, err := url.Parse(address)
+	if err != nil {
+		return address
+	}
+	u.User = nil
+	return u.String()
+}
+
 // Home render repository home page
 func Home(ctx *context.Context) {
 	if len(ctx.Repo.Units) > 0 {
+		if ctx.Repo.Repository.IsBeingCreated() {
+			task, err := models.GetMigratingTask(ctx.Repo.Repository.ID)
+			if err != nil {
+				ctx.ServerError("models.GetMigratingTask", err)
+				return
+			}
+			cfg, err := task.MigrateConfig()
+			if err != nil {
+				ctx.ServerError("task.MigrateConfig", err)
+				return
+			}
+
+			ctx.Data["Repo"] = ctx.Repo
+			ctx.Data["MigrateTask"] = task
+			ctx.Data["CloneAddr"] = safeURL(cfg.CloneAddr)
+			ctx.HTML(200, tplMigrating)
+			return
+		}
+
 		var firstUnit *models.Unit
 		for _, repoUnit := range ctx.Repo.Units {
 			if repoUnit.Type == models.UnitTypeCode {
diff --git a/routers/repo/wiki.go b/routers/repo/wiki.go
index bf8ac658ae..02fbe4a1dd 100644
--- a/routers/repo/wiki.go
+++ b/routers/repo/wiki.go
@@ -8,6 +8,7 @@ package repo
 import (
 	"fmt"
 	"io/ioutil"
+	"net/url"
 	"path/filepath"
 	"strings"
 
@@ -68,11 +69,22 @@ func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error)
 	if err != nil {
 		return nil, err
 	}
+	// The longest name should be checked first
 	for _, entry := range entries {
 		if entry.IsRegular() && entry.Name() == target {
 			return entry, nil
 		}
 	}
+	// Then the unescaped, shortest alternative
+	var unescapedTarget string
+	if unescapedTarget, err = url.QueryUnescape(target); err != nil {
+		return nil, err
+	}
+	for _, entry := range entries {
+		if entry.IsRegular() && entry.Name() == unescapedTarget {
+			return entry, nil
+		}
+	}
 	return nil, nil
 }
 
diff --git a/routers/routes/routes.go b/routers/routes/routes.go
index 11f2029226..9572ea8039 100644
--- a/routers/routes/routes.go
+++ b/routers/routes/routes.go
@@ -139,14 +139,14 @@ func NewMacaron() *macaron.Macaron {
 	m.Use(public.Custom(
 		&public.Options{
 			SkipLogging:  setting.DisableRouterLog,
-			ExpiresAfter: time.Hour * 6,
+			ExpiresAfter: setting.StaticCacheTime,
 		},
 	))
 	m.Use(public.Static(
 		&public.Options{
 			Directory:    path.Join(setting.StaticRootPath, "public"),
 			SkipLogging:  setting.DisableRouterLog,
-			ExpiresAfter: time.Hour * 6,
+			ExpiresAfter: setting.StaticCacheTime,
 		},
 	))
 	m.Use(public.StaticHandler(
@@ -154,7 +154,7 @@ func NewMacaron() *macaron.Macaron {
 		&public.Options{
 			Prefix:       "avatars",
 			SkipLogging:  setting.DisableRouterLog,
-			ExpiresAfter: time.Hour * 6,
+			ExpiresAfter: setting.StaticCacheTime,
 		},
 	))
 	m.Use(public.StaticHandler(
@@ -162,7 +162,7 @@ func NewMacaron() *macaron.Macaron {
 		&public.Options{
 			Prefix:       "repo-avatars",
 			SkipLogging:  setting.DisableRouterLog,
-			ExpiresAfter: time.Hour * 6,
+			ExpiresAfter: setting.StaticCacheTime,
 		},
 	))
 
@@ -513,8 +513,9 @@ func RegisterRoutes(m *macaron.Macaron) {
 		})
 	}, ignSignIn)
 
-	m.Group("", func() {
-		m.Post("/attachments", repo.UploadAttachment)
+	m.Group("/attachments", func() {
+		m.Post("", repo.UploadAttachment)
+		m.Post("/delete", repo.DeleteAttachment)
 	}, reqSignIn)
 
 	m.Group("/:username", func() {
@@ -710,6 +711,7 @@ func RegisterRoutes(m *macaron.Macaron) {
 				m.Post("/reactions/:action", bindIgnErr(auth.ReactionForm{}), repo.ChangeIssueReaction)
 				m.Post("/lock", reqRepoIssueWriter, bindIgnErr(auth.IssueLockForm{}), repo.LockIssue)
 				m.Post("/unlock", reqRepoIssueWriter, repo.UnlockIssue)
+				m.Get("/attachments", repo.GetIssueAttachments)
 			}, context.RepoMustNotBeArchived())
 
 			m.Post("/labels", reqRepoIssuesOrPullsWriter, repo.UpdateIssueLabel)
@@ -721,6 +723,7 @@ func RegisterRoutes(m *macaron.Macaron) {
 			m.Post("", repo.UpdateCommentContent)
 			m.Post("/delete", repo.DeleteComment)
 			m.Post("/reactions/:action", bindIgnErr(auth.ReactionForm{}), repo.ChangeCommentReaction)
+			m.Get("/attachments", repo.GetCommentAttachments)
 		}, context.RepoMustNotBeArchived())
 		m.Group("/labels", func() {
 			m.Post("/new", bindIgnErr(auth.CreateLabelForm{}), repo.NewLabel)
@@ -845,6 +848,8 @@ func RegisterRoutes(m *macaron.Macaron) {
 
 		m.Get("/archive/*", repo.MustBeNotEmpty, reqRepoCodeReader, repo.Download)
 
+		m.Get("/status", reqRepoCodeReader, repo.Status)
+
 		m.Group("/branches", func() {
 			m.Get("", repo.Branches)
 		}, repo.MustBeNotEmpty, context.RepoRef(), reqRepoCodeReader)
diff --git a/routers/user/auth.go b/routers/user/auth.go
index 3def867f64..82a508e4dc 100644
--- a/routers/user/auth.go
+++ b/routers/user/auth.go
@@ -17,10 +17,12 @@ import (
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/password"
 	"code.gitea.io/gitea/modules/recaptcha"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/timeutil"
 	"code.gitea.io/gitea/modules/util"
+	"code.gitea.io/gitea/services/externalaccount"
 	"code.gitea.io/gitea/services/mailer"
 
 	"gitea.com/macaron/captcha"
@@ -277,7 +279,7 @@ func TwoFactorPost(ctx *context.Context, form auth.TwoFactorAuthForm) {
 				return
 			}
 
-			err = models.LinkAccountToUser(u, gothUser.(goth.User))
+			err = externalaccount.LinkAccountToUser(u, gothUser.(goth.User))
 			if err != nil {
 				ctx.ServerError("UserSignIn", err)
 				return
@@ -452,7 +454,7 @@ func U2FSign(ctx *context.Context, signResp u2f.SignResponse) {
 					return
 				}
 
-				err = models.LinkAccountToUser(user, gothUser.(goth.User))
+				err = externalaccount.LinkAccountToUser(user, gothUser.(goth.User))
 				if err != nil {
 					ctx.ServerError("UserSignIn", err)
 					return
@@ -601,36 +603,42 @@ func handleOAuth2SignIn(u *models.User, gothUser goth.User, ctx *context.Context
 	// Instead, redirect them to the 2FA authentication page.
 	_, err = models.GetTwoFactorByUID(u.ID)
 	if err != nil {
-		if models.IsErrTwoFactorNotEnrolled(err) {
-			err = ctx.Session.Set("uid", u.ID)
-			if err != nil {
-				log.Error(fmt.Sprintf("Error setting session: %v", err))
-			}
-			err = ctx.Session.Set("uname", u.Name)
-			if err != nil {
-				log.Error(fmt.Sprintf("Error setting session: %v", err))
-			}
-
-			// Clear whatever CSRF has right now, force to generate a new one
-			ctx.SetCookie(setting.CSRFCookieName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true)
-
-			// Register last login
-			u.SetLastLogin()
-			if err := models.UpdateUserCols(u, "last_login_unix"); err != nil {
-				ctx.ServerError("UpdateUserCols", err)
-				return
-			}
-
-			if redirectTo := ctx.GetCookie("redirect_to"); len(redirectTo) > 0 {
-				ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL, "", setting.SessionConfig.Secure, true)
-				ctx.RedirectToFirst(redirectTo)
-				return
-			}
-
-			ctx.Redirect(setting.AppSubURL + "/")
-		} else {
+		if !models.IsErrTwoFactorNotEnrolled(err) {
 			ctx.ServerError("UserSignIn", err)
+			return
 		}
+
+		err = ctx.Session.Set("uid", u.ID)
+		if err != nil {
+			log.Error(fmt.Sprintf("Error setting session: %v", err))
+		}
+		err = ctx.Session.Set("uname", u.Name)
+		if err != nil {
+			log.Error(fmt.Sprintf("Error setting session: %v", err))
+		}
+
+		// Clear whatever CSRF has right now, force to generate a new one
+		ctx.SetCookie(setting.CSRFCookieName, "", -1, setting.AppSubURL, setting.SessionConfig.Domain, setting.SessionConfig.Secure, true)
+
+		// Register last login
+		u.SetLastLogin()
+		if err := models.UpdateUserCols(u, "last_login_unix"); err != nil {
+			ctx.ServerError("UpdateUserCols", err)
+			return
+		}
+
+		// update external user information
+		if err := models.UpdateExternalUser(u, gothUser); err != nil {
+			log.Error("UpdateExternalUser failed: %v", err)
+		}
+
+		if redirectTo := ctx.GetCookie("redirect_to"); len(redirectTo) > 0 {
+			ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL, "", setting.SessionConfig.Secure, true)
+			ctx.RedirectToFirst(redirectTo)
+			return
+		}
+
+		ctx.Redirect(setting.AppSubURL + "/")
 		return
 	}
 
@@ -675,7 +683,7 @@ func oAuth2UserLoginCallback(loginSource *models.LoginSource, request *http.Requ
 	}
 
 	if hasUser {
-		return user, goth.User{}, nil
+		return user, gothUser, nil
 	}
 
 	// search in external linked users
@@ -689,7 +697,7 @@ func oAuth2UserLoginCallback(loginSource *models.LoginSource, request *http.Requ
 	}
 	if hasUser {
 		user, err = models.GetUserByID(externalLoginUser.UserID)
-		return user, goth.User{}, err
+		return user, gothUser, err
 	}
 
 	// no user found to login
@@ -789,16 +797,18 @@ func LinkAccountPostSignIn(ctx *context.Context, signInForm auth.SignInForm) {
 	// Instead, redirect them to the 2FA authentication page.
 	_, err = models.GetTwoFactorByUID(u.ID)
 	if err != nil {
-		if models.IsErrTwoFactorNotEnrolled(err) {
-			err = models.LinkAccountToUser(u, gothUser.(goth.User))
-			if err != nil {
-				ctx.ServerError("UserLinkAccount", err)
-			} else {
-				handleSignIn(ctx, u, signInForm.Remember)
-			}
-		} else {
+		if !models.IsErrTwoFactorNotEnrolled(err) {
 			ctx.ServerError("UserLinkAccount", err)
+			return
 		}
+
+		err = externalaccount.LinkAccountToUser(u, gothUser.(goth.User))
+		if err != nil {
+			ctx.ServerError("UserLinkAccount", err)
+			return
+		}
+
+		handleSignIn(ctx, u, signInForm.Remember)
 		return
 	}
 
@@ -947,6 +957,11 @@ func LinkAccountPostRegister(ctx *context.Context, cpt *captcha.Captcha, form au
 		}
 	}
 
+	// update external user information
+	if err := models.UpdateExternalUser(u, gothUser.(goth.User)); err != nil {
+		log.Error("UpdateExternalUser failed: %v", err)
+	}
+
 	// Send confirmation email
 	if setting.Service.RegisterEmailConfirm && u.ID > 1 {
 		mailer.SendActivateAccountMail(ctx.Locale, u)
@@ -1320,6 +1335,11 @@ func ResetPasswdPost(ctx *context.Context) {
 		ctx.Data["Err_Password"] = true
 		ctx.RenderWithErr(ctx.Tr("auth.password_too_short", setting.MinPasswordLength), tplResetPassword, nil)
 		return
+	} else if !password.IsComplexEnough(passwd) {
+		ctx.Data["IsResetForm"] = true
+		ctx.Data["Err_Password"] = true
+		ctx.RenderWithErr(ctx.Tr("form.password_complexity"), tplResetPassword, nil)
+		return
 	}
 
 	var err error
@@ -1350,7 +1370,6 @@ func ResetPasswdPost(ctx *context.Context) {
 func MustChangePassword(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("auth.must_change_password")
 	ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/settings/change_password"
-
 	ctx.HTML(200, tplMustChangePassword)
 }
 
@@ -1358,16 +1377,12 @@ func MustChangePassword(ctx *context.Context) {
 // account was created by an admin
 func MustChangePasswordPost(ctx *context.Context, cpt *captcha.Captcha, form auth.MustChangePasswordForm) {
 	ctx.Data["Title"] = ctx.Tr("auth.must_change_password")
-
 	ctx.Data["ChangePasscodeLink"] = setting.AppSubURL + "/user/settings/change_password"
-
 	if ctx.HasError() {
 		ctx.HTML(200, tplMustChangePassword)
 		return
 	}
-
 	u := ctx.User
-
 	// Make sure only requests for users who are eligible to change their password via
 	// this method passes through
 	if !u.MustChangePassword {
diff --git a/routers/user/setting/account.go b/routers/user/setting/account.go
index 71d98fd3b9..73799c8bd7 100644
--- a/routers/user/setting/account.go
+++ b/routers/user/setting/account.go
@@ -13,6 +13,7 @@ import (
 	"code.gitea.io/gitea/modules/base"
 	"code.gitea.io/gitea/modules/context"
 	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/password"
 	"code.gitea.io/gitea/modules/setting"
 	"code.gitea.io/gitea/modules/timeutil"
 	"code.gitea.io/gitea/services/mailer"
@@ -27,7 +28,6 @@ func Account(ctx *context.Context) {
 	ctx.Data["Title"] = ctx.Tr("settings")
 	ctx.Data["PageIsSettingsAccount"] = true
 	ctx.Data["Email"] = ctx.User.Email
-	ctx.Data["EmailNotificationsPreference"] = ctx.User.EmailNotifications()
 
 	loadAccountData(ctx)
 
@@ -52,6 +52,8 @@ func AccountPost(ctx *context.Context, form auth.ChangePasswordForm) {
 		ctx.Flash.Error(ctx.Tr("settings.password_incorrect"))
 	} else if form.Password != form.Retype {
 		ctx.Flash.Error(ctx.Tr("form.password_not_match"))
+	} else if !password.IsComplexEnough(form.Password) {
+		ctx.Flash.Error(ctx.Tr("form.password_complexity"))
 	} else {
 		var err error
 		if ctx.User.Salt, err = models.GetUserSalt(); err != nil {
@@ -227,4 +229,5 @@ func loadAccountData(ctx *context.Context) {
 		return
 	}
 	ctx.Data["Emails"] = emails
+	ctx.Data["EmailNotificationsPreference"] = ctx.User.EmailNotifications()
 }
diff --git a/routers/user/setting/account_test.go b/routers/user/setting/account_test.go
index 59fbda1569..41783e19d7 100644
--- a/routers/user/setting/account_test.go
+++ b/routers/user/setting/account_test.go
@@ -19,36 +19,65 @@ import (
 func TestChangePassword(t *testing.T) {
 	oldPassword := "password"
 	setting.MinPasswordLength = 6
+	var pcALL = []string{"lower", "upper", "digit", "spec"}
+	var pcLUN = []string{"lower", "upper", "digit"}
+	var pcLU = []string{"lower", "upper"}
 
 	for _, req := range []struct {
-		OldPassword string
-		NewPassword string
-		Retype      string
-		Message     string
+		OldPassword        string
+		NewPassword        string
+		Retype             string
+		Message            string
+		PasswordComplexity []string
 	}{
 		{
-			OldPassword: oldPassword,
-			NewPassword: "123456",
-			Retype:      "123456",
-			Message:     "",
+			OldPassword:        oldPassword,
+			NewPassword:        "Qwerty123456-",
+			Retype:             "Qwerty123456-",
+			Message:            "",
+			PasswordComplexity: pcALL,
 		},
 		{
-			OldPassword: oldPassword,
-			NewPassword: "12345",
-			Retype:      "12345",
-			Message:     "auth.password_too_short",
+			OldPassword:        oldPassword,
+			NewPassword:        "12345",
+			Retype:             "12345",
+			Message:            "auth.password_too_short",
+			PasswordComplexity: pcALL,
 		},
 		{
-			OldPassword: "12334",
-			NewPassword: "123456",
-			Retype:      "123456",
-			Message:     "settings.password_incorrect",
+			OldPassword:        "12334",
+			NewPassword:        "123456",
+			Retype:             "123456",
+			Message:            "settings.password_incorrect",
+			PasswordComplexity: pcALL,
 		},
 		{
-			OldPassword: oldPassword,
-			NewPassword: "123456",
-			Retype:      "12345",
-			Message:     "form.password_not_match",
+			OldPassword:        oldPassword,
+			NewPassword:        "123456",
+			Retype:             "12345",
+			Message:            "form.password_not_match",
+			PasswordComplexity: pcALL,
+		},
+		{
+			OldPassword:        oldPassword,
+			NewPassword:        "Qwerty",
+			Retype:             "Qwerty",
+			Message:            "form.password_complexity",
+			PasswordComplexity: pcALL,
+		},
+		{
+			OldPassword:        oldPassword,
+			NewPassword:        "Qwerty",
+			Retype:             "Qwerty",
+			Message:            "form.password_complexity",
+			PasswordComplexity: pcLUN,
+		},
+		{
+			OldPassword:        oldPassword,
+			NewPassword:        "QWERTY",
+			Retype:             "QWERTY",
+			Message:            "form.password_complexity",
+			PasswordComplexity: pcLU,
 		},
 	} {
 		models.PrepareTestEnv(t)
diff --git a/services/comments/comments.go b/services/comments/comments.go
index e8448e9065..010c0aaac7 100644
--- a/services/comments/comments.go
+++ b/services/comments/comments.go
@@ -38,6 +38,7 @@ func CreateIssueComment(doer *models.User, repo *models.Repository, issue *model
 		Comment:    comment.APIFormat(),
 		Repository: repo.APIFormat(mode),
 		Sender:     doer.APIFormat(),
+		IsPull:     issue.IsPull,
 	}); err != nil {
 		log.Error("PrepareWebhooks [comment_id: %d]: %v", comment.ID, err)
 	} else {
@@ -128,6 +129,7 @@ func UpdateComment(c *models.Comment, doer *models.User, oldContent string) erro
 		},
 		Repository: c.Issue.Repo.APIFormat(mode),
 		Sender:     doer.APIFormat(),
+		IsPull:     c.Issue.IsPull,
 	}); err != nil {
 		log.Error("PrepareWebhooks [comment_id: %d]: %v", c.ID, err)
 	} else {
@@ -162,6 +164,7 @@ func DeleteComment(comment *models.Comment, doer *models.User) error {
 		Comment:    comment.APIFormat(),
 		Repository: comment.Issue.Repo.APIFormat(mode),
 		Sender:     doer.APIFormat(),
+		IsPull:     comment.Issue.IsPull,
 	}); err != nil {
 		log.Error("PrepareWebhooks [comment_id: %d]: %v", comment.ID, err)
 	} else {
diff --git a/services/externalaccount/user.go b/services/externalaccount/user.go
new file mode 100644
index 0000000000..45773fdb12
--- /dev/null
+++ b/services/externalaccount/user.go
@@ -0,0 +1,62 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package externalaccount
+
+import (
+	"strings"
+
+	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/modules/structs"
+
+	"github.com/markbates/goth"
+)
+
+// LinkAccountToUser link the gothUser to the user
+func LinkAccountToUser(user *models.User, gothUser goth.User) error {
+	loginSource, err := models.GetActiveOAuth2LoginSourceByName(gothUser.Provider)
+	if err != nil {
+		return err
+	}
+
+	externalLoginUser := &models.ExternalLoginUser{
+		ExternalID:        gothUser.UserID,
+		UserID:            user.ID,
+		LoginSourceID:     loginSource.ID,
+		RawData:           gothUser.RawData,
+		Provider:          gothUser.Provider,
+		Email:             gothUser.Email,
+		Name:              gothUser.Name,
+		FirstName:         gothUser.FirstName,
+		LastName:          gothUser.LastName,
+		NickName:          gothUser.NickName,
+		Description:       gothUser.Description,
+		AvatarURL:         gothUser.AvatarURL,
+		Location:          gothUser.Location,
+		AccessToken:       gothUser.AccessToken,
+		AccessTokenSecret: gothUser.AccessTokenSecret,
+		RefreshToken:      gothUser.RefreshToken,
+		ExpiresAt:         gothUser.ExpiresAt,
+	}
+
+	if err := models.LinkExternalToUser(user, externalLoginUser); err != nil {
+		return err
+	}
+
+	externalID := externalLoginUser.ExternalID
+
+	var tp structs.GitServiceType
+	for _, s := range structs.SupportedFullGitService {
+		if strings.EqualFold(s.Name(), gothUser.Provider) {
+			tp = s
+			break
+		}
+	}
+
+	if tp.Name() != "" {
+		return models.UpdateMigrationsByType(tp, externalID, user.ID)
+	}
+
+	return nil
+}
diff --git a/services/issue/issue.go b/services/issue/issue.go
index 5afdfc9901..a5f725ab70 100644
--- a/services/issue/issue.go
+++ b/services/issue/issue.go
@@ -9,12 +9,13 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/notification"
 	api "code.gitea.io/gitea/modules/structs"
 )
 
 // NewIssue creates new issue with labels for repository.
-func NewIssue(repo *models.Repository, issue *models.Issue, labelIDs []int64, assigneeIDs []int64, uuids []string) error {
-	if err := models.NewIssue(repo, issue, labelIDs, assigneeIDs, uuids); err != nil {
+func NewIssue(repo *models.Repository, issue *models.Issue, labelIDs []int64, uuids []string) error {
+	if err := models.NewIssue(repo, issue, labelIDs, uuids); err != nil {
 		return err
 	}
 
@@ -45,3 +46,155 @@ func NewIssue(repo *models.Repository, issue *models.Issue, labelIDs []int64, as
 
 	return nil
 }
+
+// ChangeTitle changes the title of this issue, as the given user.
+func ChangeTitle(issue *models.Issue, doer *models.User, title string) (err error) {
+	oldTitle := issue.Title
+	issue.Title = title
+
+	if err = issue.ChangeTitle(doer, oldTitle); err != nil {
+		return
+	}
+
+	mode, _ := models.AccessLevel(issue.Poster, issue.Repo)
+	if issue.IsPull {
+		if err = issue.LoadPullRequest(); err != nil {
+			return fmt.Errorf("loadPullRequest: %v", err)
+		}
+		issue.PullRequest.Issue = issue
+		err = models.PrepareWebhooks(issue.Repo, models.HookEventPullRequest, &api.PullRequestPayload{
+			Action: api.HookIssueEdited,
+			Index:  issue.Index,
+			Changes: &api.ChangesPayload{
+				Title: &api.ChangesFromPayload{
+					From: oldTitle,
+				},
+			},
+			PullRequest: issue.PullRequest.APIFormat(),
+			Repository:  issue.Repo.APIFormat(mode),
+			Sender:      doer.APIFormat(),
+		})
+	} else {
+		err = models.PrepareWebhooks(issue.Repo, models.HookEventIssues, &api.IssuePayload{
+			Action: api.HookIssueEdited,
+			Index:  issue.Index,
+			Changes: &api.ChangesPayload{
+				Title: &api.ChangesFromPayload{
+					From: oldTitle,
+				},
+			},
+			Issue:      issue.APIFormat(),
+			Repository: issue.Repo.APIFormat(mode),
+			Sender:     issue.Poster.APIFormat(),
+		})
+	}
+
+	if err != nil {
+		log.Error("PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err)
+	} else {
+		go models.HookQueue.Add(issue.RepoID)
+	}
+
+	return nil
+}
+
+// UpdateAssignees is a helper function to add or delete one or multiple issue assignee(s)
+// Deleting is done the GitHub way (quote from their api documentation):
+// https://developer.github.com/v3/issues/#edit-an-issue
+// "assignees" (array): Logins for Users to assign to this issue.
+// Pass one or more user logins to replace the set of assignees on this Issue.
+// Send an empty array ([]) to clear all assignees from the Issue.
+func UpdateAssignees(issue *models.Issue, oneAssignee string, multipleAssignees []string, doer *models.User) (err error) {
+	var allNewAssignees []*models.User
+
+	// Keep the old assignee thingy for compatibility reasons
+	if oneAssignee != "" {
+		// Prevent double adding assignees
+		var isDouble bool
+		for _, assignee := range multipleAssignees {
+			if assignee == oneAssignee {
+				isDouble = true
+				break
+			}
+		}
+
+		if !isDouble {
+			multipleAssignees = append(multipleAssignees, oneAssignee)
+		}
+	}
+
+	// Loop through all assignees to add them
+	for _, assigneeName := range multipleAssignees {
+		assignee, err := models.GetUserByName(assigneeName)
+		if err != nil {
+			return err
+		}
+
+		allNewAssignees = append(allNewAssignees, assignee)
+	}
+
+	// Delete all old assignees not passed
+	if err = models.DeleteNotPassedAssignee(issue, doer, allNewAssignees); err != nil {
+		return err
+	}
+
+	// Add all new assignees
+	// Update the assignee. The function will check if the user exists, is already
+	// assigned (which he shouldn't as we deleted all assignees before) and
+	// has access to the repo.
+	for _, assignee := range allNewAssignees {
+		// Extra method to prevent double adding (which would result in removing)
+		err = AddAssigneeIfNotAssigned(issue, doer, assignee.ID)
+		if err != nil {
+			return err
+		}
+	}
+
+	return
+}
+
+// AddAssigneeIfNotAssigned adds an assignee only if he isn't already assigned to the issue.
+// Also checks for access of assigned user
+func AddAssigneeIfNotAssigned(issue *models.Issue, doer *models.User, assigneeID int64) (err error) {
+	assignee, err := models.GetUserByID(assigneeID)
+	if err != nil {
+		return err
+	}
+
+	// Check if the user is already assigned
+	isAssigned, err := models.IsUserAssignedToIssue(issue, assignee)
+	if err != nil {
+		return err
+	}
+	if isAssigned {
+		// nothing to to
+		return nil
+	}
+
+	valid, err := models.CanBeAssigned(assignee, issue.Repo, issue.IsPull)
+	if err != nil {
+		return err
+	}
+	if !valid {
+		return models.ErrUserDoesNotHaveAccessToRepo{UserID: assigneeID, RepoName: issue.Repo.Name}
+	}
+
+	removed, comment, err := issue.ToggleAssignee(doer, assigneeID)
+	if err != nil {
+		return err
+	}
+
+	notification.NotifyIssueChangeAssignee(doer, issue, assignee, removed, comment)
+
+	return nil
+}
+
+// AddAssignees adds a list of assignes (from IDs) to an issue
+func AddAssignees(issue *models.Issue, doer *models.User, assigneeIDs []int64) (err error) {
+	for _, assigneeID := range assigneeIDs {
+		if err = AddAssigneeIfNotAssigned(issue, doer, assigneeID); err != nil {
+			return err
+		}
+	}
+	return nil
+}
diff --git a/services/issue/label.go b/services/issue/label.go
new file mode 100644
index 0000000000..b393e5d38c
--- /dev/null
+++ b/services/issue/label.go
@@ -0,0 +1,111 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package issue
+
+import (
+	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/modules/log"
+	"code.gitea.io/gitea/modules/notification"
+	api "code.gitea.io/gitea/modules/structs"
+)
+
+func sendLabelUpdatedWebhook(issue *models.Issue, doer *models.User) {
+	var err error
+
+	if err = issue.LoadRepo(); err != nil {
+		log.Error("LoadRepo: %v", err)
+		return
+	}
+
+	if err = issue.LoadPoster(); err != nil {
+		log.Error("LoadPoster: %v", err)
+		return
+	}
+
+	mode, _ := models.AccessLevel(issue.Poster, issue.Repo)
+	if issue.IsPull {
+		if err = issue.LoadPullRequest(); err != nil {
+			log.Error("loadPullRequest: %v", err)
+			return
+		}
+		if err = issue.PullRequest.LoadIssue(); err != nil {
+			log.Error("LoadIssue: %v", err)
+			return
+		}
+		err = models.PrepareWebhooks(issue.Repo, models.HookEventPullRequest, &api.PullRequestPayload{
+			Action:      api.HookIssueLabelUpdated,
+			Index:       issue.Index,
+			PullRequest: issue.PullRequest.APIFormat(),
+			Repository:  issue.Repo.APIFormat(models.AccessModeNone),
+			Sender:      doer.APIFormat(),
+		})
+	} else {
+		err = models.PrepareWebhooks(issue.Repo, models.HookEventIssues, &api.IssuePayload{
+			Action:     api.HookIssueLabelUpdated,
+			Index:      issue.Index,
+			Issue:      issue.APIFormat(),
+			Repository: issue.Repo.APIFormat(mode),
+			Sender:     doer.APIFormat(),
+		})
+	}
+	if err != nil {
+		log.Error("PrepareWebhooks [is_pull: %v]: %v", issue.IsPull, err)
+	} else {
+		go models.HookQueue.Add(issue.RepoID)
+	}
+}
+
+// ClearLabels clears all of an issue's labels
+func ClearLabels(issue *models.Issue, doer *models.User) (err error) {
+	if err = issue.ClearLabels(doer); err != nil {
+		return
+	}
+
+	notification.NotifyIssueClearLabels(doer, issue)
+
+	return nil
+}
+
+// AddLabel adds a new label to the issue.
+func AddLabel(issue *models.Issue, doer *models.User, label *models.Label) error {
+	if err := models.NewIssueLabel(issue, label, doer); err != nil {
+		return err
+	}
+
+	sendLabelUpdatedWebhook(issue, doer)
+	return nil
+}
+
+// AddLabels adds a list of new labels to the issue.
+func AddLabels(issue *models.Issue, doer *models.User, labels []*models.Label) error {
+	if err := models.NewIssueLabels(issue, labels, doer); err != nil {
+		return err
+	}
+
+	sendLabelUpdatedWebhook(issue, doer)
+	return nil
+}
+
+// RemoveLabel removes a label from issue by given ID.
+func RemoveLabel(issue *models.Issue, doer *models.User, label *models.Label) error {
+	if err := issue.LoadRepo(); err != nil {
+		return err
+	}
+
+	perm, err := models.GetUserRepoPermission(issue.Repo, doer)
+	if err != nil {
+		return err
+	}
+	if !perm.CanWriteIssuesOrPulls(issue.IsPull) {
+		return models.ErrLabelNotExist{}
+	}
+
+	if err := models.DeleteIssueLabel(issue, label, doer); err != nil {
+		return err
+	}
+
+	sendLabelUpdatedWebhook(issue, doer)
+	return nil
+}
diff --git a/services/issue/label_test.go b/services/issue/label_test.go
new file mode 100644
index 0000000000..0652028943
--- /dev/null
+++ b/services/issue/label_test.go
@@ -0,0 +1,59 @@
+// Copyright 2017 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package issue
+
+import (
+	"testing"
+
+	"code.gitea.io/gitea/models"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestIssue_AddLabels(t *testing.T) {
+	var tests = []struct {
+		issueID  int64
+		labelIDs []int64
+		doerID   int64
+	}{
+		{1, []int64{1, 2}, 2}, // non-pull-request
+		{1, []int64{}, 2},     // non-pull-request, empty
+		{2, []int64{1, 2}, 2}, // pull-request
+		{2, []int64{}, 1},     // pull-request, empty
+	}
+	for _, test := range tests {
+		assert.NoError(t, models.PrepareTestDatabase())
+		issue := models.AssertExistsAndLoadBean(t, &models.Issue{ID: test.issueID}).(*models.Issue)
+		labels := make([]*models.Label, len(test.labelIDs))
+		for i, labelID := range test.labelIDs {
+			labels[i] = models.AssertExistsAndLoadBean(t, &models.Label{ID: labelID}).(*models.Label)
+		}
+		doer := models.AssertExistsAndLoadBean(t, &models.User{ID: test.doerID}).(*models.User)
+		assert.NoError(t, AddLabels(issue, doer, labels))
+		for _, labelID := range test.labelIDs {
+			models.AssertExistsAndLoadBean(t, &models.IssueLabel{IssueID: test.issueID, LabelID: labelID})
+		}
+	}
+}
+
+func TestIssue_AddLabel(t *testing.T) {
+	var tests = []struct {
+		issueID int64
+		labelID int64
+		doerID  int64
+	}{
+		{1, 2, 2}, // non-pull-request, not-already-added label
+		{1, 1, 2}, // non-pull-request, already-added label
+		{2, 2, 2}, // pull-request, not-already-added label
+		{2, 1, 2}, // pull-request, already-added label
+	}
+	for _, test := range tests {
+		assert.NoError(t, models.PrepareTestDatabase())
+		issue := models.AssertExistsAndLoadBean(t, &models.Issue{ID: test.issueID}).(*models.Issue)
+		label := models.AssertExistsAndLoadBean(t, &models.Label{ID: test.labelID}).(*models.Label)
+		doer := models.AssertExistsAndLoadBean(t, &models.User{ID: test.doerID}).(*models.User)
+		assert.NoError(t, AddLabel(issue, doer, label))
+		models.AssertExistsAndLoadBean(t, &models.IssueLabel{IssueID: test.issueID, LabelID: test.labelID})
+	}
+}
diff --git a/services/issue/main_test.go b/services/issue/main_test.go
new file mode 100644
index 0000000000..b056678a42
--- /dev/null
+++ b/services/issue/main_test.go
@@ -0,0 +1,16 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package issue
+
+import (
+	"path/filepath"
+	"testing"
+
+	"code.gitea.io/gitea/models"
+)
+
+func TestMain(m *testing.M) {
+	models.MainTest(m, filepath.Join("..", ".."))
+}
diff --git a/services/mailer/mail.go b/services/mailer/mail.go
index 3f0a789dc4..bc2aff7314 100644
--- a/services/mailer/mail.go
+++ b/services/mailer/mail.go
@@ -28,8 +28,9 @@ const (
 	mailAuthResetPassword  base.TplName = "auth/reset_passwd"
 	mailAuthRegisterNotify base.TplName = "auth/register_notify"
 
-	mailIssueComment base.TplName = "issue/comment"
-	mailIssueMention base.TplName = "issue/mention"
+	mailIssueComment  base.TplName = "issue/comment"
+	mailIssueMention  base.TplName = "issue/mention"
+	mailIssueAssigned base.TplName = "issue/assigned"
 
 	mailNotifyCollaborator base.TplName = "notify/collaborator"
 )
@@ -183,6 +184,7 @@ func composeIssueCommentMessage(issue *models.Issue, doer *models.User, content
 		data = composeTplData(subject, body, issue.HTMLURL())
 	}
 	data["Doer"] = doer
+	data["Issue"] = issue
 
 	var mailBody bytes.Buffer
 
@@ -220,3 +222,8 @@ func SendIssueMentionMail(issue *models.Issue, doer *models.User, content string
 	}
 	SendAsync(composeIssueCommentMessage(issue, doer, content, comment, mailIssueMention, tos, "issue mention"))
 }
+
+// SendIssueAssignedMail composes and sends issue assigned email
+func SendIssueAssignedMail(issue *models.Issue, doer *models.User, content string, comment *models.Comment, tos []string) {
+	SendAsync(composeIssueCommentMessage(issue, doer, content, comment, mailIssueAssigned, tos, "issue assigned"))
+}
diff --git a/services/mailer/mail_comment.go b/services/mailer/mail_comment.go
index cb477f887b..d306c14f42 100644
--- a/services/mailer/mail_comment.go
+++ b/services/mailer/mail_comment.go
@@ -9,7 +9,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/log"
-	"code.gitea.io/gitea/modules/markup"
+	"code.gitea.io/gitea/modules/references"
 )
 
 // MailParticipantsComment sends new comment emails to repository watchers
@@ -19,11 +19,18 @@ func MailParticipantsComment(c *models.Comment, opType models.ActionType, issue
 }
 
 func mailParticipantsComment(ctx models.DBContext, c *models.Comment, opType models.ActionType, issue *models.Issue) (err error) {
-	mentions := markup.FindAllMentions(c.Content)
-	if err = models.UpdateIssueMentions(ctx, c.IssueID, mentions); err != nil {
+	rawMentions := references.FindAllMentionsMarkdown(c.Content)
+	userMentions, err := issue.ResolveMentionsByVisibility(ctx, c.Poster, rawMentions)
+	if err != nil {
+		return fmt.Errorf("ResolveMentionsByVisibility [%d]: %v", c.IssueID, err)
+	}
+	if err = models.UpdateIssueMentions(ctx, c.IssueID, userMentions); err != nil {
 		return fmt.Errorf("UpdateIssueMentions [%d]: %v", c.IssueID, err)
 	}
-
+	mentions := make([]string, len(userMentions))
+	for i, u := range userMentions {
+		mentions[i] = u.LowerName
+	}
 	if len(c.Content) > 0 {
 		if err = mailIssueCommentToParticipants(issue, c.Poster, c.Content, c, mentions); err != nil {
 			log.Error("mailIssueCommentToParticipants: %v", err)
diff --git a/services/mailer/mail_issue.go b/services/mailer/mail_issue.go
index 92d2c5a879..a5f3251807 100644
--- a/services/mailer/mail_issue.go
+++ b/services/mailer/mail_issue.go
@@ -9,8 +9,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/log"
-	"code.gitea.io/gitea/modules/markup"
-	"code.gitea.io/gitea/modules/setting"
+	"code.gitea.io/gitea/modules/references"
 
 	"github.com/unknwon/com"
 )
@@ -24,9 +23,6 @@ func mailSubject(issue *models.Issue) string {
 // 1. Repository watchers and users who are participated in comments.
 // 2. Users who are not in 1. but get mentioned in current issue/comment.
 func mailIssueCommentToParticipants(issue *models.Issue, doer *models.User, content string, comment *models.Comment, mentions []string) error {
-	if !setting.Service.EnableNotifyMail {
-		return nil
-	}
 
 	watchers, err := models.GetWatchers(issue.RepoID)
 	if err != nil {
@@ -123,11 +119,18 @@ func MailParticipants(issue *models.Issue, doer *models.User, opType models.Acti
 }
 
 func mailParticipants(ctx models.DBContext, issue *models.Issue, doer *models.User, opType models.ActionType) (err error) {
-	mentions := markup.FindAllMentions(issue.Content)
-
-	if err = models.UpdateIssueMentions(ctx, issue.ID, mentions); err != nil {
+	rawMentions := references.FindAllMentionsMarkdown(issue.Content)
+	userMentions, err := issue.ResolveMentionsByVisibility(ctx, doer, rawMentions)
+	if err != nil {
+		return fmt.Errorf("ResolveMentionsByVisibility [%d]: %v", issue.ID, err)
+	}
+	if err = models.UpdateIssueMentions(ctx, issue.ID, userMentions); err != nil {
 		return fmt.Errorf("UpdateIssueMentions [%d]: %v", issue.ID, err)
 	}
+	mentions := make([]string, len(userMentions))
+	for i, u := range userMentions {
+		mentions[i] = u.LowerName
+	}
 
 	if len(issue.Content) > 0 {
 		if err = mailIssueCommentToParticipants(issue, doer, issue.Content, nil, mentions); err != nil {
diff --git a/services/mirror/mirror.go b/services/mirror/mirror.go
index 3339f72329..11430c2070 100644
--- a/services/mirror/mirror.go
+++ b/services/mirror/mirror.go
@@ -91,7 +91,7 @@ func AddressNoCredentials(m *models.Mirror) string {
 func SaveAddress(m *models.Mirror, addr string) error {
 	repoPath := m.Repo.RepoPath()
 	// Remove old origin
-	_, err := git.NewCommand("remote", "remove", "origin").RunInDir(repoPath)
+	_, err := git.NewCommand("remote", "rm", "origin").RunInDir(repoPath)
 	if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") {
 		return err
 	}
@@ -329,16 +329,16 @@ func SyncMirrors() {
 
 			// Create reference
 			if result.oldCommitID == gitShortEmptySha {
-				if err = models.MirrorSyncCreateAction(m.Repo, result.refName); err != nil {
-					log.Error("MirrorSyncCreateAction [repo_id: %d]: %v", m.RepoID, err)
+				if err = SyncCreateAction(m.Repo, result.refName); err != nil {
+					log.Error("SyncCreateAction [repo_id: %d]: %v", m.RepoID, err)
 				}
 				continue
 			}
 
 			// Delete reference
 			if result.newCommitID == gitShortEmptySha {
-				if err = models.MirrorSyncDeleteAction(m.Repo, result.refName); err != nil {
-					log.Error("MirrorSyncDeleteAction [repo_id: %d]: %v", m.RepoID, err)
+				if err = SyncDeleteAction(m.Repo, result.refName); err != nil {
+					log.Error("SyncDeleteAction [repo_id: %d]: %v", m.RepoID, err)
 				}
 				continue
 			}
@@ -359,13 +359,13 @@ func SyncMirrors() {
 				log.Error("CommitsBetweenIDs [repo_id: %d, new_commit_id: %s, old_commit_id: %s]: %v", m.RepoID, newCommitID, oldCommitID, err)
 				continue
 			}
-			if err = models.MirrorSyncPushAction(m.Repo, models.MirrorSyncPushActionOptions{
+			if err = SyncPushAction(m.Repo, SyncPushActionOptions{
 				RefName:     result.refName,
 				OldCommitID: oldCommitID,
 				NewCommitID: newCommitID,
 				Commits:     models.ListToPushCommits(commits),
 			}); err != nil {
-				log.Error("MirrorSyncPushAction [repo_id: %d]: %v", m.RepoID, err)
+				log.Error("SyncPushAction [repo_id: %d]: %v", m.RepoID, err)
 				continue
 			}
 		}
diff --git a/services/mirror/mirror_test.go b/services/mirror/mirror_test.go
index 76bd4c72f7..9ad11b7265 100644
--- a/services/mirror/mirror_test.go
+++ b/services/mirror/mirror_test.go
@@ -10,6 +10,7 @@ import (
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/git"
+	"code.gitea.io/gitea/modules/structs"
 	release_service "code.gitea.io/gitea/services/release"
 
 	"github.com/stretchr/testify/assert"
@@ -26,16 +27,26 @@ func TestRelease_MirrorDelete(t *testing.T) {
 	repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 1}).(*models.Repository)
 	repoPath := models.RepoPath(user.Name, repo.Name)
 
-	migrationOptions := models.MigrateRepoOptions{
-		Name:                 "test_mirror",
-		Description:          "Test mirror",
-		IsPrivate:            false,
-		IsMirror:             true,
-		RemoteAddr:           repoPath,
-		Wiki:                 true,
-		SyncReleasesWithTags: true,
+	opts := structs.MigrateRepoOption{
+		RepoName:    "test_mirror",
+		Description: "Test mirror",
+		Private:     false,
+		Mirror:      true,
+		CloneAddr:   repoPath,
+		Wiki:        true,
+		Releases:    false,
 	}
-	mirror, err := models.MigrateRepository(user, user, migrationOptions)
+
+	mirrorRepo, err := models.CreateRepository(user, user, models.CreateRepoOptions{
+		Name:        opts.RepoName,
+		Description: opts.Description,
+		IsPrivate:   opts.Private,
+		IsMirror:    opts.Mirror,
+		Status:      models.RepositoryBeingMigrated,
+	})
+	assert.NoError(t, err)
+
+	mirror, err := models.MigrateRepositoryGitData(user, user, mirrorRepo, opts)
 	assert.NoError(t, err)
 
 	gitRepo, err := git.OpenRepository(repoPath)
diff --git a/services/mirror/sync.go b/services/mirror/sync.go
new file mode 100644
index 0000000000..4bc153b47f
--- /dev/null
+++ b/services/mirror/sync.go
@@ -0,0 +1,87 @@
+// Copyright 2019 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package mirror
+
+import (
+	"encoding/json"
+	"fmt"
+
+	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/modules/setting"
+	api "code.gitea.io/gitea/modules/structs"
+)
+
+func syncAction(opType models.ActionType, repo *models.Repository, refName string, data []byte) error {
+	if err := models.NotifyWatchers(&models.Action{
+		ActUserID: repo.OwnerID,
+		ActUser:   repo.MustOwner(),
+		OpType:    opType,
+		RepoID:    repo.ID,
+		Repo:      repo,
+		IsPrivate: repo.IsPrivate,
+		RefName:   refName,
+		Content:   string(data),
+	}); err != nil {
+		return fmt.Errorf("notifyWatchers: %v", err)
+	}
+
+	defer func() {
+		go models.HookQueue.Add(repo.ID)
+	}()
+
+	return nil
+}
+
+// SyncPushActionOptions mirror synchronization action options.
+type SyncPushActionOptions struct {
+	RefName     string
+	OldCommitID string
+	NewCommitID string
+	Commits     *models.PushCommits
+}
+
+// SyncPushAction adds new action for mirror synchronization of pushed commits.
+func SyncPushAction(repo *models.Repository, opts SyncPushActionOptions) error {
+	if len(opts.Commits.Commits) > setting.UI.FeedMaxCommitNum {
+		opts.Commits.Commits = opts.Commits.Commits[:setting.UI.FeedMaxCommitNum]
+	}
+
+	apiCommits, err := opts.Commits.ToAPIPayloadCommits(repo.RepoPath(), repo.HTMLURL())
+	if err != nil {
+		return err
+	}
+
+	opts.Commits.CompareURL = repo.ComposeCompareURL(opts.OldCommitID, opts.NewCommitID)
+	apiPusher := repo.MustOwner().APIFormat()
+	if err := models.PrepareWebhooks(repo, models.HookEventPush, &api.PushPayload{
+		Ref:        opts.RefName,
+		Before:     opts.OldCommitID,
+		After:      opts.NewCommitID,
+		CompareURL: setting.AppURL + opts.Commits.CompareURL,
+		Commits:    apiCommits,
+		Repo:       repo.APIFormat(models.AccessModeOwner),
+		Pusher:     apiPusher,
+		Sender:     apiPusher,
+	}); err != nil {
+		return fmt.Errorf("PrepareWebhooks: %v", err)
+	}
+
+	data, err := json.Marshal(opts.Commits)
+	if err != nil {
+		return err
+	}
+
+	return syncAction(models.ActionMirrorSyncPush, repo, opts.RefName, data)
+}
+
+// SyncCreateAction adds new action for mirror synchronization of new reference.
+func SyncCreateAction(repo *models.Repository, refName string) error {
+	return syncAction(models.ActionMirrorSyncCreate, repo, refName, nil)
+}
+
+// SyncDeleteAction adds new action for mirror synchronization of delete reference.
+func SyncDeleteAction(repo *models.Repository, refName string) error {
+	return syncAction(models.ActionMirrorSyncDelete, repo, refName, nil)
+}
diff --git a/services/pull/merge.go b/services/pull/merge.go
index 6150c1518e..5eb8eaa4d4 100644
--- a/services/pull/merge.go
+++ b/services/pull/merge.go
@@ -11,9 +11,9 @@ import (
 	"fmt"
 	"io/ioutil"
 	"os"
-	"path"
 	"path/filepath"
 	"strings"
+	"time"
 
 	"code.gitea.io/gitea/models"
 	"code.gitea.io/gitea/modules/cache"
@@ -22,11 +22,18 @@ import (
 	"code.gitea.io/gitea/modules/setting"
 	api "code.gitea.io/gitea/modules/structs"
 	"code.gitea.io/gitea/modules/timeutil"
+
+	"github.com/mcuadros/go-version"
 )
 
 // Merge merges pull request to base repository.
 // FIXME: add repoWorkingPull make sure two merges does not happen at same time.
 func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repository, mergeStyle models.MergeStyle, message string) (err error) {
+	binVersion, err := git.BinVersion()
+	if err != nil {
+		return fmt.Errorf("Unable to get git version: %v", err)
+	}
+
 	if err = pr.GetHeadRepo(); err != nil {
 		return fmt.Errorf("GetHeadRepo: %v", err)
 	} else if err = pr.GetBaseRepo(); err != nil {
@@ -49,7 +56,7 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
 	}
 
 	defer func() {
-		go models.AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false)
+		go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false)
 	}()
 
 	// Clone base repo.
@@ -64,22 +71,19 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
 		}
 	}()
 
-	headRepoPath := models.RepoPath(pr.HeadUserName, pr.HeadRepo.Name)
+	headRepoPath := pr.HeadRepo.RepoPath()
 
-	if err := git.Clone(baseGitRepo.Path, tmpBasePath, git.CloneRepoOptions{
-		Shared:     true,
-		NoCheckout: true,
-		Branch:     pr.BaseBranch,
-	}); err != nil {
-		return fmt.Errorf("git clone: %v", err)
+	if err := git.InitRepository(tmpBasePath, false); err != nil {
+		return fmt.Errorf("git init: %v", err)
 	}
 
 	remoteRepoName := "head_repo"
+	baseBranch := "base"
 
 	// Add head repo remote.
 	addCacheRepo := func(staging, cache string) error {
 		p := filepath.Join(staging, ".git", "objects", "info", "alternates")
-		f, err := os.OpenFile(p, os.O_APPEND|os.O_WRONLY, 0600)
+		f, err := os.OpenFile(p, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0600)
 		if err != nil {
 			return err
 		}
@@ -91,25 +95,41 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
 		return nil
 	}
 
-	if err := addCacheRepo(tmpBasePath, headRepoPath); err != nil {
+	if err := addCacheRepo(tmpBasePath, baseGitRepo.Path); err != nil {
 		return fmt.Errorf("addCacheRepo [%s -> %s]: %v", headRepoPath, tmpBasePath, err)
 	}
 
 	var errbuf strings.Builder
+	if err := git.NewCommand("remote", "add", "-t", pr.BaseBranch, "-m", pr.BaseBranch, "origin", baseGitRepo.Path).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
+		return fmt.Errorf("git remote add [%s -> %s]: %s", baseGitRepo.Path, tmpBasePath, errbuf.String())
+	}
+
+	if err := git.NewCommand("fetch", "origin", "--no-tags", pr.BaseBranch+":"+baseBranch, pr.BaseBranch+":original_"+baseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
+		return fmt.Errorf("git fetch [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String())
+	}
+
+	if err := git.NewCommand("symbolic-ref", "HEAD", git.BranchPrefix+baseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
+		return fmt.Errorf("git symbolic-ref HEAD base [%s]: %s", tmpBasePath, errbuf.String())
+	}
+
+	if err := addCacheRepo(tmpBasePath, headRepoPath); err != nil {
+		return fmt.Errorf("addCacheRepo [%s -> %s]: %v", headRepoPath, tmpBasePath, err)
+	}
+
 	if err := git.NewCommand("remote", "add", remoteRepoName, headRepoPath).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
 		return fmt.Errorf("git remote add [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String())
 	}
 
+	trackingBranch := "tracking"
 	// Fetch head branch
-	if err := git.NewCommand("fetch", remoteRepoName, fmt.Sprintf("%s:refs/remotes/%s/%s", pr.HeadBranch, remoteRepoName, pr.HeadBranch)).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
+	if err := git.NewCommand("fetch", "--no-tags", remoteRepoName, pr.HeadBranch+":"+trackingBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
 		return fmt.Errorf("git fetch [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String())
 	}
 
-	trackingBranch := path.Join(remoteRepoName, pr.HeadBranch)
-	stagingBranch := fmt.Sprintf("%s_%s", remoteRepoName, pr.HeadBranch)
+	stagingBranch := "staging"
 
 	// Enable sparse-checkout
-	sparseCheckoutList, err := getDiffTree(tmpBasePath, pr.BaseBranch, trackingBranch)
+	sparseCheckoutList, err := getDiffTree(tmpBasePath, baseBranch, trackingBranch)
 	if err != nil {
 		return fmt.Errorf("getDiffTree: %v", err)
 	}
@@ -123,21 +143,37 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
 		return fmt.Errorf("Writing sparse-checkout file to %s: %v", sparseCheckoutListPath, err)
 	}
 
+	gitConfigCommand := func() func() *git.Command {
+		binVersion, err := git.BinVersion()
+		if err != nil {
+			log.Fatal("Error retrieving git version: %v", err)
+		}
+
+		if version.Compare(binVersion, "1.8.0", ">=") {
+			return func() *git.Command {
+				return git.NewCommand("config", "--local")
+			}
+		}
+		return func() *git.Command {
+			return git.NewCommand("config")
+		}
+	}()
+
 	// Switch off LFS process (set required, clean and smudge here also)
-	if err := git.NewCommand("config", "--local", "filter.lfs.process", "").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
+	if err := gitConfigCommand().AddArguments("filter.lfs.process", "").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
 		return fmt.Errorf("git config [filter.lfs.process -> <> ]: %v", errbuf.String())
 	}
-	if err := git.NewCommand("config", "--local", "filter.lfs.required", "false").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
+	if err := gitConfigCommand().AddArguments("filter.lfs.required", "false").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
 		return fmt.Errorf("git config [filter.lfs.required -> <false> ]: %v", errbuf.String())
 	}
-	if err := git.NewCommand("config", "--local", "filter.lfs.clean", "").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
+	if err := gitConfigCommand().AddArguments("filter.lfs.clean", "").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
 		return fmt.Errorf("git config [filter.lfs.clean -> <> ]: %v", errbuf.String())
 	}
-	if err := git.NewCommand("config", "--local", "filter.lfs.smudge", "").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
+	if err := gitConfigCommand().AddArguments("filter.lfs.smudge", "").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
 		return fmt.Errorf("git config [filter.lfs.smudge -> <> ]: %v", errbuf.String())
 	}
 
-	if err := git.NewCommand("config", "--local", "core.sparseCheckout", "true").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
+	if err := gitConfigCommand().AddArguments("core.sparseCheckout", "true").RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
 		return fmt.Errorf("git config [core.sparsecheckout -> true]: %v", errbuf.String())
 	}
 
@@ -146,6 +182,30 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
 		return fmt.Errorf("git read-tree HEAD: %s", errbuf.String())
 	}
 
+	// Determine if we should sign
+	signArg := ""
+	if version.Compare(binVersion, "1.7.9", ">=") {
+		sign, keyID := pr.BaseRepo.SignMerge(doer, tmpBasePath, "HEAD", trackingBranch)
+		if sign {
+			signArg = "-S" + keyID
+		} else if version.Compare(binVersion, "2.0.0", ">=") {
+			signArg = "--no-gpg-sign"
+		}
+	}
+
+	sig := doer.NewGitSig()
+	commitTimeStr := time.Now().Format(time.RFC3339)
+
+	// Because this may call hooks we should pass in the environment
+	env := append(os.Environ(),
+		"GIT_AUTHOR_NAME="+sig.Name,
+		"GIT_AUTHOR_EMAIL="+sig.Email,
+		"GIT_AUTHOR_DATE="+commitTimeStr,
+		"GIT_COMMITTER_NAME="+sig.Name,
+		"GIT_COMMITTER_EMAIL="+sig.Email,
+		"GIT_COMMITTER_DATE="+commitTimeStr,
+	)
+
 	// Merge commits.
 	switch mergeStyle {
 	case models.MergeStyleMerge:
@@ -153,9 +213,14 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
 			return fmt.Errorf("git merge --no-ff --no-commit [%s]: %v - %s", tmpBasePath, err, errbuf.String())
 		}
 
-		sig := doer.NewGitSig()
-		if err := git.NewCommand("commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", message).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
-			return fmt.Errorf("git commit [%s]: %v - %s", tmpBasePath, err, errbuf.String())
+		if signArg == "" {
+			if err := git.NewCommand("commit", "-m", message).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, nil, &errbuf); err != nil {
+				return fmt.Errorf("git commit [%s]: %v - %s", tmpBasePath, err, errbuf.String())
+			}
+		} else {
+			if err := git.NewCommand("commit", signArg, "-m", message).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, nil, &errbuf); err != nil {
+				return fmt.Errorf("git commit [%s]: %v - %s", tmpBasePath, err, errbuf.String())
+			}
 		}
 	case models.MergeStyleRebase:
 		// Checkout head branch
@@ -163,11 +228,11 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
 			return fmt.Errorf("git checkout: %s", errbuf.String())
 		}
 		// Rebase before merging
-		if err := git.NewCommand("rebase", "-q", pr.BaseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
+		if err := git.NewCommand("rebase", "-q", baseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
 			return fmt.Errorf("git rebase [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String())
 		}
 		// Checkout base branch again
-		if err := git.NewCommand("checkout", pr.BaseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
+		if err := git.NewCommand("checkout", baseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
 			return fmt.Errorf("git checkout: %s", errbuf.String())
 		}
 		// Merge fast forward
@@ -180,11 +245,11 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
 			return fmt.Errorf("git checkout: %s", errbuf.String())
 		}
 		// Rebase before merging
-		if err := git.NewCommand("rebase", "-q", pr.BaseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
+		if err := git.NewCommand("rebase", "-q", baseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
 			return fmt.Errorf("git rebase [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String())
 		}
 		// Checkout base branch again
-		if err := git.NewCommand("checkout", pr.BaseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
+		if err := git.NewCommand("checkout", baseBranch).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
 			return fmt.Errorf("git checkout: %s", errbuf.String())
 		}
 		// Prepare merge with commit
@@ -193,9 +258,14 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
 		}
 
 		// Set custom message and author and create merge commit
-		sig := doer.NewGitSig()
-		if err := git.NewCommand("commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", message).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
-			return fmt.Errorf("git commit [%s]: %v - %s", tmpBasePath, err, errbuf.String())
+		if signArg == "" {
+			if err := git.NewCommand("commit", "-m", message).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, nil, &errbuf); err != nil {
+				return fmt.Errorf("git commit [%s]: %v - %s", tmpBasePath, err, errbuf.String())
+			}
+		} else {
+			if err := git.NewCommand("commit", signArg, "-m", message).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, nil, &errbuf); err != nil {
+				return fmt.Errorf("git commit [%s]: %v - %s", tmpBasePath, err, errbuf.String())
+			}
 		}
 
 	case models.MergeStyleSquash:
@@ -204,8 +274,14 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
 			return fmt.Errorf("git merge --squash [%s -> %s]: %s", headRepoPath, tmpBasePath, errbuf.String())
 		}
 		sig := pr.Issue.Poster.NewGitSig()
-		if err := git.NewCommand("commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", message).RunInDirPipeline(tmpBasePath, nil, &errbuf); err != nil {
-			return fmt.Errorf("git commit [%s]: %v - %s", tmpBasePath, err, errbuf.String())
+		if signArg == "" {
+			if err := git.NewCommand("commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", message).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, nil, &errbuf); err != nil {
+				return fmt.Errorf("git commit [%s]: %v - %s", tmpBasePath, err, errbuf.String())
+			}
+		} else {
+			if err := git.NewCommand("commit", signArg, fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", message).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, nil, &errbuf); err != nil {
+				return fmt.Errorf("git commit [%s]: %v - %s", tmpBasePath, err, errbuf.String())
+			}
 		}
 	default:
 		return models.ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: mergeStyle}
@@ -216,7 +292,7 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
 	if err != nil {
 		return fmt.Errorf("Failed to get full commit id for HEAD: %v", err)
 	}
-	mergeBaseSHA, err := git.GetFullCommitID(tmpBasePath, "origin/"+pr.BaseBranch)
+	mergeBaseSHA, err := git.GetFullCommitID(tmpBasePath, "original_"+baseBranch)
 	if err != nil {
 		return fmt.Errorf("Failed to get full commit id for origin/%s: %v", pr.BaseBranch, err)
 	}
@@ -230,17 +306,20 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
 		}
 	}
 
-	headUser, err := models.GetUserByName(pr.HeadUserName)
+	var headUser *models.User
+	err = pr.HeadRepo.GetOwner()
 	if err != nil {
 		if !models.IsErrUserNotExist(err) {
-			log.Error("Can't find user: %s for head repository - %v", pr.HeadUserName, err)
+			log.Error("Can't find user: %d for head repository - %v", pr.HeadRepo.OwnerID, err)
 			return err
 		}
-		log.Error("Can't find user: %s for head repository - defaulting to doer: %s - %v", pr.HeadUserName, doer.Name, err)
+		log.Error("Can't find user: %d for head repository - defaulting to doer: %s - %v", pr.HeadRepo.OwnerID, doer.Name, err)
 		headUser = doer
+	} else {
+		headUser = pr.HeadRepo.Owner
 	}
 
-	env := models.FullPushingEnvironment(
+	env = models.FullPushingEnvironment(
 		headUser,
 		doer,
 		pr.BaseRepo,
@@ -249,7 +328,7 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
 	)
 
 	// Push back to upstream.
-	if err := git.NewCommand("push", "origin", pr.BaseBranch).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, nil, &errbuf); err != nil {
+	if err := git.NewCommand("push", "origin", baseBranch+":"+pr.BaseBranch).RunInDirTimeoutEnvPipeline(env, -1, tmpBasePath, nil, &errbuf); err != nil {
 		return fmt.Errorf("git push: %s", errbuf.String())
 	}
 
diff --git a/services/pull/pull.go b/services/pull/pull.go
index 0dbd8fcd1a..959da67405 100644
--- a/services/pull/pull.go
+++ b/services/pull/pull.go
@@ -8,13 +8,14 @@ import (
 	"fmt"
 
 	"code.gitea.io/gitea/models"
+	"code.gitea.io/gitea/modules/git"
 	"code.gitea.io/gitea/modules/log"
 	api "code.gitea.io/gitea/modules/structs"
 )
 
 // NewPullRequest creates new pull request with labels for repository.
-func NewPullRequest(repo *models.Repository, pull *models.Issue, labelIDs []int64, uuids []string, pr *models.PullRequest, patch []byte, assigneeIDs []int64) error {
-	if err := models.NewPullRequest(repo, pull, labelIDs, uuids, pr, patch, assigneeIDs); err != nil {
+func NewPullRequest(repo *models.Repository, pull *models.Issue, labelIDs []int64, uuids []string, pr *models.PullRequest, patch []byte) error {
+	if err := models.NewPullRequest(repo, pull, labelIDs, uuids, pr, patch); err != nil {
 		return err
 	}
 
@@ -47,3 +48,90 @@ func NewPullRequest(repo *models.Repository, pull *models.Issue, labelIDs []int6
 
 	return nil
 }
+
+func checkForInvalidation(requests models.PullRequestList, repoID int64, doer *models.User, branch string) error {
+	repo, err := models.GetRepositoryByID(repoID)
+	if err != nil {
+		return fmt.Errorf("GetRepositoryByID: %v", err)
+	}
+	gitRepo, err := git.OpenRepository(repo.RepoPath())
+	if err != nil {
+		return fmt.Errorf("git.OpenRepository: %v", err)
+	}
+	go func() {
+		err := requests.InvalidateCodeComments(doer, gitRepo, branch)
+		if err != nil {
+			log.Error("PullRequestList.InvalidateCodeComments: %v", err)
+		}
+	}()
+	return nil
+}
+
+func addHeadRepoTasks(prs []*models.PullRequest) {
+	for _, pr := range prs {
+		log.Trace("addHeadRepoTasks[%d]: composing new test task", pr.ID)
+		if err := pr.UpdatePatch(); err != nil {
+			log.Error("UpdatePatch: %v", err)
+			continue
+		} else if err := pr.PushToBaseRepo(); err != nil {
+			log.Error("PushToBaseRepo: %v", err)
+			continue
+		}
+
+		pr.AddToTaskQueue()
+	}
+}
+
+// AddTestPullRequestTask adds new test tasks by given head/base repository and head/base branch,
+// and generate new patch for testing as needed.
+func AddTestPullRequestTask(doer *models.User, repoID int64, branch string, isSync bool) {
+	log.Trace("AddTestPullRequestTask [head_repo_id: %d, head_branch: %s]: finding pull requests", repoID, branch)
+	prs, err := models.GetUnmergedPullRequestsByHeadInfo(repoID, branch)
+	if err != nil {
+		log.Error("Find pull requests [head_repo_id: %d, head_branch: %s]: %v", repoID, branch, err)
+		return
+	}
+
+	if isSync {
+		requests := models.PullRequestList(prs)
+		if err = requests.LoadAttributes(); err != nil {
+			log.Error("PullRequestList.LoadAttributes: %v", err)
+		}
+		if invalidationErr := checkForInvalidation(requests, repoID, doer, branch); invalidationErr != nil {
+			log.Error("checkForInvalidation: %v", invalidationErr)
+		}
+		if err == nil {
+			for _, pr := range prs {
+				pr.Issue.PullRequest = pr
+				if err = pr.Issue.LoadAttributes(); err != nil {
+					log.Error("LoadAttributes: %v", err)
+					continue
+				}
+				if err = models.PrepareWebhooks(pr.Issue.Repo, models.HookEventPullRequest, &api.PullRequestPayload{
+					Action:      api.HookIssueSynchronized,
+					Index:       pr.Issue.Index,
+					PullRequest: pr.Issue.PullRequest.APIFormat(),
+					Repository:  pr.Issue.Repo.APIFormat(models.AccessModeNone),
+					Sender:      doer.APIFormat(),
+				}); err != nil {
+					log.Error("PrepareWebhooks [pull_id: %v]: %v", pr.ID, err)
+					continue
+				}
+				go models.HookQueue.Add(pr.Issue.Repo.ID)
+			}
+		}
+
+	}
+
+	addHeadRepoTasks(prs)
+
+	log.Trace("AddTestPullRequestTask [base_repo_id: %d, base_branch: %s]: finding pull requests", repoID, branch)
+	prs, err = models.GetUnmergedPullRequestsByBaseInfo(repoID, branch)
+	if err != nil {
+		log.Error("Find pull requests [base_repo_id: %d, base_branch: %s]: %v", repoID, branch, err)
+		return
+	}
+	for _, pr := range prs {
+		pr.AddToTaskQueue()
+	}
+}
diff --git a/services/pull/review.go b/services/pull/review.go
index 3fdfaaff84..3388e4bb56 100644
--- a/services/pull/review.go
+++ b/services/pull/review.go
@@ -17,9 +17,23 @@ func CreateReview(opts models.CreateReviewOptions) (*models.Review, error) {
 		return nil, err
 	}
 
+	return review, reviewHook(review)
+}
+
+// UpdateReview updates a review
+func UpdateReview(review *models.Review) error {
+	err := models.UpdateReview(review)
+	if err != nil {
+		return err
+	}
+
+	return reviewHook(review)
+}
+
+func reviewHook(review *models.Review) error {
 	var reviewHookType models.HookEventType
 
-	switch opts.Type {
+	switch review.Type {
 	case models.ReviewTypeApprove:
 		reviewHookType = models.HookEventPullRequestApproved
 	case models.ReviewTypeComment:
@@ -28,30 +42,33 @@ func CreateReview(opts models.CreateReviewOptions) (*models.Review, error) {
 		reviewHookType = models.HookEventPullRequestRejected
 	default:
 		// unsupported review webhook type here
-		return review, nil
+		return nil
 	}
 
-	pr := opts.Issue.PullRequest
+	pr := review.Issue.PullRequest
 
 	if err := pr.LoadIssue(); err != nil {
-		return nil, err
+		return err
 	}
 
-	mode, err := models.AccessLevel(opts.Issue.Poster, opts.Issue.Repo)
+	mode, err := models.AccessLevel(review.Issue.Poster, review.Issue.Repo)
 	if err != nil {
-		return nil, err
+		return err
 	}
-
-	if err := models.PrepareWebhooks(opts.Issue.Repo, reviewHookType, &api.PullRequestPayload{
+	if err := models.PrepareWebhooks(review.Issue.Repo, reviewHookType, &api.PullRequestPayload{
 		Action:      api.HookIssueSynchronized,
-		Index:       opts.Issue.Index,
+		Index:       review.Issue.Index,
 		PullRequest: pr.APIFormat(),
-		Repository:  opts.Issue.Repo.APIFormat(mode),
-		Sender:      opts.Reviewer.APIFormat(),
+		Repository:  review.Issue.Repo.APIFormat(mode),
+		Sender:      review.Reviewer.APIFormat(),
+		Review: &api.ReviewPayload{
+			Type:    string(reviewHookType),
+			Content: review.Content,
+		},
 	}); err != nil {
-		return nil, err
+		return err
 	}
-	go models.HookQueue.Add(opts.Issue.Repo.ID)
+	go models.HookQueue.Add(review.Issue.Repo.ID)
 
-	return review, nil
+	return nil
 }
diff --git a/templates/admin/hook_new.tmpl b/templates/admin/hook_new.tmpl
index c047efe9a2..2292377f6e 100644
--- a/templates/admin/hook_new.tmpl
+++ b/templates/admin/hook_new.tmpl
@@ -11,19 +11,19 @@
 			{{end}}
 			<div class="ui right">
 				{{if eq .HookType "gitea"}}
-					<img class="img-13" src="{{AppSubUrl}}/img/gitea-sm.png">
+					<img class="img-13" src="{{StaticUrlPrefix}}/img/gitea-sm.png">
 				{{else if eq .HookType "gogs"}}
-					<img class="img-13" src="{{AppSubUrl}}/img/gogs.ico">
+					<img class="img-13" src="{{StaticUrlPrefix}}/img/gogs.ico">
 				{{else if eq .HookType "slack"}}
-					<img class="img-13" src="{{AppSubUrl}}/img/slack.png">
+					<img class="img-13" src="{{StaticUrlPrefix}}/img/slack.png">
 				{{else if eq .HookType "discord"}}
-					<img class="img-13" src="{{AppSubUrl}}/img/discord.png">
+					<img class="img-13" src="{{StaticUrlPrefix}}/img/discord.png">
 				{{else if eq .HookType "dingtalk"}}
-					<img class="img-13" src="{{AppSubUrl}}/img/dingtalk.ico">
+					<img class="img-13" src="{{StaticUrlPrefix}}/img/dingtalk.ico">
 				{{else if eq .HookType "telegram"}}
-					<img class="img-13" src="{{AppSubUrl}}/img/telegram.png">
+					<img class="img-13" src="{{StaticUrlPrefix}}/img/telegram.png">
 				{{else if eq .HookType "msteams"}}
-					<img class="img-13" src="{{AppSubUrl}}/img/msteams.png">
+					<img class="img-13" src="{{StaticUrlPrefix}}/img/msteams.png">
 				{{end}}
 			</div>
 		</h4>
diff --git a/templates/base/footer.tmpl b/templates/base/footer.tmpl
index 13718620da..7185b20377 100644
--- a/templates/base/footer.tmpl
+++ b/templates/base/footer.tmpl
@@ -12,38 +12,38 @@
 
 	{{template "base/footer_content" .}}
 
-	<script src="{{AppSubUrl}}/vendor/plugins/jquery/jquery.min.js?v=3.4.1"></script>
-	<script src="{{AppSubUrl}}/vendor/plugins/jquery-migrate/jquery-migrate.min.js?v=3.0.1"></script>
-	<script src="{{AppSubUrl}}/vendor/plugins/jquery.areyousure/jquery.are-you-sure.js"></script>
+	<script src="{{StaticUrlPrefix}}/vendor/plugins/jquery/jquery.min.js?v=3.4.1"></script>
+	<script src="{{StaticUrlPrefix}}/vendor/plugins/jquery-migrate/jquery-migrate.min.js?v=3.0.1"></script>
+	<script src="{{StaticUrlPrefix}}/vendor/plugins/jquery.areyousure/jquery.are-you-sure.js"></script>
 {{if .RequireSimpleMDE}}
-	<script src="{{AppSubUrl}}/vendor/plugins/simplemde/simplemde.min.js"></script>
-	<script src="{{AppSubUrl}}/vendor/plugins/codemirror/addon/mode/loadmode.js"></script>
-	<script src="{{AppSubUrl}}/vendor/plugins/codemirror/mode/meta.js"></script>
+	<script src="{{StaticUrlPrefix}}/vendor/plugins/simplemde/simplemde.min.js"></script>
+	<script src="{{StaticUrlPrefix}}/vendor/plugins/codemirror/addon/mode/loadmode.js"></script>
+	<script src="{{StaticUrlPrefix}}/vendor/plugins/codemirror/mode/meta.js"></script>
 	<script>
-		CodeMirror.modeURL =  "{{AppSubUrl}}/vendor/plugins/codemirror/mode/%N/%N.js";
+		CodeMirror.modeURL =  "{{StaticUrlPrefix}}/vendor/plugins/codemirror/mode/%N/%N.js";
 	</script>
 {{end}}
 {{if .RequireGitGraph}}
 	<!-- graph -->
-	<script src="{{AppSubUrl}}/vendor/plugins/gitgraph/gitgraph.js"></script>
-	<script src="{{AppSubUrl}}/js/draw.js"></script>
+	<script src="{{StaticUrlPrefix}}/vendor/plugins/gitgraph/gitgraph.js"></script>
+	<script src="{{StaticUrlPrefix}}/js/draw.js"></script>
 {{end}}
 
 <!-- Third-party libraries -->
 {{if .RequireHighlightJS}}
-	<script src="{{AppSubUrl}}/vendor/plugins/highlight/highlight.pack.js"></script>
+	<script src="{{StaticUrlPrefix}}/vendor/plugins/highlight/highlight.pack.js"></script>
 {{end}}
 {{if .RequireMinicolors}}
-	<script src="{{AppSubUrl}}/vendor/plugins/jquery.minicolors/jquery.minicolors.min.js"></script>
+	<script src="{{StaticUrlPrefix}}/vendor/plugins/jquery.minicolors/jquery.minicolors.min.js"></script>
 {{end}}
 {{if .RequireDatetimepicker}}
-	<script src="{{AppSubUrl}}/vendor/plugins/jquery.datetimepicker/jquery.datetimepicker.js"></script>
+	<script src="{{StaticUrlPrefix}}/vendor/plugins/jquery.datetimepicker/jquery.datetimepicker.js"></script>
 {{end}}
 {{if .RequireDropzone}}
-	<script src="{{AppSubUrl}}/vendor/plugins/dropzone/dropzone.js"></script>
+	<script src="{{StaticUrlPrefix}}/vendor/plugins/dropzone/dropzone.js"></script>
 {{end}}
 {{if .RequireU2F}}
-	<script src="{{AppSubUrl}}/vendor/plugins/u2f/index.js"></script>
+	<script src="{{StaticUrlPrefix}}/vendor/plugins/u2f/index.js"></script>
 {{end}}
 {{if .EnableCaptcha}}
 	{{if eq .CaptchaType "recaptcha"}}
@@ -51,7 +51,7 @@
 	{{end}}
 {{end}}
 {{if .RequireTribute}}
-	<script src="{{AppSubUrl}}/vendor/plugins/tribute/tribute.min.js"></script>
+	<script src="{{StaticUrlPrefix}}/vendor/plugins/tribute/tribute.min.js"></script>
 	<script>
 		var issuesTribute = new Tribute({
 			values: [
@@ -101,7 +101,7 @@
 					return ':' + item.original + ':';
 				},
 				menuItemTemplate: function (item) {
-					return '<img class="emoji" src="{{AppSubUrl}}/vendor/plugins/emojify/images/' + item.original + '.png"/>' + item.original;
+					return '<img class="emoji" src="{{StaticUrlPrefix}}/vendor/plugins/emojify/images/' + item.original + '.png"/>' + item.original;
 				}
 			}]
 		});
@@ -115,16 +115,16 @@
 		}
 	</script>
 {{end}}
-	<script src="{{AppSubUrl}}/vendor/plugins/emojify/emojify.min.js"></script>
-	<script src="{{AppSubUrl}}/vendor/plugins/clipboard/clipboard.min.js"></script>
-	<script src="{{AppSubUrl}}/vendor/plugins/vue/vue.min.js"></script>
+	<script src="{{StaticUrlPrefix}}/vendor/plugins/emojify/emojify.min.js"></script>
+	<script src="{{StaticUrlPrefix}}/vendor/plugins/clipboard/clipboard.min.js"></script>
+	<script src="{{StaticUrlPrefix}}/vendor/plugins/vue/vue.min.js"></script>
 
 	<!-- JavaScript -->
-	<script src="{{AppSubUrl}}/vendor/plugins/semantic/semantic.min.js"></script>
-	<script src="{{AppSubUrl}}/js/index.js?v={{MD5 AppVer}}"></script>
+	<script src="{{StaticUrlPrefix}}/vendor/plugins/semantic/semantic.min.js"></script>
+	<script src="{{StaticUrlPrefix}}/js/index.js?v={{MD5 AppVer}}"></script>
 {{if .EnableHeatmap}}
-	<script src="{{AppSubUrl}}/vendor/plugins/moment/moment.min.js" charset="utf-8"></script>
-	<script src="{{AppSubUrl}}/vendor/plugins/vue-calendar-heatmap/vue-calendar-heatmap.browser.js" charset="utf-8"></script>
+	<script src="{{StaticUrlPrefix}}/vendor/plugins/moment/moment.min.js" charset="utf-8"></script>
+	<script src="{{StaticUrlPrefix}}/vendor/plugins/vue-calendar-heatmap/vue-calendar-heatmap.browser.js" charset="utf-8"></script>
 	<script type="text/javascript">
 		initHeatmap('user-heatmap', '{{.HeatmapUser}}');
 	</script>
diff --git a/templates/base/footer_content.tmpl b/templates/base/footer_content.tmpl
index 6f680d4cb8..364e58a3d0 100644
--- a/templates/base/footer_content.tmpl
+++ b/templates/base/footer_content.tmpl
@@ -16,7 +16,7 @@
 					{{end}}
 				</div>
 			</div>
-			<a href="{{AppSubUrl}}/vendor/librejs.html" data-jslicense="1">JavaScript licenses</a>
+			<a href="{{StaticUrlPrefix}}/vendor/librejs.html" data-jslicense="1">JavaScript licenses</a>
 			{{if .EnableSwagger}}<a href="{{AppSubUrl}}/api/swagger">API</a>{{end}}
 			<a target="_blank" rel="noopener noreferrer" href="https://gitea.io">{{.i18n.Tr "website"}}</a>
 			{{if (or .ShowFooterVersion .PageIsAdmin)}}<span class="version">{{GoVer}}</span>{{end}}
diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl
index 7cdfdd34be..c0686ca93e 100644
--- a/templates/base/head.tmpl
+++ b/templates/base/head.tmpl
@@ -70,35 +70,35 @@
 	THE SOFTWARE.
 	---
 	Licensing information for additional javascript libraries can be found at:
-	  {{AppSubUrl}}/vendor/librejs.html
+	  {{StaticUrlPrefix}}/vendor/librejs.html
 
 	@licend  The above is the entire license notice
         for the JavaScript code in this page.
 	*/`}}
 	</script>
 
-	<link rel="shortcut icon" href="{{AppSubUrl}}/img/favicon.png" />
-	<link rel="mask-icon" href="{{AppSubUrl}}/img/gitea-safari.svg" color="#609926">
-	<link rel="preload" href="{{AppSubUrl}}/vendor/assets/font-awesome/css/font-awesome.min.css" as="style" onload="this.rel='stylesheet'">
-	<noscript><link rel="stylesheet" href="{{AppSubUrl}}/vendor/assets/font-awesome/css/font-awesome.min.css"></noscript>
-	<link rel="stylesheet" href="{{AppSubUrl}}/vendor/assets/octicons/octicons.min.css">
+	<link rel="shortcut icon" href="{{StaticUrlPrefix}}/img/favicon.png" />
+	<link rel="mask-icon" href="{{StaticUrlPrefix}}/img/gitea-safari.svg" color="#609926">
+	<link rel="preload" href="{{StaticUrlPrefix}}/vendor/assets/font-awesome/css/font-awesome.min.css" as="style" onload="this.rel='stylesheet'">
+	<noscript><link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/assets/font-awesome/css/font-awesome.min.css"></noscript>
+	<link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/assets/octicons/octicons.min.css">
 
 {{if .RequireSimpleMDE}}
-	<link rel="stylesheet" href="{{AppSubUrl}}/vendor/plugins/simplemde/simplemde.min.css">
+	<link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/simplemde/simplemde.min.css">
 {{end}}
 
 {{if .RequireGitGraph}}
 	<!-- graph -->
-	<link rel="stylesheet" href="{{AppSubUrl}}/vendor/plugins/gitgraph/gitgraph.css">
+	<link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/gitgraph/gitgraph.css">
 {{end}}
 
 {{if .RequireTribute}}
-	<link rel="stylesheet" href="{{AppSubUrl}}/vendor/plugins/tribute/tribute.css">
+	<link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/tribute/tribute.css">
 {{end}}
 
 	<!-- Stylesheet -->
-	<link rel="stylesheet" href="{{AppSubUrl}}/vendor/plugins/semantic/semantic.min.css">
-	<link rel="stylesheet" href="{{AppSubUrl}}/css/index.css?v={{MD5 AppVer}}">
+	<link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/semantic/semantic.min.css">
+	<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/index.css?v={{MD5 AppVer}}">
 	<noscript>
 		<style>
 			.dropdown:hover > .menu { display: block; }
@@ -107,53 +107,63 @@
 	</noscript>
 
 {{if .RequireHighlightJS}}
-	<link rel="stylesheet" href="{{AppSubUrl}}/vendor/plugins/highlight/github.css">
+	<link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/highlight/github.css">
 {{end}}
 {{if .RequireMinicolors}}
-	<link rel="stylesheet" href="{{AppSubUrl}}/vendor/plugins/jquery.minicolors/jquery.minicolors.css">
+	<link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/jquery.minicolors/jquery.minicolors.css">
 {{end}}
 {{if .RequireDatetimepicker}}
-	<link rel="stylesheet" href="{{AppSubUrl}}/vendor/plugins/jquery.datetimepicker/jquery.datetimepicker.css">
+	<link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/jquery.datetimepicker/jquery.datetimepicker.css">
 {{end}}
 {{if .RequireDropzone}}
-	<link rel="stylesheet" href="{{AppSubUrl}}/vendor/plugins/dropzone/dropzone.css">
+	<link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/dropzone/dropzone.css">
 {{end}}
 {{if .EnableHeatmap}}
-	<link rel="stylesheet" href="{{AppSubUrl}}/vendor/plugins/vue-calendar-heatmap/vue-calendar-heatmap.css">
+	<link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/vue-calendar-heatmap/vue-calendar-heatmap.css">
 {{end}}
 	<style class="list-search-style"></style>
 
-	<script src="{{AppSubUrl}}/vendor/plugins/promise-polyfill/polyfill.min.js"></script>
-	<script src="{{AppSubUrl}}/vendor/plugins/cssrelpreload/loadCSS.min.js"></script>
-	<script src="{{AppSubUrl}}/vendor/plugins/cssrelpreload/cssrelpreload.min.js"></script>
+	<script src="{{StaticUrlPrefix}}/vendor/plugins/promise-polyfill/polyfill.min.js"></script>
+	<script src="{{StaticUrlPrefix}}/vendor/plugins/cssrelpreload/loadCSS.min.js"></script>
+	<script src="{{StaticUrlPrefix}}/vendor/plugins/cssrelpreload/cssrelpreload.min.js"></script>
 {{if .PageIsUserProfile}}
 	<meta property="og:title" content="{{.Owner.Name}}" />
 	<meta property="og:type" content="profile" />
 	<meta property="og:image" content="{{.Owner.AvatarLink}}" />
 	<meta property="og:url" content="{{.Owner.HTMLURL}}" />
-	<meta property="og:site_name" content="{{AppName}}" />
+	{{if .Owner.Description}}
+		<meta property="og:description" content="{{.Owner.Description}}">
+	{{end}}
 {{else if .Repository}}
-	<meta property="og:title" content="{{.Repository.Name}}" />
+	{{if .Issue}}
+		<meta property="og:title" content="{{.Issue.Title}}" />
+		<meta property="og:url" content="{{.Issue.HTMLURL}}" />
+		{{if .Issue.Content}}
+			<meta property="og:description" content="{{.Issue.Content}}" />
+		{{end}}
+	{{else}}
+		<meta property="og:title" content="{{.Repository.Name}}" />
+		<meta property="og:url" content="{{.Repository.HTMLURL}}" />
+		{{if .Repository.Description}}
+			<meta property="og:description" content="{{.Repository.Description}}" />
+		{{end}}
+	{{end}}
 	<meta property="og:type" content="object" />
 	<meta property="og:image" content="{{.Repository.Owner.AvatarLink}}" />
-	<meta property="og:url" content="{{.Repository.HTMLURL}}" />
-	{{if .Repository.Description}}
-	<meta property="og:description" content="{{.Repository.Description}}" />
-	{{end}}
-	<meta property="og:site_name" content="{{AppName}}" />
 {{else}}
 	<meta property="og:title" content="{{AppName}}">
 	<meta property="og:type" content="website" />
-	<meta property="og:image" content="{{AppUrl}}img/gitea-lg.png" />
+	<meta property="og:image" content="{{StaticUrlPrefix}}img/gitea-lg.png" />
 	<meta property="og:url" content="{{AppUrl}}" />
 	<meta property="og:description" content="{{MetaDescription}}">
 {{end}}
+<meta property="og:site_name" content="{{AppName}}" />
 {{if .IsSigned }}
 	{{ if ne .SignedUser.Theme "gitea" }}
-		<link rel="stylesheet" href="{{AppSubUrl}}/css/theme-{{.SignedUser.Theme}}.css">
+		<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/theme-{{.SignedUser.Theme}}.css">
 	{{end}}
 {{else if ne DefaultTheme "gitea"}}
-	<link rel="stylesheet" href="{{AppSubUrl}}/css/theme-{{DefaultTheme}}.css">
+	<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/theme-{{DefaultTheme}}.css">
 {{end}}
 {{template "custom/header" .}}
 </head>
diff --git a/templates/base/head_navbar.tmpl b/templates/base/head_navbar.tmpl
index 30316e7e5b..fdba57d5bf 100644
--- a/templates/base/head_navbar.tmpl
+++ b/templates/base/head_navbar.tmpl
@@ -1,7 +1,7 @@
 <div class="ui container" id="navbar">
 	<div class="item brand" style="justify-content: space-between;">
 		<a href="{{AppSubUrl}}/">
-			<img class="ui mini image" src="{{AppSubUrl}}/img/gitea-sm.png">
+			<img class="ui mini image" src="{{StaticUrlPrefix}}/img/gitea-sm.png">
 		</a>
 		<div class="ui basic icon button mobile-only" id="navbar-expand-toggle">
 			<i class="sidebar icon"></i>
@@ -125,7 +125,7 @@
 					<i class="octicon octicon-person"></i> {{.i18n.Tr "register"}}
 				</a>
 			{{end}}
-			<a class="item{{if .PageIsSignIn}} active{{end}}" href="{{AppSubUrl}}/user/login?redirect_to={{.Link}}">
+			<a class="item{{if .PageIsSignIn}} active{{end}}" rel="nofollow" href="{{AppSubUrl}}/user/login?redirect_to={{.Link}}">
 				<i class="octicon octicon-sign-in"></i> {{.i18n.Tr "sign_in"}}
 			</a>
 		</div><!-- end anonymous right menu -->
diff --git a/templates/home.tmpl b/templates/home.tmpl
index f5eb455b55..6616e3c870 100644
--- a/templates/home.tmpl
+++ b/templates/home.tmpl
@@ -3,392 +3,51 @@
 	<div class="ui stackable middle very relaxed page grid">
 		<div class="sixteen wide center aligned centered column">
 			<div>
-				<img class="logo" src="{{AppSubUrl}}/img/gitea-lg.png" />
+				<img class="logo" src="{{StaticUrlPrefix}}/img/gitea-lg.png" />
 			</div>
 			<div class="hero">
 				<h1 class="ui icon header title">
 					{{AppName}}
 				</h1>
-				<h2>{{.i18n.Tr "app_desc"}}</h2>
+				<h2>{{.i18n.Tr "startpage.app_desc"}}</h2>
 			</div>
 		</div>
 	</div>
-	{{if eq .Lang "de-DE"}}
-		<div class="ui stackable middle very relaxed page grid">
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-flame"></i> Einfach zu installieren
-				</h1>
-				<p class="large">
-					Starte einfach <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-binary/">die Anwendung</a> für deine Plattform. Gitea gibt es auch für <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a>, <a target="_blank" rel="noopener noreferrer" href="https://github.com/alvaroaleman/ansible-gitea/blob/master/Vagrantfile">Vagrant</a> oder als <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-package/">Installationspaket</a>.
-				</p>
-			</div>
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-device-desktop"></i> Plattformübergreifend
-				</h1>
-				<p class="large">
-					Gitea läuft überall. <a target="_blank" rel="noopener noreferrer" href="http://golang.org/">Go</a> kompiliert für: Windows, macOS, Linux, ARM, etc. Wähle dasjenige System, was dir am meisten gefällt!
-				</p>
-			</div>
+	<div class="ui stackable middle very relaxed page grid">
+		<div class="eight wide center column">
+			<h1 class="hero ui icon header">
+				<i class="octicon octicon-flame"></i> {{.i18n.Tr "startpage.install"}}
+			</h1>
+			<p class="large">
+				{{.i18n.Tr "startpage.install_desc" | Str2html}}
+			</p>
 		</div>
-		<div class="ui stackable middle very relaxed page grid">
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-rocket"></i> Leichtgewicht
-				</h1>
-				<p class="large">
-					Gitea hat minimale Systemanforderungen und kann selbst auf einem günstigen und stromsparenden Raspberry Pi betrieben werden.
-				</p>
-			</div>
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-code"></i> Quelloffen
-				</h1>
-				<p class="large">
-					Der komplette Code befindet sich auf <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/">GitHub</a>! Unterstütze uns bei der Verbesserung dieses Projekts. Trau dich!
-				</p>
-			</div>
+		<div class="eight wide center column">
+			<h1 class="hero ui icon header">
+				<i class="octicon octicon-device-desktop"></i> {{.i18n.Tr "startpage.platform"}}
+			</h1>
+			<p class="large">
+				{{.i18n.Tr "startpage.platform_desc" | Str2html}}
+			</p>
 		</div>
-	{{else if eq .Lang "zh-TW"}}
-		<div class="ui stackable middle very relaxed page grid">
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-flame"></i> 易安裝
-				</h1>
-				<p class="large">
-					直接用 <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/zh-cn/install-from-binary/">執行檔安裝</a>,還可以透過 <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a> 或 <a target="_blank" rel="noopener noreferrer" href="https://github.com/alvaroaleman/ansible-gitea/blob/master/Vagrantfile">Vagrant</a>,以及 <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/zh-cn/install-from-package/">套件</a>安装。
-				</p>
-			</div>
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-device-desktop"></i> 跨平台
-				</h1>
-				<p class="large">
-					Gitea 可以運作在任何 <a target="_blank" rel="noopener noreferrer" href="http://golang.org/">Go 語言</a>能夠編譯的平台: Windows, macOS, Linux, ARM 等等。挑一個您喜歡的就好。
-				</p>
-			</div>
+	</div>
+	<div class="ui stackable middle very relaxed page grid">
+		<div class="eight wide center column">
+			<h1 class="hero ui icon header">
+				<i class="octicon octicon-rocket"></i> {{.i18n.Tr "startpage.lightweight"}}
+			</h1>
+			<p class="large">
+				{{.i18n.Tr "startpage.lightweight_desc" | Str2html}}
+			</p>
 		</div>
-		<div class="ui stackable middle very relaxed page grid">
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-rocket"></i> 輕量級
-				</h1>
-				<p class="large">
-					一個便宜的樹莓派 Raspberry Pi 就可以滿足 Gitea 的最低系統需求,節省機器資源!
-				</p>
-			</div>
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-code"></i> 開源化
-				</h1>
-				<p class="large">
-					所有程式碼都在 <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/">GitHub</a> 上,加入我們讓 Gitea 更好,別害羞,你可以做到的。
-				</p>
-			</div>
+		<div class="eight wide center column">
+			<h1 class="hero ui icon header">
+				<i class="octicon octicon-code"></i> {{.i18n.Tr "startpage.license"}}
+			</h1>
+			<p class="large">
+				{{.i18n.Tr "startpage.license_desc" | Str2html}}
+			</p>
 		</div>
-	{{else if eq .Lang "zh-CN"}}
-		<div class="ui stackable middle very relaxed page grid">
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-flame"></i> 易安装
-				</h1>
-				<p class="large">
-					您除了可以根据操作系统平台通过 <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/zh-cn/install-from-binary/">二进制运行</a>,还可以通过 <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a> 或 <a target="_blank" rel="noopener noreferrer" href="https://github.com/alvaroaleman/ansible-gitea/blob/master/Vagrantfile">Vagrant</a>,以及 <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/zh-cn/install-from-package/">包管理</a> 安装。
-				</p>
-			</div>
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-device-desktop"></i> 跨平台
-				</h1>
-				<p class="large">
-					任何 <a target="_blank" rel="noopener noreferrer" href="http://golang.org/">Go 语言</a> 支持的平台都可以运行 Gitea,包括 Windows、Mac、Linux 以及 ARM。挑一个您喜欢的就行!
-				</p>
-			</div>
-		</div>
-		<div class="ui stackable middle very relaxed page grid">
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-rocket"></i> 轻量级
-				</h1>
-				<p class="large">
-					一个廉价的树莓派的配置足以满足 Gitea 的最低系统硬件要求。最大程度上节省您的服务器资源!
-				</p>
-			</div>
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-code"></i> 开源化
-				</h1>
-				<p class="large">
-					所有的代码都开源在 <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/">GitHub</a> 上,赶快加入我们来共同发展这个伟大的项目!还等什么?成为贡献者吧!
-				</p>
-			</div>
-		</div>
-	{{else if eq .Lang "fr-FR"}}
-		<div class="ui stackable middle very relaxed page grid">
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-flame"></i> Facile à installer
-				</h1>
-				<p class="large">
-					Il suffit de <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-binary/">lancer l'exécutable</a> correspondant à votre système.
-					Ou d'utiliser Gitea avec <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a> ou
-					<a target="_blank" rel="noopener noreferrer" href="https://github.com/alvaroaleman/ansible-gitea/blob/master/Vagrantfile">Vagrant</a>
-					ou en l'installant depuis un <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-package/">package</a>.
-				</p>
-			</div>
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-device-desktop"></i> Multi-plateforme
-				</h1>
-				<p class="large">
-					Gitea tourne partout où <a target="_blank" rel="noopener noreferrer" href="http://golang.org/">Go</a> peut être compilé : Windows, macOS, Linux, ARM, etc. Choisissez votre préféré !
-				</p>
-			</div>
-		</div>
-		<div class="ui stackable middle very relaxed page grid">
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-rocket"></i> Léger
-				</h1>
-				<p class="large">
-					Gitea utilise peu de ressources. Il peut même tourner sur un Raspberry Pi très bon marché. Économisez l'énergie de vos serveurs !
-				</p>
-			</div>
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-code"></i> Open Source
-				</h1>
-				<p class="large">
-					Toutes les sources sont sur <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/">GitHub</a> ! Rejoignez-nous et contribuez à rendre ce projet encore meilleur.
-				</p>
-			</div>
-		</div>
-	{{else if eq .Lang "es-ES"}}
-		<div class="ui stackable middle very relaxed page grid">
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-flame"></i> Fácil de instalar
-				</h1>
-				<p class="large">
-					Simplemente <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-binary/">arranca el binario</a> para tu plataforma. O usa Gitea con <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a> o <a target="_blank" rel="noopener noreferrer" href="https://github.com/alvaroaleman/ansible-gitea/blob/master/Vagrantfile">Vagrant</a>, o utilice el <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-package/">paquete</a>.
-				</p>
-			</div>
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-device-desktop"></i> Multiplatforma
-				</h1>
-				<p class="large">
-					Gitea funciona en cualquier parte, <a target="_blank" rel="noopener noreferrer" href="http://golang.org/">Go</a> puede compilarse en: Windows, macOS, Linux, ARM, etc. !Elige tu favorita!
-				</p>
-			</div>
-		</div>
-		<div class="ui stackable middle very relaxed page grid">
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-rocket"></i> Ligero
-				</h1>
-				<p class="large">
-					Gitea tiene pocos requisitos y puede funcionar en una Raspberry Pi barata. !Ahorra energía!
-				</p>
-			</div>
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-code"></i> Open Source
-				</h1>
-				<p class="large">
-					¡Está todo en <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/">GitHub</a>! Uniros contribuyendo a hacer este proyecto todavía mejor. ¡No seas tímido y colabora!
-				</p>
-			</div>
-		</div>
-	{{else if eq .Lang "pt-BR"}}
-		<div class="ui stackable middle very relaxed page grid">
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-flame"></i> Fácil de instalar
-				</h1>
-				<p class="large">
-					Simplesmente <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-binary/">rode o executável</a> para o seu sistema operacional. Ou obtenha o Gitea com o <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a> ou <a target="_blank" rel="noopener noreferrer" href="https://github.com/alvaroaleman/ansible-gitea/blob/master/Vagrantfile">Vagrant</a>, ou baixe o <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-package/">pacote</a>.
-				</p>
-			</div>
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-device-desktop"></i> Multi-plataforma
-				</h1>
-				<p class="large">
-					Gitea roda em qualquer sistema operacional em que <a target="_blank" rel="noopener noreferrer" href="http://golang.org/">Go</a> consegue compilar: Windows, macOS, Linux, ARM, etc. Escolha qual você gosta mais!
-				</p>
-			</div>
-		</div>
-		<div class="ui stackable middle very relaxed page grid">
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-rocket"></i> Leve e rápido
-				</h1>
-				<p class="large">
-					Gitea utiliza poucos recursos e consegue mesmo rodar no barato Raspberry Pi. Economize energia elétrica da sua máquina!
-				</p>
-			</div>
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-code"></i> Código aberto
-				</h1>
-				<p class="large">
-					Está tudo no <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/">GitHub</a>! Contribua e torne este projeto ainda melhor. Não tenha vergonha de contribuir!
-				</p>
-			</div>
-		</div>
-	{{else if eq .Lang "ru-RU"}}
-		<div class="ui stackable middle very relaxed page grid">
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-flame"></i> Простой в установке
-				</h1>
-				<p class="large">
-					Просто <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-binary/">запустите исполняемый файл</a> для вашей платформы. Иcпользуйте Gitea с <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a> или <a target="_blank" rel="noopener noreferrer" href="https://github.com/alvaroaleman/ansible-gitea/blob/master/Vagrantfile">Vagrant</a>, или загрузите <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-package/">пакет</a>.
-				</p>
-			</div>
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-device-desktop"></i> Кроссплатформенный
-				</h1>
-				<p class="large">
-					Gitea работает на любой операционной системе, которая может компилировать <a target="_blank" rel="noopener noreferrer" href="http://golang.org/">Go</a>: Windows, macOS, Linux, ARM и т. д. Выбирайте, что вам больше нравится!
-				</p>
-			</div>
-		</div>
-		<div class="ui stackable middle very relaxed page grid">
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-rocket"></i> Легковесный
-				</h1>
-				<p class="large">
-					Gitea имеет низкие системные требования и может работать на недорогом Raspberry Pi. Экономьте энергию вашей машины!
-				</p>
-			</div>
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-code"></i> Открытый исходный код
-				</h1>
-				<p class="large">
-					Всё это на <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/">GitHub</a>! Присоединяйтесь к нам, внося вклад, чтобы сделать этот проект еще лучше. Не бойтесь помогать!
-				</p>
-			</div>
-		</div>
-	{{else if eq .Lang "nl-NL"}}
-		<div class="ui stackable middle very relaxed page grid">
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-flame"></i> Makkelijk te installeren
-				</h1>
-				<p class="large">
-					Je hoeft alleen maar de <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-binary/">binary</a> uit te voeren. Of gebruik Gitea met <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a>, <a target="_blank" rel="noopener noreferrer" href="https://github.com/alvaroaleman/ansible-gitea/blob/master/Vagrantfile">Vagrant</a>, of download een <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-package/">installatiepakket</a>.
-				</p>
-			</div>
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-device-desktop"></i> Cross-platform
-				</h1>
-				<p class="large">
-					Gitea werkt op alles waar <a target="_blank" rel="noopener noreferrer" href="http://golang.org/">Go</a> op kan compileren: Windows, macOS, Linux, ARM, etc. Kies het platform dat bij je past!
-				</p>
-			</div>
-		</div>
-		<div class="ui stackable middle very relaxed page grid">
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-rocket"></i> Lichtgewicht
-				</h1>
-				<p class="large">
-					Gitea heeft hele lage systeemeisen, je kunt Gitea al draaien op een goedkope Raspberry Pi.
-				</p>
-			</div>
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-code"></i> Open Source
-				</h1>
-				<p class="large">
-					Alles staat op <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/">GitHub</a>! Help ons door mee te bouwen aan Gitea, samen maken we dit project nog beter. Aarzel dus niet om een bijdrage te leveren!
-				</p>
-			</div>
-		</div>
-	{{else if eq .Lang "cs-CZ"}}
-		<div class="ui stackable middle very relaxed page grid">
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-flame"></i> Jednoduchá na instalaci
-				</h1>
-				<p class="large">
-					Jednoduše <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-binary/">spusťte binárku</a> pro vaší platformu. Je také k dispozici pro <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a> nebo <a target="_blank" rel="noopener noreferrer" href="https://github.com/alvaroaleman/ansible-gitea/blob/master/Vagrantfile">Vagrant</a>, nebo ji získejte z <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-package/">balíčku</a>.
-				</p>
-			</div>
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-device-desktop"></i> Multiplatformní
-				</h1>
-				<p class="large">
-					Gitea běží všude, kde <a target="_blank" rel="noopener noreferrer" href="http://golang.org/">Go</a> může kompilovat: Windows, macOS, Linux, ARM, atd. Vyberte si ten, který milujete!
-				</p>
-			</div>
-		</div>
-		<div class="ui stackable middle very relaxed page grid">
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-rocket"></i> Lehká
-				</h1>
-				<p class="large">
-					Gitea má minimální požadavky a může běžet na Raspberry Pi. Šetřete energii vašeho stroje!
-				</p>
-			</div>
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-code"></i> Open Source
-				</h1>
-				<p class="large">
-					Vše je na <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/">GitHubu</a>! Připojte se tím, že přispějete a uděláte tento projekt ještě lepší. Nestyďte se být přispěvatel!
-				</p>
-			</div>
-		</div>
-	{{else}}
-		<div class="ui stackable middle very relaxed page grid">
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-flame"></i> Easy to install
-				</h1>
-				<p class="large">
-					Simply <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-binary/">run the binary</a> for your platform. Or ship Gitea with <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea/tree/master/docker">Docker</a> or <a target="_blank" rel="noopener noreferrer" href="https://github.com/alvaroaleman/ansible-gitea/blob/master/Vagrantfile">Vagrant</a>, or get it <a target="_blank" rel="noopener noreferrer" href="https://docs.gitea.io/en-us/install-from-package/">packaged</a>.
-				</p>
-			</div>
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-device-desktop"></i> Cross-platform
-				</h1>
-				<p class="large">
-					Gitea runs anywhere <a target="_blank" rel="noopener noreferrer" href="http://golang.org/">Go</a> can compile for: Windows, macOS, Linux, ARM, etc. Choose the one you love!
-				</p>
-			</div>
-		</div>
-		<div class="ui stackable middle very relaxed page grid">
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-rocket"></i> Lightweight
-				</h1>
-				<p class="large">
-					Gitea has low minimal requirements and can run on an inexpensive Raspberry Pi. Save your machine energy!
-				</p>
-			</div>
-			<div class="eight wide center column">
-				<h1 class="hero ui icon header">
-					<i class="octicon octicon-code"></i> Open Source
-				</h1>
-				<p class="large">
-Go get <a target="_blank" rel="noopener noreferrer" href="https://code.gitea.io/gitea">code.gitea.io/gitea</a>!
-Join us by <a target="_blank" rel="noopener noreferrer" href="https://github.com/go-gitea/gitea">contributing</a>
-to make this project even better.
-Don't be shy to be a contributor!
-				</p>
-			</div>
-		</div>
-	{{end}}
+	</div>
 </div>
 {{template "base/footer" .}}
diff --git a/templates/mail/issue/assigned.tmpl b/templates/mail/issue/assigned.tmpl
new file mode 100644
index 0000000000..ab06ade1f4
--- /dev/null
+++ b/templates/mail/issue/assigned.tmpl
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+	<title>{{.Subject}}</title>
+</head>
+
+<body>
+	<p>@{{.Doer.Name}} assigned you to the {{if eq .Issue.IsPull true}}pull request{{else}}issue{{end}} <a href="{{.Link}}">#{{.Issue.Index}}</a> in repository {{.Issue.Repo.FullName}}.</p>
+    <p>
+        ---
+        <br>
+        <a href="{{.Link}}">View it on Gitea</a>.
+    </p>
+
+</body>
+</html>
diff --git a/templates/org/create.tmpl b/templates/org/create.tmpl
index 8ae0fc22af..82c8766a95 100644
--- a/templates/org/create.tmpl
+++ b/templates/org/create.tmpl
@@ -33,9 +33,9 @@
 						</div>
 					</div>
 
-					<div class="field" id="permission_box">
+					<div class="inline field" id="permission_box">
 						<label>{{.i18n.Tr "org.settings.permission"}}</label>
-						<div class="field">
+						<div class="inline-grouped-list">
 							<div class="ui checkbox">
 								<input class="hidden" type="checkbox" name="repo_admin_change_team_access" checked/>
 								<label>{{.i18n.Tr "org.settings.repoadminchangeteam"}}</label>
diff --git a/templates/org/settings/hook_new.tmpl b/templates/org/settings/hook_new.tmpl
index 5db91011d8..c83957dc6a 100644
--- a/templates/org/settings/hook_new.tmpl
+++ b/templates/org/settings/hook_new.tmpl
@@ -10,19 +10,19 @@
 					{{if .PageIsSettingsHooksNew}}{{.i18n.Tr "repo.settings.add_webhook"}}{{else}}{{.i18n.Tr "repo.settings.update_webhook"}}{{end}}
 					<div class="ui right">
 						{{if eq .HookType "gitea"}}
-							<img class="img-13" src="{{AppSubUrl}}/img/gitea-sm.png">
+							<img class="img-13" src="{{StaticUrlPrefix}}/img/gitea-sm.png">
 						{{else if eq .HookType "gogs"}}
-							<img class="img-13" src="{{AppSubUrl}}/img/gogs.ico">
+							<img class="img-13" src="{{StaticUrlPrefix}}/img/gogs.ico">
 						{{else if eq .HookType "slack"}}
-							<img class="img-13" src="{{AppSubUrl}}/img/slack.png">
+							<img class="img-13" src="{{StaticUrlPrefix}}/img/slack.png">
 						{{else if eq .HookType "discord"}}
-							<img class="img-13" src="{{AppSubUrl}}/img/discord.png">
+							<img class="img-13" src="{{StaticUrlPrefix}}/img/discord.png">
 						{{else if eq .HookType "dingtalk"}}
-							<img class="img-13" src="{{AppSubUrl}}/img/dingtalk.png">
+							<img class="img-13" src="{{StaticUrlPrefix}}/img/dingtalk.png">
 						{{else if eq .HookType "telegram"}}
-							<img class="img-13" src="{{AppSubUrl}}/img/telegram.png">
+							<img class="img-13" src="{{StaticUrlPrefix}}/img/telegram.png">
 						{{else if eq .HookType "msteams"}}
-							<img class="img-13" src="{{AppSubUrl}}/img/msteams.png">
+							<img class="img-13" src="{{StaticUrlPrefix}}/img/msteams.png">
 						{{end}}
 					</div>
 				</h4>
diff --git a/templates/pwa/manifest_json.tmpl b/templates/pwa/manifest_json.tmpl
index 557bee5d76..793b42f2e1 100644
--- a/templates/pwa/manifest_json.tmpl
+++ b/templates/pwa/manifest_json.tmpl
@@ -3,22 +3,22 @@
     "name": "Gitea - Git with a cup of tea",
     "icons": [
       {
-        "src": "{{AppSubUrl}}/img/gitea-lg.png",
+        "src": "{{StaticUrlPrefix}}/img/gitea-lg.png",
         "type": "image/png",
         "sizes": "880x880"
       },
       {
-        "src": "{{AppSubUrl}}/img/gitea-sm.png",
+        "src": "{{StaticUrlPrefix}}/img/gitea-sm.png",
         "type": "image/png",
         "sizes": "120x120"
       },
       {
-        "src": "{{AppSubUrl}}/img/gitea-512.png",
+        "src": "{{StaticUrlPrefix}}/img/gitea-512.png",
         "type": "image/png",
         "sizes": "512x512"
       },
       {
-        "src": "{{AppSubUrl}}/img/gitea-192.png",
+        "src": "{{StaticUrlPrefix}}/img/gitea-192.png",
         "type": "image/png",
         "sizes": "192x192"
       }
@@ -28,4 +28,4 @@
     "background_color": "#FAFAFA",
     "display": "standalone",
     "theme_color": "{{ThemeColorMetaTag}}"
-  }
\ No newline at end of file
+  }
diff --git a/templates/pwa/serviceworker_js.tmpl b/templates/pwa/serviceworker_js.tmpl
index ee96cd5f68..5a2756cf24 100644
--- a/templates/pwa/serviceworker_js.tmpl
+++ b/templates/pwa/serviceworker_js.tmpl
@@ -1,55 +1,55 @@
 var STATIC_CACHE = 'static-cache-v1';
 var urlsToCache = [
   // js
-  '{{AppSubUrl}}/vendor/plugins/jquery.areyousure/jquery.are-you-sure.js',
-  '{{AppSubUrl}}/vendor/plugins/jquery/jquery.min.js?v=3.4.1',
-  '{{AppSubUrl}}/vendor/plugins/jquery-migrate/jquery-migrate.min.js?v=3.0.1',
-  '{{AppSubUrl}}/vendor/plugins/semantic/semantic.min.js',
-  '{{AppSubUrl}}/js/index.js?v={{MD5 AppVer}}',
-  '{{AppSubUrl}}/js/draw.js',
-  '{{AppSubUrl}}/vendor/plugins/clipboard/clipboard.min.js',
-  '{{AppSubUrl}}/vendor/plugins/gitgraph/gitgraph.js',
-  '{{AppSubUrl}}/vendor/plugins/vue/vue.min.js',
-  '{{AppSubUrl}}/vendor/plugins/emojify/emojify.min.js',
-  '{{AppSubUrl}}/vendor/plugins/cssrelpreload/loadCSS.min.js',
-  '{{AppSubUrl}}/vendor/plugins/cssrelpreload/cssrelpreload.min.js',
-  '{{AppSubUrl}}/vendor/plugins/dropzone/dropzone.js',
-  '{{AppSubUrl}}/vendor/plugins/highlight/highlight.pack.js',
-  '{{AppSubUrl}}/vendor/plugins/jquery.datetimepicker/jquery.datetimepicker.js',
-  '{{AppSubUrl}}/vendor/plugins/jquery.minicolors/jquery.minicolors.min.js',
-  '{{AppSubUrl}}/vendor/plugins/codemirror/addon/mode/loadmode.js',
-  '{{AppSubUrl}}/vendor/plugins/codemirror/mode/meta.js',
-  '{{AppSubUrl}}/vendor/plugins/simplemde/simplemde.min.js',
+  '{{StaticUrlPrefix}}/vendor/plugins/jquery.areyousure/jquery.are-you-sure.js',
+  '{{StaticUrlPrefix}}/vendor/plugins/jquery/jquery.min.js?v=3.4.1',
+  '{{StaticUrlPrefix}}/vendor/plugins/jquery-migrate/jquery-migrate.min.js?v=3.0.1',
+  '{{StaticUrlPrefix}}/vendor/plugins/semantic/semantic.min.js',
+  '{{StaticUrlPrefix}}/js/index.js?v={{MD5 AppVer}}',
+  '{{StaticUrlPrefix}}/js/draw.js',
+  '{{StaticUrlPrefix}}/vendor/plugins/clipboard/clipboard.min.js',
+  '{{StaticUrlPrefix}}/vendor/plugins/gitgraph/gitgraph.js',
+  '{{StaticUrlPrefix}}/vendor/plugins/vue/vue.min.js',
+  '{{StaticUrlPrefix}}/vendor/plugins/emojify/emojify.min.js',
+  '{{StaticUrlPrefix}}/vendor/plugins/cssrelpreload/loadCSS.min.js',
+  '{{StaticUrlPrefix}}/vendor/plugins/cssrelpreload/cssrelpreload.min.js',
+  '{{StaticUrlPrefix}}/vendor/plugins/dropzone/dropzone.js',
+  '{{StaticUrlPrefix}}/vendor/plugins/highlight/highlight.pack.js',
+  '{{StaticUrlPrefix}}/vendor/plugins/jquery.datetimepicker/jquery.datetimepicker.js',
+  '{{StaticUrlPrefix}}/vendor/plugins/jquery.minicolors/jquery.minicolors.min.js',
+  '{{StaticUrlPrefix}}/vendor/plugins/codemirror/addon/mode/loadmode.js',
+  '{{StaticUrlPrefix}}/vendor/plugins/codemirror/mode/meta.js',
+  '{{StaticUrlPrefix}}/vendor/plugins/simplemde/simplemde.min.js',
 
   // css
-  '{{AppSubUrl}}/vendor/assets/font-awesome/css/font-awesome.min.css',
-  '{{AppSubUrl}}/vendor/assets/octicons/octicons.min.css',
-  '{{AppSubUrl}}/vendor/plugins/simplemde/simplemde.min.css',
-  '{{AppSubUrl}}/vendor/plugins/gitgraph/gitgraph.css',
-  '{{AppSubUrl}}/vendor/plugins/tribute/tribute.css',
-  '{{AppSubUrl}}/vendor/plugins/semantic/semantic.min.css',
-  '{{AppSubUrl}}/css/index.css?v={{MD5 AppVer}}',
-  '{{AppSubUrl}}/vendor/plugins/highlight/github.css',
-  '{{AppSubUrl}}/vendor/plugins/jquery.minicolors/jquery.minicolors.css',
-  '{{AppSubUrl}}/vendor/plugins/jquery.datetimepicker/jquery.datetimepicker.css',
-  '{{AppSubUrl}}/vendor/plugins/dropzone/dropzone.css',
+  '{{StaticUrlPrefix}}/vendor/assets/font-awesome/css/font-awesome.min.css',
+  '{{StaticUrlPrefix}}/vendor/assets/octicons/octicons.min.css',
+  '{{StaticUrlPrefix}}/vendor/plugins/simplemde/simplemde.min.css',
+  '{{StaticUrlPrefix}}/vendor/plugins/gitgraph/gitgraph.css',
+  '{{StaticUrlPrefix}}/vendor/plugins/tribute/tribute.css',
+  '{{StaticUrlPrefix}}/vendor/plugins/semantic/semantic.min.css',
+  '{{StaticUrlPrefix}}/css/index.css?v={{MD5 AppVer}}',
+  '{{StaticUrlPrefix}}/vendor/plugins/highlight/github.css',
+  '{{StaticUrlPrefix}}/vendor/plugins/jquery.minicolors/jquery.minicolors.css',
+  '{{StaticUrlPrefix}}/vendor/plugins/jquery.datetimepicker/jquery.datetimepicker.css',
+  '{{StaticUrlPrefix}}/vendor/plugins/dropzone/dropzone.css',
 {{if .IsSigned }}
 	{{ if ne .SignedUser.Theme "gitea" }}
-		'{{AppSubUrl}}/css/theme-{{.SignedUser.Theme}}.css',
+		'{{StaticUrlPrefix}}/css/theme-{{.SignedUser.Theme}}.css',
 	{{end}}
 {{else if ne DefaultTheme "gitea"}}
-	'{{AppSubUrl}}/css/theme-{{DefaultTheme}}.css',
+	'{{StaticUrlPrefix}}/css/theme-{{DefaultTheme}}.css',
 {{end}}
 
   // img
-  '{{AppSubUrl}}/img/gitea-sm.png',
-  '{{AppSubUrl}}/img/gitea-lg.png',
+  '{{StaticUrlPrefix}}/img/gitea-sm.png',
+  '{{StaticUrlPrefix}}/img/gitea-lg.png',
 
   // fonts
-  '{{AppSubUrl}}/vendor/plugins/semantic/themes/default/assets/fonts/icons.woff2',
-  '{{AppSubUrl}}/vendor/assets/octicons/octicons.woff2?ef21c39f0ca9b1b5116e5eb7ac5eabe6',
-  '{{AppSubUrl}}/vendor/assets/lato-fonts/lato-v14-latin-regular.woff2',
-  '{{AppSubUrl}}/vendor/assets/lato-fonts/lato-v14-latin-700.woff2'
+  '{{StaticUrlPrefix}}/vendor/plugins/semantic/themes/default/assets/fonts/icons.woff2',
+  '{{StaticUrlPrefix}}/vendor/assets/octicons/octicons.woff2?ef21c39f0ca9b1b5116e5eb7ac5eabe6',
+  '{{StaticUrlPrefix}}/vendor/assets/lato-fonts/lato-v14-latin-regular.woff2',
+  '{{StaticUrlPrefix}}/vendor/assets/lato-fonts/lato-v14-latin-700.woff2'
 ];
 
 self.addEventListener('install', function (event) {
diff --git a/templates/repo/branch/list.tmpl b/templates/repo/branch/list.tmpl
index 9c53f4e67a..26493298da 100644
--- a/templates/repo/branch/list.tmpl
+++ b/templates/repo/branch/list.tmpl
@@ -75,7 +75,11 @@
 									</td>
 									<td class="two wide right aligned">
 										{{if not .LatestPullRequest}}
-											{{if and (not .IsDeleted) $.AllowsPulls (gt .CommitsAhead 0)}}
+											{{if .IsIncluded}}
+												<a class="ui poping up orange small label" data-content="{{$.i18n.Tr "repo.branch.included_desc"}}" data-variation="tiny inverted" data-position="top right">
+													<i class="octicon octicon-git-pull-request"></i> {{$.i18n.Tr "repo.branch.included"}}
+												</a>
+											{{else if and (not .IsDeleted) $.AllowsPulls (gt .CommitsAhead 0)}}
 											<a href="{{$.RepoLink}}/compare/{{$.DefaultBranch | EscapePound}}...{{if ne $.Repository.Owner.Name $.Owner.Name}}{{$.Owner.Name}}:{{end}}{{.Name | EscapePound}}">
 												<button id="new-pull-request" class="ui compact basic button">{{$.i18n.Tr "repo.pulls.compare_changes"}}</button>
 											</a>
diff --git a/templates/repo/commit_page.tmpl b/templates/repo/commit_page.tmpl
index 0cfdf5156d..5b19523cf2 100644
--- a/templates/repo/commit_page.tmpl
+++ b/templates/repo/commit_page.tmpl
@@ -26,6 +26,16 @@
 						<img class="ui avatar image" src="{{AvatarLink .Commit.Author.Email}}" />
 						<strong>{{.Commit.Author.Name}}</strong>
 					{{end}}
+					{{if or (ne .Commit.Committer.Name .Commit.Author.Name) (ne .Commit.Committer.Email .Commit.Author.Email)}}
+						<span> </span>
+						{{if ne .Verification.CommittingUser.ID 0}}
+							<img class="ui avatar image" src="{{.Verification.CommittingUser.RelAvatarLink}}" />
+							<a href="{{.Verification.CommittingUser.HomeLink}}"><strong>{{.Commit.Committer.Name}}</strong></a> <{{.Commit.Committer.Email}}>
+						{{else}}
+							<img class="ui avatar image" src="{{AvatarLink .Commit.Committer.Email}}" />
+							<strong>{{.Commit.Committer.Name}}</strong>
+						{{end}}
+					{{end}}
 					<span class="text grey" id="authored-time">{{TimeSince .Commit.Author.When $.Lang}}</span>
 				</div>
 				<div class="seven wide right aligned column">
@@ -50,15 +60,36 @@
 		{{if .Commit.Signature}}
 			{{if .Verification.Verified }}
 				<div class="ui bottom attached positive message">
-				  <i class="green lock icon"></i>
-					<span>{{.i18n.Tr "repo.commits.signed_by"}}:</span>
-					<a href="{{.Verification.SigningUser.HomeLink}}"><strong>{{.Commit.Committer.Name}}</strong></a> <{{.Commit.Committer.Email}}>
-					<span class="pull-right"><span>{{.i18n.Tr "repo.commits.gpg_key_id"}}:</span> {{.Verification.SigningKey.KeyID}}</span>
+					{{if ne .Verification.SigningUser.ID 0}}
+						<i class="green lock icon"></i>
+						<span>{{.i18n.Tr "repo.commits.signed_by"}}:</span>
+						<img class="ui avatar image" src="{{.Verification.SigningUser.RelAvatarLink}}" />
+						<a href="{{.Verification.SigningUser.HomeLink}}"><strong>{{.Verification.SigningUser.Name}}</strong></a> <{{.Verification.SigningEmail}}>
+						<span class="pull-right"><span>{{.i18n.Tr "repo.commits.gpg_key_id"}}:</span> {{.Verification.SigningKey.KeyID}}</span>
+					{{else}}
+						<i class="icons" title="{{.i18n.Tr "gpg.default_key"}}">
+							<i class="green lock icon"></i>
+							<i class="tiny inverted cog icon centerlock"></i>
+						</i>
+						<span>{{.i18n.Tr "repo.commits.signed_by"}}:</span>
+						<img class="ui avatar image" src="{{AvatarLink .Verification.SigningEmail}}" />
+						<strong>{{.Verification.SigningUser.Name}}</strong> <{{.Verification.SigningEmail}}>
+						<span class="pull-right"><span>{{.i18n.Tr "repo.commits.gpg_key_id"}}:</span> <i class="cogs icon" title="{{.i18n.Tr "gpg.default_key"}}"></i>{{.Verification.SigningKey.KeyID}}</span>
+					{{end}}
+				</div>
+			{{else if .Verification.Warning}}
+				<div class="ui bottom attached message">
+				  <i class="red unlock icon"></i>
+				  <span class="red text">{{.i18n.Tr .Verification.Reason}}</span>
+				  <span class="pull-right"><span class="red text">{{.i18n.Tr "repo.commits.gpg_key_id"}}:</span> <i class="red warning icon"></i>{{.Verification.SigningKey.KeyID}}</span>
 				</div>
 			{{else}}
 				<div class="ui bottom attached message">
 				  <i class="grey unlock icon"></i>
 				  {{.i18n.Tr .Verification.Reason}}
+				  {{if and .Verification.SigningKey (ne .Verification.SigningKey.KeyID "")}}
+					<span class="pull-right"><span class="red text">{{.i18n.Tr "repo.commits.gpg_key_id"}}:</span> <i class="red warning icon"></i>{{.Verification.SigningKey.KeyID}}</span>
+				  {{end}}
 				</div>
 			{{end}}
 		{{end}}
diff --git a/templates/repo/commits_table.tmpl b/templates/repo/commits_table.tmpl
index e11bbee0e8..09a2c072b1 100644
--- a/templates/repo/commits_table.tmpl
+++ b/templates/repo/commits_table.tmpl
@@ -56,12 +56,21 @@
 							{{end}}
 						</td>
 						<td class="sha">
-							<a rel="nofollow" class="ui sha label {{if .Signature}} isSigned {{if .Verification.Verified }} isVerified {{end}}{{end}}" href="{{AppSubUrl}}/{{$.Username}}/{{$.Reponame}}/commit/{{.ID}}">
+							<a rel="nofollow" class="ui sha label {{if .Signature}} isSigned {{if .Verification.Verified }} isVerified {{else if .Verification.Warning}} isWarning {{end}}{{end}}" href="{{AppSubUrl}}/{{$.Username}}/{{$.Reponame}}/commit/{{.ID}}">
 								{{ShortSha .ID.String}}
 								{{if .Signature}}
 									<div class="ui detail icon button">
 										{{if .Verification.Verified}}
-											<i title="{{.Verification.Reason}}" class="lock green icon"></i>
+											{{if ne .Verification.SigningUser.ID 0}}
+												<i title="{{.Verification.Reason}}" class="lock green icon"></i>
+											{{else}}
+												<i title="{{.Verification.Reason}}" class="icons">
+													<i class="green lock icon"></i>
+													<i class="tiny inverted cog icon centerlock"></i>
+												</i>
+											{{end}}
+										{{else if .Verification.Warning}}
+											<i title="{{$.i18n.Tr .Verification.Reason}}" class="red unlock icon"></i>
 										{{else}}
 											<i title="{{$.i18n.Tr .Verification.Reason}}" class="unlock icon"></i>
 										{{end}}
diff --git a/templates/repo/diff/box.tmpl b/templates/repo/diff/box.tmpl
index b5fde36a6f..a12fc9bd4b 100644
--- a/templates/repo/diff/box.tmpl
+++ b/templates/repo/diff/box.tmpl
@@ -7,7 +7,7 @@
 				{{else}}
 					<a class="ui tiny basic toggle button" href="?style={{if .IsSplitStyle}}unified{{else}}split{{end}}">{{ if .IsSplitStyle }}{{.i18n.Tr "repo.diff.show_unified_view"}}{{else}}{{.i18n.Tr "repo.diff.show_split_view"}}{{end}}</a>
 				{{end}}
-				<a class="ui tiny basic toggle button" data-target="#diff-files">{{.i18n.Tr "repo.diff.show_diff_stats"}}</a>
+				{{template "repo/diff/options_dropdown" .}}
 				{{if and .PageIsPullFiles $.SignedUserID (not .IsArchived)}}
 					{{template "repo/diff/new_review" .}}
 				{{end}}
@@ -16,8 +16,8 @@
 	</div>
 	<h4>{{.i18n.Tr "repo.diff.data_not_available"}}</h4>
 {{else}}
-	<div class="diff-detail-box diff-box ui sticky">
-		<div>
+	<div>
+		<div class="diff-detail-box diff-box ui sticky">
 			<i class="fa fa-retweet"></i>
 			{{.i18n.Tr "repo.diff.stats_desc" .Diff.NumFiles .Diff.TotalAddition .Diff.TotalDeletion | Str2html}}
 			<div class="ui right">
@@ -26,23 +26,23 @@
 				{{else}}
 					<a class="ui tiny basic toggle button" href="?style={{if .IsSplitStyle}}unified{{else}}split{{end}}">{{ if .IsSplitStyle }}{{.i18n.Tr "repo.diff.show_unified_view"}}{{else}}{{.i18n.Tr "repo.diff.show_split_view"}}{{end}}</a>
 				{{end}}
-				<a class="ui tiny basic toggle button" data-target="#diff-files">{{.i18n.Tr "repo.diff.show_diff_stats"}}</a>
+				{{template "repo/diff/options_dropdown" .}}
 				{{if and .PageIsPullFiles $.SignedUserID (not .IsArchived)}}
 					{{template "repo/diff/new_review" .}}
 				{{end}}
 			</div>
 		</div>
-		<ol class="detail-files hide" id="diff-files">
+		<ol class="diff-detail-box diff-stats detail-files hide" id="diff-files">
 			{{range .Diff.Files}}
 				<li>
 					<div class="diff-counter count pull-right">
 						{{if not .IsBin}}
-							<span class="add" data-line="{{.Addition}}">{{.Addition}}</span>
+							<span class="add" data-line="{{.Addition}}">+{{.Addition}}</span>
 							<span class="bar">
 								<div class="pull-left add"></div>
 								<div class="pull-left del"></div>
 							</span>
-							<span class="del" data-line="{{.Deletion}}">{{.Deletion}}</span>
+							<span class="del" data-line="{{.Deletion}}">-{{.Deletion}}</span>
 						{{else}}
 							<span>{{$.i18n.Tr "repo.diff.bin"}}</span>
 						{{end}}
@@ -53,188 +53,187 @@
 				</li>
 			{{end}}
 		</ol>
-	</div>
-
-	{{range $i, $file := .Diff.Files}}
-		{{if $file.IsIncomplete}}
-			<div class="diff-file-box diff-box file-content">
-				<h4 class="ui top attached normal header rounded">
-					<div class="diff-counter count ui left">
-						{{if not $file.IsRenamed}}
-							<span class="add" data-line="{{.Addition}}">+ {{.Addition}}</span>
-							<span class="bar">
-								<div class="pull-left add"></div>
-								<div class="pull-left del"></div>
-							</span>
-							<span class="del" data-line="{{.Deletion}}">- {{.Deletion}}</span>
+		{{range $i, $file := .Diff.Files}}
+			{{if $file.IsIncomplete}}
+				<div class="diff-file-box diff-box file-content">
+					<h4 class="ui top attached normal header rounded">
+						<div class="diff-counter count ui left">
+							{{if not $file.IsRenamed}}
+								<span class="add" data-line="{{.Addition}}">+ {{.Addition}}</span>
+								<span class="bar">
+									<div class="pull-left add"></div>
+									<div class="pull-left del"></div>
+								</span>
+								<span class="del" data-line="{{.Deletion}}">- {{.Deletion}}</span>
+							{{end}}
+						</div>
+						<span class="file">{{$file.Name}}</span>
+						<div>{{$.i18n.Tr "repo.diff.file_suppressed"}}</div>
+						{{if not $file.IsSubmodule}}
+							{{if $file.IsDeleted}}
+								<a class="ui basic grey tiny button" rel="nofollow" href="{{EscapePound $.BeforeSourcePath}}/{{EscapePound .Name}}">{{$.i18n.Tr "repo.diff.view_file"}}</a>
+							{{else}}
+								<a class="ui basic grey tiny button" rel="nofollow" href="{{EscapePound $.SourcePath}}/{{EscapePound .Name}}">{{$.i18n.Tr "repo.diff.view_file"}}</a>
+							{{end}}
 						{{end}}
-					</div>
-					<span class="file">{{$file.Name}}</span>
-					<div>{{$.i18n.Tr "repo.diff.file_suppressed"}}</div>
-					{{if not $file.IsSubmodule}}
-						{{if $file.IsDeleted}}
-							<a class="ui basic grey tiny button" rel="nofollow" href="{{EscapePound $.BeforeSourcePath}}/{{EscapePound .Name}}">{{$.i18n.Tr "repo.diff.view_file"}}</a>
-						{{else}}
-							<a class="ui basic grey tiny button" rel="nofollow" href="{{EscapePound $.SourcePath}}/{{EscapePound .Name}}">{{$.i18n.Tr "repo.diff.view_file"}}</a>
+					</h4>
+				</div>
+			{{else}}
+				<div class="diff-file-box diff-box file-content {{TabSizeClass $.Editorconfig $file.Name}}" id="diff-{{.Index}}">
+					<h4 class="ui top attached normal header">
+						<div class="diff-counter count">
+							{{if $file.IsBin}}
+								{{$.i18n.Tr "repo.diff.bin"}}
+							{{else if not $file.IsRenamed}}
+								<span class="add" data-line="{{.Addition}}">+ {{.Addition}}</span>
+								<span class="bar">
+									<div class="pull-left add"></div>
+									<div class="pull-left del"></div>
+								</span>
+								<span class="del" data-line="{{.Deletion}}">- {{.Deletion}}</span>
+							{{end}}
+						</div>
+						<span class="file">{{if $file.IsRenamed}}{{$file.OldName}} &rarr; {{end}}{{$file.Name}}{{if .IsLFSFile}} ({{$.i18n.Tr "repo.stored_lfs"}}){{end}}</span>
+						{{if not $file.IsSubmodule}}
+							{{if $file.IsDeleted}}
+								<a class="ui basic grey tiny button" rel="nofollow" href="{{EscapePound $.BeforeSourcePath}}/{{EscapePound .Name}}">{{$.i18n.Tr "repo.diff.view_file"}}</a>
+							{{else}}
+								<a class="ui basic grey tiny button" rel="nofollow" href="{{EscapePound $.SourcePath}}/{{EscapePound .Name}}">{{$.i18n.Tr "repo.diff.view_file"}}</a>
+							{{end}}
 						{{end}}
-					{{end}}
-				</h4>
-			</div>
-		{{else}}
-			<div class="diff-file-box diff-box file-content {{TabSizeClass $.Editorconfig $file.Name}}" id="diff-{{.Index}}">
-				<h4 class="ui top attached normal header">
-					<div class="diff-counter count">
-						{{if $file.IsBin}}
-							{{$.i18n.Tr "repo.diff.bin"}}
-						{{else if not $file.IsRenamed}}
-							<span class="add" data-line="{{.Addition}}">+ {{.Addition}}</span>
-							<span class="bar">
-								<div class="pull-left add"></div>
-								<div class="pull-left del"></div>
-							</span>
-							<span class="del" data-line="{{.Deletion}}">- {{.Deletion}}</span>
-						{{end}}
-					</div>
-					<span class="file">{{if $file.IsRenamed}}{{$file.OldName}} &rarr; {{end}}{{$file.Name}}{{if .IsLFSFile}} ({{$.i18n.Tr "repo.stored_lfs"}}){{end}}</span>
-					{{if not $file.IsSubmodule}}
-						{{if $file.IsDeleted}}
-							<a class="ui basic grey tiny button" rel="nofollow" href="{{EscapePound $.BeforeSourcePath}}/{{EscapePound .Name}}">{{$.i18n.Tr "repo.diff.view_file"}}</a>
-						{{else}}
-							<a class="ui basic grey tiny button" rel="nofollow" href="{{EscapePound $.SourcePath}}/{{EscapePound .Name}}">{{$.i18n.Tr "repo.diff.view_file"}}</a>
-						{{end}}
-					{{end}}
-				</h4>
-				<div class="ui attached unstackable table segment">
-					{{if ne $file.Type 4}}
-						{{$isImage := false}}
-						{{if $file.IsDeleted}}
-							{{$isImage = (call $.IsImageFileInBase $file.Name)}}
-						{{else}}
-							{{$isImage = (call $.IsImageFileInHead $file.Name)}}
-						{{end}}
-						<div class="file-body file-code code-view code-diff {{if $.IsSplitStyle}}code-diff-split{{else}}code-diff-unified{{end}}">
-							<table>
-								<tbody>
-									{{if $isImage}}
-										{{template "repo/diff/image_diff" dict "file" . "root" $}}
-									{{else}}
-										{{if $.IsSplitStyle}}
-											{{$highlightClass := $file.GetHighlightClass}}
-											{{range $j, $section := $file.Sections}}
-												{{range $k, $line := $section.Lines}}
-													<tr class="{{DiffLineTypeToStr .GetType}}-code nl-{{$k}} ol-{{$k}}">
-														<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{Sha1 $file.Name}}L{{$line.LeftIdx}}{{end}}"></span></td>
-														<td class="lines-type-marker lines-type-marker-old">{{if $line.LeftIdx}}<span class="mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span>{{end}}</td>
-														<td class="lines-code lines-code-old halfwidth">{{if and $.SignedUserID $line.CanComment $.PageIsPullFiles (not (eq .GetType 2))}}<a class="ui green button add-code-comment add-code-comment-left" data-path="{{$file.Name}}" data-side="left" data-idx="{{$line.LeftIdx}}">+</a>{{end}}<span class="mono wrap{{if $highlightClass}} language-{{$highlightClass}}{{else}} nohighlight{{end}}">{{if $line.LeftIdx}}{{$section.GetComputedInlineDiffFor $line}}{{end}}</span></td>
-														<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{Sha1 $file.Name}}R{{$line.RightIdx}}{{end}}"></span></td>
-														<td class="lines-type-marker lines-type-marker-new">{{if $line.RightIdx}}<span class="mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span>{{end}}</td>
-														<td class="lines-code lines-code-new halfwidth">{{if and $.SignedUserID $line.CanComment $.PageIsPullFiles (not (eq .GetType 3))}}<a class="ui green button add-code-comment add-code-comment-right" data-path="{{$file.Name}}" data-side="right" data-idx="{{$line.RightIdx}}">+</a>{{end}}<span class="mono wrap{{if $highlightClass}} language-{{$highlightClass}}{{else}} nohighlight{{end}}">{{if $line.RightIdx}}{{$section.GetComputedInlineDiffFor $line}}{{end}}</span></td>
-													</tr>
-													{{if gt (len $line.Comments) 0}}
-														<tr class="add-code-comment">
-															<td class="lines-num"></td>
-															<td class="lines-type-marker"></td>
-															<td class="add-comment-left">
-																{{if eq $line.GetCommentSide "previous"}}
-																	<div class="field comment-code-cloud">
-																		<div class="comment-list">
-																			<ui class="ui comments">
-																			{{ template "repo/diff/comments" dict "root" $ "comments" $line.Comments}}
-																			</ui>
-																		</div>
-																	{{template "repo/diff/comment_form_datahandler" dict "reply" (index $line.Comments 0).ReviewID "hidden" true "root" $ "comment" (index $line.Comments 0)}}
-																	</div>
-																{{end}}
-															</td>
-															<td class="lines-num"></td>
-															<td class="lines-type-marker"></td>
-															<td class="add-comment-right">
-																{{if eq $line.GetCommentSide "proposed"}}
-																	<div class="field comment-code-cloud">
-																		<div class="comment-list">
-																			<ui class="ui comments">
-																			{{ template "repo/diff/comments" dict "root" $ "comments" $line.Comments}}
-																			</ui>
-																		</div>
-																		{{template "repo/diff/comment_form_datahandler" dict "reply" (index $line.Comments 0).ReviewID "hidden" true "root" $ "comment" (index $line.Comments 0)}}
-																	</div>
-																{{end}}
-															</td>
+					</h4>
+					<div class="ui attached unstackable table segment">
+						{{if ne $file.Type 4}}
+							{{$isImage := false}}
+							{{if $file.IsDeleted}}
+								{{$isImage = (call $.IsImageFileInBase $file.Name)}}
+							{{else}}
+								{{$isImage = (call $.IsImageFileInHead $file.Name)}}
+							{{end}}
+							<div class="file-body file-code code-view code-diff {{if $.IsSplitStyle}}code-diff-split{{else}}code-diff-unified{{end}}">
+								<table>
+									<tbody>
+										{{if $isImage}}
+											{{template "repo/diff/image_diff" dict "file" . "root" $}}
+										{{else}}
+											{{if $.IsSplitStyle}}
+												{{$highlightClass := $file.GetHighlightClass}}
+												{{range $j, $section := $file.Sections}}
+													{{range $k, $line := $section.Lines}}
+														<tr class="{{DiffLineTypeToStr .GetType}}-code nl-{{$k}} ol-{{$k}}">
+															<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{Sha1 $file.Name}}L{{$line.LeftIdx}}{{end}}"></span></td>
+															<td class="lines-type-marker lines-type-marker-old">{{if $line.LeftIdx}}<span class="mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span>{{end}}</td>
+															<td class="lines-code lines-code-old halfwidth">{{if and $.SignedUserID $line.CanComment $.PageIsPullFiles (not (eq .GetType 2))}}<a class="ui green button add-code-comment add-code-comment-left" data-path="{{$file.Name}}" data-side="left" data-idx="{{$line.LeftIdx}}">+</a>{{end}}<span class="mono wrap{{if $highlightClass}} language-{{$highlightClass}}{{else}} nohighlight{{end}}">{{if $line.LeftIdx}}{{$section.GetComputedInlineDiffFor $line}}{{end}}</span></td>
+															<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{Sha1 $file.Name}}R{{$line.RightIdx}}{{end}}"></span></td>
+															<td class="lines-type-marker lines-type-marker-new">{{if $line.RightIdx}}<span class="mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span>{{end}}</td>
+															<td class="lines-code lines-code-new halfwidth">{{if and $.SignedUserID $line.CanComment $.PageIsPullFiles (not (eq .GetType 3))}}<a class="ui green button add-code-comment add-code-comment-right" data-path="{{$file.Name}}" data-side="right" data-idx="{{$line.RightIdx}}">+</a>{{end}}<span class="mono wrap{{if $highlightClass}} language-{{$highlightClass}}{{else}} nohighlight{{end}}">{{if $line.RightIdx}}{{$section.GetComputedInlineDiffFor $line}}{{end}}</span></td>
 														</tr>
+														{{if gt (len $line.Comments) 0}}
+															<tr class="add-code-comment">
+																<td class="lines-num"></td>
+																<td class="lines-type-marker"></td>
+																<td class="add-comment-left">
+																	{{if eq $line.GetCommentSide "previous"}}
+																		<div class="field comment-code-cloud">
+																			<div class="comment-list">
+																				<ui class="ui comments">
+																				{{ template "repo/diff/comments" dict "root" $ "comments" $line.Comments}}
+																				</ui>
+																			</div>
+																		{{template "repo/diff/comment_form_datahandler" dict "reply" (index $line.Comments 0).ReviewID "hidden" true "root" $ "comment" (index $line.Comments 0)}}
+																		</div>
+																	{{end}}
+																</td>
+																<td class="lines-num"></td>
+																<td class="lines-type-marker"></td>
+																<td class="add-comment-right">
+																	{{if eq $line.GetCommentSide "proposed"}}
+																		<div class="field comment-code-cloud">
+																			<div class="comment-list">
+																				<ui class="ui comments">
+																				{{ template "repo/diff/comments" dict "root" $ "comments" $line.Comments}}
+																				</ui>
+																			</div>
+																			{{template "repo/diff/comment_form_datahandler" dict "reply" (index $line.Comments 0).ReviewID "hidden" true "root" $ "comment" (index $line.Comments 0)}}
+																		</div>
+																	{{end}}
+																</td>
+															</tr>
+														{{end}}
 													{{end}}
 												{{end}}
+											{{else}}
+												{{template "repo/diff/section_unified" dict "file" . "root" $}}
 											{{end}}
-										{{else}}
-											{{template "repo/diff/section_unified" dict "file" . "root" $}}
 										{{end}}
-									{{end}}
-								</tbody>
-							</table>
-						</div>
-					{{end}}
+									</tbody>
+								</table>
+							</div>
+						{{end}}
+					</div>
 				</div>
+			{{end}}
+		<br>
+		{{end}}
+
+		{{if .Diff.IsIncomplete}}
+			<div class="diff-file-box diff-box file-content">
+				<h4 class="ui top attached normal header">
+					{{$.i18n.Tr "repo.diff.too_many_files"}}
+				</h4>
 			</div>
 		{{end}}
-	<br>
-	{{end}}
 
-	{{if .Diff.IsIncomplete}}
-		<div class="diff-file-box diff-box file-content">
-			<h4 class="ui top attached normal header">
-				{{$.i18n.Tr "repo.diff.too_many_files"}}
-			</h4>
-		</div>
-	{{end}}
+		{{if not $.Repository.IsArchived}}
+			<div id="pull_review_add_comment" class="hide">
+					{{template "repo/diff/new_comment" dict "root" .}}
+					</div>
+					<div class="hide" id="edit-content-form">
+							<div class="ui comment form">
+									<div class="ui top attached tabular menu">
+											<a class="active write item">{{$.i18n.Tr "write"}}</a>
+											<a class="preview item" data-url="{{$.Repository.APIURL}}/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a>
+									</div>
+									<div class="ui bottom attached active write tab segment">
+											<textarea tabindex="1" name="content"></textarea>
+									</div>
+									<div class="ui bottom attached tab preview segment markdown">
+									{{$.i18n.Tr "loading"}}
+									</div>
+									<div class="text right edit buttons">
+											<div class="ui basic blue cancel button" tabindex="3">{{.i18n.Tr "repo.issues.cancel"}}</div>
+											<div class="ui green save button" tabindex="2">{{.i18n.Tr "repo.issues.save"}}</div>
+									</div>
+							</div>
+					</div>
+		 {{end}}
 
-	{{if not $.Repository.IsArchived}}
-		<div id="pull_review_add_comment" class="hide">
-		    {{template "repo/diff/new_comment" dict "root" .}}
-        </div>
-        <div class="hide" id="edit-content-form">
-            <div class="ui comment form">
-                <div class="ui top attached tabular menu">
-                    <a class="active write item">{{$.i18n.Tr "write"}}</a>
-                    <a class="preview item" data-url="{{$.Repository.APIURL}}/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a>
-                </div>
-                <div class="ui bottom attached active write tab segment">
-                    <textarea tabindex="1" name="content"></textarea>
-                </div>
-                <div class="ui bottom attached tab preview segment markdown">
-                {{$.i18n.Tr "loading"}}
-                </div>
-                <div class="text right edit buttons">
-                    <div class="ui basic blue cancel button" tabindex="3">{{.i18n.Tr "repo.issues.cancel"}}</div>
-                    <div class="ui green save button" tabindex="2">{{.i18n.Tr "repo.issues.save"}}</div>
-                </div>
-            </div>
-        </div>
-	 {{end}}
+		{{if .IsSplitStyle}}
+			<script>
+				document.addEventListener('DOMContentLoaded', function() {
+					$('tr.add-code').each(function() {
+						var prev = $(this).prev();
+						if(prev.is('.del-code') && prev.children().eq(5).text().trim() === '') {
+							while(prev.prev().is('.del-code') && prev.prev().children().eq(5).text().trim() === '') {
+								prev = prev.prev();
+							}
+							prev.children().eq(3).attr("data-line-num", $(this).children().eq(3).attr("data-line-num"));
+							prev.children().eq(3).html($(this).children().eq(3).html());
+							prev.children().eq(4).html($(this).children().eq(4).html());
+							prev.children().eq(5).html($(this).children().eq(5).html());
 
-	{{if .IsSplitStyle}}
-		<script>
-			document.addEventListener('DOMContentLoaded', function() {
-				$('tr.add-code').each(function() {
-					var prev = $(this).prev();
-					if(prev.is('.del-code') && prev.children().eq(5).text().trim() === '') {
-						while(prev.prev().is('.del-code') && prev.prev().children().eq(5).text().trim() === '') {
-							prev = prev.prev();
+							prev.children().eq(0).addClass('del-code');
+							prev.children().eq(1).addClass('del-code');
+							prev.children().eq(2).addClass('del-code');
+							prev.children().eq(3).addClass('add-code');
+							prev.children().eq(4).addClass('add-code');
+							prev.children().eq(5).addClass('add-code');
+
+							$(this).remove();
 						}
-						prev.children().eq(3).attr("data-line-num", $(this).children().eq(3).attr("data-line-num"));
-						prev.children().eq(3).html($(this).children().eq(3).html());
-						prev.children().eq(4).html($(this).children().eq(4).html());
-						prev.children().eq(5).html($(this).children().eq(5).html());
-
-						prev.children().eq(0).addClass('del-code');
-						prev.children().eq(1).addClass('del-code');
-						prev.children().eq(2).addClass('del-code');
-						prev.children().eq(3).addClass('add-code');
-						prev.children().eq(4).addClass('add-code');
-						prev.children().eq(5).addClass('add-code');
-
-						$(this).remove();
-					}
+					});
 				});
-			});
-		</script>
-	{{end}}
+			</script>
+		{{end}}
+	</div>
 {{end}}
diff --git a/templates/repo/diff/options_dropdown.tmpl b/templates/repo/diff/options_dropdown.tmpl
new file mode 100644
index 0000000000..9fca069f0a
--- /dev/null
+++ b/templates/repo/diff/options_dropdown.tmpl
@@ -0,0 +1,9 @@
+<div class="ui dropdown tiny button">
+	{{.i18n.Tr "repo.diff.options_button"}}
+	<i class="dropdown icon"></i>
+	<div class="menu">
+		<a class="item tiny basic toggle button" data-target="#diff-files">{{.i18n.Tr "repo.diff.show_diff_stats"}}</a>
+		<a class="item" href="{{$.RepoLink}}/pulls/{{.Issue.Index}}.patch" download="{{.Issue.Index}}.patch">{{.i18n.Tr "repo.diff.download_patch"}}</a>
+		<a class="item" href="{{$.RepoLink}}/pulls/{{.Issue.Index}}.diff" download="{{.Issue.Index}}.diff">{{.i18n.Tr "repo.diff.download_diff"}}</a>
+	</div>
+</div>
diff --git a/templates/repo/editor/edit.tmpl b/templates/repo/editor/edit.tmpl
index 134dc818d4..d0ba1becc8 100644
--- a/templates/repo/editor/edit.tmpl
+++ b/templates/repo/editor/edit.tmpl
@@ -39,8 +39,7 @@
 						data-url="{{.Repository.APIURL}}/markdown"
 						data-context="{{.RepoLink}}"
 						data-markdown-file-exts="{{.MarkdownFileExts}}"
-						data-line-wrap-extensions="{{.LineWrapExtensions}}"
-						required>
+						data-line-wrap-extensions="{{.LineWrapExtensions}}">
 {{.FileContent}}</textarea>
 				</div>
 				<div class="ui bottom attached tab segment markdown" data-tab="preview">
@@ -53,5 +52,27 @@
 			{{template "repo/editor/commit_form" .}}
 		</form>
 	</div>
+
+
+	<div class="ui small basic modal" id="edit-empty-content-modal">
+		<div class="ui icon header">
+			<i class="file icon"></i>
+            {{.i18n.Tr "repo.editor.commit_empty_file_header"}}
+		</div>
+		<div class="center content">
+			<p>{{.i18n.Tr "repo.editor.commit_empty_file_text"}}</p>
+		</div>
+		<div class="actions">
+			<div class="ui red basic cancel inverted button">
+				<i class="remove icon"></i>
+                {{.i18n.Tr "repo.editor.cancel"}}
+			</div>
+			<div class="ui green basic ok inverted button">
+				<i class="save icon"></i>
+                {{.i18n.Tr "repo.editor.commit_changes"}}
+			</div>
+		</div>
+	</div>
+
 </div>
 {{template "base/footer" .}}
diff --git a/templates/repo/graph.tmpl b/templates/repo/graph.tmpl
index 2e8d0b5d91..20fe3d1527 100644
--- a/templates/repo/graph.tmpl
+++ b/templates/repo/graph.tmpl
@@ -37,4 +37,5 @@
 	  </div>
 	</div>
 </div>
+{{template "base/paginate" .}}
 {{template "base/footer" .}}
diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl
index fc7f1b660c..111609efef 100644
--- a/templates/repo/header.tmpl
+++ b/templates/repo/header.tmpl
@@ -16,93 +16,95 @@
 				{{if .IsMirror}}<div class="fork-flag">{{$.i18n.Tr "repo.mirror_from"}} <a target="_blank" rel="noopener noreferrer" href="{{MirrorAddress $.Mirror}}">{{MirrorAddress $.Mirror}}</a></div>{{end}}
 				{{if .IsFork}}<div class="fork-flag">{{$.i18n.Tr "repo.forked_from"}} <a href="{{.BaseRepo.Link}}">{{SubStr .BaseRepo.RelLink 1 -1}}</a></div>{{end}}
 			</div>
-			<div class="repo-buttons">
-				<div class="ui labeled button" tabindex="0">
-					<a class="ui compact basic button" href="{{$.RepoLink}}/action/{{if $.IsWatchingRepo}}un{{end}}watch?redirect_to={{$.Link}}">
-						<i class="icon fa-eye{{if not $.IsWatchingRepo}}-slash{{end}}"></i>{{if $.IsWatchingRepo}}{{$.i18n.Tr "repo.unwatch"}}{{else}}{{$.i18n.Tr "repo.watch"}}{{end}}
-					</a>
-					<a class="ui basic label" href="{{.Link}}/watchers">
-						{{.NumWatches}}
-					</a>
-				</div>
-				<div class="ui labeled button" tabindex="0">
-					<a class="ui compact basic button" href="{{$.RepoLink}}/action/{{if $.IsStaringRepo}}un{{end}}star?redirect_to={{$.Link}}">
-						<i class="icon star{{if not $.IsStaringRepo}} outline{{end}}"></i>{{if $.IsStaringRepo}}{{$.i18n.Tr "repo.unstar"}}{{else}}{{$.i18n.Tr "repo.star"}}{{end}}
-					</a>
-					<a class="ui basic label" href="{{.Link}}/stars">
-						{{.NumStars}}
-					</a>
-				</div>
-				{{if and (not .IsEmpty) ($.Permission.CanRead $.UnitTypeCode)}}
-					<div class="ui labeled button {{if and ($.IsSigned) (not $.CanSignedUserFork)}}disabled-repo-button{{end}}" tabindex="0">
-						<a class="ui compact basic button {{if or (not $.IsSigned) (not $.CanSignedUserFork)}}poping up{{end}}" {{if $.CanSignedUserFork}}href="{{AppSubUrl}}/repo/fork/{{.ID}}"{{else if $.IsSigned}} data-content="{{$.i18n.Tr "repo.fork_from_self"}}" {{ else }} data-content="{{$.i18n.Tr "repo.fork_guest_user" }}" href="{{AppSubUrl}}/user/login?redirect_to={{AppSubUrl}}/repo/fork/{{.ID}}" {{end}} data-position="top center" data-variation="tiny">
-							<i class="octicon octicon-repo-forked"></i>{{$.i18n.Tr "repo.fork"}}
+			{{if not .IsBeingCreated}}
+				<div class="repo-buttons">
+					<div class="ui labeled button" tabindex="0">
+						<a class="ui compact basic button" href="{{$.RepoLink}}/action/{{if $.IsWatchingRepo}}un{{end}}watch?redirect_to={{$.Link}}">
+							<i class="icon fa-eye{{if not $.IsWatchingRepo}}-slash{{end}}"></i>{{if $.IsWatchingRepo}}{{$.i18n.Tr "repo.unwatch"}}{{else}}{{$.i18n.Tr "repo.watch"}}{{end}}
 						</a>
-						<a class="ui basic label" href="{{.Link}}/forks">
-							{{.NumForks}}
+						<a class="ui basic label" href="{{.Link}}/watchers">
+							{{.NumWatches}}
+						</a>
+					</div>
+					<div class="ui labeled button" tabindex="0">
+						<a class="ui compact basic button" href="{{$.RepoLink}}/action/{{if $.IsStaringRepo}}un{{end}}star?redirect_to={{$.Link}}">
+							<i class="icon star{{if not $.IsStaringRepo}} outline{{end}}"></i>{{if $.IsStaringRepo}}{{$.i18n.Tr "repo.unstar"}}{{else}}{{$.i18n.Tr "repo.star"}}{{end}}
+						</a>
+						<a class="ui basic label" href="{{.Link}}/stars">
+							{{.NumStars}}
+						</a>
+					</div>
+					{{if and (not .IsEmpty) ($.Permission.CanRead $.UnitTypeCode)}}
+						<div class="ui labeled button {{if and ($.IsSigned) (not $.CanSignedUserFork)}}disabled-repo-button{{end}}" tabindex="0">
+							<a class="ui compact basic button {{if or (not $.IsSigned) (not $.CanSignedUserFork)}}poping up{{end}}" {{if $.CanSignedUserFork}}href="{{AppSubUrl}}/repo/fork/{{.ID}}"{{else if $.IsSigned}} data-content="{{$.i18n.Tr "repo.fork_from_self"}}" {{ else }} data-content="{{$.i18n.Tr "repo.fork_guest_user" }}" rel="nofollow" href="{{AppSubUrl}}/user/login?redirect_to={{AppSubUrl}}/repo/fork/{{.ID}}" {{end}} data-position="top center" data-variation="tiny">
+								<i class="octicon octicon-repo-forked"></i>{{$.i18n.Tr "repo.fork"}}
+							</a>
+							<a class="ui basic label" href="{{.Link}}/forks">
+								{{.NumForks}}
+							</a>
+						</div>
+					{{end}}
+				</div>
+			{{end}}
+		</div><!-- end grid -->
+	</div><!-- end container -->
+{{end}}
+	<div class="ui tabs container">
+		{{if not .Repository.IsBeingCreated}}
+			<div class="ui tabular stackable menu navbar">
+				{{if .Permission.CanRead $.UnitTypeCode}}
+				<a class="{{if .PageIsViewCode}}active{{end}} item" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL | EscapePound}}{{end}}">
+					<i class="octicon octicon-code"></i> {{.i18n.Tr "repo.code"}}
+				</a>
+				{{end}}
+
+				{{if .Permission.CanRead $.UnitTypeIssues}}
+					<a class="{{if .PageIsIssueList}}active{{end}} item" href="{{.RepoLink}}/issues">
+						<i class="octicon octicon-issue-opened"></i> {{.i18n.Tr "repo.issues"}} <span class="ui {{if not .Repository.NumOpenIssues}}gray{{else}}blue{{end}} small label">{{.Repository.NumOpenIssues}}</span>
+					</a>
+				{{end}}
+
+				{{if .Permission.CanRead $.UnitTypeExternalTracker}}
+					<a class="{{if .PageIsIssueList}}active{{end}} item" href="{{.RepoExternalIssuesLink}}" target="_blank" rel="noopener noreferrer">
+						<i class="octicon octicon-link-external"></i> {{.i18n.Tr "repo.issues"}} </span>
+					</a>
+				{{end}}
+
+				{{if and .Repository.CanEnablePulls (.Permission.CanRead $.UnitTypePullRequests)}}
+					<a class="{{if .PageIsPullList}}active{{end}} item" href="{{.RepoLink}}/pulls">
+						<i class="octicon octicon-git-pull-request"></i> {{.i18n.Tr "repo.pulls"}} <span class="ui {{if not .Repository.NumOpenPulls}}gray{{else}}blue{{end}} small label">{{.Repository.NumOpenPulls}}</span>
+					</a>
+				{{end}}
+
+				{{if and (.Permission.CanRead $.UnitTypeReleases) (not .IsEmptyRepo) }}
+				<a class="{{if .PageIsReleaseList}}active{{end}} item" href="{{.RepoLink}}/releases">
+					<i class="octicon octicon-tag"></i> {{.i18n.Tr "repo.releases"}} <span class="ui {{if not .Repository.NumReleases}}gray{{else}}blue{{end}} small label">{{.Repository.NumReleases}}</span>
+				</a>
+				{{end}}
+
+				{{if or (.Permission.CanRead $.UnitTypeWiki) (.Permission.CanRead $.UnitTypeExternalWiki)}}
+					<a class="{{if .PageIsWiki}}active{{end}} item" href="{{.RepoLink}}/wiki" {{if (.Permission.CanRead $.UnitTypeExternalWiki)}} target="_blank" rel="noopener noreferrer" {{end}}>
+						<i class="octicon octicon-book"></i> {{.i18n.Tr "repo.wiki"}}
+					</a>
+				{{end}}
+
+				{{if and (.Permission.CanReadAny $.UnitTypePullRequests $.UnitTypeIssues $.UnitTypeReleases) (not .IsEmptyRepo)}}
+					<a class="{{if .PageIsActivity}}active{{end}} item" href="{{.RepoLink}}/activity">
+						<i class="octicon octicon-pulse"></i> {{.i18n.Tr "repo.activity"}}
+					</a>
+				{{end}}
+
+				{{template "custom/extra_tabs" .}}
+
+				{{if .Permission.IsAdmin}}
+					<div class="right menu">
+						<a class="{{if .PageIsSettings}}active{{end}} item" href="{{.RepoLink}}/settings">
+							<i class="octicon octicon-tools"></i> {{.i18n.Tr "repo.settings"}}
 						</a>
 					</div>
 				{{end}}
 			</div>
-		</div><!-- end grid -->
-	</div><!-- end container -->
-{{end}}
-
-	<div class="ui tabs container">
-		<div class="ui tabular stackable menu navbar">
-			{{if .Permission.CanRead $.UnitTypeCode}}
-			<a class="{{if .PageIsViewCode}}active{{end}} item" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL | EscapePound}}{{end}}">
-				<i class="octicon octicon-code"></i> {{.i18n.Tr "repo.code"}}
-			</a>
-			{{end}}
-
-			{{if .Permission.CanRead $.UnitTypeIssues}}
-				<a class="{{if .PageIsIssueList}}active{{end}} item" href="{{.RepoLink}}/issues">
-					<i class="octicon octicon-issue-opened"></i> {{.i18n.Tr "repo.issues"}} <span class="ui {{if not .Repository.NumOpenIssues}}gray{{else}}blue{{end}} small label">{{.Repository.NumOpenIssues}}</span>
-				</a>
-			{{end}}
-
-			{{if .Permission.CanRead $.UnitTypeExternalTracker}}
-				<a class="{{if .PageIsIssueList}}active{{end}} item" href="{{.RepoExternalIssuesLink}}" target="_blank" rel="noopener noreferrer">
-					<i class="octicon octicon-link-external"></i> {{.i18n.Tr "repo.issues"}} </span>
-				</a>
-			{{end}}
-
-			{{if and .Repository.CanEnablePulls (.Permission.CanRead $.UnitTypePullRequests)}}
-				<a class="{{if .PageIsPullList}}active{{end}} item" href="{{.RepoLink}}/pulls">
-					<i class="octicon octicon-git-pull-request"></i> {{.i18n.Tr "repo.pulls"}} <span class="ui {{if not .Repository.NumOpenPulls}}gray{{else}}blue{{end}} small label">{{.Repository.NumOpenPulls}}</span>
-				</a>
-			{{end}}
-
-			{{if and (.Permission.CanRead $.UnitTypeReleases) (not .IsEmptyRepo) }}
-			<a class="{{if .PageIsReleaseList}}active{{end}} item" href="{{.RepoLink}}/releases">
-				<i class="octicon octicon-tag"></i> {{.i18n.Tr "repo.releases"}} <span class="ui {{if not .Repository.NumReleases}}gray{{else}}blue{{end}} small label">{{.Repository.NumReleases}}</span>
-			</a>
-			{{end}}
-
-			{{if or (.Permission.CanRead $.UnitTypeWiki) (.Permission.CanRead $.UnitTypeExternalWiki)}}
-				<a class="{{if .PageIsWiki}}active{{end}} item" href="{{.RepoLink}}/wiki" {{if (.Permission.CanRead $.UnitTypeExternalWiki)}} target="_blank" rel="noopener noreferrer" {{end}}>
-					<i class="octicon octicon-book"></i> {{.i18n.Tr "repo.wiki"}}
-				</a>
-			{{end}}
-
-			{{if and (.Permission.CanReadAny $.UnitTypePullRequests $.UnitTypeIssues $.UnitTypeReleases) (not .IsEmptyRepo)}}
-				<a class="{{if .PageIsActivity}}active{{end}} item" href="{{.RepoLink}}/activity">
-					<i class="octicon octicon-pulse"></i> {{.i18n.Tr "repo.activity"}}
-				</a>
-			{{end}}
-
-			{{template "custom/extra_tabs" .}}
-
-			{{if .Permission.IsAdmin}}
-				<div class="right menu">
-					<a class="{{if .PageIsSettings}}active{{end}} item" href="{{.RepoLink}}/settings">
-						<i class="octicon octicon-tools"></i> {{.i18n.Tr "repo.settings"}}
-					</a>
-				</div>
-			{{end}}
-		</div>
+		{{end}}
 	</div>
 	<div class="ui tabs divider"></div>
-
 </div>
diff --git a/templates/repo/issue/list.tmpl b/templates/repo/issue/list.tmpl
index e64cef2724..9b354a6800 100644
--- a/templates/repo/issue/list.tmpl
+++ b/templates/repo/issue/list.tmpl
@@ -42,15 +42,16 @@
 			<div class="ten wide right aligned column">
 				<div class="ui secondary filter stackable menu labels">
 					<!-- Label -->
-					<div class="ui {{if not .Labels}}disabled{{end}} dropdown jump item" style="margin-left: auto">
+					<div class="ui {{if not .Labels}}disabled{{end}} dropdown jump item label-filter" style="margin-left: auto">
 						<span class="text">
 							{{.i18n.Tr "repo.issues.filter_label"}}
 							<i class="dropdown icon"></i>
 						</span>
 						<div class="menu">
+							<span class="info">{{.i18n.Tr "repo.issues.filter_label_exclude" | Safe}}</span>
 							<a class="item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}">{{.i18n.Tr "repo.issues.filter_label_no_select"}}</a>
 							{{range .Labels}}
-								<a class="item has-emoji" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.QueryString}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}"><span class="octicon {{if .IsSelected}}octicon-check{{end}}"></span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name}}</a>
+								<a class="item has-emoji label-filter-item" href="{{$.Link}}?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$.SortType}}&state={{$.State}}&labels={{.QueryString}}&milestone={{$.MilestoneID}}&assignee={{$.AssigneeID}}" data-label-id="{{.ID}}"><span class="octicon {{if .IsExcluded}}octicon-circle-slash{{else if .IsSelected}}octicon-check{{end}}"></span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name}}</a>
 							{{end}}
 						</div>
 					</div>
diff --git a/templates/repo/issue/view_content.tmpl b/templates/repo/issue/view_content.tmpl
index acabe34782..29d48d7089 100644
--- a/templates/repo/issue/view_content.tmpl
+++ b/templates/repo/issue/view_content.tmpl
@@ -46,7 +46,7 @@
 							{{end}}
 						</div>
 						<div class="raw-content hide">{{.Issue.Content}}</div>
-						<div class="edit-content-zone hide" data-write="issue-{{.Issue.ID}}-write" data-preview="issue-{{.Issue.ID}}-preview" data-update-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/content" data-context="{{.RepoLink}}"></div>
+						<div class="edit-content-zone hide" data-write="issue-{{.Issue.ID}}-write" data-preview="issue-{{.Issue.ID}}-preview" data-update-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/content" data-context="{{.RepoLink}}" data-attachment-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/attachments" data-view-attachment-url="{{$.RepoLink}}/issues/{{.Issue.Index}}/view-attachments"></div>
 					</div>
 					{{$reactions := .Issue.Reactions.GroupByType}}
 					{{if $reactions}}
@@ -57,15 +57,7 @@
 					{{if .Issue.Attachments}}
 						<div class="ui bottom attached segment">
 							<div class="ui small images">
-								{{range .Issue.Attachments}}
-									<a target="_blank" rel="noopener noreferrer" href="{{AppSubUrl}}/attachments/{{.UUID}}">
-										{{if FilenameIsImage .Name}}
-											<img class="ui image" src="{{AppSubUrl}}/attachments/{{.UUID}}" title='{{$.i18n.Tr "repo.issues.attachment.open_tab" .Name}}'>
-										{{else}}
-											<span class="ui image octicon octicon-desktop-download" title='{{$.i18n.Tr "repo.issues.attachment.download" .Name}}'></span>
-										{{end}}
-									</a>
-								{{end}}
+								{{template "repo/issue/view_content/attachments" Dict "ctx" $ "Attachments" .Issue.Attachments}}
 							</div>
 						</div>
 					{{end}}
@@ -182,6 +174,19 @@
 		<div class="ui bottom attached tab preview segment markdown">
 			{{$.i18n.Tr "loading"}}
 		</div>
+		{{if .IsAttachmentEnabled}}
+			<div class="comment-files"></div>
+			<div class="ui basic button dropzone" id="comment-dropzone"
+				data-upload-url="{{AppSubUrl}}/attachments"
+				data-remove-url="{{AppSubUrl}}/attachments/delete"
+				data-csrf="{{.CsrfToken}}" data-accepts="{{.AttachmentAllowedTypes}}"
+				data-max-file="{{.AttachmentMaxFiles}}" data-max-size="{{.AttachmentMaxSize}}"
+				data-default-message="{{.i18n.Tr "dropzone.default_message"}}"
+				data-invalid-input-type="{{.i18n.Tr "dropzone.invalid_input_type"}}"
+				data-file-too-big="{{.i18n.Tr "dropzone.file_too_big"}}"
+				data-remove-file="{{.i18n.Tr "dropzone.remove_file"}}">
+			</div>
+		{{end}}
 		<div class="text right edit buttons">
 			<div class="ui basic blue cancel button" tabindex="3">{{.i18n.Tr "repo.issues.cancel"}}</div>
 			<div class="ui green save button" tabindex="2">{{.i18n.Tr "repo.issues.save"}}</div>
diff --git a/templates/repo/issue/view_content/attachments.tmpl b/templates/repo/issue/view_content/attachments.tmpl
new file mode 100644
index 0000000000..e2d7d1b9de
--- /dev/null
+++ b/templates/repo/issue/view_content/attachments.tmpl
@@ -0,0 +1,9 @@
+{{range .Attachments}}
+  <a target="_blank" rel="noopener noreferrer" href="{{AppSubUrl}}/attachments/{{.UUID}}">
+    {{if FilenameIsImage .Name}}
+      <img class="ui image" src="{{AppSubUrl}}/attachments/{{.UUID}}" title='{{$.ctx.i18n.Tr "repo.issues.attachment.open_tab" .Name}}'>
+    {{else}}
+      <span class="ui image octicon octicon-desktop-download" title='{{$.ctx.i18n.Tr "repo.issues.attachment.download" .Name}}'></span>
+    {{end}}
+  </a>
+{{end}}
\ No newline at end of file
diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl
index a5f25954c7..5a3d4026c6 100644
--- a/templates/repo/issue/view_content/comments.tmpl
+++ b/templates/repo/issue/view_content/comments.tmpl
@@ -55,7 +55,7 @@
 						{{end}}
 					</div>
 					<div class="raw-content hide">{{.Content}}</div>
-					<div class="edit-content-zone hide" data-write="issuecomment-{{.ID}}-write" data-preview="issuecomment-{{.ID}}-preview" data-update-url="{{$.RepoLink}}/comments/{{.ID}}" data-context="{{$.RepoLink}}"></div>
+					<div class="edit-content-zone hide" data-write="issuecomment-{{.ID}}-write" data-preview="issuecomment-{{.ID}}-preview" data-update-url="{{$.RepoLink}}/comments/{{.ID}}" data-context="{{$.RepoLink}}" data-attachment-url="{{$.RepoLink}}/comments/{{.ID}}/attachments"></div>
 				</div>
 				{{$reactions := .Reactions.GroupByType}}
 				{{if $reactions}}
@@ -66,22 +66,14 @@
 				{{if .Attachments}}
 					<div class="ui bottom attached segment">
 						<div class="ui small images">
-							{{range .Attachments}}
-								<a target="_blank" rel="noopener noreferrer" href="{{AppSubUrl}}/attachments/{{.UUID}}">
-									{{if FilenameIsImage .Name}}
-										<img class="ui image" src="{{AppSubUrl}}/attachments/{{.UUID}}" title='{{$.i18n.Tr "repo.issues.attachment.open_tab" .Name}}'>
-									{{else}}
-										<span class="ui image octicon octicon-desktop-download" title='{{$.i18n.Tr "repo.issues.attachment.download" .Name}}'></span>
-									{{end}}
-								</a>
-							{{end}}
+							{{template "repo/issue/view_content/attachments" Dict "ctx" $ "Attachments" .Attachments}}
 						</div>
 					</div>
 				{{end}}
 			</div>
 		</div>
 	{{else if eq .Type 1}}
-		<div class="event">
+		<div class="event" id="{{.HashTag}}">
 			<span class="octicon octicon-primitive-dot"></span>
 			<a class="ui avatar image" href="{{.Poster.HomeLink}}">
 				<img src="{{.Poster.RelAvatarLink}}">
@@ -89,7 +81,7 @@
 			<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> {{$.i18n.Tr "repo.issues.reopened_at" .EventTag $createdStr | Safe}}</span>
 		</div>
 	{{else if eq .Type 2}}
-		<div class="event">
+		<div class="event" id="{{.HashTag}}">
 			<span class="octicon octicon-circle-slash issue-symbol"></span>
 			<a class="ui avatar image" href="{{.Poster.HomeLink}}">
 				<img src="{{.Poster.RelAvatarLink}}">
@@ -97,7 +89,7 @@
 			<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> {{$.i18n.Tr "repo.issues.closed_at" .EventTag $createdStr | Safe}}</span>
 		</div>
 	{{else if or (eq .Type 3) (eq .Type 5) (eq .Type 6)}}
-		<div class="event">
+		<div class="event" id="{{.HashTag}}">
 			<span class="octicon octicon-bookmark"></span>
 			<a class="ui avatar image" href="{{.Poster.HomeLink}}">
 				<img src="{{.Poster.RelAvatarLink}}">
@@ -125,7 +117,7 @@
 			</div>
 		</div>
 	{{else if eq .Type 4}}
-		<div class="event">
+		<div class="event" id="{{.HashTag}}">
 			<span class="octicon octicon-bookmark"></span>
 			<a class="ui avatar image" href="{{.Poster.HomeLink}}">
 				<img src="{{.Poster.RelAvatarLink}}">
@@ -139,7 +131,7 @@
 		</div>
 	{{else if eq .Type 7}}
 		{{if .Label}}
-			<div class="event">
+			<div class="event" id="{{.HashTag}}">
 				<span class="octicon octicon-primitive-dot"></span>
 				<a class="ui avatar image" href="{{.Poster.HomeLink}}">
 					<img src="{{.Poster.RelAvatarLink}}">
@@ -149,7 +141,7 @@
 			</div>
 		{{end}}
 	{{else if eq .Type 8}}
-		<div class="event">
+		<div class="event" id="{{.HashTag}}">
 			<span class="octicon octicon-primitive-dot"></span>
 			<a class="ui avatar image" href="{{.Poster.HomeLink}}">
 				<img src="{{.Poster.RelAvatarLink}}">
@@ -158,7 +150,7 @@
 			{{if gt .OldMilestoneID 0}}{{if gt .MilestoneID 0}}{{$.i18n.Tr "repo.issues.change_milestone_at" (.OldMilestone.Name|Escape) (.Milestone.Name|Escape) $createdStr | Safe}}{{else}}{{$.i18n.Tr "repo.issues.remove_milestone_at" (.OldMilestone.Name|Escape) $createdStr | Safe}}{{end}}{{else if gt .MilestoneID 0}}{{$.i18n.Tr "repo.issues.add_milestone_at" (.Milestone.Name|Escape) $createdStr | Safe}}{{end}}</span>
 		</div>
 	{{else if eq .Type 9}}
-		<div class="event">
+		<div class="event" id="{{.HashTag}}">
 			<span class="octicon octicon-primitive-dot"></span>
 			{{if gt .AssigneeID 0}}
 				{{if .RemovedAssignee}}
@@ -189,7 +181,7 @@
 			{{end}}
 		</div>
 	{{else if eq .Type 10}}
-		<div class="event">
+		<div class="event" id="{{.HashTag}}">
 			<span class="octicon octicon-primitive-dot"></span>
 			<a class="ui avatar image" href="{{.Poster.HomeLink}}">
 				<img src="{{.Poster.RelAvatarLink}}">
@@ -199,7 +191,7 @@
 			</span>
 		</div>
 	{{else if eq .Type 11}}
-		<div class="event">
+		<div class="event" id="{{.HashTag}}">
 			<span class="octicon octicon-primitive-dot"></span>
 			<a class="ui avatar image" href="{{.Poster.HomeLink}}">
 				<img src="{{.Poster.RelAvatarLink}}">
@@ -209,7 +201,7 @@
 			</span>
 		</div>
 	{{else if eq .Type 12}}
-		<div class="event">
+		<div class="event" id="{{.HashTag}}">
 			<span class="octicon octicon-primitive-dot"></span>
 			<a class="ui avatar image" href="{{.Poster.HomeLink}}">
 				<img src="{{.Poster.RelAvatarLink}}">
@@ -217,7 +209,7 @@
 			<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> {{$.i18n.Tr "repo.issues.start_tracking_history"  $createdStr | Safe}}</span>
 		</div>
 	{{else if eq .Type 13}}
-		<div class="event">
+		<div class="event" id="{{.HashTag}}">
 			<span class="octicon octicon-primitive-dot"></span>
 			<a class="ui avatar image" href="{{.Poster.HomeLink}}">
 				<img src="{{.Poster.RelAvatarLink}}">
@@ -230,7 +222,7 @@
 			</div>
 		</div>
 	{{else if eq .Type 14}}
-		<div class="event">
+		<div class="event" id="{{.HashTag}}">
 			<span class="octicon octicon-primitive-dot"></span>
 			<a class="ui avatar image" href="{{.Poster.HomeLink}}">
 				<img src="{{.Poster.RelAvatarLink}}">
@@ -242,7 +234,7 @@
 			</div>
 		</div>
 	{{else if eq .Type 15}}
-		<div class="event">
+		<div class="event" id="{{.HashTag}}">
 			<span class="octicon octicon-primitive-dot"></span>
 			<a class="ui avatar image" href="{{.Poster.HomeLink}}">
 				<img src="{{.Poster.RelAvatarLink}}">
@@ -250,7 +242,7 @@
 			<span class="text grey"><a href="{{.Poster.HomeLink}}">{{.Poster.GetDisplayName}}</a> {{$.i18n.Tr "repo.issues.cancel_tracking_history"  $createdStr | Safe}}</span>
 		</div>
 	{{else if eq .Type 16}}
-		<div class="event">
+		<div class="event" id="{{.HashTag}}">
 			<span class="octicon octicon-primitive-dot"></span>
 			<a class="ui avatar image" href="{{.Poster.HomeLink}}">
 				<img src="{{.Poster.RelAvatarLink}}">
@@ -260,7 +252,7 @@
 			</span>
 		</div>
 	{{else if eq .Type 17}}
-		<div class="event">
+		<div class="event" id="{{.HashTag}}">
 			<span class="octicon octicon-primitive-dot"></span>
 			<a class="ui avatar image" href="{{.Poster.HomeLink}}">
 				<img src="{{.Poster.RelAvatarLink}}">
@@ -270,7 +262,7 @@
 			</span>
 		</div>
 	{{else if eq .Type 18}}
-		<div class="event">
+		<div class="event" id="{{.HashTag}}">
 			<span class="octicon octicon-primitive-dot"></span>
 			<a class="ui avatar image" href="{{.Poster.HomeLink}}">
 				<img src="{{.Poster.RelAvatarLink}}">
@@ -280,7 +272,7 @@
 			</span>
 		</div>
 	{{else if eq .Type 19}}
-		<div class="event">
+		<div class="event" id="{{.HashTag}}">
 			<span class="octicon octicon-primitive-dot"></span>
 			<a class="ui avatar image" href="{{.Poster.HomeLink}}">
 				<img src="{{.Poster.RelAvatarLink}}">
@@ -294,7 +286,7 @@
 		 	</div>
 	 	</div>
 	{{else if eq .Type 20}}
-		<div class="event">
+		<div class="event" id="{{.HashTag}}">
 		 	<span class="octicon octicon-primitive-dot"></span>
 		 	<a class="ui avatar image" href="{{.Poster.HomeLink}}">
 			 	<img src="{{.Poster.RelAvatarLink}}">
@@ -396,7 +388,7 @@
 			{{end}}
 		</div>
 	{{else if eq .Type 23}}
-		<div class="event">
+		<div class="event" id="{{.HashTag}}">
 			<span class="octicon octicon-lock issue-symbol"></span>
 			<a class="ui avatar image" href="{{.Poster.HomeLink}}">
 				<img src="{{.Poster.RelAvatarLink}}">
@@ -413,7 +405,7 @@
 			{{ end }}
 		</div>
 	{{else if eq .Type 24}}
-		<div class="event">
+		<div class="event" id="{{.HashTag}}">
 			<span class="octicon octicon-key issue-symbol"></span>
 			<a class="ui avatar image" href="{{.Poster.HomeLink}}">
 				<img src="{{.Poster.RelAvatarLink}}">
diff --git a/templates/repo/migrating.tmpl b/templates/repo/migrating.tmpl
new file mode 100644
index 0000000000..0057325e91
--- /dev/null
+++ b/templates/repo/migrating.tmpl
@@ -0,0 +1,31 @@
+{{template "base/head" .}}
+<div class="repository quickstart">
+	{{template "repo/header" .}}
+	<div class="ui container">
+		<div class="ui grid">
+			<div class="sixteen wide column content">
+				{{template "base/alert" .}}
+				<div class="home">
+					<div class="ui stackable middle very relaxed page grid">
+						<div id="repo_migrating" class="sixteen wide center aligned centered column" repo="{{.Repo.Repository.FullName}}">
+							<div>
+								<img src="{{StaticUrlPrefix}}/img/loading.png"/>
+							</div>
+						</div>
+					</div>
+					<div class="ui stackable middle very relaxed page grid">
+						<div class="sixteen wide center aligned centered column">
+							<div id="repo_migrating_progress">
+								<p>{{.i18n.Tr "repo.migrate.migrating" .CloneAddr | Safe}}</p>
+							</div>
+							<div id="repo_migrating_failed">
+								<p>{{.i18n.Tr "repo.migrate.migrating_failed" .CloneAddr | Safe}}</p>
+							</div>
+						</div>
+					</div>
+				</div>
+			</div>
+		</div>
+	</div>
+</div>
+{{template "base/footer" .}}
diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl
index 4f7d32479e..a93efb8d25 100644
--- a/templates/repo/settings/options.tmpl
+++ b/templates/repo/settings/options.tmpl
@@ -88,15 +88,15 @@
 							<i class="icon dropdown"></i>
 							<label for="">{{.i18n.Tr "repo.need_auth"}}</label>
 						</label>
-						<div class="content {{if .Err_Auth}}active{{else if .Mirror.Username}}active{{end}}">
+						<div class="content {{if .Err_Auth}}active{{else if (MirrorUserName .Mirror)}}active{{end}}">
 							<div class="inline field {{if .Err_Auth}}error{{end}}">
 								<label for="mirror_username">{{.i18n.Tr "username"}}</label>
-								<input id="mirror_username" name="mirror_username" value="{{.Mirror.Username}}" {{if not .mirror_username}}data-need-clear="true"{{end}}>
+								<input id="mirror_username" name="mirror_username" value="{{MirrorUserName .Mirror}}" {{if not .mirror_username}}data-need-clear="true"{{end}}>
 							</div>
 							<input class="fake" type="password">
 							<div class="inline field {{if .Err_Auth}}error{{end}}">
 								<label for="mirror_password">{{.i18n.Tr "password"}}</label>
-								<input id="mirror_password" name="mirror_password" type="password" value="{{.Mirror.Password}}" {{if not .mirror_password}}data-need-clear="true"{{end}}>
+								<input id="mirror_password" name="mirror_password" type="password" value="{{MirrorPassword .Mirror}}" {{if not .mirror_password}}data-need-clear="true"{{end}}>
 							</div>
 						</div>
 					</div>
diff --git a/templates/repo/settings/protected_branch.tmpl b/templates/repo/settings/protected_branch.tmpl
index 067d1d9761..a50765c4b4 100644
--- a/templates/repo/settings/protected_branch.tmpl
+++ b/templates/repo/settings/protected_branch.tmpl
@@ -59,6 +59,13 @@
 								</div>
 							</div>
 						{{end}}
+						<br>
+						<div class="whitelist field">
+							<div class="ui checkbox">
+								<input type="checkbox" name="whitelist_deploy_keys" {{if .Branch.WhitelistDeployKeys}}checked{{end}}>
+								<label for="whitelist_deploy_keys">{{.i18n.Tr "repo.settings.protect_whitelist_deploy_keys"}}</label>
+							</div>
+						</div>
 					</div>
 
 					<div class="field">
diff --git a/templates/repo/settings/webhook/list.tmpl b/templates/repo/settings/webhook/list.tmpl
index 8fdae45b1b..d1784564dc 100644
--- a/templates/repo/settings/webhook/list.tmpl
+++ b/templates/repo/settings/webhook/list.tmpl
@@ -6,25 +6,25 @@
 			<div class="ui blue tiny button">{{.i18n.Tr "repo.settings.add_webhook"}}</div>
 			<div class="menu">
 				<a class="item" href="{{.BaseLink}}/gitea/new">
-					<img class="img-10" src="{{AppSubUrl}}/img/gitea-sm.png">Gitea
+					<img class="img-10" src="{{StaticUrlPrefix}}/img/gitea-sm.png">Gitea
 				</a>
 				<a class="item" href="{{.BaseLink}}/gogs/new">
-					<img class="img-10" src="{{AppSubUrl}}/img/gogs.ico">Gogs
+					<img class="img-10" src="{{StaticUrlPrefix}}/img/gogs.ico">Gogs
 				</a>
 				<a class="item" href="{{.BaseLink}}/slack/new">
-					<img class="img-10" src="{{AppSubUrl}}/img/slack.png">Slack
+					<img class="img-10" src="{{StaticUrlPrefix}}/img/slack.png">Slack
 				</a>
 				<a class="item" href="{{.BaseLink}}/discord/new">
-					<img class="img-10" src="{{AppSubUrl}}/img/discord.png">Discord
+					<img class="img-10" src="{{StaticUrlPrefix}}/img/discord.png">Discord
 				</a>
 				<a class="item" href="{{.BaseLink}}/dingtalk/new">
-					<img class="img-10" src="{{AppSubUrl}}/img/dingtalk.ico">Dingtalk
+					<img class="img-10" src="{{StaticUrlPrefix}}/img/dingtalk.ico">Dingtalk
 				</a>
 				<a class="item" href="{{.BaseLink}}/telegram/new">
-					<img class="img-10" src="{{AppSubUrl}}/img/telegram.png">Telegram
+					<img class="img-10" src="{{StaticUrlPrefix}}/img/telegram.png">Telegram
 				</a>
 				<a class="item" href="{{.BaseLink}}/msteams/new">
-					<img class="img-10" src="{{AppSubUrl}}/img/msteams.png">Microsoft Teams
+					<img class="img-10" src="{{StaticUrlPrefix}}/img/msteams.png">Microsoft Teams
 				</a>
 			</div>
 		</div>
diff --git a/templates/repo/settings/webhook/new.tmpl b/templates/repo/settings/webhook/new.tmpl
index 358827c0f8..1d5d849738 100644
--- a/templates/repo/settings/webhook/new.tmpl
+++ b/templates/repo/settings/webhook/new.tmpl
@@ -8,19 +8,19 @@
 			{{if .PageIsSettingsHooksNew}}{{.i18n.Tr "repo.settings.add_webhook"}}{{else}}{{.i18n.Tr "repo.settings.update_webhook"}}{{end}}
 			<div class="ui right">
 				{{if eq .HookType "gitea"}}
-					<img class="img-13" src="{{AppSubUrl}}/img/gitea-sm.png">
+					<img class="img-13" src="{{StaticUrlPrefix}}/img/gitea-sm.png">
 				{{else if eq .HookType "gogs"}}
-					<img class="img-13" src="{{AppSubUrl}}/img/gogs.ico">
+					<img class="img-13" src="{{StaticUrlPrefix}}/img/gogs.ico">
 				{{else if eq .HookType "slack"}}
-					<img class="img-13" src="{{AppSubUrl}}/img/slack.png">
+					<img class="img-13" src="{{StaticUrlPrefix}}/img/slack.png">
 				{{else if eq .HookType "discord"}}
-					<img class="img-13" src="{{AppSubUrl}}/img/discord.png">
+					<img class="img-13" src="{{StaticUrlPrefix}}/img/discord.png">
 				{{else if eq .HookType "dingtalk"}}
-					<img class="img-13" src="{{AppSubUrl}}/img/dingtalk.ico">
+					<img class="img-13" src="{{StaticUrlPrefix}}/img/dingtalk.ico">
 				{{else if eq .HookType "telegram"}}
-					<img class="img-13" src="{{AppSubUrl}}/img/telegram.png">
+					<img class="img-13" src="{{StaticUrlPrefix}}/img/telegram.png">
 				{{else if eq .HookType "msteams"}}
-					<img class="img-13" src="{{AppSubUrl}}/img/msteams.png">
+					<img class="img-13" src="{{StaticUrlPrefix}}/img/msteams.png">
 				{{end}}
 			</div>
 		</h4>
diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl
index 50e4c33946..e490fc123b 100644
--- a/templates/repo/view_file.tmpl
+++ b/templates/repo/view_file.tmpl
@@ -1,48 +1,52 @@
 <div class="{{TabSizeClass .Editorconfig .FileName}} non-diff-file-content">
-	<h4 class="ui top attached header" id="{{if .ReadmeExist}}repo-readme{{else}}repo-read-file{{end}}">
-		<div class="ui stackable grid">
-			<div class="eight wide column">
-				{{if .ReadmeExist}}
-					<i class="book icon ui left"></i>
-					{{if .ReadmeInList}}
-						<strong>{{.FileName}}</strong>
-					{{else}}
-						<strong>{{.FileName}}</strong> <span class="text grey normal">{{FileSize .FileSize}}{{if .IsLFSFile}} ({{.i18n.Tr "repo.stored_lfs"}}){{end}}</span>
-					{{end}}
-				{{else}}
-					<i class="file text outline icon ui left"></i>
-					<strong>{{.FileName}}</strong> <span class="text grey normal">{{FileSize .FileSize}}{{if .IsLFSFile}} ({{.i18n.Tr "repo.stored_lfs"}}){{end}}</span>
-				{{end}}
-			</div>
-			<div class="eight wide right aligned column">
-				{{if not .ReadmeInList}}
-					<div class="ui right file-actions">
-						<div class="ui buttons">
-							<a class="ui button" href="{{EscapePound $.RawFileLink}}">{{.i18n.Tr "repo.file_raw"}}</a>
-							{{if not .IsViewCommit}}
-								<a class="ui button" href="{{.RepoLink}}/src/commit/{{.CommitID}}/{{EscapePound .TreePath}}">{{.i18n.Tr "repo.file_permalink"}}</a>
-							{{end}}
-							{{if .IsTextFile}}
-								<a class="ui button" href="{{.RepoLink}}/blame/{{EscapePound .BranchNameSubURL}}/{{EscapePound .TreePath}}">{{.i18n.Tr "repo.blame"}}</a>
-							{{end}}
-							<a class="ui button" href="{{.RepoLink}}/commits/{{EscapePound .BranchNameSubURL}}/{{EscapePound .TreePath}}">{{.i18n.Tr "repo.file_history"}}</a>
+	<h4 class="file-header ui top attached header">
+		<div class="file-header-left">
+			{{if .ReadmeInList}}
+				<i class="book icon"></i>
+				<strong>{{.FileName}}</strong>
+			{{else}}
+				<div class="file-info text grey normal mono">
+					{{if .NumLines}}
+						<div class="file-info-entry">
+							{{.NumLines}} {{.i18n.Tr (TrN .i18n.Lang .NumLines "repo.line" "repo.lines") }}
 						</div>
-						{{if .Repository.CanEnableEditor}}
-							{{if .CanEditFile}}
-								<a href="{{.RepoLink}}/_edit/{{EscapePound .BranchName}}/{{EscapePound .TreePath}}"><i class="octicon octicon-pencil btn-octicon poping up"  data-content="{{.EditFileTooltip}}" data-position="bottom center" data-variation="tiny inverted"></i></a>
-							{{else}}
-								<i class="octicon octicon-pencil btn-octicon poping up disabled" data-content="{{.EditFileTooltip}}" data-position="bottom center" data-variation="tiny inverted"></i>
-							{{end}}
-							{{if .CanDeleteFile}}
-								<a href="{{.RepoLink}}/_delete/{{EscapePound .BranchName}}/{{EscapePound .TreePath}}"><i class="octicon octicon-trashcan btn-octicon btn-octicon-danger poping up"  data-content="{{.DeleteFileTooltip}}" data-position="bottom center" data-variation="tiny inverted"></i></a>
-							{{else}}
-								<i class="octicon octicon-trashcan btn-octicon poping up disabled" data-content="{{.DeleteFileTooltip}}" data-position="bottom center" data-variation="tiny inverted"></i>
-							{{end}}
-						{{end}}
-					</div>
+					{{end}}
+					{{if .FileSize}}
+						<div class="file-info-entry">
+							{{FileSize .FileSize}}{{if .IsLFSFile}} ({{.i18n.Tr "repo.stored_lfs"}}){{end}}
+						</div>
+					{{end}}
+				</div>
+			{{end}}
+		</div>
+		{{if not .ReadmeInList}}
+		<div class="file-header-right">
+			<div class="ui right file-actions">
+				<div class="ui buttons">
+					<a class="ui button" href="{{EscapePound $.RawFileLink}}">{{.i18n.Tr "repo.file_raw"}}</a>
+					{{if not .IsViewCommit}}
+						<a class="ui button" href="{{.RepoLink}}/src/commit/{{.CommitID}}/{{EscapePound .TreePath}}">{{.i18n.Tr "repo.file_permalink"}}</a>
+					{{end}}
+					{{if .IsTextFile}}
+						<a class="ui button" href="{{.RepoLink}}/blame/{{EscapePound .BranchNameSubURL}}/{{EscapePound .TreePath}}">{{.i18n.Tr "repo.blame"}}</a>
+					{{end}}
+					<a class="ui button" href="{{.RepoLink}}/commits/{{EscapePound .BranchNameSubURL}}/{{EscapePound .TreePath}}">{{.i18n.Tr "repo.file_history"}}</a>
+				</div>
+				{{if .Repository.CanEnableEditor}}
+					{{if .CanEditFile}}
+						<a href="{{.RepoLink}}/_edit/{{EscapePound .BranchName}}/{{EscapePound .TreePath}}"><i class="octicon octicon-pencil btn-octicon poping up"  data-content="{{.EditFileTooltip}}" data-position="bottom center" data-variation="tiny inverted"></i></a>
+					{{else}}
+						<i class="octicon octicon-pencil btn-octicon poping up disabled" data-content="{{.EditFileTooltip}}" data-position="bottom center" data-variation="tiny inverted"></i>
+					{{end}}
+					{{if .CanDeleteFile}}
+						<a href="{{.RepoLink}}/_delete/{{EscapePound .BranchName}}/{{EscapePound .TreePath}}"><i class="octicon octicon-trashcan btn-octicon btn-octicon-danger poping up"  data-content="{{.DeleteFileTooltip}}" data-position="bottom center" data-variation="tiny inverted"></i></a>
+					{{else}}
+						<i class="octicon octicon-trashcan btn-octicon poping up disabled" data-content="{{.DeleteFileTooltip}}" data-position="bottom center" data-variation="tiny inverted"></i>
+					{{end}}
 				{{end}}
 			</div>
 		</div>
+		{{end}}
 	</h4>
 	<div class="ui attached table unstackable segment">
 		<div class="file-view {{if .IsMarkup}}{{.MarkupType}} markdown{{else if .IsRenderedHTML}}plain-text{{else if .IsTextFile}}code-view{{end}} has-emoji">
@@ -63,7 +67,7 @@
 							<strong>{{.i18n.Tr "repo.audio_not_supported_in_browser"}}</strong>
 						</audio>
 					{{else if .IsPDFFile}}
-						<iframe width="100%" height="600px" src="{{AppSubUrl}}/vendor/plugins/pdfjs/web/viewer.html?file={{EscapePound $.RawFileLink}}"></iframe>
+						<iframe width="100%" height="600px" src="{{StaticUrlPrefix}}/vendor/plugins/pdfjs/web/viewer.html?file={{EscapePound $.RawFileLink}}"></iframe>
 					{{else}}
 						<a href="{{EscapePound $.RawFileLink}}" rel="nofollow" class="btn btn-gray btn-radius">{{.i18n.Tr "repo.file_view_raw"}}</a>
 					{{end}}
diff --git a/templates/status/404.tmpl b/templates/status/404.tmpl
index 8e6a013f03..f947602bf2 100644
--- a/templates/status/404.tmpl
+++ b/templates/status/404.tmpl
@@ -1,7 +1,7 @@
 {{template "base/head" .}}
 {{if .IsRepo}}<div class="repository">{{template "repo/header" .}}</div>{{end}}
 <div class="ui container center">
-	<p style="margin-top: 100px"><img src="{{AppSubUrl}}/img/404.png" alt="404"/></p>
+	<p style="margin-top: 100px"><img src="{{StaticUrlPrefix}}/img/404.png" alt="404"/></p>
 	<div class="ui divider"></div>
 	<br>
 	{{if .ShowFooterVersion}}<p>Application Version: {{AppVer}}</p>{{end}}
diff --git a/templates/status/500.tmpl b/templates/status/500.tmpl
index 2e01e8ac74..191e5929d8 100644
--- a/templates/status/500.tmpl
+++ b/templates/status/500.tmpl
@@ -1,6 +1,6 @@
 {{template "base/head" .}}
 <div class="ui container center">
-	<p style="margin-top: 100px"><img src="{{AppSubUrl}}/img/500.png" alt="500"/></p>
+	<p style="margin-top: 100px"><img src="{{StaticUrlPrefix}}/img/500.png" alt="500"/></p>
 	<div class="ui divider"></div>
 	<br>
 	{{if .ErrorMsg}}<p>An error has occurred :</p>
diff --git a/templates/swagger/ui.tmpl b/templates/swagger/ui.tmpl
index 20d4f7f63b..0125ee4a19 100644
--- a/templates/swagger/ui.tmpl
+++ b/templates/swagger/ui.tmpl
@@ -4,9 +4,9 @@
 <head>
 	<meta charset="UTF-8">
 	<title>Swagger UI</title>
-	<link rel="stylesheet" type="text/css" href="{{AppSubUrl}}/vendor/assets/swagger-ui/swagger-ui.css" >
-	<link rel="icon" type="image/png" href="{{AppSubUrl}}/vendor/assets/swagger-ui/favicon-32x32.png" sizes="32x32" />
-	<link rel="icon" type="image/png" href="{{AppSubUrl}}/vendor/assets/swagger-ui/favicon-16x16.png" sizes="16x16" />
+	<link rel="stylesheet" type="text/css" href="{{StaticUrlPrefix}}/vendor/assets/swagger-ui/swagger-ui.css" >
+	<link rel="icon" type="image/png" href="{{StaticUrlPrefix}}/vendor/assets/swagger-ui/favicon-32x32.png" sizes="32x32" />
+	<link rel="icon" type="image/png" href="{{StaticUrlPrefix}}/vendor/assets/swagger-ui/favicon-16x16.png" sizes="16x16" />
 	<style>
 	html
 	{
@@ -65,8 +65,8 @@
 
 <div id="swagger-ui"></div>
 
-<script src="{{AppSubUrl}}/vendor/assets/swagger-ui/swagger-ui-bundle.js"> </script>
-<script src="{{AppSubUrl}}/vendor/assets/swagger-ui/swagger-ui-standalone-preset.js"> </script>
+<script src="{{StaticUrlPrefix}}/vendor/assets/swagger-ui/swagger-ui-bundle.js"> </script>
+<script src="{{StaticUrlPrefix}}/vendor/assets/swagger-ui/swagger-ui-standalone-preset.js"> </script>
 <script>
 
 window.onload = function() {
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index b6b3900f92..c1d16bd4a9 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -5140,6 +5140,42 @@
         }
       }
     },
+    "/repos/{owner}/{repo}/signing-key.gpg": {
+      "get": {
+        "produces": [
+          "text/plain"
+        ],
+        "tags": [
+          "repository"
+        ],
+        "summary": "Get signing-key.gpg for given repository",
+        "operationId": "repoSigningKey",
+        "parameters": [
+          {
+            "type": "string",
+            "description": "owner of the repo",
+            "name": "owner",
+            "in": "path",
+            "required": true
+          },
+          {
+            "type": "string",
+            "description": "name of the repo",
+            "name": "repo",
+            "in": "path",
+            "required": true
+          }
+        ],
+        "responses": {
+          "200": {
+            "description": "GPG armored public key",
+            "schema": {
+              "type": "string"
+            }
+          }
+        }
+      }
+    },
     "/repos/{owner}/{repo}/stargazers": {
       "get": {
         "produces": [
@@ -5691,6 +5727,26 @@
         }
       }
     },
+    "/signing-key.gpg": {
+      "get": {
+        "produces": [
+          "text/plain"
+        ],
+        "tags": [
+          "miscellaneous"
+        ],
+        "summary": "Get default signing-key.gpg",
+        "operationId": "getSigningKey",
+        "responses": {
+          "200": {
+            "description": "GPG armored public key",
+            "schema": {
+              "type": "string"
+            }
+          }
+        }
+      }
+    },
     "/teams/{id}": {
       "get": {
         "produces": [
@@ -9533,6 +9589,9 @@
           "type": "string",
           "x-go-name": "Signature"
         },
+        "signer": {
+          "$ref": "#/definitions/PayloadUser"
+        },
         "verified": {
           "type": "boolean",
           "x-go-name": "Verified"
diff --git a/templates/user/auth/grant.tmpl b/templates/user/auth/grant.tmpl
index 85c507a74a..b67bfa110c 100644
--- a/templates/user/auth/grant.tmpl
+++ b/templates/user/auth/grant.tmpl
@@ -13,10 +13,10 @@
 				</p>
 			</div>
 			<div class="ui attached segment">
-				<p>{{.i18n.Tr "auth.authroize_redirect_notice" .ApplicationRedirectDomainHTML | Str2html}}</p>
+				<p>{{.i18n.Tr "auth.authorize_redirect_notice" .ApplicationRedirectDomainHTML | Str2html}}</p>
 			</div>
 			<div class="ui attached segment">
-				<form method="post" action="{{.AppSubUrl}}/login/oauth/grant">
+				<form method="post" action="{{AppSubUrl}}/login/oauth/grant">
 					{{.CsrfTokenHtml}}
 					<input type="hidden" name="client_id" value="{{.Application.ClientID}}">
 					<input type="hidden" name="state" value="{{.State}}">
diff --git a/templates/user/auth/signin_navbar.tmpl b/templates/user/auth/signin_navbar.tmpl
index 3f0db26713..345e221296 100644
--- a/templates/user/auth/signin_navbar.tmpl
+++ b/templates/user/auth/signin_navbar.tmpl
@@ -1,9 +1,9 @@
 {{if .EnableOpenIDSignIn}}
 <div class="ui secondary pointing tabular top attached borderless menu new-menu navbar">
-	<a class="{{if .PageIsLogin}}active{{end}} item" href="{{AppSubUrl}}/user/login">
+	<a class="{{if .PageIsLogin}}active{{end}} item" rel="nofollow" href="{{AppSubUrl}}/user/login">
 		{{.i18n.Tr "auth.login_userpass"}}
 	</a>
-	<a class="{{if .PageIsLoginOpenID}}active{{end}} item" href="{{AppSubUrl}}/user/login/openid">
+	<a class="{{if .PageIsLoginOpenID}}active{{end}} item" rel="nofollow" href="{{AppSubUrl}}/user/login/openid">
 		<i class="fa fa-openid"></i>
 		&nbsp;OpenID
 	</a>
diff --git a/vendor/gopkg.in/editorconfig/editorconfig-core-go.v1/.editorconfig b/vendor/github.com/editorconfig/editorconfig-core-go/v2/.editorconfig
similarity index 69%
rename from vendor/gopkg.in/editorconfig/editorconfig-core-go.v1/.editorconfig
rename to vendor/github.com/editorconfig/editorconfig-core-go/v2/.editorconfig
index 715e21f1de..aca7129072 100644
--- a/vendor/gopkg.in/editorconfig/editorconfig-core-go.v1/.editorconfig
+++ b/vendor/github.com/editorconfig/editorconfig-core-go/v2/.editorconfig
@@ -3,11 +3,12 @@
 root = true
 
 [*]
-end_of_line = lf
 insert_final_newline = true
 charset = utf-8
 trim_trailing_whitespace = true
+indent_style = space
+indent_size = 2
 
-[*.go]
+[{Makefile,go.mod,go.sum,*.go}]
 indent_style = tab
 indent_size = 8
diff --git a/vendor/github.com/editorconfig/editorconfig-core-go/v2/.gitattributes b/vendor/github.com/editorconfig/editorconfig-core-go/v2/.gitattributes
new file mode 100644
index 0000000000..176a458f94
--- /dev/null
+++ b/vendor/github.com/editorconfig/editorconfig-core-go/v2/.gitattributes
@@ -0,0 +1 @@
+* text=auto
diff --git a/vendor/gopkg.in/editorconfig/editorconfig-core-go.v1/.gitignore b/vendor/github.com/editorconfig/editorconfig-core-go/v2/.gitignore
similarity index 100%
rename from vendor/gopkg.in/editorconfig/editorconfig-core-go.v1/.gitignore
rename to vendor/github.com/editorconfig/editorconfig-core-go/v2/.gitignore
diff --git a/vendor/gopkg.in/editorconfig/editorconfig-core-go.v1/.gitmodules b/vendor/github.com/editorconfig/editorconfig-core-go/v2/.gitmodules
similarity index 100%
rename from vendor/gopkg.in/editorconfig/editorconfig-core-go.v1/.gitmodules
rename to vendor/github.com/editorconfig/editorconfig-core-go/v2/.gitmodules
diff --git a/vendor/github.com/editorconfig/editorconfig-core-go/v2/.travis.yml b/vendor/github.com/editorconfig/editorconfig-core-go/v2/.travis.yml
new file mode 100644
index 0000000000..9a1fdf77df
--- /dev/null
+++ b/vendor/github.com/editorconfig/editorconfig-core-go/v2/.travis.yml
@@ -0,0 +1,30 @@
+---
+language: go
+dist: xenial
+sudo: true
+
+go:
+  - '1.11.x'
+  - '1.12.x'
+
+compiler:
+  - gcc
+
+install:
+  # first we create a directory for the CMake binaries
+  - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps"
+  - mkdir ${DEPS_DIR} && cd ${DEPS_DIR}
+  # we use wget to fetch the cmake binaries
+  - travis_retry wget --no-check-certificate https://cmake.org/files/v3.14/cmake-3.14.6-Linux-x86_64.tar.gz
+  - echo "82e08e50ba921035efa82b859c74c5fbe27d3e49a4003020e3c77618a4e912cd  cmake-3.14.6-Linux-x86_64.tar.gz" > sha256sum.txt
+  - sha256sum -c sha256sum.txt
+  - tar -xvf cmake-3.14.6-Linux-x86_64.tar.gz > /dev/null
+  - mv cmake-3.14.6-Linux-x86_64 cmake-install
+  - PATH=${DEPS_DIR}/cmake-install:${DEPS_DIR}/cmake-install/bin:$PATH
+  - cd ${TRAVIS_BUILD_DIR}
+
+env:
+  - GO111MODULE=on
+
+script:
+  - make test
diff --git a/vendor/github.com/editorconfig/editorconfig-core-go/v2/CHANGELOG.md b/vendor/github.com/editorconfig/editorconfig-core-go/v2/CHANGELOG.md
new file mode 100644
index 0000000000..c09da69744
--- /dev/null
+++ b/vendor/github.com/editorconfig/editorconfig-core-go/v2/CHANGELOG.md
@@ -0,0 +1,20 @@
+# Change log
+
+## v2.1.1 - 2019-08-18
+
+- Fix a small path bug
+  ([#17](https://github.com/editorconfig/editorconfig-core-go/issues/17),
+  [#18](https://github.com/editorconfig/editorconfig-core-go/pull/18)).
+
+## v2.1.0 - 2019-08-10
+
+- This package is now *way* more compliant with the Editorconfig definition
+  thanks to a refactor work made by [@greut](https://github.com/greut)
+  ([#15](https://github.com/editorconfig/editorconfig-core-go/pull/15)).
+
+## v2.0.0 - 2019-07-14
+
+- This project now uses [Go Modules](https://blog.golang.org/using-go-modules)
+  ([#14](https://github.com/editorconfig/editorconfig-core-go/pull/14)).
+- The import path has been changed from `gopkg.in/editorconfig/editorconfig-core-go.v1`
+  to `github.com/editorconfig/editorconfig-core-go/v2`.
diff --git a/vendor/github.com/editorconfig/editorconfig-core-go/v2/CMakeLists.txt b/vendor/github.com/editorconfig/editorconfig-core-go/v2/CMakeLists.txt
new file mode 100644
index 0000000000..1ebcf1d173
--- /dev/null
+++ b/vendor/github.com/editorconfig/editorconfig-core-go/v2/CMakeLists.txt
@@ -0,0 +1,5 @@
+project(editorconfig-core-go)
+cmake_minimum_required(VERSION 3.14)
+enable_testing()
+set(EDITORCONFIG_CMD ${CMAKE_CURRENT_LIST_DIR}/editorconfig)
+add_subdirectory(core-test)
diff --git a/vendor/gopkg.in/editorconfig/editorconfig-core-go.v1/LICENSE b/vendor/github.com/editorconfig/editorconfig-core-go/v2/LICENSE
similarity index 100%
rename from vendor/gopkg.in/editorconfig/editorconfig-core-go.v1/LICENSE
rename to vendor/github.com/editorconfig/editorconfig-core-go/v2/LICENSE
diff --git a/vendor/github.com/editorconfig/editorconfig-core-go/v2/Makefile b/vendor/github.com/editorconfig/editorconfig-core-go/v2/Makefile
new file mode 100644
index 0000000000..eff4473ddf
--- /dev/null
+++ b/vendor/github.com/editorconfig/editorconfig-core-go/v2/Makefile
@@ -0,0 +1,19 @@
+PROJECT_ROOT_DIR := $(CURDIR)
+SRC := $(shell git ls-files *.go */*.go)
+
+.PHONY: bin test test-go test-core submodule
+
+test: test-go test-core
+
+submodule:
+	git submodule update --init
+
+editorconfig: $(SRC)
+	go build ./cmd/editorconfig
+
+test-go:
+	go test -v ./...
+
+test-core: editorconfig
+	cd core-test; cmake ..
+	cd core-test; ctest -E "(comments_after_section|octothorpe|unset_|_pre_0.9.0|max_|root_file_mixed_case)" --output-on-failure .
diff --git a/vendor/gopkg.in/editorconfig/editorconfig-core-go.v1/README.md b/vendor/github.com/editorconfig/editorconfig-core-go/v2/README.md
similarity index 68%
rename from vendor/gopkg.in/editorconfig/editorconfig-core-go.v1/README.md
rename to vendor/github.com/editorconfig/editorconfig-core-go/v2/README.md
index e060f1061f..784e37cf54 100644
--- a/vendor/gopkg.in/editorconfig/editorconfig-core-go.v1/README.md
+++ b/vendor/github.com/editorconfig/editorconfig-core-go/v2/README.md
@@ -1,27 +1,30 @@
-[![Build Status](https://travis-ci.org/editorconfig/editorconfig-core-go.svg?branch=master)](https://travis-ci.org/editorconfig/editorconfig-core-go)
-[![GoDoc](https://godoc.org/gopkg.in/editorconfig/editorconfig-core-go.v1?status.svg)](https://godoc.org/gopkg.in/editorconfig/editorconfig-core-go.v1)
-[![Go Report Card](https://goreportcard.com/badge/gopkg.in/editorconfig/editorconfig-core-go.v1)](https://goreportcard.com/report/gopkg.in/editorconfig/editorconfig-core-go.v1)
+<!-- Currently tests against core-test are not done so hide build status badge for now -->
+<!-- [![Build Status](https://travis-ci.org/editorconfig/editorconfig-core-go.svg?branch=master)](https://travis-ci.org/editorconfig/editorconfig-core-go) -->
+[![GoDoc](https://godoc.org/github.com/editorconfig/editorconfig-core-go?status.svg)](https://godoc.org/github.com/editorconfig/editorconfig-core-go)
+[![Go Report Card](https://goreportcard.com/badge/github.com/editorconfig/editorconfig-core-go)](https://goreportcard.com/report/github.com/editorconfig/editorconfig-core-go)
 
 # Editorconfig Core Go
 
 A [Editorconfig][editorconfig] file parser and manipulator for Go.
 
-> This package is already working, but still under testing.
+> Currently this package does some basic work but does not fully support
+> EditorConfig specs, so using it in "real world" is not recommended.
+
+## Missing features
+
+- `unset`
+- escaping comments in values, probably in [go-ini/ini](https://github.com/go-ini/ini)
 
 ## Installing
 
-We recommend the use of [gopkg.in][gopkg] for this package:
-
-```bash
-go get -u gopkg.in/editorconfig/editorconfig-core-go.v1
-```
+We recommend the use of Go 1.11+ modules for this package.
 
 Import by the same path. The package name you will use to access it is
 `editorconfig`.
 
 ```go
 import (
-    "gopkg.in/editorconfig/editorconfig-core-go.v1"
+    "github.com/editorconfig/editorconfig-core-go/v2"
 )
 ```
 
@@ -70,6 +73,7 @@ type Definition struct {
 	EndOfLine              string
 	TrimTrailingWhitespace bool
 	InsertFinalNewline     bool
+	Raw                    map[string]string
 }
 ```
 
@@ -115,8 +119,7 @@ if err != nil {
 To run the tests:
 
 ```bash
-go test -v
+go test -v ./...
 ```
 
 [editorconfig]: http://editorconfig.org/
-[gopkg]: https://gopkg.in
diff --git a/vendor/gopkg.in/editorconfig/editorconfig-core-go.v1/editorconfig.go b/vendor/github.com/editorconfig/editorconfig-core-go/v2/editorconfig.go
similarity index 71%
rename from vendor/gopkg.in/editorconfig/editorconfig-core-go.v1/editorconfig.go
rename to vendor/github.com/editorconfig/editorconfig-core-go/v2/editorconfig.go
index d9d629f143..1122612b49 100644
--- a/vendor/gopkg.in/editorconfig/editorconfig-core-go.v1/editorconfig.go
+++ b/vendor/github.com/editorconfig/editorconfig-core-go/v2/editorconfig.go
@@ -15,6 +15,7 @@ import (
 )
 
 const (
+	// ConfigNameDefault represents the name of the configuration file
 	ConfigNameDefault = ".editorconfig"
 )
 
@@ -37,6 +38,7 @@ const (
 	CharsetUTF8    = "utf-8"
 	CharsetUTF16BE = "utf-16be"
 	CharsetUTF16LE = "utf-16le"
+	CharsetUTF8BOM = "utf-8 bom"
 )
 
 // Definition represents a definition inside the .editorconfig file.
@@ -81,28 +83,21 @@ func ParseBytes(data []byte) (*Editorconfig, error) {
 		var (
 			iniSection = iniFile.Section(sectionStr)
 			definition = &Definition{}
-			raw  = make(map[string]string)
+			raw        = make(map[string]string)
 		)
 		err := iniSection.MapTo(&definition)
 		if err != nil {
 			return nil, err
 		}
 
-		// tab_width defaults to indent_size:
-		// https://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties#tab_width
-		if definition.TabWidth <= 0 {
-			if num, err := strconv.Atoi(definition.IndentSize); err == nil {
-				definition.TabWidth = num
-			}
-		}
-
 		// Shallow copy all properties
 		for k, v := range iniSection.KeysHash() {
-			raw[k] = v
+			raw[strings.ToLower(k)] = v
 		}
 
 		definition.Selector = sectionStr
 		definition.Raw = raw
+		definition.normalize()
 		editorConfig.Definitions = append(editorConfig.Definitions, definition)
 	}
 	return editorConfig, nil
@@ -122,44 +117,18 @@ var (
 	regexpBraces = regexp.MustCompile("{.*}")
 )
 
-func filenameMatches(pattern, name string) bool {
-	// basic match
-	matched, _ := filepath.Match(pattern, name)
-	if matched {
-		return true
-	}
-	// foo/bar/main.go should match main.go
-	matched, _ = filepath.Match(pattern, filepath.Base(name))
-	if matched {
-		return true
-	}
-	// foo should match foo/main.go
-	matched, _ = filepath.Match(filepath.Join(pattern, "*"), name)
-	if matched {
-		return true
-	}
-	// *.{js,go} should match main.go
-	if str := regexpBraces.FindString(pattern); len(str) > 0 {
-		// remote initial "{" and final "}"
-		str = strings.TrimPrefix(str, "{")
-		str = strings.TrimSuffix(str, "}")
+// normalize fixes some values to their lowercaes value
+func (d *Definition) normalize() {
+	d.Charset = strings.ToLower(d.Charset)
+	d.EndOfLine = strings.ToLower(d.EndOfLine)
+	d.IndentStyle = strings.ToLower(d.IndentStyle)
 
-		// testing for empty brackets: "{}"
-		if len(str) == 0 {
-			patt := regexpBraces.ReplaceAllString(pattern, "*")
-			matched, _ = filepath.Match(patt, filepath.Base(name))
-			return matched
-		}
-
-		for _, patt := range strings.Split(str, ",") {
-			patt = regexpBraces.ReplaceAllString(pattern, patt)
-			matched, _ = filepath.Match(patt, filepath.Base(name))
-			if matched {
-				return true
-			}
-		}
+	// tab_width defaults to indent_size:
+	// https://github.com/editorconfig/editorconfig/wiki/EditorConfig-Properties#tab_width
+	num, err := strconv.Atoi(d.IndentSize)
+	if err == nil && d.TabWidth <= 0 {
+		d.TabWidth = num
 	}
-	return false
 }
 
 func (d *Definition) merge(md *Definition) {
@@ -192,26 +161,71 @@ func (d *Definition) merge(md *Definition) {
 	}
 }
 
+// InsertToIniFile ... TODO
 func (d *Definition) InsertToIniFile(iniFile *ini.File) {
 	iniSec := iniFile.Section(d.Selector)
 	for k, v := range d.Raw {
-		iniSec.Key(k).SetValue(v)
+		if k == "insert_final_newline" {
+			iniSec.Key(k).SetValue(strconv.FormatBool(d.InsertFinalNewline))
+		} else if k == "trim_trailing_whitespace" {
+			iniSec.Key(k).SetValue(strconv.FormatBool(d.TrimTrailingWhitespace))
+		} else if k == "charset" {
+			iniSec.Key(k).SetValue(d.Charset)
+		} else if k == "end_of_line" {
+			iniSec.Key(k).SetValue(d.EndOfLine)
+		} else if k == "indent_style" {
+			iniSec.Key(k).SetValue(d.IndentStyle)
+		} else if k == "tab_width" {
+			iniSec.Key(k).SetValue(strconv.Itoa(d.TabWidth))
+		} else if k == "indent_size" {
+			iniSec.Key(k).SetValue(d.IndentSize)
+		} else {
+			iniSec.Key(k).SetValue(v)
+		}
+	}
+	if _, ok := d.Raw["indent_size"]; !ok {
+		if d.TabWidth > 0 {
+			iniSec.Key("indent_size").SetValue(strconv.Itoa(d.TabWidth))
+		} else if d.IndentStyle == IndentStyleTab {
+			iniSec.Key("indent_size").SetValue(IndentStyleTab)
+		}
+	}
+
+	if _, ok := d.Raw["tab_width"]; !ok && len(d.IndentSize) > 0 {
+		if _, err := strconv.Atoi(d.IndentSize); err == nil {
+			iniSec.Key("tab_width").SetValue(d.IndentSize)
+		}
 	}
 }
 
 // GetDefinitionForFilename returns a definition for the given filename.
 // The result is a merge of the selectors that matched the file.
 // The last section has preference over the priors.
-func (e *Editorconfig) GetDefinitionForFilename(name string) *Definition {
+func (e *Editorconfig) GetDefinitionForFilename(name string) (*Definition, error) {
 	def := &Definition{}
 	def.Raw = make(map[string]string)
 	for i := len(e.Definitions) - 1; i >= 0; i-- {
 		actualDef := e.Definitions[i]
-		if filenameMatches(actualDef.Selector, name) {
+		selector := actualDef.Selector
+		if !strings.HasPrefix(selector, "/") {
+			if strings.ContainsRune(selector, '/') {
+				selector = "/" + selector
+			} else {
+				selector = "/**/" + selector
+			}
+		}
+		if !strings.HasPrefix(name, "/") {
+			name = "/" + name
+		}
+		ok, err := FnmatchCase(selector, name)
+		if err != nil {
+			return nil, err
+		}
+		if ok {
 			def.merge(actualDef)
 		}
 	}
-	return def
+	return def, nil
 }
 
 func boolToString(b bool) string {
@@ -279,10 +293,23 @@ func GetDefinitionForFilenameWithConfigname(filename string, configname string)
 		if err != nil {
 			return nil, err
 		}
-		definition.merge(ec.GetDefinitionForFilename(filename))
+
+		relativeFilename := filename
+		if len(dir) < len(abs) {
+			relativeFilename = abs[len(dir):]
+		}
+
+		def, err := ec.GetDefinitionForFilename(relativeFilename)
+		if err != nil {
+			return nil, err
+		}
+
+		definition.merge(def)
+
 		if ec.Root {
 			break
 		}
 	}
+
 	return definition, nil
 }
diff --git a/vendor/github.com/editorconfig/editorconfig-core-go/v2/fnmatch.go b/vendor/github.com/editorconfig/editorconfig-core-go/v2/fnmatch.go
new file mode 100644
index 0000000000..c33544cf78
--- /dev/null
+++ b/vendor/github.com/editorconfig/editorconfig-core-go/v2/fnmatch.go
@@ -0,0 +1,177 @@
+package editorconfig
+
+import (
+	"fmt"
+	"regexp"
+	"strconv"
+	"strings"
+)
+
+var (
+	// findLeftBrackets matches the opening left bracket {
+	findLeftBrackets = regexp.MustCompile(`(^|[^\\])\{`)
+	// findLeftBrackets matches the closing right bracket {
+	findRightBrackets = regexp.MustCompile(`(^|[^\\])\}`)
+	// findNumericRange matches a range of number, e.g. -2..5
+	findNumericRange = regexp.MustCompile(`^([+-]?\d+)\.\.([+-]?\d+)$`)
+)
+
+// FnmatchCase tests whether the name matches the given pattern case included.
+func FnmatchCase(pattern, name string) (bool, error) {
+	p, err := translate(pattern)
+	if err != nil {
+		return false, err
+	}
+
+	r, err := regexp.Compile(fmt.Sprintf("^%s$", p))
+	if err != nil {
+		return false, err
+	}
+
+	return r.MatchString(name), nil
+}
+
+func translate(pattern string) (string, error) {
+	index := 0
+	pat := []rune(pattern)
+	length := len(pat)
+
+	result := strings.Builder{}
+
+	braceLevel := 0
+	isEscaped := false
+	inBrackets := false
+
+	matchesBraces := len(findLeftBrackets.FindAllString(pattern, -1)) == len(findRightBrackets.FindAllString(pattern, -1))
+
+	for index < length {
+		r := pat[index]
+		index++
+
+		if r == '*' {
+			p := index
+			if p < length && pat[p] == '*' {
+				result.WriteString(".*")
+				index++
+			} else {
+				result.WriteString("[^/]*")
+			}
+		} else if r == '/' {
+			p := index
+			if p+2 < length && pat[p] == '*' && pat[p+1] == '*' && pat[p+2] == '/' {
+				result.WriteString("(?:/|/.*/)")
+				index += 3
+			} else {
+				result.WriteRune(r)
+			}
+		} else if r == '?' {
+			result.WriteString("[^/]")
+		} else if r == '[' {
+			if inBrackets {
+				result.WriteString("\\[")
+			} else {
+				hasSlash := false
+				res := strings.Builder{}
+
+				p := index
+				for p < length {
+					if pat[p] == ']' && pat[p-1] != '\\' {
+						break
+					}
+					res.WriteRune(pat[p])
+					if pat[p] == '/' && pat[p-1] != '\\' {
+						hasSlash = true
+						break
+					}
+					p++
+				}
+				if hasSlash {
+					result.WriteString("\\[" + res.String())
+					index = p + 1
+				} else {
+					inBrackets = true
+					if index < length && pat[index] == '!' || pat[index] == '^' {
+						index++
+						result.WriteString("[^")
+					} else {
+						result.WriteRune('[')
+					}
+				}
+			}
+		} else if r == ']' {
+			if inBrackets && pat[index-2] == '\\' {
+				result.WriteString("\\]")
+			} else {
+				result.WriteRune(r)
+				inBrackets = false
+			}
+		} else if r == '{' {
+			hasComma := false
+			p := index
+			res := strings.Builder{}
+
+			for p < length {
+				if pat[p] == '}' && pat[p-1] != '\\' {
+					break
+				}
+				res.WriteRune(pat[p])
+				if pat[p] == ',' && pat[p-1] != '\\' {
+					hasComma = true
+					break
+				}
+				p++
+			}
+
+			if !hasComma && p < length {
+				inner := res.String()
+				sub := findNumericRange.FindStringSubmatch(inner)
+				if len(sub) == 3 {
+					from, _ := strconv.Atoi(sub[1])
+					to, _ := strconv.Atoi(sub[2])
+					result.WriteString("(?:")
+					// XXX does not scale well
+					for i := from; i < to; i++ {
+						result.WriteString(strconv.Itoa(i))
+						result.WriteRune('|')
+					}
+					result.WriteString(strconv.Itoa(to))
+					result.WriteRune(')')
+				} else {
+					r, _ := translate(inner)
+					result.WriteString(fmt.Sprintf("\\{%s\\}", r))
+				}
+				index = p + 1
+			} else if matchesBraces {
+				result.WriteString("(?:")
+				braceLevel++
+			} else {
+				result.WriteString("\\{")
+			}
+		} else if r == '}' {
+			if braceLevel > 0 {
+				if isEscaped {
+					result.WriteRune('}')
+					isEscaped = false
+				} else {
+					result.WriteRune(')')
+					braceLevel--
+				}
+			} else {
+				result.WriteString("\\}")
+			}
+		} else if r == ',' {
+			if braceLevel == 0 || isEscaped {
+				result.WriteRune(r)
+			} else {
+				result.WriteRune('|')
+			}
+		} else if r != '\\' || isEscaped {
+			result.WriteString(regexp.QuoteMeta(string(r)))
+			isEscaped = false
+		} else {
+			isEscaped = true
+		}
+	}
+
+	return result.String(), nil
+}
diff --git a/vendor/github.com/editorconfig/editorconfig-core-go/v2/go.mod b/vendor/github.com/editorconfig/editorconfig-core-go/v2/go.mod
new file mode 100644
index 0000000000..e4364b8b45
--- /dev/null
+++ b/vendor/github.com/editorconfig/editorconfig-core-go/v2/go.mod
@@ -0,0 +1,9 @@
+module github.com/editorconfig/editorconfig-core-go/v2
+
+go 1.12
+
+require (
+	github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a // indirect
+	github.com/stretchr/testify v1.3.0
+	gopkg.in/ini.v1 v1.42.0
+)
diff --git a/vendor/github.com/editorconfig/editorconfig-core-go/v2/go.sum b/vendor/github.com/editorconfig/editorconfig-core-go/v2/go.sum
new file mode 100644
index 0000000000..061cdf586b
--- /dev/null
+++ b/vendor/github.com/editorconfig/editorconfig-core-go/v2/go.sum
@@ -0,0 +1,22 @@
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
+github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
+github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
+github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM=
+github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
+github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a h1:pa8hGb/2YqsZKovtsgrwcDH1RZhVbTKCjLp47XpqCDs=
+github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
+gopkg.in/ini.v1 v1.42.0 h1:7N3gPTt50s8GuLortA00n8AqRTk75qOP98+mTPpgzRk=
+gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
diff --git a/vendor/github.com/facebookgo/clock/LICENSE b/vendor/github.com/facebookgo/clock/LICENSE
deleted file mode 100644
index ce212cb1ce..0000000000
--- a/vendor/github.com/facebookgo/clock/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2014 Ben Johnson
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/vendor/github.com/facebookgo/clock/README.md b/vendor/github.com/facebookgo/clock/README.md
deleted file mode 100644
index 5d4f4fe72e..0000000000
--- a/vendor/github.com/facebookgo/clock/README.md
+++ /dev/null
@@ -1,104 +0,0 @@
-clock [![Build Status](https://drone.io/github.com/benbjohnson/clock/status.png)](https://drone.io/github.com/benbjohnson/clock/latest) [![Coverage Status](https://coveralls.io/repos/benbjohnson/clock/badge.png?branch=master)](https://coveralls.io/r/benbjohnson/clock?branch=master) [![GoDoc](https://godoc.org/github.com/benbjohnson/clock?status.png)](https://godoc.org/github.com/benbjohnson/clock) ![Project status](http://img.shields.io/status/experimental.png?color=red)
-=====
-
-Clock is a small library for mocking time in Go. It provides an interface
-around the standard library's [`time`][time] package so that the application
-can use the realtime clock while tests can use the mock clock.
-
-[time]: http://golang.org/pkg/time/
-
-
-## Usage
-
-### Realtime Clock
-
-Your application can maintain a `Clock` variable that will allow realtime and
-mock clocks to be interchangable. For example, if you had an `Application` type:
-
-```go
-import "github.com/benbjohnson/clock"
-
-type Application struct {
-	Clock clock.Clock
-}
-```
-
-You could initialize it to use the realtime clock like this:
-
-```go
-var app Application
-app.Clock = clock.New()
-...
-```
-
-Then all timers and time-related functionality should be performed from the
-`Clock` variable.
-
-
-### Mocking time
-
-In your tests, you will want to use a `Mock` clock:
-
-```go
-import (
-	"testing"
-
-	"github.com/benbjohnson/clock"
-)
-
-func TestApplication_DoSomething(t *testing.T) {
-	mock := clock.NewMock()
-	app := Application{Clock: mock}
-	...
-}
-```
-
-Now that you've initialized your application to use the mock clock, you can
-adjust the time programmatically. The mock clock always starts from the Unix
-epoch (midnight, Jan 1, 1970 UTC).
-
-
-### Controlling time
-
-The mock clock provides the same functions that the standard library's `time`
-package provides. For example, to find the current time, you use the `Now()`
-function:
-
-```go
-mock := clock.NewMock()
-
-// Find the current time.
-mock.Now().UTC() // 1970-01-01 00:00:00 +0000 UTC
-
-// Move the clock forward.
-mock.Add(2 * time.Hour)
-
-// Check the time again. It's 2 hours later!
-mock.Now().UTC() // 1970-01-01 02:00:00 +0000 UTC
-```
-
-Timers and Tickers are also controlled by this same mock clock. They will only
-execute when the clock is moved forward:
-
-```
-mock := clock.NewMock()
-count := 0
-
-// Kick off a timer to increment every 1 mock second.
-go func() {
-    ticker := clock.Ticker(1 * time.Second)
-    for {
-        <-ticker.C
-        count++
-    }
-}()
-runtime.Gosched()
-
-// Move the clock forward 10 second.
-mock.Add(10 * time.Second)
-
-// This prints 10.
-fmt.Println(count)
-```
-
-
diff --git a/vendor/github.com/facebookgo/clock/clock.go b/vendor/github.com/facebookgo/clock/clock.go
deleted file mode 100644
index bca1a7ba8b..0000000000
--- a/vendor/github.com/facebookgo/clock/clock.go
+++ /dev/null
@@ -1,363 +0,0 @@
-package clock
-
-import (
-	"runtime"
-	"sort"
-	"sync"
-	"time"
-)
-
-// Clock represents an interface to the functions in the standard library time
-// package. Two implementations are available in the clock package. The first
-// is a real-time clock which simply wraps the time package's functions. The
-// second is a mock clock which will only make forward progress when
-// programmatically adjusted.
-type Clock interface {
-	After(d time.Duration) <-chan time.Time
-	AfterFunc(d time.Duration, f func()) *Timer
-	Now() time.Time
-	Sleep(d time.Duration)
-	Tick(d time.Duration) <-chan time.Time
-	Ticker(d time.Duration) *Ticker
-	Timer(d time.Duration) *Timer
-}
-
-// New returns an instance of a real-time clock.
-func New() Clock {
-	return &clock{}
-}
-
-// clock implements a real-time clock by simply wrapping the time package functions.
-type clock struct{}
-
-func (c *clock) After(d time.Duration) <-chan time.Time { return time.After(d) }
-
-func (c *clock) AfterFunc(d time.Duration, f func()) *Timer {
-	return &Timer{timer: time.AfterFunc(d, f)}
-}
-
-func (c *clock) Now() time.Time { return time.Now() }
-
-func (c *clock) Sleep(d time.Duration) { time.Sleep(d) }
-
-func (c *clock) Tick(d time.Duration) <-chan time.Time { return time.Tick(d) }
-
-func (c *clock) Ticker(d time.Duration) *Ticker {
-	t := time.NewTicker(d)
-	return &Ticker{C: t.C, ticker: t}
-}
-
-func (c *clock) Timer(d time.Duration) *Timer {
-	t := time.NewTimer(d)
-	return &Timer{C: t.C, timer: t}
-}
-
-// Mock represents a mock clock that only moves forward programmically.
-// It can be preferable to a real-time clock when testing time-based functionality.
-type Mock struct {
-	mu     sync.Mutex
-	now    time.Time   // current time
-	timers clockTimers // tickers & timers
-
-	calls      Calls
-	waiting    []waiting
-	callsMutex sync.Mutex
-}
-
-// NewMock returns an instance of a mock clock.
-// The current time of the mock clock on initialization is the Unix epoch.
-func NewMock() *Mock {
-	return &Mock{now: time.Unix(0, 0)}
-}
-
-// Add moves the current time of the mock clock forward by the duration.
-// This should only be called from a single goroutine at a time.
-func (m *Mock) Add(d time.Duration) {
-	// Calculate the final current time.
-	t := m.now.Add(d)
-
-	// Continue to execute timers until there are no more before the new time.
-	for {
-		if !m.runNextTimer(t) {
-			break
-		}
-	}
-
-	// Ensure that we end with the new time.
-	m.mu.Lock()
-	m.now = t
-	m.mu.Unlock()
-
-	// Give a small buffer to make sure the other goroutines get handled.
-	gosched()
-}
-
-// runNextTimer executes the next timer in chronological order and moves the
-// current time to the timer's next tick time. The next time is not executed if
-// it's next time if after the max time. Returns true if a timer is executed.
-func (m *Mock) runNextTimer(max time.Time) bool {
-	m.mu.Lock()
-
-	// Sort timers by time.
-	sort.Sort(m.timers)
-
-	// If we have no more timers then exit.
-	if len(m.timers) == 0 {
-		m.mu.Unlock()
-		return false
-	}
-
-	// Retrieve next timer. Exit if next tick is after new time.
-	t := m.timers[0]
-	if t.Next().After(max) {
-		m.mu.Unlock()
-		return false
-	}
-
-	// Move "now" forward and unlock clock.
-	m.now = t.Next()
-	m.mu.Unlock()
-
-	// Execute timer.
-	t.Tick(m.now)
-	return true
-}
-
-// After waits for the duration to elapse and then sends the current time on the returned channel.
-func (m *Mock) After(d time.Duration) <-chan time.Time {
-	defer m.inc(&m.calls.After)
-	return m.Timer(d).C
-}
-
-// AfterFunc waits for the duration to elapse and then executes a function.
-// A Timer is returned that can be stopped.
-func (m *Mock) AfterFunc(d time.Duration, f func()) *Timer {
-	defer m.inc(&m.calls.AfterFunc)
-	t := m.Timer(d)
-	t.C = nil
-	t.fn = f
-	return t
-}
-
-// Now returns the current wall time on the mock clock.
-func (m *Mock) Now() time.Time {
-	defer m.inc(&m.calls.Now)
-	m.mu.Lock()
-	defer m.mu.Unlock()
-	return m.now
-}
-
-// Sleep pauses the goroutine for the given duration on the mock clock.
-// The clock must be moved forward in a separate goroutine.
-func (m *Mock) Sleep(d time.Duration) {
-	defer m.inc(&m.calls.Sleep)
-	<-m.After(d)
-}
-
-// Tick is a convenience function for Ticker().
-// It will return a ticker channel that cannot be stopped.
-func (m *Mock) Tick(d time.Duration) <-chan time.Time {
-	defer m.inc(&m.calls.Tick)
-	return m.Ticker(d).C
-}
-
-// Ticker creates a new instance of Ticker.
-func (m *Mock) Ticker(d time.Duration) *Ticker {
-	defer m.inc(&m.calls.Ticker)
-	m.mu.Lock()
-	defer m.mu.Unlock()
-	ch := make(chan time.Time)
-	t := &Ticker{
-		C:    ch,
-		c:    ch,
-		mock: m,
-		d:    d,
-		next: m.now.Add(d),
-	}
-	m.timers = append(m.timers, (*internalTicker)(t))
-	return t
-}
-
-// Timer creates a new instance of Timer.
-func (m *Mock) Timer(d time.Duration) *Timer {
-	defer m.inc(&m.calls.Timer)
-	m.mu.Lock()
-	defer m.mu.Unlock()
-	ch := make(chan time.Time)
-	t := &Timer{
-		C:    ch,
-		c:    ch,
-		mock: m,
-		next: m.now.Add(d),
-	}
-	m.timers = append(m.timers, (*internalTimer)(t))
-	return t
-}
-
-func (m *Mock) removeClockTimer(t clockTimer) {
-	m.mu.Lock()
-	defer m.mu.Unlock()
-	for i, timer := range m.timers {
-		if timer == t {
-			copy(m.timers[i:], m.timers[i+1:])
-			m.timers[len(m.timers)-1] = nil
-			m.timers = m.timers[:len(m.timers)-1]
-			break
-		}
-	}
-	sort.Sort(m.timers)
-}
-
-func (m *Mock) inc(addr *uint32) {
-	m.callsMutex.Lock()
-	defer m.callsMutex.Unlock()
-	*addr++
-	var newWaiting []waiting
-	for _, w := range m.waiting {
-		if m.calls.atLeast(w.expected) {
-			close(w.done)
-			continue
-		}
-		newWaiting = append(newWaiting, w)
-	}
-	m.waiting = newWaiting
-}
-
-// Wait waits for at least the relevant calls before returning. The expected
-// Calls are always over the lifetime of the Mock. Values in the Calls struct
-// are used as the minimum number of calls, this allows you to wait for only
-// the calls you care about.
-func (m *Mock) Wait(s Calls) {
-	m.callsMutex.Lock()
-	if m.calls.atLeast(s) {
-		m.callsMutex.Unlock()
-		return
-	}
-	done := make(chan struct{})
-	m.waiting = append(m.waiting, waiting{expected: s, done: done})
-	m.callsMutex.Unlock()
-	<-done
-}
-
-// clockTimer represents an object with an associated start time.
-type clockTimer interface {
-	Next() time.Time
-	Tick(time.Time)
-}
-
-// clockTimers represents a list of sortable timers.
-type clockTimers []clockTimer
-
-func (a clockTimers) Len() int           { return len(a) }
-func (a clockTimers) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
-func (a clockTimers) Less(i, j int) bool { return a[i].Next().Before(a[j].Next()) }
-
-// Timer represents a single event.
-// The current time will be sent on C, unless the timer was created by AfterFunc.
-type Timer struct {
-	C     <-chan time.Time
-	c     chan time.Time
-	timer *time.Timer // realtime impl, if set
-	next  time.Time   // next tick time
-	mock  *Mock       // mock clock, if set
-	fn    func()      // AfterFunc function, if set
-}
-
-// Stop turns off the ticker.
-func (t *Timer) Stop() {
-	if t.timer != nil {
-		t.timer.Stop()
-	} else {
-		t.mock.removeClockTimer((*internalTimer)(t))
-	}
-}
-
-type internalTimer Timer
-
-func (t *internalTimer) Next() time.Time { return t.next }
-func (t *internalTimer) Tick(now time.Time) {
-	if t.fn != nil {
-		t.fn()
-	} else {
-		t.c <- now
-	}
-	t.mock.removeClockTimer((*internalTimer)(t))
-	gosched()
-}
-
-// Ticker holds a channel that receives "ticks" at regular intervals.
-type Ticker struct {
-	C      <-chan time.Time
-	c      chan time.Time
-	ticker *time.Ticker  // realtime impl, if set
-	next   time.Time     // next tick time
-	mock   *Mock         // mock clock, if set
-	d      time.Duration // time between ticks
-}
-
-// Stop turns off the ticker.
-func (t *Ticker) Stop() {
-	if t.ticker != nil {
-		t.ticker.Stop()
-	} else {
-		t.mock.removeClockTimer((*internalTicker)(t))
-	}
-}
-
-type internalTicker Ticker
-
-func (t *internalTicker) Next() time.Time { return t.next }
-func (t *internalTicker) Tick(now time.Time) {
-	select {
-	case t.c <- now:
-	case <-time.After(1 * time.Millisecond):
-	}
-	t.next = now.Add(t.d)
-	gosched()
-}
-
-// Sleep momentarily so that other goroutines can process.
-func gosched() { runtime.Gosched() }
-
-// Calls keeps track of the count of calls for each of the methods on the Clock
-// interface.
-type Calls struct {
-	After     uint32
-	AfterFunc uint32
-	Now       uint32
-	Sleep     uint32
-	Tick      uint32
-	Ticker    uint32
-	Timer     uint32
-}
-
-// atLeast returns true if at least the number of calls in o have been made.
-func (c Calls) atLeast(o Calls) bool {
-	if c.After < o.After {
-		return false
-	}
-	if c.AfterFunc < o.AfterFunc {
-		return false
-	}
-	if c.Now < o.Now {
-		return false
-	}
-	if c.Sleep < o.Sleep {
-		return false
-	}
-	if c.Tick < o.Tick {
-		return false
-	}
-	if c.Ticker < o.Ticker {
-		return false
-	}
-	if c.Timer < o.Timer {
-		return false
-	}
-	return true
-}
-
-type waiting struct {
-	expected Calls
-	done     chan struct{}
-}
diff --git a/vendor/github.com/facebookgo/grace/gracehttp/http.go b/vendor/github.com/facebookgo/grace/gracehttp/http.go
deleted file mode 100644
index 4ba8d284c8..0000000000
--- a/vendor/github.com/facebookgo/grace/gracehttp/http.go
+++ /dev/null
@@ -1,186 +0,0 @@
-// Package gracehttp provides easy to use graceful restart
-// functionality for HTTP server.
-package gracehttp
-
-import (
-	"bytes"
-	"crypto/tls"
-	"flag"
-	"fmt"
-	"log"
-	"net"
-	"net/http"
-	"os"
-	"os/signal"
-	"sync"
-	"syscall"
-
-	"github.com/facebookgo/grace/gracenet"
-	"github.com/facebookgo/httpdown"
-)
-
-var (
-	verbose    = flag.Bool("gracehttp.log", true, "Enable logging.")
-	didInherit = os.Getenv("LISTEN_FDS") != ""
-	ppid       = os.Getppid()
-)
-
-// An app contains one or more servers and associated configuration.
-type app struct {
-	servers   []*http.Server
-	http      *httpdown.HTTP
-	net       *gracenet.Net
-	listeners []net.Listener
-	sds       []httpdown.Server
-	errors    chan error
-}
-
-func newApp(servers []*http.Server) *app {
-	return &app{
-		servers:   servers,
-		http:      &httpdown.HTTP{},
-		net:       &gracenet.Net{},
-		listeners: make([]net.Listener, 0, len(servers)),
-		sds:       make([]httpdown.Server, 0, len(servers)),
-
-		// 2x num servers for possible Close or Stop errors + 1 for possible
-		// StartProcess error.
-		errors: make(chan error, 1+(len(servers)*2)),
-	}
-}
-
-func (a *app) listen() error {
-	for _, s := range a.servers {
-		// TODO: default addresses
-		l, err := a.net.Listen("tcp", s.Addr)
-		if err != nil {
-			return err
-		}
-		if s.TLSConfig != nil {
-			l = tls.NewListener(l, s.TLSConfig)
-		}
-		a.listeners = append(a.listeners, l)
-	}
-	return nil
-}
-
-func (a *app) serve() {
-	for i, s := range a.servers {
-		a.sds = append(a.sds, a.http.Serve(s, a.listeners[i]))
-	}
-}
-
-func (a *app) wait() {
-	var wg sync.WaitGroup
-	wg.Add(len(a.sds) * 2) // Wait & Stop
-	go a.signalHandler(&wg)
-	for _, s := range a.sds {
-		go func(s httpdown.Server) {
-			defer wg.Done()
-			if err := s.Wait(); err != nil {
-				a.errors <- err
-			}
-		}(s)
-	}
-	wg.Wait()
-}
-
-func (a *app) term(wg *sync.WaitGroup) {
-	for _, s := range a.sds {
-		go func(s httpdown.Server) {
-			defer wg.Done()
-			if err := s.Stop(); err != nil {
-				a.errors <- err
-			}
-		}(s)
-	}
-}
-
-func (a *app) signalHandler(wg *sync.WaitGroup) {
-	ch := make(chan os.Signal, 10)
-	signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM, syscall.SIGUSR2)
-	for {
-		sig := <-ch
-		switch sig {
-		case syscall.SIGINT, syscall.SIGTERM:
-			// this ensures a subsequent INT/TERM will trigger standard go behaviour of
-			// terminating.
-			signal.Stop(ch)
-			a.term(wg)
-			return
-		case syscall.SIGUSR2:
-			// we only return here if there's an error, otherwise the new process
-			// will send us a TERM when it's ready to trigger the actual shutdown.
-			if _, err := a.net.StartProcess(); err != nil {
-				a.errors <- err
-			}
-		}
-	}
-}
-
-// Serve will serve the given http.Servers and will monitor for signals
-// allowing for graceful termination (SIGTERM) or restart (SIGUSR2).
-func Serve(servers ...*http.Server) error {
-	a := newApp(servers)
-
-	// Acquire Listeners
-	if err := a.listen(); err != nil {
-		return err
-	}
-
-	// Some useful logging.
-	if *verbose {
-		if didInherit {
-			if ppid == 1 {
-				log.Printf("Listening on init activated %s", pprintAddr(a.listeners))
-			} else {
-				const msg = "Graceful handoff of %s with new pid %d and old pid %d"
-				log.Printf(msg, pprintAddr(a.listeners), os.Getpid(), ppid)
-			}
-		} else {
-			const msg = "Serving %s with pid %d"
-			log.Printf(msg, pprintAddr(a.listeners), os.Getpid())
-		}
-	}
-
-	// Start serving.
-	a.serve()
-
-	// Close the parent if we inherited and it wasn't init that started us.
-	if didInherit && ppid != 1 {
-		if err := syscall.Kill(ppid, syscall.SIGTERM); err != nil {
-			return fmt.Errorf("failed to close parent: %s", err)
-		}
-	}
-
-	waitdone := make(chan struct{})
-	go func() {
-		defer close(waitdone)
-		a.wait()
-	}()
-
-	select {
-	case err := <-a.errors:
-		if err == nil {
-			panic("unexpected nil error")
-		}
-		return err
-	case <-waitdone:
-		if *verbose {
-			log.Printf("Exiting pid %d.", os.Getpid())
-		}
-		return nil
-	}
-}
-
-// Used for pretty printing addresses.
-func pprintAddr(listeners []net.Listener) []byte {
-	var out bytes.Buffer
-	for i, l := range listeners {
-		if i != 0 {
-			fmt.Fprint(&out, ", ")
-		}
-		fmt.Fprint(&out, l.Addr())
-	}
-	return out.Bytes()
-}
diff --git a/vendor/github.com/facebookgo/grace/gracenet/net.go b/vendor/github.com/facebookgo/grace/gracenet/net.go
deleted file mode 100644
index a980954a9d..0000000000
--- a/vendor/github.com/facebookgo/grace/gracenet/net.go
+++ /dev/null
@@ -1,252 +0,0 @@
-// Package gracenet provides a family of Listen functions that either open a
-// fresh connection or provide an inherited connection from when the process
-// was started. The behave like their counterparts in the net package, but
-// transparently provide support for graceful restarts without dropping
-// connections. This is provided in a systemd socket activation compatible form
-// to allow using socket activation.
-//
-// BUG: Doesn't handle closing of listeners.
-package gracenet
-
-import (
-	"fmt"
-	"net"
-	"os"
-	"os/exec"
-	"strconv"
-	"strings"
-	"sync"
-)
-
-const (
-	// Used to indicate a graceful restart in the new process.
-	envCountKey       = "LISTEN_FDS"
-	envCountKeyPrefix = envCountKey + "="
-)
-
-// In order to keep the working directory the same as when we started we record
-// it at startup.
-var originalWD, _ = os.Getwd()
-
-// Net provides the family of Listen functions and maintains the associated
-// state. Typically you will have only once instance of Net per application.
-type Net struct {
-	inherited   []net.Listener
-	active      []net.Listener
-	mutex       sync.Mutex
-	inheritOnce sync.Once
-
-	// used in tests to override the default behavior of starting from fd 3.
-	fdStart int
-}
-
-func (n *Net) inherit() error {
-	var retErr error
-	n.inheritOnce.Do(func() {
-		n.mutex.Lock()
-		defer n.mutex.Unlock()
-		countStr := os.Getenv(envCountKey)
-		if countStr == "" {
-			return
-		}
-		count, err := strconv.Atoi(countStr)
-		if err != nil {
-			retErr = fmt.Errorf("found invalid count value: %s=%s", envCountKey, countStr)
-			return
-		}
-
-		// In tests this may be overridden.
-		fdStart := n.fdStart
-		if fdStart == 0 {
-			// In normal operations if we are inheriting, the listeners will begin at
-			// fd 3.
-			fdStart = 3
-		}
-
-		for i := fdStart; i < fdStart+count; i++ {
-			file := os.NewFile(uintptr(i), "listener")
-			l, err := net.FileListener(file)
-			if err != nil {
-				file.Close()
-				retErr = fmt.Errorf("error inheriting socket fd %d: %s", i, err)
-				return
-			}
-			if err := file.Close(); err != nil {
-				retErr = fmt.Errorf("error closing inherited socket fd %d: %s", i, err)
-				return
-			}
-			n.inherited = append(n.inherited, l)
-		}
-	})
-	return retErr
-}
-
-// Listen announces on the local network address laddr. The network net must be
-// a stream-oriented network: "tcp", "tcp4", "tcp6", "unix" or "unixpacket". It
-// returns an inherited net.Listener for the matching network and address, or
-// creates a new one using net.Listen.
-func (n *Net) Listen(nett, laddr string) (net.Listener, error) {
-	switch nett {
-	default:
-		return nil, net.UnknownNetworkError(nett)
-	case "tcp", "tcp4", "tcp6":
-		addr, err := net.ResolveTCPAddr(nett, laddr)
-		if err != nil {
-			return nil, err
-		}
-		return n.ListenTCP(nett, addr)
-	case "unix", "unixpacket", "invalid_unix_net_for_test":
-		addr, err := net.ResolveUnixAddr(nett, laddr)
-		if err != nil {
-			return nil, err
-		}
-		return n.ListenUnix(nett, addr)
-	}
-}
-
-// ListenTCP announces on the local network address laddr. The network net must
-// be: "tcp", "tcp4" or "tcp6". It returns an inherited net.Listener for the
-// matching network and address, or creates a new one using net.ListenTCP.
-func (n *Net) ListenTCP(nett string, laddr *net.TCPAddr) (*net.TCPListener, error) {
-	if err := n.inherit(); err != nil {
-		return nil, err
-	}
-
-	n.mutex.Lock()
-	defer n.mutex.Unlock()
-
-	// look for an inherited listener
-	for i, l := range n.inherited {
-		if l == nil { // we nil used inherited listeners
-			continue
-		}
-		if isSameAddr(l.Addr(), laddr) {
-			n.inherited[i] = nil
-			n.active = append(n.active, l)
-			return l.(*net.TCPListener), nil
-		}
-	}
-
-	// make a fresh listener
-	l, err := net.ListenTCP(nett, laddr)
-	if err != nil {
-		return nil, err
-	}
-	n.active = append(n.active, l)
-	return l, nil
-}
-
-// ListenUnix announces on the local network address laddr. The network net
-// must be a: "unix" or "unixpacket". It returns an inherited net.Listener for
-// the matching network and address, or creates a new one using net.ListenUnix.
-func (n *Net) ListenUnix(nett string, laddr *net.UnixAddr) (*net.UnixListener, error) {
-	if err := n.inherit(); err != nil {
-		return nil, err
-	}
-
-	n.mutex.Lock()
-	defer n.mutex.Unlock()
-
-	// look for an inherited listener
-	for i, l := range n.inherited {
-		if l == nil { // we nil used inherited listeners
-			continue
-		}
-		if isSameAddr(l.Addr(), laddr) {
-			n.inherited[i] = nil
-			n.active = append(n.active, l)
-			return l.(*net.UnixListener), nil
-		}
-	}
-
-	// make a fresh listener
-	l, err := net.ListenUnix(nett, laddr)
-	if err != nil {
-		return nil, err
-	}
-	n.active = append(n.active, l)
-	return l, nil
-}
-
-// activeListeners returns a snapshot copy of the active listeners.
-func (n *Net) activeListeners() ([]net.Listener, error) {
-	n.mutex.Lock()
-	defer n.mutex.Unlock()
-	ls := make([]net.Listener, len(n.active))
-	copy(ls, n.active)
-	return ls, nil
-}
-
-func isSameAddr(a1, a2 net.Addr) bool {
-	if a1.Network() != a2.Network() {
-		return false
-	}
-	a1s := a1.String()
-	a2s := a2.String()
-	if a1s == a2s {
-		return true
-	}
-
-	// This allows for ipv6 vs ipv4 local addresses to compare as equal. This
-	// scenario is common when listening on localhost.
-	const ipv6prefix = "[::]"
-	a1s = strings.TrimPrefix(a1s, ipv6prefix)
-	a2s = strings.TrimPrefix(a2s, ipv6prefix)
-	const ipv4prefix = "0.0.0.0"
-	a1s = strings.TrimPrefix(a1s, ipv4prefix)
-	a2s = strings.TrimPrefix(a2s, ipv4prefix)
-	return a1s == a2s
-}
-
-// StartProcess starts a new process passing it the active listeners. It
-// doesn't fork, but starts a new process using the same environment and
-// arguments as when it was originally started. This allows for a newly
-// deployed binary to be started. It returns the pid of the newly started
-// process when successful.
-func (n *Net) StartProcess() (int, error) {
-	listeners, err := n.activeListeners()
-	if err != nil {
-		return 0, err
-	}
-
-	// Extract the fds from the listeners.
-	files := make([]*os.File, len(listeners))
-	for i, l := range listeners {
-		files[i], err = l.(filer).File()
-		if err != nil {
-			return 0, err
-		}
-		defer files[i].Close()
-	}
-
-	// Use the original binary location. This works with symlinks such that if
-	// the file it points to has been changed we will use the updated symlink.
-	argv0, err := exec.LookPath(os.Args[0])
-	if err != nil {
-		return 0, err
-	}
-
-	// Pass on the environment and replace the old count key with the new one.
-	var env []string
-	for _, v := range os.Environ() {
-		if !strings.HasPrefix(v, envCountKeyPrefix) {
-			env = append(env, v)
-		}
-	}
-	env = append(env, fmt.Sprintf("%s%d", envCountKeyPrefix, len(listeners)))
-
-	allFiles := append([]*os.File{os.Stdin, os.Stdout, os.Stderr}, files...)
-	process, err := os.StartProcess(argv0, os.Args, &os.ProcAttr{
-		Dir:   originalWD,
-		Env:   env,
-		Files: allFiles,
-	})
-	if err != nil {
-		return 0, err
-	}
-	return process.Pid, nil
-}
-
-type filer interface {
-	File() (*os.File, error)
-}
diff --git a/vendor/github.com/facebookgo/httpdown/.travis.yml b/vendor/github.com/facebookgo/httpdown/.travis.yml
deleted file mode 100644
index ea316cfe50..0000000000
--- a/vendor/github.com/facebookgo/httpdown/.travis.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-language: go
-
-go:
-  - 1.6
-
-before_install:
-  - go get -v golang.org/x/tools/cmd/vet
-  - go get -v golang.org/x/tools/cmd/cover
-  - go get -v github.com/golang/lint/golint
-
-install:
-  - go install -race -v std
-  - go get -race -t -v ./...
-  - go install -race -v ./...
-
-script:
-  - go vet ./...
-  - $HOME/gopath/bin/golint .
-  - go test -cpu=2 -race -v ./...
-  - go test -cpu=2 -covermode=atomic -coverprofile=coverage.txt ./
-
-after_success:
-  - bash <(curl -s https://codecov.io/bash)
diff --git a/vendor/github.com/facebookgo/httpdown/httpdown.go b/vendor/github.com/facebookgo/httpdown/httpdown.go
deleted file mode 100644
index 34c5dea9ff..0000000000
--- a/vendor/github.com/facebookgo/httpdown/httpdown.go
+++ /dev/null
@@ -1,376 +0,0 @@
-// Package httpdown provides http.ConnState enabled graceful termination of
-// http.Server.
-package httpdown
-
-import (
-	"crypto/tls"
-	"fmt"
-	"net"
-	"net/http"
-	"os"
-	"os/signal"
-	"sync"
-	"syscall"
-	"time"
-
-	"github.com/facebookgo/clock"
-	"github.com/facebookgo/stats"
-)
-
-const (
-	defaultStopTimeout = time.Minute
-	defaultKillTimeout = time.Minute
-)
-
-// A Server allows encapsulates the process of accepting new connections and
-// serving them, and gracefully shutting down the listener without dropping
-// active connections.
-type Server interface {
-	// Wait waits for the serving loop to finish. This will happen when Stop is
-	// called, at which point it returns no error, or if there is an error in the
-	// serving loop. You must call Wait after calling Serve or ListenAndServe.
-	Wait() error
-
-	// Stop stops the listener. It will block until all connections have been
-	// closed.
-	Stop() error
-}
-
-// HTTP defines the configuration for serving a http.Server. Multiple calls to
-// Serve or ListenAndServe can be made on the same HTTP instance. The default
-// timeouts of 1 minute each result in a maximum of 2 minutes before a Stop()
-// returns.
-type HTTP struct {
-	// StopTimeout is the duration before we begin force closing connections.
-	// Defaults to 1 minute.
-	StopTimeout time.Duration
-
-	// KillTimeout is the duration before which we completely give up and abort
-	// even though we still have connected clients. This is useful when a large
-	// number of client connections exist and closing them can take a long time.
-	// Note, this is in addition to the StopTimeout. Defaults to 1 minute.
-	KillTimeout time.Duration
-
-	// Stats is optional. If provided, it will be used to record various metrics.
-	Stats stats.Client
-
-	// Clock allows for testing timing related functionality. Do not specify this
-	// in production code.
-	Clock clock.Clock
-}
-
-// Serve provides the low-level API which is useful if you're creating your own
-// net.Listener.
-func (h HTTP) Serve(s *http.Server, l net.Listener) Server {
-	stopTimeout := h.StopTimeout
-	if stopTimeout == 0 {
-		stopTimeout = defaultStopTimeout
-	}
-	killTimeout := h.KillTimeout
-	if killTimeout == 0 {
-		killTimeout = defaultKillTimeout
-	}
-	klock := h.Clock
-	if klock == nil {
-		klock = clock.New()
-	}
-
-	ss := &server{
-		stopTimeout:  stopTimeout,
-		killTimeout:  killTimeout,
-		stats:        h.Stats,
-		clock:        klock,
-		oldConnState: s.ConnState,
-		listener:     l,
-		server:       s,
-		serveDone:    make(chan struct{}),
-		serveErr:     make(chan error, 1),
-		new:          make(chan net.Conn),
-		active:       make(chan net.Conn),
-		idle:         make(chan net.Conn),
-		closed:       make(chan net.Conn),
-		stop:         make(chan chan struct{}),
-		kill:         make(chan chan struct{}),
-	}
-	s.ConnState = ss.connState
-	go ss.manage()
-	go ss.serve()
-	return ss
-}
-
-// ListenAndServe returns a Server for the given http.Server. It is equivalent
-// to ListenAndServe from the standard library, but returns immediately.
-// Requests will be accepted in a background goroutine. If the http.Server has
-// a non-nil TLSConfig, a TLS enabled listener will be setup.
-func (h HTTP) ListenAndServe(s *http.Server) (Server, error) {
-	addr := s.Addr
-	if addr == "" {
-		if s.TLSConfig == nil {
-			addr = ":http"
-		} else {
-			addr = ":https"
-		}
-	}
-	l, err := net.Listen("tcp", addr)
-	if err != nil {
-		stats.BumpSum(h.Stats, "listen.error", 1)
-		return nil, err
-	}
-	if s.TLSConfig != nil {
-		l = tls.NewListener(l, s.TLSConfig)
-	}
-	return h.Serve(s, l), nil
-}
-
-// server manages the serving process and allows for gracefully stopping it.
-type server struct {
-	stopTimeout time.Duration
-	killTimeout time.Duration
-	stats       stats.Client
-	clock       clock.Clock
-
-	oldConnState func(net.Conn, http.ConnState)
-	server       *http.Server
-	serveDone    chan struct{}
-	serveErr     chan error
-	listener     net.Listener
-
-	new    chan net.Conn
-	active chan net.Conn
-	idle   chan net.Conn
-	closed chan net.Conn
-	stop   chan chan struct{}
-	kill   chan chan struct{}
-
-	stopOnce sync.Once
-	stopErr  error
-}
-
-func (s *server) connState(c net.Conn, cs http.ConnState) {
-	if s.oldConnState != nil {
-		s.oldConnState(c, cs)
-	}
-
-	switch cs {
-	case http.StateNew:
-		s.new <- c
-	case http.StateActive:
-		s.active <- c
-	case http.StateIdle:
-		s.idle <- c
-	case http.StateHijacked, http.StateClosed:
-		s.closed <- c
-	}
-}
-
-func (s *server) manage() {
-	defer func() {
-		close(s.new)
-		close(s.active)
-		close(s.idle)
-		close(s.closed)
-		close(s.stop)
-		close(s.kill)
-	}()
-
-	var stopDone chan struct{}
-
-	conns := map[net.Conn]http.ConnState{}
-	var countNew, countActive, countIdle float64
-
-	// decConn decrements the count associated with the current state of the
-	// given connection.
-	decConn := func(c net.Conn) {
-		switch conns[c] {
-		default:
-			panic(fmt.Errorf("unknown existing connection: %s", c))
-		case http.StateNew:
-			countNew--
-		case http.StateActive:
-			countActive--
-		case http.StateIdle:
-			countIdle--
-		}
-	}
-
-	// setup a ticker to report various values every minute. if we don't have a
-	// Stats implementation provided, we Stop it so it never ticks.
-	statsTicker := s.clock.Ticker(time.Minute)
-	if s.stats == nil {
-		statsTicker.Stop()
-	}
-
-	for {
-		select {
-		case <-statsTicker.C:
-			// we'll only get here when s.stats is not nil
-			s.stats.BumpAvg("http-state.new", countNew)
-			s.stats.BumpAvg("http-state.active", countActive)
-			s.stats.BumpAvg("http-state.idle", countIdle)
-			s.stats.BumpAvg("http-state.total", countNew+countActive+countIdle)
-		case c := <-s.new:
-			conns[c] = http.StateNew
-			countNew++
-		case c := <-s.active:
-			decConn(c)
-			countActive++
-
-			conns[c] = http.StateActive
-		case c := <-s.idle:
-			decConn(c)
-			countIdle++
-
-			conns[c] = http.StateIdle
-
-			// if we're already stopping, close it
-			if stopDone != nil {
-				c.Close()
-			}
-		case c := <-s.closed:
-			stats.BumpSum(s.stats, "conn.closed", 1)
-			decConn(c)
-			delete(conns, c)
-
-			// if we're waiting to stop and are all empty, we just closed the last
-			// connection and we're done.
-			if stopDone != nil && len(conns) == 0 {
-				close(stopDone)
-				return
-			}
-		case stopDone = <-s.stop:
-			// if we're already all empty, we're already done
-			if len(conns) == 0 {
-				close(stopDone)
-				return
-			}
-
-			// close current idle connections right away
-			for c, cs := range conns {
-				if cs == http.StateIdle {
-					c.Close()
-				}
-			}
-
-			// continue the loop and wait for all the ConnState updates which will
-			// eventually close(stopDone) and return from this goroutine.
-
-		case killDone := <-s.kill:
-			// force close all connections
-			stats.BumpSum(s.stats, "kill.conn.count", float64(len(conns)))
-			for c := range conns {
-				c.Close()
-			}
-
-			// don't block the kill.
-			close(killDone)
-
-			// continue the loop and we wait for all the ConnState updates and will
-			// return from this goroutine when we're all done. otherwise we'll try to
-			// send those ConnState updates on closed channels.
-
-		}
-	}
-}
-
-func (s *server) serve() {
-	stats.BumpSum(s.stats, "serve", 1)
-	s.serveErr <- s.server.Serve(s.listener)
-	close(s.serveDone)
-	close(s.serveErr)
-}
-
-func (s *server) Wait() error {
-	if err := <-s.serveErr; !isUseOfClosedError(err) {
-		return err
-	}
-	return nil
-}
-
-func (s *server) Stop() error {
-	s.stopOnce.Do(func() {
-		defer stats.BumpTime(s.stats, "stop.time").End()
-		stats.BumpSum(s.stats, "stop", 1)
-
-		// first disable keep-alive for new connections
-		s.server.SetKeepAlivesEnabled(false)
-
-		// then close the listener so new connections can't connect come thru
-		closeErr := s.listener.Close()
-		<-s.serveDone
-
-		// then trigger the background goroutine to stop and wait for it
-		stopDone := make(chan struct{})
-		s.stop <- stopDone
-
-		// wait for stop
-		select {
-		case <-stopDone:
-		case <-s.clock.After(s.stopTimeout):
-			defer stats.BumpTime(s.stats, "kill.time").End()
-			stats.BumpSum(s.stats, "kill", 1)
-
-			// stop timed out, wait for kill
-			killDone := make(chan struct{})
-			s.kill <- killDone
-			select {
-			case <-killDone:
-			case <-s.clock.After(s.killTimeout):
-				// kill timed out, give up
-				stats.BumpSum(s.stats, "kill.timeout", 1)
-			}
-		}
-
-		if closeErr != nil && !isUseOfClosedError(closeErr) {
-			stats.BumpSum(s.stats, "listener.close.error", 1)
-			s.stopErr = closeErr
-		}
-	})
-	return s.stopErr
-}
-
-func isUseOfClosedError(err error) bool {
-	if err == nil {
-		return false
-	}
-	if opErr, ok := err.(*net.OpError); ok {
-		err = opErr.Err
-	}
-	return err.Error() == "use of closed network connection"
-}
-
-// ListenAndServe is a convenience function to serve and wait for a SIGTERM
-// or SIGINT before shutting down.
-func ListenAndServe(s *http.Server, hd *HTTP) error {
-	if hd == nil {
-		hd = &HTTP{}
-	}
-	hs, err := hd.ListenAndServe(s)
-	if err != nil {
-		return err
-	}
-
-	waiterr := make(chan error, 1)
-	go func() {
-		defer close(waiterr)
-		waiterr <- hs.Wait()
-	}()
-
-	signals := make(chan os.Signal, 10)
-	signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT)
-
-	select {
-	case err := <-waiterr:
-		if err != nil {
-			return err
-		}
-	case <-signals:
-		signal.Stop(signals)
-		if err := hs.Stop(); err != nil {
-			return err
-		}
-		if err := <-waiterr; err != nil {
-			return err
-		}
-	}
-	return nil
-}
diff --git a/vendor/github.com/facebookgo/httpdown/license b/vendor/github.com/facebookgo/httpdown/license
deleted file mode 100644
index d849082ffb..0000000000
--- a/vendor/github.com/facebookgo/httpdown/license
+++ /dev/null
@@ -1,30 +0,0 @@
-BSD License
-
-For httpdown software
-
-Copyright (c) 2015, Facebook, Inc. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice, this
-   list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright notice,
-   this list of conditions and the following disclaimer in the documentation
-   and/or other materials provided with the distribution.
-
- * Neither the name Facebook nor the names of its contributors may be used to
-   endorse or promote products derived from this software without specific
-   prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
-ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/facebookgo/httpdown/patents b/vendor/github.com/facebookgo/httpdown/patents
deleted file mode 100644
index f7133456a2..0000000000
--- a/vendor/github.com/facebookgo/httpdown/patents
+++ /dev/null
@@ -1,33 +0,0 @@
-Additional Grant of Patent Rights Version 2
-
-"Software" means the httpdown software distributed by Facebook, Inc.
-
-Facebook, Inc. ("Facebook") hereby grants to each recipient of the Software
-("you") a perpetual, worldwide, royalty-free, non-exclusive, irrevocable
-(subject to the termination provision below) license under any Necessary
-Claims, to make, have made, use, sell, offer to sell, import, and otherwise
-transfer the Software. For avoidance of doubt, no license is granted under
-Facebook’s rights in any patent claims that are infringed by (i) modifications
-to the Software made by you or any third party or (ii) the Software in
-combination with any software or other technology.
-
-The license granted hereunder will terminate, automatically and without notice,
-if you (or any of your subsidiaries, corporate affiliates or agents) initiate
-directly or indirectly, or take a direct financial interest in, any Patent
-Assertion: (i) against Facebook or any of its subsidiaries or corporate
-affiliates, (ii) against any party if such Patent Assertion arises in whole or
-in part from any software, technology, product or service of Facebook or any of
-its subsidiaries or corporate affiliates, or (iii) against any party relating
-to the Software. Notwithstanding the foregoing, if Facebook or any of its
-subsidiaries or corporate affiliates files a lawsuit alleging patent
-infringement against you in the first instance, and you respond by filing a
-patent infringement counterclaim in that lawsuit against that party that is
-unrelated to the Software, the license granted hereunder will not terminate
-under section (i) of this paragraph due to such counterclaim.
-
-A "Necessary Claim" is a claim of a patent owned by Facebook that is
-necessarily infringed by the Software standing alone.
-
-A "Patent Assertion" is any lawsuit or other action alleging direct, indirect,
-or contributory infringement or inducement to infringe any patent, including a
-cross-claim or counterclaim.
diff --git a/vendor/github.com/facebookgo/httpdown/readme.md b/vendor/github.com/facebookgo/httpdown/readme.md
deleted file mode 100644
index d5fa245dbc..0000000000
--- a/vendor/github.com/facebookgo/httpdown/readme.md
+++ /dev/null
@@ -1,41 +0,0 @@
-httpdown [![Build Status](https://secure.travis-ci.org/facebookgo/httpdown.png)](https://travis-ci.org/facebookgo/httpdown)
-========
-
-Documentation: https://godoc.org/github.com/facebookgo/httpdown
-
-Package httpdown provides a library that makes it easy to build a HTTP server
-that can be shutdown gracefully (that is, without dropping any connections).
-
-If you want graceful restart and not just graceful shutdown, look at the
-[grace](https://github.com/facebookgo/grace) package which uses this package
-underneath but also provides graceful restart.
-
-Usage
------
-
-Demo HTTP Server with graceful termination:
-https://github.com/facebookgo/httpdown/blob/master/httpdown_example/main.go
-
-1. Install the demo application
-
-        go get github.com/facebookgo/httpdown/httpdown_example
-
-1. Start it in the first terminal
-
-        httpdown_example
-
-   This will output something like:
-
-        2014/11/18 21:57:50 serving on http://127.0.0.1:8080/ with pid 17
-
-1. In a second terminal start a slow HTTP request
-
-        curl 'http://localhost:8080/?duration=20s'
-
-1. In a third terminal trigger a graceful shutdown (using the pid from your output):
-
-        kill -TERM 17
-
-This will demonstrate that the slow request was served before the server was
-shutdown. You could also have used `Ctrl-C` instead of `kill` as the example
-application triggers graceful shutdown on TERM or INT signals.
diff --git a/vendor/github.com/facebookgo/stats/.travis.yml b/vendor/github.com/facebookgo/stats/.travis.yml
deleted file mode 100644
index 9c9f036ae3..0000000000
--- a/vendor/github.com/facebookgo/stats/.travis.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-language: go
-
-go:
-  - 1.5
-
-before_install:
-  - go get -v golang.org/x/tools/cmd/vet
-  - go get -v golang.org/x/tools/cmd/cover
-  - go get -v github.com/golang/lint/golint
-
-install:
-  - go install -race -v std
-  - go get -race -t -v ./...
-  - go install -race -v ./...
-
-script:
-  - go vet ./...
-  - $HOME/gopath/bin/golint .
-  - go test -cpu=2 -race -v ./...
-  - go test -cpu=2 -covermode=atomic ./...
diff --git a/vendor/github.com/facebookgo/stats/aggregation.go b/vendor/github.com/facebookgo/stats/aggregation.go
deleted file mode 100644
index 8f57fb7f1b..0000000000
--- a/vendor/github.com/facebookgo/stats/aggregation.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package stats
-
-import "sort"
-
-// Average returns the average value
-func Average(values []float64) float64 {
-	if len(values) == 0 {
-		return 0
-	}
-
-	var val float64
-	for _, point := range values {
-		val += point
-	}
-	return val / float64(len(values))
-}
-
-// Sum returns the sum of all the given values
-func Sum(values []float64) float64 {
-	var val float64
-	for _, point := range values {
-		val += point
-	}
-	return val
-}
-
-// Percentiles returns a map containing the asked for percentiles
-func Percentiles(values []float64, percentiles map[string]float64) map[string]float64 {
-	sort.Float64s(values)
-	results := map[string]float64{}
-	for label, p := range percentiles {
-		results[label] = values[int(float64(len(values))*p)]
-	}
-	return results
-}
diff --git a/vendor/github.com/facebookgo/stats/counter.go b/vendor/github.com/facebookgo/stats/counter.go
deleted file mode 100644
index 59a0ed1e7d..0000000000
--- a/vendor/github.com/facebookgo/stats/counter.go
+++ /dev/null
@@ -1,112 +0,0 @@
-package stats
-
-import "fmt"
-
-// Type is the type of aggregation of apply
-type Type int
-
-const (
-	AggregateAvg Type = iota
-	AggregateSum
-	AggregateHistogram
-)
-
-var (
-	// HistogramPercentiles is used to determine which percentiles to return for
-	// SimpleCounter.Aggregate
-	HistogramPercentiles = map[string]float64{
-		"p50": 0.5,
-		"p95": 0.95,
-		"p99": 0.99,
-	}
-
-	// MinSamplesForPercentiles is used by SimpleCounter.Aggregate to determine
-	// what the minimum number of samples is required for percentile analysis
-	MinSamplesForPercentiles = 10
-)
-
-// Aggregates can be used to merge counters together. This is not goroutine safe
-type Aggregates map[string]Counter
-
-// Add adds the counter for aggregation. This is not goroutine safe
-func (a Aggregates) Add(c Counter) error {
-	key := c.FullKey()
-	if counter, ok := a[key]; ok {
-		if counter.GetType() != c.GetType() {
-			return fmt.Errorf("stats: mismatched aggregation type for: %s", key)
-		}
-		counter.AddValues(c.GetValues()...)
-	} else {
-		a[key] = c
-	}
-	return nil
-}
-
-// Counter is the interface used by Aggregates to merge counters together
-type Counter interface {
-	// FullKey is used to uniquely identify the counter
-	FullKey() string
-
-	// AddValues adds values for aggregation
-	AddValues(...float64)
-
-	// GetValues returns the values for aggregation
-	GetValues() []float64
-
-	// GetType returns the type of aggregation to apply
-	GetType() Type
-}
-
-// SimpleCounter is a basic implementation of the Counter interface
-type SimpleCounter struct {
-	Key    string
-	Values []float64
-	Type   Type
-}
-
-// FullKey is part of the Counter interace
-func (s *SimpleCounter) FullKey() string {
-	return s.Key
-}
-
-// GetValues is part of the Counter interface
-func (s *SimpleCounter) GetValues() []float64 {
-	return s.Values
-}
-
-// AddValues is part of the Counter interface
-func (s *SimpleCounter) AddValues(vs ...float64) {
-	s.Values = append(s.Values, vs...)
-}
-
-// GetType is part of the Counter interface
-func (s *SimpleCounter) GetType() Type {
-	return s.Type
-}
-
-// Aggregate aggregates the provided values appropriately, returning a map
-// from key to value. If AggregateHistogram is specified, the map will contain
-// the relevant percentiles as specified by HistogramPercentiles
-func (s *SimpleCounter) Aggregate() map[string]float64 {
-	switch s.Type {
-	case AggregateAvg:
-		return map[string]float64{
-			s.Key: Average(s.Values),
-		}
-	case AggregateSum:
-		return map[string]float64{
-			s.Key: Sum(s.Values),
-		}
-	case AggregateHistogram:
-		histogram := map[string]float64{
-			s.Key: Average(s.Values),
-		}
-		if len(s.Values) > MinSamplesForPercentiles {
-			for k, v := range Percentiles(s.Values, HistogramPercentiles) {
-				histogram[fmt.Sprintf("%s.%s", s.Key, k)] = v
-			}
-		}
-		return histogram
-	}
-	panic("stats: unsupported aggregation type")
-}
diff --git a/vendor/github.com/facebookgo/stats/license b/vendor/github.com/facebookgo/stats/license
deleted file mode 100644
index feae870756..0000000000
--- a/vendor/github.com/facebookgo/stats/license
+++ /dev/null
@@ -1,30 +0,0 @@
-BSD License
-
-For stats software
-
-Copyright (c) 2015, Facebook, Inc. All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification,
-are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice, this
-   list of conditions and the following disclaimer.
-
- * Redistributions in binary form must reproduce the above copyright notice,
-   this list of conditions and the following disclaimer in the documentation
-   and/or other materials provided with the distribution.
-
- * Neither the name Facebook nor the names of its contributors may be used to
-   endorse or promote products derived from this software without specific
-   prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
-ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
-ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/github.com/facebookgo/stats/patents b/vendor/github.com/facebookgo/stats/patents
deleted file mode 100644
index 5d76172129..0000000000
--- a/vendor/github.com/facebookgo/stats/patents
+++ /dev/null
@@ -1,33 +0,0 @@
-Additional Grant of Patent Rights Version 2
-
-"Software" means the stats software distributed by Facebook, Inc.
-
-Facebook, Inc. ("Facebook") hereby grants to each recipient of the Software
-("you") a perpetual, worldwide, royalty-free, non-exclusive, irrevocable
-(subject to the termination provision below) license under any Necessary
-Claims, to make, have made, use, sell, offer to sell, import, and otherwise
-transfer the Software. For avoidance of doubt, no license is granted under
-Facebook’s rights in any patent claims that are infringed by (i) modifications
-to the Software made by you or any third party or (ii) the Software in
-combination with any software or other technology.
-
-The license granted hereunder will terminate, automatically and without notice,
-if you (or any of your subsidiaries, corporate affiliates or agents) initiate
-directly or indirectly, or take a direct financial interest in, any Patent
-Assertion: (i) against Facebook or any of its subsidiaries or corporate
-affiliates, (ii) against any party if such Patent Assertion arises in whole or
-in part from any software, technology, product or service of Facebook or any of
-its subsidiaries or corporate affiliates, or (iii) against any party relating
-to the Software. Notwithstanding the foregoing, if Facebook or any of its
-subsidiaries or corporate affiliates files a lawsuit alleging patent
-infringement against you in the first instance, and you respond by filing a
-patent infringement counterclaim in that lawsuit against that party that is
-unrelated to the Software, the license granted hereunder will not terminate
-under section (i) of this paragraph due to such counterclaim.
-
-A "Necessary Claim" is a claim of a patent owned by Facebook that is
-necessarily infringed by the Software standing alone.
-
-A "Patent Assertion" is any lawsuit or other action alleging direct, indirect,
-or contributory infringement or inducement to infringe any patent, including a
-cross-claim or counterclaim.
diff --git a/vendor/github.com/facebookgo/stats/readme.md b/vendor/github.com/facebookgo/stats/readme.md
deleted file mode 100644
index f268ed307b..0000000000
--- a/vendor/github.com/facebookgo/stats/readme.md
+++ /dev/null
@@ -1,4 +0,0 @@
-stats [![Build Status](https://secure.travis-ci.org/facebookgo/stats.png)](https://travis-ci.org/facebookgo/stats)
-=====
-
-Documentation: https://godoc.org/github.com/facebookgo/stats
diff --git a/vendor/github.com/facebookgo/stats/stats.go b/vendor/github.com/facebookgo/stats/stats.go
deleted file mode 100644
index b833506aa2..0000000000
--- a/vendor/github.com/facebookgo/stats/stats.go
+++ /dev/null
@@ -1,166 +0,0 @@
-// Package stats defines a lightweight interface for collecting statistics. It
-// doesn't provide an implementation, just the shared interface.
-package stats
-
-// Client provides methods to collection statistics.
-type Client interface {
-	// BumpAvg bumps the average for the given key.
-	BumpAvg(key string, val float64)
-
-	// BumpSum bumps the sum for the given key.
-	BumpSum(key string, val float64)
-
-	// BumpHistogram bumps the histogram for the given key.
-	BumpHistogram(key string, val float64)
-
-	// BumpTime is a special version of BumpHistogram which is specialized for
-	// timers. Calling it starts the timer, and it returns a value on which End()
-	// can be called to indicate finishing the timer. A convenient way of
-	// recording the duration of a function is calling it like such at the top of
-	// the function:
-	//
-	//     defer s.BumpTime("my.function").End()
-	BumpTime(key string) interface {
-		End()
-	}
-}
-
-// PrefixClient adds multiple keys for the same value, with each prefix
-// added to the key and calls the underlying client.
-func PrefixClient(prefixes []string, client Client) Client {
-	return &prefixClient{
-		Prefixes: prefixes,
-		Client:   client,
-	}
-}
-
-type prefixClient struct {
-	Prefixes []string
-	Client   Client
-}
-
-func (p *prefixClient) BumpAvg(key string, val float64) {
-	for _, prefix := range p.Prefixes {
-		p.Client.BumpAvg(prefix+key, val)
-	}
-}
-
-func (p *prefixClient) BumpSum(key string, val float64) {
-	for _, prefix := range p.Prefixes {
-		p.Client.BumpSum(prefix+key, val)
-	}
-}
-
-func (p *prefixClient) BumpHistogram(key string, val float64) {
-	for _, prefix := range p.Prefixes {
-		p.Client.BumpHistogram(prefix+key, val)
-	}
-}
-
-func (p *prefixClient) BumpTime(key string) interface {
-	End()
-} {
-	var m multiEnder
-	for _, prefix := range p.Prefixes {
-		m = append(m, p.Client.BumpTime(prefix+key))
-	}
-	return m
-}
-
-// multiEnder combines many enders together.
-type multiEnder []interface {
-	End()
-}
-
-func (m multiEnder) End() {
-	for _, e := range m {
-		e.End()
-	}
-}
-
-// HookClient is useful for testing. It provides optional hooks for each
-// expected method in the interface, which if provided will be called. If a
-// hook is not provided, it will be ignored.
-type HookClient struct {
-	BumpAvgHook       func(key string, val float64)
-	BumpSumHook       func(key string, val float64)
-	BumpHistogramHook func(key string, val float64)
-	BumpTimeHook      func(key string) interface {
-		End()
-	}
-}
-
-// BumpAvg will call BumpAvgHook if defined.
-func (c *HookClient) BumpAvg(key string, val float64) {
-	if c.BumpAvgHook != nil {
-		c.BumpAvgHook(key, val)
-	}
-}
-
-// BumpSum will call BumpSumHook if defined.
-func (c *HookClient) BumpSum(key string, val float64) {
-	if c.BumpSumHook != nil {
-		c.BumpSumHook(key, val)
-	}
-}
-
-// BumpHistogram will call BumpHistogramHook if defined.
-func (c *HookClient) BumpHistogram(key string, val float64) {
-	if c.BumpHistogramHook != nil {
-		c.BumpHistogramHook(key, val)
-	}
-}
-
-// BumpTime will call BumpTimeHook if defined.
-func (c *HookClient) BumpTime(key string) interface {
-	End()
-} {
-	if c.BumpTimeHook != nil {
-		return c.BumpTimeHook(key)
-	}
-	return NoOpEnd
-}
-
-type noOpEnd struct{}
-
-func (n noOpEnd) End() {}
-
-// NoOpEnd provides a dummy value for use in tests as valid return value for
-// BumpTime().
-var NoOpEnd = noOpEnd{}
-
-// BumpAvg calls BumpAvg on the Client if it isn't nil. This is useful when a
-// component has an optional stats.Client.
-func BumpAvg(c Client, key string, val float64) {
-	if c != nil {
-		c.BumpAvg(key, val)
-	}
-}
-
-// BumpSum calls BumpSum on the Client if it isn't nil. This is useful when a
-// component has an optional stats.Client.
-func BumpSum(c Client, key string, val float64) {
-	if c != nil {
-		c.BumpSum(key, val)
-	}
-}
-
-// BumpHistogram calls BumpHistogram on the Client if it isn't nil. This is
-// useful when a component has an optional stats.Client.
-func BumpHistogram(c Client, key string, val float64) {
-	if c != nil {
-		c.BumpHistogram(key, val)
-	}
-}
-
-// BumpTime calls BumpTime on the Client if it isn't nil. If the Client is nil
-// it still returns a valid return value which will be a no-op. This is useful
-// when a component has an optional stats.Client.
-func BumpTime(c Client, key string) interface {
-	End()
-} {
-	if c != nil {
-		return c.BumpTime(key)
-	}
-	return NoOpEnd
-}
diff --git a/vendor/github.com/facebookgo/stats/stopper.go b/vendor/github.com/facebookgo/stats/stopper.go
deleted file mode 100644
index 38e8eab80a..0000000000
--- a/vendor/github.com/facebookgo/stats/stopper.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package stats
-
-import "time"
-
-// Stopper calls Client.BumpSum and Client.BumpHistogram when End'ed
-type Stopper struct {
-	Key    string
-	Start  time.Time
-	Client Client
-}
-
-// End the Stopper
-func (s *Stopper) End() {
-	since := time.Since(s.Start).Seconds() * 1000.0
-	s.Client.BumpSum(s.Key+".total", since)
-	s.Client.BumpHistogram(s.Key, since)
-}
diff --git a/vendor/github.com/go-xorm/xorm/go.mod b/vendor/github.com/go-xorm/xorm/go.mod
deleted file mode 100644
index 1ab39831a8..0000000000
--- a/vendor/github.com/go-xorm/xorm/go.mod
+++ /dev/null
@@ -1,20 +0,0 @@
-module github.com/go-xorm/xorm
-
-go 1.11
-
-require (
-	github.com/cockroachdb/apd v1.1.0 // indirect
-	github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4
-	github.com/go-sql-driver/mysql v1.4.1
-	github.com/gofrs/uuid v3.2.0+incompatible // indirect
-	github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 // indirect
-	github.com/jackc/pgx v3.6.0+incompatible
-	github.com/lib/pq v1.0.0
-	github.com/mattn/go-sqlite3 v1.10.0
-	github.com/pkg/errors v0.8.1 // indirect
-	github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 // indirect
-	github.com/stretchr/testify v1.4.0
-	github.com/ziutek/mymysql v1.5.4
-	xorm.io/builder v0.3.6
-	xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb
-)
diff --git a/vendor/github.com/lafriks/xormstore/go.mod b/vendor/github.com/lafriks/xormstore/go.mod
index 1a68ce6513..8a7528ee78 100644
--- a/vendor/github.com/lafriks/xormstore/go.mod
+++ b/vendor/github.com/lafriks/xormstore/go.mod
@@ -5,15 +5,14 @@ go 1.11
 require (
 	github.com/denisenkom/go-mssqldb v0.0.0-20190924004331-208c0a498538
 	github.com/go-sql-driver/mysql v1.4.1
-	github.com/go-xorm/xorm v0.7.9
 	github.com/gorilla/context v1.1.1
 	github.com/gorilla/securecookie v1.1.1
 	github.com/gorilla/sessions v1.2.0
-	github.com/kr/pretty v0.1.0 // indirect
 	github.com/lib/pq v1.2.0
 	github.com/mattn/go-sqlite3 v1.11.0
 	golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad // indirect
 	google.golang.org/appengine v1.6.4 // indirect
 	gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
 	xorm.io/core v0.7.2
+	xorm.io/xorm v0.8.0
 )
diff --git a/vendor/github.com/lafriks/xormstore/go.sum b/vendor/github.com/lafriks/xormstore/go.sum
index d3dc6aee62..9d0273a87c 100644
--- a/vendor/github.com/lafriks/xormstore/go.sum
+++ b/vendor/github.com/lafriks/xormstore/go.sum
@@ -1,6 +1,5 @@
 cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
-cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU=
 cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw=
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
@@ -10,8 +9,6 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
 github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
-github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -29,9 +26,6 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y=
 github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM=
-github.com/go-xorm/xorm v0.7.9 h1:LZze6n1UvRmM5gpL9/U9Gucwqo6aWlFVlfcHKH10qA0=
-github.com/go-xorm/xorm v0.7.9/go.mod h1:XiVxrMMIhFkwSkh96BW7PACl7UhLtx2iJIHMdmjh5sQ=
-github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
@@ -43,7 +37,6 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
 github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@@ -57,9 +50,6 @@ github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYb
 github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc=
-github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
-github.com/jackc/pgx v3.6.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
 github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
@@ -70,11 +60,9 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
 github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
 github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
-github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
 github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
 github.com/mattn/go-sqlite3 v1.11.0 h1:LDdKkqtYlom37fkvqs8rMPFKAMe8+SgjbwZ6ex1/A/Q=
 github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
@@ -86,8 +74,6 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
 github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
 github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
-github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@@ -98,13 +84,10 @@ github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
-github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE=
-github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -113,7 +96,6 @@ github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wK
 go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
 golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad h1:5E5raQxcv+6CZ11RrBYQe5WRbUIWpScjh0kvHZkZIrQ=
@@ -149,7 +131,6 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -160,7 +141,6 @@ golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3
 golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
-google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.6.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 google.golang.org/appengine v1.6.4 h1:WiKh4+/eMB2HaY7QhCfW/R7MuRAoA8QMCSJA6jP5/fo=
@@ -172,7 +152,6 @@ google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3
 google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -186,7 +165,7 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh
 honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8=
 xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=
-xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb h1:msX3zG3BPl8Ti+LDzP33/9K7BzO/WqFXk610K1kYKfo=
-xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
 xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw=
 xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
+xorm.io/xorm v0.8.0 h1:iALxgJrX8O00f8Jk22GbZwPmxJNgssV5Mv4uc2HL9PM=
+xorm.io/xorm v0.8.0/go.mod h1:ZkJLEYLoVyg7amJK/5r779bHyzs2AU8f8VMiP6BM7uY=
diff --git a/vendor/github.com/lafriks/xormstore/xormstore.go b/vendor/github.com/lafriks/xormstore/xormstore.go
index 85be25225e..e095508243 100644
--- a/vendor/github.com/lafriks/xormstore/xormstore.go
+++ b/vendor/github.com/lafriks/xormstore/xormstore.go
@@ -44,7 +44,7 @@ import (
 
 	"github.com/lafriks/xormstore/util"
 
-	"github.com/go-xorm/xorm"
+	"xorm.io/xorm"
 	"github.com/gorilla/context"
 	"github.com/gorilla/securecookie"
 	"github.com/gorilla/sessions"
diff --git a/vendor/gopkg.in/editorconfig/editorconfig-core-go.v1/.gitattributes b/vendor/gopkg.in/editorconfig/editorconfig-core-go.v1/.gitattributes
deleted file mode 100644
index d7f0d87596..0000000000
--- a/vendor/gopkg.in/editorconfig/editorconfig-core-go.v1/.gitattributes
+++ /dev/null
@@ -1,5 +0,0 @@
-* text eol=lf
-*.jpg binary
-*.jpeg binary
-*.png binary
-*.ico binary
diff --git a/vendor/gopkg.in/editorconfig/editorconfig-core-go.v1/.travis.yml b/vendor/gopkg.in/editorconfig/editorconfig-core-go.v1/.travis.yml
deleted file mode 100644
index a3183d6a78..0000000000
--- a/vendor/gopkg.in/editorconfig/editorconfig-core-go.v1/.travis.yml
+++ /dev/null
@@ -1,14 +0,0 @@
----
-language: go
-sudo: false
-go:
-  - '1.8'
-  - '1.9'
-  - '1.10'
-go_import_path: gopkg.in/editorconfig/editorconfig-core-go.v1
-
-install:
-  - make installdeps
-
-script:
-  - make test
diff --git a/vendor/gopkg.in/editorconfig/editorconfig-core-go.v1/Makefile b/vendor/gopkg.in/editorconfig/editorconfig-core-go.v1/Makefile
deleted file mode 100644
index 5151ed818d..0000000000
--- a/vendor/gopkg.in/editorconfig/editorconfig-core-go.v1/Makefile
+++ /dev/null
@@ -1,25 +0,0 @@
-PROJECT_ROOT_DIR := $(CURDIR)
-SRC := editorconfig.go cmd/editorconfig/main.go
-
-.PHONY: bin test test-go test-core submodule installdeps
-
-test: test-go test-core
-
-submodule:
-	git submodule update --init
-
-installdeps:
-	go get -t ./...
-
-editorconfig: $(SRC)
-	go build ./cmd/editorconfig
-
-test-go:
-	go test -v
-
-test-core: editorconfig
-	cd $(PROJECT_ROOT_DIR)/core-test && \
-		cmake -DEDITORCONFIG_CMD="$(PROJECT_ROOT_DIR)/editorconfig" .
-# Temporarily disable core-test
-	# cd $(PROJECT_ROOT_DIR)/core-test && \
-	# 	ctest --output-on-failure .
diff --git a/vendor/gopkg.in/ini.v1/README.md b/vendor/gopkg.in/ini.v1/README.md
index 036c56d63b..44e1fcddd8 100644
--- a/vendor/gopkg.in/ini.v1/README.md
+++ b/vendor/gopkg.in/ini.v1/README.md
@@ -28,22 +28,6 @@ $ go get gopkg.in/ini.v1
 
 Please add `-u` flag to update in the future.
 
-## Go Modules
-
-For historical reason, people use two different import paths for this package: `github.com/go-ini/ini` and `gopkg.in/ini.v1`. If you get error similar to the following one:
-
-```
-go: finding github.com/go-ini/ini v0.0.0-00010101000000-000000000000
-go: github.com/go-ini/ini@v0.0.0-00010101000000-000000000000: unknown revision 000000000000
-go: error loading module requirements
-```
-
-It is because one of your dependencies is using deprecated import path `github.com/go-ini/ini`, you can make a quick fix by adding the following line to your `go.mod` file (`v.1.44.0` was the latest version tagged on `master` branch):
-
-```
-replace github.com/go-ini/ini => gopkg.in/ini.v1 v1.44.0
-```
-
 ## Getting Help
 
 - [Getting Started](https://ini.unknwon.io/docs/intro/getting_started)
diff --git a/vendor/gopkg.in/ini.v1/data_source.go b/vendor/gopkg.in/ini.v1/data_source.go
new file mode 100644
index 0000000000..dc0277ec64
--- /dev/null
+++ b/vendor/gopkg.in/ini.v1/data_source.go
@@ -0,0 +1,74 @@
+// Copyright 2019 Unknwon
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package ini
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+)
+
+var (
+	_ dataSource = (*sourceFile)(nil)
+	_ dataSource = (*sourceData)(nil)
+	_ dataSource = (*sourceReadCloser)(nil)
+)
+
+// dataSource is an interface that returns object which can be read and closed.
+type dataSource interface {
+	ReadCloser() (io.ReadCloser, error)
+}
+
+// sourceFile represents an object that contains content on the local file system.
+type sourceFile struct {
+	name string
+}
+
+func (s sourceFile) ReadCloser() (_ io.ReadCloser, err error) {
+	return os.Open(s.name)
+}
+
+// sourceData represents an object that contains content in memory.
+type sourceData struct {
+	data []byte
+}
+
+func (s *sourceData) ReadCloser() (io.ReadCloser, error) {
+	return ioutil.NopCloser(bytes.NewReader(s.data)), nil
+}
+
+// sourceReadCloser represents an input stream with Close method.
+type sourceReadCloser struct {
+	reader io.ReadCloser
+}
+
+func (s *sourceReadCloser) ReadCloser() (io.ReadCloser, error) {
+	return s.reader, nil
+}
+
+func parseDataSource(source interface{}) (dataSource, error) {
+	switch s := source.(type) {
+	case string:
+		return sourceFile{s}, nil
+	case []byte:
+		return &sourceData{s}, nil
+	case io.ReadCloser:
+		return &sourceReadCloser{s}, nil
+	default:
+		return nil, fmt.Errorf("error parsing data source: unknown type %q", s)
+	}
+}
diff --git a/vendor/gopkg.in/ini.v1/deprecated.go b/vendor/gopkg.in/ini.v1/deprecated.go
new file mode 100644
index 0000000000..e8bda06e6f
--- /dev/null
+++ b/vendor/gopkg.in/ini.v1/deprecated.go
@@ -0,0 +1,25 @@
+// Copyright 2019 Unknwon
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package ini
+
+const (
+	// Deprecated: Use "DefaultSection" instead.
+	DEFAULT_SECTION = DefaultSection
+)
+
+var (
+	// Deprecated: AllCapsUnderscore converts to format ALL_CAPS_UNDERSCORE.
+	AllCapsUnderscore = SnackCase
+)
diff --git a/vendor/gopkg.in/ini.v1/file.go b/vendor/gopkg.in/ini.v1/file.go
index b38aadd1f8..017b77c8be 100644
--- a/vendor/gopkg.in/ini.v1/file.go
+++ b/vendor/gopkg.in/ini.v1/file.go
@@ -302,7 +302,7 @@ func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
 		}
 		alignSpaces := bytes.Repeat([]byte(" "), alignLength)
 
-	KEY_LIST:
+	KeyList:
 		for _, kname := range sec.keyList {
 			key := sec.Key(kname)
 			if len(key.Comment) > 0 {
@@ -347,7 +347,7 @@ func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) {
 					if kname != sec.keyList[len(sec.keyList)-1] {
 						buf.WriteString(LineBreak)
 					}
-					continue KEY_LIST
+					continue KeyList
 				}
 
 				// Write out alignment spaces before "=" sign
diff --git a/vendor/gopkg.in/ini.v1/helper.go b/vendor/gopkg.in/ini.v1/helper.go
new file mode 100644
index 0000000000..f9d80a682a
--- /dev/null
+++ b/vendor/gopkg.in/ini.v1/helper.go
@@ -0,0 +1,24 @@
+// Copyright 2019 Unknwon
+//
+// Licensed under the Apache License, Version 2.0 (the "License"): you may
+// not use this file except in compliance with the License. You may obtain
+// a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+// License for the specific language governing permissions and limitations
+// under the License.
+
+package ini
+
+func inSlice(str string, s []string) bool {
+	for _, v := range s {
+		if str == v {
+			return true
+		}
+	}
+	return false
+}
diff --git a/vendor/gopkg.in/ini.v1/ini.go b/vendor/gopkg.in/ini.v1/ini.go
index 36c072cdf3..9de6171c9b 100644
--- a/vendor/gopkg.in/ini.v1/ini.go
+++ b/vendor/gopkg.in/ini.v1/ini.go
@@ -18,11 +18,6 @@
 package ini
 
 import (
-	"bytes"
-	"fmt"
-	"io"
-	"io/ioutil"
-	"os"
 	"regexp"
 	"runtime"
 )
@@ -31,12 +26,10 @@ const (
 	// DefaultSection is the name of default section. You can use this constant or the string literal.
 	// In most of cases, an empty string is all you need to access the section.
 	DefaultSection = "DEFAULT"
-	// Deprecated: Use "DefaultSection" instead.
-	DEFAULT_SECTION = DefaultSection
 
 	// Maximum allowed depth when recursively substituing variable names.
 	depthValues = 99
-	version     = "1.46.0"
+	version     = "1.48.0"
 )
 
 // Version returns current package version literal.
@@ -49,26 +42,23 @@ var (
 	// This variable will be changed to "\r\n" automatically on Windows at package init time.
 	LineBreak = "\n"
 
-	// DefaultFormatLeft places custom spaces on the left when PrettyFormat and PrettyEqual are both disabled.
-	DefaultFormatLeft = ""
-	// DefaultFormatRight places custom spaces on the right when PrettyFormat and PrettyEqual are both disabled.
-	DefaultFormatRight = ""
-
 	// Variable regexp pattern: %(variable)s
-	varPattern = regexp.MustCompile(`%\(([^\)]+)\)s`)
-
-	// PrettyFormat indicates whether to align "=" sign with spaces to produce pretty output
-	// or reduce all possible spaces for compact format.
-	PrettyFormat = true
-
-	// PrettyEqual places spaces around "=" sign even when PrettyFormat is false.
-	PrettyEqual = false
+	varPattern = regexp.MustCompile(`%\(([^)]+)\)s`)
 
 	// DefaultHeader explicitly writes default section header.
 	DefaultHeader = false
 
 	// PrettySection indicates whether to put a line between sections.
 	PrettySection = true
+	// PrettyFormat indicates whether to align "=" sign with spaces to produce pretty output
+	// or reduce all possible spaces for compact format.
+	PrettyFormat = true
+	// PrettyEqual places spaces around "=" sign even when PrettyFormat is false.
+	PrettyEqual = false
+	// DefaultFormatLeft places custom spaces on the left when PrettyFormat and PrettyEqual are both disabled.
+	DefaultFormatLeft = ""
+	// DefaultFormatRight places custom spaces on the right when PrettyFormat and PrettyEqual are both disabled.
+	DefaultFormatRight = ""
 )
 
 func init() {
@@ -77,60 +67,6 @@ func init() {
 	}
 }
 
-func inSlice(str string, s []string) bool {
-	for _, v := range s {
-		if str == v {
-			return true
-		}
-	}
-	return false
-}
-
-// dataSource is an interface that returns object which can be read and closed.
-type dataSource interface {
-	ReadCloser() (io.ReadCloser, error)
-}
-
-// sourceFile represents an object that contains content on the local file system.
-type sourceFile struct {
-	name string
-}
-
-func (s sourceFile) ReadCloser() (_ io.ReadCloser, err error) {
-	return os.Open(s.name)
-}
-
-// sourceData represents an object that contains content in memory.
-type sourceData struct {
-	data []byte
-}
-
-func (s *sourceData) ReadCloser() (io.ReadCloser, error) {
-	return ioutil.NopCloser(bytes.NewReader(s.data)), nil
-}
-
-// sourceReadCloser represents an input stream with Close method.
-type sourceReadCloser struct {
-	reader io.ReadCloser
-}
-
-func (s *sourceReadCloser) ReadCloser() (io.ReadCloser, error) {
-	return s.reader, nil
-}
-
-func parseDataSource(source interface{}) (dataSource, error) {
-	switch s := source.(type) {
-	case string:
-		return sourceFile{s}, nil
-	case []byte:
-		return &sourceData{s}, nil
-	case io.ReadCloser:
-		return &sourceReadCloser{s}, nil
-	default:
-		return nil, fmt.Errorf("error parsing data source: unknown type '%s'", s)
-	}
-}
-
 // LoadOptions contains all customized options used for load data source(s).
 type LoadOptions struct {
 	// Loose indicates whether the parser should ignore nonexistent files or return error.
diff --git a/vendor/gopkg.in/ini.v1/key.go b/vendor/gopkg.in/ini.v1/key.go
index 38860ff4bd..62f9146990 100644
--- a/vendor/gopkg.in/ini.v1/key.go
+++ b/vendor/gopkg.in/ini.v1/key.go
@@ -54,6 +54,16 @@ func (k *Key) addShadow(val string) error {
 		return errors.New("cannot add shadow to auto-increment or boolean key")
 	}
 
+	// Deduplicate shadows based on their values.
+	if k.value == val {
+		return nil
+	}
+	for i := range k.shadows {
+		if k.shadows[i].value == val {
+			return nil
+		}
+	}
+
 	shadow := newKey(k.s, k.name, val)
 	shadow.isShadow = true
 	k.shadows = append(k.shadows, shadow)
@@ -554,6 +564,12 @@ func (k *Key) Uint64s(delim string) []uint64 {
 	return vals
 }
 
+// Bools returns list of bool divided by given delimiter. Any invalid input will be treated as zero value.
+func (k *Key) Bools(delim string) []bool {
+	vals, _ := k.parseBools(k.Strings(delim), true, false)
+	return vals
+}
+
 // TimesFormat parses with given format and returns list of time.Time divided by given delimiter.
 // Any invalid input will be treated as zero value (0001-01-01 00:00:00 +0000 UTC).
 func (k *Key) TimesFormat(format, delim string) []time.Time {
@@ -602,6 +618,13 @@ func (k *Key) ValidUint64s(delim string) []uint64 {
 	return vals
 }
 
+// ValidBools returns list of bool divided by given delimiter. If some value is not 64-bit unsigned
+// integer, then it will not be included to result list.
+func (k *Key) ValidBools(delim string) []bool {
+	vals, _ := k.parseBools(k.Strings(delim), false, false)
+	return vals
+}
+
 // ValidTimesFormat parses with given format and returns list of time.Time divided by given delimiter.
 func (k *Key) ValidTimesFormat(format, delim string) []time.Time {
 	vals, _ := k.parseTimesFormat(format, k.Strings(delim), false, false)
@@ -638,6 +661,11 @@ func (k *Key) StrictUint64s(delim string) ([]uint64, error) {
 	return k.parseUint64s(k.Strings(delim), false, true)
 }
 
+// StrictBools returns list of bool divided by given delimiter or error on first invalid input.
+func (k *Key) StrictBools(delim string) ([]bool, error) {
+	return k.parseBools(k.Strings(delim), false, true)
+}
+
 // StrictTimesFormat parses with given format and returns list of time.Time divided by given delimiter
 // or error on first invalid input.
 func (k *Key) StrictTimesFormat(format, delim string) ([]time.Time, error) {
@@ -650,6 +678,21 @@ func (k *Key) StrictTimes(delim string) ([]time.Time, error) {
 	return k.StrictTimesFormat(time.RFC3339, delim)
 }
 
+// parseBools transforms strings to bools.
+func (k *Key) parseBools(strs []string, addInvalid, returnOnInvalid bool) ([]bool, error) {
+	vals := make([]bool, 0, len(strs))
+	for _, str := range strs {
+		val, err := parseBool(str)
+		if err != nil && returnOnInvalid {
+			return nil, err
+		}
+		if err == nil || addInvalid {
+			vals = append(vals, val)
+		}
+	}
+	return vals, nil
+}
+
 // parseFloat64s transforms strings to float64s.
 func (k *Key) parseFloat64s(strs []string, addInvalid, returnOnInvalid bool) ([]float64, error) {
 	vals := make([]float64, 0, len(strs))
diff --git a/vendor/gopkg.in/ini.v1/struct.go b/vendor/gopkg.in/ini.v1/struct.go
index c713f8296c..2764534f31 100644
--- a/vendor/gopkg.in/ini.v1/struct.go
+++ b/vendor/gopkg.in/ini.v1/struct.go
@@ -29,8 +29,8 @@ type NameMapper func(string) string
 
 // Built-in name getters.
 var (
-	// AllCapsUnderscore converts to format ALL_CAPS_UNDERSCORE.
-	AllCapsUnderscore NameMapper = func(raw string) string {
+	// SnackCase converts to format SNACK_CASE.
+	SnackCase NameMapper = func(raw string) string {
 		newstr := make([]rune, 0, len(raw))
 		for i, chr := range raw {
 			if isUpper := 'A' <= chr && chr <= 'Z'; isUpper {
@@ -50,7 +50,7 @@ var (
 				if i > 0 {
 					newstr = append(newstr, '_')
 				}
-				chr -= ('A' - 'a')
+				chr -= 'A' - 'a'
 			}
 			newstr = append(newstr, chr)
 		}
@@ -108,6 +108,8 @@ func setSliceWithProperType(key *Key, field reflect.Value, delim string, allowSh
 		vals, err = key.parseUint64s(strs, true, false)
 	case reflect.Float64:
 		vals, err = key.parseFloat64s(strs, true, false)
+	case reflect.Bool:
+		vals, err = key.parseBools(strs, true, false)
 	case reflectTime:
 		vals, err = key.parseTimesFormat(time.RFC3339, strs, true, false)
 	default:
@@ -132,6 +134,8 @@ func setSliceWithProperType(key *Key, field reflect.Value, delim string, allowSh
 			slice.Index(i).Set(reflect.ValueOf(vals.([]uint64)[i]))
 		case reflect.Float64:
 			slice.Index(i).Set(reflect.ValueOf(vals.([]float64)[i]))
+		case reflect.Bool:
+			slice.Index(i).Set(reflect.ValueOf(vals.([]bool)[i]))
 		case reflectTime:
 			slice.Index(i).Set(reflect.ValueOf(vals.([]time.Time)[i]))
 		}
@@ -380,6 +384,8 @@ func reflectSliceWithProperType(key *Key, field reflect.Value, delim string, all
 				val = fmt.Sprint(slice.Index(i).Uint())
 			case reflect.Float64:
 				val = fmt.Sprint(slice.Index(i).Float())
+			case reflect.Bool:
+				val = fmt.Sprint(slice.Index(i).Bool())
 			case reflectTime:
 				val = slice.Index(i).Interface().(time.Time).Format(time.RFC3339)
 			default:
@@ -407,6 +413,8 @@ func reflectSliceWithProperType(key *Key, field reflect.Value, delim string, all
 			buf.WriteString(fmt.Sprint(slice.Index(i).Uint()))
 		case reflect.Float64:
 			buf.WriteString(fmt.Sprint(slice.Index(i).Float()))
+		case reflect.Bool:
+			buf.WriteString(fmt.Sprint(slice.Index(i).Bool()))
 		case reflectTime:
 			buf.WriteString(slice.Index(i).Interface().(time.Time).Format(time.RFC3339))
 		default:
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 71ba274867..e24275236a 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -119,6 +119,8 @@ github.com/denisenkom/go-mssqldb/internal/decimal
 github.com/denisenkom/go-mssqldb/internal/querytext
 # github.com/dgrijalva/jwt-go v3.2.0+incompatible
 github.com/dgrijalva/jwt-go
+# github.com/editorconfig/editorconfig-core-go/v2 v2.1.1
+github.com/editorconfig/editorconfig-core-go/v2
 # github.com/edsrzf/mmap-go v1.0.0
 github.com/edsrzf/mmap-go
 # github.com/emirpasic/gods v1.12.0
@@ -132,15 +134,6 @@ github.com/emirpasic/gods/utils
 github.com/etcd-io/bbolt
 # github.com/ethantkoenig/rupture v0.0.0-20180203182544-0a76f03a811a
 github.com/ethantkoenig/rupture
-# github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a
-github.com/facebookgo/clock
-# github.com/facebookgo/grace v0.0.0-20160926231715-5729e484473f
-github.com/facebookgo/grace/gracehttp
-github.com/facebookgo/grace/gracenet
-# github.com/facebookgo/httpdown v0.0.0-20160323221027-a3b1354551a2
-github.com/facebookgo/httpdown
-# github.com/facebookgo/stats v0.0.0-20151006221625-1b76add642e4
-github.com/facebookgo/stats
 # github.com/fsnotify/fsnotify v1.4.7
 github.com/fsnotify/fsnotify
 # github.com/gliderlabs/ssh v0.2.2
@@ -198,8 +191,6 @@ github.com/go-swagger/go-swagger/cmd/swagger/commands/initcmd
 github.com/go-swagger/go-swagger/codescan
 github.com/go-swagger/go-swagger/generator
 github.com/go-swagger/go-swagger/scan
-# github.com/go-xorm/xorm v0.7.9
-github.com/go-xorm/xorm
 # github.com/gobwas/glob v0.2.3
 github.com/gobwas/glob
 github.com/gobwas/glob/compiler
@@ -281,7 +272,7 @@ github.com/klauspost/crc32
 github.com/kr/pretty
 # github.com/kr/text v0.1.0
 github.com/kr/text
-# github.com/lafriks/xormstore v1.3.1
+# github.com/lafriks/xormstore v1.3.2
 github.com/lafriks/xormstore
 github.com/lafriks/xormstore/util
 # github.com/lib/pq v1.2.0
@@ -546,11 +537,9 @@ google.golang.org/appengine/urlfetch
 gopkg.in/alexcesaro/quotedprintable.v3
 # gopkg.in/asn1-ber.v1 v1.0.0-20150924051756-4e86f4367175
 gopkg.in/asn1-ber.v1
-# gopkg.in/editorconfig/editorconfig-core-go.v1 v1.3.0
-gopkg.in/editorconfig/editorconfig-core-go.v1
 # gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
 gopkg.in/gomail.v2
-# gopkg.in/ini.v1 v1.46.0
+# gopkg.in/ini.v1 v1.48.0
 gopkg.in/ini.v1
 # gopkg.in/ldap.v3 v3.0.2
 gopkg.in/ldap.v3
@@ -610,11 +599,13 @@ gopkg.in/testfixtures.v2
 gopkg.in/warnings.v0
 # gopkg.in/yaml.v2 v2.2.2
 gopkg.in/yaml.v2
-# mvdan.cc/xurls/v2 v2.0.0
+# mvdan.cc/xurls/v2 v2.1.0
 mvdan.cc/xurls/v2
-# strk.kbt.io/projects/go/libravatar v0.0.0-20160628055650-5eed7bff870a
+# strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251
 strk.kbt.io/projects/go/libravatar
 # xorm.io/builder v0.3.6
 xorm.io/builder
 # xorm.io/core v0.7.2
 xorm.io/core
+# xorm.io/xorm v0.8.0
+xorm.io/xorm
diff --git a/vendor/mvdan.cc/xurls/v2/.travis.yml b/vendor/mvdan.cc/xurls/v2/.travis.yml
deleted file mode 100644
index e9bd7a7650..0000000000
--- a/vendor/mvdan.cc/xurls/v2/.travis.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-language: go
-
-go:
-  - 1.10.x
-  - 1.11.x
-
-go_import_path: mvdan.cc/xurls
-
-env:
-  - GO111MODULE=on
-
-install: true
-
-script:
-  - go get -t -d ./...
-  - go build ./...
-  - go test ./...
diff --git a/vendor/mvdan.cc/xurls/v2/README.md b/vendor/mvdan.cc/xurls/v2/README.md
index 5058efe7a7..07fbdb3b25 100644
--- a/vendor/mvdan.cc/xurls/v2/README.md
+++ b/vendor/mvdan.cc/xurls/v2/README.md
@@ -1,18 +1,20 @@
 # xurls
 
 [![GoDoc](https://godoc.org/mvdan.cc/xurls?status.svg)](https://godoc.org/mvdan.cc/xurls)
-[![Travis](https://travis-ci.org/mvdan/xurls.svg?branch=master)](https://travis-ci.org/mvdan/xurls)
 
-Extract urls from text using regular expressions. Requires Go 1.10.3 or later.
+Extract urls from text using regular expressions. Requires Go 1.12 or later.
 
 ```go
 import "mvdan.cc/xurls/v2"
 
 func main() {
-	xurls.Relaxed().FindString("Do gophers live in golang.org?")
-	// "golang.org"
-	xurls.Strict().FindAllString("foo.com is http://foo.com/.", -1)
-	// []string{"http://foo.com/"}
+	rxRelaxed := xurls.Relaxed()
+	rxRelaxed.FindString("Do gophers live in golang.org?")  // "golang.org"
+	rxRelaxed.FindString("This string does not have a URL") // ""
+
+	rxStrict := xurls.Strict()
+	rxStrict.FindAllString("must have scheme: http://foo.com/.", -1) // []string{"http://foo.com/"}
+	rxStrict.FindAllString("no scheme, no match: foo.com", -1)       // []string{}
 }
 ```
 
@@ -20,7 +22,9 @@ Note that the funcs compile regexes, so avoid calling them repeatedly.
 
 #### cmd/xurls
 
-	go get -u mvdan.cc/xurls/v2/cmd/xurls
+To install the tool globally:
+
+	go get mvdan.cc/xurls/cmd/xurls
 
 ```shell
 $ echo "Do gophers live in http://golang.org?" | xurls
diff --git a/vendor/mvdan.cc/xurls/v2/go.mod b/vendor/mvdan.cc/xurls/v2/go.mod
index 6f0822a737..d9d334543c 100644
--- a/vendor/mvdan.cc/xurls/v2/go.mod
+++ b/vendor/mvdan.cc/xurls/v2/go.mod
@@ -1 +1,3 @@
 module mvdan.cc/xurls/v2
+
+go 1.13
diff --git a/vendor/mvdan.cc/xurls/v2/schemes.go b/vendor/mvdan.cc/xurls/v2/schemes.go
index 01b7944ae3..e8e6585f47 100644
--- a/vendor/mvdan.cc/xurls/v2/schemes.go
+++ b/vendor/mvdan.cc/xurls/v2/schemes.go
@@ -12,13 +12,18 @@ var Schemes = []string{
 	`about`,
 	`acap`,
 	`acct`,
+	`acd`,
 	`acr`,
 	`adiumxtra`,
+	`adt`,
 	`afp`,
 	`afs`,
 	`aim`,
+	`amss`,
+	`android`,
 	`appdata`,
 	`apt`,
+	`ark`,
 	`attachment`,
 	`aw`,
 	`barion`,
@@ -28,8 +33,11 @@ var Schemes = []string{
 	`blob`,
 	`bolo`,
 	`browserext`,
+	`calculator`,
 	`callto`,
 	`cap`,
+	`cast`,
+	`casts`,
 	`chrome`,
 	`chrome-extension`,
 	`cid`,
@@ -44,6 +52,7 @@ var Schemes = []string{
 	`conti`,
 	`crid`,
 	`cvs`,
+	`dab`,
 	`data`,
 	`dav`,
 	`diaspora`,
@@ -54,6 +63,9 @@ var Schemes = []string{
 	`dlna-playsingle`,
 	`dns`,
 	`dntp`,
+	`dpp`,
+	`drm`,
+	`drop`,
 	`dtn`,
 	`dvb`,
 	`ed2k`,
@@ -66,8 +78,11 @@ var Schemes = []string{
 	`file`,
 	`filesystem`,
 	`finger`,
+	`first-run-pen-experience`,
 	`fish`,
+	`fm`,
 	`ftp`,
+	`fuchsia-pkg`,
 	`geo`,
 	`gg`,
 	`git`,
@@ -112,6 +127,8 @@ var Schemes = []string{
 	`lastfm`,
 	`ldap`,
 	`ldaps`,
+	`leaptofrogans`,
+	`lorawan`,
 	`lvlt`,
 	`magnet`,
 	`mailserver`,
@@ -129,9 +146,11 @@ var Schemes = []string{
 	`moz`,
 	`ms-access`,
 	`ms-browser-extension`,
+	`ms-calculator`,
 	`ms-drive-to`,
 	`ms-enrollment`,
 	`ms-excel`,
+	`ms-eyecontrolspeech`,
 	`ms-gamebarservices`,
 	`ms-gamingoverlay`,
 	`ms-getoffice`,
@@ -141,6 +160,7 @@ var Schemes = []string{
 	`ms-lockscreencomponent-config`,
 	`ms-media-stream-id`,
 	`ms-mixedrealitycapture`,
+	`ms-mobileplans`,
 	`ms-officeapp`,
 	`ms-people`,
 	`ms-project`,
@@ -186,6 +206,7 @@ var Schemes = []string{
 	`msnim`,
 	`msrp`,
 	`msrps`,
+	`mss`,
 	`mtqp`,
 	`mumble`,
 	`mupdate`,
@@ -205,6 +226,7 @@ var Schemes = []string{
 	`pack`,
 	`palm`,
 	`paparazzi`,
+	`payto`,
 	`pkcs11`,
 	`platform`,
 	`pop`,
@@ -213,6 +235,7 @@ var Schemes = []string{
 	`proxy`,
 	`pwid`,
 	`psyc`,
+	`pttp`,
 	`qb`,
 	`query`,
 	`redis`,
diff --git a/vendor/mvdan.cc/xurls/v2/tlds.go b/vendor/mvdan.cc/xurls/v2/tlds.go
index 084ab84d46..c98ce508d6 100644
--- a/vendor/mvdan.cc/xurls/v2/tlds.go
+++ b/vendor/mvdan.cc/xurls/v2/tlds.go
@@ -24,7 +24,6 @@ var TLDs = []string{
 	`accountant`,
 	`accountants`,
 	`aco`,
-	`active`,
 	`actor`,
 	`ad`,
 	`adac`,
@@ -154,7 +153,6 @@ var TLDs = []string{
 	`bj`,
 	`black`,
 	`blackfriday`,
-	`blanco`,
 	`blockbuster`,
 	`blog`,
 	`bloomberg`,
@@ -163,7 +161,6 @@ var TLDs = []string{
 	`bms`,
 	`bmw`,
 	`bn`,
-	`bnl`,
 	`bnpparibas`,
 	`bo`,
 	`boats`,
@@ -307,6 +304,7 @@ var TLDs = []string{
 	`coupon`,
 	`coupons`,
 	`courses`,
+	`cpa`,
 	`cr`,
 	`credit`,
 	`creditcard`,
@@ -370,7 +368,6 @@ var TLDs = []string{
 	`doctor`,
 	`dodge`,
 	`dog`,
-	`doha`,
 	`domains`,
 	`dot`,
 	`download`,
@@ -379,7 +376,6 @@ var TLDs = []string{
 	`dubai`,
 	`duck`,
 	`dunlop`,
-	`duns`,
 	`dupont`,
 	`durban`,
 	`dvag`,
@@ -400,7 +396,6 @@ var TLDs = []string{
 	`engineer`,
 	`engineering`,
 	`enterprises`,
-	`epost`,
 	`epson`,
 	`equipment`,
 	`er`,
@@ -496,6 +491,7 @@ var TLDs = []string{
 	`games`,
 	`gap`,
 	`garden`,
+	`gay`,
 	`gb`,
 	`gbiz`,
 	`gd`,
@@ -588,7 +584,6 @@ var TLDs = []string{
 	`homes`,
 	`homesense`,
 	`honda`,
-	`honeywell`,
 	`horse`,
 	`hospital`,
 	`host`,
@@ -642,7 +637,6 @@ var TLDs = []string{
 	`ir`,
 	`irish`,
 	`is`,
-	`iselect`,
 	`ismaili`,
 	`ist`,
 	`istanbul`,
@@ -752,6 +746,7 @@ var TLDs = []string{
 	`lixil`,
 	`lk`,
 	`llc`,
+	`llp`,
 	`loan`,
 	`loans`,
 	`locker`,
@@ -827,7 +822,6 @@ var TLDs = []string{
 	`mo`,
 	`mobi`,
 	`mobile`,
-	`mobily`,
 	`moda`,
 	`moe`,
 	`moi`,
@@ -1161,21 +1155,19 @@ var TLDs = []string{
 	`sony`,
 	`soy`,
 	`space`,
-	`spiegel`,
 	`sport`,
 	`spot`,
 	`spreadbetting`,
 	`sr`,
 	`srl`,
 	`srt`,
+	`ss`,
 	`st`,
 	`stada`,
 	`staples`,
 	`star`,
-	`starhub`,
 	`statebank`,
 	`statefarm`,
-	`statoil`,
 	`stc`,
 	`stcgroup`,
 	`stockholm`,
@@ -1391,7 +1383,6 @@ var TLDs = []string{
 	`zara`,
 	`zero`,
 	`zip`,
-	`zippo`,
 	`zm`,
 	`zone`,
 	`zuerich`,
@@ -1449,7 +1440,7 @@ var TLDs = []string{
 	`كوم`,
 	`مصر`,
 	`مليسيا`,
-	`موبايلي`,
+	`موريتانيا`,
 	`موقع`,
 	`همراه`,
 	`پاكستان`,
diff --git a/vendor/mvdan.cc/xurls/v2/xurls.go b/vendor/mvdan.cc/xurls/v2/xurls.go
index d6279ae60b..7244c709a0 100644
--- a/vendor/mvdan.cc/xurls/v2/xurls.go
+++ b/vendor/mvdan.cc/xurls/v2/xurls.go
@@ -19,9 +19,9 @@ const (
 	iriChar   = letter + mark + number
 	currency  = `\p{Sc}`
 	otherSymb = `\p{So}`
-	endChar   = iriChar + `/\-+_&~*%=#` + currency + otherSymb
+	endChar   = iriChar + `/\-+&~%=#` + currency + otherSymb
 	otherPunc = `\p{Po}`
-	midChar   = endChar + `|` + otherPunc
+	midChar   = endChar + "_*" + otherPunc
 	wellParen = `\([` + midChar + `]*(\([` + midChar + `]*\)[` + midChar + `]*)*\)`
 	wellBrack = `\[[` + midChar + `]*(\[[` + midChar + `]*\][` + midChar + `]*)*\]`
 	wellBrace = `\{[` + midChar + `]*(\{[` + midChar + `]*\}[` + midChar + `]*)*\}`
@@ -72,9 +72,11 @@ func strictExp() string {
 }
 
 func relaxedExp() string {
-	site := domain + `(?i)` + anyOf(append(TLDs, PseudoTLDs...)...) + `(?-i)`
+	punycode := `xn--[a-z0-9-]+`
+	knownTLDs := anyOf(append(TLDs, PseudoTLDs...)...)
+	site := domain + `(?i)(` + punycode + `|` + knownTLDs + `)(?-i)`
 	hostName := `(` + site + `|` + ipAddr + `)`
-	webURL := hostName + port + `(/|/` + pathCont + `?|\b|$)`
+	webURL := hostName + port + `(/|/` + pathCont + `?|\b|(?m)$)`
 	return strictExp() + `|` + webURL
 }
 
diff --git a/vendor/strk.kbt.io/projects/go/libravatar/Makefile b/vendor/strk.kbt.io/projects/go/libravatar/Makefile
new file mode 100644
index 0000000000..2a70ddc72c
--- /dev/null
+++ b/vendor/strk.kbt.io/projects/go/libravatar/Makefile
@@ -0,0 +1,12 @@
+PACKAGES ?= $(shell go list ./...)
+
+.PHONY: check
+check: lint
+	go test
+
+.PHONY: lint
+lint:
+	@which golint > /dev/null; if [ $$? -ne 0 ]; then \
+		go get -u github.com/golang/lint/golint; \
+	fi
+	@for PKG in $(PACKAGES); do golint -set_exit_status $$PKG || exit 1; done;
diff --git a/vendor/strk.kbt.io/projects/go/libravatar/README.md b/vendor/strk.kbt.io/projects/go/libravatar/README.md
index 660cd90643..c0a9f942e2 100644
--- a/vendor/strk.kbt.io/projects/go/libravatar/README.md
+++ b/vendor/strk.kbt.io/projects/go/libravatar/README.md
@@ -1,12 +1,14 @@
 Simple [golang](https://www.golang.org) library for serving
 [federated avatars](https://www.libravatar.org)
 
+[![trunk](https://goreportcard.com/badge/strk.kbt.io/projects/go/libravatar)]
+(https://goreportcard.com/report/strk.kbt.io/projects/go/libravatar)
+
 # Use
 
 ```sh
 go get strk.kbt.io/projects/go/libravatar
-cd $GOPATH/src/strk.kbt.io/projects/go/libravatar
-go doc
+go doc strk.kbt.io/projects/go/libravatar
 ```
 
 # Contribute
diff --git a/vendor/strk.kbt.io/projects/go/libravatar/libravatar.go b/vendor/strk.kbt.io/projects/go/libravatar/libravatar.go
index 4c748456f3..fe544cd4b4 100644
--- a/vendor/strk.kbt.io/projects/go/libravatar/libravatar.go
+++ b/vendor/strk.kbt.io/projects/go/libravatar/libravatar.go
@@ -5,7 +5,7 @@
 // Implements support for federated avatars lookup.
 // See https://wiki.libravatar.org/api/
 
-package libravatar
+package libravatar // import "strk.kbt.io/projects/go/libravatar"
 
 import (
 	"crypto/md5"
@@ -16,6 +16,7 @@ import (
 	"net/mail"
 	"net/url"
 	"strings"
+	"sync"
 	"time"
 )
 
@@ -38,7 +39,8 @@ const (
 )
 
 var (
-	// Default object, enabling object-less function calls
+	// DefaultLibravatar is a default Libravatar object,
+	// enabling object-less function calls
 	DefaultLibravatar = New()
 )
 
@@ -53,14 +55,16 @@ type cacheValue struct {
 	checkedAt time.Time
 }
 
+// Libravatar is an opaque structure holding service configuration
 type Libravatar struct {
-	defUrl             string // default url
+	defURL             string // default url
 	picSize            int    // picture size
 	fallbackHost       string // default fallback URL
 	secureFallbackHost string // default fallback URL for secure connections
 	useHTTPS           bool
 	nameCache          map[cacheKey]cacheValue
 	nameCacheDuration  time.Duration
+	nameCacheMutex     *sync.Mutex
 	minSize            uint   // smallest image dimension allowed
 	maxSize            uint   // largest image dimension allowed
 	size               uint   // what dimension should be used
@@ -68,7 +72,7 @@ type Libravatar struct {
 	secureServiceBase  string // SRV record to be queried for federation with secure servers
 }
 
-// Instanciate a library handle
+// New instanciates a new Libravatar object (handle)
 func New() *Libravatar {
 	// According to https://wiki.libravatar.org/running_your_own/
 	// the time-to-live (cache expiry) should be set to at least 1 day.
@@ -82,27 +86,28 @@ func New() *Libravatar {
 		secureServiceBase:  `avatars-sec`,
 		nameCache:          make(map[cacheKey]cacheValue),
 		nameCacheDuration:  24 * time.Hour,
+		nameCacheMutex:     &sync.Mutex{},
 	}
 }
 
-// Set the hostname for fallbacks in case no avatar service is defined
-// for a domain
+// SetFallbackHost sets the hostname for fallbacks in case no avatar
+// service is defined for a domain
 func (v *Libravatar) SetFallbackHost(host string) {
 	v.fallbackHost = host
 }
 
-// Set the hostname for fallbacks in case no avatar service is defined
-// for a domain, when requiring secure domains
+// SetSecureFallbackHost sets the hostname for fallbacks in case no
+// avatar service is defined for a domain, when requiring secure domains
 func (v *Libravatar) SetSecureFallbackHost(host string) {
 	v.secureFallbackHost = host
 }
 
-// Set useHTTPS flag
+// SetUseHTTPS sets flag requesting use of https for fetching avatars
 func (v *Libravatar) SetUseHTTPS(use bool) {
 	v.useHTTPS = use
 }
 
-// Set Avatars image dimension (0 for default)
+// SetAvatarSize sets avatars image dimension (0 for default)
 func (v *Libravatar) SetAvatarSize(size uint) {
 	v.size = size
 }
@@ -150,8 +155,8 @@ func (v *Libravatar) process(email *mail.Address, openid *url.URL) (string, erro
 	res := fmt.Sprintf("%s/avatar/%s", URL, v.genHash(email, openid))
 
 	values := make(url.Values)
-	if v.defUrl != "" {
-		values.Add("d", v.defUrl)
+	if v.defURL != "" {
+		values.Add("d", v.defURL)
 	}
 	if v.size > 0 {
 		values.Add("s", fmt.Sprintf("%d", v.size))
@@ -181,7 +186,9 @@ func (v *Libravatar) baseURL(email *mail.Address, openid *url.URL) (string, erro
 	host := v.getDomain(email, openid)
 	key := cacheKey{service, host}
 	now := time.Now()
+	v.nameCacheMutex.Lock()
 	val, found := v.nameCache[key]
+	v.nameCacheMutex.Unlock()
 	if found && now.Sub(val.checkedAt) <= v.nameCacheDuration {
 		return protocol + val.target, nil
 	}
@@ -204,53 +211,55 @@ func (v *Libravatar) baseURL(email *mail.Address, openid *url.URL) (string, erro
 		}
 
 		var (
-			total_weight uint16
-			records      []record
-			top_priority = addrs[0].Priority
-			top_record   *net.SRV
+			totalWeight uint16
+			records     []record
+			topPriority = addrs[0].Priority
+			topRecord   *net.SRV
 		)
 
 		for _, rr := range addrs {
-			if rr.Priority > top_priority {
+			if rr.Priority > topPriority {
 				continue
-			} else if rr.Priority < top_priority {
+			} else if rr.Priority < topPriority {
 				// won't happen, because net sorts
 				// by priority, but just in case
-				total_weight = 0
+				totalWeight = 0
 				records = nil
-				top_priority = rr.Priority
+				topPriority = rr.Priority
 			}
 
-			total_weight += rr.Weight
+			totalWeight += rr.Weight
 
 			if rr.Weight > 0 {
-				records = append(records, record{rr, total_weight})
+				records = append(records, record{rr, totalWeight})
 			} else if rr.Weight == 0 {
-				records = append([]record{record{srv: rr, weight: total_weight}}, records...)
+				records = append([]record{record{srv: rr, weight: totalWeight}}, records...)
 			}
 		}
 
 		if len(records) == 1 {
-			top_record = records[0].srv
+			topRecord = records[0].srv
 		} else {
-			randnum := uint16(rand.Intn(int(total_weight)))
+			randnum := uint16(rand.Intn(int(totalWeight)))
 
 			for _, rr := range records {
 				if rr.weight >= randnum {
-					top_record = rr.srv
+					topRecord = rr.srv
 					break
 				}
 			}
 		}
 
-		domain = fmt.Sprintf("%s:%d", top_record.Target, top_record.Port)
+		domain = fmt.Sprintf("%s:%d", topRecord.Target, topRecord.Port)
 	}
 
+	v.nameCacheMutex.Lock()
 	v.nameCache[key] = cacheValue{checkedAt: now, target: domain}
+	v.nameCacheMutex.Unlock()
 	return protocol + domain, nil
 }
 
-// Return url of the avatar for the given email
+// FromEmail returns the url of the avatar for the given email
 func (v *Libravatar) FromEmail(email string) (string, error) {
 	addr, err := mail.ParseAddress(email)
 	if err != nil {
@@ -265,12 +274,13 @@ func (v *Libravatar) FromEmail(email string) (string, error) {
 	return link, nil
 }
 
-// Object-less call to DefaultLibravatar for an email adders
+// FromEmail is the object-less call to DefaultLibravatar for an email adders
 func FromEmail(email string) (string, error) {
 	return DefaultLibravatar.FromEmail(email)
 }
 
-// Return url of the avatar for the given url (typically for OpenID)
+// FromURL returns the url of the avatar for the given url (typically
+// for OpenID)
 func (v *Libravatar) FromURL(openid string) (string, error) {
 	ourl, err := url.Parse(openid)
 	if err != nil {
@@ -291,7 +301,7 @@ func (v *Libravatar) FromURL(openid string) (string, error) {
 	return link, nil
 }
 
-// Object-less call to DefaultLibravatar for a URL
+// FromURL is the object-less call to DefaultLibravatar for a URL
 func FromURL(openid string) (string, error) {
 	return DefaultLibravatar.FromURL(openid)
 }
diff --git a/vendor/github.com/go-xorm/xorm/.drone.yml b/vendor/xorm.io/xorm/.drone.yml
similarity index 94%
rename from vendor/github.com/go-xorm/xorm/.drone.yml
rename to vendor/xorm.io/xorm/.drone.yml
index b162d7c8a4..c373975df6 100644
--- a/vendor/github.com/go-xorm/xorm/.drone.yml
+++ b/vendor/xorm.io/xorm/.drone.yml
@@ -6,28 +6,11 @@ platform:
   os: linux
   arch: amd64
 
-clone:
-  disable: true
-
 workspace:
   base: /go
-  path: src/github.com/go-xorm/xorm
+  path: src/gitea.com/xorm/xorm
 
 steps:
-- name: git
-  pull: default
-  image: plugins/git:next
-  settings:
-    depth: 50
-    tags: true
-
-- name: init_postgres
-  pull: default
-  image: postgres:9.5
-  commands:
-  - "until psql -U postgres -d xorm_test -h pgsql \\\n -c \"SELECT 1;\" >/dev/null 2>&1; do sleep 1; done\n"
-  - "psql -U postgres -d xorm_test -h pgsql \\\n  -c \"create schema xorm;\"\n"
-
 - name: build
   pull: default
   image: golang:1.10
@@ -186,28 +169,11 @@ platform:
   os: linux
   arch: amd64
 
-clone:
-  disable: true
-
 workspace:
   base: /go
-  path: src/github.com/go-xorm/xorm
+  path: src/gitea.com/xorm/xorm
 
 steps:
-- name: git
-  pull: default
-  image: plugins/git:next
-  settings:
-    depth: 50
-    tags: true
-
-- name: init_postgres
-  pull: default
-  image: postgres:9.5
-  commands:
-  - "until psql -U postgres -d xorm_test -h pgsql \\\n -c \"SELECT 1;\" >/dev/null 2>&1; do sleep 1; done\n"
-  - "psql -U postgres -d xorm_test -h pgsql \\\n  -c \"create schema xorm;\"\n"
-
 - name: build
   pull: default
   image: golang:1.11
@@ -406,20 +372,11 @@ platform:
   os: linux
   arch: amd64
 
-clone:
-  disable: true
-
 workspace:
   base: /go
-  path: src/github.com/go-xorm/xorm
+  path: src/gitea.com/xorm/xorm
 
 steps:
-- name: git
-  pull: default
-  image: plugins/git:next
-  settings:
-    depth: 50
-    tags: true
 
 - name: build
   pull: default
@@ -618,20 +575,11 @@ platform:
   os: linux
   arch: amd64
 
-clone:
-  disable: true
-
 workspace:
   base: /go
-  path: src/github.com/go-xorm/xorm
+  path: src/gitea.com/xorm/xorm
 
 steps:
-- name: git
-  pull: default
-  image: plugins/git:next
-  settings:
-    depth: 50
-    tags: true
 
 - name: build
   pull: default
diff --git a/vendor/github.com/go-xorm/xorm/.gitignore b/vendor/xorm.io/xorm/.gitignore
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/.gitignore
rename to vendor/xorm.io/xorm/.gitignore
diff --git a/vendor/github.com/go-xorm/xorm/CONTRIBUTING.md b/vendor/xorm.io/xorm/CONTRIBUTING.md
similarity index 89%
rename from vendor/github.com/go-xorm/xorm/CONTRIBUTING.md
rename to vendor/xorm.io/xorm/CONTRIBUTING.md
index 37f4bc5fa8..442aa4d311 100644
--- a/vendor/github.com/go-xorm/xorm/CONTRIBUTING.md
+++ b/vendor/xorm.io/xorm/CONTRIBUTING.md
@@ -32,10 +32,10 @@ proposed functionality.
 We appreciate any bug reports, but especially ones with self-contained
 (doesn't depend on code outside of xorm), minimal (can't be simplified
 further) test cases. It's especially helpful if you can submit a pull
-request with just the failing test case(you can find some example test file like [session_get_test.go](https://github.com/go-xorm/xorm/blob/master/session_get_test.go)).
+request with just the failing test case(you can find some example test file like [session_get_test.go](https://gitea.com/xorm/xorm/src/branch/master/session_get_test.go)).
 
 If you implements a new database interface, you maybe need to add a test_<databasename>.sh file.
-For example, [mysql_test.go](https://github.com/go-xorm/xorm/blob/master/test_mysql.sh)
+For example, [mysql_test.go](https://gitea.com/xorm/xorm/src/branch/master/test_mysql.sh)
 
 ### New functionality
 
diff --git a/vendor/github.com/go-xorm/xorm/LICENSE b/vendor/xorm.io/xorm/LICENSE
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/LICENSE
rename to vendor/xorm.io/xorm/LICENSE
diff --git a/vendor/github.com/go-xorm/xorm/README.md b/vendor/xorm.io/xorm/README.md
similarity index 95%
rename from vendor/github.com/go-xorm/xorm/README.md
rename to vendor/xorm.io/xorm/README.md
index 62b40ba304..17a6ed37ff 100644
--- a/vendor/github.com/go-xorm/xorm/README.md
+++ b/vendor/xorm.io/xorm/README.md
@@ -1,11 +1,11 @@
 # xorm
 
-[中文](https://github.com/go-xorm/xorm/blob/master/README_CN.md)
+[中文](https://gitea.com/xorm/xorm/src/branch/master/README_CN.md)
 
 Xorm is a simple and powerful ORM for Go.
 
-[![CircleCI](https://circleci.com/gh/go-xorm/xorm.svg?style=shield)](https://circleci.com/gh/go-xorm/xorm) [![codecov](https://codecov.io/gh/go-xorm/xorm/branch/master/graph/badge.svg)](https://codecov.io/gh/go-xorm/xorm)
-[![](https://goreportcard.com/badge/github.com/go-xorm/xorm)](https://goreportcard.com/report/github.com/go-xorm/xorm) 
+[![Build Status](https://drone.gitea.com/api/badges/xorm/xorm/status.svg)](https://drone.gitea.com/xorm/xorm) [![](http://gocover.io/_badge/xorm.io/xorm)](https://gocover.io/xorm.io/xorm)
+[![](https://goreportcard.com/badge/xorm.io/xorm)](https://goreportcard.com/report/xorm.io/xorm)
 [![Join the chat at https://img.shields.io/discord/323460943201959939.svg](https://img.shields.io/discord/323460943201959939.svg)](https://discord.gg/HuR2CF3)
 
 ## Features
@@ -56,13 +56,13 @@ Drivers for Go's sql package which currently support database/sql includes:
 
 ## Installation
 
-	go get github.com/go-xorm/xorm
+	go get xorm.io/xorm
 
 ## Documents
 
 * [Manual](http://xorm.io/docs)
 
-* [GoDoc](http://godoc.org/github.com/go-xorm/xorm)
+* [GoDoc](http://godoc.org/xorm.io/xorm)
 
 ## Quick Start
 
@@ -337,7 +337,7 @@ if _, err := session.Exec("delete from userinfo where username = ?", user2.Usern
 return nil
 ```
 
-* Transation should on one go routine. There is transaction and resue session memory
+* Transation should be on one go routine. There is transaction and resue session memory
 
 ```Go
 session := engine.NewSession()
@@ -419,7 +419,7 @@ res, err := engine.Transaction(func(session *xorm.Session) (interface{}, error)
 
 ## Contributing
 
-If you want to pull request, please see [CONTRIBUTING](https://github.com/go-xorm/xorm/blob/master/CONTRIBUTING.md). And we also provide [Xorm on Google Groups](https://groups.google.com/forum/#!forum/xorm) to discuss.
+If you want to pull request, please see [CONTRIBUTING](https://gitea.com/xorm/xorm/src/branch/master/CONTRIBUTING.md). And we also provide [Xorm on Google Groups](https://groups.google.com/forum/#!forum/xorm) to discuss.
 
 ## Credits
 
diff --git a/vendor/github.com/go-xorm/xorm/README_CN.md b/vendor/xorm.io/xorm/README_CN.md
similarity index 95%
rename from vendor/github.com/go-xorm/xorm/README_CN.md
rename to vendor/xorm.io/xorm/README_CN.md
index 0cec6ed5c6..644bdc0b63 100644
--- a/vendor/github.com/go-xorm/xorm/README_CN.md
+++ b/vendor/xorm.io/xorm/README_CN.md
@@ -1,11 +1,11 @@
 # xorm
 
-[English](https://github.com/go-xorm/xorm/blob/master/README.md)
+[English](https://gitea.com/xorm/xorm/src/branch/master/README.md)
 
 xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作非常简便。
 
-[![CircleCI](https://circleci.com/gh/go-xorm/xorm.svg?style=shield)](https://circleci.com/gh/go-xorm/xorm) [![codecov](https://codecov.io/gh/go-xorm/xorm/branch/master/graph/badge.svg)](https://codecov.io/gh/go-xorm/xorm)
-[![](https://goreportcard.com/badge/github.com/go-xorm/xorm)](https://goreportcard.com/report/github.com/go-xorm/xorm)
+[![Build Status](https://drone.gitea.com/api/badges/xorm/builder/status.svg)](https://drone.gitea.com/xorm/builder) [![](http://gocover.io/_badge/xorm.io/xorm)](https://gocover.io/xorm.io/xorm)
+[![](https://goreportcard.com/badge/xorm.io/xorm)](https://goreportcard.com/report/xorm.io/xorm)
 [![Join the chat at https://img.shields.io/discord/323460943201959939.svg](https://img.shields.io/discord/323460943201959939.svg)](https://discord.gg/HuR2CF3)
 
 ## 特性
@@ -56,15 +56,13 @@ xorm是一个简单而强大的Go语言ORM库. 通过它可以使数据库操作
 
 ## 安装
 
-	go get github.com/go-xorm/xorm
+	go get xorm.io/xorm
 
 ## 文档
 
 * [操作指南](http://xorm.io/docs)
 
-* [GoWalker代码文档](http://gowalker.org/github.com/go-xorm/xorm)
-
-* [Godoc代码文档](http://godoc.org/github.com/go-xorm/xorm)
+* [Godoc代码文档](http://godoc.org/xorm.io/xorm)
 
 # 快速开始
 
@@ -414,7 +412,7 @@ res, err := engine.Transaction(func(session *xorm.Session) (interface{}, error)
 
 ## 贡献
 
-如果您也想为Xorm贡献您的力量,请查看 [CONTRIBUTING](https://github.com/go-xorm/xorm/blob/master/CONTRIBUTING.md)。您也可以加入QQ群  技术帮助和讨论。
+如果您也想为Xorm贡献您的力量,请查看 [CONTRIBUTING](https://gitea.com/xorm/xorm/src/branch/master/CONTRIBUTING.md)。您也可以加入QQ群  技术帮助和讨论。
 群一:280360085 (已满)
 群二:795010183
 
diff --git a/vendor/github.com/go-xorm/xorm/cache_lru.go b/vendor/xorm.io/xorm/cache_lru.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/cache_lru.go
rename to vendor/xorm.io/xorm/cache_lru.go
diff --git a/vendor/github.com/go-xorm/xorm/cache_memory_store.go b/vendor/xorm.io/xorm/cache_memory_store.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/cache_memory_store.go
rename to vendor/xorm.io/xorm/cache_memory_store.go
diff --git a/vendor/github.com/go-xorm/xorm/context_cache.go b/vendor/xorm.io/xorm/context_cache.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/context_cache.go
rename to vendor/xorm.io/xorm/context_cache.go
diff --git a/vendor/github.com/go-xorm/xorm/convert.go b/vendor/xorm.io/xorm/convert.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/convert.go
rename to vendor/xorm.io/xorm/convert.go
diff --git a/vendor/github.com/go-xorm/xorm/dialect_mssql.go b/vendor/xorm.io/xorm/dialect_mssql.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/dialect_mssql.go
rename to vendor/xorm.io/xorm/dialect_mssql.go
diff --git a/vendor/github.com/go-xorm/xorm/dialect_mysql.go b/vendor/xorm.io/xorm/dialect_mysql.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/dialect_mysql.go
rename to vendor/xorm.io/xorm/dialect_mysql.go
diff --git a/vendor/github.com/go-xorm/xorm/dialect_oracle.go b/vendor/xorm.io/xorm/dialect_oracle.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/dialect_oracle.go
rename to vendor/xorm.io/xorm/dialect_oracle.go
diff --git a/vendor/github.com/go-xorm/xorm/dialect_postgres.go b/vendor/xorm.io/xorm/dialect_postgres.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/dialect_postgres.go
rename to vendor/xorm.io/xorm/dialect_postgres.go
diff --git a/vendor/github.com/go-xorm/xorm/dialect_sqlite3.go b/vendor/xorm.io/xorm/dialect_sqlite3.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/dialect_sqlite3.go
rename to vendor/xorm.io/xorm/dialect_sqlite3.go
diff --git a/vendor/github.com/go-xorm/xorm/doc.go b/vendor/xorm.io/xorm/doc.go
similarity index 99%
rename from vendor/github.com/go-xorm/xorm/doc.go
rename to vendor/xorm.io/xorm/doc.go
index a687e69476..9620bca19f 100644
--- a/vendor/github.com/go-xorm/xorm/doc.go
+++ b/vendor/xorm.io/xorm/doc.go
@@ -10,7 +10,7 @@ Installation
 
 Make sure you have installed Go 1.6+ and then:
 
-    go get github.com/go-xorm/xorm
+    go get xorm.io/xorm
 
 Create Engine
 
diff --git a/vendor/github.com/go-xorm/xorm/engine.go b/vendor/xorm.io/xorm/engine.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/engine.go
rename to vendor/xorm.io/xorm/engine.go
diff --git a/vendor/github.com/go-xorm/xorm/engine_cond.go b/vendor/xorm.io/xorm/engine_cond.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/engine_cond.go
rename to vendor/xorm.io/xorm/engine_cond.go
diff --git a/vendor/github.com/go-xorm/xorm/engine_context.go b/vendor/xorm.io/xorm/engine_context.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/engine_context.go
rename to vendor/xorm.io/xorm/engine_context.go
diff --git a/vendor/github.com/go-xorm/xorm/engine_group.go b/vendor/xorm.io/xorm/engine_group.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/engine_group.go
rename to vendor/xorm.io/xorm/engine_group.go
diff --git a/vendor/github.com/go-xorm/xorm/engine_group_policy.go b/vendor/xorm.io/xorm/engine_group_policy.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/engine_group_policy.go
rename to vendor/xorm.io/xorm/engine_group_policy.go
diff --git a/vendor/github.com/go-xorm/xorm/engine_table.go b/vendor/xorm.io/xorm/engine_table.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/engine_table.go
rename to vendor/xorm.io/xorm/engine_table.go
diff --git a/vendor/github.com/go-xorm/xorm/error.go b/vendor/xorm.io/xorm/error.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/error.go
rename to vendor/xorm.io/xorm/error.go
diff --git a/vendor/github.com/go-xorm/xorm/gen_reserved.sh b/vendor/xorm.io/xorm/gen_reserved.sh
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/gen_reserved.sh
rename to vendor/xorm.io/xorm/gen_reserved.sh
diff --git a/vendor/xorm.io/xorm/go.mod b/vendor/xorm.io/xorm/go.mod
new file mode 100644
index 0000000000..6d8b58f41a
--- /dev/null
+++ b/vendor/xorm.io/xorm/go.mod
@@ -0,0 +1,15 @@
+module xorm.io/xorm
+
+go 1.11
+
+require (
+	github.com/denisenkom/go-mssqldb v0.0.0-20190707035753-2be1aa521ff4
+	github.com/go-sql-driver/mysql v1.4.1
+	github.com/kr/pretty v0.1.0 // indirect
+	github.com/lib/pq v1.0.0
+	github.com/mattn/go-sqlite3 v1.10.0
+	github.com/stretchr/testify v1.4.0
+	github.com/ziutek/mymysql v1.5.4
+	xorm.io/builder v0.3.6
+	xorm.io/core v0.7.2
+)
diff --git a/vendor/github.com/go-xorm/xorm/go.sum b/vendor/xorm.io/xorm/go.sum
similarity index 83%
rename from vendor/github.com/go-xorm/xorm/go.sum
rename to vendor/xorm.io/xorm/go.sum
index cf637a8e06..2102cc5b7a 100644
--- a/vendor/github.com/go-xorm/xorm/go.sum
+++ b/vendor/xorm.io/xorm/go.sum
@@ -10,8 +10,6 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF
 github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
-github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -28,15 +26,12 @@ github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a h1:9wScpmSP5A3Bk8V3XHWUcJmYTh+ZnlHVyc+A4oZYS3Y=
 github.com/go-xorm/sqlfiddle v0.0.0-20180821085327-62ce714f951a/go.mod h1:56xuuqnHyryaerycW3BfssRdxQstACi0Epw/yC5E2xM=
-github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
-github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
-github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
@@ -48,15 +43,16 @@ github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51
 github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc=
-github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
-github.com/jackc/pgx v3.6.0+incompatible h1:bJeo4JdVbDAW8KB2m8XkFeo8CPipREoG37BwEoKGz+Q=
-github.com/jackc/pgx v3.6.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
 github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
 github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A=
 github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o=
@@ -69,8 +65,6 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
 github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
 github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
-github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@@ -81,8 +75,6 @@ github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y8
 github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
-github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE=
-github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -98,8 +90,6 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
 golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU=
-golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
@@ -112,34 +102,25 @@ golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73r
 golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
-golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190606050223-4d9ae51c2468/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
 google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -164,9 +145,5 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh
 honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 xorm.io/builder v0.3.6 h1:ha28mQ2M+TFx96Hxo+iq6tQgnkC9IZkM6D8w9sKHHF8=
 xorm.io/builder v0.3.6/go.mod h1:LEFAPISnRzG+zxaxj2vPicRwz67BdhFreKg8yv8/TgU=
-xorm.io/core v0.7.0 h1:hKxuOKWZNeiFQsSuGet/KV8HZ788hclvAl+7azx3tkM=
-xorm.io/core v0.7.0/go.mod h1:TuOJjIVa7e3w/rN8tDcAvuLBMtwzdHPbyOzE6Gk1EUI=
-xorm.io/core v0.7.1 h1:I6x6Q6dYb67aDEoYFWr2t8UcKIYjJPyCHS+aXuj5V0Y=
-xorm.io/core v0.7.1/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
-xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb h1:msX3zG3BPl8Ti+LDzP33/9K7BzO/WqFXk610K1kYKfo=
-xorm.io/core v0.7.2-0.20190928055935-90aeac8d08eb/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
+xorm.io/core v0.7.2 h1:mEO22A2Z7a3fPaZMk6gKL/jMD80iiyNwRrX5HOv3XLw=
+xorm.io/core v0.7.2/go.mod h1:jJfd0UAEzZ4t87nbQYtVjmqpIODugN6PD2D9E+dJvdM=
diff --git a/vendor/github.com/go-xorm/xorm/helpers.go b/vendor/xorm.io/xorm/helpers.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/helpers.go
rename to vendor/xorm.io/xorm/helpers.go
diff --git a/vendor/github.com/go-xorm/xorm/helpler_time.go b/vendor/xorm.io/xorm/helpler_time.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/helpler_time.go
rename to vendor/xorm.io/xorm/helpler_time.go
diff --git a/vendor/github.com/go-xorm/xorm/interface.go b/vendor/xorm.io/xorm/interface.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/interface.go
rename to vendor/xorm.io/xorm/interface.go
diff --git a/vendor/github.com/go-xorm/xorm/json.go b/vendor/xorm.io/xorm/json.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/json.go
rename to vendor/xorm.io/xorm/json.go
diff --git a/vendor/github.com/go-xorm/xorm/logger.go b/vendor/xorm.io/xorm/logger.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/logger.go
rename to vendor/xorm.io/xorm/logger.go
diff --git a/vendor/github.com/go-xorm/xorm/pg_reserved.txt b/vendor/xorm.io/xorm/pg_reserved.txt
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/pg_reserved.txt
rename to vendor/xorm.io/xorm/pg_reserved.txt
diff --git a/vendor/github.com/go-xorm/xorm/processors.go b/vendor/xorm.io/xorm/processors.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/processors.go
rename to vendor/xorm.io/xorm/processors.go
diff --git a/vendor/github.com/go-xorm/xorm/rows.go b/vendor/xorm.io/xorm/rows.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/rows.go
rename to vendor/xorm.io/xorm/rows.go
diff --git a/vendor/github.com/go-xorm/xorm/session.go b/vendor/xorm.io/xorm/session.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/session.go
rename to vendor/xorm.io/xorm/session.go
diff --git a/vendor/github.com/go-xorm/xorm/session_cols.go b/vendor/xorm.io/xorm/session_cols.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/session_cols.go
rename to vendor/xorm.io/xorm/session_cols.go
diff --git a/vendor/github.com/go-xorm/xorm/session_cond.go b/vendor/xorm.io/xorm/session_cond.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/session_cond.go
rename to vendor/xorm.io/xorm/session_cond.go
diff --git a/vendor/github.com/go-xorm/xorm/session_context.go b/vendor/xorm.io/xorm/session_context.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/session_context.go
rename to vendor/xorm.io/xorm/session_context.go
diff --git a/vendor/github.com/go-xorm/xorm/session_convert.go b/vendor/xorm.io/xorm/session_convert.go
similarity index 98%
rename from vendor/github.com/go-xorm/xorm/session_convert.go
rename to vendor/xorm.io/xorm/session_convert.go
index caff5d2624..7f11354d5e 100644
--- a/vendor/github.com/go-xorm/xorm/session_convert.go
+++ b/vendor/xorm.io/xorm/session_convert.go
@@ -84,6 +84,10 @@ func (session *Session) byte2Time(col *core.Column, data []byte) (outTime time.T
 	return session.str2Time(col, string(data))
 }
 
+var (
+	nullFloatType = reflect.TypeOf(sql.NullFloat64{})
+)
+
 // convert a db data([]byte) to a field value
 func (session *Session) bytes2Value(col *core.Column, fieldValue *reflect.Value, data []byte) error {
 	if structConvert, ok := fieldValue.Addr().Interface().(core.Conversion); ok {
@@ -583,6 +587,12 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val
 			t := fieldValue.Convert(core.TimeType).Interface().(time.Time)
 			tf := session.engine.formatColTime(col, t)
 			return tf, nil
+		} else if fieldType.ConvertibleTo(nullFloatType) {
+			t := fieldValue.Convert(nullFloatType).Interface().(sql.NullFloat64)
+			if !t.Valid {
+				return nil, nil
+			}
+			return t.Float64, nil
 		}
 
 		if !col.SQLType.IsJson() {
diff --git a/vendor/github.com/go-xorm/xorm/session_delete.go b/vendor/xorm.io/xorm/session_delete.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/session_delete.go
rename to vendor/xorm.io/xorm/session_delete.go
diff --git a/vendor/github.com/go-xorm/xorm/session_exist.go b/vendor/xorm.io/xorm/session_exist.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/session_exist.go
rename to vendor/xorm.io/xorm/session_exist.go
diff --git a/vendor/github.com/go-xorm/xorm/session_find.go b/vendor/xorm.io/xorm/session_find.go
similarity index 99%
rename from vendor/github.com/go-xorm/xorm/session_find.go
rename to vendor/xorm.io/xorm/session_find.go
index 6b8aa469db..e16ae54c94 100644
--- a/vendor/github.com/go-xorm/xorm/session_find.go
+++ b/vendor/xorm.io/xorm/session_find.go
@@ -110,7 +110,7 @@ func (session *Session) find(rowsSlicePtr interface{}, condiBean ...interface{})
 			}
 		} else {
 			// !oinume! Add "<col> IS NULL" to WHERE whatever condiBean is given.
-			// See https://github.com/go-xorm/xorm/issues/179
+			// See https://gitea.com/xorm/xorm/issues/179
 			if col := table.DeletedColumn(); col != nil && !session.statement.unscoped { // tag "deleted" is enabled
 				var colName = session.engine.Quote(col.Name)
 				if addedTableName {
diff --git a/vendor/github.com/go-xorm/xorm/session_get.go b/vendor/xorm.io/xorm/session_get.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/session_get.go
rename to vendor/xorm.io/xorm/session_get.go
diff --git a/vendor/github.com/go-xorm/xorm/session_insert.go b/vendor/xorm.io/xorm/session_insert.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/session_insert.go
rename to vendor/xorm.io/xorm/session_insert.go
diff --git a/vendor/github.com/go-xorm/xorm/session_iterate.go b/vendor/xorm.io/xorm/session_iterate.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/session_iterate.go
rename to vendor/xorm.io/xorm/session_iterate.go
diff --git a/vendor/github.com/go-xorm/xorm/session_query.go b/vendor/xorm.io/xorm/session_query.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/session_query.go
rename to vendor/xorm.io/xorm/session_query.go
diff --git a/vendor/github.com/go-xorm/xorm/session_raw.go b/vendor/xorm.io/xorm/session_raw.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/session_raw.go
rename to vendor/xorm.io/xorm/session_raw.go
diff --git a/vendor/github.com/go-xorm/xorm/session_schema.go b/vendor/xorm.io/xorm/session_schema.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/session_schema.go
rename to vendor/xorm.io/xorm/session_schema.go
diff --git a/vendor/github.com/go-xorm/xorm/session_stats.go b/vendor/xorm.io/xorm/session_stats.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/session_stats.go
rename to vendor/xorm.io/xorm/session_stats.go
diff --git a/vendor/github.com/go-xorm/xorm/session_tx.go b/vendor/xorm.io/xorm/session_tx.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/session_tx.go
rename to vendor/xorm.io/xorm/session_tx.go
diff --git a/vendor/github.com/go-xorm/xorm/session_update.go b/vendor/xorm.io/xorm/session_update.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/session_update.go
rename to vendor/xorm.io/xorm/session_update.go
diff --git a/vendor/github.com/go-xorm/xorm/statement.go b/vendor/xorm.io/xorm/statement.go
similarity index 99%
rename from vendor/github.com/go-xorm/xorm/statement.go
rename to vendor/xorm.io/xorm/statement.go
index ae396c4bae..67e352136f 100644
--- a/vendor/github.com/go-xorm/xorm/statement.go
+++ b/vendor/xorm.io/xorm/statement.go
@@ -149,8 +149,12 @@ func (statement *Statement) And(query interface{}, args ...interface{}) *Stateme
 		cond := builder.Expr(query.(string), args...)
 		statement.cond = statement.cond.And(cond)
 	case map[string]interface{}:
-		cond := builder.Eq(query.(map[string]interface{}))
-		statement.cond = statement.cond.And(cond)
+		queryMap := query.(map[string]interface{})
+		newMap := make(map[string]interface{})
+		for k, v := range queryMap {
+			newMap[statement.Engine.Quote(k)] = v
+		}
+		statement.cond = statement.cond.And(builder.Eq(newMap))
 	case builder.Cond:
 		cond := query.(builder.Cond)
 		statement.cond = statement.cond.And(cond)
diff --git a/vendor/github.com/go-xorm/xorm/statement_args.go b/vendor/xorm.io/xorm/statement_args.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/statement_args.go
rename to vendor/xorm.io/xorm/statement_args.go
diff --git a/vendor/github.com/go-xorm/xorm/statement_columnmap.go b/vendor/xorm.io/xorm/statement_columnmap.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/statement_columnmap.go
rename to vendor/xorm.io/xorm/statement_columnmap.go
diff --git a/vendor/github.com/go-xorm/xorm/statement_exprparam.go b/vendor/xorm.io/xorm/statement_exprparam.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/statement_exprparam.go
rename to vendor/xorm.io/xorm/statement_exprparam.go
diff --git a/vendor/github.com/go-xorm/xorm/statement_quote.go b/vendor/xorm.io/xorm/statement_quote.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/statement_quote.go
rename to vendor/xorm.io/xorm/statement_quote.go
diff --git a/vendor/github.com/go-xorm/xorm/syslogger.go b/vendor/xorm.io/xorm/syslogger.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/syslogger.go
rename to vendor/xorm.io/xorm/syslogger.go
diff --git a/vendor/github.com/go-xorm/xorm/tag.go b/vendor/xorm.io/xorm/tag.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/tag.go
rename to vendor/xorm.io/xorm/tag.go
diff --git a/vendor/github.com/go-xorm/xorm/test_mssql.sh b/vendor/xorm.io/xorm/test_mssql.sh
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/test_mssql.sh
rename to vendor/xorm.io/xorm/test_mssql.sh
diff --git a/vendor/github.com/go-xorm/xorm/test_mssql_cache.sh b/vendor/xorm.io/xorm/test_mssql_cache.sh
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/test_mssql_cache.sh
rename to vendor/xorm.io/xorm/test_mssql_cache.sh
diff --git a/vendor/github.com/go-xorm/xorm/test_mymysql.sh b/vendor/xorm.io/xorm/test_mymysql.sh
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/test_mymysql.sh
rename to vendor/xorm.io/xorm/test_mymysql.sh
diff --git a/vendor/github.com/go-xorm/xorm/test_mymysql_cache.sh b/vendor/xorm.io/xorm/test_mymysql_cache.sh
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/test_mymysql_cache.sh
rename to vendor/xorm.io/xorm/test_mymysql_cache.sh
diff --git a/vendor/github.com/go-xorm/xorm/test_mysql.sh b/vendor/xorm.io/xorm/test_mysql.sh
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/test_mysql.sh
rename to vendor/xorm.io/xorm/test_mysql.sh
diff --git a/vendor/github.com/go-xorm/xorm/test_mysql_cache.sh b/vendor/xorm.io/xorm/test_mysql_cache.sh
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/test_mysql_cache.sh
rename to vendor/xorm.io/xorm/test_mysql_cache.sh
diff --git a/vendor/github.com/go-xorm/xorm/test_postgres.sh b/vendor/xorm.io/xorm/test_postgres.sh
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/test_postgres.sh
rename to vendor/xorm.io/xorm/test_postgres.sh
diff --git a/vendor/github.com/go-xorm/xorm/test_postgres_cache.sh b/vendor/xorm.io/xorm/test_postgres_cache.sh
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/test_postgres_cache.sh
rename to vendor/xorm.io/xorm/test_postgres_cache.sh
diff --git a/vendor/github.com/go-xorm/xorm/test_sqlite.sh b/vendor/xorm.io/xorm/test_sqlite.sh
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/test_sqlite.sh
rename to vendor/xorm.io/xorm/test_sqlite.sh
diff --git a/vendor/github.com/go-xorm/xorm/test_sqlite_cache.sh b/vendor/xorm.io/xorm/test_sqlite_cache.sh
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/test_sqlite_cache.sh
rename to vendor/xorm.io/xorm/test_sqlite_cache.sh
diff --git a/vendor/github.com/go-xorm/xorm/test_tidb.sh b/vendor/xorm.io/xorm/test_tidb.sh
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/test_tidb.sh
rename to vendor/xorm.io/xorm/test_tidb.sh
diff --git a/vendor/github.com/go-xorm/xorm/transaction.go b/vendor/xorm.io/xorm/transaction.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/transaction.go
rename to vendor/xorm.io/xorm/transaction.go
diff --git a/vendor/github.com/go-xorm/xorm/types.go b/vendor/xorm.io/xorm/types.go
similarity index 100%
rename from vendor/github.com/go-xorm/xorm/types.go
rename to vendor/xorm.io/xorm/types.go
diff --git a/vendor/github.com/go-xorm/xorm/xorm.go b/vendor/xorm.io/xorm/xorm.go
similarity index 99%
rename from vendor/github.com/go-xorm/xorm/xorm.go
rename to vendor/xorm.io/xorm/xorm.go
index 26d00d264d..e1c83b56f2 100644
--- a/vendor/github.com/go-xorm/xorm/xorm.go
+++ b/vendor/xorm.io/xorm/xorm.go
@@ -20,7 +20,7 @@ import (
 
 const (
 	// Version show the xorm's version
-	Version string = "0.7.0.0504"
+	Version string = "0.8.0.1015"
 )
 
 func regDrvsNDialects() bool {