From ad7e2f15c76d7b66623b6a7e5ca3953c383f8892 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 19 Jul 2016 17:05:45 -0400 Subject: [PATCH] Support linking to anchored headings in the first post --- .../javascripts/discourse/lib/url.js.es6 | 20 ++++++++++++++++--- .../discourse/routes/topic-from-params.js.es6 | 7 ++++++- .../pretty-text/white-lister.js.es6 | 12 +++++------ test/javascripts/lib/sanitizer-test.js.es6 | 13 +++++++++++- 4 files changed, 41 insertions(+), 11 deletions(-) diff --git a/app/assets/javascripts/discourse/lib/url.js.es6 b/app/assets/javascripts/discourse/lib/url.js.es6 index 091004578..495b2856b 100644 --- a/app/assets/javascripts/discourse/lib/url.js.es6 +++ b/app/assets/javascripts/discourse/lib/url.js.es6 @@ -16,16 +16,30 @@ const DiscourseURL = Ember.Object.extend({ // Jumps to a particular post in the stream jumpToPost(postNumber, opts) { + opts = opts || {}; const holderId = `#post_${postNumber}`; Em.run.schedule('afterRender', () => { - if (postNumber === 1) { + let elementId; + let holder; + + if (postNumber === 1 && !opts.anchor) { $(window).scrollTop(0); return; } - const lockon = new LockOn(holderId); - const holder = $(holderId); + if (opts.anchor) { + elementId = opts.anchor; + holder = $(elementId); + console.log(holder.length); + } + + if (!holder || holder.length === 0) { + elementId = holderId; + holder = $(elementId); + } + + const lockon = new LockOn(elementId); if (holder.length > 0 && opts && opts.skipIfOnScreen){ const elementTop = lockon.elementTop(); diff --git a/app/assets/javascripts/discourse/routes/topic-from-params.js.es6 b/app/assets/javascripts/discourse/routes/topic-from-params.js.es6 index 99ab6005d..7c96c1a26 100644 --- a/app/assets/javascripts/discourse/routes/topic-from-params.js.es6 +++ b/app/assets/javascripts/discourse/routes/topic-from-params.js.es6 @@ -40,7 +40,12 @@ export default Discourse.Route.extend({ Ember.run.scheduleOnce('afterRender', function() { self.appEvents.trigger('post:highlight', closest); }); - DiscourseURL.jumpToPost(closest); + + const opts = {}; + if (document.location.hash && document.location.hash.length) { + opts.anchor = document.location.hash; + } + DiscourseURL.jumpToPost(closest, opts); if (!Ember.isEmpty(topic.get('draft'))) { composerController.open({ diff --git a/app/assets/javascripts/pretty-text/white-lister.js.es6 b/app/assets/javascripts/pretty-text/white-lister.js.es6 index fc6376c3a..63bc3b742 100644 --- a/app/assets/javascripts/pretty-text/white-lister.js.es6 +++ b/app/assets/javascripts/pretty-text/white-lister.js.es6 @@ -137,12 +137,12 @@ whiteListFeature('default', [ 'img[height]', 'pre', 'hr', - 'h1', - 'h2', - 'h3', - 'h4', - 'h5', - 'h6', + 'h1[id]', + 'h2[id]', + 'h3[id]', + 'h4[id]', + 'h5[id]', + 'h6[id]', 'iframe', 'iframe[height]', 'iframe[width]', diff --git a/test/javascripts/lib/sanitizer-test.js.es6 b/test/javascripts/lib/sanitizer-test.js.es6 index 7da137725..49581fadc 100644 --- a/test/javascripts/lib/sanitizer-test.js.es6 +++ b/test/javascripts/lib/sanitizer-test.js.es6 @@ -50,7 +50,18 @@ test("sanitize", function() { cooked("it has been 1 day 0 days since our last test failure", "

it has been 1 day 0 days since our last test failure

"); }); -test("urlAllowed", function() { +test("ids on headings", () => { + const pt = new PrettyText(buildOptions({ siteSettings: {} })); + equal(pt.sanitize("

Test Heading

"), "

Test Heading

"); + equal(pt.sanitize(`

Test Heading

`), `

Test Heading

`); + equal(pt.sanitize(`

Test Heading

`), `

Test Heading

`); + equal(pt.sanitize(`

Test Heading

`), `

Test Heading

`); + equal(pt.sanitize(`

Test Heading

`), `

Test Heading

`); + equal(pt.sanitize(`
Test Heading
`), `
Test Heading
`); + equal(pt.sanitize(`
Test Heading
`), `
Test Heading
`); +}); + +test("urlAllowed", () => { const allowed = (url, msg) => equal(hrefAllowed(url), url, msg); allowed("/foo/bar.html", "allows relative urls");