code snippets

stable
Juanfran 2017-02-23 15:21:13 +01:00 committed by David Barragán Merino
parent 1f47a4b9b1
commit 1a15ab955e
19 changed files with 423 additions and 363 deletions

View File

@ -6,7 +6,7 @@ var MentionExtension = MediumEditor.Extension.extend({
this.subscribe('blur', this.cancel.bind(this));
},
isEditMode: function() {
return !this.base.origElements.parentNode.classList.contains('read-mode')
return !this.base.origElements.parentNode.classList.contains('read-mode');
},
cancel: function() {
if (this.isEditMode()) {
@ -248,7 +248,7 @@ var MentionExtension = MediumEditor.Extension.extend({
li.innerText = '@' + it.username;
}
li.addEventListener('click', this.selectMention.bind(this, it));
li.addEventListener('mousedown', this.selectMention.bind(this, it));
ul.appendChild(li);
}.bind(this));

View File

@ -214,6 +214,9 @@
}
},
"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.",
"MARKDOWN_HELP": "Markdown syntax help"
},

View File

@ -11,11 +11,12 @@
span.detail-subject.e2e-title-subject(
ng-if="!vm.permissions.canEdit"
) {{vm.item.subject}}
tg-svg.detail-edit.e2e-detail-edit(
a(
href=""
ng-if="vm.permissions.canEdit"
svg-icon="icon-edit"
ng-click="vm.editSubject(true)"
)
tg-svg.detail-edit.e2e-detail-edit(svg-icon="icon-edit")
.edit-title-wrapper(ng-if="vm.editMode")
input.edit-title-input.e2e-title-input(

View File

@ -23,17 +23,22 @@
###
class WysiwygCodeHightlighterService
constructor: () ->
if !@.languages
@.loadLanguages()
loadLanguages: () ->
$.getJSON("/#{window._version}/prism/prism-languages.json").then (_languages_) =>
getLanguages: () ->
return new Promise (resolve, reject) =>
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
return it
resolve(@.languages)
getLanguageInClassList: (classes) ->
lan = _.find @.languages, (it) ->
return !!_.find classes, (className) ->
@ -41,123 +46,6 @@ class WysiwygCodeHightlighterService
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) ->
return new Promise (resolve) ->
if !Prism.languages[lan]
@ -165,20 +53,11 @@ class WysiwygCodeHightlighterService
else
resolve()
removeHightlighter: (element) ->
codes = $(element).find('code')
codes.each (index, code) ->
code.innerHTML = code.innerText
# firefox adds br instead of new lines inside <code>
replaceCodeBrToNl: (code) ->
$(code).find('br').replaceWith('\n')
addHightlighter: (element) ->
codes = $(element).find('code')
codes.each (index, code) =>
hightlightCode: (code) ->
@.replaceCodeBrToNl(code)
lan = @.getLanguageInClassList(code.classList)
@ -186,14 +65,10 @@ class WysiwygCodeHightlighterService
if lan
@.loadLanguage(lan).then () -> Prism.highlightElement(code)
updateCodeLanguageSelector: (mediumInstance) ->
$('.medium-' + mediumInstance.id).each (index, tab) =>
node = $('.' + tab.dataset.tabId)
addHightlighter: (element) ->
codes = $(element).find('code')
if !node.length
tab.remove()
else
@.updatePositionCodeTab(node.parent()[0], tab)
codes.each (index, code) => @.hightlightCode(code)
angular.module("taigaComponents")
.service("tgWysiwygCodeHightlighterService", WysiwygCodeHightlighterService)

View File

@ -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])

View File

@ -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"
)

View File

@ -0,0 +1,3 @@
tg-wysiwyg-code-lightbox textarea {
height: 350px;
}

View File

