code snippets
parent
1f47a4b9b1
commit
1a15ab955e
|
@ -6,7 +6,7 @@ var MentionExtension = MediumEditor.Extension.extend({
|
||||||
this.subscribe('blur', this.cancel.bind(this));
|
this.subscribe('blur', this.cancel.bind(this));
|
||||||
},
|
},
|
||||||
isEditMode: function() {
|
isEditMode: function() {
|
||||||
return !this.base.origElements.parentNode.classList.contains('read-mode')
|
return !this.base.origElements.parentNode.classList.contains('read-mode');
|
||||||
},
|
},
|
||||||
cancel: function() {
|
cancel: function() {
|
||||||
if (this.isEditMode()) {
|
if (this.isEditMode()) {
|
||||||
|
@ -248,7 +248,7 @@ var MentionExtension = MediumEditor.Extension.extend({
|
||||||
li.innerText = '@' + it.username;
|
li.innerText = '@' + it.username;
|
||||||
}
|
}
|
||||||
|
|
||||||
li.addEventListener('click', this.selectMention.bind(this, it));
|
li.addEventListener('mousedown', this.selectMention.bind(this, it));
|
||||||
|
|
||||||
ul.appendChild(li);
|
ul.appendChild(li);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
|
@ -214,6 +214,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"WYSIWYG": {
|
"WYSIWYG": {
|
||||||
|
"CODE_SNIPPET": "Code Snippet",
|
||||||
|
"SELECT_LANGUAGE_PLACEHOLDER": "Select Language",
|
||||||
|
"SELECT_LANGUAGE_REMOVE_FORMATING": "Remove formatting",
|
||||||
"OUTDATED": "Another person has made changes while you were editing. Check the new version on the activiy tab before you save your changes.",
|
"OUTDATED": "Another person has made changes while you were editing. Check the new version on the activiy tab before you save your changes.",
|
||||||
"MARKDOWN_HELP": "Markdown syntax help"
|
"MARKDOWN_HELP": "Markdown syntax help"
|
||||||
},
|
},
|
||||||
|
|
|
@ -11,11 +11,12 @@
|
||||||
span.detail-subject.e2e-title-subject(
|
span.detail-subject.e2e-title-subject(
|
||||||
ng-if="!vm.permissions.canEdit"
|
ng-if="!vm.permissions.canEdit"
|
||||||
) {{vm.item.subject}}
|
) {{vm.item.subject}}
|
||||||
tg-svg.detail-edit.e2e-detail-edit(
|
a(
|
||||||
|
href=""
|
||||||
ng-if="vm.permissions.canEdit"
|
ng-if="vm.permissions.canEdit"
|
||||||
svg-icon="icon-edit"
|
|
||||||
ng-click="vm.editSubject(true)"
|
ng-click="vm.editSubject(true)"
|
||||||
)
|
)
|
||||||
|
tg-svg.detail-edit.e2e-detail-edit(svg-icon="icon-edit")
|
||||||
|
|
||||||
.edit-title-wrapper(ng-if="vm.editMode")
|
.edit-title-wrapper(ng-if="vm.editMode")
|
||||||
input.edit-title-input.e2e-title-input(
|
input.edit-title-input.e2e-title-input(
|
||||||
|
|
|
@ -23,16 +23,21 @@
|
||||||
###
|
###
|
||||||
|
|
||||||
class WysiwygCodeHightlighterService
|
class WysiwygCodeHightlighterService
|
||||||
constructor: () ->
|
getLanguages: () ->
|
||||||
if !@.languages
|
return new Promise (resolve, reject) =>
|
||||||
@.loadLanguages()
|
if @.languages
|
||||||
|
resolve(@.languages)
|
||||||
|
else if @.loadPromise
|
||||||
|
@.loadPromise.then () => resolve(@.languages)
|
||||||
|
else
|
||||||
|
@.loadPromise = $.getJSON("/#{window._version}/prism/prism-languages.json").then (_languages_) =>
|
||||||
|
@.loadPromise = null
|
||||||
|
@.languages = _.map _languages_, (it) ->
|
||||||
|
it.url = "/#{window._version}/prism/" + it.file
|
||||||
|
|
||||||
loadLanguages: () ->
|
return it
|
||||||
$.getJSON("/#{window._version}/prism/prism-languages.json").then (_languages_) =>
|
|
||||||
@.languages = _.map _languages_, (it) ->
|
|
||||||
it.url = "/#{window._version}/prism/" + it.file
|
|
||||||
|
|
||||||
return it
|
resolve(@.languages)
|
||||||
|
|
||||||
getLanguageInClassList: (classes) ->
|
getLanguageInClassList: (classes) ->
|
||||||
lan = _.find @.languages, (it) ->
|
lan = _.find @.languages, (it) ->
|
||||||
|
@ -41,123 +46,6 @@ class WysiwygCodeHightlighterService
|
||||||
|
|
||||||
return if lan then lan.name else null
|
return if lan then lan.name else null
|
||||||
|
|
||||||
addCodeLanguageSelectors: (mediumInstance) ->
|
|
||||||
$(mediumInstance.elements[0]).find('code').each (index, code) =>
|
|
||||||
if !code.classList.contains('has-code-lan-selector')
|
|
||||||
code.classList.add('has-code-lan-selector') # prevent multi instanciate
|
|
||||||
|
|
||||||
currentLan = @.getLanguageInClassList(code.classList)
|
|
||||||
code.parentNode.classList.add('language-' + currentLan)
|
|
||||||
|
|
||||||
id = new Date().getTime()
|
|
||||||
|
|
||||||
text = document.createTextNode(currentLan || 'text')
|
|
||||||
|
|
||||||
tab = document.createElement('div')
|
|
||||||
tab.appendChild(text)
|
|
||||||
tab.addEventListener 'click', () =>
|
|
||||||
@.searchLanguage tab, (lan) =>
|
|
||||||
if lan
|
|
||||||
tab.innerText = lan
|
|
||||||
@.updatePositionCodeTab(code.parentElement, tab)
|
|
||||||
|
|
||||||
languageClass = _.find code.classList, (className) ->
|
|
||||||
return className && className.indexOf('language-') != -1
|
|
||||||
|
|
||||||
if languageClass
|
|
||||||
code.classList.remove(languageClass.replace('language-', ''))
|
|
||||||
code.classList.remove(languageClass)
|
|
||||||
|
|
||||||
code.classList.add('language-' + lan)
|
|
||||||
code.classList.add(lan)
|
|
||||||
|
|
||||||
document.body.appendChild(tab)
|
|
||||||
|
|
||||||
code.dataset.tab = tab
|
|
||||||
|
|
||||||
if !code.dataset.tabId
|
|
||||||
code.dataset.tabId = id
|
|
||||||
code.classList.add(id)
|
|
||||||
|
|
||||||
tab.dataset.tabId = code.dataset.tabId
|
|
||||||
|
|
||||||
tab.classList.add('code-language-selector') # styles
|
|
||||||
tab.classList.add('medium-' + mediumInstance.id) # used to delete
|
|
||||||
|
|
||||||
@.updatePositionCodeTab(code.parentElement, tab)
|
|
||||||
|
|
||||||
removeCodeLanguageSelectors: (mediumInstance) ->
|
|
||||||
return if !mediumInstance || !mediumInstance.elements
|
|
||||||
|
|
||||||
$(mediumInstance.elements[0]).find('code').each (index, code) ->
|
|
||||||
$(code).removeClass('has-code-lan-selector')
|
|
||||||
|
|
||||||
$('.medium-' + mediumInstance.id).remove()
|
|
||||||
|
|
||||||
updatePositionCodeTab: (node, tab) ->
|
|
||||||
preRects = node.getBoundingClientRect()
|
|
||||||
|
|
||||||
tab.style.top = (preRects.top + $(window).scrollTop()) + 'px'
|
|
||||||
tab.style.left = (preRects.left + preRects.width - tab.offsetWidth) + 'px'
|
|
||||||
|
|
||||||
getCodeLanHTML: (filter = '') ->
|
|
||||||
template = _.template("""
|
|
||||||
<% _.forEach(lans, function(lan) { %>
|
|
||||||
<li><%- lan %></li><% });
|
|
||||||
%>
|
|
||||||
""");
|
|
||||||
|
|
||||||
filteresLans = _.map @.languages, (it) -> it.name
|
|
||||||
|
|
||||||
if filter.length
|
|
||||||
filteresLans = _.filter filteresLans, (it) ->
|
|
||||||
return it.indexOf(filter) != -1
|
|
||||||
|
|
||||||
return template({ 'lans': filteresLans });
|
|
||||||
|
|
||||||
searchLanguage: (tab, cb) ->
|
|
||||||
search = document.createElement('div')
|
|
||||||
|
|
||||||
search.className = 'code-language-search'
|
|
||||||
|
|
||||||
preRects = tab.getBoundingClientRect()
|
|
||||||
search.style.top = (preRects.top + $(window).scrollTop() + preRects.height) + 'px'
|
|
||||||
search.style.left = preRects.left + 'px'
|
|
||||||
|
|
||||||
input = document.createElement('input')
|
|
||||||
input.setAttribute('type', 'text')
|
|
||||||
|
|
||||||
ul = document.createElement('ul')
|
|
||||||
|
|
||||||
ul.innerHTML = @.getCodeLanHTML()
|
|
||||||
|
|
||||||
search.appendChild(input)
|
|
||||||
search.appendChild(ul)
|
|
||||||
|
|
||||||
document.body.appendChild(search)
|
|
||||||
|
|
||||||
input.focus()
|
|
||||||
|
|
||||||
close = () ->
|
|
||||||
search.remove()
|
|
||||||
$(document.body).off('.leave-search-codelan')
|
|
||||||
|
|
||||||
clickedInSearchBox = (target) ->
|
|
||||||
return $(search).is(target) || !!$(search).has(target).length
|
|
||||||
|
|
||||||
$(document.body).on 'mouseup.leave-search-codelan', (e) ->
|
|
||||||
if !clickedInSearchBox(e.target)
|
|
||||||
cb(null)
|
|
||||||
close()
|
|
||||||
|
|
||||||
$(input).on 'keyup', (e) =>
|
|
||||||
filter = e.currentTarget.value
|
|
||||||
ul.innerHTML = @.getCodeLanHTML(filter)
|
|
||||||
|
|
||||||
$(ul).on 'click', 'li', (e) ->
|
|
||||||
cb(e.currentTarget.innerText)
|
|
||||||
close()
|
|
||||||
|
|
||||||
loadLanguage: (lan) ->
|
loadLanguage: (lan) ->
|
||||||
return new Promise (resolve) ->
|
return new Promise (resolve) ->
|
||||||
if !Prism.languages[lan]
|
if !Prism.languages[lan]
|
||||||
|
@ -165,35 +53,22 @@ class WysiwygCodeHightlighterService
|
||||||
else
|
else
|
||||||
resolve()
|
resolve()
|
||||||
|
|
||||||
removeHightlighter: (element) ->
|
|
||||||
codes = $(element).find('code')
|
|
||||||
|
|
||||||
codes.each (index, code) ->
|
|
||||||
code.innerHTML = code.innerText
|
|
||||||
|
|
||||||
# firefox adds br instead of new lines inside <code>
|
# firefox adds br instead of new lines inside <code>
|
||||||
replaceCodeBrToNl: (code) ->
|
replaceCodeBrToNl: (code) ->
|
||||||
$(code).find('br').replaceWith('\n')
|
$(code).find('br').replaceWith('\n')
|
||||||
|
|
||||||
|
hightlightCode: (code) ->
|
||||||
|
@.replaceCodeBrToNl(code)
|
||||||
|
|
||||||
|
lan = @.getLanguageInClassList(code.classList)
|
||||||
|
|
||||||
|
if lan
|
||||||
|
@.loadLanguage(lan).then () -> Prism.highlightElement(code)
|
||||||
|
|
||||||
addHightlighter: (element) ->
|
addHightlighter: (element) ->
|
||||||
codes = $(element).find('code')
|
codes = $(element).find('code')
|
||||||
|
|
||||||
codes.each (index, code) =>
|
codes.each (index, code) => @.hightlightCode(code)
|
||||||
@.replaceCodeBrToNl(code)
|
|
||||||
|
|
||||||
lan = @.getLanguageInClassList(code.classList)
|
|
||||||
|
|
||||||
if lan
|
|
||||||
@.loadLanguage(lan).then () -> Prism.highlightElement(code)
|
|
||||||
|
|
||||||
updateCodeLanguageSelector: (mediumInstance) ->
|
|
||||||
$('.medium-' + mediumInstance.id).each (index, tab) =>
|
|
||||||
node = $('.' + tab.dataset.tabId)
|
|
||||||
|
|
||||||
if !node.length
|
|
||||||
tab.remove()
|
|
||||||
else
|
|
||||||
@.updatePositionCodeTab(node.parent()[0], tab)
|
|
||||||
|
|
||||||
angular.module("taigaComponents")
|
angular.module("taigaComponents")
|
||||||
.service("tgWysiwygCodeHightlighterService", WysiwygCodeHightlighterService)
|
.service("tgWysiwygCodeHightlighterService", WysiwygCodeHightlighterService)
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
###
|
||||||
|
# Copyright (C) 2014-2017 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
# Copyright (C) 2014-2017 Jesús Espino Garcia <jespinog@gmail.com>
|
||||||
|
# Copyright (C) 2014-2017 David Barragán Merino <bameda@dbarragan.com>
|
||||||
|
# Copyright (C) 2014-2017 Alejandro Alonso <alejandro.alonso@kaleidos.net>
|
||||||
|
# Copyright (C) 2014-2017 Juan Francisco Alcántara <juanfran.alcantara@kaleidos.net>
|
||||||
|
# Copyright (C) 2014-2017 Xavi Julian <xavier.julian@kaleidos.net>
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
# File: modules/components/wysiwyg/wysiwyg-code-lightbox/wysiwyg-code-lightbox.directive.coffee
|
||||||
|
###
|
||||||
|
|
||||||
|
WysiwygCodeLightbox = (lightboxService) ->
|
||||||
|
link = (scope, el, attrs, ctrl) ->
|
||||||
|
scope.$watch 'visible', (visible) ->
|
||||||
|
if visible && !el.hasClass('open')
|
||||||
|
scope.open = true
|
||||||
|
lightboxService.open(el, null, scope.onClose)
|
||||||
|
|
||||||
|
scope.$applyAsync () ->
|
||||||
|
textarea = el[0].querySelector('textarea')
|
||||||
|
if textarea
|
||||||
|
textarea.select()
|
||||||
|
|
||||||
|
else if !visible && el.hasClass('open')
|
||||||
|
scope.open = false
|
||||||
|
lightboxService.close(el)
|
||||||
|
|
||||||
|
return {
|
||||||
|
scope: {
|
||||||
|
languages: '<',
|
||||||
|
codeLanguage: '<',
|
||||||
|
code: '<',
|
||||||
|
visible: '<',
|
||||||
|
onClose: '&',
|
||||||
|
onSave: '&'
|
||||||
|
},
|
||||||
|
link: link,
|
||||||
|
templateUrl: "components/wysiwyg/wysiwyg-code-lightbox/wysiwyg-code-lightbox.html"
|
||||||
|
}
|
||||||
|
|
||||||
|
angular.module("taigaComponents")
|
||||||
|
.directive("tgWysiwygCodeLightbox", ["lightboxService", WysiwygCodeLightbox])
|
|
@ -0,0 +1,25 @@
|
||||||
|
tg-lightbox-close(on-close="onClose()")
|
||||||
|
|
||||||
|
form(
|
||||||
|
ng-if="open"
|
||||||
|
ng-submit="onSave({lan: codeLanguage, code: code})"
|
||||||
|
)
|
||||||
|
h2.title(translate="COMMON.WYSIWYG.CODE_SNIPPET")
|
||||||
|
|
||||||
|
fieldset
|
||||||
|
select(ng-model="codeLanguage")
|
||||||
|
option(value="") {{'COMMON.WYSIWYG.SELECT_LANGUAGE_PLACEHOLDER' | translate}}
|
||||||
|
option(value="remove-formating") {{'COMMON.WYSIWYG.SELECT_LANGUAGE_REMOVE_FORMATING' | translate}}
|
||||||
|
option(
|
||||||
|
ng-repeat="option in languages"
|
||||||
|
ng-value="option.name"
|
||||||
|
) {{option.name}}
|
||||||
|
|
||||||
|
fieldset
|
||||||
|
textarea(ng-model="code")
|
||||||
|
|
||||||
|
fieldset
|
||||||
|
button.button-green.submit-button(
|
||||||
|
type="submit"
|
||||||
|
translate="COMMON.SAVE"
|
||||||
|
)
|
|
@ -0,0 +1,3 @@
|
||||||
|
tg-wysiwyg-code-lightbox textarea {
|
||||||
|
height: 350px;
|
||||||
|
}
|
|
@ -26,36 +26,78 @@ taiga = @.taiga
|
||||||
bindOnce = @.taiga.bindOnce
|
bindOnce = @.taiga.bindOnce
|
||||||
|
|
||||||
Medium = ($translate, $confirm, $storage, wysiwygService, animationFrame, tgLoader, wysiwygCodeHightlighterService, wysiwygMentionService, analytics) ->
|
Medium = ($translate, $confirm, $storage, wysiwygService, animationFrame, tgLoader, wysiwygCodeHightlighterService, wysiwygMentionService, analytics) ->
|
||||||
|
removeSelections = () ->
|
||||||
|
if window.getSelection
|
||||||
|
if window.getSelection().empty
|
||||||
|
window.getSelection().empty();
|
||||||
|
else if window.getSelection().removeAllRanges
|
||||||
|
window.getSelection().removeAllRanges()
|
||||||
|
|
||||||
isCodeBlockSelected = (range, elm) ->
|
else if document.selection
|
||||||
return !!$(range.endContainer).parentsUntil('.editor', 'code').length
|
document.selection.empty()
|
||||||
|
|
||||||
refreshCodeBlockHightlight = (elm) ->
|
getRangeCodeBlock = (range) ->
|
||||||
wysiwygCodeHightlighterService.refreshCodeLanguageSelectors(elm)
|
return $(range.endContainer).parentsUntil('.editor', 'code')
|
||||||
|
|
||||||
|
isCodeBlockSelected = (range) ->
|
||||||
|
return !!getRangeCodeBlock(range).length
|
||||||
|
|
||||||
|
removeCodeBlockAndHightlight = (selection, mediumInstance) ->
|
||||||
|
if $(selection).is('code')
|
||||||
|
code = selection
|
||||||
|
else
|
||||||
|
code = $(selection).closest('code')[0]
|
||||||
|
|
||||||
removeCodeBlockAndHightlight = (range, elm) ->
|
|
||||||
code = $(range.endContainer).closest('code')[0]
|
|
||||||
pre = code.parentNode
|
pre = code.parentNode
|
||||||
|
|
||||||
p = document.createElement('p')
|
p = document.createElement('p')
|
||||||
p.innerText = code.innerText
|
p.innerText = code.innerText
|
||||||
|
|
||||||
pre.parentNode.replaceChild(p, pre)
|
pre.parentNode.replaceChild(p, pre)
|
||||||
|
mediumInstance.checkContentChanged(mediumInstance.elements[0])
|
||||||
wysiwygCodeHightlighterService.removeCodeLanguageSelectors(elm)
|
|
||||||
|
|
||||||
addCodeBlockAndHightlight = (range, elm) ->
|
addCodeBlockAndHightlight = (range, elm) ->
|
||||||
pre = document.createElement('pre')
|
pre = document.createElement('pre')
|
||||||
code = document.createElement('code')
|
code = document.createElement('code')
|
||||||
|
|
||||||
pre.appendChild(code)
|
console.log range.startContainer.parentNode.nextSibling
|
||||||
|
|
||||||
|
if !range.startContainer.parentNode.nextSibling
|
||||||
|
$('<br/>').insertAfter(range.startContainer.parentNode)
|
||||||
|
|
||||||
|
start = range.startContainer.parentNode.nextSibling
|
||||||
|
|
||||||
code.appendChild(range.extractContents())
|
code.appendChild(range.extractContents())
|
||||||
range.insertNode(pre)
|
|
||||||
|
|
||||||
elm.checkContentChanged()
|
pre.appendChild(code)
|
||||||
|
|
||||||
wysiwygCodeHightlighterService.addCodeLanguageSelectors(elm)
|
start.parentNode.insertBefore(pre, start)
|
||||||
|
|
||||||
|
refreshCodeBlocks(elm)
|
||||||
|
|
||||||
|
refreshCodeBlocks = (mediumInstance) ->
|
||||||
|
# clean empty <p> content editable adds it when range.extractContents has been execute it
|
||||||
|
for mainChildren in mediumInstance.elements[0].children
|
||||||
|
if mainChildren && mainChildren.tagName.toLowerCase() == 'p' && !mainChildren.innerText.length
|
||||||
|
mainChildren.parentNode.removeChild(mainChildren)
|
||||||
|
|
||||||
|
preList = mediumInstance.elements[0].querySelectorAll('pre')
|
||||||
|
|
||||||
|
for pre in preList
|
||||||
|
# prevent edit a pre
|
||||||
|
pre.setAttribute('contenteditable', false)
|
||||||
|
|
||||||
|
if pre.nextElementSibling && pre.nextElementSibling.nodeName.toLowerCase() == 'p' && !pre.nextElementSibling.children.length
|
||||||
|
pre.nextElementSibling.appendChild(document.createElement('br'))
|
||||||
|
|
||||||
|
# add p after every pre
|
||||||
|
else if !pre.nextElementSibling || pre.nextElementSibling.nodeName.toLowerCase() != 'p'
|
||||||
|
p = document.createElement('p')
|
||||||
|
p.appendChild(document.createElement('br'))
|
||||||
|
|
||||||
|
pre.parentNode.insertBefore(p, pre.nextSibling)
|
||||||
|
|
||||||
|
mediumInstance.checkContentChanged(mediumInstance.elements[0])
|
||||||
|
|
||||||
AlignRightButton = MediumEditor.extensions.button.extend({
|
AlignRightButton = MediumEditor.extensions.button.extend({
|
||||||
name: 'rtl',
|
name: 'rtl',
|
||||||
|
@ -107,9 +149,16 @@ Medium = ($translate, $confirm, $storage, wysiwygService, animationFrame, tgLoad
|
||||||
range = MediumEditor.selection.getSelectionRange(self.document)
|
range = MediumEditor.selection.getSelectionRange(self.document)
|
||||||
|
|
||||||
if isCodeBlockSelected(range, this.base)
|
if isCodeBlockSelected(range, this.base)
|
||||||
removeCodeBlockAndHightlight(range, this.base)
|
removeCodeBlockAndHightlight(range.endContainer, this.base)
|
||||||
else
|
else
|
||||||
addCodeBlockAndHightlight(range, this.base)
|
addCodeBlockAndHightlight(range, this.base)
|
||||||
|
removeSelections()
|
||||||
|
|
||||||
|
toolbar = this.base.getExtensionByName('toolbar')
|
||||||
|
|
||||||
|
if toolbar
|
||||||
|
toolbar.hideToolbar()
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
CustomPasteHandler = MediumEditor.extensions.paste.extend({
|
CustomPasteHandler = MediumEditor.extensions.paste.extend({
|
||||||
|
@ -141,6 +190,7 @@ Medium = ($translate, $confirm, $storage, wysiwygService, animationFrame, tgLoad
|
||||||
mediumInstance = null
|
mediumInstance = null
|
||||||
editorMedium = $el.find('.medium')
|
editorMedium = $el.find('.medium')
|
||||||
editorMarkdown = $el.find('.markdown')
|
editorMarkdown = $el.find('.markdown')
|
||||||
|
codeBlockSelected = null
|
||||||
|
|
||||||
isEditOnly = !!$attrs.$attr.editonly
|
isEditOnly = !!$attrs.$attr.editonly
|
||||||
notPersist = !!$attrs.$attr.notPersist
|
notPersist = !!$attrs.$attr.notPersist
|
||||||
|
@ -149,12 +199,47 @@ Medium = ($translate, $confirm, $storage, wysiwygService, animationFrame, tgLoad
|
||||||
$scope.editMode = isEditOnly || false
|
$scope.editMode = isEditOnly || false
|
||||||
$scope.mode = $storage.get('editor-mode', 'html')
|
$scope.mode = $storage.get('editor-mode', 'html')
|
||||||
$scope.markdown = ''
|
$scope.markdown = ''
|
||||||
|
$scope.codeEditorVisible = false
|
||||||
|
$scope.codeLans = []
|
||||||
|
|
||||||
wysiwygService.loadEmojis()
|
wysiwygService.loadEmojis()
|
||||||
|
|
||||||
|
wysiwygCodeHightlighterService.getLanguages().then (codeLans) ->
|
||||||
|
$scope.codeLans = codeLans
|
||||||
|
|
||||||
setHtmlMedium = (markdown) ->
|
setHtmlMedium = (markdown) ->
|
||||||
html = wysiwygService.getHTML(markdown)
|
html = wysiwygService.getHTML(markdown)
|
||||||
editorMedium.html(html)
|
editorMedium.html(html)
|
||||||
|
wysiwygCodeHightlighterService.addHightlighter(mediumInstance.elements[0])
|
||||||
|
refreshCodeBlocks(mediumInstance)
|
||||||
|
|
||||||
|
$scope.saveSnippet = (lan, code) ->
|
||||||
|
$scope.codeEditorVisible = false
|
||||||
|
codeBlockSelected.innerText = code
|
||||||
|
codePre = codeBlockSelected.parentNode
|
||||||
|
|
||||||
|
if lan == 'remove-formating'
|
||||||
|
codeBlockSelected.className = ''
|
||||||
|
codePre.className = ''
|
||||||
|
|
||||||
|
removeCodeBlockAndHightlight(codeBlockSelected, mediumInstance)
|
||||||
|
else if _.trim(code).length
|
||||||
|
if lan
|
||||||
|
codeBlockSelected.className = 'language-' + lan
|
||||||
|
codePre.className = 'language-' + lan
|
||||||
|
else
|
||||||
|
codeBlockSelected.className = ''
|
||||||
|
codePre.className = ''
|
||||||
|
|
||||||
|
wysiwygCodeHightlighterService.hightlightCode(codeBlockSelected)
|
||||||
|
mediumInstance.checkContentChanged(mediumInstance.elements[0])
|
||||||
|
else
|
||||||
|
codeBlockSelected.parentNode.parentNode.removeChild(codeBlockSelected.parentNode)
|
||||||
|
mediumInstance.checkContentChanged(mediumInstance.elements[0])
|
||||||
|
|
||||||
|
throttleChange()
|
||||||
|
|
||||||
|
return null
|
||||||
|
|
||||||
$scope.setMode = (mode) ->
|
$scope.setMode = (mode) ->
|
||||||
$storage.set('editor-mode', mode)
|
$storage.set('editor-mode', mode)
|
||||||
|
@ -191,7 +276,7 @@ Medium = ($translate, $confirm, $storage, wysiwygService, animationFrame, tgLoad
|
||||||
if notPersist
|
if notPersist
|
||||||
clean()
|
clean()
|
||||||
else if $scope.mode == 'html'
|
else if $scope.mode == 'html'
|
||||||
setHtmlMedium($scope.content)
|
setHtmlMedium($scope.content || null)
|
||||||
|
|
||||||
$scope.markdown = $scope.content
|
$scope.markdown = $scope.content
|
||||||
|
|
||||||
|
@ -207,19 +292,6 @@ Medium = ($translate, $confirm, $storage, wysiwygService, animationFrame, tgLoad
|
||||||
$scope.markdown = ''
|
$scope.markdown = ''
|
||||||
editorMedium.html('')
|
editorMedium.html('')
|
||||||
|
|
||||||
refreshExtras = () ->
|
|
||||||
animationFrame.add () ->
|
|
||||||
if $scope.mode == 'html'
|
|
||||||
if $scope.editMode
|
|
||||||
wysiwygCodeHightlighterService.addCodeLanguageSelectors(mediumInstance)
|
|
||||||
wysiwygCodeHightlighterService.removeHightlighter(mediumInstance.elements[0])
|
|
||||||
else
|
|
||||||
wysiwygCodeHightlighterService.addHightlighter(mediumInstance.elements[0])
|
|
||||||
wysiwygCodeHightlighterService.removeCodeLanguageSelectors(mediumInstance)
|
|
||||||
else
|
|
||||||
wysiwygCodeHightlighterService.removeHightlighter(mediumInstance.elements[0])
|
|
||||||
wysiwygCodeHightlighterService.removeCodeLanguageSelectors(mediumInstance)
|
|
||||||
|
|
||||||
saveEnd = () ->
|
saveEnd = () ->
|
||||||
$scope.saving = false
|
$scope.saving = false
|
||||||
|
|
||||||
|
@ -305,7 +377,6 @@ Medium = ($translate, $confirm, $storage, wysiwygService, animationFrame, tgLoad
|
||||||
change = () ->
|
change = () ->
|
||||||
if $scope.mode == 'html'
|
if $scope.mode == 'html'
|
||||||
updateMarkdownWithCurrentHtml()
|
updateMarkdownWithCurrentHtml()
|
||||||
wysiwygCodeHightlighterService.updateCodeLanguageSelector(mediumInstance)
|
|
||||||
|
|
||||||
localSave($scope.markdown)
|
localSave($scope.markdown)
|
||||||
|
|
||||||
|
@ -415,24 +486,6 @@ Medium = ($translate, $confirm, $storage, wysiwygService, animationFrame, tgLoad
|
||||||
mediumInstance.subscribe 'editableDrop', (event) ->
|
mediumInstance.subscribe 'editableDrop', (event) ->
|
||||||
$scope.onUploadFile({files: event.dataTransfer.files, cb: uploadEnd})
|
$scope.onUploadFile({files: event.dataTransfer.files, cb: uploadEnd})
|
||||||
|
|
||||||
editorMedium.on 'keydown', (e) ->
|
|
||||||
code = if e.keyCode then e.keyCode else e.which
|
|
||||||
range = MediumEditor.selection.getSelectionRange(document)
|
|
||||||
codeBlock = isCodeBlockSelected(range, document)
|
|
||||||
selection = window.getSelection()
|
|
||||||
|
|
||||||
if code == 13 && !e.shiftKey && selection.focusOffset == _.trimEnd(selection.focusNode.textContent).length
|
|
||||||
e.preventDefault()
|
|
||||||
document.execCommand('insertHTML', false, '<p id="last-p"><br/></p>')
|
|
||||||
|
|
||||||
lastP = $('#last-p').attr('id', '')
|
|
||||||
|
|
||||||
range = document.createRange()
|
|
||||||
range.selectNodeContents(lastP[0])
|
|
||||||
range.collapse(true);
|
|
||||||
|
|
||||||
MediumEditor.selection.selectRange(document, range)
|
|
||||||
|
|
||||||
mediumInstance.subscribe 'editableKeydown', (e) ->
|
mediumInstance.subscribe 'editableKeydown', (e) ->
|
||||||
code = if e.keyCode then e.keyCode else e.which
|
code = if e.keyCode then e.keyCode else e.which
|
||||||
|
|
||||||
|
@ -452,12 +505,18 @@ Medium = ($translate, $confirm, $storage, wysiwygService, animationFrame, tgLoad
|
||||||
|
|
||||||
$scope.editMode = editMode
|
$scope.editMode = editMode
|
||||||
|
|
||||||
$scope.$applyAsync(refreshExtras)
|
$scope.$applyAsync () ->
|
||||||
|
wysiwygCodeHightlighterService.addHightlighter(mediumInstance.elements[0])
|
||||||
|
refreshCodeBlocks(mediumInstance)
|
||||||
|
|
||||||
$scope.$watch () ->
|
$(editorMedium[0]).on 'dblclick', 'pre', (e) ->
|
||||||
return $scope.mode + ":" + $scope.editMode
|
$scope.$applyAsync () ->
|
||||||
, () ->
|
$scope.codeEditorVisible = true
|
||||||
$scope.$applyAsync(refreshExtras)
|
|
||||||
|
codeBlockSelected = e.currentTarget.querySelector('code')
|
||||||
|
|
||||||
|
$scope.currentCodeLanguage = wysiwygCodeHightlighterService.getLanguageInClassList(codeBlockSelected.classList)
|
||||||
|
$scope.code = codeBlockSelected.innerText
|
||||||
|
|
||||||
unwatch = $scope.$watch 'content', (content) ->
|
unwatch = $scope.$watch 'content', (content) ->
|
||||||
if !_.isUndefined(content)
|
if !_.isUndefined(content)
|
||||||
|
@ -466,7 +525,7 @@ Medium = ($translate, $confirm, $storage, wysiwygService, animationFrame, tgLoad
|
||||||
if !mediumInstance && isDraft()
|
if !mediumInstance && isDraft()
|
||||||
$scope.editMode = true
|
$scope.editMode = true
|
||||||
|
|
||||||
if $scope.markdown == content
|
if ($scope.markdown.length || content.length) && $scope.markdown == content
|
||||||
return
|
return
|
||||||
|
|
||||||
content = getCurrentContent()
|
content = getCurrentContent()
|
||||||
|
@ -487,7 +546,6 @@ Medium = ($translate, $confirm, $storage, wysiwygService, animationFrame, tgLoad
|
||||||
|
|
||||||
$scope.$on "$destroy", () ->
|
$scope.$on "$destroy", () ->
|
||||||
if mediumInstance
|
if mediumInstance
|
||||||
wysiwygCodeHightlighterService.removeCodeLanguageSelectors(mediumInstance)
|
|
||||||
mediumInstance.destroy()
|
mediumInstance.destroy()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -90,12 +90,12 @@
|
||||||
}
|
}
|
||||||
pre:not([class*="language-"]) {
|
pre:not([class*="language-"]) {
|
||||||
@include font-size(small);
|
@include font-size(small);
|
||||||
background: lighten($grayer, 10%);
|
background: $code-bg;
|
||||||
color: $whitish;
|
color: $whitish;
|
||||||
direction: ltr;
|
direction: ltr;
|
||||||
font-family: 'courier new', 'monospace';
|
font-family: 'courier new', 'monospace';
|
||||||
line-height: 1.4rem;
|
line-height: 1.4rem;
|
||||||
margin-bottom: 1rem;
|
margin-bottom: .5rem;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
unicode-bidi: embed;
|
unicode-bidi: embed;
|
||||||
|
@ -165,6 +165,12 @@
|
||||||
tg-wysiwyg {
|
tg-wysiwyg {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin-bottom: 2rem;
|
margin-bottom: 2rem;
|
||||||
|
div[contenteditable="true"] *:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
.outdated {
|
.outdated {
|
||||||
color: $red;
|
color: $red;
|
||||||
}
|
}
|
||||||
|
@ -191,6 +197,7 @@ tg-wysiwyg {
|
||||||
.medium-editor-placeholder,
|
.medium-editor-placeholder,
|
||||||
.markdown-editor-placeholder {
|
.markdown-editor-placeholder {
|
||||||
color: $gray-light;
|
color: $gray-light;
|
||||||
|
overflow: visible;
|
||||||
padding-left: 1rem;
|
padding-left: 1rem;
|
||||||
&::after { // overwrite medium css
|
&::after { // overwrite medium css
|
||||||
color: $gray-light;
|
color: $gray-light;
|
||||||
|
|
|
@ -56,3 +56,12 @@
|
||||||
title="{{ 'COMMON.CANCEL' | translate }}"
|
title="{{ 'COMMON.CANCEL' | translate }}"
|
||||||
)
|
)
|
||||||
tg-svg(svg-icon="icon-close")
|
tg-svg(svg-icon="icon-close")
|
||||||
|
|
||||||
|
tg-wysiwyg-code-lightbox.lightbox.lightbox-generic-form(
|
||||||
|
languages="codeLans"
|
||||||
|
code-language="currentCodeLanguage"
|
||||||
|
code="code"
|
||||||
|
visible="codeEditorVisible"
|
||||||
|
on-close="codeEditorVisible = false"
|
||||||
|
on-save="saveSnippet(lan, code)"
|
||||||
|
)
|
|
@ -59,7 +59,7 @@
|
||||||
.no-description {
|
.no-description {
|
||||||
color: $gray-light;
|
color: $gray-light;
|
||||||
}
|
}
|
||||||
textarea {
|
.markdown {
|
||||||
background: $white;
|
background: $white;
|
||||||
height: 10rem;
|
height: 10rem;
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ $tribe-secondary: darken($tribe-primary, 10%);
|
||||||
|
|
||||||
$top-icon-color: $white;
|
$top-icon-color: $white;
|
||||||
$dropdown-color: rgba(darken($primary-dark, 20%), 1);
|
$dropdown-color: rgba(darken($primary-dark, 20%), 1);
|
||||||
|
$code-bg: #272822;
|
||||||
|
|
||||||
/* Overwrite mixins */
|
/* Overwrite mixins */
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ $tribe-secondary: darken($tribe-primary, 10%);
|
||||||
|
|
||||||
$top-icon-color: $white;
|
$top-icon-color: $white;
|
||||||
$dropdown-color: rgba(darken($primary-dark, 20%), 1);
|
$dropdown-color: rgba(darken($primary-dark, 20%), 1);
|
||||||
|
$code-bg: #272822;
|
||||||
|
|
||||||
/* Overwrite mixins */
|
/* Overwrite mixins */
|
||||||
|
|
||||||
|
|
|
@ -39,3 +39,4 @@ $tribe-secondary: darken($tribe-primary, 10%);
|
||||||
|
|
||||||
$top-icon-color: #11241f;
|
$top-icon-color: #11241f;
|
||||||
$dropdown-color: rgba(darken($grayer, 20%), 1);
|
$dropdown-color: rgba(darken($grayer, 20%), 1);
|
||||||
|
$code-bg: #272822;
|
|
@ -12,7 +12,12 @@ helper.title = function() {
|
||||||
},
|
},
|
||||||
|
|
||||||
setTitle: function(title) {
|
setTitle: function(title) {
|
||||||
el.$('.e2e-detail-edit').click();
|
browser
|
||||||
|
.actions()
|
||||||
|
.mouseMove(el.$('.e2e-detail-edit'))
|
||||||
|
.click()
|
||||||
|
.perform();
|
||||||
|
|
||||||
el.$('.e2e-title-input').clear().sendKeys(title);
|
el.$('.e2e-title-input').clear().sendKeys(title);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -14,16 +14,17 @@ var shared = module.exports;
|
||||||
|
|
||||||
function selectEditorFirstChild(elm) {
|
function selectEditorFirstChild(elm) {
|
||||||
browser.executeScript(function () {
|
browser.executeScript(function () {
|
||||||
// select the first paragraph
|
|
||||||
var range = document.createRange();
|
var range = document.createRange();
|
||||||
range.selectNode(arguments[0].firstChild);
|
|
||||||
|
range.setStart(arguments[0].firstChild.firstChild, 0);
|
||||||
|
range.setEnd(arguments[0].firstChild.firstChild, arguments[0].firstChild.innerText.length);
|
||||||
|
|
||||||
var sel = window.getSelection();
|
var sel = window.getSelection();
|
||||||
sel.removeAllRanges();
|
sel.removeAllRanges();
|
||||||
sel.addRange(range);
|
sel.addRange(range);
|
||||||
}, elm.getWebElement());
|
}, elm.getWebElement());
|
||||||
|
|
||||||
browser.actions().mouseUp().perform(); // trigger medium events
|
browser.actions().mouseUp().perform(); //trigger medium events
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetSelection() {
|
function resetSelection() {
|
||||||
|
@ -32,7 +33,7 @@ function resetSelection() {
|
||||||
sel.removeAllRanges();
|
sel.removeAllRanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
browser.actions().mouseUp().perform(); // trigger medium events
|
browser.actions().mouseUp().perform(); //trigger medium events
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMarkdownText(elm) {
|
function getMarkdownText(elm) {
|
||||||
|
@ -45,34 +46,76 @@ function getMarkdownTextarea(elm) {
|
||||||
return elm.$('.e2e-markdown-textarea');}
|
return elm.$('.e2e-markdown-textarea');}
|
||||||
|
|
||||||
|
|
||||||
function htmlMode() {
|
function htmlMode(elm) {
|
||||||
$('.e2e-html-mode').click();
|
elm.$('.e2e-html-mode').click();
|
||||||
|
|
||||||
|
return utils.common.waitElementPresent($('.e2e-markdown-mode'));
|
||||||
}
|
}
|
||||||
|
|
||||||
function markdownMode() {
|
function markdownMode(elm) {
|
||||||
$('.e2e-markdown-mode').click();
|
elm.$('.e2e-markdown-mode').click();
|
||||||
|
|
||||||
|
return utils.common.waitElementPresent($('.e2e-html-mode'));
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveEdition() {
|
function saveEdition(elm) {
|
||||||
$('.e2e-save-editor').click();
|
return elm.$('.e2e-save-editor').click();
|
||||||
}
|
}
|
||||||
|
|
||||||
function cancelEdition(elm) {
|
function cancelEdition(elm) {
|
||||||
$('.e2e-cancel-editor').click();
|
elm.$('.e2e-cancel-editor').click();
|
||||||
|
|
||||||
return browser.wait(async () => {
|
return browser.wait(async () => {
|
||||||
return !!await elm.$$('.read-mode').count();
|
return !!await elm.$$('.read-mode').count();
|
||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function closeMention() {
|
||||||
|
return utils.common.waitElementNotPresent($('.medium-mention'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function preventThrottle() {
|
||||||
|
return browser.sleep(250);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSnippeLightbox(parent) {
|
||||||
|
let el = parent.$('tg-wysiwyg-code-lightbox');
|
||||||
|
|
||||||
|
let obj = {
|
||||||
|
el: el,
|
||||||
|
waitOpen: function() {
|
||||||
|
return utils.lightbox.open(el);
|
||||||
|
},
|
||||||
|
waitClose: function() {
|
||||||
|
return utils.lightbox.close(el);
|
||||||
|
},
|
||||||
|
select: function(lan) {
|
||||||
|
return el.$('select').sendKeys('javascript');
|
||||||
|
},
|
||||||
|
save: function() {
|
||||||
|
return el.$('button').click();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
||||||
async function edit(elm, elmWrapper, text = null) {
|
async function edit(elm, elmWrapper, text = null) {
|
||||||
await browser.wait(EC.elementToBeClickable(elm), 10000);
|
await browser.wait(EC.elementToBeClickable(elm), 10000);
|
||||||
|
|
||||||
elm.click();
|
elm.click();
|
||||||
|
|
||||||
browser.sleep(200);
|
await browser.sleep(2000);
|
||||||
|
|
||||||
browser.executeScript(function () {
|
if (text !== null) {
|
||||||
|
await cleanWysiwyg(elm, elmWrapper);
|
||||||
|
|
||||||
|
return elm.sendKeys(text);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function cleanWysiwyg(elm, elmWrapper) {
|
||||||
|
await browser.executeScript(function () {
|
||||||
if(arguments[0].firstChild) {
|
if(arguments[0].firstChild) {
|
||||||
var range = document.createRange();
|
var range = document.createRange();
|
||||||
range.setStart(arguments[0].firstChild, 0);
|
range.setStart(arguments[0].firstChild, 0);
|
||||||
|
@ -84,26 +127,7 @@ async function edit(elm, elmWrapper, text = null) {
|
||||||
}
|
}
|
||||||
}, elm.getWebElement());
|
}, elm.getWebElement());
|
||||||
|
|
||||||
if (text !== null) {
|
return elm.sendKeys(protractor.Key.BACK_SPACE);
|
||||||
await cleanWysiwyg(elm, elmWrapper);
|
|
||||||
|
|
||||||
return elm.sendKeys(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function cleanWysiwyg(elm, elmWrapper) {
|
|
||||||
let isHtmlMode = await elm.isDisplayed();
|
|
||||||
|
|
||||||
if (isHtmlMode) {
|
|
||||||
let isPresent = await $('.e2e-markdown-mode').isPresent();
|
|
||||||
|
|
||||||
markdownMode();
|
|
||||||
}
|
|
||||||
var markdownTextarea = getMarkdownTextarea(elmWrapper);
|
|
||||||
|
|
||||||
await utils.common.clear(markdownTextarea);
|
|
||||||
|
|
||||||
return htmlMode();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
shared.wysiwygTestingComments = function(parentSelector, section) {
|
shared.wysiwygTestingComments = function(parentSelector, section) {
|
||||||
|
@ -126,15 +150,15 @@ shared.wysiwygTestingComments = function(parentSelector, section) {
|
||||||
|
|
||||||
resetSelection();
|
resetSelection();
|
||||||
|
|
||||||
markdownMode();
|
markdownMode(editorWrapper);
|
||||||
|
|
||||||
let markdown = await getMarkdownText(editorWrapper);
|
let markdown = await getMarkdownText(editorWrapper);
|
||||||
|
|
||||||
expect(markdown).to.be.equal('**test**');
|
expect(markdown).to.be.equal('**test**');
|
||||||
|
|
||||||
htmlMode();
|
await htmlMode(editorWrapper);
|
||||||
|
|
||||||
saveEdition();
|
await saveEdition(editorWrapper);
|
||||||
|
|
||||||
let newCommentsCounter = await historyHelper.countComments();
|
let newCommentsCounter = await historyHelper.countComments();
|
||||||
expect(newCommentsCounter).to.be.equal(commentsCounter+1);
|
expect(newCommentsCounter).to.be.equal(commentsCounter+1);
|
||||||
|
@ -145,50 +169,29 @@ shared.wysiwygTestingComments = function(parentSelector, section) {
|
||||||
|
|
||||||
await edit(editor, editorWrapper, '');
|
await edit(editor, editorWrapper, '');
|
||||||
|
|
||||||
markdownMode();
|
markdownMode(editorWrapper);
|
||||||
|
|
||||||
let markdownTextarea = getMarkdownTextarea(editorWrapper);
|
let markdownTextarea = getMarkdownTextarea(editorWrapper);
|
||||||
|
|
||||||
await markdownTextarea.sendKeys('_test2_');
|
await markdownTextarea.sendKeys('_test2_');
|
||||||
|
|
||||||
htmlMode();
|
await htmlMode(editorWrapper);
|
||||||
|
|
||||||
let html = await editor.getAttribute("innerHTML");
|
let html = await editor.getAttribute("innerHTML");
|
||||||
|
|
||||||
expect(html).to.be.eql('<p><em>test2</em></p>\n');
|
expect(html).to.be.eql('<p><em>test2</em></p>\n');
|
||||||
|
|
||||||
saveEdition();
|
await saveEdition(editorWrapper);
|
||||||
|
|
||||||
let newCommentsCounter = await historyHelper.countComments();
|
let newCommentsCounter = await historyHelper.countComments();
|
||||||
expect(newCommentsCounter).to.be.equal(commentsCounter+1);
|
expect(newCommentsCounter).to.be.equal(commentsCounter+1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('code block', async () => {
|
|
||||||
await edit(editor, editorWrapper, '');
|
|
||||||
|
|
||||||
editor.sendKeys("var test = 2;");
|
|
||||||
|
|
||||||
selectEditorFirstChild(editor);
|
|
||||||
|
|
||||||
$('.medium-editor-toolbar-active .medium-editor-button-last').click();
|
|
||||||
|
|
||||||
$('.code-language-selector').click();
|
|
||||||
$('.code-language-search input').sendKeys('javascript');
|
|
||||||
$('.code-language-search li').click();
|
|
||||||
|
|
||||||
saveEdition();
|
|
||||||
|
|
||||||
let lastComment = historyHelper.getComments().last();
|
|
||||||
|
|
||||||
let hasHightlighter = !!await lastComment.$$('.token').count();
|
|
||||||
|
|
||||||
expect(hasHightlighter).to.be.true;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('confirm exit when there is changes', async () => {
|
it('confirm exit when there is changes', async () => {
|
||||||
await edit(editor, editorWrapper, '');
|
await edit(editor, editorWrapper, '');
|
||||||
|
|
||||||
editor.sendKeys('text text text');
|
editor.sendKeys('text text text');
|
||||||
|
await preventThrottle();
|
||||||
editor.sendKeys(protractor.Key.ESCAPE);
|
editor.sendKeys(protractor.Key.ESCAPE);
|
||||||
|
|
||||||
await utils.lightbox.confirm.ok();
|
await utils.lightbox.confirm.ok();
|
||||||
|
@ -206,6 +209,7 @@ shared.wysiwygTestingComments = function(parentSelector, section) {
|
||||||
await edit(editor, editorWrapper, '');
|
await edit(editor, editorWrapper, '');
|
||||||
|
|
||||||
editor.sendKeys('text text text');
|
editor.sendKeys('text text text');
|
||||||
|
await preventThrottle();
|
||||||
editor.sendKeys(protractor.Key.ESCAPE);
|
editor.sendKeys(protractor.Key.ESCAPE);
|
||||||
|
|
||||||
browser.sleep(400);
|
browser.sleep(400);
|
||||||
|
@ -225,21 +229,21 @@ shared.wysiwygTestingComments = function(parentSelector, section) {
|
||||||
it('mention user', async () => {
|
it('mention user', async () => {
|
||||||
await edit(editor, editorWrapper, '');
|
await edit(editor, editorWrapper, '');
|
||||||
|
|
||||||
editor.sendKeys('@use');
|
editor.sendKeys('@user8');
|
||||||
|
|
||||||
$$('.medium-mention li').get(2).click();
|
$$('.medium-mention li').get(0).click();
|
||||||
|
|
||||||
let html = await editor.getAttribute("innerHTML");
|
let html = await editor.getAttribute("innerHTML");
|
||||||
|
|
||||||
expect(html).to.be.eql('<p><a href="/profile/user8">@user8</a> </p>');
|
expect(html).to.be.eql('<p><a href="/profile/user8">@user8</a> </p>');
|
||||||
|
|
||||||
markdownMode();
|
markdownMode(editorWrapper);
|
||||||
|
|
||||||
let markdown = await getMarkdownText(editorWrapper);
|
let markdown = await getMarkdownText(editorWrapper);
|
||||||
|
|
||||||
expect(markdown).to.be.equal('[@user8](/profile/user8)');
|
expect(markdown).to.be.equal('[@user8](/profile/user8)');
|
||||||
|
|
||||||
htmlMode();
|
await htmlMode(editorWrapper);
|
||||||
|
|
||||||
await cancelEdition(editorWrapper);
|
await cancelEdition(editorWrapper);
|
||||||
});
|
});
|
||||||
|
@ -255,13 +259,13 @@ shared.wysiwygTestingComments = function(parentSelector, section) {
|
||||||
|
|
||||||
expect(html).to.include('1f604.png');
|
expect(html).to.include('1f604.png');
|
||||||
|
|
||||||
markdownMode();
|
markdownMode(editorWrapper);
|
||||||
|
|
||||||
let markdown = await getMarkdownText(editorWrapper);
|
let markdown = await getMarkdownText(editorWrapper);
|
||||||
|
|
||||||
expect(markdown).to.be.equal(':smile:');
|
expect(markdown).to.be.equal(':smile:');
|
||||||
|
|
||||||
htmlMode();
|
await htmlMode(editorWrapper);
|
||||||
|
|
||||||
await cancelEdition(editorWrapper);
|
await cancelEdition(editorWrapper);
|
||||||
});
|
});
|
||||||
|
@ -287,7 +291,7 @@ shared.wysiwygTestingComments = function(parentSelector, section) {
|
||||||
await edit(editLast, editWrapperLast, "This is the new and updated text");
|
await edit(editLast, editWrapperLast, "This is the new and updated text");
|
||||||
await utils.common.takeScreenshot(section, "edit comment");
|
await utils.common.takeScreenshot(section, "edit comment");
|
||||||
|
|
||||||
saveEdition();
|
await saveEdition(editWrapperLast);
|
||||||
|
|
||||||
//Show versions from last comment edited
|
//Show versions from last comment edited
|
||||||
historyHelper.showVersionsLastComment();
|
historyHelper.showVersionsLastComment();
|
||||||
|
@ -318,6 +322,32 @@ shared.wysiwygTestingComments = function(parentSelector, section) {
|
||||||
|
|
||||||
await utils.common.takeScreenshot(section, 'restored comment');
|
await utils.common.takeScreenshot(section, 'restored comment');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('code block', async () => {
|
||||||
|
await edit(editor, editorWrapper, '');
|
||||||
|
|
||||||
|
editor.sendKeys("var test = 2;");
|
||||||
|
|
||||||
|
selectEditorFirstChild(editor);
|
||||||
|
|
||||||
|
$('.medium-editor-toolbar-active .medium-editor-button-last').click();
|
||||||
|
|
||||||
|
browser.actions().doubleClick(editor.$('code')).perform();
|
||||||
|
|
||||||
|
let lb = getSnippeLightbox(editorWrapper);
|
||||||
|
|
||||||
|
await lb.waitOpen();
|
||||||
|
|
||||||
|
await lb.select('javascript');
|
||||||
|
await lb.save();
|
||||||
|
await lb.waitClose();
|
||||||
|
|
||||||
|
let hasHightlighter = !!await editor.$$('.token').count();
|
||||||
|
|
||||||
|
expect(hasHightlighter).to.be.true;
|
||||||
|
|
||||||
|
await saveEdition(editorWrapper);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
shared.wysiwygTesting = function(parentSelector) {
|
shared.wysiwygTesting = function(parentSelector) {
|
||||||
|
@ -331,9 +361,14 @@ shared.wysiwygTesting = function(parentSelector) {
|
||||||
editor.click();
|
editor.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let isHtmlMode = await editor.isDisplayed();
|
||||||
|
if (!isHtmlMode) {
|
||||||
|
await htmlMode(editorWrapper);
|
||||||
|
}
|
||||||
|
|
||||||
await cleanWysiwyg(editor, editorWrapper);
|
await cleanWysiwyg(editor, editorWrapper);
|
||||||
|
|
||||||
markdownMode();
|
markdownMode(editorWrapper);
|
||||||
|
|
||||||
var markdownTextarea = getMarkdownTextarea(editorWrapper);
|
var markdownTextarea = getMarkdownTextarea(editorWrapper);
|
||||||
|
|
||||||
|
@ -341,9 +376,9 @@ shared.wysiwygTesting = function(parentSelector) {
|
||||||
|
|
||||||
await markdownTextarea.sendKeys('test');
|
await markdownTextarea.sendKeys('test');
|
||||||
|
|
||||||
htmlMode();
|
await htmlMode(editorWrapper);
|
||||||
|
|
||||||
saveEdition();
|
await saveEdition(editorWrapper);
|
||||||
|
|
||||||
await browser.wait(EC.elementToBeClickable(editor), 10000);
|
await browser.wait(EC.elementToBeClickable(editor), 10000);
|
||||||
});
|
});
|
||||||
|
@ -364,13 +399,13 @@ shared.wysiwygTesting = function(parentSelector) {
|
||||||
|
|
||||||
let html = await editor.getAttribute("innerHTML");
|
let html = await editor.getAttribute("innerHTML");
|
||||||
|
|
||||||
expect(html).to.be.eql('<p><b>test</b></p>');
|
expect(html).to.be.eql('<p><b>test</b></p>\n');
|
||||||
|
|
||||||
saveEdition();
|
await saveEdition(editorWrapper);
|
||||||
|
|
||||||
await edit(editor, editorWrapper);
|
await edit(editor, editorWrapper);
|
||||||
|
|
||||||
markdownMode();
|
markdownMode(editorWrapper);
|
||||||
|
|
||||||
let markdown = await getMarkdownText(editorWrapper);
|
let markdown = await getMarkdownText(editorWrapper);
|
||||||
|
|
||||||
|
@ -380,43 +415,24 @@ shared.wysiwygTesting = function(parentSelector) {
|
||||||
it('convert to html', async () => {
|
it('convert to html', async () => {
|
||||||
await edit(editor, editorWrapper, '');
|
await edit(editor, editorWrapper, '');
|
||||||
|
|
||||||
markdownMode();
|
markdownMode(editorWrapper);
|
||||||
|
|
||||||
let markdownTextarea = getMarkdownTextarea(editorWrapper);
|
let markdownTextarea = getMarkdownTextarea(editorWrapper);
|
||||||
|
|
||||||
await markdownTextarea.sendKeys('_test2_');
|
await markdownTextarea.sendKeys('_test2_');
|
||||||
|
|
||||||
htmlMode();
|
htmlMode(editorWrapper);
|
||||||
|
|
||||||
let html = await editor.getAttribute("innerHTML");
|
let html = await editor.getAttribute("innerHTML");
|
||||||
|
|
||||||
expect(html).to.be.eql('<p><em>test2</em></p>\n');
|
expect(html).to.be.eql('<p><em>test2</em></p>\n');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('code block', async () => {
|
|
||||||
await edit(editor, editorWrapper, '');
|
|
||||||
|
|
||||||
editor.sendKeys("var test = 2;");
|
|
||||||
|
|
||||||
selectEditorFirstChild(editor);
|
|
||||||
|
|
||||||
$('.medium-editor-toolbar-active .medium-editor-button-last').click();
|
|
||||||
|
|
||||||
$('.code-language-selector').click();
|
|
||||||
$('.code-language-search input').sendKeys('javascript');
|
|
||||||
$('.code-language-search li').click();
|
|
||||||
|
|
||||||
saveEdition();
|
|
||||||
|
|
||||||
let hasHightlighter = !!await editor.$$('.token').count();
|
|
||||||
|
|
||||||
expect(hasHightlighter).to.be.true;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('save with confirmconfirm exit when there is changes', async () => {
|
it('save with confirmconfirm exit when there is changes', async () => {
|
||||||
await edit(editor, editorWrapper, '');
|
await edit(editor, editorWrapper, '');
|
||||||
|
|
||||||
editor.sendKeys('text text text');
|
editor.sendKeys('text text text');
|
||||||
|
await preventThrottle();
|
||||||
editor.sendKeys(protractor.Key.ESCAPE);
|
editor.sendKeys(protractor.Key.ESCAPE);
|
||||||
|
|
||||||
await utils.lightbox.confirm.ok();
|
await utils.lightbox.confirm.ok();
|
||||||
|
@ -434,6 +450,7 @@ shared.wysiwygTesting = function(parentSelector) {
|
||||||
await edit(editor, editorWrapper, '');
|
await edit(editor, editorWrapper, '');
|
||||||
|
|
||||||
editor.sendKeys('text text text');
|
editor.sendKeys('text text text');
|
||||||
|
await preventThrottle();
|
||||||
editor.sendKeys(protractor.Key.ESCAPE);
|
editor.sendKeys(protractor.Key.ESCAPE);
|
||||||
|
|
||||||
browser.sleep(400);
|
browser.sleep(400);
|
||||||
|
@ -451,21 +468,24 @@ shared.wysiwygTesting = function(parentSelector) {
|
||||||
it('mention user', async () => {
|
it('mention user', async () => {
|
||||||
await edit(editor, editorWrapper, '');
|
await edit(editor, editorWrapper, '');
|
||||||
|
|
||||||
editor.sendKeys('@use');
|
await editor.sendKeys('@user5');
|
||||||
|
|
||||||
$$('.medium-mention li').get(2).click();
|
$$('.medium-mention li').get(0).click();
|
||||||
|
|
||||||
|
await closeMention();
|
||||||
|
|
||||||
let html = await editor.getAttribute("innerHTML");
|
let html = await editor.getAttribute("innerHTML");
|
||||||
|
|
||||||
expect(html).to.be.eql('<p><a href="/profile/user8">@user8</a> </p>');
|
|
||||||
|
|
||||||
markdownMode();
|
expect(html).to.be.eql('<p><a href="/profile/user5">@user5</a> </p>\n');
|
||||||
|
|
||||||
|
markdownMode(editorWrapper);
|
||||||
|
|
||||||
let markdown = await getMarkdownText(editorWrapper);
|
let markdown = await getMarkdownText(editorWrapper);
|
||||||
|
|
||||||
expect(markdown).to.be.equal('[@user8](/profile/user8)');
|
expect(markdown).to.be.equal('[@user5](/profile/user5)');
|
||||||
|
|
||||||
htmlMode();
|
htmlMode(editorWrapper);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('emojis', async () => {
|
it('emojis', async () => {
|
||||||
|
@ -473,13 +493,15 @@ shared.wysiwygTesting = function(parentSelector) {
|
||||||
|
|
||||||
editor.sendKeys(':smil');
|
editor.sendKeys(':smil');
|
||||||
|
|
||||||
$$('.medium-mention li').get(2).click();
|
await $$('.medium-mention li').get(2).click();
|
||||||
|
|
||||||
|
await closeMention();
|
||||||
|
|
||||||
let html = await editor.getAttribute("innerHTML");
|
let html = await editor.getAttribute("innerHTML");
|
||||||
|
|
||||||
expect(html).to.include('1f604.png');
|
expect(html).to.include('1f604.png');
|
||||||
|
|
||||||
markdownMode();
|
markdownMode(editorWrapper);
|
||||||
|
|
||||||
let markdown = await getMarkdownText(editorWrapper);
|
let markdown = await getMarkdownText(editorWrapper);
|
||||||
|
|
||||||
|
@ -487,14 +509,40 @@ shared.wysiwygTesting = function(parentSelector) {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('cancel', async () => {
|
it('cancel', async () => {
|
||||||
let prevHtml = await editor.getAttribute("innerHTML");
|
let prevHtml = await editor.getAttribute("innerHTML");
|
||||||
|
|
||||||
await edit(editor, editorWrapper, 'xxx yyy zzz');
|
await edit(editor, editorWrapper, 'xxx yyy zzz');
|
||||||
|
|
||||||
await cancelEdition(editorWrapper);
|
await cancelEdition(editorWrapper);
|
||||||
|
|
||||||
let html = await editor.getAttribute("innerHTML");
|
let html = await editor.getAttribute("innerHTML");
|
||||||
|
|
||||||
expect(html).to.be.equal(prevHtml);
|
expect(html).to.be.equal(prevHtml);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('code block', async () => {
|
||||||
|
await edit(editor, editorWrapper, '');
|
||||||
|
|
||||||
|
editor.sendKeys("var test = 2;");
|
||||||
|
|
||||||
|
selectEditorFirstChild(editor);
|
||||||
|
|
||||||
|
$('.medium-editor-toolbar-active .medium-editor-button-last').click();
|
||||||
|
|
||||||
|
browser.actions().doubleClick(editor.$('code')).perform();
|
||||||
|
|
||||||
|
let lb = getSnippeLightbox(editorWrapper);
|
||||||
|
|
||||||
|
await lb.waitOpen();
|
||||||
|
|
||||||
|
await lb.select('javascript');
|
||||||
|
await lb.save();
|
||||||
|
await lb.waitClose();
|
||||||
|
|
||||||
|
await saveEdition(editorWrapper);
|
||||||
|
|
||||||
|
let hasHightlighter = !!await editor.$$('.token').count();
|
||||||
|
|
||||||
|
expect(hasHightlighter).to.be.true;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -476,7 +476,7 @@ describe('backlog', function() {
|
||||||
|
|
||||||
expect(sprintCount).is.below(newSprintCount);
|
expect(sprintCount).is.below(newSprintCount);
|
||||||
});
|
});
|
||||||
it.only('hide forecasting if no velocity', async function() {
|
it('hide forecasting if no velocity', async function() {
|
||||||
browser.get(browser.params.glob.host + 'project/project-5/backlog');
|
browser.get(browser.params.glob.host + 'project/project-5/backlog');
|
||||||
await utils.common.waitLoader();
|
await utils.common.waitLoader();
|
||||||
|
|
||||||
|
|
|
@ -65,29 +65,7 @@ describe('wiki', function() {
|
||||||
await utils.common.takeScreenshot("wiki", "deleting-the-created-link");
|
await utils.common.takeScreenshot("wiki", "deleting-the-created-link");
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('wiki editor', sharedWysiwyg.bind(this));
|
describe('wiki editor', sharedWysiwyg.bind(this, '.wiki'));
|
||||||
|
|
||||||
it('confirm close with ESC in lightbox', async function() {
|
|
||||||
wikiHelper.editor().enabledEditionMode();
|
|
||||||
|
|
||||||
browser.actions().sendKeys(protractor.Key.ESCAPE).perform();
|
|
||||||
|
|
||||||
await utils.lightbox.confirm.cancel();
|
|
||||||
|
|
||||||
let descriptionVisibility = await $('.view-wiki-content').isDisplayed();
|
|
||||||
|
|
||||||
expect(descriptionVisibility).to.be.false;
|
|
||||||
|
|
||||||
wikiHelper.editor().focus();
|
|
||||||
|
|
||||||
browser.actions().sendKeys(protractor.Key.ESCAPE).perform();
|
|
||||||
|
|
||||||
await utils.lightbox.confirm.ok();
|
|
||||||
|
|
||||||
descriptionVisibility = await $('.view-wiki-content').isDisplayed();
|
|
||||||
|
|
||||||
expect(descriptionVisibility).to.be.true;
|
|
||||||
});
|
|
||||||
|
|
||||||
it('attachments', sharedDetail.attachmentTesting);
|
it('attachments', sharedDetail.attachmentTesting);
|
||||||
|
|
||||||
|
@ -96,28 +74,4 @@ describe('wiki', function() {
|
||||||
|
|
||||||
expect(browser.getCurrentUrl()).to.be.eventually.equal(browser.params.glob.host + 'project/project-0/wiki/home');
|
expect(browser.getCurrentUrl()).to.be.eventually.equal(browser.params.glob.host + 'project/project-0/wiki/home');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Custom keyboard actions', async function(){
|
|
||||||
wikiHelper.editor().enabledEditionMode();
|
|
||||||
|
|
||||||
wikiHelper.editor().setText("- aa");
|
|
||||||
browser.actions().sendKeys(protractor.Key.ENTER).perform();
|
|
||||||
let text = await wikiHelper.editor().getText();
|
|
||||||
expect(text).to.be.equal("- aa\n- ");
|
|
||||||
|
|
||||||
wikiHelper.editor().setText("- ");
|
|
||||||
browser.actions().sendKeys(protractor.Key.ENTER).perform();
|
|
||||||
text = await wikiHelper.editor().getText();
|
|
||||||
expect(text).to.be.equal("\n");
|
|
||||||
|
|
||||||
wikiHelper.editor().setText("- bbcc");
|
|
||||||
browser.actions().sendKeys(protractor.Key.ARROW_LEFT).sendKeys(protractor.Key.ARROW_LEFT).sendKeys(protractor.Key.ENTER).perform();
|
|
||||||
text = await wikiHelper.editor().getText();
|
|
||||||
expect(text).to.be.equal("- bb\n- cc");
|
|
||||||
|
|
||||||
wikiHelper.editor().setText("- aa");
|
|
||||||
browser.actions().sendKeys(protractor.Key.HOME).sendKeys(protractor.Key.ENTER).perform();
|
|
||||||
text = await wikiHelper.editor().getText();
|
|
||||||
expect(text).to.be.equal("\n- aa");
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -20,6 +20,20 @@ common.getElm = function(el) {
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
common.waitElementNotPresent = function(el) {
|
||||||
|
return browser.wait(function() {
|
||||||
|
return el.isPresent().then(function(present) {
|
||||||
|
return !present;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
common.waitElementPresent = function(el) {
|
||||||
|
return browser.wait(function() {
|
||||||
|
return el.isPresent();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
common.hasClass = async function (element, cls) {
|
common.hasClass = async function (element, cls) {
|
||||||
let classes = await element.getAttribute('class');
|
let classes = await element.getAttribute('class');
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue