Mootools mousewheel event, and adding an onComplete - javascript

I'm using the following class along with Mootools to create custom scrolling areas on a site. It includes a mousewheel event. I need to be able to fire an onComplete once the scroller comes to a stop after using the mousewheel. So say you swipe the mousewheel to scroll, I need to fire an oncomplete once the scrolling content comes to a stop.
Suggestions?
var ScrollBar = new Class({
Implements: [Events, Options],
options: {
wheel: (Browser.safari5) ? 1 : 20
},
initialize: function(main, options) {
this.setOptions(options);
this.dragging = false;
this.inside = false;
this.main = $(main);
this.content = this.main.getFirst();
this.vScrollbar = new Element('div', {
'class': 'scrollbar'
}).inject(this.content, 'after');
this.vTrack = new Element('div', {
'class': 'track'
}).inject(this.vScrollbar);
this.vThumb = new Element('div', {
'class': 'handle'
}).inject(this.vTrack);
this.bound = {
'vStart': this.vStart.bind(this),
'end': this.end.bind(this),
'vDrag': this.vDrag.bind(this),
'wheel': this.wheel.bind(this),
'vPage': this.vPage.bind(this)
};
// set scrollarea mousein/out hide of scrollbar
this.vScrollbar.set('tween', {
duration: 200,
transition: 'cubic:out'
});
this.main.addEvent('mouseenter', function(event){
this.inside = true;
this.vScrollbar.get('tween').cancel();
this.vScrollbar.tween('width', 12);
}.bind(this));
this.main.addEvent('mouseleave', function(event){
this.inside = false;
if (!this.dragging) {
this.vScrollbar.get('tween').cancel();
this.vScrollbar.tween('width', 0);
}
}.bind(this));
this.vPosition = {};
this.vMouse = {};
this.update();
this.attach();
this.scrollContent = new Fx.Scroll(this.content, {
duration: 500,
transition: Fx.Transitions.Cubic.easeOut,
onComplete: function(){
Blinds.updateImages();
}
});
this.scrollThumb = new Fx.Morph(this.vThumb, {
duration: 500,
transition: Fx.Transitions.Cubic.easeOut
});
},
update: function() {
var panel_id = (this.content.getFirst()) ? this.content.getFirst().get('id') : '';
if ((this.content.scrollHeight <= this.main.offsetHeight) || panel_id === 'random-doodle' || (this.content.getFirst() && this.content.getFirst().hasClass('collapsed'))) {
this.main.addClass('noscroll');
return false;
}
else { this.main.removeClass('noscroll'); }
this.vContentSize = this.content.offsetHeight;
this.vContentScrollSize = this.content.scrollHeight;
this.vTrackSize = this.vTrack.offsetHeight;
this.vContentRatio = this.vContentSize / this.vContentScrollSize;
this.vThumbSize = 200;
this.vThumb.setStyle('height', this.vThumbSize);
this.vScrollRatio = this.vContentScrollSize / (this.vTrackSize - this.vThumbSize) - this.vContentRatio * (this.vContentScrollSize / (this.vTrackSize - this.vThumbSize));
this.vUpdateThumbFromContentScroll();
this.vUpdateContentFromThumbPosition();
},
vUpdateContentFromThumbPosition: function() {
this.content.scrollTop = this.vPosition.now * this.vScrollRatio;
},
vUpdateContentFromThumbPosition2: function() {
var pos = this.vPosition.now * this.vScrollRatio;
this.scrollContent.start(0, pos);
},
vUpdateThumbFromContentScroll: function() {
this.vPosition.now = (this.content.scrollTop / this.vScrollRatio).limit(0, (this.vTrackSize - this.vThumbSize));
this.vThumb.setStyle('top', this.vPosition.now);
},
vUpdateThumbFromContentScroll2: function(pos) {
this.vPosition.now = (this.content.scrollTopNew / this.vScrollRatio).limit(0, (this.vTrackSize - this.vThumbSize));
this.scrollThumb.start({
'top': this.vPosition.now
});
},
attach: function() {
if (this.options.wheel) { this.content.addEvent('mousewheel', this.bound.wheel); }
this.vThumb.addEvent('mousedown', this.bound.vStart);
this.vTrack.addEvent('mouseup', this.bound.vPage);
},
wheel: function(event) {
this.content.scrollTop -= event.wheel * this.options.wheel;
this.vUpdateThumbFromContentScroll();
event.stop();
},
scrollTo: function(pos){
myInstance = this;
this.content.scrollTopNew = pos;
this.scrollContent.start(0, this.content.scrollTopNew);
myInstance.vUpdateThumbFromContentScroll2(pos);
},
vPage: function(event) {
// if scrolling up
if (event.page.y > this.vThumb.getPosition().y) {
myInstance = this;
this.content.scrollTopNew = this.content.scrollTop.toInt() + this.content.offsetHeight.toInt();
this.scrollContent.start(0, this.content.scrollTopNew);
}
// if scrolling down
else {
myInstance = this;
this.content.scrollTopNew = this.content.scrollTop.toInt() - this.content.offsetHeight.toInt();
this.scrollContent.start(0, this.content.scrollTopNew);
}
myInstance.vUpdateThumbFromContentScroll2(event.page.y);
event.stop();
},
vStart: function(event) {
this.dragging = true;
this.vMouse.start = event.page.y;
this.vPosition.start = this.vThumb.getStyle('top').toInt();
document.addEvent('mousemove', this.bound.vDrag);
document.addEvent('mouseup', this.bound.end);
this.vThumb.addEvent('mouseup', this.bound.end);
event.stop();
},
end: function(event) {
this.dragging = false;
if (!this.inside) {
this.vScrollbar.get('tween').cancel();
this.vScrollbar.tween('width', 0);
}
document.removeEvent('mousemove', this.bound.vDrag);
document.removeEvent('mouseup', this.bound.end);
this.vThumb.removeEvent('mouseup', this.bound.end);
Blinds.updateImages();
event.stop();
},
vDrag: function(event) {
this.vMouse.now = event.page.y;
this.vPosition.now = (this.vPosition.start + (this.vMouse.now - this.vMouse.start)).limit(0, (this.vTrackSize - this.vThumbSize));
this.vUpdateContentFromThumbPosition();
this.vUpdateThumbFromContentScroll();
event.stop();
}
});

You could modify your wheel function to reset a delayed function timer (after clearing any previous timers that might still exist). To have the 'autoComplete' fired 1000ms after the last wheel event, try something like this:
wheel: function(event) {
this.content.scrollTop -= event.wheel * this.options.wheel;
this.vUpdateThumbFromContentScroll();
// clear the timer from previous wheel events, if it still exists
if(this.timer) {
clearTimeout(timer);
}
this.timer = function() {this.fireEvent('autoComplete');}.delay(1000, this);
event.stop();
},

Related

Highcharts: Tooltip delay before display

On my highchart i need a delay before the series tooltip is displayed.
I defined a new refresh function with a timer to realize it. If the timer is ready i check if the mouse position. If it moved not that much the tooltip should appear.
function (H) {
var timer = [];
var mousePosition = {
x: 0,
y: 0
};
window.addEventListener("mousemove", function (event) {
mousePosition.x = event.pageX;
mousePosition.y = event.pageY;
});
var getMousePositionX = function () {
return mousePosition.x;
};
var clearTimer = function () {
timer = [];
}
H.wrap(H.Tooltip.prototype, 'refresh', function (proceed) {
var mousePosX = getMousePositionX();
var delayForDisplay = this.chart.options.tooltip.delayForDisplay ? this.chart.options.tooltip.delayForDisplay : 1000;
timer[timer.length+1] = window.setTimeout(function () {
var currMousePosX = getMousePositionX();
if ((mousePosX >= currMousePosX - 5 && mousePosX <= currMousePosX + 5)) {
this.proceed.apply(this.tooltip, this.refreshArguments);
clearTimer();
}
}.bind({
refreshArguments: Array.prototype.slice.call(arguments, 1),
chart: this.chart,
tooltip: this,
clearTimer: clearTimer,
proceed: proceed
}), delayForDisplay);
});
};
The problem I have is, that the hover holos have also a delay.
Here is a sample: JSFiddle
Any solutions for this issue?
You can make new tooltip basing on your standard Highcharts tooltip and show it on your mouseover with some timeout:
load: function() {
chart = this;
this.myTooltip = new Highcharts.Tooltip(this, this.options.tooltip);
this.tooltip.label.element.remove();
}
point: {
events: {
mouseOver: function(e) {
var i = this.x;
points = [];
Highcharts.each(this.series.chart.series, function(s) {
Highcharts.each(s.data, function(p) {
if (p.x === i) {
points.push(p)
}
})
});
myTooltip = chart.myTooltip;
setTimeout(function() {
myTooltip.refresh(points, e)
}, 1000)
}, mouseOut: function() {
setTimeout(function() {
chart.myTooltip.hide();
}, 1000)
}
}
}
Here you can see an example how it can work: http://jsfiddle.net/az39das8/

