Commit 4cdf8045 by Matjaz Gregoric Committed by GitHub

Merge pull request #133 from edx-solutions/mtyaka/MCKIN-5772-mobile-drag-delay

[MCKIN-5774] Wait 500 milliseconds before starting drag on touch.
parents 93fbea44 b4bc19f8
...@@ -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 ***/
...@@ -725,3 +726,12 @@ ...@@ -725,3 +726,12 @@
background-color: #ffffff; background-color: #ffffff;
color: #000000; color: #000000;
} }
/* Prevent mobile browsers from emulating hover effects on item tap, which can be confusing. */
@media (hover: none) {
.xblock--drag-and-drop .drag-container .option[draggable='true']:hover {
outline: none;
box-shadow: none;
}
}
/*! 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