Init version of native contenteditable of browsers for testing.

This commit is contained in:
Martin Edenhofer 2015-01-06 15:00:38 +01:00
parent 7e1e6c6b6e
commit 642b110329
9 changed files with 4083 additions and 1991 deletions

View file

@ -24,8 +24,8 @@ class Content extends App.ControllerContent
for avatar in @$('.user.avatar') for avatar in @$('.user.avatar')
avatar = $(avatar) avatar = $(avatar)
size = switch size = switch
when avatar.hasClass('size-80') then 80 when avatar.hasClass('size-80') then 80
when avatar.hasClass('size-50') then 50 when avatar.hasClass('size-50') then 50
else 40 else 40
@createUniqueAvatar avatar, size, avatar.data('firstname'), avatar.data('lastname'), avatar.data('userid') @createUniqueAvatar avatar, size, avatar.data('firstname'), avatar.data('lastname'), avatar.data('userid')
@ -641,8 +641,76 @@ class ReferenceSetupWizard extends App.ControllerWizard
@agentEmail.add(@agentFirstName).add(@agentLastName).val('') @agentEmail.add(@agentFirstName).add(@agentLastName).val('')
@agentFirstName.focus() @agentFirstName.focus()
App.Config.set( 'layout_ref/richtext', ReferenceSetupWizard, 'Routes' )
class RichText extends App.ControllerContent
constructor: ->
super
@render()
App.Config.set( 'layout_ref/setup', ReferenceSetupWizard, 'Routes' ) @$('.js-text-oneline').ce({
mode: 'textonly'
multiline: false
maxlength: 250
})
App.Config.set( 'LayoutRef', { prio: 1700, parent: '#current_user', name: 'Layout Reference', target: '#layout_ref', role: [ 'Admin' ] }, 'NavBarRight' ) @$('.js-text-multiline').ce({
mode: 'textonly'
multiline: true
maxlength: 250
})
@$('.js-text-richtext').ce({
mode: 'richtext'
multiline: true
maxlength: 250
})
return
@$('.js-textarea').on('keyup', (e) =>
console.log('KU')
textarea = @$('.js-textarea')
App.Utils.htmlClanup(textarea)
)
@$('.js-textarea').on('paste', (e) =>
console.log('paste')
#console.log('PPP', e, e.originalEvent.clipboardData)
execute = =>
# add marker for cursor
getFirstRange = ->
sel = rangy.getSelection();
if sel.rangeCount
sel.getRangeAt(0)
else
null
range = getFirstRange()
if range
el = document.createElement('span')
$(el).attr('data-cursor', 1)
range.insertNode(el)
rangy.getSelection().setSingleRange(range)
# cleanup
textarea = @$('.js-textarea')
App.Utils.htmlClanup(textarea)
# remove marker for cursor
textarea.find('[data-cursor=1]').focus()
textarea.find('[data-cursor=1]').remove()
@delay( execute, 1)
return
)
#editable.style.borderColor = '#54c8eb';
#aloha(editable);
return
render: ->
@html App.view('layout_ref/richtext')()
App.Config.set( 'layout_ref/richtext', RichText, 'Routes' )
App.Config.set( 'LayoutRef', { prio: 1700, parent: '#current_user', name: 'Layout Reference', target: '#layout_ref', role: [ 'Admin' ] }, 'NavBarRight' )

View file

@ -1488,16 +1488,17 @@ class ArticleView extends App.Controller
# add quoted text if needed # add quoted text if needed
selectedText = App.ClipBoard.getSelected() selectedText = App.ClipBoard.getSelected()
if selectedText if selectedText
body = @ui.el.find('[data-name="body"]').html() || '' body = @ui.el.find('[data-name="body"]').html() || ''
# quote text # quote text
selectedText = App.Utils.textCleanup( selectedText ) selectedText = App.Utils.textCleanup( selectedText )
selectedText = App.Utils.quote( selectedText ) #selectedText = App.Utils.quote( selectedText )
# convert to html # convert to html
selectedText = App.Utils.text2html( selectedText ) selectedText = App.Utils.text2html( selectedText )
if selectedText
selectedText = "<div><br><br/></div><div><blockquote type=\"cite\">#{selectedText}</blockquote></div><div><br></div>"
articleNew.body = selectedText + body articleNew.body = selectedText + body

View file

