-
-
Save draeton/93c00021798c6048e954 to your computer and use it in GitHub Desktop.
Ford » Ratings and Reviews code sample
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*global jQuery,ngbs*/ | |
(function ($, ngbs) { | |
"use strict"; | |
/** | |
* ngbs.widget: ratings-and-reviews-pagination | |
* | |
* Copyright (c) 2012, Team Detroit | |
* All rights reserved | |
* | |
* @desc Pagination widget setup and event handlers | |
* @dependencies jQuery, ngbs | |
*/ | |
ngbs.widget("ratings-and-reviews-pagination", function (message_bus, $elements) { | |
var PaginationWidget = function (element) { | |
this.$element = $(element); | |
this.$pagination = this.$element.find(".rr-pagination"); | |
this.baseURL = null; | |
this.currentPage = null; | |
this.totalPages = null; | |
this.search = null; | |
this.initialize(); | |
}; | |
PaginationWidget.prototype = { | |
/** | |
* initialize | |
*/ | |
initialize: function () { | |
this.setPageData(); | |
this.proxyHandlers(); | |
this.bindHandlers(); | |
}, | |
/** | |
* Set the current page values from the data-* attributes | |
*/ | |
setPageData: function () { | |
var data = this.$element.data(); | |
this.baseurl = data.baseUrl; | |
this.currentpage = Number(data.currentPage); | |
this.totalpages = Number(data.totalPages); | |
this.search = data.search; | |
}, | |
/** | |
* proxyHandlers | |
*/ | |
proxyHandlers: function () { | |
this.clickHandler = $.proxy(this.clickHandler, this); | |
this.updateView = $.proxy(this.updateView, this); | |
}, | |
/** | |
* bindHandlers | |
*/ | |
bindHandlers: function () { | |
this.$element.on("click", "a", this.clickHandler); | |
message_bus.listen(document, "view.update", this.updateView); | |
}, | |
/** | |
* clickHandler | |
*/ | |
clickHandler: function (e) { | |
e.stopPropagation(); | |
e.preventDefault(); | |
var $anchor = $(e.currentTarget); | |
var isDisabled = $anchor.is(".disabled"); | |
var page = Number($anchor.data("page")); | |
if (!isDisabled && page !== this.currentPage) { | |
this.currentPage = page; | |
// message bus | |
message_bus.send(this.$element, "page.change", { | |
page: page | |
}); | |
} | |
}, | |
/** | |
* Update the pagination based on the JSON response from the API | |
*/ | |
updateView: function (e, data) { | |
var results = ""; | |
if (data && data.wrapper) { | |
// update pagination widget content | |
this.currentPage = data.wrapper.currentPage; | |
this.totalPages = data.wrapper.totalPages; | |
this.renderView(); | |
this.renderDisplayResults(data.wrapper); | |
} | |
}, | |
/** | |
* Calculcate start, end, next and prev based on current and total pages | |
*/ | |
calculatePages: function () { | |
var currentPage = this.currentPage; | |
var totalPages = this.totalPages; | |
// calc | |
var startPage = currentPage - ((currentPage - 1) % 3); | |
var endPage = totalPages <= (startPage + 2) ? totalPages : (startPage + 2); | |
var prevPage = startPage > 1 ? (startPage - 1) : ""; | |
var nextPage = endPage < totalPages ? (endPage + 1) : ""; | |
return { | |
start: startPage, | |
end: endPage, | |
prev: prevPage, | |
next: nextPage | |
}; | |
}, | |
/** | |
* Get the anchor href based on page number | |
*/ | |
getHref: function (page) { | |
var href = this.baseURL + page + this.search; | |
if (String(page) === "1") { | |
href = this.baseURL + this.search; | |
} | |
return href; | |
}, | |
/** | |
* Get a list item string | |
*/ | |
getListItem: function (page, className, content) { | |
var href = this.getHref(page); | |
return "<li><a href='" + href + "' data-page='" + page + "' class='" + className + "'>" + content + "</a></li>"; | |
}, | |
/** | |
* Render the page number display | |
*/ | |
renderView: function () { | |
var pages = this.calculatePages(); | |
var lis = ""; | |
// render prev | |
lis += this.getListItem(pages.prev, (pages.prev === "" ? "prev disabled" : "prev"), "<span class='ui-arrow-s ui-arrow-s-blue-l-png'></span>PREV"); | |
// render pages | |
var page; | |
for (page = pages.start; page <= pages.end; page++) { | |
lis += this.getListItem(page, (page === this.currentPage ? "selected" : ""), page); | |
} | |
// render next | |
lis += this.getListItem(pages.next, (pages.next === "" ? "next disabled" : "next"), "NEXT<span class='ui-arrow-s ui-arrow-s-blue-r-png'></span>"); | |
// place in dom | |
var $lis = $(lis); | |
this.$element.find("ul").empty().append($lis); | |
}, | |
/** | |
* Render display resutls string | |
*/ | |
renderDisplayResults: function (wrapper) { | |
var currentPage = wrapper.currentPage; | |
var reviewsPerPage = wrapper.reviewsPerPage; | |
var totalReviews = wrapper.totalReviews; | |
var start = totalReviews ? ((currentPage - 1) * reviewsPerPage) + 1 : 0; | |
var end = Math.min(currentPage * reviewsPerPage, totalReviews); | |
var results = start + "-" + end + " of " + totalReviews; | |
// message bus | |
message_bus.send(document, "results.update", results); | |
} | |
}; | |
// initialization for elements not included on page load | |
message_bus.listen(document, "ratings-and-reviews-pagination.add", function (e, data) { | |
var element = e.target; | |
var paginationWidget = new PaginationWidget(element); | |
}); | |
// get started | |
message_bus.send($elements, "ratings-and-reviews-pagination.add"); | |
}); | |
}(jQuery, ngbs)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*global jQuery,ngbs*/ | |
(function ($, ngbs) { | |
"use strict"; | |
/** | |
* ngbs.widget: ratings-and-reviews-radio | |
* | |
* Copyright (c) 2012, Team Detroit | |
* All rights reserved | |
* | |
* @desc Radio widget setup and event handlers | |
* @dependencies jQuery, ngbs | |
*/ | |
ngbs.widget("ratings-and-reviews-radio", function (message_bus, $elements) { | |
var RadioWidget = function (element) { | |
this.$element = $(element); | |
this.$radio = this.$element.find(".rr-radio"); | |
this.$input = this.$element.find("input"); | |
this.$buttons = this.$element.find(".rr-radio-button"); | |
this.initialize(); | |
}; | |
RadioWidget.prototype = { | |
/** | |
* initialize | |
*/ | |
initialize: function () { | |
this.setWidgetData(); | |
this.proxyHandlers(); | |
this.bindHandlers(); | |
}, | |
/** | |
* setWidgetData | |
*/ | |
setWidgetData: function () { | |
var data = this.$element.data(); | |
this.name = data.name; | |
this.label = data.label; | |
}, | |
/** | |
* proxyHandlers | |
*/ | |
proxyHandlers: function () { | |
this.clickButtonHandler = $.proxy(this.clickButtonHandler, this); | |
this.updateHandler = $.proxy(this.updateHandler, this); | |
}, | |
/** | |
* bindHandlers | |
*/ | |
bindHandlers: function () { | |
this.$element.on("click", "div.rr-radio-button", this.clickButtonHandler); | |
message_bus.listen(this.$element, "input.update", this.updateHandler); | |
}, | |
/** | |
* clickButtonHandler | |
*/ | |
clickButtonHandler: function (e) { | |
var $button = $(e.currentTarget); | |
var data = $button.data(); | |
var key = data.key; | |
if ($button.not(".selected")) { | |
this.$buttons.removeClass("selected"); | |
$button.addClass("selected"); | |
this.$input.val(key); | |
} | |
// message bus | |
this.sendData(); | |
}, | |
/** | |
* updateHandler | |
*/ | |
updateHandler: function (e, data) { | |
if (data) { | |
this.updateView(data.key); | |
} | |
}, | |
/** | |
* Reset all buttons and then set one to match key | |
*/ | |
updateView: function (key) { | |
var self = this; | |
// reset all buttons | |
this.$buttons.removeClass("selected"); | |
this.$input.val(""); | |
// set matched key | |
if (key) { | |
this.$input.val(key); | |
this.$buttons.filter("[data-key=" + key + "]").addClass("selected"); | |
} | |
// message bus | |
this.sendData(); | |
}, | |
/** | |
* sendData | |
*/ | |
sendData: function () { | |
var $selected = this.$buttons.filter(".selected"); | |
var value = ""; | |
if ($selected.length) { | |
value = $selected.data("value"); | |
} | |
message_bus.send(this.$element, "input.change", { | |
id: this.name, | |
key: this.$input.val(), | |
label: this.label, | |
value: value | |
}); | |
} | |
}; | |
// initialization for elements not included on page load | |
message_bus.listen(document, "ratings-and-reviews-radio.add", function (e, data) { | |
var element = e.target; | |
var radioWidget = new RadioWidget(element); | |
}); | |
// get started | |
message_bus.send($elements, "ratings-and-reviews-radio.add"); | |
}); | |
}(jQuery, ngbs)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*global jQuery,ngbs*/ | |
(function ($, ngbs) { | |
"use strict"; | |
/** | |
* debounce | |
* | |
* Adapted from http://unscriptable.com/2009/03/20/debouncing-javascript-methods/ | |
*/ | |
var debounce = function (func, threshold, execAsap) { | |
var timeout; | |
return function () { | |
var context = this; | |
var args = arguments; | |
var delayed = function () { | |
if (!execAsap) { | |
func.apply(context, args); | |
} | |
timeout = null; | |
}; | |
if (timeout) { | |
window.clearTimeout(timeout); | |
} else if (execAsap) { | |
func.apply(context, args); | |
} | |
timeout = setTimeout(delayed, threshold || 50); | |
}; | |
}; | |
/** | |
* ngbs.page: ratings-and-reviews | |
* | |
* Copyright (c) 2012, Team Detroit | |
* All rights reserved | |
* | |
* @desc Handles event binds and API calls for ratings and reviews | |
* @dependencies jQuery, ngbs | |
*/ | |
ngbs.page(function (message_bus) { | |
var Reviews = function (element) { | |
this.$element = $(element); | |
this.$loading = $(".rr-reviews-content-loading"); | |
this.$content = $(".rr-reviews-content"); | |
this.$title = $(".rr-reviews-header h2"); | |
this.$displayResults = $(".rr-reviews-display-results"); | |
this.$searchForm = this.$element.find(".rr-reviews-search"); | |
this.$billboard = this.$element.find(".rr-billboard"); | |
this.baseURL = null; | |
this.avatarURL = null; | |
this.model = null; | |
this.page = null; | |
this.filter = {}; | |
this.order = null; | |
this.initialize(); | |
}; | |
Reviews.prototype = { | |
/** | |
* initialize | |
*/ | |
initialize: function () { | |
this.setPageData(); | |
this.proxyHandlers(); | |
this.bindHandlers(); | |
this.updateShare(); | |
this.updateAvatar(); | |
this.getReviews = debounce(this.getReviews, 500); | |
}, | |
/** | |
* Set the current page values from the data-* attributes | |
*/ | |
setPageData: function () { | |
var data = this.$element.find(".rr-data").data(); | |
this.baseURL = data.baseUrl; | |
this.avatarURL = data.avatarUrl; | |
this.category = data.category; | |
this.model = data.model; | |
this.page = data.page; | |
this.order = data.order; | |
this.query = data.query; | |
}, | |
/** | |
* proxyHandlers | |
*/ | |
proxyHandlers: function () { | |
this.filterHandler = $.proxy(this.filterHandler, this); | |
this.pageHandler = $.proxy(this.pageHandler, this); | |
this.orderHandler = $.proxy(this.orderHandler, this); | |
this.reviewsHandler = $.proxy(this.reviewsHandler, this); | |
this.updateTitle = $.proxy(this.updateTitle, this); | |
this.updateDisplayResults = $.proxy(this.updateDisplayResults, this); | |
this.clickSearchHandler = $.proxy(this.clickSearchHandler, this); | |
this.blurSearchHandler = $.proxy(this.blurSearchHandler, this); | |
}, | |
/** | |
* bindHandlers | |
*/ | |
bindHandlers: function () { | |
message_bus.listen(this.$element, "filter.update", this.filterHandler); | |
message_bus.listen(this.$element, "page.update", this.pageHandler); | |
message_bus.listen(this.$element, "order.update", this.orderHandler); | |
message_bus.listen(this.$element, "reviews.update", this.reviewsHandler); | |
message_bus.listen(document, "title.update", this.updateTitle); | |
message_bus.listen(document, "results.update", this.updateDisplayResults); | |
this.$searchForm.on("click", "label", this.clickSearchHandler); | |
this.$searchForm.on("blur", "input", this.blurSearchHandler); | |
}, | |
/** | |
* Update the current filters | |
*/ | |
filterHandler: function (e, data) { | |
var self = this; | |
if (data) { | |
if (data.id) { | |
this.filter[data.id] = data.key; | |
} | |
$.each(this.filter, function (i, value) { | |
if (value === "") { | |
delete self.filter[i]; | |
} | |
}); | |
if (!data.silent) { | |
this.page = 1; | |
message_bus.send(this.$element, "reviews.update"); | |
} | |
} | |
}, | |
/** | |
* Update the current sort order | |
*/ | |
orderHandler: function (e, data) { | |
if (typeof data === "object") { | |
this.order = data.key; | |
} | |
this.page = 1; | |
message_bus.send(this.$element, "reviews.update"); | |
}, | |
/** | |
* Update the current page number | |
*/ | |
pageHandler: function (e, data) { | |
if (typeof data === "object") { | |
this.page = data.page; | |
} | |
message_bus.send(this.$element, "reviews.update"); | |
}, | |
/** | |
* Start processing for udpating on page reviews | |
*/ | |
reviewsHandler: function (e, data) { | |
var self = this; | |
var titleOffset = this.$title.offset(); | |
// hide content | |
this.$content.hide(); | |
this.$loading.show(); | |
// scroll to top of content | |
$('html, body, document').animate({scrollTop: titleOffset.top + 'px'}, 350); | |
// render callback function | |
var callback = function (response) { | |
self.updateContent(response); | |
}; | |
// select api call | |
if (this.query) { | |
this.searchReviews(callback); | |
} else { | |
this.getReviews(callback); | |
} | |
}, | |
/** | |
* Make an API call for reviews based on current settings | |
*/ | |
getReviews: function (callback) { | |
var params = this.getReviewsParams(); | |
// load reviews data | |
$.get("/core-services/reviews.json", params).done(function (response) { | |
message_bus.send(document, "view.update", response); | |
}); | |
// return promsie for reviews content | |
$.get("/core-services/reviews.html", params).done(callback); | |
// metrics | |
message_bus.send(this.$element, "click.metrics", { | |
name: "filter" | |
}); | |
}, | |
/** | |
* Serialize filters and return params object | |
*/ | |
getReviewsParams: function () { | |
var filters = []; | |
$.each(this.filter, function (index, filter) { | |
filters.push(index + "=" + filter); | |
}); | |
return { | |
model: this.category, | |
page: this.page, | |
filter: filters.join(","), | |
order: this.order | |
}; | |
}, | |
/** | |
* Make an API call to search for reviews | |
*/ | |
searchReviews: function (callback) { | |
var params = { | |
model: this.category, | |
query: this.query, | |
page: this.page | |
}; | |
// load reviews data | |
$.get("/core-services/reviews/search.json", params).done(function (response) { | |
message_bus.send(document, "view.update", response); | |
}); | |
// return promsie for reviews content | |
$.get("/core-services/reviews/search.html", params).done(callback); | |
// metrics | |
message_bus.send(this.$element, "click.metrics", { | |
name: "filter" | |
}); | |
}, | |
/** | |
* Update the on-page title | |
*/ | |
updateTitle: function (e, data) { | |
var title = data; | |
title = title.replace("%nameplate%", this.model); | |
this.$title.html(title); | |
}, | |
/** | |
* Update the on-page display results | |
*/ | |
updateDisplayResults: function (e, data) { | |
var results = data; | |
this.$displayResults.html(results); | |
}, | |
/** | |
* Update and reveal reviews content | |
*/ | |
updateContent: function (content) { | |
// change content | |
this.$content.html(content); | |
// update avatars | |
this.updateAvatar(); | |
// update share | |
this.updateShare(); | |
//reveal | |
this.$loading.hide(); | |
this.$content.show(); | |
}, | |
/** | |
* Update avatar images | |
*/ | |
updateAvatar: function () { | |
var self = this; | |
this.$element.find("img.avatar").each(function () { | |
var $img = $(this); | |
var id = $img.data("id"); | |
var src = id ? self.avatarURL + "&id=" + id : self.avatarURL; | |
$img.attr("src", src).show(); | |
}); | |
}, | |
/** | |
* Update share buttons | |
*/ | |
updateShare: function () { | |
var self = this; | |
var image = this.$billboard.data("image"); | |
// share buttons | |
this.$element.find("a.share").each(function () { | |
var $anchor = $(this); | |
var path = $anchor.data("path"); | |
var href = self.baseURL + path; | |
$anchor.attr("href", href); | |
$anchor.data("image", image); | |
// send to ImportShareThisWidget queue | |
message_bus.send($anchor, "queue"); | |
}); | |
}, | |
/** | |
* Upon clicking on the search form, hide the label | |
*/ | |
clickSearchHandler: function (e) { | |
var $label = $(e.currentTarget); | |
$label.hide(); | |
}, | |
/** | |
* On blur of the search input, show the label again if it is blank | |
*/ | |
blurSearchHandler: function (e) { | |
var $input = $(e.currentTarget); | |
var $label = this.$searchForm.find("label"); | |
if ($input.val() === "") { | |
$label.show(); | |
} | |
} | |
}; | |
// get started | |
var reviews = new Reviews(window.document.body); | |
}); | |
}(jQuery, ngbs)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment