From 5fbd914742e579432bb4a23cf1fd67a872357442 Mon Sep 17 00:00:00 2001 From: Al Serize Date: Wed, 17 Jun 2026 16:21:15 -0400 Subject: [PATCH 1/6] Add ColdFusion (CFDocs) documentation --- lib/docs/filters/coldfusion/clean_html.rb | 85 ++++++++++++++++++++++ lib/docs/filters/coldfusion/entries.rb | 66 +++++++++++++++++ lib/docs/scrapers/coldfusion.rb | 58 +++++++++++++++ public/icons/docs/coldfusion/16.png | Bin 0 -> 725 bytes public/icons/docs/coldfusion/16@2x.png | Bin 0 -> 1215 bytes public/icons/docs/coldfusion/SOURCE | 1 + 6 files changed, 210 insertions(+) create mode 100644 lib/docs/filters/coldfusion/clean_html.rb create mode 100644 lib/docs/filters/coldfusion/entries.rb create mode 100644 lib/docs/scrapers/coldfusion.rb create mode 100644 public/icons/docs/coldfusion/16.png create mode 100644 public/icons/docs/coldfusion/16@2x.png create mode 100644 public/icons/docs/coldfusion/SOURCE diff --git a/lib/docs/filters/coldfusion/clean_html.rb b/lib/docs/filters/coldfusion/clean_html.rb new file mode 100644 index 0000000000..b03863bc31 --- /dev/null +++ b/lib/docs/filters/coldfusion/clean_html.rb @@ -0,0 +1,85 @@ +module Docs + class Coldfusion + class CleanHtmlFilter < Filter + def call + # Listing/category pages (Tags, Functions, a category, or a guide index) + # use a different layout; keep their main container as-is after cleanup. + @doc = build_root + + # Remove site chrome and interactive widgets. + css('nav', 'footer', 'script', 'noscript', '#cfbreak', '.newsletter').remove + css('.modal', '.add-example-modal-lg', '.example-modal').remove + css('.example-btn', '.copy-btn', '.issuebutton', '.issuecount').remove + css('button').remove + + # Drop the "Add An Example" / edit / fork affordances. + css('a[href*="github.com"]', '#forkme', '#foundeo').remove + css('a.label.label-danger').remove # Edit links + + # Clean up the breadcrumb: keep the engine-version labels (they convey + # ColdFusion/Lucee/BoxLang availability) but drop navigation links and + # the issue tracker widget. + if (crumb = at_css('.breadcrumb')) + crumb.css('.label-warning').remove + crumb.css('.divider').remove + crumb.css('a[rel="nofollow"]').remove + # Remove navigation breadcrumb items (CFDocs > Functions > cf45 > …) + # that are not engine-availability labels. + crumb.css('li:not(.pull-right)').each do |li| + li.remove unless li.at_css('.label-acf, .label-lucee, .label-boxlang, .label-railo') + end + end + + # Code blocks: tag them so DevDocs applies CFML syntax highlighting. + css('pre.prettyprint', 'pre').each do |node| + node.remove_attribute('class') + node['data-language'] = 'coldfusion' + end + + # Inline code: nothing special needed, but strip prettyprint hints. + css('code').each { |node| node.remove_attribute('class') } + + # Remove now-empty wrappers left behind by the source template's many + # conditional blank lines. + css('div', 'p', 'span', 'ul', 'ol').each do |node| + node.remove if node.inner_html.strip.empty? && node.element_children.empty? + end + + doc + end + + # cfdocs splits an entry's content across the `.jumbotron` header (name, + # description, syntax), the `.breadcrumb`, and the main `.container` + # (arguments, compatibility, links, examples). Merge them into one root. + # + # NOTE: between filters the document is re-parsed as an HTML *fragment* + # (there is no ), so selectors must not depend on `body >`. + def build_root + # First .jumbotron is the page header; #cfbreak is the trailing + # newsletter jumbotron, which we ignore. + header = css('.jumbotron').reject { |n| n['id'] == 'cfbreak' } + .map { |n| n.at_css('.container') || n } + .first + breadcrumb = at_css('.breadcrumb') + + # The main content container holds the reference sections. It is a + # `.container` that is not the breadcrumb and not inside a jumbotron or + # nav. Identify it by the section headings it contains. + main = css('.container').find do |node| + next false if node.matches?('.breadcrumb') + next false if node.ancestors('.jumbotron').any? || node.ancestors('nav').any? + node.at_css('h2, .param, .panel') || node.at_css('#examples') + end + + root = Nokogiri::HTML.fragment('
').at_css('div') + root << header.dup if header + root << breadcrumb.dup if breadcrumb + root << main.dup if main + + # Fall back to the full document/fragment if the expected structure is + # missing (e.g. some guide pages). + root.element_children.any? ? root : (at_css('body') || doc) + end + end + end +end diff --git a/lib/docs/filters/coldfusion/entries.rb b/lib/docs/filters/coldfusion/entries.rb new file mode 100644 index 0000000000..7cdcb4445b --- /dev/null +++ b/lib/docs/filters/coldfusion/entries.rb @@ -0,0 +1,66 @@ +module Docs + class Coldfusion + class EntriesFilter < Docs::EntriesFilter + # Category/listing slugs that aggregate other entries and must not appear + # as entries themselves. Categories generally end in "-functions" or + # "-tags", but list the fixed index pages explicitly. + INDEX_SLUGS = %w(index tags functions all).freeze + + def include_default_entry? + entry_page? + end + + def get_name + if (h1 = at_css('#docname')) + h1.content.strip + elsif (h1 = at_css('h1')) + # Guide pages: use the heading text without anchor noise. + h1.content.strip + else + super + end + end + + def get_type + return 'Guides' if guide_page? + + # Use the second breadcrumb link (Tags / Functions) as the category. + crumb = css('.breadcrumb a').map { |a| a.content.strip } + if crumb.include?('Tags') + 'Tags' + elsif crumb.include?('Functions') + 'Functions' + else + 'Guides' + end + end + + private + + # A real reference entry: a tag or function page. These have a `data-doc` + # whose value matches the slug (no spaces) and usually a `#syntax` block. + def entry_page? + return false if index_slug? + return true if guide_page? + + doc_name = at_css('[data-doc]').try(:[], 'data-doc') + return false if doc_name.nil? + # Category pages have human titles with spaces (e.g. "String Functions"). + !doc_name.include?(' ') + end + + def guide_page? + # Guides have no breadcrumb but do have content; they are neither tags + # nor functions nor category indexes. + return false if index_slug? + at_css('.breadcrumb').nil? && at_css('h1') + end + + def index_slug? + s = slug.to_s.downcase + return true if INDEX_SLUGS.include?(s) + s.end_with?('-functions', '-tags') + end + end + end +end diff --git a/lib/docs/scrapers/coldfusion.rb b/lib/docs/scrapers/coldfusion.rb new file mode 100644 index 0000000000..df6a0f861b --- /dev/null +++ b/lib/docs/scrapers/coldfusion.rb @@ -0,0 +1,58 @@ +module Docs + class Coldfusion < UrlScraper + self.name = 'ColdFusion' + self.slug = 'coldfusion' + self.type = 'simple' + self.base_url = 'https://cfdocs.org/' + self.root_path = 'index.cfm' + self.links = { + home: 'https://cfdocs.org/', + code: 'https://github.com/foundeo/cfdocs' + } + + html_filters.push 'coldfusion/entries', 'coldfusion/clean_html' + + options[:root_title] = 'ColdFusion' + + # cfdocs links categories with an encoded dash (e.g. /array%2Dfunctions); + # decode and clean those so entry paths look like /array-functions. + options[:decode_and_clean_paths] = true + + # cfdocs.org renders a page for every tag/function/guide at the site root, + # e.g. /hash or /cfhtmltopdf. Category "listing" pages (such as /tags, + # /functions and /array-functions) are crawled to discover entries, but the + # Entries filter excludes them from the index. + # + # Skip site chrome, utilities, reports and other non-reference pages. + options[:skip] = %w( + 404.cfm contributors.cfm trycf.cfm ucase.cfm llms.cfm + how-to-contribute opensearch.xml robots.txt) + + options[:skip_patterns] = [ + /\Aassets\b/, + /\Areports\b/, + /\Autilities\b/, + /\Aslack\b/, + /openimage/, + /\.json\z/, + /\.png\z/, + /\.ico\z/, + /\.xml\z/, + /\.css\z/, + /\.js\z/ + ] + + options[:attribution] = <<-HTML + © 2012–present Foundeo, Inc. and the CFDocs contributors.
+ Licensed under the MIT License.
+ ColdFusion is a trademark of Adobe Systems Incorporated. + HTML + + def get_latest_version(opts) + # CFDocs is continuously updated and has no formal version number; use the + # date of the latest commit as a proxy version. + commits = get_github_commits('foundeo', 'cfdocs', opts) + commits[0]['commit']['committer']['date'][0...10] + end + end +end diff --git a/public/icons/docs/coldfusion/16.png b/public/icons/docs/coldfusion/16.png new file mode 100644 index 0000000000000000000000000000000000000000..00ccc8f85ec5620817458b13a51c7424da7592e6 GIT binary patch literal 725 zcmV;`0xJE9P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0%}P_K~y+TrBg{v zlR*@o7y?*=8(5Zj&@+jN(Rea3o{dXP#Ke;~4_=H>FD7bCJn2D0X-jEq6H5fd#09#y zAWNwhOQ<5Gos=l` znP*!3adlU2OB1P~2uEX*E)ujrt`A zZDVgCn(rvRcBEqP&Xcfekh7$Cas)}BjKNE)D86^hl+^Fxf;0$1+!@U zCY^iAi^v0Zsb#yt|QPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1XM{xK~z{r?N?n) zn`IQPW^=>M%{d)xS?2$O(P7L?bT=kOmv}#~^va(^lO-k^6XVs4UU{V%+h9-ywuub< zn{iM^3vHnchz-&)H-?n`&yL}5j`ZQga2UVMP8vH& z;o2Hki6-z_Mg-;(3;1DpKmzjSOH{*wIa_`Mm^<_@TtN; zH74e+8LGgvC^MNHqP$TuTcVUoP+k?uJls-+?cO8WIrGO#fM4JV$r0|Gx|v9o;fjW0 z_3>cPP)o}D=i0P$J4J#HNsMJ@R>6`9NXe zcXRNj6vs#1jd0O$?)TJc2fLHHCnnWqb%lr;jI-@VV39s0Blb@=1T!O(Ae2d8X@I zY00I^@pPa@rSl&szSyH{+RjNfSP3Kq(fK*d#4V7u(YY9QcGj`wD~I%Z6`4;{Xdb(w z>prvea)Fh=v^aZSdVZnrD4vuVk%=Oyj;;PGNl3|oJl8?hI1w?_fZ6#to@)JCPb@n} zi@{2ORq&`JAv-Kf(TY?OO1RbDzE(pmDQus&*YDR&$Lff6&vhE+>@Xu`Mbi4Ew(BR63Vj1TlV>c-~{MES=FgK(sB+H d+Mnmn@L!MA5?;IU&kO(n002ovPDHLkV1me-JS+eJ literal 0 HcmV?d00001 diff --git a/public/icons/docs/coldfusion/SOURCE b/public/icons/docs/coldfusion/SOURCE new file mode 100644 index 0000000000..32de636dcb --- /dev/null +++ b/public/icons/docs/coldfusion/SOURCE @@ -0,0 +1 @@ +https://cfdocs.org/apple-touch-icon.png From 99fcfa4b3ff35e143e092afc58ad904b7aca8c07 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Fri, 3 Jul 2026 23:34:54 +0200 Subject: [PATCH 2/6] coldfusion: fix get_latest_version --- lib/docs/scrapers/coldfusion.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/docs/scrapers/coldfusion.rb b/lib/docs/scrapers/coldfusion.rb index df6a0f861b..ce827d9eda 100644 --- a/lib/docs/scrapers/coldfusion.rb +++ b/lib/docs/scrapers/coldfusion.rb @@ -9,6 +9,7 @@ class Coldfusion < UrlScraper home: 'https://cfdocs.org/', code: 'https://github.com/foundeo/cfdocs' } + self.release = '2026-04-30' html_filters.push 'coldfusion/entries', 'coldfusion/clean_html' @@ -51,8 +52,7 @@ class Coldfusion < UrlScraper def get_latest_version(opts) # CFDocs is continuously updated and has no formal version number; use the # date of the latest commit as a proxy version. - commits = get_github_commits('foundeo', 'cfdocs', opts) - commits[0]['commit']['committer']['date'][0...10] + get_latest_github_commit_date('foundeo', 'cfdocs', opts) end end end From b2d4c00dd4d1f24a3e218d7f0cdeb47e7e9df148 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Fri, 3 Jul 2026 23:46:39 +0200 Subject: [PATCH 3/6] coldfusion: unwrap .container --- lib/docs/filters/coldfusion/clean_html.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/docs/filters/coldfusion/clean_html.rb b/lib/docs/filters/coldfusion/clean_html.rb index b03863bc31..b5fe9a7c27 100644 --- a/lib/docs/filters/coldfusion/clean_html.rb +++ b/lib/docs/filters/coldfusion/clean_html.rb @@ -39,6 +39,10 @@ def call # Inline code: nothing special needed, but strip prettyprint hints. css('code').each { |node| node.remove_attribute('class') } + # Unwrap Bootstrap `.container` layout wrappers; DevDocs supplies its own + # page width, so these only add centering/padding we don't want. + css('.container').each { |node| node.before(node.children).remove } + # Remove now-empty wrappers left behind by the source template's many # conditional blank lines. css('div', 'p', 'span', 'ul', 'ol').each do |node| From 172b4066807ebd18a0f0efdb2661f8ab402073f3 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Fri, 3 Jul 2026 23:48:42 +0200 Subject: [PATCH 4/6] coldfusion: news entry --- assets/javascripts/news.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/javascripts/news.json b/assets/javascripts/news.json index f9fd497a03..ef3e40f9b3 100644 --- a/assets/javascripts/news.json +++ b/assets/javascripts/news.json @@ -1,7 +1,7 @@ [ [ "2026-07-03", - "New documentations: Celery, RabbitMQ" + "New documentations: Celery, RabbitMQ, ColdFusion" ], [ "2026-06-02", From 3bffd463b50435b9ddff4e67cad239fe0b09752d Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Fri, 3 Jul 2026 23:50:11 +0200 Subject: [PATCH 5/6] coldfusion: remove #search2 --- lib/docs/filters/coldfusion/clean_html.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/docs/filters/coldfusion/clean_html.rb b/lib/docs/filters/coldfusion/clean_html.rb index b5fe9a7c27..922d07a5b8 100644 --- a/lib/docs/filters/coldfusion/clean_html.rb +++ b/lib/docs/filters/coldfusion/clean_html.rb @@ -11,6 +11,7 @@ def call css('.modal', '.add-example-modal-lg', '.example-modal').remove css('.example-btn', '.copy-btn', '.issuebutton', '.issuecount').remove css('button').remove + css('#search2').remove # Drop the "Add An Example" / edit / fork affordances. css('a[href*="github.com"]', '#forkme', '#foundeo').remove From c618f5681ee1d497f3b7a46ffb9ec9a82ab0f4d2 Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Fri, 3 Jul 2026 23:51:24 +0200 Subject: [PATCH 6/6] coldfusion: remove br --- lib/docs/filters/coldfusion/clean_html.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/docs/filters/coldfusion/clean_html.rb b/lib/docs/filters/coldfusion/clean_html.rb index 922d07a5b8..99d92a208d 100644 --- a/lib/docs/filters/coldfusion/clean_html.rb +++ b/lib/docs/filters/coldfusion/clean_html.rb @@ -11,6 +11,7 @@ def call css('.modal', '.add-example-modal-lg', '.example-modal').remove css('.example-btn', '.copy-btn', '.issuebutton', '.issuecount').remove css('button').remove + css('br').remove css('#search2').remove # Drop the "Add An Example" / edit / fork affordances.