334 lines
9.8 KiB
JavaScript
Executable File
334 lines
9.8 KiB
JavaScript
Executable File
PostLoader = function()
|
|
{
|
|
document.on("viewer:need-more-thumbs", this.need_more_post_data.bindAsEventListener(this));
|
|
document.on("viewer:perform-search", this.perform_search.bindAsEventListener(this));
|
|
|
|
this.hashchange_tags = this.hashchange_tags.bind(this);
|
|
UrlHash.observe("tags", this.hashchange_tags);
|
|
|
|
this.cached_posts = new Hash();
|
|
this.cached_pools = new Hash();
|
|
this.sample_preload_container = null;
|
|
this.preloading_sample_for_post_id = null;
|
|
|
|
this.load({results_mode: "center-on-current"});
|
|
}
|
|
|
|
PostLoader.prototype.need_more_post_data = function()
|
|
{
|
|
/* We'll receive this message often once we're close to needing more posts. Only
|
|
* start loading more data the first time. */
|
|
if(this.loaded_extended_results)
|
|
return;
|
|
|
|
this.load({extending: true});
|
|
}
|
|
|
|
|
|
/*
|
|
* This is a response time optimization. If we know the sample URL of what we want to display,
|
|
* we can start loading it from the server without waiting for the full post.json response
|
|
* to come back and tell us. This saves us the time of a round-trip before we start loading the
|
|
* image. The common case is if the user was on post and clicked on a link with "use
|
|
* post browser" enabled. This allows us to start loading the image immediately, without waiting
|
|
* for any other network activity.
|
|
*
|
|
* We only do this for the sample image, to get a head-start loading it. This is safe because
|
|
* the image URLs are immutable (or effectively so). The rest of the post information isn't cached.
|
|
*/
|
|
PostLoader.prototype.preload_sample_image = function()
|
|
{
|
|
var post_id = UrlHash.get("post-id");
|
|
if(this.preloading_sample_for_post_id == post_id)
|
|
return;
|
|
this.preloading_sample_for_post_id = post_id;
|
|
|
|
if(this.sample_preload_container)
|
|
{
|
|
this.sample_preload_container.destroy();
|
|
this.sample_preload_container = null;
|
|
}
|
|
|
|
if(post_id == null)
|
|
return;
|
|
|
|
/* If this returns null, the browser doesn't support this. */
|
|
var cached_sample_urls = Post.get_cached_sample_urls();
|
|
if(cached_sample_urls == null)
|
|
return;
|
|
|
|
if(!(String(post_id) in cached_sample_urls))
|
|
return;
|
|
var sample_url = cached_sample_urls[String(post_id)];
|
|
|
|
/* If we have an existing preload_container, just add to it and allow any other
|
|
* preloads to continue. */
|
|
debug("Advance preloading sample image for post " + post_id);
|
|
this.sample_preload_container = new PreloadContainer();
|
|
this.sample_preload_container.preload(sample_url);
|
|
}
|
|
|
|
PostLoader.prototype.server_load_pool = function()
|
|
{
|
|
if(this.result.pool_id == null)
|
|
return;
|
|
|
|
if(!this.result.disable_cache)
|
|
{
|
|
var pool = this.cached_pools.get(this.result.pool_id);
|
|
if(pool)
|
|
{
|
|
this.result.pool = pool;
|
|
this.request_finished();
|
|
return;
|
|
}
|
|
}
|
|
|
|
new Ajax.Request("/pool/show.json", {
|
|
parameters: { id: this.result.pool_id },
|
|
method: "get",
|
|
onCreate: function(resp) {
|
|
this.current_ajax_requests.push(resp.request);
|
|
}.bind(this),
|
|
|
|
onComplete: function(resp) {
|
|
this.current_ajax_requests = this.current_ajax_requests.without(resp.request);
|
|
this.request_finished();
|
|
}.bind(this),
|
|
|
|
onSuccess: function(resp) {
|
|
if(this.current_ajax_requests.indexOf(resp.request) == -1)
|
|
return;
|
|
|
|
this.result.pool = resp.responseJSON;
|
|
this.cached_pools.set(this.result.pool_id, this.result.pool);
|
|
}.bind(this)
|
|
});
|
|
}
|
|
|
|
PostLoader.prototype.server_load_posts = function()
|
|
{
|
|
var tags = this.result.tags;
|
|
|
|
// Put holds:false at the beginning, so the search can override it. Put limit: at
|
|
// the end, so it can't.
|
|
var search = "holds:false " + tags + " limit:" + this.result.post_limit;
|
|
|
|
if(!this.result.disable_cache)
|
|
{
|
|
var results = this.cached_posts.get(search);
|
|
if(results)
|
|
{
|
|
this.result.posts = results;
|
|
|
|
/* Don't Post.register the results when serving out of cache. They're already
|
|
* registered, and the data in the post registry may be more current than the
|
|
* cached search results. */
|
|
this.request_finished();
|
|
return;
|
|
}
|
|
}
|
|
|
|
new Ajax.Request("/post.json", {
|
|
parameters: {
|
|
tags: search,
|
|
api_version: 2,
|
|
filter: 1,
|
|
include_tags: 1,
|
|
include_votes: 1,
|
|
include_pools: 1
|
|
},
|
|
method: "get",
|
|
|
|
onCreate: function(resp) {
|
|
this.current_ajax_requests.push(resp.request);
|
|
}.bind(this),
|
|
|
|
onComplete: function(resp) {
|
|
this.current_ajax_requests = this.current_ajax_requests.without(resp.request);
|
|
this.request_finished();
|
|
}.bind(this),
|
|
|
|
onSuccess: function(resp) {
|
|
if(this.current_ajax_requests.indexOf(resp.request) == -1)
|
|
return;
|
|
|
|
var resp = resp.responseJSON;
|
|
this.result.posts = resp.posts;
|
|
|
|
Post.register_resp(resp);
|
|
|
|
this.cached_posts.set(search, this.result.posts);
|
|
}.bind(this),
|
|
|
|
onFailure: function(resp) {
|
|
var error = "error " + resp.status;
|
|
if(resp.responseJSON)
|
|
error = resp.responseJSON.reason;
|
|
|
|
notice("Error loading posts: " + error);
|
|
this.result.error = true;
|
|
}.bind(this)
|
|
});
|
|
}
|
|
|
|
PostLoader.prototype.request_finished = function()
|
|
{
|
|
if(this.current_ajax_requests.length)
|
|
return;
|
|
|
|
/* Event handlers for the events we fire below might make requests back to us. Save and
|
|
* clear this.result before firing the events, so that behaves properly. */
|
|
var result = this.result;
|
|
this.result = null;
|
|
|
|
/* If server_load_posts hit an error, it already displayed it; stop. */
|
|
if(result.error != null)
|
|
return;
|
|
|
|
/* If we have no search tags (result.tags == null, result.posts == null), then we're just
|
|
* displaying a post with no search, eg. "/post/browse#12345". We'll still fire off the
|
|
* same code path to make the post display in the view. */
|
|
var new_post_ids = [];
|
|
if(result.posts != null)
|
|
{
|
|
for(var i = 0; i < result.posts.length; ++i)
|
|
new_post_ids.push(result.posts[i].id);
|
|
}
|
|
|
|
document.fire("viewer:displayed-pool-changed", { pool: result.pool });
|
|
document.fire("viewer:searched-tags-changed", { tags: result.tags });
|
|
|
|
/* Tell the thumbnail viewer whether it should allow scrolling over the left side. */
|
|
var can_be_extended_further = true;
|
|
|
|
/* If we're reading from a pool, we requested a large block already. */
|
|
if(result.pool)
|
|
can_be_extended_further = false;
|
|
|
|
/* If we're already extending, don't extend further. */
|
|
if(result.load_options.extending)
|
|
can_be_extended_further = false;
|
|
|
|
/* If we received fewer results than we requested we're at the end of the results,
|
|
* so don't waste time requesting more. */
|
|
if(new_post_ids.length < result.post_limit)
|
|
{
|
|
debug("Received posts fewer than requested (" + new_post_ids.length + " < " + result.post_limit + "), clamping");
|
|
can_be_extended_further = false;
|
|
}
|
|
|
|
/* Now that we have the result, update the URL hash. Firing loaded-posts may change
|
|
* the displayed post, causing the post ID in the URL hash to change, so use set_deferred
|
|
* to help ensure these happen atomically. */
|
|
UrlHash.set_deferred({tags: result.tags});
|
|
|
|
document.fire("viewer:loaded-posts", {
|
|
tags: result.tags, /* this will be null if no search was actually performed (eg. URL with a post-id and no tags) */
|
|
post_ids: new_post_ids,
|
|
pool: result.pool,
|
|
extending: result.load_options.extending,
|
|
can_be_extended_further: can_be_extended_further,
|
|
load_options: result.load_options
|
|
});
|
|
}
|
|
|
|
/* If extending is true, load a larger set of posts. */
|
|
PostLoader.prototype.load = function(load_options)
|
|
{
|
|
if(!load_options)
|
|
load_options = {}
|
|
|
|
var disable_cache = load_options.disable_cache;
|
|
var extending = load_options.extending;
|
|
|
|
var tags = load_options.tags;
|
|
if(tags == null)
|
|
tags = UrlHash.get("tags");
|
|
|
|
/* If neither a search nor a post-id is specified, set a default search. */
|
|
if(!extending && tags == null && UrlHash.get("post-id") == null)
|
|
{
|
|
UrlHash.set({tags: ""});
|
|
|
|
/* We'll receive another hashchange message for setting "tags". Don't load now or we'll
|
|
* end up loading twice. */
|
|
return;
|
|
}
|
|
|
|
debug("PostLoader.load(" + extending + ", " + disable_cache + ")");
|
|
|
|
this.preload_sample_image();
|
|
|
|
this.loaded_extended_results = extending;
|
|
|
|
/* Discard any running AJAX requests. */
|
|
this.current_ajax_requests = [];
|
|
|
|
this.result = {};
|
|
this.result.load_options = load_options;
|
|
this.result.tags = tags;
|
|
this.result.disable_cache = disable_cache;
|
|
|
|
if(this.result.tags == null)
|
|
{
|
|
/* If no search is specified, don't run one; return empty results. */
|
|
this.request_finished();
|
|
return;
|
|
}
|
|
|
|
/* See if we have a pool search. This only checks for pool:id searches, not pool:*name* searches;
|
|
* we want to know if we're displaying posts only from a single pool. */
|
|
var pool_id = null;
|
|
this.result.tags.split(" ").each(function(tag) {
|
|
var m = tag.match(/^pool:(\d+)/);
|
|
if(!m)
|
|
return;
|
|
pool_id = parseInt(m[1]);
|
|
});
|
|
|
|
/* If we're loading from a pool, load the pool's data. */
|
|
this.result.pool_id = pool_id;
|
|
|
|
/* Load the posts to display. If we're loading a pool, load all posts (up to 1000);
|
|
* otherwise set a limit. */
|
|
var limit = extending? 1000:100;
|
|
if(pool_id != null)
|
|
limit = 1000;
|
|
this.result.post_limit = limit;
|
|
|
|
|
|
/* Make sure that request_finished doesn't consider this request complete until we've
|
|
* actually started every request. */
|
|
this.current_ajax_requests.push(null);
|
|
|
|
this.server_load_pool();
|
|
this.server_load_posts();
|
|
|
|
this.current_ajax_requests = this.current_ajax_requests.without(null);
|
|
this.request_finished();
|
|
}
|
|
|
|
PostLoader.prototype.hashchange_tags = function()
|
|
{
|
|
var tags = UrlHash.get("tags");
|
|
|
|
if(tags == this.last_seen_tags)
|
|
return;
|
|
this.last_seen_tags = tags;
|
|
|
|
debug("changed tags");
|
|
this.load();
|
|
}
|
|
|
|
PostLoader.prototype.perform_search = function(event)
|
|
{
|
|
var tags = event.memo.tags;
|
|
this.last_seen_tags = tags;
|
|
var results_mode = event.memo.results_mode || "center-on-first";
|
|
debug("do search: " + tags);
|
|
|
|
this.load({tags: tags, results_mode: results_mode});
|
|
}
|
|
|