diff --git a/src/common/text/elements/link.js b/src/common/text/elements/link.js new file mode 100644 index 0000000000000000000000000000000000000000..03315bb2c04ed8bfbf8ea04a08cfc700a73e830c --- /dev/null +++ b/src/common/text/elements/link.js @@ -0,0 +1,19 @@ +/** + * Link + */ + +module.exports = text => { + const match = text.match(/^\??\[(.+?)\]\((https?:\/\/[\w\/:%#@\$&\?!\(\)\[\]~\.=\+\-]+)\)/); + if (!match) return null; + const silent = text[0] == '?'; + const link = match[0]; + const title = match[1]; + const url = match[2]; + return { + type: 'link', + content: link, + title: title, + url: url, + silent: silent + }; +}; diff --git a/src/common/text/elements/url.js b/src/common/text/elements/url.js index f350b707acb84b4209f85638008277ed139c8e05..1003aff9c3bc6bec3806f97415aa6e6724310223 100644 --- a/src/common/text/elements/url.js +++ b/src/common/text/elements/url.js @@ -3,11 +3,12 @@ */ module.exports = text => { - const match = text.match(/^https?:\/\/[\w\/:%#@\$&\?!\(\)\[\]~\.=\+\-]+/); + const match = text.match(/^https?:\/\/[\w\/:%#@\$&\?!\(\)\[\]~\.=\+\-]+/); if (!match) return null; - const link = match[0]; + const url = match[0]; return { - type: 'link', - content: link + type: 'url', + content: url, + url: url }; }; diff --git a/src/common/text/index.js b/src/common/text/index.js index 44241690336a5f0f6f6c136b5403a0609eb1d320..ab1342230c56c86abb0503678126f4ef2ba79e90 100644 --- a/src/common/text/index.js +++ b/src/common/text/index.js @@ -5,6 +5,7 @@ const elements = [ require('./elements/bold'), require('./elements/url'), + require('./elements/link'), require('./elements/mention'), require('./elements/hashtag'), require('./elements/code'), diff --git a/src/web/app/common/scripts/text-compiler.js b/src/web/app/common/scripts/text-compiler.js index 2edffbb7664d780421ecd0fb45283f9f20500687..d4570ca923936f7f1d44aa6670b1d1f00fe1ded0 100644 --- a/src/web/app/common/scripts/text-compiler.js +++ b/src/web/app/common/scripts/text-compiler.js @@ -21,8 +21,10 @@ module.exports = (tokens, shouldBreak) => { .replace(/(\r\n|\n|\r)/g, shouldBreak ? '<br>' : ' '); case 'bold': return '<strong>' + escape(token.bold) + '</strong>'; - case 'link': + case 'url': return '<mk-url href="' + escape(token.content) + '" target="_blank"></mk-url>'; + case 'link': + return '<a class="link" href="' + escape(token.url) + '" target="_blank">' + escape(token.title) + '</a>'; case 'mention': return '<a href="' + CONFIG.url + '/' + escape(token.username) + '" target="_blank" data-user-preview="' + token.content + '" ' + (me && me.username == token.username ? 'data-is-me' : '') + '>' + token.content + '</a>'; case 'hashtag': // TODO diff --git a/src/web/app/desktop/tags/post-detail.tag b/src/web/app/desktop/tags/post-detail.tag index 69379317fcd0a0b3b39332e3ada6d22afdc8e880..ca2d59f2e2c315d4b09c901037dd15d7f4518cfe 100644 --- a/src/web/app/desktop/tags/post-detail.tag +++ b/src/web/app/desktop/tags/post-detail.tag @@ -233,6 +233,16 @@ font-size 1.5em color #717171 + .link + &:after + content "\f14c" + display inline-block + padding-left 2px + font-family FontAwesome + font-size .9em + font-weight 400 + font-style normal + > mk-url-preview margin-top 8px @@ -367,10 +377,10 @@ // URLをプレビュー tokens - .filter(t => t.type == 'link') + .filter(t => (t.type == 'url' || t.type == 'link') && !t.silent) .map(t => { riot.mount(this.refs.text.appendChild(document.createElement('mk-url-preview')), { - url: t.content + url: t.url }); }); } diff --git a/src/web/app/desktop/tags/timeline-post.tag b/src/web/app/desktop/tags/timeline-post.tag index 9a1dede2dad6142c00fa7d6e8f6ac6eb08d0aacc..8929a8dd5ae5efc7d6d62d7b5e2be05cb40dcf43 100644 --- a/src/web/app/desktop/tags/timeline-post.tag +++ b/src/web/app/desktop/tags/timeline-post.tag @@ -229,6 +229,16 @@ mk-url-preview margin-top 8px + .link + &:after + content "\f14c" + display inline-block + padding-left 2px + font-family FontAwesome + font-size .9em + font-weight 400 + font-style normal + > .reply margin-right 8px color #717171 @@ -344,10 +354,10 @@ // URLをプレビュー tokens - .filter(t => t.type == 'link') + .filter(t => (t.type == 'url' || t.type == 'link') && !t.silent) .map(t => { riot.mount(this.refs.text.appendChild(document.createElement('mk-url-preview')), { - url: t.content + url: t.url }); }); } diff --git a/src/web/app/mobile/tags/post-detail.tag b/src/web/app/mobile/tags/post-detail.tag index b32fe6173d654bbd15d67cbb6f8f968f9c717067..f6f6bb62dbb7665666a3617dcdc26f2bf8d12b01 100644 --- a/src/web/app/mobile/tags/post-detail.tag +++ b/src/web/app/mobile/tags/post-detail.tag @@ -230,6 +230,16 @@ @media (min-width 500px) font-size 24px + .link + &:after + content "\f14c" + display inline-block + padding-left 2px + font-family FontAwesome + font-size .9em + font-weight 400 + font-style normal + > mk-url-preview margin-top 8px @@ -368,10 +378,10 @@ // URLをプレビュー tokens - .filter(t => t.type == 'link') + .filter(t => (t.type == 'url' || t.type == 'link') && !t.silent) .map(t => { riot.mount(this.refs.text.appendChild(document.createElement('mk-url-preview')), { - url: t.content + url: t.url }); }); } diff --git a/src/web/app/mobile/tags/timeline-post.tag b/src/web/app/mobile/tags/timeline-post.tag index 9da25b7b2f1dfb7576de2ae5fe97f2314712072d..f706dc7de64f395e1955ebe277a63d6a4438de99 100644 --- a/src/web/app/mobile/tags/timeline-post.tag +++ b/src/web/app/mobile/tags/timeline-post.tag @@ -209,6 +209,16 @@ > .dummy display none + .link + &:after + content "\f14c" + display inline-block + padding-left 2px + font-family FontAwesome + font-size .9em + font-weight 400 + font-style normal + mk-url-preview margin-top 8px @@ -318,10 +328,10 @@ // URLをプレビュー tokens - .filter(t => t.type == 'link') + .filter(t => (t.type == 'url' || t.type == 'link') && !t.silent) .map(t => { riot.mount(this.refs.text.appendChild(document.createElement('mk-url-preview')), { - url: t.content + url: t.url }); }); } diff --git a/test/text.js b/test/text.js index e2527cfe0b097ba2786e4ca6632aeec8351e891e..dd4521c835927a235617fcf4552e9c747e8f4677 100644 --- a/test/text.js +++ b/test/text.js @@ -49,11 +49,23 @@ describe('Text', () => { ], tokens); }); - it('link', () => { + it('url', () => { const tokens = analyze('https://himasaku.net'); - assert.deepEqual([ - { type: 'link', content: 'https://himasaku.net' } - ], tokens); + assert.deepEqual([{ + type: 'url', + content: 'https://himasaku.net', + url: 'https://himasaku.net' + }], tokens); + }); + + it('link', () => { + const tokens = analyze('[ã²ã¾ã•ã](https://himasaku.net)'); + assert.deepEqual([{ + type: 'link', + content: '[ã²ã¾ã•ã](https://himasaku.net)', + title: 'ã²ã¾ã•ã', + url: 'https://himasaku.net' + }], tokens); }); it('emoji', () => {