Static nanoscroller integration

References #1892

A static integration of https://github.com/jamesflorentino/nanoScrollerJS



This is WIP with the goal of getting feedback on and deciding how we should integrate this into Ghost.
This commit is contained in:
Paul Adam Davis 2014-09-10 16:08:57 +01:00
parent bd163ada46
commit 9b94e71805
6 changed files with 1222 additions and 3 deletions

View File

@ -490,7 +490,9 @@ var _ = require('lodash'),
'bower_components/google-caja/html-css-sanitizer-bundle.js',
'core/shared/lib/showdown/extensions/ghostimagepreview.js',
'core/shared/lib/showdown/extensions/ghostgfm.js'
'core/shared/lib/showdown/extensions/ghostgfm.js',
'core/shared/lib/nanoscroller/nanoscroller.js'
]
},
@ -525,7 +527,9 @@ var _ = require('lodash'),
'bower_components/google-caja/html-css-sanitizer-bundle.js',
'core/shared/lib/showdown/extensions/ghostimagepreview.js',
'core/shared/lib/showdown/extensions/ghostgfm.js'
'core/shared/lib/showdown/extensions/ghostgfm.js',
'core/shared/lib/nanoscroller/nanoscroller.js'
]
}
},

View File

@ -0,0 +1,52 @@
.nano {
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
}
.nano > .nano-content {
position: absolute;
overflow: scroll;
overflow-x: hidden;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.nano > .nano-content:focus {
outline: thin dotted;
}
.nano > .nano-content::-webkit-scrollbar {
display: none;
}
.has-scrollbar > .nano-content::-webkit-scrollbar {
display: block;
}
.nano > .nano-pane {
background: transparent;
position: absolute;
width: 6px;
right: 2px;
top: 2px;
bottom: 2px;
opacity: 0;
transition: 0.35s;
border-radius: 10px;
}
.nano > .nano-pane > .nano-slider {
background: #444;
background: rgba(0,0,0,0.35);
position: relative;
border-radius: 10px;
}
.nano.nanoShowScrollBar > .nano-pane,
.nano-pane.active,
.nano-pane.flashed {
opacity: 1;
}
.nano > .nano-pane:hover {
width: 10px;
background: rgba(0, 0, 0, .15);
opacity: 1;
transition: 0.15s;
}

View File

@ -15,6 +15,7 @@
@import "../../../../bower_components/normalize-scss/_normalize"; // via Bower
@import "lib/nprogress";
@import "lib/codemirror";
@import "lib/nanoscroller";
//

View File

@ -0,0 +1,198 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Scrollbars</title>
<link rel="stylesheet" href="../assets/css/ghost.min.css">
</head>
<body class="manage">
<div id="container">
<a class="sr-only sr-only-focusable" href="#gh-main">Skip to main content</a>
<nav class="global-nav" role="navigation">
<a class="nav-item ghost-logo" href="/" title="/">
<div class="nav-label">
<i class="icon-ghost"></i><span>Visit blog</span>
</div>
</a>
<a class=" nav-item nav-content active" href="/ghost/">
<div class="nav-label">
<i class="icon-content"></i> Content
</div>
</a>
<a class=" nav-item nav-new" href="/ghost/editor/">
<div class="nav-label">
<i class="icon-add"></i> New Post
</div>
</a>
<a class=" nav-item nav-settings" href="/ghost/settings/">
<div class="nav-label">
<i class="icon-settings2"></i> Settings
</div>
</a>
<div class="nav-item user-menu" data-href="#">
<div class=" nav-label">
<div class="image">
<img src="../../../core/shared/img/user-image.png" alt="<script id='metamorph-30-start' type='text/x-placeholder'></script>
Paul Davis
<script id='metamorph-30-end' type='text/x-placeholder'></script>
's profile picture">
</div>
<div class="name">
aul Davis <i class="icon-chevron-down"></i>
<small>Profile &amp; Settings</small>
</div>
</div>
<div class=" ghost-popover fade-in dropdown">
<ul class="dropdown-menu dropdown-triangle-top-right" role="menu">
<li role="presentation"><a class=" dropdown-item user-menu-profile" href="/ghost/settings/users/paul-davis/" tabindex="-1">Your Profile</a></li>
<li role="presentation"><a class="dropdown-item user-menu-support" role="menuitem" tabindex="-1" href="http://support.ghost.org/">Help / Support</a></li>
<li class="divider"></li>
<li role="presentation"><a class=" dropdown-item user-menu-signout" href="/ghost/signout/" tabindex="-1">Sign Out</a></li>
</ul>
</div>
</div>
</nav>
<main id="gh-main" class="viewport" role="main">
<aside class=" notifications top"></aside>
<aside class=" notifications bottom"></aside>
<section class=" content-view-container">
<header class="page-header">
<a class="menu-button" href="#"><span class="sr-only">Menu</span></a>
<h2 class="page-title">Content</h2>
</header>
<div class="page-content">
<section class="content-list js-content-list">
<header class="floatingheader">
<section class="content-filter">
<small>All Posts</small>
</section>
<a class=" btn btn-green" href="/ghost/editor/" title="New Post"><span class="hidden">New Post</span></a>
</header>
<section class="content-list-content">
<div class="nano">
<div class="nano-content">
<ol class="posts-list">
<li>
<a class="permalink" href="">
<h3 class="entry-title">Lorem Ipsum</h3>
<section class="entry-meta">
<span class="status">
<span class="draft">Draft</span>
</span>
</section>
</a>
</li>
</ol>
</div>
</div>
</section>
</section>
<section class="content-preview js-content-preview" style="padding: 0;">
<div class="nano">
<div class="nano-content">
<header class="post-preview-header" style="padding: 15px 15px 0 15px;">
<button type="button" class="btn btn-default btn-back">Back</button>
<button type="button" class="unfeatured" title="Feature this post">
<span class="hidden">Star</span>
</button>
<small>
<span class="status">Published</span>
<span class="normal">by</span>
<span class="author">Test User</span>
</small>
<section class="post-controls">
<a class=" btn btn-default post-edit" href="/ghost/editor/34/"> Edit</a>
</section>
</header>
<section class=" content-preview-content" style="padding: 15px;">
<div class="wrapper">
<h1>Lorem Ipsum</h1>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Molestiae nesciunt consectetur ipsa sapiente recusandae suscipit excepturi asperiores ex dolores laborum rerum vero nam non amet corrupti dignissimos eum odit ut quae iure, ad ratione neque. Ea minima minus aspernatur ut, nulla quam accusantium laboriosam libero vel quos repellendus repellat eius!</p>
<p>Totam excepturi necessitatibus porro aperiam commodi quaerat blanditiis numquam sint minus placeat esse quas iste nulla doloremque eum voluptate ipsa enim ab ullam laboriosam dignissimos, quo voluptates soluta laudantium vero. Fuga assumenda quidem officiis eius, unde blanditiis! Quod eum, itaque quis, ea nobis ipsa delectus, sit modi nesciunt, harum cupiditate.</p>
<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Beatae veniam, eum, ea provident neque blanditiis omnis quae quidem iusto est pariatur odio inventore magni voluptatem aut facere sint animi nam, maxime ex perferendis reprehenderit. Assumenda ex, vitae totam quia ea obcaecati rem veniam adipisci dolores. Eaque reiciendis, numquam itaque deserunt.</p>
<p>Nihil ut eaque sit doloribus repellendus accusantium officiis, aliquam error minima fuga, in, consequatur ipsum blanditiis facilis enim quia nostrum numquam dignissimos neque ab magni rerum amet maiores exercitationem! Omnis tenetur, molestias cumque laudantium praesentium suscipit alias maxime tempora facere cum libero tempore architecto blanditiis, ullam nihil, voluptatem voluptatum! Itaque.</p>
<p>Dolor magnam nobis tenetur impedit fugit cum debitis voluptatem delectus quisquam itaque quis placeat, odit eligendi qui, asperiores consectetur porro maiores sequi atque dolorem. Quam unde odio, aperiam, eius expedita laudantium illo quisquam, eaque rerum quaerat blanditiis facilis delectus. Blanditiis quae illo et similique velit aut fugiat atque harum adipisci.</p>
<p>Temporibus expedita pariatur laboriosam nesciunt dicta, in eligendi veniam corporis veritatis aliquam esse quia quaerat eius unde sequi earum explicabo doloremque. Cupiditate numquam officia distinctio iure minus, iusto error voluptatum sed, molestiae asperiores reiciendis dolorum eos unde corporis explicabo maiores recusandae atque. Consectetur culpa quo earum laboriosam ratione fugiat assumenda!</p>
<p>Ducimus sapiente minima ipsa magni aliquid consectetur corrupti ab a quos vel. Nihil optio cum, iure iusto ut sed qui hic odit. Cupiditate quos, explicabo sunt commodi magnam nam debitis pariatur in ratione. Asperiores quaerat impedit officia iusto quae molestias, nam consectetur eum aspernatur quas. Necessitatibus quam est id hic.</p>
<p>Repudiandae deleniti minima perspiciatis quam cupiditate placeat quibusdam aspernatur, esse, libero, eveniet, fugit eaque nesciunt voluptatum. Illum tempora modi odio officiis odit exercitationem eos quis tenetur vero inventore fugit soluta voluptatibus saepe numquam, nemo, rerum atque quo cumque asperiores cum officia nisi sint animi! Eum est sed eligendi. Iure, aspernatur.</p>
<p>Cumque deleniti ut esse sit laudantium voluptate totam, fuga consectetur architecto ad eveniet magnam in similique hic dignissimos minus modi temporibus laboriosam sapiente ab dolorum molestias quia. Velit officiis doloremque, vel magnam pariatur possimus beatae eligendi animi molestiae? Laboriosam reprehenderit veniam id fugiat suscipit animi nemo molestias cupiditate, pariatur corporis.</p>
<p>Iste, nobis inventore, modi, atque harum eius necessitatibus voluptates nam deserunt aspernatur maiores similique vero sapiente vel doloribus quaerat deleniti et magnam sunt quae numquam, mollitia eveniet magni. Sequi rerum sit quaerat obcaecati molestias. Dolores voluptatibus sed quam adipisci ratione, necessitatibus asperiores commodi quasi cupiditate debitis in sit aliquam ex.</p>
<p>Eum aliquam soluta accusantium amet neque pariatur velit in quaerat esse hic nam quibusdam aspernatur quae, iusto, sapiente, at voluptas harum eos. Quas eaque pariatur beatae accusantium fugit, explicabo nulla inventore ducimus minus facilis, impedit. Sequi vel repellat modi delectus quis sit, at suscipit, impedit ipsa quibusdam itaque ullam. Qui.</p>
<p>Illo deserunt sunt commodi. Facere nemo quam delectus quo ad molestiae qui corporis dolorem, dignissimos consectetur iste repellendus animi suscipit sunt sequi, debitis reprehenderit illum repudiandae ipsam laborum fuga, quisquam atque aut unde. Culpa accusantium molestias quidem rerum tempore illo, quam a sit fuga molestiae beatae unde asperiores, deserunt sed.</p>
<p>Accusantium porro nisi nihil corporis, quidem quam nostrum alias debitis distinctio magnam est illo quis veritatis recusandae totam beatae praesentium rem repellat corrupti, commodi minima similique nemo. Quos natus, distinctio culpa eius fugit asperiores corrupti ipsa aperiam, voluptate provident nisi voluptatum eligendi omnis quaerat similique consequuntur ad tempora deserunt unde!</p>
<p>Tenetur culpa, natus aliquid molestiae odit architecto, velit amet, numquam necessitatibus non aut sunt nesciunt quas, odio cum illum provident quis repudiandae ea maxime rerum quidem autem laudantium sapiente. Molestiae ullam voluptatum facere magni optio tempora quos recusandae. Autem alias corporis saepe sit beatae illo, veniam voluptatem minima, adipisci nobis!</p>
<p>Veniam ad, accusamus hic, facilis commodi natus eum cupiditate libero ratione perspiciatis tempora consequuntur vitae dignissimos optio error voluptatum totam quis dolores molestiae, animi excepturi debitis explicabo dolor? Dolorem eveniet cum ab facere eum praesentium ipsa quam itaque. Consequuntur distinctio magnam libero eveniet eum ratione itaque. Molestiae debitis dolorum voluptatem?</p>
<p>Perferendis ut quia iusto illo, consequatur vitae obcaecati assumenda totam in molestiae modi eos rem sunt odio soluta qui at dicta, a aut deserunt voluptates nesciunt! Alias natus et dolores veniam fuga modi quo dolorem numquam! Quisquam inventore esse facere amet qui ducimus repudiandae natus mollitia consequatur ab libero, adipisci.</p>
<p>Perspiciatis error ad odit earum excepturi, cumque, mollitia molestias blanditiis impedit consectetur commodi autem architecto nihil, cupiditate nobis laborum vero voluptatibus. Sit repellat autem dicta, mollitia, cum aliquid minima provident, officia quidem molestias est. Esse iusto optio saepe, dolores debitis culpa dolor quis praesentium consectetur. Quae iusto, fugiat labore! Placeat?</p>
</div>
</section>
</div>
</div>
</section>
</div>
</section>
</main>
</div>
<script src="../../../bower_components/jquery/dist/jquery.js"></script>
<script src="../../../core/shared/lib/nanoscroller/nanoscroller.js"></script>
<script>
function debounce(fn, delay) {
var timer = null;
return function () {
var context = this, args = arguments;
clearTimeout(timer);
timer = setTimeout(function () {
fn.apply(context, args);
}, delay);
};
}
$(function(){
// Duplicate the post 40 times (for brevity of HTML)
// Not required in any way
var list = $(".posts-list li:first");
for (var i = 0; i < 40; i++){list.clone().appendTo('.posts-list');}
// Duplicate the post 40 times (for brevity of HTML)
$(".nano").nanoScroller({
iOSNativeScrolling: true
});
$(".nano").on("mouseenter", function(){
var nano = $(this);
nano.addClass("nanoShowScrollBar");
setTimeout(function(){
nano.removeClass("nanoShowScrollBar");
}, 850);
});
$(".nano-content").scroll(function() {
var nano = $(this).parent(".nano");
nano.addClass("nanoShowScrollBar");
clearTimeout($.data(this, 'scrollTimer'));
$.data(this, 'scrollTimer', setTimeout(function() {
nano.removeClass("nanoShowScrollBar");
}, 850));
});
});
</script>
</body>
</html>

View File

@ -85,4 +85,4 @@
</div>
</fieldset>
</form>
</section>
</section>

View File

@ -0,0 +1,964 @@
/*! nanoScrollerJS - v0.8.4 - 2014
* http://jamesflorentino.github.com/nanoScrollerJS/
* Copyright (c) 2014 James Florentino; Licensed MIT */
(function($, window, document) {
"use strict";
var BROWSER_IS_IE7, BROWSER_SCROLLBAR_WIDTH, DOMSCROLL, DOWN, DRAG, ENTER, KEYDOWN, KEYUP, MOUSEDOWN, MOUSEENTER, MOUSEMOVE, MOUSEUP, MOUSEWHEEL, NanoScroll, PANEDOWN, RESIZE, SCROLL, SCROLLBAR, TOUCHMOVE, UP, WHEEL, cAF, defaults, getBrowserScrollbarWidth, hasTransform, isFFWithBuggyScrollbar, rAF, transform, _elementStyle, _prefixStyle, _vendor;
defaults = {
/**
a classname for the pane element.
@property paneClass
@type String
@default 'nano-pane'
*/
paneClass: 'nano-pane',
/**
a classname for the slider element.
@property sliderClass
@type String
@default 'nano-slider'
*/
sliderClass: 'nano-slider',
/**
a classname for the content element.
@property contentClass
@type String
@default 'nano-content'
*/
contentClass: 'nano-content',
/**
a setting to enable native scrolling in iOS devices.
@property iOSNativeScrolling
@type Boolean
@default false
*/
iOSNativeScrolling: false,
/**
a setting to prevent the rest of the page being
scrolled when user scrolls the `.content` element.
@property preventPageScrolling
@type Boolean
@default false
*/
preventPageScrolling: false,
/**
a setting to disable binding to the resize event.
@property disableResize
@type Boolean
@default false
*/
disableResize: false,
/**
a setting to make the scrollbar always visible.
@property alwaysVisible
@type Boolean
@default false
*/
alwaysVisible: false,
/**
a default timeout for the `flash()` method.
@property flashDelay
@type Number
@default 1500
*/
flashDelay: 1500,
/**
a minimum height for the `.slider` element.
@property sliderMinHeight
@type Number
@default 20
*/
sliderMinHeight: 20,
/**
a maximum height for the `.slider` element.
@property sliderMaxHeight
@type Number
@default null
*/
sliderMaxHeight: null,
/**
an alternate document context.
@property documentContext
@type Document
@default null
*/
documentContext: null,
/**
an alternate window context.
@property windowContext
@type Window
@default null
*/
windowContext: null
};
/**
@property SCROLLBAR
@type String
@static
@final
@private
*/
SCROLLBAR = 'scrollbar';
/**
@property SCROLL
@type String
@static
@final
@private
*/
SCROLL = 'scroll';
/**
@property MOUSEDOWN
@type String
@final
@private
*/
MOUSEDOWN = 'mousedown';
/**
@property MOUSEENTER
@type String
@final
@private
*/
MOUSEENTER = 'mouseenter';
/**
@property MOUSEMOVE
@type String
@static
@final
@private
*/
MOUSEMOVE = 'mousemove';
/**
@property MOUSEWHEEL
@type String
@final
@private
*/
MOUSEWHEEL = 'mousewheel';
/**
@property MOUSEUP
@type String
@static
@final
@private
*/
MOUSEUP = 'mouseup';
/**
@property RESIZE
@type String
@final
@private
*/
RESIZE = 'resize';
/**
@property DRAG
@type String
@static
@final
@private
*/
DRAG = 'drag';
/**
@property ENTER
@type String
@static
@final
@private
*/
ENTER = 'enter';
/**
@property UP
@type String
@static
@final
@private
*/
UP = 'up';
/**
@property PANEDOWN
@type String
@static
@final
@private
*/
PANEDOWN = 'panedown';
/**
@property DOMSCROLL
@type String
@static
@final
@private
*/
DOMSCROLL = 'DOMMouseScroll';
/**
@property DOWN
@type String
@static
@final
@private
*/
DOWN = 'down';
/**
@property WHEEL
@type String
@static
@final
@private
*/
WHEEL = 'wheel';
/**
@property KEYDOWN
@type String
@static
@final
@private
*/
KEYDOWN = 'keydown';
/**
@property KEYUP
@type String
@static
@final
@private
*/
KEYUP = 'keyup';
/**
@property TOUCHMOVE
@type String
@static
@final
@private
*/
TOUCHMOVE = 'touchmove';
/**
@property BROWSER_IS_IE7
@type Boolean
@static
@final
@private
*/
BROWSER_IS_IE7 = window.navigator.appName === 'Microsoft Internet Explorer' && /msie 7./i.test(window.navigator.appVersion) && window.ActiveXObject;
/**
@property BROWSER_SCROLLBAR_WIDTH
@type Number
@static
@default null
@private
*/
BROWSER_SCROLLBAR_WIDTH = null;
rAF = window.requestAnimationFrame;
cAF = window.cancelAnimationFrame;
_elementStyle = document.createElement('div').style;
_vendor = (function() {
var i, transform, vendor, vendors, _i, _len;
vendors = ['t', 'webkitT', 'MozT', 'msT', 'OT'];
for (i = _i = 0, _len = vendors.length; _i < _len; i = ++_i) {
vendor = vendors[i];
transform = vendors[i] + 'ransform';
if (transform in _elementStyle) {
return vendors[i].substr(0, vendors[i].length - 1);
}
}
return false;
})();
_prefixStyle = function(style) {
if (_vendor === false) {
return false;
}
if (_vendor === '') {
return style;
}
return _vendor + style.charAt(0).toUpperCase() + style.substr(1);
};
transform = _prefixStyle('transform');
hasTransform = transform !== false;
/**
Returns browser's native scrollbar width
@method getBrowserScrollbarWidth
@return {Number} the scrollbar width in pixels
@static
@private
*/
getBrowserScrollbarWidth = function() {
var outer, outerStyle, scrollbarWidth;
outer = document.createElement('div');
outerStyle = outer.style;
outerStyle.position = 'absolute';
outerStyle.width = '100px';
outerStyle.height = '100px';
outerStyle.overflow = SCROLL;
outerStyle.top = '-9999px';
document.body.appendChild(outer);
scrollbarWidth = outer.offsetWidth - outer.clientWidth;
document.body.removeChild(outer);
return scrollbarWidth;
};
isFFWithBuggyScrollbar = function() {
var isOSXFF, ua, version;
ua = window.navigator.userAgent;
isOSXFF = /(?=.+Mac OS X)(?=.+Firefox)/.test(ua);
if (!isOSXFF) {
return false;
}
version = /Firefox\/\d{2}\./.exec(ua);
if (version) {
version = version[0].replace(/\D+/g, '');
}
return isOSXFF && +version > 23;
};
/**
@class NanoScroll
@param element {HTMLElement|Node} the main element
@param options {Object} nanoScroller's options
@constructor
*/
NanoScroll = (function() {
function NanoScroll(el, options) {
this.el = el;
this.options = options;
BROWSER_SCROLLBAR_WIDTH || (BROWSER_SCROLLBAR_WIDTH = getBrowserScrollbarWidth());
this.$el = $(this.el);
this.doc = $(this.options.documentContext || document);
this.win = $(this.options.windowContext || window);
this.body = this.doc.find('body');
this.$content = this.$el.children("." + options.contentClass);
this.$content.attr('tabindex', this.options.tabIndex || 0);
this.content = this.$content[0];
this.previousPosition = 0;
if (this.options.iOSNativeScrolling && (this.el.style.WebkitOverflowScrolling != null)) {
this.nativeScrolling();
} else {
this.generate();
}
this.createEvents();
this.addEvents();
this.reset();
}
/**
Prevents the rest of the page being scrolled
when user scrolls the `.nano-content` element.
@method preventScrolling
@param event {Event}
@param direction {String} Scroll direction (up or down)
@private
*/
NanoScroll.prototype.preventScrolling = function(e, direction) {
if (!this.isActive) {
return;
}
if (e.type === DOMSCROLL) {
if (direction === DOWN && e.originalEvent.detail > 0 || direction === UP && e.originalEvent.detail < 0) {
e.preventDefault();
}
} else if (e.type === MOUSEWHEEL) {
if (!e.originalEvent || !e.originalEvent.wheelDelta) {
return;
}
if (direction === DOWN && e.originalEvent.wheelDelta < 0 || direction === UP && e.originalEvent.wheelDelta > 0) {
e.preventDefault();
}
}
};
/**
Enable iOS native scrolling
@method nativeScrolling
@private
*/
NanoScroll.prototype.nativeScrolling = function() {
this.$content.css({
WebkitOverflowScrolling: 'touch'
});
this.iOSNativeScrolling = true;
this.isActive = true;
};
/**
Updates those nanoScroller properties that
are related to current scrollbar position.
@method updateScrollValues
@private
*/
NanoScroll.prototype.updateScrollValues = function() {
var content, direction;
content = this.content;
this.maxScrollTop = content.scrollHeight - content.clientHeight;
this.prevScrollTop = this.contentScrollTop || 0;
this.contentScrollTop = content.scrollTop;
direction = this.contentScrollTop > this.previousPosition ? "down" : this.contentScrollTop < this.previousPosition ? "up" : "same";
this.previousPosition = this.contentScrollTop;
if (direction !== "same") {
this.$el.trigger('update', {
position: this.contentScrollTop,
maximum: this.maxScrollTop,
direction: direction
});
}
if (!this.iOSNativeScrolling) {
this.maxSliderTop = this.paneHeight - this.sliderHeight;
this.sliderTop = this.maxScrollTop === 0 ? 0 : this.contentScrollTop * this.maxSliderTop / this.maxScrollTop;
}
};
/**
Updates CSS styles for current scroll position.
Uses CSS 2d transfroms and `window.requestAnimationFrame` if available.
@method setOnScrollStyles
@private
*/
NanoScroll.prototype.setOnScrollStyles = function() {
var cssValue;
if (hasTransform) {
cssValue = {};
cssValue[transform] = "translate(0, " + this.sliderTop + "px)";
} else {
cssValue = {
top: this.sliderTop
};
}
if (rAF) {
if (cAF && this.scrollRAF) {
cAF(this.scrollRAF);
}
this.scrollRAF = rAF((function(_this) {
return function() {
_this.scrollRAF = null;
return _this.slider.css(cssValue);
};
})(this));
} else {
this.slider.css(cssValue);
}
};
/**
Creates event related methods
@method createEvents
@private
*/
NanoScroll.prototype.createEvents = function() {
this.events = {
down: (function(_this) {
return function(e) {
_this.isBeingDragged = true;
_this.offsetY = e.pageY - _this.slider.offset().top;
if (!_this.slider.is(e.target)) {
_this.offsetY = 0;
}
_this.pane.addClass('active');
_this.doc.bind(MOUSEMOVE, _this.events[DRAG]).bind(MOUSEUP, _this.events[UP]);
_this.body.bind(MOUSEENTER, _this.events[ENTER]);
return false;
};
})(this),
drag: (function(_this) {
return function(e) {
_this.sliderY = e.pageY - _this.$el.offset().top - _this.paneTop - (_this.offsetY || _this.sliderHeight * 0.5);
_this.scroll();
if (_this.contentScrollTop >= _this.maxScrollTop && _this.prevScrollTop !== _this.maxScrollTop) {
_this.$el.trigger('scrollend');
} else if (_this.contentScrollTop === 0 && _this.prevScrollTop !== 0) {
_this.$el.trigger('scrolltop');
}
return false;
};
})(this),
up: (function(_this) {
return function(e) {
_this.isBeingDragged = false;
_this.pane.removeClass('active');
_this.doc.unbind(MOUSEMOVE, _this.events[DRAG]).unbind(MOUSEUP, _this.events[UP]);
_this.body.unbind(MOUSEENTER, _this.events[ENTER]);
return false;
};
})(this),
resize: (function(_this) {
return function(e) {
_this.reset();
};
})(this),
panedown: (function(_this) {
return function(e) {
_this.sliderY = (e.offsetY || e.originalEvent.layerY) - (_this.sliderHeight * 0.5);
_this.scroll();
_this.events.down(e);
return false;
};
})(this),
scroll: (function(_this) {
return function(e) {
_this.updateScrollValues();
if (_this.isBeingDragged) {
return;
}
if (!_this.iOSNativeScrolling) {
_this.sliderY = _this.sliderTop;
_this.setOnScrollStyles();
}
if (e == null) {
return;
}
if (_this.contentScrollTop >= _this.maxScrollTop) {
if (_this.options.preventPageScrolling) {
_this.preventScrolling(e, DOWN);
}
if (_this.prevScrollTop !== _this.maxScrollTop) {
_this.$el.trigger('scrollend');
}
} else if (_this.contentScrollTop === 0) {
if (_this.options.preventPageScrolling) {
_this.preventScrolling(e, UP);
}
if (_this.prevScrollTop !== 0) {
_this.$el.trigger('scrolltop');
}
}
};
})(this),
wheel: (function(_this) {
return function(e) {
var delta;
if (e == null) {
return;
}
delta = e.delta || e.wheelDelta || (e.originalEvent && e.originalEvent.wheelDelta) || -e.detail || (e.originalEvent && -e.originalEvent.detail);
if (delta) {
_this.sliderY += -delta / 3;
}
_this.scroll();
return false;
};
})(this),
enter: (function(_this) {
return function(e) {
var _ref;
if (!_this.isBeingDragged) {
return;
}
if ((e.buttons || e.which) !== 1) {
return (_ref = _this.events)[UP].apply(_ref, arguments);
}
};
})(this)
};
};
/**
Adds event listeners with jQuery.
@method addEvents
@private
*/
NanoScroll.prototype.addEvents = function() {
var events;
this.removeEvents();
events = this.events;
if (!this.options.disableResize) {
this.win.bind(RESIZE, events[RESIZE]);
}
if (!this.iOSNativeScrolling) {
this.slider.bind(MOUSEDOWN, events[DOWN]);
this.pane.bind(MOUSEDOWN, events[PANEDOWN]).bind("" + MOUSEWHEEL + " " + DOMSCROLL, events[WHEEL]);
}
this.$content.bind("" + SCROLL + " " + MOUSEWHEEL + " " + DOMSCROLL + " " + TOUCHMOVE, events[SCROLL]);
};
/**
Removes event listeners with jQuery.
@method removeEvents
@private
*/
NanoScroll.prototype.removeEvents = function() {
var events;
events = this.events;
this.win.unbind(RESIZE, events[RESIZE]);
if (!this.iOSNativeScrolling) {
this.slider.unbind();
this.pane.unbind();
}
this.$content.unbind("" + SCROLL + " " + MOUSEWHEEL + " " + DOMSCROLL + " " + TOUCHMOVE, events[SCROLL]);
};
/**
Generates nanoScroller's scrollbar and elements for it.
@method generate
@chainable
@private
*/
NanoScroll.prototype.generate = function() {
var contentClass, cssRule, currentPadding, options, pane, paneClass, sliderClass;
options = this.options;
paneClass = options.paneClass, sliderClass = options.sliderClass, contentClass = options.contentClass;
if (!(pane = this.$el.children("." + paneClass)).length && !pane.children("." + sliderClass).length) {
this.$el.append("<div class=\"" + paneClass + "\"><div class=\"" + sliderClass + "\" /></div>");
}
this.pane = this.$el.children("." + paneClass);
this.slider = this.pane.find("." + sliderClass);
if (BROWSER_SCROLLBAR_WIDTH === 0 && isFFWithBuggyScrollbar()) {
currentPadding = window.getComputedStyle(this.content, null).getPropertyValue('padding-right').replace(/[^0-9.]+/g, '');
cssRule = {
right: -14,
paddingRight: +currentPadding + 14
};
} else if (BROWSER_SCROLLBAR_WIDTH) {
cssRule = {
right: -BROWSER_SCROLLBAR_WIDTH
};
this.$el.addClass('has-scrollbar');
}
if (cssRule != null) {
this.$content.css(cssRule);
}
return this;
};
/**
@method restore
@private
*/
NanoScroll.prototype.restore = function() {
this.stopped = false;
if (!this.iOSNativeScrolling) {
this.pane.show();
}
this.addEvents();
};
/**
Resets nanoScroller's scrollbar.
@method reset
@chainable
@example
$(".nano").nanoScroller();
*/
NanoScroll.prototype.reset = function() {
var content, contentHeight, contentPosition, contentStyle, contentStyleOverflowY, paneBottom, paneHeight, paneOuterHeight, paneTop, parentMaxHeight, right, sliderHeight;
if (this.iOSNativeScrolling) {
this.contentHeight = this.content.scrollHeight;
return;
}
if (!this.$el.find("." + this.options.paneClass).length) {
this.generate().stop();
}
if (this.stopped) {
this.restore();
}
content = this.content;
contentStyle = content.style;
contentStyleOverflowY = contentStyle.overflowY;
if (BROWSER_IS_IE7) {
this.$content.css({
height: this.$content.height()
});
}
contentHeight = content.scrollHeight + BROWSER_SCROLLBAR_WIDTH;
parentMaxHeight = parseInt(this.$el.css("max-height"), 10);
if (parentMaxHeight > 0) {
this.$el.height("");
this.$el.height(content.scrollHeight > parentMaxHeight ? parentMaxHeight : content.scrollHeight);
}
paneHeight = this.pane.outerHeight(false);
paneTop = parseInt(this.pane.css('top'), 10);
paneBottom = parseInt(this.pane.css('bottom'), 10);
paneOuterHeight = paneHeight + paneTop + paneBottom;
sliderHeight = Math.round(paneOuterHeight / contentHeight * paneOuterHeight);
if (sliderHeight < this.options.sliderMinHeight) {
sliderHeight = this.options.sliderMinHeight;
} else if ((this.options.sliderMaxHeight != null) && sliderHeight > this.options.sliderMaxHeight) {
sliderHeight = this.options.sliderMaxHeight;
}
if (contentStyleOverflowY === SCROLL && contentStyle.overflowX !== SCROLL) {
sliderHeight += BROWSER_SCROLLBAR_WIDTH;
}
this.maxSliderTop = paneOuterHeight - sliderHeight;
this.contentHeight = contentHeight;
this.paneHeight = paneHeight;
this.paneOuterHeight = paneOuterHeight;
this.sliderHeight = sliderHeight;
this.paneTop = paneTop;
this.slider.height(sliderHeight);
this.events.scroll();
this.pane.show();
this.isActive = true;
if ((content.scrollHeight === content.clientHeight) || (this.pane.outerHeight(true) >= content.scrollHeight && contentStyleOverflowY !== SCROLL)) {
this.pane.hide();
this.isActive = false;
} else if (this.el.clientHeight === content.scrollHeight && contentStyleOverflowY === SCROLL) {
this.slider.hide();
} else {
this.slider.show();
}
this.pane.css({
opacity: (this.options.alwaysVisible ? 1 : ''),
visibility: (this.options.alwaysVisible ? 'visible' : '')
});
contentPosition = this.$content.css('position');
if (contentPosition === 'static' || contentPosition === 'relative') {
right = parseInt(this.$content.css('right'), 10);
if (right) {
this.$content.css({
right: '',
marginRight: right
});
}
}
return this;
};
/**
@method scroll
@private
@example
$(".nano").nanoScroller({ scroll: 'top' });
*/
NanoScroll.prototype.scroll = function() {
if (!this.isActive) {
return;
}
this.sliderY = Math.max(0, this.sliderY);
this.sliderY = Math.min(this.maxSliderTop, this.sliderY);
this.$content.scrollTop(this.maxScrollTop * this.sliderY / this.maxSliderTop);
if (!this.iOSNativeScrolling) {
this.updateScrollValues();
this.setOnScrollStyles();
}
return this;
};
/**
Scroll at the bottom with an offset value
@method scrollBottom
@param offsetY {Number}
@chainable
@example
$(".nano").nanoScroller({ scrollBottom: value });
*/
NanoScroll.prototype.scrollBottom = function(offsetY) {
if (!this.isActive) {
return;
}
this.$content.scrollTop(this.contentHeight - this.$content.height() - offsetY).trigger(MOUSEWHEEL);
this.stop().restore();
return this;
};
/**
Scroll at the top with an offset value
@method scrollTop
@param offsetY {Number}
@chainable
@example
$(".nano").nanoScroller({ scrollTop: value });
*/
NanoScroll.prototype.scrollTop = function(offsetY) {
if (!this.isActive) {
return;
}
this.$content.scrollTop(+offsetY).trigger(MOUSEWHEEL);
this.stop().restore();
return this;
};
/**
Scroll to an element
@method scrollTo
@param node {Node} A node to scroll to.
@chainable
@example
$(".nano").nanoScroller({ scrollTo: $('#a_node') });
*/
NanoScroll.prototype.scrollTo = function(node) {
if (!this.isActive) {
return;
}
this.scrollTop(this.$el.find(node).get(0).offsetTop);
return this;
};
/**
To stop the operation.
This option will tell the plugin to disable all event bindings and hide the gadget scrollbar from the UI.
@method stop
@chainable
@example
$(".nano").nanoScroller({ stop: true });
*/
NanoScroll.prototype.stop = function() {
if (cAF && this.scrollRAF) {
cAF(this.scrollRAF);
this.scrollRAF = null;
}
this.stopped = true;
this.removeEvents();
if (!this.iOSNativeScrolling) {
this.pane.hide();
}
return this;
};
/**
Destroys nanoScroller and restores browser's native scrollbar.
@method destroy
@chainable
@example
$(".nano").nanoScroller({ destroy: true });
*/
NanoScroll.prototype.destroy = function() {
if (!this.stopped) {
this.stop();
}
if (!this.iOSNativeScrolling && this.pane.length) {
this.pane.remove();
}
if (BROWSER_IS_IE7) {
this.$content.height('');
}
this.$content.removeAttr('tabindex');
if (this.$el.hasClass('has-scrollbar')) {
this.$el.removeClass('has-scrollbar');
this.$content.css({
right: ''
});
}
return this;
};
/**
To flash the scrollbar gadget for an amount of time defined in plugin settings (defaults to 1,5s).
Useful if you want to show the user (e.g. on pageload) that there is more content waiting for him.
@method flash
@chainable
@example
$(".nano").nanoScroller({ flash: true });
*/
NanoScroll.prototype.flash = function() {
if (this.iOSNativeScrolling) {
return;
}
if (!this.isActive) {
return;
}
this.reset();
this.pane.addClass('flashed');
setTimeout((function(_this) {
return function() {
_this.pane.removeClass('flashed');
};
})(this), this.options.flashDelay);
return this;
};
return NanoScroll;
})();
$.fn.nanoScroller = function(settings) {
return this.each(function() {
var options, scrollbar;
if (!(scrollbar = this.nanoscroller)) {
options = $.extend({}, defaults, settings);
this.nanoscroller = scrollbar = new NanoScroll(this, options);
}
if (settings && typeof settings === "object") {
$.extend(scrollbar.options, settings);
if (settings.scrollBottom != null) {
return scrollbar.scrollBottom(settings.scrollBottom);
}
if (settings.scrollTop != null) {
return scrollbar.scrollTop(settings.scrollTop);
}
if (settings.scrollTo) {
return scrollbar.scrollTo(settings.scrollTo);
}
if (settings.scroll === 'bottom') {
return scrollbar.scrollBottom(0);
}
if (settings.scroll === 'top') {
return scrollbar.scrollTop(0);
}
if (settings.scroll && settings.scroll instanceof $) {
return scrollbar.scrollTo(settings.scroll);
}
if (settings.stop) {
return scrollbar.stop();
}
if (settings.destroy) {
return scrollbar.destroy();
}
if (settings.flash) {
return scrollbar.flash();
}
}
return scrollbar.reset();
});
};
$.fn.nanoScroller.Constructor = NanoScroll;
})(jQuery, window, document);