Ver código fonte

Work on the card activities and comments

This commit also introduces a new CSSEvents object that is used to
abstract vendor specifics events related to CSS transitions and
animations.

Fixes #183.
Fixes #179.
Maxime Quandalle 10 anos atrás
pai
commit
c894567987

+ 1 - 0
.jshintrc

@@ -71,6 +71,7 @@
     "AccountsTemplates": true,
 
     // Our objects
+    "CSSEvents": true,
     "EscapeActions": true,
     "Filter": true,
     "Filter": true,

+ 106 - 1
client/components/activities/activities.jade

@@ -1,8 +1,113 @@
 template(name="activities")
-  .js-sidebar-activities
+  .activities.js-sidebar-activities
     //- We should use Template.dynamic here but there is a bug with
     //- blaze-components: https://github.com/peerlibrary/meteor-blaze-components/issues/30
     if $eq mode "board"
       +boardActivities
     else
       +cardActivities
+
+template(name="boardActivities")
+  each currentBoard.activities
+    .activity
+      +userAvatar(userId=user._id)
+      p.activity-desc
+        +memberName(user=user)
+
+        if($eq activityType 'createBoard')
+          | {{_ 'activity-created' boardLabel}}.
+
+        if($eq activityType 'createList')
+          | {{_ 'activity-added' list.title boardLabel}}.
+
+        if($eq activityType 'archivedList')
+          | {{_ 'activity-archived' list.title}}.
+
+        if($eq activityType 'createCard')
+          | {{{_ 'activity-added' cardLink boardLabel}}}.
+
+        if($eq activityType 'archivedCard')
+          | {{{_ 'activity-archived' cardLink}}}.
+
+        if($eq activityType 'restoredCard')
+          | {{{_ 'activity-sent' cardLink boardLabel}}}.
+
+        if($eq activityType 'moveCard')
+          | {{{_ 'activity-moved' cardLink oldList.title list.title}}}.
+
+        if($eq activityType 'addBoardMember')
+          | {{{_ 'activity-added' memberLink boardLabel}}}.
+
+        if($eq activityType 'removeBoardMember')
+          | {{{_ 'activity-excluded' memberLink boardLabel}}}.
+
+        if($eq activityType 'joinMember')
+          if($eq currentUser._id member._id)
+            | {{{_ 'activity-joined' cardLink}}}.
+          else
+            | {{{_ 'activity-added' memberLink cardLink}}}.
+
+        if($eq activityType 'unjoinMember')
+          if($eq currentUser._id member._id)
+            | {{{_ 'activity-unjoined' cardLink}}}.
+          else
+            | {{{_ 'activity-removed' memberLink cardLink}}}.
+
+        if($eq activityType 'addComment')
+          | {{{_ 'activity-on' cardLink}}}
+          a.activity-comment(href="{{ card.absoluteUrl }}")
+            +viewer
+              = comment.text
+
+        if($eq activityType 'addAttachment')
+          | {{{_ 'activity-attached' attachmentLink cardLink}}}.
+
+        span.activity-meta {{ moment createdAt }}
+
+template(name="cardActivities")
+  each currentCard.activities
+    .activity
+      +userAvatar(userId=user._id)
+      p.activity-desc
+        +memberName(user=user)
+        if($eq activityType 'createCard')
+          | {{_ 'activity-added' cardLabel list.title}}.
+        if($eq activityType 'joinMember')
+          if($eq currentUser._id member._id)
+            | {{_ 'activity-joined' cardLabel}}.
+          else
+            | {{{_ 'activity-added' cardLabel memberLink}}}.
+        if($eq activityType 'unjoinMember')
+          if($eq currentUser._id member._id)
+            | {{_ 'activity-unjoined' cardLabel}}.
+          else
+            | {{{_ 'activity-removed' cardLabel memberLink}}}.
+        if($eq activityType 'archivedCard')
+          | {{_ 'activity-archived' cardLabel}}.
+        if($eq activityType 'restoredCard')
+          | {{_ 'activity-sent' cardLabel boardLabel}}.
+        if($eq activityType 'moveCard')
+          | {{_ 'activity-moved' cardLabel oldList.title list.title}}.
+        if($eq activityType 'addAttachment')
+          | {{{_ 'activity-attached' attachmentLink cardLabel}}}.
+          if attachment.isImage
+            img.attachment-image-preview(src=attachment.url)
+
+        if($eq activityType 'addComment')
+          +inlinedForm(classNames='js-edit-comment')
+            +editor(autofocus=true)
+              = comment.text
+            .edit-controls
+              button.primary(type="submit") {{_ 'edit'}}
+          else
+            .activity-comment
+              +viewer
+                = comment.text
+            span.activity-meta
+              | {{ moment createdAt }} -
+              a.js-open-inlined-form {{_ "edit"}}
+              = ' - '
+              a.js-delete-comment {{_ "delete"}}
+
+        else
+          span.activity-meta {{ moment createdAt }}

+ 32 - 4
client/components/activities/activities.js

