Commit 6af15539 by Matjaz Gregoric

Wait 500 milliseconds before starting drag on touch.

This allows users to scroll the item bank with their finger without
accidentally starting to drag items.

Mouse drag events still start without a delay, since scrolling works
differently on desktops.

I found no way to implement this with jQuery UI draggable, so I had to
reimplement drag and drop interactions, which in some ways makes it
cleaner because it doesn't bypass virtualDOM.
parent 93fbea44
...@@ -262,12 +262,9 @@ class DragAndDropBlock( ...@@ -262,12 +262,9 @@ class DragAndDropBlock(
fragment = Fragment() fragment = Fragment()
fragment.add_content(loader.render_template('/templates/html/drag_and_drop.html')) fragment.add_content(loader.render_template('/templates/html/drag_and_drop.html'))
css_urls = ( css_urls = (
'public/css/vendor/jquery-ui-1.10.4.custom.min.css', 'public/css/drag_and_drop.css',
'public/css/drag_and_drop.css'
) )
js_urls = ( js_urls = (
'public/js/vendor/jquery-ui-1.10.4.custom.min.js',
'public/js/vendor/jquery-ui-touch-punch-0.2.3.min.js', # Makes it work on touch devices
'public/js/vendor/virtual-dom-1.3.0.min.js', 'public/js/vendor/virtual-dom-1.3.0.min.js',
'public/js/drag_and_drop.js', 'public/js/drag_and_drop.js',
) )
...@@ -357,11 +354,9 @@ class DragAndDropBlock( ...@@ -357,11 +354,9 @@ class DragAndDropBlock(
fragment.add_content(loader.render_template('/templates/html/drag_and_drop_edit.html', context)) fragment.add_content(loader.render_template('/templates/html/drag_and_drop_edit.html', context))
css_urls = ( css_urls = (
'public/css/vendor/jquery-ui-1.10.4.custom.min.css', 'public/css/drag_and_drop_edit.css',
'public/css/drag_and_drop_edit.css'
) )
js_urls = ( js_urls = (
'public/js/vendor/jquery-ui-1.10.4.custom.min.js',
'public/js/vendor/handlebars-v1.1.2.js', 'public/js/vendor/handlebars-v1.1.2.js',
'public/js/drag_and_drop_edit.js', 'public/js/drag_and_drop_edit.js',
) )
......
...@@ -67,6 +67,7 @@ ...@@ -67,6 +67,7 @@
width: auto; width: auto;
padding: 1%; padding: 1%;
background-color: #ebf0f2; background-color: #ebf0f2;
position: relative;
} }
/*** DRAGGABLE ITEMS ***/ /*** DRAGGABLE ITEMS ***/
......
/*! jQuery UI - v1.10.4 - 2014-07-07
* http://jqueryui.com
* Includes: jquery.ui.core.css, jquery.ui.resizable.css, jquery.ui.selectable.css, jquery.ui.button.css, jquery.ui.dialog.css, jquery.ui.theme.css
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana%2CArial%2Csans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=highlight_soft&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=flat&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=glass&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=glass&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=glass&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=glass&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
* Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */
.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-clearfix{min-height:0}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black}.ui-button{display:inline-block;position:relative;padding:0;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2.2em}button.ui-button-icon-only{width:2.4em}.ui-button-icons-only{width:3.4em}button.ui-button-icons-only{width:3.7em}.ui-button .ui-button-text{display:block;line-height:normal}.ui-button-text-only .ui-button-text{padding:.4em 1em}.ui-button-icon-only .ui-button-text,.ui-button-icons-only .ui-button-text{padding:.4em;text-indent:-9999999px}.ui-button-text-icon-primary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 1em .4em 2.1em}.ui-button-text-icon-secondary .ui-button-text,.ui-button-text-icons .ui-button-text{padding:.4em 2.1em .4em 1em}.ui-button-text-icons .ui-button-text{padding-left:2.1em;padding-right:2.1em}input.ui-button{padding:.4em 1em}.ui-button-icon-only .ui-icon,.ui-button-text-icon-primary .ui-icon,.ui-button-text-icon-secondary .ui-icon,.ui-button-text-icons .ui-icon,.ui-button-icons-only .ui-icon{position:absolute;top:50%;margin-top:-8px}.ui-button-icon-only .ui-icon{left:50%;margin-left:-8px}.ui-button-text-icon-primary .ui-button-icon-primary,.ui-button-text-icons .ui-button-icon-primary,.ui-button-icons-only .ui-button-icon-primary{left:.5em}.ui-button-text-icon-secondary .ui-button-icon-secondary,.ui-button-text-icons .ui-button-icon-secondary,.ui-button-icons-only .ui-button-icon-secondary{right:.5em}.ui-buttonset{margin-right:7px}.ui-buttonset .ui-button{margin-left:0;margin-right:-.3em}input.ui-button::-moz-focus-inner,button.ui-button::-moz-focus-inner{border:0;padding:0}.ui-dialog{overflow:hidden;position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:20px;margin:-10px 0 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:none;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-se{width:12px;height:12px;right:-5px;bottom:-5px;background-position:16px 16px}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-widget{font-family:Verdana,Arial,sans-serif;font-size:1.1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Verdana,Arial,sans-serif;font-size:1em}.ui-widget-content{border:1px solid #aaa;background:#fff url("images/ui-bg_flat_75_ffffff_40x100.png") 50% 50% repeat-x;color:#222}.ui-widget-content a{color:#222}.ui-widget-header{border:1px solid #aaa;background:#ccc url("images/ui-bg_highlight-soft_75_cccccc_1x100.png") 50% 50% repeat-x;color:#222;font-weight:bold}.ui-widget-header a{color:#222}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{border:1px solid #d3d3d3;background:#e6e6e6 url("images/ui-bg_glass_75_e6e6e6_1x400.png") 50% 50% repeat-x;font-weight:normal;color:#555}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited{color:#555;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus{border:1px solid #999;background:#dadada url("images/ui-bg_glass_75_dadada_1x400.png") 50% 50% repeat-x;font-weight:normal;color:#212121}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited{color:#212121;text-decoration:none}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active{border:1px solid #aaa;background:#fff url("images/ui-bg_glass_65_ffffff_1x400.png") 50% 50% repeat-x;font-weight:normal;color:#212121}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#212121;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #fcefa1;background:#fbf9ee url("images/ui-bg_glass_55_fbf9ee_1x400.png") 50% 50% repeat-x;color:#363636}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#363636}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #cd0a0a;background:#fef1ec url("images/ui-bg_glass_95_fef1ec_1x400.png") 50% 50% repeat-x;color:#cd0a0a}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#cd0a0a}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#cd0a0a}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_222222_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_222222_256x240.png")}.ui-state-default .ui-icon{background-image:url("images/ui-icons_888888_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon{background-image:url("images/ui-icons_454545_256x240.png")}.ui-state-active .ui-icon{background-image:url("images/ui-icons_454545_256x240.png")}.ui-state-highlight .ui-icon{background-image:url("images/ui-icons_2e83ff_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_cd0a0a_256x240.png")}.ui-icon-blank{background-position:16px 16px}.ui-icon-carat-1-n{background-position:0 0}.ui-icon-carat-1-ne{background-position:-16px 0}.ui-icon-carat-1-e{background-position:-32px 0}.ui-icon-carat-1-se{background-position:-48px 0}.ui-icon-carat-1-s{background-position:-64px 0}.ui-icon-carat-1-sw{background-position:-80px 0}.ui-icon-carat-1-w{background-position:-96px 0}.ui-icon-carat-1-nw{background-position:-112px 0}.ui-icon-carat-2-n-s{background-position:-128px 0}.ui-icon-carat-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-64px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-64px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:0 -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:4px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:4px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:4px}.ui-widget-overlay{background:#aaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x;opacity:.3;filter:Alpha(Opacity=30)}.ui-widget-shadow{margin:-8px 0 0 -8px;padding:8px;background:#aaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x;opacity:.3;filter:Alpha(Opacity=30);border-radius:8px}
\ No newline at end of file
...@@ -83,6 +83,11 @@ function DragAndDropTemplates(configuration) { ...@@ -83,6 +83,11 @@ function DragAndDropTemplates(configuration) {
// matches contrast between text color and background color: // matches contrast between text color and background color:
style['outline-color'] = item.color; style['outline-color'] = item.color;
} }
if (item.is_dragged) {
style.position = 'absolute';
style.left = item.drag_position.left + 'px';
style.top = item.drag_position.top + 'px';
}
if (item.is_placed) { if (item.is_placed) {
var maxWidth = (item.widthPercent || 30) / 100; var maxWidth = (item.widthPercent || 30) / 100;
var widthPercent = zone.width_percent / 100; var widthPercent = zone.width_percent / 100;
...@@ -133,13 +138,24 @@ function DragAndDropTemplates(configuration) { ...@@ -133,13 +138,24 @@ function DragAndDropTemplates(configuration) {
itemSpinnerTemplate(item), item_content, itemSRNote, item_description itemSpinnerTemplate(item), item_content, itemSRNote, item_description
]; ];
// Unique key for virtual dom change tracking. Key must be different for
// Placed vs Dragged vs Unplaced, or weird bugs can occur.
var key = item.value;
if (item.is_placed && item.is_dragged) {
key += '-pd';
} else if (item.is_placed) {
key += '-p';
} else if (item.is_dragged) {
key += '-d';
} else {
key += '-u';
}
return ( return (
h( h(
'div.option', 'div.option',
{ {
// Unique key for virtual dom change tracking. Key must be different for key: key,
// Placed vs Unplaced, or weird bugs can occur.
key: item.value + (item.is_placed ? "-p" : "-u"),
className: className, className: className,
attributes: attributes, attributes: attributes,
style: style style: style
...@@ -183,7 +199,9 @@ function DragAndDropTemplates(configuration) { ...@@ -183,7 +199,9 @@ function DragAndDropTemplates(configuration) {
var selector = ctx.display_zone_borders ? 'div.zone.zone-with-borders' : 'div.zone'; var selector = ctx.display_zone_borders ? 'div.zone.zone-with-borders' : 'div.zone';
// Mark item alignment and render its placed items as children // Mark item alignment and render its placed items as children
var item_wrapper = 'div.item-wrapper.item-align.item-align-' + zone.align; var item_wrapper = 'div.item-wrapper.item-align.item-align-' + zone.align;
var is_item_in_zone = function(i) { return i.is_placed && (i.zone === zone.uid); }; // In assessment mode already placed items can be dragged out of their current zone.
// Only render placed items that are not currently being dragged out of the zone.
var is_item_in_zone = function(i) { return i.is_placed && !i.is_dragged && (i.zone === zone.uid); };
var items_in_zone = $.grep(ctx.items, is_item_in_zone); var items_in_zone = $.grep(ctx.items, is_item_in_zone);
var zone_description_id = zone.prefixed_uid + '-description'; var zone_description_id = zone.prefixed_uid + '-description';
if (items_in_zone.length == 0) { if (items_in_zone.length == 0) {
...@@ -528,9 +546,25 @@ function DragAndDropTemplates(configuration) { ...@@ -528,9 +546,25 @@ function DragAndDropTemplates(configuration) {
var problemHeader = ctx.show_problem_header ? h('h4.title1', gettext('Problem')) : null; var problemHeader = ctx.show_problem_header ? h('h4.title1', gettext('Problem')) : null;
// Render only items in the bank here, including placeholders. Placed // Render only items in the bank here, including placeholders. Placed
// items will be rendered by zoneTemplate. // items will be rendered by zoneTemplate.
var is_item_placed = function(i) { return i.is_placed; }; var items_in_bank = [];
var items_placed = $.grep(ctx.items, is_item_placed); var items_dragged = [];
var items_in_bank = $.grep(ctx.items, is_item_placed, true); var items_placed = [];
ctx.items.forEach(function(item) {
if (item.is_dragged) {
items_dragged.push(item);
// Dragged items require a placeholder in the bank.
// In assessment mode, already placed items can be dragged.
if (item.is_placed) {
items_placed.push(item)
} else {
items_in_bank.push(item);
}
} else if (item.is_placed) {
items_placed.push(item);
} else {
items_in_bank.push(item);
}
});
var item_bank_properties = { var item_bank_properties = {
attributes: { attributes: {
'role': 'group', 'role': 'group',
...@@ -543,6 +577,19 @@ function DragAndDropTemplates(configuration) { ...@@ -543,6 +577,19 @@ function DragAndDropTemplates(configuration) {
item_bank_properties.attributes['aria-dropeffect'] = 'move'; item_bank_properties.attributes['aria-dropeffect'] = 'move';
item_bank_properties.attributes['role'] = 'button'; item_bank_properties.attributes['role'] = 'button';
} }
// Render items in the bank. If the item is currently being dragged, it should be
// rendered as a placeholder. All already placed items should also be rendered as
// placedholders (as the last content in the bank) to maintain original bank dimensions.
var bank_children = [];
items_in_bank.forEach(function(item) {
if (item.is_dragged) {
bank_children.push(itemPlaceholderTemplate(item, ctx));
} else {
bank_children.push(itemTemplate(item, ctx));
}
});
bank_children = bank_children.concat(renderCollection(itemPlaceholderTemplate, items_placed, ctx));
return ( return (
h('div.themed-xblock.xblock--drag-and-drop', main_element_properties, [ h('div.themed-xblock.xblock--drag-and-drop', main_element_properties, [
problemTitle, problemTitle,
...@@ -553,10 +600,7 @@ function DragAndDropTemplates(configuration) { ...@@ -553,10 +600,7 @@ function DragAndDropTemplates(configuration) {
h('p', {innerHTML: ctx.problem_html}), h('p', {innerHTML: ctx.problem_html}),
]), ]),
h('div.drag-container', {}, [ h('div.drag-container', {}, [
h('div.item-bank', item_bank_properties, [ h('div.item-bank', item_bank_properties, bank_children),
renderCollection(itemTemplate, items_in_bank, ctx),
renderCollection(itemPlaceholderTemplate, items_placed, ctx)
]),
h('div.target', {attributes: {'role': 'group', 'arial-label': gettext('Drop Targets')}}, [ h('div.target', {attributes: {'role': 'group', 'arial-label': gettext('Drop Targets')}}, [
itemFeedbackPopupTemplate(ctx), itemFeedbackPopupTemplate(ctx),
h('div.target-img-wrapper', [ h('div.target-img-wrapper', [
...@@ -564,6 +608,7 @@ function DragAndDropTemplates(configuration) { ...@@ -564,6 +608,7 @@ function DragAndDropTemplates(configuration) {
]), ]),
renderCollection(zoneTemplate, ctx.zones, ctx) renderCollection(zoneTemplate, ctx.zones, ctx)
]), ]),
h('div.dragged-items', renderCollection(itemTemplate, items_dragged, ctx)),
]), ]),
h("div.actions-toolbar", {attributes: {'role': 'group', 'aria-label': gettext('Actions')}}, [ h("div.actions-toolbar", {attributes: {'role': 'group', 'aria-label': gettext('Actions')}}, [
(ctx.show_submit_answer ? submitAnswerTemplate(ctx) : null), (ctx.show_submit_answer ? submitAnswerTemplate(ctx) : null),
...@@ -617,6 +662,11 @@ function DragAndDropBlock(runtime, element, configuration) { ...@@ -617,6 +662,11 @@ function DragAndDropBlock(runtime, element, configuration) {
var DEFAULT_ZONE_ALIGN = 'center'; var DEFAULT_ZONE_ALIGN = 'center';
// Number of miliseconds the user has to keep their finger on the item
// without moving for drag to begin.
// This allows user to scroll the container without accidentally dragging the items.
var TOUCH_DRAG_DELAY = 500;
// Keyboard accessibility // Keyboard accessibility
var ESC = 27; var ESC = 27;
var RET = 13; var RET = 13;
...@@ -689,6 +739,7 @@ function DragAndDropBlock(runtime, element, configuration) { ...@@ -689,6 +739,7 @@ function DragAndDropBlock(runtime, element, configuration) {
$root.empty(); $root.empty();
applyState(); applyState();
initDraggable();
initDroppable(); initDroppable();
// Indicate that problem is done loading // Indicate that problem is done loading
...@@ -795,7 +846,7 @@ function DragAndDropBlock(runtime, element, configuration) { ...@@ -795,7 +846,7 @@ function DragAndDropBlock(runtime, element, configuration) {
$keyboardHelpDialog.find('.modal-window').show().focus(); $keyboardHelpDialog.find('.modal-window').show().focus();
// Set up event handlers // Set up event handlers
$(document).on('keydown', function(evt) { $(document).on('keydown.keyboard-help', function(evt) {
keyboardEventDispatcher(evt, focusId); keyboardEventDispatcher(evt, focusId);
}); });
...@@ -813,7 +864,7 @@ function DragAndDropBlock(runtime, element, configuration) { ...@@ -813,7 +864,7 @@ function DragAndDropBlock(runtime, element, configuration) {
$keyboardHelpDialog.find('.modal-window').hide(); $keyboardHelpDialog.find('.modal-window').hide();
// Remove event handlers // Remove event handlers
$(document).off('keydown'); $(document).off('keydown.keyboard-help');
$keyboardHelpDialog.find('.modal-dismiss-button').off(); $keyboardHelpDialog.find('.modal-dismiss-button').off();
// Handle focus // Handle focus
...@@ -892,15 +943,9 @@ function DragAndDropBlock(runtime, element, configuration) { ...@@ -892,15 +943,9 @@ function DragAndDropBlock(runtime, element, configuration) {
/** /**
* Update the DOM to reflect 'state'. * Update the DOM to reflect 'state'.
*/ */
var applyState = function(keepDraggableInit) { var applyState = function() {
sendFeedbackPopupEvents(); sendFeedbackPopupEvents();
updateDOM(); updateDOM();
if (!keepDraggableInit) {
destroyDraggable();
if (!state.finished) {
initDraggable();
}
}
}; };
var sendFeedbackPopupEvents = function() { var sendFeedbackPopupEvents = function() {
...@@ -1053,6 +1098,18 @@ function DragAndDropBlock(runtime, element, configuration) { ...@@ -1053,6 +1098,18 @@ function DragAndDropBlock(runtime, element, configuration) {
return false; return false;
}; };
var returnItemToBank = function(item_id) {
if (!state.items[item_id]) {
// Nothing to do here, item is already in the bank.
return;
}
delete state.items[item_id];
applyState();
var url = runtime.handlerUrl(element, 'drop_item');
var data = {val: item_id, zone: null};
$.post(url, JSON.stringify(data), 'json');
};
var placeGrabbedItem = function($zone) { var placeGrabbedItem = function($zone) {
var zone = String($zone.data('uid')); var zone = String($zone.data('uid'));
var zone_align = $zone.data('zone_align'); var zone_align = $zone.data('zone_align');
...@@ -1066,6 +1123,11 @@ function DragAndDropBlock(runtime, element, configuration) { ...@@ -1066,6 +1123,11 @@ function DragAndDropBlock(runtime, element, configuration) {
} }
} }
if (state.items[item_id] && state.items[item_id].zone === zone) {
// Nothing to do here, item already in zone.
return;
}
var items_in_zone_count = countItemsInZone(zone, [item_id.toString()]); var items_in_zone_count = countItemsInZone(zone, [item_id.toString()]);
if (configuration.max_items_per_zone && configuration.max_items_per_zone <= items_in_zone_count) { if (configuration.max_items_per_zone && configuration.max_items_per_zone <= items_in_zone_count) {
state.last_action_correct = false; state.last_action_correct = false;
...@@ -1080,11 +1142,8 @@ function DragAndDropBlock(runtime, element, configuration) { ...@@ -1080,11 +1142,8 @@ function DragAndDropBlock(runtime, element, configuration) {
submitting_location: true, submitting_location: true,
}; };
// Wrap in setTimeout to let the droppable event finish. applyState();
setTimeout(function() { submitLocation(item_id, zone);
applyState();
submitLocation(item_id, zone);
}, 0);
}; };
var countItemsInZone = function(zone, exclude_ids) { var countItemsInZone = function(zone, exclude_ids) {
...@@ -1101,132 +1160,285 @@ function DragAndDropBlock(runtime, element, configuration) { ...@@ -1101,132 +1160,285 @@ function DragAndDropBlock(runtime, element, configuration) {
var initDroppable = function() { var initDroppable = function() {
// Set up zones for keyboard interaction // Set up zones for keyboard interaction
$root.find('.zone, .item-bank').each(function() { $root.on('keydown', '.zone, .item-bank', function(evt) {
var $zone = $(this); var $zone = $(this);
$zone.on('keydown', function(evt) { if (state.keyboard_placement_mode) {
if (state.keyboard_placement_mode) { if (isCycleKey(evt)) {
if (isCycleKey(evt)) { focusNextZone(evt, $zone);
focusNextZone(evt, $zone); } else if (isCancelKey(evt)) {
} else if (isCancelKey(evt)) { evt.preventDefault();
evt.preventDefault(); state.keyboard_placement_mode = false;
state.keyboard_placement_mode = false; releaseGrabbedItems();
releaseGrabbedItems(); } else if (isActionKey(evt)) {
} else if (isActionKey(evt)) { evt.preventDefault();
evt.preventDefault(); evt.stopPropagation();
evt.stopPropagation(); state.keyboard_placement_mode = false;
state.keyboard_placement_mode = false; if ($zone.is('.item-bank')) {
if ($zone.is('.item-bank')) { delete state.items[$selectedItem.data('value')];
delete state.items[$selectedItem.data('value')]; } else {
} else { placeGrabbedItem($zone);
placeGrabbedItem($zone);
}
releaseGrabbedItems();
}
} else if (isTabKey(evt) && !evt.shiftKey) {
// If the user just dropped an item to this zone, next TAB keypress
// should move focus to "Go to Beginning" button.
if (state.tab_to_go_to_beginning_button && canGoToBeginning()) {
evt.preventDefault();
focusGoToBeginningButton();
} }
} else if (isSpaceKey(evt)) { releaseGrabbedItems();
// Pressing the space bar moves the page down by default in most browsers. }
// That can be distracting while moving items with the keyboard, so prevent } else if (isTabKey(evt) && !evt.shiftKey) {
// the default scroll from happening while a zone is focused. // If the user just dropped an item to this zone, next TAB keypress
// should move focus to "Go to Beginning" button.
if (state.tab_to_go_to_beginning_button && canGoToBeginning()) {
evt.preventDefault(); evt.preventDefault();
focusGoToBeginningButton();
} }
}); } else if (isSpaceKey(evt)) {
$zone.on('blur', function() { // Pressing the space bar moves the page down by default in most browsers.
delete state.tab_to_go_to_beginning_button; // That can be distracting while moving items with the keyboard, so prevent
}); // the default scroll from happening while a zone is focused.
}); evt.preventDefault();
// Make zones accept items that are dropped using the mouse
$root.find('.zone').droppable({
accept: '.drag-container .option',
tolerance: 'pointer',
drop: function(evt, ui) {
var $zone = $(this);
placeGrabbedItem($zone);
} }
}); });
if (configuration.mode === DragAndDropBlock.ASSESSMENT_MODE) { $root.on('blur', '.zone, .item-bank', function(evt) {
// Make item bank accept items that are returned to the bank using the mouse delete state.tab_to_go_to_beginning_button;
$root.find('.item-bank').droppable({ });
accept: '.target .option', };
tolerance: 'pointer',
drop: function(evt, ui) {
var $item = ui.helper;
var item_id = $item.data('value');
releaseGrabbedItems();
delete state.items[item_id];
applyState();
var url = runtime.handlerUrl(element, 'drop_item');
var data = {
val: item_id,
zone: null
};
$.post(url, JSON.stringify(data), 'json') var getItemById = function(item_id) {
} for (var i = 0; i < configuration.items.length; i++) {
}); if (configuration.items[i].id === item_id) {
return configuration.items[i];
}
} }
}; };
var initDraggable = function() { var initDraggable = function() {
$root.find('.drag-container .option[draggable=true]').each(function() { var $container = $root.find('.drag-container');
var $item = $(this);
// Allow items to be "picked up" using the keyboard
$container.on('keydown', '.option[draggable=true]', function(evt) {
if (isActionKey(evt)) {
var $item = $(this);
evt.preventDefault();
evt.stopPropagation();
state.keyboard_placement_mode = true;
grabItem($item, 'keyboard');
$selectedItem = $item;
$root.find('.target .zone').first().focus();
}
});
// Allow item to be "picked up" using the keyboard // Helper functions that return pageX/pageY from either touch or mouse events.
$item.on('keydown', function(evt) { var pageX = function(evt) {
if (isActionKey(evt)) { if (evt.type === 'touchstart' || evt.type === 'touchmove') {
evt.preventDefault(); return evt.originalEvent.targetTouches[0].pageX;
evt.stopPropagation(); } else {
state.keyboard_placement_mode = true; return evt.pageX;
grabItem($item, 'keyboard'); }
$selectedItem = $item; };
$root.find('.target .zone').first().focus();
var pageY = function(evt) {
if (evt.type === 'touchstart' || evt.type === 'touchmove') {
return evt.originalEvent.targetTouches[0].pageY;
} else {
return evt.pageY;
}
};
var isValidDropTarget = function(element) {
var valid = element.classList.contains('zone');
if (!valid && configuration.mode === DragAndDropBlock.ASSESSMENT_MODE) {
// In assessment mode, items can be dropped back into the item bank.
valid = element.classList.contains('item-bank');
}
return valid;
};
// If the drag ended on a valid target zone, returns the zone, otherwise returns null;
var getTargetZone = function(evt) {
var $zone = null;
var x, y;
if (evt.type === 'mouseup') {
x = evt.clientX;
y = evt.clientY;
} else {
x = evt.originalEvent.changedTouches[0].clientX;
y = evt.originalEvent.changedTouches[0].clientY;
}
// We have to temporarily hide the dragged item so that we can get the
// to the element below it.
var $dragged_items_container = $container.find('.dragged-items');
$dragged_items_container.hide();
var element = document.elementFromPoint(x, y);
while (element) {
if (isValidDropTarget(element)) {
$zone = $(element);
break;
} else {
element = element.parentElement;
} }
}
$dragged_items_container.show();
return $zone;
};
var onDragStart = function(interaction_type, $item, drag_origin) {
var item_id = $item.data('value');
var item = getItemById(item_id);
var $document = $(document);
grabItem($item, 'mouse');
publishEvent({
event_type: 'edx.drag_and_drop_v2.item.picked_up',
item_id: item_id
}); });
// Make item draggable using the mouse var max_left = $container.innerWidth() - $item.outerWidth();
try { var max_top = $container.innerHeight() - $item.outerHeight();
$item.draggable({ var item_size = {width: $item.width(), height: $item.height()};
addClasses: false, // don't add ui-draggable-* classes as they don't play well with virtual DOM. // We need to get the item position relative to the $container.
containment: $root.find('.drag-container'), var item_offset = $item.offset();
cursor: 'move', var container_offset = $container.offset();
stack: $root.find('.drag-container .option'), var original_position = {
revert: 'invalid', left: item_offset.left - container_offset.left,
revertDuration: 150, top: item_offset.top - container_offset.top
start: function(evt, ui) { };
var $item = $(this); item.drag_position = original_position;
// Store initial position of dragged item to be able to revert back to it on cancelled drag applyState();
// (when user drops the item onto an area that is not a droppable zone).
// The jQuery UI draggable library usually knows how to revert correctly, but our dropped items // Animate the item back to its original position in the bank.
// have a translation transform that confuses jQuery UI draggable, so we "help" it do the right var revertDrag = function() {
// thing by manually storing the initial position and resetting it in the 'stop' handler below. var revert_duration = 150;
$item.data('initial-position', { var start_position = item.drag_position;
left: $item.css('left'), var start_ts = null;
top: $item.css('top') var step = function(ts) {
}); if (!start_ts) {
grabItem($item, 'mouse'); start_ts = ts;
publishEvent({ }
event_type: 'edx.drag_and_drop_v2.item.picked_up', var progress = Math.min(1, (ts - start_ts) / revert_duration);
item_id: $item.data('value'), item.drag_position = {
}); left: start_position.left + (progress * (original_position.left - start_position.left)),
}, top: start_position.top + (progress * (original_position.top - start_position.top)),
stop: function(evt, ui) { };
// Revert to original position. if (progress === 1) {
$item.css($item.data('initial-position')); delete item.drag_position;
releaseGrabbedItems(); releaseGrabbedItems();
} else {
applyState();
requestAnimationFrame(step);
} }
}
requestAnimationFrame(step);
};
var raf_id = null;
var onDragMove = function(evt) {
evt.preventDefault();
if (raf_id) {
cancelAnimationFrame(raf_id);
}
raf_id = requestAnimationFrame(function() {
var dx = pageX(evt) - drag_origin.x;
var dy = pageY(evt) - drag_origin.y;
var left = original_position.left + dx;
var top = original_position.top + dy;
left = Math.max(0, Math.min(max_left, left));
top = Math.max(0, Math.min(max_top, top));
item.drag_position = {left: left, top: top};
applyState();
raf_id = null;
}); });
} catch (e) { };
// Initializing the draggable will fail if draggable was already
// initialized. That's expected, ignore the exception. var onDragEnd = function(evt) {
if (raf_id) {
cancelAnimationFrame(raf_id);
}
if (evt.type === 'mouseup') {
$document.off('mousemove', onDragMove);
$document.off('mouseup', onDragEnd);
} else {
$item.off('touchmove', onDragMove);
$item.off('touchend touchcancel', onDragEnd);
}
if (evt.type === 'touchcancel') {
revertDrag();
return;
}
var $zone = getTargetZone(evt);
if ($zone) {
delete item.drag_position;
if ($zone.is('.item-bank')) {
returnItemToBank(item_id);
} else {
placeGrabbedItem($zone);
}
releaseGrabbedItems();
} else {
revertDrag();
}
};
if (interaction_type === 'mouse') {
// Mouse events have to be bound to the document or they won't fire after
// the item is removed from the bank.
$document.on('mousemove', onDragMove);
$document.on('mouseup', onDragEnd);
} else {
// Touch events behave in the opposite way - they have to be bound to the item
// where the touchstart event was fired, or touchmove will not fire if the item
// gets removed from the DOM; see: https://stackoverflow.com/a/45760014/51397
$item.on('touchmove', onDragMove);
$item.on('touchend touchcancel', onDragEnd);
}
};
// Touch devices emulate mousedown events after touchstart, which would cause our drag
// start event to be handled twice.
// One way to prevent that would be to call evt.preventDefault() in the touchstart handler,
// but that would also prevent scrolling, which we do not want.
// Instead of preventing the default, we use the handled_by_touch flag, which we set to true
// in the touchstart handler (and set it back to false after the event is processed).
// If the mousedown handler sees the flag set to true, it will simply ignore the event.
var handled_by_touch = false;
// Mousedown events should start the drag immediately.
$container.on('mousedown', '.option[draggable=true]', function(evt) {
if (!handled_by_touch) {
evt.preventDefault();
var $item = $(this);
var drag_origin = {x: pageX(evt), y: pageY(evt)};
onDragStart('mouse', $(this), drag_origin);
} }
}); });
// Touchstart events should start the event after a timeout.
// If the user starts moving the finger during the timeout, the drag should be cancelled.
// This allows users to scroll the item bank with their finger without accidentally starting
// to drag items.
$container.on('touchstart', '.option[draggable=true]', function(evt) {
handled_by_touch = true;
var $item = $(this);
var drag_origin = {x: pageX(evt), y: pageY(evt)};
var timeout_id = null;
var cancelDrag = function() {
clearTimeout(timeout_id);
// We need to reset the handled_by_touch in a timeout,
// so that it happens after the potentially emulated mousedown event.
setTimeout(function() {
handled_by_touch = false;
}, 0);
};
$item.one('touchmove touchend touchcancel', cancelDrag);
timeout_id = setTimeout(function() {
handled_by_touch = false;
$item.off('touchmove touchend touchcancel', cancelDrag);
onDragStart('touch', $item, drag_origin);
}, TOUCH_DRAG_DELAY);
});
// Prevent touchmove events fired on the dragged item causing scroll.
$container.on('touchmove', '.dragged-items .options[draggable=true]', function(evt) {
evt.preventDefault();
});
}; };
var grabItem = function($item, interaction_type) { var grabItem = function($item, interaction_type) {
...@@ -1241,8 +1453,7 @@ function DragAndDropBlock(runtime, element, configuration) { ...@@ -1241,8 +1453,7 @@ function DragAndDropBlock(runtime, element, configuration) {
} }
}); });
closePopup(false); closePopup(false);
// applyState(true) skips destroying and initializing draggable applyState();
applyState(true);
}; };
var releaseGrabbedItems = function() { var releaseGrabbedItems = function() {
...@@ -1250,23 +1461,7 @@ function DragAndDropBlock(runtime, element, configuration) { ...@@ -1250,23 +1461,7 @@ function DragAndDropBlock(runtime, element, configuration) {
item.grabbed = false; item.grabbed = false;
delete item.grabbed_with; delete item.grabbed_with;
}); });
// applyState(true) skips destroying and initializing draggable applyState();
applyState(true);
};
var destroyDraggable = function() {
$root.find('.drag-container .option[draggable=false]').each(function() {
var $item = $(this);
$item.off();
try {
$item.draggable('destroy');
} catch (e) {
// Destroying the draggable will fail if draggable was
// not initialized in the first place. Ignore the exception.
}
});
}; };
var submitLocation = function(item_id, zone) { var submitLocation = function(item_id, zone) {
...@@ -1473,6 +1668,8 @@ function DragAndDropBlock(runtime, element, configuration) { ...@@ -1473,6 +1668,8 @@ function DragAndDropBlock(runtime, element, configuration) {
has_image: !!item.expandedImageURL, has_image: !!item.expandedImageURL,
grabbed: grabbed, grabbed: grabbed,
grabbed_with: item.grabbed_with, grabbed_with: item.grabbed_with,
is_dragged: Boolean(item.drag_position),
drag_position: item.drag_position,
is_placed: Boolean(item_user_state), is_placed: Boolean(item_user_state),
widthPercent: item.widthPercent, // widthPercent may be undefined (auto width) widthPercent: item.widthPercent, // widthPercent may be undefined (auto width)
imgNaturalWidth: item.imgNaturalWidth, imgNaturalWidth: item.imgNaturalWidth,
......
This source diff could not be displayed because it is too large. You can view the blob instead.
/*!
* jQuery UI Touch Punch 0.2.3
*
* Copyright 2011–2014, Dave Furfero
* Dual licensed under the MIT or GPL Version 2 licenses.
*
* Depends:
* jquery.ui.widget.js
* jquery.ui.mouse.js
*/
!function(a){function f(a,b){if(!(a.originalEvent.touches.length>1)){a.preventDefault();var c=a.originalEvent.changedTouches[0],d=document.createEvent("MouseEvents");d.initMouseEvent(b,!0,!0,window,1,c.screenX,c.screenY,c.clientX,c.clientY,!1,!1,!1,!1,0,null),a.target.dispatchEvent(d)}}if(a.support.touch="ontouchend"in document,a.support.touch){var e,b=a.ui.mouse.prototype,c=b._mouseInit,d=b._mouseDestroy;b._touchStart=function(a){var b=this;!e&&b._mouseCapture(a.originalEvent.changedTouches[0])&&(e=!0,b._touchMoved=!1,f(a,"mouseover"),f(a,"mousemove"),f(a,"mousedown"))},b._touchMove=function(a){e&&(this._touchMoved=!0,f(a,"mousemove"))},b._touchEnd=function(a){e&&(f(a,"mouseup"),f(a,"mouseout"),this._touchMoved||f(a,"click"),e=!1)},b._mouseInit=function(){var b=this;b.element.bind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),c.call(b)},b._mouseDestroy=function(){var b=this;b.element.unbind({touchstart:a.proxy(b,"_touchStart"),touchmove:a.proxy(b,"_touchMove"),touchend:a.proxy(b,"_touchEnd")}),d.call(b)}}}(jQuery);
\ No newline at end of file
git+https://github.com/edx/xblock-utils.git@v1.0.4#egg=xblock-utils==1.0.4 git+https://github.com/edx/xblock-utils.git@v1.0.4#egg=xblock-utils==1.0.4
git+https://github.com/edx/XBlock.git@xblock-0.5.0#egg=XBlock==0.5.0
-e . -e .
...@@ -188,7 +188,6 @@ class TestDragAndDropRender(BaseIntegrationTest): ...@@ -188,7 +188,6 @@ class TestDragAndDropRender(BaseIntegrationTest):
self.assertEqual(zone.get_attribute('aria-dropeffect'), 'move') self.assertEqual(zone.get_attribute('aria-dropeffect'), 'move')
self.assertEqual(zone.get_attribute('data-uid'), 'Zone {}'.format(zone_number)) self.assertEqual(zone.get_attribute('data-uid'), 'Zone {}'.format(zone_number))
self.assertEqual(zone.get_attribute('data-zone_align'), 'center') self.assertEqual(zone.get_attribute('data-zone_align'), 'center')
self.assertIn('ui-droppable', self.get_element_classes(zone))
zone_box_percentages = box_percentages[index] zone_box_percentages = box_percentages[index]
self._assert_box_percentages( # pylint: disable=star-args self._assert_box_percentages( # pylint: disable=star-args
'#-Zone_{}'.format(zone_number), **zone_box_percentages '#-Zone_{}'.format(zone_number), **zone_box_percentages
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment