Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Ryan778
GitHub Repository: Ryan778/Ryan778.github.io
Path: blob/master/emojionearea/src/function/init.js
575 views
1
define([
2
'jquery',
3
'var/emojione',
4
'var/blankImg',
5
'var/slice',
6
'var/css_class',
7
'var/emojioneSupportMode',
8
'var/invisibleChar',
9
'function/trigger',
10
'function/attach',
11
'function/shortnameTo',
12
'function/pasteHtmlAtCaret',
13
'function/getOptions',
14
'function/saveSelection',
15
'function/restoreSelection',
16
'function/htmlFromText',
17
'function/textFromHtml',
18
'function/isObject',
19
'function/calcButtonPosition',
20
'function/lazyLoading',
21
'function/selector',
22
'function/div',
23
'function/updateRecent',
24
'function/getRecent',
25
'function/setRecent',
26
'function/supportsLocalStorage'
27
//'function/calcElapsedTime', // debug only
28
],
29
function($, emojione, blankImg, slice, css_class, emojioneSupportMode, invisibleChar, trigger, attach, shortnameTo,
30
pasteHtmlAtCaret, getOptions, saveSelection, restoreSelection, htmlFromText, textFromHtml, isObject,
31
calcButtonPosition, lazyLoading, selector, div, updateRecent, getRecent, setRecent, supportsLocalStorage)
32
{
33
return function(self, source, options) {
34
//calcElapsedTime('init', function() {
35
options = getOptions(options);
36
self.sprite = options.sprite && emojioneSupportMode < 3;
37
self.inline = options.inline === null ? source.is("INPUT") : options.inline;
38
self.shortnames = options.shortnames;
39
self.saveEmojisAs = options.saveEmojisAs;
40
self.standalone = options.standalone;
41
self.emojiTemplate = '<img alt="{alt}" class="emojione' + (self.sprite ? '-{uni}" src="' + blankImg + '"/>' : 'emoji" src="{img}"/>');
42
self.emojiTemplateAlt = self.sprite ? '<i class="emojione-{uni}"/>' : '<img class="emojioneemoji" src="{img}"/>';
43
self.emojiBtnTemplate = '<i class="emojibtn" role="button" data-name="{name}">' + self.emojiTemplateAlt + '</i>';
44
self.recentEmojis = options.recentEmojis && supportsLocalStorage();
45
46
var pickerPosition = options.pickerPosition;
47
self.floatingPicker = pickerPosition === 'top' || pickerPosition === 'bottom';
48
49
var sourceValFunc = source.is("TEXTAREA") || source.is("INPUT") ? "val" : "text",
50
editor, button, picker, tones, filters, filtersBtns, emojisList, categories, scrollArea,
51
app = div({
52
"class" : css_class + ((self.standalone) ? " " + css_class + "-standalone " : " ") + (source.attr("class") || ""),
53
role: "application"
54
},
55
editor = self.editor = div("editor").attr({
56
contenteditable: (self.standalone) ? false : true,
57
placeholder: options["placeholder"] || source.data("placeholder") || source.attr("placeholder") || "",
58
tabindex: 0
59
}),
60
button = self.button = div('button',
61
div('button-open'),
62
div('button-close')
63
).attr('title', options.buttonTitle),
64
picker = self.picker = div('picker',
65
div('wrapper',
66
filters = div('filters'),
67
scrollArea = div('scroll-area',
68
emojisList = div('emojis-list'),
69
tones = div('tones',
70
function() {
71
if (options.tones) {
72
this.addClass(selector('tones-' + options.tonesStyle, true));
73
for (var i = 0; i <= 5; i++) {
74
this.append($("<i/>", {
75
"class": "btn-tone btn-tone-" + i + (!i ? " active" : ""),
76
"data-skin": i,
77
role: "button"
78
}));
79
}
80
}
81
}
82
)
83
)
84
)
85
).addClass(selector('picker-position-' + options.pickerPosition, true))
86
.addClass(selector('filters-position-' + options.filtersPosition, true))
87
.addClass('hidden')
88
);
89
90
editor.data(source.data());
91
92
$.each(options.attributes, function(attr, value) {
93
editor.attr(attr, value);
94
});
95
96
$.each(options.filters, function(filter, params) {
97
var skin = 0;
98
if (filter === 'recent' && !self.recentEmojis) {
99
return;
100
}
101
if (filter !== 'tones') {
102
$("<i/>", {
103
"class": selector("filter", true) + " " + selector("filter-" + filter, true),
104
"data-filter": filter,
105
title: params.title
106
})
107
.wrapInner(shortnameTo(params.icon, self.emojiTemplateAlt))
108
.appendTo(filters);
109
} else if (options.tones) {
110
skin = 5;
111
} else {
112
return;
113
}
114
do {
115
var category = div('category').attr({name: filter, "data-tone": skin}).appendTo(emojisList),
116
items = params.emoji.replace(/[\s,;]+/g, '|');
117
if (skin > 0) {
118
category.hide();
119
items = items.split('|').join('_tone' + skin + '|') + '_tone' + skin;
120
}
121
122
if (filter === 'recent') {
123
items = getRecent();
124
}
125
126
items = shortnameTo(items,
127
self.sprite ?
128
'<i class="emojibtn" role="button" data-name="{name}"><i class="emojione-{uni}"></i></i>' :
129
'<i class="emojibtn" role="button" data-name="{name}"><img class="emojioneemoji lazy-emoji" data-src="{img}"/></i>',
130
true).split('|').join('');
131
132
category.html(items);
133
$('<h1/>').text(params.title).prependTo(category);
134
} while (--skin > 0);
135
});
136
137
options.filters = null;
138
if (!self.sprite) {
139
self.lasyEmoji = emojisList.find(".lazy-emoji");
140
}
141
142
filtersBtns = filters.find(selector("filter"));
143
filtersBtns.eq(0).addClass("active");
144
categories = emojisList.find(selector("category"));
145
146
self.recentFilter = filtersBtns.filter('[data-filter="recent"]');
147
self.recentCategory = categories.filter("[name=recent]");
148
149
self.scrollArea = scrollArea;
150
151
if (options.container) {
152
$(options.container).wrapInner(app);
153
} else {
154
app.insertAfter(source);
155
}
156
157
if (options.hideSource) {
158
source.hide();
159
}
160
161
self.setText(source[sourceValFunc]());
162
source[sourceValFunc](self.getText());
163
calcButtonPosition.apply(self);
164
165
// if in standalone mode and no value is set, initialise with a placeholder
166
if (self.standalone && !self.getText().length) {
167
var placeholder = $(source).data("emoji-placeholder") || options.emojiPlaceholder;
168
self.setText(placeholder);
169
editor.addClass("has-placeholder");
170
}
171
172
// attach() must be called before any .on() methods !!!
173
// 1) attach() stores events into possibleEvents{},
174
// 2) .on() calls bindEvent() and stores handlers into eventStorage{},
175
// 3) bindEvent() finds events in possibleEvents{} and bind founded via jQuery.on()
176
// 4) attached events via jQuery.on() calls trigger()
177
// 5) trigger() calls handlers stored into eventStorage{}
178
179
attach(self, emojisList.find(".emojibtn"), {click: "emojibtn.click"});
180
attach(self, window, {resize: "!resize"});
181
attach(self, tones.children(), {click: "tone.click"});
182
attach(self, [picker, button], {mousedown: "!mousedown"}, editor);
183
attach(self, button, {click: "button.click"});
184
attach(self, editor, {paste :"!paste"}, editor);
185
attach(self, editor, ["focus", "blur"], function() { return self.stayFocused ? false : editor; });
186
attach(self, picker, {mousedown: "picker.mousedown", mouseup: "picker.mouseup", click: "picker.click",
187
keyup: "picker.keyup", keydown: "picker.keydown", keypress: "picker.keypress"});
188
attach(self, editor, ["mousedown", "mouseup", "click", "keyup", "keydown", "keypress"]);
189
attach(self, picker.find(".emojionearea-filter"), {click: "filter.click"});
190
191
var noListenScroll = false;
192
scrollArea.on('scroll', function () {
193
if (!noListenScroll) {
194
lazyLoading.call(self);
195
if (scrollArea.is(":not(.skinnable)")) {
196
var item = categories.eq(0), scrollTop = scrollArea.offset().top;
197
categories.each(function (i, e) {
198
if ($(e).offset().top - scrollTop >= 10) {
199
return false;
200
}
201
item = $(e);
202
});
203
var filter = filtersBtns.filter('[data-filter="' + item.attr("name") + '"]');
204
if (filter[0] && !filter.is(".active")) {
205
filtersBtns.removeClass("active");
206
filter.addClass("active");
207
}
208
}
209
}
210
});
211
212
self.on("@filter.click", function(filter) {
213
var isActive = filter.is(".active");
214
if (scrollArea.is(".skinnable")) {
215
if (isActive) return;
216
tones.children().eq(0).click();
217
}
218
noListenScroll = true;
219
if (!isActive) {
220
filtersBtns.filter(".active").removeClass("active");
221
filter.addClass("active");
222
}
223
var headerOffset = categories.filter('[name="' + filter.data('filter') + '"]').offset().top,
224
scroll = scrollArea.scrollTop(),
225
offsetTop = scrollArea.offset().top;
226
scrollArea.stop().animate({
227
scrollTop: headerOffset + scroll - offsetTop - 2
228
}, 200, 'swing', function () {
229
lazyLoading.call(self);
230
noListenScroll = false;
231
});
232
})
233
234
.on("@picker.show", function() {
235
if (self.recentEmojis) {
236
updateRecent(self);
237
}
238
lazyLoading.call(self);
239
})
240
241
.on("@tone.click", function(tone) {
242
tones.children().removeClass("active");
243
var skin = tone.addClass("active").data("skin");
244
if (skin) {
245
scrollArea.addClass("skinnable");
246
categories.hide().filter("[data-tone=" + skin + "]").show();
247
if (filtersBtns.eq(0).is('.active[data-filter="recent"]')) {
248
filtersBtns.eq(0).removeClass("active").next().addClass("active");
249
}
250
} else {
251
scrollArea.removeClass("skinnable");
252
categories.hide().filter("[data-tone=0]").show();
253
filtersBtns.eq(0).click();
254
}
255
lazyLoading.call(self);
256
})
257
258
.on("@button.click", function(button) {
259
if (button.is(".active")) {
260
self.hidePicker();
261
} else {
262
self.showPicker();
263
}
264
})
265
266
.on("@!paste", function(editor, event) {
267
268
var pasteText = function(text) {
269
var caretID = "caret-" + (new Date()).getTime();
270
var html = htmlFromText(text, self);
271
pasteHtmlAtCaret(html);
272
pasteHtmlAtCaret('<i id="' + caretID +'"></i>');
273
editor.scrollTop(editorScrollTop);
274
var caret = $("#" + caretID),
275
top = caret.offset().top - editor.offset().top,
276
height = editor.height();
277
if (editorScrollTop + top >= height || editorScrollTop > top) {
278
editor.scrollTop(editorScrollTop + top - 2 * height/3);
279
}
280
caret.remove();
281
self.stayFocused = false;
282
calcButtonPosition.apply(self);
283
trigger(self, 'paste', [editor, text, html]);
284
}
285
286
if (event.originalEvent.clipboardData) {
287
var text = event.originalEvent.clipboardData.getData('text/plain');
288
pasteText(text);
289
290
if (event.preventDefault){
291
event.preventDefault();
292
} else {
293
event.stop();
294
};
295
296
event.returnValue = false;
297
event.stopPropagation();
298
return false;
299
}
300
301
self.stayFocused = true;
302
// insert invisible character for fix caret position
303
pasteHtmlAtCaret('<span>' + invisibleChar + '</span>');
304
305
var sel = saveSelection(editor[0]),
306
editorScrollTop = editor.scrollTop(),
307
clipboard = $("<div/>", {contenteditable: true})
308
.css({position: "fixed", left: "-999px", width: "1px", height: "1px", top: "20px", overflow: "hidden"})
309
.appendTo($("BODY"))
310
.focus();
311
312
window.setTimeout(function() {
313
editor.focus();
314
restoreSelection(editor[0], sel);
315
var text = textFromHtml(clipboard.html().replace(/\r\n|\n|\r/g, '<br>'), self);
316
clipboard.remove();
317
pasteText(text);
318
}, 200);
319
})
320
321
.on("@emojibtn.click", function(emojibtn) {
322
editor.removeClass("has-placeholder");
323
if (!app.is(".focused")) {
324
editor.focus();
325
}
326
if (self.standalone) {
327
editor.html(shortnameTo(emojibtn.data("name"), self.emojiTemplate));
328
self.trigger("blur");
329
} else {
330
saveSelection(editor[0]);
331
pasteHtmlAtCaret(shortnameTo(emojibtn.data("name"), self.emojiTemplate));
332
}
333
334
if (self.recentEmojis) {
335
setRecent(self, emojibtn.data("name"));
336
}
337
})
338
339
.on("@!resize @keyup @emojibtn.click", calcButtonPosition)
340
341
.on("@!mousedown", function(editor, event) {
342
if (!app.is(".focused")) {
343
editor.focus();
344
}
345
event.preventDefault();
346
return false;
347
})
348
349
.on("@change", function() {
350
var html = self.editor.html().replace(/<\/?(?:div|span|p)[^>]*>/ig, '');
351
// clear input: chrome adds <br> when contenteditable is empty
352
if (!html.length || /^<br[^>]*>$/i.test(html)) {
353
self.editor.html(self.content = '');
354
}
355
source[sourceValFunc](self.getText());
356
})
357
358
.on("@focus", function() {
359
app.addClass("focused");
360
})
361
362
.on("@blur", function() {
363
app.removeClass("focused");
364
365
if (options.hidePickerOnBlur) {
366
self.hidePicker();
367
}
368
369
var content = self.editor.html();
370
if (self.content !== content) {
371
self.content = content;
372
trigger(self, 'change', [self.editor]);
373
source.blur().trigger("change");
374
} else {
375
source.blur();
376
}
377
});
378
379
if (options.shortcuts) {
380
self.on("@keydown", function(_, e) {
381
if (!e.ctrlKey) {
382
if (e.which == 9) {
383
e.preventDefault();
384
button.click();
385
}
386
else if (e.which == 27) {
387
e.preventDefault();
388
if (button.is(".active")) {
389
self.hidePicker();
390
}
391
}
392
}
393
});
394
}
395
396
if (isObject(options.events) && !$.isEmptyObject(options.events)) {
397
$.each(options.events, function(event, handler) {
398
self.on(event.replace(/_/g, '.'), handler);
399
});
400
}
401
402
if (options.autocomplete) {
403
var autocomplete = function() {
404
var textcompleteOptions = {
405
maxCount: options.textcomplete.maxCount,
406
placement: options.textcomplete.placement
407
};
408
409
if (options.shortcuts) {
410
textcompleteOptions.onKeydown = function (e, commands) {
411
if (!e.ctrlKey && e.which == 13) {
412
return commands.KEY_ENTER;
413
}
414
};
415
}
416
417
var map = $.map(emojione.emojioneList, function (_, emoji) {
418
return !options.autocompleteTones ? /_tone[12345]/.test(emoji) ? null : emoji : emoji;
419
});
420
map.sort();
421
editor.textcomplete([
422
{
423
id: css_class,
424
match: /\B(:[\-+\w]*)$/,
425
search: function (term, callback) {
426
callback($.map(map, function (emoji) {
427
return emoji.indexOf(term) === 0 ? emoji : null;
428
}));
429
},
430
template: function (value) {
431
return shortnameTo(value, self.emojiTemplate) + " " + value.replace(/:/g, '');
432
},
433
replace: function (value) {
434
return shortnameTo(value, self.emojiTemplate);
435
},
436
cache: true,
437
index: 1
438
}
439
], textcompleteOptions);
440
441
if (options.textcomplete.placement) {
442
// Enable correct positioning for textcomplete
443
if ($(editor.data('textComplete').option.appendTo).css("position") == "static") {
444
$(editor.data('textComplete').option.appendTo).css("position", "relative");
445
}
446
}
447
};
448
if ($.fn.textcomplete) {
449
autocomplete();
450
} else {
451
$.getScript("https://cdn.rawgit.com/yuku-t/jquery-textcomplete/v1.3.4/dist/jquery.textcomplete.js",
452
autocomplete);
453
}
454
}
455
456
if (self.inline) {
457
app.addClass(selector('inline', true));
458
self.on("@keydown", function(_, e) {
459
if (e.which == 13) {
460
e.preventDefault();
461
}
462
});
463
}
464
465
if (/firefox/i.test(navigator.userAgent)) {
466
// disabling resize images on Firefox
467
document.execCommand("enableObjectResizing", false, false);
468
}
469
470
//}, self.id === 1); // calcElapsedTime()
471
};
472
});
473