Implemented notes UI.

- Switched from a global reset in www/css/ts-screen.scss to a selected
  top-level elements reset to allow default formatting for user notes.
- Restructured the #entry-list and entry displays.
- Restructured notes div, now has a sub-div for text and a textarea
  element for input.
- Added Showdown.js, a JavaScript Markdown library for formatting comments.
- Moved EntryView blur events to the View events map.
- Added images for expansion of notes.
- Added the ability to edit notes.
- Split EntryListView.addOne into renderOne and addOne so that renderOne
  can be called with a new entry (fixes duration glitch)
This commit is contained in:
Jonathan Bernard 2011-05-16 04:09:37 -05:00
parent 65a9a517f9
commit 2cc17b85f1
35 changed files with 1949 additions and 1427 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,6 +1,5 @@
Current
=======
- Add UI for note-taking
Upcoming
========
@ -16,6 +15,15 @@ Upcoming
- Create a light, alternating background for entries
- Add hover-enabled icons for editing entries/showing notes
- Create tooltips.
- Create a realtime tick-tock for the duration of the current item.
- Mobile version of the app.
- Refactor code, seperate out reusable bits for mobile version.
- Automatic code-highlighting (Highlight.js)
Done
====
- Add UI for note-taking
- Add Markdown converter for notes.
- Fix the duration bug when adding new events. Need to set the nextModel for
the previously 'current' timestamp and set the nextModel of the new timestamp
to 'null'

View File

