The communications platform that puts data protection first.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
Rocket.Chat/packages/rocketchat-ui-message/client/popup/messagePopup.coffee

282 lines
7.6 KiB

# This is not supposed to be a complete list
# it is just to improve readability in this file
keys = {
TAB: 9
ENTER: 13
ESC: 27
ARROW_LEFT: 37
ARROW_UP: 38
ARROW_RIGHT: 39
ARROW_DOWN: 40
}
getCursorPosition = (input) ->
if not input? then return
if input.selectionStart?
return input.selectionStart
else if document.selection?
input.focus()
sel = document.selection.createRange()
selLen = document.selection.createRange().text.length
sel.moveStart('character', - input.value.length)
return sel.text.length - selLen
setCursorPosition = (input, caretPos) ->
if not input? then return
if input.selectionStart?
input.focus()
return input.setSelectionRange(caretPos, caretPos)
else if document.selection?
range = input.createTextRange()
range.move('character', caretPos)
range.select()
val = (v, d) ->
return if v? then v else d
Template.messagePopup.onCreated ->
template = this
template.textFilter = new ReactiveVar ''
template.textFilterDelay = val(template.data.textFilterDelay, 0)
template.open = val(template.data.open, new ReactiveVar(false))
template.hasData = new ReactiveVar false
template.value = new ReactiveVar
template.trigger = val(template.data.trigger, '')
template.triggerAnywhere = val(template.data.triggerAnywhere, true)
template.closeOnEsc = val(template.data.closeOnEsc, true)
template.blurOnSelectItem = val(template.data.blurOnSelectItem, false)
template.prefix = val(template.data.prefix, template.trigger)
template.suffix = val(template.data.suffix, '')
if template.triggerAnywhere is true
template.matchSelectorRegex = val(template.data.matchSelectorRegex, new RegExp "(?:^| )#{template.trigger}[^\\s]*$")
else
template.matchSelectorRegex = val(template.data.matchSelectorRegex, new RegExp "(?:^)#{template.trigger}[^\\s]*$")
template.selectorRegex = val(template.data.selectorRegex, new RegExp "#{template.trigger}([^\\s]*)$")
template.replaceRegex = val(template.data.replaceRegex, new RegExp "#{template.trigger}[^\\s]*$")
template.getValue = val template.data.getValue, (_id) -> return _id
template.up = =>
current = template.find('.popup-item.selected')
previous = $(current).prev('.popup-item')[0] or template.find('.popup-item:last-child')
if previous?
current.className = current.className.replace /\sselected/, ''
previous.className += ' selected'
template.value.set previous.getAttribute('data-id')
template.down = =>
current = template.find('.popup-item.selected')
next = $(current).next('.popup-item')[0] or template.find('.popup-item')
if next?.classList.contains('popup-item')
current.className = current.className.replace /\sselected/, ''
next.className += ' selected'
template.value.set next.getAttribute('data-id')
template.verifySelection = =>
current = template.find('.popup-item.selected')
if not current?
first = template.find('.popup-item')
if first?
first.className += ' selected'
template.value.set first.getAttribute('data-id')
else
template.value.set undefined
template.onInputKeydown = (event) =>
if template.open.curValue isnt true or template.hasData.curValue isnt true
return
if event.which in [keys.ENTER, keys.TAB]
if template.blurOnSelectItem is true
template.input.blur()
else
template.open.set false
template.enterValue()
if template.data.cleanOnEnter
template.input.value = ''
event.preventDefault()
event.stopPropagation()
return
if event.which is keys.ARROW_UP
template.up()
event.preventDefault()
event.stopPropagation()
return
if event.which is keys.ARROW_DOWN
template.down()
event.preventDefault()
event.stopPropagation()
return
template.setTextFilter = _.debounce (value) ->
template.textFilter.set(value)
, template.textFilterDelay
template.onInputKeyup = (event) =>
if template.closeOnEsc is true and template.open.curValue is true and event.which is keys.ESC
template.open.set false
event.preventDefault()
event.stopPropagation()
return
value = template.input.value
value = value.substr 0, getCursorPosition(template.input)
if template.matchSelectorRegex.test value
template.setTextFilter value.match(template.selectorRegex)[1]
template.open.set true
else
template.open.set false
if template.open.curValue isnt true
return
if event.which not in [keys.ARROW_UP, keys.ARROW_DOWN]
Meteor.defer =>
template.verifySelection()
template.onFocus = (event) =>
template.clickingItem = false;
if template.open.curValue is true
return
value = template.input.value
value = value.substr 0, getCursorPosition(template.input)
if template.matchSelectorRegex.test value
template.setTextFilter value.match(template.selectorRegex)[1]
template.open.set true
Meteor.defer =>
template.verifySelection()
else
template.open.set false
template.onBlur = (event) =>
if template.open.curValue is false
return
if template.clickingItem is true
return
template.open.set false
template.enterValue = ->
if not template.value.curValue? then return
value = template.input.value
caret = getCursorPosition(template.input)
firstPartValue = value.substr 0, caret
lastPartValue = value.substr caret
getValue = this.getValue(template.value.curValue, template.data.collection, template.records.get(), firstPartValue)
if not getValue
return
firstPartValue = firstPartValue.replace(template.selectorRegex, template.prefix + getValue + template.suffix)
template.input.value = firstPartValue + lastPartValue
setCursorPosition template.input, firstPartValue.length
template.records = new ReactiveVar []
Tracker.autorun ->
if template.data.collection.findOne?
template.data.collection.find().count()
filter = template.textFilter.get()
if filter?
filterCallback = (result) =>
template.hasData.set result?.length > 0
template.records.set result
Meteor.defer =>
template.verifySelection()
result = template.data.getFilter(template.data.collection, filter, filterCallback)
if result?
filterCallback result
Template.messagePopup.onRendered ->
if this.data.getInput?
this.input = this.data.getInput?()
else if this.data.input
this.input = this.parentTemplate().find(this.data.input)
if not this.input?
console.error 'Input not found for popup'
$(this.input).on 'keyup', this.onInputKeyup.bind this
$(this.input).on 'keydown', this.onInputKeydown.bind this
$(this.input).on 'focus', this.onFocus.bind this
$(this.input).on 'blur', this.onBlur.bind this
Template.messagePopup.onDestroyed ->
$(this.input).off 'keyup', this.onInputKeyup
$(this.input).off 'keydown', this.onInputKeydown
$(this.input).off 'focus', this.onFocus
$(this.input).off 'blur', this.onBlur
Template.messagePopup.events
'mouseenter .popup-item': (e) ->
if e.currentTarget.className.indexOf('selected') > -1
return
template = Template.instance()
current = template.find('.popup-item.selected')
if current?
current.className = current.className.replace /\sselected/, ''
e.currentTarget.className += ' selected'
template.value.set this._id
'mousedown .popup-item, touchstart .popup-item': (e) ->
template = Template.instance()
template.clickingItem = true;
'mouseup .popup-item, touchend .popup-item': (e) ->
template = Template.instance()
template.clickingItem = false;
template.value.set this._id
template.enterValue()
template.open.set false
toolbarSearch.clear();
Template.messagePopup.helpers
isOpen: ->
Template.instance().open.get() and ((Template.instance().hasData.get() or Template.instance().data.emptyTemplate?) or not Template.instance().parentTemplate(1).subscriptionsReady())
data: ->
template = Template.instance()
return template.records.get()