$.fn.notify = function () { return this.stop(true, true).show('slow').delay(5000).hide('fast'); } $.fn.startLoading = function () { return this.delay(1000).queue(function (next) { $(this).addClass('loading'); next(); }); } $.fn.stopLoading = function () { return this.removeClass('loading').clearQueue(); } var Partial = {}, main_wardrobe, View = Wardrobe.getStandardView({ Preview: { swf_url: '/swfs/preview.swf?v=0.12', wrapper: $('#preview-swf'), placeholder: $('#preview-swf-container'), image_container: '#preview-image-container' } }); Partial.ItemSet = function ItemSet(wardrobe, selector) { var item_set = this, ul = $(selector), items = [], setClosetItems, setOutfitItems, setOutfitItemsControls, no_assets_full_message = $('#no-assets-full-message'), container = $('#container'), item_template = $('#item-template'); Partial.ItemSet.setWardrobe(wardrobe); function prepSetSpecificItems(type) { return function (specific_items) { var item, worn, li; for(var i = 0; i < items.length; i++) { item = items[i]; in_set = $.inArray(item, specific_items) != -1; li = $('li.object-' + item.id).toggleClass(type, in_set). data('item', item).data(type, in_set); } } } setClosetItems = prepSetSpecificItems('closeted'); setOutfitItemsControls = prepSetSpecificItems('worn'); setOutfitItems = function (specific_items) { setOutfitItemsControls(specific_items); setHasAssets(specific_items); } function setHasAssets(specific_items) { var item, no_assets, li, no_assets_message; for(var i = 0, l = specific_items.length; i < l; i++) { item = specific_items[i]; no_assets = item.couldNotLoadAssetsFitting(wardrobe.outfits.getPetType()); li = $('li.object-' + item.id).toggleClass('no-assets', no_assets); } } this.setItems = function (new_items) { var item, li, controls, info_link; items = new_items; ul.children().remove(); for(var i = 0; i < items.length; i++) { item = items[i]; li = item_template.tmpl({ id: item.id, name: item.name, thumbnail_url: item.thumbnail_url, url: item.getURL(), nc: typeof item.rarity_index != 'undefined' && (item.rarity_index == 500 || item.rarity_index == 0), owned: item.owned, wanted: item.wanted }); li.appendTo(ul); } setClosetItems(wardrobe.outfits.getClosetItems()); setOutfitItems(wardrobe.outfits.getWornItems()); } $('span.no-assets-message').live('mouseover', function () { var el = $(this), o = el.offset(); no_assets_full_message.css({ left: o.left + (el.width() / 2) - (no_assets_full_message.width() / 2) - container.offset().left, top: o.top + el.height() + 10 }); }).live('mouseout', function () { no_assets_full_message.removeAttr('style'); }); wardrobe.outfits.bind('updateItemAssets', function () { setHasAssets(wardrobe.outfits.getWornItems()) }); wardrobe.outfits.bind('updateWornItems', setOutfitItems); wardrobe.outfits.bind('updateClosetItems', setClosetItems); } Partial.ItemSet.CONTROL_SETS = {}; Partial.ItemSet.setWardrobe = function (wardrobe) { var type, toggle, toggle_fn = {}, toggle_classes = {worn: {}, closeted: {}}; toggle_classes.worn['false'] = 'wear-item'; toggle_classes.worn['true'] = 'unwear-item'; toggle_classes.closeted['false'] = 'closet-item'; toggle_classes.closeted['true'] = 'uncloset-item'; for(type in toggle_classes) { for(toggleKey in toggle_classes[type]) { var toggle = (toggleKey == 'true'); (function (type, toggle) { $('li.' + toggle_classes[type][toggle] + ' a').live('click', function (e) { e.preventDefault(); var item = $(this).closest('.object').data('item'); log(this, item, type, toggle, !toggle); toggle_fn[type][!toggle](item); }); })(type, toggle); } } toggle_fn.closeted = {}; toggle_fn.closeted[true] = $.proxy(wardrobe.outfits, 'closetItem'); toggle_fn.closeted[false] = $.proxy(wardrobe.outfits, 'unclosetItem'); toggle_fn.worn = {}; toggle_fn.worn[true] = $.proxy(wardrobe.outfits, 'wearItem'); toggle_fn.worn[false] = $.proxy(wardrobe.outfits, 'unwearItem'); Partial.ItemSet.setWardrobe = $.noop; } View.Closet = function (wardrobe) { var item_set = new Partial.ItemSet(wardrobe, '#preview-closet ul'); wardrobe.outfits.bind('updateClosetItems', $.proxy(item_set, 'setItems')); } View.Fullscreen = function (wardrobe) { var full = $(document.body).hasClass('fullscreen'), win = $(window), preview_el = $('#preview'), search_el = $('#preview-search-form'), preview_swf = $('#preview-swf'), sidebar_el = $('#preview-sidebar'), sidebar_content_el = $('#preview-sidebar-content'), sidebar_navbar_el = $('#preview-sidebar-navbar'), footer = $('#footer'), jwindow = $(window), overrideFull = false; function fit() { if(!overrideFull) { var newFull = jwindow.height() > 500; if(newFull != full) { full = newFull; $(document.body).toggleClass('fullscreen', full); if(!full) { preview_swf.removeAttr('style').css('visibility', 'visible'); preview_el.removeAttr('style'); sidebar_content_el.removeAttr('style'); } } } if(full) { preview_swf = $('#preview-swf'); // swf replaced var available = { height: search_el.offset().top - preview_el.offset().top, width: preview_el.innerWidth() - sidebar_el.outerWidth() - 12 // 12px margin }, dim = {}, margin = {}, size = { old: {height: preview_swf.height(), width: preview_swf.width()}, next: {} }, offset; if(available.height > available.width) { dim.larger = 'height'; dim.smaller = 'width'; margin.active = 'marginTop'; margin.inactive = 'marginLeft'; } else { dim.larger = 'width'; dim.smaller = 'height'; margin.active = 'marginLeft'; margin.inactive = 'marginTop'; } size.next[dim.smaller] = available[dim.smaller]; size.next[dim.larger] = available[dim.smaller]; size.next[margin.active] = (available[dim.larger] - size.next[dim.larger]) / 2; size.next[margin.inactive] = 0; preview_swf.css(size.next); preview_el.height(available.height); // Now that preview is fit, we fit the sidebar's content element, which // also has to deal with the constraint of its navbar's height. var sidebar_content_height = available.height - sidebar_navbar_el.outerHeight() - 1; // 1px bottom border sidebar_content_el.height(sidebar_content_height); } } $('#preview').data('fit', fit); win.resize(fit).load(fit); // run fit after search updates select fields function fitSoon() { setTimeout(fit, 0) } wardrobe.item_zone_sets.bind('update', fitSoon); wardrobe.pet_attributes.bind('update', fitSoon); fit(); } View.Hash = function (wardrobe) { var data = {}, proposed_data = {}, previous_query, parse_in_progress = false, TYPES = { INTEGER: 1, STRING: 2, INTEGER_ARRAY: 3 }, KEYS = { closet: TYPES.INTEGER_ARRAY, color: TYPES.INTEGER, name: TYPES.STRING, objects: TYPES.INTEGER_ARRAY, outfit: TYPES.INTEGER, search: TYPES.STRING, search_offset: TYPES.INTEGER, species: TYPES.INTEGER, state: TYPES.INTEGER }, links_with_return_to = $('a[href*=return_to]'); function checkQuery() { var query = (document.location.hash || document.location.search).substr(1); if(query != previous_query) { parseQuery(query); previous_query = query; } } function parseQuery(query) { var new_data = {}, pairs = query.split('&'); parse_in_progress = true; for(var i = 0; i < pairs.length; i++) { var pair = pairs[i].split('='), key = decodeURIComponent(pair[0]), value = decodeURIComponent(pair[1]); if(value) { if(KEYS[key] == TYPES.INTEGER) { new_data[key] = +value; } else if(KEYS[key] == TYPES.STRING) { new_data[key] = decodeURIComponent(value).replace(/\+/g, ' '); } else if(key.substr(key.length-2) == '[]') { key = key.substr(0, key.length-2); if(KEYS[key] == TYPES.INTEGER_ARRAY) { if(typeof new_data[key] == 'undefined') new_data[key] = []; new_data[key].push(+value); } } } } if(new_data.color !== data.color || new_data.species !== data.species) { wardrobe.outfits.setPetTypeByColorAndSpecies(new_data.color, new_data.species); } if(new_data.closet) { if(!arraysMatch(new_data.closet, data.closet)) { wardrobe.outfits.setClosetItemsByIds(new_data.closet.slice(0)); } } else if(new_data.objects && !arraysMatch(new_data.objects, data.closet)) { wardrobe.outfits.setClosetItemsByIds(new_data.objects.slice(0)); } else { wardrobe.outfits.setClosetItemsByIds([]); } if(new_data.objects) { if(!arraysMatch(new_data.objects, data.objects)) { wardrobe.outfits.setWornItemsByIds(new_data.objects.slice(0)); } } else { wardrobe.outfits.setWornItemsByIds([]); } if(new_data.name != data.name && new_data.name) { wardrobe.base_pet.setName(new_data.name); } if(new_data.state != data.state) { wardrobe.outfits.setPetStateById(new_data.state); } if(new_data.outfit != data.outfit) { wardrobe.outfits.setId(new_data.outfit); } if(new_data.search != data.search || new_data.search_offset != data.search_offset) { wardrobe.search.setItemsByQuery(new_data.search, {offset: new_data.search_offset}); } data = new_data; parse_in_progress = false; updateLinksWithReturnTo(); } function changeQuery(changes) { var value; if(!parse_in_progress) { for(var key in changes) { if(changes.hasOwnProperty(key)) { value = changes[key]; if(value === undefined) { delete data[key]; } else { data[key] = changes[key]; } } } updateQuery(); } } function updateQuery() { var new_query; new_query = $.param(data).replace(/%5B%5D/g, '[]'); previous_query = new_query; document.location.hash = '#' + new_query; updateLinksWithReturnTo(); } function updateLinksWithReturnTo() { links_with_return_to.each(function () { var new_return_to = 'return_to=' + encodeURIComponent( document.location.pathname + document.location.search + document.location.hash ); this.href = this.href.replace(/return_to=[^&]+/, new_return_to); }); } this.initialize = function () { checkQuery(); setInterval(checkQuery, 100); } function singleOutfitResponse(event_name, response) { wardrobe.outfits.bind(event_name, function () { if(!wardrobe.outfits.in_transaction) response.apply(this, arguments); }); } singleOutfitResponse('updateClosetItems', function (items) { var item_ids = items.mapProperty('id'); if(!arraysMatch(item_ids, data.closet)) { changeQuery({closet: item_ids}); } }); singleOutfitResponse('updateWornItems', function (items) { var item_ids = items.mapProperty('id'), changes = {}; if(!arraysMatch(item_ids, data.objects)) { changes.objects = item_ids; } if(arraysMatch(item_ids, data.closet) || arraysMatch(item_ids, data.objects)) { changes.closet = undefined; } else { changes.closet = wardrobe.outfits.getClosetItems().mapProperty('id'); } if(changes.objects || changes.closet) changeQuery(changes); }); singleOutfitResponse('updatePetType', function (pet_type) { if(pet_type.color_id != data.color || pet_type.species_id != data.species) { changeQuery({ color: pet_type.color_id, species: pet_type.species_id, state: undefined }); } }); singleOutfitResponse('petTypeNotFound', function () { window.history.back(); }); singleOutfitResponse('updatePetState', function (pet_state) { var pet_type = wardrobe.outfits.getPetType(); if(pet_state.id != data.state && pet_type && (data.state || pet_state.id != pet_type.pet_states[0].id)) { changeQuery({state: pet_state.id}); } }); singleOutfitResponse('setOutfit', function (outfit) { if(outfit.id != data.outfit) { changeQuery({outfit: outfit.id}); } }); wardrobe.outfits.bind('loadOutfit', function (outfit) { changeQuery({ closet: outfit.getClosetItemIds(), color: outfit.pet_type.color_id, objects: outfit.getWornItemIds(), outfit: outfit.id, species: outfit.pet_type.species_id, state: outfit.pet_state.id }); }); wardrobe.outfits.bind('outfitNotFound', function (outfit) { var new_id = outfit ? outfit.id : undefined; changeQuery({outfit: new_id}); }); wardrobe.search.bind('updateRequest', function (request) { if(request.offset != data.search_offset || request.query != data.search) { changeQuery({ search_offset: request.offset, search: request.query }); } }); } View.Outfits = function (wardrobe) { var current_outfit_permalink_el = $('#current-outfit-permalink'), new_outfit_form_el = $('#save-outfit-form'), new_outfit_name_el = $('#save-outfit-name'), outfits_el = $('#preview-outfits'), outfits_list_el = outfits_el.children('ul'), outfit_not_found_el = $('#outfit-not-found'), save_current_outfit_el = $('#save-current-outfit'), save_current_outfit_name_el = save_current_outfit_el.children('span'), save_outfit_wrapper_el = $('#save-outfit-wrapper'), save_success_el = $('#save-success'), save_error_el = $('#save-error'), stars = $('#preview-outfits div.outfit-star'), sidebar_el = $('#preview-sidebar'), signed_in, previously_viewing = ''; function liForOutfit(outfit) { return $('li.outfit-' + outfit.id); } function navigateTo(will_be_viewing) { var currently_viewing = sidebar_el.attr('class'); if(currently_viewing != will_be_viewing) previously_viewing = currently_viewing; sidebar_el.attr('class', will_be_viewing); } /* Show for login */ signed_in = $('meta[name=user-signed-in]').attr('content') == 'true'; if(signed_in) { $(document.body).addClass('user-signed-in'); } else { $(document.body).addClass('user-not-signed-in'); } /* Nav */ function showCloset() { sharing.onHide(); navigateTo(''); } function showOutfits() { sharing.onHide(); wardrobe.outfits.loadOutfits(); navigateTo('viewing-outfits'); } function showSharing() { sharing.onShow(); navigateTo('sharing'); } function showNewOutfitForm() { new_outfit_name_el.val(''); new_outfit_form_el.removeClass('starred').stopLoading(); save_outfit_wrapper_el.addClass('saving-outfit'); new_outfit_name_el.focus(); } function hideNewOutfitForm() { save_outfit_wrapper_el.removeClass('saving-outfit'); } $('#preview-sidebar-navbar-closet').click(showCloset); $('#preview-sidebar-navbar-sharing').click(function () { sharing.startLoading(); wardrobe.outfits.share(); showSharing(); }); $('#preview-sidebar-navbar-outfits').click(showOutfits); $('#save-outfit, #save-outfit-copy').click(showNewOutfitForm); $('#save-outfit-cancel').click(hideNewOutfitForm); $('#save-outfit-not-signed-in').click(function () { window.location.replace($('#userbar a').attr('href')); }); /* Outfits list */ var list_image_subscriptions = {}; function listSubscribeToImage(outfit) { list_image_subscriptions[outfit.id] = wardrobe.image_subscriptions.subscribe(outfit); } function listUnsubscribeFromImage(outfit) { if(outfit.id in list_image_subscriptions) { if(list_image_subscriptions[outfit.id] !== null) { wardrobe.image_subscriptions.unsubscribe(list_image_subscriptions[outfit.id]); } delete list_image_subscriptions[outfit.id]; } } $('#outfit-template').template('outfitTemplate'); wardrobe.outfits.bind('outfitsLoaded', function (outfits) { var outfit_els = $.tmpl('outfitTemplate', outfits); outfits_list_el.html('').append(outfit_els).addClass('loaded'); updateActiveOutfit(); for(var i = 0; i < outfits.length; i++) { listSubscribeToImage(outfits[i]); } }); wardrobe.outfits.bind('addOutfit', function (outfit, i) { var next_child = outfits_list_el.children().not('.hiding').eq(i), outfit_el = $.tmpl('outfitTemplate', outfit.clone()); if(next_child.length) { outfit_el.insertBefore(next_child); } else { outfit_el.appendTo(outfits_list_el); } updateActiveOutfit(); var naturalWidth = outfit_el.css('width'); log("Natural width is", naturalWidth, outfit_el.width()); outfit_el.width(0).animate({width: naturalWidth}, 'normal'); listSubscribeToImage(outfit); }); wardrobe.outfits.bind('removeOutfit', function (outfit, i) { var outfit_el = outfits_list_el.children().not('.hiding').eq(i); outfit_el.addClass('hiding').stop(true).animate({width: 0}, 'normal', function () { outfit_el.remove() }); listUnsubscribeFromImage(outfit); }); $('#preview-outfits li header, #preview-outfits li .outfit-thumbnail-wrapper').live('click', function () { wardrobe.outfits.load($(this).tmplItem().data.id); }); $('a.outfit-rename-button').live('click', function (e) { e.preventDefault(); var li = $(this).closest('li').addClass('renaming'), name = li.find('span.outfit-name').text(); li.find('input.outfit-rename-field').val(name).focus(); }); function submitRename() { var el = $(this), outfit = el.tmplItem().data, new_name = el.val(), li = el.closest('li').removeClass('renaming'); if(new_name != outfit.name) { li.startLoading(); wardrobe.outfits.renameOutfit(outfit, new_name); } } $('input.outfit-rename-field').live('blur', submitRename); $('form.outfit-rename-form').live('submit', function (e) { e.preventDefault(); var input = $(this).find('input'); submitRename.apply(input); }); $('input.outfit-url').live('mouseover', function () { this.focus(); }).live('mouseout', function () { this.blur(); }); $('a.outfit-delete').live('click', function (e) { e.stopPropagation(); e.preventDefault(); $(this).closest('li').addClass('confirming-deletion'); }); $('a.outfit-delete-confirmation-yes').live('click', function (e) { var outfit = $(this).tmplItem().data; e.preventDefault(); wardrobe.outfits.destroyOutfit(outfit); if(wardrobe.outfits.getOutfit().id == outfit.id) { wardrobe.outfits.setId(null); } }); $('a.outfit-delete-confirmation-no').live('click', function (e) { e.preventDefault(); $(this).closest('li').removeClass('confirming-deletion'); }); stars.live('click', function (e) { e.stopPropagation(); var el = $(this); el.closest('li').startLoading(); wardrobe.outfits.toggleOutfitStar(el.tmplItem().data); }); function absoluteUrl(path_or_url) { if(path_or_url.indexOf('://') == -1) { var host = document.location.protocol + "//" + document.location.host; return host + path_or_url; } else { return path_or_url; } } function generateOutfitPermalink(outfit) { return absoluteUrl("/outfits/" + outfit.id); } function setOutfitPermalink(outfit, outfit_permalink_el, outfit_url_el) { var url = generateOutfitPermalink(outfit); outfit_permalink_el.attr('href', url); if(outfit_url_el) outfit_url_el.val(url); } function setCurrentOutfitPermalink(outfit) { setOutfitPermalink(outfit, current_outfit_permalink_el); } function setActiveOutfit(outfit) { outfits_list_el.find('li.active').removeClass('active'); if(outfit.id) { setCurrentOutfitPermalink(outfit); liForOutfit(outfit).addClass('active'); save_current_outfit_name_el.text(outfit.name); } save_outfit_wrapper_el.toggleClass('active-outfit', outfit.id ? true : false); } function updateActiveOutfit() { setActiveOutfit(wardrobe.outfits.getOutfit()); } wardrobe.outfits.bind('setOutfit', setActiveOutfit); wardrobe.outfits.bind('outfitNotFound', setActiveOutfit); wardrobe.outfits.bind('outfitRenamed', function (outfit) { if(outfit.id == wardrobe.outfits.getId()) { save_current_outfit_name_el.text(outfit.name); } }); function outfitElement(outfit) { return outfits_el.find('li.outfit-' + outfit.id); } wardrobe.outfits.bind('saveSuccess', function (outfit) { listSubscribeToImage(outfit); }); wardrobe.image_subscriptions.bind('imageEnqueued', function (outfit) { if(outfit.id in list_image_subscriptions) { log("List sees imageEnqueued for", outfit); outfitElement(outfit).removeClass('thumbnail-loaded'); } }); wardrobe.image_subscriptions.bind('imageReady', function (outfit) { if(outfit.id in list_image_subscriptions) { log("List sees imageReady for", outfit); listUnsubscribeFromImage(outfit); var src = outfit.image_versions.small + '?' + (new Date()).getTime(); outfitElement(outfit).addClass('thumbnail-loaded').addClass('thumbnail-available'). find('img.outfit-thumbnail').attr('src', src); } }); /* Sharing */ var sharing = new function Sharing() { var WRAPPER = $('#preview-sharing'); var sharing_url_els = { permalink: $('#preview-sharing-permalink-url'), large_image: $('#preview-sharing-large-image-url'), medium_image: $('#preview-sharing-medium-image-url'), small_image: $('#preview-sharing-small-image-url'), }; var format_selector_els = $('#preview-sharing-url-formats li'); var thumbnail_el = $('#preview-sharing-thumbnail'); var templates = { html: { image: $('#sharing-html-image-template'), text: $('#sharing-html-text-template') }, bbcode: { image: $('#sharing-bbcode-image-template'), text: $('#sharing-bbcode-text-template') } } function templateHTML(template, options) { var contents = template.tmpl(options); var contentsHTML = contents.clone().wrap('