diff --git a/app/helpers/closet_hangers_helper.rb b/app/helpers/closet_hangers_helper.rb index 2e6a5296..81b954eb 100644 --- a/app/helpers/closet_hangers_helper.rb +++ b/app/helpers/closet_hangers_helper.rb @@ -56,9 +56,10 @@ module ClosetHangersHelper def render_unlisted_closet_hangers(owned) if @unlisted_closet_hangers_by_owned[owned] - content = render :partial => 'closet_hanger', + hangers_content = render :partial => 'closet_hanger', :collection => @unlisted_closet_hangers_by_owned[owned], :locals => {:show_controls => !public_perspective?} + content = content_tag(:div, hangers_content, :class => 'closet-list-hangers') if has_lists?(owned) content = content_tag(:header, content_tag(:h4, '(Not in a list)')) + content end diff --git a/app/stylesheets/closet_hangers/_index.sass b/app/stylesheets/closet_hangers/_index.sass index cda8d60b..80b96451 100644 --- a/app/stylesheets/closet_hangers/_index.sass +++ b/app/stylesheets/closet_hangers/_index.sass @@ -177,6 +177,26 @@ body.closet_hangers-index &:last-child border-bottom: 0 + &.droppable-active + +border-radius(1em) + +module + border-bottom-width: 1px + border-style: dotted + margin: 1em 0 + + .object + // totally hiding these elements causes the original element to change + // position, throwing off the drag + +opacity(.25) + &.ui-draggable-dragging + +opacity(1) + + .closet-list-controls + display: none + + .closet-list-hangers + overflow: hidden + .closet-hangers-group-autocomplete-item, .closet-list-autocomplete-item span font-style: italic diff --git a/app/views/closet_hangers/_closet_hanger.html.haml b/app/views/closet_hangers/_closet_hanger.html.haml index ad7d3bec..3e92dcba 100644 --- a/app/views/closet_hangers/_closet_hanger.html.haml +++ b/app/views/closet_hangers/_closet_hanger.html.haml @@ -6,6 +6,7 @@ - if show_controls = form_for closet_hanger, :url => user_item_closet_hanger_path(current_user, closet_hanger.item), :html => {:class => 'closet-hanger-update'} do |f| = return_to_field_tag + = f.hidden_field :list_id = f.hidden_field :owned = f.number_field :quantity, :min => 0, :required => true, :title => "You own #{pluralize closet_hanger.quantity, closet_hanger.item.name}" = f.submit "Save" diff --git a/app/views/closet_hangers/index.html.haml b/app/views/closet_hangers/index.html.haml index 8426ba9d..7a1b84f7 100644 --- a/app/views/closet_hangers/index.html.haml +++ b/app/views/closet_hangers/index.html.haml @@ -31,22 +31,33 @@ = f.submit "Save" %span#cancel-contact-link cancel -%p - These are the items you are tracking on Dress to Impress. Hover over an - item to remove it from the list or to change the quantity. +- unless public_perspective? + %p + These are the items you are tracking on Dress to Impress. Hover over an + item to remove it from the list or to change the quantity. -%p - You can share - = link_to "this page", request.fullpath - with the world, and they'll be able to see what items you own and want. - It's also a good idea to - %span.edit-contact-link add your Neopets username - so that when other users see your items they will know how to contact you for - trades. + %p + %strong + You can share + = link_to "this page", request.fullpath + with the world, + and they'll be able to see what items you own and want. + It's also a good idea to + %span.edit-contact-link add your Neopets username + so that when other users see your items they will know how to contact you for + trades. + + %p + You can also make lists of items. + %strong + = link_to 'Get started with an Up For Trade list', + new_user_closet_list_path(@user, :closet_list => {:hangers_owned => true, :name => 'Up For Trade'}) + then start adding to it! You can use the search form at the top to find + items, and you can also drag-and-drop items in and out of lists. Enjoy! #closet-hangers{:class => public_perspective? ? nil : 'current-user'} - [true, false].each do |owned| - .closet-hangers-group{'data-owned' => owned.to_s} + .closet-hangers-group{'data-owned' => owned.to_s, :id => "closet-hangers-group-#{owned}"} %header %h3 Items #{closet_hanger_subject} #{closet_hanger_verb(owned)} diff --git a/app/views/closet_lists/_closet_list.html.haml b/app/views/closet_lists/_closet_list.html.haml index 5da0a035..7b67f5b4 100644 --- a/app/views/closet_lists/_closet_list.html.haml +++ b/app/views/closet_lists/_closet_list.html.haml @@ -1,4 +1,4 @@ -.closet-list{'data-id' => closet_list.id} +.closet-list{'data-id' => closet_list.id, :id => "closet-list-#{closet_list.id}"} %header - if show_controls .closet-list-controls @@ -9,8 +9,10 @@ - if closet_list.description? = closet_list_description_format closet_list - - unless closet_list.hangers.empty? - = render_sorted_hangers(closet_list, show_controls) - - else - %span.empty-list This list is empty. + + .closet-list-hangers + - unless closet_list.hangers.empty? + = render_sorted_hangers(closet_list, show_controls) + - else + %span.empty-list This list is empty. diff --git a/app/views/items/_item_link.html.haml b/app/views/items/_item_link.html.haml index ed1fd79f..e9f95056 100644 --- a/app/views/items/_item_link.html.haml +++ b/app/views/items/_item_link.html.haml @@ -1,6 +1,6 @@ = link_to item_path(item, :q => @query) do = image_tag item.thumbnail_url, :alt => item.description, :title => item.description - = item.name + %span.name= item.name = nc_icon_for(item) = closeted_icons_for(item) diff --git a/public/javascripts/closet_hangers/index.js b/public/javascripts/closet_hangers/index.js index 74047135..5e1803f0 100644 --- a/public/javascripts/closet_hangers/index.js +++ b/public/javascripts/closet_hangers/index.js @@ -1,4 +1,16 @@ (function () { + var hangersInitCallbacks = []; + + function onHangersInit(callback) { + hangersInitCallbacks[hangersInitCallbacks.length] = callback; + } + + function hangersInit() { + for(var i = 0; i < hangersInitCallbacks.length; i++) { + hangersInitCallbacks[i](); + } + } + /* Hanger groups @@ -39,6 +51,14 @@ */ + $.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; @@ -54,23 +74,36 @@ } $.fn.hasChanged = function () { - return this.data('previousValue') != this.val(); + return this.attr('data-previous-value') != this.val(); } $.fn.revertValue = function () { return this.each(function () { var el = $(this); - el.val(el.data('previousValue')); + el.val(el.attr('data-previous-value')); }); } $.fn.storeValue = function () { return this.each(function () { var el = $(this); - el.data('previousValue', el.val()); + 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); @@ -89,24 +122,41 @@ objectWrapper.hide(250); } + function compareItemsByName(a, b) { + return a.find('span.name').text().localeCompare(b.find('span.name').text()); + } + + function moveItemToList(item, listId) { + if(listId) { + var list = $('#closet-list-' + listId); + } else { + var list = item.closest('.closet-hangers-group').find('div.closet-list.unlisted'); + } + var hangersWrapper = list.find('div.closet-list-hangers'); + item.insertIntoSortedList(hangersWrapper, compareItemsByName); + } + function submitUpdateForm(form) { if(form.data('loading')) return false; - var input = form.children("input[type=number]"); - if(input.hasChanged()) { + 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 = input.val(); - var span = objectWrapper.find("span").text(newQuantity); - span.parent().attr('class', 'quantity quantity-' + newQuantity); + var newQuantity = quantityEl.val(); + var quantitySpan = objectWrapper.find(".quantity span").text(newQuantity); + quantitySpan.parent().attr('class', 'quantity 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(input.val() == 0) { + if(quantityEl.val() == 0) { objectRemoved(objectWrapper); } else { objectWrapper.removeClass("loading").enableForms(); @@ -114,11 +164,14 @@ form.data('loading', false); }, success: function () { - input.storeValue(); + quantityEl.storeValue(); + listEl.storeValue(); }, error: function (xhr) { - input.revertValue(); - span.text(input.val()); + quantityEl.revertValue(); + listEl.revertValue(); + if(listChanged) moveItemToList(objectWrapper, listEl.val()); + quantitySpan.text(quantityEl.val()); handleSaveError(xhr, "updating the quantity"); } @@ -131,14 +184,23 @@ submitUpdateForm($(this)); }); - function quantityInputs() { return $(hangersElQuery + ' input[type=number]') } + function editableInputs() { return $(hangersElQuery).find('input[name=closet_hanger\[quantity\]], input[name=closet_hanger\[list_id\]]') } - quantityInputs().live('change', function () { + $(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) { @@ -205,7 +267,7 @@ success: function (html) { var doc = $(html); hangersEl.html( doc.find('#closet-hangers').html() ); - quantityInputs().storeValue(); // since all the quantity inputs are new, gotta store initial value again + hangersInit(); doc.find('.flash').hide().insertBefore(hangersEl).show(500).delay(5000).hide(250); itemsSearchField.val(""); }, @@ -354,5 +416,36 @@ $('input[type=submit][data-confirm]').live('click', function (e) { if(!confirm(this.getAttribute('data-confirm'))) e.preventDefault(); }); + + /* + Closet list droppable + */ + + onHangersInit(function () { + $('.closet-hangers-group').each(function () { + var group = $(this); + group.find('div.closet-list').droppable({ + accept: '#' + group.attr('id') + ' div.object', + activate: function () { + $(this).find('.closet-list-hangers').animate({opacity: 0, height: 100}, 250); + }, + activeClass: 'droppable-active', + deactivate: function () { + $(this).find('.closet-list-hangers').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')); + submitUpdateForm(form); + } + }); + }); + }); + + /* + Initialize + */ + + hangersInit(); })(); diff --git a/public/javascripts/jquery.ui.js b/public/javascripts/jquery.ui.js index dad0f2e5..ee5f624e 100644 --- a/public/javascripts/jquery.ui.js +++ b/public/javascripts/jquery.ui.js @@ -30,6 +30,23 @@ e?this.each(function(){var g=b.data(this,a),i=g&&b.isFunction(g[d])?g[d].apply(g this._getCreateOptions(),a);var d=this;this.element.bind("remove."+this.widgetName,function(){d.destroy()});this._create();this._trigger("create");this._init()},_getCreateOptions:function(){return b.metadata&&b.metadata.get(this.element[0])[this.widgetName]},_create:function(){},_init:function(){},destroy:function(){this.element.unbind("."+this.widgetName).removeData(this.widgetName);this.widget().unbind("."+this.widgetName).removeAttr("aria-disabled").removeClass(this.widgetBaseClass+"-disabled ui-state-disabled")}, widget:function(){return this.element},option:function(a,c){var d=a;if(arguments.length===0)return b.extend({},this.options);if(typeof a==="string"){if(c===j)return this.options[a];d={};d[a]=c}this._setOptions(d);return this},_setOptions:function(a){var c=this;b.each(a,function(d,e){c._setOption(d,e)});return this},_setOption:function(a,c){this.options[a]=c;if(a==="disabled")this.widget()[c?"addClass":"removeClass"](this.widgetBaseClass+"-disabled ui-state-disabled").attr("aria-disabled",c);return this}, enable:function(){return this._setOption("disabled",false)},disable:function(){return this._setOption("disabled",true)},_trigger:function(a,c,d){var e=this.options[a];c=b.Event(c);c.type=(a===this.widgetEventPrefix?a:this.widgetEventPrefix+a).toLowerCase();d=d||{};if(c.originalEvent){a=b.event.props.length;for(var f;a;){f=b.event.props[--a];c[f]=c.originalEvent[f]}}this.element.trigger(c,d);return!(b.isFunction(e)&&e.call(this.element[0],c,d)===false||c.isDefaultPrevented())}}})(jQuery); +;/*! + * jQuery UI Mouse 1.8.14 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Mouse + * + * Depends: + * jquery.ui.widget.js + */ +(function(b){var d=false;b(document).mousedown(function(){d=false});b.widget("ui.mouse",{options:{cancel:":input,option",distance:1,delay:0},_mouseInit:function(){var a=this;this.element.bind("mousedown."+this.widgetName,function(c){return a._mouseDown(c)}).bind("click."+this.widgetName,function(c){if(true===b.data(c.target,a.widgetName+".preventClickEvent")){b.removeData(c.target,a.widgetName+".preventClickEvent");c.stopImmediatePropagation();return false}});this.started=false},_mouseDestroy:function(){this.element.unbind("."+ +this.widgetName)},_mouseDown:function(a){if(!d){this._mouseStarted&&this._mouseUp(a);this._mouseDownEvent=a;var c=this,f=a.which==1,g=typeof this.options.cancel=="string"?b(a.target).closest(this.options.cancel).length:false;if(!f||g||!this._mouseCapture(a))return true;this.mouseDelayMet=!this.options.delay;if(!this.mouseDelayMet)this._mouseDelayTimer=setTimeout(function(){c.mouseDelayMet=true},this.options.delay);if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a)){this._mouseStarted=this._mouseStart(a)!== +false;if(!this._mouseStarted){a.preventDefault();return true}}true===b.data(a.target,this.widgetName+".preventClickEvent")&&b.removeData(a.target,this.widgetName+".preventClickEvent");this._mouseMoveDelegate=function(e){return c._mouseMove(e)};this._mouseUpDelegate=function(e){return c._mouseUp(e)};b(document).bind("mousemove."+this.widgetName,this._mouseMoveDelegate).bind("mouseup."+this.widgetName,this._mouseUpDelegate);a.preventDefault();return d=true}},_mouseMove:function(a){if(b.browser.msie&& +!(document.documentMode>=9)&&!a.button)return this._mouseUp(a);if(this._mouseStarted){this._mouseDrag(a);return a.preventDefault()}if(this._mouseDistanceMet(a)&&this._mouseDelayMet(a))(this._mouseStarted=this._mouseStart(this._mouseDownEvent,a)!==false)?this._mouseDrag(a):this._mouseUp(a);return!this._mouseStarted},_mouseUp:function(a){b(document).unbind("mousemove."+this.widgetName,this._mouseMoveDelegate).unbind("mouseup."+this.widgetName,this._mouseUpDelegate);if(this._mouseStarted){this._mouseStarted= +false;a.target==this._mouseDownEvent.target&&b.data(a.target,this.widgetName+".preventClickEvent",true);this._mouseStop(a)}return false},_mouseDistanceMet:function(a){return Math.max(Math.abs(this._mouseDownEvent.pageX-a.pageX),Math.abs(this._mouseDownEvent.pageY-a.pageY))>=this.options.distance},_mouseDelayMet:function(){return this.mouseDelayMet},_mouseStart:function(){},_mouseDrag:function(){},_mouseStop:function(){},_mouseCapture:function(){return true}})})(jQuery); ;/* * jQuery UI Position 1.8.14 * @@ -46,6 +63,83 @@ m/2;i.left=Math.round(i.left);i.top=Math.round(i.top);r={left:i.left-p,top:i.top d>0?b.left-d:Math.max(b.left-a.collisionPosition.left,b.left)},top:function(b,a){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();b.top=d>0?b.top-d:Math.max(b.top-a.collisionPosition.top,b.top)}},flip:{left:function(b,a){if(a.at[0]!=="center"){var d=c(window);d=a.collisionPosition.left+a.collisionWidth-d.width()-d.scrollLeft();var g=a.my[0]==="left"?-a.elemWidth:a.my[0]==="right"?a.elemWidth:0,e=a.at[0]==="left"?a.targetWidth:-a.targetWidth,h=-2*a.offset[0];b.left+= a.collisionPosition.left<0?g+e+h:d>0?g+e+h:0}},top:function(b,a){if(a.at[1]!=="center"){var d=c(window);d=a.collisionPosition.top+a.collisionHeight-d.height()-d.scrollTop();var g=a.my[1]==="top"?-a.elemHeight:a.my[1]==="bottom"?a.elemHeight:0,e=a.at[1]==="top"?a.targetHeight:-a.targetHeight,h=-2*a.offset[1];b.top+=a.collisionPosition.top<0?g+e+h:d>0?g+e+h:0}}}};if(!c.offset.setOffset){c.offset.setOffset=function(b,a){if(/static/.test(c.curCSS(b,"position")))b.style.position="relative";var d=c(b), g=d.offset(),e=parseInt(c.curCSS(b,"top",true),10)||0,h=parseInt(c.curCSS(b,"left",true),10)||0;g={top:a.top-g.top+e,left:a.left-g.left+h};"using"in a?a.using.call(b,g):d.css(g)};c.fn.offset=function(b){var a=this[0];if(!a||!a.ownerDocument)return null;if(b)return this.each(function(){c.offset.setOffset(this,b)});return u.call(this)}}})(jQuery); +;/* + * jQuery UI Draggable 1.8.14 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Draggables + * + * Depends: + * jquery.ui.core.js + * jquery.ui.mouse.js + * jquery.ui.widget.js + */ +(function(d){d.widget("ui.draggable",d.ui.mouse,{widgetEventPrefix:"drag",options:{addClasses:true,appendTo:"parent",axis:false,connectToSortable:false,containment:false,cursor:"auto",cursorAt:false,grid:false,handle:false,helper:"original",iframeFix:false,opacity:false,refreshPositions:false,revert:false,revertDuration:500,scope:"default",scroll:true,scrollSensitivity:20,scrollSpeed:20,snap:false,snapMode:"both",snapTolerance:20,stack:false,zIndex:false},_create:function(){if(this.options.helper== +"original"&&!/^(?:r|a|f)/.test(this.element.css("position")))this.element[0].style.position="relative";this.options.addClasses&&this.element.addClass("ui-draggable");this.options.disabled&&this.element.addClass("ui-draggable-disabled");this._mouseInit()},destroy:function(){if(this.element.data("draggable")){this.element.removeData("draggable").unbind(".draggable").removeClass("ui-draggable ui-draggable-dragging ui-draggable-disabled");this._mouseDestroy();return this}},_mouseCapture:function(a){var b= +this.options;if(this.helper||b.disabled||d(a.target).is(".ui-resizable-handle"))return false;this.handle=this._getHandle(a);if(!this.handle)return false;d(b.iframeFix===true?"iframe":b.iframeFix).each(function(){d('
').css({width:this.offsetWidth+"px",height:this.offsetHeight+"px",position:"absolute",opacity:"0.001",zIndex:1E3}).css(d(this).offset()).appendTo("body")});return true},_mouseStart:function(a){var b=this.options;this.helper= +this._createHelper(a);this._cacheHelperProportions();if(d.ui.ddmanager)d.ui.ddmanager.current=this;this._cacheMargins();this.cssPosition=this.helper.css("position");this.scrollParent=this.helper.scrollParent();this.offset=this.positionAbs=this.element.offset();this.offset={top:this.offset.top-this.margins.top,left:this.offset.left-this.margins.left};d.extend(this.offset,{click:{left:a.pageX-this.offset.left,top:a.pageY-this.offset.top},parent:this._getParentOffset(),relative:this._getRelativeOffset()}); +this.originalPosition=this.position=this._generatePosition(a);this.originalPageX=a.pageX;this.originalPageY=a.pageY;b.cursorAt&&this._adjustOffsetFromHelper(b.cursorAt);b.containment&&this._setContainment();if(this._trigger("start",a)===false){this._clear();return false}this._cacheHelperProportions();d.ui.ddmanager&&!b.dropBehaviour&&d.ui.ddmanager.prepareOffsets(this,a);this.helper.addClass("ui-draggable-dragging");this._mouseDrag(a,true);d.ui.ddmanager&&d.ui.ddmanager.dragStart(this,a);return true}, +_mouseDrag:function(a,b){this.position=this._generatePosition(a);this.positionAbs=this._convertPositionTo("absolute");if(!b){b=this._uiHash();if(this._trigger("drag",a,b)===false){this._mouseUp({});return false}this.position=b.position}if(!this.options.axis||this.options.axis!="y")this.helper[0].style.left=this.position.left+"px";if(!this.options.axis||this.options.axis!="x")this.helper[0].style.top=this.position.top+"px";d.ui.ddmanager&&d.ui.ddmanager.drag(this,a);return false},_mouseStop:function(a){var b= +false;if(d.ui.ddmanager&&!this.options.dropBehaviour)b=d.ui.ddmanager.drop(this,a);if(this.dropped){b=this.dropped;this.dropped=false}if((!this.element[0]||!this.element[0].parentNode)&&this.options.helper=="original")return false;if(this.options.revert=="invalid"&&!b||this.options.revert=="valid"&&b||this.options.revert===true||d.isFunction(this.options.revert)&&this.options.revert.call(this.element,b)){var c=this;d(this.helper).animate(this.originalPosition,parseInt(this.options.revertDuration, +10),function(){c._trigger("stop",a)!==false&&c._clear()})}else this._trigger("stop",a)!==false&&this._clear();return false},_mouseUp:function(a){this.options.iframeFix===true&&d("div.ui-draggable-iframeFix").each(function(){this.parentNode.removeChild(this)});d.ui.ddmanager&&d.ui.ddmanager.dragStop(this,a);return d.ui.mouse.prototype._mouseUp.call(this,a)},cancel:function(){this.helper.is(".ui-draggable-dragging")?this._mouseUp({}):this._clear();return this},_getHandle:function(a){var b=!this.options.handle|| +!d(this.options.handle,this.element).length?true:false;d(this.options.handle,this.element).find("*").andSelf().each(function(){if(this==a.target)b=true});return b},_createHelper:function(a){var b=this.options;a=d.isFunction(b.helper)?d(b.helper.apply(this.element[0],[a])):b.helper=="clone"?this.element.clone().removeAttr("id"):this.element;a.parents("body").length||a.appendTo(b.appendTo=="parent"?this.element[0].parentNode:b.appendTo);a[0]!=this.element[0]&&!/(fixed|absolute)/.test(a.css("position"))&& +a.css("position","absolute");return a},_adjustOffsetFromHelper:function(a){if(typeof a=="string")a=a.split(" ");if(d.isArray(a))a={left:+a[0],top:+a[1]||0};if("left"in a)this.offset.click.left=a.left+this.margins.left;if("right"in a)this.offset.click.left=this.helperProportions.width-a.right+this.margins.left;if("top"in a)this.offset.click.top=a.top+this.margins.top;if("bottom"in a)this.offset.click.top=this.helperProportions.height-a.bottom+this.margins.top},_getParentOffset:function(){this.offsetParent= +this.helper.offsetParent();var a=this.offsetParent.offset();if(this.cssPosition=="absolute"&&this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0])){a.left+=this.scrollParent.scrollLeft();a.top+=this.scrollParent.scrollTop()}if(this.offsetParent[0]==document.body||this.offsetParent[0].tagName&&this.offsetParent[0].tagName.toLowerCase()=="html"&&d.browser.msie)a={top:0,left:0};return{top:a.top+(parseInt(this.offsetParent.css("borderTopWidth"),10)||0),left:a.left+(parseInt(this.offsetParent.css("borderLeftWidth"), +10)||0)}},_getRelativeOffset:function(){if(this.cssPosition=="relative"){var a=this.element.position();return{top:a.top-(parseInt(this.helper.css("top"),10)||0)+this.scrollParent.scrollTop(),left:a.left-(parseInt(this.helper.css("left"),10)||0)+this.scrollParent.scrollLeft()}}else return{top:0,left:0}},_cacheMargins:function(){this.margins={left:parseInt(this.element.css("marginLeft"),10)||0,top:parseInt(this.element.css("marginTop"),10)||0,right:parseInt(this.element.css("marginRight"),10)||0,bottom:parseInt(this.element.css("marginBottom"), +10)||0}},_cacheHelperProportions:function(){this.helperProportions={width:this.helper.outerWidth(),height:this.helper.outerHeight()}},_setContainment:function(){var a=this.options;if(a.containment=="parent")a.containment=this.helper[0].parentNode;if(a.containment=="document"||a.containment=="window")this.containment=[a.containment=="document"?0:d(window).scrollLeft()-this.offset.relative.left-this.offset.parent.left,a.containment=="document"?0:d(window).scrollTop()-this.offset.relative.top-this.offset.parent.top, +(a.containment=="document"?0:d(window).scrollLeft())+d(a.containment=="document"?document:window).width()-this.helperProportions.width-this.margins.left,(a.containment=="document"?0:d(window).scrollTop())+(d(a.containment=="document"?document:window).height()||document.body.parentNode.scrollHeight)-this.helperProportions.height-this.margins.top];if(!/^(document|window|parent)$/.test(a.containment)&&a.containment.constructor!=Array){a=d(a.containment);var b=a[0];if(b){a.offset();var c=d(b).css("overflow")!= +"hidden";this.containment=[(parseInt(d(b).css("borderLeftWidth"),10)||0)+(parseInt(d(b).css("paddingLeft"),10)||0),(parseInt(d(b).css("borderTopWidth"),10)||0)+(parseInt(d(b).css("paddingTop"),10)||0),(c?Math.max(b.scrollWidth,b.offsetWidth):b.offsetWidth)-(parseInt(d(b).css("borderLeftWidth"),10)||0)-(parseInt(d(b).css("paddingRight"),10)||0)-this.helperProportions.width-this.margins.left-this.margins.right,(c?Math.max(b.scrollHeight,b.offsetHeight):b.offsetHeight)-(parseInt(d(b).css("borderTopWidth"), +10)||0)-(parseInt(d(b).css("paddingBottom"),10)||0)-this.helperProportions.height-this.margins.top-this.margins.bottom];this.relative_container=a}}else if(a.containment.constructor==Array)this.containment=a.containment},_convertPositionTo:function(a,b){if(!b)b=this.position;a=a=="absolute"?1:-1;var c=this.cssPosition=="absolute"&&!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName);return{top:b.top+ +this.offset.relative.top*a+this.offset.parent.top*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollTop():f?0:c.scrollTop())*a),left:b.left+this.offset.relative.left*a+this.offset.parent.left*a-(d.browser.safari&&d.browser.version<526&&this.cssPosition=="fixed"?0:(this.cssPosition=="fixed"?-this.scrollParent.scrollLeft():f?0:c.scrollLeft())*a)}},_generatePosition:function(a){var b=this.options,c=this.cssPosition=="absolute"&& +!(this.scrollParent[0]!=document&&d.ui.contains(this.scrollParent[0],this.offsetParent[0]))?this.offsetParent:this.scrollParent,f=/(html|body)/i.test(c[0].tagName),e=a.pageX,h=a.pageY;if(this.originalPosition){var g;if(this.containment){if(this.relative_container){g=this.relative_container.offset();g=[this.containment[0]+g.left,this.containment[1]+g.top,this.containment[2]+g.left,this.containment[3]+g.top]}else g=this.containment;if(a.pageX-this.offset.click.leftg[2])e=g[2]+this.offset.click.left;if(a.pageY-this.offset.click.top>g[3])h=g[3]+this.offset.click.top}if(b.grid){h=b.grid[1]?this.originalPageY+Math.round((h-this.originalPageY)/b.grid[1])*b.grid[1]:this.originalPageY;h=g?!(h-this.offset.click.topg[3])?h:!(h-this.offset.click.topg[2])?e:!(e-this.offset.click.left=0;i--){var j=c.snapElements[i].left,l=j+c.snapElements[i].width,k=c.snapElements[i].top,m=k+c.snapElements[i].height;if(j-e=j&&f<=l||h>=j&&h<=l||fl)&&(e>= +i&&e<=k||g>=i&&g<=k||ek);default:return false}};d.ui.ddmanager={current:null,droppables:{"default":[]},prepareOffsets:function(a,b){var c=d.ui.ddmanager.droppables[a.options.scope]||[],e=b?b.type:null,g=(a.currentItem||a.element).find(":data(droppable)").andSelf(),f=0;a:for(;f