/** * 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 = 'Community » '; input += 'Blogs » '; // TODO : POINT THIS AT THE BLOGS CATEGORY input += '' + data.blog.name + ""; if (data.hasOwnProperty("topic")) { input += ' » ' + data.topic.title + ""; } $("#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/ **/ 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: $('
'), 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 || {});