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
7187cf1c
Commit
7187cf1c
authored
Mar 16, 2017
by
Robert Knight
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Convert `<markdown>` to a component
parent
5b36b58c
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
177 additions
and
178 deletions
+177
-178
app.js
src/sidebar/app.js
+1
-1
markdown.js
src/sidebar/directive/markdown.js
+156
-158
markdown-test.js
src/sidebar/directive/test/markdown-test.js
+2
-2
markdown.html
src/sidebar/templates/markdown.html
+18
-17
No files found.
src/sidebar/app.js
View file @
7187cf1c
...
@@ -145,6 +145,7 @@ module.exports = angular.module('h', [
...
@@ -145,6 +145,7 @@ module.exports = angular.module('h', [
.
component
(
'loggedoutMessage'
,
require
(
'./components/loggedout-message'
))
.
component
(
'loggedoutMessage'
,
require
(
'./components/loggedout-message'
))
.
component
(
'loginControl'
,
require
(
'./components/login-control'
))
.
component
(
'loginControl'
,
require
(
'./components/login-control'
))
.
component
(
'loginForm'
,
require
(
'./components/login-form'
).
component
)
.
component
(
'loginForm'
,
require
(
'./components/login-form'
).
component
)
.
component
(
'markdown'
,
require
(
'./directive/markdown'
))
.
component
(
'moderationBanner'
,
require
(
'./components/moderation-banner'
))
.
component
(
'moderationBanner'
,
require
(
'./components/moderation-banner'
))
.
component
(
'publishAnnotationBtn'
,
require
(
'./components/publish-annotation-btn'
))
.
component
(
'publishAnnotationBtn'
,
require
(
'./components/publish-annotation-btn'
))
.
component
(
'searchInput'
,
require
(
'./components/search-input'
))
.
component
(
'searchInput'
,
require
(
'./components/search-input'
))
...
@@ -161,7 +162,6 @@ module.exports = angular.module('h', [
...
@@ -161,7 +162,6 @@ module.exports = angular.module('h', [
.
component
(
'timestamp'
,
require
(
'./components/timestamp'
))
.
component
(
'timestamp'
,
require
(
'./components/timestamp'
))
// These should use `component()` but will require some changes.
// These should use `component()` but will require some changes.
.
directive
(
'markdown'
,
require
(
'./directive/markdown'
))
.
directive
(
'topBar'
,
require
(
'./directive/top-bar'
))
.
directive
(
'topBar'
,
require
(
'./directive/top-bar'
))
.
directive
(
'formInput'
,
require
(
'./directive/form-input'
))
.
directive
(
'formInput'
,
require
(
'./directive/form-input'
))
...
...
src/sidebar/directive/markdown.js
View file @
7187cf1c
...
@@ -7,169 +7,167 @@ var mediaEmbedder = require('../media-embedder');
...
@@ -7,169 +7,167 @@ var mediaEmbedder = require('../media-embedder');
var
renderMarkdown
=
require
(
'../render-markdown'
);
var
renderMarkdown
=
require
(
'../render-markdown'
);
var
scopeTimeout
=
require
(
'../util/scope-timeout'
);
var
scopeTimeout
=
require
(
'../util/scope-timeout'
);
function
MarkdownController
(
$element
,
$sanitize
,
$scope
)
{
var
input
=
$element
[
0
].
querySelector
(
'.js-markdown-input'
);
var
output
=
$element
[
0
].
querySelector
(
'.js-markdown-preview'
);
var
self
=
this
;
/**
* Transform the editor's input field with an editor command.
*/
function
updateState
(
newStateFn
)
{
var
newState
=
newStateFn
({
text
:
input
.
value
,
selectionStart
:
input
.
selectionStart
,
selectionEnd
:
input
.
selectionEnd
,
});
input
.
value
=
newState
.
text
;
input
.
selectionStart
=
newState
.
selectionStart
;
input
.
selectionEnd
=
newState
.
selectionEnd
;
// The input field currently loses focus when the contents are
// changed. This re-focuses the input field but really it should
// happen automatically.
input
.
focus
();
self
.
onEditText
({
text
:
input
.
value
});
}
function
focusInput
()
{
// When the visibility of the editor changes, focus it.
// A timeout is used so that focus() is not called until
// the visibility change has been applied (by adding or removing
// the relevant CSS classes)
scopeTimeout
(
$scope
,
function
()
{
input
.
focus
();
},
0
);
}
this
.
insertBold
=
function
()
{
updateState
(
function
(
state
)
{
return
commands
.
toggleSpanStyle
(
state
,
'**'
,
'**'
,
'Bold'
);
});
};
this
.
insertItalic
=
function
()
{
updateState
(
function
(
state
)
{
return
commands
.
toggleSpanStyle
(
state
,
'*'
,
'*'
,
'Italic'
);
});
};
this
.
insertMath
=
function
()
{
updateState
(
function
(
state
)
{
var
before
=
state
.
text
.
slice
(
0
,
state
.
selectionStart
);
if
(
before
.
length
===
0
||
before
.
slice
(
-
1
)
===
'
\
n'
||
before
.
slice
(
-
2
)
===
'$$'
)
{
return
commands
.
toggleSpanStyle
(
state
,
'$$'
,
'$$'
,
'Insert LaTeX'
);
}
else
{
return
commands
.
toggleSpanStyle
(
state
,
'
\\
('
,
'
\\
)'
,
'Insert LaTeX'
);
}
});
};
this
.
insertLink
=
function
()
{
updateState
(
function
(
state
)
{
return
commands
.
convertSelectionToLink
(
state
);
});
};
this
.
insertIMG
=
function
()
{
updateState
(
function
(
state
)
{
return
commands
.
convertSelectionToLink
(
state
,
commands
.
LinkType
.
IMAGE_LINK
);
});
};
this
.
insertList
=
function
()
{
updateState
(
function
(
state
)
{
return
commands
.
toggleBlockStyle
(
state
,
'* '
);
});
};
this
.
insertNumList
=
function
()
{
updateState
(
function
(
state
)
{
return
commands
.
toggleBlockStyle
(
state
,
'1. '
);
});
};
this
.
insertQuote
=
function
()
{
updateState
(
function
(
state
)
{
return
commands
.
toggleBlockStyle
(
state
,
'> '
);
});
};
// Keyboard shortcuts for bold, italic, and link.
$element
.
on
(
'keydown'
,
function
(
e
)
{
var
shortcuts
=
{
66
:
self
.
insertBold
,
73
:
self
.
insertItalic
,
75
:
self
.
insertLink
,
};
var
shortcut
=
shortcuts
[
e
.
keyCode
];
if
(
shortcut
&&
(
e
.
ctrlKey
||
e
.
metaKey
))
{
e
.
preventDefault
();
shortcut
();
}
});
this
.
preview
=
false
;
this
.
togglePreview
=
function
()
{
self
.
preview
=
!
self
.
preview
;
};
var
handleInputChange
=
debounce
(
function
()
{
$scope
.
$apply
(
function
()
{
self
.
onEditText
({
text
:
input
.
value
});
});
},
100
);
input
.
addEventListener
(
'input'
,
handleInputChange
);
// Re-render the markdown when the view needs updating.
$scope
.
$watch
(
'vm.text'
,
function
()
{
output
.
innerHTML
=
renderMarkdown
(
self
.
text
||
''
,
$sanitize
);
mediaEmbedder
.
replaceLinksWithEmbeds
(
output
);
});
this
.
showEditor
=
function
()
{
return
!
self
.
readOnly
&&
!
self
.
preview
;
};
// Exit preview mode when leaving edit mode
$scope
.
$watch
(
'vm.readOnly'
,
function
()
{
self
.
preview
=
false
;
});
$scope
.
$watch
(
'vm.showEditor()'
,
function
(
show
)
{
if
(
show
)
{
input
.
value
=
self
.
text
||
''
;
focusInput
();
}
});
}
/**
/**
* @ngdoc directive
* @name markdown
* @name markdown
* @restrict A
* @description
* @description
* This directive controls both the rendering and display of markdown, as well as
* This directive controls both the rendering and display of markdown, as well as
* the markdown editor.
* the markdown editor.
*/
*/
// @ngInject
// @ngInject
module
.
exports
=
function
(
$sanitize
)
{
module
.
exports
=
{
return
{
controller
:
MarkdownController
,
controller
:
function
()
{},
controllerAs
:
'vm'
,
link
:
function
(
scope
,
elem
)
{
bindings
:
{
var
input
=
elem
[
0
].
querySelector
(
'.js-markdown-input'
);
customTextClass
:
'<?'
,
var
output
=
elem
[
0
].
querySelector
(
'.js-markdown-preview'
);
readOnly
:
'<'
,
text
:
'<?'
,
/**
onEditText
:
'&'
,
* Transform the editor's input field with an editor command.
},
*/
template
:
require
(
'../templates/markdown.html'
),
function
updateState
(
newStateFn
)
{
var
newState
=
newStateFn
({
text
:
input
.
value
,
selectionStart
:
input
.
selectionStart
,
selectionEnd
:
input
.
selectionEnd
,
});
input
.
value
=
newState
.
text
;
input
.
selectionStart
=
newState
.
selectionStart
;
input
.
selectionEnd
=
newState
.
selectionEnd
;
// The input field currently loses focus when the contents are
// changed. This re-focuses the input field but really it should
// happen automatically.
input
.
focus
();
scope
.
onEditText
({
text
:
input
.
value
});
}
function
focusInput
()
{
// When the visibility of the editor changes, focus it.
// A timeout is used so that focus() is not called until
// the visibility change has been applied (by adding or removing
// the relevant CSS classes)
scopeTimeout
(
scope
,
function
()
{
input
.
focus
();
},
0
);
}
scope
.
insertBold
=
function
()
{
updateState
(
function
(
state
)
{
return
commands
.
toggleSpanStyle
(
state
,
'**'
,
'**'
,
'Bold'
);
});
};
scope
.
insertItalic
=
function
()
{
updateState
(
function
(
state
)
{
return
commands
.
toggleSpanStyle
(
state
,
'*'
,
'*'
,
'Italic'
);
});
};
scope
.
insertMath
=
function
()
{
updateState
(
function
(
state
)
{
var
before
=
state
.
text
.
slice
(
0
,
state
.
selectionStart
);
if
(
before
.
length
===
0
||
before
.
slice
(
-
1
)
===
'
\
n'
||
before
.
slice
(
-
2
)
===
'$$'
)
{
return
commands
.
toggleSpanStyle
(
state
,
'$$'
,
'$$'
,
'Insert LaTeX'
);
}
else
{
return
commands
.
toggleSpanStyle
(
state
,
'
\\
('
,
'
\\
)'
,
'Insert LaTeX'
);
}
});
};
scope
.
insertLink
=
function
()
{
updateState
(
function
(
state
)
{
return
commands
.
convertSelectionToLink
(
state
);
});
};
scope
.
insertIMG
=
function
()
{
updateState
(
function
(
state
)
{
return
commands
.
convertSelectionToLink
(
state
,
commands
.
LinkType
.
IMAGE_LINK
);
});
};
scope
.
insertList
=
function
()
{
updateState
(
function
(
state
)
{
return
commands
.
toggleBlockStyle
(
state
,
'* '
);
});
};
scope
.
insertNumList
=
function
()
{
updateState
(
function
(
state
)
{
return
commands
.
toggleBlockStyle
(
state
,
'1. '
);
});
};
scope
.
insertQuote
=
function
()
{
updateState
(
function
(
state
)
{
return
commands
.
toggleBlockStyle
(
state
,
'> '
);
});
};
// Keyboard shortcuts for bold, italic, and link.
elem
.
on
(
'keydown'
,
function
(
e
)
{
var
shortcuts
=
{
66
:
scope
.
insertBold
,
73
:
scope
.
insertItalic
,
75
:
scope
.
insertLink
,
};
var
shortcut
=
shortcuts
[
e
.
keyCode
];
if
(
shortcut
&&
(
e
.
ctrlKey
||
e
.
metaKey
))
{
e
.
preventDefault
();
shortcut
();
}
});
scope
.
preview
=
false
;
scope
.
togglePreview
=
function
()
{
scope
.
preview
=
!
scope
.
preview
;
};
var
handleInputChange
=
debounce
(
function
()
{
scope
.
$apply
(
function
()
{
scope
.
onEditText
({
text
:
input
.
value
});
});
},
100
);
input
.
addEventListener
(
'input'
,
handleInputChange
);
// Re-render the markdown when the view needs updating.
scope
.
$watch
(
'text'
,
function
()
{
output
.
innerHTML
=
renderMarkdown
(
scope
.
text
||
''
,
$sanitize
);
mediaEmbedder
.
replaceLinksWithEmbeds
(
output
);
});
scope
.
showEditor
=
function
()
{
return
!
scope
.
readOnly
&&
!
scope
.
preview
;
};
// Exit preview mode when leaving edit mode
scope
.
$watch
(
'readOnly'
,
function
()
{
scope
.
preview
=
false
;
});
scope
.
$watch
(
'showEditor()'
,
function
(
show
)
{
if
(
show
)
{
input
.
value
=
scope
.
text
||
''
;
focusInput
();
}
});
},
restrict
:
'E'
,
scope
:
{
customTextClass
:
'<?'
,
readOnly
:
'<'
,
text
:
'<?'
,
onEditText
:
'&'
,
},
template
:
require
(
'../templates/markdown.html'
),
};
};
};
src/sidebar/directive/test/markdown-test.js
View file @
7187cf1c
...
@@ -41,7 +41,7 @@ describe('markdown', function () {
...
@@ -41,7 +41,7 @@ describe('markdown', function () {
before
(
function
()
{
before
(
function
()
{
angular
.
module
(
'app'
,
[
'ngSanitize'
])
angular
.
module
(
'app'
,
[
'ngSanitize'
])
.
directive
(
'markdown'
,
proxyquire
(
'../markdown'
,
noCallThru
({
.
component
(
'markdown'
,
proxyquire
(
'../markdown'
,
noCallThru
({
angular
:
angular
,
angular
:
angular
,
katex
:
{
katex
:
{
renderToString
:
function
(
input
)
{
renderToString
:
function
(
input
)
{
...
@@ -217,7 +217,7 @@ describe('markdown', function () {
...
@@ -217,7 +217,7 @@ describe('markdown', function () {
}
}
function
isPreviewing
()
{
function
isPreviewing
()
{
return
editor
.
isolateScope
()
.
preview
;
return
editor
.
ctrl
.
preview
;
}
}
beforeEach
(
function
()
{
beforeEach
(
function
()
{
...
...
src/sidebar/templates/markdown.html
View file @
7187cf1c
<div
ng-if=
"!
readOnly"
class=
"markdown-tools"
ng-class=
"
preview && 'disable'"
>
<div
ng-if=
"!
vm.readOnly"
class=
"markdown-tools"
ng-class=
"vm.
preview && 'disable'"
>
<span
class=
"markdown-preview-toggle"
>
<span
class=
"markdown-preview-toggle"
>
<a
class=
"markdown-tools-badge h-icon-markdown"
href=
"https://help.github.com/articles/markdown-basics"
title=
"Parsed as Markdown"
target=
"_blank"
></a>
<a
class=
"markdown-tools-badge h-icon-markdown"
href=
"https://help.github.com/articles/markdown-basics"
title=
"Parsed as Markdown"
target=
"_blank"
></a>
<a
href=
""
class=
"markdown-tools-toggle"
ng-click=
"togglePreview()"
ng-show=
"!preview"
>
Preview
</a>
<a
href=
""
class=
"markdown-tools-toggle"
ng-click=
"vm.togglePreview()"
<a
href=
""
class=
"markdown-tools-toggle"
ng-click=
"togglePreview()"
ng-show=
"preview"
>
Write
</a>
ng-show=
"!vm.preview"
>
Preview
</a>
<a
href=
""
class=
"markdown-tools-toggle"
ng-click=
"vm.togglePreview()"
ng-show=
"vm.preview"
>
Write
</a>
</span>
</span>
<i
class=
"h-icon-format-bold markdown-tools-button"
ng-click=
"insertBold()"
title=
"Embolden text"
></i>
<i
class=
"h-icon-format-bold markdown-tools-button"
ng-click=
"
vm.
insertBold()"
title=
"Embolden text"
></i>
<i
class=
"h-icon-format-italic markdown-tools-button"
ng-click=
"insertItalic()"
title=
"Italicize text"
></i>
<i
class=
"h-icon-format-italic markdown-tools-button"
ng-click=
"
vm.
insertItalic()"
title=
"Italicize text"
></i>
<i
class=
"h-icon-format-quote markdown-tools-button"
ng-click=
"insertQuote()"
title=
"Quote text"
></i>
<i
class=
"h-icon-format-quote markdown-tools-button"
ng-click=
"
vm.
insertQuote()"
title=
"Quote text"
></i>
<i
class=
"h-icon-insert-link markdown-tools-button"
ng-click=
"insertLink()"
title=
"Insert link"
></i>
<i
class=
"h-icon-insert-link markdown-tools-button"
ng-click=
"
vm.
insertLink()"
title=
"Insert link"
></i>
<i
class=
"h-icon-insert-photo markdown-tools-button"
ng-click=
"insertIMG()"
title=
"Insert image"
></i>
<i
class=
"h-icon-insert-photo markdown-tools-button"
ng-click=
"
vm.
insertIMG()"
title=
"Insert image"
></i>
<i
class=
"h-icon-functions markdown-tools-button"
ng-click=
"insertMath()"
title=
"Insert mathematical notation (LaTex is supported)"
></i>
<i
class=
"h-icon-functions markdown-tools-button"
ng-click=
"
vm.
insertMath()"
title=
"Insert mathematical notation (LaTex is supported)"
></i>
<i
class=
"h-icon-format-list-numbered markdown-tools-button"
ng-click=
"insertNumList()"
title=
"Insert numbered list"
></i>
<i
class=
"h-icon-format-list-numbered markdown-tools-button"
ng-click=
"
vm.
insertNumList()"
title=
"Insert numbered list"
></i>
<i
class=
"h-icon-format-list-bulleted markdown-tools-button"
ng-click=
"insertList()"
title=
"Insert list"
></i>
<i
class=
"h-icon-format-list-bulleted markdown-tools-button"
ng-click=
"
vm.
insertList()"
title=
"Insert list"
></i>
</div>
</div>
<textarea
class=
"form-input form-textarea js-markdown-input"
<textarea
class=
"form-input form-textarea js-markdown-input"
ng-show=
"showEditor()"
ng-show=
"
vm.
showEditor()"
ng-click=
"$event.stopPropagation()"
ng-click=
"$event.stopPropagation()"
h-branding=
"annotationFontFamily"
h-branding=
"annotationFontFamily"
></textarea>
></textarea>
<div
class=
"markdown-body js-markdown-preview"
<div
class=
"markdown-body js-markdown-preview"
ng-class=
"(
preview && 'markdown-preview') ||
customTextClass"
ng-class=
"(
vm.preview && 'markdown-preview') || vm.
customTextClass"
ng-dblclick=
"togglePreview()"
ng-dblclick=
"
vm.
togglePreview()"
ng-show=
"!showEditor()"
ng-show=
"!
vm.
showEditor()"
h-branding=
"annotationFontFamily"
></div>
h-branding=
"annotationFontFamily"
></div>
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