Renaissance

_,,ad8888888888bba,_
                  ,ad88888I888888888888888ba,
                ,88888888I88888888888888888888a,
              ,d888888888I8888888888888888888888b,
             d88888PP"""" ""YY88888888888888888888b,
           ,d88"'__,,--------,,,,.;ZZZY8888888888888,
          ,8IIl'"                ;;l"ZZZIII8888888888,
         ,I88l;'                  ;lZZZZZ888III8888888,
       ,II88Zl;.                  ;llZZZZZ888888I888888,
      ,II888Zl;.                .;;;;;lllZZZ888888I8888b
     ,II8888Z;;                 `;;;;;''llZZ8888888I8888,
     II88888Z;'                        .;lZZZ8888888I888b
     II88888Z; _,aaa,      .,aaaaa,__.l;llZZZ88888888I888
     II88888IZZZZZZZZZ,  .ZZZZZZZZZZZZZZ;llZZ88888888I888,
     II88888IZZ<'(@@>Z|  |ZZZ<'(@@>ZZZZ;;llZZ888888888I88I
    ,II88888;   `""" ;|  |ZZ; `"""     ;;llZ8888888888I888
    II888888l            `;;          .;llZZ8888888888I888,
   ,II888888Z;           ;;;        .;;llZZZ8888888888I888I
   III888888Zl;    ..,   `;;       ,;;lllZZZ88888888888I888
   II88888888Z;;...;(_    _)      ,;;;llZZZZ88888888888I888,
   II88888888Zl;;;;;' `--'Z;.   .,;;;;llZZZZ88888888888I888b
   ]I888888888Z;;;;'   ";llllll;..;;;lllZZZZ88888888888I8888,
   II888888888Zl.;;"Y88bd888P";;,..;lllZZZZZ88888888888I8888I
   II8888888888Zl;.; `"PPP";;;,..;lllZZZZZZZ88888888888I88888
   II888888888888Zl;;. `;;;l;;;;lllZZZZZZZZW88888888888I88888
   `II8888888888888Zl;.    ,;;lllZZZZZZZZWMZ88888888888I88888
    II8888888888888888ZbaalllZZZZZZZZZWWMZZZ8888888888I888888,
    `II88888888888888888b"WWZZZZZWWWMMZZZZZZI888888888I888888b
     `II88888888888888888;ZZMMMMMMZZZZZZZZllI888888888I8888888
      `II8888888888888888 `;lZZZZZZZZZZZlllll888888888I8888888,
       II8888888888888888, `;lllZZZZllllll;;.Y88888888I8888888b,
      ,II8888888888888888b   .;;lllllll;;;.;..88888888I88888888b,
      II888888888888888PZI;.  .`;;;.;;;..; ...88888888I8888888888,
      II888888888888PZ;;';;.   ;. .;.  .;. .. Y8888888I88888888888b,
     ,II888888888PZ;;'                        `8888888I8888888888888b,
     II888888888'                              888888I8888888888888888
    ,II888888888                              ,888888I8888888888888888
   ,d88888888888                              d888888I8888888888ZZZZZZ
,ad888888888888I                              8888888I8888ZZZZZZZZZZZZ
888888888888888'                              888888IZZZZZZZZZZZZZZZZZ
8888888888P'8P'                               Y888ZZZZZZZZZZZZZZZZZZZZ
888888888,  "                                 ,ZZZZZZZZZZZZZZZZZZZZZZZ
8888888888,                                ,ZZZZZZZZZZZZZZZZZZZZZZZZZZ
888888888888a,      _                    ,ZZZZZZZZZZZZZZZZZZZZ88888888
888888888888888ba,_d'                  ,ZZZZZZZZZZZZZZZZZ8888888888888
8888888888888888888888bbbaaa,,,______,ZZZZZZZZZZZZZZZ88888888888888888
88888888888888888888888888888888888ZZZZZZZZZZZZZZZ88888888888888888888
8888888888888888888888888888888888ZZZZZZZZZZZZZZ8888888888888888888888
888888888888888888888888888888888ZZZZZZZZZZZZZZ88888888888888888888888
8888888888888888888888888888888ZZZZZZZZZZZZZZ8888888888888888888888888
88888888888888888888888888888ZZZZZZZZZZZZZZ888888888888888888888888888
8888888888888888888888888888ZZZZZZZZZZZZZZ88888888888888888 Normand  8
88888888888888888888888888ZZZZZZZZZZZZZZ8888888888888888888 Veilleux 8
8888888888888888888888888ZZZZZZZZZZZZZZ8888888888888888888888888888888
This commit is contained in:
Maxime Quandalle 2015-05-12 19:20:58 +02:00
commit 2dbea30842
128 changed files with 10521 additions and 0 deletions

