+
You do not have permission to access this page.
+
This resource might belong to another user, or your session may have expired.
+
Try logging in again.
+
+
+
+
diff --git a/public/images/nc.png b/public/images/nc.png
index 42c52d05..0ca9074d 100644
Binary files a/public/images/nc.png and b/public/images/nc.png differ
diff --git a/public/images/neomail.png b/public/images/neomail.png
new file mode 100644
index 00000000..4a6c5d39
Binary files /dev/null and b/public/images/neomail.png differ
diff --git a/public/images/neomail_edit.png b/public/images/neomail_edit.png
new file mode 100644
index 00000000..244f04ae
Binary files /dev/null and b/public/images/neomail_edit.png differ
diff --git a/public/images/owned.png b/public/images/owned.png
new file mode 100644
index 00000000..a9925a06
Binary files /dev/null and b/public/images/owned.png differ
diff --git a/public/images/wanted.png b/public/images/wanted.png
new file mode 100644
index 00000000..003924f5
Binary files /dev/null and b/public/images/wanted.png differ
diff --git a/public/images/your_items.png b/public/images/your_items.png
new file mode 100644
index 00000000..87973fc6
Binary files /dev/null and b/public/images/your_items.png differ
diff --git a/public/javascripts/closet_hangers/index.js b/public/javascripts/closet_hangers/index.js
new file mode 100644
index 00000000..bfa4a025
--- /dev/null
+++ b/public/javascripts/closet_hangers/index.js
@@ -0,0 +1,510 @@
+(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');
+ });
+
+ /*
+
+ 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;
+
+ var hangersElQuery = '#closet-hangers';
+ var hangersEl = $(hangersElQuery);
+
+ $.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(id, item) {
+ if(id) {
+ return $('#closet-list-' + id);
+ } else {
+ return item.closest('.closet-hangers-group').find('div.closet-list.unlisted');
+ }
+ }
+
+ function updateListHangersCount(el) {
+ el.attr('data-hangers-count', el.find('div.object').length);
+ }
+
+ function moveItemToList(item, listId) {
+ var newList = findList(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 listEl = form.children("input[name=closet_hanger\[list_id\]]");
+ var listChanged = 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, 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 () {
+ quantityEl.storeValue();
+ listEl.storeValue();
+ },
+ error: function (xhr) {
+ quantityEl.revertValue();
+ listEl.revertValue();
+ if(listChanged) moveItemToList(objectWrapper, 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\[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.hangerInGroup) closetHanger.quantity = 1;
+
+ $.ajax({
+ url: "/user/" + itemsSearchForm.data("current-user-id") + "/items/" + item.id + "/closet_hanger",
+ 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, hangerInGroup, currentListId;
+ for(var i in hangerGroups) {
+ group = hangerGroups[i];
+ itemEl = $('div.closet-hangers-group[data-owned=' + group.owned + '] div.object[data-item-id=' + item.id + ']');
+ hangerInGroup = itemEl.length > 0;
+ currentListId = itemEl.closest('.closet-list').attr('data-id');
+
+ groupInserts[groupInserts.length] = {
+ group: group,
+ item: item,
+ label: item.label,
+ hangerInGroup: hangerInGroup,
+ hangerInList: !!currentListId
+ }
+
+ for(var i = 0; i < group.lists.length; i++) {
+ groupInserts[groupInserts.length] = {
+ group: group,
+ item: item,
+ label: item.label,
+ list: group.lists[i],
+ hangerInGroup: hangerInGroup,
+ currentListId: currentListId
+ }
+ }
+ }
+ callback(groupInserts);
+ }
+ }
+ });
+
+ var autocompleter = itemsSearchField.data("autocomplete");
+
+ autocompleter._renderItem = function( ul, item ) {
+ var li = $("