@ -4,30 +4,37 @@
*/
/* _rounded.scss */
* {
color: inherit;
margin: 0;
padding: 0; }
color: inherit; }
body {
color: #222222;
width: 75%;
margin: auto; }
margin: auto;
padding: 0; }
input {
border: solid thin #555555;
-webkit-box-shadow: inset 0px 2px 4px #CCC;
box-shadow: inset 0px 2px 4px #CCC;
margin: 0;
margin-bottom: 0.5em;
padding: 0;
font-family: Cantarell; }
#top {
background: #222222;
color: #eeeeee;
margin: 0;
opacity: 1;
padding: 0.5em 0;
padding: 0.5rem 0;
position: fixed;
top: 0px;
width: 75%; }
width: 75%;
z-index: 1; }
#top * {
margin: 0;
padding: 0; }
#top #fade-bar {
background: url("img/fade.png") repeat-x;
height: 32px;
@ -73,6 +80,8 @@ input {
#login {
background: white;
color: #eeeeee;
margin: 0;
padding: 0;
opacity: 1;
position: fixed;
top: 0px;
@ -80,6 +89,9 @@ input {
width: 100%;
height: 100em;
z-index: 10; }
#login * {
margin: 0;
padding: 0; }
#login div.container {
background: #222222;
border-radius: 10px;
@ -138,28 +150,34 @@ input {
width: 10em; }
#entry-list {
margin-top: 6em;
padding-bottom: 1em;
margin-top: 6rem;
padding-bottom: 1rem; }
margin: 6em 0 0 0;
margin: 6rem 0 0 0;
padding-bottom: 1em 0 0 0;
padding-bottom: 1rem 0 0 0; }
#entry-list .day-seperator {
background: #cccccc;
color: #222222;
font-family: Cantarell;
font-weight: bold;
margin-top: 1em;
margin: 1em 0 0 0;
margin: 1rem 0 0 0;
padding: 0 2em;
margin-top: 1rem;
padding: 0 2rem; }
#entry-list .day-seperator * {
margin: 0;
padding: 0; }
#entry-list .day-seperator h4, #entry-list .day-seperator h5 {
display: inline-block; }
#entry-list .day-seperator h5 {
color: #667; }
#entry-list #new-entry {
margin-top: 0.5em;
margin: 0.5em 0 0 0;
padding: 0 2em;
margin-top: 0.5rem;
margin: 0.5rem 0 0 0;
padding: 0 2rem; }
#entry-list #new-entry * {
margin: 0;
padding: 0; }
#entry-list .timestamp, #entry-list .timestamp-input, #entry-list .duration {
text-align: right;
width: 14%; }
@ -172,25 +190,33 @@ input {
#entry-list .entry div {
display: inline-block; }
#entry-list .entry .mark {
margin: 0;
padding: 0;
position: relative; }
#entry-list .entry .mark img.delete-icon {
#entry-list .entry .mark * {
margin: 0;
padding: 0; }
#entry-list .entry .mark img.expand-entry, #entry-list .entry .mark img.collapse-entry {
display: none;
left: -20px;
position: absolute;
top: 6px; }
#entry-list .entry .mark img.notes-icon {
display: none;
margin-top: 6px;
float: right; }
#entry-list .entry:hover .mark img {
#entry-list .entry:hover .mark img.expand-entry, #entry-list .entry.show-notes img.collapse-entry {
display: inline; }
#entry-list .entry .mark-input, #entry-list .entry .timestamp-input {
#entry-list .entry .mark-input, #entry-list .entry .timestamp-input, #entry-list .entry.show-notes:hover img.expand-entry {
display: none; }
#entry-list .entry .notes, #entry-list .entry .notes-input {
#entry-list .entry .notes {
display: none;
font-family: Cantarell;
font-size: small;
padding-left: 1em; }
margin: 0;
padding: 0 0 0 1em; }
#entry-list .entry .notes :first-child {
margin-top: 0; }
#entry-list .entry .notes .notes-input, #entry-list .entry .notes pre, #entry-list .entry .notes code {
font-family: 'Anonymous Pro'; }
#entry-list .entry .notes * {
width: 100%; }
#entry-list .entry.edit-mark .mark-input {
display: inline-block; }
#entry-list .entry.edit-mark .mark {
@ -199,11 +225,20 @@ input {
display: inline-block; }
#entry-list .entry.edit-timestamp .timestamp {
display: none; }
#entry-list .entry .notes-input {
display: none; }
#entry-list .entry.edit-notes .notes-input {
display: block; }
#entry-list .entry.edit-notes .notes-text {
display: none; }
.drop-menu {
margin: 0;
padding: 0;
position: relative; }
.drop-menu * {
margin: 0;
padding: 0; }
.drop-menu .drop-menu-items {
display: none;
list-style: none;
@ -223,10 +258,14 @@ input {
background: #222222;
color: #eeeeee;
font-family: Bentham;
margin: 0;
padding: 1em 0;
padding: 1rem 0;
text-align: center;
width: 100%; }
.footer * {
margin: 0;
padding: 0; }
.footer a {
color: white;
text-decoration: none; }

View File

@ -13,33 +13,41 @@ $medBg: #CCC;
* {
color: inherit;
margin: 0;
padding: 0;
}
body {
color: $darkTxt;
width: 75%;
margin: auto;
padding: 0;
}
input {
border: solid thin lighten($darkTxt, 20%);
-webkit-box-shadow: inset 0px 2px 4px #CCC;
box-shadow: inset 0px 2px 4px #CCC;
margin: 0;
margin-bottom: 0.5em; // IE fix
padding: 0;
font-family: Cantarell;
}
#top {
background: $darkBg;
color: $lightTxt;
margin: 0;
opacity: 1;
padding: 0.5em 0; // IE Fix
padding: 0.5rem 0;
position: fixed;
top: 0px;
width: 75%;
z-index: 1;
* {
margin: 0;
padding: 0;
}
#fade-bar {
background: url('img/fade.png') repeat-x;
@ -103,6 +111,8 @@ input {
background: white;
color: $lightTxt;
margin: 0;
padding: 0;
opacity: 1;
position: fixed;
@ -114,6 +124,11 @@ input {
z-index: 10;
* {
margin: 0;
padding: 0;
}
div.container {
background: $darkBg;
@ -197,10 +212,10 @@ input {
#entry-list {
margin-top: 6em;
padding-bottom: 1em;
margin-top: 6rem;
padding-bottom: 1rem;
margin: 6em 0 0 0;
margin: 6rem 0 0 0;
padding-bottom: 1em 0 0 0;
padding-bottom: 1rem 0 0 0;
.day-seperator {
@ -208,22 +223,31 @@ input {
color: $darkBg;
font-family: Cantarell;
font-weight: bold;
margin-top: 1em;
margin: 1em 0 0 0;
margin: 1rem 0 0 0;
padding: 0 2em;
margin-top: 1rem;
padding: 0 2rem;
* {
margin: 0;
padding: 0;
}
h4, h5 { display: inline-block; }
h5 { color: #667; }
}
#new-entry {
margin-top: 0.5em;
margin: 0.5em 0 0 0;
padding: 0 2em;
margin-top: 0.5rem;
margin: 0.5rem 0 0 0;
padding: 0 2rem;
* {
margin: 0;
padding: 0;
}
}
.timestamp, .timestamp-input, .duration {
@ -242,33 +266,42 @@ input {
div { display: inline-block; }
.mark {
margin: 0;
padding: 0;
position: relative;
img.delete-icon {
* {
margin: 0;
padding: 0;
}
img.expand-entry, img.collapse-entry {
display: none;
left: -20px;
position: absolute;
top: 6px;
}
img.notes-icon {
display: none;
margin-top: 6px;
float: right;
}
}
&:hover .mark img { display: inline; }
&:hover .mark img.expand-entry, &.show-notes img.collapse-entry { display: inline; }
.mark-input, .timestamp-input { display: none; }
.mark-input, .timestamp-input,
&.show-notes:hover img.expand-entry { display: none; }
.notes, .notes-input {
.notes {
display: none;
font-family: Cantarell;
font-size: small;
padding-left: 1em;
margin: 0;
padding: 0 0 0 1em;
:first-child { margin-top: 0; }
.notes-input, pre, code { font-family: 'Anonymous Pro'; }
}
.notes * { width: 100%; }
&.edit-mark {
.mark-input { display: inline-block; }
.mark { display: none; }
@ -279,15 +312,26 @@ input {
.timestamp { display: none; }
}
.notes-input { display: none; }
&.edit-notes .notes-input { display: block; }
&.edit-notes .notes-text { display: none; }
}
}
.drop-menu {
margin: 0;
padding: 0;
position: relative;
* {
margin: 0;
padding: 0;
}
.drop-menu-items {
display: none;
list-style: none;
@ -316,11 +360,17 @@ input {
background: $darkBg;
color: $lightTxt;
font-family: Bentham;
margin: 0;
padding: 1em 0;
padding: 1rem 0;
text-align: center;
width: 100%;
* {
margin: 0;
padding: 0;
}
a {
color: lighten($lightTxt, 20%);
text-decoration: none;

BIN
www/img/br_down_icon_16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
www/img/br_down_icon_24.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
www/img/br_down_icon_32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
www/img/br_down_icon_48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
www/img/br_up_icon_16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
www/img/br_up_icon_24.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
www/img/br_up_icon_32.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
www/img/br_up_icon_48.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -2,7 +2,7 @@
<html>
<head>
<title>TimeStamper - Simple Time Tracking</title>
<link href='http://fonts.googleapis.com/css?family=Arvo|Bentham|Cuprum|Cantarell|Geo|Josefin+Sans' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Anonymous+Pro|Arvo|Bentham|Cantarell|Josefin+Sans' rel='stylesheet' type='text/css'>
<link rel="stylesheet" media="screen" href="/css/ts-screen.css" type="text/css"/>
<!-- Needed for IE, but I'm not sure if I'm going to support IE with this tool. -->
<!--<script type="text/javascript" src="/js/json2.js"></script>-->
@ -14,6 +14,7 @@
<!-- DEV -->
<script type="text/javascript" src="/js/jquery-1.5.js"></script>
<script type="text/javascript" src="/js/showdown.js"></script>
<script type="text/javascript" src="/js/underscore.js"></script>
<script type="text/javascript" src="/js/ICanHaz.js"></script>
<script type="text/javascript" src="/js/backbone.js"></script>
@ -83,16 +84,18 @@ out(YArg) ->
</script>
<script type="text/html" id="entryTemplate">
<div class="mark">
<img class="delete-icon" src="/img/round_delete_icon&16.png"/>
<img class="expand-entry" src="/img/br_down_icon_16.png"/>
<img class="collapse-entry" src="/img/br_up_icon_16.png"/>
<span>{{mark}}</span>
<img class="notes-icon" src="/img/notepad_2_icon&16.png"/>
</div>
<input class="mark-input" type="text" value="{{mark}}"/>
<div class="timestamp">{{start}}</div>
<input class="timestamp-input" type="text" value="{{timestamp}}"/>
<div class="duration">{{duration}}</div>
<div class="notes">{{notes}}</div>
<input class="notes-input" type="textarea" value="{{notes}}"/>
<div class="notes">
<div class="notes-text">{{notes}}</div>
<textarea class="notes-input" rows="10">{{notes}}</textarea>
</div>
</script>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
</head>

419
www/js/showdown-min.js vendored Normal file
View File

@ -0,0 +1,419 @@
/*
A A L Source code at:
T C A <http://www.attacklab.net/>
T K B
*/
var Showdown={};
Showdown.converter=function(){
var _1;
var _2;
var _3;
var _4=0;
this.makeHtml=function(_5){
_1=new Array();
_2=new Array();
_3=new Array();
_5=_5.replace(/~/g,"~T");
_5=_5.replace(/\$/g,"~D");
_5=_5.replace(/\r\n/g,"\n");
_5=_5.replace(/\r/g,"\n");
_5="\n\n"+_5+"\n\n";
_5=_6(_5);
_5=_5.replace(/^[ \t]+$/mg,"");
_5=_7(_5);
_5=_8(_5);
_5=_9(_5);
_5=_a(_5);
_5=_5.replace(/~D/g,"$$");
_5=_5.replace(/~T/g,"~");
return _5;
};
var _8=function(_b){
var _b=_b.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*<?(\S+?)>?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|\Z)/gm,function(_c,m1,m2,m3,m4){
m1=m1.toLowerCase();
_1[m1]=_11(m2);
if(m3){
return m3+m4;
}else{
if(m4){
_2[m1]=m4.replace(/"/g,"&quot;");
}
}
return "";
});
return _b;
};
var _7=function(_12){
_12=_12.replace(/\n/g,"\n\n");
var _13="p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del";
var _14="p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math";
_12=_12.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm,_15);
_12=_12.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math)\b[^\r]*?.*<\/\2>[ \t]*(?=\n+)\n)/gm,_15);
_12=_12.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,_15);
_12=_12.replace(/(\n\n[ ]{0,3}<!(--[^\r]*?--\s*)+>[ \t]*(?=\n{2,}))/g,_15);
_12=_12.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,_15);
_12=_12.replace(/\n\n/g,"\n");
return _12;
};
var _15=function(_16,m1){
var _18=m1;
_18=_18.replace(/\n\n/g,"\n");
_18=_18.replace(/^\n/,"");
_18=_18.replace(/\n+$/g,"");
_18="\n\n~K"+(_3.push(_18)-1)+"K\n\n";
return _18;
};
var _9=function(_19){
_19=_1a(_19);
var key=_1c("<hr />");
_19=_19.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,key);
_19=_19.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm,key);
_19=_19.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm,key);
_19=_1d(_19);
_19=_1e(_19);
_19=_1f(_19);
_19=_7(_19);
_19=_20(_19);
return _19;
};
var _21=function(_22){
_22=_23(_22);
_22=_24(_22);
_22=_25(_22);
_22=_26(_22);
_22=_27(_22);
_22=_28(_22);
_22=_11(_22);
_22=_29(_22);
_22=_22.replace(/ +\n/g," <br />\n");
return _22;
};
var _24=function(_2a){
var _2b=/(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|<!(--.*?--\s*)+>)/gi;
_2a=_2a.replace(_2b,function(_2c){
var tag=_2c.replace(/(.)<\/?code>(?=.)/g,"$1`");
tag=_2e(tag,"\\`*_");
return tag;
});
return _2a;
};
var _27=function(_2f){
_2f=_2f.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,_30);
_2f=_2f.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()<?(.*?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,_30);
_2f=_2f.replace(/(\[([^\[\]]+)\])()()()()()/g,_30);
return _2f;
};
var _30=function(_31,m1,m2,m3,m4,m5,m6,m7){
if(m7==undefined){
m7="";
}
var _39=m1;
var _3a=m2;
var _3b=m3.toLowerCase();
var url=m4;
var _3d=m7;
if(url==""){
if(_3b==""){
_3b=_3a.toLowerCase().replace(/ ?\n/g," ");
}
url="#"+_3b;
if(_1[_3b]!=undefined){
url=_1[_3b];
if(_2[_3b]!=undefined){
_3d=_2[_3b];
}
}else{
if(_39.search(/\(\s*\)$/m)>-1){
url="";
}else{
return _39;
}
}
}
url=_2e(url,"*_");
var _3e="<a href=\""+url+"\"";
if(_3d!=""){
_3d=_3d.replace(/"/g,"&quot;");
_3d=_2e(_3d,"*_");
_3e+=" title=\""+_3d+"\"";
}
_3e+=">"+_3a+"</a>";
return _3e;
};
var _26=function(_3f){
_3f=_3f.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,_40);
_3f=_3f.replace(/(!\[(.*?)\]\s?\([ \t]*()<?(\S+?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,_40);
return _3f;
};
var _40=function(_41,m1,m2,m3,m4,m5,m6,m7){
var _49=m1;
var _4a=m2;
var _4b=m3.toLowerCase();
var url=m4;
var _4d=m7;
if(!_4d){
_4d="";
}
if(url==""){
if(_4b==""){
_4b=_4a.toLowerCase().replace(/ ?\n/g," ");
}
url="#"+_4b;
if(_1[_4b]!=undefined){
url=_1[_4b];
if(_2[_4b]!=undefined){
_4d=_2[_4b];
}
}else{
return _49;
}
}
_4a=_4a.replace(/"/g,"&quot;");
url=_2e(url,"*_");
var _4e="<img src=\""+url+"\" alt=\""+_4a+"\"";
_4d=_4d.replace(/"/g,"&quot;");
_4d=_2e(_4d,"*_");
_4e+=" title=\""+_4d+"\"";
_4e+=" />";
return _4e;
};
var _1a=function(_4f){
_4f=_4f.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,function(_50,m1){
return _1c("<h1>"+_21(m1)+"</h1>");
});
_4f=_4f.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,function(_52,m1){
return _1c("<h2>"+_21(m1)+"</h2>");
});
_4f=_4f.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,function(_54,m1,m2){
var _57=m1.length;
return _1c("<h"+_57+">"+_21(m2)+"</h"+_57+">");
});
return _4f;
};
var _58;
var _1d=function(_59){
_59+="~0";
var _5a=/^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm;
if(_4){
_59=_59.replace(_5a,function(_5b,m1,m2){
var _5e=m1;
var _5f=(m2.search(/[*+-]/g)>-1)?"ul":"ol";
_5e=_5e.replace(/\n{2,}/g,"\n\n\n");
var _60=_58(_5e);
_60=_60.replace(/\s+$/,"");
_60="<"+_5f+">"+_60+"</"+_5f+">\n";
return _60;
});
}else{
_5a=/(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g;
_59=_59.replace(_5a,function(_61,m1,m2,m3){
var _65=m1;
var _66=m2;
var _67=(m3.search(/[*+-]/g)>-1)?"ul":"ol";
var _66=_66.replace(/\n{2,}/g,"\n\n\n");
var _68=_58(_66);
_68=_65+"<"+_67+">\n"+_68+"</"+_67+">\n";
return _68;
});
}
_59=_59.replace(/~0/,"");
return _59;
};
_58=function(_69){
_4++;
_69=_69.replace(/\n{2,}$/,"\n");
_69+="~0";
_69=_69.replace(/(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm,function(_6a,m1,m2,m3,m4){
var _6f=m4;
var _70=m1;
var _71=m2;
if(_70||(_6f.search(/\n{2,}/)>-1)){
_6f=_9(_72(_6f));
}else{
_6f=_1d(_72(_6f));
_6f=_6f.replace(/\n$/,"");
_6f=_21(_6f);
}
return "<li>"+_6f+"</li>\n";
});
_69=_69.replace(/~0/g,"");
_4--;
return _69;
};
var _1e=function(_73){
_73+="~0";
_73=_73.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,function(_74,m1,m2){
var _77=m1;
var _78=m2;
_77=_79(_72(_77));
_77=_6(_77);
_77=_77.replace(/^\n+/g,"");
_77=_77.replace(/\n+$/g,"");
_77="<pre><code>"+_77+"\n</code></pre>";
return _1c(_77)+_78;
});
_73=_73.replace(/~0/,"");
return _73;
};
var _1c=function(_7a){
_7a=_7a.replace(/(^\n+|\n+$)/g,"");
return "\n\n~K"+(_3.push(_7a)-1)+"K\n\n";
};
var _23=function(_7b){
_7b=_7b.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,function(_7c,m1,m2,m3,m4){
var c=m3;
c=c.replace(/^([ \t]*)/g,"");
c=c.replace(/[ \t]*$/g,"");
c=_79(c);
return m1+"<code>"+c+"</code>";
});
return _7b;
};
var _79=function(_82){
_82=_82.replace(/&/g,"&amp;");
_82=_82.replace(/</g,"&lt;");
_82=_82.replace(/>/g,"&gt;");
_82=_2e(_82,"*_{}[]\\",false);
return _82;
};
var _29=function(_83){
_83=_83.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g,"<strong>$2</strong>");
_83=_83.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,"<em>$2</em>");
return _83;
};
var _1f=function(_84){
_84=_84.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,function(_85,m1){
var bq=m1;
bq=bq.replace(/^[ \t]*>[ \t]?/gm,"~0");
bq=bq.replace(/~0/g,"");
bq=bq.replace(/^[ \t]+$/gm,"");
bq=_9(bq);
bq=bq.replace(/(^|\n)/g,"$1 ");
bq=bq.replace(/(\s*<pre>[^\r]+?<\/pre>)/gm,function(_88,m1){
var pre=m1;
pre=pre.replace(/^ /mg,"~0");
pre=pre.replace(/~0/g,"");
return pre;
});
return _1c("<blockquote>\n"+bq+"\n</blockquote>");
});
return _84;
};
var _20=function(_8b){
_8b=_8b.replace(/^\n+/g,"");
_8b=_8b.replace(/\n+$/g,"");
var _8c=_8b.split(/\n{2,}/g);
var _8d=new Array();
var end=_8c.length;
for(var i=0;i<end;i++){
var str=_8c[i];
if(str.search(/~K(\d+)K/g)>=0){
_8d.push(str);
}else{
if(str.search(/\S/)>=0){
str=_21(str);
str=str.replace(/^([ \t]*)/g,"<p>");
str+="</p>";
_8d.push(str);
}
}
}
end=_8d.length;
for(var i=0;i<end;i++){
while(_8d[i].search(/~K(\d+)K/)>=0){
var _91=_3[RegExp.$1];
_91=_91.replace(/\$/g,"$$$$");
_8d[i]=_8d[i].replace(/~K\d+K/,_91);
}
}
return _8d.join("\n\n");
};
var _11=function(_92){
_92=_92.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g,"&amp;");
_92=_92.replace(/<(?![a-z\/?\$!])/gi,"&lt;");
return _92;
};
var _25=function(_93){
_93=_93.replace(/\\(\\)/g,_94);
_93=_93.replace(/\\([`*_{}\[\]()>#+-.!])/g,_94);
return _93;
};
var _28=function(_95){
_95=_95.replace(/<((https?|ftp|dict):[^'">\s]+)>/gi,"<a href=\"$1\">$1</a>");
_95=_95.replace(/<(?:mailto:)?([-.\w]+\@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,function(_96,m1){
return _98(_a(m1));
});
return _95;
};
var _98=function(_99){
function char2hex(ch){
var _9b="0123456789ABCDEF";
var dec=ch.charCodeAt(0);
return (_9b.charAt(dec>>4)+_9b.charAt(dec&15));
}
var _9d=[function(ch){
return "&#"+ch.charCodeAt(0)+";";
},function(ch){
return "&#x"+char2hex(ch)+";";
},function(ch){
return ch;
}];
_99="mailto:"+_99;
_99=_99.replace(/./g,function(ch){
if(ch=="@"){
ch=_9d[Math.floor(Math.random()*2)](ch);
}else{
if(ch!=":"){
var r=Math.random();
ch=(r>0.9?_9d[2](ch):r>0.45?_9d[1](ch):_9d[0](ch));
}
}
return ch;
});
_99="<a href=\""+_99+"\">"+_99+"</a>";
_99=_99.replace(/">.+:/g,"\">");
return _99;
};
var _a=function(_a3){
_a3=_a3.replace(/~E(\d+)E/g,function(_a4,m1){
var _a6=parseInt(m1);
return String.fromCharCode(_a6);
});
return _a3;
};
var _72=function(_a7){
_a7=_a7.replace(/^(\t|[ ]{1,4})/gm,"~0");
_a7=_a7.replace(/~0/g,"");
return _a7;
};
var _6=function(_a8){
_a8=_a8.replace(/\t(?=\t)/g," ");
_a8=_a8.replace(/\t/g,"~A~B");
_a8=_a8.replace(/~B(.+?)~A/g,function(_a9,m1,m2){
var _ac=m1;
var _ad=4-_ac.length%4;
for(var i=0;i<_ad;i++){
_ac+=" ";
}
return _ac;
});
_a8=_a8.replace(/~A/g," ");
_a8=_a8.replace(/~B/g,"");
return _a8;
};
var _2e=function(_af,_b0,_b1){
var _b2="(["+_b0.replace(/([\[\]\\])/g,"\\$1")+"])";
if(_b1){
_b2="\\\\"+_b2;
}
var _b3=new RegExp(_b2,"g");
_af=_af.replace(_b3,_94);
return _af;
};
var _94=function(_b4,m1){
var _b6=m1.charCodeAt(0);
return "~E"+_b6+"E";
};
};
if(typeof exports!='undefined')exports.Showdown=Showdown;

View File

@ -61,13 +61,6 @@
//
// **************************************************
// GitHub Flavored Markdown modifications by Tekkub
// http://github.github.com/github-flavored-markdown/
//
// Modifications are tagged with "GFM"
// **************************************************
//
// Showdown namespace
//
@ -154,66 +147,10 @@ this.makeHtml = function(text) {
// attacklab: Restore tildes
text = text.replace(/~T/g,"~");
// ** GFM ** Auto-link URLs and emails
text = text.replace(/https?\:\/\/[^"\s\<\>]*[^.,;'">\:\s\<\>\)\]\!]/g, function(wholeMatch,matchIndex){
var left = text.slice(0, matchIndex), right = text.slice(matchIndex)
if (left.match(/<[^>]+$/) && right.match(/^[^>]*>/)) {return wholeMatch}
return "<a href='" + wholeMatch + "'>" + wholeMatch + "</a>";
});
text = text.replace(/[a-z0-9_\-+=.]+@[a-z0-9\-]+(\.[a-z0-9-]+)+/ig, function(wholeMatch){return "<a href='mailto:" + wholeMatch + "'>" + wholeMatch + "</a>";});
// ** GFM ** Auto-link sha1 if GitHub.nameWithOwner is defined
text = text.replace(/[a-f0-9]{40}/ig, function(wholeMatch,matchIndex){
if (typeof(GitHub) == "undefined" || typeof(GitHub.nameWithOwner) == "undefined") {return wholeMatch;}
var left = text.slice(0, matchIndex), right = text.slice(matchIndex)
if (left.match(/@$/) || (left.match(/<[^>]+$/) && right.match(/^[^>]*>/))) {return wholeMatch;}
return "<a href='http://github.com/" + GitHub.nameWithOwner + "/commit/" + wholeMatch + "'>" + wholeMatch.substring(0,7) + "</a>";
});
// ** GFM ** Auto-link user@sha1 if GitHub.nameWithOwner is defined
text = text.replace(/([a-z0-9_\-+=.]+)@([a-f0-9]{40})/ig, function(wholeMatch,username,sha,matchIndex){
if (typeof(GitHub) == "undefined" || typeof(GitHub.nameWithOwner) == "undefined") {return wholeMatch;}
GitHub.repoName = GitHub.repoName || _GetRepoName()
var left = text.slice(0, matchIndex), right = text.slice(matchIndex)
if (left.match(/\/$/) || (left.match(/<[^>]+$/) && right.match(/^[^>]*>/))) {return wholeMatch;}
return "<a href='http://github.com/" + username + "/" + GitHub.repoName + "/commit/" + sha + "'>" + username + "@" + sha.substring(0,7) + "</a>";
});
// ** GFM ** Auto-link user/repo@sha1
text = text.replace(/([a-z0-9_\-+=.]+\/[a-z0-9_\-+=.]+)@([a-f0-9]{40})/ig, function(wholeMatch,repo,sha){
return "<a href='http://github.com/" + repo + "/commit/" + sha + "'>" + repo + "@" + sha.substring(0,7) + "</a>";
});
// ** GFM ** Auto-link #issue if GitHub.nameWithOwner is defined
text = text.replace(/#([0-9]+)/ig, function(wholeMatch,issue,matchIndex){
if (typeof(GitHub) == "undefined" || typeof(GitHub.nameWithOwner) == "undefined") {return wholeMatch;}
var left = text.slice(0, matchIndex), right = text.slice(matchIndex)
if (left == "" || left.match(/[a-z0-9_\-+=.]$/) || (left.match(/<[^>]+$/) && right.match(/^[^>]*>/))) {return wholeMatch;}
return "<a href='http://github.com/" + GitHub.nameWithOwner + "/issues/#issue/" + issue + "'>" + wholeMatch + "</a>";
});
// ** GFM ** Auto-link user#issue if GitHub.nameWithOwner is defined
text = text.replace(/([a-z0-9_\-+=.]+)#([0-9]+)/ig, function(wholeMatch,username,issue,matchIndex){
if (typeof(GitHub) == "undefined" || typeof(GitHub.nameWithOwner) == "undefined") {return wholeMatch;}
GitHub.repoName = GitHub.repoName || _GetRepoName()
var left = text.slice(0, matchIndex), right = text.slice(matchIndex)
if (left.match(/\/$/) || (left.match(/<[^>]+$/) && right.match(/^[^>]*>/))) {return wholeMatch;}
return "<a href='http://github.com/" + username + "/" + GitHub.repoName + "/issues/#issue/" + issue + "'>" + wholeMatch + "</a>";
});
// ** GFM ** Auto-link user/repo#issue
text = text.replace(/([a-z0-9_\-+=.]+\/[a-z0-9_\-+=.]+)#([0-9]+)/ig, function(wholeMatch,repo,issue){
return "<a href='http://github.com/" + repo + "/issues/#issue/" + issue + "'>" + wholeMatch + "</a>";
});
return text;
}
var _GetRepoName = function() {
return GitHub.nameWithOwner.match(/^.+\/(.+)$/)[1]
}
var _StripLinkDefinitions = function(text) {
//
// Strips link definitions from text, stores the URLs and titles in
@ -724,10 +661,10 @@ var _DoHeaders = function(text) {
// --------
//
text = text.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,
function(wholeMatch,m1){return hashBlock("<h1>" + _RunSpanGamut(m1) + "</h1>");});
function(wholeMatch,m1){return hashBlock('<h1 id="' + headerId(m1) + '">' + _RunSpanGamut(m1) + "</h1>");});
text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,
function(matchFound,m1){return hashBlock("<h2>" + _RunSpanGamut(m1) + "</h2>");});
function(matchFound,m1){return hashBlock('<h2 id="' + headerId(m1) + '">' + _RunSpanGamut(m1) + "</h2>");});
// atx-style headers:
// # Header 1
@ -751,9 +688,12 @@ var _DoHeaders = function(text) {
text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,
function(wholeMatch,m1,m2) {
var h_level = m1.length;
return hashBlock("<h" + h_level + ">" + _RunSpanGamut(m2) + "</h" + h_level + ">");
return hashBlock("<h" + h_level + ' id="' + headerId(m2) + '">' + _RunSpanGamut(m2) + "</h" + h_level + ">");
});
function headerId(m) {
return m.replace(/[^\w]/g, '').toLowerCase();
}
return text;
}
@ -1044,7 +984,6 @@ var _DoItalicsAndBold = function(text) {
text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g,
"<strong>$2</strong>");
text = text.replace(/(\w)_(\w)/g, "$1~E95E$2") // ** GFM ** "~E95E" == escaped "_"
text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,
"<em>$2</em>");
@ -1126,7 +1065,6 @@ var _FormParagraphs = function(text) {
}
else if (str.search(/\S/) >= 0) {
str = _RunSpanGamut(str);
str = str.replace(/\n/g,"<br />"); // ** GFM **
str = str.replace(/^([ \t]*)/g,"<p>");
str += "</p>"
grafsOut.push(str);
@ -1359,3 +1297,6 @@ var escapeCharacters_callback = function(wholeMatch,m1) {
}
} // end of Showdown.converter
// export
if (typeof exports != 'undefined') exports.Showdown = Showdown;

View File

@ -91,18 +91,30 @@ $(document).ready(function(){
className: 'entry',
notesCache: false,
events: {
"click img.notes-icon" : "toggleNotes",
"click img.expand-entry" : "showNotes",
"click img.collapse-entry" : "hideNotes",
"dblclick div.mark" : "editMark",
"dblclick div.timestamp" : "editTimestamp",
"dblclick div.notes" : "editNotes",
"keypress .mark-input" : "updateOnEnter",
"keypress .timestamp-input" : "updateOnEnter"
"keypress .timestamp-input" : "updateOnEnter",
"keypress .notes-input" : "updateOnCtrlEnter",
"blur .mark-input" : "close",
"blur .timestamp-input" : "close",
"blur .notes-input" : "close"
},
initialize: function(options) {
_.bindAll(this, 'render', 'close', 'editTImestamp',
'editMark', 'updateOnEnter', 'getViewModel', 'toggleNotes');
this.model.bind('change', this.render);
_.bindAll(this, 'render', 'close', 'editTImestamp', 'editMark',
'update', 'updateOnEnter', 'updateOnCtrlEnter', 'getViewModel',
'renderNotes', 'showNotes', 'hideNotes');
this.markdownConverter = options.markdownConverter;
this.model.bind('change', this.update);
this.model.view = this;
this.nextModel = options.nextModel;
@ -113,9 +125,12 @@ $(document).ready(function(){
* HTML content. Add new `blur` listeners to the input fields.
*/
render: function() {
// render the HTML
$(this.el).html(ich.entryTemplate(this.getViewModel()));
this.$(".mark-input").bind('blur', this.close);
this.$(".timestamp-input").bind('blur', this.close);
// invalidate the notes display cache
this.notesCache = false;
return this;
},
@ -130,9 +145,19 @@ $(document).ready(function(){
this.$('.timestamp').text(data.start);
this.$('.timestamp-input').val(data.timestamp);
this.$('.duration').text(data.duration);
this.$('.notes-text').html(this.renderNotes(data.notes));
this.$('.notes-input').val(data.notes);
return this;
},
renderNotes: function(source) {
if (!this.notesCache) {
this.notesCache = this.markdownConverter.makeHtml(source);
}
return this.notesCache
},
editMark: function() {
$(this.el).addClass('edit-mark');
this.$('.mark-input').focus();
@ -145,6 +170,18 @@ $(document).ready(function(){
return this;
},
editNotes: function() {
// invalidate notes HTML cache
this.notesCache = false;
// show notes textarea, hide display
$(this.el).addClass('edit-notes');
// focus input
this.$('.notes-input').focus();
return this;
},
/**
* Translate the model data into a form suitable to be displayed.
* @return a map including display-able `start` and `duration` values.
@ -156,20 +193,21 @@ $(document).ready(function(){
var tsDate = new Date(data.timestamp);
data.start = this.formatStart(tsDate);
data.duration = this.formatDuration(this.model, this.nextModel);
data.notes = data.notes ? data.notes : '*No notes for this entry.*';
return data;
},
/** Close editable fields. */
close: function() {
$(this.el).removeClass('edit-mark edit-timestamp');
$(this.el).removeClass('edit-mark edit-timestamp edit-notes');
},
/** Persist changes in input fields. */
save: function() {
this.model.save({
mark: this.$('.mark-input').val(),
timestamp: this.$('.timestamp-input').val()});
this.update();
timestamp: this.$('.timestamp-input').val(),
notes: this.$('.notes-input').val()});
},
/** Event handler for keypresses on entry input fields. */
@ -177,6 +215,10 @@ $(document).ready(function(){
if(e.keyCode == 13) { this.save(); this.close(); }
},
updateOnCtrlEnter: function(e) {
if (e.keyCode == 10) { this.save(); this.close(); }
},
/**
* Get the display-able start time from the entry timestamp.
* @param startDate a Date object, the entry timestamp.
@ -229,8 +271,19 @@ $(document).ready(function(){
min + "m ";
},
toggleNotes: function() {
this.$('.notes').slideToggle();
showNotes: function() {
if (!this.notesCache) {
this.$('.notes-text').html(
this.renderNotes(this.model.get('notes')))
}
this.$('.notes').slideDown();
$(this.el).addClass('show-notes');
},
hideNotes: function() {
this.$('.notes').slideUp();
$(this.el).removeClass('show-notes');
}
});
@ -243,15 +296,24 @@ $(document).ready(function(){
},
initialize: function() {
_.bindAll(this, 'addOne', 'createNewEntry', 'render');
_.bindAll(this, 'addOne', 'createNewEntry', 'render', 'renderOne');
this.collection.bind('add', this.addOne);
this.collection.bind('refresh', this.render);
this.collection.view = this;
this.entryContainer = this.$("#entries")
this.markdownConverter = new Showdown.converter();
},
addOne: function(entry, nextEntry) {
if (!entry.view) { new TS.EntryView({model: entry}); }
addOne: function(entry) {
var lastEntry = this.collection.at(this.collection.length - 2);
lastEntry.view.nextModel = entry;
lastEntry.view.update();
this.renderOne(entry, null);
},
renderOne: function(entry, nextEntry) {
if (!entry.view) { new TS.EntryView(
{model: entry, markdownConverter: this.markdownConverter}); }
entry.view.nextModel = nextEntry
this.entryContainer.prepend(entry.view.render().el);
},
@ -280,7 +342,7 @@ $(document).ready(function(){
var entry = this.collection.at(i);
var nextEntry = (i + 1 < len ? this.collection.at(i + 1) : null);
this.addOne(entry, nextEntry);
this.renderOne(entry, nextEntry);
}
}
});