View file

@ -0,0 +1,8 @@
Template.editor.events({
// Pressing Ctrl+Enter should submit the form.
'keydown textarea': function(event) {
if (event.keyCode === 13 && (event.metaKey || event.ctrlKey)) {
$(event.currentTarget).parents('form:first').submit();
}
}
});

View file

@ -0,0 +1,40 @@
template(name="header")
#header(class=currentBoard.colorClass)
//-
If the user is connected we display a small "quick-access" top bar that
list all starred boards with a link to go there. This is inspired by the
Reddit "subreddit" bar.
The first link goes to the boards page.
if currentUser
#header-quick-access
ul
li
+linkTo(route="Boards")
span.fa.fa-home
| All boards
each currentUser.starredBoards
li.separator -
li(class="{{#if $.Session.equals 'currentBoard' _id}}current{{/if}}")
+linkTo(route="Board" data=this)
= title
else
li.current Star a board to add a shortcut in this bar.
li
a.js-create-board
i.fa.fa-plus(title="Create a new board")
+headerUserBar
//-
The main bar is a colorful bar that provide all the meta-data for the
current page. This bar is contextual based.
If the user is not connected we display "sign in" and "log in" buttons.
#header-main-bar
if $.Session.get 'currentBoard'
+headerBoard
else
+headerTitle
template(name="headerTitle")
h1 LibreBoard

View file

@ -0,0 +1,10 @@
Template.header.helpers({
// Reactively set the color of the page from the color of the current board.
headerTemplate: function() {
return 'headerBoard';
}
});
Template.header.events({
'click .js-create-board': Popup.open('createBoard')
});

View file

