Path: blob/master/emojionearea/src/function/init.js
575 views
define([1'jquery',2'var/emojione',3'var/blankImg',4'var/slice',5'var/css_class',6'var/emojioneSupportMode',7'var/invisibleChar',8'function/trigger',9'function/attach',10'function/shortnameTo',11'function/pasteHtmlAtCaret',12'function/getOptions',13'function/saveSelection',14'function/restoreSelection',15'function/htmlFromText',16'function/textFromHtml',17'function/isObject',18'function/calcButtonPosition',19'function/lazyLoading',20'function/selector',21'function/div',22'function/updateRecent',23'function/getRecent',24'function/setRecent',25'function/supportsLocalStorage'26//'function/calcElapsedTime', // debug only27],28function($, emojione, blankImg, slice, css_class, emojioneSupportMode, invisibleChar, trigger, attach, shortnameTo,29pasteHtmlAtCaret, getOptions, saveSelection, restoreSelection, htmlFromText, textFromHtml, isObject,30calcButtonPosition, lazyLoading, selector, div, updateRecent, getRecent, setRecent, supportsLocalStorage)31{32return function(self, source, options) {33//calcElapsedTime('init', function() {34options = getOptions(options);35self.sprite = options.sprite && emojioneSupportMode < 3;36self.inline = options.inline === null ? source.is("INPUT") : options.inline;37self.shortnames = options.shortnames;38self.saveEmojisAs = options.saveEmojisAs;39self.standalone = options.standalone;40self.emojiTemplate = '<img alt="{alt}" class="emojione' + (self.sprite ? '-{uni}" src="' + blankImg + '"/>' : 'emoji" src="{img}"/>');41self.emojiTemplateAlt = self.sprite ? '<i class="emojione-{uni}"/>' : '<img class="emojioneemoji" src="{img}"/>';42self.emojiBtnTemplate = '<i class="emojibtn" role="button" data-name="{name}">' + self.emojiTemplateAlt + '</i>';43self.recentEmojis = options.recentEmojis && supportsLocalStorage();4445var pickerPosition = options.pickerPosition;46self.floatingPicker = pickerPosition === 'top' || pickerPosition === 'bottom';4748var sourceValFunc = source.is("TEXTAREA") || source.is("INPUT") ? "val" : "text",49editor, button, picker, tones, filters, filtersBtns, emojisList, categories, scrollArea,50app = div({51"class" : css_class + ((self.standalone) ? " " + css_class + "-standalone " : " ") + (source.attr("class") || ""),52role: "application"53},54editor = self.editor = div("editor").attr({55contenteditable: (self.standalone) ? false : true,56placeholder: options["placeholder"] || source.data("placeholder") || source.attr("placeholder") || "",57tabindex: 058}),59button = self.button = div('button',60div('button-open'),61div('button-close')62).attr('title', options.buttonTitle),63picker = self.picker = div('picker',64div('wrapper',65filters = div('filters'),66scrollArea = div('scroll-area',67emojisList = div('emojis-list'),68tones = div('tones',69function() {70if (options.tones) {71this.addClass(selector('tones-' + options.tonesStyle, true));72for (var i = 0; i <= 5; i++) {73this.append($("<i/>", {74"class": "btn-tone btn-tone-" + i + (!i ? " active" : ""),75"data-skin": i,76role: "button"77}));78}79}80}81)82)83)84).addClass(selector('picker-position-' + options.pickerPosition, true))85.addClass(selector('filters-position-' + options.filtersPosition, true))86.addClass('hidden')87);8889editor.data(source.data());9091$.each(options.attributes, function(attr, value) {92editor.attr(attr, value);93});9495$.each(options.filters, function(filter, params) {96var skin = 0;97if (filter === 'recent' && !self.recentEmojis) {98return;99}100if (filter !== 'tones') {101$("<i/>", {102"class": selector("filter", true) + " " + selector("filter-" + filter, true),103"data-filter": filter,104title: params.title105})106.wrapInner(shortnameTo(params.icon, self.emojiTemplateAlt))107.appendTo(filters);108} else if (options.tones) {109skin = 5;110} else {111return;112}113do {114var category = div('category').attr({name: filter, "data-tone": skin}).appendTo(emojisList),115items = params.emoji.replace(/[\s,;]+/g, '|');116if (skin > 0) {117category.hide();118items = items.split('|').join('_tone' + skin + '|') + '_tone' + skin;119}120121if (filter === 'recent') {122items = getRecent();123}124125items = shortnameTo(items,126self.sprite ?127'<i class="emojibtn" role="button" data-name="{name}"><i class="emojione-{uni}"></i></i>' :128'<i class="emojibtn" role="button" data-name="{name}"><img class="emojioneemoji lazy-emoji" data-src="{img}"/></i>',129true).split('|').join('');130131category.html(items);132$('<h1/>').text(params.title).prependTo(category);133} while (--skin > 0);134});135136options.filters = null;137if (!self.sprite) {138self.lasyEmoji = emojisList.find(".lazy-emoji");139}140141filtersBtns = filters.find(selector("filter"));142filtersBtns.eq(0).addClass("active");143categories = emojisList.find(selector("category"));144145self.recentFilter = filtersBtns.filter('[data-filter="recent"]');146self.recentCategory = categories.filter("[name=recent]");147148self.scrollArea = scrollArea;149150if (options.container) {151$(options.container).wrapInner(app);152} else {153app.insertAfter(source);154}155156if (options.hideSource) {157source.hide();158}159160self.setText(source[sourceValFunc]());161source[sourceValFunc](self.getText());162calcButtonPosition.apply(self);163164// if in standalone mode and no value is set, initialise with a placeholder165if (self.standalone && !self.getText().length) {166var placeholder = $(source).data("emoji-placeholder") || options.emojiPlaceholder;167self.setText(placeholder);168editor.addClass("has-placeholder");169}170171// attach() must be called before any .on() methods !!!172// 1) attach() stores events into possibleEvents{},173// 2) .on() calls bindEvent() and stores handlers into eventStorage{},174// 3) bindEvent() finds events in possibleEvents{} and bind founded via jQuery.on()175// 4) attached events via jQuery.on() calls trigger()176// 5) trigger() calls handlers stored into eventStorage{}177178attach(self, emojisList.find(".emojibtn"), {click: "emojibtn.click"});179attach(self, window, {resize: "!resize"});180attach(self, tones.children(), {click: "tone.click"});181attach(self, [picker, button], {mousedown: "!mousedown"}, editor);182attach(self, button, {click: "button.click"});183attach(self, editor, {paste :"!paste"}, editor);184attach(self, editor, ["focus", "blur"], function() { return self.stayFocused ? false : editor; });185attach(self, picker, {mousedown: "picker.mousedown", mouseup: "picker.mouseup", click: "picker.click",186keyup: "picker.keyup", keydown: "picker.keydown", keypress: "picker.keypress"});187attach(self, editor, ["mousedown", "mouseup", "click", "keyup", "keydown", "keypress"]);188attach(self, picker.find(".emojionearea-filter"), {click: "filter.click"});189190var noListenScroll = false;191scrollArea.on('scroll', function () {192if (!noListenScroll) {193lazyLoading.call(self);194if (scrollArea.is(":not(.skinnable)")) {195var item = categories.eq(0), scrollTop = scrollArea.offset().top;196categories.each(function (i, e) {197if ($(e).offset().top - scrollTop >= 10) {198return false;199}200item = $(e);201});202var filter = filtersBtns.filter('[data-filter="' + item.attr("name") + '"]');203if (filter[0] && !filter.is(".active")) {204filtersBtns.removeClass("active");205filter.addClass("active");206}207}208}209});210211self.on("@filter.click", function(filter) {212var isActive = filter.is(".active");213if (scrollArea.is(".skinnable")) {214if (isActive) return;215tones.children().eq(0).click();216}217noListenScroll = true;218if (!isActive) {219filtersBtns.filter(".active").removeClass("active");220filter.addClass("active");221}222var headerOffset = categories.filter('[name="' + filter.data('filter') + '"]').offset().top,223scroll = scrollArea.scrollTop(),224offsetTop = scrollArea.offset().top;225scrollArea.stop().animate({226scrollTop: headerOffset + scroll - offsetTop - 2227}, 200, 'swing', function () {228lazyLoading.call(self);229noListenScroll = false;230});231})232233.on("@picker.show", function() {234if (self.recentEmojis) {235updateRecent(self);236}237lazyLoading.call(self);238})239240.on("@tone.click", function(tone) {241tones.children().removeClass("active");242var skin = tone.addClass("active").data("skin");243if (skin) {244scrollArea.addClass("skinnable");245categories.hide().filter("[data-tone=" + skin + "]").show();246if (filtersBtns.eq(0).is('.active[data-filter="recent"]')) {247filtersBtns.eq(0).removeClass("active").next().addClass("active");248}249} else {250scrollArea.removeClass("skinnable");251categories.hide().filter("[data-tone=0]").show();252filtersBtns.eq(0).click();253}254lazyLoading.call(self);255})256257.on("@button.click", function(button) {258if (button.is(".active")) {259self.hidePicker();260} else {261self.showPicker();262}263})264265.on("@!paste", function(editor, event) {266267var pasteText = function(text) {268var caretID = "caret-" + (new Date()).getTime();269var html = htmlFromText(text, self);270pasteHtmlAtCaret(html);271pasteHtmlAtCaret('<i id="' + caretID +'"></i>');272editor.scrollTop(editorScrollTop);273var caret = $("#" + caretID),274top = caret.offset().top - editor.offset().top,275height = editor.height();276if (editorScrollTop + top >= height || editorScrollTop > top) {277editor.scrollTop(editorScrollTop + top - 2 * height/3);278}279caret.remove();280self.stayFocused = false;281calcButtonPosition.apply(self);282trigger(self, 'paste', [editor, text, html]);283}284285if (event.originalEvent.clipboardData) {286var text = event.originalEvent.clipboardData.getData('text/plain');287pasteText(text);288289if (event.preventDefault){290event.preventDefault();291} else {292event.stop();293};294295event.returnValue = false;296event.stopPropagation();297return false;298}299300self.stayFocused = true;301// insert invisible character for fix caret position302pasteHtmlAtCaret('<span>' + invisibleChar + '</span>');303304var sel = saveSelection(editor[0]),305editorScrollTop = editor.scrollTop(),306clipboard = $("<div/>", {contenteditable: true})307.css({position: "fixed", left: "-999px", width: "1px", height: "1px", top: "20px", overflow: "hidden"})308.appendTo($("BODY"))309.focus();310311window.setTimeout(function() {312editor.focus();313restoreSelection(editor[0], sel);314var text = textFromHtml(clipboard.html().replace(/\r\n|\n|\r/g, '<br>'), self);315clipboard.remove();316pasteText(text);317}, 200);318})319320.on("@emojibtn.click", function(emojibtn) {321editor.removeClass("has-placeholder");322if (!app.is(".focused")) {323editor.focus();324}325if (self.standalone) {326editor.html(shortnameTo(emojibtn.data("name"), self.emojiTemplate));327self.trigger("blur");328} else {329saveSelection(editor[0]);330pasteHtmlAtCaret(shortnameTo(emojibtn.data("name"), self.emojiTemplate));331}332333if (self.recentEmojis) {334setRecent(self, emojibtn.data("name"));335}336})337338.on("@!resize @keyup @emojibtn.click", calcButtonPosition)339340.on("@!mousedown", function(editor, event) {341if (!app.is(".focused")) {342editor.focus();343}344event.preventDefault();345return false;346})347348.on("@change", function() {349var html = self.editor.html().replace(/<\/?(?:div|span|p)[^>]*>/ig, '');350// clear input: chrome adds <br> when contenteditable is empty351if (!html.length || /^<br[^>]*>$/i.test(html)) {352self.editor.html(self.content = '');353}354source[sourceValFunc](self.getText());355})356357.on("@focus", function() {358app.addClass("focused");359})360361.on("@blur", function() {362app.removeClass("focused");363364if (options.hidePickerOnBlur) {365self.hidePicker();366}367368var content = self.editor.html();369if (self.content !== content) {370self.content = content;371trigger(self, 'change', [self.editor]);372source.blur().trigger("change");373} else {374source.blur();375}376});377378if (options.shortcuts) {379self.on("@keydown", function(_, e) {380if (!e.ctrlKey) {381if (e.which == 9) {382e.preventDefault();383button.click();384}385else if (e.which == 27) {386e.preventDefault();387if (button.is(".active")) {388self.hidePicker();389}390}391}392});393}394395if (isObject(options.events) && !$.isEmptyObject(options.events)) {396$.each(options.events, function(event, handler) {397self.on(event.replace(/_/g, '.'), handler);398});399}400401if (options.autocomplete) {402var autocomplete = function() {403var textcompleteOptions = {404maxCount: options.textcomplete.maxCount,405placement: options.textcomplete.placement406};407408if (options.shortcuts) {409textcompleteOptions.onKeydown = function (e, commands) {410if (!e.ctrlKey && e.which == 13) {411return commands.KEY_ENTER;412}413};414}415416var map = $.map(emojione.emojioneList, function (_, emoji) {417return !options.autocompleteTones ? /_tone[12345]/.test(emoji) ? null : emoji : emoji;418});419map.sort();420editor.textcomplete([421{422id: css_class,423match: /\B(:[\-+\w]*)$/,424search: function (term, callback) {425callback($.map(map, function (emoji) {426return emoji.indexOf(term) === 0 ? emoji : null;427}));428},429template: function (value) {430return shortnameTo(value, self.emojiTemplate) + " " + value.replace(/:/g, '');431},432replace: function (value) {433return shortnameTo(value, self.emojiTemplate);434},435cache: true,436index: 1437}438], textcompleteOptions);439440if (options.textcomplete.placement) {441// Enable correct positioning for textcomplete442if ($(editor.data('textComplete').option.appendTo).css("position") == "static") {443$(editor.data('textComplete').option.appendTo).css("position", "relative");444}445}446};447if ($.fn.textcomplete) {448autocomplete();449} else {450$.getScript("https://cdn.rawgit.com/yuku-t/jquery-textcomplete/v1.3.4/dist/jquery.textcomplete.js",451autocomplete);452}453}454455if (self.inline) {456app.addClass(selector('inline', true));457self.on("@keydown", function(_, e) {458if (e.which == 13) {459e.preventDefault();460}461});462}463464if (/firefox/i.test(navigator.userAgent)) {465// disabling resize images on Firefox466document.execCommand("enableObjectResizing", false, false);467}468469//}, self.id === 1); // calcElapsedTime()470};471});472473