1var S = { 2 initialize: function (type, search) { 3 S.recipeManager.initialize(type, search); 4 S.layoutManager.initialize(S.recipeManager.onPageScroll); 5 S.searchManager.initialize(); 6 S.shoppingManager.initialize(); 7 }, 8 imageManager: { 9 onImageLoad: function (a) { 10 a.className += ' loaded'; 11 } 12 }, 13 socialManager: { 14 analytics: { 15 track: function (id) { 16 if (typeof gtag === 'function') { 17 gtag('event', 'page_view', { 'page_location': stcajax.urlbase + '/recipes/recipe-details/' + id + '/' }); 18 gtag('event', 'view_recipe', { 'value': 'Recipe ' + id }); 19 } 20 } 21 }, 22 facebook: { 23 initialize: function () { 24 var js, fjs = document.getElementsByTagName('script')[0]; 25 if (document.getElementById('facebook-jssdk')) 26 return; 27 js = document.createElement('script'); 28 js.id = 'facebook-jssdk'; 29 js.src = "//connect.facebook.net/en_US/all.js#xfbml=1"; 30 fjs.parentNode.insertBefore(js, fjs); 31 }, 32 link: function () { 33 try { 34 FB.XFBML.parse(); 35 } catch (err) { 36 //ignore 37 } 38 } 39 }, 40 pinterest: { 41 initialize: function () { 42 var f = document.getElementsByTagName('SCRIPT')[0]; 43 var p = document.createElement('SCRIPT'); 44 p.type = 'text/javascript'; 45 p.async = true; 46 p.src = '//assets.pinterest.com/js/pinit.js'; 47 f.parentNode.insertBefore(p, f); 48 }, 49 get: function () { 50 for (var i in window) { 51 if (i.indexOf('PIN_') == 0 && typeof window[i] == 'object') { 52 return window[i]; 53 } 54 } 55 }, 56 link: function () { 57 S.socialManager.pinterest.get().f.seek.buttonPin(jQuery('.pin')[0]); 58 } 59 } 60 }, 61 layoutManager: { 62 $container: null, 63 $document: null, 64 $window: null, 65 $icon: null, 66 $error: null, 67 factory: null, 68 itemWidth: 200, 69 count: 0, 70 increment: 12, 71 maxCount: -1, 72 scrollOffset: 100, /* DEPRECATED */ 73 onScrollAjax: false, 74 wookmark: null, 75 initialize: function (onPageScroll) { 76 S.layoutManager.factory = onPageScroll; 77 S.layoutManager.$window = jQuery(window); 78 S.layoutManager.$document = jQuery(document); 79 S.layoutManager.$container = jQuery('#stc-main-content'); 80 S.layoutManager.$icon = jQuery('.grid-icon'); 81 S.layoutManager.$error = jQuery('.grid-error'); 82 S.layoutManager.wookmark = new Wookmark('#stc-main-content', { 83 offset: 15 // Optional, the distance between grid items 84 }); 85 jQuery(window).bind('scroll.wookmark', S.layoutManager.onPageScroll); 86 }, 87 generateHTML: function (json) { 88 var html = ''; 89 json['recipes'].forEach(function (recipe) { 90 html += '<div class="stc-rcp-item" style="width:' + S.layoutManager.itemWidth + 'px;">' + 91 '<a class="stc-cat-link" href="' + stcajax.urlbase + '/recipes/recipe-details/' + 92 recipe['recipe_id'] + '" onClick="return S.recipeManager.onDetailClick(\'' + recipe['recipe_id'] + '\', true);">' + 93 '<div class="item-wrapper">' + 94 '<div class="item-overlay">' + 95 '<div class="item-image">' + 96 '<div class="image-wrapper no-scroll" style="width:' + S.layoutManager.itemWidth + 'px; height:' + S.layoutManager.itemWidth + 'px;">' + 97 '<div class="dim-wrapper">' + 98 '<div class="dim-overlay"></div></div>' + 99 '<img class="item-image fade" onload="S.imageManager.onImageLoad(this)" src="https://images.shoptocook.com' + recipe['image_full'] + '" alt="' + recipe['recipe_name'] + '">' + 100 '</div></div>' + 101 '<div class="item-summary">'; 102 html += '<span>' + recipe['recipe_name'] + '</span>' + 103 '</div>'; 104 if (recipe['image_credit'] !== "") { 105 html += '<div class="credit-container">' + 106 '<span>' + 107 recipe['image_credit'] + '</span></div>'; 108 } 109 html += '</div></div></a></div>'; 110 }); 111 return html; 112 }, 113 onPageScroll: function () { 114 if (typeof S.layoutManager.factory !== "function") { 115 return; 116 } 117
118 var viewportHeight = window.innerHeight ? window.innerHeight : S.layoutManager.$window.height(); 119 var recipeBottom = jQuery('#recipe_container_outer .header-line-sub').get(0).getBoundingClientRect().top; 120 //look at removal of scroll offsets and put scroll offset back in here 121 //S.layoutManager.scrollOffset 122 if (recipeBottom + 100 < viewportHeight && !S.layoutManager.onScrollAjax && S.layoutManager.search !== null && (S.layoutManager.maxCount === -1 || S.layoutManager.count < S.layoutManager.maxCount)) { 123 S.layoutManager.$icon.addClass("grid-spinner"); 124 S.layoutManager.onScrollAjax = true; 125 S.layoutManager.factory( 126 function (data) { 127 if (data === null || data['error_message']) { 128 S.layoutManager.$icon.addClass("grid-spinner"); 129 S.layoutManager.$error.html(data !== null ? data['error_message'] : 'An error occured.'); 130 S.layoutManager.$error.show(); 131 return; 132 } 133 134 S.layoutManager.setup(data, 0); 135 S.layoutManager.onScrollAjax = false; 136 S.layoutManager.$icon.removeClass("grid-spinner"); 137 }); 138 } 139 }, 140 setup: function (json, fadeTimeout) { 141 S.layoutManager.count += S.layoutManager.increment; 142 S.layoutManager.maxCount = json['total_recipes']; 143 var $items = jQuery(S.layoutManager.generateHTML(json)).css('opacity', 0); 144 S.layoutManager.$container.append($items); 145 S.layoutManager.wookmark.initItems(); 146 if (fadeTimeout > 0) { 147 S.layoutManager.wookmark.layout(true, function () { 148 setTimeout(function () { 149 $items.fadeTo(300, 1); 150 }, fadeTimeout); 151 }); 152 } else { 153 S.layoutManager.wookmark.layout(true, function () { 154 $items.fadeTo(300, 1); 155 }); 156 } 157 } 158 }, 159 recipeManager: { 160 $overlay: null, 161 type: null, 162 search: null, 163 history: null, 164 initialize: function (type, search) { 165 S.recipeManager.type = type; 166 S.recipeManager.search = search; 167 S.recipeManager.$overlay = jQuery('<div id="append-container"></div>'); 168 jQuery('body').append(S.recipeManager.$overlay); 169 }, 170 bindBackButton: function () { 171 S.recipeManager.history = window.History; 172 S.recipeManager.history.Adapter.bind(window, 'statechange', function () { 173 var states = S.recipeManager.history.getState() 174 if (jQuery.trim(S.recipeManager.$overlay.html()) === '') { 175 S.recipeManager.onDetailClick(states.data["id"], false); 176 } else if (jQuery.isEmptyObject(states.data)) { 177 S.recipeManager.closePopup(); 178 } 179 }); 180 }, 181 bindCloseButton: function (f) { 182 jQuery('.RecFooterClose, .overlay').click(f); 183 }, 184 closePopup: function () { 185 S.recipeManager.$overlay.empty(); 186 jQuery('body').removeClass('no-scroll'); 187 jQuery('html').removeClass('no-scroll'); 188 }, 189 onDetailClick: function (id, history) { 190 S.recipeManager.$overlay.append( 191 '<div class="overlay">' + 192 '<button class="RecCloseButton borderless" aria-label="Close"><em class="stc-icon-xxsmall" style="font-size:0px;line-height:0px;">Close</em></button>' + 193 '<div id="overlay-content-wrapper" style="margin-top: 28px; margin-bottom: 14px;">' + 194 '</div>' + 195 '</div>'); 196 jQuery('body').addClass('no-scroll'); 197 jQuery('html').addClass('no-scroll'); 198 jQuery('.RecCloseButton').addClass('transformVisible'); 199 S.recipeManager.bindCloseButton(S.recipeManager.onPopupCloseClick); 200 if (S.recipeManager.history !== null && history) { 201 S.recipeManager.history.pushState({ url: document.location.href, id: id }, document.title, window.location.protocol + '//' + window.location.hostname + stcajax.urlbase + '/recipes/recipe-details/' + id + '/'); 202 } 203 var data = { 204 'action': 'stc_get_recipe', 205 'id': id 206 }; 207 jQuery.post(stcajax.ajaxurl, data, function (response) { 208 if (response === null || response['error_message']) { 209 return; 210 } 211 212 S.recipeManager.parseJson(response); 213 }, "json"); 214 return false; 215 }, 216 onDetailCloseClick: function (e) { 217 if (jQuery(e.target).parents(".overlay-content").length !== 1) { 218 window.location.href = stcajax.urlbase + "/recipes"; 219 } 220 }, 221 onPopupCloseClick: function (e) { 222 if (jQuery(e.target).parents(".overlay-content").length !== 1) { 223 S.recipeManager.history.back(); 224 } 225 }, 226 parseJson: function (data) { 227 var $wrapper = jQuery('#overlay-content-wrapper'); 228 var url = encodeURIComponent('http://' + window.location.hostname + '/recipe-details/' + data['id'] + '/'); 229 var html = ''; 230 html += '<div class="overlay-content"><div class="RecActionBar">'; 231 if (stcajax.urlbase == '') { 232 html += '<div class="floatLeft">' 233 + '<a class="RecActionButton pin first" data-pin-do="buttonPin" data-pin-count="beside" data-pin-custom="true" ' 234 + 'href="https://www.pinterest.com/pin/create/button/?url=' + url 235 + '&media=' + encodeURIComponent('https://images.shoptocook.com' + data['image']) 236 + '&description=' + encodeURIComponent(data['name']) + '">' 237 + '<img src="/wp-content/plugins/shoptocook-recipes/images/blank.png" height="24" width="48" alt="pinterest"/></a>' 238 + '<div class="fb-like" data-href="' + url + '" data-layout="button" data-action="like" data-show-faces="true" data-share="true"></div>' 239 + '</div>'; 240 } 241 html += '<div class="floatRight">'; 242 if (stcajax.urlbase == '') { 243 html += '<a class="RecActionButton email first" href="mailto:?subject=Recipe: ' + data['name'] 244 + '&body=' + url + '">' 245 + '<em class="stc-icon-xxsmall"></em> <span>Send</span></a>' 246 } 247 html += '<a class="RecActionButton print" href="' + stcajax.urlbase + '/print-recipe/?recipeid='
247+ data['id'] + '">' 248 + '<em class="stc-icon-xxsmall"></em> <span>Print</span></a>' 249 + '</div></div><div>' 250 + '<img src="https://images.shoptocook.com' + data['image'] + '" alt="' + data['name'].replace(/\"/g, "") + ' banner" />' 251 + '</div>'; 252 html += '<div class="sub-content"><h2 class="overlay-header">' 253 + data['name'] + '</h2>'; 254 if (data['image_credit'] !== "") { 255 html += '<span class="stc-recipe-credit">' + data['image_credit'] + '</span>'; 256 } 257 html += '<span class="sub-header">'; 258 var subhtml = ''; 259 for (i = 0; i < data['header'].length; i++) { 260 if (data['header'][i]['display_value'] === 'n/a') { 261 continue; 262 } 263 if (subhtml !== '') { 264 subhtml += ' • '; 265 } 266 if (data['header'][i]['display_name'] === 'Serve With') { 267 subhtml += data['header'][i]['display_value']; 268 } else { 269 subhtml += data['header'][i]['display_value'] + ' ' + data['header'][i]['display_name']; 270 } 271 } 272 html += subhtml; 273 if (data['vendor_logo'] != null && data['vendor_logo'] !== "") { 274 html += '<div style="margin-top: 20px;">
274<img src="https://images.shoptocook.com/' + data['vendor_logo'] + '" alt="Sponsor" /></div>'; 275 } 276 html += '</span></div><div class="sub-content"><div class="ingredient-container">' 277 + '<h3>Ingredients</h3><table>'; 278 for (i = 0; i < data['ingredients'].length; i++) { 279 var checkid = 'ingredient-' + data['id'] + '-' + i; 280 html += '<tr' 281 + ((i < data['ingredients'].length - 1) ? ' class="underline"' : "") 282 + '><td>'; 283 if (data['ingredients'][i]['can_add'] === "Y") { 284 html += '<input type="checkbox" id="' + checkid 285 + '" class="ingredient-checkbox" data-name="' 286 + data['ingredients'][i]['name'] + '" data-id="' 287 + data['id'] + '" /><label for="' + checkid + '"><span style="display:none;">' + data['ingredients'][i]['name'].replace(/\"/g, "") 288 + '</span></label>'; 289 } 290 html += '<span class="ingredient-name">' 291 + (data['ingredients'][i]['name'] === "\n" ? '<br/>' : data['ingredients'][i]['name']) 292 + '</span></td></tr>'; 293 } 294 html += '</table></div></div><div class="sub-content"><h3>Directions:</h3>'; 295 data['instructions'].forEach(function (instruction) { 296 html += '<p>' + instruction['line'] + '</p>'; 297 }); 298 html += '</div>'; 299 if (typeof data['nutrition'] !== 'undefined' && data['nutrition'].length > 0) { 300 html += '<div class="sub-content"><h3>Nutrition:</h3>'; 301 data['nutrition'].forEach(function (nutrition) { 302 html += '<span>' + nutrition['display_name'] + ': ' + nutrition['display_value'] + ' </span>'; 303 }); 304 html += '</div>'; 305 } 306 307 if (typeof data['wine_pairings'] !== 'undefined' && data['wine_pairings'].length > 0) { 308 html += '<div class="sub-content"><h3>Wine Pairings:</h3><span>' 309 + data['wine_pairings'].join(', ') 310 + '</span></div>'; 311 } 312 html += '</div>' 313 + '<div class="stc-footer-close"><a class="RecFooterClose stc-button"><img src="/wp-content/plugins/shoptocook-recipes/images/close-button.png" alt="close"></a>' 314 + '</div>'; 315 $wrapper.append(html); 316 S.shoppingManager.bindInputs(); 317 S.socialManager.analytics.track(data['id']); 318 if (stcajax.urlbase == '') { 319 S.socialManager.pinterest.link(); 320 S.socialManager.facebook.link(); 321 } 322 }, 323 onPageScroll: function (callback) { 324 var data = { 325 'action': 'stc_get_recipes', 326 'q': S.recipeManager.search, 327 'type': S.recipeManager.type, 328 'count': S.layoutManager.count, 329 'inc': S.layoutManager.increment 330 }; 331 jQuery.post(stcajax.ajaxurl, data, function (response) { 332 if (typeof callback === "function") { 333 callback(response); 334 } 335 }, "json"); 336 } 337 }, 338 shoppingManager: { 339 $shoppingList: null, 340 $badge: null, 341 isAnimating: false, 342 initialize: function () { 343 S.shoppingManager.$shoppingList = jQuery('<div class="stc-cart-overlay"></div>'); 344 jQuery('body').append(S.shoppingManager.$shoppingList); 345 addLoadEvent(S.shoppingManager.show); 346 }, 347 add: function (recpieId, ingredientName) { 348 if (typeof gtag === 'function') { 349 gtag('event', 'add_to_shoppinglist', { 'category': 'Recipes', 'value': ingredientName }); 350 } 351 shoppinglist_controller.addToList( 352 { 353 recipeID: recpieId, 354 ingredient: ingredientName, 355 department: 'Recipe Ingredients', 356 dealDetails: null, 357 details: null, 358 terminationDate: null, 359 storeID: null 360 }, 361 function (result) { 362 S.shoppingManager.animateBadge(result['items'].length); 363 }); 364 }, 365 animateBadge: function (count) { 366 if (S.shoppingManager.isAnimating) { 367 S.shoppingManager.$badge.children(":first").html(count); 368 return; 369 } 370 371 S.shoppingManager.$badge.css('visibility', 'visible'); 372 S.shoppingManager.isAnimating = true; 373 S.shoppingManager.$shoppingList.toggle({ 374 effect: "scale", complete: function () { 375 S.shoppingManager.$badge.children(":first").html(count); 376 S.shoppingManager.$shoppingList.toggle({ 377 effect: "scale", complete: function () { 378 S.shoppingManager.isAnimating = false; 379 } 380 }); 381 } 382 }); 383 }, 384 addClick: function (e) { 385 var $input = jQuery('#' + jQuery(e.target).attr('for')); 386 if ($input.prop('checked')) { 387 e.preventDefault(); 388 } else { 389 S.shoppingManager.add($input.data('id'), $input.data('name')); 390 } 391 }, 392 bindInputs: function () { 393 jQuery('.ingredient-checkbox + label').click(S.shoppingManager.addClick); 394 }, 395 overlayClick: function (e) { 396 window.location.href = stcajax.urlbase + "/my-shopping-list"; 397 }, 398 show: function () { 399 S.shoppingManager.$shoppingList.append( 400 '<div class="img-wrapper" title="Shopping List">' + 401 '<button class="stc-button stc-button-shop borderless" ' + 402 'type="button"><em><span style="display: none;">Shopping Cart<span></em></button><div class="stc-button ' + 403 'stc-badge-shop"><span class="stc-button-text stc-badge-text">' + 404 '</span></div></div>'); 405 S.shoppingManager.$badge = jQuery('.stc-badge-shop'); 406 jQuery('.stc-button-shop, .stc-badge-shop').click(S.shoppingManager.overlayClick); 407 shoppinglist_controller.initializeList( 408 function (result) { 409 if (result['items'].length !== 0) { 410 S.shoppingManager.$badge.children(":first").html(result['items'].length); 411 S.shoppingManager.$badge.css('visibility', 'visible'); 412 } 413 }); 414 } 415 }, 416 searchManager: { 417 $input: null, 418 $main: null, 419 $window: null, 420 offset: 0, 421 initialize: function () { 422 S.searchManager.$input = jQuery('#stc-search-header'); 423 S.searchManager.$window = jQuery(window); 424 S.layoutManager.$document = jQuery(document); 425 S.searchManager.$main = jQuery('.scroll-top-marker'); 426 jQuery(document).ready( 427 function () { 428 jQuery(window).scroll(function () { 429 if (!S.searchManager.$input.hasClass("stc-search-float") 430 && S.searchManager.$main.position().top - S.searchManager.offset < S.searchManager.$window.scrollTop()) { 431 jQuery("#stc-search-header").addClass("stc-search-float"); 432 } else if (S.searchManager.$input.hasClass("stc-search-float") 433 && S.searchManager.$main.position().top - S.searchManager.offset > S.searchManager.$window.scrollTop()) { 434 jQuery("#stc-search-header").removeClass("stc-search-float"); 435 } 436 }); 437 }); 438 } 439 } 440};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.