Emi Matchu
583f3c712f
Okay, so I still don't know why rendering is just so slow (though migrating away from item translations did help!), but I can at least cache entire closet lists as a basic measure. That way, the first user to see the latest version of a closet list will still need just as much time to load it… but *only* the ones that have changed since last time (rather than always the full page), and then subsequent users get to reuse it too! Should help a lot for high-traffic lists, which incidentally are likely to be the big ones belonging to highly active traders! One big change we needed to make was to extract the `user-owns` and `user-wants` classes (which we use for trade matches for *the user viewing the list right now*) out of the cached HTML, and apply them after with Javascript instead. I always dislike moving stuff to JS, but the wins here seem. truly very very good, all things considered!
852 lines
23 KiB
JavaScript
852 lines
23 KiB
JavaScript
(function () {
|
|
var hangersInitCallbacks = [];
|
|
|
|
function onHangersInit(callback) {
|
|
hangersInitCallbacks[hangersInitCallbacks.length] = callback;
|
|
}
|
|
|
|
function hangersInit() {
|
|
for (var i = 0; i < hangersInitCallbacks.length; i++) {
|
|
try {
|
|
hangersInitCallbacks[i]();
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
|
|
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");
|
|
});
|
|
|
|
// Read the item IDs of trade matches from the meta tags.
|
|
const ownedIds =
|
|
document
|
|
.querySelector("meta[name=trade-matches-owns]")
|
|
?.getAttribute("value")
|
|
?.split(",") ?? [];
|
|
const wantedIds =
|
|
document
|
|
.querySelector("meta[name=trade-matches-wants]")
|
|
?.getAttribute("value")
|
|
?.split(",") ?? [];
|
|
|
|
// Apply the `user-owns` and `user-wants` classes to the relevant entries.
|
|
// This both provides immediate visual feedback, and sets up "Compare with
|
|
// Your Items" to toggle to just them!
|
|
//
|
|
// NOTE: The motivation here is caching: this allows us to share a cache of
|
|
// the closet list contents across all users, without `user-owns` or
|
|
// `user-wants` classes for one specific user getting cached and reused.
|
|
const hangerEls = document.querySelectorAll("#closet-hangers .object");
|
|
for (const hangerEl of hangerEls) {
|
|
const itemId = hangerEl.getAttribute("data-item-id");
|
|
if (ownedIds.includes(itemId)) {
|
|
hangerEl.classList.add("user-owns");
|
|
}
|
|
if (wantedIds.includes(itemId)) {
|
|
hangerEl.classList.add("user-wants");
|
|
}
|
|
}
|
|
|
|
/*
|
|
|
|
Hanger forms
|
|
|
|
*/
|
|
|
|
var body = $(document.body).addClass("js");
|
|
if (!body.hasClass("current-user")) return false;
|
|
|
|
// When we get hangers HTML, add the controls. We do this in JS rather than
|
|
// in the HTML for caching, since otherwise the requests can take forever.
|
|
// If there were another way to add hangers, then we'd have to worry about
|
|
// that, but, right now, the only way to create a new hanger from this page
|
|
// is through the autocompleter, which reinitializes anyway. Geez, this thing
|
|
// is begging for a rewrite, but today we're here for performance.
|
|
$("#closet-hanger-update-tmpl").template("updateFormTmpl");
|
|
$("#closet-hanger-destroy-tmpl").template("destroyFormTmpl");
|
|
onHangersInit(function () {
|
|
// Super-lame hack to get the user ID from where it already is :/
|
|
var currentUserId = itemsSearchForm.data("current-user-id");
|
|
$("#closet-hangers div.closet-hangers-group").each(function () {
|
|
var groupEl = $(this);
|
|
var owned = groupEl.data("owned");
|
|
|
|
groupEl.find("div.closet-list").each(function () {
|
|
var listEl = $(this);
|
|
var listId = listEl.data("id");
|
|
|
|
listEl.find("div.object").each(function () {
|
|
var hangerEl = $(this);
|
|
var hangerId = hangerEl.data("id");
|
|
var quantityEl = hangerEl.find("div.quantity");
|
|
var quantity = hangerEl.data("quantity");
|
|
|
|
// Ooh, this part is weird. We only want the name to be linked, so
|
|
// lift everything else out.
|
|
var checkboxId = "hanger-selected-" + hangerId;
|
|
var label = $("<label />", { for: checkboxId });
|
|
var link = hangerEl.children("a");
|
|
link.children(":not(.name)").detach().appendTo(label);
|
|
link.detach().appendTo(label);
|
|
var checkbox = $("<input />", {
|
|
type: "checkbox",
|
|
id: checkboxId,
|
|
}).appendTo(hangerEl);
|
|
label.appendTo(hangerEl);
|
|
|
|
// I don't usually like to _blank things, but it's too easy to click
|
|
// the text when you didn't mean to and lose your selection work.
|
|
link.attr("target", "_blank");
|
|
|
|
$.tmpl("updateFormTmpl", {
|
|
user_id: currentUserId,
|
|
closet_hanger_id: hangerId,
|
|
quantity: quantity,
|
|
list_id: listId,
|
|
owned: owned,
|
|
}).appendTo(quantityEl);
|
|
|
|
$.tmpl("destroyFormTmpl", {
|
|
user_id: currentUserId,
|
|
closet_hanger_id: hangerId,
|
|
}).appendTo(hangerEl);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
$.fn.liveDraggable = function (opts) {
|
|
this.live("mouseover", function () {
|
|
if (!$(this).data("init")) {
|
|
$(this).data("init", true).draggable(opts);
|
|
}
|
|
});
|
|
};
|
|
|
|
$.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, function () {
|
|
objectWrapper.remove();
|
|
updateBulkActions();
|
|
});
|
|
}
|
|
|
|
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();
|
|
|
|
updateBulkActions();
|
|
},
|
|
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?");
|
|
},
|
|
});
|
|
},
|
|
);
|
|
|
|
$(hangersElQuery + " .select-all").live("click", function (e) {
|
|
var checkboxes = $(this)
|
|
.closest(".closet-list")
|
|
.find(".object input[type=checkbox]");
|
|
|
|
var allChecked = true;
|
|
checkboxes.each(function () {
|
|
if (!this.checked) {
|
|
allChecked = false;
|
|
return false;
|
|
}
|
|
});
|
|
|
|
checkboxes.attr("checked", !allChecked);
|
|
|
|
updateBulkActions(); // setting the checked prop doesn't fire change events
|
|
});
|
|
|
|
function getCheckboxes() {
|
|
return $(hangersElQuery + " input[type=checkbox]");
|
|
}
|
|
|
|
function getCheckedIds() {
|
|
var checkedIds = [];
|
|
getCheckboxes()
|
|
.filter(":checked")
|
|
.each(function () {
|
|
if (this.checked) checkedIds.push(this.id);
|
|
});
|
|
return checkedIds;
|
|
}
|
|
|
|
getCheckboxes().live("change", updateBulkActions);
|
|
|
|
function updateBulkActions() {
|
|
var checkedCount = getCheckboxes().filter(":checked").length;
|
|
$(".bulk-actions").attr("data-target-count", checkedCount);
|
|
$(".bulk-actions-target-count").text(checkedCount);
|
|
}
|
|
|
|
$(".bulk-actions-move-all").bind("submit", function (e) {
|
|
// TODO: DRY
|
|
e.preventDefault();
|
|
var form = $(this);
|
|
var data = form.serializeArray();
|
|
data.push({
|
|
name: "return_to",
|
|
value: window.location.pathname + window.location.search,
|
|
});
|
|
|
|
var checkedBoxes = getCheckboxes().filter(":checked");
|
|
checkedBoxes.each(function () {
|
|
data.push({
|
|
name: "ids[]",
|
|
value: $(this).closest(".object").attr("data-id"),
|
|
});
|
|
});
|
|
|
|
$.ajax({
|
|
url: form.attr("action"),
|
|
type: form.attr("method"),
|
|
data: data,
|
|
success: function (html) {
|
|
var doc = $(html);
|
|
hangersEl.html(doc.find("#closet-hangers").html());
|
|
hangersInit();
|
|
updateBulkActions(); // don't want to maintain checked; deselect em all
|
|
doc
|
|
.find(".flash")
|
|
.hide()
|
|
.insertBefore(hangersEl)
|
|
.show(500)
|
|
.delay(5000)
|
|
.hide(250);
|
|
itemsSearchField.val("");
|
|
},
|
|
error: function (xhr) {
|
|
handleSaveError(xhr, "moving these items");
|
|
},
|
|
});
|
|
});
|
|
|
|
$(".bulk-actions-remove-all").bind("submit", function (e) {
|
|
e.preventDefault();
|
|
var form = $(this);
|
|
var hangerIds = [];
|
|
var checkedBoxes = getCheckboxes().filter(":checked");
|
|
var hangerEls = $();
|
|
checkedBoxes.each(function () {
|
|
hangerEls = hangerEls.add($(this).closest(".object"));
|
|
});
|
|
hangerEls.each(function () {
|
|
hangerIds.push($(this).attr("data-id"));
|
|
});
|
|
$.ajax({
|
|
url: form.attr("action") + ".json?" + $.param({ ids: hangerIds }),
|
|
type: "delete",
|
|
dataType: "json",
|
|
success: function () {
|
|
objectRemoved(hangerEls);
|
|
},
|
|
error: function () {
|
|
$.jGrowl("Error removing items. Try again?");
|
|
},
|
|
});
|
|
});
|
|
|
|
$(".bulk-actions-deselect-all").bind("click", function (e) {
|
|
getCheckboxes().filter(":checked").attr("checked", false);
|
|
updateBulkActions();
|
|
});
|
|
|
|
function maintainCheckboxes(fn) {
|
|
var checkedIds = getCheckedIds();
|
|
|
|
fn();
|
|
|
|
checkedIds.forEach(function (id) {
|
|
document.getElementById(id).checked = true;
|
|
});
|
|
updateBulkActions();
|
|
}
|
|
|
|
/*
|
|
|
|
Search, autocomplete
|
|
|
|
*/
|
|
|
|
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);
|
|
maintainCheckboxes(function () {
|
|
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 contactForm = contactEl.children("form");
|
|
var contactField = contactForm.children("select");
|
|
|
|
var contactAddOption = $("<option/>", {
|
|
text: contactField.attr("data-new-text"),
|
|
value: -1,
|
|
});
|
|
contactAddOption.appendTo(contactField);
|
|
var currentUserId = $("meta[name=current-user-id]").attr("content");
|
|
|
|
function submitContactForm() {
|
|
var data = contactForm.serialize();
|
|
contactForm.disableForms();
|
|
$.ajax({
|
|
url: contactForm.attr("action") + ".json",
|
|
type: "post",
|
|
data: data,
|
|
dataType: "json",
|
|
complete: function () {
|
|
contactForm.enableForms();
|
|
},
|
|
error: function (xhr) {
|
|
handleSaveError(xhr, "saving Neopets username");
|
|
},
|
|
});
|
|
}
|
|
|
|
contactField.change(function (e) {
|
|
if (contactField.val() < 0) {
|
|
var newUsername = $.trim(
|
|
prompt(contactField.attr("data-new-prompt"), ""),
|
|
);
|
|
if (newUsername) {
|
|
$.ajax({
|
|
url: "/user/" + currentUserId + "/neopets-connections",
|
|
type: "POST",
|
|
data: { neopets_connection: { neopets_username: newUsername } },
|
|
dataType: "json",
|
|
success: function (connection) {
|
|
var newOption = $("<option/>", {
|
|
text: newUsername,
|
|
value: connection.id,
|
|
});
|
|
newOption.insertBefore(contactAddOption);
|
|
contactField.val(connection.id);
|
|
submitContactForm();
|
|
},
|
|
});
|
|
}
|
|
} else {
|
|
submitContactForm();
|
|
}
|
|
});
|
|
|
|
/*
|
|
|
|
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();
|
|
})();
|