@ -26,36 +26,78 @@ taiga = @.taiga
bindOnce = @.taiga.bindOnce
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) ->
return !!$(range.endContainer).parentsUntil('.editor', 'code').length
else if document.selection
document.selection.empty()
refreshCodeBlockHightlight = (elm) ->
wysiwygCodeHightlighterService.refreshCodeLanguageSelectors(elm)
getRangeCodeBlock = (range) ->
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
p = document.createElement('p')
p.innerText = code.innerText
pre.parentNode.replaceChild(p, pre)
wysiwygCodeHightlighterService.removeCodeLanguageSelectors(elm)
mediumInstance.checkContentChanged(mediumInstance.elements[0])
addCodeBlockAndHightlight = (range, elm) ->
pre = document.createElement('pre')
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())
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({
name: 'rtl',
@ -107,9 +149,16 @@ Medium = ($translate, $confirm, $storage, wysiwygService, animationFrame, tgLoad
range = MediumEditor.selection.getSelectionRange(self.document)
if isCodeBlockSelected(range, this.base)
removeCodeBlockAndHightlight(range, this.base)
removeCodeBlockAndHightlight(range.endContainer, this.base)
else
addCodeBlockAndHightlight(range, this.base)
removeSelections()
toolbar = this.base.getExtensionByName('toolbar')
if toolbar
toolbar.hideToolbar()
})
CustomPasteHandler = MediumEditor.extensions.paste.extend({
@ -141,6 +190,7 @@ Medium = ($translate, $confirm, $storage, wysiwygService, animationFrame, tgLoad
mediumInstance = null
editorMedium = $el.find('.medium')
editorMarkdown = $el.find('.markdown')
codeBlockSelected = null
isEditOnly = !!$attrs.$attr.editonly
notPersist = !!$attrs.$attr.notPersist
@ -149,12 +199,47 @@ Medium = ($translate, $confirm, $storage, wysiwygService, animationFrame, tgLoad
$scope.editMode = isEditOnly || false
$scope.mode = $storage.get('editor-mode', 'html')
$scope.markdown = ''
$scope.codeEditorVisible = false
$scope.codeLans = []
wysiwygService.loadEmojis()
wysiwygCodeHightlighterService.getLanguages().then (codeLans) ->
$scope.codeLans = codeLans
setHtmlMedium = (markdown) ->
html = wysiwygService.getHTML(markdown)
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) ->
$storage.set('editor-mode', mode)
@ -191,7 +276,7 @@ Medium = ($translate, $confirm, $storage, wysiwygService, animationFrame, tgLoad
if notPersist
clean()
else if $scope.mode == 'html'
setHtmlMedium($scope.content)
setHtmlMedium($scope.content || null)
$scope.markdown = $scope.content
@ -207,19 +292,6 @@ Medium = ($translate, $confirm, $storage, wysiwygService, animationFrame, tgLoad
$scope.markdown = ''
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 = () ->
$scope.saving = false
@ -305,7 +377,6 @@ Medium = ($translate, $confirm, $storage, wysiwygService, animationFrame, tgLoad
change = () ->
if $scope.mode == 'html'
updateMarkdownWithCurrentHtml()
wysiwygCodeHightlighterService.updateCodeLanguageSelector(mediumInstance)
localSave($scope.markdown)
@ -415,24 +486,6 @@ Medium = ($translate, $confirm, $storage, wysiwygService, animationFrame, tgLoad
mediumInstance.subscribe 'editableDrop', (event) ->
$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) ->
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.$applyAsync(refreshExtras)
$scope.$applyAsync () ->
wysiwygCodeHightlighterService.addHightlighter(mediumInstance.elements[0])
refreshCodeBlocks(mediumInstance)
$scope.$watch () ->
return $scope.mode + ":" + $scope.editMode
, () ->
$scope.$applyAsync(refreshExtras)
$(editorMedium[0]).on 'dblclick', 'pre', (e) ->
$scope.$applyAsync () ->
$scope.codeEditorVisible = true
codeBlockSelected = e.currentTarget.querySelector('code')
$scope.currentCodeLanguage = wysiwygCodeHightlighterService.getLanguageInClassList(codeBlockSelected.classList)
$scope.code = codeBlockSelected.innerText
unwatch = $scope.$watch 'content', (content) ->
if !_.isUndefined(content)
@ -466,7 +525,7 @@ Medium = ($translate, $confirm, $storage, wysiwygService, animationFrame, tgLoad
if !mediumInstance && isDraft()
$scope.editMode = true
if $scope.markdown == content
if ($scope.markdown.length || content.length) && $scope.markdown == content
return
content = getCurrentContent()
@ -487,7 +546,6 @@ Medium = ($translate, $confirm, $storage, wysiwygService, animationFrame, tgLoad
$scope.$on "$destroy", () ->
if mediumInstance
wysiwygCodeHightlighterService.removeCodeLanguageSelectors(mediumInstance)
mediumInstance.destroy()
return {

View File

@ -90,12 +90,12 @@
}
pre:not([class*="language-"]) {
@include font-size(small);
background: lighten($grayer, 10%);
background: $code-bg;
color: $whitish;
direction: ltr;
font-family: 'courier new', 'monospace';
line-height: 1.4rem;
margin-bottom: 1rem;
margin-bottom: .5rem;
overflow: auto;
padding: 1rem;
unicode-bidi: embed;
@ -165,6 +165,12 @@
tg-wysiwyg {
display: flex;
margin-bottom: 2rem;
div[contenteditable="true"] *:last-child {
margin-bottom: 0;
}
pre {
cursor: pointer;
}
.outdated {
color: $red;
}
@ -191,6 +197,7 @@ tg-wysiwyg {
.medium-editor-placeholder,
.markdown-editor-placeholder {
color: $gray-light;
overflow: visible;
padding-left: 1rem;
&::after { // overwrite medium css
color: $gray-light;

View File

@ -56,3 +56,12 @@
title="{{ 'COMMON.CANCEL' | translate }}"
)
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)"
)

View File

@ -59,7 +59,7 @@
.no-description {
color: $gray-light;
}
textarea {
.markdown {
background: $white;
height: 10rem;
}

View File

@ -39,6 +39,7 @@ $tribe-secondary: darken($tribe-primary, 10%);
$top-icon-color: $white;
$dropdown-color: rgba(darken($primary-dark, 20%), 1);
$code-bg: #272822;
/* Overwrite mixins */

View File

@ -39,6 +39,7 @@ $tribe-secondary: darken($tribe-primary, 10%);
$top-icon-color: $white;
$dropdown-color: rgba(darken($primary-dark, 20%), 1);
$code-bg: #272822;
/* Overwrite mixins */

View File

@ -39,3 +39,4 @@ $tribe-secondary: darken($tribe-primary, 10%);
$top-icon-color: #11241f;
$dropdown-color: rgba(darken($grayer, 20%), 1);
$code-bg: #272822;

View File

@ -12,7 +12,12 @@ helper.title = function() {
},
setTitle: function(title) {
el.$('.e2e-detail-edit').click();
browser
.actions()
.mouseMove(el.$('.e2e-detail-edit'))
.click()
.perform();
el.$('.e2e-title-input').clear().sendKeys(title);
},

View File

@ -14,16 +14,17 @@ var shared = module.exports;
function selectEditorFirstChild(elm) {
browser.executeScript(function () {
// select the first paragraph
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();
sel.removeAllRanges();
sel.addRange(range);
}, elm.getWebElement());
browser.actions().mouseUp().perform(); // trigger medium events
browser.actions().mouseUp().perform(); //trigger medium events
}
function resetSelection() {
@ -32,7 +33,7 @@ function resetSelection() {
sel.removeAllRanges();
});
browser.actions().mouseUp().perform(); // trigger medium events
browser.actions().mouseUp().perform(); //trigger medium events
}
function getMarkdownText(elm) {
@ -45,34 +46,76 @@ function getMarkdownTextarea(elm) {
return elm.$('.e2e-markdown-textarea');}
function htmlMode() {
$('.e2e-html-mode').click();
function htmlMode(elm) {
elm.$('.e2e-html-mode').click();
return utils.common.waitElementPresent($('.e2e-markdown-mode'));
}
function markdownMode() {
$('.e2e-markdown-mode').click();
function markdownMode(elm) {
elm.$('.e2e-markdown-mode').click();
return utils.common.waitElementPresent($('.e2e-html-mode'));
}
function saveEdition() {
$('.e2e-save-editor').click();
function saveEdition(elm) {
return elm.$('.e2e-save-editor').click();
}
function cancelEdition(elm) {
$('.e2e-cancel-editor').click();
elm.$('.e2e-cancel-editor').click();
return browser.wait(async () => {
return !!await elm.$$('.read-mode').count();
}, 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) {
await browser.wait(EC.elementToBeClickable(elm), 10000);
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) {
var range = document.createRange();
range.setStart(arguments[0].firstChild, 0);
@ -84,26 +127,7 @@ async function edit(elm, elmWrapper, text = null) {
}
}, elm.getWebElement());
if (text !== null) {
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();
return elm.sendKeys(protractor.Key.BACK_SPACE);
}
shared.wysiwygTestingComments = function(parentSelector, section) {
@ -126,15 +150,15 @@ shared.wysiwygTestingComments = function(parentSelector, section) {
resetSelection();
markdownMode();
markdownMode(editorWrapper);
let markdown = await getMarkdownText(editorWrapper);
expect(markdown).to.be.equal('**test**');
htmlMode();
await htmlMode(editorWrapper);
saveEdition();
await saveEdition(editorWrapper);
let newCommentsCounter = await historyHelper.countComments();
expect(newCommentsCounter).to.be.equal(commentsCounter+1);
@ -145,50 +169,29 @@ shared.wysiwygTestingComments = function(parentSelector, section) {
await edit(editor, editorWrapper, '');
markdownMode();
markdownMode(editorWrapper);
let markdownTextarea = getMarkdownTextarea(editorWrapper);
await markdownTextarea.sendKeys('_test2_');
htmlMode();
await htmlMode(editorWrapper);
let html = await editor.getAttribute("innerHTML");
expect(html).to.be.eql('<p><em>test2</em></p>\n');
saveEdition();
await saveEdition(editorWrapper);
let newCommentsCounter = await historyHelper.countComments();
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 () => {
await edit(editor, editorWrapper, '');
editor.sendKeys('text text text');
await preventThrottle();
editor.sendKeys(protractor.Key.ESCAPE);
await utils.lightbox.confirm.ok();
@ -206,6 +209,7 @@ shared.wysiwygTestingComments = function(parentSelector, section) {
await edit(editor, editorWrapper, '');
editor.sendKeys('text text text');
await preventThrottle();
editor.sendKeys(protractor.Key.ESCAPE);
browser.sleep(400);
@ -225,21 +229,21 @@ shared.wysiwygTestingComments = function(parentSelector, section) {
it('mention user', async () => {
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");
expect(html).to.be.eql('<p><a href="/profile/user8">@user8</a>&nbsp;</p>');
markdownMode();
markdownMode(editorWrapper);
let markdown = await getMarkdownText(editorWrapper);
expect(markdown).to.be.equal('[@user8](/profile/user8)');
htmlMode();
await htmlMode(editorWrapper);
await cancelEdition(editorWrapper);
});
@ -255,13 +259,13 @@ shared.wysiwygTestingComments = function(parentSelector, section) {
expect(html).to.include('1f604.png');
markdownMode();
markdownMode(editorWrapper);
let markdown = await getMarkdownText(editorWrapper);
expect(markdown).to.be.equal(':smile:');
htmlMode();
await htmlMode(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 utils.common.takeScreenshot(section, "edit comment");
saveEdition();
await saveEdition(editWrapperLast);
//Show versions from last comment edited
historyHelper.showVersionsLastComment();
@ -318,6 +322,32 @@ shared.wysiwygTestingComments = function(parentSelector, section) {
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) {
@ -331,9 +361,14 @@ shared.wysiwygTesting = function(parentSelector) {
editor.click();
}
let isHtmlMode = await editor.isDisplayed();
if (!isHtmlMode) {
await htmlMode(editorWrapper);
}
await cleanWysiwyg(editor, editorWrapper);
markdownMode();
markdownMode(editorWrapper);
var markdownTextarea = getMarkdownTextarea(editorWrapper);
@ -341,9 +376,9 @@ shared.wysiwygTesting = function(parentSelector) {
await markdownTextarea.sendKeys('test');
htmlMode();
await htmlMode(editorWrapper);
saveEdition();
await saveEdition(editorWrapper);
await browser.wait(EC.elementToBeClickable(editor), 10000);
});
@ -364,13 +399,13 @@ shared.wysiwygTesting = function(parentSelector) {
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);
markdownMode();
markdownMode(editorWrapper);
let markdown = await getMarkdownText(editorWrapper);
@ -380,43 +415,24 @@ shared.wysiwygTesting = function(parentSelector) {
it('convert to html', async () => {
await edit(editor, editorWrapper, '');
markdownMode();
markdownMode(editorWrapper);
let markdownTextarea = getMarkdownTextarea(editorWrapper);
await markdownTextarea.sendKeys('_test2_');
htmlMode();
htmlMode(editorWrapper);
let html = await editor.getAttribute("innerHTML");
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 () => {
await edit(editor, editorWrapper, '');
editor.sendKeys('text text text');
await preventThrottle();
editor.sendKeys(protractor.Key.ESCAPE);
await utils.lightbox.confirm.ok();
@ -434,6 +450,7 @@ shared.wysiwygTesting = function(parentSelector) {
await edit(editor, editorWrapper, '');
editor.sendKeys('text text text');
await preventThrottle();
editor.sendKeys(protractor.Key.ESCAPE);
browser.sleep(400);
@ -451,21 +468,24 @@ shared.wysiwygTesting = function(parentSelector) {
it('mention user', async () => {
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");
expect(html).to.be.eql('<p><a href="/profile/user8">@user8</a>&nbsp;</p>');
markdownMode();
expect(html).to.be.eql('<p><a href="/profile/user5">@user5</a>&nbsp;</p>\n');
markdownMode(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 () => {
@ -473,13 +493,15 @@ shared.wysiwygTesting = function(parentSelector) {
editor.sendKeys(':smil');
$$('.medium-mention li').get(2).click();
await $$('.medium-mention li').get(2).click();
await closeMention();
let html = await editor.getAttribute("innerHTML");
expect(html).to.include('1f604.png');
markdownMode();
markdownMode(editorWrapper);
let markdown = await getMarkdownText(editorWrapper);
@ -497,4 +519,30 @@ shared.wysiwygTesting = function(parentSelector) {
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;
});
};

View File

@ -476,7 +476,7 @@ describe('backlog', function() {
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');
await utils.common.waitLoader();

View File

@ -65,29 +65,7 @@ describe('wiki', function() {
await utils.common.takeScreenshot("wiki", "deleting-the-created-link");
});
describe('wiki editor', sharedWysiwyg.bind(this));
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;
});
describe('wiki editor', sharedWysiwyg.bind(this, '.wiki'));
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');
});
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");
});
});

View File

@ -20,6 +20,20 @@ common.getElm = function(el) {
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) {
let classes = await element.getAttribute('class');