Cropping image with javascript function

Hi I am trying to create a Javascript function that would crop an image to 200x300. Here's my code which is not working:
var image = new SimpleImage ("image");
function crop (image, width, height){
width = 200;
height = 300;
if (image.width > 1){image.width = 200};
if (image.width > 1){image.height = 300};
return image;
}
print (crop(image, width, height));
You can use jQuery :
$(document).ready(function () {
$('#sample_input').awesomeCropper(
{ width: 150, height: 150, debug: true }
);
});
// Generated by CoffeeScript 1.6.3
(function() {
var $;
$ = jQuery;
$.awesomeCropper = function(inputAttachTo, options) {
var $applyButton, $cancelButton, $container, $cropSandbox, $fileSelect, $imagesContainer, $inputAttachTo, $progressBar, $resultIm, $sourceIm, $urlSelect, $urlSelectButton, a, cleanImages, div, drawImage, fileAllowed, handleDragOver, handleDropFileSelect, handleFileSelect, image, input, log, readFile, removeAreaSelect, removeLoading, saveCrop, setAreaSelect, setImages, setLoading, setOriginalSize, settings;
settings = {
width: 100,
height: 100,
debug: false
};
settings = $.extend(settings, options);
log = function() {
if (settings.debug) {
return typeof console !== "undefined" && console !== null ? console.log(arguments) : void 0;
}
};
$inputAttachTo = $(inputAttachTo);
input = function(type) {
return $("<input type = \"" + type + "\" />");
};
div = function() {
return $("<div/>");
};
a = function(text) {
return $("" + text + "");
};
image = function() {
return $('<img/>');
};
$container = div().insertAfter($inputAttachTo).addClass('awesome-cropper');
$cropSandbox = $('<canvas></canvas>');
$cropSandbox.attr({
width: settings.width,
height: settings.height
});
$container.append($cropSandbox);
$fileSelect = input('file');
$container.append($fileSelect);
if (settings.proxy_path !== void 0) {
$urlSelect = input('text');
$urlSelectButton = input('button');
$urlSelectButton.val('Upload from url');
$container.append(div().addClass('form-group').append($urlSelect).append($urlSelectButton));
}
$progressBar = div().addClass('progress hide').append(div().addClass('progress-bar').attr({
role: 'progressbar',
'aria-valuenow': "60",
'aria-valuemin': "0",
'aria-valuemax': "100",
style: "width: 60%;"
}));
$container.append($progressBar);
$resultIm = image();
$container.append($resultIm);
$sourceIm = image();
$applyButton = a('Apply').addClass('btn yes btn-primary');
$cancelButton = a('Cancel').addClass('btn btn-danger').attr({
'data-dismiss': "modal"
});
$imagesContainer = div().append(div().addClass('modal-dialog').append(div().addClass('modal-content').append(div().addClass('modal-body').append(div().addClass('col-md-9').append($sourceIm)).append(div().addClass('col-md-3').append($cropSandbox)).append(div().addClass('clearfix')), div().addClass('modal-footer').append(div().addClass('btn-group').append($cancelButton).append($applyButton))))).addClass('modal').attr({
role: 'dialog'
});
$container.append($imagesContainer);
removeAreaSelect = function(image) {
return image.imgAreaSelect({
remove: true
});
};
cleanImages = function() {
var im;
removeAreaSelect($sourceIm);
im = $sourceIm;
$sourceIm = image();
return im.replaceWith($sourceIm);
};
setLoading = function() {
return $progressBar.removeClass('hide');
};
removeLoading = function() {
$imagesContainer.on('shown.bs.modal', function() {
return setAreaSelect($sourceIm);
}).on('hidden.bs.modal', function() {
return cleanImages();
}).modal();
return $progressBar.addClass('hide');
};
setOriginalSize = function(img) {
var tempImage;
tempImage = new Image();
tempImage.onload = function() {
return img.attr({
'data-original-width': tempImage.width,
'data-original-height': tempImage.height
});
};
return tempImage.src = img.attr('src');
};
setImages = function(uri) {
return $sourceIm.attr('src', uri).load(function() {
removeLoading();
return setOriginalSize($sourceIm);
});
};
drawImage = function(img, x, y, width, height) {
var context, destHeight, destWidth, destX, destY, oHeight, oWidth, r, sourceHeight, sourceWidth, sourceX, sourceY;
oWidth = img.attr('data-original-width');
oHeight = img.attr('data-original-height');
if (oWidth > oHeight) {
r = oHeight / img.height();
} else {
r = oWidth / img.width();
}
sourceX = Math.round(x * r);
sourceY = Math.round(y * r);
sourceWidth = Math.round(width * r);
sourceHeight = Math.round(height * r);
destX = 0;
destY = 0;
destWidth = settings.width;
destHeight = settings.height;
context = $cropSandbox.get(0).getContext('2d');
return context.drawImage(img.get(0), sourceX, sourceY, sourceWidth, sourceHeight, destX, destY, destWidth, destHeight);
};
setAreaSelect = function(image) {
var viewPort, x2, y2,
_this = this;
viewPort = $(window).height() - 150;
if ($sourceIm.height() > viewPort) {
$sourceIm.css({
height: viewPort + "px"
});
}
log(image.width(), image.height());
if (image.width() / settings.width >= image.height() / settings.height) {
y2 = image.height();
x2 = Math.round(settings.width * (image.height() / settings.height));
} else {
x2 = image.width();
y2 = Math.round(settings.height * (image.width() / settings.width));
}
log(x2, y2, image.width(), image.height());
drawImage($sourceIm, 0, 0, x2 - 1, y2 - 1);
return image.imgAreaSelect({
aspectRatio: "" + settings.width + ":" + settings.height,
handles: true,
x1: 0,
y1: 0,
x2: x2,
y2: y2,
onSelectEnd: function(img, selection) {
return drawImage($sourceIm, selection.x1, selection.y1, selection.width - 1, selection.height - 1);
}
});
};
fileAllowed = function(name) {
var res;
res = name.match(/\.(jpg|png|gif|jpeg)$/mi);
if (!res) {
alert('Only *.jpeg, *.jpg, *.png, *.gif files allowed');
return false;
} else {
return true;
}
};
readFile = function(file) {
var reader;
reader = new FileReader();
setLoading();
reader.onload = function(e) {
return setImages(e.target.result);
};
return reader.readAsDataURL(file);
};
handleDropFileSelect = function(evt) {
evt.stopPropagation();
evt.preventDefault();
if (evt.originalEvent.dataTransfer.files[0] !== void 0) {
if (!fileAllowed(evt.originalEvent.dataTransfer.files[0].name)) {
return;
}
return readFile(evt.originalEvent.dataTransfer.files[0]);
}
};
handleDragOver = function(e) {
e.originalEvent.dataTransfer.dropEffect = "copy";
e.stopPropagation();
return e.preventDefault();
};
handleFileSelect = function(evt) {
if (evt.target.files[0] !== void 0) {
if (!fileAllowed(evt.target.files[0].name)) {
return;
}
readFile(evt.target.files[0]);
return evt.target.value = "";
}
};
saveCrop = function() {
var result;
result = $cropSandbox.get(0).toDataURL();
$resultIm.attr('src', result);
$inputAttachTo.val(result);
return cleanImages();
};
$fileSelect.on('change', handleFileSelect);
$container.on('dragover', handleDragOver);
$container.on('drop', handleDropFileSelect);
if (settings.proxy_path !== void 0) {
$urlSelect.on('dragover', handleDragOver);
$urlSelect.on('drop', handleDropFileSelect);
$urlSelectButton.click(function() {
var url;
if (!$urlSelect.val().match(/^(https?:\/\/)?/)) {
return;
}
if (!fileAllowed($urlSelect.val())) {
return;
}
setLoading();
url = settings.proxy_path.replace(/:url/, $urlSelect.val());
return $.get(url).done(function(data) {
return setImages(data);
}).fail(function(jqXNR, textStatus) {
$progressBar.addClass('hide');
return alert("Failed to load image");
});
});
}
$cancelButton.on('click', function() {
return cleanImages();
});
return $applyButton.on('click', function() {
saveCrop();
return $imagesContainer.modal('hide');
});
};
/*
# jQuery Awesome Cropper plugin
#
# Copyright 2013 8xx8, vdv73rus
#
# v0.0.2
*/
$.fn.extend({
awesomeCropper: function(options) {
return this.each(function() {
if ($(this).data("awesomeCropper")) {
if (options.remove) {
$(this).data("awesomeCropper").remove();
$(this).removeData("awesomeCropper");
} else {
$(this).data("awesomeCropper").setOptions(options);
}
} else if (!options.remove) {
$(this).data("awesomeCropper", new $.awesomeCropper(this, options));
}
if (options.instance) {
return $(this).data("awesomeCropper");
}
return this;
});
}
});
}).call(this);
Here is my code for this prompt (Duke Learn to Program classes). This worked when running in Duke's custom environment.
var image = new SimpleImage("duke_blue_devil.png");
var image2 = new SimpleImage(200, 300);
function crop() {
for (var pix of image.values()) {
var wid = image2.getWidth();
var ht = image2.getHeight();
if (pix.getX() < wid && pix.getY() < ht) {
image2.setPixel(pix.getX(), pix.getY(), pix);
} else;
}
}
crop(image);
print(image2);
print(image2.getWidth());
print(image2.getHeight());

Jquery return not loaded in Wordpress

I have added a javascript function in Wordpress. Here is the code for this function:
/*
* CraftMap
* author: Marcin Dziewulski
* web: http://www.jscraft.net
* email: info#jscraft.net
* license: http://www.jscraft.net/licensing.html
*/
(function($) {
$.fn.craftmap = function(options) {
var D = {
cookies: false,
fullscreen: false,
container: {
name: 'imgContent'
},
image: {
width: 1475,
height: 1200,
name: 'imgMap'
},
map: {
position: 'center'
},
marker: {
name: 'marker',
center: true,
popup: true,
popup_name: 'popup',
onClick: function(marker, popup){},
onClose: function(marker, popup){}
},
controls: {
init: true,
name: 'controls',
onClick: function(marker){}
},
preloader: {
init: true,
name: 'preloader',
onLoad: function(img, dimensions){}
}
}; // default settings
var S = $.extend(true, D, options);
return this.each(function(){
alert("?");
var M = $(this),
IMG = M.find('.'+S.image.name),
P = {
init: function(){
this._container.init();
if (S.fullscreen) {
this.fullscreen.init();
this.fullscreen.resize();
}
this._globals.init();
if (S.preloader.init) this.preloader.init();
this.map.init();
this.marker.init();
if (S.controls.init) this.controls.init();
},
_container: {
init: function(){
P._container.css();
P._container.wrap();
},
css: function(){
var max = {
width: '100%',
height: '100%'
};
IMG.css(max);
var css = {
position: 'relative',
overflow: 'hidden',
cursor: 'move'
}
M.css(css);
},
wrap: function(){
var css = {
zIndex: '1',
position: 'absolute',
width: S.image.width,
height: S.image.height
}
M.wrapInner($('<div />').addClass(S.container.name).css(css));
}
},
_globals: {
init: function(){
C = M.find('.'+S.container.name),
MARKER = C.find('.'+S.marker.name),
md = false, mx = 0, my = 0, ex = 0, ey = 0, delta = 0, mv = [], interval = 0,
D = {
w: M.width(),
h: M.height()
},
I = {
w: C.width(),
h: C.height()
}
if (S.controls.init){
CONTROLS = $('.'+S.controls.name).find('a');
}
}
},
_mouse: {
get: function(e){
var x = e.pageX,
y = e.pageY;
return {'x': x, 'y': y}
},
update: function(e){
var mouse = P._mouse.get(e),
x = mouse.x,
y = mouse.y,
movex = x-mx,
movey = y-my,
top = ey+movey,
left = ex+movex,
check = P.map.position.check(left, top),
css = {
top: check.y,
left: check.x
}
C.css(css);
if (S.cookies){
P.cookies.create('position', check.x + ',' + check.y, 7);
}
},
decelerate: function(e){
var l = mv.length, timer = 0;
if (l){
var tc = 20;
interval = setInterval(function(){
var position = C.position(), left = position.left, top = position.top,
remain = (tc-timer)/tc,
last = l-1,
xd = (mv[last].x-mv[0].x)/l,
yd = (mv[last].y-mv[0].y)/l,
vx = xd*remain,
vy = yd*remain,
coords = P.map.position.check(vx+left, vy+top),
css = {
left: coords.x,
top: coords.y
};
C.css(css);
++timer;
if (timer == tc){
clearInterval(interval);
timer = 0;
}
}, 40);
}
},
wheel: {
init: function(e){
M.handle = function(e){
e.preventDefault();
if (!e) {
e = window.event;
}
if (e.wheelDelta) {
delta = e.wheelDelta/120;
if (window.opera) {
delta = -delta;
}
} else if (e.detail) {
delta = -e.detail/3;
}
}
if (window.addEventListener){
window.addEventListener('DOMMouseScroll', M.handle, false);
}
window.onmousewheel = document.onmousewheel = M.handle;
},
remove: function(){
if (window.removeEventListener){
window.removeEventListener('DOMMouseScroll', M.handle, false);
}
window.onmousewheel = document.onmousewheel = null;
}
}
},
fullscreen: {
init: function(){
var win = $(window), w = win.width(), h = win.height(),
css = {
width: w,
height: h
}
M.css(css);
},
resize: function(){
$(window).resize(function(){
P.fullscreen.init();
D = {
w: M.width(),
h: M.height()
}
});
}
},
cookies: {
create: function(name, value, days) {
if (days) {
var date = new Date(), set = date.getTime() + (days * 24 * 60 * 60 * 1000);
date.setTime(set);
var expires = '; expires=' + date.toGMTString();
} else {
var expires = '';
}
document.cookie = name+'='+value+expires+'; path=/';
},
erase: function(name) {
cookies.create(name, '', -1);
},
read: function(name) {
var e = name + '=',
ca = document.cookie.split(';');
for(var i=0;i < ca.length;i++) {
var c = ca[i];
while (c.charAt(0) == ' '){
c = c.substring(1, c.length);
}
if (c.indexOf(e) == 0){
return c.substring(e.length,c.length);
}
}
return null;
}
},
preloader: {
init: function(){
var img = new Image(),
src = IMG.attr('src');
P.preloader.create();
$(img).addClass(S.image.name).attr('src', src).load(function(){
var t = $(this),
css = {
width: this.width,
height: this.height
}
t.css(css);
IMG.remove();
P.preloader.remove();
S.preloader.onLoad.call(this, t, css);
}).appendTo(C);
},
create: function(){
var css = {
position: 'absolute',
zIndex: '10',
top: '0',
left: '0',
width: '100%',
height: '100%'
}
M.append($('<div />').addClass(S.preloader.name).css(css));
},
remove: function(){
M.find('.'+S.preloader.name).fadeOut(400, function(){
var t = $(this);
t.remove();
});
}
},
map: {
init: function(){
P.map.position.set();
P.map.move();
},
position: {
set: function(){
if (S.cookies){
if (typeof P.cookies.read('position') != 'null') {
var position = P.cookies.read('position').split(','),
x = position[0],
y = position[1];
} else {
var x = (D.w-I.w)/2,
y = (D.h-I.h)/2;
}
} else {
var position = S.map.position;
switch (position){
case 'center':
var x = (D.w-I.w)/2,
y = (D.h-I.h)/2;
break;
case 'top_left':
var x = 0,
y = 0;
break;
case 'top_right':
var x = D.w-I.w,
y = 0;
break;
case 'bottom_left':
var x = 0,
y = D.h-I.h;
break;
case 'bottom_right':
var x = D.w-I.w,
y = D.h-I.h;
break;
default:
var coords = position.split(' '),
x = -(coords[0]),
y = -(coords[1]),
coords = P.map.position.check(x, y),
x = coords.x,
y = coords.y;
}
}
var css = { top: y, left: x }
C.css(css);
},
check: function(x, y){
if (y < (D.h-I.h)){
y = D.h-I.h;
} else if (y > 0){
y = 0;
}
if (x < (D.w-I.w)){
x = D.w-I.w;
} else if (x>0){
x = 0;
}
return {'x': x, 'y': y}
}
},
move: function(){
C.bind({
mousedown: function(e){
md = true;
var mouse = P._mouse.get(e);
mx = mouse.x,
my = mouse.y;
var el = C.position();
ex = el.left,
ey = el.top;
mv = [];
clearInterval(interval);
P._mouse.update(e);
return false;
},
mousemove: function(e){
if (md) {
P._mouse.update(e);
var mouse = P._mouse.get(e),
coords = {
x: mouse.x,
y: mouse.y
}
mv.push(coords);
if (mv.length > 15){
mv.pop();
}
}
return false;
},
mouseup: function(e){
if (md) md = false;
P._mouse.decelerate(e);
return false;
},
mouseout: function(){
if (md) md = false;
P._mouse.wheel.remove();
return false;
},
mouseover: function(e){
P._mouse.wheel.init(e);
return false;
},
mousewheel: function(e){
P._zoom.init(e);
}
});
}
},
_zoom: {
init: function(e){}
},
marker: {
init: function(){
P.marker.set();
P.marker.open();
P.marker.close();
},
set: function(){
MARKER.each(function(){
var t = $(this), position = t.attr('data-coords').split(',');
x = parseInt(position[0]), y = parseInt(position[1]),
css = {
position: 'absolute',
zIndex: '2',
top: y,
left: x
}
t.css(css);
}).wrapInner($('<div />').addClass(S.marker.name+'Content').hide());
},
open: function(){
MARKER.live('click', function(){
var t = $(this), id = t.attr('id'), marker = S.marker, w = t.width(), h = t.height(),
position = t.position(), x = position.left, y = position.top, id = t.attr('id'),
html = t.find('.'+marker.name+'Content').html();
if (marker.center){
var cy = -y+D.h/2-h/2,
cx = -x+D.w/2-w/2,
c = P.map.position.check(cx, cy),
animate = {
top: c.y,
left: c.x
};
C.animate(animate);
}
if (marker.popup){
$('.'+marker.popup_name).remove();
var css = {
position:'absolute',
zIndex:'3'
}
t.after(
$('<div />').addClass(marker.popup_name+' '+id).css(css).html(html).append(
$('<a />').addClass('close')
)
);
var POPUP = t.next('.'+marker.popup_name),
pw = POPUP.innerWidth(),
ph = POPUP.innerHeight(),
x0 = 0, y0 = 0;
if (x-pw < 0){
x0 = x;
} else if (x+pw/2 > I.w){
x0 = x-pw+w;
} else {
x0 = x-(pw/2-w/2);
}
if (y-ph < 0){
y0 = y+h+h/1.5;
} else {
y0 = y-ph-h/1.5;
}
if (x-pw < 0 && y-ph < 0){
x0 = x+w*2;
y0 = y-h/2;
} else if (y-ph < 0 && x+pw/2 > I.w){
x0 = x-pw-w/2;
y0 = y-h/2;
} else if (y+ph > I.h && x+pw/2 > I.w){
x0 = x-pw+w;
y0 = y-ph-h/2;
} else if (y+ph > I.h && x-pw < 0){
x0 = x;
y0 = y-ph-h/2;
}
var css = {
left: x0,
top: y0
}
POPUP.css(css);
}
P.controls.active.set(id);
marker.onClick.call(this, t, POPUP);
return false;
});
},
close: function(){
C.find('.close').live('click', function(){
var t = $(this), popup = t.parents('.'+S.marker.popup_name), marker = popup.prev('.'+S.marker.name);
popup.remove();
P.controls.active.remove();
S.marker.onClose.call(this, marker, popup);
return false;
});
}
},
controls: {
init: function(){
P.controls.set();
},
set: function(){
CONTROLS.click(function(){
var t = $(this), rel = t.attr('rel');
div = C.find('.'+ S.marker.name).filter('#'+rel);
div.trigger('click');
S.controls.onClick.call(this, div);
return false;
});
},
active: {
set: function(id){
if (S.controls.init){
CONTROLS.removeClass('active').filter(function(){
return this.rel == id;
}).addClass('active');
}
},
remove: function(){
if (S.controls.init) {
CONTROLS.removeClass('active');
}
}
}
}
}
P.init();
});
};
}(jQuery));
The problem is that the return in never called. How can I fix this problem? I have seen that this script works with jQuery version 1.7 or 1.8. So I told wordpress to load jquery 1.8.
I've created a new js file and I'm loading it using wordpress's function: wp_enqueue_script. So everything should work fine.
The return function is never called. What should I do/change in order to make it work.

Scroll to bottom using ngScrollbar

I am trying to automatically scroll to bottom whenever there is a new message.
I use angular directive ng-scrollbar.
Here is the code of the directive there is one method of scroll named scrolTo:
/**
* #name ng-scrollbar
* #author angrytoro
* #since 9/12/2014
* #version 0.1
* #beta 0.2
* #see https://github.com/angrytoro/ngscrollbar
* #copyright 2014 angrytoro
* #license MIT: You are free to use and modify this code, on the condition that this copyright notice remains.
*
* #description The angular directive ng-scrollbar imitate the true browser scrollbar.
* It's applied to the element which set height or width attribute and the overflow is auto, but exclude body element.
* It's not necessary to imitate scrollbar for body element, if you use the AngularJS.
* suggests: don't use the directive, if you don't have to. The scrollbar which is inbuilt in browser is more highly-efficient.
*AngularJS is not fit for IE which version is less then 9, so the directive is not fit the IE(8,7,6,5).
*
*
* #example
* 1.
* <div style="height:300px;overflow:auto;" ng-scrollbar>
* <li ng-repeat="item in items">item</li>
* </div>
* 2.
* <div style="height:300px;overflow:auto;" ng-scrollbar scrollbar-x="false" scrollbar-y="true" scrollbar-config="{show:true, autoResize: true, dragSpeed: 1.2}">
* <li ng-repeat="item in items">item</li>
* </div>
* 3.
* <div ng-scrollbar>
* <div style="height:400px;width:3000px"></div>
* </div>
*
* #conf spec
* scrollbar-x the value is true or false, to configure the x scrollbar create or no create, the default value is true. but the directive can decide whether it need be created if user not set the attribute.
*
* scrollbar-y the value is true or false, to configure the y scrollbar create or no create, the default value is true. but the directive can decide whether it need be created if user not set the attribute.
*
* scrollbar-config
* default config is
*
* {
* dragSpeed: 1, //default browser delta value is 120 or -120
autoResize: false, // if need auto resize, default false
show: false, // if need show when mouse not enter the container element which need scrollbar, default false.
scrollbar: {
width: 6, //scrollbar width
hoverWidth: 8, //scrollbar width when the mouse hover on it
color: 'rgba(0,0,0,.6)' //scrollbar background color
},
scrollbarContainer: {
width: 12, //scrollbarContainer width
color: 'rgba(0,0,0,.1)' // scrollbarContainer background
}
* }
*
*/
angular.module('widget.scrollbar', [])
.directive('ngScrollbar', [
function() {
return {
restrict: 'AE',
transclude: true,
scope: {
scrollbarConfig: '=scrollbarConfig',
scrollbarX: '#', // the value is true or false, to configure the x scrollbar create or no create.
scrollbarY: '#' // the value is true or false, to configure the y scrollbar create or no create.
},
template: '<div style="position:relative;width:100%;height:100%;">\
<div class="ngscroll-content-container" style="display:inline-block;margin-top:0;margin-left:0" ng-transclude>\
</div>\
<ng-scrollbar-x ng-if="scrollbarX || scrollbarX === undefined"></ng-scrollbar-x>\
<ng-scrollbar-y ng-if="scrollbarY || scrollbarY === undefined"></ng-scrollbar-y>\
</div>',
controller: 'scrollbarController',
compile: function(element) {
element.css('overflow', 'hidden');
return function(scope, element, attrs, ctrl) {
ctrl.init(element, scope.scrollbarConfig);
};
}
};
}
])
.controller('scrollbarController', [function() {
var defaultConfig = {
dragSpeed: 1, //default browser delta value is 120 or -120
autoResize: false, // if need auto resize, default false
show: false, // if need show when mouse not enter the container element which need scrollbar, default false.
scrollbar: {
width: 6, //scrollbar width
hoverWidth: 8, //scrollbar width when the mouse hover on it
color: 'rgba(0,0,0,.6)' //scrollbar background color
},
scrollbarContainer: {
width: 12, //scrollbarContainer width
color: 'rgba(0,0,0,.1)' // scrollbarContainer background
}
};
var containerElement, // the element which need the directive of ngscrollbar
contentElement, // the element which transclude the true content
config, // config
scrollbarMargin, // the variable is used to descide the scrollbar element top or left to its parent element scrollbarContainer
scrollbarHoverMargin; // the variable is used to descide the scrollbar element top or left to its parent element scrollbarContainer when the mouse hover on the scrollbar
/**
* it must be called before the controller is used.
* #param {jqlite object} element it's necessary variable
* #param {object} scrollbarConfig the config which is defined by user
* #return
*/
this.init = function(element, scrollbarConfig) {
containerElement = element;
config = angular.copy(angular.extend(defaultConfig, scrollbarConfig || {}));
contentElement = angular.element(element[0].querySelector('.ngscroll-content-container'));
scrollbarMargin = (config.scrollbarContainer.width - config.scrollbar.width) / 2;
scrollbarHoverMargin = (config.scrollbarContainer.width - config.scrollbar.hoverWidth) / 2;
};
angular.extend(this, {
/**
* Wrap window in an angular jqLite object.
*/
winEl: angular.element(window),
/**
* get the element which need the directive of ngscrollbar
* #return {jqlite object}
*/
getContainerElement: function() {
return containerElement;
},
/**
* the element which transclude the true content
* #return {jqlite object}
*/
getContentElement: function() {
return contentElement;
},
/**
* get the config
* #return {object}
*/
getConfig: function() {
return config;
},
/**
* get the scrollbarMargin
* #return {number}
*/
getScrollbarMargin: function() {
return scrollbarMargin;
},
/**
* get the scrollbarHoverMargin
* #return {number}
*/
getScrollbarHoverMargin: function() {
return scrollbarHoverMargin;
}
});
}])
.directive('ngScrollbarY', ['$timeout', function($timeout){
return {
restrict: 'AE',
require: '^ngScrollbar',
replace: true,
template: '<div class="ngscrollbar-container-y" ng-style="styles.scrollbarContainer"><div class="ngscrollbar-y" ng-style="styles.scrollbar"></div></div>',
compile: function() {
return function(scope, element, attrs, ctrl) {
var config = ctrl.getConfig(),
docEl = angular.element(document),
containerElement = ctrl.getContainerElement(),
contentElement = ctrl.getContentElement(),
scrollbar = angular.element(element[0].querySelector('.ngscrollbar-y')),
scrollbarMargin = ctrl.getScrollbarMargin(),
scrollbarHoverMargin = ctrl.getScrollbarHoverMargin();
scope.styles = {
scrollbarContainer: {
position: 'absolute',
width: config.scrollbarContainer.width + 'px',
height: '100%',
top: 0,
right: 0,
transition: 'background .3s ease-in-out',
'border-radius': config.scrollbarContainer.width / 2 + 'px'
},
scrollbar: {
position: 'absolute',
width: config.scrollbar.width + 'px',
right: scrollbarMargin + 'px',
cursor: 'default',
opacity: 0,
transition: 'opacity .3s ease-in-out, border-radius .1s linear, width .1s linear, right .1s linear',
background: config.scrollbar.color,
'border-radius': config.scrollbar.width / 2 + 'px'
}
};
var getContentHeight = function() {
return contentElement[0].offsetHeight;
};
var getContainerHeight = function() {
return containerElement[0].offsetHeight;
};
var getScrollbarHeight = function() {
var height = Math.pow(getContainerHeight(), 2) / getContentHeight() - scrollbarMargin*2;
return height;
};
var isOverflow = function() {
return getContentHeight() > getContainerHeight();
};
var hideScrollbar = function() {
scrollbar.css('opacity', 0);
};
var showScrollbar = function() {
scrollbar.css('opacity', 1);
};
var reset = function() {
var oldMarginTop = parseInt(contentElement.css('margin-top'), 10);
contentElement.css('margin-top', '0px'); // this is for the element which has the attribute of max-height
if (isOverflow()) {
element.css('display', 'block');
scrollbar.css('height', getScrollbarHeight() + 'px');
scrollTo(oldMarginTop);
if (config.show) {
showScrollbar();
}
} else {
element.css('display', 'none');
}
};
var scrollTo = function(top) {
top = Math.min(0, Math.max(top, getContainerHeight() - getContentHeight()));
contentElement.css('margin-top', top + 'px');
scrollbar.css('top', -top/getContentHeight()*getContainerHeight() + scrollbarMargin + 'px');
};
var scroll = function(distance) {
var newTop = parseInt(contentElement.css('margin-top'), 10) + distance;
scrollTo(newTop);
};
containerElement.on('mousewheel', function(event) {
if (!isOverflow()) {
return;
}
event.preventDefault();
if (event.originalEvent !== undefined) {
event = event.originalEvent;
}
scroll(event.wheelDeltaY || event.wheelDelta);
});
if(window.navigator.userAgent.toLowerCase().indexOf('firefox') >= 0) {
containerElement.on('wheel', function(event) {
if (!isOverflow()) {
return;
}
event.preventDefault();
if (event.originalEvent !== undefined) {
event = event.originalEvent;
}
scroll(-event.deltaY * 40);// the ff delta value is 3 or -3 when scroll and the chrome or ie is -120 or 120, so it must multiply by 40
});
}
element.on('mouseenter', function() {
element.css('background', config.scrollbarContainer.color);
scrollbar.css('width', config.scrollbar.hoverWidth + 'px');
scrollbar.css('right', scrollbarHoverMargin + 'px');
scrollbar.css('border-radius', config.scrollbar.hoverWidth / 2 + 'px');
});
element.on('mouseleave', function() {
element.css('background', 'none');
scrollbar.css('width', config.scrollbar.width + 'px');
scrollbar.css('right', scrollbarMargin + 'px');
scrollbar.css('border-radius', config.scrollbar.width / 2 + 'px');
});
var scrollbarMousedown = false,
axisY,
mouseInElement = false;
if (!config.show) {
containerElement.on('mouseenter', function() {
mouseInElement = true;
showScrollbar();
});
containerElement.on('mouseleave', function() {
mouseInElement = false;
if (scrollbarMousedown) {
return;
}
hideScrollbar();
});
}
scrollbar.on('mousedown', function(event) {
event.preventDefault();
axisY = event.screenY;
scrollbarMousedown = true;
docEl.one('mouseup', function() {
scrollbarMousedown = false;
if (!config.show && !mouseInElement) {
hideScrollbar();
}
// docEl.off('mouseup', arguments.callee);
});
});
docEl.on('mousemove', function(event) {
if(scrollbarMousedown) {
event.preventDefault();
scroll(-(event.screenY - axisY) * config.dragSpeed * getContentHeight() / getContainerHeight());
axisY = event.screenY;
}
});
$timeout(function() {
reset();
if (!!document.createStyleSheet) { //if the browser is ie browser
contentElement.on('DOMNodeInserted', reset);
contentElement.on('DOMNodeRemoved', reset);
} else {
var observer = new MutationObserver(function(mutations){
if (mutations.length) {
reset();
}
});
observer.observe(contentElement[0], {childList:true, subtree: true});
}
}, 5);
// Redraw the scrollbar when window size changes.
if (config.autoResize) {
// Closure to guard against leaking variables.
(function () {
var redrawTimer;
ctrl.winEl.on('resize', function (e) {
if (redrawTimer) {
clearTimeout(redrawTimer);
}
redrawTimer = setTimeout(function () {
redrawTimer = null;
reset();
}, 50);
});
})();
}
};
}
};
}])
.directive('ngScrollbarX', ['$timeout', function($timeout) {
return {
restrict: 'AE',
replace: true,
require: '^ngScrollbar',
template: '<div class="ngscrollbar-container-x" ng-style="styles.scrollbarContainer"><div class="ngscrollbar-x" ng-style="styles.scrollbar"></div></div>',
compile: function() {
return function(scope, element, attrs, ctrl) {
var config = ctrl.getConfig(),
docEl = angular.element(document),
containerElement = ctrl.getContainerElement(),
containerDom = containerElement[0],
contentElement = ctrl.getContentElement(), //the container of content
scrollbar = angular.element(element[0].querySelector('.ngscrollbar-x')),
scrollbarMargin = ctrl.getScrollbarMargin(),
scrollbarHoverMargin = ctrl.getScrollbarHoverMargin();
scope.styles = {
scrollbarContainer: {
position: 'absolute',
width: '100%',
transition: 'background .3s ease-in-out',
'border-radius': config.scrollbarContainer.width / 2 + 'px'
},
scrollbar: {
position: 'absolute',
cursor: 'default',
opacity: 0,
transition: 'opacity .3s ease-in-out, border-radius .1s linear, width .1s linear, right .1s linear',
background: config.scrollbar.color,
'border-radius': config.scrollbar.width / 2 + 'px'
}
};
element.css('height', config.scrollbarContainer.width + 'px'); // set the scrollbarContainer height;
element.css('bottom', 0); // set scrollbarContainer top
element.css('left', 0); //set scrollbarContainer left
scrollbar.css('top', scrollbarMargin + 'px'); //set scrollbar top
scrollbar.css('height', config.scrollbar.width + 'px');
var getContentWidth = function() {
return contentElement[0].offsetWidth;
};
var getContainerWidth = function() {
return containerDom.offsetWidth;
};
var getScrollbarWidth = function() {
return Math.pow(getContainerWidth(), 2) / getContentWidth() - scrollbarMargin * 2;
};
var showScrollbar = function() {
scrollbar.css('opacity', 1);
};
var hideScrollbar = function() {
scrollbar.css('opacity', 0);
};
var isOverflow = function() {
return getContentWidth() > getContainerWidth();
};
var reset = function() {
var oldMarginLeft = parseInt(contentElement.css('margin-left'), 10);
contentElement.css('margin-left', '0px');
if (isOverflow()) {
element.css('display', 'block');
scrollbar.css('width', getScrollbarWidth() + 'px');
scrollTo(oldMarginLeft);
if (config.show) {
showScrollbar();
}
} else {
element.css('display', 'none');
}
};
var scrollTo = function(left) {
left = Math.min(0, Math.max(left, getContainerWidth() - getContentWidth()));
contentElement.css('margin-left', left + 'px');
scrollbar.css('left', -left/getContentWidth()*getContainerWidth() + scrollbarMargin + 'px');
};
var scroll = function(distance) {
var left = parseInt(contentElement.css('margin-left'), 10) + distance;
scrollTo(left);
};
element.on('mouseenter', function() {
element.css('background', config.scrollbarContainer.color);
scrollbar.css('height', config.scrollbar.hoverWidth + 'px');
scrollbar.css('top', scrollbarHoverMargin + 'px');
scrollbar.css('border-radius', config.scrollbar.hoverWidth / 2 + 'px');
});
element.on('mouseleave', function() {
element.css('background', 'none');
scrollbar.css('height', config.scrollbar.width + 'px');
scrollbar.css('top', scrollbarMargin + 'px');
scrollbar.css('border-radius', config.scrollbar.width / 2 + 'px');
});
var scrollbarMousedown = false,
axisX,
mouseInElement = false;
if (!config.show) {
containerElement.on('mouseenter', function() {
mouseInElement = true;
showScrollbar();
});
containerElement.on('mouseleave', function() {
mouseInElement = false;
if (scrollbarMousedown) {
return;
}
hideScrollbar();
});
}
scrollbar.on('mousedown', function(event) {
event.preventDefault();
scrollbarMousedown = true;
axisX = event.screenX;
docEl.one('mouseup', function() {
scrollbarMousedown = false;
if (!config.show && !mouseInElement) {
hideScrollbar();
}
// docEl.off('mouseup', arguments.callee);
});
});
docEl.on('mousemove', function(event) {
if(scrollbarMousedown) {
event.preventDefault();
scroll(-(event.screenX - axisX) * config.dragSpeed * getContentWidth() / getContainerWidth());
axisX = event.screenX;
}
});
$timeout(function() {
reset();
if (!!document.createStyleSheet) { //if the browser is ie browser
contentElement.on('DOMNodeInserted', reset);
contentElement.on('DOMNodeRemoved', reset);
} else {
var observer = new MutationObserver(function(mutations){
if (mutations.length) {
reset();
}
});
observer.observe(contentElement[0], {childList:true, subtree: true});
}
}, 5);
// Redraw the scrollbar when window size changes.
if (config.autoResize) {
// Closure to guard against leaking variables.
(function () {
var redrawTimer;
ctrl.winEl.on('resize', function (e) {
if (redrawTimer) {
clearTimeout(redrawTimer);
}
redrawTimer = setTimeout(function () {
redrawTimer = null;
reset();
}, 50);
});
})();
}
};
}
};
}]);
Thanks for any help to resolve this problem.
You could just use vanilla javascript:
window.scrollTo(0,document.body.scrollHeight);
Run the code above on the event of a new message.
Edit: added example
angular.module('app', [])
.controller('TestCtrl', function($scope) {
$scope.scrollToBottom = function() {
window.scrollTo(0, document.body.scrollHeight);
};
});
#some-content {
height: 10000px;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="TestCtrl">
top
<button ng-click="scrollToBottom()">Scroll to bottom</button>
<div id="some-content"></div>
bottom
</div>
I found a very extravagant kosher solution.
I changed code ngScrollbar like this:
angular.module('widget.scrollbar', [])
.directive('ngScrollbar', [
function() {
return {
restrict: 'AE',
transclude: true,
scope: {
name: '#',
scrollbarConfig: '=scrollbarConfig',
scrollbarX: '#', // the value is true or false, to configure the x scrollbar create or no create.
scrollbarY: '#' // the value is true or false, to configure the y scrollbar create or no create.
},
template: '<div style="position:relative;width:100%;height:100%;">\
<div class="ngscroll-content-container" style="display:inline-block;margin-top:0;margin-left:0" ng-transclude>\
</div>\
<ng-scrollbar-x ng-if="scrollbarX || scrollbarX === undefined"></ng-scrollbar-x>\
<ng-scrollbar-y ng-if="scrollbarY || scrollbarY === undefined"></ng-scrollbar-y>\
</div>',
controller: 'scrollbarController',
compile: function(element) {
element.css('overflow', 'hidden');
return function(scope, element, attrs, ctrl) {
ctrl.init(element, scope.scrollbarConfig);
};
}
};
}
])
.controller('scrollbarController', [function() {
var defaultConfig = {
dragSpeed: 1, //default browser delta value is 120 or -120
autoResize: false, // if need auto resize, default false
show: false, // if need show when mouse not enter the container element which need scrollbar, default false.
scrollbar: {
width: 6, //scrollbar width
hoverWidth: 8, //scrollbar width when the mouse hover on it
color: 'rgba(0,0,0,.6)' //scrollbar background color
},
scrollbarContainer: {
width: 12, //scrollbarContainer width
color: 'rgba(0,0,0,.1)' // scrollbarContainer background
}
};
var containerElement, // the element which need the directive of ngscrollbar
contentElement, // the element which transclude the true content
config, // config
scrollbarMargin, // the variable is used to descide the scrollbar element top or left to its parent element scrollbarContainer
scrollbarHoverMargin; // the variable is used to descide the scrollbar element top or left to its parent element scrollbarContainer when the mouse hover on the scrollbar
/**
* it must be called before the controller is used.
* #param {jqlite object} element it's necessary variable
* #param {object} scrollbarConfig the config which is defined by user
* #return
*/
this.init = function(element, scrollbarConfig) {
containerElement = element;
config = angular.copy(angular.extend(defaultConfig, scrollbarConfig || {}));
contentElement = angular.element(element[0].querySelector('.ngscroll-content-container'));
scrollbarMargin = (config.scrollbarContainer.width - config.scrollbar.width) / 2;
scrollbarHoverMargin = (config.scrollbarContainer.width - config.scrollbar.hoverWidth) / 2;
};
angular.extend(this, {
/**
* get the element which need the directive of ngscrollbar
* #return {jqlite object}
*/
getContainerElement: function() {
return containerElement;
},
/**
* the element which transclude the true content
* #return {jqlite object}
*/
getContentElement: function() {
return contentElement;
},
/**
* get the config
* #return {object}
*/
getConfig: function() {
return config;
},
/**
* get the scrollbarMargin
* #return {number}
*/
getScrollbarMargin: function() {
return scrollbarMargin;
},
/**
* get the scrollbarHoverMargin
* #return {number}
*/
getScrollbarHoverMargin: function() {
return scrollbarHoverMargin;
}
});
}])
.directive('ngScrollbarY', ['$timeout', 'ScrollbarDelegate', function($timeout, ScrollbarDelegate){
return {
restrict: 'AE',
require: '^ngScrollbar',
replace: true,
template: '<div class="ngscrollbar-container-y" ng-style="styles.scrollbarContainer"><div class="ngscrollbar-y" ng-style="styles.scrollbar"></div></div>',
compile: function() {
return function(scope, element, attrs, ctrl) {
var config = ctrl.getConfig(),
docEl = angular.element(document),
containerElement = ctrl.getContainerElement(),
contentElement = ctrl.getContentElement(),
scrollbar = angular.element(element[0].querySelector('.ngscrollbar-y')),
scrollbarMargin = ctrl.getScrollbarMargin(),
scrollbarHoverMargin = ctrl.getScrollbarHoverMargin();
scope.styles = {
scrollbarContainer: {
position: 'absolute',
width: config.scrollbarContainer.width + 'px',
height: '100%',
top: 0,
right: 0,
transition: 'background .3s ease-in-out',
'border-radius': config.scrollbarContainer.width / 2 + 'px'
},
scrollbar: {
position: 'absolute',
width: config.scrollbar.width + 'px',
right: scrollbarMargin + 'px',
cursor: 'default',
opacity: 0,
transition: 'opacity .3s ease-in-out, border-radius .1s linear, width .1s linear, right .1s linear',
background: config.scrollbar.color,
'border-radius': config.scrollbar.width / 2 + 'px'
}
};
var getContentHeight = function() {
return contentElement[0].offsetHeight;
};
var getContainerHeight = function() {
return containerElement[0].offsetHeight;
};
var getScrollbarHeight = function() {
var height = Math.pow(getContainerHeight(), 2) / getContentHeight() - scrollbarMargin*2;
return height;
};
var isOverflow = function() {
return getContentHeight() > getContainerHeight();
};
var hideScrollbar = function() {
scrollbar.css('opacity', 0);
};
var showScrollbar = function() {
scrollbar.css('opacity', 1);
};
var reset = function() {
var oldMarginTop = parseInt(contentElement.css('margin-top'), 10);
contentElement.css('margin-top', '0px'); // this is for the element which has the attribute of max-height
if (isOverflow()) {
element.css('display', 'block');
scrollbar.css('height', getScrollbarHeight() + 'px');
scrollTo(oldMarginTop);
if (config.show) {
showScrollbar();
}
} else {
element.css('display', 'none');
}
};
var scrollTo = function(top) {
top = Math.min(0, Math.max(top, getContainerHeight() - getContentHeight()));
contentElement.css('margin-top', top + 'px');
scrollbar.css('top', -top/getContentHeight()*getContainerHeight() + scrollbarMargin + 'px');
};
var scroll = function(distance) {
var newTop = parseInt(contentElement.css('margin-top'), 10) + distance;
scrollTo(newTop);
};
containerElement.on('mousewheel', function(event) {
if (!isOverflow()) {
return;
}
event.preventDefault();
if (event.originalEvent !== undefined) {
event = event.originalEvent;
}
scroll(event.wheelDeltaY || event.wheelDelta);
});
if(window.navigator.userAgent.toLowerCase().indexOf('firefox') >= 0) {
containerElement.on('wheel', function(event) {
if (!isOverflow()) {
return;
}
event.preventDefault();
if (event.originalEvent !== undefined) {
event = event.originalEvent;
}
scroll(-event.deltaY * 40);// the ff delta value is 3 or -3 when scroll and the chrome or ie is -120 or 120, so it must multiply by 40
});
}
element.on('mouseenter', function() {
element.css('background', config.scrollbarContainer.color);
scrollbar.css('width', config.scrollbar.hoverWidth + 'px');
scrollbar.css('right', scrollbarHoverMargin + 'px');
scrollbar.css('border-radius', config.scrollbar.hoverWidth / 2 + 'px');
});
element.on('mouseleave', function() {
element.css('background', 'none');
scrollbar.css('width', config.scrollbar.width + 'px');
scrollbar.css('right', scrollbarMargin + 'px');
scrollbar.css('border-radius', config.scrollbar.width / 2 + 'px');
});
var scrollbarMousedown = false,
axisY,
mouseInElement = false;
if (!config.show) {
containerElement.on('mouseenter', function() {
mouseInElement = true;
showScrollbar();
});
containerElement.on('mouseleave', function() {
mouseInElement = false;
if (scrollbarMousedown) {
return;
}
hideScrollbar();
});
}
scrollbar.on('mousedown', function(event) {
event.preventDefault();
axisY = event.screenY;
scrollbarMousedown = true;
docEl.one('mouseup', function() {
scrollbarMousedown = false;
if (!config.show && !mouseInElement) {
hideScrollbar();
}
// docEl.off('mouseup', arguments.callee);
});
});
docEl.on('mousemove', function(event) {
if(scrollbarMousedown) {
event.preventDefault();
scroll(-(event.screenY - axisY) * config.dragSpeed * getContentHeight() / getContainerHeight());
axisY = event.screenY;
}
});
$timeout(function() {
reset();
if (!!document.createStyleSheet) { //if the browser is ie browser
contentElement.on('DOMNodeInserted', reset);
contentElement.on('DOMNodeRemoved', reset);
} else {
var observer = new MutationObserver(function(mutations){
if (mutations.length) {
reset();
}
});
observer.observe(contentElement[0], {childList:true, subtree: true});
}
}, 5);
var scrollToBottom = function() {
var offset = getContainerHeight() - getContentHeight();
scrollTo(offset);
};
ctrl.scrollTo = scrollTo;
ctrl.scrollToBottom = scrollToBottom;
ctrl.getContentHeight = getContentHeight;
ctrl.getContainerHeight = getContainerHeight;
ctrl.getScrollbarHeight = getScrollbarHeight;
ScrollbarDelegate.registerInstance(scope.name, ctrl);
};
}
};
}])
.directive('ngScrollbarX', ['$timeout', function($timeout) {
return {
restrict: 'AE',
replace: true,
require: '^ngScrollbar',
template: '<div class="ngscrollbar-container-x" ng-style="styles.scrollbarContainer"><div class="ngscrollbar-x" ng-style="styles.scrollbar"></div></div>',
compile: function() {
return function(scope, element, attrs, ctrl) {
var config = ctrl.getConfig(),
docEl = angular.element(document),
containerElement = ctrl.getContainerElement(),
containerDom = containerElement[0],
contentElement = ctrl.getContentElement(), //the container of content
scrollbar = angular.element(element[0].querySelector('.ngscrollbar-x')),
scrollbarMargin = ctrl.getScrollbarMargin(),
scrollbarHoverMargin = ctrl.getScrollbarHoverMargin();
scope.styles = {
scrollbarContainer: {
position: 'absolute',
width: '100%',
transition: 'background .3s ease-in-out',
'border-radius': config.scrollbarContainer.width / 2 + 'px'
},
scrollbar: {
position: 'absolute',
cursor: 'default',
opacity: 0,
transition: 'opacity .3s ease-in-out, border-radius .1s linear, width .1s linear, right .1s linear',
background: config.scrollbar.color,
'border-radius': config.scrollbar.width / 2 + 'px'
}
};
element.css('height', config.scrollbarContainer.width + 'px'); // set the scrollbarContainer height;
element.css('bottom', 0); // set scrollbarContainer top
element.css('left', 0); //set scrollbarContainer left
scrollbar.css('top', scrollbarMargin + 'px'); //set scrollbar top
scrollbar.css('height', config.scrollbar.width + 'px');
var getContentWidth = function() {
return contentElement[0].offsetWidth;
};
var getContainerWidth = function() {
return containerDom.offsetWidth;
};
var getScrollbarWidth = function() {
return Math.pow(getContainerWidth(), 2) / getContentWidth() - scrollbarMargin * 2;
};
var showScrollbar = function() {
scrollbar.css('opacity', 1);
};
var hideScrollbar = function() {
scrollbar.css('opacity', 0);
};
var isOverflow = function() {
return getContentWidth() > getContainerWidth();
};
var reset = function() {
var oldMarginLeft = parseInt(contentElement.css('margin-left'), 10);
contentElement.css('margin-left', '0px');
if (isOverflow()) {
element.css('display', 'block');
scrollbar.css('width', getScrollbarWidth() + 'px');
scrollTo(oldMarginLeft);
if (config.show) {
showScrollbar();
}
} else {
element.css('display', 'none');
}
};
var scrollTo = function(left) {
left = Math.min(0, Math.max(left, getContainerWidth() - getContentWidth()));
contentElement.css('margin-left', left + 'px');
scrollbar.css('left', -left/getContentWidth()*getContainerWidth() + scrollbarMargin + 'px');
};
var scroll = function(distance) {
var left = parseInt(contentElement.css('margin-left'), 10) + distance;
scrollTo(left);
};
element.on('mouseenter', function() {
element.css('background', config.scrollbarContainer.color);
scrollbar.css('height', config.scrollbar.hoverWidth + 'px');
scrollbar.css('top', scrollbarHoverMargin + 'px');
scrollbar.css('border-radius', config.scrollbar.hoverWidth / 2 + 'px');
});
element.on('mouseleave', function() {
element.css('background', 'none');
scrollbar.css('height', config.scrollbar.width + 'px');
scrollbar.css('top', scrollbarMargin + 'px');
scrollbar.css('border-radius', config.scrollbar.width / 2 + 'px');
});
var scrollbarMousedown = false,
axisX,
mouseInElement = false;
if (!config.show) {
containerElement.on('mouseenter', function() {
mouseInElement = true;
showScrollbar();
});
containerElement.on('mouseleave', function() {
mouseInElement = false;
if (scrollbarMousedown) {
return;
}
hideScrollbar();
});
}
scrollbar.on('mousedown', function(event) {
event.preventDefault();
scrollbarMousedown = true;
axisX = event.screenX;
docEl.one('mouseup', function() {
scrollbarMousedown = false;
if (!config.show && !mouseInElement) {
hideScrollbar();
}
// docEl.off('mouseup', arguments.callee);
});
});
docEl.on('mousemove', function(event) {
if(scrollbarMousedown) {
event.preventDefault();
scroll(-(event.screenX - axisX) * config.dragSpeed * getContentWidth() / getContainerWidth());
axisX = event.screenX;
}
});
$timeout(function() {
reset();
if (!!document.createStyleSheet) { //if the browser is ie browser
contentElement.on('DOMNodeInserted', reset);
contentElement.on('DOMNodeRemoved', reset);
} else {
var observer = new MutationObserver(function(mutations){
if (mutations.length) {
reset();
}
});
observer.observe(contentElement[0], {childList:true, subtree: true});
}
}, 5);
};
}
};
}]);
I added this code:
name: '#',
var scrollToBottom = function() {
var offset = getContainerHeight() - getContentHeight();
scrollTo(offset);
};
ctrl.scrollTo = scrollTo;
ctrl.scrollToBottom = scrollToBottom;
ctrl.getContentHeight = getContentHeight;
ctrl.getContainerHeight = getContainerHeight;
ctrl.getScrollbarHeight = getScrollbarHeight;
ScrollbarDelegateService.registerInstance(scope.name, ctrl);
ScrollbarDelegateService - is an angular service through which controls all the scrollbars.
let ScrollbarDelegateService = function () {
let instances = {};
let getInstances = () => {
return instances;
};
let registerInstance = (name, ctrl) => {
instances[name || ''] = ctrl;
};
let deregisterInstance = (name) => {
delete instances[name || ''];
};
let instanceByName = (name) => {
let instance;
if (!(instance = instances[name || ''])) {
return undefined;
}
return instance;
};
return { getInstances, registerInstance, deregisterInstance, instanceByName };
};
export default ScrollbarDelegateService;
And last, to control the scrolling:
In template:
<section ng-scrollbar name="orders" scrollbar-x="false" scrollbar-y="true" scrollbar-config="vm.scrollbarConfig" class="tab-content-inner">
In controller:
let scrollbar = ScrollbarDelegateService.instanceByName('orders');
if (scrollbar !== undefined) {
scrollbar.scrollToBottom();
}

