您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
It makes comment groups for repliying comments on Trello.
当前为
// ==UserScript== // @name [Trello] Advanced Comments // @name:tr [Trello] Gelişmiş Yorumlar // @description It makes comment groups for repliying comments on Trello. // @description:tr Trello'da birbirine cevap olarak yazılmış yorumları gruplar. // @author nht.ctn // @namespace https://github.com/nhtctn // @license MIT // @version 1.0 // @icon data:image/png;base64,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 // @match https://trello.com/* // @grant GM_addStyle // @run-at document-end // @require https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js // ==/UserScript== /* global $ */ /*jshint esversion: 6 */ (function() { 'use strict'; // Bu betiğe özgü fonksiyonlar const c = (x) => console.log(x); const link = (x) => $(x).closest('.phenom').find('.phenom-date > a.date, .phenom-meta > a.date').attr('href'); const date = (x) => new Date( $(x).closest('.phenom').find('.phenom-date > a.date, .phenom-meta > a.date').attr('dt') ).getTime(); const type = (x) => ($(x).closest('.phenom').is('.mod-comment-type')) ? "com" : "attach"; const getLang = () => $('html').attr("lang"); const getItem = (x) => { if ($.type(x) == "string") return $('.card-detail-window a.date[href*="' + x + '"]').closest('.phenom'); else if ($.type(x) == "object") return $(x).closest('.phenom'); else return c( 'id fonksiyonunda belirsiz tip: ' + $.type(x) ); }; const id = (x) => { if ($.type(x) == "string") return x.replace(/.+\#((comment|action)-.+)/, "$1"); else if ($.type(x) == "object") return $(x).closest('.phenom').find('a.date').attr('href').replace(/.+\#((comment|action)-.+)/, "$1"); else return c( 'id fonksiyonunda belirsiz tip: ' + $.type(x) ); }; const key = (x) => { if ($.type(x) == "string") return x.replace(/(.+)?(comment|action|group|replies|)-(.+)/, "$3"); else if ($.type(x) == "object") return $(x).closest('.phenom').find('a.date').attr('href').replace(/.+\#(comment|action)-(.+)/, "$2"); else return c( 'key fonksiyonunda belirsiz tip: ' + $.type(x) ); }; let removeSlctr = `p > a:first-child[href*="trello.com"][href*="#comment-"], p > a:first-child[href*="trello.com"][href*="#action-"], p > span.atMention:nth-child(2), span.atMention:nth-child(3), p > br:nth-child(2), p > br:nth-child(3), p > br:nth-child(4), .phenom-reactions .js-attach-link`; // Yorum grubu oluştur. function groupDiv(target, comfor, link) { getItem(target).before('<div id="group-' + key(comfor) + '"><div id="replies-' + key(comfor) + '"></div></div>'); getItem(comfor).prependTo( '.card-detail-window #group-' + key(comfor) ); getItem(link).prependTo( '.card-detail-window #replies-' + key(comfor) ); let dummyCom = $('.card-detail-window .window-module > .new-comment.js-new-comment').clone().removeClass("js-new-comment is-focused is-show-controls").addClass("new-group-comment"); dummyCom.find('.js-new-comment-input').removeClass('js-new-comment-input').css("height", "20.0348px").val(""); dummyCom.appendTo('.card-detail-window #group-' + key(comfor)).wrap('<div class="group-comment-div"></div>'); $('.card-detail-window #group-' + key(comfor) + ' .new-group-comment').click(function(){ textAreaShifter(this); }); } // Bir kart ilk açıldığındaki işlemler let cardUrl = ''; waitForKeyElements('.card-detail-window p.u-bottom a.show-more.js-show-all-actions', cardWindow); function cardWindow () { things = []; cardUrl = window.location.href.replace(/(.+trello\.com\/c\/.+\/\d+).+/, "$1"); let intCount = 0; // Yeterince bekleyip tüm etkinlikleri göstere tıkla let myTimeOut = setInterval(function () { let moreButton = $('.card-detail-window p.u-bottom a.show-more.js-show-all-actions').removeAttr("href").css("cursor", "pointer"); moreButton[0].click(); if ( $('.card-detail-window .js-show-all-actions').is(":hidden")) clearInterval(myTimeOut); else if (intCount++ > 40) clearInterval(myTimeOut); }, 500); GM_addStyle(` .window-overlay > .window {width: 868px;} .card-detail-window > .window-main-col {width: 652px;} .card-detail-window > .window-sidebar {width: calc(100% - 700px);} .mod-card-back > [id^="group-"] {padding: 8px 0 2px 0;} [id^="group-"] [id^="group-"] {padding: 0 0 6px 0;} [id^="group-"] > [id^="replies-"] {padding-left: 6%;} [id^="group-"] .mod-comment-type:not(.mod-highlighted) {padding: 0 0 6px 0} [id^="group-"] .mod-comment-type.mod-highlighted {padding: 0 0 6px 48px} .group-comment-div {padding-left: 6%; padding-top: 4px} .artificialReply {text-decoration: none;} .artificialReply:hover {text-decoration: underline;} `); } // Yeni bir etkinlik saptandığındaki işlemler let things = []; waitForKeyElements('.card-detail-window .mod-card-back .phenom a.date', function(newElement){ addUnseen(newElement); }); function addUnseen(el) { // Yeniyse kaydet. let isUnseen = true; for (let x = 0; x < things.length; x++) { isUnseen = (things[x].link == link(el)) ? false : true; if(!isUnseen) break; } if (isUnseen) { let newThings = []; $('.card-detail-window .mod-card-back .phenom a.date').each(function(){ newThings.push({ link: link( $(this) ), id: id( $(this) ), date: date( $(this) ), type: type( $(this) ), comFor: findReply( $(this) ), }); things = uniqArray(newThings); arraySorter(things, "object", "date"); }); //console.log(things); newThing(); } } function findReply(this_) { // Buraya başka kartın yorum linki olmasın diye koşul koyulacak. if (type(this_) == "com") { let firstNd = $(this_.closest('.mod-comment-type').find('.current-comment p')[0].firstChild); if (firstNd.is("a") && firstNd.attr("href").search(/trello\.com\/.+\#(comment|action)-/) > 0 ) return id( firstNd.attr("href") ); else return null; } else return null; } // Bir tepkiye ilk raslandığındaki işlemler waitForKeyElements('.card-detail-window .mod-comment-type > .phenom-reactions .reactions-add-icon', function(elem) {moveReactions(elem);}); function moveReactions(this_) { // Tepkilerin yerini değiştir let react = this_.closest('.phenom-reactions'); let com = this_.closest('.mod-comment-type'); com.attr("id", id(this_)); com.find('.phenom-meta').css("display", "contents"); if (com.find('.rightOfDate').length <= 0) { com.find('.phenom-date').after('<div class="rightOfDate" style="display: contents;"></div>'); react.css("display", "inline-flex").css("float", "right").css("margin-right", "8px").appendTo('#' + id(this_) + ' .rightOfDate'); } // Yanıtla butonuyla link ekle let cardUrl = window.location.href.replace(/(.+trello\.com\/c\/.+\/\d+).+/, "$1"); react.find('.js-reply-to-action, .js-reply-to-all-action').click(function(){ $('.js-new-comment textarea.comment-box-input').val( cardUrl + '#' + id(this_) ); }); // Silme işleminde yeniden sırala react.find(".js-confirm-delete-action").click(function(){ waitForKeyElements('.js-confirm.nch-button--danger', function(){ $('.js-confirm.nch-button--danger').one("click", function(){ setTimeout( function() {newThing();}, 200); }); }, true); }); } // Başkası yorum silip sayfa bozulduysa diye arada kontrol et. setInterval(function(){ if($('[id^="group-"]'). length > 0 && $('[id^="group-"] > [id^="replies-"]:first-child'). length > 0) { newThing(); } }, 5000); function putReply(el) { // Actionlara yanıtla butonu ekle. if (el.type == "attach" && $('#' + el.id + ' .artificialReply').length <= 0) { let reply = ($('html').attr("lang") == "tr") ? "Yanıtla" : "Reply"; $('#' + el.id + ' a.date').after(' - <a class="js-reply-to-all-action artificialReply" href="#">' + reply + '</a>'); $('#' + el.id + ' .artificialReply').click(function(){ let mentions = replyMentions(getItem(this)); let val = (cardUrl + '#' + id(this) + ' ' + mentions + ' '); $('.js-new-comment textarea.comment-box-input').val(val); $('.js-new-comment textarea.comment-box-input').focus(); }); } } // Yeni bir eylem saptandığındaki işlemler function newThing() { if ($('[id^="group-"]').length > 0) clearResiduals(); for (let x = 0; x < things.length; x++) { // Her bir eyleme id ekle. let th = things[x]; if (getItem(th.id).attr("id") == undefined) getItem(th.id).attr("id", th.id); // Eylem yorumsa let isComForExist = (th.comFor != null && getItem(th.comFor).length > 0); if (isComForExist) { let isComForNonThreaded = getItem(th.comFor).closest('[id^="group-"]').length <= 0; let isComForRegular = getItem(th.comFor).is('[id^="group-"] .mod-comment-type:first-child, [id^="replies-"] .mod-comment-type:last-child'); if (isComForNonThreaded) { // Yorumun atası herhangi bir grupta değil. Yeni grup aç. groupDiv(th.id, th.comFor, th.id); } else { // Yorumun atası bir grupta. Toptan grubu yorumun olduğu yere taşı. let repliesId = getItem(th.comFor).closest('[id^="group-"]').find('[id^="replies-"]').attr("id"); if (repliesId != getItem(th.id).closest('[id^="group-"]').find('[id^="replies-"]').attr("id")) { //Birbirini hedef gösteren yorumlarda buga girmesin diye. $('#' + repliesId).closest('.mod-card-back > [id^="group-"]').insertBefore(getItem(th.id)); if (isComForRegular) { getItem(th.id).appendTo( '#' + repliesId ); // Ata grubun atası ya son mesajıysa yorumu grubun altına taşı. } else { groupDiv(th.comFor, th.comFor, th.id); // Ata grubun ortasındaysa yeni alt grup aç. } } } // Cevap olarak yazılmış bir yorum ise linki, alıntıları, satır atlamayı, bağlantı olarak ekle seçeneğini gizle. getItem(th.id).find(removeSlctr).hide(); // c(comItem(th.id).find(removeSlctr)[0].getAttribute("style")); let attachLinks = getItem(th.id).find('.phenom-reactions .js-attach-link'); let afterAttach = $(attachLinks[0].nextSibling); if (!(afterAttach.is('a, span'))) afterAttach.remove(); } else { putReply(th); } } // Çift grup kontrol. if ($('[id^="group-"] [id^="group-"]').length > 0) doubleGroup(); } function clearResiduals() { //c("residual clean"); $('.group-comment-div, #newCommentWatcher').remove(); $('[id^="replies-"]').children(':first-child').unwrap('[id^="replies-"]'); $('[id^="group-"]').children(':first-child').unwrap('[id^="group-"]'); let residual = $('[id^="group-"], [id^="replies-"], .group-comment-div'); for (let x = 0; x < residual.length; x++) { if ( $(residual[x]).children('.mod-comment-type').length <= 0 ) { $(residual[x]).remove(); } } } // Grup içindeki grupları denetle, gerekliyse dışarı at function doubleGroup() { //c("doubleGroup"); $('[id^="group-"] [id^="group-"] > .mod-comment-type .current-comment p').each(function(){ let firstNd = $(this.firstChild); let realGroup = $(this).closest('[id^="group-"] [id^="group-"]'); if (firstNd.is("a") && firstNd.attr("href").search(/trello\.com\/+c\/.+\/.+\#comment-/) > 0 ) { // Burayı henüz test edemedim. let href = id( $(firstNd).attr("href") ); let isRelated = realGroup.closest('.js-list-actions.mod-card-back > [id^="group-"]').find('a.date[href*="' + href + '"]').length > 0; if (!isRelated) realGroup.insertBefore( realGroup.closest('.js-list-actions.mod-card-back > [id^="group-"]') ); } else { c("doubleGroup problemi çözüldü"); realGroup.insertBefore( realGroup.closest('.js-list-actions.mod-card-back > [id^="group-"]') ); } }); } // Grupların altındaki klon yorum alanlarına tıklandığında function textAreaShifter(dum) { let val = ''; if ( $(dum).closest('[id^="group-"]').length > 0 ) { // Klon grubun altındaysa içine yazdırılacak yazıyı oluştur. let lastGroupCom = $(dum).closest('[id^="group-"]').children('[id^="replies-"]').children('.mod-comment-type:last-of-type'); let pageUrl = window.location.href.replace(/(.+trello\.com\/c\/.+\/\d+).+/, "$1"); let mentions = replyMentions(lastGroupCom); val = (pageUrl + '#' + id( link(lastGroupCom) ) + ' ' + mentions + ' '); // Grubun altında işi kalmayıp aktifliğini kaybettiyse geri yolla ki yanıtla tuşu kullanıldığında yerinde olsun. let groupComTimeOut = setInterval(function () { let pasiveGroupCom = $('.card-detail-window .js-new-comment:not(.is-focused, .card-detail-window .is-show-controls)'); if (pasiveGroupCom.length > 0) { swaper($('.card-detail-window .js-new-comment'), $('.card-detail-window .window-module > .new-group-comment')); $('.card-detail-window .js-new-comment .js-new-comment-input').val(""); clearInterval(groupComTimeOut); } }, 100); } // Klonla orijinal text editor'ü yer değiştir. Klon grup altına da gidebilir, orijinal yorum alanına da. swaper($('.card-detail-window .js-new-comment'), $(dum).closest('.new-group-comment')); $('.card-detail-window .js-new-comment .js-new-comment-input').val(val).click().focus(); } // Yanıtlanacak cevap için atıfları ayarla function replyMentions(el) { if (id(el).search(/action-/) >= 0) return $(el).find('.phenom-creator .member-avatar').attr("title").replace(/(.+) \((.+)\)/, "\@$2"); let myName = $('.card-detail-window .new-comment .member-avatar').attr("title").replace(/(.+) \((.+)\)/, "\@$2"); let ment = []; el.find('.atMention').each(function(){ment.push($(this).text());}); ment.push( el.find('.phenom-creator .member-avatar').attr("title").replace(/(.+) \((.+)\)/, "\@$2") ); ment = arrayRemover(ment, myName); return ment.toString().replace(/\,/g, " "); } // Hazır fonsiyonlar function uniqArray(array) { if ($.type(array[0]) == "object") { let objs = []; return array.filter(function(item) { return JSON.stringify(objs).search(JSON.stringify(item)) >= 0 ? false : objs.push(item); }); } else { var seen = {}; return array.filter(function(item) { return seen.hasOwnProperty(item) ? false : (seen[item] = true); }); } } function arrayRemover(array, removeThis, objectType) { let resultArray = []; if ($.type(array[0]) == "object") { if ($.type(removeThis) == "array") { for(let o = 0; o < array.length; o++){ for (let i = 0; i < removeThis.length; i++) { if ( array[o][objectType] == removeThis[i][objectType] ) break; else if (i+1 == removeThis.length) resultArray.push(array[o]); } } } else { resultArray = $.grep(array, function(value) { return value[objectType] != removeThis; }); } } else { if ($.type(removeThis) == "array") { for(let o = 0; o < array.length; o++){ let newArray = []; for (let i = 0; i < removeThis.length; i++) { if ( array[o] == removeThis[i] ) break; else if (i+1 == removeThis.length) resultArray.push(array[o]); } } } else { resultArray = $.grep(array, function(value) { return value != removeThis; }); } } return resultArray; } function swaper(el1, el2) { $(el1).before('<div id="dummyDiv1"></div>'); $(el2).before('<div id="dummyDiv2"></div>'); $(el1).appendTo('#dummyDiv2').unwrap('#dummyDiv2'); $(el2).appendTo('#dummyDiv1').unwrap('#dummyDiv1'); } function arraySorter(array, objectOrNot, objectType) { if (objectOrNot == "object") { array.sort(function(a, b) { var x = ( isFinite(a[objectType]) ) ? Number(a[objectType]) : a[objectType].toString().toLowerCase(); var y = ( isFinite(b[objectType]) ) ? Number(b[objectType]) : b[objectType].toString().toLowerCase(); if (x < y) {return -1;} if (x > y) {return 1;} return 0; }); } else if (objectOrNot == "nonObject") { array.sort(); } } function waitForKeyElements ( selectorTxt, /* Required: The jQuery selector string that specifies the desired element(s). */ actionFunction, /* Required: The code to run when elements are found. It is passed a jNode to the matched element. */ bWaitOnce, /* Optional: If false, will continue to scan for new elements even after the first match is found. */ iframeSelector /* Optional: If set, identifies the iframe to search. */ ) { var targetNodes, btargetsFound; if (typeof iframeSelector == "undefined") targetNodes = $(selectorTxt); else targetNodes = $(iframeSelector).contents().find(selectorTxt); if (targetNodes && targetNodes.length > 0) { btargetsFound = true; /*--- Found target node(s). Go through each and act if they are new. */ targetNodes.each(function() { var jThis = $(this); var alreadyFound = jThis.data('alreadyFound') || false; if (!alreadyFound) { //--- Call the payload function. var cancelFound = actionFunction(jThis); if (cancelFound) btargetsFound = false; else jThis.data('alreadyFound', true); } }); } else { btargetsFound = false; } //--- Get the timer-control variable for this selector. var controlObj = waitForKeyElements.controlObj || {}; var controlKey = selectorTxt.replace(/[^\w]/g, "_"); var timeControl = controlObj[controlKey]; //--- Now set or clear the timer as appropriate. if (btargetsFound && bWaitOnce && timeControl) { //--- The only condition where we need to clear the timer. clearInterval(timeControl); delete controlObj[controlKey]; } else { //--- Set a timer, if needed. if (!timeControl) { timeControl = setInterval(function() { waitForKeyElements(selectorTxt, actionFunction, bWaitOnce, iframeSelector); }, 300 ); controlObj [controlKey] = timeControl; } } waitForKeyElements.controlObj = controlObj; } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址