taiga-front/app/coffee/modules/common/wisiwyg.coffee

361 lines
14 KiB
CoffeeScript

###
# Copyright (C) 2014 Andrey Antukh <niwi@niwi.be>
# Copyright (C) 2014 Jesús Espino Garcia <jespinog@gmail.com>
# Copyright (C) 2014 David Barragán Merino <bameda@dbarragan.com>
#
# 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/common/wisiwyg.coffee
###
taiga = @.taiga
bindOnce = @.taiga.bindOnce
module = angular.module("taigaCommon")
# How to test lists (-, *, 1.)
# test it with text after & before the list
# + is the cursor position
# CASE 1
# - aa+
# --> enter
# - aa
# - +
# CASE 1
# - +
# --> enter
# +
# CASE 3
# - bb+cc
# --> enter
# - bb
# - cc
# CASE 3
# +- aa
# --> enter
# - aa
#############################################################################
## WYSIWYG markitup editor directive
#############################################################################
MarkitupDirective = ($rootscope, $rs, $selectedText, $template, $compile, $translate) ->
previewTemplate = $template.get("common/wysiwyg/wysiwyg-markitup-preview.html", true)
link = ($scope, $el, $attrs, $model) ->
element = angular.element($el)
previewDomNode = $("<div/>", {class: "preview"})
closePreviewMode = ->
element.parents(".markdown").find(".preview").remove()
element.parents(".markItUp").show()
$scope.$on "markdown-editor:submit", ->
closePreviewMode()
preview = ->
markdownDomNode = element.parents(".markdown")
markItUpDomNode = element.parents(".markItUp")
$rs.mdrender.render($scope.projectId, $model.$modelValue).then (data) ->
html = previewTemplate({data: data.data})
html = $compile(html)($scope)
markdownDomNode.append(html)
markItUpDomNode.hide()
markdown = element.closest(".markdown")
markdown.on "mouseup.preview", ".preview", (event) ->
event.preventDefault()
target = angular.element(event.target)
if !target.is('a') and $selectedText.get().length
return
markdown.off(".preview")
closePreviewMode()
setCaretPosition = (textarea, caretPosition) ->
if textarea.createTextRange
range = textarea.createTextRange()
range.move("character", caretPosition)
range.select()
else if textarea.selectionStart
textarea.focus()
textarea.setSelectionRange(caretPosition, caretPosition)
# Calculate the scroll position
totalLines = textarea.value.split("\n").length
line = textarea.value[0..(caretPosition - 1)].split("\n").length
scrollRelation = line / totalLines
$el.scrollTop((scrollRelation * $el[0].scrollHeight) - ($el.height() / 2))
addLine = (textarea, nline, replace) ->
lines = textarea.value.split("\n")
if replace
lines[nline] = replace + lines[nline]
else
lines[nline] = ""
cursorPosition = 0
for line, key in lines
cursorPosition += line.length + 1 || 1
break if key == nline
textarea.value = lines.join("\n")
#return the new position
if replace
return cursorPosition - lines[nline].length + replace.length - 1
else
return cursorPosition
prepareUrlFormatting = (markItUp) ->
regex = /(<<<|>>>)/gi
result = 0
indices = []
(indices.push(result.index)) while ( (result = regex.exec(markItUp.textarea.value)) )
markItUp.donotparse = indices
urlFormatting = (markItUp) ->
regex = /<<</gi
result = 0
startIndex = 0
loop
result = regex.exec(markItUp.textarea.value)
break if !result
if result.index not in markItUp.donotparse
startIndex = result.index
break
regex = />>>/gi
endIndex = 0
loop
result = regex.exec(markItUp.textarea.value)
break if !result
if result.index not in markItUp.donotparse
endIndex = result.index
break
value = markItUp.textarea.value
url = value.substring(startIndex, endIndex).replace('<<<', '').replace('>>>', '')
url = url.replace('(', '%28').replace(')', '%29')
url = url.replace('[', '%5B').replace(']', '%5D')
value = value.substring(0, startIndex) + url + value.substring(endIndex+3, value.length)
markItUp.textarea.value = value
markItUp.donotparse = undefined
markdownTitle = (markItUp, char) ->
heading = ""
n = $.trim(markItUp.selection or markItUp.placeHolder).length
for i in [0..n-1]
heading += char
return "\n"+heading+"\n"
renderMarkItUp = () ->
markdownSettings =
nameSpace: "markdown"
onShiftEnter: {keepDefault:false, openWith:"\n\n"}
onEnter:
keepDefault: false,
replaceWith: () -> "\n"
afterInsert: (data) ->
lines = data.textarea.value.split("\n")
cursorLine = data.textarea.value[0..(data.caretPosition - 1)].split("\n").length
newLineContent = data.textarea.value[data.caretPosition..].split("\n")[0]
lastLine = lines[cursorLine - 1]
# unordered list -
match = lastLine.match /^(\s*- ).*/
if match
emptyListItem = lastLine.match /^(\s*)\-\s$/
if emptyListItem
nline = cursorLine - 1
replace = null
else
nline = cursorLine
replace = "#{match[1]}"
markdownCaretPositon = addLine(data.textarea, nline, replace)
# unordered list *
match = lastLine.match /^(\s*\* ).*/
if match
emptyListItem = lastLine.match /^(\s*\* )$/
if emptyListItem
nline = cursorLine - 1
replace = null
else
nline = cursorLine
replace = "#{match[1]}"
markdownCaretPositon = addLine(data.textarea, nline, replace)
# ordered list
match = lastLine.match /^(\s*)(\d+)\.\s/
if match
emptyListItem = lastLine.match /^(\s*)(\d+)\.\s$/
if emptyListItem
nline = cursorLine - 1
replace = null
else
nline = cursorLine
replace = "#{match[1] + (parseInt(match[2], 10) + 1)}. "
markdownCaretPositon = addLine(data.textarea, nline, replace)
setCaretPosition(data.textarea, markdownCaretPositon) if markdownCaretPositon
markupSet: [
{
name: $translate.instant("COMMON.WYSIWYG.H1_BUTTON")
key: "1"
placeHolder: $translate.instant("COMMON.WYSIWYG.H1_SAMPLE_TEXT")
closeWith: (markItUp) -> markdownTitle(markItUp, "=")
},
{
name: $translate.instant("COMMON.WYSIWYG.H2_BUTTON")
key: "2"
placeHolder: $translate.instant("COMMON.WYSIWYG.H2_SAMPLE_TEXT")
closeWith: (markItUp) -> markdownTitle(markItUp, "-")
},
{
name: $translate.instant("COMMON.WYSIWYG.H3_BUTTON")
key: "3"
openWith: "### "
placeHolder: $translate.instant("COMMON.WYSIWYG.H3_SAMPLE_TEXT")
},
{
separator: "---------------"
},
{
name: $translate.instant("COMMON.WYSIWYG.BOLD_BUTTON")
key: "B"
openWith: "**"
closeWith: "**"
placeHolder: $translate.instant("COMMON.WYSIWYG.BOLD_BUTTON_SAMPLE_TEXT")
},
{
name: $translate.instant("COMMON.WYSIWYG.ITALIC_SAMPLE_TEXT")
key: "I"
openWith: "_"
closeWith: "_"
placeHolder: $translate.instant("COMMON.WYSIWYG.ITALIC_SAMPLE_TEXT")
},
{
name: $translate.instant("COMMON.WYSIWYG.STRIKE_BUTTON")
key: "S"
openWith: "~~"
closeWith: "~~"
placeHolder: $translate.instant("COMMON.WYSIWYG.STRIKE_SAMPLE_TEXT")
},
{
separator: "---------------"
},
{
name: $translate.instant("COMMON.WYSIWYG.BULLETED_LIST_BUTTON")
openWith: "- "
placeHolder: $translate.instant("COMMON.WYSIWYG.BULLETED_LIST_SAMPLE_TEXT")
},
{
name: $translate.instant("COMMON.WYSIWYG.NUMERIC_LIST_BUTTON")
openWith: (markItUp) -> markItUp.line+". "
placeHolder: $translate.instant("COMMON.WYSIWYG.NUMERIC_LIST_SAMPLE_TEXT")
},
{
separator: "---------------"
},
{
name: $translate.instant("COMMON.WYSIWYG.PICTURE_BUTTON")
key: "P"
openWith: "!["
closeWith: '](<<<[![Url:!:http://]!]>>> "[![Title]!]")'
placeHolder: $translate.instant("COMMON.WYSIWYG.PICTURE_SAMPLE_TEXT")
beforeInsert:(markItUp) -> prepareUrlFormatting(markItUp)
afterInsert:(markItUp) -> urlFormatting(markItUp)
},
{
name: $translate.instant("COMMON.WYSIWYG.LINK_BUTTON")
key: "L"
openWith: "["
closeWith: '](<<<[![Url:!:http://]!]>>> "[![Title]!]")'
placeHolder: $translate.instant("COMMON.WYSIWYG.LINK_SAMPLE_TEXT")
beforeInsert:(markItUp) -> prepareUrlFormatting(markItUp)
afterInsert:(markItUp) -> urlFormatting(markItUp)
},
{
separator: "---------------"
},
{
name: $translate.instant("COMMON.WYSIWYG.QUOTE_BLOCK_BUTTON")
openWith: "> "
placeHolder: $translate.instant("COMMON.WYSIWYG.QUOTE_BLOCK_SAMPLE_TEXT")
},
{
name: $translate.instant("COMMON.WYSIWYG.CODE_BLOCK_BUTTON")
openWith: "```\n"
placeHolder: $translate.instant("COMMON.WYSIWYG.CODE_BLOCK_SAMPLE_TEXT")
closeWith: "\n```"
},
{
separator: "---------------"
},
{
name: $translate.instant("COMMON.WYSIWYG.PREVIEW_BUTTON")
call: preview
className: "preview-icon"
},
]
afterInsert: (event) ->
target = angular.element(event.textarea)
$model.$setViewValue(target.val())
element
.markItUpRemove()
.markItUp(markdownSettings)
renderMarkItUp()
unbind = $rootscope.$on "$translateChangeEnd", renderMarkItUp
element.on "keypress", (event) ->
$scope.$apply()
$scope.$on "$destroy", ->
$el.off()
unbind()
return {link:link, require:"ngModel"}
module.directive("tgMarkitup", ["$rootScope", "$tgResources", "$selectedText", "$tgTemplate", "$compile",
"$translate", MarkitupDirective])