diff --git a/ui (gen4)/features/meta.js b/ui (gen4)/features/meta.js
index 2ba1ade3..cb570ecd 100755
--- a/ui (gen4)/features/meta.js	
+++ b/ui (gen4)/features/meta.js	
@@ -63,7 +63,6 @@ core.ImageGridFeatures.Feature('viewer-testing', [
 	'app-control',
 
 	// chrome...
-	'ui-direct-control',
 	'ui-animation',
 	'ui-bounds-indicators',
 	'ui-current-image-indicator',
@@ -77,6 +76,9 @@ core.ImageGridFeatures.Feature('viewer-testing', [
 	'ui-browse-actions',
 		'ui-widget-test',
 
+	//'ui-direct-control-jquery',
+	'ui-direct-control-gsap',
+
 	// experimental and optional features...
 	//'auto-single-image',
 	
diff --git a/ui (gen4)/features/ui.js b/ui (gen4)/features/ui.js
index c96468e7..5a9fc57f 100755
--- a/ui (gen4)/features/ui.js	
+++ b/ui (gen4)/features/ui.js	
@@ -2386,48 +2386,85 @@ module.AutoSingleImage = core.ImageGridFeatures.Feature({
 // XXX add pinch-zoom...
 // XXX add vertical scroll...
 // XXX BUG: current image indicator gets shown in random places...
-var DirectControl = 
-module.DirectControl = core.ImageGridFeatures.Feature({
+var DirectControljQ = 
+module.DirectControljQ = core.ImageGridFeatures.Feature({
 	title: '',
 	doc: '',
 
-	tag: 'ui-direct-control',
+	tag: 'ui-direct-control-jquery',
 	depends: [
 		'ui',
 		// this is only used to trigger reoad...
 		//'ui-partial-ribbons',
 	],
 
-	/*
-	config: {
-		'ui-direct-control-engines': [
-			'none',
-			'jquery',
-		],
-		'ui-direct-control-engine': 'jquery',
-	},
-
-	actions: actions.Actions({
-		toggleDirectControlEngine: ['Interface/',
-			base.makeConfigToggler('ui-direct-control-engine', 
-				function(){ return this.config['ui-direct-control-engines'] })],
-	}),
-	*/
-
+	// XXX add setup/taredown...
 	handlers: [
+		// setup click targets...
+		// XXX click only if we did not drag...
+		['updateImage', 
+			function(res, gid){
+				var that = this
+				var img = this.ribbons.getImage(gid)
+
+				// set the clicker only once...
+				if(!img.prop('clickable')){
+					var x, y
+					img
+						.prop('clickable', true)
+						.on('mousedown touchstart', function(){ 
+							x = event.clientX
+							y = event.clientY
+							t = Date.now()
+						})
+						.on('mouseup touchend', function(){ 
+							if(x != null 
+								&& Math.max(
+									Math.abs(x - event.clientX), 
+									Math.abs(y - event.clientY)) < 5){
+								// this will prevent double clicks...
+								x = null
+								y = null
+								that.focusImage(that.ribbons.getElemGID($(this)))
+							}
+						})
+				}
+			}],
+
+		// setup ribbon dragging...
+		// XXX this is really sloooooow...
 		// XXX hide current image indicator as soon as the image is not visible...
 		// XXX inertia...
 		// XXX limit scroll to at least one image being on screen (center?)...
-		// XXX add setup/taredown...
 		['updateRibbon', 
 			function(_, target){
 				var that = this
 				var r = this.ribbons.getRibbon(target)
 
+				var scale = 1
+
+				// setup dragging...
 				r.length > 0 
 					&& !r.hasClass('ui-draggable')
 					&& r.draggable({
 						axis: 'x',
+
+						start: function(evt, ui){
+							scale = that.ribbons.getScale()	
+						},
+						// compensate for ribbon scale...
+						drag: function(evt, ui) {
+							// compensate for scale...
+							ui.position = {
+								left: ui.originalPosition.left 
+									+ (ui.position.left 
+										- ui.originalPosition.left) / scale,
+								top: ui.originalPosition.top 
+									+ (ui.position.top 
+										- ui.originalPosition.top) / scale,
+							}
+						},
+
 						stop: function(){
 							var c = that.ribbons.getImageByPosition('center', r)
 							that
@@ -2441,6 +2478,92 @@ module.DirectControl = core.ImageGridFeatures.Feature({
 })
 
 
+var DirectControlGSAP = 
+module.DirectControlGSAP = core.ImageGridFeatures.Feature({
+	title: '',
+	doc: '',
+
+	tag: 'ui-direct-control-gsap',
+	depends: [
+		'ui',
+		// this is only used to trigger reoad...
+		//'ui-partial-ribbons',
+	],
+
+	// XXX add setup/taredown...
+	handlers: [
+		// setup click targets...
+		// XXX click only if we did not drag...
+		['updateImage', 
+			function(res, gid){
+				var that = this
+				var img = this.ribbons.getImage(gid)
+
+				// set the clicker only once...
+				if(!img.prop('clickable')){
+					var x, y
+					img
+						.prop('clickable', true)
+						.on('mousedown touchstart', function(){ 
+							x = event.clientX
+							y = event.clientY
+							t = Date.now()
+						})
+						.on('mouseup touchend', function(){ 
+							if(x != null 
+								&& Math.max(
+									Math.abs(x - event.clientX), 
+									Math.abs(y - event.clientY)) < 5){
+								// this will prevent double clicks...
+								x = null
+								y = null
+								that.focusImage(that.ribbons.getElemGID($(this)))
+							}
+						})
+				}
+			}],
+
+		// setup ribbon dragging...
+		// XXX fast but uses messes up positioning...
+		// 		...setting type: 'left' will fix this but make things 
+		// 		really slow (as slow as jQuery.ui.draggable(..))...
+		['updateRibbon', 
+			function(_, target){
+				var that = this
+				var r = this.ribbons.getRibbon(target)
+
+				// setup dragging...
+				if(r.length > 0 && !r.hasClass('draggable')){
+					r.addClass('draggable')
+
+					var o
+
+					Draggable.create(r, {
+						type: 'x',
+						onDragStart: function(){
+							o = r.position().left
+						},
+						onDragEnd: function(){
+							var l = r.position().left
+							l += o - l
+
+							that.ribbons.preventTransitions(r)
+							r[0].style.left = l
+							r[0].style.transform = 'translate3d(0, 0, 0)'
+							that.ribbons.restoreTransitions(r)
+
+							var c = that.ribbons.getImageByPosition('center', r)
+							that
+								.updateRibbon(c)
+								// XXX is this correct???
+								//.updateCurrentImageIndicator()
+						}})
+				}
+			}],
+	],
+})
+
+
 
 
 //---------------------------------------------------------------------
diff --git a/ui (gen4)/index.html b/ui (gen4)/index.html
index 06909fc5..b7a3f3c1 100755
--- a/ui (gen4)/index.html	
+++ b/ui (gen4)/index.html	
@@ -177,6 +177,12 @@ typeof(require) != 'undefined' && require('nw.gui').Window.get().showDevTools()
 
 
 
+
+
+
+
+
+
 
 
 
diff --git a/ui (gen4)/lib/util.js b/ui (gen4)/lib/util.js
index 9a3b424f..5155b8d2 100755
--- a/ui (gen4)/lib/util.js	
+++ b/ui (gen4)/lib/util.js	
@@ -38,7 +38,7 @@ function(path){
 		//.map(encodeURIComponent)
 		.join('/')
 		// NOTE: keep '%' the first...
-		.replace(/%/g, '%25')
+		//.replace(/%/g, '%25')
 		.replace(/#/g, '%23')
 		.replace(/&/g, '%26'))
 }
diff --git a/ui (gen4)/ribbons.js b/ui (gen4)/ribbons.js
index 1cc07005..7a4d5d77 100755
--- a/ui (gen4)/ribbons.js	
+++ b/ui (gen4)/ribbons.js	
@@ -485,7 +485,7 @@ var RibbonsPrototype = {
 	// Set ribbon set scale...
 	//
 	// 	.setScale()
-	// 	.setScale(, )
+	// 	.setScale(, |'current'|'closest')
 	// 	.setScale(, 'top'|'center'|'bottom'||%, 'left'|'center'|'right'||%)
 	// 		-> 
 	//
@@ -499,13 +499,20 @@ var RibbonsPrototype = {
 			return this
 		}
 
-		if(t != null && l != null){
-			this.setOrigin(t, l)
+		// default origin -- center...
+		if(t == null && l == null){
+			this.setOrigin('center', 'center')
 
+		// an image...
+		} else if(l == null){
+			t = t == 'current' ? this.getImage()
+				: t == 'closest' ? this.getImageByPosition()
+				: t
+			this.setOrigin(t)
+
+		// explicit...
 		} else {
-			var img = t == null ? this.getImage() : t
-
-			this.setOrigin(img)
+			this.setOrigin(t, l)
 		}
 
 		setElementScale(ribbon_set, scale)