From ed2ca5308b27b09edb65462251b7f2ac3b9e6e46 Mon Sep 17 00:00:00 2001 From: alekseybobkov Date: Tue, 24 Mar 2015 20:47:49 -0700 Subject: [PATCH] Implemented (draft) support for touch devices. Minor fixes. Reworked the proxy implementation in the base class. The Media Manager JS class now uses the base class. --- modules/backend/assets/js/october-min.js | 19 +- .../assets/js/october.foundation.baseclass.js | 59 ++--- modules/backend/assets/js/october.js | 2 +- modules/cms/controllers/media/index.htm | 2 +- .../mediamanager/assets/css/mediamanager.css | 45 +++- .../mediamanager/assets/js/mediamanager.js | 218 +++++++++--------- .../assets/less/mediamanager.less | 52 ++++- .../widgets/mediamanager/partials/_body.htm | 2 +- .../mediamanager/partials/_toolbar.htm | 38 +-- 9 files changed, 252 insertions(+), 185 deletions(-) diff --git a/modules/backend/assets/js/october-min.js b/modules/backend/assets/js/october-min.js index db2e33ae3..261daf430 100644 --- a/modules/backend/assets/js/october-min.js +++ b/modules/backend/assets/js/october-min.js @@ -69,7 +69,24 @@ if($.oc===undefined) $.oc={} $.oc.escapeHtmlString=function(string){var htmlEscapes={'&':'&','<':'<','>':'>','"':'"',"'":''','/':'/'},htmlEscaper=/[&<>"'\/]/g return(''+string).replace(htmlEscaper,function(match){return htmlEscapes[match];})} -+function($){"use strict";var TriggerOn=function(element,options){var $el=this.$el=$(element);this.options=options||{};if(this.options.triggerType!==false&&this.options.triggerAction===false)this.options.triggerAction=this.options.triggerType ++function($){"use strict";if($.oc===undefined) +$.oc={} +if($.oc.foundation===undefined) +$.oc.foundation={} +var Base=function(){this.proxiedMethods=[] +this.proxyCounter=0} +Base.prototype.dispose=function() +{for(var index in this.proxiedMethods) +this.proxiedMethods[index]=null +this.proxiedMethods=null} +Base.prototype.proxy=function(method){if(method.ocProxyId!==undefined){if(this.proxiedMethods[method.ocProxyId]===undefined) +throw new Error('Proxied method is not found in the proxy method scope.') +return this.proxiedMethods[method.ocProxyId]} +this.proxyCounter++ +method.ocProxyId=this.proxyCounter +this.proxiedMethods[method.ocProxyId]=method.bind(this) +return this.proxiedMethods[method.ocProxyId]} +$.oc.foundation.base=Base;}(window.jQuery);+function($){"use strict";var TriggerOn=function(element,options){var $el=this.$el=$(element);this.options=options||{};if(this.options.triggerType!==false&&this.options.triggerAction===false)this.options.triggerAction=this.options.triggerType if(this.options.triggerCondition===false) throw new Error('Trigger condition is not specified.') if(this.options.trigger===false) diff --git a/modules/backend/assets/js/october.foundation.baseclass.js b/modules/backend/assets/js/october.foundation.baseclass.js index ffb5d09d0..37197e7fe 100644 --- a/modules/backend/assets/js/october.foundation.baseclass.js +++ b/modules/backend/assets/js/october.foundation.baseclass.js @@ -2,17 +2,11 @@ * Base class for OctoberCMS back-end classes. * * The class defines base functionality for dealing with memory management - * and cleaning up bound (proxied) methods, references to the DOM elements - * and timers. + * and cleaning up bound (proxied) methods. * - * The class should be used as a parent class for JavaScript classes that - * handle DOM events and require binding and unbinding methods to the events. - * That is especially important for classes that should free the memory during - * a single page execution. - * - * The base class defines the dispose method that cleans up references to the DOM - * elements and proxied methods. If child classes implement their own dispose() - * method, they should call the base class dispose method (see the example below). + * The base class defines the dispose method that cleans up proxied methods. + * If child classes implement their own dispose() method, they should call + * the base class dispose method (see the example below). * * Use the simple parasitic combination inheritance pattern to create child classes: * @@ -21,7 +15,7 @@ * * var SubClass = function(params) { * // Call the parent constructor - * Base.call() + * Base.call(this) * } * * SubClass.prototype = Object.create(BaseProto) @@ -51,45 +45,34 @@ var Base = function() { this.proxiedMethods = [] - this.domReferences = [] - this.timers = [] + + this.proxyCounter = 0 } Base.prototype.dispose = function() { - for (var proxyName in this.proxiedMethods) - this.proxiedMethods[proxyName] = null + for (var index in this.proxiedMethods) + this.proxiedMethods[index] = null - for (var domReference in this.domReferences) - this.domReferences[domReference] = null - - for (var timer in this.timers) { - clearTimeout(this.timers[timer]) - this.timers[timer] = null - } + this.proxiedMethods = null } /* * Creates a proxied method reference or returns an existing proxied method. */ - Base.prototype.proxyMethod = function(method, proxyName) { - if (this.proxiedMethods[proxyName] === undefined) - return this.proxiedMethods[proxyName] = method.bind(this) + Base.prototype.proxy = function(method) { + if (method.ocProxyId !== undefined) { + if (this.proxiedMethods[method.ocProxyId] === undefined) + throw new Error('Proxied method is not found in the proxy method scope.') - return this.proxiedMethods[proxyName] - } - - Base.prototype.setTimeout = function(timerName, method, delay) { - this.clearTimeout(timerName) - - this.timers[timerName] = setTimeout(method, delay) - } - - Base.prototype.clearTimeout = function(timerName) { - if (this.timers[timerName] !== undefined) { - clearTimeout(this.timers[timerName]) - this.timers[timerName] = null + return this.proxiedMethods[method.ocProxyId] } + + this.proxyCounter++ + method.ocProxyId = this.proxyCounter + this.proxiedMethods[method.ocProxyId] = method.bind(this) + + return this.proxiedMethods[method.ocProxyId] } $.oc.foundation.base = Base; diff --git a/modules/backend/assets/js/october.js b/modules/backend/assets/js/october.js index 062303104..5c4ee0ed1 100644 --- a/modules/backend/assets/js/october.js +++ b/modules/backend/assets/js/october.js @@ -9,6 +9,7 @@ =require october.controls.js =require october.utils.js +=require october.foundation.baseclass.js =require october.triggerapi.js =require october.dragscroll.js =require october.dragvalue.js @@ -45,5 +46,4 @@ =require october.autocomplete.js =require october.callout.js =require october.sidenav-tree.js - */ diff --git a/modules/cms/controllers/media/index.htm b/modules/cms/controllers/media/index.htm index 3cc13640e..d6f5048c2 100644 --- a/modules/cms/controllers/media/index.htm +++ b/modules/cms/controllers/media/index.htm @@ -1,7 +1,7 @@ - 'layout']) ?> + 'layout', 'onsubmit'=>'return false']) ?> widget->manager->render() ?> \ No newline at end of file diff --git a/modules/cms/widgets/mediamanager/assets/css/mediamanager.css b/modules/cms/widgets/mediamanager/assets/css/mediamanager.css index 60af031a4..847e1d0c7 100644 --- a/modules/cms/widgets/mediamanager/assets/css/mediamanager.css +++ b/modules/cms/widgets/mediamanager/assets/css/mediamanager.css @@ -26,7 +26,8 @@ div[data-control="media-manager"] p.thumbnail-error-message { color: #bdc3c7; } div[data-control="media-manager"] .media-list { - padding: 0 0 20px 20px; + padding: 0 0 0 20px; + margin: 0; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; @@ -311,6 +312,7 @@ div[data-control="media-manager"] [data-control="item-list"] { } div[data-control="media-manager"] table.table { table-layout: fixed; + margin-bottom: 0; white-space: nowrap; } div[data-control="media-manager"] table.table div.no-wrap-text { @@ -340,7 +342,7 @@ div[data-control="media-manager"] table.table tr[data-item-type=folder] i.icon-f } div[data-control="media-manager"] div[data-control="selection-marker"] { position: absolute; - z-index: 50; + z-index: 250; border: 1px dashed #95a5a6; } div[data-control="media-manager"] .upload-progress { @@ -400,17 +402,17 @@ body:not(.no-select) div[data-control="media-manager"] .media-list.tiles li:hove body:not(.no-select) div[data-control="media-manager"] .media-list.tiles li:hover h4 { color: #2581b8; } -body:not(.no-select) div[data-control="media-manager"] .media-list .list li:hover { +body:not(.no-select) div[data-control="media-manager"] .media-list .list li:hover:not(:active) { background: #4da7e8 !important; } -body:not(.no-select) div[data-control="media-manager"] .media-list .list li:hover i, -body:not(.no-select) div[data-control="media-manager"] .media-list .list li:hover p.size { +body:not(.no-select) div[data-control="media-manager"] .media-list .list li:hover:not(:active) i, +body:not(.no-select) div[data-control="media-manager"] .media-list .list li:hover:not(:active) p.size { color: #ecf0f1; } -body:not(.no-select) div[data-control="media-manager"] .media-list .list li:hover h4 { +body:not(.no-select) div[data-control="media-manager"] .media-list .list li:hover:not(:active) h4 { color: white; } -body:not(.no-select) div[data-control="media-manager"] .media-list .list li:hover .icon-container { +body:not(.no-select) div[data-control="media-manager"] .media-list .list li:hover:not(:active) .icon-container { border-right-color: #4da7e8 !important; } @media (max-width: 1280px) { @@ -418,3 +420,32 @@ body:not(.no-select) div[data-control="media-manager"] .media-list .list li:hove width: 230px; } } +@media (max-width: 1024px) { + div[data-control="media-manager"] .media-list.list li { + display: block; + width: auto; + } +} +@media (max-width: 768px) { + div[data-control="media-manager"] [data-control="preview-sidebar"], + div[data-control="media-manager"] [data-command="toggle-sidebar"] { + display: none!important; + } + div[data-control="media-manager"] .media-list.list { + padding: 0; + } + div[data-control="media-manager"] .media-list.list li { + -webkit-border-radius: 0; + -moz-border-radius: 0; + border-radius: 0; + margin: 0; + border-right: none; + border-left: none; + border-bottom: none; + } +} +@media (max-width: 480px) { + div[data-control="media-manager"] [data-control="left-sidebar"] { + display: none!important; + } +} diff --git a/modules/cms/widgets/mediamanager/assets/js/mediamanager.js b/modules/cms/widgets/mediamanager/assets/js/mediamanager.js index aa905d49e..294ba3126 100644 --- a/modules/cms/widgets/mediamanager/assets/js/mediamanager.js +++ b/modules/cms/widgets/mediamanager/assets/js/mediamanager.js @@ -6,6 +6,9 @@ */ +function ($) { "use strict"; + var Base = $.oc.foundation.base, + BaseProto = Base.prototype + // MEDIA MANAGER CLASS DEFINITION // ============================ @@ -15,40 +18,7 @@ this.options = options - // Event handlers - this.navigateHandler = this.onNavigate.bind(this) - this.commandClickHandler = this.onCommandClick.bind(this) - this.itemClickHandler = this.onItemClick.bind(this) - this.listMouseDownHandler = this.onListMouseDown.bind(this) - this.listMouseUpHandler = this.onListMouseUp.bind(this) - this.listMouseMoveHandler = this.onListMouseMove.bind(this) - this.sortingChangedHandler = this.onSortingChanged.bind(this) - this.searchChangedHandler = this.onSearchChanged.bind(this) - this.folderPopupShownHandler = this.onFolderPopupShown.bind(this) - this.newFolderSubmitHandler = this.onNewFolderSubmit.bind(this) - this.folderPopupHiddenHandler = this.onFolderPopupHidden.bind(this) - this.movePopupShownHandler = this.onMovePopupShown.bind(this) - this.moveItemsSubmitHandler = this.onMoveItemsSubmit.bind(this) - this.movePopupHiddenHandler = this.onMovePopupHidden.bind(this) - - // Instance-bound methods - this.updateSidebarPreviewBound = this.updateSidebarPreview.bind(this) - this.replacePlaceholderBound = this.replacePlaceholder.bind(this) - this.placeholdersUpdatedBound = this.placeholdersUpdated.bind(this) - this.afterNavigateBound = this.afterNavigate.bind(this) - this.releaseSidebarThumbnailAjaxBound = this.releaseSidebarThumbnailAjax.bind(this) - this.replaceSidebarPlaceholderBound = this.replaceSidebarPlaceholder.bind(this) - this.uploadFileAddedBound = this.uploadFileAdded.bind(this) - this.uploadUpdateTotalProgressBound = this.uploadUpdateTotalProgress.bind(this) - this.uploadQueueCompleteBound = this.uploadQueueComplete.bind(this) - this.uploadSendingBound = this.uploadSending.bind(this) - this.uploadErrorBound = this.uploadError.bind(this) - this.updateSearchResultsBound = this.updateSearchResults.bind(this) - this.releaseNavigationAjaxBound = this.releaseNavigationAjax.bind(this) - this.deleteConfirmationBound = this.deleteConfirmation.bind(this) - this.refreshBound = this.refresh.bind(this) - this.folderCreatedBound = this.folderCreated.bind(this) - this.itemsMovedBound = this.itemsMoved.bind(this) + Base.call(this) // State properties this.selectTimer = null @@ -61,6 +31,8 @@ this.dropzone = null this.searchTrackInputTimer = null this.navigationAjax = null + this.dblTouchTimer = null + this.dblTouchFlag = null // // Initialization @@ -69,32 +41,19 @@ this.init() } + MediaManager.prototype = Object.create(BaseProto) + MediaManager.prototype.constructor = MediaManager + MediaManager.prototype.dispose = function() { this.unregisterHandlers() this.clearSelectTimer() this.disableUploader() this.clearSearchTrackInputTimer() this.releaseNavigationAjax() + this.clearDblTouchTimer() this.$el = null this.$form = null - this.updateSidebarPreviewBound = null - this.replacePlaceholderBound = null - this.placeholdersUpdatedBound = null - this.afterNavigateBound = null - this.replaceSidebarPlaceholderBound = null - this.uploadFileAddedBound = null - this.releaseSidebarThumbnailAjaxBound = null - this.uploadUpdateTotalProgressBound = null - this.uploadQueueCompleteBound = null - this.uploadSendingBound = null - this.uploadErrorBound = null - this.updateSearchResultsBound = null - this.releaseNavigationAjaxBound = null - this.deleteConfirmationBound = null - this.refreshBound = null - this.folderCreatedBound = null - this.itemsMovedBound = null this.sidebarPreviewElement = null this.itemListElement = null @@ -102,6 +61,8 @@ this.selectionMarker = null this.thumbnailQueue = [] this.navigationAjax = null + + BaseProto.dispose.call(this) } // MEDIA MANAGER INTERNAL METHODS @@ -117,54 +78,54 @@ } MediaManager.prototype.registerHandlers = function() { - this.$el.on('dblclick', this.navigateHandler) - this.$el.on('click.tree-path', 'ul.tree-path, [data-control="sidebar-labels"]', this.navigateHandler) - this.$el.on('click.command', '[data-command]', this.commandClickHandler) - this.$el.on('click.item', '[data-type="media-item"]', this.itemClickHandler) - this.$el.on('change', '[data-control="sorting"]', this.sortingChangedHandler) - this.$el.on('keyup', '[data-control="search"]', this.searchChangedHandler) - this.$el.on('mediarefresh', this.refreshBound) - this.$el.on('shown.oc.popup', '[data-command="create-folder"]', this.folderPopupShownHandler) - this.$el.on('hidden.oc.popup', '[data-command="create-folder"]', this.folderPopupHiddenHandler) - this.$el.on('shown.oc.popup', '[data-command="move"]', this.movePopupShownHandler) - this.$el.on('hidden.oc.popup', '[data-command="move"]', this.movePopupHiddenHandler) + this.$el.on('dblclick', this.proxy(this.onNavigate)) + this.$el.on('click.tree-path', 'ul.tree-path, [data-control="sidebar-labels"]', this.proxy(this.onNavigate)) + + this.$el.on('click.command', '[data-command]', this.proxy(this.onCommandClick)) + + // Touch devices use double-tap for the navigation and single tap for selecting. + // Another option is checkboxes visible only on touch devices, but this approach + // will require more significant changes in the code for the touch device support. + if (!Modernizr.touch) + this.$el.on('click.item', '[data-type="media-item"]', this.proxy(this.onItemClick)) + else + this.$el.on('touchend', '[data-type="media-item"]', this.proxy(this.onItemTouch)) + + this.$el.on('change', '[data-control="sorting"]', this.proxy(this.onSortingChanged)) + this.$el.on('keyup', '[data-control="search"]', this.proxy(this.onSearchChanged)) + this.$el.on('mediarefresh', this.proxy(this.refresh)) + this.$el.on('shown.oc.popup', '[data-command="create-folder"]', this.proxy(this.onFolderPopupShown)) + this.$el.on('hidden.oc.popup', '[data-command="create-folder"]', this.proxy(this.onFolderPopupHidden)) + this.$el.on('shown.oc.popup', '[data-command="move"]', this.proxy(this.onMovePopupShown)) + this.$el.on('hidden.oc.popup', '[data-command="move"]', this.proxy(this.onMovePopupHidden)) if (this.itemListElement) - this.itemListElement.addEventListener('mousedown', this.listMouseDownHandler) + this.itemListElement.addEventListener('mousedown', this.proxy(this.onListMouseDown)) } MediaManager.prototype.unregisterHandlers = function() { - this.$el.off('dblclick', this.navigateHandler) - this.$el.off('click.tree-path', this.navigateHandler) - this.$el.off('click.command', this.commandClickHandler) - this.$el.off('click.item', this.itemClickHandler) - this.$el.off('change', '[data-control="sorting"]', this.sortingChangedHandler) - this.$el.off('keyup', '[data-control="search"]', this.searchChangedHandler) - this.$el.off('shown.oc.popup', '[data-command="create-folder"]', this.folderPopupShownHandler) - this.$el.off('hidden.oc.popup', '[data-command="create-folder"]', this.folderPopupHiddenHandler) - this.$el.off('shown.oc.popup', '[data-command="move"]', this.movePopupShownHandler) - this.$el.off('hidden.oc.popup', '[data-command="move"]', this.movePopupHiddenHandler) + this.$el.off('dblclick', this.proxy(this.onNavigate)) + this.$el.off('click.tree-path', this.proxy(this.onNavigate)) + this.$el.off('click.command', this.proxy(this.onCommandClick)) + + if (!Modernizr.touch) + this.$el.off('click.item', this.proxy(this.onItemClick)) + else + this.$el.off('touchend', '[data-type="media-item"]', this.proxy(this.onItemTouch)) + + this.$el.off('change', '[data-control="sorting"]', this.proxy(this.onSortingChanged)) + this.$el.off('keyup', '[data-control="search"]', this.proxy(this.onSearchChanged)) + this.$el.off('shown.oc.popup', '[data-command="create-folder"]', this.proxy(this.onFolderPopupShown)) + this.$el.off('hidden.oc.popup', '[data-command="create-folder"]', this.proxy(this.onFolderPopupHidden)) + this.$el.off('shown.oc.popup', '[data-command="move"]', this.proxy(this.onMovePopupShown)) + this.$el.off('hidden.oc.popup', '[data-command="move"]', this.proxy(this.onMovePopupHidden)) if (this.itemListElement) { - this.itemListElement.removeEventListener('mousedown', this.listMouseDownHandler) - this.itemListElement.removeEventListener('mousemove', this.listMouseMoveHandler) + this.itemListElement.removeEventListener('mousedown', this.proxy(this.onListMouseDown)) + this.itemListElement.removeEventListener('mousemove', this.proxy(this.onListMouseMove)) } - document.removeEventListener('mouseup', this.listMouseUpHandler) - this.navigateHandler = null - this.commandClickHandler = null - this.itemClickHandler = null - this.listMouseDownHandler = null - this.listMouseUpHandler = null - this.listMouseMoveHandler = null - this.sortingChangedHandler = null - this.searchChangedHandler = null - this.folderPopupShownHandler = null - this.folderPopupHiddenHandler = null - this.newFolderSubmitHandler = null - this.movePopupShownHandler = null - this.moveItemsSubmitHandler = null - this.movePopupHiddenHandler = null + document.removeEventListener('mouseup', this.proxy(this.onListMouseUp)) } MediaManager.prototype.changeView = function(view) { @@ -224,10 +185,22 @@ if (this.isPreviewSidebarVisible()) { // Use the timeout to prevent too many AJAX requests // when the selection changes too quickly (with the keyboard arrows) - this.selectTimer = setTimeout(this.updateSidebarPreviewBound, 100) + this.selectTimer = setTimeout(this.proxy(this.updateSidebarPreview), 100) } } + MediaManager.prototype.clearDblTouchTimer = function() { + if (this.dblTouchTimer === null) + return + + clearTimeout(this.dblTouchTimer) + this.dblTouchTimer = null + } + + MediaManager.prototype.clearDblTouchFlag = function() { + this.dblTouchFlag = false + } + // // Navigation // @@ -274,8 +247,8 @@ }).always(function() { $.oc.stripeLoadIndicator.hide() }) - .done(this.afterNavigateBound) - .always(this.releaseNavigationAjaxBound) + .done(this.proxy(this.afterNavigate)) + .always(this.proxy(this.releaseNavigationAjax)) } MediaManager.prototype.releaseNavigationAjax = function() { @@ -419,8 +392,8 @@ this.sidebarThumbnailAjax = this.$form.request(this.options.alias+'::onGetSidebarThumbnail', { data: data }) - .done(this.replaceSidebarPlaceholderBound) - .always(this.releaseSidebarThumbnailAjaxBound) + .done(this.proxy(this.replaceSidebarPlaceholder)) + .always(this.proxy(this.releaseSidebarThumbnailAjax)) } MediaManager.prototype.replaceSidebarPlaceholder = function(response) { @@ -474,7 +447,7 @@ this.activeThumbnailQueueLength++ - this.handleThumbnailBatch(batch).always(this.placeholdersUpdatedBound) + this.handleThumbnailBatch(batch).always(this.proxy(this.placeholdersUpdated)) } } @@ -493,7 +466,7 @@ data: data }) - promise.done(this.replacePlaceholderBound) + promise.done(this.proxy(this.replacePlaceholder)) return promise } @@ -615,11 +588,11 @@ } this.dropzone = new Dropzone(this.$el.get(0), uploaderOptions) - this.dropzone.on('addedfile', this.uploadFileAddedBound) - this.dropzone.on('totaluploadprogress', this.uploadUpdateTotalProgressBound) - this.dropzone.on('queuecomplete', this.uploadQueueCompleteBound) - this.dropzone.on('sending', this.uploadSendingBound) - this.dropzone.on('error', this.uploadErrorBound) + this.dropzone.on('addedfile', this.proxy(this.uploadFileAdded)) + this.dropzone.on('totaluploadprogress', this.proxy(this.uploadUpdateTotalProgress)) + this.dropzone.on('queuecomplete', this.proxy(this.uploadQueueComplete)) + this.dropzone.on('sending', this.proxy(this.uploadSending)) + this.dropzone.on('error', this.proxy(this.uploadError)) } MediaManager.prototype.disableUploader = function() { @@ -737,7 +710,7 @@ this.clearSearchTrackInputTimer() - this.searchTrackInputTimer = window.setTimeout(this.updateSearchResultsBound, 300) + this.searchTrackInputTimer = window.setTimeout(this.proxy(this.updateSearchResults), 300) } // @@ -760,7 +733,7 @@ title: this.options.deleteConfirm, confirmButtonClass: 'btn-default', showCancelButton: true - }, this.deleteConfirmationBound) + }, this.proxy(this.deleteConfirmation)) } MediaManager.prototype.deleteConfirmation = function(confirmed) { @@ -786,7 +759,7 @@ data: data }).always(function() { $.oc.stripeLoadIndicator.hide() - }).done(this.afterNavigateBound) + }).done(this.proxy(this.afterNavigate)) } MediaManager.prototype.createFolder = function(ev) { @@ -797,7 +770,7 @@ MediaManager.prototype.onFolderPopupShown = function(ev, button, popup) { $(popup).find('input[name=name]').focus() - $(popup).on('submit.media', 'form', this.newFolderSubmitHandler) + $(popup).on('submit.media', 'form', this.proxy(this.onNewFolderSubmit)) } MediaManager.prototype.onFolderPopupHidden = function(ev, button, popup) { @@ -815,7 +788,7 @@ data: data }).always(function() { $.oc.stripeLoadIndicator.hide() - }).done(this.folderCreatedBound) + }).done(this.proxy(this.folderCreated)) ev.preventDefault() return false @@ -824,7 +797,7 @@ MediaManager.prototype.folderCreated = function() { this.$el.find('button[data-command="create-folder"]').popup('hide') - this.afterNavigateBound + this.afterNavigate() } MediaManager.prototype.moveItems = function(ev) { @@ -859,7 +832,7 @@ } MediaManager.prototype.onMovePopupShown = function(ev, button, popup) { - $(popup).on('submit.media', 'form', this.moveItemsSubmitHandler) + $(popup).on('submit.media', 'form', this.proxy(this.onMoveItemsSubmit)) } MediaManager.prototype.onMoveItemsSubmit = function(ev) { @@ -886,7 +859,7 @@ data: data }).always(function() { $.oc.stripeLoadIndicator.hide() - }).done(this.itemsMovedBound) + }).done(this.proxy(this.itemsMoved)) ev.preventDefault() return false @@ -899,7 +872,7 @@ MediaManager.prototype.itemsMoved = function() { this.$el.find('button[data-command="move"]').popup('hide') - this.afterNavigateBound + this.afterNavigate() } // EVENT HANDLERS @@ -967,9 +940,24 @@ this.selectItem(ev.currentTarget, ev.shiftKey) } + MediaManager.prototype.onItemTouch = function(ev) { + this.onItemClick(ev) + + if (this.dblTouchFlag) { + this.onNavigate(ev) + this.dblTouchFlag = null + } + else + this.dblTouchFlag = true + + this.clearDblTouchTimer() + + this.dblTouchTimer = setTimeout(this.proxy(this.clearDblTouchFlag), 300) + } + MediaManager.prototype.onListMouseDown = function(ev) { - this.itemListElement.addEventListener('mousemove', this.listMouseMoveHandler) - document.addEventListener('mouseup', this.listMouseUpHandler) + this.itemListElement.addEventListener('mousemove', this.proxy(this.onListMouseMove)) + document.addEventListener('mouseup', this.proxy(this.onListMouseUp)) var pagePosition = this.getEventPagePosition(ev), relativePosition = this.getRelativePosition(this.itemListElement, pagePosition.x, pagePosition.y) @@ -979,8 +967,8 @@ } MediaManager.prototype.onListMouseUp = function(ev) { - this.itemListElement.removeEventListener('mousemove', this.listMouseMoveHandler) - document.removeEventListener('mouseup', this.listMouseUpHandler) + this.itemListElement.removeEventListener('mousemove', this.proxy(this.onListMouseMove)) + document.removeEventListener('mouseup', this.proxy(this.onListMouseUp)) $(document.body).removeClass('no-select') if (this.selectionStarted) { diff --git a/modules/cms/widgets/mediamanager/assets/less/mediamanager.less b/modules/cms/widgets/mediamanager/assets/less/mediamanager.less index 174a13b07..67b7ceecd 100644 --- a/modules/cms/widgets/mediamanager/assets/less/mediamanager.less +++ b/modules/cms/widgets/mediamanager/assets/less/mediamanager.less @@ -85,7 +85,8 @@ div[data-control="media-manager"] { } .media-list { - padding: 0 0 20px 20px; + padding: 0 0 0 20px; + margin: 0; .user-select(none); li { @@ -366,6 +367,7 @@ div[data-control="media-manager"] { table.table { table-layout: fixed; + margin-bottom: 0; white-space: nowrap; div.no-wrap-text { @@ -401,7 +403,7 @@ div[data-control="media-manager"] { div[data-control="selection-marker"] { position: absolute; - z-index: 50; + z-index: 250; border: 1px dashed #95a5a6; } @@ -470,7 +472,7 @@ body:not(.no-select) { } .list { - li:hover { + li:hover:not(:active) { .media-selected-list(); } } @@ -487,4 +489,48 @@ body:not(.no-select) { } } } +} + +@media (max-width: 1024px) { + div[data-control="media-manager"] { + .media-list { + &.list { + li { + display: block; + + width: auto; + } + } + } + } +} + +@media (max-width: @screen-sm) { + div[data-control="media-manager"] { + [data-control="preview-sidebar"], + [data-command="toggle-sidebar"] { + display: none!important; + } + + .media-list { + &.list { + padding: 0; + li { + .border-radius(0); + margin: 0; + border-right: none; + border-left: none; + border-bottom: none; + } + } + } + } +} + +@media (max-width: 480px) { + div[data-control="media-manager"] { + [data-control="left-sidebar"] { + display: none!important; + } + } } \ No newline at end of file diff --git a/modules/cms/widgets/mediamanager/partials/_body.htm b/modules/cms/widgets/mediamanager/partials/_body.htm index 115d8b342..8abb28c64 100644 --- a/modules/cms/widgets/mediamanager/partials/_body.htm +++ b/modules/cms/widgets/mediamanager/partials/_body.htm @@ -13,7 +13,7 @@
-
+
makePartial('left-sidebar') ?>
diff --git a/modules/cms/widgets/mediamanager/partials/_toolbar.htm b/modules/cms/widgets/mediamanager/partials/_toolbar.htm index b3e6c9890..2d635badb 100644 --- a/modules/cms/widgets/mediamanager/partials/_toolbar.htm +++ b/modules/cms/widgets/mediamanager/partials/_toolbar.htm @@ -1,29 +1,31 @@
-
-
- - -
+
+
+
+ + +
- + -
- - -
+
+ + +
-
- makePartial('view-mode-buttons') ?> +
+ makePartial('view-mode-buttons') ?> +
-
-
+
+