Render descriptions and comments as Markdown

When displaying comments and descriptions for projects or stories,
render the content as Markdown before displaying it. This is a
simple way to support rich text descriptions and comments, and
there is no special editor as yet.

Any code in the supplied Markdown (indented by 4 spaces) will have
its syntax highlighted. The `highlightjs` module is used for syntax
highlighting and the `marked` module is used for parsing the
Markdown.

Also, stop eslint from raising an error when it thinks something is
undefined, and raise a warning instead. This is because the use of
`hljs` and `marked` was confusing the linter into thinking they
weren't defined.

Change-Id: I7896fd686a39e27f8068ee6db6747b2b5ab0ccfc
This commit is contained in:
Adam Coldrick 2015-09-11 16:59:55 +00:00
parent ec0f5651cd
commit cdf7944065
12 changed files with 67 additions and 23 deletions

View File

@ -49,6 +49,7 @@
"no-trailing-spaces": 2,
"camelcase": 0,
"no-extra-boolean-cast": 0,
"no-undef": 1,
// Stylistic
"indent": [2, 4],

View File

@ -149,7 +149,8 @@ module.exports = function (grunt) {
dir.theme + '/custom/',
dir.theme + '/storyboard/',
dir.bower + '/bootstrap/less/',
dir.bower + '/font-awesome/less/'
dir.bower + '/font-awesome/less/',
dir.bower + '/highlightjs/styles/'
];
},
cleancss: true,

View File

@ -13,7 +13,9 @@
"angular-elastic": "2.4.2",
"angular-moment": "0.9.0",
"angular-cache": "3.2.5",
"angularjs-viewhead": "0.0.1"
"angularjs-viewhead": "0.0.1",
"marked": "0.3.4",
"highlightjs": "8.4"
},
"devDependencies": {
"angular-mocks": "1.3.13",

View File

@ -46,9 +46,9 @@
</small>
</h1>
<p>
<span ng-show="project.description"
class="honor-carriage-return">{{project.description}}
</span>
<insert-markdown ng-if="project.description && !isLoading"
content="project.description">
</insert-markdown>
<em ng-hide="project.description" class="text-muted">
No description provided
</em>

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2015 Codethink Limited.
*
* Licensed under the Apache License, Version 2.0 (the 'License'); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Service for rendering text as markdown.
*/
angular.module('sb.services')
.directive('insertMarkdown', function($sanitize) {
'use strict';
return {
restrict: 'E',
scope: {
content: '='
},
link: function(scope, elem) {
scope.$watch('content', function(newVal) {
elem.html('<div>' + $sanitize(marked(newVal)) + '</div>');
}, true);
}
};
});

View File

@ -4,9 +4,9 @@
<span time-moment eventdate="event.created_at" class="pull-right"></span>
</p>
<p ng-show="event.comment.content"
class="honor-carriage-return">{{event.comment.content}}
</p>
<insert-markdown ng-show="event.comment.content"
content="event.comment.content">
</insert-markdown>
<p><em ng-hide="event.comment.content"
class="text-muted">

View File

@ -38,7 +38,6 @@
<!-- Template for the header and description -->
<script type="text/ng-template" id="/inline/story_detail.html">
<h1>
<span ng-show="story.title" view-title>
{{story.title}}
</span>
@ -52,11 +51,6 @@
</a>
<subscribe resource="story"
resource-id="story.id"></subscribe>
<button type="button"
class="btn btn-link"
ng-click="remove()" permission="is_superuser">
Remove this story
</button>
</small>
</h1>
<p><strong>Author:</strong>
@ -78,9 +72,9 @@
</em>
</p>
<p>
<span ng-show="story.description"
class="honor-carriage-return">{{story.description}}
</span>
<insert-markdown ng-show="story.description"
content="story.description">
</insert-markdown>
<em ng-hide="story.description" class="text-muted">
No description provided
</em>

View File

@ -24,8 +24,9 @@
</a>
</strong>
<br/>
<small class="text-muted honor-carriage-return"
ng-show="expandRow">{{story.description}}
<small class="text-muted"
ng-show="expandRow">
<insert-markdown content="story.description"></insert-markdown>
</small>
</p>
</td>

View File

@ -27,7 +27,7 @@ angular.module('storyboard',
'sb.auth', 'sb.story', 'sb.profile', 'sb.notification', 'sb.search',
'sb.admin', 'sb.subscription', 'sb.project_group', 'ui.router',
'ui.bootstrap', 'monospaced.elastic', 'angularMoment',
'angular-data.DSCacheFactory', 'viewhead'])
'angular-data.DSCacheFactory', 'viewhead', 'ngSanitize'])
.constant('angularMomentConfig', {
preprocess: 'utc',
timezone: 'UTC'
@ -62,6 +62,13 @@ angular.module('storyboard',
preferences: PreferenceResolver.resolvePreferences
}
});
// Set up syntax highlighting for the markdown parser
marked.setOptions({
highlight: function (code) {
return hljs.highlightAuto(code).value;
}
});
})
.run(function ($log, $rootScope, $document) {
'use strict';

View File

@ -39,6 +39,8 @@
<script src="moment/moment.js"></script>
<script src="angular-moment/angular-moment.js"></script>
<script src="angularjs-viewhead/angularjs-viewhead.js"></script>
<script src="marked/marked.min.js"></script>
<script src="highlightjs/highlight.pack.js"></script>
<!-- endbuild -->
<link rel="stylesheet" href="styles/main.css">

View File

@ -36,7 +36,7 @@
padding: @table-condensed-cell-padding;
}
> p {
> insert-markdown > div, > p {
padding: @table-cell-padding;
padding-bottom: 0px;
}

View File

@ -24,6 +24,7 @@
@import './bootstrap.less';
@import './base/bootstrap/navbar.less';
@import './font-awesome.less';
@import (less) './default.css';
// Theme
@import './theme.less';