// lib/jquery.min.js // @include lib/timer.js // @include lib/highcharts.js // @include common.js // @include components/commentsObj.js // @include components/ghostText.js // @include tf/fixFilter.js // @include tf/mutex.js // @include tf/status.js function initArticles(areaDivId, popupDivId, controller, commentAreaDivId, commentsDivId, postDivId, areaExpanded) { var loadedContext, // tells whether we're showing all articles for this filter state, or just the first 3 showingMoreArticles = false, // tells whether we're filtering the article list to show only those that apply to the current driver filteringByDriver = 0, // tells whether we're showing the full text of a single article. // defaults to true iff tf_art is set, which means the company.jsp pre-rendered an article for us showingFullArticleText = window.tf_art != null, fullArticleId = window.tf_art, // a timer used for delayed loading of the list of articles listFetchTimer = $.timer(3000, listFetchTimerCallback), // the ticker symbol that indicates the company for which we should load the article list symbolForListFetch, // an array holding the articles fetched for this company articleList, // the time (as determined by new Date().getTime()) at which we started scrolling up // to the top of the articles section scrollToArticleTime = 0, // the object that manages the comments commentsObj = initCommentsObj($("#"+commentsDivId), controller), // the article comment, the parent of all the comments on the article parentComment, // a mutex for posting comments commentPostMutex = new tf.Mutex(), // tells whether the popin is currently visible popinVisible, // tells whether the popin has content and can be shown popinEnabled, // tells whether the user has closed the popup, causing it not to appear anymore on this page popinClosed ; // for now, disable postid and links on comments commentsObj.hm = false; // the timer is running when it's first created; stop it listFetchTimer.stop(); /** * this is the function that is called when the listFetchTimer fires. This method * fetches the list of articles (based on the current symbolForListFetch). */ function listFetchTimerCallback(timer) { // stop the timer so it only fires once timer.stop(); function successCallback(data, status) { // TODO error-checks // save the list of articles articleList = data; // now we empty out the "Related Articles" area and re-populate it, // with one
for each article // var relatedArticlesAreaQuery = $("#" + areaDivId + " .art_related") .empty(); $.each(data, function(i, article) { relatedArticlesAreaQuery.append( $("
") .append( $("") .html(article.title) .click(function() { // switch to the clicked article, and scroll up to the top of the article, // with a little of the sankey visible controller.setArticle(article.id); scrollTo(400, 300); // save the time at which we started the scroll; we use this in // articleFetchCallback so that we don't show the article until the scroll // is finished scrollToArticleTime = new Date().getTime(); return false; })) .append("'s in .art_related are in 1-1 correspondence with articles in the articleList array // $("#" + areaDivId + " .art_related p").each(function(index, ptag) { var driverMatches = $.inArray(driver, articleList[index].drivers) > -1, passesFilter = !filteringByDriver || driver == null || driverMatches; $(ptag).toggle(passesFilter && (showingMoreArticles || filteredCount < 3)); if(driverMatches) wouldBeFilteredCount++; if(passesFilter) filteredCount++; }); // update the "see more" link // $("#" + areaDivId + " .art_morelink") .html(showingMoreArticles ? "See fewer articles" : "See " + (filteredCount - 3) + " more...") .toggle(filteredCount > 3); // if there's a driver selected, update the driver line based on the current filter state if(driver) crumbTrailQuery .append(filteringByDriver ? "Showing articles on " + controller.getDriverName(driver) + " (" : "Filter for articles on ") .append($("" + (filteringByDriver ? "show all" : controller.getDriverName(driver)) + "") .click(function toggleFilter() { filteringByDriver = !filteringByDriver; updateListState(); return false; })) .append(filteringByDriver ? ")" : " (" + wouldBeFilteredCount + ")"); } function getCurrentDrivername() { var driver = loadedContext.driver; return driver?controller.getDriverName(driver):null; } function setContext(context) { if(context.article) { // check if the article we should load matches the tf_art variable; if so, // that means the article was already rendered in company.jsp commentsObj.setLoadedContext(context); if(context.article == window.tf_art) { setParentComment(tf_artComments); // clear the tf_art variable so that if the user goes to a different article then comes back, // the if statement won't think that the article is already rendered tf_art = null; tf_artComments = null; // HACK: in the related articles section, replace any links to wordpress articles with links // to the same article on the company page. This will eventually be done server-side. // cleanRelatedArticlesLinks(); popinEnabled = tf_popinEnabled; } else // in this case, the content specifies an article, and the article wasn't rendered in company.jsp, // so we make an RPC to fetch it and render it // $.ajax({ type: "GET", url: getHost()+"/servlet/ArticleService/get", data: {id: context.article, withContent: 1, withRelated: 1, withComment: 1}, dataType: "json", success: articleFetchCallback, error: tf.ajaxError }); } else { // in this case, the context specifies that no article should be shown // if we're currently showing a full article, animate it out if(showingFullArticleText) $("#" + popupDivId).animate({opacity: 0}, 200, function() { $(".nonArticle").fadeIn(200, tf.fixFilter); $("#" + popupDivId).hide().css("opacity", 1); //~ setTimeout(function(){$("#" + popupDivId).hide().css("opacity", 1)}, 10); }); else // if we're not currently showing a full article, show the .nonArticle div's anyway, // because they start off invisible when the page first loads (so only the sankey is visible // during its initial opening animation) $(".nonArticle").show(); fullArticleId = null; showingFullArticleText = 0; } // if the ticker has changed, we may need to fetch a new list of articles for the "related articles" section // use a timer, for hysteresis // if(symbolForListFetch != context.symbol) { symbolForListFetch = context.symbol; articleList = null; listFetchTimer.reset(); } // if we have a list of articles and the driver changed, we'll need to update the filter state // b/c the driver changed. If articleList isn't set, that's because it was cleared up above in preparation for the RPC, // and the list state will get updated when the RPC returns anyway // var needToUpdateListState = 0; if(articleList && (!loadedContext || loadedContext.driver != context.driver)) needToUpdateListState = 1; loadedContext = context; // only call updateListState after loadedContext is updated, so that // the list state corresponds to the new context // if(needToUpdateListState) updateListState(); } /* Begin Comment-related functions */ function isChecked(cbn) { return cbn.prop("checked"); } function entryTextArea(eDiv) { return eDiv.children(".cts_entryBox").children("textarea"); } /* adamd 9/26/11: temporarily removed comment posting b/c we removed auto-posted comment for each article function setupPostBox() { var postDiv = $("#"+postDivId), shown = 0; postDiv.children().remove(); tf.fb.init(); // in case they do a facebook share. commentsObj.populatePostComment(postDiv); var ctrls = postDiv.find('.cts_controls'), ta = entryTextArea(postDiv), btm = postDiv.find('.cts_entryBtm'); ta.focus(function(){ if (!shown) { tf.track(77); ta.animate({height : 155},500, tf.webkit?function() { // this is a hack because in chrome the textarea looses focus after it expands ta[0].blur(); ta[0].focus(); }:null); btm.slideDown(500); shown = 1; } }); function resetPostBox(cb) { btm.slideUp(500); ta.animate({height : 60},500, cb); switchToGhost(ta); shown =0; } $(ctrls.find(".cts_cancel")).click(function(){ if (shown){ resetPostBox(); } return false; }); ghostTextObj(ta, "Leave a comment"); $(ctrls.find(".cts_post")).click(function(){ if(commentPostMutex.a()) { postDiv.find(".spinner").show(); commentsObj.addCommentRPC( parentComment && parentComment.postId, null, null, null, getTextFromGhostedObj(ta), null, isChecked(ctrls.find(".cts_cB_notify")), isChecked(ctrls.find(".cts_cB_tt")), isChecked($("#cts_cB_fb")), isChecked($("#cts_cB_li")), isChecked($("#cts_cB_tw")), false, false, false, "article:"+fullArticleId, "Comment on " + ($(".postTitle").html() || getCurrentDrivername() || loadedContext.symbol), function(comment, callback) { commentsObj.loadRepliesRPC(parentComment, function() { resetPostBox(function(){ commentsObj.clear(); commentsObj.addComments(parentComment.replies, parentComment.postId, callback); }); }); commentPostMutex.r(); postDiv.find(".spinner").hide(); tf.track(79); }, function() { commentPostMutex.r(); postDiv.find(".spinner").hide(); } ); tf.track(78); } return false; }); }*/ /* End Comment-related functions */ function updatePopin() { var postQuery = $("#art_popup .post"), windowQuery = $(window), popinQuery = $("#artPopin"), shouldBeVisible = !popinClosed && popinEnabled && showingFullArticleText && postQuery.offset().top + postQuery.height() - 200 < windowQuery.scrollTop() + windowQuery.height(); if(shouldBeVisible ? !popinVisible : popinVisible) { popinQuery.animate( {right: shouldBeVisible ? -1 : -450}, shouldBeVisible ? 200 : 400); popinVisible = shouldBeVisible; } setTimeout(updatePopin, 200); } updatePopin(); /* * click listeners */ $("#" + popupDivId + " .art_back").click(function() { controller.setArticle(0); // setting the article id to 0 effectively sets it to be null return false; }); $("#" + areaDivId + " .art_morelink").click(function() { showingMoreArticles = !showingMoreArticles; updateListState(); return false; }); $("#artPopin button").click(function() { popinClosed = 1; }); initFootnotes(popupDivId); initArticleCharts(); return {cc: setContext, nc: function(loadedContext, parentComment) { // this function is called when the articles is set directly. // loadedContext should define the 'symbol' key, and the 'driver' key if parentComment is null. setParentComment(parentComment); commentsObj.setLoadedContext(loadedContext); }}; } /* * This will flash the target and scroll to it without changing the anchor on the page. */ function initFootnotes(parentDivId) { $("#" + parentDivId + " a.footnote-link").live("click",function(event){ var thisObj = $(this), thisHref = thisObj.attr('href'), thatObj = $(thisHref.indexOf("#")==0?thisHref:thisHref.split('#')[1]); scrollTo(thatObj.offset().top) tf.flash(thatObj); return false; }); } function initArticleCharts() { if(window.oldCharts){ initHighcharts(); } var chartObjects = [], chartData, MAX_CHARTS = 3; window.doArticleCharts = function(data) { for(var i = 0; i < MAX_CHARTS; i++) { if(chartObjects[i]) chartObjects[i].destroy(); chartObjects[i] = null; if(i >= data.length) $("#art_chartBox" + i).hide(); } $.each(data, function(index, datum) { if(index >= MAX_CHARTS) return false; var id = "art_chart" + index, div = $("#art_chartBox" + index); if(!div.length) { div = $("