您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
See website
当前为
// ==UserScript== // @name BetterFetLife // @namespace com.fetlife.better // @include https://fetlife.com/* // @version 1.2 // @grant none // @description See website // ==/UserScript== $(document).ready(function(){ // add custom css $('head').append('<style type="text/css">' + bfl_css + '</style>'); // === USER POPUP === // // create user popup $('body').append(' ' + ' <div id="bfl-user"> ' + ' <a class="avatar-wrap"> ' + ' <div class="avatar"></div> ' + ' </a> ' + ' <a class="name"></a> ' + ' <span class="status"></span> ' + ' <span class="location"></span> ' + ' <span class="photos"></span> ' + ' </div> ' ); // show a user popup on hover $('a[href^="/users/"], a[href^="https://fetlife.com/users/"').hoverIntent(function(){ var linkEl = this; var href = $(linkEl).attr('href'); href = href.replace('https://fetlife.com', ''); // prevent non-user links if( href.split('/').length != 3 ) return; // prevent self-links if( $(linkEl).closest('#bfl-user').length > 0 ) return; // prevent closing clearTimeout(hideUserPopupTimeout); // reset the popup hideUserPopup(); // show the popup $('#bfl-user') .addClass('loading') .css({ top: $(linkEl).offset().top + $(linkEl).height() + 8, left: $(linkEl).offset().left }) .show(); $.ajax({ url: href, dataType: "html", // cache is OK cache: true, // prevent 503, fetlife don't liking ajax calls beforeSend: function(xhr) { xhr.setRequestHeader( 'X-Requested-With', { toString: function() { return ''; } } ); }, success: function(userDOM){ var userAvatarEl = $(userDOM).find('#main_content a img'); // avatar href $('#bfl-user') .attr('avatar-href', $(userAvatarEl).attr('src')); // avatar $('#bfl-user .avatar-wrap') .attr('href', href) $('#bfl-user .avatar') .css('background-image', 'url(' + $(userAvatarEl).attr('src') + ')') // name $('#bfl-user .name') .attr('href', href) .html( $(userAvatarEl).attr('alt') ); // status (age+gender+orientation) $('#bfl-user .status') .html( $(userDOM).find('#profile h2 .small').html() ); // location $('#bfl-user .location') .html( $(userDOM).find('#profile h2.bottom + p').html() ); window.userDOM = userDOM; // photos var photos = $(userDOM).find('#profile .container a[href^="/users/"][href*="/pictures"]'); photos = photos.filter(function(){ return $(this).find('img').length > 0; }) photos = photos.slice(0,5); $('#bfl-user .photos') .html('') .append( photos ); // friends status // remove the link first $(userDOM).find('.friends_badge').find('a').remove() $('#bfl-user .friends_status') .html( $(userDOM).find('.friends_badge').text() ); $('#bfl-user') .removeClass('loading') } }); }, function(e){ var linkEl = this; var href = $(linkEl).attr('href'); // prevent non-user links if( href.split('/').length != 3 ) return; clearTimeout(hideUserPopupTimeout); hideUserPopupTimeout = setTimeout(hideUserPopup, hideUserPopupDelay); }); $('#bfl-user').live('mouseover', function(e){ clearTimeout(hideUserPopupTimeout); }); $('#bfl-user').live('mouseleave', function(e){ clearTimeout(hideUserPopupTimeout); hideUserPopupTimeout = setTimeout(hideUserPopup, hideUserPopupDelay); }); var hideUserPopupTimeout = setTimeout('', 0); var hideUserPopupDelay = 500; function hideUserPopup() { $('#bfl-user .avatar').attr('style', ''); $('#bfl-user .name').html('').attr('href', ''); $('#bfl-user .status').html(''); $('#bfl-user .location').html(''); $('#bfl-user .friends_badge').html(''); $('#bfl-user .photos').html(''); $('#bfl-user').removeClass('loading'); $('#bfl-user').hide(); } // === IMAGE POPUP === // // create image popup $('body').append(' ' + ' <div id="bfl-image"> ' + ' <span class="header"> ' + ' <span class="title"></span> ' + ' <span class="like-wrap"> ' + ' <span class="like-count"></span> ' + ' <span class="like picto">k</span> ' + ' </span> ' + ' </span> ' + ' <a class="image-wrap"> ' + ' <img class="image" /> ' + ' </a> ' + ' </div> ' ); $('a[href^="/users/"][href*="/pictures"], a[href^="https://fetlife.com/users/"][href*="/pictures"]').hoverIntent(function(){ var linkEl = this; var href = $(this).attr('href'); href = href.replace('https://fetlife.com', ''); // prevent non-image links if( href.split('/').length != 5 ) return; // prevent self-links if( $(linkEl).closest('#bfl-image').length > 0 ) return; // prevent 'next image' if( $(linkEl).children('.fake_img').length > ) return; // reset the popup hideImagePopup(); // show the popup var css = { top: $(linkEl).offset().top + $(linkEl).height() + 8 } if( $(linkEl).offset().left > $(window).width()/2 ) { css.right = $(window).width() - $(linkEl).offset().left - $(linkEl).width(); $('#bfl-image').addClass('alignright'); } else { css.left = $(linkEl).offset().left; } $('#bfl-image') .addClass('loading') .css(css) .show(); $.ajax({ url: href, dataType: "html", success: function(html){ var title = $(html).find('.s.i.caption').text(); var likeUrl = href.split('/'); likeUrl = likeUrl[ likeUrl.length-1 ]; likeUrl = "/pictures/" + likeUrl + "/likes" // extract the image src var src = $(html).find('style').first().html().match(/\(\'(.*?)\'\)/); src = src[0]; src = src.replace("('", ""); src = src.replace("')", ""); $('#bfl-image .title') .html(title) .attr('title', title) $('#bfl-image .like-wrap') .attr('data-href', likeUrl); $('#bfl-image .image-wrap') .attr('href', href); $('#bfl-image .image') .load(function(){ $('#bfl-image').removeClass('loading') }) .attr('src', src) // get amount of likes $.ajax({ url: likeUrl, dataType: "json", success: function(data) { $('#bfl-image .like-wrap').toggle(data.user_can_like); if( data.is_liked_by_user ) { $('#bfl-image .like-wrap').addClass('liked'); } $('#bfl-image .like-count') .html(data.total); } }); } }); }, function(e){ var linkEl = this; var href = $(linkEl).attr('href'); // prevent non-user links if( href.split('/').length != 5 ) return; clearTimeout(hideImagePopupTimeout); hideImagePopupTimeout = setTimeout(hideImagePopup, hideImagePopupDelay); }); $('#bfl-image').live('mouseover', function(e){ clearTimeout(hideImagePopupTimeout); clearTimeout(hideUserPopupTimeout); }); $('#bfl-image').live('mouseleave', function(e){ clearTimeout(hideImagePopupTimeout); hideImagePopupTimeout = setTimeout(hideImagePopup, hideImagePopupDelay); }); var hideImagePopupTimeout = setTimeout('', 0); var hideImagePopupDelay = 500; function hideImagePopup() { $('#bfl-image .title').html('').attr('href', ''); $('#bfl-image .image').attr('src', ''); $('#bfl-image .like-wrap').attr('data-href', ''); $('#bfl-image .like-count').html(''); $('#bfl-image').removeClass('loading'); $('#bfl-image').removeClass('alignright'); $('#bfl-image').hide(); } $('#bfl-image .like-wrap').live('click', function(){ var this_ = this; $.ajax({ url: $(this_).data('href') + '/toggle', type: 'post', success: function(){ if( $(this_).hasClass('liked') ) { $('#bfl-image .like-count').html( parseInt( $('#bfl-image .like-count').html()) - 1 ) } else { $('#bfl-image .like-count').html( parseInt( $('#bfl-image .like-count').html()) + 1 ) } $(this_).toggleClass('liked'); } }); return false; }); }); var bfl_css = '' + ' #bfl-user {' + ' position: absolute;' + ' z-index: 100;' + ' display: none;' + ' padding: 4px;' + ' min-width: 180px;' + ' height: 80px;' + ' padding-left: 92px;' + ' padding-right: 8px;' + ' background: #323232;' + ' border: 3px solid #171717;' + ' }' + ' #bfl-user.loading {' + ' padding-left: 84px;' + ' padding-right: 4px;' + ' min-width: 0;' + ' }' + ' #bfl-user:before,' + ' #bfl-image:before {' + ' position: absolute;' + ' z-index: 101;' + ' display: block;' + ' content: "";' + ' left: 7px;' + ' top: -8px;' + ' border: 8px solid transparent;' + ' border-bottom-color: #171717;' + ' border-top-width: 0;' + ' }' + ' #bfl-user:after,' + ' #bfl-image:after {' + ' position: absolute;' + ' z-index: 102;' + ' display: block;' + ' content: "";' + ' left: 10px;' + ' top: -5px;' + ' border: 5px solid transparent;' + ' border-bottom-color: #323232;' + ' border-top-width: 0;' + ' }' + ' #bfl-user .avatar {' + ' position: absolute;' + ' left: 4px;' + ' width: 80px;' + ' height: 80px;' + ' padding: 0px;' + ' margin-right: 8px;' + ' background-color: transparent;' + ' background-size: cover;' + ' background-position: center center;' + ' background-repeat: no-repeat;' + ' }' + ' #bfl-user.loading .avatar {' + ' background-size: auto;' + ' background-image: url(https://flassets.a.ssl.fastly.net/std/spinners/circle_big.gif);' + ' margin-right: 0;' + ' }' + ' #bfl-user .name {' + ' white-space: nowrap;' + ' }' + ' #bfl-user .status {' + ' white-space: nowrap;' + ' color: #aaa;' + ' }' + ' #bfl-user .location {' + ' display: block;' + ' font-size: 12px;' + ' white-space: nowrap;' + ' }' + ' #bfl-user .friends_status {' + ' float: right;' + ' font-size: 12px;' + ' }' + ' #bfl-user .photos {' + ' position: absolute;' + ' right: 4px;' + ' bottom: 4px;' + ' font-size: 12px;' + ' }' + ' #bfl-user .photos a {' + ' float: left;' + ' font-size: 12px;' + ' }' + ' #bfl-user .photos a img {' + ' float: left;' + ' margin: 2px;' + ' width: 25px;' + ' height: 25px;' + ' padding: 0;' + ' }' + ' #bfl-image {' + ' position: absolute;' + ' z-index: 100;' + ' display: none;' + ' background: #323232;' + ' border: 3px solid #171717;' + ' padding: 4px;' + ' }' + ' #bfl-image.alignright:before {' + ' left: auto;' + ' right: 7px;' + ' }' + ' #bfl-image.alignright:after {' + ' left: auto;' + ' right: 10px;' + ' }' + ' #bfl-image.loading {' + ' width: 80px;' + ' height: 80px;' + ' background: #323232 url(https://flassets.a.ssl.fastly.net/std/spinners/circle_big.gif) no-repeat center center;' + ' }' + ' #bfl-image .header {' + ' position: absolute;' + ' left: 0;' + ' right: 0;' + ' top: 0;' + ' background: #323232;' + ' padding: 4px;' + ' overflow: hidden;' + ' font-size: 12px;;' + ' }' + ' #bfl-image.loading .header {' + ' display: none;' + ' }' + ' #bfl-image .title {' + ' float: left;' + ' width: 80%;' + ' white-space: nowrap;' + ' overflow: hidden;' + ' text-overflow: ellipsis;' + ' }' + ' #bfl-image .like-wrap {' + ' float: right;' + ' width: 10%;' + ' white-space: nowrap;' + ' text-align: right;' + ' cursor: pointer;' + ' }' + ' #bfl-image .like-wrap:hover {' + ' color: #ffffff;' + ' }' + ' #bfl-image .like-wrap:active {' + ' color: #bbbbbb;' + ' }' + ' #bfl-image .like-wrap.liked {' + ' color: #DD0000;' + ' }' + ' #bfl-image .like-wrap.liked:hover {' + ' color: #FF0000;' + ' }' + ' #bfl-image .like-wrap.liked:active {' + ' color: #BB0000;' + ' }' + ' #bfl-image .image {' + ' display: block;' + ' padding: 0;' + ' }' + ' #bfl-image.loading .image {' + ' opacity: 0;' + ' }' + /*! * hoverIntent v1.8.0 // 2014.06.29 // jQuery v1.9.1+ * http://cherne.net/brian/resources/jquery.hoverIntent.html * * You may use hoverIntent under the terms of the MIT license. Basically that * means you are free to use hoverIntent as long as this header is left intact. * Copyright 2007, 2014 Brian Cherne */ /** * hoverIntent is similar to jQuery's built-in "hover" function except that * instead of firing the onMouseOver event immediately, hoverIntent checks * to see if the user's mouse has slowed down (beneath the sensitivity * threshold) before firing the onMouseOver event. * * hoverIntent r6 // 2011.02.26 // jQuery 1.5.1+ * <http://cherne.net/brian/resources/jquery.hoverIntent.html> * * hoverIntent is currently available for use in all personal or commercial * projects under both MIT and GPL licenses. This means that you can choose * the license that best suits your project, and use it accordingly. * * // basic usage (just like .hover) receives onMouseOver and onMouseOut functions * $("ul li").hoverIntent( showNav , hideNav ); * * // advanced usage receives configuration object only * $("ul li").hoverIntent({ * sensitivity: 7, // number = sensitivity threshold (must be 1 or higher) * interval: 100, // number = milliseconds of polling interval * over: showNav, // function = onMouseOver callback (required) * timeout: 0, // number = milliseconds delay before onMouseOut function call * out: hideNav // function = onMouseOut callback (required) * }); * * @param f onMouseOver function || An object with configuration options * @param g onMouseOut function || Nothing (use configuration options object) * @author Brian Cherne brian(at)cherne(dot)net */ (function($) { $.fn.hoverIntent = function(f,g) { // default configuration options var cfg = { sensitivity: 7, interval: 100, timeout: 0 }; // override configuration options with user supplied object cfg = $.extend(cfg, g ? { over: f, out: g } : f ); // instantiate variables // cX, cY = current X and Y position of mouse, updated by mousemove event // pX, pY = previous X and Y position of mouse, set by mouseover and polling interval var cX, cY, pX, pY; // A private function for getting mouse position var track = function(ev) { cX = ev.pageX; cY = ev.pageY; }; // A private function for comparing current and previous mouse position var compare = function(ev,ob) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); // compare mouse positions to see if they've crossed the threshold if ( ( Math.abs(pX-cX) + Math.abs(pY-cY) ) < cfg.sensitivity ) { $(ob).unbind("mousemove",track); // set hoverIntent state to true (so mouseOut can be called) ob.hoverIntent_s = 1; return cfg.over.apply(ob,[ev]); } else { // set previous coordinates for next time pX = cX; pY = cY; // use self-calling timeout, guarantees intervals are spaced out properly (avoids JavaScript timer bugs) ob.hoverIntent_t = setTimeout( function(){compare(ev, ob);} , cfg.interval ); } }; // A private function for delaying the mouseOut function var delay = function(ev,ob) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); ob.hoverIntent_s = 0; return cfg.out.apply(ob,[ev]); }; // A private function for handling mouse 'hovering' var handleHover = function(e) { // copy objects to be passed into t (required for event object to be passed in IE) var ev = jQuery.extend({},e); var ob = this; // cancel hoverIntent timer if it exists if (ob.hoverIntent_t) { ob.hoverIntent_t = clearTimeout(ob.hoverIntent_t); } // if e.type == "mouseenter" if (e.type == "mouseenter") { // set "previous" X and Y position based on initial entry point pX = ev.pageX; pY = ev.pageY; // update "current" X and Y position based on mousemove $(ob).bind("mousemove",track); // start polling interval (self-calling timeout) to compare mouse coordinates over time if (ob.hoverIntent_s != 1) { ob.hoverIntent_t = setTimeout( function(){compare(ev,ob);} , cfg.interval );} // else e.type == "mouseleave" } else { // unbind expensive mousemove event $(ob).unbind("mousemove",track); // if hoverIntent state is true, then call the mouseOut function after the specified delay if (ob.hoverIntent_s == 1) { ob.hoverIntent_t = setTimeout( function(){delay(ev,ob);} , cfg.timeout );} } }; // bind the function to the two event listeners //return this.bind('mouseenter',handleHover).bind('mouseleave',handleHover); return this.live('hover', handleHover); }; })(jQuery);