mirror of
https://github.com/projekteuler/projekteuler.git
synced 2026-01-27 18:48:49 +01:00
Compare commits
233 Commits
logo-in-na
...
a57eca5737
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a57eca5737 | ||
| e6986b85a9 | |||
| 1af75a7f5d | |||
| 469ef622ab | |||
| 487faace6e | |||
| a49487c3b7 | |||
|
|
6f10301192 | ||
| 393669bbde | |||
| 4339ded7d3 | |||
|
|
6ef8b772a9 | ||
| 9cff35ff64 | |||
|
|
3de48f0952 | ||
| e5b5eace95 | |||
| 0683e5657c | |||
| 552bd7287a | |||
|
|
467363aadc | ||
|
|
d3470f7679 | ||
|
|
6bd5d1d740 | ||
|
|
787f6ed7f0 | ||
|
|
8f6a7afd29 | ||
|
|
a52a25a21e | ||
| d2c2273500 | |||
|
|
6b60d32314 | ||
| dfd86af95e | |||
| 21c018ddce | |||
|
|
1ad6a3ecc5 | ||
|
|
d2940d667d | ||
| 12a633bc54 | |||
|
|
5780c64643 | ||
| a6d74cd0da | |||
| e7d739c212 | |||
| 2f734d851e | |||
|
|
49fc0ea601 | ||
|
|
a0a010e53b | ||
| 8f72d768f1 | |||
| 2e2e419938 | |||
|
|
62616d1865 | ||
| 686e8d9f46 | |||
|
|
067a7ba907 | ||
| a18efbf70c | |||
| feb7a3955d | |||
|
|
f313957284 | ||
|
|
e59e9da896 | ||
| 2b659a62ca | |||
|
|
923d4ce9f6 | ||
| 6a7094667e | |||
| 48a6a31cd1 | |||
|
|
46d14c843e | ||
|
|
4b04234c71 | ||
| 54c4b24880 | |||
|
|
9316b89430 | ||
| f26ed021eb | |||
|
|
e97cf8eafa | ||
| bb6a68417a | |||
|
|
c7dbaea090 | ||
| 7c95126a96 | |||
| cb3c05b9a6 | |||
|
|
047515d288 | ||
| de47c81f5c | |||
| 2cda24dcdb | |||
| 7db0dfe75c | |||
|
|
555e11e7b7 | ||
|
|
94c4ec9657 | ||
|
|
a17827e1ec | ||
| 2f8dd8d489 | |||
| 5468ea6d8d | |||
| 414c1e76cd | |||
| 6c626bb15d | |||
| 76fab428d7 | |||
|
|
9d7d88b40d | ||
| 132828483c | |||
| 0953f262a4 | |||
| 96f8a151f0 | |||
|
|
4e10a186f5 | ||
|
|
909ea52715 | ||
|
|
6786767424 | ||
| 1e68cdcbb3 | |||
| 8bd40ac5c7 | |||
|
|
7cb99e2c4a | ||
|
|
8457dce23d | ||
| f5b952cdff | |||
| f74ff66c78 | |||
|
|
4bc0451b7b | ||
|
|
a53b453f12 | ||
| e7e2e5598e | |||
|
|
c28634dbbb | ||
| e59e95d3e1 | |||
| 04e6dc595d | |||
|
|
ac6def6056 | ||
|
|
ebc0056eef | ||
| 2064f0ef7e | |||
| 7a275670fa | |||
|
|
2019386d93 | ||
|
|
2e7c41f27c | ||
| b2f8ac9d2e | |||
|
|
523f5bbcc2 | ||
| 67dc2e6813 | |||
| 4bfd3086bc | |||
| 39a9b0b492 | |||
|
|
d00f3249e1 | ||
| 93927fd92d | |||
| 3a526588ab | |||
|
|
fa6776fe6d | ||
|
|
f3fa619e64 | ||
| 3fbaa9f0d3 | |||
| 488e985350 | |||
| af1ac52d04 | |||
|
|
b7e1050296 | ||
|
|
e0ab8d5163 | ||
|
|
601f52357d | ||
| bf2bf1b12d | |||
| 1764af457c | |||
| c7a0c6f731 | |||
|
|
6eba3a3f84 | ||
| 707619d4c6 | |||
|
|
817aaf4c54 | ||
| 89a712890c | |||
|
|
c091ec0a2e | ||
| 9604f6c9da | |||
|
|
dfb5f4d8fe | ||
| 1785487c4d | |||
| ba7eb14a72 | |||
|
|
496a4641f8 | ||
|
|
94281fa6e5 | ||
| 0115025413 | |||
| 5fbb4ed343 | |||
|
|
bff16f27ed | ||
|
|
d7681e1bc9 | ||
| f2addd8605 | |||
|
|
3102fc2350 | ||
| 5bd86859af | |||
|
|
f9e755b8ae | ||
| 0761b5503e | |||
|
|
d0bd9ca880 | ||
| 4830885ab9 | |||
|
|
d65ccedaea | ||
| ab14519763 | |||
| 01f5fdd213 | |||
|
|
90b71a20c1 | ||
|
|
24cbefb8fa | ||
| 76a2f185be | |||
| 45c37bfe7f | |||
| 2b798639d7 | |||
|
|
a6f24e7fca | ||
| f70159ef36 | |||
|
|
8c2d31af9b | ||
| d17886de8c | |||
| 38633d6e79 | |||
| 3e85290fc6 | |||
|
|
ecf7368768 | ||
| e791d2527e | |||
|
|
6cdbf3ecb1 | ||
| 9feeae796c | |||
| d6ee556661 | |||
|
|
65863ee569 | ||
|
|
f8f0f0ef20 | ||
| a9a7c494c4 | |||
|
|
77d0bbd9a7 | ||
| 42575da300 | |||
|
|
1dece84925 | ||
| 992f046c67 | |||
| 6ce5a21f50 | |||
|
|
8caf7ef5ba | ||
|
|
6e6dddf619 | ||
| 49f21e2d23 | |||
| 6b4d4a3488 | |||
|
|
8ee11d25fb | ||
|
|
378bdc44e2 | ||
| d185820c97 | |||
|
|
67cb3dbf1d | ||
| ab755af873 | |||
|
|
0ddf8f8eaa | ||
| 07ed624459 | |||
|
|
f213fea8a7 | ||
| d23d8bc068 | |||
|
|
6958c9ad7d | ||
| 22e977db4c | |||
| 45c530891a | |||
| 1b728059e5 | |||
| 0ed3c14508 | |||
| f08254ffef | |||
| f28cade211 | |||
| f6ecc19c27 | |||
| 799271b975 | |||
|
|
b230ff3abd | ||
| 8a5e666f3d | |||
| 4babe3ee12 | |||
| 948bcc5484 | |||
|
|
b70693c2c1 | ||
| 3ca1f1407b | |||
| da0e80e9e0 | |||
| 763a7700ed | |||
| 6a6859fc20 | |||
| 76bffa7361 | |||
| cc85ff0ae5 | |||
| f3a6b1cbc5 | |||
| 1be029e292 | |||
| bebb96c2bd | |||
| 6eff5c8ac9 | |||
| f6ff719862 | |||
| 5a7c7d66a9 | |||
| 5dd59efe3d | |||
| 6b1a28222f | |||
| e210a90d79 | |||
| 8e90966871 | |||
| d6f3a86908 | |||
| bb6001ee27 | |||
| 707fd26a9b | |||
| df501a09fc | |||
| 9c21330088 | |||
| a5dc6c2532 | |||
| 51f985830a | |||
| 55f1a42cef | |||
| 6cd4369893 | |||
| 92faf54f20 | |||
| ccad448550 | |||
| c07ab32af0 | |||
| e4b18f92e9 | |||
| 3e436bd44f | |||
| 15542915d9 | |||
| d56f0bf587 | |||
| bdeca60db0 | |||
| 1c2a73ba54 | |||
| 1487aa54cb | |||
| 4bc779f475 | |||
| 8a50868f45 | |||
| 9b46f9bcc5 | |||
| 7cfbc3d9f8 | |||
| 318590e1c7 | |||
| 01151e66b4 | |||
| a6b8dec199 | |||
| 1488eee7eb | |||
| bd7fd0b793 |
28
.github/workflows/ci.yml
vendored
Normal file
28
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
name: CI
|
||||
on:
|
||||
push:
|
||||
branches: [ $default-branch ]
|
||||
pull_request:
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
ruby: ['2.5', '2.6', '2.7']
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Ruby ${{ matrix.ruby }}
|
||||
uses: ruby/setup-ruby@v1
|
||||
with:
|
||||
ruby-version: ${{ matrix.ruby }}
|
||||
bundler-cache: true
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v2-beta
|
||||
with:
|
||||
node-version: '14'
|
||||
- name: Install dependencies
|
||||
run: bundle install --without production
|
||||
- name: Run tests
|
||||
run: bundle exec rake
|
||||
env:
|
||||
RAILS_ENV: test
|
||||
12
.travis.yml
12
.travis.yml
@@ -1,12 +0,0 @@
|
||||
dist: trusty
|
||||
language: ruby
|
||||
cache: bundler
|
||||
rvm:
|
||||
- 2.6
|
||||
- 2.5
|
||||
- 2.4
|
||||
- 2.3
|
||||
script:
|
||||
- export RAILS_ENV=test
|
||||
- bundle exec rake db:schema:load
|
||||
- bundle exec rake test
|
||||
48
Gemfile
48
Gemfile
@@ -2,56 +2,58 @@ source 'https://rubygems.org'
|
||||
|
||||
|
||||
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
|
||||
gem 'rails', '5.2.2'
|
||||
gem 'rails', '6.1.3.2'
|
||||
# Use Puma as the app server
|
||||
gem 'puma', '~> 5.0'
|
||||
# Use sqlite3 as the database for Active Record
|
||||
gem 'sqlite3', '~> 1.3.13'
|
||||
gem 'sqlite3', '~> 1.4.1'
|
||||
|
||||
gem 'mysql2', '~> 0.5.2', group: :production
|
||||
|
||||
gem 'bootsnap', require: false
|
||||
|
||||
# Use SCSS for stylesheets
|
||||
gem 'sassc-rails', '~> 2.1.0'
|
||||
gem 'sassc-rails', '~> 2.1.2'
|
||||
# Use Uglifier as compressor for JavaScript assets
|
||||
gem 'uglifier', '~> 4.1.20'
|
||||
gem 'uglifier', '~> 4.2.0'
|
||||
# Use CoffeeScript for .js.coffee assets and views
|
||||
gem 'coffee-rails', '~> 4.2.2'
|
||||
gem 'coffee-rails', '~> 5.0.0'
|
||||
# See https://github.com/sstephenson/execjs#readme for more supported runtimes
|
||||
# gem 'therubyracer', platforms: :ruby
|
||||
|
||||
gem 'rails-i18n', '~> 5.1.3'
|
||||
gem 'rails-i18n', '~> 6.0.0'
|
||||
|
||||
gem 'rails-controller-testing'
|
||||
|
||||
gem 'webmock', group: :test
|
||||
|
||||
# Use jquery as the JavaScript library
|
||||
gem 'jquery-rails', '~> 4.3.3'
|
||||
gem 'jquery-rails', '~> 4.4.0'
|
||||
# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks
|
||||
gem 'turbolinks', '~> 5.2.0'
|
||||
gem 'turbolinks', '~> 5.2.1'
|
||||
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
|
||||
gem 'jbuilder', '~> 2.8.0'
|
||||
gem 'jbuilder', '~> 2.11.2'
|
||||
# bundle exec rake doc:rails generates the API under doc/api.
|
||||
gem 'sdoc', '~> 1.0.0', group: :doc
|
||||
gem 'sdoc', '~> 2.1.0', group: :doc
|
||||
|
||||
gem 'web-console', '~> 3.7.0', group: :development
|
||||
gem 'web-console', '~> 4.1.0', group: :development
|
||||
gem 'listen', '~> 3.5.1', group: :development
|
||||
|
||||
# Use Codacy for coverage
|
||||
gem 'codacy-coverage', :require => false
|
||||
gem 'bootstrap', '~> 4.5.3'
|
||||
gem "bootstrap_form", "~> 4.5"
|
||||
gem 'font-awesome-sass', '~> 5.15.1'
|
||||
|
||||
gem 'bootstrap-sass', '~> 3.4.0'
|
||||
gem 'autoprefixer-rails', '~> 9.4.6'
|
||||
# Use Diffy for showing translation diffs
|
||||
gem 'diffy'
|
||||
gem 'diff-lcs'
|
||||
|
||||
gem 'bh', '~> 1.3.6'
|
||||
gem 'will_paginate', '~> 3.3.0'
|
||||
gem 'will_paginate-bootstrap4', '~> 0.2.2'
|
||||
|
||||
gem 'will_paginate', '~> 3.1.6'
|
||||
gem 'will-paginate-i18n', '~> 0.1.15'
|
||||
gem 'will_paginate-bootstrap', '~> 1.0.1'
|
||||
|
||||
gem 'devise', '~> 4.5.0'
|
||||
gem 'devise-bootstrap-views', '~> 0.0.11'
|
||||
gem 'devise', '~> 4.8.0'
|
||||
gem 'omniauth'
|
||||
gem 'omniauth-github'
|
||||
|
||||
gem 'codemirror-rails', '~> 5.16.0'
|
||||
# Use ActiveModel has_secure_password
|
||||
# gem 'bcrypt', '~> 3.1.7'
|
||||
|
||||
|
||||
407
Gemfile.lock
407
Gemfile.lock
@@ -1,274 +1,297 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actioncable (5.2.2)
|
||||
actionpack (= 5.2.2)
|
||||
actioncable (6.1.3.2)
|
||||
actionpack (= 6.1.3.2)
|
||||
activesupport (= 6.1.3.2)
|
||||
nio4r (~> 2.0)
|
||||
websocket-driver (>= 0.6.1)
|
||||
actionmailer (5.2.2)
|
||||
actionpack (= 5.2.2)
|
||||
actionview (= 5.2.2)
|
||||
activejob (= 5.2.2)
|
||||
actionmailbox (6.1.3.2)
|
||||
actionpack (= 6.1.3.2)
|
||||
activejob (= 6.1.3.2)
|
||||
activerecord (= 6.1.3.2)
|
||||
activestorage (= 6.1.3.2)
|
||||
activesupport (= 6.1.3.2)
|
||||
mail (>= 2.7.1)
|
||||
actionmailer (6.1.3.2)
|
||||
actionpack (= 6.1.3.2)
|
||||
actionview (= 6.1.3.2)
|
||||
activejob (= 6.1.3.2)
|
||||
activesupport (= 6.1.3.2)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (5.2.2)
|
||||
actionview (= 5.2.2)
|
||||
activesupport (= 5.2.2)
|
||||
rack (~> 2.0)
|
||||
actionpack (6.1.3.2)
|
||||
actionview (= 6.1.3.2)
|
||||
activesupport (= 6.1.3.2)
|
||||
rack (~> 2.0, >= 2.0.9)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||
actionview (5.2.2)
|
||||
activesupport (= 5.2.2)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
||||
actiontext (6.1.3.2)
|
||||
actionpack (= 6.1.3.2)
|
||||
activerecord (= 6.1.3.2)
|
||||
activestorage (= 6.1.3.2)
|
||||
activesupport (= 6.1.3.2)
|
||||
nokogiri (>= 1.8.5)
|
||||
actionview (6.1.3.2)
|
||||
activesupport (= 6.1.3.2)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
||||
activejob (5.2.2)
|
||||
activesupport (= 5.2.2)
|
||||
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
||||
activejob (6.1.3.2)
|
||||
activesupport (= 6.1.3.2)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (5.2.2)
|
||||
activesupport (= 5.2.2)
|
||||
activerecord (5.2.2)
|
||||
activemodel (= 5.2.2)
|
||||
activesupport (= 5.2.2)
|
||||
arel (>= 9.0)
|
||||
activestorage (5.2.2)
|
||||
actionpack (= 5.2.2)
|
||||
activerecord (= 5.2.2)
|
||||
marcel (~> 0.3.1)
|
||||
activesupport (5.2.2)
|
||||
activemodel (6.1.3.2)
|
||||
activesupport (= 6.1.3.2)
|
||||
activerecord (6.1.3.2)
|
||||
activemodel (= 6.1.3.2)
|
||||
activesupport (= 6.1.3.2)
|
||||
activestorage (6.1.3.2)
|
||||
actionpack (= 6.1.3.2)
|
||||
activejob (= 6.1.3.2)
|
||||
activerecord (= 6.1.3.2)
|
||||
activesupport (= 6.1.3.2)
|
||||
marcel (~> 1.0.0)
|
||||
mini_mime (~> 1.0.2)
|
||||
activesupport (6.1.3.2)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (>= 0.7, < 2)
|
||||
minitest (~> 5.1)
|
||||
tzinfo (~> 1.1)
|
||||
arel (9.0.0)
|
||||
autoprefixer-rails (9.4.6)
|
||||
i18n (>= 1.6, < 2)
|
||||
minitest (>= 5.1)
|
||||
tzinfo (~> 2.0)
|
||||
zeitwerk (~> 2.3)
|
||||
addressable (2.7.0)
|
||||
public_suffix (>= 2.0.2, < 5.0)
|
||||
autoprefixer-rails (10.0.2.0)
|
||||
execjs
|
||||
bcrypt (3.1.12)
|
||||
bcrypt (3.1.12-x64-mingw32)
|
||||
bcrypt (3.1.12-x86-mingw32)
|
||||
bh (1.3.6)
|
||||
actionpack
|
||||
activesupport
|
||||
bindex (0.5.0)
|
||||
bootsnap (1.3.2)
|
||||
bcrypt (3.1.16)
|
||||
bindex (0.8.1)
|
||||
bootsnap (1.7.5)
|
||||
msgpack (~> 1.0)
|
||||
bootstrap-sass (3.4.0)
|
||||
autoprefixer-rails (>= 5.2.1)
|
||||
sassc (>= 2.0.0)
|
||||
builder (3.2.3)
|
||||
codacy-coverage (2.1.0)
|
||||
simplecov
|
||||
codemirror-rails (5.16.0)
|
||||
railties (>= 3.0, < 6.0)
|
||||
coffee-rails (4.2.2)
|
||||
bootstrap (4.5.3)
|
||||
autoprefixer-rails (>= 9.1.0)
|
||||
popper_js (>= 1.14.3, < 2)
|
||||
sassc-rails (>= 2.0.0)
|
||||
bootstrap_form (4.5.0)
|
||||
actionpack (>= 5.2)
|
||||
activemodel (>= 5.2)
|
||||
builder (3.2.4)
|
||||
coffee-rails (5.0.0)
|
||||
coffee-script (>= 2.2.0)
|
||||
railties (>= 4.0.0)
|
||||
railties (>= 5.2.0)
|
||||
coffee-script (2.4.1)
|
||||
coffee-script-source
|
||||
execjs
|
||||
coffee-script-source (1.12.2)
|
||||
concurrent-ruby (1.1.4)
|
||||
crass (1.0.4)
|
||||
devise (4.5.0)
|
||||
concurrent-ruby (1.1.8)
|
||||
crack (0.4.5)
|
||||
rexml
|
||||
crass (1.0.6)
|
||||
devise (4.8.0)
|
||||
bcrypt (~> 3.0)
|
||||
orm_adapter (~> 0.1)
|
||||
railties (>= 4.1.0, < 6.0)
|
||||
railties (>= 4.1.0)
|
||||
responders
|
||||
warden (~> 1.2.3)
|
||||
devise-bootstrap-views (0.0.11)
|
||||
docile (1.3.1)
|
||||
erubi (1.8.0)
|
||||
diff-lcs (1.4.4)
|
||||
diffy (3.4.0)
|
||||
erubi (1.10.0)
|
||||
execjs (2.7.0)
|
||||
faraday (0.15.4)
|
||||
faraday (1.4.1)
|
||||
faraday-excon (~> 1.1)
|
||||
faraday-net_http (~> 1.0)
|
||||
faraday-net_http_persistent (~> 1.1)
|
||||
multipart-post (>= 1.2, < 3)
|
||||
ffi (1.9.25)
|
||||
ffi (1.9.25-x64-mingw32)
|
||||
ffi (1.9.25-x86-mingw32)
|
||||
ruby2_keywords (>= 0.0.4)
|
||||
faraday-excon (1.1.0)
|
||||
faraday-net_http (1.0.1)
|
||||
faraday-net_http_persistent (1.1.0)
|
||||
ffi (1.15.0)
|
||||
font-awesome-sass (5.15.1)
|
||||
sassc (>= 1.11)
|
||||
globalid (0.4.2)
|
||||
activesupport (>= 4.2.0)
|
||||
hashie (3.6.0)
|
||||
i18n (0.9.5)
|
||||
hashdiff (1.0.1)
|
||||
hashie (4.1.0)
|
||||
i18n (1.8.10)
|
||||
concurrent-ruby (~> 1.0)
|
||||
jbuilder (2.8.0)
|
||||
activesupport (>= 4.2.0)
|
||||
multi_json (>= 1.2)
|
||||
jquery-rails (4.3.3)
|
||||
jbuilder (2.11.2)
|
||||
activesupport (>= 5.0.0)
|
||||
jquery-rails (4.4.0)
|
||||
rails-dom-testing (>= 1, < 3)
|
||||
railties (>= 4.2.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
json (2.1.0)
|
||||
jwt (2.1.0)
|
||||
loofah (2.2.3)
|
||||
jwt (2.2.3)
|
||||
listen (3.5.1)
|
||||
rb-fsevent (~> 0.10, >= 0.10.3)
|
||||
rb-inotify (~> 0.9, >= 0.9.10)
|
||||
loofah (2.9.1)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.5.9)
|
||||
mail (2.7.1)
|
||||
mini_mime (>= 0.1.1)
|
||||
marcel (0.3.3)
|
||||
mimemagic (~> 0.3.2)
|
||||
method_source (0.9.2)
|
||||
mimemagic (0.3.3)
|
||||
mini_mime (1.0.1)
|
||||
mini_portile2 (2.4.0)
|
||||
minitest (5.11.3)
|
||||
msgpack (1.2.6)
|
||||
msgpack (1.2.6-x64-mingw32)
|
||||
msgpack (1.2.6-x86-mingw32)
|
||||
multi_json (1.13.1)
|
||||
marcel (1.0.1)
|
||||
method_source (1.0.0)
|
||||
mini_mime (1.0.3)
|
||||
mini_portile2 (2.5.1)
|
||||
minitest (5.14.4)
|
||||
msgpack (1.4.2)
|
||||
multi_json (1.15.0)
|
||||
multi_xml (0.6.0)
|
||||
multipart-post (2.0.0)
|
||||
mysql2 (0.5.2)
|
||||
mysql2 (0.5.2-x64-mingw32)
|
||||
mysql2 (0.5.2-x86-mingw32)
|
||||
nio4r (2.3.1)
|
||||
nokogiri (1.10.1)
|
||||
mini_portile2 (~> 2.4.0)
|
||||
nokogiri (1.10.1-x64-mingw32)
|
||||
mini_portile2 (~> 2.4.0)
|
||||
nokogiri (1.10.1-x86-mingw32)
|
||||
mini_portile2 (~> 2.4.0)
|
||||
oauth2 (1.4.1)
|
||||
faraday (>= 0.8, < 0.16.0)
|
||||
multipart-post (2.1.1)
|
||||
mysql2 (0.5.3)
|
||||
nio4r (2.5.7)
|
||||
nokogiri (1.11.4)
|
||||
mini_portile2 (~> 2.5.0)
|
||||
racc (~> 1.4)
|
||||
oauth2 (1.4.7)
|
||||
faraday (>= 0.8, < 2.0)
|
||||
jwt (>= 1.0, < 3.0)
|
||||
multi_json (~> 1.3)
|
||||
multi_xml (~> 0.5)
|
||||
rack (>= 1.2, < 3)
|
||||
omniauth (1.9.0)
|
||||
hashie (>= 3.4.6, < 3.7.0)
|
||||
omniauth (2.0.4)
|
||||
hashie (>= 3.4.6)
|
||||
rack (>= 1.6.2, < 3)
|
||||
omniauth-github (1.3.0)
|
||||
omniauth (~> 1.5)
|
||||
omniauth-oauth2 (>= 1.4.0, < 2.0)
|
||||
omniauth-oauth2 (1.6.0)
|
||||
oauth2 (~> 1.1)
|
||||
omniauth (~> 1.9)
|
||||
rack-protection
|
||||
omniauth-github (2.0.0)
|
||||
omniauth (~> 2.0)
|
||||
omniauth-oauth2 (~> 1.7.1)
|
||||
omniauth-oauth2 (1.7.1)
|
||||
oauth2 (~> 1.4)
|
||||
omniauth (>= 1.9, < 3)
|
||||
orm_adapter (0.5.0)
|
||||
rack (2.0.6)
|
||||
popper_js (1.16.0)
|
||||
public_suffix (4.0.6)
|
||||
puma (5.3.1)
|
||||
nio4r (~> 2.0)
|
||||
racc (1.5.2)
|
||||
rack (2.2.3)
|
||||
rack-protection (2.1.0)
|
||||
rack
|
||||
rack-test (1.1.0)
|
||||
rack (>= 1.0, < 3)
|
||||
rails (5.2.2)
|
||||
actioncable (= 5.2.2)
|
||||
actionmailer (= 5.2.2)
|
||||
actionpack (= 5.2.2)
|
||||
actionview (= 5.2.2)
|
||||
activejob (= 5.2.2)
|
||||
activemodel (= 5.2.2)
|
||||
activerecord (= 5.2.2)
|
||||
activestorage (= 5.2.2)
|
||||
activesupport (= 5.2.2)
|
||||
bundler (>= 1.3.0)
|
||||
railties (= 5.2.2)
|
||||
rails (6.1.3.2)
|
||||
actioncable (= 6.1.3.2)
|
||||
actionmailbox (= 6.1.3.2)
|
||||
actionmailer (= 6.1.3.2)
|
||||
actionpack (= 6.1.3.2)
|
||||
actiontext (= 6.1.3.2)
|
||||
actionview (= 6.1.3.2)
|
||||
activejob (= 6.1.3.2)
|
||||
activemodel (= 6.1.3.2)
|
||||
activerecord (= 6.1.3.2)
|
||||
activestorage (= 6.1.3.2)
|
||||
activesupport (= 6.1.3.2)
|
||||
bundler (>= 1.15.0)
|
||||
railties (= 6.1.3.2)
|
||||
sprockets-rails (>= 2.0.0)
|
||||
rails-controller-testing (1.0.4)
|
||||
actionpack (>= 5.0.1.x)
|
||||
actionview (>= 5.0.1.x)
|
||||
activesupport (>= 5.0.1.x)
|
||||
rails-controller-testing (1.0.5)
|
||||
actionpack (>= 5.0.1.rc1)
|
||||
actionview (>= 5.0.1.rc1)
|
||||
activesupport (>= 5.0.1.rc1)
|
||||
rails-dom-testing (2.0.3)
|
||||
activesupport (>= 4.2.0)
|
||||
nokogiri (>= 1.6)
|
||||
rails-html-sanitizer (1.0.4)
|
||||
loofah (~> 2.2, >= 2.2.2)
|
||||
rails-i18n (5.1.3)
|
||||
rails-html-sanitizer (1.3.0)
|
||||
loofah (~> 2.3)
|
||||
rails-i18n (6.0.0)
|
||||
i18n (>= 0.7, < 2)
|
||||
railties (>= 5.0, < 6)
|
||||
railties (5.2.2)
|
||||
actionpack (= 5.2.2)
|
||||
activesupport (= 5.2.2)
|
||||
railties (>= 6.0.0, < 7)
|
||||
railties (6.1.3.2)
|
||||
actionpack (= 6.1.3.2)
|
||||
activesupport (= 6.1.3.2)
|
||||
method_source
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.19.0, < 2.0)
|
||||
rake (12.3.2)
|
||||
rdoc (6.1.1)
|
||||
responders (2.4.1)
|
||||
actionpack (>= 4.2.0, < 6.0)
|
||||
railties (>= 4.2.0, < 6.0)
|
||||
sassc (2.0.0)
|
||||
ffi (~> 1.9.6)
|
||||
rake
|
||||
sassc-rails (2.1.0)
|
||||
thor (~> 1.0)
|
||||
rake (13.0.3)
|
||||
rb-fsevent (0.10.4)
|
||||
rb-inotify (0.10.1)
|
||||
ffi (~> 1.0)
|
||||
rdoc (6.3.1)
|
||||
responders (3.0.1)
|
||||
actionpack (>= 5.0)
|
||||
railties (>= 5.0)
|
||||
rexml (3.2.5)
|
||||
ruby2_keywords (0.0.4)
|
||||
sassc (2.4.0)
|
||||
ffi (~> 1.9)
|
||||
sassc-rails (2.1.2)
|
||||
railties (>= 4.0.0)
|
||||
sassc (>= 2.0)
|
||||
sprockets (> 3.0)
|
||||
sprockets-rails
|
||||
tilt
|
||||
sdoc (1.0.0)
|
||||
sdoc (2.1.0)
|
||||
rdoc (>= 5.0)
|
||||
simplecov (0.16.1)
|
||||
docile (~> 1.1)
|
||||
json (>= 1.8, < 3)
|
||||
simplecov-html (~> 0.10.0)
|
||||
simplecov-html (0.10.2)
|
||||
sprockets (3.7.2)
|
||||
sprockets (4.0.2)
|
||||
concurrent-ruby (~> 1.0)
|
||||
rack (> 1, < 3)
|
||||
sprockets-rails (3.2.1)
|
||||
sprockets-rails (3.2.2)
|
||||
actionpack (>= 4.0)
|
||||
activesupport (>= 4.0)
|
||||
sprockets (>= 3.0.0)
|
||||
sqlite3 (1.3.13)
|
||||
sqlite3 (1.3.13-x64-mingw32)
|
||||
sqlite3 (1.3.13-x86-mingw32)
|
||||
thor (0.19.4)
|
||||
thread_safe (0.3.6)
|
||||
tilt (2.0.9)
|
||||
turbolinks (5.2.0)
|
||||
sqlite3 (1.4.2)
|
||||
thor (1.1.0)
|
||||
tilt (2.0.10)
|
||||
turbolinks (5.2.1)
|
||||
turbolinks-source (~> 5.2)
|
||||
turbolinks-source (5.2.0)
|
||||
tzinfo (1.2.5)
|
||||
thread_safe (~> 0.1)
|
||||
tzinfo-data (1.2018.9)
|
||||
tzinfo (>= 1.0.0)
|
||||
uglifier (4.1.20)
|
||||
tzinfo (2.0.4)
|
||||
concurrent-ruby (~> 1.0)
|
||||
uglifier (4.2.0)
|
||||
execjs (>= 0.3.0, < 3)
|
||||
warden (1.2.8)
|
||||
rack (>= 2.0.6)
|
||||
web-console (3.7.0)
|
||||
actionview (>= 5.0)
|
||||
activemodel (>= 5.0)
|
||||
warden (1.2.9)
|
||||
rack (>= 2.0.9)
|
||||
web-console (4.1.0)
|
||||
actionview (>= 6.0.0)
|
||||
activemodel (>= 6.0.0)
|
||||
bindex (>= 0.4.0)
|
||||
railties (>= 5.0)
|
||||
websocket-driver (0.7.0)
|
||||
railties (>= 6.0.0)
|
||||
webmock (3.13.0)
|
||||
addressable (>= 2.3.6)
|
||||
crack (>= 0.3.2)
|
||||
hashdiff (>= 0.4.0, < 2.0.0)
|
||||
websocket-driver (0.7.3)
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.3)
|
||||
will-paginate-i18n (0.1.15)
|
||||
will_paginate (3.1.6)
|
||||
will_paginate-bootstrap (1.0.1)
|
||||
will_paginate (>= 3.0.3)
|
||||
websocket-extensions (0.1.5)
|
||||
will_paginate (3.3.0)
|
||||
will_paginate-bootstrap4 (0.2.2)
|
||||
will_paginate (~> 3.0, >= 3.0.0)
|
||||
zeitwerk (2.4.2)
|
||||
|
||||
PLATFORMS
|
||||
ruby
|
||||
x64-mingw32
|
||||
x86-mingw32
|
||||
|
||||
DEPENDENCIES
|
||||
autoprefixer-rails (~> 9.4.6)
|
||||
bh (~> 1.3.6)
|
||||
bootsnap
|
||||
bootstrap-sass (~> 3.4.0)
|
||||
codacy-coverage
|
||||
codemirror-rails (~> 5.16.0)
|
||||
coffee-rails (~> 4.2.2)
|
||||
devise (~> 4.5.0)
|
||||
devise-bootstrap-views (~> 0.0.11)
|
||||
jbuilder (~> 2.8.0)
|
||||
jquery-rails (~> 4.3.3)
|
||||
bootstrap (~> 4.5.3)
|
||||
bootstrap_form (~> 4.5)
|
||||
coffee-rails (~> 5.0.0)
|
||||
devise (~> 4.8.0)
|
||||
diff-lcs
|
||||
diffy
|
||||
font-awesome-sass (~> 5.15.1)
|
||||
jbuilder (~> 2.11.2)
|
||||
jquery-rails (~> 4.4.0)
|
||||
listen (~> 3.5.1)
|
||||
mysql2 (~> 0.5.2)
|
||||
omniauth
|
||||
omniauth-github
|
||||
rails (= 5.2.2)
|
||||
puma (~> 5.0)
|
||||
rails (= 6.1.3.2)
|
||||
rails-controller-testing
|
||||
rails-i18n (~> 5.1.3)
|
||||
sassc-rails (~> 2.1.0)
|
||||
sdoc (~> 1.0.0)
|
||||
sqlite3 (~> 1.3.13)
|
||||
turbolinks (~> 5.2.0)
|
||||
rails-i18n (~> 6.0.0)
|
||||
sassc-rails (~> 2.1.2)
|
||||
sdoc (~> 2.1.0)
|
||||
sqlite3 (~> 1.4.1)
|
||||
turbolinks (~> 5.2.1)
|
||||
tzinfo-data
|
||||
uglifier (~> 4.1.20)
|
||||
web-console (~> 3.7.0)
|
||||
will-paginate-i18n (~> 0.1.15)
|
||||
will_paginate (~> 3.1.6)
|
||||
will_paginate-bootstrap (~> 1.0.1)
|
||||
uglifier (~> 4.2.0)
|
||||
web-console (~> 4.1.0)
|
||||
webmock
|
||||
will_paginate (~> 3.3.0)
|
||||
will_paginate-bootstrap4 (~> 0.2.2)
|
||||
|
||||
BUNDLED WITH
|
||||
1.17.2
|
||||
2.2.17
|
||||
|
||||
59
README.md
59
README.md
@@ -1,9 +1,60 @@
|
||||
Projekt Euler
|
||||
=============
|
||||
|
||||
[](https://travis-ci.org/projekteuler/projekteuler)
|
||||
[](https://www.codacy.com/app/projekteuler/projekteuler)
|
||||

|
||||
|
||||
This is a rails app for the german translation of [projecteuler.net](https://projecteuler.net).
|
||||
This is the Ruby on Rails web-app powering [projekteuler.de](https://projekteuler.de).
|
||||
It allows visitors to view and suggest German translations of the maths puzzles found on [projecteuler.net](https://projecteuler.net).
|
||||
|
||||
Currently, it doesn't have a website, but when most of the current issues are fixed, it will be uploaded.
|
||||
## Getting started
|
||||
1. Download this repository, e.g., with `git clone`.
|
||||
2. Make sure [NodeJS](https://nodejs.org) is installed.
|
||||
3. Navigate inside the repository folder.
|
||||
4. Use bundler to install all required gems.
|
||||
|
||||
$ bundle install
|
||||
|
||||
5. Setup the database:
|
||||
|
||||
$ bin/rails db:setup
|
||||
|
||||
6. Start the server:
|
||||
|
||||
$ bin/rails server
|
||||
|
||||
7. Open `http://localhost:3000` in your browser. You should see the web app up and running!
|
||||
8. Start playing with the web app. By default, the database will contain some users and translations. The user `admin` can review translations, while the user `translator` has submitted some translations.
|
||||
You can login as any user by clicking the Login button and entering their name. This is simply a mock OAuth login; in production mode, GitHub is used for login.
|
||||
9. If you ever want to reset the database to the original state, run `rails db:reset`.
|
||||
|
||||
## Contributing
|
||||
Do you have a suggestion for an improvement for the web app? Please create an [issue](https://github.com/projekteuler/projekteuler/issues) for it.
|
||||
|
||||
Do you want to implement this improvement yourself? Follow these steps:
|
||||
1. Fork this repository on GitHub.
|
||||
2. Create a new branch for your improvement
|
||||
3. Implement your improvement, and create tests for it if applicable.
|
||||
4. Submit a pull request.
|
||||
|
||||
## Deployment
|
||||
|
||||
1. Make sure [NodeJS](https://nodejs.org) is installed.
|
||||
2. Set up a MySQL database.
|
||||
3. Copy all files from this repository.
|
||||
4. Make sure the Rails environment is set to `production`.
|
||||
5. Set up the following environment variables:
|
||||
* `RAILS_MASTER_KEY`: The master key for decryption of the Rails credentials
|
||||
* `DATABASE_NAME`: The name of the MySQL database
|
||||
* `DATABASE_USERNAME`: The username for the MYSQL database
|
||||
* `DATABASE_PASSWORD`: The password for the MYSQL database
|
||||
* `GITHUB_CLIENT_ID`: The client ID of your GitHub OAuth App (that you will need to create)
|
||||
* `GITHUB_CLIENT_SECRET`: The client secret of the GitHub OAuth App
|
||||
6. Install all required gems with `bundle install`.
|
||||
7. Load the database schema with `bin/rails db:schema:load`.
|
||||
8. Precompile all assets with `bin/rails assets:precompile`.
|
||||
9. Start the server, e.g., with `bin/rails server`.
|
||||
|
||||
Anytime you update the files, you should run `bin/rails db:migrate`, `bin/rails log:clear tmp:clear`, `bin/rails assets:precompile` and then restart the server.
|
||||
|
||||
## License
|
||||
This project is released under the [MIT License](https://opensource.org/licenses/MIT).
|
||||
|
||||
4
app/assets/config/manifest.js
Normal file
4
app/assets/config/manifest.js
Normal file
@@ -0,0 +1,4 @@
|
||||
//= link_tree ../images
|
||||
//= link application.css
|
||||
//= link application.js
|
||||
//
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.9 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 49 KiB |
@@ -10,15 +10,16 @@
|
||||
// Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details
|
||||
// about supported directives.
|
||||
//
|
||||
//= require jquery
|
||||
//= require jquery_ujs
|
||||
//= require turbolinks
|
||||
//= require jquery3
|
||||
//= require rails-ujs
|
||||
//= require popper
|
||||
//= require bootstrap-sprockets
|
||||
//= require codemirror
|
||||
//= require codemirror/addons/display/placeholder
|
||||
//= require codemirror/modes/xml
|
||||
//= require codemirror/modes/css
|
||||
//= require codemirror/modes/javascript
|
||||
//= require codemirror/modes/htmlmixed
|
||||
//= require translations
|
||||
//= require_tree .
|
||||
//= require codemirror/codemirror
|
||||
//= require codemirror/addon/display/placeholder
|
||||
//= require codemirror/mode/xml
|
||||
//= require codemirror/mode/css
|
||||
//= require codemirror/mode/javascript
|
||||
//= require codemirror/mode/htmlmixed
|
||||
//= require mathjax-config
|
||||
//= require init
|
||||
@@ -1,6 +1,4 @@
|
||||
# Place all the behaviors and hooks related to the matching controller here.
|
||||
# All this logic will automatically be available in application.js.
|
||||
# You can use CoffeeScript in this file: http://coffeescript.org/
|
||||
window.App ||= {}
|
||||
|
||||
cm = null
|
||||
|
||||
@@ -13,9 +11,13 @@ loadCodeMirror = ->
|
||||
mode: "text/html"
|
||||
})
|
||||
|
||||
$(document).on "turbolinks:load", loadCodeMirror
|
||||
App.init = ->
|
||||
MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
|
||||
loadCodeMirror();
|
||||
|
||||
$(document).on "turbolinks:load", ->
|
||||
App.init()
|
||||
|
||||
$(document).on "click", '#translationNav a[href="#preview"]', ->
|
||||
$(document).on "click", '#translationTab a[href="#preview"]', ->
|
||||
$('#preview .problem-content').html(cm.getValue())
|
||||
MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
|
||||
10
app/assets/javascripts/mathjax-config.js
Normal file
10
app/assets/javascripts/mathjax-config.js
Normal file
@@ -0,0 +1,10 @@
|
||||
window.MathJax = {
|
||||
jax: ["input/TeX", "output/HTML-CSS"],
|
||||
tex2jax: {
|
||||
inlineMath: [ ["$","$"], ["\\(","\\)"] ],
|
||||
displayMath: [ ["$$","$$"], ["\\[","\\]"] ],
|
||||
processEscapes: true,
|
||||
ignoreClass: "mathjax_ignore|diff"
|
||||
},
|
||||
"HTML-CSS": { availableFonts: ["TeX"] }
|
||||
};
|
||||
@@ -1,2 +0,0 @@
|
||||
$(document).on 'turbolinks:load', ->
|
||||
MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
|
||||
@@ -10,17 +10,18 @@
|
||||
* defined in the other CSS/SCSS files in this directory. It is generally better to create a new
|
||||
* file per style scope.
|
||||
*
|
||||
*= require_tree .
|
||||
*= require devise_bootstrap_views
|
||||
*= require codemirror
|
||||
*= require codemirror/codemirror
|
||||
*= require_self
|
||||
*= require_tree .
|
||||
*/
|
||||
|
||||
@import "bootstrap-sprockets";
|
||||
@import "bootstrap";
|
||||
@import "rails_bootstrap_forms";
|
||||
@import "font-awesome-sprockets";
|
||||
@import "font-awesome";
|
||||
|
||||
.turbolinks-progress-bar {
|
||||
background-color: $brand-primary;
|
||||
background-color: $primary;
|
||||
}
|
||||
/* Sticky footer styles
|
||||
-------------------------------------------------- */
|
||||
@@ -38,111 +39,101 @@ body {
|
||||
width: 100%;
|
||||
/* Set the fixed height of the footer here */
|
||||
height: 40px;
|
||||
background-color: #f5f5f5;
|
||||
line-height: 40px;
|
||||
background-color: $light;
|
||||
}
|
||||
|
||||
.container .text-muted {
|
||||
margin: 10px 0;
|
||||
/* Custom style for navbar toggler due to CSP problems */
|
||||
.navbar-toggler-icon {
|
||||
background-image: none !important;
|
||||
}
|
||||
|
||||
/* Style for buttons in the corner of problems/translations */
|
||||
.problem-buttons {
|
||||
position: relative;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.problem-buttons .problem-buttons-inner {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* Style for links to previous and next problem */
|
||||
.problem-prev {
|
||||
float: left;
|
||||
margin: 20px -30px 0;
|
||||
}
|
||||
|
||||
.problem-next {
|
||||
float: right;
|
||||
margin: 20px -30px 0;
|
||||
}
|
||||
|
||||
/* Style for translation diffs */
|
||||
|
||||
.diff {
|
||||
overflow:auto;
|
||||
margin-bottom: 20px;
|
||||
ul{background:#fff;overflow:auto;font-size:13px;list-style:none;margin:0;padding:0;display:table;width:100%;}
|
||||
del, ins{display:block;text-decoration:none;}
|
||||
li {
|
||||
padding:0;
|
||||
display:table-row;
|
||||
margin: 0;
|
||||
height:1em;
|
||||
ins {
|
||||
background:#dfd;
|
||||
color:#080;
|
||||
strong{font-weight:normal;background:#9f9;}
|
||||
}
|
||||
del {
|
||||
background:#fee;
|
||||
color:#b00;
|
||||
strong{font-weight:normal;background:#fcc;}
|
||||
}
|
||||
/* try 'whitespace:pre;' if you don't want lines to wrap */
|
||||
del, ins, span {white-space:pre-wrap;font-family:courier;}
|
||||
}
|
||||
}
|
||||
|
||||
/* Custom css for fixing styles within problem content */
|
||||
|
||||
.problem-content blockquote {
|
||||
margin: 0px;
|
||||
font-size: 14px;
|
||||
border-left: medium none;
|
||||
}
|
||||
.problem-content {
|
||||
blockquote {
|
||||
margin: 0px;
|
||||
font-size: 14px;
|
||||
border-left: medium none;
|
||||
}
|
||||
|
||||
.problem-content div {
|
||||
margin: 0px 0px 10px;
|
||||
}
|
||||
div {
|
||||
margin: 0px 0px 10px;
|
||||
}
|
||||
|
||||
.problem-content table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 2px;
|
||||
}
|
||||
table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 2px;
|
||||
}
|
||||
|
||||
.problem-content td {
|
||||
padding: 1px;
|
||||
}
|
||||
td {
|
||||
padding: 1px;
|
||||
}
|
||||
|
||||
/* Side notes for calling out things
|
||||
-------------------------------------------------- */
|
||||
// Make info/note boxes styled like bootstrap info alerts
|
||||
> .info, > .note {
|
||||
@extend .alert;
|
||||
@extend .alert-info;
|
||||
margin-bottom: 0px;
|
||||
> a {
|
||||
@extend .alert-link;
|
||||
}
|
||||
}
|
||||
|
||||
/* Base styles (regardless of theme) */
|
||||
.bs-callout, .problem-content > .info:first-child, .problem-content > .info:last-child{
|
||||
margin: 20px 0;
|
||||
padding: 15px 30px 15px 15px;
|
||||
border-left: 5px solid #eee;
|
||||
dfn {
|
||||
border-bottom: 1px dotted;
|
||||
font-style: normal;
|
||||
cursor: help;
|
||||
}
|
||||
}
|
||||
.bs-callout h1,
|
||||
.bs-callout h2,
|
||||
.bs-callout h3,
|
||||
.bs-callout h4,
|
||||
.bs-callout h5,
|
||||
.bs-callout h6 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.bs-callout-danger h1,
|
||||
.bs-callout-danger h2,
|
||||
.bs-callout-danger h3,
|
||||
.bs-callout-danger h4,
|
||||
.bs-callout-danger h5,
|
||||
.bs-callout-danger h6 {
|
||||
color: #B94A48;
|
||||
}
|
||||
|
||||
.bs-callout-warning h1,
|
||||
.bs-callout-warning h2,
|
||||
.bs-callout-warning h3,
|
||||
.bs-callout-warning h4,
|
||||
.bs-callout-warning h5,
|
||||
.bs-callout-warning h6 {
|
||||
color: #C09853;
|
||||
}
|
||||
|
||||
.bs-callout-info h1,
|
||||
.bs-callout-info h2,
|
||||
.bs-callout-info h3,
|
||||
.bs-callout-info h4,
|
||||
.bs-callout-info h5,
|
||||
.bs-callout-info h6 {
|
||||
color: #3A87AD;
|
||||
}
|
||||
|
||||
.bs-callout-success h1,
|
||||
.bs-callout-success h2,
|
||||
.bs-callout-success h3,
|
||||
.bs-callout-success h4,
|
||||
.bs-callout-success h5,
|
||||
.bs-callout-success h6 {
|
||||
color: #3C763D;
|
||||
}
|
||||
|
||||
.bs-callout p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.bs-callout code,
|
||||
.bs-callout .highlight {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
/* Themes for different contexts */
|
||||
.bs-callout-danger {
|
||||
background-color: #fcf2f2;
|
||||
border-color: #dFb5b4;
|
||||
}
|
||||
.bs-callout-warning {
|
||||
background-color: #fefbed;
|
||||
border-color: #f1e7bc;
|
||||
}
|
||||
.bs-callout-info, .problem-content > .info:first-child, .problem-content > .info:last-child {
|
||||
background-color: #f0f7fd;
|
||||
border-color: #d0e3f0;
|
||||
}
|
||||
.bs-callout-success {
|
||||
background-color: #dff0d8;
|
||||
border-color: #d6e9c6;
|
||||
}
|
||||
@@ -1,16 +1,11 @@
|
||||
class Admin::DashboardController < AdminController
|
||||
def index
|
||||
@current_problem_count = Problem.count
|
||||
@most_recent_pull = Problem.maximum(:pulled_at)
|
||||
end
|
||||
|
||||
def update_problem_count
|
||||
begin
|
||||
new_problem_count = params[:problem_count].to_i
|
||||
raise t('no_problem_count') unless new_problem_count
|
||||
Problem.update_count(new_problem_count)
|
||||
redirect_to({:controller => 'admin/dashboard', :action => :index}, notice: t('.success_message'))
|
||||
rescue => e
|
||||
redirect_to({:controller => 'admin/dashboard', :action => :index}, alert: t('.failure_message', error: e.message))
|
||||
end
|
||||
def pull_problems
|
||||
PullProblemsJob.perform_later
|
||||
redirect_to({:controller => 'admin/dashboard', :action => :index}, notice: t('.pull_problems_initiated'))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
class AdminController < ApplicationController
|
||||
before_action :authenticate!
|
||||
|
||||
def authenticate!
|
||||
authenticate_user!
|
||||
throw(:warden) unless current_user.admin?
|
||||
end
|
||||
before_action :authenticate_admin!
|
||||
end
|
||||
@@ -2,4 +2,9 @@ class ApplicationController < ActionController::Base
|
||||
# Prevent CSRF attacks by raising an exception.
|
||||
# For APIs, you may want to use :null_session instead.
|
||||
protect_from_forgery with: :exception
|
||||
|
||||
def authenticate_admin!
|
||||
authenticate_user!
|
||||
throw(:warden) unless current_user.admin?
|
||||
end
|
||||
end
|
||||
|
||||
@@ -7,9 +7,6 @@ class ProblemsController < ApplicationController
|
||||
end
|
||||
|
||||
def show
|
||||
unless @problem.is_translated?
|
||||
render action: "untranslated"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
class TranslationsController < ApplicationController
|
||||
before_action :set_problem, only: [:new, :create]
|
||||
before_action :set_accept, only: [:create]
|
||||
|
||||
# GET /translations/new
|
||||
def new
|
||||
@@ -7,6 +8,9 @@ class TranslationsController < ApplicationController
|
||||
if @problem.is_translated?
|
||||
@translation.title = @problem.translation.title
|
||||
@translation.content = @problem.translation.content
|
||||
else
|
||||
@translation.title = @problem.original_title
|
||||
@translation.content = @problem.original_content
|
||||
end
|
||||
end
|
||||
|
||||
@@ -17,7 +21,12 @@ class TranslationsController < ApplicationController
|
||||
@translation.author = current_user
|
||||
end
|
||||
if @translation.save
|
||||
redirect_to @problem, notice: t('translations.notice.successfully_created')
|
||||
if @accept
|
||||
@problem.set_translation(@translation)
|
||||
redirect_to @problem, notice: t('translations.notice.successfully_created_and_accepted')
|
||||
else
|
||||
redirect_to @problem, notice: t('translations.notice.successfully_created')
|
||||
end
|
||||
else
|
||||
render :new
|
||||
end
|
||||
@@ -32,4 +41,12 @@ class TranslationsController < ApplicationController
|
||||
def set_problem
|
||||
@problem = Problem.find(params[:problem_id])
|
||||
end
|
||||
|
||||
def set_accept
|
||||
if user_signed_in? and current_user.admin?
|
||||
@accept = params.fetch(:accept, false)
|
||||
else
|
||||
@accept = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,6 +4,7 @@ class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
|
||||
def sign_in_with(provider_name)
|
||||
@user = User.from_omniauth(request.env["omniauth.auth"])
|
||||
@user.remember_me!
|
||||
@user.remember_me = true
|
||||
sign_in_and_redirect @user, event: :authentication
|
||||
set_flash_message(:notice, :success, kind: provider_name) if is_navigational_format?
|
||||
end
|
||||
|
||||
@@ -27,4 +27,12 @@ module ApplicationHelper
|
||||
page_title + ' - ' + base_title
|
||||
end
|
||||
end
|
||||
|
||||
def nav_link_to(body, url)
|
||||
nav_options = { class: current_page?(url) ? 'nav-item active': 'nav-item' }
|
||||
content_tag(:li, nav_options) do
|
||||
link_to body, url, class: 'nav-link'
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
21
app/jobs/pull_problem_content_job.rb
Normal file
21
app/jobs/pull_problem_content_job.rb
Normal file
@@ -0,0 +1,21 @@
|
||||
require 'open-uri'
|
||||
|
||||
class PullProblemContentJob < ApplicationJob
|
||||
queue_as :default
|
||||
|
||||
def perform(problem)
|
||||
html = URI.open("https://projecteuler.net/minimal=#{problem.id}").read
|
||||
html.strip!
|
||||
|
||||
# Linked problems
|
||||
html.gsub!('<a href="problem=', '<a href="/problem=')
|
||||
# Linked about pages
|
||||
html.gsub!('<a href="about=', '<a href="/about=')
|
||||
# Linked txt resources
|
||||
html.gsub!('<a href="project/resources/', '<a href="https://projecteuler.net/project/resources/')
|
||||
# Included images
|
||||
html.gsub!('<img src="project/images/', '<img src="https://projecteuler.net/project/images/')
|
||||
|
||||
problem.update(original_content: html, pulled_at: Time.current())
|
||||
end
|
||||
end
|
||||
23
app/jobs/pull_problems_job.rb
Normal file
23
app/jobs/pull_problems_job.rb
Normal file
@@ -0,0 +1,23 @@
|
||||
require 'csv'
|
||||
require 'open-uri'
|
||||
|
||||
class PullProblemsJob < ApplicationJob
|
||||
queue_as :default
|
||||
|
||||
PULL_URL = "https://projecteuler.net/minimal=problems;csv"
|
||||
|
||||
def perform
|
||||
csv = CSV.parse(URI.open(PULL_URL), headers: :first_row)
|
||||
csv.each do |row|
|
||||
id = row["ID"].to_i
|
||||
last_updated = Time.at(row["Updated"].to_i)
|
||||
problem = Problem.where(id: id).first_or_create!
|
||||
if problem.pulled_at.nil? or problem.pulled_at < last_updated
|
||||
original_title = row["Description"]
|
||||
problem.update(original_title: original_title)
|
||||
# Don't update pulled_at yet until we also successfully pulled the original_content
|
||||
PullProblemContentJob.perform_later problem
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
14
app/scrubbers/translation_content_scrubber.rb
Normal file
14
app/scrubbers/translation_content_scrubber.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
class TranslationContentScrubber < Rails::Html::PermitScrubber
|
||||
def initialize
|
||||
super
|
||||
self.tags = %w( strong em b i p code pre tt samp kbd var sub
|
||||
sup dfn cite big small address hr br div span h1 h2 h3 h4 h5 h6 ul ol li dl dt dd abbr
|
||||
acronym a img blockquote del ins table th tr td thead tbody tfoot u )
|
||||
self.attributes = %w( href src width height alt cite datetime title class name xml:lang abbr style
|
||||
align border cellpadding cellspacing colspan )
|
||||
end
|
||||
|
||||
def skip_node?(node)
|
||||
node.text?
|
||||
end
|
||||
end
|
||||
@@ -1,7 +1,9 @@
|
||||
<% provide(:title, 'Copyright') %>
|
||||
|
||||
<div class="page-header"><h1>Copyright</h1></div>
|
||||
<div class="bs-callout bs-callout-info">Dieser Artikel dient gleichzeitig der Übersetzung der <a href="https://projecteuler.net/copyright">"Copyright Information" auf projecteuler.net</a> sowie der tatsächlich geltenden Copyright-Informationen für diese Seite.</div>
|
||||
<div class="pb-2 mt-4 mb-2 border-bottom">
|
||||
<h1>Copyright</h1>
|
||||
</div>
|
||||
<div class="alert alert-info" role="alert">Dieser Artikel dient gleichzeitig der Übersetzung der <a href="https://projecteuler.net/copyright">"Copyright Information" auf projecteuler.net</a> sowie der tatsächlich geltenden Copyright-Informationen für diese Seite.</div>
|
||||
<h2>Kann ich die Probleme woanders benutzen?</h2>
|
||||
<p>Ja! Es ist nicht nur erlaubt, sondern beherzt, das Material zu teilen und frei zu benutzen - für nichtkommerzielle Zwecke. Aber bitte lesen Sie weiter...<br />
|
||||
<br />
|
||||
@@ -20,7 +22,7 @@
|
||||
<p>Sie können zum Problem xxx mit der folgenden URI auf projecteuler.net verlinken:</p>
|
||||
<pre>https://projecteuler.net/problem=xxx</pre>
|
||||
<p>Ebenso können Sie zu der entsprechenden Übersetzung mit folgender URI verlinken:</p>
|
||||
<pre>http://projekteuler.de/problems/xxx</pre>
|
||||
<pre>https://projekteuler.de/problems/xxx</pre>
|
||||
<h2>Kann ich genauere Informationen zum Copyright erhalten?</h2>
|
||||
<p>Der Inhalt von projecteuler.net ist lizenziert unter einer Creative Commons Lizenz:<br />
|
||||
Attribution-NonCommercial-ShareAlike 2.0 UK: England & Wales.</p>
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
<div class="jumbotron">
|
||||
<div class="container">
|
||||
<h1>Willkommen auf Projekt Euler!</h1>
|
||||
<p>Diese Seite ist eine Übersetzung der englischen Rätsel-Webseite <a href="https://projecteuler.net/">projecteuler.net</a>.</p>
|
||||
<p>
|
||||
<%= link_to about_info_path, class: 'btn btn-primary btn-lg' do %>
|
||||
Mehr erfahren »
|
||||
<% end %>
|
||||
</div>
|
||||
<h1 class="display-4">Willkommen auf Projekt Euler!</h1>
|
||||
<p>Diese Seite ist eine Übersetzung der englischen Rätsel-Webseite <a href="https://projecteuler.net/">projecteuler.net</a>.</p>
|
||||
<p class="lead">
|
||||
<%= link_to about_info_path, class: 'btn btn-primary btn-lg' do %>
|
||||
Mehr erfahren »
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<div class="container">
|
||||
@@ -16,7 +14,7 @@
|
||||
<h2>Ansehen</h2>
|
||||
<p>Sehen Sie sich die mathematischen Probleme in deutscher Sprache an.</p>
|
||||
<p>
|
||||
<%= link_to problems_path, class: 'btn btn-default' do %>
|
||||
<%= link_to problems_path, class: 'btn btn-primary' do %>
|
||||
Zu den Problemen »
|
||||
<% end %>
|
||||
</p>
|
||||
@@ -28,7 +26,7 @@
|
||||
<div class="col-md-4">
|
||||
<h2>Verbessern</h2>
|
||||
<p>Vermissen Sie eine Funktion auf der Webseite, oder ist Ihnen ein Fehler aufgefallen? Dann helfen Sie beim Entwickeln der Webseite in Ruby on Rails!</p>
|
||||
<p><a class="btn btn-default" href="https://github.com/projekteuler/projekteuler" target="_blank" role="button">Projekt Euler auf GitHub »</a></p>
|
||||
<p><a class="btn btn-primary" href="https://github.com/projekteuler/projekteuler" target="_blank">Projekt Euler auf GitHub »</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,8 +1,8 @@
|
||||
<% provide(:title, 'Über Project Euler') %>
|
||||
|
||||
<div class="page-header">
|
||||
<h1>Über Project Euler</h1>
|
||||
<div class="pb-2 mt-4 mb-2 border-bottom">
|
||||
<h1>Über Project Euler</h1>
|
||||
</div>
|
||||
|
||||
<h2>Was ist Project Euler?</h2>
|
||||
<%= image_tag 'euler_main.jpg', alt: 'Leonhard Euler (1707-1783)', width: '150px', style: 'border:1px solid black;margin:10px;float:right' %>
|
||||
<p>Project Euler ist eine Serie von herausfordernden mathematischen bzw. Programmier-Problemen, die mehr als nur mathematische Einblicke zum Lösen erfordern. Obwohl die Mathematik Ihnen helfen wird, auf elegante und effiziente Lösungswege zu kommen, ist der Gebrauch von Computer- und Programmierkenntnissen nötig, um den Großteil der Probleme zu lösen.<br /><br />Die Motivation für das Starten und die Instandhaltung von Project Euler ist die, dem Forschergeist eine Plattform bereitzustellen, die sich mit unbekannten Gebieten befasst, und neue Konzepte in einem spaßigen und freizeitlichen Kontext zu erlernen.</p>
|
||||
@@ -18,4 +18,4 @@
|
||||
</p>
|
||||
<p>Aber wenn die Probleme nach einer Herausforderung klingen, dann können Sie sich die <%= link_to 'Probleme', problems_path %> ansehen, bevor Sie sich registrieren.
|
||||
</p>
|
||||
<blockquote><p>"Project Euler existiert zum Begeistern, Herausfordern und Entwickeln der Fähigkeiten und des Genusses von jedem mit einem Interesse für die faszinierende Welt der Mathematik."</p></blockquote>
|
||||
<blockquote class="blockquote"><p>"Project Euler existiert zum Begeistern, Herausfordern und Entwickeln der Fähigkeiten und des Genusses von jedem mit einem Interesse für die faszinierende Welt der Mathematik."</p></blockquote>
|
||||
@@ -1,6 +1,8 @@
|
||||
<% provide(:title, 'Impressum') %>
|
||||
|
||||
<div class="page-header"><h1>Impressum</h1></div>
|
||||
<div class="pb-2 mt-4 mb-2 border-bottom">
|
||||
<h1>Impressum</h1>
|
||||
</div>
|
||||
<p>Angaben gemäß § 5 TMG:<br/><br/></p>
|
||||
<p>Philipp Fischbeck<br />
|
||||
Großbeerenstraße 48<br />
|
||||
@@ -8,6 +10,8 @@
|
||||
</p>
|
||||
<h2>Kontakt:</h2>
|
||||
<p>E-Mail: projekteuler@gmail.com</p>
|
||||
<h2>Haftungsausschluss:</h2>
|
||||
<p><strong>Haftung für Inhalte</strong></p> <p>Die Inhalte unserer Seiten wurden mit größter Sorgfalt erstellt. Für die Richtigkeit, Vollständigkeit und Aktualität der Inhalte können wir jedoch keine Gewähr übernehmen. Als Diensteanbieter sind wir gemäß § 7 Abs.1 TMG für eigene Inhalte auf diesen Seiten nach den allgemeinen Gesetzen verantwortlich. Nach §§ 8 bis 10 TMG sind wir als Diensteanbieter jedoch nicht verpflichtet, übermittelte oder gespeicherte fremde Informationen zu überwachen oder nach Umständen zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen. Verpflichtungen zur Entfernung oder Sperrung der Nutzung von Informationen nach den allgemeinen Gesetzen bleiben hiervon unberührt. Eine diesbezügliche Haftung ist jedoch erst ab dem Zeitpunkt der Kenntnis einer konkreten Rechtsverletzung möglich. Bei Bekanntwerden von entsprechenden Rechtsverletzungen werden wir diese Inhalte umgehend entfernen.</p> <p><strong>Haftung für Links</strong></p> <p>Unser Angebot enthält Links zu externen Webseiten Dritter, auf deren Inhalte wir keinen Einfluss haben. Deshalb können wir für diese fremden Inhalte auch keine Gewähr übernehmen. Für die Inhalte der verlinkten Seiten ist stets der jeweilige Anbieter oder Betreiber der Seiten verantwortlich. Die verlinkten Seiten wurden zum Zeitpunkt der Verlinkung auf mögliche Rechtsverstöße überprüft. Rechtswidrige Inhalte waren zum Zeitpunkt der Verlinkung nicht erkennbar. Eine permanente inhaltliche Kontrolle der verlinkten Seiten ist jedoch ohne konkrete Anhaltspunkte einer Rechtsverletzung nicht zumutbar. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Links umgehend entfernen.</p> <p><strong>Urheberrecht</strong></p> <p>Die durch die Seitenbetreiber erstellten Inhalte und Werke auf diesen Seiten unterliegen dem deutschen Urheberrecht. Die Vervielfältigung, Bearbeitung, Verbreitung und jede Art der Verwertung außerhalb der Grenzen des Urheberrechtes bedürfen der schriftlichen Zustimmung des jeweiligen Autors bzw. Erstellers. Downloads und Kopien dieser Seite sind nur für den privaten, nicht kommerziellen Gebrauch gestattet. Soweit die Inhalte auf dieser Seite nicht vom Betreiber erstellt wurden, werden die Urheberrechte Dritter beachtet. Insbesondere werden Inhalte Dritter als solche gekennzeichnet. Sollten Sie trotzdem auf eine Urheberrechtsverletzung aufmerksam werden, bitten wir um einen entsprechenden Hinweis. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Inhalte umgehend entfernen.</p> <p><strong>Datenschutz</strong></p> <p>Die Nutzung unserer Webseite ist in der Regel ohne Angabe personenbezogener Daten möglich. Soweit auf unseren Seiten personenbezogene Daten (beispielsweise Name, Anschrift oder eMail-Adressen) erhoben werden, erfolgt dies, soweit möglich, stets auf freiwilliger Basis. Diese Daten werden ohne Ihre ausdrückliche Zustimmung nicht an Dritte weitergegeben. </p> <p>Wir weisen darauf hin, dass die Datenübertragung im Internet (z.B. bei der Kommunikation per E-Mail) Sicherheitslücken aufweisen kann. Ein lückenloser Schutz der Daten vor dem Zugriff durch Dritte ist nicht möglich. </p> <p>Der Nutzung von im Rahmen der Impressumspflicht veröffentlichten Kontaktdaten durch Dritte zur Übersendung von nicht ausdrücklich angeforderter Werbung und Informationsmaterialien wird hiermit ausdrücklich widersprochen. Die Betreiber der Seiten behalten sich ausdrücklich rechtliche Schritte im Falle der unverlangten Zusendung von Werbeinformationen, etwa durch Spam-Mails, vor.</p><p> </p>
|
||||
<p><strong>Haftung für Inhalte</strong></p> <p>Die Inhalte unserer Seiten wurden mit größter Sorgfalt erstellt. Für die Richtigkeit, Vollständigkeit und Aktualität der Inhalte können wir jedoch keine Gewähr übernehmen. Als Diensteanbieter sind wir gemäß § 7 Abs.1 TMG für eigene Inhalte auf diesen Seiten nach den allgemeinen Gesetzen verantwortlich. Nach §§ 8 bis 10 TMG sind wir als Diensteanbieter jedoch nicht verpflichtet, übermittelte oder gespeicherte fremde Informationen zu überwachen oder nach Umständen zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen. Verpflichtungen zur Entfernung oder Sperrung der Nutzung von Informationen nach den allgemeinen Gesetzen bleiben hiervon unberührt. Eine diesbezügliche Haftung ist jedoch erst ab dem Zeitpunkt der Kenntnis einer konkreten Rechtsverletzung möglich. Bei Bekanntwerden von entsprechenden Rechtsverletzungen werden wir diese Inhalte umgehend entfernen.</p>
|
||||
<p><strong>Haftung für Links</strong></p> <p>Unser Angebot enthält Links zu externen Webseiten Dritter, auf deren Inhalte wir keinen Einfluss haben. Deshalb können wir für diese fremden Inhalte auch keine Gewähr übernehmen. Für die Inhalte der verlinkten Seiten ist stets der jeweilige Anbieter oder Betreiber der Seiten verantwortlich. Die verlinkten Seiten wurden zum Zeitpunkt der Verlinkung auf mögliche Rechtsverstöße überprüft. Rechtswidrige Inhalte waren zum Zeitpunkt der Verlinkung nicht erkennbar. Eine permanente inhaltliche Kontrolle der verlinkten Seiten ist jedoch ohne konkrete Anhaltspunkte einer Rechtsverletzung nicht zumutbar. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Links umgehend entfernen.</p>
|
||||
<p><strong>Kontaktdaten</strong></p>
|
||||
<p>Der Nutzung von im Rahmen der Impressumspflicht veröffentlichten Kontaktdaten durch Dritte zur Übersendung von nicht ausdrücklich angeforderter Werbung und Informationsmaterialien wird hiermit ausdrücklich widersprochen. Die Betreiber der Seiten behalten sich ausdrücklich rechtliche Schritte im Falle der unverlangten Zusendung von Werbeinformationen, etwa durch Spam-Mails, vor.</p><p> </p>
|
||||
<p><i>Quellverweis: <a href="http://www.e-recht24.de/muster-disclaimer.htm" target="_blank">eRecht24 Disclaimer</a></i></p>
|
||||
49
app/views/about/privacy.de.html.erb
Normal file
49
app/views/about/privacy.de.html.erb
Normal file
@@ -0,0 +1,49 @@
|
||||
<% provide(:title, 'Datenschutz') %>
|
||||
|
||||
<div class="pb-2 mt-4 mb-2 border-bottom">
|
||||
<h1>Datenschutz</h1>
|
||||
</div>
|
||||
|
||||
<p>Diese Datenschutz-Information gilt für die Datenverarbeitung durch:<br /></p>
|
||||
<p>Philipp Fischbeck<br />
|
||||
Großbeerenstraße 48<br />
|
||||
14482 Potsdam<br />
|
||||
E-Mail: projekteuler@gmail.com<br />
|
||||
</p>
|
||||
<p>Die Bestellung eines betrieblichen Datenschutzbeauftragten ist nicht erforderlich.</p>
|
||||
<h2>Erhebung und Verarbeitung personenbezogener Daten</h2>
|
||||
<p><strong>Besuch der Webseite</strong></p>
|
||||
Bei Besuch dieser Webseite werden zwangsweise folgende Daten übertragen:
|
||||
<ul>
|
||||
<li>Aufgerufene URL</li>
|
||||
<li>IP-Adresse des aufrufenden Geräts (mit dem letzten Byte anonymisiert)</li>
|
||||
<li>Datum und Uhrzeit des Aufrufs</li>
|
||||
<li>Webseite, von der aus der Zugriff erfolgt</li>
|
||||
<li>Verwendeter Browser und Betriebssystem</li>
|
||||
</ul>
|
||||
|
||||
<p>Die genannten Daten werden durch mich verarbeitet, um einen Verbindungsaufbau zur Webseite zu gewährleisten und die Funktionalität der Webseite zu gewährleisten. Die Rechtsgrundlage für diese Verarbeitung ist Art.6 Abs.1 S.1 lit.f DSGVO. Die Daten werden nicht dazu verwendet, Rückschlüsse auf die Person zu ziehen.</p>
|
||||
|
||||
<p>Die oben beschriebenen Daten werden höchstens 24 Stunden gespeichert und anschließend gelöscht.</p>
|
||||
|
||||
<p><strong>Anmeldung über GitHub</strong></p>
|
||||
<p>Wenn sich der Besucher über ein GitHub-Konto anmeldet, so wird in einer Datenbank ein Eintrag für diesen Nutzer angelegt. In dem Eintrag wird eine Referenznummer auf das GitHub-Konto, der Anzeigename sowie das Datum des letzten Logins gespeichert. Zusätzlich werden Informationen gespeichert, die dem Besucher ein Fortsetzen der letzten Sitzung ermöglicht.</p>
|
||||
|
||||
<p><strong>Einstellen von Übersetzungen</strong></p>
|
||||
<p>Bei der Einstellung von Übersetzungen wird neben dem Inhalt der Übersetzung auch Datum und Uhrzeit des Absendens gespeichert. Ist der Besucher über GitHub angemeldet, so wird zusätzlich eine Referenz auf den entsprechenden Eintrag des Nutzers in der Datenbank gespeichert.</p>
|
||||
|
||||
<p><strong>Nutzung der Kontaktadresse</strong></p>
|
||||
<p>Besucher haben die Möglichkeit, mich über die angegebene Kontaktadresse zu erreichen. Sämtliche Angaben sind freiwillig. Die Datenverarbeitung für die Kontaktaufnahme erfolgt nach Art.6 Abs.1 S.1 lit.a DSGVO auf Grundlage der freiwilligen Einwilligung des Besuchers.</p>
|
||||
|
||||
<h2>Betroffenenrechte</h2>
|
||||
Sie haben das Recht auf
|
||||
<ul>
|
||||
<li>Auskunft über die bei uns über Sie gespeicherten Daten und ihre Verarbeitung (Art. 15 DSGVO),</li>
|
||||
<li>Berichtigung unrichtiger personenbezogener Daten (Art. 16 DSGVO),</li>
|
||||
<li>Löschung Ihrer bei uns gespeicherten Daten (Art. 17 DSGVO),</li>
|
||||
<li>Einschränkung der Datenverarbeitung, sofern wir Ihre Daten aufgrund gesetzlicher Pflichten noch nicht löschen dürfen (Art. 18 DSGVO),</li>
|
||||
<li>Widerspruch gegen die Verarbeitung Ihrer Daten bei uns (Art. 21 DSGVO) und</li>
|
||||
<li>Datenübertragbarkeit, sofern Sie in die Datenverarbeitung eingewilligt haben oder einen Vertrag mit uns abgeschlossen haben (Art. 20 DSGVO).</li>
|
||||
</ul>
|
||||
|
||||
Wenden Sie sich für Anfragen an die oben genannte E-Mail-Adresse.
|
||||
@@ -1,6 +1,6 @@
|
||||
<%= provide(:title, 'Über römische Zahlen') %>
|
||||
|
||||
<div class="page-header">
|
||||
<div class="pb-2 mt-4 mb-2 border-bottom">
|
||||
<h1>Über... römische Zahlen</h1>
|
||||
</div>
|
||||
<h3>Wie liest und schreibt man römische Zahlen?</h3>
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
<% provide(:title, t('.administration')) %>
|
||||
<h1><%= t('.administration') %></h1>
|
||||
<%= link_to t('.view_translations'), admin_translations_path, class: 'btn btn-default' %>
|
||||
|
||||
<h1><%= t('.update_problem_count') %></h1>
|
||||
<%= form_tag '/admin/update_problem_count', method: :post, class: 'form-inline' do %>
|
||||
<%= number_field_tag 'problem_count', @current_problem_count, min: @current_problem_count, class: 'form-control' %>
|
||||
<%= submit_tag t('.update'), class: 'btn btn-warning' %>
|
||||
<% end %>
|
||||
<div class="pb-2 mt-4 mb-2 border-bottom">
|
||||
<h1><%= t('.administration') %></h1>
|
||||
</div>
|
||||
<%= link_to t('.view_translations'), admin_translations_path, class: 'btn btn-primary' %>
|
||||
<p>
|
||||
<%= t('.number_of_problems') %>: <%= @current_problem_count %><br/>
|
||||
<% if @most_recent_pull.present? %>
|
||||
<%= t('.time_since_last_pull') %>: <%= time_ago_in_words @most_recent_pull %>
|
||||
<% end %>
|
||||
</p>
|
||||
<%= button_to t('.pull_problems'), '/admin/pull_problems', method: :post, class: 'btn btn-warning' %>
|
||||
@@ -1 +1 @@
|
||||
<%= will_paginate @translations, renderer: BootstrapPagination::Rails %>
|
||||
<%= will_paginate @translations, renderer: WillPaginate::ActionView::BootstrapLinkRenderer %>
|
||||
@@ -1,6 +1,8 @@
|
||||
<% provide(:title, Translation.model_name.human(count: 2)) %>
|
||||
|
||||
<h1><%= Translation.model_name.human(count: 2) %></h1>
|
||||
<div class="pb-2 mt-4 mb-2 border-bottom">
|
||||
<h1><%= Translation.model_name.human(count: 2) %></h1>
|
||||
</div>
|
||||
|
||||
<%= render 'translation_pagination' %>
|
||||
<table class="table table-striped table-bordered">
|
||||
|
||||
@@ -1,31 +1,47 @@
|
||||
<% provide(:title, t('problems.show.problem_subtitle', id: @translation.problem_id)) %>
|
||||
|
||||
<div class="page-header">
|
||||
<p class="text-muted">
|
||||
<div class="pb-2 mt-4 mb-2">
|
||||
<h1><%= @translation.title %></h1>
|
||||
</div>
|
||||
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<%= t 'problems.show.problem_subtitle', id: @translation.problem_id %>
|
||||
</div>
|
||||
<% if @translation.pending? %>
|
||||
<div class="problem-buttons">
|
||||
<div class="btn-group problem-buttons-inner">
|
||||
<%= link_to admin_translation_decline_path(@translation), method: :post, class: 'btn btn-danger btn-sm' do %>
|
||||
<%= icon('fas', 'times') %> <%= t '.decline_translation' %>
|
||||
<% end %>
|
||||
<%= link_to admin_translation_accept_path(@translation), method: :post, class: 'btn btn-success btn-sm' do %>
|
||||
<%= icon('fas', 'check') %> <%= t '.accept_translation' %>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
<div class="card-body problem-content">
|
||||
<%= sanitize @translation.content, scrubber: TranslationContentScrubber.new %>
|
||||
</div>
|
||||
<div class="card-footer text-muted">
|
||||
<%= render 'shared/authors', authors: Array(@translation.author) %>
|
||||
</p>
|
||||
<h1><%= @translation.title %> <small><%= t 'problems.show.problem_subtitle', id: @translation.problem_id %></small></h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% if @translation.problem.is_translated? %>
|
||||
<% if @translation.title != @translation.problem.title %>
|
||||
<h3><%= t 'activerecord.attributes.translation.title' %></h3>
|
||||
<p><%= t '.changed_title_html', old: @translation.problem.title, new: @translation.title %></p>
|
||||
<% end %>
|
||||
<h3><%= t 'activerecord.attributes.translation.content' %></h3>
|
||||
<%= raw Diffy::Diff.new(@translation.problem.content, @translation.content, allow_empty_diff: false).to_s(:html) %>
|
||||
<div class="alert alert-warning" role="alert"><%= t('.already_translated') %> <%= link_to t('.visit_current_translation'), @translation.problem, target: '_blank', class: 'alert-link' %></div>
|
||||
<% else %>
|
||||
<h2><%= t 'activerecord.attributes.translation.content' %></h2>
|
||||
<%= raw Diffy::Diff.new(@translation.content, @translation.content, allow_empty_diff: false).to_s(:html) %>
|
||||
<div class="alert alert-info" role="alert"><%= t('.is_new_translation') %></div>
|
||||
<% end %>
|
||||
<% if @translation.pending? %>
|
||||
<%= link_to admin_translation_decline_path(@translation), method: :post, class: 'btn btn-default btn-sm pull-right' do %>
|
||||
<%= icon :remove %> <%= t '.decline_translation' %>
|
||||
<% end %>
|
||||
<%= link_to admin_translation_accept_path(@translation), method: :post, class: 'btn btn-default btn-sm pull-right' do %>
|
||||
<%= icon :ok %> <%= t '.accept_translation' %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<%= panel do %>
|
||||
<div class="panel-body problem-content">
|
||||
<%= sanitize @translation.content %>
|
||||
</div>
|
||||
<% end %>
|
||||
<div class="text-center">
|
||||
<%= link_to t('problems.show.view_original_problem'), @translation.problem.original_url, target: '_blank' %>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<p class="text-muted text-center"><%= link_to t('application.copyright'), about_copyright_path %> | <%= t('application.bootstrap_html')%></p>
|
||||
<div class="container text-center">
|
||||
<span class="text-muted">
|
||||
<%= link_to t('application.legal'), about_legal_path %>
|
||||
| <%= link_to t('application.privacy'), about_privacy_path %>
|
||||
| <%= link_to t('application.copyright'), about_copyright_path %>
|
||||
| <%= t('application.bootstrap_html')%>
|
||||
</span>
|
||||
</div>
|
||||
</footer>
|
||||
@@ -1,38 +1,44 @@
|
||||
<%= navbar position: :static do %>
|
||||
<%= vertical do %>
|
||||
<%= link_to root_path, style:'display:flex;align-items:center' do %>
|
||||
<%= image_tag 'logo.png', height: '35px' %>
|
||||
<p class="navbar-text"><%= t('application.site_title') %></p>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<%= horizontal do %>
|
||||
<%= nav class: 'navbar-left' do %>
|
||||
<%= link_to t('application.info'), about_info_path %>
|
||||
<%= link_to Problem.model_name.human(count: 2), problems_path %>
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<div class="container">
|
||||
<%= link_to t('application.site_title'), root_path, class: 'navbar-brand' %>
|
||||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon">
|
||||
<%= icon('fas', 'bars') %>
|
||||
</span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<%= nav_link_to t('application.info'), about_info_path %>
|
||||
<%= nav_link_to Problem.model_name.human(count: 2), problems_path %>
|
||||
<% if user_signed_in? and current_user.admin? %>
|
||||
<%= link_to t('admin.dashboard.index.administration'), admin_dashboard_index_path %>
|
||||
<%= link_to admin_translations_path do %>
|
||||
<%= t('admin.dashboard.index.translations') %> <span class="badge"><%= Translation.pending.count %></span>
|
||||
<%= nav_link_to t('admin.dashboard.index.administration'), admin_dashboard_index_path %>
|
||||
<%= link_to admin_translations_path, class: 'nav-link' do %>
|
||||
<%= t('admin.dashboard.index.translations') %> <span class="badge badge-secondary"><%= Translation.pending.count %></span>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<%= nav class: 'navbar-right' do %>
|
||||
<%= link_to t('application.legal'), about_legal_path %>
|
||||
<% if user_signed_in? %>
|
||||
<span class="navbar-text">Eingeloggt als <b>
|
||||
<%= current_user.name %>
|
||||
</ul>
|
||||
<ul class="navbar-nav">
|
||||
<% if user_signed_in? %>
|
||||
<li class="nav-item">
|
||||
<span class="navbar-text"><%= t 'application.logged_in_as_html', name: current_user.name %>
|
||||
<% if current_user.admin? %>
|
||||
<%= icon :star %>
|
||||
<b><%= icon('fas', 'star') %></b>
|
||||
<% end %>
|
||||
</b></span>
|
||||
<%= link_to(t('application.sign_out'), destroy_user_session_path, method: :delete) %>
|
||||
<% else %>
|
||||
</span>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<%= link_to t('application.sign_out'), destroy_user_session_path, method: :delete, class: 'nav-link' %>
|
||||
</li>
|
||||
<% else %>
|
||||
<li class="nav-item">
|
||||
<% if Rails.env.development? %>
|
||||
<%= link_to(t('application.sign_in'), user_developer_omniauth_authorize_path) %>
|
||||
<%= link_to t('application.sign_in_with_github'), user_developer_omniauth_authorize_path, class: 'nav-link' %>
|
||||
<% else %>
|
||||
<%= link_to(t('application.sign_in'), user_github_omniauth_authorize_path) %>
|
||||
<%= link_to t('application.sign_in_with_github'), user_github_omniauth_authorize_path, class: 'nav-link' %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -5,28 +5,15 @@
|
||||
<%= favicon_link_tag %>
|
||||
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
|
||||
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
|
||||
<script type="text/x-mathjax-config">
|
||||
MathJax.Hub.Config({
|
||||
jax: ["input/TeX", "output/HTML-CSS"],
|
||||
tex2jax: {
|
||||
inlineMath: [ ["$","$"], ["\\(","\\)"] ],
|
||||
displayMath: [ ["$$","$$"], ["\\[","\\]"] ],
|
||||
processEscapes: true
|
||||
},
|
||||
"HTML-CSS": { availableFonts: ["TeX"] }
|
||||
});
|
||||
</script>
|
||||
<script src='https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-AMS_HTML' async></script>
|
||||
<%= javascript_include_tag 'https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-AMS_HTML-full,Safe', 'data-turbolinks-track' => true %>
|
||||
<%= csrf_meta_tags %>
|
||||
</head>
|
||||
<body>
|
||||
<main role="main">
|
||||
<%= render 'layouts/header' %>
|
||||
<div class="container">
|
||||
<div class="container mt-3 mb-3">
|
||||
<%= flash_messages %>
|
||||
<div class="row">
|
||||
<%= yield %>
|
||||
</div>
|
||||
<%= yield %>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
<%= will_paginate @problems, renderer: BootstrapPagination::Rails %>
|
||||
<%= will_paginate @problems, renderer: WillPaginate::ActionView::BootstrapLinkRenderer %>
|
||||
@@ -1,9 +1,15 @@
|
||||
<% provide(:title, Problem.model_name.human(count: 2)) %>
|
||||
|
||||
<div class="page-header"><h1><%= Problem.model_name.human(count: 2) %></h1></div>
|
||||
<div class="pb-2 mt-4 mb-2 border-bottom">
|
||||
<h1><%= Problem.model_name.human(count: 2) %></h1>
|
||||
</div>
|
||||
|
||||
<p><%= t('problems.index.translated_count', translated: Problem.translated_count, total: Problem.count) %></p>
|
||||
<%= progress_bar percentage: @translated_percentage, label: true, striped: true %>
|
||||
<div class="progress mb-3">
|
||||
<div class="progress-bar progress-bar-striped bg-primary" role="progressbar" style="width: <%= @translated_percentage %>%" aria-valuenow="<%= @translated_percentage %>>" aria-valuemin="0" aria-valuemax="100">
|
||||
<%= @translated_percentage %>%
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= render 'problem_pagination' %>
|
||||
<table class="table table-striped table-bordered">
|
||||
@@ -22,12 +28,10 @@
|
||||
<% if problem.is_translated? %>
|
||||
<%= link_to problem.title, problem %>
|
||||
<% else %>
|
||||
<i><%= t 'problems.not_yet_translated' %></i>
|
||||
<%= link_to new_problem_translation_path(problem), class: 'btn btn-default btn-xs' do %>
|
||||
<%= icon :pencil %> <%= t '.suggest_translation' %>
|
||||
<% end %>
|
||||
<%= link_to problem do %>
|
||||
<i><%= problem.original_title %> <%= t 'problems.not_translated_yet' %></i>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
|
||||
@@ -1,19 +1,51 @@
|
||||
<% provide(:title, t('problems.show.problem_subtitle', id: @problem.id)) %>
|
||||
|
||||
<div class="page-header">
|
||||
<p class="text-muted">
|
||||
<%= render 'shared/authors', authors: @problem.authors, has_anonymous_author: @problem.has_anonymous_author? %>
|
||||
</p>
|
||||
<h1><%= @problem.title %> <small><%= t '.problem_subtitle', id: @problem.id %></small></h1>
|
||||
<div class="pb-2 mt-4 mb-2">
|
||||
<h1>
|
||||
<% if @problem.is_translated? %>
|
||||
<%= @problem.title %>
|
||||
<% else %>
|
||||
<i><%= @problem.original_title %> <%= t 'problems.not_translated_yet' %></i>
|
||||
<% end %>
|
||||
</h1>
|
||||
</div>
|
||||
<%= link_to new_problem_translation_path(@problem), class: 'btn btn-default btn-sm pull-right' do %>
|
||||
<%= icon :pencil %> <%= t '.improve_translation' %>
|
||||
<% if Problem.exists?(@problem.id-1) %>
|
||||
<%= link_to problem_path(@problem.id-1), title: t('problems.show.problem_subtitle', id: @problem.id-1), class: 'problem-prev' do %>
|
||||
<%= icon('fas', 'chevron-left') %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<%= panel do %>
|
||||
<div class="panel-body problem-content">
|
||||
<%= sanitize @problem.content %>
|
||||
<% if Problem.exists?(@problem.id+1) %>
|
||||
<%= link_to problem_path(@problem.id+1), title: t('problems.show.problem_subtitle', id: @problem.id+1), class: 'problem-next' do %>
|
||||
<%= icon('fas', 'chevron-right') %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<%= t '.problem_subtitle', id: @problem.id %>
|
||||
</div>
|
||||
<div class="problem-buttons">
|
||||
<%= link_to new_problem_translation_path(@problem), class: 'problem-buttons-inner btn btn-primary btn-sm' do %>
|
||||
<%= icon('fas', 'edit') %>
|
||||
<% if @problem.is_translated? %>
|
||||
<%= t '.improve_translation' %>
|
||||
<% else %>
|
||||
<%= t '.suggest_translation' %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="card-body problem-content">
|
||||
<% if @problem.is_translated? %>
|
||||
<%= sanitize @problem.content, scrubber: TranslationContentScrubber.new %>
|
||||
<% else %>
|
||||
<%= sanitize @problem.original_content, scrubber: TranslationContentScrubber.new %>
|
||||
<% end %>
|
||||
</div>
|
||||
<% if @problem.is_translated? %>
|
||||
<div class="card-footer text-muted">
|
||||
<%= render 'shared/authors', authors: @problem.authors, has_anonymous_author: @problem.has_anonymous_author? %>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<%= link_to t('.view_original_problem'), @problem.original_url, target: '_blank' %>
|
||||
</div>
|
||||
@@ -1,10 +0,0 @@
|
||||
<% provide(:title, t('problems.show.problem_subtitle', id: @problem.id)) %>
|
||||
|
||||
<div class="page-header">
|
||||
<h1><%= t 'problems.show.problem_subtitle', id: @problem.id %></h1>
|
||||
</div>
|
||||
|
||||
<%= t 'problems.not_yet_translated' %>
|
||||
<%= link_to new_problem_translation_path(@problem), class: 'btn btn-default btn-xs' do %>
|
||||
<%= icon :pencil %> <%= t 'problems.index.suggest_translation' %>
|
||||
<% end %>
|
||||
@@ -1,5 +1,5 @@
|
||||
<% if authors.empty? %>
|
||||
Diese Übersetung wurde anonym erstellt.
|
||||
Diese Übersetzung wurde anonym erstellt.
|
||||
<% else %>
|
||||
Diese Übersetzung wurde von
|
||||
<% if local_assigns[:has_anonymous_author] %>
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
<%= form_for [@problem, @translation], layout: :basic do |f| %>
|
||||
<%= bootstrap_form_for [@problem, @translation], layout: :basic do |f| %>
|
||||
<%= f.text_field :title %>
|
||||
<%= nav id: 'translationNav' do %>
|
||||
<%= link_to t('.source_code'), '#source', data: {toggle: 'tab'} %>
|
||||
<%= link_to t('.preview'), '#preview', data: {toggle: 'tab'} %>
|
||||
<% end %>
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="source">
|
||||
<ul class="nav nav-tabs" id="translationTab" role="tablist">
|
||||
<li class="nav-item">
|
||||
<%= link_to t('.source_code'), '#source', data: {toggle: 'tab'}, class:'nav-link active', id:'source-tab', role:'tab', 'aria-controls':'source', 'aria-selected':'true' %>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<%= link_to t('.preview'), '#preview', data: {toggle: 'tab'}, class:'nav-link', id:'preview-tab', role:'tab', 'aria-controls':'preview', 'aria-selected':'false' %>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content" id="translationTabContent">
|
||||
<div class="tab-pane show active" id="source" role="tabpanel" aria-labelledby="source-tab">
|
||||
<%= f.text_area :content, placeholder: t('.translation_source_explanation') %>
|
||||
</div>
|
||||
<%= panel class: 'tab-pane', id: 'preview' do %>
|
||||
<div class="panel-body problem-content">
|
||||
<div class="tab-pane" id="preview" role="tabpanel" aria-labelledby="preview-tab">
|
||||
<div class="card mb-3">
|
||||
<div class="card-body problem-content">
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert alert-info">
|
||||
<%= t '.copyright_warning_html' %>
|
||||
@@ -19,5 +25,8 @@
|
||||
<%= t '.not_logged_in_warning' %>
|
||||
<% end %>
|
||||
</div>
|
||||
<%= f.submit %>
|
||||
<%= f.primary %>
|
||||
<% if user_signed_in? and current_user.admin? %>
|
||||
<%= f.button t('.save_and_accept'), type: :submit, name: "accept", value: true, class: "btn btn-success" %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
<%= panel heading: 'Hinweise zur Erstellung von Übersetzungen', context: :info do %>
|
||||
<div class='panel-body'>
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
Hinweise zur Erstellung von Übersetzungen
|
||||
</div>
|
||||
<div class='card-body'>
|
||||
<ul>
|
||||
<li>Zurzeit müssen Sie den HTML-Quellcode noch manuell von projecteuler.net holen. In Zukunft soll dies automatisch passieren.</li>
|
||||
<li>Die Sie-Form benutzen.</li>
|
||||
<li>Der HTML-Quellcode sollte im Allgemeinen unverändert bleiben.</li>
|
||||
<li>Als Basis für jede Übersetzung dient der <%= link_to "Text von projecteuler.net", @problem.original_url, target: '_blank' %>.</li>
|
||||
<li>Der Originaltitel und Text im untenstehenden Formular sind zu übersetzen.</li>
|
||||
<li>Der HTML-Quelltext in seiner Struktur sollte im Allgemeinen unverändert bleiben.</li>
|
||||
<li>Querverweise zu anderen Problemen sollten in der Form <code>/problem=??</code> erfolgen.</li>
|
||||
<li>Wenn Grafiken oder Dateien verwendet werden, sollte die URL <code>https://projecteuler.net/...</code> verwendet werden.</li>
|
||||
<li>Die Sie-Form benutzen.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
2
bin/bundle
Normal file → Executable file
2
bin/bundle
Normal file → Executable file
@@ -1,3 +1,3 @@
|
||||
#!/usr/bin/env ruby.exe
|
||||
#!/usr/bin/env ruby
|
||||
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
|
||||
load Gem.bin_path('bundler', 'bundle')
|
||||
|
||||
6
bin/rails
Normal file → Executable file
6
bin/rails
Normal file → Executable file
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env ruby.exe
|
||||
#!/usr/bin/env ruby
|
||||
APP_PATH = File.expand_path('../config/application', __dir__)
|
||||
require_relative '../config/boot'
|
||||
require 'rails/commands'
|
||||
require_relative "../config/boot"
|
||||
require "rails/commands"
|
||||
|
||||
6
bin/rake
Normal file → Executable file
6
bin/rake
Normal file → Executable file
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env ruby.exe
|
||||
require_relative '../config/boot'
|
||||
require 'rake'
|
||||
#!/usr/bin/env ruby
|
||||
require_relative "../config/boot"
|
||||
require "rake"
|
||||
Rake.application.run
|
||||
|
||||
18
bin/setup
Normal file → Executable file
18
bin/setup
Normal file → Executable file
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env ruby.exe
|
||||
require 'fileutils'
|
||||
include FileUtils
|
||||
#!/usr/bin/env ruby
|
||||
require "fileutils"
|
||||
|
||||
# path to your application root.
|
||||
APP_ROOT = File.expand_path('..', __dir__)
|
||||
@@ -9,24 +8,25 @@ def system!(*args)
|
||||
system(*args) || abort("\n== Command #{args} failed ==")
|
||||
end
|
||||
|
||||
chdir APP_ROOT do
|
||||
# This script is a starting point to setup your application.
|
||||
FileUtils.chdir APP_ROOT do
|
||||
# This script is a way to set up or update your development environment automatically.
|
||||
# This script is idempotent, so that you can run it at any time and get an expectable outcome.
|
||||
# Add necessary setup steps to this file.
|
||||
|
||||
puts '== Installing dependencies =='
|
||||
system! 'gem install bundler --conservative'
|
||||
system('bundle check') || system!('bundle install')
|
||||
|
||||
# Install JavaScript dependencies if using Yarn
|
||||
# system('bin/yarn')
|
||||
# Install JavaScript dependencies
|
||||
# system! 'bin/yarn'
|
||||
|
||||
# puts "\n== Copying sample files =="
|
||||
# unless File.exist?('config/database.yml')
|
||||
# cp 'config/database.yml.sample', 'config/database.yml'
|
||||
# FileUtils.cp 'config/database.yml.sample', 'config/database.yml'
|
||||
# end
|
||||
|
||||
puts "\n== Preparing database =="
|
||||
system! 'bin/rails db:setup'
|
||||
system! 'bin/rails db:prepare'
|
||||
|
||||
puts "\n== Removing old logs and tempfiles =="
|
||||
system! 'bin/rails log:clear tmp:clear'
|
||||
|
||||
2
bin/update
Normal file → Executable file
2
bin/update
Normal file → Executable file
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env ruby.exe
|
||||
#!/usr/bin/env ruby
|
||||
require 'fileutils'
|
||||
include FileUtils
|
||||
|
||||
|
||||
14
bin/yarn
Normal file → Executable file
14
bin/yarn
Normal file → Executable file
@@ -1,9 +1,15 @@
|
||||
#!/usr/bin/env ruby.exe
|
||||
#!/usr/bin/env ruby
|
||||
APP_ROOT = File.expand_path('..', __dir__)
|
||||
Dir.chdir(APP_ROOT) do
|
||||
begin
|
||||
exec "yarnpkg", *ARGV
|
||||
rescue Errno::ENOENT
|
||||
yarn = ENV["PATH"].split(File::PATH_SEPARATOR).
|
||||
select { |dir| File.expand_path(dir) != __dir__ }.
|
||||
product(["yarn", "yarn.exe"]).
|
||||
map { |dir, file| File.expand_path(file, dir) }.
|
||||
find { |file| File.executable?(file) }
|
||||
|
||||
if yarn
|
||||
exec yarn, *ARGV
|
||||
else
|
||||
$stderr.puts "Yarn executable was not detected in the system."
|
||||
$stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
|
||||
exit 1
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
# This file is used by Rack-based servers to start the application.
|
||||
|
||||
require ::File.expand_path('../config/environment', __FILE__)
|
||||
require_relative "config/environment"
|
||||
|
||||
run Rails.application
|
||||
Rails.application.load_server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
require_relative 'boot'
|
||||
require_relative "boot"
|
||||
|
||||
require 'rails/all'
|
||||
require "rails/all"
|
||||
|
||||
# Require the gems listed in Gemfile, including any gems
|
||||
# you've limited to :test, :development, or :production.
|
||||
@@ -9,16 +9,25 @@ Bundler.require(*Rails.groups)
|
||||
module Projekteuler
|
||||
class Application < Rails::Application
|
||||
# Initialize configuration defaults for originally generated Rails version.
|
||||
config.load_defaults 5.2
|
||||
config.load_defaults 6.1
|
||||
|
||||
# Settings in config/environments/* take precedence over those specified here.
|
||||
# Application configuration can go into files in config/initializers
|
||||
# -- all .rb files in that directory are automatically loaded after loading
|
||||
# the framework and any gems in your application.
|
||||
# Configuration for the application, engines, and railties goes here.
|
||||
#
|
||||
# These settings can be overridden in specific environments using the files
|
||||
# in config/environments, which are processed later.
|
||||
|
||||
config.action_dispatch.default_headers = {
|
||||
'X-Frame-Options' => 'DENY',
|
||||
'X-XSS-Protection' => '1; mode=block',
|
||||
'X-Content-Type-Options' => 'nosniff',
|
||||
'X-Download-Options' => 'noopen',
|
||||
'X-Permitted-Cross-Domain-Policies' => 'none',
|
||||
'Referrer-Policy' => 'strict-origin-when-cross-origin'
|
||||
}
|
||||
|
||||
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
|
||||
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
|
||||
# config.time_zone = 'Central Time (US & Canada)'
|
||||
config.time_zone = 'Berlin'
|
||||
|
||||
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
|
||||
config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
|
||||
|
||||
require 'bundler/setup' # Set up gems listed in the Gemfile.
|
||||
require 'bootsnap/setup' # Speed up boot time by caching expensive operations.
|
||||
require "bundler/setup" # Set up gems listed in the Gemfile.
|
||||
require "bootsnap/setup" # Speed up boot time by caching expensive operations.
|
||||
|
||||
@@ -2,7 +2,7 @@ development:
|
||||
adapter: async
|
||||
|
||||
test:
|
||||
adapter: async
|
||||
adapter: test
|
||||
|
||||
production:
|
||||
adapter: redis
|
||||
|
||||
@@ -1 +1 @@
|
||||
fmujPxH4j3Pge1kfEi/+pTVW7yA6/X3pBWwuLTIRhchpJokcEie/lbUNlpGOhzGUeOaNZpdEWbB+DVh7r8VUf9FaTMZwyA8u7ipB6Kt7FxrmyqGF4Sb3N54JdREq5KetMCR209VR5URghVnTv6fWMNY/CnfmoAhIi6z40ARP//wSgSQIYlTSsIF/LWdaI9Zn6v3eJ4KSs53EeHyIUal5E67QnvPESwWLvZ6BrKh6GO67xfjAKnoWU4YK+bkFeffubRSKqbb9fAHZLiEVD0glOAeibuJzLAZZa5worvQQgJLZnS1syWCDIz4U6PN4/ZBfrxq8Y+TpBXCEu/5JWrr6ARqbQy8Qnte+IL5EYY65Z70vyI9fcpVYiyKtvUQOsOypf/UxMjahg0MSddZlAiY3+ufyA1Sw9s7BCClaaNtOCtRM/thwNRdn3kNh2S/9YuodVSu5PV/V6DDX9BSo/FZeTbntBSsFnIe5NWwyEaC7jn1zp0FXeAT1N7beUpYcIwWXl7roGATq5J+eSycSAydj5TSPpy6pe0JV--zgHjFhqIX9PMSptP--a4kSt219OHlMrnkVTQFQVA==
|
||||
9lygNbYe2bQDZCR1eyyL6+txjBYkoqBE+qPBYeh/xEFj8uOh4sQ/B+EU3VrY1+djm7ZDTVmPeGtbxAkU5I9fSy/YADTON3m5U5WkNft+UnP/gMOkCqe72ceBBmM/HOJFEFcJ8EesCkWVaZPya8+4Q75lF0g1ke1pwxE8Y534lAkG4E9sEaiM+JTwMqNQoqPyCdhAetZd+suWm5vfbqhC/Im7G0axmxATWTd3AUcDP7PUgeuoJ/B/fI4YVlq9rsa938TIfWSDHzfHV5MSyTzwlv+e4Y0TkiJ40dUddIDEk/zZODDhe5YoImgqqgCjgmcUQKmRMKLGbvhACaIIbML+zuodwwJrtQiJRTppwou57+cUGJJ043gjW57f8JSifU9W8cckP8dhYlECuyAPmlLIYuZP0D0=--5k58Qs//NZVKi7uK--00UCCSNXZAl0g8RCUy6qwA==
|
||||
@@ -1,5 +1,5 @@
|
||||
# Load the Rails application.
|
||||
require_relative 'application'
|
||||
require_relative "application"
|
||||
|
||||
# Initialize the Rails application.
|
||||
Rails.application.initialize!
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
require "active_support/core_ext/integer/time"
|
||||
|
||||
Rails.application.configure do
|
||||
# Settings specified here will take precedence over those in config/application.rb.
|
||||
|
||||
# In the development environment your application's code is reloaded on
|
||||
# every request. This slows down response time but is perfect for development
|
||||
# In the development environment your application's code is reloaded any time
|
||||
# it changes. This slows down response time but is perfect for development
|
||||
# since you don't have to restart the web server when you make code changes.
|
||||
config.cache_classes = false
|
||||
|
||||
@@ -16,6 +18,7 @@ Rails.application.configure do
|
||||
# Run rails dev:cache to toggle caching.
|
||||
if Rails.root.join('tmp', 'caching-dev.txt').exist?
|
||||
config.action_controller.perform_caching = true
|
||||
config.action_controller.enable_fragment_cache_logging = true
|
||||
|
||||
config.cache_store = :memory_store
|
||||
config.public_file_server.headers = {
|
||||
@@ -27,7 +30,7 @@ Rails.application.configure do
|
||||
config.cache_store = :null_store
|
||||
end
|
||||
|
||||
# Store uploaded files on the local file system (see config/storage.yml for options)
|
||||
# Store uploaded files on the local file system (see config/storage.yml for options).
|
||||
config.active_storage.service = :local
|
||||
|
||||
# Don't care if the mailer can't send.
|
||||
@@ -38,6 +41,12 @@ Rails.application.configure do
|
||||
# Print deprecation notices to the Rails logger.
|
||||
config.active_support.deprecation = :log
|
||||
|
||||
# Raise exceptions for disallowed deprecations.
|
||||
config.active_support.disallowed_deprecation = :raise
|
||||
|
||||
# Tell Active Support which deprecation messages to disallow.
|
||||
config.active_support.disallowed_deprecation_warnings = []
|
||||
|
||||
# Raise an error on page load if there are pending migrations.
|
||||
config.active_record.migration_error = :page_load
|
||||
|
||||
@@ -52,10 +61,16 @@ Rails.application.configure do
|
||||
# Suppress logger output for asset requests.
|
||||
config.assets.quiet = true
|
||||
|
||||
# Raises error for missing translations
|
||||
config.action_view.raise_on_missing_translations = true
|
||||
# Raises error for missing translations.
|
||||
config.i18n.raise_on_missing_translations = true
|
||||
|
||||
# Annotate rendered view with file names.
|
||||
# config.action_view.annotate_rendered_view_with_filenames = true
|
||||
|
||||
# Use an evented file watcher to asynchronously detect changes in source code,
|
||||
# routes, locales, etc. This feature depends on the listen gem.
|
||||
# config.file_watcher = ActiveSupport::EventedFileUpdateChecker
|
||||
config.file_watcher = ActiveSupport::EventedFileUpdateChecker
|
||||
|
||||
# Uncomment if you wish to allow Action Cable access from any origin.
|
||||
# config.action_cable.disable_request_forgery_protection = true
|
||||
end
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
require "active_support/core_ext/integer/time"
|
||||
|
||||
Rails.application.configure do
|
||||
# Settings specified here will take precedence over those in config/application.rb.
|
||||
|
||||
@@ -22,36 +24,36 @@ Rails.application.configure do
|
||||
# Apache or NGINX already handles this.
|
||||
config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
|
||||
|
||||
# Compress JavaScripts and CSS.
|
||||
# Compress JS using a preprocessor.
|
||||
config.assets.js_compressor = :uglifier
|
||||
|
||||
# Compress CSS using a preprocessor.
|
||||
# config.assets.css_compressor = :sass
|
||||
|
||||
# Do not fallback to assets pipeline if a precompiled asset is missed.
|
||||
config.assets.compile = false
|
||||
|
||||
# `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb
|
||||
|
||||
# Enable serving of images, stylesheets, and JavaScripts from an asset server.
|
||||
# config.action_controller.asset_host = 'http://assets.example.com'
|
||||
# config.asset_host = 'http://assets.example.com'
|
||||
|
||||
# Specifies the header that your server uses for sending files.
|
||||
# config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
|
||||
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
|
||||
|
||||
# Store uploaded files on the local file system (see config/storage.yml for options)
|
||||
# Store uploaded files on the local file system (see config/storage.yml for options).
|
||||
config.active_storage.service = :local
|
||||
|
||||
# Mount Action Cable outside main process or domain
|
||||
# Mount Action Cable outside main process or domain.
|
||||
# config.action_cable.mount_path = nil
|
||||
# config.action_cable.url = 'wss://example.com/cable'
|
||||
# config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
|
||||
|
||||
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
|
||||
# config.force_ssl = true
|
||||
config.force_ssl = true
|
||||
|
||||
# Use the lowest log level to ensure availability of diagnostic information
|
||||
# when problems arise.
|
||||
config.log_level = :debug
|
||||
# Include generic and useful information about system operation, but avoid logging too much
|
||||
# information to avoid inadvertent exposure of personally identifiable information (PII).
|
||||
config.log_level = :info
|
||||
|
||||
# Prepend all log lines with the following tags.
|
||||
config.log_tags = [ :request_id ]
|
||||
@@ -59,9 +61,9 @@ Rails.application.configure do
|
||||
# Use a different cache store in production.
|
||||
# config.cache_store = :mem_cache_store
|
||||
|
||||
# Use a real queuing backend for Active Job (and separate queues per environment)
|
||||
# Use a real queuing backend for Active Job (and separate queues per environment).
|
||||
# config.active_job.queue_adapter = :resque
|
||||
# config.active_job.queue_name_prefix = "projekteuler_#{Rails.env}"
|
||||
# config.active_job.queue_name_prefix = "projekteuler_production"
|
||||
|
||||
config.action_mailer.perform_caching = false
|
||||
|
||||
@@ -76,11 +78,17 @@ Rails.application.configure do
|
||||
# Send deprecation notices to registered listeners.
|
||||
config.active_support.deprecation = :notify
|
||||
|
||||
# Log disallowed deprecations.
|
||||
config.active_support.disallowed_deprecation = :log
|
||||
|
||||
# Tell Active Support which deprecation messages to disallow.
|
||||
config.active_support.disallowed_deprecation_warnings = []
|
||||
|
||||
# Use default logging formatter so that PID and timestamp are not suppressed.
|
||||
config.log_formatter = ::Logger::Formatter.new
|
||||
|
||||
# Use a different logger for distributed setups.
|
||||
# require 'syslog/logger'
|
||||
# require "syslog/logger"
|
||||
# config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
|
||||
|
||||
if ENV["RAILS_LOG_TO_STDOUT"].present?
|
||||
@@ -91,4 +99,25 @@ Rails.application.configure do
|
||||
|
||||
# Do not dump schema after migrations.
|
||||
config.active_record.dump_schema_after_migration = false
|
||||
|
||||
# Inserts middleware to perform automatic connection switching.
|
||||
# The `database_selector` hash is used to pass options to the DatabaseSelector
|
||||
# middleware. The `delay` is used to determine how long to wait after a write
|
||||
# to send a subsequent read to the primary.
|
||||
#
|
||||
# The `database_resolver` class is used by the middleware to determine which
|
||||
# database is appropriate to use based on the time delay.
|
||||
#
|
||||
# The `database_resolver_context` class is used by the middleware to set
|
||||
# timestamps for the last write to the primary. The resolver uses the context
|
||||
# class timestamps to determine how long to wait before reading from the
|
||||
# replica.
|
||||
#
|
||||
# By default Rails will store a last write timestamp in the session. The
|
||||
# DatabaseSelector middleware is designed as such you can define your own
|
||||
# strategy for connection switching and pass that into the middleware through
|
||||
# these configuration options.
|
||||
# config.active_record.database_selector = { delay: 2.seconds }
|
||||
# config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
|
||||
# config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
|
||||
end
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
# The test environment is used exclusively to run your application's
|
||||
# test suite. You never need to work with it otherwise. Remember that
|
||||
# your test database is "scratch space" for the test suite and is wiped
|
||||
# and recreated between test runs. Don't rely on the data there!
|
||||
|
||||
Rails.application.configure do
|
||||
# Settings specified here will take precedence over those in config/application.rb.
|
||||
|
||||
# The test environment is used exclusively to run your application's
|
||||
# test suite. You never need to work with it otherwise. Remember that
|
||||
# your test database is "scratch space" for the test suite and is wiped
|
||||
# and recreated between test runs. Don't rely on the data there!
|
||||
config.cache_classes = true
|
||||
|
||||
# Do not eager load code on boot. This avoids loading your whole application
|
||||
@@ -21,6 +22,7 @@ Rails.application.configure do
|
||||
# Show full error reports and disable caching.
|
||||
config.consider_all_requests_local = true
|
||||
config.action_controller.perform_caching = false
|
||||
config.cache_store = :null_store
|
||||
|
||||
# Raise exceptions instead of rendering exception templates.
|
||||
config.action_dispatch.show_exceptions = false
|
||||
@@ -28,7 +30,7 @@ Rails.application.configure do
|
||||
# Disable request forgery protection in test environment.
|
||||
config.action_controller.allow_forgery_protection = false
|
||||
|
||||
# Store uploaded files on the local file system in a temporary directory
|
||||
# Store uploaded files on the local file system in a temporary directory.
|
||||
config.active_storage.service = :test
|
||||
|
||||
config.action_mailer.perform_caching = false
|
||||
@@ -41,6 +43,15 @@ Rails.application.configure do
|
||||
# Print deprecation notices to the stderr.
|
||||
config.active_support.deprecation = :stderr
|
||||
|
||||
# Raises error for missing translations
|
||||
config.action_view.raise_on_missing_translations = true
|
||||
# Raise exceptions for disallowed deprecations.
|
||||
config.active_support.disallowed_deprecation = :raise
|
||||
|
||||
# Tell Active Support which deprecation messages to disallow.
|
||||
config.active_support.disallowed_deprecation_warnings = []
|
||||
|
||||
# Raises error for missing translations.
|
||||
config.i18n.raise_on_missing_translations = true
|
||||
|
||||
# Annotate rendered view with file names.
|
||||
# config.action_view.annotate_rendered_view_with_filenames = true
|
||||
end
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
|
||||
# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
|
||||
# Rails.backtrace_cleaner.add_silencer { |line| /my_noisy_library/.match?(line) }
|
||||
|
||||
# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
|
||||
# Rails.backtrace_cleaner.remove_silencers!
|
||||
# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code
|
||||
# by setting BACKTRACE=1 before calling your invocation, like "BACKTRACE=1 ./bin/rails runner 'MyClass.perform'".
|
||||
Rails.backtrace_cleaner.remove_silencers! if ENV["BACKTRACE"]
|
||||
|
||||
@@ -4,21 +4,25 @@
|
||||
# For further information see the following documentation
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
|
||||
|
||||
# Rails.application.config.content_security_policy do |policy|
|
||||
# policy.default_src :self, :https
|
||||
# policy.font_src :self, :https, :data
|
||||
# policy.img_src :self, :https, :data
|
||||
# policy.object_src :none
|
||||
# policy.script_src :self, :https
|
||||
# policy.style_src :self, :https
|
||||
Rails.application.config.content_security_policy do |policy|
|
||||
policy.default_src :none
|
||||
policy.font_src :self, 'https://cdnjs.cloudflare.com'
|
||||
policy.img_src :self, 'https://cdnjs.cloudflare.com', 'https://projecteuler.net'
|
||||
policy.object_src :none
|
||||
policy.script_src :self, 'https://cdnjs.cloudflare.com'
|
||||
policy.style_src :self, :unsafe_inline
|
||||
policy.connect_src :self
|
||||
|
||||
# # Specify URI for violation reports
|
||||
# # policy.report_uri "/csp-violation-report-endpoint"
|
||||
# end
|
||||
# Specify URI for violation reports
|
||||
# policy.report_uri "/csp-violation-report-endpoint"
|
||||
end
|
||||
|
||||
# If you are using UJS then enable automatic nonce generation
|
||||
# Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
|
||||
|
||||
# Set the nonce only to specific directives
|
||||
# Rails.application.config.content_security_policy_nonce_directives = %w(script-src)
|
||||
|
||||
# Report CSP violations to a specified URI
|
||||
# For further information see the following documentation:
|
||||
# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
|
||||
|
||||
@@ -126,17 +126,17 @@ Devise.setup do |config|
|
||||
|
||||
# ==> Configuration for :rememberable
|
||||
# The time the user will be remembered without asking for credentials again.
|
||||
# config.remember_for = 2.weeks
|
||||
config.remember_for = 2.weeks
|
||||
|
||||
# Invalidates all the remember me tokens when the user signs out.
|
||||
config.expire_all_remember_me_on_sign_out = true
|
||||
|
||||
# If true, extends the user's remember period when remembered via cookie.
|
||||
# config.extend_remember_period = false
|
||||
config.extend_remember_period = true
|
||||
|
||||
# Options to be passed to the created cookie. For instance, you can set
|
||||
# secure: true in order to force SSL only cookies.
|
||||
# config.rememberable_options = {}
|
||||
config.rememberable_options = { secure: Rails.env.production? }
|
||||
|
||||
# ==> Configuration for :validatable
|
||||
# Range for password length.
|
||||
@@ -237,7 +237,7 @@ Devise.setup do |config|
|
||||
config.omniauth :developer, fields: [:name], uid_field: :name
|
||||
end
|
||||
if Rails.env.production?
|
||||
config.omniauth :github, Rails.application.credentials.github[:client_id], Rails.application.credentials.github[:client_secret], scope: ''
|
||||
config.omniauth :github, ENV['GITHUB_CLIENT_ID'], ENV['GITHUB_CLIENT_SECRET'], scope: ''
|
||||
end
|
||||
if Rails.env.test?
|
||||
config.omniauth :github, '', '', scope: ''
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
# Be sure to restart your server when you modify this file.
|
||||
|
||||
# Configure sensitive parameters which will be filtered from the log file.
|
||||
Rails.application.config.filter_parameters += [:password]
|
||||
Rails.application.config.filter_parameters += [
|
||||
:passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn
|
||||
]
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
# Be sure to restart your server when you modify this file.
|
||||
#
|
||||
# This file contains migration options to ease your Rails 5.1 upgrade.
|
||||
#
|
||||
# Once upgraded flip defaults one by one to migrate to the new default.
|
||||
#
|
||||
# Read the Guide for Upgrading Ruby on Rails for more info on each option.
|
||||
|
||||
# Make `form_with` generate non-remote forms.
|
||||
Rails.application.config.action_view.form_with_generates_remote_forms = false
|
||||
|
||||
# Unknown asset fallback will return the path passed in when the given
|
||||
# asset is not present in the asset pipeline.
|
||||
# Rails.application.config.assets.unknown_asset_fallback = false
|
||||
11
config/initializers/permissions_policy.rb
Normal file
11
config/initializers/permissions_policy.rb
Normal file
@@ -0,0 +1,11 @@
|
||||
# Define an application-wide HTTP permissions policy. For further
|
||||
# information see https://developers.google.com/web/updates/2018/06/feature-policy
|
||||
#
|
||||
Rails.application.config.permissions_policy do |f|
|
||||
f.camera :none
|
||||
f.gyroscope :none
|
||||
f.microphone :none
|
||||
f.usb :none
|
||||
f.fullscreen :self
|
||||
f.payment :none
|
||||
end
|
||||
@@ -5,12 +5,11 @@ de:
|
||||
administration: "Administration"
|
||||
translations: "Übersetzungen"
|
||||
view_translations: "Übersetzungen anschauen"
|
||||
update_problem_count: "Problem-Anzahl aktualisieren"
|
||||
update: "Aktualisieren"
|
||||
update_problem_count:
|
||||
success_message: "Problem-Anzahl wurde erfolgreich aktualisiert!"
|
||||
failure_message: "Problem-Anzahl konnte nicht aktualisiert werden! Grund: %{error}"
|
||||
no_problem_count: "Keine Problem-Anzahl gegeben!"
|
||||
pull_problems: "Probleme aktualisieren"
|
||||
number_of_problems: "Anzahl Probleme"
|
||||
time_since_last_pull: "Zeit seit letzter erfolgreicher Aktualisierung"
|
||||
pull_problems:
|
||||
pull_problems_initiated: "Das Aktualisieren der Probleme wurde angestoßen."
|
||||
translations:
|
||||
index:
|
||||
anonymous: "Anonym"
|
||||
@@ -21,6 +20,7 @@ de:
|
||||
visit_current_translation: "Aktuelle Übersetzung anschauen"
|
||||
already_translated: "Dieses Problem wurde bereits übersetzt."
|
||||
is_new_translation: "Dieses Problem hat bisher keine Übersetzung."
|
||||
changed_title_html: "Neuer Titel: <code>%{new}</code> statt <code>%{old}</code>"
|
||||
accept:
|
||||
success_message: "Übersetzung wurde erfolgreich akzeptiert!"
|
||||
decline:
|
||||
|
||||
@@ -6,11 +6,13 @@ de:
|
||||
updated_at: Aktualisiert
|
||||
application:
|
||||
site_title: "Projekt Euler"
|
||||
sign_in: 'Einloggen'
|
||||
sign_out: 'Ausloggen'
|
||||
sign_in_with_github: 'Mit GitHub anmelden'
|
||||
logged_in_as_html: "Angemeldet als <b>%{name}</b>"
|
||||
sign_out: 'Abmelden'
|
||||
info: "Info"
|
||||
privacy: "Datenschutz"
|
||||
legal: "Impressum"
|
||||
copyright: "Copyright-Informationen"
|
||||
copyright: "Copyright"
|
||||
bootstrap_html: 'Entworfen mit <a href="http://getbootstrap.com/">Bootstrap</a>'
|
||||
helpers:
|
||||
submit:
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
|
||||
de:
|
||||
problems:
|
||||
not_yet_translated: Dieses Problem wurde noch nicht übersetzt.
|
||||
not_translated_yet: "(noch nicht übersetzt)"
|
||||
index:
|
||||
suggest_translation: Übersetzung vorschlagen
|
||||
translated_count: "Übersetzte Probleme: %{translated} von %{total}"
|
||||
show:
|
||||
improve_translation: Übersetzung verbessern
|
||||
problem_subtitle: Problem %{id}
|
||||
view_original_problem: Dieses Problem auf projecteuler.net
|
||||
view_original_problem: Dieses Problem auf projecteuler.net
|
||||
suggest_translation: Übersetzung vorschlagen
|
||||
@@ -8,7 +8,9 @@ de:
|
||||
translation_source_explanation: Hier kommt der HTML-Quelltext der Übersetzung hin.
|
||||
copyright_warning_html: Sie erklären sich einverstanden, dass die Übersetzung unter der Creative Commons Lizenz <a class="alert-link" href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.de" target="_blank">CC BY-NC-SA 4.0</a> veröffentlicht wird.
|
||||
not_logged_in_warning: Wenn Sie wollen, dass die Übersetzung Ihnen zugeordnet werden kann, müssen Sie sich einloggen (oben rechts), andernfalls ist sie anonym.
|
||||
save_and_accept: Übersetzung direkt setzen
|
||||
new:
|
||||
new_translation: Neue Übersetzung für Problem %{id}
|
||||
notice:
|
||||
successfully_created: Der Übersetzungsvorschlag wurde eingereicht. Ein Organisator wird sich Ihren Vorschlag so bald wie möglich anschauen.
|
||||
successfully_created: Der Übersetzungsvorschlag wurde eingereicht. Ein Organisator wird sich Ihren Vorschlag so bald wie möglich anschauen.
|
||||
successfully_created_and_accepted: Die Übersetzung wurde erfolgreich erstellt und akzeptiert.
|
||||
@@ -1,5 +1,5 @@
|
||||
Rails.application.routes.draw do
|
||||
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
|
||||
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
|
||||
|
||||
# The priority is based upon order of creation: first created -> highest priority.
|
||||
# See how all your routes lay out with "rake routes".
|
||||
@@ -11,6 +11,7 @@ Rails.application.routes.draw do
|
||||
get 'about/info'
|
||||
get 'about/copyright'
|
||||
get 'about/legal'
|
||||
get 'about/privacy'
|
||||
|
||||
get 'about/roman_numerals'
|
||||
|
||||
@@ -31,7 +32,7 @@ Rails.application.routes.draw do
|
||||
|
||||
namespace :admin do
|
||||
get '', to: 'dashboard#index', as: 'dashboard_index'
|
||||
post '/update_problem_count', to: 'dashboard#update_problem_count', as: 'dashboard_update_problem_count'
|
||||
post '/pull_problems', to: 'dashboard#pull_problems', as: 'dashboard_pull_problems'
|
||||
resources :translations, only: [:index] do
|
||||
get '', to: 'translations#show', as: ''
|
||||
post 'accept', to: 'translations#accept'
|
||||
|
||||
5
db/migrate/20190828084343_add_remember_token_to_users.rb
Normal file
5
db/migrate/20190828084343_add_remember_token_to_users.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
class AddRememberTokenToUsers < ActiveRecord::Migration[5.2]
|
||||
def change
|
||||
add_column :users, :remember_token, :string
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,7 @@
|
||||
class AddOriginalDataToProblems < ActiveRecord::Migration[6.0]
|
||||
def change
|
||||
add_column :problems, :pulled_at, :datetime
|
||||
add_column :problems, :original_title, :string
|
||||
add_column :problems, :original_content, :text
|
||||
end
|
||||
end
|
||||
16
db/schema.rb
16
db/schema.rb
@@ -2,20 +2,23 @@
|
||||
# of editing this file, please use the migrations feature of Active Record to
|
||||
# incrementally modify your database, and then regenerate this schema definition.
|
||||
#
|
||||
# Note that this schema.rb definition is the authoritative source for your
|
||||
# database schema. If you need to create the application database on another
|
||||
# system, you should be using db:schema:load, not running all the migrations
|
||||
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
|
||||
# you'll amass, the slower it'll run and the greater likelihood for issues).
|
||||
# This file is the source Rails uses to define your schema when running `rails
|
||||
# db:schema:load`. When creating a new database, `rails db:schema:load` tends to
|
||||
# be faster and is potentially less error prone than running all of your
|
||||
# migrations from scratch. Old migrations may fail to apply correctly if those
|
||||
# migrations use external dependencies or application code.
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 2019_02_04_164033) do
|
||||
ActiveRecord::Schema.define(version: 2020_05_01_095104) do
|
||||
|
||||
create_table "problems", force: :cascade do |t|
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.integer "translation_id"
|
||||
t.datetime "pulled_at"
|
||||
t.string "original_title"
|
||||
t.text "original_content"
|
||||
t.index ["translation_id"], name: "index_problems_on_translation_id"
|
||||
end
|
||||
|
||||
@@ -39,6 +42,7 @@ ActiveRecord::Schema.define(version: 2019_02_04_164033) do
|
||||
t.datetime "created_at", null: false
|
||||
t.datetime "updated_at", null: false
|
||||
t.string "name"
|
||||
t.string "remember_token"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
29
db/seeds.rb
29
db/seeds.rb
@@ -10,13 +10,27 @@ Translation.delete_all
|
||||
Problem.delete_all
|
||||
User.delete_all
|
||||
|
||||
admin = User.create!(
|
||||
provider: :developer,
|
||||
uid: "admin",
|
||||
name: "admin",
|
||||
role: :admin
|
||||
)
|
||||
|
||||
for i in 1..20 do
|
||||
translator = User.create!(
|
||||
provider: :developer,
|
||||
uid: "translator",
|
||||
name: "translator",
|
||||
role: :user
|
||||
)
|
||||
|
||||
for i in 1..60 do
|
||||
Problem.create!(id: i)
|
||||
end
|
||||
|
||||
for i in 1..10 do
|
||||
translation = Translation.create!(
|
||||
author: translator,
|
||||
problem: Problem.find(i),
|
||||
title: "Problem Nummer #{i}",
|
||||
content: %Q(<p>Das hier ist der Inhalt von <b>Problem #{i}</b>.
|
||||
@@ -30,19 +44,6 @@ for i in 1..10 do
|
||||
end
|
||||
|
||||
|
||||
User.create!(
|
||||
provider: :developer,
|
||||
uid: "admin",
|
||||
name: "admin",
|
||||
role: 1
|
||||
)
|
||||
|
||||
User.create!(
|
||||
provider: :developer,
|
||||
uid: "translator",
|
||||
name: "translator"
|
||||
)
|
||||
|
||||
p "Created #{Problem.count} problems"
|
||||
p "Created #{Translation.count} translations"
|
||||
p "Created #{User.count} users"
|
||||
|
||||
@@ -2,6 +2,7 @@ require 'test_helper'
|
||||
|
||||
class Admin::DashboardControllerTest < ActionDispatch::IntegrationTest
|
||||
include Devise::Test::IntegrationHelpers
|
||||
include ActiveJob::TestHelper
|
||||
|
||||
setup do
|
||||
end
|
||||
@@ -18,18 +19,14 @@ class Admin::DashboardControllerTest < ActionDispatch::IntegrationTest
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "should post new problem count" do
|
||||
login_admin
|
||||
post admin_dashboard_update_problem_count_url(problem_count: 15)
|
||||
assert_redirected_to admin_dashboard_index_url
|
||||
assert_equal 15, Problem.count
|
||||
end
|
||||
test "should update problems" do
|
||||
WebMock.stub_request(:get, "https://projecteuler.net/minimal=problems;csv").to_return(body: "")
|
||||
|
||||
test "should fail incorrect problem count" do
|
||||
login_admin
|
||||
post admin_dashboard_update_problem_count_url(problem_count: 2)
|
||||
assert_enqueued_jobs 1, only: PullProblemsJob do
|
||||
post admin_dashboard_pull_problems_url
|
||||
end
|
||||
assert_redirected_to admin_dashboard_index_url
|
||||
assert_equal 3, Problem.count
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -7,6 +7,8 @@ class Admin::TranslationsControllerTest < ActionDispatch::IntegrationTest
|
||||
login_admin
|
||||
@translation = translations(:translation_one)
|
||||
@translation_alternative = translations(:translation_two_alternative)
|
||||
@translation_outdated = translations(:translation_two_outdated)
|
||||
@translation_declined = translations(:translation_two_declined)
|
||||
end
|
||||
test "should get index" do
|
||||
get admin_translations_url
|
||||
@@ -14,11 +16,26 @@ class Admin::TranslationsControllerTest < ActionDispatch::IntegrationTest
|
||||
assert_not_nil assigns(:translations)
|
||||
end
|
||||
|
||||
test "should show translation" do
|
||||
test "should show current_translation" do
|
||||
get admin_translation_url(translation_id: @translation)
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "should show alternative_translation" do
|
||||
get admin_translation_url(translation_id: @translation_alternative)
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "should show outdated_translation" do
|
||||
get admin_translation_url(translation_id: @translation_outdated)
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "should show declined_translation" do
|
||||
get admin_translation_url(translation_id: @translation_declined)
|
||||
assert_response :success
|
||||
end
|
||||
|
||||
test "should accept translation" do
|
||||
post admin_translation_accept_path(@translation_alternative)
|
||||
assert_redirected_to problem_path(2)
|
||||
|
||||
@@ -22,4 +22,12 @@ class ProblemsControllerTest < ActionDispatch::IntegrationTest
|
||||
assert_redirected_to problem_path(id: 2)
|
||||
end
|
||||
|
||||
test "should contain HTML styles in translation content" do
|
||||
get problem_url(id: 4)
|
||||
assert_response :success
|
||||
assert_select ".problem-content" do
|
||||
assert_select "b", 1
|
||||
assert_select 'p[style="text-align:center;"]', 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -40,6 +40,28 @@ class TranslationsControllerTest < ActionDispatch::IntegrationTest
|
||||
assert_equal users(:translator), Translation.last.author
|
||||
end
|
||||
|
||||
test "should create and accept translation from admin" do
|
||||
login_admin
|
||||
assert_difference('Translation.count') do
|
||||
post problem_translations_url(problem_id: 1, translation: @update, accept: true)
|
||||
end
|
||||
|
||||
assert_redirected_to problem_path(id: 1)
|
||||
assert_equal users(:admin), Translation.last.author
|
||||
assert_equal Problem.find(1).translation, Translation.last
|
||||
end
|
||||
|
||||
test "should create but not accept translation from normal user" do
|
||||
login_translator
|
||||
assert_difference('Translation.count') do
|
||||
post problem_translations_url(problem_id: 1, translation: @update, accept: true)
|
||||
end
|
||||
|
||||
assert_redirected_to problem_path(id: 1)
|
||||
assert_equal users(:translator), Translation.last.author
|
||||
assert_not_equal Problem.find(1).translation, Translation.last
|
||||
end
|
||||
|
||||
test "should not create incorrect translation" do
|
||||
assert_no_difference('Translation.count') do
|
||||
post problem_translations_url(problem_id: 1, translation: @incorrect)
|
||||
|
||||
4
test/fixtures/problems.yml
vendored
4
test/fixtures/problems.yml
vendored
@@ -11,3 +11,7 @@ two:
|
||||
three:
|
||||
id: 3
|
||||
|
||||
four:
|
||||
id: 4
|
||||
translation: translation_with_html
|
||||
|
||||
|
||||
19
test/fixtures/translations.yml
vendored
19
test/fixtures/translations.yml
vendored
@@ -17,3 +17,22 @@ translation_two_alternative:
|
||||
title: Second title
|
||||
content: The changed content for the second problem
|
||||
status: 0
|
||||
|
||||
translation_two_outdated:
|
||||
problem_id: 2
|
||||
title: Second title
|
||||
content: The old content for the second problem
|
||||
status: 2
|
||||
|
||||
|
||||
translation_two_declined:
|
||||
problem_id: 2
|
||||
title: Second title
|
||||
content: The content for the second problem with typo
|
||||
status: 3
|
||||
|
||||
translation_with_html:
|
||||
problem_id: 4
|
||||
title: Some title
|
||||
content: 'p>This is some <b>custom</b> text.</p><p style="text-align:center;">This is centered.</p>'
|
||||
status: 1
|
||||
@@ -4,7 +4,7 @@ class TranslatorFlowTest < ActionDispatch::IntegrationTest
|
||||
test "can view a problem" do
|
||||
get '/problems/1'
|
||||
assert_response :success
|
||||
assert_select "h1", "First title Problem 1"
|
||||
assert_select "h1", "First title"
|
||||
end
|
||||
|
||||
test "can login via github" do
|
||||
|
||||
15
test/jobs/pull_problem_content_job_test.rb
Normal file
15
test/jobs/pull_problem_content_job_test.rb
Normal file
@@ -0,0 +1,15 @@
|
||||
require 'test_helper'
|
||||
|
||||
class PullProblemContentJobTest < ActiveJob::TestCase
|
||||
test "should update problem count and titles" do
|
||||
|
||||
stub = WebMock.stub_request(:get, "https://projecteuler.net/minimal=3").
|
||||
to_return(body: "\n<p><a href=\"problem=5\">test</a></p>")
|
||||
|
||||
PullProblemContentJob.perform_now problems(:three)
|
||||
|
||||
assert_requested stub
|
||||
assert_equal '<p><a href="/problem=5">test</a></p>', Problem.find(3).original_content
|
||||
assert_in_delta Time.current, Problem.find(3).pulled_at, 5
|
||||
end
|
||||
end
|
||||
27
test/jobs/pull_problems_job_test.rb
Normal file
27
test/jobs/pull_problems_job_test.rb
Normal file
@@ -0,0 +1,27 @@
|
||||
require 'test_helper'
|
||||
require 'csv'
|
||||
|
||||
class PullProblemsJobTest < ActiveJob::TestCase
|
||||
test "should update problem count and titles" do
|
||||
csv_string = CSV.generate do |csv|
|
||||
csv << ["ID", "Description", "Published", "Updated", "Solved By"]
|
||||
csv << [1, "Title of problem 1", 4.days.ago.to_i, 2.days.ago.to_i, 123]
|
||||
csv << [2, "Title of problem 2", 4.days.ago.to_i, 2.days.ago.to_i, 123]
|
||||
csv << [3, "Title of problem 3", 4.days.ago.to_i, 2.days.ago.to_i, 123]
|
||||
csv << [4, "Title of problem 4", 4.days.ago.to_i, 2.days.ago.to_i, 123]
|
||||
csv << [5, "Title of problem 5", 4.days.ago.to_i, 2.days.ago.to_i, 123]
|
||||
end
|
||||
stub = WebMock.stub_request(:get, "https://projecteuler.net/minimal=problems;csv").
|
||||
to_return(body: csv_string)
|
||||
problems(:two).update(pulled_at: 3.days.ago)
|
||||
problems(:three).update(pulled_at: 1.day.ago)
|
||||
|
||||
assert_enqueued_jobs(4, only: PullProblemContentJob) do
|
||||
PullProblemsJob.perform_now
|
||||
end
|
||||
|
||||
assert_requested stub
|
||||
assert_equal 5, Problem.count
|
||||
assert_equal "Title of problem 2", Problem.find(2).original_title
|
||||
end
|
||||
end
|
||||
@@ -26,7 +26,7 @@ class ProblemTest < ActiveSupport::TestCase
|
||||
end
|
||||
|
||||
test "should return correct number of translated problems" do
|
||||
assert_equal 2, Problem.translated_count
|
||||
assert_equal 3, Problem.translated_count
|
||||
end
|
||||
|
||||
test "should allow for problem count updating" do
|
||||
@@ -44,7 +44,7 @@ class ProblemTest < ActiveSupport::TestCase
|
||||
assert_raises ArgumentError do
|
||||
Problem.update_count(1)
|
||||
end
|
||||
assert_equal 3, Problem.count()
|
||||
assert_equal 4, Problem.count()
|
||||
assert_equal "First title", Problem.find(1).title
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
require 'codacy-coverage'
|
||||
|
||||
Codacy::Reporter.start
|
||||
require 'webmock/minitest'
|
||||
|
||||
ENV['RAILS_ENV'] ||= 'test'
|
||||
require File.expand_path('../../config/environment', __FILE__)
|
||||
@@ -10,6 +8,8 @@ class ActiveSupport::TestCase
|
||||
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
|
||||
fixtures :all
|
||||
|
||||
WebMock.disable_net_connect!
|
||||
|
||||
# Add more helper methods to be used by all tests here...
|
||||
end
|
||||
|
||||
|
||||
63
vendor/assets/frameworks/codemirror/addon/display/placeholder.js
vendored
Normal file
63
vendor/assets/frameworks/codemirror/addon/display/placeholder.js
vendored
Normal file
@@ -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) === "");
|
||||
}
|
||||
});
|
||||
349
vendor/assets/frameworks/codemirror/codemirror.css
vendored
Normal file
349
vendor/assets/frameworks/codemirror/codemirror.css
vendored
Normal file
@@ -0,0 +1,349 @@
|
||||
/* BASICS */
|
||||
|
||||
.CodeMirror {
|
||||
/* Set height, width, borders, and global font properties here */
|
||||
font-family: monospace;
|
||||
height: 300px;
|
||||
color: black;
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
/* PADDING */
|
||||
|
||||
.CodeMirror-lines {
|
||||
padding: 4px 0; /* Vertical padding around content */
|
||||
}
|
||||
.CodeMirror pre.CodeMirror-line,
|
||||
.CodeMirror pre.CodeMirror-line-like {
|
||||
padding: 0 4px; /* Horizontal padding of content */
|
||||
}
|
||||
|
||||
.CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
|
||||
background-color: white; /* The little square between H and V scrollbars */
|
||||
}
|
||||
|
||||
/* GUTTER */
|
||||
|
||||
.CodeMirror-gutters {
|
||||
border-right: 1px solid #ddd;
|
||||
background-color: #f7f7f7;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.CodeMirror-linenumbers {}
|
||||
.CodeMirror-linenumber {
|
||||
padding: 0 3px 0 5px;
|
||||
min-width: 20px;
|
||||
text-align: right;
|
||||
color: #999;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.CodeMirror-guttermarker { color: black; }
|
||||
.CodeMirror-guttermarker-subtle { color: #999; }
|
||||
|
||||
/* CURSOR */
|
||||
|
||||
.CodeMirror-cursor {
|
||||
border-left: 1px solid black;
|
||||
border-right: none;
|
||||
width: 0;
|
||||
}
|
||||
/* Shown when moving in bi-directional text */
|
||||
.CodeMirror div.CodeMirror-secondarycursor {
|
||||
border-left: 1px solid silver;
|
||||
}
|
||||
.cm-fat-cursor .CodeMirror-cursor {
|
||||
width: auto;
|
||||
border: 0 !important;
|
||||
background: #7e7;
|
||||
}
|
||||
.cm-fat-cursor div.CodeMirror-cursors {
|
||||
z-index: 1;
|
||||
}
|
||||
.cm-fat-cursor-mark {
|
||||
background-color: rgba(20, 255, 20, 0.5);
|
||||
-webkit-animation: blink 1.06s steps(1) infinite;
|
||||
-moz-animation: blink 1.06s steps(1) infinite;
|
||||
animation: blink 1.06s steps(1) infinite;
|
||||
}
|
||||
.cm-animate-fat-cursor {
|
||||
width: auto;
|
||||
border: 0;
|
||||
-webkit-animation: blink 1.06s steps(1) infinite;
|
||||
-moz-animation: blink 1.06s steps(1) infinite;
|
||||
animation: blink 1.06s steps(1) infinite;
|
||||
background-color: #7e7;
|
||||
}
|
||||
@-moz-keyframes blink {
|
||||
0% {}
|
||||
50% { background-color: transparent; }
|
||||
100% {}
|
||||
}
|
||||
@-webkit-keyframes blink {
|
||||
0% {}
|
||||
50% { background-color: transparent; }
|
||||
100% {}
|
||||
}
|
||||
@keyframes blink {
|
||||
0% {}
|
||||
50% { background-color: transparent; }
|
||||
100% {}
|
||||
}
|
||||
|
||||
/* Can style cursor different in overwrite (non-insert) mode */
|
||||
.CodeMirror-overwrite .CodeMirror-cursor {}
|
||||
|
||||
.cm-tab { display: inline-block; text-decoration: inherit; }
|
||||
|
||||
.CodeMirror-rulers {
|
||||
position: absolute;
|
||||
left: 0; right: 0; top: -50px; bottom: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.CodeMirror-ruler {
|
||||
border-left: 1px solid #ccc;
|
||||
top: 0; bottom: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
/* DEFAULT THEME */
|
||||
|
||||
.cm-s-default .cm-header {color: blue;}
|
||||
.cm-s-default .cm-quote {color: #090;}
|
||||
.cm-negative {color: #d44;}
|
||||
.cm-positive {color: #292;}
|
||||
.cm-header, .cm-strong {font-weight: bold;}
|
||||
.cm-em {font-style: italic;}
|
||||
.cm-link {text-decoration: underline;}
|
||||
.cm-strikethrough {text-decoration: line-through;}
|
||||
|
||||
.cm-s-default .cm-keyword {color: #708;}
|
||||
.cm-s-default .cm-atom {color: #219;}
|
||||
.cm-s-default .cm-number {color: #164;}
|
||||
.cm-s-default .cm-def {color: #00f;}
|
||||
.cm-s-default .cm-variable,
|
||||
.cm-s-default .cm-punctuation,
|
||||
.cm-s-default .cm-property,
|
||||
.cm-s-default .cm-operator {}
|
||||
.cm-s-default .cm-variable-2 {color: #05a;}
|
||||
.cm-s-default .cm-variable-3, .cm-s-default .cm-type {color: #085;}
|
||||
.cm-s-default .cm-comment {color: #a50;}
|
||||
.cm-s-default .cm-string {color: #a11;}
|
||||
.cm-s-default .cm-string-2 {color: #f50;}
|
||||
.cm-s-default .cm-meta {color: #555;}
|
||||
.cm-s-default .cm-qualifier {color: #555;}
|
||||
.cm-s-default .cm-builtin {color: #30a;}
|
||||
.cm-s-default .cm-bracket {color: #997;}
|
||||
.cm-s-default .cm-tag {color: #170;}
|
||||
.cm-s-default .cm-attribute {color: #00c;}
|
||||
.cm-s-default .cm-hr {color: #999;}
|
||||
.cm-s-default .cm-link {color: #00c;}
|
||||
|
||||
.cm-s-default .cm-error {color: #f00;}
|
||||
.cm-invalidchar {color: #f00;}
|
||||
|
||||
.CodeMirror-composing { border-bottom: 2px solid; }
|
||||
|
||||
/* Default styles for common addons */
|
||||
|
||||
div.CodeMirror span.CodeMirror-matchingbracket {color: #0b0;}
|
||||
div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #a22;}
|
||||
.CodeMirror-matchingtag { background: rgba(255, 150, 0, .3); }
|
||||
.CodeMirror-activeline-background {background: #e8f2ff;}
|
||||
|
||||
/* STOP */
|
||||
|
||||
/* The rest of this file contains styles related to the mechanics of
|
||||
the editor. You probably shouldn't touch them. */
|
||||
|
||||
.CodeMirror {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.CodeMirror-scroll {
|
||||
overflow: scroll !important; /* Things will break if this is overridden */
|
||||
/* 30px is the magic margin used to hide the element's real scrollbars */
|
||||
/* See overflow: hidden in .CodeMirror */
|
||||
margin-bottom: -30px; margin-right: -30px;
|
||||
padding-bottom: 30px;
|
||||
height: 100%;
|
||||
outline: none; /* Prevent dragging from highlighting the element */
|
||||
position: relative;
|
||||
}
|
||||
.CodeMirror-sizer {
|
||||
position: relative;
|
||||
border-right: 30px solid transparent;
|
||||
}
|
||||
|
||||
/* The fake, visible scrollbars. Used to force redraw during scrolling
|
||||
before actual scrolling happens, thus preventing shaking and
|
||||
flickering artifacts. */
|
||||
.CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler {
|
||||
position: absolute;
|
||||
z-index: 6;
|
||||
display: none;
|
||||
}
|
||||
.CodeMirror-vscrollbar {
|
||||
right: 0; top: 0;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
.CodeMirror-hscrollbar {
|
||||
bottom: 0; left: 0;
|
||||
overflow-y: hidden;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
.CodeMirror-scrollbar-filler {
|
||||
right: 0; bottom: 0;
|
||||
}
|
||||
.CodeMirror-gutter-filler {
|
||||
left: 0; bottom: 0;
|
||||
}
|
||||
|
||||
.CodeMirror-gutters {
|
||||
position: absolute; left: 0; top: 0;
|
||||
min-height: 100%;
|
||||
z-index: 3;
|
||||
}
|
||||
.CodeMirror-gutter {
|
||||
white-space: normal;
|
||||
height: 100%;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin-bottom: -30px;
|
||||
}
|
||||
.CodeMirror-gutter-wrapper {
|
||||
position: absolute;
|
||||
z-index: 4;
|
||||
background: none !important;
|
||||
border: none !important;
|
||||
}
|
||||
.CodeMirror-gutter-background {
|
||||
position: absolute;
|
||||
top: 0; bottom: 0;
|
||||
z-index: 4;
|
||||
}
|
||||
.CodeMirror-gutter-elt {
|
||||
position: absolute;
|
||||
cursor: default;
|
||||
z-index: 4;
|
||||
}
|
||||
.CodeMirror-gutter-wrapper ::selection { background-color: transparent }
|
||||
.CodeMirror-gutter-wrapper ::-moz-selection { background-color: transparent }
|
||||
|
||||
.CodeMirror-lines {
|
||||
cursor: text;
|
||||
min-height: 1px; /* prevents collapsing before first draw */
|
||||
}
|
||||
.CodeMirror pre.CodeMirror-line,
|
||||
.CodeMirror pre.CodeMirror-line-like {
|
||||
/* Reset some styles that the rest of the page might have set */
|
||||
-moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0;
|
||||
border-width: 0;
|
||||
background: transparent;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
margin: 0;
|
||||
white-space: pre;
|
||||
word-wrap: normal;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
z-index: 2;
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
-webkit-font-variant-ligatures: contextual;
|
||||
font-variant-ligatures: contextual;
|
||||
}
|
||||
.CodeMirror-wrap pre.CodeMirror-line,
|
||||
.CodeMirror-wrap pre.CodeMirror-line-like {
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
.CodeMirror-linebackground {
|
||||
position: absolute;
|
||||
left: 0; right: 0; top: 0; bottom: 0;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.CodeMirror-linewidget {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
padding: 0.1px; /* Force widget margins to stay inside of the container */
|
||||
}
|
||||
|
||||
.CodeMirror-widget {}
|
||||
|
||||
.CodeMirror-rtl pre { direction: rtl; }
|
||||
|
||||
.CodeMirror-code {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
/* Force content-box sizing for the elements where we expect it */
|
||||
.CodeMirror-scroll,
|
||||
.CodeMirror-sizer,
|
||||
.CodeMirror-gutter,
|
||||
.CodeMirror-gutters,
|
||||
.CodeMirror-linenumber {
|
||||
-moz-box-sizing: content-box;
|
||||
box-sizing: content-box;
|
||||
}
|
||||
|
||||
.CodeMirror-measure {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.CodeMirror-cursor {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
.CodeMirror-measure pre { position: static; }
|
||||
|
||||
div.CodeMirror-cursors {
|
||||
visibility: hidden;
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
}
|
||||
div.CodeMirror-dragcursors {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.CodeMirror-focused div.CodeMirror-cursors {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.CodeMirror-selected { background: #d9d9d9; }
|
||||
.CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; }
|
||||
.CodeMirror-crosshair { cursor: crosshair; }
|
||||
.CodeMirror-line::selection, .CodeMirror-line > span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }
|
||||
.CodeMirror-line::-moz-selection, .CodeMirror-line > span::-moz-selection, .CodeMirror-line > span > span::-moz-selection { background: #d7d4f0; }
|
||||
|
||||
.cm-searching {
|
||||
background-color: #ffa;
|
||||
background-color: rgba(255, 255, 0, .4);
|
||||
}
|
||||
|
||||
/* Used to force a border model for a node */
|
||||
.cm-force-border { padding-right: .1px; }
|
||||
|
||||
@media print {
|
||||
/* Hide the cursor when printing */
|
||||
.CodeMirror div.CodeMirror-cursors {
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
/* See issue #2901 */
|
||||
.cm-tab-wrap-hack:after { content: ''; }
|
||||
|
||||
/* Help users use markselection to safely style text background */
|
||||
span.CodeMirror-selectedtext { background: none; }
|
||||
9765
vendor/assets/frameworks/codemirror/codemirror.js
vendored
Normal file
9765
vendor/assets/frameworks/codemirror/codemirror.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
831
vendor/assets/frameworks/codemirror/mode/css.js
vendored
Normal file
831
vendor/assets/frameworks/codemirror/mode/css.js
vendored
Normal file
@@ -0,0 +1,831 @@
|
||||
// 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.defineMode("css", function(config, parserConfig) {
|
||||
var inline = parserConfig.inline
|
||||
if (!parserConfig.propertyKeywords) parserConfig = CodeMirror.resolveMode("text/css");
|
||||
|
||||
var indentUnit = config.indentUnit,
|
||||
tokenHooks = parserConfig.tokenHooks,
|
||||
documentTypes = parserConfig.documentTypes || {},
|
||||
mediaTypes = parserConfig.mediaTypes || {},
|
||||
mediaFeatures = parserConfig.mediaFeatures || {},
|
||||
mediaValueKeywords = parserConfig.mediaValueKeywords || {},
|
||||
propertyKeywords = parserConfig.propertyKeywords || {},
|
||||
nonStandardPropertyKeywords = parserConfig.nonStandardPropertyKeywords || {},
|
||||
fontProperties = parserConfig.fontProperties || {},
|
||||
counterDescriptors = parserConfig.counterDescriptors || {},
|
||||
colorKeywords = parserConfig.colorKeywords || {},
|
||||
valueKeywords = parserConfig.valueKeywords || {},
|
||||
allowNested = parserConfig.allowNested,
|
||||
lineComment = parserConfig.lineComment,
|
||||
supportsAtComponent = parserConfig.supportsAtComponent === true;
|
||||
|
||||
var type, override;
|
||||
function ret(style, tp) { type = tp; return style; }
|
||||
|
||||
// Tokenizers
|
||||
|
||||
function tokenBase(stream, state) {
|
||||
var ch = stream.next();
|
||||
if (tokenHooks[ch]) {
|
||||
var result = tokenHooks[ch](stream, state);
|
||||
if (result !== false) return result;
|
||||
}
|
||||
if (ch == "@") {
|
||||
stream.eatWhile(/[\w\\\-]/);
|
||||
return ret("def", stream.current());
|
||||
} else if (ch == "=" || (ch == "~" || ch == "|") && stream.eat("=")) {
|
||||
return ret(null, "compare");
|
||||
} else if (ch == "\"" || ch == "'") {
|
||||
state.tokenize = tokenString(ch);
|
||||
return state.tokenize(stream, state);
|
||||
} else if (ch == "#") {
|
||||
stream.eatWhile(/[\w\\\-]/);
|
||||
return ret("atom", "hash");
|
||||
} else if (ch == "!") {
|
||||
stream.match(/^\s*\w*/);
|
||||
return ret("keyword", "important");
|
||||
} else if (/\d/.test(ch) || ch == "." && stream.eat(/\d/)) {
|
||||
stream.eatWhile(/[\w.%]/);
|
||||
return ret("number", "unit");
|
||||
} else if (ch === "-") {
|
||||
if (/[\d.]/.test(stream.peek())) {
|
||||
stream.eatWhile(/[\w.%]/);
|
||||
return ret("number", "unit");
|
||||
} else if (stream.match(/^-[\w\\\-]*/)) {
|
||||
stream.eatWhile(/[\w\\\-]/);
|
||||
if (stream.match(/^\s*:/, false))
|
||||
return ret("variable-2", "variable-definition");
|
||||
return ret("variable-2", "variable");
|
||||
} else if (stream.match(/^\w+-/)) {
|
||||
return ret("meta", "meta");
|
||||
}
|
||||
} else if (/[,+>*\/]/.test(ch)) {
|
||||
return ret(null, "select-op");
|
||||
} else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) {
|
||||
return ret("qualifier", "qualifier");
|
||||
} else if (/[:;{}\[\]\(\)]/.test(ch)) {
|
||||
return ret(null, ch);
|
||||
} 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");
|
||||
} else {
|
||||
return ret(null, null);
|
||||
}
|
||||
}
|
||||
|
||||
function tokenString(quote) {
|
||||
return function(stream, state) {
|
||||
var escaped = false, ch;
|
||||
while ((ch = stream.next()) != null) {
|
||||
if (ch == quote && !escaped) {
|
||||
if (quote == ")") stream.backUp(1);
|
||||
break;
|
||||
}
|
||||
escaped = !escaped && ch == "\\";
|
||||
}
|
||||
if (ch == quote || !escaped && quote != ")") state.tokenize = null;
|
||||
return ret("string", "string");
|
||||
};
|
||||
}
|
||||
|
||||
function tokenParenthesized(stream, state) {
|
||||
stream.next(); // Must be '('
|
||||
if (!stream.match(/\s*[\"\')]/, false))
|
||||
state.tokenize = tokenString(")");
|
||||
else
|
||||
state.tokenize = null;
|
||||
return ret(null, "(");
|
||||
}
|
||||
|
||||
// Context management
|
||||
|
||||
function Context(type, indent, prev) {
|
||||
this.type = type;
|
||||
this.indent = indent;
|
||||
this.prev = prev;
|
||||
}
|
||||
|
||||
function pushContext(state, stream, type, indent) {
|
||||
state.context = new Context(type, stream.indentation() + (indent === false ? 0 : indentUnit), state.context);
|
||||
return type;
|
||||
}
|
||||
|
||||
function popContext(state) {
|
||||
if (state.context.prev)
|
||||
state.context = state.context.prev;
|
||||
return state.context.type;
|
||||
}
|
||||
|
||||
function pass(type, stream, state) {
|
||||
return states[state.context.type](type, stream, state);
|
||||
}
|
||||
function popAndPass(type, stream, state, n) {
|
||||
for (var i = n || 1; i > 0; i--)
|
||||
state.context = state.context.prev;
|
||||
return pass(type, stream, state);
|
||||
}
|
||||
|
||||
// Parser
|
||||
|
||||
function wordAsValue(stream) {
|
||||
var word = stream.current().toLowerCase();
|
||||
if (valueKeywords.hasOwnProperty(word))
|
||||
override = "atom";
|
||||
else if (colorKeywords.hasOwnProperty(word))
|
||||
override = "keyword";
|
||||
else
|
||||
override = "variable";
|
||||
}
|
||||
|
||||
var states = {};
|
||||
|
||||
states.top = function(type, stream, state) {
|
||||
if (type == "{") {
|
||||
return pushContext(state, stream, "block");
|
||||
} else if (type == "}" && state.context.prev) {
|
||||
return popContext(state);
|
||||
} else if (supportsAtComponent && /@component/i.test(type)) {
|
||||
return pushContext(state, stream, "atComponentBlock");
|
||||
} else if (/^@(-moz-)?document$/i.test(type)) {
|
||||
return pushContext(state, stream, "documentTypes");
|
||||
} else if (/^@(media|supports|(-moz-)?document|import)$/i.test(type)) {
|
||||
return pushContext(state, stream, "atBlock");
|
||||
} else if (/^@(font-face|counter-style)/i.test(type)) {
|
||||
state.stateArg = type;
|
||||
return "restricted_atBlock_before";
|
||||
} else if (/^@(-(moz|ms|o|webkit)-)?keyframes$/i.test(type)) {
|
||||
return "keyframes";
|
||||
} else if (type && type.charAt(0) == "@") {
|
||||
return pushContext(state, stream, "at");
|
||||
} else if (type == "hash") {
|
||||
override = "builtin";
|
||||
} else if (type == "word") {
|
||||
override = "tag";
|
||||
} else if (type == "variable-definition") {
|
||||
return "maybeprop";
|
||||
} else if (type == "interpolation") {
|
||||
return pushContext(state, stream, "interpolation");
|
||||
} else if (type == ":") {
|
||||
return "pseudo";
|
||||
} else if (allowNested && type == "(") {
|
||||
return pushContext(state, stream, "parens");
|
||||
}
|
||||
return state.context.type;
|
||||
};
|
||||
|
||||
states.block = function(type, stream, state) {
|
||||
if (type == "word") {
|
||||
var word = stream.current().toLowerCase();
|
||||
if (propertyKeywords.hasOwnProperty(word)) {
|
||||
override = "property";
|
||||
return "maybeprop";
|
||||
} else if (nonStandardPropertyKeywords.hasOwnProperty(word)) {
|
||||
override = "string-2";
|
||||
return "maybeprop";
|
||||
} else if (allowNested) {
|
||||
override = stream.match(/^\s*:(?:\s|$)/, false) ? "property" : "tag";
|
||||
return "block";
|
||||
} else {
|
||||
override += " error";
|
||||
return "maybeprop";
|
||||
}
|
||||
} else if (type == "meta") {
|
||||
return "block";
|
||||
} else if (!allowNested && (type == "hash" || type == "qualifier")) {
|
||||
override = "error";
|
||||
return "block";
|
||||
} else {
|
||||
return states.top(type, stream, state);
|
||||
}
|
||||
};
|
||||
|
||||
states.maybeprop = function(type, stream, state) {
|
||||
if (type == ":") return pushContext(state, stream, "prop");
|
||||
return pass(type, stream, state);
|
||||
};
|
||||
|
||||
states.prop = function(type, stream, state) {
|
||||
if (type == ";") return popContext(state);
|
||||
if (type == "{" && allowNested) return pushContext(state, stream, "propBlock");
|
||||
if (type == "}" || type == "{") return popAndPass(type, stream, state);
|
||||
if (type == "(") return pushContext(state, stream, "parens");
|
||||
|
||||
if (type == "hash" && !/^#([0-9a-fA-f]{3,4}|[0-9a-fA-f]{6}|[0-9a-fA-f]{8})$/.test(stream.current())) {
|
||||
override += " error";
|
||||
} else if (type == "word") {
|
||||
wordAsValue(stream);
|
||||
} else if (type == "interpolation") {
|
||||
return pushContext(state, stream, "interpolation");
|
||||
}
|
||||
return "prop";
|
||||
};
|
||||
|
||||
states.propBlock = function(type, _stream, state) {
|
||||
if (type == "}") return popContext(state);
|
||||
if (type == "word") { override = "property"; return "maybeprop"; }
|
||||
return state.context.type;
|
||||
};
|
||||
|
||||
states.parens = function(type, stream, state) {
|
||||
if (type == "{" || type == "}") return popAndPass(type, stream, state);
|
||||
if (type == ")") return popContext(state);
|
||||
if (type == "(") return pushContext(state, stream, "parens");
|
||||
if (type == "interpolation") return pushContext(state, stream, "interpolation");
|
||||
if (type == "word") wordAsValue(stream);
|
||||
return "parens";
|
||||
};
|
||||
|
||||
states.pseudo = function(type, stream, state) {
|
||||
if (type == "meta") return "pseudo";
|
||||
|
||||
if (type == "word") {
|
||||
override = "variable-3";
|
||||
return state.context.type;
|
||||
}
|
||||
return pass(type, stream, state);
|
||||
};
|
||||
|
||||
states.documentTypes = function(type, stream, state) {
|
||||
if (type == "word" && documentTypes.hasOwnProperty(stream.current())) {
|
||||
override = "tag";
|
||||
return state.context.type;
|
||||
} else {
|
||||
return states.atBlock(type, stream, state);
|
||||
}
|
||||
};
|
||||
|
||||
states.atBlock = function(type, stream, state) {
|
||||
if (type == "(") return pushContext(state, stream, "atBlock_parens");
|
||||
if (type == "}" || type == ";") return popAndPass(type, stream, state);
|
||||
if (type == "{") return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top");
|
||||
|
||||
if (type == "interpolation") return pushContext(state, stream, "interpolation");
|
||||
|
||||
if (type == "word") {
|
||||
var word = stream.current().toLowerCase();
|
||||
if (word == "only" || word == "not" || word == "and" || word == "or")
|
||||
override = "keyword";
|
||||
else if (mediaTypes.hasOwnProperty(word))
|
||||
override = "attribute";
|
||||
else if (mediaFeatures.hasOwnProperty(word))
|
||||
override = "property";
|
||||
else if (mediaValueKeywords.hasOwnProperty(word))
|
||||
override = "keyword";
|
||||
else if (propertyKeywords.hasOwnProperty(word))
|
||||
override = "property";
|
||||
else if (nonStandardPropertyKeywords.hasOwnProperty(word))
|
||||
override = "string-2";
|
||||
else if (valueKeywords.hasOwnProperty(word))
|
||||
override = "atom";
|
||||
else if (colorKeywords.hasOwnProperty(word))
|
||||
override = "keyword";
|
||||
else
|
||||
override = "error";
|
||||
}
|
||||
return state.context.type;
|
||||
};
|
||||
|
||||
states.atComponentBlock = function(type, stream, state) {
|
||||
if (type == "}")
|
||||
return popAndPass(type, stream, state);
|
||||
if (type == "{")
|
||||
return popContext(state) && pushContext(state, stream, allowNested ? "block" : "top", false);
|
||||
if (type == "word")
|
||||
override = "error";
|
||||
return state.context.type;
|
||||
};
|
||||
|
||||
states.atBlock_parens = function(type, stream, state) {
|
||||
if (type == ")") return popContext(state);
|
||||
if (type == "{" || type == "}") return popAndPass(type, stream, state, 2);
|
||||
return states.atBlock(type, stream, state);
|
||||
};
|
||||
|
||||
states.restricted_atBlock_before = function(type, stream, state) {
|
||||
if (type == "{")
|
||||
return pushContext(state, stream, "restricted_atBlock");
|
||||
if (type == "word" && state.stateArg == "@counter-style") {
|
||||
override = "variable";
|
||||
return "restricted_atBlock_before";
|
||||
}
|
||||
return pass(type, stream, state);
|
||||
};
|
||||
|
||||
states.restricted_atBlock = function(type, stream, state) {
|
||||
if (type == "}") {
|
||||
state.stateArg = null;
|
||||
return popContext(state);
|
||||
}
|
||||
if (type == "word") {
|
||||
if ((state.stateArg == "@font-face" && !fontProperties.hasOwnProperty(stream.current().toLowerCase())) ||
|
||||
(state.stateArg == "@counter-style" && !counterDescriptors.hasOwnProperty(stream.current().toLowerCase())))
|
||||
override = "error";
|
||||
else
|
||||
override = "property";
|
||||
return "maybeprop";
|
||||
}
|
||||
return "restricted_atBlock";
|
||||
};
|
||||
|
||||
states.keyframes = function(type, stream, state) {
|
||||
if (type == "word") { override = "variable"; return "keyframes"; }
|
||||
if (type == "{") return pushContext(state, stream, "top");
|
||||
return pass(type, stream, state);
|
||||
};
|
||||
|
||||
states.at = function(type, stream, state) {
|
||||
if (type == ";") return popContext(state);
|
||||
if (type == "{" || type == "}") return popAndPass(type, stream, state);
|
||||
if (type == "word") override = "tag";
|
||||
else if (type == "hash") override = "builtin";
|
||||
return "at";
|
||||
};
|
||||
|
||||
states.interpolation = function(type, stream, state) {
|
||||
if (type == "}") return popContext(state);
|
||||
if (type == "{" || type == ";") return popAndPass(type, stream, state);
|
||||
if (type == "word") override = "variable";
|
||||
else if (type != "variable" && type != "(" && type != ")") override = "error";
|
||||
return "interpolation";
|
||||
};
|
||||
|
||||
return {
|
||||
startState: function(base) {
|
||||
return {tokenize: null,
|
||||
state: inline ? "block" : "top",
|
||||
stateArg: null,
|
||||
context: new Context(inline ? "block" : "top", base || 0, null)};
|
||||
},
|
||||
|
||||
token: function(stream, state) {
|
||||
if (!state.tokenize && stream.eatSpace()) return null;
|
||||
var style = (state.tokenize || tokenBase)(stream, state);
|
||||
if (style && typeof style == "object") {
|
||||
type = style[1];
|
||||
style = style[0];
|
||||
}
|
||||
override = style;
|
||||
if (type != "comment")
|
||||
state.state = states[state.state](type, stream, state);
|
||||
return override;
|
||||
},
|
||||
|
||||
indent: function(state, textAfter) {
|
||||
var cx = state.context, ch = textAfter && textAfter.charAt(0);
|
||||
var indent = cx.indent;
|
||||
if (cx.type == "prop" && (ch == "}" || ch == ")")) cx = cx.prev;
|
||||
if (cx.prev) {
|
||||
if (ch == "}" && (cx.type == "block" || cx.type == "top" ||
|
||||
cx.type == "interpolation" || cx.type == "restricted_atBlock")) {
|
||||
// Resume indentation from parent context.
|
||||
cx = cx.prev;
|
||||
indent = cx.indent;
|
||||
} else if (ch == ")" && (cx.type == "parens" || cx.type == "atBlock_parens") ||
|
||||
ch == "{" && (cx.type == "at" || cx.type == "atBlock")) {
|
||||
// Dedent relative to current context.
|
||||
indent = Math.max(0, cx.indent - indentUnit);
|
||||
}
|
||||
}
|
||||
return indent;
|
||||
},
|
||||
|
||||
electricChars: "}",
|
||||
blockCommentStart: "/*",
|
||||
blockCommentEnd: "*/",
|
||||
blockCommentContinue: " * ",
|
||||
lineComment: lineComment,
|
||||
fold: "brace"
|
||||
};
|
||||
});
|
||||
|
||||
function keySet(array) {
|
||||
var keys = {};
|
||||
for (var i = 0; i < array.length; ++i) {
|
||||
keys[array[i].toLowerCase()] = true;
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
var documentTypes_ = [
|
||||
"domain", "regexp", "url", "url-prefix"
|
||||
], documentTypes = keySet(documentTypes_);
|
||||
|
||||
var mediaTypes_ = [
|
||||
"all", "aural", "braille", "handheld", "print", "projection", "screen",
|
||||
"tty", "tv", "embossed"
|
||||
], mediaTypes = keySet(mediaTypes_);
|
||||
|
||||
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", "orientation",
|
||||
"device-pixel-ratio", "min-device-pixel-ratio", "max-device-pixel-ratio",
|
||||
"pointer", "any-pointer", "hover", "any-hover"
|
||||
], mediaFeatures = keySet(mediaFeatures_);
|
||||
|
||||
var mediaValueKeywords_ = [
|
||||
"landscape", "portrait", "none", "coarse", "fine", "on-demand", "hover",
|
||||
"interlace", "progressive"
|
||||
], mediaValueKeywords = keySet(mediaValueKeywords_);
|
||||
|
||||
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-blend-mode", "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", "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",
|
||||
"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-rows", "grid-column", "grid-column-end", "grid-column-gap",
|
||||
"grid-column-start", "grid-gap", "grid-row", "grid-row-end", "grid-row-gap",
|
||||
"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", "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",
|
||||
"marks", "marquee-direction", "marquee-loop",
|
||||
"marquee-play-count", "marquee-speed", "marquee-style", "max-height",
|
||||
"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",
|
||||
"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", "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",
|
||||
"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",
|
||||
"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", "will-change", "word-break",
|
||||
"word-spacing", "word-wrap", "z-index",
|
||||
// SVG-specific
|
||||
"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"
|
||||
], propertyKeywords = keySet(propertyKeywords_);
|
||||
|
||||
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"
|
||||
], nonStandardPropertyKeywords = keySet(nonStandardPropertyKeywords_);
|
||||
|
||||
var fontProperties_ = [
|
||||
"font-family", "src", "unicode-range", "font-variant", "font-feature-settings",
|
||||
"font-stretch", "font-weight", "font-style"
|
||||
], fontProperties = keySet(fontProperties_);
|
||||
|
||||
var counterDescriptors_ = [
|
||||
"additive-symbols", "fallback", "negative", "pad", "prefix", "range",
|
||||
"speak-as", "suffix", "symbols", "system"
|
||||
], counterDescriptors = keySet(counterDescriptors_);
|
||||
|
||||
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"
|
||||
], colorKeywords = keySet(colorKeywords_);
|
||||
|
||||
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", "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",
|
||||
"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", "color", "color-burn", "color-dodge", "column", "column-reverse",
|
||||
"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",
|
||||
"destination-in", "destination-out", "destination-over", "devanagari", "difference",
|
||||
"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", "exclusion", "expanded", "extends", "extra-condensed",
|
||||
"extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "flex", "flex-end", "flex-start", "footnotes",
|
||||
"forwards", "from", "geometricPrecision", "georgian", "graytext", "grid", "groove",
|
||||
"gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hard-light", "hebrew",
|
||||
"help", "hidden", "hide", "higher", "highlight", "highlighttext",
|
||||
"hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "hue", "icon", "ignore",
|
||||
"inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite",
|
||||
"infobackground", "infotext", "inherit", "initial", "inline", "inline-axis",
|
||||
"inline-block", "inline-flex", "inline-grid", "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", "lighten",
|
||||
"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", "luminosity", "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", "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", "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",
|
||||
"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", "row-resize", "row-reverse", "rtl", "run-in", "running",
|
||||
"s-resize", "sans-serif", "saturation", "scale", "scale3d", "scaleX", "scaleY", "scaleZ", "screen",
|
||||
"scroll", "scrollbar", "scroll-position", "se-resize", "searchfield",
|
||||
"searchfield-cancel-button", "searchfield-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", "space-evenly", "spell-out", "square",
|
||||
"square-button", "start", "static", "status-bar", "stretch", "stroke", "sub",
|
||||
"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",
|
||||
"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", "transform",
|
||||
"translate", "translate3d", "translateX", "translateY", "translateZ",
|
||||
"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",
|
||||
"visibleStroke", "visual", "w-resize", "wait", "wave", "wider",
|
||||
"window", "windowframe", "windowtext", "words", "wrap", "wrap-reverse", "x-large", "x-small", "xor",
|
||||
"xx-large", "xx-small"
|
||||
], valueKeywords = keySet(valueKeywords_);
|
||||
|
||||
var allWords = documentTypes_.concat(mediaTypes_).concat(mediaFeatures_).concat(mediaValueKeywords_)
|
||||
.concat(propertyKeywords_).concat(nonStandardPropertyKeywords_).concat(colorKeywords_)
|
||||
.concat(valueKeywords_);
|
||||
CodeMirror.registerHelper("hintWords", "css", allWords);
|
||||
|
||||
function tokenCComment(stream, state) {
|
||||
var maybeEnd = false, ch;
|
||||
while ((ch = stream.next()) != null) {
|
||||
if (maybeEnd && ch == "/") {
|
||||
state.tokenize = null;
|
||||
break;
|
||||
}
|
||||
maybeEnd = (ch == "*");
|
||||
}
|
||||
return ["comment", "comment"];
|
||||
}
|
||||
|
||||
CodeMirror.defineMIME("text/css", {
|
||||
documentTypes: documentTypes,
|
||||
mediaTypes: mediaTypes,
|
||||
mediaFeatures: mediaFeatures,
|
||||
mediaValueKeywords: mediaValueKeywords,
|
||||
propertyKeywords: propertyKeywords,
|
||||
nonStandardPropertyKeywords: nonStandardPropertyKeywords,
|
||||
fontProperties: fontProperties,
|
||||
counterDescriptors: counterDescriptors,
|
||||
colorKeywords: colorKeywords,
|
||||
valueKeywords: valueKeywords,
|
||||
tokenHooks: {
|
||||
"/": function(stream, state) {
|
||||
if (!stream.eat("*")) return false;
|
||||
state.tokenize = tokenCComment;
|
||||
return tokenCComment(stream, state);
|
||||
}
|
||||
},
|
||||
name: "css"
|
||||
});
|
||||
|
||||
CodeMirror.defineMIME("text/x-scss", {
|
||||
mediaTypes: mediaTypes,
|
||||
mediaFeatures: mediaFeatures,
|
||||
mediaValueKeywords: mediaValueKeywords,
|
||||
propertyKeywords: propertyKeywords,
|
||||
nonStandardPropertyKeywords: nonStandardPropertyKeywords,
|
||||
colorKeywords: colorKeywords,
|
||||
valueKeywords: valueKeywords,
|
||||
fontProperties: fontProperties,
|
||||
allowNested: true,
|
||||
lineComment: "//",
|
||||
tokenHooks: {
|
||||
"/": function(stream, state) {
|
||||
if (stream.eat("/")) {
|
||||
stream.skipToEnd();
|
||||
return ["comment", "comment"];
|
||||
} else if (stream.eat("*")) {
|
||||
state.tokenize = tokenCComment;
|
||||
return tokenCComment(stream, state);
|
||||
} else {
|
||||
return ["operator", "operator"];
|
||||
}
|
||||
},
|
||||
":": function(stream) {
|
||||
if (stream.match(/\s*\{/, false))
|
||||
return [null, null]
|
||||
return false;
|
||||
},
|
||||
"$": function(stream) {
|
||||
stream.match(/^[\w-]+/);
|
||||
if (stream.match(/^\s*:/, false))
|
||||
return ["variable-2", "variable-definition"];
|
||||
return ["variable-2", "variable"];
|
||||
},
|
||||
"#": function(stream) {
|
||||
if (!stream.eat("{")) return false;
|
||||
return [null, "interpolation"];
|
||||
}
|
||||
},
|
||||
name: "css",
|
||||
helperType: "scss"
|
||||
});
|
||||
|
||||
CodeMirror.defineMIME("text/x-less", {
|
||||
mediaTypes: mediaTypes,
|
||||
mediaFeatures: mediaFeatures,
|
||||
mediaValueKeywords: mediaValueKeywords,
|
||||
propertyKeywords: propertyKeywords,
|
||||
nonStandardPropertyKeywords: nonStandardPropertyKeywords,
|
||||
colorKeywords: colorKeywords,
|
||||
valueKeywords: valueKeywords,
|
||||
fontProperties: fontProperties,
|
||||
allowNested: true,
|
||||
lineComment: "//",
|
||||
tokenHooks: {
|
||||
"/": function(stream, state) {
|
||||
if (stream.eat("/")) {
|
||||
stream.skipToEnd();
|
||||
return ["comment", "comment"];
|
||||
} else if (stream.eat("*")) {
|
||||
state.tokenize = tokenCComment;
|
||||
return tokenCComment(stream, state);
|
||||
} else {
|
||||
return ["operator", "operator"];
|
||||
}
|
||||
},
|
||||
"@": 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/i, false)) return false;
|
||||
stream.eatWhile(/[\w\\\-]/);
|
||||
if (stream.match(/^\s*:/, false))
|
||||
return ["variable-2", "variable-definition"];
|
||||
return ["variable-2", "variable"];
|
||||
},
|
||||
"&": function() {
|
||||
return ["atom", "atom"];
|
||||
}
|
||||
},
|
||||
name: "css",
|
||||
helperType: "less"
|
||||
});
|
||||
|
||||
CodeMirror.defineMIME("text/x-gss", {
|
||||
documentTypes: documentTypes,
|
||||
mediaTypes: mediaTypes,
|
||||
mediaFeatures: mediaFeatures,
|
||||
propertyKeywords: propertyKeywords,
|
||||
nonStandardPropertyKeywords: nonStandardPropertyKeywords,
|
||||
fontProperties: fontProperties,
|
||||
counterDescriptors: counterDescriptors,
|
||||
colorKeywords: colorKeywords,
|
||||
valueKeywords: valueKeywords,
|
||||
supportsAtComponent: true,
|
||||
tokenHooks: {
|
||||
"/": function(stream, state) {
|
||||
if (!stream.eat("*")) return false;
|
||||
state.tokenize = tokenCComment;
|
||||
return tokenCComment(stream, state);
|
||||
}
|
||||
},
|
||||
name: "css",
|
||||
helperType: "gss"
|
||||
});
|
||||
|
||||
});
|
||||
152
vendor/assets/frameworks/codemirror/mode/htmlmixed.js
vendored
Normal file
152
vendor/assets/frameworks/codemirror/mode/htmlmixed.js
vendored
Normal file
@@ -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"), require("../xml/xml"), require("../javascript/javascript"), require("../css/css"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror", "../xml/xml", "../javascript/javascript", "../css/css"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
"use strict";
|
||||
|
||||
var defaultTags = {
|
||||
script: [
|
||||
["lang", /(javascript|babel)/i, "javascript"],
|
||||
["type", /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^module$|^$/i, "javascript"],
|
||||
["type", /./, "text/plain"],
|
||||
[null, null, "javascript"]
|
||||
],
|
||||
style: [
|
||||
["lang", /^css$/i, "css"],
|
||||
["type", /^(text\/)?(x-)?(stylesheet|css)$/i, "css"],
|
||||
["type", /./, "text/plain"],
|
||||
[null, null, "css"]
|
||||
]
|
||||
};
|
||||
|
||||
function maybeBackup(stream, pat, style) {
|
||||
var cur = stream.current(), close = cur.search(pat);
|
||||
if (close > -1) {
|
||||
stream.backUp(cur.length - close);
|
||||
} else if (cur.match(/<\/?$/)) {
|
||||
stream.backUp(cur.length);
|
||||
if (!stream.match(pat, false)) stream.match(cur);
|
||||
}
|
||||
return style;
|
||||
}
|
||||
|
||||
var attrRegexpCache = {};
|
||||
function getAttrRegexp(attr) {
|
||||
var regexp = attrRegexpCache[attr];
|
||||
if (regexp) return regexp;
|
||||
return attrRegexpCache[attr] = new RegExp("\\s+" + attr + "\\s*=\\s*('|\")?([^'\"]+)('|\")?\\s*");
|
||||
}
|
||||
|
||||
function getAttrValue(text, attr) {
|
||||
var match = text.match(getAttrRegexp(attr))
|
||||
return match ? /^\s*(.*?)\s*$/.exec(match[2])[1] : ""
|
||||
}
|
||||
|
||||
function getTagRegexp(tagName, anchored) {
|
||||
return new RegExp((anchored ? "^" : "") + "<\/\s*" + tagName + "\s*>", "i");
|
||||
}
|
||||
|
||||
function addTags(from, to) {
|
||||
for (var tag in from) {
|
||||
var dest = to[tag] || (to[tag] = []);
|
||||
var source = from[tag];
|
||||
for (var i = source.length - 1; i >= 0; i--)
|
||||
dest.unshift(source[i])
|
||||
}
|
||||
}
|
||||
|
||||
function findMatchingMode(tagInfo, tagText) {
|
||||
for (var i = 0; i < tagInfo.length; i++) {
|
||||
var spec = tagInfo[i];
|
||||
if (!spec[0] || spec[1].test(getAttrValue(tagText, spec[0]))) return spec[2];
|
||||
}
|
||||
}
|
||||
|
||||
CodeMirror.defineMode("htmlmixed", function (config, parserConfig) {
|
||||
var htmlMode = CodeMirror.getMode(config, {
|
||||
name: "xml",
|
||||
htmlMode: true,
|
||||
multilineTagIndentFactor: parserConfig.multilineTagIndentFactor,
|
||||
multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag
|
||||
});
|
||||
|
||||
var tags = {};
|
||||
var configTags = parserConfig && parserConfig.tags, configScript = parserConfig && parserConfig.scriptTypes;
|
||||
addTags(defaultTags, tags);
|
||||
if (configTags) addTags(configTags, tags);
|
||||
if (configScript) for (var i = configScript.length - 1; i >= 0; i--)
|
||||
tags.script.unshift(["type", configScript[i].matches, configScript[i].mode])
|
||||
|
||||
function html(stream, state) {
|
||||
var style = htmlMode.token(stream, state.htmlState), tag = /\btag\b/.test(style), tagName
|
||||
if (tag && !/[<>\s\/]/.test(stream.current()) &&
|
||||
(tagName = state.htmlState.tagName && state.htmlState.tagName.toLowerCase()) &&
|
||||
tags.hasOwnProperty(tagName)) {
|
||||
state.inTag = tagName + " "
|
||||
} else if (state.inTag && tag && />$/.test(stream.current())) {
|
||||
var inTag = /^([\S]+) (.*)/.exec(state.inTag)
|
||||
state.inTag = null
|
||||
var modeSpec = stream.current() == ">" && findMatchingMode(tags[inTag[1]], inTag[2])
|
||||
var mode = CodeMirror.getMode(config, modeSpec)
|
||||
var endTagA = getTagRegexp(inTag[1], true), endTag = getTagRegexp(inTag[1], false);
|
||||
state.token = function (stream, state) {
|
||||
if (stream.match(endTagA, false)) {
|
||||
state.token = html;
|
||||
state.localState = state.localMode = null;
|
||||
return null;
|
||||
}
|
||||
return maybeBackup(stream, endTag, state.localMode.token(stream, state.localState));
|
||||
};
|
||||
state.localMode = mode;
|
||||
state.localState = CodeMirror.startState(mode, htmlMode.indent(state.htmlState, "", ""));
|
||||
} else if (state.inTag) {
|
||||
state.inTag += stream.current()
|
||||
if (stream.eol()) state.inTag += " "
|
||||
}
|
||||
return style;
|
||||
};
|
||||
|
||||
return {
|
||||
startState: function () {
|
||||
var state = CodeMirror.startState(htmlMode);
|
||||
return {token: html, inTag: null, localMode: null, localState: null, htmlState: state};
|
||||
},
|
||||
|
||||
copyState: function (state) {
|
||||
var local;
|
||||
if (state.localState) {
|
||||
local = CodeMirror.copyState(state.localMode, state.localState);
|
||||
}
|
||||
return {token: state.token, inTag: state.inTag,
|
||||
localMode: state.localMode, localState: local,
|
||||
htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
|
||||
},
|
||||
|
||||
token: function (stream, state) {
|
||||
return state.token(stream, state);
|
||||
},
|
||||
|
||||
indent: function (state, textAfter, line) {
|
||||
if (!state.localMode || /^\s*<\//.test(textAfter))
|
||||
return htmlMode.indent(state.htmlState, textAfter, line);
|
||||
else if (state.localMode.indent)
|
||||
return state.localMode.indent(state.localState, textAfter, line);
|
||||
else
|
||||
return CodeMirror.Pass;
|
||||
},
|
||||
|
||||
innerMode: function (state) {
|
||||
return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode};
|
||||
}
|
||||
};
|
||||
}, "xml", "javascript", "css");
|
||||
|
||||
CodeMirror.defineMIME("text/html", "htmlmixed");
|
||||
});
|
||||
930
vendor/assets/frameworks/codemirror/mode/javascript.js
vendored
Normal file
930
vendor/assets/frameworks/codemirror/mode/javascript.js
vendored
Normal file
@@ -0,0 +1,930 @@
|
||||
// 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.defineMode("javascript", function(config, parserConfig) {
|
||||
var indentUnit = config.indentUnit;
|
||||
var statementIndent = parserConfig.statementIndent;
|
||||
var jsonldMode = parserConfig.jsonld;
|
||||
var jsonMode = parserConfig.json || jsonldMode;
|
||||
var isTS = parserConfig.typescript;
|
||||
var wordRE = parserConfig.wordCharacters || /[\w$\xa1-\uffff]/;
|
||||
|
||||
// Tokenizer
|
||||
|
||||
var keywords = function(){
|
||||
function kw(type) {return {type: type, style: "keyword"};}
|
||||
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"};
|
||||
|
||||
return {
|
||||
"if": kw("if"), "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
|
||||
"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
|
||||
};
|
||||
}();
|
||||
|
||||
var isOperatorChar = /[+\-*&%=<>!?|~^@]/;
|
||||
var isJsonldKeyword = /^@(context|id|value|language|type|container|list|set|reverse|index|base|vocab|graph)"/;
|
||||
|
||||
function readRegexp(stream) {
|
||||
var escaped = false, next, inSet = false;
|
||||
while ((next = stream.next()) != null) {
|
||||
if (!escaped) {
|
||||
if (next == "/" && !inSet) return;
|
||||
if (next == "[") inSet = true;
|
||||
else if (inSet && next == "]") inSet = false;
|
||||
}
|
||||
escaped = !escaped && next == "\\";
|
||||
}
|
||||
}
|
||||
|
||||
// Used as scratch variables to communicate multiple values without
|
||||
// consing up tons of objects.
|
||||
var type, content;
|
||||
function ret(tp, style, cont) {
|
||||
type = tp; content = cont;
|
||||
return style;
|
||||
}
|
||||
function tokenBase(stream, state) {
|
||||
var ch = stream.next();
|
||||
if (ch == '"' || ch == "'") {
|
||||
state.tokenize = tokenString(ch);
|
||||
return state.tokenize(stream, state);
|
||||
} else if (ch == "." && stream.match(/^\d[\d_]*(?:[eE][+\-]?[\d_]+)?/)) {
|
||||
return ret("number", "number");
|
||||
} else if (ch == "." && stream.match("..")) {
|
||||
return ret("spread", "meta");
|
||||
} else if (/[\[\]{}\(\),;\:\.]/.test(ch)) {
|
||||
return ret(ch);
|
||||
} else if (ch == "=" && stream.eat(">")) {
|
||||
return ret("=>", "operator");
|
||||
} 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_]*(?:n|(?:\.[\d_]*)?(?:[eE][+\-]?[\d_]+)?)?/);
|
||||
return ret("number", "number");
|
||||
} else if (ch == "/") {
|
||||
if (stream.eat("*")) {
|
||||
state.tokenize = tokenComment;
|
||||
return tokenComment(stream, state);
|
||||
} else if (stream.eat("/")) {
|
||||
stream.skipToEnd();
|
||||
return ret("comment", "comment");
|
||||
} else if (expressionAllowed(stream, state, 1)) {
|
||||
readRegexp(stream);
|
||||
stream.match(/^\b(([gimyus])(?![gimyus]*\2))+\b/);
|
||||
return ret("regexp", "string-2");
|
||||
} else {
|
||||
stream.eat("=");
|
||||
return ret("operator", "operator", stream.current());
|
||||
}
|
||||
} else if (ch == "`") {
|
||||
state.tokenize = tokenQuasi;
|
||||
return tokenQuasi(stream, state);
|
||||
} 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)) {
|
||||
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()
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
function tokenString(quote) {
|
||||
return function(stream, state) {
|
||||
var escaped = false, next;
|
||||
if (jsonldMode && stream.peek() == "@" && stream.match(isJsonldKeyword)){
|
||||
state.tokenize = tokenBase;
|
||||
return ret("jsonld-keyword", "meta");
|
||||
}
|
||||
while ((next = stream.next()) != null) {
|
||||
if (next == quote && !escaped) break;
|
||||
escaped = !escaped && next == "\\";
|
||||
}
|
||||
if (!escaped) state.tokenize = tokenBase;
|
||||
return ret("string", "string");
|
||||
};
|
||||
}
|
||||
|
||||
function tokenComment(stream, state) {
|
||||
var maybeEnd = false, ch;
|
||||
while (ch = stream.next()) {
|
||||
if (ch == "/" && maybeEnd) {
|
||||
state.tokenize = tokenBase;
|
||||
break;
|
||||
}
|
||||
maybeEnd = (ch == "*");
|
||||
}
|
||||
return ret("comment", "comment");
|
||||
}
|
||||
|
||||
function tokenQuasi(stream, state) {
|
||||
var escaped = false, next;
|
||||
while ((next = stream.next()) != null) {
|
||||
if (!escaped && (next == "`" || next == "$" && stream.eat("{"))) {
|
||||
state.tokenize = tokenBase;
|
||||
break;
|
||||
}
|
||||
escaped = !escaped && next == "\\";
|
||||
}
|
||||
return ret("quasi", "string-2", stream.current());
|
||||
}
|
||||
|
||||
var brackets = "([{}])";
|
||||
// This is a crude lookahead trick to try and notice that we're
|
||||
// parsing the argument patterns for a fat-arrow function before we
|
||||
// actually hit the arrow token. It only works if the arrow is on
|
||||
// the same line as the arguments and there's no strange noise
|
||||
// (comments) in between. Fallback is to only notice when we hit the
|
||||
// arrow, and not declare the arguments as locals for the arrow
|
||||
// body.
|
||||
function findFatArrow(stream, state) {
|
||||
if (state.fatArrowAt) state.fatArrowAt = null;
|
||||
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) { if (ch == "(") sawSomething = true; break; }
|
||||
} else if (bracket >= 3 && bracket < 6) {
|
||||
++depth;
|
||||
} else if (wordRE.test(ch)) {
|
||||
sawSomething = true;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
if (sawSomething && !depth) state.fatArrowAt = pos;
|
||||
}
|
||||
|
||||
// Parser
|
||||
|
||||
var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true, "this": true, "jsonld-keyword": true};
|
||||
|
||||
function JSLexical(indented, column, type, align, prev, info) {
|
||||
this.indented = indented;
|
||||
this.column = column;
|
||||
this.type = type;
|
||||
this.prev = prev;
|
||||
this.info = info;
|
||||
if (align != null) this.align = align;
|
||||
}
|
||||
|
||||
function inScope(state, varname) {
|
||||
for (var v = state.localVars; v; v = v.next)
|
||||
if (v.name == varname) return true;
|
||||
for (var cx = state.context; cx; cx = cx.prev) {
|
||||
for (var v = cx.vars; v; v = v.next)
|
||||
if (v.name == varname) return true;
|
||||
}
|
||||
}
|
||||
|
||||
function parseJS(state, style, type, content, stream) {
|
||||
var cc = state.cc;
|
||||
// Communicate our context to the combinators.
|
||||
// (Less wasteful than consing up a hundred closures on every call.)
|
||||
cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc; cx.style = style;
|
||||
|
||||
if (!state.lexical.hasOwnProperty("align"))
|
||||
state.lexical.align = true;
|
||||
|
||||
while(true) {
|
||||
var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
|
||||
if (combinator(type, content)) {
|
||||
while(cc.length && cc[cc.length - 1].lex)
|
||||
cc.pop()();
|
||||
if (cx.marked) return cx.marked;
|
||||
if (type == "variable" && inScope(state, content)) return "variable-2";
|
||||
return style;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Combinator utils
|
||||
|
||||
var cx = {state: null, column: null, marked: null, cc: null};
|
||||
function pass() {
|
||||
for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
|
||||
}
|
||||
function cont() {
|
||||
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) {
|
||||
var state = cx.state;
|
||||
cx.marked = "def";
|
||||
if (state.context) {
|
||||
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
|
||||
|
||||
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 = 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
|
||||
}
|
||||
popcontext.lex = true
|
||||
function pushlex(type, info) {
|
||||
var result = function() {
|
||||
var state = cx.state, indent = state.indented;
|
||||
if (state.lexical.type == "stat") indent = state.lexical.indented;
|
||||
else for (var outer = state.lexical; outer && outer.type == ")" && outer.align; outer = outer.prev)
|
||||
indent = outer.indented;
|
||||
state.lexical = new JSLexical(indent, cx.stream.column(), type, null, state.lexical, info);
|
||||
};
|
||||
result.lex = true;
|
||||
return result;
|
||||
}
|
||||
function poplex() {
|
||||
var state = cx.state;
|
||||
if (state.lexical.prev) {
|
||||
if (state.lexical.type == ")")
|
||||
state.indented = state.lexical.indented;
|
||||
state.lexical = state.lexical.prev;
|
||||
}
|
||||
}
|
||||
poplex.lex = true;
|
||||
|
||||
function expect(wanted) {
|
||||
function exp(type) {
|
||||
if (type == wanted) return cont();
|
||||
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), 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 == "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"), parenExpr, statement, poplex, maybeelse);
|
||||
}
|
||||
if (type == "function") return cont(functiondef);
|
||||
if (type == "for") return cont(pushlex("form"), forspec, statement, 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, maybeCatchBinding, statement, poplex, popcontext);
|
||||
if (type == "export") return cont(pushlex("stat"), afterExport, poplex);
|
||||
if (type == "import") return cont(pushlex("stat"), afterImport, poplex);
|
||||
if (type == "async") return cont(statement)
|
||||
if (value == "@") return cont(expression, statement)
|
||||
return pass(pushlex("stat"), expression, expect(";"), poplex);
|
||||
}
|
||||
function maybeCatchBinding(type) {
|
||||
if (type == "(") return cont(funarg, expect(")"))
|
||||
}
|
||||
function expression(type, value) {
|
||||
return expressionInner(type, value, false);
|
||||
}
|
||||
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(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 == "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 maybeoperatorComma(type, value) {
|
||||
if (type == ",") return cont(expression);
|
||||
return maybeoperatorNoComma(type, value, false);
|
||||
}
|
||||
function maybeoperatorNoComma(type, value, noComma) {
|
||||
var me = noComma == false ? maybeoperatorComma : maybeoperatorNoComma;
|
||||
var expr = noComma == false ? expression : expressionNoComma;
|
||||
if (type == "=>") return cont(pushcontext, noComma ? arrowBodyNoComma : arrowBody, popcontext);
|
||||
if (type == "operator") {
|
||||
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);
|
||||
}
|
||||
if (type == "quasi") { return pass(quasi, me); }
|
||||
if (type == ";") return;
|
||||
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();
|
||||
if (value.slice(value.length - 2) != "${") return cont(quasi);
|
||||
return cont(expression, continueQuasi);
|
||||
}
|
||||
function continueQuasi(type) {
|
||||
if (type == "}") {
|
||||
cx.marked = "string-2";
|
||||
cx.state.tokenize = tokenQuasi;
|
||||
return cont(quasi);
|
||||
}
|
||||
}
|
||||
function arrowBody(type) {
|
||||
findFatArrow(cx.stream, cx.state);
|
||||
return pass(type == "{" ? statement : expression);
|
||||
}
|
||||
function arrowBodyNoComma(type) {
|
||||
findFatArrow(cx.stream, cx.state);
|
||||
return pass(type == "{" ? statement : expressionNoComma);
|
||||
}
|
||||
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);
|
||||
};
|
||||
}
|
||||
function target(_, value) {
|
||||
if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorComma); }
|
||||
}
|
||||
function targetNoComma(_, value) {
|
||||
if (value == "target") { cx.marked = "keyword"; return cont(maybeoperatorNoComma); }
|
||||
}
|
||||
function maybelabel(type) {
|
||||
if (type == ":") return cont(poplex, statement);
|
||||
return pass(maybeoperatorComma, expect(";"), poplex);
|
||||
}
|
||||
function property(type) {
|
||||
if (type == "variable") {cx.marked = "property"; return cont();}
|
||||
}
|
||||
function objprop(type, value) {
|
||||
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 (isTS && isModifier(value)) {
|
||||
cx.marked = "keyword"
|
||||
return cont(objprop)
|
||||
} else if (type == "[") {
|
||||
return cont(expression, maybetype, expect("]"), afterprop);
|
||||
} else if (type == "spread") {
|
||||
return cont(expressionNoComma, afterprop);
|
||||
} else if (value == "*") {
|
||||
cx.marked = "keyword";
|
||||
return cont(objprop);
|
||||
} else if (type == ":") {
|
||||
return pass(afterprop)
|
||||
}
|
||||
}
|
||||
function getterSetter(type) {
|
||||
if (type != "variable") return pass(afterprop);
|
||||
cx.marked = "property";
|
||||
return cont(functiondef);
|
||||
}
|
||||
function afterprop(type) {
|
||||
if (type == ":") return cont(expressionNoComma);
|
||||
if (type == "(") return pass(functiondef);
|
||||
}
|
||||
function commasep(what, end, sep) {
|
||||
function proceed(type, value) {
|
||||
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) {
|
||||
if (type == end || value == end) return pass()
|
||||
return pass(what)
|
||||
}, proceed);
|
||||
}
|
||||
if (type == end || value == end) return cont();
|
||||
if (sep && sep.indexOf(";") > -1) return pass(what)
|
||||
return cont(expect(end));
|
||||
}
|
||||
return function(type, value) {
|
||||
if (type == end || value == end) return cont();
|
||||
return pass(what, proceed);
|
||||
};
|
||||
}
|
||||
function contCommasep(what, end, info) {
|
||||
for (var i = 3; i < arguments.length; i++)
|
||||
cx.cc.push(arguments[i]);
|
||||
return cont(pushlex(end, info), commasep(what, end), poplex);
|
||||
}
|
||||
function block(type) {
|
||||
if (type == "}") return cont();
|
||||
return pass(statement, block);
|
||||
}
|
||||
function maybetype(type, value) {
|
||||
if (isTS) {
|
||||
if (type == ":") return cont(typeexpr);
|
||||
if (value == "?") return cont(maybetype);
|
||||
}
|
||||
}
|
||||
function maybetypeOrIn(type, value) {
|
||||
if (isTS && (type == ":" || value == "in")) return cont(typeexpr)
|
||||
}
|
||||
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(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 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 (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(eltpattern, "]");
|
||||
if (type == "{") return contCommasep(proppattern, "}");
|
||||
}
|
||||
function proppattern(type, value) {
|
||||
if (type == "variable" && !cx.stream.match(/^\s*:/, false)) {
|
||||
register(value);
|
||||
return cont(maybeAssign);
|
||||
}
|
||||
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);
|
||||
}
|
||||
function vardefCont(type) {
|
||||
if (type == ",") return cont(vardef);
|
||||
}
|
||||
function maybeelse(type, value) {
|
||||
if (type == "keyword b" && value == "else") return cont(pushlex("form", "else"), statement, 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, forspec2);
|
||||
if (type == "variable") return cont(forspec2);
|
||||
return pass(forspec2)
|
||||
}
|
||||
function forspec2(type, value) {
|
||||
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, mayberettype, statement, popcontext);
|
||||
if (isTS && value == "<") return cont(pushlex(">"), commasep(typeparam, ">"), poplex, functiondef)
|
||||
}
|
||||
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);
|
||||
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 == "<") 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 == "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 (isTS && type == "(") return pass(functiondecl, classBody)
|
||||
if (type == ";" || type == ",") return cont(classBody);
|
||||
if (type == "}") return cont();
|
||||
if (value == "@") return cont(expression, classBody)
|
||||
}
|
||||
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) {
|
||||
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();
|
||||
if (type == "(") return pass(expression);
|
||||
return pass(importSpec, maybeMoreImports, maybeFrom);
|
||||
}
|
||||
function importSpec(type, value) {
|
||||
if (type == "{") return contCommasep(importSpec, "}");
|
||||
if (type == "variable") register(value);
|
||||
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); }
|
||||
}
|
||||
function maybeFrom(_type, value) {
|
||||
if (value == "from") { cx.marked = "keyword"; return cont(expression); }
|
||||
}
|
||||
function arrayLiteral(type) {
|
||||
if (type == "]") return cont();
|
||||
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) {
|
||||
return state.lastType == "operator" || state.lastType == "," ||
|
||||
isOperatorChar.test(textAfter.charAt(0)) ||
|
||||
/[,.]/.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 {
|
||||
startState: function(basecolumn) {
|
||||
var state = {
|
||||
tokenize: tokenBase,
|
||||
lastType: "sof",
|
||||
cc: [],
|
||||
lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
|
||||
localVars: parserConfig.localVars,
|
||||
context: parserConfig.localVars && new Context(null, null, false),
|
||||
indented: basecolumn || 0
|
||||
};
|
||||
if (parserConfig.globalVars && typeof parserConfig.globalVars == "object")
|
||||
state.globalVars = parserConfig.globalVars;
|
||||
return state;
|
||||
},
|
||||
|
||||
token: function(stream, state) {
|
||||
if (stream.sol()) {
|
||||
if (!state.lexical.hasOwnProperty("align"))
|
||||
state.lexical.align = false;
|
||||
state.indented = stream.indentation();
|
||||
findFatArrow(stream, state);
|
||||
}
|
||||
if (state.tokenize != tokenComment && stream.eatSpace()) return null;
|
||||
var style = state.tokenize(stream, state);
|
||||
if (type == "comment") return style;
|
||||
state.lastType = type == "operator" && (content == "++" || content == "--") ? "incdec" : type;
|
||||
return parseJS(state, style, type, content, stream);
|
||||
},
|
||||
|
||||
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, 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;
|
||||
}
|
||||
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.length + 1 : 0);
|
||||
else if (type == "form" && firstChar == "{") return lexical.indented;
|
||||
else if (type == "form") return lexical.indented + indentUnit;
|
||||
else if (type == "stat")
|
||||
return lexical.indented + (isContinuedStatement(state, textAfter) ? statementIndent || indentUnit : 0);
|
||||
else if (lexical.info == "switch" && !closing && parserConfig.doubleIndentSwitch != false)
|
||||
return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
|
||||
else if (lexical.align) return lexical.column + (closing ? 0 : 1);
|
||||
else return lexical.indented + (closing ? 0 : indentUnit);
|
||||
},
|
||||
|
||||
electricInput: /^\s*(?:case .*?:|default:|\{|\})$/,
|
||||
blockCommentStart: jsonMode ? null : "/*",
|
||||
blockCommentEnd: jsonMode ? null : "*/",
|
||||
blockCommentContinue: jsonMode ? null : " * ",
|
||||
lineComment: jsonMode ? null : "//",
|
||||
fold: "brace",
|
||||
closeBrackets: "()[]{}''\"\"``",
|
||||
|
||||
helperType: jsonMode ? "json" : "javascript",
|
||||
jsonldMode: jsonldMode,
|
||||
jsonMode: jsonMode,
|
||||
|
||||
expressionAllowed: expressionAllowed,
|
||||
|
||||
skipExpression: function(state) {
|
||||
var top = state.cc[state.cc.length - 1]
|
||||
if (top == expression || top == expressionNoComma) state.cc.pop()
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
CodeMirror.registerHelper("wordChars", "javascript", /[\w$]/);
|
||||
|
||||
CodeMirror.defineMIME("text/javascript", "javascript");
|
||||
CodeMirror.defineMIME("text/ecmascript", "javascript");
|
||||
CodeMirror.defineMIME("application/javascript", "javascript");
|
||||
CodeMirror.defineMIME("application/x-javascript", "javascript");
|
||||
CodeMirror.defineMIME("application/ecmascript", "javascript");
|
||||
CodeMirror.defineMIME("application/json", {name: "javascript", json: true});
|
||||
CodeMirror.defineMIME("application/x-json", {name: "javascript", json: true});
|
||||
CodeMirror.defineMIME("application/ld+json", {name: "javascript", jsonld: true});
|
||||
CodeMirror.defineMIME("text/typescript", { name: "javascript", typescript: true });
|
||||
CodeMirror.defineMIME("application/typescript", { name: "javascript", typescript: true });
|
||||
|
||||
});
|
||||
413
vendor/assets/frameworks/codemirror/mode/xml.js
vendored
Normal file
413
vendor/assets/frameworks/codemirror/mode/xml.js
vendored
Normal file
@@ -0,0 +1,413 @@
|
||||
// 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 htmlConfig = {
|
||||
autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
|
||||
'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
|
||||
'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
|
||||
'track': true, 'wbr': true, 'menuitem': true},
|
||||
implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
|
||||
'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
|
||||
'th': true, 'tr': true},
|
||||
contextGrabbers: {
|
||||
'dd': {'dd': true, 'dt': true},
|
||||
'dt': {'dd': true, 'dt': true},
|
||||
'li': {'li': true},
|
||||
'option': {'option': true, 'optgroup': true},
|
||||
'optgroup': {'optgroup': true},
|
||||
'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
|
||||
'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
|
||||
'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
|
||||
'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
|
||||
'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
|
||||
'rp': {'rp': true, 'rt': true},
|
||||
'rt': {'rp': true, 'rt': true},
|
||||
'tbody': {'tbody': true, 'tfoot': true},
|
||||
'td': {'td': true, 'th': true},
|
||||
'tfoot': {'tbody': true},
|
||||
'th': {'td': true, 'th': true},
|
||||
'thead': {'tbody': true, 'tfoot': true},
|
||||
'tr': {'tr': true}
|
||||
},
|
||||
doNotIndent: {"pre": true},
|
||||
allowUnquoted: true,
|
||||
allowMissing: true,
|
||||
caseFold: true
|
||||
}
|
||||
|
||||
var xmlConfig = {
|
||||
autoSelfClosers: {},
|
||||
implicitlyClosed: {},
|
||||
contextGrabbers: {},
|
||||
doNotIndent: {},
|
||||
allowUnquoted: false,
|
||||
allowMissing: false,
|
||||
allowMissingTagName: false,
|
||||
caseFold: false
|
||||
}
|
||||
|
||||
CodeMirror.defineMode("xml", function(editorConf, config_) {
|
||||
var indentUnit = editorConf.indentUnit
|
||||
var config = {}
|
||||
var defaults = config_.htmlMode ? htmlConfig : xmlConfig
|
||||
for (var prop in defaults) config[prop] = defaults[prop]
|
||||
for (var prop in config_) config[prop] = config_[prop]
|
||||
|
||||
// Return variables for tokenizers
|
||||
var type, setStyle;
|
||||
|
||||
function inText(stream, state) {
|
||||
function chain(parser) {
|
||||
state.tokenize = parser;
|
||||
return parser(stream, state);
|
||||
}
|
||||
|
||||
var ch = stream.next();
|
||||
if (ch == "<") {
|
||||
if (stream.eat("!")) {
|
||||
if (stream.eat("[")) {
|
||||
if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
|
||||
else return null;
|
||||
} else if (stream.match("--")) {
|
||||
return chain(inBlock("comment", "-->"));
|
||||
} else if (stream.match("DOCTYPE", true, true)) {
|
||||
stream.eatWhile(/[\w\._\-]/);
|
||||
return chain(doctype(1));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
} else if (stream.eat("?")) {
|
||||
stream.eatWhile(/[\w\._\-]/);
|
||||
state.tokenize = inBlock("meta", "?>");
|
||||
return "meta";
|
||||
} else {
|
||||
type = stream.eat("/") ? "closeTag" : "openTag";
|
||||
state.tokenize = inTag;
|
||||
return "tag bracket";
|
||||
}
|
||||
} else if (ch == "&") {
|
||||
var ok;
|
||||
if (stream.eat("#")) {
|
||||
if (stream.eat("x")) {
|
||||
ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
|
||||
} else {
|
||||
ok = stream.eatWhile(/[\d]/) && stream.eat(";");
|
||||
}
|
||||
} else {
|
||||
ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
|
||||
}
|
||||
return ok ? "atom" : "error";
|
||||
} else {
|
||||
stream.eatWhile(/[^&<]/);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
inText.isInText = true;
|
||||
|
||||
function inTag(stream, state) {
|
||||
var ch = stream.next();
|
||||
if (ch == ">" || (ch == "/" && stream.eat(">"))) {
|
||||
state.tokenize = inText;
|
||||
type = ch == ">" ? "endTag" : "selfcloseTag";
|
||||
return "tag bracket";
|
||||
} else if (ch == "=") {
|
||||
type = "equals";
|
||||
return null;
|
||||
} else if (ch == "<") {
|
||||
state.tokenize = inText;
|
||||
state.state = baseState;
|
||||
state.tagName = state.tagStart = null;
|
||||
var next = state.tokenize(stream, state);
|
||||
return next ? next + " tag error" : "tag error";
|
||||
} else if (/[\'\"]/.test(ch)) {
|
||||
state.tokenize = inAttribute(ch);
|
||||
state.stringStartCol = stream.column();
|
||||
return state.tokenize(stream, state);
|
||||
} else {
|
||||
stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/);
|
||||
return "word";
|
||||
}
|
||||
}
|
||||
|
||||
function inAttribute(quote) {
|
||||
var closure = function(stream, state) {
|
||||
while (!stream.eol()) {
|
||||
if (stream.next() == quote) {
|
||||
state.tokenize = inTag;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return "string";
|
||||
};
|
||||
closure.isInAttribute = true;
|
||||
return closure;
|
||||
}
|
||||
|
||||
function inBlock(style, terminator) {
|
||||
return function(stream, state) {
|
||||
while (!stream.eol()) {
|
||||
if (stream.match(terminator)) {
|
||||
state.tokenize = inText;
|
||||
break;
|
||||
}
|
||||
stream.next();
|
||||
}
|
||||
return style;
|
||||
}
|
||||
}
|
||||
|
||||
function doctype(depth) {
|
||||
return function(stream, state) {
|
||||
var ch;
|
||||
while ((ch = stream.next()) != null) {
|
||||
if (ch == "<") {
|
||||
state.tokenize = doctype(depth + 1);
|
||||
return state.tokenize(stream, state);
|
||||
} else if (ch == ">") {
|
||||
if (depth == 1) {
|
||||
state.tokenize = inText;
|
||||
break;
|
||||
} else {
|
||||
state.tokenize = doctype(depth - 1);
|
||||
return state.tokenize(stream, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
return "meta";
|
||||
};
|
||||
}
|
||||
|
||||
function Context(state, tagName, startOfLine) {
|
||||
this.prev = state.context;
|
||||
this.tagName = tagName;
|
||||
this.indent = state.indented;
|
||||
this.startOfLine = startOfLine;
|
||||
if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent))
|
||||
this.noIndent = true;
|
||||
}
|
||||
function popContext(state) {
|
||||
if (state.context) state.context = state.context.prev;
|
||||
}
|
||||
function maybePopContext(state, nextTagName) {
|
||||
var parentTagName;
|
||||
while (true) {
|
||||
if (!state.context) {
|
||||
return;
|
||||
}
|
||||
parentTagName = state.context.tagName;
|
||||
if (!config.contextGrabbers.hasOwnProperty(parentTagName) ||
|
||||
!config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) {
|
||||
return;
|
||||
}
|
||||
popContext(state);
|
||||
}
|
||||
}
|
||||
|
||||
function baseState(type, stream, state) {
|
||||
if (type == "openTag") {
|
||||
state.tagStart = stream.column();
|
||||
return tagNameState;
|
||||
} else if (type == "closeTag") {
|
||||
return closeTagNameState;
|
||||
} else {
|
||||
return baseState;
|
||||
}
|
||||
}
|
||||
function tagNameState(type, stream, state) {
|
||||
if (type == "word") {
|
||||
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;
|
||||
}
|
||||
}
|
||||
function closeTagNameState(type, stream, state) {
|
||||
if (type == "word") {
|
||||
var tagName = stream.current();
|
||||
if (state.context && state.context.tagName != tagName &&
|
||||
config.implicitlyClosed.hasOwnProperty(state.context.tagName))
|
||||
popContext(state);
|
||||
if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) {
|
||||
setStyle = "tag";
|
||||
return closeState;
|
||||
} else {
|
||||
setStyle = "tag error";
|
||||
return closeStateErr;
|
||||
}
|
||||
} else if (config.allowMissingTagName && type == "endTag") {
|
||||
setStyle = "tag bracket";
|
||||
return closeState(type, stream, state);
|
||||
} else {
|
||||
setStyle = "error";
|
||||
return closeStateErr;
|
||||
}
|
||||
}
|
||||
|
||||
function closeState(type, _stream, state) {
|
||||
if (type != "endTag") {
|
||||
setStyle = "error";
|
||||
return closeState;
|
||||
}
|
||||
popContext(state);
|
||||
return baseState;
|
||||
}
|
||||
function closeStateErr(type, stream, state) {
|
||||
setStyle = "error";
|
||||
return closeState(type, stream, state);
|
||||
}
|
||||
|
||||
function attrState(type, _stream, state) {
|
||||
if (type == "word") {
|
||||
setStyle = "attribute";
|
||||
return attrEqState;
|
||||
} else if (type == "endTag" || type == "selfcloseTag") {
|
||||
var tagName = state.tagName, tagStart = state.tagStart;
|
||||
state.tagName = state.tagStart = null;
|
||||
if (type == "selfcloseTag" ||
|
||||
config.autoSelfClosers.hasOwnProperty(tagName)) {
|
||||
maybePopContext(state, tagName);
|
||||
} else {
|
||||
maybePopContext(state, tagName);
|
||||
state.context = new Context(state, tagName, tagStart == state.indented);
|
||||
}
|
||||
return baseState;
|
||||
}
|
||||
setStyle = "error";
|
||||
return attrState;
|
||||
}
|
||||
function attrEqState(type, stream, state) {
|
||||
if (type == "equals") return attrValueState;
|
||||
if (!config.allowMissing) setStyle = "error";
|
||||
return attrState(type, stream, state);
|
||||
}
|
||||
function attrValueState(type, stream, state) {
|
||||
if (type == "string") return attrContinuedState;
|
||||
if (type == "word" && config.allowUnquoted) {setStyle = "string"; return attrState;}
|
||||
setStyle = "error";
|
||||
return attrState(type, stream, state);
|
||||
}
|
||||
function attrContinuedState(type, stream, state) {
|
||||
if (type == "string") return attrContinuedState;
|
||||
return attrState(type, stream, state);
|
||||
}
|
||||
|
||||
return {
|
||||
startState: function(baseIndent) {
|
||||
var state = {tokenize: inText,
|
||||
state: baseState,
|
||||
indented: baseIndent || 0,
|
||||
tagName: null, tagStart: null,
|
||||
context: null}
|
||||
if (baseIndent != null) state.baseIndent = baseIndent
|
||||
return state
|
||||
},
|
||||
|
||||
token: function(stream, state) {
|
||||
if (!state.tagName && stream.sol())
|
||||
state.indented = stream.indentation();
|
||||
|
||||
if (stream.eatSpace()) return null;
|
||||
type = null;
|
||||
var style = state.tokenize(stream, state);
|
||||
if ((style || type) && style != "comment") {
|
||||
setStyle = null;
|
||||
state.state = state.state(type || style, stream, state);
|
||||
if (setStyle)
|
||||
style = setStyle == "error" ? style + " error" : setStyle;
|
||||
}
|
||||
return style;
|
||||
},
|
||||
|
||||
indent: function(state, textAfter, fullLine) {
|
||||
var context = state.context;
|
||||
// Indent multi-line strings (e.g. css).
|
||||
if (state.tokenize.isInAttribute) {
|
||||
if (state.tagStart == state.indented)
|
||||
return state.stringStartCol + 1;
|
||||
else
|
||||
return state.indented + indentUnit;
|
||||
}
|
||||
if (context && context.noIndent) return CodeMirror.Pass;
|
||||
if (state.tokenize != inTag && state.tokenize != inText)
|
||||
return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
|
||||
// Indent the starts of attribute names.
|
||||
if (state.tagName) {
|
||||
if (config.multilineTagIndentPastTag !== false)
|
||||
return state.tagStart + state.tagName.length + 2;
|
||||
else
|
||||
return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1);
|
||||
}
|
||||
if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
|
||||
var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter);
|
||||
if (tagAfter && tagAfter[1]) { // Closing tag spotted
|
||||
while (context) {
|
||||
if (context.tagName == tagAfter[2]) {
|
||||
context = context.prev;
|
||||
break;
|
||||
} else if (config.implicitlyClosed.hasOwnProperty(context.tagName)) {
|
||||
context = context.prev;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (tagAfter) { // Opening tag spotted
|
||||
while (context) {
|
||||
var grabbers = config.contextGrabbers[context.tagName];
|
||||
if (grabbers && grabbers.hasOwnProperty(tagAfter[2]))
|
||||
context = context.prev;
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (context && context.prev && !context.startOfLine)
|
||||
context = context.prev;
|
||||
if (context) return context.indent + indentUnit;
|
||||
else return state.baseIndent || 0;
|
||||
},
|
||||
|
||||
electricInput: /<\/[\s\w:]+>$/,
|
||||
blockCommentStart: "<!--",
|
||||
blockCommentEnd: "-->",
|
||||
|
||||
configuration: config.htmlMode ? "html" : "xml",
|
||||
helperType: config.htmlMode ? "html" : "xml",
|
||||
|
||||
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()
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
CodeMirror.defineMIME("text/xml", "xml");
|
||||
CodeMirror.defineMIME("application/xml", "xml");
|
||||
if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
|
||||
CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
|
||||
|
||||
});
|
||||
Reference in New Issue
Block a user