Chat launch page for BareRTC

This commit is contained in:
Noah Petherbridge 2023-02-05 20:26:36 -08:00
parent 811650214e
commit 050ce19160
17 changed files with 929 additions and 51 deletions

1
go.mod
View File

@ -21,6 +21,7 @@ require (
github.com/disintegration/imaging v1.6.2 // indirect
github.com/edwvee/exiffix v0.0.0-20210922235313-0f6cbda5e58f // indirect
github.com/go-redis/redis v6.15.9+incompatible // indirect
github.com/golang-jwt/jwt/v4 v4.4.3 // indirect
github.com/gorilla/css v1.0.0 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.12.1 // indirect

2
go.sum
View File

@ -28,6 +28,8 @@ github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang-jwt/jwt/v4 v4.4.3 h1:Hxl6lhQFj4AnOX6MLrsCb/+7tCj7DxP7VA+2rDIq5AU=
github.com/golang-jwt/jwt/v4 v4.4.3/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=

View File

@ -22,6 +22,7 @@ type Variable struct {
Mail Mail
Redis Redis
Database Database
BareRTC BareRTC
UseXForwardedFor bool
}
@ -107,3 +108,9 @@ type Database struct {
SQLite string
Postgres string
}
// BareRTC chat room settings.
type BareRTC struct {
JWTSecret string
URL string
}

View File

@ -0,0 +1,89 @@
package chat
import (
"fmt"
"net/http"
"strings"
"time"
"code.nonshy.com/nonshy/website/pkg/config"
"code.nonshy.com/nonshy/website/pkg/photo"
"code.nonshy.com/nonshy/website/pkg/session"
"code.nonshy.com/nonshy/website/pkg/templates"
"github.com/golang-jwt/jwt/v4"
)
// JWT claims.
type Claims struct {
// Custom claims.
IsAdmin bool `json:"op"`
Avatar string `json:"img"`
ProfileURL string `json:"url"`
// Standard claims. Notes:
// subject = username
jwt.RegisteredClaims
}
// Landing page for chat rooms.
func Landing() http.HandlerFunc {
tmpl := templates.Must("chat.html")
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Get the current user.
currentUser, err := session.CurrentUser(r)
if err != nil {
session.FlashError(w, r, "Couldn't get current user: %s", err)
templates.Redirect(w, "/")
return
}
// Are they logging into the chat room?
var intent = r.FormValue("intent")
if intent == "join" {
// Get our Chat JWT secret.
var (
secret = []byte(config.Current.BareRTC.JWTSecret)
chatURL = config.Current.BareRTC.URL
)
if len(secret) == 0 || chatURL == "" {
session.FlashError(w, r, "Couldn't sign you into the chat: JWT secret key or chat URL not configured!")
templates.Redirect(w, r.URL.Path)
return
}
// Create the JWT claims.
claims := Claims{
IsAdmin: currentUser.IsAdmin,
Avatar: photo.URLPath(currentUser.ProfilePhoto.CroppedFilename),
ProfileURL: "/u/" + currentUser.Username,
RegisteredClaims: jwt.RegisteredClaims{
// TODO: ExpiresAt 60 minutes to work around chat server reliability,
// should be shorter like 5 minutes.
ExpiresAt: jwt.NewNumericDate(time.Now().Add(60 * time.Minute)),
IssuedAt: jwt.NewNumericDate(time.Now()),
NotBefore: jwt.NewNumericDate(time.Now()),
Issuer: config.Title,
Subject: currentUser.Username,
ID: fmt.Sprintf("%d", currentUser.ID),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
ss, err := token.SignedString(secret)
if err != nil {
session.FlashError(w, r, "Couldn't sign you into the chat: %s", err)
templates.Redirect(w, r.URL.Path)
return
}
// Redirect them to the chat room.
templates.Redirect(w, strings.TrimSuffix(chatURL, "/")+"/?jwt="+ss)
return
}
var vars = map[string]interface{}{}
if err := tmpl.Execute(w, r, vars); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
}

View File

@ -6,10 +6,11 @@ import (
"code.nonshy.com/nonshy/website/pkg/templates"
)
// Static pages.
func About() http.HandlerFunc {
tmpl := templates.Must("about.html")
// StaticTemplate creates a simple controller that loads a Go html/template
// such as "about.html" relative to the web/templates path.
func StaticTemplate(filename string) func() http.HandlerFunc {
return func() http.HandlerFunc {
tmpl := templates.Must(filename)
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if err := tmpl.Execute(w, r, nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
@ -17,33 +18,4 @@ func About() http.HandlerFunc {
}
})
}
func FAQ() http.HandlerFunc {
tmpl := templates.Must("faq.html")
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if err := tmpl.Execute(w, r, nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
}
func TOS() http.HandlerFunc {
tmpl := templates.Must("tos.html")
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if err := tmpl.Execute(w, r, nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
}
func Privacy() http.HandlerFunc {
tmpl := templates.Must("privacy.html")
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if err := tmpl.Execute(w, r, nil); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
})
}

View File

@ -9,6 +9,7 @@ import (
"code.nonshy.com/nonshy/website/pkg/controller/admin"
"code.nonshy.com/nonshy/website/pkg/controller/api"
"code.nonshy.com/nonshy/website/pkg/controller/block"
"code.nonshy.com/nonshy/website/pkg/controller/chat"
"code.nonshy.com/nonshy/website/pkg/controller/comment"
"code.nonshy.com/nonshy/website/pkg/controller/forum"
"code.nonshy.com/nonshy/website/pkg/controller/friend"
@ -25,16 +26,17 @@ func New() http.Handler {
// Register controller endpoints.
mux.HandleFunc("/", index.Create())
mux.HandleFunc("/favicon.ico", index.Favicon())
mux.HandleFunc("/about", index.About())
mux.HandleFunc("/faq", index.FAQ())
mux.HandleFunc("/tos", index.TOS())
mux.HandleFunc("/privacy", index.Privacy())
mux.HandleFunc("/about", index.StaticTemplate("about.html")())
mux.HandleFunc("/faq", index.StaticTemplate("faq.html")())
mux.HandleFunc("/tos", index.StaticTemplate("tos.html")())
mux.HandleFunc("/privacy", index.StaticTemplate("privacy.html")())
mux.HandleFunc("/contact", index.Contact())
mux.HandleFunc("/login", account.Login())
mux.HandleFunc("/logout", account.Logout())
mux.HandleFunc("/signup", account.Signup())
mux.HandleFunc("/forgot-password", account.ForgotPassword())
mux.HandleFunc("/settings/confirm-email", account.ConfirmEmailChange())
mux.HandleFunc("/markdown", index.StaticTemplate("markdown.html")())
// Login Required. Pages that non-certified users can access.
mux.Handle("/me", middleware.LoginRequired(account.Dashboard()))
@ -64,6 +66,7 @@ func New() http.Handler {
// Certification Required. Pages that only full (verified) members can access.
mux.Handle("/photo/gallery", middleware.CertRequired(photo.SiteGallery()))
mux.Handle("/members", middleware.CertRequired(account.Search()))
mux.Handle("/chat", middleware.CertRequired(chat.Landing()))
mux.Handle("/forum", middleware.CertRequired(forum.Landing()))
mux.Handle("/forum/post", middleware.CertRequired(forum.NewPost()))
mux.Handle("/forum/thread/", middleware.CertRequired(forum.Thread()))

View File

@ -173,7 +173,7 @@
name="about_me"
placeholder="A little blurb about myself">{{$User.GetProfileField "about_me"}}</textarea>
<p class="help">
Write a bit about yourself. Markdown formatting is supported here.
Write a bit about yourself. <a href="/markdown" target="_blank">Markdown formatting</a> is supported here.
</p>
</div>

View File

@ -49,6 +49,11 @@
<span>Home</span>
</a>
<a class="navbar-item" href="/chat">
<span class="icon"><i class="fa fa-message"></i></span>
<span>Chat</span>
</a>
<a class="navbar-item" href="/forum">
<span class="icon"><i class="fa fa-comments"></i></span>
<span>Forum</span>

64
web/templates/chat.html Normal file
View File

@ -0,0 +1,64 @@
{{define "title"}}Chat Rooms{{end}}
{{define "content"}}
<div class="block">
<section class="hero is-light is-bold">
<div class="hero-body">
<div class="container">
<h1 class="title">
<i class="fa fa-message mr-2"></i>
Chat Rooms (Beta)
<span class="tag is-success">NEW!</span>
</h1>
</div>
</div>
</section>
</div>
<div class="block p-4">
<div class="content">
<p>
{{PrettyTitle}} has a new chat room! Come and check it out. It features some public rooms, direct
messages, and optional webcam support too. You may broadcast your video and other users may click
to watch yours, and you can open one or multiple videos broadcasted by the other chatters.
</p>
<p>
This chat room is currently ready for <strong>beta</strong> testing. It's a very new app built
specifically for {{PrettyTitle}} and may still be lacking in some features and may be rough around
the edges. Give it a try and <a href="/contact">send feedback</a> if you run into any technical
issues with the chat room!
</p>
<h3>Chat Room Rules</h3>
<p>
Please observe the following rules for the chat room.
</p>
<ul>
<li>
For now, <strong>keep your webcam content non-sexual.</strong> In the future there will be an
ability to mark your webcam feed as Explicit or there will be a NSFW channel dedicated to that,
but for now all the webcams are in the same place and users may not want to see you being naughty
on camera if they click into your video.
</li>
<li>
Be nice and respectful. <a href="/tos">Global website rules</a> apply to the chat room as well!
</li>
</ul>
<p>
Click on the button below to join the chat room:
</p>
<p>
<a href="/chat?intent=join" target="_blank"
class="button is-large is-link">
Join the Chat Room Now
<i class="fa fa-external-link ml-3"></i>
</a>
</p>
</div>
</div>
{{end}}

View File

@ -60,7 +60,7 @@
required
placeholder="Message">{{.Message}}</textarea>
<p class="help">
Markdown formatting supported.
<a href="/markdown" target="_blank">Markdown formatting</a> supported.
</p>
</div>

View File

@ -62,7 +62,7 @@
name="description" id="description"
placeholder="A short description of the forum.">{{if .EditForum}}{{.EditForum.Description}}{{end}}</textarea>
<p class="help">
Write a short description of the forum. Markdown formatting
Write a short description of the forum. <a href="/markdown" target="_blank">Markdown formatting</a>
is supported here.
</p>
</div>

View File

@ -77,7 +77,7 @@
{{if not .Forum.PermitPhotos}}required{{end}}
placeholder="Message">{{.Message}}</textarea>
<p class="help">
Markdown formatting supported.
<a href="/markdown" target="_blank">Markdown formatting</a> supported.
</p>
</div>

View File

@ -342,7 +342,7 @@
{{if not .Forum.PermitPhotos}}required{{end}}
placeholder="Message"></textarea>
<p class="help">
Markdown formatting supported.
<a href="/markdown" target="_blank">Markdown formatting</a> supported.
</p>
</div>

View File

@ -49,7 +49,7 @@
required
placeholder="Message"></textarea>
<p class="help">
Markdown formatting supported.
<a href="/markdown" target="_blank">Markdown formatting</a> supported.
</p>
</div>

View File

@ -37,6 +37,9 @@
<textarea class="textarea block" cols="80" rows="4"
name="message"
placeholder="Write a response"></textarea>
<p class="help">
<a href="/markdown" target="_blank">Markdown formatting</a> supported.
</p>
<div class="columns is-mobile">
<div class="column">

732
web/templates/markdown.html Normal file
View File

@ -0,0 +1,732 @@
{{ define "title" }}Markdown Cheatsheet{{ end }}
{{ define "content" }}
<div class="block">
<section class="hero is-light is-bold">
<div class="hero-body">
<div class="container">
<h1 class="title">
<i class="fa-brands fa-markdown mr-2"></i>
Markdown Cheatsheet
</h1>
</div>
</div>
</section>
</div>
<div class="block p-4 content">
<p>
<strong>Markdown</strong> syntax can enable you to be more expressive with your text posts on this
website, with the ability to write <strong>**bold**</strong> text, add <a href="#">hyperlinks</a> or
embed images onto your profile page, Direct Messages, forum posts and elsewhere - any place on
{{PrettyTitle}} that says "Markdown formatting supported."
</p>
<p>
This is a simple reference sheet for Markdown syntax. Markdown was pioneered by John Gruber and the
de facto place to find in-depth documentation is at
<a href="https://daringfireball.net/projects/markdown/syntax">https://daringfireball.net/projects/markdown/syntax</a>
where it was originally described.
</p>
<p>
Markdown is now a widely supported format across a variety of apps and websites, and there are a
few different "flavors" of Markdown with slightly varied behaviors. This website uses
<a href="https://github.github.com/gfm/">GitHub Flavored Markdown</a>, an extension of Markdown
that supports fenced code blocks, tables, and other useful features - many of which you can learn
about on this page.
</p>
<ul>
<li>
<a href="#block">Block Elements</a>
<ul>
<li><a href="#pbr">Paragraphs and Line Breaks</a></li>
<li><a href="#h1">Headers</a></li>
<li><a href="#bq">Blockquotes</a></li>
<li><a href="#ul">Lists</a></li>
<li><a href="#pre">Code Blocks</a></li>
<li><a href="#hr">Horizontal Rules</a></li>
</ul>
</li>
<li>
<a href="#span">Span Elements</a>
<ul>
<li><a href="#a">Links</a></li>
<li><a href="#em">Emphasis</a></li>
<li><a href="#code">Code</a></li>
<li><a href="#img">Images</a></li>
</ul>
</li>
<li>
<a href="#misc">Miscellaneous</a>
<ul>
<li><a href="#autolink">Automatic Links</a></li>
<li><a href="#escape">Backslash Escapes</a></li>
</ul>
</li>
</ul>
<h1 id="block">Block Elements</h1>
<a name="pbr"></a>
<h2>Paragraphs and Line Breaks</h2>
<p>A paragraph is defined as a group of lines of text separated from other groups
by at least one blank line. A hard return inside a paragraph doesn't get rendered
in the output.</p>
<h2 id="h1">Headers</h2>
<p>There are two methods to declare a header in Markdown: "underline" it by
writing <code>===</code> or <code>---</code> on the line directly below the
heading (for <code>&lt;h1&gt;</code> and <code>&lt;h2&gt;</code>, respectively),
or by prefixing the heading with <code>#</code> symbols. Usually the latter
option is the easiest, and you can get more levels of headers this way.</p>
<table width="100%">
<thead>
<tr>
<th width="50%">Markdown Syntax</th>
<th width="50%">Output</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<pre>This is an H1
=============
This is an H2
-------------</pre>
</td>
<td>
<h1>This is an H1</h1>
<h2>This is an H2</h2>
</td>
</tr>
<tr>
<td>
<pre># This is an H1
## This is an H2
#### This is an H4</pre>
</td>
<td>
<h1>This is an H1</h1>
<h2>This is an H2</h2>
<h4>This is an H4</h4>
</td>
</tr>
</tbody>
</table>
<h3 id="bq">Blockquotes</h3>
<p>Prefix a line of text with <code>&gt;</code> to "quote" it -- like in
"e-mail syntax."</p>
<p>You may have multiple layers of quotes by using multiple <code>&gt;</code>
symbols.</p>
<table width="100%">
<thead>
<tr>
<th width="50%">Markdown Syntax</th>
<th width="50%">Output</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<pre>&gt; This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
&gt; consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
&gt; Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
&gt;
&gt; Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
&gt; id sem consectetuer libero luctus adipiscing.
</pre>
</td>
<td>
<blockquote>
<p>This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.</p>
<p>Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
id sem consectetuer libero luctus adipiscing.</p>
</blockquote>
</td>
</tr>
<tr>
<td>
<code>
&gt; This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,<br>
consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.<br>
Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.<br><br>
&gt; Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse<br>
id sem consectetuer libero luctus adipiscing.
</code>
</td>
<td>
<blockquote>
<p>This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.<p>
<p>Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
id sem consectetuer libero luctus adipiscing.</p>
</blockquote>
</td>
</tr>
<tr>
<td>
<code>
&gt; This is the first level of quoting.<br>
&gt;<br>
&gt;&gt; This is nested blockquote.<br>
&gt;&gt;&gt; A third level.<br>
&gt;<br>
&gt; Back to the first level.
</code>
</td>
<td>
<blockquote>
This is the first level of quoting.
<blockquote>
This is nested blockquote.
<blockquote>
A third level.
</blockquote>
</blockquote>
Back to the first level.
</blockquote>
</td>
</tr>
<tr>
<td>
<code>
&gt; ## This is a header.<br>
&gt;<br>
&gt; 1. This is the first list item.<br>
&gt; 2. This is the second list item.<br>
&gt;<br>
&gt;Here's some example code:<br>
&gt;<br>
&gt;&nbsp;&nbsp;&nbsp;&nbsp;return shell_exec("echo $input | $markdown_script");
</code>
</td>
<td>
<blockquote>
<h2>This is a header.</h2>
<ol>
<li>This is the first list item.</li>
<li>This is the second list item.</li>
</ol>
Here's some example code:
<pre>return shell_exec("echo $input | $markdown_script");</pre>
</blockquote>
</td>
</tr>
</tbody>
</table>
<a name="ul"></a>
<h2>Lists</h2>
<table width="100%" class="table">
<thead>
<tr>
<th width="50%">Markdown Syntax</th>
<th width="50%">Output</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>
* Red<br>
* Green<br>
* Blue
</code>
</td>
<td>
<ul>
<li>Red</li>
<li>Green</li>
<li>Blue</li>
</ul>
</td>
</tr>
<tr>
<td>
<code>
+ Red<br>
+ Green<br>
+ Blue
</code>
</td>
<td>
<ul>
<li>Red</li>
<li>Green</li>
<li>Blue</li>
</ul>
</td>
</tr>
<tr>
<td>
<code>
- Red<br>
- Green<br>
- Blue
</code>
</td>
<td>
<ul>
<li>Red</li>
<li>Green</li>
<li>Blue</li>
</ul>
</td>
</tr>
<tr>
<td>
<code>
1. Bird<br>
2. McHale<br>
3. Parish
</code>
</td>
<td>
<ol>
<li>Bird</li>
<li>McHale</li>
<li>Parish</li>
</ol>
</td>
</tr>
<tr>
<td>
<code>
1.&nbsp;&nbsp;This is a list item with two paragraphs. Lorem ipsum dolor<br>
&nbsp;&nbsp;&nbsp;&nbsp;sit amet, consectetuer adipiscing elit. Aliquam hendrerit<br>
&nbsp;&nbsp;&nbsp;&nbsp;mi posuere lectus.<p>
&nbsp;&nbsp;&nbsp;&nbsp;Vestibulum enim wisi, viverra nec, fringilla in, laoreet<br>
&nbsp;&nbsp;&nbsp;&nbsp;vitae, risus. Donec sit amet nisl. Aliquam semper ipsum<br>
&nbsp;&nbsp;&nbsp;&nbsp;sit amet velit.<p>
2.&nbsp;&nbsp;Suspendisse id sem consectetuer libero luctus adipiscing.
</code>
</td>
<td>
<ol>
<li>This is a list item with two paragraphs. Lorem ipsum dolor
sit amet, consectetuer adipiscing elit. Aliquam hendrerit
mi posuere lectus.<p>
Vestibulum enim wisi, viverra nec, fringilla in, laoreet
vitae, risus. Donec sit amet nisl. Aliquam semper ipsum
sit amet velit.</li>
<li>Suspendisse id sem consectetuer libero luctus adipiscing.</li>
</ol>
</td>
</tr>
</tbody>
</table>
<a name="pre"></a>
<h2>Code Blocks</h2>
The typical Markdown way to write a code block is to indent each line of a paragraph with at
least 4 spaces or 1 tab character. The Rophako CMS also uses GitHub-style code blocks, where
you can use three backticks before and after the code block and then you don't need to indent
each line of the code (makes copying/pasting easier!)<p>
Like GitHub-flavored Markdown, with a fenced code block you can also specify a programming
language to get syntax highlighting for the code.<p>
<table width="100%" class="table">
<thead>
<tr>
<th width="50%">Markdown Syntax</th>
<th width="50%">Output</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>
This is a normal paragraph.<p>
&nbsp;&nbsp;&nbsp;&nbsp;This is a code block.
</code>
</td>
<td>
This is a normal paragraph.<p>
<pre>This is a code block</pre>
</td>
</tr>
<tr>
<td>
<code>
This is a normal paragraph.<p>
```<br>
This is a GitHub style "fenced code block".<br>
```
</code>
</td>
<td>
This is a normal paragraph.<p>
<pre>This is a GitHub style "fenced code block".</pre>
</td>
</tr>
<tr>
<td>
<code>
```javascript<br>
document.writeln("Hello world.");<br>
```
</code>
</td>
<td>
<div class="codehilite"><pre><span class="nb">document</span><span class="p">.</span><span class="nx">writeln</span><span class="p">(</span><span class="s2">&quot;Hello world.&quot;</span><span class="p">);</span></pre></div>
</td>
</tr>
</tbody>
</table>
<a name="hr"></a>
<h2>Horizontal Rules</h2>
<table width="100%" class="table">
<thead>
<tr>
<th width="50%">Markdown Syntax</th>
<th width="50%">Output</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>
* * *<p>
***<p>
*****<p>
- - -<p>
---------------------------
</code>
</td>
<td>
<hr><p>
<hr><p>
<hr><p>
<hr><p>
<hr>
</td>
</tr>
</tbody>
</table>
<a name="span"></a>
<h1>Span Elements</h1>
<a name="a"></a>
<h2>Links</h2>
<table width="100%" class="table">
<thead>
<tr>
<th width="50%">Markdown Syntax</th>
<th width="50%">Output</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>
This is [an example](http://example.com/ "Title") inline link.<p>
[This link](http://example.net/) has no title attribute.
</code>
</td>
<td>
This is <a href="http://example.com/" title="Title">an example</a> inline link.<p>
<a href="http://example.net/">This link</a> has no title attribute.
</td>
</tr>
<tr>
<td>
<code>
See my [About](/about) page for details.
</code>
</td>
<td>
See my <a href="/about">About</a> page for details.
</td>
</tr>
<tr>
<td>
<code>
This is [an example][id] reference-style link.<p>
[id]: http://example.com/ "Optional Title Here"
</code>
</td>
<td>
This is <a href="http://example.com/" title="Optional Title Here">an example</a> reference-style link.
</td>
</tr>
<tr>
<td>
<code>
This is an example of an implicit reference-style link: search [Google][] for more.<p>
[Google]: http://google.com/
</code>
</td>
<td>
This is an example of an implicit reference-style link: search <a href="http://google.com/">Google</a> for more.
</td>
</tr>
<tr>
<td>
<code>
I get 10 times more traffic from [Google] [1] than from<br>
[Yahoo] [2] or [Bing] [3].<p>
[1]: http://google.com/ "Google"<br>
[2]: http://search.yahoo.com/ "Yahoo Search"<br>
[3]: http://bing.com/ "Bing"
</code>
</td>
<td>
I get 10 times more traffic from <a href="http://google.com/" title="Google">Google</a> than from
<a href="http://search.yahoo.com/" title="Yahoo Search">Yahoo</a> or
<a href="http://bing.com/" title="Bing">Bing</a>.
</td>
</tr>
</tbody>
</table>
<a name="em"></a>
<h2>Emphasis</h2>
<table width="100%" class="table">
<thead>
<tr>
<th width="50%">Markdown Syntax</th>
<th width="50%">Output</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>
*single asterisks*<p>
_single underscores_<p>
**double asterisks**<p>
__double underscores__
</code>
</td>
<td>
<em>single asterisks</em><p>
<em>single underscores</em><p>
<strong>double asterisks</strong><p>
<strong>double underscores</strong>
</td>
</tr>
<tr>
<td>
<code>
un*frigging*believable
</code>
</td>
<td>
un<em>frigging</em>believable
</td>
</tr>
<tr>
<td>
<code>
\*this text is surrounded by literal asterisks\*
</code>
</td>
<td>
*this text is surrounded by literal asterisks*
</td>
</tr>
</tbody>
</table>
<a name="code"></a>
<h2>Code</h2>
<table width="100%" class="table">
<thead>
<tr>
<th width="50%">Markdown Syntax</th>
<th width="50%">Output</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>
Use the `printf()` function.
</code>
</td>
<td>
Use the <code>printf()</code> function.
</td>
</tr>
<tr>
<td>
<code>
``There is a literal backtick (`) here.``
</code>
</td>
<td>
<code>There is a literal backtick (`) here.</code>
</td>
</tr>
<tr>
<td>
<code>
A single backtick in a code span: `` ` ``<p>
A backtick-delimited string in a code span: `` `foo` ``
</code>
</td>
<td>
A single backtick in a code span: <code>`</code><p>
A backtick-delimited string in a code span: <code>`foo`</code>
</td>
</tr>
<tr>
<td>
<code>Please don't use any `&lt;blink&gt;` tags.</code>
</td>
<td>
Please don't use any <code>&lt;blink&gt;</code> tags.
</td>
</tr>
<tr>
<td>
<code>`&amp;#8212;` is the decimal-encoded equivalent of `&amp;mdash;`.</code>
</td>
<td>
<code>&amp;#8212;</code> is the decimal-encoded equivalent of
<code>&amp;mdash;</code>.
</td>
</tr>
</tbody>
</table>
<a name="img"></a>
<h2>Images</h2>
<table width="100%" class="table">
<thead>
<tr>
<th width="50%">Markdown Syntax</th>
<th width="50%">Output</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>![Alt text](/static/avatars/default.png)</code>
</td>
<td>
<img src="/static/avatars/default.png" alt="Alt text">
</td>
</tr>
<tr>
<td>
<code>![Alt text](/static/avatars/default.png "Optional title")</code>
</td>
<td>
<img src="/static/avatars/default.png" alt="Alt text" title="Optional title">
</td>
</tr>
<tr>
<td>
<code>
![Alt text][id]<p>
[id]: /static/avatars/default.png "Optional title attribute"
</code>
</td>
<td>
<img src="/static/avatars/default.png" alt="Alt text" title="Optional title attribute">
</td>
</tr>
</tbody>
</table>
<a name="misc"></a>
<h1>Miscellaneous</h1>
<a name="autolink"></a>
<h2>Automatic Links</h2>
E-mail links get automatically converted into a random mess of HTML attributes to
attempt to thwart e-mail harvesting spam bots.<p>
<table width="100%" class="table">
<thead>
<tr>
<th width="50%">Markdown Syntax</th>
<th width="50%">Output</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<code>&lt;http://example.com/&gt;</code>
</td>
<td>
<a href="http://example.com/">http://example.com/</a>
</td>
</tr>
<tr>
<td>
<code>&lt;address@example.com&gt;</code>
</td>
<td>
<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#97;&#100;&#100;&#114;&#101;&#115;&#115;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;">&#97;&#100;&#100;&#114;&#101;&#115;&#115;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;</a><p>
(Source: <code>&lt;a href="&amp;#109;&amp;#97;&amp;#105;&amp;#108;&amp;#116;&amp;#111;&amp;#58; &amp;#97;&amp;#100;&amp;#100;&amp;#114;&amp;#101;&amp;#115;&amp;#115;&amp;#64; &amp;#101;&amp;#120;&amp;#97;&amp;#109;&amp;#112;&amp;#108; &amp;#101;&amp;#46;&amp;#99;&amp;#111;&amp;#109;"&gt;&amp;#97; &amp;#100;&amp;#100;&amp;#114;&amp;#101;&amp;#115;&amp;#115; &amp;#64;&amp;#101;&amp;#120;&amp;#97;&amp;#109;&amp;#112; &amp;#108;&amp;#101;&amp;#46;&amp;#99;&amp;#111; &amp;#109;&lt;/a&gt;</code>)
</td>
</tr>
</tbody>
</table>
<a name="escape"></a>
<h2>Backslash Escapes</h2>
Use backslash characters to escape any other special characters in the Markdown syntax. For example,
<code>\*</code> to insert a literal asterisk so that it doesn't get mistaken for e.g. emphasized text,
a list item, etc.<p>
Markdown provides backslash escapes for the following characters:<p>
<pre>\ backslash
` backtick
* asterisk
_ underscore
{} curly braces
[] square brackets
() parenthesis
# hash mark
+ plus sign
- minus sign (hyphen)
. dot
! exclamation mark</pre>
</div><!-- /div class="content" -->
{{ end }}

View File

@ -158,7 +158,7 @@
placeholder="Add your comment"
required></textarea>
<p class="help">
Markdown formatting supported.
<a href="/markdown" target="_blank">Markdown formatting</a> supported.
</p>
</div>