Created
October 8, 2012 23:33
-
-
Save natchiketa/3855626 to your computer and use it in GitHub Desktop.
Fetching results, sorting, filter events
This file contains hidden or 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
/* | |
* __ _ _ | |
* / _| ___| |_ ___| |__ _ __ __ _ _ __ __ _ _ __ ___ ___ | |
* | |_ / _ \ __/ __| '_ \ | '_ \ / _` | '__/ _` | '_ ` _ \/ __| | |
* | _| __/ || (__| | | | | |_) | (_| | | | (_| | | | | | \__ \ | |
* |_| \___|\__\___|_| |_| | .__/ \__,_|_| \__,_|_| |_| |_|___/ | |
* |_| | |
* | |
* */ | |
// used as a key for referencing the CSS class to ascending or descending when making a fetchResults Ajax call | |
var orderingKey = { az:'asc', za:'desc' }; | |
var globalTemplates = {}; | |
// make AJAX call to follow specified user | |
function follow_user(username) { | |
console.log('Toggling following of user ' + username); | |
$.get("/ajax/follow/member", {id:username}, function (data) { | |
}); | |
} | |
function offset() { | |
return parseInt($("#offset").val()); | |
} | |
function numberOfResults(options) { | |
return (typeof options == 'undefined') ? parseInt($("#number_of_results").val()) : options.number_of_results; | |
} | |
function updateOptReset(element) { | |
// if there are any hidden options set the text of the more link to 'more' | |
if ($(element).parent().siblings('.show_w_more').is(":visible")) { | |
$(element).siblings('.more').text('Less'); | |
} else { | |
$(element).siblings('.more').text('More'); | |
} | |
} | |
function optParams(paramOverrides) { | |
// 1. Get the option params for the left nav | |
// 2. return as an object | |
// initialize the params object with search_terms as the first value | |
var params = { | |
'search_terms':$("#current_search_params").val(), | |
'offset':offset(), | |
'number_of_results':numberOfResults() | |
}; | |
var param; | |
try { | |
var order_criteria = $('#order_by a.active').attr('id'); | |
var order_asc_desc = $('#'+order_criteria).attr('class').split(' ').first(); | |
var order_param = order_criteria+','+orderingKey[order_asc_desc]; | |
$.extend(params, { | |
order_by:order_param | |
}); | |
} catch(e) { | |
/*console.log(e);*/ | |
} | |
// iterate through the list options (e.g. style, skill, location) | |
$('ul.cat > li > a.cat_opt').each(function () { | |
if ($(this).hasClass('active')) { | |
param = $(this).parent().attr('class').split(' ')[0]; | |
params[param] = 1; | |
} | |
}); | |
// iterate through the checkbox options (e.g. media, signed, gender, etc.) | |
$('.cat input.cat_opt').each(function () { | |
if ($(this).is(':checked')) { | |
param = $(this).parent().attr('class').split(' ')[0]; | |
params[param] = 1; | |
} | |
}); | |
// get the age slider params | |
$.extend(params, { | |
'min_age': parseInt($('#slider-range').slider("values",0)), | |
'max_age': parseInt($('#slider-range').slider("values",1)) | |
}); | |
paramOverrides = (typeof paramOverrides == 'undefined') ? {} : paramOverrides; | |
$.extend(true, params, paramOverrides); | |
return params; | |
} | |
/* LAZY-LOADING FUNCTIONS */ | |
function resultLoadingBlock(numberOfResults, cls) { | |
cls = (typeof cls == "undefined") ? "loading" : cls; | |
var blocks = []; | |
for (var i = 0; i < numberOfResults; i++) { | |
blocks.push($('<li/>', { | |
class:'singer ' + cls | |
})[0]); | |
} | |
return blocks; | |
} | |
function fetchResults(method, options) { | |
/** | |
* .. js:function:: fetchResults(method) | |
* Fetch more results via AJAX call. Requires jQuery and webtookit.base64.js (at https://gist.github.com/73711) | |
* | |
* Arguments: | |
* :param string method: How to handle fetched results. Possible Values are: | |
* - 'replace' : Remove the current items | |
* - 'append' : Use jQuery to append each result | |
* - 'handler' : an object which passes functions to handle any additional logic for either replace or append. | |
* :param string uri: Ajax endpoint from which to fetch results. | |
* :param object handler: An object 'replace' and 'append' functions which are passed the Ajax response. | |
* | |
*/ | |
// If the uri or handler params were not passed, set them to the global defaults (i.e. fetchUri, options.handler) | |
/* uri = (typeof uri == 'undefined') ? fetchUri : uri; | |
handler = (typeof handler == 'undefined') ? options.handler : handler; | |
if (typeof fetchElem == 'undefined' ) { fetchElem = options.selector }*/ | |
keepLazyLoadStyles = (typeof keepLazyLoadStyles == 'undefined') ? false : keepLazyLoadStyles; | |
options.paramOverrides = (typeof options.paramOverrides == 'undefined') ? {} : options.paramOverrides; | |
// Begin the transition based on the method. | |
// - replace | |
if (method == 'replace') { | |
$(options.selector.container).addClass('fetching'); | |
$('#offset').val(0); | |
// if there the results container div is shorter than the window | |
if ($(options.selector.container).height() < $(window).height()) { | |
// animate/increase the height of the container to make the transition smoother | |
$(options.selector.container).animate({ | |
height:$(window).height() | |
}, fadeouts.speed * 0.3); | |
} | |
// If there are no results (e.g. initial page load), add a result so the | |
// first callback below is still triggered (we remove the 'loading' class | |
// so the block is treated as an old result | |
var singers = $(options.selector.result).length; | |
if (singers < numberOfResults(options.paramOverrides)) { | |
$(options.selector.container).append(resultLoadingBlock((numberOfResults(options.paramOverrides) - singers), "")); | |
if (options.handler.loadblock) { options.handler.loadblock() } | |
// $(options.selector.loading).removeClass('loading'); | |
} | |
// set the results container height explicitly so its height doesn't fluxuate during the transition | |
$(options.selector.container).height($(options.selector.container).height()); | |
// fade out the current results (w/callback to remove when faded) | |
$(options.selector.container).animate( | |
{ opacity:0 }, fadeouts.speed / 2, | |
// callback after each of the current results are faded out | |
function () { | |
//remove each element | |
$(options.selector.container).html(""); | |
if (options.handler.loadblock) { options.handler.loadblock() } | |
// append loading spinners and fade them in | |
$(options.selector.container).append(resultLoadingBlock(numberOfResults(), "loading")); | |
$(options.selector.container).animate({opacity:1}, fadeouts.speed / 2, function () { | |
// fetch results via AJAX | |
$.get(options.uri, optParams(options.paramOverrides), function (data) { | |
console.log('response received successfully'); | |
// call handler.filter() on the data, if it has been defined | |
data = (typeof options.handler.filter == "function") ? options.handler.filter(data) : data; | |
// reset data.results to its base-64 decoded value | |
/*data.results = Base64.decode(data.results);*/ | |
// fade out the current results (w/callback to remove when faded) | |
$(options.selector.container).animate( | |
{ opacity:0 }, fadeouts.speed / 2, | |
// callback after the current results are faded out | |
function () { | |
$(options.selector.loading).animate({opacity:0}, fadeouts.speed / 2, | |
function () { | |
$(this).remove(); | |
}); | |
// handler.replace logic runs here: | |
options.handler.replace(data); | |
// call the prefadein handler if it's defined | |
if (options.handler.prefadein) { options.handler.prefadein() } | |
// - fade in the new results | |
$(options.selector.container).animate({opacity:1}, fadeouts.speed / 2, function () { | |
if (keepLazyLoadStyles != true) { | |
$(options.selector.container).removeAttr('style'); | |
$(options.selector.result).removeAttr('style'); | |
} | |
$(options.selector.new).removeClass('new'); | |
// call the prefadein handler if it's defined | |
if (options.handler.postfadein) { options.handler.postfadein() } | |
$(options.selector.container).removeClass('fetching'); | |
}); | |
}); | |
}); | |
}); | |
}); | |
} | |
// - append | |
if (method == 'append') { | |
$(options.selector.container).addClass('fetching'); | |
// append loading spinners and fade them in | |
$(options.selector.container).append(resultLoadingBlock(numberOfResults(), "loading hide")); | |
if (options.handler.loadblock) { options.handler.loadblock() } | |
$(options.selector.loading).animate({opacity:1}, fadeouts.speed, function () { | |
// set the results container height explicitly so its height doesn't fluxuate during the transition | |
$(options.selector.container).height($(options.selector.container).height()); | |
// increment counter first (we're comparing with a 1-indexed value) | |
fadeouts.replSong++; | |
// if this is the last callback, show the loading blocks | |
if (fadeouts.replSong == numberOfResults()) { | |
// reset the counter | |
fadeouts.replSong = 0; | |
// fetch results via AJAX | |
$.get(options.uri, optParams(options.paramOverrides), function (data) { | |
console.log('response received successfully') | |
// call handler.filter() on the data, if it has been defined | |
data = (typeof options.handler.filter == "function") ? options.handler.filter(data) : data; | |
setTimeout(function () { | |
// handler.append logic runs here: | |
options.handler.append(data); | |
// - hide the new results | |
$(options.selector.new).css('opacity', 0); | |
// - fade out, then continue on the last iteration of the callback | |
$(options.selector.loading).animate({opacity:0}, fadeouts.speed, function () { | |
//remove each element | |
$(this).remove(); | |
// increment counter first (we're comparing with a 1-indexed value) | |
fadeouts.replSong++; | |
// if this is the last callback, show the loading blocks | |
if (fadeouts.replSong == numberOfResults()) { | |
// reset the counter | |
fadeouts.replSong = 0; | |
// remove the loading blocks | |
$(options.selector.loading).remove(); | |
// call the prefadein handler if it's defined | |
if (options.handler.prefadein) { options.handler.prefadein() } | |
// fade in the results | |
$(options.selector.new).animate({opacity:1}, fadeouts.speed); | |
$(options.selector.new).removeClass('new'); | |
// remove the element style to revert to 'height: auto;' (default) | |
if (keepLazyLoadStyles != true) { | |
$(options.selector.container).removeAttr('style'); | |
} | |
// call the prefadein handler if it's defined | |
if (options.handler.postfadein) { options.handler.postfadein() } | |
$(options.selector.container).removeClass('fetching'); | |
} | |
}); | |
}, 0); | |
}); | |
} | |
}); | |
} | |
} | |
// POPOVER HELPER | |
/* TODO: Consider refactoring this as jQuery plugin */ | |
function positionAsPopover(element, popover, offVals, spacing) { | |
/* | |
Position an element relative to another element. This works in essentially two ways: | |
1: Simple centered positioning with offVals as a string: | |
Position popover at center-edge of element. | |
Valid values for offVals are the strings 'top', 'bottom', 'left' or 'right'. Popover | |
is positioned so that its vertical center aligns with that of element when on left or right, | |
or on horizontal centers if 'top' or 'bottom'. | |
2: offVals as an object with additional offset values | |
For this, offVals must be an .offset() object with top and left values, as well as a offVals | |
attribute which is a string as above. In this case, popover will center as above, then add the | |
additional offset values. NOTE: the values are always added, so to move up or left, make the | |
values negative, positive for down or right. | |
This is useful for when popover has a callout arrow graphic, and you want to position | |
the popover so that that arrow points at element. | |
*/ | |
spacing = (typeof spacing == 'undefined') ? 10 : spacing; | |
// element values for convenience | |
var elemOff = $(element).offset(); | |
var elemHt = $(element).height(); | |
var elemWt = $(element).width(); | |
// element values for convenience | |
var popOff = $(popover).offset(); | |
var popHt = $(popover).height(); | |
var popWt = $(popover).width(); | |
var sideOff; | |
var sideval = (typeof offVals == 'string') ? offVals : offVals.side; | |
switch(sideval) { | |
case 'top': | |
case 'bottom': | |
case 'left': | |
sideOff = elemOff.left; | |
break; | |
case 'right': | |
sideOff = elemOff.left + elemWt; | |
break; | |
} | |
var spaceOff = (offVals == 'left' || offVals == 'top' || offVals == 'bottom') ? spacing*-1 : spacing; | |
var popoverNewpos = { | |
//align element on popover on their vertical centers | |
top: (elemOff.top + (elemHt/2)) - (popHt /2), | |
left: sideOff + spaceOff | |
} | |
if (typeof offVals == 'object') { | |
// NOTE: top and left must both be defined, even if only adjusting one of them | |
popoverNewpos.top += offVals.top; | |
popoverNewpos.left += offVals.left; | |
} | |
$(popover).offset(popoverNewpos); | |
} | |
function initStars(member) { | |
/** | |
* Initialize a raty node. The node should have the '.new' class, which is then removed after | |
* initialization. */ | |
var starImages = { | |
off: { | |
'star_small new' : 'star-off11x11.png', | |
'stars_big new': 'star-off.png', | |
'star_big new' : 'star-off.png' | |
}, | |
on: { | |
'star_small new': 'star-on11x11.png', | |
'stars_big new': 'star-on.png', | |
'star_big new' : 'star-on.png' | |
} | |
} | |
$('.star_big.new, .stars_big.new, .star_small.new') | |
.raty({ | |
path:'/images/raty-img/', | |
starOff:starImages.off[$(this).attr('class')], | |
starOn:starImages.on[$(this).attr('class')], | |
number:5, | |
// Should be read-only if user is not logged in | |
readOnly: ($('input[name="is_logged_in"]').val() != 1), | |
click:function (score, evt) { | |
var AjaxManager; | |
AjaxManager = $.manageAjax.create('AjaxRatingProfile', { | |
queue:true, | |
cacheResponse:false | |
}); | |
AjaxManager.clear('AjaxRatingProfile', true); | |
$.manageAjax.add('AjaxRatingProfile', { | |
url:'/ajax/rating/add', | |
type:'POST', | |
data:{ | |
"rating":score, | |
"member_id": member | |
}, | |
success:function (data, textStatus, jqXHR) { | |
} | |
}); | |
}, | |
start:function () { | |
return $(this).attr('data-rating'); | |
} | |
}) | |
.removeClass('new'); | |
} | |
/* GLOBAL TEMPLATES */ | |
function loadGlobalMustacheTemplates() { | |
$.ajax({ url:'/mt/tooltip.html', async:false }) | |
.done(function (template) { | |
globalTemplates.tooltip = Base64.encode(template) | |
}); | |
} | |
/* HELPER FUNCTIONS */ | |
// requires this plugin: http://sanderkorvemaker.nl/jquery/js/jquery.lorem.js | |
function loremWords(amount) { | |
return $('<div/>').lorem({ type:'words', amount:Math.round(Math.random()*amount), ptags:false}).text() | |
} | |
// Takes a Javascript timestamp (milliseconds since 1970) and returns things like: | |
// 'Today 12:34', '2 days ago', '2 weeks ago' or '03 Jul' (if more than three weeks ago) | |
function humanizeDate(timestamp){ | |
var date = Date.create(timestamp); | |
var humanized = date.format(); | |
if (date.isToday()) { humanized = date.format("today {HH}:{mm}") } | |
if (date.isYesterday()) { humanized = date.format("yesterday {HH}:{mm}") } | |
if (date.isBetween('2 days ago', '12 days ago')) { humanized = date.daysUntil('today') + ' days ago' } | |
if (date.isBetween('13 days ago', '4 weeks ago')) { humanized = date.weeksUntil('today') + ' weeks ago' } | |
if (date.isBefore('4 weeks ago')) { humanized = date.format('{d} {Mon}') } | |
return humanized; | |
} | |
$ = jQuery.noConflict(); | |
jQuery(document).ready(function ($) { | |
$('#instruments_options a').click(function (obj) { | |
obj.preventDefault() | |
$('#instruments_options div.active').fadeOut('slow').removeClass('active') | |
$(this).next('div').fadeIn('slow').addClass('active'); | |
}) | |
$('#green, #pink, #yellow, #default').click(function (obj) { | |
var skill; | |
$(this).parent('div').find('span').removeClass('active') | |
switch ($(this).attr('id')) { | |
case 'yellow': | |
$(this).children('span').addClass('active') | |
skill = 'yellow' | |
break | |
case 'green': | |
$(this).children('span').addClass('active') | |
skill = 'green' | |
break | |
case 'pink': | |
$(this).children('span').addClass('active') | |
skill = 'pink' | |
break | |
case 'default': | |
$(this).children('span').addClass('active') | |
skill = '' | |
break | |
} | |
$(this).parents('div').prev('a').removeClass().addClass(skill) | |
$(this).parent('div').fadeOut('slow') | |
}) | |
$('.ellipsis').ellipsis(); | |
/* FILTERS EVENTS/INITIALIZATION */ | |
// Categories' More/Less click | |
$('ul.cat > li > a.more').click(function (event) { | |
// animate hiding/showing of each option that has the 'show_w_more' class | |
$(this).parent().siblings('.show_w_more').each(function () { | |
event.preventDefault(); | |
if ($(this).css('display') == 'none') { | |
$(this).slideDown(100, function () { | |
updateOptReset(this); | |
}); | |
$(this).parent().find('a.more').text('Less'); | |
} else { | |
if ($(this).find('a').hasClass('active') == false) { | |
$(this).slideUp(100, function () { | |
updateOptReset(this); | |
}); | |
$(this).parent().find('a.more').text('More'); | |
} | |
} | |
}); | |
}); | |
// Categories Any/All click | |
$("a.cat_reset").click(function (event) { | |
event.preventDefault(); | |
$(this).parent().parent().find('.cat_opt').removeClass('active'); | |
$(this).addClass('no_opts'); | |
setTimeout(function () { | |
fetchResults('replace', filterTarget); | |
}, 0); | |
}); | |
// Categories' click (toggle) | |
$("a.cat_opt").click(function (event) { | |
event.preventDefault(); | |
// if this is the 'folder' category, unselect all options | |
if ($(this).parents('ul').hasClass('folders')) { | |
$(this).parents('ul').find('li a.cat_opt').removeClass('active') | |
} | |
// toggle the element's active state | |
$(this).toggleClass('active'); | |
// capture reset link to variable | |
var resetLink = $(this).parent().parent().find('.cat_reset'); | |
// if this option is now active and the reset button has the 'no_opts' class, remove it. | |
if ($(this).hasClass('active') && $(resetLink).hasClass('no_opts')) { | |
$(resetLink).removeClass('no_opts'); | |
} | |
// if there are now no options selected for this group, add 'no_opts' class to reset link | |
if ($(this).parent().parent().find('.cat_opt').hasClass('active') == false) { | |
$(resetLink).addClass('no_opts') | |
} | |
setTimeout(function () { | |
fetchResults('replace', filterTarget); | |
}, 0); | |
}); | |
// Checkbox click | |
$("input.cat_opt").change(function(){ | |
setTimeout(function(){ | |
fetchResults('replace', filterTarget); | |
}, 0); | |
}); | |
// 'Order by' click | |
// sorting links | |
$('#order_by a').click(function(event) { | |
event.preventDefault(); | |
// if the sort method does not have a sorting class, give it the ascending class, 'az' | |
if ( !($(this).hasClass('az')) && !($(this).hasClass('za')) ) { | |
$(this).addClass('az'); | |
} | |
// if the sort method is already active, reverse the first class (resulting in either 'az' or 'za') | |
if ($(this).hasClass('active')) { | |
$(this) | |
.removeClass('active') // 1. remove the active class, | |
.attr('class', $(this).attr('class').reverse()) // 2. which makes it easier to toggle az/za | |
.addClass('active'); // 3. then add the active class back in | |
} else { | |
// otherwise just remove active from the other links, then add the active class | |
$('#order_by a').removeClass('active'); | |
$(this).addClass('active'); | |
} | |
fetchResults('replace', filterTarget); | |
}); | |
// Initialize the "Age range" slider if it exists | |
if ($("#slider-range").length > 0) { | |
// initial values parsed from hidden inputs in the DOM | |
var initMin = parseInt($('#min_age').val()); | |
var initMax = parseInt($('#max_age').val()); | |
var min = 18; | |
var max = 99; | |
$("#slider-range").slider({ | |
range:true, | |
min:min, | |
max:max, | |
values:[ initMin, initMax ], | |
slide:function (event, ui) { | |
// Update the age range in the UI | |
$(".cat.age_range").html("Age range (" + ui.values[ 0 ] + " - " + ui.values[ 1 ] + ")"); | |
}, | |
change:function (event, ui) { | |
setTimeout(function () { | |
fetchResults('replace', filterTarget); | |
}, 0); | |
} | |
}); | |
$(".cat.age_range").html("Age range (" + $("#slider-range").slider("values", 0) + | |
" - " + $("#slider-range").slider("values", 1) + ")"); | |
$('<div/>', { id:'slider-track' }).insertBefore('.ui-slider-range.ui-widget-header'); | |
$('<div/>', { class:'slider_label min unselectable' }).insertAfter($("#slider-range").children().last()); | |
$('<div/>', { class:'slider_label mid unselectable' }).insertAfter($("#slider-range").children().last()); | |
$('<div/>', { class:'slider_label max unselectable' }).insertAfter($("#slider-range").children().last()); | |
$('.slider_label.min').text(min); | |
$('.slider_label.mid').text(Math.round(( (max - min) / 2) + min)); | |
$('.slider_label.max').text(max); | |
} | |
// If there are filter checkboxes | |
if ($("input.cat_opt").length > 0) { | |
// ensure that they are unchecked on initial page load | |
$("input.cat_opt").prop("checked", false); | |
} | |
initStars( $('#member_userid').val() ); | |
/* GLOBAL TEMPLATES */ | |
loadGlobalMustacheTemplates(); | |
}) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment