-
Notifications
You must be signed in to change notification settings - Fork 0
/
scroll_hopper.js
299 lines (263 loc) · 10.3 KB
/
scroll_hopper.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
/**
SCROLL HOPPER! v0.1.0 - 2015-05-15
* https://github.com/russellchapin/hopper
* Copyright (c) 2015 Russell Chapin | San Diego Union Tribune LLC; Licensed MIT
1. Measure height of article div and sidebar div to find out how much remaining space is left from the bottom of the Facebook comments section.
2. If that space is greater than 1000px, then create a wrapper div after the sidebar.
3. In the new wrapper div, append one 300px x 250px ad and then append a 100% x 650px wrapper containing the three most recent A Wire articles.
4. Measure the remaining height of the wrapper div and divide that height by 1000px, which is the sum of one ad spot and one article wrapper div.
5. Above quotient will first set the value for the number of ad variables and then be multiplied by three to set as the value of the variable that dictates the number of articles called through AJAX.
6. The object containing all of the articles will then be split in to triplets, wrapped inside parent containers and rendered in to the DOM simultaneously with the ad spots.
*/
var app = window.app || {};
app.scroll_loader = function() {
$(window).load(function() {
// call the init function eventually
window.setTimeout(init, 5000);
});
var article_cache = {},
articles_per_box = 3,
last_index = 0;
function init(){
app._article_height = $("article").height();
var content_height = $("article").height();
rendered_sidebar = $("#sidebar").height();
/**
* If space below sidebar is > 800px then work needs to be done.
*/
if (content_height - rendered_sidebar > 920 && $(window).width() > 960){
ad_box_draw(0);
}
}
/**
helper function, makes sure that ad support is there before re-initialization
@param {element} jQuery element(s) to work on
*/
function init_ads(element){
if (app.adsLazy !== undefined){
app.adsLazy.init(element);
}
}
function ad_box_draw(ad_number) {
console.log( "Ad box is drawing." );
var para = document.createElement("div"),
element = $('<div id="ad_box_wrapper" class="span3 col alpha omega"></div>');
element.height($("article").height() - $("#sidebar").height());
// push it into the dom
element.insertAfter("#sidebar");
mod_number = ad_number;
article_loader();
}
/*
function auto_ad(){
var para = document.createElement("div"),
element = $("#sidebar");
// add the add call to our element
element.append('<div data-boxy="true" id="dfp_300x250_2" data-slot="300x250_2" data-size="300x250" class="auto_boxy"></div>');
var boxy_ad = $('[data-boxy="true"]');
// initialize ads
init_ads(boxy_ad);
// remove the data slot so we don't initialize again
boxy_ad.removeAttr("data-slot");
boxy_ad.removeAttr("data-boxy");
}
*/
/**
Fetch defaults for an API call
*/
function get_data(limit) {
var data = {};
data.limit = limit;
if(app.photoWidth){
data.photo_width = app.photoWidth;
}
if(app.photoHeight){
data.photo_height = app.photoHeight;
}
data.lead_photo__isnull = 'False';
return data;
}
/**
Create an HTML represietation of an anchor
@param {item}
{item.url} url to the item
{item.lead_photo} photo for the item
{item.h3_class} class for the item
{item.title} title for the link (anchor text)
@return {html} of the item specified
*/
function create_html(item){
return '<a href="'+ item.url +'">' + item.lead_photo +'<h3 '+ item.h3_class + '>'+ item.title +'</h3><span style="display:none">'+ item.url +' nav-stories</span></a>';
}
/**
Error message in the event of an API error
@return {html} error message in an LI
*/
function get_error_message(){
return "<li><h3>Something went wrong fetching the stories. Please try again later.</h3></li>";
}
/**
Renders an item returned from an API call
@param {item} item returned from an API call
@param {element} Element to append to
@returns Nothing
*/
function render_item(item, element, i) {
// var element = document.getElementById("a_wire_wrapper"),
var article_link = document.createElement("li");
if(item.start_time) {
return null;
}
item.h3_class = '';
// This assumes that the API has returned an image link.
// this is not the proper way of handling this.
// This version of the API does not return a content type and it's assumed
// that the lead_photo is a hard link to the photo
if(item.lead_photo) {
if(item.lead_photo.indexOf('img src') === -1){
item.lead_photo = '<div class="scroller_img_wrap"><img src="'+ item.lead_photo + '" /></div>';
}
} else {
item.lead_photo = '';
item.h3_class = 'class="nav-stories-no-photo"';
}
article_link.className = "scroll_loader_stories recommended";
//}
element.appendChild(article_link);
article_link.innerHTML = create_html(item);
}
/**
Callback reciever for an api call, takes the generated json and a parent element to append to.
@param {json} json returned from an API call
@param {element} element to work with
*/
function callback_receiver(json, element) {
console.log(element);
if(json.items.length === 1){
// if we are here then nothing was returned from the api (empty section perhaps?)
element.empty();
element.prepend(get_error_message());
} else {
for(i=last_index; i < json.items.length; i++){
render_item(json.items[i], element);
}
}
}
/**
Callback reciever for an api call, takes the generated json and a parent element to append to.
@param {json} json returned from an API call
@param {element} element to work with
*/
function box_creator(json, element) {
if(json.items.length === 1){
element.empty();
element.prepend(get_error_message());
} else {
// For each 3 json items:
// Create a container,
// Create the story box,
// Populate the story box
// Create an ad-box
// Populate the ad-box
// Done.
var div = $(document.createElement('div')),
wrapper = $("#ad_box_wrapper"),
more_title = $(document.createElement('h3')),
recommend_title = $(document.createElement('h3')),
link_wrap = $(document.createElement('ul'));
link_wrap.data('article-type', 'article');
console.log(element);
var ad_number = 0;
for(i=0; i < json.items.length; i++){
render_item(json.items[i], element, i);
if(i%3 === 0){
ad_loader(ad_number);
recommend_title.insertAfter($(".count"+count));
recommend_title.text('More From ' + app.section_name );
ad_number++;
}
}
}
}
function eraser(){
var a = $("#ad_box_wrapper");
var atop = a.position().top;
var ad = $('.recommended');
var article = $('.scroll_loader_stories');
$('.recommended').each(function() {
var adcount = $(this);
var adtop = adcount.position().top;
if(adtop + ad.height() > atop + a.height()){
console.log("Removing"+adcount);
adcount.remove();
}
});
}
/**
Make an ajax call, takes a url, data and element that will be acted on
@param {url} URL to make the api call to
@param {data} any data to send to the API
@param {element} jQuery element to work with
@param {callback} is a function callback
@returns nothing
*/
function ajax_call(url, data, element, callback){
console.log(element);
return $.ajax({
url: url,
data: data,
type: "GET",
dataType : "jsonp", // jsonp is here to prevent a cors request.
cache: true,
success: function( json, status) {
callback(json, element);
},
error: function( xhr, status, errorThrown ) {
console.log( "PROBLEM" );
console.log( "Error: " + errorThrown );
console.log( "Status: " + status );
console.dir( xhr );
}
});
}
/**
Creates a "Top Stories" div and poplates it with data returned from the API
TODO (chapin): make a_wire_loader and article_loader work together
*/
var count = 0;
function ad_loader(count) {
$("#ad_box_wrapper").append('<div data-boxy="true" id="dfp_300x250_2" data-slot="300x250_2" data-size="300x250" class="count'+count+ ' recommended"></div>');
//console.log("count"+count);
var boxy_ad = $('[data-boxy="true"]');
//initialize ads
eraser();
init_ads(boxy_ad);
//remove the data slot so we don't initialize again
boxy_ad.removeAttr("data-slot");
boxy_ad.removeAttr("data-boxy");
}
/**
Loads up the article shell elements with data from the API
@param {e} NOT USED
@param {wrapper} element to load data into
@returns nothing
*/
function article_loader(e, wrapper) {
var limit = Math.round($("#ad_box_wrapper").outerHeight(true)/920)*3,
link_wrap = $("#ad_box_wrapper");
var data = get_data(limit);
ajax_call("/api/widgets/v1/news/section/"+ app.section_url +"/", data, link_wrap[0], box_creator);
}
/**
Public functions. IMPLICIT self
This is for the initr to work properly, it will call init to make sure everything is initialized after the dom is loaded
*/
return {
init: function() {
if(app.fetchFromCache === undefined){
app.fetchFromCache = true;
}
self.init();
}
}
}();