%PDF- %PDF-
Direktori : /home/vacivi36/intranet.vacivitta.com.br/protected/humhub/widgets/views/ |
Current File : /home/vacivi36/intranet.vacivitta.com.br/protected/humhub/widgets/views/richTextEditor.php |
<?php use yii\helpers\Url; ?> <script <?= \humhub\libs\Html::nonce() ?>> $(document).ready(function() { //The original form input element will be hidden var $formInput = $('#<?php echo $id; ?>').hide(); var placeholder = $formInput.attr('placeholder'); var $editableContent = $('#<?php echo $id; ?>_contenteditable'); if(!$editableContent.length) { $formInput.after('<div id="<?php echo $id; ?>_contenteditable" autocomplete="off" class="atwho-input form-control atwho-placeholder" data-query="0" contenteditable="true">' + placeholder + '</div>'); $editableContent = $('#<?php echo $id; ?>_contenteditable'); } var emojis = [ "Relaxed", "Yum", "Relieved", "Hearteyes", "Cool", "Smirk", "KissingClosedEyes", "StuckOutTongue", "StuckOutTongueWinkingEye", "StuckOutTongueClosedEyes", "Disappointed", "Frown", "ColdSweat", "TiredFace", "Grin", "Sob", "Gasp", "Gasp2", "Laughing", "Joy", "Sweet", "Satisfied", "Innocent", "Wink", "Ambivalent", "Expressionless", "Sad", "Slant", "Worried", "Kissing", "KissingHeart", "Angry", "Naughty", "Furious", "Cry", "OpenMouth", "Fearful", "Confused", "Weary", "Scream", "Astonished", "Flushed", "Sleeping", "NoMouth", "Mask", "Worried", "Smile", "Muscle", "Facepunch", "ThumbsUp", "ThumbsDown", "Beers", "Cocktail", "Burger", "PoultryLeg", "Party", "Cake", "Sun", "Fire", "Heart" ]; var emojis_list = $.map(emojis, function(value, i) { return {'id': i, 'name': value}; }); // Note we use ​ to mark the end of a mentioning link $editableContent.atwho({ at: "@", data: [{image: '', 'cssClass': 'hint', name: "<?= Yii::t('base', 'Please type at least 3 characters') ?>"}], insert_tpl: "<a href='${link}' class='atwho-user richtext-link' contenteditable='false' target='_blank' data-user-guid='${atwho-at}-${type}${guid}'>${atwho-data-value}​</a>", tpl: "<li class='hint' data-value=''>${name}</li>", limit: 10, highlight_first: false, callbacks: { matcher: function(flag, subtext, should_start_with_space) { var match, regexp; regexp = new RegExp(/(\s+|^)@([\u00C0-\u1FFF\u2C00-\uD7FF\w\s\-\']*$)/); match = regexp.exec(subtext); this.setting.tpl = "<li class='hint' data-value=''>${name}</li>"; if(match && typeof match[2] !== 'undefined') { return match[2]; } return null; }, remote_filter: function(query, callback) { this.setting.highlight_first = false; // check the char length and data-query attribute for changing plugin settings for showing results if(query.length >= 3 && $('#<?= $id; ?>_contenteditable').attr('data-query') == '1') { // Render loading user feedback. this.setting.tpl = "<li class='hint' data-value=''>${name}</li>"; this.view.render([{"type": "test", "cssClass": "hint", "name": "<?= Yii::t('base', 'Loading...') ?>", "image": "", "link": ""}]); // set plugin settings for showing results this.setting.highlight_first = true; this.setting.tpl = '<li class="${cssClass}" data-value="@${name}">${image} ${name}</li>'; $.getJSON("<?php echo Url::to([$userSearchUrl]); ?>", {keyword: query}, function(data) { callback(data); }); // reset query count query.length = 0; } }, beforeInsert: function(value, $li) { if ($li.data('item-data').link == undefined) { return ""; } else { return value; } } } }).atwho({ at: ":", insert_tpl: "<img data-emoji-name=';${name};' class='atwho-emoji' with='18' height='18' src='<?php echo Yii::getAlias('@web-static/img/emoji/${name}.svg'); ?>' />", tpl: "<li class='atwho-emoji-entry' data-value=';${name};'><img with='18' height='18' src='<?php echo Yii::getAlias('@web-static/img/emoji/${name}.svg'); ?>'/></li>", data: emojis_list, highlight_first: true, limit: 100 }); //it seems atwho detatches the original element so we have to do a requery $editableContent = $('#<?php echo $id; ?>_contenteditable'); //This is a workaround for mobile browsers especially for Android Chrome which is not able to remove contenteditable="false" nodes. if (/Android|webOS|iPhone|iPad|iPod|BlackBerry|BB|PlayBook|IEMobile|Windows Phone|Kindle|Silk|Opera Mini/i.test(navigator.userAgent)) { $editableContent.on('contextmenu', 'a, img', function() { if($(this).parent().is('span')) { $(this).parent().remove(); } else { $(this).remove(); } _checkForEmptySpans($editableContent); return false; }); } // remove placeholder text $editableContent.on('focus', function() { if($(this).hasClass('atwho-placeholder')) { $(this).removeClass('atwho-placeholder'); $(this).html(''); $(this).focus(); } }).on('focusout', function() { $('#<?php echo $id; ?>').val(getPlainInput($(this).clone())); // add placeholder text, if input is empty var html = $(this).html(); if(html == "" || html == " " || html.trim() == "<br>") { $(this).attr('spellcheck', false); $(this).html(placeholder); $(this).addClass('atwho-placeholder'); } }).on('paste', function(event) { // disable standard behavior event.preventDefault(); event.stopImmediatePropagation(); // create variable for clipboard content var text = ""; if(event.originalEvent.clipboardData) { // get clipboard data (Firefox, Webkit) text = event.originalEvent.clipboardData.getData('text/plain'); } else if(window.clipboardData) { // get clipboard data (IE) text = window.clipboardData.getData("Text"); } // create jQuery object and paste content var $result = $('<div></div>').append(escapeHtml(text)); // set plain text at current cursor position insertTextAtCursor($result.text()); }).on('keydown', function(e) { _checkForEmptySpans($editableContent); }).on('keypress', function(e) { switch(e.which) { case 13: // Enter // Insert a space after some delay to not interupt the browsers default new line insertion. var $context = $(window.getSelection().getRangeAt(0).commonAncestorContainer); setTimeout(function() { if($context[0].nodeType === Node.TEXT_NODE) { $context[0].textContent += '\u00a0'; } }, 1000); break; case 8: // Backspace // Note chrome won't fire the backspace keypress event, but we don't need the // workaround for chrome so its ok.. _checkRichTextLinkRemoval($editableContent); break; } $(this).attr('spellcheck', true); }).on("shown.atwho", function(event) { // set attribute for showing search results $(this).attr('data-query', '1'); }).on("inserted.atwho", function(event, $li) { $('.atwho-emoji').each(function() { if($(this).closest('.richtext-link').length) { $(this).closest('.richtext-link').after(this); } }); // set attribute for showing search hint $(this).attr('data-query', '0'); }).on('clear', function(evt) { $(this).html(placeholder); $(this).addClass('atwho-placeholder'); }); }); var _checkRichTextLinkRemoval = function($editableContent) { /** * This is a workaround for deleting links as a whole in firefox https://bugzilla.mozilla.org/show_bug.cgi?id=685445 */ var position = $editableContent.caret('offset'); if(!position) { return; } // Check if the caret position is right before a link, if yes remove link and perhaps also the parent if empty. $('.richtext-link').each(function() { var $this = $(this); var offset = $this.offset(); var right = offset.left + $this.outerWidth(true); // The caret top position seems a bit out in some cases... if(Math.abs(position.left - right) < 1 && Math.abs(position.top - offset.top) < 18) { $this.remove(); // This is a workaround for a caret position issue in firefox https://bugzilla.mozilla.org/show_bug.cgi?id=904846 _checkCaretPositionAfterRemove($editableContent); return false; // leave loop } }); }; var _checkCaretPositionAfterRemove = function($editableContent) { if(!$editableContent.text().length) { var spaceText = document.createTextNode("\u00a0"); $editableContent.prepend(spaceText); var sel = window.getSelection(); var range = document.createRange(); range.setStart(spaceText, 0); range.collapse(true); sel.removeAllRanges(); sel.addRange(range); } }; var _checkForEmptySpans = function($editableContent) { $editableContent.find('span').each(function() { _checkEmptySpan($(this)); }); }; var _checkEmptySpan = function($node) { if($node.is('span') && !$node.contents().length) { var $parent = $node.parent(); $node.remove(); _checkEmptySpan($parent); } }; var _getPreviousTextNode = function($node) { var $prev = $($node[0].previousSibling); var $parent = $node.parent(); // As long we have not found a non empty text node. while(!$prev.length || ($prev[0].nodeType === Node.TEXT_NODE && !$prev[0].textContent.length)) { // If current prev is not defined or an empty text node test the next prev node if($prev.length && $prev[0].nodeType === Node.TEXT_NODE && !$prev[0].textContent.length) { $prev = $($prev[0].previousSibling); } else if($parent.is('[contenteditable]')) { // If our parent is the editable itself we stop searching for other parent siblings $prev = undefined; break; } else { // Else we traverse up the dom tree and search for a prev node. $prev = $($parent[0].previousSibling); $parent = $parent.parent(); } } if($prev && $prev.length && $prev[0].nodeType === Node.TEXT_NODE) { return $prev; } else if($prev) { // Some other node (span/a) return its text. return $prev.contents().filter(function() { return this.nodeType === 3; }); } }; var entityMap = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''', '/': '/', '`': '`', '=': '=' }; function escapeHtml(string) { return String(string).replace(/[&<>"'`=\/]/g, function fromEntityMap(s) { return entityMap[s]; }); } /** * Convert contenteditable div content into plain text * @param element jQuery contenteditable div element * @returns plain text */ function getPlainInput(element) { // GENERATE USER GUIDS var userCount = element.find('.atwho-user').length; for(var i = 0; i <= userCount; i++) { var userGuid = element.find('.atwho-user:first').attr('data-user-guid'); element.find('.atwho-user:first').text(userGuid); element.find('.atwho-user:first').removeClass('atwho-user'); } // GENERATE SPACE GUIDS var spaceCount = element.find('.atwho-space').length; for(var i = 0; i <= spaceCount; i++) { var spaceGuid = element.find('.atwho-space:first').attr('data-space-guid'); element.find('.atwho-space:first').text(spaceGuid); element.find('.atwho-space:first').removeClass('atwho-space'); } // GENERATE SMILEYS var emojiCount = element.find('.atwho-emoji').length; for(var i = 0; i <= emojiCount; i++) { var emojiName = element.find('.atwho-emoji:first').attr('data-emoji-name'); element.find('.atwho-emoji:first').replaceWith(emojiName); } // save html from contenteditable div var html = element.html(); // replace html space html = html.replace(/\ /g, ' '); // rebuild tag structure for webkit browsers html = html.replace(/\<div>\s*<br\s*\\*>\<\/div>/g, '<div></div>'); // replace all div tags with br tags (webkit) html = html.replace(/\<div>/g, '<br>'); // replace all p tags with br tags (IE) html = html.replace(/\<p>\<br\s*\\*>\<\/p>/g, '<br>'); html = html.replace(/\<\/p>/g, '<br>'); // remove all line breaks html = html.replace(/(?:\r\n|\r|\n)/g, ""); // replace all <br> with new line break element.html(html.replace(/\<br\s*\>/g, '\n')); // return plain text without html tags return element.text().trim(); } /** * Insert a text at the current cursor position * @param text insert string */ function insertTextAtCursor(text, prevTrim) { var lastNode; var sel = window.getSelection(); var range = sel.getRangeAt(0); range.deleteContents(); //Remove leading line-breaks and spaces text = text.replace(/^(?:\r\n|\r|\n)/g, ''); if(!prevTrim) { text.trim(); } //We insert the lines reversed since we don't have to align the range var lines = text.split(/(?:\r\n|\r|\n)/g).reverse(); $.each(lines, function(i, line) { //Prevent break after last line if(i !== 0) { var br = document.createElement("br"); range.insertNode(br); } //Insert new node var newNode = document.createTextNode(line.trim()); range.insertNode(newNode); //Insert leading spaces as textnodes var leadingSpaces = line.match(/^\s+/); if(leadingSpaces) { var spaceCount = leadingSpaces[0].length; while(spaceCount > 0) { var spaceNode = document.createTextNode("\u00a0"); range.insertNode(spaceNode); spaceCount--; } } //The last node is the first node since we insert reversed if(i === 0) { lastNode = newNode; } }); //Align range range.setStartAfter(lastNode); range.setEndAfter(lastNode); sel.removeAllRanges(); sel.addRange(range); } </script>