From a21d34776bf618939e7ba1ad4332e48ad89c5f0a Mon Sep 17 00:00:00 2001 From: Marina Glancy Date: Tue, 12 Jun 2018 12:11:23 +0800 Subject: [PATCH 1/6] MDL-51803 core: autoscroll module from OU --- lib/amd/build/autoscroll.min.js | 1 + lib/amd/src/autoscroll.js | 192 ++++++++++++++++++++++++++++++++ 2 files changed, 193 insertions(+) create mode 100644 lib/amd/build/autoscroll.min.js create mode 100644 lib/amd/src/autoscroll.js diff --git a/lib/amd/build/autoscroll.min.js b/lib/amd/build/autoscroll.min.js new file mode 100644 index 0000000000000..9bd86ba45044c --- /dev/null +++ b/lib/amd/build/autoscroll.min.js @@ -0,0 +1 @@ +define(["jquery"],function(a){var b={SCROLL_THRESHOLD:30,SCROLL_FREQUENCY:1e3/60,SCROLL_SPEED:.5,scrollingId:null,scrollAmount:0,callback:null,start:function(c){a(window).on("mousemove",b.mouseMove),a(window).on("touchmove",b.touchMove),b.callback=c},stop:function(){a(window).off("mousemove",b.mouseMove),a(window).off("touchmove",b.touchMove),null!==b.scrollingId&&b.stopScrolling()},touchMove:function(a){for(var c=0;ca(window).height()-b.SCROLL_THRESHOLD?b.scrollAmount=Math.min(d-(a(window).height()-b.SCROLL_THRESHOLD),b.SCROLL_THRESHOLD):b.scrollAmount=0,b.scrollAmount&&null===b.scrollingId?b.startScrolling():b.scrollAmount||null===b.scrollingId||b.stopScrolling()},startScrolling:function(){var c=a(document).height-a(window).height;b.scrollingId=window.setInterval(function(){var d=a(window).scrollTop(),e=Math.round(b.scrollAmount*b.SCROLL_SPEED);if(d+e<0&&(e=-d),d+e>c&&(e=c-d),0!==e){a(window).scrollTop(d+e);var f=a(window).scrollTop()-d;0!==f&&b.callback&&b.callback(f)}},b.SCROLL_FREQUENCY)},stopScrolling:function(){window.clearInterval(b.scrollingId),b.scrollingId=null}};return b}); \ No newline at end of file diff --git a/lib/amd/src/autoscroll.js b/lib/amd/src/autoscroll.js new file mode 100644 index 0000000000000..f8a57b13b3805 --- /dev/null +++ b/lib/amd/src/autoscroll.js @@ -0,0 +1,192 @@ +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see . + +/* + * JavaScript to provide automatic scrolling, e.g. during a drag operation. + * + * Note: this module is defined statically. It is a singleton. You + * can only have one use of it active at any time. However, since this + * is usually used in relation to drag-drop, and since you only ever + * drag one thing at a time, this is not a problem in practice. + * + * @module core/autoscroll + * @class autoscroll + * @package core + * @copyright 2016 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @since 3.6 + */ +define(['jquery'], function($) { + /** + * @alias module:core/autoscroll + */ + var autoscroll = { + /** + * Size of area near edge of screen that triggers scrolling. + * @private + */ + SCROLL_THRESHOLD: 30, + + /** + * How frequently to scroll window. + * @private + */ + SCROLL_FREQUENCY: 1000 / 60, + + /** + * How many pixels to scroll per unit (1 = max scroll 30). + * @private + */ + SCROLL_SPEED: 0.5, + + /** + * Set if currently scrolling up/down. + * @private + */ + scrollingId: null, + + /** + * Speed we are supposed to scroll (range 1 to SCROLL_THRESHOLD). + * @private + */ + scrollAmount: 0, + + /** + * Optional callback called when it scrolls + * @private + */ + callback: null, + + /** + * Starts automatically scrolling if user moves near edge of window. + * This should be called in response to mouse down or touch start. + * + * @public + * @param {Object} callback Optional callback that is called every time it scrolls + */ + start: function(callback) { + $(window).on('mousemove', autoscroll.mouseMove); + $(window).on('touchmove', autoscroll.touchMove); + autoscroll.callback = callback; + }, + + /** + * Stops automatically scrolling. This should be called in response to mouse up or touch end. + * + * @public + */ + stop: function() { + $(window).off('mousemove', autoscroll.mouseMove); + $(window).off('touchmove', autoscroll.touchMove); + if (autoscroll.scrollingId !== null) { + autoscroll.stopScrolling(); + } + }, + + /** + * Event handler for touch move. + * + * @private + * @param {Object} e Event + */ + touchMove: function(e) { + for (var i = 0; i < e.changedTouches.length; i++) { + autoscroll.handleMove(e.changedTouches[i].clientX, e.changedTouches[i].clientY); + } + }, + + /** + * Event handler for mouse move. + * + * @private + * @param {Object} e Event + */ + mouseMove: function(e) { + autoscroll.handleMove(e.clientX, e.clientY); + }, + + /** + * Handles user moving. + * + * @private + * @param {number} clientX X + * @param {number} clientY Y + */ + handleMove: function(clientX, clientY) { + // If near the bottom or top, start auto-scrolling. + if (clientY < autoscroll.SCROLL_THRESHOLD) { + autoscroll.scrollAmount = -Math.min(autoscroll.SCROLL_THRESHOLD - clientY, autoscroll.SCROLL_THRESHOLD); + } else if (clientY > $(window).height() - autoscroll.SCROLL_THRESHOLD) { + autoscroll.scrollAmount = Math.min(clientY - ($(window).height() - autoscroll.SCROLL_THRESHOLD), + autoscroll.SCROLL_THRESHOLD); + } else { + autoscroll.scrollAmount = 0; + } + if (autoscroll.scrollAmount && autoscroll.scrollingId === null) { + autoscroll.startScrolling(); + } else if (!autoscroll.scrollAmount && autoscroll.scrollingId !== null) { + autoscroll.stopScrolling(); + } + }, + + /** + * Starts automatic scrolling. + * + * @private + */ + startScrolling: function() { + var maxScroll = $(document).height - $(window).height; + autoscroll.scrollingId = window.setInterval(function() { + // Work out how much to scroll. + var y = $(window).scrollTop(); + var offset = Math.round(autoscroll.scrollAmount * autoscroll.SCROLL_SPEED); + if (y + offset < 0) { + offset = -y; + } + if (y + offset > maxScroll) { + offset = maxScroll - y; + } + if (offset === 0) { + return; + } + + // Scroll. + $(window).scrollTop(y + offset); + var realOffset = $(window).scrollTop() - y; + if (realOffset === 0) { + return; + } + + // Inform callback + if (autoscroll.callback) { + autoscroll.callback(realOffset); + } + + }, autoscroll.SCROLL_FREQUENCY); + }, + + /** + * Stops the automatic scrolling. + * + * @private + */ + stopScrolling: function() { + window.clearInterval(autoscroll.scrollingId); + autoscroll.scrollingId = null; + } + }; + + return autoscroll; +}); From 539cb25050a27e6bbcf63776bf23a03a351e29ad Mon Sep 17 00:00:00 2001 From: Marina Glancy Date: Mon, 10 Sep 2018 14:38:21 +0200 Subject: [PATCH 2/6] MDL-51803 core: reusable element for sorting lists --- lang/en/moodle.php | 2 + lib/amd/build/sortable_list.min.js | 1 + lib/amd/src/sortable_list.js | 692 +++++++++++++++++++++++++++++ 3 files changed, 695 insertions(+) create mode 100644 lib/amd/build/sortable_list.min.js create mode 100644 lib/amd/src/sortable_list.js diff --git a/lang/en/moodle.php b/lang/en/moodle.php index ee02e871581ad..6fbf005401eff 100644 --- a/lang/en/moodle.php +++ b/lang/en/moodle.php @@ -1269,6 +1269,8 @@ $string['movecoursemodule'] = 'Move resource'; $string['movecoursesection'] = 'Move section'; $string['movecontent'] = 'Move {$a}'; +$string['movecontentafter'] = 'After "{$a}"'; +$string['movecontenttothetop'] = 'To the top of the list'; $string['movecategorycontentto'] = 'Move into'; $string['movecategorysuccess'] = 'Successfully moved category \'{$a->moved}\' into category \'{$a->to}\''; $string['movecategoriessuccess'] = 'Successfully moved {$a->count} categories into category \'{$a->to}\''; diff --git a/lib/amd/build/sortable_list.min.js b/lib/amd/build/sortable_list.min.js new file mode 100644 index 0000000000000..85971fb787f8c --- /dev/null +++ b/lib/amd/build/sortable_list.min.js @@ -0,0 +1 @@ +define(["jquery","core/log","core/autoscroll","core/str","core/modal_factory","core/modal_events","core/notification"],function(a,b,c,d,e,f,g){var h,i,j,k={listSelector:null,moveHandlerSelector:null,isHorizontal:!1,autoScroll:!0,elementNameCallback:function(a){return a.text()},destinationNameCallback:function(a,b){return b.length?D(b).then(function(a){return d.get_string("movecontentafter","moodle",a)}):d.get_string("movecontenttothetop","moodle")},moveDialogueTitleCallback:function(a){return D(a).then(function(a){return d.get_string("movecontent","moodle",a)})}},l={keyboardDragClass:"dragdrop-keyboard-drag",isDraggedClass:"sortable-list-is-dragged",currentPositionClass:"sortable-list-current-position",sourceListClass:"sortable-list-source",targetListClass:"sortable-list-target",overElementClass:"sortable-list-over-element"},m={},n=null,o=0,p=function(){var b=a(m.listSelector);b.children().removeClass(m.isDraggedClass).removeClass(m.currentPositionClass).removeClass(m.overElementClass),b.removeClass(m.targetListClass).removeClass(m.sourceListClass),h&&(h.remove(),h=a())},q=function(b){if(b.originalEvent&&b.originalEvent.touches&&void 0!==b.originalEvent.touches[0]){var c=b.originalEvent.touches[0];b.pageX=c.pageX,b.pageY=c.pageY}void 0===b.pageX?(b.pageX=j.pageX,b.pageY=j.pageY):j=b,void 0===b.clientX&&(b.clientX=Math.round(b.pageX-a(window).scrollLeft()),b.clientY=Math.round(b.pageY-a(window).scrollTop()))},r=function(b){if(m=b.data.params,null!==n){if("click"===n.type)return;y(n.sourceList,n.sourceNextElement),z()}if("mousedown"!==b.type||1===b.which){q(b);var d=a(b.currentTarget);if(null===m.moveHandlerSelector||a(b.target).closest(m.moveHandlerSelector,d).length){b.stopPropagation(),b.preventDefault(),o++,n={element:d,sourceNextElement:d.next(),sourceList:d.parent(),targetNextElement:d.next(),targetList:d.parent(),type:b.type,dropped:!1,startX:b.pageX,startY:b.pageY,startTime:(new Date).getTime()},a(m.listSelector).addClass(m.targetListClass);var e=d.offset();d.addClass(m.currentPositionClass),i={x:e.left-b.pageX,y:e.top-b.pageY},h=a();var f=o;setTimeout(function(){null!==n&&"click"!==n.type&&"keypress"!==n.type&&o===f&&s()},500),a("body").on("mousemove touchmove mouseup touchend",x),a("body").on("keypress",B),m.autoScroll&&c.start(function(){a("body").trigger("mousemove")}),A("dragstart")}}},s=function(){h=n.element.clone(),n.sourceList.append(h),h.removeAttr("id").removeClass(m.currentPositionClass).addClass(m.isDraggedClass).css({position:"fixed"}),h.offset({top:i.y+j.pageY,left:i.x+j.pageX})},t=function(b){if(!("keypress"===b.type&&13!==b.originalEvent.keyCode&&32!==b.originalEvent.keyCode||null!==n&&"click"===n.type)){b.preventDefault(),b.stopPropagation(),m=b.data.params;var c=a(b.currentTarget).closest(m.listSelector),d=c.children().filter(function(){return a.contains(this,b.currentTarget)});d.length&&(o++,n={element:d,sourceNextElement:d.next(),sourceList:c,targetNextElement:d.next(),targetList:c,dropped:!1,type:b.type,startTime:(new Date).getTime()},A("dragstart"),H())}},u=function(a,b,c){if(!c.length)return null;var d=c[0],e=0,f=d.getBoundingClientRect(),g=b-(f.top+window.scrollY),h=a-(f.left+window.scrollX);return h>=-e&&h<=f.width+e&&g>=-e&&g<=f.height+e?{x:h,y:g,xRatio:f.width?h/f.width:0,yRatio:f.height?g/f.height:0}:null},v=function(){return!h||!h.length||this!==h[0]},w=function(a){var b=m.isHorizontal;return b===!0||b===!1?b:b(a)},x=function(b){q(b),h.offset({top:-1e3,left:-1e3});var c=a(document.elementFromPoint(b.clientX,b.clientY)),d=c.closest("."+m.targetListClass+" > :not(."+m.isDraggedClass+")"),e=c.closest("."+m.targetListClass);if(a("."+m.overElementClass).removeClass(m.overElementClass),d.addClass(m.overElementClass),h.offset({top:i.y+b.pageY,left:i.x+b.pageX}),e.length&&!e.children().filter(v).length)y(e,a());else if(1===d.length&&!n.element.find(d[0]).length){var f=u(b.pageX,b.pageY,d);if(f){var g=d.parent(),j=w(g)?f.xRatio:f.yRatio,k=d.find("."+m.targetListClass),l=function(){return this!==n.element[0]},o=!k.children().filter(v).filter(l).length;k.length&&o&&j>.2&&j<.8?y(k,a()):j>.5?y(g,d.next().filter(v)):y(g,d)}}"mouseup"!==b.type&&"touchend"!==b.type||(n.endX=b.pageX,n.endY=b.pageY,n.endTime=(new Date).getTime(),n.dropped=!0,A("drop"),z())},y=function(a,b){var c=n.element;b.length&&b[0]===c[0]||a[0]===n.targetList[0]&&b.length===n.targetNextElement.length&&b[0]===n.targetNextElement[0]||(b.length?a[0].insertBefore(c[0],b[0]):h&&h.parent().length&&h.parent()[0]===a[0]?a[0].insertBefore(c[0],h[0]):a[0].appendChild(c[0]),n.targetList=a,n.targetNextElement=b,A("drag"))},z=function(){p(),m.autoScroll&&c.stop(),a("body").off("mousemove touchmove mouseup touchend",x),a("body").off("keypress",B),A("dragend"),n=null},A=function(a){n.element.trigger("sortablelist-"+a,n)},B=function(a){"keypress"===a.type&&27===a.originalEvent.keyCode&&(y(n.sourceList,n.sourceNextElement),z())},C=function(b){var c=b;return"object"==typeof b&&b.hasOwnProperty("then")||(c=a.Deferred(),c.resolve(b)),c},D=function(a){return C(m.elementNameCallback(a))},E=function(a,b){return C(m.destinationNameCallback(a,b))},F=function(a){return C(m.moveDialogueTitleCallback(a))},G=function(b){var c=[],d=a(m.listSelector),e=a("