app/assets/javascripts/listing_form.js
window.ST = window.ST || {};
(function(module) {
/* global disable_submit_button */
/* global set_textarea_maxlength */
/* global auto_resize_text_areas */
/* jshint eqeqeq: false */ // Some parts of the code in this file actually compares number that is string to a number
// Update the state of the new listing form based on current status
function update_listing_form_view(locale, attribute_array, listing_form_menu_titles, ordered_attributes, selected_attributes) {
// Hide everything
$('a.selected').addClass('hidden');
$('a.option').addClass('hidden');
// Display correct selected attributes
$('.selected-group').each(function() {
if (selected_attributes[$(this).attr('name')] != null) {
$(this).find('a.selected[data-id=' + selected_attributes[$(this).attr('name')] + ']').removeClass('hidden');
}
});
// Display correct attribute menus and their titles
var title = "";
var shouldLoadForm = false;
if (should_show_menu_for("category", selected_attributes, attribute_array)) {
title = listing_form_menu_titles["category"];
display_option_group("category", selected_attributes, attribute_array);
} else if (should_show_menu_for("subcategory", selected_attributes, attribute_array)) {
title = listing_form_menu_titles["subcategory"];
display_option_group("subcategory", selected_attributes, attribute_array);
} else if (should_show_menu_for("listing_shape", selected_attributes, attribute_array)) {
title = listing_form_menu_titles["listing_shape"];
display_option_group("listing_shape", selected_attributes, attribute_array);
} else {
shouldLoadForm = true;
}
$('h2.listing-form-title').html(title);
return shouldLoadForm;
}
// Return subcategories for given category.
// Returns empty array if there are no subcategories.
function get_subcategories_for(category_id, category_array) {
return _.chain(category_array)
.filter(function(category) {
return category["id"] == category_id;
})
.filter(function(category) {
return category["subcategories"] !== undefined;
})
.map(function(category) {
return category["subcategories"];
})
.flatten()
.value();
}
// Check if category has a certain subcategory
function has_subcategory(category_id, subcategory_id, attribute_array) {
var subcategories = get_subcategories_for(category_id, attribute_array);
return _.any(subcategories, function(subcategory) {
return subcategory['id'] == subcategory_id;
});
}
// Returns true if given attribute has been selected
function attribute_selected(attribute, selected_attributes) {
return (selected_attributes[attribute] != null);
}
// Returns the object that has the given id
// from an array of objects
function find_by_id(id, array) {
return _.find(array, function(item) {
return item.id === id;
});
}
// Return listing shapes of given category (expects
// that this category does not have subcategories)
function get_listing_shapes_for_category(category_id, category_array) {
var category = find_by_id(Number(category_id), category_array);
return category["listing_shapes"];
}
// Returns listing shape of given subcategory
function get_listing_shapes_for_subcategory(category_id, subcategory_id, category_array) {
var category = find_by_id(Number(category_id), category_array);
var subcategory = find_by_id(Number(subcategory_id), category["subcategories"]);
return subcategory["listing_shapes"];
}
// Return true if given menu should be displayed
function should_show_menu_for(attribute, selected_attributes, attribute_array) {
if (attribute_selected(attribute, selected_attributes)) {
return false;
} else if (attribute == "category") {
if (attribute_array.length < 2) {
// If there is exactly 1 category, it should be marked automatically as selected,
// without showing the form.
if (attribute_array.length == 1) {
selected_attributes["category"] = attribute_array[0]["id"];
}
return false;
} else {
return true;
}
} else if (attribute == "subcategory") {
if (should_show_menu_for("category", selected_attributes, attribute_array)) {
return false;
} else {
var subcategories = get_subcategories_for(selected_attributes["category"], attribute_array);
if (subcategories.length < 2) {
// If there is exactly 1 subcategory, it should be marked automatically as selected,
// without showing the form.
if (subcategories.length == 1) {
selected_attributes["subcategory"] = subcategories[0]["id"];
}
return false;
} else {
return true;
}
}
} else if (attribute == "listing_shape") {
if (should_show_menu_for("category", selected_attributes, attribute_array)) {
return false;
} else if (should_show_menu_for("subcategory", selected_attributes, attribute_array)) {
return false;
} else {
var listing_shapes;
if (attribute_selected("subcategory", selected_attributes)) {
listing_shapes = get_listing_shapes_for_subcategory(selected_attributes["category"], selected_attributes["subcategory"], attribute_array);
} else {
listing_shapes = get_listing_shapes_for_category(selected_attributes["category"], attribute_array);
}
// If there is exactly 1 listing shape, it should be marked automatically as selected,
// without showing the form
if (listing_shapes.length === 1) {
selected_attributes["listing_shape"] = listing_shapes[0]["id"];
}
return (listing_shapes.length > 1);
}
}
}
// Ajax call to display listing form after categories and
// listing shape has been selected
function display_new_listing_form(selected_attributes, options) {
$.get(options.new_form_content_path, selected_attributes, function(data) {
$('.js-form-fields').html(data);
$('.js-form-fields').removeClass('hidden');
});
}
function display_edit_listing_form(selected_attributes, locale, id) {
var edit_listing_path = '/' + locale + '/listings/edit_form_content';
var request_params = _.assign({}, selected_attributes, {id: id});
$.get(edit_listing_path, request_params, function(data) {
$('.js-form-fields').html(data);
$('.js-form-fields').removeClass('hidden');
});
}
// Check if selected category or subcategory has certain listing shape
function has_listing_shape(selected_attributes, listing_shape_id, attribute_array) {
// If subcategory is selected, loop through listing shapes of that subcategory
var listing_shapes;
if (attribute_selected("subcategory", selected_attributes)) {
listing_shapes = get_listing_shapes_for_subcategory(selected_attributes["category"], selected_attributes["subcategory"],attribute_array);
// If there's no subcategory, it means this top level category has no subcategories.
// Thus, loop through listing_shapes of top level category.
} else {
listing_shapes = get_listing_shapes_for_category(selected_attributes["category"] ,attribute_array);
}
return _.any(listing_shapes, function(listing_shape) {
return listing_shape['id'] == listing_shape_id;
});
}
// Displays the given menu where category or listing shape can be selected
function display_option_group(group_type, selected_attributes, attribute_array) {
$('.option-group[name=' + group_type + ']').children().each(function() {
if (group_type == "category") {
$(this).removeClass('hidden');
} else if (group_type == "subcategory") {
if (has_subcategory(selected_attributes["category"], $(this).attr('data-id'), attribute_array)) {
$(this).removeClass('hidden');
}
} else if (group_type == "listing_shape") {
if (has_listing_shape(selected_attributes, $(this).attr('data-id'), attribute_array)) {
$(this).removeClass('hidden');
}
}
});
}
// Called when a link is clicked in the listing form attribute menus
function select_listing_form_menu_link(link, locale, attribute_array, listing_form_menu_titles, ordered_attributes, selected_attributes) {
// Update selected attributes based on the selection that has been made
if (link.hasClass('option')) {
selected_attributes[link.parent().attr('name')] = link.attr('data-id');
} else {
selected_attributes[link.parent().attr('name')] = null;
// Unselect also all sub-attributes if certain attribute is unselected
// (for instance, unselect subcategory if category is unselected).
var index_found = false;
for (var i = 0; i < ordered_attributes.length; i++) {
if (ordered_attributes[i] == link.parent().attr('name')) {
index_found = true;
}
if (index_found === true) {
selected_attributes[ordered_attributes[i]] = null;
}
}
}
// Update form view based on the selection that has been made
var shouldLoadForm = update_listing_form_view(locale, attribute_array, listing_form_menu_titles, ordered_attributes, selected_attributes);
return shouldLoadForm;
}
var setPushState = function(selectedAttributes) {
if(window.history == null || window.history.pushState == null ) {
return;
}
var url = window.location.origin + window.location.pathname;
window.history.pushState(selectedAttributes, null, addQueryParams(url, selectedAttributes));
};
var addQueryParams = function(url, selectedAttributes) {
var attrs = hashCompact(selectedAttributes);
if(_.isEmpty(attrs)) {
return url;
} else {
var q = _.map(attrs, function(val, key) {
return key + "=" + val;
}).join("&");
return [url, q].join("?");
}
};
var hashCompact = function(h) {
return _.reduce(h, function(acc, val, key) {
if(val != null) {
acc[key] = val;
}
return acc;
}, {});
};
var emptySelection = {"category": null, "subcategory": null, "listing_shape": null};
var selectedAttributesFromQueryParams = function(search) {
if(!search) {
return {};
}
var without_q = search.replace(/^\?/, ''); // Remove the first char if it's question mark
var attrsFromQuery = _.zipObject(without_q.split("&").map(function(keyValuePair) { return keyValuePair.split("="); }));
return _.assign({}, emptySelection, attrsFromQuery);
};
// Initialize the listing type & category selection part of the form
module.initialize_new_listing_form_selectors = function(options) {
var ordered_attributes = ["category", "subcategory", "listing_shape"];
var selected_attributes = selectedAttributesFromQueryParams(window.location.search);
// Reset the view to initial state
var shouldLoadForm = update_listing_form_view(options.locale, options.category_tree, options.menu_titles, ordered_attributes, selected_attributes);
if(shouldLoadForm) {
display_new_listing_form(selected_attributes, options);
}
var menuStateChanged = function(shouldLoadForm) {
if(shouldLoadForm) {
display_new_listing_form(selected_attributes, options);
}
};
// Listen for back button click
window.addEventListener('popstate', function(evt) {
selected_attributes = evt.state || emptySelection;
$('.js-form-fields').addClass('hidden');
var shouldLoadForm = select_listing_form_menu_link($(this), options.locale, options.category_tree, options.menu_titles, ordered_attributes, selected_attributes);
menuStateChanged(shouldLoadForm);
});
// Listener for attribute menu clicks
$('.new-listing-form').find('a.select').click(
function() {
$('.js-form-fields').addClass('hidden');
var shouldLoadForm = select_listing_form_menu_link($(this), options.locale, options.category_tree, options.menu_titles, ordered_attributes, selected_attributes);
setPushState(selected_attributes);
menuStateChanged(shouldLoadForm);
}
);
};
module.initialize_edit_listing_form_selectors = function(locale, attribute_array, listing_form_menu_titles, category, subcategory, listing_shape, id) {
var ordered_attributes = ["category", "subcategory", "listing_shape"];
// Selected values (string or null required)
category = category ? "" + category : null;
subcategory = subcategory ? "" + subcategory : null;
listing_shape = listing_shape ? "" + listing_shape : null;
var selected_attributes = {"category": category, "subcategory": subcategory, "listing_shape": listing_shape};
var originalSelection = _.clone(selected_attributes);
var current_attributes = _.clone(selected_attributes);
// Reset the view to initial state
var shouldShowForm = update_listing_form_view(locale, attribute_array, listing_form_menu_titles, ordered_attributes, selected_attributes);
if(shouldShowForm) {
$('.js-form-fields').removeClass('hidden');
}
var menuStateChanged = function(shouldLoadForm) {
if(shouldLoadForm) {
var loadNotNeeded = _.isEqual(selected_attributes, current_attributes);
current_attributes = _.clone(selected_attributes);
if(loadNotNeeded) {
$('.js-form-fields').removeClass('hidden');
} else {
$('.js-form-fields').html("");
display_edit_listing_form(selected_attributes, locale, id);
}
}
};
// Listen for back button click
window.addEventListener('popstate', function(evt) {
selected_attributes = evt.state || originalSelection;
$('.js-form-fields').addClass('hidden');
var shouldLoadForm = select_listing_form_menu_link($(this), locale, attribute_array, listing_form_menu_titles, ordered_attributes, selected_attributes);
menuStateChanged(shouldLoadForm);
});
// Listener for attribute menu clicks
$('.new-listing-form').find('a.select').click(
function() {
$('.js-form-fields').addClass('hidden');
var shouldLoadForm = select_listing_form_menu_link($(this), locale, attribute_array, listing_form_menu_titles, ordered_attributes, selected_attributes);
setPushState(selected_attributes);
menuStateChanged(shouldLoadForm);
}
);
};
// Initialize the actual form fields
module.initialize_new_listing_form = function(
fileDefaultText,
fileBtnText,
locale,
share_type_message,
date_message,
listing_id,
price_required,
price_message,
minimum_price,
subunit_to_unit,
minimum_price_message,
numeric_field_names,
listingImages,
listingImageOpts,
imageLoadingInProgressConfirm) {
$('#help_valid_until_link').click(function() { $('#help_valid_until').lightbox_me({centered: true, zIndex: 1000000}); });
$('input.title_text_field:first').focus();
var $shipping_price_container = $('.js-shipping-price-container');
var $shipping_checkbox = $('#shipping-checkbox');
$shipping_checkbox.click(function() { togglePrice(); });
var togglePrice = function(){
if($shipping_checkbox.is(":checked")) {
$shipping_price_container.show();
} else {
$shipping_price_container.hide();
}
};
togglePrice(); //initialize
var $unit = $(".js-listing-unit");
if ($unit.length) {
var $additionalShipping = $(".js-shipping-price-additional");
var toggleAdditional = function() {
var kind = $unit.find(":selected").data("kind");
if (kind === "quantity") {
$additionalShipping.css({display: "table"});
} else {
$additionalShipping.hide();
}
};
$unit.change(toggleAdditional);
toggleAdditional(); // init
}
var form_id = (listing_id == "false") ? "#new_listing" : ("#edit_listing_" + listing_id);
// Is price required?
var pr = null;
if (price_required == "true") {
pr = true;
} else {
pr = false;
}
var numericRules = numeric_field_names.reduce(function(rules, name) {
var el = module.utils.findElementByName(name);
var min = el.data("min");
var max = el.data("max");
rules[name] = {number_min: min, number_max: max};
return rules;
}, {});
module.listingForm = $(form_id).validate({
errorPlacement: function(error, element) {
if (element.attr("name") == "listing[valid_until(1i)]") {
error.appendTo(element.parent());
} else if (element.attr("name") == "listing[price]") {
error.appendTo(element.parent());
} else if ($(element).hasClass("custom_field_checkbox")) {
var container = $(element).closest(".checkbox-group-container");
error.insertAfter(container);
} else if ($(element).hasClass("delivery-method-checkbox")) {
error.insertAfter($(".delivery-options-container"));
} else if (element.attr("name") == "listing[shipping_price]") {
error.insertAfter($(".shipping-price-default"));
} else if (element.attr("name") == "listing[shipping_price_additional]") {
error.insertAfter($(".js-shipping-price-additional"));
} else {
error.insertAfter(element);
}
},
debug: false,
rules: _.extend(numericRules, {
"listing[title]": {required: true, minlength: 2, maxlength: 65},
"listing[origin]": {address_validator: true},
"listing[price]": {required: pr, money: true, minimum_price_required: [minimum_price, subunit_to_unit]},
"listing[shipping_price]": {money: true},
"listing[shipping_price_additional]": {money: true},
"listing[valid_until(1i)]": { min_date: true, max_date: true }
}),
messages: {
"listing[valid_until(1i)]": { min_date: date_message, max_date: date_message },
"listing[price]": { minimum_price_required: minimum_price_message }
},
// Run validations only when submitting the form.
onkeyup: false,
onclick: false,
onfocusout: false,
onsubmit: true
});
var status = window.ST.imageUploader(listingImages, listingImageOpts).log("status returned");
status.onValue(function(stats) {
$('.flash-notifications').click(function() {
$('.flash-notifications').fadeOut('slow');
});
if(stats.loading === 0) {
$(".js-listing-image-loading").hide();
if(stats.processing === 0) {
$(".js-listing-image-loading-done").hide();
} else {
$(".js-listing-image-loading-done").show();
}
} else {
$(".js-listing-image-loading-done").hide();
$(".js-listing-image-loading").show();
}
});
var formSubmitted = $(form_id).asEventStream("submit");
var validFormSubmitted = formSubmitted.filter(function() {
return $(form_id).valid();
});
var isLoading = status.map(function(stats) { return stats.loading > 0; });
// This handler is used only when Image uploader is loading
validFormSubmitted.filter(isLoading).onValue(function(e) {
var confirmed = window.confirm(imageLoadingInProgressConfirm);
if(!confirmed) {
e.preventDefault();
// This will prevent the jQuery validation submitHandler from
// executing. Please note that the order matters. This works
// before it's called BEFORE the submitHandler
e.stopImmediatePropagation();
}
});
// This handler is used when Image uploader is not loading
validFormSubmitted.filter(isLoading.not()).onValue(function(e) {
window.ST.analytics.logEvent("listing", "created");
disable_submit_button(form_id, locale);
});
set_textarea_maxlength();
auto_resize_text_areas("listing_description_textarea");
$(form_id).addClass("js-listing-form-ready");
};
})(window.ST);