@ -77,3 +77,52 @@ class App.Utils
'> ' + match '> ' + match
else else
'>' '>'
@htmlRemoveTags: (textarea) ->
# remove tags, keep content
textarea.find('a, div, span, li, ul, ol, a, hr, blockquote, br').replaceWith( ->
$(@).contents()
)
@htmlRemoveRichtext: (textarea) ->
# remove style and class
textarea.find('div, span, li, ul, ol, a').removeAttr( 'style' ).removeAttr( 'class' ).removeAttr( 'title' )
# remove tags, keep content
textarea.find('a, li, ul, ol, a, hr').replaceWith( ->
$(@).contents()
)
@htmlClanup: (textarea) ->
# remove style and class
textarea.find('div, span, li, ul, ol, a').removeAttr( 'style' ).removeAttr( 'class' ).removeAttr( 'title' )
# remove tags & content
textarea.find('hr').remove()
# remove tags, keep content
textarea.find('a').replaceWith( ->
$(@).contents()
)
# replace tags with generic div
# New type of the tag
replacementTag = 'div';
# Replace all a tags with the type of replacementTag
textarea.find('h1, h2, h3, h4, h5, h6').each( ->
outer = this.outerHTML;
# Replace opening tag
regex = new RegExp('<' + this.tagName, 'i');
newTag = outer.replace(regex, '<' + replacementTag);
# Replace closing tag
regex = new RegExp('</' + this.tagName, 'i');
newTag = newTag.replace(regex, '</' + replacementTag);
$(@).replaceWith(newTag);
)

View file

