diff --git a/assets/javascripts/views/pages/simple.coffee b/assets/javascripts/views/pages/simple.coffee
index 20e13fcd..33b47deb 100644
--- a/assets/javascripts/views/pages/simple.coffee
+++ b/assets/javascripts/views/pages/simple.coffee
@@ -16,6 +16,7 @@ app.views.GoPage =
app.views.KotlinPage =
app.views.LaravelPage =
app.views.LodashPage =
+app.views.LovePage =
app.views.MarionettePage =
app.views.MdnPage =
app.views.MeteorPage =
diff --git a/assets/stylesheets/application-dark.css.scss b/assets/stylesheets/application-dark.css.scss
index 7e90020b..c2d697fd 100644
--- a/assets/stylesheets/application-dark.css.scss
+++ b/assets/stylesheets/application-dark.css.scss
@@ -53,6 +53,7 @@
'pages/knockout',
'pages/kotlin',
'pages/laravel',
+ 'pages/love',
'pages/lua',
'pages/mdn',
'pages/meteor',
diff --git a/assets/stylesheets/application.css.scss b/assets/stylesheets/application.css.scss
index 6d44015d..26385273 100644
--- a/assets/stylesheets/application.css.scss
+++ b/assets/stylesheets/application.css.scss
@@ -53,6 +53,7 @@
'pages/knockout',
'pages/kotlin',
'pages/laravel',
+ 'pages/love',
'pages/lua',
'pages/mdn',
'pages/meteor',
diff --git a/assets/stylesheets/pages/_love.scss b/assets/stylesheets/pages/_love.scss
new file mode 100644
index 00000000..e7993815
--- /dev/null
+++ b/assets/stylesheets/pages/_love.scss
@@ -0,0 +1,67 @@
+._love {
+ padding-left: 1rem;
+
+ h1, h2 { margin-left: -1rem; }
+ h2 { @extend %block-heading; }
+ h3 { margin-left: -0.5rem; @extend %block-label; }
+
+ ._mobile & {
+ padding-left: 0;
+
+ h1, h2, h3 { margin-left: 0; }
+ }
+
+ p > code, li > code { @extend %label; }
+ blockquote { @extend %note; }
+
+ .box { @extend %box; }
+ .note { @extend %note; }
+ .label { @extend %label; }
+
+ .box-heading {
+ @extend %heading-box;
+ padding: .5em .75em;
+ margin-top: 1.5rem;
+ margin-bottom: 0px;
+ border-bottom: none;
+ border-bottom-left-radius: 0px;
+ border-bottom-right-radius: 0px;
+ }
+ .box-with-heading {
+ @extend %box;
+ padding: .5em .75em;
+ margin-top: 0px;
+ margin-bottom: 1.5rem;
+ border-top-left-radius: 0px;
+ border-top-right-radius: 0px;
+ }
+
+ .note-green, .label-green { @extend %note-green; }
+ .note-red, .label-red { @extend %note-red; }
+ .note-orange, .label-orange { @extend %note-orange; }
+ .cell-green { background: $noteGreenBackground; }
+ .cell-red { background: $noteRedBackground; }
+
+ .smwtable {
+ width: 100%;
+ tr {
+ td {
+ word-wrap: break-word;
+ }
+
+ td:first-child, td:nth-last-child(2), td:last-child {
+ vertical-align: middle;
+ white-space: nowrap;
+ width: 1em;
+ overflow: hidden;
+ }
+ }
+ }
+
+ hr {
+ border: none;
+ height: 1px;
+ background-color: $textColorLighter;
+ margin: 1.5em 0 1em;
+ }
+}
diff --git a/lib/docs/filters/love/clean_html.rb b/lib/docs/filters/love/clean_html.rb
new file mode 100644
index 00000000..8e83c742
--- /dev/null
+++ b/lib/docs/filters/love/clean_html.rb
@@ -0,0 +1,103 @@
+module Docs
+ class Love
+ class CleanHtmlFilter < Filter
+ def call
+ # Fix syntax highlighting
+ css('.mw-code').each do |node|
+ node.content = node.at_css("div > pre").content
+ node['data-language'] = 'lua'
+ node.name = 'pre'
+ end
+
+ # Move header tags up
+ css('h2', 'h3').each do |node|
+ headline = node.at_css('.mw-headline')
+ node['id'] = headline['id']
+ node.content = headline.inner_text
+ end
+
+ # Move dt tags up
+ css('dt > span').each do |node|
+ node.parent.content = node.inner_text
+ end
+
+ # Style notices and new/removed sections
+ css('.notice', '.new-section', '.removed-section', '.removed-new-section').each do |node|
+ case node['class']
+ when 'notice'
+ node['class'] = 'note note-warning'
+ node.inner_html = node.at_css('td:nth-child(2)').inner_html
+ node.next.remove unless node.next.nil? or node.next.name != 'br'
+ when 'new-section', 'removed-section', 'removed-new-section'
+ node['class'] = node['class'] == 'new-section' ? 'note note-green' : 'note note-red'
+ node.inner_html = node.at_css('tr > td > i').inner_html \
+ + '
' \
+ + node.at_css('tr > td > small').inner_html
+ end
+
+ node.name = 'p'
+ node.remove_attribute('bgcolor')
+ node.remove_attribute('style')
+ node.remove_attribute('align')
+ end
+
+ # Style new/removed features
+ css('.new-feature', '.removed-feature', '.removed-new-feature').each do |node|
+ node.name = 'div'
+ node['class'] = node['class'] == 'new-feature' ? 'box-heading label-green' : 'box-heading label-red'
+ node.remove_attribute('style')
+
+ container = node.next_element
+ container.name = 'div'
+ container['class'] = 'box-with-heading'
+ container.remove_attribute('style')
+ end
+
+ # Style tables
+ css('table.smwtable').each do |table|
+ table.remove_attribute('style')
+ table.css('td').each do |cell|
+ cell.remove_attribute('style')
+ end
+ table.css('td:last-child', 'td:nth-last-child(2)').each do |cell|
+ img = cell.at_css('img')
+ if img then
+ if img['alt'] == 'Added since' then
+ cell['class'] = 'cell-green'
+ elsif img['alt'] == 'Removed in'
+ cell['class'] = 'cell-red'
+ end
+ img.remove
+ end
+ end
+ end
+
+ # Remove Other Languages
+ css('#Other_Languages').remove
+ css('.i18n').remove
+
+ # Remove changelog
+ node = at_css('h2#Changelog')
+ if !node.nil? then
+ begin
+ nxt = node.next
+ node.remove
+ node = nxt
+ end while !node.nil? and node.name != 'h2'
+ end
+
+ # Remove empty paragraphs
+ css('p').each do |node|
+ node.remove if node.inner_text.strip == ''
+ end
+
+ # Remove linebreaks that are the first or last child of a paragraph
+ css('p > br:first-child', 'p > br:last-child').each do |node|
+ node.remove
+ end
+
+ doc
+ end
+ end
+ end
+end
diff --git a/lib/docs/filters/love/entries.rb b/lib/docs/filters/love/entries.rb
new file mode 100644
index 00000000..47d21ad3
--- /dev/null
+++ b/lib/docs/filters/love/entries.rb
@@ -0,0 +1,22 @@
+module Docs
+ class Love
+ class EntriesFilter < Docs::EntriesFilter
+ def get_type
+ if TYPE_OVERRIDE.key?(slug) then
+ return TYPE_OVERRIDE[slug]
+ elsif m = slug.match(/\A(love\.\w+)\z/) then
+ # modules and funcions
+ return LOVE_MODULES.include?(m[1]) ? m[1] : 'love'
+ elsif m = slug.match(/\A(love\.\w+)\.(\w+)/) then
+ # functions in modules
+ return m[1]
+ elsif context[:list_classes] and (m = slug.match(/\A\(?([A-Z]\w+)\)?(\:\w+)?/)) then
+ # classes, their members and enums
+ return m[1] unless m[1].include?('_')
+ end
+ # usually this shouldn't happen
+ "Other"
+ end
+ end
+ end
+end
diff --git a/lib/docs/scrapers/love.rb b/lib/docs/scrapers/love.rb
new file mode 100644
index 00000000..4fa1b13b
--- /dev/null
+++ b/lib/docs/scrapers/love.rb
@@ -0,0 +1,99 @@
+module Docs
+ class Love < UrlScraper
+ LOVE_MODULES = %w(
+ love
+ love.audio
+ love.event
+ love.filesystem
+ love.font
+ love.graphics
+ love.image
+ love.joystick
+ love.keyboard
+ love.math
+ love.mouse
+ love.physics
+ love.sound
+ love.system
+ love.thread
+ love.timer
+ love.touch
+ love.video
+ love.window
+ )
+ TYPE_OVERRIDE = {
+ "Audio_Formats" => "love.sound",
+ "ImageFontFormat" => "love.font",
+ "BlendMode_Formulas" => "BlendMode",
+ "Shader_Variables" => "Shader"
+ }
+
+ self.name = 'LÖVE'
+ self.slug = 'love'
+ self.type = 'love'
+ self.base_url = 'https://love2d.org/wiki/'
+ self.root_path = 'love'
+ self.initial_paths = LOVE_MODULES
+ self.links = {
+ home: 'https://love2d.org/',
+ code: 'https://bitbucket.org/rude/love'
+ }
+
+ html_filters.push 'love/clean_html', 'love/entries', 'title'
+
+ options[:root_title] = 'love'
+ options[:initial_paths] = LOVE_MODULES
+
+ options[:decode_and_clean_paths] = true
+
+ # Add types to classes and their members
+ options[:list_classes] = true
+
+ options[:container] = '#mw-content-text'
+
+ options[:only_patterns] = [
+ /\A(love\z|love\.|[A-Z]|\([A-Z])/
+ # love
+ # love.* (modules and functions)
+ # Uppercased (classes and enums)
+ # (Uppercased) (generalized classes)
+ ]
+ options[:skip] = %w(
+ Getting_Started
+ Building_LÖVE
+ Tutorial
+ Tutorials
+ Game_Distribution
+ License
+ Games
+ Libraries
+ Software
+ Snippets
+ Version_History
+ Lovers
+ PO2_Syndrome
+ HSL_color
+ )
+ options[:skip_patterns] = [
+ /_\([^\)]+\)\z/,
+ # anything_(language) (this might have to be tweaked)
+ /\ASpecial:/,
+ /\ACategory:/,
+ /\AFile:/,
+ /\AHelp:/,
+ /\ATemplate:/,
+ /\AUser:/,
+ /\ATutorial:/
+ # special pages are indistinguishable from instance methods
+ ]
+
+ options[:replace_paths] = {
+ "Config_Files" => "love.conf"
+ }
+
+ options[:attribution] = <<-HTML
+ © LÖVE Development Team
+ Licensed under the GNU Free Documentation License, Version 1.3.
+ HTML
+ end
+end
diff --git a/public/icons/docs/love/16.png b/public/icons/docs/love/16.png
new file mode 100644
index 00000000..9aed38b0
Binary files /dev/null and b/public/icons/docs/love/16.png differ
diff --git a/public/icons/docs/love/16@2x.png b/public/icons/docs/love/16@2x.png
new file mode 100644
index 00000000..1a49ad5a
Binary files /dev/null and b/public/icons/docs/love/16@2x.png differ
diff --git a/public/icons/docs/love/SOURCE b/public/icons/docs/love/SOURCE
new file mode 100644
index 00000000..a24a3802
--- /dev/null
+++ b/public/icons/docs/love/SOURCE
@@ -0,0 +1 @@
+https://bytebucket.org/rude/love/raw/default/platform/unix/love.svg