forked from OpenNeo/impress
totally pro wardrobe image adapter, via konami
This commit is contained in:
parent
926f2a5350
commit
6c9ddac8dd
11 changed files with 447 additions and 224 deletions
|
@ -1,8 +0,0 @@
|
|||
class AssetImageConversionRequestsController < ApplicationController
|
||||
def create
|
||||
@swf_asset = SwfAsset.find params[:swf_asset_id]
|
||||
@swf_asset.request_image_conversion!
|
||||
render :nothing => true
|
||||
end
|
||||
end
|
||||
|
|
@ -26,8 +26,22 @@ class SwfAssetsController < ApplicationController
|
|||
json = @swf_assets.map { |a| a.as_json(:parent_id => pet_state_id, :for => 'wardrobe') }
|
||||
elsif params[:pet_type_id]
|
||||
@swf_assets = PetType.find(params[:pet_type_id]).pet_states.emotion_order.first.swf_assets
|
||||
elsif params[:ids]
|
||||
@swf_assets = []
|
||||
if params[:ids][:biology]
|
||||
@swf_assets += SwfAsset.biology_assets.where(:id => params[:ids][:biology]).all
|
||||
end
|
||||
if params[:ids][:object]
|
||||
@swf_assets += SwfAsset.object_assets.where(:id => params[:ids][:object]).all
|
||||
end
|
||||
end
|
||||
if @swf_assets
|
||||
@swf_assets = @swf_assets.all unless @swf_assets.is_a? Array
|
||||
@swf_assets.each(&:request_image_conversion!)
|
||||
json = @swf_assets unless json
|
||||
else
|
||||
json = nil
|
||||
end
|
||||
json ||= @swf_assets ? @swf_assets.all : nil
|
||||
render :json => json
|
||||
end
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
class AssetImageConversionRequest
|
||||
@queue = :requested_asset_images
|
||||
|
||||
def self.perform(asset_id)
|
||||
asset = SwfAsset.find asset_id
|
||||
def self.perform(asset_type, asset_id)
|
||||
asset = SwfAsset.where(:type => asset_type).find(asset_id)
|
||||
asset.convert_swf_if_not_converted!
|
||||
end
|
||||
|
||||
|
|
|
@ -27,11 +27,10 @@ class SwfAsset < ActiveRecord::Base
|
|||
|
||||
def after_swf_conversion(images)
|
||||
images.each do |size, path|
|
||||
s3_key = URI.encode("#{self.id}/#{size.join 'x'}.png")
|
||||
|
||||
print "Uploading #{s3_key}..."
|
||||
key = s3_key(size)
|
||||
print "Uploading #{key}..."
|
||||
IMAGE_BUCKET.put(
|
||||
s3_key,
|
||||
key,
|
||||
File.open(path),
|
||||
{}, # meta headers
|
||||
IMAGE_PERMISSION, # permission
|
||||
|
@ -43,6 +42,25 @@ class SwfAsset < ActiveRecord::Base
|
|||
end
|
||||
end
|
||||
|
||||
def s3_key(size)
|
||||
URI.encode("#{s3_path}/#{size.join 'x'}.png")
|
||||
end
|
||||
|
||||
def s3_path
|
||||
"#{type}/#{s3_partition_path}#{self.id}"
|
||||
end
|
||||
|
||||
PARTITION_COUNT = 3
|
||||
PARTITION_DIGITS = 3
|
||||
PARTITION_ID_LENGTH = PARTITION_COUNT * PARTITION_DIGITS
|
||||
def s3_partition_path
|
||||
(id / 10**PARTITION_DIGITS).to_s.rjust(PARTITION_ID_LENGTH, '0').tap do |id_str|
|
||||
PARTITION_COUNT.times do |n|
|
||||
id_str.insert(PARTITION_ID_LENGTH - (n * PARTITION_DIGITS), '/')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def convert_swf_if_not_converted!
|
||||
if has_image?
|
||||
false
|
||||
|
@ -58,7 +76,7 @@ class SwfAsset < ActiveRecord::Base
|
|||
if image_requested?
|
||||
false
|
||||
else
|
||||
Resque.enqueue(AssetImageConversionRequest, self.id)
|
||||
Resque.enqueue(AssetImageConversionRequest, self.type, self.id)
|
||||
self.image_requested = true
|
||||
save!
|
||||
true
|
||||
|
@ -101,12 +119,14 @@ class SwfAsset < ActiveRecord::Base
|
|||
def as_json(options={})
|
||||
json = {
|
||||
:id => id,
|
||||
:type => type,
|
||||
:depth => depth,
|
||||
:body_id => body_id,
|
||||
:zone_id => zone_id,
|
||||
:zones_restrict => zones_restrict,
|
||||
:is_body_specific => body_specific?,
|
||||
:has_image => has_image?
|
||||
:has_image => has_image?,
|
||||
:s3_path => s3_path
|
||||
}
|
||||
if options[:for] == 'wardrobe'
|
||||
json[:local_path] = local_url
|
||||
|
@ -179,8 +199,8 @@ class SwfAsset < ActiveRecord::Base
|
|||
self.body_id = 0 if !self.body_specific? || (!self.new_record? && self.body_id_changed?)
|
||||
end
|
||||
|
||||
after_create do
|
||||
Resque.enqueue(AssetImageConversionRequest::OnCreation, self.id)
|
||||
after_commit :on => :create do
|
||||
Resque.enqueue(AssetImageConversionRequest::OnCreation, self.type, self.id)
|
||||
end
|
||||
|
||||
class DownloadError < Exception;end
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
@import ../shared/jquery.jgrowl
|
||||
@import partials/wardrobe
|
||||
|
||||
@import icon
|
||||
@import star
|
||||
|
@ -156,6 +157,16 @@ body.outfits-edit
|
|||
margin-bottom: 1em
|
||||
position: relative
|
||||
width: $preview-dimension
|
||||
&.swf-adapter
|
||||
#preview-image-container
|
||||
display: none
|
||||
&.image-adapter
|
||||
#preview-swf-container
|
||||
display: none
|
||||
#preview-image-container
|
||||
+wardrobe-image-wrapper
|
||||
margin: 1em auto
|
||||
position: relative
|
||||
#preview-swf-overlay
|
||||
+opacity(0)
|
||||
background: black
|
||||
|
@ -164,6 +175,18 @@ body.outfits-edit
|
|||
position: absolute
|
||||
top: 0
|
||||
width: 100%
|
||||
#preview-images-pending
|
||||
background: black
|
||||
background: rgba(0, 0, 0, 0.75)
|
||||
bottom: 0
|
||||
color: white
|
||||
font-size: 75%
|
||||
padding: .5em
|
||||
position: absolute
|
||||
right: 0
|
||||
z-index: 1000
|
||||
&.waiting-on-0
|
||||
display: none
|
||||
#preview-sidebar
|
||||
+border-radius(10px)
|
||||
border: 1px solid $soft-border-color
|
||||
|
|
6
app/stylesheets/partials/_wardrobe.sass
Normal file
6
app/stylesheets/partials/_wardrobe.sass
Normal file
|
@ -0,0 +1,6 @@
|
|||
=wardrobe-image-wrapper
|
||||
img
|
||||
left: 0
|
||||
position: absolute
|
||||
top: 0
|
||||
|
|
@ -35,6 +35,7 @@
|
|||
#preview-swf-container
|
||||
%p Flash and Javascript (but not Java!) are required to preview outfits.
|
||||
%p If this message stays after the page is done loading, check those first.
|
||||
#preview-image-container
|
||||
#preview-sidebar
|
||||
#outfit-not-found Outfit not found
|
||||
#save-success Outfit successfully saved
|
||||
|
|
|
@ -22,9 +22,7 @@ OpenneoImpressItems::Application.routes.draw do |map|
|
|||
end
|
||||
resources :outfits, :only => [:show, :create, :update, :destroy]
|
||||
resources :pet_attributes, :only => [:index]
|
||||
resources :swf_assets, :only => [:show] do
|
||||
resources :asset_image_conversion_requests, :path => 'conversions', :only => [:create]
|
||||
end
|
||||
resources :swf_assets, :only => [:index, :show]
|
||||
|
||||
match '/users/current-user/outfits' => 'outfits#index', :as => :current_user_outfits
|
||||
|
||||
|
|
|
@ -25,8 +25,9 @@ var Partial = {}, main_wardrobe,
|
|||
View = Wardrobe.getStandardView({
|
||||
Preview: {
|
||||
swf_url: '/swfs/preview.swf?v=0.12',
|
||||
wrapper: $('#preview'),
|
||||
placeholder: $('#preview-swf-container')
|
||||
wrapper: $('#preview-swf'),
|
||||
placeholder: $('#preview-swf-container'),
|
||||
image_container: '#preview-image-container'
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -223,18 +224,6 @@ View.Fullscreen = function (wardrobe) {
|
|||
wardrobe.item_zone_sets.bind('update', fitSoon);
|
||||
wardrobe.pet_attributes.bind('update', fitSoon);
|
||||
fit();
|
||||
|
||||
var Konami=function(){var a={addEvent:function(b,c,d,e){if(b.addEventListener)b.addEventListener(c,d,false);else if(b.attachEvent){b["e"+c+d]=d;b[c+d]=function(){b["e"+c+d](window.event,e)};b.attachEvent("on"+c,b[c+d])}},input:"",pattern:"3838404037393739666513",load:function(b){this.addEvent(document,"keydown",function(c,d){if(d)a=d;a.input+=c?c.keyCode:event.keyCode;if(a.input.indexOf(a.pattern)!=-1){a.code(b);a.input=""}},this);this.iphone.load(b)},code:function(b){window.location=b},iphone:{start_x:0,start_y:0,stop_x:0,stop_y:0,tap:false,capture:false,keys:["UP","UP","DOWN","DOWN","LEFT","RIGHT","LEFT","RIGHT","TAP","TAP","TAP"],code:function(b){a.code(b)},load:function(b){a.addEvent(document,"touchmove",function(c){if(c.touches.length==1&&a.iphone.capture==true){c=c.touches[0];a.iphone.stop_x=c.pageX;a.iphone.stop_y=c.pageY;a.iphone.tap=false;a.iphone.capture=false;a.iphone.check_direction()}});a.addEvent(document,"touchend",function(){a.iphone.tap==true&&a.iphone.check_direction(b)},false);a.addEvent(document,"touchstart",function(c){a.iphone.start_x=c.changedTouches[0].pageX;a.iphone.start_y=c.changedTouches[0].pageY;a.iphone.tap=true;a.iphone.capture=true})},check_direction:function(b){x_magnitude=Math.abs(this.start_x-this.stop_x);y_magnitude=Math.abs(this.start_y-this.stop_y);x=this.start_x-this.stop_x<0?"RIGHT":"LEFT";y=this.start_y-this.stop_y<0?"DOWN":"UP";result=x_magnitude>y_magnitude?x:y;result=this.tap==true?"TAP":result;if(result==this.keys[0])this.keys=this.keys.slice(1,this.keys.length);this.keys.length==0&&this.code(b)}}};return a};
|
||||
konami = new Konami();
|
||||
konami.code = function () {
|
||||
overrideFull = true;
|
||||
$(document.body).removeClass('fullscreen');
|
||||
preview_swf.removeAttr('style').css('visibility', 'visible');
|
||||
preview_el.removeAttr('style');
|
||||
wardrobe.search.setItemsByQuery(wardrobe.search.request.query, {offset: wardrobe.search.request.offset});
|
||||
full = false;
|
||||
}
|
||||
konami.load();
|
||||
}
|
||||
|
||||
View.Hash = function (wardrobe) {
|
||||
|
@ -825,6 +814,16 @@ View.PetTypeForm = function (wardrobe) {
|
|||
});
|
||||
}
|
||||
|
||||
View.PreviewAdapterForm = function (wardrobe) {
|
||||
var preview = wardrobe.views.Preview;
|
||||
var Konami=function(){var a={addEvent:function(b,c,d,e){if(b.addEventListener)b.addEventListener(c,d,false);else if(b.attachEvent){b["e"+c+d]=d;b[c+d]=function(){b["e"+c+d](window.event,e)};b.attachEvent("on"+c,b[c+d])}},input:"",pattern:"3838404037393739666513",load:function(b){this.addEvent(document,"keydown",function(c,d){if(d)a=d;a.input+=c?c.keyCode:event.keyCode;if(a.input.indexOf(a.pattern)!=-1){a.code(b);a.input=""}},this);this.iphone.load(b)},code:function(b){window.location=b},iphone:{start_x:0,start_y:0,stop_x:0,stop_y:0,tap:false,capture:false,keys:["UP","UP","DOWN","DOWN","LEFT","RIGHT","LEFT","RIGHT","TAP","TAP","TAP"],code:function(b){a.code(b)},load:function(b){a.addEvent(document,"touchmove",function(c){if(c.touches.length==1&&a.iphone.capture==true){c=c.touches[0];a.iphone.stop_x=c.pageX;a.iphone.stop_y=c.pageY;a.iphone.tap=false;a.iphone.capture=false;a.iphone.check_direction()}});a.addEvent(document,"touchend",function(){a.iphone.tap==true&&a.iphone.check_direction(b)},false);a.addEvent(document,"touchstart",function(c){a.iphone.start_x=c.changedTouches[0].pageX;a.iphone.start_y=c.changedTouches[0].pageY;a.iphone.tap=true;a.iphone.capture=true})},check_direction:function(b){x_magnitude=Math.abs(this.start_x-this.stop_x);y_magnitude=Math.abs(this.start_y-this.stop_y);x=this.start_x-this.stop_x<0?"RIGHT":"LEFT";y=this.start_y-this.stop_y<0?"DOWN":"UP";result=x_magnitude>y_magnitude?x:y;result=this.tap==true?"TAP":result;if(result==this.keys[0])this.keys=this.keys.slice(1,this.keys.length);this.keys.length==0&&this.code(b)}}};return a};
|
||||
konami = new Konami();
|
||||
konami.code = function () {
|
||||
preview.toggleAdapter();
|
||||
}
|
||||
konami.load();
|
||||
}
|
||||
|
||||
View.Search = function (wardrobe) {
|
||||
var form_selector = '#preview-search-form', form = $(form_selector),
|
||||
item_set = new Partial.ItemSet(wardrobe, form_selector + ' ul'),
|
||||
|
|
|
@ -76,18 +76,22 @@ function Wardrobe() {
|
|||
}
|
||||
}
|
||||
|
||||
function Asset(data) {
|
||||
function Asset(newData) {
|
||||
var asset = this;
|
||||
|
||||
this.imageURL = function (size) {
|
||||
return Wardrobe.IMAGE_CONFIG.base_url + this.s3_path + "/" + size[0] + "x" + size[1] + ".png";
|
||||
}
|
||||
|
||||
this.update = function (data) {
|
||||
for(var key in data) {
|
||||
if(data.hasOwnProperty(key)) {
|
||||
asset[key] = data[key];
|
||||
}
|
||||
}
|
||||
|
||||
this.requestImageConversion = function () {
|
||||
$.post('/swf_assets/' + this.id + '/conversions');
|
||||
}
|
||||
|
||||
this.update(newData);
|
||||
}
|
||||
|
||||
function BiologyAsset(data) {
|
||||
|
@ -271,7 +275,8 @@ function Wardrobe() {
|
|||
// note: may contain duplicates - loop through assets, not these, for
|
||||
// best performance
|
||||
var restricted_zones = [],
|
||||
restrictors = outfit.worn_items.concat(outfit.pet_state.assets);
|
||||
restrictors = outfit.worn_items;
|
||||
if(outfit.pet_state) restrictors = restrictors.concat(outfit.pet_state.assets);
|
||||
$.each(restrictors, function () {
|
||||
restricted_zones = restricted_zones.concat(this.restricted_zones);
|
||||
});
|
||||
|
@ -366,8 +371,9 @@ function Wardrobe() {
|
|||
}
|
||||
|
||||
this.getVisibleAssets = function () {
|
||||
var assets = this.pet_state.assets, restricted_zones = getRestrictedZones(),
|
||||
var assets, restricted_zones = getRestrictedZones(),
|
||||
visible_assets = [];
|
||||
assets = this.pet_state ? this.pet_state.assets : [];
|
||||
for(var i = 0; i < outfit.worn_items.length; i++) {
|
||||
assets = assets.concat(outfit.worn_items[i].getAssetsFitting(outfit.pet_type));
|
||||
}
|
||||
|
@ -1114,6 +1120,15 @@ function Wardrobe() {
|
|||
}
|
||||
}
|
||||
|
||||
Wardrobe.IMAGE_CONFIG = {
|
||||
base_url: "https://s3.amazonaws.com/impress-asset-images/",
|
||||
sizes: [
|
||||
[600, 600],
|
||||
[300, 300],
|
||||
[150, 150]
|
||||
]
|
||||
}
|
||||
|
||||
Wardrobe.StandardPreview = {
|
||||
views_by_swf_id: {}
|
||||
};
|
||||
|
@ -1163,6 +1178,7 @@ Wardrobe.getStandardView = function (options) {
|
|||
}
|
||||
|
||||
StandardView.Preview = function (wardrobe) {
|
||||
var preview = this;
|
||||
var preview_el = $(options.Preview.wrapper),
|
||||
preview_swf_placeholder = $(options.Preview.placeholder);
|
||||
|
||||
|
@ -1171,6 +1187,8 @@ Wardrobe.getStandardView = function (options) {
|
|||
preview_swf,
|
||||
update_pending_flash = false;
|
||||
|
||||
preview_el.removeClass('image-adapter').addClass('swf-adapter');
|
||||
|
||||
swfobject.embedSWF(
|
||||
options.Preview.swf_url,
|
||||
preview_swf_id,
|
||||
|
@ -1205,28 +1223,145 @@ Wardrobe.getStandardView = function (options) {
|
|||
}
|
||||
|
||||
function ImageAdapter() {
|
||||
var pendingAssets = {}, pendingAssetIds = [], pendingInterval,
|
||||
pendingAssetsCount = 0,
|
||||
pendingMessageEl = $('<span/>', {id: 'preview-images-pending'}),
|
||||
previewImageContainer = $(options.Preview.image_container);
|
||||
|
||||
var ASSET_PING_RATE = 5000;
|
||||
|
||||
preview_el.removeClass('swf-adapter').addClass('image-adapter');
|
||||
pendingMessageEl.appendTo(previewImageContainer);
|
||||
|
||||
this.updateAssets = function () {
|
||||
var assets = wardrobe.outfit.getVisibleAssets(), asset;
|
||||
var imagesPending = 0;
|
||||
var assets = wardrobe.outfit.getVisibleAssets(), asset,
|
||||
availableAssets = [];
|
||||
pendingAssets = {};
|
||||
pendingAssetsCount = 0;
|
||||
clearView();
|
||||
for(var i in assets) {
|
||||
if(!assets.hasOwnProperty(i)) continue;
|
||||
asset = assets[i];
|
||||
if(!asset.has_image) {
|
||||
assets[i].requestImageConversion();
|
||||
imagesPending++;
|
||||
if(asset.has_image) {
|
||||
addToView(asset);
|
||||
} else {
|
||||
pendingAssets[asset.id] = asset;
|
||||
pendingAssetsCount++;
|
||||
}
|
||||
}
|
||||
preview_swf_placeholder.text("Waiting on " + imagesPending + " images.");
|
||||
updatePendingStatus();
|
||||
}
|
||||
|
||||
function addToView(asset) {
|
||||
$(
|
||||
'<img/>',
|
||||
{
|
||||
css: {
|
||||
zIndex: asset.depth
|
||||
},
|
||||
src: asset.imageURL(bestSize())
|
||||
}
|
||||
).appendTo(previewImageContainer);
|
||||
}
|
||||
|
||||
// TODO: choose new best size on window resize
|
||||
function bestSize() {
|
||||
var sizes = Wardrobe.IMAGE_CONFIG.sizes,
|
||||
width = preview_el.width(), height = preview_el.height();
|
||||
for(var i in sizes) {
|
||||
if(sizes[i][0] < width && sizes[i][1] < height) return sizes[i];
|
||||
}
|
||||
return sizes[sizes.length - 1];
|
||||
}
|
||||
|
||||
function clearView() {
|
||||
previewImageContainer.children('img').remove();
|
||||
}
|
||||
|
||||
function loadPendingAssets() {
|
||||
var pendingAssetIds = {
|
||||
biology: [],
|
||||
object: []
|
||||
}, asset;
|
||||
for(var i in pendingAssets) {
|
||||
if(pendingAssets.hasOwnProperty(i)) {
|
||||
pendingAssetIds[pendingAssets[i].type].push(pendingAssets[i].id);
|
||||
}
|
||||
}
|
||||
$.getJSON(
|
||||
'/swf_assets.json',
|
||||
{
|
||||
ids: pendingAssetIds
|
||||
},
|
||||
function (assetsData) {
|
||||
var assetData, asset;
|
||||
for(var i in assetsData) {
|
||||
assetData = assetsData[i];
|
||||
if(assetData.has_image && pendingAssets.hasOwnProperty(assetData.id)) {
|
||||
asset = pendingAssets[assetData.id];
|
||||
asset.update(assetData);
|
||||
delete pendingAssets[assetData.id];
|
||||
pendingAssetsCount--;
|
||||
addToView(asset);
|
||||
}
|
||||
}
|
||||
updatePendingStatus();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function updateContainerSize() {
|
||||
var size = bestSize();
|
||||
previewImageContainer.css({
|
||||
height: size[1],
|
||||
width: size[0]
|
||||
});
|
||||
}
|
||||
|
||||
function updatePendingInterval() {
|
||||
if(pendingAssetsCount) {
|
||||
if(pendingInterval == null) {
|
||||
pendingInterval = setInterval(loadPendingAssets, ASSET_PING_RATE);
|
||||
}
|
||||
} else {
|
||||
if(pendingInterval != null) {
|
||||
clearInterval(pendingInterval);
|
||||
pendingInterval = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//this.adapter = new SWFAdapter();
|
||||
this.adapter = new ImageAdapter();
|
||||
function updatePendingMessage() {
|
||||
pendingMessageEl.text("Waiting on " + pendingAssetsCount + " images").
|
||||
attr("className", "waiting-on-" + pendingAssetsCount);
|
||||
}
|
||||
|
||||
function updatePendingStatus() {
|
||||
updatePendingInterval();
|
||||
updatePendingMessage();
|
||||
}
|
||||
|
||||
updateContainerSize();
|
||||
$(window).resize(updateContainerSize);
|
||||
}
|
||||
|
||||
this.adapter = new SWFAdapter();
|
||||
|
||||
function updateAssets() {
|
||||
preview.adapter.updateAssets();
|
||||
}
|
||||
|
||||
var updateAssets = $.proxy(this.adapter, 'updateAssets');
|
||||
wardrobe.outfit.bind('updateWornItems', updateAssets);
|
||||
wardrobe.outfit.bind('updateItemAssets', updateAssets);
|
||||
wardrobe.outfit.bind('updatePetState', updateAssets);
|
||||
|
||||
this.useSWFAdapter = function () { preview.adapter = new SWFAdapter(); updateAssets(); }
|
||||
this.useImageAdapter = function () { preview.adapter = new ImageAdapter(); updateAssets(); }
|
||||
this.toggleAdapter = function () {
|
||||
var nextAdapter = preview.adapter.constructor == SWFAdapter ? ImageAdapter : SWFAdapter;
|
||||
preview.adapter = new nextAdapter();
|
||||
updateAssets();
|
||||
}
|
||||
}
|
||||
|
||||
window.previewSWFIsReady = function (id) {
|
||||
|
|
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue