Browse Source

add emoji, pw level, db address maybe change

lonelysheep9 5 years ago
parent
commit
d6bf82c460

BIN
src/img/IconsetSmiles.png


BIN
src/img/IconsetSmiles_1x.png


BIN
src/img/IconsetW.png


BIN
src/img/IconsetW_1x.png


BIN
src/img/blank.gif


BIN
src/img/emoji_spritesheet_0.png


BIN
src/img/emoji_spritesheet_1.png


BIN
src/img/emoji_spritesheet_2.png


BIN
src/img/emoji_spritesheet_3.png


BIN
src/img/emoji_spritesheet_4.png


+ 45 - 3
src/index.php

@@ -164,6 +164,20 @@ setInterval(refresh,1000);
 	width:305px !important;
 }
 </style>
+    
+    
+<meta http-equiv="X-UA-Compatible" content="IE=edge">
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<meta name="description" content="">
+<meta name="author" content="">
+<link rel="shortcut icon" href="favicon.png">
+    
+<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" integrity="sha384-rwoIResjU2yc3z8GV/NPeZWAv56rSmLldC3R/AZzGRnGxQQKnKkoFVhFQhNUwEyJ" crossorigin="anonymous">
+<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
+
+<!-- Begin emoji-picker Stylesheets -->
+<link href="script/css/emoji.css" rel="stylesheet">
+<!-- End emoji-picker Stylesheets -->
 </head>
 <body>
 <div class="ts tabbed menu extrapadding">
@@ -262,19 +276,47 @@ setInterval(refresh,1000);
 -->
 			</div>
 		</div>
+    
+    
     <div class="twelve wide column">
 		<div class="ts text container">
-		<div class="ts segment">
+		  <div class="ts segment">
 			<div class="ts form">
 				<form name="message" action="updateSql.php" method="post">
 				<div class="field">
 					<label>Share Post</label>
-					<textarea rows="4" placeholder="Want to share something?" name="message" cols="40"></textarea>
+                    <p class="lead emoji-picker-container">
+					<textarea rows="4" placeholder="Want to share something?" name="message" cols="40" data-emojiable="true"></textarea>
+                    </p>
 				</div>
 				<input type="submit" class="ts primary fluid button" value="Post">
 				</form>
 			</div>
 		</div>
+            
+       
+    <!-- emoji-picker JavaScript -->
+    <script src="https://code.jquery.com/jquery-1.11.3.min.js"></script>
+    <script src="script/js/config.js"></script>
+    <script src="script/js/util.js"></script>
+    <script src="script/js/jquery.emojiarea.js"></script>
+    <script src="script/js/emoji-picker.js"></script>
+    <!-- emoji-picker JavaScript -->
+            
+    <script>
+      $(function() {
+        // Initializes and creates emoji set from sprite sheet
+        window.emojiPicker = new EmojiPicker({
+          emojiable_selector: '[data-emojiable=true]',
+          assetsPath: 'img/',
+          popupButtonClasses: 'fa fa-smile-o'
+        });
+        window.emojiPicker.discover();
+      });
+    </script>
+            
+
+            
 		<?php
 			 //include_once("requestDB.php"); //Handle all Database access, see the query command below
 			$postData = query("select * from post where username='" . $_SESSION['login'] . "' ORDER BY postid DESC","teabag");
@@ -307,7 +349,7 @@ refresh();
 <div id="chatHistory" class="chathistory"></div>
 <form name="message" method="post">
 	<a id="chatTarget" class="author"></a><br/>
-	<textarea rows="4" placeholder="Write something here to chat!" name="message" cols="40" class="typing" id="message"></textarea>
+    <p class="lead emoji-picker-container"><textarea rows="4" placeholder="Write something here to chat!" name="message" cols="40" class="typing" id="message" data-emojiable="true"></textarea></p>
 	<input type="button" onclick="update()" class="ts primary fluid button" value="Send">
 </form>
 </div>

+ 74 - 3
src/regi.php

@@ -34,15 +34,17 @@ if (file_exists($databasePath)){
 */
 //See if this page is requested for command.
 $errormsg = "";
+
 if (isset($_POST['act']) && $_POST['act'] != ""){
 	$action = $_POST['act'];
-	if ($action == "newuser"){
+    if ($action == "newuser"){
 		if (isset($_POST['username']) && isset($_POST['secretecode'])){
 			$newusername = $_POST['username'];
 			$password = $_POST['secretecode'];
 			if ($password == ""){
 				showerror("Password cannot be empty!");
 			}
+            
 			$encodedpw = hash('sha512',$password);
 			$content = trim($content);
 			$users = explode(PHP_EOL,$content);
@@ -103,6 +105,36 @@ if (isset($_POST['act']) && $_POST['act'] != ""){
 <link rel="stylesheet" href="script/tocas/tocas.css">
 <script src="script/tocas/tocas.js"></script>
 <script src="script/jquery.min.js"></script>
+<script src="script/zxcvbn.js"></script>
+<script type="text/javascript" src="script/jquery-1.9.1.min.js"></script>
+<script type="text/javascript" src="script/knockout-2.2.1.js"></script>
+<style>
+meter {
+  /* Reset the default appearance */
+  
+
+  margin: 0 auto 1em;
+  width: 100%;
+  height: 0.5em;
+    
+  background: none;
+  background-color: rgba(0, 0, 0, 0.1);
+}
+
+/* Webkit based browsers */
+meter[value="1"]::-webkit-meter-optimum-value { background: red; }
+meter[value="2"]::-webkit-meter-optimum-value { background: yellow; }
+meter[value="3"]::-webkit-meter-optimum-value { background: orange; }
+meter[value="4"]::-webkit-meter-optimum-value { background: green; }
+
+/* Gecko based browsers */
+meter[value="1"]::-moz-meter-bar { background: red; }
+meter[value="2"]::-moz-meter-bar { background: yellow; }
+meter[value="3"]::-moz-meter-bar { background: orange; }
+meter[value="4"]::-moz-meter-bar { background: green; }
+
+</style>
+    
 </head>
 <body>
 <!--
@@ -128,8 +160,11 @@ if (isset($_POST['act']) && $_POST['act'] != ""){
 					<input name="username" type="text">
 				</div>
 				<div class="field">
-					<label>Password</label>
+					<label for="secretecode">Password</label>
+                    <p id="password-strength-text"></p>
 					<input id="passwordfield" name="secretecode" type="password">
+                    <meter max="4" id="password-strength-meter"></meter>
+                    <span id="passwordNotStrong"> </span>
 				</div>
 				<input name="act" type="text" value="newuser" style="display:none;">
 				<code>Please login to your new account after you have added the first new user.</code><br><br>
@@ -173,6 +208,7 @@ if (isset($_POST['act']) && $_POST['act'] != ""){
 		<div class="ts divider"></div>
 		ArOZ Online Authentication System feat. IMUS Laboratory
 	</div>
+    
 	<script>
 	var selectedUser = "";
 	setTimeout(function(){ hideMsgBox(); }, 5000);
@@ -216,6 +252,41 @@ if (isset($_POST['act']) && $_POST['act'] != ""){
 		$(this).addClass("selected");
 		selectedUser = $(this).text();
 	});
-	</script>
+    
+//password safety level
+var strength = {
+  0: "Worst",
+  1: "Bad",
+  2: "Weak",
+  3: "Good",
+  4: "Strong"
+}
+
+var password = document.getElementById('passwordfield');
+var meter = document.getElementById('password-strength-meter');
+var text = document.getElementById('password-strength-text');
+
+password.addEventListener('input', function() {
+  var val = password.value;
+  var result = zxcvbn(val);
+
+  // Update the password strength meter
+  meter.value = result.score;
+
+  // Update the text indicator
+  if (val !== "") {
+    text.innerHTML = "Strength: " + strength[result.score]; 
+  } else {
+    text.innerHTML = "";
+  }
+});
+        
+
+
+    </script>
+
+    
+
+    
 </body>
 </html>

+ 2 - 2
src/requestDB.php

@@ -1,7 +1,7 @@
 <?php
 //Pass query to database and return something
 function query($query,$table){
-	$conn = mysqli_connect("localhost","teabag","teabagpassword",$table);
+	$conn = mysqli_connect("localhost","root","","teabag");
 	if($conn->connect_error)
 	{
 		echo "Unable to connect to database";
@@ -23,7 +23,7 @@ function query($query,$table){
 
 //Write things to database without returning anything
 function queryw($query,$table){
-	$conn = mysqli_connect("localhost","teabag","teabagpassword",$table);
+	$conn = mysqli_connect("localhost","root","","teabag");
 	if($conn->connect_error)
 	{
 		echo "Unable to connect to database";

+ 251 - 0
src/script/css/emoji.css

@@ -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; /* 35 */
+    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;
+}

File diff suppressed because it is too large
+ 853 - 0
src/script/js/config.js


+ 102 - 0
src/script/js/emoji-picker.coffee

@@ -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 - 0
src/script/js/emoji-picker.js

@@ -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

File diff suppressed because it is too large
+ 8 - 0
src/script/js/emoji-picker.js.map


+ 773 - 0
src/script/js/jquery.emojiarea.js

@@ -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, '&amp;').replace(/</g, '&lt;')
+        .replace(/>/g, '&gt;').replace(/"/g, '&quot;');
+  };
+
+  /*
+   * ! 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 - 0
src/script/js/util.js

@@ -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;
+})();

+ 1 - 0
src/userdata/cookieseeds/1575354241.auth

@@ -0,0 +1 @@
+5dbbc6f2a94e53f30952b8102231f8a6e95970161b482ba834ed15587de54dd98f891ba92dc4bf7532ba23cc4a882ebb489ab3a7d12c8c29df7ff4361182049d

+ 1 - 0
src/userdata/whitelist.config

@@ -2,3 +2,4 @@ kent,D404559F602EAB6FD602AC7680DACBFAADD13630335E951F097AF3900E9DE176B6DB28512F2
 admin,C7AD44CBAD762A5DA0A452F9E854FDC1E0E7A52A38015F23F3EAB1D80B931DD472634DFAC71CD34EBC35D16AB7FB8A90C81F975113D6C7538DC69DD8DE9077EC
 xxxabc,D404559F602EAB6FD602AC7680DACBFAADD13630335E951F097AF3900E9DE176B6DB28512F2E000B9D04FBA5133E8B1C6E8DF59DB3A8AB9D60BE4B97CC9E81DB
 xdxd,D404559F602EAB6FD602AC7680DACBFAADD13630335E951F097AF3900E9DE176B6DB28512F2E000B9D04FBA5133E8B1C6E8DF59DB3A8AB9D60BE4B97CC9E81DB
+dicksontesting,100E528084C5B64EA9B2F2652632FEB232853D75BFD1D3C53F9724246A204907E873AC1ED73A6C99BC5593D9F1CFD2905F75ECBE61CB5DFB768882340F5C0F5E

Some files were not shown because too many files changed in this diff