Unified · Split
| @@ -0,0 +1,11 @@ | ||
| 1 | +root = true | |
| 2 | + | |
| 3 | +[*] | |
| 4 | +charset = utf-8 | |
| 5 | +indent_style = space | |
| 6 | +indent_size = 2 | |
| 7 | +insert_final_newline = true | |
| 8 | +trim_trailing_whitespace = true | |
| 9 | + | |
| 10 | +[*.md] | |
| 11 | +trim_trailing_whitespace = false | |
| @@ -24,20 +24,18 @@ Want to contribute? Great. Please review the following guidelines carefully and · 23 unmodified lines | ||
| 24 | 24 | ## Requesting new features |
| 25 | 25 | |
| 26 | 26 | 1. Search for similar feature requests; someone may have already requested it. |
| 27 | −2. Make sure your feature fits DevDocs's [vision](https://github.com/Thibaut/devdocs/blob/master/README.md#vision). | |
| 27 | +2. Make sure your feature fits DevDocs's [vision](../README.md#vision). | |
| 28 | 28 | 3. Provide a clear and detailed explanation of the feature and why it's important to add it. |
| 29 | 29 | |
| 30 | −For general feedback and ideas, please use the [mailing list](https://groups.google.com/d/forum/devdocs). | |
| 31 | − | |
| 32 | 30 | ## Requesting new documentations |
| 33 | 31 | |
| 34 | −Please don't open issues to request new documentations. | |
| 32 | +Please don't open issues to request new documentations. | |
| 35 | 33 | Use the [Trello board](https://trello.com/b/6BmTulfx/devdocs-documentation) where everyone can vote. |
| 36 | 34 | |
| 37 | 35 | ## Contributing code and features |
| 38 | 36 | |
| 39 | 37 | 1. Search for existing issues; someone may already be working on a similar feature. |
| 40 | −2. Before embarking on any significant pull request, please open an issue describing the changes you intend to make. Otherwise you risk spending a lot of time working on something I may not want to merge. This also tells other contributors that you're working on the feature. | |
| 38 | +2. Before embarking on any significant pull request, please open an issue describing the changes you intend to make. Otherwise you risk spending a lot of time working on something we may not want to merge. This also tells other contributors that you're working on the feature. | |
| 41 | 39 | 3. Follow the [coding conventions](#coding-conventions). |
| 42 | 40 | 4. If you're modifying the Ruby code, include tests and ensure they pass. |
| 43 | 41 | 5. Try to keep your pull request small and simple. |
| @@ -46,7 +44,7 @@ Use the [Trello board](https://trello.com/b/6BmTulfx/devdocs-documentation) wher · 2 unmodified lines | ||
| 46 | 44 | |
| 47 | 45 | ## Contributing new documentations |
| 48 | 46 | |
| 49 | −See the [wiki](https://github.com/Thibaut/devdocs/wiki) to learn how to add new documentations. | |
| 47 | +See the [`docs` folder](https://github.com/freeCodeCamp/devdocs/tree/master/docs) to learn how to add new documentations. | |
| 50 | 48 | |
| 51 | 49 | **Important:** the documentation's license must permit alteration, redistribution and commercial use, and the documented software must be released under an open source license. Feel free to get in touch if you're not sure if a documentation meets those requirements. |
| 52 | 50 | |
| @@ -57,20 +55,12 @@ In addition to the [guidelines for contributing code](#contributing-code-and-fea · 4 unmodified lines | ||
| 57 | 55 | * Remove as much content and HTML markup as possible, particularly content not associated with any entry (e.g. introduction, changelog, etc.). |
| 58 | 56 | * Names must be as short as possible and unique across the documentation. |
| 59 | 57 | * The number of types (categories) should ideally be less than 100. |
| 60 | −* Don't modify the icon sprite. I'll do it after your pull request is merged. | |
| 61 | 58 | |
| 62 | 59 | ## Updating existing documentations |
| 63 | 60 | |
| 64 | 61 | Please don't submit a pull request updating the version number of a documentation, unless a change is required in the scraper and you've verified that it works. |
| 65 | 62 | |
| 66 | −To ask that an existing documentation be updated, please use the [Trello board](https://trello.com/c/2B0hmW7M/52-request-updates-here). | |
| 67 | − | |
| 68 | −## Other contributions | |
| 69 | − | |
| 70 | −Besides new docs and features, here are other ways you can contribute: | |
| 71 | − | |
| 72 | −* **Improve our copy.** English isn't my first language so if you notice grammatical or usage errors, feel free to submit a pull request — it'll be much appreciated. | |
| 73 | −* **Participate in the issue tracker.** Your opinion matters — feel free to add comments to existing issues. You're also welcome to participate to the [mailing list](https://groups.google.com/d/forum/devdocs). | |
| 63 | +To ask that an existing documentation be updated, first check the last two [documentation versions reports](https://github.com/freeCodeCamp/devdocs/issues?utf8=%E2%9C%93&q=Documentation+versions+report+is%3Aissue+author%3Adevdocs-bot+sort%3Acreated-desc). Only create an issue if the documentation has been wrongly marked as up-to-date. | |
| 74 | 64 | |
| 75 | 65 | ## Coding conventions |
| 76 | 66 | |
| @@ -80,4 +70,4 @@ Besides new docs and features, here are other ways you can contribute: · 3 unmodified lines | ||
| 80 | 70 | |
| 81 | 71 | ## Questions? |
| 82 | 72 | |
| 83 | −If you have any questions, please feel free to ask on the [mailing list](https://groups.google.com/d/forum/devdocs). | |
| 73 | +If you have any questions, please feel free to ask them on the contributor chat room on [Gitter](https://gitter.im/FreeCodeCamp/DevDocs). | |
| @@ -0,0 +1,7 @@ | ||
| 1 | +<!-- | |
| 2 | + Please read the contributing guidelines before opening an issue: | |
| 3 | + https://github.com/freeCodeCamp/devdocs/blob/master/.github/CONTRIBUTING.md | |
| 4 | + | |
| 5 | + Go here to request a new documentation: | |
| 6 | + https://trello.com/b/6BmTulfx/devdocs-documentation | |
| 7 | +--> | |
| @@ -0,0 +1,8 @@ | ||
| 1 | +daysUntilClose: 30 | |
| 2 | +responseRequiredLabel: needs-info | |
| 3 | +closeComment: > | |
| 4 | + This issue has been automatically closed because there has been no response | |
| 5 | + to our request for more information from the original author. With only the | |
| 6 | + information that’s currently in the issue, we don’t have enough information | |
| 7 | + to take action. Please comment if you have or find the answer we need so we | |
| 8 | + can investigate further. | |
| @@ -1,10 +1,9 @@ | ||
| 1 | 1 | .DS_Store |
| 2 | 2 | .bundle |
| 3 | −*.pxm | |
| 4 | −*.sketch | |
| 3 | +log | |
| 5 | 4 | tmp |
| 6 | 5 | public/assets |
| 7 | 6 | public/fonts |
| 8 | 7 | public/docs/**/* |
| 9 | −!public/docs/docs.json | |
| 10 | −!public/docs/**/index.json | |
| 8 | +docs/**/* | |
| 9 | +!docs/*.md | |
| @@ -1 +1 @@ | ||
| 1 | −2.4.2 | |
| 1 | +2.6.3 | |
| @@ -0,0 +1 @@ | ||
| 1 | +test | |
| @@ -1 +1,26 @@ | ||
| 1 | 1 | language: ruby |
| 2 | + | |
| 3 | +addons: | |
| 4 | + apt: | |
| 5 | + packages: | |
| 6 | + - libcurl4-openssl-dev | |
| 7 | + | |
| 8 | +cache: bundler | |
| 9 | + | |
| 10 | +before_install: | |
| 11 | + - "echo 'gem: --no-document' > ~/.gemrc" | |
| 12 | + - gem update --system | |
| 13 | + - gem install bundler | |
| 14 | + | |
| 15 | +script: | |
| 16 | + - if [ "$TRAVIS_EVENT_TYPE" != "cron" ]; then bundle exec rake; fi | |
| 17 | + - if [ "$TRAVIS_EVENT_TYPE" = "cron" ]; then bundle exec thor updates:check --github-token $GH_TOKEN --upload; fi | |
| 18 | + | |
| 19 | +deploy: | |
| 20 | + provider: heroku | |
| 21 | + app: devdocs | |
| 22 | + on: | |
| 23 | + branch: master | |
| 24 | + condition: $TRAVIS_EVENT_TYPE != cron | |
| 25 | + api_key: | |
| 26 | + secure: "4p1klvWJZSOImzFcKOduILjP93hlOlAhceWlYMKS4tU+TCFE8qTBzdKdFPSCsCgjB+YR9pBss+L0lJpVVMjSwFHXqpKe6EeUSltO2k7DFHfW7kXLUM/L0AfqXz+YXk76XUyZMhvOEbldPfaMaj10e8vgDOQCSHABDyK/4CU+hnI=" | |
| @@ -0,0 +1,2 @@ | ||
| 1 | + | |
| 2 | +> Our Code of Conduct is available here: <https://code-of-conduct.freecodecamp.org/> | |
| @@ -1,13 +1,13 @@ | ||
| 1 | −Copyright 2013-2017 Thibaut Courouble and other contributors | |
| 1 | +Copyright 2013-2019 Thibaut Courouble and other contributors | |
| 2 | 2 | |
| 3 | 3 | This Source Code Form is subject to the terms of the Mozilla Public |
| 4 | 4 | License, v. 2.0. If a copy of the MPL was not distributed with this |
| 5 | 5 | file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| 6 | 6 | |
| 7 | 7 | Please do not use the name DevDocs to endorse or promote products |
| 8 | −derived from this software without my permission, except as may be | |
| 9 | −necessary to comply with the notice/attribution requirements. | |
| 8 | +derived from this software without the maintainers' permission, except | |
| 9 | +as may be necessary to comply with the notice/attribution requirements. | |
| 10 | 10 | |
| 11 | −I also wish that any documentation file generated using this software | |
| 11 | +We also wish that any documentation file generated using this software | |
| 12 | 12 | be attributed to DevDocs. Let's be fair to all contributors by giving |
| 13 | 13 | credit where credit's due. Thanks. |
| @@ -1,9 +1,12 @@ | ||
| 1 | −FROM ruby:2.4.2 | |
| 1 | +FROM ruby:2.6.3 | |
| 2 | + | |
| 3 | +ENV LANG=C.UTF-8 | |
| 4 | +ENV ENABLE_SERVICE_WORKER=true | |
| 2 | 5 | |
| 3 | 6 | WORKDIR /devdocs |
| 4 | 7 | |
| 5 | 8 | RUN apt-get update && \ |
| 6 | − apt-get -y install git nodejs && \ | |
| 9 | + apt-get -y install git nodejs libcurl4 && \ | |
| 7 | 10 | gem install bundler && \ |
| 8 | 11 | rm -rf /var/lib/apt/lists/* |
| 9 | 12 | |
| @@ -1,10 +1,13 @@ | ||
| 1 | −FROM ruby:2.4.2-alpine | |
| 1 | +FROM ruby:2.6.3-alpine | |
| 2 | + | |
| 3 | +ENV LANG=C.UTF-8 | |
| 4 | +ENV ENABLE_SERVICE_WORKER=true | |
| 2 | 5 | |
| 3 | 6 | WORKDIR /devdocs |
| 4 | 7 | |
| 5 | 8 | COPY . /devdocs |
| 6 | 9 | |
| 7 | −RUN apk --update add nodejs build-base libstdc++ gzip git zlib-dev && \ | |
| 10 | +RUN apk --update add nodejs build-base libstdc++ gzip git zlib-dev libcurl && \ | |
| 8 | 11 | gem install bundler && \ |
| 9 | 12 | bundle install --system --without test && \ |
| 10 | 13 | thor docs:download --all && \ |
| @@ -1,16 +1,20 @@ | ||
| 1 | 1 | source 'https://rubygems.org' |
| 2 | −ruby '2.4.2' | |
| 2 | +ruby '2.6.3' | |
| 3 | 3 | |
| 4 | 4 | gem 'rake' |
| 5 | 5 | gem 'thor' |
| 6 | −gem 'pry', '~> 0.11.0' | |
| 7 | −gem 'activesupport', '~> 5.1', require: false | |
| 6 | +gem 'pry', '~> 0.12.0' | |
| 7 | +gem 'activesupport', '~> 5.2', require: false | |
| 8 | 8 | gem 'yajl-ruby', require: false |
| 9 | +gem 'html-pipeline' | |
| 10 | +gem 'typhoeus' | |
| 11 | +gem 'nokogiri' | |
| 9 | 12 | |
| 10 | 13 | group :app do |
| 11 | 14 | gem 'rack' |
| 12 | 15 | gem 'sinatra' |
| 13 | 16 | gem 'sinatra-contrib' |
| 17 | + gem 'rack-ssl-enforcer' | |
| 14 | 18 | gem 'thin' |
| 15 | 19 | gem 'sprockets' |
| 16 | 20 | gem 'sprockets-helpers' |
| @@ -18,10 +22,13 @@ group :app do · 1 unmodified line | ||
| 18 | 22 | gem 'browser' |
| 19 | 23 | gem 'sass' |
| 20 | 24 | gem 'coffee-script' |
| 25 | + gem 'chunky_png' | |
| 26 | + gem 'sprockets-sass' | |
| 21 | 27 | end |
| 22 | 28 | |
| 23 | 29 | group :production do |
| 24 | 30 | gem 'uglifier' |
| 31 | + gem 'newrelic_rpm' | |
| 25 | 32 | end |
| 26 | 33 | |
| 27 | 34 | group :development do |
| @@ -29,14 +36,13 @@ group :development do · 1 unmodified line | ||
| 29 | 36 | end |
| 30 | 37 | |
| 31 | 38 | group :docs do |
| 32 | − gem 'typhoeus' | |
| 33 | − gem 'nokogiri' | |
| 34 | − gem 'html-pipeline' | |
| 35 | 39 | gem 'image_optim' |
| 36 | 40 | gem 'image_optim_pack', platforms: :ruby |
| 37 | 41 | gem 'progress_bar', require: false |
| 38 | 42 | gem 'unix_utils', require: false |
| 39 | 43 | gem 'tty-pager', require: false |
| 44 | + gem 'net-sftp', '>= 2.1.3.rc2', require: false | |
| 45 | + gem 'terminal-table', require: false | |
| 40 | 46 | end |
| 41 | 47 | |
| 42 | 48 | group :test do |
| @@ -1,139 +1,156 @@ | ||
| 1 | 1 | GEM |
| 2 | 2 | remote: https://rubygems.org/ |
| 3 | 3 | specs: |
| 4 | − activesupport (5.1.4) | |
| 4 | + activesupport (5.2.3) | |
| 5 | 5 | concurrent-ruby (~> 1.0, >= 1.0.2) |
| 6 | − i18n (~> 0.7) | |
| 6 | + i18n (>= 0.7, < 2) | |
| 7 | 7 | minitest (~> 5.1) |
| 8 | 8 | tzinfo (~> 1.1) |
| 9 | − backports (3.9.1) | |
| 10 | − better_errors (2.3.0) | |
| 9 | + backports (3.15.0) | |
| 10 | + better_errors (2.5.1) | |
| 11 | 11 | coderay (>= 1.0.0) |
| 12 | 12 | erubi (>= 1.0.0) |
| 13 | 13 | rack (>= 0.9.0) |
| 14 | − browser (2.5.1) | |
| 14 | + browser (2.6.1) | |
| 15 | + chunky_png (1.3.11) | |
| 15 | 16 | coderay (1.1.2) |
| 16 | 17 | coffee-script (2.4.1) |
| 17 | 18 | coffee-script-source |
| 18 | 19 | execjs |
| 19 | 20 | coffee-script-source (1.12.2) |
| 20 | − concurrent-ruby (1.0.5) | |
| 21 | − daemons (1.2.4) | |
| 22 | − erubi (1.6.1) | |
| 23 | − ethon (0.10.1) | |
| 21 | + concurrent-ruby (1.1.5) | |
| 22 | + daemons (1.3.1) | |
| 23 | + erubi (1.8.0) | |
| 24 | + ethon (0.12.0) | |
| 24 | 25 | ffi (>= 1.3.0) |
| 25 | − eventmachine (1.2.5) | |
| 26 | + eventmachine (1.2.7) | |
| 26 | 27 | execjs (2.7.0) |
| 27 | − exifr (1.3.2) | |
| 28 | − ffi (1.9.18) | |
| 29 | − fspath (3.1.0) | |
| 30 | − highline (1.7.8) | |
| 31 | − html-pipeline (2.7.1) | |
| 28 | + exifr (1.3.6) | |
| 29 | + ffi (1.11.1) | |
| 30 | + fspath (3.1.2) | |
| 31 | + highline (2.0.2) | |
| 32 | + html-pipeline (2.11.1) | |
| 32 | 33 | activesupport (>= 2) |
| 33 | 34 | nokogiri (>= 1.4) |
| 34 | − i18n (0.8.6) | |
| 35 | − image_optim (0.25.0) | |
| 35 | + i18n (1.6.0) | |
| 36 | + concurrent-ruby (~> 1.0) | |
| 37 | + image_optim (0.26.5) | |
| 36 | 38 | exifr (~> 1.2, >= 1.2.2) |
| 37 | 39 | fspath (~> 3.0) |
| 38 | − image_size (~> 1.5) | |
| 40 | + image_size (>= 1.5, < 3) | |
| 39 | 41 | in_threads (~> 1.3) |
| 40 | 42 | progress (~> 3.0, >= 3.0.1) |
| 41 | − image_optim_pack (0.5.0.20171001) | |
| 43 | + image_optim_pack (0.5.6) | |
| 42 | 44 | fspath (>= 2.1, < 4) |
| 43 | 45 | image_optim (~> 0.19) |
| 44 | − image_size (1.5.0) | |
| 45 | − in_threads (1.4.0) | |
| 46 | − method_source (0.9.0) | |
| 47 | − mini_portile2 (2.3.0) | |
| 48 | − minitest (5.10.3) | |
| 49 | − multi_json (1.12.2) | |
| 50 | − mustermann (1.0.1) | |
| 51 | − nokogiri (1.8.1) | |
| 52 | − mini_portile2 (~> 2.3.0) | |
| 46 | + image_size (2.0.2) | |
| 47 | + in_threads (1.5.3) | |
| 48 | + method_source (0.9.2) | |
| 49 | + mini_portile2 (2.4.0) | |
| 50 | + minitest (5.11.3) | |
| 51 | + multi_json (1.13.1) | |
| 52 | + mustermann (1.0.3) | |
| 53 | + net-sftp (3.0.0.beta1) | |
| 54 | + net-ssh (>= 5.0.0, < 6.0.0) | |
| 55 | + net-ssh (5.2.0) | |
| 56 | + newrelic_rpm (6.5.0.357) | |
| 57 | + nokogiri (1.10.3) | |
| 58 | + mini_portile2 (~> 2.4.0) | |
| 53 | 59 | options (2.3.2) |
| 54 | − progress (3.3.1) | |
| 55 | − progress_bar (1.1.0) | |
| 56 | − highline (~> 1.6) | |
| 60 | + progress (3.5.2) | |
| 61 | + progress_bar (1.3.0) | |
| 62 | + highline (>= 1.6, < 3) | |
| 57 | 63 | options (~> 2.3.0) |
| 58 | − pry (0.11.1) | |
| 64 | + pry (0.12.2) | |
| 59 | 65 | coderay (~> 1.1.0) |
| 60 | 66 | method_source (~> 0.9.0) |
| 61 | − rack (2.0.3) | |
| 62 | − rack-protection (2.0.0) | |
| 67 | + rack (2.0.7) | |
| 68 | + rack-protection (2.0.5) | |
| 63 | 69 | rack |
| 64 | − rack-test (0.7.0) | |
| 70 | + rack-ssl-enforcer (0.2.9) | |
| 71 | + rack-test (1.1.0) | |
| 65 | 72 | rack (>= 1.0, < 3) |
| 66 | − rake (12.1.0) | |
| 67 | − rb-fsevent (0.10.2) | |
| 68 | − rb-inotify (0.9.10) | |
| 69 | − ffi (>= 0.5.0, < 2) | |
| 73 | + rake (12.3.2) | |
| 74 | + rb-fsevent (0.10.3) | |
| 75 | + rb-inotify (0.10.0) | |
| 76 | + ffi (~> 1.0) | |
| 70 | 77 | rr (1.2.1) |
| 71 | − sass (3.5.2) | |
| 78 | + sass (3.7.4) | |
| 72 | 79 | sass-listen (~> 4.0.0) |
| 73 | 80 | sass-listen (4.0.0) |
| 74 | 81 | rb-fsevent (~> 0.9, >= 0.9.4) |
| 75 | 82 | rb-inotify (~> 0.9, >= 0.9.7) |
| 76 | − sinatra (2.0.0) | |
| 83 | + sinatra (2.0.5) | |
| 77 | 84 | mustermann (~> 1.0) |
| 78 | 85 | rack (~> 2.0) |
| 79 | − rack-protection (= 2.0.0) | |
| 86 | + rack-protection (= 2.0.5) | |
| 80 | 87 | tilt (~> 2.0) |
| 81 | − sinatra-contrib (2.0.0) | |
| 82 | − backports (>= 2.0) | |
| 88 | + sinatra-contrib (2.0.5) | |
| 89 | + backports (>= 2.8.2) | |
| 83 | 90 | multi_json |
| 84 | 91 | mustermann (~> 1.0) |
| 85 | − rack-protection (= 2.0.0) | |
| 86 | − sinatra (= 2.0.0) | |
| 92 | + rack-protection (= 2.0.5) | |
| 93 | + sinatra (= 2.0.5) | |
| 87 | 94 | tilt (>= 1.3, < 3) |
| 88 | − sprockets (3.7.1) | |
| 95 | + sprockets (3.7.2) | |
| 89 | 96 | concurrent-ruby (~> 1.0) |
| 90 | 97 | rack (> 1, < 3) |
| 91 | 98 | sprockets-helpers (1.2.1) |
| 92 | 99 | sprockets (>= 2.2) |
| 100 | + sprockets-sass (2.0.0.beta2) | |
| 101 | + sprockets (>= 2.0, < 4.0) | |
| 102 | + strings (0.1.5) | |
| 103 | + strings-ansi (~> 0.1) | |
| 104 | + unicode-display_width (~> 1.5) | |
| 105 | + unicode_utils (~> 1.4) | |
| 106 | + strings-ansi (0.1.0) | |
| 107 | + terminal-table (1.8.0) | |
| 108 | + unicode-display_width (~> 1.1, >= 1.1.1) | |
| 93 | 109 | thin (1.7.2) |
| 94 | 110 | daemons (~> 1.0, >= 1.0.9) |
| 95 | 111 | eventmachine (~> 1.0, >= 1.0.4) |
| 96 | 112 | rack (>= 1, < 3) |
| 97 | − thor (0.20.0) | |
| 113 | + thor (0.20.3) | |
| 98 | 114 | thread_safe (0.3.6) |
| 99 | − tilt (2.0.8) | |
| 100 | − tty-pager (0.9.0) | |
| 101 | − tty-screen (~> 0.5.0) | |
| 102 | − tty-which (~> 0.3.0) | |
| 103 | − verse (~> 0.5.0) | |
| 104 | − tty-screen (0.5.0) | |
| 105 | − tty-which (0.3.0) | |
| 106 | − typhoeus (1.3.0) | |
| 115 | + tilt (2.0.9) | |
| 116 | + tty-pager (0.12.1) | |
| 117 | + strings (~> 0.1.4) | |
| 118 | + tty-screen (~> 0.6) | |
| 119 | + tty-which (~> 0.4) | |
| 120 | + tty-screen (0.7.0) | |
| 121 | + tty-which (0.4.1) | |
| 122 | + typhoeus (1.3.1) | |
| 107 | 123 | ethon (>= 0.9.0) |
| 108 | − tzinfo (1.2.3) | |
| 124 | + tzinfo (1.2.5) | |
| 109 | 125 | thread_safe (~> 0.1) |
| 110 | − uglifier (3.2.0) | |
| 126 | + uglifier (4.1.20) | |
| 111 | 127 | execjs (>= 0.3.0, < 3) |
| 112 | − unicode-display_width (1.1.3) | |
| 128 | + unicode-display_width (1.6.0) | |
| 113 | 129 | unicode_utils (1.4.0) |
| 114 | 130 | unix_utils (0.0.15) |
| 115 | − verse (0.5.0) | |
| 116 | − unicode-display_width (~> 1.1.0) | |
| 117 | − unicode_utils (~> 1.4.0) | |
| 118 | − yajl-ruby (1.3.0) | |
| 131 | + yajl-ruby (1.4.1) | |
| 119 | 132 | |
| 120 | 133 | PLATFORMS |
| 121 | 134 | ruby |
| 122 | 135 | |
| 123 | 136 | DEPENDENCIES |
| 124 | − activesupport (~> 5.1) | |
| 137 | + activesupport (~> 5.2) | |
| 125 | 138 | better_errors |
| 126 | 139 | browser |
| 140 | + chunky_png | |
| 127 | 141 | coffee-script |
| 128 | 142 | erubi |
| 129 | 143 | html-pipeline |
| 130 | 144 | image_optim |
| 131 | 145 | image_optim_pack |
| 132 | 146 | minitest |
| 147 | + net-sftp (>= 2.1.3.rc2) | |
| 148 | + newrelic_rpm | |
| 133 | 149 | nokogiri |
| 134 | 150 | progress_bar |
| 135 | − pry (~> 0.11.0) | |
| 151 | + pry (~> 0.12.0) | |
| 136 | 152 | rack |
| 153 | + rack-ssl-enforcer | |
| 137 | 154 | rack-test |
| 138 | 155 | rake |
| 139 | 156 | rr |
| @@ -142,6 +159,8 @@ DEPENDENCIES · 2 unmodified lines | ||
| 142 | 159 | sinatra-contrib |
| 143 | 160 | sprockets |
| 144 | 161 | sprockets-helpers |
| 162 | + sprockets-sass | |
| 163 | + terminal-table | |
| 145 | 164 | thin |
| 146 | 165 | thor |
| 147 | 166 | tty-pager |
| @@ -151,7 +170,7 @@ DEPENDENCIES · 3 unmodified lines | ||
| 151 | 170 | yajl-ruby |
| 152 | 171 | |
| 153 | 172 | RUBY VERSION |
| 154 | − ruby 2.4.2p198 | |
| 173 | + ruby 2.6.3p62 | |
| 155 | 174 | |
| 156 | 175 | BUNDLED WITH |
| 157 | − 1.15.4 | |
| 176 | + 2.0.2 | |
| @@ -1,7 +0,0 @@ | ||
| 1 | −<!-- | |
| 2 | − Please read the contributing guidelines before opening an issue: | |
| 3 | − https://github.com/Thibaut/devdocs/blob/master/CONTRIBUTING.md | |
| 4 | − | |
| 5 | − To request a new documentation, or an update of an existing documentation, go here: | |
| 6 | − https://trello.com/b/6BmTulfx/devdocs-documentation | |
| 7 | −--> | |
| @@ -0,0 +1,11 @@ | ||
| 1 | +If you’re adding a new scraper, please ensure that you have: | |
| 2 | + | |
| 3 | +- [ ] Tested the scraper on a local copy of DevDocs | |
| 4 | +- [ ] Ensured that the docs are styled similarly to other docs on DevDocs | |
| 5 | +<!-- If the docs don’t have an icon, delete the next four items: --> | |
| 6 | +- [ ] Added these files to the <code>public/icons/*your_scraper_name*/</code> directory: | |
| 7 | + - [ ] `16.png`: a 16×16 pixel icon for the doc | |
| 8 | + - [ ] `16@2x.png`: a 32×32 pixel icon for the doc | |
| 9 | + - [ ] `SOURCE`: A text file containing the URL to the page the image can be found on or the URL of the original image itself | |
| 10 | + | |
| 11 | +<!-- Replace the `[ ]` with a `[x]` once you’ve completed each step. --> | |
| @@ -0,0 +1 @@ | ||
| 1 | +web: bundle exec rackup config.ru -p $PORT | |
| @@ -1,29 +1,27 @@ | ||
| 1 | −# [DevDocs](http://devdocs.io) [](https://travis-ci.org/Thibaut/devdocs) | |
| 1 | +# [DevDocs](https://devdocs.io) — API Documentation Browser [](https://travis-ci.org/freeCodeCamp/devdocs) | |
| 2 | 2 | |
| 3 | −DevDocs combines multiple API documentations in a fast, organized, and searchable interface. | |
| 3 | +DevDocs combines multiple developer documentations in a clean and organized web UI with instant search, offline support, mobile version, dark theme, keyboard shortcuts, and more. | |
| 4 | 4 | |
| 5 | −* Created by [Thibaut Courouble](https://thibaut.me) | |
| 5 | +DevDocs was created by [Thibaut Courouble](https://thibaut.me) and is operated by [freeCodeCamp](https://www.freecodecamp.org). | |
| 6 | 6 | |
| 7 | 7 | Keep track of development news: |
| 8 | 8 | |
| 9 | −* Watch the repository on [GitHub](https://github.com/Thibaut/devdocs/subscription) | |
| 9 | +* Join the contributor chat room on [Gitter](https://gitter.im/FreeCodeCamp/DevDocs) | |
| 10 | +* Watch the repository on [GitHub](https://github.com/freeCodeCamp/devdocs/subscription) | |
| 10 | 11 | * Follow [@DevDocs](https://twitter.com/DevDocs) on Twitter |
| 11 | −* Join the [mailing list](https://groups.google.com/d/forum/devdocs) | |
| 12 | 12 | |
| 13 | −DevDocs is free and open source. If you like it, please consider supporting my work on [Gratipay](https://gratipay.com/devdocs/). Thanks! | |
| 14 | − | |
| 15 | −**Table of Contents:** [Quick Start](#quick-start) · [Vision](#vision) · [App](#app) · [Scraper](#scraper) · [Commands](#available-commands) · [Contributing](#contributing) · [License](#copyright--license) · [Questions?](#questions) | |
| 13 | +**Table of Contents:** [Quick Start](#quick-start) · [Vision](#vision) · [App](#app) · [Scraper](#scraper) · [Commands](#available-commands) · [Contributing](#contributing) · [Documentation](#documentation) · [Plugins and Extensions](#plugins-and-extensions) · [License](#copyright--license) · [Questions?](#questions) | |
| 16 | 14 | |
| 17 | 15 | ## Quick Start |
| 18 | 16 | |
| 19 | −Unless you wish to contribute to the project, I recommend using the hosted version at [devdocs.io](http://devdocs.io). It's up-to-date and works offline out-of-the-box. | |
| 17 | +Unless you wish to contribute to the project, we recommend using the hosted version at [devdocs.io](https://devdocs.io). It's up-to-date and works offline out-of-the-box. | |
| 20 | 18 | |
| 21 | 19 | DevDocs is made of two pieces: a Ruby scraper that generates the documentation and metadata, and a JavaScript app powered by a small Sinatra app. |
| 22 | 20 | |
| 23 | −DevDocs requires Ruby 2.4.2, libcurl, and a JavaScript runtime supported by [ExecJS](https://github.com/rails/execjs#readme) (included in OS X and Windows; [Node.js](https://nodejs.org/en/) on Linux). Once you have these installed, run the following commands: | |
| 21 | +DevDocs requires Ruby 2.6.0, libcurl, and a JavaScript runtime supported by [ExecJS](https://github.com/rails/execjs#readme) (included in OS X and Windows; [Node.js](https://nodejs.org/en/) on Linux). Once you have these installed, run the following commands: | |
| 24 | 22 | |
| 25 | 23 | ``` |
| 26 | −git clone https://github.com/Thibaut/devdocs.git && cd devdocs | |
| 24 | +git clone https://github.com/freeCodeCamp/devdocs.git && cd devdocs | |
| 27 | 25 | gem install bundler |
| 28 | 26 | bundle install |
| 29 | 27 | bundle exec thor docs:download --default |
| @@ -34,13 +32,13 @@ Finally, point your browser at [localhost:9292](http://localhost:9292) (the firs · 4 unmodified lines | ||
| 34 | 32 | |
| 35 | 33 | The `thor docs:download` command is used to download pre-generated documentations from DevDocs's servers (e.g. `thor docs:download html css`). You can see the list of available documentations and versions by running `thor docs:list`. To update all downloaded documentations, run `thor docs:download --installed`. |
| 36 | 34 | |
| 37 | −**Note:** there is currently no update mechanism other than `git pull origin master` to update the code and `thor docs:download --installed` to download the latest version of the docs. To stay informed about new releases, be sure to [watch](https://github.com/Thibaut/devdocs/subscription) this repository. | |
| 35 | +**Note:** there is currently no update mechanism other than `git pull origin master` to update the code and `thor docs:download --installed` to download the latest version of the docs. To stay informed about new releases, be sure to [watch](https://github.com/freeCodeCamp/devdocs/subscription) this repository. | |
| 38 | 36 | |
| 39 | 37 | Alternatively, DevDocs may be started as a Docker container: |
| 40 | 38 | |
| 41 | 39 | ``` |
| 42 | 40 | # First, build the image |
| 43 | −git clone https://github.com/Thibaut/devdocs.git && cd devdocs | |
| 41 | +git clone https://github.com/freeCodeCamp/devdocs.git && cd devdocs | |
| 44 | 42 | docker build -t thibaut/devdocs . |
| 45 | 43 | |
| 46 | 44 | # Finally, start a DevDocs container (access http://localhost:9292) |
| @@ -61,18 +59,14 @@ The web app is all client-side JavaScript, written in [CoffeeScript](http://coff · 14 unmodified lines | ||
| 61 | 59 | |
| 62 | 60 | Many of the code's design decisions were driven by the fact that the app uses XHR to load content directly into the main frame. This includes stripping the original documents of most of their HTML markup (e.g. scripts and stylesheets) to avoid polluting the main frame, and prefixing all CSS class names with an underscore to prevent conflicts. |
| 63 | 61 | |
| 64 | −Another driving factor is performance and the fact that everything happens in the browser. `applicationCache` (which comes with its own set of constraints) and `localStorage` are used to speed up the boot time, while memory consumption is kept in check by allowing the user to pick his/her own set of documentations. The search algorithm is kept simple because it needs to be fast even searching through 100,000 strings. | |
| 62 | +Another driving factor is performance and the fact that everything happens in the browser. A service worker (which comes with its own set of constraints) and `localStorage` are used to speed up the boot time, while memory consumption is kept in check by allowing the user to pick his/her own set of documentations. The search algorithm is kept simple because it needs to be fast even searching through 100,000 strings. | |
| 65 | 63 | |
| 66 | 64 | DevDocs being a developer tool, the browser requirements are high: |
| 67 | 65 | |
| 68 | −1. On the desktop: | |
| 69 | − * Recent version of Chrome, Firefox, or Opera | |
| 70 | − * Safari 8+ | |
| 71 | − * IE / Edge 10+ | |
| 72 | −2. On mobile: | |
| 73 | − * iOS 8+ | |
| 74 | − * Android 4.1+ | |
| 75 | − * Windows Phone 8+ | |
| 66 | +* Recent versions of Firefox, Chrome, or Opera | |
| 67 | +* Safari 11.1+ | |
| 68 | +* Edge 17+ | |
| 69 | +* iOS 11.3+ | |
| 76 | 70 | |
| 77 | 71 | This allows the code to take advantage of the latest DOM and HTML5 APIs and make developing DevDocs a lot more fun! |
| 78 | 72 | |
| @@ -89,12 +83,13 @@ Modifications made to each document include: · 10 unmodified lines | ||
| 89 | 83 | * replacing all external (not scraped) URLs with their fully qualified counterpart |
| 90 | 84 | * replacing all internal (scraped) URLs with their unqualified and relative counterpart |
| 91 | 85 | * adding content, such as a title and link to the original document |
| 86 | +* ensuring correct syntax highlighting using [Prism](http://prismjs.com/) | |
| 92 | 87 | |
| 93 | 88 | These modifications are applied via a set of filters using the [HTML::Pipeline](https://github.com/jch/html-pipeline) library. Each scraper includes filters specific to itself, one of which is tasked with figuring out the pages' metadata. |
| 94 | 89 | |
| 95 | 90 | The end result is a set of normalized HTML partials and two JSON files (index + offline data). Because the index files are loaded separately by the [app](#app) following the user's preferences, the scraper also creates a JSON manifest file containing information about the documentations currently available on the system (such as their name, version, update date, etc.). |
| 96 | 91 | |
| 97 | −More information about scrapers and filters is available on the [wiki](https://github.com/Thibaut/devdocs/wiki). | |
| 92 | +More information about [scrapers](./docs/scraper-reference.md) and [filters](./docs/filter-reference.md) is available in the `docs` folder. | |
| 98 | 93 | |
| 99 | 94 | ## Available Commands |
| 100 | 95 | |
| @@ -134,20 +129,45 @@ If multiple versions of Ruby are installed on your system, commands must be run · 33 unmodified lines | ||
| 134 | 129 | |
| 135 | 130 | ## Contributing |
| 136 | 131 | |
| 137 | −Contributions are welcome. Please read the [contributing guidelines](https://github.com/Thibaut/devdocs/blob/master/CONTRIBUTING.md). | |
| 138 | − | |
| 139 | −DevDocs's own documentation is available on the [wiki](https://github.com/Thibaut/devdocs/wiki). | |
| 132 | +Contributions are welcome. Please read the [contributing guidelines](./.github/CONTRIBUTING.md). | |
| 133 | + | |
| 134 | +## Documentation | |
| 135 | + | |
| 136 | +* [Adding documentations to DevDocs](./docs/adding-docs.md) | |
| 137 | +* [Scraper Reference](./docs/scraper-reference.md) | |
| 138 | +* [Filter Reference](./docs/filter-reference.md) | |
| 139 | +* [Maintainers’ Guide](./docs/maintainers.md) | |
| 140 | + | |
| 141 | +## Plugins and Extensions | |
| 142 | + | |
| 143 | +* [Chrome web app](https://chrome.google.com/webstore/detail/devdocs/mnfehgbmkapmjnhcnbodoamcioleeooe) | |
| 144 | +* [Ubuntu Touch app](https://uappexplorer.com/app/devdocsunofficial.berkes) | |
| 145 | +* [Sublime Text plugin](https://sublime.wbond.net/packages/DevDocs) | |
| 146 | +* [Atom plugin](https://atom.io/packages/devdocs) | |
| 147 | +* [Brackets extension](https://github.com/gruehle/dev-docs-viewer) | |
| 148 | +* [Fluid](http://fluidapp.com) for turning DevDocs into a real OS X app | |
| 149 | +* [GTK shell / Vim integration](https://github.com/naquad/devdocs-shell) | |
| 150 | +* [Emacs lookup](https://github.com/skeeto/devdocs-lookup) | |
| 151 | +* [Alfred Workflow](https://github.com/yannickglt/alfred-devdocs) | |
| 152 | +* [Vim search plugin with Devdocs in its defaults](https://github.com/waiting-for-dev/vim-www) Just set `let g:www_shortcut_engines = { 'devdocs': ['Devdocs', '<leader>dd'] }` to have a `:Devdocs` command and a `<leader>dd` mapping. | |
| 153 | +* [Visual Studio Code plugin](https://marketplace.visualstudio.com/items?itemName=akfish.vscode-devdocs ) (1) | |
| 154 | +* [Visual Studio Code plugin](https://marketplace.visualstudio.com/items?itemName=deibit.devdocs) (2) | |
| 155 | +* [Desktop application](https://github.com/egoist/devdocs-desktop) | |
| 156 | +* [Doc Browser](https://github.com/qwfy/doc-browser) is a native Linux app that supports DevDocs docsets | |
| 157 | +* [GNOME Application](https://github.com/hardpixel/devdocs-desktop) GTK3 application with search integrated in headerbar | |
| 158 | +* [macOS Application](https://github.com/dteoh/devdocs-macos) | |
| 159 | +* [Android Application](https://github.com/Merith-TK/devdocs_webapp_kotlin) is a fully working, advanced WebView | |
| 140 | 160 | |
| 141 | 161 | ## Copyright / License |
| 142 | 162 | |
| 143 | −Copyright 2013-2017 Thibaut Courouble and [other contributors](https://github.com/Thibaut/devdocs/graphs/contributors) | |
| 163 | +Copyright 2013-2019 Thibaut Courouble and [other contributors](https://github.com/freeCodeCamp/devdocs/graphs/contributors) | |
| 144 | 164 | |
| 145 | −This software is licensed under the terms of the Mozilla Public License v2.0. See the [COPYRIGHT](https://github.com/Thibaut/devdocs/blob/master/COPYRIGHT) and [LICENSE](https://github.com/Thibaut/devdocs/blob/master/LICENSE) files. | |
| 165 | +This software is licensed under the terms of the Mozilla Public License v2.0. See the [COPYRIGHT](./COPYRIGHT) and [LICENSE](./LICENSE) files. | |
| 146 | 166 | |
| 147 | −Please do not use the name DevDocs to endorse or promote products derived from this software without my permission, except as may be necessary to comply with the notice/attribution requirements. | |
| 167 | +Please do not use the name DevDocs to endorse or promote products derived from this software without the maintainers' permission, except as may be necessary to comply with the notice/attribution requirements. | |
| 148 | 168 | |
| 149 | −I also wish that any documentation file generated using this software be attributed to DevDocs. Let's be fair to all contributors by giving credit where credit's due. Thanks! | |
| 169 | +We also wish that any documentation file generated using this software be attributed to DevDocs. Let's be fair to all contributors by giving credit where credit's due. Thanks! | |
| 150 | 170 | |
| 151 | 171 | ## Questions? |
| 152 | 172 | |
| 153 | −If you have any questions, please feel free to ask them on the [mailing list](https://groups.google.com/d/forum/devdocs). | |
| 173 | +If you have any questions, please feel free to ask them on the contributor chat room on [Gitter](https://gitter.im/FreeCodeCamp/DevDocs). | |
| @@ -3,6 +3,7 @@ · 2 unmodified lines | ||
| 3 | 3 | require 'bundler/setup' |
| 4 | 4 | require 'thor' |
| 5 | 5 | |
| 6 | +Bundler.require :default | |
| 6 | 7 | $LOAD_PATH.unshift 'lib' |
| 7 | 8 | |
| 8 | 9 | task :default do |
| @@ -13,6 +14,9 @@ end · 4 unmodified lines | ||
| 13 | 14 | namespace :assets do |
| 14 | 15 | desc 'Compile all assets' |
| 15 | 16 | task :precompile do |
| 17 | + load 'tasks/docs.thor' | |
| 18 | + DocsCLI.new.prepare_deploy | |
| 19 | + | |
| 16 | 20 | load 'tasks/assets.thor' |
| 17 | 21 | AssetsCLI.new.compile |
| 18 | 22 | end |
| @@ -0,0 +1 @@ | ||
| 1 | +sprites/**/* | |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
| @@ -13,10 +13,12 @@ · 12 unmodified lines | ||
| 13 | 13 | |
| 14 | 14 | @el = $('._app') |
| 15 | 15 | @localStorage = new LocalStorageStore |
| 16 | − @appCache = new app.AppCache if app.AppCache.isEnabled() | |
| 16 | + @serviceWorker = new app.ServiceWorker if app.ServiceWorker.isEnabled() | |
| 17 | 17 | @settings = new app.Settings |
| 18 | 18 | @db = new app.DB() |
| 19 | 19 | |
| 20 | + @settings.initLayout() | |
| 21 | + | |
| 20 | 22 | @docs = new app.collections.Docs |
| 21 | 23 | @disabledDocs = new app.collections.Docs |
| 22 | 24 | @entries = new app.collections.Entries |
| @@ -76,7 +78,7 @@ · 53 unmodified lines | ||
| 76 | 78 | .install() |
| 77 | 79 | @previousErrorHandler = onerror |
| 78 | 80 | window.onerror = @onWindowError.bind(@) |
| 79 | − CookieStore.onBlocked = @onCookieBlocked | |
| 81 | + CookiesStore.onBlocked = @onCookieBlocked | |
| 80 | 82 | return |
| 81 | 83 | |
| 82 | 84 | bootOne: -> |
| @@ -138,7 +140,10 @@ · 55 unmodified lines | ||
| 138 | 140 | @docs.sort() |
| 139 | 141 | @initDoc(doc) |
| 140 | 142 | @saveDocs() |
| 141 | − _onSuccess() | |
| 143 | + if app.settings.get('autoInstall') | |
| 144 | + doc.install(_onSuccess, onError) | |
| 145 | + else | |
| 146 | + _onSuccess() | |
| 142 | 147 | return |
| 143 | 148 | |
| 144 | 149 | doc.load onSuccess, onError, writeCache: true |
| @@ -147,7 +152,7 @@ · 2 unmodified lines | ||
| 147 | 152 | saveDocs: -> |
| 148 | 153 | @settings.setDocs(doc.slug for doc in @docs.all()) |
| 149 | 154 | @db.migrate() |
| 150 | − @appCache?.updateInBackground() | |
| 155 | + @serviceWorker?.updateInBackground() | |
| 151 | 156 | |
| 152 | 157 | welcomeBack: -> |
| 153 | 158 | visitCount = @settings.get('count') |
| @@ -157,17 +162,24 @@ · 3 unmodified lines | ||
| 157 | 162 | new app.views.Updates() |
| 158 | 163 | @updateChecker = new app.UpdateChecker() |
| 159 | 164 | |
| 165 | + reboot: -> | |
| 166 | + if location.pathname isnt '/' and location.pathname isnt '/settings' | |
| 167 | + window.location = "/##{location.pathname}" | |
| 168 | + else | |
| 169 | + window.location = '/' | |
| 170 | + return | |
| 171 | + | |
| 160 | 172 | reload: -> |
| 161 | 173 | @docs.clearCache() |
| 162 | 174 | @disabledDocs.clearCache() |
| 163 | − if @appCache then @appCache.reload() else window.location = '/' | |
| 175 | + if @serviceWorker then @serviceWorker.reload() else @reboot() | |
| 164 | 176 | return |
| 165 | 177 | |
| 166 | 178 | reset: -> |
| 167 | 179 | @localStorage.reset() |
| 168 | 180 | @settings.reset() |
| 169 | 181 | @db?.reset() |
| 170 | − @appCache?.update() | |
| 182 | + @serviceWorker?.update() | |
| 171 | 183 | window.location = '/' |
| 172 | 184 | return |
| 173 | 185 | |
| @@ -186,9 +198,9 @@ · 12 unmodified lines | ||
| 186 | 198 | return |
| 187 | 199 | |
| 188 | 200 | indexHost: -> |
| 189 | − # Can't load the index files from the host/CDN when applicationCache is | |
| 201 | + # Can't load the index files from the host/CDN when service worker is | |
| 190 | 202 | # enabled because it doesn't support caching URLs that use CORS. |
| 191 | − @config[if @appCache and @settings.hasDocs() then 'index_path' else 'docs_origin'] | |
| 203 | + @config[if @serviceWorker and @settings.hasDocs() then 'index_path' else 'docs_origin'] | |
| 192 | 204 | |
| 193 | 205 | onBootError: (args...) -> |
| 194 | 206 | @trigger 'bootError' |
| @@ -245,7 +257,7 @@ · 50 unmodified lines | ||
| 245 | 257 | matchMedia: !!window.matchMedia |
| 246 | 258 | insertAdjacentHTML: !!document.body.insertAdjacentHTML |
| 247 | 259 | defaultPrevented: document.createEvent('CustomEvent').defaultPrevented is false |
| 248 | − cssGradients: supportsCssGradients() | |
| 260 | + cssVariables: !!CSS?.supports?('(--t: 0)') | |
| 249 | 261 | |
| 250 | 262 | for key, value of features when not value |
| 251 | 263 | Raven.captureMessage "unsupported/#{key}", level: 'info' |
| @@ -268,9 +280,4 @@ · 16 unmodified lines | ||
| 268 | 280 | isInvalidLocation: -> |
| 269 | 281 | @config.env is 'production' and location.host.indexOf(app.config.production_host) isnt 0 |
| 270 | 282 | |
| 271 | −supportsCssGradients = -> | |
| 272 | − el = document.createElement('div') | |
| 273 | − el.style.cssText = "background-image: -webkit-linear-gradient(top, #000, #fff); background-image: linear-gradient(to top, #000, #fff);" | |
| 274 | − el.style.backgroundImage.indexOf('gradient') >= 0 | |
| 275 | − | |
| 276 | 283 | $.extend app, Events |
| @@ -1,42 +0,0 @@ | ||
| 1 | −class app.AppCache | |
| 2 | − $.extend @prototype, Events | |
| 3 | − | |
| 4 | − @isEnabled: -> | |
| 5 | − try | |
| 6 | − applicationCache and applicationCache.status isnt applicationCache.UNCACHED | |
| 7 | − catch | |
| 8 | − | |
| 9 | − constructor: -> | |
| 10 | − @cache = applicationCache | |
| 11 | − @notifyUpdate = true | |
| 12 | − @onUpdateReady() if @cache.status is @cache.UPDATEREADY | |
| 13 | − | |
| 14 | − $.on @cache, 'progress', @onProgress | |
| 15 | − $.on @cache, 'updateready', @onUpdateReady | |
| 16 | − | |
| 17 | − update: -> | |
| 18 | − @notifyUpdate = true | |
| 19 | − @notifyProgress = true | |
| 20 | − try @cache.update() catch | |
| 21 | − return | |
| 22 | − | |
| 23 | − updateInBackground: -> | |
| 24 | − @notifyUpdate = false | |
| 25 | − @notifyProgress = false | |
| 26 | − try @cache.update() catch | |
| 27 | − return | |
| 28 | − | |
| 29 | − reload: -> | |
| 30 | − $.on @cache, 'updateready noupdate error', -> window.location = '/' | |
| 31 | − @notifyUpdate = false | |
| 32 | − @notifyProgress = true | |
| 33 | − try @cache.update() catch | |
| 34 | − return | |
| 35 | − | |
| 36 | − onProgress: (event) => | |
| 37 | − @trigger 'progress', event if @notifyProgress | |
| 38 | − return | |
| 39 | − | |
| 40 | − onUpdateReady: => | |
| 41 | − @trigger 'updateready' if @notifyUpdate | |
| 42 | − return | |
| @@ -13,3 +13,6 @@ app.config = · 12 unmodified lines | ||
| 13 | 13 | version: <%= Time.now.to_i %> |
| 14 | 14 | release: <%= Time.now.utc.httpdate.to_json %> |
| 15 | 15 | mathml_stylesheet: '<%= App.cdn_origin %>/mathml.css' |
| 16 | + favicon_spritesheet: '<%= image_path('sprites/docs.png') %>' | |
| 17 | + service_worker_path: '/service-worker.js' | |
| 18 | + service_worker_enabled: <%= App.environment == :production || ENV['ENABLE_SERVICE_WORKER'] == 'true' %> | |
| @@ -0,0 +1,49 @@ | ||
| 1 | +class app.ServiceWorker | |
| 2 | + $.extend @prototype, Events | |
| 3 | + | |
| 4 | + @isEnabled: -> | |
| 5 | + !!navigator.serviceWorker and app.config.service_worker_enabled | |
| 6 | + | |
| 7 | + constructor: -> | |
| 8 | + @registration = null | |
| 9 | + @notifyUpdate = true | |
| 10 | + | |
| 11 | + navigator.serviceWorker.register(app.config.service_worker_path, {scope: '/'}) | |
| 12 | + .then( | |
| 13 | + (registration) => @updateRegistration(registration), | |
| 14 | + (error) -> console.error('Could not register service worker:', error) | |
| 15 | + ) | |
| 16 | + | |
| 17 | + update: -> | |
| 18 | + return unless @registration | |
| 19 | + @notifyUpdate = true | |
| 20 | + return @registration.update().catch(->) | |
| 21 | + | |
| 22 | + updateInBackground: -> | |
| 23 | + return unless @registration | |
| 24 | + @notifyUpdate = false | |
| 25 | + return @registration.update().catch(->) | |
| 26 | + | |
| 27 | + reload: -> | |
| 28 | + return @updateInBackground().then(() -> app.reboot()) | |
| 29 | + | |
| 30 | + updateRegistration: (registration) -> | |
| 31 | + @registration = registration | |
| 32 | + $.on @registration, 'updatefound', @onUpdateFound | |
| 33 | + return | |
| 34 | + | |
| 35 | + onUpdateFound: => | |
| 36 | + $.off @installingRegistration, 'statechange', @onStateChange() if @installingRegistration | |
| 37 | + @installingRegistration = @registration.installing | |
| 38 | + $.on @installingRegistration, 'statechange', @onStateChange | |
| 39 | + return | |
| 40 | + | |
| 41 | + onStateChange: => | |
| 42 | + if @installingRegistration and @installingRegistration.state == 'installed' and navigator.serviceWorker.controller | |
| 43 | + @installingRegistration = null | |
| 44 | + @onUpdateReady() | |
| 45 | + return | |
| 46 | + | |
| 47 | + onUpdateReady: -> | |
| 48 | + @trigger 'updateready' if @notifyUpdate | |
| 49 | + return | |
| @@ -5,11 +5,13 @@ class app.Settings · 4 unmodified lines | ||
| 5 | 5 | 'manualUpdate' |
| 6 | 6 | 'fastScroll' |
| 7 | 7 | 'arrowScroll' |
| 8 | + 'analyticsConsent' | |
| 8 | 9 | 'docs' |
| 9 | 10 | 'dark' |
| 10 | 11 | 'layout' |
| 11 | 12 | 'size' |
| 12 | 13 | 'tips' |
| 14 | + 'autoInstall' | |
| 13 | 15 | ] |
| 14 | 16 | |
| 15 | 17 | INTERNAL_KEYS = [ |
| @@ -19,6 +21,8 @@ class app.Settings · 3 unmodified lines | ||
| 19 | 21 | 'news' |
| 20 | 22 | ] |
| 21 | 23 | |
| 24 | + LAYOUTS: ['_max-width', '_sidebar-hidden', '_native-scrollbars'] | |
| 25 | + | |
| 22 | 26 | @defaults: |
| 23 | 27 | count: 0 |
| 24 | 28 | hideDisabled: false |
| @@ -26,9 +30,10 @@ class app.Settings · 1 unmodified line | ||
| 26 | 30 | news: 0 |
| 27 | 31 | manualUpdate: false |
| 28 | 32 | schema: 1 |
| 33 | + analyticsConsent: false | |
| 29 | 34 | |
| 30 | 35 | constructor: -> |
| 31 | − @store = new CookieStore | |
| 36 | + @store = new CookiesStore | |
| 32 | 37 | @cache = {} |
| 33 | 38 | |
| 34 | 39 | get: (key) -> |
| @@ -38,6 +43,7 @@ class app.Settings · 3 unmodified lines | ||
| 38 | 43 | set: (key, value) -> |
| 39 | 44 | @store.set(key, value) |
| 40 | 45 | delete @cache[key] |
| 46 | + @toggleDark(value) if key == 'dark' | |
| 41 | 47 | return |
| 42 | 48 | |
| 43 | 49 | del: (key) -> |
| @@ -63,6 +69,8 @@ class app.Settings · 19 unmodified lines | ||
| 63 | 69 | return |
| 64 | 70 | |
| 65 | 71 | setLayout: (name, enable) -> |
| 72 | + @toggleLayout(name, enable) | |
| 73 | + | |
| 66 | 74 | layout = (@store.get('layout') || '').split(' ') |
| 67 | 75 | $.arrayDelete(layout, '') |
| 68 | 76 | |
| @@ -104,3 +112,28 @@ class app.Settings · 35 unmodified lines | ||
| 104 | 112 | @store.reset() |
| 105 | 113 | @cache = {} |
| 106 | 114 | return |
| 115 | + | |
| 116 | + initLayout: -> | |
| 117 | + @toggleDark(@get('dark') is 1) | |
| 118 | + @toggleLayout(layout, @hasLayout(layout)) for layout in @LAYOUTS | |
| 119 | + @initSidebarWidth() | |
| 120 | + return | |
| 121 | + | |
| 122 | + toggleDark: (enable) -> | |
| 123 | + classList = document.documentElement.classList | |
| 124 | + classList.toggle('_theme-default', !enable) | |
| 125 | + classList.toggle('_theme-dark', enable) | |
| 126 | + color = getComputedStyle(document.documentElement).getPropertyValue('--headerBackground').trim() | |
| 127 | + $('meta[name=theme-color]').setAttribute('content', color) | |
| 128 | + return | |
| 129 | + | |
| 130 | + toggleLayout: (layout, enable) -> | |
| 131 | + classList = document.body.classList | |
| 132 | + classList.toggle(layout, enable) unless layout is '_sidebar-hidden' | |
| 133 | + classList.toggle('_overlay-scrollbars', $.overlayScrollbarsEnabled()) | |
| 134 | + return | |
| 135 | + | |
| 136 | + initSidebarWidth: -> | |
| 137 | + size = @get('size') | |
| 138 | + document.documentElement.style.setProperty('--sidebarWidth', size + 'px') if size | |
| 139 | + return | |
| @@ -149,6 +149,9 @@ class app.Shortcuts · 148 unmodified lines | ||
| 149 | 149 | when 40 |
| 150 | 150 | @trigger 'altDown' |
| 151 | 151 | false |
| 152 | + when 68 | |
| 153 | + @trigger 'altD' | |
| 154 | + false | |
| 152 | 155 | when 70 |
| 153 | 156 | @trigger 'altF', event |
| 154 | 157 | when 71 |
| @@ -3,13 +3,13 @@ class app.UpdateChecker · 2 unmodified lines | ||
| 3 | 3 | @lastCheck = Date.now() |
| 4 | 4 | |
| 5 | 5 | $.on window, 'focus', @onFocus |
| 6 | − app.appCache.on 'updateready', @onUpdateReady if app.appCache | |
| 6 | + app.serviceWorker?.on 'updateready', @onUpdateReady | |
| 7 | 7 | |
| 8 | 8 | setTimeout @checkDocs, 0 |
| 9 | 9 | |
| 10 | 10 | check: -> |
| 11 | − if app.appCache | |
| 12 | − app.appCache.update() | |
| 11 | + if app.serviceWorker | |
| 12 | + app.serviceWorker.update() | |
| 13 | 13 | else |
| 14 | 14 | ajax |
| 15 | 15 | url: $('script[src*="application"]').getAttribute('src') |
| @@ -1,4 +1,8 @@ | ||
| 1 | −class @CookieStore | |
| 1 | +class @CookiesStore | |
| 2 | + # Intentionally called CookiesStore instead of CookieStore | |
| 3 | + # Calling it CookieStore causes issues when the Experimental Web Platform features flag is enabled in Chrome | |
| 4 | + # Related issue: https://github.com/freeCodeCamp/devdocs/issues/932 | |
| 5 | + | |
| 2 | 6 | INT = /^\d+$/ |
| 3 | 7 | |
| 4 | 8 | @onBlocked: -> |
| @@ -0,0 +1,64 @@ | ||
| 1 | +defaultUrl = null | |
| 2 | +currentSlug = null | |
| 3 | + | |
| 4 | +imageCache = {} | |
| 5 | +urlCache = {} | |
| 6 | + | |
| 7 | +withImage = (url, action) -> | |
| 8 | + if imageCache[url] | |
| 9 | + action(imageCache[url]) | |
| 10 | + else | |
| 11 | + img = new Image() | |
| 12 | + img.crossOrigin = 'anonymous' | |
| 13 | + img.src = url | |
| 14 | + img.onload = () => | |
| 15 | + imageCache[url] = img | |
| 16 | + action(img) | |
| 17 | + | |
| 18 | +@setFaviconForDoc = (doc) -> | |
| 19 | + return if currentSlug == doc.slug | |
| 20 | + | |
| 21 | + favicon = $('link[rel="icon"]') | |
| 22 | + | |
| 23 | + if defaultUrl == null | |
| 24 | + defaultUrl = favicon.href | |
| 25 | + | |
| 26 | + if urlCache[doc.slug] | |
| 27 | + favicon.href = urlCache[doc.slug] | |
| 28 | + currentSlug = doc.slug | |
| 29 | + return | |
| 30 | + | |
| 31 | + styles = window.getComputedStyle($("._icon-#{doc.slug.split('~')[0]}"), ':before') | |
| 32 | + | |
| 33 | + bgUrl = app.config.favicon_spritesheet | |
| 34 | + sourceSize = 16 | |
| 35 | + sourceX = Math.abs(parseInt(styles['background-position-x'].slice(0, -2))) | |
| 36 | + sourceY = Math.abs(parseInt(styles['background-position-y'].slice(0, -2))) | |
| 37 | + | |
| 38 | + withImage(bgUrl, (docImg) -> | |
| 39 | + withImage(defaultUrl, (defaultImg) -> | |
| 40 | + size = defaultImg.width | |
| 41 | + | |
| 42 | + canvas = document.createElement('canvas') | |
| 43 | + ctx = canvas.getContext('2d') | |
| 44 | + | |
| 45 | + canvas.width = size | |
| 46 | + canvas.height = size | |
| 47 | + ctx.drawImage(defaultImg, 0, 0) | |
| 48 | + | |
| 49 | + docIconPercentage = 65 | |
| 50 | + destinationCoords = size / 100 * (100 - docIconPercentage) | |
| 51 | + destinationSize = size / 100 * docIconPercentage | |
| 52 | + ctx.drawImage(docImg, sourceX, sourceY, sourceSize, sourceSize, destinationCoords, destinationCoords, destinationSize, destinationSize) | |
| 53 | + | |
| 54 | + urlCache[doc.slug] = canvas.toDataURL() | |
| 55 | + favicon.href = urlCache[doc.slug] | |
| 56 | + | |
| 57 | + currentSlug = doc.slug | |
| 58 | + ) | |
| 59 | + ) | |
| 60 | + | |
| 61 | +@resetFavicon = () -> | |
| 62 | + if defaultUrl != null and currentSlug != null | |
| 63 | + $('link[rel="icon"]').href = defaultUrl | |
| 64 | + currentSlug = null | |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | ### |
| 2 | − * Copyright 2013-2017 Thibaut Courouble and other contributors | |
| 2 | + * Copyright 2013-2019 Thibaut Courouble and other contributors | |
| 3 | 3 | * |
| 4 | 4 | * This source code is licensed under the terms of the Mozilla |
| 5 | 5 | * Public License, v. 2.0, a copy of which may be obtained at: |
| @@ -190,7 +190,7 @@ isSameOrigin = (url) -> · 189 unmodified lines | ||
| 190 | 190 | |
| 191 | 191 | updateCanonicalLink = -> |
| 192 | 192 | @canonicalLink ||= document.head.querySelector('link[rel="canonical"]') |
| 193 | − @canonicalLink.setAttribute('href', "http://#{location.host}#{location.pathname}") | |
| 193 | + @canonicalLink.setAttribute('href', "https://#{location.host}#{location.pathname}") | |
| 194 | 194 | |
| 195 | 195 | trackers = [] |
| 196 | 196 | |
| @@ -199,5 +199,21 @@ page.track = (fn) -> · 2 unmodified lines | ||
| 199 | 199 | return |
| 200 | 200 | |
| 201 | 201 | track = -> |
| 202 | − tracker.call() for tracker in trackers | |
| 202 | + consentGiven = Cookies.get('analyticsConsent') | |
| 203 | + consentAsked = Cookies.get('analyticsConsentAsked') | |
| 204 | + | |
| 205 | + if consentGiven == '1' | |
| 206 | + tracker.call() for tracker in trackers | |
| 207 | + else if consentGiven == undefined and consentAsked == undefined | |
| 208 | + # Only ask for consent once per browser session | |
| 209 | + Cookies.set('analyticsConsentAsked', '1') | |
| 210 | + | |
| 211 | + new app.views.Notif 'AnalyticsConsent', autoHide: null | |
| 212 | + return | |
| 213 | + | |
| 214 | +@resetAnalytics = -> | |
| 215 | + for cookie in document.cookie.split(/;\s?/) | |
| 216 | + name = cookie.split('=')[0] | |
| 217 | + if name[0] == '_' | |
| 218 | + Cookies.expire(name) | |
| 203 | 219 | return |
| @@ -124,7 +124,7 @@ $.empty = (el) -> · 123 unmodified lines | ||
| 124 | 124 | return |
| 125 | 125 | |
| 126 | 126 | # Calls the function while the element is off the DOM to avoid triggering |
| 127 | −# unecessary reflows and repaints. | |
| 127 | +# unnecessary reflows and repaints. | |
| 128 | 128 | $.batchUpdate = (el, fn) -> |
| 129 | 129 | parent = el.parentNode |
| 130 | 130 | sibling = el.nextSibling |
| @@ -352,6 +352,10 @@ isIE = null · 221 unmodified lines | ||
| 352 | 352 | $.isIE = -> |
| 353 | 353 | isIE ?= navigator.userAgent?.indexOf('MSIE') >= 0 || navigator.userAgent?.indexOf('rv:11.0') >= 0 |
| 354 | 354 | |
| 355 | +isChromeForAndroid = null | |
| 356 | +$.isChromeForAndroid = -> | |
| 357 | + isChromeForAndroid ?= navigator.userAgent?.indexOf('Android') >= 0 && /Chrome\/([.0-9])+ Mobile/.test(navigator.userAgent) | |
| 358 | + | |
| 355 | 359 | isAndroid = null |
| 356 | 360 | $.isAndroid = -> |
| 357 | 361 | isAndroid ?= navigator.userAgent?.indexOf('Android') >= 0 |
| @@ -142,4 +142,6 @@ class app.models.Doc extends app.Model · 141 unmodified lines | ||
| 142 | 142 | return |
| 143 | 143 | |
| 144 | 144 | isOutdated: (status) -> |
| 145 | − status and status.installed and @mtime isnt status.mtime | |
| 145 | + return false if not status | |
| 146 | + isInstalled = status.installed or app.settings.get('autoInstall') | |
| 147 | + isInstalled and @mtime isnt status.mtime | |
| @@ -1,5 +1,37 @@ | ||
| 1 | 1 | [ |
| 2 | 2 | [ |
| 3 | + "2019-07-21", | |
| 4 | + "Fixed several bugs, added an option to automatically download documentation and <a href=\"https://github.com/freeCodeCamp/devdocs/pulls?utf8=%E2%9C%93&q=is%3Apr+is%3Aclosed+sort%3Aupdated-desc+closed%3A%3E2019-07-18+977+988+986+870+886+1024+979+975+941+831+1005+848+942\" target=\"_blank\">more</a>." | |
| 5 | + ], | |
| 6 | + [ | |
| 7 | + "2019-07-19", | |
| 8 | + "Replaced the AppCache with a Service Worker (which makes DevDocs an installable PWA) and fixed layout preferences on Firefox." | |
| 9 | + ], | |
| 10 | + [ | |
| 11 | + "2018-09-23", | |
| 12 | + "New documentations: <a href=\"/puppeteer/\">Puppeteer</a> and <a href=\"/handlebars/\">Handlebars.js</a>" | |
| 13 | + ], [ | |
| 14 | + "2018-08-12", | |
| 15 | + "New documentations: <a href=\"/dart/\">Dart</a> and <a href=\"/qt/\">Qt</a>" | |
| 16 | + ], [ | |
| 17 | + "2018-07-29", | |
| 18 | + "New documentations: <a href=\"/bash/\">Bash</a>, <a href=\"/graphite/\">Graphite</a> and <a href=\"/pygame/\">Pygame</a>" | |
| 19 | + ], [ | |
| 20 | + "2018-07-08", | |
| 21 | + "New documentations: <a href=\"/leaflet/\">Leaflet</a>, <a href=\"/terraform/\">Terraform</a> and <a href=\"/koa/\">Koa</a>" | |
| 22 | + ], [ | |
| 23 | + "2018-03-26", | |
| 24 | + "DevDocs is joining the freeCodeCamp community. Read the announcement <a href=\"https://medium.freecodecamp.org/devdocs-is-joining-the-freecodecamp-community-ae185a1c14a6\" target=\"_blank\">here</a>." | |
| 25 | + ], [ | |
| 26 | + "2018-02-04", | |
| 27 | + "New documentations: <a href=\"/babel/\">Babel</a>, <a href=\"/jekyll/\">Jekyll</a> and <a href=\"/jsdoc/\">JSDoc</a>" | |
| 28 | + ], [ | |
| 29 | + "2017-11-26", | |
| 30 | + "New documentations: <a href=\"/bluebird/\">Bluebird</a>, <a href=\"/eslint/\">ESLint</a> and <a href=\"/homebrew/\">Homebrew</a>" | |
| 31 | + ], [ | |
| 32 | + "2017-11-18", | |
| 33 | + "Added print & PDF stylesheet.\nFeedback welcome on <a href=\"https://twitter.com/DevDocs\" target=\"_blank\" rel=\"noopener\">Twitter</a> and <a href=\"https://github.com/freeCodeCamp/devdocs\" target=\"_blank\" rel=\"noopener\">GitHub</a>." | |
| 34 | + ], [ | |
| 3 | 35 | "2017-09-10", |
| 4 | 36 | "<a href=\"/settings\">Preferences</a> can now be exported and imported." |
| 5 | 37 | ], [ |
| @@ -110,7 +142,7 @@ · 104 unmodified lines | ||
| 110 | 142 | "New documentations: <a href=\"/q/\">Q</a> and <a href=\"/opentsdb/\">OpenTSDB</a>" |
| 111 | 143 | ], [ |
| 112 | 144 | "2015-07-26", |
| 113 | − "Added search aliases (e.g. <code class=\"_label\">$</code> is an alias for <code class=\"_label\">jQuery</code>).\n<a href=\"/help#aliases\">Click here</a> to see the full list. Feel free to suggest more on <a href=\"https://github.com/Thibaut/devdocs/issues/new\" target=\"_blank\" rel=\"noopener\">GitHub</a>.", | |
| 145 | + "Added search aliases (e.g. <code class=\"_label\">$</code> is an alias for <code class=\"_label\">jQuery</code>).\n<a href=\"/help#aliases\">Click here</a> to see the full list. Feel free to suggest more on <a href=\"https://github.com/freeCodeCamp/devdocs/issues/new\" target=\"_blank\" rel=\"noopener\">GitHub</a>.", | |
| 114 | 146 | "Added <code class=\"_label\">shift + ↓/↑</code> shortcut for scrolling (same as <code class=\"_label\">alt + ↓/↑</code>)." |
| 115 | 147 | ], [ |
| 116 | 148 | "2015-07-05", |
| @@ -151,7 +183,7 @@ · 34 unmodified lines | ||
| 151 | 183 | "New <a href=\"/python2/\">Python 2</a> documentation" |
| 152 | 184 | ], [ |
| 153 | 185 | "2014-11-09", |
| 154 | − "New design\nFeedback welcome on <a href=\"https://twitter.com/DevDocs\" target=\"_blank\" rel=\"noopener\">Twitter</a> and <a href=\"https://github.com/Thibaut/devdocs\" target=\"_blank\" rel=\"noopener\">GitHub</a>." | |
| 186 | + "New design\nFeedback welcome on <a href=\"https://twitter.com/DevDocs\" target=\"_blank\" rel=\"noopener\">Twitter</a> and <a href=\"https://github.com/freeCodeCamp/devdocs\" target=\"_blank\" rel=\"noopener\">GitHub</a>." | |
| 155 | 187 | ], [ |
| 156 | 188 | "2014-10-19", |
| 157 | 189 | "New <a href=\"/svg/\">SVG</a>, <a href=\"/marionette/\">Marionette.js</a>, and <a href=\"/mongoose/\">Mongoose</a> documentations" |
| @@ -236,7 +268,7 @@ · 78 unmodified lines | ||
| 236 | 268 | "New <a href=\"/ruby/\">Ruby</a> documentation" |
| 237 | 269 | ], [ |
| 238 | 270 | "2013-10-24", |
| 239 | − "DevDocs is now <a href=\"https://github.com/Thibaut/devdocs\">open source</a>." | |
| 271 | + "DevDocs is now <a href=\"https://github.com/freeCodeCamp/devdocs\">open source</a>." | |
| 240 | 272 | ], [ |
| 241 | 273 | "2013-10-09", |
| 242 | 274 | "DevDocs is now available as a <a href=\"https://chrome.google.com/webstore/detail/devdocs/mnfehgbmkapmjnhcnbodoamcioleeooe\">Chrome web app</a>." |
| @@ -12,8 +12,8 @@ app.templates.notFoundPage = -> · 11 unmodified lines | ||
| 12 | 12 | |
| 13 | 13 | app.templates.pageLoadError = -> |
| 14 | 14 | error """ The page failed to load. """, |
| 15 | − """ It may be missing from the server (try reloading the app) or you could be offline.<br> | |
| 16 | − If you keep seeing this, you're likely behind a proxy or firewall that blocks cross-domain requests. """, | |
| 15 | + """ It may be missing from the server (try reloading the app) or you could be offline (try <a href="/offline">installing the documentation for offline usage</a> when online again).<br> | |
| 16 | + If you're online and you keep seeing this, you're likely behind a proxy or firewall that blocks cross-domain requests. """, | |
| 17 | 17 | """ #{back} · <a href="/##{location.pathname}" target="_top" class="_error-link">Reload</a> |
| 18 | 18 | · <a href="#" class="_error-link" data-retry>Retry</a> """ |
| 19 | 19 | |
| @@ -37,10 +37,10 @@ app.templates.offlineError = (reason, exception) -> · 17 unmodified lines | ||
| 37 | 37 | """ Your browser appears to be running in private mode.<br> |
| 38 | 38 | This prevents DevDocs from caching documentations for offline access.""" |
| 39 | 39 | when 'exception' |
| 40 | − """ An error occured when trying to open the IndexedDB database:<br> | |
| 40 | + """ An error occurred when trying to open the IndexedDB database:<br> | |
| 41 | 41 | <code class="_label">#{exception.name}: #{exception.message}</code> """ |
| 42 | 42 | when 'cant_open' |
| 43 | − """ An error occured when trying to open the IndexedDB database:<br> | |
| 43 | + """ An error occurred when trying to open the IndexedDB database:<br> | |
| 44 | 44 | <code class="_label">#{exception.name}: #{exception.message}</code><br> |
| 45 | 45 | This could be because you're browsing in private mode or have disallowed offline storage on the domain. """ |
| 46 | 46 | when 'version' |
| @@ -56,21 +56,18 @@ app.templates.unsupportedBrowser = """ · 9 unmodified lines | ||
| 56 | 56 | <h1 class="_fail-title">Your browser is unsupported, sorry.</h1> |
| 57 | 57 | <p class="_fail-text">DevDocs is an API documentation browser which supports the following browsers: |
| 58 | 58 | <ul class="_fail-list"> |
| 59 | − <li>Recent versions of Chrome and Firefox | |
| 60 | − <li>Safari 5.1+ | |
| 61 | − <li>Opera 12.1+ | |
| 62 | − <li>Internet Explorer 10+ | |
| 63 | − <li>iOS 6+ | |
| 64 | − <li>Android 4.1+ | |
| 65 | − <li>Windows Phone 8+ | |
| 59 | + <li>Recent versions of Firefox, Chrome, or Opera | |
| 60 | + <li>Safari 11.1+ | |
| 61 | + <li>Edge 17+ | |
| 62 | + <li>iOS 11.3+ | |
| 66 | 63 | </ul> |
| 67 | 64 | <p class="_fail-text"> |
| 68 | − If you're unable to upgrade, I apologize. | |
| 69 | − I decided to prioritize speed and new features over support for older browsers. | |
| 65 | + If you're unable to upgrade, we apologize. | |
| 66 | + We decided to prioritize speed and new features over support for older browsers. | |
| 70 | 67 | <p class="_fail-text"> |
| 71 | 68 | Note: if you're already using one of the browsers above, check your settings and add-ons. |
| 72 | 69 | The app uses feature detection, not user agent sniffing. |
| 73 | 70 | <p class="_fail-text"> |
| 74 | − — Thibaut <a href="https://twitter.com/DevDocs" class="_fail-link">@DevDocs</a> | |
| 71 | + — <a href="https://twitter.com/DevDocs">@DevDocs</a> | |
| 75 | 72 | </div> |
| 76 | 73 | """ |
| @@ -2,7 +2,7 @@ notice = (text) -> """<p class="_notice-text">#{text}</p>""" · 1 unmodified line | ||
| 2 | 2 | |
| 3 | 3 | app.templates.singleDocNotice = (doc) -> |
| 4 | 4 | notice """ You're browsing the #{doc.fullName} documentation. To browse all docs, go to |
| 5 | − <a href="http://#{app.config.production_host}" target="_top">#{app.config.production_host}</a> (or press <code>esc</code>). """ | |
| 5 | + <a href="//#{app.config.production_host}" target="_top">#{app.config.production_host}</a> (or press <code>esc</code>). """ | |
| 6 | 6 | |
| 7 | 7 | app.templates.disabledDocNotice = -> |
| 8 | 8 | notice """ <strong>This documentation is disabled.</strong> |
| @@ -13,10 +13,10 @@ app.templates.notifUpdateReady = -> · 12 unmodified lines | ||
| 13 | 13 | """<span data-behavior="reboot"><a href="#" data-behavior="reboot">Reload the page</a> to use the new version.</span>""" |
| 14 | 14 | |
| 15 | 15 | app.templates.notifError = -> |
| 16 | − textNotif """ Oops, an error occured. """, | |
| 16 | + textNotif """ Oops, an error occurred. """, | |
| 17 | 17 | """ Try <a href="#" data-behavior="hard-reload">reloading</a>, and if the problem persists, |
| 18 | 18 | <a href="#" data-behavior="reset">resetting the app</a>.<br> |
| 19 | − You can also report this issue on <a href="https://github.com/Thibaut/devdocs/issues/new" target="_blank" rel="noopener">GitHub</a>. """ | |
| 19 | + You can also report this issue on <a href="https://github.com/freeCodeCamp/devdocs/issues/new" target="_blank" rel="noopener">GitHub</a>. """ | |
| 20 | 20 | |
| 21 | 21 | app.templates.notifQuotaExceeded = -> |
| 22 | 22 | textNotif """ The offline database has exceeded its size limitation. """, |
| @@ -31,7 +31,7 @@ app.templates.notifInvalidLocation = -> · 8 unmodified lines | ||
| 31 | 31 | """ Otherwise things are likely to break. """ |
| 32 | 32 | |
| 33 | 33 | app.templates.notifImportInvalid = -> |
| 34 | − textNotif """ Oops, an error occured. """, | |
| 34 | + textNotif """ Oops, an error occurred. """, | |
| 35 | 35 | """ The file you selected is invalid. """ |
| 36 | 36 | |
| 37 | 37 | app.templates.notifNews = (news) -> |
| @@ -68,3 +68,9 @@ app.templates.notifShare = -> · 30 unmodified lines | ||
| 68 | 68 | app.templates.notifUpdateDocs = -> |
| 69 | 69 | textNotif """ Documentation updates available. """, |
| 70 | 70 | """ <a href="/offline">Install them</a> as soon as possible to avoid broken pages. """ |
| 71 | + | |
| 72 | +app.templates.notifAnalyticsConsent = -> | |
| 73 | + textNotif """ Tracking cookies """, | |
| 74 | + """ We would like to gather usage data about how DevDocs is used through Google Analytics and Gauges. We only collect anonymous traffic information. | |
| 75 | + Please confirm if you accept our tracking cookies. You can always change your decision in the settings. | |
| 76 | + <br><span class="_notif-right"><a href="#" data-behavior="accept-analytics">Accept</a> or <a href="#" data-behavior="decline-analytics">Decline</a></span> """ | |
| @@ -11,28 +11,22 @@ app.templates.aboutPage = -> """ · 10 unmodified lines | ||
| 11 | 11 | </nav> |
| 12 | 12 | |
| 13 | 13 | <h1 class="_lined-heading">DevDocs: API Documentation Browser</h1> |
| 14 | − <p>DevDocs combines multiple API documentations in a clean and organized web UI with instant search, offline support, mobile version, dark theme, keyboard shortcuts, and more. | |
| 15 | − <ul> | |
| 16 | − <li>Created and maintained by <a href="https://thibaut.me">Thibaut Courouble</a> | |
| 17 | − <li>Free and <a href="https://github.com/Thibaut/devdocs">open source</a> | |
| 18 | − <iframe class="_github-btn" src="https://ghbtns.com/github-btn.html?user=Thibaut&repo=devdocs&type=watch&count=true" allowtransparency="true" frameborder="0" scrolling="0" width="100" height="20" tabindex="-1"></iframe> | |
| 19 | − </ul> | |
| 14 | + <p>DevDocs combines multiple developer documentations in a clean and organized web UI with instant search, offline support, mobile version, dark theme, keyboard shortcuts, and more. | |
| 15 | + <p>DevDocs is free and <a href="https://github.com/freeCodeCamp/devdocs">open source</a>. It was created by <a href="https://thibaut.me">Thibaut Courouble</a> and is operated by <a href="https://www.freecodecamp.org/">freeCodeCamp</a>. | |
| 20 | 16 | <p>To keep up-to-date with the latest news: |
| 21 | 17 | <ul> |
| 22 | 18 | <li>Follow <a href="https://twitter.com/DevDocs">@DevDocs</a> on Twitter |
| 23 | − <li>Watch the repository on <a href="https://github.com/Thibaut/devdocs/subscription">GitHub</a> | |
| 24 | − <li>Join the <a href="https://groups.google.com/d/forum/devdocs">mailing list</a> | |
| 19 | + <li>Watch the repository on <a href="https://github.com/freeCodeCamp/devdocs/subscription">GitHub</a> <iframe class="_github-btn" src="https://ghbtns.com/github-btn.html?user=freeCodeCamp&repo=devdocs&type=watch&count=true" allowtransparency="true" frameborder="0" scrolling="0" width="100" height="20" tabindex="-1"></iframe> | |
| 20 | + <li>Join the <a href="https://gitter.im/FreeCodeCamp/DevDocs">Gitter</a> chat room | |
| 25 | 21 | </ul> |
| 26 | − <p class="_note _note-green">If you like DevDocs, please spread the word and consider supporting my work on | |
| 27 | − <a href="https://gratipay.com/devdocs/">Gratipay</a>. Thanks!<br> | |
| 28 | 22 | |
| 29 | 23 | <h2 class="_block-heading" id="copyright">Copyright and License</h2> |
| 30 | 24 | <p class="_note"> |
| 31 | − <strong>Copyright 2013–2017 Thibaut Courouble and <a href="https://github.com/Thibaut/devdocs/graphs/contributors">other contributors</a></strong><br> | |
| 25 | + <strong>Copyright 2013–2019 Thibaut Courouble and <a href="https://github.com/freeCodeCamp/devdocs/graphs/contributors">other contributors</a></strong><br> | |
| 32 | 26 | This software is licensed under the terms of the Mozilla Public License v2.0.<br> |
| 33 | − You may obtain a copy of the source code at <a href="https://github.com/Thibaut/devdocs">github.com/Thibaut/devdocs</a>.<br> | |
| 34 | − For more information, see the <a href="https://github.com/Thibaut/devdocs/blob/master/COPYRIGHT">COPYRIGHT</a> | |
| 35 | − and <a href="https://github.com/Thibaut/devdocs/blob/master/LICENSE">LICENSE</a> files. | |
| 27 | + You may obtain a copy of the source code at <a href="https://github.com/freeCodeCamp/devdocs">github.com/freeCodeCamp/devdocs</a>.<br> | |
| 28 | + For more information, see the <a href="https://github.com/freeCodeCamp/devdocs/blob/master/COPYRIGHT">COPYRIGHT</a> | |
| 29 | + and <a href="https://github.com/freeCodeCamp/devdocs/blob/master/LICENSE">LICENSE</a> files. | |
| 36 | 30 | |
| 37 | 31 | <h2 class="_block-heading" id="plugins">Plugins and Extensions</h2> |
| 38 | 32 | <ul> |
| @@ -49,20 +43,19 @@ app.templates.aboutPage = -> """ · 10 unmodified lines | ||
| 49 | 43 | <dl> |
| 50 | 44 | <dt>Where can I suggest new docs and features? |
| 51 | 45 | <dd>You can suggest and vote for new docs on the <a href="https://trello.com/b/6BmTulfx/devdocs-documentation">Trello board</a>.<br> |
| 52 | − If you have a specific feature request, add it to the <a href="https://github.com/Thibaut/devdocs/issues">issue tracker</a>.<br> | |
| 53 | − Otherwise use the <a href="https://groups.google.com/d/forum/devdocs">mailing list</a>. | |
| 46 | + If you have a specific feature request, add it to the <a href="https://github.com/freeCodeCamp/devdocs/issues">issue tracker</a>.<br> | |
| 47 | + Otherwise, come talk to us in the <a href="https://gitter.im/FreeCodeCamp/DevDocs">Gitter</a> chat room. | |
| 54 | 48 | <dt>Where can I report bugs? |
| 55 | − <dd>In the <a href="https://github.com/Thibaut/devdocs/issues">issue tracker</a>. Thanks! | |
| 49 | + <dd>In the <a href="https://github.com/freeCodeCamp/devdocs/issues">issue tracker</a>. Thanks! | |
| 56 | 50 | </dl> |
| 57 | − <p>For anything else, feel free to email me at <a href="mailto:thibaut@devdocs.io">thibaut@devdocs.io</a>. | |
| 58 | 51 | |
| 59 | 52 | <h2 class="_block-heading" id="credits">Credits</h2> |
| 60 | 53 | |
| 61 | 54 | <p><strong>Special thanks to:</strong> |
| 62 | 55 | <ul> |
| 63 | − <li><a href="https://out.devdocs.io/s/maxcdn">MaxCDN</a>, <a href="https://sentry.io/">Sentry</a> and <a href="http://get.gaug.es/?utm_source=devdocs&utm_medium=referral&utm_campaign=sponsorships" title="Real Time Web Analytics">Gauges</a> for offering a free account to DevDocs | |
| 56 | + <li><a href="https://out.devdocs.io/s/maxcdn">MaxCDN</a>, <a href="https://sentry.io/">Sentry</a> and <a href="https://get.gaug.es/?utm_source=devdocs&utm_medium=referral&utm_campaign=sponsorships" title="Real Time Web Analytics">Gauges</a> for offering a free account to DevDocs | |
| 64 | 57 | <li><a href="https://out.devdocs.io/s/maxcdn">MaxCDN</a>, <a href="https://out.devdocs.io/s/shopify">Shopify</a>, <a href="https://out.devdocs.io/s/jetbrains">JetBrains</a> and <a href="https://out.devdocs.io/s/code-school">Code School</a> for sponsoring DevDocs in the past |
| 65 | − <li><a href="https://www.heroku.com">Heroku</a> and <a href="http://newrelic.com">New Relic</a> for providing awesome free service | |
| 58 | + <li><a href="https://www.heroku.com">Heroku</a> and <a href="https://newrelic.com/">New Relic</a> for providing awesome free service | |
| 66 | 59 | <li><a href="https://www.jeremykratz.com/">Jeremy Kratz</a> for the C/C++ logo |
| 67 | 60 | </ul> |
| 68 | 61 | |
| @@ -78,43 +71,59 @@ app.templates.aboutPage = -> """ · 9 unmodified lines | ||
| 78 | 71 | |
| 79 | 72 | <h2 class="_block-heading" id="privacy">Privacy Policy</h2> |
| 80 | 73 | <ul> |
| 81 | − <li><a href="http://devdocs.io">devdocs.io</a> ("App") is operated by Thibaut Courouble ("We"). | |
| 82 | − <li>We do not collect personal information. | |
| 83 | − <li>We use Google Analytics, Gauges and Sentry to collect anonymous traffic information and improve the app. | |
| 74 | + <li><a href="https://devdocs.io">devdocs.io</a> ("App") is operated by <a href="https://www.freecodecamp.org/">freeCodeCamp</a> ("We"). | |
| 75 | + <li>We do not collect personal information through the app. | |
| 76 | + <li>We use Google Analytics and Gauges to collect anonymous traffic information if you have given consent to this. You can change your decision in the <a href="/settings">settings</a>. | |
| 77 | + <li>We use Sentry to collect crash data and improve the app. | |
| 84 | 78 | <li>The app uses cookies to store user preferences. |
| 85 | 79 | <li>By using the app, you signify your acceptance of this policy. If you do not agree to this policy, please do not use the app. |
| 86 | − <li>If you have any questions regarding privacy, please email <a href="mailto:thibaut@devdocs.io">thibaut@devdocs.io</a>. | |
| 80 | + <li>If you have any questions regarding privacy, please email <a href="mailto:privacy@freecodecamp.org">privacy@freecodecamp.org</a>. | |
| 87 | 81 | </ul> |
| 88 | 82 | """ |
| 89 | 83 | |
| 90 | 84 | credits = [ |
| 91 | 85 | [ 'Angular<br>Angular.js', |
| 92 | − '2010-2017 Google, Inc.', | |
| 86 | + '2010-2018 Google, Inc.', | |
| 93 | 87 | 'CC BY', |
| 94 | 88 | 'https://creativecommons.org/licenses/by/4.0/' |
| 95 | 89 | ], [ |
| 96 | 90 | 'Ansible', |
| 97 | − '2012-2017 Michael DeHaan<br>© 2017 Red Hat, Inc.', | |
| 91 | + '2012-2018 Michael DeHaan<br>© 2018–2019 Red Hat, Inc.', | |
| 98 | 92 | 'GPLv3', |
| 99 | 93 | 'https://raw.githubusercontent.com/ansible/ansible/devel/COPYING' |
| 100 | 94 | ], [ |
| 101 | 95 | 'Apache HTTP Server<br>Apache Pig', |
| 102 | − '2017 The Apache Software Foundation<br>Apache and the Apache feather logo are trademarks of The Apache Software Foundation.', | |
| 96 | + '2018 The Apache Software Foundation<br>Apache and the Apache feather logo are trademarks of The Apache Software Foundation.', | |
| 103 | 97 | 'Apache', |
| 104 | 98 | 'https://www.apache.org/licenses/LICENSE-2.0' |
| 105 | 99 | ], [ |
| 106 | 100 | 'Async', |
| 107 | − '2010-2017 Caolan McMahon', | |
| 101 | + '2010-2018 Caolan McMahon', | |
| 108 | 102 | 'MIT', |
| 109 | 103 | 'https://raw.githubusercontent.com/caolan/async/master/LICENSE' |
| 104 | + ], [ | |
| 105 | + 'Babel', | |
| 106 | + '2018 Sebastian McKenzie', | |
| 107 | + 'MIT', | |
| 108 | + 'https://raw.githubusercontent.com/babel/website/master/LICENSE' | |
| 110 | 109 | ], [ |
| 111 | 110 | 'Backbone.js', |
| 112 | 111 | '2010-2016 Jeremy Ashkenas, DocumentCloud', |
| 113 | 112 | 'MIT', |
| 114 | 113 | 'https://raw.githubusercontent.com/jashkenas/backbone/master/LICENSE' |
| 114 | + ], [ | |
| 115 | + 'Bash', | |
| 116 | + '2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc.', | |
| 117 | + 'GFDL', | |
| 118 | + 'https://www.gnu.org/licenses/fdl-1.3.en.html' | |
| 119 | + ], [ | |
| 120 | + 'Bluebird', | |
| 121 | + '2013-2017 Petka Antonov', | |
| 122 | + 'MIT', | |
| 123 | + 'https://raw.githubusercontent.com/petkaantonov/bluebird/master/LICENSE' | |
| 115 | 124 | ], [ |
| 116 | 125 | 'Bootstrap', |
| 117 | − '2011-2017 Twitter, Inc.<br>2011-2017 The Bootstrap Authors', | |
| 126 | + '2011-2019 Twitter, Inc.<br>2011-2019 The Bootstrap Authors', | |
| 118 | 127 | 'CC BY', |
| 119 | 128 | 'https://creativecommons.org/licenses/by/3.0/' |
| 120 | 129 | ], [ |
| @@ -124,7 +133,7 @@ credits = [ · 3 unmodified lines | ||
| 124 | 133 | 'https://raw.githubusercontent.com/bottlepy/bottle/master/LICENSE' |
| 125 | 134 | ], [ |
| 126 | 135 | 'Bower', |
| 127 | − '2017 Bower contributors', | |
| 136 | + '2018 Bower contributors', | |
| 128 | 137 | 'MIT', |
| 129 | 138 | 'https://github.com/bower/bower.github.io/blob/1057905c18d899106f91372e6cca7ef54a91d60f/package.json#L20' |
| 130 | 139 | ], [ |
| @@ -134,9 +143,9 @@ credits = [ · 3 unmodified lines | ||
| 134 | 143 | 'http://en.cppreference.com/w/Cppreference:Copyright/CC-BY-SA' |
| 135 | 144 | ], [ |
| 136 | 145 | 'CakePHP', |
| 137 | − '2005-2017 The Cake Software Foundation, Inc.', | |
| 146 | + '2005-2018 The Cake Software Foundation, Inc.', | |
| 138 | 147 | 'MIT', |
| 139 | − 'https://raw.githubusercontent.com/cakephp/cakephp/master/LICENSE.txt' | |
| 148 | + 'https://raw.githubusercontent.com/cakephp/cakephp/master/LICENSE' | |
| 140 | 149 | ], [ |
| 141 | 150 | 'Chai', |
| 142 | 151 | '2016 Chai.js Assertion Library', |
| @@ -154,12 +163,12 @@ credits = [ · 11 unmodified lines | ||
| 154 | 163 | 'https://github.com/clojure/clojure/blob/master/epl-v10.html' |
| 155 | 164 | ], [ |
| 156 | 165 | 'CMake', |
| 157 | − '2000-2017 Kitware, Inc. and Contributors', | |
| 166 | + '2000-2019 Kitware, Inc. and Contributors', | |
| 158 | 167 | 'BSD', |
| 159 | 168 | 'https://cmake.org/licensing/' |
| 160 | 169 | ], [ |
| 161 | 170 | 'Codeception', |
| 162 | − '2011-2017 Michael Bodnarchuk and contributors', | |
| 171 | + '2011 Michael Bodnarchuk and contributors', | |
| 163 | 172 | 'MIT', |
| 164 | 173 | 'https://raw.githubusercontent.com/Codeception/Codeception/master/LICENSE' |
| 165 | 174 | ], [ |
| @@ -169,17 +178,17 @@ credits = [ · 3 unmodified lines | ||
| 169 | 178 | 'https://raw.githubusercontent.com/Codeception/CodeceptJS/master/LICENSE' |
| 170 | 179 | ], [ |
| 171 | 180 | 'CodeIgniter', |
| 172 | − '2014-2017 British Columbia Institute of Technology', | |
| 181 | + '2014-2018 British Columbia Institute of Technology', | |
| 173 | 182 | 'MIT', |
| 174 | 183 | 'https://raw.githubusercontent.com/bcit-ci/CodeIgniter/develop/license.txt' |
| 175 | 184 | ], [ |
| 176 | 185 | 'CoffeeScript', |
| 177 | − '2009-2017 Jeremy Ashkenas', | |
| 186 | + '2009-2018 Jeremy Ashkenas', | |
| 178 | 187 | 'MIT', |
| 179 | − 'https://raw.githubusercontent.com/jashkenas/coffee-script/master/LICENSE' | |
| 188 | + 'https://raw.githubusercontent.com/jashkenas/coffeescript/master/LICENSE' | |
| 180 | 189 | ], [ |
| 181 | 190 | 'Cordova', |
| 182 | − '2012-2017 The Apache Software Foundation', | |
| 191 | + '2012-2018 The Apache Software Foundation', | |
| 183 | 192 | 'Apache', |
| 184 | 193 | 'https://raw.githubusercontent.com/apache/cordova-docs/master/LICENSE' |
| 185 | 194 | ], [ |
| @@ -194,14 +203,19 @@ credits = [ · 8 unmodified lines | ||
| 194 | 203 | 'https://raw.githubusercontent.com/crystal-lang/crystal/master/LICENSE' |
| 195 | 204 | ], [ |
| 196 | 205 | 'D', |
| 197 | − '1999-2017 The D Language Foundation', | |
| 206 | + '1999-2018 The D Language Foundation', | |
| 198 | 207 | 'Boost', |
| 199 | 208 | 'https://raw.githubusercontent.com/dlang/phobos/master/LICENSE_1_0.txt' |
| 200 | 209 | ], [ |
| 201 | 210 | 'D3.js', |
| 202 | − '2010-2017 Michael Bostock', | |
| 211 | + '2010-2018 Michael Bostock', | |
| 203 | 212 | 'BSD', |
| 204 | 213 | 'https://raw.githubusercontent.com/d3/d3/master/LICENSE' |
| 214 | + ], [ | |
| 215 | + 'Dart', | |
| 216 | + '2012 the Dart project authors', | |
| 217 | + 'CC BY-SA', | |
| 218 | + 'https://creativecommons.org/licenses/by-sa/4.0/' | |
| 205 | 219 | ], [ |
| 206 | 220 | 'Django', |
| 207 | 221 | 'Django Software Foundation and individual contributors', |
| @@ -209,7 +223,7 @@ credits = [ · 1 unmodified line | ||
| 209 | 223 | 'https://raw.githubusercontent.com/django/django/master/LICENSE' |
| 210 | 224 | ], [ |
| 211 | 225 | 'Docker', |
| 212 | − '2017 Docker, Inc.<br>Docker and the Docker logo are trademarks of Docker, Inc.', | |
| 226 | + '2019 Docker, Inc.<br>Docker and the Docker logo are trademarks of Docker, Inc.', | |
| 213 | 227 | 'Apache', |
| 214 | 228 | 'https://raw.githubusercontent.com/docker/docker.github.io/master/LICENSE' |
| 215 | 229 | ], [ |
| @@ -224,12 +238,12 @@ credits = [ · 8 unmodified lines | ||
| 224 | 238 | 'https://api.drupal.org/api/drupal/LICENSE.txt' |
| 225 | 239 | ], [ |
| 226 | 240 | 'Electron', |
| 227 | − '2013-2017 GitHub Inc.', | |
| 241 | + '2013-2018 GitHub Inc.', | |
| 228 | 242 | 'MIT', |
| 229 | 243 | 'https://raw.githubusercontent.com/electron/electron/master/LICENSE' |
| 230 | 244 | ], [ |
| 231 | 245 | 'Elixir', |
| 232 | − '2012-2017 Plataformatec', | |
| 246 | + '2012 Plataformatec', | |
| 233 | 247 | 'Apache', |
| 234 | 248 | 'https://raw.githubusercontent.com/elixir-lang/elixir/master/LICENSE' |
| 235 | 249 | ], [ |
| @@ -242,6 +256,11 @@ credits = [ · 6 unmodified lines | ||
| 242 | 256 | '2010-2017 Ericsson AB', |
| 243 | 257 | 'Apache', |
| 244 | 258 | 'https://raw.githubusercontent.com/erlang/otp/maint/LICENSE.txt' |
| 259 | + ], [ | |
| 260 | + 'ESLint', | |
| 261 | + 'JS Foundation and other contributors', | |
| 262 | + 'MIT', | |
| 263 | + 'https://raw.githubusercontent.com/eslint/eslint/master/LICENSE' | |
| 245 | 264 | ], [ |
| 246 | 265 | 'Express', |
| 247 | 266 | '2017 StrongLoop, IBM, and other expressjs.com contributors.', |
| @@ -264,7 +283,7 @@ credits = [ · 16 unmodified lines | ||
| 264 | 283 | 'https://www.gnu.org/licenses/fdl-1.3.en.html' |
| 265 | 284 | ], [ |
| 266 | 285 | 'Git', |
| 267 | − '2005-2017 Linus Torvalds and others', | |
| 286 | + '2005-2018 Linus Torvalds and others', | |
| 268 | 287 | 'GPLv2', |
| 269 | 288 | 'https://raw.githubusercontent.com/git/git/master/COPYING' |
| 270 | 289 | ], [ |
| @@ -274,14 +293,24 @@ credits = [ · 3 unmodified lines | ||
| 274 | 293 | 'https://creativecommons.org/licenses/by/3.0/' |
| 275 | 294 | ], [ |
| 276 | 295 | 'Godot', |
| 277 | − '2014-2017 Juan Linietsky, Ariel Manzur, Godot Engine contributors', | |
| 296 | + '2014-2018 Juan Linietsky, Ariel Manzur, Godot Engine contributors', | |
| 278 | 297 | 'MIT', |
| 279 | 298 | 'https://raw.githubusercontent.com/godotengine/godot/master/LICENSE.txt' |
| 299 | + ], [ | |
| 300 | + 'Graphite', | |
| 301 | + '2008-2012 Chris Davis<br>© 2011-2016 The Graphite Project', | |
| 302 | + 'Apache', | |
| 303 | + 'https://raw.githubusercontent.com/graphite-project/graphite-web/master/LICENSE' | |
| 280 | 304 | ], [ |
| 281 | 305 | 'Grunt', |
| 282 | 306 | 'GruntJS Team', |
| 283 | 307 | 'MIT', |
| 284 | 308 | 'https://github.com/gruntjs/grunt-docs/blob/master/package.json#L10' |
| 309 | + ], [ | |
| 310 | + 'Handlebars', | |
| 311 | + '2011-2017 Yehuda Katz', | |
| 312 | + 'MIT', | |
| 313 | + 'https://raw.githubusercontent.com/wycats/handlebars.js/master/LICENSE' | |
| 285 | 314 | ], [ |
| 286 | 315 | 'Haskell', |
| 287 | 316 | 'The University of Glasgow', |
| @@ -289,9 +318,14 @@ credits = [ · 1 unmodified line | ||
| 289 | 318 | 'https://www.haskell.org/ghc/license' |
| 290 | 319 | ], [ |
| 291 | 320 | 'Haxe', |
| 292 | − '2005-2016 Haxe Foundation', | |
| 321 | + '2005-2018 Haxe Foundation', | |
| 293 | 322 | 'MIT', |
| 294 | − 'http://haxe.org/foundation/open-source.html' | |
| 323 | + 'https://haxe.org/foundation/open-source.html' | |
| 324 | + ], [ | |
| 325 | + 'Homebrew', | |
| 326 | + '2009-present Homebrew contributors', | |
| 327 | + 'BSD', | |
| 328 | + 'https://raw.githubusercontent.com/Homebrew/brew/master/LICENSE.txt' | |
| 295 | 329 | ], [ |
| 296 | 330 | 'Immutable.js', |
| 297 | 331 | '2014-2016 Facebook, Inc.', |
| @@ -307,6 +341,11 @@ credits = [ · 9 unmodified lines | ||
| 307 | 341 | '2008-2017 Pivotal Labs', |
| 308 | 342 | 'MIT', |
| 309 | 343 | 'https://raw.githubusercontent.com/jasmine/jasmine/master/MIT.LICENSE' |
| 344 | + ], [ | |
| 345 | + 'Jekyll', | |
| 346 | + '2008-2018 Tom Preston-Werner and Jekyll contributors', | |
| 347 | + 'MIT', | |
| 348 | + 'https://raw.githubusercontent.com/jekyll/jekyll/master/LICENSE' | |
| 310 | 349 | ], [ |
| 311 | 350 | 'Jest', |
| 312 | 351 | '2014-present Facebook Inc.', |
| @@ -329,17 +368,27 @@ credits = [ · 16 unmodified lines | ||
| 329 | 368 | 'https://raw.githubusercontent.com/jquery/api.jqueryui.com/master/LICENSE.txt' |
| 330 | 369 | ], [ |
| 331 | 370 | 'Julia', |
| 332 | − '2009-2016 Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors', | |
| 371 | + '2009-2018 Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors', | |
| 333 | 372 | 'MIT', |
| 334 | 373 | 'https://raw.githubusercontent.com/JuliaLang/julia/master/LICENSE.md' |
| 374 | + ], [ | |
| 375 | + 'JSDoc', | |
| 376 | + '2011-2017 the contributors to the JSDoc 3 documentation project', | |
| 377 | + 'CC BY-SA', | |
| 378 | + 'https://raw.githubusercontent.com/jsdoc3/jsdoc3.github.com/master/LICENSE' | |
| 335 | 379 | ], [ |
| 336 | 380 | 'Knockout.js', |
| 337 | 381 | 'Steven Sanderson, the Knockout.js team, and other contributors', |
| 338 | 382 | 'MIT', |
| 339 | 383 | 'https://raw.githubusercontent.com/knockout/knockout/master/LICENSE' |
| 384 | + ], [ | |
| 385 | + 'Koa', | |
| 386 | + '2018 Koa contributors', | |
| 387 | + 'MIT', | |
| 388 | + 'https://raw.githubusercontent.com/koajs/koa/master/LICENSE' | |
| 340 | 389 | ], [ |
| 341 | 390 | 'Kotlin', |
| 342 | − '2010-2017 JetBrains s.r.o.', | |
| 391 | + '2010-2019 JetBrains s.r.o.', | |
| 343 | 392 | 'Apache', |
| 344 | 393 | 'https://raw.githubusercontent.com/JetBrains/kotlin/master/license/LICENSE.txt' |
| 345 | 394 | ], [ |
| @@ -347,6 +396,11 @@ credits = [ · 1 unmodified line | ||
| 347 | 396 | 'Taylor Otwell', |
| 348 | 397 | 'MIT', |
| 349 | 398 | 'https://raw.githubusercontent.com/laravel/framework/master/LICENSE.txt' |
| 399 | + ], [ | |
| 400 | + 'Leaflet', | |
| 401 | + '2010-2019 Vladimir Agafonkin<br>© 2010-2011, CloudMade<br>Maps © OpenStreetMap contributors.', | |
| 402 | + 'BSD', | |
| 403 | + 'https://raw.githubusercontent.com/Leaflet/Leaflet/master/LICENSE' | |
| 350 | 404 | ], [ |
| 351 | 405 | 'Less', |
| 352 | 406 | '2009-2016 The Core Less Team', |
| @@ -359,9 +413,9 @@ credits = [ · 6 unmodified lines | ||
| 359 | 413 | 'https://raw.githubusercontent.com/Shopify/liquid/master/LICENSE' |
| 360 | 414 | ], [ |
| 361 | 415 | 'Lo-Dash', |
| 362 | − '2012-2016 The Dojo Foundation', | |
| 416 | + 'JS Foundation and other contributors', | |
| 363 | 417 | 'MIT', |
| 364 | − 'https://raw.githubusercontent.com/lodash/lodash/master/LICENSE.txt' | |
| 418 | + 'https://raw.githubusercontent.com/lodash/lodash/master/LICENSE' | |
| 365 | 419 | ], [ |
| 366 | 420 | 'Lua', |
| 367 | 421 | '1994–2017 Lua.org, PUC-Rio', |
| @@ -384,7 +438,7 @@ credits = [ · 16 unmodified lines | ||
| 384 | 438 | 'https://daringfireball.net/projects/markdown/license' |
| 385 | 439 | ], [ |
| 386 | 440 | 'Matplotlib', |
| 387 | − '2012-2017 Matplotlib Development Team. All rights reserved.', | |
| 441 | + '2012-2018 Matplotlib Development Team. All rights reserved.', | |
| 388 | 442 | 'Custom', |
| 389 | 443 | 'https://raw.githubusercontent.com/matplotlib/matplotlib/master/LICENSE/LICENSE' |
| 390 | 444 | ], [ |
| @@ -399,7 +453,7 @@ credits = [ · 8 unmodified lines | ||
| 399 | 453 | 'https://github.com/seattlerb/minitest/blob/master/README.rdoc#license' |
| 400 | 454 | ], [ |
| 401 | 455 | 'Mocha', |
| 402 | − '2016 JS Foundation and contributors', | |
| 456 | + '2011-2018 JS Foundation and contributors', | |
| 403 | 457 | 'CC BY', |
| 404 | 458 | 'https://creativecommons.org/licenses/by/4.0/' |
| 405 | 459 | ], [ |
| @@ -419,12 +473,12 @@ credits = [ · 13 unmodified lines | ||
| 419 | 473 | 'https://github.com/LearnBoost/mongoose/blob/master/README.md#license' |
| 420 | 474 | ], [ |
| 421 | 475 | 'nginx', |
| 422 | − '2002-2017 Igor Sysoev<br>© 2011-2017 Nginx, Inc.', | |
| 476 | + '2002-2019 Igor Sysoev<br>© 2011-2019 Nginx, Inc.', | |
| 423 | 477 | 'BSD', |
| 424 | 478 | 'http://nginx.org/LICENSE' |
| 425 | 479 | ], [ |
| 426 | 480 | 'nginx / Lua Module', |
| 427 | − '2009-2016 Xiaozhe Wang (chaoslawful)<br>© 2009-2017 Yichun "agentzh" Zhang (章亦春), OpenResty Inc.', | |
| 481 | + '2009-2017 Xiaozhe Wang (chaoslawful)<br>© 2009-2018 Yichun "agentzh" Zhang (章亦春), OpenResty Inc.', | |
| 428 | 482 | 'BSD', |
| 429 | 483 | 'https://github.com/openresty/lua-nginx-module#copyright-and-license' |
| 430 | 484 | ], [ |
| @@ -464,7 +518,7 @@ credits = [ · 33 unmodified lines | ||
| 464 | 518 | 'https://raw.githubusercontent.com/OpenTSDB/opentsdb.net/gh-pages/COPYING.LESSER' |
| 465 | 519 | ], [ |
| 466 | 520 | 'Padrino', |
| 467 | − '2010-2016 Padrino', | |
| 521 | + '2010-2019 Padrino', | |
| 468 | 522 | 'MIT', |
| 469 | 523 | 'https://raw.githubusercontent.com/padrino/padrino-framework/master/padrino/LICENSE.txt' |
| 470 | 524 | ], [ |
| @@ -494,7 +548,7 @@ credits = [ · 23 unmodified lines | ||
| 494 | 548 | 'https://raw.githubusercontent.com/phoenixframework/phoenix/master/LICENSE.md' |
| 495 | 549 | ], [ |
| 496 | 550 | 'PHP', |
| 497 | − '1997-2017 The PHP Documentation Group', | |
| 551 | + '1997-2018 The PHP Documentation Group', | |
| 498 | 552 | 'CC BY', |
| 499 | 553 | 'https://secure.php.net/manual/en/copyright.php' |
| 500 | 554 | ], [ |
| @@ -504,12 +558,22 @@ credits = [ · 3 unmodified lines | ||
| 504 | 558 | 'https://creativecommons.org/licenses/by/3.0/' |
| 505 | 559 | ], [ |
| 506 | 560 | 'PostgreSQL', |
| 507 | − '1996-2017 The PostgreSQL Global Development Group<br>© 1994 The Regents of the University of California', | |
| 561 | + '1996-2018 The PostgreSQL Global Development Group<br>© 1994 The Regents of the University of California', | |
| 508 | 562 | 'PostgreSQL', |
| 509 | 563 | 'https://www.postgresql.org/about/licence/' |
| 564 | + ], [ | |
| 565 | + 'Puppeteer', | |
| 566 | + '2017 Google Inc', | |
| 567 | + 'Apache', | |
| 568 | + 'https://raw.githubusercontent.com/GoogleChrome/puppeteer/master/LICENSE' | |
| 569 | + ], [ | |
| 570 | + 'Pygame', | |
| 571 | + 'Pygame Developers', | |
| 572 | + 'LGPLv2.1', | |
| 573 | + 'https://raw.githubusercontent.com/pygame/pygame/master/LICENSE' | |
| 510 | 574 | ], [ |
| 511 | 575 | 'Python', |
| 512 | − '2001-2017 Python Software Foundation<br>Python is a trademark of the Python Software Foundation.', | |
| 576 | + '2001-2018 Python Software Foundation<br>Python is a trademark of the Python Software Foundation.', | |
| 513 | 577 | 'PSFL', |
| 514 | 578 | 'https://docs.python.org/3/license.html' |
| 515 | 579 | ], [ |
| @@ -517,6 +581,11 @@ credits = [ · 1 unmodified line | ||
| 517 | 581 | '2009-2017 Kristopher Michael Kowal', |
| 518 | 582 | 'MIT', |
| 519 | 583 | 'https://raw.githubusercontent.com/kriskowal/q/master/LICENSE' |
| 584 | + ], [ | |
| 585 | + 'Qt', | |
| 586 | + '2012-2018 The Qt Company Ltd', | |
| 587 | + 'GFDL', | |
| 588 | + 'https://doc.qt.io/qt-5/licensing.html' | |
| 520 | 589 | ], [ |
| 521 | 590 | 'Ramda', |
| 522 | 591 | '2013-2016 Scott Sauyet and Michael Hurley', |
| @@ -524,12 +593,12 @@ credits = [ · 1 unmodified line | ||
| 524 | 593 | 'https://raw.githubusercontent.com/ramda/ramda/master/LICENSE.txt' |
| 525 | 594 | ], [ |
| 526 | 595 | 'React, React Native, Flow, Relay', |
| 527 | − '2013-2017 Facebook Inc.', | |
| 528 | − 'CC BY', | |
| 529 | − 'https://raw.githubusercontent.com/facebook/react/master/LICENSE-docs' | |
| 596 | + '2013-present Facebook Inc.', | |
| 597 | + 'MIT', | |
| 598 | + 'https://raw.githubusercontent.com/facebook/react/master/LICENSE' | |
| 530 | 599 | ], [ |
| 531 | 600 | 'Redis', |
| 532 | − '2009-2017 Salvatore Sanfilippo', | |
| 601 | + '2009-2018 Salvatore Sanfilippo', | |
| 533 | 602 | 'CC BY-SA', |
| 534 | 603 | 'https://creativecommons.org/licenses/by-sa/4.0/' |
| 535 | 604 | ], [ |
| @@ -574,12 +643,12 @@ credits = [ · 38 unmodified lines | ||
| 574 | 643 | 'http://scikit-image.org/docs/dev/license.html' |
| 575 | 644 | ], [ |
| 576 | 645 | 'scikit-learn', |
| 577 | − '2007-2017 The scikit-learn developers', | |
| 646 | + '2007-2018 The scikit-learn developers', | |
| 578 | 647 | 'BSD', |
| 579 | 648 | 'https://raw.githubusercontent.com/scikit-learn/scikit-learn/master/COPYING' |
| 580 | 649 | ], [ |
| 581 | 650 | 'Sinon', |
| 582 | − '2010-2017 Christian Johansen', | |
| 651 | + '2010-2018 Christian Johansen', | |
| 583 | 652 | 'BSD', |
| 584 | 653 | 'https://raw.githubusercontent.com/sinonjs/sinon/master/LICENSE' |
| 585 | 654 | ], [ |
| @@ -609,14 +678,19 @@ credits = [ · 23 unmodified lines | ||
| 609 | 678 | 'http://tcl.tk/software/tcltk/license.html' |
| 610 | 679 | ], [ |
| 611 | 680 | 'TensorFlow', |
| 612 | − '2017 The TensorFlow Authors', | |
| 681 | + '2018 The TensorFlow Authors', | |
| 613 | 682 | 'CC BY', |
| 614 | 683 | 'https://creativecommons.org/licenses/by/3.0/' |
| 684 | + ], [ | |
| 685 | + 'Terraform', | |
| 686 | + '2018 HashiCorp', | |
| 687 | + 'MPL', | |
| 688 | + 'https://raw.githubusercontent.com/hashicorp/terraform-website/master/LICENSE.md' | |
| 615 | 689 | ], [ |
| 616 | 690 | 'Twig', |
| 617 | − '2009-2017 The Twig Team', | |
| 691 | + '2009-2018 The Twig Team', | |
| 618 | 692 | 'BSD', |
| 619 | − 'https://twig.sensiolabs.org/license' | |
| 693 | + 'https://twig.symfony.com/license' | |
| 620 | 694 | ], [ |
| 621 | 695 | 'TypeScript', |
| 622 | 696 | 'Microsoft and other contributors', |
| @@ -624,17 +698,17 @@ credits = [ · 1 unmodified line | ||
| 624 | 698 | 'https://raw.githubusercontent.com/Microsoft/TypeScript-Handbook/master/LICENSE' |
| 625 | 699 | ], [ |
| 626 | 700 | 'Underscore.js', |
| 627 | − '2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors', | |
| 701 | + '2009-2018 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors', | |
| 628 | 702 | 'MIT', |
| 629 | 703 | 'https://raw.githubusercontent.com/jashkenas/underscore/master/LICENSE' |
| 630 | 704 | ], [ |
| 631 | 705 | 'Vagrant', |
| 632 | − '2010-2017 Mitchell Hashimoto', | |
| 706 | + '2010-2018 Mitchell Hashimoto', | |
| 633 | 707 | 'MPL', |
| 634 | 708 | 'https://raw.githubusercontent.com/mitchellh/vagrant/master/website/LICENSE.md' |
| 635 | 709 | ], [ |
| 636 | 710 | 'Vue.js', |
| 637 | − '2013-2017 Evan You, Vue.js contributors', | |
| 711 | + '2013-2018 Evan You, Vue.js contributors', | |
| 638 | 712 | 'MIT', |
| 639 | 713 | 'https://raw.githubusercontent.com/vuejs/vue/master/LICENSE' |
| 640 | 714 | ], [ |
| @@ -649,7 +723,7 @@ credits = [ · 8 unmodified lines | ||
| 649 | 723 | 'https://creativecommons.org/licenses/by/4.0/' |
| 650 | 724 | ], [ |
| 651 | 725 | 'Yarn', |
| 652 | − '2016-2017 Yarn Contributors', | |
| 726 | + '2016-present Yarn Contributors', | |
| 653 | 727 | 'BSD', |
| 654 | 728 | 'https://raw.githubusercontent.com/yarnpkg/yarn/master/LICENSE' |
| 655 | 729 | ], [ |
| @@ -20,7 +20,7 @@ app.templates.helpPage = -> · 19 unmodified lines | ||
| 20 | 20 | </ul> |
| 21 | 21 | </nav> |
| 22 | 22 | |
| 23 | − <h1 class="_lined-heading">User Guide</h2> | |
| 23 | + <h1 class="_lined-heading">User Guide</h1> | |
| 24 | 24 | |
| 25 | 25 | <h2 class="_block-heading" id="managing-documentations">Managing Documentations</h2> |
| 26 | 26 | <p> |
| @@ -57,9 +57,7 @@ app.templates.helpPage = -> · 30 unmodified lines | ||
| 57 | 57 | <ul> |
| 58 | 58 | <li>On Chrome, the setup is done automatically. Simply press <code class="_label">tab</code> when devdocs.io is autocompleted |
| 59 | 59 | in the omnibox (to set a custom keyword, click <em>Manage search engines\u2026</em> in Chrome's settings). |
| 60 | − <li>On Firefox, open the search engine list (icon in the search bar) and click <em>Add "DevDocs Search"</em>. | |
| 61 | − DevDocs is now available in the search bar. You can also search from the location bar by following | |
| 62 | − <a href="https://support.mozilla.org/en-US/kb/how-search-from-address-bar">these instructions</a>. | |
| 60 | + <li>On Firefox, right-click the DevDocs search field and select <em>Add a Keyword for this Search…</em>. Then, type the added keyword followed by a query in the address bar to search DevDocs. | |
| 63 | 61 | </dl> |
| 64 | 62 | <p> |
| 65 | 63 | <i>Note: the above search features only work for documentations that are enabled.</i> |
| @@ -133,6 +131,9 @@ app.templates.helpPage = -> · 67 unmodified lines | ||
| 133 | 131 | <dt class="_shortcuts-dt"> |
| 134 | 132 | <code class="_shortcut-code">alt + s</code> |
| 135 | 133 | <dd class="_shortcuts-dd">Search on Stack Overflow |
| 134 | + <dt class="_shortcuts-dt"> | |
| 135 | + <code class="_shortcut-code">alt + d</code> | |
| 136 | + <dd class="_shortcuts-dd">Search on DuckDuckGo | |
| 136 | 137 | </dl> |
| 137 | 138 | <p class="_note _note-green"> |
| 138 | 139 | <strong>Tip:</strong> If the cursor is no longer in the search field, press <code class="_label">/</code> or |
| @@ -153,5 +154,5 @@ app.templates.helpPage = -> · 14 unmodified lines | ||
| 153 | 154 | #{("<tr><td class=\"_code\">#{key}<td class=\"_code\">#{value}" for key, value of aliases_two).join('')} |
| 154 | 155 | </table> |
| 155 | 156 | </div> |
| 156 | − <p>Feel free to suggest new aliases on <a href="https://github.com/Thibaut/devdocs/issues/new">GitHub</a>. | |
| 157 | + <p>Feel free to suggest new aliases on <a href="https://github.com/freeCodeCamp/devdocs/issues/new">GitHub</a>. | |
| 157 | 158 | """ |
| @@ -4,7 +4,7 @@ app.templates.newsPage = -> · 3 unmodified lines | ||
| 4 | 4 | """ <h1 class="_lined-heading">Changelog</h1> |
| 5 | 5 | <p class="_note"> |
| 6 | 6 | For the latest news, follow <a href="https://twitter.com/DevDocs">@DevDocs</a>.<br> |
| 7 | − For development updates, follow the project on <a href="https://github.com/Thibaut/devdocs">GitHub</a>. | |
| 7 | + For development updates, follow the project on <a href="https://github.com/freeCodeCamp/devdocs">GitHub</a>. | |
| 8 | 8 | <div class="_news">#{app.templates.newsList app.news}</div> """ |
| 9 | 9 | |
| 10 | 10 | app.templates.newsList = (news, options = {}) -> |
| @@ -25,14 +25,14 @@ app.templates.offlinePage = (docs) -> """ · 24 unmodified lines | ||
| 25 | 25 | <h2 class="_block-heading">Questions & Answers</h2> |
| 26 | 26 | <dl> |
| 27 | 27 | <dt>How does this work? |
| 28 | − <dd>Each page is cached as a key-value pair in <a href="https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API">IndexedDB</a> (downloaded from a single file).<br> | |
| 29 | − The app also uses <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Using_the_application_cache">AppCache</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API">localStorage</a> to cache the assets and index files. | |
| 28 | + <dd>Each page is cached as a key-value pair in <a href="https://devdocs.io/dom/indexeddb_api">IndexedDB</a> (downloaded from a single file).<br> | |
| 29 | + The app also uses <a href="https://devdocs.io/dom/service_worker_api/using_service_workers">Service Workers</a> and <a href="https://devdocs.io/dom/web_storage_api">localStorage</a> to cache the assets and index files. | |
| 30 | 30 | <dt>Can I close the tab/browser? |
| 31 | 31 | <dd>#{canICloseTheTab()} |
| 32 | 32 | <dt>What if I don't update a documentation? |
| 33 | 33 | <dd>You'll see outdated content and some pages will be missing or broken, because the rest of the app (including data for the search and sidebar) uses a different caching mechanism that's updated automatically. |
| 34 | 34 | <dt>I found a bug, where do I report it? |
| 35 | − <dd>In the <a href="https://github.com/Thibaut/devdocs/issues">issue tracker</a>. Thanks! | |
| 35 | + <dd>In the <a href="https://github.com/freeCodeCamp/devdocs/issues">issue tracker</a>. Thanks! | |
| 36 | 36 | <dt>How do I uninstall/reset the app? |
| 37 | 37 | <dd>Click <a href="#" data-behavior="reset">here</a>. |
| 38 | 38 | <dt>Why aren't all documentations listed above? |
| @@ -41,10 +41,15 @@ app.templates.offlinePage = (docs) -> """ · 2 unmodified lines | ||
| 41 | 41 | """ |
| 42 | 42 | |
| 43 | 43 | canICloseTheTab = -> |
| 44 | − if app.AppCache.isEnabled() | |
| 44 | + if app.ServiceWorker.isEnabled() | |
| 45 | 45 | """ Yes! Even offline, you can open a new tab, go to <a href="//devdocs.io">devdocs.io</a>, and everything will work as if you were online (provided you installed all the documentations you want to use beforehand). """ |
| 46 | 46 | else |
| 47 | − """ No. AppCache isn't available in your browser (or is disabled), so loading <a href="//devdocs.io">devdocs.io</a> offline won't work.<br> | |
| 47 | + reason = "aren't available in your browser (or are disabled)" | |
| 48 | + | |
| 49 | + if app.config.env != 'production' | |
| 50 | + reason = "are disabled in your development instance of DevDocs (enable them by setting the <code>ENABLE_SERVICE_WORKER</code> environment variable to <code>true</code>)" | |
| 51 | + | |
| 52 | + """ No. Service Workers #{reason}, so loading <a href="//devdocs.io">devdocs.io</a> offline won't work.<br> | |
| 48 | 53 | The current tab will continue to function even when you go offline (provided you installed all the documentations beforehand). """ |
| 49 | 54 | |
| 50 | 55 | app.templates.offlineDoc = (doc, status) -> |
| @@ -8,18 +8,17 @@ app.templates.intro = """ · 7 unmodified lines | ||
| 8 | 8 | <p>Thanks for downloading DevDocs. Here are a few things you should know: |
| 9 | 9 | <ol class="_intro-list"> |
| 10 | 10 | <li>Your local version of DevDocs won't self-update. Unless you're modifying the code, |
| 11 | − I recommend using the hosted version at <a href="http://devdocs.io">devdocs.io</a>. | |
| 11 | + we recommend using the hosted version at <a href="https://devdocs.io">devdocs.io</a>. | |
| 12 | 12 | <li>Run <code>thor docs:list</code> to see all available documentations. |
| 13 | 13 | <li>Run <code>thor docs:download <name></code> to download documentations. |
| 14 | 14 | <li>Run <code>thor docs:download --installed</code> to update all downloaded documentations. |
| 15 | − <li>To be notified about new versions, don't forget to <a href="https://github.com/Thibaut/devdocs/subscription">watch the repository</a> on GitHub. | |
| 16 | − <li>The <a href="https://github.com/Thibaut/devdocs/issues">issue tracker</a> is the preferred channel for bug reports and | |
| 17 | − feature requests. For everything else, use the <a href="https://groups.google.com/d/forum/devdocs">mailing list</a>. | |
| 18 | − <li>Contributions are welcome. See the <a href="https://github.com/Thibaut/devdocs/blob/master/CONTRIBUTING.md">guidelines</a>. | |
| 15 | + <li>To be notified about new versions, don't forget to <a href="https://github.com/freeCodeCamp/devdocs/subscription">watch the repository</a> on GitHub. | |
| 16 | + <li>The <a href="https://github.com/freeCodeCamp/devdocs/issues">issue tracker</a> is the preferred channel for bug reports and | |
| 17 | + feature requests. For everything else, use <a href="https://gitter.im/FreeCodeCamp/DevDocs">Gitter</a>. | |
| 18 | + <li>Contributions are welcome. See the <a href="https://github.com/freeCodeCamp/devdocs/blob/master/CONTRIBUTING.md">guidelines</a>. | |
| 19 | 19 | <li>DevDocs is licensed under the terms of the Mozilla Public License v2.0. For more information, |
| 20 | − see the <a href="https://github.com/Thibaut/devdocs/blob/master/COPYRIGHT">COPYRIGHT</a> and | |
| 21 | − <a href="https://github.com/Thibaut/devdocs/blob/master/LICENSE">LICENSE</a> files. | |
| 22 | − <li>If you like the app, please consider supporting the project on <a href="https://gratipay.com/devdocs/">Gratipay</a>. Thanks! | |
| 20 | + see the <a href="https://github.com/freeCodeCamp/devdocs/blob/master/COPYRIGHT">COPYRIGHT</a> and | |
| 21 | + <a href="https://github.com/freeCodeCamp/devdocs/blob/master/LICENSE">LICENSE</a> files. | |
| 23 | 22 | </ol> |
| 24 | 23 | <p>Happy coding! |
| 25 | 24 | </div></div> |
| @@ -39,9 +38,8 @@ app.templates.intro = """ · 13 unmodified lines | ||
| 39 | 38 | <li>You can search using your browser's address bar — <a href="/help#browser_search">learn how</a>. |
| 40 | 39 | <li>DevDocs works <a href="/offline">offline</a>, on mobile, and can be installed on <a href="https://chrome.google.com/webstore/detail/devdocs/mnfehgbmkapmjnhcnbodoamcioleeooe">Chrome</a>. |
| 41 | 40 | <li>For the latest news, follow <a href="https://twitter.com/DevDocs">@DevDocs</a>. |
| 42 | − <li>DevDocs is free and <a href="https://github.com/Thibaut/devdocs">open source</a>. | |
| 43 | − <iframe class="_github-btn" src="//ghbtns.com/github-btn.html?user=Thibaut&repo=devdocs&type=watch&count=true" allowtransparency="true" frameborder="0" scrolling="0" width="100" height="20"></iframe> | |
| 44 | − <li>If you like the app, please consider supporting the project on <a href="https://gratipay.com/devdocs/">Gratipay</a>. Thanks! | |
| 41 | + <li>DevDocs is free and <a href="https://github.com/freeCodeCamp/devdocs">open source</a>. | |
| 42 | + <object data="https://img.shields.io/github/stars/freeCodeCamp/devdocs.svg?style=social" type="image/svg+xml" aria-hidden="true"></object> | |
| 45 | 43 | <li>And if you're new to coding, check out <a href="https://www.freecodecamp.org/">freeCodeCamp's open source curriculum</a>. |
| 46 | 44 | </ol> |
| 47 | 45 | <p>Happy coding! |
| @@ -59,7 +57,7 @@ app.templates.mobileIntro = """ · 11 unmodified lines | ||
| 59 | 57 | <li>The search supports fuzzy matching. |
| 60 | 58 | <li>To search a specific documentation, type its name (or an abbr.), then Space. |
| 61 | 59 | <li>For the latest news, follow <a href="https://twitter.com/DevDocs">@DevDocs</a>. |
| 62 | − <li>DevDocs is <a href="https://github.com/Thibaut/devdocs">open source</a>. | |
| 60 | + <li>DevDocs is <a href="https://github.com/freeCodeCamp/devdocs">open source</a>. | |
| 63 | 61 | </ol> |
| 64 | 62 | <p>Happy coding! |
| 65 | 63 | <a class="_intro-hide" data-hide-intro>Stop showing this message</a> |
| @@ -71,6 +69,6 @@ app.templates.androidWarning = """ · 5 unmodified lines | ||
| 71 | 69 | <h2 class="_intro-title">Hi there</h2> |
| 72 | 70 | <p>DevDocs is running inside an Android WebView. Some features may not work properly. |
| 73 | 71 | <p>If you downloaded an app called DevDocs on the Play Store, please uninstall it — it's made by someone who is using (and profiting from) the name DevDocs without permission. |
| 74 | − <p>To install DevDocs on your phone, visit <a href="http://devdocs.io" target="_blank" rel="noopener">devdocs.io</a> in Chrome and select "Add to home screen" in the menu. | |
| 72 | + <p>To install DevDocs on your phone, visit <a href="https://devdocs.io" target="_blank" rel="noopener">devdocs.io</a> in Chrome and select "Add to home screen" in the menu. | |
| 75 | 73 | </div> |
| 76 | 74 | """ |
| @@ -15,6 +15,14 @@ app.templates.settingsPage = (settings) -> """ · 14 unmodified lines | ||
| 15 | 15 | <input type="checkbox" form="settings" name="layout" value="_sidebar-hidden"#{if settings['_sidebar-hidden'] then ' checked' else ''}>Automatically hide and show the sidebar |
| 16 | 16 | <small>Tip: drag the edge of the sidebar to resize it.</small> |
| 17 | 17 | </label> |
| 18 | + <label class="_settings-label"> | |
| 19 | + <input type="checkbox" form="settings" name="autoInstall" value="_auto-install"#{if settings.autoInstall then ' checked' else ''}>Automatically download documentation for offline use | |
| 20 | + <small>Only enable this when bandwidth isn't a concern to you.</small> | |
| 21 | + </label> | |
| 22 | + <label class="_settings-label"> | |
| 23 | + <input type="checkbox" form="settings" name="analyticsConsent"#{if settings.analyticsConsent then ' checked' else ''}>Enable tracking cookies | |
| 24 | + <small>With this checked, we enable Google Analytics and Gauges to collect anonymous traffic information.</small> | |
| 25 | + </label> | |
| 18 | 26 | </div> |
| 19 | 27 | </div> |
| 20 | 28 | |
| @@ -1,28 +1,32 @@ | ||
| 1 | 1 | try { |
| 2 | − if (app.config.env == 'production') { | |
| 3 | − (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ | |
| 4 | − (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), | |
| 5 | − m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) | |
| 6 | − })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); | |
| 7 | − ga('create', 'UA-5544833-12', 'devdocs.io'); | |
| 8 | − page.track(function() { | |
| 9 | − ga('send', 'pageview', { | |
| 10 | − page: location.pathname + location.search + location.hash, | |
| 11 | − dimension1: app.router.context && app.router.context.doc && app.router.context.doc.slug_without_version | |
| 2 | + if (app.config.env === 'production') { | |
| 3 | + if (Cookies.get('analyticsConsent') === '1') { | |
| 4 | + (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ | |
| 5 | + (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), | |
| 6 | + m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) | |
| 7 | + })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); | |
| 8 | + ga('create', 'UA-5544833-12', 'devdocs.io'); | |
| 9 | + page.track(function() { | |
| 10 | + ga('send', 'pageview', { | |
| 11 | + page: location.pathname + location.search + location.hash, | |
| 12 | + dimension1: app.router.context && app.router.context.doc && app.router.context.doc.slug_without_version | |
| 13 | + }); | |
| 12 | 14 | }); |
| 13 | − }); | |
| 14 | 15 | |
| 15 | − page.track(function() { | |
| 16 | − if (window._gauges) | |
| 17 | − _gauges.push(['track']); | |
| 18 | − else | |
| 19 | − (function() { | |
| 20 | − var _gauges=_gauges||[];!function(){var a=document.createElement("script"); | |
| 21 | − a.type="text/javascript",a.async=!0,a.id="gauges-tracker", | |
| 22 | − a.setAttribute("data-site-id","51c15f82613f5d7819000067"), | |
| 23 | − a.src="https://secure.gaug.es/track.js";var b=document.getElementsByTagName("script")[0]; | |
| 24 | − b.parentNode.insertBefore(a,b)}(); | |
| 25 | − })(); | |
| 26 | − }); | |
| 16 | + page.track(function() { | |
| 17 | + if (window._gauges) | |
| 18 | + _gauges.push(['track']); | |
| 19 | + else | |
| 20 | + (function() { | |
| 21 | + var _gauges=_gauges||[];!function(){var a=document.createElement("script"); | |
| 22 | + a.type="text/javascript",a.async=!0,a.id="gauges-tracker", | |
| 23 | + a.setAttribute("data-site-id","51c15f82613f5d7819000067"), | |
| 24 | + a.src="https://secure.gaug.es/track.js";var b=document.getElementsByTagName("script")[0]; | |
| 25 | + b.parentNode.insertBefore(a,b)}(); | |
| 26 | + })(); | |
| 27 | + }); | |
| 28 | + } else { | |
| 29 | + resetAnalytics(); | |
| 30 | + } | |
| 27 | 31 | } |
| 28 | 32 | } catch(e) { } |
| @@ -413,7 +413,7 @@ · 412 unmodified lines | ||
| 413 | 413 | // when the user next taps anywhere else on the page, new touchstart and touchend events are dispatched |
| 414 | 414 | // with the same identifier as the touch event that previously triggered the click that triggered the alert. |
| 415 | 415 | // Sadly, there is an issue on iOS 4 that causes some normal touch events to have the same identifier as an |
| 416 | − // immediately preceeding touch event (issue #52), so this fix is unavailable on that platform. | |
| 416 | + // immediately preceding touch event (issue #52), so this fix is unavailable on that platform. | |
| 417 | 417 | // Issue 120: touch.identifier is 0 when Chrome dev tools 'Emulate touch events' is set with an iOS device UA string, |
| 418 | 418 | // which causes all touch events to be ignored. As this block only applies to iOS, and iOS identifiers are always long, |
| 419 | 419 | // random integers, it's safe to to continue if the identifier is 0 here. |
| @@ -805,7 +805,7 @@ · 385 unmodified lines | ||
| 805 | 805 | } |
| 806 | 806 | } |
| 807 | 807 | |
| 808 | − // IE11: prefixed -ms-touch-action is no longer supported and it's recomended to use non-prefixed version | |
| 808 | + // IE11: prefixed -ms-touch-action is no longer supported and it's recommended to use non-prefixed version | |
| 809 | 809 | // http://msdn.microsoft.com/en-us/library/windows/apps/Hh767313.aspx |
| 810 | 810 | if (layer.style.touchAction === 'none' || layer.style.touchAction === 'manipulation') { |
| 811 | 811 | return true; |
| @@ -1,4 +1,5 @@ | ||
| 1 | −/* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript+c+cpp+coffeescript+ruby+d+elixir+erlang+go+java+json+kotlin+lua+nginx+nim+perl+php+python+jsx+crystal+rust+scss+sql+typescript */ | |
| 1 | +/* PrismJS 1.15.0 | |
| 2 | +https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+c+bash+cpp+coffeescript+ruby+d+dart+django+elixir+markup-templating+erlang+go+java+json+kotlin+lua+crystal+nginx+nim+perl+php+sql+scss+python+jsx+typescript+rust+yaml */ | |
| 2 | 3 | var _self = (typeof window !== 'undefined') |
| 3 | 4 | ? window // if in browser |
| 4 | 5 | : ( |
| @@ -16,11 +17,12 @@ var _self = (typeof window !== 'undefined') · 11 unmodified lines | ||
| 16 | 17 | var Prism = (function(){ |
| 17 | 18 | |
| 18 | 19 | // Private helper vars |
| 19 | −var lang = /\blang(?:uage)?-(\w+)\b/i; | |
| 20 | +var lang = /\blang(?:uage)?-([\w-]+)\b/i; | |
| 20 | 21 | var uniqueId = 0; |
| 21 | 22 | |
| 22 | 23 | var _ = _self.Prism = { |
| 23 | 24 | manual: _self.Prism && _self.Prism.manual, |
| 25 | + disableWorkerMessageHandler: _self.Prism && _self.Prism.disableWorkerMessageHandler, | |
| 24 | 26 | util: { |
| 25 | 27 | encode: function (tokens) { |
| 26 | 28 | if (tokens instanceof Token) { |
| @@ -44,23 +46,38 @@ var _ = _self.Prism = { · 17 unmodified lines | ||
| 44 | 46 | }, |
| 45 | 47 | |
| 46 | 48 | // Deep clone a language definition (e.g. to extend it) |
| 47 | − clone: function (o) { | |
| 49 | + clone: function (o, visited) { | |
| 48 | 50 | var type = _.util.type(o); |
| 51 | + visited = visited || {}; | |
| 49 | 52 | |
| 50 | 53 | switch (type) { |
| 51 | 54 | case 'Object': |
| 55 | + if (visited[_.util.objId(o)]) { | |
| 56 | + return visited[_.util.objId(o)]; | |
| 57 | + } | |
| 52 | 58 | var clone = {}; |
| 59 | + visited[_.util.objId(o)] = clone; | |
| 53 | 60 | |
| 54 | 61 | for (var key in o) { |
| 55 | 62 | if (o.hasOwnProperty(key)) { |
| 56 | − clone[key] = _.util.clone(o[key]); | |
| 63 | + clone[key] = _.util.clone(o[key], visited); | |
| 57 | 64 | } |
| 58 | 65 | } |
| 59 | 66 | |
| 60 | 67 | return clone; |
| 61 | 68 | |
| 62 | 69 | case 'Array': |
| 63 | − return o.map(function(v) { return _.util.clone(v); }); | |
| 70 | + if (visited[_.util.objId(o)]) { | |
| 71 | + return visited[_.util.objId(o)]; | |
| 72 | + } | |
| 73 | + var clone = []; | |
| 74 | + visited[_.util.objId(o)] = clone; | |
| 75 | + | |
| 76 | + o.forEach(function (v, i) { | |
| 77 | + clone[i] = _.util.clone(v, visited); | |
| 78 | + }); | |
| 79 | + | |
| 80 | + return clone; | |
| 64 | 81 | } |
| 65 | 82 | |
| 66 | 83 | return o; |
| @@ -155,6 +172,10 @@ var _ = _self.Prism = { · 88 unmodified lines | ||
| 155 | 172 | plugins: {}, |
| 156 | 173 | |
| 157 | 174 | highlightAll: function(async, callback) { |
| 175 | + _.highlightAllUnder(document, async, callback); | |
| 176 | + }, | |
| 177 | + | |
| 178 | + highlightAllUnder: function(container, async, callback) { | |
| 158 | 179 | var env = { |
| 159 | 180 | callback: callback, |
| 160 | 181 | selector: 'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code' |
| @@ -162,7 +183,7 @@ var _ = _self.Prism = { · 1 unmodified line | ||
| 162 | 183 | |
| 163 | 184 | _.hooks.run("before-highlightall", env); |
| 164 | 185 | |
| 165 | − var elements = env.elements || document.querySelectorAll(env.selector); | |
| 186 | + var elements = env.elements || container.querySelectorAll(env.selector); | |
| 166 | 187 | |
| 167 | 188 | for (var i=0, element; element = elements[i++];) { |
| 168 | 189 | _.highlightElement(element, async === true, env.callback); |
| @@ -185,11 +206,13 @@ var _ = _self.Prism = { · 16 unmodified lines | ||
| 185 | 206 | // Set language on the element, if not present |
| 186 | 207 | element.className = element.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language; |
| 187 | 208 | |
| 188 | − // Set language on the parent, for styling | |
| 189 | − parent = element.parentNode; | |
| 209 | + if (element.parentNode) { | |
| 210 | + // Set language on the parent, for styling | |
| 211 | + parent = element.parentNode; | |
| 190 | 212 | |
| 191 | − if (/pre/i.test(parent.nodeName)) { | |
| 192 | − parent.className = parent.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language; | |
| 213 | + if (/pre/i.test(parent.nodeName)) { | |
| 214 | + parent.className = parent.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language; | |
| 215 | + } | |
| 193 | 216 | } |
| 194 | 217 | |
| 195 | 218 | var code = element.textContent; |
| @@ -251,8 +274,15 @@ var _ = _self.Prism = { · 55 unmodified lines | ||
| 251 | 274 | }, |
| 252 | 275 | |
| 253 | 276 | highlight: function (text, grammar, language) { |
| 254 | − var tokens = _.tokenize(text, grammar); | |
| 255 | − return Token.stringify(_.util.encode(tokens), language); | |
| 277 | + var env = { | |
| 278 | + code: text, | |
| 279 | + grammar: grammar, | |
| 280 | + language: language | |
| 281 | + }; | |
| 282 | + _.hooks.run('before-tokenize', env); | |
| 283 | + env.tokens = _.tokenize(env.code, env.grammar); | |
| 284 | + _.hooks.run('after-tokenize', env); | |
| 285 | + return Token.stringify(_.util.encode(env.tokens), env.language); | |
| 256 | 286 | }, |
| 257 | 287 | |
| 258 | 288 | matchGrammar: function (text, strarr, grammar, index, startPos, oneshot, target) { |
| @@ -300,15 +330,9 @@ var _ = _self.Prism = { · 41 unmodified lines | ||
| 300 | 330 | continue; |
| 301 | 331 | } |
| 302 | 332 | |
| 303 | − pattern.lastIndex = 0; | |
| 304 | − | |
| 305 | − var match = pattern.exec(str), | |
| 306 | − delNum = 1; | |
| 307 | − | |
| 308 | − // Greedy patterns can override/remove up to two previously matched tokens | |
| 309 | − if (!match && greedy && i != strarr.length - 1) { | |
| 333 | + if (greedy && i != strarr.length - 1) { | |
| 310 | 334 | pattern.lastIndex = pos; |
| 311 | − match = pattern.exec(text); | |
| 335 | + var match = pattern.exec(text); | |
| 312 | 336 | if (!match) { |
| 313 | 337 | break; |
| 314 | 338 | } |
| @@ -327,11 +351,8 @@ var _ = _self.Prism = { · 12 unmodified lines | ||
| 327 | 351 | } |
| 328 | 352 | } |
| 329 | 353 | |
| 330 | − /* | |
| 331 | − * If strarr[i] is a Token, then the match starts inside another Token, which is invalid | |
| 332 | − * If strarr[k - 1] is greedy we are in conflict with another greedy pattern | |
| 333 | − */ | |
| 334 | − if (strarr[i] instanceof Token || strarr[k - 1].greedy) { | |
| 354 | + // If strarr[i] is a Token, then the match starts inside another Token, which is invalid | |
| 355 | + if (strarr[i] instanceof Token) { | |
| 335 | 356 | continue; |
| 336 | 357 | } |
| 337 | 358 | |
| @@ -339,6 +360,11 @@ var _ = _self.Prism = { · 1 unmodified line | ||
| 339 | 360 | delNum = k - i; |
| 340 | 361 | str = text.slice(pos, p); |
| 341 | 362 | match.index -= pos; |
| 363 | + } else { | |
| 364 | + pattern.lastIndex = 0; | |
| 365 | + | |
| 366 | + var match = pattern.exec(str), | |
| 367 | + delNum = 1; | |
| 342 | 368 | } |
| 343 | 369 | |
| 344 | 370 | if (!match) { |
| @@ -350,7 +376,7 @@ var _ = _self.Prism = { · 5 unmodified lines | ||
| 350 | 376 | } |
| 351 | 377 | |
| 352 | 378 | if(lookbehind) { |
| 353 | − lookbehindLength = match[1].length; | |
| 379 | + lookbehindLength = match[1] ? match[1].length : 0; | |
| 354 | 380 | } |
| 355 | 381 | |
| 356 | 382 | var from = match.index + lookbehindLength, |
| @@ -460,10 +486,6 @@ Token.stringify = function(o, language, parent) { · 103 unmodified lines | ||
| 460 | 486 | parent: parent |
| 461 | 487 | }; |
| 462 | 488 | |
| 463 | − if (env.type == 'comment') { | |
| 464 | − env.attributes['spellcheck'] = 'true'; | |
| 465 | − } | |
| 466 | − | |
| 467 | 489 | if (o.alias) { |
| 468 | 490 | var aliases = _.util.type(o.alias) === 'Array' ? o.alias : [o.alias]; |
| 469 | 491 | Array.prototype.push.apply(env.classes, aliases); |
| @@ -484,18 +506,21 @@ Token.stringify = function(o, language, parent) { · 14 unmodified lines | ||
| 484 | 506 | // // in Node.js |
| 485 | 507 | // return _self.Prism; |
| 486 | 508 | // } |
| 487 | −// // In worker | |
| 488 | −// _self.addEventListener('message', function(evt) { | |
| 489 | −// var message = JSON.parse(evt.data), | |
| 490 | −// lang = message.language, | |
| 491 | −// code = message.code, | |
| 492 | −// immediateClose = message.immediateClose; | |
| 493 | − | |
| 494 | −// _self.postMessage(_.highlight(code, _.languages[lang], lang)); | |
| 495 | −// if (immediateClose) { | |
| 496 | −// _self.close(); | |
| 497 | −// } | |
| 498 | −// }, false); | |
| 509 | + | |
| 510 | +// if (!_.disableWorkerMessageHandler) { | |
| 511 | +// // In worker | |
| 512 | +// _self.addEventListener('message', function (evt) { | |
| 513 | +// var message = JSON.parse(evt.data), | |
| 514 | +// lang = message.language, | |
| 515 | +// code = message.code, | |
| 516 | +// immediateClose = message.immediateClose; | |
| 517 | + | |
| 518 | +// _self.postMessage(_.highlight(code, _.languages[lang], lang)); | |
| 519 | +// if (immediateClose) { | |
| 520 | +// _self.close(); | |
| 521 | +// } | |
| 522 | +// }, false); | |
| 523 | +// } | |
| 499 | 524 | |
| 500 | 525 | // return _self.Prism; |
| 501 | 526 | // } |
| @@ -539,7 +564,8 @@ Prism.languages.markup = { · 37 unmodified lines | ||
| 539 | 564 | 'doctype': /<!DOCTYPE[\s\S]+?>/i, |
| 540 | 565 | 'cdata': /<!\[CDATA\[[\s\S]*?]]>/i, |
| 541 | 566 | 'tag': { |
| 542 | − pattern: /<\/?(?!\d)[^\s>\/=$<]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\\1|\\?(?!\1)[\s\S])*\1|[^\s'">=]+))?)*\s*\/?>/i, | |
| 567 | + pattern: /<\/?(?!\d)[^\s>\/=$<%]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+))?)*\s*\/?>/i, | |
| 568 | + greedy: true, | |
| 543 | 569 | inside: { |
| 544 | 570 | 'tag': { |
| 545 | 571 | pattern: /^<\/?[^\s>\/]+/i, |
| @@ -549,9 +575,15 @@ Prism.languages.markup = { · 3 unmodified lines | ||
| 549 | 575 | } |
| 550 | 576 | }, |
| 551 | 577 | 'attr-value': { |
| 552 | − pattern: /=(?:('|")[\s\S]*?(\1)|[^\s>]+)/i, | |
| 578 | + pattern: /=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+)/i, | |
| 553 | 579 | inside: { |
| 554 | − 'punctuation': /[=>"']/ | |
| 580 | + 'punctuation': [ | |
| 581 | + /^=/, | |
| 582 | + { | |
| 583 | + pattern: /(^|[^\\])["']/, | |
| 584 | + lookbehind: true | |
| 585 | + } | |
| 586 | + ] | |
| 555 | 587 | } |
| 556 | 588 | }, |
| 557 | 589 | 'punctuation': /\/?>/, |
| @@ -586,25 +618,25 @@ Prism.languages.svg = Prism.languages.markup; · 28 unmodified lines | ||
| 586 | 618 | Prism.languages.css = { |
| 587 | 619 | 'comment': /\/\*[\s\S]*?\*\//, |
| 588 | 620 | 'atrule': { |
| 589 | − pattern: /@[\w-]+?.*?(;|(?=\s*\{))/i, | |
| 621 | + pattern: /@[\w-]+?.*?(?:;|(?=\s*\{))/i, | |
| 590 | 622 | inside: { |
| 591 | 623 | 'rule': /@[\w-]+/ |
| 592 | 624 | // See rest below |
| 593 | 625 | } |
| 594 | 626 | }, |
| 595 | − 'url': /url\((?:(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1|.*?)\)/i, | |
| 596 | − 'selector': /[^\{\}\s][^\{\};]*?(?=\s*\{)/, | |
| 627 | + 'url': /url\((?:(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1|.*?)\)/i, | |
| 628 | + 'selector': /[^{}\s][^{};]*?(?=\s*\{)/, | |
| 597 | 629 | 'string': { |
| 598 | − pattern: /("|')(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/, | |
| 630 | + pattern: /("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/, | |
| 599 | 631 | greedy: true |
| 600 | 632 | }, |
| 601 | − 'property': /(\b|\B)[\w-]+(?=\s*:)/i, | |
| 633 | + 'property': /[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i, | |
| 602 | 634 | 'important': /\B!important\b/i, |
| 603 | 635 | 'function': /[-a-z0-9]+(?=\()/i, |
| 604 | 636 | 'punctuation': /[(){};:]/ |
| 605 | 637 | }; |
| 606 | 638 | |
| 607 | −Prism.languages.css['atrule'].inside.rest = Prism.util.clone(Prism.languages.css); | |
| 639 | +Prism.languages.css['atrule'].inside.rest = Prism.languages.css; | |
| 608 | 640 | |
| 609 | 641 | if (Prism.languages.markup) { |
| 610 | 642 | Prism.languages.insertBefore('markup', 'tag', { |
| @@ -612,13 +644,14 @@ if (Prism.languages.markup) { · 1 unmodified line | ||
| 612 | 644 | pattern: /(<style[\s\S]*?>)[\s\S]*?(?=<\/style>)/i, |
| 613 | 645 | lookbehind: true, |
| 614 | 646 | inside: Prism.languages.css, |
| 615 | − alias: 'language-css' | |
| 647 | + alias: 'language-css', | |
| 648 | + greedy: true | |
| 616 | 649 | } |
| 617 | 650 | }); |
| 618 | 651 | |
| 619 | 652 | Prism.languages.insertBefore('inside', 'attr-value', { |
| 620 | 653 | 'style-attr': { |
| 621 | − pattern: /\s*style=("|').*?\1/i, | |
| 654 | + pattern: /\s*style=("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/i, | |
| 622 | 655 | inside: { |
| 623 | 656 | 'attr-name': { |
| 624 | 657 | pattern: /^\s*style/i, |
| @@ -642,63 +675,71 @@ Prism.languages.clike = { · 17 unmodified lines | ||
| 642 | 675 | }, |
| 643 | 676 | { |
| 644 | 677 | pattern: /(^|[^\\:])\/\/.*/, |
| 645 | − lookbehind: true | |
| 678 | + lookbehind: true, | |
| 679 | + greedy: true | |
| 646 | 680 | } |
| 647 | 681 | ], |
| 648 | 682 | 'string': { |
| 649 | − pattern: /(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/, | |
| 683 | + pattern: /(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/, | |
| 650 | 684 | greedy: true |
| 651 | 685 | }, |
| 652 | 686 | 'class-name': { |
| 653 | − pattern: /((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/i, | |
| 687 | + pattern: /((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[\w.\\]+/i, | |
| 654 | 688 | lookbehind: true, |
| 655 | 689 | inside: { |
| 656 | − punctuation: /(\.|\\)/ | |
| 690 | + punctuation: /[.\\]/ | |
| 657 | 691 | } |
| 658 | 692 | }, |
| 659 | − 'keyword': /\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/, | |
| 660 | − 'boolean': /\b(true|false)\b/, | |
| 693 | + 'keyword': /\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/, | |
| 694 | + 'boolean': /\b(?:true|false)\b/, | |
| 661 | 695 | 'function': /[a-z0-9_]+(?=\()/i, |
| 662 | − 'number': /\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)\b/i, | |
| 696 | + 'number': /\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i, | |
| 663 | 697 | 'operator': /--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/, |
| 664 | 698 | 'punctuation': /[{}[\];(),.:]/ |
| 665 | 699 | }; |
| 666 | 700 | |
| 667 | 701 | Prism.languages.javascript = Prism.languages.extend('clike', { |
| 668 | − 'keyword': /\b(as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield)\b/, | |
| 669 | − 'number': /\b-?(0[xX][\dA-Fa-f]+|0[bB][01]+|0[oO][0-7]+|\d*\.?\d+([Ee][+-]?\d+)?|NaN|Infinity)\b/, | |
| 702 | + 'keyword': /\b(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield)\b/, | |
| 703 | + 'number': /\b(?:0[xX][\dA-Fa-f]+|0[bB][01]+|0[oO][0-7]+|NaN|Infinity)\b|(?:\b\d+\.?\d*|\B\.\d+)(?:[Ee][+-]?\d+)?/, | |
| 670 | 704 | // Allow for all non-ASCII characters (See http://stackoverflow.com/a/2008444) |
| 671 | − 'function': /[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*(?=\()/i, | |
| 705 | + 'function': /[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*\()/i, | |
| 672 | 706 | 'operator': /-[-=]?|\+[+=]?|!=?=?|<<?=?|>>?>?=?|=(?:==?|>)?|&[&=]?|\|[|=]?|\*\*?=?|\/=?|~|\^=?|%=?|\?|\.{3}/ |
| 673 | 707 | }); |
| 674 | 708 | |
| 675 | 709 | Prism.languages.insertBefore('javascript', 'keyword', { |
| 676 | 710 | 'regex': { |
| 677 | − pattern: /(^|[^/])\/(?!\/)(\[[^\]\r\n]+]|\\.|[^/\\\[\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})]))/, | |
| 711 | + pattern: /((?:^|[^$\w\xA0-\uFFFF."'\])\s])\s*)\/(\[[^\]\r\n]+]|\\.|[^/\\\[\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})\]]))/, | |
| 678 | 712 | lookbehind: true, |
| 679 | 713 | greedy: true |
| 680 | − } | |
| 714 | + }, | |
| 715 | + // This must be declared before keyword because we use "function" inside the look-forward | |
| 716 | + 'function-variable': { | |
| 717 | + pattern: /[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=\s*(?:function\b|(?:\([^()]*\)|[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/i, | |
| 718 | + alias: 'function' | |
| 719 | + }, | |
| 720 | + 'constant': /\b[A-Z][A-Z\d_]*\b/ | |
| 681 | 721 | }); |
| 682 | 722 | |
| 683 | 723 | Prism.languages.insertBefore('javascript', 'string', { |
| 684 | 724 | 'template-string': { |
| 685 | − pattern: /`(?:\\\\|\\?[^\\])*?`/, | |
| 725 | + pattern: /`(?:\\[\s\S]|\${[^}]+}|[^\\`])*`/, | |
| 686 | 726 | greedy: true, |
| 687 | 727 | inside: { |
| 688 | 728 | 'interpolation': { |
| 689 | − pattern: /\$\{[^}]+\}/, | |
| 729 | + pattern: /\${[^}]+}/, | |
| 690 | 730 | inside: { |
| 691 | 731 | 'interpolation-punctuation': { |
| 692 | − pattern: /^\$\{|\}$/, | |
| 732 | + pattern: /^\${|}$/, | |
| 693 | 733 | alias: 'punctuation' |
| 694 | 734 | }, |
| 695 | − rest: Prism.languages.javascript | |
| 735 | + rest: null // See below | |
| 696 | 736 | } |
| 697 | 737 | }, |
| 698 | 738 | 'string': /[\s\S]+/ |
| 699 | 739 | } |
| 700 | 740 | } |
| 701 | 741 | }); |
| 742 | +Prism.languages.javascript['template-string'].inside['interpolation'].inside.rest = Prism.languages.javascript; | |
| 702 | 743 | |
| 703 | 744 | if (Prism.languages.markup) { |
| 704 | 745 | Prism.languages.insertBefore('markup', 'tag', { |
| @@ -706,7 +747,8 @@ if (Prism.languages.markup) { · 1 unmodified line | ||
| 706 | 747 | pattern: /(<script[\s\S]*?>)[\s\S]*?(?=<\/script>)/i, |
| 707 | 748 | lookbehind: true, |
| 708 | 749 | inside: Prism.languages.javascript, |
| 709 | − alias: 'language-javascript' | |
| 750 | + alias: 'language-javascript', | |
| 751 | + greedy: true | |
| 710 | 752 | } |
| 711 | 753 | }); |
| 712 | 754 | } |
| @@ -714,51 +756,145 @@ if (Prism.languages.markup) { · 1 unmodified line | ||
| 714 | 756 | Prism.languages.js = Prism.languages.javascript; |
| 715 | 757 | |
| 716 | 758 | Prism.languages.c = Prism.languages.extend('clike', { |
| 717 | − 'keyword': /\b(_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|asm|typeof|inline|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|union|unsigned|void|volatile|while)\b/, | |
| 718 | − 'operator': /\-[>-]?|\+\+?|!=?|<<?=?|>>?=?|==?|&&?|\|?\||[~^%?*\/]/, | |
| 719 | − 'number': /\b-?(?:0x[\da-f]+|\d*\.?\d+(?:e[+-]?\d+)?)[ful]*\b/i | |
| 759 | + 'keyword': /\b(?:_Alignas|_Alignof|_Atomic|_Bool|_Complex|_Generic|_Imaginary|_Noreturn|_Static_assert|_Thread_local|asm|typeof|inline|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|union|unsigned|void|volatile|while)\b/, | |
| 760 | + 'operator': /-[>-]?|\+\+?|!=?|<<?=?|>>?=?|==?|&&?|\|\|?|[~^%?*\/]/, | |
| 761 | + 'number': /(?:\b0x[\da-f]+|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?)[ful]*/i | |
| 720 | 762 | }); |
| 721 | 763 | |
| 722 | 764 | Prism.languages.insertBefore('c', 'string', { |
| 723 | 765 | 'macro': { |
| 724 | 766 | // allow for multiline macro definitions |
| 725 | 767 | // spaces after the # character compile fine with gcc |
| 726 | − pattern: /(^\s*)#\s*[a-z]+([^\r\n\\]|\\.|\\(?:\r\n?|\n))*/im, | |
| 768 | + pattern: /(^\s*)#\s*[a-z]+(?:[^\r\n\\]|\\(?:\r\n|[\s\S]))*/im, | |
| 727 | 769 | lookbehind: true, |
| 728 | 770 | alias: 'property', |
| 729 | 771 | inside: { |
| 730 | 772 | // highlight the path of the include statement as a string |
| 731 | 773 | 'string': { |
| 732 | − pattern: /(#\s*include\s*)(<.+?>|("|')(\\?.)+?\3)/, | |
| 774 | + pattern: /(#\s*include\s*)(?:<.+?>|("|')(?:\\?.)+?\2)/, | |
| 733 | 775 | lookbehind: true |
| 734 | 776 | }, |
| 735 | 777 | // highlight macro directives as keywords |
| 736 | 778 | 'directive': { |
| 737 | − pattern: /(#\s*)\b(define|defined|elif|else|endif|error|ifdef|ifndef|if|import|include|line|pragma|undef|using)\b/, | |
| 779 | + pattern: /(#\s*)\b(?:define|defined|elif|else|endif|error|ifdef|ifndef|if|import|include|line|pragma|undef|using)\b/, | |
| 738 | 780 | lookbehind: true, |
| 739 | 781 | alias: 'keyword' |
| 740 | 782 | } |
| 741 | 783 | } |
| 742 | 784 | }, |
| 743 | 785 | // highlight predefined macros as constants |
| 744 | − 'constant': /\b(__FILE__|__LINE__|__DATE__|__TIME__|__TIMESTAMP__|__func__|EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|stdin|stdout|stderr)\b/ | |
| 786 | + 'constant': /\b(?:__FILE__|__LINE__|__DATE__|__TIME__|__TIMESTAMP__|__func__|EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|stdin|stdout|stderr)\b/ | |
| 745 | 787 | }); |
| 746 | 788 | |
| 747 | 789 | delete Prism.languages.c['class-name']; |
| 748 | 790 | delete Prism.languages.c['boolean']; |
| 749 | 791 | |
| 792 | +(function(Prism) { | |
| 793 | + var insideString = { | |
| 794 | + variable: [ | |
| 795 | + // Arithmetic Environment | |
| 796 | + { | |
| 797 | + pattern: /\$?\(\([\s\S]+?\)\)/, | |
| 798 | + inside: { | |
| 799 | + // If there is a $ sign at the beginning highlight $(( and )) as variable | |
| 800 | + variable: [{ | |
| 801 | + pattern: /(^\$\(\([\s\S]+)\)\)/, | |
| 802 | + lookbehind: true | |
| 803 | + }, | |
| 804 | + /^\$\(\(/ | |
| 805 | + ], | |
| 806 | + number: /\b0x[\dA-Fa-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:[Ee]-?\d+)?/, | |
| 807 | + // Operators according to https://www.gnu.org/software/bash/manual/bashref.html#Shell-Arithmetic | |
| 808 | + operator: /--?|-=|\+\+?|\+=|!=?|~|\*\*?|\*=|\/=?|%=?|<<=?|>>=?|<=?|>=?|==?|&&?|&=|\^=?|\|\|?|\|=|\?|:/, | |
| 809 | + // If there is no $ sign at the beginning highlight (( and )) as punctuation | |
| 810 | + punctuation: /\(\(?|\)\)?|,|;/ | |
| 811 | + } | |
| 812 | + }, | |
| 813 | + // Command Substitution | |
| 814 | + { | |
| 815 | + pattern: /\$\([^)]+\)|`[^`]+`/, | |
| 816 | + greedy: true, | |
| 817 | + inside: { | |
| 818 | + variable: /^\$\(|^`|\)$|`$/ | |
| 819 | + } | |
| 820 | + }, | |
| 821 | + /\$(?:[\w#?*!@]+|\{[^}]+\})/i | |
| 822 | + ] | |
| 823 | + }; | |
| 824 | + | |
| 825 | + Prism.languages.bash = { | |
| 826 | + 'shebang': { | |
| 827 | + pattern: /^#!\s*\/bin\/bash|^#!\s*\/bin\/sh/, | |
| 828 | + alias: 'important' | |
| 829 | + }, | |
| 830 | + 'comment': { | |
| 831 | + pattern: /(^|[^"{\\])#.*/, | |
| 832 | + lookbehind: true | |
| 833 | + }, | |
| 834 | + 'string': [ | |
| 835 | + //Support for Here-Documents https://en.wikipedia.org/wiki/Here_document | |
| 836 | + { | |
| 837 | + pattern: /((?:^|[^<])<<\s*)["']?(\w+?)["']?\s*\r?\n(?:[\s\S])*?\r?\n\2/, | |
| 838 | + lookbehind: true, | |
| 839 | + greedy: true, | |
| 840 | + inside: insideString | |
| 841 | + }, | |
| 842 | + { | |
| 843 | + pattern: /(["'])(?:\\[\s\S]|\$\([^)]+\)|`[^`]+`|(?!\1)[^\\])*\1/, | |
| 844 | + greedy: true, | |
| 845 | + inside: insideString | |
| 846 | + } | |
| 847 | + ], | |
| 848 | + 'variable': insideString.variable, | |
| 849 | + // Originally based on http://ss64.com/bash/ | |
| 850 | + 'function': { | |
| 851 | + pattern: /(^|[\s;|&])(?:alias|apropos|apt-get|aptitude|aspell|awk|basename|bash|bc|bg|builtin|bzip2|cal|cat|cd|cfdisk|chgrp|chmod|chown|chroot|chkconfig|cksum|clear|cmp|comm|command|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|du|egrep|eject|enable|env|ethtool|eval|exec|expand|expect|export|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|getopts|git|grep|groupadd|groupdel|groupmod|groups|gzip|hash|head|help|hg|history|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|jobs|join|kill|killall|less|link|ln|locate|logname|logout|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|make|man|mkdir|mkfifo|mkisofs|mknod|more|most|mount|mtools|mtr|mv|mmv|nano|netstat|nice|nl|nohup|notify-send|npm|nslookup|open|op|passwd|paste|pathchk|ping|pkill|popd|pr|printcap|printenv|printf|ps|pushd|pv|pwd|quota|quotacheck|quotactl|ram|rar|rcp|read|readarray|readonly|reboot|rename|renice|remsync|rev|rm|rmdir|rsync|screen|scp|sdiff|sed|seq|service|sftp|shift|shopt|shutdown|sleep|slocate|sort|source|split|ssh|stat|strace|su|sudo|sum|suspend|sync|tail|tar|tee|test|time|timeout|times|touch|top|traceroute|trap|tr|tsort|tty|type|ulimit|umask|umount|unalias|uname|unexpand|uniq|units|unrar|unshar|uptime|useradd|userdel|usermod|users|uuencode|uudecode|v|vdir|vi|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yes|zip)(?=$|[\s;|&])/, | |
| 852 | + lookbehind: true | |
| 853 | + }, | |
| 854 | + 'keyword': { | |
| 855 | + pattern: /(^|[\s;|&])(?:let|:|\.|if|then|else|elif|fi|for|break|continue|while|in|case|function|select|do|done|until|echo|exit|return|set|declare)(?=$|[\s;|&])/, | |
| 856 | + lookbehind: true | |
| 857 | + }, | |
| 858 | + 'boolean': { | |
| 859 | + pattern: /(^|[\s;|&])(?:true|false)(?=$|[\s;|&])/, | |
| 860 | + lookbehind: true | |
| 861 | + }, | |
| 862 | + 'operator': /&&?|\|\|?|==?|!=?|<<<?|>>|<=?|>=?|=~/, | |
| 863 | + 'punctuation': /\$?\(\(?|\)\)?|\.\.|[{}[\];]/ | |
| 864 | + }; | |
| 865 | + | |
| 866 | + var inside = insideString.variable[1].inside; | |
| 867 | + inside.string = Prism.languages.bash.string; | |
| 868 | + inside['function'] = Prism.languages.bash['function']; | |
| 869 | + inside.keyword = Prism.languages.bash.keyword; | |
| 870 | + inside['boolean'] = Prism.languages.bash['boolean']; | |
| 871 | + inside.operator = Prism.languages.bash.operator; | |
| 872 | + inside.punctuation = Prism.languages.bash.punctuation; | |
| 873 | + | |
| 874 | + Prism.languages.shell = Prism.languages.bash; | |
| 875 | +})(Prism); | |
| 876 | + | |
| 750 | 877 | Prism.languages.cpp = Prism.languages.extend('c', { |
| 751 | − 'keyword': /\b(alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|class|compl|const|constexpr|const_cast|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|float|for|friend|goto|if|inline|int|long|mutable|namespace|new|noexcept|nullptr|operator|private|protected|public|register|reinterpret_cast|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/, | |
| 752 | − 'boolean': /\b(true|false)\b/, | |
| 753 | − 'operator': /[-+]{1,2}|!=?|<{1,2}=?|>{1,2}=?|\->|:{1,2}|={1,2}|\^|~|%|&{1,2}|\|?\||\?|\*|\/|\b(and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/ | |
| 878 | + 'keyword': /\b(?:alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|class|compl|const|constexpr|const_cast|continue|decltype|default|delete|do|double|dynamic_cast|else|enum|explicit|export|extern|float|for|friend|goto|if|inline|int|int8_t|int16_t|int32_t|int64_t|uint8_t|uint16_t|uint32_t|uint64_t|long|mutable|namespace|new|noexcept|nullptr|operator|private|protected|public|register|reinterpret_cast|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/, | |
| 879 | + 'boolean': /\b(?:true|false)\b/, | |
| 880 | + 'operator': /--?|\+\+?|!=?|<{1,2}=?|>{1,2}=?|->|:{1,2}|={1,2}|\^|~|%|&{1,2}|\|\|?|\?|\*|\/|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/ | |
| 754 | 881 | }); |
| 755 | 882 | |
| 756 | 883 | Prism.languages.insertBefore('cpp', 'keyword', { |
| 757 | 884 | 'class-name': { |
| 758 | − pattern: /(class\s+)[a-z0-9_]+/i, | |
| 885 | + pattern: /(class\s+)\w+/i, | |
| 759 | 886 | lookbehind: true |
| 760 | 887 | } |
| 761 | 888 | }); |
| 889 | + | |
| 890 | +Prism.languages.insertBefore('cpp', 'string', { | |
| 891 | + 'raw-string': { | |
| 892 | + pattern: /R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/, | |
| 893 | + alias: 'string', | |
| 894 | + greedy: true | |
| 895 | + } | |
| 896 | +}); | |
| 897 | + | |
| 762 | 898 | (function(Prism) { |
| 763 | 899 | |
| 764 | 900 | // Ignore comments starting with { to privilege string interpolation highlighting |
| @@ -774,20 +910,20 @@ Prism.languages.coffeescript = Prism.languages.extend('javascript', { · 9 unmodified lines | ||
| 774 | 910 | |
| 775 | 911 | // Strings are multiline |
| 776 | 912 | { |
| 777 | − pattern: /'(?:\\?[^\\])*?'/, | |
| 913 | + pattern: /'(?:\\[\s\S]|[^\\'])*'/, | |
| 778 | 914 | greedy: true |
| 779 | 915 | }, |
| 780 | 916 | |
| 781 | 917 | { |
| 782 | 918 | // Strings are multiline |
| 783 | − pattern: /"(?:\\?[^\\])*?"/, | |
| 919 | + pattern: /"(?:\\[\s\S]|[^\\"])*"/, | |
| 784 | 920 | greedy: true, |
| 785 | 921 | inside: { |
| 786 | 922 | 'interpolation': interpolation |
| 787 | 923 | } |
| 788 | 924 | } |
| 789 | 925 | ], |
| 790 | − 'keyword': /\b(and|break|by|catch|class|continue|debugger|delete|do|each|else|extend|extends|false|finally|for|if|in|instanceof|is|isnt|let|loop|namespace|new|no|not|null|of|off|on|or|own|return|super|switch|then|this|throw|true|try|typeof|undefined|unless|until|when|while|window|with|yes|yield)\b/, | |
| 926 | + 'keyword': /\b(?:and|break|by|catch|class|continue|debugger|delete|do|each|else|extend|extends|false|finally|for|if|in|instanceof|is|isnt|let|loop|namespace|new|no|not|null|of|off|on|or|own|return|super|switch|then|this|throw|true|try|typeof|undefined|unless|until|when|while|window|with|yes|yield)\b/, | |
| 791 | 927 | 'class-member': { |
| 792 | 928 | pattern: /@(?!\d)\w+/, |
| 793 | 929 | alias: 'variable' |
| @@ -813,7 +949,7 @@ Prism.languages.insertBefore('coffeescript', 'comment', { · 19 unmodified lines | ||
| 813 | 949 | |
| 814 | 950 | Prism.languages.insertBefore('coffeescript', 'string', { |
| 815 | 951 | 'inline-javascript': { |
| 816 | − pattern: /`(?:\\?[\s\S])*?`/, | |
| 952 | + pattern: /`(?:\\[\s\S]|[^\\`])*`/, | |
| 817 | 953 | inside: { |
| 818 | 954 | 'delimiter': { |
| 819 | 955 | pattern: /^`|`$/, |
| @@ -859,10 +995,13 @@ delete Prism.languages.coffeescript['template-string']; · 39 unmodified lines | ||
| 859 | 995 | (function(Prism) { |
| 860 | 996 | Prism.languages.ruby = Prism.languages.extend('clike', { |
| 861 | 997 | 'comment': [ |
| 862 | − /#(?!\{[^\r\n]*?\}).*/, | |
| 863 | − /^=begin(?:\r?\n|\r)(?:.*(?:\r?\n|\r))*?=end/m | |
| 998 | + /#.*/, | |
| 999 | + { | |
| 1000 | + pattern: /^=begin(?:\r?\n|\r)(?:.*(?:\r?\n|\r))*?=end/m, | |
| 1001 | + greedy: true | |
| 1002 | + } | |
| 864 | 1003 | ], |
| 865 | − 'keyword': /\b(alias|and|BEGIN|begin|break|case|class|def|define_method|defined|do|each|else|elsif|END|end|ensure|false|for|if|in|module|new|next|nil|not|or|raise|redo|require|rescue|retry|return|self|super|then|throw|true|undef|unless|until|when|while|yield)\b/ | |
| 1004 | + 'keyword': /\b(?:alias|and|BEGIN|begin|break|case|class|def|define_method|defined|do|each|else|elsif|END|end|ensure|false|for|if|in|module|new|next|nil|not|or|protected|private|public|raise|redo|require|rescue|retry|return|self|super|then|throw|true|undef|unless|until|when|while|yield)\b/ | |
| 866 | 1005 | }); |
| 867 | 1006 | |
| 868 | 1007 | var interpolation = { |
| @@ -872,14 +1011,14 @@ delete Prism.languages.coffeescript['template-string']; · 3 unmodified lines | ||
| 872 | 1011 | pattern: /^#\{|\}$/, |
| 873 | 1012 | alias: 'tag' |
| 874 | 1013 | }, |
| 875 | − rest: Prism.util.clone(Prism.languages.ruby) | |
| 1014 | + rest: Prism.languages.ruby | |
| 876 | 1015 | } |
| 877 | 1016 | }; |
| 878 | 1017 | |
| 879 | 1018 | Prism.languages.insertBefore('ruby', 'keyword', { |
| 880 | 1019 | 'regex': [ |
| 881 | 1020 | { |
| 882 | − pattern: /%r([^a-zA-Z0-9\s\{\(\[<])(?:[^\\]|\\[\s\S])*?\1[gim]{0,3}/, | |
| 1021 | + pattern: /%r([^a-zA-Z0-9\s{(\[<])(?:(?!\1)[^\\]|\\[\s\S])*\1[gim]{0,3}/, | |
| 883 | 1022 | greedy: true, |
| 884 | 1023 | inside: { |
| 885 | 1024 | 'interpolation': interpolation |
| @@ -920,18 +1059,21 @@ delete Prism.languages.coffeescript['template-string']; · 34 unmodified lines | ||
| 920 | 1059 | greedy: true |
| 921 | 1060 | } |
| 922 | 1061 | ], |
| 923 | − 'variable': /[@$]+[a-zA-Z_][a-zA-Z_0-9]*(?:[?!]|\b)/, | |
| 924 | − 'symbol': /:[a-zA-Z_][a-zA-Z_0-9]*(?:[?!]|\b)/ | |
| 1062 | + 'variable': /[@$]+[a-zA-Z_]\w*(?:[?!]|\b)/, | |
| 1063 | + 'symbol': { | |
| 1064 | + pattern: /(^|[^:]):[a-zA-Z_]\w*(?:[?!]|\b)/, | |
| 1065 | + lookbehind: true | |
| 1066 | + } | |
| 925 | 1067 | }); |
| 926 | 1068 | |
| 927 | 1069 | Prism.languages.insertBefore('ruby', 'number', { |
| 928 | − 'builtin': /\b(Array|Bignum|Binding|Class|Continuation|Dir|Exception|FalseClass|File|Stat|File|Fixnum|Float|Hash|Integer|IO|MatchData|Method|Module|NilClass|Numeric|Object|Proc|Range|Regexp|String|Struct|TMS|Symbol|ThreadGroup|Thread|Time|TrueClass)\b/, | |
| 929 | − 'constant': /\b[A-Z][a-zA-Z_0-9]*(?:[?!]|\b)/ | |
| 1070 | + 'builtin': /\b(?:Array|Bignum|Binding|Class|Continuation|Dir|Exception|FalseClass|File|Stat|Fixnum|Float|Hash|Integer|IO|MatchData|Method|Module|NilClass|Numeric|Object|Proc|Range|Regexp|String|Struct|TMS|Symbol|ThreadGroup|Thread|Time|TrueClass)\b/, | |
| 1071 | + 'constant': /\b[A-Z]\w*(?:[?!]|\b)/ | |
| 930 | 1072 | }); |
| 931 | 1073 | |
| 932 | 1074 | Prism.languages.ruby.string = [ |
| 933 | 1075 | { |
| 934 | − pattern: /%[qQiIwWxs]?([^a-zA-Z0-9\s\{\(\[<])(?:[^\\]|\\[\s\S])*?\1/, | |
| 1076 | + pattern: /%[qQiIwWxs]?([^a-zA-Z0-9\s{(\[<])(?:(?!\1)[^\\]|\\[\s\S])*\1/, | |
| 935 | 1077 | greedy: true, |
| 936 | 1078 | inside: { |
| 937 | 1079 | 'interpolation': interpolation |
| @@ -967,7 +1109,7 @@ delete Prism.languages.coffeescript['template-string']; · 29 unmodified lines | ||
| 967 | 1109 | } |
| 968 | 1110 | }, |
| 969 | 1111 | { |
| 970 | − pattern: /("|')(#\{[^}]+\}|\\(?:\r?\n|\r)|\\?.)*?\1/, | |
| 1112 | + pattern: /("|')(?:#\{[^}]+\}|\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/, | |
| 971 | 1113 | greedy: true, |
| 972 | 1114 | inside: { |
| 973 | 1115 | 'interpolation': interpolation |
| @@ -978,7 +1120,7 @@ delete Prism.languages.coffeescript['template-string']; · 4 unmodified lines | ||
| 978 | 1120 | Prism.languages.d = Prism.languages.extend('clike', { |
| 979 | 1121 | 'string': [ |
| 980 | 1122 | // r"", x"" |
| 981 | − /\b[rx]"(\\.|[^\\"])*"[cwd]?/, | |
| 1123 | + /\b[rx]"(?:\\[\s\S]|[^\\"])*"[cwd]?/, | |
| 982 | 1124 | // q"[]", q"()", q"<>", q"{}" |
| 983 | 1125 | /\bq"(?:\[[\s\S]*?\]|\([\s\S]*?\)|<[\s\S]*?>|\{[\s\S]*?\})"/, |
| 984 | 1126 | // q"IDENT |
| @@ -990,7 +1132,7 @@ Prism.languages.d = Prism.languages.extend('clike', { · 5 unmodified lines | ||
| 990 | 1132 | // Characters |
| 991 | 1133 | /'(?:\\'|\\?[^']+)'/, |
| 992 | 1134 | |
| 993 | − /(["`])(\\.|(?!\1)[^\\])*\1[cwd]?/ | |
| 1135 | + /(["`])(?:\\[\s\S]|(?!\1)[^\\])*\1[cwd]?/ | |
| 994 | 1136 | ], |
| 995 | 1137 | |
| 996 | 1138 | 'number': [ |
| @@ -1023,7 +1165,7 @@ Prism.languages.d.comment = [ · 26 unmodified lines | ||
| 1023 | 1165 | Prism.languages.insertBefore('d', 'comment', { |
| 1024 | 1166 | 'token-string': { |
| 1025 | 1167 | // Allow one level of nesting |
| 1026 | − pattern: /\bq\{(?:|\{[^}]*\}|[^}])*\}/, | |
| 1168 | + pattern: /\bq\{(?:\{[^}]*\}|[^}])*\}/, | |
| 1027 | 1169 | alias: 'string' |
| 1028 | 1170 | } |
| 1029 | 1171 | }); |
| @@ -1039,20 +1181,86 @@ Prism.languages.insertBefore('d', 'function', { · 9 unmodified lines | ||
| 1039 | 1181 | alias: 'variable' |
| 1040 | 1182 | } |
| 1041 | 1183 | }); |
| 1184 | +Prism.languages.dart = Prism.languages.extend('clike', { | |
| 1185 | + 'string': [ | |
| 1186 | + { | |
| 1187 | + pattern: /r?("""|''')[\s\S]*?\1/, | |
| 1188 | + greedy: true | |
| 1189 | + }, | |
| 1190 | + { | |
| 1191 | + pattern: /r?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/, | |
| 1192 | + greedy: true | |
| 1193 | + } | |
| 1194 | + ], | |
| 1195 | + 'keyword': [ | |
| 1196 | + /\b(?:async|sync|yield)\*/, | |
| 1197 | + /\b(?:abstract|assert|async|await|break|case|catch|class|const|continue|default|deferred|do|dynamic|else|enum|export|external|extends|factory|final|finally|for|get|if|implements|import|in|library|new|null|operator|part|rethrow|return|set|static|super|switch|this|throw|try|typedef|var|void|while|with|yield)\b/ | |
| 1198 | + ], | |
| 1199 | + 'operator': /\bis!|\b(?:as|is)\b|\+\+|--|&&|\|\||<<=?|>>=?|~(?:\/=?)?|[+\-*\/%&^|=!<>]=?|\?/ | |
| 1200 | +}); | |
| 1201 | + | |
| 1202 | +Prism.languages.insertBefore('dart','function',{ | |
| 1203 | + 'metadata': { | |
| 1204 | + pattern: /@\w+/, | |
| 1205 | + alias: 'symbol' | |
| 1206 | + } | |
| 1207 | +}); | |
| 1208 | +// Django/Jinja2 syntax definition for Prism.js <http://prismjs.com> syntax highlighter. | |
| 1209 | +// Mostly it works OK but can paint code incorrectly on complex html/template tag combinations. | |
| 1210 | + | |
| 1211 | +var _django_template = { | |
| 1212 | + 'property': { | |
| 1213 | + pattern: /(?:{{|{%)[\s\S]*?(?:%}|}})/g, | |
| 1214 | + greedy: true, | |
| 1215 | + inside: { | |
| 1216 | + 'string': { | |
| 1217 | + pattern: /("|')(?:\\.|(?!\1)[^\\\r\n])*\1/, | |
| 1218 | + greedy: true | |
| 1219 | + }, | |
| 1220 | + 'keyword': /\b(?:\||load|verbatim|widthratio|ssi|firstof|for|url|ifchanged|csrf_token|lorem|ifnotequal|autoescape|now|templatetag|debug|cycle|ifequal|regroup|comment|filter|endfilter|if|spaceless|with|extends|block|include|else|empty|endif|endfor|as|endblock|endautoescape|endverbatim|trans|endtrans|[Tt]rue|[Ff]alse|[Nn]one|in|is|static|macro|endmacro|call|endcall|set|endset|raw|endraw)\b/, | |
| 1221 | + 'operator' : /[-+=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]|\b(?:or|and|not)\b/, | |
| 1222 | + 'function': /\b(?:_|abs|add|addslashes|attr|batch|callable|capfirst|capitalize|center|count|cut|d|date|default|default_if_none|defined|dictsort|dictsortreversed|divisibleby|e|equalto|escape|escaped|escapejs|even|filesizeformat|first|float|floatformat|force_escape|forceescape|format|get_digit|groupby|indent|int|iriencode|iterable|join|last|length|length_is|linebreaks|linebreaksbr|linenumbers|list|ljust|lower|make_list|map|mapping|number|odd|phone2numeric|pluralize|pprint|random|reject|rejectattr|removetags|replace|reverse|rjust|round|safe|safeseq|sameas|select|selectattr|sequence|slice|slugify|sort|string|stringformat|striptags|sum|time|timesince|timeuntil|title|trim|truncate|truncatechars|truncatechars_html|truncatewords|truncatewords_html|undefined|unordered_list|upper|urlencode|urlize|urlizetrunc|wordcount|wordwrap|xmlattr|yesno)\b/, | |
| 1223 | + 'important': /\b-?\d+(?:\.\d+)?\b/, | |
| 1224 | + 'variable': /\b\w+?\b/, | |
| 1225 | + 'punctuation' : /[[\];(),.:]/ | |
| 1226 | + } | |
| 1227 | + } | |
| 1228 | +}; | |
| 1229 | + | |
| 1230 | +Prism.languages.django = Prism.languages.extend('markup', {'comment': /(?:<!--|{#)[\s\S]*?(?:#}|-->)/}); | |
| 1231 | +// Updated html tag pattern to allow template tags inside html tags | |
| 1232 | +Prism.languages.django.tag.pattern = /<\/?(?!\d)[^\s>\/=$<]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^>=]+))?)*\s*\/?>/i; | |
| 1233 | +Prism.languages.insertBefore('django', 'entity', _django_template); | |
| 1234 | +Prism.languages.insertBefore('inside', 'tag', _django_template, Prism.languages.django.tag); | |
| 1235 | + | |
| 1236 | +if (Prism.languages.javascript) { | |
| 1237 | + // Combine js code and template tags painting inside <script> blocks | |
| 1238 | + Prism.languages.insertBefore('inside', 'string', _django_template, Prism.languages.django.script); | |
| 1239 | + Prism.languages.django.script.inside.string.inside = _django_template; | |
| 1240 | +} | |
| 1241 | +if (Prism.languages.css) { | |
| 1242 | + // Combine css code and template tags painting inside <style> blocks | |
| 1243 | + Prism.languages.insertBefore('inside', 'atrule', {'tag': _django_template.property}, Prism.languages.django.style); | |
| 1244 | + Prism.languages.django.style.inside.string.inside = _django_template; | |
| 1245 | +} | |
| 1246 | + | |
| 1247 | +// Add an Jinja2 alias | |
| 1248 | +Prism.languages.jinja2 = Prism.languages.django; | |
| 1249 | + | |
| 1042 | 1250 | Prism.languages.elixir = { |
| 1043 | − // Negative look-ahead is needed for string interpolation | |
| 1044 | − // Negative look-behind is needed to avoid highlighting markdown headers in | |
| 1045 | − // multi-line doc strings | |
| 1046 | 1251 | 'comment': { |
| 1047 | − pattern: /(^|[^#])#(?![{#]).*/m, | |
| 1252 | + pattern: /#.*/m, | |
| 1048 | 1253 | lookbehind: true |
| 1049 | 1254 | }, |
| 1050 | − // ~r"""foo""", ~r'''foo''', ~r/foo/, ~r|foo|, ~r"foo", ~r'foo', ~r(foo), ~r[foo], ~r{foo}, ~r<foo> | |
| 1051 | − 'regex': /~[rR](?:("""|'''|[\/|"'])(?:\\.|(?!\1)[^\\])+\1|\((?:\\\)|[^)])+\)|\[(?:\\\]|[^\]])+\]|\{(?:\\\}|[^}])+\}|<(?:\\>|[^>])+>)[uismxfr]*/, | |
| 1255 | + // ~r"""foo""" (multi-line), ~r'''foo''' (multi-line), ~r/foo/, ~r|foo|, ~r"foo", ~r'foo', ~r(foo), ~r[foo], ~r{foo}, ~r<foo> | |
| 1256 | + 'regex': { | |
| 1257 | + pattern: /~[rR](?:("""|''')(?:\\[\s\S]|(?!\1)[^\\])+\1|([\/|"'])(?:\\.|(?!\2)[^\\\r\n])+\2|\((?:\\.|[^\\)\r\n])+\)|\[(?:\\.|[^\\\]\r\n])+\]|\{(?:\\.|[^\\}\r\n])+\}|<(?:\\.|[^\\>\r\n])+>)[uismxfr]*/, | |
| 1258 | + greedy: true | |
| 1259 | + }, | |
| 1052 | 1260 | 'string': [ |
| 1053 | 1261 | { |
| 1054 | − // ~s"""foo""", ~s'''foo''', ~s/foo/, ~s|foo|, ~s"foo", ~s'foo', ~s(foo), ~s[foo], ~s{foo}, ~s<foo> | |
| 1055 | − pattern: /~[cCsSwW](?:("""|'''|[\/|"'])(?:\\.|(?!\1)[^\\])+\1|\((?:\\\)|[^)])+\)|\[(?:\\\]|[^\]])+\]|\{(?:\\\}|#\{[^}]+\}|[^}])+\}|<(?:\\>|[^>])+>)[csa]?/, | |
| 1262 | + // ~s"""foo""" (multi-line), ~s'''foo''' (multi-line), ~s/foo/, ~s|foo|, ~s"foo", ~s'foo', ~s(foo), ~s[foo], ~s{foo} (with interpolation care), ~s<foo> | |
| 1263 | + pattern: /~[cCsSwW](?:("""|''')(?:\\[\s\S]|(?!\1)[^\\])+\1|([\/|"'])(?:\\.|(?!\2)[^\\\r\n])+\2|\((?:\\.|[^\\)\r\n])+\)|\[(?:\\.|[^\\\]\r\n])+\]|\{(?:\\.|#\{[^}]+\}|[^\\}\r\n])+\}|<(?:\\.|[^\\>\r\n])+>)[csa]?/, | |
| 1056 | 1264 | greedy: true, |
| 1057 | 1265 | inside: { |
| 1058 | 1266 | // See interpolation below |
| @@ -1067,7 +1275,7 @@ Prism.languages.elixir = { · 8 unmodified lines | ||
| 1067 | 1275 | }, |
| 1068 | 1276 | { |
| 1069 | 1277 | // Multi-line strings are allowed |
| 1070 | − pattern: /("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/, | |
| 1278 | + pattern: /("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/, | |
| 1071 | 1279 | greedy: true, |
| 1072 | 1280 | inside: { |
| 1073 | 1281 | // See interpolation below |
| @@ -1095,7 +1303,7 @@ Prism.languages.elixir = { · 21 unmodified lines | ||
| 1095 | 1303 | alias: 'variable' |
| 1096 | 1304 | }, |
| 1097 | 1305 | 'attribute': { |
| 1098 | − pattern: /@[\S]+/, | |
| 1306 | + pattern: /@\w+/, | |
| 1099 | 1307 | alias: 'variable' |
| 1100 | 1308 | }, |
| 1101 | 1309 | 'number': /\b(?:0[box][a-f\d_]+|\d[\d_]*)(?:\.[\d_]+)?(?:e[+-]?[\d_]+)?\b/i, |
| @@ -1126,25 +1334,114 @@ Prism.languages.elixir.string.forEach(function(o) { · 24 unmodified lines | ||
| 1126 | 1334 | pattern: /^#\{|\}$/, |
| 1127 | 1335 | alias: 'punctuation' |
| 1128 | 1336 | }, |
| 1129 | − rest: Prism.util.clone(Prism.languages.elixir) | |
| 1337 | + rest: Prism.languages.elixir | |
| 1130 | 1338 | } |
| 1131 | 1339 | } |
| 1132 | 1340 | }; |
| 1133 | 1341 | }); |
| 1134 | 1342 | |
| 1135 | 1343 | |
| 1344 | +Prism.languages['markup-templating'] = {}; | |
| 1345 | + | |
| 1346 | +Object.defineProperties(Prism.languages['markup-templating'], { | |
| 1347 | + buildPlaceholders: { | |
| 1348 | + // Tokenize all inline templating expressions matching placeholderPattern | |
| 1349 | + // If the replaceFilter function is provided, it will be called with every match. | |
| 1350 | + // If it returns false, the match will not be replaced. | |
| 1351 | + value: function (env, language, placeholderPattern, replaceFilter) { | |
| 1352 | + if (env.language !== language) { | |
| 1353 | + return; | |
| 1354 | + } | |
| 1355 | + | |
| 1356 | + env.tokenStack = []; | |
| 1357 | + | |
| 1358 | + env.code = env.code.replace(placeholderPattern, function(match) { | |
| 1359 | + if (typeof replaceFilter === 'function' && !replaceFilter(match)) { | |
| 1360 | + return match; | |
| 1361 | + } | |
| 1362 | + var i = env.tokenStack.length; | |
| 1363 | + // Check for existing strings | |
| 1364 | + while (env.code.indexOf('___' + language.toUpperCase() + i + '___') !== -1) | |
| 1365 | + ++i; | |
| 1366 | + | |
| 1367 | + // Create a sparse array | |
| 1368 | + env.tokenStack[i] = match; | |
| 1369 | + | |
| 1370 | + return '___' + language.toUpperCase() + i + '___'; | |
| 1371 | + }); | |
| 1372 | + | |
| 1373 | + // Switch the grammar to markup | |
| 1374 | + env.grammar = Prism.languages.markup; | |
| 1375 | + } | |
| 1376 | + }, | |
| 1377 | + tokenizePlaceholders: { | |
| 1378 | + // Replace placeholders with proper tokens after tokenizing | |
| 1379 | + value: function (env, language) { | |
| 1380 | + if (env.language !== language || !env.tokenStack) { | |
| 1381 | + return; | |
| 1382 | + } | |
| 1383 | + | |
| 1384 | + // Switch the grammar back | |
| 1385 | + env.grammar = Prism.languages[language]; | |
| 1386 | + | |
| 1387 | + var j = 0; | |
| 1388 | + var keys = Object.keys(env.tokenStack); | |
| 1389 | + var walkTokens = function (tokens) { | |
| 1390 | + if (j >= keys.length) { | |
| 1391 | + return; | |
| 1392 | + } | |
| 1393 | + for (var i = 0; i < tokens.length; i++) { | |
| 1394 | + var token = tokens[i]; | |
| 1395 | + if (typeof token === 'string' || (token.content && typeof token.content === 'string')) { | |
| 1396 | + var k = keys[j]; | |
| 1397 | + var t = env.tokenStack[k]; | |
| 1398 | + var s = typeof token === 'string' ? token : token.content; | |
| 1399 | + | |
| 1400 | + var index = s.indexOf('___' + language.toUpperCase() + k + '___'); | |
| 1401 | + if (index > -1) { | |
| 1402 | + ++j; | |
| 1403 | + var before = s.substring(0, index); | |
| 1404 | + var middle = new Prism.Token(language, Prism.tokenize(t, env.grammar, language), 'language-' + language, t); | |
| 1405 | + var after = s.substring(index + ('___' + language.toUpperCase() + k + '___').length); | |
| 1406 | + var replacement; | |
| 1407 | + if (before || after) { | |
| 1408 | + replacement = [before, middle, after].filter(function (v) { return !!v; }); | |
| 1409 | + walkTokens(replacement); | |
| 1410 | + } else { | |
| 1411 | + replacement = middle; | |
| 1412 | + } | |
| 1413 | + if (typeof token === 'string') { | |
| 1414 | + Array.prototype.splice.apply(tokens, [i, 1].concat(replacement)); | |
| 1415 | + } else { | |
| 1416 | + token.content = replacement; | |
| 1417 | + } | |
| 1418 | + | |
| 1419 | + if (j >= keys.length) { | |
| 1420 | + break; | |
| 1421 | + } | |
| 1422 | + } | |
| 1423 | + } else if (token.content && typeof token.content !== 'string') { | |
| 1424 | + walkTokens(token.content); | |
| 1425 | + } | |
| 1426 | + } | |
| 1427 | + }; | |
| 1428 | + | |
| 1429 | + walkTokens(env.tokens); | |
| 1430 | + } | |
| 1431 | + } | |
| 1432 | +}); | |
| 1136 | 1433 | Prism.languages.erlang = { |
| 1137 | 1434 | 'comment': /%.+/, |
| 1138 | 1435 | 'string': { |
| 1139 | − pattern: /"(?:\\?.)*?"/, | |
| 1436 | + pattern: /"(?:\\.|[^\\"\r\n])*"/, | |
| 1140 | 1437 | greedy: true |
| 1141 | 1438 | }, |
| 1142 | 1439 | 'quoted-function': { |
| 1143 | − pattern: /'(?:\\.|[^'\\])+'(?=\()/, | |
| 1440 | + pattern: /'(?:\\.|[^\\'\r\n])+'(?=\()/, | |
| 1144 | 1441 | alias: 'function' |
| 1145 | 1442 | }, |
| 1146 | 1443 | 'quoted-atom': { |
| 1147 | − pattern: /'(?:\\.|[^'\\])+'/, | |
| 1444 | + pattern: /'(?:\\.|[^\\'\r\n])+'/, | |
| 1148 | 1445 | alias: 'atom' |
| 1149 | 1446 | }, |
| 1150 | 1447 | 'boolean': /\b(?:true|false)\b/, |
| @@ -1152,7 +1449,7 @@ Prism.languages.erlang = { · 1 unmodified line | ||
| 1152 | 1449 | 'number': [ |
| 1153 | 1450 | /\$\\?./, |
| 1154 | 1451 | /\d+#[a-z0-9]+/i, |
| 1155 | − /(?:\b|-)\d*\.?\d+([Ee][+-]?\d+)?\b/ | |
| 1452 | + /(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i | |
| 1156 | 1453 | ], |
| 1157 | 1454 | 'function': /\b[a-z][\w@]*(?=\()/, |
| 1158 | 1455 | 'variable': { |
| @@ -1178,21 +1475,21 @@ Prism.languages.erlang = { · 19 unmodified lines | ||
| 1178 | 1475 | |
| 1179 | 1476 | }; |
| 1180 | 1477 | Prism.languages.go = Prism.languages.extend('clike', { |
| 1181 | − 'keyword': /\b(break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go(to)?|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/, | |
| 1182 | − 'builtin': /\b(bool|byte|complex(64|128)|error|float(32|64)|rune|string|u?int(8|16|32|64|)|uintptr|append|cap|close|complex|copy|delete|imag|len|make|new|panic|print(ln)?|real|recover)\b/, | |
| 1183 | − 'boolean': /\b(_|iota|nil|true|false)\b/, | |
| 1478 | + 'keyword': /\b(?:break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go(?:to)?|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b/, | |
| 1479 | + 'builtin': /\b(?:bool|byte|complex(?:64|128)|error|float(?:32|64)|rune|string|u?int(?:8|16|32|64)?|uintptr|append|cap|close|complex|copy|delete|imag|len|make|new|panic|print(?:ln)?|real|recover)\b/, | |
| 1480 | + 'boolean': /\b(?:_|iota|nil|true|false)\b/, | |
| 1184 | 1481 | 'operator': /[*\/%^!=]=?|\+[=+]?|-[=-]?|\|[=|]?|&(?:=|&|\^=?)?|>(?:>=?|=)?|<(?:<=?|=|-)?|:=|\.\.\./, |
| 1185 | − 'number': /\b(-?(0x[a-f\d]+|(\d+\.?\d*|\.\d+)(e[-+]?\d+)?)i?)\b/i, | |
| 1482 | + 'number': /(?:\b0x[a-f\d]+|(?:\b\d+\.?\d*|\B\.\d+)(?:e[-+]?\d+)?)i?/i, | |
| 1186 | 1483 | 'string': { |
| 1187 | − pattern: /("|'|`)(\\?.|\r|\n)*?\1/, | |
| 1484 | + pattern: /(["'`])(\\[\s\S]|(?!\1)[^\\])*\1/, | |
| 1188 | 1485 | greedy: true |
| 1189 | 1486 | } |
| 1190 | 1487 | }); |
| 1191 | 1488 | delete Prism.languages.go['class-name']; |
| 1192 | 1489 | |
| 1193 | 1490 | Prism.languages.java = Prism.languages.extend('clike', { |
| 1194 | − 'keyword': /\b(abstract|continue|for|new|switch|assert|default|goto|package|synchronized|boolean|do|if|private|this|break|double|implements|protected|throw|byte|else|import|public|throws|case|enum|instanceof|return|transient|catch|extends|int|short|try|char|final|interface|static|void|class|finally|long|strictfp|volatile|const|float|native|super|while)\b/, | |
| 1195 | − 'number': /\b0b[01]+\b|\b0x[\da-f]*\.?[\da-fp\-]+\b|\b\d*\.?\d+(?:e[+-]?\d+)?[df]?\b/i, | |
| 1491 | + 'keyword': /\b(?:abstract|continue|for|new|switch|assert|default|goto|package|synchronized|boolean|do|if|private|this|break|double|implements|protected|throw|byte|else|import|public|throws|case|enum|instanceof|return|transient|catch|extends|int|short|try|char|final|interface|static|void|class|finally|long|strictfp|volatile|const|float|native|super|while)\b/, | |
| 1492 | + 'number': /\b0b[01]+\b|\b0x[\da-f]*\.?[\da-fp-]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?[df]?/i, | |
| 1196 | 1493 | 'operator': { |
| 1197 | 1494 | pattern: /(^|[^.])(?:\+[+=]?|-[-=]?|!=?|<<?=?|>>?>?=?|==?|&[&=]?|\|[|=]?|\*=?|\/=?|%=?|\^=?|[?:~])/m, |
| 1198 | 1495 | lookbehind: true |
| @@ -1207,14 +1504,28 @@ Prism.languages.insertBefore('java','function', { · 8 unmodified lines | ||
| 1207 | 1504 | } |
| 1208 | 1505 | }); |
| 1209 | 1506 | |
| 1507 | +Prism.languages.insertBefore('java', 'class-name', { | |
| 1508 | + 'generics': { | |
| 1509 | + pattern: /<\s*\w+(?:\.\w+)?(?:\s*,\s*\w+(?:\.\w+)?)*>/i, | |
| 1510 | + alias: 'function', | |
| 1511 | + inside: { | |
| 1512 | + keyword: Prism.languages.java.keyword, | |
| 1513 | + punctuation: /[<>(),.:]/ | |
| 1514 | + } | |
| 1515 | + } | |
| 1516 | +}); | |
| 1517 | + | |
| 1210 | 1518 | Prism.languages.json = { |
| 1211 | − 'property': /"(?:\\.|[^\\"])*"(?=\s*:)/ig, | |
| 1212 | − 'string': /"(?!:)(?:\\.|[^\\"])*"(?!:)/g, | |
| 1213 | − 'number': /\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee][+-]?\d+)?)\b/g, | |
| 1214 | − 'punctuation': /[{}[\]);,]/g, | |
| 1519 | + 'property': /"(?:\\.|[^\\"\r\n])*"(?=\s*:)/i, | |
| 1520 | + 'string': { | |
| 1521 | + pattern: /"(?:\\.|[^\\"\r\n])*"(?!\s*:)/, | |
| 1522 | + greedy: true | |
| 1523 | + }, | |
| 1524 | + 'number': /-?\d+\.?\d*([Ee][+-]?\d+)?/, | |
| 1525 | + 'punctuation': /[{}[\],]/, | |
| 1215 | 1526 | 'operator': /:/g, |
| 1216 | − 'boolean': /\b(true|false)\b/gi, | |
| 1217 | − 'null': /\bnull\b/gi | |
| 1527 | + 'boolean': /\b(?:true|false)\b/i, | |
| 1528 | + 'null': /\bnull\b/i | |
| 1218 | 1529 | }; |
| 1219 | 1530 | |
| 1220 | 1531 | Prism.languages.jsonp = Prism.languages.json; |
| @@ -1223,7 +1534,7 @@ Prism.languages.jsonp = Prism.languages.json; · 2 unmodified lines | ||
| 1223 | 1534 | Prism.languages.kotlin = Prism.languages.extend('clike', { |
| 1224 | 1535 | 'keyword': { |
| 1225 | 1536 | // The lookbehind prevents wrong highlighting of e.g. kotlin.properties.get |
| 1226 | − pattern: /(^|[^.])\b(?:abstract|annotation|as|break|by|catch|class|companion|const|constructor|continue|crossinline|data|do|else|enum|final|finally|for|fun|get|if|import|in|init|inline|inner|interface|internal|is|lateinit|noinline|null|object|open|out|override|package|private|protected|public|reified|return|sealed|set|super|tailrec|this|throw|to|try|val|var|when|where|while)\b/, | |
| 1537 | + pattern: /(^|[^.])\b(?:abstract|actual|annotation|as|break|by|catch|class|companion|const|constructor|continue|crossinline|data|do|dynamic|else|enum|expect|external|final|finally|for|fun|get|if|import|in|infix|init|inline|inner|interface|internal|is|lateinit|noinline|null|object|open|operator|out|override|package|private|protected|public|reified|return|sealed|set|super|suspend|tailrec|this|throw|to|try|typealias|val|var|vararg|when|where|while)\b/, | |
| 1227 | 1538 | lookbehind: true |
| 1228 | 1539 | }, |
| 1229 | 1540 | 'function': [ |
| @@ -1233,7 +1544,7 @@ Prism.languages.jsonp = Prism.languages.json; · 3 unmodified lines | ||
| 1233 | 1544 | lookbehind: true |
| 1234 | 1545 | } |
| 1235 | 1546 | ], |
| 1236 | − 'number': /\b(?:0[bx][\da-fA-F]+|\d+(?:\.\d+)?(?:e[+-]?\d+)?[fFL]?)\b/, | |
| 1547 | + 'number': /\b(?:0[xX][\da-fA-F]+(?:_[\da-fA-F]+)*|0[bB][01]+(?:_[01]+)*|\d+(?:_\d+)*(?:\.\d+(?:_\d+)*)?(?:[eE][+-]?\d+(?:_\d+)*)?[fFL]?)\b/, | |
| 1237 | 1548 | 'operator': /\+[+=]?|-[-=>]?|==?=?|!(?:!|==?)?|[\/*%<>]=?|[?:]:?|\.\.|&&|\|\||\b(?:and|inv|or|shl|shr|ushr|xor)\b/ |
| 1238 | 1549 | }); |
| 1239 | 1550 | |
| @@ -1241,7 +1552,7 @@ Prism.languages.jsonp = Prism.languages.json; · 1 unmodified line | ||
| 1241 | 1552 | |
| 1242 | 1553 | Prism.languages.insertBefore('kotlin', 'string', { |
| 1243 | 1554 | 'raw-string': { |
| 1244 | − pattern: /(["'])\1\1[\s\S]*?\1{3}/, | |
| 1555 | + pattern: /("""|''')[\s\S]*?\1/, | |
| 1245 | 1556 | alias: 'string' |
| 1246 | 1557 | // See interpolation below |
| 1247 | 1558 | } |
| @@ -1267,7 +1578,7 @@ Prism.languages.jsonp = Prism.languages.json; · 19 unmodified lines | ||
| 1267 | 1578 | pattern: /^\$\{|\}$/, |
| 1268 | 1579 | alias: 'variable' |
| 1269 | 1580 | }, |
| 1270 | − rest: Prism.util.clone(Prism.languages.kotlin) | |
| 1581 | + rest: Prism.languages.kotlin | |
| 1271 | 1582 | } |
| 1272 | 1583 | }, |
| 1273 | 1584 | { |
| @@ -1281,6 +1592,7 @@ Prism.languages.jsonp = Prism.languages.json; · 7 unmodified lines | ||
| 1281 | 1592 | }; |
| 1282 | 1593 | |
| 1283 | 1594 | }(Prism)); |
| 1595 | + | |
| 1284 | 1596 | Prism.languages.lua = { |
| 1285 | 1597 | 'comment': /^#!.+|--(?:\[(=*)\[[\s\S]*?\]\1\]|.*)/m, |
| 1286 | 1598 | // \z may be used to skip the following space |
| @@ -1301,16 +1613,68 @@ Prism.languages.lua = { · 14 unmodified lines | ||
| 1301 | 1613 | ], |
| 1302 | 1614 | 'punctuation': /[\[\](){},;]|\.+|:+/ |
| 1303 | 1615 | }; |
| 1616 | +(function(Prism) { | |
| 1617 | + Prism.languages.crystal = Prism.languages.extend('ruby', { | |
| 1618 | + keyword: [ | |
| 1619 | + /\b(?:abstract|alias|as|asm|begin|break|case|class|def|do|else|elsif|end|ensure|enum|extend|for|fun|if|include|instance_sizeof|lib|macro|module|next|of|out|pointerof|private|protected|rescue|return|require|select|self|sizeof|struct|super|then|type|typeof|uninitialized|union|unless|until|when|while|with|yield|__DIR__|__END_LINE__|__FILE__|__LINE__)\b/, | |
| 1620 | + { | |
| 1621 | + pattern: /(\.\s*)(?:is_a|responds_to)\?/, | |
| 1622 | + lookbehind: true | |
| 1623 | + } | |
| 1624 | + ], | |
| 1625 | + | |
| 1626 | + number: /\b(?:0b[01_]*[01]|0o[0-7_]*[0-7]|0x[\da-fA-F_]*[\da-fA-F]|(?:\d(?:[\d_]*\d)?)(?:\.[\d_]*\d)?(?:[eE][+-]?[\d_]*\d)?)(?:_(?:[uif](?:8|16|32|64))?)?\b/ | |
| 1627 | + }); | |
| 1628 | + | |
| 1629 | + Prism.languages.insertBefore('crystal', 'string', { | |
| 1630 | + attribute: { | |
| 1631 | + pattern: /@\[.+?\]/, | |
| 1632 | + alias: 'attr-name', | |
| 1633 | + inside: { | |
| 1634 | + delimiter: { | |
| 1635 | + pattern: /^@\[|\]$/, | |
| 1636 | + alias: 'tag' | |
| 1637 | + }, | |
| 1638 | + rest: Prism.languages.crystal | |
| 1639 | + } | |
| 1640 | + }, | |
| 1641 | + | |
| 1642 | + expansion: [ | |
| 1643 | + { | |
| 1644 | + pattern: /\{\{.+?\}\}/, | |
| 1645 | + inside: { | |
| 1646 | + delimiter: { | |
| 1647 | + pattern: /^\{\{|\}\}$/, | |
| 1648 | + alias: 'tag' | |
| 1649 | + }, | |
| 1650 | + rest: Prism.languages.crystal | |
| 1651 | + } | |
| 1652 | + }, | |
| 1653 | + { | |
| 1654 | + pattern: /\{%.+?%\}/, | |
| 1655 | + inside: { | |
| 1656 | + delimiter: { | |
| 1657 | + pattern: /^\{%|%\}$/, | |
| 1658 | + alias: 'tag' | |
| 1659 | + }, | |
| 1660 | + rest: Prism.languages.crystal | |
| 1661 | + } | |
| 1662 | + } | |
| 1663 | + ] | |
| 1664 | + }); | |
| 1665 | + | |
| 1666 | +}(Prism)); | |
| 1667 | + | |
| 1304 | 1668 | Prism.languages.nginx = Prism.languages.extend('clike', { |
| 1305 | − 'comment': { | |
| 1306 | − pattern: /(^|[^"{\\])#.*/, | |
| 1307 | − lookbehind: true | |
| 1308 | − }, | |
| 1309 | − 'keyword': /\b(?:CONTENT_|DOCUMENT_|GATEWAY_|HTTP_|HTTPS|if_not_empty|PATH_|QUERY_|REDIRECT_|REMOTE_|REQUEST_|SCGI|SCRIPT_|SERVER_|http|server|events|location|include|accept_mutex|accept_mutex_delay|access_log|add_after_body|add_before_body|add_header|addition_types|aio|alias|allow|ancient_browser|ancient_browser_value|auth|auth_basic|auth_basic_user_file|auth_http|auth_http_header|auth_http_timeout|autoindex|autoindex_exact_size|autoindex_localtime|break|charset|charset_map|charset_types|chunked_transfer_encoding|client_body_buffer_size|client_body_in_file_only|client_body_in_single_buffer|client_body_temp_path|client_body_timeout|client_header_buffer_size|client_header_timeout|client_max_body_size|connection_pool_size|create_full_put_path|daemon|dav_access|dav_methods|debug_connection|debug_points|default_type|deny|devpoll_changes|devpoll_events|directio|directio_alignment|disable_symlinks|empty_gif|env|epoll_events|error_log|error_page|expires|fastcgi_buffer_size|fastcgi_buffers|fastcgi_busy_buffers_size|fastcgi_cache|fastcgi_cache_bypass|fastcgi_cache_key|fastcgi_cache_lock|fastcgi_cache_lock_timeout|fastcgi_cache_methods|fastcgi_cache_min_uses|fastcgi_cache_path|fastcgi_cache_purge|fastcgi_cache_use_stale|fastcgi_cache_valid|fastcgi_connect_timeout|fastcgi_hide_header|fastcgi_ignore_client_abort|fastcgi_ignore_headers|fastcgi_index|fastcgi_intercept_errors|fastcgi_keep_conn|fastcgi_max_temp_file_size|fastcgi_next_upstream|fastcgi_no_cache|fastcgi_param|fastcgi_pass|fastcgi_pass_header|fastcgi_read_timeout|fastcgi_redirect_errors|fastcgi_send_timeout|fastcgi_split_path_info|fastcgi_store|fastcgi_store_access|fastcgi_temp_file_write_size|fastcgi_temp_path|flv|geo|geoip_city|geoip_country|google_perftools_profiles|gzip|gzip_buffers|gzip_comp_level|gzip_disable|gzip_http_version|gzip_min_length|gzip_proxied|gzip_static|gzip_types|gzip_vary|if|if_modified_since|ignore_invalid_headers|image_filter|image_filter_buffer|image_filter_jpeg_quality|image_filter_sharpen|image_filter_transparency|imap_capabilities|imap_client_buffer|include|index|internal|ip_hash|keepalive|keepalive_disable|keepalive_requests|keepalive_timeout|kqueue_changes|kqueue_events|large_client_header_buffers|limit_conn|limit_conn_log_level|limit_conn_zone|limit_except|limit_rate|limit_rate_after|limit_req|limit_req_log_level|limit_req_zone|limit_zone|lingering_close|lingering_time|lingering_timeout|listen|location|lock_file|log_format|log_format_combined|log_not_found|log_subrequest|map|map_hash_bucket_size|map_hash_max_size|master_process|max_ranges|memcached_buffer_size|memcached_connect_timeout|memcached_next_upstream|memcached_pass|memcached_read_timeout|memcached_send_timeout|merge_slashes|min_delete_depth|modern_browser|modern_browser_value|mp4|mp4_buffer_size|mp4_max_buffer_size|msie_padding|msie_refresh|multi_accept|open_file_cache|open_file_cache_errors|open_file_cache_min_uses|open_file_cache_valid|open_log_file_cache|optimize_server_names|override_charset|pcre_jit|perl|perl_modules|perl_require|perl_set|pid|pop3_auth|pop3_capabilities|port_in_redirect|post_action|postpone_output|protocol|proxy|proxy_buffer|proxy_buffer_size|proxy_buffering|proxy_buffers|proxy_busy_buffers_size|proxy_cache|proxy_cache_bypass|proxy_cache_key|proxy_cache_lock|proxy_cache_lock_timeout|proxy_cache_methods|proxy_cache_min_uses|proxy_cache_path|proxy_cache_use_stale|proxy_cache_valid|proxy_connect_timeout|proxy_cookie_domain|proxy_cookie_path|proxy_headers_hash_bucket_size|proxy_headers_hash_max_size|proxy_hide_header|proxy_http_version|proxy_ignore_client_abort|proxy_ignore_headers|proxy_intercept_errors|proxy_max_temp_file_size|proxy_method|proxy_next_upstream|proxy_no_cache|proxy_pass|proxy_pass_error_message|proxy_pass_header|proxy_pass_request_body|proxy_pass_request_headers|proxy_read_timeout|proxy_redirect|proxy_redirect_errors|proxy_send_lowat|proxy_send_timeout|proxy_set_body|proxy_set_header|proxy_ssl_session_reuse|proxy_store|proxy_store_access|proxy_temp_file_write_size|proxy_temp_path|proxy_timeout|proxy_upstream_fail_timeout|proxy_upstream_max_fails|random_index|read_ahead|real_ip_header|recursive_error_pages|request_pool_size|reset_timedout_connection|resolver|resolver_timeout|return|rewrite|root|rtsig_overflow_events|rtsig_overflow_test|rtsig_overflow_threshold|rtsig_signo|satisfy|satisfy_any|secure_link_secret|send_lowat|send_timeout|sendfile|sendfile_max_chunk|server|server_name|server_name_in_redirect|server_names_hash_bucket_size|server_names_hash_max_size|server_tokens|set|set_real_ip_from|smtp_auth|smtp_capabilities|so_keepalive|source_charset|split_clients|ssi|ssi_silent_errors|ssi_types|ssi_value_length|ssl|ssl_certificate|ssl_certificate_key|ssl_ciphers|ssl_client_certificate|ssl_crl|ssl_dhparam|ssl_engine|ssl_prefer_server_ciphers|ssl_protocols|ssl_session_cache|ssl_session_timeout|ssl_verify_client|ssl_verify_depth|starttls|stub_status|sub_filter|sub_filter_once|sub_filter_types|tcp_nodelay|tcp_nopush|timeout|timer_resolution|try_files|types|types_hash_bucket_size|types_hash_max_size|underscores_in_headers|uninitialized_variable_warn|upstream|use|user|userid|userid_domain|userid_expires|userid_name|userid_p3p|userid_path|userid_service|valid_referers|variables_hash_bucket_size|variables_hash_max_size|worker_connections|worker_cpu_affinity|worker_priority|worker_processes|worker_rlimit_core|worker_rlimit_nofile|worker_rlimit_sigpending|working_directory|xclient|xml_entities|xslt_entities|xslt_stylesheet|xslt_types)\b/i, | |
| 1669 | + 'comment': { | |
| 1670 | + pattern: /(^|[^"{\\])#.*/, | |
| 1671 | + lookbehind: true | |
| 1672 | + }, | |
| 1673 | + 'keyword': /\b(?:CONTENT_|DOCUMENT_|GATEWAY_|HTTP_|HTTPS|if_not_empty|PATH_|QUERY_|REDIRECT_|REMOTE_|REQUEST_|SCGI|SCRIPT_|SERVER_|http|events|accept_mutex|accept_mutex_delay|access_log|add_after_body|add_before_body|add_header|addition_types|aio|alias|allow|ancient_browser|ancient_browser_value|auth|auth_basic|auth_basic_user_file|auth_http|auth_http_header|auth_http_timeout|autoindex|autoindex_exact_size|autoindex_localtime|break|charset|charset_map|charset_types|chunked_transfer_encoding|client_body_buffer_size|client_body_in_file_only|client_body_in_single_buffer|client_body_temp_path|client_body_timeout|client_header_buffer_size|client_header_timeout|client_max_body_size|connection_pool_size|create_full_put_path|daemon|dav_access|dav_methods|debug_connection|debug_points|default_type|deny|devpoll_changes|devpoll_events|directio|directio_alignment|disable_symlinks|empty_gif|env|epoll_events|error_log|error_page|expires|fastcgi_buffer_size|fastcgi_buffers|fastcgi_busy_buffers_size|fastcgi_cache|fastcgi_cache_bypass|fastcgi_cache_key|fastcgi_cache_lock|fastcgi_cache_lock_timeout|fastcgi_cache_methods|fastcgi_cache_min_uses|fastcgi_cache_path|fastcgi_cache_purge|fastcgi_cache_use_stale|fastcgi_cache_valid|fastcgi_connect_timeout|fastcgi_hide_header|fastcgi_ignore_client_abort|fastcgi_ignore_headers|fastcgi_index|fastcgi_intercept_errors|fastcgi_keep_conn|fastcgi_max_temp_file_size|fastcgi_next_upstream|fastcgi_no_cache|fastcgi_param|fastcgi_pass|fastcgi_pass_header|fastcgi_read_timeout|fastcgi_redirect_errors|fastcgi_send_timeout|fastcgi_split_path_info|fastcgi_store|fastcgi_store_access|fastcgi_temp_file_write_size|fastcgi_temp_path|flv|geo|geoip_city|geoip_country|google_perftools_profiles|gzip|gzip_buffers|gzip_comp_level|gzip_disable|gzip_http_version|gzip_min_length|gzip_proxied|gzip_static|gzip_types|gzip_vary|if|if_modified_since|ignore_invalid_headers|image_filter|image_filter_buffer|image_filter_jpeg_quality|image_filter_sharpen|image_filter_transparency|imap_capabilities|imap_client_buffer|include|index|internal|ip_hash|keepalive|keepalive_disable|keepalive_requests|keepalive_timeout|kqueue_changes|kqueue_events|large_client_header_buffers|limit_conn|limit_conn_log_level|limit_conn_zone|limit_except|limit_rate|limit_rate_after|limit_req|limit_req_log_level|limit_req_zone|limit_zone|lingering_close|lingering_time|lingering_timeout|listen|location|lock_file|log_format|log_format_combined|log_not_found|log_subrequest|map|map_hash_bucket_size|map_hash_max_size|master_process|max_ranges|memcached_buffer_size|memcached_connect_timeout|memcached_next_upstream|memcached_pass|memcached_read_timeout|memcached_send_timeout|merge_slashes|min_delete_depth|modern_browser|modern_browser_value|mp4|mp4_buffer_size|mp4_max_buffer_size|msie_padding|msie_refresh|multi_accept|open_file_cache|open_file_cache_errors|open_file_cache_min_uses|open_file_cache_valid|open_log_file_cache|optimize_server_names|override_charset|pcre_jit|perl|perl_modules|perl_require|perl_set|pid|pop3_auth|pop3_capabilities|port_in_redirect|post_action|postpone_output|protocol|proxy|proxy_buffer|proxy_buffer_size|proxy_buffering|proxy_buffers|proxy_busy_buffers_size|proxy_cache|proxy_cache_bypass|proxy_cache_key|proxy_cache_lock|proxy_cache_lock_timeout|proxy_cache_methods|proxy_cache_min_uses|proxy_cache_path|proxy_cache_use_stale|proxy_cache_valid|proxy_connect_timeout|proxy_cookie_domain|proxy_cookie_path|proxy_headers_hash_bucket_size|proxy_headers_hash_max_size|proxy_hide_header|proxy_http_version|proxy_ignore_client_abort|proxy_ignore_headers|proxy_intercept_errors|proxy_max_temp_file_size|proxy_method|proxy_next_upstream|proxy_no_cache|proxy_pass|proxy_pass_error_message|proxy_pass_header|proxy_pass_request_body|proxy_pass_request_headers|proxy_read_timeout|proxy_redirect|proxy_redirect_errors|proxy_send_lowat|proxy_send_timeout|proxy_set_body|proxy_set_header|proxy_ssl_session_reuse|proxy_store|proxy_store_access|proxy_temp_file_write_size|proxy_temp_path|proxy_timeout|proxy_upstream_fail_timeout|proxy_upstream_max_fails|random_index|read_ahead|real_ip_header|recursive_error_pages|request_pool_size|reset_timedout_connection|resolver|resolver_timeout|return|rewrite|root|rtsig_overflow_events|rtsig_overflow_test|rtsig_overflow_threshold|rtsig_signo|satisfy|satisfy_any|secure_link_secret|send_lowat|send_timeout|sendfile|sendfile_max_chunk|server|server_name|server_name_in_redirect|server_names_hash_bucket_size|server_names_hash_max_size|server_tokens|set|set_real_ip_from|smtp_auth|smtp_capabilities|so_keepalive|source_charset|split_clients|ssi|ssi_silent_errors|ssi_types|ssi_value_length|ssl|ssl_certificate|ssl_certificate_key|ssl_ciphers|ssl_client_certificate|ssl_crl|ssl_dhparam|ssl_engine|ssl_prefer_server_ciphers|ssl_protocols|ssl_session_cache|ssl_session_timeout|ssl_verify_client|ssl_verify_depth|starttls|stub_status|sub_filter|sub_filter_once|sub_filter_types|tcp_nodelay|tcp_nopush|timeout|timer_resolution|try_files|types|types_hash_bucket_size|types_hash_max_size|underscores_in_headers|uninitialized_variable_warn|upstream|use|user|userid|userid_domain|userid_expires|userid_name|userid_p3p|userid_path|userid_service|valid_referers|variables_hash_bucket_size|variables_hash_max_size|worker_connections|worker_cpu_affinity|worker_priority|worker_processes|worker_rlimit_core|worker_rlimit_nofile|worker_rlimit_sigpending|working_directory|xclient|xml_entities|xslt_entities|xslt_stylesheet|xslt_types)\b/i | |
| 1310 | 1674 | }); |
| 1311 | 1675 | |
| 1312 | 1676 | Prism.languages.insertBefore('nginx', 'keyword', { |
| 1313 | − 'variable': /\$[a-z_]+/i | |
| 1677 | + 'variable': /\$[a-z_]+/i | |
| 1314 | 1678 | }); |
| 1315 | 1679 | Prism.languages.nim = { |
| 1316 | 1680 | 'comment': /#.*/, |
| @@ -1361,13 +1725,13 @@ Prism.languages.perl = { · 44 unmodified lines | ||
| 1361 | 1725 | 'string': [ |
| 1362 | 1726 | // q/.../ |
| 1363 | 1727 | { |
| 1364 | − pattern: /\b(?:q|qq|qx|qw)\s*([^a-zA-Z0-9\s\{\(\[<])(?:[^\\]|\\[\s\S])*?\1/, | |
| 1728 | + pattern: /\b(?:q|qq|qx|qw)\s*([^a-zA-Z0-9\s{(\[<])(?:(?!\1)[^\\]|\\[\s\S])*\1/, | |
| 1365 | 1729 | greedy: true |
| 1366 | 1730 | }, |
| 1367 | 1731 | |
| 1368 | 1732 | // q a...a |
| 1369 | 1733 | { |
| 1370 | − pattern: /\b(?:q|qq|qx|qw)\s+([a-zA-Z0-9])(?:[^\\]|\\[\s\S])*?\1/, | |
| 1734 | + pattern: /\b(?:q|qq|qx|qw)\s+([a-zA-Z0-9])(?:(?!\1)[^\\]|\\[\s\S])*\1/, | |
| 1371 | 1735 | greedy: true |
| 1372 | 1736 | }, |
| 1373 | 1737 | |
| @@ -1397,7 +1761,7 @@ Prism.languages.perl = { · 23 unmodified lines | ||
| 1397 | 1761 | |
| 1398 | 1762 | // "...", `...` |
| 1399 | 1763 | { |
| 1400 | − pattern: /("|`)(?:[^\\]|\\[\s\S])*?\1/, | |
| 1764 | + pattern: /("|`)(?:(?!\1)[^\\]|\\[\s\S])*\1/, | |
| 1401 | 1765 | greedy: true |
| 1402 | 1766 | }, |
| 1403 | 1767 | |
| @@ -1411,13 +1775,13 @@ Prism.languages.perl = { · 7 unmodified lines | ||
| 1411 | 1775 | 'regex': [ |
| 1412 | 1776 | // m/.../ |
| 1413 | 1777 | { |
| 1414 | − pattern: /\b(?:m|qr)\s*([^a-zA-Z0-9\s\{\(\[<])(?:[^\\]|\\[\s\S])*?\1[msixpodualngc]*/, | |
| 1778 | + pattern: /\b(?:m|qr)\s*([^a-zA-Z0-9\s{(\[<])(?:(?!\1)[^\\]|\\[\s\S])*\1[msixpodualngc]*/, | |
| 1415 | 1779 | greedy: true |
| 1416 | 1780 | }, |
| 1417 | 1781 | |
| 1418 | 1782 | // m a...a |
| 1419 | 1783 | { |
| 1420 | − pattern: /\b(?:m|qr)\s+([a-zA-Z0-9])(?:[^\\]|\\.)*?\1[msixpodualngc]*/, | |
| 1784 | + pattern: /\b(?:m|qr)\s+([a-zA-Z0-9])(?:(?!\1)[^\\]|\\[\s\S])*\1[msixpodualngc]*/, | |
| 1421 | 1785 | greedy: true |
| 1422 | 1786 | }, |
| 1423 | 1787 | |
| @@ -1449,14 +1813,14 @@ Prism.languages.perl = { · 25 unmodified lines | ||
| 1449 | 1813 | // FIXME We don't handle change of separator like s(...)[...] |
| 1450 | 1814 | // s/.../.../ |
| 1451 | 1815 | { |
| 1452 | − pattern: /(^|[^-]\b)(?:s|tr|y)\s*([^a-zA-Z0-9\s\{\(\[<])(?:[^\\]|\\[\s\S])*?\2(?:[^\\]|\\[\s\S])*?\2[msixpodualngcer]*/, | |
| 1816 | + pattern: /(^|[^-]\b)(?:s|tr|y)\s*([^a-zA-Z0-9\s{(\[<])(?:(?!\2)[^\\]|\\[\s\S])*\2(?:(?!\2)[^\\]|\\[\s\S])*\2[msixpodualngcer]*/, | |
| 1453 | 1817 | lookbehind: true, |
| 1454 | 1818 | greedy: true |
| 1455 | 1819 | }, |
| 1456 | 1820 | |
| 1457 | 1821 | // s a...a...a |
| 1458 | 1822 | { |
| 1459 | − pattern: /(^|[^-]\b)(?:s|tr|y)\s+([a-zA-Z0-9])(?:[^\\]|\\[\s\S])*?\2(?:[^\\]|\\[\s\S])*?\2[msixpodualngcer]*/, | |
| 1823 | + pattern: /(^|[^-]\b)(?:s|tr|y)\s+([a-zA-Z0-9])(?:(?!\2)[^\\]|\\[\s\S])*\2(?:(?!\2)[^\\]|\\[\s\S])*\2[msixpodualngcer]*/, | |
| 1460 | 1824 | lookbehind: true, |
| 1461 | 1825 | greedy: true |
| 1462 | 1826 | }, |
| @@ -1508,7 +1872,7 @@ Prism.languages.perl = { · 45 unmodified lines | ||
| 1508 | 1872 | // ${...} |
| 1509 | 1873 | /[&*$@%]#?(?=\{)/, |
| 1510 | 1874 | // $foo |
| 1511 | − /[&*$@%]#?((::)*'?(?!\d)[\w$]+)+(::)*/i, | |
| 1875 | + /[&*$@%]#?(?:(?:::)*'?(?!\d)[\w$]+)+(?:::)*/i, | |
| 1512 | 1876 | // $1 |
| 1513 | 1877 | /[&*$@%]\d+/, |
| 1514 | 1878 | // $_, @_, %! |
| @@ -1522,7 +1886,7 @@ Prism.languages.perl = { · 7 unmodified lines | ||
| 1522 | 1886 | }, |
| 1523 | 1887 | 'vstring': { |
| 1524 | 1888 | // v1.2, 1.2.3 |
| 1525 | − pattern: /v\d+(\.\d+)*|\d+(\.\d+){2,}/, | |
| 1889 | + pattern: /v\d+(?:\.\d+)*|\d+(?:\.\d+){2,}/, | |
| 1526 | 1890 | alias: 'string' |
| 1527 | 1891 | }, |
| 1528 | 1892 | 'function': { |
| @@ -1531,9 +1895,9 @@ Prism.languages.perl = { · 2 unmodified lines | ||
| 1531 | 1895 | keyword: /sub/ |
| 1532 | 1896 | } |
| 1533 | 1897 | }, |
| 1534 | − 'keyword': /\b(any|break|continue|default|delete|die|do|else|elsif|eval|for|foreach|given|goto|if|last|local|my|next|our|package|print|redo|require|say|state|sub|switch|undef|unless|until|use|when|while)\b/, | |
| 1535 | − 'number': /\b-?(0x[\dA-Fa-f](_?[\dA-Fa-f])*|0b[01](_?[01])*|(\d(_?\d)*)?\.?\d(_?\d)*([Ee][+-]?\d+)?)\b/, | |
| 1536 | − 'operator': /-[rwxoRWXOezsfdlpSbctugkTBMAC]\b|\+[+=]?|-[-=>]?|\*\*?=?|\/\/?=?|=[=~>]?|~[~=]?|\|\|?=?|&&?=?|<(?:=>?|<=?)?|>>?=?|![~=]?|[%^]=?|\.(?:=|\.\.?)?|[\\?]|\bx(?:=|\b)|\b(lt|gt|le|ge|eq|ne|cmp|not|and|or|xor)\b/, | |
| 1898 | + 'keyword': /\b(?:any|break|continue|default|delete|die|do|else|elsif|eval|for|foreach|given|goto|if|last|local|my|next|our|package|print|redo|require|say|state|sub|switch|undef|unless|until|use|when|while)\b/, | |
| 1899 | + 'number': /\b(?:0x[\dA-Fa-f](?:_?[\dA-Fa-f])*|0b[01](?:_?[01])*|(?:\d(?:_?\d)*)?\.?\d(?:_?\d)*(?:[Ee][+-]?\d+)?)\b/, | |
| 1900 | + 'operator': /-[rwxoRWXOezsfdlpSbctugkTBMAC]\b|\+[+=]?|-[-=>]?|\*\*?=?|\/\/?=?|=[=~>]?|~[~=]?|\|\|?=?|&&?=?|<(?:=>?|<=?)?|>>?=?|![~=]?|[%^]=?|\.(?:=|\.\.?)?|[\\?]|\bx(?:=|\b)|\b(?:lt|gt|le|ge|eq|ne|cmp|not|and|or|xor)\b/, | |
| 1537 | 1901 | 'punctuation': /[{}[\];(),:]/ |
| 1538 | 1902 | }; |
| 1539 | 1903 | |
| @@ -1549,136 +1913,239 @@ Prism.languages.perl = { · 9 unmodified lines | ||
| 1549 | 1913 | * Adds the following new token classes: |
| 1550 | 1914 | * constant, delimiter, variable, function, package |
| 1551 | 1915 | */ |
| 1552 | − | |
| 1553 | −Prism.languages.php = Prism.languages.extend('clike', { | |
| 1554 | − 'keyword': /\b(and|or|xor|array|as|break|case|cfunction|class|const|continue|declare|default|die|do|else|elseif|enddeclare|endfor|endforeach|endif|endswitch|endwhile|extends|for|foreach|function|include|include_once|global|if|new|return|static|switch|use|require|require_once|var|while|abstract|interface|public|implements|private|protected|parent|throw|null|echo|print|trait|namespace|final|yield|goto|instanceof|finally|try|catch)\b/i, | |
| 1555 | − 'constant': /\b[A-Z0-9_]{2,}\b/, | |
| 1556 | − 'comment': { | |
| 1557 | − pattern: /(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/, | |
| 1558 | − lookbehind: true | |
| 1559 | − } | |
| 1560 | −}); | |
| 1561 | − | |
| 1562 | −// Shell-like comments are matched after strings, because they are less | |
| 1563 | −// common than strings containing hashes... | |
| 1564 | −Prism.languages.insertBefore('php', 'class-name', { | |
| 1565 | − 'shell-comment': { | |
| 1566 | − pattern: /(^|[^\\])#.*/, | |
| 1567 | − lookbehind: true, | |
| 1568 | − alias: 'comment' | |
| 1569 | − } | |
| 1570 | −}); | |
| 1571 | − | |
| 1572 | −Prism.languages.insertBefore('php', 'keyword', { | |
| 1573 | − 'delimiter': { | |
| 1574 | − pattern: /\?>|<\?(?:php|=)?/i, | |
| 1575 | − alias: 'important' | |
| 1576 | − }, | |
| 1577 | − 'variable': /\$\w+\b/i, | |
| 1578 | − 'package': { | |
| 1579 | − pattern: /(\\|namespace\s+|use\s+)[\w\\]+/, | |
| 1580 | − lookbehind: true, | |
| 1581 | − inside: { | |
| 1582 | − punctuation: /\\/ | |
| 1583 | − } | |
| 1584 | − } | |
| 1585 | −}); | |
| 1586 | − | |
| 1587 | −// Must be defined after the function pattern | |
| 1588 | −Prism.languages.insertBefore('php', 'operator', { | |
| 1589 | − 'property': { | |
| 1590 | − pattern: /(->)[\w]+/, | |
| 1591 | − lookbehind: true | |
| 1592 | − } | |
| 1593 | −}); | |
| 1594 | − | |
| 1595 | −// Add HTML support if the markup language exists | |
| 1596 | −if (Prism.languages.markup) { | |
| 1597 | − | |
| 1598 | − // Tokenize all inline PHP blocks that are wrapped in <?php ?> | |
| 1599 | − // This allows for easy PHP + markup highlighting | |
| 1600 | − Prism.hooks.add('before-highlight', function(env) { | |
| 1601 | − if (env.language !== 'php' || !/(?:<\?php|<\?)/ig.test(env.code)) { | |
| 1602 | − return; | |
| 1916 | +(function (Prism) { | |
| 1917 | + Prism.languages.php = Prism.languages.extend('clike', { | |
| 1918 | + 'keyword': /\b(?:and|or|xor|array|as|break|case|cfunction|class|const|continue|declare|default|die|do|else|elseif|enddeclare|endfor|endforeach|endif|endswitch|endwhile|extends|for|foreach|function|include|include_once|global|if|new|return|static|switch|use|require|require_once|var|while|abstract|interface|public|implements|private|protected|parent|throw|null|echo|print|trait|namespace|final|yield|goto|instanceof|finally|try|catch)\b/i, | |
| 1919 | + 'constant': /\b[A-Z0-9_]{2,}\b/, | |
| 1920 | + 'comment': { | |
| 1921 | + pattern: /(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/, | |
| 1922 | + lookbehind: true | |
| 1603 | 1923 | } |
| 1604 | − | |
| 1605 | − env.tokenStack = []; | |
| 1606 | − | |
| 1607 | − env.backupCode = env.code; | |
| 1608 | − env.code = env.code.replace(/(?:<\?php|<\?)[\s\S]*?(?:\?>|$)/ig, function(match) { | |
| 1609 | − var i = env.tokenStack.length; | |
| 1610 | − // Check for existing strings | |
| 1611 | − while (env.backupCode.indexOf('___PHP' + i + '___') !== -1) | |
| 1612 | − ++i; | |
| 1613 | − | |
| 1614 | − // Create a sparse array | |
| 1615 | − env.tokenStack[i] = match; | |
| 1616 | − | |
| 1617 | − return '___PHP' + i + '___'; | |
| 1618 | − }); | |
| 1619 | − | |
| 1620 | − // Switch the grammar to markup | |
| 1621 | − env.grammar = Prism.languages.markup; | |
| 1622 | 1924 | }); |
| 1623 | 1925 | |
| 1624 | − // Restore env.code for other plugins (e.g. line-numbers) | |
| 1625 | − Prism.hooks.add('before-insert', function(env) { | |
| 1626 | − if (env.language === 'php' && env.backupCode) { | |
| 1627 | − env.code = env.backupCode; | |
| 1628 | − delete env.backupCode; | |
| 1926 | + Prism.languages.insertBefore('php', 'string', { | |
| 1927 | + 'shell-comment': { | |
| 1928 | + pattern: /(^|[^\\])#.*/, | |
| 1929 | + lookbehind: true, | |
| 1930 | + alias: 'comment' | |
| 1629 | 1931 | } |
| 1630 | 1932 | }); |
| 1631 | 1933 | |
| 1632 | − // Re-insert the tokens after highlighting | |
| 1633 | − Prism.hooks.add('after-highlight', function(env) { | |
| 1634 | − if (env.language !== 'php' || !env.tokenStack) { | |
| 1635 | − return; | |
| 1934 | + Prism.languages.insertBefore('php', 'keyword', { | |
| 1935 | + 'delimiter': { | |
| 1936 | + pattern: /\?>|<\?(?:php|=)?/i, | |
| 1937 | + alias: 'important' | |
| 1938 | + }, | |
| 1939 | + 'variable': /\$+(?:\w+\b|(?={))/i, | |
| 1940 | + 'package': { | |
| 1941 | + pattern: /(\\|namespace\s+|use\s+)[\w\\]+/, | |
| 1942 | + lookbehind: true, | |
| 1943 | + inside: { | |
| 1944 | + punctuation: /\\/ | |
| 1945 | + } | |
| 1636 | 1946 | } |
| 1947 | + }); | |
| 1637 | 1948 | |
| 1638 | − // Switch the grammar back | |
| 1639 | − env.grammar = Prism.languages.php; | |
| 1640 | − | |
| 1641 | − for (var i = 0, keys = Object.keys(env.tokenStack); i < keys.length; ++i) { | |
| 1642 | − var k = keys[i]; | |
| 1643 | − var t = env.tokenStack[k]; | |
| 1644 | − | |
| 1645 | − // The replace prevents $$, $&, $`, $', $n, $nn from being interpreted as special patterns | |
| 1646 | − env.highlightedCode = env.highlightedCode.replace('___PHP' + k + '___', | |
| 1647 | − "<span class=\"token php language-php\">" + | |
| 1648 | − Prism.highlight(t, env.grammar, 'php').replace(/\$/g, '$$$$') + | |
| 1649 | − "</span>"); | |
| 1949 | + // Must be defined after the function pattern | |
| 1950 | + Prism.languages.insertBefore('php', 'operator', { | |
| 1951 | + 'property': { | |
| 1952 | + pattern: /(->)[\w]+/, | |
| 1953 | + lookbehind: true | |
| 1650 | 1954 | } |
| 1651 | − | |
| 1652 | − env.element.innerHTML = env.highlightedCode; | |
| 1653 | 1955 | }); |
| 1654 | −} | |
| 1655 | −; | |
| 1656 | −Prism.languages.python= { | |
| 1657 | − 'triple-quoted-string': { | |
| 1658 | − pattern: /"""[\s\S]+?"""|'''[\s\S]+?'''/, | |
| 1659 | − alias: 'string' | |
| 1660 | − }, | |
| 1661 | − 'comment': { | |
| 1956 | + | |
| 1957 | + Prism.languages.insertBefore('php', 'string', { | |
| 1958 | + 'nowdoc-string': { | |
| 1959 | + pattern: /<<<'([^']+)'(?:\r\n?|\n)(?:.*(?:\r\n?|\n))*?\1;/, | |
| 1960 | + greedy: true, | |
| 1961 | + alias: 'string', | |
| 1962 | + inside: { | |
| 1963 | + 'delimiter': { | |
| 1964 | + pattern: /^<<<'[^']+'|[a-z_]\w*;$/i, | |
| 1965 | + alias: 'symbol', | |
| 1966 | + inside: { | |
| 1967 | + 'punctuation': /^<<<'?|[';]$/ | |
| 1968 | + } | |
| 1969 | + } | |
| 1970 | + } | |
| 1971 | + }, | |
| 1972 | + 'heredoc-string': { | |
| 1973 | + pattern: /<<<(?:"([^"]+)"(?:\r\n?|\n)(?:.*(?:\r\n?|\n))*?\1;|([a-z_]\w*)(?:\r\n?|\n)(?:.*(?:\r\n?|\n))*?\2;)/i, | |
| 1974 | + greedy: true, | |
| 1975 | + alias: 'string', | |
| 1976 | + inside: { | |
| 1977 | + 'delimiter': { | |
| 1978 | + pattern: /^<<<(?:"[^"]+"|[a-z_]\w*)|[a-z_]\w*;$/i, | |
| 1979 | + alias: 'symbol', | |
| 1980 | + inside: { | |
| 1981 | + 'punctuation': /^<<<"?|[";]$/ | |
| 1982 | + } | |
| 1983 | + }, | |
| 1984 | + 'interpolation': null // See below | |
| 1985 | + } | |
| 1986 | + }, | |
| 1987 | + 'single-quoted-string': { | |
| 1988 | + pattern: /'(?:\\[\s\S]|[^\\'])*'/, | |
| 1989 | + greedy: true, | |
| 1990 | + alias: 'string' | |
| 1991 | + }, | |
| 1992 | + 'double-quoted-string': { | |
| 1993 | + pattern: /"(?:\\[\s\S]|[^\\"])*"/, | |
| 1994 | + greedy: true, | |
| 1995 | + alias: 'string', | |
| 1996 | + inside: { | |
| 1997 | + 'interpolation': null // See below | |
| 1998 | + } | |
| 1999 | + } | |
| 2000 | + }); | |
| 2001 | + // The different types of PHP strings "replace" the C-like standard string | |
| 2002 | + delete Prism.languages.php['string']; | |
| 2003 | + | |
| 2004 | + var string_interpolation = { | |
| 2005 | + pattern: /{\$(?:{(?:{[^{}]+}|[^{}]+)}|[^{}])+}|(^|[^\\{])\$+(?:\w+(?:\[.+?]|->\w+)*)/, | |
| 2006 | + lookbehind: true, | |
| 2007 | + inside: { | |
| 2008 | + rest: Prism.languages.php | |
| 2009 | + } | |
| 2010 | + }; | |
| 2011 | + Prism.languages.php['heredoc-string'].inside['interpolation'] = string_interpolation; | |
| 2012 | + Prism.languages.php['double-quoted-string'].inside['interpolation'] = string_interpolation; | |
| 2013 | + | |
| 2014 | + Prism.hooks.add('before-tokenize', function(env) { | |
| 2015 | + if (!/(?:<\?php|<\?)/ig.test(env.code)) { | |
| 2016 | + return; | |
| 2017 | + } | |
| 2018 | + | |
| 2019 | + var phpPattern = /(?:<\?php|<\?)[\s\S]*?(?:\?>|$)/ig; | |
| 2020 | + Prism.languages['markup-templating'].buildPlaceholders(env, 'php', phpPattern); | |
| 2021 | + }); | |
| 2022 | + | |
| 2023 | + Prism.hooks.add('after-tokenize', function(env) { | |
| 2024 | + Prism.languages['markup-templating'].tokenizePlaceholders(env, 'php'); | |
| 2025 | + }); | |
| 2026 | + | |
| 2027 | +}(Prism)); | |
| 2028 | +Prism.languages.sql= { | |
| 2029 | + 'comment': { | |
| 2030 | + pattern: /(^|[^\\])(?:\/\*[\s\S]*?\*\/|(?:--|\/\/|#).*)/, | |
| 2031 | + lookbehind: true | |
| 2032 | + }, | |
| 2033 | + 'string' : { | |
| 2034 | + pattern: /(^|[^@\\])("|')(?:\\[\s\S]|(?!\2)[^\\])*\2/, | |
| 2035 | + greedy: true, | |
| 2036 | + lookbehind: true | |
| 2037 | + }, | |
| 2038 | + 'variable': /@[\w.$]+|@(["'`])(?:\\[\s\S]|(?!\1)[^\\])+\1/, | |
| 2039 | + 'function': /\b(?:AVG|COUNT|FIRST|FORMAT|LAST|LCASE|LEN|MAX|MID|MIN|MOD|NOW|ROUND|SUM|UCASE)(?=\s*\()/i, // Should we highlight user defined functions too? | |
| 2040 | + 'keyword': /\b(?:ACTION|ADD|AFTER|ALGORITHM|ALL|ALTER|ANALYZE|ANY|APPLY|AS|ASC|AUTHORIZATION|AUTO_INCREMENT|BACKUP|BDB|BEGIN|BERKELEYDB|BIGINT|BINARY|BIT|BLOB|BOOL|BOOLEAN|BREAK|BROWSE|BTREE|BULK|BY|CALL|CASCADED?|CASE|CHAIN|CHAR(?:ACTER|SET)?|CHECK(?:POINT)?|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMNS?|COMMENT|COMMIT(?:TED)?|COMPUTE|CONNECT|CONSISTENT|CONSTRAINT|CONTAINS(?:TABLE)?|CONTINUE|CONVERT|CREATE|CROSS|CURRENT(?:_DATE|_TIME|_TIMESTAMP|_USER)?|CURSOR|CYCLE|DATA(?:BASES?)?|DATE(?:TIME)?|DAY|DBCC|DEALLOCATE|DEC|DECIMAL|DECLARE|DEFAULT|DEFINER|DELAYED|DELETE|DELIMITERS?|DENY|DESC|DESCRIBE|DETERMINISTIC|DISABLE|DISCARD|DISK|DISTINCT|DISTINCTROW|DISTRIBUTED|DO|DOUBLE|DROP|DUMMY|DUMP(?:FILE)?|DUPLICATE|ELSE(?:IF)?|ENABLE|ENCLOSED|END|ENGINE|ENUM|ERRLVL|ERRORS|ESCAPED?|EXCEPT|EXEC(?:UTE)?|EXISTS|EXIT|EXPLAIN|EXTENDED|FETCH|FIELDS|FILE|FILLFACTOR|FIRST|FIXED|FLOAT|FOLLOWING|FOR(?: EACH ROW)?|FORCE|FOREIGN|FREETEXT(?:TABLE)?|FROM|FULL|FUNCTION|GEOMETRY(?:COLLECTION)?|GLOBAL|GOTO|GRANT|GROUP|HANDLER|HASH|HAVING|HOLDLOCK|HOUR|IDENTITY(?:_INSERT|COL)?|IF|IGNORE|IMPORT|INDEX|INFILE|INNER|INNODB|INOUT|INSERT|INT|INTEGER|INTERSECT|INTERVAL|INTO|INVOKER|ISOLATION|ITERATE|JOIN|KEYS?|KILL|LANGUAGE|LAST|LEAVE|LEFT|LEVEL|LIMIT|LINENO|LINES|LINESTRING|LOAD|LOCAL|LOCK|LONG(?:BLOB|TEXT)|LOOP|MATCH(?:ED)?|MEDIUM(?:BLOB|INT|TEXT)|MERGE|MIDDLEINT|MINUTE|MODE|MODIFIES|MODIFY|MONTH|MULTI(?:LINESTRING|POINT|POLYGON)|NATIONAL|NATURAL|NCHAR|NEXT|NO|NONCLUSTERED|NULLIF|NUMERIC|OFF?|OFFSETS?|ON|OPEN(?:DATASOURCE|QUERY|ROWSET)?|OPTIMIZE|OPTION(?:ALLY)?|ORDER|OUT(?:ER|FILE)?|OVER|PARTIAL|PARTITION|PERCENT|PIVOT|PLAN|POINT|POLYGON|PRECEDING|PRECISION|PREPARE|PREV|PRIMARY|PRINT|PRIVILEGES|PROC(?:EDURE)?|PUBLIC|PURGE|QUICK|RAISERROR|READS?|REAL|RECONFIGURE|REFERENCES|RELEASE|RENAME|REPEAT(?:ABLE)?|REPLACE|REPLICATION|REQUIRE|RESIGNAL|RESTORE|RESTRICT|RETURNS?|REVOKE|RIGHT|ROLLBACK|ROUTINE|ROW(?:COUNT|GUIDCOL|S)?|RTREE|RULE|SAVE(?:POINT)?|SCHEMA|SECOND|SELECT|SERIAL(?:IZABLE)?|SESSION(?:_USER)?|SET(?:USER)?|SHARE|SHOW|SHUTDOWN|SIMPLE|SMALLINT|SNAPSHOT|SOME|SONAME|SQL|START(?:ING)?|STATISTICS|STATUS|STRIPED|SYSTEM_USER|TABLES?|TABLESPACE|TEMP(?:ORARY|TABLE)?|TERMINATED|TEXT(?:SIZE)?|THEN|TIME(?:STAMP)?|TINY(?:BLOB|INT|TEXT)|TOP?|TRAN(?:SACTIONS?)?|TRIGGER|TRUNCATE|TSEQUAL|TYPES?|UNBOUNDED|UNCOMMITTED|UNDEFINED|UNION|UNIQUE|UNLOCK|UNPIVOT|UNSIGNED|UPDATE(?:TEXT)?|USAGE|USE|USER|USING|VALUES?|VAR(?:BINARY|CHAR|CHARACTER|YING)|VIEW|WAITFOR|WARNINGS|WHEN|WHERE|WHILE|WITH(?: ROLLUP|IN)?|WORK|WRITE(?:TEXT)?|YEAR)\b/i, | |
| 2041 | + 'boolean': /\b(?:TRUE|FALSE|NULL)\b/i, | |
| 2042 | + 'number': /\b0x[\da-f]+\b|\b\d+\.?\d*|\B\.\d+\b/i, | |
| 2043 | + 'operator': /[-+*\/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?|\b(?:AND|BETWEEN|IN|LIKE|NOT|OR|IS|DIV|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b/i, | |
| 2044 | + 'punctuation': /[;[\]()`,.]/ | |
| 2045 | +}; | |
| 2046 | +Prism.languages.scss = Prism.languages.extend('css', { | |
| 2047 | + 'comment': { | |
| 2048 | + pattern: /(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/, | |
| 2049 | + lookbehind: true | |
| 2050 | + }, | |
| 2051 | + 'atrule': { | |
| 2052 | + pattern: /@[\w-]+(?:\([^()]+\)|[^(])*?(?=\s+[{;])/, | |
| 2053 | + inside: { | |
| 2054 | + 'rule': /@[\w-]+/ | |
| 2055 | + // See rest below | |
| 2056 | + } | |
| 2057 | + }, | |
| 2058 | + // url, compassified | |
| 2059 | + 'url': /(?:[-a-z]+-)*url(?=\()/i, | |
| 2060 | + // CSS selector regex is not appropriate for Sass | |
| 2061 | + // since there can be lot more things (var, @ directive, nesting..) | |
| 2062 | + // a selector must start at the end of a property or after a brace (end of other rules or nesting) | |
| 2063 | + // it can contain some characters that aren't used for defining rules or end of selector, & (parent selector), or interpolated variable | |
| 2064 | + // the end of a selector is found when there is no rules in it ( {} or {\s}) or if there is a property (because an interpolated var | |
| 2065 | + // can "pass" as a selector- e.g: proper#{$erty}) | |
| 2066 | + // this one was hard to do, so please be careful if you edit this one :) | |
| 2067 | + 'selector': { | |
| 2068 | + // Initial look-ahead is used to prevent matching of blank selectors | |
| 2069 | + pattern: /(?=\S)[^@;{}()]?(?:[^@;{}()]|&|#\{\$[-\w]+\})+(?=\s*\{(?:\}|\s|[^}]+[:{][^}]+))/m, | |
| 2070 | + inside: { | |
| 2071 | + 'parent': { | |
| 2072 | + pattern: /&/, | |
| 2073 | + alias: 'important' | |
| 2074 | + }, | |
| 2075 | + 'placeholder': /%[-\w]+/, | |
| 2076 | + 'variable': /\$[-\w]+|#\{\$[-\w]+\}/ | |
| 2077 | + } | |
| 2078 | + } | |
| 2079 | +}); | |
| 2080 | + | |
| 2081 | +Prism.languages.insertBefore('scss', 'atrule', { | |
| 2082 | + 'keyword': [ | |
| 2083 | + /@(?:if|else(?: if)?|for|each|while|import|extend|debug|warn|mixin|include|function|return|content)/i, | |
| 2084 | + { | |
| 2085 | + pattern: /( +)(?:from|through)(?= )/, | |
| 2086 | + lookbehind: true | |
| 2087 | + } | |
| 2088 | + ] | |
| 2089 | +}); | |
| 2090 | + | |
| 2091 | +Prism.languages.scss.property = { | |
| 2092 | + pattern: /(?:[\w-]|\$[-\w]+|#\{\$[-\w]+\})+(?=\s*:)/i, | |
| 2093 | + inside: { | |
| 2094 | + 'variable': /\$[-\w]+|#\{\$[-\w]+\}/ | |
| 2095 | + } | |
| 2096 | +}; | |
| 2097 | + | |
| 2098 | +Prism.languages.insertBefore('scss', 'important', { | |
| 2099 | + // var and interpolated vars | |
| 2100 | + 'variable': /\$[-\w]+|#\{\$[-\w]+\}/ | |
| 2101 | +}); | |
| 2102 | + | |
| 2103 | +Prism.languages.insertBefore('scss', 'function', { | |
| 2104 | + 'placeholder': { | |
| 2105 | + pattern: /%[-\w]+/, | |
| 2106 | + alias: 'selector' | |
| 2107 | + }, | |
| 2108 | + 'statement': { | |
| 2109 | + pattern: /\B!(?:default|optional)\b/i, | |
| 2110 | + alias: 'keyword' | |
| 2111 | + }, | |
| 2112 | + 'boolean': /\b(?:true|false)\b/, | |
| 2113 | + 'null': /\bnull\b/, | |
| 2114 | + 'operator': { | |
| 2115 | + pattern: /(\s)(?:[-+*\/%]|[=!]=|<=?|>=?|and|or|not)(?=\s)/, | |
| 2116 | + lookbehind: true | |
| 2117 | + } | |
| 2118 | +}); | |
| 2119 | + | |
| 2120 | +Prism.languages.scss['atrule'].inside.rest = Prism.languages.scss; | |
| 2121 | +Prism.languages.python = { | |
| 2122 | + 'comment': { | |
| 1662 | 2123 | pattern: /(^|[^\\])#.*/, |
| 1663 | 2124 | lookbehind: true |
| 1664 | 2125 | }, |
| 2126 | + 'triple-quoted-string': { | |
| 2127 | + pattern: /("""|''')[\s\S]+?\1/, | |
| 2128 | + greedy: true, | |
| 2129 | + alias: 'string' | |
| 2130 | + }, | |
| 1665 | 2131 | 'string': { |
| 1666 | − pattern: /("|')(?:\\\\|\\?[^\\\r\n])*?\1/, | |
| 2132 | + pattern: /("|')(?:\\.|(?!\1)[^\\\r\n])*\1/, | |
| 1667 | 2133 | greedy: true |
| 1668 | 2134 | }, |
| 1669 | − 'function' : { | |
| 1670 | − pattern: /((?:^|\s)def[ \t]+)[a-zA-Z_][a-zA-Z0-9_]*(?=\()/g, | |
| 2135 | + 'function': { | |
| 2136 | + pattern: /((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g, | |
| 1671 | 2137 | lookbehind: true |
| 1672 | 2138 | }, |
| 1673 | 2139 | 'class-name': { |
| 1674 | − pattern: /(\bclass\s+)[a-z0-9_]+/i, | |
| 2140 | + pattern: /(\bclass\s+)\w+/i, | |
| 1675 | 2141 | lookbehind: true |
| 1676 | 2142 | }, |
| 1677 | − 'keyword' : /\b(?:as|assert|async|await|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|pass|print|raise|return|try|while|with|yield)\b/, | |
| 1678 | − 'boolean' : /\b(?:True|False)\b/, | |
| 1679 | − 'number' : /\b-?(?:0[bo])?(?:(?:\d|0x[\da-f])[\da-f]*\.?\d*|\.\d+)(?:e[+-]?\d+)?j?\b/i, | |
| 1680 | − 'operator' : /[-+%=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]|\b(?:or|and|not)\b/, | |
| 1681 | − 'punctuation' : /[{}[\];(),.:]/ | |
| 2143 | + 'keyword': /\b(?:as|assert|async|await|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|nonlocal|pass|print|raise|return|try|while|with|yield)\b/, | |
| 2144 | + 'builtin':/\b(?:__import__|abs|all|any|apply|ascii|basestring|bin|bool|buffer|bytearray|bytes|callable|chr|classmethod|cmp|coerce|compile|complex|delattr|dict|dir|divmod|enumerate|eval|execfile|file|filter|float|format|frozenset|getattr|globals|hasattr|hash|help|hex|id|input|int|intern|isinstance|issubclass|iter|len|list|locals|long|map|max|memoryview|min|next|object|oct|open|ord|pow|property|range|raw_input|reduce|reload|repr|reversed|round|set|setattr|slice|sorted|staticmethod|str|sum|super|tuple|type|unichr|unicode|vars|xrange|zip)\b/, | |
| 2145 | + 'boolean': /\b(?:True|False|None)\b/, | |
| 2146 | + 'number': /(?:\b(?=\d)|\B(?=\.))(?:0[bo])?(?:(?:\d|0x[\da-f])[\da-f]*\.?\d*|\.\d+)(?:e[+-]?\d+)?j?\b/i, | |
| 2147 | + 'operator': /[-+%=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]|\b(?:or|and|not)\b/, | |
| 2148 | + 'punctuation': /[{}[\];(),.:]/ | |
| 1682 | 2149 | }; |
| 1683 | 2150 | |
| 1684 | 2151 | (function(Prism) { |
| @@ -1686,93 +2153,134 @@ Prism.languages.python= { · 1 unmodified line | ||
| 1686 | 2153 | var javascript = Prism.util.clone(Prism.languages.javascript); |
| 1687 | 2154 | |
| 1688 | 2155 | Prism.languages.jsx = Prism.languages.extend('markup', javascript); |
| 1689 | −Prism.languages.jsx.tag.pattern= /<\/?[\w\.:-]+\s*(?:\s+(?:[\w\.:-]+(?:=(?:("|')(\\?[\s\S])*?\1|[^\s'">=]+|(\{[\s\S]*?\})))?|\{\.{3}\w+\}))*\s*\/?>/i; | |
| 2156 | +Prism.languages.jsx.tag.pattern= /<\/?(?:[\w.:-]+\s*(?:\s+(?:[\w.:-]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s{'">=]+|\{(?:\{(?:\{[^}]*\}|[^{}])*\}|[^{}])+\}))?|\{\.{3}[a-z_$][\w$]*(?:\.[a-z_$][\w$]*)*\}))*\s*\/?)?>/i; | |
| 1690 | 2157 | |
| 1691 | −Prism.languages.jsx.tag.inside['attr-value'].pattern = /=(?!\{)(?:('|")[\s\S]*?(\1)|[^\s>]+)/i; | |
| 2158 | +Prism.languages.jsx.tag.inside['tag'].pattern = /^<\/?[^\s>\/]*/i; | |
| 2159 | +Prism.languages.jsx.tag.inside['attr-value'].pattern = /=(?!\{)(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">]+)/i; | |
| 1692 | 2160 | |
| 1693 | 2161 | Prism.languages.insertBefore('inside', 'attr-name', { |
| 1694 | 2162 | 'spread': { |
| 1695 | − pattern: /\{\.{3}\w+\}/, | |
| 2163 | + pattern: /\{\.{3}[a-z_$][\w$]*(?:\.[a-z_$][\w$]*)*\}/, | |
| 1696 | 2164 | inside: { |
| 1697 | − 'punctuation': /\{|\}|\./, | |
| 2165 | + 'punctuation': /\.{3}|[{}.]/, | |
| 1698 | 2166 | 'attr-value': /\w+/ |
| 1699 | 2167 | } |
| 1700 | 2168 | } |
| 1701 | 2169 | }, Prism.languages.jsx.tag); |
| 1702 | 2170 | |
| 1703 | −var jsxExpression = Prism.util.clone(Prism.languages.jsx); | |
| 1704 | − | |
| 1705 | −delete jsxExpression.punctuation | |
| 1706 | − | |
| 1707 | −jsxExpression = Prism.languages.insertBefore('jsx', 'operator', { | |
| 1708 | − 'punctuation': /=(?={)|[{}[\];(),.:]/ | |
| 1709 | −}, { jsx: jsxExpression }); | |
| 1710 | − | |
| 1711 | 2171 | Prism.languages.insertBefore('inside', 'attr-value',{ |
| 1712 | 2172 | 'script': { |
| 1713 | − // Allow for one level of nesting | |
| 1714 | − pattern: /=(\{(?:\{[^}]*\}|[^}])+\})/i, | |
| 1715 | − inside: jsxExpression, | |
| 2173 | + // Allow for two levels of nesting | |
| 2174 | + pattern: /=(\{(?:\{(?:\{[^}]*\}|[^}])*\}|[^}])+\})/i, | |
| 2175 | + inside: { | |
| 2176 | + 'script-punctuation': { | |
| 2177 | + pattern: /^=(?={)/, | |
| 2178 | + alias: 'punctuation' | |
| 2179 | + }, | |
| 2180 | + rest: Prism.languages.jsx | |
| 2181 | + }, | |
| 1716 | 2182 | 'alias': 'language-javascript' |
| 1717 | 2183 | } |
| 1718 | 2184 | }, Prism.languages.jsx.tag); |
| 1719 | 2185 | |
| 1720 | −}(Prism)); | |
| 2186 | +// The following will handle plain text inside tags | |
| 2187 | +var stringifyToken = function (token) { | |
| 2188 | + if (!token) { | |
| 2189 | + return ''; | |
| 2190 | + } | |
| 2191 | + if (typeof token === 'string') { | |
| 2192 | + return token; | |
| 2193 | + } | |
| 2194 | + if (typeof token.content === 'string') { | |
| 2195 | + return token.content; | |
| 2196 | + } | |
| 2197 | + return token.content.map(stringifyToken).join(''); | |
| 2198 | +}; | |
| 1721 | 2199 | |
| 1722 | −(function(Prism) { | |
| 1723 | − Prism.languages.crystal = Prism.languages.extend('ruby', { | |
| 1724 | − keyword: [ | |
| 1725 | − /\b(?:abstract|alias|as|asm|begin|break|case|class|def|do|else|elsif|end|ensure|enum|extend|for|fun|if|include|instance_sizeof|lib|macro|module|next|of|out|pointerof|private|protected|rescue|return|require|select|self|sizeof|struct|super|then|type|typeof|uninitialized|union|unless|until|when|while|with|yield|__DIR__|__END_LINE__|__FILE__|__LINE__)\b/, | |
| 1726 | − { | |
| 1727 | − pattern: /(\.\s*)(?:is_a|responds_to)\?/, | |
| 1728 | − lookbehind: true | |
| 1729 | − } | |
| 1730 | − ], | |
| 2200 | +var walkTokens = function (tokens) { | |
| 2201 | + var openedTags = []; | |
| 2202 | + for (var i = 0; i < tokens.length; i++) { | |
| 2203 | + var token = tokens[i]; | |
| 2204 | + var notTagNorBrace = false; | |
| 2205 | + | |
| 2206 | + if (typeof token !== 'string') { | |
| 2207 | + if (token.type === 'tag' && token.content[0] && token.content[0].type === 'tag') { | |
| 2208 | + // We found a tag, now find its kind | |
| 2209 | + | |
| 2210 | + if (token.content[0].content[0].content === '</') { | |
| 2211 | + // Closing tag | |
| 2212 | + if (openedTags.length > 0 && openedTags[openedTags.length - 1].tagName === stringifyToken(token.content[0].content[1])) { | |
| 2213 | + // Pop matching opening tag | |
| 2214 | + openedTags.pop(); | |
| 2215 | + } | |
| 2216 | + } else { | |
| 2217 | + if (token.content[token.content.length - 1].content === '/>') { | |
| 2218 | + // Autoclosed tag, ignore | |
| 2219 | + } else { | |
| 2220 | + // Opening tag | |
| 2221 | + openedTags.push({ | |
| 2222 | + tagName: stringifyToken(token.content[0].content[1]), | |
| 2223 | + openedBraces: 0 | |
| 2224 | + }); | |
| 2225 | + } | |
| 2226 | + } | |
| 2227 | + } else if (openedTags.length > 0 && token.type === 'punctuation' && token.content === '{') { | |
| 1731 | 2228 | |
| 1732 | − number: /\b(?:0b[01_]*[01]|0o[0-7_]*[0-7]|0x[0-9a-fA-F_]*[0-9a-fA-F]|(?:\d(?:[0-9_]*\d)?)(?:\.[0-9_]*\d)?(?:[eE][+-]?[0-9_]*\d)?)(?:_(?:[uif](?:8|16|32|64))?)?\b/, | |
| 1733 | − }); | |
| 2229 | + // Here we might have entered a JSX context inside a tag | |
| 2230 | + openedTags[openedTags.length - 1].openedBraces++; | |
| 1734 | 2231 | |
| 1735 | − var rest = Prism.util.clone(Prism.languages.crystal); | |
| 2232 | + } else if (openedTags.length > 0 && openedTags[openedTags.length - 1].openedBraces > 0 && token.type === 'punctuation' && token.content === '}') { | |
| 1736 | 2233 | |
| 1737 | − Prism.languages.insertBefore('crystal', 'string', { | |
| 1738 | − attribute: { | |
| 1739 | − pattern: /@\[.+?\]/, | |
| 1740 | − alias: 'attr-name', | |
| 1741 | − inside: { | |
| 1742 | − delimiter: { | |
| 1743 | − pattern: /^@\[|\]$/, | |
| 1744 | − alias: 'tag' | |
| 1745 | − }, | |
| 1746 | − rest: rest | |
| 1747 | − } | |
| 1748 | − }, | |
| 2234 | + // Here we might have left a JSX context inside a tag | |
| 2235 | + openedTags[openedTags.length - 1].openedBraces--; | |
| 1749 | 2236 | |
| 1750 | − expansion: [ | |
| 1751 | − { | |
| 1752 | − pattern: /\{\{.+?\}\}/, | |
| 1753 | − inside: { | |
| 1754 | − delimiter: { | |
| 1755 | − pattern: /^\{\{|\}\}$/, | |
| 1756 | − alias: 'tag' | |
| 1757 | − }, | |
| 1758 | − rest: rest | |
| 2237 | + } else { | |
| 2238 | + notTagNorBrace = true | |
| 1759 | 2239 | } |
| 1760 | − }, | |
| 1761 | − { | |
| 1762 | − pattern: /\{%.+?%\}/, | |
| 1763 | − inside: { | |
| 1764 | − delimiter: { | |
| 1765 | − pattern: /^\{%|%\}$/, | |
| 1766 | − alias: 'tag' | |
| 1767 | − }, | |
| 1768 | − rest: rest | |
| 2240 | + } | |
| 2241 | + if (notTagNorBrace || typeof token === 'string') { | |
| 2242 | + if (openedTags.length > 0 && openedTags[openedTags.length - 1].openedBraces === 0) { | |
| 2243 | + // Here we are inside a tag, and not inside a JSX context. | |
| 2244 | + // That's plain text: drop any tokens matched. | |
| 2245 | + var plainText = stringifyToken(token); | |
| 2246 | + | |
| 2247 | + // And merge text with adjacent text | |
| 2248 | + if (i < tokens.length - 1 && (typeof tokens[i + 1] === 'string' || tokens[i + 1].type === 'plain-text')) { | |
| 2249 | + plainText += stringifyToken(tokens[i + 1]); | |
| 2250 | + tokens.splice(i + 1, 1); | |
| 2251 | + } | |
| 2252 | + if (i > 0 && (typeof tokens[i - 1] === 'string' || tokens[i - 1].type === 'plain-text')) { | |
| 2253 | + plainText = stringifyToken(tokens[i - 1]) + plainText; | |
| 2254 | + tokens.splice(i - 1, 1); | |
| 2255 | + i--; | |
| 2256 | + } | |
| 2257 | + | |
| 2258 | + tokens[i] = new Prism.Token('plain-text', plainText, null, plainText); | |
| 1769 | 2259 | } |
| 1770 | 2260 | } |
| 1771 | − ] | |
| 1772 | − }); | |
| 2261 | + | |
| 2262 | + if (token.content && typeof token.content !== 'string') { | |
| 2263 | + walkTokens(token.content); | |
| 2264 | + } | |
| 2265 | + } | |
| 2266 | +}; | |
| 2267 | + | |
| 2268 | +Prism.hooks.add('after-tokenize', function (env) { | |
| 2269 | + if (env.language !== 'jsx' && env.language !== 'tsx') { | |
| 2270 | + return; | |
| 2271 | + } | |
| 2272 | + walkTokens(env.tokens); | |
| 2273 | +}); | |
| 1773 | 2274 | |
| 1774 | 2275 | }(Prism)); |
| 1775 | 2276 | |
| 2277 | +Prism.languages.typescript = Prism.languages.extend('javascript', { | |
| 2278 | + // From JavaScript Prism keyword list and TypeScript language spec: https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#221-reserved-words | |
| 2279 | + 'keyword': /\b(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield|module|declare|constructor|namespace|abstract|require|type)\b/, | |
| 2280 | + 'builtin': /\b(?:string|Function|any|number|boolean|Array|symbol|console)\b/, | |
| 2281 | +}); | |
| 2282 | + | |
| 2283 | +Prism.languages.ts = Prism.languages.typescript; | |
| 1776 | 2284 | /* TODO |
| 1777 | 2285 | Add support for Markdown notation inside doc comments |
| 1778 | 2286 | Add support for nested block comments... |
| @@ -1793,14 +2301,22 @@ Prism.languages.rust = { · 14 unmodified lines | ||
| 1793 | 2301 | ], |
| 1794 | 2302 | 'string': [ |
| 1795 | 2303 | { |
| 1796 | − pattern: /b?r(#*)"(?:\\?.)*?"\1/, | |
| 2304 | + pattern: /b?r(#*)"(?:\\.|(?!"\1)[^\\\r\n])*"\1/, | |
| 1797 | 2305 | greedy: true |
| 1798 | 2306 | }, |
| 1799 | 2307 | { |
| 1800 | − pattern: /b?("|')(?:\\?.)*?\1/, | |
| 2308 | + pattern: /b?"(?:\\.|[^\\\r\n"])*"/, | |
| 1801 | 2309 | greedy: true |
| 1802 | 2310 | } |
| 1803 | 2311 | ], |
| 2312 | + 'char': { | |
| 2313 | + pattern: /b?'(?:\\(?:x[0-7][\da-fA-F]|u{(?:[\da-fA-F]_*){1,6}|.)|[^\\\r\n\t'])'/, | |
| 2314 | + alias: 'string' | |
| 2315 | + }, | |
| 2316 | + 'lifetime-annotation': { | |
| 2317 | + pattern: /'[^\s>']+/, | |
| 2318 | + alias: 'symbol' | |
| 2319 | + }, | |
| 1804 | 2320 | 'keyword': /\b(?:abstract|alignof|as|be|box|break|const|continue|crate|do|else|enum|extern|false|final|fn|for|if|impl|in|let|loop|match|mod|move|mut|offsetof|once|override|priv|pub|pure|ref|return|sizeof|static|self|struct|super|true|trait|type|typeof|unsafe|unsized|use|virtual|where|while|yield)\b/, |
| 1805 | 2321 | |
| 1806 | 2322 | 'attribute': { |
| @@ -1810,125 +2326,71 @@ Prism.languages.rust = { · 3 unmodified lines | ||
| 1810 | 2326 | }, |
| 1811 | 2327 | |
| 1812 | 2328 | 'function': [ |
| 1813 | − /[a-z0-9_]+(?=\s*\()/i, | |
| 2329 | + /\w+(?=\s*\()/, | |
| 1814 | 2330 | // Macros can use parens or brackets |
| 1815 | − /[a-z0-9_]+!(?=\s*\(|\[)/i | |
| 2331 | + /\w+!(?=\s*\(|\[)/ | |
| 1816 | 2332 | ], |
| 1817 | 2333 | 'macro-rules': { |
| 1818 | − pattern: /[a-z0-9_]+!/i, | |
| 2334 | + pattern: /\w+!/, | |
| 1819 | 2335 | alias: 'function' |
| 1820 | 2336 | }, |
| 1821 | 2337 | |
| 1822 | 2338 | // Hex, oct, bin, dec numbers with visual separators and type suffix |
| 1823 | − 'number': /\b-?(?:0x[\dA-Fa-f](?:_?[\dA-Fa-f])*|0o[0-7](?:_?[0-7])*|0b[01](?:_?[01])*|(\d(_?\d)*)?\.?\d(_?\d)*([Ee][+-]?\d+)?)(?:_?(?:[iu](?:8|16|32|64)?|f32|f64))?\b/, | |
| 2339 | + 'number': /\b(?:0x[\dA-Fa-f](?:_?[\dA-Fa-f])*|0o[0-7](?:_?[0-7])*|0b[01](?:_?[01])*|(\d(?:_?\d)*)?\.?\d(?:_?\d)*(?:[Ee][+-]?\d+)?)(?:_?(?:[iu](?:8|16|32|64)?|f32|f64))?\b/, | |
| 1824 | 2340 | |
| 1825 | 2341 | // Closure params should not be confused with bitwise OR | |
| 1826 | 2342 | 'closure-params': { |
| 1827 | 2343 | pattern: /\|[^|]*\|(?=\s*[{-])/, |
| 1828 | 2344 | inside: { |
| 1829 | − 'punctuation': /[\|:,]/, | |
| 2345 | + 'punctuation': /[|:,]/, | |
| 1830 | 2346 | 'operator': /[&*]/ |
| 1831 | 2347 | } |
| 1832 | 2348 | }, |
| 1833 | 2349 | 'punctuation': /[{}[\];(),:]|\.+|->/, |
| 1834 | − 'operator': /[-+*\/%!^=]=?|@|&[&=]?|\|[|=]?|<<?=?|>>?=?/ | |
| 2350 | + 'operator': /[-+*\/%!^]=?|=[=>]?|@|&[&=]?|\|[|=]?|<<?=?|>>?=?/ | |
| 1835 | 2351 | }; |
| 1836 | −Prism.languages.scss = Prism.languages.extend('css', { | |
| 1837 | − 'comment': { | |
| 1838 | − pattern: /(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/, | |
| 1839 | − lookbehind: true | |
| 2352 | +Prism.languages.yaml = { | |
| 2353 | + 'scalar': { | |
| 2354 | + pattern: /([\-:]\s*(?:![^\s]+)?[ \t]*[|>])[ \t]*(?:((?:\r?\n|\r)[ \t]+)[^\r\n]+(?:\2[^\r\n]+)*)/, | |
| 2355 | + lookbehind: true, | |
| 2356 | + alias: 'string' | |
| 1840 | 2357 | }, |
| 1841 | − 'atrule': { | |
| 1842 | − pattern: /@[\w-]+(?:\([^()]+\)|[^(])*?(?=\s+[{;])/, | |
| 1843 | − inside: { | |
| 1844 | − 'rule': /@[\w-]+/ | |
| 1845 | − // See rest below | |
| 1846 | − } | |
| 2358 | + 'comment': /#.*/, | |
| 2359 | + 'key': { | |
| 2360 | + pattern: /(\s*(?:^|[:\-,[{\r\n?])[ \t]*(?:![^\s]+)?[ \t]*)[^\r\n{[\]},#\s]+?(?=\s*:\s)/, | |
| 2361 | + lookbehind: true, | |
| 2362 | + alias: 'atrule' | |
| 1847 | 2363 | }, |
| 1848 | − // url, compassified | |
| 1849 | − 'url': /(?:[-a-z]+-)*url(?=\()/i, | |
| 1850 | − // CSS selector regex is not appropriate for Sass | |
| 1851 | − // since there can be lot more things (var, @ directive, nesting..) | |
| 1852 | − // a selector must start at the end of a property or after a brace (end of other rules or nesting) | |
| 1853 | − // it can contain some characters that aren't used for defining rules or end of selector, & (parent selector), or interpolated variable | |
| 1854 | − // the end of a selector is found when there is no rules in it ( {} or {\s}) or if there is a property (because an interpolated var | |
| 1855 | − // can "pass" as a selector- e.g: proper#{$erty}) | |
| 1856 | − // this one was hard to do, so please be careful if you edit this one :) | |
| 1857 | − 'selector': { | |
| 1858 | − // Initial look-ahead is used to prevent matching of blank selectors | |
| 1859 | − pattern: /(?=\S)[^@;\{\}\(\)]?([^@;\{\}\(\)]|&|#\{\$[-_\w]+\})+(?=\s*\{(\}|\s|[^\}]+(:|\{)[^\}]+))/m, | |
| 1860 | − inside: { | |
| 1861 | − 'parent': { | |
| 1862 | − pattern: /&/, | |
| 1863 | − alias: 'important' | |
| 1864 | − }, | |
| 1865 | − 'placeholder': /%[-_\w]+/, | |
| 1866 | − 'variable': /\$[-_\w]+|#\{\$[-_\w]+\}/ | |
| 1867 | − } | |
| 1868 | − } | |
| 1869 | −}); | |
| 1870 | − | |
| 1871 | −Prism.languages.insertBefore('scss', 'atrule', { | |
| 1872 | − 'keyword': [ | |
| 1873 | − /@(?:if|else(?: if)?|for|each|while|import|extend|debug|warn|mixin|include|function|return|content)/i, | |
| 1874 | − { | |
| 1875 | − pattern: /( +)(?:from|through)(?= )/, | |
| 1876 | − lookbehind: true | |
| 1877 | − } | |
| 1878 | − ] | |
| 1879 | −}); | |
| 1880 | − | |
| 1881 | −Prism.languages.scss.property = { | |
| 1882 | − pattern: /(?:[\w-]|\$[-_\w]+|#\{\$[-_\w]+\})+(?=\s*:)/i, | |
| 1883 | − inside: { | |
| 1884 | − 'variable': /\$[-_\w]+|#\{\$[-_\w]+\}/ | |
| 1885 | − } | |
| 1886 | −}; | |
| 1887 | − | |
| 1888 | −Prism.languages.insertBefore('scss', 'important', { | |
| 1889 | − // var and interpolated vars | |
| 1890 | − 'variable': /\$[-_\w]+|#\{\$[-_\w]+\}/ | |
| 1891 | −}); | |
| 1892 | − | |
| 1893 | −Prism.languages.insertBefore('scss', 'function', { | |
| 1894 | − 'placeholder': { | |
| 1895 | − pattern: /%[-_\w]+/, | |
| 1896 | − alias: 'selector' | |
| 2364 | + 'directive': { | |
| 2365 | + pattern: /(^[ \t]*)%.+/m, | |
| 2366 | + lookbehind: true, | |
| 2367 | + alias: 'important' | |
| 1897 | 2368 | }, |
| 1898 | − 'statement': { | |
| 1899 | − pattern: /\B!(?:default|optional)\b/i, | |
| 1900 | − alias: 'keyword' | |
| 2369 | + 'datetime': { | |
| 2370 | + pattern: /([:\-,[{]\s*(?:![^\s]+)?[ \t]*)(?:\d{4}-\d\d?-\d\d?(?:[tT]|[ \t]+)\d\d?:\d{2}:\d{2}(?:\.\d*)?[ \t]*(?:Z|[-+]\d\d?(?::\d{2})?)?|\d{4}-\d{2}-\d{2}|\d\d?:\d{2}(?::\d{2}(?:\.\d*)?)?)(?=[ \t]*(?:$|,|]|}))/m, | |
| 2371 | + lookbehind: true, | |
| 2372 | + alias: 'number' | |
| 1901 | 2373 | }, |
| 1902 | − 'boolean': /\b(?:true|false)\b/, | |
| 1903 | − 'null': /\bnull\b/, | |
| 1904 | − 'operator': { | |
| 1905 | − pattern: /(\s)(?:[-+*\/%]|[=!]=|<=?|>=?|and|or|not)(?=\s)/, | |
| 1906 | − lookbehind: true | |
| 1907 | − } | |
| 1908 | −}); | |
| 1909 | − | |
| 1910 | −Prism.languages.scss['atrule'].inside.rest = Prism.util.clone(Prism.languages.scss); | |
| 1911 | −Prism.languages.sql= { | |
| 1912 | − 'comment': { | |
| 1913 | − pattern: /(^|[^\\])(?:\/\*[\s\S]*?\*\/|(?:--|\/\/|#).*)/, | |
| 1914 | − lookbehind: true | |
| 2374 | + 'boolean': { | |
| 2375 | + pattern: /([:\-,[{]\s*(?:![^\s]+)?[ \t]*)(?:true|false)[ \t]*(?=$|,|]|})/im, | |
| 2376 | + lookbehind: true, | |
| 2377 | + alias: 'important' | |
| 1915 | 2378 | }, |
| 1916 | − 'string' : { | |
| 1917 | − pattern: /(^|[^@\\])("|')(?:\\?[\s\S])*?\2/, | |
| 1918 | − greedy: true, | |
| 2379 | + 'null': { | |
| 2380 | + pattern: /([:\-,[{]\s*(?:![^\s]+)?[ \t]*)(?:null|~)[ \t]*(?=$|,|]|})/im, | |
| 2381 | + lookbehind: true, | |
| 2382 | + alias: 'important' | |
| 2383 | + }, | |
| 2384 | + 'string': { | |
| 2385 | + pattern: /([:\-,[{]\s*(?:![^\s]+)?[ \t]*)("|')(?:(?!\2)[^\\\r\n]|\\.)*\2(?=[ \t]*(?:$|,|]|}))/m, | |
| 2386 | + lookbehind: true, | |
| 2387 | + greedy: true | |
| 2388 | + }, | |
| 2389 | + 'number': { | |
| 2390 | + pattern: /([:\-,[{]\s*(?:![^\s]+)?[ \t]*)[+-]?(?:0x[\da-f]+|0o[0-7]+|(?:\d+\.?\d*|\.?\d+)(?:e[+-]?\d+)?|\.inf|\.nan)[ \t]*(?=$|,|]|})/im, | |
| 1919 | 2391 | lookbehind: true |
| 1920 | 2392 | }, |
| 1921 | − 'variable': /@[\w.$]+|@("|'|`)(?:\\?[\s\S])+?\1/, | |
| 1922 | − 'function': /\b(?:COUNT|SUM|AVG|MIN|MAX|FIRST|LAST|UCASE|LCASE|MID|LEN|ROUND|NOW|FORMAT)(?=\s*\()/i, // Should we highlight user defined functions too? | |
| 1923 | − 'keyword': /\b(?:ACTION|ADD|AFTER|ALGORITHM|ALL|ALTER|ANALYZE|ANY|APPLY|AS|ASC|AUTHORIZATION|AUTO_INCREMENT|BACKUP|BDB|BEGIN|BERKELEYDB|BIGINT|BINARY|BIT|BLOB|BOOL|BOOLEAN|BREAK|BROWSE|BTREE|BULK|BY|CALL|CASCADED?|CASE|CHAIN|CHAR VARYING|CHARACTER (?:SET|VARYING)|CHARSET|CHECK|CHECKPOINT|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMN|COLUMNS|COMMENT|COMMIT|COMMITTED|COMPUTE|CONNECT|CONSISTENT|CONSTRAINT|CONTAINS|CONTAINSTABLE|CONTINUE|CONVERT|CREATE|CROSS|CURRENT(?:_DATE|_TIME|_TIMESTAMP|_USER)?|CURSOR|DATA(?:BASES?)?|DATE(?:TIME)?|DBCC|DEALLOCATE|DEC|DECIMAL|DECLARE|DEFAULT|DEFINER|DELAYED|DELETE|DELIMITER(?:S)?|DENY|DESC|DESCRIBE|DETERMINISTIC|DISABLE|DISCARD|DISK|DISTINCT|DISTINCTROW|DISTRIBUTED|DO|DOUBLE(?: PRECISION)?|DROP|DUMMY|DUMP(?:FILE)?|DUPLICATE KEY|ELSE|ENABLE|ENCLOSED BY|END|ENGINE|ENUM|ERRLVL|ERRORS|ESCAPE(?:D BY)?|EXCEPT|EXEC(?:UTE)?|EXISTS|EXIT|EXPLAIN|EXTENDED|FETCH|FIELDS|FILE|FILLFACTOR|FIRST|FIXED|FLOAT|FOLLOWING|FOR(?: EACH ROW)?|FORCE|FOREIGN|FREETEXT(?:TABLE)?|FROM|FULL|FUNCTION|GEOMETRY(?:COLLECTION)?|GLOBAL|GOTO|GRANT|GROUP|HANDLER|HASH|HAVING|HOLDLOCK|IDENTITY(?:_INSERT|COL)?|IF|IGNORE|IMPORT|INDEX|INFILE|INNER|INNODB|INOUT|INSERT|INT|INTEGER|INTERSECT|INTO|INVOKER|ISOLATION LEVEL|JOIN|KEYS?|KILL|LANGUAGE SQL|LAST|LEFT|LIMIT|LINENO|LINES|LINESTRING|LOAD|LOCAL|LOCK|LONG(?:BLOB|TEXT)|MATCH(?:ED)?|MEDIUM(?:BLOB|INT|TEXT)|MERGE|MIDDLEINT|MODIFIES SQL DATA|MODIFY|MULTI(?:LINESTRING|POINT|POLYGON)|NATIONAL(?: CHAR VARYING| CHARACTER(?: VARYING)?| VARCHAR)?|NATURAL|NCHAR(?: VARCHAR)?|NEXT|NO(?: SQL|CHECK|CYCLE)?|NONCLUSTERED|NULLIF|NUMERIC|OFF?|OFFSETS?|ON|OPEN(?:DATASOURCE|QUERY|ROWSET)?|OPTIMIZE|OPTION(?:ALLY)?|ORDER|OUT(?:ER|FILE)?|OVER|PARTIAL|PARTITION|PERCENT|PIVOT|PLAN|POINT|POLYGON|PRECEDING|PRECISION|PREV|PRIMARY|PRINT|PRIVILEGES|PROC(?:EDURE)?|PUBLIC|PURGE|QUICK|RAISERROR|READ(?:S SQL DATA|TEXT)?|REAL|RECONFIGURE|REFERENCES|RELEASE|RENAME|REPEATABLE|REPLICATION|REQUIRE|RESTORE|RESTRICT|RETURNS?|REVOKE|RIGHT|ROLLBACK|ROUTINE|ROW(?:COUNT|GUIDCOL|S)?|RTREE|RULE|SAVE(?:POINT)?|SCHEMA|SELECT|SERIAL(?:IZABLE)?|SESSION(?:_USER)?|SET(?:USER)?|SHARE MODE|SHOW|SHUTDOWN|SIMPLE|SMALLINT|SNAPSHOT|SOME|SONAME|START(?:ING BY)?|STATISTICS|STATUS|STRIPED|SYSTEM_USER|TABLES?|TABLESPACE|TEMP(?:ORARY|TABLE)?|TERMINATED BY|TEXT(?:SIZE)?|THEN|TIMESTAMP|TINY(?:BLOB|INT|TEXT)|TOP?|TRAN(?:SACTIONS?)?|TRIGGER|TRUNCATE|TSEQUAL|TYPES?|UNBOUNDED|UNCOMMITTED|UNDEFINED|UNION|UNIQUE|UNPIVOT|UPDATE(?:TEXT)?|USAGE|USE|USER|USING|VALUES?|VAR(?:BINARY|CHAR|CHARACTER|YING)|VIEW|WAITFOR|WARNINGS|WHEN|WHERE|WHILE|WITH(?: ROLLUP|IN)?|WORK|WRITE(?:TEXT)?)\b/i, | |
| 1924 | − 'boolean': /\b(?:TRUE|FALSE|NULL)\b/i, | |
| 1925 | − 'number': /\b-?(?:0x)?\d*\.?[\da-f]+\b/, | |
| 1926 | − 'operator': /[-+*\/=%^~]|&&?|\|?\||!=?|<(?:=>?|<|>)?|>[>=]?|\b(?:AND|BETWEEN|IN|LIKE|NOT|OR|IS|DIV|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b/i, | |
| 1927 | − 'punctuation': /[;[\]()`,.]/ | |
| 2393 | + 'tag': /![^\s]+/, | |
| 2394 | + 'important': /[&*][\w]+/, | |
| 2395 | + 'punctuation': /---|[:[\]{}\-,|>?]|\.\.\./ | |
| 1928 | 2396 | }; |
| 1929 | −Prism.languages.typescript = Prism.languages.extend('javascript', { | |
| 1930 | − // From JavaScript Prism keyword list and TypeScript language spec: https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#221-reserved-words | |
| 1931 | − 'keyword': /\b(as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield|false|true|module|declare|constructor|string|Function|any|number|boolean|Array|enum|symbol|namespace|abstract|require|type)\b/ | |
| 1932 | −}); | |
| 1933 | − | |
| 1934 | −Prism.languages.ts = Prism.languages.typescript; | |
| @@ -1,4 +1,4 @@ | ||
| 1 | −/*! Raven.js 3.18.0 (b771bb2) | github.com/getsentry/raven-js */ | |
| 1 | +/*! Raven.js 3.20.1 (42adaf5) | github.com/getsentry/raven-js */ | |
| 2 | 2 | |
| 3 | 3 | /* |
| 4 | 4 | * Includes TraceKit |
| @@ -10,322 +10,270 @@ · 5 unmodified lines | ||
| 10 | 10 | * |
| 11 | 11 | */ |
| 12 | 12 | |
| 13 | −(function(f) { | |
| 14 | − if (typeof exports === 'object' && typeof module !== 'undefined') { | |
| 15 | − module.exports = f(); | |
| 16 | − } else if (typeof define === 'function' && define.amd) { | |
| 17 | − define([], f); | |
| 18 | − } else { | |
| 19 | − var g; | |
| 20 | − if (typeof window !== 'undefined') { | |
| 21 | − g = window; | |
| 22 | − } else if (typeof global !== 'undefined') { | |
| 23 | − g = global; | |
| 24 | − } else if (typeof self !== 'undefined') { | |
| 25 | − g = self; | |
| 26 | − } else { | |
| 27 | − g = this; | |
| 28 | − } | |
| 29 | − g.Raven = f(); | |
| 30 | − } | |
| 31 | −})(function() { | |
| 32 | − var define, module, exports; | |
| 33 | − return (function e(t, n, r) { | |
| 34 | − function s(o, u) { | |
| 35 | − if (!n[o]) { | |
| 36 | − if (!t[o]) { | |
| 37 | − var a = typeof require == 'function' && require; | |
| 38 | − if (!u && a) return a(o, !0); | |
| 39 | − if (i) return i(o, !0); | |
| 40 | − var f = new Error("Cannot find module '" + o + "'"); | |
| 41 | − throw ((f.code = 'MODULE_NOT_FOUND'), f); | |
| 42 | − } | |
| 43 | − var l = (n[o] = {exports: {}}); | |
| 44 | − t[o][0].call( | |
| 45 | − l.exports, | |
| 46 | − function(e) { | |
| 47 | − var n = t[o][1][e]; | |
| 48 | − return s(n ? n : e); | |
| 49 | − }, | |
| 50 | − l, | |
| 51 | − l.exports, | |
| 52 | − e, | |
| 53 | − t, | |
| 54 | − n, | |
| 55 | − r | |
| 56 | − ); | |
| 57 | − } | |
| 58 | − return n[o].exports; | |
| 59 | − } | |
| 60 | − var i = typeof require == 'function' && require; | |
| 61 | − for (var o = 0; o < r.length; o++) s(r[o]); | |
| 62 | − return s; | |
| 63 | − })( | |
| 64 | − { | |
| 65 | − 1: [ | |
| 66 | − function(_dereq_, module, exports) { | |
| 67 | − function RavenConfigError(message) { | |
| 68 | − this.name = 'RavenConfigError'; | |
| 69 | − this.message = message; | |
| 70 | − } | |
| 71 | − RavenConfigError.prototype = new Error(); | |
| 72 | − RavenConfigError.prototype.constructor = RavenConfigError; | |
| 73 | − | |
| 74 | − module.exports = RavenConfigError; | |
| 75 | − }, | |
| 76 | − {} | |
| 77 | − ], | |
| 78 | − 2: [ | |
| 79 | − function(_dereq_, module, exports) { | |
| 80 | − var wrapMethod = function(console, level, callback) { | |
| 81 | − var originalConsoleLevel = console[level]; | |
| 82 | − var originalConsole = console; | |
| 83 | − | |
| 84 | − if (!(level in console)) { | |
| 85 | − return; | |
| 86 | − } | |
| 13 | +(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Raven = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(_dereq_,module,exports){ | |
| 14 | +function RavenConfigError(message) { | |
| 15 | + this.name = 'RavenConfigError'; | |
| 16 | + this.message = message; | |
| 17 | +} | |
| 18 | +RavenConfigError.prototype = new Error(); | |
| 19 | +RavenConfigError.prototype.constructor = RavenConfigError; | |
| 87 | 20 | |
| 88 | − var sentryLevel = level === 'warn' ? 'warning' : level; | |
| 89 | − | |
| 90 | − console[level] = function() { | |
| 91 | − var args = [].slice.call(arguments); | |
| 92 | − | |
| 93 | − var msg = '' + args.join(' '); | |
| 94 | − var data = { | |
| 95 | − level: sentryLevel, | |
| 96 | − logger: 'console', | |
| 97 | − extra: {arguments: args} | |
| 98 | − }; | |
| 99 | − | |
| 100 | − if (level === 'assert') { | |
| 101 | − if (args[0] === false) { | |
| 102 | − // Default browsers message | |
| 103 | − msg = | |
| 104 | − 'Assertion failed: ' + (args.slice(1).join(' ') || 'console.assert'); | |
| 105 | − data.extra.arguments = args.slice(1); | |
| 106 | − callback && callback(msg, data); | |
| 107 | − } | |
| 108 | − } else { | |
| 109 | − callback && callback(msg, data); | |
| 110 | − } | |
| 111 | − | |
| 112 | − // this fails for some browsers. :( | |
| 113 | − if (originalConsoleLevel) { | |
| 114 | − // IE9 doesn't allow calling apply on console functions directly | |
| 115 | − // See: https://stackoverflow.com/questions/5472938/does-ie9-support-console-log-and-is-it-a-real-function#answer-5473193 | |
| 116 | − Function.prototype.apply.call( | |
| 117 | − originalConsoleLevel, | |
| 118 | − originalConsole, | |
| 119 | − args | |
| 120 | − ); | |
| 121 | − } | |
| 122 | − }; | |
| 123 | − }; | |
| 124 | − | |
| 125 | − module.exports = { | |
| 126 | − wrapMethod: wrapMethod | |
| 127 | − }; | |
| 128 | − }, | |
| 129 | − {} | |
| 130 | − ], | |
| 131 | − 3: [ | |
| 132 | − function(_dereq_, module, exports) { | |
| 133 | − (function(global) { | |
| 134 | − /*global XDomainRequest:false, __DEV__:false*/ | |
| 21 | +module.exports = RavenConfigError; | |
| 135 | 22 | |
| 136 | − var TraceKit = _dereq_(6); | |
| 137 | − var stringify = _dereq_(7); | |
| 138 | − var RavenConfigError = _dereq_(1); | |
| 139 | − var utils = _dereq_(5); | |
| 23 | +},{}],2:[function(_dereq_,module,exports){ | |
| 24 | +var wrapMethod = function(console, level, callback) { | |
| 25 | + var originalConsoleLevel = console[level]; | |
| 26 | + var originalConsole = console; | |
| 140 | 27 | |
| 141 | − var isError = utils.isError, | |
| 142 | − isObject = utils.isObject; | |
| 28 | + if (!(level in console)) { | |
| 29 | + return; | |
| 30 | + } | |
| 143 | 31 | |
| 144 | − var wrapConsoleMethod = _dereq_(2).wrapMethod; | |
| 32 | + var sentryLevel = level === 'warn' ? 'warning' : level; | |
| 145 | 33 | |
| 146 | − var dsnKeys = 'source protocol user pass host port path'.split(' '), | |
| 147 | − dsnPattern = /^(?:(\w+):)?\/\/(?:(\w+)(:\w+)?@)?([\w\.-]+)(?::(\d+))?(\/.*)/; | |
| 34 | + console[level] = function() { | |
| 35 | + var args = [].slice.call(arguments); | |
| 148 | 36 | |
| 149 | − function now() { | |
| 150 | − return +new Date(); | |
| 151 | − } | |
| 37 | + var msg = '' + args.join(' '); | |
| 38 | + var data = {level: sentryLevel, logger: 'console', extra: {arguments: args}}; | |
| 152 | 39 | |
| 153 | − // This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785) | |
| 154 | − var _window = | |
| 155 | − typeof window !== 'undefined' | |
| 156 | − ? window | |
| 157 | − : typeof global !== 'undefined' | |
| 158 | − ? global | |
| 159 | − : typeof self !== 'undefined' ? self : {}; | |
| 160 | − var _document = _window.document; | |
| 161 | − var _navigator = _window.navigator; | |
| 162 | − | |
| 163 | − function keepOriginalCallback(original, callback) { | |
| 164 | − return isFunction(callback) | |
| 165 | − ? function(data) { | |
| 166 | − return callback(data, original); | |
| 167 | − } | |
| 168 | − : callback; | |
| 169 | − } | |
| 40 | + if (level === 'assert') { | |
| 41 | + if (args[0] === false) { | |
| 42 | + // Default browsers message | |
| 43 | + msg = 'Assertion failed: ' + (args.slice(1).join(' ') || 'console.assert'); | |
| 44 | + data.extra.arguments = args.slice(1); | |
| 45 | + callback && callback(msg, data); | |
| 46 | + } | |
| 47 | + } else { | |
| 48 | + callback && callback(msg, data); | |
| 49 | + } | |
| 170 | 50 | |
| 171 | − // First, check for JSON support | |
| 172 | − // If there is no JSON, we no-op the core features of Raven | |
| 173 | − // since JSON is required to encode the payload | |
| 174 | − function Raven() { | |
| 175 | − this._hasJSON = !!(typeof JSON === 'object' && JSON.stringify); | |
| 176 | − // Raven can run in contexts where there's no document (react-native) | |
| 177 | − this._hasDocument = !isUndefined(_document); | |
| 178 | − this._hasNavigator = !isUndefined(_navigator); | |
| 179 | − this._lastCapturedException = null; | |
| 180 | − this._lastData = null; | |
| 181 | − this._lastEventId = null; | |
| 182 | − this._globalServer = null; | |
| 183 | − this._globalKey = null; | |
| 184 | − this._globalProject = null; | |
| 185 | − this._globalContext = {}; | |
| 186 | − this._globalOptions = { | |
| 187 | − logger: 'javascript', | |
| 188 | − ignoreErrors: [], | |
| 189 | − ignoreUrls: [], | |
| 190 | − whitelistUrls: [], | |
| 191 | − includePaths: [], | |
| 192 | − collectWindowErrors: true, | |
| 193 | − maxMessageLength: 0, | |
| 194 | − | |
| 195 | − // By default, truncates URL values to 250 chars | |
| 196 | − maxUrlLength: 250, | |
| 197 | − stackTraceLimit: 50, | |
| 198 | − autoBreadcrumbs: true, | |
| 199 | − instrument: true, | |
| 200 | − sampleRate: 1 | |
| 201 | − }; | |
| 202 | − this._ignoreOnError = 0; | |
| 203 | − this._isRavenInstalled = false; | |
| 204 | − this._originalErrorStackTraceLimit = Error.stackTraceLimit; | |
| 205 | − // capture references to window.console *and* all its methods first | |
| 206 | − // before the console plugin has a chance to monkey patch | |
| 207 | − this._originalConsole = _window.console || {}; | |
| 208 | − this._originalConsoleMethods = {}; | |
| 209 | − this._plugins = []; | |
| 210 | − this._startTime = now(); | |
| 211 | − this._wrappedBuiltIns = []; | |
| 212 | − this._breadcrumbs = []; | |
| 213 | − this._lastCapturedEvent = null; | |
| 214 | − this._keypressTimeout; | |
| 215 | − this._location = _window.location; | |
| 216 | − this._lastHref = this._location && this._location.href; | |
| 217 | − this._resetBackoff(); | |
| 218 | − | |
| 219 | − // eslint-disable-next-line guard-for-in | |
| 220 | − for (var method in this._originalConsole) { | |
| 221 | − this._originalConsoleMethods[method] = this._originalConsole[method]; | |
| 222 | − } | |
| 223 | − } | |
| 51 | + // this fails for some browsers. :( | |
| 52 | + if (originalConsoleLevel) { | |
| 53 | + // IE9 doesn't allow calling apply on console functions directly | |
| 54 | + // See: https://stackoverflow.com/questions/5472938/does-ie9-support-console-log-and-is-it-a-real-function#answer-5473193 | |
| 55 | + Function.prototype.apply.call(originalConsoleLevel, originalConsole, args); | |
| 56 | + } | |
| 57 | + }; | |
| 58 | +}; | |
| 59 | + | |
| 60 | +module.exports = { | |
| 61 | + wrapMethod: wrapMethod | |
| 62 | +}; | |
| 63 | + | |
| 64 | +},{}],3:[function(_dereq_,module,exports){ | |
| 65 | +(function (global){ | |
| 66 | +/*global XDomainRequest:false */ | |
| 67 | + | |
| 68 | +var TraceKit = _dereq_(6); | |
| 69 | +var stringify = _dereq_(7); | |
| 70 | +var RavenConfigError = _dereq_(1); | |
| 71 | + | |
| 72 | +var utils = _dereq_(5); | |
| 73 | +var isError = utils.isError; | |
| 74 | +var isObject = utils.isObject; | |
| 75 | +var isErrorEvent = utils.isErrorEvent; | |
| 76 | +var isUndefined = utils.isUndefined; | |
| 77 | +var isFunction = utils.isFunction; | |
| 78 | +var isString = utils.isString; | |
| 79 | +var isArray = utils.isArray; | |
| 80 | +var isEmptyObject = utils.isEmptyObject; | |
| 81 | +var each = utils.each; | |
| 82 | +var objectMerge = utils.objectMerge; | |
| 83 | +var truncate = utils.truncate; | |
| 84 | +var objectFrozen = utils.objectFrozen; | |
| 85 | +var hasKey = utils.hasKey; | |
| 86 | +var joinRegExp = utils.joinRegExp; | |
| 87 | +var urlencode = utils.urlencode; | |
| 88 | +var uuid4 = utils.uuid4; | |
| 89 | +var htmlTreeAsString = utils.htmlTreeAsString; | |
| 90 | +var isSameException = utils.isSameException; | |
| 91 | +var isSameStacktrace = utils.isSameStacktrace; | |
| 92 | +var parseUrl = utils.parseUrl; | |
| 93 | +var fill = utils.fill; | |
| 94 | + | |
| 95 | +var wrapConsoleMethod = _dereq_(2).wrapMethod; | |
| 96 | + | |
| 97 | +var dsnKeys = 'source protocol user pass host port path'.split(' '), | |
| 98 | + dsnPattern = /^(?:(\w+):)?\/\/(?:(\w+)(:\w+)?@)?([\w\.-]+)(?::(\d+))?(\/.*)/; | |
| 99 | + | |
| 100 | +function now() { | |
| 101 | + return +new Date(); | |
| 102 | +} | |
| 103 | + | |
| 104 | +// This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785) | |
| 105 | +var _window = | |
| 106 | + typeof window !== 'undefined' | |
| 107 | + ? window | |
| 108 | + : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; | |
| 109 | +var _document = _window.document; | |
| 110 | +var _navigator = _window.navigator; | |
| 111 | + | |
| 112 | +function keepOriginalCallback(original, callback) { | |
| 113 | + return isFunction(callback) | |
| 114 | + ? function(data) { | |
| 115 | + return callback(data, original); | |
| 116 | + } | |
| 117 | + : callback; | |
| 118 | +} | |
| 119 | + | |
| 120 | +// First, check for JSON support | |
| 121 | +// If there is no JSON, we no-op the core features of Raven | |
| 122 | +// since JSON is required to encode the payload | |
| 123 | +function Raven() { | |
| 124 | + this._hasJSON = !!(typeof JSON === 'object' && JSON.stringify); | |
| 125 | + // Raven can run in contexts where there's no document (react-native) | |
| 126 | + this._hasDocument = !isUndefined(_document); | |
| 127 | + this._hasNavigator = !isUndefined(_navigator); | |
| 128 | + this._lastCapturedException = null; | |
| 129 | + this._lastData = null; | |
| 130 | + this._lastEventId = null; | |
| 131 | + this._globalServer = null; | |
| 132 | + this._globalKey = null; | |
| 133 | + this._globalProject = null; | |
| 134 | + this._globalContext = {}; | |
| 135 | + this._globalOptions = { | |
| 136 | + logger: 'javascript', | |
| 137 | + ignoreErrors: [], | |
| 138 | + ignoreUrls: [], | |
| 139 | + whitelistUrls: [], | |
| 140 | + includePaths: [], | |
| 141 | + collectWindowErrors: true, | |
| 142 | + maxMessageLength: 0, | |
| 143 | + | |
| 144 | + // By default, truncates URL values to 250 chars | |
| 145 | + maxUrlLength: 250, | |
| 146 | + stackTraceLimit: 50, | |
| 147 | + autoBreadcrumbs: true, | |
| 148 | + instrument: true, | |
| 149 | + sampleRate: 1 | |
| 150 | + }; | |
| 151 | + this._ignoreOnError = 0; | |
| 152 | + this._isRavenInstalled = false; | |
| 153 | + this._originalErrorStackTraceLimit = Error.stackTraceLimit; | |
| 154 | + // capture references to window.console *and* all its methods first | |
| 155 | + // before the console plugin has a chance to monkey patch | |
| 156 | + this._originalConsole = _window.console || {}; | |
| 157 | + this._originalConsoleMethods = {}; | |
| 158 | + this._plugins = []; | |
| 159 | + this._startTime = now(); | |
| 160 | + this._wrappedBuiltIns = []; | |
| 161 | + this._breadcrumbs = []; | |
| 162 | + this._lastCapturedEvent = null; | |
| 163 | + this._keypressTimeout; | |
| 164 | + this._location = _window.location; | |
| 165 | + this._lastHref = this._location && this._location.href; | |
| 166 | + this._resetBackoff(); | |
| 167 | + | |
| 168 | + // eslint-disable-next-line guard-for-in | |
| 169 | + for (var method in this._originalConsole) { | |
| 170 | + this._originalConsoleMethods[method] = this._originalConsole[method]; | |
| 171 | + } | |
| 172 | +} | |
| 224 | 173 | |
| 225 | − /* | |
| 174 | +/* | |
| 226 | 175 | * The core Raven singleton |
| 227 | 176 | * |
| 228 | 177 | * @this {Raven} |
| 229 | 178 | */ |
| 230 | 179 | |
| 231 | − Raven.prototype = { | |
| 232 | − // Hardcode version string so that raven source can be loaded directly via | |
| 233 | − // webpack (using a build step causes webpack #1617). Grunt verifies that | |
| 234 | − // this value matches package.json during build. | |
| 235 | − // See: https://github.com/getsentry/raven-js/issues/465 | |
| 236 | − VERSION: '3.18.0', | |
| 180 | +Raven.prototype = { | |
| 181 | + // Hardcode version string so that raven source can be loaded directly via | |
| 182 | + // webpack (using a build step causes webpack #1617). Grunt verifies that | |
| 183 | + // this value matches package.json during build. | |
| 184 | + // See: https://github.com/getsentry/raven-js/issues/465 | |
| 185 | + VERSION: '3.20.1', | |
| 237 | 186 | |
| 238 | − debug: false, | |
| 187 | + debug: false, | |
| 239 | 188 | |
| 240 | − TraceKit: TraceKit, // alias to TraceKit | |
| 189 | + TraceKit: TraceKit, // alias to TraceKit | |
| 241 | 190 | |
| 242 | − /* | |
| 191 | + /* | |
| 243 | 192 | * Configure Raven with a DSN and extra options |
| 244 | 193 | * |
| 245 | 194 | * @param {string} dsn The public Sentry DSN |
| 246 | 195 | * @param {object} options Set of global options [optional] |
| 247 | 196 | * @return {Raven} |
| 248 | 197 | */ |
| 249 | − config: function(dsn, options) { | |
| 250 | − var self = this; | |
| 251 | − | |
| 252 | − if (self._globalServer) { | |
| 253 | − this._logDebug('error', 'Error: Raven has already been configured'); | |
| 254 | − return self; | |
| 255 | − } | |
| 256 | − if (!dsn) return self; | |
| 257 | − | |
| 258 | − var globalOptions = self._globalOptions; | |
| 259 | − | |
| 260 | − // merge in options | |
| 261 | − if (options) { | |
| 262 | − each(options, function(key, value) { | |
| 263 | − // tags and extra are special and need to be put into context | |
| 264 | − if (key === 'tags' || key === 'extra' || key === 'user') { | |
| 265 | − self._globalContext[key] = value; | |
| 266 | − } else { | |
| 267 | − globalOptions[key] = value; | |
| 268 | − } | |
| 269 | − }); | |
| 270 | − } | |
| 198 | + config: function(dsn, options) { | |
| 199 | + var self = this; | |
| 271 | 200 | |
| 272 | − self.setDSN(dsn); | |
| 273 | − | |
| 274 | − // "Script error." is hard coded into browsers for errors that it can't read. | |
| 275 | − // this is the result of a script being pulled in from an external domain and CORS. | |
| 276 | − globalOptions.ignoreErrors.push(/^Script error\.?$/); | |
| 277 | − globalOptions.ignoreErrors.push( | |
| 278 | − /^Javascript error: Script error\.? on line 0$/ | |
| 279 | − ); | |
| 280 | − | |
| 281 | − // join regexp rules into one big rule | |
| 282 | − globalOptions.ignoreErrors = joinRegExp(globalOptions.ignoreErrors); | |
| 283 | − globalOptions.ignoreUrls = globalOptions.ignoreUrls.length | |
| 284 | − ? joinRegExp(globalOptions.ignoreUrls) | |
| 285 | − : false; | |
| 286 | − globalOptions.whitelistUrls = globalOptions.whitelistUrls.length | |
| 287 | − ? joinRegExp(globalOptions.whitelistUrls) | |
| 288 | − : false; | |
| 289 | − globalOptions.includePaths = joinRegExp(globalOptions.includePaths); | |
| 290 | − globalOptions.maxBreadcrumbs = Math.max( | |
| 291 | − 0, | |
| 292 | − Math.min(globalOptions.maxBreadcrumbs || 100, 100) | |
| 293 | − ); // default and hard limit is 100 | |
| 294 | − | |
| 295 | − var autoBreadcrumbDefaults = { | |
| 296 | − xhr: true, | |
| 297 | − console: true, | |
| 298 | − dom: true, | |
| 299 | − location: true | |
| 300 | − }; | |
| 201 | + if (self._globalServer) { | |
| 202 | + this._logDebug('error', 'Error: Raven has already been configured'); | |
| 203 | + return self; | |
| 204 | + } | |
| 205 | + if (!dsn) return self; | |
| 206 | + | |
| 207 | + var globalOptions = self._globalOptions; | |
| 208 | + | |
| 209 | + // merge in options | |
| 210 | + if (options) { | |
| 211 | + each(options, function(key, value) { | |
| 212 | + // tags and extra are special and need to be put into context | |
| 213 | + if (key === 'tags' || key === 'extra' || key === 'user') { | |
| 214 | + self._globalContext[key] = value; | |
| 215 | + } else { | |
| 216 | + globalOptions[key] = value; | |
| 217 | + } | |
| 218 | + }); | |
| 219 | + } | |
| 301 | 220 | |
| 302 | − var autoBreadcrumbs = globalOptions.autoBreadcrumbs; | |
| 303 | − if ({}.toString.call(autoBreadcrumbs) === '[object Object]') { | |
| 304 | − autoBreadcrumbs = objectMerge(autoBreadcrumbDefaults, autoBreadcrumbs); | |
| 305 | − } else if (autoBreadcrumbs !== false) { | |
| 306 | − autoBreadcrumbs = autoBreadcrumbDefaults; | |
| 307 | − } | |
| 308 | − globalOptions.autoBreadcrumbs = autoBreadcrumbs; | |
| 221 | + self.setDSN(dsn); | |
| 222 | + | |
| 223 | + // "Script error." is hard coded into browsers for errors that it can't read. | |
| 224 | + // this is the result of a script being pulled in from an external domain and CORS. | |
| 225 | + globalOptions.ignoreErrors.push(/^Script error\.?$/); | |
| 226 | + globalOptions.ignoreErrors.push(/^Javascript error: Script error\.? on line 0$/); | |
| 227 | + | |
| 228 | + // join regexp rules into one big rule | |
| 229 | + globalOptions.ignoreErrors = joinRegExp(globalOptions.ignoreErrors); | |
| 230 | + globalOptions.ignoreUrls = globalOptions.ignoreUrls.length | |
| 231 | + ? joinRegExp(globalOptions.ignoreUrls) | |
| 232 | + : false; | |
| 233 | + globalOptions.whitelistUrls = globalOptions.whitelistUrls.length | |
| 234 | + ? joinRegExp(globalOptions.whitelistUrls) | |
| 235 | + : false; | |
| 236 | + globalOptions.includePaths = joinRegExp(globalOptions.includePaths); | |
| 237 | + globalOptions.maxBreadcrumbs = Math.max( | |
| 238 | + 0, | |
| 239 | + Math.min(globalOptions.maxBreadcrumbs || 100, 100) | |
| 240 | + ); // default and hard limit is 100 | |
| 241 | + | |
| 242 | + var autoBreadcrumbDefaults = { | |
| 243 | + xhr: true, | |
| 244 | + console: true, | |
| 245 | + dom: true, | |
| 246 | + location: true, | |
| 247 | + sentry: true | |
| 248 | + }; | |
| 249 | + | |
| 250 | + var autoBreadcrumbs = globalOptions.autoBreadcrumbs; | |
| 251 | + if ({}.toString.call(autoBreadcrumbs) === '[object Object]') { | |
| 252 | + autoBreadcrumbs = objectMerge(autoBreadcrumbDefaults, autoBreadcrumbs); | |
| 253 | + } else if (autoBreadcrumbs !== false) { | |
| 254 | + autoBreadcrumbs = autoBreadcrumbDefaults; | |
| 255 | + } | |
| 256 | + globalOptions.autoBreadcrumbs = autoBreadcrumbs; | |
| 309 | 257 | |
| 310 | − var instrumentDefaults = { | |
| 311 | − tryCatch: true | |
| 312 | − }; | |
| 258 | + var instrumentDefaults = { | |
| 259 | + tryCatch: true | |
| 260 | + }; | |
| 313 | 261 | |
| 314 | − var instrument = globalOptions.instrument; | |
| 315 | − if ({}.toString.call(instrument) === '[object Object]') { | |
| 316 | − instrument = objectMerge(instrumentDefaults, instrument); | |
| 317 | − } else if (instrument !== false) { | |
| 318 | − instrument = instrumentDefaults; | |
| 319 | − } | |
| 320 | − globalOptions.instrument = instrument; | |
| 262 | + var instrument = globalOptions.instrument; | |
| 263 | + if ({}.toString.call(instrument) === '[object Object]') { | |
| 264 | + instrument = objectMerge(instrumentDefaults, instrument); | |
| 265 | + } else if (instrument !== false) { | |
| 266 | + instrument = instrumentDefaults; | |
| 267 | + } | |
| 268 | + globalOptions.instrument = instrument; | |
| 321 | 269 | |
| 322 | − TraceKit.collectWindowErrors = !!globalOptions.collectWindowErrors; | |
| 270 | + TraceKit.collectWindowErrors = !!globalOptions.collectWindowErrors; | |
| 323 | 271 | |
| 324 | − // return for chaining | |
| 325 | − return self; | |
| 326 | − }, | |
| 272 | + // return for chaining | |
| 273 | + return self; | |
| 274 | + }, | |
| 327 | 275 | |
| 328 | − /* | |
| 276 | + /* | |
| 329 | 277 | * Installs a global window.onerror error handler |
| 330 | 278 | * to capture and report uncaught exceptions. |
| 331 | 279 | * At this point, install() is required to be called due |
| @@ -333,63 +281,58 @@ · 1 unmodified line | ||
| 333 | 281 | * |
| 334 | 282 | * @return {Raven} |
| 335 | 283 | */ |
| 336 | − install: function() { | |
| 337 | − var self = this; | |
| 338 | − if (self.isSetup() && !self._isRavenInstalled) { | |
| 339 | − TraceKit.report.subscribe(function() { | |
| 340 | − self._handleOnErrorStackInfo.apply(self, arguments); | |
| 341 | − }); | |
| 342 | − if ( | |
| 343 | − self._globalOptions.instrument && | |
| 344 | − self._globalOptions.instrument.tryCatch | |
| 345 | − ) { | |
| 346 | − self._instrumentTryCatch(); | |
| 347 | − } | |
| 284 | + install: function() { | |
| 285 | + var self = this; | |
| 286 | + if (self.isSetup() && !self._isRavenInstalled) { | |
| 287 | + TraceKit.report.subscribe(function() { | |
| 288 | + self._handleOnErrorStackInfo.apply(self, arguments); | |
| 289 | + }); | |
| 348 | 290 | |
| 349 | − if (self._globalOptions.autoBreadcrumbs) self._instrumentBreadcrumbs(); | |
| 291 | + self._patchFunctionToString(); | |
| 350 | 292 | |
| 351 | − // Install all of the plugins | |
| 352 | − self._drainPlugins(); | |
| 293 | + if (self._globalOptions.instrument && self._globalOptions.instrument.tryCatch) { | |
| 294 | + self._instrumentTryCatch(); | |
| 295 | + } | |
| 353 | 296 | |
| 354 | − self._isRavenInstalled = true; | |
| 355 | − } | |
| 297 | + if (self._globalOptions.autoBreadcrumbs) self._instrumentBreadcrumbs(); | |
| 356 | 298 | |
| 357 | − Error.stackTraceLimit = self._globalOptions.stackTraceLimit; | |
| 358 | − return this; | |
| 359 | − }, | |
| 299 | + // Install all of the plugins | |
| 300 | + self._drainPlugins(); | |
| 360 | 301 | |
| 361 | − /* | |
| 302 | + self._isRavenInstalled = true; | |
| 303 | + } | |
| 304 | + | |
| 305 | + Error.stackTraceLimit = self._globalOptions.stackTraceLimit; | |
| 306 | + return this; | |
| 307 | + }, | |
| 308 | + | |
| 309 | + /* | |
| 362 | 310 | * Set the DSN (can be called multiple time unlike config) |
| 363 | 311 | * |
| 364 | 312 | * @param {string} dsn The public Sentry DSN |
| 365 | 313 | */ |
| 366 | − setDSN: function(dsn) { | |
| 367 | − var self = this, | |
| 368 | − uri = self._parseDSN(dsn), | |
| 369 | − lastSlash = uri.path.lastIndexOf('/'), | |
| 370 | − path = uri.path.substr(1, lastSlash); | |
| 371 | − | |
| 372 | − self._dsn = dsn; | |
| 373 | − self._globalKey = uri.user; | |
| 374 | − self._globalSecret = uri.pass && uri.pass.substr(1); | |
| 375 | − self._globalProject = uri.path.substr(lastSlash + 1); | |
| 376 | − | |
| 377 | − self._globalServer = self._getGlobalServer(uri); | |
| 378 | − | |
| 379 | − self._globalEndpoint = | |
| 380 | − self._globalServer + | |
| 381 | − '/' + | |
| 382 | − path + | |
| 383 | − 'api/' + | |
| 384 | − self._globalProject + | |
| 385 | − '/store/'; | |
| 386 | − | |
| 387 | − // Reset backoff state since we may be pointing at a | |
| 388 | − // new project/server | |
| 389 | − this._resetBackoff(); | |
| 390 | − }, | |
| 391 | − | |
| 392 | − /* | |
| 314 | + setDSN: function(dsn) { | |
| 315 | + var self = this, | |
| 316 | + uri = self._parseDSN(dsn), | |
| 317 | + lastSlash = uri.path.lastIndexOf('/'), | |
| 318 | + path = uri.path.substr(1, lastSlash); | |
| 319 | + | |
| 320 | + self._dsn = dsn; | |
| 321 | + self._globalKey = uri.user; | |
| 322 | + self._globalSecret = uri.pass && uri.pass.substr(1); | |
| 323 | + self._globalProject = uri.path.substr(lastSlash + 1); | |
| 324 | + | |
| 325 | + self._globalServer = self._getGlobalServer(uri); | |
| 326 | + | |
| 327 | + self._globalEndpoint = | |
| 328 | + self._globalServer + '/' + path + 'api/' + self._globalProject + '/store/'; | |
| 329 | + | |
| 330 | + // Reset backoff state since we may be pointing at a | |
| 331 | + // new project/server | |
| 332 | + this._resetBackoff(); | |
| 333 | + }, | |
| 334 | + | |
| 335 | + /* | |
| 393 | 336 | * Wrap code within a context so Raven can capture errors |
| 394 | 337 | * reliably across domains that is executed immediately. |
| 395 | 338 | * |
| @@ -397,17 +340,17 @@ · 1 unmodified line | ||
| 397 | 340 | * @param {function} func The callback to be immediately executed within the context |
| 398 | 341 | * @param {array} args An array of arguments to be called with the callback [optional] |
| 399 | 342 | */ |
| 400 | − context: function(options, func, args) { | |
| 401 | − if (isFunction(options)) { | |
| 402 | − args = func || []; | |
| 403 | − func = options; | |
| 404 | − options = undefined; | |
| 405 | − } | |
| 343 | + context: function(options, func, args) { | |
| 344 | + if (isFunction(options)) { | |
| 345 | + args = func || []; | |
| 346 | + func = options; | |
| 347 | + options = undefined; | |
| 348 | + } | |
| 406 | 349 | |
| 407 | − return this.wrap(options, func).apply(this, args); | |
| 408 | − }, | |
| 350 | + return this.wrap(options, func).apply(this, args); | |
| 351 | + }, | |
| 409 | 352 | |
| 410 | − /* | |
| 353 | + /* | |
| 411 | 354 | * Wrap code within a context and returns back a new function to be executed |
| 412 | 355 | * |
| 413 | 356 | * @param {object} options A specific set of options for this context [optional] |
| @@ -415,381 +358,397 @@ · 1 unmodified line | ||
| 415 | 358 | * @param {function} func A function to call before the try/catch wrapper [optional, private] |
| 416 | 359 | * @return {function} The newly wrapped functions with a context |
| 417 | 360 | */ |
| 418 | − wrap: function(options, func, _before) { | |
| 419 | − var self = this; | |
| 420 | − // 1 argument has been passed, and it's not a function | |
| 421 | − // so just return it | |
| 422 | − if (isUndefined(func) && !isFunction(options)) { | |
| 423 | − return options; | |
| 424 | − } | |
| 425 | − | |
| 426 | − // options is optional | |
| 427 | − if (isFunction(options)) { | |
| 428 | − func = options; | |
| 429 | − options = undefined; | |
| 430 | − } | |
| 361 | + wrap: function(options, func, _before) { | |
| 362 | + var self = this; | |
| 363 | + // 1 argument has been passed, and it's not a function | |
| 364 | + // so just return it | |
| 365 | + if (isUndefined(func) && !isFunction(options)) { | |
| 366 | + return options; | |
| 367 | + } | |
| 431 | 368 | |
| 432 | − // At this point, we've passed along 2 arguments, and the second one | |
| 433 | − // is not a function either, so we'll just return the second argument. | |
| 434 | − if (!isFunction(func)) { | |
| 435 | − return func; | |
| 436 | − } | |
| 369 | + // options is optional | |
| 370 | + if (isFunction(options)) { | |
| 371 | + func = options; | |
| 372 | + options = undefined; | |
| 373 | + } | |
| 437 | 374 | |
| 438 | − // We don't wanna wrap it twice! | |
| 439 | − try { | |
| 440 | − if (func.__raven__) { | |
| 441 | − return func; | |
| 442 | − } | |
| 375 | + // At this point, we've passed along 2 arguments, and the second one | |
| 376 | + // is not a function either, so we'll just return the second argument. | |
| 377 | + if (!isFunction(func)) { | |
| 378 | + return func; | |
| 379 | + } | |
| 443 | 380 | |
| 444 | − // If this has already been wrapped in the past, return that | |
| 445 | − if (func.__raven_wrapper__) { | |
| 446 | − return func.__raven_wrapper__; | |
| 447 | − } | |
| 448 | − } catch (e) { | |
| 449 | − // Just accessing custom props in some Selenium environments | |
| 450 | − // can cause a "Permission denied" exception (see raven-js#495). | |
| 451 | − // Bail on wrapping and return the function as-is (defers to window.onerror). | |
| 452 | − return func; | |
| 453 | − } | |
| 381 | + // We don't wanna wrap it twice! | |
| 382 | + try { | |
| 383 | + if (func.__raven__) { | |
| 384 | + return func; | |
| 385 | + } | |
| 454 | 386 | |
| 455 | − function wrapped() { | |
| 456 | − var args = [], | |
| 457 | − i = arguments.length, | |
| 458 | − deep = !options || (options && options.deep !== false); | |
| 387 | + // If this has already been wrapped in the past, return that | |
| 388 | + if (func.__raven_wrapper__) { | |
| 389 | + return func.__raven_wrapper__; | |
| 390 | + } | |
| 391 | + } catch (e) { | |
| 392 | + // Just accessing custom props in some Selenium environments | |
| 393 | + // can cause a "Permission denied" exception (see raven-js#495). | |
| 394 | + // Bail on wrapping and return the function as-is (defers to window.onerror). | |
| 395 | + return func; | |
| 396 | + } | |
| 459 | 397 | |
| 460 | − if (_before && isFunction(_before)) { | |
| 461 | − _before.apply(this, arguments); | |
| 462 | − } | |
| 398 | + function wrapped() { | |
| 399 | + var args = [], | |
| 400 | + i = arguments.length, | |
| 401 | + deep = !options || (options && options.deep !== false); | |
| 463 | 402 | |
| 464 | − // Recursively wrap all of a function's arguments that are | |
| 465 | − // functions themselves. | |
| 466 | − while (i--) | |
| 467 | − args[i] = deep ? self.wrap(options, arguments[i]) : arguments[i]; | |
| 403 | + if (_before && isFunction(_before)) { | |
| 404 | + _before.apply(this, arguments); | |
| 405 | + } | |
| 468 | 406 | |
| 469 | − try { | |
| 470 | − // Attempt to invoke user-land function | |
| 471 | − // NOTE: If you are a Sentry user, and you are seeing this stack frame, it | |
| 472 | − // means Raven caught an error invoking your application code. This is | |
| 473 | − // expected behavior and NOT indicative of a bug with Raven.js. | |
| 474 | − return func.apply(this, args); | |
| 475 | − } catch (e) { | |
| 476 | − self._ignoreNextOnError(); | |
| 477 | − self.captureException(e, options); | |
| 478 | − throw e; | |
| 479 | − } | |
| 480 | − } | |
| 407 | + // Recursively wrap all of a function's arguments that are | |
| 408 | + // functions themselves. | |
| 409 | + while (i--) args[i] = deep ? self.wrap(options, arguments[i]) : arguments[i]; | |
| 410 | + | |
| 411 | + try { | |
| 412 | + // Attempt to invoke user-land function | |
| 413 | + // NOTE: If you are a Sentry user, and you are seeing this stack frame, it | |
| 414 | + // means Raven caught an error invoking your application code. This is | |
| 415 | + // expected behavior and NOT indicative of a bug with Raven.js. | |
| 416 | + return func.apply(this, args); | |
| 417 | + } catch (e) { | |
| 418 | + self._ignoreNextOnError(); | |
| 419 | + self.captureException(e, options); | |
| 420 | + throw e; | |
| 421 | + } | |
| 422 | + } | |
| 481 | 423 | |
| 482 | − // copy over properties of the old function | |
| 483 | − for (var property in func) { | |
| 484 | − if (hasKey(func, property)) { | |
| 485 | − wrapped[property] = func[property]; | |
| 486 | − } | |
| 487 | − } | |
| 488 | − wrapped.prototype = func.prototype; | |
| 424 | + // copy over properties of the old function | |
| 425 | + for (var property in func) { | |
| 426 | + if (hasKey(func, property)) { | |
| 427 | + wrapped[property] = func[property]; | |
| 428 | + } | |
| 429 | + } | |
| 430 | + wrapped.prototype = func.prototype; | |
| 489 | 431 | |
| 490 | − func.__raven_wrapper__ = wrapped; | |
| 491 | − // Signal that this function has been wrapped already | |
| 492 | − // for both debugging and to prevent it to being wrapped twice | |
| 493 | − wrapped.__raven__ = true; | |
| 494 | − wrapped.__inner__ = func; | |
| 432 | + func.__raven_wrapper__ = wrapped; | |
| 433 | + // Signal that this function has been wrapped/filled already | |
| 434 | + // for both debugging and to prevent it to being wrapped/filled twice | |
| 435 | + wrapped.__raven__ = true; | |
| 436 | + wrapped.__orig__ = func; | |
| 495 | 437 | |
| 496 | − return wrapped; | |
| 497 | − }, | |
| 438 | + return wrapped; | |
| 439 | + }, | |
| 498 | 440 | |
| 499 | − /* | |
| 441 | + /* | |
| 500 | 442 | * Uninstalls the global error handler. |
| 501 | 443 | * |
| 502 | 444 | * @return {Raven} |
| 503 | 445 | */ |
| 504 | − uninstall: function() { | |
| 505 | − TraceKit.report.uninstall(); | |
| 446 | + uninstall: function() { | |
| 447 | + TraceKit.report.uninstall(); | |
| 506 | 448 | |
| 507 | − this._restoreBuiltIns(); | |
| 449 | + this._unpatchFunctionToString(); | |
| 450 | + this._restoreBuiltIns(); | |
| 508 | 451 | |
| 509 | − Error.stackTraceLimit = this._originalErrorStackTraceLimit; | |
| 510 | − this._isRavenInstalled = false; | |
| 452 | + Error.stackTraceLimit = this._originalErrorStackTraceLimit; | |
| 453 | + this._isRavenInstalled = false; | |
| 511 | 454 | |
| 512 | − return this; | |
| 513 | − }, | |
| 455 | + return this; | |
| 456 | + }, | |
| 514 | 457 | |
| 515 | − /* | |
| 458 | + /* | |
| 516 | 459 | * Manually capture an exception and send it over to Sentry |
| 517 | 460 | * |
| 518 | 461 | * @param {error} ex An exception to be logged |
| 519 | 462 | * @param {object} options A specific set of options for this error [optional] |
| 520 | 463 | * @return {Raven} |
| 521 | 464 | */ |
| 522 | − captureException: function(ex, options) { | |
| 523 | − // If not an Error is passed through, recall as a message instead | |
| 524 | − if (!isError(ex)) { | |
| 525 | − return this.captureMessage( | |
| 526 | − ex, | |
| 527 | − objectMerge( | |
| 528 | − { | |
| 529 | − trimHeadFrames: 1, | |
| 530 | − stacktrace: true // if we fall back to captureMessage, default to attempting a new trace | |
| 531 | − }, | |
| 532 | − options | |
| 533 | − ) | |
| 534 | − ); | |
| 535 | − } | |
| 536 | − | |
| 537 | − // Store the raw exception object for potential debugging and introspection | |
| 538 | − this._lastCapturedException = ex; | |
| 465 | + captureException: function(ex, options) { | |
| 466 | + // Cases for sending ex as a message, rather than an exception | |
| 467 | + var isNotError = !isError(ex); | |
| 468 | + var isNotErrorEvent = !isErrorEvent(ex); | |
| 469 | + var isErrorEventWithoutError = isErrorEvent(ex) && !ex.error; | |
| 470 | + | |
| 471 | + if ((isNotError && isNotErrorEvent) || isErrorEventWithoutError) { | |
| 472 | + return this.captureMessage( | |
| 473 | + ex, | |
| 474 | + objectMerge( | |
| 475 | + { | |
| 476 | + trimHeadFrames: 1, | |
| 477 | + stacktrace: true // if we fall back to captureMessage, default to attempting a new trace | |
| 478 | + }, | |
| 479 | + options | |
| 480 | + ) | |
| 481 | + ); | |
| 482 | + } | |
| 539 | 483 | |
| 540 | − // TraceKit.report will re-raise any exception passed to it, | |
| 541 | − // which means you have to wrap it in try/catch. Instead, we | |
| 542 | − // can wrap it here and only re-raise if TraceKit.report | |
| 543 | − // raises an exception different from the one we asked to | |
| 544 | − // report on. | |
| 545 | − try { | |
| 546 | − var stack = TraceKit.computeStackTrace(ex); | |
| 547 | − this._handleStackInfo(stack, options); | |
| 548 | − } catch (ex1) { | |
| 549 | − if (ex !== ex1) { | |
| 550 | − throw ex1; | |
| 551 | − } | |
| 552 | − } | |
| 484 | + // Get actual Error from ErrorEvent | |
| 485 | + if (isErrorEvent(ex)) ex = ex.error; | |
| 486 | + | |
| 487 | + // Store the raw exception object for potential debugging and introspection | |
| 488 | + this._lastCapturedException = ex; | |
| 489 | + | |
| 490 | + // TraceKit.report will re-raise any exception passed to it, | |
| 491 | + // which means you have to wrap it in try/catch. Instead, we | |
| 492 | + // can wrap it here and only re-raise if TraceKit.report | |
| 493 | + // raises an exception different from the one we asked to | |
| 494 | + // report on. | |
| 495 | + try { | |
| 496 | + var stack = TraceKit.computeStackTrace(ex); | |
| 497 | + this._handleStackInfo(stack, options); | |
| 498 | + } catch (ex1) { | |
| 499 | + if (ex !== ex1) { | |
| 500 | + throw ex1; | |
| 501 | + } | |
| 502 | + } | |
| 553 | 503 | |
| 554 | − return this; | |
| 555 | − }, | |
| 504 | + return this; | |
| 505 | + }, | |
| 556 | 506 | |
| 557 | − /* | |
| 507 | + /* | |
| 558 | 508 | * Manually send a message to Sentry |
| 559 | 509 | * |
| 560 | 510 | * @param {string} msg A plain message to be captured in Sentry |
| 561 | 511 | * @param {object} options A specific set of options for this message [optional] |
| 562 | 512 | * @return {Raven} |
| 563 | 513 | */ |
| 564 | − captureMessage: function(msg, options) { | |
| 565 | − // config() automagically converts ignoreErrors from a list to a RegExp so we need to test for an | |
| 566 | − // early call; we'll error on the side of logging anything called before configuration since it's | |
| 567 | − // probably something you should see: | |
| 568 | − if ( | |
| 569 | − !!this._globalOptions.ignoreErrors.test && | |
| 570 | − this._globalOptions.ignoreErrors.test(msg) | |
| 571 | − ) { | |
| 572 | − return; | |
| 573 | − } | |
| 514 | + captureMessage: function(msg, options) { | |
| 515 | + // config() automagically converts ignoreErrors from a list to a RegExp so we need to test for an | |
| 516 | + // early call; we'll error on the side of logging anything called before configuration since it's | |
| 517 | + // probably something you should see: | |
| 518 | + if ( | |
| 519 | + !!this._globalOptions.ignoreErrors.test && | |
| 520 | + this._globalOptions.ignoreErrors.test(msg) | |
| 521 | + ) { | |
| 522 | + return; | |
| 523 | + } | |
| 574 | 524 | |
| 575 | − options = options || {}; | |
| 576 | − | |
| 577 | − var data = objectMerge( | |
| 578 | − { | |
| 579 | − message: msg + '' // Make sure it's actually a string | |
| 580 | − }, | |
| 581 | − options | |
| 582 | − ); | |
| 583 | − | |
| 584 | − if (this._globalOptions.stacktrace || (options && options.stacktrace)) { | |
| 585 | − var ex; | |
| 586 | − // Generate a "synthetic" stack trace from this point. | |
| 587 | − // NOTE: If you are a Sentry user, and you are seeing this stack frame, it is NOT indicative | |
| 588 | − // of a bug with Raven.js. Sentry generates synthetic traces either by configuration, | |
| 589 | − // or if it catches a thrown object without a "stack" property. | |
| 590 | − try { | |
| 591 | − throw new Error(msg); | |
| 592 | − } catch (ex1) { | |
| 593 | − ex = ex1; | |
| 594 | − } | |
| 525 | + options = options || {}; | |
| 526 | + | |
| 527 | + var data = objectMerge( | |
| 528 | + { | |
| 529 | + message: msg + '' // Make sure it's actually a string | |
| 530 | + }, | |
| 531 | + options | |
| 532 | + ); | |
| 533 | + | |
| 534 | + var ex; | |
| 535 | + // Generate a "synthetic" stack trace from this point. | |
| 536 | + // NOTE: If you are a Sentry user, and you are seeing this stack frame, it is NOT indicative | |
| 537 | + // of a bug with Raven.js. Sentry generates synthetic traces either by configuration, | |
| 538 | + // or if it catches a thrown object without a "stack" property. | |
| 539 | + try { | |
| 540 | + throw new Error(msg); | |
| 541 | + } catch (ex1) { | |
| 542 | + ex = ex1; | |
| 543 | + } | |
| 595 | 544 | |
| 596 | − // null exception name so `Error` isn't prefixed to msg | |
| 597 | − ex.name = null; | |
| 598 | − | |
| 599 | − options = objectMerge( | |
| 600 | − { | |
| 601 | − // fingerprint on msg, not stack trace (legacy behavior, could be | |
| 602 | − // revisited) | |
| 603 | − fingerprint: msg, | |
| 604 | − // since we know this is a synthetic trace, the top N-most frames | |
| 605 | − // MUST be from Raven.js, so mark them as in_app later by setting | |
| 606 | − // trimHeadFrames | |
| 607 | − trimHeadFrames: (options.trimHeadFrames || 0) + 1 | |
| 608 | − }, | |
| 609 | − options | |
| 610 | − ); | |
| 611 | − | |
| 612 | − var stack = TraceKit.computeStackTrace(ex); | |
| 613 | − var frames = this._prepareFrames(stack, options); | |
| 614 | − data.stacktrace = { | |
| 615 | − // Sentry expects frames oldest to newest | |
| 616 | − frames: frames.reverse() | |
| 617 | − }; | |
| 618 | − } | |
| 545 | + // null exception name so `Error` isn't prefixed to msg | |
| 546 | + ex.name = null; | |
| 547 | + var stack = TraceKit.computeStackTrace(ex); | |
| 548 | + | |
| 549 | + // stack[0] is `throw new Error(msg)` call itself, we are interested in the frame that was just before that, stack[1] | |
| 550 | + var initialCall = isArray(stack.stack) && stack.stack[1]; | |
| 551 | + var fileurl = (initialCall && initialCall.url) || ''; | |
| 552 | + | |
| 553 | + if ( | |
| 554 | + !!this._globalOptions.ignoreUrls.test && | |
| 555 | + this._globalOptions.ignoreUrls.test(fileurl) | |
| 556 | + ) { | |
| 557 | + return; | |
| 558 | + } | |
| 619 | 559 | |
| 620 | − // Fire away! | |
| 621 | − this._send(data); | |
| 560 | + if ( | |
| 561 | + !!this._globalOptions.whitelistUrls.test && | |
| 562 | + !this._globalOptions.whitelistUrls.test(fileurl) | |
| 563 | + ) { | |
| 564 | + return; | |
| 565 | + } | |
| 622 | 566 | |
| 623 | − return this; | |
| 624 | − }, | |
| 567 | + if (this._globalOptions.stacktrace || (options && options.stacktrace)) { | |
| 568 | + options = objectMerge( | |
| 569 | + { | |
| 570 | + // fingerprint on msg, not stack trace (legacy behavior, could be | |
| 571 | + // revisited) | |
| 572 | + fingerprint: msg, | |
| 573 | + // since we know this is a synthetic trace, the top N-most frames | |
| 574 | + // MUST be from Raven.js, so mark them as in_app later by setting | |
| 575 | + // trimHeadFrames | |
| 576 | + trimHeadFrames: (options.trimHeadFrames || 0) + 1 | |
| 577 | + }, | |
| 578 | + options | |
| 579 | + ); | |
| 580 | + | |
| 581 | + var frames = this._prepareFrames(stack, options); | |
| 582 | + data.stacktrace = { | |
| 583 | + // Sentry expects frames oldest to newest | |
| 584 | + frames: frames.reverse() | |
| 585 | + }; | |
| 586 | + } | |
| 625 | 587 | |
| 626 | − captureBreadcrumb: function(obj) { | |
| 627 | − var crumb = objectMerge( | |
| 628 | − { | |
| 629 | − timestamp: now() / 1000 | |
| 630 | − }, | |
| 631 | − obj | |
| 632 | − ); | |
| 588 | + // Fire away! | |
| 589 | + this._send(data); | |
| 633 | 590 | |
| 634 | − if (isFunction(this._globalOptions.breadcrumbCallback)) { | |
| 635 | − var result = this._globalOptions.breadcrumbCallback(crumb); | |
| 591 | + return this; | |
| 592 | + }, | |
| 636 | 593 | |
| 637 | − if (isObject(result) && !isEmptyObject(result)) { | |
| 638 | − crumb = result; | |
| 639 | − } else if (result === false) { | |
| 640 | − return this; | |
| 641 | − } | |
| 642 | − } | |
| 594 | + captureBreadcrumb: function(obj) { | |
| 595 | + var crumb = objectMerge( | |
| 596 | + { | |
| 597 | + timestamp: now() / 1000 | |
| 598 | + }, | |
| 599 | + obj | |
| 600 | + ); | |
| 643 | 601 | |
| 644 | − this._breadcrumbs.push(crumb); | |
| 645 | − if (this._breadcrumbs.length > this._globalOptions.maxBreadcrumbs) { | |
| 646 | − this._breadcrumbs.shift(); | |
| 647 | − } | |
| 648 | − return this; | |
| 649 | − }, | |
| 602 | + if (isFunction(this._globalOptions.breadcrumbCallback)) { | |
| 603 | + var result = this._globalOptions.breadcrumbCallback(crumb); | |
| 604 | + | |
| 605 | + if (isObject(result) && !isEmptyObject(result)) { | |
| 606 | + crumb = result; | |
| 607 | + } else if (result === false) { | |
| 608 | + return this; | |
| 609 | + } | |
| 610 | + } | |
| 650 | 611 | |
| 651 | − addPlugin: function(plugin /*arg1, arg2, ... argN*/) { | |
| 652 | − var pluginArgs = [].slice.call(arguments, 1); | |
| 612 | + this._breadcrumbs.push(crumb); | |
| 613 | + if (this._breadcrumbs.length > this._globalOptions.maxBreadcrumbs) { | |
| 614 | + this._breadcrumbs.shift(); | |
| 615 | + } | |
| 616 | + return this; | |
| 617 | + }, | |
| 653 | 618 | |
| 654 | − this._plugins.push([plugin, pluginArgs]); | |
| 655 | − if (this._isRavenInstalled) { | |
| 656 | − this._drainPlugins(); | |
| 657 | − } | |
| 619 | + addPlugin: function(plugin /*arg1, arg2, ... argN*/) { | |
| 620 | + var pluginArgs = [].slice.call(arguments, 1); | |
| 658 | 621 | |
| 659 | − return this; | |
| 660 | − }, | |
| 622 | + this._plugins.push([plugin, pluginArgs]); | |
| 623 | + if (this._isRavenInstalled) { | |
| 624 | + this._drainPlugins(); | |
| 625 | + } | |
| 626 | + | |
| 627 | + return this; | |
| 628 | + }, | |
| 661 | 629 | |
| 662 | − /* | |
| 630 | + /* | |
| 663 | 631 | * Set/clear a user to be sent along with the payload. |
| 664 | 632 | * |
| 665 | 633 | * @param {object} user An object representing user data [optional] |
| 666 | 634 | * @return {Raven} |
| 667 | 635 | */ |
| 668 | − setUserContext: function(user) { | |
| 669 | − // Intentionally do not merge here since that's an unexpected behavior. | |
| 670 | − this._globalContext.user = user; | |
| 636 | + setUserContext: function(user) { | |
| 637 | + // Intentionally do not merge here since that's an unexpected behavior. | |
| 638 | + this._globalContext.user = user; | |
| 671 | 639 | |
| 672 | − return this; | |
| 673 | − }, | |
| 640 | + return this; | |
| 641 | + }, | |
| 674 | 642 | |
| 675 | − /* | |
| 643 | + /* | |
| 676 | 644 | * Merge extra attributes to be sent along with the payload. |
| 677 | 645 | * |
| 678 | 646 | * @param {object} extra An object representing extra data [optional] |
| 679 | 647 | * @return {Raven} |
| 680 | 648 | */ |
| 681 | − setExtraContext: function(extra) { | |
| 682 | − this._mergeContext('extra', extra); | |
| 649 | + setExtraContext: function(extra) { | |
| 650 | + this._mergeContext('extra', extra); | |
| 683 | 651 | |
| 684 | − return this; | |
| 685 | − }, | |
| 652 | + return this; | |
| 653 | + }, | |
| 686 | 654 | |
| 687 | − /* | |
| 655 | + /* | |
| 688 | 656 | * Merge tags to be sent along with the payload. |
| 689 | 657 | * |
| 690 | 658 | * @param {object} tags An object representing tags [optional] |
| 691 | 659 | * @return {Raven} |
| 692 | 660 | */ |
| 693 | − setTagsContext: function(tags) { | |
| 694 | − this._mergeContext('tags', tags); | |
| 661 | + setTagsContext: function(tags) { | |
| 662 | + this._mergeContext('tags', tags); | |
| 695 | 663 | |
| 696 | − return this; | |
| 697 | − }, | |
| 664 | + return this; | |
| 665 | + }, | |
| 698 | 666 | |
| 699 | − /* | |
| 667 | + /* | |
| 700 | 668 | * Clear all of the context. |
| 701 | 669 | * |
| 702 | 670 | * @return {Raven} |
| 703 | 671 | */ |
| 704 | − clearContext: function() { | |
| 705 | − this._globalContext = {}; | |
| 672 | + clearContext: function() { | |
| 673 | + this._globalContext = {}; | |
| 706 | 674 | |
| 707 | − return this; | |
| 708 | − }, | |
| 675 | + return this; | |
| 676 | + }, | |
| 709 | 677 | |
| 710 | − /* | |
| 678 | + /* | |
| 711 | 679 | * Get a copy of the current context. This cannot be mutated. |
| 712 | 680 | * |
| 713 | 681 | * @return {object} copy of context |
| 714 | 682 | */ |
| 715 | − getContext: function() { | |
| 716 | − // lol javascript | |
| 717 | − return JSON.parse(stringify(this._globalContext)); | |
| 718 | − }, | |
| 683 | + getContext: function() { | |
| 684 | + // lol javascript | |
| 685 | + return JSON.parse(stringify(this._globalContext)); | |
| 686 | + }, | |
| 719 | 687 | |
| 720 | − /* | |
| 688 | + /* | |
| 721 | 689 | * Set environment of application |
| 722 | 690 | * |
| 723 | 691 | * @param {string} environment Typically something like 'production'. |
| 724 | 692 | * @return {Raven} |
| 725 | 693 | */ |
| 726 | − setEnvironment: function(environment) { | |
| 727 | − this._globalOptions.environment = environment; | |
| 694 | + setEnvironment: function(environment) { | |
| 695 | + this._globalOptions.environment = environment; | |
| 728 | 696 | |
| 729 | − return this; | |
| 730 | − }, | |
| 697 | + return this; | |
| 698 | + }, | |
| 731 | 699 | |
| 732 | − /* | |
| 700 | + /* | |
| 733 | 701 | * Set release version of application |
| 734 | 702 | * |
| 735 | 703 | * @param {string} release Typically something like a git SHA to identify version |
| 736 | 704 | * @return {Raven} |
| 737 | 705 | */ |
| 738 | − setRelease: function(release) { | |
| 739 | − this._globalOptions.release = release; | |
| 706 | + setRelease: function(release) { | |
| 707 | + this._globalOptions.release = release; | |
| 740 | 708 | |
| 741 | − return this; | |
| 742 | − }, | |
| 709 | + return this; | |
| 710 | + }, | |
| 743 | 711 | |
| 744 | − /* | |
| 712 | + /* | |
| 745 | 713 | * Set the dataCallback option |
| 746 | 714 | * |
| 747 | 715 | * @param {function} callback The callback to run which allows the |
| 748 | 716 | * data blob to be mutated before sending |
| 749 | 717 | * @return {Raven} |
| 750 | 718 | */ |
| 751 | − setDataCallback: function(callback) { | |
| 752 | − var original = this._globalOptions.dataCallback; | |
| 753 | − this._globalOptions.dataCallback = keepOriginalCallback( | |
| 754 | − original, | |
| 755 | − callback | |
| 756 | − ); | |
| 757 | − return this; | |
| 758 | − }, | |
| 759 | − | |
| 760 | − /* | |
| 719 | + setDataCallback: function(callback) { | |
| 720 | + var original = this._globalOptions.dataCallback; | |
| 721 | + this._globalOptions.dataCallback = keepOriginalCallback(original, callback); | |
| 722 | + return this; | |
| 723 | + }, | |
| 724 | + | |
| 725 | + /* | |
| 761 | 726 | * Set the breadcrumbCallback option |
| 762 | 727 | * |
| 763 | 728 | * @param {function} callback The callback to run which allows filtering |
| 764 | 729 | * or mutating breadcrumbs |
| 765 | 730 | * @return {Raven} |
| 766 | 731 | */ |
| 767 | − setBreadcrumbCallback: function(callback) { | |
| 768 | − var original = this._globalOptions.breadcrumbCallback; | |
| 769 | − this._globalOptions.breadcrumbCallback = keepOriginalCallback( | |
| 770 | − original, | |
| 771 | − callback | |
| 772 | − ); | |
| 773 | − return this; | |
| 774 | − }, | |
| 775 | − | |
| 776 | − /* | |
| 732 | + setBreadcrumbCallback: function(callback) { | |
| 733 | + var original = this._globalOptions.breadcrumbCallback; | |
| 734 | + this._globalOptions.breadcrumbCallback = keepOriginalCallback(original, callback); | |
| 735 | + return this; | |
| 736 | + }, | |
| 737 | + | |
| 738 | + /* | |
| 777 | 739 | * Set the shouldSendCallback option |
| 778 | 740 | * |
| 779 | 741 | * @param {function} callback The callback to run which allows |
| 780 | 742 | * introspecting the blob before sending |
| 781 | 743 | * @return {Raven} |
| 782 | 744 | */ |
| 783 | − setShouldSendCallback: function(callback) { | |
| 784 | − var original = this._globalOptions.shouldSendCallback; | |
| 785 | − this._globalOptions.shouldSendCallback = keepOriginalCallback( | |
| 786 | − original, | |
| 787 | − callback | |
| 788 | − ); | |
| 789 | − return this; | |
| 790 | − }, | |
| 791 | − | |
| 792 | − /** | |
| 745 | + setShouldSendCallback: function(callback) { | |
| 746 | + var original = this._globalOptions.shouldSendCallback; | |
| 747 | + this._globalOptions.shouldSendCallback = keepOriginalCallback(original, callback); | |
| 748 | + return this; | |
| 749 | + }, | |
| 750 | + | |
| 751 | + /** | |
| 793 | 752 | * Override the default HTTP transport mechanism that transmits data |
| 794 | 753 | * to the Sentry server. |
| 795 | 754 | * |
| @@ -798,438 +757,443 @@ · 2 unmodified lines | ||
| 798 | 757 | * |
| 799 | 758 | * @return {Raven} |
| 800 | 759 | */ |
| 801 | − setTransport: function(transport) { | |
| 802 | − this._globalOptions.transport = transport; | |
| 760 | + setTransport: function(transport) { | |
| 761 | + this._globalOptions.transport = transport; | |
| 803 | 762 | |
| 804 | − return this; | |
| 805 | − }, | |
| 763 | + return this; | |
| 764 | + }, | |
| 806 | 765 | |
| 807 | − /* | |
| 766 | + /* | |
| 808 | 767 | * Get the latest raw exception that was captured by Raven. |
| 809 | 768 | * |
| 810 | 769 | * @return {error} |
| 811 | 770 | */ |
| 812 | − lastException: function() { | |
| 813 | − return this._lastCapturedException; | |
| 814 | − }, | |
| 771 | + lastException: function() { | |
| 772 | + return this._lastCapturedException; | |
| 773 | + }, | |
| 815 | 774 | |
| 816 | − /* | |
| 775 | + /* | |
| 817 | 776 | * Get the last event id |
| 818 | 777 | * |
| 819 | 778 | * @return {string} |
| 820 | 779 | */ |
| 821 | − lastEventId: function() { | |
| 822 | − return this._lastEventId; | |
| 823 | − }, | |
| 780 | + lastEventId: function() { | |
| 781 | + return this._lastEventId; | |
| 782 | + }, | |
| 824 | 783 | |
| 825 | − /* | |
| 784 | + /* | |
| 826 | 785 | * Determine if Raven is setup and ready to go. |
| 827 | 786 | * |
| 828 | 787 | * @return {boolean} |
| 829 | 788 | */ |
| 830 | − isSetup: function() { | |
| 831 | − if (!this._hasJSON) return false; // needs JSON support | |
| 832 | − if (!this._globalServer) { | |
| 833 | − if (!this.ravenNotConfiguredError) { | |
| 834 | − this.ravenNotConfiguredError = true; | |
| 835 | − this._logDebug('error', 'Error: Raven has not been configured.'); | |
| 836 | − } | |
| 837 | − return false; | |
| 838 | − } | |
| 839 | − return true; | |
| 840 | − }, | |
| 789 | + isSetup: function() { | |
| 790 | + if (!this._hasJSON) return false; // needs JSON support | |
| 791 | + if (!this._globalServer) { | |
| 792 | + if (!this.ravenNotConfiguredError) { | |
| 793 | + this.ravenNotConfiguredError = true; | |
| 794 | + this._logDebug('error', 'Error: Raven has not been configured.'); | |
| 795 | + } | |
| 796 | + return false; | |
| 797 | + } | |
| 798 | + return true; | |
| 799 | + }, | |
| 841 | 800 | |
| 842 | − afterLoad: function() { | |
| 843 | − // TODO: remove window dependence? | |
| 801 | + afterLoad: function() { | |
| 802 | + // TODO: remove window dependence? | |
| 844 | 803 | |
| 845 | − // Attempt to initialize Raven on load | |
| 846 | − var RavenConfig = _window.RavenConfig; | |
| 847 | − if (RavenConfig) { | |
| 848 | − this.config(RavenConfig.dsn, RavenConfig.config).install(); | |
| 849 | − } | |
| 850 | − }, | |
| 804 | + // Attempt to initialize Raven on load | |
| 805 | + var RavenConfig = _window.RavenConfig; | |
| 806 | + if (RavenConfig) { | |
| 807 | + this.config(RavenConfig.dsn, RavenConfig.config).install(); | |
| 808 | + } | |
| 809 | + }, | |
| 851 | 810 | |
| 852 | − showReportDialog: function(options) { | |
| 853 | − if ( | |
| 854 | − !_document // doesn't work without a document (React native) | |
| 855 | − ) | |
| 856 | − return; | |
| 811 | + showReportDialog: function(options) { | |
| 812 | + if ( | |
| 813 | + !_document // doesn't work without a document (React native) | |
| 814 | + ) | |
| 815 | + return; | |
| 857 | 816 | |
| 858 | − options = options || {}; | |
| 817 | + options = options || {}; | |
| 859 | 818 | |
| 860 | − var lastEventId = options.eventId || this.lastEventId(); | |
| 861 | − if (!lastEventId) { | |
| 862 | − throw new RavenConfigError('Missing eventId'); | |
| 863 | − } | |
| 819 | + var lastEventId = options.eventId || this.lastEventId(); | |
| 820 | + if (!lastEventId) { | |
| 821 | + throw new RavenConfigError('Missing eventId'); | |
| 822 | + } | |
| 864 | 823 | |
| 865 | − var dsn = options.dsn || this._dsn; | |
| 866 | − if (!dsn) { | |
| 867 | − throw new RavenConfigError('Missing DSN'); | |
| 868 | − } | |
| 824 | + var dsn = options.dsn || this._dsn; | |
| 825 | + if (!dsn) { | |
| 826 | + throw new RavenConfigError('Missing DSN'); | |
| 827 | + } | |
| 869 | 828 | |
| 870 | − var encode = encodeURIComponent; | |
| 871 | − var qs = ''; | |
| 872 | − qs += '?eventId=' + encode(lastEventId); | |
| 873 | − qs += '&dsn=' + encode(dsn); | |
| 829 | + var encode = encodeURIComponent; | |
| 830 | + var qs = ''; | |
| 831 | + qs += '?eventId=' + encode(lastEventId); | |
| 832 | + qs += '&dsn=' + encode(dsn); | |
| 874 | 833 | |
| 875 | − var user = options.user || this._globalContext.user; | |
| 876 | − if (user) { | |
| 877 | − if (user.name) qs += '&name=' + encode(user.name); | |
| 878 | − if (user.email) qs += '&email=' + encode(user.email); | |
| 879 | − } | |
| 834 | + var user = options.user || this._globalContext.user; | |
| 835 | + if (user) { | |
| 836 | + if (user.name) qs += '&name=' + encode(user.name); | |
| 837 | + if (user.email) qs += '&email=' + encode(user.email); | |
| 838 | + } | |
| 880 | 839 | |
| 881 | − var globalServer = this._getGlobalServer(this._parseDSN(dsn)); | |
| 882 | − | |
| 883 | − var script = _document.createElement('script'); | |
| 884 | − script.async = true; | |
| 885 | − script.src = globalServer + '/api/embed/error-page/' + qs; | |
| 886 | − (_document.head || _document.body).appendChild(script); | |
| 887 | − }, | |
| 888 | − | |
| 889 | − /**** Private functions ****/ | |
| 890 | − _ignoreNextOnError: function() { | |
| 891 | − var self = this; | |
| 892 | − this._ignoreOnError += 1; | |
| 893 | − setTimeout(function() { | |
| 894 | − // onerror should trigger before setTimeout | |
| 895 | − self._ignoreOnError -= 1; | |
| 896 | − }); | |
| 897 | − }, | |
| 840 | + var globalServer = this._getGlobalServer(this._parseDSN(dsn)); | |
| 898 | 841 | |
| 899 | − _triggerEvent: function(eventType, options) { | |
| 900 | − // NOTE: `event` is a native browser thing, so let's avoid conflicting wiht it | |
| 901 | − var evt, key; | |
| 842 | + var script = _document.createElement('script'); | |
| 843 | + script.async = true; | |
| 844 | + script.src = globalServer + '/api/embed/error-page/' + qs; | |
| 845 | + (_document.head || _document.body).appendChild(script); | |
| 846 | + }, | |
| 902 | 847 | |
| 903 | − if (!this._hasDocument) return; | |
| 848 | + /**** Private functions ****/ | |
| 849 | + _ignoreNextOnError: function() { | |
| 850 | + var self = this; | |
| 851 | + this._ignoreOnError += 1; | |
| 852 | + setTimeout(function() { | |
| 853 | + // onerror should trigger before setTimeout | |
| 854 | + self._ignoreOnError -= 1; | |
| 855 | + }); | |
| 856 | + }, | |
| 904 | 857 | |
| 905 | − options = options || {}; | |
| 858 | + _triggerEvent: function(eventType, options) { | |
| 859 | + // NOTE: `event` is a native browser thing, so let's avoid conflicting with it | |
| 860 | + var evt, key; | |
| 906 | 861 | |
| 907 | − eventType = | |
| 908 | − 'raven' + eventType.substr(0, 1).toUpperCase() + eventType.substr(1); | |
| 862 | + if (!this._hasDocument) return; | |
| 909 | 863 | |
| 910 | − if (_document.createEvent) { | |
| 911 | − evt = _document.createEvent('HTMLEvents'); | |
| 912 | − evt.initEvent(eventType, true, true); | |
| 913 | − } else { | |
| 914 | − evt = _document.createEventObject(); | |
| 915 | − evt.eventType = eventType; | |
| 916 | − } | |
| 864 | + options = options || {}; | |
| 917 | 865 | |
| 918 | − for (key in options) | |
| 919 | − if (hasKey(options, key)) { | |
| 920 | − evt[key] = options[key]; | |
| 921 | − } | |
| 866 | + eventType = 'raven' + eventType.substr(0, 1).toUpperCase() + eventType.substr(1); | |
| 922 | 867 | |
| 923 | − if (_document.createEvent) { | |
| 924 | − // IE9 if standards | |
| 925 | − _document.dispatchEvent(evt); | |
| 926 | − } else { | |
| 927 | − // IE8 regardless of Quirks or Standards | |
| 928 | − // IE9 if quirks | |
| 929 | − try { | |
| 930 | − _document.fireEvent('on' + evt.eventType.toLowerCase(), evt); | |
| 931 | − } catch (e) { | |
| 932 | − // Do nothing | |
| 933 | − } | |
| 934 | − } | |
| 935 | − }, | |
| 868 | + if (_document.createEvent) { | |
| 869 | + evt = _document.createEvent('HTMLEvents'); | |
| 870 | + evt.initEvent(eventType, true, true); | |
| 871 | + } else { | |
| 872 | + evt = _document.createEventObject(); | |
| 873 | + evt.eventType = eventType; | |
| 874 | + } | |
| 875 | + | |
| 876 | + for (key in options) | |
| 877 | + if (hasKey(options, key)) { | |
| 878 | + evt[key] = options[key]; | |
| 879 | + } | |
| 880 | + | |
| 881 | + if (_document.createEvent) { | |
| 882 | + // IE9 if standards | |
| 883 | + _document.dispatchEvent(evt); | |
| 884 | + } else { | |
| 885 | + // IE8 regardless of Quirks or Standards | |
| 886 | + // IE9 if quirks | |
| 887 | + try { | |
| 888 | + _document.fireEvent('on' + evt.eventType.toLowerCase(), evt); | |
| 889 | + } catch (e) { | |
| 890 | + // Do nothing | |
| 891 | + } | |
| 892 | + } | |
| 893 | + }, | |
| 936 | 894 | |
| 937 | − /** | |
| 895 | + /** | |
| 938 | 896 | * Wraps addEventListener to capture UI breadcrumbs |
| 939 | 897 | * @param evtName the event name (e.g. "click") |
| 940 | 898 | * @returns {Function} |
| 941 | 899 | * @private |
| 942 | 900 | */ |
| 943 | − _breadcrumbEventHandler: function(evtName) { | |
| 944 | − var self = this; | |
| 945 | − return function(evt) { | |
| 946 | − // reset keypress timeout; e.g. triggering a 'click' after | |
| 947 | − // a 'keypress' will reset the keypress debounce so that a new | |
| 948 | − // set of keypresses can be recorded | |
| 949 | − self._keypressTimeout = null; | |
| 950 | − | |
| 951 | − // It's possible this handler might trigger multiple times for the same | |
| 952 | − // event (e.g. event propagation through node ancestors). Ignore if we've | |
| 953 | − // already captured the event. | |
| 954 | − if (self._lastCapturedEvent === evt) return; | |
| 955 | − | |
| 956 | − self._lastCapturedEvent = evt; | |
| 957 | − | |
| 958 | − // try/catch both: | |
| 959 | − // - accessing evt.target (see getsentry/raven-js#838, #768) | |
| 960 | − // - `htmlTreeAsString` because it's complex, and just accessing the DOM incorrectly | |
| 961 | − // can throw an exception in some circumstances. | |
| 962 | − var target; | |
| 963 | − try { | |
| 964 | − target = htmlTreeAsString(evt.target); | |
| 965 | − } catch (e) { | |
| 966 | − target = '<unknown>'; | |
| 967 | − } | |
| 901 | + _breadcrumbEventHandler: function(evtName) { | |
| 902 | + var self = this; | |
| 903 | + return function(evt) { | |
| 904 | + // reset keypress timeout; e.g. triggering a 'click' after | |
| 905 | + // a 'keypress' will reset the keypress debounce so that a new | |
| 906 | + // set of keypresses can be recorded | |
| 907 | + self._keypressTimeout = null; | |
| 908 | + | |
| 909 | + // It's possible this handler might trigger multiple times for the same | |
| 910 | + // event (e.g. event propagation through node ancestors). Ignore if we've | |
| 911 | + // already captured the event. | |
| 912 | + if (self._lastCapturedEvent === evt) return; | |
| 913 | + | |
| 914 | + self._lastCapturedEvent = evt; | |
| 915 | + | |
| 916 | + // try/catch both: | |
| 917 | + // - accessing evt.target (see getsentry/raven-js#838, #768) | |
| 918 | + // - `htmlTreeAsString` because it's complex, and just accessing the DOM incorrectly | |
| 919 | + // can throw an exception in some circumstances. | |
| 920 | + var target; | |
| 921 | + try { | |
| 922 | + target = htmlTreeAsString(evt.target); | |
| 923 | + } catch (e) { | |
| 924 | + target = '<unknown>'; | |
| 925 | + } | |
| 968 | 926 | |
| 969 | − self.captureBreadcrumb({ | |
| 970 | − category: 'ui.' + evtName, // e.g. ui.click, ui.input | |
| 971 | − message: target | |
| 972 | − }); | |
| 973 | − }; | |
| 974 | − }, | |
| 927 | + self.captureBreadcrumb({ | |
| 928 | + category: 'ui.' + evtName, // e.g. ui.click, ui.input | |
| 929 | + message: target | |
| 930 | + }); | |
| 931 | + }; | |
| 932 | + }, | |
| 975 | 933 | |
| 976 | − /** | |
| 934 | + /** | |
| 977 | 935 | * Wraps addEventListener to capture keypress UI events |
| 978 | 936 | * @returns {Function} |
| 979 | 937 | * @private |
| 980 | 938 | */ |
| 981 | − _keypressEventHandler: function() { | |
| 982 | − var self = this, | |
| 983 | − debounceDuration = 1000; // milliseconds | |
| 984 | − | |
| 985 | − // TODO: if somehow user switches keypress target before | |
| 986 | − // debounce timeout is triggered, we will only capture | |
| 987 | − // a single breadcrumb from the FIRST target (acceptable?) | |
| 988 | − return function(evt) { | |
| 989 | − var target; | |
| 990 | − try { | |
| 991 | − target = evt.target; | |
| 992 | − } catch (e) { | |
| 993 | − // just accessing event properties can throw an exception in some rare circumstances | |
| 994 | − // see: https://github.com/getsentry/raven-js/issues/838 | |
| 995 | − return; | |
| 996 | − } | |
| 997 | − var tagName = target && target.tagName; | |
| 998 | − | |
| 999 | − // only consider keypress events on actual input elements | |
| 1000 | − // this will disregard keypresses targeting body (e.g. tabbing | |
| 1001 | − // through elements, hotkeys, etc) | |
| 1002 | − if ( | |
| 1003 | − !tagName || | |
| 1004 | − (tagName !== 'INPUT' && | |
| 1005 | − tagName !== 'TEXTAREA' && | |
| 1006 | − !target.isContentEditable) | |
| 1007 | − ) | |
| 1008 | − return; | |
| 1009 | − | |
| 1010 | − // record first keypress in a series, but ignore subsequent | |
| 1011 | − // keypresses until debounce clears | |
| 1012 | − var timeout = self._keypressTimeout; | |
| 1013 | − if (!timeout) { | |
| 1014 | − self._breadcrumbEventHandler('input')(evt); | |
| 1015 | − } | |
| 1016 | − clearTimeout(timeout); | |
| 1017 | − self._keypressTimeout = setTimeout(function() { | |
| 1018 | − self._keypressTimeout = null; | |
| 1019 | − }, debounceDuration); | |
| 1020 | − }; | |
| 1021 | − }, | |
| 1022 | − | |
| 1023 | − /** | |
| 939 | + _keypressEventHandler: function() { | |
| 940 | + var self = this, | |
| 941 | + debounceDuration = 1000; // milliseconds | |
| 942 | + | |
| 943 | + // TODO: if somehow user switches keypress target before | |
| 944 | + // debounce timeout is triggered, we will only capture | |
| 945 | + // a single breadcrumb from the FIRST target (acceptable?) | |
| 946 | + return function(evt) { | |
| 947 | + var target; | |
| 948 | + try { | |
| 949 | + target = evt.target; | |
| 950 | + } catch (e) { | |
| 951 | + // just accessing event properties can throw an exception in some rare circumstances | |
| 952 | + // see: https://github.com/getsentry/raven-js/issues/838 | |
| 953 | + return; | |
| 954 | + } | |
| 955 | + var tagName = target && target.tagName; | |
| 956 | + | |
| 957 | + // only consider keypress events on actual input elements | |
| 958 | + // this will disregard keypresses targeting body (e.g. tabbing | |
| 959 | + // through elements, hotkeys, etc) | |
| 960 | + if ( | |
| 961 | + !tagName || | |
| 962 | + (tagName !== 'INPUT' && tagName !== 'TEXTAREA' && !target.isContentEditable) | |
| 963 | + ) | |
| 964 | + return; | |
| 965 | + | |
| 966 | + // record first keypress in a series, but ignore subsequent | |
| 967 | + // keypresses until debounce clears | |
| 968 | + var timeout = self._keypressTimeout; | |
| 969 | + if (!timeout) { | |
| 970 | + self._breadcrumbEventHandler('input')(evt); | |
| 971 | + } | |
| 972 | + clearTimeout(timeout); | |
| 973 | + self._keypressTimeout = setTimeout(function() { | |
| 974 | + self._keypressTimeout = null; | |
| 975 | + }, debounceDuration); | |
| 976 | + }; | |
| 977 | + }, | |
| 978 | + | |
| 979 | + /** | |
| 1024 | 980 | * Captures a breadcrumb of type "navigation", normalizing input URLs |
| 1025 | 981 | * @param to the originating URL |
| 1026 | 982 | * @param from the target URL |
| 1027 | 983 | * @private |
| 1028 | 984 | */ |
| 1029 | − _captureUrlChange: function(from, to) { | |
| 1030 | − var parsedLoc = parseUrl(this._location.href); | |
| 1031 | − var parsedTo = parseUrl(to); | |
| 1032 | − var parsedFrom = parseUrl(from); | |
| 1033 | − | |
| 1034 | − // because onpopstate only tells you the "new" (to) value of location.href, and | |
| 1035 | − // not the previous (from) value, we need to track the value of the current URL | |
| 1036 | − // state ourselves | |
| 1037 | − this._lastHref = to; | |
| 1038 | − | |
| 1039 | − // Use only the path component of the URL if the URL matches the current | |
| 1040 | − // document (almost all the time when using pushState) | |
| 1041 | − if ( | |
| 1042 | − parsedLoc.protocol === parsedTo.protocol && | |
| 1043 | − parsedLoc.host === parsedTo.host | |
| 1044 | − ) | |
| 1045 | − to = parsedTo.relative; | |
| 1046 | − if ( | |
| 1047 | − parsedLoc.protocol === parsedFrom.protocol && | |
| 1048 | − parsedLoc.host === parsedFrom.host | |
| 1049 | − ) | |
| 1050 | − from = parsedFrom.relative; | |
| 1051 | − | |
| 1052 | − this.captureBreadcrumb({ | |
| 1053 | − category: 'navigation', | |
| 1054 | − data: { | |
| 1055 | − to: to, | |
| 1056 | − from: from | |
| 1057 | − } | |
| 1058 | − }); | |
| 1059 | − }, | |
| 985 | + _captureUrlChange: function(from, to) { | |
| 986 | + var parsedLoc = parseUrl(this._location.href); | |
| 987 | + var parsedTo = parseUrl(to); | |
| 988 | + var parsedFrom = parseUrl(from); | |
| 989 | + | |
| 990 | + // because onpopstate only tells you the "new" (to) value of location.href, and | |
| 991 | + // not the previous (from) value, we need to track the value of the current URL | |
| 992 | + // state ourselves | |
| 993 | + this._lastHref = to; | |
| 994 | + | |
| 995 | + // Use only the path component of the URL if the URL matches the current | |
| 996 | + // document (almost all the time when using pushState) | |
| 997 | + if (parsedLoc.protocol === parsedTo.protocol && parsedLoc.host === parsedTo.host) | |
| 998 | + to = parsedTo.relative; | |
| 999 | + if (parsedLoc.protocol === parsedFrom.protocol && parsedLoc.host === parsedFrom.host) | |
| 1000 | + from = parsedFrom.relative; | |
| 1001 | + | |
| 1002 | + this.captureBreadcrumb({ | |
| 1003 | + category: 'navigation', | |
| 1004 | + data: { | |
| 1005 | + to: to, | |
| 1006 | + from: from | |
| 1007 | + } | |
| 1008 | + }); | |
| 1009 | + }, | |
| 1010 | + | |
| 1011 | + _patchFunctionToString: function() { | |
| 1012 | + var self = this; | |
| 1013 | + self._originalFunctionToString = Function.prototype.toString; | |
| 1014 | + // eslint-disable-next-line no-extend-native | |
| 1015 | + Function.prototype.toString = function() { | |
| 1016 | + if (typeof this === 'function' && this.__raven__) { | |
| 1017 | + return self._originalFunctionToString.apply(this.__orig__, arguments); | |
| 1018 | + } | |
| 1019 | + return self._originalFunctionToString.apply(this, arguments); | |
| 1020 | + }; | |
| 1021 | + }, | |
| 1022 | + | |
| 1023 | + _unpatchFunctionToString: function() { | |
| 1024 | + if (this._originalFunctionToString) { | |
| 1025 | + // eslint-disable-next-line no-extend-native | |
| 1026 | + Function.prototype.toString = this._originalFunctionToString; | |
| 1027 | + } | |
| 1028 | + }, | |
| 1060 | 1029 | |
| 1061 | − /** | |
| 1030 | + /** | |
| 1062 | 1031 | * Wrap timer functions and event targets to catch errors and provide |
| 1063 | 1032 | * better metadata. |
| 1064 | 1033 | */ |
| 1065 | − _instrumentTryCatch: function() { | |
| 1066 | − var self = this; | |
| 1067 | − | |
| 1068 | − var wrappedBuiltIns = self._wrappedBuiltIns; | |
| 1069 | − | |
| 1070 | − function wrapTimeFn(orig) { | |
| 1071 | − return function(fn, t) { | |
| 1072 | − // preserve arity | |
| 1073 | − // Make a copy of the arguments to prevent deoptimization | |
| 1074 | − // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments | |
| 1075 | − var args = new Array(arguments.length); | |
| 1076 | − for (var i = 0; i < args.length; ++i) { | |
| 1077 | − args[i] = arguments[i]; | |
| 1078 | − } | |
| 1079 | − var originalCallback = args[0]; | |
| 1080 | − if (isFunction(originalCallback)) { | |
| 1081 | − args[0] = self.wrap(originalCallback); | |
| 1082 | − } | |
| 1083 | − | |
| 1084 | − // IE < 9 doesn't support .call/.apply on setInterval/setTimeout, but it | |
| 1085 | − // also supports only two arguments and doesn't care what this is, so we | |
| 1086 | − // can just call the original function directly. | |
| 1087 | − if (orig.apply) { | |
| 1088 | − return orig.apply(this, args); | |
| 1089 | − } else { | |
| 1090 | − return orig(args[0], args[1]); | |
| 1091 | − } | |
| 1092 | − }; | |
| 1093 | − } | |
| 1034 | + _instrumentTryCatch: function() { | |
| 1035 | + var self = this; | |
| 1036 | + | |
| 1037 | + var wrappedBuiltIns = self._wrappedBuiltIns; | |
| 1038 | + | |
| 1039 | + function wrapTimeFn(orig) { | |
| 1040 | + return function(fn, t) { | |
| 1041 | + // preserve arity | |
| 1042 | + // Make a copy of the arguments to prevent deoptimization | |
| 1043 | + // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments | |
| 1044 | + var args = new Array(arguments.length); | |
| 1045 | + for (var i = 0; i < args.length; ++i) { | |
| 1046 | + args[i] = arguments[i]; | |
| 1047 | + } | |
| 1048 | + var originalCallback = args[0]; | |
| 1049 | + if (isFunction(originalCallback)) { | |
| 1050 | + args[0] = self.wrap(originalCallback); | |
| 1051 | + } | |
| 1052 | + | |
| 1053 | + // IE < 9 doesn't support .call/.apply on setInterval/setTimeout, but it | |
| 1054 | + // also supports only two arguments and doesn't care what this is, so we | |
| 1055 | + // can just call the original function directly. | |
| 1056 | + if (orig.apply) { | |
| 1057 | + return orig.apply(this, args); | |
| 1058 | + } else { | |
| 1059 | + return orig(args[0], args[1]); | |
| 1060 | + } | |
| 1061 | + }; | |
| 1062 | + } | |
| 1063 | + | |
| 1064 | + var autoBreadcrumbs = this._globalOptions.autoBreadcrumbs; | |
| 1065 | + | |
| 1066 | + function wrapEventTarget(global) { | |
| 1067 | + var proto = _window[global] && _window[global].prototype; | |
| 1068 | + if (proto && proto.hasOwnProperty && proto.hasOwnProperty('addEventListener')) { | |
| 1069 | + fill( | |
| 1070 | + proto, | |
| 1071 | + 'addEventListener', | |
| 1072 | + function(orig) { | |
| 1073 | + return function(evtName, fn, capture, secure) { | |
| 1074 | + // preserve arity | |
| 1075 | + try { | |
| 1076 | + if (fn && fn.handleEvent) { | |
| 1077 | + fn.handleEvent = self.wrap(fn.handleEvent); | |
| 1078 | + } | |
| 1079 | + } catch (err) { | |
| 1080 | + // can sometimes get 'Permission denied to access property "handle Event' | |
| 1081 | + } | |
| 1094 | 1082 | |
| 1095 | − var autoBreadcrumbs = this._globalOptions.autoBreadcrumbs; | |
| 1096 | − | |
| 1097 | − function wrapEventTarget(global) { | |
| 1098 | − var proto = _window[global] && _window[global].prototype; | |
| 1099 | − if ( | |
| 1100 | − proto && | |
| 1101 | − proto.hasOwnProperty && | |
| 1102 | − proto.hasOwnProperty('addEventListener') | |
| 1103 | − ) { | |
| 1104 | − fill( | |
| 1105 | − proto, | |
| 1106 | − 'addEventListener', | |
| 1107 | − function(orig) { | |
| 1108 | − return function(evtName, fn, capture, secure) { | |
| 1109 | − // preserve arity | |
| 1110 | − try { | |
| 1111 | − if (fn && fn.handleEvent) { | |
| 1112 | − fn.handleEvent = self.wrap(fn.handleEvent); | |
| 1113 | − } | |
| 1114 | − } catch (err) { | |
| 1115 | − // can sometimes get 'Permission denied to access property "handle Event' | |
| 1116 | − } | |
| 1117 | − | |
| 1118 | − // More breadcrumb DOM capture ... done here and not in `_instrumentBreadcrumbs` | |
| 1119 | − // so that we don't have more than one wrapper function | |
| 1120 | − var before, clickHandler, keypressHandler; | |
| 1121 | − | |
| 1122 | − if ( | |
| 1123 | − autoBreadcrumbs && | |
| 1124 | − autoBreadcrumbs.dom && | |
| 1125 | − (global === 'EventTarget' || global === 'Node') | |
| 1126 | − ) { | |
| 1127 | − // NOTE: generating multiple handlers per addEventListener invocation, should | |
| 1128 | − // revisit and verify we can just use one (almost certainly) | |
| 1129 | − clickHandler = self._breadcrumbEventHandler('click'); | |
| 1130 | − keypressHandler = self._keypressEventHandler(); | |
| 1131 | − before = function(evt) { | |
| 1132 | − // need to intercept every DOM event in `before` argument, in case that | |
| 1133 | − // same wrapped method is re-used for different events (e.g. mousemove THEN click) | |
| 1134 | − // see #724 | |
| 1135 | − if (!evt) return; | |
| 1136 | − | |
| 1137 | − var eventType; | |
| 1138 | − try { | |
| 1139 | − eventType = evt.type; | |
| 1140 | − } catch (e) { | |
| 1141 | − // just accessing event properties can throw an exception in some rare circumstances | |
| 1142 | − // see: https://github.com/getsentry/raven-js/issues/838 | |
| 1143 | − return; | |
| 1144 | − } | |
| 1145 | − if (eventType === 'click') return clickHandler(evt); | |
| 1146 | − else if (eventType === 'keypress') | |
| 1147 | − return keypressHandler(evt); | |
| 1148 | − }; | |
| 1149 | − } | |
| 1150 | − return orig.call( | |
| 1151 | − this, | |
| 1152 | − evtName, | |
| 1153 | − self.wrap(fn, undefined, before), | |
| 1154 | − capture, | |
| 1155 | − secure | |
| 1156 | − ); | |
| 1157 | − }; | |
| 1158 | − }, | |
| 1159 | − wrappedBuiltIns | |
| 1160 | − ); | |
| 1161 | − fill( | |
| 1162 | − proto, | |
| 1163 | − 'removeEventListener', | |
| 1164 | − function(orig) { | |
| 1165 | − return function(evt, fn, capture, secure) { | |
| 1166 | − try { | |
| 1167 | − fn = fn && (fn.__raven_wrapper__ ? fn.__raven_wrapper__ : fn); | |
| 1168 | − } catch (e) { | |
| 1169 | − // ignore, accessing __raven_wrapper__ will throw in some Selenium environments | |
| 1170 | − } | |
| 1171 | − return orig.call(this, evt, fn, capture, secure); | |
| 1172 | − }; | |
| 1173 | − }, | |
| 1174 | − wrappedBuiltIns | |
| 1175 | − ); | |
| 1083 | + // More breadcrumb DOM capture ... done here and not in `_instrumentBreadcrumbs` | |
| 1084 | + // so that we don't have more than one wrapper function | |
| 1085 | + var before, clickHandler, keypressHandler; | |
| 1086 | + | |
| 1087 | + if ( | |
| 1088 | + autoBreadcrumbs && | |
| 1089 | + autoBreadcrumbs.dom && | |
| 1090 | + (global === 'EventTarget' || global === 'Node') | |
| 1091 | + ) { | |
| 1092 | + // NOTE: generating multiple handlers per addEventListener invocation, should | |
| 1093 | + // revisit and verify we can just use one (almost certainly) | |
| 1094 | + clickHandler = self._breadcrumbEventHandler('click'); | |
| 1095 | + keypressHandler = self._keypressEventHandler(); | |
| 1096 | + before = function(evt) { | |
| 1097 | + // need to intercept every DOM event in `before` argument, in case that | |
| 1098 | + // same wrapped method is re-used for different events (e.g. mousemove THEN click) | |
| 1099 | + // see #724 | |
| 1100 | + if (!evt) return; | |
| 1101 | + | |
| 1102 | + var eventType; | |
| 1103 | + try { | |
| 1104 | + eventType = evt.type; | |
| 1105 | + } catch (e) { | |
| 1106 | + // just accessing event properties can throw an exception in some rare circumstances | |
| 1107 | + // see: https://github.com/getsentry/raven-js/issues/838 | |
| 1108 | + return; | |
| 1176 | 1109 | } |
| 1177 | − } | |
| 1110 | + if (eventType === 'click') return clickHandler(evt); | |
| 1111 | + else if (eventType === 'keypress') return keypressHandler(evt); | |
| 1112 | + }; | |
| 1113 | + } | |
| 1114 | + return orig.call( | |
| 1115 | + this, | |
| 1116 | + evtName, | |
| 1117 | + self.wrap(fn, undefined, before), | |
| 1118 | + capture, | |
| 1119 | + secure | |
| 1120 | + ); | |
| 1121 | + }; | |
| 1122 | + }, | |
| 1123 | + wrappedBuiltIns | |
| 1124 | + ); | |
| 1125 | + fill( | |
| 1126 | + proto, | |
| 1127 | + 'removeEventListener', | |
| 1128 | + function(orig) { | |
| 1129 | + return function(evt, fn, capture, secure) { | |
| 1130 | + try { | |
| 1131 | + fn = fn && (fn.__raven_wrapper__ ? fn.__raven_wrapper__ : fn); | |
| 1132 | + } catch (e) { | |
| 1133 | + // ignore, accessing __raven_wrapper__ will throw in some Selenium environments | |
| 1134 | + } | |
| 1135 | + return orig.call(this, evt, fn, capture, secure); | |
| 1136 | + }; | |
| 1137 | + }, | |
| 1138 | + wrappedBuiltIns | |
| 1139 | + ); | |
| 1140 | + } | |
| 1141 | + } | |
| 1178 | 1142 | |
| 1179 | − fill(_window, 'setTimeout', wrapTimeFn, wrappedBuiltIns); | |
| 1180 | − fill(_window, 'setInterval', wrapTimeFn, wrappedBuiltIns); | |
| 1181 | − if (_window.requestAnimationFrame) { | |
| 1182 | − fill( | |
| 1183 | − _window, | |
| 1184 | − 'requestAnimationFrame', | |
| 1185 | − function(orig) { | |
| 1186 | − return function(cb) { | |
| 1187 | − return orig(self.wrap(cb)); | |
| 1188 | − }; | |
| 1189 | − }, | |
| 1190 | − wrappedBuiltIns | |
| 1191 | − ); | |
| 1192 | − } | |
| 1143 | + fill(_window, 'setTimeout', wrapTimeFn, wrappedBuiltIns); | |
| 1144 | + fill(_window, 'setInterval', wrapTimeFn, wrappedBuiltIns); | |
| 1145 | + if (_window.requestAnimationFrame) { | |
| 1146 | + fill( | |
| 1147 | + _window, | |
| 1148 | + 'requestAnimationFrame', | |
| 1149 | + function(orig) { | |
| 1150 | + return function(cb) { | |
| 1151 | + return orig(self.wrap(cb)); | |
| 1152 | + }; | |
| 1153 | + }, | |
| 1154 | + wrappedBuiltIns | |
| 1155 | + ); | |
| 1156 | + } | |
| 1193 | 1157 | |
| 1194 | − // event targets borrowed from bugsnag-js: | |
| 1195 | − // https://github.com/bugsnag/bugsnag-js/blob/master/src/bugsnag.js#L666 | |
| 1196 | − var eventTargets = [ | |
| 1197 | − 'EventTarget', | |
| 1198 | − 'Window', | |
| 1199 | − 'Node', | |
| 1200 | − 'ApplicationCache', | |
| 1201 | − 'AudioTrackList', | |
| 1202 | − 'ChannelMergerNode', | |
| 1203 | − 'CryptoOperation', | |
| 1204 | − 'EventSource', | |
| 1205 | − 'FileReader', | |
| 1206 | − 'HTMLUnknownElement', | |
| 1207 | − 'IDBDatabase', | |
| 1208 | − 'IDBRequest', | |
| 1209 | − 'IDBTransaction', | |
| 1210 | − 'KeyOperation', | |
| 1211 | − 'MediaController', | |
| 1212 | − 'MessagePort', | |
| 1213 | − 'ModalWindow', | |
| 1214 | − 'Notification', | |
| 1215 | − 'SVGElementInstance', | |
| 1216 | − 'Screen', | |
| 1217 | − 'TextTrack', | |
| 1218 | − 'TextTrackCue', | |
| 1219 | − 'TextTrackList', | |
| 1220 | − 'WebSocket', | |
| 1221 | − 'WebSocketWorker', | |
| 1222 | − 'Worker', | |
| 1223 | − 'XMLHttpRequest', | |
| 1224 | − 'XMLHttpRequestEventTarget', | |
| 1225 | − 'XMLHttpRequestUpload' | |
| 1226 | − ]; | |
| 1227 | − for (var i = 0; i < eventTargets.length; i++) { | |
| 1228 | − wrapEventTarget(eventTargets[i]); | |
| 1229 | − } | |
| 1230 | − }, | |
| 1158 | + // event targets borrowed from bugsnag-js: | |
| 1159 | + // https://github.com/bugsnag/bugsnag-js/blob/master/src/bugsnag.js#L666 | |
| 1160 | + var eventTargets = [ | |
| 1161 | + 'EventTarget', | |
| 1162 | + 'Window', | |
| 1163 | + 'Node', | |
| 1164 | + 'ApplicationCache', | |
| 1165 | + 'AudioTrackList', | |
| 1166 | + 'ChannelMergerNode', | |
| 1167 | + 'CryptoOperation', | |
| 1168 | + 'EventSource', | |
| 1169 | + 'FileReader', | |
| 1170 | + 'HTMLUnknownElement', | |
| 1171 | + 'IDBDatabase', | |
| 1172 | + 'IDBRequest', | |
| 1173 | + 'IDBTransaction', | |
| 1174 | + 'KeyOperation', | |
| 1175 | + 'MediaController', | |
| 1176 | + 'MessagePort', | |
| 1177 | + 'ModalWindow', | |
| 1178 | + 'Notification', | |
| 1179 | + 'SVGElementInstance', | |
| 1180 | + 'Screen', | |
| 1181 | + 'TextTrack', | |
| 1182 | + 'TextTrackCue', | |
| 1183 | + 'TextTrackList', | |
| 1184 | + 'WebSocket', | |
| 1185 | + 'WebSocketWorker', | |
| 1186 | + 'Worker', | |
| 1187 | + 'XMLHttpRequest', | |
| 1188 | + 'XMLHttpRequestEventTarget', | |
| 1189 | + 'XMLHttpRequestUpload' | |
| 1190 | + ]; | |
| 1191 | + for (var i = 0; i < eventTargets.length; i++) { | |
| 1192 | + wrapEventTarget(eventTargets[i]); | |
| 1193 | + } | |
| 1194 | + }, | |
| 1231 | 1195 | |
| 1232 | − /** | |
| 1196 | + /** | |
| 1233 | 1197 | * Instrument browser built-ins w/ breadcrumb capturing |
| 1234 | 1198 | * - XMLHttpRequests |
| 1235 | 1199 | * - DOM interactions (click/typing) |
| @@ -1238,546 +1202,515 @@ · 2 unmodified lines | ||
| 1238 | 1202 | * |
| 1239 | 1203 | * Can be disabled or individually configured via the `autoBreadcrumbs` config option |
| 1240 | 1204 | */ |
| 1241 | − _instrumentBreadcrumbs: function() { | |
| 1242 | − var self = this; | |
| 1243 | − var autoBreadcrumbs = this._globalOptions.autoBreadcrumbs; | |
| 1205 | + _instrumentBreadcrumbs: function() { | |
| 1206 | + var self = this; | |
| 1207 | + var autoBreadcrumbs = this._globalOptions.autoBreadcrumbs; | |
| 1244 | 1208 | |
| 1245 | − var wrappedBuiltIns = self._wrappedBuiltIns; | |
| 1209 | + var wrappedBuiltIns = self._wrappedBuiltIns; | |
| 1246 | 1210 | |
| 1247 | − function wrapProp(prop, xhr) { | |
| 1248 | − if (prop in xhr && isFunction(xhr[prop])) { | |
| 1249 | − fill(xhr, prop, function(orig) { | |
| 1250 | − return self.wrap(orig); | |
| 1251 | − }); // intentionally don't track filled methods on XHR instances | |
| 1252 | − } | |
| 1253 | − } | |
| 1211 | + function wrapProp(prop, xhr) { | |
| 1212 | + if (prop in xhr && isFunction(xhr[prop])) { | |
| 1213 | + fill(xhr, prop, function(orig) { | |
| 1214 | + return self.wrap(orig); | |
| 1215 | + }); // intentionally don't track filled methods on XHR instances | |
| 1216 | + } | |
| 1217 | + } | |
| 1254 | 1218 | |
| 1255 | − if (autoBreadcrumbs.xhr && 'XMLHttpRequest' in _window) { | |
| 1256 | − var xhrproto = XMLHttpRequest.prototype; | |
| 1257 | − fill( | |
| 1258 | − xhrproto, | |
| 1259 | − 'open', | |
| 1260 | − function(origOpen) { | |
| 1261 | − return function(method, url) { | |
| 1262 | − // preserve arity | |
| 1263 | − | |
| 1264 | − // if Sentry key appears in URL, don't capture | |
| 1265 | − if (isString(url) && url.indexOf(self._globalKey) === -1) { | |
| 1266 | − this.__raven_xhr = { | |
| 1267 | − method: method, | |
| 1268 | − url: url, | |
| 1269 | − status_code: null | |
| 1270 | − }; | |
| 1271 | − } | |
| 1272 | − | |
| 1273 | − return origOpen.apply(this, arguments); | |
| 1274 | − }; | |
| 1275 | − }, | |
| 1276 | − wrappedBuiltIns | |
| 1277 | − ); | |
| 1278 | − | |
| 1279 | − fill( | |
| 1280 | − xhrproto, | |
| 1281 | − 'send', | |
| 1282 | − function(origSend) { | |
| 1283 | − return function(data) { | |
| 1284 | − // preserve arity | |
| 1285 | − var xhr = this; | |
| 1286 | − | |
| 1287 | − function onreadystatechangeHandler() { | |
| 1288 | − if (xhr.__raven_xhr && xhr.readyState === 4) { | |
| 1289 | − try { | |
| 1290 | − // touching statusCode in some platforms throws | |
| 1291 | − // an exception | |
| 1292 | − xhr.__raven_xhr.status_code = xhr.status; | |
| 1293 | − } catch (e) { | |
| 1294 | − /* do nothing */ | |
| 1295 | − } | |
| 1296 | − | |
| 1297 | − self.captureBreadcrumb({ | |
| 1298 | − type: 'http', | |
| 1299 | − category: 'xhr', | |
| 1300 | − data: xhr.__raven_xhr | |
| 1301 | − }); | |
| 1302 | − } | |
| 1303 | − } | |
| 1304 | − | |
| 1305 | − var props = ['onload', 'onerror', 'onprogress']; | |
| 1306 | − for (var j = 0; j < props.length; j++) { | |
| 1307 | − wrapProp(props[j], xhr); | |
| 1308 | − } | |
| 1309 | − | |
| 1310 | − if ( | |
| 1311 | − 'onreadystatechange' in xhr && | |
| 1312 | − isFunction(xhr.onreadystatechange) | |
| 1313 | − ) { | |
| 1314 | − fill( | |
| 1315 | − xhr, | |
| 1316 | − 'onreadystatechange', | |
| 1317 | − function(orig) { | |
| 1318 | − return self.wrap( | |
| 1319 | − orig, | |
| 1320 | − undefined, | |
| 1321 | − onreadystatechangeHandler | |
| 1322 | − ); | |
| 1323 | − } /* intentionally don't track this instrumentation */ | |
| 1324 | − ); | |
| 1325 | − } else { | |
| 1326 | − // if onreadystatechange wasn't actually set by the page on this xhr, we | |
| 1327 | − // are free to set our own and capture the breadcrumb | |
| 1328 | − xhr.onreadystatechange = onreadystatechangeHandler; | |
| 1329 | − } | |
| 1330 | − | |
| 1331 | − return origSend.apply(this, arguments); | |
| 1332 | − }; | |
| 1333 | − }, | |
| 1334 | − wrappedBuiltIns | |
| 1335 | − ); | |
| 1336 | − } | |
| 1219 | + if (autoBreadcrumbs.xhr && 'XMLHttpRequest' in _window) { | |
| 1220 | + var xhrproto = XMLHttpRequest.prototype; | |
| 1221 | + fill( | |
| 1222 | + xhrproto, | |
| 1223 | + 'open', | |
| 1224 | + function(origOpen) { | |
| 1225 | + return function(method, url) { | |
| 1226 | + // preserve arity | |
| 1227 | + | |
| 1228 | + // if Sentry key appears in URL, don't capture | |
| 1229 | + if (isString(url) && url.indexOf(self._globalKey) === -1) { | |
| 1230 | + this.__raven_xhr = { | |
| 1231 | + method: method, | |
| 1232 | + url: url, | |
| 1233 | + status_code: null | |
| 1234 | + }; | |
| 1235 | + } | |
| 1337 | 1236 | |
| 1338 | − if (autoBreadcrumbs.xhr && 'fetch' in _window) { | |
| 1339 | − fill( | |
| 1340 | − _window, | |
| 1341 | − 'fetch', | |
| 1342 | − function(origFetch) { | |
| 1343 | − return function(fn, t) { | |
| 1344 | − // preserve arity | |
| 1345 | − // Make a copy of the arguments to prevent deoptimization | |
| 1346 | − // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments | |
| 1347 | − var args = new Array(arguments.length); | |
| 1348 | − for (var i = 0; i < args.length; ++i) { | |
| 1349 | − args[i] = arguments[i]; | |
| 1350 | − } | |
| 1351 | − | |
| 1352 | − var fetchInput = args[0]; | |
| 1353 | − var method = 'GET'; | |
| 1354 | − var url; | |
| 1355 | − | |
| 1356 | − if (typeof fetchInput === 'string') { | |
| 1357 | − url = fetchInput; | |
| 1358 | − } else { | |
| 1359 | − url = fetchInput.url; | |
| 1360 | − if (fetchInput.method) { | |
| 1361 | − method = fetchInput.method; | |
| 1362 | − } | |
| 1363 | − } | |
| 1364 | − | |
| 1365 | − if (args[1] && args[1].method) { | |
| 1366 | − method = args[1].method; | |
| 1367 | − } | |
| 1368 | − | |
| 1369 | − var fetchData = { | |
| 1370 | − method: method, | |
| 1371 | − url: url, | |
| 1372 | − status_code: null | |
| 1373 | − }; | |
| 1374 | − | |
| 1375 | − self.captureBreadcrumb({ | |
| 1376 | − type: 'http', | |
| 1377 | − category: 'fetch', | |
| 1378 | − data: fetchData | |
| 1379 | − }); | |
| 1380 | − | |
| 1381 | − return origFetch.apply(this, args).then(function(response) { | |
| 1382 | − fetchData.status_code = response.status; | |
| 1383 | − | |
| 1384 | − return response; | |
| 1385 | − }); | |
| 1386 | − }; | |
| 1387 | − }, | |
| 1388 | − wrappedBuiltIns | |
| 1389 | − ); | |
| 1237 | + return origOpen.apply(this, arguments); | |
| 1238 | + }; | |
| 1239 | + }, | |
| 1240 | + wrappedBuiltIns | |
| 1241 | + ); | |
| 1242 | + | |
| 1243 | + fill( | |
| 1244 | + xhrproto, | |
| 1245 | + 'send', | |
| 1246 | + function(origSend) { | |
| 1247 | + return function(data) { | |
| 1248 | + // preserve arity | |
| 1249 | + var xhr = this; | |
| 1250 | + | |
| 1251 | + function onreadystatechangeHandler() { | |
| 1252 | + if (xhr.__raven_xhr && xhr.readyState === 4) { | |
| 1253 | + try { | |
| 1254 | + // touching statusCode in some platforms throws | |
| 1255 | + // an exception | |
| 1256 | + xhr.__raven_xhr.status_code = xhr.status; | |
| 1257 | + } catch (e) { | |
| 1258 | + /* do nothing */ | |
| 1390 | 1259 | } |
| 1391 | 1260 | |
| 1392 | − // Capture breadcrumbs from any click that is unhandled / bubbled up all the way | |
| 1393 | − // to the document. Do this before we instrument addEventListener. | |
| 1394 | − if (autoBreadcrumbs.dom && this._hasDocument) { | |
| 1395 | − if (_document.addEventListener) { | |
| 1396 | − _document.addEventListener( | |
| 1397 | − 'click', | |
| 1398 | − self._breadcrumbEventHandler('click'), | |
| 1399 | − false | |
| 1400 | − ); | |
| 1401 | − _document.addEventListener( | |
| 1402 | − 'keypress', | |
| 1403 | − self._keypressEventHandler(), | |
| 1404 | − false | |
| 1405 | − ); | |
| 1406 | − } else { | |
| 1407 | − // IE8 Compatibility | |
| 1408 | − _document.attachEvent( | |
| 1409 | − 'onclick', | |
| 1410 | − self._breadcrumbEventHandler('click') | |
| 1411 | − ); | |
| 1412 | − _document.attachEvent('onkeypress', self._keypressEventHandler()); | |
| 1413 | − } | |
| 1414 | − } | |
| 1261 | + self.captureBreadcrumb({ | |
| 1262 | + type: 'http', | |
| 1263 | + category: 'xhr', | |
| 1264 | + data: xhr.__raven_xhr | |
| 1265 | + }); | |
| 1266 | + } | |
| 1267 | + } | |
| 1415 | 1268 | |
| 1416 | − // record navigation (URL) changes | |
| 1417 | − // NOTE: in Chrome App environment, touching history.pushState, *even inside | |
| 1418 | − // a try/catch block*, will cause Chrome to output an error to console.error | |
| 1419 | − // borrowed from: https://github.com/angular/angular.js/pull/13945/files | |
| 1420 | − var chrome = _window.chrome; | |
| 1421 | − var isChromePackagedApp = chrome && chrome.app && chrome.app.runtime; | |
| 1422 | − var hasPushState = | |
| 1423 | − !isChromePackagedApp && _window.history && history.pushState; | |
| 1424 | − if (autoBreadcrumbs.location && hasPushState) { | |
| 1425 | − // TODO: remove onpopstate handler on uninstall() | |
| 1426 | − var oldOnPopState = _window.onpopstate; | |
| 1427 | − _window.onpopstate = function() { | |
| 1428 | − var currentHref = self._location.href; | |
| 1429 | − self._captureUrlChange(self._lastHref, currentHref); | |
| 1430 | − | |
| 1431 | − if (oldOnPopState) { | |
| 1432 | − return oldOnPopState.apply(this, arguments); | |
| 1433 | − } | |
| 1434 | − }; | |
| 1435 | − | |
| 1436 | − fill( | |
| 1437 | − history, | |
| 1438 | − 'pushState', | |
| 1439 | − function(origPushState) { | |
| 1440 | − // note history.pushState.length is 0; intentionally not declaring | |
| 1441 | − // params to preserve 0 arity | |
| 1442 | − return function(/* state, title, url */) { | |
| 1443 | − var url = arguments.length > 2 ? arguments[2] : undefined; | |
| 1444 | − | |
| 1445 | − // url argument is optional | |
| 1446 | − if (url) { | |
| 1447 | − // coerce to string (this is what pushState does) | |
| 1448 | − self._captureUrlChange(self._lastHref, url + ''); | |
| 1449 | − } | |
| 1450 | − | |
| 1451 | − return origPushState.apply(this, arguments); | |
| 1452 | − }; | |
| 1453 | − }, | |
| 1454 | − wrappedBuiltIns | |
| 1455 | − ); | |
| 1456 | − } | |
| 1269 | + var props = ['onload', 'onerror', 'onprogress']; | |
| 1270 | + for (var j = 0; j < props.length; j++) { | |
| 1271 | + wrapProp(props[j], xhr); | |
| 1272 | + } | |
| 1457 | 1273 | |
| 1458 | − if (autoBreadcrumbs.console && 'console' in _window && console.log) { | |
| 1459 | − // console | |
| 1460 | − var consoleMethodCallback = function(msg, data) { | |
| 1461 | − self.captureBreadcrumb({ | |
| 1462 | − message: msg, | |
| 1463 | − level: data.level, | |
| 1464 | − category: 'console' | |
| 1465 | − }); | |
| 1466 | − }; | |
| 1467 | − | |
| 1468 | − each(['debug', 'info', 'warn', 'error', 'log'], function(_, level) { | |
| 1469 | − wrapConsoleMethod(console, level, consoleMethodCallback); | |
| 1470 | − }); | |
| 1471 | − } | |
| 1472 | − }, | |
| 1274 | + if ('onreadystatechange' in xhr && isFunction(xhr.onreadystatechange)) { | |
| 1275 | + fill( | |
| 1276 | + xhr, | |
| 1277 | + 'onreadystatechange', | |
| 1278 | + function(orig) { | |
| 1279 | + return self.wrap(orig, undefined, onreadystatechangeHandler); | |
| 1280 | + } /* intentionally don't track this instrumentation */ | |
| 1281 | + ); | |
| 1282 | + } else { | |
| 1283 | + // if onreadystatechange wasn't actually set by the page on this xhr, we | |
| 1284 | + // are free to set our own and capture the breadcrumb | |
| 1285 | + xhr.onreadystatechange = onreadystatechangeHandler; | |
| 1286 | + } | |
| 1287 | + | |
| 1288 | + return origSend.apply(this, arguments); | |
| 1289 | + }; | |
| 1290 | + }, | |
| 1291 | + wrappedBuiltIns | |
| 1292 | + ); | |
| 1293 | + } | |
| 1473 | 1294 | |
| 1474 | − _restoreBuiltIns: function() { | |
| 1475 | − // restore any wrapped builtins | |
| 1476 | − var builtin; | |
| 1477 | − while (this._wrappedBuiltIns.length) { | |
| 1478 | − builtin = this._wrappedBuiltIns.shift(); | |
| 1295 | + if (autoBreadcrumbs.xhr && 'fetch' in _window) { | |
| 1296 | + fill( | |
| 1297 | + _window, | |
| 1298 | + 'fetch', | |
| 1299 | + function(origFetch) { | |
| 1300 | + return function(fn, t) { | |
| 1301 | + // preserve arity | |
| 1302 | + // Make a copy of the arguments to prevent deoptimization | |
| 1303 | + // https://github.com/petkaantonov/bluebird/wiki/Optimization-killers#32-leaking-arguments | |
| 1304 | + var args = new Array(arguments.length); | |
| 1305 | + for (var i = 0; i < args.length; ++i) { | |
| 1306 | + args[i] = arguments[i]; | |
| 1307 | + } | |
| 1479 | 1308 | |
| 1480 | − var obj = builtin[0], | |
| 1481 | − name = builtin[1], | |
| 1482 | − orig = builtin[2]; | |
| 1309 | + var fetchInput = args[0]; | |
| 1310 | + var method = 'GET'; | |
| 1311 | + var url; | |
| 1483 | 1312 | |
| 1484 | − obj[name] = orig; | |
| 1485 | − } | |
| 1486 | − }, | |
| 1313 | + if (typeof fetchInput === 'string') { | |
| 1314 | + url = fetchInput; | |
| 1315 | + } else if ('Request' in _window && fetchInput instanceof _window.Request) { | |
| 1316 | + url = fetchInput.url; | |
| 1317 | + if (fetchInput.method) { | |
| 1318 | + method = fetchInput.method; | |
| 1319 | + } | |
| 1320 | + } else { | |
| 1321 | + url = '' + fetchInput; | |
| 1322 | + } | |
| 1487 | 1323 | |
| 1488 | − _drainPlugins: function() { | |
| 1489 | − var self = this; | |
| 1324 | + if (args[1] && args[1].method) { | |
| 1325 | + method = args[1].method; | |
| 1326 | + } | |
| 1490 | 1327 | |
| 1491 | − // FIX ME TODO | |
| 1492 | − each(this._plugins, function(_, plugin) { | |
| 1493 | − var installer = plugin[0]; | |
| 1494 | − var args = plugin[1]; | |
| 1495 | − installer.apply(self, [self].concat(args)); | |
| 1496 | − }); | |
| 1497 | − }, | |
| 1328 | + var fetchData = { | |
| 1329 | + method: method, | |
| 1330 | + url: url, | |
| 1331 | + status_code: null | |
| 1332 | + }; | |
| 1498 | 1333 | |
| 1499 | − _parseDSN: function(str) { | |
| 1500 | − var m = dsnPattern.exec(str), | |
| 1501 | − dsn = {}, | |
| 1502 | − i = 7; | |
| 1334 | + self.captureBreadcrumb({ | |
| 1335 | + type: 'http', | |
| 1336 | + category: 'fetch', | |
| 1337 | + data: fetchData | |
| 1338 | + }); | |
| 1503 | 1339 | |
| 1504 | − try { | |
| 1505 | − while (i--) dsn[dsnKeys[i]] = m[i] || ''; | |
| 1506 | − } catch (e) { | |
| 1507 | − throw new RavenConfigError('Invalid DSN: ' + str); | |
| 1508 | − } | |
| 1340 | + return origFetch.apply(this, args).then(function(response) { | |
| 1341 | + fetchData.status_code = response.status; | |
| 1509 | 1342 | |
| 1510 | − if (dsn.pass && !this._globalOptions.allowSecretKey) { | |
| 1511 | − throw new RavenConfigError( | |
| 1512 | − 'Do not specify your secret key in the DSN. See: http://bit.ly/raven-secret-key' | |
| 1513 | − ); | |
| 1514 | − } | |
| 1343 | + return response; | |
| 1344 | + }); | |
| 1345 | + }; | |
| 1346 | + }, | |
| 1347 | + wrappedBuiltIns | |
| 1348 | + ); | |
| 1349 | + } | |
| 1515 | 1350 | |
| 1516 | − return dsn; | |
| 1517 | − }, | |
| 1351 | + // Capture breadcrumbs from any click that is unhandled / bubbled up all the way | |
| 1352 | + // to the document. Do this before we instrument addEventListener. | |
| 1353 | + if (autoBreadcrumbs.dom && this._hasDocument) { | |
| 1354 | + if (_document.addEventListener) { | |
| 1355 | + _document.addEventListener('click', self._breadcrumbEventHandler('click'), false); | |
| 1356 | + _document.addEventListener('keypress', self._keypressEventHandler(), false); | |
| 1357 | + } else { | |
| 1358 | + // IE8 Compatibility | |
| 1359 | + _document.attachEvent('onclick', self._breadcrumbEventHandler('click')); | |
| 1360 | + _document.attachEvent('onkeypress', self._keypressEventHandler()); | |
| 1361 | + } | |
| 1362 | + } | |
| 1518 | 1363 | |
| 1519 | − _getGlobalServer: function(uri) { | |
| 1520 | − // assemble the endpoint from the uri pieces | |
| 1521 | − var globalServer = '//' + uri.host + (uri.port ? ':' + uri.port : ''); | |
| 1364 | + // record navigation (URL) changes | |
| 1365 | + // NOTE: in Chrome App environment, touching history.pushState, *even inside | |
| 1366 | + // a try/catch block*, will cause Chrome to output an error to console.error | |
| 1367 | + // borrowed from: https://github.com/angular/angular.js/pull/13945/files | |
| 1368 | + var chrome = _window.chrome; | |
| 1369 | + var isChromePackagedApp = chrome && chrome.app && chrome.app.runtime; | |
| 1370 | + var hasPushAndReplaceState = | |
| 1371 | + !isChromePackagedApp && | |
| 1372 | + _window.history && | |
| 1373 | + history.pushState && | |
| 1374 | + history.replaceState; | |
| 1375 | + if (autoBreadcrumbs.location && hasPushAndReplaceState) { | |
| 1376 | + // TODO: remove onpopstate handler on uninstall() | |
| 1377 | + var oldOnPopState = _window.onpopstate; | |
| 1378 | + _window.onpopstate = function() { | |
| 1379 | + var currentHref = self._location.href; | |
| 1380 | + self._captureUrlChange(self._lastHref, currentHref); | |
| 1381 | + | |
| 1382 | + if (oldOnPopState) { | |
| 1383 | + return oldOnPopState.apply(this, arguments); | |
| 1384 | + } | |
| 1385 | + }; | |
| 1386 | + | |
| 1387 | + var historyReplacementFunction = function(origHistFunction) { | |
| 1388 | + // note history.pushState.length is 0; intentionally not declaring | |
| 1389 | + // params to preserve 0 arity | |
| 1390 | + return function(/* state, title, url */) { | |
| 1391 | + var url = arguments.length > 2 ? arguments[2] : undefined; | |
| 1392 | + | |
| 1393 | + // url argument is optional | |
| 1394 | + if (url) { | |
| 1395 | + // coerce to string (this is what pushState does) | |
| 1396 | + self._captureUrlChange(self._lastHref, url + ''); | |
| 1397 | + } | |
| 1522 | 1398 | |
| 1523 | − if (uri.protocol) { | |
| 1524 | − globalServer = uri.protocol + ':' + globalServer; | |
| 1525 | − } | |
| 1526 | − return globalServer; | |
| 1527 | − }, | |
| 1399 | + return origHistFunction.apply(this, arguments); | |
| 1400 | + }; | |
| 1401 | + }; | |
| 1528 | 1402 | |
| 1529 | − _handleOnErrorStackInfo: function() { | |
| 1530 | − // if we are intentionally ignoring errors via onerror, bail out | |
| 1531 | − if (!this._ignoreOnError) { | |
| 1532 | − this._handleStackInfo.apply(this, arguments); | |
| 1533 | − } | |
| 1534 | − }, | |
| 1403 | + fill(history, 'pushState', historyReplacementFunction, wrappedBuiltIns); | |
| 1404 | + fill(history, 'replaceState', historyReplacementFunction, wrappedBuiltIns); | |
| 1405 | + } | |
| 1535 | 1406 | |
| 1536 | − _handleStackInfo: function(stackInfo, options) { | |
| 1537 | − var frames = this._prepareFrames(stackInfo, options); | |
| 1407 | + if (autoBreadcrumbs.console && 'console' in _window && console.log) { | |
| 1408 | + // console | |
| 1409 | + var consoleMethodCallback = function(msg, data) { | |
| 1410 | + self.captureBreadcrumb({ | |
| 1411 | + message: msg, | |
| 1412 | + level: data.level, | |
| 1413 | + category: 'console' | |
| 1414 | + }); | |
| 1415 | + }; | |
| 1416 | + | |
| 1417 | + each(['debug', 'info', 'warn', 'error', 'log'], function(_, level) { | |
| 1418 | + wrapConsoleMethod(console, level, consoleMethodCallback); | |
| 1419 | + }); | |
| 1420 | + } | |
| 1421 | + }, | |
| 1538 | 1422 | |
| 1539 | − this._triggerEvent('handle', { | |
| 1540 | − stackInfo: stackInfo, | |
| 1541 | − options: options | |
| 1542 | − }); | |
| 1423 | + _restoreBuiltIns: function() { | |
| 1424 | + // restore any wrapped builtins | |
| 1425 | + var builtin; | |
| 1426 | + while (this._wrappedBuiltIns.length) { | |
| 1427 | + builtin = this._wrappedBuiltIns.shift(); | |
| 1543 | 1428 | |
| 1544 | − this._processException( | |
| 1545 | − stackInfo.name, | |
| 1546 | − stackInfo.message, | |
| 1547 | − stackInfo.url, | |
| 1548 | − stackInfo.lineno, | |
| 1549 | − frames, | |
| 1550 | − options | |
| 1551 | − ); | |
| 1552 | − }, | |
| 1553 | − | |
| 1554 | − _prepareFrames: function(stackInfo, options) { | |
| 1555 | − var self = this; | |
| 1556 | − var frames = []; | |
| 1557 | − if (stackInfo.stack && stackInfo.stack.length) { | |
| 1558 | − each(stackInfo.stack, function(i, stack) { | |
| 1559 | − var frame = self._normalizeFrame(stack, stackInfo.url); | |
| 1560 | − if (frame) { | |
| 1561 | − frames.push(frame); | |
| 1562 | − } | |
| 1563 | − }); | |
| 1564 | − | |
| 1565 | − // e.g. frames captured via captureMessage throw | |
| 1566 | − if (options && options.trimHeadFrames) { | |
| 1567 | − for ( | |
| 1568 | − var j = 0; | |
| 1569 | − j < options.trimHeadFrames && j < frames.length; | |
| 1570 | − j++ | |
| 1571 | − ) { | |
| 1572 | − frames[j].in_app = false; | |
| 1573 | − } | |
| 1574 | − } | |
| 1575 | − } | |
| 1576 | − frames = frames.slice(0, this._globalOptions.stackTraceLimit); | |
| 1577 | − return frames; | |
| 1578 | − }, | |
| 1579 | − | |
| 1580 | − _normalizeFrame: function(frame, stackInfoUrl) { | |
| 1581 | − // normalize the frames data | |
| 1582 | − var normalized = { | |
| 1583 | − filename: frame.url, | |
| 1584 | − lineno: frame.line, | |
| 1585 | − colno: frame.column, | |
| 1586 | − function: frame.func || '?' | |
| 1587 | − }; | |
| 1429 | + var obj = builtin[0], | |
| 1430 | + name = builtin[1], | |
| 1431 | + orig = builtin[2]; | |
| 1588 | 1432 | |
| 1589 | − // Case when we don't have any information about the error | |
| 1590 | − // E.g. throwing a string or raw object, instead of an `Error` in Firefox | |
| 1591 | − // Generating synthetic error doesn't add any value here | |
| 1592 | − // | |
| 1593 | − // We should probably somehow let a user know that they should fix their code | |
| 1594 | − if (!frame.url) { | |
| 1595 | − normalized.filename = stackInfoUrl; // fallback to whole stacks url from onerror handler | |
| 1596 | − } | |
| 1433 | + obj[name] = orig; | |
| 1434 | + } | |
| 1435 | + }, | |
| 1436 | + | |
| 1437 | + _drainPlugins: function() { | |
| 1438 | + var self = this; | |
| 1439 | + | |
| 1440 | + // FIX ME TODO | |
| 1441 | + each(this._plugins, function(_, plugin) { | |
| 1442 | + var installer = plugin[0]; | |
| 1443 | + var args = plugin[1]; | |
| 1444 | + installer.apply(self, [self].concat(args)); | |
| 1445 | + }); | |
| 1446 | + }, | |
| 1447 | + | |
| 1448 | + _parseDSN: function(str) { | |
| 1449 | + var m = dsnPattern.exec(str), | |
| 1450 | + dsn = {}, | |
| 1451 | + i = 7; | |
| 1452 | + | |
| 1453 | + try { | |
| 1454 | + while (i--) dsn[dsnKeys[i]] = m[i] || ''; | |
| 1455 | + } catch (e) { | |
| 1456 | + throw new RavenConfigError('Invalid DSN: ' + str); | |
| 1457 | + } | |
| 1597 | 1458 | |
| 1598 | − normalized.in_app = !// determine if an exception came from outside of our app | |
| 1599 | − // first we check the global includePaths list. | |
| 1600 | − ( | |
| 1601 | − (!!this._globalOptions.includePaths.test && | |
| 1602 | − !this._globalOptions.includePaths.test(normalized.filename)) || | |
| 1603 | − // Now we check for fun, if the function name is Raven or TraceKit | |
| 1604 | − /(Raven|TraceKit)\./.test(normalized['function']) || | |
| 1605 | − // finally, we do a last ditch effort and check for raven.min.js | |
| 1606 | − /raven\.(min\.)?js$/.test(normalized.filename) | |
| 1607 | − ); | |
| 1608 | − | |
| 1609 | − return normalized; | |
| 1610 | − }, | |
| 1611 | − | |
| 1612 | − _processException: function( | |
| 1613 | − type, | |
| 1614 | − message, | |
| 1615 | − fileurl, | |
| 1616 | − lineno, | |
| 1617 | − frames, | |
| 1618 | − options | |
| 1619 | − ) { | |
| 1620 | − var testString = (type || '') + ': ' + (message || ''); | |
| 1621 | − | |
| 1622 | − if ( | |
| 1623 | − !!this._globalOptions.ignoreErrors.test && | |
| 1624 | − this._globalOptions.ignoreErrors.test(testString) | |
| 1625 | − ) | |
| 1626 | − return; | |
| 1627 | − | |
| 1628 | − var stacktrace; | |
| 1629 | − | |
| 1630 | − if (frames && frames.length) { | |
| 1631 | − fileurl = frames[0].filename || fileurl; | |
| 1632 | − // Sentry expects frames oldest to newest | |
| 1633 | − // and JS sends them as newest to oldest | |
| 1634 | − frames.reverse(); | |
| 1635 | − stacktrace = {frames: frames}; | |
| 1636 | − } else if (fileurl) { | |
| 1637 | − stacktrace = { | |
| 1638 | − frames: [ | |
| 1639 | − { | |
| 1640 | − filename: fileurl, | |
| 1641 | − lineno: lineno, | |
| 1642 | − in_app: true | |
| 1643 | − } | |
| 1644 | − ] | |
| 1645 | − }; | |
| 1646 | − } | |
| 1459 | + if (dsn.pass && !this._globalOptions.allowSecretKey) { | |
| 1460 | + throw new RavenConfigError( | |
| 1461 | + 'Do not specify your secret key in the DSN. See: http://bit.ly/raven-secret-key' | |
| 1462 | + ); | |
| 1463 | + } | |
| 1647 | 1464 | |
| 1648 | − if ( | |
| 1649 | − !!this._globalOptions.ignoreUrls.test && | |
| 1650 | − this._globalOptions.ignoreUrls.test(fileurl) | |
| 1651 | − ) | |
| 1652 | − return; | |
| 1653 | − if ( | |
| 1654 | − !!this._globalOptions.whitelistUrls.test && | |
| 1655 | − !this._globalOptions.whitelistUrls.test(fileurl) | |
| 1656 | − ) | |
| 1657 | − return; | |
| 1658 | − | |
| 1659 | − var data = objectMerge( | |
| 1660 | − { | |
| 1661 | − // sentry.interfaces.Exception | |
| 1662 | − exception: { | |
| 1663 | − values: [ | |
| 1664 | − { | |
| 1665 | − type: type, | |
| 1666 | − value: message, | |
| 1667 | − stacktrace: stacktrace | |
| 1668 | − } | |
| 1669 | − ] | |
| 1670 | − }, | |
| 1671 | − culprit: fileurl | |
| 1672 | − }, | |
| 1673 | − options | |
| 1674 | − ); | |
| 1675 | − | |
| 1676 | − // Fire away! | |
| 1677 | − this._send(data); | |
| 1678 | − }, | |
| 1679 | − | |
| 1680 | − _trimPacket: function(data) { | |
| 1681 | − // For now, we only want to truncate the two different messages | |
| 1682 | − // but this could/should be expanded to just trim everything | |
| 1683 | − var max = this._globalOptions.maxMessageLength; | |
| 1684 | − if (data.message) { | |
| 1685 | − data.message = truncate(data.message, max); | |
| 1686 | − } | |
| 1687 | − if (data.exception) { | |
| 1688 | − var exception = data.exception.values[0]; | |
| 1689 | − exception.value = truncate(exception.value, max); | |
| 1690 | − } | |
| 1465 | + return dsn; | |
| 1466 | + }, | |
| 1691 | 1467 | |
| 1692 | − var request = data.request; | |
| 1693 | − if (request) { | |
| 1694 | − if (request.url) { | |
| 1695 | − request.url = truncate(request.url, this._globalOptions.maxUrlLength); | |
| 1696 | − } | |
| 1697 | − if (request.Referer) { | |
| 1698 | − request.Referer = truncate( | |
| 1699 | − request.Referer, | |
| 1700 | − this._globalOptions.maxUrlLength | |
| 1701 | − ); | |
| 1702 | − } | |
| 1703 | − } | |
| 1468 | + _getGlobalServer: function(uri) { | |
| 1469 | + // assemble the endpoint from the uri pieces | |
| 1470 | + var globalServer = '//' + uri.host + (uri.port ? ':' + uri.port : ''); | |
| 1471 | + | |
| 1472 | + if (uri.protocol) { | |
| 1473 | + globalServer = uri.protocol + ':' + globalServer; | |
| 1474 | + } | |
| 1475 | + return globalServer; | |
| 1476 | + }, | |
| 1477 | + | |
| 1478 | + _handleOnErrorStackInfo: function() { | |
| 1479 | + // if we are intentionally ignoring errors via onerror, bail out | |
| 1480 | + if (!this._ignoreOnError) { | |
| 1481 | + this._handleStackInfo.apply(this, arguments); | |
| 1482 | + } | |
| 1483 | + }, | |
| 1484 | + | |
| 1485 | + _handleStackInfo: function(stackInfo, options) { | |
| 1486 | + var frames = this._prepareFrames(stackInfo, options); | |
| 1487 | + | |
| 1488 | + this._triggerEvent('handle', { | |
| 1489 | + stackInfo: stackInfo, | |
| 1490 | + options: options | |
| 1491 | + }); | |
| 1492 | + | |
| 1493 | + this._processException( | |
| 1494 | + stackInfo.name, | |
| 1495 | + stackInfo.message, | |
| 1496 | + stackInfo.url, | |
| 1497 | + stackInfo.lineno, | |
| 1498 | + frames, | |
| 1499 | + options | |
| 1500 | + ); | |
| 1501 | + }, | |
| 1502 | + | |
| 1503 | + _prepareFrames: function(stackInfo, options) { | |
| 1504 | + var self = this; | |
| 1505 | + var frames = []; | |
| 1506 | + if (stackInfo.stack && stackInfo.stack.length) { | |
| 1507 | + each(stackInfo.stack, function(i, stack) { | |
| 1508 | + var frame = self._normalizeFrame(stack, stackInfo.url); | |
| 1509 | + if (frame) { | |
| 1510 | + frames.push(frame); | |
| 1511 | + } | |
| 1512 | + }); | |
| 1513 | + | |
| 1514 | + // e.g. frames captured via captureMessage throw | |
| 1515 | + if (options && options.trimHeadFrames) { | |
| 1516 | + for (var j = 0; j < options.trimHeadFrames && j < frames.length; j++) { | |
| 1517 | + frames[j].in_app = false; | |
| 1518 | + } | |
| 1519 | + } | |
| 1520 | + } | |
| 1521 | + frames = frames.slice(0, this._globalOptions.stackTraceLimit); | |
| 1522 | + return frames; | |
| 1523 | + }, | |
| 1524 | + | |
| 1525 | + _normalizeFrame: function(frame, stackInfoUrl) { | |
| 1526 | + // normalize the frames data | |
| 1527 | + var normalized = { | |
| 1528 | + filename: frame.url, | |
| 1529 | + lineno: frame.line, | |
| 1530 | + colno: frame.column, | |
| 1531 | + function: frame.func || '?' | |
| 1532 | + }; | |
| 1533 | + | |
| 1534 | + // Case when we don't have any information about the error | |
| 1535 | + // E.g. throwing a string or raw object, instead of an `Error` in Firefox | |
| 1536 | + // Generating synthetic error doesn't add any value here | |
| 1537 | + // | |
| 1538 | + // We should probably somehow let a user know that they should fix their code | |
| 1539 | + if (!frame.url) { | |
| 1540 | + normalized.filename = stackInfoUrl; // fallback to whole stacks url from onerror handler | |
| 1541 | + } | |
| 1542 | + | |
| 1543 | + normalized.in_app = !// determine if an exception came from outside of our app | |
| 1544 | + // first we check the global includePaths list. | |
| 1545 | + ( | |
| 1546 | + (!!this._globalOptions.includePaths.test && | |
| 1547 | + !this._globalOptions.includePaths.test(normalized.filename)) || | |
| 1548 | + // Now we check for fun, if the function name is Raven or TraceKit | |
| 1549 | + /(Raven|TraceKit)\./.test(normalized['function']) || | |
| 1550 | + // finally, we do a last ditch effort and check for raven.min.js | |
| 1551 | + /raven\.(min\.)?js$/.test(normalized.filename) | |
| 1552 | + ); | |
| 1553 | + | |
| 1554 | + return normalized; | |
| 1555 | + }, | |
| 1556 | + | |
| 1557 | + _processException: function(type, message, fileurl, lineno, frames, options) { | |
| 1558 | + var prefixedMessage = (type ? type + ': ' : '') + (message || ''); | |
| 1559 | + if ( | |
| 1560 | + !!this._globalOptions.ignoreErrors.test && | |
| 1561 | + (this._globalOptions.ignoreErrors.test(message) || | |
| 1562 | + this._globalOptions.ignoreErrors.test(prefixedMessage)) | |
| 1563 | + ) { | |
| 1564 | + return; | |
| 1565 | + } | |
| 1566 | + | |
| 1567 | + var stacktrace; | |
| 1568 | + | |
| 1569 | + if (frames && frames.length) { | |
| 1570 | + fileurl = frames[0].filename || fileurl; | |
| 1571 | + // Sentry expects frames oldest to newest | |
| 1572 | + // and JS sends them as newest to oldest | |
| 1573 | + frames.reverse(); | |
| 1574 | + stacktrace = {frames: frames}; | |
| 1575 | + } else if (fileurl) { | |
| 1576 | + stacktrace = { | |
| 1577 | + frames: [ | |
| 1578 | + { | |
| 1579 | + filename: fileurl, | |
| 1580 | + lineno: lineno, | |
| 1581 | + in_app: true | |
| 1582 | + } | |
| 1583 | + ] | |
| 1584 | + }; | |
| 1585 | + } | |
| 1586 | + | |
| 1587 | + if ( | |
| 1588 | + !!this._globalOptions.ignoreUrls.test && | |
| 1589 | + this._globalOptions.ignoreUrls.test(fileurl) | |
| 1590 | + ) { | |
| 1591 | + return; | |
| 1592 | + } | |
| 1704 | 1593 | |
| 1705 | − if (data.breadcrumbs && data.breadcrumbs.values) | |
| 1706 | − this._trimBreadcrumbs(data.breadcrumbs); | |
| 1594 | + if ( | |
| 1595 | + !!this._globalOptions.whitelistUrls.test && | |
| 1596 | + !this._globalOptions.whitelistUrls.test(fileurl) | |
| 1597 | + ) { | |
| 1598 | + return; | |
| 1599 | + } | |
| 1600 | + | |
| 1601 | + var data = objectMerge( | |
| 1602 | + { | |
| 1603 | + // sentry.interfaces.Exception | |
| 1604 | + exception: { | |
| 1605 | + values: [ | |
| 1606 | + { | |
| 1607 | + type: type, | |
| 1608 | + value: message, | |
| 1609 | + stacktrace: stacktrace | |
| 1610 | + } | |
| 1611 | + ] | |
| 1612 | + }, | |
| 1613 | + culprit: fileurl | |
| 1614 | + }, | |
| 1615 | + options | |
| 1616 | + ); | |
| 1617 | + | |
| 1618 | + // Fire away! | |
| 1619 | + this._send(data); | |
| 1620 | + }, | |
| 1621 | + | |
| 1622 | + _trimPacket: function(data) { | |
| 1623 | + // For now, we only want to truncate the two different messages | |
| 1624 | + // but this could/should be expanded to just trim everything | |
| 1625 | + var max = this._globalOptions.maxMessageLength; | |
| 1626 | + if (data.message) { | |
| 1627 | + data.message = truncate(data.message, max); | |
| 1628 | + } | |
| 1629 | + if (data.exception) { | |
| 1630 | + var exception = data.exception.values[0]; | |
| 1631 | + exception.value = truncate(exception.value, max); | |
| 1632 | + } | |
| 1707 | 1633 | |
| 1708 | − return data; | |
| 1709 | − }, | |
| 1634 | + var request = data.request; | |
| 1635 | + if (request) { | |
| 1636 | + if (request.url) { | |
| 1637 | + request.url = truncate(request.url, this._globalOptions.maxUrlLength); | |
| 1638 | + } | |
| 1639 | + if (request.Referer) { | |
| 1640 | + request.Referer = truncate(request.Referer, this._globalOptions.maxUrlLength); | |
| 1641 | + } | |
| 1642 | + } | |
| 1710 | 1643 | |
| 1711 | − /** | |
| 1644 | + if (data.breadcrumbs && data.breadcrumbs.values) | |
| 1645 | + this._trimBreadcrumbs(data.breadcrumbs); | |
| 1646 | + | |
| 1647 | + return data; | |
| 1648 | + }, | |
| 1649 | + | |
| 1650 | + /** | |
| 1712 | 1651 | * Truncate breadcrumb values (right now just URLs) |
| 1713 | 1652 | */ |
| 1714 | − _trimBreadcrumbs: function(breadcrumbs) { | |
| 1715 | − // known breadcrumb properties with urls | |
| 1716 | − // TODO: also consider arbitrary prop values that start with (https?)?:// | |
| 1717 | − var urlProps = ['to', 'from', 'url'], | |
| 1718 | − urlProp, | |
| 1719 | − crumb, | |
| 1720 | − data; | |
| 1721 | − | |
| 1722 | − for (var i = 0; i < breadcrumbs.values.length; ++i) { | |
| 1723 | − crumb = breadcrumbs.values[i]; | |
| 1724 | − if ( | |
| 1725 | − !crumb.hasOwnProperty('data') || | |
| 1726 | − !isObject(crumb.data) || | |
| 1727 | − objectFrozen(crumb.data) | |
| 1728 | − ) | |
| 1729 | − continue; | |
| 1730 | − | |
| 1731 | − data = objectMerge({}, crumb.data); | |
| 1732 | − for (var j = 0; j < urlProps.length; ++j) { | |
| 1733 | − urlProp = urlProps[j]; | |
| 1734 | − if (data.hasOwnProperty(urlProp) && data[urlProp]) { | |
| 1735 | − data[urlProp] = truncate( | |
| 1736 | − data[urlProp], | |
| 1737 | − this._globalOptions.maxUrlLength | |
| 1738 | − ); | |
| 1739 | − } | |
| 1740 | − } | |
| 1741 | − breadcrumbs.values[i].data = data; | |
| 1742 | − } | |
| 1743 | − }, | |
| 1653 | + _trimBreadcrumbs: function(breadcrumbs) { | |
| 1654 | + // known breadcrumb properties with urls | |
| 1655 | + // TODO: also consider arbitrary prop values that start with (https?)?:// | |
| 1656 | + var urlProps = ['to', 'from', 'url'], | |
| 1657 | + urlProp, | |
| 1658 | + crumb, | |
| 1659 | + data; | |
| 1660 | + | |
| 1661 | + for (var i = 0; i < breadcrumbs.values.length; ++i) { | |
| 1662 | + crumb = breadcrumbs.values[i]; | |
| 1663 | + if ( | |
| 1664 | + !crumb.hasOwnProperty('data') || | |
| 1665 | + !isObject(crumb.data) || | |
| 1666 | + objectFrozen(crumb.data) | |
| 1667 | + ) | |
| 1668 | + continue; | |
| 1669 | + | |
| 1670 | + data = objectMerge({}, crumb.data); | |
| 1671 | + for (var j = 0; j < urlProps.length; ++j) { | |
| 1672 | + urlProp = urlProps[j]; | |
| 1673 | + if (data.hasOwnProperty(urlProp) && data[urlProp]) { | |
| 1674 | + data[urlProp] = truncate(data[urlProp], this._globalOptions.maxUrlLength); | |
| 1675 | + } | |
| 1676 | + } | |
| 1677 | + breadcrumbs.values[i].data = data; | |
| 1678 | + } | |
| 1679 | + }, | |
| 1744 | 1680 | |
| 1745 | − _getHttpData: function() { | |
| 1746 | − if (!this._hasNavigator && !this._hasDocument) return; | |
| 1747 | − var httpData = {}; | |
| 1681 | + _getHttpData: function() { | |
| 1682 | + if (!this._hasNavigator && !this._hasDocument) return; | |
| 1683 | + var httpData = {}; | |
| 1748 | 1684 | |
| 1749 | − if (this._hasNavigator && _navigator.userAgent) { | |
| 1750 | − httpData.headers = { | |
| 1751 | − 'User-Agent': navigator.userAgent | |
| 1752 | − }; | |
| 1753 | − } | |
| 1685 | + if (this._hasNavigator && _navigator.userAgent) { | |
| 1686 | + httpData.headers = { | |
| 1687 | + 'User-Agent': navigator.userAgent | |
| 1688 | + }; | |
| 1689 | + } | |
| 1754 | 1690 | |
| 1755 | − if (this._hasDocument) { | |
| 1756 | − if (_document.location && _document.location.href) { | |
| 1757 | − httpData.url = _document.location.href; | |
| 1758 | − } | |
| 1759 | − if (_document.referrer) { | |
| 1760 | − if (!httpData.headers) httpData.headers = {}; | |
| 1761 | − httpData.headers.Referer = _document.referrer; | |
| 1762 | − } | |
| 1763 | − } | |
| 1691 | + if (this._hasDocument) { | |
| 1692 | + if (_document.location && _document.location.href) { | |
| 1693 | + httpData.url = _document.location.href; | |
| 1694 | + } | |
| 1695 | + if (_document.referrer) { | |
| 1696 | + if (!httpData.headers) httpData.headers = {}; | |
| 1697 | + httpData.headers.Referer = _document.referrer; | |
| 1698 | + } | |
| 1699 | + } | |
| 1764 | 1700 | |
| 1765 | − return httpData; | |
| 1766 | − }, | |
| 1701 | + return httpData; | |
| 1702 | + }, | |
| 1767 | 1703 | |
| 1768 | − _resetBackoff: function() { | |
| 1769 | − this._backoffDuration = 0; | |
| 1770 | − this._backoffStart = null; | |
| 1771 | − }, | |
| 1704 | + _resetBackoff: function() { | |
| 1705 | + this._backoffDuration = 0; | |
| 1706 | + this._backoffStart = null; | |
| 1707 | + }, | |
| 1772 | 1708 | |
| 1773 | − _shouldBackoff: function() { | |
| 1774 | − return ( | |
| 1775 | − this._backoffDuration && | |
| 1776 | − now() - this._backoffStart < this._backoffDuration | |
| 1777 | − ); | |
| 1778 | − }, | |
| 1709 | + _shouldBackoff: function() { | |
| 1710 | + return this._backoffDuration && now() - this._backoffStart < this._backoffDuration; | |
| 1711 | + }, | |
| 1779 | 1712 | |
| 1780 | − /** | |
| 1713 | + /** | |
| 1781 | 1714 | * Returns true if the in-process data payload matches the signature |
| 1782 | 1715 | * of the previously-sent data |
| 1783 | 1716 | * |
| @@ -1786,362 +1719,449 @@ · 2 unmodified lines | ||
| 1786 | 1719 | * other old browsers). This can take the form of an "exception" |
| 1787 | 1720 | * data object with a single frame (derived from the onerror args). |
| 1788 | 1721 | */ |
| 1789 | − _isRepeatData: function(current) { | |
| 1790 | − var last = this._lastData; | |
| 1791 | − | |
| 1792 | − if ( | |
| 1793 | − !last || | |
| 1794 | − current.message !== last.message || // defined for captureMessage | |
| 1795 | − current.culprit !== last.culprit // defined for captureException/onerror | |
| 1796 | − ) | |
| 1797 | − return false; | |
| 1798 | − | |
| 1799 | − // Stacktrace interface (i.e. from captureMessage) | |
| 1800 | − if (current.stacktrace || last.stacktrace) { | |
| 1801 | − return isSameStacktrace(current.stacktrace, last.stacktrace); | |
| 1802 | − } else if (current.exception || last.exception) { | |
| 1803 | − // Exception interface (i.e. from captureException/onerror) | |
| 1804 | − return isSameException(current.exception, last.exception); | |
| 1805 | − } | |
| 1722 | + _isRepeatData: function(current) { | |
| 1723 | + var last = this._lastData; | |
| 1724 | + | |
| 1725 | + if ( | |
| 1726 | + !last || | |
| 1727 | + current.message !== last.message || // defined for captureMessage | |
| 1728 | + current.culprit !== last.culprit // defined for captureException/onerror | |
| 1729 | + ) | |
| 1730 | + return false; | |
| 1731 | + | |
| 1732 | + // Stacktrace interface (i.e. from captureMessage) | |
| 1733 | + if (current.stacktrace || last.stacktrace) { | |
| 1734 | + return isSameStacktrace(current.stacktrace, last.stacktrace); | |
| 1735 | + } else if (current.exception || last.exception) { | |
| 1736 | + // Exception interface (i.e. from captureException/onerror) | |
| 1737 | + return isSameException(current.exception, last.exception); | |
| 1738 | + } | |
| 1806 | 1739 | |
| 1807 | − return true; | |
| 1808 | − }, | |
| 1740 | + return true; | |
| 1741 | + }, | |
| 1809 | 1742 | |
| 1810 | − _setBackoffState: function(request) { | |
| 1811 | − // If we are already in a backoff state, don't change anything | |
| 1812 | − if (this._shouldBackoff()) { | |
| 1813 | − return; | |
| 1814 | − } | |
| 1743 | + _setBackoffState: function(request) { | |
| 1744 | + // If we are already in a backoff state, don't change anything | |
| 1745 | + if (this._shouldBackoff()) { | |
| 1746 | + return; | |
| 1747 | + } | |
| 1815 | 1748 | |
| 1816 | − var status = request.status; | |
| 1749 | + var status = request.status; | |
| 1750 | + | |
| 1751 | + // 400 - project_id doesn't exist or some other fatal | |
| 1752 | + // 401 - invalid/revoked dsn | |
| 1753 | + // 429 - too many requests | |
| 1754 | + if (!(status === 400 || status === 401 || status === 429)) return; | |
| 1755 | + | |
| 1756 | + var retry; | |
| 1757 | + try { | |
| 1758 | + // If Retry-After is not in Access-Control-Expose-Headers, most | |
| 1759 | + // browsers will throw an exception trying to access it | |
| 1760 | + retry = request.getResponseHeader('Retry-After'); | |
| 1761 | + retry = parseInt(retry, 10) * 1000; // Retry-After is returned in seconds | |
| 1762 | + } catch (e) { | |
| 1763 | + /* eslint no-empty:0 */ | |
| 1764 | + } | |
| 1817 | 1765 | |
| 1818 | − // 400 - project_id doesn't exist or some other fatal | |
| 1819 | − // 401 - invalid/revoked dsn | |
| 1820 | − // 429 - too many requests | |
| 1821 | − if (!(status === 400 || status === 401 || status === 429)) return; | |
| 1766 | + this._backoffDuration = retry | |
| 1767 | + ? // If Sentry server returned a Retry-After value, use it | |
| 1768 | + retry | |
| 1769 | + : // Otherwise, double the last backoff duration (starts at 1 sec) | |
| 1770 | + this._backoffDuration * 2 || 1000; | |
| 1822 | 1771 | |
| 1823 | − var retry; | |
| 1824 | − try { | |
| 1825 | − // If Retry-After is not in Access-Control-Expose-Headers, most | |
| 1826 | − // browsers will throw an exception trying to access it | |
| 1827 | − retry = request.getResponseHeader('Retry-After'); | |
| 1828 | − retry = parseInt(retry, 10) * 1000; // Retry-After is returned in seconds | |
| 1829 | − } catch (e) { | |
| 1830 | − /* eslint no-empty:0 */ | |
| 1831 | − } | |
| 1772 | + this._backoffStart = now(); | |
| 1773 | + }, | |
| 1832 | 1774 | |
| 1833 | − this._backoffDuration = retry | |
| 1834 | − ? // If Sentry server returned a Retry-After value, use it | |
| 1835 | − retry | |
| 1836 | − : // Otherwise, double the last backoff duration (starts at 1 sec) | |
| 1837 | − this._backoffDuration * 2 || 1000; | |
| 1775 | + _send: function(data) { | |
| 1776 | + var globalOptions = this._globalOptions; | |
| 1838 | 1777 | |
| 1839 | − this._backoffStart = now(); | |
| 1840 | − }, | |
| 1778 | + var baseData = { | |
| 1779 | + project: this._globalProject, | |
| 1780 | + logger: globalOptions.logger, | |
| 1781 | + platform: 'javascript' | |
| 1782 | + }, | |
| 1783 | + httpData = this._getHttpData(); | |
| 1841 | 1784 | |
| 1842 | − _send: function(data) { | |
| 1843 | − var globalOptions = this._globalOptions; | |
| 1785 | + if (httpData) { | |
| 1786 | + baseData.request = httpData; | |
| 1787 | + } | |
| 1844 | 1788 | |
| 1845 | − var baseData = { | |
| 1846 | − project: this._globalProject, | |
| 1847 | − logger: globalOptions.logger, | |
| 1848 | − platform: 'javascript' | |
| 1849 | − }, | |
| 1850 | − httpData = this._getHttpData(); | |
| 1789 | + // HACK: delete `trimHeadFrames` to prevent from appearing in outbound payload | |
| 1790 | + if (data.trimHeadFrames) delete data.trimHeadFrames; | |
| 1851 | 1791 | |
| 1852 | − if (httpData) { | |
| 1853 | − baseData.request = httpData; | |
| 1854 | − } | |
| 1792 | + data = objectMerge(baseData, data); | |
| 1855 | 1793 | |
| 1856 | − // HACK: delete `trimHeadFrames` to prevent from appearing in outbound payload | |
| 1857 | − if (data.trimHeadFrames) delete data.trimHeadFrames; | |
| 1858 | − | |
| 1859 | − data = objectMerge(baseData, data); | |
| 1860 | − | |
| 1861 | − // Merge in the tags and extra separately since objectMerge doesn't handle a deep merge | |
| 1862 | − data.tags = objectMerge( | |
| 1863 | − objectMerge({}, this._globalContext.tags), | |
| 1864 | − data.tags | |
| 1865 | − ); | |
| 1866 | − data.extra = objectMerge( | |
| 1867 | − objectMerge({}, this._globalContext.extra), | |
| 1868 | − data.extra | |
| 1869 | − ); | |
| 1870 | − | |
| 1871 | − // Send along our own collected metadata with extra | |
| 1872 | − data.extra['session:duration'] = now() - this._startTime; | |
| 1873 | − | |
| 1874 | − if (this._breadcrumbs && this._breadcrumbs.length > 0) { | |
| 1875 | − // intentionally make shallow copy so that additions | |
| 1876 | − // to breadcrumbs aren't accidentally sent in this request | |
| 1877 | − data.breadcrumbs = { | |
| 1878 | − values: [].slice.call(this._breadcrumbs, 0) | |
| 1879 | − }; | |
| 1880 | − } | |
| 1794 | + // Merge in the tags and extra separately since objectMerge doesn't handle a deep merge | |
| 1795 | + data.tags = objectMerge(objectMerge({}, this._globalContext.tags), data.tags); | |
| 1796 | + data.extra = objectMerge(objectMerge({}, this._globalContext.extra), data.extra); | |
| 1881 | 1797 | |
| 1882 | − // If there are no tags/extra, strip the key from the payload alltogther. | |
| 1883 | − if (isEmptyObject(data.tags)) delete data.tags; | |
| 1798 | + // Send along our own collected metadata with extra | |
| 1799 | + data.extra['session:duration'] = now() - this._startTime; | |
| 1884 | 1800 | |
| 1885 | − if (this._globalContext.user) { | |
| 1886 | − // sentry.interfaces.User | |
| 1887 | − data.user = this._globalContext.user; | |
| 1888 | − } | |
| 1801 | + if (this._breadcrumbs && this._breadcrumbs.length > 0) { | |
| 1802 | + // intentionally make shallow copy so that additions | |
| 1803 | + // to breadcrumbs aren't accidentally sent in this request | |
| 1804 | + data.breadcrumbs = { | |
| 1805 | + values: [].slice.call(this._breadcrumbs, 0) | |
| 1806 | + }; | |
| 1807 | + } | |
| 1889 | 1808 | |
| 1890 | − // Include the environment if it's defined in globalOptions | |
| 1891 | − if (globalOptions.environment) | |
| 1892 | − data.environment = globalOptions.environment; | |
| 1809 | + // If there are no tags/extra, strip the key from the payload alltogther. | |
| 1810 | + if (isEmptyObject(data.tags)) delete data.tags; | |
| 1893 | 1811 | |
| 1894 | − // Include the release if it's defined in globalOptions | |
| 1895 | − if (globalOptions.release) data.release = globalOptions.release; | |
| 1812 | + if (this._globalContext.user) { | |
| 1813 | + // sentry.interfaces.User | |
| 1814 | + data.user = this._globalContext.user; | |
| 1815 | + } | |
| 1896 | 1816 | |
| 1897 | − // Include server_name if it's defined in globalOptions | |
| 1898 | − if (globalOptions.serverName) data.server_name = globalOptions.serverName; | |
| 1817 | + // Include the environment if it's defined in globalOptions | |
| 1818 | + if (globalOptions.environment) data.environment = globalOptions.environment; | |
| 1899 | 1819 | |
| 1900 | − if (isFunction(globalOptions.dataCallback)) { | |
| 1901 | − data = globalOptions.dataCallback(data) || data; | |
| 1902 | − } | |
| 1820 | + // Include the release if it's defined in globalOptions | |
| 1821 | + if (globalOptions.release) data.release = globalOptions.release; | |
| 1903 | 1822 | |
| 1904 | − // Why?????????? | |
| 1905 | − if (!data || isEmptyObject(data)) { | |
| 1906 | − return; | |
| 1907 | − } | |
| 1823 | + // Include server_name if it's defined in globalOptions | |
| 1824 | + if (globalOptions.serverName) data.server_name = globalOptions.serverName; | |
| 1908 | 1825 | |
| 1909 | − // Check if the request should be filtered or not | |
| 1910 | − if ( | |
| 1911 | − isFunction(globalOptions.shouldSendCallback) && | |
| 1912 | − !globalOptions.shouldSendCallback(data) | |
| 1913 | − ) { | |
| 1914 | − return; | |
| 1915 | − } | |
| 1826 | + if (isFunction(globalOptions.dataCallback)) { | |
| 1827 | + data = globalOptions.dataCallback(data) || data; | |
| 1828 | + } | |
| 1916 | 1829 | |
| 1917 | − // Backoff state: Sentry server previously responded w/ an error (e.g. 429 - too many requests), | |
| 1918 | − // so drop requests until "cool-off" period has elapsed. | |
| 1919 | − if (this._shouldBackoff()) { | |
| 1920 | − this._logDebug('warn', 'Raven dropped error due to backoff: ', data); | |
| 1921 | − return; | |
| 1922 | − } | |
| 1830 | + // Why?????????? | |
| 1831 | + if (!data || isEmptyObject(data)) { | |
| 1832 | + return; | |
| 1833 | + } | |
| 1923 | 1834 | |
| 1924 | − if (typeof globalOptions.sampleRate === 'number') { | |
| 1925 | − if (Math.random() < globalOptions.sampleRate) { | |
| 1926 | − this._sendProcessedPayload(data); | |
| 1927 | − } | |
| 1928 | − } else { | |
| 1929 | − this._sendProcessedPayload(data); | |
| 1930 | − } | |
| 1931 | − }, | |
| 1835 | + // Check if the request should be filtered or not | |
| 1836 | + if ( | |
| 1837 | + isFunction(globalOptions.shouldSendCallback) && | |
| 1838 | + !globalOptions.shouldSendCallback(data) | |
| 1839 | + ) { | |
| 1840 | + return; | |
| 1841 | + } | |
| 1842 | + | |
| 1843 | + // Backoff state: Sentry server previously responded w/ an error (e.g. 429 - too many requests), | |
| 1844 | + // so drop requests until "cool-off" period has elapsed. | |
| 1845 | + if (this._shouldBackoff()) { | |
| 1846 | + this._logDebug('warn', 'Raven dropped error due to backoff: ', data); | |
| 1847 | + return; | |
| 1848 | + } | |
| 1932 | 1849 | |
| 1933 | − _getUuid: function() { | |
| 1934 | − return uuid4(); | |
| 1935 | − }, | |
| 1850 | + if (typeof globalOptions.sampleRate === 'number') { | |
| 1851 | + if (Math.random() < globalOptions.sampleRate) { | |
| 1852 | + this._sendProcessedPayload(data); | |
| 1853 | + } | |
| 1854 | + } else { | |
| 1855 | + this._sendProcessedPayload(data); | |
| 1856 | + } | |
| 1857 | + }, | |
| 1936 | 1858 | |
| 1937 | − _sendProcessedPayload: function(data, callback) { | |
| 1938 | − var self = this; | |
| 1939 | − var globalOptions = this._globalOptions; | |
| 1859 | + _getUuid: function() { | |
| 1860 | + return uuid4(); | |
| 1861 | + }, | |
| 1940 | 1862 | |
| 1941 | − if (!this.isSetup()) return; | |
| 1863 | + _sendProcessedPayload: function(data, callback) { | |
| 1864 | + var self = this; | |
| 1865 | + var globalOptions = this._globalOptions; | |
| 1942 | 1866 | |
| 1943 | − // Try and clean up the packet before sending by truncating long values | |
| 1944 | − data = this._trimPacket(data); | |
| 1867 | + if (!this.isSetup()) return; | |
| 1945 | 1868 | |
| 1946 | − // ideally duplicate error testing should occur *before* dataCallback/shouldSendCallback, | |
| 1947 | − // but this would require copying an un-truncated copy of the data packet, which can be | |
| 1948 | − // arbitrarily deep (extra_data) -- could be worthwhile? will revisit | |
| 1949 | − if (!this._globalOptions.allowDuplicates && this._isRepeatData(data)) { | |
| 1950 | − this._logDebug('warn', 'Raven dropped repeat event: ', data); | |
| 1951 | − return; | |
| 1952 | − } | |
| 1869 | + // Try and clean up the packet before sending by truncating long values | |
| 1870 | + data = this._trimPacket(data); | |
| 1953 | 1871 | |
| 1954 | − // Send along an event_id if not explicitly passed. | |
| 1955 | − // This event_id can be used to reference the error within Sentry itself. | |
| 1956 | − // Set lastEventId after we know the error should actually be sent | |
| 1957 | − this._lastEventId = data.event_id || (data.event_id = this._getUuid()); | |
| 1872 | + // ideally duplicate error testing should occur *before* dataCallback/shouldSendCallback, | |
| 1873 | + // but this would require copying an un-truncated copy of the data packet, which can be | |
| 1874 | + // arbitrarily deep (extra_data) -- could be worthwhile? will revisit | |
| 1875 | + if (!this._globalOptions.allowDuplicates && this._isRepeatData(data)) { | |
| 1876 | + this._logDebug('warn', 'Raven dropped repeat event: ', data); | |
| 1877 | + return; | |
| 1878 | + } | |
| 1958 | 1879 | |
| 1959 | − // Store outbound payload after trim | |
| 1960 | − this._lastData = data; | |
| 1880 | + // Send along an event_id if not explicitly passed. | |
| 1881 | + // This event_id can be used to reference the error within Sentry itself. | |
| 1882 | + // Set lastEventId after we know the error should actually be sent | |
| 1883 | + this._lastEventId = data.event_id || (data.event_id = this._getUuid()); | |
| 1961 | 1884 | |
| 1962 | − this._logDebug('debug', 'Raven about to send:', data); | |
| 1885 | + // Store outbound payload after trim | |
| 1886 | + this._lastData = data; | |
| 1963 | 1887 | |
| 1964 | − var auth = { | |
| 1965 | − sentry_version: '7', | |
| 1966 | − sentry_client: 'raven-js/' + this.VERSION, | |
| 1967 | − sentry_key: this._globalKey | |
| 1968 | − }; | |
| 1888 | + this._logDebug('debug', 'Raven about to send:', data); | |
| 1969 | 1889 | |
| 1970 | − if (this._globalSecret) { | |
| 1971 | − auth.sentry_secret = this._globalSecret; | |
| 1972 | − } | |
| 1890 | + var auth = { | |
| 1891 | + sentry_version: '7', | |
| 1892 | + sentry_client: 'raven-js/' + this.VERSION, | |
| 1893 | + sentry_key: this._globalKey | |
| 1894 | + }; | |
| 1973 | 1895 | |
| 1974 | − var exception = data.exception && data.exception.values[0]; | |
| 1975 | − this.captureBreadcrumb({ | |
| 1976 | − category: 'sentry', | |
| 1977 | − message: exception | |
| 1978 | − ? (exception.type ? exception.type + ': ' : '') + exception.value | |
| 1979 | − : data.message, | |
| 1980 | − event_id: data.event_id, | |
| 1981 | − level: data.level || 'error' // presume error unless specified | |
| 1982 | − }); | |
| 1896 | + if (this._globalSecret) { | |
| 1897 | + auth.sentry_secret = this._globalSecret; | |
| 1898 | + } | |
| 1983 | 1899 | |
| 1984 | − var url = this._globalEndpoint; | |
| 1985 | − (globalOptions.transport || this._makeRequest).call(this, { | |
| 1986 | − url: url, | |
| 1987 | − auth: auth, | |
| 1988 | − data: data, | |
| 1989 | − options: globalOptions, | |
| 1990 | − onSuccess: function success() { | |
| 1991 | − self._resetBackoff(); | |
| 1992 | − | |
| 1993 | − self._triggerEvent('success', { | |
| 1994 | − data: data, | |
| 1995 | − src: url | |
| 1996 | − }); | |
| 1997 | − callback && callback(); | |
| 1998 | − }, | |
| 1999 | − onError: function failure(error) { | |
| 2000 | − self._logDebug('error', 'Raven transport failed to send: ', error); | |
| 2001 | − | |
| 2002 | − if (error.request) { | |
| 2003 | − self._setBackoffState(error.request); | |
| 2004 | − } | |
| 2005 | − | |
| 2006 | − self._triggerEvent('failure', { | |
| 2007 | − data: data, | |
| 2008 | − src: url | |
| 2009 | − }); | |
| 2010 | − error = | |
| 2011 | − error || | |
| 2012 | − new Error('Raven send failed (no additional details provided)'); | |
| 2013 | − callback && callback(error); | |
| 2014 | − } | |
| 2015 | − }); | |
| 2016 | − }, | |
| 2017 | − | |
| 2018 | − _makeRequest: function(opts) { | |
| 2019 | − var request = _window.XMLHttpRequest && new _window.XMLHttpRequest(); | |
| 2020 | − if (!request) return; | |
| 2021 | − | |
| 2022 | − // if browser doesn't support CORS (e.g. IE7), we are out of luck | |
| 2023 | − var hasCORS = | |
| 2024 | − 'withCredentials' in request || typeof XDomainRequest !== 'undefined'; | |
| 2025 | − | |
| 2026 | − if (!hasCORS) return; | |
| 2027 | − | |
| 2028 | − var url = opts.url; | |
| 2029 | − | |
| 2030 | − if ('withCredentials' in request) { | |
| 2031 | − request.onreadystatechange = function() { | |
| 2032 | − if (request.readyState !== 4) { | |
| 2033 | − return; | |
| 2034 | − } else if (request.status === 200) { | |
| 2035 | − opts.onSuccess && opts.onSuccess(); | |
| 2036 | − } else if (opts.onError) { | |
| 2037 | − var err = new Error('Sentry error code: ' + request.status); | |
| 2038 | − err.request = request; | |
| 2039 | − opts.onError(err); | |
| 2040 | − } | |
| 2041 | − }; | |
| 2042 | − } else { | |
| 2043 | − request = new XDomainRequest(); | |
| 2044 | − // xdomainrequest cannot go http -> https (or vice versa), | |
| 2045 | − // so always use protocol relative | |
| 2046 | − url = url.replace(/^https?:/, ''); | |
| 2047 | − | |
| 2048 | − // onreadystatechange not supported by XDomainRequest | |
| 2049 | − if (opts.onSuccess) { | |
| 2050 | − request.onload = opts.onSuccess; | |
| 2051 | − } | |
| 2052 | − if (opts.onError) { | |
| 2053 | − request.onerror = function() { | |
| 2054 | − var err = new Error('Sentry error code: XDomainRequest'); | |
| 2055 | − err.request = request; | |
| 2056 | − opts.onError(err); | |
| 2057 | − }; | |
| 2058 | − } | |
| 2059 | − } | |
| 1900 | + var exception = data.exception && data.exception.values[0]; | |
| 1901 | + | |
| 1902 | + // only capture 'sentry' breadcrumb is autoBreadcrumbs is truthy | |
| 1903 | + if ( | |
| 1904 | + this._globalOptions.autoBreadcrumbs && | |
| 1905 | + this._globalOptions.autoBreadcrumbs.sentry | |
| 1906 | + ) { | |
| 1907 | + this.captureBreadcrumb({ | |
| 1908 | + category: 'sentry', | |
| 1909 | + message: exception | |
| 1910 | + ? (exception.type ? exception.type + ': ' : '') + exception.value | |
| 1911 | + : data.message, | |
| 1912 | + event_id: data.event_id, | |
| 1913 | + level: data.level || 'error' // presume error unless specified | |
| 1914 | + }); | |
| 1915 | + } | |
| 2060 | 1916 | |
| 2061 | − // NOTE: auth is intentionally sent as part of query string (NOT as custom | |
| 2062 | − // HTTP header) so as to avoid preflight CORS requests | |
| 2063 | − request.open('POST', url + '?' + urlencode(opts.auth)); | |
| 2064 | − request.send(stringify(opts.data)); | |
| 2065 | − }, | |
| 2066 | − | |
| 2067 | − _logDebug: function(level) { | |
| 2068 | − if (this._originalConsoleMethods[level] && this.debug) { | |
| 2069 | − // In IE<10 console methods do not have their own 'apply' method | |
| 2070 | − Function.prototype.apply.call( | |
| 2071 | − this._originalConsoleMethods[level], | |
| 2072 | − this._originalConsole, | |
| 2073 | − [].slice.call(arguments, 1) | |
| 2074 | − ); | |
| 2075 | − } | |
| 2076 | − }, | |
| 2077 | − | |
| 2078 | − _mergeContext: function(key, context) { | |
| 2079 | − if (isUndefined(context)) { | |
| 2080 | − delete this._globalContext[key]; | |
| 2081 | − } else { | |
| 2082 | − this._globalContext[key] = objectMerge( | |
| 2083 | − this._globalContext[key] || {}, | |
| 2084 | − context | |
| 2085 | − ); | |
| 2086 | − } | |
| 2087 | − } | |
| 2088 | − }; | |
| 1917 | + var url = this._globalEndpoint; | |
| 1918 | + (globalOptions.transport || this._makeRequest).call(this, { | |
| 1919 | + url: url, | |
| 1920 | + auth: auth, | |
| 1921 | + data: data, | |
| 1922 | + options: globalOptions, | |
| 1923 | + onSuccess: function success() { | |
| 1924 | + self._resetBackoff(); | |
| 1925 | + | |
| 1926 | + self._triggerEvent('success', { | |
| 1927 | + data: data, | |
| 1928 | + src: url | |
| 1929 | + }); | |
| 1930 | + callback && callback(); | |
| 1931 | + }, | |
| 1932 | + onError: function failure(error) { | |
| 1933 | + self._logDebug('error', 'Raven transport failed to send: ', error); | |
| 1934 | + | |
| 1935 | + if (error.request) { | |
| 1936 | + self._setBackoffState(error.request); | |
| 1937 | + } | |
| 1938 | + | |
| 1939 | + self._triggerEvent('failure', { | |
| 1940 | + data: data, | |
| 1941 | + src: url | |
| 1942 | + }); | |
| 1943 | + error = error || new Error('Raven send failed (no additional details provided)'); | |
| 1944 | + callback && callback(error); | |
| 1945 | + } | |
| 1946 | + }); | |
| 1947 | + }, | |
| 1948 | + | |
| 1949 | + _makeRequest: function(opts) { | |
| 1950 | + var request = _window.XMLHttpRequest && new _window.XMLHttpRequest(); | |
| 1951 | + if (!request) return; | |
| 1952 | + | |
| 1953 | + // if browser doesn't support CORS (e.g. IE7), we are out of luck | |
| 1954 | + var hasCORS = 'withCredentials' in request || typeof XDomainRequest !== 'undefined'; | |
| 1955 | + | |
| 1956 | + if (!hasCORS) return; | |
| 1957 | + | |
| 1958 | + var url = opts.url; | |
| 1959 | + | |
| 1960 | + if ('withCredentials' in request) { | |
| 1961 | + request.onreadystatechange = function() { | |
| 1962 | + if (request.readyState !== 4) { | |
| 1963 | + return; | |
| 1964 | + } else if (request.status === 200) { | |
| 1965 | + opts.onSuccess && opts.onSuccess(); | |
| 1966 | + } else if (opts.onError) { | |
| 1967 | + var err = new Error('Sentry error code: ' + request.status); | |
| 1968 | + err.request = request; | |
| 1969 | + opts.onError(err); | |
| 1970 | + } | |
| 1971 | + }; | |
| 1972 | + } else { | |
| 1973 | + request = new XDomainRequest(); | |
| 1974 | + // xdomainrequest cannot go http -> https (or vice versa), | |
| 1975 | + // so always use protocol relative | |
| 1976 | + url = url.replace(/^https?:/, ''); | |
| 1977 | + | |
| 1978 | + // onreadystatechange not supported by XDomainRequest | |
| 1979 | + if (opts.onSuccess) { | |
| 1980 | + request.onload = opts.onSuccess; | |
| 1981 | + } | |
| 1982 | + if (opts.onError) { | |
| 1983 | + request.onerror = function() { | |
| 1984 | + var err = new Error('Sentry error code: XDomainRequest'); | |
| 1985 | + err.request = request; | |
| 1986 | + opts.onError(err); | |
| 1987 | + }; | |
| 1988 | + } | |
| 1989 | + } | |
| 1990 | + | |
| 1991 | + // NOTE: auth is intentionally sent as part of query string (NOT as custom | |
| 1992 | + // HTTP header) so as to avoid preflight CORS requests | |
| 1993 | + request.open('POST', url + '?' + urlencode(opts.auth)); | |
| 1994 | + request.send(stringify(opts.data)); | |
| 1995 | + }, | |
| 1996 | + | |
| 1997 | + _logDebug: function(level) { | |
| 1998 | + if (this._originalConsoleMethods[level] && this.debug) { | |
| 1999 | + // In IE<10 console methods do not have their own 'apply' method | |
| 2000 | + Function.prototype.apply.call( | |
| 2001 | + this._originalConsoleMethods[level], | |
| 2002 | + this._originalConsole, | |
| 2003 | + [].slice.call(arguments, 1) | |
| 2004 | + ); | |
| 2005 | + } | |
| 2006 | + }, | |
| 2007 | + | |
| 2008 | + _mergeContext: function(key, context) { | |
| 2009 | + if (isUndefined(context)) { | |
| 2010 | + delete this._globalContext[key]; | |
| 2011 | + } else { | |
| 2012 | + this._globalContext[key] = objectMerge(this._globalContext[key] || {}, context); | |
| 2013 | + } | |
| 2014 | + } | |
| 2015 | +}; | |
| 2016 | + | |
| 2017 | +// Deprecations | |
| 2018 | +Raven.prototype.setUser = Raven.prototype.setUserContext; | |
| 2019 | +Raven.prototype.setReleaseContext = Raven.prototype.setRelease; | |
| 2020 | + | |
| 2021 | +module.exports = Raven; | |
| 2089 | 2022 | |
| 2090 | − /*------------------------------------------------ | |
| 2091 | − * utils | |
| 2023 | +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
| 2024 | +},{"1":1,"2":2,"5":5,"6":6,"7":7}],4:[function(_dereq_,module,exports){ | |
| 2025 | +(function (global){ | |
| 2026 | +/** | |
| 2027 | + * Enforces a single instance of the Raven client, and the | |
| 2028 | + * main entry point for Raven. If you are a consumer of the | |
| 2029 | + * Raven library, you SHOULD load this file (vs raven.js). | |
| 2030 | + **/ | |
| 2031 | + | |
| 2032 | +var RavenConstructor = _dereq_(3); | |
| 2033 | + | |
| 2034 | +// This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785) | |
| 2035 | +var _window = | |
| 2036 | + typeof window !== 'undefined' | |
| 2037 | + ? window | |
| 2038 | + : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; | |
| 2039 | +var _Raven = _window.Raven; | |
| 2040 | + | |
| 2041 | +var Raven = new RavenConstructor(); | |
| 2042 | + | |
| 2043 | +/* | |
| 2044 | + * Allow multiple versions of Raven to be installed. | |
| 2045 | + * Strip Raven from the global context and returns the instance. | |
| 2092 | 2046 | * |
| 2093 | − * conditionally exported for test via Raven.utils | |
| 2094 | − ================================================= | |
| 2047 | + * @return {Raven} | |
| 2095 | 2048 | */ |
| 2096 | − var objectPrototype = Object.prototype; | |
| 2049 | +Raven.noConflict = function() { | |
| 2050 | + _window.Raven = _Raven; | |
| 2051 | + return Raven; | |
| 2052 | +}; | |
| 2053 | + | |
| 2054 | +Raven.afterLoad(); | |
| 2055 | + | |
| 2056 | +module.exports = Raven; | |
| 2057 | + | |
| 2058 | +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
| 2059 | +},{"3":3}],5:[function(_dereq_,module,exports){ | |
| 2060 | +(function (global){ | |
| 2061 | +var _window = | |
| 2062 | + typeof window !== 'undefined' | |
| 2063 | + ? window | |
| 2064 | + : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; | |
| 2065 | + | |
| 2066 | +function isObject(what) { | |
| 2067 | + return typeof what === 'object' && what !== null; | |
| 2068 | +} | |
| 2069 | + | |
| 2070 | +// Yanked from https://git.io/vS8DV re-used under CC0 | |
| 2071 | +// with some tiny modifications | |
| 2072 | +function isError(value) { | |
| 2073 | + switch ({}.toString.call(value)) { | |
| 2074 | + case '[object Error]': | |
| 2075 | + return true; | |
| 2076 | + case '[object Exception]': | |
| 2077 | + return true; | |
| 2078 | + case '[object DOMException]': | |
| 2079 | + return true; | |
| 2080 | + default: | |
| 2081 | + return value instanceof Error; | |
| 2082 | + } | |
| 2083 | +} | |
| 2097 | 2084 | |
| 2098 | − function isUndefined(what) { | |
| 2099 | − return what === void 0; | |
| 2100 | − } | |
| 2085 | +function isErrorEvent(value) { | |
| 2086 | + return supportsErrorEvent() && {}.toString.call(value) === '[object ErrorEvent]'; | |
| 2087 | +} | |
| 2101 | 2088 | |
| 2102 | − function isFunction(what) { | |
| 2103 | − return typeof what === 'function'; | |
| 2104 | − } | |
| 2089 | +function isUndefined(what) { | |
| 2090 | + return what === void 0; | |
| 2091 | +} | |
| 2105 | 2092 | |
| 2106 | − function isString(what) { | |
| 2107 | − return objectPrototype.toString.call(what) === '[object String]'; | |
| 2108 | − } | |
| 2093 | +function isFunction(what) { | |
| 2094 | + return typeof what === 'function'; | |
| 2095 | +} | |
| 2109 | 2096 | |
| 2110 | − function isEmptyObject(what) { | |
| 2111 | − for (var _ in what) return false; // eslint-disable-line guard-for-in, no-unused-vars | |
| 2112 | − return true; | |
| 2113 | − } | |
| 2097 | +function isString(what) { | |
| 2098 | + return Object.prototype.toString.call(what) === '[object String]'; | |
| 2099 | +} | |
| 2114 | 2100 | |
| 2115 | − function each(obj, callback) { | |
| 2116 | − var i, j; | |
| 2101 | +function isArray(what) { | |
| 2102 | + return Object.prototype.toString.call(what) === '[object Array]'; | |
| 2103 | +} | |
| 2117 | 2104 | |
| 2118 | − if (isUndefined(obj.length)) { | |
| 2119 | − for (i in obj) { | |
| 2120 | − if (hasKey(obj, i)) { | |
| 2121 | − callback.call(null, i, obj[i]); | |
| 2122 | − } | |
| 2123 | − } | |
| 2124 | − } else { | |
| 2125 | − j = obj.length; | |
| 2126 | − if (j) { | |
| 2127 | − for (i = 0; i < j; i++) { | |
| 2128 | − callback.call(null, i, obj[i]); | |
| 2129 | − } | |
| 2130 | − } | |
| 2131 | − } | |
| 2132 | − } | |
| 2105 | +function isEmptyObject(what) { | |
| 2106 | + for (var _ in what) { | |
| 2107 | + if (what.hasOwnProperty(_)) { | |
| 2108 | + return false; | |
| 2109 | + } | |
| 2110 | + } | |
| 2111 | + return true; | |
| 2112 | +} | |
| 2113 | + | |
| 2114 | +function supportsErrorEvent() { | |
| 2115 | + try { | |
| 2116 | + new ErrorEvent(''); // eslint-disable-line no-new | |
| 2117 | + return true; | |
| 2118 | + } catch (e) { | |
| 2119 | + return false; | |
| 2120 | + } | |
| 2121 | +} | |
| 2133 | 2122 | |
| 2134 | − function objectMerge(obj1, obj2) { | |
| 2135 | − if (!obj2) { | |
| 2136 | − return obj1; | |
| 2137 | − } | |
| 2138 | − each(obj2, function(key, value) { | |
| 2139 | − obj1[key] = value; | |
| 2140 | − }); | |
| 2141 | − return obj1; | |
| 2142 | − } | |
| 2123 | +function wrappedCallback(callback) { | |
| 2124 | + function dataCallback(data, original) { | |
| 2125 | + var normalizedData = callback(data) || data; | |
| 2126 | + if (original) { | |
| 2127 | + return original(normalizedData) || normalizedData; | |
| 2128 | + } | |
| 2129 | + return normalizedData; | |
| 2130 | + } | |
| 2131 | + | |
| 2132 | + return dataCallback; | |
| 2133 | +} | |
| 2134 | + | |
| 2135 | +function each(obj, callback) { | |
| 2136 | + var i, j; | |
| 2137 | + | |
| 2138 | + if (isUndefined(obj.length)) { | |
| 2139 | + for (i in obj) { | |
| 2140 | + if (hasKey(obj, i)) { | |
| 2141 | + callback.call(null, i, obj[i]); | |
| 2142 | + } | |
| 2143 | + } | |
| 2144 | + } else { | |
| 2145 | + j = obj.length; | |
| 2146 | + if (j) { | |
| 2147 | + for (i = 0; i < j; i++) { | |
| 2148 | + callback.call(null, i, obj[i]); | |
| 2149 | + } | |
| 2150 | + } | |
| 2151 | + } | |
| 2152 | +} | |
| 2153 | + | |
| 2154 | +function objectMerge(obj1, obj2) { | |
| 2155 | + if (!obj2) { | |
| 2156 | + return obj1; | |
| 2157 | + } | |
| 2158 | + each(obj2, function(key, value) { | |
| 2159 | + obj1[key] = value; | |
| 2160 | + }); | |
| 2161 | + return obj1; | |
| 2162 | +} | |
| 2143 | 2163 | |
| 2144 | − /** | |
| 2164 | +/** | |
| 2145 | 2165 | * This function is only used for react-native. |
| 2146 | 2166 | * react-native freezes object that have already been sent over the |
| 2147 | 2167 | * js bridge. We need this function in order to check if the object is frozen. |
| @@ -2149,398 +2169,298 @@ · 1 unmodified line | ||
| 2149 | 2169 | * supported because it's not relevant for other "platforms". See related issue: |
| 2150 | 2170 | * https://github.com/getsentry/react-native-sentry/issues/57 |
| 2151 | 2171 | */ |
| 2152 | − function objectFrozen(obj) { | |
| 2153 | − if (!Object.isFrozen) { | |
| 2154 | − return false; | |
| 2155 | − } | |
| 2156 | − return Object.isFrozen(obj); | |
| 2157 | − } | |
| 2172 | +function objectFrozen(obj) { | |
| 2173 | + if (!Object.isFrozen) { | |
| 2174 | + return false; | |
| 2175 | + } | |
| 2176 | + return Object.isFrozen(obj); | |
| 2177 | +} | |
| 2158 | 2178 | |
| 2159 | − function truncate(str, max) { | |
| 2160 | − return !max || str.length <= max ? str : str.substr(0, max) + '\u2026'; | |
| 2161 | − } | |
| 2179 | +function truncate(str, max) { | |
| 2180 | + return !max || str.length <= max ? str : str.substr(0, max) + '\u2026'; | |
| 2181 | +} | |
| 2162 | 2182 | |
| 2163 | − /** | |
| 2183 | +/** | |
| 2164 | 2184 | * hasKey, a better form of hasOwnProperty |
| 2165 | 2185 | * Example: hasKey(MainHostObject, property) === true/false |
| 2166 | 2186 | * |
| 2167 | 2187 | * @param {Object} host object to check property |
| 2168 | 2188 | * @param {string} key to check |
| 2169 | 2189 | */ |
| 2170 | − function hasKey(object, key) { | |
| 2171 | − return objectPrototype.hasOwnProperty.call(object, key); | |
| 2172 | − } | |
| 2173 | − | |
| 2174 | − function joinRegExp(patterns) { | |
| 2175 | − // Combine an array of regular expressions and strings into one large regexp | |
| 2176 | − // Be mad. | |
| 2177 | − var sources = [], | |
| 2178 | − i = 0, | |
| 2179 | − len = patterns.length, | |
| 2180 | − pattern; | |
| 2181 | − | |
| 2182 | − for (; i < len; i++) { | |
| 2183 | − pattern = patterns[i]; | |
| 2184 | − if (isString(pattern)) { | |
| 2185 | − // If it's a string, we need to escape it | |
| 2186 | − // Taken from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions | |
| 2187 | − sources.push(pattern.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1')); | |
| 2188 | − } else if (pattern && pattern.source) { | |
| 2189 | − // If it's a regexp already, we want to extract the source | |
| 2190 | − sources.push(pattern.source); | |
| 2191 | − } | |
| 2192 | − // Intentionally skip other cases | |
| 2193 | − } | |
| 2194 | − return new RegExp(sources.join('|'), 'i'); | |
| 2195 | − } | |
| 2196 | − | |
| 2197 | − function urlencode(o) { | |
| 2198 | − var pairs = []; | |
| 2199 | − each(o, function(key, value) { | |
| 2200 | − pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(value)); | |
| 2201 | − }); | |
| 2202 | − return pairs.join('&'); | |
| 2203 | − } | |
| 2204 | − | |
| 2205 | − // borrowed from https://tools.ietf.org/html/rfc3986#appendix-B | |
| 2206 | − // intentionally using regex and not <a/> href parsing trick because React Native and other | |
| 2207 | − // environments where DOM might not be available | |
| 2208 | − function parseUrl(url) { | |
| 2209 | − var match = url.match( | |
| 2210 | − /^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/ | |
| 2211 | − ); | |
| 2212 | − if (!match) return {}; | |
| 2213 | − | |
| 2214 | − // coerce to undefined values to empty string so we don't get 'undefined' | |
| 2215 | − var query = match[6] || ''; | |
| 2216 | − var fragment = match[8] || ''; | |
| 2217 | − return { | |
| 2218 | − protocol: match[2], | |
| 2219 | − host: match[4], | |
| 2220 | − path: match[5], | |
| 2221 | − relative: match[5] + query + fragment // everything minus origin | |
| 2222 | − }; | |
| 2223 | − } | |
| 2224 | − function uuid4() { | |
| 2225 | − var crypto = _window.crypto || _window.msCrypto; | |
| 2226 | − | |
| 2227 | − if (!isUndefined(crypto) && crypto.getRandomValues) { | |
| 2228 | − // Use window.crypto API if available | |
| 2229 | − // eslint-disable-next-line no-undef | |
| 2230 | − var arr = new Uint16Array(8); | |
| 2231 | − crypto.getRandomValues(arr); | |
| 2232 | − | |
| 2233 | − // set 4 in byte 7 | |
| 2234 | − arr[3] = (arr[3] & 0xfff) | 0x4000; | |
| 2235 | − // set 2 most significant bits of byte 9 to '10' | |
| 2236 | − arr[4] = (arr[4] & 0x3fff) | 0x8000; | |
| 2237 | − | |
| 2238 | − var pad = function(num) { | |
| 2239 | − var v = num.toString(16); | |
| 2240 | − while (v.length < 4) { | |
| 2241 | − v = '0' + v; | |
| 2242 | − } | |
| 2243 | − return v; | |
| 2244 | − }; | |
| 2245 | − | |
| 2246 | − return ( | |
| 2247 | − pad(arr[0]) + | |
| 2248 | − pad(arr[1]) + | |
| 2249 | − pad(arr[2]) + | |
| 2250 | − pad(arr[3]) + | |
| 2251 | − pad(arr[4]) + | |
| 2252 | − pad(arr[5]) + | |
| 2253 | − pad(arr[6]) + | |
| 2254 | − pad(arr[7]) | |
| 2255 | − ); | |
| 2256 | − } else { | |
| 2257 | − // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523 | |
| 2258 | − return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) { | |
| 2259 | − var r = (Math.random() * 16) | 0, | |
| 2260 | − v = c === 'x' ? r : (r & 0x3) | 0x8; | |
| 2261 | − return v.toString(16); | |
| 2262 | − }); | |
| 2263 | − } | |
| 2264 | − } | |
| 2190 | +function hasKey(object, key) { | |
| 2191 | + return Object.prototype.hasOwnProperty.call(object, key); | |
| 2192 | +} | |
| 2193 | + | |
| 2194 | +function joinRegExp(patterns) { | |
| 2195 | + // Combine an array of regular expressions and strings into one large regexp | |
| 2196 | + // Be mad. | |
| 2197 | + var sources = [], | |
| 2198 | + i = 0, | |
| 2199 | + len = patterns.length, | |
| 2200 | + pattern; | |
| 2201 | + | |
| 2202 | + for (; i < len; i++) { | |
| 2203 | + pattern = patterns[i]; | |
| 2204 | + if (isString(pattern)) { | |
| 2205 | + // If it's a string, we need to escape it | |
| 2206 | + // Taken from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions | |
| 2207 | + sources.push(pattern.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1')); | |
| 2208 | + } else if (pattern && pattern.source) { | |
| 2209 | + // If it's a regexp already, we want to extract the source | |
| 2210 | + sources.push(pattern.source); | |
| 2211 | + } | |
| 2212 | + // Intentionally skip other cases | |
| 2213 | + } | |
| 2214 | + return new RegExp(sources.join('|'), 'i'); | |
| 2215 | +} | |
| 2216 | + | |
| 2217 | +function urlencode(o) { | |
| 2218 | + var pairs = []; | |
| 2219 | + each(o, function(key, value) { | |
| 2220 | + pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(value)); | |
| 2221 | + }); | |
| 2222 | + return pairs.join('&'); | |
| 2223 | +} | |
| 2224 | + | |
| 2225 | +// borrowed from https://tools.ietf.org/html/rfc3986#appendix-B | |
| 2226 | +// intentionally using regex and not <a/> href parsing trick because React Native and other | |
| 2227 | +// environments where DOM might not be available | |
| 2228 | +function parseUrl(url) { | |
| 2229 | + var match = url.match(/^(([^:\/?#]+):)?(\/\/([^\/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?$/); | |
| 2230 | + if (!match) return {}; | |
| 2231 | + | |
| 2232 | + // coerce to undefined values to empty string so we don't get 'undefined' | |
| 2233 | + var query = match[6] || ''; | |
| 2234 | + var fragment = match[8] || ''; | |
| 2235 | + return { | |
| 2236 | + protocol: match[2], | |
| 2237 | + host: match[4], | |
| 2238 | + path: match[5], | |
| 2239 | + relative: match[5] + query + fragment // everything minus origin | |
| 2240 | + }; | |
| 2241 | +} | |
| 2242 | +function uuid4() { | |
| 2243 | + var crypto = _window.crypto || _window.msCrypto; | |
| 2244 | + | |
| 2245 | + if (!isUndefined(crypto) && crypto.getRandomValues) { | |
| 2246 | + // Use window.crypto API if available | |
| 2247 | + // eslint-disable-next-line no-undef | |
| 2248 | + var arr = new Uint16Array(8); | |
| 2249 | + crypto.getRandomValues(arr); | |
| 2250 | + | |
| 2251 | + // set 4 in byte 7 | |
| 2252 | + arr[3] = (arr[3] & 0xfff) | 0x4000; | |
| 2253 | + // set 2 most significant bits of byte 9 to '10' | |
| 2254 | + arr[4] = (arr[4] & 0x3fff) | 0x8000; | |
| 2255 | + | |
| 2256 | + var pad = function(num) { | |
| 2257 | + var v = num.toString(16); | |
| 2258 | + while (v.length < 4) { | |
| 2259 | + v = '0' + v; | |
| 2260 | + } | |
| 2261 | + return v; | |
| 2262 | + }; | |
| 2263 | + | |
| 2264 | + return ( | |
| 2265 | + pad(arr[0]) + | |
| 2266 | + pad(arr[1]) + | |
| 2267 | + pad(arr[2]) + | |
| 2268 | + pad(arr[3]) + | |
| 2269 | + pad(arr[4]) + | |
| 2270 | + pad(arr[5]) + | |
| 2271 | + pad(arr[6]) + | |
| 2272 | + pad(arr[7]) | |
| 2273 | + ); | |
| 2274 | + } else { | |
| 2275 | + // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/2117523#2117523 | |
| 2276 | + return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) { | |
| 2277 | + var r = (Math.random() * 16) | 0, | |
| 2278 | + v = c === 'x' ? r : (r & 0x3) | 0x8; | |
| 2279 | + return v.toString(16); | |
| 2280 | + }); | |
| 2281 | + } | |
| 2282 | +} | |
| 2265 | 2283 | |
| 2266 | − /** | |
| 2284 | +/** | |
| 2267 | 2285 | * Given a child DOM element, returns a query-selector statement describing that |
| 2268 | 2286 | * and its ancestors |
| 2269 | 2287 | * e.g. [HTMLElement] => body > div > input#foo.btn[name=baz] |
| 2270 | 2288 | * @param elem |
| 2271 | 2289 | * @returns {string} |
| 2272 | 2290 | */ |
| 2273 | − function htmlTreeAsString(elem) { | |
| 2274 | − /* eslint no-extra-parens:0*/ | |
| 2275 | − var MAX_TRAVERSE_HEIGHT = 5, | |
| 2276 | − MAX_OUTPUT_LEN = 80, | |
| 2277 | − out = [], | |
| 2278 | − height = 0, | |
| 2279 | − len = 0, | |
| 2280 | − separator = ' > ', | |
| 2281 | − sepLength = separator.length, | |
| 2282 | − nextStr; | |
| 2283 | − | |
| 2284 | − while (elem && height++ < MAX_TRAVERSE_HEIGHT) { | |
| 2285 | − nextStr = htmlElementAsString(elem); | |
| 2286 | − // bail out if | |
| 2287 | − // - nextStr is the 'html' element | |
| 2288 | − // - the length of the string that would be created exceeds MAX_OUTPUT_LEN | |
| 2289 | − // (ignore this limit if we are on the first iteration) | |
| 2290 | − if ( | |
| 2291 | − nextStr === 'html' || | |
| 2292 | − (height > 1 && | |
| 2293 | − len + out.length * sepLength + nextStr.length >= MAX_OUTPUT_LEN) | |
| 2294 | − ) { | |
| 2295 | − break; | |
| 2296 | − } | |
| 2291 | +function htmlTreeAsString(elem) { | |
| 2292 | + /* eslint no-extra-parens:0*/ | |
| 2293 | + var MAX_TRAVERSE_HEIGHT = 5, | |
| 2294 | + MAX_OUTPUT_LEN = 80, | |
| 2295 | + out = [], | |
| 2296 | + height = 0, | |
| 2297 | + len = 0, | |
| 2298 | + separator = ' > ', | |
| 2299 | + sepLength = separator.length, | |
| 2300 | + nextStr; | |
| 2301 | + | |
| 2302 | + while (elem && height++ < MAX_TRAVERSE_HEIGHT) { | |
| 2303 | + nextStr = htmlElementAsString(elem); | |
| 2304 | + // bail out if | |
| 2305 | + // - nextStr is the 'html' element | |
| 2306 | + // - the length of the string that would be created exceeds MAX_OUTPUT_LEN | |
| 2307 | + // (ignore this limit if we are on the first iteration) | |
| 2308 | + if ( | |
| 2309 | + nextStr === 'html' || | |
| 2310 | + (height > 1 && len + out.length * sepLength + nextStr.length >= MAX_OUTPUT_LEN) | |
| 2311 | + ) { | |
| 2312 | + break; | |
| 2313 | + } | |
| 2297 | 2314 | |
| 2298 | − out.push(nextStr); | |
| 2315 | + out.push(nextStr); | |
| 2299 | 2316 | |
| 2300 | − len += nextStr.length; | |
| 2301 | − elem = elem.parentNode; | |
| 2302 | − } | |
| 2317 | + len += nextStr.length; | |
| 2318 | + elem = elem.parentNode; | |
| 2319 | + } | |
| 2303 | 2320 | |
| 2304 | − return out.reverse().join(separator); | |
| 2305 | − } | |
| 2321 | + return out.reverse().join(separator); | |
| 2322 | +} | |
| 2306 | 2323 | |
| 2307 | − /** | |
| 2324 | +/** | |
| 2308 | 2325 | * Returns a simple, query-selector representation of a DOM element |
| 2309 | 2326 | * e.g. [HTMLElement] => input#foo.btn[name=baz] |
| 2310 | 2327 | * @param HTMLElement |
| 2311 | 2328 | * @returns {string} |
| 2312 | 2329 | */ |
| 2313 | − function htmlElementAsString(elem) { | |
| 2314 | − var out = [], | |
| 2315 | − className, | |
| 2316 | − classes, | |
| 2317 | − key, | |
| 2318 | − attr, | |
| 2319 | − i; | |
| 2320 | − | |
| 2321 | − if (!elem || !elem.tagName) { | |
| 2322 | − return ''; | |
| 2323 | − } | |
| 2330 | +function htmlElementAsString(elem) { | |
| 2331 | + var out = [], | |
| 2332 | + className, | |
| 2333 | + classes, | |
| 2334 | + key, | |
| 2335 | + attr, | |
| 2336 | + i; | |
| 2337 | + | |
| 2338 | + if (!elem || !elem.tagName) { | |
| 2339 | + return ''; | |
| 2340 | + } | |
| 2324 | 2341 | |
| 2325 | − out.push(elem.tagName.toLowerCase()); | |
| 2326 | − if (elem.id) { | |
| 2327 | − out.push('#' + elem.id); | |
| 2328 | − } | |
| 2342 | + out.push(elem.tagName.toLowerCase()); | |
| 2343 | + if (elem.id) { | |
| 2344 | + out.push('#' + elem.id); | |
| 2345 | + } | |
| 2329 | 2346 | |
| 2330 | − className = elem.className; | |
| 2331 | − if (className && isString(className)) { | |
| 2332 | − classes = className.split(/\s+/); | |
| 2333 | − for (i = 0; i < classes.length; i++) { | |
| 2334 | − out.push('.' + classes[i]); | |
| 2335 | − } | |
| 2336 | − } | |
| 2337 | − var attrWhitelist = ['type', 'name', 'title', 'alt']; | |
| 2338 | − for (i = 0; i < attrWhitelist.length; i++) { | |
| 2339 | − key = attrWhitelist[i]; | |
| 2340 | − attr = elem.getAttribute(key); | |
| 2341 | − if (attr) { | |
| 2342 | − out.push('[' + key + '="' + attr + '"]'); | |
| 2343 | − } | |
| 2344 | − } | |
| 2345 | − return out.join(''); | |
| 2346 | − } | |
| 2347 | + className = elem.className; | |
| 2348 | + if (className && isString(className)) { | |
| 2349 | + classes = className.split(/\s+/); | |
| 2350 | + for (i = 0; i < classes.length; i++) { | |
| 2351 | + out.push('.' + classes[i]); | |
| 2352 | + } | |
| 2353 | + } | |
| 2354 | + var attrWhitelist = ['type', 'name', 'title', 'alt']; | |
| 2355 | + for (i = 0; i < attrWhitelist.length; i++) { | |
| 2356 | + key = attrWhitelist[i]; | |
| 2357 | + attr = elem.getAttribute(key); | |
| 2358 | + if (attr) { | |
| 2359 | + out.push('[' + key + '="' + attr + '"]'); | |
| 2360 | + } | |
| 2361 | + } | |
| 2362 | + return out.join(''); | |
| 2363 | +} | |
| 2347 | 2364 | |
| 2348 | − /** | |
| 2365 | +/** | |
| 2349 | 2366 | * Returns true if either a OR b is truthy, but not both |
| 2350 | 2367 | */ |
| 2351 | − function isOnlyOneTruthy(a, b) { | |
| 2352 | − return !!(!!a ^ !!b); | |
| 2353 | − } | |
| 2368 | +function isOnlyOneTruthy(a, b) { | |
| 2369 | + return !!(!!a ^ !!b); | |
| 2370 | +} | |
| 2354 | 2371 | |
| 2355 | − /** | |
| 2372 | +/** | |
| 2356 | 2373 | * Returns true if the two input exception interfaces have the same content |
| 2357 | 2374 | */ |
| 2358 | − function isSameException(ex1, ex2) { | |
| 2359 | − if (isOnlyOneTruthy(ex1, ex2)) return false; | |
| 2375 | +function isSameException(ex1, ex2) { | |
| 2376 | + if (isOnlyOneTruthy(ex1, ex2)) return false; | |
| 2360 | 2377 | |
| 2361 | − ex1 = ex1.values[0]; | |
| 2362 | − ex2 = ex2.values[0]; | |
| 2378 | + ex1 = ex1.values[0]; | |
| 2379 | + ex2 = ex2.values[0]; | |
| 2363 | 2380 | |
| 2364 | − if (ex1.type !== ex2.type || ex1.value !== ex2.value) return false; | |
| 2381 | + if (ex1.type !== ex2.type || ex1.value !== ex2.value) return false; | |
| 2365 | 2382 | |
| 2366 | − return isSameStacktrace(ex1.stacktrace, ex2.stacktrace); | |
| 2367 | − } | |
| 2383 | + return isSameStacktrace(ex1.stacktrace, ex2.stacktrace); | |
| 2384 | +} | |
| 2368 | 2385 | |
| 2369 | − /** | |
| 2386 | +/** | |
| 2370 | 2387 | * Returns true if the two input stack trace interfaces have the same content |
| 2371 | 2388 | */ |
| 2372 | − function isSameStacktrace(stack1, stack2) { | |
| 2373 | − if (isOnlyOneTruthy(stack1, stack2)) return false; | |
| 2374 | − | |
| 2375 | − var frames1 = stack1.frames; | |
| 2376 | − var frames2 = stack2.frames; | |
| 2377 | − | |
| 2378 | − // Exit early if frame count differs | |
| 2379 | − if (frames1.length !== frames2.length) return false; | |
| 2380 | − | |
| 2381 | − // Iterate through every frame; bail out if anything differs | |
| 2382 | − var a, b; | |
| 2383 | − for (var i = 0; i < frames1.length; i++) { | |
| 2384 | − a = frames1[i]; | |
| 2385 | − b = frames2[i]; | |
| 2386 | − if ( | |
| 2387 | − a.filename !== b.filename || | |
| 2388 | − a.lineno !== b.lineno || | |
| 2389 | − a.colno !== b.colno || | |
| 2390 | − a['function'] !== b['function'] | |
| 2391 | − ) | |
| 2392 | − return false; | |
| 2393 | − } | |
| 2394 | − return true; | |
| 2395 | − } | |
| 2389 | +function isSameStacktrace(stack1, stack2) { | |
| 2390 | + if (isOnlyOneTruthy(stack1, stack2)) return false; | |
| 2391 | + | |
| 2392 | + var frames1 = stack1.frames; | |
| 2393 | + var frames2 = stack2.frames; | |
| 2394 | + | |
| 2395 | + // Exit early if frame count differs | |
| 2396 | + if (frames1.length !== frames2.length) return false; | |
| 2397 | + | |
| 2398 | + // Iterate through every frame; bail out if anything differs | |
| 2399 | + var a, b; | |
| 2400 | + for (var i = 0; i < frames1.length; i++) { | |
| 2401 | + a = frames1[i]; | |
| 2402 | + b = frames2[i]; | |
| 2403 | + if ( | |
| 2404 | + a.filename !== b.filename || | |
| 2405 | + a.lineno !== b.lineno || | |
| 2406 | + a.colno !== b.colno || | |
| 2407 | + a['function'] !== b['function'] | |
| 2408 | + ) | |
| 2409 | + return false; | |
| 2410 | + } | |
| 2411 | + return true; | |
| 2412 | +} | |
| 2396 | 2413 | |
| 2397 | − /** | |
| 2414 | +/** | |
| 2398 | 2415 | * Polyfill a method |
| 2399 | 2416 | * @param obj object e.g. `document` |
| 2400 | 2417 | * @param name method name present on object e.g. `addEventListener` |
| 2401 | 2418 | * @param replacement replacement function |
| 2402 | 2419 | * @param track {optional} record instrumentation to an array |
| 2403 | 2420 | */ |
| 2404 | − function fill(obj, name, replacement, track) { | |
| 2405 | − var orig = obj[name]; | |
| 2406 | − obj[name] = replacement(orig); | |
| 2407 | − if (track) { | |
| 2408 | − track.push([obj, name, orig]); | |
| 2409 | − } | |
| 2410 | − } | |
| 2411 | − | |
| 2412 | − if (typeof __DEV__ !== 'undefined' && __DEV__) { | |
| 2413 | − Raven.utils = { | |
| 2414 | − isUndefined: isUndefined, | |
| 2415 | − isFunction: isFunction, | |
| 2416 | − isString: isString, | |
| 2417 | − isObject: isObject, | |
| 2418 | − isEmptyObject: isEmptyObject, | |
| 2419 | − isError: isError, | |
| 2420 | − each: each, | |
| 2421 | − objectMerge: objectMerge, | |
| 2422 | − truncate: truncate, | |
| 2423 | − hasKey: hasKey, | |
| 2424 | − joinRegExp: joinRegExp, | |
| 2425 | − urlencode: urlencode, | |
| 2426 | − uuid4: uuid4, | |
| 2427 | − htmlTreeAsString: htmlTreeAsString, | |
| 2428 | − htmlElementAsString: htmlElementAsString, | |
| 2429 | − parseUrl: parseUrl, | |
| 2430 | − fill: fill | |
| 2431 | − }; | |
| 2432 | − } | |
| 2433 | − | |
| 2434 | − // Deprecations | |
| 2435 | − Raven.prototype.setUser = Raven.prototype.setUserContext; | |
| 2436 | − Raven.prototype.setReleaseContext = Raven.prototype.setRelease; | |
| 2437 | − | |
| 2438 | − module.exports = Raven; | |
| 2439 | − }.call( | |
| 2440 | − this, | |
| 2441 | − typeof global !== 'undefined' | |
| 2442 | − ? global | |
| 2443 | − : typeof self !== 'undefined' | |
| 2444 | − ? self | |
| 2445 | − : typeof window !== 'undefined' ? window : {} | |
| 2446 | − )); | |
| 2447 | − }, | |
| 2448 | − {'1': 1, '2': 2, '5': 5, '6': 6, '7': 7} | |
| 2449 | − ], | |
| 2450 | − 4: [ | |
| 2451 | − function(_dereq_, module, exports) { | |
| 2452 | − (function(global) { | |
| 2453 | − /** | |
| 2454 | − * Enforces a single instance of the Raven client, and the | |
| 2455 | − * main entry point for Raven. If you are a consumer of the | |
| 2456 | − * Raven library, you SHOULD load this file (vs raven.js). | |
| 2457 | − **/ | |
| 2458 | − | |
| 2459 | − var RavenConstructor = _dereq_(3); | |
| 2460 | − | |
| 2461 | − // This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785) | |
| 2462 | − var _window = | |
| 2463 | − typeof window !== 'undefined' | |
| 2464 | − ? window | |
| 2465 | − : typeof global !== 'undefined' | |
| 2466 | − ? global | |
| 2467 | − : typeof self !== 'undefined' ? self : {}; | |
| 2468 | − var _Raven = _window.Raven; | |
| 2469 | − | |
| 2470 | − var Raven = new RavenConstructor(); | |
| 2471 | − | |
| 2472 | − /* | |
| 2473 | − * Allow multiple versions of Raven to be installed. | |
| 2474 | − * Strip Raven from the global context and returns the instance. | |
| 2475 | − * | |
| 2476 | − * @return {Raven} | |
| 2477 | − */ | |
| 2478 | − Raven.noConflict = function() { | |
| 2479 | − _window.Raven = _Raven; | |
| 2480 | − return Raven; | |
| 2481 | − }; | |
| 2482 | − | |
| 2483 | − Raven.afterLoad(); | |
| 2484 | − | |
| 2485 | − module.exports = Raven; | |
| 2486 | − }.call( | |
| 2487 | − this, | |
| 2488 | − typeof global !== 'undefined' | |
| 2489 | − ? global | |
| 2490 | − : typeof self !== 'undefined' | |
| 2491 | − ? self | |
| 2492 | − : typeof window !== 'undefined' ? window : {} | |
| 2493 | − )); | |
| 2494 | − }, | |
| 2495 | − {'3': 3} | |
| 2496 | − ], | |
| 2497 | − 5: [ | |
| 2498 | − function(_dereq_, module, exports) { | |
| 2499 | − function isObject(what) { | |
| 2500 | − return typeof what === 'object' && what !== null; | |
| 2501 | − } | |
| 2502 | − | |
| 2503 | − // Yanked from https://git.io/vS8DV re-used under CC0 | |
| 2504 | − // with some tiny modifications | |
| 2505 | − function isError(value) { | |
| 2506 | − switch ({}.toString.call(value)) { | |
| 2507 | − case '[object Error]': | |
| 2508 | − return true; | |
| 2509 | − case '[object Exception]': | |
| 2510 | − return true; | |
| 2511 | − case '[object DOMException]': | |
| 2512 | − return true; | |
| 2513 | − default: | |
| 2514 | − return value instanceof Error; | |
| 2515 | − } | |
| 2516 | − } | |
| 2517 | − | |
| 2518 | − function wrappedCallback(callback) { | |
| 2519 | − function dataCallback(data, original) { | |
| 2520 | − var normalizedData = callback(data) || data; | |
| 2521 | − if (original) { | |
| 2522 | − return original(normalizedData) || normalizedData; | |
| 2523 | − } | |
| 2524 | − return normalizedData; | |
| 2525 | − } | |
| 2526 | − | |
| 2527 | − return dataCallback; | |
| 2528 | − } | |
| 2421 | +function fill(obj, name, replacement, track) { | |
| 2422 | + var orig = obj[name]; | |
| 2423 | + obj[name] = replacement(orig); | |
| 2424 | + obj[name].__raven__ = true; | |
| 2425 | + obj[name].__orig__ = orig; | |
| 2426 | + if (track) { | |
| 2427 | + track.push([obj, name, orig]); | |
| 2428 | + } | |
| 2429 | +} | |
| 2430 | + | |
| 2431 | +module.exports = { | |
| 2432 | + isObject: isObject, | |
| 2433 | + isError: isError, | |
| 2434 | + isErrorEvent: isErrorEvent, | |
| 2435 | + isUndefined: isUndefined, | |
| 2436 | + isFunction: isFunction, | |
| 2437 | + isString: isString, | |
| 2438 | + isArray: isArray, | |
| 2439 | + isEmptyObject: isEmptyObject, | |
| 2440 | + supportsErrorEvent: supportsErrorEvent, | |
| 2441 | + wrappedCallback: wrappedCallback, | |
| 2442 | + each: each, | |
| 2443 | + objectMerge: objectMerge, | |
| 2444 | + truncate: truncate, | |
| 2445 | + objectFrozen: objectFrozen, | |
| 2446 | + hasKey: hasKey, | |
| 2447 | + joinRegExp: joinRegExp, | |
| 2448 | + urlencode: urlencode, | |
| 2449 | + uuid4: uuid4, | |
| 2450 | + htmlTreeAsString: htmlTreeAsString, | |
| 2451 | + htmlElementAsString: htmlElementAsString, | |
| 2452 | + isSameException: isSameException, | |
| 2453 | + isSameStacktrace: isSameStacktrace, | |
| 2454 | + parseUrl: parseUrl, | |
| 2455 | + fill: fill | |
| 2456 | +}; | |
| 2457 | + | |
| 2458 | +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) | |
| 2459 | +},{}],6:[function(_dereq_,module,exports){ | |
| 2460 | +(function (global){ | |
| 2461 | +var utils = _dereq_(5); | |
| 2529 | 2462 | |
| 2530 | − module.exports = { | |
| 2531 | − isObject: isObject, | |
| 2532 | − isError: isError, | |
| 2533 | − wrappedCallback: wrappedCallback | |
| 2534 | − }; | |
| 2535 | − }, | |
| 2536 | − {} | |
| 2537 | − ], | |
| 2538 | − 6: [ | |
| 2539 | − function(_dereq_, module, exports) { | |
| 2540 | − (function(global) { | |
| 2541 | − var utils = _dereq_(5); | |
| 2542 | − | |
| 2543 | − /* | |
| 2463 | +/* | |
| 2544 | 2464 | TraceKit - Cross brower stack traces |
| 2545 | 2465 | |
| 2546 | 2466 | This was originally forked from github.com/occ/TraceKit, but has since been |
| @@ -2550,33 +2470,31 @@ · 3 unmodified lines | ||
| 2550 | 2470 | MIT license |
| 2551 | 2471 | */ |
| 2552 | 2472 | |
| 2553 | − var TraceKit = { | |
| 2554 | − collectWindowErrors: true, | |
| 2555 | − debug: false | |
| 2556 | − }; | |
| 2473 | +var TraceKit = { | |
| 2474 | + collectWindowErrors: true, | |
| 2475 | + debug: false | |
| 2476 | +}; | |
| 2557 | 2477 | |
| 2558 | − // This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785) | |
| 2559 | − var _window = | |
| 2560 | − typeof window !== 'undefined' | |
| 2561 | − ? window | |
| 2562 | − : typeof global !== 'undefined' | |
| 2563 | − ? global | |
| 2564 | − : typeof self !== 'undefined' ? self : {}; | |
| 2478 | +// This is to be defensive in environments where window does not exist (see https://github.com/getsentry/raven-js/pull/785) | |
| 2479 | +var _window = | |
| 2480 | + typeof window !== 'undefined' | |
| 2481 | + ? window | |
| 2482 | + : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; | |
| 2565 | 2483 | |
| 2566 | − // global reference to slice | |
| 2567 | − var _slice = [].slice; | |
| 2568 | − var UNKNOWN_FUNCTION = '?'; | |
| 2484 | +// global reference to slice | |
| 2485 | +var _slice = [].slice; | |
| 2486 | +var UNKNOWN_FUNCTION = '?'; | |
| 2569 | 2487 | |
| 2570 | − // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#Error_types | |
| 2571 | − var ERROR_TYPES_RE = /^(?:[Uu]ncaught (?:exception: )?)?(?:((?:Eval|Internal|Range|Reference|Syntax|Type|URI|)Error): )?(.*)$/; | |
| 2488 | +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#Error_types | |
| 2489 | +var ERROR_TYPES_RE = /^(?:[Uu]ncaught (?:exception: )?)?(?:((?:Eval|Internal|Range|Reference|Syntax|Type|URI|)Error): )?(.*)$/; | |
| 2572 | 2490 | |
| 2573 | − function getLocationHref() { | |
| 2574 | − if (typeof document === 'undefined' || document.location == null) return ''; | |
| 2491 | +function getLocationHref() { | |
| 2492 | + if (typeof document === 'undefined' || document.location == null) return ''; | |
| 2575 | 2493 | |
| 2576 | − return document.location.href; | |
| 2577 | − } | |
| 2494 | + return document.location.href; | |
| 2495 | +} | |
| 2578 | 2496 | |
| 2579 | − /** | |
| 2497 | +/** | |
| 2580 | 2498 | * TraceKit.report: cross-browser processing of unhandled exceptions |
| 2581 | 2499 | * |
| 2582 | 2500 | * Syntax: |
| @@ -2615,68 +2533,68 @@ · 32 unmodified lines | ||
| 2615 | 2533 | * Handlers receive a stackInfo object as described in the |
| 2616 | 2534 | * TraceKit.computeStackTrace docs. |
| 2617 | 2535 | */ |
| 2618 | − TraceKit.report = (function reportModuleWrapper() { | |
| 2619 | − var handlers = [], | |
| 2620 | − lastArgs = null, | |
| 2621 | − lastException = null, | |
| 2622 | − lastExceptionStack = null; | |
| 2536 | +TraceKit.report = (function reportModuleWrapper() { | |
| 2537 | + var handlers = [], | |
| 2538 | + lastArgs = null, | |
| 2539 | + lastException = null, | |
| 2540 | + lastExceptionStack = null; | |
| 2623 | 2541 | |
| 2624 | − /** | |
| 2542 | + /** | |
| 2625 | 2543 | * Add a crash handler. |
| 2626 | 2544 | * @param {Function} handler |
| 2627 | 2545 | */ |
| 2628 | − function subscribe(handler) { | |
| 2629 | − installGlobalHandler(); | |
| 2630 | − handlers.push(handler); | |
| 2631 | − } | |
| 2546 | + function subscribe(handler) { | |
| 2547 | + installGlobalHandler(); | |
| 2548 | + handlers.push(handler); | |
| 2549 | + } | |
| 2632 | 2550 | |
| 2633 | − /** | |
| 2551 | + /** | |
| 2634 | 2552 | * Remove a crash handler. |
| 2635 | 2553 | * @param {Function} handler |
| 2636 | 2554 | */ |
| 2637 | − function unsubscribe(handler) { | |
| 2638 | − for (var i = handlers.length - 1; i >= 0; --i) { | |
| 2639 | − if (handlers[i] === handler) { | |
| 2640 | − handlers.splice(i, 1); | |
| 2641 | − } | |
| 2642 | − } | |
| 2643 | − } | |
| 2555 | + function unsubscribe(handler) { | |
| 2556 | + for (var i = handlers.length - 1; i >= 0; --i) { | |
| 2557 | + if (handlers[i] === handler) { | |
| 2558 | + handlers.splice(i, 1); | |
| 2559 | + } | |
| 2560 | + } | |
| 2561 | + } | |
| 2644 | 2562 | |
| 2645 | − /** | |
| 2563 | + /** | |
| 2646 | 2564 | * Remove all crash handlers. |
| 2647 | 2565 | */ |
| 2648 | − function unsubscribeAll() { | |
| 2649 | − uninstallGlobalHandler(); | |
| 2650 | − handlers = []; | |
| 2651 | − } | |
| 2566 | + function unsubscribeAll() { | |
| 2567 | + uninstallGlobalHandler(); | |
| 2568 | + handlers = []; | |
| 2569 | + } | |
| 2652 | 2570 | |
| 2653 | − /** | |
| 2571 | + /** | |
| 2654 | 2572 | * Dispatch stack information to all handlers. |
| 2655 | 2573 | * @param {Object.<string, *>} stack |
| 2656 | 2574 | */ |
| 2657 | − function notifyHandlers(stack, isWindowError) { | |
| 2658 | − var exception = null; | |
| 2659 | − if (isWindowError && !TraceKit.collectWindowErrors) { | |
| 2660 | − return; | |
| 2661 | − } | |
| 2662 | − for (var i in handlers) { | |
| 2663 | − if (handlers.hasOwnProperty(i)) { | |
| 2664 | − try { | |
| 2665 | − handlers[i].apply(null, [stack].concat(_slice.call(arguments, 2))); | |
| 2666 | − } catch (inner) { | |
| 2667 | − exception = inner; | |
| 2668 | − } | |
| 2669 | − } | |
| 2670 | − } | |
| 2575 | + function notifyHandlers(stack, isWindowError) { | |
| 2576 | + var exception = null; | |
| 2577 | + if (isWindowError && !TraceKit.collectWindowErrors) { | |
| 2578 | + return; | |
| 2579 | + } | |
| 2580 | + for (var i in handlers) { | |
| 2581 | + if (handlers.hasOwnProperty(i)) { | |
| 2582 | + try { | |
| 2583 | + handlers[i].apply(null, [stack].concat(_slice.call(arguments, 2))); | |
| 2584 | + } catch (inner) { | |
| 2585 | + exception = inner; | |
| 2586 | + } | |
| 2587 | + } | |
| 2588 | + } | |
| 2671 | 2589 | |
| 2672 | − if (exception) { | |
| 2673 | − throw exception; | |
| 2674 | − } | |
| 2675 | − } | |
| 2590 | + if (exception) { | |
| 2591 | + throw exception; | |
| 2592 | + } | |
| 2593 | + } | |
| 2676 | 2594 | |
| 2677 | − var _oldOnerrorHandler, _onErrorHandlerInstalled; | |
| 2595 | + var _oldOnerrorHandler, _onErrorHandlerInstalled; | |
| 2678 | 2596 | |
| 2679 | − /** | |
| 2597 | + /** | |
| 2680 | 2598 | * Ensures all global unhandled exceptions are recorded. |
| 2681 | 2599 | * Supported by Gecko and IE. |
| 2682 | 2600 | * @param {string} message Error message. |
| @@ -2687,135 +2605,132 @@ · 4 unmodified lines | ||
| 2687 | 2605 | * occurred. |
| 2688 | 2606 | * @param {?Error} ex The actual Error object. |
| 2689 | 2607 | */ |
| 2690 | − function traceKitWindowOnError(message, url, lineNo, colNo, ex) { | |
| 2691 | − var stack = null; | |
| 2692 | − | |
| 2693 | − if (lastExceptionStack) { | |
| 2694 | − TraceKit.computeStackTrace.augmentStackTraceWithInitialElement( | |
| 2695 | − lastExceptionStack, | |
| 2696 | − url, | |
| 2697 | − lineNo, | |
| 2698 | − message | |
| 2699 | − ); | |
| 2700 | − processLastException(); | |
| 2701 | − } else if (ex && utils.isError(ex)) { | |
| 2702 | − // non-string `ex` arg; attempt to extract stack trace | |
| 2703 | − | |
| 2704 | − // New chrome and blink send along a real error object | |
| 2705 | − // Let's just report that like a normal error. | |
| 2706 | − // See: https://mikewest.org/2013/08/debugging-runtime-errors-with-window-onerror | |
| 2707 | − stack = TraceKit.computeStackTrace(ex); | |
| 2708 | − notifyHandlers(stack, true); | |
| 2709 | − } else { | |
| 2710 | − var location = { | |
| 2711 | − url: url, | |
| 2712 | − line: lineNo, | |
| 2713 | − column: colNo | |
| 2714 | − }; | |
| 2715 | − | |
| 2716 | − var name = undefined; | |
| 2717 | − var msg = message; // must be new var or will modify original `arguments` | |
| 2718 | − var groups; | |
| 2719 | − if ({}.toString.call(message) === '[object String]') { | |
| 2720 | − var groups = message.match(ERROR_TYPES_RE); | |
| 2721 | − if (groups) { | |
| 2722 | − name = groups[1]; | |
| 2723 | − msg = groups[2]; | |
| 2724 | − } | |
| 2725 | − } | |
| 2608 | + function traceKitWindowOnError(message, url, lineNo, colNo, ex) { | |
| 2609 | + var stack = null; | |
| 2610 | + | |
| 2611 | + if (lastExceptionStack) { | |
| 2612 | + TraceKit.computeStackTrace.augmentStackTraceWithInitialElement( | |
| 2613 | + lastExceptionStack, | |
| 2614 | + url, | |
| 2615 | + lineNo, | |
| 2616 | + message | |
| 2617 | + ); | |
| 2618 | + processLastException(); | |
| 2619 | + } else if (ex && utils.isError(ex)) { | |
| 2620 | + // non-string `ex` arg; attempt to extract stack trace | |
| 2621 | + | |
| 2622 | + // New chrome and blink send along a real error object | |
| 2623 | + // Let's just report that like a normal error. | |
| 2624 | + // See: https://mikewest.org/2013/08/debugging-runtime-errors-with-window-onerror | |
| 2625 | + stack = TraceKit.computeStackTrace(ex); | |
| 2626 | + notifyHandlers(stack, true); | |
| 2627 | + } else { | |
| 2628 | + var location = { | |
| 2629 | + url: url, | |
| 2630 | + line: lineNo, | |
| 2631 | + column: colNo | |
| 2632 | + }; | |
| 2633 | + | |
| 2634 | + var name = undefined; | |
| 2635 | + var msg = message; // must be new var or will modify original `arguments` | |
| 2636 | + var groups; | |
| 2637 | + if ({}.toString.call(message) === '[object String]') { | |
| 2638 | + var groups = message.match(ERROR_TYPES_RE); | |
| 2639 | + if (groups) { | |
| 2640 | + name = groups[1]; | |
| 2641 | + msg = groups[2]; | |
| 2642 | + } | |
| 2643 | + } | |
| 2726 | 2644 | |
| 2727 | − location.func = UNKNOWN_FUNCTION; | |
| 2645 | + location.func = UNKNOWN_FUNCTION; | |
| 2728 | 2646 | |
| 2729 | − stack = { | |
| 2730 | − name: name, | |
| 2731 | − message: msg, | |
| 2732 | − url: getLocationHref(), | |
| 2733 | − stack: [location] | |
| 2734 | − }; | |
| 2735 | − notifyHandlers(stack, true); | |
| 2736 | − } | |
| 2647 | + stack = { | |
| 2648 | + name: name, | |
| 2649 | + message: msg, | |
| 2650 | + url: getLocationHref(), | |
| 2651 | + stack: [location] | |
| 2652 | + }; | |
| 2653 | + notifyHandlers(stack, true); | |
| 2654 | + } | |
| 2737 | 2655 | |
| 2738 | − if (_oldOnerrorHandler) { | |
| 2739 | − return _oldOnerrorHandler.apply(this, arguments); | |
| 2740 | − } | |
| 2656 | + if (_oldOnerrorHandler) { | |
| 2657 | + return _oldOnerrorHandler.apply(this, arguments); | |
| 2658 | + } | |
| 2741 | 2659 | |
| 2742 | − return false; | |
| 2743 | − } | |
| 2660 | + return false; | |
| 2661 | + } | |
| 2744 | 2662 | |
| 2745 | − function installGlobalHandler() { | |
| 2746 | − if (_onErrorHandlerInstalled) { | |
| 2747 | − return; | |
| 2748 | − } | |
| 2749 | − _oldOnerrorHandler = _window.onerror; | |
| 2750 | − _window.onerror = traceKitWindowOnError; | |
| 2751 | − _onErrorHandlerInstalled = true; | |
| 2752 | − } | |
| 2663 | + function installGlobalHandler() { | |
| 2664 | + if (_onErrorHandlerInstalled) { | |
| 2665 | + return; | |
| 2666 | + } | |
| 2667 | + _oldOnerrorHandler = _window.onerror; | |
| 2668 | + _window.onerror = traceKitWindowOnError; | |
| 2669 | + _onErrorHandlerInstalled = true; | |
| 2670 | + } | |
| 2753 | 2671 | |
| 2754 | − function uninstallGlobalHandler() { | |
| 2755 | − if (!_onErrorHandlerInstalled) { | |
| 2756 | − return; | |
| 2757 | − } | |
| 2758 | − _window.onerror = _oldOnerrorHandler; | |
| 2759 | − _onErrorHandlerInstalled = false; | |
| 2760 | − _oldOnerrorHandler = undefined; | |
| 2761 | − } | |
| 2672 | + function uninstallGlobalHandler() { | |
| 2673 | + if (!_onErrorHandlerInstalled) { | |
| 2674 | + return; | |
| 2675 | + } | |
| 2676 | + _window.onerror = _oldOnerrorHandler; | |
| 2677 | + _onErrorHandlerInstalled = false; | |
| 2678 | + _oldOnerrorHandler = undefined; | |
| 2679 | + } | |
| 2762 | 2680 | |
| 2763 | − function processLastException() { | |
| 2764 | − var _lastExceptionStack = lastExceptionStack, | |
| 2765 | − _lastArgs = lastArgs; | |
| 2766 | − lastArgs = null; | |
| 2767 | − lastExceptionStack = null; | |
| 2768 | − lastException = null; | |
| 2769 | − notifyHandlers.apply( | |
| 2770 | − null, | |
| 2771 | − [_lastExceptionStack, false].concat(_lastArgs) | |
| 2772 | − ); | |
| 2773 | − } | |
| 2681 | + function processLastException() { | |
| 2682 | + var _lastExceptionStack = lastExceptionStack, | |
| 2683 | + _lastArgs = lastArgs; | |
| 2684 | + lastArgs = null; | |
| 2685 | + lastExceptionStack = null; | |
| 2686 | + lastException = null; | |
| 2687 | + notifyHandlers.apply(null, [_lastExceptionStack, false].concat(_lastArgs)); | |
| 2688 | + } | |
| 2774 | 2689 | |
| 2775 | − /** | |
| 2690 | + /** | |
| 2776 | 2691 | * Reports an unhandled Error to TraceKit. |
| 2777 | 2692 | * @param {Error} ex |
| 2778 | 2693 | * @param {?boolean} rethrow If false, do not re-throw the exception. |
| 2779 | 2694 | * Only used for window.onerror to not cause an infinite loop of |
| 2780 | 2695 | * rethrowing. |
| 2781 | 2696 | */ |
| 2782 | − function report(ex, rethrow) { | |
| 2783 | − var args = _slice.call(arguments, 1); | |
| 2784 | − if (lastExceptionStack) { | |
| 2785 | − if (lastException === ex) { | |
| 2786 | − return; // already caught by an inner catch block, ignore | |
| 2787 | − } else { | |
| 2788 | − processLastException(); | |
| 2789 | − } | |
| 2790 | − } | |
| 2697 | + function report(ex, rethrow) { | |
| 2698 | + var args = _slice.call(arguments, 1); | |
| 2699 | + if (lastExceptionStack) { | |
| 2700 | + if (lastException === ex) { | |
| 2701 | + return; // already caught by an inner catch block, ignore | |
| 2702 | + } else { | |
| 2703 | + processLastException(); | |
| 2704 | + } | |
| 2705 | + } | |
| 2791 | 2706 | |
| 2792 | − var stack = TraceKit.computeStackTrace(ex); | |
| 2793 | − lastExceptionStack = stack; | |
| 2794 | − lastException = ex; | |
| 2795 | − lastArgs = args; | |
| 2796 | − | |
| 2797 | − // If the stack trace is incomplete, wait for 2 seconds for | |
| 2798 | − // slow slow IE to see if onerror occurs or not before reporting | |
| 2799 | − // this exception; otherwise, we will end up with an incomplete | |
| 2800 | − // stack trace | |
| 2801 | − setTimeout(function() { | |
| 2802 | − if (lastException === ex) { | |
| 2803 | − processLastException(); | |
| 2804 | − } | |
| 2805 | − }, stack.incomplete ? 2000 : 0); | |
| 2707 | + var stack = TraceKit.computeStackTrace(ex); | |
| 2708 | + lastExceptionStack = stack; | |
| 2709 | + lastException = ex; | |
| 2710 | + lastArgs = args; | |
| 2711 | + | |
| 2712 | + // If the stack trace is incomplete, wait for 2 seconds for | |
| 2713 | + // slow slow IE to see if onerror occurs or not before reporting | |
| 2714 | + // this exception; otherwise, we will end up with an incomplete | |
| 2715 | + // stack trace | |
| 2716 | + setTimeout(function() { | |
| 2717 | + if (lastException === ex) { | |
| 2718 | + processLastException(); | |
| 2719 | + } | |
| 2720 | + }, stack.incomplete ? 2000 : 0); | |
| 2806 | 2721 | |
| 2807 | − if (rethrow !== false) { | |
| 2808 | − throw ex; // re-throw to propagate to the top level (and cause window.onerror) | |
| 2809 | − } | |
| 2810 | − } | |
| 2722 | + if (rethrow !== false) { | |
| 2723 | + throw ex; // re-throw to propagate to the top level (and cause window.onerror) | |
| 2724 | + } | |
| 2725 | + } | |
| 2811 | 2726 | |
| 2812 | − report.subscribe = subscribe; | |
| 2813 | − report.unsubscribe = unsubscribe; | |
| 2814 | − report.uninstall = unsubscribeAll; | |
| 2815 | − return report; | |
| 2816 | − })(); | |
724 more lines aren’t shown here.
| @@ -19,7 +19,10 @@ class app.views.Content extends app.View · 18 unmodified lines | ||
| 19 | 19 | after: 'afterRoute' |
| 20 | 20 | |
| 21 | 21 | init: -> |
| 22 | − @scrollEl = if app.isMobile() then document.body else @el | |
| 22 | + @scrollEl = if app.isMobile() | |
| 23 | + (document.scrollingElement || document.body) | |
| 24 | + else | |
| 25 | + @el | |
| 23 | 26 | @scrollMap = {} |
| 24 | 27 | @scrollStack = [] |
| 25 | 28 | |
| @@ -150,6 +153,9 @@ class app.views.Content extends app.View · 124 unmodified lines | ||
| 150 | 153 | return |
| 151 | 154 | |
| 152 | 155 | afterRoute: (route, context) => |
| 156 | + if route != 'entry' and route != 'type' | |
| 157 | + resetFavicon() | |
| 158 | + | |
| 153 | 159 | switch route |
| 154 | 160 | when 'root' |
| 155 | 161 | @show @rootPage |
| @@ -40,6 +40,7 @@ class app.views.EntryPage extends app.View · 39 unmodified lines | ||
| 40 | 40 | if app.disabledDocs.findBy 'slug', @entry.doc.slug |
| 41 | 41 | @hiddenView = new app.views.HiddenPage @el, @entry |
| 42 | 42 | |
| 43 | + setFaviconForDoc(@entry.doc) | |
| 43 | 44 | @delay @polyfillMathML |
| 44 | 45 | @trigger 'loaded' |
| 45 | 46 | return |
| @@ -123,7 +124,7 @@ class app.views.EntryPage extends app.View · 77 unmodified lines | ||
| 123 | 124 | @render @tmpl('pageLoadError') |
| 124 | 125 | @resetClass() |
| 125 | 126 | @addClass @constructor.errorClass |
| 126 | − app.appCache?.update() | |
| 127 | + app.serviceWorker?.update() | |
| 127 | 128 | return |
| 128 | 129 | |
| 129 | 130 | cache: -> |
| @@ -57,6 +57,7 @@ class app.views.OfflinePage extends app.View · 56 unmodified lines | ||
| 57 | 57 | doc[action](@onInstallSuccess.bind(@, doc), @onInstallError.bind(@, doc), @onInstallProgress.bind(@, doc)) |
| 58 | 58 | el.parentNode.innerHTML = "#{el.textContent.replace(/e$/, '')}ing…" |
| 59 | 59 | else if action = el.getAttribute('data-action-all') |
| 60 | + return unless action isnt 'uninstall' or window.confirm('Uninstall all docs?') | |
| 60 | 61 | app.db.migrate() |
| 61 | 62 | $.click(el) for el in @findAll("[data-action='#{action}']") |
| 62 | 63 | return |
| @@ -9,10 +9,17 @@ class app.views.RootPage extends app.View · 8 unmodified lines | ||
| 9 | 9 | |
| 10 | 10 | render: -> |
| 11 | 11 | @empty() |
| 12 | − if app.isAndroidWebview() | |
| 13 | − @append @tmpl('androidWarning') | |
| 12 | + | |
| 13 | + tmpl = if app.isAndroidWebview() | |
| 14 | + 'androidWarning' | |
| 15 | + else if @isHidden() | |
| 16 | + 'splash' | |
| 17 | + else if app.isMobile() | |
| 18 | + 'mobileIntro' | |
| 14 | 19 | else |
| 15 | − @append @tmpl if @isHidden() then 'splash' else if app.isMobile() then 'mobileIntro' else 'intro' | |
| 20 | + 'intro' | |
| 21 | + | |
| 22 | + @append @tmpl(tmpl) | |
| 16 | 23 | return |
| 17 | 24 | |
| 18 | 25 | hideIntro: -> |
| @@ -1,7 +1,4 @@ | ||
| 1 | 1 | class app.views.SettingsPage extends app.View |
| 2 | − LAYOUTS = ['_max-width', '_sidebar-hidden', '_native-scrollbars'] | |
| 3 | − SIDEBAR_HIDDEN_LAYOUT = '_sidebar-hidden' | |
| 4 | − | |
| 5 | 2 | @className: '_static' |
| 6 | 3 | |
| 7 | 4 | @events: |
| @@ -17,32 +14,31 @@ class app.views.SettingsPage extends app.View · 9 unmodified lines | ||
| 17 | 14 | settings.dark = app.settings.get('dark') |
| 18 | 15 | settings.smoothScroll = !app.settings.get('fastScroll') |
| 19 | 16 | settings.arrowScroll = app.settings.get('arrowScroll') |
| 20 | − settings[layout] = app.settings.hasLayout(layout) for layout in LAYOUTS | |
| 17 | + settings.autoInstall = app.settings.get('autoInstall') | |
| 18 | + settings.analyticsConsent = app.settings.get('analyticsConsent') | |
| 19 | + settings[layout] = app.settings.hasLayout(layout) for layout in app.settings.LAYOUTS | |
| 21 | 20 | settings |
| 22 | 21 | |
| 23 | 22 | getTitle: -> |
| 24 | 23 | 'Preferences' |
| 25 | 24 | |
| 26 | 25 | toggleDark: (enable) -> |
| 27 | − css = $('link[rel="stylesheet"][data-alt]') | |
| 28 | − alt = css.getAttribute('data-alt') | |
| 29 | − css.setAttribute('data-alt', css.getAttribute('href')) | |
| 30 | − css.setAttribute('href', alt) | |
| 31 | 26 | app.settings.set('dark', !!enable) |
| 32 | − app.appCache?.updateInBackground() | |
| 33 | 27 | return |
| 34 | 28 | |
| 35 | 29 | toggleLayout: (layout, enable) -> |
| 36 | − document.body.classList[if enable then 'add' else 'remove'](layout) unless layout is SIDEBAR_HIDDEN_LAYOUT | |
| 37 | − document.body.classList[if $.overlayScrollbarsEnabled() then 'add' else 'remove']('_overlay-scrollbars') | |
| 38 | 30 | app.settings.setLayout(layout, enable) |
| 39 | − app.appCache?.updateInBackground() | |
| 40 | 31 | return |
| 41 | 32 | |
| 42 | 33 | toggleSmoothScroll: (enable) -> |
| 43 | 34 | app.settings.set('fastScroll', !enable) |
| 44 | 35 | return |
| 45 | 36 | |
| 37 | + toggleAnalyticsConsent: (enable) -> | |
| 38 | + app.settings.set('analyticsConsent', if enable then '1' else '0') | |
| 39 | + resetAnalytics() unless enable | |
| 40 | + return | |
| 41 | + | |
| 46 | 42 | toggle: (name, enable) -> |
| 47 | 43 | app.settings.set(name, enable) |
| 48 | 44 | return |
| @@ -86,6 +82,8 @@ class app.views.SettingsPage extends app.View · 37 unmodified lines | ||
| 86 | 82 | @toggleSmoothScroll input.checked |
| 87 | 83 | when 'import' |
| 88 | 84 | @import input.files[0], input |
| 85 | + when 'analyticsConsent' | |
| 86 | + @toggleAnalyticsConsent input.checked | |
| 89 | 87 | else |
| 90 | 88 | @toggle input.name, input.checked |
| 91 | 89 | return |
| @@ -9,6 +9,7 @@ class app.views.TypePage extends app.View · 8 unmodified lines | ||
| 9 | 9 | |
| 10 | 10 | render: (@type) -> |
| 11 | 11 | @html @tmpl('typePage', @type) |
| 12 | + setFaviconForDoc(@type.doc) | |
| 12 | 13 | return |
| 13 | 14 | |
| 14 | 15 | getTitle: -> |
| @@ -28,7 +28,7 @@ class app.views.Document extends app.View · 27 unmodified lines | ||
| 28 | 28 | return |
| 29 | 29 | |
| 30 | 30 | setTitle: (title) -> |
| 31 | − @el.title = if title then "DevDocs — #{title}" else 'DevDocs API Documentation' | |
| 31 | + @el.title = if title then "#{title} — DevDocs" else 'DevDocs API Documentation' | |
| 32 | 32 | |
| 33 | 33 | afterRoute: (route) => |
| 34 | 34 | if route is 'settings' |
| @@ -75,9 +75,11 @@ class app.views.Document extends app.View · 40 unmodified lines | ||
| 75 | 75 | return unless target.hasAttribute('data-behavior') |
| 76 | 76 | $.stopEvent(event) |
| 77 | 77 | switch target.getAttribute('data-behavior') |
| 78 | − when 'back' then history.back() | |
| 79 | − when 'reload' then window.location.reload() | |
| 80 | − when 'reboot' then window.location = '/' | |
| 81 | − when 'hard-reload' then app.reload() | |
| 82 | − when 'reset' then app.reset() if confirm('Are you sure you want to reset DevDocs?') | |
| 78 | + when 'back' then history.back() | |
| 79 | + when 'reload' then window.location.reload() | |
| 80 | + when 'reboot' then app.reboot() | |
| 81 | + when 'hard-reload' then app.reload() | |
| 82 | + when 'reset' then app.reset() if confirm('Are you sure you want to reset DevDocs?') | |
| 83 | + when 'accept-analytics' then Cookies.set('analyticsConsent', '1') && app.reboot() | |
| 84 | + when 'decline-analytics' then Cookies.set('analyticsConsent', '0') && app.reboot() | |
| 83 | 85 | return |
| @@ -16,8 +16,8 @@ class app.views.Mobile extends app.View · 15 unmodified lines | ||
| 16 | 16 | @detect: -> |
| 17 | 17 | try |
| 18 | 18 | (window.matchMedia('(max-width: 480px)').matches) or |
| 19 | − (window.matchMedia('(max-device-width: 767px)').matches) or | |
| 20 | − (window.matchMedia('(max-device-height: 767px) and (max-device-width: 1024px)').matches) or | |
| 19 | + (window.matchMedia('(max-width: 767px)').matches) or | |
| 20 | + (window.matchMedia('(max-height: 767px) and (max-width: 1024px)').matches) or | |
| 21 | 21 | # Need to sniff the user agent because some Android and Windows Phone devices don't take |
| 22 | 22 | # resolution (dpi) into account when reporting device width/height. |
| 23 | 23 | (navigator.userAgent.indexOf('Android') isnt -1 and navigator.userAgent.indexOf('Mobile') isnt -1) or |
| @@ -11,9 +11,6 @@ class app.views.Resizer extends app.View · 10 unmodified lines | ||
| 11 | 11 | init: -> |
| 12 | 12 | @el.setAttribute('draggable', 'true') |
| 13 | 13 | @appendTo $('._app') |
| 14 | − | |
| 15 | − @style = $('style[data-resizer]') | |
| 16 | − @size = @style.getAttribute('data-size') | |
| 17 | 14 | return |
| 18 | 15 | |
| 19 | 16 | MIN = 260 |
| @@ -24,15 +21,11 @@ class app.views.Resizer extends app.View · 4 unmodified lines | ||
| 24 | 21 | return unless value > 0 |
| 25 | 22 | value = Math.min(Math.max(Math.round(value), MIN), MAX) |
| 26 | 23 | newSize = "#{value}px" |
| 27 | − @style.innerHTML = @style.innerHTML.replace(new RegExp(@size, 'g'), newSize) | |
| 28 | − @size = newSize | |
| 29 | − if save | |
| 30 | − app.settings.setSize(value) | |
| 31 | − app.appCache?.updateInBackground() | |
| 24 | + document.documentElement.style.setProperty('--sidebarWidth', newSize) | |
| 25 | + app.settings.setSize(value) if save | |
| 32 | 26 | return |
| 33 | 27 | |
| 34 | 28 | onDragStart: (event) => |
| 35 | − @style.removeAttribute('disabled') | |
| 36 | 29 | event.dataTransfer.effectAllowed = 'link' |
| 37 | 30 | event.dataTransfer.setData('Text', '') |
| 38 | 31 | $.on(window, 'dragover', @onDrag) |
| @@ -50,7 +43,7 @@ class app.views.Resizer extends app.View · 11 unmodified lines | ||
| 50 | 43 | onDragEnd: (event) => |
| 51 | 44 | $.off(window, 'dragover', @onDrag) |
| 52 | 45 | value = event.pageX or (event.screenX - window.screenX) |
| 53 | − if @lastDragValue and not (@lastDragValue - 5 < value < @lastDragValue + 5) # https://github.com/Thibaut/devdocs/issues/265 | |
| 46 | + if @lastDragValue and not (@lastDragValue - 5 < value < @lastDragValue + 5) # https://github.com/freeCodeCamp/devdocs/issues/265 | |
| 54 | 47 | value = @lastDragValue |
| 55 | 48 | @resize(value, true) |
| 56 | 49 | return |
| @@ -25,7 +25,6 @@ class app.views.Settings extends app.View · 24 unmodified lines | ||
| 25 | 25 | if super |
| 26 | 26 | @render() |
| 27 | 27 | document.body.classList.remove(SIDEBAR_HIDDEN_LAYOUT) |
| 28 | − app.appCache?.on 'progress', @onAppCacheProgress | |
| 29 | 28 | return |
| 30 | 29 | |
| 31 | 30 | deactivate: -> |
| @@ -33,7 +32,6 @@ class app.views.Settings extends app.View · 1 unmodified line | ||
| 33 | 32 | @resetClass() |
| 34 | 33 | @docPicker.detach() |
| 35 | 34 | document.body.classList.add(SIDEBAR_HIDDEN_LAYOUT) if app.settings.hasLayout(SIDEBAR_HIDDEN_LAYOUT) |
| 36 | − app.appCache?.off 'progress', @onAppCacheProgress | |
| 37 | 35 | return |
| 38 | 36 | |
| 39 | 37 | render: -> |
| @@ -52,7 +50,7 @@ class app.views.Settings extends app.View · 12 unmodified lines | ||
| 52 | 50 | docs = @docPicker.getSelectedDocs() |
| 53 | 51 | app.settings.setDocs(docs) |
| 54 | 52 | |
| 55 | − @saveBtn.textContent = if app.appCache then 'Downloading\u2026' else 'Saving\u2026' | |
| 53 | + @saveBtn.textContent = 'Saving\u2026' | |
| 56 | 54 | disabledDocs = new app.collections.Docs(doc for doc in app.docs.all() when docs.indexOf(doc.slug) is -1) |
| 57 | 55 | disabledDocs.uninstall -> |
| 58 | 56 | app.db.migrate() |
| @@ -83,9 +81,3 @@ class app.views.Settings extends app.View · 24 unmodified lines | ||
| 83 | 81 | $.stopEvent(event) |
| 84 | 82 | app.router.show '/' |
| 85 | 83 | return |
| 86 | − | |
| 87 | − onAppCacheProgress: (event) => | |
| 88 | − if event.lengthComputable | |
| 89 | − percentage = Math.round event.loaded * 100 / event.total | |
| 90 | − @saveBtn.textContent = "Downloading\u2026 (#{percentage}%)" | |
| 91 | − return | |
| @@ -17,6 +17,7 @@ class app.views.Search extends app.View · 16 unmodified lines | ||
| 17 | 17 | typing: 'focus' |
| 18 | 18 | altG: 'google' |
| 19 | 19 | altS: 'stackoverflow' |
| 20 | + altD: 'duckduckgo' | |
| 20 | 21 | |
| 21 | 22 | @routes: |
| 22 | 23 | after: 'afterRoute' |
| @@ -29,6 +30,9 @@ class app.views.Search extends app.View · 6 unmodified lines | ||
| 29 | 30 | .on 'results', @onResults |
| 30 | 31 | .on 'end', @onEnd |
| 31 | 32 | |
| 33 | + @scope | |
| 34 | + .on 'change', @onScopeChange | |
| 35 | + | |
| 32 | 36 | app.on 'ready', @onReady |
| 33 | 37 | $.on window, 'hashchange', @searchUrl |
| 34 | 38 | $.on window, 'focus', @onWindowFocus |
| @@ -113,6 +117,10 @@ class app.views.Search extends app.View · 78 unmodified lines | ||
| 113 | 117 | @externalSearch "https://stackoverflow.com/search?q=" |
| 114 | 118 | return |
| 115 | 119 | |
| 120 | + duckduckgo: => | |
| 121 | + @externalSearch "https://duckduckgo.com/?t=devdocs&q=" | |
| 122 | + return | |
| 123 | + | |
| 116 | 124 | onResults: (results) => |
| 117 | 125 | @hasResults = true if results.length |
| 118 | 126 | @trigger 'results', results, @flags |
| @@ -127,13 +135,17 @@ class app.views.Search extends app.View · 8 unmodified lines | ||
| 127 | 135 | if event.target is @resetLink |
| 128 | 136 | $.stopEvent(event) |
| 129 | 137 | @reset() |
| 130 | − app.document.onEscape() | |
| 131 | 138 | return |
| 132 | 139 | |
| 133 | 140 | onSubmit: (event) -> |
| 134 | 141 | $.stopEvent(event) |
| 135 | 142 | return |
| 136 | 143 | |
| 144 | + onScopeChange: => | |
| 145 | + @value = '' | |
| 146 | + @onInput() | |
| 147 | + return | |
| 148 | + | |
| 137 | 149 | afterRoute: (name, context) => |
| 138 | 150 | return if app.shortcuts.eventInProgress?.name is 'escape' |
| 139 | 151 | @reset(true) if not context.init and app.router.isIndex() |
| @@ -6,7 +6,9 @@ class app.views.SearchScope extends app.View · 5 unmodified lines | ||
| 6 | 6 | tag: '._search-tag' |
| 7 | 7 | |
| 8 | 8 | @events: |
| 9 | + click: 'onClick' | |
| 9 | 10 | keydown: 'onKeydown' |
| 11 | + textInput: 'onTextInput' | |
| 10 | 12 | |
| 11 | 13 | @routes: |
| 12 | 14 | after: 'afterRoute' |
| @@ -87,17 +89,33 @@ class app.views.SearchScope extends app.View · 74 unmodified lines | ||
| 87 | 89 | @trigger 'change', null, previousDoc |
| 88 | 90 | return |
| 89 | 91 | |
| 92 | + doScopeSearch: (event) => | |
| 93 | + @search @input.value[0...@input.selectionStart] | |
| 94 | + $.stopEvent(event) if @doc | |
| 95 | + return | |
| 96 | + | |
| 97 | + onClick: (event) => | |
| 98 | + if event.target is @tag | |
| 99 | + @reset() | |
| 100 | + $.stopEvent(event) | |
| 101 | + return | |
| 102 | + | |
| 90 | 103 | onKeydown: (event) => |
| 91 | 104 | if event.which is 8 # backspace |
| 92 | − if @doc and not @input.value | |
| 93 | − $.stopEvent(event) | |
| 105 | + if @doc and @input.selectionEnd is 0 | |
| 94 | 106 | @reset() |
| 95 | − else if not @doc and @input.value | |
| 107 | + $.stopEvent(event) | |
| 108 | + else if not @doc and @input.value and not $.isChromeForAndroid() | |
| 96 | 109 | return if event.ctrlKey or event.metaKey or event.altKey or event.shiftKey |
| 97 | 110 | if event.which is 9 or # tab |
| 98 | 111 | (event.which is 32 and app.isMobile()) # space |
| 99 | − @search @input.value[0...@input.selectionStart] | |
| 100 | − $.stopEvent(event) if @doc | |
| 112 | + @doScopeSearch(event) | |
| 113 | + return | |
| 114 | + | |
| 115 | + onTextInput: (event) => | |
| 116 | + return unless $.isChromeForAndroid() | |
| 117 | + if not @doc and @input.value and event.data == ' ' | |
| 118 | + @doScopeSearch(event) | |
| 101 | 119 | return |
| 102 | 120 | |
| 103 | 121 | extractHashValue: -> |
| @@ -28,7 +28,7 @@ class app.views.Sidebar extends app.View · 27 unmodified lines | ||
| 28 | 28 | |
| 29 | 29 | app.on 'ready', @onReady |
| 30 | 30 | |
| 31 | − $.on document.documentElement, 'mouseleave', (event) => @display() if event.clientX < 10 | |
| 31 | + $.on document.documentElement, 'mouseleave', (event) => @display() unless event.clientX <= 0 | |
| 32 | 32 | $.on document.documentElement, 'mouseenter', => @resetDisplay(forceNoHover: false) |
| 33 | 33 | return |
| 34 | 34 | |
| @@ -1,101 +0,0 @@ | ||
| 1 | −//= depend_on docs-1.png | |
| 2 | −//= depend_on docs-1@2x.png | |
| 3 | −//= depend_on docs-2.png | |
| 4 | −//= depend_on docs-2@2x.png | |
| 5 | − | |
| 6 | −/*! | |
| 7 | − * Copyright 2013-2017 Thibaut Courouble and other contributors | |
| 8 | − * | |
| 9 | − * This source code is licensed under the terms of the Mozilla | |
| 10 | − * Public License, v. 2.0, a copy of which may be obtained at: | |
| 11 | − * http://mozilla.org/MPL/2.0/ | |
| 12 | − */ | |
| 13 | − | |
| 14 | −@import 'global/variables-dark', | |
| 15 | − 'global/icons', | |
| 16 | − 'global/classes', | |
| 17 | − 'global/base'; | |
| 18 | − | |
| 19 | −@import 'components/app', | |
| 20 | − 'components/header', | |
| 21 | − 'components/notif', | |
| 22 | − 'components/sidebar', | |
| 23 | − 'components/settings', | |
| 24 | − 'components/content', | |
| 25 | − 'components/page', | |
| 26 | − 'components/fail', | |
| 27 | − 'components/path', | |
| 28 | − 'components/notice', | |
| 29 | − 'components/prism', | |
| 30 | − 'components/mobile'; | |
| 31 | − | |
| 32 | −@import 'pages/simple', | |
| 33 | − 'pages/angular', | |
| 34 | − 'pages/angularjs', | |
| 35 | − 'pages/apache', | |
| 36 | − 'pages/async', | |
| 37 | − 'pages/bootstrap', | |
| 38 | − 'pages/c', | |
| 39 | − 'pages/cakephp', | |
| 40 | − 'pages/clojure', | |
| 41 | − 'pages/codeception', | |
| 42 | − 'pages/coffeescript', | |
| 43 | − 'pages/cordova', | |
| 44 | − 'pages/crystal', | |
| 45 | − 'pages/d', | |
| 46 | − 'pages/d3', | |
| 47 | − 'pages/dojo', | |
| 48 | − 'pages/drupal', | |
| 49 | − 'pages/elixir', | |
| 50 | − 'pages/ember', | |
| 51 | − 'pages/erlang', | |
| 52 | − 'pages/express', | |
| 53 | − 'pages/git', | |
| 54 | − 'pages/github', | |
| 55 | − 'pages/go', | |
| 56 | − 'pages/haskell', | |
| 57 | − 'pages/jquery', | |
| 58 | − 'pages/julia', | |
| 59 | − 'pages/knockout', | |
| 60 | − 'pages/kotlin', | |
| 61 | − 'pages/laravel', | |
| 62 | − 'pages/liquid', | |
| 63 | − 'pages/love', | |
| 64 | − 'pages/lua', | |
| 65 | − 'pages/mdn', | |
| 66 | − 'pages/meteor', | |
| 67 | − 'pages/modernizr', | |
| 68 | − 'pages/moment', | |
| 69 | − 'pages/nginx', | |
| 70 | − 'pages/node', | |
| 71 | − 'pages/npm', | |
| 72 | − 'pages/openjdk', | |
| 73 | − 'pages/perl', | |
| 74 | − 'pages/phalcon', | |
| 75 | − 'pages/phaser', | |
| 76 | − 'pages/php', | |
| 77 | − 'pages/phpunit', | |
| 78 | − 'pages/postgres', | |
| 79 | − 'pages/pug', | |
| 80 | − 'pages/ramda', | |
| 81 | − 'pages/rdoc', | |
| 82 | − 'pages/react_native', | |
| 83 | − 'pages/redis', | |
| 84 | − 'pages/rethinkdb', | |
| 85 | − 'pages/rfc', | |
| 86 | − 'pages/rubydoc', | |
| 87 | − 'pages/rust', | |
| 88 | − 'pages/scala', | |
| 89 | − 'pages/sinon', | |
| 90 | − 'pages/socketio', | |
| 91 | − 'pages/sphinx', | |
| 92 | − 'pages/sphinx_simple', | |
| 93 | − 'pages/sqlite', | |
| 94 | − 'pages/support_tables', | |
| 95 | − 'pages/tcl_tk', | |
| 96 | − 'pages/tensorflow', | |
| 97 | − 'pages/underscore', | |
| 98 | − 'pages/vue', | |
| 99 | − 'pages/webpack', | |
| 100 | − 'pages/yard', | |
| 101 | − 'pages/yii'; | |
| @@ -1,17 +1,19 @@ | ||
| 1 | −//= depend_on docs-1.png | |
| 2 | −//= depend_on docs-1@2x.png | |
| 3 | −//= depend_on docs-2.png | |
| 4 | −//= depend_on docs-2@2x.png | |
| 1 | +//= depend_on sprites/docs.png | |
| 2 | +//= depend_on sprites/docs@2x.png | |
| 3 | +//= depend_on sprites/docs.json | |
| 5 | 4 | |
| 6 | 5 | /*! |
| 7 | − * Copyright 2013-2017 Thibaut Courouble and other contributors | |
| 6 | + * Copyright 2013-2019 Thibaut Courouble and other contributors | |
| 8 | 7 | * |
| 9 | 8 | * This source code is licensed under the terms of the Mozilla |
| 10 | 9 | * Public License, v. 2.0, a copy of which may be obtained at: |
| 11 | 10 | * http://mozilla.org/MPL/2.0/ |
| 12 | 11 | */ |
| 13 | 12 | |
| 14 | −@import 'global/variables', | |
| 13 | +@import 'global/variables-light', | |
| 14 | + 'global/variables-dark', | |
| 15 | + 'global/variables', | |
| 16 | + 'global/mixins', | |
| 15 | 17 | 'global/icons', |
| 16 | 18 | 'global/classes', |
| 17 | 19 | 'global/base'; |
| @@ -34,6 +36,7 @@ · 16 unmodified lines | ||
| 34 | 36 | 'pages/angularjs', |
| 35 | 37 | 'pages/apache', |
| 36 | 38 | 'pages/async', |
| 39 | + 'pages/bash', | |
| 37 | 40 | 'pages/bootstrap', |
| 38 | 41 | 'pages/c', |
| 39 | 42 | 'pages/cakephp', |
| @@ -44,6 +47,7 @@ · 4 unmodified lines | ||
| 44 | 47 | 'pages/crystal', |
| 45 | 48 | 'pages/d', |
| 46 | 49 | 'pages/d3', |
| 50 | + 'pages/dart', | |
| 47 | 51 | 'pages/dojo', |
| 48 | 52 | 'pages/drupal', |
| 49 | 53 | 'pages/elixir', |
| @@ -53,7 +57,9 @@ · 3 unmodified lines | ||
| 53 | 57 | 'pages/git', |
| 54 | 58 | 'pages/github', |
| 55 | 59 | 'pages/go', |
| 60 | + 'pages/graphite', | |
| 56 | 61 | 'pages/haskell', |
| 62 | + 'pages/jekyll', | |
| 57 | 63 | 'pages/jquery', |
| 58 | 64 | 'pages/julia', |
| 59 | 65 | 'pages/knockout', |
| @@ -77,6 +83,8 @@ · 17 unmodified lines | ||
| 77 | 83 | 'pages/phpunit', |
| 78 | 84 | 'pages/postgres', |
| 79 | 85 | 'pages/pug', |
| 86 | + 'pages/python', | |
| 87 | + 'pages/qt', | |
| 80 | 88 | 'pages/ramda', |
| 81 | 89 | 'pages/rdoc', |
| 82 | 90 | 'pages/react_native', |
| @@ -94,6 +102,7 @@ · 11 unmodified lines | ||
| 94 | 102 | 'pages/support_tables', |
| 95 | 103 | 'pages/tcl_tk', |
| 96 | 104 | 'pages/tensorflow', |
| 105 | + 'pages/terraform', | |
| 97 | 106 | 'pages/underscore', |
| 98 | 107 | 'pages/vue', |
| 99 | 108 | 'pages/webpack', |
| @@ -1,6 +1,6 @@ | ||
| 1 | −html._booting { background: $contentBackground; } | |
| 1 | +html._booting { background: var(--contentBackground); } | |
| 2 | 2 | body._max-width { background: none; } |
| 3 | −html._booting body._max-width { background: $documentBackground; } | |
| 3 | +html._booting body._max-width { background: var(--documentBackground); } | |
| 4 | 4 | |
| 5 | 5 | ._app { |
| 6 | 6 | position: relative; |
| @@ -15,8 +15,8 @@ html._booting body._max-width { background: $documentBackground; } · 8 unmodified lines | ||
| 15 | 15 | |
| 16 | 16 | ._max-width & { |
| 17 | 17 | margin: 0 auto; |
| 18 | − max-width: $maxWidth; | |
| 19 | − background: $contentBackground; | |
| 20 | − box-shadow: 1px 0 $headerBorder, -1px 0 $headerBorder; | |
| 18 | + max-width: var(--maxWidth); | |
| 19 | + background: var(--contentBackground); | |
| 20 | + box-shadow: 1px 0 var(--headerBorder), -1px 0 var(--headerBorder); | |
| 21 | 21 | } |
| 22 | 22 | } |
| @@ -4,13 +4,13 @@ · 3 unmodified lines | ||
| 4 | 4 | |
| 5 | 5 | ._container { |
| 6 | 6 | position: relative; |
| 7 | − z-index: $contentZ; | |
| 7 | + z-index: var(--contentZ); | |
| 8 | 8 | height: 100%; |
| 9 | − margin-left: $sidebarWidth; | |
| 9 | + margin-left: var(--sidebarWidth); | |
| 10 | 10 | pointer-events: none; |
| 11 | 11 | @extend %border-box; |
| 12 | 12 | |
| 13 | − @media #{$mediumScreen} { margin-left: $sidebarMediumWidth; } | |
| 13 | + @include mobile { margin-left: var(--sidebarMediumWidth); } | |
| 14 | 14 | |
| 15 | 15 | ._sidebar-hidden & { margin-left: 0; } |
| 16 | 16 | body:not(._native-scrollbars) & { -webkit-margin-end: -1px; } |
| @@ -30,7 +30,7 @@ · 13 unmodified lines | ||
| 30 | 30 | ._sidebar-hidden &:before { |
| 31 | 31 | content: ''; |
| 32 | 32 | display: block; |
| 33 | − margin-top: $headerHeight; | |
| 33 | + margin-top: var(--headerHeight); | |
| 34 | 34 | } |
| 35 | 35 | |
| 36 | 36 | ._overlay-scrollbars & { padding-left: .625rem; } |
| @@ -54,7 +54,7 @@ · 17 unmodified lines | ||
| 54 | 54 | font-size: 4rem; |
| 55 | 55 | font-weight: 300; |
| 56 | 56 | letter-spacing: -.125rem; |
| 57 | − color: $loadingText; | |
| 57 | + color: var(--loadingText); | |
| 58 | 58 | text-align: center; |
| 59 | 59 | cursor: default; |
| 60 | 60 | } |
| @@ -68,7 +68,7 @@ · 7 unmodified lines | ||
| 68 | 68 | // |
| 69 | 69 | |
| 70 | 70 | ._splash-title { |
| 71 | − color: $splashText; | |
| 71 | + color: var(--splashText); | |
| 72 | 72 | @extend %loading, %user-select-none; |
| 73 | 73 | } |
| 74 | 74 | |
| @@ -88,7 +88,7 @@ · 13 unmodified lines | ||
| 88 | 88 | min-height: calc(100vh - 2.375rem); |
| 89 | 89 | |
| 90 | 90 | ._sidebar-hidden & { |
| 91 | − min-height: calc(100vh - 2.375rem - #{$headerHeight}); | |
| 91 | + min-height: calc(100vh - 2.375rem - var(--headerHeight)); | |
| 92 | 92 | } |
| 93 | 93 | } |
| 94 | 94 | |
| @@ -140,12 +140,12 @@ · 45 unmodified lines | ||
| 140 | 140 | |
| 141 | 141 | ._error-text { |
| 142 | 142 | margin: 0 0 1rem; |
| 143 | − color: $textColorLight; | |
| 143 | + color: var(--textColorLight); | |
| 144 | 144 | } |
| 145 | 145 | |
| 146 | 146 | ._error-links { |
| 147 | 147 | font-size: 1rem; |
| 148 | − font-weight: $boldFontWeight; | |
| 148 | + font-weight: var(--boldFontWeight); | |
| 149 | 149 | } |
| 150 | 150 | |
| 151 | 151 | ._error-link { padding: 0 .5rem; } |
| @@ -167,7 +167,7 @@ · 15 unmodified lines | ||
| 167 | 167 | height: 1px; |
| 168 | 168 | margin-top: .25rem; |
| 169 | 169 | margin-left: 1rem; |
| 170 | − background: $boxBorderLight; | |
| 170 | + background: var(--boxBorderLight); | |
| 171 | 171 | } |
| 172 | 172 | } |
| 173 | 173 | |
| @@ -197,7 +197,7 @@ · 23 unmodified lines | ||
| 197 | 197 | ._toc-title { |
| 198 | 198 | margin: 0 0 .5rem; |
| 199 | 199 | font-size: inherit; |
| 200 | − font-weight: $boldFontWeight; | |
| 200 | + font-weight: var(--boldFontWeight); | |
| 201 | 201 | } |
| 202 | 202 | |
| 203 | 203 | ._toc-list { |
| @@ -252,7 +252,7 @@ · 48 unmodified lines | ||
| 252 | 252 | ._docs-size { |
| 253 | 253 | text-align: right; |
| 254 | 254 | |
| 255 | − > small { color: $textColorLight; } | |
| 255 | + > small { color: var(--textColorLight); } | |
| 256 | 256 | } |
| 257 | 257 | |
| 258 | 258 | ._docs-tools { |
| @@ -285,7 +285,7 @@ · 26 unmodified lines | ||
| 285 | 285 | padding: 0 .75rem; |
| 286 | 286 | } |
| 287 | 287 | ._btn-link:not(._show) { display: none; } |
| 288 | − ._btn-link._show ~ ._btn-link._show { border-left: 1px solid $boxBorder; } | |
| 288 | + ._btn-link._show ~ ._btn-link._show { border-left: 1px solid var(--boxBorder); } | |
| 289 | 289 | } |
| 290 | 290 | |
| 291 | 291 | // |
| @@ -297,7 +297,7 @@ · 5 unmodified lines | ||
| 297 | 297 | position: relative; |
| 298 | 298 | padding-left: 10em; |
| 299 | 299 | font-size: .8125rem; |
| 300 | − color: $textColorLight; | |
| 300 | + color: var(--textColorLight); | |
| 301 | 301 | |
| 302 | 302 | + ._news-row { margin-top: 1em; } |
| 303 | 303 | } |
| @@ -305,7 +305,7 @@ · 1 unmodified line | ||
| 305 | 305 | ._news-title { |
| 306 | 306 | display: block; |
| 307 | 307 | font-size: .875rem; |
| 308 | − color: $textColor; | |
| 308 | + color: var(--textColor); | |
| 309 | 309 | } |
| 310 | 310 | |
| 311 | 311 | ._news-date { |
| @@ -372,16 +372,18 @@ · 60 unmodified lines | ||
| 372 | 372 | // Utilities |
| 373 | 373 | // |
| 374 | 374 | |
| 375 | −._bold { font-weight: $boldFontWeight; } | |
| 375 | +._bold { font-weight: var(--boldFontWeight); } | |
| 376 | 376 | ._note { @extend %note; } |
| 377 | 377 | ._note-green { @extend %note-green; } |
| 378 | 378 | ._label { @extend %label; } |
| 379 | 379 | ._code { @extend %code; } |
| 380 | −._highlight { background: $highlightBackground !important; } | |
| 380 | +._highlight { background: var(--highlightBackground) !important; } | |
| 381 | 381 | |
| 382 | 382 | ._table { width: 100%; } |
| 383 | 383 | ._mobile ._table { overflow-x: auto; } |
| 384 | 384 | |
| 385 | +._pre-heading { @extend %pre-heading; } | |
| 386 | + | |
| 385 | 387 | ._pre-clip { |
| 386 | 388 | display: none; |
| 387 | 389 | position: absolute; |
| @@ -394,12 +396,9 @@ · 6 unmodified lines | ||
| 394 | 396 | pre:hover > & { display: block; } |
| 395 | 397 | &:hover { opacity: 1; } |
| 396 | 398 | |
| 397 | − > svg { @extend %svg-icon; } | |
| 398 | − | |
| 399 | − @if $style == 'dark' { | |
| 400 | − > svg { fill: white; } | |
| 401 | − } @else { | |
| 402 | − > svg { fill: black; } | |
| 399 | + > svg { | |
| 400 | + @extend %svg-icon; | |
| 401 | + fill: var(--absolute); | |
| 403 | 402 | } |
| 404 | 403 | |
| 405 | 404 | &._pre-clip-success > svg, |
| @@ -417,15 +416,13 @@ · 11 unmodified lines | ||
| 417 | 416 | line-height: normal; |
| 418 | 417 | white-space: nowrap; |
| 419 | 418 | padding: .375rem .675rem; |
| 420 | − background-image: linear-gradient(lighten($boxBackground, 4%), darken($boxBackground, 2%)); | |
| 421 | − border: 1px solid $boxBorder; | |
| 419 | + background-color: var(--boxBackground); | |
| 420 | + border: 1px solid var(--boxBorder); | |
| 422 | 421 | border-radius: 3px; |
| 423 | − box-shadow: 0 1px rgba($boxBorder, .08); | |
| 424 | 422 | cursor: pointer; |
| 425 | 423 | |
| 426 | 424 | &:active { |
| 427 | − background-color: $boxBackground; | |
| 428 | − box-shadow: inset 0 1px 1px rgba(black, .05), inset 0 1px 4px $boxBorder; | |
| 425 | + box-shadow: inset 0 1px 1px rgba(black, .05), inset 0 1px 4px var(--boxBorder); | |
| 429 | 426 | } |
| 430 | 427 | } |
| 431 | 428 | |
| @@ -445,22 +442,23 @@ · 13 unmodified lines | ||
| 445 | 442 | |
| 446 | 443 | ._btn-link { |
| 447 | 444 | line-height: inherit; |
| 448 | − color: $linkColor; | |
| 449 | − text-decoration: $linkTextDecoration; | |
| 445 | + color: var(--linkColor); | |
| 446 | + text-decoration: var(--linkTextDecoration); | |
| 450 | 447 | |
| 451 | 448 | &:hover { |
| 452 | − color: $linkColorHover; | |
| 449 | + color: var(--linkColorHover); | |
| 453 | 450 | text-decoration: underline; |
| 454 | 451 | } |
| 455 | 452 | } |
| 456 | 453 | |
| 457 | 454 | ._reset-btn, |
| 458 | 455 | ._reset-btn:hover { |
| 459 | − color: $textColorRed; | |
| 456 | + color: var(--textColorRed); | |
| 460 | 457 | } |
| 461 | 458 | |
| 462 | 459 | ._github-btn { |
| 463 | 460 | display: inline-block; |
| 464 | 461 | vertical-align: text-top; |
| 465 | 462 | margin-left: .25rem; |
| 463 | + background: inherit; | |
| 466 | 464 | } |
| @@ -1,4 +1,4 @@ | ||
| 1 | −._fail { | |
| 1 | +._fail { // Don't use CSS variables, in case the browser doesn't support them. | |
| 2 | 2 | display: block; |
| 3 | 3 | position: relative; |
| 4 | 4 | top: 1.5rem; |
| @@ -23,7 +23,7 @@ · 18 unmodified lines | ||
| 23 | 23 | ._fail-title { |
| 24 | 24 | margin: 0 0 1rem; |
| 25 | 25 | font-size: 1rem; |
| 26 | − font-weight: $boldFontWeight; | |
| 26 | + font-weight: bold; | |
| 27 | 27 | } |
| 28 | 28 | |
| 29 | 29 | ._fail-text, ._fail-list { |
| @@ -32,5 +32,3 @@ · 2 unmodified lines | ||
| 32 | 32 | } |
| 33 | 33 | |
| 34 | 34 | ._fail-text:last-child { margin: 0; } |
| 35 | − | |
| 36 | −._fail-link { float: right; } | |
| @@ -4,20 +4,20 @@ · 3 unmodified lines | ||
| 4 | 4 | |
| 5 | 5 | ._header { |
| 6 | 6 | position: absolute; |
| 7 | − z-index: $headerZ; | |
| 7 | + z-index: var(--headerZ); | |
| 8 | 8 | top: 0; |
| 9 | 9 | left: 0; |
| 10 | 10 | display: -ms-flexbox; |
| 11 | 11 | display: flex; |
| 12 | − width: $sidebarWidth; | |
| 13 | − height: $headerHeight; | |
| 14 | − background: $headerBackground; | |
| 15 | − border-bottom: 1px solid $headerBorder; | |
| 16 | − border-right: 1px solid $headerBorder; | |
| 12 | + width: var(--sidebarWidth); | |
| 13 | + height: var(--headerHeight); | |
| 14 | + background: var(--headerBackground); | |
| 15 | + border-bottom: 1px solid var(--headerBorder); | |
| 16 | + border-right: 1px solid var(--headerBorder); | |
| 17 | 17 | @extend %border-box; |
| 18 | 18 | @extend %user-select-none; |
| 19 | 19 | |
| 20 | − @media #{$mediumScreen} { width: $sidebarMediumWidth; } | |
| 20 | + @include mobile { width: var(--sidebarMediumWidth); } | |
| 21 | 21 | } |
| 22 | 22 | |
| 23 | 23 | ._header-left { |
| @@ -35,7 +35,7 @@ · 11 unmodified lines | ||
| 35 | 35 | flex: 0 0 auto; |
| 36 | 36 | width: 2.25rem; |
| 37 | 37 | height: 100%; |
| 38 | − color: $textColorLight; | |
| 38 | + color: var(--textColorLight); | |
| 39 | 39 | text-align: center; |
| 40 | 40 | |
| 41 | 41 | &[hidden] { display: none; } |
| @@ -67,8 +67,8 @@ · 25 unmodified lines | ||
| 67 | 67 | word-wrap: normal; |
| 68 | 68 | overflow-wrap: normal; |
| 69 | 69 | font-size: .875rem; |
| 70 | − background: $contentBackground; | |
| 71 | − border: 1px solid $headerBorder; | |
| 70 | + background: var(--contentBackground); | |
| 71 | + border: 1px solid var(--headerBorder); | |
| 72 | 72 | border-radius: 3px; |
| 73 | 73 | box-shadow: -1px 1px 1px rgba(black, .05); |
| 74 | 74 | transition: all 0ms cubic-bezier(0.23, 1, 0.32, 1) 1ms; |
| @@ -98,10 +98,10 @@ · 23 unmodified lines | ||
| 98 | 98 | margin: 0; |
| 99 | 99 | line-height: 1.5rem; |
| 100 | 100 | font-size: 1rem; |
| 101 | − font-weight: $boldFontWeight; | |
| 101 | + font-weight: var(--boldFontWeight); | |
| 102 | 102 | letter-spacing: -.5px; |
| 103 | − background: $sidebarBackground; | |
| 104 | − border-bottom: 1px solid $sidebarBorder; | |
| 103 | + background: var(--sidebarBackground); | |
| 104 | + border-bottom: 1px solid var(--sidebarBorder); | |
| 105 | 105 | border-radius: 2px 2px 0 0; |
| 106 | 106 | } |
| 107 | 107 | |
| @@ -109,7 +109,7 @@ · 1 unmodified line | ||
| 109 | 109 | ._menu-title-link:hover { |
| 110 | 110 | display: block; |
| 111 | 111 | padding: .5rem 1rem; |
| 112 | − color: $focusText; | |
| 112 | + color: var(--focusText); | |
| 113 | 113 | text-decoration: none; |
| 114 | 114 | } |
| 115 | 115 | |
| @@ -121,9 +121,9 @@ · 5 unmodified lines | ||
| 121 | 121 | text-decoration: none; |
| 122 | 122 | |
| 123 | 123 | &:hover { |
| 124 | − color: $focusText; | |
| 124 | + color: var(--focusText); | |
| 125 | 125 | text-decoration: none; |
| 126 | − background: $sidebarBackground; | |
| 126 | + background: var(--sidebarBackground); | |
| 127 | 127 | } |
| 128 | 128 | |
| 129 | 129 | &:last-child { border-radius: 0 0 2px 2px; } |
| @@ -151,11 +151,7 @@ · 21 unmodified lines | ||
| 151 | 151 | opacity: .42; |
| 152 | 152 | @extend %svg-icon; |
| 153 | 153 | |
| 154 | − @if $style == 'dark' { | |
| 155 | − fill: white; | |
| 156 | − } @else { | |
| 157 | − fill: black; | |
| 158 | − } | |
| 154 | + fill: var(--absolute); | |
| 159 | 155 | } |
| 160 | 156 | } |
| 161 | 157 | |
| @@ -166,21 +162,19 @@ · 4 unmodified lines | ||
| 166 | 162 | height: 100%; |
| 167 | 163 | padding: 0 .75rem 1px 1.75rem; |
| 168 | 164 | font-size: .875rem; |
| 169 | − background: $contentBackground; | |
| 165 | + background: var(--contentBackground); | |
| 170 | 166 | border: 1px solid; |
| 171 | − border-color: darken($headerBorder, 2%); | |
| 167 | + border-color: var(--searchBorder); | |
| 172 | 168 | border-radius: 3px; |
| 173 | 169 | |
| 174 | 170 | &:focus { |
| 175 | 171 | outline: 0; |
| 176 | − @if $inputFocusBorder { | |
| 177 | − border-color: $inputFocusBorder; | |
| 178 | − box-shadow: 0 0 1px $inputFocusBorder; | |
| 179 | − } | |
| 172 | + border-color: var(--inputFocusBorder); | |
| 173 | + box-shadow: 0 0 1px var(--inputFocusBorder); | |
| 180 | 174 | } |
| 181 | 175 | |
| 182 | 176 | &[disabled] { |
| 183 | − background: $sidebarBackground; | |
| 177 | + background: var(--sidebarBackground); | |
| 184 | 178 | cursor: not-allowed; |
| 185 | 179 | } |
| 186 | 180 | } |
| @@ -202,13 +196,9 @@ · 15 unmodified lines | ||
| 202 | 196 | top: .5rem; |
| 203 | 197 | left: .375rem; |
| 204 | 198 | @extend %svg-icon; |
| 199 | + fill: var(--absolute); | |
| 205 | 200 | } |
| 206 | 201 | |
| 207 | − @if $style == 'dark' { | |
| 208 | − > svg { fill: white; } | |
| 209 | − } @else { | |
| 210 | − > svg { fill: black; } | |
| 211 | − } | |
| 212 | 202 | ._search-active > & { display: block; } |
| 213 | 203 | } |
| 214 | 204 | |
| @@ -222,8 +212,9 @@ · 7 unmodified lines | ||
| 222 | 212 | line-height: 1.25rem; |
| 223 | 213 | max-width: 50%; |
| 224 | 214 | font-size: .8125rem; |
| 225 | − color: $textColorLight; | |
| 226 | − background: darken($headerBackground, 5%); | |
| 215 | + color: var(--textColorLight); | |
| 216 | + background: var(--searchTagBackground); | |
| 227 | 217 | border-radius: 2px; |
| 218 | + cursor: pointer; | |
| 228 | 219 | @extend %truncate-text; |
| 229 | 220 | } |
| @@ -4,7 +4,7 @@ · 3 unmodified lines | ||
| 4 | 4 | |
| 5 | 5 | ._mobile { |
| 6 | 6 | font-size: 100%; |
| 7 | − background: $contentBackground; | |
| 7 | + background: var(--contentBackground); | |
| 8 | 8 | |
| 9 | 9 | ._hide-on-mobile { display: none; } |
| 10 | 10 | |
| @@ -18,7 +18,7 @@ · 7 unmodified lines | ||
| 18 | 18 | |
| 19 | 19 | ._container { |
| 20 | 20 | margin: 0; |
| 21 | − padding-top: $headerHeight; | |
| 21 | + padding-top: var(--headerHeight); | |
| 22 | 22 | } |
| 23 | 23 | |
| 24 | 24 | ._content { |
| @@ -1,15 +1,15 @@ | ||
| 1 | 1 | ._notice { |
| 2 | 2 | position: absolute; |
| 3 | − z-index: $noticeZ; | |
| 3 | + z-index: var(--noticeZ); | |
| 4 | 4 | bottom: 0; |
| 5 | − left: $sidebarWidth; | |
| 5 | + left: var(--sidebarWidth); | |
| 6 | 6 | right: 0; |
| 7 | 7 | height: 2.5rem; |
| 8 | 8 | padding: 0 1.25rem; |
| 9 | − background: $noticeBackground; | |
| 10 | − box-shadow: inset 0 1px $noticeBorder; | |
| 9 | + background: var(--noticeBackground); | |
| 10 | + box-shadow: inset 0 1px var(--noticeBorder); | |
| 11 | 11 | |
| 12 | − @media #{$mediumScreen} { left: $sidebarMediumWidth; } | |
| 12 | + @include mobile { left: var(--sidebarMediumWidth); } | |
| 13 | 13 | |
| 14 | 14 | ._sidebar-hidden & { left: 0; } |
| 15 | 15 | |
| @@ -7,9 +7,9 @@ · 6 unmodified lines | ||
| 7 | 7 | max-width: 90%; |
| 8 | 8 | padding: .625rem 1rem; |
| 9 | 9 | font-size: .75rem; |
| 10 | − color: $notifColor; | |
| 11 | − background: $notifBackground; | |
| 12 | − border: $notifBorder; | |
| 10 | + color: var(--notifColor); | |
| 11 | + background: var(--notifBackground); | |
| 12 | + border: var(--notifBorder); | |
| 13 | 13 | border-radius: .25rem; |
| 14 | 14 | transition: opacity .2s; |
| 15 | 15 | opacity: 0; |
| @@ -30,7 +30,7 @@ · 14 unmodified lines | ||
| 30 | 30 | |
| 31 | 31 | ._notif-info { |
| 32 | 32 | float: right; |
| 33 | − color: $notifColorLight; | |
| 33 | + color: var(--notifColorLight); | |
| 34 | 34 | } |
| 35 | 35 | |
| 36 | 36 | ._notif-link, |
| @@ -69,13 +69,13 @@ · 32 unmodified lines | ||
| 69 | 69 | &::-webkit-scrollbar { width: 10px !important; } |
| 70 | 70 | |
| 71 | 71 | &::-webkit-scrollbar-track { |
| 72 | − background: $notifBackground !important; | |
| 72 | + background: var(--notifBackground) !important; | |
| 73 | 73 | border: 0 !important; |
| 74 | 74 | border-radius: 5px !important; |
| 75 | 75 | } |
| 76 | 76 | |
| 77 | 77 | &::-webkit-scrollbar-thumb { |
| 78 | − border: 3px solid $notifBackground !important; | |
| 78 | + border: 3px solid var(--notifBackground) !important; | |
| 79 | 79 | |
| 80 | 80 | &:hover, &:active { border-width: 2px !important; } |
| 81 | 81 | } |
| @@ -90,7 +90,7 @@ · 8 unmodified lines | ||
| 90 | 90 | > ._news-row { |
| 91 | 91 | line-height: 1.125rem; |
| 92 | 92 | font-size: .6875rem; |
| 93 | − color: $notifColorLight; | |
| 93 | + color: var(--notifColorLight); | |
| 94 | 94 | margin-bottom: .25rem; |
| 95 | 95 | |
| 96 | 96 | + ._news-row { margin-top: .625rem; } |
| @@ -107,7 +107,7 @@ · 10 unmodified lines | ||
| 107 | 107 | ._news-date { |
| 108 | 108 | float: right; |
| 109 | 109 | margin-left: 1rem; |
| 110 | − font-weight: $boldFontWeight; | |
| 110 | + font-weight: var(--boldFontWeight); | |
| 111 | 111 | } |
| 112 | 112 | |
| 113 | 113 | code { |
| @@ -128,9 +128,13 @@ · 14 unmodified lines | ||
| 128 | 128 | } |
| 129 | 129 | |
| 130 | 130 | ._notif-tip { |
| 131 | − color: $textColor; | |
| 132 | − background: $tipBackground; | |
| 133 | − border: $tipBorder; | |
| 131 | + color: var(--textColor); | |
| 132 | + background: var(--tipBackground); | |
| 133 | + border: var(--tipBorder); | |
| 134 | 134 | |
| 135 | − ._notif-info { color: $textColorLight; } | |
| 135 | + ._notif-info { color: var(--textColorLight); } | |
| 136 | +} | |
| 137 | + | |
| 138 | +._notif-right { | |
| 139 | + float: right; | |
| 136 | 140 | } |
| @@ -23,7 +23,7 @@ · 22 unmodified lines | ||
| 23 | 23 | max-width: 100%; |
| 24 | 24 | margin-bottom: 1em; |
| 25 | 25 | padding: 1px; |
| 26 | − border: 1px dotted $boxBorder; | |
| 26 | + border: 1px dotted var(--boxBorder); | |
| 27 | 27 | border-radius: 3px; |
| 28 | 28 | @extend %border-box; |
| 29 | 29 | } |
| @@ -50,7 +50,7 @@ · 20 unmodified lines | ||
| 50 | 50 | display: inline-block; |
| 51 | 51 | vertical-align: top; |
| 52 | 52 | padding: 0 .5rem; |
| 53 | − background: $contentBackground; | |
| 53 | + background: var(--contentBackground); | |
| 54 | 54 | @extend %internal-link; |
| 55 | 55 | |
| 56 | 56 | & + & { margin-left: .75rem; } |
| @@ -66,7 +66,7 @@ · 9 unmodified lines | ||
| 66 | 66 | clear: both; |
| 67 | 67 | margin: 2rem 0 1.5rem; |
| 68 | 68 | font-size: .75rem; |
| 69 | − color: $textColorLight; | |
| 69 | + color: var(--textColorLight); | |
| 70 | 70 | text-align: center; |
| 71 | 71 | -webkit-font-smoothing: subpixel-antialiased; |
| 72 | 72 | |
| @@ -78,7 +78,7 @@ · 5 unmodified lines | ||
| 78 | 78 | display: inline-block; |
| 79 | 79 | margin: 0; |
| 80 | 80 | padding: .25rem .75rem; |
| 81 | − background: $labelBackground; | |
| 81 | + background: var(--labelBackground); | |
| 82 | 82 | border-radius: 3px; |
| 83 | 83 | } |
| 84 | 84 | |
| @@ -1,17 +1,17 @@ | ||
| 1 | 1 | ._path { |
| 2 | 2 | position: absolute; |
| 3 | − z-index: $noticeZ - 1; | |
| 3 | + z-index: var(--headerZ); | |
| 4 | 4 | bottom: 0; |
| 5 | − left: $sidebarWidth; | |
| 5 | + left: var(--sidebarWidth); | |
| 6 | 6 | right: 0; |
| 7 | 7 | height: 2rem; |
| 8 | 8 | line-height: 2rem; |
| 9 | 9 | padding: 0 .375rem; |
| 10 | 10 | font-size: .875rem; |
| 11 | − background: $pathBackground; | |
| 12 | − box-shadow: inset 0 1px $pathBorder; | |
| 11 | + background: var(--pathBackground); | |
| 12 | + box-shadow: inset 0 1px var(--pathBorder); | |
| 13 | 13 | |
| 14 | − @media #{$mediumScreen} { left: $sidebarMediumWidth; } | |
| 14 | + @include mobile { left: var(--sidebarMediumWidth); } | |
| 15 | 15 | |
| 16 | 16 | ._sidebar-hidden & { left: 0; } |
| 17 | 17 | |
| @@ -24,7 +24,7 @@ · 6 unmodified lines | ||
| 24 | 24 | display: inline-block; |
| 25 | 25 | vertical-align: top; |
| 26 | 26 | padding: 0 .375rem; |
| 27 | − color: $textColor; | |
| 27 | + color: var(--textColor); | |
| 28 | 28 | text-decoration: none; |
| 29 | 29 | |
| 30 | 30 | &:first-child:before { |
| @@ -1,9 +1,26 @@ | ||
| 1 | +html { | |
| 2 | + --prismValue: #905; | |
| 3 | + --prismText: #5e8e01; | |
| 4 | + --prismOperator: #a67f59; | |
| 5 | + --prismKeyword: #0070a3; | |
| 6 | + --prismFunction: #dd4a68; | |
| 7 | + --prismVariable: #e90; | |
| 8 | +} | |
| 9 | +html._theme-dark { | |
| 10 | + --prismValue: #eb8160; | |
| 11 | + --prismText: #ddcf88; | |
| 12 | + --prismOperator: #b1c676; | |
| 13 | + --prismKeyword: #91b3ed; | |
| 14 | + --prismFunction: #c79e6b; | |
| 15 | + --prismVariable: #e9c062; | |
| 16 | +} | |
| 17 | + | |
| 1 | 18 | .token.comment, |
| 2 | 19 | .token.prolog, |
| 3 | 20 | .token.doctype, |
| 4 | 21 | .token.cdata, |
| 5 | 22 | .token.punctuation { |
| 6 | − color: $textColorLight; | |
| 23 | + color: var(--textColorLight); | |
| 7 | 24 | } |
| 8 | 25 | |
| 9 | 26 | .namespace { |
| @@ -17,11 +34,7 @@ · 7 unmodified lines | ||
| 17 | 34 | .token.constant, |
| 18 | 35 | .token.symbol, |
| 19 | 36 | .token.deleted { |
| 20 | − @if $style == 'dark' { | |
| 21 | − color: #eb8160; | |
| 22 | − } @else { | |
| 23 | − color: #905; | |
| 24 | − } | |
| 37 | + color: var(--prismValue); | |
| 25 | 38 | } |
| 26 | 39 | |
| 27 | 40 | .token.selector, |
| @@ -30,11 +43,7 @@ · 2 unmodified lines | ||
| 30 | 43 | .token.char, |
| 31 | 44 | .token.builtin, |
| 32 | 45 | .token.inserted { |
| 33 | − @if $style == 'dark' { | |
| 34 | − color: #ddcf88; | |
| 35 | − } @else { | |
| 36 | − color: #5e8e01; | |
| 37 | − } | |
| 46 | + color: var(--prismText); | |
| 38 | 47 | } |
| 39 | 48 | |
| 40 | 49 | .token.operator, |
| @@ -42,44 +51,28 @@ · 1 unmodified line | ||
| 42 | 51 | .token.url, |
| 43 | 52 | .language-css .token.string, |
| 44 | 53 | .style .token.string { |
| 45 | − @if $style == 'dark' { | |
| 46 | − color: #b1c676; | |
| 47 | − } @else { | |
| 48 | − color: #a67f59; | |
| 49 | − } | |
| 54 | + color: var(--prismOperator); | |
| 50 | 55 | } |
| 51 | 56 | |
| 52 | 57 | .token.atrule, |
| 53 | 58 | .token.attr-value, |
| 54 | 59 | .token.keyword { |
| 55 | − @if $style == 'dark' { | |
| 56 | − color: #91b3ed; | |
| 57 | − } @else { | |
| 58 | − color: #0070a3; | |
| 59 | − } | |
| 60 | + color: var(--prismKeyword); | |
| 60 | 61 | } |
| 61 | 62 | |
| 62 | 63 | .token.function { |
| 63 | − @if $style == 'dark' { | |
| 64 | − color: #c79e6b; | |
| 65 | − } @else { | |
| 66 | − color: #dd4a68; | |
| 67 | − } | |
| 64 | + color: var(--prismFunction); | |
| 68 | 65 | } |
| 69 | 66 | |
| 70 | 67 | .token.regex, |
| 71 | 68 | .token.important, |
| 72 | 69 | .token.variable { |
| 73 | − @if $style == 'dark' { | |
| 74 | − color: #e9c062; | |
| 75 | − } @else { | |
| 76 | − color: #e90; | |
| 77 | − } | |
| 70 | + color: var(--prismVariable); | |
| 78 | 71 | } |
| 79 | 72 | |
| 80 | 73 | .token.important, |
| 81 | 74 | .token.bold { |
| 82 | − font-weight: $boldFontWeight; | |
| 75 | + font-weight: var(--boldFontWeight); | |
| 83 | 76 | } |
| 84 | 77 | |
| 85 | 78 | .token.italic { |
| @@ -7,15 +7,15 @@ · 6 unmodified lines | ||
| 7 | 7 | position: absolute; |
| 8 | 8 | top: 0; |
| 9 | 9 | bottom: 0; |
| 10 | − z-index: $headerZ; | |
| 10 | + z-index: var(--headerZ); | |
| 11 | 11 | |
| 12 | 12 | &._in { display: block; } |
| 13 | 13 | |
| 14 | 14 | > ._header { justify-content: space-between; } |
| 15 | 15 | |
| 16 | 16 | &._dirty > ._header { |
| 17 | − background: $noteGreenBackground; | |
| 18 | − border-color: $noteGreenBorder; | |
| 17 | + background: var(--noteGreenBackground); | |
| 18 | + border-color: var(--noteGreenBorder); | |
| 19 | 19 | } |
| 20 | 20 | } |
| 21 | 21 | |
| @@ -37,7 +37,7 @@ · 15 unmodified lines | ||
| 37 | 37 | padding-right: .5rem; |
| 38 | 38 | line-height: inherit; |
| 39 | 39 | font-size: inherit; |
| 40 | − font-weight: $boldFontWeight; | |
| 40 | + font-weight: var(--boldFontWeight); | |
| 41 | 41 | text-align: right; |
| 42 | 42 | @extend %border-box; |
| 43 | 43 | } |
| @@ -52,7 +52,7 @@ · 8 unmodified lines | ||
| 52 | 52 | |
| 53 | 53 | > small { |
| 54 | 54 | display: block; |
| 55 | − color: $textColorLight; | |
| 55 | + color: var(--textColorLight); | |
| 56 | 56 | margin-left: 1.75rem; |
| 57 | 57 | } |
| 58 | 58 | |
| @@ -62,7 +62,7 @@ · 3 unmodified lines | ||
| 62 | 62 | } |
| 63 | 63 | } |
| 64 | 64 | |
| 65 | −@media (max-width: $maxWidth) { | |
| 65 | +@media (max-width: 80rem) { | |
| 66 | 66 | ._setting-max-width { display: none; } |
| 67 | 67 | } |
| 68 | 68 | |
| @@ -80,7 +80,7 @@ · 11 unmodified lines | ||
| 80 | 80 | line-height: 1.5rem; |
| 81 | 81 | padding: 0 .75rem; |
| 82 | 82 | font-size: .875rem; |
| 83 | − font-weight: $boldFontWeight; | |
| 83 | + font-weight: var(--boldFontWeight); | |
| 84 | 84 | color: inherit; |
| 85 | 85 | text-align: left; |
| 86 | 86 | cursor: pointer; |
| @@ -119,12 +119,12 @@ · 32 unmodified lines | ||
| 119 | 119 | position: relative; |
| 120 | 120 | vertical-align: top; |
| 121 | 121 | padding: 0 .75rem; |
| 122 | − line-height: $headerHeight; | |
| 123 | − color: $textColorLight; | |
| 122 | + line-height: var(--headerHeight); | |
| 123 | + color: var(--textColorLight); | |
| 124 | 124 | |
| 125 | 125 | &.active { |
| 126 | − color: $textColor; | |
| 127 | − font-weight: $boldFontWeight; | |
| 128 | − box-shadow: inset 0 -2px $linkColor; | |
| 126 | + color: var(--textColor); | |
| 127 | + font-weight: var(--boldFontWeight); | |
| 128 | + box-shadow: inset 0 -2px var(--linkColor); | |
| 129 | 129 | } |
| 130 | 130 | } |
| @@ -4,14 +4,14 @@ · 3 unmodified lines | ||
| 4 | 4 | |
| 5 | 5 | ._sidebar { |
| 6 | 6 | position: absolute; |
| 7 | − z-index: $sidebarZ; | |
| 7 | + z-index: var(--sidebarZ); | |
| 8 | 8 | top: 0; |
| 9 | 9 | bottom: 0; |
| 10 | 10 | left: 0; |
| 11 | 11 | overflow-x: hidden; |
| 12 | 12 | overflow-y: scroll; |
| 13 | − padding-top: $headerHeight; | |
| 14 | − background: $sidebarBackground; | |
| 13 | + padding-top: var(--headerHeight); | |
| 14 | + background: var(--sidebarBackground); | |
| 15 | 15 | background-clip: content-box; |
| 16 | 16 | -webkit-overflow-scrolling: touch; |
| 17 | 17 | -ms-overflow-style: none; // IE 10 doesn't support pointer-events |
| @@ -22,13 +22,13 @@ · 4 unmodified lines | ||
| 22 | 22 | |
| 23 | 23 | ._overlay-scrollbars & { |
| 24 | 24 | padding-top: 0; |
| 25 | − top: $headerHeight; | |
| 25 | + top: var(--headerHeight); | |
| 26 | 26 | } |
| 27 | 27 | |
| 28 | 28 | body:not(._native-scrollbars) & { |
| 29 | 29 | &::-webkit-scrollbar { width: 10px; } |
| 30 | 30 | &::-webkit-scrollbar-track { |
| 31 | − background: $contentBackground; | |
| 31 | + background: var(--contentBackground); | |
| 32 | 32 | border: 0; |
| 33 | 33 | } |
| 34 | 34 | &::-webkit-scrollbar-thumb { |
| @@ -52,10 +52,10 @@ · 17 unmodified lines | ||
| 52 | 52 | |
| 53 | 53 | ._resizer { |
| 54 | 54 | position: absolute; |
| 55 | − z-index: $sidebarZ + 1; | |
| 56 | − top: $headerHeight; | |
| 57 | − bottom: $headerHeight; | |
| 58 | − left: $sidebarWidth; | |
| 55 | + z-index: var(--headerZ); | |
| 56 | + top: var(--headerHeight); | |
| 57 | + bottom: var(--headerHeight); | |
| 58 | + left: var(--sidebarWidth); | |
| 59 | 59 | margin-left: -2px; |
| 60 | 60 | width: 3px; |
| 61 | 61 | cursor: col-resize; |
| @@ -72,11 +72,11 @@ · 10 unmodified lines | ||
| 72 | 72 | margin: 0; |
| 73 | 73 | padding: 0; |
| 74 | 74 | list-style: none; |
| 75 | − width: $sidebarWidth; | |
| 76 | − box-shadow: inset -1px 0 $sidebarBorder; | |
| 75 | + width: var(--sidebarWidth); | |
| 76 | + box-shadow: inset -1px 0 var(--sidebarBorder); | |
| 77 | 77 | @extend %border-box; |
| 78 | 78 | |
| 79 | − @media #{$mediumScreen} { width: $sidebarMediumWidth; } | |
| 79 | + @include mobile { width: var(--sidebarMediumWidth); } | |
| 80 | 80 | |
| 81 | 81 | ._sidebar > & { min-height: 100%; } |
| 82 | 82 | |
| @@ -89,7 +89,7 @@ · 6 unmodified lines | ||
| 89 | 89 | padding: 0 .75rem 0 2.125rem; |
| 90 | 90 | line-height: 2rem; |
| 91 | 91 | font-size: .75rem; |
| 92 | − color: $textColorLight; | |
| 92 | + color: var(--textColorLight); | |
| 93 | 93 | text-transform: uppercase; |
| 94 | 94 | cursor: default; |
| 95 | 95 | } |
| @@ -110,8 +110,8 @@ · 14 unmodified lines | ||
| 110 | 110 | line-height: 1.5rem; |
| 111 | 111 | font-size: .875rem; |
| 112 | 112 | cursor: default; |
| 113 | − background: $sidebarBackground; | |
| 114 | − box-shadow: inset -1px 0 $sidebarBorder; | |
| 113 | + background: var(--sidebarBackground); | |
| 114 | + box-shadow: inset -1px 0 var(--sidebarBorder); | |
| 115 | 115 | @extend %truncate-text; |
| 116 | 116 | |
| 117 | 117 | &, &:hover { |
| @@ -123,16 +123,16 @@ · 5 unmodified lines | ||
| 123 | 123 | &.focus:hover, |
| 124 | 124 | &.active, |
| 125 | 125 | &.active:hover { |
| 126 | − color: $focusText; | |
| 127 | − background: $focusBackground; | |
| 128 | − box-shadow: inset -1px 0 $focusBorder; | |
| 126 | + color: var(--focusText); | |
| 127 | + background: var(--focusBackground); | |
| 128 | + box-shadow: inset -1px 0 var(--focusBorder); | |
| 129 | 129 | } |
| 130 | 130 | |
| 131 | 131 | &.active, |
| 132 | 132 | &.active:hover { |
| 133 | − color: $selectionText; | |
| 134 | − background: $selectionBackground; | |
| 135 | − box-shadow: inset -1px 0 $selectionBorder; | |
| 133 | + color: var(--selectionText); | |
| 134 | + background: var(--selectionBackground); | |
| 135 | + box-shadow: inset -1px 0 var(--selectionBorder); | |
| 136 | 136 | } |
| 137 | 137 | |
| 138 | 138 | &:before { |
| @@ -160,7 +160,7 @@ · 21 unmodified lines | ||
| 160 | 160 | } |
| 161 | 161 | |
| 162 | 162 | ._list-count { |
| 163 | − color: $textColorLighter; | |
| 163 | + color: var(--textColorLighter); | |
| 164 | 164 | pointer-events: none; |
| 165 | 165 | |
| 166 | 166 | ._list-disabled:hover > & { display: none; } |
| @@ -168,7 +168,7 @@ · 1 unmodified line | ||
| 168 | 168 | |
| 169 | 169 | ._list-enable { |
| 170 | 170 | display: none; |
| 171 | − color: $linkColor; | |
| 171 | + color: var(--linkColor); | |
| 172 | 172 | cursor: pointer; |
| 173 | 173 | |
| 174 | 174 | &:hover { text-decoration: underline; } |
| @@ -188,7 +188,7 @@ · 13 unmodified lines | ||
| 188 | 188 | ._list-disabled { |
| 189 | 189 | @extend %_list-dir; |
| 190 | 190 | |
| 191 | − &, &:hover { color: $textColorLight; } | |
| 191 | + &, &:hover { color: var(--textColorLight); } | |
| 192 | 192 | &:before { opacity: .7; } |
| 193 | 193 | } |
| 194 | 194 | |
| @@ -200,11 +200,9 @@ · 5 unmodified lines | ||
| 200 | 200 | width: 1rem; |
| 201 | 201 | height: 1rem; |
| 202 | 202 | cursor: pointer; |
| 203 | − fill: black; | |
| 203 | + fill: var(--absolute); | |
| 204 | 204 | opacity: .4; |
| 205 | 205 | |
| 206 | − @if $style == 'dark' { fill: white; } | |
| 207 | − | |
| 208 | 206 | &:hover { opacity: .65; } |
| 209 | 207 | |
| 210 | 208 | ._list-rdir > & { |
| @@ -239,11 +237,11 @@ · 28 unmodified lines | ||
| 239 | 237 | // |
| 240 | 238 | |
| 241 | 239 | ._list-pagelink { |
| 242 | − color: $linkColor; | |
| 240 | + color: var(--linkColor); | |
| 243 | 241 | cursor: pointer; |
| 244 | 242 | |
| 245 | 243 | &:hover { |
| 246 | − color: $linkColorHover; | |
| 244 | + color: var(--linkColorHover); | |
| 247 | 245 | text-decoration: underline; |
| 248 | 246 | } |
| 249 | 247 | } |
| @@ -275,9 +273,9 @@ · 25 unmodified lines | ||
| 275 | 273 | left: .75rem; |
| 276 | 274 | width: .75rem; |
| 277 | 275 | height: 1px; |
| 278 | − background: rgba($selectionText, .9); | |
| 279 | − box-shadow: 0 -3px rgba($selectionText, .9), // top line | |
| 280 | − 0 3px rgba($selectionText, .9); // bottom line | |
| 276 | + background: var(--transparentSelectionText); | |
| 277 | + box-shadow: 0 -3px var(--transparentSelectionText), // top line | |
| 278 | + 0 3px var(--transparentSelectionText); // bottom line | |
| 281 | 279 | } |
| 282 | 280 | } |
| 283 | 281 | |
| @@ -289,7 +287,7 @@ · 5 unmodified lines | ||
| 289 | 287 | padding: .5rem .75rem; |
| 290 | 288 | line-height: 1.25rem; |
| 291 | 289 | font-size: .8125rem; |
| 292 | − color: $textColorLight; | |
| 290 | + color: var(--textColorLight); | |
| 293 | 291 | |
| 294 | 292 | & + & { padding-top: 0; } |
| 295 | 293 | } |
| @@ -303,9 +301,9 @@ · 7 unmodified lines | ||
| 303 | 301 | ._list-hover.clone { |
| 304 | 302 | position: fixed; |
| 305 | 303 | overflow: visible; |
| 306 | − z-index: $hoverZ; | |
| 304 | + z-index: var(--hoverZ); | |
| 307 | 305 | left: 0; |
| 308 | − min-width: $sidebarWidth; | |
| 306 | + min-width: var(--sidebarWidth); | |
| 309 | 307 | padding: .25rem .75rem; |
| 310 | 308 | pointer-events: none; |
| 311 | 309 | -webkit-font-smoothing: subpixel-antialiased; |
| @@ -313,7 +311,7 @@ · 1 unmodified line | ||
| 313 | 311 | transform: translate3d(0, 0, 0); |
| 314 | 312 | @extend %border-box; |
| 315 | 313 | |
| 316 | − @media #{$mediumScreen} { min-width: $sidebarMediumWidth; } | |
| 314 | + @include mobile { min-width: var(--sidebarMediumWidth); } | |
| 317 | 315 | |
| 318 | 316 | > ._list-text { display: inline; } |
| 319 | 317 | |
| @@ -346,10 +344,10 @@ · 26 unmodified lines | ||
| 346 | 344 | padding: .5rem .75rem .25rem .75rem; |
| 347 | 345 | line-height: 1.5rem; |
| 348 | 346 | font-size: .75rem; |
| 349 | − font-weight: $bolderFontWeight; | |
| 350 | − color: $textColorLight; | |
| 347 | + font-weight: var(--bolderFontWeight); | |
| 348 | + color: var(--textColorLight); | |
| 351 | 349 | text-transform: uppercase; |
| 352 | − background: linear-gradient(to bottom, $sidebarBackground, $sidebarBackground 75%, rgba($sidebarBackground, 0)); | |
| 350 | + background: linear-gradient(to bottom, var(--sidebarBackground), var(--sidebarBackground) 75%, var(--transparentSidebarBackground)); | |
| 353 | 351 | cursor: default; |
| 354 | 352 | } |
| 355 | 353 | |
| @@ -1,9 +1,13 @@ | ||
| 1 | 1 | html { |
| 2 | 2 | height: 100%; |
| 3 | 3 | font-size: 100%; |
| 4 | − background: $documentBackground; | |
| 4 | + background: #fff; // fallback to show the error message to browsers that don't support CSS variables. | |
| 5 | + background: var(--documentBackground); | |
| 5 | 6 | |
| 6 | − @media #{$mediumScreen} { font-size: 93.75%; } | |
| 7 | + @include mobile { font-size: 93.75%; } | |
| 8 | + | |
| 9 | + @include print { background: none; } | |
| 10 | + @import 'global/print'; | |
| 7 | 11 | } |
| 8 | 12 | |
| 9 | 13 | body { |
| @@ -14,10 +18,11 @@ body { · 4 unmodified lines | ||
| 14 | 18 | font-weight: normal; |
| 15 | 19 | font-family: $baseFont; |
| 16 | 20 | line-height: 1.7; |
| 17 | − color: $textColor; | |
| 21 | + color: $textColor; // fallback to show the error message to browsers that don't support CSS variables. | |
| 22 | + color: var(--textColor); | |
| 18 | 23 | word-wrap: break-word; |
| 19 | 24 | overflow-wrap: break-word; |
| 20 | − background: $contentBackground; | |
| 25 | + background: var(--contentBackground); | |
| 21 | 26 | touch-action: manipulation; |
| 22 | 27 | -webkit-tap-highlight-color: rgba(black, 0); |
| 23 | 28 | -webkit-touch-callout: none; |
| @@ -26,11 +31,11 @@ body { · 2 unmodified lines | ||
| 26 | 31 | } |
| 27 | 32 | |
| 28 | 33 | a { |
| 29 | − color: $linkColor; | |
| 30 | − text-decoration: $linkTextDecoration; | |
| 34 | + color: var(--linkColor); | |
| 35 | + text-decoration: var(--linkTextDecoration); | |
| 31 | 36 | |
| 32 | 37 | &:hover { |
| 33 | − color: $linkColorHover; | |
| 38 | + color: var(--linkColorHover); | |
| 34 | 39 | text-decoration: underline; |
| 35 | 40 | } |
| 36 | 41 | } |
| @@ -44,7 +49,7 @@ img { · 7 unmodified lines | ||
| 44 | 49 | h1, h2, h3, h4, h5, h6 { |
| 45 | 50 | margin: 1.5em 0 1em; |
| 46 | 51 | line-height: 1.3; |
| 47 | − font-weight: $bolderFontWeight; | |
| 52 | + font-weight: var(--bolderFontWeight); | |
| 48 | 53 | } |
| 49 | 54 | |
| 50 | 55 | h1 { font-size: 1.5em; } |
| @@ -56,7 +61,7 @@ h5, h6 { font-size: 1em; } · 5 unmodified lines | ||
| 56 | 61 | p { margin: 0 0 1em; } |
| 57 | 62 | p:last-child { margin-bottom: 0; } |
| 58 | 63 | |
| 59 | −b, strong { font-weight: $boldFontWeight; } | |
| 64 | +b, strong { font-weight: var(--boldFontWeight); } | |
| 60 | 65 | |
| 61 | 66 | small { font-size: .9em; } |
| 62 | 67 | |
| @@ -76,7 +81,7 @@ li > ul, li > ol, dd > ul, dd > ol { margin: .5em 0; } · 13 unmodified lines | ||
| 76 | 81 | li > p { margin-bottom: .25em; } |
| 77 | 82 | |
| 78 | 83 | dl { margin: 1.5em 0; } |
| 79 | −dt { font-weight: $boldFontWeight; } | |
| 84 | +dt { font-weight: var(--boldFontWeight); } | |
| 80 | 85 | dd { |
| 81 | 86 | margin: .375em; |
| 82 | 87 | padding-left: 1em; |
| @@ -88,15 +93,15 @@ dfn, var { font-style: normal; } · 5 unmodified lines | ||
| 88 | 93 | |
| 89 | 94 | abbr, acronym, dfn { |
| 90 | 95 | cursor: help; |
| 91 | − border-bottom: 1px dotted $textColor; | |
| 96 | + border-bottom: 1px dotted var(--textColor); | |
| 92 | 97 | } |
| 93 | 98 | |
| 94 | 99 | pre, code, samp, %pre, %code { |
| 95 | − font-family: $monoFont; | |
| 100 | + font-family: var(--monoFont); | |
| 96 | 101 | font-weight: normal; |
| 97 | 102 | font-style: normal; |
| 98 | 103 | font-size: .9em; |
| 99 | − color: $textColor; | |
| 104 | + color: var(--textColor); | |
| 100 | 105 | white-space: pre-wrap; |
| 101 | 106 | direction: ltr; |
| 102 | 107 | -moz-tab-size: 2; |
| @@ -118,14 +123,14 @@ a > code { color: inherit; } · 15 unmodified lines | ||
| 118 | 123 | table { |
| 119 | 124 | margin: 1.5em 0; |
| 120 | 125 | background: none; |
| 121 | − border: 1px solid $boxBorder; | |
| 126 | + border: 1px solid var(--boxBorder); | |
| 122 | 127 | border-collapse: separate; |
| 123 | 128 | border-spacing: 0; |
| 124 | 129 | border-radius: 3px; |
| 125 | 130 | } |
| 126 | 131 | |
| 127 | 132 | caption { |
| 128 | − font-weight: $boldFontWeight; | |
| 133 | + font-weight: var(--boldFontWeight); | |
| 129 | 134 | padding: 0 .7em .3em; |
| 130 | 135 | } |
| 131 | 136 | |
| @@ -139,15 +144,15 @@ th, td { · 7 unmodified lines | ||
| 139 | 144 | } |
| 140 | 145 | |
| 141 | 146 | th { |
| 142 | − font-weight: $boldFontWeight; | |
| 147 | + font-weight: var(--boldFontWeight); | |
| 143 | 148 | border: 0; |
| 144 | − border-bottom: 1px solid $boxBorder; | |
| 149 | + border-bottom: 1px solid var(--boxBorder); | |
| 145 | 150 | border-radius: 0; |
| 146 | 151 | @extend %heading-box; |
| 147 | 152 | |
| 148 | 153 | &:empty { background: none; } |
| 149 | 154 | |
| 150 | − + th, + td { border-left: 1px solid $boxBorder; } | |
| 155 | + + th, + td { border-left: 1px solid var(--boxBorder); } | |
| 151 | 156 | |
| 152 | 157 | tr:first-child > &:first-child { border-top-left-radius: 3px; } |
| 153 | 158 | tr:first-child > &:last-child { border-top-right-radius: 3px; } |
| @@ -158,10 +163,10 @@ th { · 4 unmodified lines | ||
| 158 | 163 | } |
| 159 | 164 | |
| 160 | 165 | td { |
| 161 | − background: $contentBackground; | |
| 162 | − border-bottom: 1px solid $boxBorderLight; | |
| 166 | + background: var(--contentBackground); | |
| 167 | + border-bottom: 1px solid var(--boxBorderLight); | |
| 163 | 168 | |
| 164 | − + td { border-left: 1px solid $boxBorderLight; } | |
| 169 | + + td { border-left: 1px solid var(--boxBorderLight); } | |
| 165 | 170 | tr:last-child > & { border-bottom: 0; } |
| 166 | 171 | |
| 167 | 172 | > pre:only-child, > p:only-child, > ul:only-child, > ol:only-child { |
| @@ -187,7 +192,7 @@ input, button { · 19 unmodified lines | ||
| 187 | 192 | margin: 0; |
| 188 | 193 | font-family: inherit; |
| 189 | 194 | font-size: 100%; |
| 190 | − color: $textColor; | |
| 195 | + color: var(--textColor); | |
| 191 | 196 | line-height: normal; |
| 192 | 197 | @extend %border-box; |
| 193 | 198 | } |
| @@ -215,6 +220,10 @@ button:focus { · 21 unmodified lines | ||
| 215 | 220 | outline: -webkit-focus-ring-color auto 5px; |
| 216 | 221 | } |
| 217 | 222 | |
| 223 | +img, iframe { | |
| 224 | + background: var(--externalsBackground); | |
| 225 | +} | |
| 226 | + | |
| 218 | 227 | input[type="search"]::-webkit-search-cancel-button, |
| 219 | 228 | input[type="search"]::-webkit-search-decoration { |
| 220 | 229 | -webkit-appearance: none; |
| @@ -227,9 +236,9 @@ input[type="search"]::-webkit-search-decoration { · 6 unmodified lines | ||
| 227 | 236 | border: 0 !important; |
| 228 | 237 | } |
| 229 | 238 | |
| 230 | −::-webkit-input-placeholder { color: $textColorLighter; } | |
| 231 | −::-moz-placeholder { color: $textColorLighter; opacity: 1; } | |
| 232 | −:-ms-input-placeholder { color: $textColorLighter; } | |
| 239 | +::-webkit-input-placeholder { color: var(--textColorLighter); } | |
| 240 | +::-moz-placeholder { color: var(--textColorLighter); opacity: 1; } | |
| 241 | +:-ms-input-placeholder { color: var(--textColorLighter); } | |
| 233 | 242 | |
| 234 | 243 | body:not(._native-scrollbars) { |
| 235 | 244 | *::-webkit-scrollbar { -webkit-appearance: none; } |
| @@ -240,12 +249,12 @@ body:not(._native-scrollbars) { · 4 unmodified lines | ||
| 240 | 249 | *::-webkit-scrollbar-corner { display: none; } |
| 241 | 250 | |
| 242 | 251 | *::-webkit-scrollbar-track { |
| 243 | − background: $contentBackground; | |
| 244 | − border: 1px solid $contentBackground; | |
| 252 | + background: var(--contentBackground); | |
| 253 | + border: 1px solid var(--contentBackground); | |
| 245 | 254 | |
| 246 | 255 | &:hover { |
| 247 | − background: $sidebarBackground; | |
| 248 | − border-color: $sidebarBorder; | |
| 256 | + background: var(--sidebarBackground); | |
| 257 | + border-color: var(--sidebarBorder); | |
| 249 | 258 | } |
| 250 | 259 | |
| 251 | 260 | &:vertical { border-width: 0 0 0 1px; } |
| @@ -263,14 +272,14 @@ body:not(._native-scrollbars) { · 11 unmodified lines | ||
| 263 | 272 | |
| 264 | 273 | *::-webkit-scrollbar-thumb { |
| 265 | 274 | min-height: 2rem; |
| 266 | − background: $scrollbarColor; | |
| 275 | + background: var(--scrollbarColor); | |
| 267 | 276 | background-clip: padding-box; |
| 268 | 277 | border: 5px solid rgba(black, 0); |
| 269 | 278 | border-radius: 10px; |
| 270 | 279 | |
| 271 | 280 | &:hover, |
| 272 | 281 | &:active { |
| 273 | − background-color: $scrollbarColorHover; | |
| 282 | + background-color: var(--scrollbarColorHover); | |
| 274 | 283 | border-width: 4px; |
| 275 | 284 | } |
| 276 | 285 | } |
| @@ -36,15 +36,15 @@ · 35 unmodified lines | ||
| 36 | 36 | // |
| 37 | 37 | |
| 38 | 38 | %box { |
| 39 | − background: $boxBackground; | |
| 40 | − border: 1px solid $boxBorder; | |
| 39 | + background: var(--boxBackground); | |
| 40 | + border: 1px solid var(--boxBorder); | |
| 41 | 41 | border-radius: 3px; |
| 42 | 42 | } |
| 43 | 43 | |
| 44 | 44 | %heading-box { |
| 45 | − color: $boxHeaderColor; | |
| 46 | − background: $boxHeaderBackground; | |
| 47 | − border: 1px solid $boxBorder; | |
| 45 | + color: var(--boxHeaderColor); | |
| 46 | + background: var(--boxHeaderBackground); | |
| 47 | + border: 1px solid var(--boxBorder); | |
| 48 | 48 | border-radius: 3px; |
| 49 | 49 | } |
| 50 | 50 | |
| @@ -82,15 +82,15 @@ · 31 unmodified lines | ||
| 82 | 82 | %note { |
| 83 | 83 | margin: 1.5rem 0; |
| 84 | 84 | padding: .5rem .875rem; |
| 85 | − background: $noteBackground; | |
| 86 | − border: 1px solid $noteBorder; | |
| 85 | + background: var(--noteBackground); | |
| 86 | + border: 1px solid var(--noteBorder); | |
| 87 | 87 | border-radius: 3px; |
| 88 | 88 | } |
| 89 | 89 | |
| 90 | 90 | %label { |
| 91 | 91 | margin: 0 1px; |
| 92 | 92 | padding: 1px 4px 2px; |
| 93 | − background: $labelBackground; | |
| 93 | + background: var(--labelBackground); | |
| 94 | 94 | border-radius: 3px; |
| 95 | 95 | } |
| 96 | 96 | |
| @@ -102,35 +102,35 @@ · 5 unmodified lines | ||
| 102 | 102 | padding-right: .5em; |
| 103 | 103 | overflow: hidden; |
| 104 | 104 | font-size: inherit; |
| 105 | − color: $boxHeaderColor; | |
| 106 | − border: 1px solid $boxBorder; | |
| 105 | + color: var(--boxHeaderColor); | |
| 106 | + border: 1px solid var(--boxBorder); | |
| 107 | 107 | border-radius: 2px; |
| 108 | 108 | @extend %label; |
| 109 | 109 | } |
| 110 | 110 | |
| 111 | 111 | %label-yellow { |
| 112 | − background: $noteBackground; | |
| 113 | − border-color: $noteBorder; | |
| 112 | + background: var(--noteBackground); | |
| 113 | + border-color: var(--noteBorder); | |
| 114 | 114 | } |
| 115 | 115 | |
| 116 | 116 | %note-green, %label-green { |
| 117 | − background: $noteGreenBackground; | |
| 118 | − border-color: $noteGreenBorder; | |
| 117 | + background: var(--noteGreenBackground); | |
| 118 | + border-color: var(--noteGreenBorder); | |
| 119 | 119 | } |
| 120 | 120 | |
| 121 | 121 | %note-blue, %label-blue { |
| 122 | − background: $noteBlueBackground; | |
| 123 | − border-color: $noteBlueBorder; | |
| 122 | + background: var(--noteBlueBackground); | |
| 123 | + border-color: var(--noteBlueBorder); | |
| 124 | 124 | } |
| 125 | 125 | |
| 126 | 126 | %note-orange, %label-orange { |
| 127 | − background: $noteOrangeBackground; | |
| 128 | − border-color: $noteOrangeBorder; | |
| 127 | + background: var(--noteOrangeBackground); | |
| 128 | + border-color: var(--noteOrangeBorder); | |
| 129 | 129 | } |
| 130 | 130 | |
| 131 | 131 | %note-red, %label-red { |
| 132 | − background: $noteRedBackground; | |
| 133 | − border-color: $noteRedBorder; | |
| 132 | + background: var(--noteRedBackground); | |
| 133 | + border-color: var(--noteRedBorder); | |
| 134 | 134 | } |
| 135 | 135 | |
| 136 | 136 | // |
| @@ -149,12 +149,11 @@ · 12 unmodified lines | ||
| 149 | 149 | pointer-events: none; |
| 150 | 150 | |
| 151 | 151 | // <svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M15,15H2V6h2.595c0,0,0.689-0.896,2.17-2H1C0.447,4,0,4.449,0,5v11c0,0.553,0.447,1,1,1h15c0.553,0,1-0.447,1-1v-3.746 l-2,1.645V15z M13.361,8.05v3.551L20,6.4l-6.639-4.999v3.131C5.3,4.532,5.3,12.5,5.3,12.5C7.582,8.752,8.986,8.05,13.361,8.05z"/></svg> |
| 152 | − @if $style == 'dark' { | |
| 153 | − background-image: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjAgMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbD0iI2NiZDBkMCIgZD0iTTE1LDE1SDJWNmgyLjU5NWMwLDAsMC42ODktMC44OTYsMi4xNy0ySDFDMC40NDcsNCwwLDQuNDQ5LDAsNXYxMWMwLDAuNTUzLDAuNDQ3LDEsMSwxaDE1YzAuNTUzLDAsMS0wLjQ0NywxLTF2LTMuNzQ2IGwtMiwxLjY0NVYxNXogTTEzLjM2MSw4LjA1djMuNTUxTDIwLDYuNGwtNi42MzktNC45OTl2My4xMzFDNS4zLDQuNTMyLDUuMywxMi41LDUuMywxMi41QzcuNTgyLDguNzUyLDguOTg2LDguMDUsMTMuMzYxLDguMDV6Ii8+PC9zdmc+); | |
| 154 | − } @else { | |
| 155 | − background-image: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjAgMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbD0iIzMzNzdjMCIgZD0iTTE1LDE1SDJWNmgyLjU5NWMwLDAsMC42ODktMC44OTYsMi4xNy0ySDFDMC40NDcsNCwwLDQuNDQ5LDAsNXYxMWMwLDAuNTUzLDAuNDQ3LDEsMSwxaDE1YzAuNTUzLDAsMS0wLjQ0NywxLTF2LTMuNzQ2IGwtMiwxLjY0NVYxNXogTTEzLjM2MSw4LjA1djMuNTUxTDIwLDYuNGwtNi42MzktNC45OTl2My4xMzFDNS4zLDQuNTMyLDUuMywxMi41LDUuMywxMi41QzcuNTgyLDguNzUyLDguOTg2LDguMDUsMTMuMzYxLDguMDV6Ii8+PC9zdmc+); | |
| 156 | − } | |
| 152 | + background-image: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjAgMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbD0iIzMzNzdjMCIgZD0iTTE1LDE1SDJWNmgyLjU5NWMwLDAsMC42ODktMC44OTYsMi4xNy0ySDFDMC40NDcsNCwwLDQuNDQ5LDAsNXYxMWMwLDAuNTUzLDAuNDQ3LDEsMSwxaDE1YzAuNTUzLDAsMS0wLjQ0NywxLTF2LTMuNzQ2IGwtMiwxLjY0NVYxNXogTTEzLjM2MSw4LjA1djMuNTUxTDIwLDYuNGwtNi42MzktNC45OTl2My4xMzFDNS4zLDQuNTMyLDUuMywxMi41LDUuMywxMi41QzcuNTgyLDguNzUyLDguOTg2LDguMDUsMTMuMzYxLDguMDV6Ii8+PC9zdmc+); | |
| 157 | 153 | } |
| 158 | 154 | } |
| 155 | +html._theme-dark %external-link:after { | |
| 156 | + background-image: url(data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjAgMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbD0iI2NiZDBkMCIgZD0iTTE1LDE1SDJWNmgyLjU5NWMwLDAsMC42ODktMC44OTYsMi4xNy0ySDFDMC40NDcsNCwwLDQuNDQ5LDAsNXYxMWMwLDAuNTUzLDAuNDQ3LDEsMSwxaDE1YzAuNTUzLDAsMS0wLjQ0NywxLTF2LTMuNzQ2IGwtMiwxLjY0NVYxNXogTTEzLjM2MSw4LjA1djMuNTUxTDIwLDYuNGwtNi42MzktNC45OTl2My4xMzFDNS4zLDQuNTMyLDUuMywxMi41LDUuMywxMi41QzcuNTgyLDguNzUyLDguOTg2LDguMDUsMTMuMzYxLDguMDV6Ii8+PC9zdmc+); | |
| 157 | +} | |
| 159 | 158 | |
| 160 | 159 | %internal-link:after { content: none !important; } |
| @@ -1,166 +0,0 @@ | ||
| 1 | −%svg-icon { | |
| 2 | − display: inline-block; | |
| 3 | − vertical-align: top; | |
| 4 | − width: 1rem; | |
| 5 | − height: 1rem; | |
| 6 | − pointer-events: none; | |
| 7 | − fill: currentColor; | |
| 8 | −} | |
| 9 | − | |
| 10 | −%doc-icon { | |
| 11 | − content: ''; | |
| 12 | − display: block; | |
| 13 | − width: 1rem; | |
| 14 | − height: 1rem; | |
| 15 | − background-image: image-url('docs-1.png'); | |
| 16 | − background-size: 10rem 10rem; | |
| 17 | −} | |
| 18 | − | |
| 19 | −%doc-icon-2 { background-image: image-url('docs-2.png') !important; } | |
| 20 | − | |
| 21 | −@media (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) { | |
| 22 | − %doc-icon { background-image: image-url('docs-1@2x.png'); } | |
| 23 | − %doc-icon-2 { background-image: image-url('docs-2@2x.png') !important; } | |
| 24 | −} | |
| 25 | − | |
| 26 | −%darkIconFix { | |
| 27 | − @if $style == 'dark' { | |
| 28 | − filter: invert(100%) grayscale(100%); | |
| 29 | − -webkit-filter: invert(100%) grayscale(100%); | |
| 30 | − } | |
| 31 | −} | |
| 32 | − | |
| 33 | −._icon-jest:before { background-position: 0 0; } | |
| 34 | −._icon-liquid:before { background-position: -1rem 0; } | |
| 35 | −._icon-openjdk:before { background-position: -2rem 0; } | |
| 36 | −._icon-codeceptjs:before { background-position: -3rem 0; } | |
| 37 | −._icon-codeception:before { background-position: -4rem 0; } | |
| 38 | −._icon-sqlite:before { background-position: -5rem 0; @extend %darkIconFix !optional; } | |
| 39 | −._icon-async:before { background-position: -6rem 0; @extend %darkIconFix !optional; } | |
| 40 | −._icon-http:before { background-position: -7rem 0; @extend %darkIconFix !optional; } | |
| 41 | −._icon-jquery:before { background-position: -8rem 0; @extend %darkIconFix !optional; } | |
| 42 | −._icon-underscore:before { background-position: -9rem 0; @extend %darkIconFix !optional; } | |
| 43 | −._icon-html:before { background-position: 0 -1rem; } | |
| 44 | −._icon-css:before { background-position: -1rem -1rem; } | |
| 45 | −._icon-dom:before { background-position: -2rem -1rem; } | |
| 46 | −._icon-dom_events:before { background-position: -3rem -1rem; } | |
| 47 | −._icon-javascript:before { background-position: -4rem -1rem; } | |
| 48 | −._icon-backbone:before { background-position: -5rem -1rem; @extend %darkIconFix !optional; } | |
| 49 | −._icon-node:before, | |
| 50 | −._icon-node_lts:before { background-position: -6rem -1rem; } | |
| 51 | −._icon-sass:before { background-position: -7rem -1rem; } | |
| 52 | −._icon-less:before { background-position: -8rem -1rem; } | |
| 53 | −._icon-angularjs:before { background-position: -9rem -1rem; } | |
| 54 | −._icon-coffeescript:before { background-position: 0 -2rem; @extend %darkIconFix !optional; } | |
| 55 | −._icon-ember:before { background-position: -1rem -2rem; } | |
| 56 | −._icon-yarn:before { background-position: -2rem -2rem; } | |
| 57 | −._icon-immutable:before { background-position: -3rem -2rem; @extend %darkIconFix !optional; } | |
| 58 | −._icon-jqueryui:before { background-position: -4rem -2rem; } | |
| 59 | −._icon-jquerymobile:before { background-position: -5rem -2rem; } | |
| 60 | −._icon-lodash:before { background-position: -6rem -2rem; } | |
| 61 | −._icon-php:before { background-position: -7rem -2rem; } | |
| 62 | −._icon-ruby:before, | |
| 63 | −._icon-minitest:before { background-position: -8rem -2rem; } | |
| 64 | −._icon-rails:before { background-position: -9rem -2rem; } | |
| 65 | −._icon-python:before, | |
| 66 | −._icon-python2:before { background-position: 0 -3rem; } | |
| 67 | −._icon-git:before { background-position: -1rem -3rem; } | |
| 68 | −._icon-redis:before { background-position: -2rem -3rem; } | |
| 69 | −._icon-postgresql:before { background-position: -3rem -3rem; } | |
| 70 | −._icon-d3:before { background-position: -4rem -3rem; } | |
| 71 | −._icon-knockout:before { background-position: -5rem -3rem; } | |
| 72 | −._icon-moment:before { background-position: -6rem -3rem; @extend %darkIconFix !optional; } | |
| 73 | −._icon-c:before { background-position: -7rem -3rem; } | |
| 74 | −._icon-statsmodels:before { background-position: -8rem -3rem; } | |
| 75 | −._icon-yii:before, | |
| 76 | −._icon-yii1:before { background-position: -9rem -3rem; } | |
| 77 | −._icon-cpp:before { background-position: 0 -4rem; } | |
| 78 | −._icon-go:before { background-position: -1rem -4rem; } | |
| 79 | −._icon-express:before { background-position: -2rem -4rem; } | |
| 80 | −._icon-grunt:before { background-position: -3rem -4rem; } | |
| 81 | −._icon-rust:before { background-position: -4rem -4rem; @extend %darkIconFix !optional; } | |
| 82 | −._icon-laravel:before { background-position: -5rem -4rem; } | |
| 83 | −._icon-haskell:before { background-position: -6rem -4rem; } | |
| 84 | −._icon-requirejs:before { background-position: -7rem -4rem; } | |
| 85 | −._icon-chai:before { background-position: -8rem -4rem; } | |
| 86 | −._icon-sinon:before { background-position: -9rem -4rem; } | |
| 87 | −._icon-cordova:before { background-position: 0 -5rem; } | |
| 88 | −._icon-markdown:before { background-position: -1rem -5rem; @extend %darkIconFix !optional; } | |
| 89 | −._icon-django:before { background-position: -2rem -5rem; } | |
| 90 | −._icon-xslt_xpath:before { background-position: -3rem -5rem; } | |
| 91 | −._icon-nginx:before, | |
| 92 | −._icon-nginx_lua_module:before { background-position: -4rem -5rem; } | |
| 93 | −._icon-svg:before { background-position: -5rem -5rem; } | |
| 94 | −._icon-marionette:before { background-position: -6rem -5rem; } | |
| 95 | −._icon-mongoose:before { background-position: -7rem -5rem; } | |
| 96 | −._icon-phpunit:before { background-position: -8rem -5rem; } | |
| 97 | −._icon-nokogiri:before { background-position: -9rem -5rem; @extend %darkIconFix !optional; } | |
| 98 | −._icon-rethinkdb:before { background-position: 0 -6rem; } | |
| 99 | −._icon-react:before { background-position: -1rem -6rem; } | |
| 100 | −._icon-socketio:before { background-position: -2rem -6rem; } | |
| 101 | −._icon-modernizr:before { background-position: -3rem -6rem; } | |
| 102 | −._icon-bower:before { background-position: -4rem -6rem; } | |
| 103 | −._icon-fish:before { background-position: -5rem -6rem; @extend %darkIconFix !optional; } | |
| 104 | −._icon-scikit_image:before { background-position: -6rem -6rem; } | |
| 105 | −._icon-twig:before { background-position: -7rem -6rem; } | |
| 106 | −._icon-pandas:before { background-position: -8rem -6rem; } | |
| 107 | −._icon-scikit_learn:before { background-position: -9rem -6rem; } | |
| 108 | −._icon-bottle:before { background-position: 0 -7rem; } | |
| 109 | −._icon-docker:before { background-position: -1rem -7rem; } | |
| 110 | −._icon-cakephp:before { background-position: -2rem -7rem; } | |
| 111 | −._icon-lua:before { background-position: -3rem -7rem; @extend %darkIconFix !optional; } | |
| 112 | −._icon-clojure:before { background-position: -4rem -7rem; } | |
| 113 | −._icon-symfony:before { background-position: -5rem -7rem; } | |
| 114 | −._icon-mocha:before { background-position: -6rem -7rem; } | |
| 115 | −._icon-meteor:before { background-position: -7rem -7rem; @extend %darkIconFix !optional; } | |
| 116 | −._icon-npm:before { background-position: -8rem -7rem; } | |
| 117 | −._icon-apache_http_server:before { background-position: -9rem -7rem; } | |
| 118 | −._icon-drupal:before { background-position: 0 -8rem; } | |
| 119 | −._icon-webpack:before { background-position: -1rem -8rem; } | |
| 120 | −._icon-phaser:before { background-position: -2rem -8rem; } | |
| 121 | −._icon-vue:before { background-position: -3rem -8rem; } | |
| 122 | −._icon-opentsdb:before { background-position: -4rem -8rem; } | |
| 123 | −._icon-q:before { background-position: -5rem -8rem; } | |
| 124 | −._icon-crystal:before { background-position: -6rem -8rem; @extend %darkIconFix !optional; } | |
| 125 | −._icon-julia:before { background-position: -7rem -8rem; @extend %darkIconFix !optional; } | |
| 126 | −._icon-redux:before { background-position: -8rem -8rem; @extend %darkIconFix !optional; } | |
| 127 | −._icon-bootstrap:before { background-position: -9rem -8rem; } | |
| 128 | −._icon-react_native:before { background-position: 0 -9rem; } | |
| 129 | −._icon-phalcon:before { background-position: -1rem -9rem; } | |
| 130 | −._icon-matplotlib:before { background-position: -2rem -9rem; } | |
| 131 | −._icon-cmake:before { background-position: -3rem -9rem; } | |
| 132 | −._icon-elixir:before { background-position: -4rem -9rem; @extend %darkIconFix !optional; } | |
| 133 | −._icon-vagrant:before { background-position: -5rem -9rem; } | |
| 134 | −._icon-dojo:before { background-position: -6rem -9rem; } | |
| 135 | −._icon-flow:before { background-position: -7rem -9rem; } | |
| 136 | −._icon-relay:before { background-position: -8rem -9rem; } | |
| 137 | −._icon-phoenix:before { background-position: -9rem -9rem; } | |
| 138 | − | |
| 139 | −._icon-tcl_tk:before { background-position: 0 0; @extend %doc-icon-2; } | |
| 140 | −._icon-erlang:before { background-position: -1rem 0; @extend %doc-icon-2; } | |
| 141 | −._icon-chef:before { background-position: -2rem 0; @extend %doc-icon-2; } | |
| 142 | −._icon-ramda:before { background-position: -3rem 0; @extend %doc-icon-2; @extend %darkIconFix !optional; } | |
| 143 | −._icon-codeigniter:before { background-position: -4rem 0; @extend %doc-icon-2; @extend %darkIconFix !optional; } | |
| 144 | −._icon-influxdata:before { background-position: -5rem 0; @extend %doc-icon-2; @extend %darkIconFix !optional; } | |
| 145 | −._icon-tensorflow:before { background-position: -6rem 0; @extend %doc-icon-2; } | |
| 146 | −._icon-haxe:before { background-position: -7rem 0; @extend %doc-icon-2; } | |
| 147 | −._icon-ansible:before { background-position: -8rem 0; @extend %doc-icon-2; @extend %darkIconFix !optional; } | |
| 148 | −._icon-typescript:before { background-position: -9rem 0; @extend %doc-icon-2; } | |
| 149 | −._icon-browser_support_tables:before { background-position: 0rem -1rem; @extend %doc-icon-2; } | |
| 150 | −._icon-gnu_fortran:before { background-position: -1rem -1rem; @extend %doc-icon-2; } | |
| 151 | −._icon-gcc:before { background-position: -2rem -1rem; @extend %doc-icon-2; } | |
| 152 | −._icon-perl:before { background-position: -3rem -1rem; @extend %doc-icon-2; } | |
| 153 | −._icon-apache_pig:before { background-position: -4rem -1rem; @extend %doc-icon-2; } | |
| 154 | −._icon-numpy:before { background-position: -5rem -1rem; @extend %doc-icon-2; } | |
| 155 | −._icon-kotlin:before { background-position: -6rem -1rem; @extend %doc-icon-2; } | |
| 156 | −._icon-padrino:before { background-position: -7rem -1rem; @extend %doc-icon-2; } | |
| 157 | −._icon-angular:before { background-position: -8rem -1rem; @extend %doc-icon-2; } | |
| 158 | −._icon-love:before { background-position: -9rem -1rem; @extend %doc-icon-2; } | |
| 159 | −._icon-jasmine:before { background-position: 0 -2rem; @extend %doc-icon-2; } | |
| 160 | −._icon-pug:before { background-position: -1rem -2rem; @extend %doc-icon-2; } | |
| 161 | −._icon-electron:before { background-position: -2rem -2rem; @extend %doc-icon-2; } | |
| 162 | −._icon-falcon:before { background-position: -3rem -2rem; @extend %doc-icon-2; } | |
| 163 | −._icon-godot:before { background-position: -4rem -2rem; @extend %doc-icon-2; } | |
| 164 | −._icon-nim:before { background-position: -5rem -2rem; @extend %doc-icon-2; @extend %darkIconFix !optional; } | |
| 165 | −._icon-vulkan:before { background-position: -6rem -2rem; @extend %doc-icon-2; @extend %darkIconFix !optional; } | |
| 166 | −._icon-d:before { background-position: -7rem -2rem; @extend %doc-icon-2; } | |
| @@ -0,0 +1,43 @@ | ||
| 1 | +<% manifest = JSON.parse(File.read('assets/images/sprites/docs.json')) %> | |
| 2 | + | |
| 3 | +%svg-icon { | |
| 4 | + display: inline-block; | |
| 5 | + vertical-align: top; | |
| 6 | + width: 1rem; | |
| 7 | + height: 1rem; | |
| 8 | + pointer-events: none; | |
| 9 | + fill: currentColor; | |
| 10 | +} | |
| 11 | + | |
| 12 | +%doc-icon { | |
| 13 | + content: ''; | |
| 14 | + display: block; | |
| 15 | + width: 1rem; | |
| 16 | + height: 1rem; | |
| 17 | + background-image: image-url('sprites/docs.png'); | |
| 18 | + background-size: <%= manifest['icons_per_row'] %>rem <%= manifest['icons_per_row'] %>rem; | |
| 19 | +} | |
| 20 | + | |
| 21 | +@media (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) { | |
| 22 | + %doc-icon { background-image: image-url('sprites/docs@2x.png'); } | |
| 23 | +} | |
| 24 | + | |
| 25 | +html._theme-dark { | |
| 26 | + %darkIconFix { | |
| 27 | + filter: invert(100%) grayscale(100%); | |
| 28 | + -webkit-filter: invert(100%) grayscale(100%); | |
| 29 | + } | |
| 30 | +} | |
| 31 | + | |
| 32 | +<%= | |
| 33 | + items = [] | |
| 34 | + | |
| 35 | + manifest['items'].each do |item| | |
| 36 | + rules = [] | |
| 37 | + rules << "background-position: -#{item['col']}rem -#{item['row']}rem;" | |
| 38 | + rules << "@extend %darkIconFix !optional;" if item['dark_icon_fix'] | |
| 39 | + items << "._icon-#{item['type']}:before { #{rules.join(' ')} }" | |
| 40 | + end | |
| 41 | + | |
| 42 | + items.join('') | |
| 43 | + %> | |
| @@ -0,0 +1,15 @@ | ||
| 1 | +// | |
| 2 | +// Mixins | |
| 3 | +// | |
| 4 | + | |
| 5 | +@mixin print { | |
| 6 | + @media print { | |
| 7 | + @content; | |
| 8 | + } | |
| 9 | +} | |
| 10 | + | |
| 11 | +@mixin mobile { | |
| 12 | + @media (max-width: 800px) { | |
| 13 | + @content; | |
| 14 | + } | |
| 15 | +} | |
| @@ -0,0 +1,57 @@ | ||
| 1 | +@include print { | |
| 2 | + ._header, ._sidebar, ._path, ._notif, ._toc, ._pre-clip, ._notice, ._links { | |
| 3 | + display: none !important; | |
| 4 | + } | |
| 5 | + | |
| 6 | + body, ._app, ._container, ._content { | |
| 7 | + margin: 0; | |
| 8 | + padding: 0; | |
| 9 | + height: initial; | |
| 10 | + background: none; | |
| 11 | + | |
| 12 | + &:after { | |
| 13 | + content: ''; | |
| 14 | + clear: both; | |
| 15 | + } | |
| 16 | + } | |
| 17 | + | |
| 18 | + ::-webkit-scrollbar { | |
| 19 | + display: none; | |
| 20 | + } | |
| 21 | + | |
| 22 | + %external-link:after { | |
| 23 | + display: none; | |
| 24 | + } | |
| 25 | + | |
| 26 | + ._attribution-p { | |
| 27 | + background: none; | |
| 28 | + border: 2px solid var(--boxBorder); | |
| 29 | + } | |
| 30 | + | |
| 31 | + ._attribution:last-child:after { | |
| 32 | + content: 'Exported from DevDocs \2014 https://devdocs.io'; | |
| 33 | + display: block; | |
| 34 | + margin-top: 1rem; | |
| 35 | + font-weight: var(--bolderFontWeight); | |
| 36 | + } | |
| 37 | + | |
| 38 | + ._attribution { | |
| 39 | + page-break-inside: avoid; | |
| 40 | + } | |
| 41 | + | |
| 42 | + h1, h2, h3, h4, h5, h6 { | |
| 43 | + page-break-inside: avoid; | |
| 44 | + page-break-after: avoid; | |
| 45 | + } | |
| 46 | + | |
| 47 | + pre { | |
| 48 | + page-break-before: avoid; | |
| 49 | + orphans: 5; | |
| 50 | + widows: 5; | |
| 51 | + } | |
| 52 | + | |
| 53 | + p { | |
| 54 | + orphans: 2; | |
| 55 | + widows: 2; | |
| 56 | + } | |
| 57 | +} | |
| @@ -1,92 +1,76 @@ | ||
| 1 | −$baseFont: -apple-system, BlinkMacSystemFont, 'San Francisco', 'Segoe UI', Roboto, Ubuntu, 'Helvetica Neue', Arial, sans-serif; | |
| 2 | −$monoFont: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; | |
| 3 | −$boldFontWeight: 500; | |
| 4 | −$bolderFontWeight: 600; | |
| 1 | +html._theme-dark { | |
| 2 | + --absolute: white; | |
| 5 | 3 | |
| 6 | −$style: 'dark'; | |
| 4 | + --documentBackground: #222; | |
| 5 | + --contentBackground: #33373a; | |
| 7 | 6 | |
| 8 | −$maxWidth: 80rem; | |
| 9 | −$headerHeight: 3rem; | |
| 10 | −$sidebarWidth: 20rem; | |
| 11 | −$sidebarMediumWidth: 16rem; | |
| 7 | + --textColor: #cbd0d0; | |
| 8 | + --textColorLight: #9da5ad; | |
| 9 | + --textColorLighter: #77787a; | |
| 12 | 10 | |
| 13 | −$documentBackground: #222; | |
| 14 | −$contentBackground: #33373a; | |
| 11 | + --externalsBackground: #fff; | |
| 15 | 12 | |
| 16 | −$textColor: #cbd0d0; | |
| 17 | −$textColorLight: #9da5ad; | |
| 18 | −$textColorLighter: #77787a; | |
| 19 | −$textColorRed: #f44336; | |
| 13 | + --inputFocusBorder: transparent; | |
| 20 | 14 | |
| 21 | −$inputFocusBorder: false; | |
| 15 | + --focusBackground: #3f4042; | |
| 16 | + --focusBorder: #000; | |
| 17 | + --focusText: #f7f2f2; | |
| 22 | 18 | |
| 23 | −$focusBackground: #3f4042; | |
| 24 | −$focusBorder: #000; | |
| 25 | −$focusText: #f7f2f2; | |
| 19 | + --loadingText: #5d6164; | |
| 26 | 20 | |
| 27 | −$loadingText: #5d6164; | |
| 28 | −$splashText: $loadingText; | |
| 21 | + --selectionBackground: #007acc; | |
| 22 | + --selectionBorder: #000; | |
| 29 | 23 | |
| 30 | −$selectionBackground: #007acc; | |
| 31 | −$selectionBorder: #000; | |
| 32 | −$selectionText: #fff; | |
| 24 | + --highlightBackground: #64675f; | |
| 33 | 25 | |
| 34 | −$highlightBackground: #64675f; | |
| 26 | + --linkColor: var(--textColor); | |
| 27 | + --linkColorHover: white; | |
| 28 | + --linkTextDecoration: underline; | |
| 35 | 29 | |
| 36 | −$linkColor: $textColor; | |
| 37 | −$linkColorHover: white; | |
| 38 | −$linkTextDecoration: underline; | |
| 30 | + --headerBackground: #1c1c1c; | |
| 31 | + --headerBorder: #000; | |
| 32 | + --searchTagBackground: #{darken(#1c1c1c, 5%)}; | |
| 33 | + --searchBorder: #{darken(#000, 2%)}; | |
| 39 | 34 | |
| 40 | −$headerBackground: #1c1c1c; | |
| 41 | −$headerBorder: #000; | |
| 35 | + --sidebarBackground: #24282a; | |
| 36 | + --transparentSidebarBackground: #{rgba(#24282a, 0)}; | |
| 37 | + --sidebarBorder: #000; | |
| 42 | 38 | |
| 43 | −$sidebarBackground: #24282a; | |
| 44 | −$sidebarBorder: #000; | |
| 39 | + --scrollbarColor: #6c6c6f; | |
| 40 | + --scrollbarColorHover: #949697; | |
| 45 | 41 | |
| 46 | −$scrollbarColor: #6c6c6f; | |
| 47 | −$scrollbarColorHover: #949697; | |
| 42 | + --pathBackground: var(--headerBackground); | |
| 43 | + --pathBorder: var(--headerBorder); | |
| 48 | 44 | |
| 49 | −$pathBackground: $headerBackground; | |
| 50 | −$pathBorder: $headerBorder; | |
| 45 | + --noticeBackground: var(--sidebarBackground); | |
| 46 | + --noticeBorder: var(--sidebarBorder); | |
| 51 | 47 | |
| 52 | −$noticeBackground: $sidebarBackground; | |
| 53 | −$noticeBorder: $sidebarBorder; | |
| 48 | + --boxBackground: var(--sidebarBackground); | |
| 49 | + --boxBorder: var(--headerBorder); | |
| 50 | + --boxBorderLight: var(--headerBorder); | |
| 51 | + --boxHeaderColor: #dbe4e4; | |
| 52 | + --boxHeaderBackground: var(--sidebarBackground); | |
| 54 | 53 | |
| 55 | −$boxBackground: $sidebarBackground; | |
| 56 | −$boxBorder: $headerBorder; | |
| 57 | −$boxBorderLight: $headerBorder; | |
| 58 | −$boxHeaderColor: #dbe4e4; | |
| 59 | −$boxHeaderBackground: $sidebarBackground; | |
| 54 | + --noteBackground: #45474b; | |
| 55 | + --noteBorder: #000; | |
| 60 | 56 | |
| 61 | −$noteBackground: #45474b; | |
| 62 | −$noteBorder: #000; | |
| 57 | + --noteGreenBackground: #284a2a; | |
| 58 | + --noteGreenBorder: #000; | |
| 63 | 59 | |
| 64 | −$noteGreenBackground: #284a2a; | |
| 65 | −$noteGreenBorder: #000; | |
| 60 | + --noteBlueBackground: #2a4151; | |
| 61 | + --noteBlueBorder: #000; | |
| 66 | 62 | |
| 67 | −$noteBlueBackground: #2a4151; | |
| 68 | −$noteBlueBorder: #000; | |
| 63 | + --noteOrangeBackground: #563322; | |
| 64 | + --noteOrangeBorder: #000; | |
| 69 | 65 | |
| 70 | −$noteOrangeBackground: #563322; | |
| 71 | −$noteOrangeBorder: #000; | |
| 66 | + --noteRedBackground: #603033; | |
| 67 | + --noteRedBorder: #000; | |
| 72 | 68 | |
| 73 | −$noteRedBackground: #603033; | |
| 74 | −$noteRedBorder: #000; | |
| 69 | + --labelBackground: var(--boxBackground); | |
| 75 | 70 | |
| 76 | −$labelBackground: $boxBackground; | |
| 71 | + --notifBackground: #{rgba(#555, .95)}; | |
| 72 | + --notifBorder: 1px solid #000; | |
| 77 | 73 | |
| 78 | −$notifBackground: rgba(#555, .95); | |
| 79 | −$notifBorder: 1px solid #000; | |
| 80 | −$notifColor: #fff; | |
| 81 | −$notifColorLight: #ccc; | |
| 82 | − | |
| 83 | −$tipBackground: $notifBackground; | |
| 84 | −$tipBorder: $notifBorder; | |
| 85 | − | |
| 86 | −$mediumScreen: '(max-width: 800px)'; | |
| 87 | − | |
| 88 | −$contentZ: 1; | |
| 89 | −$sidebarZ: 2; | |
| 90 | −$headerZ: 3; | |
| 91 | −$noticeZ: 4; | |
| 92 | −$hoverZ: 5; | |
| 74 | + --tipBackground: var(--notifBackground); | |
| 75 | + --tipBorder: var(--notifBorder); | |
| 76 | +} | |
| @@ -0,0 +1,76 @@ | ||
| 1 | +html._theme-default { | |
| 2 | + --absolute: black; | |
| 3 | + | |
| 4 | + --documentBackground: #f3f3f3; | |
| 5 | + --contentBackground: #fff; | |
| 6 | + | |
| 7 | + --textColor: #333; | |
| 8 | + --textColorLight: #666; | |
| 9 | + --textColorLighter: #888; | |
| 10 | + | |
| 11 | + --externalsBackground: #fff; | |
| 12 | + | |
| 13 | + --inputFocusBorder: #35b5f4; | |
| 14 | + | |
| 15 | + --focusBackground: #e5e5e5; | |
| 16 | + --focusBorder: #d4d4d4; | |
| 17 | + --focusText: #000; | |
| 18 | + | |
| 19 | + --loadingText: #ccc; | |
| 20 | + | |
| 21 | + --selectionBackground: #398df0; | |
| 22 | + --selectionBorder: #196fc2; | |
| 23 | + | |
| 24 | + --highlightBackground: #fffdcd; | |
| 25 | + | |
| 26 | + --linkColor: #3377c0; | |
| 27 | + --linkColorHover: #2f6cb6; | |
| 28 | + --linkTextDecoration: none; | |
| 29 | + | |
| 30 | + --headerBackground: #eee; | |
| 31 | + --headerBorder: #d7d7d7; | |
| 32 | + --searchTagBackground: #{darken(#eee, 5%)}; | |
| 33 | + --searchBorder: #{darken(#d7d7d7, 2%)}; | |
| 34 | + | |
| 35 | + --sidebarBackground: #f9f9f9; | |
| 36 | + --transparentSidebarBackground: #{rgba(#f9f9f9, 0)}; | |
| 37 | + --sidebarBorder: #e1e1e1; | |
| 38 | + | |
| 39 | + --scrollbarColor: #ccc; | |
| 40 | + --scrollbarColorHover: #999; | |
| 41 | + | |
| 42 | + --pathBackground: var(--sidebarBackground); | |
| 43 | + --pathBorder: var(--sidebarBorder); | |
| 44 | + | |
| 45 | + --noticeBackground: #faf9e2; | |
| 46 | + --noticeBorder: #e2e2c1; | |
| 47 | + | |
| 48 | + --boxBackground: #fafafa; | |
| 49 | + --boxBorder: #d8d8d8; | |
| 50 | + --boxBorderLight: #e5e5e5; | |
| 51 | + --boxHeaderColor: var(--textColor); | |
| 52 | + --boxHeaderBackground: #f5f5f5; | |
| 53 | + | |
| 54 | + --noteBackground: #f8f8dd; | |
| 55 | + --noteBorder: #d3d952; | |
| 56 | + | |
| 57 | + --noteGreenBackground: #e7f8e1; | |
| 58 | + --noteGreenBorder: #89da70; | |
| 59 | + | |
| 60 | + --noteBlueBackground: #d4f3fd; | |
| 61 | + --noteBlueBorder: #94bbeb; | |
| 62 | + | |
| 63 | + --noteOrangeBackground: #fbe6d1; | |
| 64 | + --noteOrangeBorder: #ec8b01; | |
| 65 | + | |
| 66 | + --noteRedBackground: #fed5d3; | |
| 67 | + --noteRedBorder: #dc7874; | |
| 68 | + | |
| 69 | + --labelBackground: #f4f4f4; | |
| 70 | + | |
| 71 | + --notifBackground: #{rgba(#333, .85)}; | |
| 72 | + --notifBorder: none; | |
| 73 | + | |
| 74 | + --tipBackground: #{rgba(#fffdcd, .95)}; | |
| 75 | + --tipBorder: 1px solid #e7dca9; | |
| 76 | +} | |
| @@ -1,92 +1,35 @@ | ||
| 1 | +// Variables needed to style the error message for browsers that don't support CSS variables. | |
| 1 | 2 | $baseFont: -apple-system, BlinkMacSystemFont, 'San Francisco', 'Segoe UI', Roboto, Ubuntu, 'Helvetica Neue', Arial, sans-serif; |
| 2 | −$monoFont: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; | |
| 3 | −$boldFontWeight: 500; | |
| 4 | −$bolderFontWeight: 600; | |
| 5 | − | |
| 6 | −$style: 'light'; | |
| 7 | − | |
| 8 | −$maxWidth: 80rem; | |
| 9 | −$headerHeight: 3rem; | |
| 10 | −$sidebarWidth: 20rem; | |
| 11 | −$sidebarMediumWidth: 16rem; | |
| 12 | − | |
| 13 | −$documentBackground: #f3f3f3; | |
| 14 | −$contentBackground: #fff; | |
| 15 | − | |
| 16 | 3 | $textColor: #333; |
| 17 | −$textColorLight: #666; | |
| 18 | −$textColorLighter: #888; | |
| 19 | −$textColorRed: #f44336; | |
| 20 | − | |
| 21 | −$inputFocusBorder: #35b5f4; | |
| 22 | − | |
| 23 | −$focusBackground: #e5e5e5; | |
| 24 | −$focusBorder: #d4d4d4; | |
| 25 | −$focusText: #000; | |
| 26 | − | |
| 27 | −$loadingText: #ccc; | |
| 28 | −$splashText: #ccc; | |
| 29 | − | |
| 30 | −$selectionBackground: #398df0; | |
| 31 | −$selectionBorder: #196fc2; | |
| 32 | −$selectionText: #fff; | |
| 33 | − | |
| 34 | −$highlightBackground: #fffdcd; | |
| 35 | − | |
| 36 | −$linkColor: #3377c0; | |
| 37 | −$linkColorHover: #2f6cb6; | |
| 38 | −$linkTextDecoration: none; | |
| 39 | − | |
| 40 | −$headerBackground: #eee; | |
| 41 | −$headerBorder: #d7d7d7; | |
| 42 | − | |
| 43 | −$sidebarBackground: #f9f9f9; | |
| 44 | −$sidebarBorder: #e1e1e1; | |
| 45 | − | |
| 46 | −$scrollbarColor: #ccc; | |
| 47 | −$scrollbarColorHover: #999; | |
| 48 | − | |
| 49 | −$pathBackground: $sidebarBackground; | |
| 50 | −$pathBorder: $sidebarBorder; | |
| 51 | − | |
| 52 | −$noticeBackground: #faf9e2; | |
| 53 | −$noticeBorder: #e2e2c1; | |
| 54 | − | |
| 55 | −$boxBackground: #fafafa; | |
| 56 | −$boxBorder: #d8d8d8; | |
| 57 | −$boxBorderLight: #e5e5e5; | |
| 58 | −$boxHeaderColor: $textColor; | |
| 59 | −$boxHeaderBackground: #f5f5f5; | |
| 60 | − | |
| 61 | −$noteBackground: #f8f8dd; | |
| 62 | −$noteBorder: #d3d952; | |
| 63 | − | |
| 64 | −$noteGreenBackground: #e7f8e1; | |
| 65 | −$noteGreenBorder: #89da70; | |
| 66 | 4 | |
| 67 | −$noteBlueBackground: #d4f3fd; | |
| 68 | −$noteBlueBorder: #94bbeb; | |
| 5 | +html { | |
| 6 | + --baseFont: #{$baseFont}; | |
| 7 | + --monoFont: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace; | |
| 8 | + --boldFontWeight: 500; | |
| 9 | + --bolderFontWeight: 600; | |
| 69 | 10 | |
| 70 | −$noteOrangeBackground: #fbe6d1; | |
| 71 | −$noteOrangeBorder: #ec8b01; | |
| 11 | + --textColorRed: #f44336; | |
| 72 | 12 | |
| 73 | −$noteRedBackground: #fed5d3; | |
| 74 | −$noteRedBorder: #dc7874; | |
| 13 | + --splashText: var(--loadingText); | |
| 75 | 14 | |
| 76 | −$labelBackground: #f4f4f4; | |
| 15 | + --selectionText: #fff; | |
| 16 | + --transparentSelectionText: rgba(255, 255, 255, 0.9); | |
| 77 | 17 | |
| 78 | −$notifBackground: rgba(#333, .85); | |
| 79 | −$notifBorder: none; | |
| 80 | −$notifColor: #fff; | |
| 81 | −$notifColorLight: #ccc; | |
| 18 | + --notifColor: #fff; | |
| 19 | + --notifColorLight: #ccc; | |
| 82 | 20 | |
| 83 | −$tipBackground: rgba(#fffdcd, .95); | |
| 84 | −$tipBorder: 1px solid #e7dca9; | |
| 21 | + --maxWidth: 80rem; | |
| 22 | + --headerHeight: 3rem; | |
| 23 | + --sidebarWidth: 20rem; | |
| 24 | + --sidebarMediumWidth: 16rem; | |
| 85 | 25 | |
| 86 | −$mediumScreen: '(max-width: 800px)'; | |
| 26 | + --focusBackground: #e5e5e5; | |
| 27 | + --focusBorder: #d4d4d4; | |
| 28 | + --focusText: #000; | |
| 87 | 29 | |
| 88 | −$contentZ: 1; | |
| 89 | −$sidebarZ: 2; | |
| 90 | −$headerZ: 3; | |
| 91 | −$noticeZ: 4; | |
| 92 | −$hoverZ: 5; | |
| 30 | + --contentZ: 1; | |
| 31 | + --sidebarZ: 2; | |
| 32 | + --headerZ: 3; | |
| 33 | + --noticeZ: 4; | |
| 34 | + --hoverZ: 5; | |
| 35 | +} | |
| @@ -19,4 +19,6 @@ · 18 unmodified lines | ||
| 19 | 19 | font-style: italic; |
| 20 | 20 | text-align: right; |
| 21 | 21 | } |
| 22 | + | |
| 23 | + td h3 { margin: 0 !important; } | |
| 22 | 24 | } |
| @@ -8,7 +8,7 @@ · 7 unmodified lines | ||
| 8 | 8 | .nav-index-section { |
| 9 | 9 | margin: 1.5em 0 1em -2em; |
| 10 | 10 | list-style: none; |
| 11 | − font-weight: $boldFontWeight; | |
| 11 | + font-weight: var(--boldFontWeight); | |
| 12 | 12 | text-transform: capitalize; |
| 13 | 13 | } |
| 14 | 14 | |
| @@ -3,13 +3,13 @@ · 2 unmodified lines | ||
| 3 | 3 | |
| 4 | 4 | h3 > .type-signature { |
| 5 | 5 | float: right; |
| 6 | − color: $textColorLight; | |
| 6 | + color: var(--textColorLight); | |
| 7 | 7 | } |
| 8 | 8 | |
| 9 | 9 | h3 > .signature-attributes { |
| 10 | 10 | font-size: .75rem; |
| 11 | 11 | font-weight: normal; |
| 12 | 12 | font-style: italic; |
| 13 | − color: $textColorLighter; | |
| 13 | + color: var(--textColorLighter); | |
| 14 | 14 | } |
| 15 | 15 | } |
| @@ -0,0 +1,12 @@ | ||
| 1 | +._bash { | |
| 2 | + dl > dt > code, | |
| 3 | + dl > dt > kbd { | |
| 4 | + @extend %block-label, %label-blue; | |
| 5 | + } | |
| 6 | + | |
| 7 | + th[align=left] { | |
| 8 | + border-left: 1px solid var(--boxBorder); | |
| 9 | + } | |
| 10 | + | |
| 11 | + code { @extend %label; } | |
| 12 | +} | |
| @@ -4,7 +4,7 @@ · 3 unmodified lines | ||
| 4 | 4 | h4 > code, h5 > code, strong > code { @extend %label; } |
| 5 | 5 | |
| 6 | 6 | h2 > small { |
| 7 | − color: $textColorLight; | |
| 7 | + color: var(--textColorLight); | |
| 8 | 8 | float: right; |
| 9 | 9 | } |
| 10 | 10 | |
| @@ -23,7 +23,7 @@ · 22 unmodified lines | ||
| 23 | 23 | |
| 24 | 24 | .t-spar { // language/switch, language/for, etc. |
| 25 | 25 | font-style: italic; |
| 26 | − color: $textColorLight; | |
| 26 | + color: var(--textColorLight); | |
| 27 | 27 | } |
| 28 | 28 | .t-sdsc-nopad dl, .t-sdsc-nopad dd { margin: 0; } |
| 29 | 29 | |
| @@ -46,6 +46,8 @@ · 16 unmodified lines | ||
| 46 | 46 | |
| 47 | 47 | + div { margin-left: .5em; } |
| 48 | 48 | } |
| 49 | + | |
| 50 | + > table { margin: 0; } | |
| 49 | 51 | } |
| 50 | 52 | |
| 51 | 53 | .t-dcl-rev-aux > td:empty { padding: 0; } |
| @@ -55,7 +57,7 @@ · 3 unmodified lines | ||
| 55 | 57 | margin: 1rem 0; |
| 56 | 58 | padding: .375rem; |
| 57 | 59 | font-size: .75rem; |
| 58 | − border: 1px solid $boxBorder; | |
| 60 | + border: 1px solid var(--boxBorder); | |
| 59 | 61 | border-radius: 2px; |
| 60 | 62 | } |
| 61 | 63 | |
| @@ -5,6 +5,6 @@ · 4 unmodified lines | ||
| 5 | 5 | .type { |
| 6 | 6 | float: right; |
| 7 | 7 | font-size: .9em; |
| 8 | − color: $textColorLight; | |
| 8 | + color: var(--textColorLight); | |
| 9 | 9 | } |
| 10 | 10 | } |
| @@ -2,10 +2,10 @@ · 1 unmodified line | ||
| 2 | 2 | h2 { @extend %block-heading; } |
| 3 | 3 | h3, .d_decl { @extend %block-label, %label-blue; } |
| 4 | 4 | .d_decl { @extend %code; } |
| 5 | − .d_decl > small { color: $textColorLight; } | |
| 6 | − .d_decl > strong { font-weight: $bolderFontWeight; } | |
| 5 | + .d_decl > small { color: var(--textColorLight); } | |
| 6 | + .d_decl > strong { font-weight: var(--bolderFontWeight); } | |
| 7 | 7 | |
| 8 | 8 | p > code, li > code, td > code, dd > code { @extend %label; } |
| 9 | 9 | |
| 10 | − span.red { color: $textColorRed; } | |
| 10 | + span.red { color: var(--textColorRed); } | |
| 11 | 11 | } |
| @@ -0,0 +1,12 @@ | ||
| 1 | +._dart { | |
| 2 | + @extend %simple; | |
| 3 | + | |
| 4 | + dl:not(.dl-horizontal) dt, .multi-line-signature { | |
| 5 | + @extend %block-label; | |
| 6 | + | |
| 7 | + .features { | |
| 8 | + float: right; | |
| 9 | + color: var(--textColorLight); | |
| 10 | + } | |
| 11 | + } | |
| 12 | +} | |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | ._dojo { |
| 2 | 2 | @extend %simple; |
| 3 | 3 | |
| 4 | − .jsdoc-inheritance { color: $textColorLight; } | |
| 4 | + .jsdoc-inheritance { color: var(--textColorLight); } | |
| 5 | 5 | } |
| @@ -5,7 +5,7 @@ · 4 unmodified lines | ||
| 5 | 5 | |
| 6 | 6 | h3 > .access { |
| 7 | 7 | float: right; |
| 8 | − color: $textColorLight; | |
| 8 | + color: var(--textColorLight); | |
| 9 | 9 | font-weight: normal; |
| 10 | 10 | } |
| 11 | 11 | |
| @@ -15,6 +15,6 @@ · 3 unmodified lines | ||
| 15 | 15 | } |
| 16 | 16 | |
| 17 | 17 | p.github-link { |
| 18 | − color: $textColorLight; | |
| 18 | + color: var(--textColorLight); | |
| 19 | 19 | } |
| 20 | 20 | } |
| @@ -5,5 +5,5 @@ · 4 unmodified lines | ||
| 5 | 5 | code.code { @extend %label; } |
| 6 | 6 | .note { @extend %note; } |
| 7 | 7 | .warning { @extend %note, %note-red; } |
| 8 | − .note .label, .warning .label { font-weight: $boldFontWeight; } | |
| 8 | + .note .label, .warning .label { font-weight: var(--boldFontWeight); } | |
| 9 | 9 | } |
| @@ -0,0 +1,7 @@ | ||
| 1 | +._graphite { | |
| 2 | + @extend %simple; | |
| 3 | + | |
| 4 | + dl > dt { | |
| 5 | + @extend %block-label, %label-blue; | |
| 6 | + } | |
| 7 | +} | |
| @@ -0,0 +1,7 @@ | ||
| 1 | +._jekyll { | |
| 2 | + @extend %simple; | |
| 3 | + | |
| 4 | + .note.info { @extend %note-blue; } | |
| 5 | + .note.warning { @extend %note-red; } | |
| 6 | + .note.unreleased { @extend %note-orange; } | |
| 7 | +} | |
| @@ -32,7 +32,7 @@ · 31 unmodified lines | ||
| 32 | 32 | |
| 33 | 33 | .toc-list { |
| 34 | 34 | margin-top: 0; |
| 35 | − font-weight: $boldFontWeight; | |
| 35 | + font-weight: var(--boldFontWeight); | |
| 36 | 36 | |
| 37 | 37 | > li + li { margin-top: 1em; } |
| 38 | 38 | > li > ul { font-weight: normal; } |
| @@ -48,7 +48,7 @@ · 9 unmodified lines | ||
| 48 | 48 | .returns, |
| 49 | 49 | .option-type { |
| 50 | 50 | float: right; |
| 51 | − font-weight: $boldFontWeight; | |
| 51 | + font-weight: var(--boldFontWeight); | |
| 52 | 52 | margin-left: 1em; |
| 53 | 53 | } |
| 54 | 54 | |
| @@ -12,6 +12,6 @@ · 11 unmodified lines | ||
| 12 | 12 | .smwtable { width: 100%; } |
| 13 | 13 | .smwtable td:nth-last-child(2), .smwtable td:last-child { width: 2.5em; } |
| 14 | 14 | |
| 15 | − .cell-green { background: $noteGreenBackground; } | |
| 16 | − .cell-red { background: $noteRedBackground; } | |
| 15 | + .cell-green { background: var(--noteGreenBackground); } | |
| 16 | + .cell-red { background: var(--noteRedBackground); } | |
| 17 | 17 | } |
| @@ -7,7 +7,7 @@ · 6 unmodified lines | ||
| 7 | 7 | > span { |
| 8 | 8 | display: block; |
| 9 | 9 | font-size: 1rem; |
| 10 | − font-weight: $boldFontWeight; | |
| 10 | + font-weight: var(--boldFontWeight); | |
| 11 | 11 | } |
| 12 | 12 | |
| 13 | 13 | ul, ol { |
| @@ -30,6 +30,7 @@ · 16 unmodified lines | ||
| 30 | 30 | .notice, |
| 31 | 31 | .warning, |
| 32 | 32 | .overheadIndicator, |
| 33 | + .blockIndicator, | |
| 33 | 34 | .syntaxbox, // CSS, JavaScript |
| 34 | 35 | .twopartsyntaxbox, // CSS |
| 35 | 36 | .inheritsbox, // JavaScript |
| @@ -42,7 +43,7 @@ · 6 unmodified lines | ||
| 42 | 43 | > .note { |
| 43 | 44 | em { |
| 44 | 45 | font-style: normal; |
| 45 | − font-weight: $boldFontWeight; | |
| 46 | + font-weight: var(--boldFontWeight); | |
| 46 | 47 | } |
| 47 | 48 | |
| 48 | 49 | > ul { margin: 1em 0; } |
| @@ -93,7 +94,7 @@ · 44 unmodified lines | ||
| 93 | 94 | dt > strong > code, // HTML element attribute |
| 94 | 95 | dl > dt > code { // CSS property value, Javascript function argument |
| 95 | 96 | font-family: inherit; |
| 96 | − font-weight: $boldFontWeight; | |
| 97 | + font-weight: var(--boldFontWeight); | |
| 97 | 98 | font-size: inherit; |
| 98 | 99 | } |
| 99 | 100 | |
| @@ -104,4 +105,28 @@ · 4 unmodified lines | ||
| 104 | 105 | .cleared { clear: both; } // CSS/box-shadow |
| 105 | 106 | |
| 106 | 107 | code > strong { font-weight: normal; } |
| 108 | + | |
| 109 | + // Compatibility tablees | |
| 110 | + | |
| 111 | + .bc-github-link { | |
| 112 | + float: right; | |
| 113 | + font-size: .75rem; | |
| 114 | + } | |
| 115 | + | |
| 116 | + .bc-supports-yes, .bc-supports-yes + dd, .bc-supports-yes + dd + dd { background: var(--noteGreenBackground); } | |
| 117 | + .bc-supports-partial, .bc-supports-partial + dd, .bc-supports-partial + dd + dd { background: var(--noteOrangeBackground); } | |
| 118 | + .bc-supports-no, .bc-supports-no + dd, .bc-supports-no + dd + dd { background: var(--noteRedBackground); } | |
| 119 | + | |
| 120 | + .bc-table { | |
| 121 | + min-width: 100%; | |
| 122 | + | |
| 123 | + dl { | |
| 124 | + margin: .25rem 0 0; | |
| 125 | + padding: .25rem 0 0; | |
| 126 | + font-size: .75rem; | |
| 127 | + border-top: 1px solid var(--boxBorder); | |
| 128 | + } | |
| 129 | + | |
| 130 | + dd { margin: 0; } | |
| 131 | + } | |
| 107 | 132 | } |
| @@ -9,10 +9,10 @@ · 8 unmodified lines | ||
| 9 | 9 | dt > code { @extend %label; } |
| 10 | 10 | |
| 11 | 11 | .api-heading { overflow: hidden; } |
| 12 | − .api-heading > code { font-weight: $boldFontWeight; } | |
| 12 | + .api-heading > code { font-weight: var(--boldFontWeight); } | |
| 13 | 13 | .locus, .src-code { float: right; } |
| 14 | 14 | .locus, .type, .src-code { margin-left: .5em; } |
| 15 | 15 | h2 .subtext-api { margin-top: .25rem; } |
| 16 | 16 | .locus, .subtext-api, .subtext-api > code { font-size: .75rem; } |
| 17 | − .locus, .type { color: $textColorLight; } | |
| 17 | + .locus, .type { color: var(--textColorLight); } | |
| 18 | 18 | } |
| @@ -20,5 +20,8 @@ · 19 unmodified lines | ||
| 20 | 20 | margin: 0 0 1em 1em; |
| 21 | 21 | @extend %label; |
| 22 | 22 | } |
| 23 | + | |
| 24 | + .srclink { float: right; } | |
| 25 | + details > table { margin: 0; } | |
| 23 | 26 | } |
| 24 | 27 | |
| @@ -8,7 +8,7 @@ · 7 unmodified lines | ||
| 8 | 8 | |
| 9 | 9 | .faint.heading { |
| 10 | 10 | font-size: .9em; |
| 11 | − color: $textColorLight; | |
| 11 | + color: var(--textColorLight); | |
| 12 | 12 | } |
| 13 | 13 | |
| 14 | 14 | .youtube-video iframe { width: 420px; height: 315px; } |
| @@ -3,6 +3,6 @@ · 2 unmodified lines | ||
| 3 | 3 | |
| 4 | 4 | h3 > small { |
| 5 | 5 | float: right; |
| 6 | − color: $textColorLight; | |
| 6 | + color: var(--textColorLight); | |
| 7 | 7 | } |
| 8 | 8 | } |
| @@ -13,7 +13,7 @@ · 12 unmodified lines | ||
| 13 | 13 | |
| 14 | 14 | .verinfo { |
| 15 | 15 | float: right; |
| 16 | − font-weight: $boldFontWeight; | |
| 16 | + font-weight: var(--boldFontWeight); | |
| 17 | 17 | } |
| 18 | 18 | |
| 19 | 19 | .classsynopsis, |
| @@ -21,7 +21,7 @@ · 1 unmodified line | ||
| 21 | 21 | .description > .methodsynopsis, |
| 22 | 22 | .description > .fieldsynopsis { @extend %note, %note-blue; } |
| 23 | 23 | |
| 24 | − .classsynopsisinfo_comment { color: $textColorLight; } | |
| 24 | + .classsynopsisinfo_comment { color: var(--textColorLight); } | |
| 25 | 25 | |
| 26 | 26 | .classsynopsisinfo_comment, |
| 27 | 27 | .classsynopsis > .constructorsynopsis, |
| @@ -1,19 +1,24 @@ | ||
| 1 | 1 | ._postgres { |
| 2 | 2 | padding-left: 1rem; |
| 3 | 3 | |
| 4 | − h1, h1 ~ p, h1 ~ pre, h1 ~ blockquote, h2, .NAVFOOTER { margin-left: -1rem; } | |
| 4 | + h1, h1 ~ p, h1 ~ pre, h1 ~ ul, h1 ~ blockquote, h2, .navfooter { margin-left: -1rem; } | |
| 5 | 5 | h2 { @extend %block-heading; } |
| 6 | 6 | |
| 7 | − .VARIABLELIST dt { @extend %block-label, %label-blue; } | |
| 7 | + .variablelist dt { @extend %block-label, %label-blue; } | |
| 8 | 8 | |
| 9 | − blockquote.NOTE, blockquote.IMPORTANT, blockquote.TIP, blockquote.CAUTION { @extend %note; } | |
| 10 | − blockquote.TIP { @extend %note-green; } | |
| 11 | − blockquote.CAUTION { @extend %note-orange; } | |
| 9 | + blockquote.note, blockquote.important, blockquote.tip, blockquote.caution { @extend %note; } | |
| 10 | + blockquote.tip { @extend %note-green; } | |
| 11 | + blockquote.caution { @extend %note-orange; } | |
| 12 | + | |
| 13 | + blockquote > h3 { | |
| 14 | + font-size: .875rem; | |
| 15 | + margin: 0 0 .25rem; | |
| 16 | + } | |
| 12 | 17 | |
| 13 | 18 | p > code { @extend %label; } |
| 14 | − p.c2 { font-weight: $boldFontWeight; } | |
| 19 | + p.c2 { font-weight: var(--boldFontWeight); } | |
| 15 | 20 | |
| 16 | − .NAVFOOTER > table { width: 100%; } | |
| 21 | + .navfooter > table { width: 100%; } | |
| 17 | 22 | td[align=center] { text-align: center; } |
| 18 | 23 | td[align=right] { text-align: right; } |
| 19 | 24 | } |
| @@ -0,0 +1,6 @@ | ||
| 1 | +._python { | |
| 2 | + @extend %sphinx; | |
| 3 | + | |
| 4 | + h2 > a, h3 > a, dt[id] > a.external, dt[id] > a.internal { float: none !important; } | |
| 5 | + } | |
| 6 | + | |
| @@ -0,0 +1,9 @@ | ||
| 1 | +._qt { | |
| 2 | + @extend %simple; | |
| 3 | + | |
| 4 | + // Function headers | |
| 5 | + h3.fn > code { | |
| 6 | + float: right; | |
| 7 | + color: var(--textColorLight); | |
| 8 | + } | |
| 9 | +} | |
| @@ -6,7 +6,7 @@ · 5 unmodified lines | ||
| 6 | 6 | .method-description > h2, h3, h4, h5, h6 { font-size: 1em; } |
| 7 | 7 | |
| 8 | 8 | .method-heading { |
| 9 | − font-weight: $boldFontWeight; | |
| 9 | + font-weight: var(--boldFontWeight); | |
| 10 | 10 | @extend %block-label, %label-blue; |
| 11 | 11 | |
| 12 | 12 | + .method-heading { margin-top: -.5em; } |
| @@ -22,26 +22,19 @@ · 9 unmodified lines | ||
| 22 | 22 | a.method-click-advice { |
| 23 | 23 | float: right; |
| 24 | 24 | font-size: .75rem; |
| 25 | − color: $linkColor; | |
| 25 | + color: var(--linkColor); | |
| 26 | 26 | cursor: pointer; |
| 27 | 27 | @extend %user-select-none; |
| 28 | 28 | |
| 29 | 29 | &:hover { text-decoration: underline; } |
| 30 | − } | |
| 31 | 30 | |
| 32 | − .method-description { position: relative; } | |
| 31 | + @include print { | |
| 32 | + display: none; | |
| 33 | + } | |
| 34 | + } | |
| 33 | 35 | |
| 34 | 36 | .method-source-code { |
| 35 | 37 | display: none; |
| 36 | − position: absolute; | |
| 37 | − z-index: 1; | |
| 38 | − top: 0; | |
| 39 | − left: -1em; | |
| 40 | − right: 0; | |
| 41 | − background: rgba($contentBackground, .95); | |
| 42 | − box-shadow: 0 1em 1em 1em $contentBackground; | |
| 43 | − | |
| 44 | − > pre { margin: 0; } | |
| 45 | 38 | } |
| 46 | 39 | |
| 47 | 40 | // Rails guides |
| @@ -2,7 +2,7 @@ · 1 unmodified line | ||
| 2 | 2 | @extend %simple; |
| 3 | 3 | |
| 4 | 4 | .deprecated { @extend %note, %note-orange; } |
| 5 | − .deprecatedTitle { font-weight: $boldFontWeight; } | |
| 5 | + .deprecatedTitle { font-weight: var(--boldFontWeight); } | |
| 6 | 6 | |
| 7 | 7 | span.platform { float: right; } |
| 8 | 8 | span.propType, span.platform { font-weight: normal; } |
| @@ -14,7 +14,7 @@ · 13 unmodified lines | ||
| 14 | 14 | |
| 15 | 15 | .args { |
| 16 | 16 | font-size: .75rem; |
| 17 | − color: $textColorLight; | |
| 17 | + color: var(--textColorLight); | |
| 18 | 18 | } |
| 19 | 19 | |
| 20 | 20 | // Others |
| @@ -35,7 +35,7 @@ · 14 unmodified lines | ||
| 35 | 35 | > .prompt { |
| 36 | 36 | float: left; |
| 37 | 37 | margin-right: .5em; |
| 38 | − color: $textColorLight; | |
| 38 | + color: var(--textColorLight); | |
| 39 | 39 | } |
| 40 | 40 | |
| 41 | 41 | > code { |
| @@ -10,6 +10,6 @@ · 9 unmodified lines | ||
| 10 | 10 | |
| 11 | 11 | > h1, > h2, > h3, > h4, > h5 { |
| 12 | 12 | margin: 0; |
| 13 | − font-family: $baseFont; | |
| 13 | + font-family: var(--baseFont); | |
| 14 | 14 | } |
| 15 | 15 | } |
| @@ -3,9 +3,18 @@ · 2 unmodified lines | ||
| 3 | 3 | |
| 4 | 4 | h4 { @extend %block-label; } |
| 5 | 5 | .docblock { margin-left: 1em; } |
| 6 | + div.information, div.important-traits { | |
| 7 | + @extend %note; | |
| 8 | + | |
| 9 | + > pre { margin: .5rem 0; } | |
| 10 | + } | |
| 6 | 11 | |
| 7 | 12 | div.stability { margin-bottom: 1em; } |
| 8 | 13 | em.stab, span.stab { @extend %label; } |
| 9 | 14 | em.stab.unstable, span.stab.unstable { @extend %label-orange; } |
| 10 | − .since, .out-of-band { float: right; } | |
| 15 | + .out-of-band { float: right; } | |
| 16 | + .since, .srclink { | |
| 17 | + float: right; | |
| 18 | + margin-left: .5rem; | |
| 19 | + } | |
| 11 | 20 | } |
| @@ -17,34 +17,4 @@ · 16 unmodified lines | ||
| 17 | 17 | blockquote > h4, blockquote > h5 { margin-top: .25rem; } |
| 18 | 18 | } |
| 19 | 19 | |
| 20 | −._apache_pig, | |
| 21 | −._bower, | |
| 22 | −._chai, | |
| 23 | −._codeceptjs, | |
| 24 | −._docker, | |
| 25 | −._electron, | |
| 26 | −._fish, | |
| 27 | −._flow, | |
| 28 | −._gnu, | |
| 29 | −._grunt, | |
| 30 | −._haxe, | |
| 31 | −._immutable, | |
| 32 | −._influxdata, | |
| 33 | −._jasmine, | |
| 34 | −._jest, | |
| 35 | −._less, | |
| 36 | −._lodash, | |
| 37 | −._marionette, | |
| 38 | −._markdown, | |
| 39 | −._mocha, | |
| 40 | −._mongoose, | |
| 41 | −._nim, | |
| 42 | −._redux, | |
| 43 | −._requirejs, | |
| 44 | −._simple, | |
| 45 | −._typescript, | |
| 46 | −._vagrant, | |
| 47 | −._vulkan, | |
| 48 | −._yarn { | |
| 49 | − @extend %simple; | |
| 50 | −} | |
| 20 | +._simple { @extend %simple; } | |
| @@ -10,10 +10,11 @@ · 9 unmodified lines | ||
| 10 | 10 | |
| 11 | 11 | .important { @extend %note-orange; } |
| 12 | 12 | .warning, .deprecated-removed, .deprecated { @extend %note-red; } |
| 13 | + .hint { @extend %note-green; } | |
| 13 | 14 | |
| 14 | 15 | .versionmodified, span.title, .topic-title { |
| 15 | 16 | display: block; |
| 16 | − font-weight: $boldFontWeight; | |
| 17 | + font-weight: var(--boldFontWeight); | |
| 17 | 18 | } |
| 18 | 19 | |
| 19 | 20 | p > code, li > code, dd > code, .docutils > dt > code { @extend %label; } |
| @@ -25,7 +26,7 @@ · 5 unmodified lines | ||
| 25 | 26 | .admonition-title { |
| 26 | 27 | float: left; |
| 27 | 28 | margin: 0 .5em 0 0; |
| 28 | − font-weight: $boldFontWeight; | |
| 29 | + font-weight: var(--boldFontWeight); | |
| 29 | 30 | |
| 30 | 31 | &:after { content: ':'; } |
| 31 | 32 | } |
| @@ -6,7 +6,7 @@ · 5 unmodified lines | ||
| 6 | 6 | .admonition.tip { @extend %note-green; } |
| 7 | 7 | .admonition-title { |
| 8 | 8 | margin: 0 0 .25rem; |
| 9 | − font-weight: $boldFontWeight; | |
| 9 | + font-weight: var(--boldFontWeight); | |
| 10 | 10 | } |
| 11 | 11 | |
| 12 | 12 | code { @extend %label; } |
| @@ -24,7 +24,7 @@ · 23 unmodified lines | ||
| 24 | 24 | } |
| 25 | 25 | |
| 26 | 26 | tr.current { |
| 27 | − font-weight: $boldFontWeight; | |
| 27 | + font-weight: var(--boldFontWeight); | |
| 28 | 28 | font-size: 1rem; |
| 29 | 29 | } |
| 30 | 30 | |
| @@ -0,0 +1,4 @@ | ||
| 1 | +._terraform { | |
| 2 | + @extend %simple; | |
| 3 | + .note, .alert { @extend %note; } | |
| 4 | +} | |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | ._yard { |
| 2 | 2 | @extend %simple; |
| 3 | 3 | |
| 4 | − .tag_title { font-weight: $boldFontWeight; } | |
| 4 | + .tag_title { font-weight: var(--boldFontWeight); } | |
| 5 | 5 | } |
| @@ -3,6 +3,6 @@ · 2 unmodified lines | ||
| 3 | 3 | |
| 4 | 4 | .detail-header-tag, .detailHeaderTag { |
| 5 | 5 | float: right; |
| 6 | − color: $textColorLight; | |
| 6 | + color: var(--textColorLight); | |
| 7 | 7 | } |
| 8 | 8 | } |
| @@ -0,0 +1,24 @@ | ||
| 1 | +Adding a documentation may look like a daunting task but once you get the hang of it, it's actually quite simple. Don't hesitate to ask for help [in Gitter](https://gitter.im/FreeCodeCamp/DevDocs) if you ever get stuck. | |
| 2 | + | |
| 3 | +**Note:** please read the [contributing guidelines](../.github/CONTRIBUTING.md) before submitting a new documentation. | |
| 4 | + | |
| 5 | +1. Create a subclass of `Docs::UrlScraper` or `Docs::FileScraper` in the `lib/docs/scrapers/` directory. Its name should be the [PascalCase](http://api.rubyonrails.org/classes/String.html#method-i-camelize) equivalent of the filename (e.g. `my_doc` → `MyDoc`) | |
| 6 | +2. Add the appropriate class attributes and filter options (see the [Scraper Reference](./scraper-reference.md) page). | |
| 7 | +3. Check that the scraper is listed in `thor docs:list`. | |
| 8 | +4. Create filters specific to the scraper in the `lib/docs/filters/[my_doc]/` directory and add them to the class's [filter stacks](./scraper-reference.md#filter-stacks). You may create any number of filters but will need at least the following two: | |
| 9 | + * A [`CleanHtml`](./filter-reference.md#cleanhtmlfilter) filter whose task is to clean the HTML markup (e.g. adding `id` attributes to headings) and remove everything superfluous and/or nonessential. | |
| 10 | + * An [`Entries`](./filter-reference.md#entriesfilter) filter whose task is to determine the pages' metadata (the list of entries, each with a name, type and path). | |
| 11 | + The [Filter Reference](./filter-reference.md) page has all the details about filters. | |
| 12 | +5. Using the `thor docs:page [my_doc] [path]` command, check that the scraper works properly. Files will appear in the `public/docs/[my_doc]/` directory (but not inside the app as the command doesn't touch the index). `path` in this case refers to either the remote path (if using `UrlScraper`) or the local path (if using `FileScraper`). | |
| 13 | +6. Generate the full documentation using the `thor docs:generate [my_doc] --force` command. Additionally, you can use the `--verbose` option to see which files are being created/updated/deleted (useful to see what changed since the last run), and the `--debug` option to see which URLs are being requested and added to the queue (useful to pin down which page adds unwanted URLs to the queue). | |
| 14 | +7. Start the server, open the app, enable the documentation, and see how everything plays out. | |
| 15 | +8. Tweak the scraper/filters and repeat 5) and 6) until the pages and metadata are ok. | |
| 16 | +9. To customize the pages' styling, create an SCSS file in the `assets/stylesheets/pages/` directory and import it in both `application.css.scss` AND `application-dark.css.scss`. Both the file and CSS class should be named `_[type]` where [type] is equal to the scraper's `type` attribute (documentations with the same type share the same custom CSS and JS). Setting the type to `simple` will apply the general styling rules in `assets/stylesheets/pages/_simple.scss`, which can be used for documentations where little to no CSS changes are needed. | |
| 17 | +10. To add syntax highlighting or execute custom JavaScript on the pages, create a file in the `assets/javascripts/views/pages/` directory (take a look at the other files to see how it works). | |
| 18 | +11. Add the documentation's icon in the `public/icons/docs/[my_doc]/` directory, in both 16x16 and 32x32-pixels formats. The icon spritesheet is automatically generated when you (re)start your local DevDocs instance. | |
| 19 | +12. Add the documentation's copyright details to the list in `assets/javascripts/templates/pages/about_tmpl.coffee`. This is the data shown in the table on the [about](https://devdocs.io/about) page, and is ordered alphabetically. Simply copying an existing item, placing it in the right slot and updating the values to match the new scraper will do the job. | |
| 20 | +13. Ensure `thor updates:check [my_doc]` shows the correct latest version. | |
| 21 | + | |
| 22 | +If the documentation includes more than a few hundreds pages and is available for download, try to scrape it locally (e.g. using `FileScraper`). It'll make the development process much faster and avoids putting too much load on the source site. (It's not a problem if your scraper is coupled to your local setup, just explain how it works in your pull request.) | |
| 23 | + | |
| 24 | +Finally, try to document your scraper and filters' behavior as much as possible using comments (e.g. why some URLs are ignored, HTML markup removed, metadata that way, etc.). It'll make updating the documentation much easier. | |
| @@ -0,0 +1,224 @@ | ||
| 1 | +**Table of contents:** | |
| 2 | + | |
| 3 | +* [Overview](#overview) | |
| 4 | +* [Instance methods](#instance-methods) | |
| 5 | +* [Core filters](#core-filters) | |
| 6 | +* [Custom filters](#custom-filters) | |
| 7 | + - [CleanHtmlFilter](#cleanhtmlfilter) | |
| 8 | + - [EntriesFilter](#entriesfilter) | |
| 9 | + | |
| 10 | +## Overview | |
| 11 | + | |
| 12 | +Filters use the [HTML::Pipeline](https://github.com/jch/html-pipeline) library. They take an HTML string or [Nokogiri](http://nokogiri.org/) node as input, optionally perform modifications and/or extract information from it, and then outputs the result. Together they form a pipeline where each filter hands its output to the next filter's input. Every documentation page passes through this pipeline before being copied on the local filesystem. | |
| 13 | + | |
| 14 | +Filters are subclasses of the [`Docs::Filter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/core/filter.rb) class and require a `call` method. A basic implementation looks like this: | |
| 15 | + | |
| 16 | +```ruby | |
| 17 | +module Docs | |
| 18 | + class CustomFilter < Filter | |
| 19 | + def call | |
| 20 | + doc | |
| 21 | + end | |
| 22 | + end | |
| 23 | +end | |
| 24 | +``` | |
| 25 | + | |
| 26 | +Filters which manipulate the Nokogiri node object (`doc` and related methods) are _HTML filters_ and must not manipulate the HTML string (`html`). Vice-versa, filters which manipulate the string representation of the document are _text filters_ and must not manipulate the Nokogiri node object. The two types are divided into two stacks within the scrapers. These stacks are then combined into a pipeline that calls the HTML filters before the text filters (more details [here](./scraper-reference.md#filter-stacks)). This is to avoid parsing the document multiple times. | |
| 27 | + | |
| 28 | +The `call` method must return either `doc` or `html`, depending on the type of filter. | |
| 29 | + | |
| 30 | +## Instance methods | |
| 31 | + | |
| 32 | +* `doc` [Nokogiri::XML::Node] | |
| 33 | + The Nokogiri representation of the container element. | |
| 34 | + See [Nokogiri's API docs](http://www.rubydoc.info/github/sparklemotion/nokogiri/Nokogiri/XML/Node) for the list of available methods. | |
| 35 | + | |
| 36 | +* `html` [String] | |
| 37 | + The string representation of the container element. | |
| 38 | + | |
| 39 | +* `context` [Hash] **(frozen)** | |
| 40 | + The scraper's `options` along with a few additional keys: `:base_url`, `:root_url`, `:root_page` and `:url`. | |
| 41 | + | |
| 42 | +* `result` [Hash] | |
| 43 | + Used to store the page's metadata and pass back information to the scraper. | |
| 44 | + Possible keys: | |
| 45 | + | |
| 46 | + - `:path` — the page's normalized path | |
| 47 | + - `:store_path` — the path where the page will be stored (equal to `:path` with `.html` at the end) | |
| 48 | + - `:internal_urls` — the list of distinct internal URLs found within the page | |
| 49 | + - `:entries` — the [`Entry`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/core/models/entry.rb) objects to add to the index | |
| 50 | + | |
| 51 | +* `css`, `at_css`, `xpath`, `at_xpath` | |
| 52 | + Shortcuts for `doc.css`, `doc.xpath`, etc. | |
| 53 | + | |
| 54 | +* `base_url`, `current_url`, `root_url` [Docs::URL] | |
| 55 | + Shortcuts for `context[:base_url]`, `context[:url]`, and `context[:root_url]` respectively. | |
| 56 | + | |
| 57 | +* `root_path` [String] | |
| 58 | + Shortcut for `context[:root_path]`. | |
| 59 | + | |
| 60 | +* `subpath` [String] | |
| 61 | + The sub-path from the base URL of the current URL. | |
| 62 | + _Example: if `base_url` equals `example.com/docs` and `current_url` equals `example.com/docs/file?raw`, the returned value is `/file`._ | |
| 63 | + | |
| 64 | +* `slug` [String] | |
| 65 | + The `subpath` removed of any leading slash or `.html` extension. | |
| 66 | + _Example: if `subpath` equals `/dir/file.html`, the returned value is `dir/file`._ | |
| 67 | + | |
| 68 | +* `root_page?` [Boolean] | |
| 69 | + Returns `true` if the current page is the root page. | |
| 70 | + | |
| 71 | +* `initial_page?` [Boolean] | |
| 72 | + Returns `true` if the current page is the root page or its subpath is one of the scraper's `initial_paths`. | |
| 73 | + | |
| 74 | +## Core filters | |
| 75 | + | |
| 76 | +* [`ContainerFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/container.rb) — changes the root node of the document (remove everything outside) | |
| 77 | +* [`CleanHtmlFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/clean_html.rb) — removes HTML comments, `<script>`, `<style>`, etc. | |
| 78 | +* [`NormalizeUrlsFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/normalize_urls.rb) — replaces all URLs with their fully qualified counterpart | |
| 79 | +* [`InternalUrlsFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/internal_urls.rb) — detects internal URLs (the ones to scrape) and replaces them with their unqualified, relative counterpart | |
| 80 | +* [`NormalizePathsFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/normalize_paths.rb) — makes the internal paths consistent (e.g. always end with `.html`) | |
| 81 | +* [`CleanLocalUrlsFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/clean_local_urls.rb) — removes links, iframes and images pointing to localhost (`FileScraper` only) | |
| 82 | +* [`InnerHtmlFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/inner_html.rb) — converts the document to a string | |
| 83 | +* [`CleanTextFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/clean_text.rb) — removes empty nodes | |
| 84 | +* [`AttributionFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/attribution.rb) — appends the license info and link to the original document | |
| 85 | +* [`TitleFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/title.rb) — prepends the document with a title (disabled by default) | |
| 86 | +* [`EntriesFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/entries.rb) — abstract filter for extracting the page's metadata | |
| 87 | + | |
| 88 | +## Custom filters | |
| 89 | + | |
| 90 | +Scrapers can have any number of custom filters but require at least the two described below. | |
| 91 | + | |
| 92 | +**Note:** filters are located in the [`lib/docs/filters`](https://github.com/Thibaut/devdocs/tree/master/lib/docs/filters/) directory. The class's name must be the [CamelCase](http://api.rubyonrails.org/classes/String.html#method-i-camelize) equivalent of the filename. | |
| 93 | + | |
| 94 | +### `CleanHtmlFilter` | |
| 95 | + | |
| 96 | +The `CleanHtml` filter is tasked with cleaning the HTML markup where necessary and removing anything superfluous or nonessential. Only the core documentation should remain at the end. | |
| 97 | + | |
| 98 | +Nokogiri's many jQuery-like methods make it easy to search and modify elements — see the [API docs](http://www.rubydoc.info/github/sparklemotion/nokogiri/Nokogiri/XML/Node). | |
| 99 | + | |
| 100 | +Here's an example implementation that covers the most common use-cases: | |
| 101 | + | |
| 102 | +```ruby | |
| 103 | +module Docs | |
| 104 | + class MyScraper | |
| 105 | + class CleanHtmlFilter < Filter | |
| 106 | + def call | |
| 107 | + css('hr').remove | |
| 108 | + css('#changelog').remove if root_page? | |
| 109 | + | |
| 110 | + # Set id attributes on <h3> instead of an empty <a> | |
| 111 | + css('h3').each do |node| | |
| 112 | + node['id'] = node.at_css('a')['id'] | |
| 113 | + end | |
| 114 | + | |
| 115 | + # Make proper table headers | |
| 116 | + css('td.header').each do |node| | |
| 117 | + node.name = 'th' | |
| 118 | + end | |
| 119 | + | |
| 120 | + # Remove code highlighting | |
| 121 | + css('pre').each do |node| | |
| 122 | + node.content = node.content | |
| 123 | + end | |
| 124 | + | |
| 125 | + doc | |
| 126 | + end | |
| 127 | + end | |
| 128 | + end | |
| 129 | +end | |
| 130 | +``` | |
| 131 | + | |
| 132 | +**Notes:** | |
| 133 | + | |
| 134 | +* Empty elements will be automatically removed by the core `CleanTextFilter` later in the pipeline's execution. | |
| 135 | +* Although the goal is to end up with a clean version of the page, try to keep the number of modifications to a minimum, so as to make the code easier to maintain. Custom CSS is the preferred way of normalizing the pages (except for hiding stuff which should always be done by removing the markup). | |
| 136 | +* Try to document your filter's behavior as much as possible, particularly modifications that apply only to a subset of pages. It'll make updating the documentation easier. | |
| 137 | + | |
| 138 | +### `EntriesFilter` | |
| 139 | + | |
| 140 | +The `Entries` filter is responsible for extracting the page's metadata, represented by a set of _entries_, each with a name, type and path. | |
| 141 | + | |
| 142 | +The following two models are used under the hood to represent the metadata: | |
| 143 | + | |
| 144 | +* [`Entry(name, type, path)`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/core/models/entry.rb) | |
| 145 | +* [`Type(name, slug, count)`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/core/models/type.rb) | |
| 146 | + | |
| 147 | +Each scraper must implement its own `EntriesFilter` by subclassing the [`Docs::EntriesFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/entries.rb) class. The base class already implements the `call` method and includes four methods which the subclasses can override: | |
| 148 | + | |
| 149 | +* `get_name` [String] | |
| 150 | + The name of the default entry (aka. the page's name). | |
| 151 | + It is usually guessed from the `slug` (documented above) or by searching the HTML markup. | |
| 152 | + **Default:** modified version of `slug` (underscores are replaced with spaces and forward slashes with dots) | |
| 153 | + | |
| 154 | +* `get_type` [String] | |
| 155 | + The type of the default entry (aka. the page's type). | |
| 156 | + Entries without a type can be searched for but won't be listed in the app's sidebar (unless no other entries have a type). | |
| 157 | + **Default:** `nil` | |
| 158 | + | |
| 159 | +* `include_default_entry?` [Boolean] | |
| 160 | + Whether to include the default entry. | |
| 161 | + Used when a page consists of multiple entries (returned by `additional_entries`) but doesn't have a name/type of its own, or to remove a page from the index (if it has no additional entries), in which case it won't be copied on the local filesystem and any link to it in the other pages will be broken (as explained on the [Scraper Reference](./scraper-reference.md) page, this is used to keep the `:skip` / `:skip_patterns` options to a maintainable size, or if the page includes links that can't reached from anywhere else). | |
| 162 | + **Default:** `true` | |
| 163 | + | |
| 164 | +* `additional_entries` [Array] | |
| 165 | + The list of additional entries. | |
| 166 | + Each entry is represented by an Array of three attributes: its name, fragment identifier, and type. The fragment identifier refers to the `id` attribute of the HTML element (usually a heading) that the entry relates to. It is combined with the page's path to become the entry's path. If absent or `nil`, the page's path is used. If the type is absent or `nil`, the default `type` is used. | |
| 167 | + Example: `[ ['One'], ['Two', 'id'], ['Three', nil, 'type'] ]` adds three additional entries, the first one named "One" with the default path and type, the second one named "Two" with the URL fragment "#id" and the default type, and the third one named "Three" with the default path and the type "type". | |
| 168 | + The list is usually constructed by running through the markup. Exceptions can also be hard-coded for specific pages. | |
| 169 | + **Default:** `[]` | |
| 170 | + | |
| 171 | +The following accessors are also available, but must not be overridden: | |
| 172 | + | |
| 173 | +* `name` [String] | |
| 174 | + Memoized version of `get_name` (`nil` for the root page). | |
| 175 | + | |
| 176 | +* `type` [String] | |
| 177 | + Memoized version of `get_type` (`nil` for the root page). | |
| 178 | + | |
| 179 | +**Notes:** | |
| 180 | + | |
| 181 | +* Leading and trailing whitespace is automatically removed from names and types. | |
| 182 | +* Names must be unique across the documentation and as short as possible (ideally less than 30 characters). Whenever possible, methods should be differentiated from properties by appending `()`, and instance methods should be differentiated from class methods using the `Class#method` or `object.method` conventions. | |
| 183 | +* You can call `name` from `get_type` or `type` from `get_name` but doing both will cause a stack overflow (i.e. you can infer the name from the type or the type from the name, but you can't do both at the same time). Don't call `get_name` or `get_type` directly as their value isn't memoized. | |
| 184 | +* The root page has no name and no type (both are `nil`). `get_name` and `get_type` won't get called with the page (but `additional_entries` will). | |
| 185 | +* `Docs::EntriesFilter` is an _HTML filter_. It must be added to the scraper's `html_filters` stack. | |
| 186 | +* Try to document the code as much as possible, particularly the special cases. It'll make updating the documentation easier. | |
| 187 | + | |
| 188 | +**Example:** | |
| 189 | + | |
| 190 | +```ruby | |
| 191 | +module Docs | |
| 192 | + class MyScraper | |
| 193 | + class EntriesFilter < Docs::EntriesFilter | |
| 194 | + def get_name | |
| 195 | + node = at_css('h1') | |
| 196 | + result = node.content.strip | |
| 197 | + result << ' event' if type == 'Events' | |
| 198 | + result << '()' if node['class'].try(:include?, 'function') | |
| 199 | + result | |
| 200 | + end | |
| 201 | + | |
| 202 | + def get_type | |
| 203 | + object, method = *slug.split('/') | |
| 204 | + method ? object : 'Miscellaneous' | |
| 205 | + end | |
| 206 | + | |
| 207 | + def additional_entries | |
| 208 | + return [] if root_page? | |
| 209 | + | |
| 210 | + css('h2').map do |node| | |
| 211 | + [node.content, node['id']] | |
| 212 | + end | |
| 213 | + end | |
| 214 | + | |
| 215 | + def include_default_entry? | |
| 216 | + !at_css('.obsolete') | |
| 217 | + end | |
| 218 | + end | |
| 219 | + end | |
| 220 | +end | |
| 221 | +``` | |
| 222 | + | |
| 223 | + | |
| 224 | +return [[Home]] | |
| @@ -0,0 +1,102 @@ | ||
| 1 | +# Maintainer's Guide | |
| 2 | + | |
| 3 | +This document is intended for [DevDocs maintainers](#list-of-maintainers). | |
| 4 | + | |
| 5 | +## Merging pull requests | |
| 6 | + | |
| 7 | +- PRs should be approved by at least one maintainer before being merged. | |
| 8 | + | |
| 9 | +- PRs that add or update documentations should always be merged locally, and the app deployed, before the merge is pushed to GitHub. | |
| 10 | + | |
| 11 | + This workflow is required because there is a dependency between the local and production environments. The `thor docs:download` command downloads documentations from production files uploaded by the `thor docs:upload` command. If a PR adding a new documentation is merged and pushed to GitHub before the files have been uploaded to production, the `thor docs:download` will fail for the new documentation and the docker container will not build properly until the new documentation is deployed to production. | |
| 12 | + | |
| 13 | +## Updating docs | |
| 14 | + | |
| 15 | +The process for updating docs is as follow: | |
| 16 | + | |
| 17 | +- Make version/release changes in the scraper file. | |
| 18 | +- If needed, update the copyright notice of the documentation in the scraper file (`options[:attribution]`) and the about page (`about_tmpl.coffee`). The copyright notice must be the same as the one on the original documentation. | |
| 19 | +- Run `thor docs:generate`. | |
| 20 | +- Make sure the documentation still works well. The `thor docs:generate` command outputs a summary of the changes, which helps identifying issues (e.g. deleted files) and new pages to check out in the app. Verify locally that everything works, especially the files that were created (if any), and that the categorization of entries is still good. Often, updates will require code changes to tweak some new markup in the source website or categorize new entries. | |
| 21 | +- Commit the changes (protip: use the `thor docs:commit` command documented below). | |
| 22 | +- Optional: do more updates. | |
| 23 | +- Run `thor docs:upload` (documented below). | |
| 24 | +- Push to GitHub to [deploy the app](#deploying-devdocs) and verify that everything works in production. | |
| 25 | +- Run `thor docs:clean` (documented below). | |
| 26 | + | |
| 27 | +Note: changes to `public/docs/docs.json` should never be committed. This file reflects which documentations have been downloaded or generated locally, which is always none on a fresh `git clone`. | |
| 28 | + | |
| 29 | +## Setup requirements | |
| 30 | + | |
| 31 | +In order to deploy DevDocs, you must: | |
| 32 | + | |
| 33 | +- be given access to Heroku, [configure the Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli) on your computer, and familiarize yourself with Heroku's UI and CLI, as well as that of New Relic (accessible through [the Heroku dashboard](https://dashboard.heroku.com/apps/devdocs)). | |
| 34 | + | |
| 35 | +- be given access to DevDocs's [Sentry instance](https://sentry.io/devdocs/devdocs-js/) (for JS error tracking) and familiarize yourself with its UI. | |
| 36 | + | |
| 37 | +- be provided with DevDocs's S3 credentials, and install (`brew install awscli` on macOS) and [configure](https://docs.aws.amazon.com/cli/latest/reference/configure/) the AWS CLI on your computer. The configuration must add a named profile called "devdocs": | |
| 38 | + ``` | |
| 39 | + aws configure --profile devdocs | |
| 40 | + ``` | |
| 41 | + | |
| 42 | +- be provided with DevDocs's MaxCDN push zone credentials, and add them to your `.bash_profile` as such: | |
| 43 | + ``` | |
| 44 | + export DEVDOCS_DL_USERNAME="username" | |
| 45 | + export DEVDOCS_DL_PASSWORD="password" | |
| 46 | + ``` | |
| 47 | + | |
| 48 | +## Thor commands | |
| 49 | + | |
| 50 | +In addition to the [publicly-documented commands](https://github.com/freeCodeCamp/devdocs#available-commands), the following commands are aimed at DevDocs maintainers: | |
| 51 | + | |
| 52 | +- `thor docs:package` | |
| 53 | + | |
| 54 | + Generates packages for one or more documentations. Those packages are intended to be uploaded to DevDocs's MaxCDN push zone by maintainers via the `thor docs:upload` command, and downloaded by users via the `thor docs:download` command. | |
| 55 | + | |
| 56 | + Versions can be specified as such: `thor docs:package rails@5.2 node@10\ LTS`. | |
| 57 | + | |
| 58 | + Packages can also be automatically generated during the scraping process by passing the `--package` option to `thor docs:generate`. | |
| 59 | + | |
| 60 | +- `thor docs:upload` | |
| 61 | + | |
| 62 | + This command does two operations: | |
| 63 | + | |
| 64 | + 1. sync the files for the specified documentations with S3 (used by the Heroku app); | |
| 65 | + 2. upload the documentations' packages to DevDocs's MaxCDN push zone (used by the `thor docs:download` command). | |
| 66 | + | |
| 67 | + For the command to work, you must have the AWS CLI and MaxCDN credentials configured as indicated above. | |
| 68 | + | |
| 69 | + **Important:** the app should always be deployed immediately after this command has finished running. Do not run this command unless you are able and ready to deploy DevDocs. | |
| 70 | + | |
| 71 | + To upload all documentations that are packaged on your computer, run `thor docs:upload --packaged`. | |
| 72 | + To test your configuration and the effect of this command without uploading anything, pass the `--dryrun` option. | |
| 73 | + | |
| 74 | +- `thor docs:commit` | |
| 75 | + | |
| 76 | + Shortcut command to create a Git commit for a given documentation once it has been updated. Scraper and `assets/` file changes will be committed. The commit message will include the most recent version that the documentation was updated to. If some files were missed by the commit, use `git commit --amend` to add them to the commit. The command may be run before `thor docs:upload` is run, but the commit should not be pushed to GitHub before the files have been uploaded and the app deployed. | |
| 77 | + | |
| 78 | +- `thor docs:clean` | |
| 79 | + | |
| 80 | + Shortcut command to delete all package files (once uploaded via `thor docs:upload`, they are not needed anymore). | |
| 81 | + | |
| 82 | +## Deploying DevDocs | |
| 83 | + | |
| 84 | +Once docs have been uploaded via `thor docs:upload` (if applicable), you can push to the DevDocs master branch (or merge the PR containing the updates). If the Travis build succeeds, the Heroku application will be deployed automatically. | |
| 85 | + | |
| 86 | +- If you're deploying documentation updates, verify that the documentations work properly once the deploy is done. Keep in mind that you'll need to wait a few seconds for the service worker to finish caching the new assets. You should see a "DevDocs has been updated" notification appear when the caching is done, after which you need to refresh the page to see the changes. | |
| 87 | +- If you're deploying frontend changes, monitor [Sentry](https://sentry.io/devdocs/devdocs-js/) for new JS errors once the deploy is done. | |
| 88 | +- If you're deploying server changes, monitor New Relic (accessible through [the Heroku dashboard](https://dashboard.heroku.com/apps/devdocs)) for Ruby exceptions and throughput or response time changes once the deploy is done. | |
| 89 | + | |
| 90 | +If any issue arises, run `heroku rollback` to rollback to the previous version of the app (this can also be done via Heroku's UI). Note that this will not revert changes made to documentation files that were uploaded via `thor docs:upload`. Try and fix the issue as quickly as possible, then re-deploy the app. Reach out to other maintainers if you need help. | |
| 91 | + | |
| 92 | +If this is your first deploy, make sure another maintainer is around to assist. | |
| 93 | + | |
| 94 | +## List of maintainers | |
| 95 | + | |
| 96 | +- [Jed Fox](https://github.com/j-f1) | |
| 97 | +- [Jasper van Merle](https://github.com/jmerle) | |
| 98 | +- [Ahmad Abdolsaheb](https://github.com/ahmadabdolsaheb) | |
| 99 | +- [Mrugesh Mohapatra](https://github.com/raisedadead) | |
| 100 | +- [Thibaut Courouble](https://github.com/thibaut) | |
| 101 | + | |
| 102 | +Interested in helping maintain DevDocs? Come talk to us on [Gitter](https://gitter.im/FreeCodeCamp/DevDocs) :) | |
| @@ -0,0 +1,227 @@ | ||
| 1 | +**Table of contents:** | |
| 2 | + | |
| 3 | +* [Overview](#overview) | |
| 4 | +* [Configuration](#configuration) | |
| 5 | + - [Attributes](#attributes) | |
| 6 | + - [Filter stacks](#filter-stacks) | |
| 7 | + - [Filter options](#filter-options) | |
| 8 | + | |
| 9 | +## Overview | |
| 10 | + | |
| 11 | +Starting from a root URL, scrapers recursively follow links that match a set of rules, passing each valid response through a chain of filters before writing the file on the local filesystem. They also create an index of the pages' metadata (determined by one filter), which is dumped into a JSON file at the end. | |
| 12 | + | |
| 13 | +Scrapers rely on the following libraries: | |
| 14 | + | |
| 15 | +* [Typhoeus](https://github.com/typhoeus/typhoeus) for making HTTP requests | |
| 16 | +* [HTML::Pipeline](https://github.com/jch/html-pipeline) for applying filters | |
| 17 | +* [Nokogiri](http://nokogiri.org/) for parsing HTML | |
| 18 | + | |
| 19 | +There are currently two kinds of scrapers: [`UrlScraper`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/core/scrapers/url_scraper.rb) which downloads files via HTTP and [`FileScraper`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/core/scrapers/file_scraper.rb) which reads them from the local filesystem. They function almost identically (both use URLs), except that `FileScraper` substitutes the base URL with a local path before reading a file. `FileScraper` uses the placeholder `localhost` base URL by default and includes a filter to remove any URL pointing to it at the end. | |
| 20 | + | |
| 21 | +To be processed, a response must meet the following requirements: | |
| 22 | + | |
| 23 | +* 200 status code | |
| 24 | +* HTML content type | |
| 25 | +* effective URL (after redirection) contained in the base URL (explained below) | |
| 26 | + | |
| 27 | +(`FileScraper` only checks if the file exists and is not empty.) | |
| 28 | + | |
| 29 | +Each URL is requested only once (case-insensitive). | |
| 30 | + | |
| 31 | +## Configuration | |
| 32 | + | |
| 33 | +Configuration is done via class attributes and divided into three main categories: | |
| 34 | + | |
| 35 | +* [Attributes](#attributes) — essential information such as name, version, URL, etc. | |
| 36 | +* [Filter stacks](#filter-stacks) — the list of filters that will be applied to each page. | |
| 37 | +* [Filter options](#filter-options) — the options passed to said filters. | |
| 38 | + | |
| 39 | +**Note:** scrapers are located in the [`lib/docs/scrapers`](https://github.com/Thibaut/devdocs/tree/master/lib/docs/scrapers/) directory. The class's name must be the [CamelCase](http://api.rubyonrails.org/classes/String.html#method-i-camelize) equivalent of the filename. | |
| 40 | + | |
| 41 | +### Attributes | |
| 42 | + | |
| 43 | +* `name` [String] | |
| 44 | + Must be unique. | |
| 45 | + Defaults to the class's name. | |
| 46 | + | |
| 47 | +* `slug` [String] | |
| 48 | + Must be unique, lowercase, and not include dashes (underscores are ok). | |
| 49 | + Defaults to `name` lowercased. | |
| 50 | + | |
| 51 | +* `type` [String] **(required, inherited)** | |
| 52 | + Defines the CSS class name (`_[type]`) and custom JavaScript class (`app.views.[Type]Page`) that will be added/loaded on each page. Documentations sharing a similar structure (e.g. generated with the same tool or originating from the same website) should use the same `type` to avoid duplicating the CSS and JS. | |
| 53 | + Must include lowercase letters only. | |
| 54 | + | |
| 55 | +* `release` [String] **(required)** | |
| 56 | + The version of the software at the time the scraper was last run. This is only informational and doesn't affect the scraper's behavior. | |
| 57 | + | |
| 58 | +* `base_url` [String] **(required in `UrlScraper`)** | |
| 59 | + The documents' location. Only URLs _inside_ the `base_url` will be scraped. "inside" more or less means "starting with" except that `/docs` is outside `/doc` (but `/doc/` is inside). | |
| 60 | + Defaults to `localhost` in `FileScraper`. _(Note: any iframe, image, or skipped link pointing to localhost will be removed by the `CleanLocalUrls` filter; the value should be overridden if the documents are available online.)_ | |
| 61 | + Unless `root_path` is set, the root/initial URL is equal to `base_url`. | |
| 62 | + | |
| 63 | +* `root_path` [String] **(inherited)** | |
| 64 | + The path from the `base_url` of the root URL. | |
| 65 | + | |
| 66 | +* `initial_paths` [Array] **(inherited)** | |
| 67 | + A list of paths (from the `base_url`) to add to the initial queue. Useful for scraping isolated documents. | |
| 68 | + Defaults to `[]`. _(Note: the `root_path` is added to the array at runtime.)_ | |
| 69 | + | |
| 70 | +* `dir` [String] **(required, `FileScraper` only)** | |
| 71 | + The absolute path where the files are located on the local filesystem. | |
| 72 | + _Note: `FileScraper` works exactly like `UrlScraper` (manipulating the same kind of URLs) except that it substitutes `base_url` with `dir` in order to read files instead of making HTTP requests._ | |
| 73 | + | |
| 74 | +* `params` [Hash] **(inherited, `UrlScraper` only)** | |
| 75 | + Query string parameters to append to every URL. (e.g. `{ format: 'raw' }` → `?format=raw`) | |
| 76 | + Defaults to `{}`. | |
| 77 | + | |
| 78 | +* `abstract` [Boolean] | |
| 79 | + Make the scraper abstract / not runnable. Used for sharing behavior with other scraper classes (e.g. all MDN scrapers inherit from the abstract [`Mdn`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/scrapers/mdn/mdn.rb) class). | |
| 80 | + Defaults to `false`. | |
| 81 | + | |
| 82 | +### Filter stacks | |
| 83 | + | |
| 84 | +Each scraper has two [filter](https://github.com/Thibaut/devdocs/blob/master/lib/docs/core/filter.rb) [stacks](https://github.com/Thibaut/devdocs/blob/master/lib/docs/core/filter_stack.rb): `html_filters` and `text_filters`. They are combined into a pipeline (using the [HTML::Pipeline](https://github.com/jch/html-pipeline) library) which causes each filter to hand its output to the next filter's input. | |
| 85 | + | |
| 86 | +HTML filters are executed first and manipulate a parsed version of the document (a [Nokogiri](http://nokogiri.org/Nokogiri/XML/Node.html) node object), whereas text filters manipulate the document as a string. This separation avoids parsing the document multiple times. | |
| 87 | + | |
| 88 | +Filter stacks are like sorted sets. They can modified using the following methods: | |
| 89 | + | |
| 90 | +```ruby | |
| 91 | +push(*names) # append one or more filters at the end | |
| 92 | +insert_before(index, *names) # insert one filter before another (index can be a name) | |
| 93 | +insert_after(index, *names) # insert one filter after another (index can be a name) | |
| 94 | +replace(index, name) # replace one filter with another (index can be a name) | |
| 95 | +``` | |
| 96 | + | |
| 97 | +"names" are `require` paths relative to `Docs` (e.g. `jquery/clean_html` → `Docs::Jquery::CleanHtml`). | |
| 98 | + | |
| 99 | +Default `html_filters`: | |
| 100 | + | |
| 101 | +* [`ContainerFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/container.rb) — changes the root node of the document (remove everything outside) | |
| 102 | +* [`CleanHtmlFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/clean_html.rb) — removes HTML comments, `<script>`, `<style>`, etc. | |
| 103 | +* [`NormalizeUrlsFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/normalize_urls.rb) — replaces all URLs with their fully qualified counterpart | |
| 104 | +* [`InternalUrlsFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/internal_urls.rb) — detects internal URLs (the ones to scrape) and replaces them with their unqualified, relative counterpart | |
| 105 | +* [`NormalizePathsFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/normalize_paths.rb) — makes the internal paths consistent (e.g. always end with `.html`) | |
| 106 | +* [`CleanLocalUrlsFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/clean_local_urls.rb) — removes links, iframes and images pointing to localhost (`FileScraper` only) | |
| 107 | + | |
| 108 | +Default `text_filters`: | |
| 109 | + | |
| 110 | +* [`InnerHtmlFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/inner_html.rb) — converts the document to a string | |
| 111 | +* [`CleanTextFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/clean_text.rb) — removes empty nodes | |
| 112 | +* [`AttributionFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/attribution.rb) — appends the license info and link to the original document | |
| 113 | + | |
| 114 | +Additionally: | |
| 115 | + | |
| 116 | +* [`TitleFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/title.rb) is a core HTML filter, disabled by default, which prepends the document with a title (`<h1>`). | |
| 117 | +* [`EntriesFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/entries.rb) is an abstract HTML filter that each scraper must implement and responsible for extracting the page's metadata. | |
| 118 | + | |
| 119 | +### Filter options | |
| 120 | + | |
| 121 | +The filter options are stored in the `options` Hash. The Hash is inheritable (a recursive copy) and empty by default. | |
| 122 | + | |
| 123 | +More information about how filters work is available on the [Filter Reference](./filter-reference.md) page. | |
| 124 | + | |
| 125 | +* [`ContainerFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/container.rb) | |
| 126 | + | |
| 127 | + - `:container` [String or Proc] | |
| 128 | + A CSS selector of the container element. Everything outside of it will be removed and become unavailable to the other filters. If more than one element match the selector, the first one inside the DOM is used. If no elements match the selector, an error is raised. | |
| 129 | + If the value is a Proc, it is called for each page with the filter instance as argument, and should return a selector or `nil`. | |
| 130 | + The default container is the `<body>` element. | |
| 131 | + _Note: links outside of the container element will not be followed by the scraper. To remove links that should be followed, use a [`CleanHtml`](./filter-reference.md#cleanhtmlfilter) filter later in the stack._ | |
| 132 | + | |
| 133 | +* [`NormalizeUrlsFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/normalize_urls.rb) | |
| 134 | + The following options are used to modify URLs in the pages. They are useful to remove duplicates (when the same page is accessible from multiple URLs) and fix websites that have a bunch of redirections in place (when URLs that should be scraped, aren't, because they are behind a redirection which is outside of the `base_url` — see the MDN scrapers for examples of this). | |
| 135 | + | |
| 136 | + - `:replace_urls` [Hash] | |
| 137 | + Replaces all instances of a URL with another. | |
| 138 | + Format: `{ 'original_url' => 'new_url' }` | |
| 139 | + - `:replace_paths` [Hash] | |
| 140 | + Replaces all instances of a sub-path (path from the `base_url`) with another. | |
| 141 | + Format: `{ 'original_path' => 'new_path' }` | |
| 142 | + - `:fix_urls` [Proc] | |
| 143 | + Called with each URL. If the returned value is `nil`, the URL isn't modified. Otherwise the returned value is used as replacement. | |
| 144 | + | |
| 145 | + _Note: before these rules are applied, all URLs are converted to their fully qualified counterpart (http://...)._ | |
| 146 | + | |
| 147 | +* [`InternalUrlsFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/internal_urls.rb) | |
| 148 | + | |
| 149 | + Internal URLs are the ones _inside_ the scraper's `base_url` ("inside" more or less means "starting with", except that `/docs` is outside `/doc`). They will be scraped unless excluded by one of the following rules. All internal URLs are converted to relative URLs inside the pages. | |
| 150 | + | |
| 151 | + - `:skip_links` [Boolean or Proc] | |
| 152 | + If `false`, does not convert or follow any internal URL (creating a single-page documentation). | |
| 153 | + If the value is a Proc, it is called for each page with the filter instance as argument. | |
| 154 | + - `:follow_links` [Proc] | |
| 155 | + Called for page with the filter instance as argument. If the returned value is `false`, does not add internal URLs to the queue. | |
| 156 | + - `:trailing_slash` [Boolean] | |
| 157 | + If `true`, adds a trailing slash to all internal URLs. If `false`, removes it. | |
| 158 | + This is another option used to remove duplicate pages. | |
| 159 | + - `:skip` [Array] | |
| 160 | + Ignores internal URLs whose sub-paths (path from the `base_url`) are in the Array (case-insensitive). | |
| 161 | + - `:skip_patterns` [Array] | |
| 162 | + Ignores internal URLs whose sub-paths match any Regexp in the Array. | |
| 163 | + - `:only` [Array] | |
| 164 | + Ignores internal URLs whose sub-paths aren't in the Array (case-insensitive) and don't match any Regexp in `:only_patterns`. | |
| 165 | + - `:only_patterns` [Array] | |
| 166 | + Ignores internal URLs whose sub-paths don't match any Regexp in the Array and aren't in `:only`. | |
| 167 | + | |
| 168 | + If the scraper has a `root_path`, the empty and `/` paths are automatically skipped. | |
| 169 | + If `:only` or `:only_patterns` is set, the root path is automatically added to `:only`. | |
| 170 | + | |
| 171 | + _Note: pages can be excluded from the index based on their content using the [`Entries`](./filter-reference.md#entriesfilter) filter. However, their URLs will still be converted to relative in the other pages and trying to open them will return a 404 error. Although not ideal, this is often better than having to maintain a long list of `:skip` URLs._ | |
| 172 | + | |
| 173 | +* [`AttributionFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/attribution.rb) | |
| 174 | + | |
| 175 | + - `:attribution` [String] **(required)** | |
| 176 | + An HTML string with the copyright and license information. See the other scrapers for examples. | |
| 177 | + | |
| 178 | +* [`TitleFilter`](https://github.com/Thibaut/devdocs/blob/master/lib/docs/filters/core/title.rb) | |
| 179 | + | |
| 180 | + - `:title` [String or Boolean or Proc] | |
| 181 | + Unless the value is `false`, adds a title to every page. | |
| 182 | + If the value is `nil`, the title is the name of the page as determined by the [`Entries`](./filter-reference.md#entriesfilter) filter. Otherwise the title is the String or the value returned by the Proc (called for each page, with the filter instance as argument). If the Proc returns `nil` or `false`, no title is added. | |
| 183 | + - `:root_title` [String or Boolean] | |
| 184 | + Overrides the `:title` option for the root page only. | |
| 185 | + | |
| 186 | + _Note: this filter is disabled by default._ | |
| 187 | + | |
| 188 | +## Keeping scrapers up-to-date | |
| 189 | + | |
| 190 | +In order to keep scrapers up-to-date the `get_latest_version(opts)` method should be overridden. If `self.release` is defined, this should return the latest version of the documentation. If `self.release` is not defined, it should return the Epoch time when the documentation was last modified. If the documentation will never change, simply return `1.0.0`. The result of this method is periodically reported in a "Documentation versions report" issue which helps maintainers keep track of outdated documentations. | |
| 191 | + | |
| 192 | +To make life easier, there are a few utility methods that you can use in `get_latest_version`: | |
| 193 | +* `fetch(url, opts)` | |
| 194 | + | |
| 195 | + Makes a GET request to the url and returns the response body. | |
| 196 | + | |
| 197 | + Example: [lib/docs/scrapers/bash.rb](../lib/docs/scrapers/bash.rb) | |
| 198 | +* `fetch_doc(url, opts)` | |
| 199 | + | |
| 200 | + Makes a GET request to the url and returns the HTML body converted to a Nokogiri document. | |
| 201 | + | |
| 202 | + Example: [lib/docs/scrapers/git.rb](../lib/docs/scrapers/git.rb) | |
| 203 | +* `fetch_json(url, opts)` | |
| 204 | + | |
| 205 | + Makes a GET request to the url and returns the JSON body converted to a dictionary. | |
| 206 | + | |
| 207 | + Example: [lib/docs/scrapers/mdn/mdn.rb](../lib/docs/scrapers/mdn/mdn.rb) | |
| 208 | +* `get_npm_version(package, opts)` | |
| 209 | + | |
| 210 | + Returns the latest version of the given npm package. | |
| 211 | + | |
| 212 | + Example: [lib/docs/scrapers/bower.rb](../lib/docs/scrapers/bower.rb) | |
| 213 | +* `get_latest_github_release(owner, repo, opts)` | |
| 214 | + | |
| 215 | + Returns the tag name of the latest GitHub release of the given repository. If the tag name is preceded by a "v", the "v" will be removed. | |
| 216 | + | |
| 217 | + Example: [lib/docs/scrapers/jsdoc.rb](../lib/docs/scrapers/jsdoc.rb) | |
| 218 | +* `get_github_tags(owner, repo, opts)` | |
| 219 | + | |
| 220 | + Returns the list of tags on the given repository ([format](https://developer.github.com/v3/repos/#list-tags)). | |
| 221 | + | |
| 222 | + Example: [lib/docs/scrapers/liquid.rb](../lib/docs/scrapers/liquid.rb) | |
| 223 | +* `get_github_file_contents(owner, repo, path, opts)` | |
| 224 | + | |
| 225 | + Returns the contents of the requested file in the default branch of the given repository. | |
| 226 | + | |
| 227 | + Example: [lib/docs/scrapers/minitest.rb](../lib/docs/scrapers/minitest.rb) | |
| @@ -12,6 +12,8 @@ class App < Sinatra::Application · 11 unmodified lines | ||
| 12 | 12 | Rack::Mime::MIME_TYPES['.webapp'] = 'application/x-web-app-manifest+json' |
| 13 | 13 | |
| 14 | 14 | configure do |
| 15 | + use Rack::SslEnforcer, only_environments: ['production', 'test'], hsts: true, force_secure_cookies: false | |
| 16 | + | |
| 15 | 17 | set :sentry_dsn, ENV['SENTRY_DSN'] |
| 16 | 18 | set :protection, except: [:frame_options, :xss_header] |
| 17 | 19 | |
| @@ -35,6 +37,9 @@ class App < Sinatra::Application · 17 unmodified lines | ||
| 35 | 37 | |
| 36 | 38 | set :csp, false |
| 37 | 39 | |
| 40 | + require 'docs' | |
| 41 | + Docs.generate_manifest | |
| 42 | + | |
| 38 | 43 | Dir[docs_path, root.join(assets_prefix, '*/')].each do |path| |
| 39 | 44 | sprockets.append_path(path) |
| 40 | 45 | end |
| @@ -48,6 +53,11 @@ class App < Sinatra::Application · 7 unmodified lines | ||
| 48 | 53 | end |
| 49 | 54 | |
| 50 | 55 | configure :test, :development do |
| 56 | + require 'thor' | |
| 57 | + load 'tasks/sprites.thor' | |
| 58 | + | |
| 59 | + SpritesCLI.new.invoke(:generate) | |
| 60 | + | |
| 51 | 61 | require 'active_support/per_thread_registry' |
| 52 | 62 | require 'active_support/cache' |
| 53 | 63 | sprockets.cache = ActiveSupport::Cache.lookup_store :file_store, root.join('tmp', 'cache', 'assets', environment.to_s) |
| @@ -67,7 +77,7 @@ class App < Sinatra::Application · 13 unmodified lines | ||
| 67 | 77 | set :static, false |
| 68 | 78 | set :cdn_origin, 'https://cdn.devdocs.io' |
| 69 | 79 | set :docs_origin, '//docs.devdocs.io' |
| 70 | − set :csp, "default-src 'self' *; script-src 'self' 'nonce-devdocs' http://cdn.devdocs.io https://cdn.devdocs.io https://www.google-analytics.com https://secure.gaug.es http://*.jquery.com https://*.jquery.com; font-src 'none'; style-src 'self' 'unsafe-inline' *; img-src 'self' * data:;" | |
| 80 | + set :csp, "default-src 'self' *; script-src 'self' 'nonce-devdocs' https://cdn.devdocs.io https://www.google-analytics.com https://secure.gaug.es https://*.jquery.com; font-src 'none'; style-src 'self' 'unsafe-inline' *; img-src 'self' * data:;" | |
| 71 | 81 | |
| 72 | 82 | use Rack::ConditionalGet |
| 73 | 83 | use Rack::ETag |
| @@ -133,7 +143,7 @@ class App < Sinatra::Application · 59 unmodified lines | ||
| 133 | 143 | end |
| 134 | 144 | |
| 135 | 145 | def canonical_origin |
| 136 | − "http://#{request.host_with_port}" | |
| 146 | + "https://#{request.host_with_port}" | |
| 137 | 147 | end |
| 138 | 148 | |
| 139 | 149 | def browser |
| @@ -190,51 +200,45 @@ class App < Sinatra::Application · 50 unmodified lines | ||
| 190 | 200 | request.query_string.empty? ? nil : "?#{request.query_string}" |
| 191 | 201 | end |
| 192 | 202 | |
| 193 | − def manifest_asset_urls | |
| 194 | − @@manifest_asset_urls ||= [ | |
| 203 | + def service_worker_asset_urls | |
| 204 | + @@service_worker_asset_urls ||= [ | |
| 195 | 205 | javascript_path('application', asset_host: false), |
| 196 | 206 | stylesheet_path('application'), |
| 197 | − stylesheet_path('application-dark'), | |
| 198 | − image_path('docs-1.png'), | |
| 199 | − image_path('docs-1@2x.png'), | |
| 200 | − image_path('docs-2.png'), | |
| 201 | − image_path('docs-2@2x.png'), | |
| 202 | − asset_path('docs.js') | |
| 203 | − ] | |
| 204 | − end | |
| 205 | − | |
| 206 | − def main_stylesheet_path | |
| 207 | − stylesheet_paths[dark_theme? ? :dark : :default] | |
| 208 | − end | |
| 209 | − | |
| 210 | − def alternate_stylesheet_path | |
| 211 | − stylesheet_paths[dark_theme? ? :default : :dark] | |
| 207 | + image_path('sprites/docs.png'), | |
| 208 | + image_path('sprites/docs@2x.png'), | |
| 209 | + asset_path('docs.js'), | |
| 210 | + App.production? ? nil : javascript_path('debug'), | |
| 211 | + ].compact | |
| 212 | 212 | end |
| 213 | 213 | |
| 214 | − def stylesheet_paths | |
| 215 | − @@stylesheet_paths ||= { | |
| 216 | − default: stylesheet_path('application'), | |
| 217 | − dark: stylesheet_path('application-dark') | |
| 218 | − } | |
| 219 | − end | |
| 220 | − | |
| 221 | − def app_size | |
| 222 | − @app_size ||= memoized_cookies['size'].nil? ? '20rem' : "#{memoized_cookies['size']}px" | |
| 223 | − end | |
| 214 | + # Returns a cache name for the service worker to use which changes if any of the assets changes | |
| 215 | + # When a manifest exist, this name is only created once based on the asset manifest because it never changes without a server restart | |
| 216 | + # If a manifest does not exist, it is created every time this method is called because the assets can change while the server is running | |
| 217 | + def service_worker_cache_name | |
| 218 | + if File.exist?(App.assets_manifest_path) | |
| 219 | + if defined?(@@service_worker_cache_name) | |
| 220 | + return @@service_worker_cache_name | |
| 221 | + end | |
| 224 | 222 | |
| 225 | − def app_layout | |
| 226 | − memoized_cookies['layout'] | |
| 227 | − end | |
| 223 | + digest = Sprockets::Manifest | |
| 224 | + .new(nil, App.assets_manifest_path) | |
| 225 | + .files | |
| 226 | + .values | |
| 227 | + .map {|file| file["digest"]} | |
| 228 | + .join | |
| 228 | 229 | |
| 229 | − def app_theme | |
| 230 | − @app_theme ||= memoized_cookies['dark'].nil? ? 'default' : 'dark' | |
| 231 | − end | |
| 230 | + return @@service_worker_cache_name ||= Digest::MD5.hexdigest(digest) | |
| 231 | + else | |
| 232 | + paths = App.sprockets | |
| 233 | + .each_file | |
| 234 | + .to_a | |
| 235 | + .reject {|file| file.start_with?(App.docs_path)} | |
| 232 | 236 | |
| 233 | − def dark_theme? | |
| 234 | − app_theme == 'dark' | |
| 237 | + return App.sprockets.pack_hexdigest(App.sprockets.files_digest(paths)) | |
| 238 | + end | |
| 235 | 239 | end |
| 236 | 240 | |
| 237 | − def redirect_via_js(path) # courtesy of HTML5 App Cache | |
| 241 | + def redirect_via_js(path) | |
| 238 | 242 | response.set_cookie :initial_path, value: path, expires: Time.now + 15, path: '/' |
| 239 | 243 | redirect '/', 302 |
| 240 | 244 | end |
| @@ -253,19 +257,19 @@ class App < Sinatra::Application · 12 unmodified lines | ||
| 253 | 257 | before do |
| 254 | 258 | if request.host == OUT_HOST && !request.path.start_with?('/s/') |
| 255 | 259 | query_string = "?#{request.query_string}" unless request.query_string.empty? |
| 256 | − redirect "http://devdocs.io#{request.path}#{query_string}", 302 | |
| 260 | + redirect "https://devdocs.io#{request.path}#{query_string}", 302 | |
| 257 | 261 | end |
| 258 | 262 | end |
| 259 | 263 | |
| 260 | − get '/manifest.appcache' do | |
| 261 | − content_type 'text/cache-manifest' | |
| 264 | + get '/service-worker.js' do | |
| 265 | + content_type 'application/javascript' | |
| 262 | 266 | expires 0, :'no-cache' |
| 263 | − erb :manifest | |
| 267 | + erb :'service-worker.js' | |
| 264 | 268 | end |
| 265 | 269 | |
| 266 | 270 | get '/' do |
| 267 | 271 | return redirect "/#q=#{params[:q]}" if params[:q] |
| 268 | − return redirect '/' unless request.query_string.empty? # courtesy of HTML5 App Cache | |
| 272 | + return redirect '/' unless request.query_string.empty? | |
| 269 | 273 | response.headers['Content-Security-Policy'] = settings.csp if settings.csp |
| 270 | 274 | erb :index |
| 271 | 275 | end |
| @@ -435,13 +439,13 @@ class App < Sinatra::Application · 163 unmodified lines | ||
| 435 | 439 | |
| 436 | 440 | maker.channel.links.new_link do |link| |
| 437 | 441 | link.rel = 'self' |
| 438 | − link.href = 'http://devdocs.io/feed.atom' | |
| 442 | + link.href = 'https://devdocs.io/feed.atom' | |
| 439 | 443 | link.type = 'application/atom+xml' |
| 440 | 444 | end |
| 441 | 445 | |
| 442 | 446 | maker.channel.links.new_link do |link| |
| 443 | 447 | link.rel = 'alternate' |
| 444 | − link.href = 'http://devdocs.io/' | |
| 448 | + link.href = 'https://devdocs.io/' | |
| 445 | 449 | link.type = 'text/html' |
| 446 | 450 | end |
| 447 | 451 | |
| @@ -450,14 +454,14 @@ class App < Sinatra::Application · 2 unmodified lines | ||
| 450 | 454 | item.id = "tag:devdocs.io,2014:News/#{settings.news.length - i}" |
| 451 | 455 | item.title = news[1].split("\n").first.gsub(/<\/?[^>]*>/, '') |
| 452 | 456 | item.description do |desc| |
| 453 | − desc.content = news[1..-1].join.gsub("\n", '<br>').gsub('href="/', 'href="http://devdocs.io/') | |
| 457 | + desc.content = news[1..-1].join.gsub("\n", '<br>').gsub('href="/', 'href="https://devdocs.io/') | |
| 454 | 458 | desc.type = 'html' |
| 455 | 459 | end |
| 456 | 460 | item.updated = "#{news.first}T14:00:00Z" |
| 457 | 461 | item.published = "#{news.first}T14:00:00Z" |
| 458 | 462 | item.links.new_link do |link| |
| 459 | 463 | link.rel = 'alternate' |
| 460 | − link.href = 'http://devdocs.io/' | |
| 464 | + link.href = 'https://devdocs.io/' | |
| 461 | 465 | link.type = 'text/html' |
| 462 | 466 | end |
| 463 | 467 | end |
| @@ -1,5 +1,5 @@ | ||
| 1 | 1 | require 'bundler/setup' |
| 2 | −Bundler.require :docs | |
| 2 | +Bundler.require :default, :docs | |
| 3 | 3 | |
| 4 | 4 | require 'active_support' |
| 5 | 5 | require 'active_support/core_ext' |
| @@ -29,6 +29,7 @@ module Docs · 23 unmodified lines | ||
| 29 | 29 | self.rescue_errors = false |
| 30 | 30 | |
| 31 | 31 | class DocNotFound < NameError; end |
| 32 | + class SetupError < StandardError; end | |
| 32 | 33 | |
| 33 | 34 | def self.all |
| 34 | 35 | Dir["#{root_path}/docs/scrapers/**/*.rb"]. |
| @@ -74,6 +75,22 @@ module Docs · 39 unmodified lines | ||
| 74 | 75 | end |
| 75 | 76 | end |
| 76 | 77 | |
| 78 | + def self.find_by_slug(slug, version = nil) | |
| 79 | + doc = all.find { |klass| klass.slug == slug } | |
| 80 | + | |
| 81 | + unless doc | |
| 82 | + raise DocNotFound.new(%(could not find doc with "#{slug}"), slug) | |
| 83 | + end | |
| 84 | + | |
| 85 | + if version.present? | |
| 86 | + version = doc.versions.find { |klass| klass.version == version || klass.version_slug == version } | |
| 87 | + raise DocNotFound.new(%(could not find version "#{version}" for doc "#{doc.name}"), doc.name) unless version | |
| 88 | + doc = version | |
| 89 | + end | |
| 90 | + | |
| 91 | + doc | |
| 92 | + end | |
| 93 | + | |
| 77 | 94 | def self.generate_page(name, version, page_id) |
| 78 | 95 | find(name, version).store_page(store, page_id) |
| 79 | 96 | end |
| @@ -2,6 +2,7 @@ module Docs · 1 unmodified line | ||
| 2 | 2 | class Doc |
| 3 | 3 | INDEX_FILENAME = 'index.json' |
| 4 | 4 | DB_FILENAME = 'db.json' |
| 5 | + META_FILENAME = 'meta.json' | |
| 5 | 6 | |
| 6 | 7 | class << self |
| 7 | 8 | include Instrumentable |
| @@ -73,6 +74,10 @@ module Docs · 65 unmodified lines | ||
| 73 | 74 | File.join path, DB_FILENAME |
| 74 | 75 | end |
| 75 | 76 | |
| 77 | + def meta_path | |
| 78 | + File.join path, META_FILENAME | |
| 79 | + end | |
| 80 | + | |
| 76 | 81 | def as_json |
| 77 | 82 | json = { name: name, slug: slug, type: type } |
| 78 | 83 | json[:links] = links if links.present? |
| @@ -90,6 +95,9 @@ module Docs · 11 unmodified lines | ||
| 90 | 95 | false |
| 91 | 96 | end |
| 92 | 97 | end |
| 98 | + rescue Docs::SetupError => error | |
| 99 | + puts "ERROR: #{error.message}" | |
| 100 | + false | |
| 93 | 101 | end |
| 94 | 102 | |
| 95 | 103 | def store_pages(store) |
| @@ -107,11 +115,15 @@ module Docs · 11 unmodified lines | ||
| 107 | 115 | if index.present? |
| 108 | 116 | store_index(store, INDEX_FILENAME, index) |
| 109 | 117 | store_index(store, DB_FILENAME, pages) |
| 118 | + store_meta(store) | |
| 110 | 119 | true |
| 111 | 120 | else |
| 112 | 121 | false |
| 113 | 122 | end |
| 114 | 123 | end |
| 124 | + rescue Docs::SetupError => error | |
| 125 | + puts "ERROR: #{error.message}" | |
| 126 | + false | |
| 115 | 127 | end |
| 116 | 128 | |
| 117 | 129 | private |
| @@ -131,6 +143,13 @@ module Docs · 13 unmodified lines | ||
| 131 | 143 | instrument "#{filename.remove('.json')}.doc", before: old_json, after: new_json |
| 132 | 144 | store.write(filename, new_json) |
| 133 | 145 | end |
| 146 | + | |
| 147 | + def store_meta(store) | |
| 148 | + json = as_json | |
| 149 | + json[:mtime] = Time.now.to_i | |
| 150 | + json[:db_size] = store.size(DB_FILENAME) | |
| 151 | + store.write(META_FILENAME, json.to_json) | |
| 152 | + end | |
| 134 | 153 | end |
| 135 | 154 | |
| 136 | 155 | def initialize |
| @@ -144,5 +163,104 @@ module Docs · 7 unmodified lines | ||
| 144 | 163 | def build_pages(&block) |
| 145 | 164 | raise NotImplementedError |
| 146 | 165 | end |
| 166 | + | |
| 167 | + def get_scraper_version(opts) | |
| 168 | + if self.class.method_defined?(:options) and !options[:release].nil? | |
| 169 | + options[:release] | |
| 170 | + else | |
| 171 | + # If options[:release] does not exist, we return the Epoch timestamp of when the doc was last modified in DevDocs production | |
| 172 | + json = fetch_json('https://devdocs.io/docs.json', opts) | |
| 173 | + items = json.select {|item| item['name'] == self.class.name} | |
| 174 | + items = items.map {|item| item['mtime']} | |
| 175 | + items.max | |
| 176 | + end | |
| 177 | + end | |
| 178 | + | |
| 179 | + # Should return the latest version of this documentation | |
| 180 | + # If options[:release] is defined, it should be in the same format | |
| 181 | + # If options[:release] is not defined, it should return the Epoch timestamp of when the documentation was last updated | |
| 182 | + # If the docs will never change, simply return '1.0.0' | |
| 183 | + def get_latest_version(opts) | |
| 184 | + raise NotImplementedError | |
| 185 | + end | |
| 186 | + | |
| 187 | + # Returns whether or not this scraper is outdated. | |
| 188 | + # | |
| 189 | + # The default implementation assumes the documentation uses a semver(-like) approach when it comes to versions. | |
| 190 | + # Patch updates are ignored because there are usually little to no documentation changes in bug-fix-only releases. | |
| 191 | + # | |
| 192 | + # Scrapers of documentations that do not use this versioning approach should override this method. | |
| 193 | + # | |
| 194 | + # Examples of the default implementation: | |
| 195 | + # 1 -> 2 = outdated | |
| 196 | + # 1.1 -> 1.2 = outdated | |
| 197 | + # 1.1.1 -> 1.1.2 = not outdated | |
| 198 | + def is_outdated(scraper_version, latest_version) | |
| 199 | + scraper_parts = scraper_version.to_s.split(/\./).map(&:to_i) | |
| 200 | + latest_parts = latest_version.to_s.split(/\./).map(&:to_i) | |
| 201 | + | |
| 202 | + # Only check the first two parts, the third part is for patch updates | |
| 203 | + [0, 1].each do |i| | |
| 204 | + break if i >= scraper_parts.length or i >= latest_parts.length | |
| 205 | + return true if latest_parts[i] > scraper_parts[i] | |
| 206 | + return false if latest_parts[i] < scraper_parts[i] | |
| 207 | + end | |
| 208 | + | |
| 209 | + false | |
| 210 | + end | |
| 211 | + | |
| 212 | + private | |
| 213 | + | |
| 214 | + # | |
| 215 | + # Utility methods for get_latest_version | |
| 216 | + # | |
| 217 | + | |
| 218 | + def fetch(url, opts) | |
| 219 | + headers = {} | |
| 220 | + | |
| 221 | + if opts.key?(:github_token) and url.start_with?('https://api.github.com/') | |
| 222 | + headers['Authorization'] = "token #{opts[:github_token]}" | |
| 223 | + end | |
| 224 | + | |
| 225 | + opts[:logger].debug("Fetching #{url}") | |
| 226 | + response = Request.run(url, { connecttimeout: 15, headers: headers }) | |
| 227 | + | |
| 228 | + if response.success? | |
| 229 | + response.body | |
| 230 | + else | |
| 231 | + reason = response.timed_out? ? "Timed out while connecting to #{url}" : "Couldn't fetch #{url} (response code #{response.code})" | |
| 232 | + opts[:logger].error(reason) | |
| 233 | + raise reason | |
| 234 | + end | |
| 235 | + end | |
| 236 | + | |
| 237 | + def fetch_doc(url, opts) | |
| 238 | + body = fetch(url, opts) | |
| 239 | + Nokogiri::HTML.parse(body, nil, 'UTF-8') | |
| 240 | + end | |
| 241 | + | |
| 242 | + def fetch_json(url, opts) | |
| 243 | + JSON.parse fetch(url, opts) | |
| 244 | + end | |
| 245 | + | |
| 246 | + def get_npm_version(package, opts) | |
| 247 | + json = fetch_json("https://registry.npmjs.com/#{package}", opts) | |
| 248 | + json['dist-tags']['latest'] | |
| 249 | + end | |
| 250 | + | |
| 251 | + def get_latest_github_release(owner, repo, opts) | |
| 252 | + release = fetch_json("https://api.github.com/repos/#{owner}/#{repo}/releases/latest", opts) | |
| 253 | + tag_name = release['tag_name'] | |
| 254 | + tag_name.start_with?('v') ? tag_name[1..-1] : tag_name | |
| 255 | + end | |
| 256 | + | |
| 257 | + def get_github_tags(owner, repo, opts) | |
| 258 | + fetch_json("https://api.github.com/repos/#{owner}/#{repo}/tags", opts) | |
| 259 | + end | |
| 260 | + | |
| 261 | + def get_github_file_contents(owner, repo, path, opts) | |
| 262 | + json = fetch_json("https://api.github.com/repos/#{owner}/#{repo}/contents/#{path}", opts) | |
| 263 | + Base64.decode64(json['content']) | |
| 264 | + end | |
| 147 | 265 | end |
| 148 | 266 | end |
| @@ -91,19 +91,10 @@ module Docs · 90 unmodified lines | ||
| 91 | 91 | super |
| 92 | 92 | end |
| 93 | 93 | |
| 94 | − def decode_cloudflare_email(str) | |
| 95 | − mask = "0x#{str[0..1]}".hex | 0 | |
| 96 | − result = '' | |
| 97 | − | |
| 98 | − str.chars.drop(2).each_slice(2) do |slice| | |
| 99 | − result += "%" + "0#{("0x#{slice.join}".hex ^ mask).to_s(16)}"[-2..-1] | |
| 100 | − end | |
| 101 | − | |
| 102 | − URI.decode(result) | |
| 103 | − end | |
| 104 | − | |
| 105 | 94 | def clean_path(path) |
| 106 | − path.gsub %r{[!;:]+}, '-' | |
| 95 | + path = path.gsub %r{[!;:]}, '-' | |
| 96 | + path = path.gsub %r{\+}, '_plus_' | |
| 97 | + path | |
| 107 | 98 | end |
| 108 | 99 | end |
| 109 | 100 | end |
| @@ -14,32 +14,14 @@ module Docs · 13 unmodified lines | ||
| 14 | 14 | end |
| 15 | 15 | |
| 16 | 16 | def as_json |
| 17 | − indexed_docs.map do |doc| | |
| 18 | − json = doc.as_json | |
| 19 | − json[:mtime] = doc_mtime(doc) | |
| 20 | − json[:db_size] = doc_db_size(doc) | |
| 21 | − json | |
| 17 | + @docs.each_with_object [] do |doc, result| | |
| 18 | + next unless @store.exist?(doc.meta_path) | |
| 19 | + result << JSON.parse(@store.read(doc.meta_path)) | |
| 22 | 20 | end |
| 23 | 21 | end |
| 24 | 22 | |
| 25 | 23 | def to_json |
| 26 | − JSON.generate(as_json) | |
| 27 | − end | |
| 28 | − | |
| 29 | − private | |
| 30 | − | |
| 31 | − def indexed_docs | |
| 32 | − @docs.select do |doc| | |
| 33 | − @store.exist?(doc.index_path) && @store.exist?(doc.db_path) | |
| 34 | − end | |
| 35 | − end | |
| 36 | − | |
| 37 | − def doc_mtime(doc) | |
| 38 | − [@store.mtime(doc.index_path).to_i, @store.mtime(doc.db_path).to_i].max | |
| 39 | − end | |
| 40 | − | |
| 41 | − def doc_db_size(doc) | |
| 42 | − @store.size(doc.db_path) | |
| 24 | + JSON.pretty_generate(as_json) | |
| 43 | 25 | end |
| 44 | 26 | end |
| 45 | 27 | end |
| @@ -1,5 +1,7 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Requester < Typhoeus::Hydra |
| 3 | + include Instrumentable | |
| 4 | + | |
| 3 | 5 | attr_reader :request_options |
| 4 | 6 | |
| 5 | 7 | def self.run(urls, options = {}, &block) |
| @@ -20,7 +22,7 @@ module Docs · 14 unmodified lines | ||
| 20 | 22 | def initialize(options = {}) |
| 21 | 23 | @request_options = options.extract!(:request_options)[:request_options].try(:dup) || {} |
| 22 | 24 | options[:max_concurrency] ||= 20 |
| 23 | − options[:pipelining] = false | |
| 25 | + options[:pipelining] = 0 | |
| 24 | 26 | super |
| 25 | 27 | end |
| 26 | 28 | |
| @@ -52,9 +54,11 @@ module Docs · 25 unmodified lines | ||
| 52 | 54 | end |
| 53 | 55 | |
| 54 | 56 | def handle_response(response) |
| 55 | − on_response.each do |callback| | |
| 56 | − result = callback.call(response) | |
| 57 | − result.each { |url| request(url) } if result.is_a? Array | |
| 57 | + instrument 'handle_response.requester', url: response.url do | |
| 58 | + on_response.each do |callback| | |
| 59 | + result = callback.call(response) | |
| 60 | + result.each { |url| request(url) } if result.is_a?(Array) | |
| 61 | + end | |
| 58 | 62 | end |
| 59 | 63 | end |
| 60 | 64 | end |
| @@ -41,7 +41,7 @@ module Docs · 40 unmodified lines | ||
| 41 | 41 | self.html_filters = FilterStack.new |
| 42 | 42 | self.text_filters = FilterStack.new |
| 43 | 43 | |
| 44 | − html_filters.push 'apply_base_url', 'container', 'clean_html', 'normalize_urls', 'internal_urls', 'normalize_paths' | |
| 44 | + html_filters.push 'apply_base_url', 'container', 'clean_html', 'normalize_urls', 'internal_urls', 'normalize_paths', 'parse_cf_email' | |
| 45 | 45 | text_filters.push 'images' # ensure the images filter runs after all html filters |
| 46 | 46 | text_filters.push 'inner_html', 'clean_text', 'attribution' |
| 47 | 47 | |
| @@ -1,14 +1,13 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class FileScraper < Scraper |
| 3 | + SOURCE_DIRECTORY = File.expand_path '../../../../../docs', __FILE__ | |
| 4 | + | |
| 3 | 5 | Response = Struct.new :body, :url |
| 4 | 6 | |
| 5 | 7 | class << self |
| 6 | − attr_accessor :dir | |
| 7 | − | |
| 8 | 8 | def inherited(subclass) |
| 9 | 9 | super |
| 10 | 10 | subclass.base_url = base_url |
| 11 | − subclass.dir = dir | |
| 12 | 11 | end |
| 13 | 12 | end |
| 14 | 13 | |
| @@ -16,13 +15,25 @@ module Docs · 1 unmodified line | ||
| 16 | 15 | |
| 17 | 16 | html_filters.push 'clean_local_urls' |
| 18 | 17 | |
| 18 | + def source_directory | |
| 19 | + @source_directory ||= File.join(SOURCE_DIRECTORY, self.class.path) | |
| 20 | + end | |
| 21 | + | |
| 19 | 22 | private |
| 20 | 23 | |
| 24 | + def assert_source_directory_exists | |
| 25 | + unless Dir.exists?(source_directory) | |
| 26 | + raise SetupError, "The #{self.class.name} scraper requires the original documentation files to be stored in the \"#{source_directory}\" directory." | |
| 27 | + end | |
| 28 | + end | |
| 29 | + | |
| 21 | 30 | def request_one(url) |
| 22 | − Response.new read_file(file_path_for(url)), URL.parse(url) | |
| 31 | + assert_source_directory_exists | |
| 32 | + Response.new read_file(File.join(source_directory, url_to_path(url))), URL.parse(url) | |
| 23 | 33 | end |
| 24 | 34 | |
| 25 | 35 | def request_all(urls) |
| 36 | + assert_source_directory_exists | |
| 26 | 37 | queue = [urls].flatten |
| 27 | 38 | until queue.empty? |
| 28 | 39 | result = yield request_one(queue.shift) |
| @@ -34,12 +45,15 @@ module Docs · 5 unmodified lines | ||
| 34 | 45 | response.body.present? |
| 35 | 46 | end |
| 36 | 47 | |
| 37 | − def file_path_for(url) | |
| 38 | − File.join self.class.dir, url.remove(base_url.to_s) | |
| 48 | + def url_to_path(url) | |
| 49 | + url.remove(base_url.to_s) | |
| 39 | 50 | end |
| 40 | 51 | |
| 41 | 52 | def read_file(path) |
| 42 | − File.read(path) rescue nil | |
| 53 | + File.read(path) | |
| 54 | + rescue | |
| 55 | + instrument 'warn.doc', msg: "Failed to open file: #{path}" | |
| 56 | + nil | |
| 43 | 57 | end |
| 44 | 58 | end |
| 45 | 59 | end |
| @@ -13,6 +13,8 @@ module Docs · 12 unmodified lines | ||
| 13 | 13 | end |
| 14 | 14 | end |
| 15 | 15 | |
| 16 | + @@rate_limiter = nil | |
| 17 | + | |
| 16 | 18 | self.params = {} |
| 17 | 19 | self.headers = { 'User-Agent' => 'DevDocs' } |
| 18 | 20 | self.force_gzip = false |
| @@ -24,6 +26,15 @@ module Docs · 5 unmodified lines | ||
| 24 | 26 | end |
| 25 | 27 | |
| 26 | 28 | def request_all(urls, &block) |
| 29 | + if options[:rate_limit] | |
| 30 | + if @@rate_limiter | |
| 31 | + @@rate_limiter.limit = options[:rate_limit] | |
| 32 | + else | |
| 33 | + @@rate_limiter = RateLimiter.new(options[:rate_limit]) | |
| 34 | + Typhoeus.before(&@@rate_limiter.to_proc) | |
| 35 | + end | |
| 36 | + end | |
| 37 | + | |
| 27 | 38 | Requester.run urls, request_options: request_options, &block |
| 28 | 39 | end |
| 29 | 40 | |
| @@ -35,7 +46,11 @@ module Docs · 5 unmodified lines | ||
| 35 | 46 | |
| 36 | 47 | def process_response?(response) |
| 37 | 48 | if response.error? |
| 38 | − raise "Error status code (#{response.code}): #{response.return_message}\n#{response.url}" | |
| 49 | + raise <<~ERROR | |
| 50 | + Error status code (#{response.code}): #{response.return_message} | |
| 51 | + #{response.url} | |
| 52 | + #{JSON.pretty_generate(response.headers).slice(2..-3)} | |
| 53 | + ERROR | |
| 39 | 54 | elsif response.blank? |
| 40 | 55 | raise "Empty response body: #{response.url}" |
| 41 | 56 | end |
| @@ -161,5 +176,35 @@ module Docs · 119 unmodified lines | ||
| 161 | 176 | super.merge! redirections: self.class.redirections |
| 162 | 177 | end |
| 163 | 178 | end |
| 179 | + | |
| 180 | + class RateLimiter | |
| 181 | + attr_accessor :limit | |
| 182 | + | |
| 183 | + def initialize(limit) | |
| 184 | + @limit = limit | |
| 185 | + @minute = nil | |
| 186 | + @counter = 0 | |
| 187 | + end | |
| 188 | + | |
| 189 | + def call(*) | |
| 190 | + if @minute != Time.now.min | |
| 191 | + @minute = Time.now.min | |
| 192 | + @counter = 0 | |
| 193 | + end | |
| 194 | + | |
| 195 | + @counter += 1 | |
| 196 | + | |
| 197 | + if @counter >= @limit | |
| 198 | + wait = Time.now.end_of_minute.to_i - Time.now.to_i + 1 | |
| 199 | + sleep wait | |
| 200 | + end | |
| 201 | + | |
| 202 | + true | |
| 203 | + end | |
| 204 | + | |
| 205 | + def to_proc | |
| 206 | + method(:call).to_proc | |
| 207 | + end | |
| 208 | + end | |
| 164 | 209 | end |
| 165 | 210 | end |
| @@ -72,7 +72,7 @@ module Docs · 71 unmodified lines | ||
| 72 | 72 | |
| 73 | 73 | if base == dest |
| 74 | 74 | '' |
| 75 | − elsif dest.start_with? File.join(base, '') | |
| 75 | + elsif dest.start_with?(::File.join(base, '')) | |
| 76 | 76 | url.path[(path.length)..-1] |
| 77 | 77 | end |
| 78 | 78 | end |
| @@ -7,7 +7,7 @@ module Docs · 6 unmodified lines | ||
| 7 | 7 | at_css('h1').content = 'Angular Documentation' |
| 8 | 8 | end |
| 9 | 9 | |
| 10 | − css('br', 'hr', '.material-icons', '.header-link').remove | |
| 10 | + css('br', 'hr', '.material-icons', '.header-link', '.breadcrumb').remove | |
| 11 | 11 | |
| 12 | 12 | css('.content', 'article', '.api-header', 'section', '.instance-member').each do |node| |
| 13 | 13 | node.before(node.children).remove |
| @@ -86,6 +86,14 @@ module Docs · 72 unmodified lines | ||
| 86 | 86 | at_css('h1').content = subpath.remove('api/') |
| 87 | 87 | end |
| 88 | 88 | |
| 89 | + css('th h3').each do |node| | |
| 90 | + node.name = 'span' | |
| 91 | + end | |
| 92 | + | |
| 93 | + css('code code').each do |node| | |
| 94 | + node.before(node.children).remove | |
| 95 | + end | |
| 96 | + | |
| 89 | 97 | doc |
| 90 | 98 | end |
| 91 | 99 | end |
| @@ -16,6 +16,8 @@ module Docs · 15 unmodified lines | ||
| 16 | 16 | node.next_element.content.remove('@angular/') |
| 17 | 17 | elsif at_css('.api-type-label.module') |
| 18 | 18 | name.split('/').first |
| 19 | + elsif slug.start_with?('api/') | |
| 20 | + slug.split('/').second | |
| 19 | 21 | else |
| 20 | 22 | 'Miscellaneous' |
| 21 | 23 | end |
| @@ -4,8 +4,8 @@ module Docs · 3 unmodified lines | ||
| 4 | 4 | def call |
| 5 | 5 | @doc = at_css('#page-content') |
| 6 | 6 | |
| 7 | − css('.__cf_email__').each do |node| | |
| 8 | − node.replace(decode_cloudflare_email(node['data-cfemail'])) | |
| 7 | + css('font').each do |node| | |
| 8 | + node.before(node.children).remove | |
| 9 | 9 | end |
| 10 | 10 | |
| 11 | 11 | doc |
| @@ -11,14 +11,38 @@ module Docs · 10 unmodified lines | ||
| 11 | 11 | end |
| 12 | 12 | |
| 13 | 13 | def get_type |
| 14 | − if slug.include?('module') | |
| 15 | − if name =~ /\A[a-z]/ && node = css('.toctree-l2.current').last | |
| 16 | − "Modules: #{node.content.remove(' Modules')}" | |
| 14 | + if version == '2.4' | |
| 15 | + if slug.include?('module') | |
| 16 | + if name =~ /\A[a-z]/ && node = css('.toctree-l2.current').last | |
| 17 | + return "Modules: #{node.content.remove(' Modules')}" | |
| 18 | + else | |
| 19 | + return 'Modules' | |
| 20 | + end | |
| 21 | + end | |
| 22 | + end | |
| 23 | + | |
| 24 | + if slug =~ /\Acli\// | |
| 25 | + 'CLI Reference' | |
| 26 | + elsif slug =~ /\Anetwork\// | |
| 27 | + 'Network' | |
| 28 | + elsif slug =~ /\Aplugins\// | |
| 29 | + if name =~ /\A[a-z]/ && node = css('.toctree-l3.current').last | |
| 30 | + "Plugins: #{node.content.sub(/ Plugins.*/, '')}" | |
| 31 | + else | |
| 32 | + 'Plugins' | |
| 33 | + end | |
| 34 | + elsif slug =~ /\Amodules\// | |
| 35 | + if slug =~ /\Amodules\/list_/ || slug=~ /_maintained\z/ | |
| 36 | + 'Modules: Categories' | |
| 17 | 37 | else |
| 18 | 38 | 'Modules' |
| 19 | 39 | end |
| 20 | 40 | elsif slug.include?('playbook') |
| 21 | 41 | 'Playbooks' |
| 42 | + elsif slug =~ /\Auser_guide\// | |
| 43 | + 'Guides: User' | |
| 44 | + elsif slug =~ /\Ascenario_guides\// | |
| 45 | + 'Guides: Scenarios' | |
| 22 | 46 | elsif slug.include?('guide') |
| 23 | 47 | 'Guides' |
| 24 | 48 | else |
| @@ -0,0 +1,38 @@ | ||
| 1 | +module Docs | |
| 2 | + class Babel | |
| 3 | + class CleanHtmlFilter < Filter | |
| 4 | + def call | |
| 5 | + if root_page? | |
| 6 | + doc.inner_html = '<h1>Babel</h1>' | |
| 7 | + return doc | |
| 8 | + end | |
| 9 | + | |
| 10 | + header = at_css('.docs-header .col-md-12') | |
| 11 | + @doc = at_css('.docs-content') | |
| 12 | + doc.prepend_child(header) | |
| 13 | + | |
| 14 | + css('.btn-clipboard', '.package-links').remove | |
| 15 | + | |
| 16 | + css('.col-md-12', 'h1 a', 'h2 a', 'h3 a', 'h4 a', 'h5 a', 'h5 a').each do |node| | |
| 17 | + node.before(node.children).remove | |
| 18 | + end | |
| 19 | + | |
| 20 | + css('div.highlighter-rouge').each do |node| | |
| 21 | + pre = node.at_css('pre') | |
| 22 | + | |
| 23 | + lang = node['class'][/language-(\w+)/, 1] | |
| 24 | + lang = 'bash' if lang == 'sh' | |
| 25 | + pre['data-language'] = lang | |
| 26 | + | |
| 27 | + pre.remove_attribute('class') | |
| 28 | + pre.content = pre.content | |
| 29 | + node.replace(pre) | |
| 30 | + end | |
| 31 | + | |
| 32 | + css('code').remove_attr('class') | |
| 33 | + | |
| 34 | + doc | |
| 35 | + end | |
| 36 | + end | |
| 37 | + end | |
| 38 | +end | |
| @@ -0,0 +1,40 @@ | ||
| 1 | +module Docs | |
| 2 | + class Babel | |
| 3 | + class EntriesFilter < Docs::EntriesFilter | |
| 4 | + def get_name | |
| 5 | + at_css('h1').content | |
| 6 | + end | |
| 7 | + | |
| 8 | + def get_type | |
| 9 | + if subpath.start_with?('plugins/preset') | |
| 10 | + 'Presets' | |
| 11 | + elsif subpath.start_with?('plugins/transform') | |
| 12 | + 'Transform Plugins' | |
| 13 | + elsif subpath.start_with?('plugins/minify') | |
| 14 | + 'Minification' | |
| 15 | + elsif subpath.start_with?('plugins/syntax') | |
| 16 | + 'Syntax Plugins' | |
| 17 | + elsif subpath.start_with?('plugins') | |
| 18 | + 'Plugins' | |
| 19 | + elsif subpath.start_with?('usage/') | |
| 20 | + 'Usage' | |
| 21 | + elsif subpath.start_with?('core-packages/') | |
| 22 | + 'Core Packages' | |
| 23 | + else | |
| 24 | + 'Miscellaneous' | |
| 25 | + end | |
| 26 | + end | |
| 27 | + | |
| 28 | + def additional_entries | |
| 29 | + return [] unless slug.include?('api') | |
| 30 | + | |
| 31 | + css('h2').each_with_object [] do |node, entries| | |
| 32 | + name = node.content.strip | |
| 33 | + next unless name.start_with?('babel.') | |
| 34 | + name.sub! %r{\(.*}, '()' | |
| 35 | + entries << [name, node['id']] | |
| 36 | + end | |
| 37 | + end | |
| 38 | + end | |
| 39 | + end | |
| 40 | +end | |
| @@ -0,0 +1,74 @@ | ||
| 1 | +module Docs | |
| 2 | + class Bash | |
| 3 | + class CleanHtmlFilter < Filter | |
| 4 | + def call | |
| 5 | + # Remove the navigation header and footer and the lines underneath and above it | |
| 6 | + at_css('.header + hr').remove | |
| 7 | + line_above = at_xpath('//div[@class="header"]/preceding::hr[1]') | |
| 8 | + line_above.remove unless line_above.nil? | |
| 9 | + css('.header').remove | |
| 10 | + | |
| 11 | + # Remove chapter and section numbers from title | |
| 12 | + title_node = at_css('h1, h2, h3, h4, h5, h6') | |
| 13 | + title_node.content = title_node.content.gsub(/(\d+\.?)+/, '').strip | |
| 14 | + title_node.name = 'h1' | |
| 15 | + | |
| 16 | + # Remove the "D. " from names like "D. Concept Index" and "D. Function Index" | |
| 17 | + title_node.content = title_node.content[3..-1] if title_node.content.start_with?("D. ") | |
| 18 | + | |
| 19 | + # Remove columns containing a single space from tables | |
| 20 | + # In the original reference they are used to add width between two columns | |
| 21 | + xpath('//td[text()=" " and not(descendant::*)]').remove | |
| 22 | + | |
| 23 | + # Add id's to additional entry nodes | |
| 24 | + css('dl > dt > code').each do |node| | |
| 25 | + # Only take the direct text (i.e. "<div>Hello <span>World</span></div>" becomes "Hello") | |
| 26 | + node['id'] = node.xpath('text()').to_s.strip | |
| 27 | + end | |
| 28 | + | |
| 29 | + # Fix hashes of index entries so they link to the correct hash on the linked page | |
| 30 | + css('table[class^=index-] td[valign=top] > a').each do |node| | |
| 31 | + path = node['href'].split('#')[0] | |
| 32 | + hash = node.content | |
| 33 | + | |
| 34 | + # Fix the index entries linking to the Special Parameters page | |
| 35 | + # There are multiple index entries that should link to the same paragraph on that page | |
| 36 | + # Example: the documentation for "$!" is equal to the documentation for "!" | |
| 37 | + if path.downcase.include?('special-parameters') | |
| 38 | + if hash.size > 1 && hash[0] == '$' | |
| 39 | + hash = hash[1..-1] | |
| 40 | + end | |
| 41 | + end | |
| 42 | + | |
| 43 | + node['href'] = path + '#' + hash | |
| 44 | + end | |
| 45 | + | |
| 46 | + # Fix index table letter hashes (the "Jump to" hashes) | |
| 47 | + css('table[class^=index-] th > a').each do |node| | |
| 48 | + node['id'] = node['name'] | |
| 49 | + end | |
| 50 | + | |
| 51 | + # Remove the rows with a horizontal line in them from the index tables | |
| 52 | + css('td[colspan="4"]').remove | |
| 53 | + | |
| 54 | + # Remove additional text from menu entry and index entry cells | |
| 55 | + css('td[valign=top]').each do |node| | |
| 56 | + link = node.at_css('a') | |
| 57 | + node.children = link unless link.nil? | |
| 58 | + end | |
| 59 | + | |
| 60 | + css('tt', 'code', 'table').remove_attr('class') | |
| 61 | + | |
| 62 | + css('tt').each do |node| | |
| 63 | + node.name = 'code' | |
| 64 | + end | |
| 65 | + | |
| 66 | + css('pre').each do |node| | |
| 67 | + node.content = node.content | |
| 68 | + end | |
| 69 | + | |
| 70 | + doc | |
| 71 | + end | |
| 72 | + end | |
| 73 | + end | |
| 74 | +end | |
| @@ -0,0 +1,56 @@ | ||
| 1 | +module Docs | |
| 2 | + class Bash | |
| 3 | + class EntriesFilter < Docs::EntriesFilter | |
| 4 | + def get_name | |
| 5 | + name = at_css('hr + a + *').content.gsub(/(\d+\.?)+/, '') | |
| 6 | + | |
| 7 | + # Remove the "D. " from names like "D. Concept Index" and "D. Function Index" | |
| 8 | + name = name[3..-1] if name.start_with?('D. ') | |
| 9 | + | |
| 10 | + name | |
| 11 | + end | |
| 12 | + | |
| 13 | + def get_type | |
| 14 | + return 'Manual: Appendices' if name.start_with?('Appendix') | |
| 15 | + return 'Manual: Indexes' if at_css('a[rel=up]').content.include?("Index") | |
| 16 | + "Manual" | |
| 17 | + end | |
| 18 | + | |
| 19 | + def additional_entries | |
| 20 | + entry_type = { | |
| 21 | + "Function Index" => "Functions", | |
| 22 | + "Index of Shell Builtin Commands" => "Builtin Commands", | |
| 23 | + "Index of Shell Reserved Words" => "Reserved Words", | |
| 24 | + "Parameter and Variable Index" => "Parameters and Variables" | |
| 25 | + }[name] | |
| 26 | + | |
| 27 | + # Only extract additional entries from certain index pages | |
| 28 | + return [] if entry_type.nil? | |
| 29 | + | |
| 30 | + entries = [] | |
| 31 | + | |
| 32 | + css('table[class^=index-] td[valign=top] > a').each_slice(2) do |entry_node, section_node| | |
| 33 | + entry_name = entry_node.content | |
| 34 | + | |
| 35 | + page = section_node['href'].split('#')[0] | |
| 36 | + hash = entry_name | |
| 37 | + | |
| 38 | + # The Special Parameters page has multiple additional entries which should link to the same paragraph | |
| 39 | + # Example: the documentation for "$!" is equal to the documentation for "!" | |
| 40 | + if page == 'special-parameters' | |
| 41 | + if hash.size > 1 && hash[0] == '$' | |
| 42 | + hash = hash[1..-1] | |
| 43 | + end | |
| 44 | + end | |
| 45 | + | |
| 46 | + # Construct path to the page which the index links to | |
| 47 | + entry_path = '/html_node/' + page + '#' + hash | |
| 48 | + | |
| 49 | + entries << [entry_name, entry_path, entry_type] | |
| 50 | + end | |
| 51 | + | |
| 52 | + entries | |
| 53 | + end | |
| 54 | + end | |
| 55 | + end | |
| 56 | +end | |
| @@ -0,0 +1,36 @@ | ||
| 1 | +module Docs | |
| 2 | + class Bluebird | |
| 3 | + class CleanHtmlFilter < Filter | |
| 4 | + def call | |
| 5 | + @doc = at_css('.post') | |
| 6 | + | |
| 7 | + css('hr').remove | |
| 8 | + | |
| 9 | + css('.api-code-section').each do |node| | |
| 10 | + node.previous_element.remove | |
| 11 | + end | |
| 12 | + | |
| 13 | + css('.post-header', '.post-content', '.api-reference-menu', '.api-code-section', 'markdown', '.highlight', 'code code').each do |node| | |
| 14 | + node.before(node.children).remove | |
| 15 | + end | |
| 16 | + | |
| 17 | + at_css('> h2:first-child').name = 'h1' unless at_css('h1') | |
| 18 | + | |
| 19 | + css('.header-anchor[name]').each do |node| | |
| 20 | + node.parent['id'] = node['name'] | |
| 21 | + end | |
| 22 | + | |
| 23 | + css('pre').each do |node| | |
| 24 | + node.content = node.content | |
| 25 | + node['data-language'] = 'javascript' | |
| 26 | + end | |
| 27 | + | |
| 28 | + css('.info-box').each do |node| | |
| 29 | + node.name = 'blockquote' | |
| 30 | + end | |
| 31 | + | |
| 32 | + doc | |
| 33 | + end | |
| 34 | + end | |
| 35 | + end | |
| 36 | +end | |
| @@ -0,0 +1,48 @@ | ||
| 1 | +module Docs | |
| 2 | + class Bluebird | |
| 3 | + class EntriesFilter < Docs::EntriesFilter | |
| 4 | + TYPE_MAP = { | |
| 5 | + Core: %w(new-promise then spread catch error finally bind promise.join | |
| 6 | + promise.try promise.method promise.resolve promise.reject core | |
| 7 | + promise.bind), | |
| 8 | + 'Synchronous inspection': %w(promiseinspection isfulfilled isrejected | |
| 9 | + ispending iscancelled value reason), | |
| 10 | + Collections: %w(promise.all promise.props promise.any promise.some | |
| 11 | + promise.map promise.reduce promise.filter promise.each | |
| 12 | + promise.mapseries promise.race all props any some map reduce filter | |
| 13 | + each mapseries), | |
| 14 | + 'Resource management': %w(promise.using disposer), | |
| 15 | + Promisification: %w(promise.promisify promise.promisifyall | |
| 16 | + promise.fromcallback ascallback), | |
| 17 | + Timers: %w(delay timeout promise.delay), | |
| 18 | + Cancellation: %w(cancel), | |
| 19 | + Generators: %w(promise.coroutine promise.coroutine.addyieldhandler), | |
| 20 | + Utility: %w(tap tapcatch call get return throw catchreturn catchthrow | |
| 21 | + reflect promise.getnewlibrarycopy promise.noconflict | |
| 22 | + promise.setscheduler), | |
| 23 | + 'Built-in error types': %w(operationalerror timeouterror | |
| 24 | + cancellationerror aggregateerror), | |
| 25 | + Configuration: %w(global-rejection-events local-rejection-events | |
| 26 | + done promise.config suppressunhandledrejections | |
| 27 | + promise.onpossiblyunhandledrejection promise.bind | |
| 28 | + promise.onunhandledrejectionhandled), | |
| 29 | + } | |
| 30 | + | |
| 31 | + def get_name | |
| 32 | + name = at_css('h1').content.strip | |
| 33 | + name << '()' if doc.to_html.include?("#{name}(") | |
| 34 | + name | |
| 35 | + end | |
| 36 | + | |
| 37 | + def get_type | |
| 38 | + if slug.start_with?('api') | |
| 39 | + TYPE_MAP.each do |key, value| | |
| 40 | + return key.to_s if value.include?(slug.remove('api/')) | |
| 41 | + end | |
| 42 | + else | |
| 43 | + 'Guides' | |
| 44 | + end | |
| 45 | + end | |
| 46 | + end | |
| 47 | + end | |
| 48 | +end | |
| @@ -27,7 +27,7 @@ module Docs · 26 unmodified lines | ||
| 27 | 27 | node.remove_attribute('data-example-id') |
| 28 | 28 | prev = node.previous_element |
| 29 | 29 | prev = prev.previous_element until prev['id'] |
| 30 | − node.inner_html = %(<a href="#{current_url}/##{prev['id']}">Open example on getbootstrap.com</a>) | |
| 30 | + node.inner_html = %(<a href="#{current_url}##{prev['id']}">Open example on getbootstrap.com</a>) | |
| 31 | 31 | end |
| 32 | 32 | end |
| 33 | 33 | |
| @@ -39,10 +39,6 @@ module Docs · 5 unmodified lines | ||
| 39 | 39 | node['class'] = 'col' |
| 40 | 40 | end |
| 41 | 41 | |
| 42 | − css('.__cf_email__').each do |node| | |
| 43 | − node.replace(decode_cloudflare_email(node['data-cfemail'])) | |
| 44 | − end | |
| 45 | − | |
| 46 | 42 | css('figure.highlight').each do |node| |
| 47 | 43 | code = node.at_css('code') |
| 48 | 44 | node['data-language'] = code['data-lang'] |
| @@ -17,6 +17,8 @@ module Docs · 16 unmodified lines | ||
| 17 | 17 | end |
| 18 | 18 | |
| 19 | 19 | css('.bd-example', '.responsive-utilities-test').each do |node| |
| 20 | + next unless node.previous_element | |
| 21 | + | |
| 20 | 22 | if node.previous_element['class'].try(:include?, 'bd-example') |
| 21 | 23 | node.remove |
| 22 | 24 | else |
| @@ -26,7 +28,7 @@ module Docs · 3 unmodified lines | ||
| 26 | 28 | node.remove_attribute('data-example-id') |
| 27 | 29 | prev = node.previous_element |
| 28 | 30 | prev = prev.previous_element until prev['id'] |
| 29 | − node.inner_html = %(<a href="#{current_url}/##{prev['id']}">Open example on getbootstrap.com</a>) | |
| 31 | + node.inner_html = %(<a href="#{current_url}##{prev['id']}">Open example on getbootstrap.com</a>) | |
| 30 | 32 | end |
| 31 | 33 | end |
| 32 | 34 | |
| @@ -38,10 +40,6 @@ module Docs · 5 unmodified lines | ||
| 38 | 40 | node['class'] = 'col' |
| 39 | 41 | end |
| 40 | 42 | |
| 41 | − css('.__cf_email__').each do |node| | |
| 42 | − node.replace(decode_cloudflare_email(node['data-cfemail'])) | |
| 43 | − end | |
| 44 | − | |
| 45 | 43 | css('.highlight').each do |node| |
| 46 | 44 | code = node.at_css('code') |
| 47 | 45 | node['data-language'] = code['data-lang'] |
| @@ -4,12 +4,17 @@ module Docs · 3 unmodified lines | ||
| 4 | 4 | def call |
| 5 | 5 | css('h1').remove if root_page? |
| 6 | 6 | |
| 7 | + css('.t-dcl-rev-aux td[rowspan]').each do |node| | |
| 8 | + rowspan = node['rowspan'].to_i | |
| 9 | + node['rowspan'] = node.ancestors('tbody').css('tr').length if rowspan > 3 | |
| 10 | + end | |
| 11 | + | |
| 7 | 12 | css('#siteSub', '#contentSub', '.printfooter', '.t-navbar', '.editsection', '#toc', |
| 8 | 13 | '.t-dsc-sep', '.t-dcl-sep', '#catlinks', '.ambox-notice', '.mw-cite-backlink', |
| 9 | 14 | '.t-sdsc-sep:first-child:last-child', '.t-example-live-link', |
| 10 | 15 | '.t-dcl-rev-num > .t-dcl-rev-aux ~ tr:not(.t-dcl-rev-aux) > td:nth-child(2)').remove |
| 11 | 16 | |
| 12 | − css('#bodyContent', '.mw-content-ltr', 'span[style]', 'div[class^="t-ref"]', | |
| 17 | + css('#bodyContent', '.mw-content-ltr', 'span[style]', 'div[class^="t-ref"]', '.t-image', | |
| 13 | 18 | 'th > div', 'td > div', '.t-dsc-see', '.mainpagediv', 'code > b', 'tbody').each do |node| |
| 14 | 19 | node.before(node.children).remove |
| 15 | 20 | end |
| @@ -78,7 +83,7 @@ module Docs · 62 unmodified lines | ||
| 78 | 83 | end |
| 79 | 84 | node.inner_html = node.inner_html.strip |
| 80 | 85 | node << '.' if node.content =~ /[a-zA-Z0-9\)]\z/ |
| 81 | − node.remove if node.content.blank? | |
| 86 | + node.remove if node.content.blank? && !node.at_css('img') | |
| 82 | 87 | end |
| 83 | 88 | |
| 84 | 89 | css('pre').each do |node| |
| @@ -100,6 +105,10 @@ module Docs · 15 unmodified lines | ||
| 100 | 105 | node.content = node.content |
| 101 | 106 | end |
| 102 | 107 | |
| 108 | + css('img').each do |node| | |
| 109 | + node['src'] = node['src'].sub! %r{http://en.cppreference.com/common/([^"']+?)\.svg}, 'http://upload.cppreference.com/mwiki/\1.svg' | |
| 110 | + end | |
| 111 | + | |
| 103 | 112 | doc |
| 104 | 113 | end |
| 105 | 114 | end |
| @@ -20,6 +20,7 @@ module Docs · 19 unmodified lines | ||
| 20 | 20 | 'prop_sf' => 'Properties: Source Files', |
| 21 | 21 | 'prop_test' => 'Properties: Tests', |
| 22 | 22 | 'prop_tgt' => 'Properties: Targets', |
| 23 | + 'envvar' => 'Environment Variables', | |
| 23 | 24 | 'variable' => 'Variables' } |
| 24 | 25 | |
| 25 | 26 | def get_name |
| @@ -4,6 +4,12 @@ module Docs · 3 unmodified lines | ||
| 4 | 4 | def call |
| 5 | 5 | @doc = doc.at_css('#page') |
| 6 | 6 | |
| 7 | + css('.algolia__search-content').remove | |
| 8 | + | |
| 9 | + css('.algolia__initial-content').each do |node| | |
| 10 | + node.before(node.children).remove | |
| 11 | + end | |
| 12 | + | |
| 7 | 13 | while doc.element_children.length == 1 |
| 8 | 14 | doc.first_element_child.before(doc.first_element_child.children).remove |
| 9 | 15 | end |
| @@ -2,7 +2,7 @@ module Docs · 1 unmodified line | ||
| 2 | 2 | class Codeception |
| 3 | 3 | class EntriesFilter < Docs::EntriesFilter |
| 4 | 4 | def get_name |
| 5 | − name = (at_css('h1') || at_css('h2')).content | |
| 5 | + name = (at_css('.js-algolia__initial-content h1') || at_css('.js-algolia__initial-content h2')).content | |
| 6 | 6 | |
| 7 | 7 | if number = subpath[/\A\d+/] |
| 8 | 8 | name.prepend "#{number.to_i}. " |
| @@ -20,8 +20,8 @@ module Docs · 19 unmodified lines | ||
| 20 | 20 | def additional_entries |
| 21 | 21 | return [] unless subpath.start_with?('helpers') && subpath != 'helpers/' |
| 22 | 22 | |
| 23 | − css('h2').each_with_object [] do |node, entries| | |
| 24 | − next if node['id'] == 'access-from-helpers' | |
| 23 | + css('h2, h3').each_with_object [] do |node, entries| | |
| 24 | + next if node['id'] == 'access-from-helpers' || node.content !~ /\s*[a-z_]/ | |
| 25 | 25 | entries << ["#{node.content} (#{name})", node['id']] |
| 26 | 26 | end |
| 27 | 27 | end |
| @@ -2,7 +2,7 @@ module Docs · 1 unmodified line | ||
| 2 | 2 | class Cordova |
| 3 | 3 | class CleanHtmlFilter < Filter |
| 4 | 4 | def call |
| 5 | − @doc = at_css('.page-content > div') | |
| 5 | + @doc = at_css('#page-toc-source') || at_css('.page-content > div') | |
| 6 | 6 | |
| 7 | 7 | at_css('h1').content = 'Apache Cordova' if root_page? |
| 8 | 8 | |
| @@ -0,0 +1,21 @@ | ||
| 1 | +# frozen_string_literal: true | |
| 2 | + | |
| 3 | +module Docs | |
| 4 | + class ParseCfEmailFilter < Filter | |
| 5 | + def call | |
| 6 | + css('.__cf_email__').each do |node| | |
| 7 | + str = node['data-cfemail'] | |
| 8 | + mask = "0x#{str[0..1]}".hex | 0 | |
| 9 | + result = '' | |
| 10 | + | |
| 11 | + str.chars.drop(2).each_slice(2) do |slice| | |
| 12 | + result += "%" + "0#{("0x#{slice.join}".hex ^ mask).to_s(16)}"[-2..-1] | |
| 13 | + end | |
| 14 | + | |
| 15 | + node.replace(URI.decode(result)) | |
| 16 | + end | |
| 17 | + | |
| 18 | + doc | |
| 19 | + end | |
| 20 | + end | |
| 21 | +end | |
| @@ -31,6 +31,8 @@ module Docs · 30 unmodified lines | ||
| 31 | 31 | 'Keywords' |
| 32 | 32 | elsif subpath.start_with?('experimental') |
| 33 | 33 | 'Experimental libraries' |
| 34 | + elsif subpath.start_with?('language/') | |
| 35 | + 'Language' | |
| 34 | 36 | elsif type = at_css('.t-navbar > div:nth-child(4) > :first-child').try(:content) |
| 35 | 37 | type.strip! |
| 36 | 38 | type.remove! ' library' |
| @@ -4,7 +4,6 @@ module Docs · 3 unmodified lines | ||
| 4 | 4 | def call |
| 5 | 5 | html.gsub! File.join(Cpp.base_url, Cpp.root_path), Cpp.base_url[0..-2] |
| 6 | 6 | html.gsub! %r{#{Cpp.base_url}([^"']+?)\.html}, "#{Cpp.base_url}\\1" |
| 7 | − html.gsub! %r{http://en.cppreference.com/common/([^"']+?)\.svg}, 'http://upload.cppreference.com/mwiki/\1.svg' | |
| 8 | 7 | html |
| 9 | 8 | end |
| 10 | 9 | end |
| @@ -16,7 +16,7 @@ module Docs · 15 unmodified lines | ||
| 16 | 16 | end |
| 17 | 17 | |
| 18 | 18 | def api |
| 19 | − @doc = at_css('#main-content') | |
| 19 | + @doc = at_css('.main-content') | |
| 20 | 20 | |
| 21 | 21 | at_css('h1 + p').remove if root_page? |
| 22 | 22 | |
| @@ -32,7 +32,7 @@ module Docs · 31 unmodified lines | ||
| 32 | 32 | if hierarchy && hierarchy.content.include?('Exception') |
| 33 | 33 | 'Exceptions' |
| 34 | 34 | else |
| 35 | − type = at_css('#types-list > ul > .current > a').content | |
| 35 | + type = at_css('.types-list > ul > .current > a').content | |
| 36 | 36 | type = 'Float' if type.start_with?('Float') |
| 37 | 37 | type = 'Int' if type.start_with?('Int') |
| 38 | 38 | type = 'UInt' if type.start_with?('UInt') |
| @@ -6,13 +6,16 @@ module Docs · 5 unmodified lines | ||
| 6 | 6 | 'CSS_Background_and_Borders' => 'Backgrounds & Borders', |
| 7 | 7 | 'CSS_Columns' => 'Multi-column Layout', |
| 8 | 8 | 'CSS_Flexible_Box_Layout' => 'Flexible Box Layout', |
| 9 | + 'CSS_Fonts' => 'Fonts', | |
| 9 | 10 | 'CSS_Grid_Layout' => 'Grid Layout', |
| 10 | − 'CSS_Images' => 'Image Values', | |
| 11 | − 'CSS_Lists_and_Counters' => 'Lists & Counters', | |
| 11 | + 'CSS_Images' => 'Images', | |
| 12 | + 'CSS_Lists_and_Counters' => 'Lists', | |
| 12 | 13 | 'CSS_Transforms' => 'Transforms', |
| 13 | 14 | 'Media_Queries' => 'Media Queries', |
| 15 | + 'filter-function' => 'Filter Effects', | |
| 14 | 16 | 'transform-function' => 'Transforms', |
| 15 | 17 | '@media' => 'Media Queries', |
| 18 | + 'overscroll' => 'Overscroll', | |
| 16 | 19 | 'text-size-adjust' => 'Miscellaneous', |
| 17 | 20 | 'resolved_value' => 'Miscellaneous', |
| 18 | 21 | 'touch-action' => 'Miscellaneous', |
| @@ -34,7 +37,7 @@ module Docs · 15 unmodified lines | ||
| 34 | 37 | "#{super}()" |
| 35 | 38 | elsif slug =~ /\A[a-z]+_/i |
| 36 | 39 | slug.to_s.gsub('_', ' ').gsub('/', ': ') |
| 37 | − elsif slug.start_with?('transform-function') | |
| 40 | + elsif slug.start_with?('transform-function') || slug.start_with?('filter-function') | |
| 38 | 41 | slug.split('/').last + '()' |
| 39 | 42 | else |
| 40 | 43 | super |
| @@ -42,7 +45,7 @@ module Docs · 1 unmodified line | ||
| 42 | 45 | end |
| 43 | 46 | |
| 44 | 47 | def get_type |
| 45 | − if slug.include?('-webkit') || slug.include?('-moz') | |
| 48 | + if slug.include?('-webkit') || slug.include?('-moz') || slug.include?('-ms') | |
| 46 | 49 | 'Extensions' |
| 47 | 50 | elsif type = TYPE_BY_PATH[slug.split('/').first] |
| 48 | 51 | type |
| @@ -51,6 +54,7 @@ module Docs · 2 unmodified lines | ||
| 51 | 54 | type.remove! ' Module' |
| 52 | 55 | type.remove! %r{ Level \d\z} |
| 53 | 56 | type.remove! %r{\(.*\)} |
| 57 | + type.remove! %r{ \d\z} | |
| 54 | 58 | type.sub! 'and', '&' |
| 55 | 59 | type.strip! |
| 56 | 60 | type = 'Grid Layout' if type.include?('Grid Layout') |
| @@ -65,18 +69,22 @@ module Docs · 8 unmodified lines | ||
| 65 | 69 | 'Pseudo-Elements' |
| 66 | 70 | elsif name.start_with?(':') |
| 67 | 71 | 'Selectors' |
| 72 | + elsif name.start_with?('display-') | |
| 73 | + 'Display' | |
| 68 | 74 | else |
| 69 | 75 | 'Miscellaneous' |
| 70 | 76 | end |
| 71 | 77 | end |
| 72 | 78 | |
| 73 | 79 | STATUSES = { |
| 74 | − 'spec-Living' => 0, | |
| 75 | − 'spec-REC' => 1, | |
| 76 | − 'spec-CR' => 2, | |
| 77 | − 'spec-LC' => 3, | |
| 78 | − 'spec-WD' => 4, | |
| 79 | − 'spec-ED' => 5 | |
| 80 | + 'spec-Living' => 0, | |
| 81 | + 'spec-REC' => 1, | |
| 82 | + 'spec-CR' => 2, | |
| 83 | + 'spec-PR' => 3, | |
| 84 | + 'spec-LC' => 4, | |
| 85 | + 'spec-WD' => 5, | |
| 86 | + 'spec-ED' => 6, | |
| 87 | + 'spec-Obsolete' => 7 | |
| 80 | 88 | } |
| 81 | 89 | |
| 82 | 90 | PRIORITY_STATUSES = %w(spec-REC spec-CR) |
| @@ -84,11 +92,14 @@ module Docs · 1 unmodified line | ||
| 84 | 92 | |
| 85 | 93 | def get_spec |
| 86 | 94 | return unless table = at_css('#Specifications + table') || css('.standard-table').last |
| 95 | + | |
| 87 | 96 | specs = table.css('tbody tr').to_a |
| 88 | 97 | # [link, span] |
| 89 | 98 | specs.map! { |node| [node.at_css('> td:nth-child(1) > a'), node.at_css('> td:nth-child(2) > span')] } |
| 90 | 99 | # ignore non-CSS specs |
| 91 | 100 | specs.select! { |pair| pair.first && pair.first['href'] =~ /css|fxtf|fullscreen|svg/i && !pair.first['href'].include?('compat.spec') } |
| 101 | + # ignore specs with no status | |
| 102 | + specs.select! { |pair| pair.second } | |
| 92 | 103 | # ["Spec", "spec-REC"] |
| 93 | 104 | specs.map! { |pair| [pair.first.child.content, pair.second['class']] } |
| 94 | 105 | # sort by status |
| @@ -106,8 +117,8 @@ module Docs · 11 unmodified lines | ||
| 106 | 117 | 'shape' => [ |
| 107 | 118 | %w(rect() Syntax) ], |
| 108 | 119 | 'timing-function' => [ |
| 109 | − %w(cubic-bezier() The_cubic-bezier()_class_of_timing-functions), | |
| 110 | − %w(steps() The_steps()_class_of_timing-functions), | |
| 120 | + %w(cubic-bezier()), | |
| 121 | + %w(steps()), | |
| 111 | 122 | %w(linear linear), |
| 112 | 123 | %w(ease ease), |
| 113 | 124 | %w(ease-in ease-in), |
| @@ -19,6 +19,8 @@ module Docs · 18 unmodified lines | ||
| 19 | 19 | name.split('.')[0..2].join('.') |
| 20 | 20 | elsif name.start_with?('ddmd') |
| 21 | 21 | 'ddmd' |
| 22 | + elsif name.start_with?('rt') | |
| 23 | + 'rt' | |
| 22 | 24 | else |
| 23 | 25 | name.split('.')[0..1].join('.') |
| 24 | 26 | end |
| @@ -0,0 +1,66 @@ | ||
| 1 | +module Docs | |
| 2 | + class Dart | |
| 3 | + class CleanHtmlFilter < Filter | |
| 4 | + def call | |
| 5 | + # Move the title into the main content node in the v1 docs | |
| 6 | + title = at_css('h1.title') | |
| 7 | + if title | |
| 8 | + name = title.children.last.content.strip | |
| 9 | + kind = title.at_css('.kind').content | |
| 10 | + at_css('.main-content').prepend_child("<h1>#{name} #{kind}</h1>") | |
| 11 | + end | |
| 12 | + | |
| 13 | + # Add a title to the homepage of the v2 docs | |
| 14 | + if subpath == 'index.html' && at_css('.main-content > h1').nil? | |
| 15 | + at_css('.main-content').prepend_child('<h1>Dart SDK</h1>') | |
| 16 | + end | |
| 17 | + | |
| 18 | + # Add the library to the main content (it is not always visible in the menu entry) | |
| 19 | + breadcrumbs = at_css('.breadcrumbs').css('li:not(.self-crumb) > a') | |
| 20 | + if breadcrumbs.length > 1 | |
| 21 | + library = breadcrumbs[1].content | |
| 22 | + | |
| 23 | + # Generate the link to the homepage of the library | |
| 24 | + with_hypens = library.gsub(/:/, '-') | |
| 25 | + location = "#{'../' * subpath.count('/')}#{with_hypens}/#{with_hypens}-library" | |
| 26 | + link = "<a href=\"#{location}\" class=\"_links-link\">#{library}</span>" | |
| 27 | + | |
| 28 | + # Add the link to the main title, just like how the "Homepage" and "Source code" links appear | |
| 29 | + at_css('.main-content').prepend_child("<p class=\"_links\">#{link}</p>") | |
| 30 | + end | |
| 31 | + | |
| 32 | + # Extract the actual content | |
| 33 | + # We can't use options[:container] here because the entries filter uses the breadcrumbs node | |
| 34 | + @doc = at_css('.main-content') | |
| 35 | + | |
| 36 | + # Move the features (i.e. "read-only, inherited") into the blue header | |
| 37 | + css('.features').each do |node| | |
| 38 | + header = node.xpath('parent::dd/preceding::dt').last | |
| 39 | + header.add_child node unless header.nil? | |
| 40 | + end | |
| 41 | + | |
| 42 | + css('section').each do |node| | |
| 43 | + if node['id'] && node.first_element_child | |
| 44 | + node.first_element_child['id'] ||= node['id'] | |
| 45 | + end | |
| 46 | + | |
| 47 | + node.before(node.children).remove | |
| 48 | + end | |
| 49 | + | |
| 50 | + css('span').each do |node| | |
| 51 | + node.before(node.children).remove | |
| 52 | + end | |
| 53 | + | |
| 54 | + # Make code blocks detectable by Prism | |
| 55 | + css('pre').each do |node| | |
| 56 | + node['data-language'] = 'dart' | |
| 57 | + node.content = node.content | |
| 58 | + end | |
| 59 | + | |
| 60 | + css('.properties', '.property', '.callables', '.callable').remove_attr('class') | |
| 61 | + | |
| 62 | + doc | |
| 63 | + end | |
| 64 | + end | |
| 65 | + end | |
| 66 | +end | |
| @@ -0,0 +1,42 @@ | ||
| 1 | +module Docs | |
| 2 | + class Dart | |
| 3 | + class EntriesFilter < Docs::EntriesFilter | |
| 4 | + def get_name | |
| 5 | + title = at_css('h1.title') | |
| 6 | + if title # v1 | |
| 7 | + name = title.element_children.last.content.strip | |
| 8 | + kind = title.at_css('.kind').content | |
| 9 | + else # v2 | |
| 10 | + title = at_css('.main-content > h1') | |
| 11 | + name = title.content[/(.*)( )/, 1].split(' top-level')[0] | |
| 12 | + kind = title.content[/(.*)( )(.+)/, 3] | |
| 13 | + end | |
| 14 | + | |
| 15 | + breadcrumbs = at_css('.breadcrumbs').css('li:not(.self-crumb) > a') | |
| 16 | + first_part = '' | |
| 17 | + | |
| 18 | + if breadcrumbs.length == 2 && !kind.include?('class') | |
| 19 | + first_part = breadcrumbs[1].content | |
| 20 | + elsif breadcrumbs.length == 3 | |
| 21 | + first_part = breadcrumbs[2].content | |
| 22 | + end | |
| 23 | + | |
| 24 | + separator = '' | |
| 25 | + | |
| 26 | + unless first_part.empty? | |
| 27 | + if kind.include?('class') | |
| 28 | + separator = ':' | |
| 29 | + else | |
| 30 | + separator = '.' | |
| 31 | + end | |
| 32 | + end | |
| 33 | + | |
| 34 | + "#{first_part}#{separator}#{name}" | |
| 35 | + end | |
| 36 | + | |
| 37 | + def get_type | |
| 38 | + at_css('.breadcrumbs > li:nth-child(2)').content.split(' ')[0] | |
| 39 | + end | |
| 40 | + end | |
| 41 | + end | |
| 42 | +end | |
| @@ -30,7 +30,7 @@ module Docs · 29 unmodified lines | ||
| 30 | 30 | end |
| 31 | 31 | |
| 32 | 32 | # Remove <div> wrapping .overheadIndicator |
| 33 | − css('div > .overheadIndicator:first-child:last-child').each do |node| | |
| 33 | + css('div > .overheadIndicator:first-child:last-child', 'div > .blockIndicator:first-child:last-child').each do |node| | |
| 34 | 34 | node.parent.replace(node) |
| 35 | 35 | end |
| 36 | 36 | |
| @@ -6,10 +6,13 @@ module Docs · 5 unmodified lines | ||
| 6 | 6 | 'EXT_' => 'WebGL', |
| 7 | 7 | 'OES_' => 'WebGL', |
| 8 | 8 | 'WEBGL_' => 'WebGL', |
| 9 | + 'Sensor API' => 'Sensors', | |
| 9 | 10 | 'Ambient Light' => 'Ambient Light', |
| 10 | 11 | 'Audio' => 'Audio', |
| 11 | 12 | 'Battery Status' => 'Battery Status', |
| 12 | 13 | 'Canvas ' => 'Canvas', |
| 14 | + 'Clipboard' => 'Clipboard', | |
| 15 | + 'Content Security' => 'Content Security Policy', | |
| 13 | 16 | 'Cooperative Scheduling' => 'Scheduling', |
| 14 | 17 | 'CSS Font Loading' => 'CSS', |
| 15 | 18 | 'CSS Object Model' => 'CSS', |
| @@ -20,10 +23,13 @@ module Docs · 4 unmodified lines | ||
| 20 | 23 | 'Encrypted Media Extensions' => 'Encrypted Media', |
| 21 | 24 | 'Fetch' => 'Fetch', |
| 22 | 25 | 'File API' => 'File', |
| 26 | + 'Fullscreen' => 'Fullscreen', | |
| 23 | 27 | 'Geolocation' => 'Geolocation', |
| 24 | 28 | 'Geometry' => 'Geometry', |
| 25 | 29 | 'High Resolution Time' => 'Performance', |
| 26 | 30 | 'Intersection' => 'Intersection Observer', |
| 31 | + 'Keyboard' => 'Keyboard', | |
| 32 | + 'Media Capabilities' => 'Media', | |
| 27 | 33 | 'Media Capture' => 'Media', |
| 28 | 34 | 'Media Session' => 'Media', |
| 29 | 35 | 'Media Source' => 'Media', |
| @@ -32,6 +38,7 @@ module Docs · 2 unmodified lines | ||
| 32 | 38 | 'MIDI' => 'Audio', |
| 33 | 39 | 'Navigation Timing' => 'Performance', |
| 34 | 40 | 'Network Information' => 'Network Information', |
| 41 | + 'Orientation Sensor' => 'Sensors', | |
| 35 | 42 | 'Payment' => 'Payments', |
| 36 | 43 | 'Performance Timeline' => 'Performance', |
| 37 | 44 | 'Pointer Events' => 'Pointer Events', |
| @@ -45,15 +52,22 @@ module Docs · 7 unmodified lines | ||
| 45 | 52 | 'Stream API' => 'Media Streams', |
| 46 | 53 | 'Streams' => 'Media Streams', |
| 47 | 54 | 'Touch Events' => 'Touch Events', |
| 55 | + 'Visual Viewport' => 'Visual Viewport', | |
| 48 | 56 | 'Web Animations' => 'Animation', |
| 49 | 57 | 'Web App Manifest' => 'Web App Manifest', |
| 50 | 58 | 'Budget' => 'Budget', |
| 59 | + 'Web Authentication' => 'Authentication', | |
| 60 | + 'Web Locks' => 'Locks', | |
| 51 | 61 | 'Web Workers' => 'Web Workers', |
| 52 | 62 | 'WebGL' => 'WebGL', |
| 53 | 63 | 'WebRTC' => 'WebRTC', |
| 54 | − 'WebVR' => 'WebVR' } | |
| 64 | + 'WebUSB' => 'WebUSB', | |
| 65 | + 'WebVR' => 'WebVR', | |
| 66 | + 'WebVTT' => 'WebVTT' } | |
| 55 | 67 | |
| 56 | 68 | TYPE_BY_NAME_STARTS_WITH = { |
| 69 | + 'AbortController' => 'Fetch', | |
| 70 | + 'AbortSignal' => 'Fetch', | |
| 57 | 71 | 'Ambient' => 'Ambient Light', |
| 58 | 72 | 'Attr' => 'Nodes', |
| 59 | 73 | 'Audio' => 'Audio', |
| @@ -61,6 +75,7 @@ module Docs · 1 unmodified line | ||
| 61 | 75 | 'Broadcast' => 'Broadcast Channel', |
| 62 | 76 | 'Budget' => 'Budget', |
| 63 | 77 | 'Canvas' => 'Canvas', |
| 78 | + 'Clipboard' => 'Clipboard', | |
| 64 | 79 | 'CSS' => 'CSS', |
| 65 | 80 | 'CharacterData' => 'Nodes', |
| 66 | 81 | 'ChildNode' => 'Nodes', |
| @@ -69,6 +84,7 @@ module Docs · 2 unmodified lines | ||
| 69 | 84 | 'CustomElement' => 'Custom Elements', |
| 70 | 85 | 'DataTransfer' => 'Drag & Drop', |
| 71 | 86 | 'document' => 'Document', |
| 87 | + 'Document Object' => 'DOM', | |
| 72 | 88 | 'DocumentFragment' => 'DocumentFragment', |
| 73 | 89 | 'DocumentType' => 'Nodes', |
| 74 | 90 | 'DOM' => 'DOM', |
| @@ -79,10 +95,12 @@ module Docs · 4 unmodified lines | ||
| 79 | 95 | 'Fetch' => 'Fetch', |
| 80 | 96 | 'File' => 'File', |
| 81 | 97 | 'GlobalEventHandlers' => 'GlobalEventHandlers', |
| 98 | + 'HMDVR' => 'WebVR', | |
| 82 | 99 | 'history' => 'History', |
| 83 | 100 | 'HTML Drag' => 'Drag & Drop', |
| 84 | 101 | 'HTML' => 'Elements', |
| 85 | 102 | 'IDB' => 'IndexedDB', |
| 103 | + 'Keyboard' => 'Keyboard', | |
| 86 | 104 | 'location' => 'Location', |
| 87 | 105 | 'navigator' => 'Navigator', |
| 88 | 106 | 'MediaKeySession' => 'Encrypted Media', |
| @@ -90,6 +108,7 @@ module Docs · 1 unmodified line | ||
| 90 | 108 | 'MediaSession' => 'Media Session', |
| 91 | 109 | 'MediaTrack' => 'Media Streams', |
| 92 | 110 | 'Message' => 'Channel Messaging', |
| 111 | + 'Mutation' => 'DOM', | |
| 93 | 112 | 'NamedNode' => 'Nodes', |
| 94 | 113 | 'Node' => 'Nodes', |
| 95 | 114 | 'Notification' => 'Notification', |
| @@ -105,9 +124,13 @@ module Docs · 9 unmodified lines | ||
| 105 | 124 | 'screen' => 'Screen', |
| 106 | 125 | 'Selection' => 'Selection', |
| 107 | 126 | 'Shadow' => 'Shadow DOM', |
| 127 | + 'StaticRange' => 'Range', | |
| 128 | + 'Streams' => 'Media Streams', | |
| 129 | + 'StyleProperty' => 'CSS', | |
| 108 | 130 | 'StyleSheet' => 'CSS', |
| 109 | 131 | 'Stylesheet' => 'CSS', |
| 110 | 132 | 'SVG' => 'SVG', |
| 133 | + 'TextTrack' => 'WebVTT', | |
| 111 | 134 | 'TimeRanges' => 'Media', |
| 112 | 135 | 'timing' => 'Performance', |
| 113 | 136 | 'Timing' => 'Performance', |
| @@ -116,6 +139,7 @@ module Docs · 2 unmodified lines | ||
| 116 | 139 | 'URL' => 'URL', |
| 117 | 140 | 'VR' => 'WebVR', |
| 118 | 141 | 'WebSocket' => 'Web Sockets', |
| 142 | + 'USB' => 'WebUSB', | |
| 119 | 143 | 'window' => 'Window', |
| 120 | 144 | 'Window' => 'Window', |
| 121 | 145 | 'XMLHttpRequest' => 'XMLHTTPRequest' } |
| @@ -145,6 +169,7 @@ module Docs · 23 unmodified lines | ||
| 145 | 169 | 'timing' => 'Performance', |
| 146 | 170 | 'Timing' => 'Performance', |
| 147 | 171 | 'udio' => 'Audio', |
| 172 | + 'VRDevice' => 'WebVR', | |
| 148 | 173 | 'WebGL' => 'WebGL', |
| 149 | 174 | 'WEBGL' => 'WebGL', |
| 150 | 175 | 'WebRTC' => 'WebRTC', |
| @@ -164,7 +189,6 @@ module Docs · 13 unmodified lines | ||
| 164 | 189 | |
| 165 | 190 | CLEANUP_NAMES = %w( |
| 166 | 191 | CSS\ Object\ Model. |
| 167 | − Document\ Object\ Model. | |
| 168 | 192 | Tutorial. |
| 169 | 193 | XMLHttpRequest. |
| 170 | 194 | ANGLE\ instanced\ arrays.) |
| @@ -172,6 +196,7 @@ module Docs · 1 unmodified line | ||
| 172 | 196 | def get_name |
| 173 | 197 | name = super |
| 174 | 198 | CLEANUP_NAMES.each { |str| name.remove!(str) } |
| 199 | + name.sub! %r{Document\ Object\ Model\.}i, 'Document Object Model: ' | |
| 175 | 200 | name.sub! 'Input.', 'HTMLInputElement.' |
| 176 | 201 | name.sub! 'window.navigator', 'navigator' |
| 177 | 202 | name.sub! 'API.', 'API: ' |
| @@ -224,7 +249,7 @@ module Docs · 46 unmodified lines | ||
| 224 | 249 | |
| 225 | 250 | def include_default_entry? |
| 226 | 251 | return true if type == 'Console' |
| 227 | − return true unless node = doc.at_css('.overheadIndicator') | |
| 252 | + return true unless node = doc.at_css('.overheadIndicator, .blockIndicator') | |
| 228 | 253 | node = node.parent while node.parent != doc |
| 229 | 254 | return true if node.previous_element.try(:name).in?(%w(h2 h3)) |
| 230 | 255 | content = node.content |
| @@ -4,7 +4,7 @@ module Docs · 3 unmodified lines | ||
| 4 | 4 | def call |
| 5 | 5 | css('.header-link', 'hr + .text-center', 'hr').remove |
| 6 | 6 | |
| 7 | − css('.grid', '.row', '.col-ms-12', 'ul.docs-list > ul.docs-list').each do |node| | |
| 7 | + css('.grid', '.row', '.col-ms-12', 'ul.docs-list > ul.docs-list', '.sub-section').each do |node| | |
| 8 | 8 | node.before(node.children).remove |
| 9 | 9 | end |
| 10 | 10 | |
| @@ -16,18 +16,26 @@ module Docs · 5 unmodified lines | ||
| 16 | 16 | end |
| 17 | 17 | end |
| 18 | 18 | |
| 19 | − at_css('h2').name = 'h1' unless at_css('h1') | |
| 19 | + at_css('h2').name = 'h1' if !at_css('h1') && at_css('h2') | |
| 20 | 20 | |
| 21 | 21 | css('h3', 'h4', 'h5').each do |node| |
| 22 | 22 | node.name = node.name.sub(/\d/) { |i| i.to_i - 1 } unless node.name == 'h3' && node.at_css('code') |
| 23 | 23 | end if !at_css('h2') && at_css('h4') |
| 24 | 24 | |
| 25 | + css('h1 > a', 'h2 > a', 'h3 > a', 'h4 > a').each do |node| | |
| 26 | + node.before(node.children).remove | |
| 27 | + end | |
| 28 | + | |
| 25 | 29 | css('div.highlighter-rouge').each do |node| |
| 26 | 30 | node['data-language'] = node['class'][/language-(\w+)/, 1] if node['class'] |
| 27 | 31 | node.content = node.content.strip |
| 28 | 32 | node.name = 'pre' |
| 29 | 33 | end |
| 30 | 34 | |
| 35 | + css('pre > code.hljs').each do |node| | |
| 36 | + node.parent['data-language'] = node['class'][/language-(\w+)/, 1] | |
| 37 | + end | |
| 38 | + | |
| 31 | 39 | css('.highlighter-rouge').remove_attr('class') |
| 32 | 40 | |
| 33 | 41 | css('pre').each do |node| |
| @@ -2,6 +2,8 @@ module Docs · 1 unmodified line | ||
| 2 | 2 | class Electron |
| 3 | 3 | class EntriesFilter < Docs::EntriesFilter |
| 4 | 4 | def get_name |
| 5 | + return 'API' if subpath == '/api' | |
| 6 | + | |
| 5 | 7 | name = at_css('h1, h2').content |
| 6 | 8 | name.remove! 'Class: ' |
| 7 | 9 | name.remove! ' Object' |
| @@ -12,11 +14,13 @@ module Docs · 4 unmodified lines | ||
| 12 | 14 | end |
| 13 | 15 | |
| 14 | 16 | def get_type |
| 15 | − if subpath.start_with?('tutorial') || slug.in?(%w(glossary/ faq/)) | |
| 17 | + return 'API' if subpath == '/api' | |
| 18 | + | |
| 19 | + if subpath.start_with?('/tutorial') || subpath.in?(%w(/glossary /faq)) | |
| 16 | 20 | 'Guides' |
| 17 | − elsif subpath.start_with?('development') | |
| 21 | + elsif subpath.start_with?('/development') | |
| 18 | 22 | 'Guides: Development' |
| 19 | − elsif slug.in?(%w(api/synopsis/ api/chrome-command-line-switches/)) | |
| 23 | + elsif subpath.in?(%w(/api/synopsis /api/chrome-command-line-switches)) | |
| 20 | 24 | 'API' |
| 21 | 25 | elsif at_css('h1, h2').content.include?(' Object') |
| 22 | 26 | 'API: Objects' |
| @@ -26,7 +30,7 @@ module Docs · 3 unmodified lines | ||
| 26 | 30 | end |
| 27 | 31 | |
| 28 | 32 | def additional_entries |
| 29 | − return [] unless slug.start_with?('api/') | |
| 33 | + return [] unless subpath.start_with?('/api') | |
| 30 | 34 | |
| 31 | 35 | css('h3 > code', 'h4 > code').each_with_object [] do |node, entries| |
| 32 | 36 | next if node.previous.try(:content).present? || node.next.try(:content).present? |
| @@ -37,10 +41,6 @@ module Docs · 4 unmodified lines | ||
| 37 | 41 | entries << [name, node.parent['id']] unless name == self.name |
| 38 | 42 | end |
| 39 | 43 | end |
| 40 | − | |
| 41 | − def include_default_entry? | |
| 42 | − slug != 'api/' | |
| 43 | − end | |
| 44 | 44 | end |
| 45 | 45 | end |
| 46 | 46 | end |
| @@ -57,8 +57,14 @@ module Docs · 56 unmodified lines | ||
| 57 | 57 | node.parent.after(node) |
| 58 | 58 | end |
| 59 | 59 | |
| 60 | + css('.signature').each do |node| | |
| 61 | + non_text_children = node.xpath('node()[not(self::text())]') | |
| 62 | + non_text_children.to_a.reverse.each { |child| node.parent.add_next_sibling(child) } | |
| 63 | + end | |
| 64 | + | |
| 60 | 65 | css('pre').each do |node| |
| 61 | 66 | node['data-language'] = 'elixir' |
| 67 | + node.content = node.content | |
| 62 | 68 | end |
| 63 | 69 | end |
| 64 | 70 | end |
| @@ -0,0 +1,28 @@ | ||
| 1 | +module Docs | |
| 2 | + class Eslint | |
| 3 | + class CleanHtmlFilter < Filter | |
| 4 | + def call | |
| 5 | + @doc = at_css('.doc') if at_css('.doc') | |
| 6 | + | |
| 7 | + css('.glyphicon').remove | |
| 8 | + css('hr', 'colgroup', 'td:empty').remove | |
| 9 | + | |
| 10 | + css('.container').each do |node| | |
| 11 | + node.before(node.children).remove | |
| 12 | + end | |
| 13 | + | |
| 14 | + css('div.highlighter-rouge').each do |node| | |
| 15 | + lang = node['class'][/language-(\w+)/, 1] | |
| 16 | + node['data-language'] = lang if lang | |
| 17 | + node.content = node.content.strip | |
| 18 | + node.name = 'pre' | |
| 19 | + node.remove_attribute('class') | |
| 20 | + end | |
| 21 | + | |
| 22 | + css('code', 'p').remove_attr('class') | |
| 23 | + | |
| 24 | + doc | |
| 25 | + end | |
| 26 | + end | |
| 27 | + end | |
| 28 | +end | |
| @@ -0,0 +1,25 @@ | ||
| 1 | +module Docs | |
| 2 | + class Eslint | |
| 3 | + class EntriesFilter < Docs::EntriesFilter | |
| 4 | + def get_name | |
| 5 | + name = at_css('h1').content.strip | |
| 6 | + | |
| 7 | + if subpath.start_with?('rules/') && subpath != 'rules/' | |
| 8 | + name = name[/\(([\w\-]+?)\)\z/, 1] || name[/\A([\w\-]+?):/, 1] | |
| 9 | + end | |
| 10 | + | |
| 11 | + name | |
| 12 | + end | |
| 13 | + | |
| 14 | + def get_type | |
| 15 | + if subpath.include?('developer-guide') | |
| 16 | + 'Developer Guide' | |
| 17 | + elsif subpath.include?('guide') | |
| 18 | + 'Guide' | |
| 19 | + elsif subpath.start_with?('rules') | |
| 20 | + 'Rules' | |
| 21 | + end | |
| 22 | + end | |
| 23 | + end | |
| 24 | + end | |
| 25 | +end | |
| @@ -42,6 +42,11 @@ module Docs · 41 unmodified lines | ||
| 42 | 42 | node.parent.content = node.parent.content |
| 43 | 43 | end |
| 44 | 44 | |
| 45 | + # Fix links to the method reference | |
| 46 | + css('a').each do |node| | |
| 47 | + node['href'] = node['href'].sub('4x/api', 'index') | |
| 48 | + end | |
| 49 | + | |
| 45 | 50 | doc |
| 46 | 51 | end |
| 47 | 52 | end |
| @@ -45,10 +45,6 @@ module Docs · 44 unmodified lines | ||
| 45 | 45 | end |
| 46 | 46 | end |
| 47 | 47 | |
| 48 | − css('.__cf_email__').each do |node| | |
| 49 | − node.replace(decode_cloudflare_email(node['data-cfemail'])) | |
| 50 | − end | |
| 51 | − | |
| 52 | 48 | css('.editor').each do |node| |
| 53 | 49 | pre = node.at_css('.editor-code > pre') |
| 54 | 50 | pre['data-language'] = 'javascript' |
| @@ -6,6 +6,7 @@ module Docs · 5 unmodified lines | ||
| 6 | 6 | end |
| 7 | 7 | |
| 8 | 8 | def get_type |
| 9 | + return 'React' if slug.start_with?('react') | |
| 9 | 10 | type = at_css('.guide-nav .nav-item').content.strip |
| 10 | 11 | type.remove! %r{ \(.*} |
| 11 | 12 | type |
| @@ -9,7 +9,7 @@ module Docs · 8 unmodified lines | ||
| 9 | 9 | |
| 10 | 10 | css('.highlight > pre').each do |node| |
| 11 | 11 | node['data-language'] = node.parent['class'][/highlight-source-(\w+)/, 1] |
| 12 | − node.content = node.content.strip_heredoc.gsub(' ', ' ') | |
| 12 | + node.content = node.content.strip_heredoc | |
| 13 | 13 | node.parent.replace(node) |
| 14 | 14 | end |
| 15 | 15 | |
| @@ -17,6 +17,7 @@ module Docs · 16 unmodified lines | ||
| 17 | 17 | css('#plusone', '#nav', '.pkgGopher', '#footer', '.collapsed', '.permalink', '#pkg-callgraph').remove |
| 18 | 18 | |
| 19 | 19 | css('span[style]', '.toggleVisible', '.expanded', 'div.toggle').each do |node| |
| 20 | + node.first_element_child['id'] = node['id'] if node['id'] | |
| 20 | 21 | node.before(node.children).remove |
| 21 | 22 | end |
| 22 | 23 | |
| @@ -0,0 +1,27 @@ | ||
| 1 | +module Docs | |
| 2 | + class Graphite | |
| 3 | + class CleanHtmlFilter < Filter | |
| 4 | + def call | |
| 5 | + # Remove the paragraph icon after all headers | |
| 6 | + css('.headerlink').remove | |
| 7 | + | |
| 8 | + css('dl.function > dt').each do |node| | |
| 9 | + node.content = node.content | |
| 10 | + end | |
| 11 | + | |
| 12 | + css('.section').each do |node| | |
| 13 | + node.before(node.children).remove | |
| 14 | + end | |
| 15 | + | |
| 16 | + css('div[class*="highlight-"]').each do |node| | |
| 17 | + node.content = node.content.strip | |
| 18 | + node.name = 'pre' | |
| 19 | + node['data-language'] = node['class'][/highlight\-(\w+)/, 1] | |
| 20 | + node.remove_attribute('class') | |
| 21 | + end | |
| 22 | + | |
| 23 | + doc | |
| 24 | + end | |
| 25 | + end | |
| 26 | + end | |
| 27 | +end | |
| @@ -0,0 +1,46 @@ | ||
| 1 | +module Docs | |
| 2 | + class Graphite | |
| 3 | + class EntriesFilter < Docs::EntriesFilter | |
| 4 | + def get_name | |
| 5 | + at_css('h1').children[0].to_s | |
| 6 | + end | |
| 7 | + | |
| 8 | + def get_type | |
| 9 | + get_name | |
| 10 | + end | |
| 11 | + | |
| 12 | + def additional_entries | |
| 13 | + entries = [] | |
| 14 | + | |
| 15 | + # Sections | |
| 16 | + css('.section > .section').each do |node| | |
| 17 | + title = node.at_css('h2, h3') | |
| 18 | + | |
| 19 | + next if title.nil? | |
| 20 | + | |
| 21 | + # Move the id attribute to the title | |
| 22 | + # If this is excluded, the complete section will be highlighted in yellow when someone navigates to it | |
| 23 | + title['id'] = node['id'] | |
| 24 | + node.remove_attribute('id') | |
| 25 | + | |
| 26 | + parent_title_selector = "parent::div[@class='section']/preceding::#{title.name == 'h2' ? 'h1' : 'h2'}" | |
| 27 | + | |
| 28 | + entries << [ | |
| 29 | + title.children[0].to_s, | |
| 30 | + title['id'], | |
| 31 | + title.xpath(parent_title_selector).last.children[0].to_s | |
| 32 | + ] | |
| 33 | + end | |
| 34 | + | |
| 35 | + # Functions | |
| 36 | + css('dl.function > dt').each do |node| | |
| 37 | + name = node.at_css('.descname').content | |
| 38 | + name << '()' | |
| 39 | + entries << [name, node['id'], 'Functions'] | |
| 40 | + end | |
| 41 | + | |
| 42 | + entries | |
| 43 | + end | |
| 44 | + end | |
| 45 | + end | |
| 46 | +end | |
| @@ -0,0 +1,44 @@ | ||
| 1 | +module Docs | |
| 2 | + class Handlebars | |
| 3 | + class CleanHtmlFilter < Filter | |
| 4 | + def call | |
| 5 | + # Remove the t-shirt shop advertisement | |
| 6 | + css('#callout').remove | |
| 7 | + | |
| 8 | + # The title filter is used to add titles to pages without one, remove original headers | |
| 9 | + css('h1').remove | |
| 10 | + | |
| 11 | + # Remove the link to the issue tracker | |
| 12 | + css('.issue-tracker').remove | |
| 13 | + | |
| 14 | + css('pre').each do |node| | |
| 15 | + # Remove nested nodes inside pre tags | |
| 16 | + node.content = node.content | |
| 17 | + | |
| 18 | + # Add syntax highlighting | |
| 19 | + node['data-language'] = 'html' | |
| 20 | + end | |
| 21 | + | |
| 22 | + # Transform 'Learn More' links to headers in the "Getting Started" part of the homepage | |
| 23 | + # If this step is skipped, that section looks cluttered with 4 sub-sections without any dividers | |
| 24 | + css('#getting-started + .contents a.more-info').each do |node| | |
| 25 | + clone = node.clone | |
| 26 | + | |
| 27 | + # Move it to the top of the sub-section | |
| 28 | + node.parent.prepend_child(clone) | |
| 29 | + | |
| 30 | + # Turn it into a header | |
| 31 | + clone.name = 'h3' | |
| 32 | + | |
| 33 | + # Remove the "Learn More: " part | |
| 34 | + clone.content = clone.content[12..-1] | |
| 35 | + end | |
| 36 | + | |
| 37 | + # Remove class attributes from div elements to reduce file size | |
| 38 | + css('div').remove_attr('class') | |
| 39 | + | |
| 40 | + doc | |
| 41 | + end | |
| 42 | + end | |
| 43 | + end | |
| 44 | +end | |
| @@ -0,0 +1,19 @@ | ||
| 1 | +module Docs | |
| 2 | + class Handlebars | |
| 3 | + class EntriesFilter < Docs::EntriesFilter | |
| 4 | + def get_name | |
| 5 | + subpath[0..-6].titleize | |
| 6 | + end | |
| 7 | + | |
| 8 | + def get_type | |
| 9 | + name | |
| 10 | + end | |
| 11 | + | |
| 12 | + def additional_entries | |
| 13 | + css('h2, h3').to_a.map do |node| | |
| 14 | + [node.content.strip, node['id'], root_page? ? 'Manual' : nil] | |
| 15 | + end | |
| 16 | + end | |
| 17 | + end | |
| 18 | + end | |
| 19 | +end | |
| @@ -65,6 +65,10 @@ module Docs · 64 unmodified lines | ||
| 65 | 65 | end |
| 66 | 66 | end |
| 67 | 67 | |
| 68 | + css('pre code').each do |node| | |
| 69 | + node.before(node.children).remove | |
| 70 | + end | |
| 71 | + | |
| 68 | 72 | doc |
| 69 | 73 | end |
| 70 | 74 | end |
| @@ -52,7 +52,7 @@ module Docs · 51 unmodified lines | ||
| 52 | 52 | return [] if subpath.start_with?('users_guide') |
| 53 | 53 | return [] if IGNORE_ENTRIES_PATHS.include?(subpath.split('/').last) |
| 54 | 54 | |
| 55 | − css('#synopsis > ul > li').each_with_object [] do |node, entries| | |
| 55 | + css('#synopsis > details > ul > li').each_with_object [] do |node, entries| | |
| 56 | 56 | link = node.at_css('a') |
| 57 | 57 | name = node.content.strip |
| 58 | 58 | name.remove! %r{\A(?:module|data|newtype|class|type family m|type)\s+} |
| @@ -75,7 +75,7 @@ module Docs · 16 unmodified lines | ||
| 75 | 75 | end |
| 76 | 76 | |
| 77 | 77 | def include_default_entry? |
| 78 | − subpath.start_with?('users_guide') || at_css('#synopsis > ul > li') | |
| 78 | + subpath.start_with?('users_guide') || at_css('#synopsis > details > ul > li') | |
| 79 | 79 | end |
| 80 | 80 | end |
| 81 | 81 | end |
| @@ -0,0 +1,24 @@ | ||
| 1 | +module Docs | |
| 2 | + class Homebrew | |
| 3 | + class CleanHtmlFilter < Filter | |
| 4 | + def call | |
| 5 | + css('hr') | |
| 6 | + | |
| 7 | + if at_css('h1').nil? | |
| 8 | + title = current_url.normalized_path[1..-1].gsub(/-/, ' ') | |
| 9 | + doc.children.before("<h1>#{title}</h1>") | |
| 10 | + end | |
| 11 | + | |
| 12 | + css('div.highlighter-rouge').each do |node| | |
| 13 | + lang = node['class'][/language-(\w+)/, 1] | |
| 14 | + node['data-language'] = lang if lang | |
| 15 | + node.content = node.content.strip | |
| 16 | + node.name = 'pre' | |
| 17 | + node.remove_attribute('class') | |
| 18 | + end | |
| 19 | + | |
| 20 | + doc | |
| 21 | + end | |
| 22 | + end | |
| 23 | + end | |
| 24 | +end | |
| @@ -0,0 +1,34 @@ | ||
| 1 | +module Docs | |
| 2 | + class Homebrew | |
| 3 | + class EntriesFilter < Docs::EntriesFilter | |
| 4 | + def get_name | |
| 5 | + header = at_css('h1') | |
| 6 | + name = header.nil? ? current_url.normalized_path[1..-1].gsub(/-/, ' ') : header.content.strip | |
| 7 | + name.remove! %r{\(.*} | |
| 8 | + name | |
| 9 | + end | |
| 10 | + | |
| 11 | + CONTRIBUTOR_SLUGS = %w( | |
| 12 | + How-To-Open-a-Homebrew-Pull-Request | |
| 13 | + Formula-Cookbook | |
| 14 | + Acceptable-Formulae | |
| 15 | + Versions | |
| 16 | + Node-for-Formula-Authors | |
| 17 | + Python-for-Formula-Authors | |
| 18 | + Migrating-A-Formula-To-A-Tap | |
| 19 | + Rename-A-Formula | |
| 20 | + Building-Against-Non-Homebrew-Dependencies | |
| 21 | + How-to-Create-and-Maintain-a-Tap | |
| 22 | + Brew-Test-Bot | |
| 23 | + Prose-Style-Guidelines) | |
| 24 | + | |
| 25 | + def get_type | |
| 26 | + if CONTRIBUTOR_SLUGS.include?(slug) | |
| 27 | + 'Contributors' | |
| 28 | + else | |
| 29 | + 'Users' | |
| 30 | + end | |
| 31 | + end | |
| 32 | + end | |
| 33 | + end | |
| 34 | +end | |
| @@ -28,7 +28,7 @@ module Docs · 27 unmodified lines | ||
| 28 | 28 | |
| 29 | 29 | def include_default_entry? |
| 30 | 30 | return false if %w(Element/Heading_Elements).include?(slug) |
| 31 | − (node = doc.at_css '.overheadIndicator').nil? || node.content.exclude?('not on a standards track') | |
| 31 | + (node = doc.at_css '.overheadIndicator, .blockIndicator').nil? || node.content.exclude?('not on a standards track') | |
| 32 | 32 | end |
| 33 | 33 | |
| 34 | 34 | def additional_entries |
| @@ -11,12 +11,12 @@ module Docs · 10 unmodified lines | ||
| 11 | 11 | |
| 12 | 12 | def other |
| 13 | 13 | # Remove "style" attribute |
| 14 | − css('.inheritsbox', '.overheadIndicator').each do |node| | |
| 14 | + css('.inheritsbox', '.overheadIndicator', '.blockIndicator').each do |node| | |
| 15 | 15 | node.remove_attribute 'style' |
| 16 | 16 | end |
| 17 | 17 | |
| 18 | 18 | # Remove <div> wrapping .overheadIndicator |
| 19 | − css('div > .overheadIndicator:first-child:last-child').each do |node| | |
| 19 | + css('div > .overheadIndicator:first-child:last-child', 'div > .blockIndicator:first-child:last-child').each do |node| | |
| 20 | 20 | node.parent.replace(node) |
| 21 | 21 | end |
| 22 | 22 | end |
| @@ -2,7 +2,7 @@ module Docs · 1 unmodified line | ||
| 2 | 2 | class Javascript |
| 3 | 3 | class EntriesFilter < Docs::EntriesFilter |
| 4 | 4 | TYPES = %w(Array ArrayBuffer Atomics Boolean DataView Date Function |
| 5 | − Generator Intl JSON Map Math Number Object Promise Reflect RegExp | |
| 5 | + Generator Intl JSON Map Math Number Object PluralRules Promise Reflect RegExp | |
| 6 | 6 | Set SharedArrayBuffer SIMD String Symbol TypedArray WeakMap WeakSet) |
| 7 | 7 | INTL_OBJECTS = %w(Collator DateTimeFormat NumberFormat) |
| 8 | 8 | |
| @@ -88,7 +88,7 @@ module Docs · 79 unmodified lines | ||
| 88 | 88 | end |
| 89 | 89 | |
| 90 | 90 | def include_default_entry? |
| 91 | − node = doc.at_css '.overheadIndicator, .warning' | |
| 91 | + node = doc.at_css '.blockIndicator, .warning' | |
| 92 | 92 | |
| 93 | 93 | # Can't use :first-child because #doc is a DocumentFragment |
| 94 | 94 | return true unless node && node.parent == doc && !node.previous_element |
| @@ -0,0 +1,43 @@ | ||
| 1 | +module Docs | |
| 2 | + class Jekyll | |
| 3 | + class CleanHtmlFilter < Filter | |
| 4 | + def call | |
| 5 | + @doc = at_css('article') | |
| 6 | + | |
| 7 | + at_css('h1').content = 'Jekyll' if root_page? | |
| 8 | + | |
| 9 | + css('.improve, .section-nav').remove | |
| 10 | + | |
| 11 | + css('div.highlighter-rouge').each do |node| | |
| 12 | + pre = node.at_css('pre') | |
| 13 | + | |
| 14 | + lang = node['class'][/language-(\w+)/, 1] | |
| 15 | + # HACK: Prism shell highlighting highlights `|`, | |
| 16 | + # which makes the tree on this page look terrible | |
| 17 | + unless slug.include?('structure') && lang == 'sh' | |
| 18 | + lang = 'bash' if lang == 'sh' | |
| 19 | + pre['data-language'] = lang | |
| 20 | + end | |
| 21 | + | |
| 22 | + pre.remove_attribute('class') | |
| 23 | + pre.content = pre.content | |
| 24 | + node.replace(pre) | |
| 25 | + end | |
| 26 | + | |
| 27 | + css('code').remove_attr('class') | |
| 28 | + | |
| 29 | + css('.note').each do |node| | |
| 30 | + node.name = 'blockquote' | |
| 31 | + | |
| 32 | + # <div class="note">...<br>...</div> -> <div class="note">...</div> | |
| 33 | + (node > 'br').each(&:remove) | |
| 34 | + # <div class="note">...<p>...<br><br>...</p>...</div> -> | |
| 35 | + # <div class="note">...<p>...<br>...</p>...</div> | |
| 36 | + node.css('br + br').each(&:remove) | |
| 37 | + end | |
| 38 | + | |
| 39 | + doc | |
| 40 | + end | |
| 41 | + end | |
| 42 | + end | |
| 43 | +end | |
| @@ -0,0 +1,28 @@ | ||
| 1 | +module Docs | |
| 2 | + class Jekyll | |
| 3 | + class EntriesFilter < Docs::EntriesFilter | |
| 4 | + def get_name | |
| 5 | + at_css('h1').content | |
| 6 | + end | |
| 7 | + | |
| 8 | + def get_type | |
| 9 | + if slug.include?('continuous-integration') | |
| 10 | + 'Deployment' | |
| 11 | + else | |
| 12 | + nav_link = doc.document # document | |
| 13 | + .at_css('aside li.current') # item in navbar | |
| 14 | + | |
| 15 | + if nav_link | |
| 16 | + nav_link | |
| 17 | + .parent # <ul> in navbar | |
| 18 | + .previous_element # header before <ul> | |
| 19 | + .content # category | |
| 20 | + else | |
| 21 | + 'Miscellaneous' | |
| 22 | + end | |
| 23 | + end | |
| 24 | + end | |
| 25 | + | |
| 26 | + end | |
| 27 | + end | |
| 28 | +end | |
| @@ -12,13 +12,14 @@ module Docs · 11 unmodified lines | ||
| 12 | 12 | node.before(node.children).remove |
| 13 | 13 | end |
| 14 | 14 | |
| 15 | − css('.anchor[name]').each do |node| | |
| 16 | − node.parent['id'] = node['name'] | |
| 15 | + css('.anchor').each do |node| | |
| 16 | + node.parent['id'] = node['id'] | |
| 17 | 17 | node.remove |
| 18 | 18 | end |
| 19 | 19 | |
| 20 | 20 | css('pre').each do |node| |
| 21 | − node['data-language'] = node['class'][/language-(\w+)/, 1] | |
| 21 | + node['data-language'] = 'js' | |
| 22 | + node['data-language'] = node['class'][/language-(\w+)/, 1] if node['class'] | |
| 22 | 23 | node.content = node.content |
| 23 | 24 | end |
| 24 | 25 | |
| @@ -6,7 +6,7 @@ module Docs · 5 unmodified lines | ||
| 6 | 6 | end |
| 7 | 7 | |
| 8 | 8 | def get_type |
| 9 | − type = at_css('.navItemActive').ancestors('.navGroup').first.at_css('h3').content | |
| 9 | + type = at_css('.navListItemActive').ancestors('.navGroup').first.at_css('h3').content | |
| 10 | 10 | |
| 11 | 11 | if type == 'Introduction' |
| 12 | 12 | 'Guides: Introduction' |
| @@ -0,0 +1,26 @@ | ||
| 1 | +module Docs | |
| 2 | + class Jsdoc | |
| 3 | + class CleanHtmlFilter < Filter | |
| 4 | + def call | |
| 5 | + at_css('h1').content = 'JSDoc' if root_page? | |
| 6 | + | |
| 7 | + css('.prettyprint').each do |node| | |
| 8 | + node.content = node.content | |
| 9 | + node['data-language'] = node['class'][/lang-(\w+)/, 1] | |
| 10 | + node.remove_attribute('class') | |
| 11 | + end | |
| 12 | + | |
| 13 | + css('figcaption').each do |node| | |
| 14 | + node.name = 'div' | |
| 15 | + node['class'] = '_pre-heading' | |
| 16 | + end | |
| 17 | + | |
| 18 | + css('figure').each do |node| | |
| 19 | + node.before(node.children).remove | |
| 20 | + end | |
| 21 | + | |
| 22 | + doc | |
| 23 | + end | |
| 24 | + end | |
| 25 | + end | |
| 26 | +end | |
| @@ -0,0 +1,28 @@ | ||
| 1 | +module Docs | |
| 2 | + class Jsdoc | |
| 3 | + class EntriesFilter < Docs::EntriesFilter | |
| 4 | + def get_name | |
| 5 | + name = at_css('h1').content | |
| 6 | + name.prepend 'JSDoc: ' if !name.include?('@') && !name.include?('JSDoc') | |
| 7 | + name | |
| 8 | + end | |
| 9 | + | |
| 10 | + def get_type | |
| 11 | + case slug | |
| 12 | + when /^about-/ | |
| 13 | + 'Getting Started' | |
| 14 | + when /^plugins-/ | |
| 15 | + 'Plugins' | |
| 16 | + when /^howto-/ | |
| 17 | + 'Examples' | |
| 18 | + when /^tags-inline-/ | |
| 19 | + 'Inline Tags' | |
| 20 | + when /^tags-/ | |
| 21 | + 'Tags' | |
| 22 | + else | |
| 23 | + 'Miscellaneous' # Only shown if a new category gets added in the upstream docs | |
| 24 | + end | |
| 25 | + end | |
| 26 | + end | |
| 27 | + end | |
| 28 | +end | |
| @@ -0,0 +1,42 @@ | ||
| 1 | +# frozen_string_literal: true | |
| 2 | + | |
| 3 | +module Docs | |
| 4 | + class Koa | |
| 5 | + class CleanHtmlFilter < Filter | |
| 6 | + def call | |
| 7 | + fix_homepage if slug.start_with? 'api/index' | |
| 8 | + | |
| 9 | + css('[data-language=shell]').each do |node| | |
| 10 | + node['data-language'] = 'bash' | |
| 11 | + end | |
| 12 | + | |
| 13 | + doc | |
| 14 | + end | |
| 15 | + | |
| 16 | + def fix_homepage | |
| 17 | + # Shrink the headers | |
| 18 | + for n in (1..5).to_a.reverse | |
| 19 | + css("h#{n}").each do |header| | |
| 20 | + header.name = "h#{n+1}" | |
| 21 | + end | |
| 22 | + end | |
| 23 | + | |
| 24 | + # Add an introduction | |
| 25 | + doc.children.before <<-HTML.strip_heredoc | |
| 26 | + <h1>Koa</h1> | |
| 27 | + <!-- https://github.com/koajs/koa/blob/841844e/Readme.md --> | |
| 28 | + <h2 id="introduction">Introduction</h2> | |
| 29 | + <p> | |
| 30 | + Expressive HTTP middleware framework for node.js to make web applications and APIs more enjoyable to write. Koa's middleware stack flows in a stack-like manner, allowing you to perform actions downstream then filter and manipulate the response upstream. | |
| 31 | + </p> | |
| 32 | + <p> | |
| 33 | + Only methods that are common to nearly all HTTP servers are integrated directly into Koa's small ~570 SLOC codebase. This includes things like content negotiation, normalization of node inconsistencies, redirection, and a few others. | |
| 34 | + </p> | |
| 35 | + <p> | |
| 36 | + Koa is not bundled with any middleware. | |
| 37 | + </p> | |
| 38 | + HTML | |
| 39 | + end | |
| 40 | + end | |
| 41 | + end | |
| 42 | +end | |
| @@ -0,0 +1,36 @@ | ||
| 1 | +module Docs | |
| 2 | + class Koa | |
| 3 | + class EntriesFilter < Docs::EntriesFilter | |
| 4 | + @root_type = 'Koa' | |
| 5 | + def get_name | |
| 6 | + at_css('h1').content | |
| 7 | + end | |
| 8 | + | |
| 9 | + def additional_entries | |
| 10 | + return [] unless slug.match?(/^api/) | |
| 11 | + type = get_name | |
| 12 | + css('h2, h3').to_a | |
| 13 | + .delete_if do |node| | |
| 14 | + node.content == 'API' || | |
| 15 | + (slug.include?('index') && !node.content.include?('.')) | |
| 16 | + end | |
| 17 | + .map do |node| | |
| 18 | + name = node.content.strip.sub(/\(.*\)\z/, '()') | |
| 19 | + type = 'API' if type == @root_type && name.include?('.') | |
| 20 | + [name, node['id'], type] | |
| 21 | + end | |
| 22 | + end | |
| 23 | + | |
| 24 | + def get_type | |
| 25 | + case slug | |
| 26 | + when /^api\/index/ | |
| 27 | + 'API' | |
| 28 | + when /^api/ | |
| 29 | + get_name | |
| 30 | + else | |
| 31 | + 'Guides' | |
| 32 | + end | |
| 33 | + end | |
| 34 | + end | |
| 35 | + end | |
| 36 | +end | |
| @@ -46,6 +46,13 @@ module Docs · 45 unmodified lines | ||
| 46 | 46 | parent.content = parent.content |
| 47 | 47 | parent['data-language'] = 'kotlin' |
| 48 | 48 | end |
| 49 | + | |
| 50 | + css('.tags').each do |wrapper| | |
| 51 | + platforms = wrapper.css('.platform:not(.tag-value-Common)').to_a | |
| 52 | + platforms = platforms.map { |node| "#{node.content} (#{node['data-tag-version']})" } | |
| 53 | + platforms = "<b>Platform and version requirements:</b> #{platforms.join ", "}" | |
| 54 | + wrapper.replace(platforms) | |
| 55 | + end | |
| 49 | 56 | end |
| 50 | 57 | end |
| 51 | 58 | end |
| @@ -5,7 +5,9 @@ module Docs · 4 unmodified lines | ||
| 5 | 5 | if subpath.start_with?('api') |
| 6 | 6 | breadcrumbs[1..-1].join('.') |
| 7 | 7 | else |
| 8 | − (at_css('h1') || at_css('h2')).content | |
| 8 | + node = (at_css('h1') || at_css('h2')) | |
| 9 | + return node.content unless node.nil? | |
| 10 | + subpath[/\/([a-z0-9_-]+)\./][1..-2].titleize.sub('Faq', 'FAQ') | |
| 9 | 11 | end |
| 10 | 12 | end |
| 11 | 13 | |
| @@ -8,10 +8,6 @@ module Docs · 7 unmodified lines | ||
| 8 | 8 | other |
| 9 | 9 | end |
| 10 | 10 | |
| 11 | − css('a.__cf_email__').each do |node| | |
| 12 | − node.replace(decode_cloudflare_email(node['data-cfemail'])) | |
| 13 | − end | |
| 14 | − | |
| 15 | 11 | # Remove code highlighting |
| 16 | 12 | css('pre').each do |node| |
| 17 | 13 | node.content = node.content |
| @@ -0,0 +1,40 @@ | ||
| 1 | +module Docs | |
| 2 | + class Leaflet | |
| 3 | + class CleanHtmlFilter < Filter | |
| 4 | + def call | |
| 5 | + css('#toc', '.expander', '.footer').remove | |
| 6 | + | |
| 7 | + css('h1').each do |node| | |
| 8 | + node.name = 'h2' | |
| 9 | + end | |
| 10 | + | |
| 11 | + at_css('> h2:first-child').name = 'h1' | |
| 12 | + | |
| 13 | + # remove "This reference reflects Leaflet 1.2.0." | |
| 14 | + css('h1 ~ p').each do |node| | |
| 15 | + node.remove | |
| 16 | + break | |
| 17 | + end | |
| 18 | + | |
| 19 | + css('section', 'code b', '.accordion', '.accordion-overflow', '.accordion-content').each do |node| | |
| 20 | + node.before(node.children).remove | |
| 21 | + end | |
| 22 | + | |
| 23 | + css('pre > code').each do |node| | |
| 24 | + node['class'] ||= '' | |
| 25 | + lang = if node['class'].include?('lang-html') || node.content =~ /\A</ | |
| 26 | + 'html' | |
| 27 | + elsif node['class'].include?('lang-css') | |
| 28 | + 'css' | |
| 29 | + elsif node['class'].include?('lang-js') || node['class'].include?('lang-javascript') | |
| 30 | + 'javascript' | |
| 31 | + end | |
| 32 | + node.parent['data-language'] = lang if lang | |
| 33 | + node.before(node.children).remove | |
| 34 | + end | |
| 35 | + | |
| 36 | + doc | |
| 37 | + end | |
| 38 | + end | |
| 39 | + end | |
| 40 | +end | |
| @@ -0,0 +1,33 @@ | ||
| 1 | +module Docs | |
| 2 | + class Leaflet | |
| 3 | + class EntriesFilter < Docs::EntriesFilter | |
| 4 | + def additional_entries | |
| 5 | + entries = [] | |
| 6 | + type = nil | |
| 7 | + subtype = nil | |
| 8 | + | |
| 9 | + css('*').each do |node| | |
| 10 | + if node.name == 'h2' && node['id'] | |
| 11 | + type = node.content | |
| 12 | + subtype = nil | |
| 13 | + entries << [type, node['id'], type] | |
| 14 | + elsif node.name == 'h3' | |
| 15 | + subtype = node.content | |
| 16 | + elsif node.name == 'tr' && node['id'] | |
| 17 | + value = node.css('td > code > b').first.content | |
| 18 | + if subtype && subtype.end_with?(' options') | |
| 19 | + name = "#{subtype}: #{value}" | |
| 20 | + elsif subtype | |
| 21 | + name = "#{type} #{subtype.downcase}: #{value}" | |
| 22 | + else | |
| 23 | + name = "#{type}: #{value}" | |
| 24 | + end | |
| 25 | + entries << [name, node['id'], type] | |
| 26 | + end | |
| 27 | + end | |
| 28 | + | |
| 29 | + entries | |
| 30 | + end | |
| 31 | + end | |
| 32 | + end | |
| 33 | +end | |
| @@ -14,6 +14,8 @@ module Docs · 13 unmodified lines | ||
| 14 | 14 | '#Quick_Links', |
| 15 | 15 | 'hr'] |
| 16 | 16 | |
| 17 | + BROWSER_UNNECESSARY_CLASS_REGEX = /\s*bc-browser[\w_-]+/ | |
| 18 | + | |
| 17 | 19 | def call |
| 18 | 20 | css(*REMOVE_NODES).remove |
| 19 | 21 | |
| @@ -61,6 +63,94 @@ module Docs · 41 unmodified lines | ||
| 61 | 63 | node.previous_element << node |
| 62 | 64 | end |
| 63 | 65 | |
| 66 | + # New compatibility tables | |
| 67 | + | |
| 68 | + css('.bc-data #Legend + dl', '.bc-data #Legend', '.bc-data #Legend_2 + dl', '.bc-data #Legend_2', '.bc-browser-name').remove | |
| 69 | + | |
| 70 | + css('abbr.only-icon[title="Full support"]', | |
| 71 | + 'abbr.only-icon[title="Partial support"]', | |
| 72 | + 'abbr.only-icon[title="No support"]', | |
| 73 | + 'abbr.only-icon[title="See implementation notes"]').remove | |
| 74 | + | |
| 75 | + css('.bc-data .ic-altname', '.bc-data .ic-deprecated', '.bc-data .ic-non-standard', '.bc-data .ic-experimental').each do |node| | |
| 76 | + node.parent.remove | |
| 77 | + end | |
| 78 | + | |
| 79 | + css('abbr.only-icon').each do |node| | |
| 80 | + node.replace(node.content) | |
| 81 | + end | |
| 82 | + | |
| 83 | + css('.bc-table .bc-platforms td', '.bc-table .bc-browsers td').each do |node| | |
| 84 | + node.name = 'th' | |
| 85 | + end | |
| 86 | + | |
| 87 | + css('.bc-data').each do |node| | |
| 88 | + link = node.at_css('.bc-github-link') | |
| 89 | + prev = node.previous_element | |
| 90 | + prev = prev.previous_element until prev.name == 'h2' | |
| 91 | + prev.add_child(link) | |
| 92 | + | |
| 93 | + node.before(node.children).remove | |
| 94 | + end | |
| 95 | + | |
| 96 | + css('.bc-table').each do |node| | |
| 97 | + desktop_table = node | |
| 98 | + | |
| 99 | + mobile_table = node.dup | |
| 100 | + desktop_table.after(mobile_table) | |
| 101 | + | |
| 102 | + if desktop_table.at_css('.bc-platform-server') | |
| 103 | + server_table = node.dup | |
| 104 | + mobile_table.after(server_table) | |
| 105 | + end | |
| 106 | + | |
| 107 | + desktop_columns = desktop_table.at_css('th.bc-platform-desktop')['colspan'].to_i | |
| 108 | + mobile_columns = desktop_table.at_css('th.bc-platform-mobile')['colspan'].to_i | |
| 109 | + | |
| 110 | + desktop_table.css('.bc-platform-mobile').remove | |
| 111 | + desktop_table.css('.bc-platform-server').remove | |
| 112 | + desktop_table.css('.bc-browsers th').to_a[(desktop_columns + 1)..-1].each(&:remove) | |
| 113 | + desktop_table.css('tr:not(.bc-platforms):not(.bc-browsers)').each do |line| | |
| 114 | + line.css('td').to_a[(desktop_columns)..-1].each(&:remove) | |
| 115 | + end | |
| 116 | + | |
| 117 | + mobile_table.css('.bc-platform-desktop').remove | |
| 118 | + mobile_table.css('.bc-platform-server').remove | |
| 119 | + mobile_table.css('.bc-browsers th').to_a[1..(desktop_columns)].each(&:remove) | |
| 120 | + mobile_table.css('.bc-browsers th').to_a[(mobile_columns + 1)..-1].each(&:remove) | |
| 121 | + mobile_table.css('tr:not(.bc-platforms):not(.bc-browsers)').each do |line| | |
| 122 | + line.css('td').to_a[0..(desktop_columns - 1)].each(&:remove) | |
| 123 | + line.css('td').to_a[(mobile_columns)..-1].each(&:remove) | |
| 124 | + end | |
| 125 | + | |
| 126 | + if server_table | |
| 127 | + server_table.css('.bc-platform-desktop').remove | |
| 128 | + server_table.css('.bc-platform-mobile').remove | |
| 129 | + server_table.css('.bc-browsers th').to_a[1..(desktop_columns + mobile_columns)].each(&:remove) | |
| 130 | + server_table.css('tr:not(.bc-platforms):not(.bc-browsers)').each do |line| | |
| 131 | + line.css('td').to_a[0..(desktop_columns + mobile_columns - 1)].each(&:remove) | |
| 132 | + end | |
| 133 | + end | |
| 134 | + end | |
| 135 | + | |
| 136 | + # Reduce page size to make the offline bundle smaller. | |
| 137 | + css('.bc-supports-unknown').remove_attr('class') | |
| 138 | + css('td[class*="bc-platform"], th[class*="bc-platform"]').remove_attr('class') | |
| 139 | + css('td[class*="bc-browser"], th[class*="bc-browser"]').each do |node| | |
| 140 | + class_name = node['class'] | |
| 141 | + class_name.remove!(BROWSER_UNNECESSARY_CLASS_REGEX) | |
| 142 | + | |
| 143 | + if class_name.present? | |
| 144 | + node['class'] = class_name | |
| 145 | + else | |
| 146 | + node.remove_attribute('class') | |
| 147 | + end | |
| 148 | + end | |
| 149 | + | |
| 150 | + css('abbr[title*="Compatibility unknown"]').each do |node| | |
| 151 | + node.before(node.children).remove | |
| 152 | + end | |
| 153 | + | |
| 64 | 154 | doc |
| 65 | 155 | end |
| 66 | 156 | end |
| @@ -30,6 +30,10 @@ module Docs · 29 unmodified lines | ||
| 30 | 30 | node['data-language'] = 'nginx' |
| 31 | 31 | end |
| 32 | 32 | |
| 33 | + css('code code').each do |node| | |
| 34 | + node.before(node.children).remove | |
| 35 | + end | |
| 36 | + | |
| 33 | 37 | doc |
| 34 | 38 | end |
| 35 | 39 | end |
| @@ -20,7 +20,7 @@ module Docs · 19 unmodified lines | ||
| 20 | 20 | def additional_entries |
| 21 | 21 | css('h1 + ul a').each_with_object [] do |node, entries| |
| 22 | 22 | name = node.content.strip |
| 23 | − next if name =~ /\A[A-Z]/ | |
| 23 | + next if name =~ /\A[A-Z]/ || name.start_with?('/') | |
| 24 | 24 | |
| 25 | 25 | id = node['href'].remove('#') |
| 26 | 26 | next if id.blank? |
| @@ -16,16 +16,12 @@ module Docs · 15 unmodified lines | ||
| 16 | 16 | |
| 17 | 17 | css('pre').each do |node| |
| 18 | 18 | if lang = node.at_css('code')['class'] |
| 19 | − node['data-language'] = lang.remove('lang-') | |
| 19 | + node['data-language'] = lang.remove(%r{lang(uage)?-}) | |
| 20 | 20 | end |
| 21 | 21 | |
| 22 | 22 | node.content = node.content |
| 23 | 23 | end |
| 24 | 24 | |
| 25 | − css('.__cf_email__').each do |node| | |
| 26 | − node.replace(decode_cloudflare_email(node['data-cfemail'])) | |
| 27 | − end | |
| 28 | − | |
| 29 | 25 | doc |
| 30 | 26 | end |
| 31 | 27 | end |
| @@ -26,6 +26,7 @@ module Docs · 25 unmodified lines | ||
| 26 | 26 | |
| 27 | 27 | def get_type |
| 28 | 28 | type = at_css('h1').content.strip |
| 29 | + type.remove! %r{\[.*\]} | |
| 29 | 30 | REPLACE_TYPES[type] || "#{type.first.upcase}#{type[1..-1]}" |
| 30 | 31 | end |
| 31 | 32 | |
| @@ -40,6 +41,7 @@ module Docs · 8 unmodified lines | ||
| 40 | 41 | |
| 41 | 42 | klass = nil if node.name == 'h2' |
| 42 | 43 | name = node.content.strip |
| 44 | + name.remove! %r{\s*\[src\]} | |
| 43 | 45 | |
| 44 | 46 | # Skip constructors |
| 45 | 47 | if name.start_with? 'new ' |
| @@ -9,7 +9,7 @@ module Docs · 8 unmodified lines | ||
| 9 | 9 | css('meta', '.colophon').remove |
| 10 | 10 | end |
| 11 | 11 | |
| 12 | − css('a.deep-link[id]').each do |node| | |
| 12 | + css('a.deep-link[id]', 'a.anchor[id]').each do |node| | |
| 13 | 13 | node.parent['id'] = node['id'] |
| 14 | 14 | node.remove |
| 15 | 15 | end |
| @@ -28,11 +28,11 @@ module Docs · 27 unmodified lines | ||
| 28 | 28 | when 'files/package.json' |
| 29 | 29 | css('#page > h2').each_with_object [] do |node, entries| |
| 30 | 30 | next if node.content =~ /\A[A-Z]/ |
| 31 | − entries << ["package.json: #{node.content}", node.at_css('.deep-link')['id']] | |
| 31 | + entries << ["package.json: #{node.content}", node.at_css('.anchor')['id']] | |
| 32 | 32 | end |
| 33 | 33 | when 'misc/config' |
| 34 | 34 | css('h2:contains("Config Settings") ~ h3').map do |node| |
| 35 | − ["config: #{node.content}", node.at_css('.deep-link')['id']] | |
| 35 | + ["config: #{node.content}", node.at_css('.anchor')['id']] | |
| 36 | 36 | end |
| 37 | 37 | else |
| 38 | 38 | [] |
| @@ -2,8 +2,10 @@ module Docs · 1 unmodified line | ||
| 2 | 2 | class Pandas |
| 3 | 3 | class EntriesFilter < Docs::EntriesFilter |
| 4 | 4 | def get_name |
| 5 | − if subpath.start_with?('generated') | |
| 6 | − name = at_css('dt').content.strip | |
| 5 | + if subpath.start_with?('generated') || (subpath.include?('reference') && !subpath.include?('reference/index')) | |
| 6 | + name_node = at_css('dt') | |
| 7 | + name_node = at_css('h1') if name_node.nil? | |
| 8 | + name = name_node.content.strip | |
| 7 | 9 | name.sub! %r{\(.*}, '()' |
| 8 | 10 | name.remove! %r{\s=.*} |
| 9 | 11 | name.remove! %r{\A(class(method)?) (pandas\.)?} |
| @@ -16,7 +18,7 @@ module Docs · 6 unmodified lines | ||
| 16 | 18 | end |
| 17 | 19 | |
| 18 | 20 | def get_type |
| 19 | − if subpath.start_with?('generated') | |
| 21 | + if subpath.start_with?('generated') || (subpath.include?('reference') && !subpath.include?('reference/index')) | |
| 20 | 22 | css('.toctree-l2.current > a').last.content.remove(/\s\(.+?\)/) |
| 21 | 23 | else |
| 22 | 24 | 'Manual' |
| @@ -13,18 +13,22 @@ module Docs · 12 unmodified lines | ||
| 13 | 13 | def other |
| 14 | 14 | @doc = at_css('#docContent') |
| 15 | 15 | |
| 16 | − css('.NAVHEADER', 'hr', '.NAVFOOTER a[accesskey="H"]').remove | |
| 16 | + css('.navheader', 'hr', '.navfooter a[accesskey="H"]').remove | |
| 17 | + | |
| 18 | + unless at_css('h1') | |
| 19 | + at_css('.refnamediv h2, .titlepage h2').name = 'h1' | |
| 20 | + end | |
| 17 | 21 | |
| 18 | 22 | css('a[name]').each do |node| |
| 19 | 23 | node.parent['id'] = node['name'] |
| 20 | 24 | node.before(node.children).remove |
| 21 | 25 | end |
| 22 | 26 | |
| 23 | − css('div.SECT1', 'pre > kbd', 'tt > code', 'h1 > tt', '> .CHAPTER', 'div.NOTE', '.APPENDIX').each do |node| | |
| 27 | + css('div.sect1', '.refentry', '.refnamediv', '.refentrytitle', '.refsynopsisdiv', 'pre > kbd', 'tt > code', 'h1 > tt', '> .chapter', '.appendix', '.titlepage', 'div:not([class]):not([id])', 'br', 'a.indexterm', 'acronym', '.productname', 'div.itemizedlist', 'span.sect2', 'span.application', 'em.replaceable', 'span.term').each do |node| | |
| 24 | 28 | node.before(node.children).remove |
| 25 | 29 | end |
| 26 | 30 | |
| 27 | − css('div.CAUTION table.CAUTION').each do |node| | |
| 31 | + css('div.caution table.caution').each do |node| | |
| 28 | 32 | parent = node.parent |
| 29 | 33 | title = node.at_css('.c2, .c3, .c4, .c5').content |
| 30 | 34 | node.replace(node.css('p')) |
| @@ -43,11 +47,27 @@ module Docs · 12 unmodified lines | ||
| 43 | 47 | node.remove_attribute 'valign' |
| 44 | 48 | end |
| 45 | 49 | |
| 50 | + css('.sect2 > h3').each do |node| | |
| 51 | + node.name = 'h2' | |
| 52 | + end | |
| 53 | + | |
| 54 | + css('.sect3 > h4').each do |node| | |
| 55 | + node.name = 'h3' | |
| 56 | + end | |
| 57 | + | |
| 46 | 58 | css('tt').each do |node| |
| 47 | 59 | node.name = 'code' |
| 48 | 60 | end |
| 49 | 61 | |
| 50 | − css('.REFSYNOPSISDIV > p').each do |node| | |
| 62 | + css('div.note', 'div.important', 'div.tip', 'div.caution').each do |node| | |
| 63 | + if node.at_css('blockquote') | |
| 64 | + node.before(node.children).remove | |
| 65 | + else | |
| 66 | + node.name = 'blockquote' | |
| 67 | + end | |
| 68 | + end | |
| 69 | + | |
| 70 | + css('.refsynopsisdiv > p').each do |node| | |
| 51 | 71 | node.name = 'pre' |
| 52 | 72 | node.content = node.content |
| 53 | 73 | end |
| @@ -56,9 +76,18 @@ module Docs · 2 unmodified lines | ||
| 56 | 76 | node.before(node.children).remove |
| 57 | 77 | end |
| 58 | 78 | |
| 59 | − css('pre.SYNOPSIS', 'pre.PROGRAMLISTING').each do |node| | |
| 79 | + css('code').each do |node| | |
| 80 | + node.inner_html = node.inner_html.gsub(/\s*\n\s*/, ' ') | |
| 81 | + end | |
| 82 | + | |
| 83 | + css('pre.synopsis', 'pre.programlisting').each do |node| | |
| 60 | 84 | node['data-language'] = 'sql' |
| 61 | 85 | end |
| 86 | + | |
| 87 | + css('h1', 'ul', 'li', 'pre').each do |node| | |
| 88 | + node.remove_attribute 'class' | |
| 89 | + node.remove_attribute 'style' | |
| 90 | + end | |
| 62 | 91 | end |
| 63 | 92 | end |
| 64 | 93 | end |
| @@ -90,33 +90,33 @@ module Docs · 89 unmodified lines | ||
| 90 | 90 | return config_additional_entries if type && type.include?('Configuration') |
| 91 | 91 | return data_types_additional_entries if type == 'Data Types' |
| 92 | 92 | return command_additional_entries if type == 'Commands' |
| 93 | − return get_heading_entries('h3[id]') if slug == 'functions-xml' | |
| 93 | + return get_heading_entries('h3[id], .sect3[id] > h3:first-child') if slug == 'functions-xml' | |
| 94 | 94 | |
| 95 | − entries = get_heading_entries('h2[id]') | |
| 95 | + entries = get_heading_entries('h2[id], .sect2[id] > h2:first-child') | |
| 96 | 96 | |
| 97 | 97 | case slug |
| 98 | 98 | when 'queries-union' |
| 99 | − entries.concat get_custom_entries('p > .LITERAL:first-child') | |
| 99 | + entries.concat get_custom_entries('p > .literal:first-child') | |
| 100 | 100 | when 'queries-table-expressions' |
| 101 | − entries.concat get_heading_entries('h3[id]') | |
| 102 | − entries.concat get_custom_entries('dt > .LITERAL:first-child') | |
| 101 | + entries.concat get_heading_entries('h3[id], .sect3[id] > h3:first-child') | |
| 102 | + entries.concat get_custom_entries('dt > .literal:first-child') | |
| 103 | 103 | when 'functions-logical' |
| 104 | 104 | entries.concat get_custom_entries('> table td:first-child > code') |
| 105 | 105 | when 'functions-formatting' |
| 106 | 106 | entries.concat get_custom_entries('#FUNCTIONS-FORMATTING-TABLE td:first-child > code') |
| 107 | 107 | when 'functions-admin' |
| 108 | − entries.concat get_custom_entries('.TABLE td:first-child > code') | |
| 108 | + entries.concat get_custom_entries('.table td:first-child > code') | |
| 109 | 109 | when 'functions-string' |
| 110 | 110 | entries.concat get_custom_entries('> div[id^="FUNC"] td:first-child > code') |
| 111 | 111 | entries.concat get_custom_entries('> div[id^="FORMAT"] td:first-child > code') |
| 112 | 112 | else |
| 113 | 113 | if type && type.start_with?('Functions') |
| 114 | − entries.concat get_custom_entries('> .TABLE td:first-child > code.LITERAL:first-child') | |
| 115 | − entries.concat get_custom_entries('> .TABLE td:first-child > code.FUNCTION:first-child') | |
| 116 | − entries.concat get_custom_entries('> .TABLE td:first-child > code:not(.LITERAL):first-child + code.LITERAL') | |
| 117 | − entries.concat get_custom_entries('> .TABLE td:first-child > p > code.LITERAL:first-child') | |
| 118 | − entries.concat get_custom_entries('> .TABLE td:first-child > p > code.FUNCTION:first-child') | |
| 119 | − entries.concat get_custom_entries('> .TABLE td:first-child > p > code:not(.LITERAL):first-child + code.LITERAL') | |
| 114 | + entries.concat get_custom_entries('> .table td:first-child > code.literal:first-child') | |
| 115 | + entries.concat get_custom_entries('> .table td:first-child > code.function:first-child') | |
| 116 | + entries.concat get_custom_entries('> .table td:first-child > code:not(.literal):first-child + code.literal') | |
| 117 | + entries.concat get_custom_entries('> .table td:first-child > p > code.literal:first-child') | |
| 118 | + entries.concat get_custom_entries('> .table td:first-child > p > code.function:first-child') | |
| 119 | + entries.concat get_custom_entries('> .table td:first-child > p > code:not(.literal):first-child + code.literal') | |
| 120 | 120 | if slug == 'functions-comparison' && !at_css('#FUNCTIONS-COMPARISON-PRED-TABLE') # before 9.6 |
| 121 | 121 | entries.concat %w(IS NULL BETWEEN DISTINCT\ FROM).map { |name| ["#{self.name}: #{name}"] } |
| 122 | 122 | end |
| @@ -127,8 +127,8 @@ module Docs · 4 unmodified lines | ||
| 127 | 127 | end |
| 128 | 128 | |
| 129 | 129 | def config_additional_entries |
| 130 | − css('.VARIABLELIST dt[id]').map do |node| | |
| 131 | − name = node.at_css('.VARNAME').content | |
| 130 | + css('.variablelist dt[id]').map do |node| | |
| 131 | + name = node.at_css('.varname').content | |
| 132 | 132 | ["Config: #{name}", node['id']] |
| 133 | 133 | end |
| 134 | 134 | end |
| @@ -136,27 +136,27 @@ module Docs · 1 unmodified line | ||
| 136 | 136 | def data_types_additional_entries |
| 137 | 137 | selector = case slug |
| 138 | 138 | when 'rangetypes' |
| 139 | − 'li > p > .TYPE:first-child' | |
| 139 | + 'li > p > .type:first-child' | |
| 140 | 140 | when 'datatype-textsearch' |
| 141 | − '.SECT2 > .TYPE' | |
| 141 | + '.title > .type, .sect2 > .type' | |
| 142 | 142 | else |
| 143 | − '.CALSTABLE td:first-child > .TYPE' | |
| 143 | + '.table-contents td:first-child > .type, .calstable td:first-child > .type' | |
| 144 | 144 | end |
| 145 | 145 | get_custom_entries(selector) |
| 146 | 146 | end |
| 147 | 147 | |
| 148 | 148 | def command_additional_entries |
| 149 | − css('.REFSECT2[id^="SQL"]').each_with_object([]) do |node, entries| | |
| 149 | + css('.refsect2[id^="SQL"]').each_with_object([]) do |node, entries| | |
| 150 | 150 | next unless heading = node.at_css('h3') |
| 151 | 151 | next unless heading.content.strip =~ /[A-Z_\-]+ Clause/ |
| 152 | − name = heading.at_css('.LITERAL').content | |
| 152 | + name = heading.at_css('.literal').content | |
| 153 | 153 | name.prepend "#{self.name} ... " |
| 154 | 154 | entries << [name, node['id']] |
| 155 | 155 | end |
| 156 | 156 | end |
| 157 | 157 | |
| 158 | 158 | def include_default_entry? |
| 159 | − !initial_page? && !at_css('.TOC') && type | |
| 159 | + !initial_page? && (!at_css('.toc') || at_css('.sect2, .variablelist, .refsect1')) && type | |
| 160 | 160 | end |
| 161 | 161 | |
| 162 | 162 | SKIP_ENTRIES_SLUGS = [ |
| @@ -199,7 +199,9 @@ module Docs · 36 unmodified lines | ||
| 199 | 199 | css(selector).each_with_object([]) do |node, entries| |
| 200 | 200 | name = node.content |
| 201 | 201 | clean_heading_name(name) |
| 202 | − entries << ["#{additional_entry_prefix}: #{name}", node['id']] unless skip_heading?(name) | |
| 202 | + id = node['id'] || node.parent['id'] | |
| 203 | + raise "missing ids for selector #{selector}" unless id | |
| 204 | + entries << ["#{additional_entry_prefix}: #{name}", id] unless skip_heading?(name) | |
| 203 | 205 | end |
| 204 | 206 | end |
| 205 | 207 | |
| @@ -8,19 +8,21 @@ module Docs · 7 unmodified lines | ||
| 8 | 8 | end |
| 9 | 9 | |
| 10 | 10 | def extract_up_path |
| 11 | − if node = at_css('.NAVHEADER a[accesskey="U"]') | |
| 11 | + if node = at_css('.navheader a[accesskey="u"], .navheader a[accesskey="U"]') | |
| 12 | 12 | result[:pg_up_path] = node['href'] |
| 13 | 13 | end |
| 14 | 14 | end |
| 15 | 15 | |
| 16 | 16 | def extract_chapter |
| 17 | − return unless text = at_css('.NAVHEADER td[align="center"]').content | |
| 18 | − if match = text.match(/\AChapter (\d+)\. (.+)\z/) | |
| 19 | − result[:pg_chapter] = match[1].to_i | |
| 20 | − result[:pg_chapter_name] = match[2].strip | |
| 21 | − elsif match = text.match(/\AAppendix ([A-Z])\. (.+)\z/) | |
| 22 | − result[:pg_appendix] = match[1] | |
| 23 | − result[:pg_appendix_name] = match[2].strip | |
| 17 | + css('.navheader td[align="center"], .navheader th[align="center"]').each do |node| | |
| 18 | + text = node.content.strip | |
| 19 | + if match = text.match(/\AChapter (\d+)\. (.+)\z/) | |
| 20 | + result[:pg_chapter] = match[1].to_i | |
| 21 | + result[:pg_chapter_name] = match[2].strip | |
| 22 | + elsif match = text.match(/\AAppendix ([A-Z])\. (.+)\z/) | |
| 23 | + result[:pg_appendix] = match[1] | |
| 24 | + result[:pg_appendix_name] = match[2].strip | |
| 25 | + end | |
| 24 | 26 | end |
| 25 | 27 | end |
| 26 | 28 | end |
| @@ -0,0 +1,13 @@ | ||
| 1 | +module Docs | |
| 2 | + class Postgresql | |
| 3 | + class NormalizeClassNamesFilter < Filter | |
| 4 | + def call | |
| 5 | + doc.css('*').each do |node| | |
| 6 | + node['class'] = node['class'].downcase if node['class'].present? | |
| 7 | + end | |
| 8 | + | |
| 9 | + doc | |
| 10 | + end | |
| 11 | + end | |
| 12 | + end | |
| 13 | +end | |
| @@ -0,0 +1,24 @@ | ||
| 1 | +module Docs | |
| 2 | + class Puppeteer | |
| 3 | + class CleanHtmlFilter < Filter | |
| 4 | + def call | |
| 5 | + at_css('h1').content = 'Puppeteer Documentation' | |
| 6 | + | |
| 7 | + # None of the elements to remove have classes, so the order of the remove calls is trivial | |
| 8 | + | |
| 9 | + # Remove links to previous versions of the reference | |
| 10 | + at_css('h1 + ul').remove | |
| 11 | + | |
| 12 | + # Remove table of contents | |
| 13 | + at_css('h1 + h5').remove | |
| 14 | + at_css('h1 + ul').remove | |
| 15 | + | |
| 16 | + # Make headers bigger by transforming them into a bigger variant | |
| 17 | + css('h3').each { |node| node.name = 'h2' } | |
| 18 | + css('h4').each { |node| node.name = 'h3' } | |
| 19 | + | |
| 20 | + doc | |
| 21 | + end | |
| 22 | + end | |
| 23 | + end | |
| 24 | +end | |
| @@ -0,0 +1,41 @@ | ||
| 1 | +module Docs | |
| 2 | + class Puppeteer | |
| 3 | + class EntriesFilter < Docs::EntriesFilter | |
| 4 | + # The entire reference is one big page, so get_name and get_type are not necessary | |
| 5 | + | |
| 6 | + def additional_entries | |
| 7 | + entries = [] | |
| 8 | + | |
| 9 | + css('h3').each do |node| | |
| 10 | + name = node.content.split(': ').last | |
| 11 | + | |
| 12 | + # Find all sub-items (all h4's between the current h3 and the next) | |
| 13 | + current = node.next | |
| 14 | + while !current.nil? && current.name != 'h3' | |
| 15 | + if current.name == 'h4' | |
| 16 | + current_name = current.content | |
| 17 | + | |
| 18 | + # Prepend events with the class name | |
| 19 | + if current_name.start_with?('event: ') | |
| 20 | + current_name = "#{name} event: #{current_name[/'(.*)'/, 1]}" | |
| 21 | + end | |
| 22 | + | |
| 23 | + # Remove arguments from functions | |
| 24 | + if current_name.include?('(') | |
| 25 | + current_name = current_name.split('(')[0] + '()' | |
| 26 | + end | |
| 27 | + | |
| 28 | + entries << [current_name, current['id'], name] | |
| 29 | + end | |
| 30 | + | |
| 31 | + current = current.next | |
| 32 | + end | |
| 33 | + | |
| 34 | + entries << [name, node['id'], name] | |
| 35 | + end | |
| 36 | + | |
| 37 | + entries | |
| 38 | + end | |
| 39 | + end | |
| 40 | + end | |
| 41 | +end | |
| @@ -0,0 +1,102 @@ | ||
| 1 | +module Docs | |
| 2 | + class Pygame | |
| 3 | + class CleanHtmlFilter < Filter | |
| 4 | + def call | |
| 5 | + @doc = at_css '.body' | |
| 6 | + | |
| 7 | + if root_page? | |
| 8 | + # remove unneeded stuff | |
| 9 | + at_css('.modindex-jumpbox').remove | |
| 10 | + css('[role="navigation"],.pcap, .cap, .footer').remove | |
| 11 | + # table -> list | |
| 12 | + list = at_css('table') | |
| 13 | + list.replace(list.children) | |
| 14 | + list.name = 'ul' | |
| 15 | + css('tr').each do |row| | |
| 16 | + row.name = 'li' | |
| 17 | + row.remove_attribute('class') | |
| 18 | + end | |
| 19 | + at_css('h1').content = 'Pygame' | |
| 20 | + return doc | |
| 21 | + end | |
| 22 | + | |
| 23 | + # remove unwanted stuff | |
| 24 | + # .headerlink => ¶ after links | |
| 25 | + # .toc => table of content | |
| 26 | + # .tooltip-content => tooltips after links to functions | |
| 27 | + css('table.toc.docutils, .headerlink, .tooltip-content').remove | |
| 28 | + | |
| 29 | + # Remove wrapper .section | |
| 30 | + section = at_css('.section') | |
| 31 | + definition = at_css('.definition') | |
| 32 | + definition['id'] = section['id'] | |
| 33 | + section.replace(section.children) | |
| 34 | + | |
| 35 | + # Format code for it be highlighted | |
| 36 | + css('.highlight-default.notranslate').each do |node| | |
| 37 | + pre = node.at_css('pre') | |
| 38 | + node.replace(pre) | |
| 39 | + # gets rid of the already existing syntax highlighting | |
| 40 | + pre.content = pre.content | |
| 41 | + pre['class'] = 'language-python' | |
| 42 | + pre['data-language'] = "python" | |
| 43 | + end | |
| 44 | + | |
| 45 | + # change descriptions of functions/attributes to blockquote | |
| 46 | + css('.line-block').each do |node| | |
| 47 | + node.name = 'blockquote' | |
| 48 | + end | |
| 49 | + | |
| 50 | + # change functions | |
| 51 | + css('.definition').each do |d| | |
| 52 | + | |
| 53 | + # the header is the function/attribute name. It might look something like | |
| 54 | + # this: | |
| 55 | + # pygame.image.load() | |
| 56 | + # It'll end up being something like this: | |
| 57 | + # pygame.image.load(filename) -> Surface | |
| 58 | + # pygame.image.load(fileobj, namehint="") -> Surface | |
| 59 | + | |
| 60 | + header = d.at_css('dt.title') | |
| 61 | + if d['class'].include?('class') or d['class'].include?('module') | |
| 62 | + header.name = 'h1' | |
| 63 | + @section = header.content.strip | |
| 64 | + else | |
| 65 | + header.name = 'h3' | |
| 66 | + end | |
| 67 | + # save the original header | |
| 68 | + initial_header = header.content.strip | |
| 69 | + # save the real name for the entries | |
| 70 | + header['data-name'] = initial_header | |
| 71 | + # empty the header | |
| 72 | + if header.name == 'h3' | |
| 73 | + header.inner_html = '' | |
| 74 | + end | |
| 75 | + # to replace it with the signatures | |
| 76 | + next_el = header.next_element | |
| 77 | + signatures = next_el.css('.signature') | |
| 78 | + signatures.each do |sig| | |
| 79 | + sig.name = 'code' | |
| 80 | + if header.name == 'h3' | |
| 81 | + sig.parent = header | |
| 82 | + # the signature don't contain pygame.module. I think it's better | |
| 83 | + # to display them, as it avoids confusion with methods (have a | |
| 84 | + # look at the pygame.Rect page) | |
| 85 | + if initial_header.start_with?(@section) | |
| 86 | + sig.content = @section + '.' + sig.text | |
| 87 | + end | |
| 88 | + # separate the signatures on different lines. | |
| 89 | + header.add_child "<br>" | |
| 90 | + end | |
| 91 | + end | |
| 92 | + end | |
| 93 | + | |
| 94 | + css('> dl', '> dl > dd', 'h1 code').each do |node| | |
| 95 | + node.before(node.children).remove | |
| 96 | + end | |
| 97 | + | |
| 98 | + doc | |
| 99 | + end | |
| 100 | + end | |
| 101 | + end | |
| 102 | +end | |
| @@ -0,0 +1,29 @@ | ||
| 1 | +module Docs | |
| 2 | + class Pygame | |
| 3 | + class EntriesFilter < Docs::EntriesFilter | |
| 4 | + def get_name | |
| 5 | + at_css('h1').content.remove('pygame.') | |
| 6 | + end | |
| 7 | + | |
| 8 | + def get_type | |
| 9 | + at_css('h1').content | |
| 10 | + end | |
| 11 | + | |
| 12 | + def additional_entries | |
| 13 | + return [] if root_page? | |
| 14 | + | |
| 15 | + css('h1, h2, h3').each_with_object [] do |node, entries| | |
| 16 | + name = node['id'] || node['data-name'] | |
| 17 | + | |
| 18 | + if node.parent['class'].include?('function') or node.parent['class'].include?('method') | |
| 19 | + name << '()' | |
| 20 | + end | |
| 21 | + | |
| 22 | + name.remove!('pygame.') | |
| 23 | + | |
| 24 | + entries << [name, node['id']] unless name == self.name | |
| 25 | + end | |
| 26 | + end | |
| 27 | + end | |
| 28 | + end | |
| 29 | +end | |
| @@ -15,15 +15,17 @@ module Docs · 14 unmodified lines | ||
| 15 | 15 | |
| 16 | 16 | def other |
| 17 | 17 | css('h1').each do |node| |
| 18 | − node.content = node.content.sub!(/\A[\d\.]+/) do |str| | |
| 18 | + node.content = node.content.sub(/\A[\d\.]+/) do |str| | |
| 19 | 19 | rgx = /\A#{str}/ |
| 20 | 20 | @levelRegexp = @levelRegexp ? Regexp.union(@levelRegexp, rgx) : rgx |
| 21 | 21 | '' |
| 22 | 22 | end |
| 23 | 23 | end |
| 24 | 24 | |
| 25 | − css('h2', 'h3', 'h4').each do |node| | |
| 26 | − node.inner_html = node.inner_html.remove @levelRegexp | |
| 25 | + unless @levelRegexp.nil? | |
| 26 | + css('h2', 'h3', 'h4').each do |node| | |
| 27 | + node.inner_html = node.inner_html.remove @levelRegexp | |
| 28 | + end | |
| 27 | 29 | end |
| 28 | 30 | end |
| 29 | 31 | end |
| @@ -57,16 +57,10 @@ module Docs · 56 unmodified lines | ||
| 57 | 57 | clean_id_attributes |
| 58 | 58 | entries = [] |
| 59 | 59 | |
| 60 | − css('.class > dt[id]', '.exception > dt[id]', '.attribute > dt[id]').each do |node| | |
| 60 | + css('.class > dt[id]', '.exception > dt[id]', '.attribute > dt[id]', '.data > dt[id]').each do |node| | |
| 61 | 61 | entries << [node['id'], node['id']] |
| 62 | 62 | end |
| 63 | 63 | |
| 64 | − css('.data > dt[id]').each do |node| | |
| 65 | − if node['id'].split('.').last.upcase! # skip constants | |
| 66 | − entries << [node['id'], node['id']] | |
| 67 | − end | |
| 68 | − end | |
| 69 | − | |
| 70 | 64 | css('.function > dt[id]', '.method > dt[id]', '.staticmethod > dt[id]', '.classmethod > dt[id]').each do |node| |
| 71 | 65 | entries << [node['id'] + '()', node['id']] |
| 72 | 66 | end |
| @@ -2,16 +2,18 @@ module Docs · 1 unmodified line | ||
| 2 | 2 | class Python |
| 3 | 3 | class EntriesV3Filter < Docs::EntriesFilter |
| 4 | 4 | REPLACE_TYPES = { |
| 5 | − 'Cryptographic' => 'Cryptography', | |
| 6 | − 'Custom Interpreters' => 'Interpreters', | |
| 7 | − 'Data Compression & Archiving' => 'Data Compression', | |
| 8 | − 'Generic Operating System' => 'Operating System', | |
| 9 | − 'Graphical User Interfaces with Tk' => 'Tk', | |
| 10 | − 'Internet Data Handling' => 'Internet Data', | |
| 11 | − 'Internet Protocols & Support' => 'Internet', | |
| 12 | − 'Interprocess Communication & Networking' => 'Networking', | |
| 13 | − 'Program Frameworks' => 'Frameworks', | |
| 14 | − 'Structured Markup Processing Tools' => 'Structured Markup' } | |
| 5 | + 'contextvars — Context Variables' => 'Context Variables', | |
| 6 | + 'Cryptographic' => 'Cryptography', | |
| 7 | + 'Custom Interpreters' => 'Interpreters', | |
| 8 | + 'Data Compression & Archiving' => 'Data Compression', | |
| 9 | + 'email — An email & MIME handling package' => 'Email', | |
| 10 | + 'Generic Operating System' => 'Operating System', | |
| 11 | + 'Graphical User Interfaces with Tk' => 'Tk', | |
| 12 | + 'Internet Data Handling' => 'Internet Data', | |
| 13 | + 'Internet Protocols & Support' => 'Internet', | |
| 14 | + 'Interprocess Communication & Networking' => 'Networking', | |
| 15 | + 'Program Frameworks' => 'Frameworks', | |
| 16 | + 'Structured Markup Processing Tools' => 'Structured Markup' } | |
| 15 | 17 | |
| 16 | 18 | def get_name |
| 17 | 19 | name = at_css('h1').content |
| @@ -25,6 +27,7 @@ module Docs · 7 unmodified lines | ||
| 25 | 27 | |
| 26 | 28 | def get_type |
| 27 | 29 | return 'Logging' if slug.start_with? 'library/logging' |
| 30 | + return 'Asynchronous I/O' if slug.start_with? 'library/asyncio' | |
| 28 | 31 | |
| 29 | 32 | type = at_css('.related a[accesskey="U"]').content |
| 30 | 33 | |
| @@ -45,6 +48,7 @@ module Docs · 14 unmodified lines | ||
| 45 | 48 | end |
| 46 | 49 | |
| 47 | 50 | def include_default_entry? |
| 51 | + return true if slug == 'library/asyncio' | |
| 48 | 52 | !at_css('.body > .section:only-child > .toctree-wrapper:last-child') && !type.in?(%w(Superseded)) |
| 49 | 53 | end |
| 50 | 54 | |
| @@ -53,16 +57,10 @@ module Docs · 2 unmodified lines | ||
| 53 | 57 | clean_id_attributes |
| 54 | 58 | entries = [] |
| 55 | 59 | |
| 56 | − css('.class > dt[id]', '.exception > dt[id]', '.attribute > dt[id]').each do |node| | |
| 60 | + css('.class > dt[id]', '.exception > dt[id]', '.attribute > dt[id]', '.data > dt[id]').each do |node| | |
| 57 | 61 | entries << [node['id'], node['id']] |
| 58 | 62 | end |
| 59 | 63 | |
| 60 | − css('.data > dt[id]').each do |node| | |
| 61 | − if node['id'].split('.').last.upcase! # skip constants | |
| 62 | − entries << [node['id'], node['id']] | |
| 63 | − end | |
| 64 | − end | |
| 65 | − | |
| 66 | 64 | css('.function > dt[id]', '.method > dt[id]', '.staticmethod > dt[id]', '.classmethod > dt[id]').each do |node| |
| 67 | 65 | entries << [node['id'] + '()', node['id']] |
| 68 | 66 | end |
| @@ -0,0 +1,32 @@ | ||
| 1 | +module Docs | |
| 2 | + class Qt | |
| 3 | + class CleanHtmlFilter < Filter | |
| 4 | + def call | |
| 5 | + # Remove unneeded elements | |
| 6 | + css('.copy-notice, .navigationbar, .headerNavi, .footerNavi, .sidebar, .toc, #ec_toggle', '.landingicons img', 'br').remove | |
| 7 | + | |
| 8 | + # QML property/method header | |
| 9 | + css('.qmlproto').each do |node| | |
| 10 | + id = node.at_css('tr')['id'] | |
| 11 | + id = node.at_css('a')['name'] if id.blank? | |
| 12 | + node.inner_html = node.at_css('td').inner_html | |
| 13 | + node.name = 'h3' | |
| 14 | + node['id'] = id | |
| 15 | + end | |
| 16 | + | |
| 17 | + css('.main-rounded', '.content', '.line', '.context', '.descr', '.types', '.func', '.table', 'div:not([class])', '.landing', '.col-1', '.heading', '.qmlitem', '.qmldoc', 'div.pre').each do |node| | |
| 18 | + node.before(node.children).remove | |
| 19 | + end | |
| 20 | + | |
| 21 | + css('pre').each do |node| | |
| 22 | + node.content = node.content | |
| 23 | + node['data-language'] = 'cpp' if node['class'].include?('cpp') | |
| 24 | + node['data-language'] = 'qml' if node['class'].include?('qml') | |
| 25 | + node.remove_attribute('class') | |
| 26 | + end | |
| 27 | + | |
| 28 | + doc | |
| 29 | + end | |
| 30 | + end | |
| 31 | + end | |
| 32 | +end | |
| @@ -0,0 +1,142 @@ | ||
| 1 | +# frozen_string_literal: true | |
| 2 | + | |
| 3 | +module Docs | |
| 4 | + class Qt | |
| 5 | + class EntriesFilter < Docs::EntriesFilter | |
| 6 | + def get_name | |
| 7 | + header = at_css('h1.title + .small-subtitle a') || at_css('h1.title') || at_css('.context h2') | |
| 8 | + name = header.content | |
| 9 | + name.sub! %r{ Class$}, ' (class)' | |
| 10 | + name.sub! %r{ QML Type$}, ' (QML type)' | |
| 11 | + name.sub! %r{ QML Basic Type$}, ' (QML basic type)' | |
| 12 | + | |
| 13 | + # Add '(class)' to the class pages where the subtitle name is used (e.g. qset-const-iterator.html) | |
| 14 | + if at_css('h1.title').content.strip.end_with?(' Class') && !name.include?('(class)') | |
| 15 | + name = "#{name} (class) " | |
| 16 | + end | |
| 17 | + | |
| 18 | + name | |
| 19 | + end | |
| 20 | + | |
| 21 | + def get_type | |
| 22 | + breadcrumb = css('#main_title_bar + ul li') | |
| 23 | + category = if breadcrumb.length < 3 | |
| 24 | + then 'Qt'.dup | |
| 25 | + else breadcrumb.at(1).content | |
| 26 | + end | |
| 27 | + | |
| 28 | + if category == 'Qt' | |
| 29 | + return 'Qt Platforms' if name.include?(' for ') || name == 'Qt Platform Abstraction' | |
| 30 | + return 'Qt Quick' if name == 'Qt Quick Test' || name == 'Qt Quick Test Reference Documentation' | |
| 31 | + | |
| 32 | + alwaysInQt = ['Qt Configure Options', 'Qt Image Formats'] | |
| 33 | + category = name if name.start_with?('Qt ') && !alwaysInQt.include?(name) | |
| 34 | + end | |
| 35 | + | |
| 36 | + qtPlatformsTypes = ['Qt Platform Headers', 'Qt Android Extras', 'Qt Mac Extras', 'Qt Windows Extras', 'Qt X11 Extras'] | |
| 37 | + return 'Qt Platforms' if qtPlatformsTypes.include?(category) | |
| 38 | + | |
| 39 | + category.remove!(' Manual') | |
| 40 | + category | |
| 41 | + end | |
| 42 | + | |
| 43 | + def include_default_entry? | |
| 44 | + name != 'All Classes' && name != 'All QML Types' | |
| 45 | + end | |
| 46 | + | |
| 47 | + def additional_entries | |
| 48 | + entries = [] | |
| 49 | + titles = [] | |
| 50 | + | |
| 51 | + className = at_css('h1.title').content.strip.remove(' Class') | |
| 52 | + displayedClassName = className | |
| 53 | + alternativeClassName = at_css('h1.title + .small-subtitle a') | |
| 54 | + displayedClassName = alternativeClassName.content if alternativeClassName | |
| 55 | + | |
| 56 | + # Functions signatures | |
| 57 | + css('h3.fn').each do |node| | |
| 58 | + header = node.clone | |
| 59 | + | |
| 60 | + # Skip typenames | |
| 61 | + next if header.content.strip.start_with?('typename ') | |
| 62 | + | |
| 63 | + # Remove leading <a name=""> | |
| 64 | + header.children.css('a[name]').remove | |
| 65 | + | |
| 66 | + # Remove leading <code> tag (virtual/static/… attributes) | |
| 67 | + code = header.children.first | |
| 68 | + code.remove if code.name == 'code' | |
| 69 | + | |
| 70 | + # Remove leading ‘const’ | |
| 71 | + header.children.first.remove if header.content.strip.start_with?('const ') | |
| 72 | + | |
| 73 | + # Remove return type | |
| 74 | + returnType = header.children.first | |
| 75 | + returnType.remove if returnType['class'] == 'type' | |
| 76 | + | |
| 77 | + title = header.content.strip | |
| 78 | + | |
| 79 | + # Remove leading '&'/'*' | |
| 80 | + title[0] = '' if title[0] == '&' || title[0] == '*' | |
| 81 | + | |
| 82 | + # Ignore operator overloads | |
| 83 | + next if title.start_with?('operator') | |
| 84 | + | |
| 85 | + # Remove function parameters | |
| 86 | + title.sub! %r{\(.*\)}, '()' | |
| 87 | + | |
| 88 | + # Remove template generics | |
| 89 | + title.remove!(%r{^<.*> }) | |
| 90 | + | |
| 91 | + # Remove ‘const’ at the end | |
| 92 | + title.remove!(%r{ const$}) | |
| 93 | + | |
| 94 | + # Enum/typedef formatting | |
| 95 | + title.sub! %r{(enum|typedef) (.*)}, '\2 (\1)' | |
| 96 | + | |
| 97 | + # Remove property type | |
| 98 | + title = "#{displayedClassName}::#{title}" if title.sub!(%r{ : .*$}, '') | |
| 99 | + | |
| 100 | + # Replace the class name by the alternative class name if available | |
| 101 | + title = title.sub(className, displayedClassName) if alternativeClassName | |
| 102 | + | |
| 103 | + unless titles.include?(title) # Remove duplicates (function overloading) | |
| 104 | + entries << [title, header['id']] | |
| 105 | + titles.push title | |
| 106 | + end | |
| 107 | + end | |
| 108 | + | |
| 109 | + # QML properties/functions | |
| 110 | + qmlTypeName = at_css('h1.title').content.remove(' QML Type', '') | |
| 111 | + css('.qmlproto').each do |node| | |
| 112 | + title = node.content.strip | |
| 113 | + id = node.at_css('tr')['id'] | |
| 114 | + id = node.at_css('a')['name'] if id.blank? | |
| 115 | + | |
| 116 | + # Remove options | |
| 117 | + title.remove!(%r{^\[.*\] }) | |
| 118 | + | |
| 119 | + # Remove function parameters | |
| 120 | + title.sub! %r{\(.*\)}, '()' | |
| 121 | + | |
| 122 | + # Remove property type | |
| 123 | + title.remove!(%r{ : .*$}) | |
| 124 | + | |
| 125 | + # Remove return type | |
| 126 | + title.remove!(%r{.* }) | |
| 127 | + | |
| 128 | + # Remove return type | |
| 129 | + title.remove!(%r{.* }) | |
| 130 | + | |
| 131 | + title = "#{qmlTypeName}.#{title.strip}" | |
| 132 | + unless titles.include?(title) # Remove duplicates (function overloading) | |
| 133 | + entries << [title, id] | |
| 134 | + titles.push(title) | |
| 135 | + end | |
| 136 | + end | |
| 137 | + | |
| 138 | + entries | |
| 139 | + end | |
| 140 | + end | |
| 141 | + end | |
| 142 | +end | |
| @@ -11,7 +11,7 @@ module Docs · 10 unmodified lines | ||
| 11 | 11 | css('.card').each do |card| |
| 12 | 12 | title = card.at_css('h2') |
| 13 | 13 | added_in = card.at_css('small') |
| 14 | − added_in.parent = title | |
| 14 | + added_in.parent = title if added_in.present? | |
| 15 | 15 | end |
| 16 | 16 | |
| 17 | 17 | css('.params').each do |node| |
| @@ -37,8 +37,10 @@ module Docs · 36 unmodified lines | ||
| 37 | 37 | end |
| 38 | 38 | |
| 39 | 39 | # Add class to differentiate Ruby code from C code |
| 40 | − css('.method-source-code > pre').each do |node| | |
| 41 | − node['class'] = node.at_css('.ruby-keyword') ? 'ruby' : 'c' | |
| 40 | + css('.method-source-code').each do |node| | |
| 41 | + node.parent.prepend_child(node) | |
| 42 | + pre = node.at_css('pre') | |
| 43 | + pre['class'] = pre.at_css('.ruby-keyword') ? 'ruby' : 'c' | |
| 42 | 44 | end |
| 43 | 45 | |
| 44 | 46 | # Remove code highlighting |
| @@ -6,8 +6,11 @@ module Docs · 5 unmodified lines | ||
| 6 | 6 | end |
| 7 | 7 | |
| 8 | 8 | def get_type |
| 9 | − link = at_css("nav a[href='#{result[:path].split('/').last}']") | |
| 10 | − link.ancestors('ul').last.previous_element.content | |
| 9 | + link = css("nav a[href='#{result[:path].split('/').last}']").last | |
| 10 | + return 'Miscellaneous' unless link | |
| 11 | + type = link.ancestors('ul').last.previous_element.content | |
| 12 | + type.remove! %r{\s*\(.*\)} | |
| 13 | + type | |
| 11 | 14 | end |
| 12 | 15 | |
| 13 | 16 | def additional_entries |
| @@ -24,6 +27,8 @@ module Docs · 10 unmodified lines | ||
| 24 | 27 | 'Reference: Component' |
| 25 | 28 | elsif slug == 'react-api' |
| 26 | 29 | 'Reference: React' |
| 30 | + elsif slug == 'hooks-reference' | |
| 31 | + 'Hooks' | |
| 27 | 32 | else |
| 28 | 33 | 'Reference' |
| 29 | 34 | end |
| @@ -2,7 +2,7 @@ module Docs · 1 unmodified line | ||
| 2 | 2 | class ReactNative |
| 3 | 3 | class CleanHtmlFilter < Filter |
| 4 | 4 | def call |
| 5 | − @doc = at_css('.inner-content, article.withtoc') | |
| 5 | + @doc = at_css('.post') | |
| 6 | 6 | |
| 7 | 7 | if root_page? |
| 8 | 8 | at_css('h1').content = 'React Native Documentation' |
| @@ -38,6 +38,11 @@ module Docs · 29 unmodified lines | ||
| 38 | 38 | node.content = node.content |
| 39 | 39 | end |
| 40 | 40 | |
| 41 | + css('pre > code.hljs').each do |node| | |
| 42 | + node.parent['data-language'] = 'jsx' | |
| 43 | + node.before(node.children).remove | |
| 44 | + end | |
| 45 | + | |
| 41 | 46 | css('blockquote > p:first-child').each do |node| |
| 42 | 47 | node.remove if node.content.strip == 'Note:' |
| 43 | 48 | end |
| @@ -45,7 +50,7 @@ module Docs · 1 unmodified line | ||
| 45 | 50 | css('h3#props', 'h3#methods').each { |node| node.name = 'h2' } |
| 46 | 51 | css('h4.propTitle').each { |node| node.name = 'h3' } |
| 47 | 52 | |
| 48 | − css('> div > div', '> div', 'div > span', '.props', '.prop').each do |node| | |
| 53 | + css('> div > div', '> div', 'div > span', '.props', '.prop', '> article', '.postHeader', '.web-player').each do |node| | |
| 49 | 54 | node.before(node.children).remove |
| 50 | 55 | end |
| 51 | 56 | |
| @@ -12,9 +12,9 @@ module Docs · 11 unmodified lines | ||
| 12 | 12 | end |
| 13 | 13 | |
| 14 | 14 | def get_type |
| 15 | − link = at_css('.nav-docs-section .active, .toc .active') | |
| 15 | + link = at_css('.navListItemActive') | |
| 16 | 16 | return 'Miscellaneous' unless link |
| 17 | − section = link.ancestors('.nav-docs-section, section').first | |
| 17 | + section = link.ancestors('.navGroup').first | |
| 18 | 18 | type = section.at_css('h3').content.strip |
| 19 | 19 | type = REPLACE_TYPES[type] || type |
| 20 | 20 | type += ": #{name}" if type == 'Components' |
| @@ -22,15 +22,13 @@ module Docs · 1 unmodified line | ||
| 22 | 22 | end |
| 23 | 23 | |
| 24 | 24 | def additional_entries |
| 25 | − css('.props > .prop > .propTitle', '.props > .prop > .methodTitle').each_with_object [] do |node, entries| | |
| 26 | − name = node.children.find(&:text?).try(:content) | |
| 27 | − next if name.blank? | |
| 28 | − sep = node.content.include?('static') ? '.' : '#' | |
| 29 | − name.prepend(self.name + sep) | |
| 30 | − name << '()' if node['class'].include?('methodTitle') | |
| 31 | − name.remove! %r{\??\:\s*\z} | |
| 32 | − id = node.at_css('.anchor')['name'] | |
| 33 | − entries << [name, id] | |
| 25 | + css('.mainContainer h3').each_with_object [] do |node, entries| | |
| 26 | + subname = node.text | |
| 27 | + next if subname.blank? || node.css('code').empty? | |
| 28 | + sep = subname.include?('()') ? '.' : '#' | |
| 29 | + subname.prepend(name + sep) | |
| 30 | + id = node.at_css('.anchor')['id'] | |
| 31 | + entries << [subname, id] | |
| 34 | 32 | end |
| 35 | 33 | end |
| 36 | 34 | end |
| @@ -12,7 +12,7 @@ module Docs · 11 unmodified lines | ||
| 12 | 12 | when 'HDEL' then 'Hashes' |
| 13 | 13 | when 'BLPOP' then 'Lists' |
| 14 | 14 | when 'SADD' then 'Sets' |
| 15 | − when 'ZADD' then 'Sorted Sets' | |
| 15 | + when 'BZPOPMAX' then 'Sorted Sets' | |
| 16 | 16 | when 'PSUBSCRIBE' then 'Pub/Sub' |
| 17 | 17 | when 'DISCARD' then 'Transactions' |
| 18 | 18 | when 'EVAL' then 'Scripting' |
| @@ -21,6 +21,7 @@ module Docs · 2 unmodified lines | ||
| 21 | 21 | when 'PFADD' then 'HyperLogLog' |
| 22 | 22 | when 'CLUSTER ADDSLOTS' then 'Cluster' |
| 23 | 23 | when 'GEOADD' then 'Geo' |
| 24 | + when 'XACK' then 'Stream' | |
| 24 | 25 | else 'Miscellaneous' |
| 25 | 26 | end |
| 26 | 27 | end |
| @@ -1,11 +1,11 @@ | ||
| 1 | +# frozen_string_literal: true | |
| 2 | + | |
| 1 | 3 | module Docs |
| 2 | 4 | class Rust |
| 3 | 5 | class CleanHtmlFilter < Filter |
| 4 | 6 | def call |
| 5 | − puts subpath if at_css('#versioninfo') | |
| 6 | − | |
| 7 | 7 | if slug.start_with?('book') || slug.start_with?('reference') |
| 8 | − @doc = at_css('#content') | |
| 8 | + @doc = at_css('#content main') | |
| 9 | 9 | elsif slug == 'error-index' |
| 10 | 10 | css('.error-undescribed').remove |
| 11 | 11 | |
| @@ -29,12 +29,16 @@ module Docs · 17 unmodified lines | ||
| 29 | 29 | |
| 30 | 30 | css('.rusttest', '.test-arrow', 'hr').remove |
| 31 | 31 | |
| 32 | + css('.docblock.attributes').each do |node| | |
| 33 | + node.remove if node.content.include?('#[must_use]') | |
| 34 | + end | |
| 35 | + | |
| 32 | 36 | css('a.header').each do |node| |
| 33 | − node.first_element_child['id'] = node['name'] | |
| 37 | + node.first_element_child['id'] = node['name'] || node['id'] | |
| 34 | 38 | node.before(node.children).remove |
| 35 | 39 | end |
| 36 | 40 | |
| 37 | − css('.docblock > h1').each { |node| node.name = 'h4' } | |
| 41 | + css('.docblock > h1:not(.section-header)').each { |node| node.name = 'h4' } | |
| 38 | 42 | css('h2.section-header').each { |node| node.name = 'h3' } |
| 39 | 43 | css('h1.section-header').each { |node| node.name = 'h2' } |
| 40 | 44 | |
| @@ -44,7 +48,7 @@ module Docs · 3 unmodified lines | ||
| 44 | 48 | end |
| 45 | 49 | end |
| 46 | 50 | |
| 47 | − css('> .impl-items', '> .docblock', 'pre > pre').each do |node| | |
| 51 | + css('> .impl-items', '> .docblock', 'pre > pre', '.tooltiptext', '.tooltip').each do |node| | |
| 48 | 52 | node.before(node.children).remove |
| 49 | 53 | end |
| 50 | 54 | |
| @@ -62,6 +66,35 @@ module Docs · 11 unmodified lines | ||
| 62 | 66 | node['data-language'] = 'rust' if node['class'] && node['class'].include?('rust') |
| 63 | 67 | end |
| 64 | 68 | |
| 69 | + doc.first_element_child.name = 'h1' if doc.first_element_child.name = 'h2' | |
| 70 | + at_css('h1').content = 'Rust Documentation' if root_page? | |
| 71 | + | |
| 72 | + css('.table-display').each do |node| | |
| 73 | + node.css('td').each do |td| | |
| 74 | + node.before(td.children) | |
| 75 | + end | |
| 76 | + node.remove | |
| 77 | + end | |
| 78 | + | |
| 79 | + css('.important-traits').to_a.each_with_index do |node, index| | |
| 80 | + content = node.at_css('.content.hidden .content') | |
| 81 | + node.at_css('.content.hidden').replace(content) if content | |
| 82 | + node.parent.after(node) if node.parent.name.in?(%(h2 h3 h4)) | |
| 83 | + end | |
| 84 | + | |
| 85 | + css('code.content').each do |node| | |
| 86 | + node.name = 'pre' | |
| 87 | + node.css('.fmt-newline').each do |line| | |
| 88 | + line.inner_html = line.inner_html + "\n" | |
| 89 | + end | |
| 90 | + node.inner_html = node.inner_html.gsub('<br>', "\n") | |
| 91 | + node.content = node.content | |
| 92 | + end | |
| 93 | + | |
| 94 | + css('.since + .srclink').each do |node| | |
| 95 | + node.previous_element.before(node) | |
| 96 | + end | |
| 97 | + | |
| 65 | 98 | doc |
| 66 | 99 | end |
| 67 | 100 | end |
| @@ -42,11 +42,13 @@ module Docs · 41 unmodified lines | ||
| 42 | 42 | entries << [node.content, node['id']] unless node.content.include?('Note:') |
| 43 | 43 | end |
| 44 | 44 | else |
| 45 | − css('#methods + * + div > .method', '#required-methods + div > .method', '#provided-methods + div > .method').map do |node| | |
| 46 | − name = node.at_css('.fnname').content | |
| 47 | − name.prepend "#{self.name}::" | |
| 48 | − [name, node['id']] | |
| 49 | − end | |
| 45 | + css('.method') | |
| 46 | + .each_with_object({}) { |node, entries| | |
| 47 | + name = node.at_css('.fnname').try(:content) | |
| 48 | + next unless name | |
| 49 | + name.prepend "#{self.name}::" | |
| 50 | + entries[name] ||= [name, node['id']] | |
| 51 | + }.values | |
| 50 | 52 | end |
| 51 | 53 | end |
| 52 | 54 | end |
| @@ -27,6 +27,7 @@ module Docs · 26 unmodified lines | ||
| 27 | 27 | type = at_css('dt > .descclassname').content.strip |
| 28 | 28 | type.remove! 'sklearn.' |
| 29 | 29 | type.remove! %r{\.\z} |
| 30 | + type = 'sklearn' if type.blank? | |
| 30 | 31 | type |
| 31 | 32 | elsif subpath.start_with?('tutorial') |
| 32 | 33 | 'Tutorials' |
| @@ -34,7 +34,8 @@ module Docs · 33 unmodified lines | ||
| 34 | 34 | 'rtree' => 'R*Tree Module', |
| 35 | 35 | 'rbu' => 'RBU Extension', |
| 36 | 36 | 'limits' => 'Limits', |
| 37 | − 'howtocorrupt' => 'How To Corrupt' | |
| 37 | + 'howtocorrupt' => 'How To Corrupt', | |
| 38 | + 'geopoly' => 'Geopoly' | |
| 38 | 39 | } |
| 39 | 40 | |
| 40 | 41 | def get_type |
| @@ -3,8 +3,8 @@ module Docs · 2 unmodified lines | ||
| 3 | 3 | class EntriesFilter < Docs::EntriesFilter |
| 4 | 4 | def get_name |
| 5 | 5 | name = super |
| 6 | − name.gsub!('Element.', '').try(:downcase!) | |
| 7 | − name.gsub!('Attribute.', '').try(:downcase!) | |
| 6 | + name.gsub!('Element.', '') | |
| 7 | + name.gsub!('Attribute.', '') | |
| 8 | 8 | name.gsub!('Tutorial.', '') |
| 9 | 9 | name.gsub!('_', '') |
| 10 | 10 | |
| @@ -0,0 +1,33 @@ | ||
| 1 | +module Docs | |
| 2 | + class Terraform | |
| 3 | + class CleanHtmlFilter < Filter | |
| 4 | + def call | |
| 5 | + @doc = at_css('#inner') | |
| 6 | + | |
| 7 | + css('hr', 'a.anchor').remove | |
| 8 | + | |
| 9 | + css('.alert').each do |node| | |
| 10 | + node.name = 'blockquote' | |
| 11 | + end | |
| 12 | + | |
| 13 | + css('pre').each do |node| | |
| 14 | + if language = node['class'][/(json|shell|ruby)/, 1] | |
| 15 | + node['data-language'] = language | |
| 16 | + end | |
| 17 | + # HCL isn't currently supported by Prism, Ruby syntax does an acceptable job for now | |
| 18 | + if language = node['class'][/(hcl)/, 1] | |
| 19 | + node['data-language'] = 'ruby' | |
| 20 | + end | |
| 21 | + node.content = node.content | |
| 22 | + node.remove_attribute('class') | |
| 23 | + end | |
| 24 | + | |
| 25 | + css('.highlight').each do |node| | |
| 26 | + node.before(node.children).remove | |
| 27 | + end | |
| 28 | + | |
| 29 | + doc | |
| 30 | + end | |
| 31 | + end | |
| 32 | + end | |
| 33 | +end | |
| @@ -0,0 +1,76 @@ | ||
| 1 | +module Docs | |
| 2 | + class Terraform | |
| 3 | + class EntriesFilter < Docs::EntriesFilter | |
| 4 | + | |
| 5 | + # Some providers have non-trivial mappings between the directory they live in and their name | |
| 6 | + # Anything *not* in this list will be capitalized instead. | |
| 7 | + PROVIDER_NAME_MAP = { | |
| 8 | + 'aws' => 'AWS', | |
| 9 | + 'azure' => 'Azure (Legacy)', | |
| 10 | + 'azurerm' => 'Azure', | |
| 11 | + 'centurylinkcloud' => 'CenturyLinkCloud', | |
| 12 | + 'cloudscale' => 'CloudScale.ch', | |
| 13 | + 'cloudstack' => 'CloudStack', | |
| 14 | + 'dme' => 'DNSMadeEasy', | |
| 15 | + 'dns' => 'DNS', | |
| 16 | + 'dnsimple' => 'DNSimple', | |
| 17 | + 'do' => 'DigitalOcean', | |
| 18 | + 'github' => 'GitHub', | |
| 19 | + 'google' => 'Google Cloud', | |
| 20 | + 'http' => 'HTTP', | |
| 21 | + 'mysql' => 'MySQL', | |
| 22 | + 'newrelic' => 'New Relic', | |
| 23 | + 'oneandone' => '1&1', | |
| 24 | + 'opentelekomcloud' => 'OpenTelekomCloud', | |
| 25 | + 'opsgenie' => 'OpsGenie', | |
| 26 | + 'opc' => 'Oracle Public Cloud', | |
| 27 | + 'oraclepaas' => 'Oracle Cloud Platform', | |
| 28 | + 'ovh' => 'OVH', | |
| 29 | + 'pagerduty' => 'PagerDuty', | |
| 30 | + 'panos' => 'Palo Alto Networks', | |
| 31 | + 'postgresql' => 'PostgreSQL', | |
| 32 | + 'powerdns' => 'PowerDNS', | |
| 33 | + 'profitbricks' => 'ProfitBricks', | |
| 34 | + 'rabbitmq' => 'RabbitMQ', | |
| 35 | + 'softlayer' => 'SoftLayer', | |
| 36 | + 'statuscake' => 'StatusCake', | |
| 37 | + 'tls' => 'TLS', | |
| 38 | + 'ultradns' => 'UltraDNS', | |
| 39 | + 'vcd' => 'VMware vCloud Director', | |
| 40 | + 'nsxt' => 'VMware NSX-T', | |
| 41 | + 'vsphere' => 'VMware vSphere', | |
| 42 | + } | |
| 43 | + | |
| 44 | + # Some providers have a lot (> 100) entries, which makes browsing them unwieldy. | |
| 45 | + # Any present in the list below will have an extra set of types added, breaking the pages out into the different | |
| 46 | + # products they offer. | |
| 47 | + LARGE_PROVIDERS = { | |
| 48 | + "aws" => true, | |
| 49 | + "azurerm" => true, | |
| 50 | + "google" => true, | |
| 51 | + } | |
| 52 | + | |
| 53 | + | |
| 54 | + def get_name | |
| 55 | + name ||= at_css('#inner h1').content | |
| 56 | + name.remove! "» " | |
| 57 | + name.remove! "Data Source: " | |
| 58 | + name | |
| 59 | + end | |
| 60 | + | |
| 61 | + def get_type | |
| 62 | + category, subcategory, subfolder, page = *slug.split('/') | |
| 63 | + provider = page ? subcategory : category | |
| 64 | + nice_provider = PROVIDER_NAME_MAP[provider] || provider.capitalize | |
| 65 | + | |
| 66 | + if LARGE_PROVIDERS[provider] | |
| 67 | + category_node = at_css('ul > li > ul > li.active') | |
| 68 | + parent_node = category_node.parent.previous_element if category_node | |
| 69 | + nice_provider = nice_provider + ": #{parent_node.content}" if category_node | |
| 70 | + end | |
| 71 | + | |
| 72 | + nice_provider | |
| 73 | + end | |
| 74 | + end | |
| 75 | + end | |
| 76 | +end | |
| @@ -9,6 +9,7 @@ module Docs · 8 unmodified lines | ||
| 9 | 9 | |
| 10 | 10 | def get_type |
| 11 | 11 | return 'Declaration Files' if subpath.include?('declaration-files') |
| 12 | + return 'Project Configuration' if slug == 'handbook/configuring-watch' | |
| 12 | 13 | type = at_css('#main-nav a.active').ancestors('.panel').first.at_css('> a').content |
| 13 | 14 | type = name if type == 'Handbook' |
| 14 | 15 | type |
| @@ -29,6 +29,7 @@ module Docs · 28 unmodified lines | ||
| 29 | 29 | if node.name == 'h2' |
| 30 | 30 | h2 = node['id'] |
| 31 | 31 | elsif h2 == 'available-settings' && (code = node.at_css('code')) && (name = code.content) && name.start_with?('config.') |
| 32 | + name.sub! %r{\s+=.*}, '=' | |
| 32 | 33 | id = code.parent['id'] = name.parameterize |
| 33 | 34 | entries << [name, id, 'Config'] |
| 34 | 35 | end |
| @@ -9,10 +9,6 @@ module Docs · 8 unmodified lines | ||
| 9 | 9 | |
| 10 | 10 | css('.demo', '.guide-links', '.footer', '#ad').remove |
| 11 | 11 | |
| 12 | − css('.__cf_email__').each do |node| | |
| 13 | − node.replace(decode_cloudflare_email(node['data-cfemail'])) | |
| 14 | − end | |
| 15 | − | |
| 16 | 12 | # Remove code highlighting |
| 17 | 13 | css('figure').each do |node| |
| 18 | 14 | node.name = 'pre' |
| @@ -26,9 +26,9 @@ module Docs · 25 unmodified lines | ||
| 26 | 26 | next if node.previous.try(:content).present? |
| 27 | 27 | entries << [node.content, node.parent['id']] |
| 28 | 28 | end |
| 29 | − elsif slug.start_with?('api') && slug != 'api/plugins/parser' | |
| 29 | + elsif slug.start_with?('api') && slug != 'api/parser' | |
| 30 | 30 | css('.header[id] code').each_with_object [] do |node, entries| |
| 31 | − next if node.previous.try(:content).present? | |
| 31 | + next if node.previous.try(:content).present? || node.next.try(:content).present? | |
| 32 | 32 | name = node.content.sub(/\(.*\)/, '()') |
| 33 | 33 | name.prepend "#{self.name.split(':').first}: " |
| 34 | 34 | entries << [name, node.parent['id']] |
| @@ -43,10 +43,6 @@ module Docs · 42 unmodified lines | ||
| 43 | 43 | end |
| 44 | 44 | end |
| 45 | 45 | |
| 46 | − css('.__cf_email__').each do |node| | |
| 47 | − node.replace(decode_cloudflare_email(node['data-cfemail'])) | |
| 48 | − end | |
| 49 | − | |
| 50 | 46 | css('div.highlighter-rouge').each do |node| |
| 51 | 47 | node['data-language'] = node['class'][/language-(\w+)/, 1] if node['class'] |
| 52 | 48 | node.content = node.content.strip |
| @@ -11,33 +11,17 @@ module Docs · 10 unmodified lines | ||
| 11 | 11 | options[:max_image_size] = 256_000 |
| 12 | 12 | |
| 13 | 13 | options[:attribution] = <<-HTML |
| 14 | − © 2010–2017 Google, Inc.<br> | |
| 14 | + © 2010–2018 Google, Inc.<br> | |
| 15 | 15 | Licensed under the Creative Commons Attribution License 4.0. |
| 16 | 16 | HTML |
| 17 | 17 | |
| 18 | − version do | |
| 19 | − self.release = '4.4.4' | |
| 20 | − self.base_url = 'https://angular.io/' | |
| 21 | − self.root_path = 'docs' | |
| 22 | − | |
| 23 | − html_filters.push 'angular/clean_html', 'angular/entries' | |
| 24 | − | |
| 25 | − options[:follow_links] = false | |
| 26 | − options[:only_patterns] = [/\Aguide/, /\Atutorial/, /\Aapi/] | |
| 27 | − options[:fix_urls_before_parse] = ->(url) do | |
| 28 | − url.sub! %r{\Aguide/}, '/guide/' | |
| 29 | − url.sub! %r{\Atutorial/}, '/tutorial/' | |
| 30 | − url.sub! %r{\Aapi/}, '/api/' | |
| 31 | − url.sub! %r{\Agenerated/}, '/generated/' | |
| 32 | − url | |
| 33 | − end | |
| 34 | − | |
| 18 | + module Common | |
| 35 | 19 | private |
| 36 | 20 | |
| 37 | 21 | def initial_urls |
| 38 | 22 | initial_urls = [] |
| 39 | 23 | |
| 40 | − Request.run 'https://angular.io/generated/navigation.json' do |response| | |
| 24 | + Request.run "#{self.class.base_url}generated/navigation.json" do |response| | |
| 41 | 25 | data = JSON.parse(response.body) |
| 42 | 26 | dig = ->(entry) do |
| 43 | 27 | initial_urls << url_for("generated/docs/#{entry['url']}.json") if entry['url'] && entry['url'] != 'api' |
| @@ -46,7 +30,7 @@ module Docs · 2 unmodified lines | ||
| 46 | 30 | data['SideNav'].each(&dig) |
| 47 | 31 | end |
| 48 | 32 | |
| 49 | − Request.run 'https://angular.io/generated/docs/api/api-list.json' do |response| | |
| 33 | + Request.run "#{self.class.base_url}generated/docs/api/api-list.json" do |response| | |
| 50 | 34 | data = JSON.parse(response.body) |
| 51 | 35 | dig = ->(entry) do |
| 52 | 36 | initial_urls << url_for("generated/docs/#{entry['path']}.json") if entry['path'] |
| @@ -61,7 +45,11 @@ module Docs · 8 unmodified lines | ||
| 61 | 45 | |
| 62 | 46 | def handle_response(response) |
| 63 | 47 | if response.mime_type.include?('json') |
| 64 | − response.options[:response_body] = JSON.parse(response.body)['contents'] | |
| 48 | + begin | |
| 49 | + response.options[:response_body] = JSON.parse(response.body)['contents'] | |
| 50 | + rescue JSON::ParserError | |
| 51 | + response.options[:response_body] = '' | |
| 52 | + end | |
| 65 | 53 | response.headers['Content-Type'] = 'text/html' |
| 66 | 54 | response.url.path = response.url.path.sub('/generated/docs/', '/').remove('.json') |
| 67 | 55 | response.effective_url.path = response.effective_url.path.sub('/generated/docs/', '/').remove('.json') |
| @@ -70,6 +58,66 @@ module Docs · 2 unmodified lines | ||
| 70 | 58 | end |
| 71 | 59 | end |
| 72 | 60 | |
| 61 | + version do | |
| 62 | + self.release = '6.0.4' | |
| 63 | + self.base_url = 'https://angular.io/' | |
| 64 | + self.root_path = 'docs' | |
| 65 | + | |
| 66 | + html_filters.push 'angular/clean_html', 'angular/entries' | |
| 67 | + | |
| 68 | + options[:follow_links] = false | |
| 69 | + options[:only_patterns] = [/\Aguide/, /\Atutorial/, /\Aapi/] | |
| 70 | + options[:fix_urls_before_parse] = ->(url) do | |
| 71 | + url.sub! %r{\Aguide/}, '/guide/' | |
| 72 | + url.sub! %r{\Atutorial/}, '/tutorial/' | |
| 73 | + url.sub! %r{\Aapi/}, '/api/' | |
| 74 | + url.sub! %r{\Agenerated/}, '/generated/' | |
| 75 | + url | |
| 76 | + end | |
| 77 | + | |
| 78 | + include Docs::Angular::Common | |
| 79 | + end | |
| 80 | + | |
| 81 | + version '5' do | |
| 82 | + self.release = '5.2.11' | |
| 83 | + self.base_url = 'https://v5.angular.io/' | |
| 84 | + self.root_path = 'docs' | |
| 85 | + | |
| 86 | + html_filters.push 'angular/clean_html', 'angular/entries' | |
| 87 | + | |
| 88 | + options[:follow_links] = false | |
| 89 | + options[:only_patterns] = [/\Aguide/, /\Atutorial/, /\Aapi/] | |
| 90 | + options[:fix_urls_before_parse] = ->(url) do | |
| 91 | + url.sub! %r{\Aguide/}, '/guide/' | |
| 92 | + url.sub! %r{\Atutorial/}, '/tutorial/' | |
| 93 | + url.sub! %r{\Aapi/}, '/api/' | |
| 94 | + url.sub! %r{\Agenerated/}, '/generated/' | |
| 95 | + url | |
| 96 | + end | |
| 97 | + | |
| 98 | + include Docs::Angular::Common | |
| 99 | + end | |
| 100 | + | |
| 101 | + version '4' do | |
| 102 | + self.release = '4.4.6' | |
| 103 | + self.base_url = 'https://v4.angular.io/' | |
| 104 | + self.root_path = 'docs' | |
| 105 | + | |
| 106 | + html_filters.push 'angular/clean_html', 'angular/entries' | |
| 107 | + | |
| 108 | + options[:follow_links] = false | |
| 109 | + options[:only_patterns] = [/\Aguide/, /\Atutorial/, /\Aapi/] | |
| 110 | + options[:fix_urls_before_parse] = ->(url) do | |
| 111 | + url.sub! %r{\Aguide/}, '/guide/' | |
| 112 | + url.sub! %r{\Atutorial/}, '/tutorial/' | |
| 113 | + url.sub! %r{\Aapi/}, '/api/' | |
| 114 | + url.sub! %r{\Agenerated/}, '/generated/' | |
| 115 | + url | |
| 116 | + end | |
| 117 | + | |
| 118 | + include Docs::Angular::Common | |
| 119 | + end | |
| 120 | + | |
| 73 | 121 | version '2' do |
| 74 | 122 | self.release = '2.4.10' |
| 75 | 123 | self.base_url = 'https://v2.angular.io/docs/ts/latest/' |
| @@ -107,6 +155,10 @@ module Docs · 31 unmodified lines | ||
| 107 | 155 | end |
| 108 | 156 | end |
| 109 | 157 | |
| 158 | + def get_latest_version(opts) | |
| 159 | + get_npm_version('@angular/core', opts) | |
| 160 | + end | |
| 161 | + | |
| 110 | 162 | private |
| 111 | 163 | |
| 112 | 164 | def parse(response) |
| @@ -31,10 +31,14 @@ module Docs · 30 unmodified lines | ||
| 31 | 31 | end |
| 32 | 32 | |
| 33 | 33 | options[:only_patterns] = [%r{\Aapi}, %r{\Aguide}] |
| 34 | − options[:skip] = %w(api/ng.html) | |
| 34 | + options[:skip] = %w(api/ng.html guide/tutorial/step_14.html guide/api.html guide/tutorial/.html) | |
| 35 | + options[:skip_patterns] = [ | |
| 36 | + /error\/\$compile/, | |
| 37 | + /misc/, | |
| 38 | + ] | |
| 35 | 39 | |
| 36 | 40 | options[:attribution] = <<-HTML |
| 37 | − © 2010–2017 Google, Inc.<br> | |
| 41 | + © 2010–2018 Google, Inc.<br> | |
| 38 | 42 | Licensed under the Creative Commons Attribution License 4.0. |
| 39 | 43 | HTML |
| 40 | 44 | |
| @@ -45,8 +49,13 @@ module Docs · 4 unmodified lines | ||
| 45 | 49 | capybara.execute_script("return document.querySelector('.side-navigation').innerHTML") |
| 46 | 50 | end |
| 47 | 51 | |
| 52 | + version '1.7' do | |
| 53 | + self.release = '1.7.8' | |
| 54 | + self.base_url = "https://code.angularjs.org/#{release}/docs/partials/" | |
| 55 | + end | |
| 56 | + | |
| 48 | 57 | version '1.6' do |
| 49 | − self.release = '1.6.5' | |
| 58 | + self.release = '1.6.9' | |
| 50 | 59 | self.base_url = "https://code.angularjs.org/#{release}/docs/partials/" |
| 51 | 60 | end |
| 52 | 61 | |
| @@ -69,5 +78,9 @@ module Docs · 16 unmodified lines | ||
| 69 | 78 | self.release = '1.2.32' |
| 70 | 79 | self.base_url = "https://code.angularjs.org/#{release}/docs/partials/" |
| 71 | 80 | end |
| 81 | + | |
| 82 | + def get_latest_version(opts) | |
| 83 | + get_npm_version('angular', opts) | |
| 84 | + end | |
| 72 | 85 | end |
| 73 | 86 | end |
| @@ -2,27 +2,71 @@ module Docs · 1 unmodified line | ||
| 2 | 2 | class Ansible < UrlScraper |
| 3 | 3 | self.name = 'Ansible' |
| 4 | 4 | self.type = 'sphinx' |
| 5 | − self.release = '2.4.0' | |
| 6 | − self.base_url = 'https://docs.ansible.com/ansible/latest/' | |
| 7 | 5 | self.links = { |
| 8 | 6 | home: 'https://www.ansible.com/', |
| 9 | 7 | code: 'https://github.com/ansible/ansible' |
| 10 | 8 | } |
| 11 | 9 | |
| 12 | − html_filters.push 'ansible/entries', 'ansible/clean_html', 'sphinx/clean_html' | |
| 13 | − | |
| 14 | − options[:skip] = %w( | |
| 15 | − glossary.html | |
| 16 | − faq.html | |
| 17 | − community.html | |
| 18 | − tower.html | |
| 19 | − quickstart.html | |
| 20 | − list_of_all_modules.html) | |
| 10 | + html_filters.push 'ansible/entries', 'sphinx/clean_html', 'ansible/clean_html' | |
| 21 | 11 | |
| 22 | 12 | options[:attribution] = <<-HTML |
| 23 | − © 2012–2017 Michael DeHaan<br> | |
| 24 | − © 2017 Red Hat, Inc.<br> | |
| 13 | + © 2012–2018 Michael DeHaan<br> | |
| 14 | + © 2018–2019 Red Hat, Inc.<br> | |
| 25 | 15 | Licensed under the GNU General Public License version 3. |
| 26 | 16 | HTML |
| 17 | + | |
| 18 | + options[:skip] = %w( | |
| 19 | + installation_guide/index.html | |
| 20 | + reference_appendices/glossary.html | |
| 21 | + reference_appendices/faq.html | |
| 22 | + reference_appendices/tower.html | |
| 23 | + user_guide/quickstart.html | |
| 24 | + modules/modules_by_category.html | |
| 25 | + modules/list_of_all_modules.html) | |
| 26 | + | |
| 27 | + options[:skip_patterns] = [ | |
| 28 | + /\Acommunity.*/i, | |
| 29 | + /\Adev_guide.*/i, | |
| 30 | + /\Aroadmap.*/i, | |
| 31 | + ] | |
| 32 | + | |
| 33 | + version '2.8' do | |
| 34 | + self.release = '2.8.3' | |
| 35 | + self.base_url = 'https://docs.ansible.com/ansible/2.7/' | |
| 36 | + end | |
| 37 | + | |
| 38 | + version '2.7' do | |
| 39 | + self.release = '2.7.12' | |
| 40 | + self.base_url = 'https://docs.ansible.com/ansible/2.7/' | |
| 41 | + end | |
| 42 | + | |
| 43 | + version '2.6' do | |
| 44 | + self.release = '2.6.18' | |
| 45 | + self.base_url = 'https://docs.ansible.com/ansible/2.6/' | |
| 46 | + end | |
| 47 | + | |
| 48 | + version '2.5' do | |
| 49 | + self.release = '2.5.15' | |
| 50 | + self.base_url = 'https://docs.ansible.com/ansible/2.5/' | |
| 51 | + end | |
| 52 | + | |
| 53 | + version '2.4' do | |
| 54 | + self.release = '2.4.6' | |
| 55 | + self.base_url = 'https://docs.ansible.com/ansible/2.4/' | |
| 56 | + | |
| 57 | + options[:skip] = %w( | |
| 58 | + glossary.html | |
| 59 | + faq.html | |
| 60 | + community.html | |
| 61 | + tower.html | |
| 62 | + quickstart.html | |
| 63 | + list_of_all_modules.html) | |
| 64 | + options[:skip_patterns] = [] | |
| 65 | + end | |
| 66 | + | |
| 67 | + def get_latest_version(opts) | |
| 68 | + doc = fetch_doc('https://docs.ansible.com/ansible/latest/index.html', opts) | |
| 69 | + doc.at_css('.DocSiteProduct-CurrentVersion').content.strip | |
| 70 | + end | |
| 27 | 71 | end |
| 28 | 72 | end |
| @@ -3,7 +3,7 @@ module Docs · 2 unmodified lines | ||
| 3 | 3 | self.name = 'Apache HTTP Server' |
| 4 | 4 | self.slug = 'apache_http_server' |
| 5 | 5 | self.type = 'apache' |
| 6 | − self.release = '2.4.27' | |
| 6 | + self.release = '2.4.37' | |
| 7 | 7 | self.base_url = 'https://httpd.apache.org/docs/2.4/en/' |
| 8 | 8 | self.links = { |
| 9 | 9 | home: 'https://httpd.apache.org/' |
| @@ -30,8 +30,13 @@ module Docs · 20 unmodified lines | ||
| 30 | 30 | /\Adeveloper\// ] |
| 31 | 31 | |
| 32 | 32 | options[:attribution] = <<-HTML |
| 33 | − © 2017 The Apache Software Foundation<br> | |
| 33 | + © 2018 The Apache Software Foundation<br> | |
| 34 | 34 | Licensed under the Apache License, Version 2.0. |
| 35 | 35 | HTML |
| 36 | + | |
| 37 | + def get_latest_version(opts) | |
| 38 | + doc = fetch_doc('http://httpd.apache.org/docs/', opts) | |
| 39 | + doc.at_css('#apcontents > ul a')['href'][0...-1] | |
| 40 | + end | |
| 36 | 41 | end |
| 37 | 42 | end |
| @@ -2,7 +2,7 @@ module Docs · 1 unmodified line | ||
| 2 | 2 | class ApachePig < UrlScraper |
| 3 | 3 | self.name = 'Apache Pig' |
| 4 | 4 | self.slug = 'apache_pig' |
| 5 | − self.type = 'apache_pig' | |
| 5 | + self.type = 'simple' | |
| 6 | 6 | self.links = { |
| 7 | 7 | home: 'https://pig.apache.org/' |
| 8 | 8 | } |
| @@ -43,5 +43,10 @@ module Docs · 34 unmodified lines | ||
| 43 | 43 | self.base_url = "https://pig.apache.org/docs/r#{release}/" |
| 44 | 44 | end |
| 45 | 45 | |
| 46 | + def get_latest_version(opts) | |
| 47 | + doc = fetch_doc('https://pig.apache.org/', opts) | |
| 48 | + item = doc.at_css('div[id="menu_1.2"] > .menuitem:last-child') | |
| 49 | + item.content.strip.sub(/Release /, '') | |
| 50 | + end | |
| 46 | 51 | end |
| 47 | 52 | end |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Async < UrlScraper |
| 3 | 3 | self.type = 'async' |
| 4 | − self.release = '2.5.0' | |
| 4 | + self.release = '2.6.1' | |
| 5 | 5 | self.base_url = 'https://caolan.github.io/async/' |
| 6 | 6 | self.root_path = 'docs.html' |
| 7 | 7 | self.links = { |
| @@ -14,8 +14,13 @@ module Docs · 6 unmodified lines | ||
| 14 | 14 | options[:skip_links] = true |
| 15 | 15 | |
| 16 | 16 | options[:attribution] = <<-HTML |
| 17 | − © 2010–2017 Caolan McMahon<br> | |
| 17 | + © 2010–2018 Caolan McMahon<br> | |
| 18 | 18 | Licensed under the MIT License. |
| 19 | 19 | HTML |
| 20 | + | |
| 21 | + def get_latest_version(opts) | |
| 22 | + doc = fetch_doc('https://caolan.github.io/async/v3/', opts) | |
| 23 | + doc.at_css('#version-dropdown > a').content.strip[1..-1] | |
| 24 | + end | |
| 20 | 25 | end |
| 21 | 26 | end |
| @@ -0,0 +1,31 @@ | ||
| 1 | +module Docs | |
| 2 | + class Babel < UrlScraper | |
| 3 | + self.type = 'simple' | |
| 4 | + self.base_url = 'http://babeljs.io/docs/' | |
| 5 | + self.release = '6.26.1' | |
| 6 | + self.initial_paths = %w(core-packages/) | |
| 7 | + self.links = { | |
| 8 | + home: 'https://babeljs.io/', | |
| 9 | + code: 'https://github.com/babel/babel' | |
| 10 | + } | |
| 11 | + | |
| 12 | + html_filters.push 'babel/clean_html', 'babel/entries' | |
| 13 | + | |
| 14 | + options[:trailing_slash] = true | |
| 15 | + options[:skip] = %w{setup/ editors/ community/videos/} | |
| 16 | + | |
| 17 | + options[:attribution] = <<-HTML | |
| 18 | + © 2018 Sebastian McKenzie<br> | |
| 19 | + Licensed under the MIT License. | |
| 20 | + HTML | |
| 21 | + | |
| 22 | + stub '' do | |
| 23 | + '<div></div>' | |
| 24 | + end | |
| 25 | + | |
| 26 | + def get_latest_version(opts) | |
| 27 | + doc = fetch_doc('https://babeljs.io/docs/en/', opts) | |
| 28 | + doc.at_css('a[href="/versions"] > h3').content | |
| 29 | + end | |
| 30 | + end | |
| 31 | +end | |
| @@ -20,5 +20,10 @@ module Docs · 19 unmodified lines | ||
| 20 | 20 | © 2010–2016 Jeremy Ashkenas, DocumentCloud<br> |
| 21 | 21 | Licensed under the MIT License. |
| 22 | 22 | HTML |
| 23 | + | |
| 24 | + def get_latest_version(opts) | |
| 25 | + doc = fetch_doc('https://backbonejs.org/', opts) | |
| 26 | + doc.at_css('.version').content[1...-1] | |
| 27 | + end | |
| 23 | 28 | end |
| 24 | 29 | end |
| @@ -0,0 +1,26 @@ | ||
| 1 | +module Docs | |
| 2 | + class Bash < UrlScraper | |
| 3 | + self.type = 'bash' | |
| 4 | + self.release = '5.0' | |
| 5 | + self.base_url = 'https://www.gnu.org/software/bash/manual' | |
| 6 | + self.root_path = '/html_node/index.html' | |
| 7 | + self.links = { | |
| 8 | + home: 'https://www.gnu.org/software/bash/', | |
| 9 | + code: 'http://git.savannah.gnu.org/cgit/bash.git' | |
| 10 | + } | |
| 11 | + | |
| 12 | + html_filters.push 'bash/entries', 'bash/clean_html' | |
| 13 | + | |
| 14 | + options[:only_patterns] = [/\/html_node\//] | |
| 15 | + | |
| 16 | + options[:attribution] = <<-HTML | |
| 17 | + Copyright © 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc.<br> | |
| 18 | + Licensed under the GNU Free Documentation License. | |
| 19 | + HTML | |
| 20 | + | |
| 21 | + def get_latest_version(opts) | |
| 22 | + body = fetch('https://www.gnu.org/software/bash/manual/html_node/index.html', opts) | |
| 23 | + body.scan(/, Version ([0-9.]+)/)[0][0][0...-1] | |
| 24 | + end | |
| 25 | + end | |
| 26 | +end | |
| @@ -0,0 +1,26 @@ | ||
| 1 | +module Docs | |
| 2 | + class Bluebird < UrlScraper | |
| 3 | + self.type = 'simple' | |
| 4 | + self.release = '3.5.1' | |
| 5 | + self.base_url = 'http://bluebirdjs.com/docs/' | |
| 6 | + self.root_path = 'api-reference.html' | |
| 7 | + self.force_gzip = true | |
| 8 | + self.links = { | |
| 9 | + home: 'http://bluebirdjs.com/', | |
| 10 | + code: 'https://github.com/petkaantonov/bluebird/' | |
| 11 | + } | |
| 12 | + | |
| 13 | + html_filters.push 'bluebird/clean_html', 'bluebird/entries' | |
| 14 | + | |
| 15 | + options[:skip] = %w(support.html download-api-reference.html contribute.html) | |
| 16 | + | |
| 17 | + options[:attribution] = <<-HTML | |
| 18 | + © 2013–2017 Petka Antonov<br> | |
| 19 | + Licensed under the MIT License. | |
| 20 | + HTML | |
| 21 | + | |
| 22 | + def get_latest_version(opts) | |
| 23 | + get_npm_version('bluebird', opts) | |
| 24 | + end | |
| 25 | + end | |
| 26 | +end | |
| @@ -8,16 +8,17 @@ module Docs · 7 unmodified lines | ||
| 8 | 8 | |
| 9 | 9 | options[:trailing_slash] = true |
| 10 | 10 | |
| 11 | + # https://github.com/twbs/bootstrap/blob/master/LICENSE | |
| 11 | 12 | options[:attribution] = <<-HTML |
| 12 | − © 2011–2017 Twitter, Inc.<br> | |
| 13 | − © 2011–2017 The Bootstrap Authors<br> | |
| 13 | + © 2011–2019 Twitter, Inc.<br> | |
| 14 | + © 2011–2019 The Bootstrap Authors<br> | |
| 14 | 15 | Code licensed under the MIT License.<br> |
| 15 | 16 | Documentation licensed under the Creative Commons Attribution License v3.0. |
| 16 | 17 | HTML |
| 17 | 18 | |
| 18 | 19 | version '4' do |
| 19 | − self.release = '4.0.0-beta' | |
| 20 | − self.base_url = 'https://getbootstrap.com/docs/4.0/' | |
| 20 | + self.release = '4.3.1' | |
| 21 | + self.base_url = 'https://getbootstrap.com/docs/4.3/' | |
| 21 | 22 | self.root_path = 'getting-started/introduction/' |
| 22 | 23 | |
| 23 | 24 | html_filters.push 'bootstrap/entries_v4', 'bootstrap/clean_html_v4' |
| @@ -34,5 +35,10 @@ module Docs · 10 unmodified lines | ||
| 34 | 35 | |
| 35 | 36 | options[:only] = %w(getting-started/ css/ components/ javascript/) |
| 36 | 37 | end |
| 38 | + | |
| 39 | + def get_latest_version(opts) | |
| 40 | + doc = fetch_doc('https://getbootstrap.com/', opts) | |
| 41 | + doc.at_css('#bd-versions').content.strip[1..-1] | |
| 42 | + end | |
| 37 | 43 | end |
| 38 | 44 | end |
| @@ -27,5 +27,11 @@ module Docs · 26 unmodified lines | ||
| 27 | 27 | self.release = '0.11.7' |
| 28 | 28 | self.base_url = "https://bottlepy.org/docs/#{self.version}/" |
| 29 | 29 | end |
| 30 | + | |
| 31 | + def get_latest_version(opts) | |
| 32 | + doc = fetch_doc('https://bottlepy.org/docs/stable/', opts) | |
| 33 | + label = doc.at_css('.sphinxsidebarwrapper > ul > li > b') | |
| 34 | + label.content.sub(/Bottle /, '') | |
| 35 | + end | |
| 30 | 36 | end |
| 31 | 37 | end |
| @@ -1,8 +1,8 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Bower < UrlScraper |
| 3 | 3 | self.name = 'Bower' |
| 4 | − self.type = 'bower' | |
| 5 | − self.release = '1.8.2' | |
| 4 | + self.type = 'simple' | |
| 5 | + self.release = '1.8.4' | |
| 6 | 6 | self.base_url = 'https://bower.io/docs/' |
| 7 | 7 | self.root_path = 'api' |
| 8 | 8 | self.links = { |
| @@ -16,8 +16,12 @@ module Docs · 7 unmodified lines | ||
| 16 | 16 | options[:skip] = %w(tools about) |
| 17 | 17 | |
| 18 | 18 | options[:attribution] = <<-HTML |
| 19 | − © 2017 Bower contributors<br> | |
| 19 | + © 2018 Bower contributors<br> | |
| 20 | 20 | Licensed under the MIT License. |
| 21 | 21 | HTML |
| 22 | + | |
| 23 | + def get_latest_version(opts) | |
| 24 | + get_npm_version('bower', opts) | |
| 25 | + end | |
| 22 | 26 | end |
| 23 | 27 | end |
| @@ -1,7 +1,6 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class C < FileScraper |
| 3 | 3 | self.type = 'c' |
| 4 | − self.dir = '/Users/Thibaut/DevDocs/Docs/C/c' | |
| 5 | 4 | self.base_url = 'http://en.cppreference.com/w/c/' |
| 6 | 5 | self.root_path = 'header.html' |
| 7 | 6 | |
| @@ -9,6 +8,7 @@ module Docs · 1 unmodified line | ||
| 9 | 8 | html_filters.push 'c/entries', 'c/clean_html', 'title' |
| 10 | 9 | text_filters.push 'c/fix_urls' |
| 11 | 10 | |
| 11 | + options[:decode_and_clean_paths] = true | |
| 12 | 12 | options[:container] = '#content' |
| 13 | 13 | options[:title] = false |
| 14 | 14 | options[:root_title] = 'C Programming Language' |
| @@ -25,5 +25,18 @@ module Docs · 10 unmodified lines | ||
| 25 | 25 | © cppreference.com<br> |
| 26 | 26 | Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0. |
| 27 | 27 | HTML |
| 28 | + | |
| 29 | + def get_latest_version(opts) | |
| 30 | + doc = fetch_doc('https://en.cppreference.com/w/Cppreference:Archives', opts) | |
| 31 | + link = doc.at_css('a[title^="File:"]') | |
| 32 | + date = link.content.scan(/(\d+)\./)[0][0] | |
| 33 | + DateTime.strptime(date, '%Y%m%d').to_time.to_i | |
| 34 | + end | |
| 35 | + | |
| 36 | + private | |
| 37 | + | |
| 38 | + def file_path_for(*) | |
| 39 | + URI.unescape(super) | |
| 40 | + end | |
| 28 | 41 | end |
| 29 | 42 | end |
| @@ -15,14 +15,19 @@ module Docs · 14 unmodified lines | ||
| 15 | 15 | options[:skip_patterns] = [/\Asource-/] |
| 16 | 16 | |
| 17 | 17 | options[:attribution] = <<-HTML |
| 18 | − © 2005–2017 The Cake Software Foundation, Inc.<br> | |
| 18 | + © 2005–2018 The Cake Software Foundation, Inc.<br> | |
| 19 | 19 | Licensed under the MIT License.<br> |
| 20 | 20 | CakePHP is a registered trademark of Cake Software Foundation, Inc.<br> |
| 21 | 21 | We are not endorsed by or affiliated with CakePHP. |
| 22 | 22 | HTML |
| 23 | 23 | |
| 24 | + version '3.6' do | |
| 25 | + self.release = '3.6.2' | |
| 26 | + self.base_url = 'https://api.cakephp.org/3.6/' | |
| 27 | + end | |
| 28 | + | |
| 24 | 29 | version '3.5' do |
| 25 | − self.release = '3.5.2' | |
| 30 | + self.release = '3.5.15' | |
| 26 | 31 | self.base_url = 'https://api.cakephp.org/3.5/' |
| 27 | 32 | end |
| 28 | 33 | |
| @@ -66,6 +71,11 @@ module Docs · 37 unmodified lines | ||
| 66 | 71 | self.base_url = 'https://api.cakephp.org/2.7/' |
| 67 | 72 | end |
| 68 | 73 | |
| 74 | + def get_latest_version(opts) | |
| 75 | + doc = fetch_doc('https://api.cakephp.org/3.7/', opts) | |
| 76 | + doc.at_css('.version-picker .dropdown-toggle').content.strip | |
| 77 | + end | |
| 78 | + | |
| 69 | 79 | private |
| 70 | 80 | |
| 71 | 81 | def parse(response) |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Chai < UrlScraper |
| 3 | 3 | self.name = 'Chai' |
| 4 | − self.type = 'chai' | |
| 4 | + self.type = 'simple' | |
| 5 | 5 | self.release = '4.1.0' |
| 6 | 6 | self.base_url = 'http://chaijs.com' |
| 7 | 7 | self.root_path = '/api/' |
| @@ -23,5 +23,9 @@ module Docs · 15 unmodified lines | ||
| 23 | 23 | © 2016 Chai.js Assertion Library<br> |
| 24 | 24 | Licensed under the MIT License. |
| 25 | 25 | HTML |
| 26 | + | |
| 27 | + def get_latest_version(opts) | |
| 28 | + get_npm_version('chai', opts) | |
| 29 | + end | |
| 26 | 30 | end |
| 27 | 31 | end |
| @@ -47,5 +47,10 @@ module Docs · 46 unmodified lines | ||
| 47 | 47 | |
| 48 | 48 | options[:only_patterns] = [/\A#{client_path}\//, /\A#{server_path}\//] |
| 49 | 49 | end |
| 50 | + | |
| 51 | + def get_latest_version(opts) | |
| 52 | + doc = fetch_doc('https://downloads.chef.io/chef', opts) | |
| 53 | + doc.at_css('h1.product-heading > span').content.strip | |
| 54 | + end | |
| 50 | 55 | end |
| 51 | 56 | end |
| @@ -13,14 +13,29 @@ module Docs · 12 unmodified lines | ||
| 13 | 13 | Licensed under the Eclipse Public License 1.0. |
| 14 | 14 | HTML |
| 15 | 15 | |
| 16 | − version '1.8' do | |
| 17 | − self.release = '1.8' | |
| 16 | + version '1.10' do | |
| 17 | + self.release = '1.10 (stable)' | |
| 18 | 18 | self.base_url = 'https://clojure.github.io/clojure/' |
| 19 | 19 | end |
| 20 | 20 | |
| 21 | + version '1.9' do | |
| 22 | + self.release = '1.9 (legacy)' | |
| 23 | + self.base_url = 'https://clojure.github.io/clojure/branch-clojure-1.9.0/' | |
| 24 | + end | |
| 25 | + | |
| 26 | + version '1.8' do | |
| 27 | + self.release = '1.8 (legacy)' | |
| 28 | + self.base_url = 'https://clojure.github.io/clojure/branch-clojure-1.8.0/' | |
| 29 | + end | |
| 30 | + | |
| 21 | 31 | version '1.7' do |
| 22 | − self.release = '1.7' | |
| 32 | + self.release = '1.7 (legacy)' | |
| 23 | 33 | self.base_url = 'https://clojure.github.io/clojure/branch-clojure-1.7.0/' |
| 24 | 34 | end |
| 35 | + | |
| 36 | + def get_latest_version(opts) | |
| 37 | + doc = fetch_doc('http://clojure.github.io/clojure/index.html', opts) | |
| 38 | + doc.at_css('#header-version').content[1..-1] | |
| 39 | + end | |
| 25 | 40 | end |
| 26 | 41 | end |
| @@ -13,36 +13,72 @@ module Docs · 12 unmodified lines | ||
| 13 | 13 | options[:title] = false |
| 14 | 14 | options[:root_title] = 'CMake Reference Documentation' |
| 15 | 15 | options[:skip] = %w(release/index.html genindex.html search.html) |
| 16 | − options[:skip_patterns] = [/\Agenerator/, /\Ainclude/, /\Arelease/] | |
| 16 | + options[:skip_patterns] = [/\Agenerator/, /\Acpack_gen/, /\Ainclude/, /\Arelease/] | |
| 17 | 17 | |
| 18 | 18 | options[:attribution] = <<-HTML |
| 19 | − © 2000–2017 Kitware, Inc. and Contributors<br> | |
| 19 | + © 2000–2019 Kitware, Inc. and Contributors<br> | |
| 20 | 20 | Licensed under the BSD 3-clause License. |
| 21 | 21 | HTML |
| 22 | 22 | |
| 23 | + version '3.15' do | |
| 24 | + self.release = '3.15.2' | |
| 25 | + self.base_url = "https://cmake.org/cmake/help/v#{self.version}/" | |
| 26 | + end | |
| 27 | + | |
| 28 | + version '3.14' do | |
| 29 | + self.release = '3.14.6' | |
| 30 | + self.base_url = "https://cmake.org/cmake/help/v#{self.version}/" | |
| 31 | + end | |
| 32 | + | |
| 33 | + version '3.13' do | |
| 34 | + self.release = '3.13.5' | |
| 35 | + self.base_url = "https://cmake.org/cmake/help/v#{self.version}/" | |
| 36 | + end | |
| 37 | + | |
| 38 | + version '3.12' do | |
| 39 | + self.release = '3.12.4' | |
| 40 | + self.base_url = "https://cmake.org/cmake/help/v#{self.version}/" | |
| 41 | + end | |
| 42 | + | |
| 43 | + version '3.11' do | |
| 44 | + self.release = '3.11.4' | |
| 45 | + self.base_url = "https://cmake.org/cmake/help/v#{self.version}/" | |
| 46 | + end | |
| 47 | + | |
| 48 | + version '3.10' do | |
| 49 | + self.release = '3.10.3' | |
| 50 | + self.base_url = "https://cmake.org/cmake/help/v#{self.version}/" | |
| 51 | + end | |
| 52 | + | |
| 23 | 53 | version '3.9' do |
| 24 | − self.release = '3.9.0' | |
| 25 | − self.base_url = 'https://cmake.org/cmake/help/v3.9/' | |
| 54 | + self.release = '3.9.6' | |
| 55 | + self.base_url = "https://cmake.org/cmake/help/v#{self.version}/" | |
| 26 | 56 | end |
| 27 | 57 | |
| 28 | 58 | version '3.8' do |
| 29 | 59 | self.release = '3.8.2' |
| 30 | − self.base_url = 'https://cmake.org/cmake/help/v3.8/' | |
| 60 | + self.base_url = "https://cmake.org/cmake/help/v#{self.version}/" | |
| 31 | 61 | end |
| 32 | 62 | |
| 33 | 63 | version '3.7' do |
| 34 | 64 | self.release = '3.7.2' |
| 35 | − self.base_url = 'https://cmake.org/cmake/help/v3.7/' | |
| 65 | + self.base_url = "https://cmake.org/cmake/help/v#{self.version}/" | |
| 36 | 66 | end |
| 37 | 67 | |
| 38 | 68 | version '3.6' do |
| 39 | 69 | self.release = '3.6.3' |
| 40 | − self.base_url = 'https://cmake.org/cmake/help/v3.6/' | |
| 70 | + self.base_url = "https://cmake.org/cmake/help/v#{self.version}/" | |
| 41 | 71 | end |
| 42 | 72 | |
| 43 | 73 | version '3.5' do |
| 44 | 74 | self.release = '3.5.2' |
| 45 | − self.base_url = 'https://cmake.org/cmake/help/v3.5/' | |
| 75 | + self.base_url = "https://cmake.org/cmake/help/v#{self.version}/" | |
| 76 | + end | |
| 77 | + | |
| 78 | + def get_latest_version(opts) | |
| 79 | + doc = fetch_doc('https://cmake.org/documentation/', opts) | |
| 80 | + link = doc.at_css('.entry-content ul > li > strong > a > big') | |
| 81 | + link.content.scan(/([0-9.]+)/)[0][0] | |
| 46 | 82 | end |
| 47 | 83 | end |
| 48 | 84 | end |
| @@ -2,11 +2,11 @@ module Docs · 1 unmodified line | ||
| 2 | 2 | class Codeception < UrlScraper |
| 3 | 3 | self.name = 'Codeception' |
| 4 | 4 | self.type = 'codeception' |
| 5 | − self.release = '2.3.5' | |
| 6 | − self.base_url = 'http://codeception.com/docs/' | |
| 5 | + self.release = '2.5.1' | |
| 6 | + self.base_url = 'https://codeception.com/docs/' | |
| 7 | 7 | self.root_path = 'index.html' |
| 8 | 8 | self.links = { |
| 9 | − home: 'http://codeception.com/', | |
| 9 | + home: 'https://codeception.com/', | |
| 10 | 10 | code: 'https://github.com/codeception/codeception' |
| 11 | 11 | } |
| 12 | 12 | |
| @@ -15,8 +15,13 @@ module Docs · 2 unmodified lines | ||
| 15 | 15 | options[:skip_patterns] = [/install/] |
| 16 | 16 | |
| 17 | 17 | options[:attribution] = <<-HTML |
| 18 | − © 2011–2017 Michael Bodnarchuk and contributors<br> | |
| 18 | + © 2011 Michael Bodnarchuk and contributors<br> | |
| 19 | 19 | Licensed under the MIT License. |
| 20 | 20 | HTML |
| 21 | + | |
| 22 | + def get_latest_version(opts) | |
| 23 | + doc = fetch_doc('https://codeception.com/changelog', opts) | |
| 24 | + doc.at_css('#page > h4').content | |
| 25 | + end | |
| 21 | 26 | end |
| 22 | 27 | end |
| @@ -1,12 +1,12 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Codeceptjs < UrlScraper |
| 3 | 3 | self.name = 'CodeceptJS' |
| 4 | − self.type = 'codeceptjs' | |
| 4 | + self.type = 'simple' | |
| 5 | 5 | self.root_path = 'index.html' |
| 6 | − self.release = '1.0.1' | |
| 7 | − self.base_url = 'http://codecept.io/' | |
| 6 | + self.release = '1.4.4' | |
| 7 | + self.base_url = 'https://codecept.io/' | |
| 8 | 8 | self.links = { |
| 9 | − home: 'http://codecept.io/', | |
| 9 | + home: 'https://codecept.io/', | |
| 10 | 10 | code: 'https://github.com/codeception/codeceptjs' |
| 11 | 11 | } |
| 12 | 12 | |
| @@ -21,5 +21,9 @@ module Docs · 8 unmodified lines | ||
| 21 | 21 | © 2015 DavertMik <davert@codegyre.com> (http://codegyre.com)<br> |
| 22 | 22 | Licensed under the MIT License. |
| 23 | 23 | HTML |
| 24 | + | |
| 25 | + def get_latest_version(opts) | |
| 26 | + get_npm_version('codeceptjs', opts) | |
| 27 | + end | |
| 24 | 28 | end |
| 25 | 29 | end |
| @@ -31,12 +31,18 @@ module Docs · 30 unmodified lines | ||
| 31 | 31 | ] |
| 32 | 32 | |
| 33 | 33 | options[:attribution] = <<-HTML |
| 34 | − © 2014–2017 British Columbia Institute of Technology<br> | |
| 34 | + © 2014–2018 British Columbia Institute of Technology<br> | |
| 35 | 35 | Licensed under the MIT License. |
| 36 | 36 | HTML |
| 37 | 37 | |
| 38 | 38 | version '3' do |
| 39 | − self.release = '3.1.5' | |
| 39 | + self.release = '3.1.8' | |
| 40 | + end | |
| 41 | + | |
| 42 | + def get_latest_version(opts) | |
| 43 | + doc = fetch_doc('https://codeigniter.com/user_guide/changelog.html', opts) | |
| 44 | + header = doc.at_css('#change-log h2') | |
| 45 | + header.content.scan(/([0-9.]+)/)[0][0] | |
| 40 | 46 | end |
| 41 | 47 | end |
| 42 | 48 | end |
| @@ -11,12 +11,12 @@ module Docs · 10 unmodified lines | ||
| 11 | 11 | options[:skip_links] = true |
| 12 | 12 | |
| 13 | 13 | options[:attribution] = <<-HTML |
| 14 | − © 2009–2017 Jeremy Ashkenas<br> | |
| 14 | + © 2009–2018 Jeremy Ashkenas<br> | |
| 15 | 15 | Licensed under the MIT License. |
| 16 | 16 | HTML |
| 17 | 17 | |
| 18 | 18 | version '2' do |
| 19 | − self.release = '2.0.1' | |
| 19 | + self.release = '2.3.0' | |
| 20 | 20 | self.base_url = 'http://coffeescript.org/' |
| 21 | 21 | |
| 22 | 22 | html_filters.push 'coffeescript/entries', 'coffeescript/clean_html', 'title' |
| @@ -30,5 +30,9 @@ module Docs · 7 unmodified lines | ||
| 30 | 30 | |
| 31 | 31 | options[:container] = '.container' |
| 32 | 32 | end |
| 33 | + | |
| 34 | + def get_latest_version(opts) | |
| 35 | + get_npm_version('coffeescript', opts) | |
| 36 | + end | |
| 33 | 37 | end |
| 34 | 38 | end |
| @@ -24,12 +24,17 @@ module Docs · 23 unmodified lines | ||
| 24 | 24 | end |
| 25 | 25 | |
| 26 | 26 | options[:attribution] = <<-HTML |
| 27 | − © 2012–2017 The Apache Software Foundation<br> | |
| 27 | + © 2012–2018 The Apache Software Foundation<br> | |
| 28 | 28 | Licensed under the Apache License 2.0. |
| 29 | 29 | HTML |
| 30 | 30 | |
| 31 | + version '8' do | |
| 32 | + self.release = '8.1.2' | |
| 33 | + self.base_url = 'https://cordova.apache.org/docs/en/8.x/' | |
| 34 | + end | |
| 35 | + | |
| 31 | 36 | version '7' do |
| 32 | − self.release = '7.0.1' | |
| 37 | + self.release = '7.1.0' | |
| 33 | 38 | self.base_url = 'https://cordova.apache.org/docs/en/7.x/' |
| 34 | 39 | end |
| 35 | 40 | |
| @@ -37,5 +42,15 @@ module Docs · 1 unmodified line | ||
| 37 | 42 | self.release = '6.5.0' |
| 38 | 43 | self.base_url = 'https://cordova.apache.org/docs/en/6.x/' |
| 39 | 44 | end |
| 45 | + | |
| 46 | + def get_latest_version(opts) | |
| 47 | + doc = fetch_doc('https://cordova.apache.org/docs/en/latest/', opts) | |
| 48 | + | |
| 49 | + label = doc.at_css('#versionDropdown').content.strip | |
| 50 | + version = label.scan(/([0-9.]+)/)[0][0] | |
| 51 | + version = version[0...-1] if version.end_with?('.') | |
| 52 | + | |
| 53 | + version | |
| 54 | + end | |
| 40 | 55 | end |
| 41 | 56 | end |
| @@ -3,7 +3,6 @@ module Docs · 2 unmodified lines | ||
| 3 | 3 | self.name = 'C++' |
| 4 | 4 | self.slug = 'cpp' |
| 5 | 5 | self.type = 'c' |
| 6 | − self.dir = '/Users/Thibaut/DevDocs/Docs/C/cpp' | |
| 7 | 6 | self.base_url = 'http://en.cppreference.com/w/cpp/' |
| 8 | 7 | self.root_path = 'header.html' |
| 9 | 8 | |
| @@ -11,6 +10,7 @@ module Docs · 1 unmodified line | ||
| 11 | 10 | html_filters.push 'cpp/entries', 'c/clean_html', 'title' |
| 12 | 11 | text_filters.push 'cpp/fix_urls' |
| 13 | 12 | |
| 13 | + options[:decode_and_clean_paths] = true | |
| 14 | 14 | options[:container] = '#content' |
| 15 | 15 | options[:title] = false |
| 16 | 16 | options[:root_title] = 'C++ Programming Language' |
| @@ -33,5 +33,19 @@ module Docs · 16 unmodified lines | ||
| 33 | 33 | © cppreference.com<br> |
| 34 | 34 | Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0. |
| 35 | 35 | HTML |
| 36 | + | |
| 37 | + # Same as get_latest_version in lib/docs/scrapers/c.rb | |
| 38 | + def get_latest_version(opts) | |
| 39 | + doc = fetch_doc('https://en.cppreference.com/w/Cppreference:Archives', opts) | |
| 40 | + link = doc.at_css('a[title^="File:"]') | |
| 41 | + date = link.content.scan(/(\d+)\./)[0][0] | |
| 42 | + DateTime.strptime(date, '%Y%m%d').to_time.to_i | |
| 43 | + end | |
| 44 | + | |
| 45 | + private | |
| 46 | + | |
| 47 | + def file_path_for(*) | |
| 48 | + URI.unescape(super) | |
| 49 | + end | |
| 36 | 50 | end |
| 37 | 51 | end |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Crystal < UrlScraper |
| 3 | 3 | self.type = 'crystal' |
| 4 | − self.release = '0.23.0' | |
| 4 | + self.release = '0.27.0' | |
| 5 | 5 | self.base_url = 'https://crystal-lang.org/' |
| 6 | 6 | self.root_path = "api/#{release}/index.html" |
| 7 | 7 | self.initial_paths = %w(docs/index.html) |
| @@ -29,10 +29,15 @@ module Docs · 21 unmodified lines | ||
| 29 | 29 | HTML |
| 30 | 30 | else |
| 31 | 31 | <<-HTML |
| 32 | − © 2012–2017 Manas Technology Solutions.<br> | |
| 32 | + © 2012–2018 Manas Technology Solutions.<br> | |
| 33 | 33 | Licensed under the Apache License, Version 2.0. |
| 34 | 34 | HTML |
| 35 | 35 | end |
| 36 | 36 | } |
| 37 | + | |
| 38 | + def get_latest_version(opts) | |
| 39 | + body = fetch('https://crystal-lang.org/api', opts) | |
| 40 | + body.scan(/Crystal Docs ([0-9.]+)/)[0][0] | |
| 41 | + end | |
| 37 | 42 | end |
| 38 | 43 | end |
| @@ -2,7 +2,7 @@ module Docs · 1 unmodified line | ||
| 2 | 2 | class D < UrlScraper |
| 3 | 3 | include MultipleBaseUrls |
| 4 | 4 | |
| 5 | − self.release = '2.076.0' | |
| 5 | + self.release = '2.083.0' | |
| 6 | 6 | self.type = 'd' |
| 7 | 7 | self.base_urls = ['https://dlang.org/phobos/', 'https://dlang.org/spec/'] |
| 8 | 8 | self.root_path = 'index.html' |
| @@ -19,12 +19,17 @@ module Docs · 10 unmodified lines | ||
| 19 | 19 | options[:title] = false |
| 20 | 20 | |
| 21 | 21 | options[:attribution] = <<-HTML |
| 22 | − © 1999–2017 The D Language Foundation<br> | |
| 22 | + © 1999–2018 The D Language Foundation<br> | |
| 23 | 23 | Licensed under the Boost License 1.0. |
| 24 | 24 | HTML |
| 25 | 25 | |
| 26 | 26 | def initial_urls |
| 27 | 27 | %w(https://dlang.org/phobos/index.html https://dlang.org/spec/intro.html) |
| 28 | 28 | end |
| 29 | + | |
| 30 | + def get_latest_version(opts) | |
| 31 | + doc = fetch_doc('https://dlang.org/changelog/', opts) | |
| 32 | + doc.at_css('#content > ul > li:nth-child(2) > a')['id'] | |
| 33 | + end | |
| 29 | 34 | end |
| 30 | 35 | end |
| @@ -12,12 +12,28 @@ module Docs · 11 unmodified lines | ||
| 12 | 12 | options[:container] = '.markdown-body' |
| 13 | 13 | |
| 14 | 14 | options[:attribution] = <<-HTML |
| 15 | − © 2010–2017 Michael Bostock<br> | |
| 15 | + © 2010–2018 Michael Bostock<br> | |
| 16 | 16 | Licensed under the BSD License. |
| 17 | 17 | HTML |
| 18 | 18 | |
| 19 | + version '5' do | |
| 20 | + self.release = '5.7.0' | |
| 21 | + self.base_url = 'https://github.com/d3/' | |
| 22 | + self.root_path = 'd3/blob/master/API.md' | |
| 23 | + | |
| 24 | + html_filters.push 'd3/clean_html', 'd3/entries_v4' | |
| 25 | + | |
| 26 | + options[:only_patterns] = [/\Ad3[\-\w]+\z/, /\Ad3\/blob\/master\/changes\.md\z/i] | |
| 27 | + options[:skip_patterns] = [/3\.x-api-reference/] | |
| 28 | + | |
| 29 | + options[:fix_urls] = ->(url) do | |
| 30 | + url.sub! %r{/blob/master/readme.md}i, '' | |
| 31 | + url | |
| 32 | + end | |
| 33 | + end | |
| 34 | + | |
| 19 | 35 | version '4' do |
| 20 | − self.release = '4.11.0' | |
| 36 | + self.release = '4.12.2' | |
| 21 | 37 | self.base_url = 'https://github.com/d3/' |
| 22 | 38 | self.root_path = 'd3/blob/master/API.md' |
| 23 | 39 | |
| @@ -42,5 +58,9 @@ module Docs · 18 unmodified lines | ||
| 42 | 58 | options[:root_title] = 'D3.js' |
| 43 | 59 | options[:only_patterns] = [/\.md\z/] |
| 44 | 60 | end |
| 61 | + | |
| 62 | + def get_latest_version(opts) | |
| 63 | + get_npm_version('d3', opts) | |
| 64 | + end | |
| 45 | 65 | end |
| 46 | 66 | end |
| @@ -0,0 +1,41 @@ | ||
| 1 | +module Docs | |
| 2 | + class Dart < FileScraper | |
| 3 | + self.type = 'dart' | |
| 4 | + self.root_path = 'index.html' | |
| 5 | + self.links = { | |
| 6 | + home: 'https://www.dartlang.org/', | |
| 7 | + code: 'https://github.com/dart-lang/sdk' | |
| 8 | + } | |
| 9 | + | |
| 10 | + html_filters.push 'dart/entries', 'dart/clean_html' | |
| 11 | + | |
| 12 | + options[:fix_urls] = ->(url) do | |
| 13 | + # localhost/dart-web_audio/..dart-io/dart-io-library.html > localhost/dart-io/dart-io-library.html | |
| 14 | + url.remove!(/[^\/]+\/\.\./) | |
| 15 | + url | |
| 16 | + end | |
| 17 | + | |
| 18 | + options[:attribution] = <<-HTML | |
| 19 | + © 2012 the Dart project authors<br> | |
| 20 | + Licensed under the Creative Commons Attribution-ShareAlike License v4.0. | |
| 21 | + HTML | |
| 22 | + | |
| 23 | + # Download the documentation from https://www.dartlang.org/tools/sdk/archive | |
| 24 | + | |
| 25 | + version '2' do | |
| 26 | + self.release = '2.0.0' | |
| 27 | + self.base_url = "https://api.dartlang.org/stable/#{release}/" | |
| 28 | + end | |
| 29 | + | |
| 30 | + version '1' do | |
| 31 | + self.release = '1.24.3' | |
| 32 | + self.base_url = "https://api.dartlang.org/stable/#{release}/" | |
| 33 | + end | |
| 34 | + | |
| 35 | + def get_latest_version(opts) | |
| 36 | + doc = fetch_doc('https://api.dartlang.org/', opts) | |
| 37 | + label = doc.at_css('footer > span').content.strip | |
| 38 | + label.sub(/Dart /, '') | |
| 39 | + end | |
| 40 | + end | |
| 41 | +end | |
| @@ -34,28 +34,44 @@ module Docs · 33 unmodified lines | ||
| 34 | 34 | Licensed under the BSD License. |
| 35 | 35 | HTML |
| 36 | 36 | |
| 37 | + version '2.2' do | |
| 38 | + self.release = '2.2.4' | |
| 39 | + self.base_url = 'https://docs.djangoproject.com/en/2.2/' | |
| 40 | + end | |
| 41 | + | |
| 42 | + version '2.1' do | |
| 43 | + self.release = '2.1.11' | |
| 44 | + self.base_url = 'https://docs.djangoproject.com/en/2.1/' | |
| 45 | + end | |
| 46 | + | |
| 47 | + version '2.0' do | |
| 48 | + self.release = '2.0.13' | |
| 49 | + self.base_url = 'https://docs.djangoproject.com/en/2.0/' | |
| 50 | + end | |
| 51 | + | |
| 37 | 52 | version '1.11' do |
| 38 | − self.release = '1.11.5' | |
| 39 | − self.dir = '/Users/Thibaut/DevDocs/Docs/Django111' | |
| 53 | + self.release = '1.11.23' | |
| 40 | 54 | self.base_url = 'https://docs.djangoproject.com/en/1.11/' |
| 41 | 55 | end |
| 42 | 56 | |
| 43 | 57 | version '1.10' do |
| 44 | 58 | self.release = '1.10.8' |
| 45 | − self.dir = '/Users/Thibaut/DevDocs/Docs/Django110' | |
| 46 | 59 | self.base_url = 'https://docs.djangoproject.com/en/1.10/' |
| 47 | 60 | end |
| 48 | 61 | |
| 49 | 62 | version '1.9' do |
| 50 | 63 | self.release = '1.9.13' |
| 51 | − self.dir = '/Users/Thibaut/DevDocs/Docs/Django19' | |
| 52 | 64 | self.base_url = 'https://docs.djangoproject.com/en/1.9/' |
| 53 | 65 | end |
| 54 | 66 | |
| 55 | 67 | version '1.8' do |
| 56 | 68 | self.release = '1.8.18' |
| 57 | − self.dir = '/Users/Thibaut/DevDocs/Docs/Django18' | |
| 58 | 69 | self.base_url = 'https://docs.djangoproject.com/en/1.8/' |
| 59 | 70 | end |
| 71 | + | |
| 72 | + def get_latest_version(opts) | |
| 73 | + doc = fetch_doc('https://docs.djangoproject.com/', opts) | |
| 74 | + doc.at_css('#doc-versions > li.current > span > strong').content | |
| 75 | + end | |
| 60 | 76 | end |
| 61 | 77 | end |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Docker < UrlScraper |
| 3 | − self.type = 'docker' | |
| 3 | + self.type = 'simple' | |
| 4 | 4 | self.links = { |
| 5 | 5 | home: 'https://docker.com/', |
| 6 | 6 | code: 'https://github.com/docker/docker' |
| @@ -15,15 +15,15 @@ module Docs · 8 unmodified lines | ||
| 15 | 15 | end |
| 16 | 16 | |
| 17 | 17 | options[:attribution] = <<-HTML |
| 18 | − © 2017 Docker, Inc.<br> | |
| 18 | + © 2019 Docker, Inc.<br> | |
| 19 | 19 | Licensed under the Apache License, Version 2.0.<br> |
| 20 | 20 | Docker and the Docker logo are trademarks or registered trademarks of Docker, Inc. in the United States and/or other countries.<br> |
| 21 | 21 | Docker, Inc. and other parties may also have trademark rights in other terms used herein. |
| 22 | 22 | HTML |
| 23 | 23 | |
| 24 | − version '17' do | |
| 25 | − self.release = '17.06' | |
| 26 | − self.base_url = 'https://docs.docker.com/' | |
| 24 | + version '19' do | |
| 25 | + self.release = '19.03' | |
| 26 | + self.base_url = "https://docs.docker.com/" | |
| 27 | 27 | |
| 28 | 28 | html_filters.push 'docker/entries', 'docker/clean_html' |
| 29 | 29 | |
| @@ -33,29 +33,150 @@ module Docs · 3 unmodified lines | ||
| 33 | 33 | options[:skip_patterns] = [/\Aengine\/api\/v/, /glossary/, /docker-ee/] |
| 34 | 34 | |
| 35 | 35 | options[:replace_paths] = { |
| 36 | − 'engine/installation/linux/docker-ee/linux-postinstall/' => 'engine/installation/linux/linux-postinstall/', | |
| 37 | − 'engine/installation/linux/docker-ee/' => 'engine/installation/', | |
| 38 | − 'engine/installation/linux/docker-ce/' => 'engine/installation/', | |
| 39 | − 'engine/installation/linux/' => 'engine/installation/', | |
| 40 | − 'engine/installation/windows/' => 'engine/installation/', | |
| 41 | − 'engine/userguide/intro/' => 'engine/userguide/', | |
| 42 | − 'engine/tutorials/dockervolumes/' => 'engine/admin/volumes/volumes/', | |
| 43 | − 'engine/getstarted/' => 'get-started/', | |
| 44 | − 'engine/tutorials/dockerimages/' => 'get-started/', | |
| 45 | − 'engine/tutorials/dockerrepos/' => 'get-started/', | |
| 46 | − 'engine/admin/host_integration/' => 'engine/admin/start-containers-automatically/', | |
| 47 | − 'engine/installation/linux/rhel/' => 'engine/installation/linux/docker-ee/rhel/', | |
| 48 | − 'engine/installation/linux/ubuntulinux/' => 'engine/installation/linux/docker-ee/ubuntu/', | |
| 49 | − 'engine/installation/linux/suse/' => 'engine/installation/linux/docker-ee/suse/', | |
| 50 | − 'engine/admin/logging/' => 'engine/admin/logging/view_container_logs/', | |
| 51 | − 'engine/swarm/how-swarm-mode-works/' => 'engine/swarm/how-swarm-mode-works/nodes/', | |
| 52 | − 'engine/installation/binaries/' => 'engine/installation/linux/docker-ce/binaries/', | |
| 36 | + 'install/linux/ubuntu/' => 'install/linux/docker-ce', | |
| 37 | + 'get-started/part1' => 'get-started', | |
| 38 | + 'engine/installation/' => 'install', | |
| 39 | + 'engine/installation/linux/linux-postinstall/' => 'install/linux', | |
| 40 | + 'compose/overview/' => 'compose', | |
| 41 | + 'docker-cloud/' => 'docker-hub', | |
| 42 | + 'datacenter/install/linux/' => 'ee', | |
| 43 | + 'engine/userguide/' => 'config/daemon', | |
| 44 | + 'engine/admin/' => 'config/daemon', | |
| 45 | + 'opensource/get-help/' => 'opensource', | |
| 46 | + 'engine/tutorials/dockerimages/' => 'get-started', | |
| 47 | + 'engine/admin/volumes/bind-mounts/' => 'storage', | |
| 48 | + 'engine/tutorials/dockervolumes/' => 'storage', | |
| 49 | + 'engine/admin/volumes/volumes/' => 'storage', | |
| 50 | + 'engine/userguide/labels-custom-metadata/' => 'config', | |
| 51 | + 'engine/userguide/eng-image/multistage-build/' => 'develop/develop-images', | |
| 52 | + 'engine/swarm/networking/' => 'network', | |
| 53 | + 'engine/admin/resource_constraints/' => 'config/containers', | |
| 54 | + 'engine/admin/logging/overview/' => 'config/containers/logging', | |
| 55 | + 'engine/userguide/eng-image/dockerfile_best-practices/' => 'develop/develop-images', | |
| 56 | + 'engine/tutorials/dockerrepos/' => 'get-started', | |
| 57 | + 'engine/userguide/networking/' => 'network', | |
| 58 | + 'engine/userguide/networking/get-started-overlay/' => 'network', | |
| 59 | + 'engine/reference/commandline/swarm_join_token/' => 'engine/reference/commandline', | |
| 60 | + 'engine/understanding-docker/' => 'engine', | |
| 61 | + 'engine/userguide/dockervolumes/' => 'storage', | |
| 62 | + 'engine/installation/binaries/' => 'install/linux/docker-ce', | |
| 63 | + 'engine/userguide/networking/default_network/dockerlinks/' => 'network', | |
| 64 | + 'engine/reference/api/' => 'develop/sdk', | |
| 65 | + 'engine/admin/systemd/' => 'config/daemon', | |
| 66 | + 'engine/userguide/storagedriver/imagesandcontainers/' => 'storage/storagedriver', | |
| 67 | + 'engine/api/' => 'develop/sdk', | |
| 68 | + 'engine/userguide/networking/get-started-overlay' => 'network', | |
| 69 | + 'engine/userguide/networking/overlay-security-model/' => 'network', | |
| 70 | + 'engine/installation/linux/docker-ce/binaries/' => 'install/linux/docker-ce', | |
| 71 | + 'engine/admin/volumes/' => 'storage/volumes/', | |
| 72 | + 'engine/userguide/networking//' => 'network', | |
| 73 | + 'engine/reference/commandline' => 'engine/reference/commandline/docker', | |
| 53 | 74 | 'engine/reference/commandline/' => 'engine/reference/commandline/docker/', |
| 54 | − 'engine/reference/api/' => 'engine/api/', | |
| 55 | − 'engine/userguide/dockervolumes/' => 'engine/admin/volumes/volumes/', | |
| 56 | − 'engine/understanding-docker/' => 'engine/docker-overview/', | |
| 57 | − 'engine/reference/commandline/swarm_join_token/' => 'engine/reference/commandline/swarm_join-token/', | |
| 58 | − 'engine/api/getting-started/' => 'engine/api/get-started/', | |
| 75 | + } | |
| 76 | + end | |
| 77 | + | |
| 78 | + version '18' do | |
| 79 | + self.release = '18.09' | |
| 80 | + self.base_url = "https://docs.docker.com/v#{release}/" | |
| 81 | + | |
| 82 | + html_filters.push 'docker/entries', 'docker/clean_html' | |
| 83 | + | |
| 84 | + options[:container] = '.wrapper .container-fluid .row' | |
| 85 | + | |
| 86 | + options[:only_patterns] = [/\Aget-started\//, /\Aengine\//, /\Acompose\//, /\Amachine\//, /\Anotary\//] | |
| 87 | + options[:skip_patterns] = [/\Aengine\/api\/v/, /glossary/, /docker-ee/] | |
| 88 | + | |
| 89 | + options[:replace_paths] = { | |
| 90 | + 'install/linux/ubuntu/' => 'install/linux/docker-ce', | |
| 91 | + 'get-started/part1' => 'get-started', | |
| 92 | + 'engine/installation/' => 'install', | |
| 93 | + 'engine/installation/linux/linux-postinstall/' => 'install/linux', | |
| 94 | + 'compose/overview/' => 'compose', | |
| 95 | + 'datacenter/install/linux/' => 'ee', | |
| 96 | + 'engine/userguide/' => 'config/daemon', | |
| 97 | + 'engine/admin/' => 'config/daemon', | |
| 98 | + 'opensource/get-help/' => 'opensource', | |
| 99 | + 'engine/tutorials/dockerimages/' => 'get-started', | |
| 100 | + 'engine/admin/volumes/bind-mounts/' => 'storage', | |
| 101 | + 'engine/tutorials/dockervolumes/' => 'storage', | |
| 102 | + 'engine/admin/volumes/volumes/' => 'storage', | |
| 103 | + 'engine/userguide/labels-custom-metadata/' => 'config', | |
| 104 | + 'engine/reference/api/' => 'develop/sdk', | |
| 105 | + 'engine/userguide/eng-image/multistage-build/' => 'develop/develop-images', | |
| 106 | + 'engine/swarm/networking/' => 'network', | |
| 107 | + 'engine/admin/resource_constraints/' => 'config/containers', | |
| 108 | + 'engine/admin/logging/overview/' => 'config/containers/logging', | |
| 109 | + 'engine/userguide/eng-image/dockerfile_best-practices/' => 'develop/develop-images', | |
| 110 | + 'engine/tutorials/dockerrepos/' => 'get-started', | |
| 111 | + 'engine/userguide/networking/' => 'network', | |
| 112 | + 'engine/userguide/networking/get-started-overlay/' => 'network', | |
| 113 | + 'engine/understanding-docker/' => 'engine', | |
| 114 | + 'engine/reference/commandline/swarm_join_token/' => 'engine/reference/commandline', | |
| 115 | + 'engine/userguide/dockervolumes/' => 'storage', | |
| 116 | + 'engine/admin/systemd/' => 'config/daemon', | |
| 117 | + 'engine/userguide/storagedriver/imagesandcontainers/' => 'storage/storagedriver', | |
| 118 | + 'engine/installation/binaries/' => 'install/linux/docker-ce', | |
| 119 | + 'engine/userguide/networking/default_network/dockerlinks/' => 'network', | |
| 120 | + 'engine/userguide/networking/overlay-security-model/' => 'network', | |
| 121 | + 'engine/userguide/networking/get-started-overlay' => 'network', | |
| 122 | + 'engine/api/' => 'develop/sdk', | |
| 123 | + 'engine/installation/linux/docker-ce/binaries/' => 'install/linux/docker-ce', | |
| 124 | + 'engine/admin/volumes/' => 'storage/volumes/', | |
| 125 | + 'engine/userguide/networking//' => 'network', | |
| 126 | + 'engine/reference/commandline' => 'engine/reference/commandline/docker', | |
| 127 | + 'engine/reference/commandline/' => 'engine/reference/commandline/docker/', | |
| 128 | + } | |
| 129 | + end | |
| 130 | + | |
| 131 | + version '17' do | |
| 132 | + self.release = '17.12' | |
| 133 | + self.base_url = "https://docs.docker.com/v#{release}/" | |
| 134 | + | |
| 135 | + html_filters.push 'docker/entries', 'docker/clean_html' | |
| 136 | + | |
| 137 | + options[:container] = '.wrapper .container-fluid .row' | |
| 138 | + | |
| 139 | + options[:only_patterns] = [/\Aget-started\//, /\Aengine\//, /\Acompose\//, /\Amachine\//, /\Anotary\//] | |
| 140 | + options[:skip_patterns] = [/\Aengine\/api\/v/, /glossary/, /docker-ee/] | |
| 141 | + | |
| 142 | + options[:replace_paths] = { | |
| 143 | + 'get-started/part1' => 'get-started', | |
| 144 | + 'engine/installation/' => 'install', | |
| 145 | + 'engine/installation/linux/linux-postinstall/' => 'install/linux', | |
| 146 | + 'opensource/get-help/' => 'opensource', | |
| 147 | + 'engine/admin/volumes/volumes/' => 'storage', | |
| 148 | + 'engine/tutorials/dockerimages/' => 'get-started', | |
| 149 | + 'engine/admin/volumes/bind-mounts/' => 'storage', | |
| 150 | + 'engine/tutorials/dockervolumes/' => 'storage', | |
| 151 | + 'datacenter/install/aws/' => 'docker-for-aws', | |
| 152 | + 'engine/userguide/' => 'config/daemon', | |
| 153 | + 'engine/admin/' => 'config/daemon', | |
| 154 | + 'engine/userguide/labels-custom-metadata/' => 'config', | |
| 155 | + 'engine/userguide/eng-image/multistage-build/' => 'develop/develop-images', | |
| 156 | + 'engine/swarm/networking/' => 'network', | |
| 157 | + 'engine/admin/resource_constraints/' => 'config/containers', | |
| 158 | + 'engine/admin/logging/overview/' => 'config/containers/logging', | |
| 159 | + 'engine/understanding-docker/' => 'engine', | |
| 160 | + 'engine/userguide/eng-image/dockerfile_best-practices/' => 'develop/develop-images', | |
| 161 | + 'engine/tutorials/dockerrepos/' => 'get-started', | |
| 162 | + 'engine/userguide/networking/' => 'network', | |
| 163 | + 'engine/reference/commandline/swarm_join_token/' => 'edge/engine/reference/commandline', | |
| 164 | + 'engine/userguide/networking/get-started-overlay/' => 'network', | |
| 165 | + 'engine/userguide/dockervolumes/' => 'storage', | |
| 166 | + 'engine/installation/binaries/' => 'install/linux/docker-ce', | |
| 167 | + 'engine/userguide/networking/default_network/dockerlinks/' => 'network', | |
| 168 | + 'engine/reference/api/' => 'develop/sdk', | |
| 169 | + 'engine/admin/live-restore/' => 'config/containers', | |
| 170 | + 'engine/api/' => 'develop/sdk', | |
| 171 | + 'engine/userguide/networking/get-started-overlay' => 'network', | |
| 172 | + 'security/security/' => 'engine/security', | |
| 173 | + 'engine/installation/linux/docker-ce/binaries/' => 'install/linux/docker-ce', | |
| 174 | + 'engine/reference/commandline/' => 'edge/engine/reference/commandline', | |
| 175 | + 'engine/admin/systemd/' => 'config/daemon', | |
| 176 | + 'engine/userguide/storagedriver/imagesandcontainers/' => 'storage/storagedriver', | |
| 177 | + 'engine/userguide/networking/overlay-security-model/' => 'network', | |
| 178 | + 'engine/admin/volumes/' => 'storage/volumes/', | |
| 179 | + 'engine/userguide/networking//' => 'network', | |
| 59 | 180 | } |
| 60 | 181 | end |
| 61 | 182 | |
| @@ -137,5 +258,11 @@ module Docs · 75 unmodified lines | ||
| 137 | 258 | options[:container] = '#docs' |
| 138 | 259 | options[:only_patterns] << /\Aswarm\// |
| 139 | 260 | end |
| 261 | + | |
| 262 | + def get_latest_version(opts) | |
| 263 | + doc = fetch_doc('https://docs.docker.com/', opts) | |
| 264 | + label = doc.at_css('.nav-container button.dropdown-toggle').content.strip | |
| 265 | + label.scan(/([0-9.]+)/)[0][0] | |
| 266 | + end | |
| 140 | 267 | end |
| 141 | 268 | end |
| @@ -36,6 +36,11 @@ module Docs · 35 unmodified lines | ||
| 36 | 36 | urls.map { |url| "<a href='#{url}'>#{url}</a>" }.join |
| 37 | 37 | end |
| 38 | 38 | |
| 39 | + def get_latest_version(opts) | |
| 40 | + doc = fetch_doc('https://dojotoolkit.org/api/', opts) | |
| 41 | + doc.at_css('#versionSelector > option[selected]').content | |
| 42 | + end | |
| 43 | + | |
| 39 | 44 | private |
| 40 | 45 | |
| 41 | 46 | def get_url_list(json, set = Set.new) |
| @@ -98,5 +98,10 @@ module Docs · 97 unmodified lines | ||
| 98 | 98 | /\A[\w\-\.]+\.php\/7\.x\z/ |
| 99 | 99 | ] |
| 100 | 100 | end |
| 101 | + | |
| 102 | + def get_latest_version(opts) | |
| 103 | + json = fetch_json('https://packagist.org/packages/drupal/drupal.json', opts) | |
| 104 | + json['package']['versions'].keys.find {|version| !version.end_with?('-dev')} | |
| 105 | + end | |
| 101 | 106 | end |
| 102 | 107 | end |
| @@ -1,26 +1,31 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Electron < UrlScraper |
| 3 | − self.type = 'electron' | |
| 4 | − self.base_url = 'https://electron.atom.io/docs/' | |
| 5 | − self.release = '1.7.5' | |
| 3 | + self.type = 'simple' | |
| 4 | + self.base_url = 'https://electronjs.org/docs' | |
| 5 | + self.release = '2.0.2' | |
| 6 | 6 | self.links = { |
| 7 | − home: 'https://electron.atom.io/', | |
| 7 | + home: 'https://electronjs.org/', | |
| 8 | 8 | code: 'https://github.com/electron/electron' |
| 9 | 9 | } |
| 10 | 10 | |
| 11 | 11 | html_filters.push 'electron/clean_html', 'electron/entries' |
| 12 | 12 | |
| 13 | − options[:trailing_slash] = true | |
| 13 | + options[:trailing_slash] = false | |
| 14 | 14 | options[:container] = '.page-section > .container, .page-section > .container-narrow' |
| 15 | − options[:skip] = %w(guides/ development/ tutorial/ versions/ all/) | |
| 15 | + options[:skip] = %w(guides development tutorial versions all) | |
| 16 | + options[:skip_patterns] = [/\/history\z/] | |
| 16 | 17 | options[:replace_paths] = { |
| 17 | − 'api/web-view-tag/' => 'api/webview-tag/', | |
| 18 | − 'api/web-view-tag' => 'api/webview-tag/' | |
| 18 | + 'api/web-view-tag' => 'api/webview-tag' | |
| 19 | 19 | } |
| 20 | 20 | |
| 21 | 21 | options[:attribution] = <<-HTML |
| 22 | − © 2013–2017 GitHub Inc.<br> | |
| 22 | + © 2013–2018 GitHub Inc.<br> | |
| 23 | 23 | Licensed under the MIT license. |
| 24 | 24 | HTML |
| 25 | + | |
| 26 | + def get_latest_version(opts) | |
| 27 | + doc = fetch_doc('https://electronjs.org/docs', opts) | |
| 28 | + doc.at_css('.docs-version').content | |
| 29 | + end | |
| 25 | 30 | end |
| 26 | 31 | end |
| @@ -19,7 +19,7 @@ module Docs · 18 unmodified lines | ||
| 19 | 19 | options[:root_title] = 'Elixir' |
| 20 | 20 | |
| 21 | 21 | options[:attribution] = <<-HTML |
| 22 | − © 2012–2017 Plataformatec<br> | |
| 22 | + © 2012 Plataformatec<br> | |
| 23 | 23 | Licensed under the Apache License, Version 2.0. |
| 24 | 24 | HTML |
| 25 | 25 | |
| @@ -33,8 +33,60 @@ module Docs · 7 unmodified lines | ||
| 33 | 33 | "https://elixir-lang.org/getting-started/introduction.html" ] |
| 34 | 34 | end |
| 35 | 35 | |
| 36 | + version '1.9' do | |
| 37 | + self.release = '1.9.1' | |
| 38 | + self.base_urls = [ | |
| 39 | + "https://hexdocs.pm/elixir/#{release}/", | |
| 40 | + "https://hexdocs.pm/eex/#{release}/", | |
| 41 | + "https://hexdocs.pm/ex_unit/#{release}/", | |
| 42 | + "https://hexdocs.pm/iex/#{release}/", | |
| 43 | + "https://hexdocs.pm/logger/#{release}/", | |
| 44 | + "https://hexdocs.pm/mix/#{release}/", | |
| 45 | + 'https://elixir-lang.org/getting-started/' | |
| 46 | + ] | |
| 47 | + end | |
| 48 | + | |
| 49 | + version '1.8' do | |
| 50 | + self.release = '1.8.2' | |
| 51 | + self.base_urls = [ | |
| 52 | + "https://hexdocs.pm/elixir/#{release}/", | |
| 53 | + "https://hexdocs.pm/eex/#{release}/", | |
| 54 | + "https://hexdocs.pm/ex_unit/#{release}/", | |
| 55 | + "https://hexdocs.pm/iex/#{release}/", | |
| 56 | + "https://hexdocs.pm/logger/#{release}/", | |
| 57 | + "https://hexdocs.pm/mix/#{release}/", | |
| 58 | + 'https://elixir-lang.org/getting-started/' | |
| 59 | + ] | |
| 60 | + end | |
| 61 | + | |
| 62 | + version '1.7' do | |
| 63 | + self.release = '1.7.4' | |
| 64 | + self.base_urls = [ | |
| 65 | + "https://hexdocs.pm/elixir/#{release}/", | |
| 66 | + "https://hexdocs.pm/eex/#{release}/", | |
| 67 | + "https://hexdocs.pm/ex_unit/#{release}/", | |
| 68 | + "https://hexdocs.pm/iex/#{release}/", | |
| 69 | + "https://hexdocs.pm/logger/#{release}/", | |
| 70 | + "https://hexdocs.pm/mix/#{release}/", | |
| 71 | + 'https://elixir-lang.org/getting-started/' | |
| 72 | + ] | |
| 73 | + end | |
| 74 | + | |
| 75 | + version '1.6' do | |
| 76 | + self.release = '1.6.6' | |
| 77 | + self.base_urls = [ | |
| 78 | + "https://hexdocs.pm/elixir/#{release}/", | |
| 79 | + "https://hexdocs.pm/eex/#{release}/", | |
| 80 | + "https://hexdocs.pm/ex_unit/#{release}/", | |
| 81 | + "https://hexdocs.pm/iex/#{release}/", | |
| 82 | + "https://hexdocs.pm/logger/#{release}/", | |
| 83 | + "https://hexdocs.pm/mix/#{release}/", | |
| 84 | + 'https://elixir-lang.org/getting-started/' | |
| 85 | + ] | |
| 86 | + end | |
| 87 | + | |
| 36 | 88 | version '1.5' do |
| 37 | − self.release = '1.5.2' | |
| 89 | + self.release = '1.5.3' | |
| 38 | 90 | self.base_urls = [ |
| 39 | 91 | "https://hexdocs.pm/elixir/#{release}/", |
| 40 | 92 | "https://hexdocs.pm/eex/#{release}/", |
| @@ -71,5 +123,10 @@ module Docs · 30 unmodified lines | ||
| 71 | 123 | 'https://elixir-lang.org/getting-started/' |
| 72 | 124 | ] |
| 73 | 125 | end |
| 126 | + | |
| 127 | + def get_latest_version(opts) | |
| 128 | + doc = fetch_doc('https://hexdocs.pm/elixir/api-reference.html', opts) | |
| 129 | + doc.at_css('h2.sidebar-projectVersion').content.strip[1..-1] | |
| 130 | + end | |
| 74 | 131 | end |
| 75 | 132 | end |
| @@ -56,5 +56,10 @@ module Docs · 55 unmodified lines | ||
| 56 | 56 | https://emberjs.com/api/ember-data/2.14/classes/DS |
| 57 | 57 | ) |
| 58 | 58 | end |
| 59 | + | |
| 60 | + def get_latest_version(opts) | |
| 61 | + doc = fetch_doc('https://emberjs.com/api/ember/release', opts) | |
| 62 | + doc.at_css('.sidebar > .select-container .ember-power-select-selected-item').content.strip | |
| 63 | + end | |
| 59 | 64 | end |
| 60 | 65 | end |
| @@ -40,19 +40,25 @@ module Docs · 39 unmodified lines | ||
| 40 | 40 | Licensed under the Apache License, Version 2.0. |
| 41 | 41 | HTML |
| 42 | 42 | |
| 43 | + version '21' do | |
| 44 | + self.release = '21.0' | |
| 45 | + end | |
| 46 | + | |
| 43 | 47 | version '20' do |
| 44 | − self.release = '20.1' | |
| 45 | − self.dir = '/Users/Thibaut/DevDocs/Docs/Erlang20' | |
| 48 | + self.release = '20.3' | |
| 46 | 49 | end |
| 47 | 50 | |
| 48 | 51 | version '19' do |
| 49 | 52 | self.release = '19.3' |
| 50 | − self.dir = '/Users/Thibaut/DevDocs/Docs/Erlang19' | |
| 51 | 53 | end |
| 52 | 54 | |
| 53 | 55 | version '18' do |
| 54 | 56 | self.release = '18.3' |
| 55 | − self.dir = '/Users/Thibaut/DevDocs/Docs/Erlang18' | |
| 57 | + end | |
| 58 | + | |
| 59 | + def get_latest_version(opts) | |
| 60 | + doc = fetch_doc('https://www.erlang.org/downloads', opts) | |
| 61 | + doc.at_css('.col-lg-3 > ul > li').content.strip.sub(/OTP /, '') | |
| 56 | 62 | end |
| 57 | 63 | end |
| 58 | 64 | end |
| @@ -0,0 +1,28 @@ | ||
| 1 | +module Docs | |
| 2 | + class Eslint < UrlScraper | |
| 3 | + self.name = 'ESLint' | |
| 4 | + self.type = 'simple' | |
| 5 | + self.release = '5.8.0' | |
| 6 | + self.base_url = 'https://eslint.org/docs/' | |
| 7 | + self.root_path = 'user-guide/getting-started' | |
| 8 | + self.links = { | |
| 9 | + home: 'https://eslint.org/', | |
| 10 | + code: 'https://github.com/eslint/eslint' | |
| 11 | + } | |
| 12 | + | |
| 13 | + html_filters.push 'eslint/entries', 'eslint/clean_html' | |
| 14 | + | |
| 15 | + options[:skip_patterns] = [/maintainer-guide/] | |
| 16 | + options[:skip] = %w(about about/ rules) | |
| 17 | + options[:replace_paths] = { 'user-guide' => 'user-guide/' } | |
| 18 | + | |
| 19 | + options[:attribution] = <<-HTML | |
| 20 | + © JS Foundation and other contributors<br> | |
| 21 | + Licensed under the MIT License. | |
| 22 | + HTML | |
| 23 | + | |
| 24 | + def get_latest_version(opts) | |
| 25 | + get_npm_version('eslint', opts) | |
| 26 | + end | |
| 27 | + end | |
| 28 | +end | |
| @@ -2,7 +2,7 @@ module Docs · 1 unmodified line | ||
| 2 | 2 | class Express < UrlScraper |
| 3 | 3 | self.name = 'Express' |
| 4 | 4 | self.type = 'express' |
| 5 | − self.release = '4.16.1' | |
| 5 | + self.release = '4.16.3' | |
| 6 | 6 | self.base_url = 'http://expressjs.com/en/' |
| 7 | 7 | self.root_path = '4x/api.html' |
| 8 | 8 | self.initial_paths = %w( |
| @@ -28,5 +28,9 @@ module Docs · 19 unmodified lines | ||
| 28 | 28 | © 2017 StrongLoop, IBM, and other expressjs.com contributors.<br> |
| 29 | 29 | Licensed under the Creative Commons Attribution-ShareAlike License v3.0. |
| 30 | 30 | HTML |
| 31 | + | |
| 32 | + def get_latest_version(opts) | |
| 33 | + get_npm_version('express', opts) | |
| 34 | + end | |
| 31 | 35 | end |
| 32 | 36 | end |
| @@ -15,10 +15,15 @@ module Docs · 14 unmodified lines | ||
| 15 | 15 | options[:skip_patterns] = [/\Achanges/, /\A_modules/, /\Acommunity/] |
| 16 | 16 | |
| 17 | 17 | options[:attribution] = <<-HTML |
| 18 | − © 2012–2016 by Rackspace Hosting, Inc. and other contributors<br> | |
| 18 | + © 2012–2017 by Rackspace Hosting, Inc. and other contributors<br> | |
| 19 | 19 | Licensed under the Apache License, Version 2.0. |
| 20 | 20 | HTML |
| 21 | 21 | |
| 22 | + version '1.4' do | |
| 23 | + self.release = '1.4.1' | |
| 24 | + self.base_url = "https://falcon.readthedocs.io/en/#{self.release}/" | |
| 25 | + end | |
| 26 | + | |
| 22 | 27 | version '1.3' do |
| 23 | 28 | self.release = '1.3.0' |
| 24 | 29 | self.base_url = "https://falcon.readthedocs.io/en/#{self.release}/" |
| @@ -28,5 +33,10 @@ module Docs · 3 unmodified lines | ||
| 28 | 33 | self.release = '1.2.0' |
| 29 | 34 | self.base_url = "https://falcon.readthedocs.io/en/#{self.release}/" |
| 30 | 35 | end |
| 36 | + | |
| 37 | + def get_latest_version(opts) | |
| 38 | + doc = fetch_doc('https://falcon.readthedocs.io/en/stable/changes/index.html', opts) | |
| 39 | + doc.at_css('#changelogs ul > li > a').content | |
| 40 | + end | |
| 31 | 41 | end |
| 32 | 42 | end |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Fish < UrlScraper |
| 3 | 3 | self.name = 'Fish' |
| 4 | − self.type = 'fish' | |
| 4 | + self.type = 'simple' | |
| 5 | 5 | self.root_path = 'index.html' |
| 6 | 6 | self.links = { |
| 7 | 7 | home: 'https://fishshell.com/', |
| @@ -12,11 +12,22 @@ module Docs · 4 unmodified lines | ||
| 12 | 12 | |
| 13 | 13 | options[:skip] = %w(design.html license.html) |
| 14 | 14 | |
| 15 | + # https://fishshell.com/docs/current/license.html | |
| 15 | 16 | options[:attribution] = <<-HTML |
| 16 | 17 | © 2005–2009 Axel Liljencrantz<br> |
| 17 | 18 | Licensed under the GNU General Public License, version 2. |
| 18 | 19 | HTML |
| 19 | 20 | |
| 21 | + version '3.0' do | |
| 22 | + self.release = '3.0.1' | |
| 23 | + self.base_url = "https://fishshell.com/docs/#{version}/" | |
| 24 | + end | |
| 25 | + | |
| 26 | + version '2.7' do | |
| 27 | + self.release = '2.7.1' | |
| 28 | + self.base_url = "https://fishshell.com/docs/#{version}/" | |
| 29 | + end | |
| 30 | + | |
| 20 | 31 | version '2.6' do |
| 21 | 32 | self.release = '2.6.0' |
| 22 | 33 | self.base_url = "https://fishshell.com/docs/#{version}/" |
| @@ -41,5 +52,10 @@ module Docs · 18 unmodified lines | ||
| 41 | 52 | self.release = '2.2.0' |
| 42 | 53 | self.base_url = "https://fishshell.com/docs/#{version}/" |
| 43 | 54 | end |
| 55 | + | |
| 56 | + def get_latest_version(opts) | |
| 57 | + doc = fetch_doc('http://fishshell.com/docs/current/index.html', opts) | |
| 58 | + doc.at_css('#toc-index').content.scan(/([0-9.]+)/)[0][0] | |
| 59 | + end | |
| 44 | 60 | end |
| 45 | 61 | end |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Flow < UrlScraper |
| 3 | − self.type = 'flow' | |
| 4 | − self.release = '0.56.0' | |
| 3 | + self.type = 'simple' | |
| 4 | + self.release = '0.85.0' | |
| 5 | 5 | self.base_url = 'https://flow.org/en/docs/' |
| 6 | 6 | self.links = { |
| 7 | 7 | home: 'https://flow.org/', |
| @@ -16,7 +16,11 @@ module Docs · 8 unmodified lines | ||
| 16 | 16 | |
| 17 | 17 | options[:attribution] = <<-HTML |
| 18 | 18 | © 2013–present Facebook Inc.<br> |
| 19 | − Licensed under the BSD License. | |
| 19 | + Licensed under the MIT License. | |
| 20 | 20 | HTML |
| 21 | + | |
| 22 | + def get_latest_version(opts) | |
| 23 | + get_npm_version('flow-bin', opts) | |
| 24 | + end | |
| 21 | 25 | end |
| 22 | 26 | end |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Git < UrlScraper |
| 3 | 3 | self.type = 'git' |
| 4 | − self.release = '2.14.1' | |
| 4 | + self.release = '2.19.1' | |
| 5 | 5 | self.base_url = 'https://git-scm.com/docs' |
| 6 | 6 | self.initial_paths = %w(/git.html) |
| 7 | 7 | self.links = { |
| @@ -16,8 +16,13 @@ module Docs · 8 unmodified lines | ||
| 16 | 16 | options[:skip] = %w(/howto-index.html) |
| 17 | 17 | |
| 18 | 18 | options[:attribution] = <<-HTML |
| 19 | − © 2005–2017 Linus Torvalds and others<br> | |
| 19 | + © 2005–2018 Linus Torvalds and others<br> | |
| 20 | 20 | Licensed under the GNU General Public License version 2. |
| 21 | 21 | HTML |
| 22 | + | |
| 23 | + def get_latest_version(opts) | |
| 24 | + doc = fetch_doc('https://git-scm.com/', opts) | |
| 25 | + doc.at_css('.version').content.strip | |
| 26 | + end | |
| 22 | 27 | end |
| 23 | 28 | end |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Gnu < FileScraper |
| 3 | − self.type = 'gnu' | |
| 3 | + self.type = 'simple' | |
| 4 | 4 | self.root_path = 'index.html' |
| 5 | 5 | self.abstract = 'true' |
| 6 | 6 | |
| @@ -47,14 +47,12 @@ module Docs · 46 unmodified lines | ||
| 47 | 47 | } |
| 48 | 48 | |
| 49 | 49 | version '7' do |
| 50 | − self.release = '7.2.0' | |
| 51 | − self.dir = '/Users/Thibaut/DevDocs/Docs/gcc7' | |
| 50 | + self.release = '7.3.0' | |
| 52 | 51 | self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/gcc/" |
| 53 | 52 | end |
| 54 | 53 | |
| 55 | 54 | version '7 CPP' do |
| 56 | − self.release = '7.2.0' | |
| 57 | − self.dir = '/Users/Thibaut/DevDocs/Docs/gcpp7' | |
| 55 | + self.release = '7.3.0' | |
| 58 | 56 | self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/cpp/" |
| 59 | 57 | |
| 60 | 58 | options[:replace_paths] = CPP_PATHS |
| @@ -62,7 +60,6 @@ module Docs · 1 unmodified line | ||
| 62 | 60 | |
| 63 | 61 | version '6' do |
| 64 | 62 | self.release = '6.4.0' |
| 65 | − self.dir = '/Users/Thibaut/DevDocs/Docs/gcc6' | |
| 66 | 63 | self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/gcc/" |
| 67 | 64 | |
| 68 | 65 | options[:root_title] = 'Using the GNU Compiler Collection (GCC)' |
| @@ -70,7 +67,6 @@ module Docs · 1 unmodified line | ||
| 70 | 67 | |
| 71 | 68 | version '6 CPP' do |
| 72 | 69 | self.release = '6.4.0' |
| 73 | − self.dir = '/Users/Thibaut/DevDocs/Docs/gcpp6' | |
| 74 | 70 | self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/cpp/" |
| 75 | 71 | |
| 76 | 72 | options[:replace_paths] = CPP_PATHS |
| @@ -78,7 +74,6 @@ module Docs · 1 unmodified line | ||
| 78 | 74 | |
| 79 | 75 | version '5' do |
| 80 | 76 | self.release = '5.4.0' |
| 81 | − self.dir = '/Users/Thibaut/DevDocs/Docs/gcc5' | |
| 82 | 77 | self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/gcc/" |
| 83 | 78 | |
| 84 | 79 | options[:root_title] = 'Using the GNU Compiler Collection (GCC)' |
| @@ -86,7 +81,6 @@ module Docs · 1 unmodified line | ||
| 86 | 81 | |
| 87 | 82 | version '5 CPP' do |
| 88 | 83 | self.release = '5.4.0' |
| 89 | − self.dir = '/Users/Thibaut/DevDocs/Docs/gcpp5' | |
| 90 | 84 | self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/cpp/" |
| 91 | 85 | |
| 92 | 86 | options[:replace_paths] = CPP_PATHS |
| @@ -94,7 +88,6 @@ module Docs · 1 unmodified line | ||
| 94 | 88 | |
| 95 | 89 | version '4' do |
| 96 | 90 | self.release = '4.9.3' |
| 97 | − self.dir = '/Users/Thibaut/DevDocs/Docs/gcc4' | |
| 98 | 91 | self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/gcc/" |
| 99 | 92 | |
| 100 | 93 | options[:root_title] = 'Using the GNU Compiler Collection (GCC)' |
| @@ -102,10 +95,15 @@ module Docs · 1 unmodified line | ||
| 102 | 95 | |
| 103 | 96 | version '4 CPP' do |
| 104 | 97 | self.release = '4.9.3' |
| 105 | − self.dir = '/Users/Thibaut/DevDocs/Docs/gcpp4' | |
| 106 | 98 | self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/cpp/" |
| 107 | 99 | |
| 108 | 100 | options[:replace_paths] = CPP_PATHS |
| 109 | 101 | end |
| 102 | + | |
| 103 | + def get_latest_version(opts) | |
| 104 | + doc = fetch_doc('https://gcc.gnu.org/onlinedocs/', opts) | |
| 105 | + label = doc.at_css('ul > li > ul > li > a').content.strip | |
| 106 | + label.scan(/([0-9.]+)/)[0][0] | |
| 107 | + end | |
| 110 | 108 | end |
| 111 | 109 | end |
| @@ -7,27 +7,29 @@ module Docs · 6 unmodified lines | ||
| 7 | 7 | } |
| 8 | 8 | |
| 9 | 9 | version '7' do |
| 10 | − self.release = '7.2.0' | |
| 11 | − self.dir = '/Users/Thibaut/DevDocs/Docs/gfortran7' | |
| 10 | + self.release = '7.3.0' | |
| 12 | 11 | self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/gfortran/" |
| 13 | 12 | end |
| 14 | 13 | |
| 15 | 14 | version '6' do |
| 16 | 15 | self.release = '6.4.0' |
| 17 | − self.dir = '/Users/Thibaut/DevDocs/Docs/gfortran6' | |
| 18 | 16 | self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/gfortran/" |
| 19 | 17 | end |
| 20 | 18 | |
| 21 | 19 | version '5' do |
| 22 | 20 | self.release = '5.4.0' |
| 23 | − self.dir = '/Users/Thibaut/DevDocs/Docs/gfortran5' | |
| 24 | 21 | self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/gfortran/" |
| 25 | 22 | end |
| 26 | 23 | |
| 27 | 24 | version '4' do |
| 28 | 25 | self.release = '4.9.3' |
| 29 | − self.dir = '/Users/Thibaut/DevDocs/Docs/gfortran4' | |
| 30 | 26 | self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/gfortran/" |
| 31 | 27 | end |
| 28 | + | |
| 29 | + def get_latest_version(opts) | |
| 30 | + doc = fetch_doc('https://gcc.gnu.org/onlinedocs/', opts) | |
| 31 | + label = doc.at_css('ul > li > ul > li > a').content.strip | |
| 32 | + label.scan(/([0-9.]+)/)[0][0] | |
| 33 | + end | |
| 32 | 34 | end |
| 33 | 35 | end |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Go < UrlScraper |
| 3 | 3 | self.type = 'go' |
| 4 | − self.release = '1.9.0' | |
| 4 | + self.release = '1.12' | |
| 5 | 5 | self.base_url = 'https://golang.org/pkg/' |
| 6 | 6 | self.links = { |
| 7 | 7 | home: 'https://golang.org/', |
| @@ -13,12 +13,22 @@ module Docs · 5 unmodified lines | ||
| 13 | 13 | options[:trailing_slash] = true |
| 14 | 14 | options[:container] = '#page .container' |
| 15 | 15 | options[:skip] = %w(runtime/msan/) |
| 16 | + options[:skip_patterns] = [/\/\//] | |
| 17 | + | |
| 18 | + options[:fix_urls] = ->(url) do | |
| 19 | + url.sub 'https://golang.org/pkg//', 'https://golang.org/pkg/' | |
| 20 | + end | |
| 16 | 21 | |
| 17 | 22 | options[:attribution] = <<-HTML |
| 18 | 23 | © Google, Inc.<br> |
| 19 | 24 | Licensed under the Creative Commons Attribution License 3.0. |
| 20 | 25 | HTML |
| 21 | 26 | |
| 27 | + def get_latest_version(opts) | |
| 28 | + doc = fetch_doc('https://golang.org/project/', opts) | |
| 29 | + doc.at_css('#page ul > li > a').text[3..-1] | |
| 30 | + end | |
| 31 | + | |
| 22 | 32 | private |
| 23 | 33 | |
| 24 | 34 | def parse(response) # Hook here because Nokogori removes whitespace from textareas |
| @@ -17,20 +17,30 @@ module Docs · 16 unmodified lines | ||
| 17 | 17 | options[:attribution] = ->(filter) do |
| 18 | 18 | if filter.subpath.start_with?('classes') |
| 19 | 19 | <<-HTML |
| 20 | − © 2014–2017 Juan Linietsky, Ariel Manzur, Godot Engine contributors<br> | |
| 20 | + © 2014–2018 Juan Linietsky, Ariel Manzur, Godot Engine contributors<br> | |
| 21 | 21 | Licensed under the MIT License. |
| 22 | 22 | HTML |
| 23 | 23 | else |
| 24 | 24 | <<-HTML |
| 25 | − © 2014–2017 Juan Linietsky, Ariel Manzur and the Godot community<br> | |
| 25 | + © 2014–2018 Juan Linietsky, Ariel Manzur and the Godot community<br> | |
| 26 | 26 | Licensed under the Creative Commons Attribution Unported License v3.0. |
| 27 | 27 | HTML |
| 28 | 28 | end |
| 29 | 29 | end |
| 30 | 30 | |
| 31 | + version '3.0' do | |
| 32 | + self.release = '3.0.6' | |
| 33 | + self.base_url = "http://docs.godotengine.org/en/#{self.version}/" | |
| 34 | + end | |
| 35 | + | |
| 31 | 36 | version '2.1' do |
| 32 | 37 | self.release = '2.1' |
| 33 | 38 | self.base_url = "http://docs.godotengine.org/en/#{self.version}/" |
| 34 | 39 | end |
| 40 | + | |
| 41 | + def get_latest_version(opts) | |
| 42 | + doc = fetch_doc('https://docs.godotengine.org/', opts) | |
| 43 | + doc.at_css('.version').content.strip | |
| 44 | + end | |
| 35 | 45 | end |
| 36 | 46 | end |
| @@ -0,0 +1,26 @@ | ||
| 1 | +module Docs | |
| 2 | + class Graphite < UrlScraper | |
| 3 | + self.type = 'graphite' | |
| 4 | + self.release = '1.1.4' | |
| 5 | + self.base_url = 'https://graphite.readthedocs.io/en/latest/' | |
| 6 | + self.links = { | |
| 7 | + code: 'https://github.com/graphite-project/graphite-web' | |
| 8 | + } | |
| 9 | + | |
| 10 | + html_filters.push 'graphite/entries', 'graphite/clean_html' | |
| 11 | + | |
| 12 | + options[:container] = '.document > div' | |
| 13 | + options[:skip] = %w(releases.html who-is-using.html composer.html search.html py-modindex.html genindex.html) | |
| 14 | + | |
| 15 | + options[:attribution] = <<-HTML | |
| 16 | + © 2008–2012 Chris Davis<br> | |
| 17 | + © 2011–2016 The Graphite Project<br> | |
| 18 | + Licensed under the Apache License, Version 2.0. | |
| 19 | + HTML | |
| 20 | + | |
| 21 | + def get_latest_version(opts) | |
| 22 | + doc = fetch_doc('https://graphite.readthedocs.io/en/latest/releases.html', opts) | |
| 23 | + doc.at_css('#release-notes li > a').content | |
| 24 | + end | |
| 25 | + end | |
| 26 | +end | |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Grunt < UrlScraper |
| 3 | 3 | self.name = 'Grunt' |
| 4 | − self.type = 'grunt' | |
| 4 | + self.type = 'simple' | |
| 5 | 5 | self.release = '1.0.1' |
| 6 | 6 | self.base_url = 'https://gruntjs.com/' |
| 7 | 7 | self.root_path = 'getting-started' |
| @@ -26,5 +26,9 @@ module Docs · 18 unmodified lines | ||
| 26 | 26 | © GruntJS Team<br> |
| 27 | 27 | Licensed under the MIT License. |
| 28 | 28 | HTML |
| 29 | + | |
| 30 | + def get_latest_version(opts) | |
| 31 | + get_npm_version('grunt-cli', opts) | |
| 32 | + end | |
| 29 | 33 | end |
| 30 | 34 | end |
| @@ -0,0 +1,27 @@ | ||
| 1 | +module Docs | |
| 2 | + class Handlebars < UrlScraper | |
| 3 | + self.name = 'Handlebars.js' | |
| 4 | + self.slug = 'handlebars' | |
| 5 | + self.type = 'simple' | |
| 6 | + self.release = '4.0.12' | |
| 7 | + self.base_url = 'https://handlebarsjs.com/' | |
| 8 | + self.links = { | |
| 9 | + home: 'https://handlebarsjs.com/', | |
| 10 | + code: 'https://github.com/wycats/handlebars.js/' | |
| 11 | + } | |
| 12 | + | |
| 13 | + html_filters.push 'handlebars/entries', 'handlebars/clean_html', 'title' | |
| 14 | + | |
| 15 | + options[:container] = '#contents' | |
| 16 | + options[:root_title] = 'Handlebars.js' | |
| 17 | + | |
| 18 | + options[:attribution] = <<-HTML | |
| 19 | + © 2011–2017 by Yehuda Katz<br> | |
| 20 | + Licensed under the MIT License. | |
| 21 | + HTML | |
| 22 | + | |
| 23 | + def get_latest_version(opts) | |
| 24 | + get_npm_version('handlebars', opts) | |
| 25 | + end | |
| 26 | + end | |
| 27 | +end | |
| @@ -10,7 +10,7 @@ module Docs · 9 unmodified lines | ||
| 10 | 10 | |
| 11 | 11 | html_filters.push 'haskell/entries', 'haskell/clean_html' |
| 12 | 12 | |
| 13 | − options[:container] = ->(filter) { filter.subpath.start_with?('users_guide') ? '.body' : '#content' } | |
| 13 | + options[:container] = ->(filter) {filter.subpath.start_with?('users_guide') ? '.body' : '#content'} | |
| 14 | 14 | |
| 15 | 15 | options[:only_patterns] = [/\Alibraries\//, /\Ausers_guide\//] |
| 16 | 16 | options[:skip_patterns] = [ |
| @@ -57,7 +57,7 @@ module Docs · 40 unmodified lines | ||
| 57 | 57 | end |
| 58 | 58 | |
| 59 | 59 | version '8' do |
| 60 | − self.release = '8.2.1' | |
| 60 | + self.release = '8.6.1' | |
| 61 | 61 | self.base_url = "https://downloads.haskell.org/~ghc/#{release}/docs/html/" |
| 62 | 62 | end |
| 63 | 63 | |
| @@ -68,5 +68,12 @@ module Docs · 4 unmodified lines | ||
| 68 | 68 | |
| 69 | 69 | options[:only_patterns] = [/\Alibraries\//] |
| 70 | 70 | end |
| 71 | + | |
| 72 | + def get_latest_version(opts) | |
| 73 | + doc = fetch_doc('https://downloads.haskell.org/~ghc/latest/docs/html/', opts) | |
| 74 | + links = doc.css('a').to_a | |
| 75 | + versions = links.map {|link| link['href'].scan(/ghc-([0-9.]+)/)} | |
| 76 | + versions.find {|version| !version.empty?}[0][0] | |
| 77 | + end | |
| 71 | 78 | end |
| 72 | 79 | end |
| @@ -1,58 +1,76 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Haxe < UrlScraper |
| 3 | 3 | self.name = 'Haxe' |
| 4 | − self.type = 'haxe' | |
| 5 | − self.release = '3.4.1' | |
| 6 | − self.base_url = 'http://api.haxe.org/' | |
| 4 | + self.type = 'simple' | |
| 5 | + self.release = '3.4.7' | |
| 6 | + self.base_url = 'https://api.haxe.org/' | |
| 7 | 7 | |
| 8 | 8 | html_filters.push 'haxe/clean_html', 'haxe/entries' |
| 9 | 9 | |
| 10 | 10 | options[:container] = '.span9' |
| 11 | 11 | |
| 12 | 12 | options[:attribution] = <<-HTML |
| 13 | − © 2005–2016 Haxe Foundation<br> | |
| 13 | + © 2005–2018 Haxe Foundation<br> | |
| 14 | 14 | Licensed under a MIT license. |
| 15 | 15 | HTML |
| 16 | 16 | |
| 17 | 17 | version do |
| 18 | 18 | self.links = { |
| 19 | − home: 'http://haxe.org', | |
| 19 | + home: 'https://haxe.org', | |
| 20 | 20 | code: 'https://github.com/HaxeFoundation/haxe' |
| 21 | 21 | } |
| 22 | 22 | |
| 23 | − options[:skip_patterns] = [/\A(?:cpp|cs|flash|java|js|neko|php|python)/i] | |
| 23 | + options[:skip_patterns] = [/\A(?:cpp|cs|flash|java|js|neko|php|python|lua|hl|sys)/i] | |
| 24 | 24 | end |
| 25 | 25 | |
| 26 | 26 | version 'C++' do |
| 27 | − self.base_url = 'http://api.haxe.org/cpp/' | |
| 27 | + self.base_url = 'https://api.haxe.org/cpp/' | |
| 28 | 28 | end |
| 29 | 29 | |
| 30 | 30 | version 'C#' do |
| 31 | − self.base_url = 'http://api.haxe.org/cs/' | |
| 31 | + self.base_url = 'https://api.haxe.org/cs/' | |
| 32 | 32 | end |
| 33 | 33 | |
| 34 | 34 | version 'Flash' do |
| 35 | − self.base_url = 'http://api.haxe.org/flash/' | |
| 35 | + self.base_url = 'https://api.haxe.org/flash/' | |
| 36 | 36 | end |
| 37 | 37 | |
| 38 | 38 | version 'Java' do |
| 39 | − self.base_url = 'http://api.haxe.org/java/' | |
| 39 | + self.base_url = 'https://api.haxe.org/java/' | |
| 40 | 40 | end |
| 41 | 41 | |
| 42 | 42 | version 'JavaScript' do |
| 43 | − self.base_url = 'http://api.haxe.org/js/' | |
| 43 | + self.base_url = 'https://api.haxe.org/js/' | |
| 44 | 44 | end |
| 45 | 45 | |
| 46 | 46 | version 'Neko' do |
| 47 | − self.base_url = 'http://api.haxe.org/neko/' | |
| 47 | + self.base_url = 'https://api.haxe.org/neko/' | |
| 48 | 48 | end |
| 49 | 49 | |
| 50 | 50 | version 'PHP' do |
| 51 | − self.base_url = 'http://api.haxe.org/php/' | |
| 51 | + self.base_url = 'https://api.haxe.org/php/' | |
| 52 | + end | |
| 53 | + | |
| 54 | + version 'Lua' do | |
| 55 | + self.base_url = 'https://api.haxe.org/lua/' | |
| 56 | + end | |
| 57 | + | |
| 58 | + version 'HashLink' do | |
| 59 | + self.base_url = 'https://api.haxe.org/hl/' | |
| 60 | + end | |
| 61 | + | |
| 62 | + version 'Sys' do | |
| 63 | + self.base_url = 'https://api.haxe.org/sys/' | |
| 52 | 64 | end |
| 53 | 65 | |
| 54 | 66 | version 'Python' do |
| 55 | − self.base_url = 'http://api.haxe.org/python/' | |
| 67 | + self.base_url = 'https://api.haxe.org/python/' | |
| 68 | + end | |
| 69 | + | |
| 70 | + def get_latest_version(opts) | |
| 71 | + doc = fetch_doc('https://api.haxe.org/', opts) | |
| 72 | + label = doc.at_css('.container.main-content h1 > small').content | |
| 73 | + label.sub(/version /, '') | |
| 56 | 74 | end |
| 57 | 75 | end |
| 58 | 76 | end |
| @@ -0,0 +1,27 @@ | ||
| 1 | +module Docs | |
| 2 | + class Homebrew < UrlScraper | |
| 3 | + self.name = 'Homebrew' | |
| 4 | + self.type = 'simple' | |
| 5 | + self.release = '2.1.9' | |
| 6 | + self.base_url = 'https://docs.brew.sh/' | |
| 7 | + self.links = { | |
| 8 | + home: 'https://brew.sh', | |
| 9 | + code: 'https://github.com/Homebrew/brew' | |
| 10 | + } | |
| 11 | + | |
| 12 | + html_filters.push 'homebrew/entries', 'homebrew/clean_html' | |
| 13 | + | |
| 14 | + options[:container] = ->(filter) { filter.root_page? ? '#home' : '#page' } | |
| 15 | + | |
| 16 | + options[:skip_patterns] = [/maintainer/i, /core\-contributor/i, /kickstarter/i] | |
| 17 | + | |
| 18 | + options[:attribution] = <<-HTML | |
| 19 | + © 2009–present Homebrew contributors<br> | |
| 20 | + Licensed under the BSD 2-Clause License. | |
| 21 | + HTML | |
| 22 | + | |
| 23 | + def get_latest_version(opts) | |
| 24 | + get_latest_github_release('Homebrew', 'brew', opts) | |
| 25 | + end | |
| 26 | + end | |
| 27 | +end | |
| @@ -7,10 +7,13 @@ module Docs · 6 unmodified lines | ||
| 7 | 7 | |
| 8 | 8 | html_filters.push 'http/clean_html', 'http/entries', 'title' |
| 9 | 9 | |
| 10 | + options[:mdn_tag] = 'HTTP' | |
| 11 | + | |
| 10 | 12 | options[:root_title] = 'HTTP' |
| 11 | 13 | options[:title] = ->(filter) { filter.current_url.host == 'tools.ietf.org' ? false : filter.default_title } |
| 12 | 14 | options[:container] = ->(filter) { filter.current_url.host == 'tools.ietf.org' ? '.content' : nil } |
| 13 | 15 | options[:skip_links] = ->(filter) { filter.current_url.host == 'tools.ietf.org' ? true : false } |
| 16 | + options[:replace_paths] = { '/Access_control_CORS' => '/CORS' } | |
| 14 | 17 | options[:fix_urls] = ->(url) do |
| 15 | 18 | url.sub! %r{(Status/\d\d\d)_[A-Z].+}, '\1' |
| 16 | 19 | url |
| @@ -2,7 +2,7 @@ module Docs · 1 unmodified line | ||
| 2 | 2 | class Immutable < UrlScraper |
| 3 | 3 | self.name = 'Immutable.js' |
| 4 | 4 | self.slug = 'immutable' |
| 5 | − self.type = 'immutable' | |
| 5 | + self.type = 'simple' | |
| 6 | 6 | self.release = '3.8.1' |
| 7 | 7 | self.base_url = 'https://facebook.github.io/immutable-js/docs/' |
| 8 | 8 | self.links = { |
| @@ -54,5 +54,9 @@ module Docs · 45 unmodified lines | ||
| 54 | 54 | JS |
| 55 | 55 | capybara.html |
| 56 | 56 | end |
| 57 | + | |
| 58 | + def get_latest_version(opts) | |
| 59 | + get_npm_version('immutable', opts) | |
| 60 | + end | |
| 57 | 61 | end |
| 58 | 62 | end |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Influxdata < UrlScraper |
| 3 | 3 | self.name = 'InfluxData' |
| 4 | − self.type = 'influxdata' | |
| 4 | + self.type = 'simple' | |
| 5 | 5 | self.release = '1.3' |
| 6 | 6 | self.base_url = 'https://docs.influxdata.com/' |
| 7 | 7 | self.links = { |
| @@ -46,5 +46,11 @@ module Docs · 38 unmodified lines | ||
| 46 | 46 | © 2015 InfluxData, Inc.<br> |
| 47 | 47 | Licensed under the MIT license. |
| 48 | 48 | HTML |
| 49 | + | |
| 50 | + def get_latest_version(opts) | |
| 51 | + doc = fetch_doc('https://docs.influxdata.com/influxdb/', opts) | |
| 52 | + label = doc.at_css('.navbar--current-product').content.strip | |
| 53 | + label.scan(/([0-9.]+)/)[0][0] | |
| 54 | + end | |
| 49 | 55 | end |
| 50 | 56 | end |
| @@ -1,8 +1,8 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Jasmine < UrlScraper |
| 3 | − self.type = 'jasmine' | |
| 4 | − self.release = '2.8.0' | |
| 5 | − self.base_url = 'https://jasmine.github.io/api/2.7/' | |
| 3 | + self.type = 'simple' | |
| 4 | + self.release = '3.3.0' | |
| 5 | + self.base_url = 'https://jasmine.github.io/api/3.2/' | |
| 6 | 6 | self.root_path = 'index.html' |
| 7 | 7 | self.links = { |
| 8 | 8 | home: 'https://jasmine.github.io/', |
| @@ -17,5 +17,9 @@ module Docs · 8 unmodified lines | ||
| 17 | 17 | © 2008–2017 Pivotal Labs<br> |
| 18 | 18 | Licensed under the MIT License. |
| 19 | 19 | HTML |
| 20 | + | |
| 21 | + def get_latest_version(opts) | |
| 22 | + get_latest_github_release('jasmine', 'jasmine', opts) | |
| 23 | + end | |
| 20 | 24 | end |
| 21 | 25 | end |
| @@ -0,0 +1,37 @@ | ||
| 1 | +module Docs | |
| 2 | + class Jekyll < UrlScraper | |
| 3 | + self.type = 'jekyll' | |
| 4 | + self.release = '3.7.2' | |
| 5 | + self.base_url = 'https://jekyllrb.com/docs/' | |
| 6 | + self.root_path = 'home/' | |
| 7 | + self.links = { | |
| 8 | + home: 'https://jekyllrb.com/', | |
| 9 | + code: 'https://github.com/jekyll/jekyll' | |
| 10 | + } | |
| 11 | + | |
| 12 | + html_filters.push 'jekyll/clean_html', 'jekyll/entries' | |
| 13 | + | |
| 14 | + options[:trailing_slash] = true | |
| 15 | + options[:skip] = %w(sites/ upgrading/) | |
| 16 | + options[:skip_patterns] = [ | |
| 17 | + /conduct/, | |
| 18 | + /history/, | |
| 19 | + /maintaining/, | |
| 20 | + /contributing/, | |
| 21 | + ] | |
| 22 | + options[:replace_paths] = { | |
| 23 | + '' => 'home/', | |
| 24 | + '/' => 'home/' | |
| 25 | + } | |
| 26 | + | |
| 27 | + options[:attribution] = <<-HTML | |
| 28 | + © 2008–2018 Tom Preston-Werner and Jekyll contributors<br> | |
| 29 | + Licensed under the MIT license. | |
| 30 | + HTML | |
| 31 | + | |
| 32 | + def get_latest_version(opts) | |
| 33 | + doc = fetch_doc('https://jekyllrb.com/docs/', opts) | |
| 34 | + doc.at_css('.meta a').content[1..-1] | |
| 35 | + end | |
| 36 | + end | |
| 37 | +end | |
| @@ -1,9 +1,9 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Jest < UrlScraper |
| 3 | − self.type = 'jest' | |
| 4 | − self.release = '21.2.1' | |
| 5 | − self.base_url = 'https://facebook.github.io/jest/docs/en/' | |
| 6 | − self.root_path = 'getting-started.html' | |
| 3 | + self.type = 'simple' | |
| 4 | + self.release = '23.5.0' | |
| 5 | + self.base_url = 'https://jestjs.io/docs/en/' | |
| 6 | + self.root_path = 'getting-started' | |
| 7 | 7 | self.links = { |
| 8 | 8 | home: 'https://facebook.github.io/jest/', |
| 9 | 9 | code: 'https://github.com/facebook/jest' |
| @@ -17,5 +17,10 @@ module Docs · 7 unmodified lines | ||
| 17 | 17 | © 2014–present Facebook Inc.<br> |
| 18 | 18 | Licensed under the BSD License. |
| 19 | 19 | HTML |
| 20 | + | |
| 21 | + def get_latest_version(opts) | |
| 22 | + doc = fetch_doc('https://jestjs.io/docs/en/getting-started', opts) | |
| 23 | + doc.at_css('header > a > h3').content | |
| 24 | + end | |
| 20 | 25 | end |
| 21 | 26 | end |
| @@ -1,9 +1,13 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class JqueryCore < Jquery |
| 3 | 3 | self.name = 'jQuery' |
| 4 | − self.release = 'up to 3.2.1' | |
| 4 | + self.release = '3.3.1' | |
| 5 | 5 | self.base_url = 'https://api.jquery.com/' |
| 6 | 6 | self.initial_paths = %w(/index/index) |
| 7 | + self.links = { | |
| 8 | + home: 'https://jquery.com/', | |
| 9 | + code: 'https://github.com/jquery/jquery' | |
| 10 | + } | |
| 7 | 11 | |
| 8 | 12 | html_filters.insert_before 'jquery/clean_html', 'jquery_core/entries' |
| 9 | 13 | |
| @@ -18,5 +22,9 @@ module Docs · 8 unmodified lines | ||
| 18 | 22 | /Selectors\/odd/i, |
| 19 | 23 | /index/i |
| 20 | 24 | ] |
| 25 | + | |
| 26 | + def get_latest_version(opts) | |
| 27 | + get_npm_version('jquery', opts) | |
| 28 | + end | |
| 21 | 29 | end |
| 22 | 30 | end |
| @@ -16,5 +16,10 @@ module Docs · 15 unmodified lines | ||
| 16 | 16 | options[:fix_urls] = ->(url) do |
| 17 | 17 | url.sub! 'http://api.jquerymobile.com/', 'https://api.jquerymobile.com/' |
| 18 | 18 | end |
| 19 | + | |
| 20 | + def get_latest_version(opts) | |
| 21 | + doc = fetch_doc('https://jquerymobile.com/', opts) | |
| 22 | + doc.at_css('.download-box > .download-option:last-child > span').content.sub(/Version /, '') | |
| 23 | + end | |
| 19 | 24 | end |
| 20 | 25 | end |
| @@ -15,5 +15,9 @@ module Docs · 14 unmodified lines | ||
| 15 | 15 | options[:fix_urls] = ->(url) do |
| 16 | 16 | url.sub! 'http://api.jqueryui.com/', 'https://api.jqueryui.com/' |
| 17 | 17 | end |
| 18 | + | |
| 19 | + def get_latest_version(opts) | |
| 20 | + get_npm_version('jquery-ui', opts) | |
| 21 | + end | |
| 18 | 22 | end |
| 19 | 23 | end |
| @@ -0,0 +1,29 @@ | ||
| 1 | +module Docs | |
| 2 | + class Jsdoc < UrlScraper | |
| 3 | + self.name = 'JSDoc' | |
| 4 | + self.type = 'simple' | |
| 5 | + self.release = '3.5.5' | |
| 6 | + self.base_url = 'http://usejsdoc.org/' | |
| 7 | + self.root_path = 'index.html' | |
| 8 | + self.links = { | |
| 9 | + home: 'http://usejsdoc.org/', | |
| 10 | + code: 'https://github.com/jsdoc3/jsdoc' | |
| 11 | + } | |
| 12 | + | |
| 13 | + html_filters.push 'jsdoc/clean_html', 'jsdoc/entries' | |
| 14 | + | |
| 15 | + options[:trailing_slash] = false | |
| 16 | + options[:container] = 'article' | |
| 17 | + options[:skip] = [ | |
| 18 | + 'about-license-jsdoc3.html' | |
| 19 | + ] | |
| 20 | + options[:attribution] = <<-HTML | |
| 21 | + © 2011–2017 the contributors to the JSDoc 3 documentation project<br> | |
| 22 | + Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0. | |
| 23 | + HTML | |
| 24 | + | |
| 25 | + def get_latest_version(opts) | |
| 26 | + get_latest_github_release('jsdoc3', 'jsdoc', opts) | |
| 27 | + end | |
| 28 | + end | |
| 29 | +end | |
| @@ -8,13 +8,33 @@ module Docs · 7 unmodified lines | ||
| 8 | 8 | options[:only_patterns] = [/\Amanual\//, /\Astdlib\//] |
| 9 | 9 | |
| 10 | 10 | options[:attribution] = <<-HTML |
| 11 | − © 2009–2016 Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors<br> | |
| 11 | + © 2009–2018 Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors<br> | |
| 12 | 12 | Licensed under the MIT License. |
| 13 | 13 | HTML |
| 14 | 14 | |
| 15 | + version '1.0' do | |
| 16 | + self.release = '1.0.1' | |
| 17 | + self.base_url = "https://docs.julialang.org/en/v#{release}/" | |
| 18 | + self.type = 'julia' | |
| 19 | + | |
| 20 | + html_filters.push 'julia/entries', 'julia/clean_html' | |
| 21 | + | |
| 22 | + options[:container] = '#docs' | |
| 23 | + end | |
| 24 | + | |
| 25 | + version '0.7' do | |
| 26 | + self.release = '0.7.0' | |
| 27 | + self.base_url = "https://docs.julialang.org/en/v#{release}/" | |
| 28 | + self.type = 'julia' | |
| 29 | + | |
| 30 | + html_filters.push 'julia/entries', 'julia/clean_html' | |
| 31 | + | |
| 32 | + options[:container] = '#docs' | |
| 33 | + end | |
| 34 | + | |
| 15 | 35 | version '0.6' do |
| 16 | − self.release = '0.6.0' | |
| 17 | − self.base_url = 'https://docs.julialang.org/en/release-0.6/' | |
| 36 | + self.release = '0.6.2' | |
| 37 | + self.base_url = "https://docs.julialang.org/en/v#{release}/" | |
| 18 | 38 | self.type = 'julia' |
| 19 | 39 | |
| 20 | 40 | html_filters.push 'julia/entries', 'julia/clean_html' |
| @@ -24,10 +44,14 @@ module Docs · 3 unmodified lines | ||
| 24 | 44 | |
| 25 | 45 | version '0.5' do |
| 26 | 46 | self.release = '0.5.2' |
| 27 | − self.base_url = 'https://docs.julialang.org/en/release-0.5/' | |
| 47 | + self.base_url = "https://docs.julialang.org/en/v#{release}/" | |
| 28 | 48 | self.type = 'sphinx_simple' |
| 29 | 49 | |
| 30 | 50 | html_filters.push 'julia/entries_sphinx', 'julia/clean_html_sphinx', 'sphinx/clean_html' |
| 31 | 51 | end |
| 52 | + | |
| 53 | + def get_latest_version(opts) | |
| 54 | + get_latest_github_release('JuliaLang', 'julia', opts) | |
| 55 | + end | |
| 32 | 56 | end |
| 33 | 57 | end |
| @@ -33,5 +33,9 @@ module Docs · 32 unmodified lines | ||
| 33 | 33 | © Steven Sanderson, the Knockout.js team, and other contributors<br> |
| 34 | 34 | Licensed under the MIT License. |
| 35 | 35 | HTML |
| 36 | + | |
| 37 | + def get_latest_version(opts) | |
| 38 | + get_latest_github_release('knockout', 'knockout', opts) | |
| 39 | + end | |
| 36 | 40 | end |
| 37 | 41 | end |
| @@ -0,0 +1,42 @@ | ||
| 1 | +# frozen_string_literal: true | |
| 2 | + | |
| 3 | +module Docs | |
| 4 | + class Koa < Github | |
| 5 | + self.base_url = 'https://github.com/koajs/koa/blob/master/docs/' | |
| 6 | + self.release = '2.6.1' | |
| 7 | + | |
| 8 | + self.root_path = 'api/index.md' | |
| 9 | + self.initial_paths = %w[ | |
| 10 | + error-handling | |
| 11 | + faq | |
| 12 | + guide | |
| 13 | + koa-vs-express | |
| 14 | + migration | |
| 15 | + troubleshooting | |
| 16 | + api/index | |
| 17 | + api/context | |
| 18 | + api/request | |
| 19 | + api/response | |
| 20 | + ].map { |name| name + '.md' } | |
| 21 | + | |
| 22 | + self.links = { | |
| 23 | + home: 'https://koajs.com/', | |
| 24 | + code: 'https://github.com/koajs/koa' | |
| 25 | + } | |
| 26 | + | |
| 27 | + html_filters.push 'koa/clean_html', 'koa/entries' | |
| 28 | + | |
| 29 | + options[:skip] = %w[middleware.gif] | |
| 30 | + options[:trailing_slash] = false | |
| 31 | + options[:container] = '.markdown-body' | |
| 32 | + | |
| 33 | + options[:attribution] = <<-HTML | |
| 34 | + © 2018 Koa contributors<br> | |
| 35 | + Licensed under the MIT License. | |
| 36 | + HTML | |
| 37 | + | |
| 38 | + def get_latest_version(opts) | |
| 39 | + get_npm_version('koa', opts) | |
| 40 | + end | |
| 41 | + end | |
| 42 | +end | |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Kotlin < UrlScraper |
| 3 | 3 | self.type = 'kotlin' |
| 4 | − self.release = '1.1.4' | |
| 4 | + self.release = '1.3.41' | |
| 5 | 5 | self.base_url = 'https://kotlinlang.org/' |
| 6 | 6 | self.root_path = 'api/latest/jvm/stdlib/index.html' |
| 7 | 7 | self.links = { |
| @@ -22,11 +22,23 @@ module Docs · 14 unmodified lines | ||
| 22 | 22 | docs/events.html |
| 23 | 23 | docs/resources.html |
| 24 | 24 | docs/reference/grammar.html) |
| 25 | − options[:replace_paths] = { 'api/latest/jvm/stdlib/' => 'api/latest/jvm/stdlib/index.html' } | |
| 25 | + options[:replace_paths] = { | |
| 26 | + 'api/latest/jvm/stdlib/' => 'api/latest/jvm/stdlib/index.html', | |
| 27 | + 'docs/reference/coroutines.html' => 'docs/reference/coroutines-overview.html', | |
| 28 | + 'api/latest/jvm/stdlib/kotlin/fold.html' => 'api/latest/jvm/stdlib/kotlin.collections/fold.html', | |
| 29 | + 'api/latest/jvm/stdlib/kotlin/get-or-else.html' => 'api/latest/jvm/stdlib/kotlin.collections/get-or-else.html', | |
| 30 | + 'api/latest/jvm/stdlib/kotlin/map.html' => 'api/latest/jvm/stdlib/kotlin.collections/map.html', | |
| 31 | + 'docs/tutorials/native/targeting-multiple-platforms.html' => 'docs/tutorials/native/basic-kotlin-native-app.html', | |
| 32 | + 'api/latest/jvm/stdlib/kotlin/-throwable/print-stack-trace.html' => 'api/latest/jvm/stdlib/kotlin/print-stack-trace.html', | |
| 33 | + } | |
| 26 | 34 | |
| 27 | 35 | options[:attribution] = <<-HTML |
| 28 | − © 2010–2017 JetBrains s.r.o.<br> | |
| 36 | + © 2010–2019 JetBrains s.r.o.<br> | |
| 29 | 37 | Licensed under the Apache License, Version 2.0. |
| 30 | 38 | HTML |
| 39 | + | |
| 40 | + def get_latest_version(opts) | |
| 41 | + get_latest_github_release('JetBrains', 'kotlin', opts) | |
| 42 | + end | |
| 31 | 43 | end |
| 32 | 44 | end |
| @@ -29,8 +29,34 @@ module Docs · 28 unmodified lines | ||
| 29 | 29 | Laravel is a trademark of Taylor Otwell. |
| 30 | 30 | HTML |
| 31 | 31 | |
| 32 | + version '5.7' do | |
| 33 | + self.release = '5.7.7' | |
| 34 | + self.root_path = '/api/5.7/index.html' | |
| 35 | + self.initial_paths = %w(/docs/5.7/installation /api/5.7/classes.html) | |
| 36 | + | |
| 37 | + options[:only_patterns] = [%r{\A/api/5\.7/}, %r{\A/docs/5\.7/}] | |
| 38 | + | |
| 39 | + options[:fix_urls] = ->(url) do | |
| 40 | + url.sub! %r{#{Regexp.escape(Laravel.base_url)}/docs\/(?!\d)}, "#{Laravel.base_url}/docs/5.7/" | |
| 41 | + url | |
| 42 | + end | |
| 43 | + end | |
| 44 | + | |
| 45 | + version '5.6' do | |
| 46 | + self.release = '5.6.33' | |
| 47 | + self.root_path = '/api/5.6/index.html' | |
| 48 | + self.initial_paths = %w(/docs/5.6/installation /api/5.6/classes.html) | |
| 49 | + | |
| 50 | + options[:only_patterns] = [%r{\A/api/5\.6/}, %r{\A/docs/5\.6/}] | |
| 51 | + | |
| 52 | + options[:fix_urls] = ->(url) do | |
| 53 | + url.sub! %r{#{Regexp.escape(Laravel.base_url)}/docs\/(?!\d)}, "#{Laravel.base_url}/docs/5.6/" | |
| 54 | + url | |
| 55 | + end | |
| 56 | + end | |
| 57 | + | |
| 32 | 58 | version '5.5' do |
| 33 | − self.release = '5.5.0' | |
| 59 | + self.release = '5.5.28' | |
| 34 | 60 | self.root_path = '/api/5.5/index.html' |
| 35 | 61 | self.initial_paths = %w(/docs/5.5/installation /api/5.5/classes.html) |
| 36 | 62 | |
| @@ -107,5 +133,9 @@ module Docs · 70 unmodified lines | ||
| 107 | 133 | url |
| 108 | 134 | end |
| 109 | 135 | end |
| 136 | + | |
| 137 | + def get_latest_version(opts) | |
| 138 | + get_latest_github_release('laravel', 'laravel', opts) | |
| 139 | + end | |
| 110 | 140 | end |
| 111 | 141 | end |
| @@ -0,0 +1,58 @@ | ||
| 1 | +module Docs | |
| 2 | + class Leaflet < UrlScraper | |
| 3 | + self.name = 'Leaflet' | |
| 4 | + self.type = 'simple' | |
| 5 | + self.slug = 'leaflet' | |
| 6 | + self.links = { | |
| 7 | + home: 'https://leafletjs.com/', | |
| 8 | + code: 'https://github.com/Leaflet/Leaflet' | |
| 9 | + } | |
| 10 | + | |
| 11 | + html_filters.push 'leaflet/entries', 'leaflet/clean_html', 'title' | |
| 12 | + | |
| 13 | + options[:container] = '.container' | |
| 14 | + options[:skip_links] = true | |
| 15 | + | |
| 16 | + options[:attribution] = <<-HTML | |
| 17 | + © 2010–2019 Vladimir Agafonkin<br> | |
| 18 | + © 2010–2011, CloudMade<br> | |
| 19 | + Maps © OpenStreetMap contributors. | |
| 20 | + HTML | |
| 21 | + | |
| 22 | + version '1.5' do | |
| 23 | + self.release = '1.5.0' | |
| 24 | + self.base_url = "https://leafletjs.com/reference-#{release}.html" | |
| 25 | + end | |
| 26 | + | |
| 27 | + version '1.4' do | |
| 28 | + self.release = '1.4.0' | |
| 29 | + self.base_url = "https://leafletjs.com/reference-#{release}.html" | |
| 30 | + end | |
| 31 | + | |
| 32 | + version '1.3' do | |
| 33 | + self.release = '1.3.4' | |
| 34 | + self.base_url = "https://leafletjs.com/reference-#{release}.html" | |
| 35 | + end | |
| 36 | + | |
| 37 | + version '1.2' do | |
| 38 | + self.release = '1.2.0' | |
| 39 | + self.base_url = "https://leafletjs.com/reference-#{release}.html" | |
| 40 | + end | |
| 41 | + | |
| 42 | + version '1.1' do | |
| 43 | + self.release = '1.1.0' | |
| 44 | + self.base_url = "https://leafletjs.com/reference-#{release}.html" | |
| 45 | + end | |
| 46 | + | |
| 47 | + version '1.0' do | |
| 48 | + self.release = '1.0.3' | |
| 49 | + self.base_url = "https://leafletjs.com/reference-#{release}.html" | |
| 50 | + end | |
| 51 | + | |
| 52 | + def get_latest_version(opts) | |
| 53 | + doc = fetch_doc('https://leafletjs.com/index.html', opts) | |
| 54 | + link = doc.css('ul > li > a').to_a.select {|node| node.content == 'Docs'}.first | |
| 55 | + link['href'].scan(/reference-([0-9.]+)\.html/)[0][0] | |
| 56 | + end | |
| 57 | + end | |
| 58 | +end | |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Less < UrlScraper |
| 3 | − self.type = 'less' | |
| 3 | + self.type = 'simple' | |
| 4 | 4 | self.release = '2.7.2' |
| 5 | 5 | self.base_url = 'http://lesscss.org' |
| 6 | 6 | self.root_path = '/features' |
| @@ -21,5 +21,11 @@ module Docs · 14 unmodified lines | ||
| 21 | 21 | © 2009–2016 The Core Less Team<br> |
| 22 | 22 | Licensed under the Creative Commons Attribution License 3.0. |
| 23 | 23 | HTML |
| 24 | + | |
| 25 | + def get_latest_version(opts) | |
| 26 | + doc = fetch_doc('http://lesscss.org/features/', opts) | |
| 27 | + label = doc.at_css('.footer-links > li').content | |
| 28 | + label.scan(/([0-9.]+)/)[0][0] | |
| 29 | + end | |
| 24 | 30 | end |
| 25 | 31 | end |
| @@ -19,5 +19,10 @@ module Docs · 18 unmodified lines | ||
| 19 | 19 | © 2005, 2006 Tobias Luetke<br> |
| 20 | 20 | Licensed under the MIT License. |
| 21 | 21 | HTML |
| 22 | + | |
| 23 | + def get_latest_version(opts) | |
| 24 | + tags = get_github_tags('Shopify', 'liquid', opts) | |
| 25 | + tags[0]['name'][1..-1] | |
| 26 | + end | |
| 22 | 27 | end |
| 23 | 28 | end |
| @@ -2,7 +2,7 @@ module Docs · 1 unmodified line | ||
| 2 | 2 | class Lodash < UrlScraper |
| 3 | 3 | self.name = 'lodash' |
| 4 | 4 | self.slug = 'lodash' |
| 5 | − self.type = 'lodash' | |
| 5 | + self.type = 'simple' | |
| 6 | 6 | self.links = { |
| 7 | 7 | home: 'https://lodash.com/', |
| 8 | 8 | code: 'https://github.com/lodash/lodash/' |
| @@ -14,12 +14,12 @@ module Docs · 5 unmodified lines | ||
| 14 | 14 | options[:skip_links] = true |
| 15 | 15 | |
| 16 | 16 | options[:attribution] = <<-HTML |
| 17 | − © 2012–2016 The Dojo Foundation<br> | |
| 17 | + © JS Foundation and other contributors<br> | |
| 18 | 18 | Licensed under the MIT License. |
| 19 | 19 | HTML |
| 20 | 20 | |
| 21 | 21 | version '4' do |
| 22 | − self.release = '4.17.4' | |
| 22 | + self.release = '4.17.10' | |
| 23 | 23 | self.base_url = "https://lodash.com/docs/#{release}" |
| 24 | 24 | end |
| 25 | 25 | |
| @@ -32,5 +32,10 @@ module Docs · 6 unmodified lines | ||
| 32 | 32 | self.release = '2.4.2' |
| 33 | 33 | self.base_url = "https://lodash.com/docs/#{release}" |
| 34 | 34 | end |
| 35 | + | |
| 36 | + def get_latest_version(opts) | |
| 37 | + doc = fetch_doc('https://lodash.com/docs/', opts) | |
| 38 | + doc.at_css('#version > option[selected]').content | |
| 39 | + end | |
| 35 | 40 | end |
| 36 | 41 | end |
| @@ -39,5 +39,10 @@ module Docs · 38 unmodified lines | ||
| 39 | 39 | © 2006–2016 LÖVE Development Team<br> |
| 40 | 40 | Licensed under the GNU Free Documentation License, Version 1.3. |
| 41 | 41 | HTML |
| 42 | + | |
| 43 | + def get_latest_version(opts) | |
| 44 | + doc = fetch_doc('https://love2d.org/wiki/Version_History', opts) | |
| 45 | + doc.at_css('#mw-content-text table a').content | |
| 46 | + end | |
| 42 | 47 | end |
| 43 | 48 | end |
| @@ -26,5 +26,10 @@ module Docs · 25 unmodified lines | ||
| 26 | 26 | self.release = '5.1.5' |
| 27 | 27 | self.base_url = 'https://www.lua.org/manual/5.1/' |
| 28 | 28 | end |
| 29 | + | |
| 30 | + def get_latest_version(opts) | |
| 31 | + doc = fetch_doc('https://www.lua.org/manual/', opts) | |
| 32 | + doc.at_css('p.menubar > a').content | |
| 33 | + end | |
| 29 | 34 | end |
| 30 | 35 | end |
| @@ -2,7 +2,7 @@ module Docs · 1 unmodified line | ||
| 2 | 2 | class Marionette < UrlScraper |
| 3 | 3 | self.name = 'Marionette.js' |
| 4 | 4 | self.slug = 'marionette' |
| 5 | − self.type = 'marionette' | |
| 5 | + self.type = 'simple' | |
| 6 | 6 | self.root_path = 'index' |
| 7 | 7 | self.links = { |
| 8 | 8 | home: 'https://marionettejs.com/', |
| @@ -18,8 +18,15 @@ module Docs · 9 unmodified lines | ||
| 18 | 18 | Licensed under the MIT License. |
| 19 | 19 | HTML |
| 20 | 20 | |
| 21 | + version '4' do | |
| 22 | + self.release = '4.0.0' | |
| 23 | + self.base_url = "https://marionettejs.com/docs/v#{release}/" | |
| 24 | + | |
| 25 | + html_filters.push 'marionette/entries_v3' | |
| 26 | + end | |
| 27 | + | |
| 21 | 28 | version '3' do |
| 22 | − self.release = '3.4.3' | |
| 29 | + self.release = '3.5.1' | |
| 23 | 30 | self.base_url = "https://marionettejs.com/docs/v#{release}/" |
| 24 | 31 | |
| 25 | 32 | html_filters.push 'marionette/entries_v3' |
| @@ -31,5 +38,9 @@ module Docs · 5 unmodified lines | ||
| 31 | 38 | |
| 32 | 39 | html_filters.push 'marionette/entries_v2' |
| 33 | 40 | end |
| 41 | + | |
| 42 | + def get_latest_version(opts) | |
| 43 | + get_npm_version('backbone.marionette', opts) | |
| 44 | + end | |
| 34 | 45 | end |
| 35 | 46 | end |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Markdown < UrlScraper |
| 3 | 3 | self.name = 'Markdown' |
| 4 | − self.type = 'markdown' | |
| 4 | + self.type = 'simple' | |
| 5 | 5 | self.base_url = 'http://daringfireball.net/projects/markdown/syntax' |
| 6 | 6 | |
| 7 | 7 | html_filters.push 'markdown/clean_html', 'markdown/entries' |
| @@ -13,5 +13,9 @@ module Docs · 5 unmodified lines | ||
| 13 | 13 | © 2004 John Gruber<br> |
| 14 | 14 | Licensed under the BSD License. |
| 15 | 15 | HTML |
| 16 | + | |
| 17 | + def get_latest_version(opts) | |
| 18 | + '1.0.0' | |
| 19 | + end | |
| 16 | 20 | end |
| 17 | 21 | end |
| @@ -6,7 +6,7 @@ module Docs · 5 unmodified lines | ||
| 6 | 6 | self.type = 'sphinx' |
| 7 | 7 | self.root_path = 'index.html' |
| 8 | 8 | self.links = { |
| 9 | − home: 'http://matplotlib.org/', | |
| 9 | + home: 'https://matplotlib.org/', | |
| 10 | 10 | code: 'https://github.com/matplotlib/matplotlib' |
| 11 | 11 | } |
| 12 | 12 | |
| @@ -16,26 +16,66 @@ module Docs · 3 unmodified lines | ||
| 16 | 16 | options[:skip] = %w(api_changes.html tutorial.html faq.html) |
| 17 | 17 | |
| 18 | 18 | options[:attribution] = <<-HTML |
| 19 | − © 2012–2017 Matplotlib Development Team. All rights reserved.<br> | |
| 19 | + © 2012–2018 Matplotlib Development Team. All rights reserved.<br> | |
| 20 | 20 | Licensed under the Matplotlib License Agreement. |
| 21 | 21 | HTML |
| 22 | 22 | |
| 23 | + version '3.1' do | |
| 24 | + self.release = '3.1.1' | |
| 25 | + self.base_urls = [ | |
| 26 | + "https://matplotlib.org/#{release}/api/", | |
| 27 | + "https://matplotlib.org/#{release}/mpl_toolkits/mplot3d/", | |
| 28 | + "https://matplotlib.org/#{release}/mpl_toolkits/axes_grid/api/" | |
| 29 | + ] | |
| 30 | + end | |
| 31 | + | |
| 32 | + version '3.0' do | |
| 33 | + self.release = '3.0.0' | |
| 34 | + self.base_urls = [ | |
| 35 | + "https://matplotlib.org/#{release}/api/", | |
| 36 | + "https://matplotlib.org/#{release}/mpl_toolkits/mplot3d/", | |
| 37 | + "https://matplotlib.org/#{release}/mpl_toolkits/axes_grid/api/" | |
| 38 | + ] | |
| 39 | + end | |
| 40 | + | |
| 41 | + version '2.2' do | |
| 42 | + self.release = '2.2.3' | |
| 43 | + self.base_urls = [ | |
| 44 | + "https://matplotlib.org/#{release}/api/", | |
| 45 | + "https://matplotlib.org/#{release}/mpl_toolkits/mplot3d/", | |
| 46 | + "https://matplotlib.org/#{release}/mpl_toolkits/axes_grid/api/" | |
| 47 | + ] | |
| 48 | + end | |
| 49 | + | |
| 50 | + version '2.1' do | |
| 51 | + self.release = '2.1.0' | |
| 52 | + self.base_urls = [ | |
| 53 | + "https://matplotlib.org/#{release}/api/", | |
| 54 | + "https://matplotlib.org/#{release}/mpl_toolkits/mplot3d/", | |
| 55 | + "https://matplotlib.org/#{release}/mpl_toolkits/axes_grid/api/" | |
| 56 | + ] | |
| 57 | + end | |
| 58 | + | |
| 23 | 59 | version '2.0' do |
| 24 | 60 | self.release = '2.0.2' |
| 25 | 61 | self.base_urls = [ |
| 26 | − "http://matplotlib.org/#{release}/api/", | |
| 27 | − "http://matplotlib.org/#{release}/mpl_toolkits/mplot3d/", | |
| 28 | − "http://matplotlib.org/#{release}/mpl_toolkits/axes_grid/api/" | |
| 62 | + "https://matplotlib.org/#{release}/api/", | |
| 63 | + "https://matplotlib.org/#{release}/mpl_toolkits/mplot3d/", | |
| 64 | + "https://matplotlib.org/#{release}/mpl_toolkits/axes_grid/api/" | |
| 29 | 65 | ] |
| 30 | 66 | end |
| 31 | 67 | |
| 32 | 68 | version '1.5' do |
| 33 | 69 | self.release = '1.5.3' |
| 34 | 70 | self.base_urls = [ |
| 35 | − "http://matplotlib.org/#{release}/api/", | |
| 36 | − "http://matplotlib.org/#{release}/mpl_toolkits/mplot3d/", | |
| 37 | − "http://matplotlib.org/#{release}/mpl_toolkits/axes_grid/api/" | |
| 71 | + "https://matplotlib.org/#{release}/api/", | |
| 72 | + "https://matplotlib.org/#{release}/mpl_toolkits/mplot3d/", | |
| 73 | + "https://matplotlib.org/#{release}/mpl_toolkits/axes_grid/api/" | |
| 38 | 74 | ] |
| 39 | 75 | end |
| 76 | + | |
| 77 | + def get_latest_version(opts) | |
| 78 | + get_latest_github_release('matplotlib', 'matplotlib', opts) | |
| 79 | + end | |
| 40 | 80 | end |
| 41 | 81 | end |
| @@ -6,6 +6,8 @@ module Docs · 5 unmodified lines | ||
| 6 | 6 | |
| 7 | 7 | html_filters.push 'css/clean_html', 'css/entries', 'title' |
| 8 | 8 | |
| 9 | + options[:mdn_tag] = 'CSS' | |
| 10 | + | |
| 9 | 11 | options[:root_title] = 'CSS' |
| 10 | 12 | |
| 11 | 13 | options[:skip] = %w(/CSS3 /Media/Visual /paged_media /Media/TV /Media/Tactile) |
| @@ -8,6 +8,8 @@ module Docs · 7 unmodified lines | ||
| 8 | 8 | |
| 9 | 9 | html_filters.push 'dom/clean_html', 'dom/entries', 'title' |
| 10 | 10 | |
| 11 | + options[:mdn_tag] = 'XSLT_Reference' | |
| 12 | + | |
| 11 | 13 | options[:root_title] = 'DOM' |
| 12 | 14 | |
| 13 | 15 | options[:skip] = %w( |
| @@ -1,5 +1,7 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class DomEvents < Mdn |
| 3 | + prepend FixInternalUrlsBehavior | |
| 4 | + | |
| 3 | 5 | self.name = 'DOM Events' |
| 4 | 6 | self.slug = 'dom_events' |
| 5 | 7 | self.base_url = 'https://developer.mozilla.org/en-US/docs/Web/Events' |
| @@ -7,6 +9,8 @@ module Docs · 1 unmodified line | ||
| 7 | 9 | html_filters.insert_after 'clean_html', 'dom_events/clean_html' |
| 8 | 10 | html_filters.push 'dom_events/entries', 'title' |
| 9 | 11 | |
| 12 | + options[:mdn_tag] = 'events' | |
| 13 | + | |
| 10 | 14 | options[:root_title] = 'DOM Events' |
| 11 | 15 | |
| 12 | 16 | options[:skip] = %w(/MozOrientation) |
| @@ -7,6 +7,8 @@ module Docs · 6 unmodified lines | ||
| 7 | 7 | |
| 8 | 8 | html_filters.push 'html/clean_html', 'html/entries', 'title' |
| 9 | 9 | |
| 10 | + options[:mdn_tag] = 'HTML' | |
| 11 | + | |
| 10 | 12 | options[:root_title] = 'HTML' |
| 11 | 13 | |
| 12 | 14 | options[:title] = ->(filter) do |
| @@ -8,6 +8,8 @@ module Docs · 7 unmodified lines | ||
| 8 | 8 | |
| 9 | 9 | html_filters.push 'javascript/clean_html', 'javascript/entries', 'title' |
| 10 | 10 | |
| 11 | + options[:mdn_tag] = 'JavaScript' | |
| 12 | + | |
| 11 | 13 | options[:root_title] = 'JavaScript' |
| 12 | 14 | |
| 13 | 15 | # Don't want |
| @@ -9,6 +9,7 @@ module Docs · 8 unmodified lines | ||
| 9 | 9 | html_filters.push 'mdn/clean_html' |
| 10 | 10 | text_filters.insert_before 'attribution', 'mdn/contribute_link' |
| 11 | 11 | |
| 12 | + options[:rate_limit] = 200 | |
| 12 | 13 | options[:trailing_slash] = false |
| 13 | 14 | |
| 14 | 15 | options[:skip_link] = ->(link) { |
| @@ -16,10 +17,15 @@ module Docs · 1 unmodified line | ||
| 16 | 17 | } |
| 17 | 18 | |
| 18 | 19 | options[:attribution] = <<-HTML |
| 19 | − © 2005–2017 Mozilla Developer Network and individual contributors.<br> | |
| 20 | + © 2005–2018 Mozilla Developer Network and individual contributors.<br> | |
| 20 | 21 | Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later. |
| 21 | 22 | HTML |
| 22 | 23 | |
| 24 | + def get_latest_version(opts) | |
| 25 | + json = fetch_json("https://developer.mozilla.org/en-US/docs/feeds/json/tag/#{options[:mdn_tag]}", opts) | |
| 26 | + DateTime.parse(json[0]['pubdate']).to_time.to_i | |
| 27 | + end | |
| 28 | + | |
| 23 | 29 | private |
| 24 | 30 | |
| 25 | 31 | def process_response?(response) |
| @@ -8,6 +8,8 @@ module Docs · 7 unmodified lines | ||
| 8 | 8 | |
| 9 | 9 | html_filters.push 'svg/clean_html', 'svg/entries', 'title' |
| 10 | 10 | |
| 11 | + options[:mdn_tag] = 'XSLT_Reference' | |
| 12 | + | |
| 11 | 13 | options[:root_title] = 'SVG' |
| 12 | 14 | |
| 13 | 15 | options[:title] = ->(filter) do |
| @@ -8,6 +8,8 @@ module Docs · 7 unmodified lines | ||
| 8 | 8 | |
| 9 | 9 | html_filters.push 'xslt_xpath/clean_html', 'xslt_xpath/entries', 'title' |
| 10 | 10 | |
| 11 | + options[:mdn_tag] = 'XSLT_Reference' | |
| 12 | + | |
| 11 | 13 | options[:root_title] = 'XSLT' |
| 12 | 14 | |
| 13 | 15 | options[:only_patterns] = [/\A\/XSLT/, /\A\/XPath/] |
| @@ -31,7 +31,7 @@ module Docs · 30 unmodified lines | ||
| 31 | 31 | HTML |
| 32 | 32 | |
| 33 | 33 | version '1.5' do |
| 34 | − self.release = '1.5.1' | |
| 34 | + self.release = '1.5.2' | |
| 35 | 35 | self.base_urls = ['https://docs.meteor.com/', 'https://guide.meteor.com/', 'http://blazejs.org/'] |
| 36 | 36 | end |
| 37 | 37 | |
| @@ -45,5 +45,10 @@ module Docs · 7 unmodified lines | ||
| 45 | 45 | self.base_urls = ['https://guide.meteor.com/v1.3/', "https://docs.meteor.com/v#{self.release}/"] |
| 46 | 46 | options[:fix_urls] = nil |
| 47 | 47 | end |
| 48 | + | |
| 49 | + def get_latest_version(opts) | |
| 50 | + doc = fetch_doc('https://docs.meteor.com/#/full/', opts) | |
| 51 | + doc.at_css('select.version-select > option').content | |
| 52 | + end | |
| 48 | 53 | end |
| 49 | 54 | end |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Mocha < UrlScraper |
| 3 | − self.type = 'mocha' | |
| 4 | − self.release = '3.5.0' | |
| 3 | + self.type = 'simple' | |
| 4 | + self.release = '5.2.0' | |
| 5 | 5 | self.base_url = 'https://mochajs.org/' |
| 6 | 6 | self.links = { |
| 7 | 7 | home: 'https://mochajs.org/', |
| @@ -15,8 +15,12 @@ module Docs · 7 unmodified lines | ||
| 15 | 15 | options[:skip_links] = true |
| 16 | 16 | |
| 17 | 17 | options[:attribution] = <<-HTML |
| 18 | − © 2016 JS Foundation and contributors<br> | |
| 18 | + © 2011–2018 JS Foundation and contributors<br> | |
| 19 | 19 | Licensed under the Creative Commons Attribution 4.0 International License. |
| 20 | 20 | HTML |
| 21 | + | |
| 22 | + def get_latest_version(opts) | |
| 23 | + get_npm_version('mocha', opts) | |
| 24 | + end | |
| 21 | 25 | end |
| 22 | 26 | end |
| @@ -15,5 +15,9 @@ module Docs · 14 unmodified lines | ||
| 15 | 15 | © 2009–2017 The Modernizr team<br> |
| 16 | 16 | Licensed under the MIT License. |
| 17 | 17 | HTML |
| 18 | + | |
| 19 | + def get_latest_version(opts) | |
| 20 | + get_npm_version('modernizr', opts) | |
| 21 | + end | |
| 18 | 22 | end |
| 19 | 23 | end |
| @@ -3,7 +3,7 @@ module Docs · 2 unmodified lines | ||
| 3 | 3 | self.name = 'Moment.js' |
| 4 | 4 | self.slug = 'moment' |
| 5 | 5 | self.type = 'moment' |
| 6 | − self.release = '2.18.1' | |
| 6 | + self.release = '2.22.1' | |
| 7 | 7 | self.base_url = 'http://momentjs.com' |
| 8 | 8 | self.root_path = '/docs/' |
| 9 | 9 | self.initial_paths = %w(/guides/) |
| @@ -22,5 +22,10 @@ module Docs · 12 unmodified lines | ||
| 22 | 22 | © JS Foundation and other contributors<br> |
| 23 | 23 | Licensed under the MIT License. |
| 24 | 24 | HTML |
| 25 | + | |
| 26 | + def get_latest_version(opts) | |
| 27 | + doc = fetch_doc('http://momentjs.com/', opts) | |
| 28 | + doc.at_css('.hero-title > h1 > span').content | |
| 29 | + end | |
| 25 | 30 | end |
| 26 | 31 | end |
| @@ -1,11 +1,12 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Mongoose < UrlScraper |
| 3 | 3 | self.name = 'Mongoose' |
| 4 | − self.type = 'mongoose' | |
| 5 | − self.release = '4.12.0' | |
| 4 | + self.type = 'simple' | |
| 5 | + self.release = '4.13.1' | |
| 6 | 6 | self.base_url = 'http://mongoosejs.com/docs/' |
| 7 | 7 | self.root_path = 'index.html' |
| 8 | 8 | self.initial_paths = %w(guide.html api.html) |
| 9 | + self.force_gzip = true | |
| 9 | 10 | self.links = { |
| 10 | 11 | home: 'http://mongoosejs.com/', |
| 11 | 12 | code: 'https://github.com/Automattic/mongoose' |
| @@ -25,5 +26,11 @@ module Docs · 13 unmodified lines | ||
| 25 | 26 | © 2010 LearnBoost<br> |
| 26 | 27 | Licensed under the MIT License. |
| 27 | 28 | HTML |
| 29 | + | |
| 30 | + def get_latest_version(opts) | |
| 31 | + doc = fetch_doc('https://mongoosejs.com/docs/', opts) | |
| 32 | + label = doc.at_css('.pure-menu-link').content.strip | |
| 33 | + label.sub(/Version /, '') | |
| 34 | + end | |
| 28 | 35 | end |
| 29 | 36 | end |
| @@ -2,11 +2,11 @@ module Docs · 1 unmodified line | ||
| 2 | 2 | class Nginx < UrlScraper |
| 3 | 3 | self.name = 'nginx' |
| 4 | 4 | self.type = 'nginx' |
| 5 | − self.release = '1.13.4' | |
| 5 | + self.release = '1.17.2' | |
| 6 | 6 | self.base_url = 'https://nginx.org/en/docs/' |
| 7 | 7 | self.links = { |
| 8 | 8 | home: 'https://nginx.org/', |
| 9 | − code: 'http://hg.nginx.org/nginx' | |
| 9 | + code: 'https://hg.nginx.org/nginx' | |
| 10 | 10 | } |
| 11 | 11 | |
| 12 | 12 | html_filters.push 'nginx/clean_html', 'nginx/entries' |
| @@ -21,9 +21,15 @@ module Docs · 8 unmodified lines | ||
| 21 | 21 | options[:skip_patterns] = [/\/faq\//] |
| 22 | 22 | |
| 23 | 23 | options[:attribution] = <<-HTML |
| 24 | − © 2002-2017 Igor Sysoev<br> | |
| 25 | − © 2011-2017 Nginx, Inc.<br> | |
| 24 | + © 2002-2019 Igor Sysoev<br> | |
| 25 | + © 2011-2019 Nginx, Inc.<br> | |
| 26 | 26 | Licensed under the BSD License. |
| 27 | 27 | HTML |
| 28 | + | |
| 29 | + def get_latest_version(opts) | |
| 30 | + doc = fetch_doc('https://nginx.org/en/download.html', opts) | |
| 31 | + table = doc.at_css('#content > table').inner_html | |
| 32 | + table.scan(/nginx-([0-9.]+)</)[0][0] | |
| 33 | + end | |
| 28 | 34 | end |
| 29 | 35 | end |
| @@ -2,8 +2,11 @@ module Docs · 1 unmodified line | ||
| 2 | 2 | class NginxLuaModule < Github |
| 3 | 3 | self.name = 'nginx / Lua Module' |
| 4 | 4 | self.slug = 'nginx_lua_module' |
| 5 | − self.release = '0.10.8' | |
| 5 | + self.release = '0.10.13' | |
| 6 | 6 | self.base_url = "https://github.com/openresty/lua-nginx-module/tree/v#{self.release}/" |
| 7 | + self.links = { | |
| 8 | + code: 'https://github.com/openresty/lua-nginx-module' | |
| 9 | + } | |
| 7 | 10 | |
| 8 | 11 | html_filters.push 'nginx_lua_module/clean_html', 'nginx_lua_module/entries', 'title' |
| 9 | 12 | |
| @@ -11,9 +14,15 @@ module Docs · 1 unmodified line | ||
| 11 | 14 | options[:container] = '#readme > article' |
| 12 | 15 | |
| 13 | 16 | options[:attribution] = <<-HTML |
| 14 | − © 2009–2016 Xiaozhe Wang (chaoslawful)<br> | |
| 15 | − © 2009–2017 Yichun "agentzh" Zhang (章亦春), OpenResty Inc.<br> | |
| 17 | + © 2009–2017 Xiaozhe Wang (chaoslawful)<br> | |
| 18 | + © 2009–2018 Yichun "agentzh" Zhang (章亦春), OpenResty Inc.<br> | |
| 16 | 19 | Licensed under the BSD License. |
| 17 | 20 | HTML |
| 21 | + | |
| 22 | + def get_latest_version(opts) | |
| 23 | + tags = get_github_tags('openresty', 'lua-nginx-module', opts) | |
| 24 | + tag = tags.find {|tag| !tag['name'].include?('rc')} | |
| 25 | + tag['name'][1..-1] | |
| 26 | + end | |
| 18 | 27 | end |
| 19 | 28 | end |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Nim < UrlScraper |
| 3 | − self.type = 'nim' | |
| 4 | − self.release = '0.17.0' | |
| 3 | + self.type = 'simple' | |
| 4 | + self.release = '0.19.0' | |
| 5 | 5 | self.base_url = 'https://nim-lang.org/docs/' |
| 6 | 6 | self.root_path = 'overview.html' |
| 7 | 7 | self.links = { |
| @@ -14,8 +14,13 @@ module Docs · 6 unmodified lines | ||
| 14 | 14 | options[:skip] = %w(theindex.html docgen.txt) |
| 15 | 15 | |
| 16 | 16 | options[:attribution] = <<-HTML |
| 17 | − © 2006–2017 Andreas Rumpf<br> | |
| 17 | + © 2006–2018 Andreas Rumpf<br> | |
| 18 | 18 | Licensed under the MIT License. |
| 19 | 19 | HTML |
| 20 | + | |
| 21 | + def get_latest_version(opts) | |
| 22 | + doc = fetch_doc('https://nim-lang.org/docs/overview.html', opts) | |
| 23 | + doc.at_css('.container > .docinfo > tbody > tr:last-child > td').content.strip | |
| 24 | + end | |
| 20 | 25 | end |
| 21 | 26 | end |
| @@ -23,18 +23,33 @@ module Docs · 22 unmodified lines | ||
| 23 | 23 | HTML |
| 24 | 24 | |
| 25 | 25 | version do |
| 26 | − self.release = '8.6.0' | |
| 26 | + self.release = '12.8.0' | |
| 27 | + self.base_url = 'https://nodejs.org/dist/latest-v12.x/docs/api/' | |
| 28 | + end | |
| 29 | + | |
| 30 | + version '10 LTS' do | |
| 31 | + self.release = '10.13.0' | |
| 32 | + self.base_url = 'https://nodejs.org/dist/latest-v10.x/docs/api/' | |
| 33 | + end | |
| 34 | + | |
| 35 | + version '8 LTS' do | |
| 36 | + self.release = '8.12.0' | |
| 27 | 37 | self.base_url = 'https://nodejs.org/dist/latest-v8.x/docs/api/' |
| 28 | 38 | end |
| 29 | 39 | |
| 30 | 40 | version '6 LTS' do |
| 31 | − self.release = '6.11.4' | |
| 41 | + self.release = '6.14.4' | |
| 32 | 42 | self.base_url = 'https://nodejs.org/dist/latest-v6.x/docs/api/' |
| 33 | 43 | end |
| 34 | 44 | |
| 35 | 45 | version '4 LTS' do |
| 36 | − self.release = '4.8.4' | |
| 46 | + self.release = '4.9.1' | |
| 37 | 47 | self.base_url = 'https://nodejs.org/dist/latest-v4.x/docs/api/' |
| 38 | 48 | end |
| 49 | + | |
| 50 | + def get_latest_version(opts) | |
| 51 | + doc = fetch_doc('https://nodejs.org/en/', opts) | |
| 52 | + doc.at_css('#home-intro > .home-downloadblock:last-of-type > a')['data-version'][1..-1] | |
| 53 | + end | |
| 39 | 54 | end |
| 40 | 55 | end |
| @@ -1,9 +1,13 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Nokogiri2 < Rdoc |
| 3 | + # Instructions: | |
| 4 | + # 1. Download the latest release at https://github.com/sparklemotion/nokogiri/releases | |
| 5 | + # 2. Run "bundle install && bundle exec rake docs" (in the Nokogiri directory) | |
| 6 | + # 4. Copy the "doc" directory to "docs/nokgiri" | |
| 7 | + | |
| 3 | 8 | self.name = 'Nokogiri' |
| 4 | 9 | self.slug = 'nokogiri' |
| 5 | − self.release = '1.8.1' | |
| 6 | − self.dir = '/Users/Thibaut/DevDocs/Docs/RDoc/Nokogiri' | |
| 10 | + self.release = '1.9.0' | |
| 7 | 11 | |
| 8 | 12 | html_filters.replace 'rdoc/entries', 'nokogiri2/entries' |
| 9 | 13 | |
| @@ -11,9 +15,13 @@ module Docs · 1 unmodified line | ||
| 11 | 15 | options[:only_patterns] = [/\ANokogiri/, /\AXSD/] |
| 12 | 16 | |
| 13 | 17 | options[:attribution] = <<-HTML |
| 14 | − © 2008–2017 Aaron Patterson, Mike Dalessio, Charles Nutter, Sergio Arbeo<br> | |
| 15 | − Patrick Mahoney, Yoko Harada, Akinori Musha, John Shahid<br> | |
| 18 | + © 2008–2018 Aaron Patterson, Mike Dalessio, Charles Nutter, Sergio Arbeo,<br> | |
| 19 | + Patrick Mahoney, Yoko Harada, Akinori Musha, John Shahid, Lars Kanis<br> | |
| 16 | 20 | Licensed under the MIT License. |
| 17 | 21 | HTML |
| 22 | + | |
| 23 | + def get_latest_version(opts) | |
| 24 | + get_latest_github_release('sparklemotion', 'nokogiri', opts) | |
| 25 | + end | |
| 18 | 26 | end |
| 19 | 27 | end |
| @@ -2,7 +2,7 @@ module Docs · 1 unmodified line | ||
| 2 | 2 | class Npm < UrlScraper |
| 3 | 3 | self.name = 'npm' |
| 4 | 4 | self.type = 'npm' |
| 5 | − self.release = '5.4.1' | |
| 5 | + self.release = '6.4.0' | |
| 6 | 6 | self.base_url = 'https://docs.npmjs.com/' |
| 7 | 7 | self.force_gzip = true |
| 8 | 8 | self.links = { |
| @@ -29,5 +29,9 @@ module Docs · 20 unmodified lines | ||
| 29 | 29 | Licensed under the npm License.<br> |
| 30 | 30 | npm is a trademark of npm, Inc. |
| 31 | 31 | HTML |
| 32 | + | |
| 33 | + def get_latest_version(opts) | |
| 34 | + get_latest_github_release('npm', 'cli', opts) | |
| 35 | + end | |
| 32 | 36 | end |
| 33 | 37 | end |
| @@ -2,7 +2,6 @@ module Docs · 1 unmodified line | ||
| 2 | 2 | class Numpy < FileScraper |
| 3 | 3 | self.name = 'NumPy' |
| 4 | 4 | self.type = 'sphinx' |
| 5 | − self.dir = '/Users/Thibaut/DevDocs/Docs/numpy/reference/' | |
| 6 | 5 | self.root_path = 'index.html' |
| 7 | 6 | self.links = { |
| 8 | 7 | home: 'http://www.numpy.org/', |
| @@ -26,6 +25,11 @@ module Docs · 17 unmodified lines | ||
| 26 | 25 | Licensed under the NumPy License. |
| 27 | 26 | HTML |
| 28 | 27 | |
| 28 | + version '1.14' do | |
| 29 | + self.release = '1.14.2' | |
| 30 | + self.base_url = "https://docs.scipy.org/doc/numpy-#{self.release}/reference/" | |
| 31 | + end | |
| 32 | + | |
| 29 | 33 | version '1.13' do |
| 30 | 34 | self.release = '1.13.0' |
| 31 | 35 | self.base_url = "https://docs.scipy.org/doc/numpy-#{self.release}/reference/" |
| @@ -45,5 +49,9 @@ module Docs · 13 unmodified lines | ||
| 45 | 49 | self.release = '1.10.4' |
| 46 | 50 | self.base_url = "https://docs.scipy.org/doc/numpy-#{self.release}/reference/" |
| 47 | 51 | end |
| 52 | + | |
| 53 | + def get_latest_version(opts) | |
| 54 | + get_latest_github_release('numpy', 'numpy', opts) | |
| 55 | + end | |
| 48 | 56 | end |
| 49 | 57 | end |
| @@ -1,11 +1,10 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Openjdk < FileScraper |
| 3 | + # Downloaded from packages.debian.org/sid/openjdk-8-doc | |
| 4 | + # Extracting subdirectory /usr/share/doc/openjdk-8-jre-headless/api | |
| 3 | 5 | self.name = 'OpenJDK' |
| 4 | 6 | self.type = 'openjdk' |
| 5 | 7 | self.root_path = 'overview-summary.html' |
| 6 | − # Downloaded from packages.debian.org/sid/openjdk-8-doc | |
| 7 | − # Extracting subdirectory /usr/share/doc/openjdk-8-jre-headless/api | |
| 8 | − self.dir = '/Users/Thibaut/DevDocs/Docs/OpenJDK' | |
| 9 | 8 | |
| 10 | 9 | html_filters.insert_after 'internal_urls', 'openjdk/clean_urls' |
| 11 | 10 | html_filters.push 'openjdk/entries', 'openjdk/clean_html' |
| @@ -87,5 +86,25 @@ module Docs · 75 unmodified lines | ||
| 87 | 86 | def read_file(path) |
| 88 | 87 | File.read(path).force_encoding('iso-8859-1').encode('utf-8') rescue nil |
| 89 | 88 | end |
| 89 | + | |
| 90 | + def get_latest_version(opts) | |
| 91 | + latest_version = 8 | |
| 92 | + current_attempt = latest_version | |
| 93 | + attempts = 0 | |
| 94 | + | |
| 95 | + while attempts < 3 | |
| 96 | + current_attempt += 1 | |
| 97 | + | |
| 98 | + doc = fetch_doc("https://packages.debian.org/sid/openjdk-#{current_attempt}-doc", opts) | |
| 99 | + if doc.at_css('.perror').nil? | |
| 100 | + latest_version = current_attempt | |
| 101 | + attempts = 0 | |
| 102 | + else | |
| 103 | + attempts += 1 | |
| 104 | + end | |
| 105 | + end | |
| 106 | + | |
| 107 | + latest_version | |
| 108 | + end | |
| 90 | 109 | end |
| 91 | 110 | end |
| @@ -18,5 +18,9 @@ module Docs · 17 unmodified lines | ||
| 18 | 18 | © 2010–2016 The OpenTSDB Authors<br> |
| 19 | 19 | Licensed under the GNU LGPLv2.1+ and GPLv3+ licenses. |
| 20 | 20 | HTML |
| 21 | + | |
| 22 | + def get_latest_version(opts) | |
| 23 | + get_latest_github_release('OpenTSDB', 'opentsdb', opts) | |
| 24 | + end | |
| 21 | 25 | end |
| 22 | 26 | end |
| @@ -2,8 +2,8 @@ module Docs · 1 unmodified line | ||
| 2 | 2 | class Padrino < UrlScraper |
| 3 | 3 | self.slug = 'padrino' |
| 4 | 4 | self.type = 'rubydoc' |
| 5 | − self.release = '0.13.2' | |
| 6 | − self.base_url = 'http://www.rubydoc.info/github/padrino/padrino-framework/' | |
| 5 | + self.release = '0.14.4' | |
| 6 | + self.base_url = 'https://www.rubydoc.info/github/padrino/padrino-framework/' | |
| 7 | 7 | self.root_path = 'file/README.rdoc' |
| 8 | 8 | self.initial_paths = %w(index2) |
| 9 | 9 | self.links = { |
| @@ -16,12 +16,16 @@ module Docs · 6 unmodified lines | ||
| 16 | 16 | options[:container] = ->(filter) { filter.root_page? ? '#filecontents' : '#content' } |
| 17 | 17 | |
| 18 | 18 | options[:attribution] = <<-HTML |
| 19 | − © 2010–2016 Padrino<br> | |
| 19 | + © 2010–2019 Padrino<br> | |
| 20 | 20 | Licensed under the MIT License. |
| 21 | 21 | HTML |
| 22 | 22 | |
| 23 | 23 | stub 'index2' do |
| 24 | 24 | request_one(url_for('index')).body |
| 25 | 25 | end |
| 26 | + | |
| 27 | + def get_latest_version(opts) | |
| 28 | + get_github_tags('padrino', 'padrino-framework', opts)[0]['name'] | |
| 29 | + end | |
| 26 | 30 | end |
| 27 | 31 | end |
| @@ -14,25 +14,57 @@ module Docs · 13 unmodified lines | ||
| 14 | 14 | options[:container] = '.document' |
| 15 | 15 | |
| 16 | 16 | options[:skip] = %w(internals.html release.html contributing.html whatsnew.html) |
| 17 | + options[:skip_patterns] = [/whatsnew\//] | |
| 17 | 18 | |
| 18 | 19 | options[:attribution] = <<-HTML |
| 19 | 20 | © 2008–2012, AQR Capital Management, LLC, Lambda Foundry, Inc. and PyData Development Team<br> |
| 20 | 21 | Licensed under the 3-clause BSD License. |
| 21 | 22 | HTML |
| 22 | 23 | |
| 24 | + version '0.25' do | |
| 25 | + self.release = '0.25.0' | |
| 26 | + self.base_url = "https://pandas.pydata.org/pandas-docs/version/#{self.release}/" | |
| 27 | + end | |
| 28 | + | |
| 29 | + version '0.24' do | |
| 30 | + self.release = '0.24.2' | |
| 31 | + self.base_url = "https://pandas.pydata.org/pandas-docs/version/#{self.release}/" | |
| 32 | + end | |
| 33 | + | |
| 34 | + version '0.23' do | |
| 35 | + self.release = '0.23.4' | |
| 36 | + self.base_url = "https://pandas.pydata.org/pandas-docs/version/#{self.release}/" | |
| 37 | + end | |
| 38 | + | |
| 39 | + version '0.22' do | |
| 40 | + self.release = '0.22.0' | |
| 41 | + self.base_url = "https://pandas.pydata.org/pandas-docs/version/#{self.release}/" | |
| 42 | + end | |
| 43 | + | |
| 44 | + version '0.21' do | |
| 45 | + self.release = '0.21.1' | |
| 46 | + self.base_url = "https://pandas.pydata.org/pandas-docs/version/#{self.release}/" | |
| 47 | + end | |
| 48 | + | |
| 23 | 49 | version '0.20' do |
| 24 | − self.release = '0.20.2' | |
| 25 | − self.base_url = "http://pandas.pydata.org/pandas-docs/version/#{self.release}/" | |
| 50 | + self.release = '0.20.3' | |
| 51 | + self.base_url = "https://pandas.pydata.org/pandas-docs/version/#{self.release}/" | |
| 26 | 52 | end |
| 27 | 53 | |
| 28 | 54 | version '0.19' do |
| 29 | 55 | self.release = '0.19.2' |
| 30 | − self.base_url = "http://pandas.pydata.org/pandas-docs/version/#{self.release}/" | |
| 56 | + self.base_url = "https://pandas.pydata.org/pandas-docs/version/#{self.release}/" | |
| 31 | 57 | end |
| 32 | 58 | |
| 33 | 59 | version '0.18' do |
| 34 | 60 | self.release = '0.18.1' |
| 35 | − self.base_url = "http://pandas.pydata.org/pandas-docs/version/#{self.release}/" | |
| 61 | + self.base_url = "https://pandas.pydata.org/pandas-docs/version/#{self.release}/" | |
| 62 | + end | |
| 63 | + | |
| 64 | + def get_latest_version(opts) | |
| 65 | + doc = fetch_doc('http://pandas.pydata.org/pandas-docs/stable/', opts) | |
| 66 | + label = doc.at_css('.body > .section > p').content | |
| 67 | + label.scan(/Version: ([0-9.]+)/)[0][0] | |
| 36 | 68 | end |
| 37 | 69 | end |
| 38 | 70 | end |
| @@ -2,7 +2,6 @@ module Docs · 1 unmodified line | ||
| 2 | 2 | class Perl < FileScraper |
| 3 | 3 | self.name = 'Perl' |
| 4 | 4 | self.type = 'perl' |
| 5 | − self.dir = '/Users/Thibaut/DevDocs/Docs/Perl' | |
| 6 | 5 | self.root_path = 'index.html' |
| 7 | 6 | self.links = { |
| 8 | 7 | home: 'https://www.perl.org/' |
| @@ -44,5 +43,11 @@ module Docs · 35 unmodified lines | ||
| 44 | 43 | self.release = '5.20.2' |
| 45 | 44 | self.base_url = "https://perldoc.perl.org/#{self.release}/" |
| 46 | 45 | end |
| 46 | + | |
| 47 | + def get_latest_version(opts) | |
| 48 | + doc = fetch_doc('https://perldoc.perl.org/', opts) | |
| 49 | + header = doc.at_css('h2.h1').content | |
| 50 | + header.scan(/Perl ([0-9.]+)/)[0][0] | |
| 51 | + end | |
| 47 | 52 | end |
| 48 | 53 | end |
| @@ -29,5 +29,10 @@ module Docs · 28 unmodified lines | ||
| 29 | 29 | self.release = '2.0.13' |
| 30 | 30 | self.base_url = 'https://docs.phalconphp.com/en/2.0.0/' |
| 31 | 31 | end |
| 32 | + | |
| 33 | + def get_latest_version(opts) | |
| 34 | + doc = fetch_doc('https://docs.phalconphp.com/', opts) | |
| 35 | + doc.at_css('.header__lang.expand > div > ul > li > a').content | |
| 36 | + end | |
| 32 | 37 | end |
| 33 | 38 | end |
| @@ -25,5 +25,9 @@ module Docs · 24 unmodified lines | ||
| 25 | 25 | © 2016 Richard Davey, Photon Storm Ltd.<br> |
| 26 | 26 | Licensed under the MIT License. |
| 27 | 27 | HTML |
| 28 | + | |
| 29 | + def get_latest_version(opts) | |
| 30 | + get_latest_github_release('photonstorm', 'phaser', opts) | |
| 31 | + end | |
| 28 | 32 | end |
| 29 | 33 | end |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Phoenix < UrlScraper |
| 3 | 3 | self.type = 'elixir' |
| 4 | − self.release = '1.3.0' | |
| 4 | + self.release = '1.3.4' | |
| 5 | 5 | self.base_url = 'https://hexdocs.pm/' |
| 6 | 6 | self.root_path = 'phoenix/Phoenix.html' |
| 7 | 7 | self.initial_paths = %w( |
| @@ -46,5 +46,10 @@ module Docs · 38 unmodified lines | ||
| 46 | 46 | HTML |
| 47 | 47 | end |
| 48 | 48 | } |
| 49 | + | |
| 50 | + def get_latest_version(opts) | |
| 51 | + doc = fetch_doc('https://hexdocs.pm/phoenix/Phoenix.html', opts) | |
| 52 | + doc.at_css('.sidebar-projectVersion').content.strip[1..-1] | |
| 53 | + end | |
| 49 | 54 | end |
| 50 | 55 | end |
| @@ -1,10 +1,12 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Php < FileScraper |
| 3 | + # Downloaded from php.net/download-docs.php | |
| 4 | + | |
| 3 | 5 | include FixInternalUrlsBehavior |
| 4 | 6 | |
| 5 | 7 | self.name = 'PHP' |
| 6 | 8 | self.type = 'php' |
| 7 | − self.release = 'up to 7.1.5' | |
| 9 | + self.release = '7.2.9' | |
| 8 | 10 | self.base_url = 'https://secure.php.net/manual/en/' |
| 9 | 11 | self.root_path = 'index.html' |
| 10 | 12 | self.initial_paths = %w( |
| @@ -20,12 +22,9 @@ module Docs · 9 unmodified lines | ||
| 20 | 22 | |
| 21 | 23 | self.links = { |
| 22 | 24 | home: 'https://secure.php.net/', |
| 23 | − code: 'https://github.com/php/php-src' | |
| 25 | + code: 'https://git.php.net/?p=php-src.git;a=summary' | |
| 24 | 26 | } |
| 25 | 27 | |
| 26 | − # Downloaded from php.net/download-docs.php | |
| 27 | − self.dir = '/Users/Thibaut/DevDocs/Docs/PHP' | |
| 28 | − | |
| 29 | 28 | html_filters.push 'php/internal_urls', 'php/entries', 'php/clean_html', 'title' |
| 30 | 29 | text_filters.push 'php/fix_urls' |
| 31 | 30 | |
| @@ -45,7 +44,7 @@ module Docs · 13 unmodified lines | ||
| 45 | 44 | |
| 46 | 45 | BOOKS = %w(apache apc apcu array bc bzip2 calendar csprng classobj ctype curl |
| 47 | 46 | datetime dba dir dom ds eio errorfunc ev event exec exif fileinfo filesystem filter |
| 48 | − ftp funchand gearman geoip gettext gmagick gmp hash iconv iisfunc image | |
| 47 | + ftp funchand gearman geoip gettext gmagick gmp hash ibase iconv iisfunc image | |
| 49 | 48 | imagick imap info inotify intl json judy ldap libevent libxml lua mail mailparse |
| 50 | 49 | math mbstring mcrypt memcached misc mysqli network oauth openssl |
| 51 | 50 | outcontrol password pcntl pcre pdo pgsql phar posix proctitle pthreads quickhash regex runkit |
| @@ -61,11 +60,17 @@ module Docs · 9 unmodified lines | ||
| 61 | 60 | function.mssql-select-db.html |
| 62 | 61 | pthreads.modifiers.html) |
| 63 | 62 | |
| 64 | − options[:skip_patterns] = [/mysqlnd/] | |
| 63 | + options[:skip_patterns] = [/mysqlnd/, /xdevapi/i] | |
| 65 | 64 | |
| 66 | 65 | options[:attribution] = <<-HTML |
| 67 | − © 1997–2017 The PHP Documentation Group<br> | |
| 66 | + © 1997–2018 The PHP Documentation Group<br> | |
| 68 | 67 | Licensed under the Creative Commons Attribution License v3.0 or later. |
| 69 | 68 | HTML |
| 69 | + | |
| 70 | + def get_latest_version(opts) | |
| 71 | + doc = fetch_doc('https://secure.php.net/manual/en/doc.changelog.php', opts) | |
| 72 | + label = doc.at_css('tbody.gen-changelog > tr > td').content | |
| 73 | + label.split(',').last.strip | |
| 74 | + end | |
| 70 | 75 | end |
| 71 | 76 | end |
| @@ -24,7 +24,7 @@ module Docs · 23 unmodified lines | ||
| 24 | 24 | HTML |
| 25 | 25 | |
| 26 | 26 | version '6' do |
| 27 | − self.release = '6.3' | |
| 27 | + self.release = '6.5' | |
| 28 | 28 | self.base_url = "https://phpunit.de/manual/#{release}/en/" |
| 29 | 29 | end |
| 30 | 30 | |
| @@ -37,5 +37,11 @@ module Docs · 6 unmodified lines | ||
| 37 | 37 | self.release = '4.8' |
| 38 | 38 | self.base_url = "https://phpunit.de/manual/#{release}/en/" |
| 39 | 39 | end |
| 40 | + | |
| 41 | + def get_latest_version(opts) | |
| 42 | + doc = fetch_doc('https://phpunit.readthedocs.io/', opts) | |
| 43 | + label = doc.at_css('.rst-current-version').content.strip | |
| 44 | + label.scan(/v: ([0-9.]+)/)[0][0] | |
| 45 | + end | |
| 40 | 46 | end |
| 41 | 47 | end |
| @@ -51,23 +51,40 @@ module Docs · 50 unmodified lines | ||
| 51 | 51 | /\Aunsupported-features/ ] |
| 52 | 52 | |
| 53 | 53 | options[:attribution] = <<-HTML |
| 54 | − © 1996–2017 The PostgreSQL Global Development Group<br> | |
| 54 | + © 1996–2018 The PostgreSQL Global Development Group<br> | |
| 55 | 55 | Licensed under the PostgreSQL License. |
| 56 | 56 | HTML |
| 57 | 57 | |
| 58 | + version '10' do | |
| 59 | + self.release = '10.3' | |
| 60 | + self.base_url = 'https://www.postgresql.org/docs/10/static/' | |
| 61 | + end | |
| 62 | + | |
| 58 | 63 | version '9.6' do |
| 59 | − self.release = '9.6.5' | |
| 64 | + self.release = '9.6.6' | |
| 60 | 65 | self.base_url = 'https://www.postgresql.org/docs/9.6/static/' |
| 66 | + | |
| 67 | + html_filters.insert_before 'postgresql/extract_metadata', 'postgresql/normalize_class_names' | |
| 61 | 68 | end |
| 62 | 69 | |
| 63 | 70 | version '9.5' do |
| 64 | − self.release = '9.5.9' | |
| 71 | + self.release = '9.5.10' | |
| 65 | 72 | self.base_url = 'https://www.postgresql.org/docs/9.5/static/' |
| 73 | + | |
| 74 | + html_filters.insert_before 'postgresql/extract_metadata', 'postgresql/normalize_class_names' | |
| 66 | 75 | end |
| 67 | 76 | |
| 68 | 77 | version '9.4' do |
| 69 | − self.release = '9.4.14' | |
| 78 | + self.release = '9.4.15' | |
| 70 | 79 | self.base_url = 'https://www.postgresql.org/docs/9.4/static/' |
| 80 | + | |
| 81 | + html_filters.insert_before 'postgresql/extract_metadata', 'postgresql/normalize_class_names' | |
| 82 | + end | |
| 83 | + | |
| 84 | + def get_latest_version(opts) | |
| 85 | + doc = fetch_doc('https://www.postgresql.org/docs/current/index.html', opts) | |
| 86 | + label = doc.at_css('#pgContentWrap h1.title').content | |
| 87 | + label.scan(/([0-9.]+)/)[0][0] | |
| 71 | 88 | end |
| 72 | 89 | end |
| 73 | 90 | end |
| @@ -3,7 +3,7 @@ module Docs · 2 unmodified lines | ||
| 3 | 3 | self.type = 'pug' |
| 4 | 4 | self.base_url = 'https://pugjs.org/' |
| 5 | 5 | self.root_path = 'api/getting-started.html' |
| 6 | − self.release = '2.0.0-rc.2' | |
| 6 | + self.release = '2.0.3' | |
| 7 | 7 | self.links = { |
| 8 | 8 | home: 'https://pugjs.org/', |
| 9 | 9 | code: 'https://github.com/pugjs/pug' |
| @@ -18,6 +18,10 @@ module Docs · 8 unmodified lines | ||
| 18 | 18 | Licensed under the MIT license. |
| 19 | 19 | HTML |
| 20 | 20 | |
| 21 | + def get_latest_version(opts) | |
| 22 | + get_npm_version('pug', opts) | |
| 23 | + end | |
| 24 | + | |
| 21 | 25 | private |
| 22 | 26 | |
| 23 | 27 | def parse(response) # Hook here because Nokogori removes whitespace from textareas |
| @@ -0,0 +1,23 @@ | ||
| 1 | +module Docs | |
| 2 | + class Puppeteer < Github | |
| 3 | + self.release = '1.10.0' | |
| 4 | + self.base_url = 'https://github.com/GoogleChrome/puppeteer/blob/v1.8.0/docs/api.md' | |
| 5 | + self.links = { | |
| 6 | + code: 'https://github.com/GoogleChrome/puppeteer' | |
| 7 | + } | |
| 8 | + | |
| 9 | + html_filters.push 'puppeteer/entries', 'puppeteer/clean_html' | |
| 10 | + | |
| 11 | + options[:container] = '.markdown-body' | |
| 12 | + | |
| 13 | + options[:attribution] = <<-HTML | |
| 14 | + © 2017 Google Inc<br> | |
| 15 | + Licensed under the Apache License 2.0. | |
| 16 | + HTML | |
| 17 | + | |
| 18 | + def get_latest_version(opts) | |
| 19 | + contents = get_github_file_contents('GoogleChrome', 'puppeteer', 'README.md', opts) | |
| 20 | + contents.scan(/\/v([0-9.]+)\/docs\/api\.md/)[0][0] | |
| 21 | + end | |
| 22 | + end | |
| 23 | +end | |
| @@ -0,0 +1,25 @@ | ||
| 1 | +module Docs | |
| 2 | + class Pygame < UrlScraper | |
| 3 | + self.type = 'simple' | |
| 4 | + self.release = '1.9.6' | |
| 5 | + self.base_url = 'https://www.pygame.org/docs/' | |
| 6 | + self.root_path = 'py-modindex.html' | |
| 7 | + self.links = { | |
| 8 | + home: 'https://www.pygame.org/', | |
| 9 | + code: 'https://github.com/pygame/pygame' | |
| 10 | + } | |
| 11 | + | |
| 12 | + html_filters.push 'pygame/clean_html', 'pygame/entries' | |
| 13 | + | |
| 14 | + options[:only_patterns] = [/ref\//] | |
| 15 | + | |
| 16 | + options[:attribution] = <<-HTML | |
| 17 | + © Pygame Developers.<br> | |
| 18 | + Licensed under the GNU LGPL License version 2.1. | |
| 19 | + HTML | |
| 20 | + | |
| 21 | + def get_latest_version(opts) | |
| 22 | + get_latest_github_release('pygame', 'pygame', opts) | |
| 23 | + end | |
| 24 | + end | |
| 25 | +end | |
| @@ -1,6 +1,6 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Python < FileScraper |
| 3 | − self.type = 'sphinx' | |
| 3 | + self.type = 'python' | |
| 4 | 4 | self.root_path = 'library/index.html' |
| 5 | 5 | self.links = { |
| 6 | 6 | home: 'https://www.python.org/', |
| @@ -19,32 +19,41 @@ module Docs · 12 unmodified lines | ||
| 19 | 19 | library/sunau.html) |
| 20 | 20 | |
| 21 | 21 | options[:attribution] = <<-HTML |
| 22 | − © 2001–2017 Python Software Foundation<br> | |
| 22 | + © 2001–2018 Python Software Foundation<br> | |
| 23 | 23 | Licensed under the PSF License. |
| 24 | 24 | HTML |
| 25 | 25 | |
| 26 | − version '3.6' do | |
| 27 | − self.release = '3.6.1' | |
| 28 | − self.dir = '/Users/Thibaut/DevDocs/Docs/Python36' # docs.python.org/3.6/download.html | |
| 26 | + version '3.7' do # docs.python.org/3.7/download.html | |
| 27 | + self.release = '3.7.4' | |
| 28 | + self.base_url = 'https://docs.python.org/3.7/' | |
| 29 | + | |
| 30 | + html_filters.push 'python/entries_v3', 'sphinx/clean_html', 'python/clean_html' | |
| 31 | + end | |
| 32 | + | |
| 33 | + version '3.6' do # docs.python.org/3.6/download.html | |
| 34 | + self.release = '3.6.9' | |
| 29 | 35 | self.base_url = 'https://docs.python.org/3.6/' |
| 30 | 36 | |
| 31 | 37 | html_filters.push 'python/entries_v3', 'sphinx/clean_html', 'python/clean_html' |
| 32 | 38 | end |
| 33 | 39 | |
| 34 | − version '3.5' do | |
| 35 | − self.release = '3.5.3' | |
| 36 | − self.dir = '/Users/Thibaut/DevDocs/Docs/Python35' # docs.python.org/3.5/download.html | |
| 40 | + version '3.5' do # docs.python.org/3.5/download.html | |
| 41 | + self.release = '3.5.7' | |
| 37 | 42 | self.base_url = 'https://docs.python.org/3.5/' |
| 38 | 43 | |
| 39 | 44 | html_filters.push 'python/entries_v3', 'sphinx/clean_html', 'python/clean_html' |
| 40 | 45 | end |
| 41 | 46 | |
| 42 | − version '2.7' do | |
| 43 | − self.release = '2.7.13' | |
| 44 | − self.dir = '/Users/Thibaut/DevDocs/Docs/Python27' # docs.python.org/2.7/download.html | |
| 47 | + version '2.7' do # docs.python.org/2.7/download.html | |
| 48 | + self.release = '2.7.16' | |
| 45 | 49 | self.base_url = 'https://docs.python.org/2.7/' |
| 46 | 50 | |
| 47 | 51 | html_filters.push 'python/entries_v2', 'sphinx/clean_html', 'python/clean_html' |
| 48 | 52 | end |
| 53 | + | |
| 54 | + def get_latest_version(opts) | |
| 55 | + doc = fetch_doc('https://docs.python.org/', opts) | |
| 56 | + doc.at_css('.version_switcher_placeholder').content | |
| 57 | + end | |
| 49 | 58 | end |
| 50 | 59 | end |
| @@ -1,7 +1,7 @@ | ||
| 1 | 1 | module Docs |
| 2 | 2 | class Q < Github |
| 3 | 3 | self.name = 'Q' |
| 4 | − self.release = '1.5.0' | |
| 4 | + self.release = '1.5.1' | |
| 5 | 5 | self.base_url = 'https://github.com/kriskowal/q/wiki/' |
| 6 | 6 | self.root_path = 'API-Reference' |
| 7 | 7 | self.links = { |
| @@ -19,5 +19,9 @@ module Docs · 11 unmodified lines | ||
| 19 | 19 | © 2009–2017 Kristopher Michael Kowal<br> |
| 20 | 20 | Licensed under the MIT License. |
| 21 | 21 | HTML |
| 22 | + | |
| 23 | + def get_latest_version(opts) | |
| 24 | + get_npm_version('q', opts) | |
| 25 | + end | |
| 22 | 26 | end |
| 23 | 27 | end |
| @@ -0,0 +1,136 @@ | ||
| 1 | +module Docs | |
| 2 | + class Qt < UrlScraper | |
| 3 | + self.name = 'Qt' | |
| 4 | + self.type = 'qt' | |
| 5 | + self.initial_paths = %w(classes.html qmltypes.html) | |
| 6 | + self.root_path = 'index.html' | |
| 7 | + self.links = { | |
| 8 | + home: 'https://www.qt.io', | |
| 9 | + code: 'https://code.qt.io/cgit/' | |
| 10 | + } | |
| 11 | + | |
| 12 | + html_filters.push 'qt/entries', 'qt/clean_html' | |
| 13 | + | |
| 14 | + options[:container] = '.main' | |
| 15 | + options[:max_image_size] = 156_000 | |
| 16 | + options[:skip_patterns] = [ | |
| 17 | + # License, copyright attributions | |
| 18 | + /3rdparty/, | |
| 19 | + /attribution/, | |
| 20 | + /license/, | |
| 21 | + /licensing/, | |
| 22 | + | |
| 23 | + # Examples, guides, tutorials | |
| 24 | + /example/, | |
| 25 | + /guide$/, | |
| 26 | + /tutorial/, | |
| 27 | + /porting/, | |
| 28 | + /usecase/, | |
| 29 | + /topic/, | |
| 30 | + /^modelview/, | |
| 31 | + /deploy(ing|ment)/, | |
| 32 | + /building/, | |
| 33 | + | |
| 34 | + # Old versions, changelog | |
| 35 | + /obsolete/, | |
| 36 | + /compatibility/, | |
| 37 | + /^whatsnew/, | |
| 38 | + /^newclasses/, | |
| 39 | + | |
| 40 | + # Deprecated modules | |
| 41 | + /(qtopengl|qgl)/, | |
| 42 | + /qt?script/, | |
| 43 | + | |
| 44 | + # Indexes | |
| 45 | + /members/, | |
| 46 | + /module/, | |
| 47 | + /overview/, | |
| 48 | + /^qopenglfunctions/, | |
| 49 | + | |
| 50 | + # Tooling | |
| 51 | + /^(qt)?(linguist|assistant|qdbusviewer)/, | |
| 52 | + ] | |
| 53 | + | |
| 54 | + options[:skip] = [ | |
| 55 | + "qt5-intro.html", | |
| 56 | + "compatmap.html", | |
| 57 | + | |
| 58 | + # Indexes | |
| 59 | + "classes.html", | |
| 60 | + "qtmodules.html", | |
| 61 | + "modules-qml.html", | |
| 62 | + "modules-cpp.html", | |
| 63 | + "functions.html", | |
| 64 | + "namespaces.html", | |
| 65 | + "qmltypes.html", | |
| 66 | + "qt3d-qml.html", | |
| 67 | + "qmlbasictypes.html", | |
| 68 | + "guibooks.html", | |
| 69 | + "annotated.html", | |
| 70 | + "overviews-main.html", | |
| 71 | + "reference-overview.html", | |
| 72 | + | |
| 73 | + # Tutorials | |
| 74 | + "qtvirtualkeyboard-build.html", | |
| 75 | + | |
| 76 | + # Copyright | |
| 77 | + "trademarks.html", | |
| 78 | + "lgpl.html", | |
| 79 | + "bughowto.html", | |
| 80 | + | |
| 81 | + # Changelogs | |
| 82 | + "changes.html", | |
| 83 | + "qtlocation-changes.html", | |
| 84 | + "sourcebreaks.html", | |
| 85 | + | |
| 86 | + # Best practice guides | |
| 87 | + "accessible.html", | |
| 88 | + "accessible-qtquick.html", | |
| 89 | + "sharedlibrary.html", | |
| 90 | + "exceptionsafety.html", | |
| 91 | + "scalability.html", | |
| 92 | + "session.html", | |
| 93 | + "appicon.html", | |
| 94 | + "accelerators.html", | |
| 95 | + | |
| 96 | + # Other | |
| 97 | + "ecmascript.html", | |
| 98 | + "qtremoteobjects-interaction.html", | |
| 99 | + ] | |
| 100 | + | |
| 101 | + options[:attribution] = <<-HTML | |
| 102 | + © The Qt Company Ltd<br> | |
| 103 | + Licensed under the GNU Free Documentation License, Version 1.3. | |
| 104 | + HTML | |
| 105 | + | |
| 106 | + version '5.13' do | |
| 107 | + self.release = '5.13' | |
| 108 | + self.base_url = 'https://doc.qt.io/qt-5.13/' | |
| 109 | + end | |
| 110 | + | |
26 more lines aren’t shown here.
This diff is large: its 23 lines aren’t shown here.
This diff is large: its 28 lines aren’t shown here.
This diff is large: its 64 lines aren’t shown here.
This diff is large: its 59 lines aren’t shown here.
This diff is large: its 24 lines aren’t shown here.
This diff is large: its 33 lines aren’t shown here.
This diff is large: its 25 lines aren’t shown here.
This diff is large: its 18 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 19 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 49 lines aren’t shown here.
This diff is large: its 19 lines aren’t shown here.
This diff is large: its 22 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 64 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 22 lines aren’t shown here.
This diff is large: its 19 lines aren’t shown here.
This diff is large: its 12 lines aren’t shown here.
This diff is large: its 63 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 29 lines aren’t shown here.
This diff is large: its 28 lines aren’t shown here.
This diff is large: its 40 lines aren’t shown here.
This diff is large: its 22 lines aren’t shown here.
This diff is large: its 30 lines aren’t shown here.
This diff is large: its 26 lines aren’t shown here.
This diff is large: its 25 lines aren’t shown here.
This diff is large: its 20 lines aren’t shown here.
This diff is large: its 19 lines aren’t shown here.
This diff is large: its 24 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 11 lines aren’t shown here.
This diff is large: its 14 lines aren’t shown here.
This diff is large: its 10 lines aren’t shown here.
This diff is large: its 178 lines aren’t shown here.
This diff is large: its 214 lines aren’t shown here.
This diff is large: its 316 lines aren’t shown here.
This diff is large: its 222 lines aren’t shown here.
This diff is large: its 2 lines aren’t shown here.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This diff is large: its 2 lines aren’t shown here.
Binary file not shown.
Binary file not shown.
This diff is large: its 2 lines aren’t shown here.
Binary file not shown.
Binary file not shown.
This diff is large: its 2 lines aren’t shown here.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Empty file added.
This diff is large: its 2 lines aren’t shown here.
Binary file not shown.
Binary file not shown.
This diff is large: its 2 lines aren’t shown here.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Empty file added.
This diff is large: its 2 lines aren’t shown here.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Empty file added.
This diff is large: its 2 lines aren’t shown here.
Binary file not shown.
Binary file not shown.
This diff is large: its 2 lines aren’t shown here.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This diff is large: its 3 lines aren’t shown here.
Binary file not shown.
Binary file not shown.
This diff is large: its 2 lines aren’t shown here.
Binary file not shown.
Binary file not shown.
This diff is large: its 2 lines aren’t shown here.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Empty file added.
This diff is large: its 2 lines aren’t shown here.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This diff is large: its 2 lines aren’t shown here.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This diff is large: its 2 lines aren’t shown here.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Empty file added.
This diff is large: its 15 lines aren’t shown here.
This diff is large: its 2 lines aren’t shown here.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This diff is large: its 2 lines aren’t shown here.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This diff is large: its 2 lines aren’t shown here.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This diff is large: its 19 lines aren’t shown here.
This diff is large: its 2 lines aren’t shown here.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This diff is large: its 29 lines aren’t shown here.
This diff is large: its 14 lines aren’t shown here.
This diff is large: its 254 lines aren’t shown here.
This diff is large: its 55 lines aren’t shown here.
This diff is large: its 84 lines aren’t shown here.
This diff is large: its 205 lines aren’t shown here.
This diff is large: its 25 lines aren’t shown here.
This diff is large: its 27 lines aren’t shown here.
This diff is large: its 15 lines aren’t shown here.
This diff is large: its 19 lines aren’t shown here.
This diff is large: its 62 lines aren’t shown here.
This diff is large: its 28 lines aren’t shown here.