Bug 15794: Add an emoji picker to tag entry in OPAC
This patch introduces an emoji picker int othe OPAC's tag entry form. It relies on the emoji-picker library [1] To test: - Apply the patches - Restart all services (just in case some caching is taking place): $ restart_all - Log into the OPAC - Do a search, pick a record - On the detail page for the record, click on 'Add tags' => SUCCESS: An input form is displayed, with an emoji picker on the right. => SUCCESS: Choosing an emoji populates the input with it - Set 'TagsModeration' and retry => SUCCESS: Proposed tags are displayed correctly on the staff interface for tag moderation - Sign off :-D Sponsored-by: Hotchkiss School [1] https://github.com/OneSignal/emoji-picker Signed-off-by: Mark Tompsett <mtompset@hotmail.com> Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com> Signed-off-by: Jonathan Druart <jonathan.druart@bugs.koha-community.org>
|
@ -108,6 +108,11 @@ $.widget.bridge('uitooltip', $.ui.tooltip);
|
|||
<script type="text/javascript" src="[% interface %]/[% theme %]/js/amazonimages_[% KOHA_VERSION %].js"></script>
|
||||
[% END %]
|
||||
|
||||
<script src="[% interface %]/lib/emoji-picker/js/config.js"></script>
|
||||
<script src="[% interface %]/lib/emoji-picker/js/util.js"></script>
|
||||
<script src="[% interface %]/lib/emoji-picker/js/jquery.emojiarea.js"></script>
|
||||
<script src="[% interface %]/lib/emoji-picker/js/emoji-picker.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
//<![CDATA[
|
||||
var MSG_CONFIRM_AGAIN = _("Warning: Cannot be undone. Please confirm once again")
|
||||
|
@ -252,6 +257,13 @@ $(document).ready(function() {
|
|||
jQuery.removeCookie("search_path_code", { path: '/'});
|
||||
});
|
||||
}
|
||||
|
||||
window.emojiPicker = new EmojiPicker({
|
||||
emojiable_selector: '[data-emojiable=true]',
|
||||
assetsPath: '[% interface %]/lib/emoji-picker/img/',
|
||||
popupButtonClasses: 'fa fa-smile-o'
|
||||
});
|
||||
window.emojiPicker.discover();
|
||||
});
|
||||
</script>
|
||||
[% PROCESS jsinclude %]
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
[% BLOCK cssinclude %]<link rel="stylesheet" type="text/css" href="[% interface %]/[% theme %]/css/jquery.rating_[% KOHA_VERSION %].css" />[% END %]
|
||||
[% END %]
|
||||
[% INCLUDE greybox.inc %]
|
||||
<link href="[% interface %]/lib/emoji-picker/css/emoji.css" rel="stylesheet">
|
||||
</head>
|
||||
[% INCLUDE 'bodytag.inc' bodyid='opac-detail' bodyclass='scrollto' %]
|
||||
[% INCLUDE 'masthead.inc' %]
|
||||
|
@ -411,10 +412,19 @@
|
|||
[% IF ( loggedinusername ) %]
|
||||
<form id="tagform[% biblio.biblionumber %]" method="post" action="/cgi-bin/koha/opac-tags.pl" style="display:none;">
|
||||
<label for="newtag[% biblio.biblionumber %]">New tag(s), separated by a comma:</label>
|
||||
<input name="newtag[% biblio.biblionumber %]" id="newtag[% biblio.biblionumber %]" maxlength="100" type="text"/>
|
||||
<p class="emoji-picker-container">
|
||||
<input
|
||||
name="newtag[% biblio.biblionumber %]"
|
||||
id="newtag[% biblio.biblionumber %]"
|
||||
type="text"
|
||||
maxlength="100"
|
||||
data-emojiable="true"
|
||||
data-emoji-input="unicode">
|
||||
</p>
|
||||
<input name="tagbutton" class="btn btn-small tagbutton" title="[% biblio.biblionumber %]" type="submit" value="Add" />
|
||||
<a class="cancel_tag_add" id="cancel[% biblio.biblionumber %]" href="#">(done)</a>
|
||||
</form>
|
||||
|
||||
<span id="newtag[% biblio.biblionumber %]_status" class="tagstatus" style="display:none;">
|
||||
Tag status here.
|
||||
</span>
|
||||
|
|
251
koha-tmpl/opac-tmpl/lib/emoji-picker/css/emoji.css
Normal file
|
@ -0,0 +1,251 @@
|
|||
span.emoji {
|
||||
display: -moz-inline-box;
|
||||
-moz-box-orient: vertical;
|
||||
display: inline-block;
|
||||
vertical-align: baseline;
|
||||
*vertical-align: auto;
|
||||
*zoom: 1;
|
||||
*display: inline;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
background-size: 1em;
|
||||
background-repeat: no-repeat;
|
||||
text-indent: -9999px;
|
||||
}
|
||||
|
||||
span.emoji-sizer {
|
||||
line-height: 0.81em;
|
||||
font-size: 1em;
|
||||
margin: -2px 0;
|
||||
}
|
||||
|
||||
span.emoji-outer {
|
||||
display: -moz-inline-box;
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
height: 1em;
|
||||
width: 1em;
|
||||
}
|
||||
|
||||
span.emoji-inner {
|
||||
display: -moz-inline-box;
|
||||
display: inline-block;
|
||||
text-indent: -9999px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
vertical-align: baseline;
|
||||
*vertical-align: auto;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
img.emoji {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
}
|
||||
|
||||
.emoji-wysiwyg-editor:empty:before {
|
||||
content: attr(placeholder);
|
||||
color: #9aa2ab;
|
||||
}
|
||||
|
||||
.emoji-picker-container {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.emoji-picker-icon {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 5px;
|
||||
font-size: 20px;
|
||||
opacity: 0.7;
|
||||
z-index: 100;
|
||||
transition: none;
|
||||
color: black;
|
||||
-moz-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-o-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.emoji-picker-icon.parent-has-scroll {
|
||||
right: 28px;
|
||||
}
|
||||
|
||||
.emoji-picker-icon:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Emoji area */
|
||||
.emoji-wysiwyg-editor:empty:before {
|
||||
content: attr(placeholder);
|
||||
color: #9aa2ab;
|
||||
}
|
||||
|
||||
.emoji-wysiwyg-editor:active:before,
|
||||
.emoji-wysiwyg-editor:focus:before {
|
||||
content: none;
|
||||
}
|
||||
|
||||
.emoji-wysiwyg-editor {
|
||||
padding: 6px;
|
||||
padding-right: 35px;
|
||||
margin-bottom: 0px;
|
||||
min-height: 35px;
|
||||
height: 30px;
|
||||
max-height: 284px;
|
||||
overflow: auto;
|
||||
line-height: 17px;
|
||||
border: 1px solid #d2dbe3;
|
||||
border-radius: 2px;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
-webkit-transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
|
||||
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
|
||||
-webkit-user-select: text;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.emoji-wysiwyg-editor.parent-has-scroll {
|
||||
padding-right: 40px;
|
||||
}
|
||||
|
||||
.emoji-wysiwyg-editor.single-line-editor {
|
||||
min-height: 35px;
|
||||
height: inherit;
|
||||
}
|
||||
|
||||
.emoji-wysiwyg-editor img {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
vertical-align: middle;
|
||||
margin: -3px 0 0 0;
|
||||
}
|
||||
|
||||
.emoji-menu {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
z-index: 999;
|
||||
width: 225px;
|
||||
overflow: hidden;
|
||||
border: 1px #dfdfdf solid;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
-webkit-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.1);
|
||||
-moz-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.emoji-items-wrap1 {
|
||||
background: #ffffff;
|
||||
padding: 5px 2px 5px 5px;
|
||||
}
|
||||
|
||||
.emoji-items-wrap1 .emoji-menu-tabs {
|
||||
width: 100%;
|
||||
margin-bottom: 8px;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
.emoji-items-wrap1 .emoji-menu-tabs td {
|
||||
text-align: center;
|
||||
color: white;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
.emoji-menu-tabs .emoji-menu-tab {
|
||||
display: inline-block;
|
||||
width: 24px;
|
||||
height: 29px;
|
||||
background: url('../img/IconsetSmiles.png') no-repeat;
|
||||
background-size: 42px 350px;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.is_1x .emoji-menu-tabs .emoji-menu-tab {
|
||||
background-image: url('../img/IconsetSmiles_1x.png');
|
||||
}
|
||||
|
||||
.emoji-menu-tabs .icon-recent { background-position: -9px -306px; }
|
||||
|
||||
.emoji-menu-tabs .icon-recent-selected { background-position: -9px -277px; }
|
||||
|
||||
.emoji-menu-tabs .icon-smile { background-position: -9px -34px; }
|
||||
|
||||
.emoji-menu-tabs .icon-smile-selected { background-position: -9px -5px; }
|
||||
|
||||
.emoji-menu-tabs .icon-flower { background-position: -9px -145px; }
|
||||
|
||||
.emoji-menu-tabs .icon-flower-selected { background-position: -9px -118px; }
|
||||
|
||||
.emoji-menu-tabs .icon-bell { background-position: -9px -89px; }
|
||||
|
||||
.emoji-menu-tabs .icon-bell-selected { background-position: -9px -61px; }
|
||||
|
||||
.emoji-menu-tabs .icon-car { background-position: -9px -196px; }
|
||||
|
||||
.emoji-menu-tabs .icon-car-selected { background-position: -9px -170px; }
|
||||
|
||||
.emoji-menu-tabs .icon-grid { background-position: -9px -248px; }
|
||||
|
||||
.emoji-menu-tabs .icon-grid-selected { background-position: -9px -222px; }
|
||||
|
||||
.emoji-menu-tabs .icon-smile,
|
||||
.emoji-menu-tabs .icon-flower,
|
||||
.emoji-menu-tabs .icon-bell,
|
||||
.emoji-menu-tabs .icon-car,
|
||||
.emoji-menu-tabs .icon-grid {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.emoji-menu-tabs .icon-smile:hover,
|
||||
.emoji-menu-tabs .icon-flower:hover,
|
||||
.emoji-menu-tabs .icon-bell:hover,
|
||||
.emoji-menu-tabs .icon-car:hover,
|
||||
.emoji-menu-tabs .icon-grid:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.emoji-menu .emoji-items-wrap {
|
||||
position: relative;
|
||||
height: 174px;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.emoji-menu .emoji-items {
|
||||
padding-right: 8px;
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
.emoji-menu img {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
vertical-align: middle;
|
||||
border: 0 none;
|
||||
}
|
||||
|
||||
.emoji-menu .emoji-items a {
|
||||
margin: -1px 0 0 -1px;
|
||||
padding: 5px;
|
||||
display: block;
|
||||
float: left;
|
||||
border-radius: 2px;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.emoji-menu .emoji-items a:hover {
|
||||
background-color: #edf2f5;
|
||||
}
|
||||
|
||||
.emoji-menu:after {
|
||||
content: ' ';
|
||||
display: block;
|
||||
clear: left;
|
||||
}
|
||||
|
||||
.emoji-menu a .label {
|
||||
display: none;
|
||||
}
|
7
koha-tmpl/opac-tmpl/lib/emoji-picker/css/emoji.css.map
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"version": 3,
|
||||
"mappings": "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",
|
||||
"sources": ["emoji.scss"],
|
||||
"names": [],
|
||||
"file": "emoji.css"
|
||||
}
|
BIN
koha-tmpl/opac-tmpl/lib/emoji-picker/img/IconsetSmiles.png
Normal file
After Width: | Height: | Size: 6.8 KiB |
BIN
koha-tmpl/opac-tmpl/lib/emoji-picker/img/IconsetSmiles_1x.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
koha-tmpl/opac-tmpl/lib/emoji-picker/img/IconsetW.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
koha-tmpl/opac-tmpl/lib/emoji-picker/img/IconsetW_1x.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
koha-tmpl/opac-tmpl/lib/emoji-picker/img/blank.gif
Normal file
After Width: | Height: | Size: 49 B |
BIN
koha-tmpl/opac-tmpl/lib/emoji-picker/img/emoji_spritesheet_0.png
Normal file
After Width: | Height: | Size: 722 KiB |
BIN
koha-tmpl/opac-tmpl/lib/emoji-picker/img/emoji_spritesheet_1.png
Normal file
After Width: | Height: | Size: 531 KiB |
BIN
koha-tmpl/opac-tmpl/lib/emoji-picker/img/emoji_spritesheet_2.png
Normal file
After Width: | Height: | Size: 956 KiB |
BIN
koha-tmpl/opac-tmpl/lib/emoji-picker/img/emoji_spritesheet_3.png
Normal file
After Width: | Height: | Size: 402 KiB |
BIN
koha-tmpl/opac-tmpl/lib/emoji-picker/img/emoji_spritesheet_4.png
Normal file
After Width: | Height: | Size: 532 KiB |
3542
koha-tmpl/opac-tmpl/lib/emoji-picker/js/config.js
Normal file
102
koha-tmpl/opac-tmpl/lib/emoji-picker/js/emoji-picker.coffee
Normal file
|
@ -0,0 +1,102 @@
|
|||
class @EmojiPicker
|
||||
# Options:
|
||||
# spriteSheetPath: Path to each category's sprite sheet. Use '!' as a placeholder for the number (see default).
|
||||
# iconSize: The size of each Emoji icon in the picker.
|
||||
# textareaId: The ID to select the textarea that will be converted to a WYSIWYG.
|
||||
# popupElementId: The ID of the element that, when clicked, will display the popup menu.
|
||||
constructor: (options = {}) ->
|
||||
$.emojiarea.iconSize = options.iconSize ? 25;
|
||||
$.emojiarea.assetsPath = options.assetsPath ? '';
|
||||
@generateEmojiIconSets(options)
|
||||
options.emojiable_selector = '[data-emojiable=true]' if !options.emojiable_selector
|
||||
this.options = options;
|
||||
|
||||
discover: ->
|
||||
isiOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
|
||||
if (isiOS)
|
||||
return;
|
||||
# Convert every emojiable field to an emoji area
|
||||
$(this.options.emojiable_selector).emojiarea($.extend({
|
||||
emojiPopup: this,
|
||||
norealTime: true,
|
||||
}, this.options));
|
||||
|
||||
|
||||
generateEmojiIconSets:(options) ->
|
||||
icons = {}
|
||||
reverseIcons = {}
|
||||
i = undefined
|
||||
j = undefined
|
||||
hex = undefined
|
||||
name = undefined
|
||||
dataItem = undefined
|
||||
row = undefined
|
||||
column = undefined
|
||||
totalColumns = undefined
|
||||
j = 0
|
||||
while j < Config.EmojiCategories.length
|
||||
totalColumns = Config.EmojiCategorySpritesheetDimens[j][1]
|
||||
i = 0
|
||||
while i < Config.EmojiCategories[j].length
|
||||
dataItem = Config.Emoji[Config.EmojiCategories[j][i]]
|
||||
name = dataItem[1][0]
|
||||
row = Math.floor(i / totalColumns)
|
||||
column = i % totalColumns
|
||||
icons[':' + name + ':'] = [j, row, column, ':' + name + ':'];
|
||||
reverseIcons[name] = dataItem[0]
|
||||
i++
|
||||
j++
|
||||
|
||||
$.emojiarea.icons = icons;
|
||||
$.emojiarea.reverseIcons = reverseIcons;
|
||||
|
||||
colonToUnicode:(input) ->
|
||||
if !input
|
||||
return ''
|
||||
if !Config.rx_colons
|
||||
Config.init_unified()
|
||||
input.replace Config.rx_colons, (m) ->
|
||||
val = Config.mapcolon[m]
|
||||
if val
|
||||
val
|
||||
else
|
||||
''
|
||||
|
||||
appendUnicodeAsImageToElement:(element, input) ->
|
||||
if !input
|
||||
return ''
|
||||
if !Config.rx_codes
|
||||
Config.init_unified()
|
||||
|
||||
split_on_unicode = input.split(Config.rx_codes)
|
||||
for text in split_on_unicode
|
||||
val = ''
|
||||
if Config.rx_codes.test(text)
|
||||
val = Config.reversemap[text]
|
||||
if val
|
||||
val = ':' + val + ':'
|
||||
val = $.emojiarea.createIcon($.emojiarea.icons[val])
|
||||
else
|
||||
val = document.createTextNode(text)
|
||||
element.append(val)
|
||||
|
||||
input.replace Config.rx_codes, (m) ->
|
||||
val = Config.reversemap[m]
|
||||
if val
|
||||
val = ':' + val + ':'
|
||||
$img = $.emojiarea.createIcon($.emojiarea.icons[val])
|
||||
$img
|
||||
else
|
||||
''
|
||||
|
||||
colonToImage:(input) ->
|
||||
if !input
|
||||
return ''
|
||||
if !Config.rx_colons
|
||||
Config.init_unified()
|
||||
input.replace Config.rx_colons, (m) ->
|
||||
if m
|
||||
$img = $.emojiarea.createIcon($.emojiarea.icons[m])
|
||||
$img
|
||||
else
|
||||
''
|
139
koha-tmpl/opac-tmpl/lib/emoji-picker/js/emoji-picker.js
Normal file
|
@ -0,0 +1,139 @@
|
|||
// Generated by CoffeeScript 1.12.5
|
||||
(function() {
|
||||
this.EmojiPicker = (function() {
|
||||
function EmojiPicker(options) {
|
||||
var ref, ref1;
|
||||
if (options == null) {
|
||||
options = {};
|
||||
}
|
||||
$.emojiarea.iconSize = (ref = options.iconSize) != null ? ref : 25;
|
||||
$.emojiarea.assetsPath = (ref1 = options.assetsPath) != null ? ref1 : '';
|
||||
this.generateEmojiIconSets(options);
|
||||
if (!options.emojiable_selector) {
|
||||
options.emojiable_selector = '[data-emojiable=true]';
|
||||
}
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
EmojiPicker.prototype.discover = function() {
|
||||
var isiOS;
|
||||
isiOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
|
||||
if (isiOS) {
|
||||
return;
|
||||
}
|
||||
return $(this.options.emojiable_selector).emojiarea($.extend({
|
||||
emojiPopup: this,
|
||||
norealTime: true
|
||||
}, this.options));
|
||||
};
|
||||
|
||||
EmojiPicker.prototype.generateEmojiIconSets = function(options) {
|
||||
var column, dataItem, hex, i, icons, j, name, reverseIcons, row, totalColumns;
|
||||
icons = {};
|
||||
reverseIcons = {};
|
||||
i = void 0;
|
||||
j = void 0;
|
||||
hex = void 0;
|
||||
name = void 0;
|
||||
dataItem = void 0;
|
||||
row = void 0;
|
||||
column = void 0;
|
||||
totalColumns = void 0;
|
||||
j = 0;
|
||||
while (j < Config.EmojiCategories.length) {
|
||||
totalColumns = Config.EmojiCategorySpritesheetDimens[j][1];
|
||||
i = 0;
|
||||
while (i < Config.EmojiCategories[j].length) {
|
||||
dataItem = Config.Emoji[Config.EmojiCategories[j][i]];
|
||||
name = dataItem[1][0];
|
||||
row = Math.floor(i / totalColumns);
|
||||
column = i % totalColumns;
|
||||
icons[':' + name + ':'] = [j, row, column, ':' + name + ':'];
|
||||
reverseIcons[name] = dataItem[0];
|
||||
i++;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
$.emojiarea.icons = icons;
|
||||
return $.emojiarea.reverseIcons = reverseIcons;
|
||||
};
|
||||
|
||||
EmojiPicker.prototype.colonToUnicode = function(input) {
|
||||
if (!input) {
|
||||
return '';
|
||||
}
|
||||
if (!Config.rx_colons) {
|
||||
Config.init_unified();
|
||||
}
|
||||
return input.replace(Config.rx_colons, function(m) {
|
||||
var val;
|
||||
val = Config.mapcolon[m];
|
||||
if (val) {
|
||||
return val;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
EmojiPicker.prototype.appendUnicodeAsImageToElement = function(element, input) {
|
||||
var k, len, split_on_unicode, text, val;
|
||||
if (!input) {
|
||||
return '';
|
||||
}
|
||||
if (!Config.rx_codes) {
|
||||
Config.init_unified();
|
||||
}
|
||||
split_on_unicode = input.split(Config.rx_codes);
|
||||
for (k = 0, len = split_on_unicode.length; k < len; k++) {
|
||||
text = split_on_unicode[k];
|
||||
val = '';
|
||||
if (Config.rx_codes.test(text)) {
|
||||
val = Config.reversemap[text];
|
||||
if (val) {
|
||||
val = ':' + val + ':';
|
||||
val = $.emojiarea.createIcon($.emojiarea.icons[val]);
|
||||
}
|
||||
} else {
|
||||
val = document.createTextNode(text);
|
||||
}
|
||||
element.append(val);
|
||||
}
|
||||
return input.replace(Config.rx_codes, function(m) {
|
||||
var $img;
|
||||
val = Config.reversemap[m];
|
||||
if (val) {
|
||||
val = ':' + val + ':';
|
||||
$img = $.emojiarea.createIcon($.emojiarea.icons[val]);
|
||||
return $img;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
EmojiPicker.prototype.colonToImage = function(input) {
|
||||
if (!input) {
|
||||
return '';
|
||||
}
|
||||
if (!Config.rx_colons) {
|
||||
Config.init_unified();
|
||||
}
|
||||
return input.replace(Config.rx_colons, function(m) {
|
||||
var $img;
|
||||
if (m) {
|
||||
$img = $.emojiarea.createIcon($.emojiarea.icons[m]);
|
||||
return $img;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return EmojiPicker;
|
||||
|
||||
})();
|
||||
|
||||
}).call(this);
|
||||
|
||||
//# sourceMappingURL=emoji-picker.js.map
|
10
koha-tmpl/opac-tmpl/lib/emoji-picker/js/emoji-picker.js.map
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"version": 3,
|
||||
"file": "emoji-picker.js",
|
||||
"sourceRoot": "",
|
||||
"sources": [
|
||||
"emoji-picker.coffee"
|
||||
],
|
||||
"names": [],
|
||||
"mappings": "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"
|
||||
}
|
773
koha-tmpl/opac-tmpl/lib/emoji-picker/js/jquery.emojiarea.js
Normal file
|
@ -0,0 +1,773 @@
|
|||
/**
|
||||
* emojiarea - A rich textarea control that supports emojis, WYSIWYG-style.
|
||||
* Copyright (c) 2012 DIY Co
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
|
||||
* file except in compliance with the License. You may obtain a copy of the License at:
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under
|
||||
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
||||
* ANY KIND, either express or implied. See the License for the specific language
|
||||
* governing permissions and limitations under the License.
|
||||
*
|
||||
* @author Brian Reavis <brian@diy.org>
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file also contains some modifications by Igor Zhukov in order to add
|
||||
* custom scrollbars to EmojiMenu See keyword `MODIFICATION` in source code.
|
||||
*/
|
||||
(function($, window, document) {
|
||||
|
||||
var ELEMENT_NODE = 1;
|
||||
var TEXT_NODE = 3;
|
||||
var TAGS_BLOCK = [ 'p', 'div', 'pre', 'form' ];
|
||||
var KEY_ESC = 27;
|
||||
var KEY_TAB = 9;
|
||||
/* Keys that are not intercepted and canceled when the textbox has reached its max length:
|
||||
Backspace, Tab, Ctrl, Alt, Left Arrow, Up Arrow, Right Arrow, Down Arrow, Cmd Key, Delete
|
||||
*/
|
||||
var MAX_LENGTH_ALLOWED_KEYS = [8, 9, 17, 18, 37, 38, 39, 40, 91, 46];
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
/*
|
||||
* ! MODIFICATION START Options 'spritesheetPath', 'spritesheetDimens',
|
||||
* 'iconSize' added by Andre Staltz.
|
||||
*/
|
||||
$.emojiarea = {
|
||||
assetsPath : '',
|
||||
spriteSheetPath: '',
|
||||
blankGifPath: '',
|
||||
iconSize : 25,
|
||||
icons : {},
|
||||
};
|
||||
var defaultRecentEmojis = ':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(',');
|
||||
/* ! MODIFICATION END */
|
||||
|
||||
$.fn.emojiarea = function(options) {
|
||||
options = $.extend({}, options);
|
||||
return this
|
||||
.each(function () {
|
||||
var originalInput = $(this);
|
||||
if ('contentEditable' in document.body
|
||||
&& options.wysiwyg !== false) {
|
||||
var id = getGuid();
|
||||
new EmojiArea_WYSIWYG(originalInput, id, $.extend({}, options));
|
||||
} else {
|
||||
var id = getGuid();
|
||||
new EmojiArea_Plain(originalInput, id, options);
|
||||
}
|
||||
originalInput.attr(
|
||||
{
|
||||
'data-emojiable': 'converted',
|
||||
'data-id': id,
|
||||
'data-type': 'original-input'
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
var util = {};
|
||||
|
||||
util.restoreSelection = (function() {
|
||||
if (window.getSelection) {
|
||||
return function(savedSelection) {
|
||||
var sel = window.getSelection();
|
||||
sel.removeAllRanges();
|
||||
for (var i = 0, len = savedSelection.length; i < len; ++i) {
|
||||
sel.addRange(savedSelection[i]);
|
||||
}
|
||||
};
|
||||
} else if (document.selection && document.selection.createRange) {
|
||||
return function(savedSelection) {
|
||||
if (savedSelection) {
|
||||
savedSelection.select();
|
||||
}
|
||||
};
|
||||
}
|
||||
})();
|
||||
|
||||
util.saveSelection = (function() {
|
||||
if (window.getSelection) {
|
||||
return function() {
|
||||
var sel = window.getSelection(), ranges = [];
|
||||
if (sel.rangeCount) {
|
||||
for (var i = 0, len = sel.rangeCount; i < len; ++i) {
|
||||
ranges.push(sel.getRangeAt(i));
|
||||
}
|
||||
}
|
||||
return ranges;
|
||||
};
|
||||
} else if (document.selection && document.selection.createRange) {
|
||||
return function() {
|
||||
var sel = document.selection;
|
||||
return (sel.type.toLowerCase() !== 'none') ? sel.createRange()
|
||||
: null;
|
||||
};
|
||||
}
|
||||
})();
|
||||
|
||||
util.replaceSelection = (function() {
|
||||
if (window.getSelection) {
|
||||
return function(content) {
|
||||
var range, sel = window.getSelection();
|
||||
var node = typeof content === 'string' ? document
|
||||
.createTextNode(content) : content;
|
||||
if (sel.getRangeAt && sel.rangeCount) {
|
||||
range = sel.getRangeAt(0);
|
||||
range.deleteContents();
|
||||
//range.insertNode(document.createTextNode(''));
|
||||
range.insertNode(node);
|
||||
range.setStart(node, 0);
|
||||
|
||||
window.setTimeout(function() {
|
||||
range = document.createRange();
|
||||
range.setStartAfter(node);
|
||||
range.collapse(true);
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
} else if (document.selection && document.selection.createRange) {
|
||||
return function(content) {
|
||||
var range = document.selection.createRange();
|
||||
if (typeof content === 'string') {
|
||||
range.text = content;
|
||||
} else {
|
||||
range.pasteHTML(content.outerHTML);
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
util.insertAtCursor = function(text, el) {
|
||||
text = ' ' + text;
|
||||
var val = el.value, endIndex, startIndex, range;
|
||||
if (typeof el.selectionStart != 'undefined'
|
||||
&& typeof el.selectionEnd != 'undefined') {
|
||||
startIndex = el.selectionStart;
|
||||
endIndex = el.selectionEnd;
|
||||
el.value = val.substring(0, startIndex) + text
|
||||
+ val.substring(el.selectionEnd);
|
||||
el.selectionStart = el.selectionEnd = startIndex + text.length;
|
||||
} else if (typeof document.selection != 'undefined'
|
||||
&& typeof document.selection.createRange != 'undefined') {
|
||||
el.focus();
|
||||
range = document.selection.createRange();
|
||||
range.text = text;
|
||||
range.select();
|
||||
}
|
||||
};
|
||||
|
||||
util.extend = function(a, b) {
|
||||
if (typeof a === 'undefined' || !a) {
|
||||
a = {};
|
||||
}
|
||||
if (typeof b === 'object') {
|
||||
for ( var key in b) {
|
||||
if (b.hasOwnProperty(key)) {
|
||||
a[key] = b[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return a;
|
||||
};
|
||||
|
||||
util.escapeRegex = function(str) {
|
||||
return (str + '').replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
|
||||
};
|
||||
|
||||
util.htmlEntities = function(str) {
|
||||
return String(str).replace(/&/g, '&').replace(/</g, '<')
|
||||
.replace(/>/g, '>').replace(/"/g, '"');
|
||||
};
|
||||
|
||||
/*
|
||||
* ! MODIFICATION START This function was added by Igor Zhukov to save
|
||||
* recent used emojis.
|
||||
*/
|
||||
util.emojiInserted = function(emojiKey, menu) {
|
||||
ConfigStorage.get('emojis_recent', function(curEmojis) {
|
||||
curEmojis = curEmojis || defaultRecentEmojis || [];
|
||||
|
||||
var pos = curEmojis.indexOf(emojiKey);
|
||||
if (!pos) {
|
||||
return false;
|
||||
}
|
||||
if (pos != -1) {
|
||||
curEmojis.splice(pos, 1);
|
||||
}
|
||||
curEmojis.unshift(emojiKey);
|
||||
if (curEmojis.length > 42) {
|
||||
curEmojis = curEmojis.slice(42);
|
||||
}
|
||||
|
||||
ConfigStorage.set({
|
||||
emojis_recent : curEmojis
|
||||
});
|
||||
})
|
||||
};
|
||||
/* ! MODIFICATION END */
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
var EmojiArea = function() {
|
||||
};
|
||||
|
||||
EmojiArea.prototype.setup = function() {
|
||||
var self = this;
|
||||
|
||||
this.$editor.on('focus', function() {
|
||||
self.hasFocus = true;
|
||||
});
|
||||
this.$editor.on('blur', function() {
|
||||
self.hasFocus = false;
|
||||
});
|
||||
|
||||
// Assign a unique instance of an emojiMenu to
|
||||
self.emojiMenu = new EmojiMenu(self);
|
||||
|
||||
this.setupButton();
|
||||
};
|
||||
|
||||
EmojiArea.prototype.setupButton = function() {
|
||||
var self = this;
|
||||
var $button = $('[data-id=' + this.id + '][data-type=picker]');
|
||||
|
||||
$button.on('click', function(e) {
|
||||
self.emojiMenu.show(self);
|
||||
});
|
||||
|
||||
this.$button = $button;
|
||||
this.$dontHideOnClick = 'emoji-picker';
|
||||
};
|
||||
|
||||
/*
|
||||
* ! MODIFICATION START This function was modified by Andre Staltz so that
|
||||
* the icon is created from a spritesheet.
|
||||
*/
|
||||
EmojiArea.createIcon = function(emoji, menu) {
|
||||
var category = emoji[0];
|
||||
var row = emoji[1];
|
||||
var column = emoji[2];
|
||||
var name = emoji[3];
|
||||
var filename = $.emojiarea.spriteSheetPath ? $.emojiarea.spriteSheetPath : $.emojiarea.assetsPath + '/emoji_spritesheet_!.png';
|
||||
var blankGifPath = $.emojiarea.blankGifPath ? $.emojiarea.blankGifPath : $.emojiarea.assetsPath + '/blank.gif';
|
||||
var iconSize = menu && Config.Mobile ? 26 : $.emojiarea.iconSize
|
||||
var xoffset = -(iconSize * column);
|
||||
var yoffset = -(iconSize * row);
|
||||
var scaledWidth = (Config.EmojiCategorySpritesheetDimens[category][1] * iconSize);
|
||||
var scaledHeight = (Config.EmojiCategorySpritesheetDimens[category][0] * iconSize);
|
||||
|
||||
var style = 'display:inline-block;';
|
||||
style += 'width:' + iconSize + 'px;';
|
||||
style += 'height:' + iconSize + 'px;';
|
||||
style += 'background:url(\'' + filename.replace('!', category) + '\') '
|
||||
+ xoffset + 'px ' + yoffset + 'px no-repeat;';
|
||||
style += 'background-size:' + scaledWidth + 'px ' + scaledHeight
|
||||
+ 'px;';
|
||||
return '<img src="' + blankGifPath + '" class="img" style="'
|
||||
+ style + '" alt="' + util.htmlEntities(name) + '">';
|
||||
};
|
||||
|
||||
$.emojiarea.createIcon = EmojiArea.createIcon;
|
||||
/* ! MODIFICATION END */
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
/**
|
||||
* Editor (plain-text)
|
||||
*
|
||||
* @constructor
|
||||
* @param {object}
|
||||
* $textarea
|
||||
* @param {object}
|
||||
* options
|
||||
*/
|
||||
|
||||
var EmojiArea_Plain = function($textarea, id, options) {
|
||||
this.options = options;
|
||||
this.$textarea = $textarea;
|
||||
this.$editor = $textarea;
|
||||
this.id = id;
|
||||
this.setup();
|
||||
};
|
||||
|
||||
EmojiArea_Plain.prototype.insert = function(emoji) {
|
||||
if (!$.emojiarea.icons.hasOwnProperty(emoji))
|
||||
return;
|
||||
util.insertAtCursor(emoji, this.$textarea[0]);
|
||||
/*
|
||||
* MODIFICATION: Following line was added by Igor Zhukov, in order to
|
||||
* save recent emojis
|
||||
*/
|
||||
util.emojiInserted(emoji, this.menu);
|
||||
this.$textarea.trigger('change');
|
||||
};
|
||||
|
||||
EmojiArea_Plain.prototype.val = function() {
|
||||
if (this.$textarea == '\n')
|
||||
return '';
|
||||
return this.$textarea.val();
|
||||
};
|
||||
|
||||
util.extend(EmojiArea_Plain.prototype, EmojiArea.prototype);
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
/**
|
||||
* Editor (rich)
|
||||
*
|
||||
* @constructor
|
||||
* @param {object}
|
||||
* $textarea
|
||||
* @param {object}
|
||||
* options
|
||||
*/
|
||||
|
||||
var EmojiArea_WYSIWYG = function($textarea, id, options) {
|
||||
var self = this;
|
||||
|
||||
this.options = options || {};
|
||||
if ($($textarea).attr('data-emoji-input') === 'unicode')
|
||||
this.options.inputMethod = 'unicode';
|
||||
else
|
||||
this.options.inputMethod = 'image';
|
||||
this.id = id;
|
||||
this.$textarea = $textarea;
|
||||
this.emojiPopup = options.emojiPopup;
|
||||
this.$editor = $('<div>').addClass('emoji-wysiwyg-editor').addClass($($textarea)[0].className);
|
||||
this.$editor.data('self', this);
|
||||
|
||||
if ($textarea.attr('maxlength')) {
|
||||
this.$editor.attr('maxlength', $textarea.attr('maxlength'));
|
||||
}
|
||||
this.$editor.height($textarea.outerHeight()); //auto adjust height
|
||||
this.emojiPopup.appendUnicodeAsImageToElement(this.$editor, $textarea.val());
|
||||
|
||||
this.$editor.attr({
|
||||
'data-id': id,
|
||||
'data-type': 'input',
|
||||
'placeholder': $textarea.attr('placeholder'),
|
||||
'contenteditable': 'true',
|
||||
});
|
||||
|
||||
/*
|
||||
* ! MODIFICATION START Following code was modified by Igor Zhukov, in
|
||||
* order to improve rich text paste
|
||||
*/
|
||||
var changeEvents = 'blur change';
|
||||
if (!this.options.norealTime) {
|
||||
changeEvents += ' keyup';
|
||||
}
|
||||
this.$editor.on(changeEvents, function(e) {
|
||||
return self.onChange.apply(self, [ e ]);
|
||||
});
|
||||
/* ! MODIFICATION END */
|
||||
|
||||
this.$editor.on('mousedown focus', function() {
|
||||
document.execCommand('enableObjectResizing', false, false);
|
||||
});
|
||||
this.$editor.on('blur', function() {
|
||||
document.execCommand('enableObjectResizing', true, true);
|
||||
});
|
||||
|
||||
var editorDiv = this.$editor;
|
||||
this.$editor.on("change keydown keyup resize scroll", function(e) {
|
||||
if(MAX_LENGTH_ALLOWED_KEYS.indexOf(e.which) == -1 &&
|
||||
!((e.ctrlKey || e.metaKey) && e.which == 65) && // Ctrl + A
|
||||
!((e.ctrlKey || e.metaKey) && e.which == 67) && // Ctrl + C
|
||||
editorDiv.text().length + editorDiv.find('img').length >= editorDiv.attr('maxlength'))
|
||||
{
|
||||
e.preventDefault();
|
||||
}
|
||||
self.updateBodyPadding(editorDiv);
|
||||
});
|
||||
|
||||
this.$editor.on("paste", function (e) {
|
||||
e.preventDefault();
|
||||
var content;
|
||||
var charsRemaining = editorDiv.attr('maxlength') - (editorDiv.text().length + editorDiv.find('img').length);
|
||||
if ((e.originalEvent || e).clipboardData) {
|
||||
content = (e.originalEvent || e).clipboardData.getData('text/plain');
|
||||
if (self.options.onPaste) {
|
||||
content = self.options.onPaste(content);
|
||||
}
|
||||
if (charsRemaining < content.length) {
|
||||
content = content.substring(0, charsRemaining);
|
||||
}
|
||||
document.execCommand('insertText', false, content);
|
||||
}
|
||||
else if (window.clipboardData) {
|
||||
content = window.clipboardData.getData('Text');
|
||||
if (self.options.onPaste) {
|
||||
content = self.options.onPaste(content);
|
||||
}
|
||||
if (charsRemaining < content.length) {
|
||||
content = content.substring(0, charsRemaining);
|
||||
}
|
||||
document.selection.createRange().pasteHTML(content);
|
||||
}
|
||||
editorDiv.scrollTop(editorDiv[0].scrollHeight);
|
||||
});
|
||||
|
||||
$textarea.after("<i class='emoji-picker-icon emoji-picker " + this.options.popupButtonClasses + "' data-id='" + id + "' data-type='picker'></i>");
|
||||
|
||||
$textarea.hide().after(this.$editor);
|
||||
this.setup();
|
||||
|
||||
/*
|
||||
* MODIFICATION: Following line was modified by Igor Zhukov, in order to
|
||||
* improve emoji insert behaviour
|
||||
*/
|
||||
$(document.body).on('mousedown', function() {
|
||||
if (self.hasFocus) {
|
||||
self.selection = util.saveSelection();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
EmojiArea_WYSIWYG.prototype.updateBodyPadding = function(target) {
|
||||
var emojiPicker = $('[data-id=' + this.id + '][data-type=picker]');
|
||||
if ($(target).hasScrollbar()) {
|
||||
if (!(emojiPicker.hasClass('parent-has-scroll')))
|
||||
emojiPicker.addClass('parent-has-scroll');
|
||||
if (!($(target).hasClass('parent-has-scroll')))
|
||||
$(target).addClass('parent-has-scroll');
|
||||
} else {
|
||||
if ((emojiPicker.hasClass('parent-has-scroll')))
|
||||
emojiPicker.removeClass('parent-has-scroll');
|
||||
if (($(target).hasClass('parent-has-scroll')))
|
||||
$(target).removeClass('parent-has-scroll');
|
||||
}
|
||||
};
|
||||
|
||||
EmojiArea_WYSIWYG.prototype.onChange = function(e) {
|
||||
var event = new CustomEvent('input', { bubbles: true });
|
||||
this.$textarea.val(this.val())[0].dispatchEvent(event);
|
||||
};
|
||||
|
||||
EmojiArea_WYSIWYG.prototype.insert = function(emoji) {
|
||||
var content;
|
||||
/*
|
||||
* MODIFICATION: Following line was modified by Andre Staltz, to use new
|
||||
* implementation of createIcon function.
|
||||
*/
|
||||
var insertionContent = '';
|
||||
if (this.options.inputMethod == 'unicode') {
|
||||
insertionContent = this.emojiPopup.colonToUnicode(emoji);
|
||||
} else {
|
||||
var $img = $(EmojiArea.createIcon($.emojiarea.icons[emoji]));
|
||||
if ($img[0].attachEvent) {
|
||||
$img[0].attachEvent('onresizestart', function(e) {
|
||||
e.returnValue = false;
|
||||
}, false);
|
||||
}
|
||||
insertionContent = $img[0];
|
||||
}
|
||||
|
||||
this.$editor.trigger('focus');
|
||||
if (this.selection) {
|
||||
util.restoreSelection(this.selection);
|
||||
}
|
||||
try {
|
||||
util.replaceSelection(insertionContent);
|
||||
} catch (e) {
|
||||
}
|
||||
|
||||
/*
|
||||
* MODIFICATION: Following line was added by Igor Zhukov, in order to
|
||||
* save recent emojis
|
||||
*/
|
||||
util.emojiInserted(emoji, this.menu);
|
||||
|
||||
this.onChange();
|
||||
};
|
||||
|
||||
EmojiArea_WYSIWYG.prototype.val = function() {
|
||||
var lines = [];
|
||||
var line = [];
|
||||
var emojiPopup = this.emojiPopup;
|
||||
|
||||
var flush = function() {
|
||||
lines.push(line.join(''));
|
||||
line = [];
|
||||
};
|
||||
|
||||
var sanitizeNode = function(node) {
|
||||
if (node.nodeType === TEXT_NODE) {
|
||||
line.push(node.nodeValue);
|
||||
} else if (node.nodeType === ELEMENT_NODE) {
|
||||
var tagName = node.tagName.toLowerCase();
|
||||
var isBlock = TAGS_BLOCK.indexOf(tagName) !== -1;
|
||||
|
||||
if (isBlock && line.length)
|
||||
flush();
|
||||
|
||||
if (tagName === 'img') {
|
||||
var alt = node.getAttribute('alt') || '';
|
||||
if (alt) {
|
||||
line.push(alt);
|
||||
}
|
||||
return;
|
||||
} else if (tagName === 'br') {
|
||||
flush();
|
||||
}
|
||||
|
||||
var children = node.childNodes;
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
sanitizeNode(children[i]);
|
||||
}
|
||||
|
||||
if (isBlock && line.length)
|
||||
flush();
|
||||
}
|
||||
};
|
||||
|
||||
var children = this.$editor[0].childNodes;
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
sanitizeNode(children[i]);
|
||||
}
|
||||
|
||||
if (line.length)
|
||||
flush();
|
||||
|
||||
var returnValue = lines.join('\n');
|
||||
return emojiPopup.colonToUnicode(returnValue);
|
||||
};
|
||||
|
||||
util.extend(EmojiArea_WYSIWYG.prototype, EmojiArea.prototype);
|
||||
|
||||
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
jQuery.fn.hasScrollbar = function() {
|
||||
var scrollHeight = this.get(0).scrollHeight;
|
||||
|
||||
//safari's scrollHeight includes padding
|
||||
//if ($.browser.safari)
|
||||
// scrollHeight -= parseInt(this.css('padding-top')) + parseInt(this.css('padding-bottom'));
|
||||
if (this.outerHeight() < scrollHeight)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Emoji Dropdown Menu
|
||||
*
|
||||
* @constructor
|
||||
* @param {object}
|
||||
* emojiarea
|
||||
*/
|
||||
var EmojiMenu = function(emojiarea) {
|
||||
var self = this;
|
||||
self.id = emojiarea.id;
|
||||
var $body = $(document.body);
|
||||
var $window = $(window);
|
||||
|
||||
this.visible = false;
|
||||
this.emojiarea = emojiarea;
|
||||
EmojiMenu.menuZIndex = 5000;
|
||||
this.$menu = $('<div>');
|
||||
this.$menu.addClass('emoji-menu');
|
||||
this.$menu.attr('data-id', self.id);
|
||||
this.$menu.attr('data-type', 'menu');
|
||||
this.$menu.hide();
|
||||
|
||||
this.$itemsTailWrap = $('<div class="emoji-items-wrap1"></div>')
|
||||
.appendTo(this.$menu);
|
||||
this.$categoryTabs = $(
|
||||
'<table class="emoji-menu-tabs"><tr>'
|
||||
+ '<td><a class="emoji-menu-tab icon-recent" ></a></td>'
|
||||
+ '<td><a class="emoji-menu-tab icon-smile" ></a></td>'
|
||||
+ '<td><a class="emoji-menu-tab icon-flower"></a></td>'
|
||||
+ '<td><a class="emoji-menu-tab icon-bell"></a></td>'
|
||||
+ '<td><a class="emoji-menu-tab icon-car"></a></td>'
|
||||
+ '<td><a class="emoji-menu-tab icon-grid"></a></td>'
|
||||
+ '</tr></table>').appendTo(this.$itemsTailWrap);
|
||||
this.$itemsWrap = $(
|
||||
'<div class="emoji-items-wrap mobile_scrollable_wrap"></div>')
|
||||
.appendTo(this.$itemsTailWrap);
|
||||
this.$items = $('<div class="emoji-items">').appendTo(
|
||||
this.$itemsWrap);
|
||||
|
||||
this.emojiarea.$editor.after(this.$menu)
|
||||
|
||||
$body.on('keydown', function(e) {
|
||||
if (e.keyCode === KEY_ESC || e.keyCode === KEY_TAB) {
|
||||
self.hide();
|
||||
}
|
||||
});
|
||||
|
||||
/*
|
||||
* ! MODIFICATION: Following 3 lines were added by Igor Zhukov, in order
|
||||
* to hide menu on message submit with keyboard
|
||||
*/
|
||||
$body.on('message_send', function(e) {
|
||||
self.hide();
|
||||
});
|
||||
|
||||
$body.on('mouseup', function(e) {
|
||||
e = e.originalEvent || e;
|
||||
var target = e.target || window;
|
||||
|
||||
if ($(target).hasClass(self.emojiarea.$dontHideOnClick)) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (target && target != window) {
|
||||
target = target.parentNode;
|
||||
if (target == self.$menu[0] || self.emojiarea
|
||||
&& target == self.emojiarea.$button[0]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
self.hide();
|
||||
});
|
||||
|
||||
this.$menu.on('mouseup', 'a', function(e) {
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
});
|
||||
|
||||
this.$menu.on('click', 'a', function(e) {
|
||||
|
||||
self.emojiarea.updateBodyPadding(self.emojiarea.$editor);
|
||||
if ($(this).hasClass('emoji-menu-tab')) {
|
||||
if (self.getTabIndex(this) !== self.currentCategory) {
|
||||
self.selectCategory(self.getTabIndex(this));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
var emoji = $('.label', $(this)).text();
|
||||
window.setTimeout(function() {
|
||||
self.onItemSelected(emoji);
|
||||
if (e.ctrlKey || e.metaKey) {
|
||||
self.hide();
|
||||
}
|
||||
}, 0);
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
});
|
||||
|
||||
this.selectCategory(0);
|
||||
};
|
||||
|
||||
/*
|
||||
* ! MODIFICATION START Following code was added by Andre Staltz, to
|
||||
* implement category selection.
|
||||
*/
|
||||
EmojiMenu.prototype.getTabIndex = function(tab) {
|
||||
return this.$categoryTabs.find('.emoji-menu-tab').index(tab);
|
||||
};
|
||||
|
||||
EmojiMenu.prototype.selectCategory = function(category) {
|
||||
var self = this;
|
||||
this.$categoryTabs.find('.emoji-menu-tab').each(function(index) {
|
||||
if (index === category) {
|
||||
this.className += '-selected';
|
||||
} else {
|
||||
this.className = this.className.replace('-selected', '');
|
||||
}
|
||||
});
|
||||
this.currentCategory = category;
|
||||
this.load(category);
|
||||
};
|
||||
/* ! MODIFICATION END */
|
||||
|
||||
EmojiMenu.prototype.onItemSelected = function(emoji) {
|
||||
if(this.emojiarea.$editor.text().length + this.emojiarea.$editor.find('img').length >= this.emojiarea.$editor.attr('maxlength'))
|
||||
{
|
||||
return;
|
||||
}
|
||||
this.emojiarea.insert(emoji);
|
||||
};
|
||||
|
||||
/*
|
||||
* MODIFICATION: The following function argument was modified by Andre
|
||||
* Staltz, in order to load only icons from a category. Also function was
|
||||
* modified by Igor Zhukov in order to display recent emojis from
|
||||
* localStorage
|
||||
*/
|
||||
EmojiMenu.prototype.load = function(category) {
|
||||
var html = [];
|
||||
var options = $.emojiarea.icons;
|
||||
var path = $.emojiarea.assetsPath;
|
||||
var self = this;
|
||||
if (path.length && path.charAt(path.length - 1) !== '/') {
|
||||
path += '/';
|
||||
}
|
||||
|
||||
/*
|
||||
* ! MODIFICATION: Following function was added by Igor Zhukov, in order
|
||||
* to add scrollbars to EmojiMenu
|
||||
*/
|
||||
var updateItems = function() {
|
||||
self.$items.html(html.join(''));
|
||||
}
|
||||
|
||||
if (category > 0) {
|
||||
for ( var key in options) {
|
||||
/*
|
||||
* MODIFICATION: The following 2 lines were modified by Andre
|
||||
* Staltz, in order to load only icons from the specified
|
||||
* category.
|
||||
*/
|
||||
if (options.hasOwnProperty(key)
|
||||
&& options[key][0] === (category - 1)) {
|
||||
html.push('<a href="javascript:void(0)" title="'
|
||||
+ util.htmlEntities(key) + '">'
|
||||
+ EmojiArea.createIcon(options[key], true)
|
||||
+ '<span class="label">' + util.htmlEntities(key)
|
||||
+ '</span></a>');
|
||||
}
|
||||
}
|
||||
updateItems();
|
||||
} else {
|
||||
ConfigStorage.get('emojis_recent', function(curEmojis) {
|
||||
curEmojis = curEmojis || defaultRecentEmojis || [];
|
||||
var key, i;
|
||||
for (i = 0; i < curEmojis.length; i++) {
|
||||
key = curEmojis[i]
|
||||
if (options[key]) {
|
||||
html.push('<a href="javascript:void(0)" title="'
|
||||
+ util.htmlEntities(key) + '">'
|
||||
+ EmojiArea.createIcon(options[key], true)
|
||||
+ '<span class="label">'
|
||||
+ util.htmlEntities(key) + '</span></a>');
|
||||
}
|
||||
}
|
||||
updateItems();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
EmojiMenu.prototype.hide = function(callback) {
|
||||
this.visible = false;
|
||||
this.$menu.hide("fast");
|
||||
};
|
||||
|
||||
EmojiMenu.prototype.show = function(emojiarea) {
|
||||
/*
|
||||
* MODIFICATION: Following line was modified by Igor Zhukov, in order to
|
||||
* improve EmojiMenu behaviour
|
||||
*/
|
||||
if (this.visible)
|
||||
return this.hide();
|
||||
$(this.$menu).css('z-index', ++EmojiMenu.menuZIndex);
|
||||
this.$menu.show("fast");
|
||||
/*
|
||||
* MODIFICATION: Following 3 lines were added by Igor Zhukov, in order
|
||||
* to update EmojiMenu contents
|
||||
*/
|
||||
if (!this.currentCategory) {
|
||||
this.load(0);
|
||||
}
|
||||
this.visible = true;
|
||||
};
|
||||
|
||||
})(jQuery, window, document);
|
225
koha-tmpl/opac-tmpl/lib/emoji-picker/js/util.js
Normal file
|
@ -0,0 +1,225 @@
|
|||
'use strict';
|
||||
|
||||
function cancelEvent (event) {
|
||||
event = event || window.event;
|
||||
if (event) {
|
||||
event = event.originalEvent || event;
|
||||
|
||||
if (event.stopPropagation) event.stopPropagation();
|
||||
if (event.preventDefault) event.preventDefault();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function getGuid() {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
//ConfigStorage
|
||||
(function(window)
|
||||
{
|
||||
var keyPrefix = '';
|
||||
var noPrefix = false;
|
||||
var cache = {};
|
||||
var useCs = !!(window.chrome && chrome.storage && chrome.storage.local);
|
||||
var useLs = !useCs && !!window.localStorage;
|
||||
|
||||
function storageSetPrefix(newPrefix)
|
||||
{
|
||||
keyPrefix = newPrefix;
|
||||
}
|
||||
|
||||
function storageSetNoPrefix()
|
||||
{
|
||||
noPrefix = true;
|
||||
}
|
||||
|
||||
function storageGetPrefix()
|
||||
{
|
||||
if (noPrefix)
|
||||
{
|
||||
noPrefix = false;
|
||||
return '';
|
||||
}
|
||||
return keyPrefix;
|
||||
}
|
||||
|
||||
function storageGetValue()
|
||||
{
|
||||
var keys = Array.prototype.slice.call(arguments),
|
||||
callback = keys.pop(),
|
||||
result = [],
|
||||
single = keys.length == 1,
|
||||
value,
|
||||
allFound = true,
|
||||
prefix = storageGetPrefix(),
|
||||
i, key;
|
||||
|
||||
for (i = 0; i < keys.length; i++)
|
||||
{
|
||||
key = keys[i] = prefix + keys[i];
|
||||
if (key.substr(0, 3) != 'xt_' && cache[key] !== undefined)
|
||||
{
|
||||
result.push(cache[key]);
|
||||
}
|
||||
else if (useLs)
|
||||
{
|
||||
try
|
||||
{
|
||||
value = localStorage.getItem(key);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
useLs = false;
|
||||
}
|
||||
try
|
||||
{
|
||||
value = (value === undefined || value === null) ? false : JSON.parse(value);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
value = false;
|
||||
}
|
||||
result.push(cache[key] = value);
|
||||
}
|
||||
else if (!useCs)
|
||||
{
|
||||
result.push(cache[key] = false);
|
||||
}
|
||||
else
|
||||
{
|
||||
allFound = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (allFound)
|
||||
{
|
||||
return callback(single ? result[0] : result);
|
||||
}
|
||||
|
||||
chrome.storage.local.get(keys, function(resultObj)
|
||||
{
|
||||
var value;
|
||||
result = [];
|
||||
for (i = 0; i < keys.length; i++)
|
||||
{
|
||||
key = keys[i];
|
||||
value = resultObj[key];
|
||||
value = value === undefined || value === null ? false : JSON.parse(value);
|
||||
result.push(cache[key] = value);
|
||||
}
|
||||
|
||||
callback(single ? result[0] : result);
|
||||
});
|
||||
};
|
||||
|
||||
function storageSetValue(obj, callback)
|
||||
{
|
||||
var keyValues = {},
|
||||
prefix = storageGetPrefix(),
|
||||
key, value;
|
||||
|
||||
for (key in obj)
|
||||
{
|
||||
if (obj.hasOwnProperty(key))
|
||||
{
|
||||
value = obj[key];
|
||||
key = prefix + key;
|
||||
cache[key] = value;
|
||||
value = JSON.stringify(value);
|
||||
if (useLs)
|
||||
{
|
||||
try
|
||||
{
|
||||
localStorage.setItem(key, value);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
useLs = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
keyValues[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (useLs || !useCs)
|
||||
{
|
||||
if (callback)
|
||||
{
|
||||
callback();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
chrome.storage.local.set(keyValues, callback);
|
||||
};
|
||||
|
||||
function storageRemoveValue()
|
||||
{
|
||||
var keys = Array.prototype.slice.call(arguments),
|
||||
prefix = storageGetPrefix(),
|
||||
i, key, callback;
|
||||
|
||||
if (typeof keys[keys.length - 1] === 'function')
|
||||
{
|
||||
callback = keys.pop();
|
||||
}
|
||||
|
||||
for (i = 0; i < keys.length; i++)
|
||||
{
|
||||
key = keys[i] = prefix + keys[i];
|
||||
delete cache[key];
|
||||
if (useLs)
|
||||
{
|
||||
try
|
||||
{
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
useLs = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (useCs)
|
||||
{
|
||||
chrome.storage.local.remove(keys, callback);
|
||||
}
|
||||
else if (callback)
|
||||
{
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
window.ConfigStorage = {
|
||||
prefix: storageSetPrefix,
|
||||
noPrefix: storageSetNoPrefix,
|
||||
get: storageGetValue,
|
||||
set: storageSetValue,
|
||||
remove: storageRemoveValue
|
||||
};
|
||||
})(this);
|
||||
|
||||
// Pollyfill for IE 9 support of CustomEvent
|
||||
(function () {
|
||||
|
||||
if ( typeof window.CustomEvent === "function" ) return false;
|
||||
|
||||
function CustomEvent ( event, params ) {
|
||||
params = params || { bubbles: false, cancelable: false, detail: undefined };
|
||||
var evt = document.createEvent( 'CustomEvent' );
|
||||
evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
|
||||
return evt;
|
||||
}
|
||||
|
||||
CustomEvent.prototype = window.Event.prototype;
|
||||
|
||||
window.CustomEvent = CustomEvent;
|
||||
})();
|