1/* https://github.com/peledies/google-places */ 2(function($) { 3 4 var namespace = 'googlePlaces'; 5 6 $.googlePlaces = function(element, options) { 7 8 var defaults = { 9 placeId: 'ChIJN1t_tDeuEmsRUsoyG83frY4' // placeId provided by google api documentation 10 , render: ['reviews'] 11 , min_rating: 0 12 , max_rows: 0 13 , map_plug_id: 'map-plug' 14 , rotateTime: false 15 , shorten_names: true 16 , schema:{ 17 displayElement: '#schema' 18 , type: 'Store' 19 , beforeText: 'Google Users Have Rated' 20 , middleText: 'based on' 21 , afterText: 'ratings and reviews' 22 , image: null 23 , priceRange: null 24 } 25 , address:{ 26 displayElement: "#google-address" 27 } 28 , phone:{ 29 displayElement: "#google-phone" 30 } 31 , staticMap:{ 32 displayElement: "#google-static-map" 33 , width: 512 34 , height: 512 35 , zoom: 17 36 , type: "roadmap" 37 } 38 , hours:{ 39 displayElement: "#google-hours" 40 } 41 }; 42 43 var plugin = this; 44 45 plugin.settings = {} 46 47 var $element = $(element), 48 element = element; 49 50 plugin.init = function() { 51 plugin.settings = $.extend({}, defaults, options); 52 plugin.settings.schema = $.extend({}, defaults.schema, options.schema); 53 $element.html("<div id='" + plugin.settings.map_plug_id + "'></div>"); // create a plug for google to load data into 54 initialize_place(function(place){ 55 plugin.place_data = place; 56 57 // Trigger event before render 58 $element.trigger('beforeRender.' + namespace); 59 60 if(plugin.settings.render.indexOf('rating') > -1){ 61 renderRating(plugin.place_data.rating); 62 } 63 // render specified sections 64 if(plugin.settings.render.indexOf('reviews') > -1){ 65 renderReviews(plugin.place_data.reviews); 66 if(!!plugin.settings.rotateTime) { 67 initRotation(); 68 } 69 } 70 if(plugin.settings.render.indexOf('address') > -1){ 71 renderAddress( 72 capture_element(plugin.settings.address.displayElement) 73 , plugin.place_data.adr_address 74 ); 75 } 76 if(plugin.settings.render.indexOf('phone') > -1){ 77 renderPhone( 78 capture_element(plugin.settings.phone.displayElement) 79 , plugin.place_data.formatted_phone_number 80 ); 81 } 82 if(plugin.settings.render.indexOf('staticMap') > -1){ 83 renderStaticMap( 84 capture_element(plugin.settings.staticMap.displayElement) 85 , plugin.place_data.formatted_address 86 ); 87 } 88 if(plugin.settings.render.indexOf('hours') > -1){ 89 renderHours( 90 capture_element(plugin.settings.hours.displayElement) 91 , plugin.place_data.opening_hours 92 ); 93 } 94 95 // render schema markup 96 addSchemaMarkup( 97 capture_element(plugin.settings.schema.displayElement) 98 , plugin.place_data 99 ); 100 101 // Trigger event after render 102 $element.trigger('afterRender.' + namespace); 103 104 }); 105 } 106 107 var capture_element = function(element){ 108 if(element instanceof jQuery){ 109 return element; 110 }else if(typeof element == 'string'){ 111 try{ 112 var ele = $(element); 113 if( ele.length ){ 114 return ele; 115 }else{ 116 throw 'Element [' + element + '] couldnt be found in the DOM. Skipping '+element+' markup generation.'; 117 } 118 }catch(e){ 119 console.warn(e); 120 } 121 } 122 } 123 124 var initialize_place = function(c){ 125 var map = new google.maps.Map(document.getElementById(plugin.settings.map_plug_id)); 126 127 var request = { 128 placeId: plugin.settings.placeId 129 }; 130 131 var service = new google.maps.places.PlacesService(map); 132 133 service.getDetails(request, function(place, status) { 134 if (status == google.maps.places.PlacesServiceStatus.OK) { 135 c(place); 136 } 137 }); 138 } 139 140 var sort_by_date = function(ray) { 141 ray.sort(function(a, b){ 142 var keyA = new Date(a.time), 143 keyB = new Date(b.time); 144 // Compare the 2 dates 145 if(keyA < keyB) return -1; 146 if(keyA > keyB) return 1; 147 return 0; 148 }); 149 return ray; 150 } 151 152 var filter_minimum_rating = function(reviews){ 153 for (var i = reviews.length -1; i >= 0; i--) { 154 if(reviews[i].rating < plugin.settings.min_rating){ 155 reviews.splice(i,1); 156 } 157 } 158 return reviews; 159 } 160 161 var renderRating = function(rating){ 162 var html = ""; 163 var star = renderAverageStars(rating); 164 html = "<div class='average-rating'><h4>"+star+"</h4></div>"; 165 $element.append(html); 166 } 167 168 var shorten_name = function(name) { 169 if (name.split(" ").length > 1) { 170 var xname = ""; 171 xname = name.split(" "); 172 return xname[0] + " " + xname[1][0] + "."; 173 } 174 } 175 176 var renderReviews = function(reviews){ 177 reviews = sort_by_date(reviews); 178 reviews = filter_minimum_rating(reviews); 179 var html = ""; 180 var row_count = (plugin.settings.max_rows > 0)? plugin.settings.max_rows - 1 : reviews.length - 1; 181 // make sure the row_count is not greater than available records 182 row_count = (row_count > reviews.length-1)? reviews.length -1 : row_count; 183 for (var i = row_count; i >= 0; i--) { 184 var stars = renderStars(reviews[i].rating); 185 var date = convertTime(reviews[i].time); 186 if(plugin.settings.shorten_names == true) { 187 var name = shorten_name(reviews[i].author_name); 188 } else { 189 var name = reviews[i].author_name + "</span><span class='review-sep'>, </span>"; 190 }; 191 html = html+"<div class='review-item'><div class='review-meta'><span class='review-author'>"+name+"<span class='review-date'>"+date+"</span></div>"+stars+"<p class='review-text'>"+reviews[i].text+"</p></div>" 192 }; 193 $element.append(html); 194 } 195 196 var renderHours = function(element, data){ 197 if(element instanceof jQuery){ 198 var html = "<ul>"; 199 data.weekday_text.forEach(function(day){ 200 html += "<li>"+day+"</li>"; 201 }); 202 html += "</ul>"; 203 element.append(html); 204 } 205 } 206 207 var renderStaticMap = function(element, data){ 208 if(element instanceof jQuery){ 209 var map = plugin.settings.staticMap; 210 element.append(
211 "<img src='https://maps.googleapis.com/maps/api/staticmap"+ 212 "?size="+map.width+"x"+map.height+ 213 "&zoom="+map.zoom+ 214 "&maptype="+map.type+ 215 "&markers=size:large%7Ccolor:red%7C"+data+"'>"+ 216 "</img>"); 217 } 218 } 219 220 var renderAddress = function(element, data){ 221 if(element instanceof jQuery){ 222 element.append(data); 223 } 224 } 225 226 var renderPhone = function(element, data){ 227 if(element instanceof jQuery){ 228 element.append(data); 229 } 230 } 231 232 var initRotation = function() { 233 var $reviewEls = $element.children('.review-item'); 234 var currentIdx = $reviewEls.length > 0 ? 0 : false; 235 $reviewEls.hide(); 236 if(currentIdx !== false) { 237 $($reviewEls[currentIdx]).show(); 238 setInterval(function(){ 239 if(++currentIdx >= $reviewEls.length) { 240 currentIdx = 0; 241 } 242 $reviewEls.hide(); 243 $($reviewEls[currentIdx]).fadeIn('slow'); 244 }, plugin.settings.rotateTime); 245 } 246 } 247 248 var renderStars = function(rating){ 249 var stars = "<div class='review-stars'><ul>"; 250 251 // fill in gold stars 252 for (var i = 0; i < rating; i++) { 253 stars = stars+"<li><i class='star'></i></li>"; 254 }; 255 256 // fill in empty stars 257 if(rating < 5){ 258 for (var i = 0; i < (5 - rating); i++) { 259 stars = stars+"<li><i class='star inactive'></i></li>"; 260 }; 261 } 262 stars = stars+"</ul></div>"; 263 return stars; 264 } 265 266 var renderAverageStars = function(rating){ 267 var stars = "<div class='review-stars'><ul><li><i>"+rating+" </i></li>"; 268 var activeStars = parseInt(rating); 269 var inactiveStars = 5 - activeStars; 270 var width = (rating - activeStars) * 100 + '%'; 271 272 // fill in gold stars 273 for (var i = 0; i < activeStars; i++) { 274 stars += "<li><i class='star'></i></li>"; 275 }; 276 277 // fill in empty stars 278 if(inactiveStars > 0){ 279 for (var i = 0; i < inactiveStars; i++) { 280 if (i === 0) { 281 stars += "<li style='position: relative;'><i class='star inactive'></i><i class='star' style='position: absolute;top: 0;left: 0;overflow: hidden;width: "+width+"'></i></li>"; 282 } else { 283 stars += "<li><i class='star inactive'></i></li>"; 284 } 285 }; 286 } 287 stars += "</ul></div>"; 288 return stars; 289 } 290 291 var convertTime = function(UNIX_timestamp){ 292 var a = new Date(UNIX_timestamp * 1000); 293 var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; 294 var time = months[a.getMonth()] + ' ' + a.getDate() + ', ' + a.getFullYear(); 295 return time; 296 } 297 298 var addSchemaMarkup = function(element, placeData) { 299 300 if(element instanceof jQuery){ 301 var schema = plugin.settings.schema; 302 var schemaMarkup = '<span itemscope="" itemtype="http://schema.org/' + schema.type + '">'; 303 304 if(schema.image !== null) { 305 schemaMarkup += generateSchemaItemMarkup('image', schema.image); 306 } else { 307 console.warn('Image is required for some schema types. Visit https://search.google.com/structured-data/testing-tool to test your schema output.'); 308 } 309 310 if(schema.priceRange !== null) { 311 schemaMarkup += generateSchemaItemMarkup('priceRange', schema.priceRange); 312 } 313 314 schemaMarkup += generateSchemaItemMarkup('url', location.origin); 315 schemaMarkup += generateSchemaItemMarkup('telephone', plugin.place_data.formatted_phone_number ); 316 schemaMarkup += generateSchemaAddressMarkup(); 317 schemaMarkup += generateSchemaRatingMarkup(placeData, schema); 318 schemaMarkup += '</span>'; 319 320 element.append(schemaMarkup); 321 } 322 } 323 324 var generateSchemaAddressMarkup = function() { 325 var $address = $('<div />', { 326 itemprop: "address" 327 , itemscope: '' 328 , itemtype: "http://schema.org/PostalAddress" 329 }).css('display', 'none'); 330 $address.append(plugin.place_data.adr_address); 331 $address.children('.street-address').attr('itemprop', 'streetAddress'); 332 $address.children('.locality').attr('itemprop', 'addressLocality'); 333 $address.children('.region').attr('itemprop', 'addressRegion'); 334 $address.children('.postal-code').attr('itemprop', 'postalCode'); 335 $address.children('.country-name').attr('itemprop', 'addressCountry'); 336 return $address[0].outerHTML; 337 } 338 339 var generateSchemaRatingMarkup = function(placeData, schema) { 340 var reviews = placeData.reviews; 341 var lastIndex = reviews.length - 1; 342 var reviewPointTotal = 0; 343 344 for (var i = lastIndex; i >= 0; i--) { 345 reviewPointTotal += reviews[i].rating; 346 }; 347 348 var averageReview = reviewPointTotal / ( reviews.length ); 349 350 return schema.beforeText + ' <span itemprop="name">' + placeData.name + '</span> ' 351 + '<span itemprop="aggregateRating" itemscope="" itemtype="http://schema.org/AggregateRating">'
352 + '<span itemprop="ratingValue">' + averageReview.toFixed(2) + '</span>/<span itemprop="bestRating">5</span> ' 353 + schema.middleText + ' <span itemprop="ratingCount">' + reviews.length + '</span> ' 354 + schema.afterText 355 + '</span>' 356 } 357 358 var generateSchemaItemMarkup = function(name, value) { 359 return '<meta itemprop="' + name + '" content="' + value + '">' 360 } 361 362 plugin.init(); 363 364 } 365 366 $.fn.googlePlaces = function(options) { 367 368 return this.each(function() { 369 if (undefined == $(this).data(namespace)) { 370 var plugin = new $.googlePlaces(this, options); 371 $(this).data(namespace, plugin); 372 } 373 }); 374 375 } 376 377})(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.