Sortable.js рдЖрдзреБрдирд┐рдХ рдмреНрд░рд╛рдЙрдЬрд╝рд░реЛрдВ рдФрд░ рд╕реНрдкрд░реНрд╢ рдЙрдкрдХрд░рдгреЛрдВ рдХреЗ рд▓рд┐рдП рдПрдХ рдиреНрдпреВрдирддрдо рдкреБрд╕реНрддрдХрд╛рд▓рдп рд╣реИ рдЬрд┐рд╕рдореЗрдВ jQuery рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рд╣реЛрддреА рд╣реИред
рдЬреИрд╕рд╛ рдХрд┐ рдЖрдк рдирд╛рдо рд╕реЗ рдЕрдиреБрдорд╛рди рд▓рдЧрд╛ рд╕рдХрддреЗ рд╣реИрдВ, рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдХреЛ drag'n'drop рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЖрдЗрдЯрдо рд╕реЙрд░реНрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдбрд┐рдЬрд╝рд╛рдЗрди рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред рдРрд╕реЗ рдорд╛рдорд▓реЛрдВ рдореЗрдВ рдорд╛рдирдХ рд╕рдорд╛рдзрд╛рди
jQuery UI /
Sortable рд╣реИ , рдЬреЛ рди рддреЛ рдЕрдзрд┐рдХ рд╣реИ рдФрд░ рди рд╣реА 64 kb + 10 kb рд╕реЗ рдХрдо рд╣реИред рдХреБрд▓ 75 kb рдПрдХ рдкрд░рд┐рдпреЛрдЬрдирд╛ рдореЗрдВ gzipped рд╣реИ рдЬрд╣рд╛рдБ jQuery рдХрд╛ рдЙрдкрдпреЛрдЧ рдмрд┐рд▓реНрдХреБрд▓ рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдЕрдкреЗрдХреНрд╖рд╛рдХреГрдд рд╣рд╛рд▓ рд╣реА рдореЗрдВ Habr├й рдкрд░ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдЗрд╕реА рддрд░рд╣ рдХреА рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рдХреЛ рд▓рд╛рдЧреВ рдХрд░рдиреЗ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдПрдХ
рд▓реЗрдЦ рдерд╛, рд▓реЗрдХрд┐рди рдлрд┐рд░ рд╕реЗ рдкреНрд░рд╕реНрддрд╛рд╡рд┐рдд рд╕рдорд╛рдзрд╛рди рдореЗрдВ jQuery рдФрд░ рд╕реНрдкрд░реНрд╢ рдЙрдкрдХрд░рдгреЛрдВ рдХрд╛ рд╕рдорд░реНрдерди рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИред
рд╡рдЬрди рдХреЗ рд╕рд╛рде рд╕рдорд╕реНрдпрд╛рдУрдВ рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдореБрдЭреЗ рдорд┐рд▓реА рд╕рднреА рд▓рд╛рдЗрдмреНрд░реЗрд░реА рдЧрддрд┐рд╢реАрд▓ рд░реВрдк рд╕реЗ рдмрджрд▓рддреА рд╕реВрдЪреА рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдореЗрдВ рд╕рдХреНрд╖рдо рдирд╣реАрдВ рдереАрдВред рдкреНрд▓рдЧрдЗрди рдЗрдирд┐рд╢рд┐рдпрд▓рд╛рдЗрдЬрд╝реЗрд╢рди рдХреЗ рд╕рдордп, рдЙрдиреНрд╣реЛрдВрдиреЗ рд╕рднреА рддрддреНрд╡реЛрдВ рдХреА рд╕реНрдерд┐рддрд┐ рдирд┐рд░реНрдзрд╛рд░рд┐рдд рдХреА, рдФрд░ рдЙрдиреНрд╣реЗрдВ рдЕрдкрдбреЗрдЯ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдкреНрд▓рдЧрдЗрди рдХреЛ рдлрд┐рд░ рд╕реЗ рд╕рдВрдЧрдард┐рдд рдХрд░рдирд╛ рдпрд╛
$('...').sortable('refresh')
рдХреЛ рд╕реЙрд░реНрдЯ рдХрд░рдирд╛ рдЖрд╡рд╢реНрдпрдХ рдерд╛
$('...').sortable('refresh')
рд╡рд┐рдзрд┐, рдЬреЛ рдмрд╣реБрдд рдЕрд╕реБрд╡рд┐рдзрд╛рдЬрдирдХ рд╣реИред
рдЪреВрдВрдХрд┐ рдореЗрд░реЗ рдХрд╛рд░реНрдп рдХреЛ рдкреБрд░рд╛рдиреЗ рдмреНрд░рд╛рдЙрдЬрд╝рд░реЛрдВ рдХреЗ рд╕рдорд░реНрдерди рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рдирд╣реАрдВ рдереА, рдЗрд╕рд▓рд┐рдП рдореИрдВрдиреЗ
HTML5 Drag'n'Drop рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рд╢реБрджреНрдз рдЬреЗрдПрд╕ рдореЗрдВ рдЖрд╡рд╢реНрдпрдХ рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рдмрдирд╛рдиреЗ рдХреА рдХреЛрд╢рд┐рд╢ рдХрд░рдиреЗ рдХрд╛ рдлреИрд╕рд▓рд╛ рдХрд┐рдпрд╛ред
рдЗрд╕ рд╡рд┐рд╖рдп рдкрд░ рд▓реЗрдЦ рдкрдврд╝рдиреЗ рдХреЗ рдмрд╛рдж, рдпрд╣ рдкрддрд╛ рдЪрд▓рд╛ рд╣реИ рдХрд┐ рдЕрдм рдЗрд╕ рддрд░рд╣ рдХреЗ рдХрд╛рд░реНрдпрд╛рддреНрдордХ рдмрдирд╛рдирд╛ рдмрд╣реБрдд рдЖрд╕рд╛рди рд╣реИ, рдЖрдк
25 рдкрдВрдХреНрддрд┐рдпреЛрдВ рдХреЛ рднреА рдкреВрд░рд╛ рдХрд░ рд╕рдХрддреЗ
рд╣реИрдВ (рдпрджрд┐ рдЖрдк рдЯрд┐рдкреНрдкрдгрд┐рдпреЛрдВ рдФрд░ рдмреАрдЯреНрд╕ рдХреЛ рд╣рдЯрд╛рддреЗ рд╣реИрдВ):
http://jsfiddle.net/RubaXa/zLq5J/ function sortable(rootEl, onUpdate){ var dragEl;
рдЬреИрд╕рд╛ рдХрд┐ рдЖрдк рдХреЛрдб рд╕реЗ рджреЗрдЦ рд╕рдХрддреЗ рд╣реИрдВ, рдкреВрд░реА рдЫрдБрдЯрд╛рдИ рдореЗрдВ рдХреЗрд╡рд▓
rootEl.insertBefore(dragEl, target.nextSibling || target)
рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ
rootEl.insertBefore(dragEl, target.nextSibling || target)
, рдЬрд╣рд╛рдБ
target
рд╡рд╣ рддрддреНрд╡ рд╣реИ рдЬрд┐рд╕реЗ
rootEl.insertBefore(dragEl, target.nextSibling || target)
ред рдпрджрд┐ рдЖрдкрдиреЗ рдкрд╣рд▓реЗ рд╣реА рдЙрджрд╛рд╣рд░рдг рдХрд╛ рдкрд░реАрдХреНрд╖рдг рдХрд░ рд▓рд┐рдпрд╛ рд╣реИ, рддреЛ рд╕рдВрднрд╡рддрдГ рдЖрдкрдиреЗ рджреЗрдЦрд╛ рд╣реИ рдХрд┐ рдЖрдк рдХрд┐рд╕реА рддрддреНрд╡ рдХреЛ рдкрд╣рд▓реА рд╕реНрдерд┐рддрд┐ рдореЗрдВ рдирд╣реАрдВ рдЦреАрдВрдЪ рд╕рдХрддреЗред рд╡рд┐рдзрд┐ рдХреА рдПрдХ рдФрд░ рдмрд╛рд░реАрдХрд┐рдпреЛрдВ -
onUpdate
рдХреЛ рд╣рд░ рдмрд╛рд░ рдХрд╣рд╛ рдЬрд╛рддрд╛ рд╣реИ, рднрд▓реЗ рд╣реА рдЖрдЗрдЯрдо рдХреЛ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдирд╣реАрдВ рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реЛред
рдкрд╣рд▓реА рд╕рдорд╕реНрдпрд╛ рд╕реЗ рдЫреБрдЯрдХрд╛рд░рд╛ рдкрд╛рдиреЗ рдХреЗ рд▓рд┐рдП, рдмрд╕ рдЫрдВрдЯрд╛рдИ рдХреЗ рджреМрд░рд╛рди рдПрдХ рдЬрд╛рдВрдЪ рдЬреЛрдбрд╝реЗрдВред рдХрд┐рд╕реА рддрддреНрд╡
target.nextSibling
рдмрд╛рдж
target.nextSibling
рдХреЗрд╡рд▓ рдЗрд╕ рд╕реВрдЪреА рдХрд╛ рдкрд╣рд▓рд╛ рддрддреНрд╡ рдирд╣реАрдВ рд╣реИред
http://jsfiddle.net/RubaXa/zLq5J/3/ if( target && target !== dragEl && target.nodeName == 'LI' ){
рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдмрд╕
nextEl = dragEl.nextSibling
рдХреЗ рд╕рдордп рдЕрдЧрд▓реЗ рддрддреНрд╡ (
nextEl = dragEl.nextSibling
) рдХреЗ рд▓рд┐рдВрдХ рдХреЛ рд╕рд╣реЗрдЬрдиреЗ рд╕реЗ рдЖрдкрдХреЛ рджреВрд╕рд░реА рд╕рдорд╕реНрдпрд╛ (http://jsfiddle.net/RubaXa/zdq5J/4/29 рдФрд░ рд▓рд╛рдЗрди 38) рд╕реЗ рдЫреБрдЯрдХрд╛рд░рд╛ рдорд┐рд▓ рд╕рдХрддрд╛ рд╣реИред
рдкрд╣рд▓реА рдирдЬрд╝рд░ рдореЗрдВ, рд╕рдм рдХреБрдЫ рдЕрдЪреНрдЫрд╛ рд▓рдЧ рд░рд╣рд╛ рд╣реИ, рдпрд╣ рдХреЙрдореНрдкреИрдХреНрдЯ рдФрд░ рд╕реНрдкрд╖реНрдЯ рдХреЛрдб рд╣реИ рдЬреЛ
рдЕрдзрд┐рдХрд╛рдВрд╢ рдмреНрд░рд╛рдЙрдЬрд╝рд░ рдХрд╛ рд╕рдорд░реНрдерди рдХрд░рддрд╛ рд╣реИ, рдФрд░ рдпрджрд┐ рдЖрдк
attachEvent
рд╕рдорд░реНрдерди рдЬреЛрдбрд╝рддреЗ рд╣реИрдВ рдФрд░
dragEl.classList.add/remove
рд╣рдЯрд╛ рджреЗрдВ
dragEl.classList.add/remove
, рдХреЛрдб IE5.5 рдореЗрдВ рднреА рдХрд╛рдо рдХрд░реЗрдЧрд╛:]
рд▓реЗрдХрд┐рди, рдпрджрд┐ рд╣рдо
рдЙрджрд╛рд╣рд░рдг рдХреЛ рдереЛрдбрд╝рд╛ рдмрджрд▓рддреЗ рд╣реИрдВ, рддреЛ рдмрд╕ рд╕реВрдЪреА рдХреА рд╡рд╕реНрддреБрдУрдВ рдХреА рдКрдВрдЪрд╛рдИ рдмрдврд╝рд╛рддреЗ рд╣реБрдП, рд╣рдореЗрдВ рддреАрд╕рд░реА рд╕рдорд╕реНрдпрд╛ рдорд┐рд▓рддреА рд╣реИред рдЫрдВрдЯрдиреА рд╕рд╛рдорд╛рдиреНрдп рд░реВрдк рд╕реЗ рдКрдкрд░ рд╕реЗ рдиреАрдЪреЗ рддрдХ рдХрд╛рдо рдХрд░рддреА рд╣реИ, рд▓реЗрдХрд┐рди рдЗрд╕рдХреЗ рд╡рд┐рдкрд░реАрдд - рдпрд╣ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рдЦрд░рд╛рдм рд╣реИред рдЗрд╕рд▓рд┐рдП, рддрддреНрд╡ рдХреЗ рд╕рдореНрдорд┐рд▓рди, "рдкрд╣рд▓реЗ" рдпрд╛ "рдмрд╛рдж" рдХреЛ рдЪреБрдирдиреЗ рдХреЗ рддрд░реНрдХ рдХреЛ рдлрд┐рд░ рд╕реЗ рд▓рд┐рдЦрдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рддрд╛рдХрд┐ рдпрд╣ рдзреНрдпрд╛рди рдореЗрдВ рд░рдЦреЗ рдХрд┐ рдорд╛рдЙрд╕ рдХрд░реНрд╕рд░ рдХрд╛ рдЖрдзрд╛ рднрд╛рдЧ "рд╢реАрд░реНрд╖" рдпрд╛ "рдиреАрдЪреЗ" рд╕реНрдерд┐рдд рд╣реИред рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, onDragOver рдкрд░ рд╣рдореЗрдВ рд╕реНрдХреНрд░реАрди рдХреЗ рд╕рд╛рдкреЗрдХреНрд╖ рддрддреНрд╡ рдХреЗ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рдорд┐рд▓рддреЗ рд╣реИрдВ рдФрд░ рдЬрд╛рдВрдЪрддреЗ рд╣реИрдВ рдХрд┐ рдЖрдзрд╛ рдХрд░реНрд╕рд░ рдХреМрди рд╕рд╛ рд╣реИ:
http://jsfiddle.net/RubaXa/zLq5J/6/ var rect = target.getBoundingClientRect(); var next = (evt.clientY - rect.top)/(rect.bottom - rect.top) > .5; rootEl.insertBefore(dragEl, next && target.nextSibling || target);
рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдореБрдЭреЗ рдЗрдирд▓рд╛рдЗрди рддрддреНрд╡реЛрдВ рдФрд░ рдлреНрд▓реЛрдЯ рдмреНрд▓реЙрдХреЛрдВ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХреЛ рднреА
рдкрд░рд┐рд╖реНрдХреГрдд рдХрд░рдирд╛ рдкрдбрд╝рд╛ред
рд╕реНрдкрд░реНрд╢ рд╕рдорд░реНрдерди
рдХрд╛рд╢, drag'n'drop рд╕реНрдкрд░реНрд╢ рдЙрдкрдХрд░рдгреЛрдВ рдкрд░ рдХрд╛рдо рдирд╣реАрдВ рдХрд░рддрд╛ рд╣реИ, рддреЛ рдХрд┐рд╕реА рддрд░рд╣ рд╣рдо рд╕реНрдкрд░реНрд╢ рдШрдЯрдирд╛рдУрдВ рдХреЗ рдЖрдзрд╛рд░ рдкрд░ рдЕрдиреБрдХрд░рдг рдХрд░рдирд╛ рдерд╛ред рдореИрдВрдиреЗ рд▓рдВрдмреЗ рд╕рдордп рддрдХ рдЕрдкрдиреЗ рджрд┐рдорд╛рдЧ рдХреЛ рд░реИрдХ рдХрд┐рдпрд╛, рджрд╕реНрддрд╛рд╡реЗрдЬрд╝реАрдХрд░рдг рдкрдврд╝рд╛, рд▓реЗрдХрд┐рди рдЬрд╡рд╛рдм рдирд╣реАрдВ рдорд┐рд▓рд╛ред рдирддреАрдЬрддрди, рдереЛрдбрд╝реА рд╕реА рдЦреБрджрд╛рдИ рдХреЗ рдмрд╛рдж, рдореБрдЭреЗ рдЕрджреНрднреБрдд
рджрд╕реНрддрд╛рд╡реЗрдЬ рдпрд╛рдж рдЖрдпрд╛ редelementFromPoint рд╡рд┐рдзрд┐
, рдЬреЛ рдЖрдкрдХреЛ рдирд┐рд░реНрджреЗрд╢рд╛рдВрдХ рджреНрд╡рд╛рд░рд╛ рдПрдХ рддрддреНрд╡ рдХрд╛ рд▓рд┐рдВрдХ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред
рдирддреАрдЬрддрди,
touchstart
рдореИрдВ рдПрдХ рддрддреНрд╡ рдХреЛ
рдХреНрд▓реЛрди рдХрд░рддрд╛ рд╣реВрдВ рдЬреЛ рдореЗрд░реА рдЙрдВрдЧрд▓реА рдХреЗ рдиреАрдЪреЗ рдПрдХ "рднреВрдд" рдХреЗ рд░реВрдк рдореЗрдВ рдХрд╛рдо рдХрд░реЗрдЧрд╛ рдФрд░ рдЗрд╕реЗ
touchmove
рдкрд░
translate3d
рдХрд╛ рдЙрдкрдпреЛрдЧ
touchmove
рд▓реЗ рдЬрд╛рдПрдЧрд╛:
var touch = evt.touches[0] , dx = touch.clientX - tapEvt.clientX , dy = touch.clientY - tapEvt.clientY ;
рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рдореИрдВ
setInterval
рдЪрд▓рд╛рддрд╛ рд╣реВрдВ, рдЬрд┐рд╕рдореЗрдВ рдкреНрд░рддреНрдпреЗрдХ 100ms рдореИрдВ
рдЙрд╕ рддрддреНрд╡ рдХреА
рдЬрд╛рдВрдЪ рдХрд░рддрд╛ рд╣реВрдВ, рдЬрд┐рд╕ рдкрд░ рдореЗрд░реА рдЙрдВрдЧрд▓реА рд╡рд░реНрддрдорд╛рди рдореЗрдВ рд╣реИ:
_emulateDragOver: function (){ if( touchEvt ){
рдЬреИрд╕рд╛ рдХрд┐ рдЖрдк рджреЗрдЦ рд░рд╣реЗ рд╣реИрдВ, рдпрд╣ рд╕рдм рдХреБрдЫ рдЕрд▓реМрдХрд┐рдХ рдирд╣реАрдВ рд╣реИред рд╣рдо рдХреЛрдб рдмрдирд╛рддреЗ рд╣реИрдВ, рдереЛрдбрд╝рд╛ рджрд╕реНрддрд╛рд╡реЗрдЬ рд▓рд┐рдЦрддреЗ рд╣реИрдВ рдФрд░ рдорд╛рдЗрдХреНрд░реЛ рд▓рд╛рдЗрдмреНрд░реЗрд░реА рддреИрдпрд╛рд░ рд╣реЛрддреА рд╣реИред
рдХреНрд░рдорд┐рдд рдХрд░рдиреЗ рдпреЛрдЧреНрдп
рдкреБрд╕реНрддрдХрд╛рд▓рдп 2 kb рдХрд╛ рд╣реЛ рдЧрдпрд╛ рдФрд░ рдирд┐рдореНрдирд▓рд┐рдЦрд┐рдд рд╡рд┐рд╢реЗрд╖рддрд╛рдПрдВ рд╣реИрдВ:
- рдКрд░реНрдзреНрд╡рд╛рдзрд░ рдФрд░ рдХреНрд╖реИрддрд┐рдЬ рд╕реВрдЪрд┐рдпреЛрдВ рдХреЛ рдХреНрд░рдордмрджреНрдз рдХрд░реЗрдВ;
- рдЫрдВрдЯрд╛рдИ (рд╕реАрдПрд╕рдПрд╕-рдЪрдпрдирдХрд░реНрддрд╛) рдХреЗ рд▓рд┐рдП рдЖрдЗрдЯрдо рд╕реЗрдЯ рдХрд░рдиреЗ рдХреА рдХреНрд╖рдорддрд╛;
- рд╕рдореВрд╣рди;
- рд╕рдВрднрд╛рд▓ рдирд┐рд░реНрджрд┐рд╖реНрдЯ рдХрд░рдиреЗ рдХреА рдХреНрд╖рдорддрд╛ (рдПрдХ рддрддреНрд╡ рдЬрд┐рд╕рдХреЗ рд▓рд┐рдП рдЖрдк рдЦреАрдВрдЪ рд╕рдХрддреЗ рд╣реИрдВ);
- рдЬрд┐рд╕ рд╡рд░реНрдЧ рдХреЛ рдЖрдЗрдЯрдо рдореЗрдВ рдЬреЛрдбрд╝рд╛ рдЬрд╛рддрд╛ рд╣реИ рдЙрд╕реЗ рд╕реНрдерд╛рдирд╛рдВрддрд░рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ;
- рдШрдЯрдирд╛рдПрдБ onAdd, onUpdate, onRemove;
- рдЧрддрд┐рд╢реАрд▓ рд░реВрдк рд╕реЗ рдмрджрд▓рддреА рд╕реВрдЪреА рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░реЗрдВред
рдХреЛрдб рдЙрджрд╛рд╣рд░рдг:
рдлрд┐рд▓рд╣рд╛рд▓, рдХреЗрд╡рд▓ рдмреБрдирд┐рдпрд╛рджреА рдХрд╛рд░реНрдпрдХреНрд╖рдорддрд╛ рд╣реИ, рдореБрдЭреЗ рдХрд┐рд╕реА рднреА рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдпрд╛
рдкреБрд▓ рдЕрдиреБрд░реЛрдз рдкрд░ рдЦреБрд╢реА рд╣реЛрдЧреА, рдЖрдкрдХреЗ рдзреНрдпрд╛рди рдХреЗ рд▓рд┐рдП рдзрдиреНрдпрд╡рд╛рджред
рдбреЗрдореЛ |
рд╕реНрд░реЛрддрдЖрдк рд╣рдорд╛рд░реА рдкрд░рд┐рдпреЛрдЬрдирд╛рдУрдВ рдХрд╛ рднреА рдЕрдиреБрд╕рд░рдг рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ:
github.com/mailru - FileAPI, рдЯрд╛рд░рдирдЯреВрд▓, рдЙрддреНрд╕рд╡ рдФрд░ рдмрд╣реБрдд рдХреБрдЫ
github.com/rubaxa - рдореЗрд░рд╛ рдЬреАрдереВрдм
@ibnRubaXa