History

Merge branch 'master' into emilekm-master

Jasper van Merle committed 7 years ago

Commit 14049da2 parents cc87443 fef9589315 files · +6564 −2879Browse files

Unified · Split

315 files
  1. .editorconfig +3 −0
  2. .github/CONTRIBUTING.md +4 −5
  3. .github/ISSUE_TEMPLATE.md +1 −1
  4. .gitignore +1 −0
  5. .ruby-version +1 −1
  6. .slugignore +1 −2
  7. .travis.yml +20 −1
  8. CODE_OF_CONDUCT.md +2 −0
  9. Dockerfile +3 −2
  10. Dockerfile-alpine +3 −2
  11. Gemfile +4 −1
  12. Gemfile.lock +39 −31
  13. README.md +9 −9
  14. assets/images/.gitignore +1 −0
  15. assets/images/docs-1.png +0 −0
  16. assets/images/docs-1@2x.png +0 −0
  17. assets/images/docs-2.png +0 −0
  18. assets/images/docs-2@2x.png +0 −0
  19. assets/javascripts/app/app.coffee +14 −9
  20. assets/javascripts/app/appcache.coffee +0 −42
  21. assets/javascripts/app/config.coffee.erb +3 −0
  22. assets/javascripts/app/serviceworker.coffee +49 −0
  23. assets/javascripts/app/settings.coffee +34 −1
  24. assets/javascripts/app/update_checker.coffee +3 −3
  25. assets/javascripts/lib/cookies_store.coffee +5 −1
  26. assets/javascripts/lib/favicon.coffee +64 −0
  27. assets/javascripts/lib/page.coffee +19 −1
  28. assets/javascripts/lib/util.coffee +4 −0
  29. assets/javascripts/models/doc.coffee +3 −1
  30. assets/javascripts/news.json +8 −0
  31. assets/javascripts/templates/error_tmpl.coffee +5 −5
  32. assets/javascripts/templates/notif_tmpl.coffee +6 −0
  33. assets/javascripts/templates/pages/about_tmpl.coffee +66 −10
  34. assets/javascripts/templates/pages/offline_tmpl.coffee +9 −4
  35. assets/javascripts/templates/pages/root_tmpl.coffee.erb +2 −2
  36. assets/javascripts/templates/pages/settings_tmpl.coffee +8 −0
  37. assets/javascripts/tracking.js +27 −23
  38. assets/javascripts/vendor/fastclick.js +2 −2
  39. assets/javascripts/vendor/prism.js +2577 −2210
  40. assets/javascripts/vendor/raven.js +1 −1
  41. assets/javascripts/views/content/content.coffee +3 −0
  42. assets/javascripts/views/content/entry_page.coffee +2 −1
  43. assets/javascripts/views/content/settings_page.coffee +10 −11
  44. assets/javascripts/views/content/type_page.coffee +1 −0
  45. assets/javascripts/views/layout/document.coffee +7 −5
  46. assets/javascripts/views/layout/resizer.coffee +2 −9
  47. assets/javascripts/views/layout/settings.coffee +1 −9
  48. assets/javascripts/views/search/search.coffee +8 −0
  49. assets/javascripts/views/search/search_scope.coffee +23 −5
  50. assets/javascripts/views/sidebar/sidebar.coffee +1 −1
  51. assets/stylesheets/application.css.scss +10 −5
  52. assets/stylesheets/components/_environment.scss.erb +3 −0
  53. assets/stylesheets/components/_header.scss +1 −0
  54. assets/stylesheets/components/_notif.scss +4 −0
  55. assets/stylesheets/global/_icons.scss +0 −182
  56. assets/stylesheets/global/_icons.scss.erb +43 −0
  57. assets/stylesheets/pages/_cypress.scss +21 −0
  58. assets/stylesheets/pages/_octave.scss +9 −0
  59. assets/stylesheets/pages/_rxjs.scss +24 −0
  60. assets/stylesheets/pages/_scala.scss +4 −0
  61. assets/stylesheets/pages/_wordpress.scss +15 −0
  62. docs/adding-docs.md +6 −4
  63. docs/filter-reference.md +0 −0
  64. docs/maintainers.md +7 −7
  65. docs/scraper-reference.md +41 −0
  66. lib/app.rb +42 −24
  67. lib/docs/core/doc.rb +99 −1
  68. lib/docs/core/scrapers/file_scraper.rb +2 −2
  69. lib/docs/filters/cmake/entries.rb +1 −0
  70. lib/docs/filters/composer/clean_html.rb +35 −0
  71. lib/docs/filters/composer/entries.rb +36 −0
  72. lib/docs/filters/cypress/clean_html.rb +26 −0
  73. lib/docs/filters/cypress/entries.rb +34 −0
  74. lib/docs/filters/elixir/clean_html.rb +5 −0
  75. lib/docs/filters/go/clean_html.rb +1 −0
  76. lib/docs/filters/homebrew/clean_html.rb +5 −0
  77. lib/docs/filters/homebrew/entries.rb +3 −1
  78. lib/docs/filters/kotlin/clean_html.rb +7 −0
  79. lib/docs/filters/kotlin/entries.rb +3 −1
  80. lib/docs/filters/octave/clean_html.rb +46 −0
  81. lib/docs/filters/octave/entries.rb +22 −0
  82. lib/docs/filters/pandas/entries.rb +5 −3
  83. lib/docs/filters/pony/clean_html.rb +16 −0
  84. lib/docs/filters/pony/entries.rb +35 −0
  85. lib/docs/filters/pygame/clean_html.rb +1 −1
  86. lib/docs/filters/python/entries_v2.rb +1 −7
  87. lib/docs/filters/python/entries_v3.rb +3 −7
  88. lib/docs/filters/qt/clean_html.rb +1 −0
  89. lib/docs/filters/qt/entries.rb +1 −0
  90. lib/docs/filters/ramda/clean_html.rb +1 −1
  91. lib/docs/filters/rxjs/clean_html.rb +139 −0
  92. lib/docs/filters/rxjs/entries.rb +29 −0
  93. lib/docs/filters/salt_stack/clean_html.rb +27 −0
  94. lib/docs/filters/salt_stack/entries.rb +38 −0
  95. lib/docs/filters/scala/clean_html.rb +109 −0
  96. lib/docs/filters/scala/entries.rb +103 −0
  97. lib/docs/filters/trio/clean_html.rb +58 −0
  98. lib/docs/filters/trio/entries.rb +48 −0
  99. lib/docs/filters/vue_router/clean_html.rb +21 −0
  100. lib/docs/filters/vue_router/entries.rb +71 −0
  101. lib/docs/filters/vuex/clean_html.rb +17 −0
  102. lib/docs/filters/vuex/entries.rb +69 −0
  103. lib/docs/filters/wordpress/clean_html.rb +39 −0
  104. lib/docs/filters/wordpress/entries.rb +19 −0
  105. lib/docs/scrapers/angular.rb +4 −0
  106. lib/docs/scrapers/angularjs.rb +16 −3
  107. lib/docs/scrapers/ansible.rb +31 −50
  108. lib/docs/scrapers/apache.rb +5 −0
  109. lib/docs/scrapers/apache_pig.rb +5 −0
  110. lib/docs/scrapers/async.rb +5 −0
  111. lib/docs/scrapers/babel.rb +5 −0
  112. lib/docs/scrapers/backbone.rb +5 −0
  113. lib/docs/scrapers/bash.rb +6 −1
  114. lib/docs/scrapers/bluebird.rb +4 −0
  115. lib/docs/scrapers/bootstrap.rb +10 −4
  116. lib/docs/scrapers/bottle.rb +6 −0
  117. lib/docs/scrapers/bower.rb +4 −0
  118. lib/docs/scrapers/c.rb +7 −0
  119. lib/docs/scrapers/cakephp.rb +5 −0
  120. lib/docs/scrapers/chai.rb +4 −0
  121. lib/docs/scrapers/chef.rb +5 −0
  122. lib/docs/scrapers/clojure.rb +14 −4
  123. lib/docs/scrapers/cmake.rb +32 −11
  124. lib/docs/scrapers/codeception.rb +5 −0
  125. lib/docs/scrapers/codeceptjs.rb +4 −0
  126. lib/docs/scrapers/codeigniter.rb +6 −0
  127. lib/docs/scrapers/coffeescript.rb +4 −0
  128. lib/docs/scrapers/composer.rb +28 −0
  129. lib/docs/scrapers/cordova.rb +10 −0
  130. lib/docs/scrapers/cpp.rb +8 −0
  131. lib/docs/scrapers/crystal.rb +5 −0
  132. lib/docs/scrapers/cypress.rb +36 −0
  133. lib/docs/scrapers/d.rb +5 −0
  134. lib/docs/scrapers/d3.rb +4 −0
  135. lib/docs/scrapers/dart.rb +6 −0
  136. lib/docs/scrapers/django.rb +13 −3
  137. lib/docs/scrapers/docker.rb +153 −26
  138. lib/docs/scrapers/dojo.rb +5 −0
  139. lib/docs/scrapers/drupal.rb +5 −0
  140. lib/docs/scrapers/electron.rb +5 −0
  141. lib/docs/scrapers/elixir.rb +33 −2
  142. lib/docs/scrapers/ember.rb +5 −0
  143. lib/docs/scrapers/erlang.rb +5 −0
  144. lib/docs/scrapers/eslint.rb +4 −0
  145. lib/docs/scrapers/express.rb +4 −0
  146. lib/docs/scrapers/falcon.rb +5 −0
  147. lib/docs/scrapers/fish.rb +11 −0
  148. lib/docs/scrapers/flow.rb +4 −0
  149. lib/docs/scrapers/git.rb +5 −0
  150. lib/docs/scrapers/gnu/gcc.rb +6 −0
  151. lib/docs/scrapers/gnu/gnu_fortran.rb +6 −0
  152. lib/docs/scrapers/go.rb +6 −1
  153. lib/docs/scrapers/godot.rb +5 −0
  154. lib/docs/scrapers/graphite.rb +5 −0
  155. lib/docs/scrapers/grunt.rb +4 −0
  156. lib/docs/scrapers/handlebars.rb +4 −0
  157. lib/docs/scrapers/haskell.rb +8 −1
  158. lib/docs/scrapers/haxe.rb +6 −0
  159. lib/docs/scrapers/homebrew.rb +5 −1
  160. lib/docs/scrapers/http.rb +2 −0
  161. lib/docs/scrapers/immutable.rb +4 −0
  162. lib/docs/scrapers/influxdata.rb +6 −0
  163. lib/docs/scrapers/jasmine.rb +4 −0
  164. lib/docs/scrapers/jekyll.rb +5 −0
  165. lib/docs/scrapers/jest.rb +5 −0
  166. lib/docs/scrapers/jquery/jquery_core.rb +4 −0
  167. lib/docs/scrapers/jquery/jquery_mobile.rb +5 −0
  168. lib/docs/scrapers/jquery/jquery_ui.rb +4 −0
  169. lib/docs/scrapers/jsdoc.rb +4 −0
  170. lib/docs/scrapers/julia.rb +4 −0
  171. lib/docs/scrapers/knockout.rb +4 −0
  172. lib/docs/scrapers/koa.rb +4 −0
  173. lib/docs/scrapers/kotlin.rb +15 −3
  174. lib/docs/scrapers/laravel.rb +4 −0
  175. lib/docs/scrapers/leaflet.rb +16 −1
  176. lib/docs/scrapers/less.rb +6 −0
  177. lib/docs/scrapers/liquid.rb +5 −0
  178. lib/docs/scrapers/lodash.rb +5 −0
  179. lib/docs/scrapers/love.rb +5 −0
  180. lib/docs/scrapers/lua.rb +5 −0
  181. lib/docs/scrapers/marionette.rb +4 −0
  182. lib/docs/scrapers/markdown.rb +4 −0
  183. lib/docs/scrapers/matplotlib.rb +13 −0
  184. lib/docs/scrapers/mdn/css.rb +2 −0
  185. lib/docs/scrapers/mdn/dom.rb +2 −0
  186. lib/docs/scrapers/mdn/dom_events.rb +2 −0
  187. lib/docs/scrapers/mdn/html.rb +2 −0
  188. lib/docs/scrapers/mdn/javascript.rb +2 −0
  189. lib/docs/scrapers/mdn/mdn.rb +5 −0
  190. lib/docs/scrapers/mdn/svg.rb +2 −0
  191. lib/docs/scrapers/mdn/xslt_xpath.rb +2 −0
  192. lib/docs/scrapers/meteor.rb +5 −0
  193. lib/docs/scrapers/mocha.rb +4 −0
  194. lib/docs/scrapers/modernizr.rb +4 −0
  195. lib/docs/scrapers/moment.rb +5 −0
  196. lib/docs/scrapers/mongoose.rb +6 −0
  197. lib/docs/scrapers/nginx.rb +10 −4
  198. lib/docs/scrapers/nginx_lua_module.rb +9 −0
  199. lib/docs/scrapers/nim.rb +5 −0
  200. lib/docs/scrapers/node.rb +7 −2
  201. lib/docs/scrapers/nokogiri2.rb +4 −0
  202. lib/docs/scrapers/npm.rb +4 −0
  203. lib/docs/scrapers/numpy.rb +4 −0
  204. lib/docs/scrapers/octave.rb +38 −0
  205. lib/docs/scrapers/openjdk.rb +20 −0
  206. lib/docs/scrapers/opentsdb.rb +4 −0
  207. lib/docs/scrapers/padrino.rb +7 −3
  208. lib/docs/scrapers/pandas.rb +24 −7
  209. lib/docs/scrapers/perl.rb +6 −0
  210. lib/docs/scrapers/phalcon.rb +5 −0
  211. lib/docs/scrapers/phaser.rb +4 −0
  212. lib/docs/scrapers/phoenix.rb +5 −0
  213. lib/docs/scrapers/php.rb +6 −0
  214. lib/docs/scrapers/phpunit.rb +6 −0
  215. lib/docs/scrapers/pony.rb +27 −0
  216. lib/docs/scrapers/postgresql.rb +6 −0
  217. lib/docs/scrapers/pug.rb +4 −0
  218. lib/docs/scrapers/puppeteer.rb +5 −0
  219. lib/docs/scrapers/pygame.rb +7 −2
  220. lib/docs/scrapers/python.rb +9 −4
  221. lib/docs/scrapers/q.rb +4 −0
  222. lib/docs/scrapers/qt.rb +17 −2
  223. lib/docs/scrapers/ramda.rb +7 −3
  224. lib/docs/scrapers/rdoc/minitest.rb +5 −0
  225. lib/docs/scrapers/rdoc/rails.rb +4 −0
  226. lib/docs/scrapers/rdoc/ruby.rb +11 −0
  227. lib/docs/scrapers/react.rb +5 −0
  228. lib/docs/scrapers/react_native.rb +5 −0
  229. lib/docs/scrapers/redis.rb +6 −0
  230. lib/docs/scrapers/redux.rb +4 −0
  231. lib/docs/scrapers/relay.rb +5 −0
  232. lib/docs/scrapers/requirejs.rb +4 −0
  233. lib/docs/scrapers/rethinkdb.rb +4 −0
  234. lib/docs/scrapers/rust.rb +6 −0
  235. lib/docs/scrapers/rxjs.rb +94 −0
  236. lib/docs/scrapers/salt_stack.rb +36 −0
  237. lib/docs/scrapers/sass.rb +4 −0
  238. lib/docs/scrapers/scala.rb +61 −0
  239. lib/docs/scrapers/scikit_image.rb +5 −0
  240. lib/docs/scrapers/scikit_learn.rb +4 −0
  241. lib/docs/scrapers/sinon.rb +5 −0
  242. lib/docs/scrapers/socketio.rb +4 −0
  243. lib/docs/scrapers/sqlite.rb +5 −0
  244. lib/docs/scrapers/statsmodels.rb +4 −0
  245. lib/docs/scrapers/support_tables.rb +6 −0
  246. lib/docs/scrapers/symfony.rb +4 −0
  247. lib/docs/scrapers/tcl_tk.rb +5 −0
  248. lib/docs/scrapers/tensorflow.rb +4 −0
  249. lib/docs/scrapers/terraform.rb +5 −0
  250. lib/docs/scrapers/trio.rb +31 −0
  251. lib/docs/scrapers/twig.rb +5 −0
  252. lib/docs/scrapers/typescript.rb +4 −0
  253. lib/docs/scrapers/underscore.rb +5 −0
  254. lib/docs/scrapers/vagrant.rb +5 −0
  255. lib/docs/scrapers/vue.rb +4 −0
  256. lib/docs/scrapers/vue_router.rb +29 −0
  257. lib/docs/scrapers/vuex.rb +27 −0
  258. lib/docs/scrapers/vulkan.rb +5 −0
  259. lib/docs/scrapers/webpack.rb +4 −0
  260. lib/docs/scrapers/wordpress.rb +44 −0
  261. lib/docs/scrapers/yarn.rb +4 −0
  262. lib/docs/scrapers/yii.rb +4 −0
  263. lib/tasks/assets.thor +3 −0
  264. lib/tasks/sprites.thor +213 −0
  265. lib/tasks/updates.thor +315 −0
  266. public/docs/docs.json +0 −1
  267. public/icons/docs-1.pxm +0 −0
  268. public/icons/docs-1@2x.pxm +0 −0
  269. public/icons/docs-2.pxm +0 −0
  270. public/icons/docs-2@2x.pxm +0 −0
  271. public/icons/docs/bluebird/16@2x.png +0 −0
  272. public/icons/docs/composer/16.png +0 −0
  273. public/icons/docs/composer/16@2x.png +0 −0
  274. public/icons/docs/composer/SOURCE +1 −0
  275. public/icons/docs/cypress/16.png +0 −0
  276. public/icons/docs/cypress/16@2x.png +0 −0
  277. public/icons/docs/cypress/SOURCE +1 −0
  278. public/icons/docs/godot/16.png +0 −0
  279. public/icons/docs/godot/16@2x.png +0 −0
  280. public/icons/docs/octave/16.png +0 −0
  281. public/icons/docs/octave/16@2x.png +0 −0
  282. public/icons/docs/octave/SOURCE +1 −0
  283. public/icons/docs/pony/16.png +0 −0
  284. public/icons/docs/pony/16@2x.png +0 −0
  285. public/icons/docs/pony/SOURCE +1 −0
  286. public/icons/docs/rxjs/16.png +0 −0
  287. public/icons/docs/rxjs/16@2x.png +0 −0
  288. public/icons/docs/rxjs/SOURCE +1 −0
  289. public/icons/docs/saltstack/16.png +0 −0
  290. public/icons/docs/saltstack/16@2x.png +0 −0
  291. public/icons/docs/saltstack/SOURCE +1 −0
  292. public/icons/docs/scala/16.png +0 −0
  293. public/icons/docs/scala/16@2x.png +0 −0
  294. public/icons/docs/trio/16.png +0 −0
  295. public/icons/docs/trio/16@2x.png +0 −0
  296. public/icons/docs/trio/SOURCE +1 −0
  297. public/icons/docs/vue_router/16.png +0 −0
  298. public/icons/docs/vue_router/16@2x.png +0 −0
  299. public/icons/docs/vue_router/SOURCE +1 −0
  300. public/icons/docs/vuex/16.png +0 −0
  301. public/icons/docs/vuex/16@2x.png +0 −0
  302. public/icons/docs/vuex/SOURCE +1 −0
  303. public/icons/docs/wordpress/16.png +0 −0
  304. public/icons/docs/wordpress/16@2x.png +0 −0
  305. public/icons/docs/wordpress/SOURCE +1 −0
  306. public/images/webapp-icon-192.png +0 −0
  307. public/manifest.json +6 −0
  308. test/app_test.rb +0 −81
  309. test/lib/docs/core/scrapers/file_scraper_test.rb +2 −2
  310. views/app.erb +1 −13
  311. views/index.erb +3 −2
  312. views/manifest.erb +0 −14
  313. views/other.erb +3 −2
  314. views/service-worker.js.erb +58 −0
  315. views/unsupported.erb +3 −3
.editorconfig+3−0
@@ -6,3 +6,6 @@ indent_style = space · 5 unmodified lines
6indent_size = 26indent_size = 2
7insert_final_newline = true7insert_final_newline = true
8trim_trailing_whitespace = true8trim_trailing_whitespace = true
9
10[*.md]
11trim_trailing_whitespace = false
.github/CONTRIBUTING.md+4−5
@@ -24,12 +24,12 @@ Want to contribute? Great. Please review the following guidelines carefully and · 23 unmodified lines
24## Requesting new features24## Requesting new features
25 25
261. Search for similar feature requests; someone may have already requested it.261. Search for similar feature requests; someone may have already requested it.
272. Make sure your feature fits DevDocs's [vision](https://github.com/freeCodeCamp/devdocs/blob/master/README.md#vision).272. Make sure your feature fits DevDocs's [vision](../README.md#vision).
283. Provide a clear and detailed explanation of the feature and why it's important to add it.283. Provide a clear and detailed explanation of the feature and why it's important to add it.
29 29
30## Requesting new documentations30## Requesting new documentations
31 31
32Please don't open issues to request new documentations. 32Please don't open issues to request new documentations.
33Use the [Trello board](https://trello.com/b/6BmTulfx/devdocs-documentation) where everyone can vote.33Use the [Trello board](https://trello.com/b/6BmTulfx/devdocs-documentation) where everyone can vote.
34 34
35## Contributing code and features35## Contributing code and features
@@ -55,13 +55,12 @@ In addition to the [guidelines for contributing code](#contributing-code-and-fea · 19 unmodified lines
55* Remove as much content and HTML markup as possible, particularly content not associated with any entry (e.g. introduction, changelog, etc.).55* Remove as much content and HTML markup as possible, particularly content not associated with any entry (e.g. introduction, changelog, etc.).
56* Names must be as short as possible and unique across the documentation.56* Names must be as short as possible and unique across the documentation.
57* The number of types (categories) should ideally be less than 100.57* The number of types (categories) should ideally be less than 100.
58* Don't modify the icon sprite. I'll do it after your pull request is merged.
59 58
60## Updating existing documentations59## Updating existing documentations
61 60
62Please 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.61Please don't submit a pull request updating only the version number and/or the attribution of a documentation. Do submit a pull request if a bigger change is required in the scraper and you've verified that it works.
63 62
64To ask that an existing documentation be updated, please use the [Trello board](https://trello.com/c/2B0hmW7M/52-request-updates-here).63To ask that an existing documentation be updated, first check the latest [documentation versions report](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.
65 64
66## Coding conventions65## Coding conventions
67 66
.github/ISSUE_TEMPLATE.md+1−1
@@ -2,6 +2,6 @@ · 1 unmodified line
2 Please read the contributing guidelines before opening an issue:2 Please read the contributing guidelines before opening an issue:
3 https://github.com/freeCodeCamp/devdocs/blob/master/.github/CONTRIBUTING.md3 https://github.com/freeCodeCamp/devdocs/blob/master/.github/CONTRIBUTING.md
4 4
5 To request a new documentation, or an update of an existing documentation, go here:5 Go here to request a new documentation:
6 https://trello.com/b/6BmTulfx/devdocs-documentation6 https://trello.com/b/6BmTulfx/devdocs-documentation
7-->7-->
.gitignore+1−0
@@ -1,5 +1,6 @@
1.DS_Store1.DS_Store
2.bundle2.bundle
3log
3tmp4tmp
4public/assets5public/assets
5public/fonts6public/fonts
.ruby-version+1−1
@@ -1 +1 @@
12.6.012.6.3
.slugignore+1−2
@@ -1,2 +1 @@
1public/icons1test
2test
.travis.yml+20−1
@@ -1,7 +1,26 @@
1language: ruby1language: ruby
2 2
3addons:
4 apt:
5 packages:
6 - libcurl4-openssl-dev
7
3cache: bundler8cache: bundler
4 9
5before_script:10before_install:
11 - "echo 'gem: --no-document' > ~/.gemrc"
6 - gem update --system12 - gem update --system
7 - gem install bundler13 - gem install bundler
14
15script:
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
19deploy:
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="
CODE_OF_CONDUCT.md+2−0
@@ -0,0 +1,2 @@
1
2> Our Code of Conduct is available here: <https://code-of-conduct.freecodecamp.org/>
Dockerfile+3−2
@@ -1,11 +1,12 @@
1FROM ruby:2.6.01FROM ruby:2.6.3
2 2
3ENV LANG=C.UTF-83ENV LANG=C.UTF-8
4ENV ENABLE_SERVICE_WORKER=true
4 5
5WORKDIR /devdocs6WORKDIR /devdocs
6 7
7RUN apt-get update && \8RUN apt-get update && \
8 apt-get -y install git nodejs && \9 apt-get -y install git nodejs libcurl4 && \
9 gem install bundler && \10 gem install bundler && \
10 rm -rf /var/lib/apt/lists/*11 rm -rf /var/lib/apt/lists/*
11 12
Dockerfile-alpine+3−2
@@ -1,12 +1,13 @@
1FROM ruby:2.6.0-alpine1FROM ruby:2.6.3-alpine
2 2
3ENV LANG=C.UTF-83ENV LANG=C.UTF-8
4ENV ENABLE_SERVICE_WORKER=true
4 5
5WORKDIR /devdocs6WORKDIR /devdocs
6 7
7COPY . /devdocs8COPY . /devdocs
8 9
9RUN apk --update add nodejs build-base libstdc++ gzip git zlib-dev && \10RUN apk --update add nodejs build-base libstdc++ gzip git zlib-dev libcurl && \
10 gem install bundler && \11 gem install bundler && \
11 bundle install --system --without test && \12 bundle install --system --without test && \
12 thor docs:download --all && \13 thor docs:download --all && \
Gemfile+4−1
@@ -1,5 +1,5 @@
1source 'https://rubygems.org'1source 'https://rubygems.org'
2ruby '2.6.0'2ruby '2.6.3'
3 3
4gem 'rake'4gem 'rake'
5gem 'thor'5gem 'thor'
@@ -22,6 +22,8 @@ group :app do · 16 unmodified lines
22 gem 'browser'22 gem 'browser'
23 gem 'sass'23 gem 'sass'
24 gem 'coffee-script'24 gem 'coffee-script'
25 gem 'chunky_png'
26 gem 'sprockets-sass'
25end27end
26 28
27group :production do29group :production do
@@ -40,6 +42,7 @@ group :docs do · 12 unmodified lines
40 gem 'unix_utils', require: false42 gem 'unix_utils', require: false
41 gem 'tty-pager', require: false43 gem 'tty-pager', require: false
42 gem 'net-sftp', '>= 2.1.3.rc2', require: false44 gem 'net-sftp', '>= 2.1.3.rc2', require: false
45 gem 'terminal-table', require: false
43end46end
44 47
45group :test do48group :test do
Gemfile.lock+39−31
@@ -1,49 +1,50 @@
1GEM1GEM
2 remote: https://rubygems.org/2 remote: https://rubygems.org/
3 specs:3 specs:
4 activesupport (5.2.2)4 activesupport (5.2.3)
5 concurrent-ruby (~> 1.0, >= 1.0.2)5 concurrent-ruby (~> 1.0, >= 1.0.2)
6 i18n (>= 0.7, < 2)6 i18n (>= 0.7, < 2)
7 minitest (~> 5.1)7 minitest (~> 5.1)
8 tzinfo (~> 1.1)8 tzinfo (~> 1.1)
9 backports (3.11.4)9 backports (3.15.0)
10 better_errors (2.5.0)10 better_errors (2.5.1)
11 coderay (>= 1.0.0)11 coderay (>= 1.0.0)
12 erubi (>= 1.0.0)12 erubi (>= 1.0.0)
13 rack (>= 0.9.0)13 rack (>= 0.9.0)
14 browser (2.5.3)14 browser (2.6.1)
15 chunky_png (1.3.11)
15 coderay (1.1.2)16 coderay (1.1.2)
16 coffee-script (2.4.1)17 coffee-script (2.4.1)
17 coffee-script-source18 coffee-script-source
18 execjs19 execjs
19 coffee-script-source (1.12.2)20 coffee-script-source (1.12.2)
20 concurrent-ruby (1.1.4)21 concurrent-ruby (1.1.5)
21 daemons (1.3.1)22 daemons (1.3.1)
22 erubi (1.8.0)23 erubi (1.8.0)
23 ethon (0.12.0)24 ethon (0.12.0)
24 ffi (>= 1.3.0)25 ffi (>= 1.3.0)
25 eventmachine (1.2.7)26 eventmachine (1.2.7)
26 execjs (2.7.0)27 execjs (2.7.0)
27 exifr (1.3.5)28 exifr (1.3.6)
28 ffi (1.10.0)29 ffi (1.11.1)
29 fspath (3.1.0)30 fspath (3.1.2)
30 highline (2.0.0)31 highline (2.0.2)
31 html-pipeline (2.10.0)32 html-pipeline (2.11.1)
32 activesupport (>= 2)33 activesupport (>= 2)
33 nokogiri (>= 1.4)34 nokogiri (>= 1.4)
34 i18n (1.5.2)35 i18n (1.6.0)
35 concurrent-ruby (~> 1.0)36 concurrent-ruby (~> 1.0)
36 image_optim (0.26.3)37 image_optim (0.26.5)
37 exifr (~> 1.2, >= 1.2.2)38 exifr (~> 1.2, >= 1.2.2)
38 fspath (~> 3.0)39 fspath (~> 3.0)
39 image_size (>= 1.5, < 3)40 image_size (>= 1.5, < 3)
40 in_threads (~> 1.3)41 in_threads (~> 1.3)
41 progress (~> 3.0, >= 3.0.1)42 progress (~> 3.0, >= 3.0.1)
42 image_optim_pack (0.5.1.20190105)43 image_optim_pack (0.5.6)
43 fspath (>= 2.1, < 4)44 fspath (>= 2.1, < 4)
44 image_optim (~> 0.19)45 image_optim (~> 0.19)
45 image_size (2.0.0)46 image_size (2.0.2)
46 in_threads (1.5.1)47 in_threads (1.5.3)
47 method_source (0.9.2)48 method_source (0.9.2)
48 mini_portile2 (2.4.0)49 mini_portile2 (2.4.0)
49 minitest (5.11.3)50 minitest (5.11.3)
@@ -51,19 +52,19 @@ GEM · 1 unmodified line
51 mustermann (1.0.3)52 mustermann (1.0.3)
52 net-sftp (3.0.0.beta1)53 net-sftp (3.0.0.beta1)
53 net-ssh (>= 5.0.0, < 6.0.0)54 net-ssh (>= 5.0.0, < 6.0.0)
54 net-ssh (5.1.0)55 net-ssh (5.2.0)
55 newrelic_rpm (5.7.0.350)56 newrelic_rpm (6.5.0.357)
56 nokogiri (1.10.1)57 nokogiri (1.10.3)
57 mini_portile2 (~> 2.4.0)58 mini_portile2 (~> 2.4.0)
58 options (2.3.2)59 options (2.3.2)
59 progress (3.5.0)60 progress (3.5.2)
60 progress_bar (1.3.0)61 progress_bar (1.3.0)
61 highline (>= 1.6, < 3)62 highline (>= 1.6, < 3)
62 options (~> 2.3.0)63 options (~> 2.3.0)
63 pry (0.12.2)64 pry (0.12.2)
64 coderay (~> 1.1.0)65 coderay (~> 1.1.0)
65 method_source (~> 0.9.0)66 method_source (~> 0.9.0)
66 rack (2.0.6)67 rack (2.0.7)
67 rack-protection (2.0.5)68 rack-protection (2.0.5)
68 rack69 rack
69 rack-ssl-enforcer (0.2.9)70 rack-ssl-enforcer (0.2.9)
@@ -74,7 +75,7 @@ GEM · 4 unmodified lines
74 rb-inotify (0.10.0)75 rb-inotify (0.10.0)
75 ffi (~> 1.0)76 ffi (~> 1.0)
76 rr (1.2.1)77 rr (1.2.1)
77 sass (3.7.3)78 sass (3.7.4)
78 sass-listen (~> 4.0.0)79 sass-listen (~> 4.0.0)
79 sass-listen (4.0.0)80 sass-listen (4.0.0)
80 rb-fsevent (~> 0.9, >= 0.9.4)81 rb-fsevent (~> 0.9, >= 0.9.4)
@@ -96,11 +97,15 @@ GEM · 15 unmodified lines
96 rack (> 1, < 3)97 rack (> 1, < 3)
97 sprockets-helpers (1.2.1)98 sprockets-helpers (1.2.1)
98 sprockets (>= 2.2)99 sprockets (>= 2.2)
99 strings (0.1.4)100 sprockets-sass (2.0.0.beta2)
100 strings-ansi (~> 0.1.0)101 sprockets (>= 2.0, < 4.0)
101 unicode-display_width (~> 1.4.0)102 strings (0.1.5)
102 unicode_utils (~> 1.4.0)103 strings-ansi (~> 0.1)
104 unicode-display_width (~> 1.5)
105 unicode_utils (~> 1.4)
103 strings-ansi (0.1.0)106 strings-ansi (0.1.0)
107 terminal-table (1.8.0)
108 unicode-display_width (~> 1.1, >= 1.1.1)
104 thin (1.7.2)109 thin (1.7.2)
105 daemons (~> 1.0, >= 1.0.9)110 daemons (~> 1.0, >= 1.0.9)
106 eventmachine (~> 1.0, >= 1.0.4)111 eventmachine (~> 1.0, >= 1.0.4)
@@ -108,19 +113,19 @@ GEM · 1 unmodified line
108 thor (0.20.3)113 thor (0.20.3)
109 thread_safe (0.3.6)114 thread_safe (0.3.6)
110 tilt (2.0.9)115 tilt (2.0.9)
111 tty-pager (0.12.0)116 tty-pager (0.12.1)
112 strings (~> 0.1.4)117 strings (~> 0.1.4)
113 tty-screen (~> 0.6)118 tty-screen (~> 0.6)
114 tty-which (~> 0.4)119 tty-which (~> 0.4)
115 tty-screen (0.6.5)120 tty-screen (0.7.0)
116 tty-which (0.4.0)121 tty-which (0.4.1)
117 typhoeus (1.3.1)122 typhoeus (1.3.1)
118 ethon (>= 0.9.0)123 ethon (>= 0.9.0)
119 tzinfo (1.2.5)124 tzinfo (1.2.5)
120 thread_safe (~> 0.1)125 thread_safe (~> 0.1)
121 uglifier (4.1.20)126 uglifier (4.1.20)
122 execjs (>= 0.3.0, < 3)127 execjs (>= 0.3.0, < 3)
123 unicode-display_width (1.4.1)128 unicode-display_width (1.6.0)
124 unicode_utils (1.4.0)129 unicode_utils (1.4.0)
125 unix_utils (0.0.15)130 unix_utils (0.0.15)
126 yajl-ruby (1.4.1)131 yajl-ruby (1.4.1)
@@ -132,6 +137,7 @@ DEPENDENCIES · 5 unmodified lines
132 activesupport (~> 5.2)137 activesupport (~> 5.2)
133 better_errors138 better_errors
134 browser139 browser
140 chunky_png
135 coffee-script141 coffee-script
136 erubi142 erubi
137 html-pipeline143 html-pipeline
@@ -153,6 +159,8 @@ DEPENDENCIES · 15 unmodified lines
153 sinatra-contrib159 sinatra-contrib
154 sprockets160 sprockets
155 sprockets-helpers161 sprockets-helpers
162 sprockets-sass
163 terminal-table
156 thin164 thin
157 thor165 thor
158 tty-pager166 tty-pager
@@ -162,7 +170,7 @@ DEPENDENCIES · 3 unmodified lines
162 yajl-ruby170 yajl-ruby
163 171
164RUBY VERSION172RUBY VERSION
165 ruby 2.6.0p0173 ruby 2.6.3p62
166 174
167BUNDLED WITH175BUNDLED WITH
168 1.17.2176 2.0.2
README.md+9−9
@@ -59,14 +59,14 @@ The web app is all client-side JavaScript, written in [CoffeeScript](http://coff · 58 unmodified lines
59 59
60Many 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.60Many 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.
61 61
62Another 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.62Another 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.
63 63
64DevDocs being a developer tool, the browser requirements are high:64DevDocs being a developer tool, the browser requirements are high:
65 65
66* Recent versions of Firefox, Chrome, or Opera66* Recent versions of Firefox, Chrome, or Opera
67* Safari 9.1+67* Safari 11.1+
68* Edge 16+68* Edge 17+
69* iOS 10+69* iOS 11.3+
70 70
71This allows the code to take advantage of the latest DOM and HTML5 APIs and make developing DevDocs a lot more fun!71This allows the code to take advantage of the latest DOM and HTML5 APIs and make developing DevDocs a lot more fun!
72 72
@@ -89,7 +89,7 @@ These modifications are applied via a set of filters using the [HTML::Pipeline]( · 16 unmodified lines
89 89
90The 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.).90The 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.).
91 91
92More information about [scrapers](https://github.com/freeCodeCamp/devdocs/blob/master/docs/Scraper-Reference.md) and [filters](https://github.com/freeCodeCamp/devdocs/blob/master/docs/Filter-Reference.md) is available in the `docs` folder.92More information about [scrapers](./docs/scraper-reference.md) and [filters](./docs/filter-reference.md) is available in the `docs` folder.
93 93
94## Available Commands94## Available Commands
95 95
@@ -129,7 +129,7 @@ If multiple versions of Ruby are installed on your system, commands must be run · 33 unmodified lines
129 129
130## Contributing130## Contributing
131 131
132Contributions are welcome. Please read the [contributing guidelines](https://github.com/freeCodeCamp/devdocs/blob/master/.github/CONTRIBUTING.md).132Contributions are welcome. Please read the [contributing guidelines](./.github/CONTRIBUTING.md).
133 133
134## Documentation134## Documentation
135 135
@@ -144,7 +144,7 @@ Contributions are welcome. Please read the [contributing guidelines](https://git · 8 unmodified lines
144* [Ubuntu Touch app](https://uappexplorer.com/app/devdocsunofficial.berkes)144* [Ubuntu Touch app](https://uappexplorer.com/app/devdocsunofficial.berkes)
145* [Sublime Text plugin](https://sublime.wbond.net/packages/DevDocs)145* [Sublime Text plugin](https://sublime.wbond.net/packages/DevDocs)
146* [Atom plugin](https://atom.io/packages/devdocs)146* [Atom plugin](https://atom.io/packages/devdocs)
147* [Brackets extension](https://github.com/gruehle/dev-docs-viewer) 147* [Brackets extension](https://github.com/gruehle/dev-docs-viewer)
148* [Fluid](http://fluidapp.com) for turning DevDocs into a real OS X app148* [Fluid](http://fluidapp.com) for turning DevDocs into a real OS X app
149* [GTK shell / Vim integration](https://github.com/naquad/devdocs-shell)149* [GTK shell / Vim integration](https://github.com/naquad/devdocs-shell)
150* [Emacs lookup](https://github.com/skeeto/devdocs-lookup)150* [Emacs lookup](https://github.com/skeeto/devdocs-lookup)
@@ -156,13 +156,13 @@ Contributions are welcome. Please read the [contributing guidelines](https://git · 5 unmodified lines
156* [Doc Browser](https://github.com/qwfy/doc-browser) is a native Linux app that supports DevDocs docsets156* [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 headerbar157* [GNOME Application](https://github.com/hardpixel/devdocs-desktop) GTK3 application with search integrated in headerbar
158* [macOS Application](https://github.com/dteoh/devdocs-macos)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 with AppCache enabled159* [Android Application](https://github.com/Merith-TK/devdocs_webapp_kotlin) is a fully working, advanced WebView
160 160
161## Copyright / License161## Copyright / License
162 162
163Copyright 2013-2019 Thibaut Courouble and [other contributors](https://github.com/freeCodeCamp/devdocs/graphs/contributors)163Copyright 2013-2019 Thibaut Courouble and [other contributors](https://github.com/freeCodeCamp/devdocs/graphs/contributors)
164 164
165This software is licensed under the terms of the Mozilla Public License v2.0. See the [COPYRIGHT](https://github.com/freeCodeCamp/devdocs/blob/master/COPYRIGHT) and [LICENSE](https://github.com/freeCodeCamp/devdocs/blob/master/LICENSE) files.165This software is licensed under the terms of the Mozilla Public License v2.0. See the [COPYRIGHT](./COPYRIGHT) and [LICENSE](./LICENSE) files.
166 166
167Please 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.167Please 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.
168 168
assets/images/.gitignore+1−0
@@ -0,0 +1 @@
1sprites/**/*
assets/images/docs-1.png+0−0

Binary file not shown.

assets/images/docs-1@2x.png+0−0

Binary file not shown.

assets/images/docs-2.png+0−0

Binary file not shown.

assets/images/docs-2@2x.png+0−0

Binary file not shown.

assets/javascripts/app/app.coffee+14−9
@@ -13,10 +13,12 @@ · 12 unmodified lines
13 13
14 @el = $('._app')14 @el = $('._app')
15 @localStorage = new LocalStorageStore15 @localStorage = new LocalStorageStore
16 @appCache = new app.AppCache if app.AppCache.isEnabled()16 @serviceWorker = new app.ServiceWorker if app.ServiceWorker.isEnabled()
17 @settings = new app.Settings17 @settings = new app.Settings
18 @db = new app.DB()18 @db = new app.DB()
19 19
20 @settings.initLayout()
21
20 @docs = new app.collections.Docs22 @docs = new app.collections.Docs
21 @disabledDocs = new app.collections.Docs23 @disabledDocs = new app.collections.Docs
22 @entries = new app.collections.Entries24 @entries = new app.collections.Entries
@@ -76,7 +78,7 @@ · 53 unmodified lines
76 .install()78 .install()
77 @previousErrorHandler = onerror79 @previousErrorHandler = onerror
78 window.onerror = @onWindowError.bind(@)80 window.onerror = @onWindowError.bind(@)
79 CookieStore.onBlocked = @onCookieBlocked81 CookiesStore.onBlocked = @onCookieBlocked
80 return82 return
81 83
82 bootOne: ->84 bootOne: ->
@@ -138,7 +140,10 @@ · 55 unmodified lines
138 @docs.sort()140 @docs.sort()
139 @initDoc(doc)141 @initDoc(doc)
140 @saveDocs()142 @saveDocs()
141 _onSuccess()143 if app.settings.get('autoInstall')
144 doc.install(_onSuccess, onError)
145 else
146 _onSuccess()
142 return147 return
143 148
144 doc.load onSuccess, onError, writeCache: true149 doc.load onSuccess, onError, writeCache: true
@@ -147,7 +152,7 @@ · 2 unmodified lines
147 saveDocs: ->152 saveDocs: ->
148 @settings.setDocs(doc.slug for doc in @docs.all())153 @settings.setDocs(doc.slug for doc in @docs.all())
149 @db.migrate()154 @db.migrate()
150 @appCache?.updateInBackground()155 @serviceWorker?.updateInBackground()
151 156
152 welcomeBack: ->157 welcomeBack: ->
153 visitCount = @settings.get('count')158 visitCount = @settings.get('count')
@@ -167,14 +172,14 @@ · 13 unmodified lines
167 reload: ->172 reload: ->
168 @docs.clearCache()173 @docs.clearCache()
169 @disabledDocs.clearCache()174 @disabledDocs.clearCache()
170 if @appCache then @appCache.reload() else @reboot()175 if @serviceWorker then @serviceWorker.reload() else @reboot()
171 return176 return
172 177
173 reset: ->178 reset: ->
174 @localStorage.reset()179 @localStorage.reset()
175 @settings.reset()180 @settings.reset()
176 @db?.reset()181 @db?.reset()
177 @appCache?.update()182 @serviceWorker?.update()
178 window.location = '/'183 window.location = '/'
179 return184 return
180 185
@@ -193,9 +198,9 @@ · 12 unmodified lines
193 return198 return
194 199
195 indexHost: ->200 indexHost: ->
196 # Can't load the index files from the host/CDN when applicationCache is201 # Can't load the index files from the host/CDN when service worker is
197 # enabled because it doesn't support caching URLs that use CORS.202 # enabled because it doesn't support caching URLs that use CORS.
198 @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']
199 204
200 onBootError: (args...) ->205 onBootError: (args...) ->
201 @trigger 'bootError'206 @trigger 'bootError'
@@ -252,7 +257,7 @@ · 50 unmodified lines
252 matchMedia: !!window.matchMedia257 matchMedia: !!window.matchMedia
253 insertAdjacentHTML: !!document.body.insertAdjacentHTML258 insertAdjacentHTML: !!document.body.insertAdjacentHTML
254 defaultPrevented: document.createEvent('CustomEvent').defaultPrevented is false259 defaultPrevented: document.createEvent('CustomEvent').defaultPrevented is false
255 cssVariables: CSS.supports and CSS.supports('(--t: 0)')260 cssVariables: !!CSS?.supports?('(--t: 0)')
256 261
257 for key, value of features when not value262 for key, value of features when not value
258 Raven.captureMessage "unsupported/#{key}", level: 'info'263 Raven.captureMessage "unsupported/#{key}", level: 'info'
assets/javascripts/app/appcache.coffee+0−42
@@ -1,42 +0,0 @@
1class 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', -> app.reboot()
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
assets/javascripts/app/config.coffee.erb+3−0
@@ -13,3 +13,6 @@ app.config = · 12 unmodified lines
13 version: <%= Time.now.to_i %>13 version: <%= Time.now.to_i %>
14 release: <%= Time.now.utc.httpdate.to_json %>14 release: <%= Time.now.utc.httpdate.to_json %>
15 mathml_stylesheet: '<%= App.cdn_origin %>/mathml.css'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' %>
assets/javascripts/app/serviceworker.coffee+49−0
@@ -0,0 +1,49 @@
1class 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
assets/javascripts/app/settings.coffee+34−1
@@ -5,11 +5,13 @@ class app.Settings · 4 unmodified lines
5 'manualUpdate'5 'manualUpdate'
6 'fastScroll'6 'fastScroll'
7 'arrowScroll'7 'arrowScroll'
8 'analyticsConsent'
8 'docs'9 'docs'
9 'dark'10 'dark'
10 'layout'11 'layout'
11 'size'12 'size'
12 'tips'13 'tips'
14 'autoInstall'
13 ]15 ]
14 16
15 INTERNAL_KEYS = [17 INTERNAL_KEYS = [
@@ -19,6 +21,8 @@ class app.Settings · 3 unmodified lines
19 'news'21 'news'
20 ]22 ]
21 23
24 LAYOUTS: ['_max-width', '_sidebar-hidden', '_native-scrollbars']
25
22 @defaults:26 @defaults:
23 count: 027 count: 0
24 hideDisabled: false28 hideDisabled: false
@@ -26,9 +30,10 @@ class app.Settings · 1 unmodified line
26 news: 030 news: 0
27 manualUpdate: false31 manualUpdate: false
28 schema: 132 schema: 1
33 analyticsConsent: false
29 34
30 constructor: ->35 constructor: ->
31 @store = new CookieStore36 @store = new CookiesStore
32 @cache = {}37 @cache = {}
33 38
34 get: (key) ->39 get: (key) ->
@@ -38,6 +43,7 @@ class app.Settings · 3 unmodified lines
38 set: (key, value) ->43 set: (key, value) ->
39 @store.set(key, value)44 @store.set(key, value)
40 delete @cache[key]45 delete @cache[key]
46 @toggleDark(value) if key == 'dark'
41 return47 return
42 48
43 del: (key) ->49 del: (key) ->
@@ -63,6 +69,8 @@ class app.Settings · 19 unmodified lines
63 return69 return
64 70
65 setLayout: (name, enable) ->71 setLayout: (name, enable) ->
72 @toggleLayout(name, enable)
73
66 layout = (@store.get('layout') || '').split(' ')74 layout = (@store.get('layout') || '').split(' ')
67 $.arrayDelete(layout, '')75 $.arrayDelete(layout, '')
68 76
@@ -104,3 +112,28 @@ class app.Settings · 35 unmodified lines
104 @store.reset()112 @store.reset()
105 @cache = {}113 @cache = {}
106 return114 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
assets/javascripts/app/update_checker.coffee+3−3
@@ -3,13 +3,13 @@ class app.UpdateChecker · 2 unmodified lines
3 @lastCheck = Date.now()3 @lastCheck = Date.now()
4 4
5 $.on window, 'focus', @onFocus5 $.on window, 'focus', @onFocus
6 app.appCache.on 'updateready', @onUpdateReady if app.appCache6 app.serviceWorker?.on 'updateready', @onUpdateReady
7 7
8 setTimeout @checkDocs, 08 setTimeout @checkDocs, 0
9 9
10 check: ->10 check: ->
11 if app.appCache11 if app.serviceWorker
12 app.appCache.update()12 app.serviceWorker.update()
13 else13 else
14 ajax14 ajax
15 url: $('script[src*="application"]').getAttribute('src')15 url: $('script[src*="application"]').getAttribute('src')
assets/javascripts/lib/cookies_store.coffeerenamed from assets/javascripts/lib/cookie_store.coffee+5−1
@@ -1,4 +1,8 @@
1class @CookieStore1class @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 INT = /^\d+$/6 INT = /^\d+$/
3 7
4 @onBlocked: ->8 @onBlocked: ->
assets/javascripts/lib/favicon.coffee+64−0
@@ -0,0 +1,64 @@
1defaultUrl = null
2currentSlug = null
3
4imageCache = {}
5urlCache = {}
6
7withImage = (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
assets/javascripts/lib/page.coffee+19−1
@@ -199,5 +199,23 @@ page.track = (fn) -> · 198 unmodified lines
199 return199 return
200 200
201track = ->201track = ->
202 tracker.call() for tracker in trackers202 return unless app.config.env == 'production'
203
204 consentGiven = Cookies.get('analyticsConsent')
205 consentAsked = Cookies.get('analyticsConsentAsked')
206
207 if consentGiven == '1'
208 tracker.call() for tracker in trackers
209 else if consentGiven == undefined and consentAsked == undefined
210 # Only ask for consent once per browser session
211 Cookies.set('analyticsConsentAsked', '1')
212
213 new app.views.Notif 'AnalyticsConsent', autoHide: null
214 return
215
216@resetAnalytics = ->
217 for cookie in document.cookie.split(/;\s?/)
218 name = cookie.split('=')[0]
219 if name[0] == '_' && name[1] != '_'
220 Cookies.expire(name)
203 return221 return
assets/javascripts/lib/util.coffee+4−0
@@ -352,6 +352,10 @@ isIE = null · 351 unmodified lines
352$.isIE = ->352$.isIE = ->
353 isIE ?= navigator.userAgent?.indexOf('MSIE') >= 0 || navigator.userAgent?.indexOf('rv:11.0') >= 0353 isIE ?= navigator.userAgent?.indexOf('MSIE') >= 0 || navigator.userAgent?.indexOf('rv:11.0') >= 0
354 354
355isChromeForAndroid = null
356$.isChromeForAndroid = ->
357 isChromeForAndroid ?= navigator.userAgent?.indexOf('Android') >= 0 && /Chrome\/([.0-9])+ Mobile/.test(navigator.userAgent)
358
355isAndroid = null359isAndroid = null
356$.isAndroid = ->360$.isAndroid = ->
357 isAndroid ?= navigator.userAgent?.indexOf('Android') >= 0361 isAndroid ?= navigator.userAgent?.indexOf('Android') >= 0
assets/javascripts/models/doc.coffee+3−1
@@ -142,4 +142,6 @@ class app.models.Doc extends app.Model · 141 unmodified lines
142 return142 return
143 143
144 isOutdated: (status) ->144 isOutdated: (status) ->
145 status and status.installed and @mtime isnt status.mtime145 return false if not status
146 isInstalled = status.installed or app.settings.get('autoInstall')
147 isInstalled and @mtime isnt status.mtime
assets/javascripts/news.json+8−0
@@ -1,4 +1,12 @@
1[1[
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 ],
2 [10 [
3 "2018-09-23",11 "2018-09-23",
4 "New documentations: <a href=\"/puppeteer/\">Puppeteer</a> and <a href=\"/handlebars/\">Handlebars.js</a>"12 "New documentations: <a href=\"/puppeteer/\">Puppeteer</a> and <a href=\"/handlebars/\">Handlebars.js</a>"
assets/javascripts/templates/error_tmpl.coffee+5−5
@@ -12,8 +12,8 @@ app.templates.notFoundPage = -> · 11 unmodified lines
12 12
13app.templates.pageLoadError = ->13app.templates.pageLoadError = ->
14 error """ The page failed to load. """,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>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 keep seeing this, you're likely behind a proxy or firewall that blocks cross-domain requests. """,16 If you're online and you keep seeing this, you're likely behind a proxy or firewall that blocks cross-domain requests. """,
17 """ #{back} &middot; <a href="/##{location.pathname}" target="_top" class="_error-link">Reload</a>17 """ #{back} &middot; <a href="/##{location.pathname}" target="_top" class="_error-link">Reload</a>
18 &middot; <a href="#" class="_error-link" data-retry>Retry</a> """18 &middot; <a href="#" class="_error-link" data-retry>Retry</a> """
19 19
@@ -57,9 +57,9 @@ app.templates.unsupportedBrowser = """ · 37 unmodified lines
57 <p class="_fail-text">DevDocs is an API documentation browser which supports the following browsers:57 <p class="_fail-text">DevDocs is an API documentation browser which supports the following browsers:
58 <ul class="_fail-list">58 <ul class="_fail-list">
59 <li>Recent versions of Firefox, Chrome, or Opera59 <li>Recent versions of Firefox, Chrome, or Opera
60 <li>Safari 9.1+60 <li>Safari 11.1+
61 <li>Edge 16+61 <li>Edge 17+
62 <li>iOS 10+62 <li>iOS 11.3+
63 </ul>63 </ul>
64 <p class="_fail-text">64 <p class="_fail-text">
65 If you're unable to upgrade, we apologize.65 If you're unable to upgrade, we apologize.
assets/javascripts/templates/notif_tmpl.coffee+6−0
@@ -68,3 +68,9 @@ app.templates.notifShare = -> · 67 unmodified lines
68app.templates.notifUpdateDocs = ->68app.templates.notifUpdateDocs = ->
69 textNotif """ Documentation updates available. """,69 textNotif """ Documentation updates available. """,
70 """ <a href="/offline">Install them</a> as soon as possible to avoid broken pages. """70 """ <a href="/offline">Install them</a> as soon as possible to avoid broken pages. """
71
72app.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> """
assets/javascripts/templates/pages/about_tmpl.coffee+66−10
@@ -73,7 +73,8 @@ app.templates.aboutPage = -> """ · 72 unmodified lines
73 <ul>73 <ul>
74 <li><a href="https://devdocs.io">devdocs.io</a> ("App") is operated by <a href="https://www.freecodecamp.org/">freeCodeCamp</a> ("We").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.75 <li>We do not collect personal information through the app.
76 <li>We use Google Analytics, Gauges and Sentry to collect anonymous traffic information and improve 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.
77 <li>The app uses cookies to store user preferences.78 <li>The app uses cookies to store user preferences.
78 <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.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.
79 <li>If you have any questions regarding privacy, please email <a href="mailto:privacy@freecodecamp.org">privacy@freecodecamp.org</a>.80 <li>If you have any questions regarding privacy, please email <a href="mailto:privacy@freecodecamp.org">privacy@freecodecamp.org</a>.
@@ -87,7 +88,7 @@ credits = [ · 7 unmodified lines
87 'https://creativecommons.org/licenses/by/4.0/'88 'https://creativecommons.org/licenses/by/4.0/'
88 ], [89 ], [
89 'Ansible',90 'Ansible',
90 '2012-2018 Michael DeHaan<br>&copy; 2018 Red Hat, Inc.',91 '2012-2018 Michael DeHaan<br>&copy; 2018–2019 Red Hat, Inc.',
91 'GPLv3',92 'GPLv3',
92 'https://raw.githubusercontent.com/ansible/ansible/devel/COPYING'93 'https://raw.githubusercontent.com/ansible/ansible/devel/COPYING'
93 ], [94 ], [
@@ -122,7 +123,7 @@ credits = [ · 28 unmodified lines
122 'https://raw.githubusercontent.com/petkaantonov/bluebird/master/LICENSE'123 'https://raw.githubusercontent.com/petkaantonov/bluebird/master/LICENSE'
123 ], [124 ], [
124 'Bootstrap',125 'Bootstrap',
125 '2011-2018 Twitter, Inc.<br>2011-2018 The Bootstrap Authors',126 '2011-2019 Twitter, Inc.<br>2011-2019 The Bootstrap Authors',
126 'CC BY',127 'CC BY',
127 'https://creativecommons.org/licenses/by/3.0/'128 'https://creativecommons.org/licenses/by/3.0/'
128 ], [129 ], [
@@ -162,7 +163,7 @@ credits = [ · 33 unmodified lines
162 'https://github.com/clojure/clojure/blob/master/epl-v10.html'163 'https://github.com/clojure/clojure/blob/master/epl-v10.html'
163 ], [164 ], [
164 'CMake',165 'CMake',
165 '2000-2018 Kitware, Inc. and Contributors',166 '2000-2019 Kitware, Inc. and Contributors',
166 'BSD',167 'BSD',
167 'https://cmake.org/licensing/'168 'https://cmake.org/licensing/'
168 ], [169 ], [
@@ -185,6 +186,11 @@ credits = [ · 16 unmodified lines
185 '2009-2018 Jeremy Ashkenas',186 '2009-2018 Jeremy Ashkenas',
186 'MIT',187 'MIT',
187 'https://raw.githubusercontent.com/jashkenas/coffeescript/master/LICENSE'188 'https://raw.githubusercontent.com/jashkenas/coffeescript/master/LICENSE'
189 ], [
190 'Composer',
191 'Nils Adermann, Jordi Boggiano',
192 'MIT',
193 'https://raw.githubusercontent.com/composer/composer/master/LICENSE'
188 ], [194 ], [
189 'Cordova',195 'Cordova',
190 '2012-2018 The Apache Software Foundation',196 '2012-2018 The Apache Software Foundation',
@@ -200,6 +206,11 @@ credits = [ · 9 unmodified lines
200 '2012-2017 Manas Technology Solutions',206 '2012-2017 Manas Technology Solutions',
201 'Apache',207 'Apache',
202 'https://raw.githubusercontent.com/crystal-lang/crystal/master/LICENSE'208 'https://raw.githubusercontent.com/crystal-lang/crystal/master/LICENSE'
209 ], [
210 'Cypress',
211 '2017 Cypress.io',
212 'MIT',
213 'https://raw.githubusercontent.com/cypress-io/cypress-documentation/develop/LICENSE.md'
203 ], [214 ], [
204 'D',215 'D',
205 '1999-2018 The D Language Foundation',216 '1999-2018 The D Language Foundation',
@@ -222,7 +233,7 @@ credits = [ · 16 unmodified lines
222 'https://raw.githubusercontent.com/django/django/master/LICENSE'233 'https://raw.githubusercontent.com/django/django/master/LICENSE'
223 ], [234 ], [
224 'Docker',235 'Docker',
225 '2017 Docker, Inc.<br>Docker and the Docker logo are trademarks of Docker, Inc.',236 '2019 Docker, Inc.<br>Docker and the Docker logo are trademarks of Docker, Inc.',
226 'Apache',237 'Apache',
227 'https://raw.githubusercontent.com/docker/docker.github.io/master/LICENSE'238 'https://raw.githubusercontent.com/docker/docker.github.io/master/LICENSE'
228 ], [239 ], [
@@ -387,7 +398,7 @@ credits = [ · 158 unmodified lines
387 'https://raw.githubusercontent.com/koajs/koa/master/LICENSE'398 'https://raw.githubusercontent.com/koajs/koa/master/LICENSE'
388 ], [399 ], [
389 'Kotlin',400 'Kotlin',
390 '2010-2018 JetBrains s.r.o.',401 '2010-2019 JetBrains s.r.o.',
391 'Apache',402 'Apache',
392 'https://raw.githubusercontent.com/JetBrains/kotlin/master/license/LICENSE.txt'403 'https://raw.githubusercontent.com/JetBrains/kotlin/master/license/LICENSE.txt'
393 ], [404 ], [
@@ -397,7 +408,7 @@ credits = [ · 3 unmodified lines
397 'https://raw.githubusercontent.com/laravel/framework/master/LICENSE.txt'408 'https://raw.githubusercontent.com/laravel/framework/master/LICENSE.txt'
398 ], [409 ], [
399 'Leaflet',410 'Leaflet',
400 '2010-2018 Vladimir Agafonkin<br>&copy; 2010-2011, CloudMade<br>Maps &copy; OpenStreetMap contributors.',411 '2010-2019 Vladimir Agafonkin<br>&copy; 2010-2011, CloudMade<br>Maps &copy; OpenStreetMap contributors.',
401 'BSD',412 'BSD',
402 'https://raw.githubusercontent.com/Leaflet/Leaflet/master/LICENSE'413 'https://raw.githubusercontent.com/Leaflet/Leaflet/master/LICENSE'
403 ], [414 ], [
@@ -472,7 +483,7 @@ credits = [ · 68 unmodified lines
472 'https://github.com/LearnBoost/mongoose/blob/master/README.md#license'483 'https://github.com/LearnBoost/mongoose/blob/master/README.md#license'
473 ], [484 ], [
474 'nginx',485 'nginx',
475 '2002-2018 Igor Sysoev<br>&copy; 2011-2018 Nginx, Inc.',486 '2002-2019 Igor Sysoev<br>&copy; 2011-2019 Nginx, Inc.',
476 'BSD',487 'BSD',
477 'http://nginx.org/LICENSE'488 'http://nginx.org/LICENSE'
478 ], [489 ], [
@@ -505,6 +516,11 @@ credits = [ · 26 unmodified lines
505 '2008-2017 NumPy Developers',516 '2008-2017 NumPy Developers',
506 'NumPy',517 'NumPy',
507 'https://raw.githubusercontent.com/numpy/numpy/master/LICENSE.txt'518 'https://raw.githubusercontent.com/numpy/numpy/master/LICENSE.txt'
519 ], [
520 'Octave',
521 '1996-2018 John W. Eaton',
522 'Octave',
523 'https://octave.org/doc/interpreter/'
508 ], [524 ], [
509 'OpenJDK',525 'OpenJDK',
510 '1993-2017, Oracle and/or its affiliates. All rights reserved.<br>Licensed under the GNU General Public License, version 2, with the Classpath Exception.<br>Various third party code in OpenJDK is licensed under different licenses.<br>Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.',526 '1993-2017, Oracle and/or its affiliates. All rights reserved.<br>Licensed under the GNU General Public License, version 2, with the Classpath Exception.<br>Various third party code in OpenJDK is licensed under different licenses.<br>Java and OpenJDK are trademarks or registered trademarks of Oracle and/or its affiliates.',
@@ -517,7 +533,7 @@ credits = [ · 6 unmodified lines
517 'https://raw.githubusercontent.com/OpenTSDB/opentsdb.net/gh-pages/COPYING.LESSER'533 'https://raw.githubusercontent.com/OpenTSDB/opentsdb.net/gh-pages/COPYING.LESSER'
518 ], [534 ], [
519 'Padrino',535 'Padrino',
520 '2010-2016 Padrino',536 '2010-2019 Padrino',
521 'MIT',537 'MIT',
522 'https://raw.githubusercontent.com/padrino/padrino-framework/master/padrino/LICENSE.txt'538 'https://raw.githubusercontent.com/padrino/padrino-framework/master/padrino/LICENSE.txt'
523 ], [539 ], [
@@ -555,6 +571,11 @@ credits = [ · 31 unmodified lines
555 '2005-2017 Sebastian Bergmann',571 '2005-2017 Sebastian Bergmann',
556 'CC BY',572 'CC BY',
557 'https://creativecommons.org/licenses/by/3.0/'573 'https://creativecommons.org/licenses/by/3.0/'
574 ], [
575 'Pony',
576 '2016-2018, The Pony Developers & 2014-2015, Causality Ltd.',
577 'BSD',
578 'https://raw.githubusercontent.com/ponylang/ponyc/master/LICENSE'
558 ], [579 ], [
559 'PostgreSQL',580 'PostgreSQL',
560 '1996-2018 The PostgreSQL Global Development Group<br>&copy; 1994 The Regents of the University of California',581 '1996-2018 The PostgreSQL Global Development Group<br>&copy; 1994 The Regents of the University of California',
@@ -567,7 +588,7 @@ credits = [ · 6 unmodified lines
567 'https://raw.githubusercontent.com/GoogleChrome/puppeteer/master/LICENSE'588 'https://raw.githubusercontent.com/GoogleChrome/puppeteer/master/LICENSE'
568 ], [589 ], [
569 'Pygame',590 'Pygame',
570 'Pygame Developpers',591 'Pygame Developers',
571 'LGPLv2.1',592 'LGPLv2.1',
572 'https://raw.githubusercontent.com/pygame/pygame/master/LICENSE'593 'https://raw.githubusercontent.com/pygame/pygame/master/LICENSE'
573 ], [594 ], [
@@ -630,11 +651,26 @@ credits = [ · 56 unmodified lines
630 '2010 The Rust Project Developers',651 '2010 The Rust Project Developers',
631 'MIT',652 'MIT',
632 'https://raw.githubusercontent.com/rust-lang/rust/master/LICENSE-MIT'653 'https://raw.githubusercontent.com/rust-lang/rust/master/LICENSE-MIT'
654 ], [
655 'RxJS',
656 '2015-2018 Google, Inc., Netflix, Inc., Microsoft Corp. and contributors',
657 'Apache',
658 'https://raw.githubusercontent.com/ReactiveX/rxjs/master/LICENSE.txt'
659 ], [
660 'Salt Stack',
661 '2019 SaltStack',
662 'Apache',
663 'https://raw.githubusercontent.com/saltstack/salt/develop/LICENSE'
633 ], [664 ], [
634 'Sass',665 'Sass',
635 '2006-2016 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein',666 '2006-2016 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein',
636 'MIT',667 'MIT',
637 'https://raw.githubusercontent.com/sass/sass/stable/MIT-LICENSE'668 'https://raw.githubusercontent.com/sass/sass/stable/MIT-LICENSE'
669 ], [
670 'Scala',
671 '2002-2019 EPFL, with contributions from Lightbend',
672 'Apache',
673 'https://raw.githubusercontent.com/scala/scala-lang/master/license.md'
638 ], [674 ], [
639 'scikit-image',675 'scikit-image',
640 '2011 the scikit-image team',676 '2011 the scikit-image team',
@@ -685,6 +721,11 @@ credits = [ · 44 unmodified lines
685 '2018 HashiCorp',721 '2018 HashiCorp',
686 'MPL',722 'MPL',
687 'https://raw.githubusercontent.com/hashicorp/terraform-website/master/LICENSE.md'723 'https://raw.githubusercontent.com/hashicorp/terraform-website/master/LICENSE.md'
724 ], [
725 'Trio',
726 '2017 Nathaniel J. Smith',
727 'MIT',
728 'https://raw.githubusercontent.com/python-trio/trio/master/LICENSE.MIT'
688 ], [729 ], [
689 'Twig',730 'Twig',
690 '2009-2018 The Twig Team',731 '2009-2018 The Twig Team',
@@ -705,11 +746,21 @@ credits = [ · 14 unmodified lines
705 '2010-2018 Mitchell Hashimoto',746 '2010-2018 Mitchell Hashimoto',
706 'MPL',747 'MPL',
707 'https://raw.githubusercontent.com/mitchellh/vagrant/master/website/LICENSE.md'748 'https://raw.githubusercontent.com/mitchellh/vagrant/master/website/LICENSE.md'
749 ], [
750 'Vue Router',
751 '2013-present Evan You',
752 'MIT',
753 'https://raw.githubusercontent.com/vuejs/vue-router/dev/LICENSE'
708 ], [754 ], [
709 'Vue.js',755 'Vue.js',
710 '2013-2018 Evan You, Vue.js contributors',756 '2013-2018 Evan You, Vue.js contributors',
711 'MIT',757 'MIT',
712 'https://raw.githubusercontent.com/vuejs/vue/master/LICENSE'758 'https://raw.githubusercontent.com/vuejs/vue/master/LICENSE'
759 ], [
760 'Vuex',
761 '2015-present Evan You',
762 'MIT',
763 'https://raw.githubusercontent.com/vuejs/vuex/dev/LICENSE'
713 ], [764 ], [
714 'Vulkan',765 'Vulkan',
715 '2014-2017 Khronos Group Inc.<br>Vulkan and the Vulkan logo are registered trademarks of the Khronos Group Inc.',766 '2014-2017 Khronos Group Inc.<br>Vulkan and the Vulkan logo are registered trademarks of the Khronos Group Inc.',
@@ -720,6 +771,11 @@ credits = [ · 4 unmodified lines
720 'JS Foundation and other contributors',771 'JS Foundation and other contributors',
721 'CC BY',772 'CC BY',
722 'https://creativecommons.org/licenses/by/4.0/'773 'https://creativecommons.org/licenses/by/4.0/'
774 ], [
775 'Wordpress',
776 '2003-2019 WordPress Foundation',
777 'GPLv2+',
778 'https://wordpress.org/about/license/'
723 ], [779 ], [
724 'Yarn',780 'Yarn',
725 '2016-present Yarn Contributors',781 '2016-present Yarn Contributors',
assets/javascripts/templates/pages/offline_tmpl.coffee+9−4
@@ -25,8 +25,8 @@ app.templates.offlinePage = (docs) -> """ · 24 unmodified lines
25 <h2 class="_block-heading">Questions & Answers</h2>25 <h2 class="_block-heading">Questions & Answers</h2>
26 <dl>26 <dl>
27 <dt>How does this work?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>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://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.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 <dt>Can I close the tab/browser?30 <dt>Can I close the tab/browser?
31 <dd>#{canICloseTheTab()}31 <dd>#{canICloseTheTab()}
32 <dt>What if I don't update a documentation?32 <dt>What if I don't update a documentation?
@@ -41,10 +41,15 @@ app.templates.offlinePage = (docs) -> """ · 8 unmodified lines
41"""41"""
42 42
43canICloseTheTab = ->43canICloseTheTab = ->
44 if app.AppCache.isEnabled()44 if app.ServiceWorker.isEnabled()
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). """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 else46 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 The current tab will continue to function even when you go offline (provided you installed all the documentations beforehand). """53 The current tab will continue to function even when you go offline (provided you installed all the documentations beforehand). """
49 54
50app.templates.offlineDoc = (doc, status) ->55app.templates.offlineDoc = (doc, status) ->
assets/javascripts/templates/pages/root_tmpl.coffee.erb+2−2
@@ -8,7 +8,7 @@ app.templates.intro = """ · 7 unmodified lines
8 <p>Thanks for downloading DevDocs. Here are a few things you should know:8 <p>Thanks for downloading DevDocs. Here are a few things you should know:
9 <ol class="_intro-list">9 <ol class="_intro-list">
10 <li>Your local version of DevDocs won't self-update. Unless you're modifying the code,10 <li>Your local version of DevDocs won't self-update. Unless you're modifying the code,
11 I&nbsp;recommend using the hosted version at <a href="https://devdocs.io">devdocs.io</a>.11 we&nbsp;recommend using the hosted version at <a href="https://devdocs.io">devdocs.io</a>.
12 <li>Run <code>thor docs:list</code> to see all available documentations.12 <li>Run <code>thor docs:list</code> to see all available documentations.
13 <li>Run <code>thor docs:download &lt;name&gt;</code> to download documentations.13 <li>Run <code>thor docs:download &lt;name&gt;</code> to download documentations.
14 <li>Run <code>thor docs:download --installed</code> to update all downloaded documentations.14 <li>Run <code>thor docs:download --installed</code> to update all downloaded documentations.
@@ -39,7 +39,7 @@ app.templates.intro = """ · 24 unmodified lines
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>.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>.
40 <li>For the latest news, follow <a href="https://twitter.com/DevDocs">@DevDocs</a>.40 <li>For the latest news, follow <a href="https://twitter.com/DevDocs">@DevDocs</a>.
41 <li>DevDocs is free and <a href="https://github.com/freeCodeCamp/devdocs">open source</a>.41 <li>DevDocs is free and <a href="https://github.com/freeCodeCamp/devdocs">open source</a>.
42 <iframe class="_github-btn" src="//ghbtns.com/github-btn.html?user=freeCodeCamp&repo=devdocs&type=watch&count=true" allowtransparency="true" frameborder="0" scrolling="0" width="100" height="20"></iframe>42 <object data="https://img.shields.io/github/stars/freeCodeCamp/devdocs.svg?style=social" type="image/svg+xml" aria-hidden="true"></object>
43 <li>And if you're new to coding, check out <a href="https://www.freecodecamp.org/">freeCodeCamp's open source curriculum</a>.43 <li>And if you're new to coding, check out <a href="https://www.freecodecamp.org/">freeCodeCamp's open source curriculum</a>.
44 </ol>44 </ol>
45 <p>Happy coding!45 <p>Happy coding!
assets/javascripts/templates/pages/settings_tmpl.coffee+8−0
@@ -15,6 +15,14 @@ app.templates.settingsPage = (settings) -> """ · 14 unmodified lines
15 <input type="checkbox" form="settings" name="layout" value="_sidebar-hidden"#{if settings['_sidebar-hidden'] then ' checked' else ''}>Automatically hide and show the sidebar15 <input type="checkbox" form="settings" name="layout" value="_sidebar-hidden"#{if settings['_sidebar-hidden'] then ' checked' else ''}>Automatically hide and show the sidebar
16 <small>Tip: drag the edge of the sidebar to resize it.</small>16 <small>Tip: drag the edge of the sidebar to resize it.</small>
17 </label>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 _hide-in-development">
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 </div>26 </div>
19 </div>27 </div>
20 28
assets/javascripts/tracking.js+27−23
@@ -1,28 +1,32 @@
1try {1try {
2 if (app.config.env == 'production') {2 if (app.config.env === 'production') {
3 (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){3 if (Cookies.get('analyticsConsent') === '1') {
4 (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),4 (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
5 m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)5 (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
6 })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');6 m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
7 ga('create', 'UA-5544833-12', 'devdocs.io');7 })(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
8 page.track(function() {8 ga('create', 'UA-5544833-12', 'devdocs.io');
9 ga('send', 'pageview', {9 page.track(function() {
10 page: location.pathname + location.search + location.hash,10 ga('send', 'pageview', {
11 dimension1: app.router.context && app.router.context.doc && app.router.context.doc.slug_without_version11 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 page.track(function() {
16 if (window._gauges)17 if (window._gauges)
17 _gauges.push(['track']);18 _gauges.push(['track']);
18 else19 else
19 (function() {20 (function() {
20 var _gauges=_gauges||[];!function(){var a=document.createElement("script");21 var _gauges=_gauges||[];!function(){var a=document.createElement("script");
21 a.type="text/javascript",a.async=!0,a.id="gauges-tracker",22 a.type="text/javascript",a.async=!0,a.id="gauges-tracker",
22 a.setAttribute("data-site-id","51c15f82613f5d7819000067"),23 a.setAttribute("data-site-id","51c15f82613f5d7819000067"),
23 a.src="https://secure.gaug.es/track.js";var b=document.getElementsByTagName("script")[0];24 a.src="https://secure.gaug.es/track.js";var b=document.getElementsByTagName("script")[0];
24 b.parentNode.insertBefore(a,b)}();25 b.parentNode.insertBefore(a,b)}();
25 })();26 })();
26 });27 });
28 } else {
29 resetAnalytics();
30 }
27 }31 }
28} catch(e) { }32} catch(e) { }
assets/javascripts/vendor/fastclick.js+2−2
@@ -413,7 +413,7 @@ · 412 unmodified lines
413 // when the user next taps anywhere else on the page, new touchstart and touchend events are dispatched413 // when the user next taps anywhere else on the page, new touchstart and touchend events are dispatched
414 // with the same identifier as the touch event that previously triggered the click that triggered the alert.414 // with the same identifier as the touch event that previously triggered the click that triggered the alert.
415 // Sadly, there is an issue on iOS 4 that causes some normal touch events to have the same identifier as an415 // 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 // Issue 120: touch.identifier is 0 when Chrome dev tools 'Emulate touch events' is set with an iOS device UA string,417 // Issue 120: touch.identifier is 0 when Chrome dev tools 'Emulate touch events' is set with an iOS device UA string,
418 // which causes all touch events to be ignored. As this block only applies to iOS, and iOS identifiers are always long,418 // which causes all touch events to be ignored. As this block only applies to iOS, and iOS identifiers are always long,
419 // random integers, it's safe to to continue if the identifier is 0 here.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 version808 // IE11: prefixed -ms-touch-action is no longer supported and it's recommended to use non-prefixed version
809 // http://msdn.microsoft.com/en-us/library/windows/apps/Hh767313.aspx809 // http://msdn.microsoft.com/en-us/library/windows/apps/Hh767313.aspx
810 if (layer.style.touchAction === 'none' || layer.style.touchAction === 'manipulation') {810 if (layer.style.touchAction === 'none' || layer.style.touchAction === 'manipulation') {
811 return true;811 return true;
assets/javascripts/vendor/prism.js+2577−2210
@@ -1,12 +1,12 @@
1/* PrismJS 1.15.01/* PrismJS 1.17.1
2https://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 */2https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+c+bash+cpp+coffeescript+ruby+d+dart+markup-templating+elixir+erlang+go+java+php+json+kotlin+crystal+lua+django+matlab+typescript+nginx+nim+perl+sql+scss+python+jsx+rust+yaml */
3var _self = (typeof window !== 'undefined')3var _self = (typeof window !== 'undefined')
4 ? window // if in browser4 ? window // if in browser
5 : (5 : (
6 (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope)6 (typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope)
7 ? self // if in worker7 ? self // if in worker
8 : {} // if in node js8 : {} // if in node js
9 );9 );
10 10
11/**11/**
12 * Prism: Lightweight, robust, elegant syntax highlighting12 * Prism: Lightweight, robust, elegant syntax highlighting
@@ -14,977 +14,1174 @@ var _self = (typeof window !== 'undefined') · 1 unmodified line
14 * @author Lea Verou http://lea.verou.me14 * @author Lea Verou http://lea.verou.me
15 */15 */
16 16
17var Prism = (function(){17var Prism = (function (_self){
18 18
19// Private helper vars19// Private helper vars
20var lang = /\blang(?:uage)?-([\w-]+)\b/i;20 var lang = /\blang(?:uage)?-([\w-]+)\b/i;
21var uniqueId = 0;21 var uniqueId = 0;
22 22
23var _ = _self.Prism = {23 var _ = {
24 manual: _self.Prism && _self.Prism.manual,24 manual: _self.Prism && _self.Prism.manual,
25 disableWorkerMessageHandler: _self.Prism && _self.Prism.disableWorkerMessageHandler,25 disableWorkerMessageHandler: _self.Prism && _self.Prism.disableWorkerMessageHandler,
26 util: {26 util: {
27 encode: function (tokens) {27 encode: function (tokens) {
28 if (tokens instanceof Token) {28 if (tokens instanceof Token) {
29 return new Token(tokens.type, _.util.encode(tokens.content), tokens.alias);29 return new Token(tokens.type, _.util.encode(tokens.content), tokens.alias);
30 } else if (_.util.type(tokens) === 'Array') {30 } else if (Array.isArray(tokens)) {
31 return tokens.map(_.util.encode);31 return tokens.map(_.util.encode);
32 } else {32 } else {
33 return tokens.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/\u00a0/g, ' ');33 return tokens.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/\u00a0/g, ' ');
34 }34 }
35 },35 },
36 36
37 type: function (o) {37 type: function (o) {
38 return Object.prototype.toString.call(o).match(/\[object (\w+)\]/)[1];38 return Object.prototype.toString.call(o).slice(8, -1);
39 },39 },
40 40
41 objId: function (obj) {41 objId: function (obj) {
42 if (!obj['__id']) {42 if (!obj['__id']) {
43 Object.defineProperty(obj, '__id', { value: ++uniqueId });43 Object.defineProperty(obj, '__id', { value: ++uniqueId });
44 }44 }
45 return obj['__id'];45 return obj['__id'];
46 },46 },
47 47
48 // Deep clone a language definition (e.g. to extend it)48 // Deep clone a language definition (e.g. to extend it)
49 clone: function (o, visited) {49 clone: function deepClone(o, visited) {
50 var type = _.util.type(o);50 var clone, id, type = _.util.type(o);
51 visited = visited || {};51 visited = visited || {};
52 52
53 switch (type) {53 switch (type) {
54 case 'Object':54 case 'Object':
55 if (visited[_.util.objId(o)]) {55 id = _.util.objId(o);
56 return visited[_.util.objId(o)];56 if (visited[id]) {
57 }57 return visited[id];
58 var clone = {};58 }
59 visited[_.util.objId(o)] = clone;59 clone = {};
60 60 visited[id] = clone;
61 for (var key in o) {61
62 if (o.hasOwnProperty(key)) {62 for (var key in o) {
63 clone[key] = _.util.clone(o[key], visited);63 if (o.hasOwnProperty(key)) {
64 }64 clone[key] = deepClone(o[key], visited);
65 }65 }
66 66 }
67 return clone;67
68 68 return clone;
69 case 'Array':69
70 if (visited[_.util.objId(o)]) {70 case 'Array':
71 return visited[_.util.objId(o)];71 id = _.util.objId(o);
72 }72 if (visited[id]) {
73 var clone = [];73 return visited[id];
74 visited[_.util.objId(o)] = clone;74 }
75 75 clone = [];
76 o.forEach(function (v, i) {76 visited[id] = clone;
77 clone[i] = _.util.clone(v, visited);77
78 });78 o.forEach(function (v, i) {
79 79 clone[i] = deepClone(v, visited);
80 return clone;80 });
81 }81
82 82 return clone;
83 return o;83
84 }84 default:
85 },85 return o;
86 86 }
87 languages: {87 }
88 extend: function (id, redef) {88 },
89 var lang = _.util.clone(_.languages[id]);89
90 90 languages: {
91 for (var key in redef) {91 extend: function (id, redef) {
92 lang[key] = redef[key];92 var lang = _.util.clone(_.languages[id]);
93 }93
94 94 for (var key in redef) {
95 return lang;95 lang[key] = redef[key];
96 },96 }
97 97
98 /**98 return lang;
99 * Insert a token before another token in a language literal99 },
100 * As this needs to recreate the object (we cannot actually insert before keys in object literals),100
101 * we cannot just provide an object, we need anobject and a key.101 /**
102 * @param inside The key (or language id) of the parent102 * Insert a token before another token in a language literal
103 * @param before The key to insert before. If not provided, the function appends instead.103 * As this needs to recreate the object (we cannot actually insert before keys in object literals),
104 * @param insert Object with the key/value pairs to insert104 * we cannot just provide an object, we need an object and a key.
105 * @param root The object that contains `inside`. If equal to Prism.languages, it can be omitted.105 * @param inside The key (or language id) of the parent
106 */106 * @param before The key to insert before.
107 insertBefore: function (inside, before, insert, root) {107 * @param insert Object with the key/value pairs to insert
108 root = root || _.languages;108 * @param root The object that contains `inside`. If equal to Prism.languages, it can be omitted.
109 var grammar = root[inside];109 */
110 110 insertBefore: function (inside, before, insert, root) {
111 if (arguments.length == 2) {111 root = root || _.languages;
112 insert = arguments[1];112 var grammar = root[inside];
113 113 var ret = {};
114 for (var newToken in insert) {114
115 if (insert.hasOwnProperty(newToken)) {115 for (var token in grammar) {
116 grammar[newToken] = insert[newToken];116 if (grammar.hasOwnProperty(token)) {
117 }117
118 }118 if (token == before) {
119 119 for (var newToken in insert) {
120 return grammar;120 if (insert.hasOwnProperty(newToken)) {
121 }121 ret[newToken] = insert[newToken];
122 122 }
123 var ret = {};123 }
124 124 }
125 for (var token in grammar) {125
126 126 // Do not insert token which also occur in insert. See #1525
127 if (grammar.hasOwnProperty(token)) {127 if (!insert.hasOwnProperty(token)) {
128 128 ret[token] = grammar[token];
129 if (token == before) {129 }
130 130 }
131 for (var newToken in insert) {131 }
132 132
133 if (insert.hasOwnProperty(newToken)) {133 var old = root[inside];
134 ret[newToken] = insert[newToken];134 root[inside] = ret;
135 }135
136 }136 // Update references in other language definitions
137 }137 _.languages.DFS(_.languages, function(key, value) {
138 138 if (value === old && key != inside) {
139 ret[token] = grammar[token];139 this[key] = ret;
140 }140 }
141 }141 });
142 142
143 // Update references in other language definitions143 return ret;
144 _.languages.DFS(_.languages, function(key, value) {144 },
145 if (value === root[inside] && key != inside) {145
146 this[key] = ret;146 // Traverse a language definition with Depth First Search
147 }147 DFS: function DFS(o, callback, type, visited) {
148 });148 visited = visited || {};
149 149
150 return root[inside] = ret;150 var objId = _.util.objId;
151 },151
152 152 for (var i in o) {
153 // Traverse a language definition with Depth First Search153 if (o.hasOwnProperty(i)) {
154 DFS: function(o, callback, type, visited) {154 callback.call(o, i, o[i], type || i);
155 visited = visited || {};155
156 for (var i in o) {156 var property = o[i],
157 if (o.hasOwnProperty(i)) {157 propertyType = _.util.type(property);
158 callback.call(o, i, o[i], type || i);158
159 159 if (propertyType === 'Object' && !visited[objId(property)]) {
160 if (_.util.type(o[i]) === 'Object' && !visited[_.util.objId(o[i])]) {160 visited[objId(property)] = true;
161 visited[_.util.objId(o[i])] = true;161 DFS(property, callback, null, visited);
162 _.languages.DFS(o[i], callback, null, visited);162 }
163 }163 else if (propertyType === 'Array' && !visited[objId(property)]) {
164 else if (_.util.type(o[i]) === 'Array' && !visited[_.util.objId(o[i])]) {164 visited[objId(property)] = true;
165 visited[_.util.objId(o[i])] = true;165 DFS(property, callback, i, visited);
166 _.languages.DFS(o[i], callback, i, visited);166 }
167 }167 }
168 }168 }
169 }169 }
170 }170 },
171 },171 plugins: {},
172 plugins: {},172
173 173 highlightAll: function(async, callback) {
174 highlightAll: function(async, callback) {174 _.highlightAllUnder(document, async, callback);
175 _.highlightAllUnder(document, async, callback);175 },
176 },176
177 177 highlightAllUnder: function(container, async, callback) {
178 highlightAllUnder: function(container, async, callback) {178 var env = {
179 var env = {179 callback: callback,
180 callback: callback,180 selector: 'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'
181 selector: 'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'181 };
182 };182
183 183 _.hooks.run('before-highlightall', env);
184 _.hooks.run("before-highlightall", env);184
185 185 var elements = container.querySelectorAll(env.selector);
186 var elements = env.elements || container.querySelectorAll(env.selector);186
187 187 for (var i=0, element; element = elements[i++];) {
188 for (var i=0, element; element = elements[i++];) {188 _.highlightElement(element, async === true, env.callback);
189 _.highlightElement(element, async === true, env.callback);189 }
190 }190 },
191 },191
192 192 highlightElement: function(element, async, callback) {
193 highlightElement: function(element, async, callback) {193 // Find language
194 // Find language194 var language = 'none', grammar, parent = element;
195 var language, grammar, parent = element;195
196 196 while (parent && !lang.test(parent.className)) {
197 while (parent && !lang.test(parent.className)) {197 parent = parent.parentNode;
198 parent = parent.parentNode;198 }
199 }199
200 200 if (parent) {
201 if (parent) {201 language = (parent.className.match(lang) || [,'none'])[1].toLowerCase();
202 language = (parent.className.match(lang) || [,''])[1].toLowerCase();202 grammar = _.languages[language];
203 grammar = _.languages[language];203 }
204 }204
205 205 // Set language on the element, if not present
206 // Set language on the element, if not present206 element.className = element.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language;
207 element.className = element.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language;207
208 208 if (element.parentNode) {
209 if (element.parentNode) {209 // Set language on the parent, for styling
210 // Set language on the parent, for styling210 parent = element.parentNode;
211 parent = element.parentNode;211
212 212 if (/pre/i.test(parent.nodeName)) {
213 if (/pre/i.test(parent.nodeName)) {213 parent.className = parent.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language;
214 parent.className = parent.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language;214 }
215 }215 }
216 }216
217 217 var code = element.textContent;
218 var code = element.textContent;218
219 219 var env = {
220 var env = {220 element: element,
221 element: element,221 language: language,
222 language: language,222 grammar: grammar,
223 grammar: grammar,223 code: code
224 code: code224 };
225 };225
226 226 var insertHighlightedCode = function (highlightedCode) {
227 _.hooks.run('before-sanity-check', env);227 env.highlightedCode = highlightedCode;
228 228
229 if (!env.code || !env.grammar) {229 _.hooks.run('before-insert', env);
230 if (env.code) {230
231 _.hooks.run('before-highlight', env);231 env.element.innerHTML = env.highlightedCode;
232 env.element.textContent = env.code;232
233 _.hooks.run('after-highlight', env);233 _.hooks.run('after-highlight', env);
234 }234 _.hooks.run('complete', env);
235 _.hooks.run('complete', env);235 callback && callback.call(env.element);
236 return;236 }
237 }237
238 238 _.hooks.run('before-sanity-check', env);
239 _.hooks.run('before-highlight', env);239
240 240 if (!env.code) {
241 // if (async && _self.Worker) {241 _.hooks.run('complete', env);
242 // var worker = new Worker(_.filename);242 return;
243 243 }
244 // worker.onmessage = function(evt) {244
245 // env.highlightedCode = evt.data;245 _.hooks.run('before-highlight', env);
246 246
247 // _.hooks.run('before-insert', env);247 if (!env.grammar) {
248 248 insertHighlightedCode(_.util.encode(env.code));
249 // env.element.innerHTML = env.highlightedCode;249 return;
250 250 }
251 // callback && callback.call(env.element);251
252 // _.hooks.run('after-highlight', env);252 if (async && _self.Worker) {
253 // _.hooks.run('complete', env);253 var worker = new Worker(_.filename);
254 // };254
255 255 worker.onmessage = function(evt) {
256 // worker.postMessage(JSON.stringify({256 insertHighlightedCode(evt.data);
257 // language: env.language,257 };
258 // code: env.code,258
259 // immediateClose: true259 worker.postMessage(JSON.stringify({
260 // }));260 language: env.language,
261 // }261 code: env.code,
262 // else {262 immediateClose: true
263 env.highlightedCode = _.highlight(env.code, env.grammar, env.language);263 }));
264 264 }
265 _.hooks.run('before-insert', env);265 else {
266 266 insertHighlightedCode(_.highlight(env.code, env.grammar, env.language));
267 env.element.innerHTML = env.highlightedCode;267 }
268 268 },
269 callback && callback.call(element);269
270 270 highlight: function (text, grammar, language) {
271 _.hooks.run('after-highlight', env);271 var env = {
272 _.hooks.run('complete', env);272 code: text,
273 // }273 grammar: grammar,
274 },274 language: language
275 275 };
276 highlight: function (text, grammar, language) {276 _.hooks.run('before-tokenize', env);
277 var env = {277 env.tokens = _.tokenize(env.code, env.grammar);
278 code: text,278 _.hooks.run('after-tokenize', env);
279 grammar: grammar,279 return Token.stringify(_.util.encode(env.tokens), env.language);
280 language: language280 },
281 };281
282 _.hooks.run('before-tokenize', env);282 matchGrammar: function (text, strarr, grammar, index, startPos, oneshot, target) {
283 env.tokens = _.tokenize(env.code, env.grammar);283 for (var token in grammar) {
284 _.hooks.run('after-tokenize', env);284 if(!grammar.hasOwnProperty(token) || !grammar[token]) {
285 return Token.stringify(_.util.encode(env.tokens), env.language);285 continue;
286 },286 }
287 287
288 matchGrammar: function (text, strarr, grammar, index, startPos, oneshot, target) {288 if (token == target) {
289 var Token = _.Token;289 return;
290 290 }
291 for (var token in grammar) {291
292 if(!grammar.hasOwnProperty(token) || !grammar[token]) {292 var patterns = grammar[token];
293 continue;293 patterns = (_.util.type(patterns) === "Array") ? patterns : [patterns];
294 }294
295 295 for (var j = 0; j < patterns.length; ++j) {
296 if (token == target) {296 var pattern = patterns[j],
297 return;297 inside = pattern.inside,
298 }298 lookbehind = !!pattern.lookbehind,
299 299 greedy = !!pattern.greedy,
300 var patterns = grammar[token];300 lookbehindLength = 0,
301 patterns = (_.util.type(patterns) === "Array") ? patterns : [patterns];301 alias = pattern.alias;
302 302
303 for (var j = 0; j < patterns.length; ++j) {303 if (greedy && !pattern.pattern.global) {
304 var pattern = patterns[j],304 // Without the global flag, lastIndex won't work
305 inside = pattern.inside,305 var flags = pattern.pattern.toString().match(/[imuy]*$/)[0];
306 lookbehind = !!pattern.lookbehind,306 pattern.pattern = RegExp(pattern.pattern.source, flags + "g");
307 greedy = !!pattern.greedy,307 }
308 lookbehindLength = 0,308
309 alias = pattern.alias;309 pattern = pattern.pattern || pattern;
310 310
311 if (greedy && !pattern.pattern.global) {311 // Don’t cache length as it changes during the loop
312 // Without the global flag, lastIndex won't work312 for (var i = index, pos = startPos; i < strarr.length; pos += strarr[i].length, ++i) {
313 var flags = pattern.pattern.toString().match(/[imuy]*$/)[0];313
314 pattern.pattern = RegExp(pattern.pattern.source, flags + "g");314 var str = strarr[i];
315 }315
316 316 if (strarr.length > text.length) {
317 pattern = pattern.pattern || pattern;317 // Something went terribly wrong, ABORT, ABORT!
318 318 return;
319 // Don’t cache length as it changes during the loop319 }
320 for (var i = index, pos = startPos; i < strarr.length; pos += strarr[i].length, ++i) {320
321 321 if (str instanceof Token) {
322 var str = strarr[i];322 continue;
323 323 }
324 if (strarr.length > text.length) {324
325 // Something went terribly wrong, ABORT, ABORT!325 if (greedy && i != strarr.length - 1) {
326 return;326 pattern.lastIndex = pos;
327 }327 var match = pattern.exec(text);
328 328 if (!match) {
329 if (str instanceof Token) {329 break;
330 continue;330 }
331 }331
332 332 var from = match.index + (lookbehind ? match[1].length : 0),
333 if (greedy && i != strarr.length - 1) {333 to = match.index + match[0].length,
334 pattern.lastIndex = pos;334 k = i,
335 var match = pattern.exec(text);335 p = pos;
336 if (!match) {336
337 break;337 for (var len = strarr.length; k < len && (p < to || (!strarr[k].type && !strarr[k - 1].greedy)); ++k) {
338 }338 p += strarr[k].length;
339 339 // Move the index i to the element in strarr that is closest to from
340 var from = match.index + (lookbehind ? match[1].length : 0),340 if (from >= p) {
341 to = match.index + match[0].length,341 ++i;
342 k = i,342 pos = p;
343 p = pos;343 }
344 344 }
345 for (var len = strarr.length; k < len && (p < to || (!strarr[k].type && !strarr[k - 1].greedy)); ++k) {345
346 p += strarr[k].length;346 // If strarr[i] is a Token, then the match starts inside another Token, which is invalid
347 // Move the index i to the element in strarr that is closest to from347 if (strarr[i] instanceof Token) {
348 if (from >= p) {348 continue;
349 ++i;349 }
350 pos = p;350
351 }351 // Number of tokens to delete and replace with the new match
352 }352 delNum = k - i;
353 353 str = text.slice(pos, p);
354 // If strarr[i] is a Token, then the match starts inside another Token, which is invalid354 match.index -= pos;
355 if (strarr[i] instanceof Token) {355 } else {
356 continue;356 pattern.lastIndex = 0;
357 }357
358 358 var match = pattern.exec(str),
359 // Number of tokens to delete and replace with the new match359 delNum = 1;
360 delNum = k - i;360 }
361 str = text.slice(pos, p);361
362 match.index -= pos;362 if (!match) {
363 } else {363 if (oneshot) {
364 pattern.lastIndex = 0;364 break;
365 365 }
366 var match = pattern.exec(str),366
367 delNum = 1;367 continue;
368 }368 }
369 369
370 if (!match) {370 if(lookbehind) {
371 if (oneshot) {371 lookbehindLength = match[1] ? match[1].length : 0;
372 break;372 }
373 }373
374 374 var from = match.index + lookbehindLength,
375 continue;375 match = match[0].slice(lookbehindLength),
376 }376 to = from + match.length,
377 377 before = str.slice(0, from),
378 if(lookbehind) {378 after = str.slice(to);
379 lookbehindLength = match[1] ? match[1].length : 0;379
380 }380 var args = [i, delNum];
381 381
382 var from = match.index + lookbehindLength,382 if (before) {
383 match = match[0].slice(lookbehindLength),383 ++i;
384 to = from + match.length,384 pos += before.length;
385 before = str.slice(0, from),385 args.push(before);
386 after = str.slice(to);386 }
387
388 var wrapped = new Token(token, inside? _.tokenize(match, inside) : match, alias, match, greedy);
389
390 args.push(wrapped);
391
392 if (after) {
393 args.push(after);
394 }
395
396 Array.prototype.splice.apply(strarr, args);
397
398 if (delNum != 1)
399 _.matchGrammar(text, strarr, grammar, i, pos, true, token);
400
401 if (oneshot)
402 break;
403 }
404 }
405 }
406 },
407
408 tokenize: function(text, grammar) {
409 var strarr = [text];
410
411 var rest = grammar.rest;
412
413 if (rest) {
414 for (var token in rest) {
415 grammar[token] = rest[token];
416 }
417
418 delete grammar.rest;
419 }
420
421 _.matchGrammar(text, strarr, grammar, 0, 0, false);
422
423 return strarr;
424 },
425
426 hooks: {
427 all: {},
428
429 add: function (name, callback) {
430 var hooks = _.hooks.all;
431
432 hooks[name] = hooks[name] || [];
433
434 hooks[name].push(callback);
435 },
436
437 run: function (name, env) {
438 var callbacks = _.hooks.all[name];
439
440 if (!callbacks || !callbacks.length) {
441 return;
442 }
387 443
388 var args = [i, delNum];444 for (var i=0, callback; callback = callbacks[i++];) {
389 445 callback(env);
390 if (before) {446 }
391 ++i;447 }
392 pos += before.length;448 },
393 args.push(before);
394 }
395 449
396 var wrapped = new Token(token, inside? _.tokenize(match, inside) : match, alias, match, greedy);450 Token: Token
397 451 };
398 args.push(wrapped);
399 452
400 if (after) {453 _self.Prism = _;
401 args.push(after);
402 }
403
404 Array.prototype.splice.apply(strarr, args);
405
406 if (delNum != 1)
407 _.matchGrammar(text, strarr, grammar, i, pos, true, token);
408 454
409 if (oneshot)455 function Token(type, content, alias, matchedStr, greedy) {
410 break;456 this.type = type;
411 }457 this.content = content;
412 }458 this.alias = alias;
413 }459 // Copy of the full string this token was created from
414 },460 this.length = (matchedStr || "").length|0;
461 this.greedy = !!greedy;
462 }
415 463
416 tokenize: function(text, grammar, language) {464 Token.stringify = function(o, language) {
417 var strarr = [text];465 if (typeof o == 'string') {
466 return o;
467 }
418 468
419 var rest = grammar.rest;469 if (Array.isArray(o)) {
470 return o.map(function(element) {
471 return Token.stringify(element, language);
472 }).join('');
473 }
420 474
421 if (rest) {475 var env = {
422 for (var token in rest) {476 type: o.type,
423 grammar[token] = rest[token];477 content: Token.stringify(o.content, language),
424 }478 tag: 'span',
425 479 classes: ['token', o.type],
426 delete grammar.rest;480 attributes: {},
427 }481 language: language
428 482 };
429 _.matchGrammar(text, strarr, grammar, 0, 0, false);
430
431 return strarr;
432 },
433
434 hooks: {
435 all: {},
436
437 add: function (name, callback) {
438 var hooks = _.hooks.all;
439
440 hooks[name] = hooks[name] || [];
441
442 hooks[name].push(callback);
443 },
444
445 run: function (name, env) {
446 var callbacks = _.hooks.all[name];
447
448 if (!callbacks || !callbacks.length) {
449 return;
450 }
451
452 for (var i=0, callback; callback = callbacks[i++];) {
453 callback(env);
454 }
455 }
456 }
457};
458
459var Token = _.Token = function(type, content, alias, matchedStr, greedy) {
460 this.type = type;
461 this.content = content;
462 this.alias = alias;
463 // Copy of the full string this token was created from
464 this.length = (matchedStr || "").length|0;
465 this.greedy = !!greedy;
466};
467
468Token.stringify = function(o, language, parent) {
469 if (typeof o == 'string') {
470 return o;
471 }
472
473 if (_.util.type(o) === 'Array') {
474 return o.map(function(element) {
475 return Token.stringify(element, language, o);
476 }).join('');
477 }
478
479 var env = {
480 type: o.type,
481 content: Token.stringify(o.content, language, parent),
482 tag: 'span',
483 classes: ['token', o.type],
484 attributes: {},
485 language: language,
486 parent: parent
487 };
488
489 if (o.alias) {
490 var aliases = _.util.type(o.alias) === 'Array' ? o.alias : [o.alias];
491 Array.prototype.push.apply(env.classes, aliases);
492 }
493
494 _.hooks.run('wrap', env);
495
496 var attributes = Object.keys(env.attributes).map(function(name) {
497 return name + '="' + (env.attributes[name] || '').replace(/"/g, '&quot;') + '"';
498 }).join(' ');
499
500 return '<' + env.tag + ' class="' + env.classes.join(' ') + '"' + (attributes ? ' ' + attributes : '') + '>' + env.content + '</' + env.tag + '>';
501
502};
503 483
504// if (!_self.document) {484 if (o.alias) {
505// if (!_self.addEventListener) {485 var aliases = Array.isArray(o.alias) ? o.alias : [o.alias];
506// // in Node.js486 Array.prototype.push.apply(env.classes, aliases);
507// return _self.Prism;487 }
508// }488
509 489 _.hooks.run('wrap', env);
510// if (!_.disableWorkerMessageHandler) {490
511// // In worker491 var attributes = Object.keys(env.attributes).map(function(name) {
512// _self.addEventListener('message', function (evt) {492 return name + '="' + (env.attributes[name] || '').replace(/"/g, '&quot;') + '"';
513// var message = JSON.parse(evt.data),493 }).join(' ');
514// lang = message.language,494
515// code = message.code,495 return '<' + env.tag + ' class="' + env.classes.join(' ') + '"' + (attributes ? ' ' + attributes : '') + '>' + env.content + '</' + env.tag + '>';
516// immediateClose = message.immediateClose;496 };
517 497
518// _self.postMessage(_.highlight(code, _.languages[lang], lang));498 if (!_self.document) {
519// if (immediateClose) {499 if (!_self.addEventListener) {
520// _self.close();500 // in Node.js
521// }501 return _;
522// }, false);502 }
523// }503
524 504 if (!_.disableWorkerMessageHandler) {
525// return _self.Prism;505 // In worker
526// }506 _self.addEventListener('message', function (evt) {
527 507 var message = JSON.parse(evt.data),
528// //Get current script and highlight508 lang = message.language,
529// var script = document.currentScript || [].slice.call(document.getElementsByTagName("script")).pop();509 code = message.code,
530 510 immediateClose = message.immediateClose;
531// if (script) {511
532// _.filename = script.src;512 _self.postMessage(_.highlight(code, _.languages[lang], lang));
533 513 if (immediateClose) {
534// if (!_.manual && !script.hasAttribute('data-manual')) {514 _self.close();
535// if(document.readyState !== "loading") {515 }
536// if (window.requestAnimationFrame) {516 }, false);
537// window.requestAnimationFrame(_.highlightAll);517 }
538// } else {518
539// window.setTimeout(_.highlightAll, 16);519 return _;
540// }520 }
541// }521
542// else {522//Get current script and highlight
543// document.addEventListener('DOMContentLoaded', _.highlightAll);523 var script = document.currentScript || [].slice.call(document.getElementsByTagName("script")).pop();
544// }524
545// }525 if (script) {
546// }526 _.filename = script.src;
547 527
548return _self.Prism;528 if (!_.manual && !script.hasAttribute('data-manual')) {
549 529 if(document.readyState !== "loading") {
550})();530 if (window.requestAnimationFrame) {
531 window.requestAnimationFrame(_.highlightAll);
532 } else {
533 window.setTimeout(_.highlightAll, 16);
534 }
535 }
536 else {
537 document.addEventListener('DOMContentLoaded', _.highlightAll);
538 }
539 }
540 }
541
542 return _;
543
544})(_self);
551 545
552if (typeof module !== 'undefined' && module.exports) {546if (typeof module !== 'undefined' && module.exports) {
553 module.exports = Prism;547 module.exports = Prism;
554}548}
555 549
556// hack for components to work correctly in node.js550// hack for components to work correctly in node.js
557if (typeof global !== 'undefined') {551if (typeof global !== 'undefined') {
558 global.Prism = Prism;552 global.Prism = Prism;
559}553}
560;554;
561Prism.languages.markup = {555Prism.languages.markup = {
562 'comment': /<!--[\s\S]*?-->/,556 'comment': /<!--[\s\S]*?-->/,
563 'prolog': /<\?[\s\S]+?\?>/,557 'prolog': /<\?[\s\S]+?\?>/,
564 'doctype': /<!DOCTYPE[\s\S]+?>/i,558 'doctype': /<!DOCTYPE[\s\S]+?>/i,
565 'cdata': /<!\[CDATA\[[\s\S]*?]]>/i,559 'cdata': /<!\[CDATA\[[\s\S]*?]]>/i,
566 'tag': {560 'tag': {
567 pattern: /<\/?(?!\d)[^\s>\/=$<%]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+))?)*\s*\/?>/i,561 pattern: /<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/i,
568 greedy: true,562 greedy: true,
569 inside: {563 inside: {
570 'tag': {564 'tag': {
571 pattern: /^<\/?[^\s>\/]+/i,565 pattern: /^<\/?[^\s>\/]+/i,
572 inside: {566 inside: {
573 'punctuation': /^<\/?/,567 'punctuation': /^<\/?/,
574 'namespace': /^[^\s>\/:]+:/568 'namespace': /^[^\s>\/:]+:/
575 }569 }
576 },570 },
577 'attr-value': {571 'attr-value': {
578 pattern: /=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+)/i,572 pattern: /=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/i,
579 inside: {573 inside: {
580 'punctuation': [574 'punctuation': [
581 /^=/,575 /^=/,
582 {576 {
583 pattern: /(^|[^\\])["']/,577 pattern: /^(\s*)["']|["']$/,
584 lookbehind: true578 lookbehind: true
585 }579 }
586 ]580 ]
587 }581 }
588 },582 },
589 'punctuation': /\/?>/,583 'punctuation': /\/?>/,
590 'attr-name': {584 'attr-name': {
591 pattern: /[^\s>\/]+/,585 pattern: /[^\s>\/]+/,
592 inside: {586 inside: {
593 'namespace': /^[^\s>\/:]+:/587 'namespace': /^[^\s>\/:]+:/
594 }588 }
595 }589 }
596 590
597 }591 }
598 },592 },
599 'entity': /&#?[\da-z]{1,8};/i593 'entity': /&#?[\da-z]{1,8};/i
600};594};
601 595
602Prism.languages.markup['tag'].inside['attr-value'].inside['entity'] =596Prism.languages.markup['tag'].inside['attr-value'].inside['entity'] =
603 Prism.languages.markup['entity'];597 Prism.languages.markup['entity'];
604 598
605// Plugin to make entity title show the real entity, idea by Roman Komarov599// Plugin to make entity title show the real entity, idea by Roman Komarov
606Prism.hooks.add('wrap', function(env) {600Prism.hooks.add('wrap', function(env) {
607 601
608 if (env.type === 'entity') {602 if (env.type === 'entity') {
609 env.attributes['title'] = env.content.replace(/&amp;/, '&');603 env.attributes['title'] = env.content.replace(/&amp;/, '&');
610 }604 }
605});
606
607Object.defineProperty(Prism.languages.markup.tag, 'addInlined', {
608 /**
609 * Adds an inlined language to markup.
610 *
611 * An example of an inlined language is CSS with `<style>` tags.
612 *
613 * @param {string} tagName The name of the tag that contains the inlined language. This name will be treated as
614 * case insensitive.
615 * @param {string} lang The language key.
616 * @example
617 * addInlined('style', 'css');
618 */
619 value: function addInlined(tagName, lang) {
620 var includedCdataInside = {};
621 includedCdataInside['language-' + lang] = {
622 pattern: /(^<!\[CDATA\[)[\s\S]+?(?=\]\]>$)/i,
623 lookbehind: true,
624 inside: Prism.languages[lang]
625 };
626 includedCdataInside['cdata'] = /^<!\[CDATA\[|\]\]>$/i;
627
628 var inside = {
629 'included-cdata': {
630 pattern: /<!\[CDATA\[[\s\S]*?\]\]>/i,
631 inside: includedCdataInside
632 }
633 };
634 inside['language-' + lang] = {
635 pattern: /[\s\S]+/,
636 inside: Prism.languages[lang]
637 };
638
639 var def = {};
640 def[tagName] = {
641 pattern: RegExp(/(<__[\s\S]*?>)(?:<!\[CDATA\[[\s\S]*?\]\]>\s*|[\s\S])*?(?=<\/__>)/.source.replace(/__/g, tagName), 'i'),
642 lookbehind: true,
643 greedy: true,
644 inside: inside
645 };
646
647 Prism.languages.insertBefore('markup', 'cdata', def);
648 }
611});649});
612 650
613Prism.languages.xml = Prism.languages.markup;651Prism.languages.xml = Prism.languages.extend('markup', {});
614Prism.languages.html = Prism.languages.markup;652Prism.languages.html = Prism.languages.markup;
615Prism.languages.mathml = Prism.languages.markup;653Prism.languages.mathml = Prism.languages.markup;
616Prism.languages.svg = Prism.languages.markup;654Prism.languages.svg = Prism.languages.markup;
617 655
618Prism.languages.css = {656(function (Prism) {
619 'comment': /\/\*[\s\S]*?\*\//,
620 'atrule': {
621 pattern: /@[\w-]+?.*?(?:;|(?=\s*\{))/i,
622 inside: {
623 'rule': /@[\w-]+/
624 // See rest below
625 }
626 },
627 'url': /url\((?:(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,
628 'selector': /[^{}\s][^{};]*?(?=\s*\{)/,
629 'string': {
630 pattern: /("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,
631 greedy: true
632 },
633 'property': /[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i,
634 'important': /\B!important\b/i,
635 'function': /[-a-z0-9]+(?=\()/i,
636 'punctuation': /[(){};:]/
637};
638 657
639Prism.languages.css['atrule'].inside.rest = Prism.languages.css;658 var string = /("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/;
659
660 Prism.languages.css = {
661 'comment': /\/\*[\s\S]*?\*\//,
662 'atrule': {
663 pattern: /@[\w-]+[\s\S]*?(?:;|(?=\s*\{))/,
664 inside: {
665 'rule': /@[\w-]+/
666 // See rest below
667 }
668 },
669 'url': {
670 pattern: RegExp('url\\((?:' + string.source + '|[^\n\r()]*)\\)', 'i'),
671 inside: {
672 'function': /^url/i,
673 'punctuation': /^\(|\)$/
674 }
675 },
676 'selector': RegExp('[^{}\\s](?:[^{};"\']|' + string.source + ')*?(?=\\s*\\{)'),
677 'string': {
678 pattern: string,
679 greedy: true
680 },
681 'property': /[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i,
682 'important': /!important\b/i,
683 'function': /[-a-z0-9]+(?=\()/i,
684 'punctuation': /[(){};:,]/
685 };
686
687 Prism.languages.css['atrule'].inside.rest = Prism.languages.css;
688
689 var markup = Prism.languages.markup;
690 if (markup) {
691 markup.tag.addInlined('style', 'css');
692
693 Prism.languages.insertBefore('inside', 'attr-value', {
694 'style-attr': {
695 pattern: /\s*style=("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/i,
696 inside: {
697 'attr-name': {
698 pattern: /^\s*style/i,
699 inside: markup.tag.inside
700 },
701 'punctuation': /^\s*=\s*['"]|['"]\s*$/,
702 'attr-value': {
703 pattern: /.+/i,
704 inside: Prism.languages.css
705 }
706 },
707 alias: 'language-css'
708 }
709 }, markup.tag);
710 }
711
712}(Prism));
640 713
641if (Prism.languages.markup) {
642 Prism.languages.insertBefore('markup', 'tag', {
643 'style': {
644 pattern: /(<style[\s\S]*?>)[\s\S]*?(?=<\/style>)/i,
645 lookbehind: true,
646 inside: Prism.languages.css,
647 alias: 'language-css',
648 greedy: true
649 }
650 });
651
652 Prism.languages.insertBefore('inside', 'attr-value', {
653 'style-attr': {
654 pattern: /\s*style=("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/i,
655 inside: {
656 'attr-name': {
657 pattern: /^\s*style/i,
658 inside: Prism.languages.markup.tag.inside
659 },
660 'punctuation': /^\s*=\s*['"]|['"]\s*$/,
661 'attr-value': {
662 pattern: /.+/i,
663 inside: Prism.languages.css
664 }
665 },
666 alias: 'language-css'
667 }
668 }, Prism.languages.markup.tag);
669};
670Prism.languages.clike = {714Prism.languages.clike = {
671 'comment': [715 'comment': [
672 {716 {
673 pattern: /(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,717 pattern: /(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,
674 lookbehind: true718 lookbehind: true
675 },719 },
676 {720 {
677 pattern: /(^|[^\\:])\/\/.*/,721 pattern: /(^|[^\\:])\/\/.*/,
678 lookbehind: true,722 lookbehind: true,
679 greedy: true723 greedy: true
680 }724 }
681 ],725 ],
682 'string': {726 'string': {
683 pattern: /(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,727 pattern: /(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,
684 greedy: true728 greedy: true
685 },729 },
686 'class-name': {730 'class-name': {
687 pattern: /((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[\w.\\]+/i,731 pattern: /((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[\w.\\]+/i,
688 lookbehind: true,732 lookbehind: true,
689 inside: {733 inside: {
690 punctuation: /[.\\]/734 punctuation: /[.\\]/
691 }735 }
692 },736 },
693 'keyword': /\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,737 '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/,738 'boolean': /\b(?:true|false)\b/,
695 'function': /[a-z0-9_]+(?=\()/i,739 'function': /\w+(?=\()/,
696 'number': /\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,740 'number': /\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,
697 'operator': /--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,741 'operator': /--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,
698 'punctuation': /[{}[\];(),.:]/742 'punctuation': /[{}[\];(),.:]/
699};743};
700 744
701Prism.languages.javascript = Prism.languages.extend('clike', {745Prism.languages.javascript = Prism.languages.extend('clike', {
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/,746 'class-name': [
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+)?/,747 Prism.languages.clike['class-name'],
704 // Allow for all non-ASCII characters (See http://stackoverflow.com/a/2008444)748 {
705 'function': /[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*\()/i,749 pattern: /(^|[^$\w\xA0-\uFFFF])[_$A-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\.(?:prototype|constructor))/,
706 'operator': /-[-=]?|\+[+=]?|!=?=?|<<?=?|>>?>?=?|=(?:==?|>)?|&[&=]?|\|[|=]?|\*\*?=?|\/=?|~|\^=?|%=?|\?|\.{3}/750 lookbehind: true
751 }
752 ],
753 'keyword': [
754 {
755 pattern: /((?:^|})\s*)(?:catch|finally)\b/,
756 lookbehind: true
757 },
758 {
759 pattern: /(^|[^.])\b(?:as|async(?=\s*(?:function\b|\(|[$\w\xA0-\uFFFF]|$))|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|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|undefined|var|void|while|with|yield)\b/,
760 lookbehind: true
761 },
762 ],
763 'number': /\b(?:(?:0[xX](?:[\dA-Fa-f](?:_[\dA-Fa-f])?)+|0[bB](?:[01](?:_[01])?)+|0[oO](?:[0-7](?:_[0-7])?)+)n?|(?:\d(?:_\d)?)+n|NaN|Infinity)\b|(?:\b(?:\d(?:_\d)?)+\.?(?:\d(?:_\d)?)*|\B\.(?:\d(?:_\d)?)+)(?:[Ee][+-]?(?:\d(?:_\d)?)+)?/,
764 // Allow for all non-ASCII characters (See http://stackoverflow.com/a/2008444)
765 'function': /#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*(?:\.\s*(?:apply|bind|call)\s*)?\()/,
766 'operator': /-[-=]?|\+[+=]?|!=?=?|<<?=?|>>?>?=?|=(?:==?|>)?|&[&=]?|\|[|=]?|\*\*?=?|\/=?|~|\^=?|%=?|\?|\.{3}/
707});767});
708 768
769Prism.languages.javascript['class-name'][0].pattern = /(\b(?:class|interface|extends|implements|instanceof|new)\s+)[\w.\\]+/;
770
709Prism.languages.insertBefore('javascript', 'keyword', {771Prism.languages.insertBefore('javascript', 'keyword', {
710 'regex': {772 'regex': {
711 pattern: /((?:^|[^$\w\xA0-\uFFFF."'\])\s])\s*)\/(\[[^\]\r\n]+]|\\.|[^/\\\[\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})\]]))/,773 pattern: /((?:^|[^$\w\xA0-\uFFFF."'\])\s])\s*)\/(\[(?:[^\]\\\r\n]|\\.)*]|\\.|[^/\\\[\r\n])+\/[gimyus]{0,6}(?=\s*($|[\r\n,.;})\]]))/,
712 lookbehind: true,774 lookbehind: true,
713 greedy: true775 greedy: true
714 },776 },
715 // This must be declared before keyword because we use "function" inside the look-forward777 // This must be declared before keyword because we use "function" inside the look-forward
716 'function-variable': {778 'function-variable': {
717 pattern: /[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=\s*(?:function\b|(?:\([^()]*\)|[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/i,779 pattern: /#?[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*[=:]\s*(?:async\s*)?(?:\bfunction\b|(?:\((?:[^()]|\([^()]*\))*\)|[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/,
718 alias: 'function'780 alias: 'function'
719 },781 },
720 'constant': /\b[A-Z][A-Z\d_]*\b/782 'parameter': [
783 {
784 pattern: /(function(?:\s+[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)?\s*\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\))/,
785 lookbehind: true,
786 inside: Prism.languages.javascript
787 },
788 {
789 pattern: /[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=>)/i,
790 inside: Prism.languages.javascript
791 },
792 {
793 pattern: /(\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*=>)/,
794 lookbehind: true,
795 inside: Prism.languages.javascript
796 },
797 {
798 pattern: /((?:\b|\s|^)(?!(?: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|undefined|var|void|while|with|yield)(?![$\w\xA0-\uFFFF]))(?:[_$A-Za-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*\s*)\(\s*)(?!\s)(?:[^()]|\([^()]*\))+?(?=\s*\)\s*\{)/,
799 lookbehind: true,
800 inside: Prism.languages.javascript
801 }
802 ],
803 'constant': /\b[A-Z](?:[A-Z_]|\dx?)*\b/
721});804});
722 805
723Prism.languages.insertBefore('javascript', 'string', {806Prism.languages.insertBefore('javascript', 'string', {
724 'template-string': {807 'template-string': {
725 pattern: /`(?:\\[\s\S]|\${[^}]+}|[^\\`])*`/,808 pattern: /`(?:\\[\s\S]|\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}|(?!\${)[^\\`])*`/,
726 greedy: true,809 greedy: true,
727 inside: {810 inside: {
728 'interpolation': {811 'template-punctuation': {
729 pattern: /\${[^}]+}/,812 pattern: /^`|`$/,
730 inside: {813 alias: 'string'
731 'interpolation-punctuation': {814 },
732 pattern: /^\${|}$/,815 'interpolation': {
733 alias: 'punctuation'816 pattern: /((?:^|[^\\])(?:\\{2})*)\${(?:[^{}]|{(?:[^{}]|{[^}]*})*})+}/,
734 },817 lookbehind: true,
735 rest: null // See below818 inside: {
736 }819 'interpolation-punctuation': {
737 },820 pattern: /^\${|}$/,
738 'string': /[\s\S]+/821 alias: 'punctuation'
739 }822 },
740 }823 rest: Prism.languages.javascript
824 }
825 },
826 'string': /[\s\S]+/
827 }
828 }
741});829});
742Prism.languages.javascript['template-string'].inside['interpolation'].inside.rest = Prism.languages.javascript;
743 830
744if (Prism.languages.markup) {831if (Prism.languages.markup) {
745 Prism.languages.insertBefore('markup', 'tag', {832 Prism.languages.markup.tag.addInlined('script', 'javascript');
746 'script': {
747 pattern: /(<script[\s\S]*?>)[\s\S]*?(?=<\/script>)/i,
748 lookbehind: true,
749 inside: Prism.languages.javascript,
750 alias: 'language-javascript',
751 greedy: true
752 }
753 });
754}833}
755 834
756Prism.languages.js = Prism.languages.javascript;835Prism.languages.js = Prism.languages.javascript;
757 836
758Prism.languages.c = Prism.languages.extend('clike', {837Prism.languages.c = Prism.languages.extend('clike', {
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/,838 'class-name': {
760 'operator': /-[>-]?|\+\+?|!=?|<<?=?|>>?=?|==?|&&?|\|\|?|[~^%?*\/]/,839 pattern: /(\b(?:enum|struct)\s+)\w+/,
761 'number': /(?:\b0x[\da-f]+|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?)[ful]*/i840 lookbehind: true
841 },
842 '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/,
843 'operator': />>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?/,
844 'number': /(?:\b0x(?:[\da-f]+\.?[\da-f]*|\.[\da-f]+)(?:p[+-]?\d+)?|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?)[ful]*/i
762});845});
763 846
764Prism.languages.insertBefore('c', 'string', {847Prism.languages.insertBefore('c', 'string', {
765 'macro': {848 'macro': {
766 // allow for multiline macro definitions849 // allow for multiline macro definitions
767 // spaces after the # character compile fine with gcc850 // spaces after the # character compile fine with gcc
768 pattern: /(^\s*)#\s*[a-z]+(?:[^\r\n\\]|\\(?:\r\n|[\s\S]))*/im,851 pattern: /(^\s*)#\s*[a-z]+(?:[^\r\n\\]|\\(?:\r\n|[\s\S]))*/im,
769 lookbehind: true,852 lookbehind: true,
770 alias: 'property',853 alias: 'property',
771 inside: {854 inside: {
772 // highlight the path of the include statement as a string855 // highlight the path of the include statement as a string
773 'string': {856 'string': {
774 pattern: /(#\s*include\s*)(?:<.+?>|("|')(?:\\?.)+?\2)/,857 pattern: /(#\s*include\s*)(?:<.+?>|("|')(?:\\?.)+?\2)/,
775 lookbehind: true858 lookbehind: true
776 },859 },
777 // highlight macro directives as keywords860 // highlight macro directives as keywords
778 'directive': {861 'directive': {
779 pattern: /(#\s*)\b(?:define|defined|elif|else|endif|error|ifdef|ifndef|if|import|include|line|pragma|undef|using)\b/,862 pattern: /(#\s*)\b(?:define|defined|elif|else|endif|error|ifdef|ifndef|if|import|include|line|pragma|undef|using)\b/,
780 lookbehind: true,863 lookbehind: true,
781 alias: 'keyword'864 alias: 'keyword'
782 }865 }
783 }866 }
784 },867 },
785 // highlight predefined macros as constants868 // highlight predefined macros as constants
786 'constant': /\b(?:__FILE__|__LINE__|__DATE__|__TIME__|__TIMESTAMP__|__func__|EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|stdin|stdout|stderr)\b/869 'constant': /\b(?:__FILE__|__LINE__|__DATE__|__TIME__|__TIMESTAMP__|__func__|EOF|NULL|SEEK_CUR|SEEK_END|SEEK_SET|stdin|stdout|stderr)\b/
787});870});
788 871
789delete Prism.languages.c['class-name'];
790delete Prism.languages.c['boolean'];872delete Prism.languages.c['boolean'];
791 873
792(function(Prism) {874(function(Prism) {
793 var insideString = {875 // $ set | grep '^[A-Z][^[:space:]]*=' | cut -d= -f1 | tr '\n' '|'
794 variable: [876 // + LC_ALL, RANDOM, REPLY, SECONDS.
795 // Arithmetic Environment877 // + make sure PS1..4 are here as they are not always set,
796 {878 // - some useless things.
797 pattern: /\$?\(\([\s\S]+?\)\)/,879 var envVars = '\\b(?:BASH|BASHOPTS|BASH_ALIASES|BASH_ARGC|BASH_ARGV|BASH_CMDS|BASH_COMPLETION_COMPAT_DIR|BASH_LINENO|BASH_REMATCH|BASH_SOURCE|BASH_VERSINFO|BASH_VERSION|COLORTERM|COLUMNS|COMP_WORDBREAKS|DBUS_SESSION_BUS_ADDRESS|DEFAULTS_PATH|DESKTOP_SESSION|DIRSTACK|DISPLAY|EUID|GDMSESSION|GDM_LANG|GNOME_KEYRING_CONTROL|GNOME_KEYRING_PID|GPG_AGENT_INFO|GROUPS|HISTCONTROL|HISTFILE|HISTFILESIZE|HISTSIZE|HOME|HOSTNAME|HOSTTYPE|IFS|INSTANCE|JOB|LANG|LANGUAGE|LC_ADDRESS|LC_ALL|LC_IDENTIFICATION|LC_MEASUREMENT|LC_MONETARY|LC_NAME|LC_NUMERIC|LC_PAPER|LC_TELEPHONE|LC_TIME|LESSCLOSE|LESSOPEN|LINES|LOGNAME|LS_COLORS|MACHTYPE|MAILCHECK|MANDATORY_PATH|NO_AT_BRIDGE|OLDPWD|OPTERR|OPTIND|ORBIT_SOCKETDIR|OSTYPE|PAPERSIZE|PATH|PIPESTATUS|PPID|PS1|PS2|PS3|PS4|PWD|RANDOM|REPLY|SECONDS|SELINUX_INIT|SESSION|SESSIONTYPE|SESSION_MANAGER|SHELL|SHELLOPTS|SHLVL|SSH_AUTH_SOCK|TERM|UID|UPSTART_EVENTS|UPSTART_INSTANCE|UPSTART_JOB|UPSTART_SESSION|USER|WINDOWID|XAUTHORITY|XDG_CONFIG_DIRS|XDG_CURRENT_DESKTOP|XDG_DATA_DIRS|XDG_GREETER_DATA_DIR|XDG_MENU_PREFIX|XDG_RUNTIME_DIR|XDG_SEAT|XDG_SEAT_PATH|XDG_SESSION_DESKTOP|XDG_SESSION_ID|XDG_SESSION_PATH|XDG_SESSION_TYPE|XDG_VTNR|XMODIFIERS)\\b';
798 inside: {880 var insideString = {
799 // If there is a $ sign at the beginning highlight $(( and )) as variable881 'environment': {
800 variable: [{882 pattern: RegExp("\\$" + envVars),
801 pattern: /(^\$\(\([\s\S]+)\)\)/,883 alias: 'constant'
802 lookbehind: true884 },
803 },885 'variable': [
804 /^\$\(\(/886 // [0]: Arithmetic Environment
805 ],887 {
806 number: /\b0x[\dA-Fa-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:[Ee]-?\d+)?/,888 pattern: /\$?\(\([\s\S]+?\)\)/,
807 // Operators according to https://www.gnu.org/software/bash/manual/bashref.html#Shell-Arithmetic889 greedy: true,
808 operator: /--?|-=|\+\+?|\+=|!=?|~|\*\*?|\*=|\/=?|%=?|<<=?|>>=?|<=?|>=?|==?|&&?|&=|\^=?|\|\|?|\|=|\?|:/,890 inside: {
809 // If there is no $ sign at the beginning highlight (( and )) as punctuation891 // If there is a $ sign at the beginning highlight $(( and )) as variable
810 punctuation: /\(\(?|\)\)?|,|;/892 'variable': [
811 }893 {
812 },894 pattern: /(^\$\(\([\s\S]+)\)\)/,
813 // Command Substitution895 lookbehind: true
814 {896 },
815 pattern: /\$\([^)]+\)|`[^`]+`/,897 /^\$\(\(/
816 greedy: true,898 ],
817 inside: {899 'number': /\b0x[\dA-Fa-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:[Ee]-?\d+)?/,
818 variable: /^\$\(|^`|\)$|`$/900 // Operators according to https://www.gnu.org/software/bash/manual/bashref.html#Shell-Arithmetic
819 }901 'operator': /--?|-=|\+\+?|\+=|!=?|~|\*\*?|\*=|\/=?|%=?|<<=?|>>=?|<=?|>=?|==?|&&?|&=|\^=?|\|\|?|\|=|\?|:/,
820 },902 // If there is no $ sign at the beginning highlight (( and )) as punctuation
821 /\$(?:[\w#?*!@]+|\{[^}]+\})/i903 'punctuation': /\(\(?|\)\)?|,|;/
822 ]904 }
823 };905 },
824 906 // [1]: Command Substitution
825 Prism.languages.bash = {907 {
826 'shebang': {908 pattern: /\$\((?:\([^)]+\)|[^()])+\)|`[^`]+`/,
827 pattern: /^#!\s*\/bin\/bash|^#!\s*\/bin\/sh/,909 greedy: true,
828 alias: 'important'910 inside: {
829 },911 'variable': /^\$\(|^`|\)$|`$/
830 'comment': {912 }
831 pattern: /(^|[^"{\\])#.*/,913 },
832 lookbehind: true914 // [2]: Brace expansion
833 },915 {
834 'string': [916 pattern: /\$\{[^}]+\}/,
835 //Support for Here-Documents https://en.wikipedia.org/wiki/Here_document917 greedy: true,
836 {918 inside: {
837 pattern: /((?:^|[^<])<<\s*)["']?(\w+?)["']?\s*\r?\n(?:[\s\S])*?\r?\n\2/,919 'operator': /:[-=?+]?|[!\/]|##?|%%?|\^\^?|,,?/,
838 lookbehind: true,920 'punctuation': /[\[\]]/,
839 greedy: true,921 'environment': {
840 inside: insideString922 pattern: RegExp("(\\{)" + envVars),
841 },923 lookbehind: true,
842 {924 alias: 'constant'
843 pattern: /(["'])(?:\\[\s\S]|\$\([^)]+\)|`[^`]+`|(?!\1)[^\\])*\1/,925 }
844 greedy: true,926 }
845 inside: insideString927 },
846 }928 /\$(?:\w+|[#?*!@$])/
847 ],929 ],
848 'variable': insideString.variable,930 // Escape sequences from echo and printf's manuals, and escaped quotes.
849 // Originally based on http://ss64.com/bash/931 'entity': /\\(?:[abceEfnrtv\\"]|O?[0-7]{1,3}|x[0-9a-fA-F]{1,2}|u[0-9a-fA-F]{4}|U[0-9a-fA-F]{8})/
850 'function': {932 };
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;|&])/,933
852 lookbehind: true934 Prism.languages.bash = {
853 },935 'shebang': {
854 'keyword': {936 pattern: /^#!\s*\/.*/,
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;|&])/,937 alias: 'important'
856 lookbehind: true938 },
857 },939 'comment': {
858 'boolean': {940 pattern: /(^|[^"{\\$])#.*/,
859 pattern: /(^|[\s;|&])(?:true|false)(?=$|[\s;|&])/,941 lookbehind: true
860 lookbehind: true942 },
861 },943 'function-name': [
862 'operator': /&&?|\|\|?|==?|!=?|<<<?|>>|<=?|>=?|=~/,944 // a) function foo {
863 'punctuation': /\$?\(\(?|\)\)?|\.\.|[{}[\];]/945 // b) foo() {
864 };946 // c) function foo() {
865 947 // but not “foo {”
866 var inside = insideString.variable[1].inside;948 {
867 inside.string = Prism.languages.bash.string;949 // a) and c)
868 inside['function'] = Prism.languages.bash['function'];950 pattern: /(\bfunction\s+)\w+(?=(?:\s*\(?:\s*\))?\s*\{)/,
869 inside.keyword = Prism.languages.bash.keyword;951 lookbehind: true,
870 inside['boolean'] = Prism.languages.bash['boolean'];952 alias: 'function'
871 inside.operator = Prism.languages.bash.operator;953 },
872 inside.punctuation = Prism.languages.bash.punctuation;954 {
873 955 // b)
874 Prism.languages.shell = Prism.languages.bash;956 pattern: /\b\w+(?=\s*\(\s*\)\s*\{)/,
957 alias: 'function'
958 }
959 ],
960 // Highlight variable names as variables in for and select beginnings.
961 'for-or-select': {
962 pattern: /(\b(?:for|select)\s+)\w+(?=\s+in\s)/,
963 alias: 'variable',
964 lookbehind: true
965 },
966 // Highlight variable names as variables in the left-hand part
967 // of assignments (“=” and “+=”).
968 'assign-left': {
969 pattern: /(^|[\s;|&]|[<>]\()\w+(?=\+?=)/,
970 inside: {
971 'environment': {
972 pattern: RegExp("(^|[\\s;|&]|[<>]\\()" + envVars),
973 lookbehind: true,
974 alias: 'constant'
975 }
976 },
977 alias: 'variable',
978 lookbehind: true
979 },
980 'string': [
981 // Support for Here-documents https://en.wikipedia.org/wiki/Here_document
982 {
983 pattern: /((?:^|[^<])<<-?\s*)(\w+?)\s*(?:\r?\n|\r)(?:[\s\S])*?(?:\r?\n|\r)\2/,
984 lookbehind: true,
985 greedy: true,
986 inside: insideString
987 },
988 // Here-document with quotes around the tag
989 // → No expansion (so no “inside”).
990 {
991 pattern: /((?:^|[^<])<<-?\s*)(["'])(\w+)\2\s*(?:\r?\n|\r)(?:[\s\S])*?(?:\r?\n|\r)\3/,
992 lookbehind: true,
993 greedy: true
994 },
995 // “Normal” string
996 {
997 pattern: /(["'])(?:\\[\s\S]|\$\([^)]+\)|`[^`]+`|(?!\1)[^\\])*\1/,
998 greedy: true,
999 inside: insideString
1000 }
1001 ],
1002 'environment': {
1003 pattern: RegExp("\\$?" + envVars),
1004 alias: 'constant'
1005 },
1006 'variable': insideString.variable,
1007 'function': {
1008 pattern: /(^|[\s;|&]|[<>]\()(?:add|apropos|apt|aptitude|apt-cache|apt-get|aspell|automysqlbackup|awk|basename|bash|bc|bconsole|bg|bzip2|cal|cat|cfdisk|chgrp|chkconfig|chmod|chown|chroot|cksum|clear|cmp|column|comm|cp|cron|crontab|csplit|curl|cut|date|dc|dd|ddrescue|debootstrap|df|diff|diff3|dig|dir|dircolors|dirname|dirs|dmesg|du|egrep|eject|env|ethtool|expand|expect|expr|fdformat|fdisk|fg|fgrep|file|find|fmt|fold|format|free|fsck|ftp|fuser|gawk|git|gparted|grep|groupadd|groupdel|groupmod|groups|grub-mkconfig|gzip|halt|head|hg|history|host|hostname|htop|iconv|id|ifconfig|ifdown|ifup|import|install|ip|jobs|join|kill|killall|less|link|ln|locate|logname|logrotate|look|lpc|lpr|lprint|lprintd|lprintq|lprm|ls|lsof|lynx|make|man|mc|mdadm|mkconfig|mkdir|mke2fs|mkfifo|mkfs|mkisofs|mknod|mkswap|mmv|more|most|mount|mtools|mtr|mutt|mv|nano|nc|netstat|nice|nl|nohup|notify-send|npm|nslookup|op|open|parted|passwd|paste|pathchk|ping|pkill|pnpm|popd|pr|printcap|printenv|ps|pushd|pv|quota|quotacheck|quotactl|ram|rar|rcp|reboot|remsync|rename|renice|rev|rm|rmdir|rpm|rsync|scp|screen|sdiff|sed|sendmail|seq|service|sftp|sh|shellcheck|shuf|shutdown|sleep|slocate|sort|split|ssh|stat|strace|su|sudo|sum|suspend|swapon|sync|tac|tail|tar|tee|time|timeout|top|touch|tr|traceroute|tsort|tty|umount|uname|unexpand|uniq|units|unrar|unshar|unzip|update-grub|uptime|useradd|userdel|usermod|users|uudecode|uuencode|v|vdir|vi|vim|virsh|vmstat|wait|watch|wc|wget|whereis|which|who|whoami|write|xargs|xdg-open|yarn|yes|zenity|zip|zsh|zypper)(?=$|[)\s;|&])/,
1009 lookbehind: true
1010 },
1011 'keyword': {
1012 pattern: /(^|[\s;|&]|[<>]\()(?:if|then|else|elif|fi|for|while|in|case|esac|function|select|do|done|until)(?=$|[)\s;|&])/,
1013 lookbehind: true
1014 },
1015 // https://www.gnu.org/software/bash/manual/html_node/Shell-Builtin-Commands.html
1016 'builtin': {
1017 pattern: /(^|[\s;|&]|[<>]\()(?:\.|:|break|cd|continue|eval|exec|exit|export|getopts|hash|pwd|readonly|return|shift|test|times|trap|umask|unset|alias|bind|builtin|caller|command|declare|echo|enable|help|let|local|logout|mapfile|printf|read|readarray|source|type|typeset|ulimit|unalias|set|shopt)(?=$|[)\s;|&])/,
1018 lookbehind: true,
1019 // Alias added to make those easier to distinguish from strings.
1020 alias: 'class-name'
1021 },
1022 'boolean': {
1023 pattern: /(^|[\s;|&]|[<>]\()(?:true|false)(?=$|[)\s;|&])/,
1024 lookbehind: true
1025 },
1026 'file-descriptor': {
1027 pattern: /\B&\d\b/,
1028 alias: 'important'
1029 },
1030 'operator': {
1031 // Lots of redirections here, but not just that.
1032 pattern: /\d?<>|>\||\+=|==?|!=?|=~|<<[<-]?|[&\d]?>>|\d?[<>]&?|&[>&]?|\|[&|]?|<=?|>=?/,
1033 inside: {
1034 'file-descriptor': {
1035 pattern: /^\d/,
1036 alias: 'important'
1037 }
1038 }
1039 },
1040 'punctuation': /\$?\(\(?|\)\)?|\.\.|[{}[\];\\]/,
1041 'number': {
1042 pattern: /(^|\s)(?:[1-9]\d*|0)(?:[.,]\d+)?\b/,
1043 lookbehind: true
1044 }
1045 };
1046
1047 /* Patterns in command substitution. */
1048 var toBeCopied = [
1049 'comment',
1050 'function-name',
1051 'for-or-select',
1052 'assign-left',
1053 'string',
1054 'environment',
1055 'function',
1056 'keyword',
1057 'builtin',
1058 'boolean',
1059 'file-descriptor',
1060 'operator',
1061 'punctuation',
1062 'number'
1063 ];
1064 var inside = insideString.variable[1].inside;
1065 for(var i = 0; i < toBeCopied.length; i++) {
1066 inside[toBeCopied[i]] = Prism.languages.bash[toBeCopied[i]];
1067 }
1068
1069 Prism.languages.shell = Prism.languages.bash;
875})(Prism);1070})(Prism);
876 1071
877Prism.languages.cpp = Prism.languages.extend('c', {1072Prism.languages.cpp = Prism.languages.extend('c', {
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/,1073 'class-name': {
879 'boolean': /\b(?:true|false)\b/,1074 pattern: /(\b(?:class|enum|struct)\s+)\w+/,
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/1075 lookbehind: true
881});1076 },
882 1077 '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/,
883Prism.languages.insertBefore('cpp', 'keyword', {1078 'number': {
884 'class-name': {1079 pattern: /(?:\b0b[01']+|\b0x(?:[\da-f']+\.?[\da-f']*|\.[\da-f']+)(?:p[+-]?[\d']+)?|(?:\b[\d']+\.?[\d']*|\B\.[\d']+)(?:e[+-]?[\d']+)?)[ful]*/i,
885 pattern: /(class\s+)\w+/i,1080 greedy: true
886 lookbehind: true1081 },
887 }1082 'operator': />>=?|<<=?|->|([-+&|:])\1|[?:~]|[-+*/%&|^!=<>]=?|\b(?:and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/,
1083 'boolean': /\b(?:true|false)\b/
888});1084});
889 1085
890Prism.languages.insertBefore('cpp', 'string', {1086Prism.languages.insertBefore('cpp', 'string', {
891 'raw-string': {1087 'raw-string': {
892 pattern: /R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/,1088 pattern: /R"([^()\\ ]{0,16})\([\s\S]*?\)\1"/,
893 alias: 'string',1089 alias: 'string',
894 greedy: true1090 greedy: true
895 }1091 }
896});1092});
897 1093
898(function(Prism) {1094(function(Prism) {
899 1095
900// Ignore comments starting with { to privilege string interpolation highlighting1096// Ignore comments starting with { to privilege string interpolation highlighting
901var comment = /#(?!\{).+/,1097 var comment = /#(?!\{).+/,
902 interpolation = {1098 interpolation = {
903 pattern: /#\{[^}]+\}/,1099 pattern: /#\{[^}]+\}/,
904 alias: 'variable'1100 alias: 'variable'
905 };1101 };
906 1102
907Prism.languages.coffeescript = Prism.languages.extend('javascript', {1103 Prism.languages.coffeescript = Prism.languages.extend('javascript', {
908 'comment': comment,1104 'comment': comment,
909 'string': [1105 'string': [
910 1106
911 // Strings are multiline1107 // Strings are multiline
912 {1108 {
913 pattern: /'(?:\\[\s\S]|[^\\'])*'/,1109 pattern: /'(?:\\[\s\S]|[^\\'])*'/,
914 greedy: true1110 greedy: true
915 },1111 },
916 1112
917 {1113 {
918 // Strings are multiline1114 // Strings are multiline
919 pattern: /"(?:\\[\s\S]|[^\\"])*"/,1115 pattern: /"(?:\\[\s\S]|[^\\"])*"/,
920 greedy: true,1116 greedy: true,
921 inside: {1117 inside: {
922 'interpolation': interpolation1118 'interpolation': interpolation
923 }1119 }
924 }1120 }
925 ],1121 ],
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/,1122 '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/,
927 'class-member': {1123 'class-member': {
928 pattern: /@(?!\d)\w+/,1124 pattern: /@(?!\d)\w+/,
929 alias: 'variable'1125 alias: 'variable'
930 }1126 }
931});1127 });
932 1128
933Prism.languages.insertBefore('coffeescript', 'comment', {1129 Prism.languages.insertBefore('coffeescript', 'comment', {
934 'multiline-comment': {1130 'multiline-comment': {
935 pattern: /###[\s\S]+?###/,1131 pattern: /###[\s\S]+?###/,
936 alias: 'comment'1132 alias: 'comment'
937 },1133 },
938 1134
939 // Block regexp can contain comments and interpolation1135 // Block regexp can contain comments and interpolation
940 'block-regex': {1136 'block-regex': {
941 pattern: /\/{3}[\s\S]*?\/{3}/,1137 pattern: /\/{3}[\s\S]*?\/{3}/,
942 alias: 'regex',1138 alias: 'regex',
943 inside: {1139 inside: {
944 'comment': comment,1140 'comment': comment,
945 'interpolation': interpolation1141 'interpolation': interpolation
946 }1142 }
947 }1143 }
948});1144 });
949 1145
950Prism.languages.insertBefore('coffeescript', 'string', {1146 Prism.languages.insertBefore('coffeescript', 'string', {
951 'inline-javascript': {1147 'inline-javascript': {
952 pattern: /`(?:\\[\s\S]|[^\\`])*`/,1148 pattern: /`(?:\\[\s\S]|[^\\`])*`/,
953 inside: {1149 inside: {
954 'delimiter': {1150 'delimiter': {
955 pattern: /^`|`$/,1151 pattern: /^`|`$/,
956 alias: 'punctuation'1152 alias: 'punctuation'
957 },1153 },
958 rest: Prism.languages.javascript1154 rest: Prism.languages.javascript
959 }1155 }
960 },1156 },
961 1157
962 // Block strings1158 // Block strings
963 'multiline-string': [1159 'multiline-string': [
964 {1160 {
965 pattern: /'''[\s\S]*?'''/,1161 pattern: /'''[\s\S]*?'''/,
966 greedy: true,1162 greedy: true,
967 alias: 'string'1163 alias: 'string'
968 },1164 },
969 {1165 {
970 pattern: /"""[\s\S]*?"""/,1166 pattern: /"""[\s\S]*?"""/,
971 greedy: true,1167 greedy: true,
972 alias: 'string',1168 alias: 'string',
973 inside: {1169 inside: {
974 interpolation: interpolation1170 interpolation: interpolation
975 }1171 }
976 }1172 }
977 ]1173 ]
978 1174
979});1175 });
980 1176
981Prism.languages.insertBefore('coffeescript', 'keyword', {1177 Prism.languages.insertBefore('coffeescript', 'keyword', {
982 // Object property1178 // Object property
983 'property': /(?!\d)\w+(?=\s*:(?!:))/1179 'property': /(?!\d)\w+(?=\s*:(?!:))/
984});1180 });
985 1181
986delete Prism.languages.coffeescript['template-string'];1182 delete Prism.languages.coffeescript['template-string'];
987 1183
1184 Prism.languages.coffee = Prism.languages.coffeescript;
988}(Prism));1185}(Prism));
989/**1186/**
990 * Original by Samuel Flores1187 * Original by Samuel Flores
@@ -993,1294 +1190,1461 @@ delete Prism.languages.coffeescript['template-string']; · 2 unmodified lines
993 * constant, builtin, variable, symbol, regex1190 * constant, builtin, variable, symbol, regex
994 */1191 */
995(function(Prism) {1192(function(Prism) {
996 Prism.languages.ruby = Prism.languages.extend('clike', {1193 Prism.languages.ruby = Prism.languages.extend('clike', {
997 'comment': [1194 'comment': [
998 /#.*/,1195 /#.*/,
999 {1196 {
1000 pattern: /^=begin(?:\r?\n|\r)(?:.*(?:\r?\n|\r))*?=end/m,1197 pattern: /^=begin\s[\s\S]*?^=end/m,
1001 greedy: true1198 greedy: true
1002 }1199 }
1003 ],1200 ],
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/1201 '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/
1005 });1202 });
1006 1203
1007 var interpolation = {1204 var interpolation = {
1008 pattern: /#\{[^}]+\}/,1205 pattern: /#\{[^}]+\}/,
1009 inside: {1206 inside: {
1010 'delimiter': {1207 'delimiter': {
1011 pattern: /^#\{|\}$/,1208 pattern: /^#\{|\}$/,
1012 alias: 'tag'1209 alias: 'tag'
1013 },1210 },
1014 rest: Prism.languages.ruby1211 rest: Prism.languages.ruby
1015 }1212 }
1016 };1213 };
1017 1214
1018 Prism.languages.insertBefore('ruby', 'keyword', {1215 delete Prism.languages.ruby.function;
1019 'regex': [1216
1020 {1217 Prism.languages.insertBefore('ruby', 'keyword', {
1021 pattern: /%r([^a-zA-Z0-9\s{(\[<])(?:(?!\1)[^\\]|\\[\s\S])*\1[gim]{0,3}/,1218 'regex': [
1022 greedy: true,1219 {
1023 inside: {1220 pattern: /%r([^a-zA-Z0-9\s{(\[<])(?:(?!\1)[^\\]|\\[\s\S])*\1[gim]{0,3}/,
1024 'interpolation': interpolation1221 greedy: true,
1025 }1222 inside: {
1026 },1223 'interpolation': interpolation
1027 {1224 }
1028 pattern: /%r\((?:[^()\\]|\\[\s\S])*\)[gim]{0,3}/,1225 },
1029 greedy: true,1226 {
1030 inside: {1227 pattern: /%r\((?:[^()\\]|\\[\s\S])*\)[gim]{0,3}/,
1031 'interpolation': interpolation1228 greedy: true,
1032 }1229 inside: {
1033 },1230 'interpolation': interpolation
1034 {1231 }
1035 // Here we need to specifically allow interpolation1232 },
1036 pattern: /%r\{(?:[^#{}\\]|#(?:\{[^}]+\})?|\\[\s\S])*\}[gim]{0,3}/,1233 {
1037 greedy: true,1234 // Here we need to specifically allow interpolation
1038 inside: {1235 pattern: /%r\{(?:[^#{}\\]|#(?:\{[^}]+\})?|\\[\s\S])*\}[gim]{0,3}/,
1039 'interpolation': interpolation1236 greedy: true,
1040 }1237 inside: {
1041 },1238 'interpolation': interpolation
1042 {1239 }
1043 pattern: /%r\[(?:[^\[\]\\]|\\[\s\S])*\][gim]{0,3}/,1240 },
1044 greedy: true,1241 {
1045 inside: {1242 pattern: /%r\[(?:[^\[\]\\]|\\[\s\S])*\][gim]{0,3}/,
1046 'interpolation': interpolation1243 greedy: true,
1047 }1244 inside: {
1048 },1245 'interpolation': interpolation
1049 {1246 }
1050 pattern: /%r<(?:[^<>\\]|\\[\s\S])*>[gim]{0,3}/,1247 },
1051 greedy: true,1248 {
1052 inside: {1249 pattern: /%r<(?:[^<>\\]|\\[\s\S])*>[gim]{0,3}/,
1053 'interpolation': interpolation1250 greedy: true,
1054 }1251 inside: {
1055 },1252 'interpolation': interpolation
1056 {1253 }
1057 pattern: /(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\\\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/,1254 },
1058 lookbehind: true,1255 {
1059 greedy: true1256 pattern: /(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\\\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/,
1060 }1257 lookbehind: true,
1061 ],1258 greedy: true
1062 'variable': /[@$]+[a-zA-Z_]\w*(?:[?!]|\b)/,1259 }
1063 'symbol': {1260 ],
1064 pattern: /(^|[^:]):[a-zA-Z_]\w*(?:[?!]|\b)/,1261 'variable': /[@$]+[a-zA-Z_]\w*(?:[?!]|\b)/,
1065 lookbehind: true1262 'symbol': {
1066 }1263 pattern: /(^|[^:]):[a-zA-Z_]\w*(?:[?!]|\b)/,
1067 });1264 lookbehind: true
1068 1265 },
1069 Prism.languages.insertBefore('ruby', 'number', {1266 'method-definition': {
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/,1267 pattern: /(\bdef\s+)[\w.]+/,
1071 'constant': /\b[A-Z]\w*(?:[?!]|\b)/1268 lookbehind: true,
1072 });1269 inside: {
1073 1270 'function': /\w+$/,
1074 Prism.languages.ruby.string = [1271 rest: Prism.languages.ruby
1075 {1272 }
1076 pattern: /%[qQiIwWxs]?([^a-zA-Z0-9\s{(\[<])(?:(?!\1)[^\\]|\\[\s\S])*\1/,1273 }
1077 greedy: true,1274 });
1078 inside: {1275
1079 'interpolation': interpolation1276 Prism.languages.insertBefore('ruby', 'number', {
1080 }1277 '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/,
1081 },1278 'constant': /\b[A-Z]\w*(?:[?!]|\b)/
1082 {1279 });
1083 pattern: /%[qQiIwWxs]?\((?:[^()\\]|\\[\s\S])*\)/,1280
1084 greedy: true,1281 Prism.languages.ruby.string = [
1085 inside: {1282 {
1086 'interpolation': interpolation1283 pattern: /%[qQiIwWxs]?([^a-zA-Z0-9\s{(\[<])(?:(?!\1)[^\\]|\\[\s\S])*\1/,
1087 }1284 greedy: true,
1088 },1285 inside: {
1089 {1286 'interpolation': interpolation
1090 // Here we need to specifically allow interpolation1287 }
1091 pattern: /%[qQiIwWxs]?\{(?:[^#{}\\]|#(?:\{[^}]+\})?|\\[\s\S])*\}/,1288 },
1092 greedy: true,1289 {
1093 inside: {1290 pattern: /%[qQiIwWxs]?\((?:[^()\\]|\\[\s\S])*\)/,
1094 'interpolation': interpolation1291 greedy: true,
1095 }1292 inside: {
1096 },1293 'interpolation': interpolation
1097 {1294 }
1098 pattern: /%[qQiIwWxs]?\[(?:[^\[\]\\]|\\[\s\S])*\]/,1295 },
1099 greedy: true,1296 {
1100 inside: {1297 // Here we need to specifically allow interpolation
1101 'interpolation': interpolation1298 pattern: /%[qQiIwWxs]?\{(?:[^#{}\\]|#(?:\{[^}]+\})?|\\[\s\S])*\}/,
1102 }1299 greedy: true,
1103 },1300 inside: {
1104 {1301 'interpolation': interpolation
1105 pattern: /%[qQiIwWxs]?<(?:[^<>\\]|\\[\s\S])*>/,1302 }
1106 greedy: true,1303 },
1107 inside: {1304 {
1108 'interpolation': interpolation1305 pattern: /%[qQiIwWxs]?\[(?:[^\[\]\\]|\\[\s\S])*\]/,
1109 }1306 greedy: true,
1110 },1307 inside: {
1111 {1308 'interpolation': interpolation
1112 pattern: /("|')(?:#\{[^}]+\}|\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,1309 }
1113 greedy: true,1310 },
1114 inside: {1311 {
1115 'interpolation': interpolation1312 pattern: /%[qQiIwWxs]?<(?:[^<>\\]|\\[\s\S])*>/,
1116 }1313 greedy: true,
1117 }1314 inside: {
1118 ];1315 'interpolation': interpolation
1316 }
1317 },
1318 {
1319 pattern: /("|')(?:#\{[^}]+\}|\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,
1320 greedy: true,
1321 inside: {
1322 'interpolation': interpolation
1323 }
1324 }
1325 ];
1326
1327 Prism.languages.rb = Prism.languages.ruby;
1119}(Prism));1328}(Prism));
1329
1120Prism.languages.d = Prism.languages.extend('clike', {1330Prism.languages.d = Prism.languages.extend('clike', {
1121 'string': [1331 'string': [
1122 // r"", x""1332 // r"", x""
1123 /\b[rx]"(?:\\[\s\S]|[^\\"])*"[cwd]?/,1333 /\b[rx]"(?:\\[\s\S]|[^\\"])*"[cwd]?/,
1124 // q"[]", q"()", q"<>", q"{}"1334 // q"[]", q"()", q"<>", q"{}"
1125 /\bq"(?:\[[\s\S]*?\]|\([\s\S]*?\)|<[\s\S]*?>|\{[\s\S]*?\})"/,1335 /\bq"(?:\[[\s\S]*?\]|\([\s\S]*?\)|<[\s\S]*?>|\{[\s\S]*?\})"/,
1126 // q"IDENT1336 // q"IDENT
1127 // ...1337 // ...
1128 // IDENT"1338 // IDENT"
1129 /\bq"([_a-zA-Z][_a-zA-Z\d]*)(?:\r?\n|\r)[\s\S]*?(?:\r?\n|\r)\1"/,1339 /\bq"([_a-zA-Z][_a-zA-Z\d]*)(?:\r?\n|\r)[\s\S]*?(?:\r?\n|\r)\1"/,
1130 // q"//", q"||", etc.1340 // q"//", q"||", etc.
1131 /\bq"(.)[\s\S]*?\1"/,1341 /\bq"(.)[\s\S]*?\1"/,
1132 // Characters1342 // Characters
1133 /'(?:\\'|\\?[^']+)'/,1343 /'(?:\\'|\\?[^']+)'/,
1134 1344
1135 /(["`])(?:\\[\s\S]|(?!\1)[^\\])*\1[cwd]?/1345 /(["`])(?:\\[\s\S]|(?!\1)[^\\])*\1[cwd]?/
1136 ],1346 ],
1137 1347
1138 'number': [1348 'number': [
1139 // The lookbehind and the negative look-ahead try to prevent bad highlighting of the .. operator1349 // The lookbehind and the negative look-ahead try to prevent bad highlighting of the .. operator
1140 // Hexadecimal numbers must be handled separately to avoid problems with exponent "e"1350 // Hexadecimal numbers must be handled separately to avoid problems with exponent "e"
1141 /\b0x\.?[a-f\d_]+(?:(?!\.\.)\.[a-f\d_]*)?(?:p[+-]?[a-f\d_]+)?[ulfi]*/i,1351 /\b0x\.?[a-f\d_]+(?:(?!\.\.)\.[a-f\d_]*)?(?:p[+-]?[a-f\d_]+)?[ulfi]*/i,
1142 {1352 {
1143 pattern: /((?:\.\.)?)(?:\b0b\.?|\b|\.)\d[\d_]*(?:(?!\.\.)\.[\d_]*)?(?:e[+-]?\d[\d_]*)?[ulfi]*/i,1353 pattern: /((?:\.\.)?)(?:\b0b\.?|\b|\.)\d[\d_]*(?:(?!\.\.)\.[\d_]*)?(?:e[+-]?\d[\d_]*)?[ulfi]*/i,
1144 lookbehind: true1354 lookbehind: true
1145 }1355 }
1146 ],1356 ],
1147 1357
1148 // In order: $, keywords and special tokens, globally defined symbols1358 // In order: $, keywords and special tokens, globally defined symbols
1149 'keyword': /\$|\b(?:abstract|alias|align|asm|assert|auto|body|bool|break|byte|case|cast|catch|cdouble|cent|cfloat|char|class|const|continue|creal|dchar|debug|default|delegate|delete|deprecated|do|double|else|enum|export|extern|false|final|finally|float|for|foreach|foreach_reverse|function|goto|idouble|if|ifloat|immutable|import|inout|int|interface|invariant|ireal|lazy|long|macro|mixin|module|new|nothrow|null|out|override|package|pragma|private|protected|public|pure|real|ref|return|scope|shared|short|static|struct|super|switch|synchronized|template|this|throw|true|try|typedef|typeid|typeof|ubyte|ucent|uint|ulong|union|unittest|ushort|version|void|volatile|wchar|while|with|__(?:(?:FILE|MODULE|LINE|FUNCTION|PRETTY_FUNCTION|DATE|EOF|TIME|TIMESTAMP|VENDOR|VERSION)__|gshared|traits|vector|parameters)|string|wstring|dstring|size_t|ptrdiff_t)\b/,1359 'keyword': /\$|\b(?:abstract|alias|align|asm|assert|auto|body|bool|break|byte|case|cast|catch|cdouble|cent|cfloat|char|class|const|continue|creal|dchar|debug|default|delegate|delete|deprecated|do|double|else|enum|export|extern|false|final|finally|float|for|foreach|foreach_reverse|function|goto|idouble|if|ifloat|immutable|import|inout|int|interface|invariant|ireal|lazy|long|macro|mixin|module|new|nothrow|null|out|override|package|pragma|private|protected|public|pure|real|ref|return|scope|shared|short|static|struct|super|switch|synchronized|template|this|throw|true|try|typedef|typeid|typeof|ubyte|ucent|uint|ulong|union|unittest|ushort|version|void|volatile|wchar|while|with|__(?:(?:FILE|MODULE|LINE|FUNCTION|PRETTY_FUNCTION|DATE|EOF|TIME|TIMESTAMP|VENDOR|VERSION)__|gshared|traits|vector|parameters)|string|wstring|dstring|size_t|ptrdiff_t)\b/,
1150 'operator': /\|[|=]?|&[&=]?|\+[+=]?|-[-=]?|\.?\.\.|=[>=]?|!(?:i[ns]\b|<>?=?|>=?|=)?|\bi[ns]\b|(?:<[<>]?|>>?>?|\^\^|[*\/%^~])=?/1360 'operator': /\|[|=]?|&[&=]?|\+[+=]?|-[-=]?|\.?\.\.|=[>=]?|!(?:i[ns]\b|<>?=?|>=?|=)?|\bi[ns]\b|(?:<[<>]?|>>?>?|\^\^|[*\/%^~])=?/
1151});1361});
1152 1362
1153 1363
1154Prism.languages.d.comment = [1364Prism.languages.d.comment = [
1155 // Shebang1365 // Shebang
1156 /^\s*#!.+/,1366 /^\s*#!.+/,
1157 // /+ +/1367 // /+ +/
1158 {1368 {
1159 // Allow one level of nesting1369 // Allow one level of nesting
1160 pattern: /(^|[^\\])\/\+(?:\/\+[\s\S]*?\+\/|[\s\S])*?\+\//,1370 pattern: /(^|[^\\])\/\+(?:\/\+[\s\S]*?\+\/|[\s\S])*?\+\//,
1161 lookbehind: true1371 lookbehind: true
1162 }1372 }
1163].concat(Prism.languages.d.comment);1373].concat(Prism.languages.d.comment);
1164 1374
1165Prism.languages.insertBefore('d', 'comment', {1375Prism.languages.insertBefore('d', 'comment', {
1166 'token-string': {1376 'token-string': {
1167 // Allow one level of nesting1377 // Allow one level of nesting
1168 pattern: /\bq\{(?:\{[^}]*\}|[^}])*\}/,1378 pattern: /\bq\{(?:\{[^}]*\}|[^}])*\}/,
1169 alias: 'string'1379 alias: 'string'
1170 }1380 }
1171});1381});
1172 1382
1173Prism.languages.insertBefore('d', 'keyword', {1383Prism.languages.insertBefore('d', 'keyword', {
1174 'property': /\B@\w*/1384 'property': /\B@\w*/
1175});1385});
1176 1386
1177Prism.languages.insertBefore('d', 'function', {1387Prism.languages.insertBefore('d', 'function', {
1178 'register': {1388 'register': {
1179 // Iasm registers1389 // Iasm registers
1180 pattern: /\b(?:[ABCD][LHX]|E[ABCD]X|E?(?:BP|SP|DI|SI)|[ECSDGF]S|CR[0234]|DR[012367]|TR[3-7]|X?MM[0-7]|R[ABCD]X|[BS]PL|R[BS]P|[DS]IL|R[DS]I|R(?:[89]|1[0-5])[BWD]?|XMM(?:[89]|1[0-5])|YMM(?:1[0-5]|\d))\b|\bST(?:\([0-7]\)|\b)/,1390 pattern: /\b(?:[ABCD][LHX]|E[ABCD]X|E?(?:BP|SP|DI|SI)|[ECSDGF]S|CR[0234]|DR[012367]|TR[3-7]|X?MM[0-7]|R[ABCD]X|[BS]PL|R[BS]P|[DS]IL|R[DS]I|R(?:[89]|1[0-5])[BWD]?|XMM(?:[89]|1[0-5])|YMM(?:1[0-5]|\d))\b|\bST(?:\([0-7]\)|\b)/,
1181 alias: 'variable'1391 alias: 'variable'
1182 }1392 }
1183});1393});
1184Prism.languages.dart = Prism.languages.extend('clike', {1394Prism.languages.dart = Prism.languages.extend('clike', {
1185 'string': [1395 'string': [
1186 {1396 {
1187 pattern: /r?("""|''')[\s\S]*?\1/,1397 pattern: /r?("""|''')[\s\S]*?\1/,
1188 greedy: true1398 greedy: true
1189 },1399 },
1190 {1400 {
1191 pattern: /r?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/,1401 pattern: /r?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/,
1192 greedy: true1402 greedy: true
1193 }1403 }
1194 ],1404 ],
1195 'keyword': [1405 'keyword': [
1196 /\b(?:async|sync|yield)\*/,1406 /\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/1407 /\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 ],1408 ],
1199 'operator': /\bis!|\b(?:as|is)\b|\+\+|--|&&|\|\||<<=?|>>=?|~(?:\/=?)?|[+\-*\/%&^|=!<>]=?|\?/1409 'operator': /\bis!|\b(?:as|is)\b|\+\+|--|&&|\|\||<<=?|>>=?|~(?:\/=?)?|[+\-*\/%&^|=!<>]=?|\?/
1200});1410});
1201 1411
1202Prism.languages.insertBefore('dart','function',{1412Prism.languages.insertBefore('dart','function',{
1203 'metadata': {1413 'metadata': {
1204 pattern: /@\w+/,1414 pattern: /@\w+/,
1205 alias: 'symbol'1415 alias: 'symbol'
1206 }1416 }
1207});1417});
1208// Django/Jinja2 syntax definition for Prism.js <http://prismjs.com> syntax highlighter.1418(function (Prism) {
1209// Mostly it works OK but can paint code incorrectly on complex html/template tag combinations.
1210
1211var _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
1230Prism.languages.django = Prism.languages.extend('markup', {'comment': /(?:<!--|{#)[\s\S]*?(?:#}|-->)/});
1231// Updated html tag pattern to allow template tags inside html tags
1232Prism.languages.django.tag.pattern = /<\/?(?!\d)[^\s>\/=$<]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^>=]+))?)*\s*\/?>/i;
1233Prism.languages.insertBefore('django', 'entity', _django_template);
1234Prism.languages.insertBefore('inside', 'tag', _django_template, Prism.languages.django.tag);
1235 1419
1236if (Prism.languages.javascript) {1420 /**
1237 // Combine js code and template tags painting inside <script> blocks1421 * Returns the placeholder for the given language id and index.
1238 Prism.languages.insertBefore('inside', 'string', _django_template, Prism.languages.django.script);1422 *
1239 Prism.languages.django.script.inside.string.inside = _django_template;1423 * @param {string} language
1240}1424 * @param {string|number} index
1241if (Prism.languages.css) {1425 * @returns {string}
1242 // Combine css code and template tags painting inside <style> blocks1426 */
1243 Prism.languages.insertBefore('inside', 'atrule', {'tag': _django_template.property}, Prism.languages.django.style);1427 function getPlaceholder(language, index) {
1244 Prism.languages.django.style.inside.string.inside = _django_template;1428 return '___' + language.toUpperCase() + index + '___';
1245}1429 }
1430
1431 Object.defineProperties(Prism.languages['markup-templating'] = {}, {
1432 buildPlaceholders: {
1433 /**
1434 * Tokenize all inline templating expressions matching `placeholderPattern`.
1435 *
1436 * If `replaceFilter` is provided, only matches of `placeholderPattern` for which `replaceFilter` returns
1437 * `true` will be replaced.
1438 *
1439 * @param {object} env The environment of the `before-tokenize` hook.
1440 * @param {string} language The language id.
1441 * @param {RegExp} placeholderPattern The matches of this pattern will be replaced by placeholders.
1442 * @param {(match: string) => boolean} [replaceFilter]
1443 */
1444 value: function (env, language, placeholderPattern, replaceFilter) {
1445 if (env.language !== language) {
1446 return;
1447 }
1448
1449 var tokenStack = env.tokenStack = [];
1450
1451 env.code = env.code.replace(placeholderPattern, function (match) {
1452 if (typeof replaceFilter === 'function' && !replaceFilter(match)) {
1453 return match;
1454 }
1455 var i = tokenStack.length;
1456 var placeholder;
1457
1458 // Check for existing strings
1459 while (env.code.indexOf(placeholder = getPlaceholder(language, i)) !== -1)
1460 ++i;
1461
1462 // Create a sparse array
1463 tokenStack[i] = match;
1464
1465 return placeholder;
1466 });
1467
1468 // Switch the grammar to markup
1469 env.grammar = Prism.languages.markup;
1470 }
1471 },
1472 tokenizePlaceholders: {
1473 /**
1474 * Replace placeholders with proper tokens after tokenizing.
1475 *
1476 * @param {object} env The environment of the `after-tokenize` hook.
1477 * @param {string} language The language id.
1478 */
1479 value: function (env, language) {
1480 if (env.language !== language || !env.tokenStack) {
1481 return;
1482 }
1483
1484 // Switch the grammar back
1485 env.grammar = Prism.languages[language];
1486
1487 var j = 0;
1488 var keys = Object.keys(env.tokenStack);
1489
1490 function walkTokens(tokens) {
1491 for (var i = 0; i < tokens.length; i++) {
1492 // all placeholders are replaced already
1493 if (j >= keys.length) {
1494 break;
1495 }
1496
1497 var token = tokens[i];
1498 if (typeof token === 'string' || (token.content && typeof token.content === 'string')) {
1499 var k = keys[j];
1500 var t = env.tokenStack[k];
1501 var s = typeof token === 'string' ? token : token.content;
1502 var placeholder = getPlaceholder(language, k);
1503
1504 var index = s.indexOf(placeholder);
1505 if (index > -1) {
1506 ++j;
1507
1508 var before = s.substring(0, index);
1509 var middle = new Prism.Token(language, Prism.tokenize(t, env.grammar), 'language-' + language, t);
1510 var after = s.substring(index + placeholder.length);
1511
1512 var replacement = [];
1513 if (before) {
1514 replacement.push.apply(replacement, walkTokens([before]));
1515 }
1516 replacement.push(middle);
1517 if (after) {
1518 replacement.push.apply(replacement, walkTokens([after]));
1519 }
1520
1521 if (typeof token === 'string') {
1522 tokens.splice.apply(tokens, [i, 1].concat(replacement));
1523 } else {
1524 token.content = replacement;
1525 }
1526 }
1527 } else if (token.content /* && typeof token.content !== 'string' */) {
1528 walkTokens(token.content);
1529 }
1530 }
1531
1532 return tokens;
1533 }
1534
1535 walkTokens(env.tokens);
1536 }
1537 }
1538 });
1246 1539
1247// Add an Jinja2 alias1540}(Prism));
1248Prism.languages.jinja2 = Prism.languages.django;
1249 1541
1250Prism.languages.elixir = {1542Prism.languages.elixir = {
1251 'comment': {1543 'comment': /#.*/m,
1252 pattern: /#.*/m,1544 // ~r"""foo""" (multi-line), ~r'''foo''' (multi-line), ~r/foo/, ~r|foo|, ~r"foo", ~r'foo', ~r(foo), ~r[foo], ~r{foo}, ~r<foo>
1253 lookbehind: true1545 'regex': {
1254 },1546 pattern: /~[rR](?:("""|''')(?:\\[\s\S]|(?!\1)[^\\])+\1|([\/|"'])(?:\\.|(?!\2)[^\\\r\n])+\2|\((?:\\.|[^\\)\r\n])+\)|\[(?:\\.|[^\\\]\r\n])+\]|\{(?:\\.|[^\\}\r\n])+\}|<(?:\\.|[^\\>\r\n])+>)[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>1547 greedy: true
1256 'regex': {1548 },
1257 pattern: /~[rR](?:("""|''')(?:\\[\s\S]|(?!\1)[^\\])+\1|([\/|"'])(?:\\.|(?!\2)[^\\\r\n])+\2|\((?:\\.|[^\\)\r\n])+\)|\[(?:\\.|[^\\\]\r\n])+\]|\{(?:\\.|[^\\}\r\n])+\}|<(?:\\.|[^\\>\r\n])+>)[uismxfr]*/,1549 'string': [
1258 greedy: true1550 {
1259 },1551 // ~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>
1260 'string': [1552 pattern: /~[cCsSwW](?:("""|''')(?:\\[\s\S]|(?!\1)[^\\])+\1|([\/|"'])(?:\\.|(?!\2)[^\\\r\n])+\2|\((?:\\.|[^\\)\r\n])+\)|\[(?:\\.|[^\\\]\r\n])+\]|\{(?:\\.|#\{[^}]+\}|[^\\}\r\n])+\}|<(?:\\.|[^\\>\r\n])+>)[csa]?/,
1261 {1553 greedy: true,
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>1554 inside: {
1263 pattern: /~[cCsSwW](?:("""|''')(?:\\[\s\S]|(?!\1)[^\\])+\1|([\/|"'])(?:\\.|(?!\2)[^\\\r\n])+\2|\((?:\\.|[^\\)\r\n])+\)|\[(?:\\.|[^\\\]\r\n])+\]|\{(?:\\.|#\{[^}]+\}|[^\\}\r\n])+\}|<(?:\\.|[^\\>\r\n])+>)[csa]?/,1555 // See interpolation below
1264 greedy: true,1556 }
1265 inside: {1557 },
1266 // See interpolation below1558 {
1267 }1559 pattern: /("""|''')[\s\S]*?\1/,
1268 },1560 greedy: true,
1269 {1561 inside: {
1270 pattern: /("""|''')[\s\S]*?\1/,1562 // See interpolation below
1271 greedy: true,1563 }
1272 inside: {1564 },
1273 // See interpolation below1565 {
1274 }1566 // Multi-line strings are allowed
1275 },1567 pattern: /("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,
1276 {1568 greedy: true,
1277 // Multi-line strings are allowed1569 inside: {
1278 pattern: /("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,1570 // See interpolation below
1279 greedy: true,1571 }
1280 inside: {1572 }
1281 // See interpolation below1573 ],
1282 }1574 'atom': {
1283 }1575 // Look-behind prevents bad highlighting of the :: operator
1284 ],1576 pattern: /(^|[^:]):\w+/,
1285 'atom': {1577 lookbehind: true,
1286 // Look-behind prevents bad highlighting of the :: operator1578 alias: 'symbol'
1287 pattern: /(^|[^:]):\w+/,1579 },
1288 lookbehind: true,1580 // Look-ahead prevents bad highlighting of the :: operator
1289 alias: 'symbol'1581 'attr-name': /\w+:(?!:)/,
1290 },1582 'capture': {
1291 // Look-ahead prevents bad highlighting of the :: operator1583 // Look-behind prevents bad highlighting of the && operator
1292 'attr-name': /\w+:(?!:)/,1584 pattern: /(^|[^&])&(?:[^&\s\d()][^\s()]*|(?=\())/,
1293 'capture': {1585 lookbehind: true,
1294 // Look-behind prevents bad highlighting of the && operator1586 alias: 'function'
1295 pattern: /(^|[^&])&(?:[^&\s\d()][^\s()]*|(?=\())/,1587 },
1296 lookbehind: true,1588 'argument': {
1297 alias: 'function'1589 // Look-behind prevents bad highlighting of the && operator
1298 },1590 pattern: /(^|[^&])&\d+/,
1299 'argument': {1591 lookbehind: true,
1300 // Look-behind prevents bad highlighting of the && operator1592 alias: 'variable'
1301 pattern: /(^|[^&])&\d+/,1593 },
1302 lookbehind: true,1594 'attribute': {
1303 alias: 'variable'1595 pattern: /@\w+/,
1304 },1596 alias: 'variable'
1305 'attribute': {1597 },
1306 pattern: /@\w+/,1598 'number': /\b(?:0[box][a-f\d_]+|\d[\d_]*)(?:\.[\d_]+)?(?:e[+-]?[\d_]+)?\b/i,
1307 alias: 'variable'1599 'keyword': /\b(?:after|alias|and|case|catch|cond|def(?:callback|exception|impl|module|p|protocol|struct)?|do|else|end|fn|for|if|import|not|or|require|rescue|try|unless|use|when)\b/,
1308 },1600 'boolean': /\b(?:true|false|nil)\b/,
1309 'number': /\b(?:0[box][a-f\d_]+|\d[\d_]*)(?:\.[\d_]+)?(?:e[+-]?[\d_]+)?\b/i,1601 'operator': [
1310 'keyword': /\b(?:after|alias|and|case|catch|cond|def(?:callback|exception|impl|module|p|protocol|struct)?|do|else|end|fn|for|if|import|not|or|require|rescue|try|unless|use|when)\b/,1602 /\bin\b|&&?|\|[|>]?|\\\\|::|\.\.\.?|\+\+?|-[->]?|<[-=>]|>=|!==?|\B!|=(?:==?|[>~])?|[*\/^]/,
1311 'boolean': /\b(?:true|false|nil)\b/,1603 {
1312 'operator': [1604 // We don't want to match <<
1313 /\bin\b|&&?|\|[|>]?|\\\\|::|\.\.\.?|\+\+?|-[->]?|<[-=>]|>=|!==?|\B!|=(?:==?|[>~])?|[*\/^]/,1605 pattern: /([^<])<(?!<)/,
1314 {1606 lookbehind: true
1315 // We don't want to match <<1607 },
1316 pattern: /([^<])<(?!<)/,1608 {
1317 lookbehind: true1609 // We don't want to match >>
1318 },1610 pattern: /([^>])>(?!>)/,
1319 {1611 lookbehind: true
1320 // We don't want to match >>1612 }
1321 pattern: /([^>])>(?!>)/,1613 ],
1322 lookbehind: true1614 'punctuation': /<<|>>|[.,%\[\]{}()]/
1323 }
1324 ],
1325 'punctuation': /<<|>>|[.,%\[\]{}()]/
1326};1615};
1327 1616
1328Prism.languages.elixir.string.forEach(function(o) {1617Prism.languages.elixir.string.forEach(function(o) {
1329 o.inside = {1618 o.inside = {
1330 'interpolation': {1619 'interpolation': {
1331 pattern: /#\{[^}]+\}/,1620 pattern: /#\{[^}]+\}/,
1332 inside: {1621 inside: {
1333 'delimiter': {1622 'delimiter': {
1334 pattern: /^#\{|\}$/,1623 pattern: /^#\{|\}$/,
1335 alias: 'punctuation'1624 alias: 'punctuation'
1336 },1625 },
1337 rest: Prism.languages.elixir1626 rest: Prism.languages.elixir
1338 }1627 }
1339 }1628 }
1340 };1629 };
1341});1630});
1342 1631
1343
1344Prism.languages['markup-templating'] = {};
1345
1346Object.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});
1433Prism.languages.erlang = {1632Prism.languages.erlang = {
1434 'comment': /%.+/,1633 'comment': /%.+/,
1435 'string': {1634 'string': {
1436 pattern: /"(?:\\.|[^\\"\r\n])*"/,1635 pattern: /"(?:\\.|[^\\"\r\n])*"/,
1437 greedy: true1636 greedy: true
1438 },1637 },
1439 'quoted-function': {1638 'quoted-function': {
1440 pattern: /'(?:\\.|[^\\'\r\n])+'(?=\()/,1639 pattern: /'(?:\\.|[^\\'\r\n])+'(?=\()/,
1441 alias: 'function'1640 alias: 'function'
1442 },1641 },
1443 'quoted-atom': {1642 'quoted-atom': {
1444 pattern: /'(?:\\.|[^\\'\r\n])+'/,1643 pattern: /'(?:\\.|[^\\'\r\n])+'/,
1445 alias: 'atom'1644 alias: 'atom'
1446 },1645 },
1447 'boolean': /\b(?:true|false)\b/,1646 'boolean': /\b(?:true|false)\b/,
1448 'keyword': /\b(?:fun|when|case|of|end|if|receive|after|try|catch)\b/,1647 'keyword': /\b(?:fun|when|case|of|end|if|receive|after|try|catch)\b/,
1449 'number': [1648 'number': [
1450 /\$\\?./,1649 /\$\\?./,
1451 /\d+#[a-z0-9]+/i,1650 /\d+#[a-z0-9]+/i,
1452 /(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i1651 /(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i
1453 ],1652 ],
1454 'function': /\b[a-z][\w@]*(?=\()/,1653 'function': /\b[a-z][\w@]*(?=\()/,
1455 'variable': {1654 'variable': {
1456 // Look-behind is used to prevent wrong highlighting of atoms containing "@"1655 // Look-behind is used to prevent wrong highlighting of atoms containing "@"
1457 pattern: /(^|[^@])(?:\b|\?)[A-Z_][\w@]*/,1656 pattern: /(^|[^@])(?:\b|\?)[A-Z_][\w@]*/,
1458 lookbehind: true1657 lookbehind: true
1459 },1658 },
1460 'operator': [1659 'operator': [
1461 /[=\/<>:]=|=[:\/]=|\+\+?|--?|[=*\/!]|\b(?:bnot|div|rem|band|bor|bxor|bsl|bsr|not|and|or|xor|orelse|andalso)\b/,1660 /[=\/<>:]=|=[:\/]=|\+\+?|--?|[=*\/!]|\b(?:bnot|div|rem|band|bor|bxor|bsl|bsr|not|and|or|xor|orelse|andalso)\b/,
1462 {1661 {
1463 // We don't want to match <<1662 // We don't want to match <<
1464 pattern: /(^|[^<])<(?!<)/,1663 pattern: /(^|[^<])<(?!<)/,
1465 lookbehind: true1664 lookbehind: true
1466 },1665 },
1467 {1666 {
1468 // We don't want to match >>1667 // We don't want to match >>
1469 pattern: /(^|[^>])>(?!>)/,1668 pattern: /(^|[^>])>(?!>)/,
1470 lookbehind: true1669 lookbehind: true
1471 }1670 }
1472 ],1671 ],
1473 'atom': /\b[a-z][\w@]*/,1672 'atom': /\b[a-z][\w@]*/,
1474 'punctuation': /[()[\]{}:;,.#|]|<<|>>/1673 'punctuation': /[()[\]{}:;,.#|]|<<|>>/
1475 1674
1476};1675};
1477Prism.languages.go = Prism.languages.extend('clike', {1676Prism.languages.go = Prism.languages.extend('clike', {
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/,1677 '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/,1678 '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/,1679 'boolean': /\b(?:_|iota|nil|true|false)\b/,
1481 'operator': /[*\/%^!=]=?|\+[=+]?|-[=-]?|\|[=|]?|&(?:=|&|\^=?)?|>(?:>=?|=)?|<(?:<=?|=|-)?|:=|\.\.\./,1680 'operator': /[*\/%^!=]=?|\+[=+]?|-[=-]?|\|[=|]?|&(?:=|&|\^=?)?|>(?:>=?|=)?|<(?:<=?|=|-)?|:=|\.\.\./,
1482 'number': /(?:\b0x[a-f\d]+|(?:\b\d+\.?\d*|\B\.\d+)(?:e[-+]?\d+)?)i?/i,1681 'number': /(?:\b0x[a-f\d]+|(?:\b\d+\.?\d*|\B\.\d+)(?:e[-+]?\d+)?)i?/i,
1483 'string': {1682 'string': {
1484 pattern: /(["'`])(\\[\s\S]|(?!\1)[^\\])*\1/,1683 pattern: /(["'`])(\\[\s\S]|(?!\1)[^\\])*\1/,
1485 greedy: true1684 greedy: true
1486 }1685 }
1487});1686});
1488delete Prism.languages.go['class-name'];1687delete Prism.languages.go['class-name'];
1489 1688
1490Prism.languages.java = Prism.languages.extend('clike', {1689(function (Prism) {
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,
1493 'operator': {
1494 pattern: /(^|[^.])(?:\+[+=]?|-[-=]?|!=?|<<?=?|>>?>?=?|==?|&[&=]?|\|[|=]?|\*=?|\/=?|%=?|\^=?|[?:~])/m,
1495 lookbehind: true
1496 }
1497});
1498 1690
1499Prism.languages.insertBefore('java','function', {1691 var keywords = /\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|var|null|exports|module|open|opens|provides|requires|to|transitive|uses|with)\b/;
1500 'annotation': {1692
1501 alias: 'punctuation',1693 // based on the java naming conventions
1502 pattern: /(^|[^.])@\w+/,1694 var className = /\b[A-Z](?:\w*[a-z]\w*)?\b/;
1503 lookbehind: true1695
1504 }1696 Prism.languages.java = Prism.languages.extend('clike', {
1505});1697 'class-name': [
1698 className,
1699
1700 // variables and parameters
1701 // this to support class names (or generic parameters) which do not contain a lower case letter (also works for methods)
1702 /\b[A-Z]\w*(?=\s+\w+\s*[;,=())])/
1703 ],
1704 'keyword': keywords,
1705 'function': [
1706 Prism.languages.clike.function,
1707 {
1708 pattern: /(\:\:)[a-z_]\w*/,
1709 lookbehind: true
1710 }
1711 ],
1712 'number': /\b0b[01][01_]*L?\b|\b0x[\da-f_]*\.?[\da-f_p+-]+\b|(?:\b\d[\d_]*\.?[\d_]*|\B\.\d[\d_]*)(?:e[+-]?\d[\d_]*)?[dfl]?/i,
1713 'operator': {
1714 pattern: /(^|[^.])(?:<<=?|>>>?=?|->|([-+&|])\2|[?:~]|[-+*/%&|^!=<>]=?)/m,
1715 lookbehind: true
1716 }
1717 });
1718
1719 Prism.languages.insertBefore('java', 'class-name', {
1720 'annotation': {
1721 alias: 'punctuation',
1722 pattern: /(^|[^.])@\w+/,
1723 lookbehind: true
1724 },
1725 'namespace': {
1726 pattern: /(\b(?:exports|import(?:\s+static)?|module|open|opens|package|provides|requires|to|transitive|uses|with)\s+)[a-z]\w*(\.[a-z]\w*)+/,
1727 lookbehind: true,
1728 inside: {
1729 'punctuation': /\./,
1730 }
1731 },
1732 'generics': {
1733 pattern: /<(?:[\w\s,.&?]|<(?:[\w\s,.&?]|<(?:[\w\s,.&?]|<[\w\s,.&?]*>)*>)*>)*>/,
1734 inside: {
1735 'class-name': className,
1736 'keyword': keywords,
1737 'punctuation': /[<>(),.:]/,
1738 'operator': /[?&|]/
1739 }
1740 }
1741 });
1742}(Prism));
1506 1743
1507Prism.languages.insertBefore('java', 'class-name', {1744/**
1508 'generics': {1745 * Original by Aaron Harun: http://aahacreative.com/2012/07/31/php-syntax-highlighting-prism/
1509 pattern: /<\s*\w+(?:\.\w+)?(?:\s*,\s*\w+(?:\.\w+)?)*>/i,1746 * Modified by Miles Johnson: http://milesj.me
1510 alias: 'function',1747 *
1511 inside: {1748 * Supports the following:
1512 keyword: Prism.languages.java.keyword,1749 * - Extends clike syntax
1513 punctuation: /[<>(),.:]/1750 * - Support for PHP 5.3+ (namespaces, traits, generators, etc)
1514 }1751 * - Smarter constant and function matching
1515 }1752 *
1516});1753 * Adds the following new token classes:
1754 * constant, delimiter, variable, function, package
1755 */
1756(function (Prism) {
1757 Prism.languages.php = Prism.languages.extend('clike', {
1758 'keyword': /\b(?:__halt_compiler|abstract|and|array|as|break|callable|case|catch|class|clone|const|continue|declare|default|die|do|echo|else|elseif|empty|enddeclare|endfor|endforeach|endif|endswitch|endwhile|eval|exit|extends|final|finally|for|foreach|function|global|goto|if|implements|include|include_once|instanceof|insteadof|interface|isset|list|namespace|new|or|parent|print|private|protected|public|require|require_once|return|static|switch|throw|trait|try|unset|use|var|while|xor|yield)\b/i,
1759 'boolean': {
1760 pattern: /\b(?:false|true)\b/i,
1761 alias: 'constant'
1762 },
1763 'constant': [
1764 /\b[A-Z_][A-Z0-9_]*\b/,
1765 /\b(?:null)\b/i,
1766 ],
1767 'comment': {
1768 pattern: /(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,
1769 lookbehind: true
1770 }
1771 });
1772
1773 Prism.languages.insertBefore('php', 'string', {
1774 'shell-comment': {
1775 pattern: /(^|[^\\])#.*/,
1776 lookbehind: true,
1777 alias: 'comment'
1778 }
1779 });
1780
1781 Prism.languages.insertBefore('php', 'comment', {
1782 'delimiter': {
1783 pattern: /\?>$|^<\?(?:php(?=\s)|=)?/i,
1784 alias: 'important'
1785 }
1786 });
1787
1788 Prism.languages.insertBefore('php', 'keyword', {
1789 'variable': /\$+(?:\w+\b|(?={))/i,
1790 'package': {
1791 pattern: /(\\|namespace\s+|use\s+)[\w\\]+/,
1792 lookbehind: true,
1793 inside: {
1794 punctuation: /\\/
1795 }
1796 }
1797 });
1798
1799 // Must be defined after the function pattern
1800 Prism.languages.insertBefore('php', 'operator', {
1801 'property': {
1802 pattern: /(->)[\w]+/,
1803 lookbehind: true
1804 }
1805 });
1806
1807 var string_interpolation = {
1808 pattern: /{\$(?:{(?:{[^{}]+}|[^{}]+)}|[^{}])+}|(^|[^\\{])\$+(?:\w+(?:\[.+?]|->\w+)*)/,
1809 lookbehind: true,
1810 inside: {
1811 rest: Prism.languages.php
1812 }
1813 };
1814
1815 Prism.languages.insertBefore('php', 'string', {
1816 'nowdoc-string': {
1817 pattern: /<<<'([^']+)'(?:\r\n?|\n)(?:.*(?:\r\n?|\n))*?\1;/,
1818 greedy: true,
1819 alias: 'string',
1820 inside: {
1821 'delimiter': {
1822 pattern: /^<<<'[^']+'|[a-z_]\w*;$/i,
1823 alias: 'symbol',
1824 inside: {
1825 'punctuation': /^<<<'?|[';]$/
1826 }
1827 }
1828 }
1829 },
1830 'heredoc-string': {
1831 pattern: /<<<(?:"([^"]+)"(?:\r\n?|\n)(?:.*(?:\r\n?|\n))*?\1;|([a-z_]\w*)(?:\r\n?|\n)(?:.*(?:\r\n?|\n))*?\2;)/i,
1832 greedy: true,
1833 alias: 'string',
1834 inside: {
1835 'delimiter': {
1836 pattern: /^<<<(?:"[^"]+"|[a-z_]\w*)|[a-z_]\w*;$/i,
1837 alias: 'symbol',
1838 inside: {
1839 'punctuation': /^<<<"?|[";]$/
1840 }
1841 },
1842 'interpolation': string_interpolation // See below
1843 }
1844 },
1845 'single-quoted-string': {
1846 pattern: /'(?:\\[\s\S]|[^\\'])*'/,
1847 greedy: true,
1848 alias: 'string'
1849 },
1850 'double-quoted-string': {
1851 pattern: /"(?:\\[\s\S]|[^\\"])*"/,
1852 greedy: true,
1853 alias: 'string',
1854 inside: {
1855 'interpolation': string_interpolation // See below
1856 }
1857 }
1858 });
1859 // The different types of PHP strings "replace" the C-like standard string
1860 delete Prism.languages.php['string'];
1861
1862 Prism.hooks.add('before-tokenize', function(env) {
1863 if (!/<\?/.test(env.code)) {
1864 return;
1865 }
1866
1867 var phpPattern = /<\?(?:[^"'/#]|\/(?![*/])|("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|(?:\/\/|#)(?:[^?\n\r]|\?(?!>))*|\/\*[\s\S]*?(?:\*\/|$))*?(?:\?>|$)/ig;
1868 Prism.languages['markup-templating'].buildPlaceholders(env, 'php', phpPattern);
1869 });
1870
1871 Prism.hooks.add('after-tokenize', function(env) {
1872 Prism.languages['markup-templating'].tokenizePlaceholders(env, 'php');
1873 });
1874
1875}(Prism));
1517 1876
1518Prism.languages.json = {1877Prism.languages.json = {
1519 'property': /"(?:\\.|[^\\"\r\n])*"(?=\s*:)/i,1878 'property': {
1520 'string': {1879 pattern: /"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,
1521 pattern: /"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,1880 greedy: true
1522 greedy: true1881 },
1523 },1882 'string': {
1524 'number': /-?\d+\.?\d*([Ee][+-]?\d+)?/,1883 pattern: /"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,
1525 'punctuation': /[{}[\],]/,1884 greedy: true
1526 'operator': /:/g,1885 },
1527 'boolean': /\b(?:true|false)\b/i,1886 'comment': /\/\/.*|\/\*[\s\S]*?(?:\*\/|$)/,
1528 'null': /\bnull\b/i1887 'number': /-?\d+\.?\d*(e[+-]?\d+)?/i,
1888 'punctuation': /[{}[\],]/,
1889 'operator': /:/,
1890 'boolean': /\b(?:true|false)\b/,
1891 'null': {
1892 pattern: /\bnull\b/,
1893 alias: 'keyword'
1894 }
1529};1895};
1530 1896
1531Prism.languages.jsonp = Prism.languages.json;
1532
1533(function (Prism) {1897(function (Prism) {
1534 Prism.languages.kotlin = Prism.languages.extend('clike', {1898 Prism.languages.kotlin = Prism.languages.extend('clike', {
1535 'keyword': {1899 'keyword': {
1536 // The lookbehind prevents wrong highlighting of e.g. kotlin.properties.get1900 // The lookbehind prevents wrong highlighting of e.g. kotlin.properties.get
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/,1901 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/,
1538 lookbehind: true1902 lookbehind: true
1539 },1903 },
1540 'function': [1904 'function': [
1541 /\w+(?=\s*\()/,1905 /\w+(?=\s*\()/,
1542 {1906 {
1543 pattern: /(\.)\w+(?=\s*\{)/,1907 pattern: /(\.)\w+(?=\s*\{)/,
1544 lookbehind: true1908 lookbehind: true
1545 }1909 }
1546 ],1910 ],
1547 'number': /\b(?:0[xX][\da-fA-F]+(?:_[\da-fA-F]+)*|0[bB][01]+(?:_[01]+)*|\d+(?:_\d+)*(?:\.\d+(?:_\d+)*)?(?:[eE][+-]?\d+(?:_\d+)*)?[fFL]?)\b/,1911 'number': /\b(?:0[xX][\da-fA-F]+(?:_[\da-fA-F]+)*|0[bB][01]+(?:_[01]+)*|\d+(?:_\d+)*(?:\.\d+(?:_\d+)*)?(?:[eE][+-]?\d+(?:_\d+)*)?[fFL]?)\b/,
1548 'operator': /\+[+=]?|-[-=>]?|==?=?|!(?:!|==?)?|[\/*%<>]=?|[?:]:?|\.\.|&&|\|\||\b(?:and|inv|or|shl|shr|ushr|xor)\b/1912 'operator': /\+[+=]?|-[-=>]?|==?=?|!(?:!|==?)?|[\/*%<>]=?|[?:]:?|\.\.|&&|\|\||\b(?:and|inv|or|shl|shr|ushr|xor)\b/
1549 });1913 });
1550 1914
1551 delete Prism.languages.kotlin["class-name"];1915 delete Prism.languages.kotlin["class-name"];
1552 1916
1553 Prism.languages.insertBefore('kotlin', 'string', {1917 Prism.languages.insertBefore('kotlin', 'string', {
1554 'raw-string': {1918 'raw-string': {
1555 pattern: /("""|''')[\s\S]*?\1/,1919 pattern: /("""|''')[\s\S]*?\1/,
1556 alias: 'string'1920 alias: 'string'
1557 // See interpolation below1921 // See interpolation below
1558 }1922 }
1559 });1923 });
1560 Prism.languages.insertBefore('kotlin', 'keyword', {1924 Prism.languages.insertBefore('kotlin', 'keyword', {
1561 'annotation': {1925 'annotation': {
1562 pattern: /\B@(?:\w+:)?(?:[A-Z]\w*|\[[^\]]+\])/,1926 pattern: /\B@(?:\w+:)?(?:[A-Z]\w*|\[[^\]]+\])/,
1563 alias: 'builtin'1927 alias: 'builtin'
1564 }1928 }
1565 });1929 });
1566 Prism.languages.insertBefore('kotlin', 'function', {1930 Prism.languages.insertBefore('kotlin', 'function', {
1567 'label': {1931 'label': {
1568 pattern: /\w+@|@\w+/,1932 pattern: /\w+@|@\w+/,
1569 alias: 'symbol'1933 alias: 'symbol'
1570 }1934 }
1571 });1935 });
1572 1936
1573 var interpolation = [1937 var interpolation = [
1574 {1938 {
1575 pattern: /\$\{[^}]+\}/,1939 pattern: /\$\{[^}]+\}/,
1576 inside: {1940 inside: {
1577 delimiter: {1941 delimiter: {
1578 pattern: /^\$\{|\}$/,1942 pattern: /^\$\{|\}$/,
1579 alias: 'variable'1943 alias: 'variable'
1580 },1944 },
1581 rest: Prism.languages.kotlin1945 rest: Prism.languages.kotlin
1582 }1946 }
1583 },1947 },
1584 {1948 {
1585 pattern: /\$\w+/,1949 pattern: /\$\w+/,
1586 alias: 'variable'1950 alias: 'variable'
1587 }1951 }
1588 ];1952 ];
1589 1953
1590 Prism.languages.kotlin['string'].inside = Prism.languages.kotlin['raw-string'].inside = {1954 Prism.languages.kotlin['string'].inside = Prism.languages.kotlin['raw-string'].inside = {
1591 interpolation: interpolation1955 interpolation: interpolation
1592 };1956 };
1593 1957
1594}(Prism));1958}(Prism));
1595 1959
1596Prism.languages.lua = {
1597 'comment': /^#!.+|--(?:\[(=*)\[[\s\S]*?\]\1\]|.*)/m,
1598 // \z may be used to skip the following space
1599 'string': {
1600 pattern: /(["'])(?:(?!\1)[^\\\r\n]|\\z(?:\r\n|\s)|\\(?:\r\n|[\s\S]))*\1|\[(=*)\[[\s\S]*?\]\2\]/,
1601 greedy: true
1602 },
1603 'number': /\b0x[a-f\d]+\.?[a-f\d]*(?:p[+-]?\d+)?\b|\b\d+(?:\.\B|\.?\d*(?:e[+-]?\d+)?\b)|\B\.\d+(?:e[+-]?\d+)?\b/i,
1604 'keyword': /\b(?:and|break|do|else|elseif|end|false|for|function|goto|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b/,
1605 'function': /(?!\d)\w+(?=\s*(?:[({]))/,
1606 'operator': [
1607 /[-+*%^&|#]|\/\/?|<[<=]?|>[>=]?|[=~]=?/,
1608 {
1609 // Match ".." but don't break "..."
1610 pattern: /(^|[^.])\.\.(?!\.)/,
1611 lookbehind: true
1612 }
1613 ],
1614 'punctuation': /[\[\](){},;]|\.+|:+/
1615};
1616(function(Prism) {1960(function(Prism) {
1617 Prism.languages.crystal = Prism.languages.extend('ruby', {1961 Prism.languages.crystal = Prism.languages.extend('ruby', {
1618 keyword: [1962 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/,1963 /\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 {1964 {
1621 pattern: /(\.\s*)(?:is_a|responds_to)\?/,1965 pattern: /(\.\s*)(?:is_a|responds_to)\?/,
1622 lookbehind: true1966 lookbehind: true
1623 }1967 }
1624 ],1968 ],
1625 1969
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/1970 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 });1971 });
1628 1972
1629 Prism.languages.insertBefore('crystal', 'string', {1973 Prism.languages.insertBefore('crystal', 'string', {
1630 attribute: {1974 attribute: {
1631 pattern: /@\[.+?\]/,1975 pattern: /@\[.+?\]/,
1632 alias: 'attr-name',1976 alias: 'attr-name',
1633 inside: {1977 inside: {
1634 delimiter: {1978 delimiter: {
1635 pattern: /^@\[|\]$/,1979 pattern: /^@\[|\]$/,
1636 alias: 'tag'1980 alias: 'tag'
1637 },1981 },
1638 rest: Prism.languages.crystal1982 rest: Prism.languages.crystal
1639 }1983 }
1640 },1984 },
1641 1985
1642 expansion: [1986 expansion: [
1643 {1987 {
1644 pattern: /\{\{.+?\}\}/,1988 pattern: /\{\{.+?\}\}/,
1645 inside: {1989 inside: {
1646 delimiter: {1990 delimiter: {
1647 pattern: /^\{\{|\}\}$/,1991 pattern: /^\{\{|\}\}$/,
1648 alias: 'tag'1992 alias: 'tag'
1649 },1993 },
1650 rest: Prism.languages.crystal1994 rest: Prism.languages.crystal
1651 }1995 }
1652 },1996 },
1653 {1997 {
1654 pattern: /\{%.+?%\}/,1998 pattern: /\{%.+?%\}/,
1655 inside: {1999 inside: {
1656 delimiter: {2000 delimiter: {
1657 pattern: /^\{%|%\}$/,2001 pattern: /^\{%|%\}$/,
1658 alias: 'tag'2002 alias: 'tag'
1659 },2003 },
1660 rest: Prism.languages.crystal2004 rest: Prism.languages.crystal
1661 }2005 }
1662 }2006 }
1663 ]2007 ]
1664 });2008 });
1665 2009
1666}(Prism));2010}(Prism));
1667 2011
2012Prism.languages.lua = {
2013 'comment': /^#!.+|--(?:\[(=*)\[[\s\S]*?\]\1\]|.*)/m,
2014 // \z may be used to skip the following space
2015 'string': {
2016 pattern: /(["'])(?:(?!\1)[^\\\r\n]|\\z(?:\r\n|\s)|\\(?:\r\n|[\s\S]))*\1|\[(=*)\[[\s\S]*?\]\2\]/,
2017 greedy: true
2018 },
2019 'number': /\b0x[a-f\d]+\.?[a-f\d]*(?:p[+-]?\d+)?\b|\b\d+(?:\.\B|\.?\d*(?:e[+-]?\d+)?\b)|\B\.\d+(?:e[+-]?\d+)?\b/i,
2020 'keyword': /\b(?:and|break|do|else|elseif|end|false|for|function|goto|if|in|local|nil|not|or|repeat|return|then|true|until|while)\b/,
2021 'function': /(?!\d)\w+(?=\s*(?:[({]))/,
2022 'operator': [
2023 /[-+*%^&|#]|\/\/?|<[<=]?|>[>=]?|[=~]=?/,
2024 {
2025 // Match ".." but don't break "..."
2026 pattern: /(^|[^.])\.\.(?!\.)/,
2027 lookbehind: true
2028 }
2029 ],
2030 'punctuation': /[\[\](){},;]|\.+|:+/
2031};
2032// Django/Jinja2 syntax definition for Prism.js <http://prismjs.com> syntax highlighter.
2033// Mostly it works OK but can paint code incorrectly on complex html/template tag combinations.
2034
2035(function (Prism) {
2036
2037 Prism.languages.django = {
2038 'comment': /^{#[\s\S]*?#}$/,
2039 'tag': {
2040 pattern: /(^{%[+-]?\s*)\w+/,
2041 lookbehind: true,
2042 alias: 'keyword'
2043 },
2044 'delimiter': {
2045 pattern: /^{[{%][+-]?|[+-]?[}%]}$/,
2046 alias: 'punctuation'
2047 },
2048 'string': {
2049 pattern: /("|')(?:\\.|(?!\1)[^\\\r\n])*\1/,
2050 greedy: true
2051 },
2052 'filter': {
2053 pattern: /(\|)\w+/,
2054 lookbehind: true,
2055 alias: 'function'
2056 },
2057 'test': {
2058 pattern: /(\bis\s+(?:not\s+)?)(?!not\b)\w+/,
2059 lookbehind: true,
2060 alias: 'function'
2061 },
2062 'function': /\b[a-z_]\w+(?=\s*\()/i,
2063 'keyword': /\b(?:and|as|by|else|for|if|import|in|is|loop|not|or|recursive|with|without)\b/,
2064 'operator': /[-+*/%=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,
2065 'number': /\b\d+(?:\.\d+)?\b/,
2066 'boolean': /[Tt]rue|[Ff]alse|[Nn]one/,
2067 'variable': /\b\w+?\b/,
2068 'punctuation': /[{}[\](),.:;]/
2069 };
2070
2071
2072 var pattern = /{{[\s\S]*?}}|{%[\s\S]*?%}|{#[\s\S]*?#}/g;
2073 var markupTemplating = Prism.languages['markup-templating'];
2074
2075 Prism.hooks.add('before-tokenize', function (env) {
2076 markupTemplating.buildPlaceholders(env, 'django', pattern);
2077 });
2078 Prism.hooks.add('after-tokenize', function (env) {
2079 markupTemplating.tokenizePlaceholders(env, 'django');
2080 });
2081
2082 // Add an Jinja2 alias
2083 Prism.languages.jinja2 = Prism.languages.django;
2084 Prism.hooks.add('before-tokenize', function (env) {
2085 markupTemplating.buildPlaceholders(env, 'jinja2', pattern);
2086 });
2087 Prism.hooks.add('after-tokenize', function (env) {
2088 markupTemplating.tokenizePlaceholders(env, 'jinja2');
2089 });
2090
2091})(Prism);
2092
2093Prism.languages.matlab = {
2094 'comment': [
2095 /%\{[\s\S]*?\}%/,
2096 /%.+/
2097 ],
2098 'string': {
2099 pattern: /\B'(?:''|[^'\r\n])*'/,
2100 greedy: true
2101 },
2102 // FIXME We could handle imaginary numbers as a whole
2103 'number': /(?:\b\d+\.?\d*|\B\.\d+)(?:[eE][+-]?\d+)?(?:[ij])?|\b[ij]\b/,
2104 'keyword': /\b(?:break|case|catch|continue|else|elseif|end|for|function|if|inf|NaN|otherwise|parfor|pause|pi|return|switch|try|while)\b/,
2105 'function': /(?!\d)\w+(?=\s*\()/,
2106 'operator': /\.?[*^\/\\']|[+\-:@]|[<>=~]=?|&&?|\|\|?/,
2107 'punctuation': /\.{3}|[.,;\[\](){}!]/
2108};
2109Prism.languages.typescript = Prism.languages.extend('javascript', {
2110 // From JavaScript Prism keyword list and TypeScript language spec: https://github.com/Microsoft/TypeScript/blob/master/doc/spec.md#221-reserved-words
2111 'keyword': /\b(?:abstract|as|async|await|break|case|catch|class|const|constructor|continue|debugger|declare|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|is|keyof|let|module|namespace|new|null|of|package|private|protected|public|readonly|return|require|set|static|super|switch|this|throw|try|type|typeof|var|void|while|with|yield)\b/,
2112 'builtin': /\b(?:string|Function|any|number|boolean|Array|symbol|console|Promise|unknown|never)\b/,
2113});
2114
2115Prism.languages.ts = Prism.languages.typescript;
2116
1668Prism.languages.nginx = Prism.languages.extend('clike', {2117Prism.languages.nginx = Prism.languages.extend('clike', {
1669 'comment': {2118 'comment': {
1670 pattern: /(^|[^"{\\])#.*/,2119 pattern: /(^|[^"{\\])#.*/,
1671 lookbehind: true2120 lookbehind: true
1672 },2121 },
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/i2122 '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|ssl_session_tickets|ssl_stapling|ssl_stapling_verify|ssl_ecdh_curve|ssl_trusted_certificate|more_set_headers|ssl_early_data)\b/i
1674});2123});
1675 2124
1676Prism.languages.insertBefore('nginx', 'keyword', {2125Prism.languages.insertBefore('nginx', 'keyword', {
1677 'variable': /\$[a-z_]+/i2126 'variable': /\$[a-z_]+/i
1678});2127});
2128
1679Prism.languages.nim = {2129Prism.languages.nim = {
1680 'comment': /#.*/,2130 'comment': /#.*/,
1681 // Double-quoted strings can be prefixed by an identifier (Generalized raw string literals)2131 // Double-quoted strings can be prefixed by an identifier (Generalized raw string literals)
1682 // Character literals are handled specifically to prevent issues with numeric type suffixes2132 // Character literals are handled specifically to prevent issues with numeric type suffixes
1683 'string': {2133 'string': {
1684 pattern: /(?:(?:\b(?!\d)(?:\w|\\x[8-9a-fA-F][0-9a-fA-F])+)?(?:"""[\s\S]*?"""(?!")|"(?:\\[\s\S]|""|[^"\\])*")|'(?:\\(?:\d+|x[\da-fA-F]{2}|.)|[^'])')/,2134 pattern: /(?:(?:\b(?!\d)(?:\w|\\x[8-9a-fA-F][0-9a-fA-F])+)?(?:"""[\s\S]*?"""(?!")|"(?:\\[\s\S]|""|[^"\\])*")|'(?:\\(?:\d+|x[\da-fA-F]{2}|.)|[^'])')/,
1685 greedy: true2135 greedy: true
1686 },2136 },
1687 // The negative look ahead prevents wrong highlighting of the .. operator2137 // The negative look ahead prevents wrong highlighting of the .. operator
1688 'number': /\b(?:0[xXoObB][\da-fA-F_]+|\d[\d_]*(?:(?!\.\.)\.[\d_]*)?(?:[eE][+-]?\d[\d_]*)?)(?:'?[iuf]\d*)?/,2138 'number': /\b(?:0[xXoObB][\da-fA-F_]+|\d[\d_]*(?:(?!\.\.)\.[\d_]*)?(?:[eE][+-]?\d[\d_]*)?)(?:'?[iuf]\d*)?/,
1689 'keyword': /\b(?:addr|as|asm|atomic|bind|block|break|case|cast|concept|const|continue|converter|defer|discard|distinct|do|elif|else|end|enum|except|export|finally|for|from|func|generic|if|import|include|interface|iterator|let|macro|method|mixin|nil|object|out|proc|ptr|raise|ref|return|static|template|try|tuple|type|using|var|when|while|with|without|yield)\b/,2139 'keyword': /\b(?:addr|as|asm|atomic|bind|block|break|case|cast|concept|const|continue|converter|defer|discard|distinct|do|elif|else|end|enum|except|export|finally|for|from|func|generic|if|import|include|interface|iterator|let|macro|method|mixin|nil|object|out|proc|ptr|raise|ref|return|static|template|try|tuple|type|using|var|when|while|with|without|yield)\b/,
1690 'function': {2140 'function': {
1691 pattern: /(?:(?!\d)(?:\w|\\x[8-9a-fA-F][0-9a-fA-F])+|`[^`\r\n]+`)\*?(?:\[[^\]]+\])?(?=\s*\()/,2141 pattern: /(?:(?!\d)(?:\w|\\x[8-9a-fA-F][0-9a-fA-F])+|`[^`\r\n]+`)\*?(?:\[[^\]]+\])?(?=\s*\()/,
1692 inside: {2142 inside: {
1693 'operator': /\*$/2143 'operator': /\*$/
1694 }2144 }
1695 },2145 },
1696 // We don't want to highlight operators inside backticks2146 // We don't want to highlight operators inside backticks
1697 'ignore': {2147 'ignore': {
1698 pattern: /`[^`\r\n]+`/,2148 pattern: /`[^`\r\n]+`/,
1699 inside: {2149 inside: {
1700 'punctuation': /`/2150 'punctuation': /`/
1701 }2151 }
1702 },2152 },
1703 'operator': {2153 'operator': {
1704 // Look behind and look ahead prevent wrong highlighting of punctuations [. .] {. .} (. .)2154 // Look behind and look ahead prevent wrong highlighting of punctuations [. .] {. .} (. .)
1705 // but allow the slice operator .. to take precedence over them2155 // but allow the slice operator .. to take precedence over them
1706 // One can define his own operators in Nim so all combination of operators might be an operator.2156 // One can define his own operators in Nim so all combination of operators might be an operator.
1707 pattern: /(^|[({\[](?=\.\.)|(?![({\[]\.).)(?:(?:[=+\-*\/<>@$~&%|!?^:\\]|\.\.|\.(?![)}\]]))+|\b(?:and|div|of|or|in|is|isnot|mod|not|notin|shl|shr|xor)\b)/m,2157 pattern: /(^|[({\[](?=\.\.)|(?![({\[]\.).)(?:(?:[=+\-*\/<>@$~&%|!?^:\\]|\.\.|\.(?![)}\]]))+|\b(?:and|div|of|or|in|is|isnot|mod|not|notin|shl|shr|xor)\b)/m,
1708 lookbehind: true2158 lookbehind: true
1709 },2159 },
1710 'punctuation': /[({\[]\.|\.[)}\]]|[`(){}\[\],:]/2160 'punctuation': /[({\[]\.|\.[)}\]]|[`(){}\[\],:]/
1711};2161};
1712Prism.languages.perl = {2162Prism.languages.perl = {
1713 'comment': [2163 'comment': [
1714 {2164 {
1715 // POD2165 // POD
1716 pattern: /(^\s*)=\w+[\s\S]*?=cut.*/m,2166 pattern: /(^\s*)=\w+[\s\S]*?=cut.*/m,
1717 lookbehind: true2167 lookbehind: true
1718 },2168 },
1719 {2169 {
1720 pattern: /(^|[^\\$])#.*/,2170 pattern: /(^|[^\\$])#.*/,
1721 lookbehind: true2171 lookbehind: true
1722 }2172 }
1723 ],2173 ],
1724 // TODO Could be nice to handle Heredoc too.2174 // TODO Could be nice to handle Heredoc too.
1725 'string': [2175 'string': [
1726 // q/.../2176 // q/.../
1727 {2177 {
1728 pattern: /\b(?:q|qq|qx|qw)\s*([^a-zA-Z0-9\s{(\[<])(?:(?!\1)[^\\]|\\[\s\S])*\1/,2178 pattern: /\b(?:q|qq|qx|qw)\s*([^a-zA-Z0-9\s{(\[<])(?:(?!\1)[^\\]|\\[\s\S])*\1/,
1729 greedy: true2179 greedy: true
1730 },2180 },
1731 2181
1732 // q a...a2182 // q a...a
1733 {2183 {
1734 pattern: /\b(?:q|qq|qx|qw)\s+([a-zA-Z0-9])(?:(?!\1)[^\\]|\\[\s\S])*\1/,2184 pattern: /\b(?:q|qq|qx|qw)\s+([a-zA-Z0-9])(?:(?!\1)[^\\]|\\[\s\S])*\1/,
1735 greedy: true2185 greedy: true
1736 },2186 },
1737 2187
1738 // q(...)2188 // q(...)
1739 {2189 {
1740 pattern: /\b(?:q|qq|qx|qw)\s*\((?:[^()\\]|\\[\s\S])*\)/,2190 pattern: /\b(?:q|qq|qx|qw)\s*\((?:[^()\\]|\\[\s\S])*\)/,
1741 greedy: true2191 greedy: true
1742 },2192 },
1743 2193
1744 // q{...}2194 // q{...}
1745 {2195 {
1746 pattern: /\b(?:q|qq|qx|qw)\s*\{(?:[^{}\\]|\\[\s\S])*\}/,2196 pattern: /\b(?:q|qq|qx|qw)\s*\{(?:[^{}\\]|\\[\s\S])*\}/,
1747 greedy: true2197 greedy: true
1748 },2198 },
1749 2199
1750 // q[...]2200 // q[...]
1751 {2201 {
1752 pattern: /\b(?:q|qq|qx|qw)\s*\[(?:[^[\]\\]|\\[\s\S])*\]/,2202 pattern: /\b(?:q|qq|qx|qw)\s*\[(?:[^[\]\\]|\\[\s\S])*\]/,
1753 greedy: true2203 greedy: true
1754 },2204 },
1755 2205
1756 // q<...>2206 // q<...>
1757 {2207 {
1758 pattern: /\b(?:q|qq|qx|qw)\s*<(?:[^<>\\]|\\[\s\S])*>/,2208 pattern: /\b(?:q|qq|qx|qw)\s*<(?:[^<>\\]|\\[\s\S])*>/,
1759 greedy: true2209 greedy: true
1760 },2210 },
1761 2211
1762 // "...", `...`2212 // "...", `...`
1763 {2213 {
1764 pattern: /("|`)(?:(?!\1)[^\\]|\\[\s\S])*\1/,2214 pattern: /("|`)(?:(?!\1)[^\\]|\\[\s\S])*\1/,
1765 greedy: true2215 greedy: true
1766 },2216 },
1767 2217
1768 // '...'2218 // '...'
1769 // FIXME Multi-line single-quoted strings are not supported as they would break variables containing '2219 // FIXME Multi-line single-quoted strings are not supported as they would break variables containing '
1770 {2220 {
1771 pattern: /'(?:[^'\\\r\n]|\\.)*'/,2221 pattern: /'(?:[^'\\\r\n]|\\.)*'/,
1772 greedy: true2222 greedy: true
1773 }2223 }
1774 ],2224 ],
1775 'regex': [2225 'regex': [
1776 // m/.../2226 // m/.../
1777 {2227 {
1778 pattern: /\b(?:m|qr)\s*([^a-zA-Z0-9\s{(\[<])(?:(?!\1)[^\\]|\\[\s\S])*\1[msixpodualngc]*/,2228 pattern: /\b(?:m|qr)\s*([^a-zA-Z0-9\s{(\[<])(?:(?!\1)[^\\]|\\[\s\S])*\1[msixpodualngc]*/,
1779 greedy: true2229 greedy: true
1780 },2230 },
1781 2231
1782 // m a...a2232 // m a...a
1783 {2233 {
1784 pattern: /\b(?:m|qr)\s+([a-zA-Z0-9])(?:(?!\1)[^\\]|\\[\s\S])*\1[msixpodualngc]*/,2234 pattern: /\b(?:m|qr)\s+([a-zA-Z0-9])(?:(?!\1)[^\\]|\\[\s\S])*\1[msixpodualngc]*/,
1785 greedy: true2235 greedy: true
1786 },2236 },
1787 2237
1788 // m(...)2238 // m(...)
1789 {2239 {
1790 pattern: /\b(?:m|qr)\s*\((?:[^()\\]|\\[\s\S])*\)[msixpodualngc]*/,2240 pattern: /\b(?:m|qr)\s*\((?:[^()\\]|\\[\s\S])*\)[msixpodualngc]*/,
1791 greedy: true2241 greedy: true
1792 },2242 },
1793 2243
1794 // m{...}2244 // m{...}
1795 {2245 {
1796 pattern: /\b(?:m|qr)\s*\{(?:[^{}\\]|\\[\s\S])*\}[msixpodualngc]*/,2246 pattern: /\b(?:m|qr)\s*\{(?:[^{}\\]|\\[\s\S])*\}[msixpodualngc]*/,
1797 greedy: true2247 greedy: true
1798 },2248 },
1799 2249
1800 // m[...]2250 // m[...]
1801 {2251 {
1802 pattern: /\b(?:m|qr)\s*\[(?:[^[\]\\]|\\[\s\S])*\][msixpodualngc]*/,2252 pattern: /\b(?:m|qr)\s*\[(?:[^[\]\\]|\\[\s\S])*\][msixpodualngc]*/,
1803 greedy: true2253 greedy: true
1804 },2254 },
1805 2255
1806 // m<...>2256 // m<...>
1807 {2257 {
1808 pattern: /\b(?:m|qr)\s*<(?:[^<>\\]|\\[\s\S])*>[msixpodualngc]*/,2258 pattern: /\b(?:m|qr)\s*<(?:[^<>\\]|\\[\s\S])*>[msixpodualngc]*/,
1809 greedy: true2259 greedy: true
1810 },2260 },
1811 2261
1812 // The lookbehinds prevent -s from breaking2262 // The lookbehinds prevent -s from breaking
1813 // FIXME We don't handle change of separator like s(...)[...]2263 // FIXME We don't handle change of separator like s(...)[...]
1814 // s/.../.../2264 // s/.../.../
1815 {2265 {
1816 pattern: /(^|[^-]\b)(?:s|tr|y)\s*([^a-zA-Z0-9\s{(\[<])(?:(?!\2)[^\\]|\\[\s\S])*\2(?:(?!\2)[^\\]|\\[\s\S])*\2[msixpodualngcer]*/,2266 pattern: /(^|[^-]\b)(?:s|tr|y)\s*([^a-zA-Z0-9\s{(\[<])(?:(?!\2)[^\\]|\\[\s\S])*\2(?:(?!\2)[^\\]|\\[\s\S])*\2[msixpodualngcer]*/,
1817 lookbehind: true,2267 lookbehind: true,
1818 greedy: true2268 greedy: true
1819 },2269 },
1820 2270
1821 // s a...a...a2271 // s a...a...a
1822 {2272 {
1823 pattern: /(^|[^-]\b)(?:s|tr|y)\s+([a-zA-Z0-9])(?:(?!\2)[^\\]|\\[\s\S])*\2(?:(?!\2)[^\\]|\\[\s\S])*\2[msixpodualngcer]*/,2273 pattern: /(^|[^-]\b)(?:s|tr|y)\s+([a-zA-Z0-9])(?:(?!\2)[^\\]|\\[\s\S])*\2(?:(?!\2)[^\\]|\\[\s\S])*\2[msixpodualngcer]*/,
1824 lookbehind: true,2274 lookbehind: true,
1825 greedy: true2275 greedy: true
1826 },2276 },
1827 2277
1828 // s(...)(...)2278 // s(...)(...)
1829 {2279 {
1830 pattern: /(^|[^-]\b)(?:s|tr|y)\s*\((?:[^()\\]|\\[\s\S])*\)\s*\((?:[^()\\]|\\[\s\S])*\)[msixpodualngcer]*/,2280 pattern: /(^|[^-]\b)(?:s|tr|y)\s*\((?:[^()\\]|\\[\s\S])*\)\s*\((?:[^()\\]|\\[\s\S])*\)[msixpodualngcer]*/,
1831 lookbehind: true,2281 lookbehind: true,
1832 greedy: true2282 greedy: true
1833 },2283 },
1834 2284
1835 // s{...}{...}2285 // s{...}{...}
1836 {2286 {
1837 pattern: /(^|[^-]\b)(?:s|tr|y)\s*\{(?:[^{}\\]|\\[\s\S])*\}\s*\{(?:[^{}\\]|\\[\s\S])*\}[msixpodualngcer]*/,2287 pattern: /(^|[^-]\b)(?:s|tr|y)\s*\{(?:[^{}\\]|\\[\s\S])*\}\s*\{(?:[^{}\\]|\\[\s\S])*\}[msixpodualngcer]*/,
1838 lookbehind: true,2288 lookbehind: true,
1839 greedy: true2289 greedy: true
1840 },2290 },
1841 2291
1842 // s[...][...]2292 // s[...][...]
1843 {2293 {
1844 pattern: /(^|[^-]\b)(?:s|tr|y)\s*\[(?:[^[\]\\]|\\[\s\S])*\]\s*\[(?:[^[\]\\]|\\[\s\S])*\][msixpodualngcer]*/,2294 pattern: /(^|[^-]\b)(?:s|tr|y)\s*\[(?:[^[\]\\]|\\[\s\S])*\]\s*\[(?:[^[\]\\]|\\[\s\S])*\][msixpodualngcer]*/,
1845 lookbehind: true,2295 lookbehind: true,
1846 greedy: true2296 greedy: true
1847 },2297 },
1848 2298
1849 // s<...><...>2299 // s<...><...>
1850 {2300 {
1851 pattern: /(^|[^-]\b)(?:s|tr|y)\s*<(?:[^<>\\]|\\[\s\S])*>\s*<(?:[^<>\\]|\\[\s\S])*>[msixpodualngcer]*/,2301 pattern: /(^|[^-]\b)(?:s|tr|y)\s*<(?:[^<>\\]|\\[\s\S])*>\s*<(?:[^<>\\]|\\[\s\S])*>[msixpodualngcer]*/,
1852 lookbehind: true,2302 lookbehind: true,
1853 greedy: true2303 greedy: true
1854 },2304 },
1855 2305
1856 // /.../2306 // /.../
1857 // The look-ahead tries to prevent two divisions on2307 // The look-ahead tries to prevent two divisions on
1858 // the same line from being highlighted as regex.2308 // the same line from being highlighted as regex.
1859 // This does not support multi-line regex.2309 // This does not support multi-line regex.
1860 {2310 {
1861 pattern: /\/(?:[^\/\\\r\n]|\\.)*\/[msixpodualngc]*(?=\s*(?:$|[\r\n,.;})&|\-+*~<>!?^]|(lt|gt|le|ge|eq|ne|cmp|not|and|or|xor|x)\b))/,2311 pattern: /\/(?:[^\/\\\r\n]|\\.)*\/[msixpodualngc]*(?=\s*(?:$|[\r\n,.;})&|\-+*~<>!?^]|(lt|gt|le|ge|eq|ne|cmp|not|and|or|xor|x)\b))/,
1862 greedy: true2312 greedy: true
1863 }2313 }
1864 ],2314 ],
1865 2315
1866 // FIXME Not sure about the handling of ::, ', and #2316 // FIXME Not sure about the handling of ::, ', and #
1867 'variable': [2317 'variable': [
1868 // ${^POSTMATCH}2318 // ${^POSTMATCH}
1869 /[&*$@%]\{\^[A-Z]+\}/,2319 /[&*$@%]\{\^[A-Z]+\}/,
1870 // $^V2320 // $^V
1871 /[&*$@%]\^[A-Z_]/,2321 /[&*$@%]\^[A-Z_]/,
1872 // ${...}2322 // ${...}
1873 /[&*$@%]#?(?=\{)/,2323 /[&*$@%]#?(?=\{)/,
1874 // $foo2324 // $foo
1875 /[&*$@%]#?(?:(?:::)*'?(?!\d)[\w$]+)+(?:::)*/i,2325 /[&*$@%]#?(?:(?:::)*'?(?!\d)[\w$]+)+(?:::)*/i,
1876 // $12326 // $1
1877 /[&*$@%]\d+/,2327 /[&*$@%]\d+/,
1878 // $_, @_, %!2328 // $_, @_, %!
1879 // The negative lookahead prevents from breaking the %= operator2329 // The negative lookahead prevents from breaking the %= operator
1880 /(?!%=)[$@%][!"#$%&'()*+,\-.\/:;<=>?@[\\\]^_`{|}~]/2330 /(?!%=)[$@%][!"#$%&'()*+,\-.\/:;<=>?@[\\\]^_`{|}~]/
1881 ],2331 ],
1882 'filehandle': {2332 'filehandle': {
1883 // <>, <FOO>, _2333 // <>, <FOO>, _
1884 pattern: /<(?![<=])\S*>|\b_\b/,2334 pattern: /<(?![<=])\S*>|\b_\b/,
1885 alias: 'symbol'2335 alias: 'symbol'
1886 },2336 },
1887 'vstring': {2337 'vstring': {
1888 // v1.2, 1.2.32338 // v1.2, 1.2.3
1889 pattern: /v\d+(?:\.\d+)*|\d+(?:\.\d+){2,}/,2339 pattern: /v\d+(?:\.\d+)*|\d+(?:\.\d+){2,}/,
1890 alias: 'string'2340 alias: 'string'
1891 },2341 },
1892 'function': {2342 'function': {
1893 pattern: /sub [a-z0-9_]+/i,2343 pattern: /sub [a-z0-9_]+/i,
1894 inside: {2344 inside: {
1895 keyword: /sub/2345 keyword: /sub/
1896 }2346 }
1897 },2347 },
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/,2348 '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|return|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/,2349 '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/,2350 'operator': /-[rwxoRWXOezsfdlpSbctugkTBMAC]\b|\+[+=]?|-[-=>]?|\*\*?=?|\/\/?=?|=[=~>]?|~[~=]?|\|\|?=?|&&?=?|<(?:=>?|<=?)?|>>?=?|![~=]?|[%^]=?|\.(?:=|\.\.?)?|[\\?]|\bx(?:=|\b)|\b(?:lt|gt|le|ge|eq|ne|cmp|not|and|or|xor)\b/,
1901 'punctuation': /[{}[\];(),:]/2351 'punctuation': /[{}[\];(),:]/
1902};2352};
1903 2353
1904/**2354Prism.languages.sql = {
1905 * Original by Aaron Harun: http://aahacreative.com/2012/07/31/php-syntax-highlighting-prism/2355 'comment': {
1906 * Modified by Miles Johnson: http://milesj.me2356 pattern: /(^|[^\\])(?:\/\*[\s\S]*?\*\/|(?:--|\/\/|#).*)/,
1907 *2357 lookbehind: true
1908 * Supports the following:2358 },
1909 * - Extends clike syntax2359 'variable': [
1910 * - Support for PHP 5.3+ (namespaces, traits, generators, etc)2360 {
1911 * - Smarter constant and function matching2361 pattern: /@(["'`])(?:\\[\s\S]|(?!\1)[^\\])+\1/,
1912 *2362 greedy: true
1913 * Adds the following new token classes:2363 },
1914 * constant, delimiter, variable, function, package2364 /@[\w.$]+/
1915 */2365 ],
1916(function (Prism) {2366 'string': {
1917 Prism.languages.php = Prism.languages.extend('clike', {2367 pattern: /(^|[^@\\])("|')(?:\\[\s\S]|(?!\2)[^\\]|\2\2)*\2/,
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,2368 greedy: true,
1919 'constant': /\b[A-Z0-9_]{2,}\b/,2369 lookbehind: true
1920 'comment': {2370 },
1921 pattern: /(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,2371 '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?
1922 lookbehind: true2372 '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,
1923 }2373 'boolean': /\b(?:TRUE|FALSE|NULL)\b/i,
1924 });2374 'number': /\b0x[\da-f]+\b|\b\d+\.?\d*|\B\.\d+\b/i,
1925 2375 'operator': /[-+*\/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?|\b(?:AND|BETWEEN|IN|LIKE|NOT|OR|IS|DIV|REGEXP|RLIKE|SOUNDS LIKE|XOR)\b/i,
1926 Prism.languages.insertBefore('php', 'string', {2376 'punctuation': /[;[\]()`,.]/
1927 'shell-comment': {
1928 pattern: /(^|[^\\])#.*/,
1929 lookbehind: true,
1930 alias: 'comment'
1931 }
1932 });
1933
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 }
1946 }
1947 });
1948
1949 // Must be defined after the function pattern
1950 Prism.languages.insertBefore('php', 'operator', {
1951 'property': {
1952 pattern: /(->)[\w]+/,
1953 lookbehind: true
1954 }
1955 });
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));
2028Prism.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};2377};
2378
2046Prism.languages.scss = Prism.languages.extend('css', {2379Prism.languages.scss = Prism.languages.extend('css', {
2047 'comment': {2380 'comment': {
2048 pattern: /(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,2381 pattern: /(^|[^\\])(?:\/\*[\s\S]*?\*\/|\/\/.*)/,
2049 lookbehind: true2382 lookbehind: true
2050 },2383 },
2051 'atrule': {2384 'atrule': {
2052 pattern: /@[\w-]+(?:\([^()]+\)|[^(])*?(?=\s+[{;])/,2385 pattern: /@[\w-]+(?:\([^()]+\)|[^(])*?(?=\s+[{;])/,
2053 inside: {2386 inside: {
2054 'rule': /@[\w-]+/2387 'rule': /@[\w-]+/
2055 // See rest below2388 // See rest below
2056 }2389 }
2057 },2390 },
2058 // url, compassified2391 // url, compassified
2059 'url': /(?:[-a-z]+-)*url(?=\()/i,2392 'url': /(?:[-a-z]+-)?url(?=\()/i,
2060 // CSS selector regex is not appropriate for Sass2393 // CSS selector regex is not appropriate for Sass
2061 // since there can be lot more things (var, @ directive, nesting..)2394 // 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)2395 // 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 variable2396 // 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 var2397 // 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})2398 // 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 :)2399 // this one was hard to do, so please be careful if you edit this one :)
2067 'selector': {2400 'selector': {
2068 // Initial look-ahead is used to prevent matching of blank selectors2401 // Initial look-ahead is used to prevent matching of blank selectors
2069 pattern: /(?=\S)[^@;{}()]?(?:[^@;{}()]|&|#\{\$[-\w]+\})+(?=\s*\{(?:\}|\s|[^}]+[:{][^}]+))/m,2402 pattern: /(?=\S)[^@;{}()]?(?:[^@;{}()]|#\{\$[-\w]+\})+(?=\s*\{(?:\}|\s|[^}]+[:{][^}]+))/m,
2070 inside: {2403 inside: {
2071 'parent': {2404 'parent': {
2072 pattern: /&/,2405 pattern: /&/,
2073 alias: 'important'2406 alias: 'important'
2074 },2407 },
2075 'placeholder': /%[-\w]+/,2408 'placeholder': /%[-\w]+/,
2076 'variable': /\$[-\w]+|#\{\$[-\w]+\}/2409 'variable': /\$[-\w]+|#\{\$[-\w]+\}/
2077 }2410 }
2078 }2411 },
2412 'property': {
2413 pattern: /(?:[\w-]|\$[-\w]+|#\{\$[-\w]+\})+(?=\s*:)/,
2414 inside: {
2415 'variable': /\$[-\w]+|#\{\$[-\w]+\}/
2416 }
2417 }
2079});2418});
2080 2419
2081Prism.languages.insertBefore('scss', 'atrule', {2420Prism.languages.insertBefore('scss', 'atrule', {
2082 'keyword': [2421 'keyword': [
2083 /@(?:if|else(?: if)?|for|each|while|import|extend|debug|warn|mixin|include|function|return|content)/i,2422 /@(?:if|else(?: if)?|for|each|while|import|extend|debug|warn|mixin|include|function|return|content)/i,
2084 {2423 {
2085 pattern: /( +)(?:from|through)(?= )/,2424 pattern: /( +)(?:from|through)(?= )/,
2086 lookbehind: true2425 lookbehind: true
2087 }2426 }
2088 ]2427 ]
2089});2428});
2090 2429
2091Prism.languages.scss.property = {
2092 pattern: /(?:[\w-]|\$[-\w]+|#\{\$[-\w]+\})+(?=\s*:)/i,
2093 inside: {
2094 'variable': /\$[-\w]+|#\{\$[-\w]+\}/
2095 }
2096};
2097
2098Prism.languages.insertBefore('scss', 'important', {2430Prism.languages.insertBefore('scss', 'important', {
2099 // var and interpolated vars2431 // var and interpolated vars
2100 'variable': /\$[-\w]+|#\{\$[-\w]+\}/2432 'variable': /\$[-\w]+|#\{\$[-\w]+\}/
2101});2433});
2102 2434
2103Prism.languages.insertBefore('scss', 'function', {2435Prism.languages.insertBefore('scss', 'function', {
2104 'placeholder': {2436 'placeholder': {
2105 pattern: /%[-\w]+/,2437 pattern: /%[-\w]+/,
2106 alias: 'selector'2438 alias: 'selector'
2107 },2439 },
2108 'statement': {2440 'statement': {
2109 pattern: /\B!(?:default|optional)\b/i,2441 pattern: /\B!(?:default|optional)\b/i,
2110 alias: 'keyword'2442 alias: 'keyword'
2111 },2443 },
2112 'boolean': /\b(?:true|false)\b/,2444 'boolean': /\b(?:true|false)\b/,
2113 'null': /\bnull\b/,2445 'null': {
2114 'operator': {2446 pattern: /\bnull\b/,
2115 pattern: /(\s)(?:[-+*\/%]|[=!]=|<=?|>=?|and|or|not)(?=\s)/,2447 alias: 'keyword'
2116 lookbehind: true2448 },
2117 }2449 'operator': {
2450 pattern: /(\s)(?:[-+*\/%]|[=!]=|<=?|>=?|and|or|not)(?=\s)/,
2451 lookbehind: true
2452 }
2118});2453});
2119 2454
2120Prism.languages.scss['atrule'].inside.rest = Prism.languages.scss;2455Prism.languages.scss['atrule'].inside.rest = Prism.languages.scss;
2456
2121Prism.languages.python = {2457Prism.languages.python = {
2122 'comment': {2458 'comment': {
2123 pattern: /(^|[^\\])#.*/,2459 pattern: /(^|[^\\])#.*/,
2124 lookbehind: true2460 lookbehind: true
2125 },2461 },
2126 'triple-quoted-string': {2462 'string-interpolation': {
2127 pattern: /("""|''')[\s\S]+?\1/,2463 pattern: /(?:f|rf|fr)(?:("""|''')[\s\S]+?\1|("|')(?:\\.|(?!\2)[^\\\r\n])*\2)/i,
2128 greedy: true,2464 greedy: true,
2129 alias: 'string'2465 inside: {
2130 },2466 'interpolation': {
2131 'string': {2467 // "{" <expression> <optional "!s", "!r", or "!a"> <optional ":" format specifier> "}"
2132 pattern: /("|')(?:\\.|(?!\1)[^\\\r\n])*\1/,2468 pattern: /((?:^|[^{])(?:{{)*){(?!{)(?:[^{}]|{(?!{)(?:[^{}]|{(?!{)(?:[^{}])+})+})+}/,
2133 greedy: true2469 lookbehind: true,
2134 },2470 inside: {
2135 'function': {2471 'format-spec': {
2136 pattern: /((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,2472 pattern: /(:)[^:(){}]+(?=}$)/,
2137 lookbehind: true2473 lookbehind: true
2138 },2474 },
2139 'class-name': {2475 'conversion-option': {
2140 pattern: /(\bclass\s+)\w+/i,2476 pattern: /![sra](?=[:}]$)/,
2141 lookbehind: true2477 alias: 'punctuation'
2142 },2478 },
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/,2479 rest: null
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/,2480 }
2145 'boolean': /\b(?:True|False|None)\b/,2481 },
2146 'number': /(?:\b(?=\d)|\B(?=\.))(?:0[bo])?(?:(?:\d|0x[\da-f])[\da-f]*\.?\d*|\.\d+)(?:e[+-]?\d+)?j?\b/i,2482 'string': /[\s\S]+/
2147 'operator': /[-+%=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]|\b(?:or|and|not)\b/,2483 }
2148 'punctuation': /[{}[\];(),.:]/2484 },
2485 'triple-quoted-string': {
2486 pattern: /(?:[rub]|rb|br)?("""|''')[\s\S]+?\1/i,
2487 greedy: true,
2488 alias: 'string'
2489 },
2490 'string': {
2491 pattern: /(?:[rub]|rb|br)?("|')(?:\\.|(?!\1)[^\\\r\n])*\1/i,
2492 greedy: true
2493 },
2494 'function': {
2495 pattern: /((?:^|\s)def[ \t]+)[a-zA-Z_]\w*(?=\s*\()/g,
2496 lookbehind: true
2497 },
2498 'class-name': {
2499 pattern: /(\bclass\s+)\w+/i,
2500 lookbehind: true
2501 },
2502 'decorator': {
2503 pattern: /(^\s*)@\w+(?:\.\w+)*/i,
2504 lookbehind: true,
2505 alias: ['annotation', 'punctuation'],
2506 inside: {
2507 'punctuation': /\./
2508 }
2509 },
2510 'keyword': /\b(?:and|as|assert|async|await|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|nonlocal|not|or|pass|print|raise|return|try|while|with|yield)\b/,
2511 '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/,
2512 'boolean': /\b(?:True|False|None)\b/,
2513 'number': /(?:\b(?=\d)|\B(?=\.))(?:0[bo])?(?:(?:\d|0x[\da-f])[\da-f]*\.?\d*|\.\d+)(?:e[+-]?\d+)?j?\b/i,
2514 'operator': /[-+%=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]/,
2515 'punctuation': /[{}[\];(),.:]/
2149};2516};
2150 2517
2518Prism.languages.python['string-interpolation'].inside['interpolation'].inside.rest = Prism.languages.python;
2519
2520Prism.languages.py = Prism.languages.python;
2151(function(Prism) {2521(function(Prism) {
2152 2522
2153var javascript = Prism.util.clone(Prism.languages.javascript);2523 var javascript = Prism.util.clone(Prism.languages.javascript);
2154 2524
2155Prism.languages.jsx = Prism.languages.extend('markup', javascript);2525 Prism.languages.jsx = Prism.languages.extend('markup', javascript);
2156Prism.languages.jsx.tag.pattern= /<\/?(?:[\w.:-]+\s*(?:\s+(?:[\w.:-]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s{'">=]+|\{(?:\{(?:\{[^}]*\}|[^{}])*\}|[^{}])+\}))?|\{\.{3}[a-z_$][\w$]*(?:\.[a-z_$][\w$]*)*\}))*\s*\/?)?>/i;2526 Prism.languages.jsx.tag.pattern= /<\/?(?:[\w.:-]+\s*(?:\s+(?:[\w.:-]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s{'">=]+|\{(?:\{(?:\{[^}]*\}|[^{}])*\}|[^{}])+\}))?|\{\.{3}[a-z_$][\w$]*(?:\.[a-z_$][\w$]*)*\}))*\s*\/?)?>/i;
2157 2527
2158Prism.languages.jsx.tag.inside['tag'].pattern = /^<\/?[^\s>\/]*/i;2528 Prism.languages.jsx.tag.inside['tag'].pattern = /^<\/?[^\s>\/]*/i;
2159Prism.languages.jsx.tag.inside['attr-value'].pattern = /=(?!\{)(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">]+)/i;2529 Prism.languages.jsx.tag.inside['attr-value'].pattern = /=(?!\{)(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">]+)/i;
2160 2530 Prism.languages.jsx.tag.inside['tag'].inside['class-name'] = /^[A-Z]\w*(?:\.[A-Z]\w*)*$/;
2161Prism.languages.insertBefore('inside', 'attr-name', {2531
2162 'spread': {2532 Prism.languages.insertBefore('inside', 'attr-name', {
2163 pattern: /\{\.{3}[a-z_$][\w$]*(?:\.[a-z_$][\w$]*)*\}/,2533 'spread': {
2164 inside: {2534 pattern: /\{\.{3}[a-z_$][\w$]*(?:\.[a-z_$][\w$]*)*\}/,
2165 'punctuation': /\.{3}|[{}.]/,2535 inside: {
2166 'attr-value': /\w+/2536 'punctuation': /\.{3}|[{}.]/,
2167 }2537 'attr-value': /\w+/
2168 }2538 }
2169}, Prism.languages.jsx.tag);2539 }
2170 2540 }, Prism.languages.jsx.tag);
2171Prism.languages.insertBefore('inside', 'attr-value',{2541
2172 'script': {2542 Prism.languages.insertBefore('inside', 'attr-value',{
2173 // Allow for two levels of nesting2543 'script': {
2174 pattern: /=(\{(?:\{(?:\{[^}]*\}|[^}])*\}|[^}])+\})/i,2544 // Allow for two levels of nesting
2175 inside: {2545 pattern: /=(\{(?:\{(?:\{[^}]*\}|[^}])*\}|[^}])+\})/i,
2176 'script-punctuation': {2546 inside: {
2177 pattern: /^=(?={)/,2547 'script-punctuation': {
2178 alias: 'punctuation'2548 pattern: /^=(?={)/,
2179 },2549 alias: 'punctuation'
2180 rest: Prism.languages.jsx2550 },
2181 },2551 rest: Prism.languages.jsx
2182 'alias': 'language-javascript'2552 },
2183 }2553 'alias': 'language-javascript'
2184}, Prism.languages.jsx.tag);2554 }
2555 }, Prism.languages.jsx.tag);
2185 2556
2186// The following will handle plain text inside tags2557// The following will handle plain text inside tags
2187var stringifyToken = function (token) {2558 var stringifyToken = function (token) {
2188 if (!token) {2559 if (!token) {
2189 return '';2560 return '';
2190 }2561 }
2191 if (typeof token === 'string') {2562 if (typeof token === 'string') {
2192 return token;2563 return token;
2193 }2564 }
2194 if (typeof token.content === 'string') {2565 if (typeof token.content === 'string') {
2195 return token.content;2566 return token.content;
2196 }2567 }
2197 return token.content.map(stringifyToken).join('');2568 return token.content.map(stringifyToken).join('');
2198};2569 };
2199 2570
2200var walkTokens = function (tokens) {2571 var walkTokens = function (tokens) {
2201 var openedTags = [];2572 var openedTags = [];
2202 for (var i = 0; i < tokens.length; i++) {2573 for (var i = 0; i < tokens.length; i++) {
2203 var token = tokens[i];2574 var token = tokens[i];
2204 var notTagNorBrace = false;2575 var notTagNorBrace = false;
2205 2576
2206 if (typeof token !== 'string') {2577 if (typeof token !== 'string') {
2207 if (token.type === 'tag' && token.content[0] && token.content[0].type === 'tag') {2578 if (token.type === 'tag' && token.content[0] && token.content[0].type === 'tag') {
2208 // We found a tag, now find its kind2579 // We found a tag, now find its kind
2209 2580
2210 if (token.content[0].content[0].content === '</') {2581 if (token.content[0].content[0].content === '</') {
2211 // Closing tag2582 // Closing tag
2212 if (openedTags.length > 0 && openedTags[openedTags.length - 1].tagName === stringifyToken(token.content[0].content[1])) {2583 if (openedTags.length > 0 && openedTags[openedTags.length - 1].tagName === stringifyToken(token.content[0].content[1])) {
2213 // Pop matching opening tag2584 // Pop matching opening tag
2214 openedTags.pop();2585 openedTags.pop();
2215 }2586 }
2216 } else {2587 } else {
2217 if (token.content[token.content.length - 1].content === '/>') {2588 if (token.content[token.content.length - 1].content === '/>') {
2218 // Autoclosed tag, ignore2589 // Autoclosed tag, ignore
2219 } else {2590 } else {
2220 // Opening tag2591 // Opening tag
2221 openedTags.push({2592 openedTags.push({
2222 tagName: stringifyToken(token.content[0].content[1]),2593 tagName: stringifyToken(token.content[0].content[1]),
2223 openedBraces: 02594 openedBraces: 0
2224 });2595 });
2225 }2596 }
2226 }2597 }
2227 } else if (openedTags.length > 0 && token.type === 'punctuation' && token.content === '{') {2598 } else if (openedTags.length > 0 && token.type === 'punctuation' && token.content === '{') {
2228 2599
2229 // Here we might have entered a JSX context inside a tag2600 // Here we might have entered a JSX context inside a tag
2230 openedTags[openedTags.length - 1].openedBraces++;2601 openedTags[openedTags.length - 1].openedBraces++;
2231 2602
2232 } else if (openedTags.length > 0 && openedTags[openedTags.length - 1].openedBraces > 0 && token.type === 'punctuation' && token.content === '}') {2603 } else if (openedTags.length > 0 && openedTags[openedTags.length - 1].openedBraces > 0 && token.type === 'punctuation' && token.content === '}') {
2233 2604
2234 // Here we might have left a JSX context inside a tag2605 // Here we might have left a JSX context inside a tag
2235 openedTags[openedTags.length - 1].openedBraces--;2606 openedTags[openedTags.length - 1].openedBraces--;
2236 2607
2237 } else {2608 } else {
2238 notTagNorBrace = true2609 notTagNorBrace = true
2239 }2610 }
2240 }2611 }
2241 if (notTagNorBrace || typeof token === 'string') {2612 if (notTagNorBrace || typeof token === 'string') {
2242 if (openedTags.length > 0 && openedTags[openedTags.length - 1].openedBraces === 0) {2613 if (openedTags.length > 0 && openedTags[openedTags.length - 1].openedBraces === 0) {
2243 // Here we are inside a tag, and not inside a JSX context.2614 // Here we are inside a tag, and not inside a JSX context.
2244 // That's plain text: drop any tokens matched.2615 // That's plain text: drop any tokens matched.
2245 var plainText = stringifyToken(token);2616 var plainText = stringifyToken(token);
2246 2617
2247 // And merge text with adjacent text2618 // And merge text with adjacent text
2248 if (i < tokens.length - 1 && (typeof tokens[i + 1] === 'string' || tokens[i + 1].type === 'plain-text')) {2619 if (i < tokens.length - 1 && (typeof tokens[i + 1] === 'string' || tokens[i + 1].type === 'plain-text')) {
2249 plainText += stringifyToken(tokens[i + 1]);2620 plainText += stringifyToken(tokens[i + 1]);
2250 tokens.splice(i + 1, 1);2621 tokens.splice(i + 1, 1);
2251 }2622 }
2252 if (i > 0 && (typeof tokens[i - 1] === 'string' || tokens[i - 1].type === 'plain-text')) {2623 if (i > 0 && (typeof tokens[i - 1] === 'string' || tokens[i - 1].type === 'plain-text')) {
2253 plainText = stringifyToken(tokens[i - 1]) + plainText;2624 plainText = stringifyToken(tokens[i - 1]) + plainText;
2254 tokens.splice(i - 1, 1);2625 tokens.splice(i - 1, 1);
2255 i--;2626 i--;
2256 }2627 }
2257 2628
2258 tokens[i] = new Prism.Token('plain-text', plainText, null, plainText);2629 tokens[i] = new Prism.Token('plain-text', plainText, null, plainText);
2259 }2630 }
2260 }2631 }
2261 2632
2262 if (token.content && typeof token.content !== 'string') {2633 if (token.content && typeof token.content !== 'string') {
2263 walkTokens(token.content);2634 walkTokens(token.content);
2264 }2635 }
2265 }2636 }
2266};2637 };
2267 2638
2268Prism.hooks.add('after-tokenize', function (env) {2639 Prism.hooks.add('after-tokenize', function (env) {
2269 if (env.language !== 'jsx' && env.language !== 'tsx') {2640 if (env.language !== 'jsx' && env.language !== 'tsx') {
2270 return;2641 return;
2271 }2642 }
2272 walkTokens(env.tokens);2643 walkTokens(env.tokens);
2273});2644 });
2274 2645
2275}(Prism));2646}(Prism));
2276 2647
2277Prism.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
2283Prism.languages.ts = Prism.languages.typescript;
2284/* TODO2648/* TODO
2285 Add support for Markdown notation inside doc comments2649 Add support for Markdown notation inside doc comments
2286 Add support for nested block comments...2650 Add support for nested block comments...
@@ -2289,108 +2653,111 @@ Prism.languages.ts = Prism.languages.typescript; · 2 unmodified lines
2289*/2653*/
2290 2654
2291Prism.languages.rust = {2655Prism.languages.rust = {
2292 'comment': [2656 'comment': [
2293 {2657 {
2294 pattern: /(^|[^\\])\/\*[\s\S]*?\*\//,2658 pattern: /(^|[^\\])\/\*[\s\S]*?\*\//,
2295 lookbehind: true2659 lookbehind: true
2296 },2660 },
2297 {2661 {
2298 pattern: /(^|[^\\:])\/\/.*/,2662 pattern: /(^|[^\\:])\/\/.*/,
2299 lookbehind: true2663 lookbehind: true
2300 }2664 }
2301 ],2665 ],
2302 'string': [2666 'string': [
2303 {2667 {
2304 pattern: /b?r(#*)"(?:\\.|(?!"\1)[^\\\r\n])*"\1/,2668 pattern: /b?r(#*)"(?:\\.|(?!"\1)[^\\\r\n])*"\1/,
2305 greedy: true2669 greedy: true
2306 },2670 },
2307 {2671 {
2308 pattern: /b?"(?:\\.|[^\\\r\n"])*"/,2672 pattern: /b?"(?:\\.|[^\\\r\n"])*"/,
2309 greedy: true2673 greedy: true
2310 }2674 }
2311 ],2675 ],
2312 'char': {2676 'char': {
2313 pattern: /b?'(?:\\(?:x[0-7][\da-fA-F]|u{(?:[\da-fA-F]_*){1,6}|.)|[^\\\r\n\t'])'/,2677 pattern: /b?'(?:\\(?:x[0-7][\da-fA-F]|u{(?:[\da-fA-F]_*){1,6}|.)|[^\\\r\n\t'])'/,
2314 alias: 'string'2678 alias: 'string'
2315 },2679 },
2316 'lifetime-annotation': {2680 'lifetime-annotation': {
2317 pattern: /'[^\s>']+/,2681 pattern: /'[^\s>']+/,
2318 alias: 'symbol'2682 alias: 'symbol'
2319 },2683 },
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/,2684 'keyword': /\b(?:abstract|alignof|as|async|await|be|box|break|const|continue|crate|do|dyn|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|Self|struct|super|true|trait|type|typeof|union|unsafe|unsized|use|virtual|where|while|yield)\b/,
2321 2685
2322 'attribute': {2686 'attribute': {
2323 pattern: /#!?\[.+?\]/,2687 pattern: /#!?\[.+?\]/,
2324 greedy: true,2688 greedy: true,
2325 alias: 'attr-name'2689 alias: 'attr-name'
2326 },2690 },
2327 2691
2328 'function': [2692 'function': [
2329 /\w+(?=\s*\()/,2693 /\w+(?=\s*\()/,
2330 // Macros can use parens or brackets2694 // Macros can use parens or brackets
2331 /\w+!(?=\s*\(|\[)/2695 /\w+!(?=\s*\(|\[)/
2332 ],2696 ],
2333 'macro-rules': {2697 'macro-rules': {
2334 pattern: /\w+!/,2698 pattern: /\w+!/,
2335 alias: 'function'2699 alias: 'function'
2336 },2700 },
2337 2701
2338 // Hex, oct, bin, dec numbers with visual separators and type suffix2702 // Hex, oct, bin, dec numbers with visual separators and type suffix
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/,2703 '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/,
2340 2704
2341 // Closure params should not be confused with bitwise OR |2705 // Closure params should not be confused with bitwise OR |
2342 'closure-params': {2706 'closure-params': {
2343 pattern: /\|[^|]*\|(?=\s*[{-])/,2707 pattern: /\|[^|]*\|(?=\s*[{-])/,
2344 inside: {2708 inside: {
2345 'punctuation': /[|:,]/,2709 'punctuation': /[|:,]/,
2346 'operator': /[&*]/2710 'operator': /[&*]/
2347 }2711 }
2348 },2712 },
2349 'punctuation': /[{}[\];(),:]|\.+|->/,2713 'punctuation': /->|\.\.=|\.{1,3}|::|[{}[\];(),:]/,
2350 'operator': /[-+*\/%!^]=?|=[=>]?|@|&[&=]?|\|[|=]?|<<?=?|>>?=?/2714 'operator': /[-+*\/%!^]=?|=[=>]?|&[&=]?|\|[|=]?|<<?=?|>>?=?|[@?]/
2351};2715};
2716
2352Prism.languages.yaml = {2717Prism.languages.yaml = {
2353 'scalar': {2718 'scalar': {
2354 pattern: /([\-:]\s*(?:![^\s]+)?[ \t]*[|>])[ \t]*(?:((?:\r?\n|\r)[ \t]+)[^\r\n]+(?:\2[^\r\n]+)*)/,2719 pattern: /([\-:]\s*(?:![^\s]+)?[ \t]*[|>])[ \t]*(?:((?:\r?\n|\r)[ \t]+)[^\r\n]+(?:\2[^\r\n]+)*)/,
2355 lookbehind: true,2720 lookbehind: true,
2356 alias: 'string'2721 alias: 'string'
2357 },2722 },
2358 'comment': /#.*/,2723 'comment': /#.*/,
2359 'key': {2724 'key': {
2360 pattern: /(\s*(?:^|[:\-,[{\r\n?])[ \t]*(?:![^\s]+)?[ \t]*)[^\r\n{[\]},#\s]+?(?=\s*:\s)/,2725 pattern: /(\s*(?:^|[:\-,[{\r\n?])[ \t]*(?:![^\s]+)?[ \t]*)[^\r\n{[\]},#\s]+?(?=\s*:\s)/,
2361 lookbehind: true,2726 lookbehind: true,
2362 alias: 'atrule'2727 alias: 'atrule'
2363 },2728 },
2364 'directive': {2729 'directive': {
2365 pattern: /(^[ \t]*)%.+/m,2730 pattern: /(^[ \t]*)%.+/m,
2366 lookbehind: true,2731 lookbehind: true,
2367 alias: 'important'2732 alias: 'important'
2368 },2733 },
2369 'datetime': {2734 '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,2735 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,2736 lookbehind: true,
2372 alias: 'number'2737 alias: 'number'
2373 },2738 },
2374 'boolean': {2739 'boolean': {
2375 pattern: /([:\-,[{]\s*(?:![^\s]+)?[ \t]*)(?:true|false)[ \t]*(?=$|,|]|})/im,2740 pattern: /([:\-,[{]\s*(?:![^\s]+)?[ \t]*)(?:true|false)[ \t]*(?=$|,|]|})/im,
2376 lookbehind: true,2741 lookbehind: true,
2377 alias: 'important'2742 alias: 'important'
2378 },2743 },
2379 'null': {2744 'null': {
2380 pattern: /([:\-,[{]\s*(?:![^\s]+)?[ \t]*)(?:null|~)[ \t]*(?=$|,|]|})/im,2745 pattern: /([:\-,[{]\s*(?:![^\s]+)?[ \t]*)(?:null|~)[ \t]*(?=$|,|]|})/im,
2381 lookbehind: true,2746 lookbehind: true,
2382 alias: 'important'2747 alias: 'important'
2383 },2748 },
2384 'string': {2749 'string': {
2385 pattern: /([:\-,[{]\s*(?:![^\s]+)?[ \t]*)("|')(?:(?!\2)[^\\\r\n]|\\.)*\2(?=[ \t]*(?:$|,|]|}))/m,2750 pattern: /([:\-,[{]\s*(?:![^\s]+)?[ \t]*)("|')(?:(?!\2)[^\\\r\n]|\\.)*\2(?=[ \t]*(?:$|,|]|}|\s*#))/m,
2386 lookbehind: true,2751 lookbehind: true,
2387 greedy: true2752 greedy: true
2388 },2753 },
2389 'number': {2754 'number': {
2390 pattern: /([:\-,[{]\s*(?:![^\s]+)?[ \t]*)[+-]?(?:0x[\da-f]+|0o[0-7]+|(?:\d+\.?\d*|\.?\d+)(?:e[+-]?\d+)?|\.inf|\.nan)[ \t]*(?=$|,|]|})/im,2755 pattern: /([:\-,[{]\s*(?:![^\s]+)?[ \t]*)[+-]?(?:0x[\da-f]+|0o[0-7]+|(?:\d+\.?\d*|\.?\d+)(?:e[+-]?\d+)?|\.inf|\.nan)[ \t]*(?=$|,|]|})/im,
2391 lookbehind: true2756 lookbehind: true
2392 },2757 },
2393 'tag': /![^\s]+/,2758 'tag': /![^\s]+/,
2394 'important': /[&*][\w]+/,2759 'important': /[&*][\w]+/,
2395 'punctuation': /---|[:[\]{}\-,|>?]|\.\.\./2760 'punctuation': /---|[:[\]{}\-,|>?]|\.\.\./
2396};2761};
2762
2763Prism.languages.yml = Prism.languages.yaml;
assets/javascripts/vendor/raven.js+1−1
@@ -856,7 +856,7 @@ Raven.prototype = { · 855 unmodified lines
856 },856 },
857 857
858 _triggerEvent: function(eventType, options) {858 _triggerEvent: function(eventType, options) {
859 // NOTE: `event` is a native browser thing, so let's avoid conflicting wiht it859 // NOTE: `event` is a native browser thing, so let's avoid conflicting with it
860 var evt, key;860 var evt, key;
861 861
862 if (!this._hasDocument) return;862 if (!this._hasDocument) return;
assets/javascripts/views/content/content.coffee+3−0
@@ -153,6 +153,9 @@ class app.views.Content extends app.View · 152 unmodified lines
153 return153 return
154 154
155 afterRoute: (route, context) =>155 afterRoute: (route, context) =>
156 if route != 'entry' and route != 'type'
157 resetFavicon()
158
156 switch route159 switch route
157 when 'root'160 when 'root'
158 @show @rootPage161 @show @rootPage
assets/javascripts/views/content/entry_page.coffee+2−1
@@ -40,6 +40,7 @@ class app.views.EntryPage extends app.View · 39 unmodified lines
40 if app.disabledDocs.findBy 'slug', @entry.doc.slug40 if app.disabledDocs.findBy 'slug', @entry.doc.slug
41 @hiddenView = new app.views.HiddenPage @el, @entry41 @hiddenView = new app.views.HiddenPage @el, @entry
42 42
43 setFaviconForDoc(@entry.doc)
43 @delay @polyfillMathML44 @delay @polyfillMathML
44 @trigger 'loaded'45 @trigger 'loaded'
45 return46 return
@@ -123,7 +124,7 @@ class app.views.EntryPage extends app.View · 77 unmodified lines
123 @render @tmpl('pageLoadError')124 @render @tmpl('pageLoadError')
124 @resetClass()125 @resetClass()
125 @addClass @constructor.errorClass126 @addClass @constructor.errorClass
126 app.appCache?.update()127 app.serviceWorker?.update()
127 return128 return
128 129
129 cache: ->130 cache: ->
assets/javascripts/views/content/settings_page.coffee+10−11
@@ -1,7 +1,4 @@
1class app.views.SettingsPage extends app.View1class app.views.SettingsPage extends app.View
2 LAYOUTS = ['_max-width', '_sidebar-hidden', '_native-scrollbars']
3 SIDEBAR_HIDDEN_LAYOUT = '_sidebar-hidden'
4
5 @className: '_static'2 @className: '_static'
6 3
7 @events:4 @events:
@@ -17,31 +14,31 @@ class app.views.SettingsPage extends app.View · 9 unmodified lines
17 settings.dark = app.settings.get('dark')14 settings.dark = app.settings.get('dark')
18 settings.smoothScroll = !app.settings.get('fastScroll')15 settings.smoothScroll = !app.settings.get('fastScroll')
19 settings.arrowScroll = app.settings.get('arrowScroll')16 settings.arrowScroll = app.settings.get('arrowScroll')
20 settings[layout] = app.settings.hasLayout(layout) for layout in LAYOUTS17 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 settings20 settings
22 21
23 getTitle: ->22 getTitle: ->
24 'Preferences'23 'Preferences'
25 24
26 toggleDark: (enable) ->25 toggleDark: (enable) ->
27 html = document.documentElement
28 html.classList.toggle('_theme-default')
29 html.classList.toggle('_theme-dark')
30 app.settings.set('dark', !!enable)26 app.settings.set('dark', !!enable)
31 app.appCache?.updateInBackground()
32 return27 return
33 28
34 toggleLayout: (layout, enable) ->29 toggleLayout: (layout, enable) ->
35 document.body.classList[if enable then 'add' else 'remove'](layout) unless layout is SIDEBAR_HIDDEN_LAYOUT
36 document.body.classList[if $.overlayScrollbarsEnabled() then 'add' else 'remove']('_overlay-scrollbars')
37 app.settings.setLayout(layout, enable)30 app.settings.setLayout(layout, enable)
38 app.appCache?.updateInBackground()
39 return31 return
40 32
41 toggleSmoothScroll: (enable) ->33 toggleSmoothScroll: (enable) ->
42 app.settings.set('fastScroll', !enable)34 app.settings.set('fastScroll', !enable)
43 return35 return
44 36
37 toggleAnalyticsConsent: (enable) ->
38 app.settings.set('analyticsConsent', if enable then '1' else '0')
39 resetAnalytics() unless enable
40 return
41
45 toggle: (name, enable) ->42 toggle: (name, enable) ->
46 app.settings.set(name, enable)43 app.settings.set(name, enable)
47 return44 return
@@ -85,6 +82,8 @@ class app.views.SettingsPage extends app.View · 37 unmodified lines
85 @toggleSmoothScroll input.checked82 @toggleSmoothScroll input.checked
86 when 'import'83 when 'import'
87 @import input.files[0], input84 @import input.files[0], input
85 when 'analyticsConsent'
86 @toggleAnalyticsConsent input.checked
88 else87 else
89 @toggle input.name, input.checked88 @toggle input.name, input.checked
90 return89 return
assets/javascripts/views/content/type_page.coffee+1−0
@@ -9,6 +9,7 @@ class app.views.TypePage extends app.View · 8 unmodified lines
9 9
10 render: (@type) ->10 render: (@type) ->
11 @html @tmpl('typePage', @type)11 @html @tmpl('typePage', @type)
12 setFaviconForDoc(@type.doc)
12 return13 return
13 14
14 getTitle: ->15 getTitle: ->
assets/javascripts/views/layout/document.coffee+7−5
@@ -75,9 +75,11 @@ class app.views.Document extends app.View · 74 unmodified lines
75 return unless target.hasAttribute('data-behavior')75 return unless target.hasAttribute('data-behavior')
76 $.stopEvent(event)76 $.stopEvent(event)
77 switch target.getAttribute('data-behavior')77 switch target.getAttribute('data-behavior')
78 when 'back' then history.back()78 when 'back' then history.back()
79 when 'reload' then window.location.reload()79 when 'reload' then window.location.reload()
80 when 'reboot' then app.reboot()80 when 'reboot' then app.reboot()
81 when 'hard-reload' then app.reload()81 when 'hard-reload' then app.reload()
82 when 'reset' then app.reset() if confirm('Are you sure you want to reset DevDocs?')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', expires: 1e8) && app.reboot()
84 when 'decline-analytics' then Cookies.set('analyticsConsent', '0', expires: 1e8) && app.reboot()
83 return85 return
assets/javascripts/views/layout/resizer.coffee+2−9
@@ -11,9 +11,6 @@ class app.views.Resizer extends app.View · 10 unmodified lines
11 init: ->11 init: ->
12 @el.setAttribute('draggable', 'true')12 @el.setAttribute('draggable', 'true')
13 @appendTo $('._app')13 @appendTo $('._app')
14
15 @style = $('style[data-resizer]')
16 @size = @style.getAttribute('data-size')
17 return14 return
18 15
19 MIN = 26016 MIN = 260
@@ -24,15 +21,11 @@ class app.views.Resizer extends app.View · 4 unmodified lines
24 return unless value > 021 return unless value > 0
25 value = Math.min(Math.max(Math.round(value), MIN), MAX)22 value = Math.min(Math.max(Math.round(value), MIN), MAX)
26 newSize = "#{value}px"23 newSize = "#{value}px"
27 @style.innerHTML = @style.innerHTML.replace(new RegExp(@size, 'g'), newSize)24 document.documentElement.style.setProperty('--sidebarWidth', newSize)
28 @size = newSize25 app.settings.setSize(value) if save
29 if save
30 app.settings.setSize(value)
31 app.appCache?.updateInBackground()
32 return26 return
33 27
34 onDragStart: (event) =>28 onDragStart: (event) =>
35 @style.removeAttribute('disabled')
36 event.dataTransfer.effectAllowed = 'link'29 event.dataTransfer.effectAllowed = 'link'
37 event.dataTransfer.setData('Text', '')30 event.dataTransfer.setData('Text', '')
38 $.on(window, 'dragover', @onDrag)31 $.on(window, 'dragover', @onDrag)
assets/javascripts/views/layout/settings.coffee+1−9
@@ -25,7 +25,6 @@ class app.views.Settings extends app.View · 24 unmodified lines
25 if super25 if super
26 @render()26 @render()
27 document.body.classList.remove(SIDEBAR_HIDDEN_LAYOUT)27 document.body.classList.remove(SIDEBAR_HIDDEN_LAYOUT)
28 app.appCache?.on 'progress', @onAppCacheProgress
29 return28 return
30 29
31 deactivate: ->30 deactivate: ->
@@ -33,7 +32,6 @@ class app.views.Settings extends app.View · 1 unmodified line
33 @resetClass()32 @resetClass()
34 @docPicker.detach()33 @docPicker.detach()
35 document.body.classList.add(SIDEBAR_HIDDEN_LAYOUT) if app.settings.hasLayout(SIDEBAR_HIDDEN_LAYOUT)34 document.body.classList.add(SIDEBAR_HIDDEN_LAYOUT) if app.settings.hasLayout(SIDEBAR_HIDDEN_LAYOUT)
36 app.appCache?.off 'progress', @onAppCacheProgress
37 return35 return
38 36
39 render: ->37 render: ->
@@ -52,7 +50,7 @@ class app.views.Settings extends app.View · 12 unmodified lines
52 docs = @docPicker.getSelectedDocs()50 docs = @docPicker.getSelectedDocs()
53 app.settings.setDocs(docs)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 disabledDocs = new app.collections.Docs(doc for doc in app.docs.all() when docs.indexOf(doc.slug) is -1)54 disabledDocs = new app.collections.Docs(doc for doc in app.docs.all() when docs.indexOf(doc.slug) is -1)
57 disabledDocs.uninstall ->55 disabledDocs.uninstall ->
58 app.db.migrate()56 app.db.migrate()
@@ -83,9 +81,3 @@ class app.views.Settings extends app.View · 24 unmodified lines
83 $.stopEvent(event)81 $.stopEvent(event)
84 app.router.show '/'82 app.router.show '/'
85 return83 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
assets/javascripts/views/search/search.coffee+8−0
@@ -30,6 +30,9 @@ class app.views.Search extends app.View · 29 unmodified lines
30 .on 'results', @onResults30 .on 'results', @onResults
31 .on 'end', @onEnd31 .on 'end', @onEnd
32 32
33 @scope
34 .on 'change', @onScopeChange
35
33 app.on 'ready', @onReady36 app.on 'ready', @onReady
34 $.on window, 'hashchange', @searchUrl37 $.on window, 'hashchange', @searchUrl
35 $.on window, 'focus', @onWindowFocus38 $.on window, 'focus', @onWindowFocus
@@ -138,6 +141,11 @@ class app.views.Search extends app.View · 102 unmodified lines
138 $.stopEvent(event)141 $.stopEvent(event)
139 return142 return
140 143
144 onScopeChange: =>
145 @value = ''
146 @onInput()
147 return
148
141 afterRoute: (name, context) =>149 afterRoute: (name, context) =>
142 return if app.shortcuts.eventInProgress?.name is 'escape'150 return if app.shortcuts.eventInProgress?.name is 'escape'
143 @reset(true) if not context.init and app.router.isIndex()151 @reset(true) if not context.init and app.router.isIndex()
assets/javascripts/views/search/search_scope.coffee+23−5
@@ -6,7 +6,9 @@ class app.views.SearchScope extends app.View · 5 unmodified lines
6 tag: '._search-tag'6 tag: '._search-tag'
7 7
8 @events:8 @events:
9 click: 'onClick'
9 keydown: 'onKeydown'10 keydown: 'onKeydown'
11 textInput: 'onTextInput'
10 12
11 @routes:13 @routes:
12 after: 'afterRoute'14 after: 'afterRoute'
@@ -87,17 +89,33 @@ class app.views.SearchScope extends app.View · 74 unmodified lines
87 @trigger 'change', null, previousDoc89 @trigger 'change', null, previousDoc
88 return90 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 onKeydown: (event) =>103 onKeydown: (event) =>
91 if event.which is 8 # backspace104 if event.which is 8 # backspace
92 if @doc and not @input.value105 if @doc and @input.selectionEnd is 0
93 $.stopEvent(event)
94 @reset()106 @reset()
95 else if not @doc and @input.value107 $.stopEvent(event)
108 else if not @doc and @input.value and not $.isChromeForAndroid()
96 return if event.ctrlKey or event.metaKey or event.altKey or event.shiftKey109 return if event.ctrlKey or event.metaKey or event.altKey or event.shiftKey
97 if event.which is 9 or # tab110 if event.which is 9 or # tab
98 (event.which is 32 and app.isMobile()) # space111 (event.which is 32 and app.isMobile()) # space
99 @search @input.value[0...@input.selectionStart]112 @doScopeSearch(event)
100 $.stopEvent(event) if @doc113 return
114
115 onTextInput: (event) =>
116 return unless $.isChromeForAndroid()
117 if not @doc and @input.value and event.data == ' '
118 @doScopeSearch(event)
101 return119 return
102 120
103 extractHashValue: ->121 extractHashValue: ->
assets/javascripts/views/sidebar/sidebar.coffee+1−1
@@ -28,7 +28,7 @@ class app.views.Sidebar extends app.View · 27 unmodified lines
28 28
29 app.on 'ready', @onReady29 app.on 'ready', @onReady
30 30
31 $.on document.documentElement, 'mouseleave', (event) => @display() if event.clientX < 1031 $.on document.documentElement, 'mouseleave', (event) => @display() unless event.clientX <= 0
32 $.on document.documentElement, 'mouseenter', => @resetDisplay(forceNoHover: false)32 $.on document.documentElement, 'mouseenter', => @resetDisplay(forceNoHover: false)
33 return33 return
34 34
assets/stylesheets/application.css.scss+10−5
@@ -1,7 +1,6 @@
1//= depend_on docs-1.png1//= depend_on sprites/docs.png
2//= depend_on docs-1@2x.png2//= depend_on sprites/docs@2x.png
3//= depend_on docs-2.png3//= depend_on sprites/docs.json
4//= depend_on docs-2@2x.png
5 4
6/*!5/*!
7 * Copyright 2013-2019 Thibaut Courouble and other contributors6 * Copyright 2013-2019 Thibaut Courouble and other contributors
@@ -30,7 +29,8 @@ · 22 unmodified lines
30 'components/path',29 'components/path',
31 'components/notice',30 'components/notice',
32 'components/prism',31 'components/prism',
33 'components/mobile';32 'components/mobile',
33 'components/environment';
34 34
35@import 'pages/simple',35@import 'pages/simple',
36 'pages/angular',36 'pages/angular',
@@ -46,6 +46,7 @@ · 9 unmodified lines
46 'pages/coffeescript',46 'pages/coffeescript',
47 'pages/cordova',47 'pages/cordova',
48 'pages/crystal',48 'pages/crystal',
49 'pages/cypress',
49 'pages/d',50 'pages/d',
50 'pages/d3',51 'pages/d3',
51 'pages/dart',52 'pages/dart',
@@ -77,6 +78,7 @@ · 25 unmodified lines
77 'pages/nginx',78 'pages/nginx',
78 'pages/node',79 'pages/node',
79 'pages/npm',80 'pages/npm',
81 'pages/octave',
80 'pages/openjdk',82 'pages/openjdk',
81 'pages/perl',83 'pages/perl',
82 'pages/phalcon',84 'pages/phalcon',
@@ -95,6 +97,8 @@ · 12 unmodified lines
95 'pages/rfc',97 'pages/rfc',
96 'pages/rubydoc',98 'pages/rubydoc',
97 'pages/rust',99 'pages/rust',
100 'pages/rxjs',
101 'pages/scala',
98 'pages/sinon',102 'pages/sinon',
99 'pages/socketio',103 'pages/socketio',
100 'pages/sphinx',104 'pages/sphinx',
@@ -107,5 +111,6 @@ · 6 unmodified lines
107 'pages/underscore',111 'pages/underscore',
108 'pages/vue',112 'pages/vue',
109 'pages/webpack',113 'pages/webpack',
114 'pages/wordpress',
110 'pages/yard',115 'pages/yard',
111 'pages/yii';116 'pages/yii';
assets/stylesheets/components/_environment.scss.erb+3−0
@@ -0,0 +1,3 @@
1._hide-in-development {
2 <%= App.environment != :production ? 'display: none;' : '' %>
3}
assets/stylesheets/components/_header.scss+1−0
@@ -215,5 +215,6 @@ · 214 unmodified lines
215 color: var(--textColorLight);215 color: var(--textColorLight);
216 background: var(--searchTagBackground);216 background: var(--searchTagBackground);
217 border-radius: 2px;217 border-radius: 2px;
218 cursor: pointer;
218 @extend %truncate-text;219 @extend %truncate-text;
219}220}
assets/stylesheets/components/_notif.scss+4−0
@@ -134,3 +134,7 @@ · 133 unmodified lines
134 134
135 ._notif-info { color: var(--textColorLight); }135 ._notif-info { color: var(--textColorLight); }
136}136}
137
138._notif-right {
139 float: right;
140}
assets/stylesheets/global/_icons.scss+0−182
@@ -1,182 +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
26html._theme-dark {
27 %darkIconFix {
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-jsdoc:before,
96._icon-koa:before,
97._icon-graphite:before,
98._icon-mongoose:before { background-position: -7rem -5rem; }
99._icon-phpunit:before { background-position: -8rem -5rem; }
100._icon-nokogiri:before { background-position: -9rem -5rem; @extend %darkIconFix !optional; }
101._icon-rethinkdb:before { background-position: 0 -6rem; }
102._icon-react:before { background-position: -1rem -6rem; }
103._icon-socketio:before { background-position: -2rem -6rem; }
104._icon-modernizr:before { background-position: -3rem -6rem; }
105._icon-bower:before { background-position: -4rem -6rem; }
106._icon-fish:before { background-position: -5rem -6rem; @extend %darkIconFix !optional; }
107._icon-scikit_image:before { background-position: -6rem -6rem; }
108._icon-twig:before { background-position: -7rem -6rem; }
109._icon-pandas:before { background-position: -8rem -6rem; }
110._icon-scikit_learn:before { background-position: -9rem -6rem; }
111._icon-bottle:before { background-position: 0 -7rem; }
112._icon-docker:before { background-position: -1rem -7rem; }
113._icon-cakephp:before { background-position: -2rem -7rem; }
114._icon-lua:before { background-position: -3rem -7rem; @extend %darkIconFix !optional; }
115._icon-clojure:before { background-position: -4rem -7rem; }
116._icon-symfony:before { background-position: -5rem -7rem; }
117._icon-mocha:before { background-position: -6rem -7rem; }
118._icon-meteor:before { background-position: -7rem -7rem; @extend %darkIconFix !optional; }
119._icon-npm:before { background-position: -8rem -7rem; }
120._icon-apache_http_server:before { background-position: -9rem -7rem; }
121._icon-drupal:before { background-position: 0 -8rem; }
122._icon-webpack:before { background-position: -1rem -8rem; }
123._icon-phaser:before { background-position: -2rem -8rem; }
124._icon-vue:before { background-position: -3rem -8rem; }
125._icon-opentsdb:before { background-position: -4rem -8rem; }
126._icon-q:before { background-position: -5rem -8rem; }
127._icon-crystal:before { background-position: -6rem -8rem; @extend %darkIconFix !optional; }
128._icon-julia:before { background-position: -7rem -8rem; @extend %darkIconFix !optional; }
129._icon-redux:before { background-position: -8rem -8rem; @extend %darkIconFix !optional; }
130._icon-bootstrap:before { background-position: -9rem -8rem; }
131._icon-react_native:before { background-position: 0 -9rem; }
132._icon-phalcon:before { background-position: -1rem -9rem; }
133._icon-matplotlib:before { background-position: -2rem -9rem; }
134._icon-cmake:before { background-position: -3rem -9rem; }
135._icon-elixir:before { background-position: -4rem -9rem; @extend %darkIconFix !optional; }
136._icon-vagrant:before { background-position: -5rem -9rem; }
137._icon-dojo:before { background-position: -6rem -9rem; }
138._icon-flow:before { background-position: -7rem -9rem; }
139._icon-relay:before { background-position: -8rem -9rem; }
140._icon-phoenix:before { background-position: -9rem -9rem; }
141
142._icon-tcl_tk:before { background-position: 0 0; @extend %doc-icon-2; }
143._icon-erlang:before { background-position: -1rem 0; @extend %doc-icon-2; }
144._icon-chef:before { background-position: -2rem 0; @extend %doc-icon-2; }
145._icon-ramda:before { background-position: -3rem 0; @extend %doc-icon-2; @extend %darkIconFix !optional; }
146._icon-codeigniter:before { background-position: -4rem 0; @extend %doc-icon-2; @extend %darkIconFix !optional; }
147._icon-influxdata:before { background-position: -5rem 0; @extend %doc-icon-2; @extend %darkIconFix !optional; }
148._icon-tensorflow:before { background-position: -6rem 0; @extend %doc-icon-2; }
149._icon-haxe:before { background-position: -7rem 0; @extend %doc-icon-2; }
150._icon-ansible:before { background-position: -8rem 0; @extend %doc-icon-2; @extend %darkIconFix !optional; }
151._icon-typescript:before { background-position: -9rem 0; @extend %doc-icon-2; }
152._icon-browser_support_tables:before { background-position: 0rem -1rem; @extend %doc-icon-2; }
153._icon-gnu_fortran:before { background-position: -1rem -1rem; @extend %doc-icon-2; }
154._icon-gcc:before { background-position: -2rem -1rem; @extend %doc-icon-2; }
155._icon-perl:before { background-position: -3rem -1rem; @extend %doc-icon-2; }
156._icon-apache_pig:before { background-position: -4rem -1rem; @extend %doc-icon-2; }
157._icon-numpy:before { background-position: -5rem -1rem; @extend %doc-icon-2; }
158._icon-kotlin:before { background-position: -6rem -1rem; @extend %doc-icon-2; }
159._icon-padrino:before { background-position: -7rem -1rem; @extend %doc-icon-2; }
160._icon-angular:before { background-position: -8rem -1rem; @extend %doc-icon-2; }
161._icon-love:before { background-position: -9rem -1rem; @extend %doc-icon-2; }
162._icon-jasmine:before { background-position: 0 -2rem; @extend %doc-icon-2; }
163._icon-pug:before { background-position: -1rem -2rem; @extend %doc-icon-2; }
164._icon-electron:before { background-position: -2rem -2rem; @extend %doc-icon-2; }
165._icon-falcon:before { background-position: -3rem -2rem; @extend %doc-icon-2; }
166._icon-godot:before { background-position: -4rem -2rem; @extend %doc-icon-2; }
167._icon-nim:before { background-position: -5rem -2rem; @extend %doc-icon-2; @extend %darkIconFix !optional; }
168._icon-vulkan:before { background-position: -6rem -2rem; @extend %doc-icon-2; @extend %darkIconFix !optional; }
169._icon-d:before { background-position: -7rem -2rem; @extend %doc-icon-2; }
170._icon-bluebird:before { background-position: -8rem -2rem; @extend %doc-icon-2; }
171._icon-eslint:before { background-position: -9rem -2rem; @extend %doc-icon-2; }
172._icon-homebrew:before { background-position: 0 -3rem; @extend %doc-icon-2; }
173._icon-jekyll:before { background-position: -1rem -3rem; @extend %doc-icon-2; }
174._icon-babel:before { background-position: -2rem -3rem; @extend %doc-icon-2; }
175._icon-leaflet:before { background-position: -3rem -3rem; @extend %doc-icon-2; }
176._icon-terraform:before { background-position: -4rem -3rem; @extend %doc-icon-2; }
177._icon-pygame:before { background-position: -5rem -3rem; @extend %doc-icon-2; }
178._icon-bash:before { background-position: -6rem -3rem; @extend %doc-icon-2; }
179._icon-dart:before { background-position: -7rem -3rem; @extend %doc-icon-2; }
180._icon-qt:before { background-position: -8rem -3rem; @extend %doc-icon-2; }
181._icon-puppeteer:before { background-position: -9rem -3rem; @extend %doc-icon-2; }
182._icon-handlebars:before { background-position: 0 -4rem; @extend %doc-icon-2; @extend %darkIconFix !optional; }
assets/stylesheets/global/_icons.scss.erb+43−0
@@ -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
25html._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 %>
assets/stylesheets/pages/_cypress.scss+21−0
@@ -0,0 +1,21 @@
1._cypress {
2 @extend %simple;
3
4 .note {
5 h1 {
6 margin-left: inherit
7 }
8
9 &.danger {
10 @extend %note-red
11 }
12
13 &.info {
14 @extend %note-blue
15 }
16
17 &.success {
18 @extend %note-green
19 }
20 }
21}
assets/stylesheets/pages/_octave.scss+9−0
@@ -0,0 +1,9 @@
1._octave {
2 @extend %simple;
3
4 dl:not([compact]) > dt { @extend %block-label, %label-blue; }
5
6 dl[compact] > dt { @extend %block-label; }
7
8 .footnotes-heading { @extend %block-heading; }
9}
assets/stylesheets/pages/_rxjs.scss+24−0
@@ -0,0 +1,24 @@
1._rxjs {
2 @extend %simple;
3
4 .pre-title { @extend %pre-heading; }
5
6 .breadcrumbs { @extend %note; }
7 .banner { @extend %note-green; }
8 code.stable { @extend %label-green; }
9 code.experimental { @extend %label-orange; }
10 code.deprecated { @extend %label-red; }
11 .alert.is-important { @extend %note-red; }
12 .alert.is-helpful, .breadcrumbs { @extend %note-blue; }
13
14 .breadcrumbs { padding-left: 2em; }
15
16 img { margin: 1em 0; }
17
18 .location-badge {
19 font-style: italic;
20 text-align: right;
21 }
22
23 td h3 { margin: 0 !important; }
24}
assets/stylesheets/pages/_scala.scss+4−0
@@ -0,0 +1,4 @@
1._scala {
2 @extend %simple;
3 .deprecated { @extend %label-red; }
4}
assets/stylesheets/pages/_wordpress.scss+15−0
@@ -0,0 +1,15 @@
1._wordpress {
2 @extend %simple;
3
4 .breadcrumbs {
5 display: none;
6 }
7
8 .callout-warning {
9 @extend %note, %note-red;
10 }
11
12 .callout-alert {
13 @extend %note, %note-orange;
14 }
15}
docs/adding-docs.md+6−4
@@ -1,9 +1,9 @@
1Adding 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.1Adding 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 2
3**Note:** please read the [contributing guidelines](https://github.com/Thibaut/devdocs/blob/master/.github/CONTRIBUTING.md) before submitting a new documentation.3**Note:** please read the [contributing guidelines](../.github/CONTRIBUTING.md) before submitting a new documentation.
4 4
51. 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`)51. 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`)
62. Add the appropriate class attributes and filter options (see the [Scraper Reference](./Scraper-Reference.md) page).62. Add the appropriate class attributes and filter options (see the [Scraper Reference](./scraper-reference.md) page).
73. Check that the scraper is listed in `thor docs:list`.73. Check that the scraper is listed in `thor docs:list`.
84. 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:84. 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.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.
@@ -13,9 +13,11 @@ Adding a documentation may look like a daunting task but once you get the hang o · 3 unmodified lines
136. 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).136. 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).
147. Start the server, open the app, enable the documentation, and see how everything plays out.147. Start the server, open the app, enable the documentation, and see how everything plays out.
158. Tweak the scraper/filters and repeat 5) and 6) until the pages and metadata are ok.158. Tweak the scraper/filters and repeat 5) and 6) until the pages and metadata are ok.
169. 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). _(Note: feel free to submit a pull request without custom CSS/JS)_169. 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.
1710. 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).1710. 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).
1811. Add the documentation's icon in the `public/icons/docs/[my_doc]/` directory, in both 16x16 and 32x32-pixels formats. It'll be added to the icon sprite after your pull request is merged.1811. 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.
1912. 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.
2013. Ensure `thor updates:check [my_doc]` shows the correct latest version.
19 21
20If 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.)22If 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.)
21 23
docs/filter-reference.mdrenamed from docs/Filter-Reference.md+0−0

File renamed without changes.

docs/maintainers.md+7−7
@@ -4,7 +4,7 @@ This document is intended for [DevDocs maintainers](#list-of-maintainers). · 3 unmodified lines
4 4
5## Merging pull requests5## Merging pull requests
6 6
7- Unless the change is trivial or in an area that you are familiar with, PRs should be approved by at least two maintainers before being merged.7- PRs should be approved by at least one maintainer before being merged.
8 8
9- PRs that add or update documentations should always be merged locally, and the app deployed, before the merge is pushed to GitHub.9- PRs that add or update documentations should always be merged locally, and the app deployed, before the merge is pushed to GitHub.
10 10
@@ -21,8 +21,7 @@ The process for updating docs is as follow: · 10 unmodified lines
21- Commit the changes (protip: use the `thor docs:commit` command documented below).21- Commit the changes (protip: use the `thor docs:commit` command documented below).
22- Optional: do more updates.22- Optional: do more updates.
23- Run `thor docs:upload` (documented below).23- Run `thor docs:upload` (documented below).
24- [Deploy the app](#deploying-devdocs) and verify that everything works in production.24- Push to GitHub to [deploy the app](#deploying-devdocs) and verify that everything works in production.
25- Push to GitHub.
26- Run `thor docs:clean` (documented below).25- Run `thor docs:clean` (documented below).
27 26
28Note: 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`.27Note: 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`.
@@ -82,21 +81,22 @@ In addition to the [publicly-documented commands](https://github.com/freeCodeCam · 53 unmodified lines
82 81
83## Deploying DevDocs82## Deploying DevDocs
84 83
85Once docs have been uploaded via `thor docs:upload` (if applicable), deploying DevDocs is as simple as running `git push heroku master`. See [Heroku's documentation](https://devcenter.heroku.com/articles/git) for more information.84Once 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.
86 85
87- If you're deploying documentation updates, verify that the documentations work properly once the deploy is done (you will need to reload [devdocs.io](https://devdocs.io/) a couple times for the application cache to update and the new version to load).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.
88- If you're deploying frontend changes, monitor [Sentry](https://sentry.io/devdocs/devdocs-js/) for new JS errors once the deploy is done.87- If you're deploying frontend changes, monitor [Sentry](https://sentry.io/devdocs/devdocs-js/) for new JS errors once the deploy is done.
89- 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.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.
90 89
91If any issue arises, run `heroku rollback` to rollback to the previous 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.90If 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.
92 91
93If this is your first deploy, make sure another maintainer is around to assist. 92If this is your first deploy, make sure another maintainer is around to assist.
94 93
95## List of maintainers94## List of maintainers
96 95
97- [Beau Carnes](https://github.com/beaucarnes)
98- [Jed Fox](https://github.com/j-f1)96- [Jed Fox](https://github.com/j-f1)
99- [Jasper van Merle](https://github.com/jmerle)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)100- [Thibaut Courouble](https://github.com/thibaut)
101 101
102Interested in helping maintain DevDocs? Come talk to us on [Gitter](https://gitter.im/FreeCodeCamp/DevDocs) :)102Interested in helping maintain DevDocs? Come talk to us on [Gitter](https://gitter.im/FreeCodeCamp/DevDocs) :)
docs/scraper-reference.mdrenamed from docs/Scraper-Reference.md+41−0
@@ -184,3 +184,44 @@ More information about how filters work is available on the [Filter Reference](. · 183 unmodified lines
184 Overrides the `:title` option for the root page only.184 Overrides the `:title` option for the root page only.
185 185
186 _Note: this filter is disabled by default._186 _Note: this filter is disabled by default._
187
188## Keeping scrapers up-to-date
189
190In 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
192To 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)
lib/app.rb+42−24
@@ -37,6 +37,9 @@ class App < Sinatra::Application · 36 unmodified lines
37 37
38 set :csp, false38 set :csp, false
39 39
40 require 'docs'
41 Docs.generate_manifest
42
40 Dir[docs_path, root.join(assets_prefix, '*/')].each do |path|43 Dir[docs_path, root.join(assets_prefix, '*/')].each do |path|
41 sprockets.append_path(path)44 sprockets.append_path(path)
42 end45 end
@@ -50,6 +53,11 @@ class App < Sinatra::Application · 7 unmodified lines
50 end53 end
51 54
52 configure :test, :development do55 configure :test, :development do
56 require 'thor'
57 load 'tasks/sprites.thor'
58
59 SpritesCLI.new.invoke(:generate)
60
53 require 'active_support/per_thread_registry'61 require 'active_support/per_thread_registry'
54 require 'active_support/cache'62 require 'active_support/cache'
55 sprockets.cache = ActiveSupport::Cache.lookup_store :file_store, root.join('tmp', 'cache', 'assets', environment.to_s)63 sprockets.cache = ActiveSupport::Cache.lookup_store :file_store, root.join('tmp', 'cache', 'assets', environment.to_s)
@@ -192,35 +200,45 @@ class App < Sinatra::Application · 136 unmodified lines
192 request.query_string.empty? ? nil : "?#{request.query_string}"200 request.query_string.empty? ? nil : "?#{request.query_string}"
193 end201 end
194 202
195 def manifest_asset_urls203 def service_worker_asset_urls
196 @@manifest_asset_urls ||= [204 @@service_worker_asset_urls ||= [
197 javascript_path('application', asset_host: false),205 javascript_path('application', asset_host: false),
198 stylesheet_path('application'),206 stylesheet_path('application'),
199 image_path('docs-1.png'),207 image_path('sprites/docs.png'),
200 image_path('docs-1@2x.png'),208 image_path('sprites/docs@2x.png'),
201 image_path('docs-2.png'),209 asset_path('docs.js'),
202 image_path('docs-2@2x.png'),210 App.production? ? nil : javascript_path('debug'),
203 asset_path('docs.js')211 ].compact
204 ]
205 end212 end
206 213
207 def app_size214 # Returns a cache name for the service worker to use which changes if any of the assets changes
208 @app_size ||= memoized_cookies['size'].nil? ? '20rem' : "#{memoized_cookies['size']}px"215 # When a manifest exist, this name is only created once based on the asset manifest because it never changes without a server restart
209 end216 # 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
210 222
211 def app_layout223 digest = Sprockets::Manifest
212 memoized_cookies['layout']224 .new(nil, App.assets_manifest_path)
213 end225 .files
226 .values
227 .map {|file| file["digest"]}
228 .join
214 229
215 def app_theme230 return @@service_worker_cache_name ||= Digest::MD5.hexdigest(digest)
216 @app_theme ||= memoized_cookies['dark'].nil? ? 'default' : 'dark'231 else
217 end232 paths = App.sprockets
233 .each_file
234 .to_a
235 .reject {|file| file.start_with?(App.docs_path)}
218 236
219 def dark_theme?237 return App.sprockets.pack_hexdigest(App.sprockets.files_digest(paths))
220 app_theme == 'dark'238 end
221 end239 end
222 240
223 def redirect_via_js(path) # courtesy of HTML5 App Cache241 def redirect_via_js(path)
224 response.set_cookie :initial_path, value: path, expires: Time.now + 15, path: '/'242 response.set_cookie :initial_path, value: path, expires: Time.now + 15, path: '/'
225 redirect '/', 302243 redirect '/', 302
226 end244 end
@@ -243,15 +261,15 @@ class App < Sinatra::Application · 16 unmodified lines
243 end261 end
244 end262 end
245 263
246 get '/manifest.appcache' do264 get '/service-worker.js' do
247 content_type 'text/cache-manifest'265 content_type 'application/javascript'
248 expires 0, :'no-cache'266 expires 0, :'no-cache'
249 erb :manifest267 erb :'service-worker.js'
250 end268 end
251 269
252 get '/' do270 get '/' do
253 return redirect "/#q=#{params[:q]}" if params[:q]271 return redirect "/#q=#{params[:q]}" if params[:q]
254 return redirect '/' unless request.query_string.empty? # courtesy of HTML5 App Cache272 return redirect '/' unless request.query_string.empty?
255 response.headers['Content-Security-Policy'] = settings.csp if settings.csp273 response.headers['Content-Security-Policy'] = settings.csp if settings.csp
256 erb :index274 erb :index
257 end275 end
lib/docs/core/doc.rb+99−1
@@ -152,7 +152,6 @@ module Docs · 151 unmodified lines
152 end152 end
153 end153 end
154 154
155
156 def initialize155 def initialize
157 raise NotImplementedError, "#{self.class} is an abstract class and cannot be instantiated." if self.class.abstract156 raise NotImplementedError, "#{self.class} is an abstract class and cannot be instantiated." if self.class.abstract
158 end157 end
@@ -164,5 +163,104 @@ module Docs · 5 unmodified lines
164 def build_pages(&block)163 def build_pages(&block)
165 raise NotImplementedError164 raise NotImplementedError
166 end165 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
167 end265 end
168end266end
lib/docs/core/scrapers/file_scraper.rb+2−2
@@ -29,7 +29,7 @@ module Docs · 28 unmodified lines
29 29
30 def request_one(url)30 def request_one(url)
31 assert_source_directory_exists31 assert_source_directory_exists
32 Response.new read_file(url_to_path(url)), URL.parse(url)32 Response.new read_file(File.join(source_directory, url_to_path(url))), URL.parse(url)
33 end33 end
34 34
35 def request_all(urls)35 def request_all(urls)
@@ -50,7 +50,7 @@ module Docs · 14 unmodified lines
50 end50 end
51 51
52 def read_file(path)52 def read_file(path)
53 File.read(File.join(source_directory, path))53 File.read(path)
54 rescue54 rescue
55 instrument 'warn.doc', msg: "Failed to open file: #{path}"55 instrument 'warn.doc', msg: "Failed to open file: #{path}"
56 nil56 nil
lib/docs/filters/cmake/entries.rb+1−0
@@ -20,6 +20,7 @@ module Docs · 19 unmodified lines
20 'prop_sf' => 'Properties: Source Files',20 'prop_sf' => 'Properties: Source Files',
21 'prop_test' => 'Properties: Tests',21 'prop_test' => 'Properties: Tests',
22 'prop_tgt' => 'Properties: Targets',22 'prop_tgt' => 'Properties: Targets',
23 'envvar' => 'Environment Variables',
23 'variable' => 'Variables' }24 'variable' => 'Variables' }
24 25
25 def get_name26 def get_name
lib/docs/filters/composer/clean_html.rb+35−0
@@ -0,0 +1,35 @@
1module Docs
2 class Composer
3 class CleanHtmlFilter < Filter
4 def call
5 # Remove unneeded elements
6 css('#searchbar, .toc, .fork-and-edit, .anchor').remove
7
8 # Fix the home page titles
9 if subpath == ''
10 css('h1').each do |node|
11 node.name = 'h2'
12 end
13
14 # Add a main title before the first subtitle
15 at_css('h2').before('<h1>Composer</h1>')
16 end
17
18 # Code blocks
19 css('pre').each do |node|
20 code = node.at_css('code[class]')
21
22 unless code.nil?
23 node['data-language'] = 'javascript' if code['class'].include?('javascript')
24 node['data-language'] = 'php' if code['class'].include?('php')
25 end
26
27 node.content = node.content.strip
28 node.remove_attribute('class')
29 end
30
31 doc
32 end
33 end
34 end
35end
lib/docs/filters/composer/entries.rb+36−0
@@ -0,0 +1,36 @@
1module Docs
2 class Composer
3 class EntriesFilter < Docs::EntriesFilter
4 def get_name
5 title = at_css('h1').content
6 title = "#{Integer(subpath[1]) + 1}. #{title}" if type == 'Book'
7 title
8 end
9
10 def get_type
11 return 'Articles' if subpath.start_with?('articles/')
12 'Book'
13 end
14
15 def additional_entries
16 entries = []
17
18 if subpath == '04-schema.md' # JSON Schema
19 css('h3').each do |node|
20 name = node.content.strip
21 name.remove!(' (root-only)')
22 entries << [name, node['id'], 'JSON Schema']
23 end
24 end
25
26 if subpath == '06-config.md' # Composer config
27 css('h2').each do |node|
28 entries << [node.content.strip, node['id'], 'Configuration Options']
29 end
30 end
31
32 entries
33 end
34 end
35 end
36end
lib/docs/filters/cypress/clean_html.rb+26−0
@@ -0,0 +1,26 @@
1# frozen_string_literal: true
2
3module Docs
4 class Cypress
5 class CleanHtmlFilter < Filter
6 def call
7 article_div = at_css('#article > div')
8 @doc = article_div unless article_div.nil?
9
10 header = at_css('h1.article-title')
11 doc.prepend_child(header) unless header.nil?
12
13 css('.article-edit-link').remove
14 css('.article-footer').remove
15 css('.article-footer-updated').remove
16
17 css('pre').each do |node|
18 node.content = node.content
19 node['data-language'] = 'javascript'
20 end
21
22 doc
23 end
24 end
25 end
26end
lib/docs/filters/cypress/entries.rb+34−0
@@ -0,0 +1,34 @@
1# frozen_string_literal: true
2
3module Docs
4 class Cypress
5 class EntriesFilter < Docs::EntriesFilter
6 SECTIONS = %w[
7 commands
8 core-concepts
9 cypress-api
10 events
11 getting-started
12 guides
13 overview
14 plugins
15 references
16 utilities
17 ].freeze
18
19 def get_name
20 at_css('h1.article-title').content.strip
21 end
22
23 def get_type
24 path = context[:url].path
25
26 SECTIONS.each do |section|
27 if path.match?("/#{section}/")
28 return section.split('-').map(&:capitalize).join(' ')
29 end
30 end
31 end
32 end
33 end
34end
lib/docs/filters/elixir/clean_html.rb+5−0
@@ -57,6 +57,11 @@ module Docs · 56 unmodified lines
57 node.parent.after(node)57 node.parent.after(node)
58 end58 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 css('pre').each do |node|65 css('pre').each do |node|
61 node['data-language'] = 'elixir'66 node['data-language'] = 'elixir'
62 node.content = node.content67 node.content = node.content
lib/docs/filters/go/clean_html.rb+1−0
@@ -17,6 +17,7 @@ module Docs · 16 unmodified lines
17 css('#plusone', '#nav', '.pkgGopher', '#footer', '.collapsed', '.permalink', '#pkg-callgraph').remove17 css('#plusone', '#nav', '.pkgGopher', '#footer', '.collapsed', '.permalink', '#pkg-callgraph').remove
18 18
19 css('span[style]', '.toggleVisible', '.expanded', 'div.toggle').each do |node|19 css('span[style]', '.toggleVisible', '.expanded', 'div.toggle').each do |node|
20 node.first_element_child['id'] = node['id'] if node['id']
20 node.before(node.children).remove21 node.before(node.children).remove
21 end22 end
22 23
lib/docs/filters/homebrew/clean_html.rb+5−0
@@ -4,6 +4,11 @@ module Docs · 3 unmodified lines
4 def call4 def call
5 css('hr')5 css('hr')
6 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
7 css('div.highlighter-rouge').each do |node|12 css('div.highlighter-rouge').each do |node|
8 lang = node['class'][/language-(\w+)/, 1]13 lang = node['class'][/language-(\w+)/, 1]
9 node['data-language'] = lang if lang14 node['data-language'] = lang if lang
lib/docs/filters/homebrew/entries.rb+3−1
@@ -2,7 +2,8 @@ module Docs · 1 unmodified line
2 class Homebrew2 class Homebrew
3 class EntriesFilter < Docs::EntriesFilter3 class EntriesFilter < Docs::EntriesFilter
4 def get_name4 def get_name
5 name = at_css('h1').content.strip5 header = at_css('h1')
6 name = header.nil? ? current_url.normalized_path[1..-1].gsub(/-/, ' ') : header.content.strip
6 name.remove! %r{\(.*}7 name.remove! %r{\(.*}
7 name8 name
8 end9 end
@@ -16,6 +17,7 @@ module Docs · 7 unmodified lines
16 Python-for-Formula-Authors17 Python-for-Formula-Authors
17 Migrating-A-Formula-To-A-Tap18 Migrating-A-Formula-To-A-Tap
18 Rename-A-Formula19 Rename-A-Formula
20 Building-Against-Non-Homebrew-Dependencies
19 How-to-Create-and-Maintain-a-Tap21 How-to-Create-and-Maintain-a-Tap
20 Brew-Test-Bot22 Brew-Test-Bot
21 Prose-Style-Guidelines)23 Prose-Style-Guidelines)
lib/docs/filters/kotlin/clean_html.rb+7−0
@@ -46,6 +46,13 @@ module Docs · 45 unmodified lines
46 parent.content = parent.content46 parent.content = parent.content
47 parent['data-language'] = 'kotlin'47 parent['data-language'] = 'kotlin'
48 end48 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 end56 end
50 end57 end
51 end58 end
lib/docs/filters/kotlin/entries.rb+3−1
@@ -5,7 +5,9 @@ module Docs · 4 unmodified lines
5 if subpath.start_with?('api')5 if subpath.start_with?('api')
6 breadcrumbs[1..-1].join('.')6 breadcrumbs[1..-1].join('.')
7 else7 else
8 (at_css('h1') || at_css('h2')).content8 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 end11 end
10 end12 end
11 13
lib/docs/filters/octave/clean_html.rb+46−0
@@ -0,0 +1,46 @@
1module Docs
2 class Octave
3 class CleanHtmlFilter < Filter
4 def call
5 root_page? ? root : other
6 doc
7 end
8
9 def root
10 @doc = at_css('.contents')
11 end
12
13 def other
14 css('.header', 'hr').remove
15
16 css('.footnote > h3').each do |node|
17 node.name = 'h5'
18 end
19
20 at_css('h2, h3, h4').name = 'h1'
21
22 css('.example').each do |node|
23 node.name = 'pre'
24 node['data-language'] = 'matlab'
25 node.content = node.content.strip
26 end
27
28 css('a[name] + dl, a[name] + h4').each do |node|
29 node['id'] = node.previous_element['name']
30 end
31
32 css('dt > a[name]', 'th > a[name]').each do |node|
33 node.parent['id'] = node['name']
34 node.parent.content = node.parent.content.strip
35 end
36
37 css('h5 > a').each do |node|
38 node.parent['id'] = node['name']
39 node.parent.content = node.content
40 end
41
42 xpath('//td[text()=" " and not(descendant::*)]').remove
43 end
44 end
45 end
46end
lib/docs/filters/octave/entries.rb+22−0
@@ -0,0 +1,22 @@
1module Docs
2 class Octave
3 class EntriesFilter < Docs::EntriesFilter
4 def get_name
5 at_css('h1').content.sub(/(A?[0-9.]+ )/, '')
6 end
7
8 def get_type
9 return 'Manual: Appendices' if name.start_with?('Appendix')
10 return 'Manual: Indexes' if name.end_with?('Index')
11 'Manual'
12 end
13
14 def additional_entries
15 css('dl:not([compact]) > dt').each_with_object [] do |node, entries|
16 name = node.content.gsub(/[A-z0-9\,… ]*\=/, '').strip.split(' ')[0]
17 entries << [name, node['id'], 'Functions'] unless node['id'] =~ /-\d+\Z/
18 end
19 end
20 end
21 end
22end
lib/docs/filters/pandas/entries.rb+5−3
@@ -2,8 +2,10 @@ module Docs · 1 unmodified line
2 class Pandas2 class Pandas
3 class EntriesFilter < Docs::EntriesFilter3 class EntriesFilter < Docs::EntriesFilter
4 def get_name4 def get_name
5 if subpath.start_with?('generated')5 if subpath.start_with?('generated') || (subpath.include?('reference') && !subpath.include?('reference/index'))
6 name = at_css('dt').content.strip6 name_node = at_css('dt')
7 name_node = at_css('h1') if name_node.nil?
8 name = name_node.content.strip
7 name.sub! %r{\(.*}, '()'9 name.sub! %r{\(.*}, '()'
8 name.remove! %r{\s=.*}10 name.remove! %r{\s=.*}
9 name.remove! %r{\A(class(method)?) (pandas\.)?}11 name.remove! %r{\A(class(method)?) (pandas\.)?}
@@ -16,7 +18,7 @@ module Docs · 6 unmodified lines
16 end18 end
17 19
18 def get_type20 def get_type
19 if subpath.start_with?('generated')21 if subpath.start_with?('generated') || (subpath.include?('reference') && !subpath.include?('reference/index'))
20 css('.toctree-l2.current > a').last.content.remove(/\s\(.+?\)/)22 css('.toctree-l2.current > a').last.content.remove(/\s\(.+?\)/)
21 else23 else
22 'Manual'24 'Manual'
lib/docs/filters/pony/clean_html.rb+16−0
@@ -0,0 +1,16 @@
1module Docs
2 class Pony
3 class CleanHtmlFilter < Filter
4 def call
5 css('.headerlink').remove
6 css('hr').remove
7
8 css('pre').each do |node|
9 node.content = node.content
10 end
11
12 doc
13 end
14 end
15 end
16end
lib/docs/filters/pony/entries.rb+35−0
@@ -0,0 +1,35 @@
1module Docs
2 class Pony
3 class EntriesFilter < Docs::EntriesFilter
4 def get_name
5 title = context[:html_title].sub(/ - .*/, '').split(' ').last
6 title = "1. #{type}" if title == 'Package'
7 title
8 end
9
10 def get_type
11 subpath.split(/-([^a-z])/)[0][0..-1].sub('-', '/')
12 end
13
14 def additional_entries
15 return [] if root_page? || name.start_with?("1. ")
16
17 entries = []
18
19 css('h3').each do |node|
20 member_name = node.content
21
22 is_field = member_name.start_with?('let ')
23 member_name = member_name[4..-1] if is_field
24
25 member_name = member_name.scan(/^([a-zA-Z0-9_]+)/)[0][0]
26 member_name += '()' unless is_field
27
28 entries << ["#{name}.#{member_name}", node['id']]
29 end
30
31 entries
32 end
33 end
34 end
35end
lib/docs/filters/pygame/clean_html.rb+1−1
@@ -85,7 +85,7 @@ module Docs · 84 unmodified lines
85 if initial_header.start_with?(@section)85 if initial_header.start_with?(@section)
86 sig.content = @section + '.' + sig.text86 sig.content = @section + '.' + sig.text
87 end87 end
88 # seperate the signatures on different lines.88 # separate the signatures on different lines.
89 header.add_child "<br>"89 header.add_child "<br>"
90 end90 end
91 end91 end
lib/docs/filters/python/entries_v2.rb+1−7
@@ -57,16 +57,10 @@ module Docs · 56 unmodified lines
57 clean_id_attributes57 clean_id_attributes
58 entries = []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 entries << [node['id'], node['id']]61 entries << [node['id'], node['id']]
62 end62 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 css('.function > dt[id]', '.method > dt[id]', '.staticmethod > dt[id]', '.classmethod > dt[id]').each do |node|64 css('.function > dt[id]', '.method > dt[id]', '.staticmethod > dt[id]', '.classmethod > dt[id]').each do |node|
71 entries << [node['id'] + '()', node['id']]65 entries << [node['id'] + '()', node['id']]
72 end66 end
lib/docs/filters/python/entries_v3.rb+3−7
@@ -27,6 +27,7 @@ module Docs · 26 unmodified lines
27 27
28 def get_type28 def get_type
29 return 'Logging' if slug.start_with? 'library/logging'29 return 'Logging' if slug.start_with? 'library/logging'
30 return 'Asynchronous I/O' if slug.start_with? 'library/asyncio'
30 31
31 type = at_css('.related a[accesskey="U"]').content32 type = at_css('.related a[accesskey="U"]').content
32 33
@@ -47,6 +48,7 @@ module Docs · 14 unmodified lines
47 end48 end
48 49
49 def include_default_entry?50 def include_default_entry?
51 return true if slug == 'library/asyncio'
50 !at_css('.body > .section:only-child > .toctree-wrapper:last-child') && !type.in?(%w(Superseded))52 !at_css('.body > .section:only-child > .toctree-wrapper:last-child') && !type.in?(%w(Superseded))
51 end53 end
52 54
@@ -55,16 +57,10 @@ module Docs · 2 unmodified lines
55 clean_id_attributes57 clean_id_attributes
56 entries = []58 entries = []
57 59
58 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|
59 entries << [node['id'], node['id']]61 entries << [node['id'], node['id']]
60 end62 end
61 63
62 css('.data > dt[id]').each do |node|
63 if node['id'].split('.').last.upcase! # skip constants
64 entries << [node['id'], node['id']]
65 end
66 end
67
68 css('.function > dt[id]', '.method > dt[id]', '.staticmethod > dt[id]', '.classmethod > dt[id]').each do |node|64 css('.function > dt[id]', '.method > dt[id]', '.staticmethod > dt[id]', '.classmethod > dt[id]').each do |node|
69 entries << [node['id'] + '()', node['id']]65 entries << [node['id'] + '()', node['id']]
70 end66 end
lib/docs/filters/qt/clean_html.rb+1−0
@@ -8,6 +8,7 @@ module Docs · 7 unmodified lines
8 # QML property/method header8 # QML property/method header
9 css('.qmlproto').each do |node|9 css('.qmlproto').each do |node|
10 id = node.at_css('tr')['id']10 id = node.at_css('tr')['id']
11 id = node.at_css('a')['name'] if id.blank?
11 node.inner_html = node.at_css('td').inner_html12 node.inner_html = node.at_css('td').inner_html
12 node.name = 'h3'13 node.name = 'h3'
13 node['id'] = id14 node['id'] = id
lib/docs/filters/qt/entries.rb+1−0
@@ -111,6 +111,7 @@ module Docs · 110 unmodified lines
111 css('.qmlproto').each do |node|111 css('.qmlproto').each do |node|
112 title = node.content.strip112 title = node.content.strip
113 id = node.at_css('tr')['id']113 id = node.at_css('tr')['id']
114 id = node.at_css('a')['name'] if id.blank?
114 115
115 # Remove options116 # Remove options
116 title.remove!(%r{^\[.*\] })117 title.remove!(%r{^\[.*\] })
lib/docs/filters/ramda/clean_html.rb+1−1
@@ -11,7 +11,7 @@ module Docs · 10 unmodified lines
11 css('.card').each do |card|11 css('.card').each do |card|
12 title = card.at_css('h2')12 title = card.at_css('h2')
13 added_in = card.at_css('small')13 added_in = card.at_css('small')
14 added_in.parent = title14 added_in.parent = title if added_in.present?
15 end15 end
16 16
17 css('.params').each do |node|17 css('.params').each do |node|
lib/docs/filters/rxjs/clean_html.rb+139−0
@@ -0,0 +1,139 @@
1module Docs
2 class Rxjs
3 class CleanHtmlFilter < Filter
4 def call
5 if root_page?
6 css('.card-container').remove
7 at_css('h1').content = 'RxJS Documentation'
8 end
9
10 if at_css('h1').nil?
11 title = subpath.rpartition('/').last.titleize
12 doc.prepend_child("<h1>#{title}</h1>")
13 end
14
15 css('br', 'hr', '.material-icons', '.header-link', '.breadcrumb').remove
16
17 css('.content', 'article', '.api-header', 'section', '.instance-member').each do |node|
18 node.before(node.children).remove
19 end
20
21 css('label', 'h2 > em', 'h3 > em').each do |node|
22 node.name = 'code'
23 end
24
25 css('h1 + code').each do |node|
26 node.before('<p></p>')
27 while node.next_element.name == 'code'
28 node.previous_element << ' '
29 node.previous_element << node.next_element
30 end
31 node.previous_element.prepend_child(node)
32 end
33
34 css('td h3', '.l-sub-section > h3', '.alert h3', '.row-margin > h3', '.api-heading ~ h3', '.api-heading + h2', '.metadata-member h3').each do |node|
35 node.name = 'h4'
36 end
37
38 css('.l-sub-section', '.alert', '.banner').each do |node|
39 node.name = 'blockquote'
40 end
41
42 css('.file').each do |node|
43 node.content = node.content.strip
44 end
45
46 css('.filetree .children').each do |node|
47 node.css('.file').each do |n|
48 n.content = " #{n.content}"
49 end
50 end
51
52 css('.filetree').each do |node|
53 node.content = node.css('.file').map(&:inner_html).join("\n")
54 node.name = 'pre'
55 node.remove_attribute('class')
56 end
57
58 css('pre').each do |node|
59 node.content = node.content.strip
60
61 node['data-language'] = 'typescript' if node['path'].try(:ends_with?, '.ts')
62 node['data-language'] = 'html' if node['path'].try(:ends_with?, '.html')
63 node['data-language'] = 'css' if node['path'].try(:ends_with?, '.css')
64 node['data-language'] = 'js' if node['path'].try(:ends_with?, '.js')
65 node['data-language'] = 'json' if node['path'].try(:ends_with?, '.json')
66 node['data-language'] = node['language'].sub(/\Ats/, 'typescript').strip if node['language']
67 node['data-language'] ||= 'typescript' if node.content.start_with?('@')
68
69 node.before(%(<div class="pre-title">#{node['title']}</div>)) if node['title']
70
71 if node['class'] && node['class'].include?('api-heading')
72 node.name = 'h3'
73
74 unless node.ancestors('.instance-method').empty?
75 matches = node.inner_html.scan(/([^(& ]+)[(&]/)
76
77 unless matches.empty? || matches[0][0] == 'constructor'
78 node['name'] = matches[0][0]
79 node['id'] = node['name'].downcase + '-'
80 end
81 end
82
83 node.inner_html = "<code>#{node.inner_html}</code>"
84 end
85
86 node.remove_attribute('path')
87 node.remove_attribute('region')
88 node.remove_attribute('linenums')
89 node.remove_attribute('title')
90 node.remove_attribute('language')
91 node.remove_attribute('hidecopy')
92 node.remove_attribute('class')
93 end
94
95 css('td > .overloads').each do |node|
96 node.replace node.at_css('.detail-contents')
97 end
98
99 css('td.short-description p').each do |node|
100 signature = node.parent.parent.next_element.at_css('h3[id]')
101 signature.after(node) unless signature.nil?
102 end
103
104 css('.method-table').each do |node|
105 node.replace node.at_css('tbody')
106 end
107
108 css('.api-body > table > caption').each do |node|
109 node.name = 'center'
110 lift_out_of_table node
111 end
112
113 css('.api-body > table > tbody > tr:not([class]) > td > *').each do |node|
114 lift_out_of_table node
115 end
116
117 css('.api-body > table').each do |node|
118 node.remove if node.content.strip.blank?
119 end
120
121 css('h1[class]').remove_attr('class')
122 css('table[class]').remove_attr('class')
123 css('table[width]').remove_attr('width')
124 css('tr[style]').remove_attr('style')
125
126 css('code code').each do |node|
127 node.before(node.children).remove
128 end
129
130 doc
131 end
132
133 def lift_out_of_table(node)
134 table = node.ancestors('table').first
135 table.previous_element.after(node)
136 end
137 end
138 end
139end
lib/docs/filters/rxjs/entries.rb+29−0
@@ -0,0 +1,29 @@
1module Docs
2 class Rxjs
3 class EntriesFilter < Docs::EntriesFilter
4 def get_name
5 title = at_css('h1')
6 name = title.nil? ? subpath.rpartition('/').last.titleize : title.content
7 name.prepend "#{$1}. " if subpath =~ /\-pt(\d+)/
8 name += '()' unless at_css('.api-type-label.function').nil?
9 name
10 end
11
12 def get_type
13 if slug.start_with?('guide')
14 'Guide'
15 elsif slug.start_with?('api/')
16 slug.split('/').second
17 else
18 'Miscellaneous'
19 end
20 end
21
22 def additional_entries
23 css('h3[id]').map do |node|
24 ["#{name}.#{node['name']}()", node['id']]
25 end
26 end
27 end
28 end
29end
lib/docs/filters/salt_stack/clean_html.rb+27−0
@@ -0,0 +1,27 @@
1module Docs
2 class SaltStack
3 class CleanHtmlFilter < Filter
4 def call
5 if root_page?
6 doc.inner_html = '<h1>SaltStack</h1>'
7 return doc
8 end
9
10 css('.headerlink').remove
11
12 css('div[class^="highlight-"]').each do |node|
13 node.name = 'pre'
14 node['data-language'] = node['class'].scan(/highlight-([a-z]+)/i)[0][0]
15 node.content = node.content.strip
16 end
17
18 css('.function > dt').each do |node|
19 node.name = 'h3'
20 node.content = node.content
21 end
22
23 doc
24 end
25 end
26 end
27end
lib/docs/filters/salt_stack/entries.rb+38−0
@@ -0,0 +1,38 @@
1module Docs
2 class SaltStack
3 class EntriesFilter < Docs::EntriesFilter
4 SALT_REF_RGX = /salt\.([^\.]+)\.([^\s]+)/
5
6 def get_name
7 header = at_css('h1').content
8
9 ref_match = SALT_REF_RGX.match(header)
10 if ref_match
11 ns, mod = ref_match.captures
12 "#{ns}.#{mod}"
13 else
14 header
15 end
16 end
17
18 def get_type
19 slug.split('/', 3)[1]
20 end
21
22 def include_default_entry?
23 slug.split('/').last.start_with? 'salt'
24 end
25
26 def additional_entries
27 entries = []
28
29 css('.function > h3').each do |node|
30 name = node.content.remove('salt.').split('(')[0] + '()'
31 entries << [name, node['id']]
32 end
33
34 entries
35 end
36 end
37 end
38end
lib/docs/filters/scala/clean_html.rb+109−0
@@ -0,0 +1,109 @@
1module Docs
2 class Scala
3 class CleanHtmlFilter < Filter
4 def call
5 @doc = at_css('#content')
6
7 always
8 add_title
9
10 doc
11 end
12
13 def always
14 # Remove deprecated sections
15 css('.members').each do |members|
16 header = members.at_css('h3')
17 members.remove if header.text.downcase.include? 'deprecate'
18 end
19
20 css('#mbrsel, #footer').remove
21
22 css('.diagram-container').remove
23 css('.toggleContainer > .toggle').each do |node|
24 title = node.at_css('span')
25 next if title.nil?
26
27 content = node.at_css('.hiddenContent')
28 next if content.nil?
29
30 title.name = 'dt'
31
32 content.remove_attribute('class')
33 content.remove_attribute('style')
34 content.name = 'dd'
35
36 attributes = at_css('.attributes')
37 unless attributes.nil?
38 title.parent = attributes
39 content.parent = attributes
40 end
41 end
42
43 signature = at_css('#signature')
44 signature.replace "<h2 id=\"signature\">#{signature.inner_html}</h2>"
45
46 css('div.members > h3').each do |node|
47 node.name = 'h2'
48 end
49
50 css('div.members > ol').each do |list|
51 list.css('li').each do |li|
52 h3 = doc.document.create_element 'h3'
53 h3['id'] = li['name'].rpartition('#').last unless li['name'].nil?
54
55 li.prepend_child h3
56 li.css('.shortcomment').remove
57
58 modifier = li.at_css('.modifier_kind')
59 modifier.parent = h3 unless modifier.nil?
60
61 kind = li.at_css('.modifier_kind .kind')
62 kind.content = kind.content + ' ' unless kind.nil?
63
64 symbol = li.at_css('.symbol')
65 symbol.parent = h3 unless symbol.nil?
66
67 li.swap li.children
68 end
69
70 list.swap list.children
71 end
72
73 css('.fullcomment pre, .fullcommenttop pre').each do |pre|
74 pre['data-language'] = 'scala'
75 pre.content = pre.content
76 end
77
78 # Sections of the documentation which do not seem useful
79 %w(#inheritedMembers #groupedMembers .permalink .hiddenContent .material-icons).each do |selector|
80 css(selector).remove
81 end
82
83 # Things that are not shown on the site, like deprecated members
84 css('li[visbl=prt]').remove
85 end
86
87 def add_title
88 css('.permalink').remove
89
90 definition = at_css('#definition')
91 return if definition.nil?
92
93 type_full_name = {a: 'Annotation', c: 'Class', t: 'Trait', o: 'Object', p: 'Package'}
94 type = type_full_name[definition.at_css('.big-circle').text.to_sym]
95 name = CGI.escapeHTML definition.at_css('h1').text
96
97 package = definition.at_css('#owner').text rescue ''
98 package = package + '.' unless name.empty? || package.empty?
99
100 other = definition.at_css('.morelinks').dup
101 other_content = other ? "<h3>#{other.to_html}</h3>" : ''
102
103 title_content = root_page? ? 'Package root' : "#{type} #{package}#{name}".strip
104 title = "<h1>#{title_content}</h1>"
105 definition.replace title + other_content
106 end
107 end
108 end
109end
lib/docs/filters/scala/entries.rb+103−0
@@ -0,0 +1,103 @@
1module Docs
2 class Scala
3 class EntriesFilter < Docs::EntriesFilter
4 REPLACEMENTS = {
5 '$eq' => '=',
6 '$colon' => ':',
7 '$less' => '<',
8 }
9
10 def get_name
11 if is_package?
12 symbol = at_css('#definition h1')
13 symbol ? symbol.text.gsub(/\W+/, '') : "package"
14 else
15 name = slug.split('/').last
16
17 # Some objects have inner objects, show ParentObject$.ChildObject$ instead of ParentObject$$ChildObject$
18 name = name.gsub('$$', '$.')
19
20 REPLACEMENTS.each do |key, value|
21 name = name.gsub(key, value)
22 end
23
24 # If a dollar sign is used as separator between two characters, replace it with a dot
25 name.gsub(/([^$.])\$([^$.])/, '\1.\2')
26 end
27 end
28
29 def get_type
30 # if this entry is for a package, we group the package under the parent package
31 if is_package?
32 parent_package
33 # otherwise, group it under the regular package name
34 else
35 package_name
36 end
37 end
38
39 def include_default_entry?
40 true
41 end
42
43 def additional_entries
44 entries = []
45
46 full_name = "#{type}.#{name}".remove('$')
47 css(".members li[name^=\"#{full_name}\"]").each do |node|
48 # Ignore packages
49 kind = node.at_css('.modifier_kind > .kind')
50 next if !kind.nil? && kind.content == 'package'
51
52 # Ignore deprecated members
53 next unless node.at_css('.symbol > .name.deprecated').nil?
54
55 id = node['name'].rpartition('#').last
56 member_name = node.at_css('.name')
57
58 # Ignore members only existing of hashtags, we can't link to that
59 next if member_name.nil? || member_name.content.strip.remove('#').blank?
60
61 member = "#{name}.#{member_name.content}()"
62 entries << [member, id]
63 end
64
65 entries
66 end
67
68 private
69
70 # For the package name, we use the slug rather than parsing the package
71 # name from the HTML because companion object classes may be broken out into
72 # their own entries (by the source documentation). When that happens,
73 # we want to group these classes (like `scala.reflect.api.Annotations.Annotation`)
74 # under the package name, and not the fully-qualfied name which would
75 # include the companion object.
76 def package_name
77 name = package_drop_last(slug_parts)
78 name.empty? ? '_root_' : name
79 end
80
81 def parent_package
82 parent = package_drop_last(package_name.split('.'))
83 parent.empty? ? '_root_' : parent
84 end
85
86 def package_drop_last(parts)
87 parts[0...-1].join('.')
88 end
89
90 def slug_parts
91 slug.split('/')
92 end
93
94 def owner
95 at_css('#owner')
96 end
97
98 def is_package?
99 slug.ends_with?('index') || slug.ends_with?('package')
100 end
101 end
102 end
103end
lib/docs/filters/trio/clean_html.rb+58−0
@@ -0,0 +1,58 @@
1module Docs
2 class Trio
3 class CleanHtmlFilter < Filter
4 def call
5 @doc = at_css('div[role="main"]')
6
7 css('.section, [itemprop=articleBody]').each do |node|
8 node.replace node.children
9 end
10
11 css('.headerlink').remove
12
13 css('dt').each do |node|
14 node.name = 'h3'
15
16 if node.parent.classes.include? 'field-list'
17 node.name = 'h4'
18 node['style'] = 'margin: 0'
19
20 if node.text == 'Parameters' or node.text == 'Raises'
21 node.next_element.css('strong').each do |n|
22 n.name = 'code'
23 end
24 end
25 else
26 code = doc.document.create_element 'code'
27
28 if em = node.at_css('.property')
29 code.inner_html = "<em>#{em.text.strip}</em> "
30 em.remove
31 end
32
33 code.inner_html += node.inner_text.strip
34 node.inner_html = code
35 end
36 end
37
38 css('pre').each do |node|
39 node.content = node.content.strip
40
41 classes = node.parent.parent.classes
42 if classes.include? 'highlight-python3'
43 node['data-language'] = 'python'
44 end
45
46 node.parent.parent.replace(node)
47 end
48
49 css('.admonition').each do |node|
50 node.name = 'blockquote'
51 node.at_css('.admonition-title').name = 'h4'
52 end
53
54 doc
55 end
56 end
57 end
58end
lib/docs/filters/trio/entries.rb+48−0
@@ -0,0 +1,48 @@
1module Docs
2 class Trio
3 class EntriesFilter < Docs::EntriesFilter
4 def get_name
5 at_css('h1').text[0...-1]
6 end
7
8 def get_type
9 at_css('h1').text[0...-1]
10 end
11
12 def additional_entries
13 css('.descname').each_with_object [] do |node, entries|
14 name = node.text
15 if node.previous.classes.include? 'descclassname'
16 name = node.previous.text + name
17 end
18 name.strip!
19
20 dl = node.parent.parent
21
22 if dl.classes.include?('attribute') \
23 or dl.classes.include?('method') \
24 or dl.classes.include?('data')
25 parent = dl.parent.previous_element
26 cls = ''
27
28 if n = parent.at_css('.descclassname')
29 cls += n.text
30 end
31
32 if n = parent.at_css('.descname')
33 if n.text == "The nursery interface"
34 cls += "Nursery."
35 else
36 cls += n.text + '.'
37 end
38 end
39
40 name = cls + name
41 end
42
43 entries << [name, node.parent['id']]
44 end
45 end
46 end
47 end
48end
lib/docs/filters/vue_router/clean_html.rb+21−0
@@ -0,0 +1,21 @@
1module Docs
2 class VueRouter
3 class CleanHtmlFilter < Filter
4 def call
5 @doc = at_css('.content')
6
7 # Remove unneeded elements
8 css('.bit-sponsor, .header-anchor').remove
9
10 css('.custom-block').each do |node|
11 node.name = 'blockquote'
12
13 title = node.at_css('.custom-block-title')
14 title.name = 'strong' unless title.nil?
15 end
16
17 doc
18 end
19 end
20 end
21end
lib/docs/filters/vue_router/entries.rb+71−0
@@ -0,0 +1,71 @@
1module Docs
2 class VueRouter
3 class EntriesFilter < Docs::EntriesFilter
4 def get_name
5 name = at_css('h1').content
6 name.remove! '# '
7 name
8 end
9
10 def get_type
11 return 'Other Guides' if subpath.start_with?('guide/advanced')
12 return 'Basic Guides' if subpath.start_with?('guide') || subpath.start_with?('installation')
13 'API Reference'
14 end
15
16 def include_default_entry?
17 name != 'API Reference'
18 end
19
20 def additional_entries
21 return [] unless subpath.start_with?('api')
22
23 entries = [
24 ['<router-link>', 'router-link', 'API Reference'],
25 ['<router-view>', 'router-view', 'API Reference'],
26 ['$route', 'the-route-object', 'API Reference'],
27 ['Component Injections', 'component-injections', 'API Reference']
28 ]
29
30 css('h3').each do |node|
31 entry_name = node.content.strip
32
33 # Get the previous h2 title
34 title = node
35 title = title.previous_element until title.name == 'h2'
36 title = title.content.strip
37 title.remove! '# '
38
39 entry_name.remove! '# '
40
41 case title
42 when 'Router Construction Options'
43 entry_name = "RouterOptions.#{entry_name}"
44 when '<router-view> Props'
45 entry_name = "<router-view> `#{entry_name}` prop"
46 when '<router-link> Props'
47 entry_name = "<router-link> `#{entry_name}` prop"
48 when 'Router Instance Methods'
49 entry_name = "#{entry_name}()"
50 end
51
52 entry_name = entry_name.split(' API ')[0] if entry_name.start_with?('v-slot')
53
54 unless title == "Component Injections" || node['id'] == 'route-object-properties'
55 entries << [entry_name, node['id'], 'API Reference']
56 end
57 end
58
59 css('#route-object-properties + ul > li > p:first-child > strong').each do |node|
60 entry_name = node.content.strip
61 id = "route-object-#{entry_name.remove('$route.')}"
62
63 node['id'] = id
64 entries << [entry_name, node['id'], 'API Reference']
65 end
66
67 entries
68 end
69 end
70 end
71end
lib/docs/filters/vuex/clean_html.rb+17−0
@@ -0,0 +1,17 @@
1module Docs
2 class Vuex
3 class CleanHtmlFilter < Filter
4 def call
5 @doc = at_css('.content')
6
7 # Remove video from root page
8 css('a[href="#"]').remove if root_page?
9
10 # Remove unneeded elements
11 css('.header-anchor').remove
12
13 doc
14 end
15 end
16 end
17end
lib/docs/filters/vuex/entries.rb+69−0
@@ -0,0 +1,69 @@
1module Docs
2 class Vuex
3 class EntriesFilter < Docs::EntriesFilter
4 def get_name
5 name = at_css('h1').content
6
7 name.remove! '# '
8
9 # Add index on guides
10 unless subpath.start_with?('api')
11 sidebar_link = at_css('.sidebar-link.active')
12 all_links = css('.sidebar-link:not([href="/"]):not([href="../index"])')
13
14 index = all_links.index(sidebar_link)
15
16 name.prepend "#{index + 1}. " if index
17 end
18
19 name
20 end
21
22 def get_type
23 'Guide'
24 end
25
26 def include_default_entry?
27 name != 'API Reference'
28 end
29
30 def additional_entries
31 return [] unless subpath.start_with?('api')
32
33 entries = [
34 ['Component Binding Helpers', 'component-binding-helpers', 'API Reference'],
35 ['Store', 'vuex-store', 'API Reference'],
36 ]
37
38 css('h3').each do |node|
39 entry_name = node.content.strip
40
41 # Get the previous h2 title
42 title = node
43 title = title.previous_element until title.name == 'h2'
44 title = title.content.strip
45 title.remove! '# '
46
47 entry_name.remove! '# '
48
49 unless entry_name.start_with?('router.')
50 case title
51 when "Vuex.Store Constructor Options"
52 entry_name = "StoreOptions.#{entry_name}"
53 when "Vuex.Store Instance Properties"
54 entry_name = "Vuex.Store.#{entry_name}"
55 when "Vuex.Store Instance Methods"
56 entry_name = "Vuex.Store.#{entry_name}()"
57 when "Component Binding Helpers"
58 entry_name = "#{entry_name}()"
59 end
60 end
61
62 entries << [entry_name, node['id'], 'API Reference']
63 end
64
65 entries
66 end
67 end
68 end
69end
lib/docs/filters/wordpress/clean_html.rb+39−0
@@ -0,0 +1,39 @@
1module Docs
2 class Wordpress
3 class CleanHtmlFilter < Filter
4 def call
5 if root_page?
6 doc.inner_html = '<h1>WordPress</h1>'
7 return doc
8 end
9
10 article = at_css('article[id^="post-"]')
11 @doc = at_css('article[id^="post-"]') unless article.nil?
12
13 css('hr', '.screen-reader-text', '.table-of-contents',
14 '.anchor', '.toc-jump', '.source-code-links', '.user-notes',
15 '.show-more', '.hide-more').remove
16
17 br = /<br\s?\/?>/i
18
19 header = at_css('h1')
20 header.content = header.content.strip
21 doc.prepend_child header
22
23 # Add PHP code highlighting
24 css('pre').each do |node|
25 node['data-language'] = 'php'
26 end
27
28 css('.source-code-container').each do |node|
29 node.name = 'pre'
30 node.inner_html = node.inner_html.gsub(br, "\n")
31 node.content = node.content.strip
32 node['data-language'] = 'php'
33 end
34
35 doc
36 end
37 end
38 end
39end
lib/docs/filters/wordpress/entries.rb+19−0
@@ -0,0 +1,19 @@
1module Docs
2 class Wordpress
3 class EntriesFilter < Docs::EntriesFilter
4 def get_name
5 at_css('.breadcrumbs .trail-end').content
6 end
7
8 def get_type
9 if subpath.starts_with?('classes')
10 'Classes'
11 elsif subpath.starts_with?('hooks')
12 'Hooks'
13 elsif subpath.starts_with?('functions')
14 'Functions'
15 end
16 end
17 end
18 end
19end
lib/docs/scrapers/angular.rb+4−0
@@ -155,6 +155,10 @@ module Docs · 154 unmodified lines
155 end155 end
156 end156 end
157 157
158 def get_latest_version(opts)
159 get_npm_version('@angular/core', opts)
160 end
161
158 private162 private
159 163
160 def parse(response)164 def parse(response)
lib/docs/scrapers/angularjs.rb+16−3
@@ -31,10 +31,14 @@ module Docs · 30 unmodified lines
31 end31 end
32 32
33 options[:only_patterns] = [%r{\Aapi}, %r{\Aguide}]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 options[:attribution] = <<-HTML40 options[:attribution] = <<-HTML
37 &copy; 2010&ndash;2017 Google, Inc.<br>41 &copy; 2010&ndash;2018 Google, Inc.<br>
38 Licensed under the Creative Commons Attribution License 4.0.42 Licensed under the Creative Commons Attribution License 4.0.
39 HTML43 HTML
40 44
@@ -45,8 +49,13 @@ module Docs · 4 unmodified lines
45 capybara.execute_script("return document.querySelector('.side-navigation').innerHTML")49 capybara.execute_script("return document.querySelector('.side-navigation').innerHTML")
46 end50 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 version '1.6' do57 version '1.6' do
49 self.release = '1.6.6'58 self.release = '1.6.9'
50 self.base_url = "https://code.angularjs.org/#{release}/docs/partials/"59 self.base_url = "https://code.angularjs.org/#{release}/docs/partials/"
51 end60 end
52 61
@@ -69,5 +78,9 @@ module Docs · 16 unmodified lines
69 self.release = '1.2.32'78 self.release = '1.2.32'
70 self.base_url = "https://code.angularjs.org/#{release}/docs/partials/"79 self.base_url = "https://code.angularjs.org/#{release}/docs/partials/"
71 end80 end
81
82 def get_latest_version(opts)
83 get_npm_version('angular', opts)
84 end
72 end85 end
73end86end
lib/docs/scrapers/ansible.rb+31−50
@@ -11,72 +11,47 @@ module Docs · 10 unmodified lines
11 11
12 options[:attribution] = <<-HTML12 options[:attribution] = <<-HTML
13 &copy; 2012&ndash;2018 Michael DeHaan<br>13 &copy; 2012&ndash;2018 Michael DeHaan<br>
14 &copy; 2018 Red Hat, Inc.<br>14 &copy; 2018&ndash;2019 Red Hat, Inc.<br>
15 Licensed under the GNU General Public License version 3.15 Licensed under the GNU General Public License version 3.
16 HTML16 HTML
17 17
18 version '2.7' do18 options[:skip] = %w(
19 self.release = '2.7.1'19 installation_guide/index.html
20 self.base_url = 'https://docs.ansible.com/ansible/2.7/'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)
21 26
22 options[:skip] = %w(27 options[:skip_patterns] = [
23 installation_guide/index.html28 /\Acommunity.*/i,
24 reference_appendices/glossary.html29 /\Adev_guide.*/i,
25 reference_appendices/faq.html30 /\Aroadmap.*/i,
26 reference_appendices/tower.html31 ]
27 user_guide/quickstart.html32
28 modules/modules_by_category.html33 version '2.8' do
29 modules/list_of_all_modules.html)34 self.release = '2.8.3'
35 self.base_url = 'https://docs.ansible.com/ansible/2.7/'
36 end
30 37
31 options[:skip_patterns] = [38 version '2.7' do
32 /\Acommunity.*/i,39 self.release = '2.7.12'
33 /\Adev_guide.*/i,40 self.base_url = 'https://docs.ansible.com/ansible/2.7/'
34 /\Aroadmap.*/i,
35 ]
36 end41 end
37 42
38 version '2.6' do43 version '2.6' do
39 self.release = '2.6.7'44 self.release = '2.6.18'
40 self.base_url = 'https://docs.ansible.com/ansible/2.6/'45 self.base_url = 'https://docs.ansible.com/ansible/2.6/'
41
42 options[:skip] = %w(
43 installation_guide/index.html
44 reference_appendices/glossary.html
45 reference_appendices/faq.html
46 reference_appendices/tower.html
47 user_guide/quickstart.html
48 modules/modules_by_category.html
49 modules/list_of_all_modules.html)
50
51 options[:skip_patterns] = [
52 /\Acommunity.*/i,
53 /\Adev_guide.*/i,
54 /\Aroadmap.*/i,
55 ]
56 end46 end
57 47
58 version '2.5' do48 version '2.5' do
59 self.release = '2.5.3'49 self.release = '2.5.15'
60 self.base_url = 'https://docs.ansible.com/ansible/2.5/'50 self.base_url = 'https://docs.ansible.com/ansible/2.5/'
61
62 options[:skip] = %w(
63 installation_guide/index.html
64 reference_appendices/glossary.html
65 reference_appendices/faq.html
66 reference_appendices/tower.html
67 user_guide/quickstart.html
68 modules/modules_by_category.html
69 modules/list_of_all_modules.html)
70
71 options[:skip_patterns] = [
72 /\Acommunity.*/i,
73 /\Adev_guide.*/i,
74 /\Aroadmap.*/i,
75 ]
76 end51 end
77 52
78 version '2.4' do53 version '2.4' do
79 self.release = '2.4.3'54 self.release = '2.4.6'
80 self.base_url = 'https://docs.ansible.com/ansible/2.4/'55 self.base_url = 'https://docs.ansible.com/ansible/2.4/'
81 56
82 options[:skip] = %w(57 options[:skip] = %w(
@@ -86,6 +61,12 @@ module Docs · 3 unmodified lines
86 tower.html61 tower.html
87 quickstart.html62 quickstart.html
88 list_of_all_modules.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
89 end70 end
90 end71 end
91end72end
lib/docs/scrapers/apache.rb+5−0
@@ -33,5 +33,10 @@ module Docs · 32 unmodified lines
33 &copy; 2018 The Apache Software Foundation<br>33 &copy; 2018 The Apache Software Foundation<br>
34 Licensed under the Apache License, Version 2.0.34 Licensed under the Apache License, Version 2.0.
35 HTML35 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 end41 end
37end42end
lib/docs/scrapers/apache_pig.rb+5−0
@@ -43,5 +43,10 @@ module Docs · 42 unmodified lines
43 self.base_url = "https://pig.apache.org/docs/r#{release}/"43 self.base_url = "https://pig.apache.org/docs/r#{release}/"
44 end44 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 end51 end
47end52end
lib/docs/scrapers/async.rb+5−0
@@ -17,5 +17,10 @@ module Docs · 16 unmodified lines
17 &copy; 2010&ndash;2018 Caolan McMahon<br>17 &copy; 2010&ndash;2018 Caolan McMahon<br>
18 Licensed under the MIT License.18 Licensed under the MIT License.
19 HTML19 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 end25 end
21end26end
lib/docs/scrapers/babel.rb+5−0
@@ -22,5 +22,10 @@ module Docs · 21 unmodified lines
22 stub '' do22 stub '' do
23 '<div></div>'23 '<div></div>'
24 end24 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
25 end30 end
26end31end
lib/docs/scrapers/backbone.rb+5−0
@@ -20,5 +20,10 @@ module Docs · 19 unmodified lines
20 &copy; 2010&ndash;2016 Jeremy Ashkenas, DocumentCloud<br>20 &copy; 2010&ndash;2016 Jeremy Ashkenas, DocumentCloud<br>
21 Licensed under the MIT License.21 Licensed under the MIT License.
22 HTML22 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 end28 end
24end29end
lib/docs/scrapers/bash.rb+6−1
@@ -1,7 +1,7 @@
1module Docs1module Docs
2 class Bash < UrlScraper2 class Bash < UrlScraper
3 self.type = 'bash'3 self.type = 'bash'
4 self.release = '4.4'4 self.release = '5.0'
5 self.base_url = 'https://www.gnu.org/software/bash/manual'5 self.base_url = 'https://www.gnu.org/software/bash/manual'
6 self.root_path = '/html_node/index.html'6 self.root_path = '/html_node/index.html'
7 self.links = {7 self.links = {
@@ -17,5 +17,10 @@ module Docs · 9 unmodified lines
17 Copyright &copy; 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc.<br>17 Copyright &copy; 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc.<br>
18 Licensed under the GNU Free Documentation License.18 Licensed under the GNU Free Documentation License.
19 HTML19 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
20 end25 end
21end26end
lib/docs/scrapers/bluebird.rb+4−0
@@ -18,5 +18,9 @@ module Docs · 17 unmodified lines
18 &copy; 2013&ndash;2017 Petka Antonov<br>18 &copy; 2013&ndash;2017 Petka Antonov<br>
19 Licensed under the MIT License.19 Licensed under the MIT License.
20 HTML20 HTML
21
22 def get_latest_version(opts)
23 get_npm_version('bluebird', opts)
24 end
21 end25 end
22end26end
lib/docs/scrapers/bootstrap.rb+10−4
@@ -8,16 +8,17 @@ module Docs · 7 unmodified lines
8 8
9 options[:trailing_slash] = true9 options[:trailing_slash] = true
10 10
11 # https://github.com/twbs/bootstrap/blob/master/LICENSE
11 options[:attribution] = <<-HTML12 options[:attribution] = <<-HTML
12 &copy; 2011&ndash;2018 Twitter, Inc.<br>13 &copy; 2011&ndash;2019 Twitter, Inc.<br>
13 &copy; 2011&ndash;2018 The Bootstrap Authors<br>14 &copy; 2011&ndash;2019 The Bootstrap Authors<br>
14 Code licensed under the MIT License.<br>15 Code licensed under the MIT License.<br>
15 Documentation licensed under the Creative Commons Attribution License v3.0.16 Documentation licensed under the Creative Commons Attribution License v3.0.
16 HTML17 HTML
17 18
18 version '4' do19 version '4' do
19 self.release = '4.1.3'20 self.release = '4.3.1'
20 self.base_url = 'https://getbootstrap.com/docs/4.1/'21 self.base_url = 'https://getbootstrap.com/docs/4.3/'
21 self.root_path = 'getting-started/introduction/'22 self.root_path = 'getting-started/introduction/'
22 23
23 html_filters.push 'bootstrap/entries_v4', 'bootstrap/clean_html_v4'24 html_filters.push 'bootstrap/entries_v4', 'bootstrap/clean_html_v4'
@@ -34,5 +35,10 @@ module Docs · 10 unmodified lines
34 35
35 options[:only] = %w(getting-started/ css/ components/ javascript/)36 options[:only] = %w(getting-started/ css/ components/ javascript/)
36 end37 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 end43 end
38end44end
lib/docs/scrapers/bottle.rb+6−0
@@ -27,5 +27,11 @@ module Docs · 26 unmodified lines
27 self.release = '0.11.7'27 self.release = '0.11.7'
28 self.base_url = "https://bottlepy.org/docs/#{self.version}/"28 self.base_url = "https://bottlepy.org/docs/#{self.version}/"
29 end29 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 end36 end
31end37end
lib/docs/scrapers/bower.rb+4−0
@@ -19,5 +19,9 @@ module Docs · 18 unmodified lines
19 &copy; 2018 Bower contributors<br>19 &copy; 2018 Bower contributors<br>
20 Licensed under the MIT License.20 Licensed under the MIT License.
21 HTML21 HTML
22
23 def get_latest_version(opts)
24 get_npm_version('bower', opts)
25 end
22 end26 end
23end27end
lib/docs/scrapers/c.rb+7−0
@@ -26,6 +26,13 @@ module Docs · 25 unmodified lines
26 Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.26 Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
27 HTML27 HTML
28 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
29 private36 private
30 37
31 def file_path_for(*)38 def file_path_for(*)
lib/docs/scrapers/cakephp.rb+5−0
@@ -71,6 +71,11 @@ module Docs · 70 unmodified lines
71 self.base_url = 'https://api.cakephp.org/2.7/'71 self.base_url = 'https://api.cakephp.org/2.7/'
72 end72 end
73 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
74 private79 private
75 80
76 def parse(response)81 def parse(response)
lib/docs/scrapers/chai.rb+4−0
@@ -23,5 +23,9 @@ module Docs · 22 unmodified lines
23 &copy; 2016 Chai.js Assertion Library<br>23 &copy; 2016 Chai.js Assertion Library<br>
24 Licensed under the MIT License.24 Licensed under the MIT License.
25 HTML25 HTML
26
27 def get_latest_version(opts)
28 get_npm_version('chai', opts)
29 end
26 end30 end
27end31end
lib/docs/scrapers/chef.rb+5−0
@@ -47,5 +47,10 @@ module Docs · 46 unmodified lines
47 47
48 options[:only_patterns] = [/\A#{client_path}\//, /\A#{server_path}\//]48 options[:only_patterns] = [/\A#{client_path}\//, /\A#{server_path}\//]
49 end49 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 end55 end
51end56end
lib/docs/scrapers/clojure.rb+14−4
@@ -13,19 +13,29 @@ module Docs · 12 unmodified lines
13 Licensed under the Eclipse Public License 1.0.13 Licensed under the Eclipse Public License 1.0.
14 HTML14 HTML
15 15
16 version '1.9' do16 version '1.10' do
17 self.release = '1.9'17 self.release = '1.10 (stable)'
18 self.base_url = 'https://clojure.github.io/clojure/'18 self.base_url = 'https://clojure.github.io/clojure/'
19 end19 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
21 version '1.8' do26 version '1.8' do
22 self.release = '1.8'27 self.release = '1.8 (legacy)'
23 self.base_url = 'https://clojure.github.io/clojure/branch-clojure-1.8.0/'28 self.base_url = 'https://clojure.github.io/clojure/branch-clojure-1.8.0/'
24 end29 end
25 30
26 version '1.7' do31 version '1.7' do
27 self.release = '1.7'32 self.release = '1.7 (legacy)'
28 self.base_url = 'https://clojure.github.io/clojure/branch-clojure-1.7.0/'33 self.base_url = 'https://clojure.github.io/clojure/branch-clojure-1.7.0/'
29 end34 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
30 end40 end
31end41end
lib/docs/scrapers/cmake.rb+32−11
@@ -13,51 +13,72 @@ module Docs · 12 unmodified lines
13 options[:title] = false13 options[:title] = false
14 options[:root_title] = 'CMake Reference Documentation'14 options[:root_title] = 'CMake Reference Documentation'
15 options[:skip] = %w(release/index.html genindex.html search.html)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 options[:attribution] = <<-HTML18 options[:attribution] = <<-HTML
19 &copy; 2000&ndash;2018 Kitware, Inc. and Contributors<br>19 &copy; 2000&ndash;2019 Kitware, Inc. and Contributors<br>
20 Licensed under the BSD 3-clause License.20 Licensed under the BSD 3-clause License.
21 HTML21 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
23 version '3.12' do38 version '3.12' do
24 self.release = '3.12.1'39 self.release = '3.12.4'
25 self.base_url = 'https://cmake.org/cmake/help/v3.12/'40 self.base_url = "https://cmake.org/cmake/help/v#{self.version}/"
26 end41 end
27 42
28 version '3.11' do43 version '3.11' do
29 self.release = '3.11.4'44 self.release = '3.11.4'
30 self.base_url = 'https://cmake.org/cmake/help/v3.11/'45 self.base_url = "https://cmake.org/cmake/help/v#{self.version}/"
31 end46 end
32 47
33 version '3.10' do48 version '3.10' do
34 self.release = '3.10.3'49 self.release = '3.10.3'
35 self.base_url = 'https://cmake.org/cmake/help/v3.10/'50 self.base_url = "https://cmake.org/cmake/help/v#{self.version}/"
36 end51 end
37 52
38 version '3.9' do53 version '3.9' do
39 self.release = '3.9.6'54 self.release = '3.9.6'
40 self.base_url = 'https://cmake.org/cmake/help/v3.9/'55 self.base_url = "https://cmake.org/cmake/help/v#{self.version}/"
41 end56 end
42 57
43 version '3.8' do58 version '3.8' do
44 self.release = '3.8.2'59 self.release = '3.8.2'
45 self.base_url = 'https://cmake.org/cmake/help/v3.8/'60 self.base_url = "https://cmake.org/cmake/help/v#{self.version}/"
46 end61 end
47 62
48 version '3.7' do63 version '3.7' do
49 self.release = '3.7.2'64 self.release = '3.7.2'
50 self.base_url = 'https://cmake.org/cmake/help/v3.7/'65 self.base_url = "https://cmake.org/cmake/help/v#{self.version}/"
51 end66 end
52 67
53 version '3.6' do68 version '3.6' do
54 self.release = '3.6.3'69 self.release = '3.6.3'
55 self.base_url = 'https://cmake.org/cmake/help/v3.6/'70 self.base_url = "https://cmake.org/cmake/help/v#{self.version}/"
56 end71 end
57 72
58 version '3.5' do73 version '3.5' do
59 self.release = '3.5.2'74 self.release = '3.5.2'
60 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]
61 end82 end
62 end83 end
63end84end
lib/docs/scrapers/codeception.rb+5−0
@@ -18,5 +18,10 @@ module Docs · 17 unmodified lines
18 &copy; 2011 Michael Bodnarchuk and contributors<br>18 &copy; 2011 Michael Bodnarchuk and contributors<br>
19 Licensed under the MIT License.19 Licensed under the MIT License.
20 HTML20 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 end26 end
22end27end
lib/docs/scrapers/codeceptjs.rb+4−0
@@ -21,5 +21,9 @@ module Docs · 20 unmodified lines
21 &copy; 2015 DavertMik &lt;davert@codegyre.com&gt; (http://codegyre.com)<br>21 &copy; 2015 DavertMik &lt;davert@codegyre.com&gt; (http://codegyre.com)<br>
22 Licensed under the MIT License.22 Licensed under the MIT License.
23 HTML23 HTML
24
25 def get_latest_version(opts)
26 get_npm_version('codeceptjs', opts)
27 end
24 end28 end
25end29end
lib/docs/scrapers/codeigniter.rb+6−0
@@ -38,5 +38,11 @@ module Docs · 37 unmodified lines
38 version '3' do38 version '3' do
39 self.release = '3.1.8'39 self.release = '3.1.8'
40 end40 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]
46 end
41 end47 end
42end48end
lib/docs/scrapers/coffeescript.rb+4−0
@@ -30,5 +30,9 @@ module Docs · 29 unmodified lines
30 30
31 options[:container] = '.container'31 options[:container] = '.container'
32 end32 end
33
34 def get_latest_version(opts)
35 get_npm_version('coffeescript', opts)
36 end
33 end37 end
34end38end
lib/docs/scrapers/composer.rb+28−0
@@ -0,0 +1,28 @@
1module Docs
2 class Composer < UrlScraper
3 self.type = 'simple'
4 self.release = '1.9.0'
5 self.base_url = 'https://getcomposer.org/doc/'
6 self.links = {
7 home: 'https://getcomposer.org',
8 code: 'https://github.com/composer/composer'
9 }
10
11 html_filters.push 'composer/clean_html', 'composer/entries'
12
13 options[:container] = '#main'
14
15 options[:skip_patterns] = [
16 /^faqs/
17 ]
18
19 options[:attribution] = <<-HTML
20 &copy; Nils Adermann, Jordi Boggiano<br>
21 Licensed under the MIT License.
22 HTML
23
24 def get_latest_version(opts)
25 get_latest_github_release('composer', 'composer', opts)
26 end
27 end
28end
lib/docs/scrapers/cordova.rb+10−0
@@ -42,5 +42,15 @@ module Docs · 41 unmodified lines
42 self.release = '6.5.0'42 self.release = '6.5.0'
43 self.base_url = 'https://cordova.apache.org/docs/en/6.x/'43 self.base_url = 'https://cordova.apache.org/docs/en/6.x/'
44 end44 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
45 end55 end
46end56end
lib/docs/scrapers/cpp.rb+8−0
@@ -34,6 +34,14 @@ module Docs · 33 unmodified lines
34 Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.34 Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
35 HTML35 HTML
36 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
37 private45 private
38 46
39 def file_path_for(*)47 def file_path_for(*)
lib/docs/scrapers/crystal.rb+5−0
@@ -34,5 +34,10 @@ module Docs · 33 unmodified lines
34 HTML34 HTML
35 end35 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 end42 end
38end43end
lib/docs/scrapers/cypress.rb+36−0
@@ -0,0 +1,36 @@
1# frozen_string_literal: true
2
3module Docs
4 class Cypress < UrlScraper
5 self.name = 'Cypress'
6 self.type = 'cypress'
7 self.release = '3.4.1'
8 self.base_url = 'https://docs.cypress.io'
9 self.root_path = '/api/api/table-of-contents.html'
10 self.links = {
11 home: 'https://www.cypress.io/',
12 code: 'https://github.com/cypress-io/cypress',
13 }
14
15 html_filters.push 'cypress/entries', 'cypress/clean_html'
16
17 options[:container] = '#content'
18 options[:max_image_size] = 300_000
19 options[:include_default_entry] = true
20
21 options[:skip_patterns] = [/examples\//]
22 options[:skip_link] = ->(link) {
23 href = link.attr(:href)
24 href.nil? ? true : EntriesFilter::SECTIONS.none? { |section| href.match?("/#{section}/") }
25 }
26
27 options[:attribution] = <<-HTML
28 &copy; 2017 Cypress.io<br>
29 Licensed under the MIT License.
30 HTML
31
32 def get_latest_version(opts)
33 get_latest_github_release('cypress-io', 'cypress', opts)
34 end
35 end
36end
lib/docs/scrapers/d.rb+5−0
@@ -26,5 +26,10 @@ module Docs · 25 unmodified lines
26 def initial_urls26 def initial_urls
27 %w(https://dlang.org/phobos/index.html https://dlang.org/spec/intro.html)27 %w(https://dlang.org/phobos/index.html https://dlang.org/spec/intro.html)
28 end28 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 end34 end
30end35end
lib/docs/scrapers/d3.rb+4−0
@@ -58,5 +58,9 @@ module Docs · 57 unmodified lines
58 options[:root_title] = 'D3.js'58 options[:root_title] = 'D3.js'
59 options[:only_patterns] = [/\.md\z/]59 options[:only_patterns] = [/\.md\z/]
60 end60 end
61
62 def get_latest_version(opts)
63 get_npm_version('d3', opts)
64 end
61 end65 end
62end66end
lib/docs/scrapers/dart.rb+6−0
@@ -31,5 +31,11 @@ module Docs · 30 unmodified lines
31 self.release = '1.24.3'31 self.release = '1.24.3'
32 self.base_url = "https://api.dartlang.org/stable/#{release}/"32 self.base_url = "https://api.dartlang.org/stable/#{release}/"
33 end33 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
34 end40 end
35end41end
lib/docs/scrapers/django.rb+13−3
@@ -34,18 +34,23 @@ module Docs · 33 unmodified lines
34 Licensed under the BSD License.34 Licensed under the BSD License.
35 HTML35 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
37 version '2.1' do42 version '2.1' do
38 self.release = '2.1.0'43 self.release = '2.1.11'
39 self.base_url = 'https://docs.djangoproject.com/en/2.1/'44 self.base_url = 'https://docs.djangoproject.com/en/2.1/'
40 end45 end
41 46
42 version '2.0' do47 version '2.0' do
43 self.release = '2.0.7'48 self.release = '2.0.13'
44 self.base_url = 'https://docs.djangoproject.com/en/2.0/'49 self.base_url = 'https://docs.djangoproject.com/en/2.0/'
45 end50 end
46 51
47 version '1.11' do52 version '1.11' do
48 self.release = '1.11.9'53 self.release = '1.11.23'
49 self.base_url = 'https://docs.djangoproject.com/en/1.11/'54 self.base_url = 'https://docs.djangoproject.com/en/1.11/'
50 end55 end
51 56
@@ -63,5 +68,10 @@ module Docs · 11 unmodified lines
63 self.release = '1.8.18'68 self.release = '1.8.18'
64 self.base_url = 'https://docs.djangoproject.com/en/1.8/'69 self.base_url = 'https://docs.djangoproject.com/en/1.8/'
65 end70 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
66 end76 end
67end77end
lib/docs/scrapers/docker.rb+153−26
@@ -15,15 +15,15 @@ module Docs · 14 unmodified lines
15 end15 end
16 16
17 options[:attribution] = <<-HTML17 options[:attribution] = <<-HTML
18 &copy; 2017 Docker, Inc.<br>18 &copy; 2019 Docker, Inc.<br>
19 Licensed under the Apache License, Version 2.0.<br>19 Licensed under the Apache License, Version 2.0.<br>
20 Docker and the Docker logo are trademarks or registered trademarks of Docker, Inc. in the United States and/or other countries.<br>20 Docker and the Docker logo are trademarks or registered trademarks of Docker, Inc. in the United States and/or other countries.<br>
21 Docker, Inc. and other parties may also have trademark rights in other terms used herein.21 Docker, Inc. and other parties may also have trademark rights in other terms used herein.
22 HTML22 HTML
23 23
24 version '17' do24 version '19' do
25 self.release = '17.06'25 self.release = '19.03'
26 self.base_url = 'https://docs.docker.com/'26 self.base_url = "https://docs.docker.com/"
27 27
28 html_filters.push 'docker/entries', 'docker/clean_html'28 html_filters.push 'docker/entries', 'docker/clean_html'
29 29
@@ -33,29 +33,150 @@ module Docs · 3 unmodified lines
33 options[:skip_patterns] = [/\Aengine\/api\/v/, /glossary/, /docker-ee/]33 options[:skip_patterns] = [/\Aengine\/api\/v/, /glossary/, /docker-ee/]
34 34
35 options[:replace_paths] = {35 options[:replace_paths] = {
36 'engine/installation/linux/docker-ee/linux-postinstall/' => 'engine/installation/linux/linux-postinstall/',36 'install/linux/ubuntu/' => 'install/linux/docker-ce',
37 'engine/installation/linux/docker-ee/' => 'engine/installation/',37 'get-started/part1' => 'get-started',
38 'engine/installation/linux/docker-ce/' => 'engine/installation/',38 'engine/installation/' => 'install',
39 'engine/installation/linux/' => 'engine/installation/',39 'engine/installation/linux/linux-postinstall/' => 'install/linux',
40 'engine/installation/windows/' => 'engine/installation/',40 'compose/overview/' => 'compose',
41 'engine/userguide/intro/' => 'engine/userguide/',41 'docker-cloud/' => 'docker-hub',
42 'engine/tutorials/dockervolumes/' => 'engine/admin/volumes/volumes/',42 'datacenter/install/linux/' => 'ee',
43 'engine/getstarted/' => 'get-started/',43 'engine/userguide/' => 'config/daemon',
44 'engine/tutorials/dockerimages/' => 'get-started/',44 'engine/admin/' => 'config/daemon',
45 'engine/tutorials/dockerrepos/' => 'get-started/',45 'opensource/get-help/' => 'opensource',
46 'engine/admin/host_integration/' => 'engine/admin/start-containers-automatically/',46 'engine/tutorials/dockerimages/' => 'get-started',
47 'engine/installation/linux/rhel/' => 'engine/installation/linux/docker-ee/rhel/',47 'engine/admin/volumes/bind-mounts/' => 'storage',
48 'engine/installation/linux/ubuntulinux/' => 'engine/installation/linux/docker-ee/ubuntu/',48 'engine/tutorials/dockervolumes/' => 'storage',
49 'engine/installation/linux/suse/' => 'engine/installation/linux/docker-ee/suse/',49 'engine/admin/volumes/volumes/' => 'storage',
50 'engine/admin/logging/' => 'engine/admin/logging/view_container_logs/',50 'engine/userguide/labels-custom-metadata/' => 'config',
51 'engine/swarm/how-swarm-mode-works/' => 'engine/swarm/how-swarm-mode-works/nodes/',51 'engine/userguide/eng-image/multistage-build/' => 'develop/develop-images',
52 'engine/installation/binaries/' => 'engine/installation/linux/docker-ce/binaries/',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 'engine/reference/commandline/' => 'engine/reference/commandline/docker/',74 'engine/reference/commandline/' => 'engine/reference/commandline/docker/',
54 'engine/reference/api/' => 'engine/api/',75 }
55 'engine/userguide/dockervolumes/' => 'engine/admin/volumes/volumes/',76 end
56 'engine/understanding-docker/' => 'engine/docker-overview/',77
57 'engine/reference/commandline/swarm_join_token/' => 'engine/reference/commandline/swarm_join-token/',78 version '18' do
58 'engine/api/getting-started/' => 'engine/api/get-started/',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 end181 end
61 182
@@ -137,5 +258,11 @@ module Docs · 75 unmodified lines
137 options[:container] = '#docs'258 options[:container] = '#docs'
138 options[:only_patterns] << /\Aswarm\//259 options[:only_patterns] << /\Aswarm\//
139 end260 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 end267 end
141end268end
lib/docs/scrapers/dojo.rb+5−0
@@ -36,6 +36,11 @@ module Docs · 35 unmodified lines
36 urls.map { |url| "<a href='#{url}'>#{url}</a>" }.join36 urls.map { |url| "<a href='#{url}'>#{url}</a>" }.join
37 end37 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 private44 private
40 45
41 def get_url_list(json, set = Set.new)46 def get_url_list(json, set = Set.new)
lib/docs/scrapers/drupal.rb+5−0
@@ -98,5 +98,10 @@ module Docs · 97 unmodified lines
98 /\A[\w\-\.]+\.php\/7\.x\z/98 /\A[\w\-\.]+\.php\/7\.x\z/
99 ]99 ]
100 end100 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 end106 end
102end107end
lib/docs/scrapers/electron.rb+5−0
@@ -22,5 +22,10 @@ module Docs · 21 unmodified lines
22 &copy; 2013&ndash;2018 GitHub Inc.<br>22 &copy; 2013&ndash;2018 GitHub Inc.<br>
23 Licensed under the MIT license.23 Licensed under the MIT license.
24 HTML24 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 end30 end
26end31end
lib/docs/scrapers/elixir.rb+33−2
@@ -33,8 +33,34 @@ module Docs · 32 unmodified lines
33 "https://elixir-lang.org/getting-started/introduction.html" ]33 "https://elixir-lang.org/getting-started/introduction.html" ]
34 end34 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
36 version '1.7' do62 version '1.7' do
37 self.release = '1.7.3'63 self.release = '1.7.4'
38 self.base_urls = [64 self.base_urls = [
39 "https://hexdocs.pm/elixir/#{release}/",65 "https://hexdocs.pm/elixir/#{release}/",
40 "https://hexdocs.pm/eex/#{release}/",66 "https://hexdocs.pm/eex/#{release}/",
@@ -47,7 +73,7 @@ module Docs · 6 unmodified lines
47 end73 end
48 74
49 version '1.6' do75 version '1.6' do
50 self.release = '1.6.5'76 self.release = '1.6.6'
51 self.base_urls = [77 self.base_urls = [
52 "https://hexdocs.pm/elixir/#{release}/",78 "https://hexdocs.pm/elixir/#{release}/",
53 "https://hexdocs.pm/eex/#{release}/",79 "https://hexdocs.pm/eex/#{release}/",
@@ -97,5 +123,10 @@ module Docs · 43 unmodified lines
97 'https://elixir-lang.org/getting-started/'123 'https://elixir-lang.org/getting-started/'
98 ]124 ]
99 end125 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
100 end131 end
101end132end
lib/docs/scrapers/ember.rb+5−0
@@ -56,5 +56,10 @@ module Docs · 55 unmodified lines
56 https://emberjs.com/api/ember-data/2.14/classes/DS56 https://emberjs.com/api/ember-data/2.14/classes/DS
57 )57 )
58 end58 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 end64 end
60end65end
lib/docs/scrapers/erlang.rb+5−0
@@ -55,5 +55,10 @@ module Docs · 54 unmodified lines
55 version '18' do55 version '18' do
56 self.release = '18.3'56 self.release = '18.3'
57 end57 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 /, '')
62 end
58 end63 end
59end64end
lib/docs/scrapers/eslint.rb+4−0
@@ -20,5 +20,9 @@ module Docs · 19 unmodified lines
20 &copy; JS Foundation and other contributors<br>20 &copy; JS Foundation and other contributors<br>
21 Licensed under the MIT License.21 Licensed under the MIT License.
22 HTML22 HTML
23
24 def get_latest_version(opts)
25 get_npm_version('eslint', opts)
26 end
23 end27 end
24end28end
lib/docs/scrapers/express.rb+4−0
@@ -28,5 +28,9 @@ module Docs · 27 unmodified lines
28 &copy; 2017 StrongLoop, IBM, and other expressjs.com contributors.<br>28 &copy; 2017 StrongLoop, IBM, and other expressjs.com contributors.<br>
29 Licensed under the Creative Commons Attribution-ShareAlike License v3.0.29 Licensed under the Creative Commons Attribution-ShareAlike License v3.0.
30 HTML30 HTML
31
32 def get_latest_version(opts)
33 get_npm_version('express', opts)
34 end
31 end35 end
32end36end
lib/docs/scrapers/falcon.rb+5−0
@@ -33,5 +33,10 @@ module Docs · 32 unmodified lines
33 self.release = '1.2.0'33 self.release = '1.2.0'
34 self.base_url = "https://falcon.readthedocs.io/en/#{self.release}/"34 self.base_url = "https://falcon.readthedocs.io/en/#{self.release}/"
35 end35 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
36 end41 end
37end42end
lib/docs/scrapers/fish.rb+11−0
@@ -12,11 +12,17 @@ module Docs · 11 unmodified lines
12 12
13 options[:skip] = %w(design.html license.html)13 options[:skip] = %w(design.html license.html)
14 14
15 # https://fishshell.com/docs/current/license.html
15 options[:attribution] = <<-HTML16 options[:attribution] = <<-HTML
16 &copy; 2005&ndash;2009 Axel Liljencrantz<br>17 &copy; 2005&ndash;2009 Axel Liljencrantz<br>
17 Licensed under the GNU General Public License, version 2.18 Licensed under the GNU General Public License, version 2.
18 HTML19 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
20 version '2.7' do26 version '2.7' do
21 self.release = '2.7.1'27 self.release = '2.7.1'
22 self.base_url = "https://fishshell.com/docs/#{version}/"28 self.base_url = "https://fishshell.com/docs/#{version}/"
@@ -46,5 +52,10 @@ module Docs · 23 unmodified lines
46 self.release = '2.2.0'52 self.release = '2.2.0'
47 self.base_url = "https://fishshell.com/docs/#{version}/"53 self.base_url = "https://fishshell.com/docs/#{version}/"
48 end54 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
49 end60 end
50end61end
lib/docs/scrapers/flow.rb+4−0
@@ -18,5 +18,9 @@ module Docs · 17 unmodified lines
18 &copy; 2013&ndash;present Facebook Inc.<br>18 &copy; 2013&ndash;present Facebook Inc.<br>
19 Licensed under the MIT License.19 Licensed under the MIT License.
20 HTML20 HTML
21
22 def get_latest_version(opts)
23 get_npm_version('flow-bin', opts)
24 end
21 end25 end
22end26end
lib/docs/scrapers/git.rb+5−0
@@ -19,5 +19,10 @@ module Docs · 18 unmodified lines
19 &copy; 2005&ndash;2018 Linus Torvalds and others<br>19 &copy; 2005&ndash;2018 Linus Torvalds and others<br>
20 Licensed under the GNU General Public License version 2.20 Licensed under the GNU General Public License version 2.
21 HTML21 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 end27 end
23end28end
lib/docs/scrapers/gnu/gcc.rb+6−0
@@ -99,5 +99,11 @@ module Docs · 98 unmodified lines
99 99
100 options[:replace_paths] = CPP_PATHS100 options[:replace_paths] = CPP_PATHS
101 end101 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
102 end108 end
103end109end
lib/docs/scrapers/gnu/gnu_fortran.rb+6−0
@@ -25,5 +25,11 @@ module Docs · 24 unmodified lines
25 self.release = '4.9.3'25 self.release = '4.9.3'
26 self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/gfortran/"26 self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/gfortran/"
27 end27 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
28 end34 end
29end35end
lib/docs/scrapers/go.rb+6−1
@@ -1,7 +1,7 @@
1module Docs1module Docs
2 class Go < UrlScraper2 class Go < UrlScraper
3 self.type = 'go'3 self.type = 'go'
4 self.release = '1.11.2'4 self.release = '1.12'
5 self.base_url = 'https://golang.org/pkg/'5 self.base_url = 'https://golang.org/pkg/'
6 self.links = {6 self.links = {
7 home: 'https://golang.org/',7 home: 'https://golang.org/',
@@ -24,6 +24,11 @@ module Docs · 16 unmodified lines
24 Licensed under the Creative Commons Attribution License 3.0.24 Licensed under the Creative Commons Attribution License 3.0.
25 HTML25 HTML
26 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
27 private32 private
28 33
29 def parse(response) # Hook here because Nokogori removes whitespace from textareas34 def parse(response) # Hook here because Nokogori removes whitespace from textareas
lib/docs/scrapers/godot.rb+5−0
@@ -37,5 +37,10 @@ module Docs · 36 unmodified lines
37 self.release = '2.1'37 self.release = '2.1'
38 self.base_url = "http://docs.godotengine.org/en/#{self.version}/"38 self.base_url = "http://docs.godotengine.org/en/#{self.version}/"
39 end39 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
40 end45 end
41end46end
lib/docs/scrapers/graphite.rb+5−0
@@ -17,5 +17,10 @@ module Docs · 16 unmodified lines
17 &copy; 2011&ndash;2016 The Graphite Project<br>17 &copy; 2011&ndash;2016 The Graphite Project<br>
18 Licensed under the Apache License, Version 2.0.18 Licensed under the Apache License, Version 2.0.
19 HTML19 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
20 end25 end
21end26end
lib/docs/scrapers/grunt.rb+4−0
@@ -26,5 +26,9 @@ module Docs · 25 unmodified lines
26 &copy; GruntJS Team<br>26 &copy; GruntJS Team<br>
27 Licensed under the MIT License.27 Licensed under the MIT License.
28 HTML28 HTML
29
30 def get_latest_version(opts)
31 get_npm_version('grunt-cli', opts)
32 end
29 end33 end
30end34end
lib/docs/scrapers/handlebars.rb+4−0
@@ -19,5 +19,9 @@ module Docs · 18 unmodified lines
19 &copy; 2011&ndash;2017 by Yehuda Katz<br>19 &copy; 2011&ndash;2017 by Yehuda Katz<br>
20 Licensed under the MIT License.20 Licensed under the MIT License.
21 HTML21 HTML
22
23 def get_latest_version(opts)
24 get_npm_version('handlebars', opts)
25 end
22 end26 end
23end27end
lib/docs/scrapers/haskell.rb+8−1
@@ -10,7 +10,7 @@ module Docs · 9 unmodified lines
10 10
11 html_filters.push 'haskell/entries', 'haskell/clean_html'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 options[:only_patterns] = [/\Alibraries\//, /\Ausers_guide\//]15 options[:only_patterns] = [/\Alibraries\//, /\Ausers_guide\//]
16 options[:skip_patterns] = [16 options[:skip_patterns] = [
@@ -68,5 +68,12 @@ module Docs · 51 unmodified lines
68 68
69 options[:only_patterns] = [/\Alibraries\//]69 options[:only_patterns] = [/\Alibraries\//]
70 end70 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 end78 end
72end79end
lib/docs/scrapers/haxe.rb+6−0
@@ -66,5 +66,11 @@ module Docs · 65 unmodified lines
66 version 'Python' do66 version 'Python' do
67 self.base_url = 'https://api.haxe.org/python/'67 self.base_url = 'https://api.haxe.org/python/'
68 end68 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 /, '')
74 end
69 end75 end
70end76end
lib/docs/scrapers/homebrew.rb+5−1
@@ -2,7 +2,7 @@ module Docs · 1 unmodified line
2 class Homebrew < UrlScraper2 class Homebrew < UrlScraper
3 self.name = 'Homebrew'3 self.name = 'Homebrew'
4 self.type = 'simple'4 self.type = 'simple'
5 self.release = '1.8.1'5 self.release = '2.1.9'
6 self.base_url = 'https://docs.brew.sh/'6 self.base_url = 'https://docs.brew.sh/'
7 self.links = {7 self.links = {
8 home: 'https://brew.sh',8 home: 'https://brew.sh',
@@ -19,5 +19,9 @@ module Docs · 10 unmodified lines
19 &copy; 2009&ndash;present Homebrew contributors<br>19 &copy; 2009&ndash;present Homebrew contributors<br>
20 Licensed under the BSD 2-Clause License.20 Licensed under the BSD 2-Clause License.
21 HTML21 HTML
22
23 def get_latest_version(opts)
24 get_latest_github_release('Homebrew', 'brew', opts)
25 end
22 end26 end
23end27end
lib/docs/scrapers/http.rb+2−0
@@ -7,6 +7,8 @@ module Docs · 6 unmodified lines
7 7
8 html_filters.push 'http/clean_html', 'http/entries', 'title'8 html_filters.push 'http/clean_html', 'http/entries', 'title'
9 9
10 options[:mdn_tag] = 'HTTP'
11
10 options[:root_title] = 'HTTP'12 options[:root_title] = 'HTTP'
11 options[:title] = ->(filter) { filter.current_url.host == 'tools.ietf.org' ? false : filter.default_title }13 options[:title] = ->(filter) { filter.current_url.host == 'tools.ietf.org' ? false : filter.default_title }
12 options[:container] = ->(filter) { filter.current_url.host == 'tools.ietf.org' ? '.content' : nil }14 options[:container] = ->(filter) { filter.current_url.host == 'tools.ietf.org' ? '.content' : nil }
lib/docs/scrapers/immutable.rb+4−0
@@ -54,5 +54,9 @@ module Docs · 53 unmodified lines
54 JS54 JS
55 capybara.html55 capybara.html
56 end56 end
57
58 def get_latest_version(opts)
59 get_npm_version('immutable', opts)
60 end
57 end61 end
58end62end
lib/docs/scrapers/influxdata.rb+6−0
@@ -46,5 +46,11 @@ module Docs · 45 unmodified lines
46 &copy; 2015 InfluxData, Inc.<br>46 &copy; 2015 InfluxData, Inc.<br>
47 Licensed under the MIT license.47 Licensed under the MIT license.
48 HTML48 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 end55 end
50end56end
lib/docs/scrapers/jasmine.rb+4−0
@@ -17,5 +17,9 @@ module Docs · 16 unmodified lines
17 &copy; 2008&ndash;2017 Pivotal Labs<br>17 &copy; 2008&ndash;2017 Pivotal Labs<br>
18 Licensed under the MIT License.18 Licensed under the MIT License.
19 HTML19 HTML
20
21 def get_latest_version(opts)
22 get_latest_github_release('jasmine', 'jasmine', opts)
23 end
20 end24 end
21end25end
lib/docs/scrapers/jekyll.rb+5−0
@@ -28,5 +28,10 @@ module Docs · 27 unmodified lines
28 &copy; 2008&ndash;2018 Tom Preston-Werner and Jekyll contributors<br>28 &copy; 2008&ndash;2018 Tom Preston-Werner and Jekyll contributors<br>
29 Licensed under the MIT license.29 Licensed under the MIT license.
30 HTML30 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
31 end36 end
32end37end
lib/docs/scrapers/jest.rb+5−0
@@ -17,5 +17,10 @@ module Docs · 16 unmodified lines
17 &copy; 2014&ndash;present Facebook Inc.<br>17 &copy; 2014&ndash;present Facebook Inc.<br>
18 Licensed under the BSD License.18 Licensed under the BSD License.
19 HTML19 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 end25 end
21end26end
lib/docs/scrapers/jquery/jquery_core.rb+4−0
@@ -22,5 +22,9 @@ module Docs · 21 unmodified lines
22 /Selectors\/odd/i,22 /Selectors\/odd/i,
23 /index/i23 /index/i
24 ]24 ]
25
26 def get_latest_version(opts)
27 get_npm_version('jquery', opts)
28 end
25 end29 end
26end30end
lib/docs/scrapers/jquery/jquery_mobile.rb+5−0
@@ -16,5 +16,10 @@ module Docs · 15 unmodified lines
16 options[:fix_urls] = ->(url) do16 options[:fix_urls] = ->(url) do
17 url.sub! 'http://api.jquerymobile.com/', 'https://api.jquerymobile.com/'17 url.sub! 'http://api.jquerymobile.com/', 'https://api.jquerymobile.com/'
18 end18 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 end24 end
20end25end
lib/docs/scrapers/jquery/jquery_ui.rb+4−0
@@ -15,5 +15,9 @@ module Docs · 14 unmodified lines
15 options[:fix_urls] = ->(url) do15 options[:fix_urls] = ->(url) do
16 url.sub! 'http://api.jqueryui.com/', 'https://api.jqueryui.com/'16 url.sub! 'http://api.jqueryui.com/', 'https://api.jqueryui.com/'
17 end17 end
18
19 def get_latest_version(opts)
20 get_npm_version('jquery-ui', opts)
21 end
18 end22 end
19end23end
lib/docs/scrapers/jsdoc.rb+4−0
@@ -21,5 +21,9 @@ module Docs · 20 unmodified lines
21 &copy; 2011&ndash;2017 the contributors to the JSDoc 3 documentation project<br>21 &copy; 2011&ndash;2017 the contributors to the JSDoc 3 documentation project<br>
22 Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.22 Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
23 HTML23 HTML
24
25 def get_latest_version(opts)
26 get_latest_github_release('jsdoc3', 'jsdoc', opts)
27 end
24 end28 end
25end29end
lib/docs/scrapers/julia.rb+4−0
@@ -49,5 +49,9 @@ module Docs · 48 unmodified lines
49 49
50 html_filters.push 'julia/entries_sphinx', 'julia/clean_html_sphinx', 'sphinx/clean_html'50 html_filters.push 'julia/entries_sphinx', 'julia/clean_html_sphinx', 'sphinx/clean_html'
51 end51 end
52
53 def get_latest_version(opts)
54 get_latest_github_release('JuliaLang', 'julia', opts)
55 end
52 end56 end
53end57end
lib/docs/scrapers/knockout.rb+4−0
@@ -33,5 +33,9 @@ module Docs · 32 unmodified lines
33 &copy; Steven Sanderson, the Knockout.js team, and other contributors<br>33 &copy; Steven Sanderson, the Knockout.js team, and other contributors<br>
34 Licensed under the MIT License.34 Licensed under the MIT License.
35 HTML35 HTML
36
37 def get_latest_version(opts)
38 get_latest_github_release('knockout', 'knockout', opts)
39 end
36 end40 end
37end41end
lib/docs/scrapers/koa.rb+4−0
@@ -34,5 +34,9 @@ module Docs · 33 unmodified lines
34 &copy; 2018 Koa contributors<br>34 &copy; 2018 Koa contributors<br>
35 Licensed under the MIT License.35 Licensed under the MIT License.
36 HTML36 HTML
37
38 def get_latest_version(opts)
39 get_npm_version('koa', opts)
40 end
37 end41 end
38end42end
lib/docs/scrapers/kotlin.rb+15−3
@@ -1,7 +1,7 @@
1module Docs1module Docs
2 class Kotlin < UrlScraper2 class Kotlin < UrlScraper
3 self.type = 'kotlin'3 self.type = 'kotlin'
4 self.release = '1.2.41'4 self.release = '1.3.41'
5 self.base_url = 'https://kotlinlang.org/'5 self.base_url = 'https://kotlinlang.org/'
6 self.root_path = 'api/latest/jvm/stdlib/index.html'6 self.root_path = 'api/latest/jvm/stdlib/index.html'
7 self.links = {7 self.links = {
@@ -22,11 +22,23 @@ module Docs · 14 unmodified lines
22 docs/events.html22 docs/events.html
23 docs/resources.html23 docs/resources.html
24 docs/reference/grammar.html)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 options[:attribution] = <<-HTML35 options[:attribution] = <<-HTML
28 &copy; 2010&ndash;2018 JetBrains s.r.o.<br>36 &copy; 2010&ndash;2019 JetBrains s.r.o.<br>
29 Licensed under the Apache License, Version 2.0.37 Licensed under the Apache License, Version 2.0.
30 HTML38 HTML
39
40 def get_latest_version(opts)
41 get_latest_github_release('JetBrains', 'kotlin', opts)
42 end
31 end43 end
32end44end
lib/docs/scrapers/laravel.rb+4−0
@@ -133,5 +133,9 @@ module Docs · 132 unmodified lines
133 url133 url
134 end134 end
135 end135 end
136
137 def get_latest_version(opts)
138 get_latest_github_release('laravel', 'laravel', opts)
139 end
136 end140 end
137end141end
lib/docs/scrapers/leaflet.rb+16−1
@@ -14,11 +14,21 @@ module Docs · 13 unmodified lines
14 options[:skip_links] = true14 options[:skip_links] = true
15 15
16 options[:attribution] = <<-HTML16 options[:attribution] = <<-HTML
17 &copy; 2010&ndash;2018 Vladimir Agafonkin<br>17 &copy; 2010&ndash;2019 Vladimir Agafonkin<br>
18 &copy; 2010&ndash;2011, CloudMade<br>18 &copy; 2010&ndash;2011, CloudMade<br>
19 Maps &copy; OpenStreetMap contributors.19 Maps &copy; OpenStreetMap contributors.
20 HTML20 HTML
21 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
22 version '1.3' do32 version '1.3' do
23 self.release = '1.3.4'33 self.release = '1.3.4'
24 self.base_url = "https://leafletjs.com/reference-#{release}.html"34 self.base_url = "https://leafletjs.com/reference-#{release}.html"
@@ -39,5 +49,10 @@ module Docs · 14 unmodified lines
39 self.base_url = "https://leafletjs.com/reference-#{release}.html"49 self.base_url = "https://leafletjs.com/reference-#{release}.html"
40 end50 end
41 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
42 end57 end
43end58end
lib/docs/scrapers/less.rb+6−0
@@ -21,5 +21,11 @@ module Docs · 20 unmodified lines
21 &copy; 2009&ndash;2016 The Core Less Team<br>21 &copy; 2009&ndash;2016 The Core Less Team<br>
22 Licensed under the Creative Commons Attribution License 3.0.22 Licensed under the Creative Commons Attribution License 3.0.
23 HTML23 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 end30 end
25end31end
lib/docs/scrapers/liquid.rb+5−0
@@ -19,5 +19,10 @@ module Docs · 18 unmodified lines
19 &copy; 2005, 2006 Tobias Luetke<br>19 &copy; 2005, 2006 Tobias Luetke<br>
20 Licensed under the MIT License.20 Licensed under the MIT License.
21 HTML21 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 end27 end
23end28end
lib/docs/scrapers/lodash.rb+5−0
@@ -32,5 +32,10 @@ module Docs · 31 unmodified lines
32 self.release = '2.4.2'32 self.release = '2.4.2'
33 self.base_url = "https://lodash.com/docs/#{release}"33 self.base_url = "https://lodash.com/docs/#{release}"
34 end34 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 end40 end
36end41end
lib/docs/scrapers/love.rb+5−0
@@ -39,5 +39,10 @@ module Docs · 38 unmodified lines
39 &copy; 2006&ndash;2016 L&Ouml;VE Development Team<br>39 &copy; 2006&ndash;2016 L&Ouml;VE Development Team<br>
40 Licensed under the GNU Free Documentation License, Version 1.3.40 Licensed under the GNU Free Documentation License, Version 1.3.
41 HTML41 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 end47 end
43end48end
lib/docs/scrapers/lua.rb+5−0
@@ -26,5 +26,10 @@ module Docs · 25 unmodified lines
26 self.release = '5.1.5'26 self.release = '5.1.5'
27 self.base_url = 'https://www.lua.org/manual/5.1/'27 self.base_url = 'https://www.lua.org/manual/5.1/'
28 end28 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 end34 end
30end35end
lib/docs/scrapers/marionette.rb+4−0
@@ -38,5 +38,9 @@ module Docs · 37 unmodified lines
38 38
39 html_filters.push 'marionette/entries_v2'39 html_filters.push 'marionette/entries_v2'
40 end40 end
41
42 def get_latest_version(opts)
43 get_npm_version('backbone.marionette', opts)
44 end
41 end45 end
42end46end
lib/docs/scrapers/markdown.rb+4−0
@@ -13,5 +13,9 @@ module Docs · 12 unmodified lines
13 &copy; 2004 John Gruber<br>13 &copy; 2004 John Gruber<br>
14 Licensed under the BSD License.14 Licensed under the BSD License.
15 HTML15 HTML
16
17 def get_latest_version(opts)
18 '1.0.0'
19 end
16 end20 end
17end21end
lib/docs/scrapers/matplotlib.rb+13−0
@@ -20,6 +20,15 @@ module Docs · 19 unmodified lines
20 Licensed under the Matplotlib License Agreement.20 Licensed under the Matplotlib License Agreement.
21 HTML21 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
23 version '3.0' do32 version '3.0' do
24 self.release = '3.0.0'33 self.release = '3.0.0'
25 self.base_urls = [34 self.base_urls = [
@@ -64,5 +73,9 @@ module Docs · 38 unmodified lines
64 "https://matplotlib.org/#{release}/mpl_toolkits/axes_grid/api/"73 "https://matplotlib.org/#{release}/mpl_toolkits/axes_grid/api/"
65 ]74 ]
66 end75 end
76
77 def get_latest_version(opts)
78 get_latest_github_release('matplotlib', 'matplotlib', opts)
79 end
67 end80 end
68end81end
lib/docs/scrapers/mdn/css.rb+2−0
@@ -6,6 +6,8 @@ module Docs · 5 unmodified lines
6 6
7 html_filters.push 'css/clean_html', 'css/entries', 'title'7 html_filters.push 'css/clean_html', 'css/entries', 'title'
8 8
9 options[:mdn_tag] = 'CSS'
10
9 options[:root_title] = 'CSS'11 options[:root_title] = 'CSS'
10 12
11 options[:skip] = %w(/CSS3 /Media/Visual /paged_media /Media/TV /Media/Tactile)13 options[:skip] = %w(/CSS3 /Media/Visual /paged_media /Media/TV /Media/Tactile)
lib/docs/scrapers/mdn/dom.rb+2−0
@@ -8,6 +8,8 @@ module Docs · 7 unmodified lines
8 8
9 html_filters.push 'dom/clean_html', 'dom/entries', 'title'9 html_filters.push 'dom/clean_html', 'dom/entries', 'title'
10 10
11 options[:mdn_tag] = 'XSLT_Reference'
12
11 options[:root_title] = 'DOM'13 options[:root_title] = 'DOM'
12 14
13 options[:skip] = %w(15 options[:skip] = %w(
lib/docs/scrapers/mdn/dom_events.rb+2−0
@@ -9,6 +9,8 @@ module Docs · 8 unmodified lines
9 html_filters.insert_after 'clean_html', 'dom_events/clean_html'9 html_filters.insert_after 'clean_html', 'dom_events/clean_html'
10 html_filters.push 'dom_events/entries', 'title'10 html_filters.push 'dom_events/entries', 'title'
11 11
12 options[:mdn_tag] = 'events'
13
12 options[:root_title] = 'DOM Events'14 options[:root_title] = 'DOM Events'
13 15
14 options[:skip] = %w(/MozOrientation)16 options[:skip] = %w(/MozOrientation)
lib/docs/scrapers/mdn/html.rb+2−0
@@ -7,6 +7,8 @@ module Docs · 6 unmodified lines
7 7
8 html_filters.push 'html/clean_html', 'html/entries', 'title'8 html_filters.push 'html/clean_html', 'html/entries', 'title'
9 9
10 options[:mdn_tag] = 'HTML'
11
10 options[:root_title] = 'HTML'12 options[:root_title] = 'HTML'
11 13
12 options[:title] = ->(filter) do14 options[:title] = ->(filter) do
lib/docs/scrapers/mdn/javascript.rb+2−0
@@ -8,6 +8,8 @@ module Docs · 7 unmodified lines
8 8
9 html_filters.push 'javascript/clean_html', 'javascript/entries', 'title'9 html_filters.push 'javascript/clean_html', 'javascript/entries', 'title'
10 10
11 options[:mdn_tag] = 'JavaScript'
12
11 options[:root_title] = 'JavaScript'13 options[:root_title] = 'JavaScript'
12 14
13 # Don't want15 # Don't want
lib/docs/scrapers/mdn/mdn.rb+5−0
@@ -21,6 +21,11 @@ module Docs · 20 unmodified lines
21 Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.21 Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
22 HTML22 HTML
23 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
24 private29 private
25 30
26 def process_response?(response)31 def process_response?(response)
lib/docs/scrapers/mdn/svg.rb+2−0
@@ -8,6 +8,8 @@ module Docs · 7 unmodified lines
8 8
9 html_filters.push 'svg/clean_html', 'svg/entries', 'title'9 html_filters.push 'svg/clean_html', 'svg/entries', 'title'
10 10
11 options[:mdn_tag] = 'XSLT_Reference'
12
11 options[:root_title] = 'SVG'13 options[:root_title] = 'SVG'
12 14
13 options[:title] = ->(filter) do15 options[:title] = ->(filter) do
lib/docs/scrapers/mdn/xslt_xpath.rb+2−0
@@ -8,6 +8,8 @@ module Docs · 7 unmodified lines
8 8
9 html_filters.push 'xslt_xpath/clean_html', 'xslt_xpath/entries', 'title'9 html_filters.push 'xslt_xpath/clean_html', 'xslt_xpath/entries', 'title'
10 10
11 options[:mdn_tag] = 'XSLT_Reference'
12
11 options[:root_title] = 'XSLT'13 options[:root_title] = 'XSLT'
12 14
13 options[:only_patterns] = [/\A\/XSLT/, /\A\/XPath/]15 options[:only_patterns] = [/\A\/XSLT/, /\A\/XPath/]
lib/docs/scrapers/meteor.rb+5−0
@@ -45,5 +45,10 @@ module Docs · 44 unmodified lines
45 self.base_urls = ['https://guide.meteor.com/v1.3/', "https://docs.meteor.com/v#{self.release}/"]45 self.base_urls = ['https://guide.meteor.com/v1.3/', "https://docs.meteor.com/v#{self.release}/"]
46 options[:fix_urls] = nil46 options[:fix_urls] = nil
47 end47 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 end53 end
49end54end
lib/docs/scrapers/mocha.rb+4−0
@@ -18,5 +18,9 @@ module Docs · 17 unmodified lines
18 &copy; 2011&ndash;2018 JS Foundation and contributors<br>18 &copy; 2011&ndash;2018 JS Foundation and contributors<br>
19 Licensed under the Creative Commons Attribution 4.0 International License.19 Licensed under the Creative Commons Attribution 4.0 International License.
20 HTML20 HTML
21
22 def get_latest_version(opts)
23 get_npm_version('mocha', opts)
24 end
21 end25 end
22end26end
lib/docs/scrapers/modernizr.rb+4−0
@@ -15,5 +15,9 @@ module Docs · 14 unmodified lines
15 &copy; 2009&ndash;2017 The Modernizr team<br>15 &copy; 2009&ndash;2017 The Modernizr team<br>
16 Licensed under the MIT License.16 Licensed under the MIT License.
17 HTML17 HTML
18
19 def get_latest_version(opts)
20 get_npm_version('modernizr', opts)
21 end
18 end22 end
19end23end
lib/docs/scrapers/moment.rb+5−0
@@ -22,5 +22,10 @@ module Docs · 21 unmodified lines
22 &copy; JS Foundation and other contributors<br>22 &copy; JS Foundation and other contributors<br>
23 Licensed under the MIT License.23 Licensed under the MIT License.
24 HTML24 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 end30 end
26end31end
lib/docs/scrapers/mongoose.rb+6−0
@@ -26,5 +26,11 @@ module Docs · 25 unmodified lines
26 &copy; 2010 LearnBoost<br>26 &copy; 2010 LearnBoost<br>
27 Licensed under the MIT License.27 Licensed under the MIT License.
28 HTML28 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
29 end35 end
30end36end
lib/docs/scrapers/nginx.rb+10−4
@@ -2,11 +2,11 @@ module Docs · 1 unmodified line
2 class Nginx < UrlScraper2 class Nginx < UrlScraper
3 self.name = 'nginx'3 self.name = 'nginx'
4 self.type = 'nginx'4 self.type = 'nginx'
5 self.release = '1.15.5'5 self.release = '1.17.2'
6 self.base_url = 'https://nginx.org/en/docs/'6 self.base_url = 'https://nginx.org/en/docs/'
7 self.links = {7 self.links = {
8 home: 'https://nginx.org/',8 home: 'https://nginx.org/',
9 code: 'http://hg.nginx.org/nginx'9 code: 'https://hg.nginx.org/nginx'
10 }10 }
11 11
12 html_filters.push 'nginx/clean_html', 'nginx/entries'12 html_filters.push 'nginx/clean_html', 'nginx/entries'
@@ -21,9 +21,15 @@ module Docs · 8 unmodified lines
21 options[:skip_patterns] = [/\/faq\//]21 options[:skip_patterns] = [/\/faq\//]
22 22
23 options[:attribution] = <<-HTML23 options[:attribution] = <<-HTML
24 &copy; 2002-2018 Igor Sysoev<br>24 &copy; 2002-2019 Igor Sysoev<br>
25 &copy; 2011-2018 Nginx, Inc.<br>25 &copy; 2011-2019 Nginx, Inc.<br>
26 Licensed under the BSD License.26 Licensed under the BSD License.
27 HTML27 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 end34 end
29end35end
lib/docs/scrapers/nginx_lua_module.rb+9−0
@@ -4,6 +4,9 @@ module Docs · 3 unmodified lines
4 self.slug = 'nginx_lua_module'4 self.slug = 'nginx_lua_module'
5 self.release = '0.10.13'5 self.release = '0.10.13'
6 self.base_url = "https://github.com/openresty/lua-nginx-module/tree/v#{self.release}/"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 html_filters.push 'nginx_lua_module/clean_html', 'nginx_lua_module/entries', 'title'11 html_filters.push 'nginx_lua_module/clean_html', 'nginx_lua_module/entries', 'title'
9 12
@@ -15,5 +18,11 @@ module Docs · 5 unmodified lines
15 &copy; 2009&ndash;2018 Yichun "agentzh" Zhang (章亦春), OpenResty Inc.<br>18 &copy; 2009&ndash;2018 Yichun "agentzh" Zhang (章亦春), OpenResty Inc.<br>
16 Licensed under the BSD License.19 Licensed under the BSD License.
17 HTML20 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 end27 end
19end28end
lib/docs/scrapers/nim.rb+5−0
@@ -17,5 +17,10 @@ module Docs · 16 unmodified lines
17 &copy; 2006&ndash;2018 Andreas Rumpf<br>17 &copy; 2006&ndash;2018 Andreas Rumpf<br>
18 Licensed under the MIT License.18 Licensed under the MIT License.
19 HTML19 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 end25 end
21end26end
lib/docs/scrapers/node.rb+7−2
@@ -23,8 +23,8 @@ module Docs · 22 unmodified lines
23 HTML23 HTML
24 24
25 version do25 version do
26 self.release = '11.1.0'26 self.release = '12.8.0'
27 self.base_url = 'https://nodejs.org/dist/latest-v11.x/docs/api/'27 self.base_url = 'https://nodejs.org/dist/latest-v12.x/docs/api/'
28 end28 end
29 29
30 version '10 LTS' do30 version '10 LTS' do
@@ -46,5 +46,10 @@ module Docs · 15 unmodified lines
46 self.release = '4.9.1'46 self.release = '4.9.1'
47 self.base_url = 'https://nodejs.org/dist/latest-v4.x/docs/api/'47 self.base_url = 'https://nodejs.org/dist/latest-v4.x/docs/api/'
48 end48 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
49 end54 end
50end55end
lib/docs/scrapers/nokogiri2.rb+4−0
@@ -19,5 +19,9 @@ module Docs · 18 unmodified lines
19 Patrick Mahoney, Yoko Harada, Akinori Musha, John Shahid, Lars Kanis<br>19 Patrick Mahoney, Yoko Harada, Akinori Musha, John Shahid, Lars Kanis<br>
20 Licensed under the MIT License.20 Licensed under the MIT License.
21 HTML21 HTML
22
23 def get_latest_version(opts)
24 get_latest_github_release('sparklemotion', 'nokogiri', opts)
25 end
22 end26 end
23end27end
lib/docs/scrapers/npm.rb+4−0
@@ -29,5 +29,9 @@ module Docs · 28 unmodified lines
29 Licensed under the npm License.<br>29 Licensed under the npm License.<br>
30 npm is a trademark of npm, Inc.30 npm is a trademark of npm, Inc.
31 HTML31 HTML
32
33 def get_latest_version(opts)
34 get_latest_github_release('npm', 'cli', opts)
35 end
32 end36 end
33end37end
lib/docs/scrapers/numpy.rb+4−0
@@ -49,5 +49,9 @@ module Docs · 48 unmodified lines
49 self.release = '1.10.4'49 self.release = '1.10.4'
50 self.base_url = "https://docs.scipy.org/doc/numpy-#{self.release}/reference/"50 self.base_url = "https://docs.scipy.org/doc/numpy-#{self.release}/reference/"
51 end51 end
52
53 def get_latest_version(opts)
54 get_latest_github_release('numpy', 'numpy', opts)
55 end
52 end56 end
53end57end
lib/docs/scrapers/octave.rb+38−0
@@ -0,0 +1,38 @@
1module Docs
2 class Octave < UrlScraper
3 self.name = 'Octave'
4 self.type = 'octave'
5 self.release = '5.1.0'
6 self.base_url = 'https://octave.org/doc/interpreter/'
7 self.root_path = 'index.html'
8 self.links = {
9 home: 'http://www.octave.org/',
10 code: 'http://www.octave.org/hg/octave'
11 }
12
13 html_filters.push 'octave/clean_html', 'octave/entries', 'title'
14
15 options[:skip] = %w(
16 Copying.html
17 Preface.html
18 Acknowledgements.html
19 Citing-Octave-in-Publications.html
20 How-You-Can-Contribute-to-Octave.html
21 Distribution.html)
22
23 options[:title] = false
24 options[:root_title] = 'GNU Octave'
25
26 options[:attribution] = <<-HTML
27 &copy; 1996&ndash;2018 John W. Eaton<br>
28 Permission is granted to make and distribute verbatim copies of this manual provided the copyright notice and this permission notice are preserved on all copies.<br/>
29Permission is granted to copy and distribute modified versions of this manual under the conditions for verbatim copying, provided that the entire resulting derived work is distributed under the terms of a permission notice identical to this one.</br>
30Permission is granted to copy and distribute translations of this manual into another language, under the above conditions for modified versions.
31 HTML
32
33 def get_latest_version(opts)
34 doc = fetch_doc('https://octave.org/doc/interpreter/', opts)
35 doc.at_css('h1').content.scan(/([0-9.]+)/)[0][0]
36 end
37 end
38end
lib/docs/scrapers/openjdk.rb+20−0
@@ -86,5 +86,25 @@ module Docs · 85 unmodified lines
86 def read_file(path)86 def read_file(path)
87 File.read(path).force_encoding('iso-8859-1').encode('utf-8') rescue nil87 File.read(path).force_encoding('iso-8859-1').encode('utf-8') rescue nil
88 end88 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
89 end109 end
90end110end
lib/docs/scrapers/opentsdb.rb+4−0
@@ -18,5 +18,9 @@ module Docs · 17 unmodified lines
18 &copy; 2010&ndash;2016 The OpenTSDB Authors<br>18 &copy; 2010&ndash;2016 The OpenTSDB Authors<br>
19 Licensed under the GNU LGPLv2.1+ and GPLv3+ licenses.19 Licensed under the GNU LGPLv2.1+ and GPLv3+ licenses.
20 HTML20 HTML
21
22 def get_latest_version(opts)
23 get_latest_github_release('OpenTSDB', 'opentsdb', opts)
24 end
21 end25 end
22end26end
lib/docs/scrapers/padrino.rb+7−3
@@ -2,8 +2,8 @@ module Docs · 1 unmodified line
2 class Padrino < UrlScraper2 class Padrino < UrlScraper
3 self.slug = 'padrino'3 self.slug = 'padrino'
4 self.type = 'rubydoc'4 self.type = 'rubydoc'
5 self.release = '0.14.1'5 self.release = '0.14.4'
6 self.base_url = 'http://www.rubydoc.info/github/padrino/padrino-framework/'6 self.base_url = 'https://www.rubydoc.info/github/padrino/padrino-framework/'
7 self.root_path = 'file/README.rdoc'7 self.root_path = 'file/README.rdoc'
8 self.initial_paths = %w(index2)8 self.initial_paths = %w(index2)
9 self.links = {9 self.links = {
@@ -16,12 +16,16 @@ module Docs · 6 unmodified lines
16 options[:container] = ->(filter) { filter.root_page? ? '#filecontents' : '#content' }16 options[:container] = ->(filter) { filter.root_page? ? '#filecontents' : '#content' }
17 17
18 options[:attribution] = <<-HTML18 options[:attribution] = <<-HTML
19 &copy; 2010&ndash;2016 Padrino<br>19 &copy; 2010&ndash;2019 Padrino<br>
20 Licensed under the MIT License.20 Licensed under the MIT License.
21 HTML21 HTML
22 22
23 stub 'index2' do23 stub 'index2' do
24 request_one(url_for('index')).body24 request_one(url_for('index')).body
25 end25 end
26
27 def get_latest_version(opts)
28 get_github_tags('padrino', 'padrino-framework', opts)[0]['name']
29 end
26 end30 end
27end31end
lib/docs/scrapers/pandas.rb+24−7
@@ -14,40 +14,57 @@ module Docs · 13 unmodified lines
14 options[:container] = '.document'14 options[:container] = '.document'
15 15
16 options[:skip] = %w(internals.html release.html contributing.html whatsnew.html)16 options[:skip] = %w(internals.html release.html contributing.html whatsnew.html)
17 options[:skip_patterns] = [/whatsnew\//]
17 18
18 options[:attribution] = <<-HTML19 options[:attribution] = <<-HTML
19 &copy; 2008&ndash;2012, AQR Capital Management, LLC, Lambda Foundry, Inc. and PyData Development Team<br>20 &copy; 2008&ndash;2012, AQR Capital Management, LLC, Lambda Foundry, Inc. and PyData Development Team<br>
20 Licensed under the 3-clause BSD License.21 Licensed under the 3-clause BSD License.
21 HTML22 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
23 version '0.23' do34 version '0.23' do
24 self.release = '0.23.4'35 self.release = '0.23.4'
25 self.base_url = "http://pandas.pydata.org/pandas-docs/version/#{self.release}/"36 self.base_url = "https://pandas.pydata.org/pandas-docs/version/#{self.release}/"
26 end37 end
27 38
28 version '0.22' do39 version '0.22' do
29 self.release = '0.22.0'40 self.release = '0.22.0'
30 self.base_url = "http://pandas.pydata.org/pandas-docs/version/#{self.release}/"41 self.base_url = "https://pandas.pydata.org/pandas-docs/version/#{self.release}/"
31 end42 end
32 43
33 version '0.21' do44 version '0.21' do
34 self.release = '0.21.0'45 self.release = '0.21.1'
35 self.base_url = "http://pandas.pydata.org/pandas-docs/version/#{self.release}/"46 self.base_url = "https://pandas.pydata.org/pandas-docs/version/#{self.release}/"
36 end47 end
37 48
38 version '0.20' do49 version '0.20' do
39 self.release = '0.20.3'50 self.release = '0.20.3'
40 self.base_url = "http://pandas.pydata.org/pandas-docs/version/#{self.release}/"51 self.base_url = "https://pandas.pydata.org/pandas-docs/version/#{self.release}/"
41 end52 end
42 53
43 version '0.19' do54 version '0.19' do
44 self.release = '0.19.2'55 self.release = '0.19.2'
45 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}/"
46 end57 end
47 58
48 version '0.18' do59 version '0.18' do
49 self.release = '0.18.1'60 self.release = '0.18.1'
50 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]
51 end68 end
52 end69 end
53end70end
lib/docs/scrapers/perl.rb+6−0
@@ -43,5 +43,11 @@ module Docs · 42 unmodified lines
43 self.release = '5.20.2'43 self.release = '5.20.2'
44 self.base_url = "https://perldoc.perl.org/#{self.release}/"44 self.base_url = "https://perldoc.perl.org/#{self.release}/"
45 end45 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
46 end52 end
47end53end
lib/docs/scrapers/phalcon.rb+5−0
@@ -29,5 +29,10 @@ module Docs · 28 unmodified lines
29 self.release = '2.0.13'29 self.release = '2.0.13'
30 self.base_url = 'https://docs.phalconphp.com/en/2.0.0/'30 self.base_url = 'https://docs.phalconphp.com/en/2.0.0/'
31 end31 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 end37 end
33end38end
lib/docs/scrapers/phaser.rb+4−0
@@ -25,5 +25,9 @@ module Docs · 24 unmodified lines
25 &copy; 2016 Richard Davey, Photon Storm Ltd.<br>25 &copy; 2016 Richard Davey, Photon Storm Ltd.<br>
26 Licensed under the MIT License.26 Licensed under the MIT License.
27 HTML27 HTML
28
29 def get_latest_version(opts)
30 get_latest_github_release('photonstorm', 'phaser', opts)
31 end
28 end32 end
29end33end
lib/docs/scrapers/phoenix.rb+5−0
@@ -46,5 +46,10 @@ module Docs · 45 unmodified lines
46 HTML46 HTML
47 end47 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 end54 end
50end55end
lib/docs/scrapers/php.rb+6−0
@@ -66,5 +66,11 @@ module Docs · 65 unmodified lines
66 &copy; 1997&ndash;2018 The PHP Documentation Group<br>66 &copy; 1997&ndash;2018 The PHP Documentation Group<br>
67 Licensed under the Creative Commons Attribution License v3.0 or later.67 Licensed under the Creative Commons Attribution License v3.0 or later.
68 HTML68 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
69 end75 end
70end76end
lib/docs/scrapers/phpunit.rb+6−0
@@ -37,5 +37,11 @@ module Docs · 36 unmodified lines
37 self.release = '4.8'37 self.release = '4.8'
38 self.base_url = "https://phpunit.de/manual/#{release}/en/"38 self.base_url = "https://phpunit.de/manual/#{release}/en/"
39 end39 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 end46 end
41end47end
lib/docs/scrapers/pony.rb+27−0
@@ -0,0 +1,27 @@
1module Docs
2 class Pony < UrlScraper
3 self.type = 'simple'
4 self.release = '0.30.0'
5 self.base_url = 'https://stdlib.ponylang.io/'
6 self.links = {
7 home: 'https://www.ponylang.io/',
8 code: 'https://github.com/ponylang/ponyc'
9 }
10
11 html_filters.push 'pony/clean_html', 'pony/entries'
12
13 options[:attribution] = <<-HTML
14 &copy; 2016-2018, The Pony Developers<br>
15 &copy; 2014-2015, Causality Ltd.<br>
16 Licensed under the BSD 2-Clause License.
17 HTML
18
19 options[:container] = 'article'
20 options[:trailing_slash] = false
21 options[:skip_patterns] = [/src/, /stdlib--index/]
22
23 def get_latest_version(opts)
24 get_latest_github_release('ponylang', 'ponyc', opts)
25 end
26 end
27end
lib/docs/scrapers/postgresql.rb+6−0
@@ -80,5 +80,11 @@ module Docs · 79 unmodified lines
80 80
81 html_filters.insert_before 'postgresql/extract_metadata', 'postgresql/normalize_class_names'81 html_filters.insert_before 'postgresql/extract_metadata', 'postgresql/normalize_class_names'
82 end82 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]
88 end
83 end89 end
84end90end
lib/docs/scrapers/pug.rb+4−0
@@ -18,6 +18,10 @@ module Docs · 17 unmodified lines
18 Licensed under the MIT license.18 Licensed under the MIT license.
19 HTML19 HTML
20 20
21 def get_latest_version(opts)
22 get_npm_version('pug', opts)
23 end
24
21 private25 private
22 26
23 def parse(response) # Hook here because Nokogori removes whitespace from textareas27 def parse(response) # Hook here because Nokogori removes whitespace from textareas
lib/docs/scrapers/puppeteer.rb+5−0
@@ -14,5 +14,10 @@ module Docs · 13 unmodified lines
14 &copy; 2017 Google Inc<br>14 &copy; 2017 Google Inc<br>
15 Licensed under the Apache License 2.0.15 Licensed under the Apache License 2.0.
16 HTML16 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
17 end22 end
18end23end
lib/docs/scrapers/pygame.rb+7−2
@@ -1,7 +1,8 @@
1module Docs1module Docs
2 class Pygame < UrlScraper2 class Pygame < UrlScraper
3 self.type = 'simple'3 self.type = 'simple'
4 self.release = '1.9.4'4 self.release = '1.9.6'
5 self.base_url = 'https://www.pygame.org/docs/'
5 self.root_path = 'py-modindex.html'6 self.root_path = 'py-modindex.html'
6 self.links = {7 self.links = {
7 home: 'https://www.pygame.org/',8 home: 'https://www.pygame.org/',
@@ -13,8 +14,12 @@ module Docs · 5 unmodified lines
13 options[:only_patterns] = [/ref\//]14 options[:only_patterns] = [/ref\//]
14 15
15 options[:attribution] = <<-HTML16 options[:attribution] = <<-HTML
16 &copy; Pygame Developpers.<br>17 &copy; Pygame Developers.<br>
17 Licensed under the GNU LGPL License version 2.1.18 Licensed under the GNU LGPL License version 2.1.
18 HTML19 HTML
20
21 def get_latest_version(opts)
22 get_latest_github_release('pygame', 'pygame', opts)
23 end
19 end24 end
20end25end
lib/docs/scrapers/python.rb+9−4
@@ -24,31 +24,36 @@ module Docs · 23 unmodified lines
24 HTML24 HTML
25 25
26 version '3.7' do # docs.python.org/3.7/download.html26 version '3.7' do # docs.python.org/3.7/download.html
27 self.release = '3.7.0'27 self.release = '3.7.4'
28 self.base_url = 'https://docs.python.org/3.7/'28 self.base_url = 'https://docs.python.org/3.7/'
29 29
30 html_filters.push 'python/entries_v3', 'sphinx/clean_html', 'python/clean_html'30 html_filters.push 'python/entries_v3', 'sphinx/clean_html', 'python/clean_html'
31 end31 end
32 32
33 version '3.6' do # docs.python.org/3.6/download.html33 version '3.6' do # docs.python.org/3.6/download.html
34 self.release = '3.6.6'34 self.release = '3.6.9'
35 self.base_url = 'https://docs.python.org/3.6/'35 self.base_url = 'https://docs.python.org/3.6/'
36 36
37 html_filters.push 'python/entries_v3', 'sphinx/clean_html', 'python/clean_html'37 html_filters.push 'python/entries_v3', 'sphinx/clean_html', 'python/clean_html'
38 end38 end
39 39
40 version '3.5' do # docs.python.org/3.5/download.html40 version '3.5' do # docs.python.org/3.5/download.html
41 self.release = '3.5.3'41 self.release = '3.5.7'
42 self.base_url = 'https://docs.python.org/3.5/'42 self.base_url = 'https://docs.python.org/3.5/'
43 43
44 html_filters.push 'python/entries_v3', 'sphinx/clean_html', 'python/clean_html'44 html_filters.push 'python/entries_v3', 'sphinx/clean_html', 'python/clean_html'
45 end45 end
46 46
47 version '2.7' do # docs.python.org/2.7/download.html47 version '2.7' do # docs.python.org/2.7/download.html
48 self.release = '2.7.13'48 self.release = '2.7.16'
49 self.base_url = 'https://docs.python.org/2.7/'49 self.base_url = 'https://docs.python.org/2.7/'
50 50
51 html_filters.push 'python/entries_v2', 'sphinx/clean_html', 'python/clean_html'51 html_filters.push 'python/entries_v2', 'sphinx/clean_html', 'python/clean_html'
52 end52 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
53 end58 end
54end59end
lib/docs/scrapers/q.rb+4−0
@@ -19,5 +19,9 @@ module Docs · 18 unmodified lines
19 &copy; 2009&ndash;2017 Kristopher Michael Kowal<br>19 &copy; 2009&ndash;2017 Kristopher Michael Kowal<br>
20 Licensed under the MIT License.20 Licensed under the MIT License.
21 HTML21 HTML
22
23 def get_latest_version(opts)
24 get_npm_version('q', opts)
25 end
22 end26 end
23end27end
lib/docs/scrapers/qt.rb+17−2
@@ -103,9 +103,19 @@ module Docs · 102 unmodified lines
103 Licensed under the GNU Free Documentation License, Version 1.3.103 Licensed under the GNU Free Documentation License, Version 1.3.
104 HTML104 HTML
105 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
111 version '5.12' do
112 self.release = '5.12'
113 self.base_url = 'https://doc.qt.io/qt-5.12/'
114 end
115
106 version '5.11' do116 version '5.11' do
107 self.release = '5.11'117 self.release = '5.11'
108 self.base_url = 'https://doc.qt.io/qt-5/'118 self.base_url = 'https://doc.qt.io/archives/qt-5.11/'
109 end119 end
110 120
111 version '5.9' do121 version '5.9' do
@@ -115,7 +125,12 @@ module Docs · 3 unmodified lines
115 125
116 version '5.6' do126 version '5.6' do
117 self.release = '5.6'127 self.release = '5.6'
118 self.base_url = 'https://doc.qt.io/qt-5.6/'128 self.base_url = 'https://doc.qt.io/archives/qt-5.6/'
129 end
130
131 def get_latest_version(opts)
132 doc = fetch_doc('https://doc.qt.io/qt-5/index.html', opts)
133 doc.at_css('.mainContent h1.title').content.sub(/Qt /, '')
119 end134 end
120 end135 end
121end136end
lib/docs/scrapers/ramda.rb+7−3
@@ -1,8 +1,8 @@
1module Docs1module Docs
2 class Ramda < UrlScraper2 class Ramda < UrlScraper
3 self.type = 'ramda'3 self.type = 'ramda'
4 self.release = '0.25.0'4 self.release = '0.26.1'
5 self.base_url = "http://ramdajs.com/#{release}/docs/"5 self.base_url = "https://ramdajs.com/#{release}/docs/"
6 self.links = {6 self.links = {
7 home: 'http://ramdajs.com/',7 home: 'http://ramdajs.com/',
8 code: 'https://github.com/ramda/ramda/'8 code: 'https://github.com/ramda/ramda/'
@@ -15,6 +15,10 @@ module Docs · 6 unmodified lines
15 &copy; 2013&ndash;2016 Scott Sauyet and Michael Hurley<br>15 &copy; 2013&ndash;2016 Scott Sauyet and Michael Hurley<br>
16 Licensed under the MIT License.16 Licensed under the MIT License.
17 HTML17 HTML
18
19 def get_latest_version(opts)
20 doc = fetch_doc('https://ramdajs.com/docs/', opts)
21 doc.at_css('.navbar-brand > .version').content[1..-1]
22 end
18 end23 end
19end24end
20
lib/docs/scrapers/rdoc/minitest.rb+5−0
@@ -21,5 +21,10 @@ module Docs · 20 unmodified lines
21 &copy; Ryan Davis, seattle.rb<br>21 &copy; Ryan Davis, seattle.rb<br>
22 Licensed under the MIT License.22 Licensed under the MIT License.
23 HTML23 HTML
24
25 def get_latest_version(opts)
26 contents = get_github_file_contents('seattlerb', 'minitest', 'History.rdoc', opts)
27 contents.scan(/([0-9.]+)/)[0][0]
28 end
24 end29 end
25end30end
lib/docs/scrapers/rdoc/rails.rb+4−0
@@ -93,5 +93,9 @@ module Docs · 92 unmodified lines
93 version '4.1' do93 version '4.1' do
94 self.release = '4.1.16'94 self.release = '4.1.16'
95 end95 end
96
97 def get_latest_version(opts)
98 get_latest_github_release('rails', 'rails', opts)
99 end
96 end100 end
97end101end
lib/docs/scrapers/rdoc/ruby.rb+11−0
@@ -84,5 +84,16 @@ module Docs · 83 unmodified lines
84 version '2.2' do84 version '2.2' do
85 self.release = '2.2.10'85 self.release = '2.2.10'
86 end86 end
87
88 def get_latest_version(opts)
89 tags = get_github_tags('ruby', 'ruby', opts)
90 tags.each do |tag|
91 version = tag['name'].gsub(/_/, '.')[1..-1]
92
93 if !/^([0-9.]+)$/.match(version).nil? && version.count('.') == 2
94 return version
95 end
96 end
97 end
87 end98 end
88end99end
lib/docs/scrapers/react.rb+5−0
@@ -30,5 +30,10 @@ module Docs · 29 unmodified lines
30 &copy; 2013&ndash;present Facebook Inc.<br>30 &copy; 2013&ndash;present Facebook Inc.<br>
31 Licensed under the Creative Commons Attribution 4.0 International Public License.31 Licensed under the Creative Commons Attribution 4.0 International Public License.
32 HTML32 HTML
33
34 def get_latest_version(opts)
35 doc = fetch_doc('https://reactjs.org/docs/getting-started.html', opts)
36 doc.at_css('a[href="/versions"]').content.strip[1..-1]
37 end
33 end38 end
34end39end
lib/docs/scrapers/react_native.rb+5−0
@@ -30,5 +30,10 @@ module Docs · 29 unmodified lines
30 &copy; 2015&ndash;2018 Facebook Inc.<br>30 &copy; 2015&ndash;2018 Facebook Inc.<br>
31 Licensed under the Creative Commons Attribution 4.0 International Public License.31 Licensed under the Creative Commons Attribution 4.0 International Public License.
32 HTML32 HTML
33
34 def get_latest_version(opts)
35 doc = fetch_doc('https://facebook.github.io/react-native/docs/getting-started.html', opts)
36 doc.at_css('header > a > h3').content
37 end
33 end38 end
34end39end
lib/docs/scrapers/redis.rb+6−0
@@ -19,5 +19,11 @@ module Docs · 18 unmodified lines
19 &copy; 2009&ndash;2018 Salvatore Sanfilippo<br>19 &copy; 2009&ndash;2018 Salvatore Sanfilippo<br>
20 Licensed under the Creative Commons Attribution-ShareAlike License 4.0.20 Licensed under the Creative Commons Attribution-ShareAlike License 4.0.
21 HTML21 HTML
22
23 def get_latest_version(opts)
24 body = fetch('http://download.redis.io/redis-stable/00-RELEASENOTES', opts)
25 body = body.lines[1..-1].join
26 body.scan(/Redis ([0-9.]+)/)[0][0]
27 end
22 end28 end
23end29end
lib/docs/scrapers/redux.rb+4−0
@@ -20,5 +20,9 @@ module Docs · 19 unmodified lines
20 stub '' do20 stub '' do
21 request_one('http://redux.js.org/index.html').body21 request_one('http://redux.js.org/index.html').body
22 end22 end
23
24 def get_latest_version(opts)
25 get_npm_version('redux', opts)
26 end
23 end27 end
24end28end
lib/docs/scrapers/relay.rb+5−0
@@ -18,5 +18,10 @@ module Docs · 17 unmodified lines
18 &copy; 2013&ndash;present Facebook Inc.<br>18 &copy; 2013&ndash;present Facebook Inc.<br>
19 Licensed under the BSD License.19 Licensed under the BSD License.
20 HTML20 HTML
21
22 def get_latest_version(opts)
23 doc = fetch_doc('http://facebook.github.io/relay/en/', opts)
24 doc.at_css('header > a > h3').content[1..-1]
25 end
21 end26 end
22end27end
lib/docs/scrapers/requirejs.rb+4−0
@@ -30,5 +30,9 @@ module Docs · 29 unmodified lines
30 &copy; jQuery Foundation and other contributors<br>30 &copy; jQuery Foundation and other contributors<br>
31 Licensed under the MIT License.31 Licensed under the MIT License.
32 HTML32 HTML
33
34 def get_latest_version(opts)
35 get_npm_version('requirejs', opts)
36 end
33 end37 end
34end38end
lib/docs/scrapers/rethinkdb.rb+4−0
@@ -58,6 +58,10 @@ module Docs · 57 unmodified lines
58 CODE58 CODE
59 end59 end
60 60
61 def get_latest_version(opts)
62 get_latest_github_release('rethinkdb', 'rethinkdb', opts)
63 end
64
61 private65 private
62 66
63 def process_response?(response)67 def process_response?(response)
lib/docs/scrapers/rust.rb+6−0
@@ -39,6 +39,12 @@ module Docs · 38 unmodified lines
39 Licensed under the Apache License, Version 2.0 or the MIT license, at your option.39 Licensed under the Apache License, Version 2.0 or the MIT license, at your option.
40 HTML40 HTML
41 41
42 def get_latest_version(opts)
43 doc = fetch_doc('https://www.rust-lang.org/', opts)
44 label = doc.at_css('.button-download + p > a').content
45 label.sub(/Version /, '')
46 end
47
42 private48 private
43 49
44 REDIRECT_RGX = /http-equiv="refresh"/i50 REDIRECT_RGX = /http-equiv="refresh"/i
lib/docs/scrapers/rxjs.rb+94−0
@@ -0,0 +1,94 @@
1require 'yajl/json_gem'
2
3module Docs
4 class Rxjs < UrlScraper
5 self.name = 'RxJS'
6 self.type = 'rxjs'
7 self.release = '6.5.2'
8 self.base_url = 'https://rxjs.dev/'
9 self.root_path = 'guide/overview'
10 self.links = {
11 home: 'https://rxjs.dev/',
12 code: 'https://github.com/ReactiveX/rxjs'
13 }
14
15 html_filters.push 'rxjs/clean_html', 'rxjs/entries'
16
17 options[:follow_links] = false
18 options[:only_patterns] = [/guide\//, /api\//]
19 options[:skip_patterns] = [/api\/([^\/]+)\.json/]
20 options[:fix_urls_before_parse] = ->(url) do
21 url.sub! %r{\A(\.\/)?guide/}, '/guide/'
22 url.sub! %r{\Aapi/}, '/api/'
23 url.sub! %r{\Agenerated/}, '/generated/'
24 url
25 end
26
27 options[:max_image_size] = 256_000
28
29 options[:attribution] = <<-HTML
30 &copy; 2015&ndash;2018 Google, Inc., Netflix, Inc., Microsoft Corp. and contributors.<br>
31 Code licensed under an Apache-2.0 License. Documentation licensed under CC BY 4.0.
32 HTML
33
34 def get_latest_version(opts)
35 json = fetch_json('https://rxjs.dev/generated/navigation.json', opts)
36 json['__versionInfo']['raw']
37 end
38
39 private
40
41 def initial_urls
42 initial_urls = []
43
44 Request.run "#{self.class.base_url}generated/navigation.json" do |response|
45 data = JSON.parse(response.body)
46 dig = ->(entry) do
47 initial_urls << url_for("generated/docs/#{entry['url']}.json") if entry['url'] && entry['url'] != 'api'
48 entry['children'].each(&dig) if entry['children']
49 end
50 data['SideNav'].each(&dig)
51 end
52
53 Request.run "#{self.class.base_url}generated/docs/api/api-list.json" do |response|
54 data = JSON.parse(response.body)
55 dig = ->(entry) do
56 initial_urls << url_for("generated/docs/#{entry['path']}.json") if entry['path']
57 initial_urls << url_for("generated/docs/api/#{entry['name']}.json") if entry['name'] && !entry['path']
58 entry['items'].each(&dig) if entry['items']
59 end
60 data.each(&dig)
61 end
62
63 initial_urls.select do |url|
64 options[:only_patterns].any? { |pattern| url =~ pattern } &&
65 options[:skip_patterns].none? { |pattern| url =~ pattern }
66 end
67 end
68
69 def handle_response(response)
70 if response.mime_type.include?('json')
71 begin
72 response.options[:response_body] = JSON.parse(response.body)['contents']
73 rescue JSON::ParserError
74 response.options[:response_body] = ''
75 end
76 response.headers['Content-Type'] = 'text/html'
77 response.url.path = response.url.path.sub('/generated/docs/', '/').remove('.json')
78 response.effective_url.path = response.effective_url.path.sub('/generated/docs/', '/').remove('.json')
79 end
80 super
81 end
82
83 def parse(response)
84 response.body.gsub! '<code-example', '<pre'
85 response.body.gsub! '</code-example', '</pre'
86 response.body.gsub! '<code-pane', '<pre'
87 response.body.gsub! '</code-pane', '</pre'
88 response.body.gsub! '<live-example></live-example>', 'live example'
89 response.body.gsub! '<live-example', '<span'
90 response.body.gsub! '</live-example', '</span'
91 super
92 end
93 end
94end
lib/docs/scrapers/salt_stack.rb+36−0
@@ -0,0 +1,36 @@
1module Docs
2 # The official documentation website is heavily rate-limited
3 #
4 # The documentation can be generated like this (replace 2019.2 with the correct tag):
5 # $ git clone https://github.com/saltstack/salt.git --branch 2019.2 --depth 1
6 # $ cd salt/doc
7 # $ pip install sphinx
8 # $ make html
9 #
10 # The generated html can be found in salt/doc/_build/html
11 class SaltStack < FileScraper
12 self.type = 'simple'
13 self.slug = 'saltstack'
14 self.release = '2019.2.0'
15 self.base_url = 'https://docs.saltstack.com/en/latest/'
16 self.root_path = 'ref/index.html'
17 self.links = {
18 home: 'https://www.saltstack.com/',
19 code: 'https://github.com/saltstack/salt'
20 }
21
22 html_filters.push 'salt_stack/clean_html', 'salt_stack/entries'
23
24 options[:only_patterns] = [/all\//]
25 options[:container] = '.body-content > .section'
26
27 options[:attribution] = <<-HTML
28 &copy; 2019 SaltStack.<br>
29 Licensed under the Apache License, Version 2.0.
30 HTML
31
32 def get_latest_version(opts)
33 get_latest_github_release('saltstack', 'salt', opts)
34 end
35 end
36end
lib/docs/scrapers/sass.rb+4−0
@@ -23,5 +23,9 @@ module Docs · 22 unmodified lines
23 &copy; 2006&ndash;2016 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein<br>23 &copy; 2006&ndash;2016 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein<br>
24 Licensed under the MIT License.24 Licensed under the MIT License.
25 HTML25 HTML
26
27 def get_latest_version(opts)
28 get_latest_github_release('sass', 'libsass', opts)
29 end
26 end30 end
27end31end
lib/docs/scrapers/scala.rb+61−0
@@ -0,0 +1,61 @@
1module Docs
2 class Scala < FileScraper
3 self.name = 'Scala'
4 self.type = 'scala'
5 self.links = {
6 home: 'http://www.scala-lang.org/',
7 code: 'https://github.com/scala/scala'
8 }
9
10 options[:container] = '#content-container'
11 options[:attribution] = <<-HTML
12 &copy; 2002-2019 EPFL, with contributions from Lightbend.<br>
13 Licensed under the Apache License, Version 2.0.
14 HTML
15
16 # https://downloads.lightbend.com/scala/2.13.0/scala-docs-2.13.0.zip
17 # Extract api/scala-library into docs/scala~2.13_library
18 version '2.13 Library' do
19 self.release = '2.13.0'
20 self.base_url = 'https://www.scala-lang.org/api/2.13.0/'
21 self.root_path = 'index.html'
22
23 html_filters.push 'scala/entries', 'scala/clean_html'
24 end
25
26 # https://downloads.lightbend.com/scala/2.13.0/scala-docs-2.13.0.zip
27 # Extract api/scala-reflect into docs/scala~2.13_reflection
28 version '2.13 Reflection' do
29 self.release = '2.13.0'
30 self.base_url = 'https://www.scala-lang.org/api/2.13.0/scala-reflect/'
31 self.root_path = 'index.html'
32
33 html_filters.push 'scala/entries', 'scala/clean_html'
34 end
35
36 # https://downloads.lightbend.com/scala/2.12.9/scala-docs-2.12.9.zip
37 # Extract api/scala-library into docs/scala~2.12_library
38 version '2.12 Library' do
39 self.release = '2.12.9'
40 self.base_url = 'https://www.scala-lang.org/api/2.12.9/'
41 self.root_path = 'index.html'
42
43 html_filters.push 'scala/entries', 'scala/clean_html'
44 end
45
46 # https://downloads.lightbend.com/scala/2.12.9/scala-docs-2.12.9.zip
47 # Extract api/scala-reflect into docs/scala~2.12_reflection
48 version '2.12 Reflection' do
49 self.release = '2.12.9'
50 self.base_url = 'https://www.scala-lang.org/api/2.12.9/scala-reflect/'
51 self.root_path = 'index.html'
52
53 html_filters.push 'scala/entries', 'scala/clean_html'
54 end
55
56 def get_latest_version(opts)
57 doc = fetch_doc('https://www.scala-lang.org/api/current/', opts)
58 doc.at_css('#doc-version').content
59 end
60 end
61end
lib/docs/scrapers/scikit_image.rb+5−0
@@ -20,5 +20,10 @@ module Docs · 19 unmodified lines
20 &copy; 2011 the scikit-image team<br>20 &copy; 2011 the scikit-image team<br>
21 Licensed under the BSD 3-clause License.21 Licensed under the BSD 3-clause License.
22 HTML22 HTML
23
24 def get_latest_version(opts)
25 tags = get_github_tags('scikit-image', 'scikit-image', opts)
26 tags[0]['name'][1..-1]
27 end
23 end28 end
24end29end
lib/docs/scrapers/scikit_learn.rb+4−0
@@ -25,5 +25,9 @@ module Docs · 24 unmodified lines
25 Licensed under the 3-clause BSD License.25 Licensed under the 3-clause BSD License.
26 HTML26 HTML
27 27
28 def get_latest_version(opts)
29 doc = fetch_doc('https://scikit-learn.org/stable/documentation.html', opts)
30 doc.at_css('.body h1').content.scan(/([0-9.]+)/)[0][0]
31 end
28 end32 end
29end33end
lib/docs/scrapers/sinon.rb+5−0
@@ -52,5 +52,10 @@ module Docs · 51 unmodified lines
52 self.release = '1.17.7'52 self.release = '1.17.7'
53 self.base_url = "https://sinonjs.org/releases/v#{release}/"53 self.base_url = "https://sinonjs.org/releases/v#{release}/"
54 end54 end
55
56 def get_latest_version(opts)
57 body = fetch('https://sinonjs.org/', opts)
58 body.scan(/\/releases\/v([0-9.]+)/)[0][0]
59 end
55 end60 end
56end61end
lib/docs/scrapers/socketio.rb+4−0
@@ -20,5 +20,9 @@ module Docs · 19 unmodified lines
20 &copy; 2014&ndash;2015 Automattic<br>20 &copy; 2014&ndash;2015 Automattic<br>
21 Licensed under the MIT License.21 Licensed under the MIT License.
22 HTML22 HTML
23
24 def get_latest_version(opts)
25 get_npm_version('socket.io', opts)
26 end
23 end27 end
24end28end
lib/docs/scrapers/sqlite.rb+5−0
@@ -41,6 +41,11 @@ module Docs · 40 unmodified lines
41 41
42 options[:attribution] = 'SQLite is in the Public Domain.'42 options[:attribution] = 'SQLite is in the Public Domain.'
43 43
44 def get_latest_version(opts)
45 doc = fetch_doc('https://sqlite.org/chronology.html', opts)
46 doc.at_css('#chrontab > tbody > tr > td:last-child > a').content
47 end
48
44 private49 private
45 50
46 def parse(response)51 def parse(response)
lib/docs/scrapers/statsmodels.rb+4−0
@@ -21,5 +21,9 @@ module Docs · 20 unmodified lines
21 Licensed under the 3-clause BSD License.21 Licensed under the 3-clause BSD License.
22 HTML22 HTML
23 23
24 def get_latest_version(opts)
25 doc = fetch_doc('http://www.statsmodels.org/stable/', opts)
26 doc.at_css('.sphinxsidebarwrapper h3 + p > b').content[1..-1]
27 end
24 end28 end
25end29end
lib/docs/scrapers/support_tables.rb+6−0
@@ -178,5 +178,11 @@ module Docs · 177 unmodified lines
178 </p>178 </p>
179 </div>179 </div>
180 HTML180 HTML
181
182 def get_latest_version(opts)
183 body = fetch('https://feeds.feedburner.com/WhenCanIUse?format=xml', opts)
184 timestamp = body.scan(/<updated>([^<]+)<\/updated>/)[0][0]
185 DateTime.parse(timestamp).to_time.to_i
186 end
181 end187 end
182end188end
lib/docs/scrapers/symfony.rb+4−0
@@ -70,5 +70,9 @@ module Docs · 69 unmodified lines
70 self.release = '2.7.35'70 self.release = '2.7.35'
71 self.base_url = "https://api.symfony.com/#{version}/"71 self.base_url = "https://api.symfony.com/#{version}/"
72 end72 end
73
74 def get_latest_version(opts)
75 get_latest_github_release('symfony', 'symfony', opts)
76 end
73 end77 end
74end78end
lib/docs/scrapers/tcl_tk.rb+5−0
@@ -25,5 +25,10 @@ module Docs · 24 unmodified lines
25 options[:attribution] = <<-HTML25 options[:attribution] = <<-HTML
26 Licensed under <a href="http://tcl.tk/software/tcltk/license.html">Tcl/Tk terms</a>26 Licensed under <a href="http://tcl.tk/software/tcltk/license.html">Tcl/Tk terms</a>
27 HTML27 HTML
28
29 def get_latest_version(opts)
30 doc = fetch_doc('https://www.tcl.tk/man/tcl/contents.htm', opts)
31 doc.at_css('h2').content.scan(/Tk([0-9.]+)/)[0][0]
32 end
28 end33 end
29end34end
lib/docs/scrapers/tensorflow.rb+4−0
@@ -56,6 +56,10 @@ module Docs · 55 unmodified lines
56 /\Aextend/]56 /\Aextend/]
57 end57 end
58 58
59 def get_latest_version(opts)
60 get_latest_github_release('tensorflow', 'tensorflow', opts)
61 end
62
59 private63 private
60 64
61 def parse(response)65 def parse(response)
lib/docs/scrapers/terraform.rb+5−0
@@ -18,5 +18,10 @@ module Docs · 17 unmodified lines
18 &copy; 2018 HashiCorp</br>18 &copy; 2018 HashiCorp</br>
19 Licensed under the MPL 2.0 License.19 Licensed under the MPL 2.0 License.
20 HTML20 HTML
21
22 def get_latest_version(opts)
23 contents = get_github_file_contents('hashicorp', 'terraform-website', 'content/config.rb', opts)
24 contents.scan(/version\s+=\s+"([0-9.]+)"/)[0][0]
25 end
21 end26 end
22end27end
lib/docs/scrapers/trio.rb+31−0
@@ -0,0 +1,31 @@
1module Docs
2 class Trio < UrlScraper
3 self.type = 'simple'
4 self.release = '0.12.1'
5 self.base_url = 'https://trio.readthedocs.io/en/v0.12.1/'
6 self.root_path = 'index.html'
7 self.links = {
8 home: 'https://trio.readthedocs.io/',
9 code: 'https://github.com/python-trio/trio'
10 }
11
12 html_filters.push 'trio/entries', 'trio/clean_html'
13
14 options[:only_patterns] = [
15 /reference-core/,
16 /reference-io/,
17 /reference-testing/,
18 /reference-hazmat/,
19 ]
20
21 options[:attribution] = <<-HTML
22 &copy; 2017 Nathaniel J. Smith<br>
23 Licensed under the MIT License.
24 HTML
25
26 def get_latest_version(opts)
27 doc = fetch_doc('https://trio.readthedocs.io/en/stable/', opts)
28 doc.at_css('.rst-other-versions a[href^="/en/v"]').content[1..-1]
29 end
30 end
31end
lib/docs/scrapers/twig.rb+5−0
@@ -28,5 +28,10 @@ module Docs · 27 unmodified lines
28 self.release = '1.34.3'28 self.release = '1.34.3'
29 self.base_url = 'https://twig.symfony.com/doc/1.x/'29 self.base_url = 'https://twig.symfony.com/doc/1.x/'
30 end30 end
31
32 def get_latest_version(opts)
33 tags = get_github_tags('twigphp', 'Twig', opts)
34 tags[0]['name'][1..-1]
35 end
31 end36 end
32end37end
lib/docs/scrapers/typescript.rb+4−0
@@ -24,6 +24,10 @@ module Docs · 23 unmodified lines
24 &copy; Microsoft and other contributors<br>24 &copy; Microsoft and other contributors<br>
25 Licensed under the Apache License, Version 2.0.25 Licensed under the Apache License, Version 2.0.
26 HTML26 HTML
27
28 def get_latest_version(opts)
29 get_latest_github_release('Microsoft', 'TypeScript', opts)
30 end
27 end31 end
28end32end
29 33
lib/docs/scrapers/underscore.rb+5−0
@@ -20,5 +20,10 @@ module Docs · 19 unmodified lines
20 &copy; 2009&ndash;2018 Jeremy Ashkenas, DocumentCloud and Investigative Reporters &amp; Editors<br>20 &copy; 2009&ndash;2018 Jeremy Ashkenas, DocumentCloud and Investigative Reporters &amp; Editors<br>
21 Licensed under the MIT License.21 Licensed under the MIT License.
22 HTML22 HTML
23
24 def get_latest_version(opts)
25 doc = fetch_doc('https://underscorejs.org/', opts)
26 doc.at_css('.version').content[1...-1]
27 end
23 end28 end
24end29end
lib/docs/scrapers/vagrant.rb+5−0
@@ -18,5 +18,10 @@ module Docs · 17 unmodified lines
18 &copy; 2010&ndash;2018 Mitchell Hashimoto<br>18 &copy; 2010&ndash;2018 Mitchell Hashimoto<br>
19 Licensed under the MPL 2.0 License.19 Licensed under the MPL 2.0 License.
20 HTML20 HTML
21
22 def get_latest_version(opts)
23 contents = get_github_file_contents('hashicorp', 'vagrant', 'website/config.rb', opts)
24 contents.scan(/version\s+=\s+"([0-9.]+)"/)[0][0]
25 end
21 end26 end
22end27end
lib/docs/scrapers/vue.rb+4−0
@@ -32,5 +32,9 @@ module Docs · 31 unmodified lines
32 self.root_path = '/guide/index.html'32 self.root_path = '/guide/index.html'
33 self.initial_paths = %w(/api/index.html)33 self.initial_paths = %w(/api/index.html)
34 end34 end
35
36 def get_latest_version(opts)
37 get_latest_github_release('vuejs', 'vue', opts)
38 end
35 end39 end
36end40end
lib/docs/scrapers/vue_router.rb+29−0
@@ -0,0 +1,29 @@
1module Docs
2 class VueRouter < UrlScraper
3 self.name = 'Vue Router'
4 self.slug = 'vue_router'
5 self.type = 'simple'
6 self.release = '3.1.2'
7 self.base_url = 'https://router.vuejs.org/'
8 self.links = {
9 home: 'https://router.vuejs.org',
10 code: 'https://github.com/vuejs/vue-router'
11 }
12
13 html_filters.push 'vue_router/entries', 'vue_router/clean_html'
14
15 options[:skip_patterns] = [
16 # Other languages
17 /^(zh|ja|ru|kr|fr)\//,
18 ]
19
20 options[:attribution] = <<-HTML
21 &copy; 2013&ndash;present Evan You<br>
22 Licensed under the MIT License.
23 HTML
24
25 def get_latest_version(opts)
26 get_latest_github_release('vuejs', 'vue-router', opts)
27 end
28 end
29end
lib/docs/scrapers/vuex.rb+27−0
@@ -0,0 +1,27 @@
1module Docs
2 class Vuex < UrlScraper
3 self.type = 'simple'
4 self.release = '3.1.1'
5 self.base_url = 'https://vuex.vuejs.org/'
6 self.links = {
7 home: 'https://vuex.vuejs.org',
8 code: 'https://github.com/vuejs/vuex'
9 }
10
11 html_filters.push 'vuex/entries', 'vuex/clean_html'
12
13 options[:skip_patterns] = [
14 # Other languages
15 /^(zh|ja|ru|kr|fr|ptbr)\//,
16 ]
17
18 options[:attribution] = <<-HTML
19 &copy; 2015&ndash;present Evan You<br>
20 Licensed under the MIT License.
21 HTML
22
23 def get_latest_version(opts)
24 get_npm_version('vuex', opts)
25 end
26 end
27end
lib/docs/scrapers/vulkan.rb+5−0
@@ -20,5 +20,10 @@ module Docs · 19 unmodified lines
20 Licensed under the Creative Commons Attribution 4.0 International License.<br>20 Licensed under the Creative Commons Attribution 4.0 International License.<br>
21 Vulkan and the Vulkan logo are registered trademarks of the Khronos Group Inc.21 Vulkan and the Vulkan logo are registered trademarks of the Khronos Group Inc.
22 HTML22 HTML
23
24 def get_latest_version(opts)
25 tags = get_github_tags('KhronosGroup', 'Vulkan-Docs', opts)
26 tags[0]['name'][1..-1]
27 end
23 end28 end
24end29end
lib/docs/scrapers/webpack.rb+4−0
@@ -68,5 +68,9 @@ module Docs · 67 unmodified lines
68 Licensed under the MIT License.68 Licensed under the MIT License.
69 HTML69 HTML
70 end70 end
71
72 def get_latest_version(opts)
73 get_npm_version('webpack', opts)
74 end
71 end75 end
72end76end
lib/docs/scrapers/wordpress.rb+44−0
@@ -0,0 +1,44 @@
1module Docs
2 class Wordpress < UrlScraper
3 self.name = 'WordPress'
4 self.type = 'wordpress'
5 self.release = '5.2.2'
6 self.base_url = 'https://developer.wordpress.org/reference/'
7 self.initial_paths = %w(
8 functions/
9 hooks/
10 classes/
11 )
12
13 self.links = {
14 home: 'https://wordpress.org/',
15 code: 'https://github.com/WordPress/WordPress'
16 }
17
18 html_filters.push 'wordpress/entries', 'wordpress/clean_html'
19
20 options[:container] = '#content-area'
21 options[:trailing_slash] = false
22 options[:only_patterns] = [
23 /\Afunctions\//,
24 /\Ahooks\//,
25 /\Aclasses\//
26 ]
27
28 options[:skip_patterns] = [
29 /\Afunctions\/page\/\d+/,
30 /\Ahooks\/page\/\d+/,
31 /\Aclasses\/page\/\d+/
32 ]
33
34 options[:attribution] = <<-HTML
35 &copy; 2003&ndash;2019 WordPress Foundation<br>
36 Licensed under the GNU GPLv2+ License.
37 HTML
38
39 def get_latest_version(opts)
40 doc = fetch_doc('https://wordpress.org/download/releases/', opts)
41 doc.at_css('.releases.latest td').content
42 end
43 end
44end
lib/docs/scrapers/yarn.rb+4−0
@@ -20,5 +20,9 @@ module Docs · 19 unmodified lines
20 &copy; 2016&ndash;present Yarn Contributors<br>20 &copy; 2016&ndash;present Yarn Contributors<br>
21 Licensed under the BSD License.21 Licensed under the BSD License.
22 HTML22 HTML
23
24 def get_latest_version(opts)
25 get_latest_github_release('yarnpkg', 'yarn', opts)
26 end
23 end27 end
24end28end
lib/docs/scrapers/yii.rb+4−0
@@ -34,5 +34,9 @@ module Docs · 33 unmodified lines
34 34
35 options[:container] = '.grid_9'35 options[:container] = '.grid_9'
36 end36 end
37
38 def get_latest_version(opts)
39 get_latest_github_release('yiisoft', 'yii2', opts)
40 end
37 end41 end
38end42end
lib/tasks/assets.thor+3−0
@@ -14,6 +14,9 @@ class AssetsCLI < Thor · 13 unmodified lines
14 option :keep, type: :numeric, default: 0, desc: 'Number of old assets to keep'14 option :keep, type: :numeric, default: 0, desc: 'Number of old assets to keep'
15 option :verbose, type: :boolean15 option :verbose, type: :boolean
16 def compile16 def compile
17 load 'tasks/sprites.thor'
18 invoke 'sprites:generate', [], :remove_public_icons => true, :verbose => options[:verbose]
19
17 manifest.compile App.assets_compile20 manifest.compile App.assets_compile
18 manifest.clean(options[:keep]) if options[:clean]21 manifest.clean(options[:keep]) if options[:clean]
19 end22 end
lib/tasks/sprites.thor+213−0
@@ -0,0 +1,213 @@
1class SpritesCLI < Thor
2 def self.to_s
3 'Sprites'
4 end
5
6 def initialize(*args)
7 require 'docs'
8 require 'chunky_png'
9 require 'fileutils'
10 super
11 end
12
13 desc 'generate [--remove-public-icons] [--verbose]', 'Generate the documentation icon spritesheets'
14 option :remove_public_icons, type: :boolean, desc: 'Remove public/icons after generating the spritesheets'
15 option :verbose, type: :boolean
16 def generate
17 items = get_items
18 items_with_icons = items.select {|item| item[:has_icons]}
19 items_without_icons = items.select {|item| !item[:has_icons]}
20 icons_per_row = Math.sqrt(items_with_icons.length).ceil
21
22 bg_color = get_sidebar_background
23
24 items_with_icons.each_with_index do |item, index|
25 item[:row] = (index / icons_per_row).floor
26 item[:col] = index - item[:row] * icons_per_row
27
28 item[:icon_16] = get_icon(item[:path_16], 16)
29 item[:icon_32] = get_icon(item[:path_32], 32)
30
31 item[:dark_icon_fix] = needs_dark_icon_fix(item[:icon_32], bg_color)
32 end
33
34 return unless items_with_icons.length > 0
35
36 log_details(items_with_icons, icons_per_row)
37
38 generate_spritesheet(16, items_with_icons, 'assets/images/sprites/docs.png') {|item| item[:icon_16]}
39 generate_spritesheet(32, items_with_icons, 'assets/images/sprites/docs@2x.png') {|item| item[:icon_32]}
40
41 # Add Mongoose's icon details to docs without custom icons
42 default_item = items_with_icons.find {|item| item[:type] == 'mongoose'}
43 items_without_icons.each do |item|
44 item[:row] = default_item[:row]
45 item[:col] = default_item[:col]
46 item[:dark_icon_fix] = default_item[:dark_icon_fix]
47 end
48
49 save_manifest(items, icons_per_row, 'assets/images/sprites/docs.json')
50
51 if options[:remove_public_icons]
52 logger.info('Removing public/icons')
53 FileUtils.rm_rf('public/icons')
54 end
55 end
56
57 private
58
59 def get_items
60 items = Docs.all.map do |doc|
61 base_path = "public/icons/docs/#{doc.slug}"
62 {
63 :type => doc.slug,
64 :path_16 => "#{base_path}/16.png",
65 :path_32 => "#{base_path}/16@2x.png"
66 }
67 end
68
69 # Checking paths against an array of possible paths is faster than 200+ File.exist? calls
70 files = Dir.glob('public/icons/docs/**/*.png')
71
72 items.each do |item|
73 item[:has_icons] = files.include?(item[:path_16]) && files.include?(item[:path_32])
74 end
75 end
76
77 def get_icon(path, max_size)
78 icon = ChunkyPNG::Image.from_file(path)
79
80 # Check if the icon is too big
81 # If it is, resize the image without changing the aspect ratio
82 if icon.width > max_size || icon.height > max_size
83 ratio = icon.width.to_f / icon.height
84 new_width = (icon.width >= icon.height ? max_size : max_size * ratio).floor
85 new_height = (icon.width >= icon.height ? max_size / ratio : max_size).floor
86
87 logger.warn("Icon #{path} is too big: max size is #{max_size} x #{max_size}, icon is #{icon.width} x #{icon.height}, resizing to #{new_width} x #{new_height}")
88
89 icon.resample_nearest_neighbor!(new_width, new_height)
90 end
91
92 icon
93 end
94
95 def get_sidebar_background
96 # This is a hacky way to get the background color of the sidebar
97 # Unfortunately, it's not possible to get the value of a SCSS variable from a Thor task
98 # Because hard-coding the value is even worse, we extract it using some regex
99 path = 'assets/stylesheets/global/_variables-dark.scss'
100 regex = /--sidebarBackground:\s+([^;]+);/
101 ChunkyPNG::Color.parse(File.read(path)[regex, 1])
102 end
103
104 def needs_dark_icon_fix(icon, bg_color)
105 # Determine whether the icon needs to be grayscaled if the user has enabled the dark theme
106 # The logic is roughly based on https://www.w3.org/TR/2008/REC-WCAG20-20081211/#visual-audio-contrast
107 contrast = icon.pixels.select {|pixel| ChunkyPNG::Color.a(pixel) > 0}.map do |pixel|
108 get_contrast(bg_color, pixel)
109 end
110
111 avg = contrast.reduce(:+) / contrast.size.to_f
112 avg < 3.5
113 end
114
115 def get_contrast(base, other)
116 # Calculating the contrast ratio as described in the WCAG 2.0:
117 # https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef
118 l1 = get_luminance(base) + 0.05
119 l2 = get_luminance(other) + 0.05
120 ratio = l1 / l2
121 l2 > l1 ? 1 / ratio : ratio
122 end
123
124 def get_luminance(color)
125 rgb = [
126 ChunkyPNG::Color.r(color).to_f,
127 ChunkyPNG::Color.g(color).to_f,
128 ChunkyPNG::Color.b(color).to_f
129 ]
130
131 # Calculating the relative luminance as described in the WCAG 2.0:
132 # https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef
133
134 rgb.map! do |value|
135 value /= 255
136 value < 0.03928 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4
137 end
138
139 0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2]
140 end
141
142 def generate_spritesheet(size, items_with_icons, output_path, &item_to_icon)
143 logger.info("Generating spritesheet #{output_path} with icons of size #{size} x #{size}")
144
145 icons_per_row = Math.sqrt(items_with_icons.length).ceil
146 spritesheet = ChunkyPNG::Image.new(size * icons_per_row, size * icons_per_row)
147
148 items_with_icons.each do |item|
149 icon = item_to_icon.call(item)
150
151 # Calculate the base coordinates
152 base_x = item[:col] * size
153 base_y = item[:row] * size
154
155 # Center the icon if it's not a perfect rectangle
156 x = base_x + ((size - icon.width) / 2).floor
157 y = base_y + ((size - icon.height) / 2).floor
158
159 spritesheet.compose!(icon, x, y)
160 end
161
162 FileUtils.mkdir_p(File.dirname(output_path))
163 spritesheet.save(output_path)
164 end
165
166 def save_manifest(items, icons_per_row, path)
167 logger.info("Saving spritesheet details to #{path}")
168
169 FileUtils.mkdir_p(File.dirname(path))
170
171 # Only save the details that the scss file needs
172 manifest_items = items.map do |item|
173 {
174 :type => item[:type],
175 :row => item[:row],
176 :col => item[:col],
177 :dark_icon_fix => item[:dark_icon_fix]
178 }
179 end
180
181 manifest = {:icons_per_row => icons_per_row, :items => manifest_items}
182
183 File.open(path, 'w') do |f|
184 f.write(JSON.generate(manifest))
185 end
186 end
187
188 def log_details(items_with_icons, icons_per_row)
189 logger.debug("Amount of icons: #{items_with_icons.length}")
190 logger.debug("Amount of icons needing the dark icon fix: #{items_with_icons.count {|item| item[:dark_icon_fix]}}")
191 logger.debug("Amount of icons per row: #{icons_per_row}")
192
193 max_type_length = items_with_icons.map {|item| item[:type].length}.max
194 border = "+#{'-' * (max_type_length + 2)}+#{'-' * 5}+#{'-' * 8}+#{'-' * 15}+"
195
196 logger.debug(border)
197 logger.debug("| #{'Type'.ljust(max_type_length)} | Row | Column | Dark icon fix |")
198 logger.debug(border)
199
200 items_with_icons.each do |item|
201 logger.debug("| #{item[:type].ljust(max_type_length)} | #{item[:row].to_s.ljust(3)} | #{item[:col].to_s.ljust(6)} | #{(item[:dark_icon_fix] ? 'Yes' : 'No').ljust(13)} |")
202 end
203
204 logger.debug(border)
205 end
206
207 def logger
208 @logger ||= Logger.new($stdout).tap do |logger|
209 logger.level = options[:verbose] ? Logger::DEBUG : Logger::INFO
210 logger.formatter = proc {|severity, datetime, progname, msg| "#{msg}\n"}
211 end
212 end
213end
lib/tasks/updates.thor+315−0
@@ -0,0 +1,315 @@
1class UpdatesCLI < Thor
2 # The GitHub user that is allowed to upload reports
3 UPLOAD_USER = 'devdocs-bot'
4
5 # The repository to create an issue in when uploading the results
6 UPLOAD_REPO = 'freeCodeCamp/devdocs'
7
8 def self.to_s
9 'Updates'
10 end
11
12 def initialize(*args)
13 require 'docs'
14 require 'progress_bar'
15 require 'terminal-table'
16 require 'date'
17 super
18 end
19
20 desc 'check [--github-token] [--upload] [--verbose] [doc]...', 'Check for outdated documentations'
21 option :github_token, :type => :string
22 option :upload, :type => :boolean
23 option :verbose, :type => :boolean
24 def check(*names)
25 # Convert names to a list of Scraper instances
26 # Versions are omitted, if v10 is outdated than v8 is aswell
27 docs = names.map {|name| Docs.find(name.split(/@|~/)[0], false)}.uniq
28
29 # Check all documentations for updates when no arguments are given
30 docs = Docs.all if docs.empty?
31
32 opts = {
33 logger: logger
34 }
35
36 if options.key?(:github_token)
37 opts[:github_token] = options[:github_token]
38 end
39
40 with_progress_bar do |bar|
41 bar.max = docs.length
42 bar.write
43 end
44
45 results = docs.map do |doc|
46 result = check_doc(doc, opts)
47 with_progress_bar(&:increment!)
48 result
49 end
50
51 process_results(results)
52 rescue Docs::DocNotFound => error
53 logger.error(error)
54 logger.info('Run "thor docs:list" to see the list of docs.')
55 end
56
57 private
58
59 def check_doc(doc, opts)
60 logger.debug("Checking #{doc.name}")
61
62 instance = doc.versions.first.new
63 scraper_version = instance.get_scraper_version(opts)
64 latest_version = instance.get_latest_version(opts)
65
66 {
67 name: doc.name,
68 scraper_version: format_version(scraper_version),
69 latest_version: format_version(latest_version),
70 is_outdated: instance.is_outdated(scraper_version, latest_version)
71 }
72 rescue NotImplementedError
73 logger.warn("Couldn't check #{doc.name}, get_latest_version is not implemented")
74 error_result(doc, '`get_latest_version` is not implemented')
75 rescue => error
76 logger.error("Error while checking #{doc.name}\n#{error.full_message.strip}")
77 error_result(doc, error.message.gsub(/'/, '`'))
78 end
79
80 def format_version(version)
81 str = version.to_s
82
83 # If the version is numeric and greater than or equal to 1e9 it's probably a timestamp
84 return str if str.match(/^(\d)+$/).nil? or str.to_i < 1e9
85
86 DateTime.strptime(str, '%s').strftime('%F')
87 end
88
89 def error_result(doc, reason)
90 {
91 name: doc.name,
92 error: reason
93 }
94 end
95
96 def process_results(results)
97 successful_results = results.select {|result| result.key?(:is_outdated)}
98 failed_results = results.select {|result| result.key?(:error)}
99
100 up_to_date_results = successful_results.select {|result| !result[:is_outdated]}
101 outdated_results = successful_results.select {|result| result[:is_outdated]}
102
103 log_results(outdated_results, up_to_date_results, failed_results)
104 upload_results(outdated_results, up_to_date_results, failed_results) if options[:upload]
105 end
106
107 #
108 # Result logging methods
109 #
110
111 def log_results(outdated_results, up_to_date_results, failed_results)
112 log_failed_results(failed_results) unless failed_results.empty?
113 log_successful_results('Up-to-date', up_to_date_results) unless up_to_date_results.empty?
114 log_successful_results('Outdated', outdated_results) unless outdated_results.empty?
115 end
116
117 def log_successful_results(label, results)
118 title = "#{label} documentations (#{results.length})"
119 headings = ['Documentation', 'Scraper version', 'Latest version']
120 rows = results.map {|result| [result[:name], result[:scraper_version], result[:latest_version]]}
121
122 table = Terminal::Table.new :title => title, :headings => headings, :rows => rows
123 puts table
124 end
125
126 def log_failed_results(results)
127 title = "Documentations that could not be checked (#{results.length})"
128 headings = %w(Documentation Reason)
129 rows = results.map {|result| [result[:name], result[:error]]}
130
131 table = Terminal::Table.new :title => title, :headings => headings, :rows => rows
132 puts table
133 end
134
135 #
136 # Upload methods
137 #
138
139 def upload_results(outdated_results, up_to_date_results, failed_results)
140 # We can't create issues without a GitHub token
141 unless options.key?(:github_token)
142 logger.error("Please specify a GitHub token with the public_repo permission for #{UPLOAD_USER} with the --github-token parameter")
143 return
144 end
145
146 logger.info('Uploading the results to a new GitHub issue')
147
148 logger.info('Checking if the GitHub token belongs to the correct user')
149 user = github_get('/user')
150
151 # Only allow the DevDocs bot to upload reports
152 unless user['login'] == UPLOAD_USER
153 logger.error("Only #{UPLOAD_USER} is supposed to upload the results to a new issue. The specified github token is not for #{UPLOAD_USER}.")
154 return
155 end
156
157 logger.info('Creating a new GitHub issue')
158
159 issue = results_to_issue(outdated_results, up_to_date_results, failed_results)
160 created_issue = github_post("/repos/#{UPLOAD_REPO}/issues", issue)
161
162 logger.info('Checking if the previous issue is still open')
163
164 search_params = {
165 q: "Documentation versions report in:title author:#{UPLOAD_USER} is:issue repo:#{UPLOAD_REPO}",
166 sort: 'created',
167 order: 'desc'
168 }
169
170 matching_issues = github_get('/search/issues', search_params)
171 previous_issue = matching_issues['items'].find {|item| item['number'] != created_issue['number']}
172
173 if previous_issue.nil?
174 logger.info('No previous issue found')
175 log_upload_success(created_issue)
176 else
177 logger.info('Commenting on the previous issue')
178
179 comment = "This report was superseded by ##{created_issue['number']}."
180 github_post("/repos/#{UPLOAD_REPO}/issues/#{previous_issue['number']}/comments", {body: comment})
181 if previous_issue['closed_at'].nil?
182 logger.info('Closing the previous issue')
183 github_patch("/repos/#{UPLOAD_REPO}/issues/#{previous_issue['number']}", {state: 'closed'})
184 log_upload_success(created_issue)
185 else
186 logger.info('The previous issue has already been closed')
187 log_upload_success(created_issue)
188 end
189 end
190 end
191
192 def results_to_issue(outdated_results, up_to_date_results, failed_results)
193 results = [
194 successful_results_to_markdown('Outdated', outdated_results),
195 successful_results_to_markdown('Up-to-date', up_to_date_results),
196 failed_results_to_markdown(failed_results)
197 ]
198
199 results_str = results.select {|result| !result.nil?}.join("\n\n")
200 travis_str = ENV['TRAVIS'].nil? ? '' : "\n\nThis issue was created by Travis CI build [##{ENV['TRAVIS_BUILD_NUMBER']}](#{ENV['TRAVIS_BUILD_WEB_URL']})."
201
202 title = "Documentation versions report for #{Date.today.strftime('%B %Y')}"
203 body = <<-MARKDOWN
204## What is this?
205
206This is an automatically created issue which contains information about the version status of the documentations available on DevDocs. The results of this report can be used by maintainers when updating outdated documentations.
207
208Maintainers can close this issue when all documentations are up-to-date. The issue is also automatically closed when the next report is created.#{travis_str}
209
210## Results
211
212The #{outdated_results.length + up_to_date_results.length + failed_results.length} documentations are divided as follows:
213- #{outdated_results.length} that #{outdated_results.length == 1 ? 'is' : 'are'} outdated
214- #{up_to_date_results.length} that #{up_to_date_results.length == 1 ? 'is' : 'are'} up-to-date (patch updates are ignored)
215- #{failed_results.length} that could not be checked
216 MARKDOWN
217
218 {
219 title: title,
220 body: body.strip + "\n\n" + results_str
221 }
222 end
223
224 def successful_results_to_markdown(label, results)
225 return nil if results.empty?
226
227 title = "#{label} documentations (#{results.length})"
228 headings = ['Documentation', 'Scraper version', 'Latest version']
229 rows = results.map {|result| [result[:name], result[:scraper_version], result[:latest_version]]}
230
231 results_to_markdown(title, headings, rows)
232 end
233
234 def failed_results_to_markdown(results)
235 return nil if results.empty?
236
237 title = "Documentations that could not be checked (#{results.length})"
238 headings = %w(Documentation Reason)
239 rows = results.map {|result| [result[:name], result[:error]]}
240
241 results_to_markdown(title, headings, rows)
242 end
243
244 def results_to_markdown(title, headings, rows)
245 "<details>\n<summary>#{title}</summary>\n\n#{create_markdown_table(headings, rows)}\n</details>"
246 end
247
248 def create_markdown_table(headings, rows)
249 header = headings.join(' | ')
250 separator = '-|' * headings.length
251 body = rows.map {|row| row.join(' | ')}
252
253 header + "\n" + separator[0...-1] + "\n" + body.join("\n")
254 end
255
256 def log_upload_success(created_issue)
257 logger.info("Successfully uploaded the results to #{created_issue['html_url']}")
258 end
259
260 #
261 # HTTP utilities
262 #
263
264 def github_get(endpoint, params = {})
265 github_request(endpoint, {method: :get, params: params})
266 end
267
268 def github_post(endpoint, params)
269 github_request(endpoint, {method: :post, body: params.to_json})
270 end
271
272 def github_patch(endpoint, params)
273 github_request(endpoint, {method: :patch, body: params.to_json})
274 end
275
276 def github_request(endpoint, opts)
277 url = "https://api.github.com#{endpoint}"
278
279 # GitHub token authentication
280 opts[:headers] = {
281 Authorization: "token #{options[:github_token]}"
282 }
283
284 # GitHub requires the Content-Type to be application/json when a body is passed
285 if opts.key?(:body)
286 opts[:headers]['Content-Type'] = 'application/json'
287 end
288
289 logger.debug("Making a #{opts[:method]} request to #{url}")
290 response = Docs::Request.run(url, opts)
291
292 # response.success? is false if the response code is 201
293 # GitHub returns 201 Created after an issue is created
294 if response.success? || response.code == 201
295 JSON.parse(response.body)
296 else
297 logger.error("Couldn't make a #{opts[:method]} request to #{url} (response code #{response.code})")
298 nil
299 end
300 end
301
302 # A utility method which ensures no progress bar is shown when stdout is not a tty
303 def with_progress_bar(&block)
304 return unless $stdout.tty?
305 @progress_bar ||= ::ProgressBar.new
306 block.call @progress_bar
307 end
308
309 def logger
310 @logger ||= Logger.new($stdout).tap do |logger|
311 logger.level = options[:verbose] ? Logger::DEBUG : Logger::INFO
312 logger.formatter = proc {|severity, datetime, progname, msg| "[#{severity}] #{msg}\n"}
313 end
314 end
315end
public/docs/docs.json+0−1
@@ -1 +0,0 @@
1[]
public/icons/docs-1.pxm+0−0

Binary file not shown.

public/icons/docs-1@2x.pxm+0−0

Binary file not shown.

public/icons/docs-2.pxm+0−0

Binary file not shown.

public/icons/docs-2@2x.pxm+0−0

Binary file not shown.

public/icons/docs/bluebird/16@2x.png+0−0

Binary file not shown.

public/icons/docs/composer/16.png+0−0

Binary file not shown.

public/icons/docs/composer/16@2x.png+0−0

Binary file not shown.

public/icons/docs/composer/SOURCE+1−0
@@ -0,0 +1 @@
1https://github.com/composer/getcomposer.org/blob/master/web/img/logo-composer-transparent.png
public/icons/docs/cypress/16.png+0−0

Binary file not shown.

public/icons/docs/cypress/16@2x.png+0−0

Binary file not shown.

public/icons/docs/cypress/SOURCE+1−0
@@ -0,0 +1 @@
1https://github.com/cypress-io/cypress-documentation/raw/develop/themes/cypress/source/img/favicon.ico
public/icons/docs/godot/16.png+0−0

Binary file not shown.

public/icons/docs/godot/16@2x.png+0−0

Binary file not shown.

public/icons/docs/octave/16.png+0−0

Binary file not shown.

public/icons/docs/octave/16@2x.png+0−0

Binary file not shown.

public/icons/docs/octave/SOURCE+1−0
@@ -0,0 +1 @@
1https://www.gnu.org/software/octave/img/octave-logo.svg
public/icons/docs/pony/16.png+0−0

Binary file not shown.

public/icons/docs/pony/16@2x.png+0−0

Binary file not shown.

public/icons/docs/pony/SOURCE+1−0
@@ -0,0 +1 @@
1https://raw.githubusercontent.com/ponylang/ponylang-website/master/static/images/logo.png
public/icons/docs/rxjs/16.png+0−0

Binary file not shown.

public/icons/docs/rxjs/16@2x.png+0−0

Binary file not shown.

public/icons/docs/rxjs/SOURCE+1−0
@@ -0,0 +1 @@
1https://github.com/ReactiveX/reactivex.github.io/blob/develop/favicon.ico
public/icons/docs/saltstack/16.png+0−0

Binary file not shown.

public/icons/docs/saltstack/16@2x.png+0−0

Binary file not shown.

public/icons/docs/saltstack/SOURCE+1−0
@@ -0,0 +1 @@
1https://github.com/saltstack/salt/blob/develop/doc/_static/salt-logo.svg
public/icons/docs/scala/16.png+0−0

Binary file not shown.

public/icons/docs/scala/16@2x.png+0−0

Binary file not shown.

public/icons/docs/trio/16.png+0−0

Binary file not shown.

public/icons/docs/trio/16@2x.png+0−0

Binary file not shown.

public/icons/docs/trio/SOURCE+1−0
@@ -0,0 +1 @@
1https://github.com/python-trio/trio/blob/37de153f858e29df3a19db9fffcd0fb3f2308951/logo/logo-transparent-no-text.svg
public/icons/docs/vue_router/16.png+0−0

Binary file not shown.

public/icons/docs/vue_router/16@2x.png+0−0

Binary file not shown.

public/icons/docs/vue_router/SOURCE+1−0
@@ -0,0 +1 @@
1https://github.com/vuejs/vuejs.org/blob/master/assets/logo.ai
public/icons/docs/vuex/16.png+0−0

Binary file not shown.

public/icons/docs/vuex/16@2x.png+0−0

Binary file not shown.

public/icons/docs/vuex/SOURCE+1−0
@@ -0,0 +1 @@
1https://github.com/vuejs/vuejs.org/blob/master/assets/logo.ai
public/icons/docs/wordpress/16.png+0−0

Binary file not shown.

public/icons/docs/wordpress/16@2x.png+0−0

Binary file not shown.

public/icons/docs/wordpress/SOURCE+1−0
@@ -0,0 +1 @@
1https://wordpress.org/about/logos/
public/images/webapp-icon-192.png+0−0

Binary file not shown.

public/manifest.json+6−0
@@ -4,6 +4,7 @@ · 3 unmodified lines
4 "description": "API Documentation Browser",4 "description": "API Documentation Browser",
5 "start_url": "/",5 "start_url": "/",
6 "display": "standalone",6 "display": "standalone",
7 "background_color": "#EEEEEE",
7 "icons": [8 "icons": [
8 {9 {
9 "src": "/images/webapp-icon-32.png",10 "src": "/images/webapp-icon-32.png",
@@ -25,6 +26,11 @@ · 15 unmodified lines
25 "sizes": "128x128",26 "sizes": "128x128",
26 "type": "image/png"27 "type": "image/png"
27 },28 },
29 {
30 "src": "/images/webapp-icon-192.png",
31 "sizes": "192x192",
32 "type": "image/png"
33 },
28 {34 {
29 "src": "/images/webapp-icon-256.png",35 "src": "/images/webapp-icon-256.png",
30 "sizes": "256x256",36 "sizes": "256x256",
test/app_test.rb+0−81
@@ -43,35 +43,6 @@ class AppTest < MiniTest::Spec · 42 unmodified lines
43 assert last_response.redirect?43 assert last_response.redirect?
44 assert_equal 'https://example.org/', last_response['Location']44 assert_equal 'https://example.org/', last_response['Location']
45 end45 end
46
47 it "sets default size" do
48 get '/'
49 assert_includes last_response.body, 'data-size="20rem"'
50 end
51
52 it "sets size from cookie" do
53 set_cookie('size=42')
54 get '/'
55 assert_includes last_response.body, 'data-size="42px"'
56 end
57
58 it "sets layout from cookie" do
59 set_cookie('layout=foo')
60 get '/'
61 assert_includes last_response.body, '<body class="foo">'
62 end
63
64 it "sets the <html> theme from cookie" do
65 get '/'
66 assert_match %r{<html [^>]*class="[^\"]*_theme-default}, last_response.body
67 refute_includes last_response.body, '_theme-dark'
68
69 set_cookie('dark=1')
70
71 get '/'
72 assert_match %r{<html [^>]*class="[^\"]*_theme-dark}, last_response.body
73 refute_includes last_response.body, '_theme-default'
74 end
75 end46 end
76 47
77 describe "/[static-page]" do48 describe "/[static-page]" do
@@ -106,58 +77,6 @@ class AppTest < MiniTest::Spec · 28 unmodified lines
106 end77 end
107 end78 end
108 79
109 describe "/manifest.appcache" do
110 it "works" do
111 get '/manifest.appcache'
112 assert last_response.ok?
113 end
114
115 it "works with cookie" do
116 set_cookie('docs=css/html~5')
117 get '/manifest.appcache'
118 assert last_response.ok?
119 assert_includes last_response.body, '/css/index.json?1420139788'
120 assert_includes last_response.body, '/html~5/index.json?1420139791'
121 end
122
123 it "ignores invalid docs in the cookie" do
124 set_cookie('docs=foo')
125 get '/manifest.appcache'
126 assert last_response.ok?
127 refute_includes last_response.body, 'foo'
128 end
129
130 it "has the word 'default' when no 'dark' cookie is set" do
131 get '/manifest.appcache'
132 assert_includes last_response.body, '# default'
133 refute_includes last_response.body, '# dark'
134 end
135
136 it "has the word 'dark' when the cookie is set" do
137 set_cookie('dark=1')
138 get '/manifest.appcache'
139 assert_includes last_response.body, '# dark'
140 refute_includes last_response.body, '# default'
141 end
142
143 it "sets default size" do
144 get '/manifest.appcache'
145 assert_includes last_response.body, '20rem'
146 end
147
148 it "sets size from cookie" do
149 set_cookie('size=42')
150 get '/manifest.appcache'
151 assert_includes last_response.body, '42px'
152 end
153
154 it "sets layout from cookie" do
155 set_cookie('layout=foo_layout')
156 get '/manifest.appcache'
157 assert_includes last_response.body, 'foo_layout'
158 end
159 end
160
161 describe "/[doc]" do80 describe "/[doc]" do
162 it "renders when the doc exists and isn't enabled" do81 it "renders when the doc exists and isn't enabled" do
163 set_cookie('docs=html~5')82 set_cookie('docs=html~5')
test/lib/docs/core/scrapers/file_scraper_test.rb+2−2
@@ -63,7 +63,7 @@ class FileScraperTest < MiniTest::Spec · 62 unmodified lines
63 end63 end
64 64
65 it "reads a file" do65 it "reads a file" do
66 mock(scraper).read_file(path)66 mock(scraper).read_file(File.join(ROOT_PATH, 'docs/scraper', path))
67 result67 result
68 end68 end
69 69
@@ -165,7 +165,7 @@ class FileScraperTest < MiniTest::Spec · 95 unmodified lines
165 165
166 describe "#read_file" do166 describe "#read_file" do
167 let :result do167 let :result do
168 scraper.send :read_file, 'file'168 scraper.send :read_file, File.join(ROOT_PATH, 'docs', 'scraper', 'file')
169 end169 end
170 170
171 it "returns the file's content when the file exists in the source directory" do171 it "returns the file's content when the file exists in the source directory" do
views/app.erb+1−13
@@ -28,13 +28,7 @@ · 27 unmodified lines
28 </nav>28 </nav>
29 </header>29 </header>
30 <section class="_sidebar" tabindex="-1">30 <section class="_sidebar" tabindex="-1">
31 <div class="_list" role="navigation">31 <div class="_list" role="navigation"></div>
32 <% unless @doc %>
33 <% App.docs.each do |slug, doc| %>
34 <a href="/<%= slug %>/" class="_list-item"><%= doc['full_name'] %></a>
35 <% end %>
36 <% end %>
37 </div>
38 </section>32 </section>
39 <div class="_container" role="document">33 <div class="_container" role="document">
40 <main class="_content _content-loading" role="main"></main>34 <main class="_content _content-loading" role="main"></main>
@@ -63,9 +57,3 @@ · 22 unmodified lines
63 <symbol id="icon-external-link" viewBox="0 0 24 24"><path d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/></symbol>57 <symbol id="icon-external-link" viewBox="0 0 24 24"><path d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/></symbol>
64 </defs>58 </defs>
65</svg>59</svg>
66<style data-size="<%= app_size %>" data-resizer>
67 ._container { margin-left: <%= app_size %>; }
68 ._header, ._list { width: <%= app_size %>; }
69 ._list-hover.clone { min-width: <%= app_size %>; }
70 ._notice, ._path, ._resizer { left: <%= app_size %>; }
71</style>
views/index.erb+3−2
@@ -1,5 +1,5 @@
1<!DOCTYPE html>1<!DOCTYPE html>
2<html<%= ' manifest="/manifest.appcache"' if App.production? %> prefix="og: http://ogp.me/ns#" lang="en" class="_booting _theme-<%= app_theme %>">2<html prefix="og: http://ogp.me/ns#" lang="en" class="_booting">
3<head>3<head>
4 <meta charset="utf-8">4 <meta charset="utf-8">
5 <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no,shrink-to-fit=no">5 <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no,shrink-to-fit=no">
@@ -14,6 +14,7 @@ · 8 unmodified lines
14 <meta name="apple-mobile-web-app-capable" content="yes">14 <meta name="apple-mobile-web-app-capable" content="yes">
15 <meta name="apple-mobile-web-app-status-bar-style" content="black">15 <meta name="apple-mobile-web-app-status-bar-style" content="black">
16 <meta name="format-detection" content="telephone=no">16 <meta name="format-detection" content="telephone=no">
17 <meta name="theme-color" content="#eee">
17 <meta name="robots" content="noodp">18 <meta name="robots" content="noodp">
18 <title>DevDocs API Documentation</title>19 <title>DevDocs API Documentation</title>
19 <link rel="canonical" href="<%= canonical_origin %>">20 <link rel="canonical" href="<%= canonical_origin %>">
@@ -32,7 +33,7 @@ · 12 unmodified lines
32 <link rel="mask-icon" href="<%= App.cdn_origin %>/images/webkit-mask-icon.svg" color="#398df0">33 <link rel="mask-icon" href="<%= App.cdn_origin %>/images/webkit-mask-icon.svg" color="#398df0">
33 <%= stylesheet_tag 'application' %>34 <%= stylesheet_tag 'application' %>
34</head>35</head>
35<body<%= %( class="#{app_layout}") if app_layout %>>36<body>
36<noscript class="_fail">DevDocs requires JavaScript to run.</noscript>37<noscript class="_fail">DevDocs requires JavaScript to run.</noscript>
37<%= erb :app -%>38<%= erb :app -%>
38<%= javascript_tag 'application', asset_host: false %>39<%= javascript_tag 'application', asset_host: false %>
views/manifest.erb+0−14
@@ -1,14 +0,0 @@
1CACHE MANIFEST
2# <%= app_theme %> <%= app_size %> <%= app_layout %>
3
4CACHE:
5/
6<%= manifest_asset_urls.join "\n" %>
7<%= doc_index_urls.join "\n" %>
8
9NETWORK:
10/s/
11*
12
13FALLBACK:
14/ /
views/other.erb+3−2
@@ -1,10 +1,11 @@
1<!DOCTYPE html>1<!DOCTYPE html>
2<html lang="en" class="_booting _theme-<%= app_theme %>">2<html lang="en" class="_booting">
3<head>3<head>
4 <meta charset="utf-8">4 <meta charset="utf-8">
5 <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">5 <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no">
6 <% if doc_index_page? %><meta name="description" content="<%= @doc['name'] %> <%= @doc['release'] %> API documentation with instant search, offline support, keyboard shortcuts, mobile version, and more."><% else %><meta name="robots" content="noindex"><% end %>6 <% if doc_index_page? %><meta name="description" content="<%= @doc['name'] %> <%= @doc['release'] %> API documentation with instant search, offline support, keyboard shortcuts, mobile version, and more."><% else %><meta name="robots" content="noindex"><% end %>
7 <meta name="format-detection" content="telephone=no">7 <meta name="format-detection" content="telephone=no">
8 <meta name="theme-color" content="#eee">
8 <meta property="og:image" content="<%= App.cdn_origin %>/images/icon-320.png">9 <meta property="og:image" content="<%= App.cdn_origin %>/images/icon-320.png">
9 <title>DevDocs<%= " &mdash; #{@doc['full_name']} documentation" if doc_index_page? %></title>10 <title>DevDocs<%= " &mdash; #{@doc['full_name']} documentation" if doc_index_page? %></title>
10 <link rel="canonical" href="<%= canonical_origin %><%= request.path %>">11 <link rel="canonical" href="<%= canonical_origin %><%= request.path %>">
@@ -13,7 +14,7 @@ · 2 unmodified lines
13 <link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="Search DevDocs">14 <link rel="search" type="application/opensearchdescription+xml" href="/opensearch.xml" title="Search DevDocs">
14 <%= stylesheet_tag 'application' %>15 <%= stylesheet_tag 'application' %>
15</head>16</head>
16<body<%= %( class="#{app_layout}") if app_layout %> data-doc="<%= CGI::escape_html @doc.to_json %>">17<body data-doc="<%= CGI::escape_html @doc.to_json %>">
17<noscript class="_fail">DevDocs requires JavaScript to run.</noscript>18<noscript class="_fail">DevDocs requires JavaScript to run.</noscript>
18<%= erb :app -%>19<%= erb :app -%>
19<%= javascript_tag 'application', asset_host: false %><% unless App.production? %>20<%= javascript_tag 'application', asset_host: false %><% unless App.production? %>
views/service-worker.js.erb+58−0
@@ -0,0 +1,58 @@
1<%# The name of the cache to store responses in %>
2<%# If the cache name changes DevDocs is assumed to be updated %>
3const cacheName = '<%= service_worker_cache_name %>';
4
5<%# Url's to cache when the service worker is installed %>
6const urlsToCache = [
7 '/',
8 '/favicon.ico',
9 '/manifest.json',
10 '<%= service_worker_asset_urls.join "',\n '" %>',
11 '<%= doc_index_urls.join "',\n '" %>',
12];
13
14<%# Set-up the cache %>
15self.addEventListener('install', event => {
16 self.skipWaiting();
17
18 event.waitUntil(
19 caches.open(cacheName).then(cache => cache.addAll(urlsToCache)),
20 );
21});
22
23<%# Remove old caches %>
24self.addEventListener('activate', event => {
25 event.waitUntil((async () => {
26 const keys = await caches.keys();
27 const jobs = keys.map(key => key !== cacheName ? caches.delete(key) : Promise.resolve());
28 return Promise.all(jobs);
29 })());
30});
31
32<%# Handle HTTP requests %>
33self.addEventListener('fetch', event => {
34 event.respondWith((async () => {
35 const cachedResponse = await caches.match(event.request);
36 if (cachedResponse) return cachedResponse;
37
38 try {
39 const response = await fetch(event.request);
40 return response;
41 } catch (err) {
42 const url = new URL(event.request.url);
43
44 const pathname = url.pathname;
45 const filename = pathname.substr(1 + pathname.lastIndexOf('/')).split(/\#|\?/g)[0];
46 const extensions = ['.html', '.css', '.js', '.json', '.png', '.ico', '.svg', '.xml'];
47
48 <%# Attempt to return the index page from the cache if the user is visiting a url like devdocs.io/offline or devdocs.io/javascript/global_objects/array/find %>
49 <%# The index page will make sure the correct documentation or a proper offline page is shown %>
50 if (url.origin === location.origin && !extensions.some(ext => filename.endsWith(ext))) {
51 const cachedIndex = await caches.match('/');
52 if (cachedIndex) return cachedIndex;
53 }
54
55 throw err;
56 }
57 })());
58});
views/unsupported.erb+3−3
@@ -11,9 +11,9 @@ · 10 unmodified lines
11 <p class="_fail-text">DevDocs is an API documentation browser which supports the following browsers:</p>11 <p class="_fail-text">DevDocs is an API documentation browser which supports the following browsers:</p>
12 <ul class="_fail-list">12 <ul class="_fail-list">
13 <li>Recent versions of Firefox, Chrome, or Opera</li>13 <li>Recent versions of Firefox, Chrome, or Opera</li>
14 <li>Safari 9.1+</li>14 <li>Safari 11.1+</li>
15 <li>Edge 16+</li>15 <li>Edge 17+</li>
16 <li>iOS 10+</li>16 <li>iOS 11.3+</li>
17 </ul>17 </ul>
18 <p class="_fail-text">18 <p class="_fail-text">
19 If you're unable to upgrade, we apologize.19 If you're unable to upgrade, we apologize.