From f36ba0b5bfcf3fe0f0c8bea372d0ca9ee5ec8fb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Mon, 30 Nov 2015 11:32:01 +0100 Subject: [PATCH] add discourse-details plugin --- .gitignore | 1 + plugins/discourse-details/LICENSE | 22 +++++++ plugins/discourse-details/README.md | 27 +++++++++ .../assets/javascripts/details.js | 58 +++++++++++++++++++ .../assets/javascripts/details_dialect.js | 26 +++++++++ .../initializers/apply-details.js.es6 | 10 ++++ .../assets/stylesheets/details.scss | 38 ++++++++++++ .../config/locales/server.en.yml | 3 + plugins/discourse-details/config/settings.yml | 4 ++ plugins/discourse-details/plugin.rb | 27 +++++++++ 10 files changed, 216 insertions(+) create mode 100644 plugins/discourse-details/LICENSE create mode 100644 plugins/discourse-details/README.md create mode 100644 plugins/discourse-details/assets/javascripts/details.js create mode 100644 plugins/discourse-details/assets/javascripts/details_dialect.js create mode 100644 plugins/discourse-details/assets/javascripts/initializers/apply-details.js.es6 create mode 100644 plugins/discourse-details/assets/stylesheets/details.scss create mode 100644 plugins/discourse-details/config/locales/server.en.yml create mode 100644 plugins/discourse-details/config/settings.yml create mode 100644 plugins/discourse-details/plugin.rb diff --git a/.gitignore b/.gitignore index 7b2156d4d..3bdbb95c8 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,7 @@ log/ !/plugins/emoji/ !/plugins/lazyYT/ !/plugins/poll/ +!/plugins/discourse-details/ /plugins/*/auto_generated/ /spec/fixtures/plugins/my_plugin/auto_generated diff --git a/plugins/discourse-details/LICENSE b/plugins/discourse-details/LICENSE new file mode 100644 index 000000000..e87025e8d --- /dev/null +++ b/plugins/discourse-details/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Discourse + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/plugins/discourse-details/README.md b/plugins/discourse-details/README.md new file mode 100644 index 000000000..afebd4f1a --- /dev/null +++ b/plugins/discourse-details/README.md @@ -0,0 +1,27 @@ +### discourse-details + +HTML 5.1 `
` polyfill for [Discourse](https://www.discourse.org). + +NOTE: Does not work on IE9. + +## Usage + +In your posts, surround text with `[details=your summary]` ... `[/details]`. +For example: + +``` + I watched the murder mystery on TV last night. [details=Who did it?]The butler did it[/details]. +``` + +## Installation + +Follow our [Install a Plugin](https://meta.discourse.org/t/install-a-plugin/19157) howto, using +`git clone https://github.com/discourse/discourse-details.git` as the plugin command. + +## Issues + +If you have issues or suggestions for the plugin, please bring them up on [Discourse Meta](https://meta.discourse.org). + +## License + +MIT diff --git a/plugins/discourse-details/assets/javascripts/details.js b/plugins/discourse-details/assets/javascripts/details.js new file mode 100644 index 000000000..54436783b --- /dev/null +++ b/plugins/discourse-details/assets/javascripts/details.js @@ -0,0 +1,58 @@ +(function(document, $) { + + // cf. http://mths.be/details + var hasNativeSupport = (function(doc) { + var fake, el = doc.createElement("details"); + // fail-fast + if (!("open" in el)) { return false; } + // figure out a root node + var root = doc.body || (function() { + var de = doc.documentElement; + fake = true; + return de.insertBefore(doc.createElement("body"), de.firstElementChild || de.firstChild); + })(); + // setup test element + el.innerHTML = "ab"; + el.style.display = "block"; + // add test element to the root node + root.appendChild(el); + // can we open it? + var diff = el.offsetHeight; + el.open = true; + diff = diff !== el.offsetHeight; + // cleanup + root.removeChild(el); + if (fake) { root.parentNode.removeChild(root); } + // return the result + return diff; + })(document); + + function toggleOpen($details) { + $details.toggleClass("open"); + } + + $.fn.details = function() { + if (hasNativeSupport) { return this; } + + return this.each(function() { + var $details = $(this), + $firstSummary = $("summary", $details).first(); + + $firstSummary.prop("tabIndex", 0); + + $firstSummary.on("keydown", function(event) { + if (event.keyCode === 32 /* SPACE */ || event.keyCode === 13 /* ENTER */) { + toggleOpen($details); + return false; + } + }); + + $firstSummary.on("click", function() { + $firstSummary.focus(); + toggleOpen($details); + }); + + }); + }; + +})(document, jQuery); diff --git a/plugins/discourse-details/assets/javascripts/details_dialect.js b/plugins/discourse-details/assets/javascripts/details_dialect.js new file mode 100644 index 000000000..4c80824d4 --- /dev/null +++ b/plugins/discourse-details/assets/javascripts/details_dialect.js @@ -0,0 +1,26 @@ +(function() { + + function insertDetails(_, summary, details) { + return "
" + summary + "" + details + "
"; + } + + // replace all [details] BBCode with HTML 5.1 equivalent + function replaceDetails(text) { + text = text || ""; + + while (text !== (text = text.replace(/\[details=([^\]]+)\]((?:(?!\[details=[^\]]+\]|\[\/details\])[\S\s])*)\[\/details\]/ig, insertDetails))); + + // add new lines to make sure we *always* have a

element after and around

+ // otherwise we can't hide the content since we can't target text nodes via CSS + return text.replace(/<\/summary>/ig, "\n\n") + .replace(/<\/details>/ig, "\n\n\n\n"); + } + + Discourse.Dialect.addPreProcessor(function(text) { + if (Discourse.SiteSettings.details_enabled) { + text = replaceDetails(text); + } + return text; + }); + +})(); diff --git a/plugins/discourse-details/assets/javascripts/initializers/apply-details.js.es6 b/plugins/discourse-details/assets/javascripts/initializers/apply-details.js.es6 new file mode 100644 index 000000000..9813a1d6e --- /dev/null +++ b/plugins/discourse-details/assets/javascripts/initializers/apply-details.js.es6 @@ -0,0 +1,10 @@ +import { decorateCooked } from "discourse/lib/plugin-api"; + +export default { + name: "apply-details", + + initialize(container) { + decorateCooked(container, $elem => $("details", $elem).details()); + } + +}; diff --git a/plugins/discourse-details/assets/stylesheets/details.scss b/plugins/discourse-details/assets/stylesheets/details.scss new file mode 100644 index 000000000..f1de007d2 --- /dev/null +++ b/plugins/discourse-details/assets/stylesheets/details.scss @@ -0,0 +1,38 @@ +details { + position: relative; +} + +details > *, +details .lightbox-wrapper { + display: none; +} + +summary:first-of-type { + cursor: pointer; + display: block; +} + +summary:before { + content: '\25BA'; + margin-right: .25em; +} + +details[open] > summary:before, +details.open > summary:before { + content: '\25BC'; +} + +details[open] > summary:first-of-type ~ *, +details.open > summary:first-of-type ~ * { + display: block; +} + +/* hide native indicator */ +summary::-webkit-details-marker { + display: none; +} + +/* FF: hide div generated by lazyYT plugin */ +details .lazyYT-container { + display: none; +} diff --git a/plugins/discourse-details/config/locales/server.en.yml b/plugins/discourse-details/config/locales/server.en.yml new file mode 100644 index 000000000..cca3dd701 --- /dev/null +++ b/plugins/discourse-details/config/locales/server.en.yml @@ -0,0 +1,3 @@ +en: + site_settings: + details_enabled: "Enable the details plugin. If you change this, you must rebake all posts with: \"rake posts:rebake\"." diff --git a/plugins/discourse-details/config/settings.yml b/plugins/discourse-details/config/settings.yml new file mode 100644 index 000000000..9add46e43 --- /dev/null +++ b/plugins/discourse-details/config/settings.yml @@ -0,0 +1,4 @@ +plugins: + details_enabled: + default: true + client: true diff --git a/plugins/discourse-details/plugin.rb b/plugins/discourse-details/plugin.rb new file mode 100644 index 000000000..c028cf305 --- /dev/null +++ b/plugins/discourse-details/plugin.rb @@ -0,0 +1,27 @@ +# name: discourse-details +# about: HTML5.1 Details polyfill for Discourse +# version: 0.3 +# authors: RĂ©gis Hanol +# url: https://github.com/discourse/discourse/tree/master/plugins/discourse-details + +enabled_site_setting :details_enabled + +register_asset "javascripts/details.js" +register_asset "javascripts/details_dialect.js", :server_side + +register_asset "stylesheets/details.scss" + +after_initialize do + + # replace all details with their summary in emails + Email::Styles.register_plugin_style do |fragment| + if SiteSetting.details_enabled + fragment.css("details").each do |details| + summary = details.css("summary")[0] + summary.name = "p" + details.replace(summary) + end + end + end + +end