/** @jsx React.DOM */
(function($) {
// Console-polyfill. MIT license.
// https://github.com/paulmillr/console-polyfill
// Make it safe to do console.log() always.
var console = (function (con) {
'use strict';
var prop, method;
var empty = {};
var dummy = function() {};
var properties = 'memory'.split(',');
var methods = ('assert,count,debug,dir,dirxml,error,exception,group,' +
'groupCollapsed,groupEnd,info,log,markTimeline,profile,profileEnd,' +
'time,timeEnd,trace,warn').split(',');
while (prop = properties.pop()) con[prop] = con[prop] || empty;
while (method = methods.pop()) con[method] = con[method] || dummy;
return con;
})(window.console || {});
var Neopia = {
User: {
get: function(id) {
return $.getJSON(Neopia.API_URL + "/users/" + id).then(function(response) {
return response.users[0];
});
}
},
Customization: {
request: function(petId, type) {
return $.ajax({
dataType: "json",
type: type,
url: Neopia.API_URL + "/pets/" + petId + "/customization"
});
},
get: function(petId) {
return this.request(petId, "GET");
},
post: function(petId) {
return this.request(petId, "POST");
}
},
init: function() {
var hostEl = $('meta[name=neopia-host]');
if (!hostEl.length) {
throw "missing neopia-host meta tag";
}
var host = hostEl.attr('content');
if (!host) {
throw "neopia-host meta tag exists, but is empty";
}
Neopia.API_URL = "http://" + host + "/api/1";
}
};
var ImpressUser = (function() {
var userSignedIn = ($('meta[name=user-signed-in]').attr('content') === 'true');
if (userSignedIn) {
return {
// TODO
};
} else {
return {
_key: "guestNeopetsUsernames",
_setNeopetsUsernames: function(usernames) {
localStorage.setItem(this._key, JSON.stringify(usernames));
},
addNeopetsUsername: function(username) {
this._setNeopetsUsernames(this.getNeopetsUsernames().concat([username]));
},
removeNeopetsUsername: function(username) {
this._setNeopetsUsernames(this.getNeopetsUsernames().filter(function(u) {
return u !== username;
}));
},
getNeopetsUsernames: function() {
return JSON.parse(localStorage.getItem(this._key)) || [];
}
};
}
})();
var Modeling = {
_customizationsByPetId: {},
_customizations: [],
_itemsById: {},
_items: [],
_usersComponent: {setState: function() {}},
_neopetsUsernamesPresenceMap: {},
_addCustomization: function(customization) {
// Set all equipped, interesting items' statuses as success and cross
// them off the list.
var itemsById = this._itemsById;
var equippedByZone = customization.custom_pet.equipped_by_zone;
var closetItems = customization.closet_items;
Object.keys(equippedByZone).forEach(function(zoneId) {
var equippedClosetId = equippedByZone[zoneId].closet_obj_id;
var equippedObjectId = closetItems[equippedClosetId].obj_info_id;
if (itemsById.hasOwnProperty(equippedObjectId)) {
// TODO: i18n title
customization.statusByItemId[equippedObjectId] = "success";
itemsById[equippedObjectId].el.find("span[data-body-id=" +
customization.custom_pet.body_id + "]").addClass("modeled")
.attr("title", "You just finished modeling this—thanks so much!");
}
});
this._customizationsByPetId[customization.custom_pet.name] = customization;
this._customizations = this._buildCustomizations();
this._updateCustomizations();
},
_addNewCustomization: function(customization) {
customization.loadingForItemId = null;
customization.statusByItemId = {};
this._addCustomization(customization);
},
_buildCustomizations: function() {
var modelCustomizationsByPetId = this._customizationsByPetId;
return Object.keys(modelCustomizationsByPetId).map(function(petId) {
return modelCustomizationsByPetId[petId];
});
},
_createItems: function($) {
var itemsById = this._itemsById;
this._items = $('#newest-unmodeled-items li').map(function() {
var el = $(this);
var item = {
el: el,
id: el.attr('data-item-id'),
name: el.find('h2').text(),
missingBodyIdsPresenceMap: el.find('span[data-body-id]').toArray().reduce(function(map, node) {
map[$(node).attr('data-body-id')] = true;
return map;
}, {})
};
item.component = React.renderComponent(,
el.find('.models').get(0));
itemsById[item.id] = item;
return item;
}).toArray();
},
_loadPetCustomization: function(neopiaPetId) {
return Neopia.Customization.get(neopiaPetId)
.done(this._addNewCustomization.bind(this))
.fail(function() {
console.error("couldn't load pet %s", neopiaPetId);
});
},
_loadManyPetsCustomizations: function(neopiaPetIds) {
return neopiaPetIds.map(this._loadPetCustomization.bind(this));
},
_loadUserCustomizations: function(neopiaUserId) {
return Neopia.User.get(neopiaUserId).then(function(neopiaUser) {
return neopiaUser.links.pets;
}).then(this._loadManyPetsCustomizations.bind(this)).fail(function() {
console.error("couldn't load user %s's customizations", neopiaUserId);
});
},
_startLoading: function(neopiaPetId, itemId) {
var customization = this._customizationsByPetId[neopiaPetId];
customization.loadingForItemId = itemId;
customization.statusByItemId[itemId] = "loading";
this._updateCustomizations();
},
_stopLoading: function(neopiaPetId, itemId, status) {
var customization = this._customizationsByPetId[neopiaPetId];
customization.loadingForItemId = null;
customization.statusByItemId[itemId] = status;
this._updateCustomizations();
},
_updateCustomizations: function() {
var neopetsUsernamesPresenceMap = this._neopetsUsernamesPresenceMap;
var liveCustomizations = this._customizations.filter(function(c) {
return neopetsUsernamesPresenceMap[c.custom_pet.owner];
});
this._items.forEach(function(item) {
var filteredCustomizations = liveCustomizations.filter(function(c) {
return item.missingBodyIdsPresenceMap[c.custom_pet.body_id];
});
item.component.setState({customizations: filteredCustomizations});
});
},
_updateUsernames: function() {
var usernames = Object.keys(this._neopetsUsernamesPresenceMap);
this._usersComponent.setState({usernames: usernames});
},
init: function($) {
Neopia.init();
this._createItems($);
var usersEl = $('#modeling-neopets-users');
this._usersComponent = React.renderComponent(,
usersEl.get(0));
var usernames = ImpressUser.getNeopetsUsernames();
usernames.forEach(this.addUsername.bind(this));
},
model: function(neopiaPetId, itemId) {
var oldCustomization = this._customizationsByPetId[neopiaPetId];
var itemsById = this._itemsById;
this._startLoading(neopiaPetId, itemId);
return Neopia.Customization.post(neopiaPetId)
.done(function(newCustomization) {
// Add this field as null for consistency.
newCustomization.loadingForItemId = null;
// Copy previous statuses.
newCustomization.statusByItemId = oldCustomization.statusByItemId;
// Set the attempted item's status as unworn (to possibly be
// overridden by the upcoming loop in _addCustomization).
newCustomization.statusByItemId[itemId] = "unworn";
// Now, finally, let's overwrite the old customization with the new.
Modeling._addCustomization(newCustomization);
})
.fail(function() {
Modeling._stopLoading(neopiaPetId, itemId, "error");
});
},
addUsername: function(username) {
if (typeof this._neopetsUsernamesPresenceMap[username] === 'undefined') {
this._neopetsUsernamesPresenceMap[username] = true;
ImpressUser.addNeopetsUsername(username);
this._loadUserCustomizations(username);
this._updateUsernames();
}
},
removeUsername: function(username) {
if (this._neopetsUsernamesPresenceMap[username]) {
delete this._neopetsUsernamesPresenceMap[username];
ImpressUser.removeNeopetsUsername(username);
this._updateCustomizations();
this._updateUsernames();
}
}
};
var ModelForItem = React.createClass({
getInitialState: function() {
return {customizations: []};
},
render: function() {
var item = this.props.item;
function createModelPet(customization) {
return ;
}
var sortedCustomizations = this.state.customizations.slice(0).sort(function(a, b) {
var aName = a.custom_pet.name.toLowerCase();
var bName = b.custom_pet.name.toLowerCase();
if (aName < bName) return -1;
if (aName > bName) return 1;
return 0;
});
return
{sortedCustomizations.map(createModelPet)}
;
}
});
var ModelPet = React.createClass({
render: function() {
var petName = this.props.customization.custom_pet.name;
var status = this.props.customization.statusByItemId[this.props.item.id];
var loadingForItemId = this.props.customization.loadingForItemId;
var disabled = (status === "loading"
|| status === "success");
if (loadingForItemId !== null && loadingForItemId !== this.props.item.id) {
disabled = true;
}
var itemName = this.props.item.name;
var imageSrc = "http://pets.neopets.com/cpn/" + petName + "/1/1.png";
// TODO: i18n
var title = "Submit " + petName + " as a model, especially if they're " +
"wearing the " + itemName + "!";
if (status === "success") {
var statusMessage = "Thanks! <3";
} else if (status === "unworn") {
var statusMessage = "Not wearing this item.";
} else if (status === "error") {
var statusMessage = "Couldn't load. Try again?";
} else {
var statusMessage ="";
}
return ;
},
handleClick: function(e) {
Modeling.model(this.props.customization.custom_pet.name, this.props.item.id);
}
});
var NeopetsUsernamesForm = React.createClass({
getInitialState: function() {
return {usernames: [], newUsername: ""};
},
render: function() {
function buildUsernameItem(username) {
return ;
}
return
{this.state.usernames.slice(0).sort().map(buildUsernameItem)}
;
},
handleChange: function(e) {
this.setState({newUsername: e.target.value});
},
handleSubmit: function(e) {
e.preventDefault();
this.state.newUsername = $.trim(this.state.newUsername);
if (this.state.newUsername.length) {
Modeling.addUsername(this.state.newUsername);
this.setState({newUsername: ""});
}
}
});
var NeopetsUsernameItem = React.createClass({
render: function() {
return {this.props.username}
},
handleClick: function(e) {
Modeling.removeUsername(this.props.username);
}
});
Modeling.init($);
})(jQuery);