@@ -49,10 +49,6 @@ BlazeComponent.extendComponent({
     return TAPi18n.__('this-board');
   },
 
-  cardLabel: function() {
-    return TAPi18n.__('this-card');
-  },
-
   cardLink: function() {
     var card = this.currentData().card();
     return Blaze.toHTML(HTML.A({
@@ -75,3 +71,35 @@ BlazeComponent.extendComponent({
     }, attachment.name()));
   }
 }).register('activities');
+
+BlazeComponent.extendComponent({
+  template: function() {
+    return 'cardActivities';
+  },
+
+  cardLabel: function() {
+    return TAPi18n.__('this-card');
+  },
+
+  events: function() {
+    return [{
+      // XXX We should use Popup.afterConfirmation here
+      'click .js-delete-comment': function() {
+        var commentId = this.currentData().commentId;
+        CardComments.remove(commentId);
+      },
+      'submit .js-edit-comment': function(evt) {
+        evt.preventDefault();
+        var commentText = this.currentComponent().getValue();
+        var commentId = Template.parentData().commentId;
+        if ($.trim(commentText)) {
+          CardComments.update(commentId, {
+            $set: {
+              text: commentText
+            }
+          });
+        }
+      }
+    }];
+  }
+}).register('cardActivities');

+ 28 - 0
client/components/activities/activities.styl

@@ -0,0 +1,28 @@
+.activities
+  clear: both
+
+  .activity
+    margin: 6px 0
+    display: flex
+
+    .member
+      width: 24px
+      height: @width
+
+    .activity-desc
+      flex: 1
+      align-self: center
+      margin: 0
+
+      .activity-comment
+        display: block
+        border-radius: 3px
+        background: white
+        text-decoration: none
+        box-shadow: 0 1px 2px rgba(0,0,0,.2)
+        margin-top: 5px
+        padding: 5px
+
+      .activity-meta
+        font-size: 0.8em
+        color: darken(white, 40%)

+ 8 - 0
client/components/activities/comments.jade

@@ -0,0 +1,8 @@
+template(name="commentForm")
+  .new-comment.js-new-comment(
+    class="{{#if commentFormIsOpen}}is-open{{/if}}")
+    +userAvatar(userId=currentUser._id)
+    form.js-new-comment-form
+      +editor(class="js-new-comment-input")
+      .add-controls
+        button.primary.confirm.clear.js-add-comment(type="submit") {{_ 'comment'}}

+ 49 - 0
client/components/activities/comments.js

@@ -0,0 +1,49 @@
+var commentFormIsOpen = new ReactiveVar(false);
+
+Template.commentForm.helpers({
+  commentFormIsOpen: function() {
+    return commentFormIsOpen.get();
+  }
+});
+
+Template.commentForm.events({
+  'click .js-new-comment:not(.focus)': function() {
+    commentFormIsOpen.set(true);
+  },
+  'submit .js-new-comment-form': function(evt, tpl) {
+    var input = tpl.$('.js-new-comment-input');
+    if ($.trim(input.val())) {
+      CardComments.insert({
+        boardId: this.boardId,
+        cardId: this._id,
+        text: input.val()
+      });
+      input.val('');
+      input.blur();
+      commentFormIsOpen.set(false);
+      Tracker.flush();
+      autosize.update(input);
+    }
+    evt.preventDefault();
+  },
+  // Pressing Ctrl+Enter should submit the form
+  'keydown form textarea': function(evt, tpl) {
+    if (evt.keyCode === 13 && (evt.metaKey || evt.ctrlKey)) {
+      tpl.find('button[type=submit]').click();
+    }
+  }
+});
+
+Template.commentForm.onDestroyed(function() {
+  commentFormIsOpen.set(false);
+});
+
+EscapeActions.register('inlinedForm',
+  function() {
+    commentFormIsOpen.set(false);
+    $('.js-new-comment-input').blur();
+  },
+  function() { return commentFormIsOpen.get(); }, {
+    noClickEscapeOn: '.js-new-comment'
+  }
+);

+ 20 - 20
client/components/activities/comments.styl

@@ -8,15 +8,15 @@
     top: 1px
     left: -38px
 
-  &.focus
+  &.is-open
     .member
       opacity: 1
 
     .helper
       display: inline-block
 
-    .new-comment-input
-      min-height: 108px
+    textarea
+      min-height: 100px
       color: #4d4d4d
       cursor: auto
       overflow: hidden
@@ -25,22 +25,22 @@
   .too-long
     margin-top: 8px
 
-.new-comment-input
-  background-color: #fff
-  border: 0
-  box-shadow: 0 1px 2px rgba(0, 0, 0, .23)
-  color: #8c8c8c
-  height: 36px
-  margin: 4px 4px 6px 0
-  padding: 9px 11px
-  width: 100%
-
-  &:hover,
-  &:focus
+  textarea
     background-color: #fff
-    box-shadow: 0 1px 3px rgba(0, 0, 0, .33)
     border: 0
-    cursor: pointer
-
-  &:focus
-    cursor: auto
+    box-shadow: 0 1px 2px rgba(0, 0, 0, .23)
+    color: #8c8c8c
+    height: 36px
+    margin: 4px 4px 6px 0
+    padding: 9px 11px
+    width: 100%
+
+    &:hover,
+    &:is-open
+      background-color: #fff
+      box-shadow: 0 1px 3px rgba(0, 0, 0, .33)
+      border: 0
+      cursor: pointer
+
+    &:is-open
+      cursor: auto

+ 0 - 30
client/components/activities/events.js

@@ -1,30 +0,0 @@
-Template.cardActivities.events({
-  'click .js-edit-action': function(evt) {
-    var $this = $(evt.currentTarget);
-    var container = $this.parents('.phenom-comment');
-
-    // open and focus
-    container.addClass('editing');
-    container.find('textarea').focus();
-  },
-  'click .js-confirm-delete-action': function() {
-    CardComments.remove(this._id);
-  },
-  'submit form': function(evt) {
-    var $this = $(evt.currentTarget);
-    var container = $this.parents('.phenom-comment');
-    var text = container.find('textarea');
-
-    if ($.trim(text.val())) {
-      CardComments.update(this._id, {
-        $set: {
-          text: text.val()
-        }
-      });
-
-      // reset editing class
-      $('.editing').removeClass('editing');
-    }
-    evt.preventDefault();
-  }
-});

+ 0 - 154
client/components/activities/templates.html

@@ -1,154 +0,0 @@
-<template name="boardActivities">
-    {{# each currentBoard.activities }}
-        <div class="phenom phenom-action clearfix phenom-other">
-            {{> userAvatar userId=user._id}}
-            <div class="phenom-desc">
-                {{ > memberName user=user }}
-
-                {{# if $eq activityType 'createBoard' }}
-                    {{_ 'activity-created' boardLabel}}.
-                {{ /if }}
-
-                {{# if $eq activityType 'createList' }}
-                    {{_ 'activity-added' list.title boardLabel}}.
-                {{ /if }}
-
-                {{# if $eq activityType 'archivedList' }}
-                    {{_ 'activity-archived' list.title}}.
-                {{ /if }}
-
-                {{# if $eq activityType 'createCard' }}
-                    {{{_ 'activity-added' cardLink boardLabel}}}.
-                {{ /if }}
-
-                {{# if $eq activityType 'archivedCard' }}
-                    {{{_ 'activity-archived' cardLink}}}.
-                {{ /if }}
-
-                {{# if $eq activityType 'restoredCard' }}
-                    {{{_ 'activity-sent' cardLink boardLabel}}}.
-                {{ /if }}
-
-                {{# if $eq activityType 'moveCard' }}
-                    {{{_ 'activity-moved' cardLink oldList.title list.title}}}.
-                {{ /if }}
-
-                {{# if $eq activityType 'addBoardMember' }}
-                    {{{_ 'activity-added' memberLink boardLabel}}}.
-                {{ /if }}
-
-                {{# if $eq activityType 'removeBoardMember' }}
-                    {{{_ 'activity-excluded' memberLink boardLabel}}}.
-                {{ /if }}
-
-                {{# if $eq activityType 'joinMember' }}
-                    {{# if $eq currentUser._id member._id }}
-                        {{{_ 'activity-joined' cardLink}}}.
-                    {{ else }}
-                        {{{_ 'activity-added' memberLink cardLink}}}.
-                    {{/if}}
-                {{ /if }}
-
-                {{# if $eq activityType 'unjoinMember' }}
-                    {{# if $eq currentUser._id member._id }}
-                        {{{_ 'activity-unjoined' cardLink}}}.
-                    {{ else }}
-                        {{{_ 'activity-removed' memberLink cardLink}}}.
-                    {{/if}}
-                {{ /if }}
-
-                {{# if $eq activityType 'addComment' }}
-                    <div class="phenom-desc">
-                    {{{_ 'activity-on' cardLink}}}
-                        <div class="action-comment markeddown">
-                            <a href="{{ card.absoluteUrl }}" class="current-comment show tdn">
-                                <p>{{#viewer}}{{ comment.text }}{{/viewer}}</p>
-                            </a>
-                        </div>
-                    </div>
-                {{ /if }}
-
-                {{# if $eq activityType 'addAttachment' }}
-                    <div class="phenom-desc">
-                        {{{_ 'activity-attached' attachmentLink cardLink}}}.
-                    </div>
-                {{ /if }}
-            </div>
-            <p class="phenom-meta quiet">
-                <span class="date js-hide-on-sending">
-                    {{ moment createdAt }}
-                </span>
-            </p>
-        </div>
-    {{ /each }}
-</template>
-
-<template name="cardActivities">
-    {{# each currentCard.comments }}
-        <div class="phenom phenom-action clearfix phenom-comment">
-            {{> userAvatar userId=user._id}}
-            <form>
-                <div class="phenom-desc">
-                    {{ > memberName user=user }}
-                    <div class="action-comment markeddown">
-                        <div class="current-comment">
-                          {{#viewer}}{{ text }}{{/viewer}}
-                        </div>
-                        <textarea class="js-text" tabindex="1">{{ text }}</textarea>
-                    </div>
-                </div>
-                <div class="edit-controls clearfix">
-                    <input type="submit" class="primary confirm js-save-edit" value="{{_ 'save'}}" tabindex="2">
-                </div>
-            </form>
-            <p class="phenom-meta quiet">
-                <span class="date js-hide-on-sending">{{ moment createdAt }}</span>
-                {{# if currentUser }}
-                    <span class="js-hide-on-sending">
-                        - <a href="#" class="js-edit-action">{{_ "edit"}}</a>
-                        - <a href="#" class="js-confirm-delete-action">{{_ "delete"}}</a>
-                    </span>
-                {{/ if }}
-            </p>
-        </div>
-    {{/each}}
-
-    {{# each currentCard.activities }}
-        <div class="phenom phenom-action clearfix phenom-other">
-            {{> userAvatar userId=user._id size="extra-small" class="creator js-show-mem-menu" }}
-            {{ > memberName user=user }}
-            {{# if $eq activityType 'createCard' }}
-                {{_ 'activity-added' cardLabel list.title}}.
-            {{ /if }}
-            {{# if $eq activityType 'joinMember' }}
-                {{# if $eq currentUser._id member._id }}
-                    {{_ 'activity-joined' cardLabel}}.
-                {{ else }}
-                    {{{_ 'activity-added' cardLabel memberLink}}}.
-                {{/if}}
-            {{/if}}
-            {{# if $eq activityType 'unjoinMember' }}
-                {{# if $eq currentUser._id member._id }}
-                    {{_ 'activity-unjoined' cardLabel}}.
-                {{ else }}
-                    {{{_ 'activity-removed' cardLabel memberLink}}}.
-                {{/if}}
-            {{ /if }}
-            {{# if $eq activityType 'archivedCard' }}
-                {{_ 'activity-archived' cardLabel}}.
-            {{ /if }}
-            {{# if $eq activityType 'restoredCard' }}
-                {{_ 'activity-sent' cardLabel boardLabel}}.
-            {{/ if }}
-            {{# if $eq activityType 'moveCard' }}
-                {{_ 'activity-moved' cardLabel oldList.title list.title}}.
-            {{/ if }}
-            {{# if $eq activityType 'addAttachment' }}
-                {{{_ 'activity-attached' attachmentLink cardLabel}}}.
-                {{# if attachment.isImage }}
-                    <img src="{{ attachment.url }}" class="attachment-image-preview">
-                {{/if}}
-            {{/ if}}
-        </div>
-    {{/each}}
-</template>

+ 1 - 10
client/components/boards/boardBody.js

@@ -1,12 +1,3 @@
-// XXX This event list must be abstracted somewhere else.
-var endTransitionEvents = [
-  'webkitTransitionEnd',
-  'otransitionend',
-  'oTransitionEnd',
-  'msTransitionEnd',
-  'transitionend'
-].join(' ');
-
 BlazeComponent.extendComponent({
   template: function() {
     return 'boardComponent';
@@ -69,7 +60,7 @@ BlazeComponent.extendComponent({
             flexBasis: 0,
             padding: 0
           });
-          $(lists).one(endTransitionEvents, removeNode);
+          $(lists).one(CSSEvents.transitionend, removeNode);
         } else {
           removeNode();
         }

+ 2 - 0
client/components/boards/boardHeader.jade

@@ -106,9 +106,11 @@ template(name="createBoardPopup")
       p.quiet
         if $eq visibility.get 'public'
           span.fa.fa-globe.colorful
+          = " "
           | {{{_ 'board-public-info'}}}
         else
           span.fa.fa-lock.colorful
+          = " "
           | {{{_ 'board-private-info'}}}
         a.js-change-visibility Change.
     input.primary.wide(type="submit" value="{{_ 'create'}}")

+ 24 - 8
client/components/cards/details.jade

@@ -1,5 +1,5 @@
 template(name="cardDetails")
-  section.card-details.js-card-details: .card-details-canvas
+  section.card-details.js-card-details.js-perfect-scrollbar: .card-details-canvas
     if cover
       .card-details-cover(style="background-image: url({{ cover.url }})")
 
@@ -42,7 +42,7 @@ template(name="cardDetails")
     //- XXX We should use "editable" to avoide repetiting ourselves
     if currentUser.isBoardMember
       h3.card-details-item-title Description
-      +inlinedForm(classNames="js-card-description")
+      +inlinedForm(classNames="card-description js-card-description")
         +editor(autofocus=true)
           = description
         .edit-controls.clearfix
@@ -62,9 +62,13 @@ template(name="cardDetails")
     if attachments.count
       hr
       +WindowAttachmentsModule(card=this)
-    if isLoaded
-      hr
-      +WindowActivityModule(card=this)
+
+    hr
+    h2 {{ _ 'activity'}}
+    if currentUser.isBoardMember
+      +commentForm
+    if isLoaded.get
+      +activities(card=this mode="card")
 
 template(name="cardDetailsActionsPopup")
   if currentUser.isBoardMember
@@ -75,14 +79,15 @@ template(name="cardDetailsActionsPopup")
   hr
   ul.pop-over-list
     li: a.js-copy Copy Card
-    li: a.js-archive Archive Card
-    li: a.js-delete Delete Card
+    unless archived
+      li: a.js-archive Archive Card
+    li: a.js-more More
 
 template(name="moveCardPopup")
   +boardLists
 
 template(name="cardMembersPopup")
-  ul.pop-over-member-list
+  ul.pop-over-list.pop-over-member-list
     each board.members
       li.item(class="{{#if isCardMember}}active{{/if}}")
         a.name.js-select-member(href="#")
@@ -105,6 +110,17 @@ template(name="cardLabelsPopup")
               span.card-label-selectable-icon.fa.fa-check
   a.quiet-button.full.js-add-label {{_ 'label-create'}}
 
+template(name="cardMorePopup")
+  p.quiet
+    span.clearfix
+      span {{_ 'link-card'}}
+      = ' '
+      i.fa.colorful(class="{{#if board.isPublic}}fa-globe{{else}}fa-lock{{/if}}")
+      input.inline-input(type="text" readonly value="{{ rootUrl }}")
+    | {{_ 'added'}}
+    span.date(title=card.createdAt) {{ moment createdAt 'LLL' }}
+    a.js-delete(title="{{_ 'card-delete-notice'}}") {{_ 'delete'}}
+
 template(name="cardDeletePopup")
   p {{_ "card-delete-pop"}}
   unless archived

+ 23 - 8
client/components/cards/details.js

@@ -4,7 +4,7 @@ BlazeComponent.extendComponent({
   },
 
   mixins: function() {
-    return [Mixins.InfiniteScrolling];
+    return [Mixins.InfiniteScrolling, Mixins.PerfectScrollbar];
   },
 
   calculateNextPeak: function() {
@@ -35,8 +35,19 @@ BlazeComponent.extendComponent({
     });
   },
 
+  onCreated: function() {
+    this.isLoaded = new ReactiveVar(false);
+  },
+
   events: function() {
-    return [{
+    // XXX We can't define this event directly in the event map below because we
+    // miss ES6 object keys interpolation.
+    var events = {};
+    events[CSSEvents.animationend + ' .js-card-details'] = function() {
+      this.isLoaded.set(true);
+    };
+
+    return [_.extend(events, {
       'click .js-close-card-details': function() {
         Utils.goBoardId(this.data().boardId);
       },
@@ -60,7 +71,7 @@ BlazeComponent.extendComponent({
       'mouseenter .js-card-details': function() {
         this.componentParent().showOverlay.set(true);
       }
-    }];
+    })];
   }
 }).register('cardDetails');
 
@@ -78,11 +89,7 @@ Template.cardDetailsActionsPopup.events({
     });
     Popup.close();
   },
-  'click .js-delete': Popup.afterConfirm('cardDelete', function() {
-    var cardId = this._id;
-    Cards.remove(cardId);
-    Popup.close();
-  })
+  'click .js-more': Popup.open('cardMore')
 });
 
 Template.moveCardPopup.events({
@@ -100,6 +107,14 @@ Template.moveCardPopup.events({
   }
 });
 
+Template.cardMorePopup.events({
+  'click .js-delete': Popup.afterConfirm('cardDelete', function() {
+    Popup.close();
+    Cards.remove(this._id);
+    Utils.goBoardId(this.board()._id);
+  })
+});
+
 // Close the card details pane by pressing escape
 EscapeActions.register('detailsPane',
   function() { Utils.goBoardId(Session.get('currentBoard')); },

+ 10 - 59
client/components/cards/details.styl

@@ -10,9 +10,9 @@
   background: white
   border-radius: 3px
   z-index: 20 !important
-  animation: flexGrowIn 0.2s
+  animation: flexGrowIn 0.1s
   box-shadow: 0 0 7px 0 darken(white, 30%)
-  transition: flex-basis 0.2s, padding 0.2s
+  transition: flex-basis 0.1s
   margin-top: -9px
 
   .card-details-canvas
@@ -62,13 +62,18 @@
           border-radius: 3px
           padding: 0px 5px
 
+  .card-description textarea
+    min-height: 100px
 
   .card-details-items
     display: flex
     margin: 15px 0
 
     .card-details-item
-      flex-grow: 1
+      &.card-details-item-labels,
+      &.card-details-item-members
+        width: 50%
+        flex-shrink: 1
 
   .card-details-item-title
     font-size: 14px
@@ -78,62 +83,8 @@
     padding-top: 5px
     padding-bottom: 5px
 
-.new-comment
-  position: relative
-  margin: 0 0 20px 38px
-
-  .member
-    opacity: .7
-    position: absolute
-    top: 1px
-    left: -38px
-
-  .helper
-    bottom: 0
-    display: none
-    position: absolute
-    right: 9px
-
-  &.focus
-
-    .member
-      opacity: 1
-
-    .helper
-      display: inline-block
-
-    .new-comment-input
-      min-height: 108px
-      color: #4d4d4d
-      cursor: auto
-      overflow: hidden
-      word-wrap: break-word
-
-  .too-long
-    margin-top: 8px
-
-.new-comment-input
-  background-color: #fff
-  border: 0
-  box-shadow: 0 1px 2px rgba(0, 0, 0, .23)
-  color: #8c8c8c
-  height: 36px
-  margin: 4px 4px 6px 0
-  padding: 9px 11px
-  width: 100%
-
-  &:hover,
-  &:focus
-    background-color: #fff
-    box-shadow: 0 1px 3px rgba(0, 0, 0, .33)
-    border: 0
-    cursor: pointer
-
-  &:focus
-    cursor: auto
-
-.card-composer
-  padding-bottom: 8px
+  .activities
+    padding-top: 10px
 
 input[type="text"].attachment-add-link-input
   float: left

+ 0 - 182
client/components/cards/events.js

@@ -1,61 +1,3 @@
-Template.cardMemberPopup.events({
-  'click .js-remove-member': function() {
-    Cards.update(this.cardId, {$pull: {members: this.userId}});
-    Popup.close();
-  }
-});
-
-Template.WindowActivityModule.events({
-  'click .js-new-comment:not(.focus)': function(evt) {
-    var $this = $(evt.currentTarget);
-    $this.addClass('focus');
-  },
-  'submit #CommentForm': function(evt, t) {
-    var text = t.$('.js-new-comment-input');
-    if ($.trim(text.val())) {
-      CardComments.insert({
-        boardId: this.card.boardId,
-        cardId: this.card._id,
-        text: text.val()
-      });
-      text.val('');
-      $('.focus').removeClass('focus');
-    }
-    evt.preventDefault();
-  }
-});
-
-Template.WindowSidebarModule.events({
-  'click .js-change-card-members': Popup.open('cardMembers'),
-  'click .js-edit-labels': Popup.open('cardLabels'),
-  'click .js-archive-card': function(evt) {
-    // Update
-    Cards.update(this.card._id, {
-      $set: {
-        archived: true
-      }
-    });
-    evt.preventDefault();
-  },
-  'click .js-unarchive-card': function(evt) {
-    Cards.update(this.card._id, {
-      $set: {
-        archived: false
-      }
-    });
-    evt.preventDefault();
-  },
-  'click .js-delete-card': Popup.afterConfirm('cardDelete', function() {
-    Cards.remove(this.card._id);
-
-    // redirect board
-    Utils.goBoardId(this.card.board()._id);
-    Popup.close();
-  }),
-  'click .js-more-menu': Popup.open('cardMore'),
-  'click .js-attach': Popup.open('cardAttachments')
-});
-
 Template.WindowAttachmentsModule.events({
   'click .js-attach': Popup.open('cardAttachments'),
   'click .js-confirm-delete': Popup.afterConfirm('attachmentDelete',
@@ -77,130 +19,6 @@ Template.WindowAttachmentsModule.events({
   }
 });
 
-Template.cardMembersPopup.events({
-  'click .js-select-member': function(evt) {
-    var cardId = Template.parentData(2).data._id;
-    var memberId = this.userId;
-    var operation;
-    if (Cards.find({ _id: cardId, members: memberId}).count() === 0)
-      operation = '$addToSet';
-    else
-      operation = '$pull';
-
-    var query = {};
-    query[operation] = {
-      members: memberId
-    };
-    Cards.update(cardId, query);
-    evt.preventDefault();
-  }
-});
-
-Template.cardLabelsPopup.events({
-  'click .js-select-label': function(evt) {
-    var cardId = Template.parentData(2).data._id;
-    var labelId = this._id;
-    var operation;
-    if (Cards.find({ _id: cardId, labelIds: labelId}).count() === 0)
-      operation = '$addToSet';
-    else
-      operation = '$pull';
-
-    var query = {};
-    query[operation] = {
-      labelIds: labelId
-    };
-    Cards.update(cardId, query);
-    evt.preventDefault();
-  },
-  'click .js-edit-label': Popup.open('editLabel'),
-  'click .js-add-label': Popup.open('createLabel')
-});
-
-Template.formLabel.events({
-  'click .js-palette-color': function(evt) {
-    var $this = $(evt.currentTarget);
-
-    // hide selected ll colors
-    $('.js-palette-select').addClass('hide');
-
-    // show select color
-    $this.find('.js-palette-select').removeClass('hide');
-  }
-});
-
-Template.createLabelPopup.events({
-  // Create the new label
-  'submit .create-label': function(evt, tpl) {
-    var name = tpl.$('#labelName').val().trim();
-    var boardId = Session.get('currentBoard');
-    var selectLabelDom = tpl.$('.js-palette-select').get(0);
-    var selectLabel = Blaze.getData(selectLabelDom);
-    Boards.update(boardId, {
-      $push: {
-        labels: {
-          _id: Random.id(6),
-          name: name,
-          color: selectLabel.color
-        }
-      }
-    });
-    Popup.back();
-    evt.preventDefault();
-  }
-});
-
-Template.editLabelPopup.events({
-  'click .js-delete-label': Popup.afterConfirm('deleteLabel', function() {
-    var boardId = Session.get('currentBoard');
-    Boards.update(boardId, {
-      $pull: {
-        labels: {
-          _id: this._id
-        }
-      }
-    });
-    Popup.back(2);
-  }),
-  'submit .edit-label': function(evt, tpl) {
-    var name = tpl.$('#labelName').val().trim();
-    var boardId = Session.get('currentBoard');
-    var getLabel = Utils.getLabelIndex(boardId, this._id);
-    var selectLabelDom = tpl.$('.js-palette-select').get(0);
-    var selectLabel = Blaze.getData(selectLabelDom);
-    var $set = {};
-
-    // set label index
-    $set[getLabel.key('name')] = name;
-
-    // set color
-    $set[getLabel.key('color')] = selectLabel.color;
-
-    // update
-    Boards.update(boardId, { $set: $set });
-
-    // return to the previous popup view trigger
-    Popup.back();
-
-    evt.preventDefault();
-  },
-  'click .js-select-label': function() {
-    Cards.remove(this.cardId);
-
-    // redirect board
-    Utils.goBoardId(this.boardId);
-  }
-});
-
-Template.cardMorePopup.events({
-  'click .js-delete': Popup.afterConfirm('cardDelete', function() {
-    Cards.remove(this.card._id);
-
-    // redirect board
-    Utils.goBoardId(this.card.board()._id);
-  })
-});
-
 Template.cardAttachmentsPopup.events({
   'change .js-attach-file': function(evt) {
     var card = this.card;

+ 0 - 48
client/components/cards/helpers.js

@@ -1,48 +0,0 @@
-Template.cardMembersPopup.helpers({
-  isCardMember: function() {
-    var cardId = Template.parentData()._id;
-    var cardMembers = Cards.findOne(cardId).members || [];
-    return _.contains(cardMembers, this.userId);
-  },
-  user: function() {
-    return Users.findOne(this.userId);
-  }
-});
-
-Template.cardLabelsPopup.helpers({
-  isLabelSelected: function(cardId) {
-    return _.contains(Cards.findOne(cardId).labelIds, this._id);
-  }
-});
-
-var labelColors;
-Meteor.startup(function() {
-  labelColors = Boards.simpleSchema()._schema['labels.$.color'].allowedValues;
-});
-
-Template.createLabelPopup.helpers({
-  // This is the default color for a new label. We search the first color that
-  // is not already used in the board (although it's not a problem if two
-  // labels have the same color).
-  defaultColor: function() {
-    var labels = this.labels || this.card.board().labels;
-    var usedColors = _.pluck(labels, 'color');
-    var availableColors = _.difference(labelColors, usedColors);
-    return availableColors.length > 1 ? availableColors[0] : labelColors[0];
-  }
-});
-
-Template.formLabel.helpers({
-  labels: function() {
-    return _.map(labelColors, function(color) {
-      return { color: color, name: '' };
-    });
-  }
-});
-
-Blaze.registerHelper('currentCard', function() {
-  var cardId = Session.get('currentCard');
-  if (cardId) {
-    return Cards.findOne(cardId);
-  }
-});

+ 27 - 0
client/components/cards/labels.jade

@@ -0,0 +1,27 @@
+template(name="formLabel")
+  .colors
+  label(for="labelName") {{_ 'name'}}
+  input.js-label-name#labelName(type="text" name="name" value=name autofocus)
+
+  label {{_ "select-color"}}
+  each labels
+    span.card-label.card-label--selectable.palette-color.js-palette-color(class="card-label-{{color}}")
+      if($eq color ../color)
+        i.fa.fa-check
+
+template(name="createLabelPopup")
+  form.create-label
+    with(color=defaultColor)
+      +formLabel
+    button.primary.wide(type="submit") {{_ 'create'}}
+
+template(name="editLabelPopup")
+  form.edit-label
+    +formLabel
+    button.primary.wide.left(type="submit") {{_ 'save'}}
+    span.right
+
+
+template(name="deleteLabelPopup")
+  p {{_ "label-delete-pop"}}
+  button.js-confirm.negate.full(type="submit") {{_ 'delete'}}

+ 126 - 0
client/components/cards/labels.js

@@ -0,0 +1,126 @@
+Template.cardLabelsPopup.events({
+  'click .js-select-label': function(evt) {
+    var cardId = Template.parentData(2).data._id;
+    var labelId = this._id;
+    var operation;
+    if (Cards.find({ _id: cardId, labelIds: labelId}).count() === 0)
+      operation = '$addToSet';
+    else
+      operation = '$pull';
+
+    var query = {};
+    query[operation] = {
+      labelIds: labelId
+    };
+    Cards.update(cardId, query);
+    evt.preventDefault();
+  },
+  'click .js-edit-label': Popup.open('editLabel'),
+  'click .js-add-label': Popup.open('createLabel')
+});
+
+Template.formLabel.events({
+  'click .js-palette-color': function(evt) {
+    var $this = $(evt.currentTarget);
+
+    // hide selected ll colors
+    $('.js-palette-select').addClass('hide');
+
+    // show select color
+    $this.find('.js-palette-select').removeClass('hide');
+  }
+});
+
+Template.createLabelPopup.events({
+  // Create the new label
+  'submit .create-label': function(evt, tpl) {
+    var name = tpl.$('#labelName').val().trim();
+    var boardId = Session.get('currentBoard');
+    var selectLabelDom = tpl.$('.js-palette-select').get(0);
+    var selectLabel = Blaze.getData(selectLabelDom);
+    Boards.update(boardId, {
+      $push: {
+        labels: {
+          _id: Random.id(6),
+          name: name,
+          color: selectLabel.color
+        }
+      }
+    });
+    Popup.back();
+    evt.preventDefault();
+  }
+});
+
+Template.editLabelPopup.events({
+  'click .js-delete-label': Popup.afterConfirm('deleteLabel', function() {
+    var boardId = Session.get('currentBoard');
+    Boards.update(boardId, {
+      $pull: {
+        labels: {
+          _id: this._id
+        }
+      }
+    });
+    Popup.back(2);
+  }),
+  'submit .edit-label': function(evt, tpl) {
+    var name = tpl.$('#labelName').val().trim();
+    var boardId = Session.get('currentBoard');
+    var getLabel = Utils.getLabelIndex(boardId, this._id);
+    var selectLabelDom = tpl.$('.js-palette-select').get(0);
+    var selectLabel = Blaze.getData(selectLabelDom);
+    var $set = {};
+
+    // set label index
+    $set[getLabel.key('name')] = name;
+
+    // set color
+    $set[getLabel.key('color')] = selectLabel.color;
+
+    // update
+    Boards.update(boardId, { $set: $set });
+
+    // return to the previous popup view trigger
+    Popup.back();
+
+    evt.preventDefault();
+  },
+  'click .js-select-label': function() {
+    Cards.remove(this.cardId);
+
+    // redirect board
+    Utils.goBoardId(this.boardId);
+  }
+});
+
+Template.cardLabelsPopup.helpers({
+  isLabelSelected: function(cardId) {
+    return _.contains(Cards.findOne(cardId).labelIds, this._id);
+  }
+});
+
+var labelColors;
+Meteor.startup(function() {
+  labelColors = Boards.simpleSchema()._schema['labels.$.color'].allowedValues;
+});
+
+Template.createLabelPopup.helpers({
+  // This is the default color for a new label. We search the first color that
+  // is not already used in the board (although it's not a problem if two
+  // labels have the same color).
+  defaultColor: function() {
+    var labels = this.labels || this.card.board().labels;
+    var usedColors = _.pluck(labels, 'color');
+    var availableColors = _.difference(labelColors, usedColors);
+    return availableColors.length > 1 ? availableColors[0] : labelColors[0];
+  }
+});
+
+Template.formLabel.helpers({
+  labels: function() {
+    return _.map(labelColors, function(color) {
+      return { color: color, name: '' };
+    });
+  }
+});

+ 3 - 13
client/components/cards/labels.styl

@@ -85,10 +85,6 @@
     left: 0
     width: 260px
 
-.editable-labels .card-label:hover
-  cursor: pointer
-  opacity: .75
-
 .edit-labels-pop-over
   margin-bottom: 8px
 
@@ -98,7 +94,9 @@
 .card-label-selectable
   border-radius: 3px
   cursor: pointer
-  margin: 0 50px 4px 0
+  margin: 0
+  margin-bottom: 3px
+  width: 190px
   min-height: 18px
   padding: 8px
   position: relative
@@ -113,21 +111,13 @@
   &.active,
   &.active.selected:hover,
   &.active.selected
-    margin-right: 38px
     padding-right: 32px
 
     .card-label-selectable-icon
       right: 6px
 
-  &.active:hover:hover,
-  &.active:hover,
-  &.active.selected:hover:hover,
-  &.active.selected:hover
-    margin-right: 38px
-
   &.selected,
   &:hover
-    margin-right: 38px
     opacity: .8
 
 .active .card-label-selectable

+ 1 - 1
client/components/cards/minicard.jade

@@ -15,7 +15,7 @@ template(name="minicard")
       if comments.count
         .badge(title="{{_ 'card-comments-title' comments.count }}")
           span.badge-icon.fa.fa-comment-o
-          .badge-text= comments.count
+          span.badge-text= comments.count
       if description
         .badge.badge-state-image-only(title=description)
           span.badge-icon.fa.fa-align-left

+ 17 - 9
client/components/cards/minicard.styl

@@ -78,20 +78,34 @@
     margin-bottom: 2px
     text-decoration: none
     word-wrap: break-word
-    clear: both
 
     &::selection
       background: transparent
 
   .minicard-labels
     float: right
+    display: flex
 
     .minicard-label
-      float: right
       width: 11px
       height: @width
       border-radius: 2px
-      margin-right: 3px
+      margin-left: 3px
+
+  .badges
+    float: left
+    margin-top: 5px
+    color: darken(white, 60%)
+
+    &:empty
+      display: none
+
+    .badge
+      float: left
+      margin-right: 10px
+
+      .badge-text
+        font-size: 0.9em
 
   .minicard-members
     float: right
@@ -109,12 +123,6 @@
   .minicard-members:empty
     display: none
 
-  .badges
-    float: left
-
-    &:empty
-      display: none
-
   &.minicard-composer
     margin-bottom: 10px
 

+ 0 - 110
client/components/cards/templates.html

@@ -1,34 +1,3 @@
-<template name="cardMemberPopup">
-    <div class="board-member-menu">
-        <div class="mini-profile-info">
-            {{> userAvatar userId=user._id }}
-            <div class="info">
-                <h3 class="bottom" style="margin-right: 40px;">
-                    <a class="js-profile" href="{{ pathFor route='Profile' username=user.username }}">{{ user.profile.name }}</a>
-                </h3>
-                <p class="quiet bottom">@{{ user.username }}</p>
-            </div>
-        </div>
-        {{# if currentUser.isBoardMember }}
-            <ul class="pop-over-list">
-                <li><a class="js-remove-member">{{_ 'remove-member-from-card'}}</a></li>
-            </ul>
-        {{/ if }}
-    </div>
-</template>
-
-<template name="cardMorePopup">
-    <p class="quiet bottom">
-        <span class="clearfix">
-            <span>{{_ 'link-card'}}</span>
-            <span class="icon-sm fa {{#if card.board.isPublic}}fa-globe{{else}}fa-lock{{/if}}"></span>
-            <input class="js-url js-autoselect inline-input" type="text" readonly="readonly" value="{{ card.rootUrl }}">
-        </span>
-        {{_ 'added'}} <span class="date" title="{{ card.createdAt }}">{{ moment card.createdAt 'LLL' }}</span> -
-        <a class="js-delete" href="#" title="{{_ 'card-delete-notice'}}">{{_ 'delete'}}</a>
-    </p>
-</template>
-
 <template name="cardAttachmentsPopup">
     <div>
         <ul class="pop-over-list">
@@ -42,43 +11,6 @@
     </div>
 </template>
 
-<template name="formLabel">
-    <div class="colors clearfix">
-        <label for="labelName">{{_ 'name'}}</label>
-        <input id="labelName" type="text" name="name" class="js-label-name" value='{{ name }}' autofocus>
-        <label>{{_ "select-color"}}</label>
-        {{# each labels }}
-            <span class="card-label card-label--selectable card-label-{{ color }} palette-color js-palette-color">
-            <span class="card-label-color-select-icon icon-sm fa fa-check light js-palette-select {{#if $neq color ../color}}hide{{/if}}"></span>
-            </span>
-        {{/each}}
-    </div>
-</template>
-
-<template name="createLabelPopup">
-    <form class="create-label">
-        {{#with color=defaultColor}}
-            {{> formLabel}}
-        {{/with}}
-        <input type="submit" class="primary wide left" value="{{_ 'create'}}">
-    </form>
-</template>
-
-<template name="editLabelPopup">
-    <form class="edit-label">
-        {{> formLabel}}
-        <input type="submit" class="primary wide left" value="{{_ 'save'}}">
-        <span class="right">
-            <input type="submit" value="{{_ 'delete'}}" class="negate js-delete-label">
-        </span>
-    </form>
-</template>
-
-<template name="deleteLabelPopup">
-    <p>{{_ "label-delete-pop"}}</p>
-    <input type="submit" class="js-confirm negate full" value="{{_ 'delete'}}">
-</template>
-
 <template name="attachmentDeletePopup">
     <p>{{_ "attachment-delete-pop"}}</p>
     <input type="submit" class="js-confirm negate full" value="{{_ 'delete'}}">
@@ -263,45 +195,3 @@
         </div>
     </div>
 </template>
-
-<template name="WindowSidebarModule">
-    <div class="window-sidebar" style="position: relative;">
-        <div class="window-module clearfix">
-            <h3>{{_ 'add'}}</h3>
-            <div class="clearfix">
-                <a href="#" class="button-link js-change-card-members" title="{{_ 'members-title'}}">
-                    <span class="icon-sm fa fa-user"></span> {{_ 'members'}}
-                </a>
-                <a href="#" class="button-link js-edit-labels" title="{{_ 'labels-title'}}">
-                    <span class="icon-sm fa fa-tags"></span> {{_ 'labels'}}
-                </a>
-                <a href="#" class="button-link js-attach" title="{{_ 'attachment-title'}}">
-                    <span class="icon-sm fa fa-paperclip"></span> {{_ 'attachment'}}
-                </a>
-            </div>
-        </div>
-        <div class="window-module other-actions clearfix">
-            <h3>{{_ 'actions'}}</h3>
-            <div class="clearfix">
-                <hr>
-                {{ #if card.archived }}
-                    <a href="#" class="button-link js-unarchive-card" title="{{_ 'send-to-board-title'}}">
-                        <span class="icon-sm fa fa-recycle"></span> {{_ 'send-to-board'}}
-                    </a>
-                    <a href="#" class="button-link negate js-delete-card" title="{{_ 'delete-title'}}">
-                        <span class="icon-sm fa fa-trash-o"></span> {{_ 'delete'}}
-                    </a>
-                {{ else }}
-                    <a href="#" class="button-link js-archive-card" title="{{_ 'archive-title'}}">
-                        <span class="icon-sm fa fa-archive"></span> {{_ 'archive'}}
-                    </a>
-                {{ /if }}
-            </div>
-        </div>
-        <div class="window-module clearfix">
-            <p class="quiet bottom">
-                <a href="#" class="quiet-button js-more-menu" title="{{_ 'share-and-more-title'}}">{{_ 'share-and-more'}}</a>
-            </p>
-        </div>
-    </div>
-</template>

+ 5 - 0
client/components/forms/forms.styl

@@ -95,6 +95,11 @@ textarea
   resize: vertical
   width: 100%
 
+  &.editor
+    resize: none
+    padding-bottom: 22px
+
+
 .button
   border-radius: 3px
   text-decoration: none

+ 0 - 2
client/components/main/editor.styl

@@ -1,2 +0,0 @@
-textarea.editor
-  min-height: 100px

+ 1 - 20
client/components/main/popup.js

@@ -1,22 +1,3 @@
-// XXX This event list must be abstracted somewhere else.
-function whichTransitionEvent() {
-  var t;
-  var el = document.createElement('fakeelement');
-  var transitions = {
-    transition:'transitionend',
-    OTransition:'oTransitionEnd',
-    MozTransition:'transitionend',
-    WebkitTransition:'webkitTransitionEnd'
-  };
-
-  for (t in transitions) {
-    if (el.style[t] !== undefined) {
-      return transitions[t];
-    }
-  }
-}
-var transitionEvent = whichTransitionEvent();
-
 Popup.template.events({
   'click .js-back-view': function() {
     Popup.back();
@@ -50,7 +31,7 @@ Popup.template.onRendered(function() {
   container._uihooks = {
     removeElement: function(node) {
       $(node).addClass('no-height');
-      $(container).one(transitionEvent, function() {
+      $(container).one(CSSEvents.transitionend, function() {
         node.parentNode.removeChild(node);
       });
     }

+ 18 - 4
client/components/users/userAvatar.jade

@@ -19,14 +19,17 @@ template(name="userPopup")
       +userAvatar(userId=user._id)
       .info
         h3.bottom
-          a.js-profile(href="{{pathFor route='Profile' username=user.username}}")= user.profile.name
+          = user.profile.fullname
           p.quiet.bottom @{{ user.username }}
 
 template(name="memberName")
-  a.js-show-mem-menu(href="{{pathFor route='Profile' username=user.username}}")
+  if showBoth
     = user.profile.fullname
-    if username
-      | ({{ user.username }})
+    | ({{ user.username }})
+  else if user.profile.fullname
+    = user.profile.fullname
+  else
+    = user.username
 
 template(name="changeAvatarPopup")
   ul.pop-over-list
@@ -54,3 +57,14 @@ template(name="changeAvatarPopup")
   button.full.js-upload-avatar
     i.fa.fa-upload
     | Upload an avatar
+
+template(name="cardMemberPopup")
+  .board-member-menu
+    .mini-profile-info
+      +userAvatar(userId=user._id)
+      .info
+        h3.bottom= user.profile.fullname
+        p.quiet.bottom @{{ user.username }}
+    if currentUser.isBoardMember
+      ul.pop-over-list
+        li: a.js-remove-member {{_ 'remove-member-from-card'}}

+ 37 - 0
client/components/users/userAvatar.js

@@ -111,3 +111,40 @@ BlazeComponent.extendComponent({
     }];
   }
 }).register('changeAvatarPopup');
+
+Template.cardMembersPopup.helpers({
+  isCardMember: function() {
+    var cardId = Template.parentData()._id;
+    var cardMembers = Cards.findOne(cardId).members || [];
+    return _.contains(cardMembers, this.userId);
+  },
+  user: function() {
+    return Users.findOne(this.userId);
+  }
+});
+
+Template.cardMembersPopup.events({
+  'click .js-select-member': function(evt) {
+    var cardId = Template.parentData(2).data._id;
+    var memberId = this.userId;
+    var operation;
+    if (Cards.find({ _id: cardId, members: memberId}).count() === 0)
+      operation = '$addToSet';
+    else
+      operation = '$pull';
+
+    var query = {};
+    query[operation] = {
+      members: memberId
+    };
+    Cards.update(cardId, query);
+    evt.preventDefault();
+  }
+});
+
+Template.cardMemberPopup.events({
+  'click .js-remove-member': function() {
+    Cards.update(this.cardId, {$pull: {members: this.userId}});
+    Popup.close();
+  }
+});

+ 6 - 0
client/config/helpers.js

@@ -0,0 +1,6 @@
+Blaze.registerHelper('currentCard', function() {
+  var cardId = Session.get('currentCard');
+  if (cardId) {
+    return Cards.findOne(cardId);
+  }
+});

+ 42 - 0
client/lib/cssEvents.js

@@ -0,0 +1,42 @@
+// XXX Should we use something like Moderniz instead of our custom detector?
+
+var whichTransitionEvent = function() {
+  var t;
+  var el = document.createElement('fakeelement');
+  var transitions = {
+    transition:'transitionend',
+    OTransition:'oTransitionEnd',
+    MSTransition:'msTransitionEnd',
+    MozTransition:'transitionend',
+    WebkitTransition:'webkitTransitionEnd'
+  };
+
+  for (t in transitions) {
+    if (el.style[t] !== undefined) {
+      return transitions[t];
+    }
+  }
+};
+
+var whichAnimationEvent = function() {
+  var t;
+  var el = document.createElement('fakeelement');
+  var transitions = {
+    animation:'animationend',
+    OAnimation:'oAnimationEnd',
+    MSTransition:'msAnimationEnd',
+    MozAnimation:'animationend',
+    WebkitAnimation:'webkitAnimationEnd'
+  };
+
+  for (t in transitions) {
+    if (el.style[t] !== undefined) {
+      return transitions[t];
+    }
+  }
+};
+
+CSSEvents = {
+  transitionend: whichTransitionEvent(),
+  animationend: whichAnimationEvent()
+};

+ 3 - 0
client/styles/main.styl

@@ -77,6 +77,9 @@ a
     cursor: default
     text-decoration: none
 
+strong
+  font-weight: bold
+
 p
   a
     text-decoration: underline

+ 1 - 9
collections/cards.js

@@ -130,8 +130,7 @@ Cards.helpers({
     return _.contains(this.members, memberId);
   },
   activities: function() {
-    return Activities.find({ type: 'card', cardId: this._id },
-                                                    { sort: { createdAt: -1 }});
+    return Activities.find({ cardId: this._id }, { sort: { createdAt: -1 }});
   },
   comments: function() {
     return CardComments.find({ cardId: this._id }, { sort: { createdAt: -1 }});
@@ -182,7 +181,6 @@ CardComments.before.insert(function(userId, doc) {
 if (Meteor.isServer) {
   Cards.after.insert(function(userId, doc) {
     Activities.insert({
-      type: 'card',
       activityType: 'createCard',
       boardId: doc.boardId,
       listId: doc.listId,
@@ -196,7 +194,6 @@ if (Meteor.isServer) {
     if (_.contains(fieldNames, 'archived')) {
       if (doc.archived) {
         Activities.insert({
-          type: 'card',
           activityType: 'archivedCard',
           boardId: doc.boardId,
           listId: doc.listId,
@@ -205,7 +202,6 @@ if (Meteor.isServer) {
         });
       } else {
         Activities.insert({
-          type: 'card',
           activityType: 'restoredCard',
           boardId: doc.boardId,
           listId: doc.listId,
@@ -221,7 +217,6 @@ if (Meteor.isServer) {
     var oldListId = this.previous.listId;
     if (_.contains(fieldNames, 'listId') && doc.listId !== oldListId) {
       Activities.insert({
-        type: 'card',
         activityType: 'moveCard',
         listId: doc.listId,
         oldListId: oldListId,
@@ -242,7 +237,6 @@ if (Meteor.isServer) {
       memberId = modifier.$addToSet.members;
       if (! _.contains(doc.members, memberId)) {
         Activities.insert({
-          type: 'card',
           activityType: 'joinMember',
           boardId: doc.boardId,
           cardId: doc._id,
@@ -256,7 +250,6 @@ if (Meteor.isServer) {
     if (modifier.$pull && modifier.$pull.members) {
       memberId = modifier.$pull.members;
       Activities.insert({
-        type: 'card',
         activityType: 'unjoinMember',
         boardId: doc.boardId,
         cardId: doc._id,
@@ -275,7 +268,6 @@ if (Meteor.isServer) {
 
   CardComments.after.insert(function(userId, doc) {
     Activities.insert({
-      type: 'comment',
       activityType: 'addComment',
       boardId: doc.boardId,
       cardId: doc.cardId,