550 lines
15 KiB
JavaScript
550 lines
15 KiB
JavaScript
(function () {
|
|
var hangersInitCallbacks = [];
|
|
|
|
function onHangersInit(callback) {
|
|
hangersInitCallbacks[hangersInitCallbacks.length] = callback;
|
|
}
|
|
|
|
function hangersInit() {
|
|
for(var i = 0; i < hangersInitCallbacks.length; i++) {
|
|
hangersInitCallbacks[i]();
|
|
}
|
|
}
|
|
|
|
/*
|
|
|
|
Hanger groups
|
|
|
|
*/
|
|
|
|
var hangerGroups = [];
|
|
|
|
$('div.closet-hangers-group').each(function () {
|
|
var el = $(this);
|
|
var lists = [];
|
|
|
|
el.find('div.closet-list').each(function () {
|
|
var el = $(this);
|
|
var id = el.attr('data-id');
|
|
if(id) {
|
|
lists[lists.length] = {
|
|
id: parseInt(id, 10),
|
|
label: el.find('h4').text()
|
|
}
|
|
}
|
|
});
|
|
|
|
hangerGroups[hangerGroups.length] = {
|
|
label: el.find('h3').text(),
|
|
lists: lists,
|
|
owned: (el.attr('data-owned') == 'true')
|
|
};
|
|
});
|
|
|
|
$('div.closet-hangers-group span.toggle').live('click', function () {
|
|
$(this).closest('.closet-hangers-group').toggleClass('hidden');
|
|
});
|
|
|
|
var hangersElQuery = '#closet-hangers';
|
|
var hangersEl = $(hangersElQuery);
|
|
|
|
/*
|
|
|
|
Compare with Your Items
|
|
|
|
*/
|
|
|
|
$('#toggle-compare').click(function () {
|
|
hangersEl.toggleClass('comparing');
|
|
});
|
|
|
|
/*
|
|
|
|
Hanger forms
|
|
|
|
*/
|
|
|
|
$.fn.liveDraggable = function (opts) {
|
|
this.live("mouseover", function() {
|
|
if (!$(this).data("init")) {
|
|
$(this).data("init", true).draggable(opts);
|
|
}
|
|
});
|
|
};
|
|
|
|
var body = $(document.body).addClass("js");
|
|
if(!body.hasClass("current-user")) return false;
|
|
|
|
$.fn.disableForms = function () {
|
|
return this.data("formsDisabled", true).find("input").attr("disabled", "disabled").end();
|
|
}
|
|
|
|
$.fn.enableForms = function () {
|
|
return this.data("formsDisabled", false).find("input").removeAttr("disabled").end();
|
|
}
|
|
|
|
$.fn.hasChanged = function () {
|
|
return this.attr('data-previous-value') != this.val();
|
|
}
|
|
|
|
$.fn.revertValue = function () {
|
|
return this.each(function () {
|
|
var el = $(this);
|
|
el.val(el.attr('data-previous-value'));
|
|
});
|
|
}
|
|
|
|
$.fn.storeValue = function () {
|
|
return this.each(function () {
|
|
var el = $(this);
|
|
el.attr('data-previous-value', el.val());
|
|
});
|
|
}
|
|
|
|
$.fn.insertIntoSortedList = function (list, compare) {
|
|
var newChild = this, inserted = false;
|
|
list.children().each(function () {
|
|
if(compare(newChild, $(this)) < 1) {
|
|
newChild.insertBefore(this);
|
|
inserted = true;
|
|
return false;
|
|
}
|
|
});
|
|
if(!inserted) newChild.appendTo(list);
|
|
return this;
|
|
}
|
|
|
|
function handleSaveError(xhr, action) {
|
|
try {
|
|
var data = $.parseJSON(xhr.responseText);
|
|
} catch(e) {
|
|
var data = {};
|
|
}
|
|
|
|
if(typeof data.errors != 'undefined') {
|
|
$.jGrowl("Error " + action + ": " + data.errors.join(", "));
|
|
} else {
|
|
$.jGrowl("We had trouble " + action + " just now. Try again?");
|
|
}
|
|
}
|
|
|
|
function objectRemoved(objectWrapper) {
|
|
objectWrapper.hide(250, $.proxy(objectWrapper, 'remove'));
|
|
}
|
|
|
|
function compareItemsByName(a, b) {
|
|
return a.find('span.name').text().localeCompare(b.find('span.name').text());
|
|
}
|
|
|
|
function findList(owned, id, item) {
|
|
if(id) {
|
|
return $('#closet-list-' + id);
|
|
} else {
|
|
return $("div.closet-hangers-group[data-owned=" + owned + "] div.closet-list.unlisted");
|
|
}
|
|
}
|
|
|
|
function updateListHangersCount(el) {
|
|
el.attr('data-hangers-count', el.find('div.object').length);
|
|
}
|
|
|
|
function moveItemToList(item, owned, listId) {
|
|
var newList = findList(owned, listId, item);
|
|
var oldList = item.closest('div.closet-list');
|
|
var hangersWrapper = newList.find('div.closet-list-hangers');
|
|
item.insertIntoSortedList(hangersWrapper, compareItemsByName);
|
|
updateListHangersCount(oldList);
|
|
updateListHangersCount(newList);
|
|
}
|
|
|
|
function submitUpdateForm(form) {
|
|
if(form.data('loading')) return false;
|
|
var quantityEl = form.children("input[name=closet_hanger\[quantity\]]");
|
|
var ownedEl = form.children("input[name=closet_hanger\[owned\]]");
|
|
var listEl = form.children("input[name=closet_hanger\[list_id\]]");
|
|
var listChanged = ownedEl.hasChanged() || listEl.hasChanged();
|
|
if(listChanged || quantityEl.hasChanged()) {
|
|
var objectWrapper = form.closest(".object").addClass("loading");
|
|
var newQuantity = quantityEl.val();
|
|
var quantitySpan = objectWrapper.find(".quantity span").text(newQuantity);
|
|
objectWrapper.attr('data-quantity', newQuantity);
|
|
var data = form.serialize(); // get data before disabling inputs
|
|
objectWrapper.disableForms();
|
|
form.data('loading', true);
|
|
if(listChanged) moveItemToList(objectWrapper, ownedEl.val(), listEl.val());
|
|
$.ajax({
|
|
url: form.attr("action") + ".json",
|
|
type: "post",
|
|
data: data,
|
|
dataType: "json",
|
|
complete: function (data) {
|
|
if(quantityEl.val() == 0) {
|
|
objectRemoved(objectWrapper);
|
|
} else {
|
|
objectWrapper.removeClass("loading").enableForms();
|
|
}
|
|
form.data('loading', false);
|
|
},
|
|
success: function () {
|
|
// Now that the move was successful, let's merge it with any
|
|
// conflicting hangers
|
|
var id = objectWrapper.attr("data-item-id");
|
|
var conflictingHanger = findList(ownedEl.val(), listEl.val(), objectWrapper).
|
|
find("div[data-item-id=" + id + "]").not(objectWrapper);
|
|
if(conflictingHanger.length) {
|
|
var conflictingQuantity = parseInt(
|
|
conflictingHanger.attr('data-quantity'),
|
|
10
|
|
);
|
|
|
|
var currentQuantity = parseInt(newQuantity, 10);
|
|
|
|
var mergedQuantity = conflictingQuantity + currentQuantity;
|
|
|
|
quantitySpan.text(mergedQuantity);
|
|
quantityEl.val(mergedQuantity);
|
|
objectWrapper.attr('data-quantity', mergedQuantity);
|
|
|
|
conflictingHanger.remove();
|
|
}
|
|
|
|
quantityEl.storeValue();
|
|
ownedEl.storeValue();
|
|
listEl.storeValue();
|
|
},
|
|
error: function (xhr) {
|
|
quantityEl.revertValue();
|
|
ownedEl.revertValue();
|
|
listEl.revertValue();
|
|
if(listChanged) moveItemToList(objectWrapper, ownedEl.val(), listEl.val());
|
|
quantitySpan.text(quantityEl.val());
|
|
|
|
handleSaveError(xhr, "updating the quantity");
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
$(hangersElQuery + ' form.closet-hanger-update').live('submit', function (e) {
|
|
e.preventDefault();
|
|
submitUpdateForm($(this));
|
|
});
|
|
|
|
function editableInputs() {
|
|
return $(hangersElQuery).find(
|
|
'input[name=closet_hanger\[quantity\]], ' +
|
|
'input[name=closet_hanger\[owned\]], ' +
|
|
'input[name=closet_hanger\[list_id\]]'
|
|
)
|
|
}
|
|
|
|
$(hangersElQuery + 'input[name=closet_hanger\[quantity\]]').live('change', function () {
|
|
submitUpdateForm($(this).parent());
|
|
}).storeValue();
|
|
|
|
onHangersInit(function () {
|
|
editableInputs().storeValue();
|
|
});
|
|
|
|
$(hangersElQuery + ' div.object').live('mouseleave', function () {
|
|
submitUpdateForm($(this).find('form.closet-hanger-update'));
|
|
}).liveDraggable({
|
|
appendTo: '#closet-hangers',
|
|
distance: 20,
|
|
helper: "clone",
|
|
revert: "invalid"
|
|
});
|
|
|
|
$(hangersElQuery + " form.closet-hanger-destroy").live("submit", function (e) {
|
|
e.preventDefault();
|
|
var form = $(this);
|
|
var button = form.children("input[type=submit]").val("Removing…");
|
|
var objectWrapper = form.closest(".object").addClass("loading");
|
|
var data = form.serialize(); // get data before disabling inputs
|
|
objectWrapper.addClass("loading").disableForms();
|
|
$.ajax({
|
|
url: form.attr("action") + ".json",
|
|
type: "post",
|
|
data: data,
|
|
dataType: "json",
|
|
complete: function () {
|
|
button.val("Remove");
|
|
},
|
|
success: function () {
|
|
objectRemoved(objectWrapper);
|
|
},
|
|
error: function () {
|
|
objectWrapper.removeClass("loading").enableForms();
|
|
$.jGrowl("Error removing item. Try again?");
|
|
}
|
|
});
|
|
});
|
|
|
|
/*
|
|
|
|
Search, autocomplete
|
|
|
|
*/
|
|
|
|
$('input, textarea').placeholder();
|
|
|
|
var itemsSearchForm = $("#closet-hangers-items-search[data-current-user-id]");
|
|
var itemsSearchField = itemsSearchForm.children("input[name=q]");
|
|
|
|
itemsSearchField.autocomplete({
|
|
select: function (e, ui) {
|
|
if(ui.item.is_item) {
|
|
// Let the autocompleter finish up this search before starting a new one
|
|
setTimeout(function () {
|
|
itemsSearchField.autocomplete("search", ui.item);
|
|
}, 0);
|
|
} else {
|
|
var item = ui.item.item;
|
|
var group = ui.item.group;
|
|
|
|
itemsSearchField.addClass("loading");
|
|
|
|
var closetHanger = {
|
|
owned: group.owned,
|
|
list_id: ui.item.list ? ui.item.list.id : ''
|
|
};
|
|
|
|
if(!item.hasHanger) closetHanger.quantity = 1;
|
|
|
|
$.ajax({
|
|
url: "/user/" + itemsSearchForm.data("current-user-id") + "/items/" + item.id + "/closet_hangers",
|
|
type: "post",
|
|
data: {closet_hanger: closetHanger, return_to: window.location.pathname + window.location.search},
|
|
complete: function () {
|
|
itemsSearchField.removeClass("loading");
|
|
},
|
|
success: function (html) {
|
|
var doc = $(html);
|
|
hangersEl.html( doc.find('#closet-hangers').html() );
|
|
hangersInit();
|
|
doc.find('.flash').hide().insertBefore(hangersEl).show(500).delay(5000).hide(250);
|
|
itemsSearchField.val("");
|
|
},
|
|
error: function (xhr) {
|
|
handleSaveError(xhr, "adding the item");
|
|
}
|
|
});
|
|
}
|
|
},
|
|
source: function (input, callback) {
|
|
if(typeof input.term == 'string') { // user-typed query
|
|
$.getJSON("/items.json?q=" + input.term, function (data) {
|
|
var output = [];
|
|
var items = data.items;
|
|
for(var i in items) {
|
|
items[i].label = items[i].name;
|
|
items[i].is_item = true;
|
|
output[output.length] = items[i];
|
|
}
|
|
callback(output);
|
|
});
|
|
} else { // item was chosen, now choose a group to insert
|
|
var groupInserts = [], group;
|
|
var item = input.term, itemEl, occupiedGroups, hasHanger;
|
|
for(var i in hangerGroups) {
|
|
group = hangerGroups[i];
|
|
itemEl = $('div.closet-hangers-group[data-owned=' + group.owned + '] div.object[data-item-id=' + item.id + ']');
|
|
occupiedGroups = itemEl.closest('.closet-list');
|
|
hasHanger = occupiedGroups.filter('.unlisted').length > 0;
|
|
|
|
groupInserts[groupInserts.length] = {
|
|
group: group,
|
|
item: item,
|
|
label: item.label,
|
|
hasHanger: hasHanger
|
|
}
|
|
|
|
for(var i = 0; i < group.lists.length; i++) {
|
|
hasHanger = occupiedGroups.
|
|
filter("[data-id=" + group.lists[i].id + "]").length > 0;
|
|
groupInserts[groupInserts.length] = {
|
|
group: group,
|
|
item: item,
|
|
label: item.label,
|
|
list: group.lists[i],
|
|
hasHanger: hasHanger
|
|
}
|
|
}
|
|
}
|
|
callback(groupInserts);
|
|
}
|
|
}
|
|
});
|
|
|
|
var autocompleter = itemsSearchField.data("autocomplete");
|
|
|
|
autocompleter._renderItem = function( ul, item ) {
|
|
var li = $("<li></li>").data("item.autocomplete", item);
|
|
if(item.is_item) { // these are items from the server
|
|
$('#autocomplete-item-tmpl').tmpl({item_name: item.label}).appendTo(li);
|
|
} else if(item.list) { // these are list inserts
|
|
var listName = item.list.label;
|
|
if(item.hasHanger) {
|
|
$('#autocomplete-already-in-collection-tmpl').
|
|
tmpl({collection_name: listName}).appendTo(li);
|
|
} else {
|
|
$('#autocomplete-add-to-list-tmpl').tmpl({list_name: listName}).
|
|
appendTo(li);
|
|
}
|
|
li.addClass("closet-list-autocomplete-item");
|
|
} else { // these are group inserts
|
|
var groupName = item.group.label;
|
|
if(!item.hasHanger) {
|
|
$('#autocomplete-add-to-group-tmpl').
|
|
tmpl({group_name: groupName.replace(/\s+$/, '')}).appendTo(li);
|
|
} else {
|
|
$('#autocomplete-already-in-collection-tmpl').
|
|
tmpl({collection_name: groupName}).appendTo(li);
|
|
}
|
|
li.addClass('closet-hangers-group-autocomplete-item');
|
|
}
|
|
return li.appendTo(ul);
|
|
}
|
|
|
|
/*
|
|
|
|
Contact Neopets username form
|
|
|
|
*/
|
|
|
|
var contactEl = $('#closet-hangers-contact');
|
|
var editContactLink = $('.edit-contact-link');
|
|
var contactForm = contactEl.children('form');
|
|
var cancelContactLink = $('#cancel-contact-link');
|
|
var contactFormUsername = contactForm.children('input[type=text]');
|
|
var editContactLinkUsername = $('#contact-link-has-value span');
|
|
|
|
function closeContactForm() {
|
|
contactEl.removeClass('editing');
|
|
}
|
|
|
|
editContactLink.click(function () {
|
|
contactEl.addClass('editing');
|
|
contactFormUsername.focus();
|
|
});
|
|
|
|
cancelContactLink.click(closeContactForm);
|
|
|
|
contactForm.submit(function (e) {
|
|
var data = contactForm.serialize();
|
|
contactForm.disableForms();
|
|
$.ajax({
|
|
url: contactForm.attr('action') + '.json',
|
|
type: 'post',
|
|
data: data,
|
|
dataType: 'json',
|
|
complete: function () {
|
|
contactForm.enableForms();
|
|
},
|
|
success: function () {
|
|
var newName = contactFormUsername.val();
|
|
if(newName.length > 0) {
|
|
editContactLink.addClass('has-value');
|
|
editContactLinkUsername.text(newName);
|
|
} else {
|
|
editContactLink.removeClass('has-value');
|
|
}
|
|
closeContactForm();
|
|
},
|
|
error: function (xhr) {
|
|
handleSaveError(xhr, 'saving Neopets username');
|
|
}
|
|
});
|
|
e.preventDefault();
|
|
});
|
|
|
|
/*
|
|
|
|
Hanger list controls
|
|
|
|
*/
|
|
|
|
$('input[type=submit][data-confirm]').live('click', function (e) {
|
|
if(!confirm(this.getAttribute('data-confirm'))) e.preventDefault();
|
|
});
|
|
|
|
/*
|
|
|
|
Closet list droppable
|
|
|
|
*/
|
|
|
|
onHangersInit(function () {
|
|
$('div.closet-list').droppable({
|
|
accept: 'div.object',
|
|
activate: function () {
|
|
$(this).find('.closet-list-content').animate({opacity: 0, height: 100}, 250);
|
|
},
|
|
activeClass: 'droppable-active',
|
|
deactivate: function () {
|
|
$(this).find('.closet-list-content').css('height', 'auto').animate({opacity: 1}, 250);
|
|
},
|
|
drop: function (e, ui) {
|
|
var form = ui.draggable.find('form.closet-hanger-update');
|
|
form.find('input[name=closet_hanger\[list_id\]]').
|
|
val(this.getAttribute('data-id'));
|
|
form.find('input[name=closet_hanger\[owned\]]').
|
|
val($(this).closest('.closet-hangers-group').attr('data-owned'));
|
|
submitUpdateForm(form);
|
|
}
|
|
});
|
|
});
|
|
|
|
/*
|
|
|
|
Visibility Descriptions
|
|
|
|
*/
|
|
|
|
function updateVisibilityDescription() {
|
|
var descriptions = $(this).closest('.visibility-form').
|
|
find('ul.visibility-descriptions');
|
|
|
|
descriptions.children('li.current').removeClass('current');
|
|
descriptions.children('li[data-id=' + $(this).val() + ']').addClass('current');
|
|
}
|
|
|
|
function visibilitySelects() { return $('form.visibility-form select') }
|
|
|
|
visibilitySelects().live('change', updateVisibilityDescription);
|
|
|
|
onHangersInit(function () {
|
|
visibilitySelects().each(updateVisibilityDescription);
|
|
});
|
|
|
|
/*
|
|
|
|
Help
|
|
|
|
*/
|
|
|
|
$('#toggle-help').click(function () {
|
|
$('#closet-hangers-help').toggleClass('hidden');
|
|
});
|
|
|
|
/*
|
|
|
|
Share URL
|
|
|
|
*/
|
|
|
|
$('#closet-hangers-share-box').mouseover(function () {
|
|
$(this).focus();
|
|
}).mouseout(function () {
|
|
$(this).blur();
|
|
});
|
|
|
|
/*
|
|
|
|
Initialize
|
|
|
|
*/
|
|
|
|
hangersInit();
|
|
})();
|
|
|