@ -12,6 +12,32 @@
defaults = { defaults = {
mode: 'richtext', mode: 'richtext',
multiline: true, multiline: true,
allowKey: {
8: true, // backspace
9: true, // tab
16: true, // shift
17: true, // ctrl
18: true, // alt
20: true, // cabslock
37: true, // up
38: true, // right
39: true, // down
40: true, // left
91: true, // cmd left
92: true, // cmd right
},
extraAllowKey: {
65: true, // a + ctrl - select all
67: true, // c + ctrl - copy
86: true, // v + ctrl - paste
88: true, // x + ctrl - cut
90: true, // z + ctrl - undo
},
richTextFormatKey: {
66: true, // b
73: true, // i
85: true, // u
},
}; };
function Plugin( element, options ) { function Plugin( element, options ) {
@ -28,26 +54,157 @@
this.options.placeholder = this.$element.data('placeholder') this.options.placeholder = this.$element.data('placeholder')
} }
// link input this.preventInput = false
if ( !this.options.multiline ) {
editorMode = Medium.inlineMode this.init();
// bind
// bind paste
}
Plugin.prototype.init = function () {
var _this = this
// handle enter
this.$element.on('keydown', function (e) {
console.log('keydown', e.keyCode)
if ( _this.preventInput ) {
console.log('preventInput', _this.preventInput)
return
}
// strap the return key being pressed
if (e.keyCode === 13) {
// disbale multi line
if ( !_this.options.multiline ) {
e.preventDefault()
return
}
// limit check
if ( !_this.maxLengthOk( true ) ) {
e.preventDefault()
return
}
}
})
this.$element.on('keyup', function (e) {
console.log('KU')
if ( _this.options.mode === 'textonly' ) {
console.log('REMOVE TAGS')
if ( !_this.options.multiline ) {
App.Utils.htmlRemoveTags(_this.$element)
}
else {
App.Utils.htmlRemoveRichtext(_this.$element)
}
}
else {
App.Utils.htmlClanup(_this.$element)
}
})
// just paste text
this.$element.on('paste', function (e) {
console.log('paste')
if ( _this.options.mode === 'textonly' ) {
console.log('REMOVE TAGS')
if ( !_this.options.multiline ) {
App.Utils.htmlRemoveTags(_this.$element)
}
else {
App.Utils.htmlRemoveRichtext(_this.$element)
}
}
else {
App.Utils.htmlClanup(_this.$element)
}
return true
if ( this.options.mode === 'textonly' ) {
e.preventDefault()
var text
if (window.clipboardData) { // IE
text = window.clipboardData.getData('Text')
}
else {
text = (e.originalEvent || e).clipboardData.getData('text/plain')
}
var overlimit = false
if (text) {
// replace new lines
if ( !_this.options.multiline ) {
text = text.replace(/\n/g, '')
text = text.replace(/\r/g, '')
text = text.replace(/\t/g, '')
}
// limit length, limit paste string
if ( _this.options.maxlength ) {
var pasteLength = text.length
var currentLength = _this.$element.text().length
var overSize = ( currentLength + pasteLength ) - _this.options.maxlength
if ( overSize > 0 ) {
text = text.substr( 0, pasteLength - overSize )
overlimit = true
}
}
// insert new text
if (document.selection) { // IE
var range = document.selection.createRange()
range.pasteHTML(text)
}
else {
document.execCommand('inserttext', false, text)
}
_this.maxLengthOk( overlimit )
}
}
})
// disable rich text b/u/i
if ( this.options.mode === 'textonly' ) {
this.$element.on('keydown', function (e) {
if ( _this.richTextKey(e) ) {
e.preventDefault()
}
})
} }
// link textarea
else if ( this.options.multiline && this.options.mode != 'richtext' ) {
editorMode = Medium.partialMode
}
// rich text }
else {
editorMode = Medium.richMode
}
// max length validation // check if rich text key is pressed
var validation = function(element) { Plugin.prototype.richTextKey = function(e) {
// e.altKey
// e.ctrlKey
// e.metaKey
// on mac block e.metaKey + i/b/u
if ( !e.altKey && e.metaKey && this.options.richTextFormatKey[ e.keyCode ] ) {
return true
}
// on win block e.ctrlKey + i/b/u
if ( !e.altKey && e.ctrlKey && this.options.richTextFormatKey[ e.keyCode ] ) {
return true
}
return false
}
// max length check
Plugin.prototype.maxLengthOk = function(typeAhead) {
var length = this.$element.text().length
if (typeAhead) {
length = length + 1
}
if ( length > this.options.maxlength ) {
// try to set error on framework form // try to set error on framework form
var parent = $(element).parent().parent() var parent = this.$element.parent().parent()
if ( parent.hasClass('controls') ) { if ( parent.hasClass('controls') ) {
parent.addClass('has-error') parent.addClass('has-error')
setTimeout($.proxy(function(){ setTimeout($.proxy(function(){
@ -59,31 +216,15 @@
// set validation on element // set validation on element
else { else {
$(element).addClass('invalid') this.$element.addClass('invalid')
setTimeout($.proxy(function(){ setTimeout($.proxy(function(){
$(element).removeClass('invalid') this.$element.removeClass('invalid')
}, this), 1000) }, this), 1000)
return false return false
} }
} }
new Medium({ return true
element: element,
modifier: 'auto',
placeholder: this.options.placeholder || '',
autofocus: false,
autoHR: false,
mode: editorMode,
maxLength: this.options.maxlength || -1,
maxLengthReached: validation,
tags: {
'break': 'br',
'horizontalRule': 'hr',
'paragraph': 'div',
'outerLevel': ['pre', 'blockquote', 'figure'],
'innerLevel': ['a', 'b', 'u', 'i', 'img', 'strong']
},
});
} }
// get value // get value

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -18,6 +18,7 @@
<li><a href="#layout_ref/user_profile">User Profile</a></li> <li><a href="#layout_ref/user_profile">User Profile</a></li>
<li><a href="#layout_ref/organization_profile">Organization Profile</a></li> <li><a href="#layout_ref/organization_profile">Organization Profile</a></li>
<li><a href="#layout_ref/setup">Setup Wizard</a></li> <li><a href="#layout_ref/setup">Setup Wizard</a></li>
<li><a href="#layout_ref/richtext">Richtext</a></li>
</ul> </ul>
</div> </div>

View file

@ -0,0 +1,26 @@
<div class="main flex">
<h1>Richtext<h1>
<h2>Singleline / Textonly</h2>
<div class="text-bubble">
<div class="bubble-arrow"></div>
<div class="js-text-oneline" contenteditable="true" data-name="body"></div>
</div>
<h2>Multiline / Textonly</h2>
<div class="text-bubble">
<div class="bubble-arrow"></div>
<div class="js-text-multiline" contenteditable="true" data-name="body"></div>
</div>
<h2>Multiline / Richtext</h2>
<div class="text-bubble">
<div class="bubble-arrow"></div>
<div class="js-text-richtext" contenteditable="true" data-name="body"></div>
</div>
</div>

View file

@ -36,6 +36,9 @@ small {
font-size: 12px; font-size: 12px;
} }
blockquote {
font-size: inherit;
}
.u-unclickable { .u-unclickable {
pointer-events: none; pointer-events: none;