1/** 2 * RCRiotAvailPricing RiotJS Mixin 3 */ 4var RCRiotIsSearching; 5(function($) { 6 var lastSearch, 7 lastPricing, 8 avpdata = {}; 9 var RCRiotAvailPricing = { 10 RCRiotAvailPricing: riot.observable(), 11 init: function() { 12 var tag = this; 13 this.mixin('RCRiotUtils'); 14 if (typeof lastSearch == 'undefined') { 15 tag.loadLastSearch(); 16 tag.loadLastPricing(); 17 } 18 19 /** 20 * Respond to BTLeads asking for metadata about items about to be 21 * shared. 22 */ 23 RiotTagsDispatch.on('BTLeadsShareItems', function(tag, items, entityType) { 24 25 if (entityType !== 'rc_core_item') { 26 return false; 27 } 28 items.items.forEach(function(value, key) { 29 if (avpdata['eid-' + value.item_id]) { 30 var pricingObj = avpdata['eid-' + value.item_id]; 31 32 if(avpdata['eid-' + value.item_id].hasOwnProperty('prices') && avpdata['eid-' + value.item_id].prices.length) { 33 var pValue = parseFloat(avpdata['eid-' + value.item_id].prices[0].p).toFixed(2); 34 pricingObj.prices[0].p = parseFloat(pValue); 35 items.items[key].prices = pricingObj; 36 } 37 } 38 }); 39 }); 40 41 /** 42 * Respond to current search asking to remove all facets. 43 */ 44 RiotTagsDispatch.on('RiotSolrCurrentSearchRemoveAll', function(currentSearch, widgetInstance) { 45 widgetInstance.manager.store.remove('rcav'); 46 }); 47 }, 48 formatAvailPrice: function(val, digits, currency) { 49 return !val 50 ? '' 51 : val.toLocaleString('en', { 52 minimumFractionDigits: digits, 53 maximumFractionDigits: digits, 54 style: 'currency', 55 currency: currency || 'USD', 56 currencyDisplay: 'symbol', 57 useGrouping: true, 58 }); 59 }, 60 setAvailPricing: function(pricing, query) { 61 var eventData = { 62 pricing: pricing, 63 query: query, 64 }; 65 avpdata = pricing; 66 RiotTagsDispatch.trigger('RCRiotPricingChanged', this, eventData); 67 this.RCRiotAvailPricing.trigger('PricingChanged', eventData); 68 }, 69 getItemPricing: function(eid) { 70 return this.getAvailPricing()['eid-' + eid]; 71 }, 72 getAvailPricing: function() { 73 return avpdata; 74 }, 75 storeLastSearch: function(params) { 76 localStorage.setItem('rcriotavailpricingparams', JSON.stringify(params)); 77 }, 78 loadLastSearch: function() { 79 var ls = JSON.parse(localStorage.getItem('rcriotavailpricingparams')); 80 lastSearch = ls; 81 return ls; 82 }, 83 storeLastPricing: function(pricing) { 84 localStorage.setItem('rcriotavailpricingpricing', JSON.stringify(pricing)); 85 }, 86 loadLastPricing: function() { 87 var lr = JSON.parse(localStorage.getItem('rcriotavailpricingpricing')); 88 lastPricing = lr; 89 avpdata = lr || {}; 90 return lr; 91 }, 92 doSearch: function(query, store, multi) { 93 lastSearch = query; 94 var tag = this, 95 pricing = {}; 96 multi = multi || 'all'; 97 RCRiotIsSearching = true; 98 document.body.classList.add("rcav-loading-indicator"); 99 var promise = $.Deferred(function() { 100 tag.RCRiotAvailPricing.trigger('SearchStart', query); 101 $.getJSON('/rcapi/item/avail/search', query) 102 .done(function(data) { 103 $(data).each(function(ind, val) { 104 var min = null, 105 max = null, 106 lowp = null; 107 if (this.prices.length > 1) { 108 switch (multi) { 109 case 'first': 110 this.prices = [this.prices[0]]; 111 break; 112 case 'low': 113 $(this.prices).each(function() { 114 if (lowp == null || this.p < lowp.p) { 115 lowp = this; 116 } 117 }); 118 this.prices = [lowp]; 119 break; 120 case 'low_day': 121 var lowestPerDay = function(prices) { 122 var pricesByStart = {}; 123 var returnPrices = []; 124
125 prices.forEach(function(price) { 126 if (!pricesByStart.hasOwnProperty(price.bt) || pricesByStart[price.bt].p > price.p) { 127 pricesByStart[price.bt] = price; 128 } 129 }); 130 131 for (var price in pricesByStart) { 132 if (!pricesByStart.hasOwnProperty(price)) { 133 continue; 134 } 135 136 returnPrices.push(pricesByStart[price]); 137 } 138 139 return returnPrices; 140 }; 141 this.prices = lowestPerDay(this.prices); 142 break; 143 } 144 } 145 $(this.prices).each(function() { 146 var price = parseInt(this.p, 10); 147 var travelers = parseInt(this.qp.rcav.adult, 10) + parseInt(this.qp.rcav.child, 10); 148 if(this.qp.senior) { 149 travelers += parseInt(this.qp.rcav.senior, 10); 150 } 151 this.ppp = price / travelers; 152 // Set begin and end date timestamps according to browser timezone settings 153 if (typeof this.b != 'undefined') { 154 this.bt = tag.parseDate(this.b); 155 this.et = tag.parseDate(this.e); 156 this.nt = ((this.et - this.bt) / 1000) / 86400; 157 this.wk = (this.nt) / 7; 158 this.ppn = price / this.nt; 159 this.ppw = price / this.wk; 160 this.pppn = (price / this.nt) / travelers; 161 } 162 163 if (min == null || this.p < min) { 164 min = this.p; 165 } 166 if (max == null || this.p > max) { 167 max = this.p; 168 } 169 //@todo: decodeURIComponent - do we need it? 170 this['itemBuyURL'] = decodeURIComponent(tag.RCRiotUtils.getBuyUrl(this)); 171 this['itemURLHash'] = '#' + $.param({ rcav: JSON.stringify(this.qp) }); 172 }); 173 this.range = [min, max]; 174 }); 175 lastPricing = data; 176 $(data).each(function(ind, obj) { 177 pricing['eid-' + obj.eid] = obj; 178 }); 179 if (store) { 180 this.storeLastSearch(query); 181 this.storeLastPricing(pricing); 182 } 183 tag.setAvailPricing(pricing, query); 184 promise.resolve({ 185 pricing: pricing, 186 query: query, 187 }); 188 }) 189 .always(function() { 190 tag.RCRiotAvailPricing.trigger('SearchStop'); 191 RCRiotIsSearching = false; 192 document.body.classList.remove("rcav-loading-indicator"); 193 }); 194 }); 195 return promise; 196 }, 197 getLastSearch: function() { 198 return lastSearch; 199 }, 200 setLastSearch: function(query) { 201 lastSearch = query; 202 }, 203 getItemPriceRange: function(eid) { 204 var pricing = this.getItemPricing(eid), 205 min = null, 206 max = null; 207 if (typeof pricing != 'undefined') { 208 $(pricing.prices).each(function() { 209 if (min == undefined || min.p > this.p) { 210 min = this; 211 } 212 if (max == undefined || max.p < this.p) { 213 max = this; 214 } 215 }); 216 } 217 return [min, max]; 218 }, 219 getItemPriceRangePerNight: function(eid) { 220 var pricing = this.getItemPricing(eid), 221 min = null, 222 max = null; 223 if (typeof pricing != 'undefined') { 224 $(pricing.prices).each(function() { 225 if (min == undefined || min.ppn > this.ppn) { 226 min = this; 227 } 228 if (max == undefined || max.ppn < this.ppn) { 229 max = this; 230 } 231 }); 232 } 233 return [min, max]; 234 }, 235 getItemPriceRangePerWeek: function(eid) { 236 var pricing = this.getItemPricing(eid), 237 min = null, 238 max = null; 239 if (typeof pricing != 'undefined') { 240 $(pricing.prices).each(function() { 241 if (min == undefined || min.ppw > this.ppw) { 242 min = this; 243 } 244 if (max == undefined || max.ppw < this.ppw) { 245 max = this; 246 } 247 }); 248 } 249 return [min, max]; 250 }, 251 getItemPriceRangePerPerson: function(eid) { 252 var pricing = this.getItemPricing(eid), 253 min = null, 254 max = null; 255 if (typeof pricing != 'undefined') { 256 $(pricing.prices).each(function() { 257 if (min == undefined || min.ppp > this.ppp) { 258 min = this; 259 } 260 if (max == undefined || max.ppp < this.ppp) { 261 max = this; 262 } 263 }); 264 } 265 return [min, max]; 266 }, 267 getItemPriceRangePerPersonPerNight: function(eid) { 268 var pricing = this.getItemPricing(eid), 269 min = null, 270 max = null; 271 if (typeof pricing != 'undefined') { 272 $(pricing.prices).each(function() { 273 if (min == undefined || min.pppn > this.pppn) { 274 min = this; 275 } 276 if (max == undefined || max.pppn < this.pppn) { 277 max = this; 278 } 279 }); 280 } 281 return [min, max]; 282 }, 283 getItemSavingsRange: function(eid) { 284 var pricing = this.getItemPricing(eid), 285 min = null, 286 max = null; 287 if (typeof pricing != 'undefined') { 288 $(pricing.prices).each(function() { 289 if (typeof this.s != 'undefined' && (min == undefined || min.s > this.s)) { 290 min = this; 291 } 292 if (typeof this.s != 'undefined' && (max == undefined || max.s < this.s)) { 293 max = this; 294 } 295 }); 296 } 297 return [min, max]; 298 }, 299 formatItemPriceRange: function(eid, decs, format) { 300 decs = isNaN(decs) ? 0 : decs; 301 var range = this.getItemPriceRange(eid), 302 ret = ''; 303 if (range[0] && range[0].p > 0) { 304 ret = this.RCRiotUtils.formatPrice(range[0], decs, format); 305 if (range[0].p != range[1].p) { 306 ret += ' - ' + this.RCRiotUtils.formatPrice(range[1], decs, format); 307 } 308 } 309 return ret; 310 }, 311 formatItemSavingsRange: function(eid, decs, format) { 312 decs = isNaN(decs) ? 0 : decs; 313 var range = this.getItemSavingsRange(eid), 314 ret = ''; 315 if (range[1] != null && typeof range[1].s != 'undefined' && range[1].s > 0) { 316 ret = this.RCRiotUtils.formatHelper(parseFloat(range[1].s), range[1], decs, format); 317 } 318 return ret; 319 }, 320 getLastSearchQuery: function() { 321 return lastSearch; 322 }, 323 getItemHasPackage: function(eid) { 324 var ret = false, 325 ip = this.getItemPricing(eid), 326 ind; 327 if (typeof ip != 'undefined' && typeof ip.prices != 'undefined') { 328 for (ind in ip.prices) { 329 if (this.getPriceHasPackage(ip.prices[ind])) { 330 ret = true; 331 break; 332 } 333 } 334 } 335 return ret; 336 }, 337 getPriceIsAlternate: function(price) { 338 var ret = true; 339 if(typeof price == undefined || typeof price.qp == undefined) { 340 return ret; 341 } 342 ret = price.qp.rcav.begin !== lastSearch.rcav.begin; 343 return ret; 344 }, 345 getPriceHasPackage: function(price) { 346 var ret = false;
347 if(typeof price == undefined || typeof price.qp == undefined) { 348 return ret; 349 } 350 if (typeof price.qp.rcav.IDs != 'undefined' && typeof price.qp.rcav.IDs[2] != 'undefined') { 351 ret = true; 352 } 353 return ret; 354 }, 355 getPriceHasCoupon: function(price) { 356 var ret = false; 357 if(typeof price == undefined || typeof price.qp == undefined) { 358 return ret; 359 } 360 361 if (typeof price.qp.rcav.coupon != 'undefined' && price.qp.rcav.coupon != '') { 362 ret = true; 363 } 364 return ret; 365 }, 366 getPriceHasSavings: function(price) { 367 var ret = false; 368 if(typeof price == undefined || typeof price.qp == undefined) { 369 return ret; 370 } 371 if (typeof price.s != 'undefined' && parseFloat(price.s) > 0) { 372 ret = true; 373 } 374 return ret; 375 }, 376 parseDate: function(datestr) { 377 var dt = new Date(0, 0, 0, 0, 0, 0, 0); 378 var parts = datestr.split('-'); 379 if (parts.length == 3) { 380 dt.setFullYear(parts[0], parts[1] - 1, parts[2]); 381 } else { 382 parts = datestr.split('/'); 383 if (parts.length == 3) { 384 dt.setFullYear(parts[2], parts[0] - 1, parts[1]); 385 } 386 } 387 return dt.getTime(); 388 }, 389 }; 390 riot.mixin('RCRiotAvailPricing', RCRiotAvailPricing); 391 392 $(document).ready(function() { 393 if (typeof RiotSolrItemSortHandlers !== 'undefined') { 394 RiotSolrItemSortHandlers['sortPrice'] = { 395 primer: function(eid, reverse) { 396 var pricing = RCRiotAvailPricing.getItemPricing(eid); 397 if (pricing && pricing.hasOwnProperty('range') && Array.isArray(pricing.range) && pricing.range.length >= 2) { 398 // Price range values are low [0] and high [1]. 399 return reverse ? parseFloat(pricing.range[1]) : parseFloat(pricing.range[0]); 400 } 401 // Don't affect the sort if we have no prices. 402 return 0; 403 }, 404 callback: function(items, field, reverse, secondary) { 405 var self = this, 406 r = !reverse ? 1 : -1; 407 items.sort(function(a, b) { 408 var a_field = self.primer(a[field], reverse), 409 b_field = self.primer(b[field], reverse), 410 order = r * ((a_field > b_field) - (b_field > a_field)); 411 412 if (!order && 413 secondary && 414 typeof RiotSolrItemSortHandlers.secondarySortOrder === 'function') { 415 order = RiotSolrItemSortHandlers.secondarySortOrder(a, b, secondary); 416 } 417 418 return order; 419 }); 420 return items; 421 }, 422 }; 423 } 424 }); 425})(jQuery);
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.