diff --git a/assets/javascripts/news.json b/assets/javascripts/news.json
index 052f4918..2c393d97 100644
--- a/assets/javascripts/news.json
+++ b/assets/javascripts/news.json
@@ -1,4 +1,8 @@
[
+ [
+ "2025-02-16",
+ "New documentation: OpenLayers"
+ ],
[
"2024-11-23",
"New documentation: DuckDB"
diff --git a/assets/stylesheets/application.css.scss b/assets/stylesheets/application.css.scss
index ca75b69e..b45e7b60 100644
--- a/assets/stylesheets/application.css.scss
+++ b/assets/stylesheets/application.css.scss
@@ -97,6 +97,7 @@
'pages/nushell',
'pages/octave',
'pages/openjdk',
+ 'pages/openlayers',
'pages/perl',
'pages/phalcon',
'pages/phaser',
diff --git a/assets/stylesheets/pages/_openlayers.scss b/assets/stylesheets/pages/_openlayers.scss
new file mode 100644
index 00000000..50628b2f
--- /dev/null
+++ b/assets/stylesheets/pages/_openlayers.scss
@@ -0,0 +1,10 @@
+._openlayers {
+ @extend %simple;
+ .nameContainer {
+ @extend %block-label;
+ > * { display: inline-block; margin: 0; }
+ > .tag-source { float: right; }
+ }
+ .card { @extend %box; margin-bottom: 1rem; padding: 1rem; }
+ .signature, .type-signature { @extend %code; }
+}
diff --git a/lib/docs/filters/openlayers/clean_html.rb b/lib/docs/filters/openlayers/clean_html.rb
new file mode 100644
index 00000000..713306c2
--- /dev/null
+++ b/lib/docs/filters/openlayers/clean_html.rb
@@ -0,0 +1,18 @@
+module Docs
+ class Openlayers
+ class CleanHtmlFilter < Filter
+ def call
+ @doc = at_css('section')
+
+ at_css('h2').name = 'h1' if at_css('h2')
+
+ css('pre.prettyprint').each do |node|
+ node['data-language'] = node['class'].include?('html') ? 'html' : 'js'
+ node.content = node.content
+ end
+
+ doc
+ end
+ end
+ end
+end
diff --git a/lib/docs/filters/openlayers/entries.rb b/lib/docs/filters/openlayers/entries.rb
new file mode 100644
index 00000000..05e18850
--- /dev/null
+++ b/lib/docs/filters/openlayers/entries.rb
@@ -0,0 +1,22 @@
+module Docs
+ class Openlayers
+ class EntriesFilter < Docs::EntriesFilter
+ def get_name
+ at_css('h2').text.split('~').last.strip
+ end
+
+ def get_type
+ slug[/ol_([^_]+)_/, 1] or 'ol'
+ end
+
+ def additional_entries
+ css('h4.name').each_with_object [] do |node, entries|
+ node['id'] = node.previous_element['id']
+ name = node.children.find {|n| n.text? }.text.strip
+ name.prepend "#{self.name}."
+ entries << [name, node['id']]
+ end
+ end
+ end
+ end
+end
diff --git a/lib/docs/scrapers/openlayers.rb b/lib/docs/scrapers/openlayers.rb
new file mode 100644
index 00000000..1c957fbd
--- /dev/null
+++ b/lib/docs/scrapers/openlayers.rb
@@ -0,0 +1,24 @@
+module Docs
+ class Openlayers < UrlScraper
+ self.name = 'OpenLayers'
+ self.type = 'openlayers'
+ self.slug = 'openlayers'
+ self.release = '10.4.0'
+ self.base_url = "https://openlayers.org/en/latest/apidoc/"
+ self.links = {
+ home: 'https://openlayers.org/',
+ code: 'https://github.com/openlayers/openlayers'
+ }
+
+ html_filters.push 'openlayers/entries', 'openlayers/clean_html'
+
+ options[:attribution] = <<-HTML
+ © 2005-present, OpenLayers Contributors All rights reserved.
+ Licensed under the BSD 2-Clause License.
+ HTML
+
+ def get_latest_version(opts)
+ get_npm_version('ol', opts)
+ end
+ end
+end
diff --git a/public/icons/docs/openlayers/16.png b/public/icons/docs/openlayers/16.png
new file mode 100644
index 00000000..fe90ebfb
Binary files /dev/null and b/public/icons/docs/openlayers/16.png differ
diff --git a/public/icons/docs/openlayers/16@2x.png b/public/icons/docs/openlayers/16@2x.png
new file mode 100644
index 00000000..c39658a1
Binary files /dev/null and b/public/icons/docs/openlayers/16@2x.png differ
diff --git a/public/icons/docs/openlayers/SOURCE b/public/icons/docs/openlayers/SOURCE
new file mode 100644
index 00000000..74be1eaa
--- /dev/null
+++ b/public/icons/docs/openlayers/SOURCE
@@ -0,0 +1,2 @@
+https://github.com/openlayers
+https://avatars.githubusercontent.com/u/240579?s=64