This repository has been archived on 2022-07-01. You can view files and clone it, but cannot push or open issues or pull requests.
-/Download/​​​ A Mathematical Curiosity_files/blog_router.js

583 lines
15 KiB
JavaScript

/**
* blog_router.js Holds the router for Blogs. It gets the ball rolling once the page is loaded.
*
*/
AoPS.Blog = (function (Blog) {
var Lang = AoPS.Community.Lang,
Constants = AoPS.Community.Constants;
Blog.Router = AoPS.Community.Utils.routerBase.extend({
routes: {
"": "constructAoPSMasterCollection",
},
initialize: function (options) {
var self = this;
this.models = {};
this.models.search_categories = {};
this.models.master = options.master;
// Will this take care of everything?
this.myPage = AoPS.Page.constructPage("wrapper");
this.route(
/((?:c\d+)?(?:t\d+)?(?:f\d+)?(?:h\d+)?(?:p\d+)?(?:s\d+)?)/,
"parseEncodedUrl"
);
this.route(/((?:c\d+)(.*)\/(.*))/, "search");
this.bind("route", this.onFinishRoute);
this.first_pass = true;
// If there is a preloaded topic for directly navigating to a topic on page load,
// then this will be set to the preloaded id when the preloaded data is parsed.
// We then set this back to -1 once the preloaded topic is set as the focus_topic.
// This flag is used to prevent a redundant Ajax call (change_focus_topic) when the topic is built.
this.preloaded_topic_id = -1;
Backbone.on("logged_out", this.onAjaxDetectLogout);
Backbone.on("logged_in", this.onAjaxDetectLogin);
if (AoPS.bootstrap_data.hasOwnProperty("blog_base")) {
this.models.blog = this.models.master.constructNewCategory(
AoPS.bootstrap_data.blog_base
);
// TODO : check that topics exist
_.each(AoPS.bootstrap_data.blog_topics.topics_data, function (topic) {
self.models.master.processPotentialNewTopic(topic, true);
});
if (String(AoPS.bootstrap_data.blog_topics.no_more_topics) === "1") {
this.models.blog.set("all_topics_loaded", true);
}
} else {
// TODO : Throw error, you are in the wrong place.
}
this.models.master.set("focus_category", this.models.blog);
AoPS.Community.Utils.activateLatexOnclick();
$("body").addClass("community");
},
goToTopic: function (args) {
var url = "#";
if (args.hasOwnProperty("category_id")) {
url += "c" + args.category_id;
}
url += "h" + args.topic_id;
this.navigate(url, {
trigger: true,
replace: true, //(args.hasOwnProperty('replace') ? args.replace : false)
});
},
buildBreadcrumbs: function (data) {
var input = '<a href="/community">Community</a> &raquo; ';
input +=
'<a href="/community/c' +
Constants.aops_blogroll_id +
'">Blogs</a> &raquo; '; // TODO : POINT THIS AT THE BLOGS CATEGORY
input +=
'<a data-blog href="/community/c' +
data.blog.category_id +
'">' +
data.blog.name +
"</a>";
if (data.hasOwnProperty("topic")) {
input +=
' &raquo; <a data-blog href="/community/c' +
data.blog.category_id +
"h" +
data.topic.topic_id +
'">' +
data.topic.title +
"</a>";
}
$("#left_navigation_box").html(input);
},
onFinishRoute: function (name, args) {
this.previous_route = name;
if (!this.keep_fullscreen_mode) {
$("body").removeClass("fullscreen");
}
$(window).trigger("resize");
this.keep_fullscreen_mode = false;
},
/**
* Search this blog
*
* @param string of form /c<id>/<encoded search text>
**/
search: function (url) {
var pieces, str, coords, search_settings;
pieces = url.split("/");
if (pieces[1].length === 0) {
this.parseEncodedUrl(pieces[0]);
return;
}
str = pieces[1];
coords = {
category_id: 0,
tag_id: 0,
tag_forum_id: 0,
search_text: str,
is_search: true,
};
if (this.models.search_categories.hasOwnProperty(str)) {
coords.category = this.models.search_categories[str].category;
coords.topic_list = this.models.search_categories[str].topic_list;
} else {
search_settings = {
blog_id: this.models.blog.get("category_id"),
search_text: decodeURIComponent(pieces[1]),
};
coords.category =
this.models.master.buildSearchCategory(search_settings);
this.models.search_categories[str] = {
category: coords.category,
};
}
this.startConstructingPage(coords);
},
/** Start functions for building pages that have ids encoded in URL **/
/**
* parseEncodedUrl parses the url and then routes us to the appropriate function
* to construct the page once we're sure we have some topics to play with.
* (We'll likely handle that "check for some topics" differently when we build the real system.)
*
* @param url: everything after # in the url.
*
*/
parseEncodedUrl: function (url) {
var coords = {
topic: null,
category: null,
};
function extractValueFromUrl(stub, url_in) {
var match_array;
if (_.isNull(url_in)) {
return 0;
}
match_array = url_in.match(new RegExp(stub + "(\\d+)"));
if (_.isNull(match_array)) {
return 0;
} else {
return parseInt(match_array[1]);
}
}
/**
* If we're hitting a category, topic, or post on page load (first_pass through the
* router), then we check the preload_cmty_data property of bootstrap to see if
* there's anything we can preload, and to see if we need to rewrite the url.
*
* There are later url rewrite checks, but they shouldn't ever be triggered.
* I'm leaving them in, in case down the line we introduce ways that users can
* travel down routes missing properties (like topic id with no category id) in
* some way other than the initial page load.
**/
if (this.first_pass) {
if (AoPS.bootstrap_data.hasOwnProperty("preload_cmty_data")) {
this.first_pass = false;
if (this.parsePreloadedData()) {
return;
}
}
}
// Fill coords object from url
_.each(this.url_parameters, function (item) {
coords[item.property] = extractValueFromUrl(item.letter, url);
});
coords.is_search = false;
this.startConstructingPage(coords);
},
startConstructingPage: function (coords) {
this.myPage.clearPage();
this.myPage.showElement({
id: "blog-top",
constructor: _.bind(function () {
return new AoPS.Community.Views.CategoryCellBlogHeading({
model: this.models.blog,
});
}, this),
});
if (coords.is_search) {
if (coords.hasOwnProperty("topic_list")) {
this.prepareBlogFront(coords);
} else {
this.checkTopicListThenContinue(coords);
}
} else if (coords.topic_id > 0) {
this.prepareBlogTopic(coords);
} else {
this.prepareBlogFront(coords);
}
return;
},
/**
* The sidebar and the clearfix are needed everywhere.
**/
finishBuildingPage: function (coords) {
this.myPage.showElement({
id: "blog-sidebar",
constructor: _.bind(function () {
return new AoPS.Community.Views.BlogSidebar({
model: this.models.blog,
});
}, this),
location: "content",
});
this.myPage.showElement({
id: "blog-clearfix",
type: "jQuery_object",
jQuery_object: $('<div class="clear"></div>'),
location: "content",
});
},
prepareBlogTopic: function (coords) {
var self = this;
coords.topic = this.models.master.fetchTopicById(coords.topic_id);
if (_.isNull(coords.topic) && !_.isUndefined(coords.topic)) {
this.listenTo(
this.models.master,
"single_topic_load",
_.bind(function (obj) {
self.myPage.hideLoader();
if (obj.topic_id === coords.topic_id) {
// We found the topic
this.stopListening(this.models.master, "single_topic_load");
if (obj.ok) {
coords.topic = obj.topic;
this.buildBlogTopic(coords);
} else {
// Database fetch failed dues to lack of permissions
if (obj.error_code === "E_NO_PERMISSION") {
if (AoPS.session.logged_in) {
this.throwError(
Lang["router-err-no-topic-perms-logged-in"]
);
} else {
this.throwError(
Lang["router-err-no-topic-perms-logged-out"]
);
}
} else if (obj.error_code === "E_NO_SUCH_TOPIC") {
this.throwError(Lang["router-err-no-topic"]);
}
}
}
}, this)
);
this.myPage.showLoader();
this.models.master.fetchTopicFromDb({
topic_id: coords.topic_id,
});
return;
} else {
// If we have a topic, process all the tags on it (add them to their categories).
this.buildBlogTopic(coords);
return;
}
},
buildBlogTopic: function (coords) {
var topic_page_element,
blog_name = this.models.blog.get("category_name"),
topic_title = coords.topic.get("topic_title");
this.myPage.setTitle(
_.unescape(blog_name) + " : " + _.unescape(topic_title)
);
coords.topic.set("category", this.models.blog);
this.models.master.set("focus_topic", coords.topic, {
suppress_ajax: coords.topic_id === this.preloaded_topic_id,
fetch_all: true,
});
this.preloaded_topic_id = -1;
/**
* If we navigate in the blog to this top, the scrollbar might be set somewhere
* far down the page
**/
$(window).scrollTop(0);
topic_page_element = this.myPage.showElement({
id: "blog-topic-" + coords.topic_id,
constructor: _.bind(function () {
return new AoPS.Community.Views.BlogTopicFull({
model: coords.topic,
post_id: coords.post_id,
});
}, this),
on_add_settings: {
post_id: coords.post_id,
},
location: "content",
});
this.finishBuildingPage(coords);
// Reply triggered
if (coords.state === 3) {
if (coords.topic.getPermission("c_can_reply")) {
topic_page_element.dom_element.trigger("open_reply");
}
}
this.buildBreadcrumbs({
blog: {
category_id: this.models.blog.get("category_id"),
name: blog_name,
},
topic: {
title: topic_title,
topic_id: coords.topic.get("topic_id"),
},
});
},
buildBlogFrontPage: function (coords) {
var topic_list,
blog_name = this.models.blog.get("category_name");
if (coords.hasOwnProperty("topic_list")) {
topic_list = coords.topic_list;
} else {
topic_list = this.models.master.fetchFilteredTopicList({
category: this.models.blog,
category_id: this.models.blog.get("category_id"),
tag_ids: [],
});
if (coords.is_search) {
this.models.search_categories[coords.search_text].topic_list =
topic_list;
}
}
this.myPage.hideLoader();
this.myPage.setTitle(_.unescape(blog_name));
this.myPage.showElement({
id:
"blog-topics" +
coords.tag_id +
(coords.hasOwnProperty("search_text") ? coords.search_text : ""),
constructor: _.bind(function () {
return new AoPS.Community.Views.TopicsListBlog({
collection: topic_list,
blog: this.models.blog,
});
}, this),
location: "content",
});
this.models.master.set("focus_topic", null);
this.buildBreadcrumbs({
blog: {
category_id: this.models.blog.get("category_id"),
name: blog_name,
},
});
this.finishBuildingPage(coords);
},
/***
* Construct the topic list that we use for the page. If the topic list
* has no topics, then we go get some topics before building.
*
* @param coords: settings used to build the page.
*/
checkTopicListThenContinue: function (coords) {
var self = this;
// Construct or fetch the Community.Models.FilteredTopicList that matches
// the set of parameters we seek.
// Gets called twice for topics that are shorn of tags, but we can live with that, I think
// That will be rare. We could do a property check, though. Might be more expensive than it's worth.
coords.topic_list = this.models.master.fetchFilteredTopicList({
category:
coords.hasOwnProperty("category") && !_.isNull(coords.category)
? coords.category
: this.models.blog,
category_id: coords.category_id,
tag_ids:
coords.tag_id > 0
? [
{
tag_id: coords.tag_id,
tag_forum_id: coords.tag_forum_id,
},
]
: [], //currently assumes at most one tag_id.
});
// Not enough topics in this list; I'm gonna get more.
if (
coords.topic_list.length <=
AoPS.Community.Constants.min_topic_list_initial_length &&
!coords.topic_list.all_topics_fetched
) {
this.myPage.showLoader();
coords.topic_list.fetchMoreTopics({
onFinish: _.bind(function () {
this.buildBlogFrontPage(coords);
}, this),
onError: function (data) {
var msg;
if (data.error_code === "E_AJAX_CANCEL") {
return;
}
if (
typeof Lang["initial-fetch-err-" + data.error_code] === "string"
) {
msg = AoPS.Community.Utils.formatString(
Lang["initial-fetch-blog-err-" + data.error_code],
[coords.category_id]
);
console.log(msg);
console.log(coords.category_id);
} else {
msg = Lang["unexpected-error-code"] + data.error_code;
}
// Force page reload after error
$("a[data-blog]").removeAttr("data-blog");
self.throwError(msg);
},
});
} else {
this.buildBlogFrontPage(coords);
}
},
prepareBlogFront: function (coords) {
if (coords.tag_id > 0) {
coords.tag_forum_id = coords.category_id;
this.checkTopicListThenContinue(coords);
} else {
this.buildBlogFrontPage(coords);
}
},
setBreadcrumbs: function (crumbs) {
crumbs = _.map(crumbs, function (crumb) {
crumb.data = "data-cmty";
return crumb;
});
this.myPage.setBreadcrumbs(this.breadcrumb_base.concat(crumbs));
},
onAjaxDetectLogout: function () {
if (!AoPS.login.user_clicked_logout) {
AoPS.Ui.buildLoginConfirm(Lang["unexpected-logout"]);
AoPS.login.onUserAjaxLogout();
}
},
onAjaxDetectLogin: function () {
if (!AoPS.login.user_clicked_login) {
document.location.reload(true);
}
},
onAjaxDetectLoginChange: function () {
//if (
},
});
$(window).load(function () {
var master, app;
master = new AoPS.Community.Models.Master();
app = new Blog.Router({
master: master,
});
$("#page-wrapper").append(app.myPage.el);
Backbone.history.start({
pushState: true,
root: "community",
});
$("a.blog-login").on("click", function (e) {
AoPS.login.display();
e.stopPropagation();
e.preventDefault();
});
$("a.blog-logout").on("click", function (e) {
AoPS.login.logout(e);
e.stopPropagation();
e.preventDefault();
});
$(document).on("click", "a[data-blog]", function (e) {
e.stopPropagation();
e.preventDefault();
Backbone.history.navigate($(this).attr("href").substring(10), {
trigger: true,
});
});
if (AoPS.session.logged_in) {
$("#blog-subscribe").on("click", function (e) {
AoPS.Community.Views.toggleBlogSubscription({
blog: app.models.blog,
user: master.get("current_user"),
});
e.stopPropagation();
e.preventDefault();
});
}
/* FEED MUST COME AFTER Backbone start!*/
// experimenting with delay to let the page render before Feed is built.
// If this seems to work well, we'll want to animate the feed in.
setTimeout(function () {
var feed = new AoPS.Community.Models.Feed({
master: master,
}),
feed_view = new AoPS.Feed.Views.FeedMaster({
model: feed,
});
$("body").append(feed_view.$el);
}, 100);
// console.log('end page ' + (new Date().getTime() - start));
// });
});
return Blog;
})(AoPS.Blog || {});