From 5984d3f5e24c176f0f0aca9a6982ef57a0f29ad9 Mon Sep 17 00:00:00 2001 From: Lev Lazinskiy Date: Wed, 25 Nov 2015 15:06:35 -0800 Subject: [PATCH 1/2] [feature] added hotkeys for todo item and timestamp --- app/static/js/braindump.js | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/app/static/js/braindump.js b/app/static/js/braindump.js index 59b6e79..37b4264 100644 --- a/app/static/js/braindump.js +++ b/app/static/js/braindump.js @@ -15,3 +15,22 @@ editor.getSession().on('change', function(){ textarea_html.val(marked(editor.getSession().getValue())); textarea.val(editor.getSession().getValue()); }); + +// Hot Keys +editor.commands.addCommand({ + name: 'insert text', + bindKey: {win: 'Ctrl-1', mac: 'Ctrl-1'}, + exec: function(editor) { + editor.insert("*TODO ") + }, + readOnly: true // false if this command should not apply in readOnly mode +}); + +editor.commands.addCommand({ + name: 'Insert TimeStamp', + bindKey: {win: 'Ctrl-d', mac: 'Ctrl-d'}, + exec: function(editor) { + editor.insert(moment().format('MMMM Do YYYY, h:mm:ss a') + "\n"); + }, + readOnly: true // false if this command should not apply in readOnly mode +}); From 4742f38ff8563d7c938f832baeb0c93208d7ed21 Mon Sep 17 00:00:00 2001 From: Lev Lazinskiy Date: Wed, 25 Nov 2015 15:53:55 -0800 Subject: [PATCH 2/2] View Single Notes, and Task Lists Permalink are now created for notes. GFM TaksLists are supported now. :) Thanks to [this](https://github.com/chjj/marked/pull/587) PR. --- app/main/views.py | 10 +++++++- app/static/css/style.css | 4 +++ app/static/js/braindump.js | 4 +-- app/static/js/libs/marked.js | 46 +++++++++++++++++++++++++++------- app/static/js/libs/releases.js | 4 +-- app/templates/_notes.html | 2 +- app/templates/note.html | 7 ++++++ 7 files changed, 62 insertions(+), 15 deletions(-) create mode 100644 app/templates/note.html diff --git a/app/main/views.py b/app/main/views.py index 8b8ab37..50035f5 100644 --- a/app/main/views.py +++ b/app/main/views.py @@ -1,5 +1,5 @@ from datetime import datetime -from flask import render_template, session, redirect, url_for, flash +from flask import render_template, session, redirect, url_for, flash, abort from flask.ext.login import current_user, login_required from . import main @@ -21,6 +21,14 @@ def index(): else: return render_template('index.html') +@main.route('/note/') +@login_required +def note(id): + note = Note.query.get_or_404(id) + if current_user != note.author: + abort(403) + return render_template('note.html', notes=[note]) + @main.route('/edit/', methods=['GET', 'POST']) @login_required def edit(id): diff --git a/app/static/css/style.css b/app/static/css/style.css index 1f8b55b..79150fc 100644 --- a/app/static/css/style.css +++ b/app/static/css/style.css @@ -85,6 +85,10 @@ a.buttons { text-decoration: none; } +li.task-list-item { + list-style-type: none; +} + /* Style for Releases Plugin */ #releases { list-style-type: none; diff --git a/app/static/js/braindump.js b/app/static/js/braindump.js index 37b4264..2b02de2 100644 --- a/app/static/js/braindump.js +++ b/app/static/js/braindump.js @@ -5,7 +5,7 @@ editor.session.setMode("ace/mode/markdown"); editor.getSession().setUseWrapMode(true); editor.setAutoScrollEditorIntoView(true); editor.setOption("minLines", 10); -editor.setOption("maxLines", 100); +editor.setOption("maxLines", 25); var textarea = $('textarea[id="body"]').hide(); var textarea_html_label = $('label[for="body_html"]').hide(); @@ -21,7 +21,7 @@ editor.commands.addCommand({ name: 'insert text', bindKey: {win: 'Ctrl-1', mac: 'Ctrl-1'}, exec: function(editor) { - editor.insert("*TODO ") + editor.insert("- [ ] TODO") }, readOnly: true // false if this command should not apply in readOnly mode }); diff --git a/app/static/js/libs/marked.js b/app/static/js/libs/marked.js index 03251f3..8c21a4a 100644 --- a/app/static/js/libs/marked.js +++ b/app/static/js/libs/marked.js @@ -27,6 +27,7 @@ var block = { text: /^[^\n]+/ }; +block.checkbox = /^\[([ x])\] +/; block.bullet = /(?:[*+-]|\d+\.)/; block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/; block.item = replace(block.item, 'gm') @@ -157,7 +158,8 @@ Lexer.prototype.token = function(src, top, bq) { , item , space , i - , l; + , l + , checked; while (src) { // newline @@ -304,6 +306,16 @@ Lexer.prototype.token = function(src, top, bq) { space = item.length; item = item.replace(/^ *([*+-]|\d+\.) +/, ''); + if (this.options.gfm) { + checked = block.checkbox.exec(item); + + if (checked) { + checked = checked[1] === 'x'; + item = item.replace(block.checkbox, ''); + } else { + checked = undefined; + } + } // Outdent whatever the // list item contains. Hacky. if (~item.indexOf('\n ')) { @@ -333,6 +345,7 @@ Lexer.prototype.token = function(src, top, bq) { } this.tokens.push({ + checked: checked, type: loose ? 'loose_item_start' : 'list_item_start' @@ -809,13 +822,23 @@ Renderer.prototype.hr = function() { return this.options.xhtml ? '
\n' : '
\n'; }; -Renderer.prototype.list = function(body, ordered) { +Renderer.prototype.list = function(body, ordered, taskList) { var type = ordered ? 'ol' : 'ul'; - return '<' + type + '>\n' + body + '\n'; + var classes = taskList ? ' class="task-list"' : ''; + return '<' + type + classes + '>\n' + body + '\n'; }; -Renderer.prototype.listitem = function(text) { - return '
  • ' + text + '
  • \n'; +Renderer.prototype.listitem = function(text, checked) { + if (checked === undefined) { + return '
  • ' + text + '
  • \n'; + } + + return '
  • ' + + ' ' + + text + + '
  • \n'; }; Renderer.prototype.paragraph = function(text) { @@ -1037,16 +1060,21 @@ Parser.prototype.tok = function() { } case 'list_start': { var body = '' + , taskList = false , ordered = this.token.ordered; while (this.next().type !== 'list_end') { + if (this.token.checked !== undefined) { + taskList = true; + } body += this.tok(); } - return this.renderer.list(body, ordered); + return this.renderer.list(body, ordered, taskList); } case 'list_item_start': { - var body = ''; + var body = '' + , checked = this.token.checked; while (this.next().type !== 'list_item_end') { body += this.token.type === 'text' @@ -1054,7 +1082,7 @@ Parser.prototype.tok = function() { : this.tok(); } - return this.renderer.listitem(body); + return this.renderer.listitem(body, checked); } case 'loose_item_start': { var body = ''; @@ -1063,7 +1091,7 @@ Parser.prototype.tok = function() { body += this.tok(); } - return this.renderer.listitem(body); + return this.renderer.listitem(body, checked); } case 'html': { var html = !this.token.pre && !this.options.pedantic diff --git a/app/static/js/libs/releases.js b/app/static/js/libs/releases.js index d981a61..444aa34 100644 --- a/app/static/js/libs/releases.js +++ b/app/static/js/libs/releases.js @@ -11,8 +11,8 @@ breaks: true, pedantic: false, sanitize: true, - smartLists: true, - smartypants: true + smartLists: false, + smartypants: false }); getReleaseUrl = function(repo) { diff --git a/app/templates/_notes.html b/app/templates/_notes.html index d6b9ba7..75b5e75 100644 --- a/app/templates/_notes.html +++ b/app/templates/_notes.html @@ -2,7 +2,7 @@ {% for note in notes %}
  • -

    {{ note.title }}

    +
    {{ moment(note.timestamp).fromNow() }} by {{ note.author.username }}
    diff --git a/app/templates/note.html b/app/templates/note.html new file mode 100644 index 0000000..ec2d968 --- /dev/null +++ b/app/templates/note.html @@ -0,0 +1,7 @@ +{% extends 'base.html' %} + +{% block title %} Braindump | Note {% endblock %} + +{% block page_content %} +{% include '_notes.html' %} +{% endblock %}