Javascript scrollbar class and mousewheel speed in different browsers

I'm getting many reports that the mousewheel behaves differently in different browsers when using this scrollbar class. In some browsers (like Firefox) it's extremely slow while in others (mostly newer versions of Safari on Snow Leopard) it's perfect.
Any ideas what's going on here and how to fix it? I'm using the Mootools library. One line to pay attention to here is the wheel: (Browser.firefox) ? 20 : 1 line. This is where you set the speed or steps for the mousewheel.
Here it is set up in a jsFiddle: http://jsfiddle.net/brandondurham/6SUyM/
var ScrollBar = new Class({
Implements: [Events, Options],
options: {
wheel: (Browser.firefox) ? 20 : 1
},
initialize: function(main, options) {
this.setOptions(options);
this.main = $(main);
this.content = this.main.getFirst();
this.vScrollbar = new Element('div', {
'class': 'scrollbar'
}).inject(this.content, 'after');
this.vTrack = new Element('div', {
'class': 'track'
}).inject(this.vScrollbar);
this.vThumb = new Element('div', {
'class': 'handle'
}).inject(this.vTrack);
this.bound = {
'vStart': this.vStart.bind(this),
'end': this.end.bind(this),
'vDrag': this.vDrag.bind(this),
'vTouchDrag': this.vTouchDrag.bind(this),
'wheel': this.wheel.bind(this),
'vPage': this.vPage.bind(this),
};
this.vScrollbar.set('tween', {
duration: 200,
transition: 'cubic:out'
});
this.main.addEvent('mouseenter', function(event){
this.vScrollbar.get('tween').cancel();
this.vScrollbar.tween('width', 12);
}.bind(this));
this.main.addEvent('mouseleave', function(event){
this.vScrollbar.get('tween').cancel();
this.vScrollbar.tween('width', 0);
}.bind(this));
this.vPosition = {};
this.vMouse = {};
this.update();
this.attach();
this.scrollContent = new Fx.Scroll(this.content, {
duration: 800,
transition: Fx.Transitions.Cubic.easeOut,
});
this.scrollThumb = new Fx.Morph(this.vThumb, {
duration: 400,
transition: Fx.Transitions.Cubic.easeOut,
});
},
update: function() {
var panel_id = (this.content.getFirst()) ? this.content.getFirst().get('id') : '';
if ((this.content.scrollHeight <= this.main.offsetHeight) || panel_id == 'random-doodle') this.main.addClass('noscroll');
else this.main.removeClass('noscroll');
this.vContentSize = this.content.offsetHeight;
this.vContentScrollSize = this.content.scrollHeight;
this.vTrackSize = this.vTrack.offsetHeight;
this.vContentRatio = this.vContentSize / this.vContentScrollSize;
this.vThumbSize = (this.vTrackSize * this.vContentRatio).limit(12, this.vTrackSize);
this.vScrollRatio = this.vContentScrollSize / this.vTrackSize;
this.vThumb.setStyle('height', this.vThumbSize);
this.vUpdateThumbFromContentScroll();
this.vUpdateContentFromThumbPosition();
},
vUpdateContentFromThumbPosition: function() {
this.content.scrollTop = this.vPosition.now * this.vScrollRatio;
},
vUpdateContentFromThumbPosition2: function() {
var pos = this.vPosition.now * this.vScrollRatio;
this.scrollContent.start(0, pos);
},
vUpdateThumbFromContentScroll: function() {
this.vPosition.now = (this.content.scrollTop / this.vScrollRatio).limit(0, (this.vTrackSize - this.vThumbSize));
this.vThumb.setStyle('top', this.vPosition.now);
},
vUpdateThumbFromContentScroll2: function(pos) {
this.vPosition.now = (this.content.scrollTopNew / this.vScrollRatio).limit(0, (this.vTrackSize - this.vThumbSize));
this.scrollThumb.start({
'top': this.vPosition.now
});
},
attach: function() {
if (this.options.wheel) this.content.addEvent('mousewheel', this.bound.wheel);
this.content.addEvent('touchstart', this.bound.vStart);
this.vThumb.addEvent('mousedown', this.bound.vStart);
this.vTrack.addEvent('mouseup', this.bound.vPage);
},
wheel: function(event) {
this.content.scrollTop -= event.wheel * this.options.wheel;
this.vUpdateThumbFromContentScroll();
event.stop();
},
scrollTo: function(pos){
myInstance = this;
this.content.scrollTopNew = pos;
this.scrollContent.start(0, this.content.scrollTopNew);
myInstance.vUpdateThumbFromContentScroll2(pos);
},
vPage: function(event) {
// if scrolling up
if (event.page.y > this.vThumb.getPosition().y) {
myInstance = this;
this.content.scrollTopNew = this.content.scrollTop.toInt() + this.content.offsetHeight.toInt();
this.scrollContent.start(0, this.content.scrollTopNew);
}
// if scrolling down
else {
myInstance = this;
this.content.scrollTopNew = this.content.scrollTop.toInt() - this.content.offsetHeight.toInt();
this.scrollContent.start(0, this.content.scrollTopNew);
}
myInstance.vUpdateThumbFromContentScroll2(event.page.y);
event.stop();
},
vStart: function(event) {
this.vMouse.start = event.page.y;
this.vPosition.start = this.vThumb.getStyle('top').toInt();
document.addEvent('touchmove', this.bound.vTouchDrag);
document.addEvent('touchend', this.bound.end);
document.addEvent('mousemove', this.bound.vDrag);
document.addEvent('mouseup', this.bound.end);
this.vThumb.addEvent('mouseup', this.bound.end);
event.stop();
},
end: function(event) {
document.removeEvent('touchmove', this.bound.vTouchDrag);
document.removeEvent('mousemove', this.bound.vDrag);
document.removeEvent('mouseup', this.bound.end);
this.vThumb.removeEvent('mouseup', this.bound.end);
event.stop();
},
vTouchDrag: function(event) {
this.vMouse.now = event.page.y;
this.vPosition.now = (this.vPosition.start - (this.vMouse.now - this.vMouse.start)).limit(0, (this.vTrackSize - this.vThumbSize));
this.vUpdateContentFromThumbPosition();
this.vUpdateThumbFromContentScroll();
event.stop();
},
vDrag: function(event) {
this.vMouse.now = event.page.y;
this.vPosition.now = (this.vPosition.start + (this.vMouse.now - this.vMouse.start)).limit(0, (this.vTrackSize - this.vThumbSize));
this.vUpdateContentFromThumbPosition();
this.vUpdateThumbFromContentScroll();
event.stop();
}
});
The mouse wheel event is very dodgy in javascript, main issue being usually Safari as they used to adjust the ratio on every point release, and even then the values reported by the event are not the same in all major browsers.
There has been some discussion about this on the MooTools tracker some time ago (link) and comparing different solutions I concluded that there's no standard way to normalize the event.
The last message on that issue shows a possible solution for normalizing (link), but it breaks wheel acceleration in Safari (and probably any other acceleration that other Browser/OS/Mouse Driver combination offers) so it is a tradeoff that you will have to evaluate if it fits to the requirements of your usage scenario.

Categories