/* global Redactor */ Redactor.add('plugin', 'emoji', { translations: { en: { emoji: { emoji: 'Emoji', favorites: 'Favorites', smileys: 'Smileys', gestures: 'Gestures', animals: 'Animals', food: 'Food', activities: 'Activities', travel: 'Travel' } } }, defaults: { context: true, icon: '', trigger: ':', items: { favorites: { faceTearsJoy: '😂', heart: '❤️', rollingFloorLaughing: '🤣', thumbsUpSign: '👍', loudlyCryingFace: '😭', foldedHands: '🙏', throwingKiss: '😘', smilingFaceSmilingEyesThreeHearts: '🥰', smilingFaceHeartShapedEyes: '😍', partyPopper: '🎉', grinningFaceSmilingEyes: '😁', fire: '🔥', birthdayCake: '🎂', flushedFace: '😳', smilingFaceSunglasses: '😎', sparkles: '✨', eyes: '👀', rightPointingBackhand: '👉', hundredPointsSymbol: '💯', poutingFace: '😡' }, smileys: { slightlySmilingFace: '🙂', smile: '😄', laughing: '😆', wink: '😉', heartEyes: '😍', tongueOut: '😛', blush: '😊', smirk: '😏', thinking: '🤔', sleepy: '😪' }, gestures: { thumbsUp: '👍', thumbsDown: '👎', peaceSign: '✌️', clappingHands: '👏', raisingHands: '🙌', facepalm: '🤦', shrug: '🤷', fistBump: '👊', wavingHand: '👋', okHand: '👌' }, animals: { dogFace: '🐶', catFace: '🐱', mouseFace: '🐭', hamsterFace: '🐹', rabbitFace: '🐰', bearFace: '🐻', pandaFace: '🐼', lionFace: '🦁', pigFace: '🐷', frogFace: '🐸' }, food: { greenApple: '🍏', pizza: '🍕', hamburger: '🍔', fries: '🍟', spaghetti: '🍝', sushi: '🍣', iceCream: '🍨', donut: '🍩', cookie: '🍪', cake: '🍰' }, activities: { soccerBall: '⚽', basketball: '🏀', football: '🏈', baseball: '⚾', tennis: '🎾', bowling: '🎳', golf: '🏌️‍♂️', fishingPole: '🎣', bicycle: '🚴', videoGame: '🎮' }, travel: { airplane: '✈️', car: '🚗', bicycle: '🚲', train: '🚆', boat: '⛵', map: '🗺️', beachUmbrella: '🏖️', mountain: '⛰️', camping: '🏕️', suitcase: '🧳' } } }, subscribe: { 'editor.keyup': function (event) { if (!this.opts.is('emoji.trigger')) return this._handle(event) } }, start () { const button = { title: '## emoji.emoji ##', icon: this.opts.get('emoji.icon'), command: 'emoji.popup' } this.handleStr = '' this.handleLen = 1 this.app.toolbar.add('emoji', button) if (this.opts.is('emoji.context')) { this.app.context.add('emoji', button) } }, popup (e, button) { const stack = this.app.create('stack') stack.create('emoji', { width: '372px' }) const $modal = stack.getBody() this._buildEmoji($modal) // open this.app.modal.open({ name: 'emoji', stack, button }) }, // =private _handle (event) { const e = event.get('e') const key = e.which const ctrl = e.ctrlKey || e.metaKey const arrows = [37, 38, 39, 40] const ks = this.app.keycodes if (key === ks.ESC) { this.app.editor.restore() return } if (key === ks.DELETE || key === ks.SPACE || key === ks.SHIFT || ctrl || (arrows.indexOf(key) !== -1)) { return } if (key === ks.BACKSPACE) { this.handleLen = this.handleLen - 2 if (this.handleLen <= 0) { this.handleLen = 1 this._hide() } else if (this.handleLen <= 1) { this._hide() } } this._emit() }, _emit () { const selection = this.app.create('selection') const trigger = this.opts.get('emoji.trigger') const re = new RegExp('^' + trigger) this.handleStr = selection.getText('before', this.handleLen) this.handleStr2 = selection.getText('before', this.handleLen + 1) // detect if (re.test(this.handleStr)) { if (this.handleStr2 && (this.handleStr2[0] === ' ' || this.handleStr2[0] === '' || this.handleStr2[0] === trigger)) { this.handleStr = this.handleStr.replace(trigger, '') this.handleLen++ if ((this.handleLen - 1) > 0) { this._load() } } } }, _load () { this._createPanel() const sections = this._buildEmoji(this.$panel, this.handleStr, true) if (sections === 0) { this._hide() } }, _createPanel () { this.$panel = this.app.panel.build(this, '_insertFromPanel') this.$panel.addClass('rx-panel-emoji').css('max-width', '372px') const scrollTop = this.app.getDoc().scrollTop() const selection = this.app.create('selection') const pos = selection.getPosition() this.app.panel.open({ top: (pos.bottom + scrollTop), left: pos.left }) this.app.editor.save() }, _buildEmoji ($modal, filter, panel) { const items = this.opts.get('emoji.items') let sections = 0 const type = (panel) ? 'panel' : 'emoji' for (const [name, section] of Object.entries(items)) { const $section = this.dom('
') const $title = this.dom('
') const $box = this.dom('
') let size = 0 const title = (this.lang.has('emoji.' + name)) ? this.lang.get('emoji.' + name) : name.charAt(0).toUpperCase() + name.slice(1) $title.html(title) $section.append($title) $section.append($box) for (const [key, value] of Object.entries(section)) { if (filter && filter !== '' && key.search(filter) === -1) continue const $item = this.dom('') $item.html(value) if (panel) { $item.on('click', this._insertFromPanel.bind(this)) } else { $item.on('click', this._insert.bind(this)) } $box.append($item) size++ } if (size > 0) { sections++ $modal.append($section) } } return sections }, _insert (e) { this.app.modal.close() e.preventDefault() e.stopPropagation() const $target = this.dom(e.target) const value = $target.html() const insertion = this.app.create('insertion') insertion.insertText(value, 'after') }, _insertFromPanel (e, $el) { this.app.editor.restore() const $item = ($el) || this.dom(e.target) const replacement = $item.html() const trigger = this.opts.get('emoji.trigger') const offset = this.app.create('offset') const selection = this.app.create('selection') const current = selection.getCurrent() let currentText = current.textContent const offsetObj = offset.get() const leftFix = (trigger + this.handleStr).length const what = trigger + this.handleStr const n = currentText.lastIndexOf(what) if (n >= 0) { currentText = currentText.substring(0, n) + replacement + currentText.substring(n + what.length) } current.textContent = currentText offsetObj.start = offsetObj.start - leftFix + replacement.length offsetObj.end = offsetObj.end - leftFix + replacement.length offset.set(offsetObj) // hide this._hideForce() }, _hidePanel (e) { let hidable = false const key = (e && e.which) const ks = this.app.keycodes if (!e) { hidable = true } else if (e.type === 'click' || key === ks.ESC || key === ks.SPACE) { hidable = true } if (hidable) { this._hideForce() } }, _hide () { this.app.panel.close() this._stopEvents() }, _hideForce () { this._hide() this.handleStr = '' this.handleLen = 1 }, _startEvents () { const name = 'click.rx-plugin-emoji keydown.rx-plugin-emoji' this.app.getDoc().on(name, this._hidePanel.bind(this)) this.app.editor.getEditor().on(name, this._hidePanel.bind(this)) }, _stopEvents () { const name = '.rx-plugin-emoji' this.app.getDoc().off(name) this.app.editor.getEditor().off(name) } })