more flexible page parsing, guess avatar uri

This commit is contained in:
Dylan Knutson
2023-08-21 07:54:12 -07:00
parent a4247ff8b0
commit 52dd497d4c
12 changed files with 792 additions and 41 deletions

View File

@@ -6,6 +6,8 @@ module Domain::Fa::UsersHelper
format: "jpg",
thumb: thumb,
)
elsif (path = user.avatar&.guess_file_uri_from_hles)
path
end
end

View File

@@ -69,7 +69,7 @@ export default function UserSearchBar({ isServerRendered }: PropTypes) {
setPendingRequest(null);
pendingRequest.abort();
}
}, [pendingRequest]);
}, [pendingRequest, setPendingRequest]);
const sendSearchRequest = useCallback(
(userName) => {
@@ -94,9 +94,9 @@ export default function UserSearchBar({ isServerRendered }: PropTypes) {
setUserList(gotUserList.users);
}
} catch (err) {
console.log("error loading user trie: ", err);
setPendingRequest(null);
if (!err.message.includes("aborted")) {
console.log("error loading user trie: ", err);
setPendingRequest(null);
setErrorMessage(`error loading users: ` + err.message);
}
}
@@ -105,7 +105,7 @@ export default function UserSearchBar({ isServerRendered }: PropTypes) {
return () => controller.abort();
},
[cancelPendingRequest]
[cancelPendingRequest, setPendingRequest]
);
const searchForUser = useCallback(

View File

@@ -4,4 +4,12 @@ class Domain::Fa::Parser::Base
def unimplemented_version!
raise("unimplemented page version: #{page_version}")
end
def first_matching_css(elem, csses)
for css in csses
e = elem.css(css).first
return e if e
end
nil
end
end

View File

@@ -110,30 +110,38 @@ class Domain::Fa::Parser::Page < Domain::Fa::Parser::Base
case @page_version
when VERSION_2
logged_in_user_elem && logged_in_user_elem["alt"]
else
when VERSION_0, VERSION_1
logged_in_user_elem&.text&.delete("~")
else
unimplemented_version!
end
end
def logged_in_user_elem
@logged_in_user_elem ||= case @page_version
when VERSION_0, VERSION_1
@page.css("li a#my-username").first
when VERSION_2
@page.css("img.loggedin_user_avatar").first
else
@page.css("li a#my-username").first
unimplemented_version!
end
end
def probably_listings_page?
# center.flow seems to wrap all lists of submissions
case @page_version
when VERSION_0, VERSION_1
@page.css("center.flow").first || @page.css("section.submission-list") ? true : false
when VERSION_0
first_matching_css(@page, [".submission-list", "td > section.gallery"]) ? true : false
when VERSION_1
(@page.css(".submission-list").first) ? true : false
when VERSION_2
(@page.css(".submission-list").first ||
@page.css("#gallery-browse").first ||
@page.css("#gallery-favorites").first ||
@page.css("#gallery-frontpage-submissions").first) ? true : false
first_matching_css(@page, [
".submission-list",
"#gallery-browse",
"#gallery-favorites",
"#gallery-frontpage-submissions",
]) ? true : false
else unimplemented_version!
end
end

View File

@@ -55,6 +55,16 @@ class Domain::Fa::Parser::SubmissionParserHelper < Domain::Fa::Parser::Base
artist_user_page_path.split("/").last
end
def artist_avatar_url
@artist_avatar_url ||= case @page_version
when VERSION_2
@elem.css(".submission-user-icon.avatar")&.first&.[]("src")
when VERSION_1
@elem.css("a img.avatar")&.first&.[]("src")
else unimplemented_version!
end
end
def description_html
case @page_version
# when VERSION_0

View File

@@ -105,12 +105,7 @@ class Domain::Fa::Post < ReduxApplicationRecord
pa = super
return pa if pa
@posted_at ||= begin
contents = HttpLogEntry.where(
uri_host: "www.furaffinity.net",
uri_path: ["/view/#{self.fa_id}/", "/view/#{self.fa_id}"],
uri_query: nil,
status_code: 200,
).order(created_at: :desc).first&.response&.contents
contents = guess_last_submission_page&.response&.contents
if contents
parser = Domain::Fa::Parser::Page.new(contents)
if parser.probably_submission?
@@ -128,6 +123,17 @@ class Domain::Fa::Post < ReduxApplicationRecord
HttpLogEntry.find_by_id(self.log_entry_detail["last_submission_page_id"])
end
def guess_last_submission_page
last_submission_page || begin
HttpLogEntry.where(
uri_host: "www.furaffinity.net",
uri_path: ["/view/#{self.fa_id}/", "/view/#{self.fa_id}"],
uri_query: nil,
status_code: 200,
).order(created_at: :desc).first
end
end
def have_file?
self.file_id.present?
end

View File

@@ -39,4 +39,32 @@ class Domain::Fa::UserAvatar < ReduxApplicationRecord
def guess_user_page_log_entry
user.guess_user_page_log_entry
end
def guess_file_uri_from_hles
hle = guess_user_page_log_entry
if hle
page = Domain::Fa::Parser::Page.new(hle.response.contents)
if page.probably_user_page? && (url = page.user_page.profile_thumb_url)
return url
end
end
posts = user.
posts.
where(state: [:ok, nil]).
where("file_url_str IS NOT NULL").
limit(1).
to_a
for post in posts
if (hle = post.guess_last_submission_page)
page = Domain::Fa::Parser::Page.new(hle.response.contents)
next unless page.probably_submission?
url = page.submission.artist_avatar_url
return url if url
end
end
nil
end
end

View File

@@ -40,12 +40,17 @@
<% end %>
</section>
<section class='border-2 border-slate-300 rounded-md'>
<div class='text-lg border-b-2 border-b-slate-300 text-slate-600 p-1 italic'>Similar Artists</div>
<div class='text-lg border-b-2 border-b-slate-300 text-slate-600 p-1 italic'>Similar Users</div>
<div class='bg-slate-100'>
<% similar_users_by_followed(user).each do |user| %>
<div class='flex flex-row py-1 px-2 border-b-2 last:border-b-0'>
<%= render "inline_link", user: user %>
</div>
<% similar = similar_users_by_followed(user) %>
<% if similar %>
<% similar.each do |user| %>
<div class='flex flex-row py-1 px-2 border-b-2 last:border-b-0'>
<%= render "inline_link", user: user %>
</div>
<% end %>
<% else %>
<div class='p-2'>No similar users</div>
<% end %>
</div>
</section>

View File

@@ -1,6 +1,9 @@
require "rails_helper"
require_relative "page_spec_helper"
describe Domain::Fa::Parser::Page do
include Domain::Fa::Parser::PageSpecHelper
it "sees_not_logged_in" do
parser = get_parser(
"index_not_logged_in.html",
@@ -118,8 +121,7 @@ describe Domain::Fa::Parser::Page do
it "browse_page_update_1" do
parser = get_parser "browse_page_fa_update_1.html"
assert parser.probably_listings_page?
assert !parser.probably_submission?
assert_page_type parser, :probably_listings_page?
listings = parser.submissions_parsed
assert_equal 48, listings.length
@@ -136,8 +138,7 @@ describe Domain::Fa::Parser::Page do
it "gallery_page_is_correct_fa_update_1" do
parser = get_parser "gallerey_draconicon_fa_update_1.html"
assert parser.probably_listings_page?
assert !parser.probably_submission?
assert_page_type parser, :probably_listings_page?
listings = parser.submissions_parsed
assert_equal 72, listings.length
@@ -296,6 +297,14 @@ describe Domain::Fa::Parser::Page do
assert_equal "Skye", sub.title
end
it "is a legacy submission and can be parsed" do
parser = get_parser("submission_22390847_legacy.html")
assert_page_type parser, :probably_submission?
submission = parser.submission
assert_equal "puppymachine", submission.artist
assert_equal "//a.facdn.net/1482804755/puppymachine.gif", submission.artist_avatar_url
end
def get_parser(file, require_logged_in: true)
path = File.join("domain/fa/parser/legacy", file)
contents = SpecUtil.read_fixture_file(path) || raise("Couldn't open #{path}")

View File

@@ -0,0 +1,20 @@
module Domain::Fa::Parser::PageSpecHelper
def assert_page_type(parser, expected_type)
types = [
:probably_listings_page?,
:probably_user_page?,
:probably_submission?,
]
outcomes = types.map do |type|
[type, parser.send(type)]
end.to_h
num_types = outcomes.values.filter { |a| a }.to_a.length
if num_types == 0
fail "parser detected no page types: #{outcomes}"
elsif num_types != 1
fail "parser detected multiple page types: #{outcomes}"
end
assert outcomes[expected_type], "parser has wrong guessed page type, expected #{expected_type}, got #{outcomes}"
end
end

View File

@@ -1,6 +1,9 @@
require "rails_helper"
require_relative "page_spec_helper"
describe Domain::Fa::Parser::Page do
include Domain::Fa::Parser::PageSpecHelper
it "sees_not_logged_in" do
parser = get_parser("login_page.html", require_logged_in: false)
refute parser.logged_in?
@@ -179,6 +182,7 @@ describe Domain::Fa::Parser::Page do
assert_equal 51067333, sub.id
assert_equal "BlauHaher", sub.artist
assert_equal "/user/blauhaher/", sub.artist_user_page_path
assert_equal "//a.furaffinity.net/1676066883/blauhaher.gif", sub.artist_avatar_url
assert_equal '"CUTIE!"', sub.title
assert_equal :general, sub.rating
assert_equal "//d.furaffinity.net/art/blauhaher/1676764049/1676764049.blauhaher_хорнь.png", sub.small_img
@@ -315,18 +319,4 @@ describe Domain::Fa::Parser::Page do
assert_equal Domain::Fa::Parser::Page::VERSION_2, page.page_version, "page version mismatch for file #{file}"
page
end
def assert_page_type(parser, expected_type)
types = [
:probably_listings_page?,
:probably_user_page?,
:probably_submission?,
]
outcomes = types.map do |type|
[type, parser.send(type)]
end.to_h
assert_equal 1, outcomes.values.filter { |a| a }.to_a.length, "parser detected multiple page types: #{outcomes}"
assert outcomes[expected_type], "parser has wrong guessed page type, expected #{expected_type}, got #{outcomes}"
end
end

View File

@@ -0,0 +1,665 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>guns and roses by puppymachine -- Fur Affinity [dot] net</title>
<link rel="icon" href="/themes/classic/img/favicon.ico" type="image/x-icon" />
<link rel="shortcut icon" href="/themes/classic/img/favicon.ico" type="image/x-icon" />
<meta name="description" content="Fur Affinity, the internet's largest online furry art community.">
<meta name="keywords" content="fur, furry, furry fandom, furry comics, furry art, fursuits, anthro, anthropomorphic, anthropomorphism">
<meta name="copyright" content="Ferrox Art LLC">
<meta name="author" content="Ferrox Art LLC">
<meta name="Distribution" content="Global">
<meta http-equiv="X-UA-Compatible" content="IE=9; IE=EDGE" />
<meta name="ero_verify" content="1d42658d34f4c71f70af90b130f37c66" />
<meta property="og:image" content="/themes/classic/img/banners/fa_logo.png" />
<meta property="og:title" content="guns and roses by puppymachine" />
<meta property="og:url" content="http://www.furaffinity.net/view/22390847/" />
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:site" content="furaffinity" />
<meta property="twitter:title" content="guns and roses by puppymachine" />
<meta property="twitter:description" content="did this as a warm up today :) excessively invasive comments will be hidden" />
<meta property="og:type" content="website" />
<link type="text/css" rel="stylesheet" href="/themes/classic/css/dark.css?u=2017011903" />
<!--[if lte IE 7]>
<link type="text/css" rel="stylesheet" href="/themes/classic/css/dark_ie.css?u=2017011903" />
<![endif]-->
<script type="text/javascript">
var STATIC_PATH='/themes/classic';
var Queue=function(){var q=[];return{add:function(f){q.push(f)},process:function(f){while((f=q.shift()))f();}};}();
var LazyLoad=function(){var d=document,pending=null,queue=[],ua;function getUserAgent(){if(ua){return}var a=navigator.userAgent,m;ua={gecko:0,ie:0,webkit:0};m=a.match(/AppleWebKit\/(\S*)/);if(m&&m[1]){ua.webkit=parseFloat(m[1])}else{m=a.match(/MSIE\s([^;]*)/);if(m&&m[1]){ua.ie=parseFloat(m[1])}else if((/Gecko\/(\S*)/).test(a)){ua.gecko=1;m=a.match(/rv:([^\s\)]*)/);if(m&&m[1]){ua.gecko=parseFloat(m[1])}}}}return{load:function(a,b,c,e){var f=d.getElementsByTagName('head')[0],i,script;if(a){a=a.constructor===Array?a:[a];for(i=0;i<a.length;++i){queue.push({'url':a[i],'callback':i===a.length-1?b:null,'obj':c,'scope':e})}}if(pending||!(pending=queue.shift())){return}getUserAgent();script=d.createElement('script');if(ua.ie){script.onreadystatechange=function(){if(this.readyState==='loaded'||this.readyState==='complete'){LazyLoad.requestComplete()}}}else{script.onload=LazyLoad.requestComplete;script.onerror=LazyLoad.requestComplete}script.src=pending.url;f.appendChild(script)},requestComplete:function(){if(pending.callback){if(pending.obj){if(pending.scope){pending.callback.call(pending.obj)}else{pending.callback.call(window,pending.obj)}}else{pending.callback.call()}}pending=null;if(queue.length){LazyLoad.load()}}}}();
</script>
</head>
<body>
<a name="top"></a>
<div>
<!-- top-menu -->
<!-- top-menu -->
<table width="100%" cellpadding="0" cellspacing="0" border="0" class="block-menu-top">
<tr>
<td class="header_bkg">
<ul class="dropdown dropdown-horizontal">
<li><a href="/browse/">Browse</a></li>
<li><a href="/search/">Search</a></li>
<li><a href="/submit/">Submit</a></li>
<li><a href="#" class="dir">&#9660; Community</a>
<ul>
<li><a href="http://forums.furaffinity.net/">Fur Affinity Forums</a></li>
<li><a href="http://twitter.com/furaffinity">Twitter</a></li>
<li><a href="http://www.facebook.com/furaffinity">Facebook</a></li>
<li><hr></li>
<li><a href="/advertising">Advertising</a>
</ul>
</li>
<li><a href="#" class="dir">&#9660; Support</a>
<ul>
<li><a href="/journals/fender/">News and Updates</a></li>
<li><a href="/help">Knowledgebase</a></li>
<li><hr></li>
<li><a href="/staff/">Site Staff</a></li>
<li><a href="/tos">Terms of Service</a></li>
<li><a href="/coc">Code of Conduct</a></li>
<li><a href="/aup">Acceptable Upload Policy</a></li>
</ul>
</li>
</ul>
</td>
<td class="header_bkg">
<ul class="dropdown dropdown-horizontal dropdown-left">
<li><a id="my-username" href="/user/zzreg/">~zzreg</a></li>
<li><a href="#" class="dir">&#9660; My FA</a>
<ul>
<li><a href="/msg/pms/">Notes</a></li>
<li><a href="/controls/journal/">Journals</a></li>
<li><hr></li>
<li><a href="#">&#9660; Account Management</a>
<ul>
<li><a href="/controls/settings/">Account Settings</a></li>
<li><a href="/controls/site-settings/">Site Settings</a></li>
<li><a href="/controls/user-settings/">User Settings</a></li>
<li><a href="/controls/profile/">Profile Info</a></li>
<li><a href="/controls/contacts/">Contacts / Social Media</a></li>
<li><a href="/controls/avatar/">Upload / Change Avatar</a></li>
</ul>
</li>
<li><a href="#">&#9660; Page Management</a>
<ul>
<li><a href="/controls/journal/">Journals</a></li>
<li><a href="/controls/submissions/">Submissions</a></li>
<li><a href="/controls/folders/submissions/">Gallery Folders</a></li>
<li><a href="/commissions/zzreg/">Commission Info</a></li>
<li><a href="/controls/shouts/">Page Shouts</a></li>
<li><a href="/controls/favorites/">Favorites</a></li>
<li><a href="/controls/buddylist/">Watch List</a></li>
</ul>
</li>
<li><a href="#">&#9660; Site Security</a>
<ul>
<li><a href="/controls/sessions/logins/">Active Sessions</a></li>
<li><a href="/controls/sessions/logs/">Activity Log</a></li>
<li><a href="/controls/sessions/labels/">Browser Labels</a></li>
</ul>
</li>
<li><a href="/controls/troubletickets/">Report a Problem</a></li>
<li></li>
</ul>
</li>
<li class="noblock">
<a title="Submission Notifications" href="/msg/submissions/">115054S</a>,&nbsp;<a title="Comment Notifications" href="/msg/others/#comments">1C</a>,&nbsp;<a title="Journal Notifications" href="/msg/others/#journals">5996J</a> </li>
<li class="noblock sfw-toggle" id="sfw-toggle"><a href="?" title="Toggle to hide Mature and Adult submissions. Effective starting next page load.">SFW</a></li>
<li class="noblock"><a id="logout-link" href="/logout/">Log Out</a>
</li>
</ul>
</td>
</tr>
</table>
<!-- /top-menu -->
<!-- /top-menu -->
<!-- banner -->
<div class="block-banners clearfix">
<a href="/" id="fa_header"></a>
<div class="ads">
<div class="in">
<div id="ad-2" class="ad hidden first"></div>
<div id="ad-4" class="ad hidden"></div>
</div>
</div>
</div>
<!-- /banner -->
<div id="news" class="date-1480293290">
<img class="dismiss" src="/themes/classic/img/close_panel.png" title="Dismiss" />
<a href="/journal/7944727">11/27/2016 - Site Updates</a>
</div>
<div id="ad-extra-flat" class="p10" style="text-align: center; min-height: 90px"></div>
<br />
<div style="width:98%; margin: 0 auto;">
<div class="p402_premium"></div>
<div id="page-submission">
<table cellpadding="0" cellspacing="0" border="0" width="100%">
<tr>
<td width="100%" valign="top" align="center">
<table cellpadding="2" cellspacing="1" border="0" width="100%" class="maintable">
<tr>
<th class="cat">
guns and roses </th>
</tr>
<tr>
<td align="center" class="alt1">
<!-- Media embed container -->
<br/>
<script type="text/javascript">
var small_url = "//t.facdn.net/22390847@400-1485271214.jpg";
var full_url = "//d.facdn.net/art/puppymachine/1485271214/1485271214.puppymachine_rosess.png";
var is_full = 0;
function xor_view()
{
var url = "";
var img_wrap = document.getElementById("submission-embed-wrap");
if (is_full == 1)
{
is_full = 0;
url = small_url;
if(img_wrap) {
img_wrap.className = "halfview-size";
}
}
else
{
is_full = 1;
url = full_url;
if(img_wrap) {
img_wrap.className = "";
}
}
return url;
}
</script><img id="submissionImg" title="Click to change the View" alt="guns and roses" src="//t.facdn.net/22390847@400-1485271214.jpg" onclick="javascript: this.src=xor_view();" style="cursor: pointer;" /> <br/>
<!-- END Media embed container -->
<!-- actions bar -->
<div class="alt1 actions aligncenter" style="margin-top: 8px;">
<b><a href="/fav/22390847/?key=49dc5b8470b78aad707866e849162dedc2a976aa">+Add to Favorites</a></b> |
<b><a href="//d.facdn.net/art/puppymachine/1485271214/1485271214.puppymachine_rosess.png">Download</a></b> |
<b><a href="/full/22390847/">Full View</a></b>
| <b><a href="/newpm/puppymachine/">Send note</a></b>
&nbsp;<a href="/view/22220132/" class="prev button-link">Older&nbsp;&#x276f;&#x276f;</a>
<div>Submission &copy; 2017 puppymachine</div>
</div>
<!-- folder minigalleries -->
<div class="minigalleries">
<!-- gallery/scraps -->
<div class="minigallery-container">
<center class="flow thumb-size-100">
<b class="minigallery-title"><u><s>
<a href="/gallery/puppymachine/" class="dotted">
Main Gallery </a>
<br/>
<i>5 submissions</i>
</s></u></b>
<b id="sid_22220132" class="r-adult"><u><s><a href="/view/22220132/"><img alt="" title="Electric Feel - Part 3" src="//t.facdn.net/22220132@100-1483814445.jpg"/></a></s></u></b>
<b id="sid_22220113" class="r-adult"><u><s><a href="/view/22220113/"><img alt="" title="Electric Feel - Part 2" src="//t.facdn.net/22220113@100-1483814380.jpg"/></a></s></u></b>
<b id="sid_22220105" class="r-adult"><u><s><a href="/view/22220105/"><img alt="" title="Electric Feel - Part 1" src="//t.facdn.net/22220105@100-1483814324.jpg"/></a></s></u></b>
<b id="sid_22110126" class="r-adult"><u><s><a href="/view/22110126/"><img alt="" title="Round Two?" src="//t.facdn.net/22110126@100-1482804876.jpg"/></a></s></u></b>
</center>
</div>
</div>
<!-- comments ad -->
<div id="ad-extra-comments"></div>
<!-- END comments ad -->
<br/>
<table cellpadding="2" cellspacing="1" border="0" width="98%" class="maintable" align="center">
<tr>
<td valign="middle" align="left" width="90%" class="cat" height="1" style="padding: 4px">
<b>guns and roses</b> - by <a href="/user/puppymachine/">puppymachine</a>
</td>
<td valign="top" align="left" rowspan="2" width="10%" class="alt1">
<table cellpadding="3" cellspacing="1" border="0" width="100%">
<tr>
<td valign="top" align="left" class="alt1 stats-container">
&nbsp;<b>Submission information:</b><br/>
&nbsp;&nbsp;&nbsp;&nbsp; <b>Posted:</b> <span title="Jan 24th, 2017 10:20 AM" class="popup_date">some seconds ago</span><br/>
&nbsp;&nbsp;&nbsp;&nbsp; <b>Category:</b> All<br/>
&nbsp;&nbsp;&nbsp;&nbsp; <b>Theme:</b> All<br/>
&nbsp;&nbsp;&nbsp;&nbsp; <b>Species:</b> Unspecified / Any<br/>
&nbsp;&nbsp;&nbsp;&nbsp; <b>Gender:</b> Any<br/>
&nbsp;&nbsp;&nbsp;&nbsp; <b>Favorites:</b>
0 <br/>
&nbsp;&nbsp;&nbsp;&nbsp; <b>Comments:</b> 0<br />
&nbsp;&nbsp;&nbsp;&nbsp; <b>Views:</b> 2<br /><br />
&nbsp;<b>Image Specifications:</b><br/>
&nbsp;&nbsp;&nbsp;&nbsp; <b>Resolution:</b> 1180x1243<br /><br />
<div align="left" style="padding-left: 6px;">
<img alt="Adult rating" src="/themes/classic/img/labels/adult.gif" /> <br />
<br />
</div>
</td>
</tr>
</table>
</td>
</tr>
<tr>
<td valign="top" align="left" width="70%" class="alt1" style="padding:8px">
<a href="/user/puppymachine/"><img class="avatar" alt="puppymachine" src="//a.facdn.net/1482804755/puppymachine.gif"></a>
<br/><br/>
did this as a warm up today :) <br />
excessively invasive comments will be hidden </td>
</tr>
</table>
<br/>
</td>
</tr>
</table> </td>
</tr>
<tr>
<td valign="top" align="center">
</td>
</tr>
<tr>
<td valign="top" align="right">
<br />
<br/>
<div align="center">
<form name="myform" method="post" action="/view/22390847/" id="add_comment_form" align="center">
<input type="hidden" name="action" value="reply" id="form-action">
<input type="hidden" name="f" value="0"/>
<input type="hidden" name="replyto" id="form-replyto" value=""/>
<table cellpadding="3" cellspacing="1" width="70%" border="0" class="maintable">
<tr>
<td class="cat" align="center">
<B>Write a reply?</B>
</td>
</tr>
<tr>
<td class="alt1 addpad" align="center">
<table cellpadding="3" cellspacing="0" border="0" width="100%">
<tr>
<td valign="top">&nbsp;</td>
<td align="left"><img class="hand" src="/themes/classic/img/bbcode/b.gif" alt="Bold" onclick="performInsert($('JSMessage'), '[b]', '[/b]');"/>
<img class="hand" src="/themes/classic/img/bbcode/i.gif" alt="Italic" onclick="performInsert($('JSMessage'), '[i]', '[/i]');"/>
<img class="hand" src="/themes/classic/img/bbcode/u.gif" alt="Underlined" onclick="performInsert($('JSMessage'), '[u]', '[/u]');"/>
&nbsp;&nbsp;&nbsp;
<img class="hand" src="/themes/classic/img/bbcode/align_left.gif" alt="Align Left" onclick="performInsert($('JSMessage'), '[left]', '[/left]');"/>
<img class="hand" src="/themes/classic/img/bbcode/align_center.gif" alt="Align Center" onclick="performInsert($('JSMessage'), '[center]', '[/center]');"/>
<img class="hand" src="/themes/classic/img/bbcode/align_right.gif" alt="Align Right" onclick="performInsert($('JSMessage'), '[right]', '[/right]');"/>
</td>
</tr>
<tr>
<td style="width: 130px;" valign="top">
<table class="bpix tpix rpix lpix"> <tr>
<td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/tongue.png" style="cursor: pointer;" onclick="bbtag(':-p');" /></td><td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/cool.png" style="cursor: pointer;" onclick="bbtag(':cool:');" /></td><td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/wink.png" style="cursor: pointer;" onclick="bbtag(';-)');" /></td><td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/oooh.png" style="cursor: pointer;" onclick="bbtag(':-o');" /></td> </tr>
<tr>
<td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/smile.png" style="cursor: pointer;" onclick="bbtag(':-)');" /></td><td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/evil.png" style="cursor: pointer;" onclick="bbtag(':evil:');" /></td><td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/huh.png" style="cursor: pointer;" onclick="bbtag(':huh:');" /></td><td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/dunno.png" style="cursor: pointer;" onclick="bbtag(':whatever:');" /></td> </tr>
<tr>
<td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/angel.png" style="cursor: pointer;" onclick="bbtag(':angel:');" /></td><td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/badhairday.png" style="cursor: pointer;" onclick="bbtag(':badhair:');" /></td><td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/lmao.png" style="cursor: pointer;" onclick="bbtag(':lmao:');" /></td><td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/cd.png" style="cursor: pointer;" onclick="bbtag(':cd:');" /></td> </tr>
<tr>
<td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/crying.png" style="cursor: pointer;" onclick="bbtag(':cry:');" /></td><td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/dunno.png" style="cursor: pointer;" onclick="bbtag(':idunno:');" /></td><td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/embarrassed.png" style="cursor: pointer;" onclick="bbtag(':embarrassed:');" /></td><td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/gift.png" style="cursor: pointer;" onclick="bbtag(':gift:');" /></td> </tr>
<tr>
<td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/coffee.png" style="cursor: pointer;" onclick="bbtag(':coffee:');" /></td><td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/love.png" style="cursor: pointer;" onclick="bbtag(':love:');" /></td><td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/nerd.png" style="cursor: pointer;" onclick="bbtag(':isanerd:');" /></td><td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/note.png" style="cursor: pointer;" onclick="bbtag(':note:');" /></td> </tr>
<tr>
<td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/derp.png" style="cursor: pointer;" onclick="bbtag(':derp:');" /></td><td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/sarcastic.png" style="cursor: pointer;" onclick="bbtag(':sarcastic:');" /></td><td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/serious.png" style="cursor: pointer;" onclick="bbtag(':serious:');" /></td><td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/sad.png" style="cursor: pointer;" onclick="bbtag(':-(');" /></td> </tr>
<tr>
<td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/sleepy.png" style="cursor: pointer;" onclick="bbtag(':sleepy:');" /></td><td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/teeth.png" style="cursor: pointer;" onclick="bbtag(':teeth:');" /></td><td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/veryhappy.png" style="cursor: pointer;" onclick="bbtag(':veryhappy:');" /></td><td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/yelling.png" style="cursor: pointer;" onclick="bbtag(':yelling:');" /></td> </tr>
<tr>
<td width="25%" align="center" class="alt1" valign="top"><img alt="" src="/themes/classic/img/smilies/zipped.png" style="cursor: pointer;" onclick="bbtag(':zipped:');" /></td><td width="25%" align="center" class="alt1" valign="top">&nbsp;</td><td width="25%" align="center" class="alt1" valign="top">&nbsp;</td><td width="25%" align="center" class="alt1" valign="top">&nbsp;</td> </tr>
</table>
</td>
<td>
<textarea id="JSMessage" name="reply" style="width: 100%" rows="20" class="textarea"></textarea>
</td>
</tr>
</table>
<input class="button" type="submit" name="mysubmit" value="Add Reply" onclick="submit_form()" />
</td>
</tr>
</table>
</form>
</div>
<script type="text/javascript">
Queue.add(function(){LazyLoad.load("/themes/classic/js/quick-replyto.js?u=2017011903", function(){})});
</script>
</td>
</tr>
</table>
</div>
<script type="text/javascript">
var ctrl_key_hold = false;
var shift_key_hold = false;
var comment_edit_window_sec = 300;
Queue.add(function(){
document.observe('keydown', function(evt){
if(evt.keyCode == 16) {
shift_key_hold = true;
}
if(evt.keyCode == 17) {
ctrl_key_hold = true;
}
});
document.observe('keyup', function(evt){
var selected_tags = $$('#keywords .keyword-active');
var search_keywords = '';
var delimiter = shift_key_hold ? ' | ' : ' ';
for(var i=0, cnt=selected_tags.length; i<cnt; i++) {
search_keywords += selected_tags[i].innerHTML.replace(/[^-_\w\d ]+/, '') + delimiter;
}
if(search_keywords) {
search_keywords = search_keywords.substr(0, search_keywords.length-(shift_key_hold?3:1));
var url = '/search/@keywords '+escape(search_keywords);
window.location.href = url;
}
shift_key_hold = false;
ctrl_key_hold = false;
});
document.observe('click', function(evt){
if(!evt.findElement('#keywords')) {
return true;
}
var elm = evt.element();
if(elm.tagName.toLowerCase() != 'a') {
return true;
}
if(shift_key_hold || ctrl_key_hold){
evt.stop();
elm.toggleClassName('keyword-active');
}
});
// hide comment edit links upon comment edit window expiring
edit_links_hide_handler();
});
Queue.add(function(){
$$('span.popup_date').each(function(elm){
elm.observe('click',function(evt){
var elm = evt.element();
var tmp=elm.title;
elm.title=elm.innerHTML;
elm.innerHTML=tmp;
})
});
});
</script>
<br/>
</div>
<div class="footer">
<div class="ads" style="height: auto !important;">
<span id="ad-10" class="ad hidden" style="display:inline-block; margin-bottom: 10px;"></span>
<img class="falogo" src="/themes/classic/img/banners/fa_logo.png">
<div class="ftr">
<div id="ad-6" class="ad hidden spacer"></div>
<div id="ad-8" class="ad hidden"></div>
</div>
</div>
<br />
<p>
<a href="/advertising">Advertising</a> |
<a href="http://twitter.com/furaffinity">Twitter</a> |
<a href="http://www.facebook.com/furaffinity"> Facebook</a> |
<a href="/tos">Terms of Service</a> |
<a href="/coc">Code of Conduct</a> |
<a href="/aup">Acceptable Upload Policy</a>
</p>
Fur Affinity is &copy; 2005-2017 IMVU<br /><br />
<center>
12189 <b><span title="Measured in the last 900 seconds">Users online</span></b> &mdash; 3354 <b>guests</b> and 8835 <b>registered</b>
<!-- Online Counter Last Update: Tue, 24 Jan 2017 10:18:41 -0500 -->
</center>
Server Local Time: Jan 24th, 2017 10:20 AM <br />
Page generated in 0.011 seconds [ 30.1% PHP, 69.9% SQL ] (21 queries) </div>
<script type="text/javascript">
var server_timestamp = 1485271225;
var client_timestamp = ((new Date()).getTime())/1000;
var server_timestamp_delta = server_timestamp - client_timestamp;
var sfw_cookie_name = 'sfw';
var news_cookie_name = 'n';
</script>
<script type="text/javascript" src="/themes/classic/js/prototype.js"></script>
<script type="text/javascript" src="/themes/classic/js/script.js?u=2017011903"></script>
<script type="text/javascript">
//
// Load and init FA ads
//
// non-swf
var ad_zone_ids = [2,4,6,8,10];
var ad_zone_info_src = '//rv.furaffinity.net/www/delivery/spc.php?zones='+(ad_zone_ids.join('|'))+'&r='+((new Date()).getTime());
try {
if(window.location) {
ad_zone_info_src += '&loc='+escape(window.location);
}
if(document.referrer) {
ad_zone_info_src += '&referer='+escape(document.referrer);
}
} catch(e){
console && console.log && console.log('JS Error caught: %o', e);
}
LazyLoad.load(ad_zone_info_src, function(){
try {
if(typeof(OA_output) == 'undefined') {
return;
}
var tmp_output, ad_block;
for(var i=0, cnt=ad_zone_ids.length; i<cnt; i++) {
tmp_output = OA_output[ad_zone_ids[i]];
if(typeof(tmp_output) != 'undefined') {
// make URLs protocol relative
tmp_output = tmp_output.replace(/https?:\/\/rv\./g, '//rv.');
// remove impression tracking
//tmp_output = tmp_output.replace(/<div\ id='beacon.+?<\/div>/g, '');
ad_block = $('ad-'+ad_zone_ids[i]);
if(ad_block) {
ad_block.innerHTML = tmp_output;
ad_block.removeClassName('hidden');
}
}
}
} catch(e) {
console && console.log && console.log('JS Error caught: %o', e);
}
});
//
// load and init additional ads
//
(function(w, d) {
// ad zone definition, general rating
w.ad_data = {
'top': {
id : 'ad-extra-flat',
zone: '/6017/FA_GA',
size: [[728, 90]]
},
'submission_flat': {
id : 'ad-extra-comments',
zone: '/6017/FA_GA/Low_Value_GA_728_90',
size: [[728, 90]]
},
'submission_square': {
id : 'ad-extra-submission-square',
zone: '/6017/FA_GA/FA_GA_Web_300_250',
size: [[300, 250]]
}
};
// zone override for mature rating
w.ad_data['top'].zone = '/6017/FA_MA';
w.ad_data['submission_flat'].zone = '/6017/FA_MA';
w.ad_data['submission_square'].zone = '/6017/FA_MA/FA_MA_Web_300_250';
// init ads
w.googletag = w.googletag || {cmd: []};
w.AdBridg = w.AdBridg || {cmd: []};
w.AdBridg.cmd.push(function() {
for(var zone_handle in w.ad_data) {
if(w.ad_data.hasOwnProperty(zone_handle)) {
var data = w.ad_data[zone_handle];
var elm = d.getElementById(data.id);
if(elm) {
w.AdBridg.defineSlot(data.zone, data.size, data.id);
w.AdBridg.display(data.id);
}
}
}
w.googletag.pubads().enableSingleRequest();
w.googletag.pubads().collapseEmptyDivs();
w.AdBridg.serve();
});
// load google ad code
LazyLoad.load(['//www.googletagservices.com/tag/js/gpt.js'], function() {
if(typeof(googletag._vars_) === 'undefined') {
// google ad code has been blocked
// hide all ad containers
for(var zone_handle in w.ad_data) {
if(w.ad_data.hasOwnProperty(zone_handle)) {
var elm = d.getElementById(w.ad_data[zone_handle].id);
if(elm) {
elm.className = elm.className + ' hidden';
}
}
}
}
});
// If AdBridg is blocked or unable to load (!exists), all API
// commands are replaced with fallbacks to native GPT commands.
LazyLoad.load(['//prod.adspsp.com/adb.4216892.min.js'], function() {
if (w.AdBridg.exists) { return; }
d.createElement('IMG').src = '//adspsp.com:' + ('https:' == d.location.protocol ? '5051' : '5050') + '/pt/4216892/3/1/?e=0';
w.googletag.cmd.push(function() {
var dq = [], es;
dq.process = function() { while(dq.length) { w.googletag.display(dq.shift()); }}
w.AdBridg.defineSlot = function(p, s, d) { w.googletag.defineSlot(p, s, d).addService(w.googletag.pubads()); }
w.AdBridg.display = function(d) { dq.push(d); if (es) { dq.process(); }}
w.AdBridg.refresh = function(s, o) { w.googletag.pubads().refresh(s, o); }
w.AdBridg.render = function() { }
w.AdBridg.serve = function() { w.googletag.enableServices(); es=1; dq.process(); }
while (w.AdBridg.cmd.length) { w.googletag.cmd.push(w.AdBridg.cmd.shift()); }
w.AdBridg.cmd = w.googletag.cmd;
});
});
})(window, document);
</script>
<script type="text/javascript">
//<!-- Start Google Analytics -->
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-60038462-1', 'auto');
ga('send', 'pageview');
// <!-- End Google Analytics -->
// <!-- Quantcast Tag -->
var _qevents = _qevents || [];
(function() {
var elem = document.createElement('script');
elem.src = (document.location.protocol == "https:" ? "https://secure" : "http://edge") + ".quantserve.com/quant.js";
elem.async = true;
elem.type = "text/javascript";
var scpt = document.getElementsByTagName('script')[0];
scpt.parentNode.insertBefore(elem, scpt);
})();
_qevents.push({qacct:"p-8fZNjMQsH1Ews"});
</script>
<noscript><div style="display:none;"><img src="//pixel.quantserve.com/pixel/p-8fZNjMQsH1Ews.gif" border="0" height="1" width="1" alt="Quantcast"/></div></noscript>
<!-- End Quantcast tag -->
<script async nonce="" type="text/javascript" id="mini-profiler" src="/mini-profiler-resources/includes.js?v=35a79b300ab5afa978cb59af0b05e059" data-css-url="/mini-profiler-resources/includes.css?v=35a79b300ab5afa978cb59af0b05e059" data-version="35a79b300ab5afa978cb59af0b05e059" data-path="/mini-profiler-resources/" data-current-id="68kfhxefomgq5dy9zrtd" data-ids="68kfhxefomgq5dy9zrtd,jx7npcfwgx6pggmhwf1i,yp95c7z2ir4jx5g9uu6c,uac5p0qgqg6a70i3930a,o91guk8p1ojyo4dg0cp9" data-horizontal-position="right" data-vertical-position="top" data-trivial="false" data-children="false" data-max-traces="20" data-controls="false" data-total-sql-count="false" data-authorized="true" data-toggle-shortcut="alt+p" data-start-hidden="false" data-collapse-results="true" data-html-container="body" data-hidden-custom-fields="" data-turbo-permanent="false"></script>
</body>
</html>