9b650c5c8f
make the behavior same as before for both RTL and LTR languages make solution use dynamic scrollbar size as suggested in CR
1709 lines
50 KiB
JavaScript
1709 lines
50 KiB
JavaScript
/*!
|
||
* Webogram v0.5.5 - messaging web application for MTProto
|
||
* https://github.com/zhukov/webogram
|
||
* Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>
|
||
* https://github.com/zhukov/webogram/blob/master/LICENSE
|
||
*/
|
||
|
||
'use strict'
|
||
|
||
/* EmojiHelper */
|
||
|
||
;(function (global, emojis, categories, spritesheets) {
|
||
var emojis = {}
|
||
var shortcuts = {}
|
||
var spritesheetPositions = {}
|
||
var index = false
|
||
|
||
var popular = 'joy,kissing_heart,heart,heart_eyes,blush,grin,+1,relaxed,pensive,smile,sob,kiss,unamused,flushed,stuck_out_tongue_winking_eye,see_no_evil,wink,smiley,cry,stuck_out_tongue_closed_eyes,scream,rage,smirk,disappointed,sweat_smile,kissing_closed_eyes,speak_no_evil,relieved,grinning,yum,laughing,ok_hand,neutral_face,confused'.split(',')
|
||
|
||
var i
|
||
var j
|
||
var code
|
||
var shortcut
|
||
var emoji
|
||
var row
|
||
var column
|
||
var totalColumns
|
||
var len1
|
||
var len2
|
||
|
||
for (i = 0, len1 = categories.length; i < len1; i++) {
|
||
totalColumns = spritesheets[i][1]
|
||
for (j = 0, len2 = categories[i].length; j < len2; j++) {
|
||
code = categories[i][j]
|
||
emoji = Config.Emoji[code]
|
||
shortcut = emoji[1][0]
|
||
emojis[code] = [emoji[0], shortcut]
|
||
shortcuts[shortcut] = code
|
||
spritesheetPositions[code] = [i, j, Math.floor(j / totalColumns), j % totalColumns]
|
||
}
|
||
}
|
||
|
||
function getPopularEmoji (callback) {
|
||
ConfigStorage.get('emojis_popular', function (popEmojis) {
|
||
var result = []
|
||
if (popEmojis && popEmojis.length) {
|
||
for (var i = 0, len = popEmojis.length; i < len; i++) {
|
||
result.push({code: popEmojis[i][0], rate: popEmojis[i][1]})
|
||
}
|
||
callback(result)
|
||
return
|
||
}
|
||
ConfigStorage.get('emojis_recent', function (recentEmojis) {
|
||
recentEmojis = recentEmojis || popular || []
|
||
var shortcut
|
||
var code
|
||
for (var i = 0, len = recentEmojis.length; i < len; i++) {
|
||
shortcut = recentEmojis[i]
|
||
if (Array.isArray(shortcut)) {
|
||
shortcut = shortcut[0]
|
||
}
|
||
if (shortcut && typeof shortcut === 'string') {
|
||
if (shortcut.charAt(0) == ':') {
|
||
shortcut = shortcut.substr(1, shortcut.length - 2)
|
||
}
|
||
if (code = shortcuts[shortcut]) {
|
||
result.push({code: code, rate: 1})
|
||
}
|
||
}
|
||
}
|
||
callback(result)
|
||
})
|
||
})
|
||
}
|
||
|
||
function pushPopularEmoji (code) {
|
||
getPopularEmoji(function (popularEmoji) {
|
||
var exists = false
|
||
var count = popularEmoji.length
|
||
var result = []
|
||
for (var i = 0; i < count; i++) {
|
||
if (popularEmoji[i].code == code) {
|
||
exists = true
|
||
popularEmoji[i].rate++
|
||
}
|
||
result.push([popularEmoji[i].code, popularEmoji[i].rate])
|
||
}
|
||
if (exists) {
|
||
result.sort(function (a, b) {
|
||
return b[1] - a[1]
|
||
})
|
||
} else {
|
||
if (result.length > 41) {
|
||
result = result.slice(0, 41)
|
||
}
|
||
result.push([code, 1])
|
||
}
|
||
ConfigStorage.set({emojis_popular: result})
|
||
})
|
||
}
|
||
|
||
function indexEmojis () {
|
||
if (index === false) {
|
||
index = SearchIndexManager.createIndex()
|
||
var shortcut
|
||
for (shortcut in shortcuts) {
|
||
if (shortcuts.hasOwnProperty(shortcut)) {
|
||
SearchIndexManager.indexObject(shortcuts[shortcut], shortcut, index)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
function searchEmojis (q) {
|
||
indexEmojis()
|
||
var foundObject = SearchIndexManager.search(q, index)
|
||
var foundCodes = []
|
||
var code
|
||
for (code in foundObject) {
|
||
if (foundObject.hasOwnProperty(code)) {
|
||
foundCodes.push(code)
|
||
}
|
||
}
|
||
return foundCodes
|
||
}
|
||
|
||
global.EmojiHelper = {
|
||
emojis: emojis,
|
||
shortcuts: shortcuts,
|
||
spritesheetPositions: spritesheetPositions,
|
||
getPopularEmoji: getPopularEmoji,
|
||
pushPopularEmoji: pushPopularEmoji,
|
||
indexEmojis: indexEmojis,
|
||
searchEmojis: searchEmojis
|
||
}
|
||
})(window, Config.Emoji, Config.EmojiCategories, Config.EmojiCategorySpritesheetDimens)
|
||
|
||
function EmojiTooltip (btnEl, options) {
|
||
options = options || {}
|
||
var self = this
|
||
|
||
this.btnEl = $(btnEl)
|
||
this.onEmojiSelected = options.onEmojiSelected
|
||
this.onStickerSelected = options.onStickerSelected
|
||
this.getStickers = options.getStickers
|
||
this.getStickerImage = options.getStickerImage
|
||
this.onStickersetSelected = options.onStickersetSelected
|
||
this.langpack = options.langpack || {}
|
||
|
||
if (!Config.Navigator.touch) {
|
||
$(this.btnEl).on('mouseenter mouseleave', function (e) {
|
||
self.isOverBtn = e.type == 'mouseenter'
|
||
self.createTooltip()
|
||
|
||
if (self.isOverBtn) {
|
||
self.onMouseEnter(true)
|
||
} else {
|
||
self.onMouseLeave(true)
|
||
}
|
||
})
|
||
}
|
||
$(this.btnEl).on('mousedown', function (e) {
|
||
if (!self.shown) {
|
||
clearTimeout(self.showTimeout)
|
||
delete self.showTimeout
|
||
self.createTooltip()
|
||
self.show()
|
||
} else {
|
||
clearTimeout(self.hideTimeout)
|
||
delete self.hideTimeout
|
||
self.hide()
|
||
}
|
||
return cancelEvent(e)
|
||
})
|
||
$(document).on('mousedown', function (e) {
|
||
if (self.shown) {
|
||
self.hide()
|
||
}
|
||
})
|
||
}
|
||
|
||
EmojiTooltip.prototype.onMouseEnter = function (triggerShow) {
|
||
if (this.hideTimeout) {
|
||
clearTimeout(this.hideTimeout)
|
||
delete this.hideTimeout
|
||
}
|
||
else if (triggerShow && !this.showTimeout) {
|
||
this.showTimeout = setTimeout(this.show.bind(this), 100)
|
||
}
|
||
}
|
||
|
||
EmojiTooltip.prototype.onMouseLeave = function (triggerUnshow) {
|
||
if (!this.hideTimeout) {
|
||
var self = this
|
||
this.hideTimeout = setTimeout(function () {
|
||
self.hide()
|
||
}, 600)
|
||
}
|
||
else if (triggerUnshow && this.showTimeout) {
|
||
clearTimeout(this.showTimeout)
|
||
delete this.showTimeout
|
||
}
|
||
}
|
||
|
||
EmojiTooltip.prototype.createTooltip = function () {
|
||
if (this.tooltipEl) {
|
||
return false
|
||
}
|
||
|
||
var html =
|
||
'<div class="composer_emoji_tooltip noselect">\
|
||
<div class="composer_emoji_tooltip_tabs">\
|
||
<div class="composer_emoji_tooltip_tab composer_emoji_tooltip_tab_emoji">' + this.langpack.im_emoji_tab + '</div>\
|
||
<div class="composer_emoji_tooltip_tab composer_emoji_tooltip_tab_stickers">' + this.langpack.im_stickers_tab + '</div>\
|
||
<div class="composer_emoji_tooltip_tab_shadow"></div>\
|
||
</div>\
|
||
<div class="composer_emoji_tooltip_tabs_wrap">\
|
||
<div class="composer_emoji_tooltip_tabs_contents clearfix">\
|
||
<div class="composer_emoji_tooltip_tab_emoji_content">\
|
||
<div class="composer_emoji_tooltip_content_wrap">\
|
||
<div class="composer_emoji_tooltip_content composer_emoji_tooltip_content_emoji clearfix"></div>\
|
||
</div>\
|
||
<div class="composer_emoji_tooltip_categories">\
|
||
<a class="composer_emoji_tooltip_category active" data-category="0"><i class="composer_emoji_tooltip_category_recent"></i></a>\
|
||
<a class="composer_emoji_tooltip_category" data-category="1"><i class="composer_emoji_tooltip_category_smile"></i></a>\
|
||
<a class="composer_emoji_tooltip_category" data-category="2"><i class="composer_emoji_tooltip_category_flower"></i></a>\
|
||
<a class="composer_emoji_tooltip_category" data-category="3"><i class="composer_emoji_tooltip_category_bell"></i></a>\
|
||
<a class="composer_emoji_tooltip_category" data-category="4"><i class="composer_emoji_tooltip_category_car"></i></a>\
|
||
<a class="composer_emoji_tooltip_category" data-category="5"><i class="composer_emoji_tooltip_category_grid"></i></a>\
|
||
</div>\
|
||
</div>\
|
||
<div class="composer_emoji_tooltip_tab_stickers_content">\
|
||
<div class="composer_emoji_tooltip_content_wrap">\
|
||
<div class="composer_emoji_tooltip_content composer_emoji_tooltip_content_stickers clearfix"></div>\
|
||
</div>\
|
||
<div class="composer_emoji_tooltip_categories"></div>\
|
||
</div>\
|
||
</div>\
|
||
</div>\
|
||
<div class="composer_emoji_tooltip_tail"><i class="icon icon-tooltip-tail"></i></div>\
|
||
</div>'
|
||
|
||
html = html.replace(/>\s+</g, '><')
|
||
|
||
var self = this
|
||
this.tooltipEl = $(html).appendTo(document.body)
|
||
|
||
this.tabsEl = $('.composer_emoji_tooltip_tabs', this.tooltipEl)
|
||
this.categoriesEl = $('.composer_emoji_tooltip_categories', this.tooltipEl)
|
||
this.stickersCategoriesEl = $('.composer_emoji_tooltip_tab_stickers_content .composer_emoji_tooltip_categories', this.tooltipEl)
|
||
|
||
this.contentEl = $('.composer_emoji_tooltip_content', this.tooltipEl)
|
||
this.emojiContentEl = $('.composer_emoji_tooltip_content_emoji', this.tooltipEl)
|
||
this.stickersContentEl = $('.composer_emoji_tooltip_content_stickers', this.tooltipEl)
|
||
|
||
// Tabs
|
||
angular.forEach(['emoji', 'stickers'], function (tabName, tabIndex) {
|
||
var tab = $('.composer_emoji_tooltip_tab_' + tabName, self.tabsEl)
|
||
.on('mousedown', function (e) {
|
||
self.selectTab(tabIndex)
|
||
return cancelEvent(e)
|
||
})
|
||
|
||
if (!Config.Navigator.touch) {
|
||
tab.on('mouseenter mouseleave', function (e) {
|
||
clearTimeout(self.selectTabTimeout)
|
||
if (e.type == 'mouseenter') {
|
||
self.selectTabTimeout = setTimeout(function () {
|
||
self.selectTab(tabIndex)
|
||
}, 300)
|
||
}
|
||
})
|
||
}
|
||
})
|
||
|
||
// Categories
|
||
var handleEvents = 'mousedown'
|
||
if (!Config.Navigator.touch) {
|
||
handleEvents += ' mouseover mouseout'
|
||
}
|
||
this.categoriesEl.on(handleEvents, function (e) {
|
||
e = e.originalEvent || e
|
||
var target = e.target
|
||
if (target.tagName != 'A') {
|
||
target = target.parentNode
|
||
}
|
||
if (target.tagName != 'A') {
|
||
return
|
||
}
|
||
var catIndex = parseInt(target.getAttribute('data-category'))
|
||
if (e.type == 'mousedown') {
|
||
self.selectCategory(catIndex)
|
||
return cancelEvent(e)
|
||
}
|
||
if (self.tab) {
|
||
return
|
||
}
|
||
var isOver = e.type == 'mouseover'
|
||
if (isOver && self.selectCategoryIndex == catIndex) {
|
||
return
|
||
}
|
||
clearTimeout(self.selectCategoryTimeout)
|
||
delete self.selectCategoryTimeout
|
||
if (isOver) {
|
||
self.selectCategoryIndex = catIndex
|
||
self.selectCategoryTimeout = setTimeout(function () {
|
||
delete self.selectCategoryIndex
|
||
delete self.selectCategoryTimeout
|
||
self.selectCategory(catIndex)
|
||
}, 300)
|
||
} else {
|
||
delete self.selectCategoryIndex
|
||
}
|
||
})
|
||
|
||
this.emojiScroller = new Scroller(this.emojiContentEl, {classPrefix: 'composer_emoji_tooltip'})
|
||
this.stickersScroller = new Scroller(this.stickersContentEl, {classPrefix: 'composer_emoji_tooltip'})
|
||
this.stickersScroller.onScroll(function (el, st) {
|
||
self.onStickersScroll(el, st)
|
||
})
|
||
|
||
this.contentEl.on('mousedown', function (e) {
|
||
e = e.originalEvent || e
|
||
var target = $(e.target), code, sticker, stickerset
|
||
if (target[0].tagName != 'A') {
|
||
target = $(target[0].parentNode)
|
||
}
|
||
if (code = target.attr('data-code')) {
|
||
if (self.onEmojiSelected) {
|
||
self.onEmojiSelected(code)
|
||
}
|
||
EmojiHelper.pushPopularEmoji(code)
|
||
}
|
||
if (sticker = target.attr('data-sticker')) {
|
||
if (self.onStickerSelected) {
|
||
self.onStickerSelected(sticker)
|
||
}
|
||
if (Config.Mobile) {
|
||
self.hide()
|
||
}
|
||
}
|
||
if (stickerset = target.attr('data-stickerset')) {
|
||
if (self.onStickersetSelected) {
|
||
self.onStickersetSelected(stickerset)
|
||
}
|
||
self.hide()
|
||
}
|
||
return cancelEvent(e)
|
||
})
|
||
|
||
if (!Config.Navigator.touch) {
|
||
this.tooltipEl.on('mouseenter mouseleave', function (e) {
|
||
if (e.type == 'mouseenter') {
|
||
self.onMouseEnter()
|
||
} else {
|
||
self.onMouseLeave()
|
||
}
|
||
})
|
||
}
|
||
|
||
this.selectTab(0)
|
||
|
||
$(window).on('resize', this.updatePosition.bind(this))
|
||
|
||
return true
|
||
}
|
||
|
||
EmojiTooltip.prototype.selectCategory = function (cat, force) {
|
||
if (!this.tab && this.cat === cat && !force) {
|
||
return false
|
||
}
|
||
$('.active', this.categoriesEl).removeClass('active')
|
||
this.cat = cat
|
||
|
||
if (this.tab) {
|
||
this.activateStickerCategory()
|
||
this.updateStickersContents(force)
|
||
} else {
|
||
$(this.categoriesEl[this.tab].childNodes[cat]).addClass('active')
|
||
this.updateEmojiContents()
|
||
}
|
||
}
|
||
|
||
EmojiTooltip.prototype.selectTab = function (tab, force) {
|
||
if (this.tab === tab && !force) {
|
||
return false
|
||
}
|
||
|
||
this.tab = tab
|
||
this.selectCategory(0, true)
|
||
|
||
var self = this
|
||
setTimeout(function () {
|
||
$(self.tooltipEl).toggleClass('composer_emoji_tooltip_tabs_stickers_active', tab == 1)
|
||
}, 0)
|
||
}
|
||
|
||
EmojiTooltip.prototype.updateEmojiContents = function () {
|
||
var html = []
|
||
var self = this
|
||
var iconSize = 26
|
||
|
||
var renderContent = function () {
|
||
self.emojiContentEl.html(html.join(''))
|
||
self.emojiScroller.reinit()
|
||
}
|
||
|
||
if (this.cat > 0) {
|
||
var categoryIndex = this.cat - 1
|
||
var emoticonCodes = Config.EmojiCategories[categoryIndex]
|
||
var totalColumns = Config.EmojiCategorySpritesheetDimens[categoryIndex][1]
|
||
var count = emoticonCodes.length
|
||
var emoticonCode
|
||
var emoticonData
|
||
var i
|
||
var x
|
||
var y
|
||
|
||
for (i = 0; i < count; i++) {
|
||
emoticonCode = emoticonCodes[i]
|
||
emoticonData = Config.Emoji[emoticonCode]
|
||
x = iconSize * (i % totalColumns)
|
||
y = iconSize * Math.floor(i / totalColumns)
|
||
html.push('<a class="composer_emoji_btn" title=":' + encodeEntities(emoticonData[1][0]) + ':" data-code="' + encodeEntities(emoticonCode) + '"><i class="emoji emoji-w' + iconSize + ' emoji-spritesheet-' + categoryIndex + '" style="background-position: -' + x + 'px -' + y + 'px;"></i></a>')
|
||
}
|
||
renderContent()
|
||
}else {
|
||
EmojiHelper.getPopularEmoji(function (popularEmoji) {
|
||
var emoticonCode
|
||
var emoticonData
|
||
var spritesheet
|
||
var pos
|
||
var categoryIndex
|
||
var count = popularEmoji.length
|
||
var i
|
||
var x
|
||
var y
|
||
|
||
for (i = 0; i < count; i++) {
|
||
emoticonCode = popularEmoji[i].code
|
||
if (emoticonData = Config.Emoji[emoticonCode]) {
|
||
spritesheet = EmojiHelper.spritesheetPositions[emoticonCode]
|
||
categoryIndex = spritesheet[0]
|
||
pos = spritesheet[1]
|
||
x = iconSize * spritesheet[3]
|
||
y = iconSize * spritesheet[2]
|
||
html.push('<a class="composer_emoji_btn" title=":' + encodeEntities(emoticonData[1][0]) + ':" data-code="' + encodeEntities(emoticonCode) + '"><i class="emoji emoji-w' + iconSize + ' emoji-spritesheet-' + categoryIndex + '" style="background-position: -' + x + 'px -' + y + 'px;"></i></a>')
|
||
}
|
||
}
|
||
renderContent()
|
||
})
|
||
}
|
||
}
|
||
|
||
EmojiTooltip.prototype.updateStickersContents = function (force) {
|
||
var html = []
|
||
var categoriesHtml = []
|
||
var self = this
|
||
var iconSize = 26
|
||
|
||
var scrollStickers = function () {
|
||
var scrollTop = self.cat ? self.stickersetPositions[self.cat][0] : 0
|
||
self.stickersScroller.scrollTo(scrollTop, force ? 0 : 200)
|
||
}
|
||
|
||
if (!force && self.stickersetPositions.length) {
|
||
scrollStickers()
|
||
return
|
||
}
|
||
|
||
var renderStickers = function (stickersets) {
|
||
var set
|
||
var docID
|
||
var i
|
||
var j
|
||
var len1
|
||
var len2
|
||
for (i = 0, len1 = stickersets.length; i < len1; i++) {
|
||
set = stickersets[i]
|
||
if (!set.docIDs.length) {
|
||
continue
|
||
}
|
||
html.push('<div class="composer_stickerset_wrap clearfix">')
|
||
if (set.title) {
|
||
html.push(
|
||
'<a class="composer_stickerset_title',
|
||
set.id ? '' : ' disabled',
|
||
'" data-stickerset="',
|
||
encodeEntities(set.short_name),
|
||
'">',
|
||
encodeEntities(set.title),
|
||
'</a>'
|
||
)
|
||
}
|
||
if (!set.id) {
|
||
categoriesHtml.push('<a class="composer_emoji_tooltip_category active" data-category="0"><i class="composer_emoji_tooltip_category_recent"></i></a>')
|
||
} else {
|
||
categoriesHtml.push('<a class="composer_sticker_btn" data-sticker="' + set.docIDs[0] + '" data-category="' + i + '"></a>')
|
||
}
|
||
for (j = 0, len2 = set.docIDs.length; j < len2; j++) {
|
||
docID = set.docIDs[j]
|
||
html.push('<a class="composer_sticker_btn" data-sticker="' + docID + '"></a>')
|
||
}
|
||
html.push('</div>')
|
||
}
|
||
|
||
self.stickersContentEl.html(html.join(''))
|
||
self.stickersCategoriesEl.html(categoriesHtml.join(''))
|
||
self.stickersScroller.reinit()
|
||
|
||
var scrollPositions = []
|
||
$('.composer_stickerset_wrap', self.stickersContentEl).each(function (k, stickerSetEl) {
|
||
var height = stickerSetEl.offsetHeight
|
||
var top = stickerSetEl.offsetTop
|
||
scrollPositions.push([top, height])
|
||
})
|
||
self.stickersetPositions = scrollPositions
|
||
scrollStickers()
|
||
|
||
var preload = []
|
||
self.contentEl.find('.composer_sticker_btn').each(function (k, element) {
|
||
if (k < 12) {
|
||
self.replaceStickerImage(element)
|
||
} else {
|
||
preload.push([element.offsetTop, element])
|
||
}
|
||
})
|
||
self.stickersPreload = preload
|
||
|
||
self.stickersCategoriesEl.find('.composer_sticker_btn').each(function (k, element) {
|
||
self.replaceStickerImage(element)
|
||
})
|
||
}
|
||
this.getStickers(renderStickers)
|
||
}
|
||
|
||
EmojiTooltip.prototype.replaceStickerImage = function (element) {
|
||
element = $(element)
|
||
this.getStickerImage(element, element.attr('data-sticker'))
|
||
}
|
||
|
||
EmojiTooltip.prototype.onStickersScroll = function (scrollable, scrollTop) {
|
||
var ch = scrollable.clientHeight
|
||
var sh = scrollable.scrollHeight
|
||
var len = this.stickersetPositions.length
|
||
var currentCat = false
|
||
var currentPos
|
||
var i
|
||
|
||
if (scrollTop < 20) {
|
||
currentCat = 0
|
||
} else if (scrollTop > sh - ch - 20) {
|
||
currentCat = len - 1
|
||
} else {
|
||
for (i = 0; i < len; i++) {
|
||
currentPos = this.stickersetPositions[i]
|
||
if (scrollTop >= currentPos[0] &&
|
||
scrollTop < (currentPos[0] + currentPos[1])) {
|
||
currentCat = i
|
||
break
|
||
}
|
||
}
|
||
}
|
||
var len = this.stickersPreload.length
|
||
if (len) {
|
||
for (i = 0; i < len; i++) {
|
||
currentPos = this.stickersPreload[i]
|
||
if (currentPos[0] >= scrollTop && currentPos[0] <= scrollTop + ch) {
|
||
// console.log('replace', currentPos[1], i)
|
||
this.replaceStickerImage(currentPos[1])
|
||
this.stickersPreload.splice(i, 1)
|
||
i--
|
||
len--
|
||
}
|
||
}
|
||
}
|
||
// console.log('on sticker scroll', scrollTop, ch, sh, currentCat, this.stickersetPositions)
|
||
if (this.cat === currentCat || currentCat === false) {
|
||
return
|
||
}
|
||
$('.active', this.categoriesEl).removeClass('active')
|
||
this.cat = currentCat
|
||
this.activateStickerCategory()
|
||
}
|
||
|
||
EmojiTooltip.prototype.onStickersChanged = function () {
|
||
if (this.tab) {
|
||
this.updateStickersContents(true)
|
||
}
|
||
}
|
||
|
||
EmojiTooltip.prototype.activateStickerCategory = function () {
|
||
var categoriesEl = this.categoriesEl[1]
|
||
var categoryEl = categoriesEl.childNodes[this.cat]
|
||
if (!categoryEl) {
|
||
return
|
||
}
|
||
$(categoryEl).addClass('active')
|
||
|
||
var left = categoryEl.offsetLeft
|
||
var width = categoryEl.offsetWidth
|
||
var viewportWidth = categoriesEl.clientWidth
|
||
|
||
// console.log('current cat el', categoryEl, left, width, viewportWidth)
|
||
$(categoriesEl).stop(true).animate({scrollLeft: left - (viewportWidth - width) / 2}, 200)
|
||
}
|
||
|
||
EmojiTooltip.prototype.updatePosition = function () {
|
||
var offset = this.btnEl.offset()
|
||
this.tooltipEl.css({top: offset.top, left: offset.left})
|
||
}
|
||
|
||
EmojiTooltip.prototype.show = function () {
|
||
this.updatePosition()
|
||
if (this.tab) {
|
||
this.updateStickersContents(true)
|
||
} else {
|
||
this.updateEmojiContents()
|
||
}
|
||
this.tooltipEl.addClass('composer_emoji_tooltip_shown')
|
||
this.btnEl.addClass('composer_emoji_insert_btn_on')
|
||
delete this.showTimeout
|
||
this.shown = true
|
||
}
|
||
|
||
EmojiTooltip.prototype.hide = function () {
|
||
if (this.tooltipEl) {
|
||
this.tooltipEl.removeClass('composer_emoji_tooltip_shown')
|
||
this.btnEl.removeClass('composer_emoji_insert_btn_on')
|
||
}
|
||
delete this.hideTimeout
|
||
delete this.shown
|
||
}
|
||
|
||
function EmojiPanel (containerEl, options) {
|
||
options = options || {}
|
||
var self = this
|
||
|
||
this.containerEl = $(containerEl)
|
||
this.onEmojiSelected = options.onEmojiSelected
|
||
|
||
this.containerEl.on('mousedown', function (e) {
|
||
e = e.originalEvent || e
|
||
var target = $(e.target), code
|
||
if (target[0].tagName != 'A') {
|
||
target = $(target[0].parentNode)
|
||
}
|
||
if (code = target.attr('data-code')) {
|
||
if (self.onEmojiSelected) {
|
||
self.onEmojiSelected(code)
|
||
}
|
||
EmojiHelper.pushPopularEmoji(code)
|
||
}
|
||
return cancelEvent(e)
|
||
})
|
||
|
||
this.update()
|
||
}
|
||
|
||
EmojiPanel.prototype.update = function () {
|
||
var html = []
|
||
var self = this
|
||
var iconSize = Config.Mobile ? 26 : 20
|
||
|
||
EmojiHelper.getPopularEmoji(function (popularEmoji) {
|
||
var emoticonCode
|
||
var emoticonData
|
||
var spritesheet
|
||
var pos
|
||
var categoryIndex
|
||
var count = popularEmoji.length
|
||
var i
|
||
var x
|
||
var y
|
||
|
||
for (i = 0; i < count; i++) {
|
||
emoticonCode = popularEmoji[i].code
|
||
if (emoticonData = Config.Emoji[emoticonCode]) {
|
||
spritesheet = EmojiHelper.spritesheetPositions[emoticonCode]
|
||
categoryIndex = spritesheet[0]
|
||
pos = spritesheet[1]
|
||
x = iconSize * spritesheet[3]
|
||
y = iconSize * spritesheet[2]
|
||
html.push('<a class="composer_emoji_btn" title=":' + encodeEntities(emoticonData[1][0]) + ':" data-code="' + encodeEntities(emoticonCode) + '"><i class="emoji emoji-w20 emoji-spritesheet-' + categoryIndex + '" style="background-position: -' + x + 'px -' + y + 'px;"></i></a>')
|
||
}
|
||
}
|
||
self.containerEl.html(html.join(''))
|
||
})
|
||
}
|
||
|
||
function MessageComposer (textarea, options) {
|
||
var self = this
|
||
|
||
this.textareaEl = $(textarea)
|
||
|
||
this.setUpInput()
|
||
|
||
this.autoCompleteWrapEl = $('<div class="composer_dropdown_wrap"></div>').appendTo(document.body)
|
||
var autoCompleteEl = $('<div></div>').appendTo(this.autoCompleteWrapEl)
|
||
|
||
options.dropdownDirective(autoCompleteEl, function (scope, newAutoCompleteEl) {
|
||
self.autoCompleteEl = newAutoCompleteEl
|
||
self.autoCompleteScope = scope
|
||
self.setUpAutoComplete()
|
||
})
|
||
|
||
this.isActive = false
|
||
|
||
this.onTyping = options.onTyping
|
||
this.onMessageSubmit = options.onMessageSubmit
|
||
this.onDirectionKey = options.onDirectionKey
|
||
this.getSendOnEnter = options.getSendOnEnter
|
||
this.onFilePaste = options.onFilePaste
|
||
this.onCommandSend = options.onCommandSend
|
||
this.onInlineResultSend = options.onInlineResultSend
|
||
this.mentions = options.mentions
|
||
this.commands = options.commands
|
||
}
|
||
|
||
MessageComposer.autoCompleteRegEx = /(\s|^)(:|@|\/)([\S]*)$/
|
||
|
||
MessageComposer.prototype.setUpInput = function () {
|
||
this.inlinePlaceholderWrap = $('<div class="im_inline_placeholder_wrap"></div>').prependTo(this.textareaEl[0].parentNode)
|
||
this.inlinePlaceholderPrefixEl = $('<span class="im_inline_placeholder_prefix"></span>').appendTo(this.inlinePlaceholderWrap)
|
||
this.inlinePlaceholderEl = $('<span class="im_inline_placeholder"></span>').appendTo(this.inlinePlaceholderWrap)
|
||
|
||
if ('contentEditable' in document.body) {
|
||
this.setUpRich()
|
||
} else {
|
||
this.setUpPlaintext()
|
||
}
|
||
|
||
if (!Config.Mobile) {
|
||
var sbWidth = getScrollWidth()
|
||
if (sbWidth) {
|
||
// hide scrollbar for both LTR and RTL languages
|
||
// both scrollbars are hidden inside the paddings
|
||
// that are overflown outside of view
|
||
(this.richTextareaEl || this.textareaEl).css({
|
||
left: -sbWidth,
|
||
width: 'calc(100% + ' + (2 * sbWidth) + 'px)',
|
||
'padding-left': sbWidth + 2,
|
||
'padding-right': sbWidth + 28
|
||
})
|
||
}
|
||
}
|
||
}
|
||
|
||
MessageComposer.prototype.setInlinePlaceholder = function (prefix, placeholder) {
|
||
this.inlinePlaceholderPrefix = prefix
|
||
this.inlinePlaceholderPrefixEl.html(encodeEntities(prefix))
|
||
this.inlinePlaceholderEl.html(encodeEntities(placeholder))
|
||
this.onChange()
|
||
}
|
||
|
||
MessageComposer.prototype.updateInlinePlaceholder = function () {
|
||
var prefix = this.inlinePlaceholderPrefix
|
||
if (prefix) {
|
||
var value = this.textareaEl.val()
|
||
this.inlinePlaceholderWrap.toggleClass('active', value == prefix)
|
||
}
|
||
}
|
||
|
||
MessageComposer.prototype.setUpAutoComplete = function () {
|
||
this.scroller = new Scroller(this.autoCompleteEl, {maxHeight: 180})
|
||
|
||
var self = this
|
||
this.autoCompleteEl.on('mousedown', function (e) {
|
||
e = e.originalEvent || e
|
||
var target = e.target
|
||
var mention
|
||
var code
|
||
var command
|
||
var inlineID
|
||
while (target && target.tagName != 'A') {
|
||
target = target.parentNode
|
||
}
|
||
if (!target) {
|
||
return cancelEvent(e)
|
||
}
|
||
target = $(target)
|
||
if (code = target.attr('data-code')) {
|
||
if (self.onEmojiSelected) {
|
||
self.onEmojiSelected(code, true)
|
||
}
|
||
EmojiHelper.pushPopularEmoji(code)
|
||
}
|
||
if (mention = target.attr('data-mention')) {
|
||
self.onMentionSelected(mention, target.attr('data-name'))
|
||
}
|
||
if (command = target.attr('data-command')) {
|
||
if (self.onCommandSelected) {
|
||
self.onCommandSelected(command)
|
||
}
|
||
self.hideSuggestions()
|
||
}
|
||
if (inlineID = target.attr('data-inlineid')) {
|
||
if (self.onInlineResultSend) {
|
||
self.onInlineResultSend(inlineID)
|
||
}
|
||
self.hideSuggestions()
|
||
}
|
||
return cancelEvent(e)
|
||
})
|
||
}
|
||
|
||
MessageComposer.prototype.setUpRich = function () {
|
||
this.textareaEl.hide()
|
||
this.richTextareaEl = $('<div class="composer_rich_textarea" contenteditable="true" dir="auto"></div>')
|
||
|
||
this.textareaEl[0].parentNode.insertBefore(this.richTextareaEl[0], this.textareaEl[0])
|
||
|
||
this.richTextareaEl.on('keyup keydown', this.onKeyEvent.bind(this))
|
||
this.richTextareaEl.on('focus blur', this.onFocusBlur.bind(this))
|
||
this.richTextareaEl.on('paste', this.onRichPaste.bind(this))
|
||
this.richTextareaEl.on('DOMNodeInserted', this.onRichPasteNode.bind(this))
|
||
|
||
$(document.body).on('keydown', this.backupSelection.bind(this))
|
||
}
|
||
|
||
MessageComposer.prototype.setUpPlaintext = function () {
|
||
this.textareaEl.on('keyup keydown', this.onKeyEvent.bind(this))
|
||
this.textareaEl.on('focus blur', this.onFocusBlur.bind(this))
|
||
}
|
||
|
||
MessageComposer.prototype.onKeyEvent = function (e) {
|
||
var self = this
|
||
if (e.type == 'keyup') {
|
||
// console.log(dT(), 'keyup', e.keyCode)
|
||
this.checkAutocomplete()
|
||
|
||
var length = false
|
||
if (this.richTextareaEl) {
|
||
clearTimeout(this.updateValueTO)
|
||
var now = tsNow()
|
||
if (this.keyupStarted === undefined) {
|
||
this.keyupStarted = now
|
||
}
|
||
if (now - this.keyupStarted > 3000 || true) {
|
||
this.onChange()
|
||
}else {
|
||
length = this.richTextareaEl[0].textContent.length
|
||
if (this.wasEmpty != !length) {
|
||
this.wasEmpty = !this.wasEmpty
|
||
this.onChange()
|
||
} else if (this.inlinePlaceholderPrefix) {
|
||
this.onChange()
|
||
} else {
|
||
this.updateValueTO = setTimeout(this.onChange.bind(this), 1000)
|
||
}
|
||
}
|
||
}
|
||
|
||
if (this.onTyping) {
|
||
var now = tsNow()
|
||
if (now - this.lastTyping > 5000) {
|
||
if (length === false) {
|
||
length = (this.richTextareaEl ? this.richTextareaEl[0].textContent : this.textareaEl[0].value).length
|
||
}
|
||
|
||
if (length != this.lastLength) {
|
||
this.lastTyping = now
|
||
this.lastLength = length
|
||
this.onTyping()
|
||
}
|
||
}
|
||
}
|
||
}
|
||
if (e.type == 'keydown') {
|
||
var checkSubmit = !this.autocompleteShown
|
||
if (this.autocompleteShown) {
|
||
if (e.keyCode == 38 || e.keyCode == 40) { // UP / DOWN
|
||
var next = e.keyCode == 40
|
||
var currentSel = $(this.autoCompleteEl).find('li.composer_autocomplete_option_active')
|
||
var allLIs = Array.prototype.slice.call($(this.autoCompleteEl).find('li'))
|
||
var nextSel
|
||
|
||
if (currentSel.length) {
|
||
var pos = allLIs.indexOf(currentSel[0])
|
||
var nextPos = pos + (next ? 1 : -1)
|
||
nextSel = allLIs[nextPos]
|
||
currentSel.removeClass('composer_autocomplete_option_active')
|
||
if (nextSel) {
|
||
$(nextSel).addClass('composer_autocomplete_option_active')
|
||
this.scroller.scrollToNode(nextSel)
|
||
// console.log(dT(), 'keydown cancel', e.keyCode)
|
||
return cancelEvent(e)
|
||
}
|
||
}
|
||
|
||
nextSel = allLIs[next ? 0 : allLIs.length - 1]
|
||
this.scroller.scrollToNode(nextSel)
|
||
$(nextSel).addClass('composer_autocomplete_option_active')
|
||
|
||
// console.log(dT(), 'keydown cancel', e.keyCode)
|
||
return cancelEvent(e)
|
||
}
|
||
|
||
if (e.keyCode == 13 || e.keyCode == 9) { // Enter or Tab
|
||
var currentSel = $(this.autoCompleteEl).find('li.composer_autocomplete_option_active')
|
||
if (!currentSel.length && e.keyCode == 9) {
|
||
currentSel = $(this.autoCompleteEl).find('li:first')
|
||
}
|
||
currentSel = currentSel.find('a:first')
|
||
var code
|
||
var mention
|
||
var command
|
||
var inlineID
|
||
if (code = currentSel.attr('data-code')) {
|
||
this.onEmojiSelected(code, true)
|
||
EmojiHelper.pushPopularEmoji(code)
|
||
return cancelEvent(e)
|
||
}
|
||
if (mention = currentSel.attr('data-mention')) {
|
||
this.onMentionSelected(mention, currentSel.attr('data-name'))
|
||
return cancelEvent(e)
|
||
}
|
||
if (command = currentSel.attr('data-command')) {
|
||
if (this.onCommandSelected) {
|
||
this.onCommandSelected(command, e.keyCode == 9)
|
||
}
|
||
return cancelEvent(e)
|
||
}
|
||
if (inlineID = currentSel.attr('data-inlineid')) {
|
||
if (self.onInlineResultSend) {
|
||
self.onInlineResultSend(inlineID)
|
||
}
|
||
self.hideSuggestions()
|
||
// console.log(dT(), 'keydown cancel', e.keyCode)
|
||
return cancelEvent(e)
|
||
}
|
||
checkSubmit = true
|
||
}
|
||
}
|
||
|
||
if (checkSubmit && e.keyCode == 13) {
|
||
var submit = false
|
||
var sendOnEnter = true
|
||
if (this.getSendOnEnter && !this.getSendOnEnter()) {
|
||
sendOnEnter = false
|
||
}
|
||
if (sendOnEnter && !e.shiftKey) {
|
||
submit = true
|
||
} else if (!sendOnEnter && (e.ctrlKey || e.metaKey)) {
|
||
submit = true
|
||
}
|
||
|
||
if (submit) {
|
||
this.onMessageSubmit(e)
|
||
return cancelEvent(e)
|
||
}
|
||
}
|
||
|
||
// Direction keys when content is empty
|
||
if ([33, 34, 35, 36, 38, 39].indexOf(e.keyCode) != -1 &&
|
||
!e.shiftKey &&
|
||
!e.altKey &&
|
||
!e.ctrlKey &&
|
||
!e.metaKey &&
|
||
this.richTextareaEl &&
|
||
!this.richTextareaEl[0].textContent.length) {
|
||
return this.onDirectionKey(e)
|
||
}
|
||
}
|
||
}
|
||
|
||
MessageComposer.prototype.backupSelection = function () {
|
||
delete this.selection
|
||
|
||
if (!this.isActive) {
|
||
return
|
||
}
|
||
if (window.getSelection) {
|
||
var sel = window.getSelection()
|
||
if (sel.getRangeAt && sel.rangeCount) {
|
||
this.selection = sel.getRangeAt(0)
|
||
}
|
||
} else if (document.selection && document.selection.createRange) {
|
||
this.selection = document.selection.createRange()
|
||
}
|
||
}
|
||
|
||
MessageComposer.prototype.restoreSelection = function () {
|
||
if (!this.selection) {
|
||
return false
|
||
}
|
||
var result = false
|
||
if (window.getSelection) {
|
||
var sel = window.getSelection()
|
||
sel.removeAllRanges()
|
||
sel.addRange(this.selection)
|
||
result = true
|
||
}
|
||
else if (document.selection && this.selection.select) {
|
||
this.selection.select()
|
||
result = true
|
||
}
|
||
delete this.selection
|
||
|
||
return result
|
||
}
|
||
|
||
MessageComposer.prototype.checkAutocomplete = function (forceFull) {
|
||
var pos
|
||
var value
|
||
if (this.richTextareaEl) {
|
||
var textarea = this.richTextareaEl[0]
|
||
var valueCaret = getRichValueWithCaret(textarea)
|
||
var value = valueCaret[0]
|
||
var pos = valueCaret[1] >= 0 ? valueCaret[1] : value.length
|
||
|
||
if (!pos) {
|
||
this.cleanRichTextarea(value, true)
|
||
}
|
||
} else {
|
||
var textarea = this.textareaEl[0]
|
||
var pos = getFieldSelection(textarea)
|
||
var value = textarea.value
|
||
}
|
||
|
||
if (value &&
|
||
this.curInlineResults &&
|
||
this.curInlineResults.text == value) {
|
||
this.showInlineSuggestions(this.curInlineResults)
|
||
return
|
||
}
|
||
|
||
if (!forceFull) {
|
||
value = value.substr(0, pos)
|
||
}
|
||
|
||
var matches = value.match(MessageComposer.autoCompleteRegEx)
|
||
if (matches) {
|
||
if (this.previousQuery == matches[0]) {
|
||
return
|
||
}
|
||
this.previousQuery = matches[0]
|
||
var query = SearchIndexManager.cleanSearchText(matches[3])
|
||
|
||
if (matches[2] == '@') { // mentions
|
||
if (this.mentions && this.mentions.index) {
|
||
if (query.length) {
|
||
var foundObject = SearchIndexManager.search(query, this.mentions.index)
|
||
var foundUsers = []
|
||
var user
|
||
for (var i = 0, length = this.mentions.users.length; i < length; i++) {
|
||
user = this.mentions.users[i]
|
||
if (foundObject[user.id]) {
|
||
foundUsers.push(user)
|
||
}
|
||
}
|
||
} else {
|
||
var foundUsers = this.mentions.users
|
||
}
|
||
if (foundUsers.length) {
|
||
this.showMentionSuggestions(foundUsers)
|
||
} else {
|
||
this.hideSuggestions()
|
||
}
|
||
} else {
|
||
this.hideSuggestions()
|
||
}
|
||
}
|
||
else if (!matches[1] && matches[2] == '/') { // commands
|
||
if (this.commands && this.commands.index) {
|
||
if (query.length) {
|
||
var foundObject = SearchIndexManager.search(query, this.commands.index)
|
||
var foundCommands = []
|
||
var command
|
||
for (var i = 0, length = this.commands.list.length; i < length; i++) {
|
||
command = this.commands.list[i]
|
||
if (foundObject[command.value]) {
|
||
foundCommands.push(command)
|
||
}
|
||
}
|
||
} else {
|
||
var foundCommands = this.commands.list
|
||
}
|
||
if (foundCommands.length) {
|
||
this.showCommandsSuggestions(foundCommands)
|
||
} else {
|
||
this.hideSuggestions()
|
||
}
|
||
} else {
|
||
this.hideSuggestions()
|
||
}
|
||
}
|
||
else if (matches[2] == ':') { // emoji
|
||
EmojiHelper.getPopularEmoji((function (popular) {
|
||
if (query.length) {
|
||
var found = EmojiHelper.searchEmojis(query)
|
||
if (found.length) {
|
||
var popularFound = [],
|
||
code
|
||
var pos
|
||
for (var i = 0, len = popular.length; i < len; i++) {
|
||
code = popular[i].code
|
||
pos = found.indexOf(code)
|
||
if (pos >= 0) {
|
||
popularFound.push(code)
|
||
found.splice(pos, 1)
|
||
if (!found.length) {
|
||
break
|
||
}
|
||
}
|
||
}
|
||
this.showEmojiSuggestions(popularFound.concat(found))
|
||
} else {
|
||
this.hideSuggestions()
|
||
}
|
||
} else {
|
||
this.showEmojiSuggestions(popular)
|
||
}
|
||
}).bind(this))
|
||
}
|
||
}else {
|
||
delete this.previousQuery
|
||
this.hideSuggestions()
|
||
}
|
||
}
|
||
|
||
MessageComposer.prototype.onFocusBlur = function (e) {
|
||
this.isActive = e.type == 'focus'
|
||
|
||
if (!this.isActive) {
|
||
this.cleanRichTextarea()
|
||
this.hideSuggestions()
|
||
} else {
|
||
setTimeout(this.checkAutocomplete.bind(this), 100)
|
||
}
|
||
if (this.richTextareaEl) {
|
||
document.execCommand('enableObjectResizing', !this.isActive, !this.isActive)
|
||
}
|
||
}
|
||
|
||
MessageComposer.prototype.onRichPaste = function (e) {
|
||
var cData = (e.originalEvent || e).clipboardData
|
||
var items = cData && cData.items || [],
|
||
i
|
||
for (i = 0; i < items.length; i++) {
|
||
if (items[i].kind == 'file') {
|
||
e.preventDefault()
|
||
return true
|
||
}
|
||
}
|
||
|
||
try {
|
||
var text = cData.getData('text/plain')
|
||
} catch (e) {
|
||
return true
|
||
}
|
||
setZeroTimeout(this.onChange.bind(this), 0)
|
||
if (text.length) {
|
||
document.execCommand('insertText', false, text)
|
||
return cancelEvent(e)
|
||
}
|
||
return true
|
||
}
|
||
|
||
MessageComposer.prototype.cleanRichTextarea = function (value, focused) {
|
||
if (!this.richTextareaEl[0]) {
|
||
return
|
||
}
|
||
if (value === undefined) {
|
||
value = getRichValue(this.richTextareaEl[0])
|
||
}
|
||
if (value.match(/^\s*$/) && this.richTextareaEl.html().length > 0) {
|
||
this.richTextareaEl.html('')
|
||
this.lastLength = 0
|
||
this.wasEmpty = true
|
||
|
||
if (focused) {
|
||
var self = this
|
||
setZeroTimeout(function () {
|
||
self.focus()
|
||
})
|
||
}
|
||
}
|
||
}
|
||
|
||
MessageComposer.prototype.onRichPasteNode = function (e) {
|
||
var element = (e.originalEvent || e).target
|
||
var src = (element || {}).src || ''
|
||
var remove = false
|
||
|
||
if (src.substr(0, 5) == 'data:') {
|
||
remove = true
|
||
var blob = dataUrlToBlob(src)
|
||
this.onFilePaste(blob)
|
||
setZeroTimeout(function () {
|
||
element.parentNode.replaceChild(document.createTextNode(' '), element)
|
||
})
|
||
}
|
||
else if (src && !src.match(/img\/blank\.gif/)) {
|
||
var replacementNode = document.createTextNode(' ' + src + ' ')
|
||
setTimeout(function () {
|
||
element.parentNode.replaceChild(replacementNode, element)
|
||
}, 100)
|
||
}
|
||
}
|
||
|
||
MessageComposer.prototype.onEmojiSelected = function (code, autocomplete) {
|
||
if (this.richTextareaEl) {
|
||
var textarea = this.richTextareaEl[0]
|
||
if (!this.isActive) {
|
||
if (!this.restoreSelection()) {
|
||
setRichFocus(textarea)
|
||
}
|
||
}
|
||
if (autocomplete) {
|
||
var valueCaret = getRichValueWithCaret(textarea)
|
||
var fullValue = valueCaret[0]
|
||
var pos = valueCaret[1] >= 0 ? valueCaret[1] : fullValue.length
|
||
var suffix = fullValue.substr(pos)
|
||
var prefix = fullValue.substr(0, pos)
|
||
var matches = prefix.match(/:([\S]*)$/)
|
||
var emoji = EmojiHelper.emojis[code]
|
||
|
||
var newValuePrefix
|
||
if (matches && matches[0]) {
|
||
newValuePrefix = prefix.substr(0, matches.index) + ':' + emoji[1] + ':'
|
||
} else {
|
||
newValuePrefix = prefix + ':' + emoji[1] + ':'
|
||
}
|
||
textarea.value = newValue
|
||
|
||
var html
|
||
if (suffix.length) {
|
||
this.selId = (this.selId || 0) + 1
|
||
html = this.getRichHtml(newValuePrefix) + ' <span id="composer_sel' + this.selId + '"></span>' + this.getRichHtml(suffix)
|
||
this.richTextareaEl.html(html)
|
||
setRichFocus(textarea, $('#composer_sel' + this.selId)[0])
|
||
} else {
|
||
html = this.getRichHtml(newValuePrefix) + ' '
|
||
this.richTextareaEl.html(html)
|
||
setRichFocus(textarea)
|
||
}
|
||
} else {
|
||
var html = this.getEmojiHtml(code)
|
||
if (window.getSelection) {
|
||
var sel = window.getSelection()
|
||
if (sel.getRangeAt && sel.rangeCount) {
|
||
var el = document.createElement('div')
|
||
el.innerHTML = html
|
||
var node = el.firstChild
|
||
var range = sel.getRangeAt(0)
|
||
range.deleteContents()
|
||
range.insertNode(document.createTextNode(' '))
|
||
range.insertNode(node)
|
||
range.setStart(node, 0)
|
||
|
||
setTimeout(function () {
|
||
range = document.createRange()
|
||
range.setStartAfter(node)
|
||
range.collapse(true)
|
||
sel.removeAllRanges()
|
||
sel.addRange(range)
|
||
}, 0)
|
||
}
|
||
} else if (document.selection && document.selection.type != 'Control') {
|
||
document.selection.createRange().pasteHTML(html)
|
||
}
|
||
}
|
||
}else {
|
||
var textarea = this.textareaEl[0]
|
||
var fullValue = textarea.value
|
||
var pos = this.isActive ? getFieldSelection(textarea) : fullValue.length
|
||
var suffix = fullValue.substr(pos)
|
||
var prefix = fullValue.substr(0, pos)
|
||
var matches = autocomplete && prefix.match(/:([\S]*)$/)
|
||
var emoji = EmojiHelper.emojis[code]
|
||
|
||
if (matches && matches[0]) {
|
||
var newValue = prefix.substr(0, matches.index) + ':' + emoji[1] + ': ' + suffix
|
||
var newPos = matches.index + emoji[1].length + 3
|
||
} else {
|
||
var newValue = prefix + ':' + emoji[1] + ': ' + suffix
|
||
var newPos = prefix.length + emoji[1].length + 3
|
||
}
|
||
textarea.value = newValue
|
||
setFieldSelection(textarea, newPos)
|
||
}
|
||
|
||
this.hideSuggestions()
|
||
this.onChange()
|
||
}
|
||
|
||
MessageComposer.prototype.onMentionsUpdated = function (username) {
|
||
delete this.previousQuery
|
||
if (this.isActive) {
|
||
this.checkAutocomplete()
|
||
}
|
||
}
|
||
|
||
MessageComposer.prototype.onMentionSelected = function (username, firstName) {
|
||
var hasUsername = true;
|
||
if (username.charAt(0) == '#') {
|
||
hasUsername = false;
|
||
username = username.substr(1);
|
||
firstName = firstName.replace(/\(\)@/, '')
|
||
}
|
||
|
||
if (this.richTextareaEl) {
|
||
var textarea = this.richTextareaEl[0]
|
||
if (!this.isActive) {
|
||
if (!this.restoreSelection()) {
|
||
setRichFocus(textarea)
|
||
}
|
||
}
|
||
var valueCaret = getRichValueWithCaret(textarea)
|
||
var fullValue = valueCaret[0]
|
||
var pos = valueCaret[1] >= 0 ? valueCaret[1] : fullValue.length
|
||
var suffix = fullValue.substr(pos)
|
||
var prefix = fullValue.substr(0, pos)
|
||
var matches = prefix.match(/@([\S]*)$/)
|
||
|
||
var newValuePrefix
|
||
if (matches && matches[0]) {
|
||
newValuePrefix = prefix.substr(0, matches.index) + '@' + username
|
||
} else {
|
||
newValuePrefix = prefix + '@' + username
|
||
}
|
||
|
||
var html
|
||
if (hasUsername) {
|
||
if (suffix.length) {
|
||
this.selId = (this.selId || 0) + 1
|
||
html = this.getRichHtml(newValuePrefix) + ' <span id="composer_sel' + this.selId + '"></span>' + this.getRichHtml(suffix)
|
||
this.richTextareaEl.html(html)
|
||
setRichFocus(textarea, $('#composer_sel' + this.selId)[0])
|
||
} else {
|
||
html = this.getRichHtml(newValuePrefix) + ' '
|
||
this.richTextareaEl.html(html)
|
||
setRichFocus(textarea)
|
||
}
|
||
} else {
|
||
this.selId = (this.selId || 0) + 1
|
||
html = this.getRichHtml(newValuePrefix) + ' (<span id="composer_sel' + this.selId + '">' + encodeEntities(firstName) + '</span>) ' + this.getRichHtml(suffix)
|
||
this.richTextareaEl.html(html)
|
||
setRichFocus(textarea, $('#composer_sel' + this.selId)[0], true)
|
||
}
|
||
} else {
|
||
var textarea = this.textareaEl[0]
|
||
var fullValue = textarea.value
|
||
var pos = this.isActive ? getFieldSelection(textarea) : fullValue.length
|
||
var suffix = fullValue.substr(pos)
|
||
var prefix = fullValue.substr(0, pos)
|
||
var matches = prefix.match(/@([\S]*)$/)
|
||
|
||
var newValuePrefix
|
||
var newValue
|
||
var newPos
|
||
var newPosTo
|
||
if (matches && matches[0]) {
|
||
newValuePrefix = prefix.substr(0, matches.index) + '@' + username
|
||
} else {
|
||
newValuePrefix = prefix + '@' + username
|
||
}
|
||
|
||
if (hasUsername) {
|
||
newValue = newValuePrefix + '@' + username + ' ' + suffix
|
||
newPos = matches.index + username.length + 2
|
||
} else {
|
||
newValue = newValuePrefix + '@' + username + ' (' + firstName + ') ' + suffix
|
||
newPos = matches.index + username.length + 2
|
||
newPosTo = newPos + firstName.length
|
||
}
|
||
textarea.value = newValue
|
||
setFieldSelection(textarea, newPos, newPosTo)
|
||
}
|
||
|
||
this.hideSuggestions()
|
||
this.onChange()
|
||
}
|
||
|
||
MessageComposer.prototype.onCommandSelected = function (command, isTab) {
|
||
if (isTab) {
|
||
if (this.richTextareaEl) {
|
||
this.richTextareaEl.html(encodeEntities(command) + ' ')
|
||
setRichFocus(this.richTextareaEl[0])
|
||
}else {
|
||
var textarea = this.textareaEl[0]
|
||
textarea.value = command + ' '
|
||
setFieldSelection(textarea)
|
||
}
|
||
} else {
|
||
this.onCommandSend(command)
|
||
}
|
||
|
||
this.hideSuggestions()
|
||
this.onChange()
|
||
}
|
||
|
||
MessageComposer.prototype.onChange = function (e) {
|
||
if (this.richTextareaEl) {
|
||
delete this.keyupStarted
|
||
var richValue = getRichValue(this.richTextareaEl[0])
|
||
this.textareaEl.val(richValue).trigger('change')
|
||
}
|
||
this.updateInlinePlaceholder()
|
||
}
|
||
|
||
MessageComposer.prototype.getEmojiHtml = function (code, emoji) {
|
||
emoji = emoji || EmojiHelper.emojis[code]
|
||
var iconSize = 20
|
||
var spritesheet = EmojiHelper.spritesheetPositions[code]
|
||
var categoryIndex = spritesheet[0]
|
||
var pos = spritesheet[1]
|
||
var x = iconSize * spritesheet[3]
|
||
var y = iconSize * spritesheet[2]
|
||
|
||
return '<img src="img/blank.gif" alt=":' + encodeEntities(emoji[1]) + ':" data-code="' + encodeEntities(code) + '" class="emoji emoji-w20 emoji-spritesheet-' + categoryIndex + '" style="background-position: -' + x + 'px -' + y + 'px;" onresizestart="return false" />'
|
||
}
|
||
|
||
MessageComposer.prototype.setValue = function (text) {
|
||
if (this.richTextareaEl) {
|
||
this.richTextareaEl.html(this.getRichHtml(text))
|
||
this.lastLength = text.length
|
||
this.wasEmpty = !text.length
|
||
this.onKeyEvent({type: 'keyup'})
|
||
} else {
|
||
this.textareaEl.val(text)
|
||
}
|
||
}
|
||
|
||
MessageComposer.prototype.setFocusedValue = function (parts) {
|
||
var prefix = parts[0]
|
||
var selection = parts[1]
|
||
var suffix = parts[2]
|
||
|
||
if (this.richTextareaEl) {
|
||
this.selId = (this.selId || 0) + 1
|
||
var html =
|
||
this.getRichHtml(prefix) +
|
||
'<span id="composer_sel' + this.selId + '">' +
|
||
this.getRichHtml(selection) +
|
||
'</span>' +
|
||
this.getRichHtml(suffix)
|
||
|
||
this.richTextareaEl.html(html)
|
||
|
||
setRichFocus(this.richTextareaEl[0], $('#composer_sel' + this.selId)[0], true)
|
||
} else {
|
||
this.textareaEl.val(prefix + selection + suffix)
|
||
setFieldSelection(this.textareaEl[0], prefix.length, prefix.length + selection.length)
|
||
}
|
||
}
|
||
|
||
MessageComposer.prototype.getRichHtml = function (text) {
|
||
var html = $('<div>').text(text).html()
|
||
html = html.replace(/\n/g, '<br/>')
|
||
html = html.replace(/:([A-Za-z0-9\-\+\*_]+?):/gi, (function (all, shortcut) {
|
||
var code = EmojiHelper.shortcuts[shortcut]
|
||
if (code !== undefined) {
|
||
return this.getEmojiHtml(code)
|
||
}
|
||
return all
|
||
}).bind(this))
|
||
html = html.replace(/ /g, ' \u00A0').replace(/^ | $/g, '\u00A0')
|
||
|
||
return html
|
||
}
|
||
|
||
MessageComposer.prototype.focus = function () {
|
||
if (this.richTextareaEl) {
|
||
setZeroTimeout((function () {
|
||
setRichFocus(this.richTextareaEl[0])
|
||
}).bind(this))
|
||
} else {
|
||
setFieldSelection(this.textareaEl[0])
|
||
}
|
||
}
|
||
|
||
MessageComposer.prototype.blur = function () {
|
||
if (this.richTextareaEl) {
|
||
this.richTextareaEl[0].blur()
|
||
} else {
|
||
this.textareaEl[0].blur()
|
||
}
|
||
}
|
||
|
||
MessageComposer.prototype.renderSuggestions = function () {
|
||
this.autoCompleteWrapEl.show()
|
||
this.scroller.reinit()
|
||
this.updatePosition()
|
||
this.autocompleteShown = true
|
||
}
|
||
|
||
MessageComposer.prototype.showEmojiSuggestions = function (codes) {
|
||
var self = this
|
||
setZeroTimeout(function () {
|
||
self.autoCompleteScope.$apply(function () {
|
||
self.autoCompleteScope.type = 'emoji'
|
||
self.autoCompleteScope.emojiCodes = codes
|
||
})
|
||
onContentLoaded(function () {
|
||
self.renderSuggestions()
|
||
})
|
||
})
|
||
}
|
||
|
||
MessageComposer.prototype.showMentionSuggestions = function (users) {
|
||
var self = this
|
||
setZeroTimeout(function () {
|
||
self.autoCompleteScope.$apply(function () {
|
||
self.autoCompleteScope.type = 'mentions'
|
||
self.autoCompleteScope.mentionUsers = users
|
||
})
|
||
onContentLoaded(function () {
|
||
self.renderSuggestions()
|
||
})
|
||
})
|
||
}
|
||
|
||
MessageComposer.prototype.showCommandsSuggestions = function (commands) {
|
||
var self = this
|
||
setZeroTimeout(function () {
|
||
self.autoCompleteScope.$apply(function () {
|
||
self.autoCompleteScope.type = 'commands'
|
||
self.autoCompleteScope.commands = commands
|
||
})
|
||
onContentLoaded(function () {
|
||
self.renderSuggestions()
|
||
})
|
||
})
|
||
}
|
||
|
||
MessageComposer.prototype.showInlineSuggestions = function (botResults) {
|
||
if (!botResults || !botResults.results.length) {
|
||
this.hideSuggestions()
|
||
return
|
||
}
|
||
var self = this
|
||
if (self.autoCompleteScope.type == 'inline' &&
|
||
self.autoCompleteScope.botResults == botResults &&
|
||
self.autocompleteShown) {
|
||
return
|
||
}
|
||
setZeroTimeout(function () {
|
||
self.autoCompleteScope.$apply(function () {
|
||
self.autoCompleteScope.type = 'inline'
|
||
self.autoCompleteScope.botResults = botResults
|
||
})
|
||
onContentLoaded(function () {
|
||
self.renderSuggestions()
|
||
})
|
||
})
|
||
}
|
||
|
||
MessageComposer.prototype.setInlineSuggestions = function (botResults) {
|
||
this.curInlineResults = botResults
|
||
this.checkAutocomplete()
|
||
}
|
||
|
||
MessageComposer.prototype.updatePosition = function () {
|
||
var offset = (this.richTextareaEl || this.textareaEl).offset()
|
||
var height = this.scroller.updateHeight()
|
||
var width = $((this.richTextareaEl || this.textareaEl)[0].parentNode).outerWidth()
|
||
this.autoCompleteWrapEl.css({
|
||
top: offset.top - height,
|
||
left: Config.Mobile ? 0 : offset.left,
|
||
width: Config.Mobile ? '100%' : width - 2
|
||
})
|
||
this.scroller.update()
|
||
}
|
||
|
||
MessageComposer.prototype.hideSuggestions = function () {
|
||
// console.trace()
|
||
// return
|
||
this.autoCompleteWrapEl.hide()
|
||
delete this.autocompleteShown
|
||
}
|
||
|
||
MessageComposer.prototype.resetTyping = function () {
|
||
this.lastTyping = 0
|
||
this.lastLength = 0
|
||
}
|
||
|
||
MessageComposer.prototype.setPlaceholder = function (newPlaceholder) {
|
||
(this.richTextareaEl || this.textareaEl).attr('placeholder', newPlaceholder)
|
||
}
|
||
|
||
function Scroller (content, options) {
|
||
options = options || {}
|
||
var classPrefix = options.classPrefix || 'scroller'
|
||
|
||
this.content = $(content)
|
||
this.useNano = options.nano !== undefined ? options.nano : !Config.Mobile
|
||
this.maxHeight = options.maxHeight
|
||
this.minHeight = options.minHeight
|
||
|
||
if (this.useNano) {
|
||
this.setUpNano()
|
||
} else {
|
||
this.setUpNative()
|
||
}
|
||
this.updateHeight()
|
||
}
|
||
Scroller.prototype.setUpNano = function () {
|
||
this.content.wrap('<div class="scroller_scrollable_container"><div class="scroller_scrollable_wrap nano"><div class="scroller_scrollable nano-content "></div></div></div>')
|
||
|
||
this.scrollable = $(this.content[0].parentNode)
|
||
this.scroller = $(this.scrollable[0].parentNode)
|
||
this.wrap = $(this.scroller[0].parentNode)
|
||
|
||
this.scroller.nanoScroller({preventPageScrolling: true, tabIndex: -1})
|
||
}
|
||
|
||
Scroller.prototype.setUpNative = function () {
|
||
this.content.wrap('<div class="scroller_native_scrollable"></div>')
|
||
this.scrollable = $(this.content[0].parentNode)
|
||
|
||
this.scrollable.css({overflow: 'auto'})
|
||
if (this.maxHeight) {
|
||
this.scrollable.css({maxHeight: this.maxHeight})
|
||
}
|
||
if (this.minHeight) {
|
||
this.scrollable.css({minHeight: this.minHeight})
|
||
}
|
||
}
|
||
|
||
Scroller.prototype.onScroll = function (cb) {
|
||
var self = this
|
||
var scrollable = this.scrollable[0]
|
||
this.scrollable.on('scroll', function (e) {
|
||
if (self.isAnimatedScroll) {
|
||
return
|
||
}
|
||
cb(scrollable, scrollable.scrollTop)
|
||
})
|
||
}
|
||
|
||
Scroller.prototype.update = function () {
|
||
if (this.useNano) {
|
||
$(this.scroller).nanoScroller()
|
||
}
|
||
}
|
||
|
||
Scroller.prototype.reinit = function () {
|
||
this.scrollTo(0)
|
||
if (this.useNano) {
|
||
setTimeout((function () {
|
||
this.updateHeight()
|
||
}).bind(this), 100)
|
||
}
|
||
}
|
||
|
||
Scroller.prototype.updateHeight = function () {
|
||
var height
|
||
if (this.useNano) {
|
||
if (this.maxHeight || this.minHeight) {
|
||
height = this.content[0].offsetHeight
|
||
if (this.maxHeight && height > this.maxHeight) {
|
||
height = this.maxHeight
|
||
}
|
||
if (this.minHeight && height < this.minHeight) {
|
||
height = this.minHeight
|
||
}
|
||
this.wrap.css({height: height})
|
||
} else {
|
||
height = this.scroller[0].offsetHeight
|
||
}
|
||
$(this.scroller).nanoScroller()
|
||
} else {
|
||
height = this.scrollable[0].offsetHeight
|
||
}
|
||
return height
|
||
}
|
||
|
||
Scroller.prototype.scrollTo = function (scrollTop, animation, cb) {
|
||
if (animation > 0) {
|
||
var self = this
|
||
this.isAnimatedScroll = true
|
||
this.scrollable.animate({scrollTop: scrollTop}, animation, function () {
|
||
delete self.isAnimatedScroll
|
||
if (self.useNano) {
|
||
$(self.scroller).nanoScroller({flash: true})
|
||
}
|
||
self.scrollable.trigger('scroll')
|
||
if (cb) {
|
||
cb()
|
||
}
|
||
})
|
||
} else {
|
||
this.scrollable[0].scrollTop = scrollTop
|
||
if (this.useNano) {
|
||
$(this.scroller).nanoScroller({flash: true})
|
||
}
|
||
if (cb) {
|
||
cb()
|
||
}
|
||
}
|
||
}
|
||
|
||
Scroller.prototype.scrollToNode = function (node) {
|
||
node = node[0] || node
|
||
var elTop = node.offsetTop - 15
|
||
var elHeight = node.offsetHeight + 30
|
||
var scrollTop = this.scrollable[0].scrollTop
|
||
var viewportHeight = this.scrollable[0].clientHeight
|
||
|
||
if (scrollTop > elTop) { // we are below the node to scroll
|
||
this.scrollTo(elTop)
|
||
}
|
||
else if (scrollTop < elTop + elHeight - viewportHeight) { // we are over the node to scroll
|
||
this.scrollTo(elTop + elHeight - viewportHeight)
|
||
}
|
||
}
|