@ -0,0 +1,266 @@
@import 'nib'
global-reset()
#header
color: white
transition: background-color 0.4s
background: #27AE60
#header-quick-access
background-color: rgba(0, 0, 0, 0.2)
padding: 4px 10px
height: 16px
font-size: 12px
display: flex
ul li, #header-user-bar
color: darken(white, 17%)
a
color: inherit
text-decoration: none
&:hover
color: white
ul
flex: 1
transition: opacity 0.2s
margin-left: 5px
li
display: block
float: left
width: auto
color: darken(white, 15%)
padding: 0 4px 1px 4px
&.separator
padding: 0 2px 1px 2px
&.current
font-style: italic
&:first-child .fa-home
margin-right: 5px
#header-main-bar
height: 30px
padding: 8px
h1
font-size: 19px
line-height: 1.7em
margin: 0 20px 0 10px
float: left
&.header-board-menu
cursor: pointer
.fa-angle-down
font-size: 0.8em
// line-height: 1.1em
margin-left: 5px
.board-header-starred .fa
color: yellow
.board-header-members
float: right
.member
display: block
width: 32px
height: @width
.add-board-member
color: white
display: flex
align-items: center
justify-content: center
border: 1px solid white
height: 32px - 2px
width: @height
i.fa-plus
margin-top: 2px
.header-btn:last-child
margin-right: 0
// #header {
// background: #138871;
// height: 30px;
// overflow: hidden;
// padding: 5px;
// position: relative;
// z-index: 10;
// }
// .header-logo {
// bottom: 0;
// display: block;
// height: 25px;
// left: 50%;
// position: absolute;
// top: 8px;
// width: 80px;
// margin-left: - @width/2;
// text-align: center;
// z-index: 2;
// opacity: .5;
// transition: opacity ease-in 85ms;
// color: white;
// font-size: 22px;
// text-decoration: none;
// background-image: url('/logos/white_logo.png');
// &:hover {
// opacity: .8;
// color: white;
// }
// }
// .header-btn.header-btn-feedback {
// background: rgba(255, 255, 255, .1);
// background: linear-gradient(to bottom, rgba(255, 255, 255, .1) 0, rgba(255, 255, 255, .05) 100%);
// padding-left: 22px;
// margin-right: 16px;
// .header-btn-icon {
// top: 1px;
// }
// }
.header-btn {
border-radius: 3px;
user-select: none;
background: rgba(255, 255, 255, .3);
background: linear-gradient(to bottom, rgba(255, 255, 255, .3) 0, rgba(255, 255, 255, .2) 100%);
color: #f3f3f3;
display: block;
float: left;
font-weight: 700;
height: 30px;
line-height: 30px;
padding: 0 10px;
position: relative;
margin-right: 8px;
min-width: 30px;
text-decoration: none;
cursor: pointer;
.header-btn-icon {
font-size: 16px;
line-height: 28px;
position: absolute;
top: 0;
left: 0;
}
&.new-notifications {
background: #ba1212;
&:hover {
background: #d11515;
}
}
&.header-member .member {
margin: 0;
border-top-left-radius: 3px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-bottom-left-radius: 3px;
&:hover .member-avatar {
opacity: 1;
}
}
&:hover {
background: rgba(255, 255, 255, .4);
background: linear-gradient(to bottom, rgba(255, 255, 255, .4) 0, rgba(255, 255, 255, .3) 100%);
color: #fff;
.header-btn-count {
background: #d11515;
}
}
&:active {
background: rgba(255, 255, 255, .4);
background: linear-gradient(to bottom, rgba(255, 255, 255, .4) 0, rgba(255, 255, 255, .3) 100%);
}
&.upgrade {
margin-right: 16px;
.icon-sm {
padding: 6px 2px 6px 4px;
}
}
&.upgrade,
&.header-boards {
padding-left: 4px;
}
&.header-boards {
padding-right: 4px;
}
&.header-login,
&.header-signup {
padding: 0 12px;
}
&.header-signup {
background: #48b512;
background: linear-gradient(to bottom, #48b512 0, #3d990f 100%);
&:hover {
background: #3d990f;
background: linear-gradient(to bottom, #3d990f 0, #327d0c 100%);
}
&:active {
background: #327d0c;
}
}
&.header-go-to-boards {
padding: 0 8px 0 38px;
}
&.header-go-to-boards .member {
border-top-left-radius: 3px;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
border-bottom-left-radius: 3px;
position: absolute;
left: 0;
}
}
// .header-btn-text {
// padding: 0 8px;
// }
// .header-notification-list ul {
// margin-top: 8px;
// }
// .header-notification-list .action-comment {
// max-height: 250px;
// overflow-y: auto;
// }
// .header-user {
// position: absolute;
// top: 5px;
// right: 0;
// }

View file

@ -0,0 +1,63 @@
var Helpers = {
error: function() {
return Session.get('error');
},
toLowerCase: function(text) {
return text && text.toLowerCase();
},
toUpperCase: function(text) {
return text && text.toUpperCase();
},
firstChar: function(text) {
return text && text[0].toUpperCase();
},
session: function(prop) {
return Session.get(prop);
},
getUser: function(userId) {
return Users.findOne(userId);
}
};
// Register all Helpers
_.each(Helpers, function(fn, name) { Blaze.registerHelper(name, fn); });
// XXX I believe we should compute a HTML rendered field on the server that
// would handle markdown, emojies and user mentions. We can simply have two
// fields, one source, and one compiled version (in HTML) and send only the
// compiled version to most users -- who don't need to edit.
// In the meantime, all the transformation are done on the client using the
// Blaze API.
var at = HTML.CharRef({html: '&commat;', str: '@'});
Blaze.Template.registerHelper('mentions', new Template('mentions', function() {
var view = this;
var content = Blaze.toHTML(view.templateContentBlock);
var currentBoard = Session.get('currentBoard');
var knowedUsers = _.map(currentBoard.members, function(member) {
member.username = Users.findOne(member.userId).username;
return member;
});
var mentionRegex = /\B@(\w*)/gi;
var currentMention, knowedUser, href, linkClass, linkValue, link;
while (currentMention = mentionRegex.exec(content)) {
knowedUser = _.findWhere(knowedUsers, { username: currentMention[1] });
if (! knowedUser)
continue;
linkValue = [' ', at, knowedUser.username];
href = Router.url('Profile', { username: knowedUser.username });
linkClass = 'atMention' + (knowedUser.userId === Meteor.userId() ? ' me' : '');
link = HTML.A({ href: href, 'class': linkClass }, linkValue);
content = content.replace(currentMention[0], Blaze.toHTML(link));
}
return HTML.Raw(content);
}));

View file

@ -0,0 +1,17 @@
head
title LibreBoard
meta(name="viewport"
content="maximum-scale=1.0,width=device-width,initial-scale=1.0,user-scalable=0")
link(rel="shortcut icon" href="/favicon.png")
template(name="userFormsLayout")
h1.at-form-landing-logo
img(src="/logo.png" title="LibreBoard")
+yield
template(name="defaultLayout")
#surface
+header
#content
+yield

View file

@ -0,0 +1,16 @@
Popup.template.events({
click: function(evt) {
if (evt.originalEvent) {
evt.originalEvent.clickInPopup = true;
}
},
'click .js-back-view': function() {
Popup.back();
},
'click .js-close-popover': function() {
Popup.close();
},
'click .js-confirm': function() {
this.__afterConfirmAction.call(this);
}
});

View file

@ -0,0 +1,585 @@
@import 'nib'
.pop-over
background: #fff
border-radius: 3px
border: 1px solid #dbdbdb
border-bottom-color: #c2c2c2
box-shadow: 0 1px 6px rgba(0, 0, 0, .3)
display: none
overflow: hidden
position: absolute
width: 300px
z-index: 99999
margin-top: 5px
hr
margin: 4px -10px
width: 275px + 2*10px
input[type="text"],
input[type="email"],
input[type="password"]
margin: 4px 0 12px
width: 100%
input[type="file"]
width: 240px
select
width: 100%
margin-bottom: 14px
textarea
height: 72px
margin: 4px 0 12px
width: 100%
.empty
margin: 0
img
max-width: 270px
.custom-image img
height: 18px
left: 9px
top: 9px
width: 18px
.title
line-height: 32px
.header
height: 36px
position: relative
margin-bottom: 8px
background: #F7F7F7
border-bottom: 1px solid #dcdcdc
color: darken(white, 60%)
.header-title
display: block
line-height: 32px
padding-top: 4px
margin: 0 10px
font-weight: bold
overflow: hidden
text-overflow: ellipsis
white-space: nowrap
.back-btn, .close-btn
&:hover .icon-sm
color: darken(white, 80%)
.back-btn
padding: 10px
float: left
.close-btn
padding: 10px 10px 10px 4px
position: absolute
top: 0
right: 0
.content
overflow-x: hidden
overflow-y: auto
padding: 0 10px 10px
max-height: 550px
.quiet
padding: 6px 6px 4px
&.search-over
background: #f0f0f0
min-height: 114px
.header
display: none
.content
padding: 8px 4px 8px 10px
margin-right: 8px
&::-webkit-scrollbar-button
display: block
height: 4px
width: 4px
.select-members-list
margin-bottom: 8px
.pop-over-list
&.navigable li.not-selectable>a:hover,
li.not-selectable>a:hover
color: #8c8c8c
cursor: default
.icon-sm
color: #a6a6a6
li > a
cursor: pointer
display: block
font-weight: 700
padding: 6px 10px
position: relative
margin: 0 -10px
text-decoration: none
.item-name
display: block
width: auto
padding-right: 22px
&:hover
background-color: #005377
color: #fff
.sub-name,
.quiet
color: #eee
.unread-indicator
background: #fff
.icon-sm
color: #fff
.sub-name
clear: both
color: #8c8c8c
display: block
font-size: 12px
font-weight: 400
line-height: 15px
margin-top: 4px
&.current
background-color: #e2e6e9
.unread-indicator
background: #2e85b8
background: linear-gradient(to bottom, #2e85b8 0, #2b7cab 100%)
border-radius: 7px
display: block
height: 14px
opacity: 0
position: absolute
right: 16px
top: 8px
width: 14px
&.any
opacity: 1
&:active
background-color: #2e85b8
&.disabled
color: #8c8c8c
cursor: default
.vis-icon
opacity: .35
.icon-sm
color: #a6a6a6
&:hover
background: none
.sub-name,
.quiet
color: #8c8c8c
.icon-sm
color: #a6a6a6
&:active
background: none
&.inset li > a
border-radius: 3px
margin: 0
.pop-over-list.checkable
.icon-check
display: none
position: absolute
top: 6px
right: 12px
li.active a
padding-right: 28px
.icon-check
display: block
&.left-check
.icon-check
right: auto
left: 10px
li a
padding-right: 10px
padding-left: 30px
li.active a
padding-right: 10px
&.normal-weight li>a
font-weight: 400
&.navigable
li > a:hover
background-color: transparent
color: #4d4d4d
.sub-name,
.quiet
color: #8c8c8c
.icon-sm
color: #a6a6a6
li.selected > a
background-color: #005377
color: #fff
.sub-name,
.quiet
color: #eee
li.selected > a
&.current
background-color: #005377
.unread-indicator
background: #fff
.icon-sm
color: #fff
&:active
background-color: #005377
.pop-over.miniprofile
.header
border-bottom-color: transparent
height: 30px
position: absolute
right: 0
top: 0
width: 60px
z-index: 1
.header-title
display: none
.pop-over-list
padding-top: 8px
.mini-profile-info
margin-top: 8px
min-height: 56px
position: relative
.member-large
position: absolute
top: 2px
left: 2px
.info
margin: 0 0 0 64px
word-wrap: break-word
h3 a
text-decoration: none
&:hover
text-decoration: underline
.pop-over.avdetail .header
border-bottom-color: transparent
height: 20px
position: absolute
top: 8px
left: 8px
right: 8px
z-index: 0
.pop-over.avdetail .header-title
display: none
.pop-over.avdetail .content
text-align: center
.pop-over.avdetail .mem-info
margin: 2px 24px 8px
position: relative
z-index: 1
width: 222px
.pop-over.avdetail .mem-info h3 a
text-decoration: none
.pop-over.avdetail .mem-info h3 a:hover
text-decoration: underline
.pop-over-label-list li,
.pop-over-member-list li
&.disabled a
cursor:default
&:not(.disabled):hover a
background-color: #005377
color: #fff
.pop-over-label-list,
.pop-over-member-list,
.pop-over-emoji-list,
.pop-over-card-list
li
a
border-radius: 3px
display: block
height: 30px
line-height: 30px
overflow: hidden
position: relative
text-overflow: ellipsis
text-decoration: none
white-space: nowrap
padding: 4px
margin-bottom: 2px
&.multi-line
line-height: 16px
.member
margin-right: 8px
.card-label
float: left
height: 30px
margin: 0 8px 0 0
padding: 0
width: 30px
.option,
.icon-check
background-clip: content-box
background-origin: content-box
padding: 11px
position: absolute
top: 0
right: 0
.sub-name
font-size: 12px
&:last-child a
margin-bottom: 0
&.disabled
opacity: .5
&.active a,
&.selected a
background: none
color: #4d4d4d
cursor: default
.quiet
color: #8c8c8c
&.email-invite
.member
display: none
a
padding: 0 10px
&.selected a
background-color: #005377
color: #fff
.quiet
color: #eee
.card-label
border-radius: 3px
.icon-check
color: #fff
&.active a .icon-check
display: block
&.unconfirmed a.name
line-height: 16px
&.options li
&.selected a
padding-right: 28px
.option
display: block
opacity: .5
&:hover
opacity: 1
&.disabled.selected a
padding-right: 0
.option
display: none
&.no-option.selected a
padding-right: 6px
.option
display: none
&.collapsed
&.checkable li.active a
padding-right: 0
li
float: left
margin: 0 3px 3px 0
a
padding: 0
margin: 0
width: 30px
.member
opacity: .8
.full-name
display: none
&.selected a .member,
&.active.selected a .member
border-color: #005377
opacity: .9
&.active a
.member
border-color: #2e85b8
opacity: 1
.icon-check
border-radius: 3px
background-color: #2e85b8
bottom: 0
color: #fff
display: block
padding: 0
right: 0
top: auto
&.checkable li.active a
padding-right: 28px
&.filtered li
display: none
&.matches-filter
display: block
&.limited li.exceeds-limit
display: none
.pop-over-emoji-list li > a
padding: 2px 4px
.emoji
margin: 0 6px
.pop-over-card-list li > a
padding: 2px 4px
.login-signup-popover
padding: 15px
.form-tabs
display: none
h1
margin-bottom: 15px
p
margin: 8px 0
.form-parts-container
position: relative
.active-box
position: absolute
top: 0
background: #e2e2e2
border: 1px solid #c9c9c9
border-radius: 3px
z-index: 1
height: 100%
width: 49%
transition-property: all
transition-duration: .4s
opacity: 1
&.start
opacity: 0
left: 25%
.signup-form,
.login-form
position: relative
box-sizing: border-box
padding: 20px
width: 50%
z-index: 2
opacity: .3
transition-property: opacity
transition-duration: .2s
.active
opacity: 1
.js-signup-form-pos
left: 0
.login-form
position: absolute
top: 0
.login-form .icon-google
position: absolute
left: 5px
top: 3px
.login-form .button.google
padding-left: 40px
margin: 0 0 15px 0
.js-login-form-pos
left: 50%

View file

@ -0,0 +1,13 @@
.pop-over.clearfix(
class="{{#unless title}}miniprofile{{/unless}}"
class=currentBoard.colorClass
style="display:block; left:{{offset.left}}px; top:{{offset.top}}px;")
.header.clearfix
if hasPopupParent
a.back-btn.js-back-view
i.fa.fa-chevron-left
span.header-title= title
a.close-btn.js-close-popover
i.fa.fa-times
.content.clearfix
+Template.dynamic(template=popupName data=dataContext)

View file

@ -0,0 +1,40 @@
Template.editor.rendered = function() {
this.$('textarea').textcomplete([
// Emojies
{
match: /\B:([\-+\w]*)$/,
search: function(term, callback) {
callback($.map(Emoji.values, function(emoji) {
return emoji.indexOf(term) === 0 ? emoji : null;
}));
},
template: function(value) {
var image = '<img src="' + Emoji.baseImagePath + value + '.png"></img>';
return image + value;
},
replace: function(value) {
return ':' + value + ':';
},
index: 1
},
// User mentions
{
match: /\B@(\w*)$/,
search: function(term, callback) {
var currentBoard = Boards.findOne(Session.get('currentBoard'));
callback($.map(currentBoard.members, function(member) {
var username = Users.findOne(member.userId).username;
return username.indexOf(term) === 0 ? username : null;
}));
},
template: function(value) {
return value;
},
replace: function(username) {
return '@' + username + ' ';
},
index: 1
}
]);
};

View file

@ -0,0 +1,5 @@
Router.route('/', {
name: 'Home',
redirectLoggedInUsers: true,
authenticated: true
});

View file

@ -0,0 +1,45 @@
/*
* From https://github.com/tobiasahlin/SpinKit
*
* Usage:
*
* <div class="sk-spinner sk-spinner-wave">
* <div class="sk-rect1"></div>
* <div class="sk-rect2"></div>
* <div class="sk-rect3"></div>
* <div class="sk-rect4"></div>
* <div class="sk-rect5"></div>
* </div>
*
*/
.sk-spinner-wave {
&.sk-spinner {
width: 50px;
height: 50px;
margin: auto;
margin-top: 30vh;
text-align: center;
font-size: 10px;
}
div {
background-color: #333;
height: 100%;
width: 6px;
display: inline-block;
animation: sk-waveStretchDelay 1.2s infinite ease-in-out;
}
.sk-rect2 { animation-delay: -1.1s }
.sk-rect3 { animation-delay: -1.0s }
.sk-rect4 { animation-delay: -0.9s }
.sk-rect5 { animation-delay: -0.8s }
}
@keyframes sk-waveStretchDelay {
0%, 40%, 100% { transform: scaleY(0.4) }
20% { transform: scaleY(1.0) }
}

View file

@ -0,0 +1,6 @@
.sk-spinner.sk-spinner-wave(class=currentBoard.colorClass)
.sk-rect1
.sk-rect2
.sk-rect3
.sk-rect4
.sk-rect5

View file

@ -0,0 +1,18 @@
<template name="notfound">
{{ > message label='page-not-found'}}
</template>
<template name='message'>
<div class="big-message quiet {{ color }}">
<h1>{{_ label}}</h1>
{{#with pathFor route='Login'}}
<p>{{{_ 'page-maybe-private' this}}}</p>
{{/with}}
</div>
</template>
<template name="editor">
<textarea class="{{class}}" placeholder="{{_ 'comment-placeholder'}}" id="{{id}}" tabindex="1">{{> UI.contentBlock }}</textarea>
</template>
<template name="viewer">{{#markdown}}{{#emoji}}{{#mentions}}{{> UI.contentBlock }}{{/mentions}}{{/emoji}}{{/markdown}}</template>