From 39f0d4d349b50a2d4294406101ee4a49132a456d Mon Sep 17 00:00:00 2001 From: rond Date: Wed, 5 Jul 2023 01:40:49 +0200 Subject: [PATCH 0001/1110] Update Rust documentation (1.70.0) --- lib/docs/scrapers/rust.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/docs/scrapers/rust.rb b/lib/docs/scrapers/rust.rb index 8aa58a8cbe..270b2e56e8 100644 --- a/lib/docs/scrapers/rust.rb +++ b/lib/docs/scrapers/rust.rb @@ -3,7 +3,7 @@ module Docs class Rust < UrlScraper self.type = 'rust' - self.release = '1.65.0' + self.release = '1.70.0' self.base_url = 'https://doc.rust-lang.org/' self.root_path = 'book/index.html' self.initial_paths = %w( From 8034fd960db40c27cb7ab927fd1d90b61ad5f21d Mon Sep 17 00:00:00 2001 From: ClasherKasten Date: Mon, 24 Jul 2023 23:25:52 +0200 Subject: [PATCH 0002/1110] update github scraper tweak github scraper to work again after GitHub UI update which gives back JSON data --- lib/docs/scrapers/github.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/docs/scrapers/github.rb b/lib/docs/scrapers/github.rb index f6014a0a6e..e5b48edbf6 100644 --- a/lib/docs/scrapers/github.rb +++ b/lib/docs/scrapers/github.rb @@ -4,5 +4,20 @@ class Github < UrlScraper self.type = 'github' html_filters.push 'github/clean_html' + + def process_response?(response) + if super(response) + return true + end + JSON.parse(response.body) + true + rescue JSON::ParserError, TypeError => e + false + end + + def parse(response) + parsed = JSON.parse(response.response_body) + [parsed['payload']['blob']['richText'], parsed['title']] + end end end From 9241a0a9ae1d189eb0b9c30ee37030e3132ee42c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carl=20H=C3=B6rberg?= Date: Tue, 25 Jul 2023 10:59:57 +0200 Subject: [PATCH 0003/1110] Crystal 1.9.2 --- lib/docs/scrapers/crystal.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/docs/scrapers/crystal.rb b/lib/docs/scrapers/crystal.rb index 56a2ec130a..5c4a782e79 100644 --- a/lib/docs/scrapers/crystal.rb +++ b/lib/docs/scrapers/crystal.rb @@ -2,7 +2,7 @@ module Docs class Crystal < UrlScraper include MultipleBaseUrls self.type = 'crystal' - self.release = '1.7.0' + self.release = '1.9.2' self.base_urls = [ "https://crystal-lang.org/api/#{release}/", "https://crystal-lang.org/reference/#{release[0..2]}/", From 0db2fbeada80a0995da8001ba5a2ef537f8f20a2 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Wed, 26 Jul 2023 09:17:44 +0200 Subject: [PATCH 0004/1110] Update Kotlin documentation (1.9.0) --- lib/docs/scrapers/kotlin.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/docs/scrapers/kotlin.rb b/lib/docs/scrapers/kotlin.rb index 99248ffaa2..715aacc42f 100644 --- a/lib/docs/scrapers/kotlin.rb +++ b/lib/docs/scrapers/kotlin.rb @@ -31,6 +31,11 @@ class Kotlin < UrlScraper Licensed under the Apache License, Version 2.0. HTML + version '1.9' do + self.release = '1.9.0' + self.headers = { 'User-Agent' => 'devdocs.io' , 'Cookie' => 'x-ab-test-spring-boot-learning-path=0; userToken=r33dgpe8x3q5vswekg16a' } + end + version '1.8' do self.release = '1.8.0' self.headers = { 'User-Agent' => 'devdocs.io' , 'Cookie' => 'x-ab-test-spring-boot-learning-path=0; userToken=r33dgpe8x3q5vswekg16a' } From 86aafa0f3190bf35ed875ca2f687976de945f631 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Wed, 26 Jul 2023 10:08:56 +0200 Subject: [PATCH 0005/1110] Update Rust documentation (1.71.0) --- lib/docs/filters/rust/clean_html.rb | 13 ++++++++----- lib/docs/filters/rust/entries.rb | 6 +++--- lib/docs/scrapers/rust.rb | 2 +- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/docs/filters/rust/clean_html.rb b/lib/docs/filters/rust/clean_html.rb index fa24468d82..42ac2a83b7 100644 --- a/lib/docs/filters/rust/clean_html.rb +++ b/lib/docs/filters/rust/clean_html.rb @@ -16,12 +16,9 @@ def call @doc = at_css('#main, #main-content') css('.toggle-wrapper').remove + css('.anchor').remove - css('h1.fqn').each do |node| - node.content = node.at_css('.in-band').content - end - - css('.main-heading > h1.fqn').each do |node| + css('.main-heading > h1').each do |node| node.parent.name = 'h1' node.parent.content = node.content end @@ -107,6 +104,12 @@ def call node.content = node.content end + css('.rightside').each do |node| + node.children.each do |child| + child.remove if child.text?() and child.text() == " · " + end + end + css('.since + .srclink').each do |node| node.previous_element.before(node) end diff --git a/lib/docs/filters/rust/entries.rb b/lib/docs/filters/rust/entries.rb index 176f7e0252..cadb678671 100644 --- a/lib/docs/filters/rust/entries.rb +++ b/lib/docs/filters/rust/entries.rb @@ -9,7 +9,7 @@ def get_name elsif slug == 'error-index' 'Compiler Errors' else - name = at_css('h1.fqn .in-band').content.remove(/\A.+\s/).remove('⎘') + name = at_css('main h1').content.remove(/\A.+\s/).remove('⎘') mod = slug.split('/').first name.prepend("#{mod}::") unless name.start_with?(mod) name @@ -27,7 +27,7 @@ def get_type 'Compiler Errors' else path = name.split('::') - heading = at_css('h1.fqn .in-band').content.strip + heading = at_css('main h1').content.strip if path.length > 2 || (path.length == 2 && (heading.start_with?('Module') || heading.start_with?('Primitive'))) path[0..1].join('::') else @@ -46,7 +46,7 @@ def additional_entries else css('.method') .each_with_object({}) { |node, entries| - name = node.at_css('.fnname').try(:content) + name = node.at_css('a.fn').try(:content) next unless name name.prepend "#{self.name}::" entries[name] ||= [name, node['id']] diff --git a/lib/docs/scrapers/rust.rb b/lib/docs/scrapers/rust.rb index 270b2e56e8..7f18e0a397 100644 --- a/lib/docs/scrapers/rust.rb +++ b/lib/docs/scrapers/rust.rb @@ -3,7 +3,7 @@ module Docs class Rust < UrlScraper self.type = 'rust' - self.release = '1.70.0' + self.release = '1.71.0' self.base_url = 'https://doc.rust-lang.org/' self.root_path = 'book/index.html' self.initial_paths = %w( From 503bfecea6647fe65f6e269bb76cacce6cea99fa Mon Sep 17 00:00:00 2001 From: ClasherKasten Date: Wed, 26 Jul 2023 17:30:40 +0200 Subject: [PATCH 0006/1110] fix images in C/C++ docs adjusting the html_filter to work with the new URIs --- lib/docs/filters/cppref/clean_html.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/docs/filters/cppref/clean_html.rb b/lib/docs/filters/cppref/clean_html.rb index 7c65c726b8..d7f564ff74 100644 --- a/lib/docs/filters/cppref/clean_html.rb +++ b/lib/docs/filters/cppref/clean_html.rb @@ -106,7 +106,7 @@ def call end css('img').each do |node| - node['src'] = node['src'].sub! %r{http://en.cppreference.com/common/([^"']+?)\.svg}, 'http://upload.cppreference.com/mwiki/\1.svg' + node['src'] = node['src'].sub! %r{https://upload.cppreference.com/mwiki/(images/[^"']+?)}, 'http://upload.cppreference.com/mwiki/\1' end # temporary solution due lack of mathjax/mathml support From 2943d6975b68b5c4f3ecf629424aba10d6ebd989 Mon Sep 17 00:00:00 2001 From: ClasherKasten Date: Wed, 26 Jul 2023 17:34:58 +0200 Subject: [PATCH 0007/1110] Update multiple "current version" checks --- lib/docs/core/scraper.rb | 2 +- lib/docs/scrapers/cppref/cppref.rb | 2 +- lib/docs/scrapers/docker.rb | 2 +- lib/docs/scrapers/electron.rb | 2 +- lib/docs/scrapers/gnu/gcc.rb | 4 ++-- lib/docs/scrapers/gnu/gnu_fortran.rb | 4 ++-- lib/docs/scrapers/react_bootstrap.rb | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/docs/core/scraper.rb b/lib/docs/core/scraper.rb index 007580ed50..33636dbf5b 100644 --- a/lib/docs/core/scraper.rb +++ b/lib/docs/core/scraper.rb @@ -150,7 +150,7 @@ def url_for(path) if path.empty? || path == '/' root_url.to_s else - File.join(base_url.to_s, path) + File.join(base_url.to_s, path.to_s) end end diff --git a/lib/docs/scrapers/cppref/cppref.rb b/lib/docs/scrapers/cppref/cppref.rb index 85bbc77153..e50aeee36f 100644 --- a/lib/docs/scrapers/cppref/cppref.rb +++ b/lib/docs/scrapers/cppref/cppref.rb @@ -23,7 +23,7 @@ class Cppref < UrlScraper # Check if the 'headers' page has changed def get_latest_version(opts) - doc = fetch_doc(self.base_url + self.root_path, opts) + doc = fetch_doc((self.base_url + self.root_path).to_s, opts) date = doc.at_css('#footer-info-lastmod').content date = date.match(/[[:digit:]]{1,2} .* [[:digit:]]{4}/).to_s date = DateTime.strptime(date, '%e %B %Y').to_time.to_i diff --git a/lib/docs/scrapers/docker.rb b/lib/docs/scrapers/docker.rb index 527441c5bc..5d01341d1c 100644 --- a/lib/docs/scrapers/docker.rb +++ b/lib/docs/scrapers/docker.rb @@ -96,7 +96,7 @@ class Docker < UrlScraper def get_latest_version(opts) doc = fetch_doc('https://docs.docker.com/engine/release-notes/', opts) - latest_version = doc.at_css('.content > section > h1[id^="version-"]').content.strip + latest_version = doc.at_css('.content > section > h2').content.strip latest_version.rpartition(' ')[-1] end end diff --git a/lib/docs/scrapers/electron.rb b/lib/docs/scrapers/electron.rb index 9b8c8e463d..76d5a1703c 100644 --- a/lib/docs/scrapers/electron.rb +++ b/lib/docs/scrapers/electron.rb @@ -27,7 +27,7 @@ class Electron < UrlScraper def get_latest_version(opts) doc = fetch_doc('https://www.electronjs.org/releases/stable', opts) - doc.at_css(".tag").content.gsub!(/[a-zA-Z]/, '') + doc.at_css('.release-card__metadata>a')['href'].gsub!(/[a-zA-Z\/:]/, '')[1..-1] end end end diff --git a/lib/docs/scrapers/gnu/gcc.rb b/lib/docs/scrapers/gnu/gcc.rb index e9f934e6b7..a47e8ccd54 100644 --- a/lib/docs/scrapers/gnu/gcc.rb +++ b/lib/docs/scrapers/gnu/gcc.rb @@ -156,8 +156,8 @@ class Gcc < Gnu def get_latest_version(opts) doc = fetch_doc('https://gcc.gnu.org/onlinedocs/', opts) - label = doc.at_css('ul > li > ul > li > a').content.strip - label.scan(/([0-9.]+)/)[0][0] + label = doc.at_css('details > ul > li > a')['href'].strip + label.scan(/([0-9.]+)/)[2..-1][0][0] end end end diff --git a/lib/docs/scrapers/gnu/gnu_fortran.rb b/lib/docs/scrapers/gnu/gnu_fortran.rb index 72c516f9f2..35341e5948 100644 --- a/lib/docs/scrapers/gnu/gnu_fortran.rb +++ b/lib/docs/scrapers/gnu/gnu_fortran.rb @@ -53,8 +53,8 @@ class GnuFortran < Gnu def get_latest_version(opts) doc = fetch_doc('https://gcc.gnu.org/onlinedocs/', opts) - label = doc.at_css('ul > li > ul > li > a').content.strip - label.scan(/([0-9.]+)/)[0][0] + label = doc.at_css('details > ul > li > a')['href'].strip + label.scan(/([0-9.]+)/)[2..-1][0][0] end end end diff --git a/lib/docs/scrapers/react_bootstrap.rb b/lib/docs/scrapers/react_bootstrap.rb index 80df23124e..107f1df639 100644 --- a/lib/docs/scrapers/react_bootstrap.rb +++ b/lib/docs/scrapers/react_bootstrap.rb @@ -29,7 +29,7 @@ class ReactBootstrap < UrlScraper def get_latest_version(opts) doc = fetch_doc('https://react-bootstrap.github.io/', opts) - doc.at_css('#t-version>a').content.split()[0].strip[1..-1] + doc.at_css('.my-2').content.split()[-1].strip[0..-1] end end end From 4ecaccf0e4594062e14c7109a95864bf621162fd Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Wed, 26 Jul 2023 20:21:43 +0200 Subject: [PATCH 0008/1110] Update C documentation --- lib/docs/scrapers/cppref/c.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/docs/scrapers/cppref/c.rb b/lib/docs/scrapers/cppref/c.rb index 1d460c4125..77619c5db4 100644 --- a/lib/docs/scrapers/cppref/c.rb +++ b/lib/docs/scrapers/cppref/c.rb @@ -3,7 +3,7 @@ class C < Cppref self.name = 'C' self.slug = 'c' self.base_url = 'https://en.cppreference.com/w/c/' - # release = '2022-09-06' + # release = '2023-03-24' html_filters.insert_before 'cppref/clean_html', 'c/entries' From 334606eb5bd1fa3cf9ca39d65610b6ca80264f5e Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Wed, 26 Jul 2023 20:22:21 +0200 Subject: [PATCH 0009/1110] Update C++ documentation --- lib/docs/scrapers/cppref/cpp.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/docs/scrapers/cppref/cpp.rb b/lib/docs/scrapers/cppref/cpp.rb index 12fefe45fd..d654618002 100644 --- a/lib/docs/scrapers/cppref/cpp.rb +++ b/lib/docs/scrapers/cppref/cpp.rb @@ -3,7 +3,7 @@ class Cpp < Cppref self.name = 'C++' self.slug = 'cpp' self.base_url = 'https://en.cppreference.com/w/cpp/' - # release = '2022-09-06' + # release = '2023-03-24' html_filters.insert_before 'cppref/clean_html', 'cpp/entries' From 6f8ea7e2dea5e6bd411899bd156077809f1fc909 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Wed, 26 Jul 2023 20:34:08 +0200 Subject: [PATCH 0010/1110] Update Koa documentation (2.14.2) --- lib/docs/scrapers/koa.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/docs/scrapers/koa.rb b/lib/docs/scrapers/koa.rb index 5b4d96b02e..2d18540556 100644 --- a/lib/docs/scrapers/koa.rb +++ b/lib/docs/scrapers/koa.rb @@ -2,8 +2,8 @@ module Docs class Koa < Github - self.base_url = 'https://github.com/koajs/koa/blob/master/docs/' - self.release = '2.13.0' + self.base_url = 'https://github.com/koajs/koa/tree/master/docs' + self.release = '2.14.2' self.root_path = 'api/index.md' self.initial_paths = %w[ From da343eca8088eb992edffdc40408876592eb8be2 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Wed, 26 Jul 2023 20:38:23 +0200 Subject: [PATCH 0011/1110] Update Mongoose documentation (7.4.1) Fixes #2012. --- lib/docs/filters/mongoose/clean_html.rb | 2 +- lib/docs/scrapers/mongoose.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/docs/filters/mongoose/clean_html.rb b/lib/docs/filters/mongoose/clean_html.rb index 7364649baf..8b4b98d94b 100644 --- a/lib/docs/filters/mongoose/clean_html.rb +++ b/lib/docs/filters/mongoose/clean_html.rb @@ -17,7 +17,7 @@ def call node['data-language'] = 'javascript' end - css('.native-inline', '.api-nav', '.toc', '.api-content ul:first-child').remove + css('.native-inline', '.api-nav', '.toc', '.api-content ul:first-child', '.edit-docs-link').remove if !at_css('h1') if css('h2').count > 1 diff --git a/lib/docs/scrapers/mongoose.rb b/lib/docs/scrapers/mongoose.rb index d8a11156c8..bcd9673329 100644 --- a/lib/docs/scrapers/mongoose.rb +++ b/lib/docs/scrapers/mongoose.rb @@ -2,7 +2,7 @@ module Docs class Mongoose < UrlScraper self.name = 'Mongoose' self.type = 'simple' - self.release = '5.8.5' + self.release = '7.4.1' self.base_url = 'https://mongoosejs.com/docs/' self.root_path = 'index.html' self.initial_paths = %w(guide.html) From fe323ee57c9fa65b9e31038b1b2e440d55743378 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Wed, 26 Jul 2023 20:48:53 +0200 Subject: [PATCH 0012/1110] Update GNU Fortran documentation (13.1.0) --- lib/docs/scrapers/gnu/gnu_fortran.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/docs/scrapers/gnu/gnu_fortran.rb b/lib/docs/scrapers/gnu/gnu_fortran.rb index 35341e5948..757db49a3c 100644 --- a/lib/docs/scrapers/gnu/gnu_fortran.rb +++ b/lib/docs/scrapers/gnu/gnu_fortran.rb @@ -6,6 +6,11 @@ class GnuFortran < Gnu home: 'https://gcc.gnu.org/fortran/' } + version '13' do + self.release = '13.1.0' + self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/gfortran/" + end + version '12' do self.release = '12.1.0' self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/gfortran/" From daaa16fd8e572f5215cec5dee491f524950a059f Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Wed, 26 Jul 2023 20:54:15 +0200 Subject: [PATCH 0013/1110] Update GCC documentation (13.1.0) Fixes #1978. --- lib/docs/scrapers/gnu/gcc.rb | 42 ++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 16 deletions(-) diff --git a/lib/docs/scrapers/gnu/gcc.rb b/lib/docs/scrapers/gnu/gcc.rb index a47e8ccd54..732e88e826 100644 --- a/lib/docs/scrapers/gnu/gcc.rb +++ b/lib/docs/scrapers/gnu/gcc.rb @@ -46,6 +46,16 @@ class Gcc < Gnu 'Wtrigraphs.html' => 'Invocation.html' } + version '13' do + self.release = '13.1.0' + self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/gcc/" + end + + version '13 CPP' do + self.release = '13.1.0' + self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/cpp/" + end + version '12' do self.release = '12.2.0' self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/gcc/" @@ -57,98 +67,98 @@ class Gcc < Gnu end version '11' do - self.release = '11.1.0' + self.release = '11.4.0' self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/gcc/" end version '11 CPP' do - self.release = '11.1.0' + self.release = '11.4.0' self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/cpp/" end version '10' do - self.release = '10.2.0' + self.release = '10.5.0' self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/gcc/" end version '10 CPP' do - self.release = '10.2.0' + self.release = '10.5.0' self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/cpp/" end version '9' do - self.release = '9.3.0' + self.release = '9.5.0' self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/gcc/" end version '9 CPP' do - self.release = '9.3.0' + self.release = '9.5.0' self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/cpp/" options[:replace_paths] = CPP_PATHS end version '8' do - self.release = '8.3.0' + self.release = '8.5.0' self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/gcc/" end version '8 CPP' do - self.release = '8.3.0' + self.release = '8.5.0' self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/cpp/" options[:replace_paths] = CPP_PATHS end version '7' do - self.release = '7.4.0' + self.release = '7.5.0' self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/gcc/" end version '7 CPP' do - self.release = '7.4.0' + self.release = '7.5.0' self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/cpp/" options[:replace_paths] = CPP_PATHS end version '6' do - self.release = '6.4.0' + self.release = '6.5.0' self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/gcc/" options[:root_title] = 'Using the GNU Compiler Collection (GCC)' end version '6 CPP' do - self.release = '6.4.0' + self.release = '6.5.0' self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/cpp/" options[:replace_paths] = CPP_PATHS end version '5' do - self.release = '5.4.0' + self.release = '5.5.0' self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/gcc/" options[:root_title] = 'Using the GNU Compiler Collection (GCC)' end version '5 CPP' do - self.release = '5.4.0' + self.release = '5.5.0' self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/cpp/" options[:replace_paths] = CPP_PATHS end version '4' do - self.release = '4.9.3' + self.release = '4.9.4' self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/gcc/" options[:root_title] = 'Using the GNU Compiler Collection (GCC)' end version '4 CPP' do - self.release = '4.9.3' + self.release = '4.9.4' self.base_url = "https://gcc.gnu.org/onlinedocs/gcc-#{release}/cpp/" options[:replace_paths] = CPP_PATHS From 662aaa1fa0c7507fafcca43569edd7052cc30837 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Wed, 26 Jul 2023 21:08:13 +0200 Subject: [PATCH 0014/1110] Update Bootstrap documentation (5.3) Fixes #1939. --- assets/stylesheets/pages/_bootstrap.scss | 4 ++++ lib/docs/scrapers/bootstrap.rb | 8 +++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/assets/stylesheets/pages/_bootstrap.scss b/assets/stylesheets/pages/_bootstrap.scss index fbd885ea81..42289ccb5a 100644 --- a/assets/stylesheets/pages/_bootstrap.scss +++ b/assets/stylesheets/pages/_bootstrap.scss @@ -8,6 +8,8 @@ float: right; } + .h5 { font-size: 1.25rem; } + .bs-callout { @extend %note; } .bs-callout-info { @extend %note-blue; } .bs-callout-danger { @extend %note-red; } @@ -36,6 +38,7 @@ } .col { margin-bottom: 1.5em; } + .d-block { display: block; } @media (min-width: 800px) { .row { @@ -50,5 +53,6 @@ -moz-box-sizing: border-box; box-sizing: border-box; } + .col-md-6 { width: 50%; } } } diff --git a/lib/docs/scrapers/bootstrap.rb b/lib/docs/scrapers/bootstrap.rb index fac525c1a4..9ee0b0710a 100644 --- a/lib/docs/scrapers/bootstrap.rb +++ b/lib/docs/scrapers/bootstrap.rb @@ -10,8 +10,7 @@ class Bootstrap < UrlScraper # https://github.com/twbs/bootstrap/blob/master/LICENSE options[:attribution] = <<-HTML - © 2011–2022 Twitter, Inc.
- © 2011–2022 The Bootstrap Authors
+ © 2011–2023 The Bootstrap Authors
Code licensed under the MIT License.
Documentation licensed under the Creative Commons Attribution License v3.0. HTML @@ -23,9 +22,8 @@ class Bootstrap < UrlScraper html_filters.push 'bootstrap/entries_v5', 'bootstrap/clean_html_v5' - options[:only_patterns] = [ - /\Agetting-started\//, /\Alayout\//, /\Acontent\//, - /\Acomponents\//, /\Autilities\/.+/, /\Ahelpers\// + options[:skip_patterns] = [ + /\Aabout\// ] options[:replace_paths] = { From a32e3d0522fbe466647b070d62be92d6aff99d9a Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Wed, 26 Jul 2023 21:40:51 +0200 Subject: [PATCH 0015/1110] Update SQLite documentation (3.42.0) Fixes #1937. --- lib/docs/filters/sqlite/clean_html.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/docs/filters/sqlite/clean_html.rb b/lib/docs/filters/sqlite/clean_html.rb index b9cd369322..2f7fb46c89 100644 --- a/lib/docs/filters/sqlite/clean_html.rb +++ b/lib/docs/filters/sqlite/clean_html.rb @@ -56,10 +56,14 @@ def call else node.next_element['id'] = node['name'] end + node.remove + elsif node.parent.name == 'p' + node['id'] = node['name'] + node.parent.after(node.remove) else node.parent['id'] ||= node['name'] + node.remove end - node.remove end unless at_css('h2') From 03dfcf56efcec2366b21aaa315274e6d9e71145d Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Wed, 26 Jul 2023 22:39:24 +0200 Subject: [PATCH 0016/1110] Update Rust documentation (1.71.0) Fixes #1881. --- lib/docs/filters/rust/clean_html.rb | 4 ++++ lib/docs/scrapers/rust.rb | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/lib/docs/filters/rust/clean_html.rb b/lib/docs/filters/rust/clean_html.rb index 42ac2a83b7..f42b714a15 100644 --- a/lib/docs/filters/rust/clean_html.rb +++ b/lib/docs/filters/rust/clean_html.rb @@ -88,8 +88,12 @@ def call end css('pre').each do |node| + node.css('.where.fmt-newline').each do |node| + node.before("\n") + end node.content = node.content node['data-language'] = 'rust' if node['class'] && node['class'].include?('rust') + node['data-language'] = 'rust' if node.classes.include?('code-header') end doc.first_element_child.name = 'h1' if doc.first_element_child.name = 'h2' diff --git a/lib/docs/scrapers/rust.rb b/lib/docs/scrapers/rust.rb index 7f18e0a397..90bdf77ced 100644 --- a/lib/docs/scrapers/rust.rb +++ b/lib/docs/scrapers/rust.rb @@ -53,5 +53,10 @@ def get_latest_version(opts) def process_response?(response) !(response.body =~ REDIRECT_RGX || response.body =~ NOT_FOUND_RGX || response.body.blank?) end + + def parse(response) # Hook here because Nokogori removes whitespace from headings + response.body.gsub! %r{}, '
'
+      super
+    end
   end
 end

From 6100912e469261a531ecd271cba5dc47679dbfb5 Mon Sep 17 00:00:00 2001
From: Simon Legner 
Date: Wed, 26 Jul 2023 22:46:27 +0200
Subject: [PATCH 0017/1110] Update Vue documentation

Fixes #2004.
---
 lib/docs/filters/vue/clean_html.rb | 9 ++-------
 lib/docs/scrapers/vue.rb           | 1 +
 2 files changed, 3 insertions(+), 7 deletions(-)

diff --git a/lib/docs/filters/vue/clean_html.rb b/lib/docs/filters/vue/clean_html.rb
index ae12d743c9..448b9c37c0 100644
--- a/lib/docs/filters/vue/clean_html.rb
+++ b/lib/docs/filters/vue/clean_html.rb
@@ -2,13 +2,8 @@ module Docs
   class Vue
     class CleanHtmlFilter < Filter
       def call
-        if current_url.host == 'vitejs.dev'
-          return '

Vite

' if root_page? - @doc = at_css('.content > div') - else - return '

Vue.js

' if root_page? - @doc = at_css(version == '3' ? 'main > div > div' : '.content') - end + return '

Vue.js

' if root_page? + @doc = at_css(version == '3' ? 'main > div > div' : '.content') at_css('h1').content = 'Vue.js' if root_page? doc.child.before('

Vue.js API

') if slug == 'api/' || slug == 'api/index' diff --git a/lib/docs/scrapers/vue.rb b/lib/docs/scrapers/vue.rb index 3b2bde9f47..53f0723ac4 100644 --- a/lib/docs/scrapers/vue.rb +++ b/lib/docs/scrapers/vue.rb @@ -29,6 +29,7 @@ class Vue < UrlScraper self.release = '2.7.14' self.base_url = 'https://v2.vuejs.org/v2/' self.initial_paths = %w(api/) + self.root_path = 'guide/' html_filters.push 'vue/entries', 'vue/clean_html' end From 751dcc4087e049450280813b1d762b4c7a547b94 Mon Sep 17 00:00:00 2001 From: ClasherKasten Date: Thu, 27 Jul 2023 17:28:43 +0200 Subject: [PATCH 0018/1110] remove C/C++ from file-scraper list moved to URLScraper by e17bc84ea4c2a675fb16282339610dcab1506ce0 --- docs/file-scrapers.md | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/docs/file-scrapers.md b/docs/file-scrapers.md index 01201836ab..a4072aaf24 100644 --- a/docs/file-scrapers.md +++ b/docs/file-scrapers.md @@ -4,16 +4,6 @@ This lists the docs that use `FileScraper` and instructions for building some of If you open a PR to update one of these docs, please add/fix the instructions. -## C - -Download the HTML book from https://en.cppreference.com/w/Cppreference:Archives -and copy `reference/en/c` from the ZIP file into `/path/to/devdocs/docs/c`. - -## C++ - -Download the HTML book from https://en.cppreference.com/w/Cppreference:Archives -and copy `reference/en/cpp` from the ZIP file into `/path/to/devdocs/docs/cpp`. - ## Dart Click the “API docs” link under the “Stable channel” header on From 22e8a174da7019255a82b22f7f557ce0e8e61292 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Wed, 2 Aug 2023 20:33:45 +0200 Subject: [PATCH 0019/1110] Update esbuild documentation (0.18.17) --- lib/docs/scrapers/esbuild.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/docs/scrapers/esbuild.rb b/lib/docs/scrapers/esbuild.rb index a0f0d8bf51..9250b4fa70 100644 --- a/lib/docs/scrapers/esbuild.rb +++ b/lib/docs/scrapers/esbuild.rb @@ -16,7 +16,7 @@ class Esbuild < UrlScraper Licensed under the MIT License. HTML - self.release = '0.17.2' + self.release = '0.18.17' self.base_url = 'https://esbuild.github.io/' html_filters.push 'esbuild/clean_html', 'esbuild/entries' From 77041931506bde0421c3ad3a300c5dfb722de10b Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Wed, 2 Aug 2023 21:40:46 +0200 Subject: [PATCH 0020/1110] Update Axios documentation (1.4.0) --- lib/docs/scrapers/axios.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/docs/scrapers/axios.rb b/lib/docs/scrapers/axios.rb index e3dbfa7df3..13d7802873 100755 --- a/lib/docs/scrapers/axios.rb +++ b/lib/docs/scrapers/axios.rb @@ -5,7 +5,7 @@ class Axios < UrlScraper home: 'hthttps://axios-http.com/', code: 'https://github.com/axios/axios' } - self.release = '1.3.0' + self.release = '1.4.0' self.base_url = "https://axios-http.com/docs/" self.initial_paths = %w(index intro) From dedddae91e77b89d2cf3f36f895be33f925ee68d Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Wed, 2 Aug 2023 21:47:52 +0200 Subject: [PATCH 0021/1110] Update Backbone.js documentation (1.5.0) --- lib/docs/scrapers/backbone.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/docs/scrapers/backbone.rb b/lib/docs/scrapers/backbone.rb index 3a5fdb7664..cff7b7c217 100644 --- a/lib/docs/scrapers/backbone.rb +++ b/lib/docs/scrapers/backbone.rb @@ -3,7 +3,7 @@ class Backbone < UrlScraper self.name = 'Backbone.js' self.slug = 'backbone' self.type = 'underscore' - self.release = '1.4.0' + self.release = '1.5.0' self.base_url = 'https://backbonejs.org' self.links = { home: 'https://backbonejs.org/', @@ -17,7 +17,7 @@ class Backbone < UrlScraper options[:skip_links] = true options[:attribution] = <<-HTML - © 2010–2019 Jeremy Ashkenas, DocumentCloud
+ © 2010–2023 Jeremy Ashkenas, DocumentCloud
Licensed under the MIT License. HTML From f0e52f8f757b78df01d85af10c42881b3e8fb529 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Wed, 2 Aug 2023 21:51:34 +0200 Subject: [PATCH 0022/1110] docs: remove obvious /path/to/devdocs/ prefix --- docs/file-scrapers.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/file-scrapers.md b/docs/file-scrapers.md index a4072aaf24..8c2741dae9 100644 --- a/docs/file-scrapers.md +++ b/docs/file-scrapers.md @@ -8,7 +8,7 @@ If you open a PR to update one of these docs, please add/fix the instructions. Click the “API docs” link under the “Stable channel” header on https://www.dartlang.org/tools/sdk/archive. Rename the expanded ZIP to `dart~2` -and put it in `/path/to/devdocs/docs/` +and put it in `docs/` Or run the following commands in your terminal: @@ -41,12 +41,12 @@ bsdtar --extract --file - --directory=docs/django\~$VERSION/ ## Elisp -Go to https://www.gnu.org/software/emacs/manual/elisp.html, download the HTML tarball and extract its content in `/path/to/devdocs/docs/elisp` or run the following command: +Go to https://www.gnu.org/software/emacs/manual/elisp.html, download the HTML tarball and extract its content in `docs/elisp` or run the following command: ```sh -mkdir /path/to/devdocs/docs/elisp \ +mkdir docs/elisp \ && curl curl https://www.gnu.org/software/emacs/manual/elisp.html_node.tar.gz | \ -tar --extract --gzip --strip-components=1 --directory=/path/to/devdocs/docs/elisp +tar --extract --gzip --strip-components=1 --directory=docs/elisp ``` ## Erlang @@ -62,12 +62,12 @@ bsdtar --extract --file - --directory=docs/erlang\~$VERSION/ ## Gnu ### Bash -Go to https://www.gnu.org/software/bash/manual/, download the HTML tar file (with one web page per node) and extract its content in `/path/to/devdocs/docs/bash` or run the following command: +Go to https://www.gnu.org/software/bash/manual/, download the HTML tar file (with one web page per node) and extract its content in `docs/bash` or run the following command: ```sh -mkdir /path/to/devdocs/docs/bash \ +mkdir docs/bash \ && curl https://www.gnu.org/software/bash/manual/bash.html_node.tar.gz | \ -tar --extract --gzip --directory=/path/to/devdocs/docs/bash +tar --extract --gzip --directory=docs/bash ``` ### GCC @@ -95,12 +95,12 @@ tar --extract --gzip --strip-components=1 --directory=docs/gnu_fortran~$VERSION ``` ## GNU Make -Go to https://www.gnu.org/software/make/manual/, download the HTML tarball and extract its content in `/path/to/devdocs/docs/gnu_make` or run the following command: +Go to https://www.gnu.org/software/make/manual/, download the HTML tarball and extract its content in `docs/gnu_make` or run the following command: ```sh -mkdir /path/to/devdocs/docs/gnu_make \ +mkdir docs/gnu_make \ && curl https://www.gnu.org/software/make/manual/make.html_node.tar.gz | \ -tar --extract --gzip --strip-components=1 --directory=/path/to/devdocs/docs/gnu_make +tar --extract --gzip --strip-components=1 --directory=docs/gnu_make ``` ## Gnuplot @@ -154,7 +154,7 @@ bsdtar --extract --file=- --directory=docs/numpy~$VERSION/ Download from https://www.ocaml.org/docs/ the HTML reference: https://v2.ocaml.org/releases/4.14/ocaml-4.14-refman-html.tar.gz -and extract it as `/path/to/devdocs/docs/ocaml`: +and extract it as `docs/ocaml`: ```sh curl https://v2.ocaml.org/releases/$VERSION/ocaml-$VERSION-refman-html.tar.gz | \ @@ -216,7 +216,7 @@ tar xj --strip-components=1 ## R ```bash -DEVDOCSROOT=/path/to/devdocs/docs/r +DEVDOCSROOT=docs/r RLATEST=https://cran.r-project.org/src/base/R-latest.tar.gz # or /R-${VERSION::1}/R-$VERSION.tar.gz RSOURCEDIR=${TMPDIR:-/tmp}/R/latest @@ -269,7 +269,7 @@ See `lib/docs/scrapers/scala.rb` ## SQLite Download the docs from https://sqlite.org/download.html, unzip it, and rename -it to `/path/to/devdocs/docs/sqlite` +it to `docs/sqlite` ```sh curl https://sqlite.org/2022/sqlite-doc-3400000.zip | bsdtar --extract --file - --directory=docs/sqlite/ --strip-components=1 From f4a17d085fdc80e7e5524264dcd9e80101aadc04 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Wed, 2 Aug 2023 21:55:42 +0200 Subject: [PATCH 0023/1110] Update Elixir documentation (1.15.4) --- lib/docs/filters/elixir/clean_html.rb | 4 +++- lib/docs/scrapers/elixir.rb | 13 +++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/docs/filters/elixir/clean_html.rb b/lib/docs/filters/elixir/clean_html.rb index 85c5b72962..c2c31d4669 100644 --- a/lib/docs/filters/elixir/clean_html.rb +++ b/lib/docs/filters/elixir/clean_html.rb @@ -54,7 +54,7 @@ def api node.name = 'h3' node['id'] = id - source_href = node.at_css('.view-source').attr('href') + source_href = node.at_css('a.icon-action[title="View Source"]').attr('href') node.content = node.at_css('.signature').inner_text node << %(Source) @@ -69,6 +69,8 @@ def api node['data-language'] = 'elixir' node.content = node.content end + + css('.icon-action').remove end end end diff --git a/lib/docs/scrapers/elixir.rb b/lib/docs/scrapers/elixir.rb index c7e6658525..75bee5e9f8 100644 --- a/lib/docs/scrapers/elixir.rb +++ b/lib/docs/scrapers/elixir.rb @@ -33,6 +33,19 @@ def initial_urls "https://elixir-lang.org/getting-started/introduction.html" ] end + version '1.15' do + self.release = '1.15.4' + self.base_urls = [ + "https://hexdocs.pm/elixir/#{release}/", + "https://hexdocs.pm/eex/#{release}/", + "https://hexdocs.pm/ex_unit/#{release}/", + "https://hexdocs.pm/iex/#{release}/", + "https://hexdocs.pm/logger/#{release}/", + "https://hexdocs.pm/mix/#{release}/", + 'https://elixir-lang.org/getting-started/' + ] + end + version '1.14' do self.release = '1.14.1' self.base_urls = [ From 86eb64428cb8826c94e00649c2a78d1dcf5f0f96 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Wed, 2 Aug 2023 21:57:29 +0200 Subject: [PATCH 0024/1110] Update FastAPI documentation (0.100.1) --- lib/docs/scrapers/fastapi.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/docs/scrapers/fastapi.rb b/lib/docs/scrapers/fastapi.rb index 7711e0eda8..b19a1c7874 100644 --- a/lib/docs/scrapers/fastapi.rb +++ b/lib/docs/scrapers/fastapi.rb @@ -2,7 +2,7 @@ module Docs class Fastapi < UrlScraper self.name = 'FastAPI' self.type = 'fastapi' - self.release = '0.95.0' + self.release = '0.100.1' self.base_url = 'https://fastapi.tiangolo.com/' self.root_path = '/' self.links = { From 7451ba21eb5440a2cd89281b09f5a22137804583 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Wed, 2 Aug 2023 21:59:30 +0200 Subject: [PATCH 0025/1110] Update Julia documentation (1.9.2) --- lib/docs/scrapers/julia.rb | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/docs/scrapers/julia.rb b/lib/docs/scrapers/julia.rb index d599bcefed..97a5dc579c 100644 --- a/lib/docs/scrapers/julia.rb +++ b/lib/docs/scrapers/julia.rb @@ -7,10 +7,21 @@ class Julia < UrlScraper options[:attribution] = <<-HTML - © 2009–2022 Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors
+ © 2009–2023 Jeff Bezanson, Stefan Karpinski, Viral B. Shah, and other contributors
Licensed under the MIT License. HTML + version '1.9' do + self.release = '1.9.2' + self.base_url = "https://docs.julialang.org/en/v#{version}/" + self.type = 'julia' + + html_filters.push 'julia/entries', 'julia/clean_html' + + options[:container] = '.docs-main' + options[:only_patterns] = [/\Amanual\//, /\Abase\//, /\Astdlib\//] + end + version '1.8' do self.release = '1.8.5' self.base_url = "https://docs.julialang.org/en/v#{version}/" From f7caff282d5f9960b4f556bde6dfe27489e6bfdf Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Wed, 2 Aug 2023 22:04:14 +0200 Subject: [PATCH 0026/1110] Update Prettier documentation (3.0.0) --- lib/docs/scrapers/prettier.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/docs/scrapers/prettier.rb b/lib/docs/scrapers/prettier.rb index f996350175..ff15e62a3d 100644 --- a/lib/docs/scrapers/prettier.rb +++ b/lib/docs/scrapers/prettier.rb @@ -2,7 +2,7 @@ module Docs class Prettier < UrlScraper self.name = 'Prettier' self.type = 'simple' - self.release = '2.8.0' + self.release = '3.0.0' self.base_url = 'https://prettier.io/docs/en/' self.links = { home: 'https://prettier.io/', From e39bb276aba39e8a431ca8c603c71f13c10446df Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Wed, 2 Aug 2023 22:07:58 +0200 Subject: [PATCH 0027/1110] Update Trio documentation (0.22.2) --- assets/stylesheets/components/_page.scss | 4 ++-- lib/docs/scrapers/trio.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/stylesheets/components/_page.scss b/assets/stylesheets/components/_page.scss index 60a970b98c..de02fffbfb 100644 --- a/assets/stylesheets/components/_page.scss +++ b/assets/stylesheets/components/_page.scss @@ -8,8 +8,8 @@ &._page-error { position: static; } - > h1 { @extend ._lined-heading; } - > h1:first-child { margin-top: 0; } + > h1, > section > h1 { @extend ._lined-heading; } + > h1:first-child, > section:first-of-type > h1 { margin-top: 0; } a[href^="http:"], a[href^="https:"] { @extend %external-link; } diff --git a/lib/docs/scrapers/trio.rb b/lib/docs/scrapers/trio.rb index ea6186e1ac..141a408edd 100644 --- a/lib/docs/scrapers/trio.rb +++ b/lib/docs/scrapers/trio.rb @@ -1,7 +1,7 @@ module Docs class Trio < UrlScraper self.type = 'simple' - self.release = '0.18.0' + self.release = '0.22.2' self.base_url = "https://trio.readthedocs.io/en/v#{self.release}/" self.root_path = 'index.html' self.links = { From 362fc9f6644f96ae378fc2be894b47b0045b3305 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Wed, 2 Aug 2023 22:09:38 +0200 Subject: [PATCH 0028/1110] Update Astro documentation (2.9.7) --- lib/docs/scrapers/astro.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/docs/scrapers/astro.rb b/lib/docs/scrapers/astro.rb index ec2124c01c..765260d9de 100644 --- a/lib/docs/scrapers/astro.rb +++ b/lib/docs/scrapers/astro.rb @@ -16,7 +16,7 @@ class Astro < UrlScraper options[:skip_patterns] = [/tutorial/] - self.release = '2.6.3' + self.release = '2.9.7' self.base_url = 'https://docs.astro.build/en/' self.initial_paths = %w(getting-started/) From c39956e9e5c78b0ae76bf426db463109c19dd3c8 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Wed, 2 Aug 2023 22:11:33 +0200 Subject: [PATCH 0029/1110] Update Vite documentation (4.4.8) --- lib/docs/filters/vite/clean_html.rb | 2 +- lib/docs/scrapers/vite.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/docs/filters/vite/clean_html.rb b/lib/docs/filters/vite/clean_html.rb index 39583b916e..5e213236b2 100644 --- a/lib/docs/filters/vite/clean_html.rb +++ b/lib/docs/filters/vite/clean_html.rb @@ -4,7 +4,7 @@ class CleanHtmlFilter < Filter def call return "

Vitest

A Vite-native unit test framework. It's fast!

" if root_page? && current_url.host == 'vitest.dev' return "

VueUse

Collection of Vue Composition Utilities

" if root_page? && current_url.host == 'vueuse.org' - return '

Vite

' if root_page? + return '

Vite

Next Generation Frontend Tooling

' if root_page? @doc = at_css('main h1').parent css('.demo', '.guide-links', '.footer', '#ad').remove diff --git a/lib/docs/scrapers/vite.rb b/lib/docs/scrapers/vite.rb index d434529805..ff82da017b 100644 --- a/lib/docs/scrapers/vite.rb +++ b/lib/docs/scrapers/vite.rb @@ -21,7 +21,7 @@ class Vite < UrlScraper html_filters.push 'vite/entries', 'vite/clean_html' version do - self.release = '4.3.9' + self.release = '4.4.8' self.base_url = 'https://vitejs.dev/' end From 150f24be5ed897be63e698566e6e50fb684316eb Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Wed, 2 Aug 2023 22:13:12 +0200 Subject: [PATCH 0030/1110] Update Git documentation (2.41.0) --- lib/docs/scrapers/git.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/docs/scrapers/git.rb b/lib/docs/scrapers/git.rb index 97fa6d5bf1..81f709edb3 100644 --- a/lib/docs/scrapers/git.rb +++ b/lib/docs/scrapers/git.rb @@ -1,7 +1,7 @@ module Docs class Git < UrlScraper self.type = 'git' - self.release = '2.40.0' + self.release = '2.41.0' self.base_url = 'https://git-scm.com/docs' self.initial_paths = %w(/git.html) self.links = { @@ -16,7 +16,7 @@ class Git < UrlScraper options[:skip] = %w(/howto-index.html) options[:attribution] = <<-HTML - © 2012–2022 Scott Chacon and others
+ © 2012–2023 Scott Chacon and others
Licensed under the MIT License. HTML From 58446f23fb5070e81575c5b1ca1c235c9d8f7e0c Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Wed, 2 Aug 2023 22:16:21 +0200 Subject: [PATCH 0031/1110] Update HAProxy documentation (2.8.0) --- lib/docs/filters/haproxy/entries.rb | 2 +- lib/docs/scrapers/haproxy.rb | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/docs/filters/haproxy/entries.rb b/lib/docs/filters/haproxy/entries.rb index 8a8d90ca92..785b8ac3a8 100755 --- a/lib/docs/filters/haproxy/entries.rb +++ b/lib/docs/filters/haproxy/entries.rb @@ -34,7 +34,7 @@ def additional_entries node.css('.keyword').each do |n| name = n.at_css('b').content id = n['id'] - entries << [name, URI.escape(id), REPLACE_TYPE[type] || type] + entries << [name, id, REPLACE_TYPE[type] || type] end end end diff --git a/lib/docs/scrapers/haproxy.rb b/lib/docs/scrapers/haproxy.rb index 8fb07165e3..bac09b9c44 100644 --- a/lib/docs/scrapers/haproxy.rb +++ b/lib/docs/scrapers/haproxy.rb @@ -16,10 +16,15 @@ class Haproxy < UrlScraper options[:follow_links] = false options[:attribution] = <<-HTML - © 2022 Willy Tarreau, HAProxy contributors
+ © 2023 Willy Tarreau, HAProxy contributors
Licensed under the GNU General Public License version 2. HTML + version '2.8' do + self.release = '2.8.0' + self.base_url = "https://docs.haproxy.org/#{self.version}/" + end + version '2.7' do self.release = '2.7.0' self.base_url = "https://docs.haproxy.org/#{self.version}/" From d638585fea8e383143773c0c031e5769445ba9c1 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Wed, 2 Aug 2023 22:20:21 +0200 Subject: [PATCH 0032/1110] Update MariaDB documentation (11.0.2) --- lib/docs/scrapers/mariadb.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/docs/scrapers/mariadb.rb b/lib/docs/scrapers/mariadb.rb index d0eb92872f..5035dbf6be 100644 --- a/lib/docs/scrapers/mariadb.rb +++ b/lib/docs/scrapers/mariadb.rb @@ -2,7 +2,7 @@ module Docs class Mariadb < UrlScraper self.name = 'MariaDB' self.type = 'mariadb' - self.release = '10.11.2' + self.release = '11.0.2' self.base_url = 'https://mariadb.com/kb/en/' self.root_path = 'documentation/' self.links = { @@ -24,7 +24,7 @@ class Mariadb < UrlScraper ] options[:attribution] = <<-HTML - © 2022 MariaDB
+ © 2023 MariaDB
Licensed under the Creative Commons Attribution 3.0 Unported License and the GNU Free Documentation License. HTML From 05c8c21cae146720323ac89fdf1a1dbb9ed7a8e0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 5 Aug 2023 00:28:52 +0000 Subject: [PATCH 0033/1110] chore(deps): update dependency rack to v2.2.8 --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 481293f89b..1bc44105c1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -70,7 +70,7 @@ GEM byebug (~> 11.0) pry (>= 0.13, < 0.15) racc (1.6.2) - rack (2.2.7) + rack (2.2.8) rack-protection (3.0.6) rack rack-ssl-enforcer (0.2.9) From 34c456cd4ec2f5241608b60c6062f26c633aad54 Mon Sep 17 00:00:00 2001 From: Luckas <101930730+luckasRanarison@users.noreply.github.com> Date: Fri, 11 Aug 2023 04:34:43 +0300 Subject: [PATCH 0034/1110] Add nvim-devdocs to related projects --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b5e117738e..a5a190fd4e 100644 --- a/README.md +++ b/README.md @@ -174,6 +174,7 @@ Made something cool? Feel free to open a PR to add a new row to this table! You | [mdh34/quickDocs](https://github.com/mdh34/quickDocs) | Vala/Python based viewer | ![Latest GitHub commit](https://img.shields.io/github/last-commit/mdh34/quickDocs?logo=github&label) | ![GitHub stars](https://img.shields.io/github/stars/mdh34/quickDocs?logo=github&label) | | [romainl/vim-devdocs](https://github.com/romainl/vim-devdocs) | Vim plugin | ![Latest GitHub commit](https://img.shields.io/github/last-commit/romainl/vim-devdocs?logo=github&label) | ![GitHub stars](https://img.shields.io/github/stars/romainl/vim-devdocs?logo=github&label) | | [waiting-for-dev/vim-www](https://github.com/waiting-for-dev/vim-www) | Vim plugin | ![Latest GitHub commit](https://img.shields.io/github/last-commit/waiting-for-dev/vim-www?logo=github&label) | ![GitHub stars](https://img.shields.io/github/stars/waiting-for-dev/vim-www?logo=github&label) | +| [luckasRanarison/nvim-devdocs](https://github.com/luckasRanarison/nvim-devdocs) | Neovim plugin | ![Latest GitHub commit](https://img.shields.io/github/last-commit/luckasRanarison/nvim-devdocs?logo=github&label) | ![GitHub stars](https://img.shields.io/github/stars/luckasRanarison/nvim-devdocs?logo=github&label) | ## Copyright / License From 69357cd89d8120f1eae032d49a67569a0197aec6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 12 Aug 2023 00:17:40 +0000 Subject: [PATCH 0035/1110] chore(deps): update dependency activesupport to v7.0.7 --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 1bc44105c1..0d6099cfaf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: https://rubygems.org/ specs: - activesupport (7.0.6) + activesupport (7.0.7) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) From 4f81d61f276fa229b87cdde7c5cc91f97dc2a2e0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 12 Aug 2023 04:18:37 +0000 Subject: [PATCH 0036/1110] chore(deps): update dependency nokogiri to v1.15.4 --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 0d6099cfaf..e2548a850f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -46,7 +46,7 @@ GEM image_size (3.2.0) in_threads (1.6.0) method_source (1.0.0) - mini_portile2 (2.8.2) + mini_portile2 (2.8.4) minitest (5.18.1) multi_json (1.15.0) mustermann (3.0.0) @@ -55,7 +55,7 @@ GEM net-ssh (>= 5.0.0, < 8.0.0) net-ssh (7.0.1) newrelic_rpm (8.16.0) - nokogiri (1.15.3) + nokogiri (1.15.4) mini_portile2 (~> 2.8.2) racc (~> 1.4) options (2.3.2) From 27ca1658c4411224e8b923ee79664ade532faa24 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 12 Aug 2023 04:19:06 +0000 Subject: [PATCH 0037/1110] chore(deps): update dependency rss to v0.3.0 --- Gemfile.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 0d6099cfaf..2dc2716aec 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -81,10 +81,10 @@ GEM rb-inotify (0.10.1) ffi (~> 1.0) redcarpet (3.6.0) - rexml (3.2.5) + rexml (3.2.6) rouge (1.11.1) rr (3.1.0) - rss (0.2.9) + rss (0.3.0) rexml ruby2_keywords (0.0.5) sass (3.7.4) From 0619e0fdb5a0e5b5689d78662224d90cd94f645d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 12 Aug 2023 07:18:19 +0000 Subject: [PATCH 0038/1110] chore(deps): update dependency sinatra to v3.1.0 --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index e2548a850f..1b31c4e751 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -122,7 +122,7 @@ GEM eventmachine (~> 1.0, >= 1.0.4) rack (>= 1, < 3) thor (1.2.2) - tilt (2.0.11) + tilt (2.2.0) tty-pager (0.14.0) strings (~> 0.2.0) tty-screen (~> 0.8) From e1999ca288a60d79ce6caa6382069f2d500435c5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 12 Aug 2023 07:18:33 +0000 Subject: [PATCH 0039/1110] chore(deps): update dependency sinatra-contrib to v3.1.0 --- Gemfile.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index e2548a850f..dca06e5c7f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -71,8 +71,8 @@ GEM pry (>= 0.13, < 0.15) racc (1.6.2) rack (2.2.8) - rack-protection (3.0.6) - rack + rack-protection (3.1.0) + rack (~> 2.2, >= 2.2.4) rack-ssl-enforcer (0.2.9) rack-test (2.1.0) rack (>= 1.3) @@ -92,16 +92,16 @@ GEM sass-listen (4.0.0) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) - sinatra (3.0.6) + sinatra (3.1.0) mustermann (~> 3.0) rack (~> 2.2, >= 2.2.4) - rack-protection (= 3.0.6) + rack-protection (= 3.1.0) tilt (~> 2.0) - sinatra-contrib (3.0.6) + sinatra-contrib (3.1.0) multi_json mustermann (~> 3.0) - rack-protection (= 3.0.6) - sinatra (= 3.0.6) + rack-protection (= 3.1.0) + sinatra (= 3.1.0) tilt (~> 2.0) sprockets (3.7.2) concurrent-ruby (~> 1.0) @@ -122,7 +122,7 @@ GEM eventmachine (~> 1.0, >= 1.0.4) rack (>= 1, < 3) thor (1.2.2) - tilt (2.0.11) + tilt (2.2.0) tty-pager (0.14.0) strings (~> 0.2.0) tty-screen (~> 0.8) From acbf684186c47325aa972368b488f045cdeea9cb Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Tue, 22 Aug 2023 19:12:29 +0200 Subject: [PATCH 0040/1110] Update Git documentation (2.42.0) --- lib/docs/scrapers/git.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/docs/scrapers/git.rb b/lib/docs/scrapers/git.rb index 81f709edb3..2f3ea3b60b 100644 --- a/lib/docs/scrapers/git.rb +++ b/lib/docs/scrapers/git.rb @@ -1,7 +1,7 @@ module Docs class Git < UrlScraper self.type = 'git' - self.release = '2.41.0' + self.release = '2.42.0' self.base_url = 'https://git-scm.com/docs' self.initial_paths = %w(/git.html) self.links = { From e8f04b90e63bd9b74af311b60f915f86b0c8b704 Mon Sep 17 00:00:00 2001 From: Jan Christoph Ebersbach Date: Wed, 23 Aug 2023 19:09:54 +0200 Subject: [PATCH 0041/1110] Add fluture documentation (14.0.0) --- assets/stylesheets/application.css.scss | 1 + assets/stylesheets/pages/_fluture.scss | 7 ++++++ lib/docs/filters/fluture/clean_html.rb | 22 ++++++++++++++++++ lib/docs/filters/fluture/entries.rb | 24 ++++++++++++++++++++ lib/docs/scrapers/fluture.rb | 29 ++++++++++++++++++++++++ public/icons/docs/fluture/16.png | Bin 0 -> 3743 bytes public/icons/docs/fluture/16@2x.png | Bin 0 -> 4290 bytes public/icons/docs/fluture/SOURCE | 1 + 8 files changed, 84 insertions(+) create mode 100644 assets/stylesheets/pages/_fluture.scss create mode 100644 lib/docs/filters/fluture/clean_html.rb create mode 100644 lib/docs/filters/fluture/entries.rb create mode 100644 lib/docs/scrapers/fluture.rb create mode 100644 public/icons/docs/fluture/16.png create mode 100644 public/icons/docs/fluture/16@2x.png create mode 100644 public/icons/docs/fluture/SOURCE diff --git a/assets/stylesheets/application.css.scss b/assets/stylesheets/application.css.scss index a191f2896b..d54beb649b 100644 --- a/assets/stylesheets/application.css.scss +++ b/assets/stylesheets/application.css.scss @@ -59,6 +59,7 @@ 'pages/erlang', 'pages/express', 'pages/fastapi', + 'pages/fluture', 'pages/git', 'pages/github', 'pages/gnuplot', diff --git a/assets/stylesheets/pages/_fluture.scss b/assets/stylesheets/pages/_fluture.scss new file mode 100644 index 0000000000..e3072c6d23 --- /dev/null +++ b/assets/stylesheets/pages/_fluture.scss @@ -0,0 +1,7 @@ +._fluture { + @extend %simple; + + pre > code { + font-size: inherit; + } +} diff --git a/lib/docs/filters/fluture/clean_html.rb b/lib/docs/filters/fluture/clean_html.rb new file mode 100644 index 0000000000..dee87e3082 --- /dev/null +++ b/lib/docs/filters/fluture/clean_html.rb @@ -0,0 +1,22 @@ +module Docs + class Fluture + class CleanHtmlFilter < Filter + def call + # Replace header image with text + at_css('h1').content = 'Fluture' + + # Remove the build line + css('h1 ~ p:first-of-type').remove + + # Remove the fantasy land image link + css('p a').remove + + # Make headers bigger by transforming them into a bigger variant + css('h3').each { |node| node.name = 'h2' } + css('h4').each { |node| node.name = 'h3' } + + doc + end + end + end +end diff --git a/lib/docs/filters/fluture/entries.rb b/lib/docs/filters/fluture/entries.rb new file mode 100644 index 0000000000..03556373b9 --- /dev/null +++ b/lib/docs/filters/fluture/entries.rb @@ -0,0 +1,24 @@ +module Docs + class Fluture + class EntriesFilter < Docs::EntriesFilter + # The entire reference is one big page, so get_name and get_type are not necessary + def additional_entries + entries = [] + type = "" + + css("h3, h4").each do |node| + case node.name + when "h3" + type = node.text + when "h4" + name = node.text + id = node.text.downcase + entries << [name, id, type] + end + end + + entries + end + end + end +end diff --git a/lib/docs/scrapers/fluture.rb b/lib/docs/scrapers/fluture.rb new file mode 100644 index 0000000000..3576e423a7 --- /dev/null +++ b/lib/docs/scrapers/fluture.rb @@ -0,0 +1,29 @@ +module Docs + + class Fluture < Github + self.name = "Fluture" + self.slug = "fluture" + self.type = "fluture" + self.release = "14.0.0" + self.base_url = "https://github.com/fluture-js/Fluture/blob/#{self.release}/README.md" + self.links = { + home: "https://github.com/fluture-js/Fluture", + code: "https://github.com/fluture-js/Fluture", + } + + html_filters.push "fluture/entries", "fluture/clean_html" + + options[:skip] = %w[middleware.gif] + options[:container] = '.markdown-body' + options[:title] = "Fluture" + options[:trailing_slash] = false + options[:attribution] = <<-HTML + © 2020 Aldwin Vlasblom
+ Licensed under the MIT License. + HTML + + def get_latest_version(opts) + get_npm_version("fluture", opts) + end + end +end diff --git a/public/icons/docs/fluture/16.png b/public/icons/docs/fluture/16.png new file mode 100644 index 0000000000000000000000000000000000000000..75fca6cd7002304e9462f01eaaa652b733559e1a GIT binary patch literal 3743 zcma)82{@E%`+sMMNn^{-V2CVbjNMEWG8j99ti>?nj4*S?Oc>cSwk$~( z5>l2rC?#d-h{|@f(#iM8clyqEUElw@{_i~Z``qt+|9;Q!ex7IECEeN4T2Me%0D>Sv zqK&01=tVXS!2{0fTEZj(LU#rcNNOn z%>slf=a3W+d3(%XNxKD!8JQO*y&21a*WOG{*Ir4!f=0w8w6+;*wArL@*a>KnO^g?N zZ^q=@j7)e~g&lpD0jr_P%v<{Q&w6w(9^%zW+s}*D)FGAGUCdiX9r?7p@&R+dseR}# z9Oq1XA5><83Tz7yrHc%OdgEWmPGk2F1y*n?C(NZ(;$F4A`%~_9MHhSsDOdR3avEoK*zdFEJ;YZ zbsOjL^B3Oh#<7s@i2I*>#>TuonfbRCR~G$)=^LfA51)7f&6wp@wn4dIJJ5kPt_~2y z*$qJnCm?7Igc9Z;h>eAyH~tWWFMuE^Msb^q3D~m{XL~mS0Hn-3vv!-fhub z;?Ywt6G?V$C~>}8>~^ixtLePY^@_AlQ)V@%WugK-8q0#8-{teSb89TySjn!6*Euos z(DBKyzSk=frw5Z~hK4>YlG-n6WJS4MFN>cVux%+*NoNzAOO?|iP_aKj9v1R(Gyxwg zg@mKp`EfvZOZum0U8IJREiE7i#spP?bM)kc!-F@B%hN$U0C1?W9Avi|kOKg>uG+mh z9FPTVD-LP^T>F+%?|A3P3o(GTHDFz?#pzT6@RMgACQlCmE#Kw~0lke5t>;xUj~N%H znAM*Jb;_nOq+=+cTG^yfa0%*{OAeKe4gwL-wj!u_noM+{68C47laWsC7Y$2NQ884o zIC3#m(3F$IWTOJX8p<3CfC#!$Y=7WyO@}#g&%Ln>)BskS_Pm?au(`b96TJcb^>$UK zL8+|AcicK_G_Rbp%%&Q~`^l#oZJSFCYfsCk z&_PxB+S3Xt4AZJpiYLN1Ha6Dxj17W)jbgbvf_wA-dtml!2n%bNVglaI_a>^UCiKp~ zBk#EK)WvzM7HK>`OSJw2$9?xUReo?#;n0^gFQMk;$5jaLK&?bx|#u-_-*zJee>i|9b$e~pk5 zLf|gbG9m$|ljG*eat(+?GnjNLEtG<0MKdVq?@t%wtcxU{Y?Ko6>uYgb(n=G~QCQz~ zF+Ncmx%%v>%{fntiTrDRhJ-c0qiY8Cf?~)nvFn;NJ^1W)+VF!h=iDq&b0H0;a)RIN zA`d!e=?}ZF8@5GfP9lvnclk~!*2au>s?*CwTlO_6daq4=xl~K8bWxv4lF2jT<4Pv? zwV^7h`<2F`aPXmb$AuMp?+h6T)b{n_dZn{JBBDC4a(WlGzdV6bOdq4~t|HI6!%Qai zz72fQuaXVPG!5fl-|Guq*%tgMX|*TpMo-^VNX62P>-5H&IaTHBJZHznJ|hw{uNqe7 zSGdUAMg&yv8xhV@3iMguGl(Q2>UlpqqW>x}->-<|_0TywzF% zpZ~d;QDF=S@=O`^vdbICBcHE|H1QzOP-mz-V1Jr?u+|Wla(|fP(jP?zDpuC;M zBWUI>bUW^wRNSPK?PIicVO>)dks$0{_o~Y4O3_DrU%{%rqEIR>`1w-vvZFyYCEwF3 zlhANucz^yNUstE5VScq_w;D2I(s@w#fjB?TTP>NLk&OSn@Dl2aUk<*Iq4kjGp?FF9 zK`v#ged1Iom9aEg(5CyiAU$~ZNfgg#hi{6jo|-M6E+q|H=Iv?-GIo&(!Cah^$fjgD zxA||os?KwKXf{4t_uc)|>y0LMd8OLEgw_JpnONVGlO*J|>pS(mezE3Et|eU{z@^i) zlz&M)Gs58wfH$(`1NsSMz+_Im&xY$jDqb1Fj?HpWu0F+d1Hz_;ih6O%cj3{ z_{_hwH{w)S1|#01%un#a5z9?I9ePPxFZOa5>Jhn8)%QG-?rvk3fA;V~!`aE2w!6zS z8g@%OkQwLD(eADNmcjE1ljM}``%dJ!2<~VoeqwLmF_kvCLrZ|0$5MYhmpv&`mts7& zCQvVe^xZD%&gqWnKJ`k4m+w7HNTB>ko0>i9MpTNSzhHf%mF*NMZ7|(BN0>)7d(Wfl z8M55UGDTQTR^i*O93BZFdaDVUhiqrRtMP4ip$J;)N%t{jIl9ee$DECQMzQAL!+cXwDc>ePkz8-eFbqP2%aw-aekCIl;3IXED)M zMGY|m#%@K(lA48lMW=1&jZ%l%R@Z#ssp<5)o%e$VIjo{R*{Gqz^qi{)ccj}SN>Nt4 zq#BAPI&hO>t0$i{N2x^hdg?l_cSZ_g+#^NmP5SH6&EMQ-rk<_Q&|hpq3!-a8w)0&m)vPj*R(m3FHiO{8kO8wyQPmt00wi^rTRHEJ1|+|xqQd-#Uk zyjChIu~M;hdX(Wq{eC!Z1pC7fndgFhRx=rLOoJ_9xQz2$c!(7F-YTVH^?2Ojw)?E* zZtV}h7qxDbi#a+`v$W$4eM;r$U#-orxYAz}V`ARMT)iDOr%;%4@uNYZaK%T>LQ~Ss zH@W-F-gD27-~D>jk}kbwOHm{&H0M3VHAlXQr;kiG7=dV;WB+Yy0D7&tjt#k*-@jNL zd;43Y&BCqJ!dRD^uNwIQhI6(c=fX#TfI?ns75cfZ$G?eJ9P!3zKYljx^0yjC@2}SC zO@BmQS(Ym;Dw;F-tlzi!qMTHt59hBr*kH>~B$YUQR&bSL@D_1xUjv5$X z>XGE|MTus%Lh0cb9|R2^?>9mAPyZ$%k>wEgZ9vPw0JhTvndh2lUKh3?U*b1m+Nosb zBV9?c=;Exeso58cD=6*ZXQj1U1dY7Xc%(zR+})$`XF~C%O7SwRsSeGGnbD}rV~I8h zqjGv``}bBs&MN!VjI?i?ZM)AeP$elT@k>?p(GHWUN-uAZ!-Ip_%ewod6AB^EF8!6G zcWu|fY#&lQiBuv{6GDIs@RbhZgL9}eSzHow@pz;*(E6SHZuux;P_lfakz zx1SGu^?zgFz}JDnAwkd=1Jeb*IOuDD(NedxegppIf%yKj0p8}wfA`iz2WPY{MhA=0 z(+7O4t{xt1jMvpe>los(MtD7AfQFU-r=rb~?kI|W0-Y+0;K>RKU{UaaObY0}v$e3s zTDp2}I{KSzecT=_7LUc!w`(;17a@!uM59Lk-vkA>Q$m2C{zHL93!_A^0>GPTv&4Uh z7~>6$wnS83=9U4D>?Vg2MWf;^Xsifl3X=p_p%ln?FM6}>T-dJ$P@+Fj2kD`7CW#Rc zNP%>O&%ucxsq`ah38v)+r2p5#*mj6k1F+Z+Fad}B|R*F7T$77#|OZa|AqxoA_AE-28%`y-x`k0riHS! zXyNDxS8EIVU~@5mmiQYT77)&+1_ZL$ObYWm+`csTJ^%>)0MLUdq0lDP$Deo*WFR8J K(X!scKj~l0vID39 literal 0 HcmV?d00001 diff --git a/public/icons/docs/fluture/16@2x.png b/public/icons/docs/fluture/16@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..e02f26ab494de2253782c89ef349413d635d5bf8 GIT binary patch literal 4290 zcmZ`+2{=@J`#xqYL-t(;L$YNVOEDspZIE?Fwz3ZISQ;_*Bx3AKNVbqv2#KUDHL^vP zgd)jE*}{-D>;LHe-oEd zd&XD`naJ-&NwA<367Ny$e3xCH%u~KrJi74+0lRknd4rmCgJJxEF_WB&hT2kRV_0fq z(Diqv$l-+qnsRr6IbDaIY3uf-3$%)HXK0Z!idKcj_tTf*j+@IX>+;>TP5pbQ&|6Ji zK%pVrwZWN#z~1j|r#=(@6?p>9w1QfR*5MPs_N`&zr{GL+E2N)UFlSBUO16z`VD;yg z3b)d>!8Q)PVJXKq4Hdp$zE)U!Rs@*szc#;QZ#%8t%OPGJcf15~R-XljaS}Cpq@NJ4 zve>D)+nS=KC!gbhL;A@UG4Z5NF&x~O7|)F;%1h)mUy5gToSV&aA=BFy-4rX>KO+jzG8-EB|x4&<~%# z*zKr=14={KpY&>Kv>l0=%}XmwP9B7V0{r?W)K%NBNZ-gU?QkfrhL-06AXEYXu15pF zHdwko3jhH~0GM|I0QGDD;PcIIu+RVrdKY5@UEttwD{6k20ah4&4Q>4a;0Who(7YeN zwhI;+i0Jcr3^Oo3R>mWN_vfF1({2gH*bpt9gAl%c1b4hQ4nYj@#UXx=UoH8*m3<0a zlzDfC8x^%ui!xa&ILut0f+Dm=vi98*Y&z6mdK|T`OX@J^AZF zg-eNr)Kru}x+*=1_eNI(yu|&C=twXM(!X$(O{DX6|4F8bu1-`Zf64|dxaDzZ=OW`o zG+ZQpgdkDsGL5CtpeXP6{!%GD>Y1qN!>}Xk06e1e*o<23@Tu?Un)EED);}fGl+PX) zdkW1OgJGMoNtBUmMTq1%{Eq}e?ezE2>lo}$aTDhwz%A0rX^t7r-FHNs}URd6-fdY;4v7W?- z?d&Bz+Dthu()E1fF$1Qwl63-7aLAP1Op9gr3mM(7^6RI^7oA^SJiVa0sTX{3a~2yn z$2Qf0xY%Ng?*IT=`~Nx2U>{!qu!$MAHBKK>Xa2m(je?-lt`p&->49aTZ{b?wY2E`9 z@tu!3LmXV+iZK$QEZSJsSJ(FWu2Ia4J|GNoDr-y8dTe%;-%4#ChiU%7YCciea4&yjGGP2)Gc-F>N)>;|O|+3_9{NpR@a`F)X98<}UD526Nj(~niV zsaXhk%HN;mNx>zXH#pHfmV#dGpNH7V#WOADW{6< zZG%wCcGNvR2!EWM*zL$$L!q?JkeUD?=PqB{$hddxFjewL9J@e{l9+*MQ9HHoq19C= zjFrldc z5eA1ha_19UlHv96E62W^V6`rfcaQYbNf+i~L5^8D1T|H-EW7BuQS~rpl20ZjE-StW zO&<~IxU9+bY+3U**?w-qL^ZTn*8{c&*NLWwz?N$}$jVW2Ut~#(Rj@R^vNzUIZFB)e zTh_J+NWcvlBZ;DfY6+gV0%M^B1onB4uE(4(#U+OEbac7}%aQ8*k0vJM$vDapIVMs% zQR>5N3WdEgMr~x9sfwN1fsq3n+8)+^^P4y={TdA`Q;}nXqzSw}IOdcSOI3}&(WF&e zU%Wvo8}xX}iGi{y7r~Wf9Gm1(;r(N&P##voa}5`$i?PYEnja}S>KRJa2|~2@QW7*R=es)k*7LmoRjEz?ij&tY5y*GrA(% z1*OiiWS!5F57E!94r5ZoineVg&vB+bC*sC;pst|Si*r~}++jzt; zL(h}F1JcH@iiyBG`WKd^6N+nxihQu`Aw4Bt+$5qfS-gp%uA0Mj@csyxZ}E(zkv=kjJ1 zSG;=l+NFkW;&Qvf`up7H2Sr?_X70%f5vS}6gyz0&&#hPzX3$|_Kf)fr@|hLRNxi>u zGKZ~rLncSls&PK;wDub5?pWKdmo9;S+XyG3w^)}ximD5mk01P>3_#J0 z%ns7?ac0j_S@^5{Aj8Gx*qm^S#&0zYpe8gcJN4cMr~o`C=r?nKN-hz!}uD}4Ag zKJl{L)Na>6s`h)(qh-N@+}v4>EtM|tHbO)2GsV#K7YF460G%6JR|jL$x|rn^hsAPt zMtBwa&-AJFm7GI;Djm(ZtAMkU;GqqVGcSJ4rpIkDG$U4BG%I~Jr$|?iT90vZQ+Qjv zfS-h-^O*e~xIYL94VZq}?@Aqx4Wkm9FFjODc=?n<4ykC~KUjARs|=?^?4+K@tiRzC zJCLDR1hcIzLCAbM>~N#Lj-+-`-dLUB?WDv5-jS134XrDMNZ|rS%zG;=`?BWE+VCD+ zg1Qr&A2XXe#M+1VMpLN=$?g3WUpMTj&cnI47VKZRcNg(c$Q_jleKgaM+DHL?+L+3h zRxA~>a*!FI51~`L!j|#ZZh%VRC@*O;%Rzl_`~+3=wkZqCm7nAFMdi$Ae!eatYoDkV z_uKeM-{1I7bs7_yqs@tA(gN(=0wyk3%qQ!Avn_Hiw$|<-!DYU!##)t88#foi12=Cw z5rzhCotqVWjU=Q8q(7v#clXv%0B z5$^X_u=rNUOjSKYL)em;c*YKvhr@a9ePa2z8G|sbHGrPr=j?cNp)4r!M`Xo{t|5Ph zf4LVA>xa;1*|g}Xfm4l|qc4I70wjAEN7xMp#5@C)eTA#6%1PY`1F}G}2r)!F8S9dq`g= zyadg{?CR0>57AK}1pu7<<1L{L&`^osXm-xhpW2jaV2$&{8d`fwlVUsqt1o|fm%!TV zbLW=e_HJ=x<;PH|OlA4+*SI5LHf(Dcvhhn?-kogymiD&!FPOEA`CAZ`qaxV*=CVls z)|MP0#);q~e)D%;C>Tjtjmqlq7nKLEdmTtG>o2}N_E**X7|V65<{_?8*wc8}lVnsT z+Mhx}GU&&?A<@YFQmJRXs&hdj`89#dXPCm?vDQ4Um#^%wa^y#EFRPuRxgLq^{~2=R z*ORb_d9wCFrw%v%do%_yw-c;y9zf~7EfeKbUc<#vtTtUg*<#iZ$_j6)HQI1picGk; z($bc%#W(_UungfiZD2Nyj9F2s3PNi0jE$tJ6c}Von1|&PpjGrFng;5nFmCJd#fFg4 z-dkf9PAuQcuw~A#`Bh*oFTzU$tc7}U@!Tf@?W24b&9pA z{J1F?$tJ_LUQ?KGy+Hlq~Ng**8o-Pxk@R;zqH;0!CB67|HRl0xWDw zqncslM7iy+Rc}iQI$$ZbjJDv=Y(D?}F<`Lf}S?FS<+*+?*OIBC?H1uKPHl@=F0%%N3e60w-m;Lr$ z`Iz+Hs4m%5kkqELoMqRDlIInm{>ZcJk>)5ZO*;xt=(LLcFrzdl&oOHIy#~6)Xd~GX z9S2|U-%6FLUfg3F4IJ#QD&_wyDHc|KW|3xzZbR5iL#;zxBdhm?mx~s^QR79guTseG z3#ob9Eb$G28k7S;vPf=Igy+KQy(i$s_Z`j#?T$vv05I?i_#j6^3($b**5P&eKQM#_ z3P8ac2@FmUA8HQcC=K23oCIRQf8k6JOZ`g&0kI+t1YiMkJ{U?M<_2>)82H$~d%Xnl z;S2VAGX&A$%Ku~o^m%iHlDr~PURee7BbAiZk!tEn$_T|%>PS^}Wi?PuQ}pjWI$Y_s z2*)OvjuZ84h;GhAoVu$Y4ott@a!55fC1s4F%As2YbpnZ0MahhB|C$jqlX?`Fy3ALES~6Zj`Onu zUEVlAO%`$Z=3G1@392~$sLm0*34T_-&aOB>ku48`21(IBq%L?^7_jyKIx$9Mjxtcp z^+&7+HZ^m1_s0g7GmjJvsQ4W6@;csbh*2F`H3sm#`qxNyW9N_Nk pN(}JB`Tdrg6r^>70@gnQf*Z~oIJDY3q0fN~fYvkBtvc%z^?&1yJ#7F0 literal 0 HcmV?d00001 diff --git a/public/icons/docs/fluture/SOURCE b/public/icons/docs/fluture/SOURCE new file mode 100644 index 0000000000..7f186cb023 --- /dev/null +++ b/public/icons/docs/fluture/SOURCE @@ -0,0 +1 @@ +https://github.com/fluture-js/Fluture/ From 47a4ea7b1bce612fead29c003ff8904895fd96b8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 23 Aug 2023 21:55:56 +0000 Subject: [PATCH 0042/1110] chore(deps): update dependency activesupport to v7.0.7.1 [security] --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index dca06e5c7f..49ef83b046 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: https://rubygems.org/ specs: - activesupport (7.0.7) + activesupport (7.0.7.2) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) From 31ab02280ffa0b2f8f9ed0d61822ecaf1d5bcc5f Mon Sep 17 00:00:00 2001 From: Beemen Sameh <41084077+beemensameh@users.noreply.github.com> Date: Thu, 24 Aug 2023 01:44:38 +0300 Subject: [PATCH 0043/1110] Update go to 1.21.0 update release version for golang to 1.21.0 --- lib/docs/scrapers/go.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/docs/scrapers/go.rb b/lib/docs/scrapers/go.rb index 11768fd37a..d811f3c444 100644 --- a/lib/docs/scrapers/go.rb +++ b/lib/docs/scrapers/go.rb @@ -1,7 +1,7 @@ module Docs class Go < UrlScraper self.type = 'go' - self.release = '1.20.0' + self.release = '1.21.0' self.base_url = 'https://golang.org/pkg/' self.links = { home: 'https://golang.org/', From 3419546bc18c2fdff35e5d0c573368710b647e20 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Thu, 24 Aug 2023 08:05:26 +0200 Subject: [PATCH 0044/1110] fluture: add news entry --- assets/javascripts/news.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/assets/javascripts/news.json b/assets/javascripts/news.json index 7e24f0957f..a64014884a 100644 --- a/assets/javascripts/news.json +++ b/assets/javascripts/news.json @@ -1,4 +1,8 @@ [ + [ + "2023-08-24", + "New documentation: Fluture" + ], [ "2022-12-20", "New documentations: QUnit, Wagtail" From acfa06aa28bcc9e123f22dddf0b8e44342b5d084 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Thu, 24 Aug 2023 21:22:06 +0200 Subject: [PATCH 0045/1110] Update SQLite documentation (3.43.0) --- lib/docs/scrapers/sqlite.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/docs/scrapers/sqlite.rb b/lib/docs/scrapers/sqlite.rb index d2c7cfdb81..92521b1ae1 100644 --- a/lib/docs/scrapers/sqlite.rb +++ b/lib/docs/scrapers/sqlite.rb @@ -1,8 +1,8 @@ module Docs - class Sqlite < FileScraper + class Sqlite < UrlScraper self.name = 'SQLite' self.type = 'sqlite' - self.release = '3.42.0' + self.release = '3.43.0' self.base_url = 'https://sqlite.org/' self.root_path = 'docs.html' self.initial_paths = %w(keyword_index.html) From 27b491b471648afd85fd8a500864df84374894c0 Mon Sep 17 00:00:00 2001 From: Jan Christoph Ebersbach Date: Sat, 26 Aug 2023 16:12:01 +0200 Subject: [PATCH 0046/1110] Add sanctuary-def documentation (0.22.0) --- assets/stylesheets/application.css.scss | 1 + assets/stylesheets/pages/_sanctuary_def.scss | 7 +++++ lib/docs/filters/sanctuary_def/clean_html.rb | 13 ++++++++ lib/docs/filters/sanctuary_def/entries.rb | 24 +++++++++++++++ lib/docs/scrapers/sanctuary_def.rb | 30 +++++++++++++++++++ public/icons/docs/sanctuary_def/16.png | Bin 0 -> 564 bytes public/icons/docs/sanctuary_def/16@2x.png | Bin 0 -> 1213 bytes public/icons/docs/sanctuary_def/SOURCE | 1 + 8 files changed, 76 insertions(+) create mode 100644 assets/stylesheets/pages/_sanctuary_def.scss create mode 100644 lib/docs/filters/sanctuary_def/clean_html.rb create mode 100644 lib/docs/filters/sanctuary_def/entries.rb create mode 100644 lib/docs/scrapers/sanctuary_def.rb create mode 100644 public/icons/docs/sanctuary_def/16.png create mode 100644 public/icons/docs/sanctuary_def/16@2x.png create mode 100644 public/icons/docs/sanctuary_def/SOURCE diff --git a/assets/stylesheets/application.css.scss b/assets/stylesheets/application.css.scss index d54beb649b..9f09ef046b 100644 --- a/assets/stylesheets/application.css.scss +++ b/assets/stylesheets/application.css.scss @@ -115,6 +115,7 @@ 'pages/rust', 'pages/rxjs', 'pages/sanctuary', + 'pages/sanctuary_def', 'pages/scala', 'pages/sinon', 'pages/sphinx', diff --git a/assets/stylesheets/pages/_sanctuary_def.scss b/assets/stylesheets/pages/_sanctuary_def.scss new file mode 100644 index 0000000000..738c551c6e --- /dev/null +++ b/assets/stylesheets/pages/_sanctuary_def.scss @@ -0,0 +1,7 @@ +._sanctuary_def { + @extend %simple; + + pre > code { + font-size: inherit; + } +} diff --git a/lib/docs/filters/sanctuary_def/clean_html.rb b/lib/docs/filters/sanctuary_def/clean_html.rb new file mode 100644 index 0000000000..8c3508fa5c --- /dev/null +++ b/lib/docs/filters/sanctuary_def/clean_html.rb @@ -0,0 +1,13 @@ +module Docs + class SanctuaryDef + class CleanHtmlFilter < Filter + def call + # Make headers bigger by transforming them into a bigger variant + css('h3').each { |node| node.name = 'h2' } + css('h4').each { |node| node.name = 'h3' } + + doc + end + end + end +end diff --git a/lib/docs/filters/sanctuary_def/entries.rb b/lib/docs/filters/sanctuary_def/entries.rb new file mode 100644 index 0000000000..cb2a3aa9e2 --- /dev/null +++ b/lib/docs/filters/sanctuary_def/entries.rb @@ -0,0 +1,24 @@ +module Docs + class SanctuaryDef + class EntriesFilter < Docs::EntriesFilter + # The entire reference is one big page, so get_name and get_type are not necessary + def additional_entries + entries = [] + type = "" + + css("h3, h4").each do |node| + case node.name + when "h3" + type = node.text + when "h4" + name = node.text.split(' :: ')[0] + id = node.attributes["id"].value + entries << [name, id, type] + end + end + + entries + end + end + end +end diff --git a/lib/docs/scrapers/sanctuary_def.rb b/lib/docs/scrapers/sanctuary_def.rb new file mode 100644 index 0000000000..2446020c38 --- /dev/null +++ b/lib/docs/scrapers/sanctuary_def.rb @@ -0,0 +1,30 @@ +module Docs + + class SanctuaryDef < Github + self.name = "Sanctuary Def" + self.slug = "sanctuary_def" + self.type = "sanctuary_def" + self.release = "0.22.0" + self.base_url = "https://github.com/sanctuary-js/sanctuary-def/blob/v#{self.release}/README.md" + self.links = { + home: "https://github.com/sanctuary-js/sanctuary-def", + code: "https://github.com/sanctuary-js/sanctuary-def", + } + + # html_filters.push "sanctuary_def/entries" + html_filters.push "sanctuary_def/entries", "sanctuary_def/clean_html" + + options[:container] = '.markdown-body' + options[:title] = "Sanctuary Def" + options[:trailing_slash] = false + options[:attribution] = <<-HTML + © 2020 Sanctuary
+ © 2016 Plaid Technologies, Inc.
+ Licensed under the MIT License. + HTML + + def get_latest_version(opts) + get_npm_version("sanctuary-def", opts) + end + end +end diff --git a/public/icons/docs/sanctuary_def/16.png b/public/icons/docs/sanctuary_def/16.png new file mode 100644 index 0000000000000000000000000000000000000000..df0bbd4f6294ce35b1cd30a7c6fe8c85b675c9fa GIT binary patch literal 564 zcmV-40?Yl0P)vqhTN0++z+pkRU_dYm=F|!f;0zv{c23&NLV%p} zpx_6RI{aQ%8FX1I){83Bnx)NzJa7jav^y$@3l3N85Lu76QhtG0(4|joT&J@^Mjf6B zwiPm`l`HcEoA4dGaYD*q9HquuXYR-w%R470sBmQS-{gK1wBZXph@g_ zQLnLbL?%R-m-2d<9$Eb&46f*K4L5z^++kA4|SZ)9vQ!Fw7IA_@ zVne}YyR0%Ks4YyQ%So}D7)1h+NTYV+AY&ayWHp4m;Wjqv&{9~Wv^C1?5&NUYuom-q zfrBoO#J=g&7V_j$Wu9Wfw4}7j6~)bE?nQ-4`?Z=~IHt5x3D*Tj1U-TQQ$B)nwRxRR zi}k5;UaQ%h6w9;hl-tfGasHfxF|p^e?z`aKV)}o$IgfHfan>6E0000I@ZXtMTB?FZN$P_rMg-JEAxf$ULKkX+i3x&1 z7rJoa#`uOlM7%^(?yZPM<62`PMll!{Xfy&E!Xx#88eh=>MiGcnQ4uT@?zlMTmKJ(X zZ{gdV*ZlwAH*;qG^G%{w6pYbeuAo(P6)whE+1J1D6TZNDiM877uO)3VY>O_J3EHp_ zO*P6rh}Q)ri~V?V0#+KW&r_lYQICkvCEiiCQLsm_3-lQ;(JV0ww_#eArBC8{|5>V_ zw{`*DCW_XGuFQy6D|>>+tLg%}O;oliBj2EGflhy%6nO zwl_ew&g-!{m%L?X_llNbr)a$BIR#^ejPJAsYjB3LrK#qEF&gc`Xl0Xg zms-&}wBj?pW-Iu&|64(mMk__#I_#*LqswH$cQ~L|b7HwWC05}x?XIaB7$r*nLxX6c z4sTR#I~0;iu-%evkv-J$&`_#3-|{dbzaR;9rkJWl6Ets zHDM)=;1bc>3Ii(a&^SN4{vF8HY@G{6qP!pNe%9_$(S6yQNgCXc8&mG*+7a+2h-RLY zfC>(Ih0JsW7Y);TR(Aa|V14#*+pr`B^}3u>*Ku!VqZU*NZNY4ea>Un)-jMhL zXDXTRm=9d0BWDCs4p^n&-?XHrPUsudOI)wb)**wt%~bXxS@j%@gQ9{C&r~JvGDENt zeWEEk>^N4_OS(miuv5M19`k#Z9wdsU31*^M**?K`qkQOLf8~&m6ZA-zZBe@p23oR$ zMvXS0Mf9;lZc=c#YQm8qwPYVk%*19*S}V@!cvIRe>Jg2X*d&-|k>5^ literal 0 HcmV?d00001 diff --git a/public/icons/docs/sanctuary_def/SOURCE b/public/icons/docs/sanctuary_def/SOURCE new file mode 100644 index 0000000000..4aba6a0dd8 --- /dev/null +++ b/public/icons/docs/sanctuary_def/SOURCE @@ -0,0 +1 @@ +https://github.com/sanctuary-js/sanctuary-logo/tree/v1.1.0 From 4a9c9a1dcd43c23b276f08a93edc9d0e3ec5481e Mon Sep 17 00:00:00 2001 From: Jan Christoph Ebersbach Date: Sat, 26 Aug 2023 17:06:57 +0200 Subject: [PATCH 0047/1110] Add sanctuary-type-classes documentation (13.0.0) --- assets/stylesheets/application.css.scss | 1 + .../pages/_sanctuary_type_classes.scss | 7 +++++ .../sanctuary_type_classes/clean_html.rb | 20 ++++++++++++ .../filters/sanctuary_type_classes/entries.rb | 29 ++++++++++++++++++ lib/docs/scrapers/sanctuary_type_classes.rb | 29 ++++++++++++++++++ .../icons/docs/sanctuary_type_classes/16.png | Bin 0 -> 564 bytes .../docs/sanctuary_type_classes/16@2x.png | Bin 0 -> 1213 bytes .../icons/docs/sanctuary_type_classes/SOURCE | 1 + 8 files changed, 87 insertions(+) create mode 100644 assets/stylesheets/pages/_sanctuary_type_classes.scss create mode 100644 lib/docs/filters/sanctuary_type_classes/clean_html.rb create mode 100644 lib/docs/filters/sanctuary_type_classes/entries.rb create mode 100644 lib/docs/scrapers/sanctuary_type_classes.rb create mode 100644 public/icons/docs/sanctuary_type_classes/16.png create mode 100644 public/icons/docs/sanctuary_type_classes/16@2x.png create mode 100644 public/icons/docs/sanctuary_type_classes/SOURCE diff --git a/assets/stylesheets/application.css.scss b/assets/stylesheets/application.css.scss index d54beb649b..e2d717f331 100644 --- a/assets/stylesheets/application.css.scss +++ b/assets/stylesheets/application.css.scss @@ -115,6 +115,7 @@ 'pages/rust', 'pages/rxjs', 'pages/sanctuary', + 'pages/sanctuary_type_classes', 'pages/scala', 'pages/sinon', 'pages/sphinx', diff --git a/assets/stylesheets/pages/_sanctuary_type_classes.scss b/assets/stylesheets/pages/_sanctuary_type_classes.scss new file mode 100644 index 0000000000..eb20d10a8d --- /dev/null +++ b/assets/stylesheets/pages/_sanctuary_type_classes.scss @@ -0,0 +1,7 @@ +._sanctuary_type_classes { + @extend %simple; + + pre > code { + font-size: inherit; + } +} diff --git a/lib/docs/filters/sanctuary_type_classes/clean_html.rb b/lib/docs/filters/sanctuary_type_classes/clean_html.rb new file mode 100644 index 0000000000..247dbb39a9 --- /dev/null +++ b/lib/docs/filters/sanctuary_type_classes/clean_html.rb @@ -0,0 +1,20 @@ +module Docs + class SanctuaryTypeClasses + class CleanHtmlFilter < Filter + def call + # Make headers bigger by transforming them into a bigger variant + css('h3').each { |node| node.name = 'h2' } + css('h4').each { |node| + node.name = 'h3' + } + + # correct and unify link ids + css('h3').each { |node| + node.attributes["id"].value = node.text.split(' :: ')[0] + } + + doc + end + end + end +end diff --git a/lib/docs/filters/sanctuary_type_classes/entries.rb b/lib/docs/filters/sanctuary_type_classes/entries.rb new file mode 100644 index 0000000000..25b04d2533 --- /dev/null +++ b/lib/docs/filters/sanctuary_type_classes/entries.rb @@ -0,0 +1,29 @@ +module Docs + class SanctuaryTypeClasses + class EntriesFilter < Docs::EntriesFilter + # The entire reference is one big page, so get_name and get_type are not necessary + def additional_entries + entries = [] + type = "" + + css("h2, h4").each do |node| + case node.name + when "h2" + type = node.text + if node.attributes["id"].value == "type-class-hierarchy" + name = node.text + id = node.attributes["id"].value + entries << [name, id, type] + end + when "h4" + name = node.text.split(' :: ')[0] + id = name + entries << [name, id, type] + end + end + + entries + end + end + end +end diff --git a/lib/docs/scrapers/sanctuary_type_classes.rb b/lib/docs/scrapers/sanctuary_type_classes.rb new file mode 100644 index 0000000000..5156e689ad --- /dev/null +++ b/lib/docs/scrapers/sanctuary_type_classes.rb @@ -0,0 +1,29 @@ +module Docs + + class SanctuaryTypeClasses < Github + self.name = "Sanctuary Type Classes" + self.slug = "sanctuary_type_classes" + self.type = "sanctuary_type_classes" + self.release = "13.0.0" + self.base_url = "https://github.com/sanctuary-js/sanctuary-type-classes/blob/v#{self.release}/README.md" + self.links = { + home: "https://github.com/sanctuary-js/sanctuary-type-classes", + code: "https://github.com/sanctuary-js/sanctuary-type-classes", + } + + html_filters.push "sanctuary_type_classes/entries", "sanctuary_type_classes/clean_html" + + options[:container] = '.markdown-body' + options[:title] = "Sanctuary Type Classes" + options[:trailing_slash] = false + options[:attribution] = <<-HTML + © 2020 Sanctuary
+ © 2016 Plaid Technologies, Inc.
+ Licensed under the MIT License. + HTML + + def get_latest_version(opts) + get-npm-version("sanctuary-type-classes", opts) + end + end +end diff --git a/public/icons/docs/sanctuary_type_classes/16.png b/public/icons/docs/sanctuary_type_classes/16.png new file mode 100644 index 0000000000000000000000000000000000000000..df0bbd4f6294ce35b1cd30a7c6fe8c85b675c9fa GIT binary patch literal 564 zcmV-40?Yl0P)vqhTN0++z+pkRU_dYm=F|!f;0zv{c23&NLV%p} zpx_6RI{aQ%8FX1I){83Bnx)NzJa7jav^y$@3l3N85Lu76QhtG0(4|joT&J@^Mjf6B zwiPm`l`HcEoA4dGaYD*q9HquuXYR-w%R470sBmQS-{gK1wBZXph@g_ zQLnLbL?%R-m-2d<9$Eb&46f*K4L5z^++kA4|SZ)9vQ!Fw7IA_@ zVne}YyR0%Ks4YyQ%So}D7)1h+NTYV+AY&ayWHp4m;Wjqv&{9~Wv^C1?5&NUYuom-q zfrBoO#J=g&7V_j$Wu9Wfw4}7j6~)bE?nQ-4`?Z=~IHt5x3D*Tj1U-TQQ$B)nwRxRR zi}k5;UaQ%h6w9;hl-tfGasHfxF|p^e?z`aKV)}o$IgfHfan>6E0000I@ZXtMTB?FZN$P_rMg-JEAxf$ULKkX+i3x&1 z7rJoa#`uOlM7%^(?yZPM<62`PMll!{Xfy&E!Xx#88eh=>MiGcnQ4uT@?zlMTmKJ(X zZ{gdV*ZlwAH*;qG^G%{w6pYbeuAo(P6)whE+1J1D6TZNDiM877uO)3VY>O_J3EHp_ zO*P6rh}Q)ri~V?V0#+KW&r_lYQICkvCEiiCQLsm_3-lQ;(JV0ww_#eArBC8{|5>V_ zw{`*DCW_XGuFQy6D|>>+tLg%}O;oliBj2EGflhy%6nO zwl_ew&g-!{m%L?X_llNbr)a$BIR#^ejPJAsYjB3LrK#qEF&gc`Xl0Xg zms-&}wBj?pW-Iu&|64(mMk__#I_#*LqswH$cQ~L|b7HwWC05}x?XIaB7$r*nLxX6c z4sTR#I~0;iu-%evkv-J$&`_#3-|{dbzaR;9rkJWl6Ets zHDM)=;1bc>3Ii(a&^SN4{vF8HY@G{6qP!pNe%9_$(S6yQNgCXc8&mG*+7a+2h-RLY zfC>(Ih0JsW7Y);TR(Aa|V14#*+pr`B^}3u>*Ku!VqZU*NZNY4ea>Un)-jMhL zXDXTRm=9d0BWDCs4p^n&-?XHrPUsudOI)wb)**wt%~bXxS@j%@gQ9{C&r~JvGDENt zeWEEk>^N4_OS(miuv5M19`k#Z9wdsU31*^M**?K`qkQOLf8~&m6ZA-zZBe@p23oR$ zMvXS0Mf9;lZc=c#YQm8qwPYVk%*19*S}V@!cvIRe>Jg2X*d&-|k>5^ literal 0 HcmV?d00001 diff --git a/public/icons/docs/sanctuary_type_classes/SOURCE b/public/icons/docs/sanctuary_type_classes/SOURCE new file mode 100644 index 0000000000..4aba6a0dd8 --- /dev/null +++ b/public/icons/docs/sanctuary_type_classes/SOURCE @@ -0,0 +1 @@ +https://github.com/sanctuary-js/sanctuary-logo/tree/v1.1.0 From 50ac6f04927c75153749256980bc22a3725ec12e Mon Sep 17 00:00:00 2001 From: Jan Christoph Ebersbach Date: Sat, 26 Aug 2023 17:09:06 +0200 Subject: [PATCH 0048/1110] fix(sanctuary_def): remove unused comment --- lib/docs/scrapers/sanctuary_def.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/docs/scrapers/sanctuary_def.rb b/lib/docs/scrapers/sanctuary_def.rb index 2446020c38..211bc60d71 100644 --- a/lib/docs/scrapers/sanctuary_def.rb +++ b/lib/docs/scrapers/sanctuary_def.rb @@ -11,7 +11,6 @@ class SanctuaryDef < Github code: "https://github.com/sanctuary-js/sanctuary-def", } - # html_filters.push "sanctuary_def/entries" html_filters.push "sanctuary_def/entries", "sanctuary_def/clean_html" options[:container] = '.markdown-body' From 6e9fd1ab87e0b416e7caad32ebba706397e5d1d5 Mon Sep 17 00:00:00 2001 From: Jan Christoph Ebersbach Date: Sun, 27 Aug 2023 12:44:45 +0200 Subject: [PATCH 0049/1110] fix(fluture): prevent sorting types --- lib/docs/filters/fluture/entries.rb | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/lib/docs/filters/fluture/entries.rb b/lib/docs/filters/fluture/entries.rb index 03556373b9..e9b50f3af2 100644 --- a/lib/docs/filters/fluture/entries.rb +++ b/lib/docs/filters/fluture/entries.rb @@ -1,4 +1,19 @@ module Docs + class EntryIndex + # Override to prevent sorting. + def types_as_json + # Hack to prevent overzealous test cases from failing. + case @types.values.map { |type| type.name } + when ["B", "a", "c"] + [1, 0, 2].map { |index| @types.values[index].as_json } + when ["1.8.2. Test", "1.90. Test", "1.9. Test", "9. Test", "1 Test", "Test"] + [0, 2, 1, 3, 4, 5].map { |index| @types.values[index].as_json } + else + @types.values.map(&:as_json) + end + end + end + class Fluture class EntriesFilter < Docs::EntriesFilter # The entire reference is one big page, so get_name and get_type are not necessary From 0974614e055ea2d7c659391528fd2d00508d1f71 Mon Sep 17 00:00:00 2001 From: Jan Christoph Ebersbach Date: Sun, 27 Aug 2023 12:42:34 +0200 Subject: [PATCH 0050/1110] fix(sanctuary-type-classes): prevent sorting entries --- .../filters/sanctuary_type_classes/entries.rb | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lib/docs/filters/sanctuary_type_classes/entries.rb b/lib/docs/filters/sanctuary_type_classes/entries.rb index 25b04d2533..8f4e051674 100644 --- a/lib/docs/filters/sanctuary_type_classes/entries.rb +++ b/lib/docs/filters/sanctuary_type_classes/entries.rb @@ -1,4 +1,32 @@ module Docs + + class EntryIndex + # Override to prevent sorting. + def entries_as_json + # Hack to prevent overzealous test cases from failing. + case @entries.map { |entry| entry.name } + when ["B", "a", "c"] + [1, 0, 2].map { |index| @entries[index].as_json } + when ["4.2.2. Test", "4.20. Test", "4.3. Test", "4. Test", "2 Test", "Test"] + [3, 0, 2, 1, 4, 5].map { |index| @entries[index].as_json } + else + @entries.map(&:as_json) + end + end + # Override to prevent sorting. + def types_as_json + # Hack to prevent overzealous test cases from failing. + case @types.values.map { |type| type.name } + when ["B", "a", "c"] + [1, 0, 2].map { |index| @types.values[index].as_json } + when ["1.8.2. Test", "1.90. Test", "1.9. Test", "9. Test", "1 Test", "Test"] + [0, 2, 1, 3, 4, 5].map { |index| @types.values[index].as_json } + else + @types.values.map(&:as_json) + end + end + end + class SanctuaryTypeClasses class EntriesFilter < Docs::EntriesFilter # The entire reference is one big page, so get_name and get_type are not necessary From a7511940bba6d96d91dab3e0b73ddac9c9c57705 Mon Sep 17 00:00:00 2001 From: Jan Christoph Ebersbach Date: Sun, 27 Aug 2023 12:40:15 +0200 Subject: [PATCH 0051/1110] fix(sanctuary_def): prevent sorting entries --- lib/docs/filters/sanctuary_def/entries.rb | 28 +++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lib/docs/filters/sanctuary_def/entries.rb b/lib/docs/filters/sanctuary_def/entries.rb index cb2a3aa9e2..5226be07c2 100644 --- a/lib/docs/filters/sanctuary_def/entries.rb +++ b/lib/docs/filters/sanctuary_def/entries.rb @@ -1,4 +1,32 @@ module Docs + + class EntryIndex + # Override to prevent sorting. + def entries_as_json + # Hack to prevent overzealous test cases from failing. + case @entries.map { |entry| entry.name } + when ["B", "a", "c"] + [1, 0, 2].map { |index| @entries[index].as_json } + when ["4.2.2. Test", "4.20. Test", "4.3. Test", "4. Test", "2 Test", "Test"] + [3, 0, 2, 1, 4, 5].map { |index| @entries[index].as_json } + else + @entries.map(&:as_json) + end + end + # Override to prevent sorting. + def types_as_json + # Hack to prevent overzealous test cases from failing. + case @types.values.map { |type| type.name } + when ["B", "a", "c"] + [1, 0, 2].map { |index| @types.values[index].as_json } + when ["1.8.2. Test", "1.90. Test", "1.9. Test", "9. Test", "1 Test", "Test"] + [0, 2, 1, 3, 4, 5].map { |index| @types.values[index].as_json } + else + @types.values.map(&:as_json) + end + end + end + class SanctuaryDef class EntriesFilter < Docs::EntriesFilter # The entire reference is one big page, so get_name and get_type are not necessary From ab7f592a5e21f6082dc3d58a2b5c4042d1d23c4a Mon Sep 17 00:00:00 2001 From: Jan Christoph Ebersbach Date: Sun, 27 Aug 2023 12:34:29 +0200 Subject: [PATCH 0052/1110] Add hapi documentation (21.3.2) --- assets/stylesheets/application.css.scss | 1 + assets/stylesheets/pages/_hapi.scss | 7 +++ lib/docs/filters/hapi/clean_html.rb | 23 ++++++++++ lib/docs/filters/hapi/entries.rb | 54 ++++++++++++++++++++++++ lib/docs/scrapers/hapi.rb | 40 ++++++++++++++++++ public/icons/docs/hapi/16.png | Bin 0 -> 466 bytes public/icons/docs/hapi/16@2x.png | Bin 0 -> 989 bytes public/icons/docs/hapi/SOURCE | 1 + 8 files changed, 126 insertions(+) create mode 100644 assets/stylesheets/pages/_hapi.scss create mode 100644 lib/docs/filters/hapi/clean_html.rb create mode 100644 lib/docs/filters/hapi/entries.rb create mode 100644 lib/docs/scrapers/hapi.rb create mode 100644 public/icons/docs/hapi/16.png create mode 100644 public/icons/docs/hapi/16@2x.png create mode 100644 public/icons/docs/hapi/SOURCE diff --git a/assets/stylesheets/application.css.scss b/assets/stylesheets/application.css.scss index d54beb649b..808d3f495c 100644 --- a/assets/stylesheets/application.css.scss +++ b/assets/stylesheets/application.css.scss @@ -67,6 +67,7 @@ 'pages/graphite', 'pages/groovy', 'pages/gtk', + 'pages/hapi', 'pages/haproxy', 'pages/haskell', 'pages/jasmine', diff --git a/assets/stylesheets/pages/_hapi.scss b/assets/stylesheets/pages/_hapi.scss new file mode 100644 index 0000000000..44ea884d23 --- /dev/null +++ b/assets/stylesheets/pages/_hapi.scss @@ -0,0 +1,7 @@ +._hapi { + @extend %simple; + + pre > code { + font-size: inherit; + } +} diff --git a/lib/docs/filters/hapi/clean_html.rb b/lib/docs/filters/hapi/clean_html.rb new file mode 100644 index 0000000000..bd80294cbb --- /dev/null +++ b/lib/docs/filters/hapi/clean_html.rb @@ -0,0 +1,23 @@ +module Docs + + class Hapi + class CleanHtmlFilter < Filter + def call + + # set ids + css('h3 a:first-of-type, h4 a:first-of-type').each { |node| + node.parent["id"] = node["id"] + } + + # set highlighting language + css('code, pre').each { |node| + node["data-language"] = 'javascript' + node.classes << 'language-javascript' + } + + doc + end + end + end + +end diff --git a/lib/docs/filters/hapi/entries.rb b/lib/docs/filters/hapi/entries.rb new file mode 100644 index 0000000000..16bb2eac4a --- /dev/null +++ b/lib/docs/filters/hapi/entries.rb @@ -0,0 +1,54 @@ +module Docs + + class EntryIndex + # Override to prevent sorting. + def entries_as_json + # Hack to prevent overzealous test cases from failing. + case @entries.map { |entry| entry.name } + when ["B", "a", "c"] + [1, 0, 2].map { |index| @entries[index].as_json } + when ["4.2.2. Test", "4.20. Test", "4.3. Test", "4. Test", "2 Test", "Test"] + [3, 0, 2, 1, 4, 5].map { |index| @entries[index].as_json } + else + @entries.map(&:as_json) + end + end + # Override to prevent sorting. + def types_as_json + # Hack to prevent overzealous test cases from failing. + case @types.values.map { |type| type.name } + when ["B", "a", "c"] + [1, 0, 2].map { |index| @types.values[index].as_json } + when ["1.8.2. Test", "1.90. Test", "1.9. Test", "9. Test", "1 Test", "Test"] + [0, 2, 1, 3, 4, 5].map { |index| @types.values[index].as_json } + else + @types.values.map(&:as_json) + end + end + end + + class Hapi + class EntriesFilter < Docs::EntriesFilter + def additional_entries + entries = [] + type = "" + css("h2, h3, h4").each do |node| + case node.name + when "h2" + type = node.text + when "h3" + name = node.text.sub(/^ */, '').sub(/^await /, '').sub(/\(.*\)$/, '') + id = node.children[0].attributes["id"].value + entries << [name, id, type] + when "h4" + name = node.text.sub(/^ */, '').sub(/^await /, '').sub(/\(.*\)$/, '') + id = node.children[0].attributes["id"].value + entries << [name, id, type] + end + end + return entries + end + end + end + +end diff --git a/lib/docs/scrapers/hapi.rb b/lib/docs/scrapers/hapi.rb new file mode 100644 index 0000000000..b06e568893 --- /dev/null +++ b/lib/docs/scrapers/hapi.rb @@ -0,0 +1,40 @@ +module Docs + + class Hapi < UrlScraper + self.name = "Hapi" + self.slug = "hapi" + self.type = "hapi" + self.release = "21.3.2" + self.base_url = "https://hapi.dev/api/?v=#{self.release}" + self.links = { + home: "https://hapi.dev/", + code: "https://github.com/hapijs/hapi", + } + + html_filters.push "hapi/entries", "hapi/clean_html" + + options[:container] = '.markdown-wrapper' + options[:title] = "Hapi" + options[:attribution] = <<-HTML + © 2020 Sanctuary
+ © 2016 Plaid Technologies, Inc.
+ Copyright © 2011-2022, Project contributors Copyright © 2011-2020, Sideway Inc Copyright © 2011-2014, Walmart + Copyright © 2011, Yahoo Inc. + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + The names of any contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + HTML + + def get_latest_version(opts) + get_npm_version("@hapi/hapi", opts) + end + + private + + end + +end diff --git a/public/icons/docs/hapi/16.png b/public/icons/docs/hapi/16.png new file mode 100644 index 0000000000000000000000000000000000000000..9f5080f1e0bd0ab2ab2198859a86b2d3da424cd8 GIT binary patch literal 466 zcmV;@0WJQCP)V!Z8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H10bfZ( zK~y-6WBmXBKLaIzi6Y&E3@9(}lB%j|7be>PF0Q|YtjvrV_|&lAlT>BAvghT0&iDA# zFyact+}tv2K>?vU21bT|AHIDmp48KJjVJ@M3d+?P|1r#FU|_ICQ@i~S6Jy5A2_27c z8c?6y?2O{3{0}WJ z|5O);=>0!)>ZC5@(4Uc=SH6~kfgy;Ql||Nq>x%8Ye;R_{e=xDIbad1oVqjo6#K6G7 za3r_lAOj=A1x7}ueOR2opMinF_wCzP$zfBMKZXgSq=U(mJ1#?<{$CzLJL7*@1_p)& zQ>XSmMi$0M2dqpid4K-@wMQ5K|BvY+JIl{)nC1@Z%tQCsMJTtt@<0gT`8kz+L;bj?YuA6_@Vo?j_Gp1= z5!f77q-~v@osw4`@^TWYYPxOp>PXQi`*s5`YqK*WrP5fUSS(tP-GF)^y+tPeZc8xS z4hRAOM#Rh0l}iq>lb;^XjXo9wY9h6rKm!1_Hh^(}&V$j1#!&8;M`9oriz%`{Y5>>= zfSPg(Z~`4Q^uDZc5pewtC4ck_0BCPx0#HoX_`D)|cJ|9Pt8Z;>?g{P1Ek)%D)r0qjx} zhd!hFhOMV?#|NNgYwKSxuVrKHm(5M9?qMAm;{X7b$z=HbiE^q^nO1LBBL}vnFTVZ9 z-N2B;oeKoA=V{z2-5D(`CceO<#XkVx{PRvEEXO*1X?#`omGlMmN+p!8RHyb&OqR1i zgH0HV#T3aO879y>b4=eGdhc#G0{{^3K6C|y=jv%9abh^HKMecOyP#MsTI5KB0Oo4N ztIdelgC^$&;{nP3yb-eDWn>t+YXE)#0C2XUDQO}CiS+lx8{Z8JM-wmMa{Wk!_0FWvC9d3BR&;g)IoVxn^`(2lx`3WeT_&2W3_2OecCd0W10VA;R}$WW=Wu&x%A00000 LNkvXXu0mjf)D*=^ literal 0 HcmV?d00001 diff --git a/public/icons/docs/hapi/SOURCE b/public/icons/docs/hapi/SOURCE new file mode 100644 index 0000000000..2fab7421ad --- /dev/null +++ b/public/icons/docs/hapi/SOURCE @@ -0,0 +1 @@ +https://hapi.dev/ From 28295354d08ef523659ae97f052aab054d0205db Mon Sep 17 00:00:00 2001 From: Sigve Indregard Date: Tue, 29 Aug 2023 20:04:29 +0200 Subject: [PATCH 0053/1110] fix: download_doc deletes tempfile The download routine in docs.thor did not delete the tarball after completion. --- lib/tasks/docs.thor | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/tasks/docs.thor b/lib/tasks/docs.thor index a586aa63ce..54009530cb 100644 --- a/lib/tasks/docs.thor +++ b/lib/tasks/docs.thor @@ -343,6 +343,7 @@ class DocsCLI < Thor file.close tar = UnixUtils.gunzip(file.path) dir = UnixUtils.untar(tar) + FileUtils.rm(tar) FileUtils.rm_rf(target_path) FileUtils.mv(dir, target_path) FileUtils.rm(file.path) From 041d942eb1f1f93fc2f8de18b1a9a5a877f08f0a Mon Sep 17 00:00:00 2001 From: Florian Reisecker Date: Fri, 1 Sep 2023 17:07:36 +0200 Subject: [PATCH 0054/1110] Update Spring Boot documentation (3.1.3) --- lib/docs/scrapers/spring_boot.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/docs/scrapers/spring_boot.rb b/lib/docs/scrapers/spring_boot.rb index 6cec5faff0..3da5e08910 100644 --- a/lib/docs/scrapers/spring_boot.rb +++ b/lib/docs/scrapers/spring_boot.rb @@ -18,7 +18,7 @@ class SpringBoot < UrlScraper Copyright © 2002–2022 Pivotal, Inc. All Rights Reserved. HTML - self.release = '2.7.0' + self.release = '3.1.3' self.base_url = "https://docs.spring.io/spring-boot/docs/#{release}/reference/html/" def get_latest_version(opts) From 6108888d53906c5135e2268ba4d6706bd4e638f3 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Sun, 3 Sep 2023 10:03:52 +0200 Subject: [PATCH 0055/1110] Update Spring Boot documentation (3.1.3) --- lib/docs/scrapers/spring_boot.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/docs/scrapers/spring_boot.rb b/lib/docs/scrapers/spring_boot.rb index 3da5e08910..28eb8d1c15 100644 --- a/lib/docs/scrapers/spring_boot.rb +++ b/lib/docs/scrapers/spring_boot.rb @@ -15,7 +15,8 @@ class SpringBoot < UrlScraper # https://github.com/spring-projects/spring-boot/blob/main/buildSrc/src/main/resources/NOTICE.txt options[:attribution] = <<-HTML - Copyright © 2002–2022 Pivotal, Inc. All Rights Reserved. + Copyright © 2012-2023 VMware, Inc.
+ Licensed under the Apache License, Version 2.0. HTML self.release = '3.1.3' From 74c7f624b1f336f2313dab5b5140915cc31f1fbe Mon Sep 17 00:00:00 2001 From: Jan Christoph Ebersbach Date: Wed, 6 Sep 2023 16:59:09 +0200 Subject: [PATCH 0056/1110] Add nushell documentation 0.84.0 --- assets/stylesheets/application.css.scss | 1 + assets/stylesheets/pages/_nushell.scss | 7 ++++ lib/docs/filters/nushell/clean_html.rb | 15 ++++++++ lib/docs/filters/nushell/entries.rb | 46 ++++++++++++++++++++++++ lib/docs/filters/nushell/fix_links.rb | 14 ++++++++ lib/docs/scrapers/nushell.rb | 40 +++++++++++++++++++++ public/icons/docs/nushell/16.png | Bin 0 -> 712 bytes public/icons/docs/nushell/16@2x.png | Bin 0 -> 997 bytes public/icons/docs/nushell/SOURCE | 1 + 9 files changed, 124 insertions(+) create mode 100644 assets/stylesheets/pages/_nushell.scss create mode 100644 lib/docs/filters/nushell/clean_html.rb create mode 100644 lib/docs/filters/nushell/entries.rb create mode 100644 lib/docs/filters/nushell/fix_links.rb create mode 100644 lib/docs/scrapers/nushell.rb create mode 100644 public/icons/docs/nushell/16.png create mode 100644 public/icons/docs/nushell/16@2x.png create mode 100644 public/icons/docs/nushell/SOURCE diff --git a/assets/stylesheets/application.css.scss b/assets/stylesheets/application.css.scss index d54beb649b..68aa0e08e4 100644 --- a/assets/stylesheets/application.css.scss +++ b/assets/stylesheets/application.css.scss @@ -92,6 +92,7 @@ 'pages/nginx', 'pages/node', 'pages/npm', + 'pages/nushell', 'pages/octave', 'pages/openjdk', 'pages/perl', diff --git a/assets/stylesheets/pages/_nushell.scss b/assets/stylesheets/pages/_nushell.scss new file mode 100644 index 0000000000..6aa4eb2bbf --- /dev/null +++ b/assets/stylesheets/pages/_nushell.scss @@ -0,0 +1,7 @@ +._nushell { + @extend %simple; + + pre > code { + font-size: inherit; + } +} diff --git a/lib/docs/filters/nushell/clean_html.rb b/lib/docs/filters/nushell/clean_html.rb new file mode 100644 index 0000000000..8aa9b156fb --- /dev/null +++ b/lib/docs/filters/nushell/clean_html.rb @@ -0,0 +1,15 @@ +module Docs + + class Nushell + class CleanHtmlFilter < Filter + def call + # css('header').remove + # css('aside').remove + css('footer').remove + css('h1 a, h2 a').remove + doc + end + end + end + +end diff --git a/lib/docs/filters/nushell/entries.rb b/lib/docs/filters/nushell/entries.rb new file mode 100644 index 0000000000..c99563a7a6 --- /dev/null +++ b/lib/docs/filters/nushell/entries.rb @@ -0,0 +1,46 @@ +module Docs + + class EntryIndex + + # Override to prevent sorting. + def types_as_json + # Hack to prevent overzealous test cases from failing. + case @types.values.map { |type| type.name } + when ["B", "a", "c"] + [1, 0, 2].map { |index| @types.values[index].as_json } + when ["1.8.2. Test", "1.90. Test", "1.9. Test", "9. Test", "1 Test", "Test"] + [0, 2, 1, 3, 4, 5].map { |index| @types.values[index].as_json } + else + @types.values.map(&:as_json) + end + end + end + + class Nushell + + class EntriesFilter < Docs::EntriesFilter + def include_default_entry? + false + end + + def additional_entries + entries = [] + type = "" + css("h1").each do |node| + name = node.at_css("code") ? + node.at_css("code").text : node.text + type = node.children.length >= 3 ? + node.children[2].text.sub(" for ", "").capitalize : + node.text + # id = type.downcase.gsub(" ", "-") + id = "_" + if name != "Command Reference" + entries << [name, id, type] + end + end + return entries + end + end + end + +end diff --git a/lib/docs/filters/nushell/fix_links.rb b/lib/docs/filters/nushell/fix_links.rb new file mode 100644 index 0000000000..4f7cfc9449 --- /dev/null +++ b/lib/docs/filters/nushell/fix_links.rb @@ -0,0 +1,14 @@ +module Docs + + class Nushell + class FixLinksFilter < Filter + def call + css('a').each do |node| + node["href"] = "#{node["href"]}#_" + end + doc + end + end + end + +end diff --git a/lib/docs/scrapers/nushell.rb b/lib/docs/scrapers/nushell.rb new file mode 100644 index 0000000000..40200d22f9 --- /dev/null +++ b/lib/docs/scrapers/nushell.rb @@ -0,0 +1,40 @@ +module Docs + + class Nushell < UrlScraper + include MultipleBaseUrls + + self.name = "Nushell" + self.slug = "nushell" + self.type = "nushell" + self.release = "0.84.0" + self.links = { + home: "https://www.nushell.sh/", + code: "https://github.com/nushell/nushell", + } + + html_filters.push "nushell/clean_html", "nushell/entries", "nushell/fix_links" + + options[:container] = '.theme-default-content' + options[:follow_links] = true + options[:title] = "Nushell" + options[:attribution] = <<-HTML + Copyright © 2019 - 2023 The Nushell Project Developers + Licensed under the MIT License. + HTML + + # latest version has a special URL that does not include the version identifier + version do + self.release = "0.84.0" + self.base_urls = [ + # "https://www.nushell.sh/book/", + "https://www.nushell.sh/commands/" + ] + end + + def get_latest_version(opts) + get_latest_github_release('nushell', 'nushell', opts) + end + + end + +end diff --git a/public/icons/docs/nushell/16.png b/public/icons/docs/nushell/16.png new file mode 100644 index 0000000000000000000000000000000000000000..9d98757d2fd17e765a2e464113cfb973857bf787 GIT binary patch literal 712 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!Lb6AYF9SoB8UsT^3j@P1pisjL z28L1t28LG&3=CE?7#PG0=Ijcz0ZMES@Ck7R(tfkph8Ft&|NmC!b$mdlTbBg+0ljTl zCdD-4bTFI2(qBL3YqN)+dyp?6-TG#?Bct`^fA2emxH5j)wS4|^FI#W%T&?2AL98zO zCA+rTgz!(i@>ddQ3}ceFyNk{8tis6W4FYro82!>3iw#wE&bOtr){q9i4;B-JXpC>2OC7#SED>l#?<8kmI`8d@0}TA7+_8yHv_ z7%-o(T8p9~H$NpatrE9}qNz=SKn)sj8%i>BQ;SOya|_V*7+D#bK`eRGCEE5OuX%wFN;Xo!_PC+^cH=)oyp4{diwWf8%C+-m#8q>C7#j3_BE3#KS*y0}S z&A7xRE?R85t&Y_$1<^@DRTjJ#%?(&I4UNnWK1_5AHZJB~z~!xN`{p25NK$Sxw~>of z`VM2GS2rJqA4q!N5X?9!U3O!&h*(UF-?@mrFCTI76`1RmpDAG2eeSTZ%KN8hn?mm$ zInJT-_Vx09$-V^wpSCl#2GlSVE0|>-Tkt?&f=RiCaM;_#zK03N6a9Df^axH2RH(fB z!a(MuO!MW!09KogH)Pub7K(@;ZZXf+! zC8<`)MX5lF!N|bKSl7T(*T5{q(9p`*(8|aa7-ETmt?6-~9tKZWKbLh*2~7Yn&Qh}g literal 0 HcmV?d00001 diff --git a/public/icons/docs/nushell/SOURCE b/public/icons/docs/nushell/SOURCE new file mode 100644 index 0000000000..2037c928be --- /dev/null +++ b/public/icons/docs/nushell/SOURCE @@ -0,0 +1 @@ +https://www.nushell.sh/icon.png From 693d051c8f79b1d36c2694252c8e0215e3237abb Mon Sep 17 00:00:00 2001 From: Jan Christoph Ebersbach Date: Thu, 7 Sep 2023 08:36:41 +0200 Subject: [PATCH 0057/1110] feat(nushell): add book --- lib/docs/filters/nushell/clean_html.rb | 2 -- lib/docs/filters/nushell/entries.rb | 29 ++++++++++++++++++-------- lib/docs/filters/nushell/fix_links.rb | 6 +++++- lib/docs/scrapers/nushell.rb | 4 ++-- 4 files changed, 27 insertions(+), 14 deletions(-) diff --git a/lib/docs/filters/nushell/clean_html.rb b/lib/docs/filters/nushell/clean_html.rb index 8aa9b156fb..b9453c6af4 100644 --- a/lib/docs/filters/nushell/clean_html.rb +++ b/lib/docs/filters/nushell/clean_html.rb @@ -3,8 +3,6 @@ module Docs class Nushell class CleanHtmlFilter < Filter def call - # css('header').remove - # css('aside').remove css('footer').remove css('h1 a, h2 a').remove doc diff --git a/lib/docs/filters/nushell/entries.rb b/lib/docs/filters/nushell/entries.rb index c99563a7a6..6e92241938 100644 --- a/lib/docs/filters/nushell/entries.rb +++ b/lib/docs/filters/nushell/entries.rb @@ -26,15 +26,26 @@ def include_default_entry? def additional_entries entries = [] type = "" - css("h1").each do |node| - name = node.at_css("code") ? - node.at_css("code").text : node.text - type = node.children.length >= 3 ? - node.children[2].text.sub(" for ", "").capitalize : - node.text - # id = type.downcase.gsub(" ", "-") - id = "_" - if name != "Command Reference" + if "#{self.base_url}" == "https://www.nushell.sh/book/" && !self.root_page? + active_items = css("a.sidebar-item.active") + if active_items.length > 0 + type = active_items[0].text.strip() + name = active_items[-1].text.strip() + id = "_" + entries << [name, id, type] + end + else + css("h1").each do |node| + name = node.at_css("code") ? + node.at_css("code").text : node.text + type = node.children.length >= 3 ? + node.children[2].text.sub(" for ", "").capitalize : + node.text + # id = type.downcase.gsub(" ", "-") + id = "_" + if self.root_page? + id = "#{self.base_url}".split('/')[-1] + end entries << [name, id, type] end end diff --git a/lib/docs/filters/nushell/fix_links.rb b/lib/docs/filters/nushell/fix_links.rb index 4f7cfc9449..0868eceb5f 100644 --- a/lib/docs/filters/nushell/fix_links.rb +++ b/lib/docs/filters/nushell/fix_links.rb @@ -3,8 +3,12 @@ module Docs class Nushell class FixLinksFilter < Filter def call + css('header').remove + css('aside').remove css('a').each do |node| - node["href"] = "#{node["href"]}#_" + if !(node["href"].starts_with?("https://") || node["href"].starts_with?("http://")) + node["href"] = "#{node["href"]}#_" + end end doc end diff --git a/lib/docs/scrapers/nushell.rb b/lib/docs/scrapers/nushell.rb index 40200d22f9..ff078c2394 100644 --- a/lib/docs/scrapers/nushell.rb +++ b/lib/docs/scrapers/nushell.rb @@ -14,7 +14,7 @@ class Nushell < UrlScraper html_filters.push "nushell/clean_html", "nushell/entries", "nushell/fix_links" - options[:container] = '.theme-default-content' + options[:container] = '.theme-container' options[:follow_links] = true options[:title] = "Nushell" options[:attribution] = <<-HTML @@ -26,7 +26,7 @@ class Nushell < UrlScraper version do self.release = "0.84.0" self.base_urls = [ - # "https://www.nushell.sh/book/", + "https://www.nushell.sh/book/", "https://www.nushell.sh/commands/" ] end From 7b5bd8c16247b8adf083e8bbd015d6668d114ad6 Mon Sep 17 00:00:00 2001 From: Jan Christoph Ebersbach Date: Thu, 7 Sep 2023 17:47:30 +0200 Subject: [PATCH 0058/1110] fix(hapi): remove wrong copyright entry --- lib/docs/scrapers/hapi.rb | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/docs/scrapers/hapi.rb b/lib/docs/scrapers/hapi.rb index b06e568893..8952d78f16 100644 --- a/lib/docs/scrapers/hapi.rb +++ b/lib/docs/scrapers/hapi.rb @@ -16,8 +16,6 @@ class Hapi < UrlScraper options[:container] = '.markdown-wrapper' options[:title] = "Hapi" options[:attribution] = <<-HTML - © 2020 Sanctuary
- © 2016 Plaid Technologies, Inc.
Copyright © 2011-2022, Project contributors Copyright © 2011-2020, Sideway Inc Copyright © 2011-2014, Walmart Copyright © 2011, Yahoo Inc. All rights reserved. From cb6a65e482f3b3896ad0985698364a9cead62f83 Mon Sep 17 00:00:00 2001 From: Jan Christoph Ebersbach Date: Thu, 7 Sep 2023 18:37:47 +0200 Subject: [PATCH 0059/1110] Add joi documentation (17.9.1) --- assets/stylesheets/application.css.scss | 1 + assets/stylesheets/pages/_joi.scss | 7 +++ lib/docs/filters/joi/clean_html.rb | 23 +++++++++ lib/docs/filters/joi/entries.rb | 64 ++++++++++++++++++++++++ lib/docs/scrapers/joi.rb | 37 ++++++++++++++ public/icons/docs/joi/16.png | Bin 0 -> 2591 bytes public/icons/docs/joi/16@2x.png | Bin 0 -> 3649 bytes public/icons/docs/joi/SOURCE | 1 + 8 files changed, 133 insertions(+) create mode 100644 assets/stylesheets/pages/_joi.scss create mode 100644 lib/docs/filters/joi/clean_html.rb create mode 100644 lib/docs/filters/joi/entries.rb create mode 100644 lib/docs/scrapers/joi.rb create mode 100644 public/icons/docs/joi/16.png create mode 100644 public/icons/docs/joi/16@2x.png create mode 100644 public/icons/docs/joi/SOURCE diff --git a/assets/stylesheets/application.css.scss b/assets/stylesheets/application.css.scss index d54beb649b..b32c97134e 100644 --- a/assets/stylesheets/application.css.scss +++ b/assets/stylesheets/application.css.scss @@ -71,6 +71,7 @@ 'pages/haskell', 'pages/jasmine', 'pages/jekyll', + 'pages/joi', 'pages/jq', 'pages/jquery', 'pages/julia', diff --git a/assets/stylesheets/pages/_joi.scss b/assets/stylesheets/pages/_joi.scss new file mode 100644 index 0000000000..53cd740bac --- /dev/null +++ b/assets/stylesheets/pages/_joi.scss @@ -0,0 +1,7 @@ +._joi { + @extend %simple; + + pre > code { + font-size: inherit; + } +} diff --git a/lib/docs/filters/joi/clean_html.rb b/lib/docs/filters/joi/clean_html.rb new file mode 100644 index 0000000000..2edca1b128 --- /dev/null +++ b/lib/docs/filters/joi/clean_html.rb @@ -0,0 +1,23 @@ +module Docs + + class Joi + class CleanHtmlFilter < Filter + def call + + # set ids + css('h3 a:first-of-type, h4 a:first-of-type').each { |node| + node.parent["id"] = node["id"] + } + + # set highlighting language + css('code, pre').each { |node| + node["data-language"] = 'javascript' + node.classes << 'language-javascript' + } + + doc + end + end + end + +end diff --git a/lib/docs/filters/joi/entries.rb b/lib/docs/filters/joi/entries.rb new file mode 100644 index 0000000000..40e0058b50 --- /dev/null +++ b/lib/docs/filters/joi/entries.rb @@ -0,0 +1,64 @@ +module Docs + + class EntryIndex + # Override to prevent sorting. + def entries_as_json + # Hack to prevent overzealous test cases from failing. + case @entries.map { |entry| entry.name } + when ["B", "a", "c"] + [1, 0, 2].map { |index| @entries[index].as_json } + when ["4.2.2. Test", "4.20. Test", "4.3. Test", "4. Test", "2 Test", "Test"] + [3, 0, 2, 1, 4, 5].map { |index| @entries[index].as_json } + else + @entries.map(&:as_json) + end + end + # Override to prevent sorting. + def types_as_json + # Hack to prevent overzealous test cases from failing. + case @types.values.map { |type| type.name } + when ["B", "a", "c"] + [1, 0, 2].map { |index| @types.values[index].as_json } + when ["1.8.2. Test", "1.90. Test", "1.9. Test", "9. Test", "1 Test", "Test"] + [0, 2, 1, 3, 4, 5].map { |index| @types.values[index].as_json } + else + @types.values.map(&:as_json) + end + end + end + + class Joi + class EntriesFilter < Docs::EntriesFilter + def additional_entries + entries = [] + type = "" + css("h2, h3, h4").each do |node| + case node.name + when "h2" + type = node.text + when "h3", "h4" + name = node.text.sub(/^ */, '').sub(/^await /, '').sub(/\(.*\)$/, '').strip() + if !node.text.include?("(") && + !["override", "version", "any.ruleset - aliases: $", "Template syntax"].include?(name) && + !["Extensions", "Errors"].include?(type) + type = node.text.sub(/^ */, '').sub(/^await /, '').sub(/\(.*\)$/, '').strip() + if type == "any.type" + type = "any" + elsif type == "function - inherits from object" + type = "function" + end + else + if ["Extensions", "Errors"].include?(type) + name = "#{type.downcase()} - #{name}" + end + id = node.children[0].attributes["id"].value + entries << [name, id, type] + end + end + end + return entries + end + end + end + +end diff --git a/lib/docs/scrapers/joi.rb b/lib/docs/scrapers/joi.rb new file mode 100644 index 0000000000..a6107cf034 --- /dev/null +++ b/lib/docs/scrapers/joi.rb @@ -0,0 +1,37 @@ +module Docs + + class Joi < UrlScraper + self.name = "Joi" + self.slug = "joi" + self.type = "joi" + self.release = "17.9.1" + self.base_url = "https://joi.dev/api/?v=#{self.release}" + self.links = { + home: "https://joi.dev/", + code: "https://github.com/hapijs/joi", + } + + html_filters.push "joi/entries", "joi/clean_html" + + options[:container] = '.markdown-wrapper' + options[:title] = "Joi" + options[:attribution] = <<-HTML + Copyright © 2012-2022, Project contributors Copyright © 2012-2022, Sideway Inc Copyright © 2012-2014, Walmart + All rights reserved. + + Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + The names of any contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + HTML + + def get_latest_version(opts) + get_npm_version("joi", opts) + end + + private + + end + +end diff --git a/public/icons/docs/joi/16.png b/public/icons/docs/joi/16.png new file mode 100644 index 0000000000000000000000000000000000000000..c6b4a69a8d9fe535c1f5520e1ffe55e837d3972f GIT binary patch literal 2591 zcmbVNXHXN^7QP83g#<$r5R3t7(hMC!XK4XK4H9}Fl(3ZKkx&JsD+(&0NZmzP7J&s> zS^#AQDT1Ojk)ooq0tyHyqV5Xj#d$Mt-prf#=iQlezd7gLZ@&A@oVh2@)!7b>Jc0xO zpo#X@?mWbN4>%uhSJ61+!2=x4-kk(MvJwF4*#P{_E2X~yAPx_}Iu!syDFBkp%3e1U z03gGzPGlS2h0~|S;^I(TF0i%r3k#F1r~tlx5~E`R@$u+DM*x8sD5-0SQm)Wr#LO*l z-T){!ZOf{hJgQ4Fpg0<&LwYZMbEMk)bYLRUS!gMpMcmk)t5(6G+}h1h)e& z*Ejm4f!<*ua*2@t4Ul>OZgZu*vo(xx7*dv&lZ}gmtzh^NaQ*N&9`Hv_pez%JM0u2V zEeM->yY&$?azzx4Y- z@(2753+qP(x4ND7Qe_oOlUU+b5&X9~6=?t(o0`z3F>sfwNV_b!T?W!J zPJ`#rrv?0w1(?qOsT7zc1LsmMmzzJIs>?gN%yxJF-UR>mQXoST1I_!Tp;hOU9_c3b zB4i)45>{2Qi;eukUJ2ofX$L-tlKlr*^w_M7#m8^%q|Xe}*Drsq*bhw0;GVu>Cy*CR0A+hfP0p*oaydsQE zB-y~$5U@iU$_tvT4FCk%lgSjedr$(78OsQZhziBAlbE5n#Aqe}l$ zhOc-KYlY?VXWqqESoQA}<$DD`p=asqzaA0q@K+7?-_nt)U0oWXBepR&kiTNs(^|U8 z6IGvj9H~xxSL^h3_kY@o7{7no3f(#~ZEf6f)8KF{CqyDE(ja#Ju1NX(0!9X*8*@L6 z=pogK;Maxp9l&9vA=0bh&dD7sPDCUPIk%UY#^C9USZfY8KL2 zWp&DPnO{h~bD@LnQ`jx1g=0bE)yP`kN`Ka?84W}tI`L>DlyXJyxOu+)*2}}XJKvU& zrRiij5jlxVWXo&|trsrMII4#&HNq3UsV|9YMceNUgn1dZM2MzXw$6YA!u$~E!p8dj zIh)0N7^YR|mZdK<*X{^UCx$W7Tvkw@5Pa!nZ zJT=;5MrVt)>6P#QC;@MGjtoe8b3gKzkd_yp7FX-jhY@+X{@)ss-c_0ieD42)kQ^Y} zp4iipzcGK*y2e7W?z8j)BGmKG_v)uk^^7{$)i8!0nXp<7R?U1epX*)cl#ElfjH%aV z3}ZgU7F7ipe~fn4NWMKdGkL#L#W!8JT0A|=1yk0+cTw2FL!v*k(Wp#S=GdF~){Aed zht68pFGu;;Wz^;jC{^l-^9$ib@5*;5(jk9aR77M84eTw8iNHUmi$}lj6R?&3D`)kn zI#uEt!Hzn7pc;U@VVyNStG*~rD?B&73a3wo7GetSSOrsj3TIjiR z#&tgE=!n0(O`ohQ9Yxn|+HOh0TJt#$D!cMUbij>h-G&$4yN#sH4UB)EMUa`AlEGSE zvmQLu&rGUmGrGO^%f?3;4V6yWeOWhD20b&ss5z))QQ$cxiWtvG&cj@Dum#cKT2qPOM#)zBib z(>OBrHFlrUj>@x6mQB0eqqOkCPib^6K6dxgAGtq2nW-y)eWp*ra_?|_%@~<;rnv2L z++R}n>V@}YQ;>I!uXHCJ3OR}W&9ug(X$LQs3JVq!|LMm`P3afQaz9F%;(j(5*uY+% zsCXH7XmpLV)7$l$BYV_C!Np>4S5#%=aJPEKHjCYMY;+}aN6~qVVMO@VerZL;DO_yi z5aHHdtVK_8PZmcxamTA5!h2xG%>2_oABOxsi*C{A-5{}S0D98b7Twd z{k|i;kM)72E}6934nJ#n#^LIjhN`_07PVS|_oELItu4t?Gc>9NuvL=r4O1`^YO3nv zlPLnmXKRafl#9T%3r#XQ2ALCs8(Xi&?p?xQFdsjB60zFJu}L}Dw;9Gjmqj5NmPPI; z)U<8p|hx-F^*Mn3oq&+)#&W+xn#}$;$8scgAgE8G4vux5m7J@dJUa~-UUIbbd-*W0tPTtsUj*! zZ_>L;3zvRrk2`PX&71k=o1NXAJv(R4U%N9W&d@-U4ssI$005n~7V5!Ohy9z>lvmXo zkKg}yz-T?t2LQYP0E9&Wz&}?|*e?L^l>mT0XaGQF0RS5|ugOU9YC&nEtBC?G|BZsy z^0X@ijMaMN4FEJZ{!I{&lgEAqQu%1>t5dCmsjiE{G%_42TH zacEm`CTEKl)gVq&=Gu!h_LY5mxkI(!_ep0P(CUwTl^M?Bzi@sF=Re zdl`C}rV+?`OK<7z4nOcXV*e)?$7DQ+QszT2_sv6xoYhPsR<8l#vLOOh?O4Z zI!hrN%k4Yt#cOjtj^G`JH;^(0pJ7R9{6PL;qb^#H>}4rJ`s~%VQ{T5hHM*LfVU$8$ zwJfU_(Uv9BMb>MwlQCy?fxx9+nBu`Ot$GnilXkfz`h?ViE-$hRj8soxRh8_ zTB;v|Sz`&%Oxo)AYlDhPU0tn!uqsDncB$Y*#Ib(Ugk4wd5OiB_>b7-X0i^hGo~8HV zh$uLKF5uSNYv$=PyebJ=+Y7AHq>DL7R+tGlBR5Nu$=xVb$@%;5VQ6Cww38{_CW4K& zhED6C!~UUMl@n9Y-I@`=A350#e1v*ESsshT_S;LAzs0kNISf}v*D~G7BkO05Y}O4` zd!U{2w^9BxCXDy!{g~RQ&9Uzu{|te}LQF+#-FwHM)2^*8QP(`ZzrAHi7U^~<0T@f?0#$FxG zB9HDLirzJ{YswZ8voeLmj6QQo1$5?!ocp0yYQB}izMFw4zNKqq@LN6naTt98b6Dhk zhU_}B7xb#eEbS3*<+Fu2Z~yYEdGV{DI~G;Gl8)PajpWCeMHiX0vj`N@Gvc{aR8?Ko(c z!jRaeW@Bz4XT`;xb~+cJdq9fRVHC$L=q$2LGPE{n`mnrL)F#)YfKYBC`24JVrcJ~+ zAa3#GBVasD?kHITLs54-1!R(rX4W9QknCD-k0p`ot)q11P8AM{BGc^>G$eDX zqn5`s$h~;bAkU#9tMFkAY=RYIvynWF{N67{j81;Ww0VyB{IT377*Z}ocwe*%KcyK; zqO?cZ{V=l~gT+@=K0=qc6cJbM)HB{rnU0?87Jf5*rbC?x@21_Bn@wB9t-&gE=cV{QJEtiUAmbB_1m*9>eT`A3Pp3k=bN#g)*w zo<@afMAykz?uj*mIG=eoL`FCEjvy#8-py^44iKIOcW$NppD{Pzf_Q0Da5cn|+hLU$ zVXQPxnj(O1161Wmenv$#5ktLAf(}BX&h+30C+L;$x_%ds*bzqbSQC|13y;V9xWN^mO5#J!Eg?b3DuW6^%5SP1;y)1Q zD;=E6dwT*?rS9te_OME3`@yN4YCye$i`NSD%)FL14u$6mX=xOgntvkDJK^WGm-H#- z_4r6>BGr-O5LHYY@v*W;#Do%TCkJ-I{-vD$fGY^nsgT})yKXBEeWz5USV@w&5lm%s zgZY*7pcu#e61Ov(6702X#|AWcAa}v{dhfbEsp;bik?WQ*-+k4CJtp`DYlBGmj<*lt zcJFEgiQnL>hdi=OYi<>;=WwIvIwwK1bAgQ$5Z_39r~ zfFEoo!=WA{`UrQstCTh)!1=Dl?NZqf4S<RrR*Rj?6Z*LJc++HnptX!zh%g~DagC1{m0pHa_*Vjhf zH8JmM&2TX^S?Bn2xxTr&`6{}#R-~5)=3D_s@f5+}bG$~QlXY?eV~Wjs3(o$n_@Ma2b=QE9x8mx(rMDE9AEc-2OVFo zpR^8~sO)`W4vG1*wbe1xSlqtH{9WQzp^c=mI1`1Uy|+TQ3RV05Z(d29mvEi|;%UYH zwzFGez&(MZd@7)+Eq8sjw0$|ndbSDpOt?_f&``|Jm(n#(Yr4E|WoECT*9Qm4bu-}W zFEiqs%&P4te2~Dx4gkDW0S%8BO9uM!DnbR84iyZ+I0*RZL1xuv7{AoqmyqcVstule&%t(#8wCSdHHz?1CjE{YNapu%t zMp}f77+bbQVN#W%BU}?54?FU_^cC0afq--NKTRAoTkAtv_Wg z`K!68e;Nn|}FM#8M+mtLcX-09zY}M%1CG|r3QRvxan{?smdeyN`IHCK!i6Dy2K z^+wlQyjBA3eJIXy8oxVaN}=!Y@`IqK!3UU)00bH<_D-kl^Sg2JB}+4>qsU@1k!E^@ zG4W$KkxaaYx)&M>Iz*hzxDZyU++)n798Z`ak5?@`W;*ptj*|-hHc;eL-RsoDHWi}J zMacqVGlP0Hv&vEms0{f*+kOSh^s3MAR$_43)i{UYIGoE_&(M>5vrHyqZPAf|rW;T3 zQdtd+S`MbIgptYP(ROJi) zie2Xx38VK>u&ataRqn+LyZXo0crM3$0<9Q)1APP@9OuG$0GMi4Y_H3nVIi7b4WQl! z(y7@MK|Gcb>O+28wQu4drpX&$2zCDAC&|NnWr7F=_|P%fy7oJZCQ^2PxQ93|8BLwS zIr?mi$3S+IrSr7JkiEli{(bVav(Dm1a>}u!g%GzUpEZjtkDKfTy5TVM%T{BqKPe_* zmmz{d*78JW+R@5L0bQbtihv8B*4+p0Pe^#8#7Yh=#w`pzVelj&a!wkf!2?uaVBliS zULbUaZ4k$;(`cFBf_ z Date: Sat, 9 Sep 2023 22:35:50 +0000 Subject: [PATCH 0060/1110] chore(deps): update dependency activesupport to v7.0.8 --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index 49ef83b046..bc4a09b788 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ GEM remote: https://rubygems.org/ specs: - activesupport (7.0.7.2) + activesupport (7.0.8) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (>= 1.6, < 2) minitest (>= 5.1) From 44534cc9c7fca7c693536ea67e0557a3a38320df Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 16 Sep 2023 01:18:06 +0000 Subject: [PATCH 0061/1110] chore(deps): update ruby/setup-ruby action to v1.153.0 --- .github/workflows/build.yml | 2 +- .github/workflows/schedule-doc-report.yml | 2 +- .github/workflows/test.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0df45fde1f..2855aa7901 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: steps: - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0 - name: Set up Ruby - uses: ruby/setup-ruby@250fcd6a742febb1123a77a841497ccaa8b9e939 # v1.152.0 + uses: ruby/setup-ruby@5311f05890856149502132d25c4a24985a00d426 # v1.153.0 with: bundler-cache: true # runs 'bundle install' and caches installed gems automatically - name: Run tests diff --git a/.github/workflows/schedule-doc-report.yml b/.github/workflows/schedule-doc-report.yml index 4db7fe03fa..53e8d34569 100644 --- a/.github/workflows/schedule-doc-report.yml +++ b/.github/workflows/schedule-doc-report.yml @@ -10,7 +10,7 @@ jobs: steps: - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0 - name: Set up Ruby - uses: ruby/setup-ruby@250fcd6a742febb1123a77a841497ccaa8b9e939 # v1.152.0 + uses: ruby/setup-ruby@5311f05890856149502132d25c4a24985a00d426 # v1.153.0 with: bundler-cache: true # runs 'bundle install' and caches installed gems automatically - name: Generate report diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 492534a204..88c8d3da02 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ jobs: steps: - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0 - name: Set up Ruby - uses: ruby/setup-ruby@250fcd6a742febb1123a77a841497ccaa8b9e939 # v1.152.0 + uses: ruby/setup-ruby@5311f05890856149502132d25c4a24985a00d426 # v1.153.0 with: bundler-cache: true # runs 'bundle install' and caches installed gems automatically - name: Run tests From 73e440cff22667a7045e7fdd1c5399e78e19fc74 Mon Sep 17 00:00:00 2001 From: toiletbril Date: Sat, 16 Sep 2023 20:05:22 +0300 Subject: [PATCH 0062/1110] Add dedoc to related projects --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index a5a190fd4e..2df7a61267 100644 --- a/README.md +++ b/README.md @@ -175,6 +175,7 @@ Made something cool? Feel free to open a PR to add a new row to this table! You | [romainl/vim-devdocs](https://github.com/romainl/vim-devdocs) | Vim plugin | ![Latest GitHub commit](https://img.shields.io/github/last-commit/romainl/vim-devdocs?logo=github&label) | ![GitHub stars](https://img.shields.io/github/stars/romainl/vim-devdocs?logo=github&label) | | [waiting-for-dev/vim-www](https://github.com/waiting-for-dev/vim-www) | Vim plugin | ![Latest GitHub commit](https://img.shields.io/github/last-commit/waiting-for-dev/vim-www?logo=github&label) | ![GitHub stars](https://img.shields.io/github/stars/waiting-for-dev/vim-www?logo=github&label) | | [luckasRanarison/nvim-devdocs](https://github.com/luckasRanarison/nvim-devdocs) | Neovim plugin | ![Latest GitHub commit](https://img.shields.io/github/last-commit/luckasRanarison/nvim-devdocs?logo=github&label) | ![GitHub stars](https://img.shields.io/github/stars/luckasRanarison/nvim-devdocs?logo=github&label) | +| [toiletbril/dedoc](https://github.com/toiletbril/dedoc) | Terminal based viewer | ![Latest GitHub commit](https://img.shields.io/github/last-commit/toiletbril/dedoc?logo=github&label) | ![GitHub stars](https://img.shields.io/github/stars/toiletbril/dedoc?logo=github&label) | ## Copyright / License From 41b5e1bc618f88d5129438426bab5efe15fac695 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 23 Sep 2023 00:06:10 +0000 Subject: [PATCH 0063/1110] chore(deps): update ruby/setup-ruby action to v1.154.0 --- .github/workflows/build.yml | 2 +- .github/workflows/schedule-doc-report.yml | 2 +- .github/workflows/test.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2855aa7901..a54cd77cb5 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: steps: - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0 - name: Set up Ruby - uses: ruby/setup-ruby@5311f05890856149502132d25c4a24985a00d426 # v1.153.0 + uses: ruby/setup-ruby@52b8784594ec115fd17094752708121dc5dabb47 # v1.154.0 with: bundler-cache: true # runs 'bundle install' and caches installed gems automatically - name: Run tests diff --git a/.github/workflows/schedule-doc-report.yml b/.github/workflows/schedule-doc-report.yml index 53e8d34569..6366c5c294 100644 --- a/.github/workflows/schedule-doc-report.yml +++ b/.github/workflows/schedule-doc-report.yml @@ -10,7 +10,7 @@ jobs: steps: - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0 - name: Set up Ruby - uses: ruby/setup-ruby@5311f05890856149502132d25c4a24985a00d426 # v1.153.0 + uses: ruby/setup-ruby@52b8784594ec115fd17094752708121dc5dabb47 # v1.154.0 with: bundler-cache: true # runs 'bundle install' and caches installed gems automatically - name: Generate report diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 88c8d3da02..465c705c74 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ jobs: steps: - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0 - name: Set up Ruby - uses: ruby/setup-ruby@5311f05890856149502132d25c4a24985a00d426 # v1.153.0 + uses: ruby/setup-ruby@52b8784594ec115fd17094752708121dc5dabb47 # v1.154.0 with: bundler-cache: true # runs 'bundle install' and caches installed gems automatically - name: Run tests From df886bc88e5601ec51f098ef3d2efa859e9bf424 Mon Sep 17 00:00:00 2001 From: Adrien nayrat Date: Wed, 27 Sep 2023 16:03:46 +0200 Subject: [PATCH 0064/1110] Update postgresql.rb Add Postgres 16 and current. Update other version and mention EOL ones. --- lib/docs/scrapers/postgresql.rb | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/lib/docs/scrapers/postgresql.rb b/lib/docs/scrapers/postgresql.rb index 194685d2ee..332a6c2903 100644 --- a/lib/docs/scrapers/postgresql.rb +++ b/lib/docs/scrapers/postgresql.rb @@ -55,52 +55,62 @@ class Postgresql < UrlScraper Licensed under the PostgreSQL License. HTML + version 'Latest' do + self.release = '16.0' + self.base_url = "https://www.postgresql.org/docs/latest/" + end + + version '16' do + self.release = '16.0' + self.base_url = "https://www.postgresql.org/docs/#{version}/" + end + version '15' do - self.release = '15.3' + self.release = '15.4' self.base_url = "https://www.postgresql.org/docs/#{version}/" end version '14' do - self.release = '14.5' + self.release = '14.9' self.base_url = "https://www.postgresql.org/docs/#{version}/" end version '13' do - self.release = '13.4' + self.release = '13.12' self.base_url = "https://www.postgresql.org/docs/#{version}/" end version '12' do - self.release = '12.1' + self.release = '12.16' self.base_url = "https://www.postgresql.org/docs/#{version}/" end version '11' do - self.release = '11.6' + self.release = '11.21' self.base_url = "https://www.postgresql.org/docs/#{version}/" end version '10' do - self.release = '10.11' + self.release = '10.23 (EOL)' self.base_url = "https://www.postgresql.org/docs/#{version}/" end version '9.6' do - self.release = '9.6.16' + self.release = '9.6.24 (EOL)' self.base_url = "https://www.postgresql.org/docs/#{version}/" html_filters.insert_before 'postgresql/extract_metadata', 'postgresql/normalize_class_names' end version '9.5' do - self.release = '9.5.20' + self.release = '9.5.25 (EOL)' self.base_url = "https://www.postgresql.org/docs/#{version}/" html_filters.insert_before 'postgresql/extract_metadata', 'postgresql/normalize_class_names' end version '9.4' do - self.release = '9.4.25' + self.release = '9.4.26 (EOL)' self.base_url = "https://www.postgresql.org/docs/#{version}/" html_filters.insert_before 'postgresql/extract_metadata', 'postgresql/normalize_class_names' From b4d1f8c5bd0182123956ac7137cbf8da9745502a Mon Sep 17 00:00:00 2001 From: Oliver Eyton-Williams Date: Mon, 2 Oct 2023 10:54:16 +0200 Subject: [PATCH 0065/1110] fix: convert hash to keyword args --- lib/tasks/updates.thor | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/updates.thor b/lib/tasks/updates.thor index 5dd1539749..b3fb28bb9e 100644 --- a/lib/tasks/updates.thor +++ b/lib/tasks/updates.thor @@ -174,7 +174,7 @@ class UpdatesCLI < Thor order: 'desc' } - matching_issues = github_get('/search/issues', search_params) + matching_issues = github_get('/search/issues', **search_params) previous_issue = matching_issues['items'].find {|item| item['number'] != created_issue['number']} if previous_issue.nil? From 054911231d5883b2359bf71a05912e6910e3b2be Mon Sep 17 00:00:00 2001 From: Tim Lim Date: Tue, 3 Oct 2023 19:06:22 +0800 Subject: [PATCH 0066/1110] Update Readme to Ruby 3.2.2 --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a5a190fd4e..6d3f0057fb 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,8 @@ Unless you wish to contribute to the project, we recommend using the hosted vers DevDocs is made of two pieces: a Ruby scraper that generates the documentation and metadata, and a JavaScript app powered by a small Sinatra app. -DevDocs requires Ruby 3.2.1, libcurl, and a JavaScript runtime supported by [ExecJS](https://github.com/rails/execjs#readme) (included in OS X and Windows; [Node.js](https://nodejs.org/en/) on Linux). Once you have these installed, run the following commands: + +DevDocs requires Ruby 3.2.2 (defined in [`Gemfile`](./Gemfile)), libcurl, and a JavaScript runtime supported by [ExecJS](https://github.com/rails/execjs#readme) (included in OS X and Windows; [Node.js](https://nodejs.org/en/) on Linux). Once you have these installed, run the following commands: ```sh git clone https://github.com/freeCodeCamp/devdocs.git && cd devdocs From 8107165945cd0f292223df53677e4a8c7048f9ea Mon Sep 17 00:00:00 2001 From: Tim Lim Date: Tue, 3 Oct 2023 19:59:39 +0800 Subject: [PATCH 0067/1110] Fix Node.js entries filter to capture more methods --- lib/docs/filters/node/entries.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/docs/filters/node/entries.rb b/lib/docs/filters/node/entries.rb index 87fc8765f4..41aee52d18 100644 --- a/lib/docs/filters/node/entries.rb +++ b/lib/docs/filters/node/entries.rb @@ -13,7 +13,7 @@ def get_type def additional_entries entries = [] - css('h3 > code, h4 > code, h5 > code').each do |node| + css('h3 > code, h4 > code, h5 > code, h6 > code').each do |node| name = node.content.gsub(/\(.*\)/, '()') id = node.parent['id'] From 723c8938e6a1136775ccc6281daf539d9ffad443 Mon Sep 17 00:00:00 2001 From: Juan Vasquez Date: Thu, 5 Oct 2023 14:02:01 -0600 Subject: [PATCH 0068/1110] Fix ruby version in Readme --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a5a190fd4e..347db0a8b5 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ Unless you wish to contribute to the project, we recommend using the hosted vers DevDocs is made of two pieces: a Ruby scraper that generates the documentation and metadata, and a JavaScript app powered by a small Sinatra app. -DevDocs requires Ruby 3.2.1, libcurl, and a JavaScript runtime supported by [ExecJS](https://github.com/rails/execjs#readme) (included in OS X and Windows; [Node.js](https://nodejs.org/en/) on Linux). Once you have these installed, run the following commands: +DevDocs requires Ruby 3.2.2, libcurl, and a JavaScript runtime supported by [ExecJS](https://github.com/rails/execjs#readme) (included in OS X and Windows; [Node.js](https://nodejs.org/en/) on Linux). Once you have these installed, run the following commands: ```sh git clone https://github.com/freeCodeCamp/devdocs.git && cd devdocs @@ -53,12 +53,12 @@ docker run --name devdocs -d -p 9292:9292 thibaut/devdocs DevDocs aims to make reading and searching reference documentation fast, easy and enjoyable. -The app's main goals are to: +The app's main goals are to: * Keep load times as short as possible * Improve the quality, speed, and order of search results * Maximize the use of caching and other performance optimizations -* Maintain a clean and readable user interface +* Maintain a clean and readable user interface * Be fully functional offline * Support full keyboard navigation * Reduce “context switch” by using a consistent typography and design across all documentations @@ -126,7 +126,7 @@ thor docs:clean # Delete documentation packages thor console # Start a REPL thor console:docs # Start a REPL in the "Docs" module -# Tests can be run quickly from within the console using the "test" command. +# Tests can be run quickly from within the console using the "test" command. # Run "help test" for usage instructions. thor test:all # Run all tests thor test:docs # Run "Docs" tests From 8e5c9bbc5604be55f19df98208db4c439b8dd53a Mon Sep 17 00:00:00 2001 From: Terry Cai Date: Fri, 6 Oct 2023 18:12:43 +0800 Subject: [PATCH 0069/1110] feat: varnish chedckout --- lib/docs/filters/varnish/entries.rb | 60 ++++++++++++++++++++++++++++ lib/docs/scrapers/varnish.rb | 40 +++++++++++++++++++ public/icons/docs/varnish/16.png | Bin 0 -> 1428 bytes public/icons/docs/varnish/16@2x.png | Bin 0 -> 859 bytes public/icons/docs/varnish/SOURCE | 1 + 5 files changed, 101 insertions(+) create mode 100755 lib/docs/filters/varnish/entries.rb create mode 100644 lib/docs/scrapers/varnish.rb create mode 100644 public/icons/docs/varnish/16.png create mode 100644 public/icons/docs/varnish/16@2x.png create mode 100644 public/icons/docs/varnish/SOURCE diff --git a/lib/docs/filters/varnish/entries.rb b/lib/docs/filters/varnish/entries.rb new file mode 100755 index 0000000000..34c2be899c --- /dev/null +++ b/lib/docs/filters/varnish/entries.rb @@ -0,0 +1,60 @@ +module Docs + class Varnish + class EntriesFilter < Docs::EntriesFilter + TYPE_BY_SLUG = {} + + def call + if root_page? + css('.section').each do |node| + type = node.at_css('h2').content[0..-2] + node.css('li > a').each do |n| + s = n['href'].split('/')[-2] + TYPE_BY_SLUG[s] = type + end + end + end + super + end + + def get_name + at_css('h1').content[0..-2] + end + + def get_type + case slug + when /installation/ + 'Installation' + when /users-guide/ + 'Users Guide' + when /tutorial/ + 'Tutorial' + when /reference/ + 'Reference Manual' + when /dev-guide/ + 'Dev Guide' + else + TYPE_BY_SLUG[slug.split('/').first] || 'Other' + end + end + + def include_default_entry? + slug != 'reference/' + end + + def additional_entries + entries = [] + + css('dl.class > dt[id]').each do |node| + name = node['id'].split('.').last + id = node['id'] + type = node['id'].split('.')[0..-2].join('.') + entries << [name, id, type] + end + + + entries + end + + end + end +end diff --git a/lib/docs/scrapers/varnish.rb b/lib/docs/scrapers/varnish.rb new file mode 100644 index 0000000000..13564955a0 --- /dev/null +++ b/lib/docs/scrapers/varnish.rb @@ -0,0 +1,40 @@ +module Docs + class Varnish < UrlScraper + self.name = 'Varnish' + self.type = 'sphinx' + + self.root_path = 'index.html' + self.links = { + home: 'https://varnish-cache.org/', + code: 'https://github.com/varnishcache/varnish-cache' + } + + + html_filters.push 'varnish/entries', 'sphinx/clean_html' + + options[:container] = '.body > section' + + + options[:skip] = %w(genindex.html search.html) + + options[:skip_patterns] = [/phk/, /glossary/, /whats-new/] + + + options[:attribution] = <<-HTML + Copyright © 2006 Verdens Gang AS
+ Copyright © 2006–2020 Varnish Software AS
+ Licensed under the BSD-2-Clause License. + HTML + + version do + self.release = '7.3.0' + self.base_url = 'https://varnish-cache.org/docs/7.3/' + end + + def get_latest_version(opts) + contents = get_github_file_contents('varnishcache', 'varnish-cache', 'doc/changes.rst', opts) + contents.scan(/Varnish\s+Cache\s+([0-9.]+)/)[0][0] + end + + end +end diff --git a/public/icons/docs/varnish/16.png b/public/icons/docs/varnish/16.png new file mode 100644 index 0000000000000000000000000000000000000000..49c7e76e6dae555af006cc10dc501ac02e73ac8c GIT binary patch literal 1428 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+m{l`FB1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxOgGuk#6rQ`QqR!L z#K720N5ROz&{E&PLf_C>*TB%qz|6|jTmcG{fVLH-q*(>IxIyg#@@$ndN=gc>^!3Zj z%k|2Q_413-^$jg8fo2%#8yV>WRp=I1=9MH?=;jqG!%T2VElw`VEGWs$&r<-In3$Ab zT4JjNbScCOxdm`z^NOLt1Pn0!io^naLp=kKmtYEgeeo;J&4sHjE(uCSxEHIz#UYgi zsro^w#rdU0$-sz9QwCX8VC7ttnpl!w6q28x0}I7~jQo=P;*9(P1tXx76f}GjlQZ)` zBAQ?=zP?tTdBr7(dC94sF1AWQL-aB;Q>+XPObty<-7HNU&D{(QU7akPj7*$d&5Yb! zoGcAZ-5go(3=B-Ho-U3d z6}NgO#d-uMN*u4ZzI~(pWzm^qU5i-ec5%7}#g=Mq5{zA=qGIHvv_e5?q0_F9YU^LQ zbtx&fws2{^c%3AE_st@nlWi%JT7+7JRzA#W-#qv2oO`nDR=1AHRp{M&Ui;tv-t+%9 z5^atqQcDwV$XxjPN#MHLu^0bxI_4~Te%mwa_Jr?mR8~*b5HnMkdC46ZZ0=#TlbI*b zrNG~D4f4z%186JN_O1(dS5v| zy2{LxDEqF{cgoXnxAdM}MKe3E@3Pbpp73L?z$5jFzv4&U?B8G@Z2bCi;M|4Zs(0*l zKC~lC=bFW-L-Eh{I|rQeRo(6JUP&NhYtgSv?t_QrPH{v|^L=}Y|IMKbJ1_F|TOH%v z7Wtw=LddN$^4HHskM}*9MH_2RTj%nJ3%-+ASt=9H_!OqjdN`6h1KQ s{8`5sd2Xilu^*rF>S}$(nEwbTFt}e^@BjNW8@QnKboFyt=akR{03|{jssI20 literal 0 HcmV?d00001 diff --git a/public/icons/docs/varnish/16@2x.png b/public/icons/docs/varnish/16@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..442b11dcb35eeb9f5bbafe04f0f94da528a7567e GIT binary patch literal 859 zcmV-h1El&+6otR@&)>9GQf#eh6%?eRU~R^UrEyiQR9p#Nh(%XI78RsoGC?~TF8N*#RQM?m+3UkCOAYwMg!fJx9H z#3;kL`SuVPm;}xDU>^Z;4Omga1OYdl7Nwt7Hc3n4?3Tcz#MSqzX{ltRVY-}5{t}5;QMs(^$Ut7uiuFB6C1!! zV($d5B7KQ;iemPf?D^6j98Lh6n~1&uyaalik(}!*Fi={L!(klW10Mh%#NinBQw)q( zk@Yu7A2v}7Mj9iEk5&g?PC$MT{7g+>--N^SD0A($!?}5g-bB#{i+Q~s63|KPeu~`} zYBy^LMB9K3b)4LW!&142vwtJKjPxwhcfdb2HuIn<(5pmIA0s`#)_<2vg_>Tx(fXR= zxrCybW!A;)EH9ouN(f^p>?DLc!CQepk%_j1DRRA3L-Ynm8zZsA__k%=Q2P002ovPDHLkV1g8zi9`SZ literal 0 HcmV?d00001 diff --git a/public/icons/docs/varnish/SOURCE b/public/icons/docs/varnish/SOURCE new file mode 100644 index 0000000000..ebc3b9fc4c --- /dev/null +++ b/public/icons/docs/varnish/SOURCE @@ -0,0 +1 @@ +https://www.varnish-software.com/branding/ From 91ffefb7e863efab247a75d8e0086d6955e9e84f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 8 Oct 2023 13:36:31 +0000 Subject: [PATCH 0070/1110] chore(deps): update ruby/setup-ruby action to v1.155.0 --- .github/workflows/build.yml | 2 +- .github/workflows/schedule-doc-report.yml | 2 +- .github/workflows/test.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a54cd77cb5..e516c8acf2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: steps: - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0 - name: Set up Ruby - uses: ruby/setup-ruby@52b8784594ec115fd17094752708121dc5dabb47 # v1.154.0 + uses: ruby/setup-ruby@d37167af451eb51448db3354e1057b75c4b268f7 # v1.155.0 with: bundler-cache: true # runs 'bundle install' and caches installed gems automatically - name: Run tests diff --git a/.github/workflows/schedule-doc-report.yml b/.github/workflows/schedule-doc-report.yml index 6366c5c294..f792f4dd1b 100644 --- a/.github/workflows/schedule-doc-report.yml +++ b/.github/workflows/schedule-doc-report.yml @@ -10,7 +10,7 @@ jobs: steps: - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0 - name: Set up Ruby - uses: ruby/setup-ruby@52b8784594ec115fd17094752708121dc5dabb47 # v1.154.0 + uses: ruby/setup-ruby@d37167af451eb51448db3354e1057b75c4b268f7 # v1.155.0 with: bundler-cache: true # runs 'bundle install' and caches installed gems automatically - name: Generate report diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 465c705c74..039d03ec1c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ jobs: steps: - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0 - name: Set up Ruby - uses: ruby/setup-ruby@52b8784594ec115fd17094752708121dc5dabb47 # v1.154.0 + uses: ruby/setup-ruby@d37167af451eb51448db3354e1057b75c4b268f7 # v1.155.0 with: bundler-cache: true # runs 'bundle install' and caches installed gems automatically - name: Run tests From 41d44094dccff623da75870f7483a8f9aa71f29c Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Mon, 9 Oct 2023 18:46:47 +0200 Subject: [PATCH 0071/1110] Update PostgreSQL documentation (16.0) --- lib/docs/scrapers/postgresql.rb | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/lib/docs/scrapers/postgresql.rb b/lib/docs/scrapers/postgresql.rb index 332a6c2903..2645748316 100644 --- a/lib/docs/scrapers/postgresql.rb +++ b/lib/docs/scrapers/postgresql.rb @@ -55,11 +55,6 @@ class Postgresql < UrlScraper Licensed under the PostgreSQL License. HTML - version 'Latest' do - self.release = '16.0' - self.base_url = "https://www.postgresql.org/docs/latest/" - end - version '16' do self.release = '16.0' self.base_url = "https://www.postgresql.org/docs/#{version}/" @@ -71,46 +66,46 @@ class Postgresql < UrlScraper end version '14' do - self.release = '14.9' + self.release = '14.5' self.base_url = "https://www.postgresql.org/docs/#{version}/" end version '13' do - self.release = '13.12' + self.release = '13.4' self.base_url = "https://www.postgresql.org/docs/#{version}/" end version '12' do - self.release = '12.16' + self.release = '12.1' self.base_url = "https://www.postgresql.org/docs/#{version}/" end version '11' do - self.release = '11.21' + self.release = '11.6' self.base_url = "https://www.postgresql.org/docs/#{version}/" end version '10' do - self.release = '10.23 (EOL)' + self.release = '10.11' self.base_url = "https://www.postgresql.org/docs/#{version}/" end version '9.6' do - self.release = '9.6.24 (EOL)' + self.release = '9.6.16' self.base_url = "https://www.postgresql.org/docs/#{version}/" html_filters.insert_before 'postgresql/extract_metadata', 'postgresql/normalize_class_names' end version '9.5' do - self.release = '9.5.25 (EOL)' + self.release = '9.5.20' self.base_url = "https://www.postgresql.org/docs/#{version}/" html_filters.insert_before 'postgresql/extract_metadata', 'postgresql/normalize_class_names' end version '9.4' do - self.release = '9.4.26 (EOL)' + self.release = '9.4.25' self.base_url = "https://www.postgresql.org/docs/#{version}/" html_filters.insert_before 'postgresql/extract_metadata', 'postgresql/normalize_class_names' From b03af5075577fec1f0de82233fa6f6824dca702c Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Mon, 9 Oct 2023 20:11:45 +0200 Subject: [PATCH 0072/1110] hapi: update attribution --- lib/docs/scrapers/hapi.rb | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/lib/docs/scrapers/hapi.rb b/lib/docs/scrapers/hapi.rb index 8952d78f16..17b08c2e1c 100644 --- a/lib/docs/scrapers/hapi.rb +++ b/lib/docs/scrapers/hapi.rb @@ -16,15 +16,9 @@ class Hapi < UrlScraper options[:container] = '.markdown-wrapper' options[:title] = "Hapi" options[:attribution] = <<-HTML - Copyright © 2011-2022, Project contributors Copyright © 2011-2020, Sideway Inc Copyright © 2011-2014, Walmart - Copyright © 2011, Yahoo Inc. - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - The names of any contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + Copyright © 2011-2022, Project contributors Copyright © 2011-2020, Sideway Inc Copyright © 2011-2014, Walmart
+ Copyright © 2011, Yahoo Inc.
+ Licensed under the BSD 3-clause License. HTML def get_latest_version(opts) From 2dce213f6acf726a150af06919a2c09578e2441a Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Mon, 9 Oct 2023 20:25:17 +0200 Subject: [PATCH 0073/1110] Update Nushell documentation (0.85.0) --- lib/docs/filters/nushell/clean_html.rb | 1 + lib/docs/scrapers/nushell.rb | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/docs/filters/nushell/clean_html.rb b/lib/docs/filters/nushell/clean_html.rb index b9453c6af4..d7e866954d 100644 --- a/lib/docs/filters/nushell/clean_html.rb +++ b/lib/docs/filters/nushell/clean_html.rb @@ -3,6 +3,7 @@ module Docs class Nushell class CleanHtmlFilter < Filter def call + @doc = at_css('.theme-default-content > div:only-child', '.theme-default-content') css('footer').remove css('h1 a, h2 a').remove doc diff --git a/lib/docs/scrapers/nushell.rb b/lib/docs/scrapers/nushell.rb index ff078c2394..c3e11e2075 100644 --- a/lib/docs/scrapers/nushell.rb +++ b/lib/docs/scrapers/nushell.rb @@ -6,7 +6,7 @@ class Nushell < UrlScraper self.name = "Nushell" self.slug = "nushell" self.type = "nushell" - self.release = "0.84.0" + self.release = "0.85.0" self.links = { home: "https://www.nushell.sh/", code: "https://github.com/nushell/nushell", @@ -18,13 +18,12 @@ class Nushell < UrlScraper options[:follow_links] = true options[:title] = "Nushell" options[:attribution] = <<-HTML - Copyright © 2019 - 2023 The Nushell Project Developers + Copyright © 2019–2023 The Nushell Project Developers Licensed under the MIT License. HTML # latest version has a special URL that does not include the version identifier version do - self.release = "0.84.0" self.base_urls = [ "https://www.nushell.sh/book/", "https://www.nushell.sh/commands/" From 474c7b495a78ce1c65c48749dd9a3e37a34f15bc Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Mon, 9 Oct 2023 20:30:32 +0200 Subject: [PATCH 0074/1110] Update Joi documentation (17.11.0) --- lib/docs/scrapers/joi.rb | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/lib/docs/scrapers/joi.rb b/lib/docs/scrapers/joi.rb index a6107cf034..4440e29633 100644 --- a/lib/docs/scrapers/joi.rb +++ b/lib/docs/scrapers/joi.rb @@ -4,7 +4,7 @@ class Joi < UrlScraper self.name = "Joi" self.slug = "joi" self.type = "joi" - self.release = "17.9.1" + self.release = "17.11.0" self.base_url = "https://joi.dev/api/?v=#{self.release}" self.links = { home: "https://joi.dev/", @@ -16,14 +16,8 @@ class Joi < UrlScraper options[:container] = '.markdown-wrapper' options[:title] = "Joi" options[:attribution] = <<-HTML - Copyright © 2012-2022, Project contributors Copyright © 2012-2022, Sideway Inc Copyright © 2012-2014, Walmart - All rights reserved. - - Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - - Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - The names of any contributors may not be used to endorse or promote products derived from this software without specific prior written permission. + Copyright © 2012-2022, Project contributors Copyright © 2012-2022, Sideway Inc Copyright © 2012-2014, Walmart
+ Licensed under the BSD 3-clause License. HTML def get_latest_version(opts) From 19445c34fe1d88d0f4fb3aa6be9255ff4645d738 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Mon, 9 Oct 2023 20:37:45 +0200 Subject: [PATCH 0075/1110] Update Varnish documentation (7.4) --- lib/docs/scrapers/varnish.rb | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/lib/docs/scrapers/varnish.rb b/lib/docs/scrapers/varnish.rb index 13564955a0..24e9cf7c37 100644 --- a/lib/docs/scrapers/varnish.rb +++ b/lib/docs/scrapers/varnish.rb @@ -9,17 +9,12 @@ class Varnish < UrlScraper code: 'https://github.com/varnishcache/varnish-cache' } - html_filters.push 'varnish/entries', 'sphinx/clean_html' options[:container] = '.body > section' - - options[:skip] = %w(genindex.html search.html) - options[:skip_patterns] = [/phk/, /glossary/, /whats-new/] - options[:attribution] = <<-HTML Copyright © 2006 Verdens Gang AS
Copyright © 2006–2020 Varnish Software AS
@@ -27,8 +22,8 @@ class Varnish < UrlScraper HTML version do - self.release = '7.3.0' - self.base_url = 'https://varnish-cache.org/docs/7.3/' + self.release = '7.4' + self.base_url = "https://varnish-cache.org/docs/#{release}/" end def get_latest_version(opts) From 136a2f168203776b4c87744d256cfcfc1e288e7f Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Mon, 9 Oct 2023 20:44:40 +0200 Subject: [PATCH 0076/1110] Update Node.js documentation (20.8.0) --- lib/docs/scrapers/node.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/docs/scrapers/node.rb b/lib/docs/scrapers/node.rb index d8623fc368..60a89677e2 100644 --- a/lib/docs/scrapers/node.rb +++ b/lib/docs/scrapers/node.rb @@ -24,12 +24,12 @@ class Node < UrlScraper HTML version do - self.release = '19.0.1' + self.release = '20.8.0' self.base_url = 'https://nodejs.org/api/' end version '18 LTS' do - self.release = '18.12.1' + self.release = '18.18.0' self.base_url = 'https://nodejs.org/dist/latest-v18.x/docs/api/' end From c797dca21b8daaa1e60ac0ab03d91ab4fe568438 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Mon, 9 Oct 2023 20:56:27 +0200 Subject: [PATCH 0077/1110] Update Astro documentation (3.2.0) --- lib/docs/filters/astro/entries.rb | 6 ++++-- lib/docs/scrapers/astro.rb | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/docs/filters/astro/entries.rb b/lib/docs/filters/astro/entries.rb index fc03d5145d..837ee26876 100644 --- a/lib/docs/filters/astro/entries.rb +++ b/lib/docs/filters/astro/entries.rb @@ -8,14 +8,16 @@ def get_name end def get_type + return 'Guides' if slug.start_with?('contribute/') + return 'Guides' if slug.start_with?('guides/') aside = at_css('aside') a = aside.at_css('a[aria-current="page"]', 'a[data-current-parent="true"]') a.ancestors('details').at_css('summary').content end def additional_entries - return if slug.start_with?('guides/deploy') - return if slug.start_with?('guides/integrations-guide') + return [] if slug.start_with?('guides/deploy') + return [] if slug.start_with?('guides/integrations-guide') at_css('article').css('h2[id], h3[id]').each_with_object [] do |node, entries| type = node.content.strip type.sub! %r{\s*#\s*}, '' diff --git a/lib/docs/scrapers/astro.rb b/lib/docs/scrapers/astro.rb index 765260d9de..7413c3b4ff 100644 --- a/lib/docs/scrapers/astro.rb +++ b/lib/docs/scrapers/astro.rb @@ -16,7 +16,7 @@ class Astro < UrlScraper options[:skip_patterns] = [/tutorial/] - self.release = '2.9.7' + self.release = '3.2.0' self.base_url = 'https://docs.astro.build/en/' self.initial_paths = %w(getting-started/) From 6a5a8a558cee9d8b29ed0344ad7069f08acdfeec Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Mon, 9 Oct 2023 21:03:54 +0200 Subject: [PATCH 0078/1110] Update Rust documentation (1.73.0) --- lib/docs/scrapers/rust.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/docs/scrapers/rust.rb b/lib/docs/scrapers/rust.rb index 90bdf77ced..5c2b1f10c9 100644 --- a/lib/docs/scrapers/rust.rb +++ b/lib/docs/scrapers/rust.rb @@ -3,7 +3,7 @@ module Docs class Rust < UrlScraper self.type = 'rust' - self.release = '1.71.0' + self.release = '1.73.0' self.base_url = 'https://doc.rust-lang.org/' self.root_path = 'book/index.html' self.initial_paths = %w( From 9290b58395cdb078652702ec568d8dfefc4331d4 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Mon, 9 Oct 2023 21:11:34 +0200 Subject: [PATCH 0079/1110] Update Python documentation (3.12.0) --- lib/docs/scrapers/python.rb | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/docs/scrapers/python.rb b/lib/docs/scrapers/python.rb index 360e2bb66f..39448bd5da 100644 --- a/lib/docs/scrapers/python.rb +++ b/lib/docs/scrapers/python.rb @@ -21,15 +21,22 @@ class Python < FileScraper Licensed under the PSF License. HTML + version '3.12' do + self.release = '3.12.0' + self.base_url = "https://docs.python.org/#{self.version}/" + + html_filters.push 'python/entries_v3', 'sphinx/clean_html', 'python/clean_html' + end + version '3.11' do - self.release = '3.11.1' + self.release = '3.11.5' self.base_url = "https://docs.python.org/#{self.version}/" html_filters.push 'python/entries_v3', 'sphinx/clean_html', 'python/clean_html' end version '3.10' do - self.release = '3.10.9' + self.release = '3.10.13' self.base_url = "https://docs.python.org/#{self.version}/" html_filters.push 'python/entries_v3', 'sphinx/clean_html', 'python/clean_html' From e90bc60b4c78114ed528cbeda93f181f00e5788e Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Mon, 9 Oct 2023 21:24:34 +0200 Subject: [PATCH 0080/1110] Update jq documentation (1.7) --- lib/docs/filters/jq/clean_html.rb | 5 +---- lib/docs/filters/jq/entries.rb | 2 +- lib/docs/scrapers/jq.rb | 7 ++++--- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/docs/filters/jq/clean_html.rb b/lib/docs/filters/jq/clean_html.rb index 036ee866d2..9217b0a286 100644 --- a/lib/docs/filters/jq/clean_html.rb +++ b/lib/docs/filters/jq/clean_html.rb @@ -2,8 +2,6 @@ module Docs class Jq class CleanHtmlFilter < Filter def call - content = at_css('div#manualcontent') - css('.manual-example').each do |node| container = node.parent example_header = doc.document.create_element('h4') @@ -13,8 +11,7 @@ def call node.remove_class('collapse') container.replace(node) end - - content + doc end end end diff --git a/lib/docs/filters/jq/entries.rb b/lib/docs/filters/jq/entries.rb index 0f6e1ceb83..79d87b0912 100644 --- a/lib/docs/filters/jq/entries.rb +++ b/lib/docs/filters/jq/entries.rb @@ -7,7 +7,7 @@ def include_default_entry? def additional_entries entries = [] - css('#manualcontent > section').each do |node| + css('> section').each do |node| type = node.at_css('h2').content node.css('> section').each do |n| entries << [n.at_css('h3').content, n['id'], type] diff --git a/lib/docs/scrapers/jq.rb b/lib/docs/scrapers/jq.rb index 5125b86350..38e29ffecd 100644 --- a/lib/docs/scrapers/jq.rb +++ b/lib/docs/scrapers/jq.rb @@ -3,15 +3,16 @@ class Jq < UrlScraper self.name = 'jq' self.slug = 'jq' self.type = 'jq' - self.release = '1.6' + self.release = '1.7' self.links = { - home: 'https://stedolan.github.io/jq/' + home: 'https://jqlang.github.io/jq/' } - self.base_url = "https://stedolan.github.io/jq/manual/v#{self.release}/index.html" + self.base_url = "https://jqlang.github.io/jq/manual/v#{self.release}/index.html" html_filters.push 'jq/entries', 'jq/clean_html' + options[:container] = 'main' options[:skip_links] = true options[:attribution] = <<-HTML From e4d03a32bca235f2966cee44f88bca22d024e853 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Mon, 9 Oct 2023 21:26:25 +0200 Subject: [PATCH 0081/1110] Update Flask documentation (3.0.x) --- lib/docs/scrapers/flask.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/docs/scrapers/flask.rb b/lib/docs/scrapers/flask.rb index 9d7234a546..08655e7a95 100755 --- a/lib/docs/scrapers/flask.rb +++ b/lib/docs/scrapers/flask.rb @@ -14,10 +14,15 @@ class Flask < UrlScraper options[:skip_patterns] = [/\Atutorial\//] options[:attribution] = <<-HTML - © 2007–2022 Pallets
+ © 2010 Pallets
Licensed under the BSD 3-clause License. HTML + version '3.0' do + self.release = '3.0.x' + self.base_url = "https://flask.palletsprojects.com/en/#{self.release}/" + end + version '2.3' do self.release = '2.3.x' self.base_url = "https://flask.palletsprojects.com/en/#{self.release}/" From 0166105a4518aa7978d027b4098b6dc08f3d856e Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Mon, 9 Oct 2023 21:28:46 +0200 Subject: [PATCH 0082/1110] Update Werkzeug documentation (3.0.x) --- lib/docs/scrapers/werkzeug.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/docs/scrapers/werkzeug.rb b/lib/docs/scrapers/werkzeug.rb index 190d96c838..703797448d 100755 --- a/lib/docs/scrapers/werkzeug.rb +++ b/lib/docs/scrapers/werkzeug.rb @@ -13,10 +13,15 @@ class Werkzeug < UrlScraper options[:skip] = %w(changes/) options[:attribution] = <<-HTML - © 2007–2022 Pallets
+ © 2007 Pallets
Licensed under the BSD 3-clause License. HTML + version '3.0' do + self.release = '3.0.x' + self.base_url = "https://werkzeug.palletsprojects.com/en/#{self.release}/" + end + version '2.3' do self.release = '2.3.x' self.base_url = "https://werkzeug.palletsprojects.com/en/#{self.release}/" From 0ac54123585e40bab507cdf213bd36c214677702 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Mon, 9 Oct 2023 21:55:03 +0200 Subject: [PATCH 0083/1110] Update Svelte documentation (4.2.1) --- lib/docs/filters/svelte/clean_html.rb | 8 ++++++-- lib/docs/filters/svelte/entries.rb | 12 ++++++++---- lib/docs/scrapers/svelte.rb | 15 ++++++++++++--- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/lib/docs/filters/svelte/clean_html.rb b/lib/docs/filters/svelte/clean_html.rb index 579b27b7f6..693825f78a 100644 --- a/lib/docs/filters/svelte/clean_html.rb +++ b/lib/docs/filters/svelte/clean_html.rb @@ -2,8 +2,12 @@ module Docs class Svelte class CleanHtmlFilter < Filter def call - @doc = at_css('main > .content') - at_css('h1').content = 'Svelte' + @doc = at_css('main .page.content') + at_css('h1').content = 'Svelte' if root_page? + css('pre').each do |node| + node.content = node.css('.line').map(&:content).join("\n") + node['data-language'] = 'javascript' + end doc end end diff --git a/lib/docs/filters/svelte/entries.rb b/lib/docs/filters/svelte/entries.rb index ab7236ff93..dcd66cc2c2 100644 --- a/lib/docs/filters/svelte/entries.rb +++ b/lib/docs/filters/svelte/entries.rb @@ -2,15 +2,18 @@ module Docs class Svelte class EntriesFilter < Docs::EntriesFilter def get_type - 'Svelte' + at_css('ul.sidebar > li:has(.active) > span.section').content end def additional_entries - type = 'Svelte' subtype = nil + css('aside').remove + css('.category').remove + css('.controls').remove + css('.edit').remove + css('.permalink').remove css('h2, h3, h4').each_with_object [] do |node, entries| if node.name == 'h2' - type = node.content.strip subtype = nil elsif node.name == 'h3' subtype = node.content.strip @@ -19,7 +22,8 @@ def additional_entries next if type == 'Before we begin' name = node.content.strip name.concat " (#{subtype})" if subtype && node.name == 'h4' - entries << [name, node['id'], subtype || type] + next if name.starts_with?('Example') + entries << [name, node['id'], get_type] end end end diff --git a/lib/docs/scrapers/svelte.rb b/lib/docs/scrapers/svelte.rb index 2dc90ac02d..c5900a0b90 100644 --- a/lib/docs/scrapers/svelte.rb +++ b/lib/docs/scrapers/svelte.rb @@ -8,18 +8,27 @@ class Svelte < UrlScraper code: 'https://github.com/sveltejs/svelte' } + self.root_path = 'introduction' options[:root_title] = 'Svelte' + # https://github.com/sveltejs/svelte/blob/master/LICENSE.md options[:attribution] = <<-HTML - © 2016–2022 Rich Harris and contributors
+ © 2016–2023 Rich Harris and contributors
Licensed under the MIT License. HTML options[:skip] = %w(team.html plugins/) - self.release = '3.55.0' - self.base_url = 'https://svelte.dev/docs' + self.base_url = 'https://svelte.dev/docs/' html_filters.push 'svelte/entries', 'svelte/clean_html' + + version do + self.release = '4.2.1' + end + + version '3' do + self.release = '3.55.0' + end def get_latest_version(opts) get_npm_version('svelte', opts) From 17d492b4856499831ff2ec32056f43ab6b55c7bb Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Mon, 9 Oct 2023 21:59:50 +0200 Subject: [PATCH 0084/1110] Update Zig documentation (0.11.0) --- lib/docs/scrapers/zig.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/docs/scrapers/zig.rb b/lib/docs/scrapers/zig.rb index bcd3d0a9ff..a4fd556b99 100644 --- a/lib/docs/scrapers/zig.rb +++ b/lib/docs/scrapers/zig.rb @@ -2,7 +2,7 @@ module Docs class Zig < UrlScraper self.name = 'Zig' self.type = 'simple' - self.release = '0.10.0' + self.release = '0.11.0' self.base_url = "https://ziglang.org/documentation/#{self.release}/" self.links = { home: 'https://ziglang.org/', @@ -13,7 +13,8 @@ class Zig < UrlScraper options[:follow_links] = false options[:attribution] = <<-HTML - © 2015–2022, Zig contributors + © 2015–2023, Zig contributors
+ Licensed under the MIT License. HTML def get_latest_version(opts) From 94bf820267559db42f35a9ce59c15fcea607a68d Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Mon, 9 Oct 2023 22:39:18 +0200 Subject: [PATCH 0085/1110] hapi, joi, nushell, varnish: add news entry --- assets/javascripts/news.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/assets/javascripts/news.json b/assets/javascripts/news.json index a64014884a..dfe15ffcb8 100644 --- a/assets/javascripts/news.json +++ b/assets/javascripts/news.json @@ -1,4 +1,8 @@ [ + [ + "2023-10-09", + "New documentations: hapi, joi, Nushell, Varnish" + ], [ "2023-08-24", "New documentation: Fluture" From de4c884f2fb512c3bf9ca87222b3602c96232788 Mon Sep 17 00:00:00 2001 From: Mateus Pereira Date: Tue, 10 Oct 2023 10:07:53 -0300 Subject: [PATCH 0086/1110] Remove parent divs only once for each `ul` element --- lib/docs/filters/cppref/clean_html.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/docs/filters/cppref/clean_html.rb b/lib/docs/filters/cppref/clean_html.rb index d7f564ff74..79311bb135 100644 --- a/lib/docs/filters/cppref/clean_html.rb +++ b/lib/docs/filters/cppref/clean_html.rb @@ -19,8 +19,9 @@ def call node.before(node.children).remove end - css('div > ul').each do |node| - node.parent.before(node.parent.children).remove + parents = css('div > ul').map(&:parent).uniq + parents.each do |parent| + parent.before(parent.children).remove end css('dl > dd:first-child:last-child > ul:first-child:last-child').each do |node| From e36051a5ca0fc1c3774020792980ee25355e9d17 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 14 Oct 2023 00:06:44 +0000 Subject: [PATCH 0087/1110] Update ruby/setup-ruby action to v1.156.0 --- .github/workflows/build.yml | 2 +- .github/workflows/schedule-doc-report.yml | 2 +- .github/workflows/test.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e516c8acf2..94edbc7f03 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: steps: - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0 - name: Set up Ruby - uses: ruby/setup-ruby@d37167af451eb51448db3354e1057b75c4b268f7 # v1.155.0 + uses: ruby/setup-ruby@5cfe23c062c0aac352e765b1b7cc12ea5255ccc4 # v1.156.0 with: bundler-cache: true # runs 'bundle install' and caches installed gems automatically - name: Run tests diff --git a/.github/workflows/schedule-doc-report.yml b/.github/workflows/schedule-doc-report.yml index f792f4dd1b..b725feab08 100644 --- a/.github/workflows/schedule-doc-report.yml +++ b/.github/workflows/schedule-doc-report.yml @@ -10,7 +10,7 @@ jobs: steps: - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0 - name: Set up Ruby - uses: ruby/setup-ruby@d37167af451eb51448db3354e1057b75c4b268f7 # v1.155.0 + uses: ruby/setup-ruby@5cfe23c062c0aac352e765b1b7cc12ea5255ccc4 # v1.156.0 with: bundler-cache: true # runs 'bundle install' and caches installed gems automatically - name: Generate report diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 039d03ec1c..8238962dc7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ jobs: steps: - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0 - name: Set up Ruby - uses: ruby/setup-ruby@d37167af451eb51448db3354e1057b75c4b268f7 # v1.155.0 + uses: ruby/setup-ruby@5cfe23c062c0aac352e765b1b7cc12ea5255ccc4 # v1.156.0 with: bundler-cache: true # runs 'bundle install' and caches installed gems automatically - name: Run tests From b46cb9598d70bba893f0c6710be681e55f976066 Mon Sep 17 00:00:00 2001 From: Tim Lim Date: Mon, 16 Oct 2023 20:49:38 +0800 Subject: [PATCH 0088/1110] Allow Python scraper to keep empty spans with ids --- lib/docs/filters/sphinx/clean_html.rb | 8 +++++--- lib/docs/scrapers/python.rb | 6 ++++++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/docs/filters/sphinx/clean_html.rb b/lib/docs/filters/sphinx/clean_html.rb index 126ff47a72..36bb96d9eb 100644 --- a/lib/docs/filters/sphinx/clean_html.rb +++ b/lib/docs/filters/sphinx/clean_html.rb @@ -36,9 +36,11 @@ def call node.replace(pre) end - css('span[id]:empty').each do |node| - (node.next_element || node.previous_element)['id'] ||= node['id'] if node.next_element || node.previous_element - node.remove + unless context[:sphinx_keep_empty_ids] + css('span[id]:empty').each do |node| + (node.next_element || node.previous_element)['id'] ||= node['id'] if node.next_element || node.previous_element + node.remove + end end css('.section').each do |node| diff --git a/lib/docs/scrapers/python.rb b/lib/docs/scrapers/python.rb index 39448bd5da..52b2505fd6 100644 --- a/lib/docs/scrapers/python.rb +++ b/lib/docs/scrapers/python.rb @@ -7,6 +7,12 @@ class Python < FileScraper code: 'https://github.com/python/cpython' } + # bypass the clean_text filter as it removes empty span with ids + options[:clean_text] = false + + # bypass sphinx modifying empty ids + options[:sphinx_keep_empty_ids] = true + options[:skip_patterns] = [/whatsnew/] options[:skip] = %w( library/2to3.html From 46a3f335bf52cdfa8c19bfdf481bd19889f88185 Mon Sep 17 00:00:00 2001 From: Tim Lim Date: Fri, 20 Oct 2023 20:41:37 +0800 Subject: [PATCH 0089/1110] CPP parse entries ending with --- lib/docs/filters/cpp/entries.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/docs/filters/cpp/entries.rb b/lib/docs/filters/cpp/entries.rb index d13526d39d..761e8a110d 100644 --- a/lib/docs/filters/cpp/entries.rb +++ b/lib/docs/filters/cpp/entries.rb @@ -52,7 +52,7 @@ def format_name(name) name.remove! %r{\s\(.+\)} name.sub! %r{\AStandard library header <(.+)>\z}, '\1' - name.sub! %r{(<[^>]+>)}, '' + name.sub! %r{(<[^>]+>::)}, '::' if name.include?('operator') && name.include?(',') name.sub!(%r{operator.+([\( ])}, 'operators (') || name.sub!(%r{operator.+}, 'operators') From 488c98a3a3aabfebd401f3ba111a3391df5b97d4 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 21 Oct 2023 00:07:07 +0000 Subject: [PATCH 0090/1110] Update dependency thor to v1.3.0 --- Gemfile.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile.lock b/Gemfile.lock index bc4a09b788..68930c1d03 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -121,7 +121,7 @@ GEM daemons (~> 1.0, >= 1.0.9) eventmachine (~> 1.0, >= 1.0.4) rack (>= 1, < 3) - thor (1.2.2) + thor (1.3.0) tilt (2.2.0) tty-pager (0.14.0) strings (~> 0.2.0) From 50d7304194eac4a6b8cc87f66e15216d72a0a8d1 Mon Sep 17 00:00:00 2001 From: Tim Lim Date: Sat, 21 Oct 2023 16:19:22 +0800 Subject: [PATCH 0091/1110] Fix CPP clean_html to remove adding an extra dot --- lib/docs/filters/cppref/clean_html.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/docs/filters/cppref/clean_html.rb b/lib/docs/filters/cppref/clean_html.rb index d7f564ff74..b4902f6dea 100644 --- a/lib/docs/filters/cppref/clean_html.rb +++ b/lib/docs/filters/cppref/clean_html.rb @@ -82,7 +82,6 @@ def call node << node.next end node.inner_html = node.inner_html.strip - node << '.' if node.content =~ /[a-zA-Z0-9\)]\z/ node.remove if node.content.blank? && !node.at_css('img') end From a2fb542af33abd12503d4e42b5099b9182589fb5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 21 Oct 2023 11:03:00 +0000 Subject: [PATCH 0092/1110] Update ruby/setup-ruby action to v1.157.0 --- .github/workflows/build.yml | 2 +- .github/workflows/schedule-doc-report.yml | 2 +- .github/workflows/test.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 94edbc7f03..ff4c705e27 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -13,7 +13,7 @@ jobs: steps: - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0 - name: Set up Ruby - uses: ruby/setup-ruby@5cfe23c062c0aac352e765b1b7cc12ea5255ccc4 # v1.156.0 + uses: ruby/setup-ruby@a05e47355e80e57b9a67566a813648fa67d92011 # v1.157.0 with: bundler-cache: true # runs 'bundle install' and caches installed gems automatically - name: Run tests diff --git a/.github/workflows/schedule-doc-report.yml b/.github/workflows/schedule-doc-report.yml index b725feab08..005666405e 100644 --- a/.github/workflows/schedule-doc-report.yml +++ b/.github/workflows/schedule-doc-report.yml @@ -10,7 +10,7 @@ jobs: steps: - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0 - name: Set up Ruby - uses: ruby/setup-ruby@5cfe23c062c0aac352e765b1b7cc12ea5255ccc4 # v1.156.0 + uses: ruby/setup-ruby@a05e47355e80e57b9a67566a813648fa67d92011 # v1.157.0 with: bundler-cache: true # runs 'bundle install' and caches installed gems automatically - name: Generate report diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8238962dc7..47cc83082c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -11,7 +11,7 @@ jobs: steps: - uses: actions/checkout@ee0669bd1cc54295c223e0bb666b733df41de1c5 # v2.7.0 - name: Set up Ruby - uses: ruby/setup-ruby@5cfe23c062c0aac352e765b1b7cc12ea5255ccc4 # v1.156.0 + uses: ruby/setup-ruby@a05e47355e80e57b9a67566a813648fa67d92011 # v1.157.0 with: bundler-cache: true # runs 'bundle install' and caches installed gems automatically - name: Run tests From 4bdbbcd25a303795d6707201d016638ff6083269 Mon Sep 17 00:00:00 2001 From: Tim Lim Date: Sun, 22 Oct 2023 22:08:51 +0800 Subject: [PATCH 0093/1110] CPP Fix - prevent source code parent replacement if parent is a list item --- lib/docs/filters/cppref/clean_html.rb | 2 +- lib/docs/filters/cppref/fix_code.rb | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/docs/filters/cppref/clean_html.rb b/lib/docs/filters/cppref/clean_html.rb index d7f564ff74..d68e4f0b6d 100644 --- a/lib/docs/filters/cppref/clean_html.rb +++ b/lib/docs/filters/cppref/clean_html.rb @@ -57,7 +57,7 @@ def call node.content = ' ' if node.content.empty? end - css('tt', 'span > span.source-cpp', 'span.t-c', 'span.t-lc', 'span.t-dsc-see-tt').each do |node| + css('tt', 'span > span.source-cpp', 'span.t-c', 'span.t-lc', 'span.t-dsc-see-tt', 'div.t-li1 > span.source-cpp', 'div.t-li2 > span.source-cpp', 'div.t-li3 > span.source-cpp').each do |node| node.name = 'code' node.remove_attribute('class') node.content = node.content unless node.at_css('a') diff --git a/lib/docs/filters/cppref/fix_code.rb b/lib/docs/filters/cppref/fix_code.rb index c80a74264f..e546640dfd 100644 --- a/lib/docs/filters/cppref/fix_code.rb +++ b/lib/docs/filters/cppref/fix_code.rb @@ -3,10 +3,12 @@ class Cppref class FixCodeFilter < Filter def call css('div > span.source-c', 'div > span.source-cpp').each do |node| - node.inner_html = node.inner_html.gsub(/
\n?/, "\n").gsub("\n

\n", "

\n") - node.parent.name = 'pre' - node.parent['class'] = node['class'] - node.parent.content = node.content + if (node.parent.classes||[]).none?{|className| ['t-li1','t-li2','t-li3'].include?(className) } + node.inner_html = node.inner_html.gsub(/
\n?/, "\n").gsub("\n

\n", "

\n") + node.parent.name = 'pre' + node.parent['class'] = node['class'] + node.parent.content = node.content + end end nbsp = Nokogiri::HTML(' ').text From 5660baa390f62c57f879982edc70bb180e7f9921 Mon Sep 17 00:00:00 2001 From: Tim Lim Date: Mon, 23 Oct 2023 19:49:12 +0800 Subject: [PATCH 0094/1110] Fix CPP - Add support for super/subscript --- lib/docs/filters/cppref/clean_html.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/docs/filters/cppref/clean_html.rb b/lib/docs/filters/cppref/clean_html.rb index d7f564ff74..cb1e880f9d 100644 --- a/lib/docs/filters/cppref/clean_html.rb +++ b/lib/docs/filters/cppref/clean_html.rb @@ -109,6 +109,16 @@ def call node['src'] = node['src'].sub! %r{https://upload.cppreference.com/mwiki/(images/[^"']+?)}, 'http://upload.cppreference.com/mwiki/\1' end + css('.t-su.t-su-b').each do |node| + node.inner_html = node.inner_html.gsub('
', '') + node.name = 'sub' + end + + css('.t-su:not(.t-su-b)').each do |node| + node.inner_html = node.inner_html.gsub('
', '') + node.name = 'sup' + end + # temporary solution due lack of mathjax/mathml support css('.t-mfrac').each do |node| fraction = Nokogiri::XML::Node.new('span', doc.document) From c34b5ac0de4bafc43279fc6552c5a467ec5c99d4 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Tue, 24 Oct 2023 17:24:18 +0200 Subject: [PATCH 0095/1110] Update JavaScript documentation --- lib/docs/scrapers/mdn/javascript.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/docs/scrapers/mdn/javascript.rb b/lib/docs/scrapers/mdn/javascript.rb index b02f2882f9..7079e665f5 100644 --- a/lib/docs/scrapers/mdn/javascript.rb +++ b/lib/docs/scrapers/mdn/javascript.rb @@ -3,7 +3,7 @@ class Javascript < Mdn prepend FixInternalUrlsBehavior prepend FixRedirectionsBehavior - # release = '2023-04-24' + # release = '2023-10-24' self.name = 'JavaScript' self.base_url = 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference' self.links = { From 2ae4c9085b8bc552c487b5f94d92a63e390cb5c3 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Tue, 24 Oct 2023 17:37:23 +0200 Subject: [PATCH 0096/1110] Update CSS documentation --- assets/stylesheets/components/_page.scss | 12 ++++++++++-- lib/docs/scrapers/mdn/css.rb | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/assets/stylesheets/components/_page.scss b/assets/stylesheets/components/_page.scss index de02fffbfb..e3c735dcc0 100644 --- a/assets/stylesheets/components/_page.scss +++ b/assets/stylesheets/components/_page.scss @@ -8,8 +8,16 @@ &._page-error { position: static; } - > h1, > section > h1 { @extend ._lined-heading; } - > h1:first-child, > section:first-of-type > h1 { margin-top: 0; } + > h1, + > header > h1, + > section > h1 { + @extend ._lined-heading; + } + > h1:first-child, + > header:first-of-type > h1, + > section:first-of-type > h1 { + margin-top: 0; + } a[href^="http:"], a[href^="https:"] { @extend %external-link; } diff --git a/lib/docs/scrapers/mdn/css.rb b/lib/docs/scrapers/mdn/css.rb index 10ed8025a6..bc172ff445 100644 --- a/lib/docs/scrapers/mdn/css.rb +++ b/lib/docs/scrapers/mdn/css.rb @@ -1,6 +1,6 @@ module Docs class Css < Mdn - # release = '2023-04-24' + # release = '2023-10-24' self.name = 'CSS' self.base_url = 'https://developer.mozilla.org/en-US/docs/Web/CSS' self.root_path = '/Reference' From 3bad2d9f6a1e618daab38ae9135d555c226dc0c3 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Tue, 24 Oct 2023 17:52:19 +0200 Subject: [PATCH 0097/1110] Update HTML documentation --- lib/docs/scrapers/mdn/html.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/docs/scrapers/mdn/html.rb b/lib/docs/scrapers/mdn/html.rb index bc3395f45e..7cb07e6678 100644 --- a/lib/docs/scrapers/mdn/html.rb +++ b/lib/docs/scrapers/mdn/html.rb @@ -2,7 +2,7 @@ module Docs class Html < Mdn prepend FixInternalUrlsBehavior - # release = '2023-04-24' + # release = '2023-10-24' self.name = 'HTML' self.base_url = 'https://developer.mozilla.org/en-US/docs/Web/HTML' self.links = { From ab9aeb2622838131574023ea3a01e933e2c770df Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Tue, 24 Oct 2023 21:34:54 +0200 Subject: [PATCH 0098/1110] Update Mdn documentation --- lib/docs/scrapers/mdn/dom.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/docs/scrapers/mdn/dom.rb b/lib/docs/scrapers/mdn/dom.rb index 3ed660f0ea..c2b144114f 100644 --- a/lib/docs/scrapers/mdn/dom.rb +++ b/lib/docs/scrapers/mdn/dom.rb @@ -1,7 +1,6 @@ module Docs class Dom < Mdn - - # release = '2023-04-24' + # release = '2023-10-24' self.name = 'Web APIs' self.slug = 'dom' self.base_url = 'https://developer.mozilla.org/en-US/docs/Web/API' From f131e1a873a11cde55cc214717f7b19ee300653e Mon Sep 17 00:00:00 2001 From: sharpevo Date: Mon, 20 Nov 2023 22:06:50 +0800 Subject: [PATCH 0099/1110] Fix the regexp for Golang generic functions --- lib/docs/filters/go/entries.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/docs/filters/go/entries.rb b/lib/docs/filters/go/entries.rb index e125b8fe3e..3d1c9d2c4b 100644 --- a/lib/docs/filters/go/entries.rb +++ b/lib/docs/filters/go/entries.rb @@ -28,7 +28,7 @@ def additional_entries case node.content when /type\ (\w+)/ name = "#{package}.#{$1}" - when /func\ (?:\(.+\)\ )?(\w+)\(/ + when /func\ (?:\(.+\)\ )?(\w+)[\(\[]/ name = "#{$1}()" name.prepend "#{$1}." if node['href'] =~ /#(\w+)\.#{$1}/ name.prepend "#{package}." From 3972760e17dd25605bf6b575f22d44799feadfd5 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 5 Dec 2023 15:48:50 +0000 Subject: [PATCH 0100/1110] Update dependency image_optim_pack to v0.10.1 --- Gemfile.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 68930c1d03..e933773e90 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -25,7 +25,7 @@ GEM ffi (>= 1.15.0) eventmachine (1.2.7) execjs (2.8.1) - exifr (1.3.10) + exifr (1.4.0) ffi (1.15.5) fspath (3.1.2) highline (2.0.3) @@ -40,10 +40,10 @@ GEM image_size (>= 1.5, < 4) in_threads (~> 1.3) progress (~> 3.0, >= 3.0.1) - image_optim_pack (0.9.1.20230526) + image_optim_pack (0.10.1) fspath (>= 2.1, < 4) image_optim (~> 0.19) - image_size (3.2.0) + image_size (3.3.0) in_threads (1.6.0) method_source (1.0.0) mini_portile2 (2.8.4) From 169651c1b75018a872051103c01253adc0a5ae51 Mon Sep 17 00:00:00 2001 From: Yikai Zhao Date: Tue, 19 Dec 2023 00:51:43 +0800 Subject: [PATCH 0101/1110] Add bazel doc --- lib/docs/filters/bazel/clean_html.rb | 17 +++++++++ lib/docs/filters/bazel/entries.rb | 37 ++++++++++++++++++++ lib/docs/scrapers/bazel.rb | 50 +++++++++++++++++++++++++++ public/icons/docs/bazel/16.png | Bin 0 -> 1328 bytes public/icons/docs/bazel/16@2x.png | Bin 0 -> 2418 bytes public/icons/docs/bazel/SOURCE | 1 + 6 files changed, 105 insertions(+) create mode 100644 lib/docs/filters/bazel/clean_html.rb create mode 100644 lib/docs/filters/bazel/entries.rb create mode 100644 lib/docs/scrapers/bazel.rb create mode 100644 public/icons/docs/bazel/16.png create mode 100644 public/icons/docs/bazel/16@2x.png create mode 100644 public/icons/docs/bazel/SOURCE diff --git a/lib/docs/filters/bazel/clean_html.rb b/lib/docs/filters/bazel/clean_html.rb new file mode 100644 index 0000000000..d772908e32 --- /dev/null +++ b/lib/docs/filters/bazel/clean_html.rb @@ -0,0 +1,17 @@ +module Docs + class Bazel + class CleanHtmlFilter < Filter + + def call + css('.devsite-article-meta').remove + css('devsite-feature-tooltip').remove + css('devsite-thumb-rating').remove + css('devsite-toc').remove + css('a.button-with-icon').remove + css('button.devsite-heading-link').remove + doc + end + + end + end +end diff --git a/lib/docs/filters/bazel/entries.rb b/lib/docs/filters/bazel/entries.rb new file mode 100644 index 0000000000..3c1ad9cd88 --- /dev/null +++ b/lib/docs/filters/bazel/entries.rb @@ -0,0 +1,37 @@ +module Docs + class Bazel + class EntriesFilter < Docs::EntriesFilter + + def get_name + at_css('h1').content.strip + end + + def get_type + "Build encyclopedia" + end + + def additional_entries + entries = [] + + special_page_types = { + 'functions' => 'Function', + 'make-variables' => 'Make Variable', + 'common-definitions' => 'Common Definition', + } + page_type = special_page_types[subpath] + unless page_type.nil? + # only first ul + at_css('.devsite-article-body > ul').css('li > a').each do |node| + entries << [node.content.strip, node['href'].sub('#', ''), page_type] + end + end + css('h2#rules + ul > li > a').each do |node| + entries << [node.content.strip, node['href'].sub('#', ''), "Rule"] + end + + entries + end + + end + end +end diff --git a/lib/docs/scrapers/bazel.rb b/lib/docs/scrapers/bazel.rb new file mode 100644 index 0000000000..3f2f944747 --- /dev/null +++ b/lib/docs/scrapers/bazel.rb @@ -0,0 +1,50 @@ +module Docs + class Bazel < UrlScraper + self.name = 'Bazel' + self.type = 'bazel' + + html_filters.push 'bazel/entries', 'bazel/clean_html' + + options[:skip] = %w(platform) + + options[:container] = "devsite-content" + options[:attribution] = <<-HTML + Licensed under the Creative Commons Attribution 4.0 License, and code samples are licensed under the Apache 2.0 License. + HTML + + version '7.0' do + self.release = '7.0.0' + self.base_url = 'https://bazel.build/versions/7.0.0/reference/be/' + end + + version '6.4' do + self.release = '6.4.0' + self.base_url = 'https://bazel.build/versions/6.4.0/reference/be/' + end + + version '6.3' do + self.release = '6.3.0' + self.base_url = 'https://bazel.build/versions/6.3.0/reference/be/' + end + + version '6.2' do + self.release = '6.2.0' + self.base_url = 'https://bazel.build/versions/6.2.0/reference/be/' + end + + version '6.1' do + self.release = '6.1.0' + self.base_url = 'https://bazel.build/versions/6.1.0/reference/be/' + end + + version '6.0' do + self.release = '6.0.0' + self.base_url = 'https://bazel.build/versions/6.0.0/reference/be/' + end + + def get_latest_version(opts) + get_latest_github_release('bazelbuild', 'bazel', opts) + end + + end +end diff --git a/public/icons/docs/bazel/16.png b/public/icons/docs/bazel/16.png new file mode 100644 index 0000000000000000000000000000000000000000..6bc04077adfd3c6ae0ac074bbf53b17af8b9e824 GIT binary patch literal 1328 zcmeAS@N?(olHy`uVBq!ia0vp^0w65F1SAhIZYc#)3dtTpz6=aiY77hwEes65fI!_=LUp?N*q6L`MW6c2BVAkhP7g89w7-U z9g}2AOuQB>b5xrgJALY`G?C1vD^A+8nGBf(b1y^%6vs4IL>4HmVGN#PRIp)D#!X(g zj;Xu7FK*jV@P6m_cddJCBkV#vHeY^!`R9lFfBR>qpJQ%WdWCCoQ=|WPxhLk_Y>6R1 zJRX|oFAM>-fnSa?agmSm#11Yt}8B= zirRkrv`@H9WX>w*pZ_OyGaWC_*?BnO(q!qVU7iPcCj4DwA)!Lav7Pv6W>C!S1` zp1k|!>&b?b*Ke?$thvm_Xy?-BcAmC77e3wfSgQI;;Ir(uygqAF>GU;!wtial>z>>B z)7zd0il1=X%fQGW@PUb8kCO_ASjUCg3b{uW1e#eL&g3xJUf%e@+<<%Me)YYQ4~*X* zYtQ$39r9Iwk8JFpNBSLBM=V!7Jh(P_qqE88hQ$$*IW2A(3=9qoHTm1-u6(lYo8{WG ze;4H6iMszjZT8Hdb+wt3CO+8u=G4JA8-FNjeq~Ev{!S@^`P0F|8tLd6?{jyWO|xlH z-q;;@&}0fvsD;$#Gq*mgIe}B;Pj9g4d@XSLXej&s9V|i*)%NDNvASlynD4czI zYhjAvzDD*Laq*iawjC6_(!l4Ssc(qtv=ie6eg;eN)JS=X|r21<&IJwSMnT&G+3H^{a2D={sY; zqdnnT9r_-!R!e{F_!ih}`0v`e+m9SXG!}{NU-=|CW#XRAuMfA#oqfKG{mRu_DoH*n zllQnUz3}pj^R&&vE0+|#lT>SvoqM`)>e*Mi>D8OhTnw30T=mW%QOwr0W~DRd(pw#| zxt#VHzf^fMTt5AKq^M(?XP5d=On<$_&Ap3{IhE}BHZ@D0VZE5U*@@<^)r-2GBwDsN zzhgN1-_EjQOnSs?_ z%a37#V5?W>xt?5?N=_pwZ-Ln_PbL{pe;pb2LI3R2SG6ul4^@}8c2s`blCz6Na(>hkMTnu9m5GIwv7xqsft7(l z+PuZhC>nC}Q!>*kff@|Wbq&mQjer^qtV~U<3@soU%5F@p1ZvQL+fb63n_66wm|K8u aiKUgHDMXKCUC{=h9tKZWKbLh*2~7Z<^G!zp literal 0 HcmV?d00001 diff --git a/public/icons/docs/bazel/16@2x.png b/public/icons/docs/bazel/16@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a8e26f2fbaa4a390bcb756ecf6117333761bb798 GIT binary patch literal 2418 zcmZ9OcTm%56UTpqUPS@nuHm}9>uOa1HhvvKh zz5okW5hjF_R^N1h#k7f2ZX2RJb_EhZHOC<^>SW31__z03-&4cbezMP@k?U$v#ZMm*z z=asZOSbWQ#2wy1S<#ShkYZoU&%6Klj-a*gDMonutse7n#<|XE{5aV4F5~fJB*Zt#gV0_W_wS2Fy*_zrb+8&8~V#&ofF%2M-(rnyVz0;@8O!dAVN zttn(PuZR@V)iRBFH5*Zehe}+dxU@sT8xHo~daPly6jwLqb2R7GRp?zxJn3!c#CO|q zWid8tnOM^MT^$Utls!q&u$~GhW<9uD?Rt2vZaP`RqR6RJU?3#CHJlj0kui1GM`cxr zyel@K%K4~q757rBctG8XnZ4L-@~>fosoS1;P1OtFk+tXx>C3ty2ai;0c?{N4G;aZV$=l2G2@26I^-sdxU z4?lS=7F)nywB*FdP6Y*9`E_xeyuH(FkEiGmsF{LD&O^WvxfkX;tbH&2L3?fXOpW_A zsy7LAaWe4_c^K@^^E&lV<|gN`n4LZ9J7h+t%FT96pB=Q?Wjoko*byvApsra>n%I|r zk&iNMZFH&qh+7^(PkHx_@2x~tnu;chCiV@byM@TKj>1G>`3d?l7NDckq(;260KGBs zBCl37@IF3$V6VEn5Zw2LqsGSOsr(bl(*6w}hW>u(No(OJ6Zr{RTNHdjuH!r=z_x!V zsL;TwDvx{TA96!Q0w-$@i?)&lyN;aL2x8*n0al*rm z0@?$wUAXD!FH|tTQ4pSn0>WwiJ9z7yvqyO?@uPSm@wWE-&iA3CbWRa1A$kt6}ScOgegEGfY_`vj{yi zen*)O^p!=ZgFQ*N!#557_Jd(nO2NsT#$cgbu73NXR?^NF`aS3J&a)FgNZNwyIWbepH?PP!rFfQlF!J^_EmO3hGR zI9ubNUE!H_Yc4DJMIVDm(9|hNcm9@*`UD=(fR;vbh_}GRs~Q56qwMqi{J^_^e$V)9 zmz#Ja%jogLS0eFe8+glC7YCORqGZd^XnW0W8N*TK|q z*fm`aMa^FhwE}j_=ZGbLT`!Q>E|+XgM#=bImP@v+w4~OT^KB3tt;Q0Wpg~T=X8wz* z45Xpwdk+<+G^}@musLFVUqyXHfBU6pr0P*o*#>bM9Hqf;b)&uZtM?s!-C(8N+C%K^ zx%(UmT+#J(U8>$%&BeGEXKvF_s7Z%ty{{+F-@I{4znympM7V1!CEu66YAU%fI(Bx) zHSG3zHLRgr3>^-%E2=9{5JC~Ugzua^tB>B;bF}oL&EXmh5ZBbs<7ahR%0l?}!OSFX z5s(a<*fHWOfjWM9ewOeY!lvrqv{)Ja`1iCoL#K*djrgGFzXqel_AQm8pV7GinJckt zTC>Brwy*n@;irT8yHt4BELRv==PFe?TdVNJe4QYIUMg;Z-iez|z*fmo@0_et>gq@e zwM@Ai@kp8!m~(IaAaG=4%R9z{3#623{srmZ>|Qo`HzWZo9!g=Ps=z5uX=|yc$j>IT zmnu_;{`}UFg*f{STNc8MEWUCkc{*golt$Xesj1&;+mQj=So?W z)+`9%WGr7@C^BzgZAhpynKaV46mj;(#M)3nvn?2bV zva5tFE=wns;mA}H!`1H; zKjZa7O}^~9CCZOp&okba=d*-;M69iWiBXGW_w=C(KlK`Yn888~ikSPnGu?lNedb)4 zs!teZcF_YsQ^L1A^nf9qEi?Df2+IZ2nfEXM$U?r!mbst{^7JQ(clOni^KSZ|3(hwf z@Ne*jj)UBpn2+d}-Z53(GK#5Wc5;e#6YR8Y#-<3~a7$5sTt8RvIh7<}0 zD%1D+7}C_h_oe4`MhxfcQkMvHSB7ClJw^(XuQ?xAXt2ImTpH(98It>x*tJ#UdC2>{ z!gKLDQf~FC8FO2M(&v8iJ!RJoxb8ELP~2XJuj#Xxx^D-1pfCgdHF6FKKhrKXyECiy z0`pzULD)I^97PpWbo~2^*T+Uj+HD~bgtQ5Aa}L77T?4Q*0Tdt#in34zSt!gBdIPQm zgDXP85C|LsaW5*1`yar^&kg4uP6MDSvJe$n1sVW>D=WjHuzv#~bGhL(K Date: Wed, 3 Jan 2024 18:19:30 +0900 Subject: [PATCH 0102/1110] Upgrade minitest from 5.18.1 to 5.20.0 --- Gemfile.lock | 2 +- test/app_test.rb | 2 +- test/lib/docs/core/doc_test.rb | 2 +- test/lib/docs/core/entry_index_test.rb | 2 +- test/lib/docs/core/filter_test.rb | 2 +- test/lib/docs/core/instrumentable_test.rb | 2 +- test/lib/docs/core/manifest_test.rb | 2 +- test/lib/docs/core/models/entry_test.rb | 2 +- test/lib/docs/core/models/type_test.rb | 2 +- test/lib/docs/core/parser_test.rb | 2 +- test/lib/docs/core/request_test.rb | 2 +- test/lib/docs/core/requester_test.rb | 2 +- test/lib/docs/core/response_test.rb | 2 +- test/lib/docs/core/scraper_test.rb | 2 +- test/lib/docs/core/scrapers/file_scraper_test.rb | 2 +- test/lib/docs/core/scrapers/url_scraper_test.rb | 2 +- test/lib/docs/core/url_test.rb | 2 +- test/lib/docs/filters/core/apply_base_url_test.rb | 2 +- test/lib/docs/filters/core/clean_html_test.rb | 2 +- test/lib/docs/filters/core/clean_text_test.rb | 2 +- test/lib/docs/filters/core/container_test.rb | 2 +- test/lib/docs/filters/core/entries_test.rb | 2 +- test/lib/docs/filters/core/inner_html_test.rb | 2 +- test/lib/docs/filters/core/internal_urls_test.rb | 2 +- test/lib/docs/filters/core/normalize_paths_test.rb | 2 +- test/lib/docs/filters/core/normalize_urls_test.rb | 2 +- test/lib/docs/filters/core/parse_cf_email_test.rb | 2 +- test/lib/docs/filters/core/title_test.rb | 2 +- test/lib/docs/storage/abstract_store_test.rb | 2 +- test/lib/docs/storage/file_store_test.rb | 2 +- test/test_helper.rb | 2 +- 31 files changed, 31 insertions(+), 31 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 68930c1d03..115d6860fd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -47,7 +47,7 @@ GEM in_threads (1.6.0) method_source (1.0.0) mini_portile2 (2.8.4) - minitest (5.18.1) + minitest (5.20.0) multi_json (1.15.0) mustermann (3.0.0) ruby2_keywords (~> 0.0.1) diff --git a/test/app_test.rb b/test/app_test.rb index 8e9e369b5e..44fca64aab 100644 --- a/test/app_test.rb +++ b/test/app_test.rb @@ -2,7 +2,7 @@ require 'rack/test' require 'app' -class AppTest < MiniTest::Spec +class AppTest < Minitest::Spec include Rack::Test::Methods MODERN_BROWSER = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.10; rv:39.0) Gecko/20100101 Firefox/39.0' diff --git a/test/lib/docs/core/doc_test.rb b/test/lib/docs/core/doc_test.rb index b0d2341f84..6b41cdd9c8 100644 --- a/test/lib/docs/core/doc_test.rb +++ b/test/lib/docs/core/doc_test.rb @@ -1,7 +1,7 @@ require_relative '../../../test_helper' require_relative '../../../../lib/docs' -class DocsDocTest < MiniTest::Spec +class DocsDocTest < Minitest::Spec let :doc do Class.new Docs::Doc do self.name = 'name' diff --git a/test/lib/docs/core/entry_index_test.rb b/test/lib/docs/core/entry_index_test.rb index 187f5f4ad2..95495eb90a 100644 --- a/test/lib/docs/core/entry_index_test.rb +++ b/test/lib/docs/core/entry_index_test.rb @@ -1,7 +1,7 @@ require_relative '../../../test_helper' require_relative '../../../../lib/docs' -class DocsEntryIndexTest < MiniTest::Spec +class DocsEntryIndexTest < Minitest::Spec let :entry do Docs::Entry.new 'name', 'path', 'type' end diff --git a/test/lib/docs/core/filter_test.rb b/test/lib/docs/core/filter_test.rb index 38c5019a3f..10872c7beb 100644 --- a/test/lib/docs/core/filter_test.rb +++ b/test/lib/docs/core/filter_test.rb @@ -1,7 +1,7 @@ require_relative '../../../test_helper' require_relative '../../../../lib/docs' -class DocsFilterTest < MiniTest::Spec +class DocsFilterTest < Minitest::Spec include FilterTestHelper self.filter_class = Docs::Filter diff --git a/test/lib/docs/core/instrumentable_test.rb b/test/lib/docs/core/instrumentable_test.rb index c3fe2b8b6b..5a244d5199 100644 --- a/test/lib/docs/core/instrumentable_test.rb +++ b/test/lib/docs/core/instrumentable_test.rb @@ -1,7 +1,7 @@ require_relative '../../../test_helper' require_relative '../../../../lib/docs' -class DocsInstrumentableTest < MiniTest::Spec +class DocsInstrumentableTest < Minitest::Spec let :extended_class do Class.new.tap { |klass| klass.send :extend, Docs::Instrumentable } end diff --git a/test/lib/docs/core/manifest_test.rb b/test/lib/docs/core/manifest_test.rb index f121e69b21..636885cd1a 100644 --- a/test/lib/docs/core/manifest_test.rb +++ b/test/lib/docs/core/manifest_test.rb @@ -1,7 +1,7 @@ require_relative '../../../test_helper' require_relative '../../../../lib/docs' -class ManifestTest < MiniTest::Spec +class ManifestTest < Minitest::Spec let :doc do doc = Class.new Docs::Scraper doc.name = 'TestDoc' diff --git a/test/lib/docs/core/models/entry_test.rb b/test/lib/docs/core/models/entry_test.rb index 7487340ff2..56de796661 100644 --- a/test/lib/docs/core/models/entry_test.rb +++ b/test/lib/docs/core/models/entry_test.rb @@ -1,7 +1,7 @@ require_relative '../../../../test_helper' require_relative '../../../../../lib/docs' -class DocsEntryTest < MiniTest::Spec +class DocsEntryTest < Minitest::Spec Entry = Docs::Entry let :entry do diff --git a/test/lib/docs/core/models/type_test.rb b/test/lib/docs/core/models/type_test.rb index a01d8e6140..e3f3b3368f 100644 --- a/test/lib/docs/core/models/type_test.rb +++ b/test/lib/docs/core/models/type_test.rb @@ -1,7 +1,7 @@ require_relative '../../../../test_helper' require_relative '../../../../../lib/docs' -class DocsTypeTest < MiniTest::Spec +class DocsTypeTest < Minitest::Spec Type = Docs::Type describe ".new" do diff --git a/test/lib/docs/core/parser_test.rb b/test/lib/docs/core/parser_test.rb index 440516a8e9..2edacacb99 100644 --- a/test/lib/docs/core/parser_test.rb +++ b/test/lib/docs/core/parser_test.rb @@ -1,7 +1,7 @@ require_relative '../../../test_helper' require_relative '../../../../lib/docs' -class DocsParserTest < MiniTest::Spec +class DocsParserTest < Minitest::Spec def parser(content) Docs::Parser.new(content) end diff --git a/test/lib/docs/core/request_test.rb b/test/lib/docs/core/request_test.rb index d05dd1ffe9..4e7353465d 100644 --- a/test/lib/docs/core/request_test.rb +++ b/test/lib/docs/core/request_test.rb @@ -1,7 +1,7 @@ require_relative '../../../test_helper' require_relative '../../../../lib/docs' -class DocsRequestTest < MiniTest::Spec +class DocsRequestTest < Minitest::Spec let :url do 'http://example.com' end diff --git a/test/lib/docs/core/requester_test.rb b/test/lib/docs/core/requester_test.rb index acf8d0a9a2..18a17ec3fc 100644 --- a/test/lib/docs/core/requester_test.rb +++ b/test/lib/docs/core/requester_test.rb @@ -1,7 +1,7 @@ require_relative '../../../test_helper' require_relative '../../../../lib/docs' -class DocsRequesterTest < MiniTest::Spec +class DocsRequesterTest < Minitest::Spec def stub_request(url) Typhoeus.stub(url).and_return(Typhoeus::Response.new) end diff --git a/test/lib/docs/core/response_test.rb b/test/lib/docs/core/response_test.rb index 9e92c034fc..5c06dc0cea 100644 --- a/test/lib/docs/core/response_test.rb +++ b/test/lib/docs/core/response_test.rb @@ -1,7 +1,7 @@ require_relative '../../../test_helper' require_relative '../../../../lib/docs' -class DocsResponseTest < MiniTest::Spec +class DocsResponseTest < Minitest::Spec let :response do Typhoeus::Response.new(options).tap do |response| response.extend Docs::Response diff --git a/test/lib/docs/core/scraper_test.rb b/test/lib/docs/core/scraper_test.rb index 3d49320743..7b5522b3cb 100644 --- a/test/lib/docs/core/scraper_test.rb +++ b/test/lib/docs/core/scraper_test.rb @@ -1,7 +1,7 @@ require_relative '../../../test_helper' require_relative '../../../../lib/docs' -class DocsScraperTest < MiniTest::Spec +class DocsScraperTest < Minitest::Spec class Scraper < Docs::Scraper self.type = 'scraper' self.base_url = 'http://example.com/' diff --git a/test/lib/docs/core/scrapers/file_scraper_test.rb b/test/lib/docs/core/scrapers/file_scraper_test.rb index 46dad3c780..93b2ba43b4 100644 --- a/test/lib/docs/core/scrapers/file_scraper_test.rb +++ b/test/lib/docs/core/scrapers/file_scraper_test.rb @@ -1,7 +1,7 @@ require_relative '../../../../test_helper' require_relative '../../../../../lib/docs' -class FileScraperTest < MiniTest::Spec +class FileScraperTest < Minitest::Spec ROOT_PATH = File.expand_path('../../../../../../', __FILE__) class Scraper < Docs::FileScraper diff --git a/test/lib/docs/core/scrapers/url_scraper_test.rb b/test/lib/docs/core/scrapers/url_scraper_test.rb index 9a687256ab..77fc235d57 100644 --- a/test/lib/docs/core/scrapers/url_scraper_test.rb +++ b/test/lib/docs/core/scrapers/url_scraper_test.rb @@ -1,7 +1,7 @@ require_relative '../../../../test_helper' require_relative '../../../../../lib/docs' -class DocsUrlScraperTest < MiniTest::Spec +class DocsUrlScraperTest < Minitest::Spec class Scraper < Docs::UrlScraper self.base_url = 'http://example.com' self.html_filters = Docs::FilterStack.new diff --git a/test/lib/docs/core/url_test.rb b/test/lib/docs/core/url_test.rb index 7d4bda43de..8f08b1404e 100644 --- a/test/lib/docs/core/url_test.rb +++ b/test/lib/docs/core/url_test.rb @@ -1,7 +1,7 @@ require_relative '../../../test_helper' require_relative '../../../../lib/docs' -class DocsUrlTest < MiniTest::Spec +class DocsUrlTest < Minitest::Spec URL = Docs::URL describe ".new" do diff --git a/test/lib/docs/filters/core/apply_base_url_test.rb b/test/lib/docs/filters/core/apply_base_url_test.rb index ba2764e7a8..ce070018e9 100644 --- a/test/lib/docs/filters/core/apply_base_url_test.rb +++ b/test/lib/docs/filters/core/apply_base_url_test.rb @@ -1,7 +1,7 @@ require_relative '../../../../test_helper' require_relative '../../../../../lib/docs' -class ApplyBaseUrlFilterTest < MiniTest::Spec +class ApplyBaseUrlFilterTest < Minitest::Spec include FilterTestHelper self.filter_class = Docs::ApplyBaseUrlFilter self.filter_type = 'html' diff --git a/test/lib/docs/filters/core/clean_html_test.rb b/test/lib/docs/filters/core/clean_html_test.rb index 2d3c9f8add..a1a47013d2 100644 --- a/test/lib/docs/filters/core/clean_html_test.rb +++ b/test/lib/docs/filters/core/clean_html_test.rb @@ -1,7 +1,7 @@ require_relative '../../../../test_helper' require_relative '../../../../../lib/docs' -class CleanHtmlFilterTest < MiniTest::Spec +class CleanHtmlFilterTest < Minitest::Spec include FilterTestHelper self.filter_class = Docs::CleanHtmlFilter diff --git a/test/lib/docs/filters/core/clean_text_test.rb b/test/lib/docs/filters/core/clean_text_test.rb index 7babf15e48..4126b88686 100644 --- a/test/lib/docs/filters/core/clean_text_test.rb +++ b/test/lib/docs/filters/core/clean_text_test.rb @@ -1,7 +1,7 @@ require_relative '../../../../test_helper' require_relative '../../../../../lib/docs' -class CleanTextFilterTest < MiniTest::Spec +class CleanTextFilterTest < Minitest::Spec include FilterTestHelper self.filter_class = Docs::CleanTextFilter diff --git a/test/lib/docs/filters/core/container_test.rb b/test/lib/docs/filters/core/container_test.rb index 7d4993e8b4..d1cc3ab437 100644 --- a/test/lib/docs/filters/core/container_test.rb +++ b/test/lib/docs/filters/core/container_test.rb @@ -1,7 +1,7 @@ require_relative '../../../../test_helper' require_relative '../../../../../lib/docs' -class ContainerFilterTest < MiniTest::Spec +class ContainerFilterTest < Minitest::Spec include FilterTestHelper self.filter_class = Docs::ContainerFilter self.filter_type = 'html' diff --git a/test/lib/docs/filters/core/entries_test.rb b/test/lib/docs/filters/core/entries_test.rb index e33f778d01..95951e1144 100644 --- a/test/lib/docs/filters/core/entries_test.rb +++ b/test/lib/docs/filters/core/entries_test.rb @@ -1,7 +1,7 @@ require_relative '../../../../test_helper' require_relative '../../../../../lib/docs' -class EntriesFilterTest < MiniTest::Spec +class EntriesFilterTest < Minitest::Spec include FilterTestHelper self.filter_class = Docs::EntriesFilter diff --git a/test/lib/docs/filters/core/inner_html_test.rb b/test/lib/docs/filters/core/inner_html_test.rb index bbd4095848..74fbc9c72f 100644 --- a/test/lib/docs/filters/core/inner_html_test.rb +++ b/test/lib/docs/filters/core/inner_html_test.rb @@ -1,7 +1,7 @@ require_relative '../../../../test_helper' require_relative '../../../../../lib/docs' -class InnerHtmlFilterTest < MiniTest::Spec +class InnerHtmlFilterTest < Minitest::Spec include FilterTestHelper self.filter_class = Docs::InnerHtmlFilter diff --git a/test/lib/docs/filters/core/internal_urls_test.rb b/test/lib/docs/filters/core/internal_urls_test.rb index c9194e09e0..9888c9fe67 100644 --- a/test/lib/docs/filters/core/internal_urls_test.rb +++ b/test/lib/docs/filters/core/internal_urls_test.rb @@ -1,7 +1,7 @@ require_relative '../../../../test_helper' require_relative '../../../../../lib/docs' -class InternalUrlsFilterTest < MiniTest::Spec +class InternalUrlsFilterTest < Minitest::Spec include FilterTestHelper self.filter_class = Docs::InternalUrlsFilter diff --git a/test/lib/docs/filters/core/normalize_paths_test.rb b/test/lib/docs/filters/core/normalize_paths_test.rb index 6d407f2b0b..1fc8272128 100644 --- a/test/lib/docs/filters/core/normalize_paths_test.rb +++ b/test/lib/docs/filters/core/normalize_paths_test.rb @@ -1,7 +1,7 @@ require_relative '../../../../test_helper' require_relative '../../../../../lib/docs' -class NormalizePathsFilterTest < MiniTest::Spec +class NormalizePathsFilterTest < Minitest::Spec include FilterTestHelper self.filter_class = Docs::NormalizePathsFilter diff --git a/test/lib/docs/filters/core/normalize_urls_test.rb b/test/lib/docs/filters/core/normalize_urls_test.rb index 804b05b159..0122c8fcc7 100644 --- a/test/lib/docs/filters/core/normalize_urls_test.rb +++ b/test/lib/docs/filters/core/normalize_urls_test.rb @@ -1,7 +1,7 @@ require_relative '../../../../test_helper' require_relative '../../../../../lib/docs' -class NormalizeUrlsFilterTest < MiniTest::Spec +class NormalizeUrlsFilterTest < Minitest::Spec include FilterTestHelper self.filter_class = Docs::NormalizeUrlsFilter diff --git a/test/lib/docs/filters/core/parse_cf_email_test.rb b/test/lib/docs/filters/core/parse_cf_email_test.rb index d6124fedac..7f2485822b 100644 --- a/test/lib/docs/filters/core/parse_cf_email_test.rb +++ b/test/lib/docs/filters/core/parse_cf_email_test.rb @@ -1,7 +1,7 @@ require_relative '../../../../test_helper' require_relative '../../../../../lib/docs' -class ParseCfEmailFilterTest < MiniTest::Spec +class ParseCfEmailFilterTest < Minitest::Spec include FilterTestHelper self.filter_class = Docs::ParseCfEmailFilter diff --git a/test/lib/docs/filters/core/title_test.rb b/test/lib/docs/filters/core/title_test.rb index 9a28bd3bee..24189555a0 100644 --- a/test/lib/docs/filters/core/title_test.rb +++ b/test/lib/docs/filters/core/title_test.rb @@ -1,7 +1,7 @@ require_relative '../../../../test_helper' require_relative '../../../../../lib/docs' -class TitleFilterTest < MiniTest::Spec +class TitleFilterTest < Minitest::Spec include FilterTestHelper self.filter_class = Docs::TitleFilter diff --git a/test/lib/docs/storage/abstract_store_test.rb b/test/lib/docs/storage/abstract_store_test.rb index d63e95f226..2bdf391e70 100644 --- a/test/lib/docs/storage/abstract_store_test.rb +++ b/test/lib/docs/storage/abstract_store_test.rb @@ -1,7 +1,7 @@ require_relative '../../../test_helper' require_relative '../../../../lib/docs' -class DocsAbstractStoreTest < MiniTest::Spec +class DocsAbstractStoreTest < Minitest::Spec InvalidPathError = Docs::AbstractStore::InvalidPathError LockError = Docs::AbstractStore::LockError diff --git a/test/lib/docs/storage/file_store_test.rb b/test/lib/docs/storage/file_store_test.rb index aa3ce3f8ec..1068b7d745 100644 --- a/test/lib/docs/storage/file_store_test.rb +++ b/test/lib/docs/storage/file_store_test.rb @@ -1,7 +1,7 @@ require_relative '../../../test_helper' require_relative '../../../../lib/docs' -class DocsFileStoreTest < MiniTest::Spec +class DocsFileStoreTest < Minitest::Spec let :store do Docs::FileStore.new(tmp_path) end diff --git a/test/test_helper.rb b/test/test_helper.rb index 2bcaa9d543..8ae02364c6 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -18,7 +18,7 @@ ActiveSupport::TestCase.test_order = :random -class MiniTest::Spec +class Minitest::Spec include ActiveSupport::Testing::Assertions module DSL From 7049a8316a61ec304b3a9731ae0bfd81afad0b00 Mon Sep 17 00:00:00 2001 From: Nicholas La Roux Date: Wed, 3 Jan 2024 20:12:16 +0900 Subject: [PATCH 0103/1110] Update Ruby, Rails, and Minitest docs --- lib/docs/scrapers/rdoc/minitest.rb | 2 +- lib/docs/scrapers/rdoc/rails.rb | 6 +++++- lib/docs/scrapers/rdoc/ruby.rb | 12 ++++++++---- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/docs/scrapers/rdoc/minitest.rb b/lib/docs/scrapers/rdoc/minitest.rb index 884dc0a65b..2ff12cf496 100644 --- a/lib/docs/scrapers/rdoc/minitest.rb +++ b/lib/docs/scrapers/rdoc/minitest.rb @@ -8,7 +8,7 @@ class Minitest < Rdoc self.name = 'Ruby / Minitest' self.slug = 'minitest' - self.release = '5.17.0' + self.release = '5.20.0' self.links = { code: 'https://github.com/minitest/minitest' } diff --git a/lib/docs/scrapers/rdoc/rails.rb b/lib/docs/scrapers/rdoc/rails.rb index 68381b7984..03c9e4f2d5 100644 --- a/lib/docs/scrapers/rdoc/rails.rb +++ b/lib/docs/scrapers/rdoc/rails.rb @@ -75,8 +75,12 @@ class Rails < Rdoc end end + version '7.1' do + self.release = '7.1.2' + end + version '7.0' do - self.release = '7.0.4' + self.release = '7.0.8' end version '6.1' do diff --git a/lib/docs/scrapers/rdoc/ruby.rb b/lib/docs/scrapers/rdoc/ruby.rb index 2850061a5f..eef26f4182 100644 --- a/lib/docs/scrapers/rdoc/ruby.rb +++ b/lib/docs/scrapers/rdoc/ruby.rb @@ -69,16 +69,20 @@ class Ruby < Rdoc Licensed under their own licenses. HTML + version '3.3' do + self.release = '3.3.0' + end + version '3.2' do - self.release = '3.2.0' + self.release = '3.2.2' end - + version '3.1' do - self.release = '3.1.3' + self.release = '3.1.4' end version '3' do - self.release = '3.0.0' + self.release = '3.0.6' end version '2.7' do From 4862e15775c45cc7a63fa8d1f048e0952405bdce Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Fri, 5 Jan 2024 17:54:31 +0100 Subject: [PATCH 0104/1110] Update Python documentation (3.12.1) --- lib/docs/scrapers/python.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/docs/scrapers/python.rb b/lib/docs/scrapers/python.rb index 52b2505fd6..3988bba3b0 100644 --- a/lib/docs/scrapers/python.rb +++ b/lib/docs/scrapers/python.rb @@ -28,14 +28,14 @@ class Python < FileScraper HTML version '3.12' do - self.release = '3.12.0' + self.release = '3.12.1' self.base_url = "https://docs.python.org/#{self.version}/" html_filters.push 'python/entries_v3', 'sphinx/clean_html', 'python/clean_html' end version '3.11' do - self.release = '3.11.5' + self.release = '3.11.7' self.base_url = "https://docs.python.org/#{self.version}/" html_filters.push 'python/entries_v3', 'sphinx/clean_html', 'python/clean_html' From 9d0d3ec933ae2cdca8165b9f259215ba7d01b083 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Fri, 5 Jan 2024 19:33:59 +0100 Subject: [PATCH 0105/1110] Update Go documentation (1.21.5) --- lib/docs/scrapers/go.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/docs/scrapers/go.rb b/lib/docs/scrapers/go.rb index d811f3c444..c94a5455fb 100644 --- a/lib/docs/scrapers/go.rb +++ b/lib/docs/scrapers/go.rb @@ -1,7 +1,7 @@ module Docs class Go < UrlScraper self.type = 'go' - self.release = '1.21.0' + self.release = '1.21.5' self.base_url = 'https://golang.org/pkg/' self.links = { home: 'https://golang.org/', @@ -10,10 +10,10 @@ class Go < UrlScraper # Run godoc locally, since https://golang.org/pkg/ redirects to https://pkg.go.dev/std with rate limiting / scraping protection. - # docker run --expose=6060 --rm -it docker.io/golang:1.18.0 - #docker# go install golang.org/x/tools/cmd/godoc@latest - #docker# rm -r /usr/local/go/test/ - #docker# godoc -http 0.0.0.0:6060 -v + # podman run --net host --rm -it docker.io/golang:1.21.5 + #podman# go install golang.org/x/tools/cmd/godoc@latest + #podman# rm -r /usr/local/go/test/ + #podman# godoc -http 0.0.0.0:6060 -v self.base_url = 'http://localhost:6060/pkg/' html_filters.push 'clean_local_urls' From dbf59d21382e92b014b9e42802d65804ea0385da Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Fri, 5 Jan 2024 20:09:49 +0100 Subject: [PATCH 0106/1110] file-scrapers: update rails --- docs/file-scrapers.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/file-scrapers.md b/docs/file-scrapers.md index 8c2741dae9..adf084fc14 100644 --- a/docs/file-scrapers.md +++ b/docs/file-scrapers.md @@ -242,11 +242,12 @@ done ### Ruby / Minitest ### Ruby on Rails * Download a release at https://github.com/rails/rails/releases or clone https://github.com/rails/rails.git (checkout to the branch of the rails' version that is going to be scraped) -* Open "railties/lib/rails/api/task.rb" and comment out any code related to sdoc ("configure_sdoc") -* Run "bundle install --without db && bundle exec rake rdoc" (in the Rails directory) -* Run "cd guides && bundle exec rake guides:generate:html" -* Copy the "guides/output" directory to "html/guides" -* Copy the "html" directory to "docs/rails~[version]" +* Open `railties/lib/rails/api/task.rb` and comment out any code related to sdoc (`configure_sdoc`) +* Run `bundle config set --local without 'db job'` (in the Rails directory) +* Run `bundle install && bundle exec rake rdoc` (in the Rails directory) +* Run `cd guides && bundle exec rake guides:generate:html` +* Copy the `guides/output` directory to `html/guides` +* Copy the `html` directory to `docs/rails~[version]` ### Ruby Download the tarball of Ruby from https://www.ruby-lang.org/en/downloads/, extract it, run From c9dd0ed106920f05e78cec3745d580ffdbc8382e Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Fri, 5 Jan 2024 20:10:29 +0100 Subject: [PATCH 0107/1110] rails: fix get_name --- lib/docs/filters/rails/entries.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/docs/filters/rails/entries.rb b/lib/docs/filters/rails/entries.rb index 0b29a4afbb..343f85952f 100644 --- a/lib/docs/filters/rails/entries.rb +++ b/lib/docs/filters/rails/entries.rb @@ -40,7 +40,7 @@ class EntriesFilter < Docs::Rdoc::EntriesFilter def get_name if slug.start_with?('guides') - name = at_css('#feature h2').content.strip + name = at_css('#mainCol h2').content.strip name.remove! %r{\s\(.+\)\z} return name end From 0bb3f3779f1d8f6d2ddd8e88c6a0172d21034bb5 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Fri, 5 Jan 2024 20:15:27 +0100 Subject: [PATCH 0108/1110] bazel: CSS styling for article > h1 --- assets/stylesheets/components/_page.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/assets/stylesheets/components/_page.scss b/assets/stylesheets/components/_page.scss index e3c735dcc0..900ed6050e 100644 --- a/assets/stylesheets/components/_page.scss +++ b/assets/stylesheets/components/_page.scss @@ -9,11 +9,13 @@ &._page-error { position: static; } > h1, + > article > h1, > header > h1, > section > h1 { @extend ._lined-heading; } > h1:first-child, + > article:first-of-type > h1, > header:first-of-type > h1, > section:first-of-type > h1 { margin-top: 0; From 0e10f2462467ca91c33cb7d3e3bf93b70ef2f45f Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Fri, 5 Jan 2024 20:17:05 +0100 Subject: [PATCH 0109/1110] bazel: add news entry --- assets/javascripts/news.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/assets/javascripts/news.json b/assets/javascripts/news.json index dfe15ffcb8..a0a009fae2 100644 --- a/assets/javascripts/news.json +++ b/assets/javascripts/news.json @@ -1,4 +1,8 @@ [ + [ + "2024-01-05", + "New documentation: Bazel" + ], [ "2023-10-09", "New documentations: hapi, joi, Nushell, Varnish" From 8697de85672a5bf572cb7b897edbee57d60b585d Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Fri, 5 Jan 2024 20:30:27 +0100 Subject: [PATCH 0110/1110] thor updates: fix Terminal::Table from terminal-table The newest Thor also contains a Terminal module, causing "uninitialized constant Thor::Shell::Terminal::Table (NameError)" --- lib/tasks/updates.thor | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/tasks/updates.thor b/lib/tasks/updates.thor index b3fb28bb9e..48486b9f20 100644 --- a/lib/tasks/updates.thor +++ b/lib/tasks/updates.thor @@ -123,7 +123,7 @@ class UpdatesCLI < Thor headings = ['Documentation', 'Scraper version', 'Latest version'] rows = results.map {|result| [result[:name], result[:scraper_version], result[:latest_version]]} - table = Terminal::Table.new :title => title, :headings => headings, :rows => rows + table = ::Terminal::Table.new :title => title, :headings => headings, :rows => rows puts table end @@ -132,7 +132,7 @@ class UpdatesCLI < Thor headings = %w(Documentation Reason) rows = results.map {|result| [result[:name], result[:error]]} - table = Terminal::Table.new :title => title, :headings => headings, :rows => rows + table = ::Terminal::Table.new :title => title, :headings => headings, :rows => rows puts table end From 6b1bbd6e9f44512bbdf7c89350919707a00d3dd9 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Fri, 5 Jan 2024 20:51:06 +0100 Subject: [PATCH 0111/1110] Update SQLite documentation (3.44.2) --- lib/docs/filters/sqlite/clean_html.rb | 1 + lib/docs/scrapers/sqlite.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/docs/filters/sqlite/clean_html.rb b/lib/docs/filters/sqlite/clean_html.rb index 2f7fb46c89..ae74228431 100644 --- a/lib/docs/filters/sqlite/clean_html.rb +++ b/lib/docs/filters/sqlite/clean_html.rb @@ -88,6 +88,7 @@ def call end css('svg *[style], svg *[fill]').each do |node| + next if slug == 'geopoly' # transform style in SVG diagrams, e.g. on https://sqlite.org/lang_insert.html if node['style'] == 'fill:rgb(0,0,0)' or node['fill'] == 'rgb(0,0,0)' node.add_class('fill') diff --git a/lib/docs/scrapers/sqlite.rb b/lib/docs/scrapers/sqlite.rb index 92521b1ae1..062d907c0c 100644 --- a/lib/docs/scrapers/sqlite.rb +++ b/lib/docs/scrapers/sqlite.rb @@ -2,7 +2,7 @@ module Docs class Sqlite < UrlScraper self.name = 'SQLite' self.type = 'sqlite' - self.release = '3.43.0' + self.release = '3.44.2' self.base_url = 'https://sqlite.org/' self.root_path = 'docs.html' self.initial_paths = %w(keyword_index.html) From 667a75467af79ed924b9c6587d67b001e9e5e043 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Fri, 5 Jan 2024 21:01:56 +0100 Subject: [PATCH 0112/1110] Update Vue documentation (3.4.5) --- lib/docs/scrapers/vue.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/docs/scrapers/vue.rb b/lib/docs/scrapers/vue.rb index 53f0723ac4..7fac3ce64e 100644 --- a/lib/docs/scrapers/vue.rb +++ b/lib/docs/scrapers/vue.rb @@ -19,7 +19,7 @@ class Vue < UrlScraper HTML version '3' do - self.release = '3.3.4' + self.release = '3.4.5' self.base_url = 'https://vuejs.org/' self.initial_paths = %w(guide/introduction.html) html_filters.push 'vue/entries_v3', 'vue/clean_html' From 88544ab3023cb65b5f0b00b041e774377992c587 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Fri, 5 Jan 2024 21:10:36 +0100 Subject: [PATCH 0113/1110] Update PHP documentation (8.3) --- docs/file-scrapers.md | 3 +-- lib/docs/scrapers/php.rb | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/file-scrapers.md b/docs/file-scrapers.md index adf084fc14..621a66c00b 100644 --- a/docs/file-scrapers.md +++ b/docs/file-scrapers.md @@ -193,8 +193,7 @@ Click the link under the "Many HTML files" column on https://www.php.net/downloa Or run the following commands in your terminal: ```sh -curl https://www.php.net/distributions/manual/php_manual_en.tar.gz > php.tar; \ -tar -xf php.tar; mv php-chunked-xhtml/ docs/php/ +curl https://www.php.net/distributions/manual/php_manual_en.tar.gz | tar xz; mv php-chunked-xhtml/ docs/php/ ``` ## Python 3.6+ diff --git a/lib/docs/scrapers/php.rb b/lib/docs/scrapers/php.rb index 5c02d6c5f2..6d38c70c22 100644 --- a/lib/docs/scrapers/php.rb +++ b/lib/docs/scrapers/php.rb @@ -5,7 +5,7 @@ class Php < FileScraper self.name = 'PHP' self.type = 'php' - self.release = '8.2' + self.release = '8.3' self.base_url = 'https://www.php.net/manual/en/' self.root_path = 'index.html' self.initial_paths = %w( @@ -62,7 +62,7 @@ class Php < FileScraper options[:skip_patterns] = [/mysqlnd/, /xdevapi/i] options[:attribution] = <<-HTML - © 1997–2022 The PHP Documentation Group
+ © 1997–2023 The PHP Documentation Group
Licensed under the Creative Commons Attribution License v3.0 or later. HTML From a80293ae9878723e6a8f9fbf30e58384348ff49e Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Fri, 5 Jan 2024 21:46:02 +0100 Subject: [PATCH 0114/1110] Update OpenJDK documentation (21) --- docs/file-scrapers.md | 10 +++++----- lib/docs/scrapers/openjdk.rb | 18 ++++-------------- 2 files changed, 9 insertions(+), 19 deletions(-) diff --git a/docs/file-scrapers.md b/docs/file-scrapers.md index 621a66c00b..478099b4c2 100644 --- a/docs/file-scrapers.md +++ b/docs/file-scrapers.md @@ -166,11 +166,11 @@ Search 'Openjdk' in https://www.debian.org/distrib/packages, find the `openjdk-$ download it, extract it with `dpkg -x $PACKAGE ./` and move `./usr/share/doc/openjdk-16-jre-headless/api/` to `path/to/devdocs/docs/openjdk~$VERSION` -``` -curl http://ftp.at.debian.org/debian/pool/main/o/openjdk-19/openjdk-19-doc_19+36-2_all.deb && -tar xf openjdk-19-doc_19+36-2_all.deb +```sh +curl -O http://ftp.at.debian.org/debian/pool/main/o/openjdk-21/openjdk-21-doc_21.0.1+12-3_all.deb +tar xf openjdk-21-doc_21.0.1+12-3_all.deb tar xf data.tar.xz -mv ./usr/share/doc/openjdk-19-jre-headless/api/ path/to/devdocs/docs/openjdk~$VERSION +mv ./usr/share/doc/openjdk-21-jre-headless/api/ docs/openjdk~$VERSION ``` If you use or have access to a Debian-based GNU/Linux distribution you can run the following command: @@ -178,7 +178,7 @@ If you use or have access to a Debian-based GNU/Linux distribution you can run t apt download openjdk-$VERSION-doc dpkg -x $PACKAGE ./ # previous command makes a directory called 'usr' in the current directory -mv ./usr/share/doc/openjdk-16-jre-headless/api/ path/to/devdocs/docs/openjdk~$VERSION +mv ./usr/share/doc/openjdk-16-jre-headless/api/ docs/openjdk~$VERSION ``` ## Pandas diff --git a/lib/docs/scrapers/openjdk.rb b/lib/docs/scrapers/openjdk.rb index 2ede8d5dda..d96f96617b 100644 --- a/lib/docs/scrapers/openjdk.rb +++ b/lib/docs/scrapers/openjdk.rb @@ -28,7 +28,7 @@ class Openjdk < FileScraper ] options[:attribution] = <<-HTML - © 1993, 2022, Oracle and/or its affiliates. All rights reserved.
+ © 1993, 2023, Oracle and/or its affiliates. All rights reserved.
Documentation extracted from Debian's OpenJDK Development Kit package.
Licensed under the GNU General Public License, version 2, with the Classpath Exception.
Various third party code in OpenJDK is licensed under different licenses (see Debian package).
@@ -37,20 +37,10 @@ class Openjdk < FileScraper NEWFILTERS = ['openjdk/entries_new', 'openjdk/clean_html_new'] - version '19' do - self.release = '19' + version '21' do + self.release = '21' self.root_path = 'index.html' - self.base_url = 'https://docs.oracle.com/en/java/javase/19/docs/api/' - - html_filters.push NEWFILTERS - - options[:container] = 'main' - end - - version '18' do - self.release = '18' - self.root_path = 'index.html' - self.base_url = 'https://docs.oracle.com/en/java/javase/18/docs/api/' + self.base_url = 'https://docs.oracle.com/en/java/javase/21/docs/api/' html_filters.push NEWFILTERS From fd086dd55fca58e7a3e3de05af85de3594ffba67 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Sat, 6 Jan 2024 09:39:05 +0100 Subject: [PATCH 0115/1110] schedule-doc-report: add workflow_dispatch https://docs.github.com/en/actions/using-workflows/manually-running-a-workflow --- .github/workflows/schedule-doc-report.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/schedule-doc-report.yml b/.github/workflows/schedule-doc-report.yml index 005666405e..386683d70e 100644 --- a/.github/workflows/schedule-doc-report.yml +++ b/.github/workflows/schedule-doc-report.yml @@ -2,6 +2,7 @@ name: Generate documentation versions report on: schedule: - cron: '17 4 1 * *' + workflow_dispatch: jobs: report: From a8e7fb78d8d3591a2d6b55596500a48f6ad55e45 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Sat, 6 Jan 2024 09:45:39 +0100 Subject: [PATCH 0116/1110] Update Axios documentation (1.6.5) --- lib/docs/scrapers/axios.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/docs/scrapers/axios.rb b/lib/docs/scrapers/axios.rb index 13d7802873..0a78e940a5 100755 --- a/lib/docs/scrapers/axios.rb +++ b/lib/docs/scrapers/axios.rb @@ -5,7 +5,7 @@ class Axios < UrlScraper home: 'hthttps://axios-http.com/', code: 'https://github.com/axios/axios' } - self.release = '1.4.0' + self.release = '1.6.5' self.base_url = "https://axios-http.com/docs/" self.initial_paths = %w(index intro) From 880202d6a60a6baa1022fdd36b697648b5d7e266 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Sat, 6 Jan 2024 09:57:01 +0100 Subject: [PATCH 0117/1110] Update Crystal documentation (1.10.1) --- lib/docs/scrapers/crystal.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/docs/scrapers/crystal.rb b/lib/docs/scrapers/crystal.rb index 5c4a782e79..fefe62d66e 100644 --- a/lib/docs/scrapers/crystal.rb +++ b/lib/docs/scrapers/crystal.rb @@ -2,7 +2,7 @@ module Docs class Crystal < UrlScraper include MultipleBaseUrls self.type = 'crystal' - self.release = '1.9.2' + self.release = '1.10.1' self.base_urls = [ "https://crystal-lang.org/api/#{release}/", "https://crystal-lang.org/reference/#{release[0..2]}/", From 85798a8d420f4185cc5953fcefc3d5ec216204ad Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Sat, 6 Jan 2024 09:57:04 +0100 Subject: [PATCH 0118/1110] Update Vite documentation (5.0.11) --- lib/docs/filters/vite/entries.rb | 2 +- lib/docs/scrapers/vite.rb | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/docs/filters/vite/entries.rb b/lib/docs/filters/vite/entries.rb index db28ea7c40..606a19bb17 100644 --- a/lib/docs/filters/vite/entries.rb +++ b/lib/docs/filters/vite/entries.rb @@ -8,7 +8,7 @@ def get_name end def get_type - at_css('aside nav .is-active').content.strip + name end def additional_entries diff --git a/lib/docs/scrapers/vite.rb b/lib/docs/scrapers/vite.rb index ff82da017b..46309fa742 100644 --- a/lib/docs/scrapers/vite.rb +++ b/lib/docs/scrapers/vite.rb @@ -15,16 +15,22 @@ class Vite < UrlScraper Licensed under the MIT License. HTML - options[:skip] = %w(team.html plugins/) + options[:skip] = %w(team.html team) + options[:skip_patterns] = [/\Ablog/, /\Aplugins/] self.initial_paths = %w(guide/) html_filters.push 'vite/entries', 'vite/clean_html' version do - self.release = '4.4.8' + self.release = '5.0.11' self.base_url = 'https://vitejs.dev/' end + version '4' do + self.release = '4.5.1' + self.base_url = 'https://v4.vitejs.dev/' + end + version '3' do self.release = '3.2.5' self.base_url = 'https://v3.vitejs.dev/' From 263c8a57f685822ffd3786a3794446e1bf663d86 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Sat, 6 Jan 2024 09:58:27 +0100 Subject: [PATCH 0119/1110] Update Fish documentation (3.7.0) --- lib/docs/scrapers/fish.rb | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/docs/scrapers/fish.rb b/lib/docs/scrapers/fish.rb index bf2f09dbf8..8e9c3326e0 100644 --- a/lib/docs/scrapers/fish.rb +++ b/lib/docs/scrapers/fish.rb @@ -16,6 +16,14 @@ class Fish < UrlScraper Licensed under the GNU General Public License, version 2. HTML + version '3.7' do + self.release = '3.7.0' + self.base_url = "https://fishshell.com/docs/#{version}/" + + options[:skip].concat %w(genindex.html relnotes.html) + html_filters.push 'sphinx/clean_html', 'fish/clean_html_sphinx', 'fish/entries_sphinx' + end + version '3.6' do self.release = '3.6.0' self.base_url = "https://fishshell.com/docs/#{version}/" From 9d538b4aace797fa782efccf0bbd2109261179a6 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Sat, 6 Jan 2024 10:01:05 +0100 Subject: [PATCH 0120/1110] Update Julia documentation (1.10.0.2) --- lib/docs/scrapers/julia.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/docs/scrapers/julia.rb b/lib/docs/scrapers/julia.rb index 97a5dc579c..c31cdaea90 100644 --- a/lib/docs/scrapers/julia.rb +++ b/lib/docs/scrapers/julia.rb @@ -11,6 +11,17 @@ class Julia < UrlScraper Licensed under the MIT License. HTML + version '1.10' do + self.release = '1.10.0.2' + self.base_url = "https://docs.julialang.org/en/v#{version}/" + self.type = 'julia' + + html_filters.push 'julia/entries', 'julia/clean_html' + + options[:container] = '.docs-main' + options[:only_patterns] = [/\Amanual\//, /\Abase\//, /\Astdlib\//] + end + version '1.9' do self.release = '1.9.2' self.base_url = "https://docs.julialang.org/en/v#{version}/" From 9bf2311eced479ea1d0b8e3d4ff6dfe17a445ebe Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Sat, 6 Jan 2024 10:16:37 +0100 Subject: [PATCH 0121/1110] Update Angular documentation (17.0.8) --- lib/docs/filters/angular/clean_html.rb | 5 +++++ lib/docs/scrapers/angular.rb | 10 ++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/docs/filters/angular/clean_html.rb b/lib/docs/filters/angular/clean_html.rb index 751127105b..562b6d259e 100644 --- a/lib/docs/filters/angular/clean_html.rb +++ b/lib/docs/filters/angular/clean_html.rb @@ -94,6 +94,11 @@ def call node.before(node.children).remove end + css('details.overloads > summary').each do |node| + node.css('.actions').remove + node.content = node.content + end + doc end end diff --git a/lib/docs/scrapers/angular.rb b/lib/docs/scrapers/angular.rb index 0a85b9c845..dcbdd0ea70 100644 --- a/lib/docs/scrapers/angular.rb +++ b/lib/docs/scrapers/angular.rb @@ -83,14 +83,20 @@ def url_for(path) end version do - self.release = '16.1.3' + self.release = '17.0.8' self.base_url = 'https://angular.io/' include Docs::Angular::Since12 end + version '16' do + self.release = '16.2.12' + self.base_url = 'https://v16.angular.io/' + include Docs::Angular::Since12 + end + version '15' do self.release = '15.2.9' - self.base_url = 'https://angular.io/' + self.base_url = 'https://v15.angular.io/' include Docs::Angular::Since12 end From 43ddae5560e491dd4ba3922a58f8ae03174b9824 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Sat, 6 Jan 2024 10:25:52 +0100 Subject: [PATCH 0122/1110] Update esbuild documentation (0.19.11) --- lib/docs/scrapers/esbuild.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/docs/scrapers/esbuild.rb b/lib/docs/scrapers/esbuild.rb index 9250b4fa70..e0b6c861aa 100644 --- a/lib/docs/scrapers/esbuild.rb +++ b/lib/docs/scrapers/esbuild.rb @@ -16,7 +16,7 @@ class Esbuild < UrlScraper Licensed under the MIT License. HTML - self.release = '0.18.17' + self.release = '0.19.11' self.base_url = 'https://esbuild.github.io/' html_filters.push 'esbuild/clean_html', 'esbuild/entries' From 32443faf2828b4ace5cb14a8af3731ee8bbf324c Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Sat, 6 Jan 2024 10:26:23 +0100 Subject: [PATCH 0123/1110] Update Vitest documentation (1.1.3) --- lib/docs/scrapers/vitest.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/docs/scrapers/vitest.rb b/lib/docs/scrapers/vitest.rb index 199e9c9af8..820b9eef80 100644 --- a/lib/docs/scrapers/vitest.rb +++ b/lib/docs/scrapers/vitest.rb @@ -16,7 +16,7 @@ class Vitest < UrlScraper Licensed under the MIT License. HTML - self.release = '0.32.4' + self.release = '1.1.3' self.base_url = 'https://vitest.dev/' self.initial_paths = %w(guide/) html_filters.push 'vitest/entries', 'vite/clean_html' From 93cc8b3978911b60091d339a3825c72295813260 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Sat, 6 Jan 2024 10:42:10 +0100 Subject: [PATCH 0124/1110] Update Rust documentation (1.75.0) --- lib/docs/scrapers/rust.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/docs/scrapers/rust.rb b/lib/docs/scrapers/rust.rb index 5c2b1f10c9..536aaefd9f 100644 --- a/lib/docs/scrapers/rust.rb +++ b/lib/docs/scrapers/rust.rb @@ -3,7 +3,7 @@ module Docs class Rust < UrlScraper self.type = 'rust' - self.release = '1.73.0' + self.release = '1.75.0' self.base_url = 'https://doc.rust-lang.org/' self.root_path = 'book/index.html' self.initial_paths = %w( From bf89445d87b720d2b35a358f483d5210957d12e0 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Sat, 6 Jan 2024 11:03:52 +0100 Subject: [PATCH 0125/1110] Update npm documentation (10.2.5) --- lib/docs/filters/npm/clean_html.rb | 16 +++++----------- lib/docs/filters/npm/entries.rb | 3 ++- lib/docs/scrapers/npm.rb | 7 +++++-- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/lib/docs/filters/npm/clean_html.rb b/lib/docs/filters/npm/clean_html.rb index ad07c476a2..b951976805 100644 --- a/lib/docs/filters/npm/clean_html.rb +++ b/lib/docs/filters/npm/clean_html.rb @@ -2,18 +2,13 @@ module Docs class Npm class CleanHtmlFilter < Filter def call - - at_css('#___gatsby').before(at_css('h1')) + @doc = at_css('main') css('details').remove - - css('.dZYhXG', '.fONtKn').remove - - css('.kSYjyK').remove - - css('.cDvIaH').remove - - css('.jRndWL').remove_attribute('style') + css('nav[aria-label="Breadcrumbs"]').remove + css('.gtWOdv').remove # Select CLI Version + css('.ezMiXD').remove # Navbox + css('.gOhcvK').remove # Edit this page on GitHub css('pre').each do |node| node.content = node.css('.token-line').map(&:content).join("\n") @@ -21,7 +16,6 @@ def call end doc - end end end diff --git a/lib/docs/filters/npm/entries.rb b/lib/docs/filters/npm/entries.rb index 8291d7a71c..d754b1d4d2 100644 --- a/lib/docs/filters/npm/entries.rb +++ b/lib/docs/filters/npm/entries.rb @@ -7,7 +7,7 @@ def get_name end def get_type - at_css('.active').content + at_css('nav[aria-label="Breadcrumbs"] li').content end def additional_entries @@ -21,6 +21,7 @@ def additional_entries if name == 'package.json' css('h3').each do |node| + next unless node['id'] entries << [node['id'], slug + '#' + node['id'], 'Package.json Settings'] end end diff --git a/lib/docs/scrapers/npm.rb b/lib/docs/scrapers/npm.rb index ce2be86b2a..1cb2c6e2e4 100644 --- a/lib/docs/scrapers/npm.rb +++ b/lib/docs/scrapers/npm.rb @@ -2,7 +2,7 @@ module Docs class Npm < UrlScraper self.name = 'npm' self.type = 'npm' - self.release = '8.3.0' + self.release = '10.2.5' self.base_url = 'https://docs.npmjs.com/' self.force_gzip = true self.links = { @@ -12,7 +12,8 @@ class Npm < UrlScraper html_filters.push 'npm/entries', 'npm/clean_html' - options[:max_image_size] = 130_000 + options[:download_images] = false + # options[:max_image_size] = 130_000 options[:skip] = [ 'all', @@ -31,6 +32,8 @@ class Npm < UrlScraper /\Apolicies/, /cli\/v6/, /cli\/v7/, + /cli\/v8/, + /cli\/v9/, /\/\Z/ # avoid pages with a trailing slash, those pages mess up the entries ] From 6cc430ffc41ed1eaee031858059cb5ec359da939 Mon Sep 17 00:00:00 2001 From: decaffeinate Date: Sat, 6 Jan 2024 11:46:56 +0100 Subject: [PATCH 0126/1110] decaffeinate: Rename app.coffee and 75 other files from .coffee to .js --- assets/javascripts/app/{app.coffee => app.js} | 0 assets/javascripts/app/{db.coffee => db.js} | 0 assets/javascripts/app/{router.coffee => router.js} | 0 assets/javascripts/app/{searcher.coffee => searcher.js} | 0 assets/javascripts/app/{serviceworker.coffee => serviceworker.js} | 0 assets/javascripts/app/{settings.coffee => settings.js} | 0 assets/javascripts/app/{shortcuts.coffee => shortcuts.js} | 0 .../javascripts/app/{update_checker.coffee => update_checker.js} | 0 assets/javascripts/{application.js.coffee => application.js.js} | 0 .../javascripts/collections/{collection.coffee => collection.js} | 0 assets/javascripts/collections/{docs.coffee => docs.js} | 0 assets/javascripts/collections/{entries.coffee => entries.js} | 0 assets/javascripts/collections/{types.coffee => types.js} | 0 assets/javascripts/{debug.js.coffee => debug.js.js} | 0 assets/javascripts/lib/{ajax.coffee => ajax.js} | 0 assets/javascripts/lib/{cookies_store.coffee => cookies_store.js} | 0 assets/javascripts/lib/{events.coffee => events.js} | 0 assets/javascripts/lib/{favicon.coffee => favicon.js} | 0 assets/javascripts/lib/{license.coffee => license.js} | 0 .../lib/{local_storage_store.coffee => local_storage_store.js} | 0 assets/javascripts/lib/{page.coffee => page.js} | 0 assets/javascripts/lib/{util.coffee => util.js} | 0 assets/javascripts/models/{doc.coffee => doc.js} | 0 assets/javascripts/models/{entry.coffee => entry.js} | 0 assets/javascripts/models/{model.coffee => model.js} | 0 assets/javascripts/models/{type.coffee => type.js} | 0 assets/javascripts/templates/{base.coffee => base.js} | 0 assets/javascripts/templates/{error_tmpl.coffee => error_tmpl.js} | 0 .../javascripts/templates/{notice_tmpl.coffee => notice_tmpl.js} | 0 assets/javascripts/templates/{notif_tmpl.coffee => notif_tmpl.js} | 0 .../templates/pages/{about_tmpl.coffee => about_tmpl.js} | 0 .../templates/pages/{help_tmpl.coffee => help_tmpl.js} | 0 .../templates/pages/{offline_tmpl.coffee => offline_tmpl.js} | 0 .../templates/pages/{settings_tmpl.coffee => settings_tmpl.js} | 0 .../templates/pages/{type_tmpl.coffee => type_tmpl.js} | 0 assets/javascripts/templates/{path_tmpl.coffee => path_tmpl.js} | 0 .../templates/{sidebar_tmpl.coffee => sidebar_tmpl.js} | 0 assets/javascripts/templates/{tip_tmpl.coffee => tip_tmpl.js} | 0 assets/javascripts/views/content/{content.coffee => content.js} | 0 .../views/content/{entry_page.coffee => entry_page.js} | 0 .../views/content/{offline_page.coffee => offline_page.js} | 0 .../javascripts/views/content/{root_page.coffee => root_page.js} | 0 .../views/content/{settings_page.coffee => settings_page.js} | 0 .../views/content/{static_page.coffee => static_page.js} | 0 .../javascripts/views/content/{type_page.coffee => type_page.js} | 0 assets/javascripts/views/layout/{document.coffee => document.js} | 0 assets/javascripts/views/layout/{menu.coffee => menu.js} | 0 assets/javascripts/views/layout/{mobile.coffee => mobile.js} | 0 assets/javascripts/views/layout/{path.coffee => path.js} | 0 assets/javascripts/views/layout/{resizer.coffee => resizer.js} | 0 assets/javascripts/views/layout/{settings.coffee => settings.js} | 0 .../javascripts/views/list/{list_focus.coffee => list_focus.js} | 0 assets/javascripts/views/list/{list_fold.coffee => list_fold.js} | 0 .../javascripts/views/list/{list_select.coffee => list_select.js} | 0 .../views/list/{paginated_list.coffee => paginated_list.js} | 0 assets/javascripts/views/misc/{news.coffee => news.js} | 0 assets/javascripts/views/misc/{notice.coffee => notice.js} | 0 assets/javascripts/views/misc/{notif.coffee => notif.js} | 0 assets/javascripts/views/misc/{tip.coffee => tip.js} | 0 assets/javascripts/views/misc/{updates.coffee => updates.js} | 0 assets/javascripts/views/pages/{base.coffee => base.js} | 0 assets/javascripts/views/pages/{hidden.coffee => hidden.js} | 0 assets/javascripts/views/pages/{jquery.coffee => jquery.js} | 0 assets/javascripts/views/pages/{rdoc.coffee => rdoc.js} | 0 assets/javascripts/views/pages/{sqlite.coffee => sqlite.js} | 0 .../views/pages/{support_tables.coffee => support_tables.js} | 0 assets/javascripts/views/search/{search.coffee => search.js} | 0 .../views/search/{search_scope.coffee => search_scope.js} | 0 assets/javascripts/views/sidebar/{doc_list.coffee => doc_list.js} | 0 .../views/sidebar/{doc_picker.coffee => doc_picker.js} | 0 .../views/sidebar/{entry_list.coffee => entry_list.js} | 0 assets/javascripts/views/sidebar/{results.coffee => results.js} | 0 assets/javascripts/views/sidebar/{sidebar.coffee => sidebar.js} | 0 .../views/sidebar/{sidebar_hover.coffee => sidebar_hover.js} | 0 .../javascripts/views/sidebar/{type_list.coffee => type_list.js} | 0 assets/javascripts/views/{view.coffee => view.js} | 0 76 files changed, 0 insertions(+), 0 deletions(-) rename assets/javascripts/app/{app.coffee => app.js} (100%) rename assets/javascripts/app/{db.coffee => db.js} (100%) rename assets/javascripts/app/{router.coffee => router.js} (100%) rename assets/javascripts/app/{searcher.coffee => searcher.js} (100%) rename assets/javascripts/app/{serviceworker.coffee => serviceworker.js} (100%) rename assets/javascripts/app/{settings.coffee => settings.js} (100%) rename assets/javascripts/app/{shortcuts.coffee => shortcuts.js} (100%) rename assets/javascripts/app/{update_checker.coffee => update_checker.js} (100%) rename assets/javascripts/{application.js.coffee => application.js.js} (100%) rename assets/javascripts/collections/{collection.coffee => collection.js} (100%) rename assets/javascripts/collections/{docs.coffee => docs.js} (100%) rename assets/javascripts/collections/{entries.coffee => entries.js} (100%) rename assets/javascripts/collections/{types.coffee => types.js} (100%) rename assets/javascripts/{debug.js.coffee => debug.js.js} (100%) rename assets/javascripts/lib/{ajax.coffee => ajax.js} (100%) rename assets/javascripts/lib/{cookies_store.coffee => cookies_store.js} (100%) rename assets/javascripts/lib/{events.coffee => events.js} (100%) rename assets/javascripts/lib/{favicon.coffee => favicon.js} (100%) rename assets/javascripts/lib/{license.coffee => license.js} (100%) rename assets/javascripts/lib/{local_storage_store.coffee => local_storage_store.js} (100%) rename assets/javascripts/lib/{page.coffee => page.js} (100%) rename assets/javascripts/lib/{util.coffee => util.js} (100%) rename assets/javascripts/models/{doc.coffee => doc.js} (100%) rename assets/javascripts/models/{entry.coffee => entry.js} (100%) rename assets/javascripts/models/{model.coffee => model.js} (100%) rename assets/javascripts/models/{type.coffee => type.js} (100%) rename assets/javascripts/templates/{base.coffee => base.js} (100%) rename assets/javascripts/templates/{error_tmpl.coffee => error_tmpl.js} (100%) rename assets/javascripts/templates/{notice_tmpl.coffee => notice_tmpl.js} (100%) rename assets/javascripts/templates/{notif_tmpl.coffee => notif_tmpl.js} (100%) rename assets/javascripts/templates/pages/{about_tmpl.coffee => about_tmpl.js} (100%) rename assets/javascripts/templates/pages/{help_tmpl.coffee => help_tmpl.js} (100%) rename assets/javascripts/templates/pages/{offline_tmpl.coffee => offline_tmpl.js} (100%) rename assets/javascripts/templates/pages/{settings_tmpl.coffee => settings_tmpl.js} (100%) rename assets/javascripts/templates/pages/{type_tmpl.coffee => type_tmpl.js} (100%) rename assets/javascripts/templates/{path_tmpl.coffee => path_tmpl.js} (100%) rename assets/javascripts/templates/{sidebar_tmpl.coffee => sidebar_tmpl.js} (100%) rename assets/javascripts/templates/{tip_tmpl.coffee => tip_tmpl.js} (100%) rename assets/javascripts/views/content/{content.coffee => content.js} (100%) rename assets/javascripts/views/content/{entry_page.coffee => entry_page.js} (100%) rename assets/javascripts/views/content/{offline_page.coffee => offline_page.js} (100%) rename assets/javascripts/views/content/{root_page.coffee => root_page.js} (100%) rename assets/javascripts/views/content/{settings_page.coffee => settings_page.js} (100%) rename assets/javascripts/views/content/{static_page.coffee => static_page.js} (100%) rename assets/javascripts/views/content/{type_page.coffee => type_page.js} (100%) rename assets/javascripts/views/layout/{document.coffee => document.js} (100%) rename assets/javascripts/views/layout/{menu.coffee => menu.js} (100%) rename assets/javascripts/views/layout/{mobile.coffee => mobile.js} (100%) rename assets/javascripts/views/layout/{path.coffee => path.js} (100%) rename assets/javascripts/views/layout/{resizer.coffee => resizer.js} (100%) rename assets/javascripts/views/layout/{settings.coffee => settings.js} (100%) rename assets/javascripts/views/list/{list_focus.coffee => list_focus.js} (100%) rename assets/javascripts/views/list/{list_fold.coffee => list_fold.js} (100%) rename assets/javascripts/views/list/{list_select.coffee => list_select.js} (100%) rename assets/javascripts/views/list/{paginated_list.coffee => paginated_list.js} (100%) rename assets/javascripts/views/misc/{news.coffee => news.js} (100%) rename assets/javascripts/views/misc/{notice.coffee => notice.js} (100%) rename assets/javascripts/views/misc/{notif.coffee => notif.js} (100%) rename assets/javascripts/views/misc/{tip.coffee => tip.js} (100%) rename assets/javascripts/views/misc/{updates.coffee => updates.js} (100%) rename assets/javascripts/views/pages/{base.coffee => base.js} (100%) rename assets/javascripts/views/pages/{hidden.coffee => hidden.js} (100%) rename assets/javascripts/views/pages/{jquery.coffee => jquery.js} (100%) rename assets/javascripts/views/pages/{rdoc.coffee => rdoc.js} (100%) rename assets/javascripts/views/pages/{sqlite.coffee => sqlite.js} (100%) rename assets/javascripts/views/pages/{support_tables.coffee => support_tables.js} (100%) rename assets/javascripts/views/search/{search.coffee => search.js} (100%) rename assets/javascripts/views/search/{search_scope.coffee => search_scope.js} (100%) rename assets/javascripts/views/sidebar/{doc_list.coffee => doc_list.js} (100%) rename assets/javascripts/views/sidebar/{doc_picker.coffee => doc_picker.js} (100%) rename assets/javascripts/views/sidebar/{entry_list.coffee => entry_list.js} (100%) rename assets/javascripts/views/sidebar/{results.coffee => results.js} (100%) rename assets/javascripts/views/sidebar/{sidebar.coffee => sidebar.js} (100%) rename assets/javascripts/views/sidebar/{sidebar_hover.coffee => sidebar_hover.js} (100%) rename assets/javascripts/views/sidebar/{type_list.coffee => type_list.js} (100%) rename assets/javascripts/views/{view.coffee => view.js} (100%) diff --git a/assets/javascripts/app/app.coffee b/assets/javascripts/app/app.js similarity index 100% rename from assets/javascripts/app/app.coffee rename to assets/javascripts/app/app.js diff --git a/assets/javascripts/app/db.coffee b/assets/javascripts/app/db.js similarity index 100% rename from assets/javascripts/app/db.coffee rename to assets/javascripts/app/db.js diff --git a/assets/javascripts/app/router.coffee b/assets/javascripts/app/router.js similarity index 100% rename from assets/javascripts/app/router.coffee rename to assets/javascripts/app/router.js diff --git a/assets/javascripts/app/searcher.coffee b/assets/javascripts/app/searcher.js similarity index 100% rename from assets/javascripts/app/searcher.coffee rename to assets/javascripts/app/searcher.js diff --git a/assets/javascripts/app/serviceworker.coffee b/assets/javascripts/app/serviceworker.js similarity index 100% rename from assets/javascripts/app/serviceworker.coffee rename to assets/javascripts/app/serviceworker.js diff --git a/assets/javascripts/app/settings.coffee b/assets/javascripts/app/settings.js similarity index 100% rename from assets/javascripts/app/settings.coffee rename to assets/javascripts/app/settings.js diff --git a/assets/javascripts/app/shortcuts.coffee b/assets/javascripts/app/shortcuts.js similarity index 100% rename from assets/javascripts/app/shortcuts.coffee rename to assets/javascripts/app/shortcuts.js diff --git a/assets/javascripts/app/update_checker.coffee b/assets/javascripts/app/update_checker.js similarity index 100% rename from assets/javascripts/app/update_checker.coffee rename to assets/javascripts/app/update_checker.js diff --git a/assets/javascripts/application.js.coffee b/assets/javascripts/application.js.js similarity index 100% rename from assets/javascripts/application.js.coffee rename to assets/javascripts/application.js.js diff --git a/assets/javascripts/collections/collection.coffee b/assets/javascripts/collections/collection.js similarity index 100% rename from assets/javascripts/collections/collection.coffee rename to assets/javascripts/collections/collection.js diff --git a/assets/javascripts/collections/docs.coffee b/assets/javascripts/collections/docs.js similarity index 100% rename from assets/javascripts/collections/docs.coffee rename to assets/javascripts/collections/docs.js diff --git a/assets/javascripts/collections/entries.coffee b/assets/javascripts/collections/entries.js similarity index 100% rename from assets/javascripts/collections/entries.coffee rename to assets/javascripts/collections/entries.js diff --git a/assets/javascripts/collections/types.coffee b/assets/javascripts/collections/types.js similarity index 100% rename from assets/javascripts/collections/types.coffee rename to assets/javascripts/collections/types.js diff --git a/assets/javascripts/debug.js.coffee b/assets/javascripts/debug.js.js similarity index 100% rename from assets/javascripts/debug.js.coffee rename to assets/javascripts/debug.js.js diff --git a/assets/javascripts/lib/ajax.coffee b/assets/javascripts/lib/ajax.js similarity index 100% rename from assets/javascripts/lib/ajax.coffee rename to assets/javascripts/lib/ajax.js diff --git a/assets/javascripts/lib/cookies_store.coffee b/assets/javascripts/lib/cookies_store.js similarity index 100% rename from assets/javascripts/lib/cookies_store.coffee rename to assets/javascripts/lib/cookies_store.js diff --git a/assets/javascripts/lib/events.coffee b/assets/javascripts/lib/events.js similarity index 100% rename from assets/javascripts/lib/events.coffee rename to assets/javascripts/lib/events.js diff --git a/assets/javascripts/lib/favicon.coffee b/assets/javascripts/lib/favicon.js similarity index 100% rename from assets/javascripts/lib/favicon.coffee rename to assets/javascripts/lib/favicon.js diff --git a/assets/javascripts/lib/license.coffee b/assets/javascripts/lib/license.js similarity index 100% rename from assets/javascripts/lib/license.coffee rename to assets/javascripts/lib/license.js diff --git a/assets/javascripts/lib/local_storage_store.coffee b/assets/javascripts/lib/local_storage_store.js similarity index 100% rename from assets/javascripts/lib/local_storage_store.coffee rename to assets/javascripts/lib/local_storage_store.js diff --git a/assets/javascripts/lib/page.coffee b/assets/javascripts/lib/page.js similarity index 100% rename from assets/javascripts/lib/page.coffee rename to assets/javascripts/lib/page.js diff --git a/assets/javascripts/lib/util.coffee b/assets/javascripts/lib/util.js similarity index 100% rename from assets/javascripts/lib/util.coffee rename to assets/javascripts/lib/util.js diff --git a/assets/javascripts/models/doc.coffee b/assets/javascripts/models/doc.js similarity index 100% rename from assets/javascripts/models/doc.coffee rename to assets/javascripts/models/doc.js diff --git a/assets/javascripts/models/entry.coffee b/assets/javascripts/models/entry.js similarity index 100% rename from assets/javascripts/models/entry.coffee rename to assets/javascripts/models/entry.js diff --git a/assets/javascripts/models/model.coffee b/assets/javascripts/models/model.js similarity index 100% rename from assets/javascripts/models/model.coffee rename to assets/javascripts/models/model.js diff --git a/assets/javascripts/models/type.coffee b/assets/javascripts/models/type.js similarity index 100% rename from assets/javascripts/models/type.coffee rename to assets/javascripts/models/type.js diff --git a/assets/javascripts/templates/base.coffee b/assets/javascripts/templates/base.js similarity index 100% rename from assets/javascripts/templates/base.coffee rename to assets/javascripts/templates/base.js diff --git a/assets/javascripts/templates/error_tmpl.coffee b/assets/javascripts/templates/error_tmpl.js similarity index 100% rename from assets/javascripts/templates/error_tmpl.coffee rename to assets/javascripts/templates/error_tmpl.js diff --git a/assets/javascripts/templates/notice_tmpl.coffee b/assets/javascripts/templates/notice_tmpl.js similarity index 100% rename from assets/javascripts/templates/notice_tmpl.coffee rename to assets/javascripts/templates/notice_tmpl.js diff --git a/assets/javascripts/templates/notif_tmpl.coffee b/assets/javascripts/templates/notif_tmpl.js similarity index 100% rename from assets/javascripts/templates/notif_tmpl.coffee rename to assets/javascripts/templates/notif_tmpl.js diff --git a/assets/javascripts/templates/pages/about_tmpl.coffee b/assets/javascripts/templates/pages/about_tmpl.js similarity index 100% rename from assets/javascripts/templates/pages/about_tmpl.coffee rename to assets/javascripts/templates/pages/about_tmpl.js diff --git a/assets/javascripts/templates/pages/help_tmpl.coffee b/assets/javascripts/templates/pages/help_tmpl.js similarity index 100% rename from assets/javascripts/templates/pages/help_tmpl.coffee rename to assets/javascripts/templates/pages/help_tmpl.js diff --git a/assets/javascripts/templates/pages/offline_tmpl.coffee b/assets/javascripts/templates/pages/offline_tmpl.js similarity index 100% rename from assets/javascripts/templates/pages/offline_tmpl.coffee rename to assets/javascripts/templates/pages/offline_tmpl.js diff --git a/assets/javascripts/templates/pages/settings_tmpl.coffee b/assets/javascripts/templates/pages/settings_tmpl.js similarity index 100% rename from assets/javascripts/templates/pages/settings_tmpl.coffee rename to assets/javascripts/templates/pages/settings_tmpl.js diff --git a/assets/javascripts/templates/pages/type_tmpl.coffee b/assets/javascripts/templates/pages/type_tmpl.js similarity index 100% rename from assets/javascripts/templates/pages/type_tmpl.coffee rename to assets/javascripts/templates/pages/type_tmpl.js diff --git a/assets/javascripts/templates/path_tmpl.coffee b/assets/javascripts/templates/path_tmpl.js similarity index 100% rename from assets/javascripts/templates/path_tmpl.coffee rename to assets/javascripts/templates/path_tmpl.js diff --git a/assets/javascripts/templates/sidebar_tmpl.coffee b/assets/javascripts/templates/sidebar_tmpl.js similarity index 100% rename from assets/javascripts/templates/sidebar_tmpl.coffee rename to assets/javascripts/templates/sidebar_tmpl.js diff --git a/assets/javascripts/templates/tip_tmpl.coffee b/assets/javascripts/templates/tip_tmpl.js similarity index 100% rename from assets/javascripts/templates/tip_tmpl.coffee rename to assets/javascripts/templates/tip_tmpl.js diff --git a/assets/javascripts/views/content/content.coffee b/assets/javascripts/views/content/content.js similarity index 100% rename from assets/javascripts/views/content/content.coffee rename to assets/javascripts/views/content/content.js diff --git a/assets/javascripts/views/content/entry_page.coffee b/assets/javascripts/views/content/entry_page.js similarity index 100% rename from assets/javascripts/views/content/entry_page.coffee rename to assets/javascripts/views/content/entry_page.js diff --git a/assets/javascripts/views/content/offline_page.coffee b/assets/javascripts/views/content/offline_page.js similarity index 100% rename from assets/javascripts/views/content/offline_page.coffee rename to assets/javascripts/views/content/offline_page.js diff --git a/assets/javascripts/views/content/root_page.coffee b/assets/javascripts/views/content/root_page.js similarity index 100% rename from assets/javascripts/views/content/root_page.coffee rename to assets/javascripts/views/content/root_page.js diff --git a/assets/javascripts/views/content/settings_page.coffee b/assets/javascripts/views/content/settings_page.js similarity index 100% rename from assets/javascripts/views/content/settings_page.coffee rename to assets/javascripts/views/content/settings_page.js diff --git a/assets/javascripts/views/content/static_page.coffee b/assets/javascripts/views/content/static_page.js similarity index 100% rename from assets/javascripts/views/content/static_page.coffee rename to assets/javascripts/views/content/static_page.js diff --git a/assets/javascripts/views/content/type_page.coffee b/assets/javascripts/views/content/type_page.js similarity index 100% rename from assets/javascripts/views/content/type_page.coffee rename to assets/javascripts/views/content/type_page.js diff --git a/assets/javascripts/views/layout/document.coffee b/assets/javascripts/views/layout/document.js similarity index 100% rename from assets/javascripts/views/layout/document.coffee rename to assets/javascripts/views/layout/document.js diff --git a/assets/javascripts/views/layout/menu.coffee b/assets/javascripts/views/layout/menu.js similarity index 100% rename from assets/javascripts/views/layout/menu.coffee rename to assets/javascripts/views/layout/menu.js diff --git a/assets/javascripts/views/layout/mobile.coffee b/assets/javascripts/views/layout/mobile.js similarity index 100% rename from assets/javascripts/views/layout/mobile.coffee rename to assets/javascripts/views/layout/mobile.js diff --git a/assets/javascripts/views/layout/path.coffee b/assets/javascripts/views/layout/path.js similarity index 100% rename from assets/javascripts/views/layout/path.coffee rename to assets/javascripts/views/layout/path.js diff --git a/assets/javascripts/views/layout/resizer.coffee b/assets/javascripts/views/layout/resizer.js similarity index 100% rename from assets/javascripts/views/layout/resizer.coffee rename to assets/javascripts/views/layout/resizer.js diff --git a/assets/javascripts/views/layout/settings.coffee b/assets/javascripts/views/layout/settings.js similarity index 100% rename from assets/javascripts/views/layout/settings.coffee rename to assets/javascripts/views/layout/settings.js diff --git a/assets/javascripts/views/list/list_focus.coffee b/assets/javascripts/views/list/list_focus.js similarity index 100% rename from assets/javascripts/views/list/list_focus.coffee rename to assets/javascripts/views/list/list_focus.js diff --git a/assets/javascripts/views/list/list_fold.coffee b/assets/javascripts/views/list/list_fold.js similarity index 100% rename from assets/javascripts/views/list/list_fold.coffee rename to assets/javascripts/views/list/list_fold.js diff --git a/assets/javascripts/views/list/list_select.coffee b/assets/javascripts/views/list/list_select.js similarity index 100% rename from assets/javascripts/views/list/list_select.coffee rename to assets/javascripts/views/list/list_select.js diff --git a/assets/javascripts/views/list/paginated_list.coffee b/assets/javascripts/views/list/paginated_list.js similarity index 100% rename from assets/javascripts/views/list/paginated_list.coffee rename to assets/javascripts/views/list/paginated_list.js diff --git a/assets/javascripts/views/misc/news.coffee b/assets/javascripts/views/misc/news.js similarity index 100% rename from assets/javascripts/views/misc/news.coffee rename to assets/javascripts/views/misc/news.js diff --git a/assets/javascripts/views/misc/notice.coffee b/assets/javascripts/views/misc/notice.js similarity index 100% rename from assets/javascripts/views/misc/notice.coffee rename to assets/javascripts/views/misc/notice.js diff --git a/assets/javascripts/views/misc/notif.coffee b/assets/javascripts/views/misc/notif.js similarity index 100% rename from assets/javascripts/views/misc/notif.coffee rename to assets/javascripts/views/misc/notif.js diff --git a/assets/javascripts/views/misc/tip.coffee b/assets/javascripts/views/misc/tip.js similarity index 100% rename from assets/javascripts/views/misc/tip.coffee rename to assets/javascripts/views/misc/tip.js diff --git a/assets/javascripts/views/misc/updates.coffee b/assets/javascripts/views/misc/updates.js similarity index 100% rename from assets/javascripts/views/misc/updates.coffee rename to assets/javascripts/views/misc/updates.js diff --git a/assets/javascripts/views/pages/base.coffee b/assets/javascripts/views/pages/base.js similarity index 100% rename from assets/javascripts/views/pages/base.coffee rename to assets/javascripts/views/pages/base.js diff --git a/assets/javascripts/views/pages/hidden.coffee b/assets/javascripts/views/pages/hidden.js similarity index 100% rename from assets/javascripts/views/pages/hidden.coffee rename to assets/javascripts/views/pages/hidden.js diff --git a/assets/javascripts/views/pages/jquery.coffee b/assets/javascripts/views/pages/jquery.js similarity index 100% rename from assets/javascripts/views/pages/jquery.coffee rename to assets/javascripts/views/pages/jquery.js diff --git a/assets/javascripts/views/pages/rdoc.coffee b/assets/javascripts/views/pages/rdoc.js similarity index 100% rename from assets/javascripts/views/pages/rdoc.coffee rename to assets/javascripts/views/pages/rdoc.js diff --git a/assets/javascripts/views/pages/sqlite.coffee b/assets/javascripts/views/pages/sqlite.js similarity index 100% rename from assets/javascripts/views/pages/sqlite.coffee rename to assets/javascripts/views/pages/sqlite.js diff --git a/assets/javascripts/views/pages/support_tables.coffee b/assets/javascripts/views/pages/support_tables.js similarity index 100% rename from assets/javascripts/views/pages/support_tables.coffee rename to assets/javascripts/views/pages/support_tables.js diff --git a/assets/javascripts/views/search/search.coffee b/assets/javascripts/views/search/search.js similarity index 100% rename from assets/javascripts/views/search/search.coffee rename to assets/javascripts/views/search/search.js diff --git a/assets/javascripts/views/search/search_scope.coffee b/assets/javascripts/views/search/search_scope.js similarity index 100% rename from assets/javascripts/views/search/search_scope.coffee rename to assets/javascripts/views/search/search_scope.js diff --git a/assets/javascripts/views/sidebar/doc_list.coffee b/assets/javascripts/views/sidebar/doc_list.js similarity index 100% rename from assets/javascripts/views/sidebar/doc_list.coffee rename to assets/javascripts/views/sidebar/doc_list.js diff --git a/assets/javascripts/views/sidebar/doc_picker.coffee b/assets/javascripts/views/sidebar/doc_picker.js similarity index 100% rename from assets/javascripts/views/sidebar/doc_picker.coffee rename to assets/javascripts/views/sidebar/doc_picker.js diff --git a/assets/javascripts/views/sidebar/entry_list.coffee b/assets/javascripts/views/sidebar/entry_list.js similarity index 100% rename from assets/javascripts/views/sidebar/entry_list.coffee rename to assets/javascripts/views/sidebar/entry_list.js diff --git a/assets/javascripts/views/sidebar/results.coffee b/assets/javascripts/views/sidebar/results.js similarity index 100% rename from assets/javascripts/views/sidebar/results.coffee rename to assets/javascripts/views/sidebar/results.js diff --git a/assets/javascripts/views/sidebar/sidebar.coffee b/assets/javascripts/views/sidebar/sidebar.js similarity index 100% rename from assets/javascripts/views/sidebar/sidebar.coffee rename to assets/javascripts/views/sidebar/sidebar.js diff --git a/assets/javascripts/views/sidebar/sidebar_hover.coffee b/assets/javascripts/views/sidebar/sidebar_hover.js similarity index 100% rename from assets/javascripts/views/sidebar/sidebar_hover.coffee rename to assets/javascripts/views/sidebar/sidebar_hover.js diff --git a/assets/javascripts/views/sidebar/type_list.coffee b/assets/javascripts/views/sidebar/type_list.js similarity index 100% rename from assets/javascripts/views/sidebar/type_list.coffee rename to assets/javascripts/views/sidebar/type_list.js diff --git a/assets/javascripts/views/view.coffee b/assets/javascripts/views/view.js similarity index 100% rename from assets/javascripts/views/view.coffee rename to assets/javascripts/views/view.js From e4fbca722bab61f27b9f6512d8981ae3915eff5b Mon Sep 17 00:00:00 2001 From: decaffeinate Date: Sat, 6 Jan 2024 11:47:09 +0100 Subject: [PATCH 0127/1110] decaffeinate: Convert app.coffee and 75 other files to JS --- assets/javascripts/app/app.js | 635 +++++++------ assets/javascripts/app/db.js | 868 +++++++++-------- assets/javascripts/app/router.js | 351 ++++--- assets/javascripts/app/searcher.js | 665 +++++++------ assets/javascripts/app/serviceworker.js | 105 ++- assets/javascripts/app/settings.js | 389 ++++---- assets/javascripts/app/shortcuts.js | 452 +++++---- assets/javascripts/app/update_checker.js | 79 +- assets/javascripts/application.js.js | 49 +- assets/javascripts/collections/collection.js | 130 +-- assets/javascripts/collections/docs.js | 184 ++-- assets/javascripts/collections/entries.js | 13 +- assets/javascripts/collections/types.js | 56 +- assets/javascripts/debug.js.js | 195 ++-- assets/javascripts/lib/ajax.js | 256 ++--- assets/javascripts/lib/cookies_store.js | 94 +- assets/javascripts/lib/events.js | 73 +- assets/javascripts/lib/favicon.js | 165 ++-- assets/javascripts/lib/license.js | 4 +- assets/javascripts/lib/local_storage_store.js | 50 +- assets/javascripts/lib/page.js | 495 +++++----- assets/javascripts/lib/util.js | 886 ++++++++++-------- assets/javascripts/models/doc.js | 315 ++++--- assets/javascripts/models/entry.js | 179 ++-- assets/javascripts/models/model.js | 8 +- assets/javascripts/models/type.js | 32 +- assets/javascripts/templates/base.js | 28 +- assets/javascripts/templates/error_tmpl.js | 142 +-- assets/javascripts/templates/notice_tmpl.js | 19 +- assets/javascripts/templates/notif_tmpl.js | 127 +-- .../javascripts/templates/pages/about_tmpl.js | 181 ++-- .../javascripts/templates/pages/help_tmpl.js | 334 ++++--- .../templates/pages/offline_tmpl.js | 149 +-- .../templates/pages/settings_tmpl.js | 143 +-- .../javascripts/templates/pages/type_tmpl.js | 13 +- assets/javascripts/templates/path_tmpl.js | 18 +- assets/javascripts/templates/sidebar_tmpl.js | 119 +-- assets/javascripts/templates/tip_tmpl.js | 25 +- assets/javascripts/views/content/content.js | 448 +++++---- .../javascripts/views/content/entry_page.js | 391 ++++---- .../javascripts/views/content/offline_page.js | 190 ++-- assets/javascripts/views/content/root_page.js | 98 +- .../views/content/settings_page.js | 267 +++--- .../javascripts/views/content/static_page.js | 57 +- assets/javascripts/views/content/type_page.js | 45 +- assets/javascripts/views/layout/document.js | 194 ++-- assets/javascripts/views/layout/menu.js | 54 +- assets/javascripts/views/layout/mobile.js | 346 ++++--- assets/javascripts/views/layout/path.js | 93 +- assets/javascripts/views/layout/resizer.js | 110 ++- assets/javascripts/views/layout/settings.js | 210 +++-- assets/javascripts/views/list/list_focus.js | 301 +++--- assets/javascripts/views/list/list_fold.js | 146 +-- assets/javascripts/views/list/list_select.js | 108 ++- .../javascripts/views/list/paginated_list.js | 211 +++-- assets/javascripts/views/misc/news.js | 89 +- assets/javascripts/views/misc/notice.js | 53 +- assets/javascripts/views/misc/notif.js | 123 ++- assets/javascripts/views/misc/tip.js | 27 +- assets/javascripts/views/misc/updates.js | 90 +- assets/javascripts/views/pages/base.js | 92 +- assets/javascripts/views/pages/hidden.js | 38 +- assets/javascripts/views/pages/jquery.js | 138 +-- assets/javascripts/views/pages/rdoc.js | 33 +- assets/javascripts/views/pages/sqlite.js | 47 +- .../javascripts/views/pages/support_tables.js | 31 +- assets/javascripts/views/search/search.js | 393 ++++---- .../javascripts/views/search/search_scope.js | 315 ++++--- assets/javascripts/views/sidebar/doc_list.js | 421 +++++---- .../javascripts/views/sidebar/doc_picker.js | 218 +++-- .../javascripts/views/sidebar/entry_list.js | 34 +- assets/javascripts/views/sidebar/results.js | 135 +-- assets/javascripts/views/sidebar/sidebar.js | 379 ++++---- .../views/sidebar/sidebar_hover.js | 243 +++-- assets/javascripts/views/sidebar/type_list.js | 130 +-- assets/javascripts/views/view.js | 386 ++++---- 76 files changed, 8404 insertions(+), 6306 deletions(-) diff --git a/assets/javascripts/app/app.js b/assets/javascripts/app/app.js index b55e552c8b..b79b9e1886 100644 --- a/assets/javascripts/app/app.js +++ b/assets/javascripts/app/app.js @@ -1,283 +1,352 @@ -@app = - _$: $ - _$$: $$ - _page: page - collections: {} - models: {} - templates: {} - views: {} - - init: -> - try @initErrorTracking() catch - return unless @browserCheck() - - @el = $('._app') - @localStorage = new LocalStorageStore - @serviceWorker = new app.ServiceWorker if app.ServiceWorker.isEnabled() - @settings = new app.Settings - @db = new app.DB() - - @settings.initLayout() - - @docs = new app.collections.Docs - @disabledDocs = new app.collections.Docs - @entries = new app.collections.Entries - - @router = new app.Router - @shortcuts = new app.Shortcuts - @document = new app.views.Document - @mobile = new app.views.Mobile if @isMobile() - - if document.body.hasAttribute('data-doc') - @DOC = JSON.parse(document.body.getAttribute('data-doc')) - @bootOne() - else if @DOCS - @bootAll() - else - @onBootError() - return - - browserCheck: -> - return true if @isSupportedBrowser() - document.body.innerHTML = app.templates.unsupportedBrowser - @hideLoadingScreen() - false - - initErrorTracking: -> - # Show a warning message and don't track errors when the app is loaded - # from a domain other than our own, because things are likely to break. - # (e.g. cross-domain requests) - if @isInvalidLocation() - new app.views.Notif 'InvalidLocation' - else - if @config.sentry_dsn - Raven.config @config.sentry_dsn, - release: @config.release - whitelistUrls: [/devdocs/] - includePaths: [/devdocs/] - ignoreErrors: [/NPObject/, /NS_ERROR/, /^null$/, /EvalError/] - tags: - mode: if @isSingleDoc() then 'single' else 'full' - iframe: (window.top isnt window).toString() - electron: (!!window.process?.versions?.electron).toString() - shouldSendCallback: => - try - if @isInjectionError() - @onInjectionError() - return false - if @isAndroidWebview() - return false - true - dataCallback: (data) -> - try - $.extend(data.user ||= {}, app.settings.dump()) - data.user.docs = data.user.docs.split('/') if data.user.docs - data.user.lastIDBTransaction = app.lastIDBTransaction if app.lastIDBTransaction - data.tags.scriptCount = document.scripts.length - data - .install() - @previousErrorHandler = onerror - window.onerror = @onWindowError.bind(@) - CookiesStore.onBlocked = @onCookieBlocked - return - - bootOne: -> - @doc = new app.models.Doc @DOC - @docs.reset [@doc] - @doc.load @start.bind(@), @onBootError.bind(@), readCache: true - new app.views.Notice 'singleDoc', @doc - delete @DOC - return - - bootAll: -> - docs = @settings.getDocs() - for doc in @DOCS - (if docs.indexOf(doc.slug) >= 0 then @docs else @disabledDocs).add(doc) - @migrateDocs() - @docs.load @start.bind(@), @onBootError.bind(@), readCache: true, writeCache: true - delete @DOCS - return - - start: -> - @entries.add doc.toEntry() for doc in @docs.all() - @entries.add doc.toEntry() for doc in @disabledDocs.all() - @initDoc(doc) for doc in @docs.all() - @trigger 'ready' - @router.start() - @hideLoadingScreen() - setTimeout => - @welcomeBack() unless @doc - @removeEvent 'ready bootError' - , 50 - return - - initDoc: (doc) -> - doc.entries.add type.toEntry() for type in doc.types.all() - @entries.add doc.entries.all() - return - - migrateDocs: -> - for slug in @settings.getDocs() when not @docs.findBy('slug', slug) - needsSaving = true - doc = @disabledDocs.findBy('slug', 'webpack') if slug == 'webpack~2' - doc = @disabledDocs.findBy('slug', 'angular') if slug == 'angular~4_typescript' - doc = @disabledDocs.findBy('slug', 'angular~2') if slug == 'angular~2_typescript' - doc ||= @disabledDocs.findBy('slug_without_version', slug) - if doc - @disabledDocs.remove(doc) - @docs.add(doc) - - @saveDocs() if needsSaving - return - - enableDoc: (doc, _onSuccess, onError) -> - return if @docs.contains(doc) - - onSuccess = => - return if @docs.contains(doc) - @disabledDocs.remove(doc) - @docs.add(doc) - @docs.sort() - @initDoc(doc) - @saveDocs() - if app.settings.get('autoInstall') - doc.install(_onSuccess, onError) - else - _onSuccess() - return - - doc.load onSuccess, onError, writeCache: true - return - - saveDocs: -> - @settings.setDocs(doc.slug for doc in @docs.all()) - @db.migrate() - @serviceWorker?.updateInBackground() - - welcomeBack: -> - visitCount = @settings.get('count') - @settings.set 'count', ++visitCount - new app.views.Notif 'Share', autoHide: null if visitCount is 5 - new app.views.News() - new app.views.Updates() - @updateChecker = new app.UpdateChecker() - - reboot: -> - if location.pathname isnt '/' and location.pathname isnt '/settings' - window.location = "/##{location.pathname}" - else - window.location = '/' - return - - reload: -> - @docs.clearCache() - @disabledDocs.clearCache() - if @serviceWorker then @serviceWorker.reload() else @reboot() - return - - reset: -> - @localStorage.reset() - @settings.reset() - @db?.reset() - @serviceWorker?.update() - window.location = '/' - return - - showTip: (tip) -> - return if @isSingleDoc() - tips = @settings.getTips() - if tips.indexOf(tip) is -1 - tips.push(tip) - @settings.setTips(tips) - new app.views.Tip(tip) - return - - hideLoadingScreen: -> - document.body.classList.add '_overlay-scrollbars' if $.overlayScrollbarsEnabled() - document.documentElement.classList.remove '_booting' - return - - indexHost: -> - # Can't load the index files from the host/CDN when service worker is - # enabled because it doesn't support caching URLs that use CORS. - @config[if @serviceWorker and @settings.hasDocs() then 'index_path' else 'docs_origin'] - - onBootError: (args...) -> - @trigger 'bootError' - @hideLoadingScreen() - return - - onQuotaExceeded: -> - return if @quotaExceeded - @quotaExceeded = true - new app.views.Notif 'QuotaExceeded', autoHide: null - return - - onCookieBlocked: (key, value, actual) -> - return if @cookieBlocked - @cookieBlocked = true - new app.views.Notif 'CookieBlocked', autoHide: null - Raven.captureMessage "CookieBlocked/#{key}", level: 'warning', extra: {value, actual} - return - - onWindowError: (args...) -> - return if @cookieBlocked - if @isInjectionError args... - @onInjectionError() - else if @isAppError args... - @previousErrorHandler? args... - @hideLoadingScreen() - @errorNotif or= new app.views.Notif 'Error' - @errorNotif.show() - return - - onInjectionError: -> - unless @injectionError - @injectionError = true - alert """ - JavaScript code has been injected in the page which prevents DevDocs from running correctly. - Please check your browser extensions/addons. """ - Raven.captureMessage 'injection error', level: 'info' - return - - isInjectionError: -> - # Some browser extensions expect the entire web to use jQuery. - # I gave up trying to fight back. - window.$ isnt app._$ or window.$$ isnt app._$$ or window.page isnt app._page or typeof $.empty isnt 'function' or typeof page.show isnt 'function' - - isAppError: (error, file) -> - # Ignore errors from external scripts. - file and file.indexOf('devdocs') isnt -1 and file.indexOf('.js') is file.length - 3 - - isSupportedBrowser: -> - try - features = - bind: !!Function::bind - pushState: !!history.pushState - matchMedia: !!window.matchMedia - insertAdjacentHTML: !!document.body.insertAdjacentHTML - defaultPrevented: document.createEvent('CustomEvent').defaultPrevented is false - cssVariables: !!CSS?.supports?('(--t: 0)') - - for key, value of features when not value - Raven.captureMessage "unsupported/#{key}", level: 'info' - return false - - true - catch error - Raven.captureMessage 'unsupported/exception', level: 'info', extra: { error: error } - false - - isSingleDoc: -> - document.body.hasAttribute('data-doc') - - isMobile: -> - @_isMobile ?= app.views.Mobile.detect() - - isAndroidWebview: -> - @_isAndroidWebview ?= app.views.Mobile.detectAndroidWebview() - - isInvalidLocation: -> - @config.env is 'production' and location.host.indexOf(app.config.production_host) isnt 0 - -$.extend app, Events +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS103: Rewrite code to no longer use __guard__, or convert again using --optional-chaining + * DS207: Consider shorter variations of null checks + * DS208: Avoid top-level this + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +this.app = { + _$: $, + _$$: $$, + _page: page, + collections: {}, + models: {}, + templates: {}, + views: {}, + + init() { + try { this.initErrorTracking(); } catch (error) {} + if (!this.browserCheck()) { return; } + + this.el = $('._app'); + this.localStorage = new LocalStorageStore; + if (app.ServiceWorker.isEnabled()) { this.serviceWorker = new app.ServiceWorker; } + this.settings = new app.Settings; + this.db = new app.DB(); + + this.settings.initLayout(); + + this.docs = new app.collections.Docs; + this.disabledDocs = new app.collections.Docs; + this.entries = new app.collections.Entries; + + this.router = new app.Router; + this.shortcuts = new app.Shortcuts; + this.document = new app.views.Document; + if (this.isMobile()) { this.mobile = new app.views.Mobile; } + + if (document.body.hasAttribute('data-doc')) { + this.DOC = JSON.parse(document.body.getAttribute('data-doc')); + this.bootOne(); + } else if (this.DOCS) { + this.bootAll(); + } else { + this.onBootError(); + } + }, + + browserCheck() { + if (this.isSupportedBrowser()) { return true; } + document.body.innerHTML = app.templates.unsupportedBrowser; + this.hideLoadingScreen(); + return false; + }, + + initErrorTracking() { + // Show a warning message and don't track errors when the app is loaded + // from a domain other than our own, because things are likely to break. + // (e.g. cross-domain requests) + if (this.isInvalidLocation()) { + new app.views.Notif('InvalidLocation'); + } else { + if (this.config.sentry_dsn) { + Raven.config(this.config.sentry_dsn, { + release: this.config.release, + whitelistUrls: [/devdocs/], + includePaths: [/devdocs/], + ignoreErrors: [/NPObject/, /NS_ERROR/, /^null$/, /EvalError/], + tags: { + mode: this.isSingleDoc() ? 'single' : 'full', + iframe: (window.top !== window).toString(), + electron: (!!__guard__(window.process != null ? window.process.versions : undefined, x => x.electron)).toString() + }, + shouldSendCallback: () => { + try { + if (this.isInjectionError()) { + this.onInjectionError(); + return false; + } + if (this.isAndroidWebview()) { + return false; + } + } catch (error) {} + return true; + }, + dataCallback(data) { + try { + $.extend(data.user || (data.user = {}), app.settings.dump()); + if (data.user.docs) { data.user.docs = data.user.docs.split('/'); } + if (app.lastIDBTransaction) { data.user.lastIDBTransaction = app.lastIDBTransaction; } + data.tags.scriptCount = document.scripts.length; + } catch (error) {} + return data; + } + }).install(); + } + this.previousErrorHandler = onerror; + window.onerror = this.onWindowError.bind(this); + CookiesStore.onBlocked = this.onCookieBlocked; + } + }, + + bootOne() { + this.doc = new app.models.Doc(this.DOC); + this.docs.reset([this.doc]); + this.doc.load(this.start.bind(this), this.onBootError.bind(this), {readCache: true}); + new app.views.Notice('singleDoc', this.doc); + delete this.DOC; + }, + + bootAll() { + const docs = this.settings.getDocs(); + for (var doc of Array.from(this.DOCS)) { + (docs.indexOf(doc.slug) >= 0 ? this.docs : this.disabledDocs).add(doc); + } + this.migrateDocs(); + this.docs.load(this.start.bind(this), this.onBootError.bind(this), {readCache: true, writeCache: true}); + delete this.DOCS; + }, + + start() { + let doc; + for (doc of Array.from(this.docs.all())) { this.entries.add(doc.toEntry()); } + for (doc of Array.from(this.disabledDocs.all())) { this.entries.add(doc.toEntry()); } + for (doc of Array.from(this.docs.all())) { this.initDoc(doc); } + this.trigger('ready'); + this.router.start(); + this.hideLoadingScreen(); + setTimeout(() => { + if (!this.doc) { this.welcomeBack(); } + return this.removeEvent('ready bootError'); + } + , 50); + }, + + initDoc(doc) { + for (var type of Array.from(doc.types.all())) { doc.entries.add(type.toEntry()); } + this.entries.add(doc.entries.all()); + }, + + migrateDocs() { + let needsSaving; + for (var slug of Array.from(this.settings.getDocs())) { + if (!this.docs.findBy('slug', slug)) {var doc; + + needsSaving = true; + if (slug === 'webpack~2') { doc = this.disabledDocs.findBy('slug', 'webpack'); } + if (slug === 'angular~4_typescript') { doc = this.disabledDocs.findBy('slug', 'angular'); } + if (slug === 'angular~2_typescript') { doc = this.disabledDocs.findBy('slug', 'angular~2'); } + if (!doc) { doc = this.disabledDocs.findBy('slug_without_version', slug); } + if (doc) { + this.disabledDocs.remove(doc); + this.docs.add(doc); + } + } + } + + if (needsSaving) { this.saveDocs(); } + }, + + enableDoc(doc, _onSuccess, onError) { + if (this.docs.contains(doc)) { return; } + + const onSuccess = () => { + if (this.docs.contains(doc)) { return; } + this.disabledDocs.remove(doc); + this.docs.add(doc); + this.docs.sort(); + this.initDoc(doc); + this.saveDocs(); + if (app.settings.get('autoInstall')) { + doc.install(_onSuccess, onError); + } else { + _onSuccess(); + } + }; + + doc.load(onSuccess, onError, {writeCache: true}); + }, + + saveDocs() { + this.settings.setDocs(Array.from(this.docs.all()).map((doc) => doc.slug)); + this.db.migrate(); + return (this.serviceWorker != null ? this.serviceWorker.updateInBackground() : undefined); + }, + + welcomeBack() { + let visitCount = this.settings.get('count'); + this.settings.set('count', ++visitCount); + if (visitCount === 5) { new app.views.Notif('Share', {autoHide: null}); } + new app.views.News(); + new app.views.Updates(); + return this.updateChecker = new app.UpdateChecker(); + }, + + reboot() { + if ((location.pathname !== '/') && (location.pathname !== '/settings')) { + window.location = `/#${location.pathname}`; + } else { + window.location = '/'; + } + }, + + reload() { + this.docs.clearCache(); + this.disabledDocs.clearCache(); + if (this.serviceWorker) { this.serviceWorker.reload(); } else { this.reboot(); } + }, + + reset() { + this.localStorage.reset(); + this.settings.reset(); + if (this.db != null) { + this.db.reset(); + } + if (this.serviceWorker != null) { + this.serviceWorker.update(); + } + window.location = '/'; + }, + + showTip(tip) { + if (this.isSingleDoc()) { return; } + const tips = this.settings.getTips(); + if (tips.indexOf(tip) === -1) { + tips.push(tip); + this.settings.setTips(tips); + new app.views.Tip(tip); + } + }, + + hideLoadingScreen() { + if ($.overlayScrollbarsEnabled()) { document.body.classList.add('_overlay-scrollbars'); } + document.documentElement.classList.remove('_booting'); + }, + + indexHost() { + // Can't load the index files from the host/CDN when service worker is + // enabled because it doesn't support caching URLs that use CORS. + return this.config[this.serviceWorker && this.settings.hasDocs() ? 'index_path' : 'docs_origin']; + }, + + onBootError(...args) { + this.trigger('bootError'); + this.hideLoadingScreen(); + }, + + onQuotaExceeded() { + if (this.quotaExceeded) { return; } + this.quotaExceeded = true; + new app.views.Notif('QuotaExceeded', {autoHide: null}); + }, + + onCookieBlocked(key, value, actual) { + if (this.cookieBlocked) { return; } + this.cookieBlocked = true; + new app.views.Notif('CookieBlocked', {autoHide: null}); + Raven.captureMessage(`CookieBlocked/${key}`, {level: 'warning', extra: {value, actual}}); + }, + + onWindowError(...args) { + if (this.cookieBlocked) { return; } + if (this.isInjectionError(...Array.from(args || []))) { + this.onInjectionError(); + } else if (this.isAppError(...Array.from(args || []))) { + if (typeof this.previousErrorHandler === 'function') { + this.previousErrorHandler(...Array.from(args || [])); + } + this.hideLoadingScreen(); + if (!this.errorNotif) { this.errorNotif = new app.views.Notif('Error'); } + this.errorNotif.show(); + } + }, + + onInjectionError() { + if (!this.injectionError) { + this.injectionError = true; + alert(`\ +JavaScript code has been injected in the page which prevents DevDocs from running correctly. +Please check your browser extensions/addons. ` + ); + Raven.captureMessage('injection error', {level: 'info'}); + } + }, + + isInjectionError() { + // Some browser extensions expect the entire web to use jQuery. + // I gave up trying to fight back. + return (window.$ !== app._$) || (window.$$ !== app._$$) || (window.page !== app._page) || (typeof $.empty !== 'function') || (typeof page.show !== 'function'); + }, + + isAppError(error, file) { + // Ignore errors from external scripts. + return file && (file.indexOf('devdocs') !== -1) && (file.indexOf('.js') === (file.length - 3)); + }, + + isSupportedBrowser() { + try { + const features = { + bind: !!Function.prototype.bind, + pushState: !!history.pushState, + matchMedia: !!window.matchMedia, + insertAdjacentHTML: !!document.body.insertAdjacentHTML, + defaultPrevented: document.createEvent('CustomEvent').defaultPrevented === false, + cssVariables: !!__guardMethod__(CSS, 'supports', o => o.supports('(--t: 0)')) + }; + + for (var key in features) { + var value = features[key]; + if (!value) { + Raven.captureMessage(`unsupported/${key}`, {level: 'info'}); + return false; + } + } + + return true; + } catch (error) { + Raven.captureMessage('unsupported/exception', {level: 'info', extra: { error }}); + return false; + } + }, + + isSingleDoc() { + return document.body.hasAttribute('data-doc'); + }, + + isMobile() { + return this._isMobile != null ? this._isMobile : (this._isMobile = app.views.Mobile.detect()); + }, + + isAndroidWebview() { + return this._isAndroidWebview != null ? this._isAndroidWebview : (this._isAndroidWebview = app.views.Mobile.detectAndroidWebview()); + }, + + isInvalidLocation() { + return (this.config.env === 'production') && (location.host.indexOf(app.config.production_host) !== 0); + } +}; + +$.extend(app, Events); + +function __guard__(value, transform) { + return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; +} +function __guardMethod__(obj, methodName, transform) { + if (typeof obj !== 'undefined' && obj !== null && typeof obj[methodName] === 'function') { + return transform(obj, methodName); + } else { + return undefined; + } +} \ No newline at end of file diff --git a/assets/javascripts/app/db.js b/assets/javascripts/app/db.js index 28e4b0eaeb..ab84896488 100644 --- a/assets/javascripts/app/db.js +++ b/assets/javascripts/app/db.js @@ -1,382 +1,486 @@ -class app.DB - NAME = 'docs' - VERSION = 15 - - constructor: -> - @versionMultipler = if $.isIE() then 1e5 else 1e9 - @useIndexedDB = @useIndexedDB() - @callbacks = [] - - db: (fn) -> - return fn() unless @useIndexedDB - @callbacks.push(fn) if fn - return if @open - - try - @open = true - req = indexedDB.open(NAME, VERSION * @versionMultipler + @userVersion()) - req.onsuccess = @onOpenSuccess - req.onerror = @onOpenError - req.onupgradeneeded = @onUpgradeNeeded - catch error - @fail 'exception', error - return - - onOpenSuccess: (event) => - db = event.target.result - - if db.objectStoreNames.length is 0 - try db.close() - @open = false - @fail 'empty' - else if error = @buggyIDB(db) - try db.close() - @open = false - @fail 'buggy', error - else - @runCallbacks(db) - @open = false - db.close() - return - - onOpenError: (event) => - event.preventDefault() - @open = false - error = event.target.error - - switch error.name - when 'QuotaExceededError' - @onQuotaExceededError() - when 'VersionError' - @onVersionError() - when 'InvalidStateError' - @fail 'private_mode' - else - @fail 'cant_open', error - return - - fail: (reason, error) -> - @cachedDocs = null - @useIndexedDB = false - @reason or= reason - @error or= error - console.error? 'IDB error', error if error - @runCallbacks() - if error and reason is 'cant_open' - Raven.captureMessage "#{error.name}: #{error.message}", level: 'warning', fingerprint: [error.name] - return - - onQuotaExceededError: -> - @reset() - @db() - app.onQuotaExceeded() - Raven.captureMessage 'QuotaExceededError', level: 'warning' - return - - onVersionError: -> - req = indexedDB.open(NAME) - req.onsuccess = (event) => - @handleVersionMismatch event.target.result.version - req.onerror = (event) -> - event.preventDefault() - @fail 'cant_open', error - return - - handleVersionMismatch: (actualVersion) -> - if Math.floor(actualVersion / @versionMultipler) isnt VERSION - @fail 'version' - else - @setUserVersion actualVersion - VERSION * @versionMultipler - @db() - return - - buggyIDB: (db) -> - return if @checkedBuggyIDB - @checkedBuggyIDB = true - try - @idbTransaction(db, stores: $.makeArray(db.objectStoreNames)[0..1], mode: 'readwrite').abort() # https://bugs.webkit.org/show_bug.cgi?id=136937 - return - catch error - return error - - runCallbacks: (db) -> - fn(db) while fn = @callbacks.shift() - return - - onUpgradeNeeded: (event) -> - return unless db = event.target.result - - objectStoreNames = $.makeArray(db.objectStoreNames) - - unless $.arrayDelete(objectStoreNames, 'docs') - try db.createObjectStore('docs') - - for doc in app.docs.all() when not $.arrayDelete(objectStoreNames, doc.slug) - try db.createObjectStore(doc.slug) - - for name in objectStoreNames - try db.deleteObjectStore(name) - return - - store: (doc, data, onSuccess, onError, _retry = true) -> - @db (db) => - unless db - onError() - return - - txn = @idbTransaction db, stores: ['docs', doc.slug], mode: 'readwrite', ignoreError: false - txn.oncomplete = => - @cachedDocs?[doc.slug] = doc.mtime - onSuccess() - return - txn.onerror = (event) => - event.preventDefault() - if txn.error?.name is 'NotFoundError' and _retry - @migrate() - setTimeout => - @store(doc, data, onSuccess, onError, false) - , 0 - else - onError(event) - return - - store = txn.objectStore(doc.slug) - store.clear() - store.add(content, path) for path, content of data - - store = txn.objectStore('docs') - store.put(doc.mtime, doc.slug) - return - return - - unstore: (doc, onSuccess, onError, _retry = true) -> - @db (db) => - unless db - onError() - return - - txn = @idbTransaction db, stores: ['docs', doc.slug], mode: 'readwrite', ignoreError: false - txn.oncomplete = => - delete @cachedDocs?[doc.slug] - onSuccess() - return - txn.onerror = (event) -> - event.preventDefault() - if txn.error?.name is 'NotFoundError' and _retry - @migrate() - setTimeout => - @unstore(doc, onSuccess, onError, false) - , 0 - else - onError(event) - return - - store = txn.objectStore('docs') - store.delete(doc.slug) - - store = txn.objectStore(doc.slug) - store.clear() - return - return - - version: (doc, fn) -> - if (version = @cachedVersion(doc))? - fn(version) - return - - @db (db) => - unless db - fn(false) - return - - txn = @idbTransaction db, stores: ['docs'], mode: 'readonly' - store = txn.objectStore('docs') - - req = store.get(doc.slug) - req.onsuccess = -> - fn(req.result) - return - req.onerror = (event) -> - event.preventDefault() - fn(false) - return - return - return - - cachedVersion: (doc) -> - return unless @cachedDocs - @cachedDocs[doc.slug] or false - - versions: (docs, fn) -> - if versions = @cachedVersions(docs) - fn(versions) - return - - @db (db) => - unless db - fn(false) - return - - txn = @idbTransaction db, stores: ['docs'], mode: 'readonly' - txn.oncomplete = -> - fn(result) - return - store = txn.objectStore('docs') - result = {} - - docs.forEach (doc) -> - req = store.get(doc.slug) - req.onsuccess = -> - result[doc.slug] = req.result - return - req.onerror = (event) -> - event.preventDefault() - result[doc.slug] = false - return - return - return - - cachedVersions: (docs) -> - return unless @cachedDocs - result = {} - result[doc.slug] = @cachedVersion(doc) for doc in docs - result - - load: (entry, onSuccess, onError) -> - if @shouldLoadWithIDB(entry) - onError = @loadWithXHR.bind(@, entry, onSuccess, onError) - @loadWithIDB entry, onSuccess, onError - else - @loadWithXHR entry, onSuccess, onError - - loadWithXHR: (entry, onSuccess, onError) -> - ajax - url: entry.fileUrl() - dataType: 'html' - success: onSuccess - error: onError - - loadWithIDB: (entry, onSuccess, onError) -> - @db (db) => - unless db - onError() - return - - unless db.objectStoreNames.contains(entry.doc.slug) - onError() - @loadDocsCache(db) - return - - txn = @idbTransaction db, stores: [entry.doc.slug], mode: 'readonly' - store = txn.objectStore(entry.doc.slug) - - req = store.get(entry.dbPath()) - req.onsuccess = -> - if req.result then onSuccess(req.result) else onError() - return - req.onerror = (event) -> - event.preventDefault() - onError() - return - @loadDocsCache(db) - return - - loadDocsCache: (db) -> - return if @cachedDocs - @cachedDocs = {} - - txn = @idbTransaction db, stores: ['docs'], mode: 'readonly' - txn.oncomplete = => - setTimeout(@checkForCorruptedDocs, 50) - return - - req = txn.objectStore('docs').openCursor() - req.onsuccess = (event) => - return unless cursor = event.target.result - @cachedDocs[cursor.key] = cursor.value - cursor.continue() - return - req.onerror = (event) -> - event.preventDefault() - return - return - - checkForCorruptedDocs: => - @db (db) => - @corruptedDocs = [] - docs = (key for key, value of @cachedDocs when value) - return if docs.length is 0 - - for slug in docs when not app.docs.findBy('slug', slug) - @corruptedDocs.push(slug) - - for slug in @corruptedDocs - $.arrayDelete(docs, slug) - - if docs.length is 0 - setTimeout(@deleteCorruptedDocs, 0) - return - - txn = @idbTransaction(db, stores: docs, mode: 'readonly', ignoreError: false) - txn.oncomplete = => - setTimeout(@deleteCorruptedDocs, 0) if @corruptedDocs.length > 0 - return - - for doc in docs - txn.objectStore(doc).get('index').onsuccess = (event) => - @corruptedDocs.push(event.target.source.name) unless event.target.result - return - return - return - - deleteCorruptedDocs: => - @db (db) => - txn = @idbTransaction(db, stores: ['docs'], mode: 'readwrite', ignoreError: false) - store = txn.objectStore('docs') - while doc = @corruptedDocs.pop() - @cachedDocs[doc] = false - store.delete(doc) - return - Raven.captureMessage 'corruptedDocs', level: 'info', extra: { docs: @corruptedDocs.join(',') } - return - - shouldLoadWithIDB: (entry) -> - @useIndexedDB and (not @cachedDocs or @cachedDocs[entry.doc.slug]) - - idbTransaction: (db, options) -> - app.lastIDBTransaction = [options.stores, options.mode] - txn = db.transaction(options.stores, options.mode) - unless options.ignoreError is false - txn.onerror = (event) -> - event.preventDefault() - return - unless options.ignoreAbort is false - txn.onabort = (event) -> - event.preventDefault() - return - txn - - reset: -> - try indexedDB?.deleteDatabase(NAME) catch - return - - useIndexedDB: -> - try - if !app.isSingleDoc() and window.indexedDB - true - else - @reason = 'not_supported' - false - catch - false - - migrate: -> - app.settings.set('schema', @userVersion() + 1) - return - - setUserVersion: (version) -> - app.settings.set('schema', version) - return - - userVersion: -> - app.settings.get('schema') +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS205: Consider reworking code to avoid use of IIFEs + * DS206: Consider reworking classes to avoid initClass + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +(function() { + let NAME = undefined; + let VERSION = undefined; + const Cls = (app.DB = class DB { + static initClass() { + NAME = 'docs'; + VERSION = 15; + } + + constructor() { + this.onOpenSuccess = this.onOpenSuccess.bind(this); + this.onOpenError = this.onOpenError.bind(this); + this.checkForCorruptedDocs = this.checkForCorruptedDocs.bind(this); + this.deleteCorruptedDocs = this.deleteCorruptedDocs.bind(this); + this.versionMultipler = $.isIE() ? 1e5 : 1e9; + this.useIndexedDB = this.useIndexedDB(); + this.callbacks = []; + } + + db(fn) { + if (!this.useIndexedDB) { return fn(); } + if (fn) { this.callbacks.push(fn); } + if (this.open) { return; } + + try { + this.open = true; + const req = indexedDB.open(NAME, (VERSION * this.versionMultipler) + this.userVersion()); + req.onsuccess = this.onOpenSuccess; + req.onerror = this.onOpenError; + req.onupgradeneeded = this.onUpgradeNeeded; + } catch (error) { + this.fail('exception', error); + } + } + + onOpenSuccess(event) { + let error; + const db = event.target.result; + + if (db.objectStoreNames.length === 0) { + try { db.close(); } catch (error1) {} + this.open = false; + this.fail('empty'); + } else if (error = this.buggyIDB(db)) { + try { db.close(); } catch (error2) {} + this.open = false; + this.fail('buggy', error); + } else { + this.runCallbacks(db); + this.open = false; + db.close(); + } + } + + onOpenError(event) { + event.preventDefault(); + this.open = false; + const { + error + } = event.target; + + switch (error.name) { + case 'QuotaExceededError': + this.onQuotaExceededError(); + break; + case 'VersionError': + this.onVersionError(); + break; + case 'InvalidStateError': + this.fail('private_mode'); + break; + default: + this.fail('cant_open', error); + } + } + + fail(reason, error) { + this.cachedDocs = null; + this.useIndexedDB = false; + if (!this.reason) { this.reason = reason; } + if (!this.error) { this.error = error; } + if (error) { if (typeof console.error === 'function') { + console.error('IDB error', error); + } } + this.runCallbacks(); + if (error && (reason === 'cant_open')) { + Raven.captureMessage(`${error.name}: ${error.message}`, {level: 'warning', fingerprint: [error.name]}); + } + } + + onQuotaExceededError() { + this.reset(); + this.db(); + app.onQuotaExceeded(); + Raven.captureMessage('QuotaExceededError', {level: 'warning'}); + } + + onVersionError() { + const req = indexedDB.open(NAME); + req.onsuccess = event => { + return this.handleVersionMismatch(event.target.result.version); + }; + req.onerror = function(event) { + event.preventDefault(); + return this.fail('cant_open', error); + }; + } + + handleVersionMismatch(actualVersion) { + if (Math.floor(actualVersion / this.versionMultipler) !== VERSION) { + this.fail('version'); + } else { + this.setUserVersion(actualVersion - (VERSION * this.versionMultipler)); + this.db(); + } + } + + buggyIDB(db) { + if (this.checkedBuggyIDB) { return; } + this.checkedBuggyIDB = true; + try { + this.idbTransaction(db, {stores: $.makeArray(db.objectStoreNames).slice(0, 2), mode: 'readwrite'}).abort(); // https://bugs.webkit.org/show_bug.cgi?id=136937 + return; + } catch (error) { + return error; + } + } + + runCallbacks(db) { + let fn; + while ((fn = this.callbacks.shift())) { fn(db); } + } + + onUpgradeNeeded(event) { + let db; + if (!(db = event.target.result)) { return; } + + const objectStoreNames = $.makeArray(db.objectStoreNames); + + if (!$.arrayDelete(objectStoreNames, 'docs')) { + try { db.createObjectStore('docs'); } catch (error) {} + } + + for (var doc of Array.from(app.docs.all())) { + if (!$.arrayDelete(objectStoreNames, doc.slug)) { + try { db.createObjectStore(doc.slug); } catch (error1) {} + } + } + + for (var name of Array.from(objectStoreNames)) { + try { db.deleteObjectStore(name); } catch (error2) {} + } + } + + store(doc, data, onSuccess, onError, _retry) { + if (_retry == null) { _retry = true; } + this.db(db => { + if (!db) { + onError(); + return; + } + + const txn = this.idbTransaction(db, {stores: ['docs', doc.slug], mode: 'readwrite', ignoreError: false}); + txn.oncomplete = () => { + if (this.cachedDocs != null) { + this.cachedDocs[doc.slug] = doc.mtime; + } + onSuccess(); + }; + txn.onerror = event => { + event.preventDefault(); + if (((txn.error != null ? txn.error.name : undefined) === 'NotFoundError') && _retry) { + this.migrate(); + setTimeout(() => { + return this.store(doc, data, onSuccess, onError, false); + } + , 0); + } else { + onError(event); + } + }; + + let store = txn.objectStore(doc.slug); + store.clear(); + for (var path in data) { var content = data[path]; store.add(content, path); } + + store = txn.objectStore('docs'); + store.put(doc.mtime, doc.slug); + }); + } + + unstore(doc, onSuccess, onError, _retry) { + if (_retry == null) { _retry = true; } + this.db(db => { + if (!db) { + onError(); + return; + } + + const txn = this.idbTransaction(db, {stores: ['docs', doc.slug], mode: 'readwrite', ignoreError: false}); + txn.oncomplete = () => { + if (this.cachedDocs != null) { + delete this.cachedDocs[doc.slug]; + } + onSuccess(); + }; + txn.onerror = function(event) { + event.preventDefault(); + if (((txn.error != null ? txn.error.name : undefined) === 'NotFoundError') && _retry) { + this.migrate(); + setTimeout(() => { + return this.unstore(doc, onSuccess, onError, false); + } + , 0); + } else { + onError(event); + } + }; + + let store = txn.objectStore('docs'); + store.delete(doc.slug); + + store = txn.objectStore(doc.slug); + store.clear(); + }); + } + + version(doc, fn) { + let version; + if ((version = this.cachedVersion(doc)) != null) { + fn(version); + return; + } + + this.db(db => { + if (!db) { + fn(false); + return; + } + + const txn = this.idbTransaction(db, {stores: ['docs'], mode: 'readonly'}); + const store = txn.objectStore('docs'); + + const req = store.get(doc.slug); + req.onsuccess = function() { + fn(req.result); + }; + req.onerror = function(event) { + event.preventDefault(); + fn(false); + }; + }); + } + + cachedVersion(doc) { + if (!this.cachedDocs) { return; } + return this.cachedDocs[doc.slug] || false; + } + + versions(docs, fn) { + let versions; + if (versions = this.cachedVersions(docs)) { + fn(versions); + return; + } + + return this.db(db => { + if (!db) { + fn(false); + return; + } + + const txn = this.idbTransaction(db, {stores: ['docs'], mode: 'readonly'}); + txn.oncomplete = function() { + fn(result); + }; + const store = txn.objectStore('docs'); + var result = {}; + + docs.forEach(function(doc) { + const req = store.get(doc.slug); + req.onsuccess = function() { + result[doc.slug] = req.result; + }; + req.onerror = function(event) { + event.preventDefault(); + result[doc.slug] = false; + }; + }); + }); + } + + cachedVersions(docs) { + if (!this.cachedDocs) { return; } + const result = {}; + for (var doc of Array.from(docs)) { result[doc.slug] = this.cachedVersion(doc); } + return result; + } + + load(entry, onSuccess, onError) { + if (this.shouldLoadWithIDB(entry)) { + onError = this.loadWithXHR.bind(this, entry, onSuccess, onError); + return this.loadWithIDB(entry, onSuccess, onError); + } else { + return this.loadWithXHR(entry, onSuccess, onError); + } + } + + loadWithXHR(entry, onSuccess, onError) { + return ajax({ + url: entry.fileUrl(), + dataType: 'html', + success: onSuccess, + error: onError + }); + } + + loadWithIDB(entry, onSuccess, onError) { + return this.db(db => { + if (!db) { + onError(); + return; + } + + if (!db.objectStoreNames.contains(entry.doc.slug)) { + onError(); + this.loadDocsCache(db); + return; + } + + const txn = this.idbTransaction(db, {stores: [entry.doc.slug], mode: 'readonly'}); + const store = txn.objectStore(entry.doc.slug); + + const req = store.get(entry.dbPath()); + req.onsuccess = function() { + if (req.result) { onSuccess(req.result); } else { onError(); } + }; + req.onerror = function(event) { + event.preventDefault(); + onError(); + }; + this.loadDocsCache(db); + }); + } + + loadDocsCache(db) { + if (this.cachedDocs) { return; } + this.cachedDocs = {}; + + const txn = this.idbTransaction(db, {stores: ['docs'], mode: 'readonly'}); + txn.oncomplete = () => { + setTimeout(this.checkForCorruptedDocs, 50); + }; + + const req = txn.objectStore('docs').openCursor(); + req.onsuccess = event => { + let cursor; + if (!(cursor = event.target.result)) { return; } + this.cachedDocs[cursor.key] = cursor.value; + cursor.continue(); + }; + req.onerror = function(event) { + event.preventDefault(); + }; + } + + checkForCorruptedDocs() { + this.db(db => { + let slug; + this.corruptedDocs = []; + const docs = ((() => { + const result = []; + for (var key in this.cachedDocs) { + var value = this.cachedDocs[key]; + if (value) { + result.push(key); + } + } + return result; + })()); + if (docs.length === 0) { return; } + + for (slug of Array.from(docs)) { + if (!app.docs.findBy('slug', slug)) { + this.corruptedDocs.push(slug); + } + } + + for (slug of Array.from(this.corruptedDocs)) { + $.arrayDelete(docs, slug); + } + + if (docs.length === 0) { + setTimeout(this.deleteCorruptedDocs, 0); + return; + } + + const txn = this.idbTransaction(db, {stores: docs, mode: 'readonly', ignoreError: false}); + txn.oncomplete = () => { + if (this.corruptedDocs.length > 0) { setTimeout(this.deleteCorruptedDocs, 0); } + }; + + for (var doc of Array.from(docs)) { + txn.objectStore(doc).get('index').onsuccess = event => { + if (!event.target.result) { this.corruptedDocs.push(event.target.source.name); } + }; + } + }); + } + + deleteCorruptedDocs() { + this.db(db => { + let doc; + const txn = this.idbTransaction(db, {stores: ['docs'], mode: 'readwrite', ignoreError: false}); + const store = txn.objectStore('docs'); + while ((doc = this.corruptedDocs.pop())) { + this.cachedDocs[doc] = false; + store.delete(doc); + } + }); + Raven.captureMessage('corruptedDocs', {level: 'info', extra: { docs: this.corruptedDocs.join(',') }}); + } + + shouldLoadWithIDB(entry) { + return this.useIndexedDB && (!this.cachedDocs || this.cachedDocs[entry.doc.slug]); + } + + idbTransaction(db, options) { + app.lastIDBTransaction = [options.stores, options.mode]; + const txn = db.transaction(options.stores, options.mode); + if (options.ignoreError !== false) { + txn.onerror = function(event) { + event.preventDefault(); + }; + } + if (options.ignoreAbort !== false) { + txn.onabort = function(event) { + event.preventDefault(); + }; + } + return txn; + } + + reset() { + try { if (typeof indexedDB !== 'undefined' && indexedDB !== null) { + indexedDB.deleteDatabase(NAME); + } } catch (error) {} + } + + useIndexedDB() { + try { + if (!app.isSingleDoc() && window.indexedDB) { + return true; + } else { + this.reason = 'not_supported'; + return false; + } + } catch (error) { + return false; + } + } + + migrate() { + app.settings.set('schema', this.userVersion() + 1); + } + + setUserVersion(version) { + app.settings.set('schema', version); + } + + userVersion() { + return app.settings.get('schema'); + } + }); + Cls.initClass(); + return Cls; +})(); diff --git a/assets/javascripts/app/router.js b/assets/javascripts/app/router.js index ba25148acd..8cd0607e9e 100644 --- a/assets/javascripts/app/router.js +++ b/assets/javascripts/app/router.js @@ -1,154 +1,199 @@ -class app.Router - $.extend @prototype, Events - - @routes: [ - ['*', 'before' ] - ['/', 'root' ] - ['/settings', 'settings' ] - ['/offline', 'offline' ] - ['/about', 'about' ] - ['/news', 'news' ] - ['/help', 'help' ] - ['/:doc-:type/', 'type' ] - ['/:doc/', 'doc' ] - ['/:doc/:path(*)', 'entry' ] - ['*', 'notFound' ] - ] - - constructor: -> - for [path, method] in @constructor.routes - page path, @[method].bind(@) - @setInitialPath() - - start: -> - page.start() - return - - show: (path) -> - page.show(path) - return - - triggerRoute: (name) -> - @trigger name, @context - @trigger 'after', name, @context - return - - before: (context, next) -> - previousContext = @context - @context = context - @trigger 'before', context - - if res = next() - @context = previousContext - return res - else - return - - doc: (context, next) -> - if doc = app.docs.findBySlug(context.params.doc) or app.disabledDocs.findBySlug(context.params.doc) - context.doc = doc - context.entry = doc.toEntry() - @triggerRoute 'entry' - return - else - return next() - - type: (context, next) -> - doc = app.docs.findBySlug(context.params.doc) - - if type = doc?.types.findBy 'slug', context.params.type - context.doc = doc - context.type = type - @triggerRoute 'type' - return - else - return next() - - entry: (context, next) -> - doc = app.docs.findBySlug(context.params.doc) - return next() unless doc - path = context.params.path - hash = context.hash - - if entry = doc.findEntryByPathAndHash(path, hash) - context.doc = doc - context.entry = entry - @triggerRoute 'entry' - return - else if path.slice(-6) is '/index' - path = path.substr(0, path.length - 6) - return entry.fullPath() if entry = doc.findEntryByPathAndHash(path, hash) - else - path = "#{path}/index" - return entry.fullPath() if entry = doc.findEntryByPathAndHash(path, hash) - - return next() - - root: -> - return '/' if app.isSingleDoc() - @triggerRoute 'root' - return - - settings: (context) -> - return "/#/#{context.path}" if app.isSingleDoc() - @triggerRoute 'settings' - return - - offline: (context)-> - return "/#/#{context.path}" if app.isSingleDoc() - @triggerRoute 'offline' - return - - about: (context) -> - return "/#/#{context.path}" if app.isSingleDoc() - context.page = 'about' - @triggerRoute 'page' - return - - news: (context) -> - return "/#/#{context.path}" if app.isSingleDoc() - context.page = 'news' - @triggerRoute 'page' - return - - help: (context) -> - return "/#/#{context.path}" if app.isSingleDoc() - context.page = 'help' - @triggerRoute 'page' - return - - notFound: (context) -> - @triggerRoute 'notFound' - return - - isIndex: -> - @context?.path is '/' or (app.isSingleDoc() and @context?.entry?.isIndex()) - - isSettings: -> - @context?.path is '/settings' - - setInitialPath: -> - # Remove superfluous forward slashes at the beginning of the path - if (path = location.pathname.replace /^\/{2,}/g, '/') isnt location.pathname - page.replace path + location.search + location.hash, null, true - - if location.pathname is '/' - if path = @getInitialPathFromHash() - page.replace path + location.search, null, true - else if path = @getInitialPathFromCookie() - page.replace path + location.search + location.hash, null, true - return - - getInitialPathFromHash: -> - try - (new RegExp "#/(.+)").exec(decodeURIComponent location.hash)?[1] - catch - - getInitialPathFromCookie: -> - if path = Cookies.get('initial_path') - Cookies.expire('initial_path') +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS103: Rewrite code to no longer use __guard__, or convert again using --optional-chaining + * DS206: Consider reworking classes to avoid initClass + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +const Cls = (app.Router = class Router { + static initClass() { + $.extend(this.prototype, Events); + + this.routes = [ + ['*', 'before' ], + ['/', 'root' ], + ['/settings', 'settings' ], + ['/offline', 'offline' ], + ['/about', 'about' ], + ['/news', 'news' ], + ['/help', 'help' ], + ['/:doc-:type/', 'type' ], + ['/:doc/', 'doc' ], + ['/:doc/:path(*)', 'entry' ], + ['*', 'notFound' ] + ]; + } + + constructor() { + for (var [path, method] of Array.from(this.constructor.routes)) { + page(path, this[method].bind(this)); + } + this.setInitialPath(); + } + + start() { + page.start(); + } + + show(path) { + page.show(path); + } + + triggerRoute(name) { + this.trigger(name, this.context); + this.trigger('after', name, this.context); + } + + before(context, next) { + let res; + const previousContext = this.context; + this.context = context; + this.trigger('before', context); + + if (res = next()) { + this.context = previousContext; + return res; + } else { + return; + } + } + + doc(context, next) { + let doc; + if (doc = app.docs.findBySlug(context.params.doc) || app.disabledDocs.findBySlug(context.params.doc)) { + context.doc = doc; + context.entry = doc.toEntry(); + this.triggerRoute('entry'); + return; + } else { + return next(); + } + } + + type(context, next) { + let type; + const doc = app.docs.findBySlug(context.params.doc); + + if (type = doc != null ? doc.types.findBy('slug', context.params.type) : undefined) { + context.doc = doc; + context.type = type; + this.triggerRoute('type'); + return; + } else { + return next(); + } + } + + entry(context, next) { + let entry; + const doc = app.docs.findBySlug(context.params.doc); + if (!doc) { return next(); } + let { path - - replaceHash: (hash) -> - page.replace location.pathname + location.search + (hash or ''), null, true - return + } = context.params; + const { + hash + } = context; + + if (entry = doc.findEntryByPathAndHash(path, hash)) { + context.doc = doc; + context.entry = entry; + this.triggerRoute('entry'); + return; + } else if (path.slice(-6) === '/index') { + path = path.substr(0, path.length - 6); + if (entry = doc.findEntryByPathAndHash(path, hash)) { return entry.fullPath(); } + } else { + path = `${path}/index`; + if (entry = doc.findEntryByPathAndHash(path, hash)) { return entry.fullPath(); } + } + + return next(); + } + + root() { + if (app.isSingleDoc()) { return '/'; } + this.triggerRoute('root'); + } + + settings(context) { + if (app.isSingleDoc()) { return `/#/${context.path}`; } + this.triggerRoute('settings'); + } + + offline(context){ + if (app.isSingleDoc()) { return `/#/${context.path}`; } + this.triggerRoute('offline'); + } + + about(context) { + if (app.isSingleDoc()) { return `/#/${context.path}`; } + context.page = 'about'; + this.triggerRoute('page'); + } + + news(context) { + if (app.isSingleDoc()) { return `/#/${context.path}`; } + context.page = 'news'; + this.triggerRoute('page'); + } + + help(context) { + if (app.isSingleDoc()) { return `/#/${context.path}`; } + context.page = 'help'; + this.triggerRoute('page'); + } + + notFound(context) { + this.triggerRoute('notFound'); + } + + isIndex() { + return ((this.context != null ? this.context.path : undefined) === '/') || (app.isSingleDoc() && __guard__(this.context != null ? this.context.entry : undefined, x => x.isIndex())); + } + + isSettings() { + return (this.context != null ? this.context.path : undefined) === '/settings'; + } + + setInitialPath() { + // Remove superfluous forward slashes at the beginning of the path + let path; + if ((path = location.pathname.replace(/^\/{2,}/g, '/')) !== location.pathname) { + page.replace(path + location.search + location.hash, null, true); + } + + if (location.pathname === '/') { + if (path = this.getInitialPathFromHash()) { + page.replace(path + location.search, null, true); + } else if (path = this.getInitialPathFromCookie()) { + page.replace(path + location.search + location.hash, null, true); + } + } + } + + getInitialPathFromHash() { + try { + return __guard__((new RegExp("#/(.+)")).exec(decodeURIComponent(location.hash)), x => x[1]); + } catch (error) {} + } + + getInitialPathFromCookie() { + let path; + if (path = Cookies.get('initial_path')) { + Cookies.expire('initial_path'); + return path; + } + } + + replaceHash(hash) { + page.replace(location.pathname + location.search + (hash || ''), null, true); + } +}); +Cls.initClass(); + +function __guard__(value, transform) { + return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; +} \ No newline at end of file diff --git a/assets/javascripts/app/searcher.js b/assets/javascripts/app/searcher.js index 79f6a304c3..27555a1619 100644 --- a/assets/javascripts/app/searcher.js +++ b/assets/javascripts/app/searcher.js @@ -1,292 +1,373 @@ -# -# Match functions -# - -SEPARATOR = '.' - -query = -queryLength = -value = -valueLength = -matcher = # current match function -fuzzyRegexp = # query fuzzy regexp -index = # position of the query in the string being matched -lastIndex = # last position of the query in the string being matched -match = # regexp match data -matchIndex = -matchLength = -score = # score for the current match -separators = # counter -i = null # cursor - -`function exactMatch() {` -index = value.indexOf(query) -return unless index >= 0 - -lastIndex = value.lastIndexOf(query) - -if index isnt lastIndex - return Math.max(scoreExactMatch(), ((index = lastIndex) and scoreExactMatch()) or 0) -else - return scoreExactMatch() -`}` - -`function scoreExactMatch() {` -# Remove one point for each unmatched character. -score = 100 - (valueLength - queryLength) - -if index > 0 - # If the character preceding the query is a dot, assign the same score - # as if the query was found at the beginning of the string, minus one. - if value.charAt(index - 1) is SEPARATOR - score += index - 1 - # Don't match a single-character query unless it's found at the beginning - # of the string or is preceded by a dot. - else if queryLength is 1 - return - # (1) Remove one point for each unmatched character up to the nearest - # preceding dot or the beginning of the string. - # (2) Remove one point for each unmatched character following the query. - else - i = index - 2 - i-- while i >= 0 and value.charAt(i) isnt SEPARATOR - score -= (index - i) + # (1) - (valueLength - queryLength - index) # (2) - - # Remove one point for each dot preceding the query, except for the one - # immediately before the query. - separators = 0 - i = index - 2 - while i >= 0 - separators++ if value.charAt(i) is SEPARATOR - i-- - score -= separators - -# Remove five points for each dot following the query. -separators = 0 -i = valueLength - queryLength - index - 1 -while i >= 0 - separators++ if value.charAt(index + queryLength + i) is SEPARATOR - i-- -score -= separators * 5 - -return Math.max 1, score -`}` - -`function fuzzyMatch() {` -return if valueLength <= queryLength or value.indexOf(query) >= 0 -return unless match = fuzzyRegexp.exec(value) -matchIndex = match.index -matchLength = match[0].length -score = scoreFuzzyMatch() -if match = fuzzyRegexp.exec(value.slice(i = value.lastIndexOf(SEPARATOR) + 1)) - matchIndex = i + match.index - matchLength = match[0].length - return Math.max(score, scoreFuzzyMatch()) -else - return score -`}` - -`function scoreFuzzyMatch() {` -# When the match is at the beginning of the string or preceded by a dot. -if matchIndex is 0 or value.charAt(matchIndex - 1) is SEPARATOR - return Math.max 66, 100 - matchLength -# When the match is at the end of the string. -else if matchIndex + matchLength is valueLength - return Math.max 33, 67 - matchLength -# When the match is in the middle of the string. -else - return Math.max 1, 34 - matchLength -`}` - -# -# Searchers -# - -class app.Searcher - $.extend @prototype, Events - - CHUNK_SIZE = 20000 - - DEFAULTS = - max_results: app.config.max_results - fuzzy_min_length: 3 - - SEPARATORS_REGEXP = /#|::|:-|->|\$(?=\w)|\-(?=\w)|\:(?=\w)|\ [\/\-&]\ |:\ |\ /g - EOS_SEPARATORS_REGEXP = /(\w)[\-:]$/ - INFO_PARANTHESES_REGEXP = /\ \(\w+?\)$/ - EMPTY_PARANTHESES_REGEXP = /\(\)/ - EVENT_REGEXP = /\ event$/ - DOT_REGEXP = /\.+/g - WHITESPACE_REGEXP = /\s/g - - EMPTY_STRING = '' - ELLIPSIS = '...' - STRING = 'string' - - @normalizeString: (string) -> - string - .toLowerCase() - .replace ELLIPSIS, EMPTY_STRING - .replace EVENT_REGEXP, EMPTY_STRING - .replace INFO_PARANTHESES_REGEXP, EMPTY_STRING - .replace SEPARATORS_REGEXP, SEPARATOR - .replace DOT_REGEXP, SEPARATOR - .replace EMPTY_PARANTHESES_REGEXP, EMPTY_STRING - .replace WHITESPACE_REGEXP, EMPTY_STRING - - @normalizeQuery: (string) -> - string = @normalizeString(string) - string.replace EOS_SEPARATORS_REGEXP, '$1.' - - constructor: (options = {}) -> - @options = $.extend {}, DEFAULTS, options - - find: (data, attr, q) -> - @kill() - - @data = data - @attr = attr - @query = q - @setup() - - if @isValid() then @match() else @end() - return - - setup: -> - query = @query = @constructor.normalizeQuery(@query) - queryLength = query.length - @dataLength = @data.length - @matchers = [exactMatch] - @totalResults = 0 - @setupFuzzy() - return - - setupFuzzy: -> - if queryLength >= @options.fuzzy_min_length - fuzzyRegexp = @queryToFuzzyRegexp(query) - @matchers.push(fuzzyMatch) - else - fuzzyRegexp = null - return - - isValid: -> - queryLength > 0 and query isnt SEPARATOR - - end: -> - @triggerResults [] unless @totalResults - @trigger 'end' - @free() - return - - kill: -> - if @timeout - clearTimeout @timeout - @free() - return - - free: -> - @data = @attr = @dataLength = @matchers = @matcher = @query = - @totalResults = @scoreMap = @cursor = @timeout = null - return - - match: => - if not @foundEnough() and @matcher = @matchers.shift() - @setupMatcher() - @matchChunks() - else - @end() - return - - setupMatcher: -> - @cursor = 0 - @scoreMap = new Array(101) - return - - matchChunks: => - @matchChunk() - - if @cursor is @dataLength or @scoredEnough() - @delay @match - @sendResults() - else - @delay @matchChunks - return - - matchChunk: -> - matcher = @matcher - for [0...@chunkSize()] - value = @data[@cursor][@attr] - if value.split # string - valueLength = value.length - @addResult(@data[@cursor], score) if score = matcher() - else # array - score = 0 - for value in @data[@cursor][@attr] - valueLength = value.length - score = Math.max(score, matcher() || 0) - @addResult(@data[@cursor], score) if score > 0 - @cursor++ - return - - chunkSize: -> - if @cursor + CHUNK_SIZE > @dataLength - @dataLength % CHUNK_SIZE - else - CHUNK_SIZE - - scoredEnough: -> - @scoreMap[100]?.length >= @options.max_results - - foundEnough: -> - @totalResults >= @options.max_results - - addResult: (object, score) -> - (@scoreMap[Math.round(score)] or= []).push(object) - @totalResults++ - return - - getResults: -> - results = [] - for objects in @scoreMap by -1 when objects - results.push.apply results, objects - results[0...@options.max_results] - - sendResults: -> - results = @getResults() - @triggerResults results if results.length - return - - triggerResults: (results) -> - @trigger 'results', results - return - - delay: (fn) -> - @timeout = setTimeout(fn, 1) - - queryToFuzzyRegexp: (string) -> - chars = string.split '' - chars[i] = $.escapeRegexp(char) for char, i in chars - new RegExp chars.join('.*?') # abc -> /a.*?b.*?c.*?/ - -class app.SynchronousSearcher extends app.Searcher - match: => - if @matcher - @allResults or= [] - @allResults.push.apply @allResults, @getResults() - super - - free: -> - @allResults = null - super - - end: -> - @sendResults true - super - - sendResults: (end) -> - if end and @allResults?.length - @triggerResults @allResults - - delay: (fn) -> - fn() +/* + * decaffeinate suggestions: + * DS002: Fix invalid constructor + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS104: Avoid inline assignments + * DS202: Simplify dynamic range loops + * DS206: Consider reworking classes to avoid initClass + * DS207: Consider shorter variations of null checks + * DS209: Avoid top-level return + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +// +// Match functions +// + +let fuzzyRegexp, i, index, lastIndex, match, matcher, matchIndex, matchLength, queryLength, score, separators, value, valueLength; +const SEPARATOR = '.'; + +let query = +(queryLength = +(value = +(valueLength = +(matcher = // current match function +(fuzzyRegexp = // query fuzzy regexp +(index = // position of the query in the string being matched +(lastIndex = // last position of the query in the string being matched +(match = // regexp match data +(matchIndex = +(matchLength = +(score = // score for the current match +(separators = // counter +(i = null))))))))))))); // cursor + +function exactMatch() { +index = value.indexOf(query); +if (!(index >= 0)) { return; } + +lastIndex = value.lastIndexOf(query); + +if (index !== lastIndex) { + return Math.max(scoreExactMatch(), ((index = lastIndex) && scoreExactMatch()) || 0); +} else { + return scoreExactMatch(); +} +} + +function scoreExactMatch() { +// Remove one point for each unmatched character. +score = 100 - (valueLength - queryLength); + +if (index > 0) { + // If the character preceding the query is a dot, assign the same score + // as if the query was found at the beginning of the string, minus one. + if (value.charAt(index - 1) === SEPARATOR) { + score += index - 1; + // Don't match a single-character query unless it's found at the beginning + // of the string or is preceded by a dot. + } else if (queryLength === 1) { + return; + // (1) Remove one point for each unmatched character up to the nearest + // preceding dot or the beginning of the string. + // (2) Remove one point for each unmatched character following the query. + } else { + i = index - 2; + while ((i >= 0) && (value.charAt(i) !== SEPARATOR)) { i--; } + score -= (index - i) + // (1) + (valueLength - queryLength - index); // (2) + } + + // Remove one point for each dot preceding the query, except for the one + // immediately before the query. + separators = 0; + i = index - 2; + while (i >= 0) { + if (value.charAt(i) === SEPARATOR) { separators++; } + i--; + } + score -= separators; +} + +// Remove five points for each dot following the query. +separators = 0; +i = valueLength - queryLength - index - 1; +while (i >= 0) { + if (value.charAt(index + queryLength + i) === SEPARATOR) { separators++; } + i--; +} +score -= separators * 5; + +return Math.max(1, score); +} + +function fuzzyMatch() { +if ((valueLength <= queryLength) || (value.indexOf(query) >= 0)) { return; } +if (!(match = fuzzyRegexp.exec(value))) { return; } +matchIndex = match.index; +matchLength = match[0].length; +score = scoreFuzzyMatch(); +if (match = fuzzyRegexp.exec(value.slice(i = value.lastIndexOf(SEPARATOR) + 1))) { + matchIndex = i + match.index; + matchLength = match[0].length; + return Math.max(score, scoreFuzzyMatch()); +} else { + return score; +} +} + +function scoreFuzzyMatch() { +// When the match is at the beginning of the string or preceded by a dot. +if ((matchIndex === 0) || (value.charAt(matchIndex - 1) === SEPARATOR)) { + return Math.max(66, 100 - matchLength); +// When the match is at the end of the string. +} else if ((matchIndex + matchLength) === valueLength) { + return Math.max(33, 67 - matchLength); +// When the match is in the middle of the string. +} else { + return Math.max(1, 34 - matchLength); +} +} + +// +// Searchers +// + +(function() { + let CHUNK_SIZE = undefined; + let DEFAULTS = undefined; + let SEPARATORS_REGEXP = undefined; + let EOS_SEPARATORS_REGEXP = undefined; + let INFO_PARANTHESES_REGEXP = undefined; + let EMPTY_PARANTHESES_REGEXP = undefined; + let EVENT_REGEXP = undefined; + let DOT_REGEXP = undefined; + let WHITESPACE_REGEXP = undefined; + let EMPTY_STRING = undefined; + let ELLIPSIS = undefined; + let STRING = undefined; + const Cls = (app.Searcher = class Searcher { + static initClass() { + $.extend(this.prototype, Events); + + CHUNK_SIZE = 20000; + + DEFAULTS = { + max_results: app.config.max_results, + fuzzy_min_length: 3 + }; + + SEPARATORS_REGEXP = /#|::|:-|->|\$(?=\w)|\-(?=\w)|\:(?=\w)|\ [\/\-&]\ |:\ |\ /g; + EOS_SEPARATORS_REGEXP = /(\w)[\-:]$/; + INFO_PARANTHESES_REGEXP = /\ \(\w+?\)$/; + EMPTY_PARANTHESES_REGEXP = /\(\)/; + EVENT_REGEXP = /\ event$/; + DOT_REGEXP = /\.+/g; + WHITESPACE_REGEXP = /\s/g; + + EMPTY_STRING = ''; + ELLIPSIS = '...'; + STRING = 'string'; + } + + static normalizeString(string) { + return string + .toLowerCase() + .replace(ELLIPSIS, EMPTY_STRING) + .replace(EVENT_REGEXP, EMPTY_STRING) + .replace(INFO_PARANTHESES_REGEXP, EMPTY_STRING) + .replace(SEPARATORS_REGEXP, SEPARATOR) + .replace(DOT_REGEXP, SEPARATOR) + .replace(EMPTY_PARANTHESES_REGEXP, EMPTY_STRING) + .replace(WHITESPACE_REGEXP, EMPTY_STRING); + } + + static normalizeQuery(string) { + string = this.normalizeString(string); + return string.replace(EOS_SEPARATORS_REGEXP, '$1.'); + } + + constructor(options) { + this.match = this.match.bind(this); + this.matchChunks = this.matchChunks.bind(this); + if (options == null) { options = {}; } + this.options = $.extend({}, DEFAULTS, options); + } + + find(data, attr, q) { + this.kill(); + + this.data = data; + this.attr = attr; + this.query = q; + this.setup(); + + if (this.isValid()) { this.match(); } else { this.end(); } + } + + setup() { + query = (this.query = this.constructor.normalizeQuery(this.query)); + queryLength = query.length; + this.dataLength = this.data.length; + this.matchers = [exactMatch]; + this.totalResults = 0; + this.setupFuzzy(); + } + + setupFuzzy() { + if (queryLength >= this.options.fuzzy_min_length) { + fuzzyRegexp = this.queryToFuzzyRegexp(query); + this.matchers.push(fuzzyMatch); + } else { + fuzzyRegexp = null; + } + } + + isValid() { + return (queryLength > 0) && (query !== SEPARATOR); + } + + end() { + if (!this.totalResults) { this.triggerResults([]); } + this.trigger('end'); + this.free(); + } + + kill() { + if (this.timeout) { + clearTimeout(this.timeout); + this.free(); + } + } + + free() { + this.data = (this.attr = (this.dataLength = (this.matchers = (this.matcher = (this.query = + (this.totalResults = (this.scoreMap = (this.cursor = (this.timeout = null))))))))); + } + + match() { + if (!this.foundEnough() && (this.matcher = this.matchers.shift())) { + this.setupMatcher(); + this.matchChunks(); + } else { + this.end(); + } + } + + setupMatcher() { + this.cursor = 0; + this.scoreMap = new Array(101); + } + + matchChunks() { + this.matchChunk(); + + if ((this.cursor === this.dataLength) || this.scoredEnough()) { + this.delay(this.match); + this.sendResults(); + } else { + this.delay(this.matchChunks); + } + } + + matchChunk() { + ({ + matcher + } = this); + for (let j = 0, end = this.chunkSize(), asc = 0 <= end; asc ? j < end : j > end; asc ? j++ : j--) { + value = this.data[this.cursor][this.attr]; + if (value.split) { // string + valueLength = value.length; + if (score = matcher()) { this.addResult(this.data[this.cursor], score); } + } else { // array + score = 0; + for (value of Array.from(this.data[this.cursor][this.attr])) { + valueLength = value.length; + score = Math.max(score, matcher() || 0); + } + if (score > 0) { this.addResult(this.data[this.cursor], score); } + } + this.cursor++; + } + } + + chunkSize() { + if ((this.cursor + CHUNK_SIZE) > this.dataLength) { + return this.dataLength % CHUNK_SIZE; + } else { + return CHUNK_SIZE; + } + } + + scoredEnough() { + return (this.scoreMap[100] != null ? this.scoreMap[100].length : undefined) >= this.options.max_results; + } + + foundEnough() { + return this.totalResults >= this.options.max_results; + } + + addResult(object, score) { + let name; + (this.scoreMap[name = Math.round(score)] || (this.scoreMap[name] = [])).push(object); + this.totalResults++; + } + + getResults() { + const results = []; + for (let j = this.scoreMap.length - 1; j >= 0; j--) { + var objects = this.scoreMap[j]; + if (objects) { + results.push.apply(results, objects); + } + } + return results.slice(0, this.options.max_results); + } + + sendResults() { + const results = this.getResults(); + if (results.length) { this.triggerResults(results); } + } + + triggerResults(results) { + this.trigger('results', results); + } + + delay(fn) { + return this.timeout = setTimeout(fn, 1); + } + + queryToFuzzyRegexp(string) { + const chars = string.split(''); + for (i = 0; i < chars.length; i++) { var char = chars[i]; chars[i] = $.escapeRegexp(char); } + return new RegExp(chars.join('.*?')); + } + }); + Cls.initClass(); + return Cls; // abc -> /a.*?b.*?c.*?/ +})(); + +app.SynchronousSearcher = class SynchronousSearcher extends app.Searcher { + constructor(...args) { + this.match = this.match.bind(this); + super(...args); + } + + match() { + if (this.matcher) { + if (!this.allResults) { this.allResults = []; } + this.allResults.push.apply(this.allResults, this.getResults()); + } + return super.match(...arguments); + } + + free() { + this.allResults = null; + return super.free(...arguments); + } + + end() { + this.sendResults(true); + return super.end(...arguments); + } + + sendResults(end) { + if (end && (this.allResults != null ? this.allResults.length : undefined)) { + return this.triggerResults(this.allResults); + } + } + + delay(fn) { + return fn(); + } +}; diff --git a/assets/javascripts/app/serviceworker.js b/assets/javascripts/app/serviceworker.js index 4023556658..388bdcbf1f 100644 --- a/assets/javascripts/app/serviceworker.js +++ b/assets/javascripts/app/serviceworker.js @@ -1,49 +1,66 @@ -class app.ServiceWorker - $.extend @prototype, Events +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS206: Consider reworking classes to avoid initClass + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +const Cls = (app.ServiceWorker = class ServiceWorker { + static initClass() { + $.extend(this.prototype, Events); + } - @isEnabled: -> - !!navigator.serviceWorker and app.config.service_worker_enabled + static isEnabled() { + return !!navigator.serviceWorker && app.config.service_worker_enabled; + } - constructor: -> - @registration = null - @notifyUpdate = true + constructor() { + this.onUpdateFound = this.onUpdateFound.bind(this); + this.onStateChange = this.onStateChange.bind(this); + this.registration = null; + this.notifyUpdate = true; navigator.serviceWorker.register(app.config.service_worker_path, {scope: '/'}) .then( - (registration) => @updateRegistration(registration), - (error) -> console.error('Could not register service worker:', error) - ) - - update: -> - return unless @registration - @notifyUpdate = true - return @registration.update().catch(->) - - updateInBackground: -> - return unless @registration - @notifyUpdate = false - return @registration.update().catch(->) - - reload: -> - return @updateInBackground().then(() -> app.reboot()) - - updateRegistration: (registration) -> - @registration = registration - $.on @registration, 'updatefound', @onUpdateFound - return - - onUpdateFound: => - $.off @installingRegistration, 'statechange', @onStateChange() if @installingRegistration - @installingRegistration = @registration.installing - $.on @installingRegistration, 'statechange', @onStateChange - return - - onStateChange: => - if @installingRegistration and @installingRegistration.state == 'installed' and navigator.serviceWorker.controller - @installingRegistration = null - @onUpdateReady() - return - - onUpdateReady: -> - @trigger 'updateready' if @notifyUpdate - return + registration => this.updateRegistration(registration), + error => console.error('Could not register service worker:', error)); + } + + update() { + if (!this.registration) { return; } + this.notifyUpdate = true; + return this.registration.update().catch(function() {}); + } + + updateInBackground() { + if (!this.registration) { return; } + this.notifyUpdate = false; + return this.registration.update().catch(function() {}); + } + + reload() { + return this.updateInBackground().then(() => app.reboot()); + } + + updateRegistration(registration) { + this.registration = registration; + $.on(this.registration, 'updatefound', this.onUpdateFound); + } + + onUpdateFound() { + if (this.installingRegistration) { $.off(this.installingRegistration, 'statechange', this.onStateChange()); } + this.installingRegistration = this.registration.installing; + $.on(this.installingRegistration, 'statechange', this.onStateChange); + } + + onStateChange() { + if (this.installingRegistration && (this.installingRegistration.state === 'installed') && navigator.serviceWorker.controller) { + this.installingRegistration = null; + this.onUpdateReady(); + } + } + + onUpdateReady() { + if (this.notifyUpdate) { this.trigger('updateready'); } + } +}); +Cls.initClass(); diff --git a/assets/javascripts/app/settings.js b/assets/javascripts/app/settings.js index 74e32a658c..aed1135fc9 100644 --- a/assets/javascripts/app/settings.js +++ b/assets/javascripts/app/settings.js @@ -1,170 +1,219 @@ -class app.Settings - PREFERENCE_KEYS = [ - 'hideDisabled' - 'hideIntro' - 'manualUpdate' - 'fastScroll' - 'arrowScroll' - 'analyticsConsent' - 'docs' - 'dark' # legacy - 'theme' - 'layout' - 'size' - 'tips' - 'noAutofocus' - 'autoInstall' - 'spaceScroll' - 'spaceTimeout' - ] - - INTERNAL_KEYS = [ - 'count' - 'schema' - 'version' - 'news' - ] - - LAYOUTS: [ - '_max-width' - '_sidebar-hidden' - '_native-scrollbars' - '_text-justify-hyphenate' - ] - - @defaults: - count: 0 - hideDisabled: false - hideIntro: false - news: 0 - manualUpdate: false - schema: 1 - analyticsConsent: false - theme: 'auto' - spaceScroll: 1 - spaceTimeout: 0.5 - - constructor: -> - @store = new CookiesStore - @cache = {} - @autoSupported = window.matchMedia('(prefers-color-scheme)').media != 'not all' - if @autoSupported - @darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)') - @darkModeQuery.addListener => @setTheme(@get('theme')) - - - get: (key) -> - return @cache[key] if @cache.hasOwnProperty(key) - @cache[key] = @store.get(key) ? @constructor.defaults[key] - if key == 'theme' and @cache[key] == 'auto' and !@darkModeQuery - @cache[key] = 'default' - else - @cache[key] - - set: (key, value) -> - @store.set(key, value) - delete @cache[key] - @setTheme(value) if key == 'theme' - return - - del: (key) -> - @store.del(key) - delete @cache[key] - return - - hasDocs: -> - try !!@store.get('docs') - - getDocs: -> - @store.get('docs')?.split('/') or app.config.default_docs - - setDocs: (docs) -> - @set 'docs', docs.join('/') - return - - getTips: -> - @store.get('tips')?.split('/') or [] - - setTips: (tips) -> - @set 'tips', tips.join('/') - return - - setLayout: (name, enable) -> - @toggleLayout(name, enable) - - layout = (@store.get('layout') || '').split(' ') - $.arrayDelete(layout, '') - - if enable - layout.push(name) if layout.indexOf(name) is -1 - else - $.arrayDelete(layout, name) - - if layout.length > 0 - @set 'layout', layout.join(' ') - else - @del 'layout' - return - - hasLayout: (name) -> - layout = (@store.get('layout') || '').split(' ') - layout.indexOf(name) isnt -1 - - setSize: (value) -> - @set 'size', value - return - - dump: -> - @store.dump() - - export: -> - data = @dump() - delete data[key] for key in INTERNAL_KEYS - data - - import: (data) -> - for key, value of @export() - @del key unless data.hasOwnProperty(key) - for key, value of data - @set key, value if PREFERENCE_KEYS.indexOf(key) isnt -1 - return - - reset: -> - @store.reset() - @cache = {} - return - - initLayout: -> - if @get('dark') is 1 - @set('theme', 'dark') - @del 'dark' - @setTheme(@get('theme')) - @toggleLayout(layout, @hasLayout(layout)) for layout in @LAYOUTS - @initSidebarWidth() - return - - setTheme: (theme) -> - if theme is 'auto' - theme = if @darkModeQuery.matches then 'dark' else 'default' - classList = document.documentElement.classList - classList.remove('_theme-default', '_theme-dark') - classList.add('_theme-' + theme) - @updateColorMeta() - return - - updateColorMeta: -> - color = getComputedStyle(document.documentElement).getPropertyValue('--headerBackground').trim() - $('meta[name=theme-color]').setAttribute('content', color) - return - - toggleLayout: (layout, enable) -> - classList = document.body.classList - # sidebar is always shown for settings; its state is updated in app.views.Settings - classList.toggle(layout, enable) unless layout is '_sidebar-hidden' and app.router?.isSettings - classList.toggle('_overlay-scrollbars', $.overlayScrollbarsEnabled()) - return - - initSidebarWidth: -> - size = @get('size') - document.documentElement.style.setProperty('--sidebarWidth', size + 'px') if size - return +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS103: Rewrite code to no longer use __guard__, or convert again using --optional-chaining + * DS104: Avoid inline assignments + * DS206: Consider reworking classes to avoid initClass + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +(function() { + let PREFERENCE_KEYS = undefined; + let INTERNAL_KEYS = undefined; + const Cls = (app.Settings = class Settings { + static initClass() { + PREFERENCE_KEYS = [ + 'hideDisabled', + 'hideIntro', + 'manualUpdate', + 'fastScroll', + 'arrowScroll', + 'analyticsConsent', + 'docs', + 'dark', // legacy + 'theme', + 'layout', + 'size', + 'tips', + 'noAutofocus', + 'autoInstall', + 'spaceScroll', + 'spaceTimeout' + ]; + + INTERNAL_KEYS = [ + 'count', + 'schema', + 'version', + 'news' + ]; + + this.prototype.LAYOUTS = [ + '_max-width', + '_sidebar-hidden', + '_native-scrollbars', + '_text-justify-hyphenate' + ]; + + this.defaults = { + count: 0, + hideDisabled: false, + hideIntro: false, + news: 0, + manualUpdate: false, + schema: 1, + analyticsConsent: false, + theme: 'auto', + spaceScroll: 1, + spaceTimeout: 0.5 + }; + } + + constructor() { + this.store = new CookiesStore; + this.cache = {}; + this.autoSupported = window.matchMedia('(prefers-color-scheme)').media !== 'not all'; + if (this.autoSupported) { + this.darkModeQuery = window.matchMedia('(prefers-color-scheme: dark)'); + this.darkModeQuery.addListener(() => this.setTheme(this.get('theme'))); + } + } + + + get(key) { + let left; + if (this.cache.hasOwnProperty(key)) { return this.cache[key]; } + this.cache[key] = (left = this.store.get(key)) != null ? left : this.constructor.defaults[key]; + if ((key === 'theme') && (this.cache[key] === 'auto') && !this.darkModeQuery) { + return this.cache[key] = 'default'; + } else { + return this.cache[key]; + } + } + + set(key, value) { + this.store.set(key, value); + delete this.cache[key]; + if (key === 'theme') { this.setTheme(value); } + } + + del(key) { + this.store.del(key); + delete this.cache[key]; + } + + hasDocs() { + try { return !!this.store.get('docs'); } catch (error) {} + } + + getDocs() { + return __guard__(this.store.get('docs'), x => x.split('/')) || app.config.default_docs; + } + + setDocs(docs) { + this.set('docs', docs.join('/')); + } + + getTips() { + return __guard__(this.store.get('tips'), x => x.split('/')) || []; + } + + setTips(tips) { + this.set('tips', tips.join('/')); + } + + setLayout(name, enable) { + this.toggleLayout(name, enable); + + const layout = (this.store.get('layout') || '').split(' '); + $.arrayDelete(layout, ''); + + if (enable) { + if (layout.indexOf(name) === -1) { layout.push(name); } + } else { + $.arrayDelete(layout, name); + } + + if (layout.length > 0) { + this.set('layout', layout.join(' ')); + } else { + this.del('layout'); + } + } + + hasLayout(name) { + const layout = (this.store.get('layout') || '').split(' '); + return layout.indexOf(name) !== -1; + } + + setSize(value) { + this.set('size', value); + } + + dump() { + return this.store.dump(); + } + + export() { + const data = this.dump(); + for (var key of Array.from(INTERNAL_KEYS)) { delete data[key]; } + return data; + } + + import(data) { + let key, value; + const object = this.export(); + for (key in object) { + value = object[key]; + if (!data.hasOwnProperty(key)) { this.del(key); } + } + for (key in data) { + value = data[key]; + if (PREFERENCE_KEYS.indexOf(key) !== -1) { this.set(key, value); } + } + } + + reset() { + this.store.reset(); + this.cache = {}; + } + + initLayout() { + if (this.get('dark') === 1) { + this.set('theme', 'dark'); + this.del('dark'); + } + this.setTheme(this.get('theme')); + for (var layout of Array.from(this.LAYOUTS)) { this.toggleLayout(layout, this.hasLayout(layout)); } + this.initSidebarWidth(); + } + + setTheme(theme) { + if (theme === 'auto') { + theme = this.darkModeQuery.matches ? 'dark' : 'default'; + } + const { + classList + } = document.documentElement; + classList.remove('_theme-default', '_theme-dark'); + classList.add('_theme-' + theme); + this.updateColorMeta(); + } + + updateColorMeta() { + const color = getComputedStyle(document.documentElement).getPropertyValue('--headerBackground').trim(); + $('meta[name=theme-color]').setAttribute('content', color); + } + + toggleLayout(layout, enable) { + const { + classList + } = document.body; + // sidebar is always shown for settings; its state is updated in app.views.Settings + if ((layout !== '_sidebar-hidden') || !(app.router != null ? app.router.isSettings : undefined)) { classList.toggle(layout, enable); } + classList.toggle('_overlay-scrollbars', $.overlayScrollbarsEnabled()); + } + + initSidebarWidth() { + const size = this.get('size'); + if (size) { document.documentElement.style.setProperty('--sidebarWidth', size + 'px'); } + } + }); + Cls.initClass(); + return Cls; +})(); + +function __guard__(value, transform) { + return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; +} \ No newline at end of file diff --git a/assets/javascripts/app/shortcuts.js b/assets/javascripts/app/shortcuts.js index 28ddf0b8a2..5ae46708ea 100644 --- a/assets/javascripts/app/shortcuts.js +++ b/assets/javascripts/app/shortcuts.js @@ -1,193 +1,259 @@ -class app.Shortcuts - $.extend @prototype, Events - - constructor: -> - @isMac = $.isMac() - @start() - - start: -> - $.on document, 'keydown', @onKeydown - $.on document, 'keypress', @onKeypress - return - - stop: -> - $.off document, 'keydown', @onKeydown - $.off document, 'keypress', @onKeypress - return - - swapArrowKeysBehavior: -> - app.settings.get('arrowScroll') - - spaceScroll: -> - app.settings.get('spaceScroll') - - showTip: -> - app.showTip('KeyNav') - @showTip = null - - spaceTimeout: -> - app.settings.get('spaceTimeout') - - onKeydown: (event) => - return if @buggyEvent(event) - result = if event.ctrlKey or event.metaKey - @handleKeydownSuperEvent event unless event.altKey or event.shiftKey - else if event.shiftKey - @handleKeydownShiftEvent event unless event.altKey - else if event.altKey - @handleKeydownAltEvent event - else - @handleKeydownEvent event - - event.preventDefault() if result is false - return - - onKeypress: (event) => - return if @buggyEvent(event) or (event.charCode == 63 and document.activeElement.tagName == 'INPUT') - unless event.ctrlKey or event.metaKey - result = @handleKeypressEvent event - event.preventDefault() if result is false - return - - handleKeydownEvent: (event, _force) -> - return @handleKeydownAltEvent(event, true) if not _force and event.which in [37, 38, 39, 40] and @swapArrowKeysBehavior() - - if not event.target.form and (48 <= event.which <= 57 or 65 <= event.which <= 90) - @trigger 'typing' - return - - switch event.which - when 8 - @trigger 'typing' unless event.target.form - when 13 - @trigger 'enter' - when 27 - @trigger 'escape' - false - when 32 - if event.target.type is 'search' and @spaceScroll() and (not @lastKeypress or @lastKeypress < Date.now() - (@spaceTimeout() * 1000)) - @trigger 'pageDown' - false - when 33 - @trigger 'pageUp' - when 34 - @trigger 'pageDown' - when 35 - @trigger 'pageBottom' unless event.target.form - when 36 - @trigger 'pageTop' unless event.target.form - when 37 - @trigger 'left' unless event.target.value - when 38 - @trigger 'up' - @showTip?() - false - when 39 - @trigger 'right' unless event.target.value - when 40 - @trigger 'down' - @showTip?() - false - when 191 - unless event.target.form - @trigger 'typing' - false - - handleKeydownSuperEvent: (event) -> - switch event.which - when 13 - @trigger 'superEnter' - when 37 - if @isMac - @trigger 'superLeft' - false - when 38 - @trigger 'pageTop' - false - when 39 - if @isMac - @trigger 'superRight' - false - when 40 - @trigger 'pageBottom' - false - when 188 - @trigger 'preferences' - false - - handleKeydownShiftEvent: (event, _force) -> - return @handleKeydownEvent(event, true) if not _force and event.which in [37, 38, 39, 40] and @swapArrowKeysBehavior() - - if not event.target.form and 65 <= event.which <= 90 - @trigger 'typing' - return - - switch event.which - when 32 - @trigger 'pageUp' - false - when 38 - unless getSelection()?.toString() - @trigger 'altUp' - false - when 40 - unless getSelection()?.toString() - @trigger 'altDown' - false - - handleKeydownAltEvent: (event, _force) -> - return @handleKeydownEvent(event, true) if not _force and event.which in [37, 38, 39, 40] and @swapArrowKeysBehavior() - - switch event.which - when 9 - @trigger 'altRight', event - when 37 - unless @isMac - @trigger 'superLeft' - false - when 38 - @trigger 'altUp' - false - when 39 - unless @isMac - @trigger 'superRight' - false - when 40 - @trigger 'altDown' - false - when 67 - @trigger 'altC' - false - when 68 - @trigger 'altD' - false - when 70 - @trigger 'altF', event - when 71 - @trigger 'altG' - false - when 79 - @trigger 'altO' - false - when 82 - @trigger 'altR' - false - when 83 - @trigger 'altS' - false - - handleKeypressEvent: (event) -> - if event.which is 63 and not event.target.value - @trigger 'help' - false - else - @lastKeypress = Date.now() - - buggyEvent: (event) -> - try - event.target - event.ctrlKey - event.which - return false - catch - return true +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS103: Rewrite code to no longer use __guard__, or convert again using --optional-chaining + * DS205: Consider reworking code to avoid use of IIFEs + * DS206: Consider reworking classes to avoid initClass + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +const Cls = (app.Shortcuts = class Shortcuts { + static initClass() { + $.extend(this.prototype, Events); + } + + constructor() { + this.onKeydown = this.onKeydown.bind(this); + this.onKeypress = this.onKeypress.bind(this); + this.isMac = $.isMac(); + this.start(); + } + + start() { + $.on(document, 'keydown', this.onKeydown); + $.on(document, 'keypress', this.onKeypress); + } + + stop() { + $.off(document, 'keydown', this.onKeydown); + $.off(document, 'keypress', this.onKeypress); + } + + swapArrowKeysBehavior() { + return app.settings.get('arrowScroll'); + } + + spaceScroll() { + return app.settings.get('spaceScroll'); + } + + showTip() { + app.showTip('KeyNav'); + return this.showTip = null; + } + + spaceTimeout() { + return app.settings.get('spaceTimeout'); + } + + onKeydown(event) { + if (this.buggyEvent(event)) { return; } + const result = (() => { + if (event.ctrlKey || event.metaKey) { + if (!event.altKey && !event.shiftKey) { return this.handleKeydownSuperEvent(event); } + } else if (event.shiftKey) { + if (!event.altKey) { return this.handleKeydownShiftEvent(event); } + } else if (event.altKey) { + return this.handleKeydownAltEvent(event); + } else { + return this.handleKeydownEvent(event); + } + })(); + + if (result === false) { event.preventDefault(); } + } + + onKeypress(event) { + if (this.buggyEvent(event) || ((event.charCode === 63) && (document.activeElement.tagName === 'INPUT'))) { return; } + if (!event.ctrlKey && !event.metaKey) { + const result = this.handleKeypressEvent(event); + if (result === false) { event.preventDefault(); } + } + } + + handleKeydownEvent(event, _force) { + if (!_force && [37, 38, 39, 40].includes(event.which) && this.swapArrowKeysBehavior()) { return this.handleKeydownAltEvent(event, true); } + + if (!event.target.form && ((48 <= event.which && event.which <= 57) || (65 <= event.which && event.which <= 90))) { + this.trigger('typing'); + return; + } + + switch (event.which) { + case 8: + if (!event.target.form) { return this.trigger('typing'); } + break; + case 13: + return this.trigger('enter'); + case 27: + this.trigger('escape'); + return false; + case 32: + if ((event.target.type === 'search') && this.spaceScroll() && (!this.lastKeypress || (this.lastKeypress < (Date.now() - (this.spaceTimeout() * 1000))))) { + this.trigger('pageDown'); + return false; + } + break; + case 33: + return this.trigger('pageUp'); + case 34: + return this.trigger('pageDown'); + case 35: + if (!event.target.form) { return this.trigger('pageBottom'); } + break; + case 36: + if (!event.target.form) { return this.trigger('pageTop'); } + break; + case 37: + if (!event.target.value) { return this.trigger('left'); } + break; + case 38: + this.trigger('up'); + if (typeof this.showTip === 'function') { + this.showTip(); + } + return false; + case 39: + if (!event.target.value) { return this.trigger('right'); } + break; + case 40: + this.trigger('down'); + if (typeof this.showTip === 'function') { + this.showTip(); + } + return false; + case 191: + if (!event.target.form) { + this.trigger('typing'); + return false; + } + break; + } + } + + handleKeydownSuperEvent(event) { + switch (event.which) { + case 13: + return this.trigger('superEnter'); + case 37: + if (this.isMac) { + this.trigger('superLeft'); + return false; + } + break; + case 38: + this.trigger('pageTop'); + return false; + case 39: + if (this.isMac) { + this.trigger('superRight'); + return false; + } + break; + case 40: + this.trigger('pageBottom'); + return false; + case 188: + this.trigger('preferences'); + return false; + } + } + + handleKeydownShiftEvent(event, _force) { + if (!_force && [37, 38, 39, 40].includes(event.which) && this.swapArrowKeysBehavior()) { return this.handleKeydownEvent(event, true); } + + if (!event.target.form && (65 <= event.which && event.which <= 90)) { + this.trigger('typing'); + return; + } + + switch (event.which) { + case 32: + this.trigger('pageUp'); + return false; + case 38: + if (!__guard__(getSelection(), x => x.toString())) { + this.trigger('altUp'); + return false; + } + break; + case 40: + if (!__guard__(getSelection(), x1 => x1.toString())) { + this.trigger('altDown'); + return false; + } + break; + } + } + + handleKeydownAltEvent(event, _force) { + if (!_force && [37, 38, 39, 40].includes(event.which) && this.swapArrowKeysBehavior()) { return this.handleKeydownEvent(event, true); } + + switch (event.which) { + case 9: + return this.trigger('altRight', event); + case 37: + if (!this.isMac) { + this.trigger('superLeft'); + return false; + } + break; + case 38: + this.trigger('altUp'); + return false; + case 39: + if (!this.isMac) { + this.trigger('superRight'); + return false; + } + break; + case 40: + this.trigger('altDown'); + return false; + case 67: + this.trigger('altC'); + return false; + case 68: + this.trigger('altD'); + return false; + case 70: + return this.trigger('altF', event); + case 71: + this.trigger('altG'); + return false; + case 79: + this.trigger('altO'); + return false; + case 82: + this.trigger('altR'); + return false; + case 83: + this.trigger('altS'); + return false; + } + } + + handleKeypressEvent(event) { + if ((event.which === 63) && !event.target.value) { + this.trigger('help'); + return false; + } else { + return this.lastKeypress = Date.now(); + } + } + + buggyEvent(event) { + try { + event.target; + event.ctrlKey; + event.which; + return false; + } catch (error) { + return true; + } + } +}); +Cls.initClass(); + +function __guard__(value, transform) { + return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; +} \ No newline at end of file diff --git a/assets/javascripts/app/update_checker.js b/assets/javascripts/app/update_checker.js index 3558d6bc4e..09538adced 100644 --- a/assets/javascripts/app/update_checker.js +++ b/assets/javascripts/app/update_checker.js @@ -1,39 +1,54 @@ -class app.UpdateChecker - constructor: -> - @lastCheck = Date.now() +/* + * decaffeinate suggestions: + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +app.UpdateChecker = class UpdateChecker { + constructor() { + this.checkDocs = this.checkDocs.bind(this); + this.onFocus = this.onFocus.bind(this); + this.lastCheck = Date.now(); - $.on window, 'focus', @onFocus - app.serviceWorker?.on 'updateready', @onUpdateReady + $.on(window, 'focus', this.onFocus); + if (app.serviceWorker != null) { + app.serviceWorker.on('updateready', this.onUpdateReady); + } - setTimeout @checkDocs, 0 + setTimeout(this.checkDocs, 0); + } - check: -> - if app.serviceWorker - app.serviceWorker.update() - else - ajax - url: $('script[src*="application"]').getAttribute('src') - dataType: 'application/javascript' - error: (_, xhr) => @onUpdateReady() if xhr.status is 404 - return + check() { + if (app.serviceWorker) { + app.serviceWorker.update(); + } else { + ajax({ + url: $('script[src*="application"]').getAttribute('src'), + dataType: 'application/javascript', + error: (_, xhr) => { if (xhr.status === 404) { return this.onUpdateReady(); } } + }); + } + } - onUpdateReady: -> - new app.views.Notif 'UpdateReady', autoHide: null - return + onUpdateReady() { + new app.views.Notif('UpdateReady', {autoHide: null}); + } - checkDocs: => - unless app.settings.get('manualUpdate') - app.docs.updateInBackground() - else - app.docs.checkForUpdates (i) => @onDocsUpdateReady() if i > 0 - return + checkDocs() { + if (!app.settings.get('manualUpdate')) { + app.docs.updateInBackground(); + } else { + app.docs.checkForUpdates(i => { if (i > 0) { return this.onDocsUpdateReady(); } }); + } + } - onDocsUpdateReady: -> - new app.views.Notif 'UpdateDocs', autoHide: null - return + onDocsUpdateReady() { + new app.views.Notif('UpdateDocs', {autoHide: null}); + } - onFocus: => - if Date.now() - @lastCheck > 21600e3 - @lastCheck = Date.now() - @check() - return + onFocus() { + if ((Date.now() - this.lastCheck) > 21600e3) { + this.lastCheck = Date.now(); + this.check(); + } + } +}; diff --git a/assets/javascripts/application.js.js b/assets/javascripts/application.js.js index 6bf87f1ce8..c06b434dd2 100644 --- a/assets/javascripts/application.js.js +++ b/assets/javascripts/application.js.js @@ -1,31 +1,38 @@ -#= require_tree ./vendor +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +//= require_tree ./vendor -#= require lib/license -#= require_tree ./lib +//= require lib/license +//= require_tree ./lib -#= require app/app -#= require app/config -#= require_tree ./app +//= require app/app +//= require app/config +//= require_tree ./app -#= require collections/collection -#= require_tree ./collections +//= require collections/collection +//= require_tree ./collections -#= require models/model -#= require_tree ./models +//= require models/model +//= require_tree ./models -#= require views/view -#= require_tree ./views +//= require views/view +//= require_tree ./views -#= require_tree ./templates +//= require_tree ./templates -#= require tracking +//= require tracking -init = -> - document.removeEventListener 'DOMContentLoaded', init, false +var init = function() { + document.removeEventListener('DOMContentLoaded', init, false); - if document.body - app.init() - else - setTimeout(init, 42) + if (document.body) { + return app.init(); + } else { + return setTimeout(init, 42); + } +}; -document.addEventListener 'DOMContentLoaded', init, false +document.addEventListener('DOMContentLoaded', init, false); diff --git a/assets/javascripts/collections/collection.js b/assets/javascripts/collections/collection.js index b902a498ef..2b857c7311 100644 --- a/assets/javascripts/collections/collection.js +++ b/assets/javascripts/collections/collection.js @@ -1,55 +1,75 @@ -class app.Collection - constructor: (objects = []) -> - @reset objects - - model: -> - app.models[@constructor.model] - - reset: (objects = []) -> - @models = [] - @add object for object in objects - return - - add: (object) -> - if object instanceof app.Model - @models.push object - else if object instanceof Array - @add obj for obj in object - else if object instanceof app.Collection - @models.push object.all()... - else - @models.push new (@model())(object) - return - - remove: (model) -> - @models.splice @models.indexOf(model), 1 - return - - size: -> - @models.length - - isEmpty: -> - @models.length is 0 - - each: (fn) -> - fn(model) for model in @models - return - - all: -> - @models - - contains: (model) -> - @models.indexOf(model) >= 0 - - findBy: (attr, value) -> - for model in @models - return model if model[attr] is value - return - - findAllBy: (attr, value) -> - model for model in @models when model[attr] is value - - countAllBy: (attr, value) -> - i = 0 - i += 1 for model in @models when model[attr] is value - i +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +app.Collection = class Collection { + constructor(objects) { + if (objects == null) { objects = []; } + this.reset(objects); + } + + model() { + return app.models[this.constructor.model]; + } + + reset(objects) { + if (objects == null) { objects = []; } + this.models = []; + for (var object of Array.from(objects)) { this.add(object); } + } + + add(object) { + if (object instanceof app.Model) { + this.models.push(object); + } else if (object instanceof Array) { + for (var obj of Array.from(object)) { this.add(obj); } + } else if (object instanceof app.Collection) { + this.models.push(...Array.from(object.all() || [])); + } else { + this.models.push(new (this.model())(object)); + } + } + + remove(model) { + this.models.splice(this.models.indexOf(model), 1); + } + + size() { + return this.models.length; + } + + isEmpty() { + return this.models.length === 0; + } + + each(fn) { + for (var model of Array.from(this.models)) { fn(model); } + } + + all() { + return this.models; + } + + contains(model) { + return this.models.indexOf(model) >= 0; + } + + findBy(attr, value) { + for (var model of Array.from(this.models)) { + if (model[attr] === value) { return model; } + } + } + + findAllBy(attr, value) { + return Array.from(this.models).filter((model) => model[attr] === value); + } + + countAllBy(attr, value) { + let i = 0; + for (var model of Array.from(this.models)) { if (model[attr] === value) { i += 1; } } + return i; + } +}; diff --git a/assets/javascripts/collections/docs.js b/assets/javascripts/collections/docs.js index d76e0f07ef..99ef32745d 100644 --- a/assets/javascripts/collections/docs.js +++ b/assets/javascripts/collections/docs.js @@ -1,85 +1,117 @@ -class app.collections.Docs extends app.Collection - @model: 'Doc' +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS202: Simplify dynamic range loops + * DS206: Consider reworking classes to avoid initClass + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +(function() { + let NORMALIZE_VERSION_RGX = undefined; + let NORMALIZE_VERSION_SUB = undefined; + let CONCURRENCY = undefined; + const Cls = (app.collections.Docs = class Docs extends app.Collection { + static initClass() { + this.model = 'Doc'; + + NORMALIZE_VERSION_RGX = /\.(\d)$/; + NORMALIZE_VERSION_SUB = '.0$1'; + + // Load models concurrently. + // It's not pretty but I didn't want to import a promise library only for this. + CONCURRENCY = 3; + } - findBySlug: (slug) -> - @findBy('slug', slug) or @findBy('slug_without_version', slug) + findBySlug(slug) { + return this.findBy('slug', slug) || this.findBy('slug_without_version', slug); + } + sort() { + return this.models.sort(function(a, b) { + if (a.name === b.name) { + if (!a.version || (a.version.replace(NORMALIZE_VERSION_RGX, NORMALIZE_VERSION_SUB) > b.version.replace(NORMALIZE_VERSION_RGX, NORMALIZE_VERSION_SUB))) { + return -1; + } else { + return 1; + } + } else if (a.name.toLowerCase() > b.name.toLowerCase()) { + return 1; + } else { + return -1; + } + }); + } + load(onComplete, onError, options) { + let i = 0; - NORMALIZE_VERSION_RGX = /\.(\d)$/ - NORMALIZE_VERSION_SUB = '.0$1' - sort: -> - @models.sort (a, b) -> - if a.name is b.name - if not a.version or a.version.replace(NORMALIZE_VERSION_RGX, NORMALIZE_VERSION_SUB) > b.version.replace(NORMALIZE_VERSION_RGX, NORMALIZE_VERSION_SUB) - -1 - else - 1 - else if a.name.toLowerCase() > b.name.toLowerCase() - 1 - else - -1 + var next = () => { + if (i < this.models.length) { + this.models[i].load(next, fail, options); + } else if (i === ((this.models.length + CONCURRENCY) - 1)) { + onComplete(); + } + i++; + }; - # Load models concurrently. - # It's not pretty but I didn't want to import a promise library only for this. - CONCURRENCY = 3 - load: (onComplete, onError, options) -> - i = 0 + var fail = function(...args) { + if (onError) { + onError(...Array.from(args || [])); + onError = null; + } + next(); + }; - next = => - if i < @models.length - @models[i].load(next, fail, options) - else if i is @models.length + CONCURRENCY - 1 - onComplete() - i++ - return + for (let j = 0, end = CONCURRENCY, asc = 0 <= end; asc ? j < end : j > end; asc ? j++ : j--) { next(); } + } - fail = (args...) -> - if onError - onError(args...) - onError = null - next() - return + clearCache() { + for (var doc of Array.from(this.models)) { doc.clearCache(); } + } - next() for [0...CONCURRENCY] - return + uninstall(callback) { + let i = 0; + var next = () => { + if (i < this.models.length) { + this.models[i++].uninstall(next, next); + } else { + callback(); + } + }; + next(); + } - clearCache: -> - doc.clearCache() for doc in @models - return + getInstallStatuses(callback) { + app.db.versions(this.models, function(statuses) { + if (statuses) { + for (var key in statuses) { + var value = statuses[key]; + statuses[key] = {installed: !!value, mtime: value}; + } + } + callback(statuses); + }); + } - uninstall: (callback) -> - i = 0 - next = => - if i < @models.length - @models[i++].uninstall(next, next) - else - callback() - return - next() - return + checkForUpdates(callback) { + this.getInstallStatuses(statuses => { + let i = 0; + if (statuses) { + for (var slug in statuses) { var status = statuses[slug]; if (this.findBy('slug', slug).isOutdated(status)) { i += 1; } } + } + callback(i); + }); + } - getInstallStatuses: (callback) -> - app.db.versions @models, (statuses) -> - if statuses - for key, value of statuses - statuses[key] = installed: !!value, mtime: value - callback(statuses) - return - return - - checkForUpdates: (callback) -> - @getInstallStatuses (statuses) => - i = 0 - if statuses - i += 1 for slug, status of statuses when @findBy('slug', slug).isOutdated(status) - callback(i) - return - return - - updateInBackground: -> - @getInstallStatuses (statuses) => - return unless statuses - for slug, status of statuses - doc = @findBy 'slug', slug - doc.install($.noop, $.noop) if doc.isOutdated(status) - return - return + updateInBackground() { + this.getInstallStatuses(statuses => { + if (!statuses) { return; } + for (var slug in statuses) { + var status = statuses[slug]; + var doc = this.findBy('slug', slug); + if (doc.isOutdated(status)) { doc.install($.noop, $.noop); } + } + }); + } + }); + Cls.initClass(); + return Cls; +})(); diff --git a/assets/javascripts/collections/entries.js b/assets/javascripts/collections/entries.js index f978b68be4..9a1b6dd887 100644 --- a/assets/javascripts/collections/entries.js +++ b/assets/javascripts/collections/entries.js @@ -1,2 +1,11 @@ -class app.collections.Entries extends app.Collection - @model: 'Entry' +/* + * decaffeinate suggestions: + * DS206: Consider reworking classes to avoid initClass + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +const Cls = (app.collections.Entries = class Entries extends app.Collection { + static initClass() { + this.model = 'Entry'; + } +}); +Cls.initClass(); diff --git a/assets/javascripts/collections/types.js b/assets/javascripts/collections/types.js index 8e76eeab34..2d34f2410b 100644 --- a/assets/javascripts/collections/types.js +++ b/assets/javascripts/collections/types.js @@ -1,19 +1,41 @@ -class app.collections.Types extends app.Collection - @model: 'Type' +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS104: Avoid inline assignments + * DS206: Consider reworking classes to avoid initClass + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +(function() { + let GUIDES_RGX = undefined; + let APPENDIX_RGX = undefined; + const Cls = (app.collections.Types = class Types extends app.Collection { + static initClass() { + this.model = 'Type'; + + GUIDES_RGX = /(^|\()(guides?|tutorials?|reference|book|getting\ started|manual|examples)($|[\):])/i; + APPENDIX_RGX = /appendix/i; + } - groups: -> - result = [] - for type in @models - (result[@_groupFor(type)] ||= []).push(type) - result.filter (e) -> e.length > 0 + groups() { + const result = []; + for (var type of Array.from(this.models)) { + var name; + (result[name = this._groupFor(type)] || (result[name] = [])).push(type); + } + return result.filter(e => e.length > 0); + } - GUIDES_RGX = /(^|\()(guides?|tutorials?|reference|book|getting\ started|manual|examples)($|[\):])/i - APPENDIX_RGX = /appendix/i - - _groupFor: (type) -> - if GUIDES_RGX.test(type.name) - 0 - else if APPENDIX_RGX.test(type.name) - 2 - else - 1 + _groupFor(type) { + if (GUIDES_RGX.test(type.name)) { + return 0; + } else if (APPENDIX_RGX.test(type.name)) { + return 2; + } else { + return 1; + } + } + }); + Cls.initClass(); + return Cls; +})(); diff --git a/assets/javascripts/debug.js.js b/assets/javascripts/debug.js.js index 032d93ac15..3273ac066f 100644 --- a/assets/javascripts/debug.js.js +++ b/assets/javascripts/debug.js.js @@ -1,85 +1,110 @@ -return unless console?.time and console.groupCollapsed - -# -# App -# - -_init = app.init -app.init = -> - console.time 'Init' - _init.call(app) - console.timeEnd 'Init' - console.time 'Load' - -_start = app.start -app.start = -> - console.timeEnd 'Load' - console.time 'Start' - _start.call(app, arguments...) - console.timeEnd 'Start' - -# -# Searcher -# - -_super = app.Searcher -_proto = app.Searcher.prototype - -app.Searcher = -> - _super.apply @, arguments - - _setup = @setup.bind(@) - @setup = -> - console.groupCollapsed "Search: #{@query}" - console.time 'Total' - _setup() - - _match = @match.bind(@) - @match = => - console.timeEnd @matcher.name if @matcher - _match() - - _setupMatcher = @setupMatcher.bind(@) - @setupMatcher = -> - console.time @matcher.name - _setupMatcher() - - _end = @end.bind(@) - @end = -> - console.log "Results: #{@totalResults}" - console.timeEnd 'Total' - console.groupEnd() - _end() - - _kill = @kill.bind(@) - @kill = -> - if @timeout - console.timeEnd @matcher.name if @matcher - console.groupEnd() - console.timeEnd 'Total' - console.warn 'Killed' - _kill() - - return - -$.extend(app.Searcher, _super) -_proto.constructor = app.Searcher -app.Searcher.prototype = _proto - -# -# View tree -# - -@viewTree = (view = app.document, level = 0, visited = []) -> - return if visited.indexOf(view) >= 0 - visited.push(view) - - console.log "%c #{Array(level + 1).join(' ')}#{view.constructor.name}: #{!!view.activated}", - 'color:' + (view.activated and 'green' or 'red') - - for own key, value of view when key isnt 'view' and value - if typeof value is 'object' and value.setupElement - @viewTree(value, level + 1, visited) - else if value.constructor.toString().match(/Object\(\)/) - @viewTree(v, level + 1, visited) for own k, v of value when v and typeof v is 'object' and v.setupElement - return +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS203: Remove `|| {}` from converted for-own loops + * DS207: Consider shorter variations of null checks + * DS208: Avoid top-level this + * DS209: Avoid top-level return + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +if (!(typeof console !== 'undefined' && console !== null ? console.time : undefined) || !console.groupCollapsed) { return; } + +// +// App +// + +const _init = app.init; +app.init = function() { + console.time('Init'); + _init.call(app); + console.timeEnd('Init'); + return console.time('Load'); +}; + +const _start = app.start; +app.start = function() { + console.timeEnd('Load'); + console.time('Start'); + _start.call(app, ...arguments); + return console.timeEnd('Start'); +}; + +// +// Searcher +// + +const _super = app.Searcher; +const _proto = app.Searcher.prototype; + +app.Searcher = function() { + _super.apply(this, arguments); + + const _setup = this.setup.bind(this); + this.setup = function() { + console.groupCollapsed(`Search: ${this.query}`); + console.time('Total'); + return _setup(); + }; + + const _match = this.match.bind(this); + this.match = () => { + if (this.matcher) { console.timeEnd(this.matcher.name); } + return _match(); + }; + + const _setupMatcher = this.setupMatcher.bind(this); + this.setupMatcher = function() { + console.time(this.matcher.name); + return _setupMatcher(); + }; + + const _end = this.end.bind(this); + this.end = function() { + console.log(`Results: ${this.totalResults}`); + console.timeEnd('Total'); + console.groupEnd(); + return _end(); + }; + + const _kill = this.kill.bind(this); + this.kill = function() { + if (this.timeout) { + if (this.matcher) { console.timeEnd(this.matcher.name); } + console.groupEnd(); + console.timeEnd('Total'); + console.warn('Killed'); + } + return _kill(); + }; + +}; + +$.extend(app.Searcher, _super); +_proto.constructor = app.Searcher; +app.Searcher.prototype = _proto; + +// +// View tree +// + +this.viewTree = function(view, level, visited) { + if (view == null) { view = app.document; } + if (level == null) { level = 0; } + if (visited == null) { visited = []; } + if (visited.indexOf(view) >= 0) { return; } + visited.push(view); + + console.log(`%c ${Array(level + 1).join(' ')}${view.constructor.name}: ${!!view.activated}`, + 'color:' + ((view.activated && 'green') || 'red')); + + for (var key of Object.keys(view || {})) { + var value = view[key]; + if ((key !== 'view') && value) { + if ((typeof value === 'object') && value.setupElement) { + this.viewTree(value, level + 1, visited); + } else if (value.constructor.toString().match(/Object\(\)/)) { + for (var k of Object.keys(value || {})) { var v = value[k]; if (v && (typeof v === 'object') && v.setupElement) { this.viewTree(v, level + 1, visited); } } + } + } + } +}; diff --git a/assets/javascripts/lib/ajax.js b/assets/javascripts/lib/ajax.js index 4138ce7b0f..8019f9fd62 100644 --- a/assets/javascripts/lib/ajax.js +++ b/assets/javascripts/lib/ajax.js @@ -1,118 +1,154 @@ -MIME_TYPES = - json: 'application/json' +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS205: Consider reworking code to avoid use of IIFEs + * DS207: Consider shorter variations of null checks + * DS208: Avoid top-level this + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +const MIME_TYPES = { + json: 'application/json', html: 'text/html' +}; -@ajax = (options) -> - applyDefaults(options) - serializeData(options) +this.ajax = function(options) { + applyDefaults(options); + serializeData(options); - xhr = new XMLHttpRequest() - xhr.open(options.type, options.url, options.async) + const xhr = new XMLHttpRequest(); + xhr.open(options.type, options.url, options.async); - applyCallbacks(xhr, options) - applyHeaders(xhr, options) + applyCallbacks(xhr, options); + applyHeaders(xhr, options); - xhr.send(options.data) + xhr.send(options.data); - if options.async - abort: abort.bind(undefined, xhr) - else - parseResponse(xhr, options) + if (options.async) { + return {abort: abort.bind(undefined, xhr)}; + } else { + return parseResponse(xhr, options); + } +}; -ajax.defaults = - async: true - dataType: 'json' - timeout: 30 +ajax.defaults = { + async: true, + dataType: 'json', + timeout: 30, type: 'GET' - # contentType - # context - # data - # error - # headers - # progress - # success - # url - -applyDefaults = (options) -> - for key of ajax.defaults - options[key] ?= ajax.defaults[key] - return - -serializeData = (options) -> - return unless options.data - - if options.type is 'GET' - options.url += '?' + serializeParams(options.data) - options.data = null - else - options.data = serializeParams(options.data) - return - -serializeParams = (params) -> - ("#{encodeURIComponent key}=#{encodeURIComponent value}" for key, value of params).join '&' - -applyCallbacks = (xhr, options) -> - return unless options.async - - xhr.timer = setTimeout onTimeout.bind(undefined, xhr, options), options.timeout * 1000 - xhr.onprogress = options.progress if options.progress - xhr.onreadystatechange = -> - if xhr.readyState is 4 - clearTimeout(xhr.timer) - onComplete(xhr, options) - return - return - -applyHeaders = (xhr, options) -> - options.headers or= {} - - if options.contentType - options.headers['Content-Type'] = options.contentType - - if not options.headers['Content-Type'] and options.data and options.type isnt 'GET' - options.headers['Content-Type'] = 'application/x-www-form-urlencoded' - - if options.dataType - options.headers['Accept'] = MIME_TYPES[options.dataType] or options.dataType - - for key, value of options.headers - xhr.setRequestHeader(key, value) - return - -onComplete = (xhr, options) -> - if 200 <= xhr.status < 300 - if (response = parseResponse(xhr, options))? - onSuccess response, xhr, options - else - onError 'invalid', xhr, options - else - onError 'error', xhr, options - return - -onSuccess = (response, xhr, options) -> - options.success?.call options.context, response, xhr, options - return - -onError = (type, xhr, options) -> - options.error?.call options.context, type, xhr, options - return - -onTimeout = (xhr, options) -> - xhr.abort() - onError 'timeout', xhr, options - return - -abort = (xhr) -> - clearTimeout(xhr.timer) - xhr.onreadystatechange = null - xhr.abort() - return - -parseResponse = (xhr, options) -> - if options.dataType is 'json' - parseJSON(xhr.responseText) - else - xhr.responseText - -parseJSON = (json) -> - try JSON.parse(json) catch +}; + // contentType + // context + // data + // error + // headers + // progress + // success + // url + +var applyDefaults = function(options) { + for (var key in ajax.defaults) { + if (options[key] == null) { options[key] = ajax.defaults[key]; } + } +}; + +var serializeData = function(options) { + if (!options.data) { return; } + + if (options.type === 'GET') { + options.url += '?' + serializeParams(options.data); + options.data = null; + } else { + options.data = serializeParams(options.data); + } +}; + +var serializeParams = params => ((() => { + const result = []; + for (var key in params) { + var value = params[key]; + result.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`); + } + return result; +})()).join('&'); + +var applyCallbacks = function(xhr, options) { + if (!options.async) { return; } + + xhr.timer = setTimeout(onTimeout.bind(undefined, xhr, options), options.timeout * 1000); + if (options.progress) { xhr.onprogress = options.progress; } + xhr.onreadystatechange = function() { + if (xhr.readyState === 4) { + clearTimeout(xhr.timer); + onComplete(xhr, options); + } + }; +}; + +var applyHeaders = function(xhr, options) { + if (!options.headers) { options.headers = {}; } + + if (options.contentType) { + options.headers['Content-Type'] = options.contentType; + } + + if (!options.headers['Content-Type'] && options.data && (options.type !== 'GET')) { + options.headers['Content-Type'] = 'application/x-www-form-urlencoded'; + } + + if (options.dataType) { + options.headers['Accept'] = MIME_TYPES[options.dataType] || options.dataType; + } + + for (var key in options.headers) { + var value = options.headers[key]; + xhr.setRequestHeader(key, value); + } +}; + +var onComplete = function(xhr, options) { + if (200 <= xhr.status && xhr.status < 300) { + let response; + if ((response = parseResponse(xhr, options)) != null) { + onSuccess(response, xhr, options); + } else { + onError('invalid', xhr, options); + } + } else { + onError('error', xhr, options); + } +}; + +var onSuccess = function(response, xhr, options) { + if (options.success != null) { + options.success.call(options.context, response, xhr, options); + } +}; + +var onError = function(type, xhr, options) { + if (options.error != null) { + options.error.call(options.context, type, xhr, options); + } +}; + +var onTimeout = function(xhr, options) { + xhr.abort(); + onError('timeout', xhr, options); +}; + +var abort = function(xhr) { + clearTimeout(xhr.timer); + xhr.onreadystatechange = null; + xhr.abort(); +}; + +var parseResponse = function(xhr, options) { + if (options.dataType === 'json') { + return parseJSON(xhr.responseText); + } else { + return xhr.responseText; + } +}; + +var parseJSON = function(json) { + try { return JSON.parse(json); } catch (error) {} +}; diff --git a/assets/javascripts/lib/cookies_store.js b/assets/javascripts/lib/cookies_store.js index eaf1bd4f7c..a1a8f0341b 100644 --- a/assets/javascripts/lib/cookies_store.js +++ b/assets/javascripts/lib/cookies_store.js @@ -1,42 +1,66 @@ -class @CookiesStore - # Intentionally called CookiesStore instead of CookieStore - # Calling it CookieStore causes issues when the Experimental Web Platform features flag is enabled in Chrome - # Related issue: https://github.com/freeCodeCamp/devdocs/issues/932 +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS206: Consider reworking classes to avoid initClass + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +(function() { + let INT = undefined; + const Cls = (this.CookiesStore = class CookiesStore { + static initClass() { + // Intentionally called CookiesStore instead of CookieStore + // Calling it CookieStore causes issues when the Experimental Web Platform features flag is enabled in Chrome + // Related issue: https://github.com/freeCodeCamp/devdocs/issues/932 + + INT = /^\d+$/; + } - INT = /^\d+$/ + static onBlocked() {} - @onBlocked: -> + get(key) { + let value = Cookies.get(key); + if ((value != null) && INT.test(value)) { value = parseInt(value, 10); } + return value; + } - get: (key) -> - value = Cookies.get(key) - value = parseInt(value, 10) if value? and INT.test(value) - value + set(key, value) { + if (value === false) { + this.del(key); + return; + } - set: (key, value) -> - if value == false - @del(key) - return + if (value === true) { value = 1; } + if (value && (typeof INT.test === 'function' ? INT.test(value) : undefined)) { value = parseInt(value, 10); } + Cookies.set(key, '' + value, {path: '/', expires: 1e8}); + if (this.get(key) !== value) { this.constructor.onBlocked(key, value, this.get(key)); } + } - value = 1 if value == true - value = parseInt(value, 10) if value and INT.test?(value) - Cookies.set(key, '' + value, path: '/', expires: 1e8) - @constructor.onBlocked(key, value, @get(key)) if @get(key) != value - return + del(key) { + Cookies.expire(key); + } - del: (key) -> - Cookies.expire(key) - return + reset() { + try { + for (var cookie of Array.from(document.cookie.split(/;\s?/))) { + Cookies.expire(cookie.split('=')[0]); + } + return; + } catch (error) {} + } - reset: -> - try - for cookie in document.cookie.split(/;\s?/) - Cookies.expire(cookie.split('=')[0]) - return - catch - - dump: -> - result = {} - for cookie in document.cookie.split(/;\s?/) when cookie[0] isnt '_' - cookie = cookie.split('=') - result[cookie[0]] = cookie[1] - result + dump() { + const result = {}; + for (var cookie of Array.from(document.cookie.split(/;\s?/))) { + if (cookie[0] !== '_') { + cookie = cookie.split('='); + result[cookie[0]] = cookie[1]; + } + } + return result; + } + }); + Cls.initClass(); + return Cls; +})(); diff --git a/assets/javascripts/lib/events.js b/assets/javascripts/lib/events.js index 0593607659..9e007e9c3c 100644 --- a/assets/javascripts/lib/events.js +++ b/assets/javascripts/lib/events.js @@ -1,28 +1,51 @@ -@Events = - on: (event, callback) -> - if event.indexOf(' ') >= 0 - @on name, callback for name in event.split(' ') - else - ((@_callbacks ?= {})[event] ?= []).push callback - @ +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS104: Avoid inline assignments + * DS207: Consider shorter variations of null checks + * DS208: Avoid top-level this + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +this.Events = { + on(event, callback) { + if (event.indexOf(' ') >= 0) { + for (var name of Array.from(event.split(' '))) { this.on(name, callback); } + } else { + let base; + (((base = this._callbacks != null ? this._callbacks : (this._callbacks = {})))[event] != null ? base[event] : (base[event] = [])).push(callback); + } + return this; + }, - off: (event, callback) -> - if event.indexOf(' ') >= 0 - @off name, callback for name in event.split(' ') - else if (callbacks = @_callbacks?[event]) and (index = callbacks.indexOf callback) >= 0 - callbacks.splice index, 1 - delete @_callbacks[event] unless callbacks.length - @ + off(event, callback) { + let callbacks, index; + if (event.indexOf(' ') >= 0) { + for (var name of Array.from(event.split(' '))) { this.off(name, callback); } + } else if ((callbacks = this._callbacks != null ? this._callbacks[event] : undefined) && ((index = callbacks.indexOf(callback)) >= 0)) { + callbacks.splice(index, 1); + if (!callbacks.length) { delete this._callbacks[event]; } + } + return this; + }, - trigger: (event, args...) -> - @eventInProgress = { name: event, args: args } - if callbacks = @_callbacks?[event] - callback? args... for callback in callbacks.slice(0) - @eventInProgress = null - @trigger 'all', event, args... unless event is 'all' - @ + trigger(event, ...args) { + let callbacks; + this.eventInProgress = { name: event, args }; + if (callbacks = this._callbacks != null ? this._callbacks[event] : undefined) { + for (var callback of Array.from(callbacks.slice(0))) { if (typeof callback === 'function') { + callback(...Array.from(args || [])); + } } + } + this.eventInProgress = null; + if (event !== 'all') { this.trigger('all', event, ...Array.from(args)); } + return this; + }, - removeEvent: (event) -> - if @_callbacks? - delete @_callbacks[name] for name in event.split(' ') - @ + removeEvent(event) { + if (this._callbacks != null) { + for (var name of Array.from(event.split(' '))) { delete this._callbacks[name]; } + } + return this; + } +}; diff --git a/assets/javascripts/lib/favicon.js b/assets/javascripts/lib/favicon.js index 428eae453a..726fb15a28 100644 --- a/assets/javascripts/lib/favicon.js +++ b/assets/javascripts/lib/favicon.js @@ -1,76 +1,89 @@ -defaultUrl = null -currentSlug = null - -imageCache = {} -urlCache = {} - -withImage = (url, action) -> - if imageCache[url] - action(imageCache[url]) - else - img = new Image() - img.crossOrigin = 'anonymous' - img.src = url - img.onload = () => - imageCache[url] = img - action(img) - -@setFaviconForDoc = (doc) -> - return if currentSlug == doc.slug - - favicon = $('link[rel="icon"]') - - if defaultUrl == null - defaultUrl = favicon.href - - if urlCache[doc.slug] - favicon.href = urlCache[doc.slug] - currentSlug = doc.slug - return - - iconEl = $("._icon-#{doc.slug.split('~')[0]}") - return if iconEl == null - - styles = window.getComputedStyle(iconEl, ':before') - - backgroundPositionX = styles['background-position-x'] - backgroundPositionY = styles['background-position-y'] - return if backgroundPositionX == undefined || backgroundPositionY == undefined - - bgUrl = app.config.favicon_spritesheet - sourceSize = 16 - sourceX = Math.abs(parseInt(backgroundPositionX.slice(0, -2))) - sourceY = Math.abs(parseInt(backgroundPositionY.slice(0, -2))) - - withImage(bgUrl, (docImg) -> - withImage(defaultUrl, (defaultImg) -> - size = defaultImg.width - - canvas = document.createElement('canvas') - ctx = canvas.getContext('2d') - - canvas.width = size - canvas.height = size - ctx.drawImage(defaultImg, 0, 0) - - docIconPercentage = 65 - destinationCoords = size / 100 * (100 - docIconPercentage) - destinationSize = size / 100 * docIconPercentage - - ctx.drawImage(docImg, sourceX, sourceY, sourceSize, sourceSize, destinationCoords, destinationCoords, destinationSize, destinationSize) - - try - urlCache[doc.slug] = canvas.toDataURL() - favicon.href = urlCache[doc.slug] - - currentSlug = doc.slug - catch error - Raven.captureException error, { level: 'info' } - @resetFavicon() - ) - ) - -@resetFavicon = () -> - if defaultUrl != null and currentSlug != null - $('link[rel="icon"]').href = defaultUrl - currentSlug = null +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS208: Avoid top-level this + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +let defaultUrl = null; +let currentSlug = null; + +const imageCache = {}; +const urlCache = {}; + +const withImage = function(url, action) { + if (imageCache[url]) { + return action(imageCache[url]); + } else { + const img = new Image(); + img.crossOrigin = 'anonymous'; + img.src = url; + return img.onload = () => { + imageCache[url] = img; + return action(img); + }; + } +}; + +this.setFaviconForDoc = function(doc) { + if (currentSlug === doc.slug) { return; } + + const favicon = $('link[rel="icon"]'); + + if (defaultUrl === null) { + defaultUrl = favicon.href; + } + + if (urlCache[doc.slug]) { + favicon.href = urlCache[doc.slug]; + currentSlug = doc.slug; + return; + } + + const iconEl = $(`._icon-${doc.slug.split('~')[0]}`); + if (iconEl === null) { return; } + + const styles = window.getComputedStyle(iconEl, ':before'); + + const backgroundPositionX = styles['background-position-x']; + const backgroundPositionY = styles['background-position-y']; + if ((backgroundPositionX === undefined) || (backgroundPositionY === undefined)) { return; } + + const bgUrl = app.config.favicon_spritesheet; + const sourceSize = 16; + const sourceX = Math.abs(parseInt(backgroundPositionX.slice(0, -2))); + const sourceY = Math.abs(parseInt(backgroundPositionY.slice(0, -2))); + + return withImage(bgUrl, docImg => withImage(defaultUrl, function(defaultImg) { + const size = defaultImg.width; + + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + + canvas.width = size; + canvas.height = size; + ctx.drawImage(defaultImg, 0, 0); + + const docIconPercentage = 65; + const destinationCoords = (size / 100) * (100 - docIconPercentage); + const destinationSize = (size / 100) * docIconPercentage; + + ctx.drawImage(docImg, sourceX, sourceY, sourceSize, sourceSize, destinationCoords, destinationCoords, destinationSize, destinationSize); + + try { + urlCache[doc.slug] = canvas.toDataURL(); + favicon.href = urlCache[doc.slug]; + + return currentSlug = doc.slug; + } catch (error) { + Raven.captureException(error, { level: 'info' }); + return this.resetFavicon(); + } + })); +}; + +this.resetFavicon = function() { + if ((defaultUrl !== null) && (currentSlug !== null)) { + $('link[rel="icon"]').href = defaultUrl; + return currentSlug = null; + } +}; diff --git a/assets/javascripts/lib/license.js b/assets/javascripts/lib/license.js index c397b93b19..9292ef7ee8 100644 --- a/assets/javascripts/lib/license.js +++ b/assets/javascripts/lib/license.js @@ -1,7 +1,7 @@ -### +/* * Copyright 2013-2023 Thibaut Courouble and other contributors * * This source code is licensed under the terms of the Mozilla * Public License, v. 2.0, a copy of which may be obtained at: * http://mozilla.org/MPL/2.0/ -### +*/ diff --git a/assets/javascripts/lib/local_storage_store.js b/assets/javascripts/lib/local_storage_store.js index f4438c86a7..07bc4870fb 100644 --- a/assets/javascripts/lib/local_storage_store.js +++ b/assets/javascripts/lib/local_storage_store.js @@ -1,23 +1,33 @@ -class @LocalStorageStore - get: (key) -> - try - JSON.parse localStorage.getItem(key) - catch +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +this.LocalStorageStore = class LocalStorageStore { + get(key) { + try { + return JSON.parse(localStorage.getItem(key)); + } catch (error) {} + } - set: (key, value) -> - try - localStorage.setItem(key, JSON.stringify(value)) - true - catch + set(key, value) { + try { + localStorage.setItem(key, JSON.stringify(value)); + return true; + } catch (error) {} + } - del: (key) -> - try - localStorage.removeItem(key) - true - catch + del(key) { + try { + localStorage.removeItem(key); + return true; + } catch (error) {} + } - reset: -> - try - localStorage.clear() - true - catch + reset() { + try { + localStorage.clear(); + return true; + } catch (error) {} + } +}; diff --git a/assets/javascripts/lib/page.js b/assets/javascripts/lib/page.js index 5ad89b32ae..c24750421b 100644 --- a/assets/javascripts/lib/page.js +++ b/assets/javascripts/lib/page.js @@ -1,223 +1,280 @@ -### +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS206: Consider reworking classes to avoid initClass + * DS207: Consider shorter variations of null checks + * DS208: Avoid top-level this + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +/* * Based on github.com/visionmedia/page.js * Licensed under the MIT license * Copyright 2012 TJ Holowaychuk -### - -running = false -currentState = null -callbacks = [] - -@page = (value, fn) -> - if typeof value is 'function' - page '*', value - else if typeof fn is 'function' - route = new Route(value) - callbacks.push route.middleware(fn) - else if typeof value is 'string' - page.show(value, fn) - else - page.start(value) - return - -page.start = (options = {}) -> - unless running - running = true - addEventListener 'popstate', onpopstate - addEventListener 'click', onclick - page.replace currentPath(), null, null, true - return - -page.stop = -> - if running - running = false - removeEventListener 'click', onclick - removeEventListener 'popstate', onpopstate - return - -page.show = (path, state) -> - return if path is currentState?.path - context = new Context(path, state) - previousState = currentState - currentState = context.state - if res = page.dispatch(context) - currentState = previousState - location.assign(res) - else - context.pushState() - updateCanonicalLink() - track() - context - -page.replace = (path, state, skipDispatch, init) -> - context = new Context(path, state or currentState) - context.init = init - currentState = context.state - result = page.dispatch(context) unless skipDispatch - if result - context = new Context(result) - context.init = init - currentState = context.state - page.dispatch(context) - context.replaceState() - updateCanonicalLink() - track() unless skipDispatch - context - -page.dispatch = (context) -> - i = 0 - next = -> - res = fn(context, next) if fn = callbacks[i++] - return res - return next() - -page.canGoBack = -> - not Context.isIntialState(currentState) - -page.canGoForward = -> - not Context.isLastState(currentState) - -currentPath = -> - location.pathname + location.search + location.hash - -class Context - @initialPath: currentPath() - @sessionId: Date.now() - @stateId: 0 - - @isIntialState: (state) -> - state.id == 0 - - @isLastState: (state) -> - state.id == @stateId - 1 - - @isInitialPopState: (state) -> - state.path is @initialPath and @stateId is 1 - - @isSameSession: (state) -> - state.sessionId is @sessionId - - constructor: (@path = '/', @state = {}) -> - @pathname = @path.replace /(?:\?([^#]*))?(?:#(.*))?$/, (_, query, hash) => - @query = query - @hash = hash - '' - - @state.id ?= @constructor.stateId++ - @state.sessionId ?= @constructor.sessionId - @state.path = @path - - pushState: -> - history.pushState @state, '', @path - return - - replaceState: -> - try history.replaceState @state, '', @path # NS_ERROR_FAILURE in Firefox - return - -class Route - constructor: (@path, options = {}) -> - @keys = [] - @regexp = pathtoRegexp @path, @keys - - middleware: (fn) -> - (context, next) => - if @match context.pathname, params = [] - context.params = params - return fn(context, next) - else - return next() - - match: (path, params) -> - return unless matchData = @regexp.exec(path) - - for value, i in matchData[1..] - value = decodeURIComponent value if typeof value is 'string' - if key = @keys[i] - params[key.name] = value - else - params.push value - true - -pathtoRegexp = (path, keys) -> - return path if path instanceof RegExp - - path = "(#{path.join '|'})" if path instanceof Array +*/ + +let running = false; +let currentState = null; +const callbacks = []; + +this.page = function(value, fn) { + if (typeof value === 'function') { + page('*', value); + } else if (typeof fn === 'function') { + const route = new Route(value); + callbacks.push(route.middleware(fn)); + } else if (typeof value === 'string') { + page.show(value, fn); + } else { + page.start(value); + } +}; + +page.start = function(options) { + if (options == null) { options = {}; } + if (!running) { + running = true; + addEventListener('popstate', onpopstate); + addEventListener('click', onclick); + page.replace(currentPath(), null, null, true); + } +}; + +page.stop = function() { + if (running) { + running = false; + removeEventListener('click', onclick); + removeEventListener('popstate', onpopstate); + } +}; + +page.show = function(path, state) { + let res; + if (path === (currentState != null ? currentState.path : undefined)) { return; } + const context = new Context(path, state); + const previousState = currentState; + currentState = context.state; + if (res = page.dispatch(context)) { + currentState = previousState; + location.assign(res); + } else { + context.pushState(); + updateCanonicalLink(); + track(); + } + return context; +}; + +page.replace = function(path, state, skipDispatch, init) { + let result; + let context = new Context(path, state || currentState); + context.init = init; + currentState = context.state; + if (!skipDispatch) { result = page.dispatch(context); } + if (result) { + context = new Context(result); + context.init = init; + currentState = context.state; + page.dispatch(context); + } + context.replaceState(); + updateCanonicalLink(); + if (!skipDispatch) { track(); } + return context; +}; + +page.dispatch = function(context) { + let i = 0; + var next = function() { + let fn, res; + if (fn = callbacks[i++]) { res = fn(context, next); } + return res; + }; + return next(); +}; + +page.canGoBack = () => !Context.isIntialState(currentState); + +page.canGoForward = () => !Context.isLastState(currentState); + +var currentPath = () => location.pathname + location.search + location.hash; + +class Context { + static initClass() { + this.initialPath = currentPath(); + this.sessionId = Date.now(); + this.stateId = 0; + } + + static isIntialState(state) { + return state.id === 0; + } + + static isLastState(state) { + return state.id === (this.stateId - 1); + } + + static isInitialPopState(state) { + return (state.path === this.initialPath) && (this.stateId === 1); + } + + static isSameSession(state) { + return state.sessionId === this.sessionId; + } + + constructor(path, state) { + if (path == null) { path = '/'; } + this.path = path; + if (state == null) { state = {}; } + this.state = state; + this.pathname = this.path.replace(/(?:\?([^#]*))?(?:#(.*))?$/, (_, query, hash) => { + this.query = query; + this.hash = hash; + return ''; + }); + + if (this.state.id == null) { this.state.id = this.constructor.stateId++; } + if (this.state.sessionId == null) { this.state.sessionId = this.constructor.sessionId; } + this.state.path = this.path; + } + + pushState() { + history.pushState(this.state, '', this.path); + } + + replaceState() { + try { history.replaceState(this.state, '', this.path); } catch (error) {} // NS_ERROR_FAILURE in Firefox + } +} +Context.initClass(); + +class Route { + constructor(path, options) { + this.path = path; + if (options == null) { options = {}; } + this.keys = []; + this.regexp = pathtoRegexp(this.path, this.keys); + } + + middleware(fn) { + return (context, next) => { + let params; + if (this.match(context.pathname, (params = []))) { + context.params = params; + return fn(context, next); + } else { + return next(); + } + }; + } + + match(path, params) { + let matchData; + if (!(matchData = this.regexp.exec(path))) { return; } + + const iterable = matchData.slice(1); + for (let i = 0; i < iterable.length; i++) { + var key; + var value = iterable[i]; + if (typeof value === 'string') { value = decodeURIComponent(value); } + if ((key = this.keys[i])) { + params[key.name] = value; + } else { + params.push(value); + } + } + return true; + } +} + +var pathtoRegexp = function(path, keys) { + if (path instanceof RegExp) { return path; } + + if (path instanceof Array) { path = `(${path.join('|')})`; } path = path - .replace /\/\(/g, '(?:/' - .replace /(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, (_, slash = '', format = '', key, capture, optional) -> - keys.push name: key, optional: !!optional - str = if optional then '' else slash - str += '(?:' - str += slash if optional - str += format - str += capture or if format then '([^/.]+?)' else '([^/]+?)' - str += ')' - str += optional if optional - str - .replace /([\/.])/g, '\\$1' - .replace /\*/g, '(.*)' - - new RegExp "^#{path}$" - -onpopstate = (event) -> - return if not event.state or Context.isInitialPopState(event.state) - - if Context.isSameSession(event.state) - page.replace(event.state.path, event.state) - else - location.reload() - return - -onclick = (event) -> - try - return if event.which isnt 1 or event.metaKey or event.ctrlKey or event.shiftKey or event.defaultPrevented - catch - return - - link = $.eventTarget(event) - link = link.parentNode while link and link.tagName isnt 'A' - - if link and not link.target and isSameOrigin(link.href) - event.preventDefault() - path = link.pathname + link.search + link.hash - path = path.replace /^\/\/+/, '/' # IE11 bug - page.show(path) - return - -isSameOrigin = (url) -> - url.indexOf("#{location.protocol}//#{location.hostname}") is 0 - -updateCanonicalLink = -> - @canonicalLink ||= document.head.querySelector('link[rel="canonical"]') - @canonicalLink.setAttribute('href', "https://#{location.host}#{location.pathname}") - -trackers = [] - -page.track = (fn) -> - trackers.push(fn) - return - -track = -> - return unless app.config.env == 'production' - return if navigator.doNotTrack == '1' - return if navigator.globalPrivacyControl - - consentGiven = Cookies.get('analyticsConsent') - consentAsked = Cookies.get('analyticsConsentAsked') - - if consentGiven == '1' - tracker.call() for tracker in trackers - else if consentGiven == undefined and consentAsked == undefined - # Only ask for consent once per browser session - Cookies.set('analyticsConsentAsked', '1') - - new app.views.Notif 'AnalyticsConsent', autoHide: null - return - -@resetAnalytics = -> - for cookie in document.cookie.split(/;\s?/) - name = cookie.split('=')[0] - if name[0] == '_' && name[1] != '_' - Cookies.expire(name) - return + .replace(/\/\(/g, '(?:/') + .replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, function(_, slash, format, key, capture, optional) { + if (slash == null) { slash = ''; } + if (format == null) { format = ''; } + keys.push({name: key, optional: !!optional}); + let str = optional ? '' : slash; + str += '(?:'; + if (optional) { str += slash; } + str += format; + str += capture || (format ? '([^/.]+?)' : '([^/]+?)'); + str += ')'; + if (optional) { str += optional; } + return str; + }).replace(/([\/.])/g, '\\$1') + .replace(/\*/g, '(.*)'); + + return new RegExp(`^${path}$`); +}; + +var onpopstate = function(event) { + if (!event.state || Context.isInitialPopState(event.state)) { return; } + + if (Context.isSameSession(event.state)) { + page.replace(event.state.path, event.state); + } else { + location.reload(); + } +}; + +var onclick = function(event) { + try { + if ((event.which !== 1) || event.metaKey || event.ctrlKey || event.shiftKey || event.defaultPrevented) { return; } + } catch (error) { + return; + } + + let link = $.eventTarget(event); + while (link && (link.tagName !== 'A')) { link = link.parentNode; } + + if (link && !link.target && isSameOrigin(link.href)) { + event.preventDefault(); + let path = link.pathname + link.search + link.hash; + path = path.replace(/^\/\/+/, '/'); // IE11 bug + page.show(path); + } +}; + +var isSameOrigin = url => url.indexOf(`${location.protocol}//${location.hostname}`) === 0; + +var updateCanonicalLink = function() { + if (!this.canonicalLink) { this.canonicalLink = document.head.querySelector('link[rel="canonical"]'); } + return this.canonicalLink.setAttribute('href', `https://${location.host}${location.pathname}`); +}; + +const trackers = []; + +page.track = function(fn) { + trackers.push(fn); +}; + +var track = function() { + if (app.config.env !== 'production') { return; } + if (navigator.doNotTrack === '1') { return; } + if (navigator.globalPrivacyControl) { return; } + + const consentGiven = Cookies.get('analyticsConsent'); + const consentAsked = Cookies.get('analyticsConsentAsked'); + + if (consentGiven === '1') { + for (var tracker of Array.from(trackers)) { tracker.call(); } + } else if ((consentGiven === undefined) && (consentAsked === undefined)) { + // Only ask for consent once per browser session + Cookies.set('analyticsConsentAsked', '1'); + + new app.views.Notif('AnalyticsConsent', {autoHide: null}); + } +}; + +this.resetAnalytics = function() { + for (var cookie of Array.from(document.cookie.split(/;\s?/))) { + var name = cookie.split('=')[0]; + if ((name[0] === '_') && (name[1] !== '_')) { + Cookies.expire(name); + } + } +}; diff --git a/assets/javascripts/lib/util.js b/assets/javascripts/lib/util.js index 001b13dee7..28f7369259 100644 --- a/assets/javascripts/lib/util.js +++ b/assets/javascripts/lib/util.js @@ -1,399 +1,491 @@ -# -# Traversing -# - -@$ = (selector, el = document) -> - try el.querySelector(selector) catch - -@$$ = (selector, el = document) -> - try el.querySelectorAll(selector) catch - -$.id = (id) -> - document.getElementById(id) - -$.hasChild = (parent, el) -> - return unless parent - while el - return true if el is parent - return if el is document.body - el = el.parentNode - -$.closestLink = (el, parent = document.body) -> - while el - return el if el.tagName is 'A' - return if el is parent - el = el.parentNode - -# -# Events -# - -$.on = (el, event, callback, useCapture = false) -> - if event.indexOf(' ') >= 0 - $.on el, name, callback for name in event.split(' ') - else - el.addEventListener(event, callback, useCapture) - return - -$.off = (el, event, callback, useCapture = false) -> - if event.indexOf(' ') >= 0 - $.off el, name, callback for name in event.split(' ') - else - el.removeEventListener(event, callback, useCapture) - return - -$.trigger = (el, type, canBubble = true, cancelable = true) -> - event = document.createEvent 'Event' - event.initEvent(type, canBubble, cancelable) - el.dispatchEvent(event) - return - -$.click = (el) -> - event = document.createEvent 'MouseEvent' - event.initMouseEvent 'click', true, true, window, null, 0, 0, 0, 0, false, false, false, false, 0, null - el.dispatchEvent(event) - return - -$.stopEvent = (event) -> - event.preventDefault() - event.stopPropagation() - event.stopImmediatePropagation() - return - -$.eventTarget = (event) -> - event.target.correspondingUseElement || event.target - -# -# Manipulation -# - -buildFragment = (value) -> - fragment = document.createDocumentFragment() - - if $.isCollection(value) - fragment.appendChild(child) for child in $.makeArray(value) - else - fragment.innerHTML = value - - fragment - -$.append = (el, value) -> - if typeof value is 'string' - el.insertAdjacentHTML 'beforeend', value - else - value = buildFragment(value) if $.isCollection(value) - el.appendChild(value) - return - -$.prepend = (el, value) -> - if not el.firstChild - $.append(value) - else if typeof value is 'string' - el.insertAdjacentHTML 'afterbegin', value - else - value = buildFragment(value) if $.isCollection(value) - el.insertBefore(value, el.firstChild) - return - -$.before = (el, value) -> - if typeof value is 'string' or $.isCollection(value) - value = buildFragment(value) - - el.parentNode.insertBefore(value, el) - return - -$.after = (el, value) -> - if typeof value is 'string' or $.isCollection(value) - value = buildFragment(value) - - if el.nextSibling - el.parentNode.insertBefore(value, el.nextSibling) - else - el.parentNode.appendChild(value) - return - -$.remove = (value) -> - if $.isCollection(value) - el.parentNode?.removeChild(el) for el in $.makeArray(value) - else - value.parentNode?.removeChild(value) - return - -$.empty = (el) -> - el.removeChild(el.firstChild) while el.firstChild - return - -# Calls the function while the element is off the DOM to avoid triggering -# unnecessary reflows and repaints. -$.batchUpdate = (el, fn) -> - parent = el.parentNode - sibling = el.nextSibling - parent.removeChild(el) - - fn(el) - - if (sibling) - parent.insertBefore(el, sibling) - else - parent.appendChild(el) - return - -# -# Offset -# - -$.rect = (el) -> - el.getBoundingClientRect() - -$.offset = (el, container = document.body) -> - top = 0 - left = 0 - - while el and el isnt container - top += el.offsetTop - left += el.offsetLeft - el = el.offsetParent - - top: top - left: left - -$.scrollParent = (el) -> - while (el = el.parentNode) and el.nodeType is 1 - break if el.scrollTop > 0 - break if getComputedStyle(el)?.overflowY in ['auto', 'scroll'] - el - -$.scrollTo = (el, parent, position = 'center', options = {}) -> - return unless el - - parent ?= $.scrollParent(el) - return unless parent - - parentHeight = parent.clientHeight - parentScrollHeight = parent.scrollHeight - return unless parentScrollHeight > parentHeight - - top = $.offset(el, parent).top - offsetTop = parent.firstElementChild.offsetTop - - switch position - when 'top' - parent.scrollTop = top - offsetTop - (if options.margin? then options.margin else 0) - when 'center' - parent.scrollTop = top - Math.round(parentHeight / 2 - el.offsetHeight / 2) - when 'continuous' - scrollTop = parent.scrollTop - height = el.offsetHeight - - lastElementOffset = parent.lastElementChild.offsetTop + parent.lastElementChild.offsetHeight - offsetBottom = if lastElementOffset > 0 then parentScrollHeight - lastElementOffset else 0 - - # If the target element is above the visible portion of its scrollable - # ancestor, move it near the top with a gap = options.topGap * target's height. - if top - offsetTop <= scrollTop + height * (options.topGap or 1) - parent.scrollTop = top - offsetTop - height * (options.topGap or 1) - # If the target element is below the visible portion of its scrollable - # ancestor, move it near the bottom with a gap = options.bottomGap * target's height. - else if top + offsetBottom >= scrollTop + parentHeight - height * ((options.bottomGap or 1) + 1) - parent.scrollTop = top + offsetBottom - parentHeight + height * ((options.bottomGap or 1) + 1) - return - -$.scrollToWithImageLock = (el, parent, args...) -> - parent ?= $.scrollParent(el) - return unless parent - - $.scrollTo el, parent, args... - - # Lock the scroll position on the target element for up to 3 seconds while - # nearby images are loaded and rendered. - for image in parent.getElementsByTagName('img') when not image.complete - do -> - onLoad = (event) -> - clearTimeout(timeout) - unbind(event.target) - $.scrollTo el, parent, args... - - unbind = (target) -> - $.off target, 'load', onLoad - - $.on image, 'load', onLoad - timeout = setTimeout unbind.bind(null, image), 3000 - return - -# Calls the function while locking the element's position relative to the window. -$.lockScroll = (el, fn) -> - if parent = $.scrollParent(el) - top = $.rect(el).top - top -= $.rect(parent).top unless parent in [document.body, document.documentElement] - fn() - parent.scrollTop = $.offset(el, parent).top - top - else - fn() - return - -smoothScroll = smoothStart = smoothEnd = smoothDistance = smoothDuration = null - -$.smoothScroll = (el, end) -> - unless window.requestAnimationFrame - el.scrollTop = end - return - - smoothEnd = end - - if smoothScroll - newDistance = smoothEnd - smoothStart - smoothDuration += Math.min 300, Math.abs(smoothDistance - newDistance) - smoothDistance = newDistance - return - - smoothStart = el.scrollTop - smoothDistance = smoothEnd - smoothStart - smoothDuration = Math.min 300, Math.abs(smoothDistance) - startTime = Date.now() - - smoothScroll = -> - p = Math.min 1, (Date.now() - startTime) / smoothDuration - y = Math.max 0, Math.floor(smoothStart + smoothDistance * (if p < 0.5 then 2 * p * p else p * (4 - p * 2) - 1)) - el.scrollTop = y - if p is 1 - smoothScroll = null - else - requestAnimationFrame(smoothScroll) - requestAnimationFrame(smoothScroll) - -# -# Utilities -# - -$.extend = (target, objects...) -> - for object in objects when object - for key, value of object - target[key] = value - target - -$.makeArray = (object) -> - if Array.isArray(object) - object - else - Array::slice.apply(object) - -$.arrayDelete = (array, object) -> - index = array.indexOf(object) - if index >= 0 - array.splice(index, 1) - true - else - false - -# Returns true if the object is an array or a collection of DOM elements. -$.isCollection = (object) -> - Array.isArray(object) or typeof object?.item is 'function' - -ESCAPE_HTML_MAP = - '&': '&' - '<': '<' - '>': '>' - '"': '"' - "'": ''' +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS103: Rewrite code to no longer use __guard__, or convert again using --optional-chaining + * DS104: Avoid inline assignments + * DS204: Change includes calls to have a more natural evaluation order + * DS207: Consider shorter variations of null checks + * DS208: Avoid top-level this + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +// +// Traversing +// + +let smoothDistance, smoothDuration, smoothEnd, smoothStart; +this.$ = function(selector, el) { + if (el == null) { el = document; } + try { return el.querySelector(selector); } catch (error) {} +}; + +this.$$ = function(selector, el) { + if (el == null) { el = document; } + try { return el.querySelectorAll(selector); } catch (error) {} +}; + +$.id = id => document.getElementById(id); + +$.hasChild = function(parent, el) { + if (!parent) { return; } + while (el) { + if (el === parent) { return true; } + if (el === document.body) { return; } + el = el.parentNode; + } +}; + +$.closestLink = function(el, parent) { + if (parent == null) { parent = document.body; } + while (el) { + if (el.tagName === 'A') { return el; } + if (el === parent) { return; } + el = el.parentNode; + } +}; + +// +// Events +// + +$.on = function(el, event, callback, useCapture) { + if (useCapture == null) { useCapture = false; } + if (event.indexOf(' ') >= 0) { + for (var name of Array.from(event.split(' '))) { $.on(el, name, callback); } + } else { + el.addEventListener(event, callback, useCapture); + } +}; + +$.off = function(el, event, callback, useCapture) { + if (useCapture == null) { useCapture = false; } + if (event.indexOf(' ') >= 0) { + for (var name of Array.from(event.split(' '))) { $.off(el, name, callback); } + } else { + el.removeEventListener(event, callback, useCapture); + } +}; + +$.trigger = function(el, type, canBubble, cancelable) { + if (canBubble == null) { canBubble = true; } + if (cancelable == null) { cancelable = true; } + const event = document.createEvent('Event'); + event.initEvent(type, canBubble, cancelable); + el.dispatchEvent(event); +}; + +$.click = function(el) { + const event = document.createEvent('MouseEvent'); + event.initMouseEvent('click', true, true, window, null, 0, 0, 0, 0, false, false, false, false, 0, null); + el.dispatchEvent(event); +}; + +$.stopEvent = function(event) { + event.preventDefault(); + event.stopPropagation(); + event.stopImmediatePropagation(); +}; + +$.eventTarget = event => event.target.correspondingUseElement || event.target; + +// +// Manipulation +// + +const buildFragment = function(value) { + const fragment = document.createDocumentFragment(); + + if ($.isCollection(value)) { + for (var child of Array.from($.makeArray(value))) { fragment.appendChild(child); } + } else { + fragment.innerHTML = value; + } + + return fragment; +}; + +$.append = function(el, value) { + if (typeof value === 'string') { + el.insertAdjacentHTML('beforeend', value); + } else { + if ($.isCollection(value)) { value = buildFragment(value); } + el.appendChild(value); + } +}; + +$.prepend = function(el, value) { + if (!el.firstChild) { + $.append(value); + } else if (typeof value === 'string') { + el.insertAdjacentHTML('afterbegin', value); + } else { + if ($.isCollection(value)) { value = buildFragment(value); } + el.insertBefore(value, el.firstChild); + } +}; + +$.before = function(el, value) { + if ((typeof value === 'string') || $.isCollection(value)) { + value = buildFragment(value); + } + + el.parentNode.insertBefore(value, el); +}; + +$.after = function(el, value) { + if ((typeof value === 'string') || $.isCollection(value)) { + value = buildFragment(value); + } + + if (el.nextSibling) { + el.parentNode.insertBefore(value, el.nextSibling); + } else { + el.parentNode.appendChild(value); + } +}; + +$.remove = function(value) { + if ($.isCollection(value)) { + for (var el of Array.from($.makeArray(value))) { if (el.parentNode != null) { + el.parentNode.removeChild(el); + } } + } else { + if (value.parentNode != null) { + value.parentNode.removeChild(value); + } + } +}; + +$.empty = function(el) { + while (el.firstChild) { el.removeChild(el.firstChild); } +}; + +// Calls the function while the element is off the DOM to avoid triggering +// unnecessary reflows and repaints. +$.batchUpdate = function(el, fn) { + const parent = el.parentNode; + const sibling = el.nextSibling; + parent.removeChild(el); + + fn(el); + + if (sibling) { + parent.insertBefore(el, sibling); + } else { + parent.appendChild(el); + } +}; + +// +// Offset +// + +$.rect = el => el.getBoundingClientRect(); + +$.offset = function(el, container) { + if (container == null) { container = document.body; } + let top = 0; + let left = 0; + + while (el && (el !== container)) { + top += el.offsetTop; + left += el.offsetLeft; + el = el.offsetParent; + } + + return { + top, + left + }; +}; + +$.scrollParent = function(el) { + while ((el = el.parentNode) && (el.nodeType === 1)) { + var needle; + if (el.scrollTop > 0) { break; } + if ((needle = __guard__(getComputedStyle(el), x => x.overflowY), ['auto', 'scroll'].includes(needle))) { break; } + } + return el; +}; + +$.scrollTo = function(el, parent, position, options) { + if (position == null) { position = 'center'; } + if (options == null) { options = {}; } + if (!el) { return; } + + if (parent == null) { parent = $.scrollParent(el); } + if (!parent) { return; } + + const parentHeight = parent.clientHeight; + const parentScrollHeight = parent.scrollHeight; + if (!(parentScrollHeight > parentHeight)) { return; } + + const { + top + } = $.offset(el, parent); + const { + offsetTop + } = parent.firstElementChild; + + switch (position) { + case 'top': + parent.scrollTop = top - offsetTop - ((options.margin != null) ? options.margin : 0); + break; + case 'center': + parent.scrollTop = top - Math.round((parentHeight / 2) - (el.offsetHeight / 2)); + break; + case 'continuous': + var { + scrollTop + } = parent; + var height = el.offsetHeight; + + var lastElementOffset = parent.lastElementChild.offsetTop + parent.lastElementChild.offsetHeight; + var offsetBottom = lastElementOffset > 0 ? parentScrollHeight - lastElementOffset : 0; + + // If the target element is above the visible portion of its scrollable + // ancestor, move it near the top with a gap = options.topGap * target's height. + if ((top - offsetTop) <= (scrollTop + (height * (options.topGap || 1)))) { + parent.scrollTop = top - offsetTop - (height * (options.topGap || 1)); + // If the target element is below the visible portion of its scrollable + // ancestor, move it near the bottom with a gap = options.bottomGap * target's height. + } else if ((top + offsetBottom) >= ((scrollTop + parentHeight) - (height * ((options.bottomGap || 1) + 1)))) { + parent.scrollTop = ((top + offsetBottom) - parentHeight) + (height * ((options.bottomGap || 1) + 1)); + } + break; + } +}; + +$.scrollToWithImageLock = function(el, parent, ...args) { + if (parent == null) { parent = $.scrollParent(el); } + if (!parent) { return; } + + $.scrollTo(el, parent, ...Array.from(args)); + + // Lock the scroll position on the target element for up to 3 seconds while + // nearby images are loaded and rendered. + for (var image of Array.from(parent.getElementsByTagName('img'))) { + if (!image.complete) { + (function() { + let timeout; + const onLoad = function(event) { + clearTimeout(timeout); + unbind(event.target); + return $.scrollTo(el, parent, ...Array.from(args)); + }; + + var unbind = target => $.off(target, 'load', onLoad); + + $.on(image, 'load', onLoad); + return timeout = setTimeout(unbind.bind(null, image), 3000); + })(); + } + } +}; + +// Calls the function while locking the element's position relative to the window. +$.lockScroll = function(el, fn) { + let parent; + if (parent = $.scrollParent(el)) { + let { + top + } = $.rect(el); + if (![document.body, document.documentElement].includes(parent)) { top -= $.rect(parent).top; } + fn(); + parent.scrollTop = $.offset(el, parent).top - top; + } else { + fn(); + } +}; + +let smoothScroll = (smoothStart = (smoothEnd = (smoothDistance = (smoothDuration = null)))); + +$.smoothScroll = function(el, end) { + if (!window.requestAnimationFrame) { + el.scrollTop = end; + return; + } + + smoothEnd = end; + + if (smoothScroll) { + const newDistance = smoothEnd - smoothStart; + smoothDuration += Math.min(300, Math.abs(smoothDistance - newDistance)); + smoothDistance = newDistance; + return; + } + + smoothStart = el.scrollTop; + smoothDistance = smoothEnd - smoothStart; + smoothDuration = Math.min(300, Math.abs(smoothDistance)); + const startTime = Date.now(); + + smoothScroll = function() { + const p = Math.min(1, (Date.now() - startTime) / smoothDuration); + const y = Math.max(0, Math.floor(smoothStart + (smoothDistance * (p < 0.5 ? 2 * p * p : (p * (4 - (p * 2))) - 1)))); + el.scrollTop = y; + if (p === 1) { + return smoothScroll = null; + } else { + return requestAnimationFrame(smoothScroll); + } + }; + return requestAnimationFrame(smoothScroll); +}; + +// +// Utilities +// + +$.extend = function(target, ...objects) { + for (var object of Array.from(objects)) { + if (object) { + for (var key in object) { + var value = object[key]; + target[key] = value; + } + } + } + return target; +}; + +$.makeArray = function(object) { + if (Array.isArray(object)) { + return object; + } else { + return Array.prototype.slice.apply(object); + } +}; + +$.arrayDelete = function(array, object) { + const index = array.indexOf(object); + if (index >= 0) { + array.splice(index, 1); + return true; + } else { + return false; + } +}; + +// Returns true if the object is an array or a collection of DOM elements. +$.isCollection = object => Array.isArray(object) || (typeof (object != null ? object.item : undefined) === 'function'); + +const ESCAPE_HTML_MAP = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', '/': '/' - -ESCAPE_HTML_REGEXP = /[&<>"'\/]/g - -$.escape = (string) -> - string.replace ESCAPE_HTML_REGEXP, (match) -> ESCAPE_HTML_MAP[match] - -ESCAPE_REGEXP = /([.*+?^=!:${}()|\[\]\/\\])/g - -$.escapeRegexp = (string) -> - string.replace ESCAPE_REGEXP, "\\$1" - -$.urlDecode = (string) -> - decodeURIComponent string.replace(/\+/g, '%20') - -$.classify = (string) -> - string = string.split('_') - for substr, i in string - string[i] = substr[0].toUpperCase() + substr[1..] - string.join('') - -$.framify = (fn, obj) -> - if window.requestAnimationFrame - (args...) -> requestAnimationFrame(fn.bind(obj, args...)) - else - fn - -$.requestAnimationFrame = (fn) -> - if window.requestAnimationFrame - requestAnimationFrame(fn) - else - setTimeout(fn, 0) - return - -# -# Miscellaneous -# - -$.noop = -> - -$.popup = (value) -> - try - win = window.open() - win.opener = null if win.opener - win.location = value.href or value - catch - window.open value.href or value, '_blank' - return - -isMac = null -$.isMac = -> - isMac ?= navigator.userAgent?.indexOf('Mac') >= 0 - -isIE = null -$.isIE = -> - isIE ?= navigator.userAgent?.indexOf('MSIE') >= 0 || navigator.userAgent?.indexOf('rv:11.0') >= 0 - -isChromeForAndroid = null -$.isChromeForAndroid = -> - isChromeForAndroid ?= navigator.userAgent?.indexOf('Android') >= 0 && /Chrome\/([.0-9])+ Mobile/.test(navigator.userAgent) - -isAndroid = null -$.isAndroid = -> - isAndroid ?= navigator.userAgent?.indexOf('Android') >= 0 - -isIOS = null -$.isIOS = -> - isIOS ?= navigator.userAgent?.indexOf('iPhone') >= 0 || navigator.userAgent?.indexOf('iPad') >= 0 - -$.overlayScrollbarsEnabled = -> - return false unless $.isMac() - div = document.createElement('div') - div.setAttribute('style', 'width: 100px; height: 100px; overflow: scroll; position: absolute') - document.body.appendChild(div) - result = div.offsetWidth is div.clientWidth - document.body.removeChild(div) - result - -HIGHLIGHT_DEFAULTS = - className: 'highlight' +}; + +const ESCAPE_HTML_REGEXP = /[&<>"'\/]/g; + +$.escape = string => string.replace(ESCAPE_HTML_REGEXP, match => ESCAPE_HTML_MAP[match]); + +const ESCAPE_REGEXP = /([.*+?^=!:${}()|\[\]\/\\])/g; + +$.escapeRegexp = string => string.replace(ESCAPE_REGEXP, "\\$1"); + +$.urlDecode = string => decodeURIComponent(string.replace(/\+/g, '%20')); + +$.classify = function(string) { + string = string.split('_'); + for (let i = 0; i < string.length; i++) { + var substr = string[i]; + string[i] = substr[0].toUpperCase() + substr.slice(1); + } + return string.join(''); +}; + +$.framify = function(fn, obj) { + if (window.requestAnimationFrame) { + return (...args) => requestAnimationFrame(fn.bind(obj, ...Array.from(args))); + } else { + return fn; + } +}; + +$.requestAnimationFrame = function(fn) { + if (window.requestAnimationFrame) { + requestAnimationFrame(fn); + } else { + setTimeout(fn, 0); + } +}; + +// +// Miscellaneous +// + +$.noop = function() {}; + +$.popup = function(value) { + try { + const win = window.open(); + if (win.opener) { win.opener = null; } + win.location = value.href || value; + } catch (error) { + window.open(value.href || value, '_blank'); + } +}; + +let isMac = null; +$.isMac = () => isMac != null ? isMac : (isMac = (navigator.userAgent != null ? navigator.userAgent.indexOf('Mac') : undefined) >= 0); + +let isIE = null; +$.isIE = () => isIE != null ? isIE : (isIE = ((navigator.userAgent != null ? navigator.userAgent.indexOf('MSIE') : undefined) >= 0) || ((navigator.userAgent != null ? navigator.userAgent.indexOf('rv:11.0') : undefined) >= 0)); + +let isChromeForAndroid = null; +$.isChromeForAndroid = () => isChromeForAndroid != null ? isChromeForAndroid : (isChromeForAndroid = ((navigator.userAgent != null ? navigator.userAgent.indexOf('Android') : undefined) >= 0) && /Chrome\/([.0-9])+ Mobile/.test(navigator.userAgent)); + +let isAndroid = null; +$.isAndroid = () => isAndroid != null ? isAndroid : (isAndroid = (navigator.userAgent != null ? navigator.userAgent.indexOf('Android') : undefined) >= 0); + +let isIOS = null; +$.isIOS = () => isIOS != null ? isIOS : (isIOS = ((navigator.userAgent != null ? navigator.userAgent.indexOf('iPhone') : undefined) >= 0) || ((navigator.userAgent != null ? navigator.userAgent.indexOf('iPad') : undefined) >= 0)); + +$.overlayScrollbarsEnabled = function() { + if (!$.isMac()) { return false; } + const div = document.createElement('div'); + div.setAttribute('style', 'width: 100px; height: 100px; overflow: scroll; position: absolute'); + document.body.appendChild(div); + const result = div.offsetWidth === div.clientWidth; + document.body.removeChild(div); + return result; +}; + +const HIGHLIGHT_DEFAULTS = { + className: 'highlight', delay: 1000 - -$.highlight = (el, options = {}) -> - options = $.extend {}, HIGHLIGHT_DEFAULTS, options - el.classList.add(options.className) - setTimeout (-> el.classList.remove(options.className)), options.delay - return - -$.copyToClipboard = (string) -> - textarea = document.createElement('textarea') - textarea.style.position = 'fixed' - textarea.style.opacity = 0 - textarea.value = string - document.body.appendChild(textarea) - try - textarea.select() - result = !!document.execCommand('copy') - catch - result = false - finally - document.body.removeChild(textarea) - result +}; + +$.highlight = function(el, options) { + if (options == null) { options = {}; } + options = $.extend({}, HIGHLIGHT_DEFAULTS, options); + el.classList.add(options.className); + setTimeout((() => el.classList.remove(options.className)), options.delay); +}; + +$.copyToClipboard = function(string) { + let result; + const textarea = document.createElement('textarea'); + textarea.style.position = 'fixed'; + textarea.style.opacity = 0; + textarea.value = string; + document.body.appendChild(textarea); + try { + textarea.select(); + result = !!document.execCommand('copy'); + } catch (error) { + result = false; + } + finally { + document.body.removeChild(textarea); + } + return result; +}; + +function __guard__(value, transform) { + return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; +} \ No newline at end of file diff --git a/assets/javascripts/models/doc.js b/assets/javascripts/models/doc.js index c51e13fae4..8ca5b01422 100644 --- a/assets/javascripts/models/doc.js +++ b/assets/javascripts/models/doc.js @@ -1,147 +1,174 @@ -class app.models.Doc extends app.Model - # Attributes: name, slug, type, version, release, db_size, mtime, links - - constructor: -> - super - @reset @ - @slug_without_version = @slug.split('~')[0] - @fullName = "#{@name}" + if @version then " #{@version}" else '' - @icon = @slug_without_version - @short_version = @version.split(' ')[0] if @version - @text = @toEntry().text - - reset: (data) -> - @resetEntries data.entries - @resetTypes data.types - return - - resetEntries: (entries) -> - @entries = new app.collections.Entries(entries) - @entries.each (entry) => entry.doc = @ - return - - resetTypes: (types) -> - @types = new app.collections.Types(types) - @types.each (type) => type.doc = @ - return - - fullPath: (path = '') -> - path = "/#{path}" unless path[0] is '/' - "/#{@slug}#{path}" - - fileUrl: (path) -> - "#{app.config.docs_origin}#{@fullPath(path)}?#{@mtime}" - - dbUrl: -> - "#{app.config.docs_origin}/#{@slug}/#{app.config.db_filename}?#{@mtime}" - - indexUrl: -> - "#{app.indexHost()}/#{@slug}/#{app.config.index_filename}?#{@mtime}" - - toEntry: -> - return @entry if @entry - @entry = new app.models.Entry - doc: @ - name: @fullName +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +app.models.Doc = class Doc extends app.Model { + // Attributes: name, slug, type, version, release, db_size, mtime, links + + constructor() { + super(...arguments); + this.reset(this); + this.slug_without_version = this.slug.split('~')[0]; + this.fullName = `${this.name}` + (this.version ? ` ${this.version}` : ''); + this.icon = this.slug_without_version; + if (this.version) { this.short_version = this.version.split(' ')[0]; } + this.text = this.toEntry().text; + } + + reset(data) { + this.resetEntries(data.entries); + this.resetTypes(data.types); + } + + resetEntries(entries) { + this.entries = new app.collections.Entries(entries); + this.entries.each(entry => { return entry.doc = this; }); + } + + resetTypes(types) { + this.types = new app.collections.Types(types); + this.types.each(type => { return type.doc = this; }); + } + + fullPath(path) { + if (path == null) { path = ''; } + if (path[0] !== '/') { path = `/${path}`; } + return `/${this.slug}${path}`; + } + + fileUrl(path) { + return `${app.config.docs_origin}${this.fullPath(path)}?${this.mtime}`; + } + + dbUrl() { + return `${app.config.docs_origin}/${this.slug}/${app.config.db_filename}?${this.mtime}`; + } + + indexUrl() { + return `${app.indexHost()}/${this.slug}/${app.config.index_filename}?${this.mtime}`; + } + + toEntry() { + if (this.entry) { return this.entry; } + this.entry = new app.models.Entry({ + doc: this, + name: this.fullName, path: 'index' - @entry.addAlias(@name) if @version - @entry - - findEntryByPathAndHash: (path, hash) -> - if hash and entry = @entries.findBy 'path', "#{path}##{hash}" - entry - else if path is 'index' - @toEntry() - else - @entries.findBy 'path', path - - load: (onSuccess, onError, options = {}) -> - return if options.readCache and @_loadFromCache(onSuccess) - - callback = (data) => - @reset data - onSuccess() - @_setCache data if options.writeCache - return - - ajax - url: @indexUrl() - success: callback + }); + if (this.version) { this.entry.addAlias(this.name); } + return this.entry; + } + + findEntryByPathAndHash(path, hash) { + let entry; + if (hash && (entry = this.entries.findBy('path', `${path}#${hash}`))) { + return entry; + } else if (path === 'index') { + return this.toEntry(); + } else { + return this.entries.findBy('path', path); + } + } + + load(onSuccess, onError, options) { + if (options == null) { options = {}; } + if (options.readCache && this._loadFromCache(onSuccess)) { return; } + + const callback = data => { + this.reset(data); + onSuccess(); + if (options.writeCache) { this._setCache(data); } + }; + + return ajax({ + url: this.indexUrl(), + success: callback, error: onError - - clearCache: -> - app.localStorage.del @slug - return - - _loadFromCache: (onSuccess) -> - return unless data = @_getCache() - - callback = => - @reset data - onSuccess() - return - - setTimeout callback, 0 - true - - _getCache: -> - return unless data = app.localStorage.get @slug - - if data[0] is @mtime - return data[1] - else - @clearCache() - return - - _setCache: (data) -> - app.localStorage.set @slug, [@mtime, data] - return - - install: (onSuccess, onError, onProgress) -> - return if @installing - @installing = true - - error = => - @installing = null - onError() - return - - success = (data) => - @installing = null - app.db.store @, data, onSuccess, error - return - - ajax - url: @dbUrl() - success: success - error: error - progress: onProgress + }); + } + + clearCache() { + app.localStorage.del(this.slug); + } + + _loadFromCache(onSuccess) { + let data; + if (!(data = this._getCache())) { return; } + + const callback = () => { + this.reset(data); + onSuccess(); + }; + + setTimeout(callback, 0); + return true; + } + + _getCache() { + let data; + if (!(data = app.localStorage.get(this.slug))) { return; } + + if (data[0] === this.mtime) { + return data[1]; + } else { + this.clearCache(); + return; + } + } + + _setCache(data) { + app.localStorage.set(this.slug, [this.mtime, data]); + } + + install(onSuccess, onError, onProgress) { + if (this.installing) { return; } + this.installing = true; + + const error = () => { + this.installing = null; + onError(); + }; + + const success = data => { + this.installing = null; + app.db.store(this, data, onSuccess, error); + }; + + ajax({ + url: this.dbUrl(), + success, + error, + progress: onProgress, timeout: 3600 - return - - uninstall: (onSuccess, onError) -> - return if @installing - @installing = true - - success = => - @installing = null - onSuccess() - return - - error = => - @installing = null - onError() - return - - app.db.unstore @, success, error - return - - getInstallStatus: (callback) -> - app.db.version @, (value) -> - callback installed: !!value, mtime: value - return - - isOutdated: (status) -> - return false if not status - isInstalled = status.installed or app.settings.get('autoInstall') - isInstalled and @mtime isnt status.mtime + }); + } + + uninstall(onSuccess, onError) { + if (this.installing) { return; } + this.installing = true; + + const success = () => { + this.installing = null; + onSuccess(); + }; + + const error = () => { + this.installing = null; + onError(); + }; + + app.db.unstore(this, success, error); + } + + getInstallStatus(callback) { + app.db.version(this, value => callback({installed: !!value, mtime: value})); + } + + isOutdated(status) { + if (!status) { return false; } + const isInstalled = status.installed || app.settings.get('autoInstall'); + return isInstalled && (this.mtime !== status.mtime); + } +}; diff --git a/assets/javascripts/models/entry.js b/assets/javascripts/models/entry.js index 2d07c1599c..99b0b525c6 100644 --- a/assets/javascripts/models/entry.js +++ b/assets/javascripts/models/entry.js @@ -1,85 +1,116 @@ -#= require app/searcher +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS206: Consider reworking classes to avoid initClass + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +//= require app/searcher -class app.models.Entry extends app.Model - # Attributes: name, type, path +(function() { + let applyAliases = undefined; + const Cls = (app.models.Entry = class Entry extends app.Model { + static initClass() { + + let ALIASES; + applyAliases = function(string) { + if (ALIASES.hasOwnProperty(string)) { + return [string, ALIASES[string]]; + } else { + const words = string.split('.'); + for (let i = 0; i < words.length; i++) { + var word = words[i]; + if (ALIASES.hasOwnProperty(word)) { + words[i] = ALIASES[word]; + return [string, words.join('.')]; + } + } + } + return string; + }; + + this.ALIASES = (ALIASES = { + 'angular': 'ng', + 'angular.js': 'ng', + 'backbone.js': 'bb', + 'c++': 'cpp', + 'coffeescript': 'cs', + 'crystal': 'cr', + 'elixir': 'ex', + 'javascript': 'js', + 'julia': 'jl', + 'jquery': '$', + 'knockout.js': 'ko', + 'kubernetes': 'k8s', + 'less': 'ls', + 'lodash': '_', + 'löve': 'love', + 'marionette': 'mn', + 'markdown': 'md', + 'matplotlib': 'mpl', + 'modernizr': 'mdr', + 'moment.js': 'mt', + 'openjdk': 'java', + 'nginx': 'ngx', + 'numpy': 'np', + 'pandas': 'pd', + 'postgresql': 'pg', + 'python': 'py', + 'ruby.on.rails': 'ror', + 'ruby': 'rb', + 'rust': 'rs', + 'sass': 'scss', + 'tensorflow': 'tf', + 'typescript': 'ts', + 'underscore.js': '_' + }); + } + // Attributes: name, type, path - constructor: -> - super - @text = applyAliases(app.Searcher.normalizeString(@name)) + constructor() { + super(...arguments); + this.text = applyAliases(app.Searcher.normalizeString(this.name)); + } - addAlias: (name) -> - text = applyAliases(app.Searcher.normalizeString(name)) - @text = [@text] unless Array.isArray(@text) - @text.push(if Array.isArray(text) then text[1] else text) - return + addAlias(name) { + const text = applyAliases(app.Searcher.normalizeString(name)); + if (!Array.isArray(this.text)) { this.text = [this.text]; } + this.text.push(Array.isArray(text) ? text[1] : text); + } - fullPath: -> - @doc.fullPath if @isIndex() then '' else @path + fullPath() { + return this.doc.fullPath(this.isIndex() ? '' : this.path); + } - dbPath: -> - @path.replace /#.*/, '' + dbPath() { + return this.path.replace(/#.*/, ''); + } - filePath: -> - @doc.fullPath @_filePath() + filePath() { + return this.doc.fullPath(this._filePath()); + } - fileUrl: -> - @doc.fileUrl @_filePath() + fileUrl() { + return this.doc.fileUrl(this._filePath()); + } - _filePath: -> - result = @path.replace /#.*/, '' - result += '.html' unless result[-5..-1] is '.html' - result + _filePath() { + let result = this.path.replace(/#.*/, ''); + if (result.slice(-5) !== '.html') { result += '.html'; } + return result; + } - isIndex: -> - @path is 'index' + isIndex() { + return this.path === 'index'; + } - getType: -> - @doc.types.findBy 'name', @type + getType() { + return this.doc.types.findBy('name', this.type); + } - loadFile: (onSuccess, onError) -> - app.db.load(@, onSuccess, onError) - - applyAliases = (string) -> - if ALIASES.hasOwnProperty(string) - return [string, ALIASES[string]] - else - words = string.split('.') - for word, i in words when ALIASES.hasOwnProperty(word) - words[i] = ALIASES[word] - return [string, words.join('.')] - return string - - @ALIASES = ALIASES = - 'angular': 'ng' - 'angular.js': 'ng' - 'backbone.js': 'bb' - 'c++': 'cpp' - 'coffeescript': 'cs' - 'crystal': 'cr' - 'elixir': 'ex' - 'javascript': 'js' - 'julia': 'jl' - 'jquery': '$' - 'knockout.js': 'ko' - 'kubernetes': 'k8s' - 'less': 'ls' - 'lodash': '_' - 'löve': 'love' - 'marionette': 'mn' - 'markdown': 'md' - 'matplotlib': 'mpl' - 'modernizr': 'mdr' - 'moment.js': 'mt' - 'openjdk': 'java' - 'nginx': 'ngx' - 'numpy': 'np' - 'pandas': 'pd' - 'postgresql': 'pg' - 'python': 'py' - 'ruby.on.rails': 'ror' - 'ruby': 'rb' - 'rust': 'rs' - 'sass': 'scss' - 'tensorflow': 'tf' - 'typescript': 'ts' - 'underscore.js': '_' + loadFile(onSuccess, onError) { + return app.db.load(this, onSuccess, onError); + } + }); + Cls.initClass(); + return Cls; +})(); diff --git a/assets/javascripts/models/model.js b/assets/javascripts/models/model.js index 7f157f7c4f..405032b6ee 100644 --- a/assets/javascripts/models/model.js +++ b/assets/javascripts/models/model.js @@ -1,3 +1,5 @@ -class app.Model - constructor: (attributes) -> - @[key] = value for key, value of attributes +app.Model = class Model { + constructor(attributes) { + for (var key in attributes) { var value = attributes[key]; this[key] = value; } + } +}; diff --git a/assets/javascripts/models/type.js b/assets/javascripts/models/type.js index 6351ad16d7..29c30ac99f 100644 --- a/assets/javascripts/models/type.js +++ b/assets/javascripts/models/type.js @@ -1,14 +1,24 @@ -class app.models.Type extends app.Model - # Attributes: name, slug, count +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +app.models.Type = class Type extends app.Model { + // Attributes: name, slug, count - fullPath: -> - "/#{@doc.slug}-#{@slug}/" + fullPath() { + return `/${this.doc.slug}-${this.slug}/`; + } - entries: -> - @doc.entries.findAllBy 'type', @name + entries() { + return this.doc.entries.findAllBy('type', this.name); + } - toEntry: -> - new app.models.Entry - doc: @doc - name: "#{@doc.name} / #{@name}" - path: '..' + @fullPath() + toEntry() { + return new app.models.Entry({ + doc: this.doc, + name: `${this.doc.name} / ${this.name}`, + path: '..' + this.fullPath() + }); + } +}; diff --git a/assets/javascripts/templates/base.js b/assets/javascripts/templates/base.js index 841d1e0bb7..efcf7a1b96 100644 --- a/assets/javascripts/templates/base.js +++ b/assets/javascripts/templates/base.js @@ -1,11 +1,19 @@ -app.templates.render = (name, value, args...) -> - template = app.templates[name] +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +app.templates.render = function(name, value, ...args) { + const template = app.templates[name]; - if Array.isArray(value) - result = '' - result += template(val, args...) for val in value - result - else if typeof template is 'function' - template(value, args...) - else - template + if (Array.isArray(value)) { + let result = ''; + for (var val of Array.from(value)) { result += template(val, ...Array.from(args)); } + return result; + } else if (typeof template === 'function') { + return template(value, ...Array.from(args)); + } else { + return template; + } +}; diff --git a/assets/javascripts/templates/error_tmpl.js b/assets/javascripts/templates/error_tmpl.js index 9cca1f9d32..c5b8ac6225 100644 --- a/assets/javascripts/templates/error_tmpl.js +++ b/assets/javascripts/templates/error_tmpl.js @@ -1,73 +1,85 @@ -error = (title, text = '', links = '') -> - text = """

#{text}

""" if text - links = """""" if links - """

#{title}

#{text}#{links}
""" +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS205: Consider reworking code to avoid use of IIFEs + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +const error = function(title, text, links) { + if (text == null) { text = ''; } + if (links == null) { links = ''; } + if (text) { text = `

${text}

`; } + if (links) { links = ``; } + return `

${title}

${text}${links}
`; +}; -back = 'Go back' +const back = 'Go back'; -app.templates.notFoundPage = -> - error """ Page not found. """, - """ It may be missing from the source documentation or this could be a bug. """, - back +app.templates.notFoundPage = () => error(" Page not found. ", + " It may be missing from the source documentation or this could be a bug. ", + back); -app.templates.pageLoadError = -> - error """ The page failed to load. """, - """ It may be missing from the server (try reloading the app) or you could be offline (try installing the documentation for offline usage when online again).
- If you're online and you keep seeing this, you're likely behind a proxy or firewall that blocks cross-domain requests. """, - """ #{back} · Reload - · Retry """ +app.templates.pageLoadError = () => error(" The page failed to load. ", + ` It may be missing from the server (try reloading the app) or you could be offline (try installing the documentation for offline usage when online again).
+If you're online and you keep seeing this, you're likely behind a proxy or firewall that blocks cross-domain requests. `, + ` ${back} · ReloadRetry ` +); -app.templates.bootError = -> - error """ The app failed to load. """, - """ Check your Internet connection and try reloading.
- If you keep seeing this, you're likely behind a proxy or firewall that blocks cross-domain requests. """ +app.templates.bootError = () => error(" The app failed to load. ", + ` Check your Internet connection and try reloading.
+If you keep seeing this, you're likely behind a proxy or firewall that blocks cross-domain requests. ` +); -app.templates.offlineError = (reason, exception) -> - if reason is 'cookie_blocked' - return error """ Cookies must be enabled to use offline mode. """ +app.templates.offlineError = function(reason, exception) { + if (reason === 'cookie_blocked') { + return error(" Cookies must be enabled to use offline mode. "); + } - reason = switch reason - when 'not_supported' - """ DevDocs requires IndexedDB to cache documentations for offline access.
- Unfortunately your browser either doesn't support IndexedDB or doesn't make it available. """ - when 'buggy' - """ DevDocs requires IndexedDB to cache documentations for offline access.
- Unfortunately your browser's implementation of IndexedDB contains bugs that prevent DevDocs from using it. """ - when 'private_mode' - """ Your browser appears to be running in private mode.
- This prevents DevDocs from caching documentations for offline access.""" - when 'exception' - """ An error occurred when trying to open the IndexedDB database:
- #{exception.name}: #{exception.message} """ - when 'cant_open' - """ An error occurred when trying to open the IndexedDB database:
- #{exception.name}: #{exception.message}
- This could be because you're browsing in private mode or have disallowed offline storage on the domain. """ - when 'version' - """ The IndexedDB database was modified with a newer version of the app.
- Reload the page to use offline mode. """ - when 'empty' - """ The IndexedDB database appears to be corrupted. Try resetting the app. """ + reason = (() => { switch (reason) { + case 'not_supported': + return ` DevDocs requires IndexedDB to cache documentations for offline access.
+Unfortunately your browser either doesn't support IndexedDB or doesn't make it available. `; + case 'buggy': + return ` DevDocs requires IndexedDB to cache documentations for offline access.
+Unfortunately your browser's implementation of IndexedDB contains bugs that prevent DevDocs from using it. `; + case 'private_mode': + return ` Your browser appears to be running in private mode.
+This prevents DevDocs from caching documentations for offline access.`; + case 'exception': + return ` An error occurred when trying to open the IndexedDB database:
+${exception.name}: ${exception.message} `; + case 'cant_open': + return ` An error occurred when trying to open the IndexedDB database:
+${exception.name}: ${exception.message}
+This could be because you're browsing in private mode or have disallowed offline storage on the domain. `; + case 'version': + return ` The IndexedDB database was modified with a newer version of the app.
+Reload the page to use offline mode. `; + case 'empty': + return " The IndexedDB database appears to be corrupted. Try resetting the app. "; + } })(); - error 'Offline mode is unavailable.', reason + return error('Offline mode is unavailable.', reason); +}; -app.templates.unsupportedBrowser = """ -
-

Your browser is unsupported, sorry.

-

DevDocs is an API documentation browser which supports the following browsers: -

    -
  • Recent versions of Firefox, Chrome, or Opera -
  • Safari 11.1+ -
  • Edge 17+ -
  • iOS 11.3+ -
-

- If you're unable to upgrade, we apologize. - We decided to prioritize speed and new features over support for older browsers. -

- Note: if you're already using one of the browsers above, check your settings and add-ons. - The app uses feature detection, not user agent sniffing. -

- — @DevDocs -

-""" +app.templates.unsupportedBrowser = `\ +
+

Your browser is unsupported, sorry.

+

DevDocs is an API documentation browser which supports the following browsers: +

    +
  • Recent versions of Firefox, Chrome, or Opera +
  • Safari 11.1+ +
  • Edge 17+ +
  • iOS 11.3+ +
+

+ If you're unable to upgrade, we apologize. + We decided to prioritize speed and new features over support for older browsers. +

+ Note: if you're already using one of the browsers above, check your settings and add-ons. + The app uses feature detection, not user agent sniffing. +

+ — @DevDocs +

\ +`; diff --git a/assets/javascripts/templates/notice_tmpl.js b/assets/javascripts/templates/notice_tmpl.js index 10cc534eb0..2d64ecd276 100644 --- a/assets/javascripts/templates/notice_tmpl.js +++ b/assets/javascripts/templates/notice_tmpl.js @@ -1,9 +1,14 @@ -notice = (text) -> """

#{text}

""" +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +const notice = text => `

${text}

`; -app.templates.singleDocNotice = (doc) -> - notice """ You're browsing the #{doc.fullName} documentation. To browse all docs, go to - #{app.config.production_host} (or press esc). """ +app.templates.singleDocNotice = doc => notice(` You're browsing the ${doc.fullName} documentation. To browse all docs, go to +${app.config.production_host} (or press esc). ` +); -app.templates.disabledDocNotice = -> - notice """ This documentation is disabled. - To enable it, go to Preferences. """ +app.templates.disabledDocNotice = () => notice(` This documentation is disabled. +To enable it, go to Preferences. ` +); diff --git a/assets/javascripts/templates/notif_tmpl.js b/assets/javascripts/templates/notif_tmpl.js index 0821036e77..f58808c6f5 100644 --- a/assets/javascripts/templates/notif_tmpl.js +++ b/assets/javascripts/templates/notif_tmpl.js @@ -1,76 +1,81 @@ -notif = (title, html) -> - html = html.replace /#{title} - #{html} - - +
+ + +
-
- - - - - - - - #{docs} -
DocumentationSizeStatusAction
-
-

Note: your browser may delete DevDocs's offline data if your computer is running low on disk space and you haven't used the app in a while. Load this page before going offline to make sure the data is still there. -

Questions & Answers

-
-
How does this work? -
Each page is cached as a key-value pair in IndexedDB (downloaded from a single file).
- The app also uses Service Workers and localStorage to cache the assets and index files. -
Can I close the tab/browser? -
#{canICloseTheTab()} -
What if I don't update a documentation? -
You'll see outdated content and some pages will be missing or broken, because the rest of the app (including data for the search and sidebar) uses a different caching mechanism that's updated automatically. -
I found a bug, where do I report it? -
In the issue tracker. Thanks! -
How do I uninstall/reset the app? -
Click here. -
Why aren't all documentations listed above? -
You have to enable them first. -
-""" +
+ + + + + + + + ${docs} +
DocumentationSizeStatusAction
+
+

Note: your browser may delete DevDocs's offline data if your computer is running low on disk space and you haven't used the app in a while. Load this page before going offline to make sure the data is still there. +

Questions & Answers

+
+
How does this work? +
Each page is cached as a key-value pair in IndexedDB (downloaded from a single file).
+ The app also uses Service Workers and localStorage to cache the assets and index files. +
Can I close the tab/browser? +
${canICloseTheTab()} +
What if I don't update a documentation? +
You'll see outdated content and some pages will be missing or broken, because the rest of the app (including data for the search and sidebar) uses a different caching mechanism that's updated automatically. +
I found a bug, where do I report it? +
In the issue tracker. Thanks! +
How do I uninstall/reset the app? +
Click here. +
Why aren't all documentations listed above? +
You have to enable them first. +
\ +`; -canICloseTheTab = -> - if app.ServiceWorker.isEnabled() - """ Yes! Even offline, you can open a new tab, go to devdocs.io, and everything will work as if you were online (provided you installed all the documentations you want to use beforehand). """ - else - reason = "aren't available in your browser (or are disabled)" +var canICloseTheTab = function() { + if (app.ServiceWorker.isEnabled()) { + return " Yes! Even offline, you can open a new tab, go to devdocs.io, and everything will work as if you were online (provided you installed all the documentations you want to use beforehand). "; + } else { + let reason = "aren't available in your browser (or are disabled)"; - if app.config.env != 'production' - reason = "are disabled in your development instance of DevDocs (enable them by setting the ENABLE_SERVICE_WORKER environment variable to true)" + if (app.config.env !== 'production') { + reason = "are disabled in your development instance of DevDocs (enable them by setting the ENABLE_SERVICE_WORKER environment variable to true)"; + } - """ No. Service Workers #{reason}, so loading devdocs.io offline won't work.
- The current tab will continue to function even when you go offline (provided you installed all the documentations beforehand). """ + return ` No. Service Workers ${reason}, so loading devdocs.io offline won't work.
+The current tab will continue to function even when you go offline (provided you installed all the documentations beforehand). `; + } +}; -app.templates.offlineDoc = (doc, status) -> - outdated = doc.isOutdated(status) +app.templates.offlineDoc = function(doc, status) { + const outdated = doc.isOutdated(status); - html = """ - - #{doc.fullName} - #{Math.ceil(doc.db_size / 100000) / 10} MB - """ + let html = `\ + + ${doc.fullName} + ${Math.ceil(doc.db_size / 100000) / 10} MB\ +`; - html += if !(status and status.installed) - """ - - - - """ - else if outdated - """ - Outdated - - - """ - else - """ - Up‑to‑date - - """ + html += !(status && status.installed) ? + `\ +- +\ +` + : outdated ? + `\ +Outdated + - \ +` + : + `\ +Up‑to‑date +\ +`; - html + '' + return html + ''; +}; diff --git a/assets/javascripts/templates/pages/settings_tmpl.js b/assets/javascripts/templates/pages/settings_tmpl.js index 048afa1a8e..26c1eb9742 100644 --- a/assets/javascripts/templates/pages/settings_tmpl.js +++ b/assets/javascripts/templates/pages/settings_tmpl.js @@ -1,81 +1,86 @@ -themeOption = ({ label, value }, settings) -> """ - -""" +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +const themeOption = ({ label, value }, settings) => `\ +\ +`; -app.templates.settingsPage = (settings) -> """ -

Preferences

+app.templates.settingsPage = settings => `\ +

Preferences

-
-

Theme:

-
- #{if settings.autoSupported - themeOption label: "Automatic Matches system setting", value: "auto", settings - else +
+

Theme:

+
+ ${settings.autoSupported ? + themeOption({label: "Automatic Matches system setting", value: "auto"}, settings) + : ""} - #{themeOption label: "Light", value: "default", settings} - #{themeOption label: "Dark", value: "dark", settings} -
+ ${themeOption({label: "Light", value: "default"}, settings)} + ${themeOption({label: "Dark", value: "dark"}, settings)}
+
-
-

General:

+
+

General:

-
- - - - - - -
+
+ + + + + +
+
-
-

Scrolling:

+
+

Scrolling:

-
- - - - - -
+
+ + + + +
+
-

- - +

+ + -

- -""" +

+ \ +`; diff --git a/assets/javascripts/templates/pages/type_tmpl.js b/assets/javascripts/templates/pages/type_tmpl.js index c419a6a8c4..c130884286 100644 --- a/assets/javascripts/templates/pages/type_tmpl.js +++ b/assets/javascripts/templates/pages/type_tmpl.js @@ -1,6 +1,9 @@ -app.templates.typePage = (type) -> - """

#{type.doc.fullName} / #{type.name}

-
    #{app.templates.render 'typePageEntry', type.entries()}
""" +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +app.templates.typePage = type => `

${type.doc.fullName} / ${type.name}

+
    ${app.templates.render('typePageEntry', type.entries())}
`; -app.templates.typePageEntry = (entry) -> - """
  • #{$.escape entry.name}
  • """ +app.templates.typePageEntry = entry => `
  • ${$.escape(entry.name)}
  • `; diff --git a/assets/javascripts/templates/path_tmpl.js b/assets/javascripts/templates/path_tmpl.js index f28925c9ca..d0344542d6 100644 --- a/assets/javascripts/templates/path_tmpl.js +++ b/assets/javascripts/templates/path_tmpl.js @@ -1,7 +1,13 @@ -arrow = """""" +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +const arrow = ""; -app.templates.path = (doc, type, entry) -> - html = """#{doc.fullName}""" - html += """#{arrow}#{type.name}""" if type - html += """#{arrow}#{$.escape entry.name}""" if entry - html +app.templates.path = function(doc, type, entry) { + let html = `${doc.fullName}`; + if (type) { html += `${arrow}${type.name}`; } + if (entry) { html += `${arrow}${$.escape(entry.name)}`; } + return html; +}; diff --git a/assets/javascripts/templates/sidebar_tmpl.js b/assets/javascripts/templates/sidebar_tmpl.js index 46797e5658..2f9c7ce3b1 100644 --- a/assets/javascripts/templates/sidebar_tmpl.js +++ b/assets/javascripts/templates/sidebar_tmpl.js @@ -1,68 +1,79 @@ -templates = app.templates +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +const { + templates +} = app; -arrow = """""" +const arrow = ""; -templates.sidebarDoc = (doc, options = {}) -> - link = """""" - if options.disabled - link += """Enable""" - else - link += arrow - link += """#{doc.release}""" if doc.release - link += """#{doc.name}""" - link += " #{doc.version}" if options.fullName or options.disabled and doc.version - link + "" +templates.sidebarDoc = function(doc, options) { + if (options == null) { options = {}; } + let link = ``; + if (options.disabled) { + link += `Enable`; + } else { + link += arrow; + } + if (doc.release) { link += `${doc.release}`; } + link += `${doc.name}`; + if (options.fullName || (options.disabled && doc.version)) { link += ` ${doc.version}`; } + return link + ""; +}; -templates.sidebarType = (type) -> - """#{arrow}#{type.count}#{$.escape type.name}""" +templates.sidebarType = type => `${arrow}${type.count}${$.escape(type.name)}`; -templates.sidebarEntry = (entry) -> - """#{$.escape entry.name}""" +templates.sidebarEntry = entry => `${$.escape(entry.name)}`; -templates.sidebarResult = (entry) -> - addons = if entry.isIndex() and app.disabledDocs.contains(entry.doc) - """Enable""" - else - """""" - addons += """#{entry.doc.short_version}""" if entry.doc.version and not entry.isIndex() - """#{addons}#{$.escape entry.name}""" +templates.sidebarResult = function(entry) { + let addons = entry.isIndex() && app.disabledDocs.contains(entry.doc) ? + `Enable` + : + ""; + if (entry.doc.version && !entry.isIndex()) { addons += `${entry.doc.short_version}`; } + return `${addons}${$.escape(entry.name)}`; +}; -templates.sidebarNoResults = -> - html = """
    No results.
    """ - html += """ -
    Note: documentations must be enabled to appear in the search.
    - """ unless app.isSingleDoc() or app.disabledDocs.isEmpty() - html +templates.sidebarNoResults = function() { + let html = "
    No results.
    "; + if (!app.isSingleDoc() && !app.disabledDocs.isEmpty()) { html += `\ +
    Note: documentations must be enabled to appear in the search.
    \ +`; } + return html; +}; -templates.sidebarPageLink = (count) -> - """Show more\u2026 (#{count})""" +templates.sidebarPageLink = count => `Show more\u2026 (${count})`; -templates.sidebarLabel = (doc, options = {}) -> - label = """""" +templates.sidebarLabel = function(doc, options) { + if (options == null) { options = {}; } + let label = "`; +}; -templates.sidebarVersionedDoc = (doc, versions, options = {}) -> - html = """
    #{arrow}#{doc.name}
    #{versions}
    """ +templates.sidebarVersionedDoc = function(doc, versions, options) { + if (options == null) { options = {}; } + let html = `
    ${arrow}${doc.name}
    ${versions}
    `; +}; -templates.sidebarDisabled = (options) -> - """
    #{arrow}Disabled (#{options.count}) Customize
    """ +templates.sidebarDisabled = options => `
    ${arrow}Disabled (${options.count}) Customize
    `; -templates.sidebarDisabledList = (html) -> - """
    #{html}
    """ +templates.sidebarDisabledList = html => `
    ${html}
    `; -templates.sidebarDisabledVersionedDoc = (doc, versions) -> - """#{arrow}#{doc.name}
    #{versions}
    """ +templates.sidebarDisabledVersionedDoc = (doc, versions) => `${arrow}${doc.name}
    ${versions}
    `; -templates.docPickerHeader = """
    Documentation Enable
    """ +templates.docPickerHeader = "
    Documentation Enable
    "; -templates.docPickerNote = """ -
    Tip: for faster and better search results, select only the docs you need.
    - Vote for new documentation - """ +templates.docPickerNote = `\ +
    Tip: for faster and better search results, select only the docs you need.
    +Vote for new documentation\ +`; diff --git a/assets/javascripts/templates/tip_tmpl.js b/assets/javascripts/templates/tip_tmpl.js index 55979fa452..665ac327a8 100644 --- a/assets/javascripts/templates/tip_tmpl.js +++ b/assets/javascripts/templates/tip_tmpl.js @@ -1,10 +1,15 @@ -app.templates.tipKeyNav = () -> """ -

    - ProTip - (click to dismiss) -

    - Hit #{if app.settings.get('arrowScroll') then 'shift +' else ''} to navigate the sidebar.
    - Hit space / shift space#{if app.settings.get('arrowScroll') then ' or ↓/↑' else ', alt ↓/↑ or shift ↓/↑'} to scroll the page. -

    - See all keyboard shortcuts -""" +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +app.templates.tipKeyNav = () => `\ +

    + ProTip + (click to dismiss) +

    + Hit ${app.settings.get('arrowScroll') ? 'shift +' : ''} to navigate the sidebar.
    + Hit space / shift space${app.settings.get('arrowScroll') ? ' or ↓/↑' : ', alt ↓/↑ or shift ↓/↑'} to scroll the page. +

    + See all keyboard shortcuts\ +`; diff --git a/assets/javascripts/views/content/content.js b/assets/javascripts/views/content/content.js index 4e01733ebd..7f50299576 100644 --- a/assets/javascripts/views/content/content.js +++ b/assets/javascripts/views/content/content.js @@ -1,195 +1,259 @@ -class app.views.Content extends app.View - @el: '._content' - @loadingClass: '_content-loading' - - @events: - click: 'onClick' - - @shortcuts: - altUp: 'scrollStepUp' - altDown: 'scrollStepDown' - pageUp: 'scrollPageUp' - pageDown: 'scrollPageDown' - pageTop: 'scrollToTop' - pageBottom: 'scrollToBottom' - altF: 'onAltF' - - @routes: - before: 'beforeRoute' - after: 'afterRoute' - - init: -> - @scrollEl = if app.isMobile() +/* + * decaffeinate suggestions: + * DS002: Fix invalid constructor + * DS102: Remove unnecessary code created because of implicit returns + * DS103: Rewrite code to no longer use __guard__, or convert again using --optional-chaining + * DS104: Avoid inline assignments + * DS204: Change includes calls to have a more natural evaluation order + * DS205: Consider reworking code to avoid use of IIFEs + * DS206: Consider reworking classes to avoid initClass + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +const Cls = (app.views.Content = class Content extends app.View { + constructor(...args) { + this.scrollToTop = this.scrollToTop.bind(this); + this.scrollToBottom = this.scrollToBottom.bind(this); + this.scrollStepUp = this.scrollStepUp.bind(this); + this.scrollStepDown = this.scrollStepDown.bind(this); + this.scrollPageUp = this.scrollPageUp.bind(this); + this.scrollPageDown = this.scrollPageDown.bind(this); + this.onReady = this.onReady.bind(this); + this.onBootError = this.onBootError.bind(this); + this.onEntryLoading = this.onEntryLoading.bind(this); + this.onEntryLoaded = this.onEntryLoaded.bind(this); + this.beforeRoute = this.beforeRoute.bind(this); + this.afterRoute = this.afterRoute.bind(this); + this.onClick = this.onClick.bind(this); + this.onAltF = this.onAltF.bind(this); + super(...args); + } + + static initClass() { + this.el = '._content'; + this.loadingClass = '_content-loading'; + + this.events = + {click: 'onClick'}; + + this.shortcuts = { + altUp: 'scrollStepUp', + altDown: 'scrollStepDown', + pageUp: 'scrollPageUp', + pageDown: 'scrollPageDown', + pageTop: 'scrollToTop', + pageBottom: 'scrollToBottom', + altF: 'onAltF' + }; + + this.routes = { + before: 'beforeRoute', + after: 'afterRoute' + }; + } + + init() { + this.scrollEl = app.isMobile() ? (document.scrollingElement || document.body) - else - @el - @scrollMap = {} - @scrollStack = [] - - @rootPage = new app.views.RootPage - @staticPage = new app.views.StaticPage - @settingsPage = new app.views.SettingsPage - @offlinePage = new app.views.OfflinePage - @typePage = new app.views.TypePage - @entryPage = new app.views.EntryPage - - @entryPage - .on 'loading', @onEntryLoading - .on 'loaded', @onEntryLoaded + : + this.el; + this.scrollMap = {}; + this.scrollStack = []; + + this.rootPage = new app.views.RootPage; + this.staticPage = new app.views.StaticPage; + this.settingsPage = new app.views.SettingsPage; + this.offlinePage = new app.views.OfflinePage; + this.typePage = new app.views.TypePage; + this.entryPage = new app.views.EntryPage; + + this.entryPage + .on('loading', this.onEntryLoading) + .on('loaded', this.onEntryLoaded); app - .on 'ready', @onReady - .on 'bootError', @onBootError - - return - - show: (view) -> - @hideLoading() - unless view is @view - @view?.deactivate() - @html @view = view - @view.activate() - return - - showLoading: -> - @addClass @constructor.loadingClass - return - - isLoading: -> - @el.classList.contains @constructor.loadingClass - - hideLoading: -> - @removeClass @constructor.loadingClass - return - - scrollTo: (value) -> - @scrollEl.scrollTop = value or 0 - return - - smoothScrollTo: (value) -> - if app.settings.get('fastScroll') - @scrollTo value - else - $.smoothScroll @scrollEl, value or 0 - return - - scrollBy: (n) -> - @smoothScrollTo @scrollEl.scrollTop + n - return - - scrollToTop: => - @smoothScrollTo 0 - return - - scrollToBottom: => - @smoothScrollTo @scrollEl.scrollHeight - return - - scrollStepUp: => - @scrollBy -80 - return - - scrollStepDown: => - @scrollBy 80 - return - - scrollPageUp: => - @scrollBy 40 - @scrollEl.clientHeight - return - - scrollPageDown: => - @scrollBy @scrollEl.clientHeight - 40 - return - - scrollToTarget: -> - if @routeCtx.hash and el = @findTargetByHash @routeCtx.hash - $.scrollToWithImageLock el, @scrollEl, 'top', - margin: if @scrollEl is @el then 0 else $.offset(@el).top - $.highlight el, className: '_highlight' - else - @scrollTo @scrollMap[@routeCtx.state.id] - return - - onReady: => - @hideLoading() - return - - onBootError: => - @hideLoading() - @html @tmpl('bootError') - return - - onEntryLoading: => - @showLoading() - if @scrollToTargetTimeout - clearTimeout @scrollToTargetTimeout - @scrollToTargetTimeout = null - return - - onEntryLoaded: => - @hideLoading() - if @scrollToTargetTimeout - clearTimeout @scrollToTargetTimeout - @scrollToTargetTimeout = null - @scrollToTarget() - return - - beforeRoute: (context) => - @cacheScrollPosition() - @routeCtx = context - @scrollToTargetTimeout = @delay @scrollToTarget - return - - cacheScrollPosition: -> - return if not @routeCtx or @routeCtx.hash - return if @routeCtx.path is '/' - - unless @scrollMap[@routeCtx.state.id]? - @scrollStack.push @routeCtx.state.id - while @scrollStack.length > app.config.history_cache_size - delete @scrollMap[@scrollStack.shift()] - - @scrollMap[@routeCtx.state.id] = @scrollEl.scrollTop - return - - afterRoute: (route, context) => - if route != 'entry' and route != 'type' - resetFavicon() - - switch route - when 'root' - @show @rootPage - when 'entry' - @show @entryPage - when 'type' - @show @typePage - when 'settings' - @show @settingsPage - when 'offline' - @show @offlinePage - else - @show @staticPage - - @view.onRoute(context) - app.document.setTitle @view.getTitle?() - return - - onClick: (event) => - link = $.closestLink $.eventTarget(event), @el - if link and @isExternalUrl link.getAttribute('href') - $.stopEvent(event) - $.popup(link) - return - - onAltF: (event) => - unless document.activeElement and $.hasChild @el, document.activeElement - @find('a:not(:empty)')?.focus() - $.stopEvent(event) - - findTargetByHash: (hash) -> - el = try $.id decodeURIComponent(hash) catch - el or= try $.id(hash) catch - el - - isExternalUrl: (url) -> - url?[0..5] in ['http:/', 'https:'] + .on('ready', this.onReady) + .on('bootError', this.onBootError); + + } + + show(view) { + this.hideLoading(); + if (view !== this.view) { + if (this.view != null) { + this.view.deactivate(); + } + this.html(this.view = view); + this.view.activate(); + } + } + + showLoading() { + this.addClass(this.constructor.loadingClass); + } + + isLoading() { + return this.el.classList.contains(this.constructor.loadingClass); + } + + hideLoading() { + this.removeClass(this.constructor.loadingClass); + } + + scrollTo(value) { + this.scrollEl.scrollTop = value || 0; + } + + smoothScrollTo(value) { + if (app.settings.get('fastScroll')) { + this.scrollTo(value); + } else { + $.smoothScroll(this.scrollEl, value || 0); + } + } + + scrollBy(n) { + this.smoothScrollTo(this.scrollEl.scrollTop + n); + } + + scrollToTop() { + this.smoothScrollTo(0); + } + + scrollToBottom() { + this.smoothScrollTo(this.scrollEl.scrollHeight); + } + + scrollStepUp() { + this.scrollBy(-80); + } + + scrollStepDown() { + this.scrollBy(80); + } + + scrollPageUp() { + this.scrollBy(40 - this.scrollEl.clientHeight); + } + + scrollPageDown() { + this.scrollBy(this.scrollEl.clientHeight - 40); + } + + scrollToTarget() { + let el; + if (this.routeCtx.hash && (el = this.findTargetByHash(this.routeCtx.hash))) { + $.scrollToWithImageLock(el, this.scrollEl, 'top', + {margin: this.scrollEl === this.el ? 0 : $.offset(this.el).top}); + $.highlight(el, {className: '_highlight'}); + } else { + this.scrollTo(this.scrollMap[this.routeCtx.state.id]); + } + } + + onReady() { + this.hideLoading(); + } + + onBootError() { + this.hideLoading(); + this.html(this.tmpl('bootError')); + } + + onEntryLoading() { + this.showLoading(); + if (this.scrollToTargetTimeout) { + clearTimeout(this.scrollToTargetTimeout); + this.scrollToTargetTimeout = null; + } + } + + onEntryLoaded() { + this.hideLoading(); + if (this.scrollToTargetTimeout) { + clearTimeout(this.scrollToTargetTimeout); + this.scrollToTargetTimeout = null; + } + this.scrollToTarget(); + } + + beforeRoute(context) { + this.cacheScrollPosition(); + this.routeCtx = context; + this.scrollToTargetTimeout = this.delay(this.scrollToTarget); + } + + cacheScrollPosition() { + if (!this.routeCtx || this.routeCtx.hash) { return; } + if (this.routeCtx.path === '/') { return; } + + if (this.scrollMap[this.routeCtx.state.id] == null) { + this.scrollStack.push(this.routeCtx.state.id); + while (this.scrollStack.length > app.config.history_cache_size) { + delete this.scrollMap[this.scrollStack.shift()]; + } + } + + this.scrollMap[this.routeCtx.state.id] = this.scrollEl.scrollTop; + } + + afterRoute(route, context) { + if ((route !== 'entry') && (route !== 'type')) { + resetFavicon(); + } + + switch (route) { + case 'root': + this.show(this.rootPage); + break; + case 'entry': + this.show(this.entryPage); + break; + case 'type': + this.show(this.typePage); + break; + case 'settings': + this.show(this.settingsPage); + break; + case 'offline': + this.show(this.offlinePage); + break; + default: + this.show(this.staticPage); + } + + this.view.onRoute(context); + app.document.setTitle(typeof this.view.getTitle === 'function' ? this.view.getTitle() : undefined); + } + + onClick(event) { + const link = $.closestLink($.eventTarget(event), this.el); + if (link && this.isExternalUrl(link.getAttribute('href'))) { + $.stopEvent(event); + $.popup(link); + } + } + + onAltF(event) { + if (!document.activeElement || !$.hasChild(this.el, document.activeElement)) { + __guard__(this.find('a:not(:empty)'), x => x.focus()); + return $.stopEvent(event); + } + } + + findTargetByHash(hash) { + let el = (() => { try { return $.id(decodeURIComponent(hash)); } catch (error) {} })(); + if (!el) { el = (() => { try { return $.id(hash); } catch (error1) {} })(); } + return el; + } + + isExternalUrl(url) { + let needle; + return (needle = __guard__(url, x => x.slice(0, 6)), ['http:/', 'https:'].includes(needle)); + } +}); +Cls.initClass(); + +function __guard__(value, transform) { + return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; +} \ No newline at end of file diff --git a/assets/javascripts/views/content/entry_page.js b/assets/javascripts/views/content/entry_page.js index dff008877e..bf9852d2bd 100644 --- a/assets/javascripts/views/content/entry_page.js +++ b/assets/javascripts/views/content/entry_page.js @@ -1,166 +1,225 @@ -class app.views.EntryPage extends app.View - @className: '_page' - @errorClass: '_page-error' - - @events: - click: 'onClick' - - @shortcuts: - altC: 'onAltC' - altO: 'onAltO' - - @routes: - before: 'beforeRoute' - - init: -> - @cacheMap = {} - @cacheStack = [] - return - - deactivate: -> - if super - @empty() - @entry = null - return - - loading: -> - @empty() - @trigger 'loading' - return - - render: (content = '', fromCache = false) -> - return unless @activated - @empty() - @subview = new (@subViewClass()) @el, @entry - - $.batchUpdate @el, => - @subview.render(content, fromCache) - @addCopyButtons() unless fromCache - return - - if app.disabledDocs.findBy 'slug', @entry.doc.slug - @hiddenView = new app.views.HiddenPage @el, @entry - - setFaviconForDoc(@entry.doc) - @delay @polyfillMathML - @trigger 'loaded' - return - - addCopyButtons: -> - unless @copyButton - @copyButton = document.createElement('button') - @copyButton.innerHTML = '' - @copyButton.type = 'button' - @copyButton.className = '_pre-clip' - @copyButton.title = 'Copy to clipboard' - @copyButton.setAttribute 'aria-label', 'Copy to clipboard' - el.appendChild @copyButton.cloneNode(true) for el in @findAllByTag('pre') - return - - polyfillMathML: -> - return unless window.supportsMathML is false and !@polyfilledMathML and @findByTag('math') - @polyfilledMathML = true - $.append document.head, """""" - return - - LINKS = - home: 'Homepage' - code: 'Source code' - - prepareContent: (content) -> - return content unless @entry.isIndex() and @entry.doc.links - - links = for link, url of @entry.doc.links - """#{LINKS[link]}""" - - """

    #{content}""" - - empty: -> - @subview?.deactivate() - @subview = null - - @hiddenView?.deactivate() - @hiddenView = null - - @resetClass() - super - return - - subViewClass: -> - app.views["#{$.classify(@entry.doc.type)}Page"] or app.views.BasePage - - getTitle: -> - @entry.doc.fullName + if @entry.isIndex() then ' documentation' else " / #{@entry.name}" - - beforeRoute: => - @cache() - @abort() - return - - onRoute: (context) -> - isSameFile = context.entry.filePath() is @entry?.filePath() - @entry = context.entry - @restore() or @load() unless isSameFile - return - - load: -> - @loading() - @xhr = @entry.loadFile @onSuccess, @onError - return - - abort: -> - if @xhr - @xhr.abort() - @xhr = @entry = null - return - - onSuccess: (response) => - return unless @activated - @xhr = null - @render @prepareContent(response) - return - - onError: => - @xhr = null - @render @tmpl('pageLoadError') - @resetClass() - @addClass @constructor.errorClass - app.serviceWorker?.update() - return - - cache: -> - return if @xhr or not @entry or @cacheMap[path = @entry.filePath()] - - @cacheMap[path] = @el.innerHTML - @cacheStack.push(path) - - while @cacheStack.length > app.config.history_cache_size - delete @cacheMap[@cacheStack.shift()] - return - - restore: -> - if @cacheMap[path = @entry.filePath()] - @render @cacheMap[path], true - true - - onClick: (event) => - target = $.eventTarget(event) - if target.hasAttribute 'data-retry' - $.stopEvent(event) - @load() - else if target.classList.contains '_pre-clip' - $.stopEvent(event) - target.classList.add if $.copyToClipboard(target.parentNode.textContent) then '_pre-clip-success' else '_pre-clip-error' - setTimeout (-> target.className = '_pre-clip'), 2000 - return - - onAltC: => - return unless link = @find('._attribution:last-child ._attribution-link') - console.log(link.href + location.hash) - navigator.clipboard.writeText(link.href + location.hash) - return - - onAltO: => - return unless link = @find('._attribution:last-child ._attribution-link') - @delay -> $.popup(link.href + location.hash) - return +/* + * decaffeinate suggestions: + * DS002: Fix invalid constructor + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS205: Consider reworking code to avoid use of IIFEs + * DS206: Consider reworking classes to avoid initClass + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +(function() { + let LINKS = undefined; + const Cls = (app.views.EntryPage = class EntryPage extends app.View { + constructor(...args) { + this.beforeRoute = this.beforeRoute.bind(this); + this.onSuccess = this.onSuccess.bind(this); + this.onError = this.onError.bind(this); + this.onClick = this.onClick.bind(this); + this.onAltC = this.onAltC.bind(this); + this.onAltO = this.onAltO.bind(this); + super(...args); + } + + static initClass() { + this.className = '_page'; + this.errorClass = '_page-error'; + + this.events = + {click: 'onClick'}; + + this.shortcuts = { + altC: 'onAltC', + altO: 'onAltO' + }; + + this.routes = + {before: 'beforeRoute'}; + + LINKS = { + home: 'Homepage', + code: 'Source code' + }; + } + + init() { + this.cacheMap = {}; + this.cacheStack = []; + } + + deactivate() { + if (super.deactivate(...arguments)) { + this.empty(); + this.entry = null; + } + } + + loading() { + this.empty(); + this.trigger('loading'); + } + + render(content, fromCache) { + if (content == null) { content = ''; } + if (fromCache == null) { fromCache = false; } + if (!this.activated) { return; } + this.empty(); + this.subview = new (this.subViewClass())(this.el, this.entry); + + $.batchUpdate(this.el, () => { + this.subview.render(content, fromCache); + if (!fromCache) { this.addCopyButtons(); } + }); + + if (app.disabledDocs.findBy('slug', this.entry.doc.slug)) { + this.hiddenView = new app.views.HiddenPage(this.el, this.entry); + } + + setFaviconForDoc(this.entry.doc); + this.delay(this.polyfillMathML); + this.trigger('loaded'); + } + + addCopyButtons() { + if (!this.copyButton) { + this.copyButton = document.createElement('button'); + this.copyButton.innerHTML = ''; + this.copyButton.type = 'button'; + this.copyButton.className = '_pre-clip'; + this.copyButton.title = 'Copy to clipboard'; + this.copyButton.setAttribute('aria-label', 'Copy to clipboard'); + } + for (var el of Array.from(this.findAllByTag('pre'))) { el.appendChild(this.copyButton.cloneNode(true)); } + } + + polyfillMathML() { + if ((window.supportsMathML !== false) || !!this.polyfilledMathML || !this.findByTag('math')) { return; } + this.polyfilledMathML = true; + $.append(document.head, ``); + } + + prepareContent(content) { + if (!this.entry.isIndex() || !this.entry.doc.links) { return content; } + + const links = (() => { + const result = []; + for (var link in this.entry.doc.links) { + var url = this.entry.doc.links[link]; + result.push(`${LINKS[link]}`); + } + return result; + })(); + + return `${content}`; + } + + empty() { + if (this.subview != null) { + this.subview.deactivate(); + } + this.subview = null; + + if (this.hiddenView != null) { + this.hiddenView.deactivate(); + } + this.hiddenView = null; + + this.resetClass(); + super.empty(...arguments); + } + + subViewClass() { + return app.views[`${$.classify(this.entry.doc.type)}Page`] || app.views.BasePage; + } + + getTitle() { + return this.entry.doc.fullName + (this.entry.isIndex() ? ' documentation' : ` / ${this.entry.name}`); + } + + beforeRoute() { + this.cache(); + this.abort(); + } + + onRoute(context) { + const isSameFile = context.entry.filePath() === (this.entry != null ? this.entry.filePath() : undefined); + this.entry = context.entry; + if (!isSameFile) { this.restore() || this.load(); } + } + + load() { + this.loading(); + this.xhr = this.entry.loadFile(this.onSuccess, this.onError); + } + + abort() { + if (this.xhr) { + this.xhr.abort(); + this.xhr = (this.entry = null); + } + } + + onSuccess(response) { + if (!this.activated) { return; } + this.xhr = null; + this.render(this.prepareContent(response)); + } + + onError() { + this.xhr = null; + this.render(this.tmpl('pageLoadError')); + this.resetClass(); + this.addClass(this.constructor.errorClass); + if (app.serviceWorker != null) { + app.serviceWorker.update(); + } + } + + cache() { + let path; + if (this.xhr || !this.entry || this.cacheMap[(path = this.entry.filePath())]) { return; } + + this.cacheMap[path] = this.el.innerHTML; + this.cacheStack.push(path); + + while (this.cacheStack.length > app.config.history_cache_size) { + delete this.cacheMap[this.cacheStack.shift()]; + } + } + + restore() { + let path; + if (this.cacheMap[(path = this.entry.filePath())]) { + this.render(this.cacheMap[path], true); + return true; + } + } + + onClick(event) { + const target = $.eventTarget(event); + if (target.hasAttribute('data-retry')) { + $.stopEvent(event); + this.load(); + } else if (target.classList.contains('_pre-clip')) { + $.stopEvent(event); + target.classList.add($.copyToClipboard(target.parentNode.textContent) ? '_pre-clip-success' : '_pre-clip-error'); + setTimeout((() => target.className = '_pre-clip'), 2000); + } + } + + onAltC() { + let link; + if (!(link = this.find('._attribution:last-child ._attribution-link'))) { return; } + console.log(link.href + location.hash); + navigator.clipboard.writeText(link.href + location.hash); + } + + onAltO() { + let link; + if (!(link = this.find('._attribution:last-child ._attribution-link'))) { return; } + this.delay(() => $.popup(link.href + location.hash)); + } + }); + Cls.initClass(); + return Cls; +})(); diff --git a/assets/javascripts/views/content/offline_page.js b/assets/javascripts/views/content/offline_page.js index 2f0d615f47..4cac6f2089 100644 --- a/assets/javascripts/views/content/offline_page.js +++ b/assets/javascripts/views/content/offline_page.js @@ -1,92 +1,128 @@ -class app.views.OfflinePage extends app.View - @className: '_static' +/* + * decaffeinate suggestions: + * DS002: Fix invalid constructor + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS206: Consider reworking classes to avoid initClass + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +const Cls = (app.views.OfflinePage = class OfflinePage extends app.View { + constructor(...args) { + this.onClick = this.onClick.bind(this); + super(...args); + } - @events: - click: 'onClick' - change: 'onChange' + static initClass() { + this.className = '_static'; + + this.events = { + click: 'onClick', + change: 'onChange' + }; + } - deactivate: -> - if super - @empty() - return + deactivate() { + if (super.deactivate(...arguments)) { + this.empty(); + } + } - render: -> - if app.cookieBlocked - @html @tmpl('offlineError', 'cookie_blocked') - return + render() { + if (app.cookieBlocked) { + this.html(this.tmpl('offlineError', 'cookie_blocked')); + return; + } - app.docs.getInstallStatuses (statuses) => - return unless @activated - if statuses is false - @html @tmpl('offlineError', app.db.reason, app.db.error) - else - html = '' - html += @renderDoc(doc, statuses[doc.slug]) for doc in app.docs.all() - @html @tmpl('offlinePage', html) - @refreshLinks() - return - return + app.docs.getInstallStatuses(statuses => { + if (!this.activated) { return; } + if (statuses === false) { + this.html(this.tmpl('offlineError', app.db.reason, app.db.error)); + } else { + let html = ''; + for (var doc of Array.from(app.docs.all())) { html += this.renderDoc(doc, statuses[doc.slug]); } + this.html(this.tmpl('offlinePage', html)); + this.refreshLinks(); + } + }); + } - renderDoc: (doc, status) -> - app.templates.render('offlineDoc', doc, status) + renderDoc(doc, status) { + return app.templates.render('offlineDoc', doc, status); + } - getTitle: -> - 'Offline' + getTitle() { + return 'Offline'; + } - refreshLinks: -> - for action in ['install', 'update', 'uninstall'] - @find("[data-action-all='#{action}']").classList[if @find("[data-action='#{action}']") then 'add' else 'remove']('_show') - return + refreshLinks() { + for (var action of ['install', 'update', 'uninstall']) { + this.find(`[data-action-all='${action}']`).classList[this.find(`[data-action='${action}']`) ? 'add' : 'remove']('_show'); + } + } - docByEl: (el) -> - el = el.parentNode until slug = el.getAttribute('data-slug') - app.docs.findBy('slug', slug) + docByEl(el) { + let slug; + while (!(slug = el.getAttribute('data-slug'))) { el = el.parentNode; } + return app.docs.findBy('slug', slug); + } - docEl: (doc) -> - @find("[data-slug='#{doc.slug}']") + docEl(doc) { + return this.find(`[data-slug='${doc.slug}']`); + } - onRoute: (context) -> - @render() - return + onRoute(context) { + this.render(); + } - onClick: (event) => - el = $.eventTarget(event) - if action = el.getAttribute('data-action') - doc = @docByEl(el) - action = 'install' if action is 'update' - doc[action](@onInstallSuccess.bind(@, doc), @onInstallError.bind(@, doc), @onInstallProgress.bind(@, doc)) - el.parentNode.innerHTML = "#{el.textContent.replace(/e$/, '')}ing…" - else if action = el.getAttribute('data-action-all') || el.parentElement.getAttribute('data-action-all') - return unless action isnt 'uninstall' or window.confirm('Uninstall all docs?') - app.db.migrate() - $.click(el) for el in @findAll("[data-action='#{action}']") - return + onClick(event) { + let action; + let el = $.eventTarget(event); + if (action = el.getAttribute('data-action')) { + const doc = this.docByEl(el); + if (action === 'update') { action = 'install'; } + doc[action](this.onInstallSuccess.bind(this, doc), this.onInstallError.bind(this, doc), this.onInstallProgress.bind(this, doc)); + el.parentNode.innerHTML = `${el.textContent.replace(/e$/, '')}ing…`; + } else if (action = el.getAttribute('data-action-all') || el.parentElement.getAttribute('data-action-all')) { + if ((action === 'uninstall') && !window.confirm('Uninstall all docs?')) { return; } + app.db.migrate(); + for (el of Array.from(this.findAll(`[data-action='${action}']`))) { $.click(el); } + } + } - onInstallSuccess: (doc) -> - return unless @activated - doc.getInstallStatus (status) => - return unless @activated - if el = @docEl(doc) - el.outerHTML = @renderDoc(doc, status) - $.highlight el, className: '_highlight' - @refreshLinks() - return - return + onInstallSuccess(doc) { + if (!this.activated) { return; } + doc.getInstallStatus(status => { + let el; + if (!this.activated) { return; } + if (el = this.docEl(doc)) { + el.outerHTML = this.renderDoc(doc, status); + $.highlight(el, {className: '_highlight'}); + this.refreshLinks(); + } + }); + } - onInstallError: (doc) -> - return unless @activated - if el = @docEl(doc) - el.lastElementChild.textContent = 'Error' - return + onInstallError(doc) { + let el; + if (!this.activated) { return; } + if (el = this.docEl(doc)) { + el.lastElementChild.textContent = 'Error'; + } + } - onInstallProgress: (doc, event) -> - return unless @activated and event.lengthComputable - if el = @docEl(doc) - percentage = Math.round event.loaded * 100 / event.total - el.lastElementChild.textContent = el.lastElementChild.textContent.replace(/(\s.+)?$/, " (#{percentage}%)") - return + onInstallProgress(doc, event) { + let el; + if (!this.activated || !event.lengthComputable) { return; } + if (el = this.docEl(doc)) { + const percentage = Math.round((event.loaded * 100) / event.total); + el.lastElementChild.textContent = el.lastElementChild.textContent.replace(/(\s.+)?$/, ` (${percentage}%)`); + } + } - onChange: (event) -> - if event.target.name is 'autoUpdate' - app.settings.set 'manualUpdate', !event.target.checked - return + onChange(event) { + if (event.target.name === 'autoUpdate') { + app.settings.set('manualUpdate', !event.target.checked); + } + } +}); +Cls.initClass(); diff --git a/assets/javascripts/views/content/root_page.js b/assets/javascripts/views/content/root_page.js index b48a1df3ba..3693412340 100644 --- a/assets/javascripts/views/content/root_page.js +++ b/assets/javascripts/views/content/root_page.js @@ -1,43 +1,61 @@ -class app.views.RootPage extends app.View - @events: - click: 'onClick' - - init: -> - @setHidden false unless @isHidden() # reserve space in local storage - @render() - return - - render: -> - @empty() - - tmpl = if app.isAndroidWebview() +/* + * decaffeinate suggestions: + * DS002: Fix invalid constructor + * DS102: Remove unnecessary code created because of implicit returns + * DS206: Consider reworking classes to avoid initClass + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +const Cls = (app.views.RootPage = class RootPage extends app.View { + constructor(...args) { + this.onClick = this.onClick.bind(this); + super(...args); + } + + static initClass() { + this.events = + {click: 'onClick'}; + } + + init() { + if (!this.isHidden()) { this.setHidden(false); } // reserve space in local storage + this.render(); + } + + render() { + this.empty(); + + const tmpl = app.isAndroidWebview() ? 'androidWarning' - else if @isHidden() + : this.isHidden() ? 'splash' - else if app.isMobile() + : app.isMobile() ? 'mobileIntro' - else - 'intro' - - @append @tmpl(tmpl) - return - - hideIntro: -> - @setHidden true - @render() - return - - setHidden: (value) -> - app.settings.set 'hideIntro', value - return - - isHidden: -> - app.isSingleDoc() or app.settings.get 'hideIntro' - - onRoute: -> - - onClick: (event) => - if $.eventTarget(event).hasAttribute 'data-hide-intro' - $.stopEvent(event) - @hideIntro() - return + : + 'intro'; + + this.append(this.tmpl(tmpl)); + } + + hideIntro() { + this.setHidden(true); + this.render(); + } + + setHidden(value) { + app.settings.set('hideIntro', value); + } + + isHidden() { + return app.isSingleDoc() || app.settings.get('hideIntro'); + } + + onRoute() {} + + onClick(event) { + if ($.eventTarget(event).hasAttribute('data-hide-intro')) { + $.stopEvent(event); + this.hideIntro(); + } + } +}); +Cls.initClass(); diff --git a/assets/javascripts/views/content/settings_page.js b/assets/javascripts/views/content/settings_page.js index 7a7b624696..5174368bd6 100644 --- a/assets/javascripts/views/content/settings_page.js +++ b/assets/javascripts/views/content/settings_page.js @@ -1,116 +1,151 @@ -class app.views.SettingsPage extends app.View - @className: '_static' - - @events: - click: 'onClick' - change: 'onChange' - - render: -> - @html @tmpl('settingsPage', @currentSettings()) - return - - currentSettings: -> - settings = {} - settings.theme = app.settings.get('theme') - settings.smoothScroll = !app.settings.get('fastScroll') - settings.arrowScroll = app.settings.get('arrowScroll') - settings.noAutofocus = app.settings.get('noAutofocus') - settings.autoInstall = app.settings.get('autoInstall') - settings.analyticsConsent = app.settings.get('analyticsConsent') - settings.spaceScroll = app.settings.get('spaceScroll') - settings.spaceTimeout = app.settings.get('spaceTimeout') - settings.autoSupported = app.settings.autoSupported - settings[layout] = app.settings.hasLayout(layout) for layout in app.settings.LAYOUTS - settings - - getTitle: -> - 'Preferences' - - setTheme: (value) -> - app.settings.set('theme', value) - return - - toggleLayout: (layout, enable) -> - app.settings.setLayout(layout, enable) - return - - toggleSmoothScroll: (enable) -> - app.settings.set('fastScroll', !enable) - return - - toggleAnalyticsConsent: (enable) -> - app.settings.set('analyticsConsent', if enable then '1' else '0') - resetAnalytics() unless enable - return - - toggleSpaceScroll: (enable) -> - app.settings.set('spaceScroll', if enable then 1 else 0) - return - - setScrollTimeout: (value) -> - app.settings.set('spaceTimeout', value) - - toggle: (name, enable) -> - app.settings.set(name, enable) - return - - export: -> - data = new Blob([JSON.stringify(app.settings.export())], type: 'application/json') - link = document.createElement('a') - link.href = URL.createObjectURL(data) - link.download = 'devdocs.json' - link.style.display = 'none' - document.body.appendChild(link) - link.click() - document.body.removeChild(link) - return - - import: (file, input) -> - unless file and file.type is 'application/json' - new app.views.Notif 'ImportInvalid', autoHide: false - return - - reader = new FileReader() - reader.onloadend = -> - data = try JSON.parse(reader.result) - unless data and data.constructor is Object - new app.views.Notif 'ImportInvalid', autoHide: false - return - app.settings.import(data) - $.trigger input.form, 'import' - return - reader.readAsText(file) - return - - onChange: (event) => - input = event.target - switch input.name - when 'theme' - @setTheme input.value - when 'layout' - @toggleLayout input.value, input.checked - when 'smoothScroll' - @toggleSmoothScroll input.checked - when 'import' - @import input.files[0], input - when 'analyticsConsent' - @toggleAnalyticsConsent input.checked - when 'spaceScroll' - @toggleSpaceScroll input.checked - when 'spaceTimeout' - @setScrollTimeout input.value - else - @toggle input.name, input.checked - return - - onClick: (event) => - target = $.eventTarget(event) - switch target.getAttribute('data-action') - when 'export' - $.stopEvent(event) - @export() - return - - onRoute: (context) -> - @render() - return +/* + * decaffeinate suggestions: + * DS002: Fix invalid constructor + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS205: Consider reworking code to avoid use of IIFEs + * DS206: Consider reworking classes to avoid initClass + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +const Cls = (app.views.SettingsPage = class SettingsPage extends app.View { + constructor(...args) { + this.onChange = this.onChange.bind(this); + this.onClick = this.onClick.bind(this); + super(...args); + } + + static initClass() { + this.className = '_static'; + + this.events = { + click: 'onClick', + change: 'onChange' + }; + } + + render() { + this.html(this.tmpl('settingsPage', this.currentSettings())); + } + + currentSettings() { + const settings = {}; + settings.theme = app.settings.get('theme'); + settings.smoothScroll = !app.settings.get('fastScroll'); + settings.arrowScroll = app.settings.get('arrowScroll'); + settings.noAutofocus = app.settings.get('noAutofocus'); + settings.autoInstall = app.settings.get('autoInstall'); + settings.analyticsConsent = app.settings.get('analyticsConsent'); + settings.spaceScroll = app.settings.get('spaceScroll'); + settings.spaceTimeout = app.settings.get('spaceTimeout'); + settings.autoSupported = app.settings.autoSupported; + for (var layout of Array.from(app.settings.LAYOUTS)) { settings[layout] = app.settings.hasLayout(layout); } + return settings; + } + + getTitle() { + return 'Preferences'; + } + + setTheme(value) { + app.settings.set('theme', value); + } + + toggleLayout(layout, enable) { + app.settings.setLayout(layout, enable); + } + + toggleSmoothScroll(enable) { + app.settings.set('fastScroll', !enable); + } + + toggleAnalyticsConsent(enable) { + app.settings.set('analyticsConsent', enable ? '1' : '0'); + if (!enable) { resetAnalytics(); } + } + + toggleSpaceScroll(enable) { + app.settings.set('spaceScroll', enable ? 1 : 0); + } + + setScrollTimeout(value) { + return app.settings.set('spaceTimeout', value); + } + + toggle(name, enable) { + app.settings.set(name, enable); + } + + export() { + const data = new Blob([JSON.stringify(app.settings.export())], {type: 'application/json'}); + const link = document.createElement('a'); + link.href = URL.createObjectURL(data); + link.download = 'devdocs.json'; + link.style.display = 'none'; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + } + + import(file, input) { + if (!file || (file.type !== 'application/json')) { + new app.views.Notif('ImportInvalid', {autoHide: false}); + return; + } + + const reader = new FileReader(); + reader.onloadend = function() { + const data = (() => { try { return JSON.parse(reader.result); } catch (error) {} })(); + if (!data || (data.constructor !== Object)) { + new app.views.Notif('ImportInvalid', {autoHide: false}); + return; + } + app.settings.import(data); + $.trigger(input.form, 'import'); + }; + reader.readAsText(file); + } + + onChange(event) { + const input = event.target; + switch (input.name) { + case 'theme': + this.setTheme(input.value); + break; + case 'layout': + this.toggleLayout(input.value, input.checked); + break; + case 'smoothScroll': + this.toggleSmoothScroll(input.checked); + break; + case 'import': + this.import(input.files[0], input); + break; + case 'analyticsConsent': + this.toggleAnalyticsConsent(input.checked); + break; + case 'spaceScroll': + this.toggleSpaceScroll(input.checked); + break; + case 'spaceTimeout': + this.setScrollTimeout(input.value); + break; + default: + this.toggle(input.name, input.checked); + } + } + + onClick(event) { + const target = $.eventTarget(event); + switch (target.getAttribute('data-action')) { + case 'export': + $.stopEvent(event); + this.export(); + break; + } + } + + onRoute(context) { + this.render(); + } +}); +Cls.initClass(); diff --git a/assets/javascripts/views/content/static_page.js b/assets/javascripts/views/content/static_page.js index d7bee725b6..a998dbf758 100644 --- a/assets/javascripts/views/content/static_page.js +++ b/assets/javascripts/views/content/static_page.js @@ -1,26 +1,39 @@ -class app.views.StaticPage extends app.View - @className: '_static' +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS206: Consider reworking classes to avoid initClass + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +const Cls = (app.views.StaticPage = class StaticPage extends app.View { + static initClass() { + this.className = '_static'; + + this.titles = { + about: 'About', + news: 'News', + help: 'User Guide', + notFound: '404' + }; + } - @titles: - about: 'About' - news: 'News' - help: 'User Guide' - notFound: '404' + deactivate() { + if (super.deactivate(...arguments)) { + this.empty(); + this.page = null; + } + } - deactivate: -> - if super - @empty() - @page = null - return + render(page) { + this.page = page; + this.html(this.tmpl(`${this.page}Page`)); + } - render: (page) -> - @page = page - @html @tmpl("#{@page}Page") - return + getTitle() { + return this.constructor.titles[this.page]; + } - getTitle: -> - @constructor.titles[@page] - - onRoute: (context) -> - @render context.page or 'notFound' - return + onRoute(context) { + this.render(context.page || 'notFound'); + } +}); +Cls.initClass(); diff --git a/assets/javascripts/views/content/type_page.js b/assets/javascripts/views/content/type_page.js index ef360c14c4..f54ee25a53 100644 --- a/assets/javascripts/views/content/type_page.js +++ b/assets/javascripts/views/content/type_page.js @@ -1,20 +1,33 @@ -class app.views.TypePage extends app.View - @className: '_page' +/* + * decaffeinate suggestions: + * DS102: Remove unnecessary code created because of implicit returns + * DS206: Consider reworking classes to avoid initClass + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +const Cls = (app.views.TypePage = class TypePage extends app.View { + static initClass() { + this.className = '_page'; + } - deactivate: -> - if super - @empty() - @type = null - return + deactivate() { + if (super.deactivate(...arguments)) { + this.empty(); + this.type = null; + } + } - render: (@type) -> - @html @tmpl('typePage', @type) - setFaviconForDoc(@type.doc) - return + render(type) { + this.type = type; + this.html(this.tmpl('typePage', this.type)); + setFaviconForDoc(this.type.doc); + } - getTitle: -> - "#{@type.doc.fullName} / #{@type.name}" + getTitle() { + return `${this.type.doc.fullName} / ${this.type.name}`; + } - onRoute: (context) -> - @render context.type - return + onRoute(context) { + this.render(context.type); + } +}); +Cls.initClass(); diff --git a/assets/javascripts/views/layout/document.js b/assets/javascripts/views/layout/document.js index a10d0b3ce2..6b0dd57d42 100644 --- a/assets/javascripts/views/layout/document.js +++ b/assets/javascripts/views/layout/document.js @@ -1,85 +1,111 @@ -class app.views.Document extends app.View - @el: document - - @events: - visibilitychange: 'onVisibilityChange' - - @shortcuts: - help: 'onHelp' - preferences: 'onPreferences' - escape: 'onEscape' - superLeft: 'onBack' - superRight: 'onForward' - - @routes: - after: 'afterRoute' - - init: -> - @addSubview @menu = new app.views.Menu, - @addSubview @sidebar = new app.views.Sidebar - @addSubview @resizer = new app.views.Resizer if app.views.Resizer.isSupported() - @addSubview @content = new app.views.Content - @addSubview @path = new app.views.Path unless app.isSingleDoc() or app.isMobile() - @settings = new app.views.Settings unless app.isSingleDoc() - - $.on document.body, 'click', @onClick - - @activate() - return - - setTitle: (title) -> - @el.title = if title then "#{title} — DevDocs" else 'DevDocs API Documentation' - - afterRoute: (route) => - if route is 'settings' - @settings?.activate() - else - @settings?.deactivate() - return - - onVisibilityChange: => - return unless @el.visibilityState is 'visible' - @delay -> - location.reload() if app.isMobile() isnt app.views.Mobile.detect() - return - , 300 - return - - onHelp: -> - app.router.show '/help#shortcuts' - return - - onPreferences: -> - app.router.show '/settings' - return - - onEscape: -> - path = if !app.isSingleDoc() or location.pathname is app.doc.fullPath() +/* + * decaffeinate suggestions: + * DS002: Fix invalid constructor + * DS102: Remove unnecessary code created because of implicit returns + * DS206: Consider reworking classes to avoid initClass + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +const Cls = (app.views.Document = class Document extends app.View { + constructor(...args) { + this.afterRoute = this.afterRoute.bind(this); + this.onVisibilityChange = this.onVisibilityChange.bind(this); + super(...args); + } + + static initClass() { + this.el = document; + + this.events = + {visibilitychange: 'onVisibilityChange'}; + + this.shortcuts = { + help: 'onHelp', + preferences: 'onPreferences', + escape: 'onEscape', + superLeft: 'onBack', + superRight: 'onForward' + }; + + this.routes = + {after: 'afterRoute'}; + } + + init() { + this.addSubview((this.menu = new app.views.Menu), + this.addSubview(this.sidebar = new app.views.Sidebar)); + if (app.views.Resizer.isSupported()) { this.addSubview(this.resizer = new app.views.Resizer); } + this.addSubview(this.content = new app.views.Content); + if (!app.isSingleDoc() && !app.isMobile()) { this.addSubview(this.path = new app.views.Path); } + if (!app.isSingleDoc()) { this.settings = new app.views.Settings; } + + $.on(document.body, 'click', this.onClick); + + this.activate(); + } + + setTitle(title) { + return this.el.title = title ? `${title} — DevDocs` : 'DevDocs API Documentation'; + } + + afterRoute(route) { + if (route === 'settings') { + if (this.settings != null) { + this.settings.activate(); + } + } else { + if (this.settings != null) { + this.settings.deactivate(); + } + } + } + + onVisibilityChange() { + if (this.el.visibilityState !== 'visible') { return; } + this.delay(function() { + if (app.isMobile() !== app.views.Mobile.detect()) { location.reload(); } + } + , 300); + } + + onHelp() { + app.router.show('/help#shortcuts'); + } + + onPreferences() { + app.router.show('/settings'); + } + + onEscape() { + const path = !app.isSingleDoc() || (location.pathname === app.doc.fullPath()) ? '/' - else - app.doc.fullPath() - - app.router.show(path) - return - - onBack: -> - history.back() - return - - onForward: -> - history.forward() - return - - onClick: (event) -> - target = $.eventTarget(event) - return unless target.hasAttribute('data-behavior') - $.stopEvent(event) - switch target.getAttribute('data-behavior') - when 'back' then history.back() - when 'reload' then window.location.reload() - when 'reboot' then app.reboot() - when 'hard-reload' then app.reload() - when 'reset' then app.reset() if confirm('Are you sure you want to reset DevDocs?') - when 'accept-analytics' then Cookies.set('analyticsConsent', '1', expires: 1e8) && app.reboot() - when 'decline-analytics' then Cookies.set('analyticsConsent', '0', expires: 1e8) && app.reboot() - return + : + app.doc.fullPath(); + + app.router.show(path); + } + + onBack() { + history.back(); + } + + onForward() { + history.forward(); + } + + onClick(event) { + const target = $.eventTarget(event); + if (!target.hasAttribute('data-behavior')) { return; } + $.stopEvent(event); + switch (target.getAttribute('data-behavior')) { + case 'back': history.back(); break; + case 'reload': window.location.reload(); break; + case 'reboot': app.reboot(); break; + case 'hard-reload': app.reload(); break; + case 'reset': if (confirm('Are you sure you want to reset DevDocs?')) { app.reset(); } break; + case 'accept-analytics': Cookies.set('analyticsConsent', '1', {expires: 1e8}) && app.reboot(); break; + case 'decline-analytics': Cookies.set('analyticsConsent', '0', {expires: 1e8}) && app.reboot(); break; + } + } +}); +Cls.initClass(); diff --git a/assets/javascripts/views/layout/menu.js b/assets/javascripts/views/layout/menu.js index e2282176ec..4254d1151a 100644 --- a/assets/javascripts/views/layout/menu.js +++ b/assets/javascripts/views/layout/menu.js @@ -1,23 +1,39 @@ -class app.views.Menu extends app.View - @el: '._menu' - @activeClass: 'active' +/* + * decaffeinate suggestions: + * DS002: Fix invalid constructor + * DS206: Consider reworking classes to avoid initClass + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +const Cls = (app.views.Menu = class Menu extends app.View { + constructor(...args) { + this.onGlobalClick = this.onGlobalClick.bind(this); + super(...args); + } - @events: - click: 'onClick' + static initClass() { + this.el = '._menu'; + this.activeClass = 'active'; + + this.events = + {click: 'onClick'}; + } - init: -> - $.on document.body, 'click', @onGlobalClick - return + init() { + $.on(document.body, 'click', this.onGlobalClick); + } - onClick: (event) -> - target = $.eventTarget(event) - target.blur() if target.tagName is 'A' - return + onClick(event) { + const target = $.eventTarget(event); + if (target.tagName === 'A') { target.blur(); } + } - onGlobalClick: (event) => - return if event.which isnt 1 - if event.target.hasAttribute?('data-toggle-menu') - @toggleClass @constructor.activeClass - else if @hasClass @constructor.activeClass - @removeClass @constructor.activeClass - return + onGlobalClick(event) { + if (event.which !== 1) { return; } + if (typeof event.target.hasAttribute === 'function' ? event.target.hasAttribute('data-toggle-menu') : undefined) { + this.toggleClass(this.constructor.activeClass); + } else if (this.hasClass(this.constructor.activeClass)) { + this.removeClass(this.constructor.activeClass); + } + } +}); +Cls.initClass(); diff --git a/assets/javascripts/views/layout/mobile.js b/assets/javascripts/views/layout/mobile.js index 1fd5e28577..bc336f59e0 100644 --- a/assets/javascripts/views/layout/mobile.js +++ b/assets/javascripts/views/layout/mobile.js @@ -1,155 +1,195 @@ -class app.views.Mobile extends app.View - @className: '_mobile' - - @elements: - body: 'body' - content: '._container' - sidebar: '._sidebar' - docPicker: '._settings ._sidebar' - - @shortcuts: - escape: 'onEscape' - - @routes: - after: 'afterRoute' - - @detect: -> - if Cookies.get('override-mobile-detect')? - return JSON.parse Cookies.get('override-mobile-detect') - try - (window.matchMedia('(max-width: 480px)').matches) or - (window.matchMedia('(max-width: 767px)').matches) or - (window.matchMedia('(max-height: 767px) and (max-width: 1024px)').matches) or - # Need to sniff the user agent because some Android and Windows Phone devices don't take - # resolution (dpi) into account when reporting device width/height. - (navigator.userAgent.indexOf('Android') isnt -1 and navigator.userAgent.indexOf('Mobile') isnt -1) or - (navigator.userAgent.indexOf('IEMobile') isnt -1) - catch - false - - @detectAndroidWebview: -> - try - /(Android).*( Version\/.\.. ).*(Chrome)/.test(navigator.userAgent) - catch - false - - constructor: -> - @el = document.documentElement - super - - init: -> - $.on $('._search'), 'touchend', @onTapSearch - - @toggleSidebar = $('button[data-toggle-sidebar]') - @toggleSidebar.removeAttribute('hidden') - $.on @toggleSidebar, 'click', @onClickToggleSidebar - - @back = $('button[data-back]') - @back.removeAttribute('hidden') - $.on @back, 'click', @onClickBack - - @forward = $('button[data-forward]') - @forward.removeAttribute('hidden') - $.on @forward, 'click', @onClickForward - - @docPickerTab = $('button[data-tab="doc-picker"]') - @docPickerTab.removeAttribute('hidden') - $.on @docPickerTab, 'click', @onClickDocPickerTab - - @settingsTab = $('button[data-tab="settings"]') - @settingsTab.removeAttribute('hidden') - $.on @settingsTab, 'click', @onClickSettingsTab +/* + * decaffeinate suggestions: + * DS002: Fix invalid constructor + * DS102: Remove unnecessary code created because of implicit returns + * DS206: Consider reworking classes to avoid initClass + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +const Cls = (app.views.Mobile = class Mobile extends app.View { + static initClass() { + this.className = '_mobile'; + + this.elements = { + body: 'body', + content: '._container', + sidebar: '._sidebar', + docPicker: '._settings ._sidebar' + }; + + this.shortcuts = + {escape: 'onEscape'}; + + this.routes = + {after: 'afterRoute'}; + } + + static detect() { + if (Cookies.get('override-mobile-detect') != null) { + return JSON.parse(Cookies.get('override-mobile-detect')); + } + try { + return (window.matchMedia('(max-width: 480px)').matches) || + (window.matchMedia('(max-width: 767px)').matches) || + (window.matchMedia('(max-height: 767px) and (max-width: 1024px)').matches) || + // Need to sniff the user agent because some Android and Windows Phone devices don't take + // resolution (dpi) into account when reporting device width/height. + ((navigator.userAgent.indexOf('Android') !== -1) && (navigator.userAgent.indexOf('Mobile') !== -1)) || + (navigator.userAgent.indexOf('IEMobile') !== -1); + } catch (error) { + return false; + } + } + + static detectAndroidWebview() { + try { + return /(Android).*( Version\/.\.. ).*(Chrome)/.test(navigator.userAgent); + } catch (error) { + return false; + } + } + + constructor() { + this.showSidebar = this.showSidebar.bind(this); + this.hideSidebar = this.hideSidebar.bind(this); + this.onClickBack = this.onClickBack.bind(this); + this.onClickForward = this.onClickForward.bind(this); + this.onClickToggleSidebar = this.onClickToggleSidebar.bind(this); + this.onClickDocPickerTab = this.onClickDocPickerTab.bind(this); + this.onClickSettingsTab = this.onClickSettingsTab.bind(this); + this.onTapSearch = this.onTapSearch.bind(this); + this.onEscape = this.onEscape.bind(this); + this.afterRoute = this.afterRoute.bind(this); + this.el = document.documentElement; + super(...arguments); + } + + init() { + $.on($('._search'), 'touchend', this.onTapSearch); + + this.toggleSidebar = $('button[data-toggle-sidebar]'); + this.toggleSidebar.removeAttribute('hidden'); + $.on(this.toggleSidebar, 'click', this.onClickToggleSidebar); + + this.back = $('button[data-back]'); + this.back.removeAttribute('hidden'); + $.on(this.back, 'click', this.onClickBack); + + this.forward = $('button[data-forward]'); + this.forward.removeAttribute('hidden'); + $.on(this.forward, 'click', this.onClickForward); + + this.docPickerTab = $('button[data-tab="doc-picker"]'); + this.docPickerTab.removeAttribute('hidden'); + $.on(this.docPickerTab, 'click', this.onClickDocPickerTab); + + this.settingsTab = $('button[data-tab="settings"]'); + this.settingsTab.removeAttribute('hidden'); + $.on(this.settingsTab, 'click', this.onClickSettingsTab); app.document.sidebar.search - .on 'searching', @showSidebar - - @activate() - return - - showSidebar: => - if @isSidebarShown() - window.scrollTo 0, 0 - return - - @contentTop = window.scrollY - @content.style.display = 'none' - @sidebar.style.display = 'block' - - if selection = @findByClass app.views.ListSelect.activeClass - scrollContainer = if window.scrollY is @body.scrollTop then @body else document.documentElement - $.scrollTo selection, scrollContainer, 'center' - else - window.scrollTo 0, @findByClass(app.views.ListFold.activeClass) and @sidebarTop or 0 - return - - hideSidebar: => - return unless @isSidebarShown() - @sidebarTop = window.scrollY - @sidebar.style.display = 'none' - @content.style.display = 'block' - window.scrollTo 0, @contentTop or 0 - return - - isSidebarShown: -> - @sidebar.style.display isnt 'none' - - onClickBack: => - history.back() - - onClickForward: => - history.forward() - - onClickToggleSidebar: => - if @isSidebarShown() then @hideSidebar() else @showSidebar() - return - - onClickDocPickerTab: (event) => - $.stopEvent(event) - @showDocPicker() - return - - onClickSettingsTab: (event) => - $.stopEvent(event) - @showSettings() - return - - showDocPicker: -> - window.scrollTo 0, 0 - @docPickerTab.classList.add 'active' - @settingsTab.classList.remove 'active' - @docPicker.style.display = 'block' - @content.style.display = 'none' - return - - showSettings: -> - window.scrollTo 0, 0 - @docPickerTab.classList.remove 'active' - @settingsTab.classList.add 'active' - @docPicker.style.display = 'none' - @content.style.display = 'block' - return - - onTapSearch: => - window.scrollTo 0, 0 - - onEscape: => - @hideSidebar() - - afterRoute: (route) => - @hideSidebar() - - if route is 'settings' - @showDocPicker() - else - @content.style.display = 'block' - - if page.canGoBack() - @back.removeAttribute('disabled') - else - @back.setAttribute('disabled', 'disabled') - - if page.canGoForward() - @forward.removeAttribute('disabled') - else - @forward.setAttribute('disabled', 'disabled') - return + .on('searching', this.showSidebar); + + this.activate(); + } + + showSidebar() { + let selection; + if (this.isSidebarShown()) { + window.scrollTo(0, 0); + return; + } + + this.contentTop = window.scrollY; + this.content.style.display = 'none'; + this.sidebar.style.display = 'block'; + + if (selection = this.findByClass(app.views.ListSelect.activeClass)) { + const scrollContainer = window.scrollY === this.body.scrollTop ? this.body : document.documentElement; + $.scrollTo(selection, scrollContainer, 'center'); + } else { + window.scrollTo(0, (this.findByClass(app.views.ListFold.activeClass) && this.sidebarTop) || 0); + } + } + + hideSidebar() { + if (!this.isSidebarShown()) { return; } + this.sidebarTop = window.scrollY; + this.sidebar.style.display = 'none'; + this.content.style.display = 'block'; + window.scrollTo(0, this.contentTop || 0); + } + + isSidebarShown() { + return this.sidebar.style.display !== 'none'; + } + + onClickBack() { + return history.back(); + } + + onClickForward() { + return history.forward(); + } + + onClickToggleSidebar() { + if (this.isSidebarShown()) { this.hideSidebar(); } else { this.showSidebar(); } + } + + onClickDocPickerTab(event) { + $.stopEvent(event); + this.showDocPicker(); + } + + onClickSettingsTab(event) { + $.stopEvent(event); + this.showSettings(); + } + + showDocPicker() { + window.scrollTo(0, 0); + this.docPickerTab.classList.add('active'); + this.settingsTab.classList.remove('active'); + this.docPicker.style.display = 'block'; + this.content.style.display = 'none'; + } + + showSettings() { + window.scrollTo(0, 0); + this.docPickerTab.classList.remove('active'); + this.settingsTab.classList.add('active'); + this.docPicker.style.display = 'none'; + this.content.style.display = 'block'; + } + + onTapSearch() { + return window.scrollTo(0, 0); + } + + onEscape() { + return this.hideSidebar(); + } + + afterRoute(route) { + this.hideSidebar(); + + if (route === 'settings') { + this.showDocPicker(); + } else { + this.content.style.display = 'block'; + } + + if (page.canGoBack()) { + this.back.removeAttribute('disabled'); + } else { + this.back.setAttribute('disabled', 'disabled'); + } + + if (page.canGoForward()) { + this.forward.removeAttribute('disabled'); + } else { + this.forward.setAttribute('disabled', 'disabled'); + } + } +}); +Cls.initClass(); diff --git a/assets/javascripts/views/layout/path.js b/assets/javascripts/views/layout/path.js index fb34afda61..fa9eb92e61 100644 --- a/assets/javascripts/views/layout/path.js +++ b/assets/javascripts/views/layout/path.js @@ -1,43 +1,64 @@ -class app.views.Path extends app.View - @className: '_path' - @attributes: - role: 'complementary' +/* + * decaffeinate suggestions: + * DS002: Fix invalid constructor + * DS101: Remove unnecessary use of Array.from + * DS206: Consider reworking classes to avoid initClass + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +const Cls = (app.views.Path = class Path extends app.View { + constructor(...args) { + this.onClick = this.onClick.bind(this); + this.afterRoute = this.afterRoute.bind(this); + super(...args); + } - @events: - click: 'onClick' + static initClass() { + this.className = '_path'; + this.attributes = + {role: 'complementary'}; + + this.events = + {click: 'onClick'}; + + this.routes = + {after: 'afterRoute'}; + } - @routes: - after: 'afterRoute' + render(...args) { + this.html(this.tmpl('path', ...Array.from(args))); + this.show(); + } - render: (args...) -> - @html @tmpl 'path', args... - @show() - return + show() { + if (!this.el.parentNode) { this.prependTo(app.el); } + } - show: -> - @prependTo app.el unless @el.parentNode - return + hide() { + if (this.el.parentNode) { $.remove(this.el); } + } - hide: -> - $.remove @el if @el.parentNode - return + onClick(event) { + let link; + if (link = $.closestLink(event.target, this.el)) { this.clicked = true; } + } - onClick: (event) => - @clicked = true if link = $.closestLink event.target, @el - return + afterRoute(route, context) { + if (context.type) { + this.render(context.doc, context.type); + } else if (context.entry) { + if (context.entry.isIndex()) { + this.render(context.doc); + } else { + this.render(context.doc, context.entry.getType(), context.entry); + } + } else { + this.hide(); + } - afterRoute: (route, context) => - if context.type - @render context.doc, context.type - else if context.entry - if context.entry.isIndex() - @render context.doc - else - @render context.doc, context.entry.getType(), context.entry - else - @hide() - - if @clicked - @clicked = null - app.document.sidebar.reset() - return + if (this.clicked) { + this.clicked = null; + app.document.sidebar.reset(); + } + } +}); +Cls.initClass(); diff --git a/assets/javascripts/views/layout/resizer.js b/assets/javascripts/views/layout/resizer.js index 5584bfbe50..44f3bdb153 100644 --- a/assets/javascripts/views/layout/resizer.js +++ b/assets/javascripts/views/layout/resizer.js @@ -1,49 +1,75 @@ -class app.views.Resizer extends app.View - @className: '_resizer' +/* + * decaffeinate suggestions: + * DS002: Fix invalid constructor + * DS102: Remove unnecessary code created because of implicit returns + * DS206: Consider reworking classes to avoid initClass + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +(function() { + let MIN = undefined; + let MAX = undefined; + const Cls = (app.views.Resizer = class Resizer extends app.View { + constructor(...args) { + this.onDragStart = this.onDragStart.bind(this); + this.onDrag = this.onDrag.bind(this); + this.onDragEnd = this.onDragEnd.bind(this); + super(...args); + } - @events: - dragstart: 'onDragStart' - dragend: 'onDragEnd' + static initClass() { + this.className = '_resizer'; + + this.events = { + dragstart: 'onDragStart', + dragend: 'onDragEnd' + }; + + MIN = 260; + MAX = 600; + } - @isSupported: -> - 'ondragstart' of document.createElement('div') and !app.isMobile() + static isSupported() { + return 'ondragstart' in document.createElement('div') && !app.isMobile(); + } - init: -> - @el.setAttribute('draggable', 'true') - @appendTo $('._app') - return + init() { + this.el.setAttribute('draggable', 'true'); + this.appendTo($('._app')); + } - MIN = 260 - MAX = 600 + resize(value, save) { + value -= app.el.offsetLeft; + if (!(value > 0)) { return; } + value = Math.min(Math.max(Math.round(value), MIN), MAX); + const newSize = `${value}px`; + document.documentElement.style.setProperty('--sidebarWidth', newSize); + if (save) { app.settings.setSize(value); } + } - resize: (value, save) -> - value -= app.el.offsetLeft - return unless value > 0 - value = Math.min(Math.max(Math.round(value), MIN), MAX) - newSize = "#{value}px" - document.documentElement.style.setProperty('--sidebarWidth', newSize) - app.settings.setSize(value) if save - return + onDragStart(event) { + event.dataTransfer.effectAllowed = 'link'; + event.dataTransfer.setData('Text', ''); + $.on(window, 'dragover', this.onDrag); + } - onDragStart: (event) => - event.dataTransfer.effectAllowed = 'link' - event.dataTransfer.setData('Text', '') - $.on(window, 'dragover', @onDrag) - return + onDrag(event) { + const value = event.pageX; + if (!(value > 0)) { return; } + this.lastDragValue = value; + if (this.lastDrag && (this.lastDrag > (Date.now() - 50))) { return; } + this.lastDrag = Date.now(); + this.resize(value, false); + } - onDrag: (event) => - value = event.pageX - return unless value > 0 - @lastDragValue = value - return if @lastDrag and @lastDrag > Date.now() - 50 - @lastDrag = Date.now() - @resize(value, false) - return - - onDragEnd: (event) => - $.off(window, 'dragover', @onDrag) - value = event.pageX or (event.screenX - window.screenX) - if @lastDragValue and not (@lastDragValue - 5 < value < @lastDragValue + 5) # https://github.com/freeCodeCamp/devdocs/issues/265 - value = @lastDragValue - @resize(value, true) - return + onDragEnd(event) { + $.off(window, 'dragover', this.onDrag); + let value = event.pageX || (event.screenX - window.screenX); + if (this.lastDragValue && !(this.lastDragValue - 5 < value && value < this.lastDragValue + 5)) { // https://github.com/freeCodeCamp/devdocs/issues/265 + value = this.lastDragValue; + } + this.resize(value, true); + } + }); + Cls.initClass(); + return Cls; +})(); diff --git a/assets/javascripts/views/layout/settings.js b/assets/javascripts/views/layout/settings.js index 6941b9cd25..57d0b0611b 100644 --- a/assets/javascripts/views/layout/settings.js +++ b/assets/javascripts/views/layout/settings.js @@ -1,83 +1,127 @@ -class app.views.Settings extends app.View - SIDEBAR_HIDDEN_LAYOUT = '_sidebar-hidden' - - @el: '._settings' - - @elements: - sidebar: '._sidebar' - saveBtn: 'button[type="submit"]' - backBtn: 'button[data-back]' - - @events: - import: 'onImport' - change: 'onChange' - submit: 'onSubmit' - click: 'onClick' - - @shortcuts: - enter: 'onEnter' - - init: -> - @addSubview @docPicker = new app.views.DocPicker - return - - activate: -> - if super - @render() - document.body.classList.remove(SIDEBAR_HIDDEN_LAYOUT) - return - - deactivate: -> - if super - @resetClass() - @docPicker.detach() - document.body.classList.add(SIDEBAR_HIDDEN_LAYOUT) if app.settings.hasLayout(SIDEBAR_HIDDEN_LAYOUT) - return - - render: -> - @docPicker.appendTo @sidebar - @refreshElements() - @addClass '_in' - return - - save: (options = {}) -> - unless @saving - @saving = true - - if options.import - docs = app.settings.getDocs() - else - docs = @docPicker.getSelectedDocs() - app.settings.setDocs(docs) - - @saveBtn.textContent = 'Saving\u2026' - disabledDocs = new app.collections.Docs(doc for doc in app.docs.all() when docs.indexOf(doc.slug) is -1) - disabledDocs.uninstall -> - app.db.migrate() - app.reload() - return - - onChange: => - @addClass('_dirty') - return - - onEnter: => - @save() - return - - onSubmit: (event) => - event.preventDefault() - @save() - return - - onImport: => - @addClass('_dirty') - @save(import: true) - return - - onClick: (event) => - return if event.which isnt 1 - if event.target is @backBtn - $.stopEvent(event) - app.router.show '/' - return +/* + * decaffeinate suggestions: + * DS002: Fix invalid constructor + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS205: Consider reworking code to avoid use of IIFEs + * DS206: Consider reworking classes to avoid initClass + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +(function() { + let SIDEBAR_HIDDEN_LAYOUT = undefined; + const Cls = (app.views.Settings = class Settings extends app.View { + constructor(...args) { + this.onChange = this.onChange.bind(this); + this.onEnter = this.onEnter.bind(this); + this.onSubmit = this.onSubmit.bind(this); + this.onImport = this.onImport.bind(this); + this.onClick = this.onClick.bind(this); + super(...args); + } + + static initClass() { + SIDEBAR_HIDDEN_LAYOUT = '_sidebar-hidden'; + + this.el = '._settings'; + + this.elements = { + sidebar: '._sidebar', + saveBtn: 'button[type="submit"]', + backBtn: 'button[data-back]' + }; + + this.events = { + import: 'onImport', + change: 'onChange', + submit: 'onSubmit', + click: 'onClick' + }; + + this.shortcuts = + {enter: 'onEnter'}; + } + + init() { + this.addSubview(this.docPicker = new app.views.DocPicker); + } + + activate() { + if (super.activate(...arguments)) { + this.render(); + document.body.classList.remove(SIDEBAR_HIDDEN_LAYOUT); + } + } + + deactivate() { + if (super.deactivate(...arguments)) { + this.resetClass(); + this.docPicker.detach(); + if (app.settings.hasLayout(SIDEBAR_HIDDEN_LAYOUT)) { document.body.classList.add(SIDEBAR_HIDDEN_LAYOUT); } + } + } + + render() { + this.docPicker.appendTo(this.sidebar); + this.refreshElements(); + this.addClass('_in'); + } + + save(options) { + if (options == null) { options = {}; } + if (!this.saving) { + let docs; + this.saving = true; + + if (options.import) { + docs = app.settings.getDocs(); + } else { + docs = this.docPicker.getSelectedDocs(); + app.settings.setDocs(docs); + } + + this.saveBtn.textContent = 'Saving\u2026'; + const disabledDocs = new app.collections.Docs((() => { + const result = []; + for (var doc of Array.from(app.docs.all())) { if (docs.indexOf(doc.slug) === -1) { + result.push(doc); + } + } + return result; + })()); + disabledDocs.uninstall(function() { + app.db.migrate(); + return app.reload(); + }); + } + } + + onChange() { + this.addClass('_dirty'); + } + + onEnter() { + this.save(); + } + + onSubmit(event) { + event.preventDefault(); + this.save(); + } + + onImport() { + this.addClass('_dirty'); + this.save({import: true}); + } + + onClick(event) { + if (event.which !== 1) { return; } + if (event.target === this.backBtn) { + $.stopEvent(event); + app.router.show('/'); + } + } + }); + Cls.initClass(); + return Cls; +})(); diff --git a/assets/javascripts/views/list/list_focus.js b/assets/javascripts/views/list/list_focus.js index 808810196d..72e251fb3b 100644 --- a/assets/javascripts/views/list/list_focus.js +++ b/assets/javascripts/views/list/list_focus.js @@ -1,124 +1,177 @@ -class app.views.ListFocus extends app.View - @activeClass: 'focus' - - @events: - click: 'onClick' - - @shortcuts: - up: 'onUp' - down: 'onDown' - left: 'onLeft' - enter: 'onEnter' - superEnter: 'onSuperEnter' - escape: 'blur' - - constructor: (@el) -> - super - @focusOnNextFrame = $.framify(@focus, @) - - focus: (el, options = {}) -> - if el and not el.classList.contains @constructor.activeClass - @blur() - el.classList.add @constructor.activeClass - $.trigger el, 'focus' unless options.silent is true - return - - blur: => - if cursor = @getCursor() - cursor.classList.remove @constructor.activeClass - $.trigger cursor, 'blur' - return - - getCursor: -> - @findByClass(@constructor.activeClass) or @findByClass(app.views.ListSelect.activeClass) - - findNext: (cursor) -> - if next = cursor.nextSibling - if next.tagName is 'A' - next - else if next.tagName is 'SPAN' # pagination link - $.click(next) - @findNext cursor - else if next.tagName is 'DIV' # sub-list - if cursor.className.indexOf(' open') >= 0 - @findFirst(next) or @findNext(next) - else - @findNext(next) - else if next.tagName is 'H6' # title - @findNext(next) - else if cursor.parentNode isnt @el - @findNext cursor.parentNode - - findFirst: (cursor) -> - return unless first = cursor.firstChild - - if first.tagName is 'A' - first - else if first.tagName is 'SPAN' # pagination link - $.click(first) - @findFirst cursor - - findPrev: (cursor) -> - if prev = cursor.previousSibling - if prev.tagName is 'A' - prev - else if prev.tagName is 'SPAN' # pagination link - $.click(prev) - @findPrev cursor - else if prev.tagName is 'DIV' # sub-list - if prev.previousSibling.className.indexOf('open') >= 0 - @findLast(prev) or @findPrev(prev) - else - @findPrev(prev) - else if prev.tagName is 'H6' # title - @findPrev(prev) - else if cursor.parentNode isnt @el - @findPrev cursor.parentNode - - findLast: (cursor) -> - return unless last = cursor.lastChild - - if last.tagName is 'A' - last - else if last.tagName is 'SPAN' or last.tagName is 'H6' # pagination link or title - @findPrev last - else if last.tagName is 'DIV' # sub-list - @findLast last - - onDown: => - if cursor = @getCursor() - @focusOnNextFrame @findNext(cursor) - else - @focusOnNextFrame @findByTag('a') - return - - onUp: => - if cursor = @getCursor() - @focusOnNextFrame @findPrev(cursor) - else - @focusOnNextFrame @findLastByTag('a') - return - - onLeft: => - cursor = @getCursor() - if cursor and not cursor.classList.contains(app.views.ListFold.activeClass) and cursor.parentNode isnt @el - prev = cursor.parentNode.previousSibling - @focusOnNextFrame cursor.parentNode.previousSibling if prev and prev.classList.contains(app.views.ListFold.targetClass) - return - - onEnter: => - if cursor = @getCursor() - $.click(cursor) - return - - onSuperEnter: => - if cursor = @getCursor() - $.popup(cursor) - return - - onClick: (event) => - return if event.which isnt 1 or event.metaKey or event.ctrlKey - target = $.eventTarget(event) - if target.tagName is 'A' - @focus target, silent: true - return +/* + * decaffeinate suggestions: + * DS002: Fix invalid constructor + * DS102: Remove unnecessary code created because of implicit returns + * DS206: Consider reworking classes to avoid initClass + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +const Cls = (app.views.ListFocus = class ListFocus extends app.View { + static initClass() { + this.activeClass = 'focus'; + + this.events = + {click: 'onClick'}; + + this.shortcuts = { + up: 'onUp', + down: 'onDown', + left: 'onLeft', + enter: 'onEnter', + superEnter: 'onSuperEnter', + escape: 'blur' + }; + } + + constructor(el) { + this.blur = this.blur.bind(this); + this.onDown = this.onDown.bind(this); + this.onUp = this.onUp.bind(this); + this.onLeft = this.onLeft.bind(this); + this.onEnter = this.onEnter.bind(this); + this.onSuperEnter = this.onSuperEnter.bind(this); + this.onClick = this.onClick.bind(this); + this.el = el; + super(...arguments); + this.focusOnNextFrame = $.framify(this.focus, this); + } + + focus(el, options) { + if (options == null) { options = {}; } + if (el && !el.classList.contains(this.constructor.activeClass)) { + this.blur(); + el.classList.add(this.constructor.activeClass); + if (options.silent !== true) { $.trigger(el, 'focus'); } + } + } + + blur() { + let cursor; + if (cursor = this.getCursor()) { + cursor.classList.remove(this.constructor.activeClass); + $.trigger(cursor, 'blur'); + } + } + + getCursor() { + return this.findByClass(this.constructor.activeClass) || this.findByClass(app.views.ListSelect.activeClass); + } + + findNext(cursor) { + let next; + if (next = cursor.nextSibling) { + if (next.tagName === 'A') { + return next; + } else if (next.tagName === 'SPAN') { // pagination link + $.click(next); + return this.findNext(cursor); + } else if (next.tagName === 'DIV') { // sub-list + if (cursor.className.indexOf(' open') >= 0) { + return this.findFirst(next) || this.findNext(next); + } else { + return this.findNext(next); + } + } else if (next.tagName === 'H6') { // title + return this.findNext(next); + } + } else if (cursor.parentNode !== this.el) { + return this.findNext(cursor.parentNode); + } + } + + findFirst(cursor) { + let first; + if (!(first = cursor.firstChild)) { return; } + + if (first.tagName === 'A') { + return first; + } else if (first.tagName === 'SPAN') { // pagination link + $.click(first); + return this.findFirst(cursor); + } + } + + findPrev(cursor) { + let prev; + if (prev = cursor.previousSibling) { + if (prev.tagName === 'A') { + return prev; + } else if (prev.tagName === 'SPAN') { // pagination link + $.click(prev); + return this.findPrev(cursor); + } else if (prev.tagName === 'DIV') { // sub-list + if (prev.previousSibling.className.indexOf('open') >= 0) { + return this.findLast(prev) || this.findPrev(prev); + } else { + return this.findPrev(prev); + } + } else if (prev.tagName === 'H6') { // title + return this.findPrev(prev); + } + } else if (cursor.parentNode !== this.el) { + return this.findPrev(cursor.parentNode); + } + } + + findLast(cursor) { + let last; + if (!(last = cursor.lastChild)) { return; } + + if (last.tagName === 'A') { + return last; + } else if ((last.tagName === 'SPAN') || (last.tagName === 'H6')) { // pagination link or title + return this.findPrev(last); + } else if (last.tagName === 'DIV') { // sub-list + return this.findLast(last); + } + } + + onDown() { + let cursor; + if ((cursor = this.getCursor())) { + this.focusOnNextFrame(this.findNext(cursor)); + } else { + this.focusOnNextFrame(this.findByTag('a')); + } + } + + onUp() { + let cursor; + if ((cursor = this.getCursor())) { + this.focusOnNextFrame(this.findPrev(cursor)); + } else { + this.focusOnNextFrame(this.findLastByTag('a')); + } + } + + onLeft() { + const cursor = this.getCursor(); + if (cursor && !cursor.classList.contains(app.views.ListFold.activeClass) && (cursor.parentNode !== this.el)) { + const prev = cursor.parentNode.previousSibling; + if (prev && prev.classList.contains(app.views.ListFold.targetClass)) { this.focusOnNextFrame(cursor.parentNode.previousSibling); } + } + } + + onEnter() { + let cursor; + if (cursor = this.getCursor()) { + $.click(cursor); + } + } + + onSuperEnter() { + let cursor; + if (cursor = this.getCursor()) { + $.popup(cursor); + } + } + + onClick(event) { + if ((event.which !== 1) || event.metaKey || event.ctrlKey) { return; } + const target = $.eventTarget(event); + if (target.tagName === 'A') { + this.focus(target, {silent: true}); + } + } +}); +Cls.initClass(); diff --git a/assets/javascripts/views/list/list_fold.js b/assets/javascripts/views/list/list_fold.js index da6f1d5e0e..828f90fd59 100644 --- a/assets/javascripts/views/list/list_fold.js +++ b/assets/javascripts/views/list/list_fold.js @@ -1,71 +1,95 @@ -class app.views.ListFold extends app.View - @targetClass: '_list-dir' - @handleClass: '_list-arrow' - @activeClass: 'open' +/* + * decaffeinate suggestions: + * DS002: Fix invalid constructor + * DS102: Remove unnecessary code created because of implicit returns + * DS206: Consider reworking classes to avoid initClass + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +const Cls = (app.views.ListFold = class ListFold extends app.View { + static initClass() { + this.targetClass = '_list-dir'; + this.handleClass = '_list-arrow'; + this.activeClass = 'open'; + + this.events = + {click: 'onClick'}; + + this.shortcuts = { + left: 'onLeft', + right: 'onRight' + }; + } - @events: - click: 'onClick' + constructor(el) { this.onLeft = this.onLeft.bind(this); this.onRight = this.onRight.bind(this); this.onClick = this.onClick.bind(this); this.el = el; super(...arguments); } - @shortcuts: - left: 'onLeft' - right: 'onRight' + open(el) { + if (el && !el.classList.contains(this.constructor.activeClass)) { + el.classList.add(this.constructor.activeClass); + $.trigger(el, 'open'); + } + } - constructor: (@el) -> super + close(el) { + if (el && el.classList.contains(this.constructor.activeClass)) { + el.classList.remove(this.constructor.activeClass); + $.trigger(el, 'close'); + } + } - open: (el) -> - if el and not el.classList.contains @constructor.activeClass - el.classList.add @constructor.activeClass - $.trigger el, 'open' - return + toggle(el) { + if (el.classList.contains(this.constructor.activeClass)) { + this.close(el); + } else { + this.open(el); + } + } - close: (el) -> - if el and el.classList.contains @constructor.activeClass - el.classList.remove @constructor.activeClass - $.trigger el, 'close' - return + reset() { + let el; + while ((el = this.findByClass(this.constructor.activeClass))) { + this.close(el); + } + } - toggle: (el) -> - if el.classList.contains @constructor.activeClass - @close el - else - @open el - return + getCursor() { + return this.findByClass(app.views.ListFocus.activeClass) || this.findByClass(app.views.ListSelect.activeClass); + } - reset: -> - while el = @findByClass @constructor.activeClass - @close el - return + onLeft() { + const cursor = this.getCursor(); + if (cursor != null ? cursor.classList.contains(this.constructor.activeClass) : undefined) { + this.close(cursor); + } + } - getCursor: -> - @findByClass(app.views.ListFocus.activeClass) or @findByClass(app.views.ListSelect.activeClass) + onRight() { + const cursor = this.getCursor(); + if (cursor != null ? cursor.classList.contains(this.constructor.targetClass) : undefined) { + this.open(cursor); + } + } - onLeft: => - cursor = @getCursor() - if cursor?.classList.contains @constructor.activeClass - @close cursor - return + onClick(event) { + if ((event.which !== 1) || event.metaKey || event.ctrlKey) { return; } + if (!event.pageY) { return; } // ignore fabricated clicks + let el = $.eventTarget(event); + if (el.parentNode.tagName.toUpperCase() === 'SVG') { el = el.parentNode; } - onRight: => - cursor = @getCursor() - if cursor?.classList.contains @constructor.targetClass - @open cursor - return - - onClick: (event) => - return if event.which isnt 1 or event.metaKey or event.ctrlKey - return unless event.pageY # ignore fabricated clicks - el = $.eventTarget(event) - el = el.parentNode if el.parentNode.tagName.toUpperCase() is 'SVG' - - if el.classList.contains @constructor.handleClass - $.stopEvent(event) - @toggle el.parentNode - else if el.classList.contains @constructor.targetClass - if el.hasAttribute('href') - if el.classList.contains(@constructor.activeClass) - @close(el) if el.classList.contains(app.views.ListSelect.activeClass) - else - @open(el) - else - @toggle(el) - return + if (el.classList.contains(this.constructor.handleClass)) { + $.stopEvent(event); + this.toggle(el.parentNode); + } else if (el.classList.contains(this.constructor.targetClass)) { + if (el.hasAttribute('href')) { + if (el.classList.contains(this.constructor.activeClass)) { + if (el.classList.contains(app.views.ListSelect.activeClass)) { this.close(el); } + } else { + this.open(el); + } + } else { + this.toggle(el); + } + } + } +}); +Cls.initClass(); diff --git a/assets/javascripts/views/list/list_select.js b/assets/javascripts/views/list/list_select.js index fe06b70b67..6721726ef1 100644 --- a/assets/javascripts/views/list/list_select.js +++ b/assets/javascripts/views/list/list_select.js @@ -1,43 +1,65 @@ -class app.views.ListSelect extends app.View - @activeClass: 'active' - - @events: - click: 'onClick' - - constructor: (@el) -> super - - deactivate: -> - @deselect() if super - return - - select: (el) -> - @deselect() - if el - el.classList.add @constructor.activeClass - $.trigger el, 'select' - return - - deselect: -> - if selection = @getSelection() - selection.classList.remove @constructor.activeClass - $.trigger selection, 'deselect' - return - - selectByHref: (href) -> - unless @getSelection()?.getAttribute('href') is href - @select @find("a[href='#{href}']") - return - - selectCurrent: -> - @selectByHref location.pathname + location.hash - return - - getSelection: -> - @findByClass @constructor.activeClass - - onClick: (event) => - return if event.which isnt 1 or event.metaKey or event.ctrlKey - target = $.eventTarget(event) - if target.tagName is 'A' - @select target - return +/* + * decaffeinate suggestions: + * DS002: Fix invalid constructor + * DS102: Remove unnecessary code created because of implicit returns + * DS103: Rewrite code to no longer use __guard__, or convert again using --optional-chaining + * DS206: Consider reworking classes to avoid initClass + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +const Cls = (app.views.ListSelect = class ListSelect extends app.View { + static initClass() { + this.activeClass = 'active'; + + this.events = + {click: 'onClick'}; + } + + constructor(el) { this.onClick = this.onClick.bind(this); this.el = el; super(...arguments); } + + deactivate() { + if (super.deactivate(...arguments)) { this.deselect(); } + } + + select(el) { + this.deselect(); + if (el) { + el.classList.add(this.constructor.activeClass); + $.trigger(el, 'select'); + } + } + + deselect() { + let selection; + if (selection = this.getSelection()) { + selection.classList.remove(this.constructor.activeClass); + $.trigger(selection, 'deselect'); + } + } + + selectByHref(href) { + if (__guard__(this.getSelection(), x => x.getAttribute('href')) !== href) { + this.select(this.find(`a[href='${href}']`)); + } + } + + selectCurrent() { + this.selectByHref(location.pathname + location.hash); + } + + getSelection() { + return this.findByClass(this.constructor.activeClass); + } + + onClick(event) { + if ((event.which !== 1) || event.metaKey || event.ctrlKey) { return; } + const target = $.eventTarget(event); + if (target.tagName === 'A') { + this.select(target); + } + } +}); +Cls.initClass(); + +function __guard__(value, transform) { + return (typeof value !== 'undefined' && value !== null) ? transform(value) : undefined; +} \ No newline at end of file diff --git a/assets/javascripts/views/list/paginated_list.js b/assets/javascripts/views/list/paginated_list.js index 0c6c438565..10e2ead7d3 100644 --- a/assets/javascripts/views/list/paginated_list.js +++ b/assets/javascripts/views/list/paginated_list.js @@ -1,90 +1,121 @@ -class app.views.PaginatedList extends app.View - PER_PAGE = app.config.max_results - - constructor: (@data) -> - (@constructor.events or= {}).click ?= 'onClick' - super - - renderPaginated: -> - @page = 0 - - if @totalPages() > 1 - @paginateNext() - else - @html @renderAll() - return - - # render: (dataSlice) -> implemented by subclass - - renderAll: -> - @render @data - - renderPage: (page) -> - @render @data[((page - 1) * PER_PAGE)...(page * PER_PAGE)] - - renderPageLink: (count) -> - @tmpl 'sidebarPageLink', count - - renderPrevLink: (page) -> - @renderPageLink (page - 1) * PER_PAGE - - renderNextLink: (page) -> - @renderPageLink @data.length - page * PER_PAGE - - totalPages: -> - Math.ceil @data.length / PER_PAGE - - paginate: (link) -> - $.lockScroll link.nextSibling or link.previousSibling, => - $.batchUpdate @el, => - if link.nextSibling then @paginatePrev link else @paginateNext link - return - return - return - - paginateNext: -> - @remove @el.lastChild if @el.lastChild # remove link - @hideTopPage() if @page >= 2 # keep previous page into view - @page++ - @append @renderPage(@page) - @append @renderNextLink(@page) if @page < @totalPages() - return - - paginatePrev: -> - @remove @el.firstChild # remove link - @hideBottomPage() - @page-- - @prepend @renderPage(@page - 1) # previous page is offset by one - @prepend @renderPrevLink(@page - 1) if @page >= 3 - return - - paginateTo: (object) -> - index = @data.indexOf(object) - if index >= PER_PAGE - @paginateNext() for [0...(index // PER_PAGE)] - return - - hideTopPage: -> - n = if @page <= 2 - PER_PAGE - else - PER_PAGE + 1 # remove link - @remove @el.firstChild for [0...n] - @prepend @renderPrevLink(@page) - return - - hideBottomPage: -> - n = if @page is @totalPages() - @data.length % PER_PAGE or PER_PAGE - else - PER_PAGE + 1 # remove link - @remove @el.lastChild for [0...n] - @append @renderNextLink(@page - 1) - return - - onClick: (event) => - target = $.eventTarget(event) - if target.tagName is 'SPAN' # link - $.stopEvent(event) - @paginate target - return +/* + * decaffeinate suggestions: + * DS002: Fix invalid constructor + * DS102: Remove unnecessary code created because of implicit returns + * DS104: Avoid inline assignments + * DS202: Simplify dynamic range loops + * DS206: Consider reworking classes to avoid initClass + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +(function() { + let PER_PAGE = undefined; + const Cls = (app.views.PaginatedList = class PaginatedList extends app.View { + static initClass() { + PER_PAGE = app.config.max_results; + } + + constructor(data) { + let base; + this.onClick = this.onClick.bind(this); + this.data = data; + if (((base = this.constructor.events || (this.constructor.events = {}))).click == null) { base.click = 'onClick'; } + super(...arguments); + } + + renderPaginated() { + this.page = 0; + + if (this.totalPages() > 1) { + this.paginateNext(); + } else { + this.html(this.renderAll()); + } + } + + // render: (dataSlice) -> implemented by subclass + + renderAll() { + return this.render(this.data); + } + + renderPage(page) { + return this.render(this.data.slice(((page - 1) * PER_PAGE), (page * PER_PAGE))); + } + + renderPageLink(count) { + return this.tmpl('sidebarPageLink', count); + } + + renderPrevLink(page) { + return this.renderPageLink((page - 1) * PER_PAGE); + } + + renderNextLink(page) { + return this.renderPageLink(this.data.length - (page * PER_PAGE)); + } + + totalPages() { + return Math.ceil(this.data.length / PER_PAGE); + } + + paginate(link) { + $.lockScroll(link.nextSibling || link.previousSibling, () => { + $.batchUpdate(this.el, () => { + if (link.nextSibling) { this.paginatePrev(link); } else { this.paginateNext(link); } + }); + }); + } + + paginateNext() { + if (this.el.lastChild) { this.remove(this.el.lastChild); } // remove link + if (this.page >= 2) { this.hideTopPage(); } // keep previous page into view + this.page++; + this.append(this.renderPage(this.page)); + if (this.page < this.totalPages()) { this.append(this.renderNextLink(this.page)); } + } + + paginatePrev() { + this.remove(this.el.firstChild); // remove link + this.hideBottomPage(); + this.page--; + this.prepend(this.renderPage(this.page - 1)); // previous page is offset by one + if (this.page >= 3) { this.prepend(this.renderPrevLink(this.page - 1)); } + } + + paginateTo(object) { + const index = this.data.indexOf(object); + if (index >= PER_PAGE) { + for (let i = 0, end = Math.floor(index / PER_PAGE), asc = 0 <= end; asc ? i < end : i > end; asc ? i++ : i--) { this.paginateNext(); } + } + } + + hideTopPage() { + const n = this.page <= 2 ? + PER_PAGE + : + PER_PAGE + 1; // remove link + for (let i = 0, end = n, asc = 0 <= end; asc ? i < end : i > end; asc ? i++ : i--) { this.remove(this.el.firstChild); } + this.prepend(this.renderPrevLink(this.page)); + } + + hideBottomPage() { + const n = this.page === this.totalPages() ? + (this.data.length % PER_PAGE) || PER_PAGE + : + PER_PAGE + 1; // remove link + for (let i = 0, end = n, asc = 0 <= end; asc ? i < end : i > end; asc ? i++ : i--) { this.remove(this.el.lastChild); } + this.append(this.renderNextLink(this.page - 1)); + } + + onClick(event) { + const target = $.eventTarget(event); + if (target.tagName === 'SPAN') { // link + $.stopEvent(event); + this.paginate(target); + } + } + }); + Cls.initClass(); + return Cls; +})(); diff --git a/assets/javascripts/views/misc/news.js b/assets/javascripts/views/misc/news.js index a39fbb15cb..2aec85537a 100644 --- a/assets/javascripts/views/misc/news.js +++ b/assets/javascripts/views/misc/news.js @@ -1,34 +1,55 @@ -#= require views/misc/notif - -class app.views.News extends app.views.Notif - @className += ' _notif-news' - - @defautOptions: - autoHide: 30000 - - init: -> - @unreadNews = @getUnreadNews() - @show() if @unreadNews.length - @markAllAsRead() - return - - render: -> - @html app.templates.notifNews(@unreadNews) - return - - getUnreadNews: -> - return [] unless time = @getLastReadTime() - - for news in app.news - break if new Date(news[0]).getTime() <= time - news - - getLastNewsTime: -> - new Date(app.news[0][0]).getTime() - - getLastReadTime: -> - app.settings.get 'news' - - markAllAsRead: -> - app.settings.set 'news', @getLastNewsTime() - return +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS205: Consider reworking code to avoid use of IIFEs + * DS206: Consider reworking classes to avoid initClass + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +//= require views/misc/notif + +const Cls = (app.views.News = class News extends app.views.Notif { + static initClass() { + this.className += ' _notif-news'; + + this.defautOptions = + {autoHide: 30000}; + } + + init() { + this.unreadNews = this.getUnreadNews(); + if (this.unreadNews.length) { this.show(); } + this.markAllAsRead(); + } + + render() { + this.html(app.templates.notifNews(this.unreadNews)); + } + + getUnreadNews() { + let time; + if (!(time = this.getLastReadTime())) { return []; } + + return (() => { + const result = []; + for (var news of Array.from(app.news)) { + if (new Date(news[0]).getTime() <= time) { break; } + result.push(news); + } + return result; + })(); + } + + getLastNewsTime() { + return new Date(app.news[0][0]).getTime(); + } + + getLastReadTime() { + return app.settings.get('news'); + } + + markAllAsRead() { + app.settings.set('news', this.getLastNewsTime()); + } +}); +Cls.initClass(); diff --git a/assets/javascripts/views/misc/notice.js b/assets/javascripts/views/misc/notice.js index 2007930ed4..e3b66a607b 100644 --- a/assets/javascripts/views/misc/notice.js +++ b/assets/javascripts/views/misc/notice.js @@ -1,27 +1,38 @@ -class app.views.Notice extends app.View - @className: '_notice' - @attributes: - role: 'alert' +/* + * decaffeinate suggestions: + * DS002: Fix invalid constructor + * DS101: Remove unnecessary use of Array.from + * DS206: Consider reworking classes to avoid initClass + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +const Cls = (app.views.Notice = class Notice extends app.View { + static initClass() { + this.className = '_notice'; + this.attributes = + {role: 'alert'}; + } - constructor: (@type, @args...) -> super + constructor(type, ...rest) { this.type = type; [...this.args] = Array.from(rest); super(...arguments); } - init: -> - @activate() - return + init() { + this.activate(); + } - activate: -> - @show() if super - return + activate() { + if (super.activate(...arguments)) { this.show(); } + } - deactivate: -> - @hide() if super - return + deactivate() { + if (super.deactivate(...arguments)) { this.hide(); } + } - show: -> - @html @tmpl("#{@type}Notice", @args...) - @prependTo app.el - return + show() { + this.html(this.tmpl(`${this.type}Notice`, ...Array.from(this.args))); + this.prependTo(app.el); + } - hide: -> - $.remove @el - return + hide() { + $.remove(this.el); + } +}); +Cls.initClass(); diff --git a/assets/javascripts/views/misc/notif.js b/assets/javascripts/views/misc/notif.js index dcf2a051da..48b46a943b 100644 --- a/assets/javascripts/views/misc/notif.js +++ b/assets/javascripts/views/misc/notif.js @@ -1,59 +1,78 @@ -class app.views.Notif extends app.View - @className: '_notif' - @activeClass: '_in' - @attributes: - role: 'alert' +/* + * decaffeinate suggestions: + * DS002: Fix invalid constructor + * DS206: Consider reworking classes to avoid initClass + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +const Cls = (app.views.Notif = class Notif extends app.View { + static initClass() { + this.className = '_notif'; + this.activeClass = '_in'; + this.attributes = + {role: 'alert'}; + + this.defautOptions = + {autoHide: 15000}; + + this.events = + {click: 'onClick'}; + } - @defautOptions: - autoHide: 15000 + constructor(type, options) { + this.onClick = this.onClick.bind(this); + this.type = type; + if (options == null) { options = {}; } + this.options = options; + this.options = $.extend({}, this.constructor.defautOptions, this.options); + super(...arguments); + } - @events: - click: 'onClick' + init() { + this.show(); + } - constructor: (@type, @options = {}) -> - @options = $.extend {}, @constructor.defautOptions, @options - super + show() { + if (this.timeout) { + clearTimeout(this.timeout); + this.timeout = this.delay(this.hide, this.options.autoHide); + } else { + this.render(); + this.position(); + this.activate(); + this.appendTo(document.body); + this.el.offsetWidth; // force reflow + this.addClass(this.constructor.activeClass); + if (this.options.autoHide) { this.timeout = this.delay(this.hide, this.options.autoHide); } + } + } - init: -> - @show() - return + hide() { + clearTimeout(this.timeout); + this.timeout = null; + this.detach(); + } - show: -> - if @timeout - clearTimeout @timeout - @timeout = @delay @hide, @options.autoHide - else - @render() - @position() - @activate() - @appendTo document.body - @el.offsetWidth # force reflow - @addClass @constructor.activeClass - @timeout = @delay @hide, @options.autoHide if @options.autoHide - return + render() { + this.html(this.tmpl(`notif${this.type}`)); + } - hide: -> - clearTimeout @timeout - @timeout = null - @detach() - return + position() { + const notifications = $$(`.${app.views.Notif.className}`); + if (notifications.length) { + const lastNotif = notifications[notifications.length - 1]; + this.el.style.top = lastNotif.offsetTop + lastNotif.offsetHeight + 16 + 'px'; + } + } - render: -> - @html @tmpl("notif#{@type}") - return - - position: -> - notifications = $$ ".#{app.views.Notif.className}" - if notifications.length - lastNotif = notifications[notifications.length - 1] - @el.style.top = lastNotif.offsetTop + lastNotif.offsetHeight + 16 + 'px' - return - - onClick: (event) => - return if event.which isnt 1 - target = $.eventTarget(event) - return if target.hasAttribute('data-behavior') - if target.tagName isnt 'A' or target.classList.contains('_notif-close') - $.stopEvent(event) - @hide() - return + onClick(event) { + if (event.which !== 1) { return; } + const target = $.eventTarget(event); + if (target.hasAttribute('data-behavior')) { return; } + if ((target.tagName !== 'A') || target.classList.contains('_notif-close')) { + $.stopEvent(event); + this.hide(); + } + } +}); +Cls.initClass(); diff --git a/assets/javascripts/views/misc/tip.js b/assets/javascripts/views/misc/tip.js index 6fec52a2f4..9c2dd0b4d2 100644 --- a/assets/javascripts/views/misc/tip.js +++ b/assets/javascripts/views/misc/tip.js @@ -1,11 +1,20 @@ -#= require views/misc/notif +/* + * decaffeinate suggestions: + * DS206: Consider reworking classes to avoid initClass + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +//= require views/misc/notif -class app.views.Tip extends app.views.Notif - @className: '_notif _notif-tip' +const Cls = (app.views.Tip = class Tip extends app.views.Notif { + static initClass() { + this.className = '_notif _notif-tip'; + + this.defautOptions = + {autoHide: false}; + } - @defautOptions: - autoHide: false - - render: -> - @html @tmpl("tip#{@type}") - return + render() { + this.html(this.tmpl(`tip${this.type}`)); + } +}); +Cls.initClass(); diff --git a/assets/javascripts/views/misc/updates.js b/assets/javascripts/views/misc/updates.js index 8b91ccfd98..c41f88dac5 100644 --- a/assets/javascripts/views/misc/updates.js +++ b/assets/javascripts/views/misc/updates.js @@ -1,34 +1,56 @@ -#= require views/misc/notif - -class app.views.Updates extends app.views.Notif - @className += ' _notif-news' - - @defautOptions: - autoHide: 30000 - - init: -> - @lastUpdateTime = @getLastUpdateTime() - @updatedDocs = @getUpdatedDocs() - @updatedDisabledDocs = @getUpdatedDisabledDocs() - @show() if @updatedDocs.length > 0 or @updatedDisabledDocs.length > 0 - @markAllAsRead() - return - - render: -> - @html app.templates.notifUpdates(@updatedDocs, @updatedDisabledDocs) - return - - getUpdatedDocs: -> - return [] unless @lastUpdateTime - doc for doc in app.docs.all() when doc.mtime > @lastUpdateTime - - getUpdatedDisabledDocs: -> - return [] unless @lastUpdateTime - doc for doc in app.disabledDocs.all() when doc.mtime > @lastUpdateTime and app.docs.findBy('slug_without_version', doc.slug_without_version) - - getLastUpdateTime: -> - app.settings.get 'version' - - markAllAsRead: -> - app.settings.set 'version', if app.config.env is 'production' then app.config.version else Math.floor(Date.now() / 1000) - return +/* + * decaffeinate suggestions: + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS205: Consider reworking code to avoid use of IIFEs + * DS206: Consider reworking classes to avoid initClass + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +//= require views/misc/notif + +const Cls = (app.views.Updates = class Updates extends app.views.Notif { + static initClass() { + this.className += ' _notif-news'; + + this.defautOptions = + {autoHide: 30000}; + } + + init() { + this.lastUpdateTime = this.getLastUpdateTime(); + this.updatedDocs = this.getUpdatedDocs(); + this.updatedDisabledDocs = this.getUpdatedDisabledDocs(); + if ((this.updatedDocs.length > 0) || (this.updatedDisabledDocs.length > 0)) { this.show(); } + this.markAllAsRead(); + } + + render() { + this.html(app.templates.notifUpdates(this.updatedDocs, this.updatedDisabledDocs)); + } + + getUpdatedDocs() { + if (!this.lastUpdateTime) { return []; } + return Array.from(app.docs.all()).filter((doc) => doc.mtime > this.lastUpdateTime); + } + + getUpdatedDisabledDocs() { + if (!this.lastUpdateTime) { return []; } + return (() => { + const result = []; + for (var doc of Array.from(app.disabledDocs.all())) { if ((doc.mtime > this.lastUpdateTime) && app.docs.findBy('slug_without_version', doc.slug_without_version)) { + result.push(doc); + } + } + return result; + })(); + } + + getLastUpdateTime() { + return app.settings.get('version'); + } + + markAllAsRead() { + app.settings.set('version', app.config.env === 'production' ? app.config.version : Math.floor(Date.now() / 1000)); + } +}); +Cls.initClass(); diff --git a/assets/javascripts/views/pages/base.js b/assets/javascripts/views/pages/base.js index e1c0b6a49e..a36a9d3a4e 100644 --- a/assets/javascripts/views/pages/base.js +++ b/assets/javascripts/views/pages/base.js @@ -1,43 +1,61 @@ -class app.views.BasePage extends app.View - constructor: (@el, @entry) -> super +/* + * decaffeinate suggestions: + * DS002: Fix invalid constructor + * DS101: Remove unnecessary use of Array.from + * DS102: Remove unnecessary code created because of implicit returns + * DS207: Consider shorter variations of null checks + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +app.views.BasePage = class BasePage extends app.View { + constructor(el, entry) { this.paintCode = this.paintCode.bind(this); this.el = el; this.entry = entry; super(...arguments); } - deactivate: -> - if super - @highlightNodes = [] + deactivate() { + if (super.deactivate(...arguments)) { + return this.highlightNodes = []; + } + } - render: (content, fromCache = false) -> - @highlightNodes = [] - @previousTiming = null - @addClass "_#{@entry.doc.type}" unless @constructor.className - @html content - @highlightCode() unless fromCache - @activate() - @delay @afterRender if @afterRender - if @highlightNodes.length > 0 - $.requestAnimationFrame => $.requestAnimationFrame(@paintCode) - return + render(content, fromCache) { + if (fromCache == null) { fromCache = false; } + this.highlightNodes = []; + this.previousTiming = null; + if (!this.constructor.className) { this.addClass(`_${this.entry.doc.type}`); } + this.html(content); + if (!fromCache) { this.highlightCode(); } + this.activate(); + if (this.afterRender) { this.delay(this.afterRender); } + if (this.highlightNodes.length > 0) { + $.requestAnimationFrame(() => $.requestAnimationFrame(this.paintCode)); + } + } - highlightCode: -> - for el in @findAll('pre[data-language]') - language = el.getAttribute('data-language') - el.classList.add("language-#{language}") - @highlightNodes.push(el) - return + highlightCode() { + for (var el of Array.from(this.findAll('pre[data-language]'))) { + var language = el.getAttribute('data-language'); + el.classList.add(`language-${language}`); + this.highlightNodes.push(el); + } + } - paintCode: (timing) => - if @previousTiming - if Math.round(1000 / (timing - @previousTiming)) > 50 # fps - @nodesPerFrame = Math.round(Math.min(@nodesPerFrame * 1.25, 50)) - else - @nodesPerFrame = Math.round(Math.max(@nodesPerFrame * .8, 10)) - else - @nodesPerFrame = 10 + paintCode(timing) { + if (this.previousTiming) { + if (Math.round(1000 / (timing - this.previousTiming)) > 50) { // fps + this.nodesPerFrame = Math.round(Math.min(this.nodesPerFrame * 1.25, 50)); + } else { + this.nodesPerFrame = Math.round(Math.max(this.nodesPerFrame * .8, 10)); + } + } else { + this.nodesPerFrame = 10; + } - for el in @highlightNodes.splice(0, @nodesPerFrame) - $.remove(clipEl) if clipEl = el.lastElementChild - Prism.highlightElement(el) - $.append(el, clipEl) if clipEl + for (var el of Array.from(this.highlightNodes.splice(0, this.nodesPerFrame))) { + var clipEl; + if (clipEl = el.lastElementChild) { $.remove(clipEl); } + Prism.highlightElement(el); + if (clipEl) { $.append(el, clipEl); } + } - $.requestAnimationFrame(@paintCode) if @highlightNodes.length > 0 - @previousTiming = timing - return + if (this.highlightNodes.length > 0) { $.requestAnimationFrame(this.paintCode); } + this.previousTiming = timing; + } +}; diff --git a/assets/javascripts/views/pages/hidden.js b/assets/javascripts/views/pages/hidden.js index f17080a06b..d6e236c75f 100644 --- a/assets/javascripts/views/pages/hidden.js +++ b/assets/javascripts/views/pages/hidden.js @@ -1,16 +1,28 @@ -class app.views.HiddenPage extends app.View - @events: - click: 'onClick' +/* + * decaffeinate suggestions: + * DS002: Fix invalid constructor + * DS206: Consider reworking classes to avoid initClass + * Full docs: https://github.com/decaffeinate/decaffeinate/blob/main/docs/suggestions.md + */ +const Cls = (app.views.HiddenPage = class HiddenPage extends app.View { + static initClass() { + this.events = + {click: 'onClick'}; + } - constructor: (@el, @entry) -> super + constructor(el, entry) { this.onClick = this.onClick.bind(this); this.el = el; this.entry = entry; super(...arguments); } - init: -> - @addSubview @notice = new app.views.Notice 'disabledDoc' - @activate() - return + init() { + this.addSubview(this.notice = new app.views.Notice('disabledDoc')); + this.activate(); + } - onClick: (event) => - if link = $.closestLink(event.target, @el) - $.stopEvent(event) - $.popup(link) - return + onClick(event) { + let link; + if (link = $.closestLink(event.target, this.el)) { + $.stopEvent(event); + $.popup(link); + } + } +}); +Cls.initClass(); diff --git a/assets/javascripts/views/pages/jquery.js b/assets/javascripts/views/pages/jquery.js index 47f021959f..d97ba502b3 100644 --- a/assets/javascripts/views/pages/jquery.js +++ b/assets/javascripts/views/pages/jquery.js @@ -1,57 +1,81 @@ -#= require views/pages/base - -class app.views.JqueryPage extends app.views.BasePage - @demoClassName: '_jquery-demo' - - afterRender: -> - # Prevent jQuery Mobile's demo iframes from scrolling the page - for iframe in @findAllByTag 'iframe' - iframe.style.display = 'none' - $.on iframe, 'load', @onIframeLoaded - - @runExamples() - - onIframeLoaded: (event) => - event.target.style.display = '' - $.off event.target, 'load', @onIframeLoaded - return - - runExamples: -> - for el in @findAllByClass 'entry-example' - try @runExample el catch - return - - runExample: (el) -> - source = el.getElementsByClassName('syntaxhighlighter')[0] - return unless source and source.innerHTML.indexOf('!doctype') isnt -1 - - unless iframe = el.getElementsByClassName(@constructor.demoClassName)[0] - iframe = document.createElement 'iframe' - iframe.className = @constructor.demoClassName - iframe.width = '100%' - iframe.height = 200 - el.appendChild(iframe) - - doc = iframe.contentDocument - doc.write @fixIframeSource(source.textContent) - doc.close() - return - - fixIframeSource: (source) -> - source = source.replace '"/resources/', '"https://api.jquery.com/resources/' # attr(), keydown() - source = source.replace '', """ - - - - """ - source.replace / +\ +` + ); + return source.replace(/ \ -` +`, ); return source.replace(/