Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
C
coopwire-hypothesis
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
孙灵跃 Leon Sun
coopwire-hypothesis
Commits
4abf518a
Commit
4abf518a
authored
Sep 24, 2014
by
Aron Carroll
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1479 from hypothesis/markdown
Introduce zero-dependency Markdown Editor.
parents
fa8e94ba
dbd633b6
Changes
5
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
332 additions
and
36 deletions
+332
-36
directives.coffee
h/static/scripts/directives.coffee
+0
-32
markdown.coffee
h/static/scripts/directives/markdown.coffee
+281
-0
annotations.scss
h/static/styles/annotations.scss
+7
-4
common.scss
h/static/styles/common.scss
+1
-0
markdown-editor.scss
h/static/styles/markdown-editor.scss
+43
-0
No files found.
h/static/scripts/directives.coffee
View file @
4abf518a
...
...
@@ -63,37 +63,6 @@ formValidate = ->
ctrl
.
submit
()
markdown
=
[
'$filter'
,
'$timeout'
,
(
$filter
,
$timeout
)
->
link
:
(
scope
,
elem
,
attr
,
ctrl
)
->
return
unless
ctrl
?
input
=
elem
.
find
(
'textarea'
)
output
=
elem
.
find
(
'div'
)
# Re-render the markdown when the view needs updating.
ctrl
.
$render
=
->
input
.
val
(
ctrl
.
$viewValue
or
''
)
scope
.
rendered
=
(
$filter
'converter'
)
(
ctrl
.
$viewValue
or
''
)
# React to the changes to the text area
input
.
bind
'blur change keyup'
,
->
ctrl
.
$setViewValue
input
.
val
()
scope
.
$digest
()
# Auto-focus the input box when the widget becomes editable.
# Re-render when it becomes uneditable.
scope
.
$watch
'readonly'
,
(
readonly
)
->
ctrl
.
$render
()
unless
readonly
then
$timeout
->
input
.
focus
()
require
:
'?ngModel'
scope
:
readonly
:
'@'
required
:
'@'
templateUrl
:
'markdown.html'
]
privacy
=
->
levels
=
[
'Public'
,
'Only Me'
]
...
...
@@ -246,7 +215,6 @@ match = ->
angular
.
module
(
'h.directives'
,
imports
)
.
directive
(
'formInput'
,
formInput
)
.
directive
(
'formValidate'
,
formValidate
)
.
directive
(
'markdown'
,
markdown
)
.
directive
(
'privacy'
,
privacy
)
.
directive
(
'tabReveal'
,
tabReveal
)
.
directive
(
'showAccount'
,
showAccount
)
...
...
h/static/scripts/directives/markdown.coffee
0 → 100644
View file @
4abf518a
###*
# @ngdoc directive
# @name markdown
# @restrict A
# @description
# This directive controls both the rendering and display of markdown, as well as
# the markdown editor.
###
markdown
=
[
'$filter'
,
'$timeout'
,
(
$filter
,
$timeout
)
->
link
:
(
scope
,
elem
,
attr
,
ctrl
)
->
return
unless
ctrl
?
inputEl
=
elem
.
find
(
'.js-markdown-input'
)
input
=
elem
.
find
(
'.js-markdown-input'
)[
0
]
output
=
elem
.
find
(
'.js-markdown-preview'
)[
0
]
userSelection
=
->
if
input
.
selectionStart
!=
undefined
startPos
=
input
.
selectionStart
endPos
=
input
.
selectionEnd
selectedText
=
input
.
value
.
substring
(
startPos
,
endPos
)
textBefore
=
input
.
value
.
substring
(
0
,
(
startPos
))
textAfter
=
input
.
value
.
substring
(
endPos
)
selection
=
{
before
:
textBefore
after
:
textAfter
selection
:
selectedText
start
:
startPos
end
:
endPos
}
return
selection
insertMarkup
=
(
value
,
selectionStart
,
selectionEnd
)
->
# New value is set for the input
input
.
value
=
value
# A new selection is set, or the cursor is positioned inside the input.
input
.
selectionStart
=
selectionStart
input
.
selectionEnd
=
selectionEnd
# Focus the input
input
.
focus
()
applyInlineMarkup
=
(
markup
,
innertext
)
->
text
=
userSelection
()
if
text
.
selection
==
""
newtext
=
text
.
before
+
markup
+
innertext
+
markup
+
text
.
after
start
=
(
text
.
before
+
markup
).
length
end
=
(
text
.
before
+
innertext
+
markup
).
length
insertMarkup
(
newtext
,
start
,
end
)
else
# Check to see if markup has already been applied before to the selection.
slice1
=
text
.
before
.
slice
(
text
.
before
.
length
-
markup
.
length
)
slice2
=
text
.
after
.
slice
(
0
,
markup
.
length
)
if
slice1
==
markup
and
slice2
==
markup
# Remove markup
newtext
=
(
text
.
before
.
slice
(
0
,
(
text
.
before
.
length
-
markup
.
length
))
+
text
.
selection
+
text
.
after
.
slice
(
markup
.
length
)
)
start
=
text
.
before
.
length
-
markup
.
length
end
=
(
text
.
before
+
text
.
selection
).
length
-
markup
.
length
insertMarkup
(
newtext
,
start
,
end
)
else
# Apply markup
newtext
=
text
.
before
+
markup
+
text
.
selection
+
markup
+
text
.
after
start
=
(
text
.
before
+
markup
).
length
end
=
(
text
.
before
+
text
.
selection
+
markup
).
length
insertMarkup
(
newtext
,
start
,
end
)
scope
.
insertBold
=
->
applyInlineMarkup
(
"**"
,
"Bold"
)
scope
.
insertItalic
=
->
applyInlineMarkup
(
"*"
,
"Italic"
)
scope
.
insertMath
=
->
applyInlineMarkup
(
"$$"
,
"LaTex"
)
scope
.
insertLink
=
->
text
=
userSelection
()
if
text
.
selection
==
""
newtext
=
text
.
before
+
"[Link Text](https://example.com)"
+
text
.
after
start
=
text
.
before
.
length
+
1
end
=
text
.
before
.
length
+
10
insertMarkup
(
newtext
,
start
,
end
)
else
# Check to see if markup has already been applied to avoid double presses.
if
text
.
selection
==
"Link Text"
or
text
.
selection
==
"https://example.com"
return
newtext
=
text
.
before
+
'['
+
text
.
selection
+
'](https://example.com)'
+
text
.
after
start
=
(
text
.
before
+
text
.
selection
).
length
+
3
end
=
(
text
.
before
+
text
.
selection
).
length
+
22
insertMarkup
(
newtext
,
start
,
end
)
scope
.
insertIMG
=
->
text
=
userSelection
()
if
text
.
selection
==
""
newtext
=
text
.
before
+
"![Image Description](https://yourimage.jpg)"
+
text
.
after
start
=
text
.
before
.
length
+
21
end
=
text
.
before
.
length
+
42
insertMarkup
(
newtext
,
start
,
end
)
else
# Check to see if markup has already been applied to avoid double presses.
if
text
.
selection
==
"https://yourimage.jpg"
return
newtext
=
text
.
before
+
'!['
+
text
.
selection
+
'](https://yourimage.jpg)'
+
text
.
after
start
=
(
text
.
before
+
text
.
selection
).
length
+
4
end
=
(
text
.
before
+
text
.
selection
).
length
+
25
insertMarkup
(
newtext
,
start
,
end
)
scope
.
applyBlockMarkup
=
(
markup
)
->
text
=
userSelection
()
if
text
.
selection
!=
""
newstring
=
""
index
=
text
.
before
.
length
if
index
==
0
# The selection takes place at the very start of the input
for
char
in
text
.
selection
if
char
==
"
\n
"
newstring
=
newstring
+
"
\n
"
+
markup
else
if
index
==
0
newstring
=
newstring
+
markup
+
char
else
newstring
=
newstring
+
char
index
+=
1
else
newlinedetected
=
false
if
input
.
value
.
substring
(
index
-
1
).
charAt
(
0
)
==
"
\n
"
# Look to see if the selection falls at the beginning of a new line.
newstring
=
newstring
+
markup
newlinedetected
=
true
for
char
in
text
.
selection
if
char
==
"
\n
"
newstring
=
newstring
+
"
\n
"
+
markup
newlinedetected
=
true
else
newstring
=
newstring
+
char
index
+=
1
if
not
newlinedetected
# Edge case: The selection does not include any new lines and does not start at 0.
# We need to find the newline before the currently selected text and add markup there.
i
=
0
indexoflastnewline
=
undefined
newstring
=
""
for
char
in
(
text
.
before
+
text
.
selection
)
if
char
==
"
\n
"
indexoflastnewline
=
i
newstring
=
newstring
+
char
i
++
if
indexoflastnewline
==
undefined
# The partial selection happens to fall on the firstline
newstring
=
markup
+
newstring
else
newstring
=
(
newstring
.
substring
(
0
,
(
indexoflastnewline
+
1
))
+
markup
+
newstring
.
substring
(
indexoflastnewline
+
1
)
)
value
=
newstring
+
text
.
after
start
=
(
text
.
before
+
markup
).
length
end
=
(
text
.
before
+
text
.
selection
+
markup
).
length
insertMarkup
(
value
,
start
,
end
)
return
# Sets input value and selection for cases where there are new lines in the selection
# or the selection is at the start
value
=
text
.
before
+
newstring
+
text
.
after
start
=
(
text
.
before
+
newstring
).
length
end
=
(
text
.
before
+
newstring
).
length
insertMarkup
(
value
,
start
,
end
)
else
if
input
.
value
.
substring
((
text
.
start
-
1
),
text
.
start
)
==
"
\n
"
# Edge case, no selection, the cursor is on a new line.
value
=
text
.
before
+
markup
+
text
.
selection
+
text
.
after
start
=
(
text
.
before
+
markup
).
length
end
=
(
text
.
before
+
markup
).
length
insertMarkup
(
value
,
start
,
end
)
else
# No selection, cursor is not on new line.
# Check to see if markup has already been inserted.
if
text
.
before
.
slice
(
text
.
before
.
length
-
markup
.
length
)
==
markup
newtext
=
(
text
.
before
.
substring
(
0
,
(
index
))
+
"
\n
"
+
text
.
before
.
substring
(
index
+
1
+
markup
.
length
)
+
text
.
after
)
i
=
0
for
char
in
text
.
before
if
char
==
"
\n
"
and
i
!=
0
index
=
i
i
+=
1
if
!
index
# If the line of text happens to fall on the first line and index is not set.
# Check to see if markup has already been inserted and undo it.
if
text
.
before
.
slice
(
0
,
markup
.
length
)
==
markup
newtext
=
text
.
before
.
substring
(
markup
.
length
)
+
text
.
after
start
=
text
.
before
.
length
-
markup
.
length
end
=
text
.
before
.
length
-
markup
.
length
insertMarkup
(
newtext
,
start
,
end
)
else
newtext
=
markup
+
text
.
before
.
substring
(
0
)
+
text
.
after
start
=
(
text
.
before
+
markup
).
length
end
=
(
text
.
before
+
markup
).
length
insertMarkup
(
newtext
,
start
,
end
)
# Check to see if markup has already been inserted and undo it.
else
if
text
.
before
.
slice
((
index
+
1
),
(
index
+
1
+
markup
.
length
))
==
markup
newtext
=
(
text
.
before
.
substring
(
0
,
(
index
))
+
"
\n
"
+
text
.
before
.
substring
(
index
+
1
+
markup
.
length
)
+
text
.
after
)
start
=
text
.
before
.
length
-
markup
.
length
end
=
text
.
before
.
length
-
markup
.
length
insertMarkup
(
newtext
,
start
,
end
)
else
newtext
=
(
text
.
before
.
substring
(
0
,
(
index
))
+
"
\n
"
+
markup
+
text
.
before
.
substring
(
index
+
1
)
+
text
.
after
)
start
=
(
text
.
before
+
markup
).
length
end
=
(
text
.
before
+
markup
).
length
insertMarkup
(
newtext
,
start
,
end
)
scope
.
insertList
=
->
scope
.
applyBlockMarkup
(
"* "
)
scope
.
insertNumList
=
->
scope
.
applyBlockMarkup
(
"1. "
)
scope
.
insertQuote
=
->
scope
.
applyBlockMarkup
(
"> "
)
scope
.
insertCode
=
->
scope
.
applyBlockMarkup
(
" "
)
# Keyboard shortcuts for bold, italic, and link.
elem
.
on
keydown
:
(
e
)
->
shortcuts
=
66
:
scope
.
insertBold
73
:
scope
.
insertItalic
75
:
scope
.
insertLink
shortcut
=
shortcuts
[
e
.
keyCode
]
if
shortcut
&&
(
e
.
ctrlKey
||
e
.
metaKey
)
e
.
preventDefault
()
shortcut
()
scope
.
preview
=
false
scope
.
togglePreview
=
->
if
!
scope
.
readonly
scope
.
preview
=
!
scope
.
preview
if
scope
.
preview
output
.
style
.
height
=
input
.
style
.
height
ctrl
.
$render
()
else
input
.
style
.
height
=
output
.
style
.
height
$timeout
->
inputEl
.
focus
()
# Re-render the markdown when the view needs updating.
ctrl
.
$render
=
->
inputEl
.
val
(
ctrl
.
$viewValue
or
''
)
scope
.
rendered
=
(
$filter
'converter'
)
(
ctrl
.
$viewValue
or
''
)
# React to the changes to the input
inputEl
.
bind
'blur change keyup'
,
->
ctrl
.
$setViewValue
inputEl
.
val
()
scope
.
$digest
()
# Reset height of output div incase it has been changed.
# Re-render when it becomes uneditable.
# Auto-focus the input box when the widget becomes editable.
scope
.
$watch
'readonly'
,
(
readonly
)
->
scope
.
preview
=
false
output
.
style
.
height
=
""
ctrl
.
$render
()
unless
readonly
then
$timeout
->
inputEl
.
focus
()
require
:
'?ngModel'
restrict
:
'A'
scope
:
readonly
:
'@'
required
:
'@'
templateUrl
:
'markdown.html'
]
angular
.
module
(
'h.directives'
).
directive
(
'markdown'
,
markdown
)
h/static/styles/annotations.scss
View file @
4abf518a
...
...
@@ -42,12 +42,15 @@
font-size
:
.923em
;
}
.icon-markdown
{
color
:
$text-color
;
line-height
:
1
.4
;
margin-left
:
.5em
;
//PRIVACY CONTROL////////////////////////////
privacy
{
position
:
relative
;
top
:
2px
;
}
//MAGICONTROL////////////////////////////////
.magicontrol
{
margin-right
:
.8em
;
color
:
$gray-lighter
;
...
...
h/static/styles/common.scss
View file @
4abf518a
...
...
@@ -8,6 +8,7 @@ $headings-color: $text-color;
@import
'annotations'
;
@import
'forms'
;
@import
'markdown-editor'
;
@import
'spinner'
;
@import
'responsive'
;
@import
'threads'
;
...
...
h/static/styles/markdown-editor.scss
0 → 100644
View file @
4abf518a
@import
'compass/css3/user-interface'
;
//MARKDOWN EDITOR //////////////////////////
.markdown-preview
{
overflow
:
auto
;
border
:
.1em
solid
$gray-lighter
;
background-color
:
$gray-lightest
;
min-height
:
120px
;
padding-left
:
0
.9em
;
resize
:
vertical
;
}
.markdown-tools
{
border-top
:
.1em
solid
#D3D3D3
;
border-left
:
.1em
solid
#D3D3D3
;
border-right
:
.1em
solid
#D3D3D3
;
border-radius
:
.15em
.15em
0
0
;
width
:
100%
;
margin-bottom
:
-.1em
;
padding
:
.7em
.7em
.7em
.5em
;
@include
user-select
(
none
);
&
.disable
{
.markdown-tools-button
{
color
:
$gray-lighter
;
pointer-events
:
none
;
}
}
.markdown-tools-button
{
padding
:
.4em
;}
.markdown-tools-button
,
.markdown-tools-toggle
,
.icon-markdown
{
color
:
$gray
;
&
:hover
{
color
:
black
;
}
}
.markdown-preview-toggle
{
float
:
right
;
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment